List<Object> myList = new ArrayList<Object>();
for(int k = 0;k<10;k++){
//注意即使是将str提到循环外声明拼接字符串还是会创建多个StringBuilder对象
String str = k+"";
A a = new A();
a.set(str);
myList.add(a);
}
分析:在上面的代码中,有两处需要进行优化,一处是在循环体内使用了String进行字符串的拼接方式,二是在循环体内声明变量和对象。为什么要在循环体外声明变量或对象呢?我们从内存分配的角度来理解这个问题:首先String str = k+""和A a = new A()都可以用两个过程来理解,String str和A a表示在栈区开辟了一个内存空间,用于接收对象的引用,创建完对象后就会将该对象的地址赋给这个变量。如果在for循环内部声明变量或对象,那么在for循环的循环期间就会创建多个栈内存空间(方法调用完之前),这样效率就显得比较低下,这个过程可以通过下图表示:
这意味着,在for循环期间(注意这里强调的是for循环的循环期间)就会创建很多个栈内存空间,这个开辟栈空间期间不仅浪费了内存,而且开辟大量栈空间时也会消耗额外的时间,因此这种做法是不太合理的;但是,如果我们把声明对象的语句放到for循环外部,那就变成下面的过程:
从上图可以看出,当执行A a时,会先在栈区开辟一个内存空间,当创建对象1时,对象1返回00x地址(即引用),这个地址存储在刚刚开辟的这个栈内存中,当创建对象2时,会返回一个00y的地址保存在刚刚开辟的这个栈内存中,也就意味着刚刚这个栈内存保存的地址由原来的00x变成了00y,因此这个栈内存的地址指向了对象2,对象1因此没有任何引用指向这段堆空间了,因此成了垃圾数据,等待垃圾回收器把对象1回收掉(在此之前已经对对象进行了操作);当创建对象3时,重复上面的过程,当for循环结束时,栈内存地址最终指向了对象3所在的内存空间。整个过程下来,只开辟了一个栈内存空间,因此节约了内存空间,是比较合理的做法。
因此上面的代码可以参考修下面的修改:
A a = null;
StringBuffer str = new StringBuffer();
for(int k = 0;k<10;k++){
str.append(k);
a = new A();
a.set(str.toString());
myList.add(a);
}
下面是Java 局部变量,类变量,实例变量的作用域和生命周期,如下所示:
(1)局部变量
局部变量是指定义在方法或代码块中的变量,局部变量必须初始化,在方法或代码块内有效,之外则无效,方法执行开始入栈时创建,执行完毕出栈时销毁。
(2)实例变量
其作用域受限定符限定,Private的只能在本类中使用,protected子类可用,public任何地方都可用。生命周期从对象创建开始到对象销毁结束。
(3)类变量
类变量是指类的static变量,用类名可以直接访问。