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);
这些更清洁、现代、更可读。