类 超类 子类
对于第四章的 Employee类,经理的办公,工资形式不同,可以创建个新类 Manager,但可以重用Employee中的部分代码,将所有域保留下来。
子类
由继承 E类来定义Manager 类的格式,关键字 extends 表继承,构造的新类派生于一个已存在的类。
定义子类
public class Manager extends Employee
{
方法
}
其中:
已存在的类称为超类 ( superclass)、 基类(base class) 或父类(parent class); 新类称为子类(subclass)、 派生类 (derivedclass) 或孩子类(child class)。 父子类并没有优劣性。
**
/构造一个新方法 setBonus,只给经理加奖金
private double bonus;
public void setBonus(double bonus)
{
this.bonus = bonus
}
boss.setBonus(500);
效果:只能给Manager加奖金,给Employee加会报错。
tip:Manager类就包括了 Employee的所有实例域和新实例域bonus。
(但是调用超类的私有域,要依照超类的方法或者基于此新建子类方法)——见覆盖方法
一般思路,将通用功能放到超类,特殊用途放到子类。
覆盖方法
修改/覆盖(override) 超类原有的方法
思路一:直接调用E类实例域 //错误
public double getSalary()
{
return salary + bonus; // won't work
}
原因:对于超类的私有域,只有超类的方法可以访问,子类的方法无法访问(子类内不会再定义一个新的已有域,因此会出错)
但子类可以通过借助共有接口(超类方法→ getSalary) 来间接访问
public double getSalary()
{
double baseSalary = getSalary();// still won't work
return baseSalary + bonus;
}
原因:程序认为你调用的是 本类的而不是超类的getSalary(),就会无限调用自己。
public double getSalary()
{
double baseSalary = super.getSalary();//work
return baseSalary + bonus;
}
添加超类方法的标识 关键字 super
子类构造器
public Manager(String name, double salary, int year, int month, int day)
{
super(name, salary, year, month, day);
bonus = 0;
}
super关键词:调用超类 含有name—day 参数的构造器 的简写形式
如果没有显式调用,会默认用超类的。
类似this关键字~
继承层次
继承并不仅限于一个层次。 例如, 可以由 Manager 类派生 Executive 类。由一个公共超类派生出来的所有类的集合被称为继承层次(inheritance hierarchy), 如图 5-1 所示。在继承 层次中, 从某个特定的类到其祖先的路径被称为该类的继承链 (inheritance chain)0
阻止继承
有时候,可能希望阻止人们利用某个类定义子类。不允许扩展的类被称为 final 类。
public final String getNameQ
抽象类
public abstract class Person
{
private String name;
public Person(St ring name)
{
this.name = name;
}
}
表述为一个抽象的大类
Employee和student在逻辑上都是通过抽象类拓展出来的。
还是不太理解抽象方法(好像在接口中会阐述)
Object
object是所有java类的始祖,每个类都是由他扩展出来的
Object obj = new Employee(xxx);
#object只是作为各种值的 通用持有者。 要进行操作还需要类型转换
//这个是向上转型(自动类型转换):小——>大
Employee = (Employee) obj
//向下转型,以便进行更改器等具体操作
equals方法
Object 类中的 equals 方法用于检测一个对象是否等于另外一个对象。在 Object 类中,这
个方法将判断两个对象是否具有相同的引用。引用相同,它们是相同
return Objects.equals(name, other.name)
public class Manager extends Employee
public boolean equals(Object otherObject)
{
if (!super equals(otherObject)) return false;
// super.equals checked that this and otherObject belong to the same class
Manager other = (Manager) otherObject;
return bonus == other.bonus;
}
}
//如果超类都不相同,就不需要比较子类,反之则需要再比较子类的实例域(manager比员工多了个bonus实例域)
public class Employee
{
public boolean equals(Employee other)
{
return other != null
&& getClassO == other.getClass0
&& Objects.equals(name, other. name)
&& salary == other.salary
&& Objects.equals(hireDay ,other.hireDay) ;
}}
该方法错误
原因:想覆盖一个新的equals方法(基于objects的),但是并没有覆盖(格式不对)。
应该改为
Override public boolean equals(Object other);
规范式写覆盖equals!!!
@Override
public boolean equals(Object otherObject)
{
// 如果引用的地址一样,肯定相等
if (this == otherObject) return true;
//没地址肯定不相等
if (otherObject == null) return false;
// 类名不一样,肯定错(既然已经区分不同类了,每个类一定有其不同的特质)
if (getClass() != otherObject.getClass()) return false;
// now we know otherObject is a non-null Employee
var other = (Employee) otherObject;
// test whether the fields have identical values
return Objects.equals(name, other.name)
&& 基本变量 == other.基本变量 && Objects.equals(对象, 对象).....;
}
- == 对于引用类型,比较的是两个对象 引用地址的值;对于基本类型,比较的是值
- equals(Object x) 比较的是引用的地址值
- getClass()返回一个对象所属的类 不判断继承关系
- instanceof比较一个对象是否是该类的实例
hashcode方法
散列码( hash code ) 是由对象导出的一个整型值。 散列码是没有规律的。如果 x 和 y 是
两个不同的对象, x.hashCode( ) 与 y.hashCode( ) 基本上不会相同。
由于 hashCode 方法定义在 Object 类中, 因此每个对象都有一个默认的散列码,其值为对象的存储地址。
Equals 与 hashCode 的定义必须一致:如果 x.equals(y) 返回 true, 那么 x.hashCode( ) 就必须与 y.hashCode( ) 具有相同的值。 例如, 如果用定义的 Employee.equals 比较雇员的 ID, 那
么 hashCode 方法就需要散列 ID,而不是雇员的姓名或存储地址。
tostring方法
在 Object 中还有一个重要的方法, 就是 toString 方法, 它用于返回表示对象值的字符
串。
绝大多数(但不是全部)的 toString方法都遵循这样的格式:类的名字,随后是一对方括
号括起来的域值。
eg:boss.toString(): equals.Manager[name=Carl Cracker,salary=80000.0,hireDay=1987-12-15][bonus=5000.0]
泛型数组列表
一般java的数组是静态的,一旦确定大小就很难更改。在 Java 中, 解决这个问题最简单的方法是使用 Java 中另外一个被称为 ArrayList 的类。它使用起来有点像数组,但在添加或删除元素时, 具有自动调节数组容量的
功能,而不需要为此编写任何代码。
下面声明和构造一个保存 Employee 对象的数组列表:
ArrayList
或 ArrayList
菱形语法类似 var,自动检查这个变量是什么泛型类型填在<>中。(尖括号的类型参数不能是基本类型)
常见方法
对象包装器与自动装箱
有时, 需要将 int 这样的基本类型转换为对象。 所有的基本类型都冇一个与之对应的类。
这些类称为包装器 ( wrapper )
这些对象包装器类
拥有很明显的名字:Integer、Long、Float、Double、Short、Byte、Character 、Void 和 Boolean。 且为final类,无法定义子类
**
自动装箱(autoboxing)和自动拆箱(autowrapping)
细节内容在第八章泛型会讲到,现在先忽视罢
如果想搭一个int的泛型数组,因为<>的限制,只能塞入对象,可以转化int而用Integer类来做
ArrayList
且Integer支持自动装箱和拆包
装箱: list.add(3);编译器自动认识为 list.add (Integer.value0f(3));
拆包:int n = list.get(i); → int n = list.get(i).intValue();
也支持在算术表达中进行这两种操作:
IInteger n = 3; n++;
补充:如果在一个条件表达式中混合使用 Integer 和 Double 类型, Integer 值就会拆箱,
提升为 double基本类型, 再装箱为 Double:
Integer n = 1;
Double
x = 2.0;
System.out.println(true ? n : x); // Prints 1.0
参数数量可变的方法
public static double max (double… values)
{
double largest = Double.NECATIVEJNFINITY;
for (double v : values) if (v > largest) largest = v;
return largest;
}
可以像下面这样调用这个方法:
double m = max(3.1, 40.4, -5);
编译器将 new double[ ] {3.1, 40.4,-5} 传递给 max 方法。
反射
能够分析类能力的程序称为反射(reflective )。反射机制的功能极其强大,在下面可以看
到, 反射机制可以用来:
•在运行时分析类的能力。
•在运行时查看对象, 例如, 编写一个 toString 方法供所有类使用。
•实现通用的数组操作代码。
•利用 Method 对象, 这个对象很像中的函数指针