面試總結-Java基礎

之前學習Java基礎的時候做的一些筆記,稍微整理了一下,也方便以後看。不過這裡都是一些較為基礎的Java知識,後期有時間,會針對裡面的泛型,多線程,算法,nio 等模塊進行更深入的解析。

如果有寫錯的地方,還望指出,一定及時更正。

基礎

1 switch支持的類型:byte, short, int, char, enum,

注意:不支持long,double,JDK7之後,開始支持String。

//簡單示例

public class MyDemo {

public static void main(String… args) {

Demo demo = Demo.A;

switch (demo) {

case A:

break;

case B:

break;

}

}

enum Demo {

A,

B,

C

}

}

2 if和switch的區別:

if :1.對具體的值進行判斷 2.對區間判斷 3.對運算結果是boolean類型的表達式進行判斷

switch :1.對具體的值進行判斷;2.值的個數通常是固定的。

對於幾個固定值的判斷,建議使用switch語句,因為switch語句會將具體的答案加載進內存,相對高效一點。

重載和重寫的區別

重載:允許存在一個以上的同名函數,只要它們的參數類型不同即可。

重寫:當子類繼承父類,沿襲了父類的功能到子類中,子類雖具備該功能,但功能內容不一致,這是使用覆蓋特性,保留父類的功能定義,並重寫功能內容。

單例模式

餓漢式

private static Single s = new Single ( ) ;

private Single ( ) { }

public static Single getInstance ()

{

return s ;

}

懶漢式

class Single {

public static Single getInstance (){

if ( s== null ){

synchronized (Single.class){//鎖不讀可以提高效率

if ( s== null ){

s = new Single () ;

}

}

return s ;

}

}

特殊關鍵字:final

  1. 可以修飾類、函數、變量;

  2. 被final修飾的類不可以被繼承。為了避免被繼承,被子類複寫。final class Demo { }

  3. 被final修飾的方法不可以被複寫。final void show () { }

  4. 被final 修飾的變量是一個常量,只能賦值一次。

  5. 內部類定義在類中的局部位置上時,只能訪問該局部被final修飾的局部變量。

    異常:(關於問題1,謝謝ylt提醒)

    try{}catch(){}finally{}

    1.在catch中return(),finally{}會不會執行?

    答:會,會在return之後執行。

    2.finally()在什麼情況下不會執行

    答:只有一種情況不會執行,當執行到System.exit(0)時,finally不會執行。

    public class Test {

    public static void main(String[] args) {

    System.out.println(“haha:” + haha(true));

    }

    private static boolean haha(boolean isTrue) {

    try {

    int i = 1 / 0;

    return isTrue ? System.out.printf(“return try !null “, “test”) != null : System.out.printf(“return try null “, “test”) == null;

    } catch (Exception e) {

    System.out.println(“catch”);

    return isTrue ? System.out.printf(“return catch !null “, “test”) != null : System.out.printf(“return catch null “, “test”) == null;

    } finally {

    System.out.println(“”);

    System.out.println(“finally”);

    }

    }

    }

    //打印結果:

    catch

    return catch !null

    finally

    haha:true

    常見Runtime異常:

    ArithmeticException, ClassCastException, IllegalArgumentException,

    IndexOutOfBoundsException, NullPointerException,

    訪問權限

權限.png

Java靜態代碼塊、構造函數、構造代碼塊

先看下面一段代碼,運行Test,會打印什麼?

package test;

public class Test {

public static void main(String… args) {

TestA a;

a = new TestA();

a = new TestA();

TestA aa = new TestA();

}

}

class TestA {

{

System.out.println(“TestA code create”);

}

 private TestB b = new TestB(); private static TestC c = new TestC(); public TestA() { System.out.println("TestA create"); } static { System.out.println("TestA static create"); }

}

class TestB {

public TestB() {

System.out.println(“TestB create”);

}

}

class TestC {

public TestC() {

System.out.println(“TestC create”);

}

}

打印結果:

TestC create

TestA static create

TestA code create

TestB create

TestA create

TestA code create

TestB create

TestA create

TestA code create

TestB create

TestA create

static特點:

  1. 隨著類的加載而加載(隨著類的消失而消失,生命週期最長)

  2. 優先於對象存在

  3. 被所有對象所共享

  4. 可以直接被類所調用

  5. static是一個修飾符,用於修飾成員

    構造代碼塊

    作用:給對象進行初始化,對象一建立就運行,而且優先於構造函數執行。

    和構造函數的區別:

    構造代碼塊是給所有對象進行統一初始化;而構造函數是給對應的對象初始化

    構造代碼塊中定義的是不同對象共性的初始化內容

    靜態代碼塊

    static

    {

    靜態代碼塊中的執行語句;

    }

    特點:隨著類的加載而執行,只執行一次(再new一個對象也不會執行,類只加載一次),

    如果定義在有主函數的類中,則優先於主函數執行。用於給類進行初始化。

    有些類不用創建對象,無法用構造函數初始化,就通過靜態代碼塊初始化。

    執行順序:靜態代碼塊先執行,如果有對象,構造代碼塊先執行,然後是構造函數。

    如果沒有對象,則構造代碼塊和構造函數都不會執行。

    Java-封裝、繼承、多態

抽象類的特點:

  1. 抽象方法一定在抽象類中。

  2. 抽象方法和抽象類都必須被abstract關鍵字修飾。

  3. 抽象類不可以用new創建對象,因為調用抽象方法沒有意義。

  4. 抽象類中的抽象方法要被使用,必須由子類複寫所有的抽象方法後,建立子類對象調用。如果子類只覆蓋了部分抽象方法,那麼該子類還是一個抽象類。強迫子類複寫,強迫子類做一些事。

  5. 抽象類中可以不定義抽象方法,如果不定義抽象方法,那麼抽象類的功能就是為了不讓該類建立對象。

    抽象關鍵字不可以和哪些關鍵字共存?

    答:(1)private不能:抽象方法就是給子類覆蓋的,私有了就不能覆蓋了。

    (2)static不能:static可以直接用類名調用,而調用抽象方法沒有意義。

    (3)final 不能:final修飾的方法不可以被複寫,修飾的類不可以被繼承。與abstract衝突。

    接口的特點

    ● 接口是對外暴露的規則。

    ● 接口是程序的功能擴展。

    ● 接口可以多實現。

    ● 類與接口直接是實現關係,而且類可以繼承一個類的同時實現多個接口。

    ● 接口與接口之間可以有繼承關係,可以多繼承。

    因為接口沒有方法體,不會存在兩個父類出現同一個方法但是方法體不同的情況,

    不會引起衝突,如下:

    public class Test implements d{

    public static void main(String… args) {

    }

    @Override

    public void as() {

    }

    }

    interface d extends e,f {

    }

    interface f{

    void as();

    }

    interface e{

    void as();

    }

    接口和抽象類的異同點:

    相同點:都是不斷向上抽取而來的。不可以被實例化

    不同點:

  6. 抽象類需要被繼承,而且只能單繼承;接口需要被實現,而且可以多實現

  7. 抽象類中可以定義抽象方法和非抽象方法,子類繼承後,可以直接使用非抽象方法;

    接口只能定義抽象方法,必須有子類實現。

  8. 抽象類的繼承,是is a關係,在定義該體系的基本共性內容;

    接口的實現是like a 關係,在定義體系額外功能。

    繼承

    子類的實例化過程:

    結論:子類的所有的構造函數,默認都會訪問父類中空參數構造函數,因為子類中每一個構造函數內的第一行都有一句隱式的super() ;

    當父類中沒有空參數的構造函數時,子類必須手動通過super或者this語句形式來指定要訪問的構造函數。

    當然:子類的構造函數第一行也可以手動指定this語句來訪問本類中的構造函數,

    子類中至少會有一個構造函數會訪問到父類中的構造函數。

    對象的初始化過程,見下圖:

對象初始化過程.png

打印結果:

打印結果.png

線程

關於線程這塊,後期有時間會寫一個完整的深入的文章,這裡寫的都是比較簡單基礎的線程的一些知識。

創建線程的兩種方式:

1 繼承Thread類。

①.定義類繼承Thread;

②.複寫父類中的方法;目的:將自定義代碼存儲在run方法中,讓線程運行。

③.調用線程的start方法,該方法有兩個作用:啟動線程,調用run方法

2 實現Runnable接口

1. 定義類實現Runnable接口。

2. 覆蓋Runnable接口中的run方法。

3. 通過Thread類建立線程對象。

4. 將Runnable接口的子類對象作為實際參數傳遞給Thread類的構造函數。

5. 調用Thread類的start方法開啟線程並調用Runnable接口子類的run方法。

實現方式和繼承方式有什麼區別?

  1. 實現方式相比繼承方式的好處:

    避免了單繼承的侷限性(單繼承只能繼承一個父類)。在定義線程時,建議使用實現方式。

    2.存放代碼的位置不一樣:

    繼承Thread:線程代碼存放Thread子類的run方法中

    實現Runnable,線程代碼存在接口的子類的run方法。

    實現Runnable接口的好處:

    1,將線程的任務從線程的子類中分離出來,進行了單獨的封裝。

    按照面向對象的思想將任務的封裝成對象。

    2,避免了java單繼承的侷限性。

    同步的兩種表現形式:

1 同步代碼塊

synchronized(對象){

需要被同步的代碼;

}

2 同步函數。

將synchronized關鍵字作為修飾符放在函數上。

public synchronized void add()

  • 同步函數用的是哪一個鎖:函數需要被對象調用,那麼該函數都有一個所屬對象引用,就是this,所以同步函數使用的鎖是this(對象)

  • JDK1.5中提供了多線程升級解決方案,將同步synchronized替換成實現Lock操作,將Object中的wait,notify,notifyAll,替換成了Condition對象的await(),signal(),signalAll(),該對象可以通過Lock鎖進行獲取。

    停止線程

    原理:run方法結束

    1 setDeamon() 守護線程:setDaemon(ture) ;

    也稱後臺線程,當前臺線程執行時後臺線程也在執行,但是當前臺線程全部執行完關閉時,

    後臺線程也會跟著自動關閉,jvm退出。

    !!該方法必須在啟動線程前調用。

    2 join()等待該線程終止:一般用於臨時加入線程。

    當A線程執行到了B線程的.join()方法時,A就會等待,等B線程都執行完,A才會執行

    3 yield()方法:釋放執行權,讓其他線程運行。

    暫停當前正在執行的線程對象,並執行其他線程。

    一個死鎖的demo

    class Test implements Runnable {

    private boolean flag;

    Test(boolean flag) {

    this.flag = flag;

    }

    public void run() {

     if (flag) { while (true) synchronized (MyLock.locka) { System.out.println(Thread.currentThread().getName() + "..if locka...."); synchronized (MyLock.lockb) { System.out.println(Thread.currentThread().getName() + "..if lockb...."); } } } else { while (true) synchronized (MyLock.lockb) { System.out.println(Thread.currentThread().getName() + "..else lockb...."); synchronized (MyLock.locka) { System.out.println(Thread.currentThread().getName() + "..else locka...."); } } }

    }

    }

    class MyLock {

    public static final Object locka = new Object();

    public static final Object lockb = new Object();

    }

    class DeadLockTest {

    public static void main(String[] args) {

    Test a = new Test(true);

    Test b = new Test(false);

    Thread t1 = new Thread(a);

    Thread t2 = new Thread(b);

    t1.start();

    t2.start();

    }

    }

    wait和sleep的區別

  1. wait 可以指定時間也可以不指定。sleep必須指定時間。

  2. 在同步中,對CPU的執行權和鎖的處理不同:

    wait:釋放執行權,釋放鎖

    sleep:釋放執行權,不釋放鎖

    StringBuffer和StringBuilder的區別

  1. 使用intrrupt()方法。該方法是結束線程的凍結狀態,使線程回到運行狀態中來。

    當線程處於凍結狀態,就不會結束讀取到標記,那麼線程就不會結束。

    當沒有指定的方式讓凍結的線程恢復到運行狀態時,這時需要對凍結進行清除。

    強制讓線程恢復到運行狀態中來,這樣就可以操作標記讓線程結束。

  2. 定義循環結束標記。線程運行代碼一般都是循環,只要控制了循環即可。

    線程常見方法

StringBuffer是線程同步(安全)。如果是單線程,效率就比較低

StringBuilder是線程不同步。

集合

Java集合關係圖.png

Collection:單列集合

List 和 set

List:元素是有序的,元素可以重複,因為該集合體繫有索引

Set:元素是無序的,元素不可以重複(存入和取出的順序不一定一致)。

List特有方法:凡是可以操作角標的方法都是該體系特有的方法

List中常見的三個子類

  1. ArrayList :底層的數據使用的是數組結構。

    特點:查詢速度很快,但是增刪稍慢。線程不同步,效率高 。

    可變長度數組,默認容量為10的空列表,如果超過了,則50%的增加

  2. LinkedList :底層的數據使用的是鏈表數據結構。

    特點:增刪數度很快,但是查詢稍慢。

  3. Vector:底層使用的是數組結構。枚舉是Vector特有的取出方式

    是同步的,效率較低,被ArrayList替代。最早出現的。

    默認容量為10的空列表,如果超過了,則100%的增加.

    LinkedList

    JDK1.6版本出現的:pollFirst(),pollLast(),peekFirst() ,peekLast(),offerFirst(),offerLast()

    (如果鏈表為空,返回null )。

    分別替代了remove 和 get 和add (如果鏈表為空,則拋出異常)。

    set常見子類

  4. HashSet:底層數據結構是哈希表。

    HashSet是如何保證元素的唯一性的:

    是通過元素的兩個方法,hashCode和equals來完成,如果元素的hashCode值相同,

    才會判斷equals是否為true,如果元素的hashCode值不同,不會調用equals 。

    開發時描述事物,需要往集合裡面存時,一般都要複寫hashCode和equals。

    TreeSet底層的數據結構:二叉樹

    保證數據元素唯一性的依據compareTo方法return 0,為0則表示是相同元素 ;

    排序的兩種方式:

    TreeSet排序的第一種方式:

    讓元素自身具備比較性。元素需要實現Comparable接口,覆蓋compareTo方法。這種方式也稱為元素的自然順序,或者叫做默認順序。

    TreeSet的第二種排序方式:

    當元素自身不具備比較性時,或具備的比較性不是所需要的,這是就需要讓集合自身具備比較性。

    定義一個比較器,將比較器對象作為參數傳遞給TreeSet集合的構造函數。

    定義一個類,實現Comparator接口,覆蓋compare方法

    當兩種排序都存在時,以比較器為主。

    泛型

    泛型技術是給編譯器使用的技術,用於編譯時期。確保了類型的安全。

    運行時,會將泛型去掉,生成的class文件中是不帶泛型的,這個稱為泛型的擦除。

    為什麼擦除呢?因為為了兼容運行的類加載器。

    泛型的補償:在類加載器原有基礎上,編寫一個補償程序。在運行時,通過反射,

    獲取元素的類型進行轉換動作。不用使用者在強制轉換了。

    Map:雙列集合

常見子類

Hashtable:底層是哈希表數據結構,不可以存入null鍵null值,該集合是線程同步的。jdk1.0 ,效率低 。

HashMap:底層是哈希表數據結構,並允許使用null鍵null值,該集合不是同步的,jdk1.2 ,效率高。

TreeMap :底層是二叉樹數據結構,線程不同步,可以給map集合中的鍵進行排序 。

Map 和 Set很像 :其實,Set底層使用了Map集合 。

map集合的兩種取出方式:

1.Set KeySet:

將Map中所有的Key存到了Set集合中,因為Set集合具備迭代器。

所有可以迭代方式取出所有的鍵,再根據get方法,獲取每一個鍵對應的值

Map集合的取出原理:將Map集合轉成Set集合,再通過迭代器取出

2.Set

大家可以點擊加入群:478052716【JAVA高級程序員】裡面有Java高級大牛直播講解知識點 走的就是高端路線 (如果你想跳槽換工作 但是技術又不夠 或者工作上遇到了 瓶頸 我這裡有一個JAVA的免費直播課程 講的是高端的知識點基礎不好的誤入喲 只要你有1-5年的開發經驗可以加群找我要課堂鏈接 注意:是免費的 沒有開發經驗誤入哦)

相關推薦

推薦中...