1. 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。
2. 栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。且,栈数据可以共享。
堆的优势是可以动态地分配内存大小,所有使用new xxx()构造出来的对象都在堆中存储,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。
3. 常量池:存放字符串常量和基本类型常量(public static final)。
常量池的好处是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。
例如字符串常量池,在编译阶段就把所有的字符串文字放到一个常量池中。
(1)节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间。
(2)节省运行时间:比较字符串时,==比equals()快。对于两个引用变量,只用==判断引用是否相等,也就可以判断实际值是否相等。
4. Java中的数据类型有两种。
一种是基本类型(primitive types), 共有8种,即int, short, long, byte, float, double, boolean, char(注意,不包含String)。
如int a = 3; 这里的a是一个指向int类型的引用,指向3这个字面值。这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。
另外,栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:
```
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与b的值后,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。
引用类型(不包含String):
引用类型说明就比较简单,堆中存放的是实例,栈中存放地址,堆中属性的存储是在实例内部。
String类型:
String类型是一种特殊的引用类型:其特殊性在于,在定义是除了堆栈外,还牵扯到了常量池的使用。
String的定义有两种方式:
String str1="abc";
String str2=new String("abc");
其区别在于第一种方式不经过堆,直接由栈指向常量池。第二种现在堆内开辟内存,在内存中存储String的值。
第一种的具体过程如下:
(1) 先定义一个名为str的对String类的对象引用变量放入栈中。
(2) 在常量池中查找是否存在内容为"abc"字符串对象。
(3) 如果不存在则在常量池中创建"abc",并让str引用该对象。
(4) 如果存在则直接让str引用该对象。
我们来更进一步,修改str1的值试试看:
这就是说,赋值的变化导致了类对象引用的变化,str1指向了另外一个新对象!而str2仍旧指向原来的对象。
我们再来看第二种定义String的方式String str = new String("abc"):
(1) 先定义一个名为str的对String类的对象引用变量放入栈中。
(2) 然后在堆中(不是常量池)创建一个指定的对象,并让str引用指向该对象。
(3) 在常量池中查找是否存在内容为"abc"字符串对象。
(4) 如果不存在,则在常量池中创建内容为"abc"的字符串对象,并将堆中的对象与之联系起来。
(5) 如果存在,则将new出来的字符串对象与字符串常量池中的对象联系起来(即让那个特殊的成员变量value的指针指向它)
最后我们以具体例子来结束String的分析:
我们定义4个String,分别为:
String str1="abc";
String str2="abc";
String str3="ab"+"c";
String str4=new String("abc");
通过==来判断其内存地址是否相同。
第一个结果很好理解,因为在编译的时候,"abc"被存储在常量池中,str1和str2的引用都是指向常量池中的"abc"。所以str1和str2引用是相同的。
第二个结果是由于编译器做了优化,编译器会先把字符串拼接,再在常量池中查找这个字符串是否存在,如果存在,则让变量直接引用该字符串。所以str1和str3引用也是相同的。
str4的对象不是显式赋值的,编译器会在堆中重新分配一个区域来存储它的对象数据。所以str1和str4的引用是不一样的。