10个Java开发者必须改掉的过时编程习惯
10个Java开发者必须改掉的过时编程习惯
"如果你一直拖着过去不放,就无法向前进步。"这正是我在代码审查时对团队说的话。然而,每次我打开一个拉取请求时,我仍然看到过时的Java特性、坏习惯和应该在几年前就被淘汰的编码习惯。所以,如果你今天在使用Java,无论你是初级、中级还是高级开发者,这篇文章都适合你 🫵 。其中一些观点可能会让你感到不舒服。有些可能与你所学的相违背。但这正是你应该读到最后的原因。
1. 错误使用 Optional.get()
Optional是一个很棒的特性,但我看到许多开发者误用了它:
Optional<String> value = getValue();
String result = value.get(); // 可能抛出 NoSuchElementException!
这是错误的!
如果你在使用**Optional**,就要拥抱它的API:
String result = value.orElse("default");
// 或者
value.ifPresent(val -> System.out.println(val));
// 或者
String result = value.orElseThrow(() -> new IllegalArgumentException("Value missing"));
2. 硬编码值而不是使用常量
这是我在代码中看到的最大罪过之一:
if (status == 3) {
// do something
}
一个新的初级开发者加入团队,他不知道 3 是什么意思。然后他错误地修改了逻辑。砰。生产环境出现了bug。
正确的方式:
// 使用 static final
public static final int STATUS_COMPLETED = 3;
if (status == STATUS_COMPLETED) {
// do something
}
// 更好的方式 - 使用枚举:
if (status == Status.COMPLETED) {
// do something
}
为什么? 因为这样你的代码变得自文档化且安全。
3. 有缺陷的单例模式的双重检查锁定
经典错误:
public class MyClass {
private static MyClass instance;
private MyClass() {
// 私有构造函数来强制单例模式
}
public static MyClass getInstance() {
if (instance == null) {
synchronized(MyClass.class) {
if (instance == null) {
instance = new MyClass();
}
}
}
return instance;
}
}
除非使用volatile
完美地完成,否则这在Java中是有缺陷的。在并发环境中可能出现微妙的错误。
这里是修复方法:
public class MyClass {
private static volatile MyClass instance; // <<< 修复:添加 'volatile'
private MyClass() {
// 私有构造函数来强制单例模式
}
public static MyClass getInstance() {
if (instance == null) {
synchronized(MyClass.class) {
if (instance == null) {
instance = new MyClass();
}
}
}
return instance;
}
}
更好的方式:
// 使用枚举单例:
public enum Singleton {
INSTANCE;
public void doSomething() {
// 实现
}
}
// 或者使用 static final:
public class MySingleton {
public static final MySingleton INSTANCE = new MySingleton();
}
这些方式安全、简单,并且避免了并发错误。
4. Vector 和 Hashtable
现在是2025年。然而,有时我仍然看到这样的代码:
Vector<String> vector = new Vector<>();
Hashtable<String, String> table = new Hashtable<>();
Vector
和Hashtable
都是Java早期的遗留类。它们是同步的,因此比现代替代方案慢得多。
应该使用什么替代:
// 对于List:
List<String> list = new ArrayList<>();
// 对于Map:
Map<String, String> map = new HashMap<>();
如果你需要线程安全,使用:
List<String> list = Collections.synchronizedList(new ArrayList<>());
Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
// 或者更好的:
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
为什么要停止使用Vector
和Hashtable
?
- 在现代应用中性能差
- 存在更好的替代方案,对同步有更精细的控制
5. 原始类型
我仍然审查到这样的PR:
List list = new ArrayList();
list.add("Hello");
list.add(123); // 什么?在字符串列表中放整数?
这是危险的💀。
原始类型移除了类型安全性,导致在运行时爆发的微妙错误。
正确的方式:
List<String> list = new ArrayList<>();
list.add("Hello");
// list.add(123); // 编译错误 - 很好!
提示: 总是使用泛型。你的IDE会帮你早期发现错误。
6. StringBuffer(当你不需要同步时)
许多开发者使用这个:
StringBuffer sb = new StringBuffer();
sb.append("Hello ");
sb.append("World");
但除非你在做多线程工作,否则StringBuffer
是不必要的慢。
应该使用什么替代:
StringBuilder sb = new StringBuilder();
sb.append("Hello ");
sb.append("World");
StringBuilder
更快,对于单线程操作来说已经足够了,这是你大部分代码的情况。
7. 直接使用 SimpleDateFormat
这是连高级开发者都会犯的经典错误:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String date = sdf.format(new Date());
问题在哪里?🤔 SimpleDateFormat
是不线程安全的。如果这段代码在Web应用或多线程环境中运行,它可能抛出奇怪的日期格式化错误。
应该使用什么替代:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String date = LocalDate.now().format(formatter);
新的java.time
包(从Java 8开始)是线程安全的,并且远远优越。
8. 使用 System.out.println 进行日志记录
我实际上见过企业应用程序中有数百个**System.out.println()
**语句。
当你调试生产问题时,你会后悔的。
正确的方式:
使用日志框架,如SLF4J with Logback或Log4j2:
private static final Logger logger = LoggerFactory.getLogger(MyClass.class);
logger.info("Processing order: {}", orderId);
logger.error("Error processing payment", ex);
- 你可以获得日志级别(info、debug、error)
- 你可以将日志重定向到文件
- 你可以控制日志格式和保留期
🙅♂️永远不要在生产代码中部署System.out.println
9. 过度使用同步而不是使用现代并发
许多开发者仍然这样做:
synchronized(this) {
// do something
}
但Java已经发展了🪴。java.util.concurrent
提供了强大、更安全、性能更好的工具:
- 使用ReentrantLock进行显式锁定
- 使用ConcurrentHashMap而不是同步映射
- 使用AtomicInteger、AtomicBoolean等
- 使用ExecutorService而不是手动管理线程
**除非绝对必要,否则避免使用synchronized。**并发很难——让现代库来帮助你。
10. 过时的集合API方法
我有时仍然看到这样的代码:
Enumeration<String> e = myVector.elements();
while (e.hasMoreElements()) {
System.out.println(e.nextElement());
}
❌ 当你可以使用现代for-each循环时,停止使用Enumeration
和Iterator
:
for (String item : myList) {
System.out.println(item);
}
// 或者更好的 - 使用流:
myList.forEach(System.out::println);
这些更清洁、现代、更可读。