网站首页 > 技术文章 正文
"如果你一直拖着过去不放,就无法向前进步。"这正是我在代码审查时对团队说的话。然而,每次我打开一个拉取请求时,我仍然看到过时的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);
这些更清洁、现代、更可读。
来源:
https://java.didispace.com/article/20250701-10-java-features-you-should-stop-using-today.html
猜你喜欢
- 2025-07-14 Java 开发人员所需的技能(java开发工程师需要具备的能力)
- 2025-07-14 1次订单系统迁移,头发都快掉完了...
- 2025-07-14 Java程序员必需掌握的 4 大基础(java程序员的日常工作)
- 2025-07-14 带你学java核心技术图形程序设计:颜色+为文本设定特殊字体+图像
- 2025-07-14 面试复盘:Java为什么有这么多“O”?——从请求链路看清楚
- 2025-07-14 学Spring源码这么久了,必须要掌握的bean实例的创建过程,安排
- 2025-07-14 做Python开发时遇到需求实现,必须调用Java方法,可以这么做
- 2024-08-04 Java锁与线程的那些“不可描述”的事儿
- 2024-08-04 真的要开始用 JDK 17 了(jdk要下载最新的吗)
- 2024-08-04 干货|Java后端需要学习哪些技术?(java后端主要学什么)
- 1512℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 556℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 504℃MySQL service启动脚本浅析(r12笔记第59天)
- 482℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 480℃启用MySQL查询缓存(mysql8.0查询缓存)
- 460℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 440℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 438℃MySQL server PID file could not be found!失败
- 最近发表
- 标签列表
-
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- c语言min函数头文件 (68)
- asynccallback (71)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)