
⭐️个体主页:Kidd
📚所属栏目:java

在上一篇《Java面向对象基础:类与对象的本质、创建方式与构造方法》中,我们掌握了类与对象的基础概念、对象创建流程及构造方法的核心用法,这是理解面向对象编程的基石。而"封装、继承、多态"作为Java面向对象的三大核心特性,是实现代码复用、扩展与解耦的关键。本文将从"是什么-为什么-怎么用"的逻辑出发,深度解析三大特性的底层逻辑,结合丰富的代码示例拆解实现细节,并通过综合实战案例巩固知识点,帮助读者从"基础入门"迈向"进阶应用"。
一、封装:数据安全与代码隐藏的核心手段
在基础篇的案例中,我们直接使用public修饰成员变量,允许外部通过"对象名.成员变量"直接修改数据。这种方式虽然简单,但会导致数据安全性问题(比如给年龄赋值为负数),同时也会让代码耦合度极高(外部直接依赖对象的内部结构)。而"封装"正是为解决这些问题而生。
1.1 封装的定义与核心思想
封装(Encapsulation)是指将对象的属性(成员变量)和操作属性的方法(成员方法)绑定在一起,同时隐藏对象的内部实现细节,只对外提供统一的访问接口。其核心思想是"隐藏细节、暴露接口",目的是:
-
保证数据安全性:防止外部直接修改对象的核心数据,通过方法对数据的访问和修改进行控制(比如校验数据合法性)。
-
降低代码耦合度:外部只需调用提供的接口,无需关注对象内部的实现逻辑,后续内部逻辑修改时,不影响外部调用。
-
提高代码可维护性:将数据和操作集中在类内部,便于统一管理和修改。
1.2 封装的实现步骤
在Java中,封装的实现主要依赖"访问修饰符"和"getter/setter方法",具体步骤如下:
-
私有化成员变量:使用
private访问修饰符修饰成员变量,禁止外部直接访问。 -
提供公共访问接口:定义公共的(
public)getter方法和setter方法,用于外部访问和修改成员变量。-
getter方法:用于获取成员变量的值,命名规范为
get+成员变量名(首字母大写)(如getName());如果成员变量是boolean类型,getter方法命名可改为is+成员变量名(首字母大写)(如isStudent())。 -
setter方法:用于修改成员变量的值,命名规范为
set+成员变量名(首字母大写)(如setAge()),方法参数为要设置的值,且在方法内部可添加数据校验逻辑。
-
1.3 封装的实现示例:Person类的封装改造
我们将基础篇中public修饰的Person类进行封装改造,添加数据校验逻辑:
java
public class Person {
// 1. 私有化成员变量(private修饰,外部无法直接访问)
private String name;
private int age;
private double height;
private boolean isStudent;
// 2. 提供公共的getter方法(获取成员变量值)
public String getName() {
return name;
}
// 3. 提供公共的setter方法(修改成员变量值,添加数据校验)
public void setName(String name) {
// 校验:姓名不能为空且长度不超过20
if (name != null && name.length() <= 20) {
this.name = name;
} else {
System.out.println("姓名不合法!");
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
// 校验:年龄必须在0-150之间
if (age >= 0 && age <= 150) {
this.age = age;
} else {
System.out.println("年龄不合法!");
}
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
// 校验:身高必须在0.5-2.5米之间
if (height >= 0.5 && height <= 2.5) {
this.height = height;
} else {
System.out.println("身高不合法!");
}
}
// boolean类型的getter方法,命名为isXXX
public boolean isStudent() {
return isStudent;
}
public void setStudent(boolean student) {
isStudent = student;
}
// 成员方法:介绍自己
public void introduce() {
String studentStr = isStudent ? "是" : "不是";
System.out.println("大家好,我叫" + name + ",今年" + age + "岁,身高" + height + "米," + studentStr + "学生。");
}
// 测试封装效果
public static void main(String[] args) {
Person zhangSan = new Person();
// 错误:无法直接访问private成员变量
// zhangSan.name = "张三";
// zhangSan.age = -25; // 非法年龄
// 通过setter方法设置值(会触发数据校验)
zhangSan.setName("张三");
zhangSan.setAge(-25); // 输出:年龄不合法!
zhangSan.setAge(25);
zhangSan.setHeight(1.75);
zhangSan.setStudent(true);
// 通过getter方法获取值
System.out.println("姓名:" + zhangSan.getName()); // 输出:张三
System.out.println("年龄:" + zhangSan.getAge()); // 输出:25
zhangSan.introduce(); // 输出:大家好,我叫张三,今年25岁,身高1.75米,是学生。
}
}
1.4 访问修饰符的完整解析
封装的实现依赖访问修饰符,Java提供了4种访问修饰符,用于控制类、成员变量、成员方法的访问权限,从严格到宽松依次为:private < 默认(无修饰符) < protected < public。其访问范围如下表所示:
| 访问修饰符 | 本类内部 | 同一包内 | 不同包的子类 | 任意位置 |
|---|---|---|---|---|
| private(私有) | 可访问 | 不可访问 | 不可访问 | 不可访问 |
| 默认(无修饰符) | 可访问 | 可访问 | 不可访问 | 不可访问 |
| protected(受保护) | 可访问 | 可访问 | 可访问 | 不可访问 |
| public(公共) | 可访问 | 可访问 | 可访问 | 可访问 |
| 使用建议: |
-
成员变量:优先使用
private修饰,通过getter/setter暴露访问接口。 -
成员方法:根据需求选择修饰符,对外提供的接口用
public,仅本类内部使用的用private,仅同一包内或子类使用的用protected或默认。 -
类:只能使用
public或默认修饰(外部类),内部类可使用任意修饰符。
二、继承:代码复用与体系构建的核心机制
在实际开发中,多个类可能会存在相同的属性和方法(比如Student类和Teacher类都有姓名、年龄属性,都有吃饭、睡觉方法)。如果每个类都重复定义这些内容,会导致代码冗余、维护成本高。而"继承"可以让一个类(子类)继承另一个类(父类)的属性和方法,实现代码复用,同时建立类之间的层级关系。
2.1 继承的定义与核心概念
继承(Inheritance)是指子类(Subclass)继承父类(Superclass)的非私有属性和方法,同时子类可以添加自己独有的属性和方法,或重写父类的方法。其核心概念:
-
父类:也叫超类、基类,是被继承的类,通常是多个子类的抽象共性(如Person类是Student、Teacher类的父类)。
-
子类:也叫派生类,是继承父类的类,具有父类的共性,同时拥有自身的特性(如Student类有学号、成绩属性,Teacher类有工号、薪资属性)。
-
继承的关键字:Java中通过
extends关键字实现继承,语法为class 子类名 extends 父类名 {}。
继承的优势:
-
代码复用:无需重复定义父类已有的属性和方法。
-
代码扩展:子类可在父类基础上添加新的属性和方法,实现功能扩展。
-
建立类体系:通过继承构建类的层级关系(如Person→Student→Undergraduate),便于代码管理和理解。
2.2 继承的实现示例:Student类继承Person类
基于前面封装后的Person类,实现Student类和Teacher类的继承:
java
// 父类:Person(已封装)
public class Person {
private String name;
private int age;
private double height;
// getter/setter方法
public String getName() {
return name;
}
public void setName(String name) {
if (name != null && name.length() <= 20) {
this.name = name;
} else {
System.out.println("姓名不合法!");
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age >= 0 && age <= 150) {
this.age = age;
} else {
System.out.println("年龄不合法!");
}
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
if (height >= 0.5 && height <= 2.5) {
this.height = height;
} else {
System.out.println("身高不合法!");
}
}
// 父类的普通方法
public void eat() {
System.out.println(name + "正在吃饭...");
}
public void sleep() {
System.out.println(name + "正在睡觉...");
}
}
// 子类:Student 继承 Person(extends关键字)
public class Student extends Person {
// 子类独有的属性(父类没有的)
private String studentId; // 学号
private double score; // 成绩
// 子类独有的getter/setter方法
public String getStudentId() {
return studentId;
}
public void setStudentId(String studentId) {
this.studentId = studentId;
}
public double getScore() {
return score;
}
public void setScore(double score) {
if (score >= 0 && score <= 100) {
this.score = score;
} else {
System.out.println("成绩不合法!");
}
}
// 子类独有的方法(父类没有的)
public void study() {
System.out.println(getName() + "(学号:" + studentId + ")正在学习...");
}
// 测试继承效果
public static void main(String[] args) {
Student zhangSan = new Student();
// 调用父类的setter方法设置属性(继承的方法)
zhangSan.setName("张三");
zhangSan.setAge(20);
zhangSan.setHeight(1.75);
// 调用子类的setter方法设置独有属性
zhangSan.setStudentId("2024001");
zhangSan.setScore(92.5);
// 调用父类的getter方法获取属性(继承的方法)
System.out.println("姓名:" + zhangSan.getName() + ",年龄:" + zhangSan.getAge());
// 调用父类的普通方法(继承的方法)
zhangSan.eat();
zhangSan.sleep();
// 调用子类的独有方法
zhangSan.study();
}
}
运行结果:
Plain
姓名:张三,年龄:20
张三正在吃饭...
张三正在睡觉...
张三(学号:2024001)正在学习...
关键说明:
-
子类继承父类后,可直接使用父类的
public和protected属性/方法(private属性/方法无法直接访问,需通过父类的public getter/setter访问)。 -
子类可添加独有的属性和方法,实现功能扩展(如Student类的studentId、score属性和study方法)。
-
Java是单继承语言:一个子类只能直接继承一个父类(即
extends后面只能跟一个类名),但一个父类可以有多个子类,子类也可以被其他类继承(形成多层继承,如Person→Student→Undergraduate)。
2.3 子类的构造方法与super关键字
在继承关系中,子类的构造方法有一个重要规则:子类的构造方法在执行时,会先默认调用父类的无参构造方法 。这是因为子类需要先初始化父类的属性和方法,才能初始化自身的属性和方法。如果父类没有无参构造方法(比如只定义了带参构造),则子类必须在构造方法的第一行通过super()显式调用父类的带参构造方法,否则编译错误。
核心知识点:super关键字的用法
super关键字用于访问父类的属性、方法和构造方法,主要有3种用法:
-
super():调用父类的构造方法,必须放在子类构造方法的第一行。如果不写,JVM会默认添加super()(调用父类无参构造)。 -
super.属性名:访问父类的非私有属性(当子类和父类有同名属性时,用于区分)。 -
super.方法名():调用父类的非私有方法(当子类重写父类方法时,用于调用父类的原方法)。
示例:子类构造方法与super的使用
java
// 父类:Person(定义带参构造,无无参构造)
public class Person {
private String name;
private int age;
// 父类的带参构造方法(无无参构造)
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("父类带参构造方法被调用");
}
// getter方法
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
// 子类:Student 继承 Person
public class Student extends Person {
private String studentId;
private double score;
// 子类的带参构造方法:必须显式调用父类的带参构造
public Student(String name, int age, String studentId, double score) {
super(name, age); // 调用父类的带参构造,必须放在第一行
this.studentId = studentId;
this.score = score;
System.out.println("子类带参构造方法被调用");
}
// 子类独有的方法
public void introduce() {
System.out.println("姓名:" + getName() + ",年龄:" + getAge() + ",学号:" + studentId + ",成绩:" + score);
}
public static void main(String[] args) {
Student zhangSan = new Student("张三", 20, "2024001", 92.5);
zhangSan.introduce();
}
}
运行结果:
Plain
父类带参构造方法被调用
子类带参构造方法被调用
姓名:张三,年龄:20,学号:2024001,成绩:92.5
注意事项:
-
super()和this()不能同时出现在子类构造方法中(因为两者都必须放在第一行)。 -
如果父类只有带参构造,子类必须显式调用父类的带参构造,否则编译错误(JVM无法找到父类的无参构造)。
2.4 方法重写(Override):子类对父类方法的改造
方法重写是指子类定义一个与父类同名、同参数列表、同返回值类型(或子类返回值类型是父类返回值类型的子类)的方法,用于覆盖父类的方法实现。其目的是:子类根据自身需求,对父类的方法进行"改造",实现个性化功能。
方法重写的规则(必须遵守,否则不是重写)
-
方法名必须完全相同。
-
参数列表必须完全相同(参数个数、类型、顺序都相同)。
-
返回值类型:
-
父类方法返回值类型是基本数据类型:子类重写方法的返回值类型必须与父类完全相同。
-
父类方法返回值类型是引用数据类型:子类重写方法的返回值类型可以是父类返回值类型的子类(称为"协变返回类型")。
-
-
访问修饰符:子类重写方法的访问权限不能低于父类方法的访问权限(如父类方法是
protected,子类可以是protected或public,但不能是private)。 -
异常抛出:子类重写方法抛出的异常不能多于父类方法抛出的异常(后续异常章节详细讲解)。
-
父类的
private方法不能被重写(因为子类无法访问父类的private方法)。 -
静态方法不能被重写(静态方法属于类,不属于对象,子类定义同名静态方法只是"隐藏"父类方法,不是重写)。
示例:Student类重写Person类的eat方法
java
// 父类:Person
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
// 父类的eat方法
public void eat() {
System.out.println(name + "正在吃家常菜...");
}
public String getName() {
return name;
}
}
// 子类:Student 重写eat方法
public class Student extends Person {
public Student(String name) {
super(name);
}
// 方法重写:重写父类的eat方法
@Override // 注解:用于校验是否是合法的重写(可选,但推荐添加)
public void eat() {
System.out.println(getName() + "正在吃食堂饭菜...");
}
// 测试重写效果
public static void main(String[] args) {
Person person = new Person("张三");
person.eat(); // 调用父类的eat方法:张三正在吃家常菜...
Student student = new Student("张三");
student.eat(); // 调用子类重写的eat方法:张三正在吃食堂饭菜...
}
}
关键说明:
-
@Override注解:是可选的,但推荐添加。它的作用是告诉编译器"这是一个重写方法",如果不符合重写规则,编译器会报错(避免误写)。 -
重写的核心是"多态的基础":当子类重写父类方法后,调用该方法时,会根据对象的实际类型(而非引用类型)执行对应的方法实现(后续多态章节详细讲解)。
三、多态:代码灵活扩展与解耦的终极武器
多态(Polymorphism)是面向对象三大特性的核心,它允许不同类的对象对同一消息做出不同的响应。简单来说:父类引用指向子类对象,调用方法时,根据子类对象的实际类型执行对应的方法实现。多态的核心价值是"解耦"------让代码不依赖于具体的子类类型,而是依赖于抽象的父类类型,从而实现代码的灵活扩展。
3.1 多态的实现条件
要实现多态,必须同时满足以下3个条件:
-
存在继承关系(子类继承父类,或实现接口------后续接口章节讲解)。
-
子类重写父类的方法(多态的核心是方法重写)。
-
父类引用指向子类对象(核心语法:
父类名 引用变量名 = new 子类名();)。
3.2 多态的实现示例:父类引用指向不同子类对象
基于前面的Person、Student、Teacher类,实现多态:
java
// 父类:Person
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
// 父类的方法(将被子类重写)
public void work() {
System.out.println(name + "正在工作...");
}
public String getName() {
return name;
}
}
// 子类1:Student 继承 Person,重写work方法
public class Student extends Person {
public Student(String name) {
super(name);
}
@Override
public void work() {
System.out.println(getName() + "正在学习(学生的工作)...");
}
}
// 子类2:Teacher 继承 Person,重写work方法
public class Teacher extends Person {
public Teacher(String name) {
super(name);
}
@Override
public void work() {
System.out.println(getName() + "正在讲课(老师的工作)...");
}
}
// 测试多态
public class PolymorphismDemo {
public static void main(String[] args) {
// 1. 父类引用指向Student对象(多态核心语法)
Person zhangSan = new Student("张三");
zhangSan.work(); // 执行Student的work方法:张三正在学习(学生的工作)...
// 2. 父类引用指向Teacher对象
Person liSi = new Teacher("李四");
liSi.work(); // 执行Teacher的work方法:李四正在讲课(老师的工作)...
// 3. 父类引用指向父类对象(非多态,执行父类方法)
Person wangWu = new Person("王五");
wangWu.work(); // 执行Person的work方法:王五正在工作...
// 4. 多态的灵活调用:定义方法接收父类引用,可传入任意子类对象
doWork(zhangSan); // 传入Student对象
doWork(liSi); // 传入Teacher对象
doWork(wangWu); // 传入Person对象
}
// 方法参数为父类类型,可接收任意子类对象(多态的核心价值:解耦)
public static void doWork(Person person) {
person.work();
}
}
运行结果:
Plain
张三正在学习(学生的工作)...
李四正在讲课(老师的工作)...
王五正在工作...
张三正在学习(学生的工作)...
李四正在讲课(老师的工作)...
王五正在工作...
关键分析:
-
多态的核心表现:
Person zhangSan = new Student("张三");中,引用变量zhangSan的类型是Person(父类),但指向的对象是Student(子类)。当调用zhangSan.work()时,执行的是Student类重写的work方法,而非Person类的work方法。 -
多态的价值:
doWork方法的参数是Person类型,无需为Student和Teacher分别定义方法,即可接收任意子类对象并调用对应的work方法。如果后续新增"Doctor""Engineer"等子类,只需让它们继承Person并重写work方法,无需修改doWork方法的代码,实现了"对扩展开放,对修改关闭"(开闭原则)。
3.3 多态中的类型转换:向上转型与向下转型
在多态中,父类引用指向子类对象时,存在"向上转型"和"向下转型"两种类型转换方式,这是多态应用中的重要知识点。
1. 向上转型(自动转型)
向上转型是指将子类对象赋值给父类引用变量,即父类名 引用 = new 子类名();。这是多态的核心语法,可以自动完成,无需手动转换。
特点:
-
安全:因为子类是父类的"子集",子类对象一定具备父类的属性和方法。
-
限制:向上转型后,父类引用只能访问父类的属性和方法,不能访问子类独有的属性和方法(因为编译器认为引用指向的是父类对象)。
java
Person zhangSan = new Student("张三"); // 向上转型(自动)
zhangSan.work(); // 可调用父类的方法(子类重写的)
// zhangSan.study(); // 错误:父类引用不能访问子类独有的study方法
2. 向下转型(强制转型)
向下转型是指将父类引用(原本指向子类对象)转换为子类引用变量,即子类名 引用 = (子类名) 父类引用;。这是强制转换,需要手动添加转型运算符。
使用场景:当需要通过父类引用访问子类独有的属性和方法时,需要进行向下转型。
特点:
-
有风险:如果父类引用指向的不是当前子类的对象,而是其他子类或父类的对象,强制转型会抛出
ClassCastException(类型转换异常)。 -
安全转型:可以通过
instanceof关键字先判断父类引用指向的对象是否是目标子类的实例,再进行转型(避免异常)。
示例:向下转型的使用与安全校验
java
public class CastDemo {
public static void main(String[] args) {
// 1. 向上转型
Person zhangSan = new Student("张三");
Person liSi = new Teacher("李四");
// 2. 向下转型:访问子类独有方法
// 安全转型:先通过instanceof判断
if (zhangSan instanceof Student) {
Student student = (Student) zhangSan; // 强制转型
student.study(); // 访问子类独有的study方法:张三正在学习...
}
// 不安全转型:父类引用指向Teacher对象,转型为Student
if (liSi instanceof Student) {
Student student = (Student) liSi;
student.study();
} else {
System.out.println("liSi不是Student的实例,无法转型");
}
// 3. 错误转型(运行时异常)
Person wangWu = new Person("王五");
// if (wangWu instanceof Student) { // 条件不成立,不会执行
// Student student = (Student) wangWu; // 运行时抛出ClassCastException
// }
}
}
// Student类的study方法(独有)
class Student extends Person {
public Student(String name) {
super(name);
}
public void study() {
System.out.println(getName() + "正在学习...");
}
@Override
public void work() {
// 重写方法
}
}
运行结果:
Plain
张三正在学习...
liSi不是Student的实例,无法转型
关键说明:
-
instanceof关键字:用于判断一个对象是否是某个类(或接口)的实例,语法为对象 instanceof 类名,返回值为boolean类型。 -
向下转型的核心原则:只有当父类引用原本指向的是目标子类的对象时,向下转型才安全 。通过
instanceof提前判断,可以避免类型转换异常。
3.4 多态的应用场景:工厂模式(简单实现)
多态的典型应用场景是"工厂模式",通过工厂类创建对象,客户端无需关注对象的具体创建细节,只需通过父类引用接收对象,实现代码解耦。下面通过简单工厂模式示例演示多态的实际价值:
java
// 父类:Product(产品抽象类)
public abstract class Product {
public abstract void produce(); // 抽象方法(子类必须重写)
}
// 子类1:Phone产品
public class Phone extends Product {
@Override
public void produce() {
System.out.println("生产手机...");
}
}
// 子类2:Computer产品
public class Computer extends Product {
@Override
public void produce() {
System.out.println("生产电脑...");
}
}
// 工厂类:ProductFactory(创建产品对象)
public class ProductFactory {
// 根据类型创建不同的产品对象(多态:返回父类引用)
public static Product createProduct(String type) {
if ("phone".equals(type)) {
return new Phone(); // 向上转型
} else if ("computer".equals(type)) {
return new Computer(); // 向上转型
} else {
throw new RuntimeException("不支持的产品类型");
}
}
}
// 客户端:使用产品(无需关注创建细节)
public class Client {
public static void main(String[] args) {
// 通过工厂创建产品对象(父类引用接收)
Product phone = ProductFactory.createProduct("phone");
Product computer = ProductFactory.createProduct("computer");
// 调用方法(多态:执行子类重写的方法)
phone.produce(); // 输出:生产手机...
computer.produce(); // 输出:生产电脑...
// 新增产品时,只需添加子类和修改工厂类,客户端代码无需修改(开闭原则)
// 示例:新增TV产品
Product tv = ProductFactory.createProduct("tv");
tv.produce();
}
}
// 新增的TV产品(扩展)
class TV extends Product {
@Override
public void produce() {
System.out.println("生产电视...");
}
}
核心价值:
-
解耦:客户端(Client)无需直接创建
Phone、Computer对象,只需调用工厂类的createProduct方法,降低了客户端与具体产品类的耦合。 -
扩展灵活:新增产品(如TV)时,只需创建TV类继承Product,并重写produce方法,再修改工厂类的判断逻辑即可,客户端代码无需任何修改,符合"开闭原则"。
四、综合实战:基于三大特性的学生管理系统优化
结合封装、继承、多态三大特性,对基础篇的"学生管理系统"进行优化,实现以下功能:
-
定义User抽象父类(封装用户共性:编号、姓名、年龄)。
-
定义Student、Teacher子类继承User,添加独有属性和方法,重写父类方法。
-
定义管理类(UserManager),使用多态实现对不同用户的统一管理(添加、查询、显示)。
实战代码实现
java
// 1. 抽象父类:User(封装共性)
abstract class User {
private String id; // 编号
private String name; // 姓名
private int age; // 年龄
// 构造方法
public User(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
// getter/setter方法(封装)
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age >= 0 && age <= 150) {
this.age = age;
} else {
System.out.println("年龄不合法!");
}
}
// 抽象方法:显示用户信息(子类必须重写)
public abstract void showInfo();
}
// 2. 子类:Student(继承User)
class Student extends User {
private double score; // 独有属性:成绩
// 构造方法
public Student(String id, String name, int age, double score) {
super(id, name, age); // 调用父类构造
this.score = score;
}
// getter/setter(封装)
public double getScore() {
return score;
}
public void setScore(double score) {
if (score >= 0 && score <= 100) {
this.score = score;
} else {
System.out.println("成绩不合法!");
}
}
// 重写父类抽象方法
@Override
public void showInfo() {
System.out.println("【学生信息】");
System.out.println("编号:" + getId());
System.out.println("姓名:" + getName());
System.out.println("年龄:" + getAge());
System.out.println("成绩:" + getScore());
System.out.println("------------------------");
}
// 独有方法:学习
public void study() {
System.out.println(getName() + "正在学习...");
}
}
// 3. 子类:Teacher(继承User)
class Teacher extends User {
private double salary; // 独有属性:薪资
// 构造方法
public Teacher(String id, String name, int age, double salary) {
super(id, name, age);
this.salary = salary;
}
// getter/setter(封装)
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
if (salary >= 0) {
this.salary = salary;
} else {
System.out.println("薪资不合法!");
}
}
// 重写父类抽象方法
@Override
public void showInfo() {
System.out.println("【教师信息】");
System.out.println("编号:" + getId());
System.out.println("姓名:" + getName());
System.out.println("年龄:" + getAge());
System.out.println("薪资:" + getSalary());
System.out.println("------------------------");
}
// 独有方法:讲课
public void teach() {
System.out.println(getName() + "正在讲课...");
}
}
// 4. 管理类:UserManager(多态实现统一管理)
class UserManager {
// 存储用户(多态:父类引用存储子类对象)
private List<User> userList = new ArrayList<>();
// 添加用户(多态:接收任意User子类对象)
public void addUser(User user) {
userList.add(user);
System.out.println("添加" + (user instanceof Student ? "学生" : "教师") + "成功!");
}
// 根据编号查询用户
public User findUserById(String id) {
for (User user : userList) {
if (user.getId().equals(id)) {
return user;
}
}
return null;
}
// 显示所有用户信息(多态:调用子类重写的showInfo方法)
public void showAllUsers() {
System.out.println("【所有用户信息】");
for (User user : userList) {
user.showInfo(); // 多态核心:根据对象实际类型执行对应方法
}
}
}
// 5. 测试类
public class StudentManagementSystem {
public static void main(String[] args) {
// 创建管理对象
UserManager userManager = new UserManager();
// 添加学生和教师(多态:父类引用指向子类对象)
User student1 = new Student("2024001", "张三", 20, 92.5);
User teacher1 = new Teacher("T2024001", "李四", 35, 8000.0);
userManager.addUser(student1);
userManager.addUser(teacher1);
// 显示所有用户信息
userManager.showAllUsers();
// 查询用户并调用独有方法(向下转型)
User findUser = userManager.findUserById("2024001");
if (findUser instanceof Student) {
Student student = (Student) findUser;
student.study(); // 调用学生独有方法
}
User findTeacher = userManager.findUserById("T2024001");
if (findTeacher instanceof Teacher) {
Teacher teacher = (Teacher) findTeacher;
teacher.teach(); // 调用教师独有方法
}
}
}
运行结果
Plain
添加学生成功!
添加教师成功!
【所有用户信息】
【学生信息】
编号:2024001
姓名:张三
年龄:20
成绩:92.5
------------------------
【教师信息】
编号:T2024001
姓名:李四
年龄:35
薪资:8000.0
------------------------
张三正在学习...
李四正在讲课...
实战总结
本实战案例充分体现了三大特性的价值:
-
封装:通过private修饰成员变量,getter/setter控制访问,保证数据安全。
-
继承:Student和Teacher继承User,复用了id、name、age等共性属性和方法,减少代码冗余。
-
多态:UserManager的addUser、showAllUsers方法接收User类型参数,可处理任意子类对象;调用showInfo方法时,自动执行子类重写的实现,实现了统一管理和灵活扩展。
五、三大特性核心脉络总结
封装、继承、多态是Java面向对象编程的核心,三者相互依赖、相辅相成,共同实现代码的安全、复用与扩展:
-
封装是基础:通过访问修饰符隐藏内部细节,暴露统一接口,保证数据安全和代码可维护性。
-
继承是前提:让子类复用父类代码,同时扩展自身功能,为多态提供了继承关系基础。
-
多态是核心:基于继承和方法重写,实现父类引用指向子类对象,让代码不依赖具体类型,实现灵活扩展和解耦,是面向对象编程的终极目标。