第 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();
}
}