堆与栈
堆(heap)与栈 (stack) 都是java用来在Ram(random access memory 随机存储器)中存放数据的地方。
java中自动管理heap 和 stack,程序员不能直接设置。
stack : 都是虚拟机stack。
堆是一个运行时数据区,对象从中分配空间。这些对象通过new、newarray、anewarray和multianewarray等指令建立,它们不需要程序代码来显式地释放。堆是由垃圾回收来负责的.
堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收 集器会自动收走这些不再使用的数据。
堆缺点是,由于要在运行时动态分配内存,存取速度较慢。
栈中用来存放一些原始数据类型的局部变量数据和对象的引用(String、数组、对象等等)但不存放对象内容。
栈的优势是,存取速度比堆要快,仅次于寄存器,(不是寄存器)。栈数据可以共享。
缺点是,存在栈中的数据大小与生存期必须是 确定的,缺乏灵活性。
栈中主要存放一些基本类型的变量数据(int,short,long,byte,float,double,boolean,char)和对象引用。
引用在白皮书中直接指出是实际就是一个C的"指针"。
假设我们同时定义:
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,它是由编译器完成的,它有利于节省空间。
字符串
1. String不是基本数据类型,String是一个对象。因为对象的默认值是null,所以String的默认值也是null。它是一种特殊的对象,有其它对象没有的一些特性。
2. new String()和new String("")都是申明一个新的空字符串,是空串不是null。
3. String str="helloword"和String str=new String("helloword")的区别
示例:
String s0="helloword";
String s1="helloword";
String s2="hello" + "word";
System.out.println(s0==s1);
System.out.println(s0==s2);
结果为:
true
true
因为例子中的s0和s1中的"word"都是字符串常量,它们在编译期就被确定了,所以s0==s1为true。而"hello"和"word"也都是字符串常量,当一个字符串由多个字符串常量连接而成时,它自己肯定也是字符串常量,所以s2也同样在编译期就被解析为一个字符串常量,所以s2也是常量池中"helloword"的一个引用。
所以得出s0==s1==s2。用new String()创建的字符串不是常量,不能在编译期就确定,所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。
示例:
String s0="helloword";
String s1=new String("helloword");
String s2="hello" + new String("word");
System.out.println(s0==s1);
System.out.println(s0==s2);
System.out.println(s1==s2);
结果为:
false
false
false
s0还是常量池中"helloword"的应用。s1因为无法在编译期确定,所以是运行时创建的新对象"helloword"的引用。s2因为有后半部分new String("word")所以也无法在编译期确定,所以也是一个新创建对象"helloword"的应用。
4. 关于equals()和==
这个对于String简单来说就是比较两字符串的Unicode序列是否相当,如果相等返回true。而==是比较两字符串的地址是否相同,是否是同一个字符串的引用。
5. 关于String是不可变的
String的实例一旦生成就不会再改变了。比如说:String str="hello"+"word"+" "+"ans"; 就是有4个字符串常量,首先"hello"和"word"生成了"helloword"存在内存中,又和"ans"生成了"helloword ans";并把这个字符串的地址赋给了str,就是因为String的"不可变"产生了很多临时变量,这也就是为什么建议用StringBuffer的原因了,因为StringBuffer是可改变的。
下面是一些String相关的常见问题:
String中的final用法和理解
final StringBuffer a = new StringBuffer("111");
final StringBuffer b = new StringBuffer("222");
a=b;//此句编译不通过
final StringBuffer a = new StringBuffer("111");
a.append("222");//编译通过
可见,final只对引用的"值"(即内存地址)有效,它迫使引用只能指向初始指向的那个对象,改变它的指向会导致编译期错误。至于它所指向的对象的变化,final是不负责的。
总结
栈中用来存放一些原始数据类型的局部变量数据和对象的引用(String、数组、对象等等)但不存放对象内容。
堆中存放使用new关键字创建的对象。
字符串是一个特殊包装类,其引用是存放在栈里的,而对象内容必须根据创建方式不同定义(常量池和堆)。有的是编译期就已经创建好,存放在字符串常量池中,而有的是运行时才被创建。