# JavaSE 基础 — Day06 笔记 > **日期**:2026-07-20 > **项目**:`c260720` > **作者**:WanJL --- ## 一、面向对象基础(Object-Oriented Programming) ### 1. 面向对象思想概述 | 对比维度 | 面向对象(Java) | 面向过程(C) | |---------|----------------|-------------| | **思想核心** | 以对象为中心,通过指挥对象实现操作 | 以过程为中心,功能都由自己实现 | | **特点** | 更符合人类思维习惯,易于维护和扩展 | 按步骤一步步执行,代码直接 | | **关键词** | 类、对象、封装、继承、多态 | 函数、过程、步骤 | > **Java 中一切皆为对象**,是一种以对象为中心的编程思想。 ### 2. 类(Class)与对象(Object)的关系 **类比**:设计图纸(类)与成品(对象)的关系。 - **1 套设计图纸** → 可以做出 **多个成品** - **1 个类** → 可以创建 **多个对象** - **类** 是模板、蓝图、设计图纸、规则、要求,描述一个对象的所有属性和操作 - **对象** 是能够看得到摸得着的真实存在的实体 > **类的定义**: > 类是现实生活中一类具有相同属性和行为的事物的抽象。 ### 3. 类的定义格式 ```java 修饰符 class 类名 { 数据类型 属性名; // 成员变量(属性) 修饰符 返回值类型 方法名(){ // 成员方法(行为) } } ``` **类的成员组成**: | 成员 | 对应概念 | 表现形式 | |-----|---------|---------| | 属性(Property) | 事物特征 | 成员变量(类中方法外的变量) | | 行为(Behavior) | 事物操作 | 成员方法(去掉 `static` 关键字的方法) | ### 4. 创建对象与使用成员 **创建对象格式**: ```java 类名 对象名 = new 类名(); ``` **调用成员格式**: ```java 对象名.成员变量; // 访问属性 对象名.成员方法(); // 调用方法 ``` ### 5. 成员变量(Member Variable)与局部变量(Local Variable)区别 **文件**:`Demo03.java` | 对比维度 | 成员变量 | 局部变量 | |---------|---------|---------| | **类中位置** | 定义在【类内部,方法外部】 | 定义在【方法内部】(方法的参数也属于局部变量) | | **内存位置** | 堆内存(Heap)中 | 栈内存(Stack)中 | | **生命周期** | 随着对象的存在而存在,随着对象的消失而消失 | 随着方法的调用而存在,随着方法的调用结束而消失 | | **初始化值** | 有默认初始化值(整数→0,引用类型→`null`) | 没有默认初始化值,必须先定义赋值才能使用 | ```java public class Student { String name; // 成员变量 —— 在类内部、方法外部 int age; // 成员变量 public void study() { String msg = "学习中"; // 局部变量 —— 在方法内部 System.out.println(msg); } } ``` --- ### 6. 对象引用与内存共享 **文件**:`Demo03.java` 当多个引用变量指向 **同一个内存地址** 时,它们操作的是同一块内存空间。任何一个引用修改了数据,其他引用获取到的都是修改后的数据。 ```java public class Demo03 { public static void main(String[] args) { Teacher t1 = new Teacher(); // t1 引用指向 0x1024 t1.name = "张三"; t1.sleep(); // 输出:张三在休息.... Teacher t2 = t1; // t2 也指向 0x1024(与 t1 同一对象) t2.name = "李四"; t2.id = "1001"; t1.sleep(); // 输出:1001李四在休息....(已被 t2 修改) } } ``` > **图解**:`t1` 和 `t2` 是两个不同的引用变量,但存储的是同一个对象的地址 `0x1024`。通过 `t2.name = "李四"` 修改后,`t1.name` 也变成"李四",因为它们是同一块内存。 --- ### 7. 对象数组(Object Array) **文件**:`Test01.java`, `Test02.java` 对象数组的数组元素类型是**类名**,存储的是对象的**引用地址**(而非对象本身)。 ```java // 定义一个可以存储 10 个 BaiLu 对象引用的数组 BaiLu[] bailus = new BaiLu[10]; // 定义一个可以存储 10 个 Bike 对象引用的数组 Bike[] bikes = new Bike[10]; ``` **创建对象并存入数组的标准操作**: ```java // 1. 先创建对象 BaiLu b = new BaiLu(); // 2. 调用对象方法 b.up("青天"); // 3. 将对象引用(地址值)存入数组 bailus[i] = b; ``` > **对比基本类型数组**:`int[] arr = new int[10]` 存的是数值;对象数组存的是引用(地址)。 --- ### 8. 封装(Encapsulation) **文件**:`Demo04.java`, `Person.java`, `PersonTest.java` #### 8.1 面向对象三大特性 Java 面向对象有三大特性,本质上都是为了 **减少写代码、让代码能够复用**: | 特性 | 说明 | |------|------| | **封装** | 把该隐藏的隐藏起来,把该显示的显示出来 | | **继承** | 子类自动拥有父类的成员,实现代码复用 | | **多态** | 同一行为在不同对象上产生不同效果 | #### 8.2 为什么要封装 **问题场景**:如果类的属性直接暴露,可以随意赋值,可能导致不符合业务逻辑的数据。 ```java Person p = new Person(); p.age = 250; // 语法正确,但业务上不合理(人的年龄不可能250岁) p.age = -250000; // 同样不合理 ``` **解决方案(封装两步走)**: ``` 第一步:把属性的权限提高,让【本类以外】都无法直接访问(赋值、取值) → 使用 private 关键字 第二步:定义公开(public)的方法来赋值和取值 → setXxx() 方法:赋值,内部做合法性判断 → getXxx() 方法:取值 ``` #### 8.3 访问权限修饰符 | 修饰符 | 含义 | 访问范围 | |--------|------|---------| | `public` | 公开的 | 任何地方的任何类都能访问 | | `private` | 私有的 | 除了自身所在的类之外,任何类都不能访问 | | `default`(默认) | 默认的 | 本类中、同包中可以访问(不写任何修饰符) | | `protected` | 受保护的 | 本类中、同包中可以访问,非同包的子类可以访问(讲继承时再细说) | #### 8.4 封装的标准实现 —— setter/getter ```java public class Person { String name; private int age; // 私有化:本类以外无法直接访问 // 公开的赋值方法(setter)—— 带合法性校验 public void setAge(int newAge) { if (newAge >= 1 && newAge < 130) { age = newAge; } else { System.out.println("年龄超过规定限制,赋值无效..."); } } // 公开的取值方法(getter) public int getAge() { return age; } } ``` #### 8.5 测试封装效果 ```java public class PersonTest { public static void main(String[] args) { Person p = new Person(); p.name = "张三"; p.setAge(200); // 赋值无效:年龄超过限制 int age = p.getAge(); // 获取到的 age 是默认值 0 System.out.println(age); // 输出:0 } } ``` > **封装的核心思想**:隐藏功能的实现细节,仅暴露功能的调用方式。就像 USB 接口,用户只需插拔,不需关心内部电路。 #### 8.6 `this` 关键字 **文件**:`Product.java` 当方法参数(局部变量)与成员变量**同名**时,按照**就近原则**,方法内部默认使用的是局部变量。此时需要用 `this` 关键字来区分。 ```java public class Product { private String brand; // 成员变量 // 参数 brand 与成员变量 brand 同名 public void setBrand(String brand) { // this.brand 是成员变量,brand 是局部变量(方法参数) this.brand = brand; } } ``` > **`this` 的含义**:当前对象的引用。`this.成员变量` 调用的一定是当前对象的成员变量,`this.成员方法()` 调用的一定是当前对象的成员方法。 **使用 `this` 的标准 getter/setter 写法**: ```java public void setName(String name) { this.name = name; // this 区分成员变量和参数 } public String getName() { return this.name; // 此处可以省略 this(无同名冲突),但加上更清晰 } ``` #### 8.7 封装的原则与好处 **封装的原则**: - 类的某些属性信息隐藏在类的内部,不允许外部程序直接访问 - 通过该类提供的 **公有方法** 来实现对隐藏信息的操作和访问 - 成员变量使用 `private` 修饰,成员方法使用 `public` 修饰 **封装的好处**: 1. **安全性**:通过方法控制成员变量的操作,可增加校验逻辑,防止非法数据 2. **复用性**:把代码用方法封装起来,可以重复调用 3. **隐藏细节**:使用者只需调用方法,不需要了解内部实现 --- ### 9. 包(Package) **文件**:`p2/Student.java`, `p2/StudentDemo.java`, `p2/StudentTest.java` #### 9.1 什么是包 **包(Package)** 是 Java 中**组织类和接口**的一种机制,相当于文件系统中的**文件夹**。 | 作用 | 说明 | |------|------| | **分类管理** | 将功能相关的类放在同一个包中,便于查找和使用 | | **避免命名冲突** | 不同包中可以有相同名称的类(如 `p1.Student` 和 `p2.Student`) | | **访问控制** | 包级别访问权限(`default`)只允许同包中的类访问 | #### 9.2 包的声明 在 Java 源文件的第一行(注释之前)使用 `package` 关键字声明: ```java package p2; // 声明该类属于 p2 包 // 有包名的类,全名是:p2.Student public class Student { // ... } ``` > **命名规范**:公司域名倒写,如 `com.lovecoding.day01`、`com.lovecoding.day02` #### 9.3 包与目录结构 包名必须与文件系统的目录结构一致: ``` c260720/src/ ├── p1/ ← 包 p1 │ ├── Demo01.java │ ├── Student.java │ └── ... └── p2/ ← 包 p2 ├── Student.java ├── StudentDemo.java └── StudentTest.java ``` #### 9.4 同包中的类相互访问 同一个包中的类可以直接使用,**无需 import**: ```java // p2/StudentTest.java — 与 Student、StudentDemo 同包 package p2; public class StudentTest { public static void main(String[] args) { StudentDemo sd = new StudentDemo(); // 直接使用,无需 import Student[] students = sd.initStudents(); int ranId = sd.randomStudent(); // ... } } ``` --- ## 二、代码实战:类与对象完整示例 ### 1. Student 学生类 **文件**:`Demo02.java` / `Student.java` **定义类(Student.java)**: ```java public class Student { String name; // 成员变量:姓名 int age; // 成员变量:年龄 // 成员方法:学习 public void study() { System.out.println("姓名:" + name + ",年龄:" + age + ",在学习"); } } ``` **使用对象(Demo02.java)**: ```java public class Demo02 { public static void main(String[] args) { // 创建第一个学生对象 Student s = new Student(); s.name = "张三"; s.age = 25; s.study(); // 创建第二个学生对象 Student s1 = new Student(); s1.name = "李四"; s1.age = 20; s1.study(); } } ``` > **要点**:同一个 `Student` 类可以创建多个对象,每个对象拥有自己独立的属性值。 --- ### 2. Dog 狗类 **文件**:`Dog.java` ```java public class Dog { double weight; // 成员变量:体重 double height; // 成员变量:身高 public static void main(String[] args) { Dog d = new Dog(); d.height = 15.6; d.weight = 3.14; } } ``` --- ### 3. Teacher 教师类 + TeacherTest 测试类 **文件**:`Teacher.java` / `TeacherTest.java` **定义类(Teacher.java)**: ```java public class Teacher { String id; // 教工号 String name; // 姓名 int age; // 年龄 public void teaching() { System.out.println(id + name + age + "在授课...."); } public void sleep() { System.out.println(id + name + age + "在休息...."); } } ``` **测试类(TeacherTest.java)—— 重点:对象地址输出**: ```java public class TeacherTest { public static void main(String[] args) { Teacher t = new Teacher(); t.id = "1001"; t.name = "张三"; t.age = 25; t.teaching(); t.sleep(); // 直接输出对象名 —— 得到内存地址 System.out.println(t); // 输出示例:Teacher@4769b07b } } ``` #### 对象地址输出解析 直接打印对象名时,输出格式为:**类型@哈希码** | 部分 | 含义 | |------|------| | `Teacher` | 对象所属的类型 | | `@` | 分隔符 | | `4769b07b` | 对象在内存中的地址(哈希码的十六进制表示) | --- ### 4. 对象数组实战①——黄鹂与白鹭(古诗场景) **文件**:`HuangLi.java`, `BaiLu.java`, `Test01.java` **定义类——黄鹂(HuangLi.java)**: ```java public class HuangLi { public void sing(String s) { System.out.println("黄鹂鸣" + s); } } ``` **定义类——白鹭(BaiLu.java)**: ```java public class BaiLu { public void up(String s) { System.out.println("白鹭上" + s); } } ``` **测试类 —— 使用对象数组**: ```java public class Test01 { public static void main(String[] args) { // 黄鹂:创建两个对象,调用方法 HuangLi h1 = new HuangLi(); HuangLi h2 = new HuangLi(); String s1 = "翠柳"; h1.sing(s1); h2.sing(s1); // 白鹭:对象数组,循环创建 10 个对象 String s2 = "青天"; BaiLu[] bailus = new BaiLu[10]; // 初始状态:数组元素全为 null System.out.println(Arrays.toString(bailus)); for (int i = 0; i < 10; i++) { BaiLu b = new BaiLu(); // 创建白鹭对象 b.up(s2); // 调用方法 bailus[i] = b; // 存入数组(存的是地址值) } } } ``` > **要点**:对象数组刚刚创建时,所有元素默认为 `null`。需要逐个 `new` 对象并赋值后才能使用。 --- ### 5. 对象数组实战②——汽车与自行车(交通场景) **文件**:`Car.java`, `Bike.java`, `Test02.java` **定义类——汽车(Car.java)**: ```java public class Car { String brand; // 品牌 String color; // 颜色 public void drive() { System.out.println(brand + "牌的" + color + "汽车行驶....."); } } ``` **定义类——自行车(Bike.java)**: ```java public class Bike { String type; // 车型 public void ride() { System.out.println("我骑着" + type + "自行车在骑行....."); } } ``` **测试类 —— 单个对象 + 对象数组**: ```java public class Test02 { public static void main(String[] args) { // 单个汽车对象 Car c = new Car(); c.brand = "奔驰"; c.color = "白色"; c.drive(); // 自行车对象数组 Bike[] bikes = new Bike[10]; for (int i = 0; i < 10; i++) { Bike b = new Bike(); b.type = "山地"; bikes[i] = b; bikes[i].ride(); } } } ``` > **要点**:单个对象与对象数组混合使用,展示了面向对象建模中「1 对 N」关系的代码实现。 --- ### 6. 封装实战——Person 类 **文件**:`Person.java`, `PersonTest.java` 完整演示封装的标准实现:私有化属性 + setter/getter 方法。 **实体类(Person.java)**: ```java public class Person { String name; // 公开属性 private int age; // 私有属性:外部不能直接访问 // setter:赋值 + 合法性校验 public void setAge(int newAge) { if (newAge >= 1 && newAge < 130) { age = newAge; } else { System.out.println("年龄超过规定限制,赋值无效..."); } } // getter:取值 public int getAge() { return age; } } ``` **测试类(PersonTest.java)**: ```java public class PersonTest { public static void main(String[] args) { Person p = new Person(); p.name = "张三"; // 公开属性可以直接赋值 p.setAge(200); // 通过 setter 赋值(会校验) int age = p.getAge(); // 通过 getter 取值 System.out.println(age); // 输出 0(因为赋值无效,age 保持默认值) // p.age = 20; // 编译错误:age 是 private 的,外部无法直接访问 } } ``` --- ### 7. 封装实战②——Product 商品类(this 关键字示范) **文件**:`Product.java` 完整的商品封装类,包含 4 个私有属性,每个属性都有标准的 getter/setter,并在参数与成员变量同名时使用 `this` 关键字。 ```java public class Product { private String brand; // 品牌 private String name; // 名称 private double price; // 价格 private int count; // 数量 // setter/getter —— 使用 this 区分同名参数和成员变量 public void setBrand(String brand) { this.brand = brand; // this.brand 是成员变量,brand 是参数 } public String getBrand() { return brand; // 无同名冲突,this 可省略 } public void setName(String name) { this.name = name; } public String getName() { return this.name; // 加上 this 更清晰 } public void setPrice(double price) { this.price = price; } public double getPrice() { return price; } public void setCount(int count) { this.count = count; } public int getCount() { return count; } } ``` > **要点**:当 setter 参数名与成员变量名一致时,`this` **不能省略**,否则方法内使用的都是局部变量,赋值无效。 --- ### 8. 包实战——Student 学生管理(p2 包综合应用) **文件**:`p2/Student.java`, `p2/StudentDemo.java`, `p2/StudentTest.java` 综合运用 **封装 + 对象数组 + Random** 的完整案例,所有类位于 `p2` 包中。 **实体类(p2/Student.java)**: ```java package p2; public class Student { private int id; // 私有属性 private String name; public void setId(int id) { this.id = id; // this 区分同名参数 } public int getId() { return id; } public void setName(String name) { this.name = name; } public String getName() { return name; } } ``` **业务类(p2/StudentDemo.java)—— 初始化学生数组 + 随机抽取**: ```java package p2; import java.util.Random; public class StudentDemo { private int studentCount = 5; // 学生数量 // 方法返回 Student[] 对象数组 public Student[] initStudents() { Student[] students = new Student[studentCount]; students[0] = new Student(); students[0].setId(1); students[0].setName("占山"); students[1] = new Student(); students[1].setId(2); students[1].setName("李四"); students[2] = new Student(); students[2].setId(3); students[2].setName("王五"); students[3] = new Student(); students[3].setId(4); students[3].setName("赵六"); students[4] = new Student(); students[4].setId(5); students[4].setName("赵赵"); return students; // 返回对象数组 } // 随机生成学生 ID public int randomStudent() { Random ran = new Random(); return ran.nextInt(studentCount) + 1; // 1 ~ studentCount } } ``` **测试类(p2/StudentTest.java)**: ```java package p2; public class StudentTest { public static void main(String[] args) { StudentDemo sd = new StudentDemo(); Student[] students = sd.initStudents(); // 获取学生数组 int ranId = sd.randomStudent(); // 获取随机 ID // 遍历数组,根据 ID 匹配学生姓名 for (int i = 0; i < students.length; i++) { Student s = students[i]; if (ranId == s.getId()) { System.out.println("随机到的学生是:" + s.getName()); } } } } ``` > **综合点**:本案例融合了**包声明**、**封装(private + getter/setter)**、**`this` 关键字**、**对象数组**、**Random 随机数**、**方法返回对象数组**等多个知识点,是面向对象基础的综合练习。 --- ## 三、随堂练习 > 以下练习在 `Demo02.java`、`Demo03.java` 和 `Demo04.java` 中以注释形式列出,作为课后实现任务。 ### 练习 1:教师类完整定义 创建 `Teacher` 类,包含: - **属性**:教工号(`id`)、姓名(`name`)、年龄(`age`) - **方法**:授课(`teaching`)——输出 `id+name+age老师在授课`、休息(`sleep`)——输出 `id+name+age老师在休息` ### 练习 2:手机类(Phone) 创建 `Phone` 类和 `PhoneTest` 测试类: - **属性**:品牌(`brand`)、价格(`price`) - **方法**:打电话(`call`)、发短信(`sendMessage`) ### 练习 3:矩形类(Rectangle) 创建矩形类,包含: - **属性**:长度(`length`)、宽度(`width`) - **方法**:计算矩形周长、计算矩形面积 ### 练习 4:圆形类(Circle) 创建圆形类,包含: - **属性**:PI(圆周率)、半径(`radius`) - **方法**:计算圆形周长、计算圆形面积 ### 练习 5:三角形类(Triangle) 创建三角形类,包含: - **属性**:边A、边B、边C、高 - **方法**:计算三角形周长、计算三角形面积 --- ### 练习 6:古诗场景建模(Demo03 练习) 分析诗句「两只黄鹂鸣翠柳,一行白鹭上青天」,创建对应类和测试类: - 黄鹂类(`HuangLi`):`sing(String s)` 方法 - 白鹭类(`BaiLu`):`up(String s)` 方法 - 测试类:创建 2 只黄鹂对象 + 10 只白鹭对象数组 ### 练习 7:交通场景建模(Demo03 练习) 分析描述「一辆汽车在路上行驶,一队自行车并排骑行」,创建对应类和测试类: - 汽车类(`Car`):品牌、颜色属性 + `drive()` 方法 - 自行车类(`Bike`):车型属性 + `ride()` 方法 - 测试类:1 个汽车对象 + 10 辆自行车对象数组 ### 练习 8:封装练习——学生年龄校验 参照 `Person` 类的封装方式,创建一个 `StudentV2` 类: - **属性**:姓名(`name`,公开)、年龄(`age`,私有) - **要求**:通过 setter 赋值时,年龄必须在 6~35 岁之间(含边界),否则提示"年龄超出学生范围,赋值无效" - **提供** `getAge()` 方法获取年龄 - **测试类**:创建对象,分别测试合法年龄和非法年龄的赋值效果 ### 练习 9:补全 Person 类的 name 封装(Demo04 练习) 在已有 `Person` 类基础上,为 `name` 属性也添加 `private` 修饰,并配置对应的 setter/getter 方法。 ### 练习 10:创建 Bird 类(Demo04 练习) 创建 `Bird` 类,定义 **3 个私有成员变量**,并对每个成员变量设置赋值和取值的方法(getter/setter),要求参数名与成员变量同名时使用 `this` 关键字。 ### 练习 11:创建 Product 测试类 为 `Product` 类创建测试类 `ProductTest`,创建商品对象并通过 setter 赋值,通过 getter 输出所有属性值。 ### 练习 12:包练习——创建 com.lovecoding 包结构 模仿 `p2` 包的做法,创建以下包结构并编写类: - 包名:`com.lovecoding.test` - 在该包下创建一个 `Hello` 类,包含一个 `sayHello()` 方法,输出"Hello from package!" - 创建测试类,在同包中直接调用 `Hello` 类的方法 --- ## 四、知识点总结 | 序号 | 知识点 | 说明 | 对应文件 | |------|--------|------|---------| | 1 | 面向对象思想 | Java 以对象为中心,区别于 C 的面向过程 | `Demo01.java` | | 2 | 类的定义 | `class` 关键字定义类,包含属性和行为 | `Demo01.java` | | 3 | 成员变量 | 类中方法外的变量,描述属性 | `Student.java`, `Dog.java`, `Teacher.java` | | 4 | 成员方法 | 类中定义方法(无 `static`),描述行为 | `Student.java`, `Teacher.java` | | 5 | 创建对象 | `类名 对象名 = new 类名();` | `Demo02.java`, `TeacherTest.java` | | 6 | 调用成员 | `对象名.成员变量` / `对象名.成员方法()` | `Demo02.java`, `TeacherTest.java` | | 7 | 多对象创建 | 一个类可创建多个独立对象 | `Demo02.java` | | 8 | 对象地址 | 直接输出对象得到 `类型@哈希码` | `TeacherTest.java` | | 9 | 成员变量 vs 局部变量 | 四维对比:位置、内存、生命周期、初始化值 | `Demo03.java` | | 10 | 对象引用与内存共享 | 多个引用指向同一内存地址,修改互相影响 | `Demo03.java` | | 11 | 对象数组 | 数组元素类型为类名,存储对象引用地址 | `Test01.java`, `Test02.java` | | 12 | 循环创建对象并存入数组 | for 循环中 `new` 对象,赋值给数组元素 | `Test01.java`, `Test02.java` | | 13 | 多个类协同建模 | 将现实场景(古诗/交通)抽象为多个类 | `HuangLi.java`, `BaiLu.java`, `Car.java`, `Bike.java` | | 14 | 封装(Encapsulation) | 面向对象三大特性之一,隐藏实现细节,暴露调用方式 | `Demo04.java` | | 15 | `private` 关键字 | 私有权限修饰符,本类以外无法访问 | `Person.java` | | 16 | setter/getter 方法 | 对外提供公有方法赋值/取值,可做合法性校验 | `Person.java`, `PersonTest.java` | | 17 | `this` 关键字 | 区分同名的成员变量和局部变量,指向当前对象 | `Product.java` | | 18 | `default` 权限修饰符 | 默认访问权限,同包中可以访问 | `Demo04.java` | | 19 | `protected` 权限修饰符 | 受保护权限,同包+非同包子类可以访问 | `Demo04.java` | | 20 | 包(Package) | 组织类和接口的机制,相当于文件夹 | `p2/Student.java` | | 21 | `package` 声明 | 源文件第一行声明所属包,与目录结构一致 | `p2/*.java` | | 22 | 同包访问规则 | 同包中的类直接使用,无需 `import` | `p2/StudentTest.java` | | 23 | 方法返回对象数组 | 方法返回值类型为 `Student[]`,返回对象数组引用 | `p2/StudentDemo.java` | --- ## 五、拓展阅读 1. **构造方法**:`new 类名()` 实际上是调用类的构造方法,默认有无参构造 2. **匿名对象**:`new Student().study();` —— 没有名字的对象,用后即弃 3. **null 与 NullPointerException**:引用变量可以不指向任何对象(`null`),调用该引用的成员会报空指针异常 4. **封装 vs 继承 vs 多态**:三大特性后续会深入学习,继承实现代码复用,多态实现灵活扩展