目录
继承
前面我们知道Java中用类来对现实世界中的实体进行描述,类经过实例化后产生的对象可以用来表示现实中的对象,但是现实生活中事物之间可能存在一些关联,比如动物和猫,狗,为了更好的设计程序,面相对象中用继承来处理不同类之间的联系。
1. 什么是继承
继承机制:是面相对象程序设计实现代码复用最重要的手段,它允许程序员在保持原有结构特性的基础上进行扩展,增加新功能,从而产生新的类(派生类)。
Java中通过接口,抽象类和类来实现继承关系,本章主要介绍类与类之间的继承关系。
下面是类之间的继承语法:
修饰符 class 子类 extends 父类 {// ...}
下面是猫,狗继承动物类的代码实例:
// Animal.java
public class Animal{
String name;
int age;
public void eat(){
System.out.println(name + "正在吃饭");
}
public void sleep(){
System.out.println(name + "正在睡觉");
}
}
// Dog.java
public class Dog extends Animal{
void bark(){
System.out.println(name + "汪汪汪~~~");
}
}
// Cat.Java
public class Cat extends Animal{
void mew(){
System.out.println(name + "喵喵喵~~~");
}
}
// TestExtend.java
public class TestExtend {
public static void main(String[] args) {
Dog dog = new Dog();
// dog类中并没有定义任何成员变量,name和age属性肯定是从父类Animal中继承下来的
System.out.println(dog.name);
System.out.println(dog.age);
// dog访问的eat()和sleep()方法也是从Animal中继承下来的
dog.eat();
dog.sleep();
dog.bark();
}
}
子类完全继承父类的变量和方法,所以在子类实例化出的对象中也能使用父类的方法。
2.子类中访问父类成员的一些注意事项
- 父类中成员变量或成员方法的访问限定等级在protected及以上是可以直接被子类使用的。
- 子类的成员变量或者成员方法相同,则在使用时会优先使用子类的,如果子类没有在从父类中寻找。
- 如果子类变量跟父类变量相同还想访问父类变量可以使用super关键字,它的作用是在子类方法中访问父类的成员。
在上述代码中我们引入super可以写成:
// Dog.java
public class Dog extends Animal{
String name=super.name;
void bark(){
System.out.println(name + "汪汪汪~~~");
}
}
// Cat.Java
public class Cat extends Animal{
String name=super.name;
void mew(){
System.out.println(name + "喵喵喵~~~");
}
}
super的使用我们需要注意:
1. 只能在非静态方法中使用
2. 在子类方法中,访问父类的成员变量和方法。
3.必须是构造方法中的第一条并且不能和this同时存在。
3. 在继承关系中各种代码块的执行顺序
在上一篇文章中我们介绍了四种代码块,其中有一个经典问题就是比较在类里静态代码块,实例代码块,和构造方法在实例化时的执行顺序,其中静态代码块只在实例化第一个对象时执行,也是最先执行的,然后是实例代码块,最后在执行构造方法。
下面我们来看看在继承关系下这三种代码块的执行顺序:
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Person:构造方法执行");
}
{
System.out.println("Person:实例代码块执行");
}
static {
System.out.println("Person:静态代码块执行");
}
}
class Student extends Person{
public Student(String name,int age) {
super(name,age);
System.out.println("Student:构造方法执行");
}
{
System.out.println("Student:实例代码块执行");
}
static {
System.out.println("Student:静态代码块执行");
}
}
public class TestDemo4 {
public static void main(String[] args) {
Student student1 = new Student("张三",19);
System.out.println("===========================");
Student student2 = new Student("李四",20);
}
public static void main1(String[] args) {
Person person1 = new Person("张三",10);
System.out.println("============================");
Person person2 = new Person("李四",20);
}
}
执行结果如下:
我们能发现依然是静态代码块先执行,从中我们还可以发现子类在实例化对象时,不仅子类构造方法会加载,父类构造方法也会被加载。
4.类与类之间的继承规则
类与类之间的继承只有这三种关系:
不能一个类继承多个类
5. final关键字
final可以修饰变量,成员方法,和类
- 修饰变量,则此变量后续不能修改。
- 修饰方法,该方法不能被重写。
- 修饰类,此类不能被继承。
什么是重写?
重写其实就是在子类中把父类本身有的方法重新写一遍。在方法名,参数列表,返回类型(除过子类中方法的返回值是父类中方法返回值的子类时)都相同的情况下, 对方法体进行修改或重写,这就是重写。但要注意子类函数的访问修饰权限不能少于父类的。为了方便辨认是否发生了重写,重写方法前要加上@Override以进行标识。
面试题:
重载与重写的区别?
答:方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求,不能根据返回类型进行区分。
6.组合与继承
组合和继承都是Java中进行代码复用的一种表现形式,我们在进行程序设计时更鼓励使用组合的形式。
举一个例子来区分组合和继承:
我们需要定义一个奔驰类,继承的思维就是先定义一个汽车类,让奔驰继承汽车类,而组合的思维则是将汽车类更加细化,在定义多个零部件类,这些部件类就不仅仅可以被汽车类使用,还能在日后被手机类,飞机类等使用,提高了代码的复用性和维护性。
// 轮胎类
class Tire{
// ...
}
// 发动机类
class Engine{
// ...
}
// 车载系统类
class VehicleSystem{
// ...
}
class Car{
private Tire tire; // 可以复用轮胎中的属性和方法
private Engine engine; // 可以复用发动机中的属性和方法
private VehicleSystem vs; // 可以复用车载系统中的属性和方法
// ...
}
// 奔驰是汽车
class Benz extend Car{
// 将汽车中包含的:轮胎、发送机、车载系统全部继承下来
}