JAVA中的堆栈问题

来源:互联网 发布:勇芳软件 编辑:程序博客网 时间:2024/05/16 19:43


          Java把内存划分成为两种:一种是,一种是与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。

      

        :存放一些基本类型的变量和对象的引用变量。

        优点:java自动释放掉所分配的空间,该内存空间可以立即被另作他用,存取速度比堆要快。

        缺点:存在栈中的数据大小与生存期必须确定的,缺乏灵活性。


        栈中主要存放一些基本类型的变量(int,short,long,byte,float,double,boolean,char)和对象引用。数据是可以共享的。假设我们同时定义:

    

int a=3;int b=3;<span style="font-family: SimSun;">  </span>

编译器先处理int a= 3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理int b= 3;在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均指向3的情况。(过程大致是:先创建变量引用,然后查找有没有值相同的,有的话把该引用指向相同变量值的地址,如果没有先开辟一个地址,然后更改引用。)
特别注意的是,这种字面值的引用与类对象的引用不同。假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另一个对象引用变量也即刻反映出这个变化。相反,通过字面值的引用来修改其值,不会导致另一个指向此字面值的引用的值也跟着改变的情况。如上例,我们定义完a与b的值后,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。
另一种是包装类数据,如Integer,String, Double等将相应的基本数据类型包装起来的类。这些类数据全部存在于【堆】中,Java用new()语句来显示地告诉编译器,在运行时才根据需要动态创建,因此比较灵活,但缺点是要占用更多的时间。
 

:存放由new创建的对象和数组(可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中这个变量就成了数组或对象的引用变量,就相对于一个名字。以后就可以使用栈中的引用变量来访问堆中的数组或对象。)。是有java虚拟机自动垃圾回收器来管理的。运行时动态分配空间。因为运行时分配内存,存取速度较慢。

String str = new String("abc"); 这种是用new()来新建对象的,它会存放在堆中,每调用一次就会创建一个新的对象。

String str = "abc"; 这种是存放在栈中,创建一个String类的对象引用变量str,然后在栈中查找有没有存放abc,如果没有,则将abc存放在栈中,并令str指向abc,如果有abc,则直接令str指向abc。


String str1 = "abc";String str2 = "abc";System.out.println(str1==str2); //true;

可以看出str1和str2是指向同一个对象的。

这里是创建了两个abc字符串,在内存中其实只存在一个对象,这种写法有利于节省内存空间,同时它也可以在一定程度上提高程序的运行速度。因为jvm会自动根据栈中数据的实际情况来决定是否有必要创建新对象。

String str1 = new String("abc");String str2 = new String("abc");String str3 = "abc";System.out.println(str1==str2); //false;System.out.println(str1==str3); //false

因为用了new,生成了两个不同的对象,是在堆中创建新的对象,而不管其他字符串值是否相等,是有必要创建新对象,从而加重了程序的负担。

由于String类是不变性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。


比较类里面的数值是否相等,用equals()方法,比较两个包装类的引用是否指向同一个对象时,用==。

String str1 = "abc";String str2 = "abc";String str3 = "a";String str4 = "bc";String a = new String("abc");String c = new String("abc");String d = new String("a");String e = new String("bc");System.out.println(str1 == str2);//trueSystem.out.println(str1.equals(str2));//trueSystem.out.println(str1 == str3+str4);//falseSystem.out.println(str1.equals(str3+str4));//trueSystem.out.println(str1==a);//falseSystem.out.println(str1.equals(a));//trueSystem.out.println(a == c);//falseSystem.out.println(a.equals(c));//trueSystem.out.println(a == d+e);//falseSystem.out.println(a.equals(d+e));//true

结论和建议:

  1. 我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,我们创建了String类的对象str。担心陷阱!对象可能并没有被创建!唯一可以肯定的是,指向String类的引用被创建了。至于这个引用到底是否指向了一个新的对象,必须根据上下文来考虑,除非你通过new()方法来显要地创建一个新的对象。因此,更为准确的说法是,我们创建了一个指向String类的对象的引用变量str,这个对象引用变量指向了某个值为"abc"的String类。清醒地认识到这一点对排除程序中难以发现的bug是很有帮助的。
  2. 使用String str = "abc";的方式,可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。这个思想应该是享元模式的思想,但JDK的内部在这里实现是否应用了这个模式,不得而知。
  3. 当比较包装类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==。
  4. 由于String类的不可改变的性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。





0 0
原创粉丝点击