继承
概念
特性
1.提高了代码的复用性
2.让类与类之间产生了关系,有了这个关系,才有了多态的特性
千万不要为了获取其他类的功能,简化代码而继承,必须是类与类之间有所属关系才可以继承
3.JAVA语言中,JAVA只支持单继承,不支持多继承[同一个子类不可以同时继承自多个父类]
为什么不能多继承?
因为多继承容易带来安全隐患:当多个父类中定义了相同功能但功能内容不同时,子类对象不确定运行哪一个
1 public class Exam { 2 public static void main(String[] args) { 3 4 } 5 } 6 7 class C { 8 public void show() { 9 System.out.println("我是父类C"); 10 } 11 } 12 13 class B { 14 public void show() { 15 System.out.println("我是父类B"); 16 } 17 } 18 19 class A extends B { //A继承了B就不能继承A,因为那样不安全 20 21 }
但是JAVA保留了这种机制,并用另一种体现形式完成表示——多实现
4.JAVA支持多层继承,也就是一个继承体系
如何使用一个继承体系中的功能?
想要使用体系,先查阅体系父类的描述,因为父类中定义的是该体系中的共性功能,了解共性功能,就可以知道该体系的基本功能,那么这个体系已经可以基本使用了
为什么在具体调用时,要创建最子类的对象?
(1)因为有可能父类不能创建对象,如抽象类
(2)创建子类对象可以使用更多功能,包括基本的也包括特有的
简单总结一句话
查阅父类功能,创建子类对象使用功能
1 public class Exam { 2 public static void main(String[] args) { 3 A a = new A(); 4 a.show(); 5 6 } 7 } 8 9 class C { 10 public void show() { 11 System.out.println("我是父类C"); 12 } 13 } 14 15 class B extends C { 16 public void show() { 17 System.out.println("我是B"); 18 } 19 } 20 21 class A extends B { 22 public void show() { 23 System.out.println("我是子类A"); 24 } 25 }
细节部分
1.子类继承父类的成员变量
当子类继承了某个类之后,便可以使用父类中的成员变量,但是并不是完全继承父类的所有成员变量。具体的原则如下:
1)能够继承父类的public和protected成员变量;不能够继承父类的private成员变量;
2)对于父类的包访问权限成员变量,如果子类和父类在同一个包下,则子类能够继承;否则,子类不能够继承;
3)对于子类可以继承的父类成员变量,如果在子类中出现了同名称的成员变量,则会发生隐藏现象,即子类的成员变量会屏蔽掉父类的同名成员变量。如果要在子类中访问父类中同名成员变量,需要使用super关键字来进行引用。
2.子类继承父类的方法
同样地,子类也并不是完全继承父类的所有方法。
1)能够继承父类的public和protected成员方法;不能够继承父类的private成员方法;
2)对于父类的包访问权限成员方法,如果子类和父类在同一个包下,则子类能够继承;否则,子类不能够继承;
3)对于子类可以继承的父类成员方法,如果在子类中出现了同名称的成员方法,则称为覆盖,即子类的成员方法会覆盖掉父类的同名成员方法。如果要在子类中访问父类中同名成员方法,需要使用super关键字来进行引用。
注意:隐藏和覆盖是不同的。隐藏是针对成员变量和静态方法的,而覆盖是针对普通方法的。
3.构造器
子类是不能够继承父类的构造器,但是要注意的是,如果父类的构造器都是带有参数的,则必须在子类的构造器中显示地通过super关键字调用父类的构造器并配以适当的参数列表。如果父类有无参构造器,则在子类的构造器中用super关键字调用父类构造器不是必须的,如果没有使用super关键字,系统会自动调用父类的无参构造器
1 class Shape { 2 3 protected String name; 4 5 public Shape(){ 6 name = "shape"; 7 } 8 9 public Shape(String name) { 10 this.name = name; 11 } 12 } 13 14 class Circle extends Shape { 15 16 private double radius; 17 18 public Circle() { 19 radius = 0; 20 } 21 22 public Circle(double radius) { 23 this.radius = radius; 24 } 25 26 public Circle(double radius,String name) { 27 this.radius = radius; 28 this.name = name; 29 } 30 }
这样的代码是没有问题的,如果把父类的无参构造器去掉,则下面的代码必然会出错:

改成下面这样就行了:

4.super
super主要有两种用法:
1)super.成员变量/super.成员方法;
2)super(parameter1,parameter2....)
第一种用法主要用来在子类中调用父类的同名成员变量或者方法;第二种主要用在子类的构造器中显示地调用父类的构造器,要注意的是,如果是用在子类构造器中,则必须是子类构造器的第一个语句。
常见的面试笔试题
1.下面这段代码的输出结果是什么?
1 public class Test { 2 public static void main(String[] args) { 3 new Circle(); 4 } 5 } 6 7 class Draw { 8 9 public Draw(String type) { 10 System.out.println(type+" draw constructor"); 11 } 12 } 13 14 class Shape { 15 private Draw draw = new Draw("shape"); 16 17 public Shape(){ 18 System.out.println("shape constructor"); 19 } 20 } 21 22 class Circle extends Shape { 23 private Draw draw = new Draw("circle"); 24 public Circle() { 25 System.out.println("circle constructor"); 26 } 27 }
1 shape draw constructor 2 shape constructor 3 circle draw constructor 4 circle constructor
这道题目主要考察的是类继承时构造器的调用顺序和初始化顺序。要记住一点:父类的构造器调用以及初始化过程一定在子类的前面。由于Circle类的父类是Shape类,所以Shape类先进行初始化,然后再执行Shape类的构造器。接着才是对子类Circle进行初始化,最后执行Circle的构造器。
2.下面这段代码的输出结果是什么?
1 public class Test { 2 public static void main(String[] args) { 3 Shape shape = new Circle(); 4 System.out.println(shape.name); 5 shape.printType(); 6 shape.printName(); 7 } 8 } 9 10 class Shape { 11 public String name = "shape"; 12 13 public Shape(){ 14 System.out.println("shape constructor"); 15 } 16 17 public void printType() { 18 System.out.println("this is shape"); 19 } 20 21 public static void printName() { 22 System.out.println("shape"); 23 } 24 } 25 26 class Circle extends Shape { 27 public String name = "circle"; 28 29 public Circle() { 30 System.out.println("circle constructor"); 31 } 32 33 public void printType() { 34 System.out.println("this is circle"); 35 } 36 37 public static void printName() { 38 System.out.println("circle"); 39 } 40 }
1 shape constructor 2 circle constructor 3 shape 4 this is circle 5 shape
这道题主要考察了隐藏和覆盖的区别(当然也和多态相关)。
覆盖只针对非静态方法(终态方法不能被继承,所以就存在覆盖一说了),而隐藏是针对成员变量和静态方法的。这2者之间的区别是:覆盖受RTTI(Runtime type identification)约束的,而隐藏却不受该约束。也就是说只有覆盖方法才会进行动态绑定,而隐藏是不会发生动态绑定的。在Java中,除了static方法和final方法,其他所有的方法都是动态绑定。因此,就会出现上面的输出结果。

浙公网安备 33010602011771号