📚 8.5 课程笔记整理:从封装、继承到多态与抽象类全景解析
1. 封装与权限控制
1.1 封装思想
定义:隐藏对象的属性和实现细节,仅对外提供公共访问方式。
核心:关注“如何调用”,不关心“内部实现”。就像使用遥控器,只需知道按键功能,无需了解内部电路。
1.2 实现方式
public class Person {
// 1. 私有化属性
private String name;
private int age;
// 2. 提供公共的 Getter/Setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// Setter 中可以加入逻辑校验,保证数据安全
public void setAge(int age) {
if (age >= 0 && age <= 120) {
this.age = age;
} else {
System.out.println("年龄不合法!");
}
}
}
1.3 引用类型的封装安全(深拷贝)
问题:对于引用类型(如 Date、数组),如果 get 方法直接返回引用,外部仍可修改对象内部数据,导致封装失效。
解决:在 get 方法中返回对象的副本(深拷贝)。
import java.util.Date;
public class User {
private Date birthDate;
// 错误的 Getter (浅拷贝) – 外部可修改 birthDate
// public Date getBirthDate() { return birthDate; }
// 正确的 Getter (深拷贝) – 返回副本
public Date getBirthDate() {
return new Date(birthDate.getTime());
}
}
1.4 访问权限
- private:仅限当前类内部访问。
- 默认(无修饰符):当前类 + 同包。
- protected:当前类 + 同包 + 子类。
- public:所有地方。
2. final 关键字
2.1 修饰变量(常量)
- 基本类型:数值一旦赋值,不可改变。
- 引用类型:引用地址(指向的对象)不可改变,但对象内部的内容可以修改。
public class FinalDemo {
public static void main(String[] args) {
// 1. 修饰基本类型
final int NUM = 10;
// NUM = 20; // 报错:无法再次赋值
// 2. 修饰引用类型
final int[] arr = {1, 2, 3};
// arr = new int[5]; // 报错:引用地址不可变
arr[0] = 99; // 正确:对象内部内容可变
System.out.println(arr[0]); // 输出 99
}
}
2.2 修饰类与方法
- 修饰类:该类不能被继承(断子绝孙类)。
- 修饰方法:该方法不能被重写(最终方法)。
// final class Father { } // 报错:无法继承 final 类
class Father {
public final void run() {
System.out.println("父亲在跑");
}
}
class Son extends Father {
// @Override
// public void run() { } // 报错:无法重写 final 方法
}
3. 静态域与静态方法 (static)
3.1 静态成员
- 归属:属于类本身,所有对象共享同一份。
- 调用:通过 类名.成员 访问,不依赖对象实例。
- 内存:类加载时分配内存(静态常量池),优先于对象存在。
3.2 访问规则
- 静态方法:只能访问静态成员(静态变量、静态方法)。
- 非静态方法:可以访问静态成员和非静态成员。
- 原因:静态方法在对象创建前就存在,无法访问依赖对象的非静态成员。
public class StaticDemo {
static int staticVar = 1;
int instanceVar = 2;
// 静态方法
public static void staticMethod() {
System.out.println(staticVar); // 正确
// System.out.println(instanceVar); // 报错:无法访问非静态变量
}
// 非静态方法
public void instanceMethod() {
System.out.println(staticVar); // 正确
System.out.println(instanceVar); // 正确
}
}
4. 参数传递机制
4.1 值传递
核心原则:Java 中只有值传递。
- 基本类型:传递的是数值的副本。方法内修改不影响外部。
- 引用类型:传递的是地址值的副本。方法内修改对象属性会影响外部,但修改引用指向(如 obj = null)不影响外部。
public class PassByValueDemo {
// 基本类型传递
public static void changeValue(int x) {
x = 100; // 修改副本,不影响 main 中的 a
}
// 引用类型传递
public static void changeObj(Person p) {
p.setName("李四"); // 修改副本指向的对象内容,影响 main 中的 person
p = new Person(); // 修改副本的指向,不影响 main 中的 person
p.setName("王五");
}
public static void main(String[] args) {
int a = 10;
changeValue(a);
System.out.println(a); // 输出 10
Person person = new Person("张三");
changeObj(person);
System.out.println(person.getName()); // 输出 "李四",不是 "王五"
}
}
5. 方法重载 (Overload)
5.1 定义
在同一个类中,允许存在多个同名方法,但参数列表必须不同(类型、数量、顺序)。
5.2 规则
- 方法签名:方法名 + 参数列表。
- 无关项:与返回值类型、访问修饰符无关。
public class OverloadDemo {
// 参数类型不同
public void add(int a, int b) { System.out.println("整数相加"); }
public void add(double a, double b) { System.out.println("小数相加"); }
// 参数数量不同
public void add(int a) { System.out.println("单个整数"); }
// 参数顺序不同
public void print(String s, int i) { }
public void print(int i, String s) { }
// public int add(int a, int b) { } // 报错:与第一个方法签名冲突
}
6. 构造器与初始化
6.1 构造器特点
- 方法名与类名相同。
- 没有返回值类型(连 void 都没有)。
- 创建对象时自动调用。
6.2 默认初始化
如果成员变量未显式赋值,系统会赋予默认值(int为0,boolean为false,引用为null)。
6.3 this() 调用
在构造器中调用本类的其他构造器,必须放在第一行。
public class Person {
private String name;
private int age;
// 无参构造
public Person() {
this("匿名", 18); // 调用有参构造,必须写在第一行
}
// 有参构造
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
7. 初始化块 (Initializer Block)
7.1 定义
使用 {} 包裹的代码块,用于初始化属性。
7.2 执行顺序(面试重点)
public class InitDemo {
static {
System.out.println("1. 静态代码块");
}
{
System.out.println("2. 普通代码块");
}
public InitDemo() {
System.out.println("3. 构造器");
}
public static void main(String[] args) {
new InitDemo();
new InitDemo();
}
}
// 输出:
// 1. 静态代码块
// 2. 普通代码块
// 3. 构造器
// 2. 普通代码块
// 3. 构造器
8. 继承与多态
8.1 继承 (extends)
- 单继承:一个类只能有一个直接父类。
- 多层继承:A继承B,B继承C。
- 根类:所有类默认继承 Object。
- 限制:private 成员不可继承,final 类不可继承。
8.2 方法重写 (Override)
- 定义:子类定义与父类方法签名完全相同的方法。
- 规则:子类方法覆盖父类方法。调用时执行子类版本。
- 对比重载:重载看参数列表,重写看继承关系且签名必须一致。
class Animal {
public void run() { System.out.println("动物跑"); }
}
class Dog extends Animal {
@Override
public void run() { System.out.println("狗跑"); } // 重写
}
8.3 多态 (Polymorphism)
- 前提:继承 + 重写。
- 表现:父类引用指向子类对象。
- 规则:编译看左边(父类),运行看右边(子类)。
- 限制:父类引用只能调用父类中定义的方法,不能调用子类新增的方法。
public class PolyDemo {
public static void main(String[] args) {
Animal a = new Dog(); // 多态
a.run(); // 输出 "狗跑",执行子类重写的方法
// a.bark(); // 报错:Animal 类中没有 bark 方法
}
}
8.4 强制类型转换
- 向上转型:自动(小碗装大碗)。
- 向下转型:强制(大碗倒小碗),需使用 instanceof 检查,防止 ClassCastException。
Animal a = new Dog();
Dog d = (Dog) a; // 强制转换
d.bark(); // 调用子类特有方法
8.5 总结
子类继承父类之后可以用父类的所以方法(重写过的就按照自己的方法运行)和自己的方法。父类不能用子类的方法。父类强制转化为子类时可以用自己的方法和子类重写的方法。父类可以使用子类定义的所有数据类型。
9. 继承中的初始化顺序(面试重点)
当涉及继承时,初始化顺序如下:
口诀:静父 -> 静子 -> 普父 -> 构父 -> 普子 -> 构子
class Father {
static { System.out.println("1. 父类静态"); }
{ System.out.println("3. 父类普通"); }
public Father() { System.out.println("4. 父类构造"); }
}
class Son extends Father {
static { System.out.println("2. 子类静态"); }
{ System.out.println("5. 子类普通"); }
public Son() { System.out.println("6. 子类构造"); }
}
public class Test {
public static void main(String[] args) {
new Son();
}
}
10. 包 (Package) 与类库管理
10.1 包的核心概念
- 本质:包(Package)本质上就是文件系统中的文件夹层级结构。
- 作用:
- 避免命名冲突:就像不同文件夹下可以有同名文件一样,不同包下可以有同名类(如 com.jd.User 和 com.taobao.User)。
- 分类管理:将功能相关的类组织在一起,便于维护。
10.2 命名规范
- 规则:通常采用公司域名的倒写形式,以确保全球唯一性。
- 示例:
- 京东:com.jd
- 百度某部门:com.baidu.department1
10.3 导入机制 (Import)
- 精确导入:import java.time.LocalDate;
- 优点:清晰,只加载需要的类。
- 通配符导入:import java.util.*;
- 缺点:
- 内存消耗:会将整个包下的类都加载到内存中。
- 打包体积:可能导致最终应用体积变大(就像把整个书架搬回家,而不仅仅是借一本书)。
- 作用:调用静态成员时可省略类名。
- 示例:out.println("Hello"); 代替 System.out.println("Hello");。
10.4 类库设计权衡 (Java vs C)
- C语言:小而多。类库功能单一,内存占用小,但数量庞大,开发者记忆负担重。
- Java:大而全。功能打包在一起,调用方便,但可能包含未使用的功能,占用更多内存。
- 优化:项目打包部署前,应检测并剔除未使用的类,以减小体积。
11. 访问权限修饰符 (Access Modifiers)
Java通过四种权限控制类成员的可见性。
| public | ✅ | ✅ | ✅ | ✅ | 公开:大门敞开,谁都能进。 |
| protected | ✅ | ✅ | ✅ | ❌ | 受保护:同包邻居能进,不同包的亲戚(子类)也能进。 |
| (默认) | ✅ | ✅ | ❌ | ❌ | 包级私有:仅限同包邻居,跨包即使是亲戚也不行。 |
| private | ✅ | ❌ | ❌ | ❌ | 私有:仅限自己,谁都不让进。 |
class Person {
public String name; // 谁都能访问
protected int age; // 同包或子类能访问
int height; // (默认) 只有同包能访问
private String secret; // 只有自己知道
public void test() {
name = "张三";
age = 18;
height = 170;
secret = "密码"; // 本类内部都可以访问
}
}
12. 抽象类 (Abstract Class)
12.1 核心概念
- 定义:使用 abstract 修饰的类。它代表一个抽象概念,不能实例化(不能 new)。
- 类比:Animal(动物)是一个抽象概念,现实中不存在叫“动物”的具体生物,只有 Cat、Dog 等具体动物。
- 作用:作为子类的模板,定义规范。
12.2 抽象方法
- 定义:没有方法体({})的方法,必须用 abstract 修饰。
- 强制约束:
- 包含抽象方法的类必须是抽象类。
- 继承抽象类的具体子类,必须重写所有抽象方法。
- 如果子类不重写,子类也必须声明为抽象类。
- 目的:强制子类实现特定行为,避免业务逻辑遗漏(如忘记实现 run 方法)。
12.3 抽象类的特性
- 构造方法:抽象类可以有构造方法。
- 作用:供子类创建对象时通过 super() 调用,用于初始化父类属性(打地基)。
- 方法组成:可以同时包含抽象方法(强制子类实现)和具体方法(子类直接继承复用)。
12.4 关键限制
- abstract 和 final 不能共存:
- abstract 要求被继承/重写,final 禁止继承/重写,语义矛盾。
- abstract 和 static 不能共存:
- static 方法随类加载而存在,abstract 方法无方法体无法调用,没有意义。
abstract class Animal {
// 抽象方法:强制子类实现
public abstract void run();
// 具体方法:子类可直接复用
public void eat() {
System.out.println("动物在吃东西");
}
}
class Cat extends Animal {
// 必须重写 run 方法,否则编译报错
@Override
public void run() {
System.out.println("猫在跑");
}
}
13. 接口 (Interface) – 简介
- 定义:比抽象类更纯粹的抽象形式。
- 特点:
- 接口中的方法默认都是抽象方法(无需写 abstract)。
- 实现接口的类(实现类)必须重写接口中所有方法。
- 与抽象类的区别:
- 抽象类:灵活,可包含具体方法和抽象方法。
- 接口:纯粹,早期版本中所有方法必须是抽象的,侧重于定义规范。
-
-
// 1. 定义一个 USB 接口(规定好必须有什么功能)
interface UsbInterface {
// 抽象方法:只声明,不写具体实现
void start();
void stop();
}// 2. 手机类实现 USB 接口
class Phone implements UsbInterface {
@Override
public void start() {
System.out.println("手机开始连接电脑,准备传输照片…");
}@Override
public void stop() {
System.out.println("手机安全断开连接。");
}
}肝死我了
-
网硕互联帮助中心



评论前必须登录!
注册