尽管Java是基于C++的,但是相比之下,Java是更纯粹的面向对象程序设计语言。
Java语言尽管将一切都视为对象,但实际上操控着程序的是对象的一个引用(reference)。可以将这种情形想象成遥控器(引用)控制着电视机(对象)。如果没有电视机,遥控器也可单独存在,即可能存在不指向任何对象的引用,如果意图使用空引用进行任何操作,都会触发空指针异常错误(java.lang.NullPointerException)。因此一种安全的做法是创建引用时便进行初始化,比如 :
String s = "当时只道是寻常"
更通用的初始化方法通常是使用new关键字来关联一个新对象。
String s = new String("当时只道是寻常")
String对象是Java语言中重要的数据类型,但它并不是Java的基本数据类型。虽然有两种初始化方式,然而过程却不一样。
举个栗子
String str1 = new String("ABC");
String str2 = new String("ABC");
str1 == str2 //false
String str1 = “ABC”;可能创建一个或者不创建对象,如果”ABC”这个字符串在java String池里不存在,会在java String池里创建一个创建一个String对象(“ABC”),然后str1指向这个内存地址,无论以后用这种方式创建多少个值为”ABC”的字符串对象,始终只有一个内存地址被分配,之后的都是String的拷贝,Java中称为“字符串驻留”,所有的字符串常量都会在编译之后自动地驻留。
String str3 = "ABC";
String str4 = "ABC";
String str5 = "AB" + "C";
str3 == str4 //true
str3 == str5 // true
String str2 = new String(“ABC”);至少创建一个对象,也可能两个。因为用到new关键字,肯定会在heap中创建一个str2的String对象,它的value是“ABC”。同时如果这个字符串在java String池里不存在,会在java池里创建这个String对象“ABC”。
String a = "ABC";
String b = "AB";
String c = b + "C";
a == c //false
a、b在编译时就已经被确定了,而c是引用变量,不会在编译时就被确定。
这就涉及到了Java中String对象的不可变性,什么叫不可变性呢,简单的说就是一旦一个String对象被创建并被赋值(初始化)这个对象的值就不会变化。
一旦一个String对象在内存中创建,它将是不可改变的,所有的String类中方法并不是改变String对象自己,而是重新创建一个新的String对象。
建议在平时的使用中,尽量使用String = “abcd”;这种方式来创建字符串,而不是String = new String(“abcd”);这种形式,因为使用new构造器创建字符串对象一定会开辟一个新的heap空间,而双引号则是采用了String interning(字符串驻留)进行了优化,效率比构造器高。
关于更多String对象的特性,请移步 http://book.51cto.com/art/201504/472207.htm
程序运行时,创建的对象和指向对象的引用是如何进行存储的?内存又是如何分配的?
-
寄存器(register)。这是最快的存储区,因为它位于不同于其他存储区的地方——处理器内部。但是寄存器的数量极其有限,所以寄存器由编译器根据需求进行分配。你不能直接控制,也不能在程序中感觉到寄存器存在的任何迹象。
-
堆栈(stack)。位于通用RAM中,但通过它的“堆栈指针”可以从处理器那里获得支持。堆栈指针若向下移动,则分配新的内存;若向上移动,则释放那些内存。这是一种快速有效的分配存储方法,仅次于寄存器。创建程序时候,JAVA编译器必须知道存储在堆栈内所有数据的确切大小和生命周期,因为它必须生成相应的代码,以便上下移动堆栈指针。这一约束限制了程序的灵活性,所以虽然某些JAVA数据存储在堆栈中——特别是对象引用,但是JAVA对象不存储其中。
-
堆(heap)。一种通用性的内存池(也存在于RAM中),用于存放所有的JAVA对象。堆不同于堆栈的好处是:编译器不需要知道要从堆里分配多少存储区域,也不必知道存储的数据在堆里存活多长时间。因此,在堆里分配存储有很大的灵活性。当你需要创建一个对象的时候,只需要new写一行简单的代码,当执行这行代码时,会自动在堆里进行存储分配。当然,为这种灵活性必须要付出相应的代价。用堆进行存储分配和清理可能比用堆栈进行存储分配需要更多的时间。
-
静态存储(static storage)。这里的“静态”是指“在固定的位置”。静态存储里存放程序运行时一直存在的数据。你可用关键字static来标识一个对象的特定元素是静态的,但JAVA对象本身从来不会存放在静态存储空间里。
-
常量存储(constant storage)。常量值通常直接存放在程序代码内部,这样做是安全的,因为它们永远不会被改变。有时,在嵌入式系统中,常量本身会和其他部分分割离开,所以在这种情况下,可以选择将其放在ROM中。
-
非RAM存储。若数据完全独立于一个程序之外,则程序不运行时仍可存在,并在程序的控制范围之外。其中两个最主要的例子便是“流式对象”和“固定对象”。对于流式对象,对象会变成字节流,通常会发给另一台机器。而对于固定对象,对象保存在磁盘中。即使程序中止运行,它们仍可保持自己的状态不变。对于这些类型的数据存储,一个特别有用的技巧就是它们能存在于其他媒体中。一旦需要,甚至能将它们恢复成普通的、基于RAM的对象。Java 1.1提供了对Lightweight persistence的支持。未来的版本甚至可能提供更完整的方案。
对象引用存储在堆栈(stack)中,对象存储于堆(heap)中,上面谈到的Java String池存储在常量存储区,常量存储区也是方法区的一部分。 了解更多Java虚拟机 运行时数据区
以上。
参考
《Java编程思想》
http://book.51cto.com/art/201504/472207.htm (作者:葛一鸣|来源:电子工业出版社)
https://www.cnblogs.com/mayj/p/7093526.html
http://www.cnblogs.com/Cratical/archive/2012/08/21/2649985.html
网友评论