今天读了《Think in JAVA》一书的1.10部分,对象的创建和生命周期,突然感觉这一部分是很常见又很容易被忽视的问题,故拿到此做一记录。
区别
栈区:由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。对象的存储空间和生命周期在编写程序时确定。这种方式将存储空间的分配和释放放在首位,但是牺牲了灵活性,必须在编写程序时知道对象的数量、生命周期和类型。
堆区:一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意与数据结构中的堆不一样。这种方式动态的创建销毁对象,知道运行时才能知道需要多少对象,生命周期如何以及具体类型时什么。
代码
//main.c
int a = 0; //全局初始化区
char *p1; //全局未初始化区
void main()
{
int b; //栈
char s[] = "abc"; //栈
char *p2; //栈
char *p3 = "123456"; //"123456"在常量区,p3在栈上。
static int c =0; //全局(静态)初始化区
p1 = (char*)malloc(10);
p2 = (char*)malloc(20); //p1,p2在栈里,分配的空间在堆中
strcpy(p1,"123456"); //"123456"放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}
注意c语言中的内存包括4块区域:栈、堆、全局区(静态区)、常量区。c++中包括5个区,和上述的类似,这里不再讲。
其他说明
- java完全采用动态内存的分配方式。
- 在堆中创建对象时,C/C++必须通过编程方式来确定何时销毁对象,否则容易发生内存泄漏;而java提供了”垃圾回收器“机制,可以自动回收内存。
- Java中的数据类型有两种。
一种是基本类型(primitive types)出于追求速度的原因,就存在于栈中。
另一种是包装类数据,如Integer, String, Double等将相应的基本数据类型包装起来的类。这些类数据全部存在于堆中,Java用new()语句来显示地告诉编译器,在运行时才根据需要动态创建,因此比较灵活,但缺点是要占用更多的时间。 - String是一个特殊的包装类数据。即可以用String str = new String(“abc”);的形式来创建,也可以用String str = “abc”;的形式来创建。前者是规范的类的创建过程,即在Java中,一切都是对象,而对象是类的实例,全部通过new()的形式来创建。
值得注意的是,一般String类中字符串值都是直接存值的。但像String str = “abc”;这种场合下,其字符串值却是保存了一个指向存在栈中数据的引用!
用new()来新建对象的,都会在堆中创建,而且其字符串是单独存值的,即使与栈中的数据相同,也不会与栈中的数据共享
使用String str = “abc”;的方式,可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String(“abc”);的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。