包括:private修飾的成員變量、getter和setter以及無參和有多個參數的有參構造方法java
String底層是字節數組byte[]。數組
String不可變,但能夠被共享。緩存
(1)字符串常量池(全局字符串池):因爲字符串自己具備不可變性,所以使用字符串常量池對於一樣的字符串能夠起到一個緩存的做用,防止屢次內存分配,從而提供內存的利用率app
(2)運行時常量池:當程序運行到某個類的時候,class文件中的信息就會被解析到內存的方法區的運行時常量池中,每一個類都有一個運行時常量池。測試
(3)class文件常量池:class常量池是在編譯後每一個class文件都有的,class文件中除了包含類的版本 字段 方法 接口等描述信息,還有一項信息就是常量池,用於存放編譯器生成的各類字面量和符號的引用。優化
從大的方面來講,JVM內存分爲幾個方面:ui
堆內存是JVM中最大的一塊區域,被全部線程共享,在JVM啓動時建立。幾乎全部對象的空間分配都在堆區來進行。spa
堆又分爲新生代和老年代內存,二者的內存大小的比例大概是1:2。線程
新生代又分爲Eden、From區和To區,三者的內存大小的比例大概是8:1:1。幾乎全部新對象的內存分配都在Eden區完成。code
劃分不一樣的區,分別保存不一樣的數據,其內存回收策略也不相同。
在GC中,Eden區活躍的對象會被移動到survivor區,不活躍的會被GC清理掉。Survivor區中的對象達到必定的年齡,就會被移動到老年代中。
堆內存能夠在物理上不連續,可是邏輯上要連續。
方法區又被稱爲永久代,也是被全部的線程所共享的,該區域主要保存類的信息,靜態變量,常量和編譯器編譯後的代碼等數據。
jdk7中,將永久代中的字符串常量池移動到堆中。jdk8 撤銷了永久代 ,引入了元空間
方法區不須要連續的內存,能夠選擇固定大小或者可擴展,而且還能夠選擇不實現垃圾回收,相對而言,垃圾收集行爲在這個區域是比較少見的,但並不是數據進入方法區就如永久代的名字同樣永久存在了。這個區域的內存回收目標主要是針對常量池的回收和對類型的卸載,圾回收機制的條件是至關苛刻的。
public static void main(String[] args) { String s1 = "abc"; String s2 = "abc"; System.out.println(s1 == s2);//true,s1指向常量池中建立的對象地址,s2指向常量池中已經存在的對象地址,這個對象是相同的 // 使用字符數組構造一個字符串 char[] c = {'a','b','c'}; String s3 = new String(c); String s4 = new String(c); System.out.println(s3 == s4);//false,堆中不一樣對象 String s5 = new String("abc"); String s6 = new String ("abc"); System.out.println(s5==s6);// false,堆中不一樣對象 System.out.println(s1 == s3);//false,s1指向常量池中建立的對象地址,s3指向堆中對象 System.out.println(s4 == s5);//false,堆中不一樣對象 }
String str1 = "abc";//在常量池中建立對象並獲得引用 String str2 = new String("abc");//在堆中建立對象並引用,而後查看常量池,此時已經有對象,因此不用再建立
public static void main(String[] args) { String s1 = "hello";//常量池中建立hello的引用 String s2 = "world";//常量池中建立world的引用 String s3 = "hello" +"world";//常量池中建立helloworld的引用 String s4 = s1 +"world";//堆中建立的對象的引用,至關於new StringBuilder(s1).append("world").toString();toString()建立一個新的堆對象。 String s5 = s1 + s2;//堆中建立對象的引用,至關於//new StringBuilder(s1).append(s2).toString();
String s6 = (s1 + s2).intern(); //該值在常量池中已經存在,返回s3的引用
String s7 = "helloworld"; //該值在常量池中已經存在,返回s3的引用
System.out.println(s3==s4);// false
System.out.println(s3 == s7);//true
System.out.println(s3==s5);//false
System.out.println(s4==s5);//false
System.out.println(s3==s6);//true }
+鏈接字符串:
常量+常量:編譯器直接優化生成結果,不須要生成StringBuilder。
常量+變量:須要生成StringBuilder,一行語句須要一個StringBuilder,append的數量基於變量和常量的個數,但當多個常量連在一塊兒時,編譯器會優化直接生成常量運算結果,算做一個append處理。
變量和變量:須要生成StringBuilder,一行語句須要一個StringBuilder,append的數量基於常量的個數
public static void main(String[] args) { String str1 = new StringBuilder("計算機").append("軟件開發").toString(); System.out.println(str1.intern()== str1);//true,str1.intern()在常量池中建立一個指向str1的引用,因此相等 String str2 = new StringBuilder("ja").append("va").toString();//java是一個特殊的字符串 在jdk的底層,存在不少「java」字符串 System.out.println(str2.intern()==str2);//false,雖然沒有人爲建立,可是常量池中存在java對象,因此intern()返回的結果是常量池中的變量且不是ss堆對象的引用
}
public static void main(String[] args) { String str1 = new String("str") + new String("01"); str1.intern();//intern()方法本質至關於把調用它的對象放入常量池,若是有就不放,並且放的是地址不是副本。 String str2 = "str01"; System.out.println(str1 == str2);//true }
public static void main(String[] args) { String s1 = new String("he") +new String("llo"); String s2 = new String("h") +new String("ello"); String s3 = s1.intern(); String s4 = s2.intern(); System.out.println(s1==s3);//true System.out.println(s2 == s4);//false System.out.println(s3 == s4);//true }
抽象類的語法格式:abstract class 類名{ }
抽象方法的語法格式:訪問修飾符 abstract 返回值 方法名( );
抽象類的特色:
接口是一系列抽象方法的集合,與抽象類不一樣,不能夠聲明普通方法。
接口的語法格式:[修飾符] interface 接口名 [extends] [接口列表]{ 接口體 }
接口前面能夠用public修飾,省略控制修飾符該接口也是公開的。
extends 關鍵詞和類語法中的 extends 相似,用來定義直接的父接口。和類不一樣,一個接口能夠繼承多個父接口,當 extends 後面有多個父接口時,它們之間用逗號隔開。接口繼承接口的語法格式爲:[修飾符] class 類名 implements 接口列表{ 類體 }
類實現接口用 implements
關鍵字,Java 中的類只能是單繼承的,但一個 Java 類能夠實現多個接口,這也是 Java 解決多繼承的方法。
接口下面不能夠有實體方法。
接口中能夠有成員變量,修飾符默認爲 public static final
(即使不寫修飾符也默認是這樣),由於是常量因此必須在聲明時對這些成員變量賦初值。能夠這樣說,接口中的成員變量實際就是常量。
接口中的抽象方法默認且必須是 public
的。
接口 C 繼承接口 A 和 B,不一樣的方法都被直接繼承下來,相同的方法合併後被繼承下來。
Java8以後,接口中能夠有默認/靜態方法,這個方法能夠有實體。
接口不能繼承抽象類,只能繼承其餘接口。
InnerClass
能夠直接訪問外部類 OuterClass
中的屬性和方法,對於成員內部類的使用,須要先生成外部類對象,而後再以 外部類對象.new 內部類() 的形式生成內部類對象。靜態內部類 InnerClass 只能訪問外部類 OuterClass 的靜態屬性和靜態方法,能夠經過類名直接調用,如 **類名.方法()**。與之相似,靜態內部類中的方法也能夠經過 **靜態內部類名.方法()** 的形式調用。若是測試類和靜態內部類不在同一個.java文件中,那麼能夠經過 **外部類名.靜態內部類.方法()** 的形式調用。