前言:
在面向对象编程中,继承是实现代码复用,建立类之间层次关系的重要机制。通过继承,我们可以将多个类的公共部分抽取到父类中,子类只需要关注自己特有的属性和行为,从而让代码更加简洁,易于维护。
本文将从为什么需要继承讲起,详细解析java中继承的语法,成员访问规则,super关键字,构造方法互动,protected访问权限,代码块初始化顺序,final的限制作用。
目录
前言:
一.为什么需要继承——代码的复用
二.继承的基本语法
2.1什么是继承?
2.2 extends关键字
三.父类成员访问规则
3.1成员变量访问:就近原则
3.2成员方法访问
四.super关键字:显示访问父类成员
五.子类构造方法与super()调用链
六.super和this的对比
七.继承关系下的初始化顺序(时间轴)
八.protected访问权限
九.final关键字:继承的“终结者”
总结:
一.为什么需要继承——代码的复用
设想我们有两个类:Dog和Cat,它们都有姓名,年龄,体重,都会吃饭睡觉。如果不使用继承,我们需要在两个类中重复编写同样的字段和方法:
//Dog.java public class Dog{ String name; int age ; float weight; public void eat(){ System.out.println(name+"正在吃饭"); } public void sleep(){ System.out.println(name+"正在睡觉"); } void bark() { System.out.println(name + "汪汪汪~~~"); } } //Cat.java public class Cat { String name; int age; float weight; public void eat() { System.out.println(name + "正在吃饭"); } public void sleep() { System.out.println(name + "正在睡觉"); } void mew() { System.out.println(name + "喵喵喵~~~"); } }大量的代码重复,这不仅导致代码臃肿,也增加了维护成本——如果要修改动物的通用行为,必须要去修改每个类。
面向对象编程为我们提供了解决方案:继承。继承允许我们抽取共性,形成层次结构,让子类自动拥有父类的特性,从而实现“站在巨人的肩膀上”编程。
二.继承的基本语法
2.1什么是继承?
继承是面向对象程序设计中实现代码复用的重要手段。它允许程序员在保持原有类特性的基础上进行拓展,增加新功能。
父类:被继承的类,代表共性。
子类:继承父类的类,代表个性。
2.2 extends关键字
在Java中,使用extends关键字来表示继承关系。
语法:
修饰符 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 + "喵喵喵~~~"); } } // 测试 public class Test { public static void main(String[] args) { Dog dog = new Dog(); dog.name = "旺财"; dog.eat(); // 继承自Animal dog.bark(); // 自己的方法 } }我们通常会将字段设为private,并通过protected的getter/setter方法来访问,以符合严格的封装原则。
注意:子类应添加自己特有的成员,否则单纯继承无意义。
三.父类成员访问规则
3.1成员变量访问:就近原则
无同名变量:子类直接访问继承来的变量。
有同名变量:遵循就近原则,优先访问子类的自己的变量。
public class Base{ int a=10; int b=20; } public class Derived extends Base{ int a=100; //同名变量 public void method(){ System.out.println(a); //输出子类的a 100 System.out.println(b); //输出父类的b 20 } }若想强制访问父类的同名成员,需要使用super关键字。
3.2成员方法访问
方法名不同:先找子类,再找父类,找不到编译报错。
方法名:相同(重写/重载):
若参数列表不同(重载),根据调用参数决定。
若参数列表完全相同(重写),则有限调用子类的方法(多态特性)。
重写(Override)要求方法名,返回类型,参数列表完全一致,子类访问权限不能严于父类。
四.super关键字:显示访问父类成员
当子类和父类出现相同名字的成员时,super可以帮我们访问父类的成员。
public class Derived extends Base { int a = 100; public void method() { System.out.println(a); // 子类自己的 a System.out.println(super.a);// 父类的 a super.methodA(); // 调用父类方法 } }super的三大用法
1.访问父类的成员变量:super.父类变量名
2.访问父类的成员方法:super.父类方法名()
3.调用父类的构造方法:super(参数列表)
五.子类构造方法与super()调用链
核心原则:先有父,再有子
子类对象包含两部分:从父类继承下俩的成员+自己新增的成员。因此,构造子类对象之前必须先构造父类对象。
规则详解:
默认调用:如果子类构造方法中没有显式写super(),编译器会自动插入super,即调用父类的无参构造方法。
显式调用:如果父类没有无参构造(例如定义了带参构造),子类必须再第一行显式调用super(参数)。
唯一性和顺序:super()必须是子类构造方法中的第一条语句,且只能出现一次。
public class Base{ public Base(int a ){ System.out.println("Base构造"+a); } } public class Derived extends Base{ public Derived(){ super(10); //必须显式调用,且位于第一行 System.out.println("Derived 构造") } }super()只能在子类构造方法中使用,且必须是第一条语句。
super()和this()不能同时出现(因为都要在第一行)。
若父类没有无参构造,子类未显式调用父类其他构造方法,编译报错。
六.super和this的对比
| this | super | |
| 本质 | 当前对象的引用 | 子类对象中父类部分的引用 |
| 访问范围 | 本类的成员 | 父类的成员 |
| 构造方法的调用 | this(...)调用本类其他 构造方法 | super(...)调用父类构造方法 |
| 是否必须第一行 | 是,与super()排斥 | 是,与this排斥 |
| 是否默认存在 | 除非手动调用this | 子类构造方法中默认有super() 父类有无参构造时 |
七.继承关系下的初始化顺序(时间轴)
“静态优先,父先子后” 父静-->子静-->父实-->父构-->子实-->子构
静态代码块:只执行一次,在类加载的时执行。父类优先于子类。
实例代码块:每次创建对象时。父类的实例块在父类构造方法之前执行
构造方法:最后执行。
class Person { public Person(String name, int 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 Test { public static void main(String[] args) { new Student("张三", 19); System.out.println("============"); new Student("李四", 20); } }八.protected访问权限
protected允许:
同一个包中的任何类访问。
不同包中的子类访问从父类继承下来的protected成员。
注意:在不同包的子类中,不能通过其他子类对象访问其protected成员
// 包 demo1 public class Base { protected int b = 100; } // 包 demo2 import demo1.Base; public class Derived extends Base { void test() { System.out.println(b); // OK,继承下来的 protected Base obj = new Base(); // System.out.println(obj.b); // 错误!不同包中不能通过父类对象访问 protected } }九.final关键字:继承的“终结者”
为了防止类被继承或方法被重写,java提供了final。
修饰类:该类不能被继承(如String类)
修饰方法:该方法不能被重写。
修饰变量:变量为常量,不能被修改。
final class Animal { } // 不可被继承 // class Dog extends Animal { } // 编译错误 class Parent { final void method() { } } class Child extends Parent { // void method() { } // 编译错误,不能重写 final 方法 }总结:
语法基础:extends和super的使用
底层逻辑:构造方法的调用链和初始化顺序
访问控制:protected允许不同包子类访问,但不能通过其他子类对象访问。
限制手段:final可以限制继承,重写,修改