面向对象(继承,多态)

本文最后更新于:1 年前

二十六、面向对象-继承

1. 继承的概念

​ 继承可以理解为就是让两个类(事物)产生从属关系,有了从属关系子类就肯定会具有父类的特征(父类中的非私有成员),这样我们用类去描述一些事物的时候就可以更方便

1.1 相关名词

​ 超类,父类都是同一个概念就是叫法不同

​ 派生类,子类都是同一个概念就是叫法不同

2. 继承的格式

​ 在子类名后面加extends 父类名

例如

1
2
3
public class Dog extends Animal {
//Dog就成为了Animal的一个子类
}

3. 继承的注意事项

  • java中类只能单继承,一个类只能有一个父类,但是可以多层继承
  • 要有实际的从属关系才可以继承,不能不合逻辑的任意继承。

4. 继承的优缺点

4.1 优点

​ 提高了代码的复用性

4.2 缺点

​ 增加了类和类之间的耦合性。违背了高内聚,低耦合原则

5. 继承后成员相关语法

5.1 成员变量

  1. 父类非私有的成员变量才会继承给子类。所以当我们看到使用子类对象的某个成员变量时,有可能这个成员变量是定义在子类中。也有可能是定义在其父类中。

  2. 父类中如果已经有了某个成员变量,我们不应该再在子类中定义同名的成员变量。否则可能会导致非常难排查的Bug。

5.2 构造方法

1. 构造方法不会继承给子类

2. 子类的构造中必须调用父类的构造并且要求在第一行。

3. 子类的构造默认都会在第一行调用父类的无参构造,所以当父类没有无参构造的时候子类构造中会报错。解决方案是给父类加上无参构造或者在子类构造中显示的调用父类的有参构造。

理解

问:继承的时候构造方法为什么不会被继承?

答: 假设会被继承,这个方法名和子类的类名就肯定不相同,所以不能算是构造方法.所以假设不成立

问:子类的构造方法会默认调用父类的无参构造super().为什么?

答:因为父类中可能有成员变量,并且这个成员变量要继承给子类使用,但是所有的变量在使用之前必须先赋值.而父类的成员变量只能用父类的构造进行默认的初始化。

问:在子类的构造方法中,能不能把父类的构造放到第二行?

答:不能,因为要保证成员变量先初始化了.如果放到第二行,有可能在第一行还没初始化的时候就使用的父类的成员变量

5.3 成员方法

​ 父类非私有的成员方法会继承给子类。所以当我们看到使用子类对象的某个成员成员方法时,有可能这个成员方法是定义在子类中。也有可能是定义在其父类中。

6. 方法重写

6.1 方法重写的概念

​ 当子类拥有父类继承下来的某个功能(成员方法),但是在子类中对这个方法的具体实现和父类不同。这个时候我们在子类中定义了一个和父类方法相同的方法(包括返回值类型,方法名,参数列表) ,这就叫做方法重写。

6.2 注意事项

  • ​ 我们在重写方法的时候方法的权限修饰符其实可以和父类不同(一般都相同)。但是子类中方法的权限不能比父类低。(权限修饰符 : private < 默认(什么都不写) < protected < public)

  • ​ 我们在重写方法的时候方法的返回值类型其实可以不同(一般都相同)。但是要求子类中方法的返回值类型必须是父类方法返回值类型的子类。

  • ​ 我们可以用 @Override 注解来校验是不是方法重写。

  • ​ 私有的方法不能被重写,因为私有的不会被继承

6.3 小思考

面试题:说说overload和override的区别。

答: 方法重载:在同一个类中,方法名相同,参数列表不同,和返回值无关
方法重写:在子父类中,子类有一个和父类方法名相同,参数列表相同,返回值类型也相同的方法。这个就叫方法的重写

7. this和super

​ this就代表本类的,super就代表父类的

使用:

  • 访问成员变量
  • ​ this.成员变量名 本类的成员变量
  • ​ super.成员变量名 父类的成员变量
  • 访问成员方法
  • ​ this.成员方法名(参数) 调用本类的成员方法
  • ​ super.成员方法名(参数) 调用父类的成员方法
  • 调用构造方法
  • ​ this(参数) 调用本类的构造方法
  • ​ super(参数) 调用父类的构造方法

二十七、面向对象-多态

1. 多态的概念

​ 同一个数据类型的不同对象对同一种行为会有多种不同的实现。

2. 多态的前提

​ ① 子类重写了父类的方法

​ ② 父类引用指向子类对象(创建的是一个子类的对象,并把该对象赋值给一个变量,这个变量的类型是其父类类型)

​ 例如:

1
2
Animal a = new Dog();
Animal b = new Cat();

3. 父类引用指向子类对象后成员访问的特点

​ 除了成员方法编译看左边,运行看右边。其他所有成员都是编译看左边,运行看左边。

​ 解读:编译期间会去看左边(父类),看父类有没有这个成员方法。如果没有则直接报错,如果有则编译通过,不报错。运行期间,实际执行代码,看的是右边(子类),看子类中有没有重写该方法,如果有则执行子类中的该方法,如果没有
则运行父类中的该方法。

4. 多态的应用场景

​ 多态最大的应用场景其实就用在方法的参数上。在适当的时候把方法的参数类型定义成父类类型。调用方法的时候就可以传入任意的子类对象。提高了代码的复用性和可扩展性。

5. 多态的优缺点

优点:提高了代码的复用性和可扩展性

缺点:不能直接使用子类的成员。

6.向上转型,向下转型

6.1 向上转型

​ 向上转型就是子类转父类。因为是绝对安全的所以会自动进行转换。

例如:

1
Animal a = new Dog();

6.2 向下转型

​ 向上转型就是父类转子类。因为不是绝对安全的所以必须使用强转的方式来进行转换。

例如:

1
2
Animal a = new Dog();
Dog d = (Dog)a;

注意:必须是这个子类的对象才可以转换成该子类,否则会出异常

6.3 instanceof进行类型判断

​ 在向下转型的时候为了保证安全我们可以使用instanceof进行类型判断。判断一个对象是否某个类的对象。如果是的话我们再把它转换成该类型,这样会更加安全。

6.3.1 使用格式

​ 对象 instanceof 类名/接口名

示例:

1
2
3
4
5
6
//判断对象是否是某个类的对象,如果是结果为true,如果不是结果为false
Animal a = new Dog();
if(a instanceof Dog){
//说明a是Dog这个类的对象,我们可以把他强转成Dog类型
Dog d = (Dog)a;
}

7. 思考题

思考下面代码的输出结果

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
class A {  
public String show(D obj){
return ("A and D");
}
public String show(A obj){
return ("A and A");
}
}
class B extends A{

public String show(B obj){
return ("B and B");
}
public String show(A obj){
return ("B and A");
}
}
class C extends B{}
class D extends B{}

class DynamicTest
{
public static void main(String[] args){
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println(a1.show(b));
System.out.println(a1.show(c));
System.out.println(a1.show(d));

System.out.println(a2.show(b));
System.out.println(a2.show(c));
System.out.println(a2.show(d));

System.out.println(b.show(b));
System.out.println(b.show(c));
System.out.println(b.show(d));

}
}

面向对象(继承,多态)
http://example.com/2020/02/01/面向对象(继承-多态)/
作者
Crush
发布于
2020年2月1日
更新于
2023年7月9日
许可协议