多态
介绍
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
使用重载体现多态
java
class B {
public void say() {
System.out.println("B say");
}
}
class A extends B {
public int sum(int n1, int n2) {
return n1 + n2;
}
public int sum(int n1, int n2, int n3) {
return n1 + n2 + n3;
}
}
public static void main(String[] args) {
A a = new A();
// 这里传入不同的参数,应付调用不同sum方法,就体现金矿
System.out.println(a.sum(1, 2));
System.out.println(a.sum(1, 2, 3));
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
使用重写体现多态
java
class B {
public void say() {
System.out.println("B say");
}
}
class A extends B {
public void say() {
System.out.println("A say");
}
}
public static void main(String[] args) {
// 使用重写实现多态
B b = new B();
a.say();
b.say();
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
对象的多态
- 一个对象的编译类型和运行类型可以不一致
- 编译类型在定义对象时,就确定了,不能改变
- 运行类型是可以变化的。
- 编译类型看定义时
=号的左边,运行类型看=号的右边
java
/**
* animal 编译类型是 Animal, 运行类型Dog
* Animal animal = new Dag();
*
* animal2 的运行类型变成了Cat, 编译类型仍然是Animal
* Animal animal2 = new Cat();
*/
public class Animal {
public void cry() {
System.out.println('Animal cry() 动物在叫...')
}
}
public class Cat extends Animal {
public void cry() {
System.out.println('Cat cry() 小猫喵喵叫...')
}
}
public class Dog extends Animal {
public void cry() {
System.out.println('Dog cry() 小狗汪汪叫...')
}
}
public static void main(String[] args) {
// animal 编译类型就是 Animal, 运行类型是 Dog
Animal animal = new Dog();
animal.cry(); // Dog cry() 小狗汪汪叫...
// animal 编译类型 Animal, 运行类型 Cat
animal = new Cat();
animal.cry(); // Cat cry() 小猫喵喵叫...
}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
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
多态案例
java
public void feed(Animal animal, Food food) {
Systeom.out.println("主人" + name + "给" + animal.getName() + "喂" + food.getName())
}1
2
3
2
3
多态注意事项和细节讨论
多态的前提是:两个对象(类)存在继承关系
多态的向上转型
- 本质:父类的引用指向了子类的对象
- 语法:
父类类型 引用名 = new 子类的对象() - 特点:编译类型看左边,运行类型看右边。可以调用父类中的所有成员(需遵守访问权限),不能调用子类中特有成员;最终运行效果看子类的具体实现!
向上转型
java
public class Animal {
String name = '动物'
int age = 10
public void sleep() {
System.out.println('睡')
}
public void run() {
System.out.println('跑')
}
public void eat() {
System.out.println('吃')
}
public void show() {
System.out.println('Hello, 你好')
}
}
public class Cat extends Animal {
public void eat() {
System.out.println('猫吃鱼')
}
public void catchMouse() {
System.out.println('猫抓老鼠')
}
}
public class PolyDetail {
public static void main(String[] args) {
// 向下转型:父类的引用指向了子类的对象
Animal animal = new Cat();
Object obj = new Cat();
// 可以调用父类的所有成员,但是不能调用子类的特有的成员
// 因为在编译阶段,能调用哪些成员,是由编译类型决定的
animal.eat(); // 猫吃鱼
}
}
``
### 向下转型
1. `语法:子类类型 引用名 = (子类类型) 父类引用`
2. 只能强转父类的引用,不能强转父类的对象
3. 要求父类的引用必须指向的是当前目标类型的对象
4. 当向下转型后,可以调用子类类型中所有的成员方法
```java
...
// cat 的编译类型是 Cat, 运行类型是 Cat
Cat cat = (Cat) animal;
cat.catchMouse(); // 要求父类的引用必须指向的是当前目标类型的对象
...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
44
45
46
47
48
49
50
51
52
53
54
55
56
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
44
45
46
47
48
49
50
51
52
53
54
55
56
属性的重写问题
- 属性没有重写之说
- instanceOf 比较操作符,用于判断对象的类型是否为
XX类型或XX类型的子类型
java
class Base {
int count = 10;
}
class Sub extends Base {
int count = 20;
}
public static void main(String[] args) {
Base base = new Sub();
System.out.println(base.count); // 10
Sub sub = new sub();
System.out.println(sub.count); // 20
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
java
class AA {}
class BB extends AA {}
public class PolyDetail {
public static void main(String[] args) {
BB bb = new BB();
System.out.println(bb instanceof BB); // true
System.out.println(bb instanceof AA); // true
// aa 编译类型是 AA, 运行类型是 BB
AA aa = new BB();
System.out.println(aa instanceof AA); // true
System.out.println(aa instanceof BB); // true
Object obj = new Object();
System.out.println(obj instanceof AA); // false
String str = 'hello'
System.out.println(str instanceof Object); // true
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
java
class Base {
int count = 10;
public void display() {
System.out.println(this.count)
}
}
class Sub extends Base {
int count = 20;
public void display() {
System.out.println(this.count)
}
}
public static void main(String[] args) {
Sub s = new Sub();
System.out.println(s.count); // 20
s.display(); // 20
Base b = s;
System.out.println(b == s) // T
System.out.println(b.count); // 10
b.display(); // 10
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
动态绑定机制
- 当调用对象方法的时候,该方法会和该对象的内存地址,即运行类型绑定
- 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用。
java
class A {
public int i = 10;
public int sum() {
return getI() + 10;
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}
class B extends A {
public int i = 20;
public int sum() {
return i + 20;
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}
public static void main(String[] args) {
// a 的编译类型是 A,运行类型是 B
A a = new B(); // 向上转型
System.out.println(a.sum()); // 40
System.out.println(a.sum1()); // 30
// 将子类的 sum 注释会打印什么
// 将子类的 sum1 注释会打印什么
}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
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
多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
java
/**
* 现有一个继承结构如下:要求创建1个Person对象,2个Student对象,和2个Teacher对象,统一放在数组中,新营市say方法。
* 如何调用子类特有的方法,比如Teacher 有一个 teach, Sudent 有一个 study, 怎么调用
*/
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String say() { // 返回名字和年龄
return name + "\t" + age;
}
}
public class Student extends Person {
private double score;
public Student(String name, int age, double score) {
super(name, age);
this.score = score
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public String say() { // 返回名字和年龄和成绩
return super.say() + "\t" + "say = " + score;
}
public void study() {
System.out.println("学生" + getName() + "正在学习");
}
}
public class Teacher extends Person {
private double salary;
public Teacher(String name, int age, double salary) {
super(name, age);
this.salary = salary;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String say() { // 返回名字和年龄和薪水
return super.say() + "\t" + "salary = " + salary;
}
public void teach() {
System.out.println("老师" + getName() + "正在授课");
}
}
public class Main {
public static void main(String[] args) {
Person[] persons = new Person[5];
persons[0] = new Person("jack", 20);
persons[1] = new Student("rose", 18, 100);
persons[2] = new Student("smith", 19, 30.1);
persons[3] = new Teacher("scott", 30, 20000);
persons[4] = new Teacher("king", 50, 25000);
// 循环遍历多态数组,调用say
for (int i = 0; i < persons.length; i++) {
// person[i] 编译类型 是 Person, 运行类型是根据实际情况决定的
persons[i].say(); // 动态绑定机制
// 判断 person[i] 的运行类型是否是 Student
if (persons[i] instanceof Student) {
// 是 Student 类型,则向下转型
Student student = (Student) persons[i];
student.study();
}
// 判断 person[i] 的运行类型是否是 Teacher
if (persons[i] instanceof Teacher) {
// 是 Teacher 类型,则向下转型
Teacher teacher = (Teacher) persons[i];
teacher.teach();
}
}
}
}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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
多态参数
方法定义的形参类型为父类类型,实参类型允许为子类类型。
java
/**
* 定义员工类Employee, 包含姓名和月工资[private], 以及计算年工资getAnnual()的方法。
* 普通员工和经理继承了员工,经理类多了资金bonus属性和管理manage()方法。
* 普通员工类我了work方法,普通员工和经理类要求分别重写getAnnual()方法。
*
* 测试类中添加一个方法 showEmpAnnal(Employee e), 实现获取任何员工对象的年工资,并在main方法中调用该方法[e.getAnnual()]
*
* 测试类中添加一个方法,testWork, 如果是普通员工,则调用work方法,如果是经理,则调用manage方法。
*/
public class Employee {
private String name;
private double salary;
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
// 得到年工资的方法
public double getAnnual() {
return 12 * salary;
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public void setName(String name) {
this.name = name;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
public class Worker extends Employee {
public Worker(String name, double salary) {
super(name, salary);
}
public void work() {
System.out.println("普通员工" + getName() + "正在工作");
}
public double getAnnual() {
return super.getAnnual();
}
}
public class Manager extends Employee {
private double bonus;
public Manager(String name, double salary, double bonus) {
super(name, salary);
this.bonus = bonus;
}
public double getBonus() {
return bonus;
}
public void manage() {
System.out.println("经理" + getName() + "正在管理");
}
public double getAnnual() {
return super.getAnnual() + bonus;
}
}
public class PlayParametes {
public static void main(String[] args) {
Worker tom = new Worker("Tom", 2500);
Manager malan = new Manager("milan", 5000, 200000);
PloyParameter ployParameter = new PloyParameter();
ployParameter.showEmpAnnual(tom);
ployParameter.showEmpAnnual(malan);
ployParameter.testWork(tom);
ployParameter.testWork(malan);
}
public void showEmpAnnual(Employee employee) {
System.out.println(employee.getAnnual());
}
public void testWork (Employee employee) {
if (e instanceof Worker) {
((Worker) e).work();
} else if (e instanceof Manager) {
((Manager) e).manage();
} else {
System.out.println("该员工没有相应的操作方法");
}
}
}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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
== 运算符
==是一个比较运算符
==既可以判断基本类型,又可以判断引用类型==如果判断基本类型,判断的是值是否相等==如果判断引用类型,判断的是地址是否相等,即判断是不是同一个对象
java
class A {}
class B extends A { }
public static void main(String[] args) {
A a = new A();
A b = a;
A c = b;
System.out.println(a == c); // true
B bObj = a;
System.out.println(bObj == c); // true
int num1 = 10;
double num2 = 10.0;
System.out.println(num1 == num2); // true
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
子类重写 equals 方法
- equals: 是 Object 类中的方法,只能判断引用类型
- 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等
java
/**
* 判断两个 Person 对象的内容是否相等,如果两个 Person 对象的各个属性值都一样,则返回 true, 反之返回 false
*/
class Person {
private String name;
private int age;
private char gender;
public Person(String name, int age, char gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
// 重写Object 的 equals 方法
public boolean equals(Object obj) {
// 如果如果比较的两个对象是同一个对象,则返回 true
if (this == obj) {
return true;
}
// 类型判断
if(obj instanceof Person) { // 是Person, 才可以比较
// 进行向下转型,因为我需要得到Obj的各个属性
Person p = (Person) obj;
return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;
}
return false;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person("jack", 10, '男');
Person p2 = new Person("jack", 10, '男');
// Object 类中的 equals 方法
System.out.println(p1.equals(p2)); // false
}
}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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
java
class Person {
private String name;
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "hspedu";
Person p2 = new Person();
p2.name = "hspedu";
System.out.println(p1 === p2); // false
System.out.println(p1.name.equals(p2.name)); // true
System.out.println(p1.equals(p2)) ; // false
String s1 = new String("asdf");
String s2 = new String("asdf");
System.out.println(s1.equals(s2)); // true
System.out.println(s1 === s2); // false
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
hashCode 方法
- 提高具有哈希结构的容器的效率!
- 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!
- 两个引用,如果指向的是不同对象,则哈希值是不一样的
- 哈希值主要根据地址号来的!不能完全将哈希值等价于地址
java
class AA {}
public class Main {
public static void main(String[] args) {
AA a1 = new AA();
AA a2 = new AA();
AA a3 = a1;
System.out.println(a1.hashCode()); // 11686312
System.out.println(a2.hashCode()); // 11686313
System.out.println(a3.hashCode()); // 11686312
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
toString
默认返回: 全类名 + @ + 哈希值的十六进制,子类往往重写toString方法,用于返回对象的属性值。打印对象或拼接对象时,都会自动调用该对象的toString形式
java
public String toString() {
// getClass().getName() 类的全类名(包名 + 类名)
// Integer.toHexString(hashCode()) 对象的hashCode值转成 16 进制字符串
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
class Monster {
private String name;
private String job;
private double sal;
public Monster(String name, String job, double sal) {
this.name = name;
this.job = job;
this.sal = sal;
}
// 重写 toString 方法, 输出对象的属性
public String toString() {
return "Monster{name='" + name + "', job='" + job + "', sal=" + sal + '}';
}
}
new Monster("小妖怪", "巡山", 1000)
System.out.println(monster.toString() + ' hashcode=' + monster.hashCode());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
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
当直接输出一个对象时,toString方法会被默认的调用
finalize
- 当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,做一皯释放资源的操作
- 什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁对象,在销毁该对象前,会先调用地inalize方法
- 垃圾回收机制的调用,是由系统来决定,也可以通过System.gc() 方法触发垃圾回收机制。
java
public class Finalize_ {
public static void main() {
Cass bmw = new Car("宝马");
bmw = null; // 这时 car 对象就是一个垃圾,垃圾回收器就会加收(销毁)对象,在 销毁对象前,
// 会调用该对象的 finalize方法,程序员就可以在 finalize 中,写自己的业务逻辑代码(比如释放资源,数据库连接,或者打开文件...)
// 如果程序员不重写 finalize, 那么就会调用 Object 类的 finalize, 即默认处理
// 如果程序员重写了 finalize, 就可以实现自己的逻辑
System.gc();
}
}
class Car{
private String name;
public Car (String name) {
this.name = name;
}
protected void finalize() throws Throwable {
System.out.println("我们销毁 汽车" + name)
System.out.println("释放了某些资源...")
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
