类 超类 子类

对于第四章的 Employee类,经理的办公,工资形式不同,可以创建个新类 Manager,但可以重用Employee中的部分代码,将所有域保留下来。

子类

由继承 E类来定义Manager 类的格式,关键字 extends 表继承,构造的新类派生于一个已存在的类。

定义子类

  1. public class Manager extends Employee
  2. {
  3. 方法
  4. }

其中:
已存在的类称为超类 ( superclass)、 基类(base class) 或父类(parent class); 新类称为子类(subclass)派生类 (derivedclass) 或孩子类(child class)。 父子类并没有优劣性。
**

  1. /构造一个新方法 setBonus,只给经理加奖金
  2. private double bonus;
  3. public void setBonus(double bonus)
  4. {
  5. this.bonus = bonus
  6. }
  7. boss.setBonus(500);
  8. 效果:只能给Manager加奖金,给Employee加会报错。

tip:Manager类就包括了 Employee的所有实例域和新实例域bonus。
(但是调用超类的私有域,要依照超类的方法或者基于此新建子类方法)——见覆盖方法

一般思路,将通用功能放到超类,特殊用途放到子类。

覆盖方法

修改/覆盖(override) 超类原有的方法

  1. 思路一:直接调用E类实例域 //错误
  2. public double getSalary()
  3. {
  4. return salary + bonus; // won't work
  5. }

原因:对于超类的私有域,只有超类的方法可以访问,子类的方法无法访问(子类内不会再定义一个新的已有域,因此会出错)
但子类可以通过借助共有接口(超类方法→ getSalary) 来间接访问

  1. public double getSalary()
  2. {
  3. double baseSalary = getSalary();// still won't work
  4. return baseSalary + bonus;
  5. }

原因:程序认为你调用的是 本类的而不是超类的getSalary(),就会无限调用自己。

  1. public double getSalary()
  2. {
  3. double baseSalary = super.getSalary();//work
  4. return baseSalary + bonus;
  5. }
  6. 添加超类方法的标识 关键字 super

子类构造器

  1. public Manager(String name, double salary, int year, int month, int day)
  2. {
  3. super(name, salary, year, month, day);
  4. bonus = 0;
  5. }

super关键词:调用超类 含有name—day 参数的构造器 的简写形式
如果没有显式调用,会默认用超类的。

类似this关键字~

继承层次

继承并不仅限于一个层次。 例如, 可以由 Manager 类派生 Executive 类。由一个公共超类派生出来的所有类的集合被称为继承层次(inheritance hierarchy), 如图 5-1 所示。在继承 层次中, 从某个特定的类到其祖先的路径被称为该类的继承链 (inheritance chain)0
image.png

阻止继承

有时候,可能希望阻止人们利用某个类定义子类。不允许扩展的类被称为 final 类。

  1. public final String getNameQ

抽象类

  1. public abstract class Person
  2. {
  3. private String name;
  4. public Person(St ring name)
  5. {
  6. this.name = name;
  7. }
  8. }

表述为一个抽象的大类
image.png
Employee和student在逻辑上都是通过抽象类拓展出来的。

还是不太理解抽象方法(好像在接口中会阐述)

Object

object是所有java类的始祖,每个类都是由他扩展出来的

  1. Object obj = new Employee(xxx);
  2. #object只是作为各种值的 通用持有者。 要进行操作还需要类型转换
  3. //这个是向上转型(自动类型转换):小——>大
  4. Employee = (Employee) obj
  5. //向下转型,以便进行更改器等具体操作

equals方法

Object 类中的 equals 方法用于检测一个对象是否等于另外一个对象。在 Object 类中,这
个方法将判断两个对象是否具有相同的引用。引用相同,它们是相同

  1. return Objects.equals(name, other.name)
  2. public class Manager extends Employee
  3. public boolean equals(Object otherObject)
  4. {
  5. if (!super equals(otherObject)) return false;
  6. // super.equals checked that this and otherObject belong to the same class
  7. Manager other = (Manager) otherObject;
  8. return bonus == other.bonus;
  9. }
  10. }
  11. //如果超类都不相同,就不需要比较子类,反之则需要再比较子类的实例域(manager比员工多了个bonus实例域)
  1. public class Employee
  2. {
  3. public boolean equals(Employee other)
  4. {
  5. return other != null
  6. && getClassO == other.getClass0
  7. && Objects.equals(name, other. name)
  8. && salary == other.salary
  9. && Objects.equals(hireDay ,other.hireDay) ;
  10. }}
  11. 该方法错误
  12. 原因:想覆盖一个新的equals方法(基于objects的),但是并没有覆盖(格式不对)。
  13. 应该改为
  14. Override public boolean equals(Object other);
  1. 规范式写覆盖equals!!!
  2. @Override
  3. public boolean equals(Object otherObject)
  4. {
  5. // 如果引用的地址一样,肯定相等
  6. if (this == otherObject) return true;
  7. //没地址肯定不相等
  8. if (otherObject == null) return false;
  9. // 类名不一样,肯定错(既然已经区分不同类了,每个类一定有其不同的特质)
  10. if (getClass() != otherObject.getClass()) return false;
  11. // now we know otherObject is a non-null Employee
  12. var other = (Employee) otherObject;
  13. // test whether the fields have identical values
  14. return Objects.equals(name, other.name)
  15. && 基本变量 == other.基本变量 && Objects.equals(对象, 对象).....;
  16. }
  • == 对于引用类型,比较的是两个对象 引用地址的值;对于基本类型,比较的是值
  • 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 staff = new ArrayList0;
或 ArrayList staff = new ArrayListoQ<>();
菱形语法类似 var,自动检查这个变量是什么泛型类型填在<>中。(尖括号的类型参数不能是基本类型)
常见方法
image.png

对象包装器与自动装箱

有时, 需要将 int 这样的基本类型转换为对象。 所有的基本类型都冇一个与之对应的类。
这些类称为包装器 ( wrapper )
这些对象包装器类 拥有很明显的名字:IntegerLongFloatDoubleShortByteCharacter Void Boolean。 且为final类,无法定义子类
**

自动装箱(autoboxing)和自动拆箱(autowrapping)

细节内容在第八章泛型会讲到,现在先忽视罢

如果想搭一个int的泛型数组,因为<>的限制,只能塞入对象,可以转化int而用Integer类来做
ArrayList list = new 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 对象, 这个对象很像中的函数指针