1. 程式人生 > >【猿小白】常見Java面試問題彙總~~~持續更新~~~直到拿到心儀的offer

【猿小白】常見Java面試問題彙總~~~持續更新~~~直到拿到心儀的offer

眼看秋招已過大半,依舊沒有拿到心儀的offer,但還是要堅持住最初的信念,必要的時候還是得喝點心靈雞湯,不管上一場面試結果如何,還是得重整旗鼓,卯足了勁往前衝,所謂百面成鋼,前提也得是總結分析失敗的教訓。下面的一些問題是一些我在面試的時候遇到的問題,會與不會的我都一併整理了下來,查缺補漏掃掃盲。這一篇主要是關於Java常見的面試問題

**

Java中常見的面試問題

**

1、Java開發中常用的設計模式
設計模式(Design Patterns)是一套被反覆使用、多數人知曉的、經過分類編目的、程式碼設計經驗的總結。使用設計模式是為了可重用程式碼、讓程式碼更容易被他人理解、保證程式碼可靠性。毫無疑問,設計模式於己於他人於系統都是多贏的,,設計模式使程式碼編制真正工程化,設計模式是軟體工程的基石,如同大廈的一塊塊磚石一樣。
一、設計模式的分類
總體來說設計模式分為三大類:
建立性模式
結構性模式
行為性模式

2、方法重寫和方法過載的區別
方法過載和方法重寫在英文中分別是overload和override,很多人在學習Java的過程中總是分不清重寫和過載這兩個方法,實際上,這兩個方法還是有很大的差別的,過載和重寫這兩個方法雖然名字有些類似,但他們之間有很少的聯絡,除了二者都是發生在方法之間,並要求方法名相同之外,沒有太大的相似之處。過載主要發生在同一個類的多個重名方法之間,重寫則發生在子類和父類同名方法之間,當然父類方法和子類方法之間也可以發生過載,因為子類會獲得父類的方法,如果子類定義了一個與父類方法有相同的方法名,但引數列表不同的方法,就會形成父類方法和子類方法的過載。
方法過載要遵循的原則:兩同一不同
(1)兩同:同一個類中的方法名相同
(2)一不同:引數列表(個數或型別)不同
至於方法中的其他部分,如方法返回值型別、修飾符等,與方法過載沒有任何關係。
方法重寫要遵循的原則:兩同兩小一大
(1)兩同:方法名相同、形參列表相同
(2)兩小:子類方法返回值型別應比父類方法返回值型別更小或相等,子類方法宣告丟擲的異常類應比父類方法宣告丟擲的異常類更小或相等
(3)一大:是子類方法的訪問許可權應比父類方法的訪問許可權更大或相等

3、static關鍵字
在《Java程式設計思想》P86頁有這樣一段話:
“static方法就是沒有this的方法。在static方法內部不能呼叫非靜態方法,反過來是可以的。而且可以在沒有建立任何物件的前提下,僅僅通過類本身來呼叫static方法。這實際上正是static方法的主要用途。”
這段話雖然只是說明了static方法的特殊之處,但是可以看出static關鍵字的基本作用,簡而言之,一句話來描述就是:
方便在沒有建立物件的情況下來進行呼叫(方法/變數)。
很顯然,被static關鍵字修飾的方法或者變數不需要依賴於物件來進行訪問,只要類被載入了,就可以通過類名去進行訪問。
static可以用來修飾類的成員方法、類的成員變數,另外可以編寫static程式碼塊來優化程式效能。
1)static方法
static方法一般稱作靜態方法,由於靜態方法不依賴於任何物件就可以進行訪問,因此對於靜態方法來說,是沒有this的,因為它不依附於任何物件,既然都沒有物件,就談不上this了。並且由於這個特性,在靜態方法中不能訪問類的非靜態成員變數和非靜態成員方法,因為非靜態成員方法/變數都是必須依賴具體的物件才能夠被呼叫。
靜態方法可以直接通過類名呼叫,任何的例項也都可以呼叫,
因此靜態方法中不能用this和super關鍵字,不能直接訪問所屬類的例項變數和例項方法(就是不帶static的成員變數和成員成員方法),只能訪問所屬類的靜態成員變數和成員方法。
因為例項成員與特定的物件關聯!這個需要去理解,想明白其中的道理,不是記憶!!!
因為static方法獨立於任何例項,因此static方法必須被實現,而不能是抽象的abstract。
例如為了方便方法的呼叫,Java API中的Math類中所有的方法都是靜態的,而一般類內部的static方法也是方便其它類對該方法的呼叫。
靜態方法是類內部的一類特殊方法,只有在需要時才將對應的方法宣告成靜態的,一個類內部的方法一般都是非靜態的
2)static變數
static變數也稱作靜態變數,靜態變數和非靜態變數的區別是:靜態變數被所有的物件所共享,在記憶體中只有一個副本,它當且僅當在類初次載入時會被初始化。而非靜態變數是物件所擁有的,在建立物件的時候被初始化,存在多個副本,各個物件擁有的副本互不影響。
static成員變數的初始化順序按照定義的順序進行初始化。
按照是否靜態的對類成員變數進行分類可分兩種:一種是被static修飾的變數,叫靜態變數或類變數;另一種是沒有被static修飾的變數,叫例項變數。
兩者的區別是:
對於靜態變數在記憶體中只有一個拷貝(節省記憶體),JVM只為靜態分配一次記憶體,在載入類的過程中完成靜態變數的記憶體分配,可用類名直接訪問(方便),當然也可以通過物件來訪問(但是這是不推薦的)。
對於例項變數,沒建立一個例項,就會為例項變數分配一次記憶體,例項變數可以在記憶體中有多個拷貝,互不影響(靈活)。
所以一般在需要實現以下兩個功能時使用靜態變數:
(1)在物件之間共享值時
(2)方便訪問變數時
3)static程式碼塊
static程式碼塊也叫靜態程式碼塊,是在類中獨立於類成員的static語句塊,可以有多個,位置可以隨便放,它不在任何的方法體內,JVM載入類時會執行這些靜態的程式碼塊,如果static程式碼塊有多個,JVM將按照它們在類中出現的先後順序依次執行它們,每個程式碼塊只會被執行一次。
static關鍵字還有一個比較關鍵的作用就是 用來形成靜態程式碼塊以優化程式效能。static塊可以置於類中的任何地方,類中可以有多個static塊。在類初次被載入的時候,會按照static塊的順序來執行每個static塊,並且只會執行一次。
為什麼說static塊可以用來優化程式效能,是因為它的特性:只會在類載入的時候執行一次。

4、垃圾回收機制
垃圾回收機制只負責回收堆記憶體中的物件,不會回收任何物理資源(例如資料庫連線、網路IO等資源)。
程式無法精確控制垃圾回收後的執行,垃圾回收會在合適的時候執行。當物件永久的失去引用後,系統會在合適的時候回收它所佔用的資源。
在垃圾回收機制回收任何資源之前,總會先呼叫它的finalize()方法,該方法可能使該物件重新復活(讓一個引用變數重新引用該物件),從而導致垃圾回收機制取消回收。
當一個物件在堆記憶體中執行時,根據它被引用變數所引起的狀態,可以把它所處的狀態分成如下三種。
可達狀態:當一個物件被建立後,若有一個以上的引用變數引用它,則這個物件在程式中處於可達狀態,程式可通過引用變數來呼叫該物件的例項變數和方法。
可恢復狀態:如果程式中某個變數不再有任何變數引用它,它就進入了可恢復狀態。在這種狀態下,系統的垃圾回收機制準備回收該物件所佔用的記憶體,在回收該物件之前,系統會呼叫所有可恢復狀態物件的finalize()方法進行資源清理,如果系統在呼叫finalize()方法時重新讓一個引用變數引用該物件,則這個物件會再次變為可達狀態,否則該物件進入不可達狀態。
不可達狀態:當物件與所有引用變數的關聯都被切斷,切系統已經呼叫所有物件的finalize()方法後依然沒有使該物件程式設計可達狀態,那麼這個物件將永久性的失去引用,最後變成不可達狀態,只有當一個物件變成不可達狀態時,系統才會真正回收該物件佔有的資源。
強制垃圾回收
系統無法精確控制Java系統垃圾回收的時機,但依然可以強制系統進行垃圾回收——這種強制只是通知系統進行垃圾回收,但系統是否進行垃圾回收依然不確定。大部分時候,程式強制系統垃圾回收後總會有一些效果。強制系統垃圾回收有如下兩種方式:
1、呼叫System類的gc()靜態方法:Syatem.gc()。
2、呼叫Runtime物件的gc()例項方法:Runtime.getRuntime().gc().

5、建立執行緒的方法
Java中建立執行緒的方法主要有三種方式;
1、繼承Thread類建立執行緒
(1)定義Thread的子類,並重寫該類的run方法,該run方法的方法體就代表了執行緒要完成的任務,因此把run()方法稱為執行體。
(2)建立Thread子類的例項,即建立了執行緒物件。
(3)呼叫執行緒物件的start()方法來啟動該執行緒。

package com.thread;  

public class FirstThreadTest extends Thread{  
    int i = 0;  
    //重寫run方法,run方法的方法體就是現場執行體  
    public void run()  
    {  
        for(;i<100;i++){  
        System.out.println(getName()+"  "+i);  

        }  
    }  
    public static void main(String[] args)  
    {  
        for(int i = 0;i< 100;i++)  
        {  
            System.out.println(Thread.currentThread().getName()+"  : "+i);  
            if(i==20)  
            {  
                new FirstThreadTest().start();  
                new FirstThreadTest().start();  
            }  
        }  
    }  

}  

2、通過Runnable介面來建立執行緒類
(1)定義Runnable介面的實現類,並重寫該介面的run()方法,該run()方法體同樣是該執行緒的程式執行體。
(2)建立Runnable實現類的例項,並依此例項作為Thread的target來建立Thread物件,該Thread物件才是真正的執行緒物件。
(3)呼叫物件的start()方法來啟動執行緒。

package com.thread;  

public class RunnableThreadTest implements Runnable  
{  

    private int i;  
    public void run()  
    {  
        for(i = 0;i <100;i++)  
        {  
            System.out.println(Thread.currentThread().getName()+" "+i);  
        }  
    }  
    public static void main(String[] args)  
    {  
        for(int i = 0;i < 100;i++)  
        {  
            System.out.println(Thread.currentThread().getName()+" "+i);  
            if(i==20)  
            {  
                RunnableThreadTest rtt = new RunnableThreadTest();  
                new Thread(rtt,"新執行緒1").start();  
                new Thread(rtt,"新執行緒2").start();  
            }  
        }  

    }  

}

3、通過Callable的Future建立執行緒
(1)建立Callable介面的實現類,並實現call()方法,該call()方法將作為執行緒執行體,並且有返回值。
(2)建立Callable實現類的例項,使用FutureTask類來包裝Callable物件,該FutureTask物件封裝了該Callable物件的call()方法的返回值。
(3)使用FutureTask物件作為Thread物件的target建立並啟動新執行緒。
(4)呼叫FutureTask物件的get()方法來獲得子執行緒執行結束後的返回值。

package com.thread;  

import java.util.concurrent.Callable;  
import java.util.concurrent.ExecutionException;  
import java.util.concurrent.FutureTask;  

public class CallableThreadTest implements Callable<Integer>  
{  

    public static void main(String[] args)  
    {  
        CallableThreadTest ctt = new CallableThreadTest();  
        FutureTask<Integer> ft = new FutureTask<>(ctt);  
        for(int i = 0;i < 100;i++)  
        {  
            System.out.println(Thread.currentThread().getName()+" 的迴圈變數i的值"+i);  
            if(i==20)  
            {  
                new Thread(ft,"有返回值的執行緒").start();  
            }  
        }  
        try  
        {  
            System.out.println("子執行緒的返回值:"+ft.get());  
        } catch (InterruptedException e)  
        {  
            e.printStackTrace();  
        } catch (ExecutionException e)  
        {  
            e.printStackTrace();  
        }  

    }  

    @Override  
    public Integer call() throws Exception  
    {  
        int i = 0;  
        for(;i<100;i++)  
        {  
            System.out.println(Thread.currentThread().getName()+" "+i);  
        }  
        return i;  
    }  

}  

三種方法比較:
採用實現Runnable、Callable介面的方式創見多執行緒時,優勢是:
執行緒類只是實現了Runnable介面或Callable介面,還可以繼承其他類。
在這種方式下,多個執行緒可以共享同一個target物件,所以非常適合多個相同執行緒來處理同一份資源的情況,從而可以將CPU、程式碼和資料分開,形成清晰的模型,較好地體現了面向物件的思想。
劣勢是:
程式設計稍微複雜,如果要訪問當前執行緒,則必須使用Thread.currentThread()方法。
使用繼承Thread類的方式建立多執行緒時優勢是:
編寫簡單,如果需要訪問當前執行緒,則無需使用Thread.currentThread()方法,直接使用this即可獲得當前執行緒。
劣勢是:
執行緒類已經繼承了Thread類,所以不能再繼承其他父類。
6、&和&&的區別
&&:與,前後兩個運算元必須都是true才返回true,否則返回false
&:不短路與,作用與&&相同,但不會短路。
7、Java的三大特徵並舉例說明
三大特徵:繼承、封裝、多型
封裝就是private public 一般屬性是private主要是防止別的類直接訪問這個屬性
對應的setter和getter方法是public,提供對該屬性的獲取和修改
繼承就是extends,主要用來對功能的擴充套件
多型的體現就是介面,
8、set、map和list的區別
list和set是實現了collection介面的,Map是個頂級介面
List:1.可以允許重複的物件。
   2.可以插入多個null元素。
3.是一個有序容器,保持了每個元素的插入順序,輸出的順序就是插入的順序。
4.常用的實現類有 ArrayList、LinkedList 和 Vector。ArrayList 最為流行,它提供了使用索引的隨意訪問,而 LinkedList 則對於經常需要從 List 中新增或刪除元素的場合更為合適。
Set:1.不允許重複物件
   2. 無序容器,你無法保證每個元素的儲存順序,TreeSet通過 Comparator 或者 Comparable 維護了一個排序順序。
   3.只允許一個 null 元素
   4.Set 介面最流行的幾個實現類是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基於 HashMap 實現的 HashSet;TreeSet 還實現了 SortedSet 介面,因此 TreeSet 是一個根據其 compare() 和 compareTo() 的定義進行排序的有序容器。
Map:1、Map 的 每個 Entry 都持有兩個物件,也就是一個鍵一個值,Map 可能會持有相同的值物件但鍵物件必須是唯一的。
2、TreeMap 也通過 Comparator 或者 Comparable 維護了一個排序順序。
3、Map 裡你可以擁有隨意個 null 值但最多隻能有一個 null 鍵。
4、Map 介面最流行的幾個實現類是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。(HashMap、TreeMap最常用)
什麼場景下使用list,set,map呢?
(或者會問為什麼這裡要用list、或者set、map,這裡回答它們的優缺點就可以了)
答:如果你經常會使用索引來對容器中的元素進行訪問,那麼 List 是你的正確的選擇。如果你已經知道索引了的話,那麼 List 的實現類比如 ArrayList 可以提供更快速的訪問,如果經常新增刪除元素的,那麼肯定要選擇LinkedList。
如果你想容器中的元素能夠按照它們插入的次序進行有序儲存,那麼還是 List,因為 List 是一個有序容器,它按照插入順序進行儲存。
如果你想保證插入元素的唯一性,也就是你不想有重複值的出現,那麼可以選擇一個 Set 的實現類,比如 HashSet、LinkedHashSet 或者 TreeSet。所有 Set 的實現類都遵循了統一約束比如唯一性,而且還提供了額外的特性比如 TreeSet 還是一個 SortedSet,所有儲存於 TreeSet 中的元素可以使用 Java 裡的 Comparator 或者 Comparable 進行排序。LinkedHashSet 也按照元素的插入順序對它們進行儲存。
如果你以鍵和值的形式進行資料儲存那麼 Map 是你正確的選擇。你可以根據你的後續需要從 Hashtable、HashMap、TreeMap 中進行選擇。
9、List和ArrayList的區別
List是一個介面,而ArrayList是List介面的一個實現類
這裡寫圖片描述

從圖中我們可以看出:
1. List是一個介面,它繼承與Collection介面,代表有序的佇列。
2. AbstractList是一個抽象類,它繼承與AbstractCollection。AbstractList實現了List介面中除了size()、get(int location)之外的方法。
3. AbstractSequentialList是一個抽象類,它繼承與AbstrctList。AbstractSequentialList實現了“連結串列中,根據index索引值操作連結串列的全部方法”。
4. ArrayList、LinkedList、Vector和Stack是List的四個實現類,其中Vector是基於JDK1.0,雖然實現了同步,但是效率低,已經不用了,Stack繼承與Vector,所以不再贅述。
5. LinkedList是個雙向連結串列,它同樣可以被當作棧、佇列或雙端佇列來使用。
ArrayList和LinkedList區別
我們知道,通常情況下,ArrayList和LinkedList的區別有以下幾點:
1. ArrayList是實現了基於動態陣列的資料結構,而LinkedList是基於連結串列的資料結構;
2. 對於隨機訪問get和set,ArrayList要優於LinkedList,因為LinkedList要移動指標;
3. 對於新增和刪除操作add和remove,一般大家都會說LinkedList要比ArrayList快,因為ArrayList要移動資料。但是實際情況並非這樣,對於新增或刪除,LinkedList和ArrayList並不能明確說明誰快誰慢,下面會詳細分析。
我們結合之前分析的原始碼,來看看為什麼是這樣的:
ArrayList中的隨機訪問、新增和刪除部分原始碼如下:

//獲取index位置的元素值  
public E get(int index) {  
    rangeCheck(index); //首先判斷index的範圍是否合法  

    return elementData(index);  
}  

//將index位置的值設為element,並返回原來的值  
public E set(int index, E element) {  
    rangeCheck(index);  

    E oldValue = elementData(index);  
    elementData[index] = element;  
    return oldValue;  
}  

//將element新增到ArrayList的指定位置  
public void add(int index, E element) {  
    rangeCheckForAdd(index);  

    ensureCapacityInternal(size + 1);  // Increments modCount!!  
    //將index以及index之後的資料複製到index+1的位置往後,即從index開始向後挪了一位  
    System.arraycopy(elementData, index, elementData, index + 1,  
                     size - index);   
    elementData[index] = element; //然後在index處插入element  
    size++;  
}  

//刪除ArrayList指定位置的元素  
public E remove(int index) {  
    rangeCheck(index);  

    modCount++;  
    E oldValue = elementData(index);  

    int numMoved = size - index - 1;  
    if (numMoved > 0)  
        //向左挪一位,index位置原來的資料已經被覆蓋了  
        System.arraycopy(elementData, index+1, elementData, index,  
                         numMoved);  
    //多出來的最後一位刪掉  
    elementData[--size] = null; // clear to let GC do its work  

    return oldValue;  
}

LinkedList中的隨機訪問、新增和刪除部分原始碼如下:

//獲得第index個節點的值  
public E get(int index) {  
    checkElementIndex(index);  
    return node(index).item;  
}  

//設定第index元素的值  
public E set(int index, E element) {  
    checkElementIndex(index);  
    Node<E> x = node(index);  
    E oldVal = x.item;  
    x.item = element;  
    return oldVal;  
}  

//在index個節點之前新增新的節點  
public void add(int index, E element) {  
    checkPositionIndex(index);  

    if (index == size)  
        linkLast(element);  
    else  
        linkBefore(element, node(index));  
}  

//刪除第index個節點  
public E remove(int index) {  
    checkElementIndex(index);  
    return unlink(node(index));  
}  

//定位index處的節點  
Node<E> node(int index) {  
    // assert isElementIndex(index);  
    //index<size/2時,從頭開始找  
    if (index < (size >> 1)) {  
        Node<E> x = first;  
        for (int i = 0; i < index; i++)  
            x = x.next;  
        return x;  
    } else { //index>=size/2時,從尾開始找  
        Node<E> x = last;  
        for (int i = size - 1; i > index; i--)  
            x = x.prev;  
        return x;  
    }  
}  

10、final,finally,finalize的區別
final 用於宣告屬性,方法和類,分別表示屬性不可變,方法不可覆蓋,類不可繼承。
finally是異常處理語句結構的一部分,表示總是執行。
finalize是Object類的一個方法,在垃圾收集器執行的時候會呼叫被回收物件的此方法,可以覆蓋此方法提供垃圾收集時的其他資源回收,例如關閉檔案等
final—修飾符(關鍵字)如果一個類被宣告為final,意味著它不能再派生出新的子類,不能作為父類被繼承。因此一個類不能既被宣告為 abstract的,又被宣告為final的。將變數或方法宣告為final,可以保證它們在使用中不被改變。被宣告為final的變數必須在宣告時給定初值,而在以後的引用中只能讀取,不可修改。被宣告為final的方法也同樣只能使用,不能過載。
finally—再異常處理時提供 finally 塊來執行任何清除操作。如果丟擲一個異常,那麼相匹配的 catch 子句就會執行,然後控制就會進入 finally 塊(如果有的話)。
finalize—方法名。Java 技術允許使用 finalize() 方法在垃圾收集器將物件從記憶體中清除出去之前做必要的清理工作。這個方法是由垃圾收集器在確定這個物件沒有被引用時對這個物件呼叫的。它是在 Object 類中定義的,因此所有的類都繼承了它。子類覆蓋 finalize() 方法以整理系統資源或者執行其他清理工作。finalize() 方法是在垃圾收集器刪除物件之前對這個物件呼叫的。
11、遍歷
在遍歷陣列時,使用foreach語句更簡單

package captain;  

public class ArrayDemo2 {  

    public static void main(String[] args) {  
        // TODO Auto-generated method stub  
        int arr[][] = new int[][]{{4,3},{1,5}};  

        //foreach語句遍歷二維陣列。  
        System.out.println("陣列中的元素是:");  
        for(int x[]:arr){  //外層遍歷得到一維陣列  
            for(int e:x){  //內層遍歷得到陣列元素  
                    System.out.print(e);  
            }  
            System.out.println();  
        }  

    }  

} 

利用Arrays工具類中的toString靜態方法可以將一維陣列轉化為字串形式並輸出

import java.util.Arrays;  

public class ArrayDemo3 {  

    public static void main(String[] args) {  
        // TODO Auto-generated method stub  

        //Arrays工具類的toString靜態方法遍歷二維陣列。  
        int arr[][] = new int[][]{{9,8},{7,6,5}};  
        for(int i = 0; i < arr.length; i++){//迴圈得到一維陣列  
            System.out.println(Arrays.toString(arr[i]));//將一維陣列轉化為字串輸出  
        }  
    }  

}  

12、equals和==的區別
1. == 是一個運算子。
  2.Equals則是string物件的方法,可以.(點)出來。
  
  我們比較無非就是這兩種 1、基本資料型別比較 2、引用物件比較
  1、基本資料型別比較
  ==和Equals都比較兩個值是否相等。相等為true 否則為false;
  
  2、引用物件比較
  ==和Equals都是比較棧記憶體中的地址是否相等 。相等為true 否則為false;
  
  需注意幾點:
  1、string是一個特殊的引用型別。對於兩個字串的比較,不管是 == 和 Equals 這兩者比較的都是字串是否相同;
  2、當你建立兩個string物件時,記憶體中的地址是不相同的,你可以賦相同的值。
  所以字串的內容相同。引用地址不一定相同,(相同內容的物件地址不一定相同),但反過來卻是肯定的;
  3、基本資料型別比較(string 除外) == 和 Equals 兩者都是比較值;
13、sleep和wait的區別
① 這兩個方法來自不同的類分別是,sleep來自Thread類,和wait來自Object類。
sleep是Thread的靜態類方法,誰呼叫的誰去睡覺,即使在a執行緒裡呼叫b的sleep方法,實際上還是a去睡覺,要讓b執行緒睡覺要在b的程式碼中呼叫sleep。
② 鎖: 最主要是sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其他執行緒可以使用同步控制塊或者方法。
sleep不出讓系統資源;wait是進入執行緒等待池等待,出讓系統資源,其他執行緒可以佔用CPU。一般wait不會加時間限制,因為如果wait執行緒的執行資源不夠,再出來也沒用,要等待其他執行緒呼叫notify/notifyAll喚醒等待池中的所有執行緒,才會進入就緒佇列等待OS分配系統資源。sleep(milliseconds)可以用時間指定使它自動喚醒過來,如果時間不到只能呼叫interrupt()強行打斷。
Thread.sleep(0)的作用是“觸發作業系統立刻重新進行一次CPU競爭”。
③ 使用範圍:wait,notify和notifyAll只能在同步控制方法或者同步控制塊裡面使用,而sleep可以在任何地方使用。
synchronized(x){
x.notify()
//或者wait()
}
14、介紹下你瞭解的排序方法,分別是內排還是外排
內排序是在排序整個過程中,待排序的所有記錄全部被放置在記憶體中。外排序是由於排序的記錄個數太多,不能同時放置在記憶體,整個排序過程需要在內外存之間多次交換資料才能進行。我們這裡主要就介紹內排序的多種方法。常見的內部排序方法包括:氣泡排序、堆排序、直接插入排序、歸併排序、快遞排序、基數排序、選擇排序、希爾排序等;
對於內排序來說,排序演算法的效能主要是受3個方面影響:
1.時間效能
排序是資料處理中經常執行的一種操作,往往屬於系統的核心部分,因此排序演算法的時間開銷是衡量其好壞的最重要的標誌。在內排序中,主要進行兩種操作:比較和移動。比較指關鍵字之間的比較,這是要做排序最起碼的操作。移動指記錄從一個位置移動到另一個位置,事實上,移動可以通過改為記錄的儲存方式來予以避免(這個我們在講解具體的演算法時再談)。總之,高效率的內排序演算法應該是具有儘可能少的關鍵字比較次數和儘可能少的記錄移動次數。

2.輔助空間
評價排序演算法的另一個主要標準是執行演算法所需要的輔助儲存空間。輔助儲存空間是除了存放待排序所佔用的儲存空間之外,執行演算法所需要的其他儲存空間。

3.演算法的複雜性
注意這裡指的是演算法本身的複雜度,而不是指演算法的時間複雜度。顯然演算法過於複雜也會影響排序的效能。

根據排序過程中藉助的主要操作,我們把內排序分為:插入排序、交換排序、選擇排序和歸併排序。可以說,這些都是比較成熟的排序技術,已經被廣泛地應用於許許多多的程式語言或資料庫當中,甚至它們都已經封裝了關於排序演算法的實現程式碼。因此,我們學習這些排序演算法的目的更多並不是為了去在現實中程式設計排序演算法,而是通過學習來提高我們編寫演算法的能力,以便於去解決更多複雜和靈活的應用性問題。

這裡寫圖片描述
下面對幾個常見的排序方法的思想做一下介紹:
(1)氣泡排序法
基本思想:在要排序的一組數中,對當前還未排好序的範圍內的全部數,自上而下對相鄰的兩個數依次進行比較和調整,讓較大的數往下沉,較小的數往上冒。即:每當兩相鄰的數比較後,發現他們的排序與排序要求相反時,就將他們互換。
(2)插入排序
插入即表示將一個新的資料插入到一個有序陣列中,並繼續保持有序。例如有一個長度為N的無序陣列,進行N-1次的插入即能完成排序;第一次,陣列第1個數認為是有序的陣列,將陣列第二個元素插入僅有1個有序的陣列中;第二次,陣列前兩個元素組成有序的陣列,將陣列第三個元素插入由兩個元素構成的有序陣列中……第N-1次,陣列前N-1個元素組成有序的陣列,將陣列的第N個元素插入由N-1個元素構成的有序陣列中,則完成了整個插入排序。
(3)快速排序
通過一趟排序將要排序的資料分割成獨立的兩部分,其中一部分的所有資料都比另外一部分的所有資料都要小,然後再按此方法對這兩部分資料分別進行快速排序,整個排序過程可以遞迴進行,以此達到整個資料變成有序序列。

15、解釋下反射機制
概念:主要是指程式可以訪問、檢測和修改它本身狀態或行為的一種能力,並能根據自身行為的狀態和結果,調整或修改應用所描述行為的狀態和相關的語義。
反射是Java中一種強大的工具,能夠使我們很方便的建立靈活的程式碼,這些程式碼可以執行時裝配,無需在元件之間進行原始碼連結,但是反射使用不當會成本很高。
反射機制的作用:
(1)反編譯:.class->.java
(2)通過反射機制訪問Java物件的屬性、方法、構造方法等

16、程序之間的通訊方式有哪些?
程序間通訊主要包括管道, 系統IPC(包括訊息佇列,訊號量,共享儲存), SOCKET.
管道( pipe ):管道是一種半雙工的通訊方式,資料只能單向流動,而且只能在具有親緣關係的程序間使用。程序的親緣關係通常是指父子程序關係。
有名管道 (named pipe) : 有名管道也是半雙工的通訊方式,但是它允許無親緣關係程序間的通訊。
訊號量( semophore ) : 訊號量是一個計數器,可以用來控制多個程序對共享資源的訪問。它常作為一種鎖機制,防止某程序正在訪問共享資源時,其他程序也訪問該資源。因此,主要作為程序間以及同一程序內不同執行緒之間的同步手段。
訊息佇列( message queue ) : 訊息佇列是由訊息的連結串列,存放在核心中並由訊息佇列識別符號標識。訊息佇列克服了訊號傳遞資訊少、管道只能承載無格式位元組流以及緩衝區大小受限等缺點。
訊號 ( sinal ) : 訊號是一種比較複雜的通訊方式,用於通知接收程序某個事件已經發生。
共享記憶體( shared memory ) :共享記憶體就是對映一段能被其他程序所訪問的記憶體,這段共享記憶體由一個程序建立,但多個程序都可以訪問。共享記憶體是最快的 IPC 方式,它是針對其他程序間通訊方式執行效率低而專門設計的。它往往與其他通訊機制,如訊號兩,配合使用,來實現程序間的同步和通訊。
套接字( socket ) : 套解口也是一種程序間通訊機制,與其他通訊機制不同的是,它可用於不同及其間的程序通訊。
17、談一下對面向物件的理解
18、介面和抽象類的區別
介面和抽象類的概念不一樣,介面是對動作的抽象,抽象類是對根源的抽象。
1、抽象類和介面都不能直接例項化,如果要例項化,抽象類變數必須指向實現所有抽象方法的子類變數,介面變數必須指向實現所有介面方法的類物件。
2、抽象類要被子類繼承,介面要被類實現。
3、介面只能做方法申明,抽象類中可以做方法申明,也可以做方法實現。
4、接口裡定義的變數只能是公共的靜態的常量,抽象類中的變數是普通變數。
5、抽象類裡的抽象方法必須全部被子類所實現,如果子類不能全部實現父類的抽象方法,,那麼該子類只能是抽象類。同樣,一個類實現介面的時候,如不能全部實現介面方法,那麼該類也只能為抽象類。
6、抽象方法只能申明,不能實現,介面是設計的結果,抽象類是重構的結果。
7、抽象類裡可以沒有抽象方法,
8、如果一個類裡有抽象方法,那麼這個類只能是抽象類
9、出現方法要被實現,所以不能是靜態的,也不能是私有的
10、介面可繼承介面,並可多繼承介面,但類只能單根繼承。

1、抽象類 和 介面 都是用來抽象具體物件的. 但是介面的抽象級別最高
2、抽象類可以有具體的方法 和屬性, 介面只能有抽象方法和不可變常量
3、抽象類主要用來抽象類別,介面主要用來抽象功能.
4、抽象類中,且不包含任何實現,派生類必須覆蓋它們。介面中所有方法都必須是未實現的。
18、程序有幾種狀態,分別是什麼?
程序的基本狀態有三種:執行(run)、就緒(ready)、等待(wait)。
執行態(Run):程序佔有處理機資源,正在執行。顯然,在單處理機系統中任一時刻只能有一個程序處理此種狀態。
就緒態(Ready):程序本身具備執行條件,但由於處理機的個數少於可執行程序的個數,暫未投入執行,即相當於等待處理機資源。
等待態(wait):也成掛起態、封鎖態、睡眠態。程序本身不具備執行條件,即使分給他處理機也不執行。程序正等待某一個事件的發生,如等待某一資源被釋放,等待與該執行緒相關的I/O傳輸的完成訊號等。

程序的三個基本狀態之間是可以相互轉換的。具體地說,當一個就緒程序獲得處理機時,其狀態由就緒變為執行;當一個執行程序被剝奪處理機時,如用完系統分給它的時間片、出現更高優先級別的其它程序,其狀態由執行變為就緒;當一個執行程序因某事件受阻時,如所申請資源被佔用、啟動I/O傳輸未完成,其狀態由執行變為等待;當所等待事件發生時,如得到申請資源、I/O傳輸完成,其狀態由等待變為就緒。

19、程序和程式的區別?
1、程式是永存的,程序是暫時的,是程式在資料集上的一次執行,有建立有撤銷,存在是暫時的;
2、程式是靜態的觀念,程序是動態的觀念,
3、程序具有併發性,而程式沒有
4、程序是競爭計算機資源的基本單位、程式不是
5、程序和程式不是一一對應的:一個程式可以對應多個程序即多個程序可以執行同一程式,一個程序可以執行一個或幾個程式。