字符串是存儲在字符串常量池中的。例如如下的兩個字符串的內存地址值是同樣的:html
String str1 = "hello" + "world"; String str2 = "helloworld"; System.out.println(str1 == str2); // true System.out.println(str1.equals(str2)); // true String str3 = "hello"; String str4 = "world"; String str5 = str3 + str4; System.out.println(str5 == str2); // false System.out.println(str5.equals(str2)); // true
在以上的代碼中str2和str5的地址值不相同,若是咱們對str5使用intern()
方法即:java
String str6 = str5.intern(); // native方法 System.out.println(str6 == str2); // true
就能夠返回true。面試
面試題:假設字符串常量池中不存在字符串"hello",那麼String s = new String("hello")
建立了幾個對象?spring
答:兩個堆空間的value值(字符數組)和字符串常量池中的hello實體。咱們能夠經過查看new String(String str)
的源碼:數組
/** The value is used for character storage. */ private final char value[]; /** * Initializes a newly created {@code String} object so that it represents * the same sequence of characters as the argument; in other words, the * newly created string is a copy of the argument string. Unless an * explicit copy of {@code original} is needed, use of this constructor is * unnecessary since Strings are immutable. * * @param original * A {@code String} */ public String(String original) { this.value = original.value; this.hash = original.hash; }
java中一切皆對象,可是咱們操做的其實是指向這個對象的句柄(Handle),這個句柄也叫作引用(Reference)或者指針(Pointer)。咱們能夠將這一情形想象成用遙控器(Handler)操做電視機(Object)。沒有電視機,遙控器。安全
即便沒有電視機,遙控器也能夠單獨存在。即句柄是能夠單獨存在的(並不指向任何實體)。例如:less
String s;
以上的java代碼建立的僅僅是句柄而不是對象。此時若是向s發送一條消息,將會或者一個運行時異常,所以更安全的作法是:建立一個句柄的時候進行顯式初始化:ide
String s = "";
Register。處理器內部(最快),因爲數量有限,因此寄存器是根據須要有編譯器分配的。咱們對它沒有直接的控制權,也不可能在程序中找到寄存器的任何蹤影。this
Stack。駐留與常規RAM區域,速度僅次於寄存器。咱們能夠經過它的「堆棧指針」得到直接的處理支持。(指針下移建立新的內存;指針上移釋放內存)。建立程序時java編譯器必須準確知道堆棧內保存的全部數據的「長度」以及「存在的時間」(必須生成相應的代碼,以便於向上或者向下移動指針)。這一限制無疑影響了程序的靈活性,因此java將對象的句柄保存在堆棧中,可是對象並不存放在其中。spa
Heap。一種常規用途的內存池(也是在RAM區域),保存java對象。Heap最吸引人的地方在於:編譯器並沒必要要知道要從堆中分配多少存儲空間,也沒必要要知道存儲的數據要在堆中停留多長時間——用堆保存數據會獲得更大的靈活性。然而every coin have two slices,在堆中分配存儲空間會花費更長的時間!
靜態存儲。「靜態」(Static)指的是「位於固定位置」。程序運行期間,靜態存儲的數據將隨時等候調用。咱們能夠用static關鍵字指出一個對象的特定元素是靜態的,可是java對象自己永遠不會置入靜態存儲空間。
常數存儲。常數存儲一般直接置於一個程序代碼內部。這樣作是安全的,由於他們永遠不會被改變。有的常數須要嚴格保護,能夠考慮將他們置入只讀存儲器(ROM)。
非RAM存儲:將數據徹底保存子啊一個程序以外。對典型的2個例子就是「流式對象」和「固定對象」。
流式對象:對象-->字節流-->另外一臺機器
固定對象:對象-->磁盤
基本數據類型因爲比較經常使用,而堆棧的效率又高於堆。因此基本數據類型都是保存在堆棧中。對於基本數據類型咱們不須要用new,而是建立了一個並不是句柄的「自動變量」,該變量容納了具體的值,並保存在堆中能夠更高效的存取。
在C、C++中使用數組是很是危險的,由於那些數組只是內存塊,若是程序訪問本身內存塊以外的數據或者在初始化以前使用內存會產生不可預料的後果。在C++中應該儘可能避免使用數組而換用Standard TemplateLibrary中更安全的容器。java中的數組會自動記性範圍檢查會形成少許的內存開銷。可是咱們能夠換來更高的工做效率。
變量的做用域是由花括號的位置決定的。
在C、C++中如下的代碼是合法的:
{ int x = 10; { int x = 100; // 不合法Duplicate local variable x } }
可是在java中編譯器會認爲變量x已經被定義,因此C、C++能將一個變量「隱藏」在一個更大的做用域中,java的設計者認爲這樣使程序產生了混淆。
成員變量都有默認值,而局部變量必須進行初始化。
文檔註釋只能爲public和protected的成員處理文檔,private和default的不會被javadoc提取。文檔註釋中能夠嵌入html,例如:
方法拋出異常的時候,該方法會從棧上當即被取出,而異常再度丟給棧上的方法(也就是調用方),若是調用方沒有對異常進行處理而是繼續拋出異常,調用方就會從棧上彈出,異常再度交給此時的棧頂方法,如此一路下去……
finally中的代碼有一種狀況下是執行不到的:finally的前面出現了System.exit(0)。
static只能修飾類的成員(變量和方法),不能修飾局部變量。static變量存放在方法區。隨着類的加載而加載,存在方法區中,隨着類的消失而消失,生命週期最長。若是沒有給定初值,static變量會被默認置0.(或者null)。
Q:若是一個類被標記爲final,再將該類中的方法標記位final是否是不少餘?
A:不僅是多餘,並且是多了不少!若是一個類爲final,那麼它根本就沒有子類,根本不可能覆寫父類中的方法(只有繼承纔有覆寫)。
工程目錄以下:
加載該配置文件應該這樣寫:
public class Test { @org.junit.Test public void test() throws IOException { ClassLoader classLoader = this.getClass().getClassLoader(); InputStream is = classLoader.getResourceAsStream("org/gpf/conf/db.properties"); Properties properties = new Properties(); properties.load(is); properties.list(System.out); } }
public class Person <K,V>{ // some code... } public class Student extends Person<String,Integer> { // some code... } Class<?> clazz = Student.class; Type type = clazz.getGenericSuperclass(); // org.gpf.Person<java.lang.String, java.lang.Integer> ParameterizedType parameterizedType = (ParameterizedType) type; // type的子接口 for (Type c : parameterizedType.getActualTypeArguments()) { System.out.println(((Class<?>)c).getName()); // Class是Type接口的實現類 }
所謂枚舉,就是枚舉類的對象的個數是有限的,能夠窮舉出來的類。實際上單例模式也可使用枚舉來實現(Effective Java中的單例經典實現,枚舉只有一個成員)。jdk1.5以前須要自定義枚舉類,jdk1.5以後提供了enum
關鍵字用於定義枚舉類。
例如季節是有限的:春夏秋冬。
// jdk1.5以前的枚舉類 public class Season { // 1.聲明final屬性 private final String seasonName; // 季節名 private final String seasonDescribe; // 季節描述 // 2.爲保證明例的數目是肯定的須要私有化構造器,在構造器中初始化final的屬性 private Season(String seasonName,String seasonDescribe) { this.seasonName = seasonName; this.seasonDescribe = seasonDescribe; } // 3.經過公用的方法調用屬性 public String getSeasonName() { return seasonName; } public String getSeasonDescribe() { return seasonDescribe; } // 4.內部實例化枚舉類的對象 public static final Season SPRING = new Season("spring", " 春暖花開"); public static final Season SUMMER = new Season("summer", " 夏日炎炎"); public static final Season FALL = new Season("fall", " 秋高氣爽"); public static final Season WINTER = new Season("spring", " 白雪皚皚"); public String show() { return "Season [seasonName=" + seasonName + ", seasonDescribe=" + seasonDescribe + "]"; } }
咱們可使用如下的方式進行調用
Season season = Season.FALL; System.out.println(season.show()); System.out.println(season.getSeasonName() + "-->" + season.getSeasonDescribe());
jdk1.5以後咱們可使用enum
關鍵字來簡化枚舉類的定義:
// jdk1.5以後的枚舉類 public enum Season { SPRING("spring", " 春暖花開"), SUMMER("summer", " 夏日炎炎"), FALL("fall", " 秋高氣爽"), WINTER("spring", " 白雪皚皚"); private final String seasonName; // 季節名 private final String seasonDescribe; // 季節描述 private Season(String seasonName,String seasonDescribe) { this.seasonName = seasonName; this.seasonDescribe = seasonDescribe; } public String getSeasonName() { return seasonName; } public String getSeasonDescribe() { return seasonDescribe; } public String show() { return "Season [seasonName=" + seasonName + ", seasonDescribe=" + seasonDescribe + "]"; } }
這樣使用枚舉類:
Season season = Season.FALL; System.out.println(season.show()); System.out.println(season.getSeasonName() + "-->" + season.getSeasonDescribe()); Season[] seasons = Season.values(); // 返回全部枚舉類的對象的數組 for (Season s : seasons) { System.out.println(s.getSeasonName()); } season = Season.valueOf("SUMMER"); // 返回枚舉類型的對象 System.out.println(season);
咱們也可讓枚舉類型實現接口:
// jdk1.5以後的枚舉類 public enum Season implements Info{ SPRING("spring", " 春暖花開"){ @Override public void show() { System.out.println(1); } }, SUMMER("summer", " 夏日炎炎"){ @Override public void show() { System.out.println(2); } }, FALL("fall", " 秋高氣爽"){ @Override public void show() { System.out.println(3); } }, WINTER("spring", " 白雪皚皚"){ @Override public void show() { System.out.println(4); } }; private final String seasonName; // 季節名 private final String seasonDescribe; // 季節描述 private Season(String seasonName,String seasonDescribe) { this.seasonName = seasonName; this.seasonDescribe = seasonDescribe; } public String getSeasonName() { return seasonName; } public String getSeasonDescribe() { return seasonDescribe; } @Override public void show() { System.out.println("Season [seasonName=" + seasonName + ", seasonDescribe=" + seasonDescribe + "]"); } }
以上的程序中每一個枚舉類型的實例都各自實現本身的方法!