JAVA笔记 第五章 继承

第五章 继承

类 超类 子类

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

子类

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

定义子类

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

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

1
2
3
4
5
6
7
8
9
10
/构造一个新方法 setBonus,只给经理加奖金
private double bonus;
public void setBonus(double bonus)
{
this.bonus = bonus
}

boss.setBonus(500);

效果:只能给Manager加奖金,给Employee加会报错。

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

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

覆盖方法

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

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


原因:对于超类的私有域,只有超类的方法可以访问,子类的方法无法访问
但子类可以通过借助共有接口(超类方法) 来间接访问

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

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

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

子类构造器

1
2
3
4
5
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
image.png

阻止继承

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

1
public final String getNameQ

抽象类

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

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

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

Object

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

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

equals方法

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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实例域)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
规范式写覆盖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 staff = new ArrayList0;

或 ArrayList staff = new ArrayListoQ<>();

菱形语法类似 var,自动检查这个变量是什么泛型类型填在<>中。(尖括号的类型参数不能是基本类型)
常见方法
image.png

对象包装器与自动装箱

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

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

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

如果想搭一个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 对象, 这个对象很像中的函数指针


本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!