类的基本结构
public class Person {
// 字段
private String name;
private int age;
// 构造函数
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 方法
public void greet() {
System.out.println("Hi, I'm " + name);
}
// getter / setter
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
// 用
var p = new Person("Alice", 30);
p.greet();
一文件多类
// Person.java
public class Person { ... } // public class 名字必须 = 文件名
class Helper { ... } // 包私有的辅助类,能在同文件
一个 public class 一文件。其他非 public 类可以同文件(少见)。
构造函数
public class Box {
private int width, height;
// 默认构造
public Box() {
width = 1; height = 1;
}
// 带参
public Box(int w, int h) {
this.width = w;
this.height = h;
}
// 调另一个构造
public Box(int size) {
this(size, size);
}
}
this(...) 调本类其他构造——必须是构造体第一行。
默认构造
如果不写任何构造,Java 自动给一个无参 public ClassName() {}。
写了任何构造(带参的)→ 默认构造消失。要无参构造必须显式写。
字段初始化
public class Config {
// 初始化字段时给默认值
private int retries = 3;
private String host = "localhost";
private List<String> tags = new ArrayList<>();
// 实例初始化块(不常用)
{
host = System.getenv("HOST");
}
public Config() {
// 构造里的赋值在字段初始化和实例块之后
}
}
字段没显式初始化时的默认值:
- 数值:0
- boolean:false
- 引用:null
final 字段
public class Order {
private final long createdAt; // 只能赋一次
public Order() {
createdAt = System.currentTimeMillis(); // ✅
}
public Order(long t) {
createdAt = t; // ✅
}
// createdAt = 0; // ❌
}
final 字段:只能在构造里赋一次。其他地方不行。
static 字段
public class Counter {
private static int total = 0; // 类级别,所有实例共享
public Counter() {
total++;
}
public static int getTotal() {
return total;
}
}
new Counter(); new Counter(); new Counter();
Counter.getTotal(); // 3
static 成员属于类,不属于实例。所有实例共享一份。
常量(static final)
public class Math2 {
public static final double PI = 3.14159265358979;
public static final int MAX_ITERATIONS = 100;
}
// 用
Math2.PI
约定:static final 字段全大写 + 下划线分隔。
static 初始化块
public class Config {
private static final Map<String, Integer> CONSTANTS;
static {
CONSTANTS = new HashMap<>();
CONSTANTS.put("a", 1);
CONSTANTS.put("b", 2);
}
}
类第一次加载时执行一次。线程安全。
但 Java 16+ 用 Map.of(...) 更简洁:
private static final Map<String, Integer> CONSTANTS = Map.of("a", 1, "b", 2);
getter / setter 模板
public class Person {
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
Java 没有 C# 的 property 语法——必须写完整方法。IDE(IntelliJ)一键生成。
或用 Lombok:
import lombok.Data;
@Data // 自动生成 getter / setter / toString / equals / hashCode
public class Person {
private String name;
private int age;
}
Lombok 是注解处理器——编译时生成代码。Java 项目里很常见。
或用 record(第 12 篇)——更现代的"数据类"。
内部类
public class Outer {
private int n = 1;
// 内部类(inner class):每个实例隐式持有外部实例
public class Inner {
public void show() {
System.out.println(n); // 能访问外部字段
}
}
// 静态嵌套类(static nested class):没有外部引用,像独立类
public static class Nested {
public void show() {
System.out.println("nested");
}
}
}
// 用
var outer = new Outer();
var inner = outer.new Inner(); // 内部类需要 outer 实例
var nested = new Outer.Nested(); // 静态嵌套不需要
优先用 static nested class——内部类的"隐式外部引用"是常见内存泄漏源。
匿名内部类
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("running");
}
};
Java 8 起绝大多数场景用 lambda 替代:
Runnable r = () -> System.out.println("running");
toString / equals / hashCode
public class Person {
private final String name;
private final int age;
public Person(String name, int age) { this.name = name; this.age = age; }
@Override
public String toString() {
return "Person{name=" + name + ", age=" + age + "}";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person p)) return false;
return age == p.age && Objects.equals(name, p.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
HashMap / HashSet 必须正确实现 equals + hashCode——否则 contains / get 失效。规则:相等的对象 hashCode 必须相等。
用 record 自动免去这些样板:
public record Person(String name, int age) {} // 自动有 equals/hashCode/toString
→ 下一篇 继承 + 多态