当前位置: 首页 > news >正文

十、Java面向对象编程入门指南:继承与多态

面向对象编程(OOP)有三大核心特性封装、继承和多态

本节主要讲解的是继承与多态,其中继承和多态是实现代码复用和灵活性的关键。

补充:封装知识点博客在——九、Java类核心语法:构造器、this、封装与static详解


目录

一、继承(Inheritance)

1. 什么是继承?

2. 为什么需要继承?

3. 基本语法

4. 继承示例

5. 继承的权限修饰符

6. 继承后的特点

7. 继承的方法重写(Override)及应用场景

8. 子类构造器的特点及应用场景

9. 继承中构造器的 `this()` 调用(兄弟构造器)

二、多态(Polymorphism)

1. 认识多态

2. 多态的好处和存在的问题

3. 多态的类型转换问题

总结


一、继承(Inheritance)

1. 什么是继承?

  • 继承是一种类与类之间的关系允许一个类(子类)"继承"另一个类(父类)的属性和方法同时可以添加自己的独特功能。 

2. 为什么需要继承?

  1. 减少重复代码:多个类有共同属性时,只需在父类定义一次
  2. 建立类的层级关系:使代码结构更清晰
  3. 为多态奠定基础

3. 基本语法

使用 `extends` 关键字实现继承:

// 父类:定义共性属性和方法
class 父类名 {属性;方法;
}// 子类:继承父类,并可扩展新功能
class 子类名 extends 父类名 {// 新增属性和方法(扩展)// 重写父类方法(修改)
}

4. 继承示例

// 父类:动物(共性)
class Animal {String name;void eat() {System.out.println(name + "在吃东西");}
}// 子类:狗(继承共性,添加个性)
class Dog extends Animal {void bark() { // 新增方法System.out.println(name + "汪汪叫");}
}// 使用
public class Test {public static void main(String[] args) {Dog dog = new Dog();dog.name = "旺财"; // 继承父类的属性dog.eat(); // 继承父类的方法dog.bark(); // 子类自己的方法}
}

5. 继承的权限修饰符

用来限制类中的成员(成员变量,成员方法,构造器)能够被访问的范围:

  • public:任意位置
  • protected:本类,同一个包中的类,子孙类中
  • 默认(缺省):本类,同一个包中的类
  • private:只能本类

关键说明:

  • `private`:父类私有成员,子类完全不可见(即使同包),需通过父类的 `public/protected` 方法间接访问。
  • `protected`:专为继承设计,允许不同包的子类访问(体现“继承可见性”)。

示例:

class Parent {private int a = 1; // 子类不可见int b = 2; // 同包子类可见,不同包不可见protected int c = 3; // 所有子类可见public int d = 4; // 所有类可见
}class Child extends Parent {void test() {// System.out.println(a); // 错误:private不可见System.out.println(b); // 同包时可见System.out.println(c); // 所有子类可见System.out.println(d); // 可见}
}

6. 继承后的特点

1)单继承限制:Java 只允许一个子类有一个直接父类,但支持多层继承(如 `A → B → C`C 是 B 的子类,B 是 A 的子类),其Object类是Java中所有类的祖宗类。

public class Test {public static void main(String[] args) {//目标:继承的注意事项和特点。A a=new A();}
}//1、java的类只能是单继承,不支持多继承,但支持多层继承。
//2、一个类要么直接继承Object,要么默认继承Object。
class A extends Object{
}
class B extends A{
}
class C extends B{
}

2)子类拥有父类非私有成员:子类可直接使用父类的 `public/protected/默认(同包)` 成员,但不能修改父类的私有成员

3)就近原则:子类没有找子类,子类没有找父类,父类没有就报错。

public class Test2 {//目标2:继承后子类的访问特点:就近原则。public static void main(String[] args) {Zi zi=new Zi();zi.show();}
}class Fu{String name="Fu的name";public void run(){System.out.println("Fu的run()");}
}class Zi extends Fu{String name="Zi的name";public void show(){String name="show()的name";System.out.println(name);//show()的name(就近原则)System.out.println(this.name);//Zi的name(当前类的对象)System.out.println(super.name);//Fu的name(父类的对象)run();//子类的run ()super.run();//父类的run()}public void run(){System.out.println("Zi的run()");}
}

7. 继承的方法重写(Override)及应用场景

  • 定义:子类对父类的方法进行重新实现(方法名、参数列表、返回值完全相同),以满足子类的特有需求。

重写规则

  1. 方法名、参数列表(类型、顺序、数量)必须与父类完全一致
  2. 返回值类型:必须与被重写方法的返回值类型一样,或范围更小。
  3. 访问权限:子类方法权限不能低于父类(如父类是 `protected`,子类可改为 `public`)。
  4. 异常:子类方法抛出的异常不能多于/严于父类。
  5. 注解:建议添加 `@Override`(编译期检查是否符合重写规则,避免拼写错误)。
  6. 私有方法和静态方法不能被重写。

示例:

public class Test {public static void main(String[] args) {//目标:认识方法重写和应用场景。Cat cat = new Cat();cat.cry();}
}
class Cat extends Animal{//方法重写:方法名称,形参列表必须相同时才可以进行方法重写。//方法重写规范:声明不变,重新实现。//方法重写的校验注解(标志):@Override//要求方法名称和形参列表必须与被重写的方法一致,否则报错!//更安全,可读性、可维护性更高@Overridepublic void cry() {System.out.println("小猫喵喵叫");}
}
class Animal {public void cry() {System.out.println("动物叫");}
}

应用场景:

  1. 当父类的方法实现不满足子类需求时,通过重写定制子类行为。
  2. 子类重写Object类的toString()方法,以便返回对象的内容。

示例:

public class Test2 {public static void main(String[] args) {//目标:方法重写的常见应用场景://子类重写Object的toString方法,以便获取对象信息Student s=new Student("彭于晏",18,'男');//System.out.println(s.toString());默认打印对象所在地址System.out.println(s);//注意1:直接输出对象,默认会自动调用toString方法(可省不写),返回对象地址信息。//注意2:输出对象的实际地址无意义,要输出对象信息,需要重写Object的toString方法。}
}class Student{private String name;private int age;private char sex;@Overridepublic String toString() {return "Stuent{" +"name='" + name + '\'' +", age=" + age +", sex=" + sex +'}';}public Student() {}public Student(String name, int age, char sex) {this.name = name;this.age = age;this.sex = sex;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public char getSex() {return sex;}public void setSex(char sex) {this.sex = sex;}
}

小窍门:快速生成toString()方法

1)右键单击鼠标->生成

2)点击toString()

3)选中所有添加的->点击确定即可

`@Override`(编译期检查是否符合重写规则,避免拼写错误)

8. 子类构造器的特点及应用场景

  • 核心特点:子类构造器必须先调用父类构造器,再执行自己的逻辑。
  • 构造器执行顺序:父类构造器 → 子类构造器(确保父类成员先初始化,子类可安全使用)。
  1. 默认调用:若子类构造器中未显式调用父类构造器,编译器会自动添加 `super()`调用父类无参构造)。
  2. 显式调用:若父类没有无参构造器(如父类只定义了有参构造),子类必须显式用 `super(参数)` 调用父类的有参构造,且 `super(参数)` 必须放在子类构造器的第一行

示例:

public class Test {public static void main(String[] args) {//目标:认识子类构造器的特点和应用场景。//子类构造器都会必须先调用父类构造器,再继续执行子类构造器的代码。Zi zi=new Zi();}
}class Zi extends Fu {public Zi(){super();//默认存在的(父类无参构造器)//super(666);指定调用父类有参构造器System.out.println("子类无参构造器");}
}class Fu {public Fu(){System.out.println("父类无参构造器");}public Fu(int a){System.out.println("父类有参构造器");}
}

子类构造器调用父类构造器的应用场景:

//子类
public class Teacher extends People{private String skill;public Teacher() {}public Teacher(String name, int age, char sex, String skill) {//子类构造器调用父类构造器应用场景。//可以把子类继承父类这部分的数据也完成初始化赋值。super(name, age, sex);this.skill = skill;}public String getSkill() {return skill;}public void setSkill(String skill) {this.skill = skill;}
}

9. 继承中构造器的 `this()` 调用(兄弟构造器)

`this()` 用于在同一个类的构造器中调用其他构造器(兄弟构造器),目的是减少代码重复

规则:

  1. `this()` 必须放在构造器的第一行
  2. `this()``super()` 不能同时使用两者都需在第一行)。

示例:

public class Student {private String name;private int age;private char sex;private String schoolName;public Student() {}public Student(String name, int age, char sex) {//this调用兄弟构造器。//注意:super()和this()不能同时使用,并且都需要写在构造器的第一行。this(name, age, sex, "清华大学");this.name = name;this.age = age;this.sex = sex;}public Student(String name, int age, char sex, String schoolName) {super();//必须写在构造方法第一行this.name = name;this.age = age;this.sex = sex;this.schoolName = schoolName;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", sex=" + sex +", schoolName='" + schoolName + '\'' +'}';}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public char getSex() {return sex;}public void setSex(char sex) {this.sex = sex;}public String getSchoolName() {return schoolName;}public void setSchoolName(String schoolName) {this.schoolName = schoolName;}
}

二、多态(Polymorphism)

1. 认识多态

定义:

  1. 多态“同一行为,不同实现”:同一方法调用,因对象不同而产生不同结果
  2. 多态是在继承/实现情况下的一种现象,表现为:对象多态,行为多态

实现条件:

  1. 存在继承关系子类继承父类)。
  2. 子类重写父类的方法。
  3. 向上转型:父类引用指向子类对象`父类类型 变量 = new 子类类型()`)。

示例:

class Animal {void makeSound() {System.out.println("动物叫");}
}class Dog extends Animal {@Overridevoid makeSound() {System.out.println("汪汪叫");}
}class Cat extends Animal {@Overridevoid makeSound() {System.out.println("喵喵叫");}
}public class Test {public static void main(String[] args) {// 向上转型:父类引用指向不同子类对象Animal a1 = new Dog();Animal a2 = new Cat();a1.makeSound(); // 输出:汪汪叫(实际调用Dog的方法)a2.makeSound(); // 输出:喵喵叫(实际调用Cat的方法)}
}

多态的两种形式:

  1. 编译时多态:方法重载(Overload):同一类中方法名相同,参数不同(编译期确定调用哪个方法)。
  2. 运行时多态:方法重写(Override)配合向上转型(运行期确定调用哪个方法,OOP 核心多态)。

2. 多态的好处和存在的问题

好处:

  1. 代码扩展性:新增子类时,无需修改依赖父类的代码。
  2. 代码灵活性:用统一的父类引用处理不同子类对象,简化逻辑。
  3. 解耦合:右边对象是解耦合的。

存在的问题:

  1. 无法直接调用子类特有方法:父类引用只能调用父类中定义的方法,子类特有方法需转型后才能调用
  2. 属性无多态性:多态只针对方法,属性的值由编译时类型(左边)决定。
  3. 可能抛出转型异常:向下转型时若类型不匹配,会抛出 `ClassCastException`

示例:

public class Test {public static void main(String[] args) {//目标:认识多态的代码。//1、多态的好处:右边对象是解耦合的。Animal a1 = new Tortoise();a1.run();//方法:编译看左边,运行看右边//a1.shinkHead();//多态下不能调用子类独有功能。(除了强制转换)Wolf w = new Wolf();go(w);Tortoise t = new Tortoise();go(t);}//宠物游戏:所有动物都可以用这个方法开始跑步。//2、多态的好处:父类类型的变量作为参数,可以接受一个子类对象。public static void go(Animal a) {System.out.println("开始了!");a.run();//a1.shinkHead();//报错,多态下不能调用子类独有功能。}
}

3. 多态的类型转换问题

多态中的类型转换分为向上转型向下转型目的是在“统一处理”和“使用子类特有功能”之间切换

1)向上转型(自动转换):

  • 语法:`父类类型 变量 = new 子类类型();`
  • 特点:安全(子类是父类的一种),但会“丢失”子类特有方法(只能调用父类定义的方法)。
  • 示例:`Animal a = new Dog();`(正确,无需显式转换)。

2)向下转型(强制转换):

  • 语法:`子类类型 变量 = (子类类型) 父类引用;`
  • 特点:需显式转换,用于恢复子类特有方法,但可能因类型不匹配抛出 `ClassCastException`
  • 解决:先用 `instanceof` 判断类型是否匹配(返回 `true` 则安全转换)。

示例:

public class Test {public static void main(String[] args) {//目标:认识多态的代码。//1、多态的好处:右边对象是解耦合的。Animal a1 = new Tortoise();a1.run();//方法:编译看左边,运行看右边//a1.shinkHead();//多态下不能调用子类独有功能。(除了强制类型转换)//强制类型转换:可以解决多态下调用子类独有功能。Tortoise t = (Tortoise)a1;t.shrinkHead();//有继承关系就可以强制类型转换,编译阶段不会报错!//运行时会出现类型转换异常:ClassCastException//Wolf w = (Wolf)a1;//        Wolf w = new Wolf();
//        go(w);
//
//        Tortoise t = new Tortoise();
//        go(t);}//宠物游戏:所有动物都可以用这个方法开始跑步。//2、多态的好处:父类类型的变量作为参数,可以接受一个子类对象。public static void go(Animal a) {System.out.println("开始了!");a.run();//a1.shinkHead();//报错,多态下不能调用子类独有功能。//java建议强制类型转换前,应该判断对象的真实类型:对象 instanceof 类型,再进行强制类型转换。if(a instanceof Tortoise) {Tortoise t = (Tortoise)a;t.shrinkHead();}else if(a instanceof Wolf) {Wolf w = (Wolf)a;w.eatSheep();}}
}

总结

  1. 继承:通过 `extends` 实现代码复用,需注意权限修饰符构造器调用规则
  2. 多态:基于继承重写,通过向上转型实现“同一行为不同表现”,提升代码扩展性,但需注意类型转换问题。 
http://www.dtcms.com/a/352213.html

相关文章:

  • 利用 OpenTelemetry 建设尾部采样
  • 大模型全栈学习路线:4 - 6 个月从入门到实战,打通技术与业务闭环
  • [灵动微电子 霍尔FOC MM32BIN560C]从引脚到应用
  • 《黑客帝国》解构:白帽黑客的极客思维宇宙
  • vue3写一个简单的时间轴组件
  • 【python】python利用QQ邮箱SMTP发送邮件
  • k8s pod resources: {} 设置的含义
  • 支持向量机(第二十九节课内容总结)
  • TensorFlow 面试题及详细答案 120道(61-70)-- 高级特性与工具
  • 如何在项目中集成XXL-JOB
  • uniapp 引入使用u-view 完整步骤,u-view 样式不生效
  • 重复文件删除查找工具 Duplicate Files Search Link v10.7.0
  • 【深度学习】Transformer 注意力机制与 LoRA target_modules 详解
  • 如何安装 VS2019 和 .NET Core SDK 2.2.301(winx64)?完整操作步骤(附安装包下载)
  • 基于YOLOv11训练无人机视角Visdrone2019数据集
  • 区块链技术探索与应用:从密码学奇迹到产业变革引擎
  • 从入门到理解:支持向量机的核心原理与实战思路
  • 计数组合学7.21(有界部分大小的平面分拆)
  • 车载铁框矫平机:一辆“会熨衣服”的工程车
  • 高性能异步任务编排框架:Gobrs-Async
  • 【项目】深房数据通——深圳房价可视化系统
  • 嵌入式第三十七课!!!TCP机制与HTTP协议
  • 【学习笔记】系统时间跳变会影响time接口解决措施
  • 相关法律、法规知识(五)
  • 单层膜可改善无铅钙钛矿太阳能电池
  • Java 企业应用单点登录(SSO)实现方案详解
  • 创维桌面云终端-创维LB2002-白盒-晶晨S905L3A-2+8G-线刷刷机包
  • 实验2 天气预报
  • Ultra Accelerator Link(UALink)Consortium
  • 网站测试报告:WEB应用反CSRF的本质与防御机制