关于Java语言for循环内外变量定义的问题

今天被TeamLeader批了一顿,本来想和他争论争论的,后来感觉自己也不清楚,遂只有来Iteye讨教讨教了。
这是个关于在for循环内外变量定义的问题,先看代码:
[code="java"]
public class Test2 {

public static void main(String[] args) {

    // 放入10000000的元素
    List<Object> list = new ArrayList<Object>(10000000);
    for(int i = 0; i < 10000000; i++) {
        list.add(i, new Object());
    }

    // 做10次循环,求均值
    for(int j = 0; j < 10; j++) {

        // 变量在循环内定义
        long t1 = System.currentTimeMillis();
        for(Iterator<Object> iterator = list.iterator(); iterator.hasNext();) {
            Object object = iterator.next();
        }
        long t2 = System.currentTimeMillis();
        System.out.println("循环内" + (j + 1) + "次, 时间:" + (t2 - t1) + ";");

        // 变量在循环外定义
        t1 = System.currentTimeMillis();
        Object object = null;
        for(Iterator<Object> iterator = list.iterator(); iterator.hasNext();) {
            object = iterator.next();
        }
        t2 = System.currentTimeMillis();
        System.out.println("循环外" + (j + 1) + "次, 时间:" + (t2 - t1) + ";");
    }
}

}

/*其中一次的运行结果

  • 变量在循环内定义 变量在循环外定义 *1次循环 359 359 *2次循环 360 343 *3次循环 344 360 *4次循环 359 359 *5次循环 344 359 *6次循环 360 359 *7次循环 360 359 *8次循环 344 359 *9次循环 344 359 *10次循环 344 359 */ [/code]

我在工作中有一段代码是放到循环内定义的,主要是习惯,以及变量最小作用域的理念。但teamleader告诉我要放到循环外面,放在里面性能不好,不太理解。这个问题发生后,我也在网络寻找答案,但结果差不多一半一半,有建议在内部定义,也有建议在外部定义,所以就更糊涂了...我现在所知道的有:在内部定义,满足变量最小作用域的理念,循环外没有使用这个变量,该变量就不会在循环外定义;但teamleader说在内部定义会多次申请栈内存,影响性能,但我写了上面的例子和查了下资料,也没有弄太清这里面是如何影响性能的,请帮助详细讲解下两种定义方式的好坏,及其原因,和jdk版本是否有关系。

从性能角度而言
[quote]teamleader说在内部定义会多次申请栈内存,影响性能[/quote],放在循环内部定义,确实会多次申请栈帧的内存空间(java中一个线程对应一个Java栈,每次方法调用会向栈中压入一个新帧,帧中存储着参数、局部变量、中间运算结果等等数据,方法调用结束后当前的帧就被弹出了,可参考我的一篇文章[url]http://boy00fly.iteye.com/blog/1096637[/url]),为什么会多占用栈空间内,原因很简单,每次循环都要重复定义局部变量,而如果放在循环体外,每次只要移动引用指向的堆内存地址即可,不必重新申请内存空间了。
其实话说回来,这样对性能的影响其实可以忽略不计,没什么大的问题,非要纠结这个性能问题的话,就如我上面说的那样。
[code="java"]
public V get(Object key)

{

if (key == null)

return getForNullKey();

int hash = hash(key.hashCode());

for (Entry e = table[indexFor(hash, table.length)]; e != null; e = e.next)
{

Object k;

if (e.hash == hash && ((k = e.key) == key || key.equals(k)))

return e.value;

}

return null;

}

[/code]
上面这段代码是jdk中HashMap的源码,你看源码的Object k定义就在循环体内部的! :wink:

循环外,就只是开辟一块内存空间。而循环里面你每次调都要开辟一次内存空间来存放变量。当然内存使用就高了。循环外定义的好处是在循环结束后变量值如果变化了可以取出来。而在循环里面的变量在外面得不到值,也就是作用域。

循环内的话,每次循环内部的局部变量在每次进for循环的时候都要重新定义一遍变量,也就是执行申请内存空间,变量压入堆栈的过程。
循环外定义的话,for循环一直用的是同一块内存空间,效率比较高,但是变量的作用域就大了,耗内存

其实内外都可以的,总之就是空间和时间的权衡,看实际情况了,局部变量的数据类型、大小什么的都有关

个人觉得,对于java来说,本来就不是什么高效的语言 我习惯于定义在里面

这个还是要看字节码滴:

变量定义在循环内
[code="java"]
aload 5 [iterator]
invokeinterface java.util.Iterator.next() : java.lang.Object [38] [nargs: 1]
astore 6
aload 5 [iterator]
invokeinterface java.util.Iterator.hasNext() : boolean [44] [nargs: 1]
ifne 58
[/code]

变量定义在循环外
[code="java"]
aconst_null
astore 7 [object]

invokeinterface java.util.List.iterator() : java.util.Iterator [34] [nargs: 1]
astore 8 [iterator]
goto 150
aload 8 [iterator]
invokeinterface java.util.Iterator.next() : java.lang.Object [38] [nargs: 1]
astore 7 [object]
aload 8 [iterator]
invokeinterface java.util.Iterator.hasNext() : boolean [44] [nargs: 1]
ifne 141
[/code]

现在是不是很清楚了?除了那个Object object = null; 的定义多了2条指令,其他没有区别。

在csdn看到的:
[quote]1.尽量使用final修饰符。

带有final修饰符的类是不可派生的。在Java核心API中,有许多应用final的例子,例如java.lang.String。为String类指定final防止了使用者覆盖length()方法。另外,如果一个类是final的,则该类所有方法都是final的。java编译器会寻找机会内联(inline)所有的final方法(这和具体的编译器实现有关)。此举能够使性能平均提高50%。

2.尽量重用对象。

特别是String对象的使用中,出现字符串连接情况时应使用StringBuffer代替,由于系统不仅要花时间生成对象,以后可能还需要花时间对这些对象进行垃圾回收和处理。因此生成过多的对象将会给程序的性能带来很大的影响。

3.尽量使用局部变量。

调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快。其他变量,如静态变量,实例变量等,都在堆(Heap)中创建,速度较慢。
[/quote]
其实你这个例子,对性能没什么关键影响,根本不用纠结

放在里面,虽然会在栈内每次都分配一块内存,但是由于栈内存的分配非常之快,仅次于寄存器,所以,可以忽略不计。