Java基礎回顧——重難點易錯點【2】
看過才明白,我自己的基礎漏洞還真是不少,不知道大牛們是不是跟我一樣也有過這個階段q.q
在Java中修飾符總共有一下幾種:
1.訪問控制修飾符
分別有:public private protected,預設
2.其它修飾符
分別有:abstract,final,static,native,synchronized,transient,volatile等
(1)對於形式引數只能用final修飾符,其它任何修飾符都會引起編譯器錯誤 。但是用這個修飾符也有一定的限制,就是在方法中不能對引數做任何修改。
(2)不過一般情況下,一個方法的形參不用final修飾。只有在特殊情況下,那就是:方法內部類。
(3)一個方法內的內部類如果使用了這個方法的引數或者區域性變數的話,這個引數或區域性變數應該是final。在接口裡面的變數預設都是public static final 的,它們是公共的,靜態的,最終的常量.相當於全域性常量,可以直接省略修飾符。
實現類可以直接訪問介面中的變數 。類內部靜態變數
示例:public class HasStatic {// 1 private static int x = 100;// 2 public static void main(String args[]) {// 3 HasStatic hsl = new HasStatic();// 4 hsl.x++;// 5 HasStatic hs2 = new HasStatic();// 6 hs2.x++;// 7
結果為:“x=”102
由於x是static的,存貯在類內,而不是物件內,所以++、–操作的是同一個變數。類變數既可以通過類來返回,也可以通過類的物件來訪問。但通過類的物件來訪問類變數時,實際不是訪問該物件所擁有的變數,因為當系統建立該類的物件時,系統不會再為類變數分配記憶體,也不會再次對類變數進行初始化,也就是說,物件根本不擁有對應類的類變數,通過物件訪問類變數只是一種假象,通過物件訪問的依然是該類的類變數。幾個結構模式定義
結構型模式是描述如何將類物件結合在一起,形成一個更大的結構,結構模式描述兩種不同的東西:類與類的例項。故可以分為類結構模式和物件結構模式。
在GoF設計模式中,結構型模式有:
1.介面卡模式 Adapter
介面卡模式是將一個類的介面轉換成客戶希望的另外一個介面。介面卡模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。
兩個成熟的類需要通訊,但是介面不同,由於開閉原則,我們不能去修改這兩個類的介面,所以就需要一個介面卡來完成銜接過程。
2.橋接模式 Bridge
橋接模式將抽象部分與它的實現部分分離,是它們都可以獨立地變化。它很好的支援了開閉原則和組合鋸和複用原則。實現系統可能有多角度分類,每一種分類都有可能變化,那麼就把這些多角度分離出來讓他們獨立變化,減少他們之間的耦合。
3.組合模式 Composite
組合模式將物件組合成樹形結構以表示部分-整體的層次結構,組合模式使得使用者對單個物件和組合物件的使用具有一致性。
4.裝飾模式 Decorator
裝飾模式動態地給一個物件新增一些額外的職責,就增加功能來說,它比生成子類更靈活。也可以這樣說,裝飾模式把複雜類中的核心職責和裝飾功能區分開了,這樣既簡化了複雜類,有去除了相關類中重複的裝飾邏輯。 裝飾模式沒有通過繼承原有類來擴充套件功能,但卻達到了一樣的目的,而且比繼承更加靈活,所以可以說裝飾模式是繼承關係的一種替代方案。
5.外觀模式 Facade
外觀模式為子系統中的一組介面提供了同意的介面,外觀模式定義了一個高層介面,這個介面使得這一子系統更加容易使用。
外觀模式中,客戶對各個具體的子系統是不瞭解的,所以對這些子系統進行了封裝,對外只提供了使用者所明白的單一而簡單的介面,使用者直接使用這個介面就可以完成操作,而不用去理睬具體的過程,而且子系統的變化不會影響到使用者,這樣就做到了資訊隱蔽。
6.享元模式 Flyweight
享元模式為運用共享技術有效的支援大量細粒度的物件。因為它可以通過共享大幅度地減少單個例項的數目,避免了大量非常相似類的開銷。.
享元模式是一個類別的多個物件共享這個類別的一個物件,而不是各自再例項化各自的物件。這樣就達到了節省記憶體的目的。
7.代理模式 Proxy
為其他物件提供一種代理,並由代理物件控制對原物件的引用,以間接控制對原物件的訪問。jvm堆記憶體問題
(單選題) 下面哪種情況會導致持久區jvm堆記憶體溢位?
A 迴圈上萬次的字串處理
B 在一段程式碼內申請上百M甚至上G的記憶體
C 使用CGLib技術直接操作位元組碼執行,生成大量的動態類
D 不斷建立物件
JVM堆記憶體分為2塊:Permanent Space 和 Heap Space。
Permanent 即 持久代(Permanent Generation),主要存放的是Java類定義資訊,與垃圾收集器要收集的Java物件關係不大。
Heap = { Old + NEW = {Eden, from, to} },Old 即 年老代(Old Generation),New 即 年輕代(Young Generation)。年老代和年輕代的劃分對垃圾收集影響比較大。
年輕代
所有新生成的物件首先都是放在年輕代。年輕代的目標就是儘可能快速的收集掉那些生命週期短的物件。年輕代一般分3個區,1個Eden區,2個Survivor區(from 和 to)。
大部分物件在Eden區中生成。當Eden區滿時,還存活的物件將被複制到Survivor區(兩個中的一個),當一個Survivor區滿時,此區的存活物件將被複制到另外一個Survivor區,當另一個Survivor區也滿了的時候,從前一個Survivor區複製過來的並且此時還存活的物件,將可能被複制到年老代。
2個Survivor區是對稱的,沒有先後關係,所以同一個Survivor區中可能同時存在從Eden區複製過來物件,和從另一個Survivor區複製過來的物件;而複製到年老區的只有從另一個Survivor區過來的物件。而且,因為需要交換的原因,Survivor區至少有一個是空的。特殊的情況下,根據程式需要,Survivor區是可以配置為多個的(多於2個),這樣可以增加物件在年輕代中的存在時間,減少被放到年老代的可能。
針對年輕代的垃圾回收即 Young GC。
年老代
在年輕代中經歷了N次(可配置)垃圾回收後仍然存活的物件,就會被複制到年老代中。因此,可以認為年老代中存放的都是一些生命週期較長的物件。
針對年老代的垃圾回收即 Full GC。
持久代
用於存放靜態型別資料,如 Java Class, Method 等。持久代對垃圾回收沒有顯著影響。但是有些應用可能動態生成或呼叫一些Class,例如 Hibernate CGLib 等,在這種時候往往需要設定一個比較大的持久代空間來存放這些執行過程中動態增加的型別。
所以,當一組物件生成時,記憶體申請過程如下:
JVM會試圖為相關Java物件在年輕代的Eden區中初始化一塊記憶體區域。
當Eden區空間足夠時,記憶體申請結束。否則執行下一步。
JVM試圖釋放在Eden區中所有不活躍的物件(Young GC)。釋放後若Eden空間仍然不足以放入新物件,JVM則試圖將部分Eden區中活躍物件放入Survivor區。
Survivor區被用來作為Eden區及年老代的中間交換區域。當年老代空間足夠時,Survivor區中存活了一定次數的物件會被移到年老代。
當年老代空間不夠時,JVM會在年老代進行完全的垃圾回收(Full GC)。
Full GC後,若Survivor區及年老代仍然無法存放從Eden區複製過來的物件,則會導致JVM無法在Eden區為新生成的物件申請記憶體,即出現“Out of Memory”。
OOM(“Out of Memory”)異常一般主要有如下2種原因:
年老代溢位,表現為:java.lang.OutOfMemoryError:Javaheapspace
這是最常見的情況,產生的原因可能是:設定的記憶體引數Xmx過小或程式的記憶體洩露及使用不當問題。
例如迴圈上萬次的字串處理、建立上千萬個物件、在一段程式碼內申請上百M甚至上G的記憶體。還有的時候雖然不會報記憶體溢位,卻會使系統不間斷的垃圾回收,也無法處理其它請求。這種情況下除了檢查程式、列印堆記憶體等方法排查,還可以藉助一些記憶體分析工具,比如MAT就很不錯。
持久代溢位,表現為:java.lang.OutOfMemoryError:PermGenspace
通常由於持久代設定過小,動態載入了大量Java類而導致溢位 ,解決辦法唯有將引數 -XX:MaxPermSize 調大(一般256m能滿足絕大多數應用程式需求)。將部分Java類放到容器共享區(例如Tomcat share lib)去載入的辦法也是一個思路,但前提是容器裡部署了多個應用,且這些應用有大量的共享類庫執行緒問題
pblic class NameList
{
private List names = new ArrayList();
public synchronized void add(String name)
{
names.add(name);
}
public synchronized void printAll() {
for (int i = 0; i < names.size(); i++)
{
System.out.print(names.get(i) + ””);
}
}
public static void main(String[]args)
{
final NameList sl = new NameList();
for (int i = 0; i < 2; i++)
{
new Thread()
{
public void run()
{
sl.add(“A”);
sl.add(“B”);
sl.add(“C”);
sl.printAll();
}
} .start();
}
}
}
Which two statements are true if this class is compiled and run?
A An exception may be thrown at runtime.
B The code may run with no output, without exiting.
C The code may run with no output, exiting normally(正常地).
D The code may rum with output “A B A B C C “, then exit.
E The code may rum with output “A B C A B C A B C “, then exit.
F The code may ruin with output “A A A B C A B C C “, then exit.
G The code may ruin with output “A B C A A B C A B C “, then exit.
【解析】
在每個執行緒中都是順序執行的,所以sl.printAll();必須在前三句執行之後執行,也就是輸出的內容必有ABC。
而執行緒之間是穿插執行的,所以一個執行緒執行 sl.printAll();之前可能有另一個執行緒執行了前三句的前幾句。
E答案相當於執行緒1順序執行完然後執行緒2順序執行完。
G答案則是執行緒1執行完前三句add之後執行緒2插一腳執行了一句add然後執行緒1再執行 sl.printAll();輸出ABCA。接著執行緒2順序執行完輸出ABCABC
輸出加起來即為ABCAABCABC。jvm中的classloader
一個jvm中預設的classloader有Bootstrap ClassLoader、Extension ClassLoader、App ClassLoader,分別各司其職:
(1)Bootstrap ClassLoader 負責載入java基礎類,主要是 %JRE_HOME/lib/ 目錄下的rt.jar、resources.jar、charsets.jar和class等
(2)Extension ClassLoader 負責載入java擴充套件類,主要是 %JRE_HOME/lib/ext 目錄下的jar和class
(3)App ClassLoader 負責載入當前java應用的classpath中的所有類。
(others):
classloader 載入類用的是全盤負責委託機制。 所謂全盤負責,即是當一個classloader載入一個Class的時候,這個Class所依賴的和引用的所有 Class也由這個classloader負責載入,除非是顯式的使用另外一個classloader載入。
所以,當我們自定義的classlo ader載入成功了 com.company.MyClass以後,MyClass裡所有依賴的class都由這個classLoader來載入完成。java異常處理知識點
例題:
public void getCustomerInfo() {
try {
// do something that may cause an Exception
} catch (java.io.FileNotFoundException ex) {
System.out.print(“FileNotFoundException!”);
} catch (java.io.IOException ex) {
System.out.print(“IOException!”);
} catch (java.lang.Exception ex) {
System.out.print(“Exception!”);
}
}
請選擇輸出結果:
A IOException!
B IOException!Exception!
C FileNotFoundException!IOException!
D FileNotFoundException!IOException!Exception!
答案是:A
try-catch-finally 規則( 異常處理語句的語法規則 )
1) 必須在 try 之後新增 catch 或 finally 塊。try 塊後可同時接 catch 和 finally 塊,但至少有一個塊。
2) 必須遵循塊順序:若程式碼同時使用 catch 和 finally 塊,則必須將 catch 塊放在 try 塊之後。
3) catch 塊與相應的異常類的型別相關。
4) 一個 try 塊可能有多個 catch 塊。若如此,則執行第一個匹配塊。即Java虛擬機器會把實際丟擲的異常物件依次和各個catch程式碼塊宣告的異常型別匹配,如果異常物件為某個異常型別或 其子類的例項,就執行這個catch程式碼塊,不會再執行其他的 catch程式碼塊
5) 可巢狀 try-catch-finally 結構。
6) 在 try-catch-finally 結構中,可重新丟擲異常。
7) 除了下列情況,總將執行 finally 做為結束: JVM 過早終止(呼叫 System.exit(int));在 finally 塊中丟擲一個未處理的異常;計算機斷電、失火、或遭遇病毒攻擊 。
由此可以看出,catch只會匹配一個,因為只要匹配了一個,虛擬機器就會使整個語句退出。