【面經】三七互娛Java遊戲研發實習(一面)

一、王者榮耀服務器實現

 

二、接口和類的區別?

1、類只能繼承一個類,但可以實現多個接口。

2、對於繼承性,類繼承了父類的方法,子類可以選擇是否覆蓋父類的方法。

3、接口的方法只有聲明,沒有實現;而類中的方法必須有方法體。

 

三、接口可以實例化嗎?爲什麼?那lambda表達式不就是傳入一個接口的實例嗎?

接口不可以實例化,因爲接口可以看成是特殊的抽象類,比抽象類的程度更高,其所有方法都是public abstract類型的,因此不能實例化。

Lambda表達式的原理是:

1、編譯器根據Lambda表達式生成一個私有的靜態函數,這個私有函數就是執行Lambda表達式裏的內容

2、生成一個內部類並調用上述所生成的私有靜態函數

3、將Lambda表達式替換爲該內部類的一個實例傳入方法體中

 

所以本質上來講Lambda表達式並非傳入一個接口的實例,而是傳入一個內部類的實例。

這裏引用一下別人的代碼:

public class LambdaTest {
    public static void printString(String s, Print<String> print) {
        print.print(s);
    }
    public static void main(String[] args) {
        printString("test", (x) -> System.out.println(x));
    }
}

@FunctionalInterface
interface Print<T> {
    public void print(T x);
}

反編譯代碼: 

public class LambdaTest {
    public static void PrintString(String s, Print<String> print) {
        print.print(s);
    }

    public static void main(String[] args) {
        PrintString("test", new LambdaTest$$Lambda$1());
    }

    private static void lambda$main$0(String x) {
        System.out.println(x);
    }

    static final class LambdaTest$$Lambda$1 implements Print {
        public void print(Object obj) {
            LambdaTest.lambda$main$0((String) obj);
        }
        private LambdaTest$$Lambda$1() {
        }
    }
}

@FunctionalInterface
interface Print<T> {
    public void print(T x);
}

 

四、可以在static方法內部調用非靜態方法嗎

static內部不能直接調用該類的非靜態方法,但是如果通過傳入一個對象的引用到靜態方法,然後通過該引用就可以調用非靜態方法和非靜態成員了。

 

五、可以在子類重寫父類的static方法嗎

邏輯上可以,編譯可以通過,但在父類引用指向子類對象的時候,通過父類引用調用的仍然是父類的類方法,而不是子類的。

public class StaticFather {
    public static void print() {
        System.out.println("I am father");
    }
}

public class StaticSon extends StaticFather {
    public static void print() {
        System.out.println("I am son");
    }
}

public class Main {
    public static void main(String[] args) {
        StaticFather A = new StaticFather();
        StaticFather B = new StaticSon();
        StaticSon C = new StaticSon();
        A.print();
        B.print();
        C.print();
    }
}
Result:
I am father
I am father    //通過父類引用調用靜態方法時,仍然是父類的靜態方法,而非子類的
I am son

 

六、Hashmap底層實現及工作過程

底層採用數組+鏈地址表法實現,jdk1.8後當鏈地址表的長度超過8時會用一棵紅黑樹來代替。

put方法工作過程:

1、調用hashcode()函數得到其哈希值

2、根據散列函數(n-1)&hashcode來計算數組下標

3、如果定位到的數組位置沒有元素就直接插入;如果有元素就檢查該元素是否重複,重複就覆蓋,否則判斷該數組位置是存放紅黑樹還是鏈表,紅黑樹就調用相應的方法插入,鏈表就在鏈表尾部插入

 

七、LinkedHashmap如何保證有序

LinkedHashMap繼承了HashMap,並定義了一個繼承自HashMap.Node的Entry靜態內部類,這個類記錄了節點的上一個節點和下一個節點,因此在插入數據的時候,就會綁定在上一個最後插入的節點後面來保證有序。

 

八、線程狀態有哪些

新建(new)

就緒(Runnable)

阻塞(Blocked)

死亡(Dead)

 

九、sleep()和wait()有什麼區別?調用後線程的狀態一致嗎? 

sleep()是Thread類的方法,可使線程暫定指定的時間,進入阻塞狀態,指定時間過後會變爲就緒態;sleep()可以在任何地方調用,且必須捕獲異常,如果不捕獲異常,當產生InterruptedException異常時該線程就會進入死亡狀態。

而wait()是Object類的方法,可是線程進入阻塞狀態,同時進入一個和該對象相關的等待池中,同時釋放掉持有的對象鎖;wait()方法只能在synchronized同步塊中調用,不需要捕獲異常。

兩者都會進入阻塞狀態。

 

九、java線程池怎麼實現

線程池是指管理一組同構工作線程的資源池,線程池的實現與工作隊列密切相關,在工作隊列中保存了所有等待執行的任務,工作者線程的任務就是從工作隊列中獲取一個任務、執行任務,然後返回線程池中並等待下一個任務的分配。

當工作隊列被填滿後,開始執行飽和策略:

1、中止(Abort)策略:拋出未檢查的RejectedExecutionException,調用者可以捕獲這個異常,並根據需求編寫處理代碼。

2、調用者運行(Caller-Runs)策略:將某些任務回退到調用者,降低新任務的流量。

3、拋棄(Discard)策略:直接拋棄該任務。

4、拋棄最舊(Discard-Oldest)策略:拋棄下一個被執行的任務,然後嘗試重新提交新的任務(如果是優先級隊列則會拋棄優先級最高的)。

 

十、算法

1、打亂一個數組

public class UpsetArray {
    public static void upset(int []array, int n) {
        Random random = new Random();
        int temp;
        int randomNum;
        for (int i = 0; i < n; i++) {
            randomNum = random.nextInt(n - i) + i;
            temp = array[randomNum];
            array[randomNum] = array[i];
            array[i] = temp;
        }
    }

    public static void test() {
        int array[] = { 0, 5, 3, 2, 1, 7, 4};
        System.out.println(Arrays.toString(array));
        upset(array, array.length);
        System.out.println(Arrays.toString(array));
    }
}
Result:
[0, 5, 3, 2, 1, 7, 4]
[3, 5, 0, 2, 1, 4, 7]

 

2、歸併排序(見排序算法實現博文)

 

引用:https://blog.csdn.net/jiankunking/article/details/79825928 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章