一、封装:把数据藏起来
1.1 什么是封装?
封装就是四个字:**私有属性,公有方法。 **
把数据藏到类里面,不让外面直接访问,只能通过特定的方法来操作。
**打个比方: **
-
ATM机:钱在机器里面,你不能直接伸手进去拿,必须通过按键(方法)来操作
-
类:数据在里面,外部不能直接改,必须通过方法
1.2 为什么要封装?
**没有封装的问题: **
public class Student {
public int age; // 公有属性,谁都能改
}
// 有人乱改
Student s = new Student();
s.age = -100; // 年龄负数,这合理吗?
**有封装的好处: **
public class Student {
private int age; // 私有属性,外部不能直接访问
// 提供公开的方法,外部只能通过这个方法修改
public void setAge(int age) {
if (age < 0 || age > 150) {
System.out.println("年龄不合法!");
return;
}
this.age = age;
}
// 获取年龄的方法
public int getAge() {
return age;
}
}
1.3 封装的实现:private + getter/setter
**标准写法: **
public class Student {
// 1. 属性私有化
private String name;
private int age;
private double score;
// 2. 无参构造
public Student() {
}
// 3. 有参构造
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
// 4. getter和setter
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) {
System.out.println("年龄不合法!");
return;
}
this.age = age;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
// 5. toString方法(调试用)
@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + ", score=" + score + "}";
}
}
**使用: **
public class Main {
public static void main(String[] args) {
Student s = new Student("张三", 18, 95.5);
// 通过方法访问属性
System.out.println(s.getName()); // 张三
System.out.println(s.getAge()); // 18
// 修改属性(会被校验)
s.setAge(-100); // 输出:年龄不合法!
s.setAge(20); // 成功
System.out.println(s.getAge()); // 20
}
}
1.4 IDEA快速生成getter/setter
不用一个个手写,IDEA一键生成:
-
把光标放在类里面
-
按
Alt + Insert -
选择
Getter and Setter -
选择要生成的属性
-
回车
二、继承:子承父业
2.1 什么是继承?
继承就是子类继承父类的属性和方法。
**打个比方: **
-
父亲有:眼睛、鼻子、会说话、会走路
-
儿子继承了父亲的这些特征,所以也有眼睛、鼻子、会说话、会走路
2.2 继承的语法
// 父类
public class Animal {
public String name;
public int age;
public void eat() {
System.out.println(name + "在吃东西");
}
public void sleep() {
System.out.println(name + "在睡觉");
}
}
// 子类:用 extends 继承父类
public class Dog extends Animal {
// Dog类自动拥有 name、age、eat()、sleep()
// 子类独有的方法
public void bark() {
System.out.println(name + "汪汪叫");
}
}
**使用: **
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "旺财";
// 从父类继承来的方法
dog.eat(); // 旺财在吃东西
dog.sleep(); // 旺财在睡觉
// 子类独有的方法
dog.bark(); // 旺财汪汪叫
}
}
2.3 继承的特点
-
子类可以继承父类的属性和方法(但构造方法不能继承)
-
子类可以有自己的独特属性和方法
-
子类可以重写父类的方法(@Override)
-
Java只支持单继承:一个类只能有一个父类,但父类可以有多个子类
2.4 方法重写(Override)
**子类可以重写父类的方法: **
public class Animal {
public void sound() {
System.out.println("动物发出声音");
}
}
public class Cat extends Animal {
// @Override 可加可不加,但加上更安全(写错方法名会报错)
@Override
public void sound() {
System.out.println("喵喵喵");
}
}
public class Dog extends Animal {
@Override
public void sound() {
System.out.println("汪汪汪");
}
}
**使用: **
Animal a1 = new Cat();
Animal a2 = new Dog();
a1.sound(); // 喵喵喵
a2.sound(); // 汪汪汪
2.5 super关键字
super 指的是父类,用来调用父类的方法或构造方法。
public class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void eat() {
System.out.println(name + "在吃东西");
}
}
public class Dog extends Animal {
public String color; // 子类独有的属性
public Dog(String name, String color) {
super(name); // 调用父类的构造方法
this.color = color;
}
@Override
public void eat() {
super.eat(); // 调用父类的方法
System.out.println(name + "还在啃骨头");
}
}
2.6 继承关系下的构造方法
**规则:子类构造方法的第一行,必须调用父类的构造方法。 **
public class Animal {
public String name;
// 父类无参构造
public Animal() {
System.out.println("Animal无参构造被调用");
}
// 父类有参构造
public Animal(String name) {
this.name = name;
System.out.println("Animal有参构造被调用");
}
}
public class Dog extends Animal {
public String color;
public Dog() {
super(); // 默认调用父类无参构造
System.out.println("Dog无参构造被调用");
}
public Dog(String name, String color) {
super(name); // 调用父类有参构造
this.color = color;
System.out.println("Dog有参构造被调用");
}
}
**注意: **
-
如果父类有无参构造,子类可以不写
super(),会自动调用 -
如果父类没有无参构造,子类必须显式调用父类的有参构造
三、多态:同一个方法,不同的表现
3.1 什么是多态?
多态:同一个引用类型,不同的对象,执行不同的行为。
**打个比方: **
-
"发出声音"这个动作,猫执行是"喵喵喵",狗执行是"汪汪汪"
-
同一句话,不同对象,效果不同
3.2 多态的前提
-
有继承关系(子类继承父类)
-
有方法重写(子类重写父类方法)
-
父类引用指向子类对象(
父类 变量 = new 子类())
3.3 多态的语法
// 父类
public class Animal {
public void sound() {
System.out.println("动物发出声音");
}
}
// 子类1
public class Cat extends Animal {
@Override
public void sound() {
System.out.println("喵喵喵");
}
}
// 子类2
public class Dog extends Animal {
@Override
public void sound() {
System.out.println("汪汪汪");
}
}
**使用多态: **
public class Main {
public static void main(String[] args) {
// 父类引用指向子类对象
Animal a1 = new Cat(); // 编译类型是Animal,运行类型是Cat
Animal a2 = new Dog(); // 编译类型是Animal,运行类型是Dog
a1.sound(); // 输出:喵喵喵
a2.sound(); // 输出:汪汪汪
}
}
3.4 多态的好处
**不用写很多if-else: **
// 不用多态:写很多if-else
public void doSound(Animal animal) {
if (animal instanceof Cat) {
System.out.println("喵喵喵");
} else if (animal instanceof Dog) {
System.out.println("汪汪汪");
}
// 还要加更多...
}
// 用多态:一行搞定
public void doSound(Animal animal) {
animal.sound(); // 自动调用对应子类的重写方法
}
3.5 多态下的属性访问
public class Animal {
public String name = "动物";
}
public class Cat extends Animal {
public String name = "猫"; // 子类独有的属性
}
Animal a = new Cat();
System.out.println(a.name); // 输出:动物(编译时看左边类型)
**结论:多态情况下,属性访问看左边(编译类型),方法调用看右边(运行类型)。 **
3.6 引用类型转换
**向上转型(自动): **
Animal a = new Cat(); // 父类引用指向子类对象,小转大,自动
**向下转型(强制,需要判断): **
Animal a = new Cat();
// 向下转型,把Cat拿出来
Cat cat = (Cat) a; // 大转小,强制
// 安全判断
if (a instanceof Cat) {
Cat cat = (Cat) a;
}
四、综合练习:员工管理系统
4.1 需求
用继承和多态,实现一个员工管理系统:
-
员工(Employee):有姓名、职位、基本工资
-
程序员(Programmer):额外有加班费
-
经理(Manager):额外有奖金
4.2 代码
// 父类:员工
public class Employee {
protected String name; // protected:子类可以访问
protected String position; // 职位
protected double baseSalary; // 基本工资
public Employee(String name, String position, double baseSalary) {
this.name = name;
this.position = position;
this.baseSalary = baseSalary;
}
// 计算月薪(被子类重写)
public double getMonthSalary() {
return baseSalary;
}
// 显示信息
public void showInfo() {
System.out.println("姓名:" + name + ",职位:" + position + ",月薪:" + getMonthSalary());
}
}
// 子类:程序员
public class Programmer extends Employee {
private double overtimePay; // 加班费
public Programmer(String name, double baseSalary, double overtimePay) {
super(name, "程序员", baseSalary);
this.overtimePay = overtimePay;
}
@Override
public double getMonthSalary() {
return baseSalary + overtimePay;
}
}
// 子类:经理
public class Manager extends Employee {
private double bonus; // 奖金
public Manager(String name, double baseSalary, double bonus) {
super(name, "经理", baseSalary);
this.bonus = bonus;
}
@Override
public double getMonthSalary() {
return baseSalary + bonus;
}
}
// 主程序
public class Main {
public static void main(String[] args) {
// 创建不同类型的员工
Employee e1 = new Employee("张三", "普通员工", 5000);
Programmer p = new Programmer("李四", 8000, 2000);
Manager m = new Manager("王五", 10000, 5000);
// 用多态统一管理
Employee[] employees = {e1, p, m};
System.out.println("=== 员工工资表 ===");
for (Employee e : employees) {
e.showInfo();
}
// 计算总工资
double total = 0;
for (Employee e : employees) {
total += e.getMonthSalary();
}
System.out.println("总工资:" + total);
}
}
4.3 运行结果
=== 员工工资表 ===
姓名:张三,职位:普通员工,月薪:5000.0
姓名:李四,职位:程序员,月薪:10000.0
姓名:王五,职位:经理,月薪:15000.0
总工资:30000.0
五、常见错误
❌ 错误1:把继承和组合搞混
// 错误:这不是继承,是组合
public class Car {
private Engine engine = new Engine(); // 组合:有一个引擎
}
// 正确:继承是"是一个"的关系
public class Car extends Vehicle {
// Car是一个Vehicle
}
❌ 错误2:忘记加 @Override
public class Cat extends Animal {
// 没有@Override,容易写错方法名而不自知
public void sound() {
System.out.println("喵");
}
}
❌ 错误3:强制转型没有判断
Animal a = new Cat();
// 错误!没有判断就转型,可能报错
Dog d = (Dog) a; // ClassCastException
// 正确:先判断
if (a instanceof Dog) {
Dog d = (Dog) a;
}
❌ 错误4:构造方法忘记调用super
public class Dog extends Animal {
public Dog() {
// 错误!如果父类没有无参构造,会报错
// 应该写:super();
}
}
六、本课总结
| 特性 | 关键字 | 作用 |
|------|--------|------|
| 封装 | private + getter/setter | 把数据藏起来,保护数据安全 |
| 继承 | extends | 子承父业,减少代码重复 |
| 多态 | 父类引用指向子类对象 | 同一个方法,不同表现 |
**记住: **
-
封装:属性私有化,方法公开化
-
继承:子类 extends 父类,单继承
-
多态:编译看左边,运行看右边
-
重写方法加
@Override更安全
七、下节课预告
第5课:接口与抽象类
-
抽象类: abstract class
-
接口: interface
-
两者的区别和使用场景
-
Java 8/9/11 接口新特性
学完这课,你就能写出规范的面向对象代码了。
**关注我,跟着老兵学Java,少走弯路。 **
💬 **评论区聊聊:封装、继承、多态,哪个最难理解? **