1.继承的核心作用:
对类的共性进行抽取,让子类能够继承父类的属性和方法,从而实现代码复用和功能扩展。
简而言之:共性抽取,代码复用
2.继承的使用语法:
修饰符 class 子类 extends 父类 {
// ...
}
public class Animal{ String name; int age; public void eat(){ System.out.println(name + "正在吃饭"); } public void sleep(){ System.out.println(name + "正在睡觉"); } } public class Dog extends Animal{ public void bark(){ System.out.println(name + "汪汪汪~~~"); } }注意:
1. 子类会将父类中的成员变量或者成员方法继承到子类中了,但不继承静态成员变量或者成员方法
2. 子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承
子类访问父类的4种情况
1.子类访问成员变量时,自己有优先自己的,如果没有则向父类中找。
2.通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找。如果没有则报错;
3.访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用方法适传递的参数选择合适的方法访问,如果没有则报错;
4.子类中存在与父类中相同的成员时,则在子类中访问父类相同名称的成员需要借助super关键字
3.super关键字
主要作用:在子类方法中访问父类的成员。
super所在的当前类一定是子类
【注意事项】
1. 只能在非静态方法中使用
2. 在子类方法中,访问父类的成员变量和方法。
1.调用父类的构造方法
在子类的构造方法里,super()用于调用父类的构造方法。这一操作可保证父类的初始化逻辑得以执行。需要注意的是,super()必须是子类构造方法中的首条语句。
2.访问父类的成员变量
若子类和父类存在同名的成员变量,可使用super关键字来访问父类的成员变量。
3.调用父类的成员方法
当子类重写了父类的方法后,若要调用父类的原始方法,可使用super关键字。
4.子类构造方法
父子父子,先有父再有子,即:子类对象构造时,需要先调用基类构造方法,然后执行子类的构造方法。
public class Base { public Base(){ System.out.println("Base()"); } } public class Derived extends Base{ public Derived(){ // super(); // 注意子类构造方法中默认会调用基类的无参构造方法:super(), // 用户没有写时,编译器会自动添加,而且super()必须是子类构造方法中第一条语句, // 并且只能出现一次 System.out.println("Derived()"); } } public class Test { public static void main(String[] args) { Derived d = new Derived(); } } 结果打印: Base() Derived()// 基类:汽车 class Car { private String brand; private int wheels; // 基类的构造方法,接收品牌和车轮数量作为参数 public Car(String brand, int wheels) { this.brand = brand; this.wheels = wheels; System.out.println("一辆 " + brand + " 汽车正在制造,它有 " + wheels + " 个轮子。"); } } // 子类:电动汽车,继承自汽车 class ElectricCar extends Car { private int batteryCapacity; // 子类的构造方法,接收品牌、车轮数量和电池容量作为参数 public ElectricCar(String brand, int wheels, int batteryCapacity) { // 调用基类的构造方法初始化品牌和车轮数量 super(brand, wheels); this.batteryCapacity = batteryCapacity; System.out.println("这辆电动汽车的电池容量是 " + batteryCapacity + " 千瓦时。"); } } public class Main { public static void main(String[] args) { // 创建一个电动汽车对象 ElectricCar tesla = new ElectricCar("特斯拉", 4, 100); } }注意:
1. 若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构造方法
2. 如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败。
3. 在子类构造方法中,super(...)调用父类构造时,必须是子类构造函数中第一条语句。
4. super(...)只能在子类构造方法中出现一次,并且不能和this同时出现。普通方法中可以同时出现
super(实参)、this(实参) 只能写在构造方法里面,普通成员方法里根本不能写 super ()、this () 这种调用构造器的语法。
区分两个东西,很容易记混:
✅super() / this():调用构造方法,仅限构造器第一行,普通方法完全不能用
✅super.属性 / this.属性:访问成员变量,普通方法里面可以用
super(...)(调用父类构造)与this(...)(调用本类其他构造)
①只能写在构造方法内;
②必须是构造方法里第一条语句;
③同一个构造器里最多出现一次;
④二者不能共存,不能同时写;
⑤普通方法中,不能写 super ()、this () 去调用构造器。
5.执行顺序
在没有继承关系时的执行顺序:
1. 静态代码块先执行,并且只执行一次,在类加载阶段执行
2. 当有对象创建时,才会执行实例代码块,实例代码块执行完成后,最后构造方法执行
有继承关系时的执行顺序:
1、父类静态代码块优先于子类静态代码块执行,且是最早执行
2、父类实例代码块和父类构造方法紧接着执行
3、子类的实例代码块和子类构造方法紧接着再执行
4、第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行
总结:静态最优先,父先于子。
6.final 关键字
final关键字可以用来修饰变量、成员方法以及类。
1. 修饰变量或字段,表示常量(即不能修改)
final int a = 10; a = 20; // 编译出错final int[] array1 = {1,2,3,4,5}; array1 = new int[10]; //报错
2. 修饰类:表示此类不能被继承
final public class Animal { ... } public class Bird extends Animal { ... } // 编译出错 //无法继承3. 修饰方法:表示该方法不能被重写(后序介绍)
7. 继承与组合
和继承类似, 组合也是一种表达类之间关系的方式, 也是能够达到代码重用的效果。组合并没有涉及到特殊的语法(诸如 extends 这样的关键字),仅仅是将一个类的实例作为另外一个类的字段。
继承表示对象之间是is-a的关系,比如:狗是动物,猫是动物
组合表示对象之间是has-a的关系,比如:汽车
// 轮胎类 class Tire{ // ... } // 发动机类 class Engine{ // ... } // 车载系统类 class VehicleSystem{ // ... } class Car{ private Tire tire; // 可以复用轮胎中的属性和方法 private Engine engine; // 可以复用发动机中的属性和方法 private VehicleSystem vs; // 可以复用车载系统中的属性和方法 // ... } // 奔驰是汽车 class Benz extend Car{ // 将汽车中包含的:轮胎、发送机、车载系统全部继承下来 }组合和继承都可以实现代码复用,应该使用继承还是组合,需要根据应用场景来选择,一般建议:能用组合尽量用组合。
8.不同包中子类的访问 (protect)
package demo2; public class TestDemo1 { protected int a = 99; }public class Test extends TestDemo1{ public void func() { TestDemo1 testDemo1 = new TestDemo1(); //System.out.println(testDemo1.a); //无法使用 System.out.println(super.a); } public static void main(String[] args) { Test test = new Test(); test.func();super不能在静态方法中使用,所以借助func访问