经典Stringstr=newString(abc)内存分配问题
今天要和存储管理相关的⼀些知识,⽹上搜了半天也没有到完善的(30%的程度都不到),没办法,下载了曾经⼤学⾥的⼀本pdf 格式的教学书,看了整整⼀天才算是搞明⽩存储管理中的⼀部分知识。曾⼏何时,我曾写过⼤学⽆⽤的⼀些⼩论题,觉得⼤学⾥教的东西不切合实际,理论化偏严重,总认为⽤理论教出来的⼈,总是说着⽜逼,做事掉渣的⼈。所以,在⼤学⾥,我的学习只能说是应付考试,太多的东西都没有去深⼊了解。直到今⽇,才总算是明⽩了:书到⽤时⽅恨少,事⾮经过不知难。很多东西,⽼师在⼤学⾥教给我们,不在乎,不⽤⼼,现在才算明⽩原来⾃⼰丢掉了⼀个财富。
当代码写到⼀定量的时候,就不会有java/C/C#之分;当技术做到⼀定深度时,就没有什么软件/硬件之分。所有的⼀切都只为⼀个⽬标:效率。不说⽆⽤的话了,直接切⼊正题,本⽂是我搜索存储管理时,偶尔发现的⼀篇⽂章,觉得很好,确实很经典,因为我也写java很久,也不清楚这两句的具体区别。复制过来,没事可以看看。虽然偷盗⾏为不可取,但是学会了就是⾃⼰的,谁敢说我是偷来的?做⼀些有助于他⼈的事情,别⼈幸福,⾃⼰也幸福。
Java中以下两句有什么区别呢?
String str1="abc";
String str2=new String("abc");
Java把内存划分成两种:⼀种是栈内存,⼀种是堆内存。
在函数中定义的⼀些基本类型的变量和对象的引⽤变量都在函数的栈内存中分配。
当在⼀段代码块定义⼀个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作⽤域后,Java会⾃动释放掉为该变量所分配的内存空间,该内存空间可以⽴即被另作他⽤。
堆内存⽤来存放由new创建的对象和数组。
在堆中分配的内存,由Java虚拟机的⾃动垃圾回收器来管理。
在堆中产⽣了⼀个数组或对象后,还可以在栈中定义⼀个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的⾸地址,栈中的这个变量就成了数组或对象的引⽤变量。
引⽤变量就相当于是为数组或对象起的⼀个名称,以后就可以在程序中使⽤栈中的引⽤变量来访问堆中的数组或对象。
具体的说:
栈与堆都是Java⽤来在Ram中存放数据的地⽅。与C++不同,Java⾃动管理栈和堆,程序员不能直接地设置栈或堆。
Java的堆是⼀个运⾏时数据区,类的(对象从中分配空间。这些对象通过new、newarray、anewarray和multianewarray等指令建⽴,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存⼤⼩,⽣存期也不必事先告诉编译器,因为它是在运⾏时动态分配内存的,Java的垃圾收集器会⾃动收⾛这些不再使⽤的数据。但缺点是,由于要在运⾏时动态分配内存,存取速度较慢。
栈的优势是,存取速度⽐堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据⼤⼩与⽣存期必须是确定的,缺乏灵活
性。栈中主要存放⼀些基本类型的变量(,int, short, long, byte, float, double, boolean, char)和对象句柄。
栈有⼀个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:
int a = 3;
int b = 3;
编译器先处理int a = 3;⾸先它会在栈中创建⼀个变量为a的引⽤,然后查栈中是否有3这个值,如果没到,就将3存放进来,然后将a指向3。接着处理int b = 3;在创建完b的引⽤变量后,因为在栈中已
经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。这时,如果再令a=4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。要注意这种数据的共享与两个对象的引⽤同时指向⼀个对象的这种共享是不同的,因为这种情况a的修改并不会影响到b, 它是由编译器完成的,它有利于节省空间。⽽⼀个对象引⽤变量修改了这个对象的内部状态,会影响到另⼀个对象引⽤变量。
String是⼀个特殊的包装类数据。可以⽤:
String str = new String("abc");
String str = "abc";
两种的形式来创建,第⼀种是⽤new()来新建对象的,它会在存放于堆中。每调⽤⼀次就会创建⼀个新的对象。
⽽第⼆种是先在栈中创建⼀个对String类的对象引⽤变量str,然后查栈中有没有存放"abc",如果没有,则将"abc"存放进栈,并令str指向”abc”,如果已经有”abc” 则直接令str指向“abc”。
⽐较类⾥⾯的数值是否相等时,⽤equals()⽅法;当测试两个包装类的引⽤是否指向同⼀个对象时,⽤==,下⾯⽤例⼦说明上⾯的理论。
(
看到这点,作者有话说,java中equals()和==是两个不同的概念,前⼀种是对值是否相等的判别,后⼀种是对引⽤地址是否相等的判别,具体可度度
String str1 = "abc";
String str2 = new String ("abc");
不防判断⼀下:
str1.equals(str2) 的值
str1 == str2 的值
)
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
可以看出str1和str2是指向同⼀个对象的。
String str1 =new String ("abc");
字符串常量存放在哪个内存区域String str2 =new String ("abc");
System.out.println(str1==str2); // false
⽤new的⽅式是⽣成不同的对象。每⼀次⽣成⼀个。
因此⽤第⼆种⽅式创建多个”abc”字符串,在内存中其实只存在⼀个对象⽽已. 这种写法有利与节省内存空间. 同时它可以在⼀定程度上提⾼程序的运⾏速度,因为JVM会⾃动根据栈中数据的实际情况来决定是否有必要创建新对象。⽽对于String str = new String("abc");的代码,则⼀概在堆中创建新对象,⽽不管其字符串值是否相等,是否有必要创建新对象,从⽽加重了程序的负担。
另⼀⽅⾯, 要注意: 我们在使⽤诸如String str = "abc";的格式定义类时,总是想当然地认为,创建了String类的对象str。担⼼陷阱!对象可能并没有被创建!⽽可能只是指向⼀个先前已经创建的对象。只有通过new()⽅法才能保证每次都创建⼀个新的对象。由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使⽤StringBuffer类,以提⾼程序效率。
练习题:
以下code⽣成⼏个String对象
String a="abc";
String b="abc";
String c=new String("abc");
String d=c.intern();
String a="abc"; //分配空间
String b="abc"; //不分配空间
String c=new String("abc"); //分配空间
String d=c.intern(); //这句是关键!!
如果仅仅是这样
c ==
d ?结果是 false;
因为引⽤变量c指向堆内存的地址;但是intern(); 却在栈中开辟了⼀块内存。
对于本题来讲:
c.intern(); 执⾏时,在栈内存中发现存在对象“abc”,所以直接将指针指向“abc”
这样 a == d, b==d 结果为true;
所以这条的答案是2
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论