面向對象是把構成問題事務分解成各個對象,建立對象的目的不是爲了完成一個步驟,而是爲了描敘某個事物在整個解決問題的步驟中的行爲。
面向對象是以功能來劃分問題,而不是步驟
2.抽象:就是把現實生活中的某一類東西提取出來,用程序代碼表示,我們通常叫做類或者接口。抽象包括兩個方面:一個是數據抽象,一個是過程抽象。數據抽象也就是對象的屬性。過程抽象是對象的行爲特徵。
封裝:把客觀事物封裝成抽象的類,並且類可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的進行封裝隱藏。封裝分爲屬性的封裝和方法的封裝。
繼承:是對有着共同特性的多類事物,進行再抽象成一個類。這個類就是多類事物的父類。父類的意義在於抽取多類事物的共性。
多態:允許不同類的對象對同一消息做出響應。方法的重載、類的覆蓋正體現了多態。實現多態的技術稱爲:動態綁定(dynamic binding),是指在執行期間判斷所引用對象的實際類型,根據其實際的類型調用其相應的方法。多態的作用:消除類型之間的耦合關係。
多態存在的三個必要條件
一、要有繼承;
二、要有重寫;
三、父類引用指向子類對象。
強制的:一種隱式做類型轉換的方法。
◆重載的:將一個標誌符用作多個意義。
◆參數的:爲不同類型的參數提供相同的操作。
◆包含的:類包含關係的抽象操作。
3.override(重寫,覆蓋)
1、方法名、參數、返回值相同。
2、子類方法不能縮小父類方法的訪問權限。
3、子類方法不能拋出比父類方法更多的異常(但子類方法可以不拋出異常)。
4、存在於父類和子類之間。
5、方法被定義爲final不能被重寫。
overload(重載,過載)
1、參數類型、個數、順序至少有一個不相同。
2、不能重載只有返回值不同的方法名。
3、存在於父類和子類、同類中。
方法的重寫(Overriding)和重載(Overloading)是Java多態性的不同表現。
重寫(Overriding)是父類與子類之間多態性的一種表現,而重載(Overloading)是一個類中多態性的一種表現。
4.不可以
,每一個類必須有自己的構造函數,負責構造自己這部分的構造
子類不會覆蓋父類的構造函數,相反必須負責在一開始調用父類的構造函數
5.private這個大家都知道,就是只有在本類中才能訪問。
public正好和private相反,在任何地方都能訪問。
protected是在本包內能訪問,而在包外只有它的子類能訪問
protected和默認(包訪問權限)是很相似的,在同一個包內,它們是一樣的,而在另一個包內,默認是不能訪問的,而protected是隻有子類能訪問
6.String類是final類故不可以繼承
String:字符串常量,字符串長度不可變
StringBuffer:字符串變量(Synchronized,即線程安全)。如果要頻繁對字符串內容進行修改,出於效率考慮最好使用StringBuffer,如果想轉成String類型,可以調用StringBuffer的toString()方法。
StringBuffer
上的主要操作是 append 和 insert 方法,
StringBuffer對方法加了同步鎖或者對調用的方法加了同步鎖,所以是線程安全的
StringBuilder並沒有對方法進行加同步鎖,所以是非線程安全的。
最後,如果程序不是多線程的,那麼使用StringBuilder效率高於StringBuffer。
7.如果要比較實際內存中的內容,那就要用equals方法
如果是你自己定義的一個類,比較自定義類用equals和==是一樣的,都是比較句柄地址,因爲自定義的類是繼承於object,而object中的equals就是用==來實現的
那爲什麼我們用的String等等類型equals是比較實際內容呢,是因爲String等常用類已經重寫了object中的equals方法,讓equals來比較實際內容
hashcode 過一個object的key來拿hashmap的value,hashmap的工作方法是,通過你傳入的object的hashcode在內存中找地址,當找到這個地址後再通過equals方法來比較這個地址中的內容是否和你原來放進去的一樣,一樣就取出value。
但假如說你new一個object作爲key去拿value是永遠得不到結果的,因爲每次new一個object,這個object的hashcode是永遠不同的,所以我們要重寫hashcode,你可以令你的hashcode是object中的一個恆量,這樣永遠可以通過你的object的hashcode來找到key的地址,然後你要重寫你的equals方法,使內存中的內容也相等。。。
8.
抽象類表示的是,這個對象是什麼。接口表示的是,這個對象能做什麼。比如,男人,女人,這兩個類(如果是類的話……),他們的抽象類是人。說明,他們都是人。
人可以吃東西,狗也可以吃東西,你可以把“吃東西”定義成一個接口,然後讓這些類去實現它.
抽象類的功能要遠超過接口,但是,定義抽象類的代價高。因爲高級語言來說(從實際設計上來說也是)每個類只能繼承一個類。在這個類中,你必須繼承或編寫出其所有子類的
所有共性。雖然接口在功能上會弱化許多,但是它只是針對一個動作的描述。而且你可以在一個類中同時實現多個接口。在設計階段會降低難度的。
第一點. 接口是抽象類的變體,接口中所有的方法都是抽象的。而抽象類是聲明方法的存在而不去實現它的類。
第二點. 接口可以多繼承,抽象類不行
第三點. 接口定義方法,不能實現,而抽象類可以實現部分方法。
第四點. 接口中基本數據類型爲static 而抽類象不是的。
當你關注一個事物的本質的時候,用抽象類;當你關注一個操作的時候,用接口。
9.基本數據類型不是對象,也就是使用int、double、boolean等定義的變量、常量。
基本數據類型沒有可調用的方法。
自動拆箱(unboxing),也就是將對象中的基本數據從對象中自動取出
10.
前面已經說了,Java的泛型是僞泛型。爲什麼說Java的泛型是僞泛型呢?因爲,在編譯期間,所有的泛型信息都會被擦除掉。正確理解泛型概念的首要前提是理解類型擦出(type erasure)。
Java中的泛型基本上都是在編譯器這個層次來實現的。在生成的Java字節碼中是不包含泛型中的類型信息的。使用泛型的時候加上的類型參數,會在編譯器在編譯的時候去掉。這個過程就稱爲類型擦除。
C++模版機制實現方式之間的重要區別。
在這個例子中,我們定義了兩個ArrayList數組,不過一個是ArrayList<String>泛型類型,只能存儲字符串。一個是ArrayList<Integer>泛型類型,只能存儲整形。最後,我們通過arrayList1對象和arrayList2對象的getClass方法獲取它們的類的信息,最後發現結果爲true。說明泛型類型String和Integer都被擦除掉了,只剩下了原始類型。
11.泛型是Java
SE 1.5的新特性,泛型的本質是參數化類型,也就是說所操作的數據類型被指定爲一個參數。這種參數類型可以用在類、接口和方法的創建中,分別稱爲泛型類、泛型接口、泛型方法。 Java語言引入泛型的好處是安全簡單。
在Java SE 1.5之前,沒有泛型的情況的下,通過對類型Object的引用來實現參數的“任意化”,“任意化”帶來的缺點是要做顯式的強制類型轉換,
很明顯,使用java泛型,我們可以省掉強制類型轉換。編譯器會保留參數的類型信息,執行類型檢查,執行類型轉換操作。因此開發人員不需要自己確保類型轉換的安全性,而把這個交給編譯器去做。
12.
有序否 |
允許元素重複否 |
||
Collection |
否 |
是 |
|
List |
是 |
是 |
|
Set |
AbstractSet |
否 |
否 |
HashSet |
|||
TreeSet |
是(用二叉樹排序) |
||
Map |
AbstractMap |
否 |
使用key-value來映射和存儲數據,Key必須惟一,value可以重複 |
HashMap |
|||
TreeMap |
是(用二叉樹排序) |
Vector和HashTable是線程同步的(synchronized)。性能上,ArrayList和HashMap分別比Vector和Hashtable要好。
Map也屬於集合系統,但和Collection接口不同。Map是key對value的映射集合
調用java.util.Collections.sort(List list)方法來進行排序的時候,List內的Object都必須實現了Comparable接口。
|
- public static void work(Map<String, Student> map) {
- Collection<Student> c = map.values();
- Iterator it = c.iterator();
- for (; it.hasNext();) {
- System.out.println(it.next());
- }
- }
13.數據結構中有數組和鏈表來實現對數據的存儲,但這兩者基本上是兩個極端
那麼我們能不能綜合兩者的特性,做出一種尋址容易,插入刪除也容易的數據結構?答案是肯定的,這就是我們要提起的哈希表。哈希表((Hash
table)既滿足了數據的查找方便,同時不佔用太多的內容空間,使用也十分方便。
哈希表有多種不同的實現方法,我接下來解釋的是最常用的一種方法—— 拉鍊法,我們可以理解爲“鏈表的數組”
一般情況是通過hash(key)%len獲得,也就是元素的key的哈希值對數組長度取模得到。
首先HashMap裏面實現一個靜態內部類Entry,其重要的屬性有 key
, value, next,從屬性key,value我們就能很明顯的看出來Entry就是HashMap鍵值對實現的一個基礎bean,我們上面說到HashMap的基礎就是一個線性數組,這個數組就是Entry[],Map裏面的內容都保存在Entry[]裏面
解決hash衝突的辦法
- 開放定址法(線性探測再散列,二次探測再散列,僞隨機探測再散列)
- 再哈希法
- 鏈地址法
- 建立一個公共溢出區
HashMap的存取實現
// 存儲時:int hash = key.hashCode(); // 這個hashCode方法這裏不詳述,只要理解每個key的hash是一個固定的int值
int index = hash % Entry[].length;
Entry[index] = value;
// 取值時:
int hash = key.hashCode();
int index = hash % Entry[].length;
return Entry[index];
1)put
疑問:如果兩個key通過hash%Entry[].length得到的index相同,會不會有覆蓋的危險?這裏HashMap裏面用到鏈式數據結構的一個概念。上面我們提到過Entry類裏面有一個next屬性,作用是指向下一個Entry。打個比方,
第一個鍵值對A進來,通過計算其key的hash得到的index=0,記做:Entry[0] = A。一會後又進來一個鍵值對B,通過計算其index也等於0,現在怎麼辦?HashMap會這樣做:B.next = A,Entry[0]
= B,如果又進來C,index也等於0,那麼C.next = B,Entry[0] = C;這樣我們發現index=0的地方其實存取了A,B,C三個鍵值對,他們通過next這個屬性鏈接在一起。所以疑問不用擔心。也就是說數組中存儲的是最後插入的元素
當然HashMap裏面也包含一些優化方面的實現,這裏也說一下。比如:Entry[]的長度一定後,隨着map裏面數據的越來越長,這樣同一個index的鏈就會很長,會不會影響性能?HashMap裏面設置一個因子,隨着map的size越來越大,Entry[]會以一定的規則加長長度。
}
再散列rehash過程
當哈希表的容量超過默認容量時,必須調整table的大小。當容量已經達到最大可能值時,那麼該方法就將容量調整到Integer.MAX_VALUE返回,這時,需要創建一張新表,將原表的映射到新表中。
}
}
14.
在Runnable中創建ThreadLocal
1、在多線程的類(如ThreadDemo類)中,創建一個ThreadLocal對象threadXxx,用來保存線程間需要隔離處理的對象xxx。
2、在ThreadDemo類中,創建一個獲取要隔離訪問的數據的方法getXxx(),在方法中判斷,若ThreadLocal對象爲null時候,應該new()一個隔離訪問類型的對象,並強制轉換爲要應用的類型。
3、在ThreadDemo類的run()方法中,通過調用getXxx()方法獲取要操作的數據,這樣可以保證每個線程對應一個數據對象,在任何時刻都操作的是這個對象。
2、在ThreadDemo類中,創建一個獲取要隔離訪問的數據的方法getXxx(),在方法中判斷,若ThreadLocal對象爲null時候,應該new()一個隔離訪問類型的對象,並強制轉換爲要應用的類型。
3、在ThreadDemo類的run()方法中,通過調用getXxx()方法獲取要操作的數據,這樣可以保證每個線程對應一個數據對象,在任何時刻都操作的是這個對象。
ublic class ThreadLocalTest implements Runnable{
ThreadLocal<Studen> studenThreadLocal = new ThreadLocal<Studen>();
@Override
public void run() {
String currentThreadName = Thread.currentThread().getName();
System.out.println(currentThreadName + " is running...");
Random random = new Random();
int age = random.nextInt(100);
System.out.println(currentThreadName + " is set age: " + age);
Studen studen = getStudent(); //通過這個方法,爲每個線程都獨立的new一個student對象,每個線程的的student對象都可以設置不同的值
studen.setAge(age);
System.out.println(currentThreadName + " is first get age: " + studen.getAge());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( currentThreadName + " is second get age: " + studen.getAge());
}
private Studen getStudent() {
Studen studen = studenThreadLocal.get();
if (null == studen) {
studen = new Studen();
studenThreadLocal.set(studen);
}
return studen;
}
public static void main(String[] args) {
ThreadLocalTest t = new ThreadLocalTest();
Thread t1 = new Thread(t,"Thread A");
Thread t2 = new Thread(t,"Thread B");
t1.start();
t2.start();
}
}
class Studen{
int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
和HashMap一樣,Hashtable 也是一個散列表,它存儲的內容是鍵值對(key-value)映射。
table是一個Entry[]數組類型,而Entry實際上就是一個單向鏈表。哈希表的"key-value鍵值對"都是存儲在Entry數組中的。
count是Hashtable的大小,它是Hashtable保存的鍵值對的數量。
threshold是Hashtable的閾值,用於判斷是否需要調整Hashtable的容量。threshold的值="容量*加載因子"。
loadFactor就是加載因子。
modCount是用來實現fail-fast機制的
get()
的作用就是獲取key對應的value,沒有的話返回null
put()
的作用是對外提供接口,讓Hashtable對象可以通過put()將“key-value”添加到Hashtable中。
putAll()
的作用是將“Map(t)”的中全部元素逐一添加到Hashtable中
remove()
的作用就是刪除Hashtable中鍵爲key的元素
通過Enumeration遍歷Hashtable的鍵:
第一步:根據elements()獲取Hashtable的集合。
第二步:通過Enumeration遍歷“第一步”得到的集合。
Enumeration enu = table.elements(); while(enu.hasMoreElements()) { System.out.println(enu.nextElement()); }
15.
ashtable和HashMap類有三個重要的不同之處
第一個不同主要是歷史原因。Hashtable是基於陳舊的Dictionary類的,HashMap是Java 1.2引進的Map接口的一個實現。也許最重要的不同是2.Hashtable的方法是同步的,而HashMap的方法不是。這就意味着,雖然你可以不用採取任何特殊的行爲就可以在一個多線程的應用程序中用一個Hashtable,但你必須同樣地爲一個HashMap提供外同步。一個方便的方法就是利用Collections類的靜態的synchronizedMap()方法,它創建一個線程安全的Map對象,並把它作爲一個封裝的對象來返回。這個對象的方法可以讓你同步訪問潛在的HashMap。這麼做的結果就是當你不需要同步時,你不能切斷Hashtable中的同步(比如在一個單線程的應用程序中),而且同步增加了很多處理費用。3第三點不同是,只有HashMap可以讓你將空值作爲一個表的條目的key或value。HashMap中只有一條記錄可以是一個空的key,但任意數量的條目可以是空的value。這就是說,如果在表中沒有發現搜索鍵,或者如果發現了搜索鍵,但它是一個空的值,那麼get()將返回nul
16.Hashtable它是實現了IDictionary和ICollection接口的,它的key與value都是object類型的,不支持泛型,進行類型轉換成需要裝箱與拆箱(boxing,unboxing),這在性能肯定會有一些影響,所以,微軟這邊給出了支持泛型的鍵值對集合Dictionary,而Dictionary本身也不是線程安全的,我們需要對它加鎖(lock),才能避免多線程環境下產生的一些錯誤。通過分析Hashtable就知道,synchronized是針對整張Hash表的,即每次鎖住整張表讓線程獨佔,安全的背後是巨大的浪費
17.
左邊便是Hashtable的實現方式---鎖整個hash表;而右邊則是ConcurrentHashMap的實現方式---鎖桶(或段)。ConcurrentHashMap將hash表分爲16個桶(默認值),諸如get,put,remove等常用操作只鎖當前需要用到的桶。試想,原來只能一個線程進入,現在卻能同時16個寫線程進入(寫線程才需要鎖定,而讀線程幾乎不受限制,之後會提到),併發性的提升是顯而易見的。ConcurrentHashMap的讀取併發,因爲在讀取的大多數時候都沒有用到鎖定,所以讀取操作幾乎是完全的併發操作,而寫操作鎖定的粒度又非常細,比起之前又更加快速(這一點在桶更多時表現得更明顯些)。只有在求size等操作時才需要鎖定整個表。 ConcurrentHashMap中主要實體類就是三個:ConcurrentHashMap(整個Hash表),Segment(桶),HashEntry(節點),
18.ector的方法都是同步的(Synchronized),是線程安全的(thread-safe),而ArrayList的方法不是
19.ArrayList的內部實現是基於內部數組Object[],所以從概念上講,它更象數組,但LinkedList的內部實現是基於一組連接的記錄,所以,它更象一個鏈表結構,所以,它們在性能上有很大的差別:
20.Collections是個java.util下的類,它包含有各種有關集合操作的靜態方法。 Collection是個java.util下的接口,它是各種集合結構的父接口。
Java中創建(實例化)對象的五種方式
1、用new語句創建對象,這是最常見的創建對象的方法。
2、通過工廠方法返回對象,如:String str = String.valueOf(23);
3、運用反射手段,調用java.lang.Class或者java.lang.reflect.Constructor類的newInstance()實例方法。如:Object obj = Class.forName("java.lang.Object").newInstance();
4、調用對象的clone()方法。
5、通過I/O流(包括反序列化),如運用反序列化手段,調用java.io.ObjectInputStream對象的 readObject()方法。