站内搜索: 请输入搜索关键词

当前页面: 开发资料首页J2SE 专题这两种做法是否有效率差别?为什么?简单程序。

这两种做法是否有效率差别?为什么?简单程序。

摘要: 这两种做法是否有效率差别?为什么?简单程序。


1:

public void test1(){
String str;
for(int i=0;i<100;i++){
str="i";
System.out.println(str);
}

}

public void test2(){

for(int i=0;i<100;i++){
String str="i";
System.out.println(str);
}

}

那种好,一个在循环外声明变量,一个在循环内。都是String对象。

如果不是String对象而是其他对象呢?谢谢。


第一种好,你每次循环都去创建个String类型的变量很影响进程,效率也相对的低很多.

这是我的认为!


提倡第二种,其他类型的都一样


更正:提倡第一种,其他类型的都一样


个人观点,除非对象在循环外会用到还是作用域越小越好


经过验证,第二种效率高.谢谢大家.!


public class TestFor {
public static void main(String[] args){

TestFor t=new TestFor();
long time1=System.currentTimeMillis();
t.test1();
long time2=System.currentTimeMillis();


long time3=System.currentTimeMillis();
t.test2();
long time4=System.currentTimeMillis();
System.out.println(time2-time1);
System.out.println("*********************");
System.out.println(time4-time3);

}

public void test1(){
String str;

for(int i=0;i<100000;i++){
str="aaaa";

System.out.println(str);
}

}

public void test2(){

for(int i=0;i<100000;i++){
String str="xxxx";
System.out.println(str);

}

}

}

结果
6219
*********************
1453



应该是第二种比较好,作用域小
效率也更高


错了,效率是一样,没有区别


可是楼上老大,我用了程序打出执行时间,为什么第二种比第一种要少很多呢?
效率似乎是有区别的吧?


lz先不要慌下结论,这里有一组数据。假定你改变的循环语句中的n数值,情况会慢慢改变:

这里有一组数据:
n = 1000
test 1 cost:0.04110746 seconds
test 2 cost:0.012370567 seconds

n = 10000
test 1 cost:0.12821881 seconds
test 2 cost:0.10286 seconds

n = 100000
test 1 cost:3.960505036 seconds
test 2 cost:5.615919062 seconds

随着n的增大,结果慢慢倾斜了。同时等待更多人参与讨论。。。。



肯定有区别

相当于new String() ; new对象的开销很大


有意思...


关于效率我就不是很清楚,
这知道这是变量的作用域和生存期:
变量在其作用域内被创建,离开其作用域时被撤消。
这意味着一个变量一旦离开它的作用域,将不再保存它的值了。因此,在一个方法内定义的变量在几次调用该方法之间将不再保存它们的值。同样,在块内定义的变量在离开该块时也将丢弃它的值。因此,一个变量的生存期就被限定在它的作用域中。

如果一个声明定义包括一个初始化,那么每次进入声明它的程序块时,该变量都要被重新初始化。例如
class LifeTime {
public static void main(String args[]) {
int x;


for(x = 0; x < 3; x++) {

int y = -1;
System.out.println("y is: " + y);
}
}
}


java 变量

日期:2005年7月22日 作者: 人气:247
 



变量是Java 程序的一个基本存储单元。变量由一个标识符,类型及一个可选初始值的组合定义。此外,所有的变量都有一个作用域,定义变量的可见性,生存期。接下来讨论变量的这些元素。
3.8.1 声明一个变量
在Java 中,所有的变量必须先声明再使用。基本的变量声明方法如下:type identifier [ = value][,identifier [= value] ...] ;

type 是Java 的基本类型之一,或类及接口类型的名字(类和接口类型在本书第1部分的后部讨论)。标识符(identifier )是变量的名字,指定一个等号和一个值来初始化变量。请记住初始化表达式必须产生与指定的变量类型一样(或兼容)的变量。声明指定类型的多个变量时,使用逗号将各变量分开。

以下是几个各种变量声明的例子。注意有一些包括了初始化。

int a, b, c; // declares three ints, a, b, and c.int d = 3, e, f = 5; // declares three more ints, initializing // d and f.byte z = 22; // initializes z.
double pi = 3.14159; // declares an approximation of pi.char x = 'x'; // the variable x has the value 'x'.

你选择的标识符名称没有任何表明它们类型的东西。许多读者记得FORTRAN 预先规定从I到N的所有标识符都为整型,其他的标识符为实型。Java 允许任何合法的标识符具有任何它们声明的类型。

3.8.2 动态初始化
尽管前面的例子仅将字面量作为其初始值,Java 也允许在变量声明时使用任何有效的表达式来动态地初始化变量。

例如,下面的短程序在给定直角三角形两个直角边长度的情况下,求其斜边长度。

// Demonstrate dynamic initialization.
class DynInit {
public static void main(String args[]) {
double a = 3.0, b = 4.0;


// c is dynamically initialized

double c = Math.sqrt(a * a + b * b);

System.out.println("Hypotenuse is " + c);
}
}


这里,定义了3个局部变量a,b,c。前两个变量a和b初始化为常量。然而直角三角形的斜边c被动态地初始化(使用勾股定理)。该程序用了Java 另外一个内置的方法sqrt(),它是Math类的一个成员,计算它的参数的平方根。这里关键的一点是初始化表达式可以使用任何有效的元素,包括方法调用、其他变量或字面量。

3.8.3 变量的作用域和生存期
到目前为止,我们使用的所有变量都是在方法main() 的后面被声明。然而,Java 允许变量在任何程序块内被声明。在第2章中已解释过了,程序块被包括在一对大括号中。一个程序块定义了一个作用域(scope )。这样,你每次开始一个新块,你就创建了一个新的作用域。你可能从先前的编程经验知道,一个作用域决定了哪些对象对程序的其他部分是可见的,它也决定了这些对象的生存期。

大多数其他计算机语言定义了两大类作用域:全局和局部。然而,这些传统型的作用域不适合Java 的严格的面向对象的模型。当然将一个变量定义为全局变量是可行的,但这是例外而不是规则。在Java 中2个主要的作用域是通过类和方法定义的。尽管类的作用域和方法的作用域的区别有点人为划定。因为类的作用域有若干独特的特点和属性,而且这些特点和属性不能应用到方法定义的作用域,这些差别还是很有意义的。因为有差别,类(以及在其内定义的变量)的作用域将被推迟到第6章当讨论类时再来讨论。到现在为止,我们将仅仅考虑由方法或在一个方法内定义的作用域。

方法定义的作用域以它的左大括号开始。但是,如果该方法有参数,那么它们也被包括在该方法的作用域中。本书在第5章将进一步讨论参数,因此,现在可认为它们与方法中其他变量的作用域一样。

作为一个通用规则,在一个作用域中定义的变量对于该作用域外的程序是不可见(即访问)的。因此,当你在一个作用域中定义一个变量时,你就将该变量局部化并且保护它不被非授权访问和/或修改。实际上,作用域规则为封装提供了基础。

作用域可以进行嵌套。例如每次当你创建一个程序块,你就创建了一个新的嵌套的作用域。这样,外面的作用域包含内部的作用域。这意味着外部作用域定义的对象对于内部作用域中的程序是可见的。但是,反过来就是错误的。内部作用域定义的对象对于外部是不可见的。

为理解嵌套作用域的效果,考虑下面的程序:

// Demonstrate block scope.
class Scope {
public static void main(String args[]) {
int x; // known to all code within main


x = 10;
if(x == 10) { // start new scope
int y = 20; // known only to this block


// x and y both known here.
System.out.println("x and y: " + x + " " + y);
x = y * 2;


}
// y = 100; // Error! y not known here


// x is still known here.
System.out.println("x is " + x);
}
}


正如注释中说明的那样,在方法main() 的开始定义了变量x,因此它对于main() 中的所有的随后的代码都是可见的。在if程序块中定义了变量y。因为一个块定义一个作用域,y 仅仅对在它的块以内的其他代码可见。这就是在它的块之外的程序行y=100; 被注释掉的原因。如果你将该行前面的注释符号去掉,编译程序时就会出现错误,因为变量y在它的程序块之外是不可见的。在if程序块中可以使用变量x,因为块(即一个嵌套作用域)中的程序可以访问被其包围作用域中定义的变量。

变量可以在程序块内的任何地方被声明,但是只有在他们被声明以后才是合法有效的。因此,如果你在一个方法的开始定义了一个变量,那么它对于在该方法以内的所有程序都是可用的。反之,如果你在一个程序块的末尾声明了一个变量,它就没有任何用处,因为没有程序会访问它。例如,下面这个程序段就是无效的,因为变量count 在它被定义以前是不能被使用的。

// This fragment is wrong!
count = 100; // oops! cannot use count before it is declared!
int count;


另一个需要记住的重要之处是:变量在其作用域内被创建,离开其作用域时被撤消。
这意味着一个变量一旦离开它的作用域,将不再保存它的值了。因此,在一个方法内定义的变量在几次调用该方法之间将不再保存它们的值。同样,在块内定义的变量在离开该块时也将丢弃它的值。因此,一个变量的生存期就被限定在它的作用域中。

如果一个声明定义包括一个初始化,那么每次进入声明它的程序块时,该变量都要被重新初始化。例如,考虑这个程序:

// Demonstrate lifetime of a variable.
class LifeTime {
public static void main(String args[]) {
int x;


for(x = 0; x < 3; x++) {

int y = -1; // y is initialized each time block is enteredSystem.out.println("y is: " + y); // this always prints -1y = 100;System.out.println("y is now: " + y);

}
}
}


该程序运行的输出如下:

y is: -1
y is now: 100
y is: -1
y is now: 100
y is: -1
y is now: 100
可以看到,每次进入内部的for循环,y都要被重新初始化为-1。即使它随后被赋值为100,该值还是被丢弃了。


所以一个在循环外声明变量只初始化一次,一个在循环内重新初始化。。。
效率就。。。。。









偶像啊


感觉应该是一样吧 String 类型的相当于一个常量,每次改变一下,相当于new了个新对象


mark xue xi


關於這個問題我也專門找過相關的資料並作過許多例子,我給大家發點有用的資料吧,大家整理一下思路:

对象泄漏
游戏程序员跟其他程序员一样——他们也需要理解 Java 运行时环境的一些微妙之处,比如垃圾收集。垃圾收集可能是使您感到难于理解的较难的概念之一, 因为它并不能总是毫无遗漏地解决 Java 运行时环境中堆管理的问题。似乎有很多类似这样的讨论,它的开头或结尾写着:“我的问题是关于垃圾收集”。

假如您正面遭遇内存耗尽(out-of-memory)的错误。于是您使用检测工具想要找到问题所在,但这是徒劳的。您很容易想到另外一个比较可信的原因:这是 Java 虚拟机堆管理的问题,而不会认为这是您自己的程序的缘故。但是,正如 Java 游戏社区的资深专家不止一次地解释的,Java 虚拟机并不存在任何被证实的对象泄漏问题。实践证明,垃圾收集器一般能够精确地判断哪些对象可被收集,并且重新收回它们的内存空间给 Java 虚拟机。所以,如果您遇到了内存耗尽的错误,那么这完全可能是由您的程序造成的,也就是说您的程序中存在着“无意识的对象保留(unintentional object retention)”。

内存泄漏与无意识的对象保留
内存泄漏和无意识的对象保留的区别是什么呢?对于用 Java 语言编写的程序来说,确实没有区别。两者都是指在您的程序中存在一些对象引用,但实际上您并不需要引用这些对象。一个典型的例子是向一个集合中加入一些对象以便以后使用它们,但是您却忘了在使用完以后从集合中删除这些对象。因为集合可以无限制地扩大,并且从来不会变小,所以当您在集合中加入了太多的对象(或者是有很多的对象被集合中的元素所引用)时,您就会因为堆的空间被填满而导致内存耗尽的错误。垃圾收集器不能收集这些您认为已经用完的对象,因为对于垃圾收集器来说,应用程序仍然可以通过这个集合在任何时候访问这些对象,所以这些对象是不可能被当作垃圾的。

对于没有垃圾收集的语言来说,例如 C++ ,内存泄漏和无意识的对象保留是有区别的。C++ 程序跟 Java 程序一样,可能产生无意识的对象保留。但是 C++ 程序中存在真正的内存泄漏,即应用程序无法访问一些对象以至于被这些对象使用的内存无法释放且返还给系统。令人欣慰的是,在 Java 程序中,这种内存泄漏是不可能出现的。所以,我们更喜欢用“无意识的对象保留”来表示这个令 Java 程序员抓破头皮的内存问题。这样,我们就能区别于其他使用没有垃圾收集语言的程序员。

跟踪被保留的对象
那么当发现了无意识的对象保留该怎么办呢?首先,需要确定哪些对象是被无意保留的,并且需要找到究竟是哪些对象在引用它们。然后必须安排好应该在哪里释放它们。最容易的方法是使用能够对堆产生快照的检测工具来标识这些对象,比较堆的快照中对象的数目,跟踪这些对象,找到引用这些对象的对象,然后强制进行垃圾收集。有了这样一个检测器,接下来的工作相对而言就比较简单了:


等待直到系统达到一个稳定的状态,这个状态下大多数新产生的对象都是暂时的,符合被收集的条件;这种状态一般在程序所有的初始化工作都完成了之后。


强制进行一次垃圾收集,并且对此时的堆做一份对象快照。


进行任何可以产生无意地保留的对象的操作。


再强制进行一次垃圾收集,然后对系统堆中的对象做第二次对象快照。


比较两次快照,看看哪些对象的被引用数量比第一次快照时增加了。因为您在快照之前强制进行了垃圾收集,那么剩下的对象都应该是被应用程序所引用的对象,并且通过比较两次快照我们可以准确地找出那些被程序保留的、新产生的对象。


根据您对应用程序本身的理解,并且根据对两次快照的比较,判断出哪些对象是被无意保留的。


跟踪这些对象的引用链,找出究竟是哪些对象在引用这些无意地保留的对象,直到您找到了那个根对象,它就是产生问题的根源。按照这个步骤,您将知道怎样解决这个问题。

显式地赋空(nulling)变量
一谈到垃圾收集这个主题,总会涉及到这样一个吸引人的讨论,即显式地赋空变量是否有助于程序的性能。赋空变量是指简单地将 null 值显式地赋值给这个变量,相对于让该变量的引用失去其作用域。

清单 1. 局部作用域


public static String scopingExample(String string) {
StringBuffer sb = new StringBuffer();
sb.append("hello ").append(string);
sb.append(", nice to see you!");
return sb.toString();
}


当该方法执行时,运行时栈保留了一个对 StringBuffer 对象的引用,这个对象是在程序的第一行产生的。在这个方法的整个执行期间,栈保存的这个对象引用将会防止该对象被当作垃圾。当这个方法执行完毕,变量 sb 也就失去了它的作用域,相应地运行时栈就会删除对该 StringBuffer 对象的引用。于是不再有对该 StringBuffer 对象的引用,现在它就可以被当作垃圾收集了。栈删除引用的操作就等于在该方法结束时将 null 值赋给变量 sb。

错误的作用域
既然 Java 虚拟机可以执行等价于赋空的操作,那么显式地赋空变量还有什么用呢?对于在正确的作用域中的变量来说,显式地赋空变量的确没用。但是让我们来看看另外一个版本的 scopingExample 方法,这一次我们将把变量 sb 放在一个错误的作用域中。

清单 2. 静态作用域


static StringBuffer sb = new StringBuffer();
public static String scopingExample(String string) {
sb = new StringBuffer();
sb.append("hello ").append(string);
sb.append(", nice to see you!");
return sb.toString();
}


现在 sb 是一个静态变量,所以只要它所在的类还装载在 Java 虚拟机中,它也将一直存在。该方法执行一次,一个新的 StringBuffer 将被创建并且被 sb 变量引用。在这种情况下,sb 变量以前引用的 StringBuffer 对象将会死亡,成为垃圾收集的对象。也就是说,这个死亡的 StringBuffer 对象被程序保留的时间比它实际需要保留的时间长得多——如果再也没有对该 scopingExample 方法的调用,它将会永远保留下去。

一个有问题的例子
即使如此,显式地赋空变量能够提高性能吗?我们会发现我们很难相信一个对象会或多或少对程序的性能产生很大影响,直到我看到了一个在 Java Games 的 Sun 工程师给出的一个例子,这个例子包含了一个不幸的大型对象。

清单 3. 仍在静态作用域中的对象


private static Object bigObject;

public static void test(int size) {
long startTime = System.currentTimeMillis();
long numObjects = 0;
while (true) {
//bigObject = null; //explicit nulling
//SizableObject could simply be a large array, e.g. byte[]
//In the JavaGaming discussion it was a BufferedImage
bigObject = new SizableObject(size);
long endTime = System.currentTimeMillis();
++numObjects;
// We print stats for every two seconds
if (endTime - startTime >= 2000) {
System.out.println("Objects created per 2 seconds = " + numObjects);
startTime = endTime;
numObjects = 0;
}
}
}


这个例子有个简单的循环,创建一个大型对象并且将它赋给同一个变量,每隔两秒钟报告一次所创建的对象个数。现在的 Java 虚拟机采用 generational 垃圾收集机制,新的对象创建之后放在一个内存空间(取名 Eden)内,然后将那些在第一次垃圾收集以后仍然保留的对象转移到另外一个内存空间。在 Eden,即创建新对象时所在的新一代空间中,收集对象要比在“老一代”空间中快得多。但是如果 Eden 空间已经满了,没有空间可供分配,那么就必须把 Eden 中的对象转移到老一代空间中,腾出空间来给新创建的对象。如果没有显式地赋空变量,而且所创建的对象足够大,那么 Eden 就会填满,并且垃圾收集器就不能收集当前所引用的这个大型对象。所产生的后果是,这个大型对象被转移到“老一代空间”,并且要花更多的时间来收集它。

通过显式地赋空变量,Eden 就能在新对象创建之前获得自由空间,这样垃圾收集就会更快。实际上,在显式赋空的情况下,该循环在两秒钟内创建的对象个数是没有显式赋空时的5倍——但是仅当您选择创建的对象要足够大而可以填满 Eden 时才是如此, 在 Windows 环境、Java虚拟机 1.4 的默认配置下大概需要 500KB。那就是一行赋空操作产生的 5 倍的性能差距。但是请注意这个性能差别产生的原因是变量的作用域不正确,这正是赋空操作发挥作用的地方,并且是因为所创建的对象非常大。更加深入的讨论请参见“赋空变量和垃圾收集”这篇文章。(参见 参考资料)。

最佳实践
这是一个有趣的例子,但是值得强调的是,最佳实践是正确地设置变量的作用域,而不要显式地赋空它们。虽然显式赋空变量一般应该没有影响,但总有一些反面的例子证明这样做会对性能产生巨大的负面影响。例如,迭代地或者递归地赋空集合内的元素使得这些集合中的对象能够满足垃圾收集的条件,实际上是增加了系统的开销而不是帮助垃圾收集。请记住这是个有意弄错作用域的例子,其实质是一个无意识的对象保留的例子。



大部分时间花在IO(println)
去掉println才可以比较!


关注.


效率应该是一样的,作用域不同


下面是我把LZ的代码换为汇编形式,从中很明显效率是一样的!区别仅仅是本地变量二与本地变量一存储的内容不同(i 和 “i” 倒了过来)
.class public T
.method ()V
aload_0
invokespecial java/lang/Object/()V
return
.end method
.method public test1()V
0: iconst_0
1: istore_2
2: iload_2
3: bipush 100
5: if_icmpge 24
8: ldc "i"
10:astore_1
11:getstatic java/lang/System/out Ljava/io/PrintStream;
14:aload_1
15:invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
18: iinc 2 1
21: goto 2
24:return
.end method

.method public test2()V
0: iconst_0
1: istore_1
2: iload_1
3: bipush 100
5: if_icmpge 24
8: ldc "i"
10: astore_2
11: getstatic java/lang/System/out Ljava/io/PrintStream;
14: aload_2
15: invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
18: iinc 1 1
21: goto 2
24: return
.end method
.end class



yzskyzj() ( ) 信誉:100

感觉应该是一样吧 String 类型的相当于一个常量,每次改变一下,相当于new了个新对象

=========================================================

同意这个,java中的String并不是基础类型,是一个比较特殊的类
第一种方法其实每次循环也要产生一个新对象,而不是简单的在内存地址里修改,而且还要修改str的指向,有可能效率比第二种还略差一点


学习……


前者申明了一个引用,后者申明了n个,你说哪个好呢?


我很喜欢 yuzl32(Hello!有酒醉) 提出的研究方法,所以自己也操练了一把。

javap 果然是个好东西,世界可以看得前所未有的清楚 :)

从 bytecode 可以看出,test1() 和 test2() 所执行的代码是一样的,所以不会有什么效率上的差别。两者唯一的差别是两个局部变量(String str 和 int i)在栈中的顺序不同。如果再把 test1() 改成下面的样子,则 bytecode 更是*完全*一样的,所以,显然就不会再有什么效率差别了。

public void test1(){
int i;
String str;
for(i=0;i<100;i++){
str="i";
//System.out.println(str);
}
}

至于前面曾有测试结果显示 test1() 执行所花的时间比 test2() 要多一些,而另一个测试结果显示当循环次数增加时,两者所花的时间逐渐接近。我对这个现象的解释是,JVM 的启动需要一个“动态润滑”的过程,先被执行到的 test1() 更多地承担了这个成本。而当循环次数增加时,这个成本所产生的影响就变得可以忽略了。

BTW: 看到前面曾有提到过“可见域”和“存在域”的问题,从 javap 的结果可以看到,实际上这个问题已经被编译器优化掉了。也就是说,所谓“可见域/存在域”,只是“程序员观点”下的概念,是写给编译器看的,而编译得到的结果(也就是 bytecode,或者不严格地说,可执行代码),实际是另外一套东西。


回到楼主的问题:

> 那种好,一个在循环外声明变量,一个在循环内。都是String对象。

经过编译器的处理,两种方式声明的变量,其“可见域/存在域”的差别已经不存在了,它们都是直接存在于“当前的栈”中,所以,一样。

> 如果不是String对象而是其他对象呢?谢谢。

这个问题跟是不是 String 对象无关。

倒是另一个问题需要注意,如果实际使用的对象是需要“创建”的,而且每次循环中创建的对象事实上并没有什么区别,那就一定要把“创建”过程放到循环外面去。在本例中,并没有创建新的 String 对象,只是把一个常量的“引用”赋值给一个局部变量,所以问题不大。当然,对于一个苛求效率的程序员,也是要把这个赋值语句放到循环外面比较好一点。


有趣的问题,我也是个新手,个人认为应该是第一种效率高些,第二种是不是在循环的每一步都要声明呢?


谢谢大家啊,一个小小得问题,能引发大家这么深入得讨论,是我没想到的.学到很多东西.


亲人啊。希望中国程序员能更强!!



可惜我在的公司小,就我一个技术人员(正被强烈的剥削中,福州号码:13665032356,拿家公司要我,打我电话啊),没有宽带,所以也就没办法把我解答的详细过程说出来.现在在网吧简单描述一下吧.其实这两种代码都是在引用JVM常量池中的字符串"i",并没有生成新的String.大家可以用
javap -verbose classfile 分析可知
另外也可以采取Oolong中的DumpClass程序获取更详细的分析
java DumpClass classfile


经实验,无明显差别


貌似編譯器會把字符串設置為共享的。
應該差不多


UP


我学Java不久,本人认为理论上第一种效率要高.
原因:第二种在每次循环时都调用了String str,第一种只调用一次String str;并且这两种方试所创建的String对象的方式相同,都是要stack中创建从1到100的String对象.
谢谢


String类型的变量只是更改指向内存中的字符值所在的内存地址.
如果仅针对英文的26个字母而言,JVM仅维护这26个字母值,而其他变量仅是拥有一个指向此值的一个地址引用.

所以上面的程序经过Compiler的自动优化以后,已经很难从测试结果上反应出性能上的差别.
不过个人认为,从开发的角度来看,还是倾向于第一种方式,唯一不同的是,在循环之后加上: str=null;
另外, 方式二比较容易产生内存碎片(如果Java垃圾收集器没有及时发挥作用的话).


第一种好啊
第一种对String的引用只在栈里面创建了一次
第二种创建了很多次又销毁了很多次.



yuzl32(Hello!有酒醉),你好象发了几次贴字啊.
不过我同意你最后的观点.
着两段代码没有太大的区别.
原因是他们都共享一个堆空间.你的一10000个str[i]不过引用了同一堆空间的值.
发贴的朋友如果是想研究作用域与效率的关系的话,例子就得改改,不要直接用
String str="i",或者 str = "i"
每次都要new一个新的空间,但如果这样的话,就无从比较了,因为你只能在循环类new你的空间.

public void test1(){
String str;
for(int i=0;i<100;i++){
str=new String("i");
System.out.println(str);
}
}

public void test2(){

for(int i=0;i<100;i++){
String str=new String("i");
System.out.println(str);
}

}
最终,你发现,你的方案统一了.
但作为一个良好的编程习惯,是应该将声明放在外面的,并且还要赋上初值.

最后说明,学JAVA只有2个星期,有错的大家指针,不要骂偶,谢谢



另外,你如果不相信我说的"原因是他们都共享一个堆空间.你的一10000个str[i]不过引用了同一堆空间的值"话,你可以用==和equals判断一下,
System.out.println(str[i] == str[i+1]);//false
System.out.println(str[i].equals( str[i+1]));//ture
第一个为false,是因为他们的在栈空间的确是有N个地址,但这些地址都同时指向了一个堆空间,所以第二个为ture.





研究的有点高深了,做一般的应用开发是不必要在乎这点速度的,程序的清晰和健壮是最重要的
==============================================================
我们用java来解决实际问题。java技术交流,讨论java的技术细节和最新技术。欢迎中高级程序员以及渴望学习java技术的初学者加入讨论。QQ群:3001581


这个是一个很有趣的问题,可能最终会讨论出一个结果,并且对java的机制有更层次的了解,但是反过来想,就算讨论出结论了又怎么样呢?


有趣的问题,一般情况下我会在外部声明,根本没想那么多。


o


有些情况必须用第一种,而有些情况必须用第二种。


↑返回目录
前一篇: 为什么执行new Socket(ip,port)速度很慢?
后一篇: 如何最快的判断一个数字(或字符串)是否是一个集合中的一员?谢谢大家。