《Java編程思想》學習筆記

前言

前陣子剛看完《Java編程思想》這本書,寫篇文章來記錄一下知識點。編程

一切都是對象

1.對象存儲到什麼地方?
(1)寄存器
最快的存儲區,數量有限。根據需求分配,沒法直接控制
(2)堆棧
位於通用RAM(隨機訪問存儲器)中。某些數據存儲於堆棧中,特別是對象引用。速度僅次於寄存器
(3)
用於存放全部的Java對象。當執行new操做時,會自動在堆裏進行存儲分配
(4)常量存儲
一般直接存放在程序代碼內部
(5)非RAM存儲
數據存活於程序以外,在程序沒有運行時也能夠存在。兩個基本的例子是流對象和持久化數組

2.基本類型變量(int/double/long/.....)直接存儲值並置於堆棧中,所以更加高效。安全

3.當建立一個數組對象時,實際上就是建立了一個引用數組,而且每一個引用都被初始化爲null。數據結構

4.當變量做爲類的成員使用時,Java才確保給定其默認值。然而此方法並不適用於局部變量(定義於方法內的變量,局部變量不會被初始化)app

5.main方法中的args用來存儲命令行參數。ui

操做符

1.若是對對象使用a=b,那麼a和b都指向本來只有b指向的那個對象。this

2.==和!=比較的是對象的引用。命令行

3.基本類型比較內容是否相等直接使用==和!=便可。線程

4.Object類的equals方法默認使用"=="比較。code

5.大多數Java類庫都重寫了equals方法,以便用來比較對象的內容,而非比較對象的引用。

6.若是對char、byte或short類型數值移位處理,在進行移位以前會先被轉換爲int類型,而且獲得的結果也是一個int類型的值。

初始化和清理

1.在Java中,"初始化"和"建立"被捆綁在一塊兒,二者不能分離。

2.涉及基本類型的重載:
若是傳入的數據類型(實參)小於方法中聲明的形參,實際數據類型就會被提高。
char型有所不一樣,若是沒法找到剛好接受char類型參數的方法,就會把char直接提高至int。
若是傳入的實際參數較大,就會強制類型轉換。

3.若是已經定義了一個構造器,編譯器就不會幫你自動建立默認構造器。構造器其實是static方法,只不過該static聲明是隱式的

4.this關鍵字只能在方法內部調用,表示對"調用方法的那個對象"的引用。

5.在構造器中可經過this調用另外一個構造器。但卻不能調用兩個構造器。此外,必須將構造器調用置於最起始處,不然會報錯。

6.在static方法的內部不能調用非靜態方法,反過來是能夠的。

7.垃圾回收器只知道回收那些經由new分配的內存。

8.native(本地方法)是一種在Java中調用非Java代碼的方式。

9.在類的內部,變量定義的前後順序決定了初始化的順序。
即便變量定義散佈於方法定義之間,它們仍舊會在任何方法(包括構造器)被調用以前獲得初始化。

10.不管建立多少個對象,靜態數據都只佔一份存儲區域。

11.靜態初始化只有在必要時刻進行。只有在"第一個對象被建立"或者"第一次訪問靜態數據"的時候,它們纔會被初始化。此後靜態對象不會再被初始化。

12.初始化的順序是先靜態對象(若是它們還沒被初始化過),然後是非靜態對象

複用類

1.每個非基本的對象都有一個toString方法。

2.Java會自動在子類的構造器中插入對父類構造器的調用。全部構造器都會顯示或隱式地調用父類構造器

3.構建過程是從父類"向外"擴散的,因此父類在子類構造器能夠訪問它以前就已經完成了初始化。

4.調用父類構造器是你在子類構造器中要作的第一件事

5.向上轉型:能夠理解爲"子類是父類的一種類型"。

6.對於基本類型,final使數值恆定不變。對於對象引用,final使引用恆定不變(即沒法再把它指向另外一個對象)。

7.final類:防止別人繼承。
final參數:你能夠讀參數,但卻沒法修改參數。(通常用來向匿名內部類傳遞數據)
final方法:把方法鎖定,以防任何繼承類修改它的含義。

多態

1.把對某個對象的引用視爲對其父類型的引用的作法被稱爲向上轉型。

2.只有非private方法才能被覆蓋。

3.域和靜態方法不是多態的。(當子類轉型爲父類引用時,任何域訪問操做都將由編譯器解析,因此不是多態的)

4.調用構造器要遵循下面的順序:
(1)調用父類構造器。
(2)按聲明順序調用成員的初始化方法。
(3)調用子類構造器。

5.爲何在子類構造器裏要先調用父類構造器???
答:在構造器內部,咱們必需要確保要使用的成員都已經構建完畢。爲確保這一目的,惟一的方法就是首先調用父類構造器。那麼在進入子類構造器時,在父類中可供咱們訪問的成員都已獲得初始化。

6.繼承與清理
(1)當覆蓋父類的dispose()方法時,務必記住調用父類版本的dispose()方法,不然父類的清理動做就不會發生。
(2)應該首先對子類進行清理,而後纔是父類。這是由於子類的清理可能會調用父類的某些方法,所以不應過早地銷燬它們。

7.構造器內部的多態行爲

Class A{
 void draw(){print("A.draw()");}
 A(){draw();}
 }
 Class B extends A{
 void draw(){print("B.draw()");}
 }
 Class Test{
 public static void main(String[] args){
     new B();
   }
 } 
 輸出:B.draw()

在B構造器中調用父類A構造器,在裏面調用的是子類B覆蓋後的draw()方法。

所以,編寫構造器時有一條有效的準則:"若是能夠的話,避免在構造器內部調用其它方法"。

接口

1.包含抽象方法的類叫作抽象類。若是一個類包含一個或多個抽象方法,該類必須被限定爲抽象的。

2.若是繼承抽象類,那麼必須爲基類中全部的抽象方法提供方法定義。若是不這樣作,那麼子類就必須限定爲抽象類。

3.接口中的域隱式地是static和final的。

4.接口中的方法默認是public的,所以實現接口中的方法時必須定義爲public的。不然其訪問權限就下降了,這是Java不容許的。

5.經過繼承來擴展接口
interface A{....}
interface B{....}
interface C extends A,B{.....}//僅適用於接口繼承
通常狀況下,只能夠將extends用於單一類,可是能夠引用多個基類接口。

6.嵌套在類中的接口能夠是private的。
嵌套在另外一個接口中的接口自動是public,而不能聲明爲private的。

7.當實現某個接口時,並不須要實現嵌套在其內部的任何接口。並且,private接口不能在定義它的類以外被實現。

內部類

1.當生成一個內部類的對象時,內部類對象隱式地保存了一個引用,指向建立它的外部類對象。(靜態內部類除外)
內部類對象可以訪問外部類對象的全部成員和方法(包括私有的)。

  1. .new和.this
class A{
      class B{}
   }
(1)建立內部類對象:
   A a=new A();
   A.B b=a.new B();
(2)生成外部類對象引用:
   class A{
       class B{
           public A getA(){
               return A.this;
       }
   }
}

3.若是定義一個匿名內部類,而且但願它使用一個在其外部定義的對象,那麼編譯器會要求其參數引用是final的。

4.當建立靜態內部類的對象時:
(1)不須要其外部類對象
(2)不能從靜態內部類對象中訪問非靜態的外部類對象

5.無論一個內部類被嵌套多少層,它都可以訪問全部它嵌入的外部類的全部成員。

6.類文件命名規則:外部類名字+$+內部類名字
(1)普通內部類:A$B
(2)匿名內部類(編譯器會生成一個數字做爲其標識符) A$1

類型信息

1.若是某個對象出如今字符串表達式中(涉及"+"和字符串對象的表達式),toString()方法會被自動調用,以生成該對象的String。

2.每當編寫而且編譯了一個新類,就會產生一個Class對象。更恰當地說,是被保存在一個同名的.class文件中。

3.Java程序在它開始運行以前並不是徹底被加載,其各個部分是在必需時才加載的。
類加載器首先檢查這個類的Class對象是否已經加載。若是還沒有加載,默認的類加載器就會根據類名查找.class文件。

4.Class對象僅在須要的時候才被加載,static初始化塊是在類加載時進行的。

5.Class.forName("A") 用來加載類A並獲取A.Class對象。字符串必須使用全限定名,包括包名。

6.Class AClass=A.class 當使用.class來建立Class對象時,不會自動地初始化該Class對象

7.static final int a=1;
static final int b=ClassInitialization.rand.nextInt(1000);
static int c=1;
若是一個static final值是編譯期常量,就像a那樣,那麼這個值不須要對初始化類就能夠被讀取。
讀取b和c則須要先對類進行初始化。

泛型

1.擦除
(1)能夠聲明ArryaList.class,但不能聲明ArrayList<Integer>.class;

(2)

Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
System.out.println(c1 == c2);//返回true

(3)List<String>和List<Integer>在運行時事實上是相同的類型,二者都被擦除爲List類型。

2.擦除的補償

public class czy<T>{
    public static void f(Object arg){
        (1)if (arg instanceof T){} //錯誤
        (2)T t = new T(); //錯誤
        (3)T[] array = new T[10]; //錯誤
    }
}

(1)沒法使用instanceof是由於其類型信息已經被擦除了。
(2)new T()失敗,部分緣由是由於擦除,另外一部分緣由是由於編譯器不能驗證T具備默認構造器。
除非引入類型標籤

public boolean f(Class<T> kind,Object arg){
    t = kind.newInstance(); //正確
    return kind.isInstance(arg); //正確
}

3.<? extends T>表示類型的上界,表示參數化類型多是T或T的子類

4.以下

public void f(List<? super Apple> apples){
    apples.add(new Apple()); //正確
    apples.add(new GreenApple()); //正確  GreenApple繼承自Apple
    apples.add(new Fruit()); //錯誤
}

向apples其中添加Apple或Apple的子類型是安全的。
由於Apple或者GreenApple確定是<? super Apple>的子類,因此編譯經過。

5.List<?>看起來等價於List<Object>,而List實際上也是List<Object>。

6.自動包裝機制不能應用於數組。

7.public class Czy<W,T>{
    void f(List<W> v){}
    void f(List<T> v){}//錯誤
}

因爲擦除的緣由,重載方法會產生相同的類型簽名。

數組

1.你不能實例化具備參數化類型的數組:

Czy<A>[] czy = new Czy<A>[10];——>編譯錯誤

擦除會移除參數類型信息,而數組必須知道它們所持有的確切類型,以強制保證類型安全。

2.複製數組
System.arraycopy()→用它複製數組比用for循環快得多。
System.arraycopy()不會執行自動包裝和自動拆包,兩個數組必須具備相同的確切類型。

3.數組比較
Arrays類提供了重載後的equals()方法,用來比較整個數組。
此方法針對全部基本類型和Object都作了重載。

4.數組排序
(1)實現Comparable接口,實現compareTo()方法。
若是沒有實現Comparable接口,調用Arrays.sort()方法會拋出異常。由於sort方法須要把參數類型變爲Conparable
(2)建立一個實現了Comparator接口的類。

容器深刻研究

1.Arrays.asList()會生成一個固定尺寸的List,該List只支持那些不會改變數組大小的操做。
任何對底層數據結構的尺寸進行修改都會拋出一個異常。
應該把Arrays.asList()的結果做爲構造器參數傳遞給Collection,這樣就能夠生成容許使用全部方法的普通容器。
好比:List<String> list = new ArrayList<>(Arrays.asList(a));

2.Collections.unmodifiableList()產生不可修改的容器。

3.散列碼是"相對惟一"的,用以表明對象的int值。

4.若是不爲你的鍵覆蓋hashcode()和equals(),那麼使用散列的數據結構(HashSet,HashMap,LinkedHashSet,LinkedHashMap)就沒法正確處理你的鍵。

5.LinkedHashMap在插入時比HashMap慢一點,由於它維護散列表數據結構的同時還要維護鏈表(以保持插入順序)。
正是因爲這個鏈表,使得其迭代速度更快。

6.HashMap使用的默認負載因子是0.75.

7.Java容器採用快速報錯(fail-fast)機制。
它會檢查容器上的任何除了你的進程所進行的操做之外的全部變化,一旦它發現其它進程修改了容器,就會拋出異常。防止在你迭代容器的時候,其它線程在修改容器的值。

8.WeakHashMap容許垃圾回收器自動清理鍵和值。

9.Iterable接口被foreach用來在序列中移動。若是你建立了任何實現Iterable接口的類,均可以將它用於foreach語句中。
foreach語句能夠用於數組或其它任何Iterable,但這並不意味着數組也是一個Iterable。

10.總結

  • 若是要進行大量的隨機訪問,就使用ArrayList;若是要常常從表中間插入或刪除元素,就應該使用LinkedList。
  • 各類Queue以及棧的行爲,由LinkedList提供支持。
  • HashMap用來快速訪問。
    TreeMap使得「鍵」始終處於排序狀態,因此沒有HashMap快。
    LinkedHashMap保持元素插入的順序。
  • Set不接受重複元素。
    HashSet提供最快的查詢速度。
    TreeSet保持元素處於排序狀態。
    LinkedHashSet以插入順序保存元素。
  • 新程序中不該該使用過期的Vector、Hashtable和Stack。
相關文章
相關標籤/搜索