多态
★★★多态
- 方法或对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承的基础之上的。
方法的多态
- 方法的重载和重写就体现出多态(虽然方法名相同,但根据具体的情况却可以对应名称相同但具体内容不同的方法,体现出多态)。
★对象的多态
一个对象的编译类型和运行类型可以不一致;
编译类型在定义对象时,就确定了,不能改变;而运行类型是可以变化的;
编译类型看定义时 “=” 的左边,运行类型看定义时 “=” 的右边。
1
2//Animal 是 Dog 的父类,Animal 是编译类型,Dog 是运行类型
Animal animal = new Dog();多态的前提:两个类存在继承关系。
★向上转型
本质:父类的引用指向了子类的对象
语法:
父类类型 引用名 = new 子类类型();
特点:编译类型看左边,运行类型看右边;可以调用父类中的所有成员(须遵守访问权限),但不能调用子类中的特有成员,因为在编译阶段,能调用哪些属性和方法是由编译类型来决定的(例如子类
Dog
有一个方法public void bark()
而父类Animal
没有,则Animal animal = new Dog();
,animal
是不能调用bark()
的);当调用方法时,是看运行类型的,即会先从子类开始查找,遵循 “继承本质详解” 中提到的查找方法的规律(例如animal.cry()
时,会先从Dog
类中调用cry()
函数,若没有,再从Animal
类中调用cry()
函数)。注意:属性没有重写之说,属性的值看编译类型,这和方法调用时看运行类型是有区别的。

只需记住只有属性特别一点,是看编译类型的,其他的,因为对象本身就是由运行类型来创建的,所以当然先看运行类型(比如
instanceof
运算符)。
向下转型
语法:
子类类型 引用名 = (子类类型)父类引用;
1
2
3//要求此时 animal 是指向的 Dog 类的对象
Dog dog = (Dog)animal;
//此时就可以使用 dog.bark() 了。也可以 ((Dog)animal).bark()特点:只能强转父类的引用,不能强转父类的对象;要求父类的引用必须指向的是当前目标类型的对象;当向下转型后,就可以调用子类类型中的所有成员(须遵循访问权限)。
★★★★★动态绑定机制
当调用对象方法的时候,该方法会和该对象内存地址 / 运行类型绑定;
当调用对象属性的时候,没有动态绑定机制,哪里声明就在哪里使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44package com.f;
public class DynamicBinding {
public static void main(String[] args) {
Base base = new Sub();
System.out.println(base.sum()); //21
System.out.println(base.sum1()); //2
}
}
class Base {
public int i = 1;
public int sum() {
//由于动态绑定机制,所以调用的是 Sub 类的 getI 函数,应该为 20 + 1 = 21
return getI() + 1;
//return this.getI() + 10; 和上面是一样的
}
public int sum1() {
//属性是没有动态绑定机制的,所以应该为 1 + 1 = 2
return i + 1;
}
public int getI() {
return i;
}
}
class Sub extends Base {
public int i = 20;
//public int sum() {
// return i + 20;
//}
//public int sum1() {
// return i + 10;
//}
public int getI() {
return i;
}
}
多态数组
定义:数组的定义类型为父类类型,里面保存的实际元素类型为子类类型。
1
2
3
4
5
6
7//Animal 是父类,Dog 和 Cat 是子类
Animal[] animals = new Animal[5];
animals[0] = new Animal();
animals[1] = new Dog();
animals[2] = new Dog();
animals[3] = new Cat();
animals[4] = new Cat();如果想让
animals[1]
和animals[2]
调用其指向的对象的运行类型(Dog
)的成员方法bark()
,可以用如下方法:1
2
3
4
5for (int i = 0; i < animals.length; i++) {
if(animals[i] instanceof Dog){ //类型判断
((Dog)animals[i]).bark(); //向下转型然后调用
}
}
多态参数
- 定义:方法定义的形参类型为父类类型,实参类型允许为子类类型。
Object类详解
- 下面是 Object 类的方法:

★==和equals

== 和
equals
的对比:== 是一个比较运算符,既可以判断基本类型,又可以判断引用类型;
如果判断基本类型,则判断的是值是否相等(
10 == 10.0
结果为true
);如果判断引用类型,则判断的是地址是否相等,即判断是不是指向同一个对象。
equals
是Object
类中的方法,只能判断引用类型;默认判断的是地址是否相等,子类中往往重写该方法(比如String
、Integer
),用于判断内容是否相等。

hashCode

- 可以提高具有哈希结构的容器的效率;
- 对于两个引用,如果指向的是同一个对象,则其哈希值肯定是一样的;反之其哈希值肯定是不一样的;
- 哈希值主要是根据地址号来的,但不能完全将哈希值等价于地址。
★toString

1 | // Object 类的 toString 方法 |
默认返回:全类名(包名加类名) + @ + 哈希值的十六进制。
子类往往重写
toString
方法,用于返回对象的属性信息(可以在IDEA
中通过Alt+insert
快捷键快速重写toString
方法)。注意:当直接输出一个对象时,
toString
方法会被默认调用。
finalize

- 当对象被回收时,系统自动调用该对象的
finalize
方法。子类可以重写该方法,做一些释放资源的操作; - 什么时候被回收:当某个对象没有任何引用时,jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用
finalize
方法; - 垃圾回收机制的调用,是由系统来决定的(jvm 的 GC 算法),也可以通过
System.gc()
主动触发垃圾回收机制。
★断点调试(debug)
断点调试是指在程序的某一行设置一个断点(breakpoint),调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误并停下,进而程序员可以找到这个 Bug。
断点调试也能帮助我们查看
java
底层源代码的执行过程,从而提高程序员的java
水平。注意:在断点调试的过程中,是运行状态,是以对象的运行类型来执行的。

断点调试快捷键
F7
:跳入。跳入方法内;F8
:跳过。不跳入方法内,逐行执行代码;shift+F8
:跳出。跳出方法;F9
:resume,执行到下一个断点处。