天津品茶,优化Java代码:避免这15个常见陷阱,效率立竿见影
作为从业十五年的Java老手,我见过太多本可避免的性能悲剧。很多“能跑就行”的代码,其实隐藏着惊人的效率黑洞。今天直接上干货,避开这15个常见陷阱,你的代码效率至少提升30%。
陷阱1:字符串拼接用“+”号
问题:循环内用“+”拼接字符串,每次都会创建新对象
// 错误写法
String result = "";
for (int i = 0; i < 1000; i++) {
result += i; // 创建999个StringBuilder和String对象!
}
// 正确写法
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i);
}
String result = sb.toString();
陷阱2:无脑用BigDecimal
问题:BigDecimal对象极重,简单计算杀鸡用牛刀
// 错误写法
BigDecimal total = BigDecimal.ZERO;
for (Order order : orders) {
total = total.add(new BigDecimal(order.getAmount()));
}
// 正确写法(精度要求不高时)
double total = 0.0d;
for (Order order : orders) {
total += order.getAmount();
}
陷阱3:未指定集合初始容量
问题:默认扩容导致多次数组拷贝
// 错误写法
List<User> users = new ArrayList<>(); // 默认容量10
for (int i = 0; i < 10000; i++) {
users.add(new User()); // 会触发多次扩容!
}
// 正确写法
List<User> users = new ArrayList<>(10000); // 一次分配到位
陷阱4:循环内创建对象
问题:无谓的对象创建增加GC压力
// 错误写法
for (String data : dataList) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); // 每次循环都创建!
Date date = sdf.parse(data);
}
// 正确写法
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
for (String data : dataList) {
Date date = sdf.parse(data);
}
陷阱5:误用异常控制流程
问题:异常处理比正常流程慢百倍
// 错误写法
try {
while (true) {
list.get(index);
index++;
}
} catch (IndexOutOfBoundsException e) {
// 用异常结束循环
}
// 正确写法
while (index < list.size()) {
list.get(index);
index++;
}
陷阱6:自动装箱拆箱泛滥
问题:隐式转换产生大量临时对象
// 错误写法
Integer sum = 0;
for (int i = 0; i < 10000; i++) {
sum += i; // 每次都要拆箱再装箱!
}
// 正确写法
int sum = 0; // 使用基本类型
for (int i = 0; i < 10000; i++) {
sum += i;
}
陷阱7:频繁调用size()方法
问题:循环条件中重复调用方法
// 错误写法
for (int i = 0; i < list.size(); i++) { // 每次循环都调用size()
// ...
}
// 正确写法
int size = list.size();
for (int i = 0; i < size; i++) {
// ...
}
陷阱8:无索引的集合查找
问题:List.contains()效率O(n)
// 错误写法
List<String> list = new ArrayList<>(10000);
if (list.contains(target)) { // 遍历整个列表!
// ...
}
// 正确写法
Set<String> set = new HashSet<>(10000); // O(1)查找
if (set.contains(target)) {
// ...
}
陷阱9:静态代码块过重
问题:类加载时执行耗时操作
// 错误写法
public class ConfigLoader {
static {
loadFromRemote(); // 类加载时阻塞!
}
}
// 正确写法
public class ConfigLoader {
private static volatile Config config;
public static Config getConfig() {
if (config == null) {
synchronized (ConfigLoader.class) {
if (config == null) {
config = loadFromRemote();
}
}
}
return config;
}
}
陷阱10:滥用synchronized
问题:锁粒度太粗导致性能瓶颈
// 错误写法
public synchronized void process() { // 锁住整个方法
readFile();
calculate();
writeDB();
}
// 正确写法
public void process() {
readFile(); // 无锁操作
synchronized (this) {
calculate(); // 只锁必要部分
}
writeDB(); // 无锁操作
}
陷阱11:过度使用finalize()
问题:finalize()执行时机不确定,影响GC
// 错误写法
public class Resource {
@Override
protected void finalize() throws Throwable {
release(); // 不确定何时执行
super.finalize();
}
}
// 正确写法
public class Resource implements AutoCloseable {
@Override
public void close() {
release(); // 确定时机执行
}
}
陷阱12:无脑用深拷贝
问题:对象图太深时拷贝成本极高
// 错误写法
public User copyUser(User user) {
return new User( // 每层都创建新对象
user.getName(),
new Address(user.getAddress()), // 地址也拷贝
new List<>(user.getOrders()) // 订单列表也拷贝
);
}
// 正确写法(视情况选择)
// 1. 使用浅拷贝
// 2. 使用不可变对象
// 3. 真正需要时才深拷贝
陷阱13:日志级别判断缺失
问题:高开销日志语句始终执行
// 错误写法
log.debug("查询结果:" + expensiveToString()); // 即使debug关闭也要执行!
// 正确写法
if (log.isDebugEnabled()) {
log.debug("查询结果:" + expensiveToString());
}
陷阱14:多次重复数据库查询
问题:循环内执行相同查询
// 错误写法
for (Long userId : userIds) {
User user = userDao.findById(userId); // 每次循环都查数据库!
// ...
}
// 正确写法
List<User> users = userDao.findByIdIn(userIds); // 一次批量查询
Map<Long, User> userMap = users.stream()
.collect(Collectors.toMap(User::getId, Function.identity()));
陷阱15:忽略连接池配置
问题:默认连接池参数不适合高并发
// 错误写法(使用默认配置)
DataSource dataSource = new HikariDataSource();
// 并发高时频繁创建连接
// 正确写法
HikariConfig config = new HikariConfig();
config.setMaximumPoolSize(20); // 根据业务调整
config.setMinimumIdle(10);
config.setConnectionTimeout(30000);
config.setIdleTimeout(600000);
config.setMaxLifetime(1800000);
最后的话
优化不是炫技,而是对生产环境的敬畏。这些陷阱看似简单,但在百万QPS系统中,任何一个都可能引发雪崩。记住:最好的性能优化,是选择不做不需要的事。
在评论区分享你遇到过的性能陷阱,点赞最高的三位,我会送出一对一的代码优化建议。
思考题:你的项目中,哪个性能问题修复后效果最明显?