V
Vel·ToolKit
简洁 · 高效 · 即开即用
ZH
第 6 章 / 共 20 章

类与对象

构造器、字段、封装、static、final、内部类

类的声明

类是"对象的蓝图":描述对象有哪些字段(状态)和方法(行为)。new ClassName() 创建实例,每个实例有自己独立的字段值。

// User.java
public class User {
    // 字段(实例变量)
    String name;
    int age;

    // 方法
    void hello() {
        System.out.println("Hi, I'm " + name);
    }

    public static void main(String[] args) {
        User u = new User();   // 创建对象
        u.name = "Alice";       // 字段赋值
        u.age = 30;
        u.hello();
    }
}

构造器与 this

构造器是创建对象时自动调用的特殊方法:名字与类名相同、没有返回类型。this 指代当前对象本身,常用来消除字段与参数同名时的歧义。

// User.java
public class User {
    String name;
    int age;

    public User(String name, int age) {
        this.name = name;   // this.name 是字段,name 是参数
        this.age = age;
    }

    public User(String name) {
        this(name, 0);       // 调用另一个构造器(必须是第一行)
    }

    public static void main(String[] args) {
        User a = new User("Alice", 30);
        User b = new User("Bob");
        System.out.println(a.name + " " + a.age);
        System.out.println(b.name + " " + b.age);
    }
}

字段初始化顺序

字段初始化按声明顺序进行:先设零值,再执行字面量初始化与初始化块,最后执行构造器。static 字段在类加载时初始化一次。

// InitOrder.java
public class InitOrder {
    int a = init("field a", 1);

    {
        // 实例初始化块(每次 new 都跑)
        System.out.println("instance block");
    }

    static int s = init("static s", 99);

    static {
        // 静态初始化块(类加载时跑一次)
        System.out.println("static block");
    }

    public InitOrder() {
        System.out.println("constructor");
    }

    static int init(String label, int v) {
        System.out.println("init " + label);
        return v;
    }

    public static void main(String[] args) {
        System.out.println("--- new 1 ---");
        new InitOrder();
        System.out.println("--- new 2 ---");
        new InitOrder();
    }
}

封装:private 字段 + getter/setter

Java 推崇"字段私有 + 公开方法"的封装:外部访问对象状态必须经过方法,方便加校验、加锁、加日志,也保留改实现的余地。

// Account.java
public class Account {
    private String owner;
    private long balance; // 单位:分

    public Account(String owner, long initial) {
        if (initial < 0) throw new IllegalArgumentException("initial < 0");
        this.owner = owner;
        this.balance = initial;
    }

    public String getOwner() { return owner; }
    public long getBalance() { return balance; }

    public void deposit(long amount) {
        if (amount <= 0) throw new IllegalArgumentException("amount <= 0");
        this.balance += amount;
    }

    public boolean withdraw(long amount) {
        if (amount <= 0 || amount > balance) return false;
        balance -= amount;
        return true;
    }

    public static void main(String[] args) {
        Account a = new Account("Alice", 10000);
        a.deposit(5000);
        System.out.println(a.getBalance()); // 15000
        System.out.println(a.withdraw(20000)); // false
        System.out.println(a.getBalance()); // 15000
    }
}

static 字段与方法

static 修饰的字段和方法属于"类"而不是"实例",所有对象共享同一份。通过 类名.成员 访问;static 方法不能直接访问实例字段。常用于工具方法、常量、计数器。

// Counter.java
public class Counter {
    static int totalCount = 0;     // 所有实例共享
    int instanceId;

    public Counter() {
        totalCount++;
        this.instanceId = totalCount;
    }

    static int getTotal() {        // static 方法
        return totalCount;
    }

    public static void main(String[] args) {
        new Counter();
        new Counter();
        new Counter();
        System.out.println(Counter.getTotal());      // 3
        System.out.println(Counter.totalCount);      // 3
    }
}

final 字段(不可变)

final 字段只能赋值一次(构造器或字面量初始化里)。常用于构造"不可变对象"——线程安全且能安全地作为 Map 的 key。

// Point.java
public final class Point {  // 类本身也可以 final,禁止被继承
    private final double x;
    private final double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public double getX() { return x; }
    public double getY() { return y; }

    public Point translate(double dx, double dy) {
        return new Point(x + dx, y + dy);   // 不修改自己,返回新对象
    }

    @Override
    public String toString() {
        return "(" + x + ", " + y + ")";
    }

    public static void main(String[] args) {
        Point p = new Point(1, 2);
        Point q = p.translate(3, 4);
        System.out.println(p);  // (1.0, 2.0)
        System.out.println(q);  // (4.0, 6.0)
    }
}

内部类与匿名类

类可以嵌套定义。常用三种:static 嵌套类(独立的命名空间)、内部类(持有外层类引用)、匿名类(一次性实现接口/继承类)。Java 14+ 还有 record(见第 10 章)替代很多匿名类用法。

// Nested.java
public class Nested {

    // static 嵌套类:和外层类只是命名空间关系
    static class Pair {
        final String key;
        final int value;
        Pair(String k, int v) { this.key = k; this.value = v; }
    }

    // 内部类:每个实例持有外层 Nested 实例的引用
    class Inner {
        void show() {
            System.out.println("outer count = " + count);
        }
    }

    int count = 10;

    public static void main(String[] args) {
        // 用 static 嵌套类
        Pair p = new Pair("x", 1);
        System.out.println(p.key + "=" + p.value);

        // 用内部类
        Nested outer = new Nested();
        Inner inner = outer.new Inner();
        inner.show();

        // 匿名类:一次性实现 Runnable
        Runnable r = new Runnable() {
            @Override public void run() {
                System.out.println("running");
            }
        };
        r.run();
    }
}