昨晚加班的时候,同事来旁边坐着跟我闲聊,不经意间看到屏幕上的new 对象代码,他说:"每次这样new 一个对象感觉好丑,有没有更好的方式方法!"。
我说:"有啊,不过也得看场景,简单的直接用 new 一把梭哈!复杂的再考虑其它的方式"
那有哪些方式呢?下面来分享一下。
1. new关键字
最直接的创建方式
java
// 就像我们平时说话:"来一辆新车"
Car car = new Car();
// 或者带参数的:"来一辆红色的宝马"
Car bmw = new Car("宝马", "红色");
使用场景 :这是最常用的方式,适合90%的日常开发场景。
优点:
- 简单直观,一眼就能看懂
- 性能好,直接调用构造方法
缺点:
- 不够灵活,创建逻辑与使用代码耦合
2. 反射机制
有时候我们需要在运行时才知道要创建什么类的对象,这时候反射就派上用场了。
java
// 传统方式:我知道要创建Car
Car car = new Car();
// 反射方式:运行时才知道要创建什么
try {
Class carClass = Class.forName("com.example.Car");
Constructor constructor = carClass.getConstructor();
Car car = (Car) constructor.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
案例 :Spring框架中,当我们配置了@Component注解的类,Spring就是通过反射来创建这些对象的。
java
// Spring内部大致是这样做的
Class beanClass = loadClass("com.example.UserService");
Object bean = beanClass.getDeclaredConstructor().newInstance();
3. clone()方法
假设你有一个配置好的对象,现在需要另一个一模一样的,这时候clone就很有用。
java
class UserSettings implements Cloneable {
private String theme = "dark";
private int fontSize = 14;
@Override
public UserSettings clone() {
try {
return (UserSettings) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
// 使用clone
UserSettings original = new UserSettings();
UserSettings cloned = original.clone(); // 得到一个完全相同的副本
注意点:
- 需要实现
Cloneable接口,否则会抛出异常 - 默认是浅拷贝,如果需要深拷贝要自己实现
4. 反序列化
当我们把对象保存到文件或通过网络传输后,需要重新把它变回对象。
java
// 对象需要实现Serializable接口
class GameSave implements Serializable {
private static final long serialVersionUID = 1L;
private int level;
private int score;
}
// 保存对象到文件
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("save.dat"))) {
oos.writeObject(gameSave);
}
// 从文件读取对象
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("save.dat"))) {
GameSave loadedSave = (GameSave) ois.readObject();
// loadedSave就是原来那个gameSave的副本
}
5. 静态工厂方法
有时候直接new不够表达我们的意图,这时候工厂方法就很有用。
java
class Color {
private int red, green, blue;
private Color(int r, int g, int b) {
this.red = r;
this.green = g;
this.blue = b;
}
// 静态工厂方法:语义更明确
public static Color fromRGB(int r, int g, int b) {
return new Color(r, g, b);
}
public static Color fromHex(String hex) {
// 解析十六进制颜色码
return new Color(/* 解析逻辑 */);
}
public static Color red() {
return new Color(255, 0, 0);
}
}
// 使用:这样读起来更自然
Color red = Color.red();
Color custom = Color.fromRGB(255, 128, 0);
优点:
- 方法名可以表达创建意图
- 可以缓存对象,避免重复创建
- 可以返回子类对象,更灵活
6. Builder模式
当对象有很多属性时,构造方法会变得很长很难读,Builder模式来解决!
java
class Computer {
private final String cpu; // 必须
private final String ram; // 必须
private final String storage; // 必须
private final String graphicsCard; // 可选
private final String monitor; // 可选
// 私有构造方法,只能通过Builder创建
private Computer(Builder builder) {
this.cpu = builder.cpu;
this.ram = builder.ram;
this.storage = builder.storage;
this.graphicsCard = builder.graphicsCard;
this.monitor = builder.monitor;
}
public static class Builder {
// 必须参数
private final String cpu;
private final String ram;
private final String storage;
// 可选参数
private String graphicsCard = "集成显卡";
private String monitor = "23寸显示器";
// Builder构造方法包含必须参数
public Builder(String cpu, String ram, String storage) {
this.cpu = cpu;
this.ram = ram;
this.storage = storage;
}
// 设置可选参数的方法
public Builder graphicsCard(String graphicsCard) {
this.graphicsCard = graphicsCard;
return this;
}
public Builder monitor(String monitor) {
this.monitor = monitor;
return this;
}
public Computer build() {
return new Computer(this);
}
}
}
// 使用Builder模式:链式调用,非常清晰
Computer gamingPC = new Computer.Builder("i7", "16GB", "1TB SSD")
.graphicsCard("RTX 3080")
.monitor("27寸4K显示器")
.build();
适用场景:
- 对象有很多属性,特别是可选属性很多时
- 希望创建过程更清晰、易读
- 需要创建不可变对象
7. 匿名内部类
当我们只需要某个接口的一次性实现时,匿名内部类很方便。
java
// 创建线程的传统方式
class MyTask implements Runnable {
@Override
public void run() {
System.out.println("执行任务");
}
}
Runnable task1 = new MyTask();
// 使用匿名内部类:更简洁
Runnable task2 = new Runnable() {
@Override
public void run() {
System.out.println("执行任务");
}
};
// Java 8以后,可以用lambda表达式进一步简化
Runnable task3 = () -> System.out.println("执行任务");
8. 方法引用
Java 8引入了函数式编程,让对象创建更加优雅。
java
class Person {
private String name;
public Person() {
this.name = "匿名";
}
public Person(String name) {
this.name = name;
}
}
// 方法引用创建对象
Supplier defaultPerson = Person::new;
Function namedPerson = Person::new;
// 使用
Person p1 = defaultPerson.get(); // 调用无参构造
Person p2 = namedPerson.apply("张三"); // 调用有参构造
总结
| 场景 | 推荐方式 | 理由 |
|---|---|---|
| 日常开发 | new关键字 | 简单直接,性能好 |
| 多参数对象 | Builder模式 | 可读性好,灵活 |
| 需要封装创建逻辑 | 静态工厂方法 | 隐藏实现细节 |
| 框架开发 | 反射机制 | 动态性,灵活性 |
| 函数式编程 | 方法引用 | 代码简洁,表达力强 |
| 创建对象副本 | clone()方法 | 语义明确,使用方便 |
简单场景用new,复杂对象用Builder,需要灵活性用工厂方法或反射。
本文首发于公众号:程序员刘大华,专注分享前后端开发的实战笔记。关注我,少走弯路,一起进步!
📌往期精彩
《这 5 个冷门 HTML 标签,让我直接删了100 行 JS 代码》