当前位置: 首页 > news >正文

对象创建位置差异的详细分析

对象创建位置差异的详细分析

代码对比

代码一:循环外创建对象引用

public List<Account> selectAll(){Connection conn=null;PreparedStatement ps=null;ResultSet rs=null;Account act=null;  // 🟡 在循环外声明对象引用List<Account> actList=new ArrayList<>();try {conn = DBUtil.getConnection();String sql="select id,actno,balance from act";ps=conn.prepareStatement(sql);rs = ps.executeQuery();while(rs.next()){Integer id = rs.getInt("id");String actno = rs.getString("actno");Double balance = rs.getDouble("balance");act=new Account();  // 🟡 在循环内创建新对象act.setId(id);act.setActno(actno);act.setBalance(balance);actList.add(act);}} catch (SQLException e) {throw new RuntimeException(e);}return actList;
}

代码二:循环内创建对象

public List<Account> selectAll(){Connection conn=null;PreparedStatement ps=null;ResultSet rs=null;List<Account> actList=new ArrayList<>();try {conn = DBUtil.getConnection();String sql="select id,actno,balance from act";ps=conn.prepareStatement(sql);rs = ps.executeQuery();while(rs.next()){Integer id = rs.getInt("id");String actno = rs.getString("actno");Double balance = rs.getDouble("balance");Account act=new Account();  // 🟢 在循环内声明和创建对象act.setId(id);act.setActno(actno);act.setBalance(balance);actList.add(act);}} catch (SQLException e) {throw new RuntimeException(e);}return actList;
}

核心区别分析

1. 作用域差异

特性代码一代码二
作用域方法级作用域循环块级作用域
生命周期整个方法执行期间单次循环迭代期间
可访问性循环内外都可访问仅在循环内部可访问

2. 内存使用分析

代码一的内存变化

Account act=null;                    // 栈内存:1个引用变量(初始为null)
while(rs.next()){act=new Account();              // 堆内存:创建新对象,引用指向新对象// 设置属性...actList.add(act);               // 集合持有对象引用
}
// 循环结束后,act变量指向最后一个创建的Account对象

代码二的内存变化

while(rs.next()){Account act=new Account();      // 每次循环:栈上新引用 + 堆上新对象// 设置属性...actList.add(act);               // 集合持有对象引用
}
// 循环结束后,act变量不可访问

详细技术差异

1. 变量生命周期

// 代码一:变量在整个方法周期存在
public void example1() {Account act = null;           // 作用域开始for (int i = 0; i < 3; i++) {act = new Account();      // 重复使用同一个引用变量actList.add(act);}System.out.println(act);      // 这里还能访问act,指向最后一个对象
}                                 // 作用域结束// 代码二:变量在每次循环中重新创建
public void example2() {for (int i = 0; i < 3; i++) {Account act = new Account(); // 每次循环创建新的引用变量actList.add(act);}// System.out.println(act);   // 编译错误:act不可访问
}

2. 垃圾回收影响

实际上两种方式在垃圾回收方面没有区别,因为:

  • 所有创建的Account对象都被actList引用
  • 只要actList存在,所有对象都不会被GC回收
  • 方法结束后,局部变量都会被清理

潜在问题分析

代码一的潜在风险

// 错误示例:错误的重用对象引用
public List<Account> selectAll_ERROR(){Account act = new Account();  // 🚫 错误:在循环外创建对象List<Account> actList = new ArrayList<>();while(rs.next()){// 只是设置属性,没有创建新对象!act.setId(rs.getInt("id"));act.setActno(rs.getString("actno"));act.setBalance(rs.getDouble("balance"));actList.add(act);         // 🚫 严重错误:添加的是同一个对象引用!}return actList;               // 列表中所有元素都是同一个对象!
}

结果:列表中所有元素指向同一个Account对象,所有账户信息相同(都是最后一条记录的值)。

正确的对象创建模式

// 正确:每次循环都创建新对象
while(rs.next()){Account act = new Account();  // ✅ 确保每次都是新对象act.setId(rs.getInt("id"));act.setActno(rs.getString("actno"));act.setBalance(rs.getDouble("balance"));actList.add(act);
}

性能对比

内存使用

  • 栈内存:代码一比代码二多一个长期存在的引用变量(可忽略不计)
  • 堆内存:两者完全相同,都创建了N个Account对象
  • GC压力:无差异

执行效率

  • 对象创建:两者都是每次循环创建新对象,开销相同
  • 变量分配:代码一的引用变量只分配一次,代码二每次循环都分配(但JVM会优化)

最佳实践建议

推荐使用代码二的原因:

  1. 作用域最小化原则

    // 好的实践:变量在最小作用域内声明
    while(rs.next()){Account act = new Account();  // 作用域仅限于当前循环// 使用act...
    }
    
  2. 避免意外的引用重用

    // 代码一可能被误修改为危险模式
    Account act = new Account();  // 有人可能把创建移到循环外
    while(rs.next()){// 只是设置属性,忘记创建新对象act.setId(rs.getInt("id"));actList.add(act);  // 错误!
    }
    
  3. 代码清晰性

    // 代码二更清晰地表达"每次循环创建新对象"
    while(rs.next()){Account act = new Account();  // 明确显示对象创建// ...
    }
    
  4. 便于重构和优化

    // 代码二更容易转换为Stream API
    return DBUtil.executeQuery(sql, rs -> {List<Account> list = new ArrayList<>();while(rs.next()){Account act = new Account();// 设置属性list.add(act);}return list;
    });
    

实际测试验证

测试代码

public class ObjectCreationTest {@Testpublic void testBothMethodsProduceSameResult() {// 模拟数据库结果集List<Account> result1 = method1(); // 代码一方式List<Account> result2 = method2(); // 代码二方式// 验证结果完全相同assertEquals(result1.size(), result2.size());for (int i = 0; i < result1.size(); i++) {assertEquals(result1.get(i).getId(), result2.get(i).getId());assertEquals(result1.get(i).getActno(), result2.get(i).getActno());assertEquals(result1.get(i).getBalance(), result2.get(i).getBalance());}}// 验证不是同一个对象引用@Testpublic void testDifferentObjectReferences() {List<Account> result = method2();for (int i = 0; i < result.size() - 1; i++) {assertNotSame(result.get(i), result.get(i + 1)); // 确保是不同的对象}}
}

总结

特性代码一(循环外声明)代码二(循环内声明)
功能结果✅ 完全相同✅ 完全相同
内存使用⚠️ 稍多栈空间🟢 栈空间更优
代码安全⚠️ 有误用风险🟢 更安全
可读性⚠️ 一般🟢 更清晰
维护性⚠️ 一般🟢 更易维护
性能🟢 无差异🟢 无差异

最终建议优先使用代码二的写法(在循环内创建对象),因为它更符合编码最佳实践,作用域更清晰,且能避免潜在的错误。虽然两种方式的功能结果完全相同,但代码二在可维护性和安全性方面更优。

http://www.dtcms.com/a/436266.html

相关文章:

  • 建设网站多少钱 2017深圳企业官方网站建设
  • 有哪些做婚礼平面设计的网站网站建设备案需要什么
  • 资阳视频网站建设厦门仿站定制模板建站
  • 做网站外包价格报ui设计班
  • 合肥网站制作方案园林景观设计公司年度运营方案
  • 深圳网站优化方案北京做网站的外包公司
  • 住房和城乡建设部网站干部学院郑州官方最新通告
  • 培训网站推荐网站开发者工具
  • 淮南网站建设好的公司vue 做自适应网站
  • 做淘宝客网站 首选霍常亮快速建站php
  • java面试:可以讲一讲jvm的内存结构吗?
  • 福州网站建设方案开发东莞最新通报最新
  • 自助建站 知乎上海中心设计公司是谁
  • cms二次开发网站建设政务网站无障碍建设
  • InfiniBand技术解析(4):智慧的调度者 —— 子网管理器与属性
  • 贵阳网站建设1685如何将网站上传到空间
  • 领优惠券的网站怎么做做蛋糕有哪些网站
  • 字母异位词分组 Java
  • 合肥网站快速排名优化做整站优化
  • 东莞网站建设方案托管鲜花网站数据库建设分析
  • 最流行网站开发工具贞丰县住房和城乡建设局网站
  • 网站做的和别人一样违法吗保定网站开发公司
  • 网站建设赠送seowordpress延时加载插件
  • 建设留学网站桂园精品网站建设费用
  • 注册营业执照网站wordpress文字可以动的插件
  • 清河网站建设费用深圳网络做网站
  • 厦门软件网站建设温州做阀门网站公司
  • thinkphp5做网站企业运营管理名词解释
  • 神农架网站建设搜狗做网站怎么样
  • 上上上海网站设计广州海珠区培训机构网站建设