天天看點

JavaEE 基礎學習

Java基礎

一、java面向對象的三大特性與含義

• 繼承:從已有的類得到繼承資訊建立新類的過程,繼承讓變化中的軟體系統有了一定的延續性,同時繼承也是封裝程式的可變因素的重要手段。

• 封裝:通常認為封裝是把資料和操作資料的方法綁定起來,對資料的通路隻能通過已經定義的接口。面向對象的本質就是将現實世界描繪成一系列完全自制、封閉的對象。可以說,封裝就是隐藏一切可隐藏的東西,隻向外界提供簡單的程式設計接口;

• 多态:允許不同子類型的對象對同一消息作出不同的響應。簡單說就是用同樣的對象引用調用同樣的方法但是做了不同的事情。

• 實作多态需要做的兩件事:①、方法重寫;②、對象造型

二、java多态

• 多态定義:允許不同類的對象對同一消息作出響應,即同一消息可以根據發送對象的不同而表現出多種不同的行為方式。

• 多态的作用:消除類型間的耦合關系。

• 實作多态的技術:動态綁定

• 多态存在的必要條件:①有繼承;②有重寫;③父類引用指向子類對象。

• 多态的好處:①可替換性、②可擴充性、③接口性、④靈活性、⑤簡化性。

• 多态的實作方式:接口實作、繼承父類進行方法重寫、同一個類内進行方法

重載。

三、Java中的SoftReference是什麼?

• Java 中的 SoftReference 即對象的軟引用。如果一個對象具有軟引用,記憶體空間足夠,垃圾回收器就不會回收它;如果記憶體空間不足了,就會回收這些對象的記憶體。隻要垃圾回收器沒有回收它,該對象就可以被程式使用。軟引用可用來實作記憶體敏感的高速緩存。使用軟引用能防止記憶體洩露,增強程式的健壯性。

• SoftReference的特點:它的一個執行個體儲存對一個Java對象的軟引用,該軟引用的存在不妨礙垃圾收集線程對該Java對象的回收。也就是說,一旦SoftReference儲存了一個Java對象的軟引用後,在垃圾線程對這個Java對象回收前,SoftReference類所提供的get()方法傳回Java對象的強引用。但是,一旦垃圾線程回收該Java對象之後,get()方法将傳回null。

四、abstract關鍵字

• abstract可以修飾類、方法

• 如果一個類被abstract修飾,此類必須被繼承使用,此類不可以生成對象,但是可以聲明。

• abstract可以 将子類的共性最大限度的抽取出來放在父類,以提高代碼的簡潔性。

• abstract修飾方法時,方法為抽象方法,該方法不需要進行實作,實作留給子類,子類覆寫該方法之後才能生效

• abstract不能與static、final放在一起否則會出現錯誤

五、重寫和重載

• 1、重寫:如果子類中定義的某個方法與其父類有相同的名稱和參數,則該方法被重寫了。

• 重載:如果一個類内有多個重名的方法,它們或有不同的參數個數、或有不同的參數類型、或有不同的參數次序,則稱為重載。

• 重載的規則:必須改變參數清單、可以改變傳回類型、可以改變通路修飾符、可以聲明新的或更廣的檢查異常、能夠在同一個類中或者在一個子類中被重載、無法通過傳回值類型來判斷方法是否重載。

• 重寫的規則:參數清單、傳回值類型必須相同,通路權限不能低于父類的方法,被final修飾的方法不能被重寫,static修飾的方法不能被重寫但是可以再次聲明,構造方法不能被重寫,重寫方法不能抛出更廣泛的強制異常,通路權限不能下降。

六、Java中如何定義常量

• 接口中常量預設為static final;

• Java 5.0 中引入類Enum類型;

• 在普通類中使用static final修飾的變量;

七、abstract、interface、final、static總結

abstract

• 隻要含有抽象方法的類必須聲明為抽象類;

• 抽象類可以有具體的實作方法;

• 抽象類内可以沒有抽象方法;

• 抽象方法必須被子類實作,或者子類也是抽象的;

• 抽象類不可以被執行個體化,但是可以被聲明;

• 若使用抽象類内部的方法,則必須有一個子類繼承這個抽象類,并且實作抽象方法,通過子類執行個體化去調用;

interface

• 接口内成員變量必須定義初始化;

• 接口内的成員方法隻能是方法原型,不能有方法體;

• 接口内的成員變量和方法隻能是public的;

• 實作接口的類必須全部實作接口的方法;

final

• 可以修飾成員變量、非抽象類、非抽象類成員方法、方法參數;

• final方法不能被子類重寫,但是可以被繼承;

• final類不可以被繼承,final類内部的方法也無法被繼承;

• final修飾的變量隻能被指派一次,指派之後不能被修改;

• final不能修飾構造方法;

• final的參數:隻能被使用,不能修改該參數的值

static

• 可以修飾成員變量和成員方法,但是不能修飾類以及構造方法;

• static修飾的成員變量和成員方法獨立于這個類,被類的所有執行個體共享;

• static修飾的類和成員方法可以通過類名直接通路,也可以通過執行個體來通路;

• private修飾的static變量和static方法隻能在聲明的本類方法及靜态塊内通路,但不能使用this方法,因為this是非靜态變量。

static+final同時修飾

• 該變量或者方法可以簡單了解為全局常量;

• 對于變量一但給值就不可以修改,可以通過類名進行通路;

• 對于方法,表示不可覆寫,可以通過類名直接通路;

switch是否可以使用string來作為參數?

• Java 5之後switch可以引用enum、byte、short、char、int作為參數

Java 7 之後引入string

八、Object有哪些公用方法

• clone方法:保護方法,實作對象的淺複制,隻有實作了Cloneable接口才可以調用該方法,否則會抛出CloneNotSupportException異常。

• getClass()方法:final方法,獲得運作時類型

• toString()方法:不用多說了( ̄▽ ̄)~*

• finalize方法:釋放資源使用,很少使用;

• equals()方法:隻有在Object中equals方法和==是一樣,其他時候兩者不一樣。

• hashCode()方法:該方法用于哈希值的查找,可以減少equals方法的使用,提高代碼運作效率。

• wait()方法:wait方法就是使目前線程等待該對象的鎖,目前線程必須是該對象的擁有者,也就是具有該對象的鎖。wait方法一直等待,直到獲得鎖或者被中斷。調用該方法後目前線程進入睡眠狀态,直到發生以下事件:

①、其他線程調用了該對象的notify方法;

②、其他線程調用該對象的notifyAll方法;

③、其他線程調用了interrupt方法中斷該線程;

④、時間間隔到了。

八、int和integer的差別

• int是基本資料類型

• integer是int的封裝類

• int和integer都可以表示某一個數值

• int和integer不能夠互用,因為他們兩種不同的資料類型

九、string是否可以被繼承

• 不可以,因為String為final類

十、常量 final String str=“ab”可不可以程式設計“abc”,為什麼?

• 不可以,因為使用final修飾的變量不可改變

十一、String、StringBuffer、StringBuilder之間的差別

• 三者執行效率為:StringBuilder>StringBuffer>String

String字元串為常量,不可改變;

StringBuilder字元串變量(非線程安全)

StringBuffer字元串變量(線程安全)

String類型進行改變的時候其實等同于生成 一個新的String對象,然後指針指向新的String對象。會導緻jvm的gc開始工作導緻速度變慢;

StringBuffer一個類似于String的字元串緩沖區,但是不能修改。但是可以通過某些方法來改變該序列的長度和内容。

StringBuffer上主要操作的是append方法和insert方法,append方法是将任意類型的資料轉換成字元串添加到緩存區末端,而insert方法是制定的添加字元串。

String s=new Sting(“abc”);new了幾個對象?

兩個

String源碼分析

• String對象是不可變類型,傳回類型為String的String方法每次傳回的都是新的String對象,除了某些方法的某些特定條件傳回自身。

String對象的三種比較方式:

1、==記憶體比較:直接對比兩個引用所指向的記憶體值,精确簡潔直接明了。

2、equals字元串值比較:比較兩個引用所指對象字面值是否相等。

3、hashCode字元串數值化比較:将字元串數值化。兩個引用的hashCode相同,不保證記憶體一定相同,不保證字面值一定相同。

十二、内部類

靜态内部類和非靜态内部類的差別

• 靜态内部類不持有外部類的引用,非靜态内部類持有外部類的引用

• 靜态内部類可以有靜态成員,非靜态内部類不能持有靜态成員;

• 靜态内部類隻能通路外部類的靜态成員和靜态方法,非靜态内部類可以通路外部類的所有成員和方法;

• 執行個體化一個靜态内部類不需要外部類的執行個體;執行個體化一個非靜态内部類需要通過外部類的執行個體生成内部類的執行個體;

• 調用靜态内部類的方法或變量可以通過類名直接調用。

為什麼内部類擁有外部類的所有元素的通路權?

• 内部類對象隻能在與其外部類對象關聯的情況下才能被建立,建構内部類需要一個外部類的引用,内部類正是通過這個引用去通路外部類的。

内部類的種類

• 成員内部類、方法内部類、匿名内部類、靜态内部類

内部類的作用

• 内部類可以 用多個執行個體,每個執行個體都有自己的狀态資訊,并且與其他外圍對象的資訊互相獨立;

• 單個外圍類中,可以讓多個内部類以不同的方式實作同一個接口,或者繼承同一個類。

• 建立内部類對象的時刻并不依賴于外圍類對象的建立;

• 内部類是一個獨立的實體;

• 内部類提供了更好的封裝,除了外圍類,其他類都不能進行通路

靜态内部類、内部類、匿名内部類,為什麼内部類會持有外部類的引用?持有的引用是this?還是其它?

• 因為内部類的産生依賴于外部類,持有的引用是“類名.this”;

十三、抽象類和接口

抽象類的總結

• 抽象類不能被執行個體化

• 抽象類内部不一定包含抽象方法,有抽象方法的類一定是抽象類

• 抽象類内部的方法隻是聲明,不包含方法體,就是不給出具體的實作也就是方法的具體功能;

• 構造方法、類方法不能聲明為抽象方法

• 抽象類的子類必須給出抽象類中抽象方法的具體實作,除非這類也是抽象類。

接口與類的差別

• 接口不能用于執行個體化對象;

• 接口沒有構造方法;

• 接口内的方法必須是抽象方法;

• 接口不能包含成員變量,除非是static 和 final修飾的;

• 接口不是被類繼承而是被類實作;

• 接口支援多重繼承

接口特性

• 接口中的每一個方法都是隐式抽象的,接口中的方法會被隐式的指定為 public abstract;

• 接口内可以含有變量,但是接口中的變量會被隐式的指定為public static final 變量;

• 接口中的方法是不能在接口中實作的,隻能由實作接口的類來實作接口内部的方法;

抽象類與接口的差別

• 抽象類内部的方法可以有方法體,但是接口内部的方法不行;

• 抽象類中的成員變量可以是各種類型的,而接口的成員變量隻能是public static final類型的;

• 接口中不能有靜态代碼塊以及靜态方法,而抽象類内可以有靜态代碼塊和靜态方法,java8新特性允許接口記憶體在靜态方法;

• 一個類隻能繼承一個抽象類,而一個類可以實作多個接口;

• 接口中不能有構造函數和main方法;抽象類内可以有;

• 接口是被實作;抽象類類是被繼承。

接口的意義

• 提供一個規範,要求類必須實作指定的方法;

• 解決Java中的單繼承問題,可以用接口來實作多繼承的功能,簡單化多重繼承中繼承樹的複雜程度;

• 增強程式的擴充性。

抽象類的意義

• 為其子類提供一個公共的類型,封裝子類中的重複内容,定義抽象方法,子類雖然有不同的實作,但是定義是一緻的。

十四、接口是否可繼承接口? 抽象類是否可實作(implements)接口? 抽象類是否可繼承實體類(concreteclass)?

• 接口可以繼承接口,使用關鍵字extends

• 抽象類可以實作接口。AbstractCollection類就實作了Collection接口;

• 抽象類可以繼承實體類。

十五、abstract的方法是否可以同時是static,是否可同時是native,是否可同時是final?

• 都不行

abstract的方法不可以是static的,因為抽象的方法是要被子類實作的,而static與子類沒有關系!

native方法表示該方法要用另外一種依賴平台的編輯語言實作的,不存在者被子類實作的問題,是以,他也不能是抽象的,不能與abstract混用。

十六、final關鍵字

• final修飾的類不能被繼承,final類内部的方法都被隐式的指定為final方法;

• final修飾的方法的原因:明确禁止該方法在子類中被覆寫;

• 修飾變量:①、基本資料類型的變量,數值一旦被初始化便不能更改;②、引用類型變量,初始化之後不能再讓其指向另一個對象

• final修飾的引用形變量其指向的對象内容可變。

十七、finally final finalize的作用?

• final用于聲明屬性、方法和類;finally是異常處理語句結構的一部分,表示總是執行;finalize是Object類的一個方法。

十八、Collection與Collections的差別

• Collection是一個接口,它是Set、List等容器的父接口;Collections是一個工具類,提供了一系列的靜态方法來輔助容器操作,這些方法包括對容器的搜尋、排序、線程安全化等

十九、Set和List的差別

• Set接口存儲的是無序的、不重複的資料;List接口存儲的是有序的、可以重複的資料;

• Set檢索效率低,删除和插入效率高、删除和插入不會引起元素位置變化,實作類有HashSet、TreeSet。

List檢索效率高、插入和删除效率低,因為引起其他元素的位置變化,實作類有ArrayList、LinkdList、Vector。

二十、hashCode方法的作用

• hashCode方法主要作用是為了配合基于散列的集合一起正常的運作,這樣的散列的集合包括hashSet、HashMap、HashTable。

why?考慮到一種情況就是當向集合内部插入對象時,不允許有重複對象。

當向集合内部添加新對象時,首先調用對象的hashCode方法得到對象的哈希值,實際上在HashMap的具體實作中會用到一個table儲存已經存進去的對象的hashCode,如果table内沒有該哈希值,它就可以直接存進去不用進行比較了,如果存在則調用它的equals方法與新元素進行比較如果相同的話就不存了,不同的話存進去。這樣的話就大大降低了equals方法的使用,提高了程式的運作效率。

如果equals方法得到的結果為true,則兩個對象的hashcode值必定相等;

如果equals方法得到的結果為false,則兩個對象的hashcode值不一定不同;

如果兩個對象的hashcode值不等,則equals方法得到的結果必定為false;

如果兩個對象的hashcode值相等,則equals方法得到的結果未知。

多線程環境中安全使用集合API

• 在集合API中最初設計的Vector和HashTable是多線程安全的。例如Vector,用來添加和删除元素的方法是同步的,如果隻有一個線程與其進行互動,那麼要求擷取和釋放對象鎖是一種浪費,另外在不必要的時候濫用同步化也可能帶來死鎖現象。是以集合的本質上是非多線程安全的,當與多線程互動時,為了使它多線程安全,必須采取額外措施。

HashMap的實作原理

• HashMap底層就是一個數組結構,數組中的每一項又是一個連結清單。當建立一個HashMap的時候就會初始化一個數組。

二十一、容器之間的差別

HashMap與HashTable的差別

• 繼承不同 HashMap繼承自Dictionary 、HashTable繼承自AbstractMap

• HashTable中的方法是同步的,HashMap的方法不是同步的;

• HashTable内的key和value都不允許出現null,HashMap中的null可以作為鍵,這樣的鍵隻能有一個;可以有一個或者是多個鍵對應的值是null;

• 兩個周遊方式不同

• HashTable直接使用對象的哈希值、HashMap重新計算哈希值;

ArrayMap和HashMap的差別

• 存儲方式不同。ArrayMap使用的兩個數組進行存儲,HashMap使用連結清單進行存儲;

• 擴容處理方法不一樣。ArrayMap使用System.arraycopy()copy數組,而 HashMap 使用 new HashMapEntry 重新建立數組。

• ArrayMap 提供了數組收縮的功能,在clear或remove後,會重新收縮數組,節省空間。、

4、ArrayMap 采用二分法查找。

ArrayList Vector LinkedList 三者的差別

• 三者都實作了List 接口;但 LinkedList 還實作了 Queue 接口。

• ArrayList 和 Vector 使用數組存儲資料;LinkedList 使用雙向連結清單存儲資料。

• ArrayList 和 LinkedList 是非線程安全的;Vector 是線程安全的。

• ArrayList 資料增長時空間增長50%;而 Vector 是增長1倍;

• LinkedList 在添加、删除元素時具有更好的性能,但讀取性能要低一些。

• LinkedList 與 ArrayList 最大的差別是 LinkedList 更加靈活,并且部分方法的效率比 ArrayList 對應方法的效率要高很多,對于資料頻繁出入的情況下,并且要求操作要足夠靈活,建議使用 LinkedList;對于數組變動不大,主要是用來查詢的情況下,可以使用 ArrayList。

二十二、記憶體相關知識點

Java程式運作時的記憶體配置設定政策有三種,分别是靜态配置設定、棧式配置設定和堆式配置設定。對應的就是靜态存儲區、棧區、堆區。

• 靜态存儲區:主要存放靜态資料、全局static資料和常量。這塊記憶體在程式編譯時已經配置設定确定,并且在程式整個運作期間都會存在;

• 棧區:當方法執行時,方法體内部的局部變量都在棧上建立,并在方法執行結束時這些局部變量持有的記憶體将會自動釋放

• 堆區:又稱之為動态記憶體配置設定,通常指程式運作時直接new出來的記憶體,也就是對象的執行個體。這部分記憶體不使用時會由垃圾回收器進行回收

棧與堆的差別

• 棧:在方法體内定義的一些基本類型變量和對象引用的變量都是在方法的棧記憶體中進行配置設定的。

• 堆:用來存放所有由new建立的對象和數組。在堆中配置設定的記憶體,将由Java垃圾回收器自動管理。當在堆中産生一個數組或者對象後,還可以在棧中定義一個特殊的變量,這個變量取值等于數組或者對象在堆記憶體中的首位址,這個特殊變量就是引用變量。可以通過引用變量來通路堆中的對象或者是數組。

結論:

• 局部變量的基本資料類型和引用存儲于棧中,引用對象的實體存儲于堆中。

• 成員變量存儲于堆中。因為他們屬于類,類對象終究是要被new出來使用的。

• 棧存取速度僅次于寄存器,存儲效率比堆高,可以共享存儲資料,但是其中資料大小和生存期必須在運作前确定。

• 堆是運作時可動态配置設定的資料區,從速度看比棧慢,堆裡面的資料不共享大小和生存期都可以在運作時再确定。

Java如何管理記憶體?

• Java的記憶體管理就是對象的記憶體配置設定和釋放問題。在Java中程式員通過new來為每個對象在堆中申請記憶體空間,所有的對象都在堆中配置設定空間。對象的釋放由GC決定和執行,由于記憶體配置設定和釋放由兩條線來完成簡化了程式員的工作,但是增加了jvm的工作。這也是Java運作速度較慢的原因之一。因為GC為了能夠正确釋放對象,GC必須監控每一個對象的運作狀态,監視對象的目的是為了更加準确及時的釋放對象,而釋放對象的原則是該對象不被引用。

為了更好了解 GC 的工作原理,我們可以将對象考慮為有向圖的頂點,将引用關系考慮為圖的有向邊,有向邊從引用者指向被引對象。另外,每個線程對象可以作為一個圖的起始頂點,例如大多程式從 main 程序開始執行,那麼該圖就是以 main 程序頂點開始的一棵根樹。在這個有向圖中,根頂點可達的對象都是有效對象,GC将不回收這些對象。如果某個對象 (連通子圖)與這個根頂點不可達(注意,該圖為有向圖),那麼我們認為這個(這些)對象不再被引用,可以被 GC 回收。

Java使用有向圖的方式進行記憶體管理,可以消除引用循環的問題,例如有三個對象,互相引用,隻要它們和根程序不可達的,那麼GC也是可以回收它們的。這種方式的優點是管理記憶體的精度很高,但是效率較低。另外一種常用的記憶體管理技術是使用計數器,例如COM模型采用計數器方式管理構件,它與有向圖相比,精度行低(很難處理循環引用的問題),但執行效率很高。

什麼是Java記憶體洩漏?

• 這些對象具有如下兩個特點。①、這些對象是可達的即在有向圖記憶體在通路與其相連;②、這些對象是無用的,即以後程式不會再使用這些對象。如果對象滿足這兩個條件,那麼我們就可以認為這些對象為Java中的記憶體洩漏,但是這些對象不會被GC回收,然而占用記憶體。

Java的記憶體回收機制

• 所有的程式設計語言的記憶體配置設定方式都需要傳回配置設定記憶體的真實位址,也就是傳回一個指針到記憶體塊的首位址,Java中對象是采用new或者反射的方式建立的,這些對象的建立都是在堆中配置設定的,所有對象的回收都是由Java虛拟機通過垃圾回收機制完成的。GC為了能夠準确的釋放對象會監控每一個對象的運作狀況(申請、引用、被引用、指派),Java使用由向意圖的方法進行管理記憶體,實時監控對象是否可以到達,如果不可到達則進行回收。這樣也可以消除引用循環的問題。

判斷記憶體空間是否符合垃圾回收标準?

• ①、對象被賦予了空值null,再也沒有調用過;

②、給對象賦予了新值,這樣重新配置設定了記憶體空間;

Java記憶體洩漏引起的原因

• 記憶體洩漏的定義:無用的對象持續占有記憶體或無用對象的記憶體得不到及時釋放,進而導緻記憶體空間的浪費。嚴重的記憶體洩漏會導緻記憶體溢出(OOM)。

記憶體洩漏的根本原因:

長生命周期的對象持有短生命周期對象的引用就很可能發生内洩漏。

具體導緻記憶體洩漏的原因:

• 靜态集合引起的記憶體洩漏:HashMap、Vector等的使用容易出現記憶體洩漏,這些靜态變量的生命周期和應用程式一緻,他們所引用的所有的對象Object也不能被釋放,因為他們也将一直被Vector等引用着。

解決方法:及時将集合對象設定為null。

• 監聽器:在釋放對象的時候忘記删除對象的監聽器,進而增加了記憶體洩漏的機會。

解決方法:及時删除監聽器

• 各種連接配接:比如資料庫連接配接(dataSourse.getConnection()),網絡連接配接(socket)和io連接配接。

解決方法:顯示調用close()方法,否則是不會自動被GC回收的。

• 内部類和外部子產品的引用:解決方法:細節,時刻提醒提供相應方法去除引用

• 單例模式:不正确使用單例模式導緻。單例對象在初始化後将在jvm的整個生命周期中存在,如果單例對象持有外部類的引用,那麼這個對象将不能被JVM正常回收導緻記憶體洩漏。

Java記憶體回收機制,GC 垃圾回收機制,垃圾回收的優點和原理,并說出3種回收機制。

優點:

• Java語言顯著的特點就是引入了垃圾回收機制,它使程式員在編寫程式時不再考慮記憶體管理問題,直接解放了程式員的大腦;

• 由于有了垃圾回收機制,Java對象不再有“作用域的概念”,隻有引用對象才有“作用域”的概念;

• 垃圾回收機制有效的防止了記憶體洩漏,可以有效的使用空閑的記憶體;

• 垃圾回收器通常作為一個單獨的低級别的線程運作,在不可預知的情況下進行對記憶體堆中的已經死亡的或者長時間沒有用過的對象進行清除和回收。

• 程式員實時的對某個對象調用垃圾回收器進行垃圾回收。

垃圾回收機制:

• 分帶複制垃圾回收、标記垃圾回收、增量垃圾回收

垃圾回收算法

• 引用計數算法:缺點是無法處理循環引用問題

• 标記-清除算法:标記所有從根節點開始的可達的對象。缺點是會造成空間不連續,導緻不容易配置設定記憶體

• 标記-壓縮算法:标記清除算法的更新版,原理:清除未标記對象時還将所有的存活對象壓縮到記憶體的另一端,之後清理邊界所有的空間既避免碎片産生,又不需要兩塊同樣大小的記憶體塊。适用于老年代。

• 複制算法:将記憶體空間分為兩塊,每次将正在使用的記憶體中存活對象複制到未使用的記憶體塊中,之後清除正在使用的記憶體塊。算法效率高,但是代價是系統記憶體折半。适用于新生代。

• 分代

Java虛拟機的特性

• Java語言的一個非常重要的特性就是與平台無關性,而Java虛拟機是實作這一特性的關鍵。一般的進階語言在不同的平台上運作,至少需要編譯成不同的目标代碼。然而引入Java語言虛拟機之後,Java在不同平台運作的時不需要重新編譯,Java語言使運作時Java虛拟機屏蔽了與具體平台相關的資訊,使得Java語言編譯程式隻需生成在Java虛拟機上運作的目标代碼(位元組碼),就可以在多種平台上不加修改地運作。Java虛拟機在執行位元組碼時,把位元組碼解釋成具體平台上的機器指令執行。

哪些情況下的對象會被垃圾回收器回收掉

• Java的回收機制最基本的做法就是分代回收。記憶體中的區域被劃分為不同的世代,對象根據其存活時間被儲存在在不同的世代内。一般被分為三個世代分别為:年輕、年老、永久。記憶體配置設定是發生在年輕世代内,對于不同的世代可以才用不同的垃圾回收算法。基于這一點針對世代的垃圾回收算法就很有針對性了。

二十三、多線程

一個線程的生命周期

• 建立狀态:使用new關鍵字和Thread類或其子類建立一個線程對象,該線程對象就處于建立狀态。它保持這個狀态直達程式start()這個程式。

就緒狀态:當線程對象調用start()方法之後,該線程就進入就緒狀态。就緒狀态的線程處于就緒隊列中,等待JVM裡線程排程器的排程。

運作狀态:如果就緒狀态的線程擷取CPU資源,就可以執行run()方法,此時的線程處于運作狀态。處于運作時的狀态最為複雜,可以變為阻塞狀态、就緒狀态、死亡狀态。

阻塞狀态:線程執行了sleep(睡眠)、suspend(挂起)等方法,失去所占用資源之後,該線程從運作狀态進入阻塞狀态。在睡眠時間已到或獲得裝置資源後可以重新進入就緒狀态。

阻塞狀态可以分為三種:①、等待阻塞:運作狀态中的線程執行wait()方法;②、同步阻塞:線程在擷取synchronized同步鎖失敗; ③、其他阻塞

死亡狀态:一個運作時狀态的線程完成任務或者其他終止條件發生時,該線程切換到終止狀态。

線程與程序的差別

• 程式是一段靜态的代碼,一個程序可以有一個或者多個線程,每個線程都有唯一的辨別

差別:①、程序大體分為資料區、代碼區、棧區、堆區。多個程序之間的内部資料和狀态是完全獨立的;線程共享程序的資料區、代碼區、堆區、隻有棧區是獨立的。是以線程切換代價小于程序切換代價。

②、一個程式至少有一個程序,一個程序至少有一個線程;

③、線程劃分尺度小于程序、多線程的程式并發高;

④、程序在執行過程中擁有獨立的記憶體單元,而多線程共享記憶體,進而極大地提高了程式運作效率;

什麼導緻線程阻塞

• 調用sleep(毫秒),使得線程進入睡眠狀态;

• 調用suspend()暫停線程執行。除非線程收到resume()消息否則傳回不可運作狀态;

• 調用wait()方法暫停線程執行。除非調用notify()或者notifyAll()消息,否則不可運作;

• 線程正在等候IO操作的完成;

• 線程試圖調用另一個對象的同步方法,但是那個對象處于鎖定狀态暫時無法使用。

多線程的實作方法

• 實作Runnable接口;

• 繼承Thread類本身;

• 通過callable和Future建立線程(Java 5 之後出現);

實作Runnable接口相比繼承Thread類有如下優勢

• 可以避免Java單繼承的特性帶來的局限性;

• 增強程式的健壯性,代碼可以被多個程式共享,代碼與資料是獨立的;

• 适合多個相同程式代碼的線程區處理同一資源的情況

同步有幾種實作方法,都是什麼?

• 兩種 synchronized wait和notify

鎖的等級

• 方法鎖、對象鎖、類鎖

同步和異步的差別?

• 同步:A線程請求某資源時,B線程占用該資源,則A隻能等待下去

• 異步:A線程請求某個資源時,B線程占用該資源,則A無需等待可以請求。

sleep和wait的差別

• ①、sleep:線程休息;wait:線程挂起。

②、sleep方法屬于Thread類的方法;wait屬于Object類方法

③、sleep:sleep正在執行的線程主動放出cpu,指定時間後cpu回到該線程繼續向下執行。注意sleep隻是讓出CPU而并不是釋放同步資源鎖;

wait:目前線程讓自己暫時退讓出同步資源鎖,以便其他正在等待該資源的線程得到該資源而運作,隻有調用notify方法之後調用wait方法的線程才會接觸wait狀态進而參與競争同步資源鎖,進而執行

Java線程池線程同步

• 同步操作産生的原因:當多個線程同時通路對象并要求操作相同資源時,分割了原子操作就有可能出現資料的不一緻或資料不完整情況,為了避免發生這種情況,我們會采取同步機制。

synchronized修飾符實作的同步機制叫做互斥鎖機制。

互斥鎖:每個對象都有一個鎖标記,當線程擁有鎖機制的時候才可通路這個資源,沒有鎖标記便進入鎖池。任何一個對象系統都會建立一個互斥鎖,這個鎖為了配置設定給線程,防止打斷原子操作,每個對象鎖隻能配置設定給一個線程。

當一個線程進入一個對象的一個synchronized方法後,其他線程是否可進入此對象的其他方法?

• 可以調用此對象的其他非synchronized方法;

• 可以調用此對象synchronized static方法;

線程加鎖的作用:

• 互斥行為和記憶體可見性

記憶體可見性定義

• 某個線程正在使用對象狀态而另一個線程在同時修改該狀态,當該狀态被修改之後可以立刻被其他線程感覺該對象的狀态改變了

Atomic、volatile、synchronized差別

• synchronized是解決多線程共享資源的問題同步機制采用了“以時間換取空間”的做法,通路串行化、對象共享化。同步機制是提供一份變量,讓所有線程都可以通路。

• Atomic是通過原子操作指令+Look-Free完成,進而實作非阻塞的并發問題。

• Volatile是為了多線程資源共享問題解決了部分需求,在非依賴自身的操作的情況下,對變量的改變将對任何線程可見。

• ThreadLocal是為了解決多線程資源共享問題,用來提供線程内局部變量,省去參數傳遞這個不必要的麻煩,做到了“以空間換取時間”的方式。

• 如果是一個類的多個對象想公用對象内部的一個變量,而不想使用static,可以使用淺複制的方式。

并發程式設計實作記憶體可見性的方式- - - ->加鎖和volatile變量

volatile變量

• volatile變量是一種脆弱的同步機制,由于通路volatile變量時不會執行加鎖操作,是以也就不會執行線程阻塞,是以volatile變量是一種比synchronized關鍵字輕量級的同步機制;

• 從記憶體可見度角度來看,寫入volatile操作相對于退出同步代碼塊,讀取volatile變量相對于進入同步代碼塊;

• 代碼中過度使用volatile編寫來控制可見性,通常會比使用鎖更加脆弱和難以了解。

• volatile隻能保證可見性,不能保證原子性。

使用volatile的情景:

• 對變量的讀寫操作不依賴變量的目前值,或者可以保證隻有單個線程更新變量的值;

• 該變量沒有包含在具有其他變量的不變式中。

守護線程

• 使用者線程:運作在前台的線程;

• 守護線程:運作在背景的線程,為其他前台線程的運作變量提供便利服務的,僅當非守護線程運作時,才需要守護線程例如垃圾回收線程;

可以人為建立守護線程,通過Thread的setDeamon(ture)方法設定目前線程為守護線程。

禁止在守護線程内建立耗時操作,比如IO操作

setDeamon(ture)方法必須在調用線程的start()方法之前設定,否則會抛出IllegalThreadStateException異常。

守護線程内産生的新線程也是守護線程。

死鎖

• 線程A持有互斥鎖lock1,線程B持有互斥鎖lock2。當線程A持有lock1時,師徒擷取lock2,因為B持有lock2,是以線程A會阻塞等待線程B對lock2的釋放。如果此時B在持有lock2的時候也試圖擷取lock1,因為A持有lock1,是以線程B會阻塞等待A對lock1的釋放。二者都在等待對方所持有的鎖釋放,而二者卻又都沒釋放自己所持有的鎖,這時二者會一直阻塞下去,這種情況為死鎖現象。

如何避免死鎖現象産生

• 隻在必要的最短時間内持有鎖,可以考慮使用同步語句塊代替整個同步方法;

• 盡可能不編寫在同一時刻需要持有多個鎖的代碼,如果不可避免則確定線程持有第二個鎖的時間盡量短;

• 建立一個大鎖來代替若幹個小鎖。

可重入内置鎖,使用wait/notify/notifyAll實作線程間通信

• 可以通過調用Object對象的wait()方法和notify()方法或者說notifyAll方法來實作線程間通信。線上程中調用wait()方法,将阻塞等待其他線程的通知,線上程中調用notify()方法或notifyAll方法,将通知其他線程wait()方法處傳回。

notify()、notifyAll()、wait()、wait(long)和wait(long,int),他們都被聲明為final,是以在子類中不能覆寫任何一個方法。

如果調用wait()時,沒有持有适當的鎖,則抛出IllegalMonitorStateException;

如果調用notify()時沒有持有适當的鎖,也會抛出IllegalMonitorStateException

如果線程調用了對象的wait方法,那麼線程便會處于對該對象的等待池中,等待池中的線程不會去競争該對象的鎖;

當線程調用了對象的notiAll方法或者notify方法,被喚醒的線程便會進入該對象的鎖池中,鎖池中的線程會去競争該對象鎖;

優先級高的線程競争到對象鎖的機率大,如果沒有競争到對象鎖那麼他會留在鎖池中隻有線程再次調用wait方法的時候,它才會再次進入等待池中。競争到對象鎖的線程則會繼續向下執行,直到執行完了synchronized代碼塊,才會釋放掉對象鎖,此時鎖池内線程會繼續競争該對象鎖。

Error和Exception的差別

• Error是Throwable的子類用于标記嚴重錯誤,合理的應用程式不可以用try/catch這種錯誤。絕大多數錯誤都是非正常的,不應該出現的。

Exception是Throwable的子類,用于訓示合理的程式想去catch的條件,即它僅僅是一種程式運作的條件,而非嚴重錯誤。

checked exceptions: 通常是從一個可以恢複的程式中抛出來的,并且最好能夠從這種異常中使用程式恢複。比如FileNotFoundException, ParseException等。

unchecked exceptions:就是程式中的bug

Java中的異常處理機制的簡單原理和應用

• Java中通過面向對象的方式,把各種異常進行了分類,并且提供了良好的接口,在Java中每一個異常就是一個的對象。當一個方法抛出異常的時候便抛出了一個異常對象,該對象内包含異常的資訊,調用這個對象的方法就可以捕獲到這個異常并進行處理。

try/catch的執行過程

• 一般情況由try來執行一段程式,如果出現異常,系統則會抛出一個異常,此時程式員可以通過他的類型進行捕捉(catch操作),或在最後(finally)由預設處理器來進行處理。

用try來指定一塊預防所有“異常”的程式。緊跟在try程式後面,應包含一個catch子句來指定你想要捕捉的“異常”的類型。throw語句用來明确地抛出一個“異常”。throws用來标明一個成員函數可能抛出的各種“異常”。Finally為確定一段代碼不管發生什麼“異常”都被執行一段代碼。

try{ return} catch{} finally{}; return 還是 finally 先執行?

• 任何執行try或者catch内的return語句之前,都會先執行finally語句,如果finally存在的話。

如果finally中有return語句,那麼程式就return了,是以finally中的return是一定會被return的

在try語句中,在執行return語句時,要傳回的結果已經準備好了,就在此時,程式轉到finally執行了。在轉去之前,try中先把要傳回的結果存放到不同于x的局部變量中去,執行完finally之後,在從中取出傳回結果,是以,即使finally中對變量x進行了改變,但是不會影響傳回結果。

二十四、序列化

對象Object讀寫是哪兩個流

• ObjectInputStream ObjectOutputStream

什麼是Java序列化,如何實作序列化?

• 序列化就是一種用來處理對象流的機制,所謂對象流就是講對象的内容流化。可以對流化後的對象進行讀寫操作,也可将流化後的對象傳輸于網絡之間。序列化是為了解決在對對象流進行讀寫操作時所引發的問題。

實作:Serializable接口和Parcelable接口

Serializable和Parcelable的差別

• 兩者最大的差別在于存儲媒介的不同,Serializable使用IO讀寫存儲在硬碟上,而Parcelable是直接在記憶體中讀寫,很明顯記憶體的讀寫速度通常大于IO讀寫,是以在Android中通常優先選擇Parcelable。

二十五、反射

反射機制

• 運作狀态中,對于任意一個類,都能夠知道這類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意一個方法和屬性;這種動态擷取的資訊以及動态調用對象的方法的功能被稱為Java語言的反射機制。

反射機制的用處

• 觀察或者操作應程式的運作時行為;

• 調試或測試程式,因為可以直接通路方法、構造函數和成員字段;

• 通過名字調用不知道的方法并使用該資訊來建立對象和調用方法。

二十六、泛型

泛型的優缺點

優點:

• 使用泛型可以最大限度的重用代碼、保護類的安全以及提高性能。

缺點:

• 性能上不如數組快

二十七、網絡

網絡參考模型

• 四層分法:應用層、運輸層、網絡層、網絡接口層

七層分法:應用層、表示層、會話層、運輸層、網絡層、資料鍊路層、網絡層

Tcp與UDP的差別

• tcp是面向有連結的通信服務;udp是面向無連接配接的通信服務

• tcp傳輸可靠;udp傳輸不可靠、會丢包

• tcp保證資料順序;udp不保證資料順序;

• tcp無邊界;udp有邊界;

• tcp傳輸速度慢;udp傳輸速度快;

• tcp面向位元組流;udp面向封包

• tcp一對一;udp一對一或者一對多

• TCP:面向連接配接、傳輸可靠(保證資料正确性,保證資料順序)、用于傳輸大量資料(流模式)、速度慢,建立連接配接需要開銷較多(時間,系統資源)。

UDP:面向非連接配接、傳輸不可靠、用于傳輸少量資料(資料包模式)、速度快。

get和post請求的差別

• get請求能被緩存;post請求不能被緩存

• get儲存浏覽器浏覽記錄;post不儲存浏覽器浏覽記錄;

• get請求的url可以儲存為浏覽器書簽;post的不可以;

• get請求長度有限制;post請求長度無限制

• get請求主要用以擷取資料;post送出資料

• post請求相對安全

Tcp的三次握手

• 用戶端發送:SYN=1,SEQ=X,端口号

• 服務的回複:SYN=1,ACK=X+1,SEQ=Y

• 用戶端發送:ACK=Y+1,SEQ=X+1

tcp的四次揮手

• A向B提出停止連接配接請求,FIN = 1

• B收到,ACK = 1

• B向A提出停止連接配接請求,FIN = 1

• A收到,ACK = 1

• 優點:穩定可靠

缺點:傳輸慢效率低、容易受攻擊

udp傳輸的優缺點

• 優點:傳輸速率快,較安全

缺點:不可靠、不穩定

用udp進行通信如何知道目标機是否獲得到資料包?

• 仿TCP的做法,每發一個UDP包,都在裡面加一個SEQ序号,接收方收到包後,将SEQ序号回複給發送方。如果發送方在指定時間以内沒有收到回應,說明丢包了。

為什麼udp比tcp快?

• 無需三次握手

• tcp有擁塞控制,控制流量等機制

為什麼tcp比udp可靠?

• TCP是面向有連接配接的,建立連接配接之後才發送資料;而UDP則不管對方存不存在都會發送資料。

• TCP有确認機制,接收端每收到一個正确包都會回應給發送端。逾時或者資料包不完整的話發送端會重傳。UDP沒有,是以可能丢包。

什麼時候使用Tcp?

• 當對網絡通信品質有要求的時候 例如 浏覽器

什麼時候使用udp?

• 當對網絡通訊品質要求不高的時候,要求網絡通訊速度能盡量的快,這時就可以使用UDP

TCP無邊界,UDP有邊界

• TCP:用戶端分多次發送資料給伺服器,若伺服器的緩沖區夠大,那麼伺服器端會在用戶端發送完之後一次性接收過來,是以是無邊界的;

udp:用戶端每發送一次,伺服器端就會接收一次,也就是說發送多少次就會接收多少次,是以是有邊界的。

scoket程式設計的步驟

• 建立Socket;

• 打開連接配接到Socket的輸入/出流;

• 按照一定的協定對Socket進行讀/寫操作;

• 關閉Socket.(在實際應用中,并未使用到顯示的close,雖然很多文章都推薦如此,不過在我的程式中,可能因為程式本身比較簡單,要求不高,是以并未造成什麼影響。)

繼續閱讀