编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • C语言入门精通

  • Cpp入门到精通

  • Java入门精通

    • README
    • 入门教程

      • README
      • 基础语法
      • 数据类型
      • 运算符
      • 字符串和数组
      • 流程语句
      • 函数方法
      • 类和对象
        • 7.1 类的介绍
          • 7.1.1 类的含义
          • 7.1.2 面向对象三大特性
          • 7.1.3 综合案例:面向对象概念演示
          • 7.1.4 训练题
        • 7.2 类定义和对象
          • 7.2.1 类的定义
          • 7.2.2 创建对象
          • 7.2.3 访问类成员
          • 7.2.4 案例实践
          • 7.2.5 综合案例:银行账户系统
          • 7.2.6 训练题
        • 7.3 类成员属性
          • 7.3.1 成员变量
          • 7.3.2 成员方法
          • 7.3.3 访问权限
          • 7.3.4 封装思想
          • 7.3.5 综合案例:学生信息封装
          • 7.3.6 训练题
        • 7.4 对象初始化
          • 7.4.1 构造方法
          • 7.4.2 构造方法重载
          • 7.4.3 this关键字
          • 7.4.4 构造代码块和静态代码块
          • 7.4.5 对象生命周期
          • 7.4.6 综合案例:对象生命周期跟踪器
          • 7.4.7 训练题
        • 7.5 static关键字
          • 7.5.1 静态变量
          • 7.5.2 静态方法
          • 7.5.3 静态常量
          • 7.5.4 综合案例:计数器与工具类
          • 7.5.5 训练题
        • 7.6 内部类
          • 7.6.1 成员内部类
          • 7.6.2 静态内部类
          • 7.6.3 匿名内部类
          • 7.6.4 综合案例:事件回调系统
          • 7.6.5 训练题
        • 7.7 枚举类
          • 7.7.1 枚举定义
          • 7.7.2 枚举方法
          • 7.7.3 综合案例:交通信号灯枚举
          • 7.7.4 训练题
      • 继承和多态
      • 接口和抽象类
      • 异常处理
      • 集合框架
      • IO流和File
      • 线程和锁
      • 泛型
      • 注解和反射
    • 综合案例

    • 专栏博客

  • Go入门到精通

  • JavaScript入门

  • CodeX
  • Java入门精通
  • 入门教程
杨充
2026-04-07
目录

类和对象

# 07.类和对象

# 目录介绍

  • 7.1 类的介绍
    • 7.1.1 类的含义
    • 7.1.2 面向对象三大特性
    • 7.1.3 综合案例:面向对象概念演示
    • 7.1.4 训练题
  • 7.2 类定义和对象
    • 7.2.1 类的定义
    • 7.2.2 创建对象
    • 7.2.3 访问类成员
    • 7.2.4 案例实践
    • 7.2.5 综合案例:银行账户系统
    • 7.2.6 训练题
  • 7.3 类成员属性
    • 7.3.1 成员变量
    • 7.3.2 成员方法
    • 7.3.3 访问权限
    • 7.3.4 封装思想
    • 7.3.5 综合案例:学生信息封装
    • 7.3.6 训练题
  • 7.4 对象初始化
    • 7.4.1 构造方法
    • 7.4.2 构造方法重载
    • 7.4.3 this关键字
    • 7.4.4 构造代码块和静态代码块
    • 7.4.5 对象生命周期
    • 7.4.6 综合案例:对象生命周期跟踪器
    • 7.4.7 训练题
  • 7.5 static关键字
    • 7.5.1 静态变量
    • 7.5.2 静态方法
    • 7.5.3 静态常量
    • 7.5.4 综合案例:计数器与工具类
    • 7.5.5 训练题
  • 7.6 内部类
    • 7.6.1 成员内部类
    • 7.6.2 静态内部类
    • 7.6.3 匿名内部类
    • 7.6.4 综合案例:事件回调系统
    • 7.6.5 训练题
  • 7.7 枚举类
    • 7.7.1 枚举定义
    • 7.7.2 枚举方法
    • 7.7.3 综合案例:交通信号灯枚举
    • 7.7.4 训练题

# 7.1 类的介绍

# 7.1.1 类的含义

类是对象的模板(蓝图),封装了数据(属性/字段)和行为(方法)。对象是类的实例。

类(Class):设计图纸        →  对象(Object):根据图纸造出的房子
Account 类(账户模板)       →  张三的账户、李四的账户(具体的账户对象)
1
2

对比 C++:概念完全一致。区别在于 Java 中所有对象都在堆上创建(通过 new),没有 C++ 中在栈上创建对象的方式。

对象创建的底层原理:执行 new 指令时,JVM 完成以下步骤:①类加载检查——检查该类是否已被加载、解析和初始化;②分配内存——在堆上为对象分配空间,使用指针碰撞(内存规整时)或空闲列表(内存不规整时)算法;③内存清零——将分配的内存空间初始化为零值(这就是成员变量有默认值的原因);④设置对象头——存储 Mark Word(哈希码、GC 分代年龄、锁状态)和类型指针;⑤执行 <init> 方法——即构造方法,完成程序员指定的初始化。

# 7.1.2 面向对象三大特性

  1. 封装:将数据和操作数据的方法绑定在一起,隐藏内部实现细节。
  2. 继承:子类继承父类的属性和方法,实现代码复用。
  3. 多态:同一个方法在不同对象上有不同的表现形式。

# 7.1.3 综合案例:面向对象概念演示

编写一个程序,用一个简单的"宠物"示例演示类、对象和面向对象三大特性的基本概念。

public class OopConceptDemo {
    // 类 = 蓝图/模板
    static class Pet {
        private String name;   // 封装:属性私有
        private String type;
        private int energy;

        Pet(String name, String type) {
            this.name = name;
            this.type = type;
            this.energy = 100;
        }

        // 封装:通过方法访问
        public String getName() { return name; }

        public void play() {
            energy -= 20;
            System.out.println(name + "(" + type + ")在玩耍, 体力: " + energy);
        }

        public void feed() {
            energy = Math.min(energy + 30, 100);
            System.out.println(name + "吃饱了, 体力: " + energy);
        }

        public void status() {
            String mood = energy >= 80 ? "开心" : energy >= 50 ? "一般" : "疲惫";
            System.out.println(name + " - " + type + " | 体力:" + energy + " | 心情:" + mood);
        }
    }

    public static void main(String[] args) {
        // 对象 = 类的实例
        Pet cat = new Pet("小花", "猫");
        Pet dog = new Pet("旺财", "狗");

        System.out.println("===== 宠物状态 =====");
        cat.status();
        dog.status();

        System.out.println("\n===== 互动 =====");
        cat.play();
        cat.play();
        cat.feed();
        dog.play();

        System.out.println("\n===== 最终状态 =====");
        cat.status();
        dog.status();

        // 两个对象互相独立
        System.out.println("\n===== 对象独立性 =====");
        System.out.println("cat和dog是同一个对象吗? " + (cat == dog));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

# 7.1.4 训练题

  1. 填空题:类是对象的 ___(蓝图),对象是类的 ___。

  2. 选择题:以下关于 Java 对象创建的说法,正确的是?

    • A. Java 对象可以在栈上创建 B. 所有 Java 对象都通过 new 在堆上创建 C. Java 对象需要手动释放 D. Java 的 new 和 C++ 的 new 用法完全一致
  3. 思考题:面向对象的三大特性(封装、继承、多态)之间有什么关系?为什么说封装是基础,多态是目标?请结合实际例子说明。

# 7.2 类定义和对象

# 7.2.1 类的定义

public class Account {
    // 成员变量(属性)
    private String name;
    private double balance;

    // 构造方法
    public Account(String name, double balance) {
        this.name = name;
        this.balance = balance;
    }

    // 成员方法(行为)
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            System.out.println(name + " 存入 " + amount + ",余额:" + balance);
        }
    }

    public void withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            System.out.println(name + " 取出 " + amount + ",余额:" + balance);
        }
    }

    // getter 和 setter
    public String getName() { return name; }
    public double getBalance() { return balance; }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 7.2.2 创建对象

// 使用 new 创建对象
Account acc1 = new Account("张三", 1000);
Account acc2 = new Account("李四", 2000);
1
2
3

对比 C++:

// C++ 可以在栈上创建
Account acc1("张三", 1000);
// C++ 也可以在堆上创建
Account* acc2 = new Account("李四", 2000);
delete acc2;  // 需要手动释放
1
2
3
4
5

Java 不需要 delete,GC 会自动回收不再使用的对象。

# 7.2.3 访问类成员

Account acc = new Account("张三", 1000);
acc.deposit(500);
acc.withdraw(200);
System.out.println(acc.getName());     // 张三
System.out.println(acc.getBalance());  // 1300
1
2
3
4
5

# 7.2.4 案例实践

public class Main {
    public static void main(String[] args) {
        Account acc1 = new Account("张三", 1000);
        Account acc2 = new Account("李四", 2000);

        acc1.deposit(500);   // 张三 存入 500,余额:1500
        acc2.withdraw(800);  // 李四 取出 800,余额:1200

        System.out.println(acc1.getName() + " 余额:" + acc1.getBalance());
        System.out.println(acc2.getName() + " 余额:" + acc2.getBalance());
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 7.2.5 综合案例:银行账户系统

编写一个完整的银行账户类,综合运用类定义、构造方法、实例方法、封装和对象交互。

public class BankAccountDemo {
    static class BankAccount {
        private String owner;
        private String accountId;
        private double balance;
        private static int nextId = 1001;

        BankAccount(String owner, double initBalance) {
            this.owner = owner;
            this.accountId = "ACC" + nextId++;
            this.balance = Math.max(initBalance, 0);
        }

        public boolean deposit(double amount) {
            if (amount <= 0) return false;
            balance += amount;
            System.out.printf("  [%s] %s 存入 %.2f, 余额: %.2f%n", accountId, owner, amount, balance);
            return true;
        }

        public boolean withdraw(double amount) {
            if (amount <= 0 || amount > balance) {
                System.out.printf("  [%s] %s 取款失败! 余额不足%n", accountId, owner);
                return false;
            }
            balance -= amount;
            System.out.printf("  [%s] %s 取出 %.2f, 余额: %.2f%n", accountId, owner, amount, balance);
            return true;
        }

        public boolean transfer(BankAccount target, double amount) {
            if (this.withdraw(amount)) {
                target.deposit(amount);
                System.out.printf("  → %s 向 %s 转账 %.2f 成功%n", owner, target.owner, amount);
                return true;
            }
            return false;
        }

        public String toString() {
            return String.format("[%s] %s: ¥%.2f", accountId, owner, balance);
        }
    }

    public static void main(String[] args) {
        BankAccount acc1 = new BankAccount("张三", 5000);
        BankAccount acc2 = new BankAccount("李四", 3000);

        System.out.println("===== 初始状态 =====");
        System.out.println(acc1);
        System.out.println(acc2);

        System.out.println("\n===== 操作 =====");
        acc1.deposit(2000);
        acc2.withdraw(1000);
        acc1.transfer(acc2, 3000);

        System.out.println("\n===== 最终状态 =====");
        System.out.println(acc1);
        System.out.println(acc2);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

# 7.2.6 训练题

  1. 实践题:定义一个 Student 类,包含 name(String)、age(int)、score(double)三个属性,提供有参构造方法和一个 introduce() 方法输出学生信息。在 main 中创建 3 个学生对象并调用 introduce()。

  2. 代码题:以下代码的输出是什么?

    Account a1 = new Account("张三", 100);
    Account a2 = a1;
    a2.deposit(200);
    System.out.println(a1.getBalance());
    
    1
    2
    3
    4
  3. 思考题:Account a1 = new Account("张三", 100); 这行代码在内存中发生了什么?栈和堆分别存了什么?如果 a1 = null,原来的对象会怎样?

# 7.3 类成员属性

# 7.3.1 成员变量

public class Account {
    // 实例变量:每个对象各自拥有
    String name;
    double balance;

    // 类变量(静态变量):所有对象共享
    static int totalCount = 0;
}
1
2
3
4
5
6
7
8

成员变量的默认值:

类型 默认值
int、long 等整数类型 0
float、double 0.0
boolean false
char '\u0000'
引用类型 null

# 7.3.2 成员方法

public class Account {
    private String name;
    private double balance;

    // 实例方法
    public void deposit(double amount) {
        balance += amount;
    }

    // 静态方法
    public static void printWelcome() {
        System.out.println("欢迎使用银行系统");
    }
}

// 调用
Account acc = new Account();
acc.deposit(100);           // 实例方法通过对象调用
Account.printWelcome();     // 静态方法通过类名调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 7.3.3 访问权限

public class Account {
    public String name;          // 公共
    protected double balance;    // 受保护
    int id;                      // 默认(包访问)
    private String password;     // 私有
}
1
2
3
4
5
6

# 7.3.4 封装思想

封装的核心思想:属性私有化,方法公开化。

public class Account {
    // 属性私有
    private String name;
    private double balance;

    // 通过公开的方法访问
    public String getName() {
        return name;
    }

    public void setName(String name) {
        // 可以在 setter 中加入验证逻辑
        if (name != null && !name.isEmpty()) {
            this.name = name;
        }
    }

    public double getBalance() {
        return balance;
    }

    // balance 只提供 getter,不提供 setter(只读)
    // 余额只能通过 deposit 和 withdraw 修改
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 7.3.5 综合案例:学生信息封装

编写一个 Student 类,综合运用属性封装(getter/setter)、数据验证和 this 关键字。

public class StudentEncapsulation {
    static class Student {
        private String name;
        private int age;
        private double score;

        public Student(String name, int age, double score) {
            setName(name);
            setAge(age);
            setScore(score);
        }

        public String getName() { return name; }
        public void setName(String name) {
            if (name == null || name.isBlank()) throw new IllegalArgumentException("姓名不能为空");
            this.name = name;
        }

        public int getAge() { return age; }
        public void setAge(int age) {
            if (age < 1 || age > 150) throw new IllegalArgumentException("年龄无效: " + age);
            this.age = age;
        }

        public double getScore() { return score; }
        public void setScore(double score) {
            if (score < 0 || score > 100) throw new IllegalArgumentException("成绩无效: " + score);
            this.score = score;
        }

        public String getGrade() {
            if (score >= 90) return "优秀";
            if (score >= 80) return "良好";
            if (score >= 60) return "及格";
            return "不及格";
        }

        public String toString() {
            return String.format("%s (年龄:%d, 成绩:%.1f, 等级:%s)", name, age, score, getGrade());
        }
    }

    public static void main(String[] args) {
        Student s1 = new Student("张三", 20, 92.5);
        Student s2 = new Student("李四", 19, 78.0);
        System.out.println(s1);
        System.out.println(s2);

        // 通过setter修改(带验证)
        s2.setScore(85.0);
        System.out.println("修改后: " + s2);

        // 验证失败演示
        try {
            new Student("", 20, 80);
        } catch (IllegalArgumentException e) {
            System.out.println("验证失败: " + e.getMessage());
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

# 7.3.6 训练题

  1. 改错题:以下代码违反了封装原则,请找出问题并修正:

    public class User {
        public String password;
        public double salary;
    }
    // 外部直接访问
    User u = new User();
    u.password = "";
    u.salary = -100;
    
    1
    2
    3
    4
    5
    6
    7
    8
  2. 实践题:设计一个 BankCard 类,要求:密码(password)只能通过 changePassword(旧密码, 新密码) 方法修改;余额(balance)只能通过 deposit() 和 withdraw() 方法修改,且余额不能为负数。

  3. 思考题:封装不只是"把属性设为 private 加 getter/setter"。有人说"给每个字段都加 getter/setter 和公开字段没区别",你同意吗?什么情况下不应该提供 setter?

# 7.4 对象初始化

# 7.4.1 构造方法

构造方法在创建对象时自动调用,用于初始化对象。构造方法的名字必须与类名相同,没有返回类型。

public class Account {
    private String name;
    private double balance;

    // 无参构造
    public Account() {
        this.name = "未命名";
        this.balance = 0;
    }

    // 有参构造
    public Account(String name, double balance) {
        this.name = name;
        this.balance = balance;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

注意:如果你不写任何构造方法,编译器会自动提供一个默认的无参构造方法。但如果你写了有参构造,编译器就不会自动生成无参构造了。

对比 C++:C++ 有构造函数和析构函数。Java 有构造方法但没有析构函数(有 finalize 方法,但已被弃用),内存由 GC 管理。

# 7.4.2 构造方法重载

public class Account {
    private String name;
    private double balance;
    private String type;

    public Account() {
        this("未命名", 0, "普通");  // 调用另一个构造方法
    }

    public Account(String name) {
        this(name, 0, "普通");
    }

    public Account(String name, double balance, String type) {
        this.name = name;
        this.balance = balance;
        this.type = type;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 7.4.3 this关键字

this 指向当前对象的引用:

public class Account {
    private String name;

    public Account(String name) {
        this.name = name;  // this.name 是成员变量,name 是参数
    }

    public Account getThis() {
        return this;  // 返回当前对象
    }
}
1
2
3
4
5
6
7
8
9
10
11

对比 C++:C++ 的 this 是指针(this->),Java 的 this 是引用(this.)。

# 7.4.4 构造代码块和静态代码块

public class Account {
    static int count;

    // 静态代码块:类加载时执行一次
    static {
        count = 0;
        System.out.println("静态代码块执行");
    }

    // 构造代码块:每次创建对象时执行(在构造方法之前)
    {
        count++;
        System.out.println("构造代码块执行");
    }

    public Account() {
        System.out.println("构造方法执行");
    }
}

// 执行顺序:静态代码块 → 构造代码块 → 构造方法

**代码块执行顺序的底层原理**:编译器在生成字节码时,会将所有**静态代码块**合并到 `<clinit>` 方法中(类初始化方法),在类首次加载时由 JVM 保证**线程安全**地执行一次。所有**构造代码块**会被编译器插入到每个构造方法的 `<init>` 方法中、`super()` 调用之后、构造方法体代码之前。这就解释了为什么构造代码块在每次创建对象时都执行,而且总是在构造方法体之前执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 7.4.5 对象生命周期

public void method() {
    Account acc = new Account("张三", 1000);  // 对象创建,在堆上分配内存
    acc.deposit(500);
}  // 方法结束,acc 引用失效,对象变成垃圾,等待 GC 回收
1
2
3
4

对比 C++:C++ 需要手动 delete 释放堆上对象,栈上对象在作用域结束时自动销毁(RAII)。Java 全部由 GC 自动管理。

# 7.4.6 综合案例:对象生命周期跟踪器

编写一个程序,通过构造方法、初始化块和 toString 演示对象从创建到使用的完整生命周期。

public class LifecycleTracker {
    static class TrackedObject {
        private static int totalCreated = 0;
        private int id;
        private String name;

        // 静态初始化块(类加载时执行一次)
        static {
            System.out.println("[类加载] TrackedObject 类被加载");
        }

        // 实例初始化块(每次创建对象时执行)
        {
            totalCreated++;
            this.id = totalCreated;
            System.out.println("[初始化块] 对象#" + id + " 初始化块执行");
        }

        // 无参构造
        TrackedObject() {
            this("未命名");
            System.out.println("[构造方法] 无参构造完成");
        }

        // 有参构造
        TrackedObject(String name) {
            this.name = name;
            System.out.println("[构造方法] 对象#" + id + " \"" + name + "\" 创建完成");
        }

        public static int getTotalCreated() { return totalCreated; }
        public String toString() { return "TrackedObject#" + id + "(" + name + ")"; }
    }

    public static void main(String[] args) {
        System.out.println("===== 创建对象 =====");
        TrackedObject obj1 = new TrackedObject("Alpha");
        System.out.println();
        TrackedObject obj2 = new TrackedObject("Beta");
        System.out.println();
        TrackedObject obj3 = new TrackedObject();  // 无参→调用有参(this)

        System.out.println("\n===== 对象信息 =====");
        System.out.println(obj1);
        System.out.println(obj2);
        System.out.println(obj3);
        System.out.println("总共创建: " + TrackedObject.getTotalCreated() + " 个对象");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

# 7.4.7 训练题

  1. 代码题:以下代码的输出顺序是什么?

    public class Demo {
        static { System.out.println("A"); }
        { System.out.println("B"); }
        public Demo() { System.out.println("C"); }
        public static void main(String[] args) {
            new Demo();
            new Demo();
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  2. 实践题:创建一个 Order 类,提供三个构造方法(无参、只传订单号、传订单号+金额+描述),使用 this() 实现构造方法的链式调用,避免重复代码。

  3. 思考题:Java 没有 C++ 的析构函数(~ClassName()),那么如果一个对象持有了文件句柄或数据库连接等资源,应该如何确保这些资源被正确释放?

# 7.5 static关键字

# 7.5.1 静态变量

public class Account {
    static int totalCount = 0;  // 所有对象共享
    String name;                // 每个对象各自拥有

    public Account(String name) {
        this.name = name;
        totalCount++;
    }
}

new Account("张三");
new Account("李四");
System.out.println(Account.totalCount);  // 2
1
2
3
4
5
6
7
8
9
10
11
12
13

# 7.5.2 静态方法

public class MathUtils {
    public static int max(int a, int b) {
        return a > b ? a : b;
    }

    public static int min(int a, int b) {
        return a < b ? a : b;
    }
}

// 通过类名调用
int result = MathUtils.max(10, 20);
1
2
3
4
5
6
7
8
9
10
11
12

# 7.5.3 静态常量

public class Constants {
    public static final double PI = 3.14159265;
    public static final int MAX_SIZE = 100;
    public static final String APP_NAME = "银行系统";
}

System.out.println(Constants.PI);
1
2
3
4
5
6
7

# 7.5.4 综合案例:计数器与工具类

编写一个程序,展示 static 关键字在计数器(共享状态)和工具类(无需实例化)中的典型应用。

public class StaticDemo {
    // 计数器类:static变量在所有实例间共享
    static class Counter {
        private static int count = 0;
        private int id;

        Counter() { this.id = ++count; }
        public int getId() { return id; }
        public static int getCount() { return count; }
        public static void reset() { count = 0; }
    }

    // 工具类:全部是static方法,构造方法私有
    static class MathUtil {
        private MathUtil() {}  // 禁止实例化

        public static int max(int... nums) {
            int m = nums[0];
            for (int n : nums) if (n > m) m = n;
            return m;
        }

        public static boolean isPrime(int n) {
            if (n <= 1) return false;
            for (int i = 2; i * i <= n; i++) {
                if (n % i == 0) return false;
            }
            return true;
        }

        public static int gcd(int a, int b) {
            return b == 0 ? a : gcd(b, a % b);
        }
    }

    public static void main(String[] args) {
        // 计数器演示
        System.out.println("===== Static计数器 =====");
        Counter c1 = new Counter();
        Counter c2 = new Counter();
        Counter c3 = new Counter();
        System.out.println("c1.id=" + c1.getId() + " c2.id=" + c2.getId() + " c3.id=" + c3.getId());
        System.out.println("总计: " + Counter.getCount());

        // 工具类演示
        System.out.println("\n===== Static工具类 =====");
        System.out.println("max(3,7,2,9,1) = " + MathUtil.max(3, 7, 2, 9, 1));
        System.out.println("isPrime(17) = " + MathUtil.isPrime(17));
        System.out.println("gcd(12, 18) = " + MathUtil.gcd(12, 18));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

# 7.5.5 训练题

  1. 判断题:静态方法中可以直接访问实例变量。( )

  2. 代码题:以下代码能编译通过吗?如果不能,说明原因并修正:

    public class Counter {
        private int count = 0;
        public static void increment() {
            count++;
        }
    }
    
    1
    2
    3
    4
    5
    6
  3. 实践题:利用 static 实现一个单例模式的 Config 类:只允许创建一个实例,通过 Config.getInstance() 获取。

  4. 思考题:什么时候应该用 static 方法,什么时候用实例方法?工具类(如 Math、Collections)为什么全是 static 方法?

# 7.6 内部类

# 7.6.1 成员内部类

public class Bank {
    private String name = "中国银行";

    // 成员内部类
    public class Account {
        public void showBankName() {
            System.out.println(name);  // 可以访问外部类的私有成员
        }
    }
}

Bank bank = new Bank();
Bank.Account acc = bank.new Account();
acc.showBankName();
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 7.6.2 静态内部类

public class Bank {
    // 静态内部类
    public static class Config {
        public static String getVersion() {
            return "1.0";
        }
    }
}

System.out.println(Bank.Config.getVersion());
1
2
3
4
5
6
7
8
9
10

# 7.6.3 匿名内部类

// 接口
interface Callback {
    void onComplete(String result);
}

// 使用匿名内部类
Callback callback = new Callback() {
    @Override
    public void onComplete(String result) {
        System.out.println("完成:" + result);
    }
};
callback.onComplete("转账成功");

// JDK 8+ 可以用 Lambda 简写(函数式接口)
Callback callback2 = result -> System.out.println("完成:" + result);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 7.6.4 综合案例:事件回调系统

编写一个程序,使用成员内部类、静态内部类和匿名内部类实现一个简单的事件监听系统。

public class EventSystemDemo {
    // 接口(事件监听器)
    interface EventListener {
        void onEvent(String event);
    }

    // 外部类:事件源
    static class EventSource {
        private String name;
        private EventListener listener;

        EventSource(String name) { this.name = name; }
        public void setListener(EventListener listener) { this.listener = listener; }

        public void trigger(String event) {
            System.out.println("[" + name + "] 触发事件: " + event);
            if (listener != null) listener.onEvent(event);
        }
    }

    // 静态内部类:日志监听器
    static class LogListener implements EventListener {
        public void onEvent(String event) {
            System.out.println("  [日志] 记录事件: " + event);
        }
    }

    public static void main(String[] args) {
        EventSource button = new EventSource("按钮");
        EventSource timer = new EventSource("定时器");

        // 使用静态内部类
        System.out.println("===== 静态内部类 =====");
        button.setListener(new LogListener());
        button.trigger("点击");

        // 使用匿名内部类
        System.out.println("\n===== 匿名内部类 =====");
        timer.setListener(new EventListener() {
            @Override
            public void onEvent(String event) {
                System.out.println("  [匿名] 处理事件: " + event);
            }
        });
        timer.trigger("超时");

        // 使用Lambda(JDK8+,函数式接口)
        System.out.println("\n===== Lambda =====");
        button.setListener(event -> System.out.println("  [Lambda] 响应事件: " + event));
        button.trigger("双击");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

# 7.6.5 训练题

  1. 选择题:以下关于内部类的说法,正确的是?

    • A. 成员内部类可以用 new Outer.Inner() 直接创建 B. 静态内部类可以访问外部类的实例变量 C. 匿名内部类可以用 Lambda 替代(限函数式接口) D. 局部内部类可以用 public 修饰
  2. 实践题:定义一个 EventBus 类,包含一个接口 EventListener(有 onEvent(String msg) 方法)。使用匿名内部类和 Lambda 两种方式分别创建监听器并调用。

  3. 思考题:Android 开发中,成员内部类持有外部类的引用可能导致内存泄漏。请解释这个问题的原因,以及如何用静态内部类 + 弱引用来解决。

# 7.7 枚举类

# 7.7.1 枚举定义

public enum AccountType {
    SAVINGS,     // 储蓄账户
    CHECKING,    // 活期账户
    CREDIT       // 信用账户
}

// 使用
AccountType type = AccountType.SAVINGS;
System.out.println(type);  // SAVINGS
1
2
3
4
5
6
7
8
9

# 7.7.2 枚举方法

public enum AccountType {
    SAVINGS("储蓄账户", 0.03),
    CHECKING("活期账户", 0.01),
    CREDIT("信用账户", 0.18);

    private String desc;
    private double rate;

    AccountType(String desc, double rate) {
        this.desc = desc;
        this.rate = rate;
    }

    public String getDesc() { return desc; }
    public double getRate() { return rate; }
}

// 使用
AccountType type = AccountType.SAVINGS;
System.out.println(type.getDesc());  // 储蓄账户
System.out.println(type.getRate());  // 0.03

// 遍历所有枚举值
for (AccountType t : AccountType.values()) {
    System.out.println(t.name() + ": " + t.getDesc());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

对比 C++:C++ 的 enum class 只是整数常量的集合,Java 的枚举是完整的类,可以有属性、方法、构造方法,功能强大得多。

# 7.7.3 综合案例:交通信号灯枚举

编写一个交通信号灯枚举,展示枚举的属性、方法、switch 使用和遍历。

public class TrafficLightDemo {
    enum TrafficLight {
        RED("红灯", 30, "停"),
        YELLOW("黄灯", 5, "注意"),
        GREEN("绿灯", 25, "行");

        private final String name;
        private final int duration;
        private final String action;

        TrafficLight(String name, int duration, String action) {
            this.name = name;
            this.duration = duration;
            this.action = action;
        }

        public String getName() { return name; }
        public int getDuration() { return duration; }

        public TrafficLight next() {
            return switch (this) {
                case RED -> GREEN;
                case GREEN -> YELLOW;
                case YELLOW -> RED;
            };
        }
    }

    public static void main(String[] args) {
        // 遍历所有枚举值
        System.out.println("===== 所有信号 =====");
        for (TrafficLight light : TrafficLight.values()) {
            System.out.printf("  %s: %s %d秒 → %s%n",
                    light.name(), light.getName(), light.getDuration(), light.action);
        }

        // switch匹配
        System.out.println("\n===== 信号切换模拟 =====");
        TrafficLight current = TrafficLight.RED;
        for (int i = 0; i < 6; i++) {
            System.out.println("  当前: " + current.getName() + " → 下一个: " + current.next().getName());
            current = current.next();
        }

        // 枚举比较
        System.out.println("\n===== 枚举比较 =====");
        System.out.println("RED == RED: " + (TrafficLight.RED == TrafficLight.RED));
        System.out.println("RED.ordinal(): " + TrafficLight.RED.ordinal());
        System.out.println("valueOf(\"GREEN\"): " + TrafficLight.valueOf("GREEN"));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

# 7.7.4 训练题

  1. 实践题:定义一个 Season 枚举,包含四季(SPRING、SUMMER、AUTUMN、WINTER),每个季节有中文名和平均温度两个属性。编写代码遍历所有季节并输出信息。

  2. 代码题:枚举可以用在 switch 语句中,编写一个方法接收 AccountType 枚举参数,根据不同类型返回不同的利率。

  3. 思考题:为什么《Effective Java》推荐使用枚举来实现单例模式?相比普通的单例写法,枚举单例有什么优势?

上次更新: 2026/06/10, 11:13:41
函数方法
继承和多态

← 函数方法 继承和多态→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式