多态(重中之重、难点)

  • 基本概念

    多态主要指同一种事物表现出来的多种形态。

  • 语法格式

    父类类型 引用变量名 = new 子类类型();

1
2
3
4
5
Person pw = new Worker();
pw.show();

解析:
编译阶段调用Person类中的show方法,运行阶段调用Worker类中的show方法。
  • 多态的效果

    (1)当父类类型的引用指向子类对象时,父类类型的引用可以直接调用父类中独有的方法.

    (2)当父类类型的引用指向子类对象时,父类类型的引用不可以直接调用子类独有的方法.

    (3)对于父子类都拥有的非静态成员方法来说,编译阶段调用父类版本,运行阶段调用子类版本.

    (4)对于父子类都拥有的静态成员方法来说,编译阶段和运行阶段都调用父类版本.

  • 引用数据类型之间的转换

    (1)引用数据类型之间的转换分为:自动类型转换 和 强制类型转换。

    其中自动类型转换主要指从小类型到大类型的转换,也就是从子类到父类的转换。

    其中强制类型转换主要指从大类型到小类型的转换,也就是从父类到子类的转换。

    (2)引用数据类型之间的转换必须发生在父子类之间,否则编译报错。

    (3)若目标类型不是引用变量指向的对象类型,则编译通过运行发生类型转换异常。

    (4)为了避免上述错误的发生则应该进行判断,具体方式如下:

    if(引用变量名 instanceof 目标类型) - 用于判断引用指向的对象是否为目标类型

  • 实际意义

    多态的实际意义在于屏蔽不同子类的差异性实现通用的编程带来不同的结果。

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
public class Person {
private String name;
private int age;

public Person() {
super();
}
public Person(String name, int age) {
super();
setName(name);
setAge(age);
}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age > 0 && age < 150) {
this.age = age;
} else {
System.out.println("年龄不合理!!!");
}
}

public void show() {
System.out.println("我是" + getName() + ",今年" + getAge() + "岁了!");
}

public static void test() {
System.out.println("Person类中的静态方法");
}
}
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
public class Worker extends Person {
private int salary;

public Worker() {
super();
}
public Worker(String name, int age, int salary) {
super(name, age);
setSalary(salary);
}

public int getSalary() {
return salary;
}
public void setSalary(int salary) {
if(salary >= 2120) {
this.salary = salary;
} else {
System.out.println("薪水不合理!!!");
}
}

@Override
public void show() {
super.show();
System.out.println("我的薪水是:" + getSalary());
}

public static void test() {
System.out.println("------Worker类中的静态方法");
}
}
1
2
3
public class Teacher extends Person {

}
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
public class PersonWorkerTest {

public static void main(String[] args) {

// 声明Person类型的引用指向Person类型的对象,没有多态
Person p = new Person("zhangfei", 30);
// 下面的代码调用Person类的show方法
p.show();
System.out.println("----------------------------------");

// 声明Worker类型的引用指向Worker类型的对象,没有多态
Worker w = new Worker("guanyu", 35, 3500);
// 当Worker类中没有重写show方法时,下面的代码调用Person类的show方法
// 当Worker类中重写show方法后,下面调用Worker类的show方法
w.show();
System.out.println("----------------------------------");

// 声明Person类型的引用指向Worker类型的对象,形成多态
// 实现了从Worker类型到Person类型的转换,小到大 自动类型转换
Person pw = new Worker("liubei", 40, 5000);
// 当Worker类中没有重写show方法时,下面的代码调用Person类的show方法
// 当Worker类中重写show方法后,下面调用Worker类的show方法
pw.show();
System.out.println("----------------------------------");

// ctrl+alt+方向键 可以实现对选中内容的复制
// alt+方向键 可以实现对选中内容的移动
String str1 = pw.getName();
System.out.println("获取到的姓名是:" + str1); // liubei

// pw.getSalary(); error
pw.test();
Person.test();
System.out.println("----------------------------------");

// 实现了从Person类型到Worker类型的转换,从大到小 强制类型转换
int ia = ((Worker) pw).getSalary();
System.out.println("获取到的价格是:" + ia); // 5000

// 实现从Person类型到String类型的转换
//String str2 = (String)pw; error
//编译ok 运行发生ClassCastException类型转换异常
//Teacher t = (Teacher)pw;
if(pw instanceof Teacher) {
System.out.println("可以放心地转换了");
} else {
System.out.println("转换有风险,操作需谨慎!");
}
}
}

抽象类(重点)

  • 抽象方法的概念

    抽象方法主要指不能具体实现的方法并且使用abstract关键字修饰,格式如下:

    访问权限 abstract 返回值类型 成员方法名(形参列表);

    如:public abstract void cry();

  • 抽象类的概念

    抽象类主要指不能具体实例化的类并且使用abstract关键字修饰。

  • 注意事项

    (1)抽象类中可以有成员变量、成员方法以及构造方法。

    (2)抽象类中可以没有抽象方法,也可以有抽象方法。

    (3)拥有抽象方法的类必须是抽象类,因此真正意义上的抽象类是由abstract关键字修饰并且拥有抽象方法的类。

  • 实际意义

    抽象类的实际意义不在于自身创建对象而在于被继承,当一个类继承抽象类之后必须重写抽象方法,否则该类也变成抽象类。

    因此抽象类对子类具有强制性和规范性,叫做模板设计模式。

  • 经验分享

    在以后的开发中推荐使用多态的方式编写代码,此时抽象类的引用可以直接调用的所有方法一定是抽象类中拥有的方法,当需要更换子类时只需要将new后面的类型修改而其它地方代码不变就立即生效,从而提高了代码的可维护性。

    该方式的缺点在于:父类引用若希望访问子类独有方法,则需要强制类型转换。

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
public abstract class AbstractTest {
private int cnt;

public AbstractTest() {
super();
}
public AbstractTest(int cnt) {
super();
setCnt(cnt);
}

public int getCnt() {
return cnt;
}
public void setCnt(int cnt) {
this.cnt = cnt;
}

// 自定义抽象方法
public abstract void show();

public static void main(String[] args) {
//AbstractTest at = new AbstractTest();
//System.out.println("at.cnt = " + at.cnt); // 0
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public /* abstract */ class SubAbstractTest extends AbstractTest {
@Override
public void show() {
System.out.println("流氓不可怕,就怕流氓有文化!我就是被迫重写的方法");
}

public static void main(String[] args) {

// 子类类型的引用指向子类类型的对象,没有多态
SubAbstractTest sat = new SubAbstractTest();
// 调用重写以后的show方法
sat.show();
System.out.println("-------------------------------------");

// 父类类型的引用指向子类类型的对象,形成了多态 多态的使用场合二
AbstractTest at = new SubAbstractTest();
// 编译阶段调用父类版本,运行阶段调用重写以后的版本
at.show();
}
}