Java类代码块与枚举详解:从基础到实战
·
1. 引言
在Java编程中,类的代码块和枚举是两个重要但容易被忽视的特性。代码块用于控制类的初始化过程,而枚举则提供了一种类型安全的方式来表示固定数量的常量。本文将结合代码示例,详细讲解这两种特性的工作原理、使用场景和最佳实践。
2. 类的代码块详解
2.1 什么是代码块
代码块(Code Block)是Java中用于组织代码的一种结构,它可以包含在类中,用于执行初始化操作。根据定义位置和执行时机的不同,代码块主要分为以下几种类型:
- 实例初始化块(Instance Initializer Block)
- 静态初始化块(Static Initializer Block)
- 局部代码块(Local Block)
2.2 实例初始化块
实例初始化块在每次创建对象时执行,先于构造器执行。如果类中有多个实例初始化块,它们将按照在类中出现的顺序依次执行。
public class InstanceBlockDemo {
private int x;
private String name;
// 实例初始化块1
{
System.out.println("第一个实例初始化块执行");
x = 10;
}
// 实例初始化块2
{
System.out.println("第二个实例初始化块执行");
name = "默认名称";
}
public InstanceBlockDemo() {
System.out.println("构造器执行,x=" + x + ", name=" + name);
}
public InstanceBlockDemo(String customName) {
this();
this.name = customName;
System.out.println("带参数的构造器执行,name=" + name);
}
public static void main(String[] args) {
System.out.println("创建第一个对象:");
InstanceBlockDemo obj1 = new InstanceBlockDemo();
System.out.println("\n创建第二个对象:");
InstanceBlockDemo obj2 = new InstanceBlockDemo("自定义名称");
}
}
输出结果:
创建第一个对象:
第一个实例初始化块执行
第二个实例初始化块执行
构造器执行,x=10, name=默认名称
创建第二个对象:
第一个实例初始化块执行
第二个实例初始化块执行
构造器执行,x=10, name=默认名称
带参数的构造器执行,name=自定义名称
2.3 静态初始化块
静态初始化块在类加载时执行,且只执行一次。它用于初始化静态变量或执行只需要执行一次的初始化操作。
public class StaticBlockDemo {
private static int staticCounter;
private static List<String> dataList;
// 静态初始化块
static {
System.out.println("静态初始化块执行 - 开始");
staticCounter = 0;
dataList = new ArrayList<>();
dataList.add("初始化数据1");
dataList.add("初始化数据2");
// 可以执行复杂的初始化逻辑
try {
// 模拟加载配置文件
System.out.println("加载配置文件...");
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("静态初始化块执行 - 结束");
}
// 实例初始化块
{
staticCounter++;
System.out.println("实例初始化块执行,当前对象编号:" + staticCounter);
}
public StaticBlockDemo() {
System.out.println("构造器执行");
}
public static void main(String[] args) {
System.out.println("主方法开始执行");
System.out.println("\n创建第一个对象:");
StaticBlockDemo obj1 = new StaticBlockDemo();
System.out.println("\n创建第二个对象:");
StaticBlockDemo obj2 = new StaticBlockDemo();
System.out.println("\n静态数据列表:" + dataList);
System.out.println("总共创建的对象数:" + staticCounter);
}
}
输出结果:
静态初始化块执行 - 开始
加载配置文件...
静态初始化块执行 - 结束
主方法开始执行
创建第一个对象:
实例初始化块执行,当前对象编号:1
构造器执行
创建第二个对象:
实例初始化块执行,当前对象编号:2
构造器执行
静态数据列表:[初始化数据1, 初始化数据2]
总共创建的对象数:2
2.4 执行顺序总结
当创建对象时,代码块的执行顺序如下:
- 父类静态初始化块(如果存在继承关系)
- 子类静态初始化块
- 父类实例初始化块
- 父类构造器
- 子类实例初始化块
- 子类构造器
class Parent {
static {
System.out.println("父类静态初始化块");
}
{
System.out.println("父类实例初始化块");
}
public Parent() {
System.out.println("父类构造器");
}
}
class Child extends Parent {
static {
System.out.println("子类静态初始化块");
}
{
System.out.println("子类实例初始化块");
}
public Child() {
System.out.println("子类构造器");
}
}
public class ExecutionOrderDemo {
public static void main(String[] args) {
System.out.println("第一次创建子类对象:");
Child child1 = new Child();
System.out.println("\n第二次创建子类对象:");
Child child2 = new Child();
}
}
输出结果:
第一次创建子类对象:
父类静态初始化块
子类静态初始化块
父类实例初始化块
父类构造器
子类实例初始化块
子类构造器
第二次创建子类对象:
父类实例初始化块
父类构造器
子类实例初始化块
子类构造器
3. 枚举详解
3.1 枚举的基本概念
枚举(Enum)是Java 5引入的一种特殊的数据类型,用于定义一组命名的常量。与使用整数常量或字符串常量相比,枚举提供了更好的类型安全性和可读性。
3.2 基本枚举定义
// 简单的枚举定义
public enum Day {
MONDAY, // 周一
TUESDAY, // 周二
WEDNESDAY, // 周三
THURSDAY, // 周四
FRIDAY, // 周五
SATURDAY, // 周六
SUNDAY // 周日
}
public class BasicEnumDemo {
public static void main(String[] args) {
// 使用枚举
Day today = Day.MONDAY;
System.out.println("今天是:" + today);
// 遍历所有枚举值
System.out.println("\n一周的所有天:");
for (Day day : Day.values()) {
System.out.println(day);
}
// 获取枚举的序号
System.out.println("\n枚举序号:");
System.out.println("MONDAY的序号:" + Day.MONDAY.ordinal());
System.out.println("SUNDAY的序号:" + Day.SUNDAY.ordinal());
// 根据字符串获取枚举
Day parsedDay = Day.valueOf("FRIDAY");
System.out.println("\n解析的枚举:" + parsedDay);
}
}
3.3 带属性和方法的枚举
枚举可以像类一样拥有属性、构造器和方法,这使得枚举更加灵活和强大。
public enum Planet {
// 枚举常量,调用构造器
MERCURY("水星", 3.303e+23, 2.4397e6),
VENUS("金星", 4.869e+24, 6.0518e6),
EARTH("地球", 5.976e+24, 6.37814e6),
MARS("火星", 6.421e+23, 3.3972e6),
JUPITER("木星", 1.9e+27, 7.1492e7),
SATURN("土星", 5.688e+26, 6.0268e7),
URANUS("天王星", 8.686e+25, 2.5559e7),
NEPTUNE("海王星", 1.024e+26, 2.4746e7);
// 枚举属性
private final String chineseName;
private final double mass; // 质量(千克)
private final double radius; // 半径(米)
// 枚举构造器(必须是private或package-private)
Planet(String chineseName, double mass, double radius) {
this.chineseName = chineseName;
this.mass = mass;
this.radius = radius;
}
// 枚举方法
public double getMass() {
return mass;
}
public double getRadius() {
return radius;
}
public String getChineseName() {
return chineseName;
}
// 计算表面重力
public double surfaceGravity() {
final double G = 6.67300E-11; // 万有引力常数
return G * mass / (radius * radius);
}
// 计算物体在行星表面的重量
public double surfaceWeight(double otherMass) {
return otherMass * surfaceGravity();
}
// 静态方法:根据中文名查找行星
public static Planet findByChineseName(String name) {
for (Planet planet : values()) {
if (planet.chineseName.equals(name)) {
return planet;
}
}
throw new IllegalArgumentException("未找到名为 " + name + " 的行星");
}
}
public class AdvancedEnumDemo {
public static void main(String[] args) {
double earthWeight = 75; // 地球上的体重(千克)
System.out.println("行星信息比较:");
for (Planet planet : Planet.values()) {
System.out.printf("%s(%s):质量=%.2e kg,半径=%.2e m,重力=%.2f m/s²,体重=%.2f kg%n",
planet,
planet.getChineseName(),
planet.getMass(),
planet.getRadius(),
planet.surfaceGravity(),
planet.surfaceWeight(earthWeight));
}
// 使用静态方法
Planet earth = Planet.findByChineseName("地球");
System.out.println("\n查找到的行星:" + earth);
// 使用switch语句
System.out.println("\n行星分类:");
classifyPlanet(Planet.EARTH);
classifyPlanet(Planet.JUPITER);
}
private static void classifyPlanet(Planet planet) {
switch (planet) {
case MERCURY:
case VENUS:
case EARTH:
case MARS:
System.out.println(planet.getChineseName() + "是类地行星");
break;
case JUPITER:
case SATURN:
System.out.println(planet.getChineseName() + "是气态巨行星");
break;
case URANUS:
case NEPTUNE:
System.out.println(planet.getChineseName() + "是冰巨星");
break;
}
}
}
3.4 枚举实现接口
枚举可以实现接口,这使得枚举可以拥有多态行为。
// 定义操作接口
interface Operation {
double apply(double x, double y);
}
// 实现接口的枚举
public enum BasicOperation implements Operation {
PLUS("+") {
public double apply(double x, double y) {
return x + y;
}
},
MINUS("-") {
public double apply(double x, double y) {
return x - y;
}
},
TIMES("*") {
public double apply(double x, double y) {
return x * y;
}
},
DIVIDE("/") {
public double apply(double x, double y) {
if (y == 0) {
throw new ArithmeticException("除数不能为零");
}
return x / y;
}
};
private final String symbol;
BasicOperation(String symbol) {
this.symbol = symbol;
}
@Override
public String toString() {
return symbol;
}
}
// 扩展的操作枚举
public enum ExtendedOperation implements Operation {
EXP("^") {
public double apply(double x, double y) {
return Math.pow(x, y);
}
},
MOD("%") {
public double apply(double x, double y) {
return x % y;
}
};
private final String symbol;
ExtendedOperation(String symbol) {
this.symbol = symbol;
}
@Override
public String toString() {
return symbol;
}
}
public class EnumInterfaceDemo {
public static void main(String[] args) {
double x = 10;
double y = 3;
System.out.println("基本运算:");
for (BasicOperation op : BasicOperation.values()) {
System.out.printf("%.1f %s %.1f = %.2f%n",
x, op, y, op.apply(x, y));
}
System.out.println("\n扩展运算:");
for (ExtendedOperation op : ExtendedOperation.values()) {
System.out.printf("%.1f %s %.1f = %.2f%n",
x, op, y, op.apply(x, y));
}
// 使用接口类型
System.out.println("\n使用接口类型:");
testOperation(BasicOperation.PLUS, x, y);
testOperation(ExtendedOperation.EXP, x, y);
}
private static void testOperation(Operation op, double x, double y) {
System.out.printf("测试 %s:%.1f %s %.1f = %.2f%n",
op.getClass().getSimpleName(), x, "?", y, op.apply(x, y));
}
}
3.5 枚举的单例模式
枚举是实现单例模式的最佳方式之一,它天生就是线程安全的,并且能防止反射攻击和序列化问题。
// 使用枚举实现单例模式
public enum Singleton {
INSTANCE;
private int counter = 0;
// 单例的业务方法
public void doSomething() {
counter++;
System.out.println("Singleton实例被调用,计数器:" + counter);
}
public int getCounter() {
return counter;
}
}
// 传统的单例实现(对比)
class TraditionalSingleton {
private static TraditionalSingleton instance;
private int counter = 0;
private TraditionalSingleton() {
// 防止反射攻击
if (instance != null) {
throw new RuntimeException("请使用getInstance()方法获取实例");
}
}
public static synchronized TraditionalSingleton getInstance() {
if (instance == null) {
instance = new TraditionalSingleton();
}
return instance;
}
public void doSomething() {
counter++;
System.out.println("TraditionalSingleton实例被调用,计数器:" + counter);
}
}
public class SingletonEnumDemo {
public static void main(String[] args) {
System.out.println("=== 枚举单例测试 ===");
Singleton singleton1 = Singleton.INSTANCE;
Singleton singleton2 = Singleton.INSTANCE;
singleton1.doSomething();
singleton2.doSomething();
System.out.println("两个引用是否相同:" + (singleton1 == singleton2));
System.out.println("计数器值:" + singleton1.getCounter());
System.out.println("\n=== 传统单例测试 ===");
TraditionalSingleton ts1 = TraditionalSingleton.getInstance();
TraditionalSingleton ts2 = TraditionalSingleton.getInstance();
ts1.doSomething();
ts2.doSomething();
System.out.println("两个引用是否相同:" + (ts1 == ts2));
}
}
4. 代码块与枚举的结合使用
4.1 枚举中的代码块
枚举中也可以使用静态初始化块和实例初始化块,用于初始化枚举常量。
public enum Status {
// 枚举常量
PENDING("等待中", 1) {
// 每个枚举常量可以有自己的匿名类实现
@Override
public boolean canTransitionTo(Status nextStatus) {
return nextStatus == PROCESSING || nextStatus == CANCELLED;
}
},
PROCESSING("处理中", 2) {
@Override
public boolean canTransitionTo(Status nextStatus) {
return nextStatus == COMPLETED || nextStatus == FAILED;
}
},
COMPLETED("已完成", 3) {
@Override
public boolean canTransitionTo(Status nextStatus) {
return false; // 已完成状态不能转换到其他状态
}
},
FAILED("失败", 4) {
@Override
public boolean canTransitionTo(Status nextStatus) {
return nextStatus == PENDING; // 失败后可以重新开始
}
},
CANCELLED("已取消", 5) {
@Override
public boolean canTransitionTo(Status nextStatus) {
return false; // 已取消状态不能转换
}
};
// 枚举属性
private final String description;
private final int code;
// 静态变量和静态初始化块
private static Map<Integer, Status> codeMap;
static {
System.out.println("Status枚举静态初始化块执行");
codeMap = new HashMap<>();
for (Status status : values()) {
codeMap.put(status.code, status);
System.out.println("初始化状态码映射:" + status.code + " -> " + status);
}
}
// 实例初始化块
{
System.out.println("初始化Status实例:" + this.name());
}
// 枚举构造器
Status(String description, int code) {
this.description = description;
this.code = code;
System.out.println("调用Status构造器:" + description);
}
// 抽象方法 - 每个枚举常量必须实现
public abstract boolean canTransitionTo(Status nextStatus);
// 静态方法:根据code查找Status
public static Status fromCode(int code) {
Status status = codeMap.get(code);
if (status == null) {
throw new IllegalArgumentException("无效的状态码:" + code);
}
return status;
}
// Getter方法
public String getDescription() {
return description;
更多推荐


所有评论(0)