一、继承的概念
- 继承是Java语言的三大特性之一,通过子类继承父类的方式,让代码方便扩展和管理。
- Java中,类与类之间可以存在继承关系,处于继承关系的两个类,通常简称为父类和子类。
- 子类使用extends关键字继承父类,extends单词是扩展的意思,表意为子类是对父类的扩展。
- Java中的类只能单一继承,例如C继承B,B继承A,可以多层级继承,但每个类只能继承一个父类
- Java中所有类都隐式继承了Object类,Object类是所有类的根系父类
父子类继承语法示例
- 创建一个父类Parent
- 创建一个子类Child,使用extends关键字继承Parent类
public class Parent { /* 父类属性和方法 */ }
public class Child extends Parent { /* 新增属性和方法 */ }
二、继承的特点(代码实例)
2.1、子类继承父类所有非私有(private)属性和方法
- 子类无需显式定义父类中存在的方法和字段,可直接调用
- 父子类继承中,会优先在子类中或局域函数方法内寻找对应变量,最后在父类中寻找(如果存在多层级继承,则根据层级往上寻找),如果都没有则直接报错。
/** 创建一个父类*/
public class Parent {
public int money = 1_0000_0000;
public void say(){
System.out.println("说话");
}
}
/** 子类*/
public class Child extends Parent { }
/** 测试类*/
public class Test {
public static void main(String[] args) {
Child child = new Child();
System.out.println(child.money); //输出结果:1_0000_0000
System.out.println(child.say()); //输出结果:说话
}
}
2.2、使用super关键字调用父类非私有(private)构造函数,属性,方法
/** 创建一个父类*/
public class Parent {
public String name = "father";
}
/** 子类*/
public class Child extends Parent {
public String name = "son";
public void say(){
String name = "函数内字段"
System.out.println(super.name);
System.out.println(this.name);
System.out.println(name);
}
}
/** 测试类*/
public class Test {
public static void main(String[] args) {
Child child = new Child();
System.out.println(child.say());
// 最终输出
// father
// son
// 函数内字段
}
}
super和this的区别
- this指向当前类引用标识(当前类名空间)
- 用于访问本类的方法和字段
- this()访问本类构造函数
- this.属性字段(访问本类)
- this.函数方法(访问本类)
- super指向直接继承的父类引用(父类空间)
- super()访问父类的构造函数
- super.属性字段(访问父类)
- super.函数方法(访问父类)
2.3、方法重写
- 子类重新解释父类的函数行为(在实际业务场景中,方法重写很常见/划重点)
- 子类重写父类的方法通过@Override修饰方法头实现【规范】
- 父类中private修饰的方法,构造函数,static,不可重写
- 重写的方法其访问修饰符权限不能更低一级
代码实例
// 创建一个手机类
public class Phone {
public void call(String name) {
System.out.println("给" + name + "打电话");
}
}
// 创建一个新版本手机类...
public class NewPhone extends Phone {
@Override
public void call(String name) {
System.out.println("开启视频功能");
super.call(name);
}
}
// 创建测试类
public class Test {
public static void main(String[] args) {
Phone p = new Phone();
p.call("张三");
NewPhone np = new NewPhone();
np.call("张三");
// 输出结果:
// 给张三打电话
// 开启视频功能
// 给张三打电话
}
}
2.4、父类引用指向子类对象(多态,向上转型)
- 即父类的引用地址不仅可以指向父类对象,也可以指向子类对象(多态特性)
public class Parent { /* 父类属性和方法 */ }
public class Child extends Parent { /* 新增功能 */ }
/** 测试类*/
public class Test {
public static void main(String[] args) {
// 父类引用指向父类对象
Parent child = new Parent();
// 创建语法正确
// Parent父类引用地址实际指向Child子类对象 new Child()
Parent child = new Child();
}
}
- 代码执行时,JVM编译器会根据实际对象类型调用方法(多态,不同的对象呈现不同的行为)
- 多态必须在继承关系中体现
- 子类必须对父类中的方法重写
- 通过父类引用调用子类中重写的方法(JVM编译器会根据实际对象类型调用对应方法,Java中的隐式转换【向上转型】)
/** 创建父类Animal */
class Animal {
// 定义一个行为特征“吃” 提供子类覆盖重写
public void eat() {}
}
/** 创建子类Dog */
class Dog extends Animal {
@Override
public void eat() {
System.out.println("吃狗粮");
}
}
/** 创建子类Cat */
class Cat extends Animal {
@Override
public void eat() {
System.out.println("吃猫粮");
}
}
/** 测试类*/
public class Test {
public static void main(String[] args) {
Animal dog = new Dog();
dog.eat();
Animal cat = new Cat();
cat.eat();
// 输出结果:
// 吃狗粮
// 吃猫粮
}
}
- 父类引用无法访问子类独有方法,如果需要访问子类独有方法,需要强制类型转换(向下转型)
/** 创建父类Animal */
class Animal {}
/** 创建子类Dog */
class Dog extends Animal {
public void wang() {
System.out.println("汪汪汪");
}
}
/** 测试类*/
public class Test {
public static void main(String[] args) {
Animal dog = new Dog();
dog.wang(); // 编译错误!父类引用无法访问子类特有方法
// 强制类型转换
((Dog) dog).wang(); // 正确
}
}
三、Java继承的特点总结(补充)
- final继承限制:被final修饰的类/方法不可继承,重写
- final是Java的一种修饰符,可以用来修饰类,变量,属性,方法
- final修饰的变量命名最好大写(规范)
- final修饰方法时,该方法不可被重写
- final修饰属性时,该属性字段值不可被修改(常量字段)
- final修饰类时,该类不可被继承,通常这个类被称为封装类
- 业务需求需要多继承时,可通过接口类实现
- 访问控制符限制
- private成员不可被子类继承(不能直接继承),子类中也无法直接使用访问,通常会封装Get和Set方法访问
- protected专为继承设计,仅供子类访问
- default默认修饰符,在同一个包下可以访问
- 继承中构造函数的继承特点
- 因为子类会使用父类的属性和函数,所以在子类创建时,会先初始化创建父类。
- 子类构造中没有显示调用父类构造函数时,子类会默认访问父类的无参构造函数(隐式调用)
- 每个类在创建的时候默认有一个无参构造函数(隐式创建),当类中显式创建构造函数时,默认的无参构造函数会失效,子类在创建时如果没有找到父类对应的构造函数,根据具体编码情况,可能会出现代码行报错,或编译出错。
- 父类没有无参构造函数,子类构造函数中没有使用super显式调用父类其他构造函数时,会出现编译报错:找不到父类的无参构造函器。
- 方法重写和方法重载的异同
- 方法重载在同一个类中实现,方法同名,参数列表不同,返回值类型可以不同
- 方法重写在父子类中实现,方法同名,参数列表相同,返回值类型必须相同,或是返回类型的子类
- 重写的方法需要用@Override修饰
- 重写方法不能缩小被重写方法的访问权限。
- 父子类继承时,代码初始化顺序
- 执行父类静态代码块{},子类静态代码块{}
- 执行父类实例代码块,父类构造方法
- 执行子类实例代码块,子类构造方法
- 最终子类实例化对象时,父类和子类的静态代码块不再执行
四、结语
这篇文档仅讨论Java继承的语法基础和理论基础,不涉及继承的实际应用场景。在实际业务场景中,继承几乎成为每个开发者的习惯,作为初学者需要多多代码实践,反复练习。
Java继承在实际业务场景中的代码案例可以参考下方蓝色字体链接
02.Java继承在实际业务场景中的应用【代码案例】-CSDN博客
如果以后功成名就,会不会感谢此刻苦熬技术文档的你?