最全的java後端面試題

包含java基礎,前端基礎,計算機網絡基礎,以及安全基礎,linux 你想要的面試題這裏都有

javase知識面試題

1.基礎知識

1.Integer與int的區別

int是java提供的8種原始數據類型之一,Java爲每個原始類型提供了封裝類,Integer是java爲int提供的封裝類。

int的默認值爲0,而Integer的默認值爲null,即Integer可以區分出未賦值和值爲0的區別,int則無法表達出未賦值的情況,例如,要想表達出沒有參加考試和考試成績爲0的區別,則只能使用Integer。在JSP開發中,Integer的默認爲null,所以用el表達式在文本框中顯示時,值爲空白字符串,而int默認的默認值爲0,所以用el表達式在文本框中顯示時,結果爲0,所以,int不適合作爲web層的表單數據的類型。

另外,Integer提供了多個與整數相關的操作方法,例如,將一個字符串轉換成整數,Integer中還定義了表示整數的 最大值和最小值的常量。

2.== 和 equals 的區別是什麼?

  • 基本類型:比較的是值是否相同;
  • 引用類型:比較的是地址是否相同;
String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true
System.out.println(x==z); // false
System.out.println(x.equals(y)); // true
System.out.println(x.equals(z)); // true

equals 本質上就是 ==,只不過 String 和 Integer 等重寫了 equals 方法

那問題來了,兩個相同值的 String 對象,爲什麼返回的是 true?代碼如下:

String s1 = new String("老王");
String s2 = new String("老王");
System.out.println(s1.equals(s2)); // true

equals的底層代碼如下:

 public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        } else {
            if (anObject instanceof String) {
                String aString = (String)anObject;
                if (this.coder() == aString.coder()) {
                    return this.isLatin1() ? StringLatin1.equals(this.value, aString.value) : StringUTF16.equals(this.value, aString.value);
                }
            }

            return false;
        }
    }

@HotSpotIntrinsicCandidate
public static boolean equals(byte[] value, byte[] other) {
    if (value.length == other.length) {
        for(int i = 0; i < value.length; ++i) {
            if (value[i] != other[i]) {
                return false;
            }
        }

        return true;
    } else {
        return false;
    }
}

3.兩個對象的 hashCode()相同,則 equals()也一定爲 true,對嗎?

不對,兩個對象的 hashCode()相同,equals()不一定 true。

String str1 = "通話";
String str2 = "重地";
System.out.println(String.format("str1:%d | str2:%d",  str1.hashCode(),str2.hashCode()));
System.out.println(str1.equals(str2));
public int hashCode() {
    int h = this.hash;
    if (h == 0 && this.value.length > 0) {
        this.hash = h = this.isLatin1() ? StringLatin1.hashCode(this.value) : StringUTF16.hashCode(this.value);
    }
    return h;
}
public static int hashCode(byte[] value) {
    int h = 0;
    int length = value.length >> 1;

    for(int i = 0; i < length; ++i) {
        h = 31 * h + getChar(value, i);
    }
    return h;
}

運行結果

str1:1179395 | str2:1179395

false

代碼解讀:很顯然“通話”和“重地”的 hashCode() 相同,然而 equals() 則爲 false,因爲在散列表中,hashCode()相等即兩個鍵值對的哈希值相等,然而哈希值相等,並不一定能得出鍵值對相等。

3.1 hashcode是什麼?

​ 答:就是在hash表中對應的位置

​ hashcode是通過hash函數得來的,通俗的說,就是通過某一種算法得到的

​ 通過對象的內存地址(也就是物理地址)轉換成一個整數,然後該整數通過hash函數的算法得到hashcode

3.1.1加法hash

所謂的加法Hash就是把輸入元素一個一個的加起來構成最後的結果。

這裏的prime是任意的質數,看得出,結果的值域爲[0,prime-1]。

static int additiveHash(String key, int prime)
 {
  int hash, i;
  for (hash = key.length(), i = 0; i < key.length(); i++)
   hash += key.charAt(i);
  return (hash % prime);
 }

4.2*8的最高效率算法

使用位運算來實現效率最高。位運算符是對操作數以二進制比特位爲單位進行操作和運算,操作數和結果都是整型 數。

對於位運算符“<<”, 是將一個數左移n位,就相當於乘以了2的n次方,那麼,一個數乘以8只要將其左移3位即可,位運算cpu直接支持的,效率最高。所以,2乘以8等於幾的最效率的方法是2 << 3

5.final和abstract關鍵字的作用

final和abstract是功能相反的兩個關鍵字,可以對比記憶

abstract可以用來修飾類和方法,不能用來修飾屬性和構造方法;

使用abstract修飾的類是抽象類,需要被繼承,使用abstract修飾的方法是抽象方法,需要子類被重寫。

final可以用來修飾類、方法和屬性,不能修飾構造方法。

使用final修飾的類不能被繼承,使用final修飾的方法不能被重寫,使用final修飾的變量的值不能被修改,所以就成了常量。

特別注意:final修飾基本類型變量,其值不能改變,由原來的變量變爲常量;但是final修飾引用類型變量,棧內存中的引用不能改變,但是所指向的堆內存中的對象的屬性值仍舊可以改變。

3. final 在 java 中有什麼作用?

  • final 修飾的類叫最終類,該類不能被繼承。

  • final 修飾的方法不能被重寫。

  • final 修飾的變量叫常量,常量必須初始化,初始化之後值就不能被修改。

4.final、finally、finalize的區別

final修飾符(關鍵字)如果一個類被聲明爲final,意味着它不能再派生出新的子類,不能作爲父類被繼承例如:String 類、Math類等。將變量或方法聲明爲final,可以保證它們在使用中不被改變。被聲明爲final的變量必須在聲明時給定初值,而在以後的引用中只能讀取,不可修改。被聲明爲final的方法也同樣只能使用,不能重寫,但是能夠重載。使用final修飾的對象,對象的引用地址不能變,但是對象的值可以變!

finally在異常處理時提供 finally 塊來執行任何清除操作。如果有finally的話,則不管是否發生異常,finally語句都會被執行。一般情況下,都把關閉物理連接(IO流、數據庫連接、Socket連接)等相關操作,放入到此代碼塊中.。

finalize方法名。Java 技術允許使用 finalize() 方法在垃圾收集器將對象從內存中清除出去之前做必要清理工作。finalize() 方法是在垃圾收集器刪除對象之前被調用的。它是在 Object 類中定義的,因此所有的類都繼承了它。子類覆蓋 finalize() 方法以整理系統資源或者執行其他清理工作。 一般情況下,此方法由JVM調用,程序員不要去調用!

5.寫出java.lang.Object類的六個常用方法

(1) public boolean equals(java.lang.Object)比較對象的地址值是否相等,如果子類重寫,則比較對象的內容是否相等;

(2) public native int hashCode() 獲取哈希碼

(3) public java.lang.String toString() 把數據轉變成字符串

(4) public final native java.lang.Class getClass() 獲取類結構信息

(5) protected void finalize() throws java.lang.Throwable垃圾回收前執行的方法

(6)protected native Object clone() throws java.lang.CloneNotSupportedException 克 隆

(7)public final void wait() throws java.lang.InterruptedException多線程中等待功能

(8)public final native void notify() 多線程中喚醒功能

(9)public final native void notifyAll() 多線程中喚醒所有等待線程的功能

6. java 中的 Math.round(-1.5) 等於多少?

等於 -1,因爲在數軸上取值時,中間值(0.5)向右取整,所以正 0.5 是往上取整,負 0.5 是直接捨棄。

7. String 屬於基礎的數據類型嗎?

String 不屬於基礎類型

基礎類型有 8 種:byte、boolean、char、short、int、float、long、double,

而 String 屬於對象。

8. java 中操作字符串都有哪些類?它們之間有什麼區別?

操作字符串的類有:String、StringBuffer、StringBuilder。

String 聲明的是不可變的對象,每次操作都會生成新的 String 對象,然後將指針指向新的 String 對象

StringBuffer、StringBuilder 可以在原有對象的基礎上進行操作,所以在經常改變字符串內容的情況下最好不要使用 String。

StringBuffer 和 StringBuilder 最大的區別在於

  • StringBuffer 是線程安全的,

  • 而 StringBuilder 是非線程安全的,

  • 但 StringBuilder 的性能卻高於 StringBuffer,

所以在單線程環境下推薦使用 StringBuilder,多線程環境下推薦使用 StringBuffer。

7. String str="i"與 String str=new String(“i”)一樣嗎?

不一樣,因爲內存的分配方式不一樣。String str="i"的方式,java 虛擬機會將其分配到常量池中;

而 String str=new String(“i”) 則會被分到堆內存中。

8. 如何將字符串反轉?

使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。

// StringBuffer reverse
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("abcdefg");
System.out.println(stringBuffer.reverse()); // gfedcba
// StringBuilder reverse
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("abcdefg");
System.out.println(stringBuilder.reverse()); // gfedcba

9. String 類的常用方法都有那些?

indexOf():返回指定字符的索引。
charAt():返回指定索引處的字符。
replace():字符串替換。
trim():去除字符串兩端空白。
split():分割字符串,返回一個分割後的字符串數組。
getBytes():返回字符串的 byte 類型數組。
length():返回字符串長度。
toLowerCase():將字符串轉成小寫字母。
toUpperCase():將字符串轉成大寫字符。
substring():截取字符串。
equals():字符串比較

10.抽象類必須要有抽象方法嗎?

不需要,抽象類不一定非要有抽象方法。

示例代碼:

abstract class Cat {
    public static void sayHi() {
        System.out.println("hi~");
    }
}

11.普通類和抽象類有哪些區別?

  • 普通類不能包含抽象方法,抽象類可以包含抽象方法。
  • 抽象類不能直接實例化,普通類可以直接實例化。

12.抽象類能使用 final 修飾嗎?

不能,定義抽象類就是讓其他類繼承的,如果定義爲 final 該類就不能被繼承,這樣彼此就會產生矛盾,所以 final 不能修飾抽象

13.接口和抽象類有什麼區別?

實現:抽象類的子類使用 extends 來繼承;接口必須使用 implements 來實現接口。
構造函數:抽象類可以有構造函數;接口不能有。
main 方法:抽象類可以有 main 方法,並且我們能運行它;接口不能有 main 方法。
實現數量:類可以實現很多個接口;但是隻能繼承一個抽象類。
訪問修飾符:接口中的方法默認使用 public 修飾;抽象類中的方法可以是任意訪問修飾符。

java.sql.Date和java.util.Date的聯繫和區別

1) java.sql.Date是java.util.Date的子類,是一個包裝了毫秒值的瘦包裝器,允許 JDBC 將毫秒值標識爲 SQL DATE 值。毫秒值表示自 1970 年 1 月 1 日 00:00:00 GMT 以來經過的毫秒數。 爲了與 SQL DATE 的定義一致,由java.sql.Date 實例包裝的毫秒值必須通過將時間、分鐘、秒和毫秒設置爲與該實例相關的特定時區中的零來“規範化”。 說白了,java.sql.Date就是與數據庫Date相對應的一個類型,而java.util.Date是純java的Date。

2) JAVA裏提供的日期和時間類java.sql.Date和java.sql.Timestamp,只會從數據庫裏讀取某部分值,這有時會 導致丟失數據。例如一個包含2002/05/22 5:00:57 PM的字段,讀取日期時得到的是2002/05/22,而讀取時間時得到的是5:00:57 PM. 你需要了解數據庫裏存儲時間的精度。有些數據庫,比如MySQL,精度爲毫秒,然而另一些數據

庫,包括Oracle,存儲SQL DATE類型數據時,毫秒部分的數據是不保存的。

以下操作中容易出現不易被發現的BUG:獲得一個JAVA裏的日期對象。 從數據庫裏讀取日期 , 試圖比較兩個日期對 象是否相等。如果毫秒部分丟失,本來認爲相等的兩個日期對象 , 用Equals方法可能返回false。.sql.Timestamp類比java.util.Date類精確度要高。

java.sql.Date 和java.util.Date 最大的不同在於java.sql.Date 只記錄日期,而沒有具體這一天的時間。所以舉例來說,如果當前是2009-12-24 23:20,你創建一個 java.sql.Date 將只記下2009-12-24這個信息。若你需要保留時間進行JDBC操作,請使用 java.sql.Timestamp 代替。

總之,java.util.Date 就是Java的日期對象,而java.sql.Date 是針對SQL語句使用的,只包含日期而沒有時間部分。

14 java 中 IO 流分爲幾種?

按功能來分:輸入流(input)、輸出流(output)。

按類型來分:字節流和字符流。

字節流和字符流的區別是:字節流按 8 位傳輸以字節爲單位輸入輸出數據,字符流按 16 位傳輸以字符爲單位輸入輸出數據。

15. Files的常用方法都有哪些?

Files.exists():檢測文件路徑是否存在。
Files.createFile():創建文件。
Files.createDirectory():創建文件夾。
Files.delete():刪除一個文件或目錄。
Files.copy():複製文件。
Files.move():移動文件。
Files.size():查看文件個數。
Files.read():讀取文件。
Files.write():寫入文件。

18. &和&&的區別?

雖然二者都要求運算符左右兩端的布爾值都是true整個表達式的值纔是true。

&&之所以稱爲短路運算是因爲,如果&&左邊的表達式的值是false,右邊的表達式會被直接短路掉,不會進行運算。很多時候我們可能都需要用&&而不是&,例如在驗證用戶登錄時判定用戶名不是null而且不是空字符串,應當寫爲:username != null &&!username.equals(“”),二者的順序不能交換,更不能用&運算符,因爲第一個條件如果不成立,根本不能進行字符串的equals比較,否則會產生NullPointerException異常。注意:邏輯或運算符(|)和短路或運算符(||)的差別也是如此。

19. switch 是否能作用在byte 上,是否能作用在long 上,是否能作用在String上?

可以是byte、short、char、int、enum、String類型,但是long類型不能

20、構造器(constructor)是否可被重寫(override)?

答:構造器不能被繼承,因此不能被重寫,但可以被重載。

Java 重載與重寫是什麼?有什麼區別?

答:

重載(Overload)是讓類以統一的方式處理不同類型數據的一種手段,實質表現就是多個具有不同的參數個數或者類型的同名函數(返回值類型可隨意,不能以返回類型作爲重載函數的區分標準)同時存在於同一個類中,是一個類中多態性的一種表現(調用方法時通過傳遞不同參數個數和參數類型來決定具體使用哪個方法的多態性)。

重寫(Override)是父類與子類之間的多態性,實質是對父類的函數進行重新定義,如果在子類中定義某方法與其父類有相同的名稱和參數則該方法被重寫,不過子類函數的訪問修飾權限不能小於父類的;若子類中的方法與父類中的某一方法具有相同的方法名、返回類型和參數表,則新方法將覆蓋原有的方法,如需父類中原有的方法則可使用 super 關鍵字。

21.是否可以繼承String類?

答:String 類是final類,不可以被繼承。

24. String s = new String(“xyz”);創建了幾個字符串對象?

答:兩個對象,一個是靜態區的”xyz”,一個是用new創建在堆上的對象。

線程同步和互斥的區別

  1. 互斥是指某一資源同時只允許一個訪問者對其進行訪問,具有唯一性和排它性。但互斥無法限制訪問者對資源的訪問順序,即訪問是無序的。
  2. 同步是指在互斥的基礎上(大多數情況),通過其它機制實現訪問者對資源的有序訪問。
  3. 同步其實已經實現了互斥,所以同步是一種更爲複雜的互斥。
  4. 互斥是一種特殊的同步。

所謂互斥,就是不同線程通過競爭進入臨界區(共享的數據和硬件資源),爲了防止訪問衝突,在有限的時間內只允許其中之一獨佔性的使用共享資源。如不允許同時寫

同步關係則是多個線程彼此合作,通過一定的邏輯關係來共同完成一個任務。一般來說,同步關係中往往包含互斥,同時對臨界區的資源會按照某種邏輯順序進行訪問。如先生產後使用

總的來說,兩者的區別就是:
互斥是通過競爭對資源的獨佔使用,彼此之間不需要知道對方的存在,執行順序是一個亂序。
同步是協調多個相互關聯線程合作完成任務,彼此之間知道對方存在,執行順序往往是有序的。

lock與unlock方法,替換synchronized,這就是互斥鎖的體現。消費者生產者模式就是同步鎖的體現。

25. 什麼是反射?

反射是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲 Java 語言的反射機制。

26. 什麼是 Java 序列化?什麼情況下需要序列化?

Java 序列化是爲了保存各種對象在內存中的狀態,並且可以把保存的對象狀態再讀出來。

以下情況需要使用 Java 序列化:

  • 想把的內存中的對象狀態保存到一個文件中或者數據庫中時候;
  • 想用套接字在網絡上傳送對象的時候;
  • 想通過RMI(遠程方法調用)傳輸對象的時候。

27.幾個常用的設計模式:

創建型

  • 工廠模式與抽象工廠模式 (Factory Pattern)(Abstract Factory Pattern)
  • 單例模式 (Singleton Pattern)
  • 建造者模式 (Builder Pattern)
  • 原型模式 (Prototype Pattern)

結構型

  • 適配器模式 (Adapter Pattern)
  • 裝飾器模式 (Decorator Pattern)
  • 橋接模式 (Bridge Pattern)
  • 外觀模式 (Facade Pattern)
  • 代理模式 (Proxy Pattern)
  • 過濾器模式 (Filter、Criteria Pattern)
  • 組合模式 (Composite Pattern)
  • 享元模式 (Flyweight Pattern)

行爲型

  • 責任鏈模式(Chain of Responsibility Pattern)

  • 觀察者模式(Observer Pattern)

  • 模板模式(Template Pattern)

  • 命令模式(Command Pattern)

  • 解釋器模式(Interpreter Pattern)

  • 迭代器模式(Iterator Pattern)

  • 中介者模式(Mediator Pattern)

  • 策略模式(Strategy Pattern)

  • 狀態模式(State Pattern)

  • 備忘錄模式(Memento Pattern)

  • 空對象模式(Null Object Pattern)

**工廠模式:**工廠類可以根據條件生成不同的子類實例,這些子類有一個公共的抽象父類並且實現了相同的方法,但是這些方法針對不同的數據進行了不同的操作(多態方法)。當得到子類的實例後,開發人員可以調用基類中的方法而不必考慮到底返回的是哪一個子類的實例。

**代理模式:**給一個對象提供一個代理對象,並由代理對象控制原對象的引用。

**適配器模式:**把一個類的接口變換成客戶端所期待的另一種接口,從而使原本因接口不匹配而無法在一起使用的類能夠一起工作。

**單例模式:**一個類只有一個實例,即一個類只有一個對象實例。

img

28.try{}裏有一個return語句,那麼緊跟在這個try後的finally{}裏的代碼會不會被執行,什麼時候被執行,在return前還是後?

會執行,在方法返回調用者前執行。

在finally中改變返回值的做法是不好的,因爲如果存在finally代碼塊,try中的return語句不會立馬返回調用者,而是記錄下返回值待finally代碼塊執行完畢之後再向調用者返回其值,然後如果在finally中修改了返回值,就會返回修改後的值

29.簡單工廠和抽象工廠有什麼區別?

  • 簡單工廠:用來生產同一等級結構中的任意產品,對於增加新的產品,無能爲力。
  • 工廠方法:用來生產同一等級結構中的固定產品,支持增加任意產品。
  • 抽象工廠:用來生產不同產品族的全部產品,對於增加新的產品,無能爲力;支持增加產品族。

2.異常

1.什麼是異常?

異常是發生在程序執行過程中阻礙程序正常執行的錯誤事件。比如:用戶輸入錯誤數據、硬件故障、網絡阻塞等都會導致出現異常。 只要在Java語句執行中產生了異常,一個異常對象就會被創建,JRE就會試圖尋找異常處理程序來處理異常。如果有合適的異常處理程序,異常對象就會被異常處理程序接管,否則,將引發運行環境異常,JRE終止程序執行。 Java異常處理框架只能處理運行時錯誤,編譯錯誤不在其考慮範圍之內。

2.常見的異常

NullPointException:空指針異常,對象是null時會拋出,在調用傳入對象時儘量判斷是否爲null,Jdk8裏面可以用Optional對象來避免

IndexOutOfBoundsException:數組下標越界,數組的下標超過了最大值時會拋出,在迭代循環時檢查下標是否越界

NumberFormatException:數字類型轉化異常,將非數字類型轉成數字類型,將類型轉化的代碼catch住

ClassCastException:類型轉換異常,發生在強轉時,將不同類型轉成同一類型,儘量少用強轉,或用instanceof(判斷繼承中子類的實例是否是父類的實現)做類型判斷,或多用泛型

FileNotFoundException:找不到指定文件,文件路徑錯誤或文件不存在,可能用了絕對路徑檢查文件是否存在,路徑是否寫錯,多用相對路徑

ClassNotFoundException:在classpath中找不到引用的類缺乏引用當前類的jar或沒有設置classpath或jar損壞-,找到jar並放入classpath中或檢查jar是否損壞

OutOfMemoryError:內存溢出異常,產生對象太多,內存不夠->不要在循環體重創建大量對象,或對象及時回收,增大初始化堆:-Xms 增加最大值:-Xmx

NoClassDefFoundError:找不到相應的類錯誤,缺乏當前引用類的jar或jar版本不對->找到jar並放入classpath中或找到合適的版本

ConcurrentModificationException:併發修改異常,在集合迭代時修改裏面的元素->在迭代時不要修改集合或用併發集合做遍歷(如:ConcurrentHashMap)

NoSuchMethodError:類裏找不到相應的方法,一般是jar版本不對,當前引用的jar版本中沒有這個方法->檢查jar版本是否正確

UnsupportedClassVersionError:版本不支持錯誤,編譯class的jdk和運行時候的jdk版本不一致或比較高->將低版本換成高版本

StackOverflowError:棧溢出錯誤,一般是函數的死循環,或遞歸調用無法退出->檢查死循環的代碼,或讓遞歸有退出值,或加大棧初始化參數

編譯時異常和運行時異常的區別

最簡單的說法:

javac出來的異常就是編譯時異常,就是說把源代碼編譯成字節碼(class)文件時報的異常,一般如果用Eclispe,你敲完代碼保存的時候就是編譯的時候。Java出來的異常就是運行時異常

Java異常可分爲3種:

(1)編譯時異常:Java.lang.Exception

(2)運行期異常:Java.lang.RuntimeException

(3)錯誤:Java.lang.Error

編譯時異常: 程序正確,但因爲外在的環境條件不滿足引發。例如:用戶錯誤及I/O問題----程序試圖打開一個並不存在的遠程Socket端口。這不是程序本身的邏輯錯誤,而很可能是遠程機器名字錯誤(用戶拼寫錯誤)。對商用軟件系統,程序開發者必須考慮並處理這個問題。Java編譯器強制要求處理這類異常,如果不捕獲這類異常,程序將不能被編譯。

運行期異常: 這意味着程序存在bug,如數組越界,0被除,入參不滿足規範…這類異常需要更改程序來避免,Java編譯器強制要求處理這類異常。

錯誤: 一般很少見,也很難通過程序解決。它可能源於程序的bug,但一般更可能源於環境問題,如內存耗盡。錯誤在程序中無須處理,而有運行環境處理。

3.java中兩種處理方式

一:通過try catch捕捉異常,順序上先處理子類異常後父類,非特殊情況catch中不能爲空,要處理錯誤,避免使用catch all(不能很準確的定位錯誤),儘量用catch,jdk7中可以處理並列異常,這個方法雖然簡潔,但是也不夠好(不同異常處理方法是一致的;多個異常間必須是平級關係)。清理數據必須放到finally裏面,無論是否拋異常,或try中有return語句,finally裏面的代碼一定會執行,除非碰到System.exit(0)(艾克色特)

二:通過throws拋出異常,子類方法中拋出的異常應該是與父類方法中相同,或是父類異常的子類;一定要在main方法裏面處理異常,不然jvm可能就退出了

4.常見方法

getMessage:錯誤信息的字符串解釋

getCause:返回異常產生的原因,一般是原始異常如果不知道原因返回null

printStackTrace:打印異常出現的位置或原因

toString:返回String格式的Throwable信息,此信息包括Throwable的名字和本地化信息

initCause:初始化原始異常

PrintStream和PrintWriter作爲產生實現重載,這樣就能實現打印棧軌跡到文件或流中

5.如何自定義異常

繼承Exception是檢查性異常,繼承RuntimeException是非檢查性異常,

一般要複寫兩個構造方法,用throw拋出新異常

如果同時有很多異常拋出,那可能就是異常鏈,就是一個異常引發另一個異常,另一個異常引發更多異常,一般我們會找它的原始異常來解決問題,一般會在開頭或結尾,異常可通過initCause串起來,可以通過自定義異常

3.文件File,io流

常見方法:

通過將給定路徑來創建一個新File實例。
new File(String pathname);   
根據parent路徑名字符串和child路徑名創建一個新File實例。parent是指上級目錄的路徑,完整的路徑爲parent+child.
new File(String parent, String child);      
根據parent抽象路徑名和child路徑名創建一個新File實例。 parent是指上級目錄的路徑,完整的路徑爲parent.getPath()+child.
說明:如果指定的路徑不存在(沒有這個文件或是文件夾),不會拋異常,這時file.exists()返回false。
new File(File parent, String child);

創建:

//在指定位置創建一個空文件,成功就返回true,如果已存在就不創建然後返回false
createNewFile() 
//在指定位置創建目錄,這隻會創建最後一級目錄,如果上級目錄不存在就拋異常。
mkdir()
//在指定位置創建目錄,這會創建路徑中所有不存在的目錄。
mkdirs()
//重命名文件或文件夾,也可以操作非空的文件夾,文件不同時相當於文件的剪切,剪切時候不能操作非空的文件夾。移動/重命名成功則返回true,失敗則返回false。
renameTo(File dest)

刪除:

//刪除文件或一個空文件夾,如果是文件夾且不爲空,則不能刪除,成功返回true,失敗返回false。
delete() 
//在虛擬機終止時,請求刪除此抽象路徑名錶示的文件或目錄,保證程序異常時創建的臨時文件也可以被刪除
deleteOnExit()  

判斷:

//文件或文件夾是否存在。
exists()
//是否是一個文件,如果不存在,則始終爲false。
isFile()
//是否是一個目錄,如果不存在,則始終爲false。
isDirectory()
//是否是一個隱藏的文件或是否是隱藏的目錄。
isHidden()
//測試此抽象路徑名是否爲絕對路徑名。
isAbsolute()

獲取:

//獲取文件或文件夾的名稱,不包含上級路徑。
getName()
//返回絕對路徑,可以是相對路徑,但是目錄要指定
getPath()
//獲取文件的絕對路徑,與文件是否存在沒關係
getAbsolutePath()
// 獲取文件的大小(字節數),如果文件不存在則返回0L,如果是文件夾也返回0L。
length()
//返回此抽象路徑名父目錄的路徑名字符串;如果此路徑名沒有指定父目錄,則返回null。
getParent()
//獲取最後一次被修改的時間。
lastModified()
//列出所有的根目錄(Window中就是所有系統的盤符)
staic File[] listRoots()
//返回目錄下的文件或者目錄名,包含隱藏文件。對於文件這樣操作會返回null。
list() 
//返回指定當前目錄中符合過濾條件的子文件或子目錄。對於文件這樣操作會返回null。
list(FilenameFilter filter) 
//返回目錄下的文件或者目錄對象(File類實例),包含隱藏文件。對於文件這樣操作會返回null。
listFiles()
//返回指定當前目錄中符合過濾條件的子文件或子目錄。對於文件這樣操作會返回null
listFiles(FilenameFilter filter)

題目:

1.在指定的路徑下新建一個 .txt 文件 “test.txt”,利用程序在文件中寫入如下內容:

private static void work1() throws IOException {
        /**要求:
        //創建文件
        File file = new File("test.txt");
        file.createNewFile();
        //寫入數據
        FileWriter fwriter = new FileWriter(file);
        fwriter.write("Java是一種可以撰寫跨平臺應用軟件的面向對象的程序設計語言.....");
        fwriter.close();
}

2.利用程序讀取 test.txt 文件的內容, 並在控制檯打印

 private static void work2() throws IOException {
        /**
         * 2.利用程序讀取 test.txt 文件的內容, 並在控制檯打印
         */
        FileReader reader = new FileReader("test.txt");
        //採用數組來緩衝
        char[] c =new char[100];
        int len ;
        while ((len=reader.read(c))!=-1){
            //遍歷數組
            for (int i=0;i<len;i++) {
                System.out.print(c[i]);
            }
		}
 }

3.利用程序複製 test.txt 爲 test1.txt

private static void work3() throws IOException {
         /*
        3.利用程序複製 test.txt 爲 test1.txt
        因爲文件類型是文本文檔,所以選擇字符流
         */
        File file = new File("test.txt");
        FileReader fr = new FileReader(file);
        FileWriter fw = new FileWriter("test1.txt");
        int len;
        char[] chars = new char[10];
        while((len = fr.read(chars)) != -1){
            fw.write(chars, 0, len);
        }
        fr.close();
        fw.close();
}

4.列出當前目錄下全部java文件的名稱

 private static void work4() {
        /**
         * 4.列出當前目錄下全部java文件的名稱
         * 考點:
         *    1.File的list/listFile方法
         *    2.過濾器FilenameFilter/Filefilter
         */
        File file = new File("D:\\AAtemp");
        String[] filenames = file.list(new FilenameFilter() {
            @Override
            //accept會對文件夾的每一個子文件夾進行檢測
            public boolean accept(File dir, String name) {
                return name.endsWith(".java");
            }
        });
        for (String f :filenames)
            System.out.println(f);
}

5.列出workspace目錄下.class的名字及其大小,並刪除其中的一個?

 private static void work5() {
        //列出workspace目錄下.class的名字及其大小,並刪除其中的一個?
        File file = new File("C:\\Test");
        File file2 = new File("C:\\Test\\WiFi_Log.txt");
        File[] file1 = file.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return name.endsWith(".txt");
            }
        });
        file2.delete();
        for (File l : file1) {
            System.out.println(l);
        }
    }

6.使用File類下的常用方法獲取某些文件的信息

private static void work6() {
/**
* 6.使用File類下的常用方法獲取某些文件的信息
* 常用方法:getName;getAbsolutePath,getPath.lenth.isFile,isDirectory
*/
File file = new File(“test.txt”);
file.getAbsolutePath();

}

集合面試題

集合的基本概念:

​ 1.一個存儲對象的容器
​ 2.集合只能存放對象
​ 3.集合存放的都是多個不同類型的對象
​ 4.對象本身還是存放在堆內存中

  • List集合的使用:
    1.List:有序的,可重複,有下標
    2.ArrayList:數組結構,帶下標,長度可變,查詢速度快,增刪速度較慢
    3.LinkedList:鏈表結構,查詢速度較慢,增刪速度快

  • map集合的使用:
    1.Map 集合一一對應(鍵值對)
    2.基於數組和鏈表進行存儲數據
    3.HashMap 類按哈希算法來存取鍵對象
    4.TreeMap是基於紅黑樹實現的,適用於按自然順序遍歷key。

  • Set集合的使用:
    1.Set集合 無序,不重複
    2.無序性(沒下標),不能用for循環去遍歷,可以用迭代器
    3.HashSet:增加刪除時效率高
    4.LinkedHashSet:查看檢索時,效率比較高

一些不太常用的Map集合:TreeMap,HashTable

題目:已知數組存放一批QQ號碼,QQ號碼最。

長爲11位,最短爲5位String[] strs =
{“12345”,“67891”,“12347809933”,“98765432102”,“67891”,“12347809933”}。
將該數組裏面的所有qq號都存放在LinkedList中,將list中重複元素刪除,將list中所有元素分別用迭代器打印出來

public static void main(String[] args) {
	//定義數組存放qq號碼
	String[] strs = {"12345","67891","12347809933","98765432102","67891","12347809933"};
	
	//定義LinkedList
	LinkedList<String> lis = new LinkedList<String>();
	
	//c.增強for循環遍歷數組存到集合
	for(String value : strs){
		lis.add(value);
	}
	
	//d.依次拿到每一個與這一個後的元素進行比較,如果相同則移除
	for(int i = 0; i < lis.size();i++)
		for(int j = i + 1;j < lis.size();j++)
			if(lis.get(i).equals(lis.get(j)))
				lis.remove(j);
	//e.用迭代器遍歷集合
	for(Iterator<String> it = lis.iterator();it.hasNext();){
		String value = it.next();
		System.out.println(value + "\t");
}

1.常見的集合有哪些?

答:Map接口和Collection接口是所有集合框架的父接口

  • Collection接口的子接口包括:Set接口和List接口

  • Map接口的實現類主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等

  • Set接口的實現類主要有:HashSet、TreeSet、LinkedHashSet等

  • List接口的實現類主要有:ArrayList、LinkedList、Stack以及Vector等

2. HashMap 和 Hashtable 有什麼區別?

  • hashMap去掉了HashTable 的contains方法,但是加上了containsValue()和containsKey()方法。
  • hashTable同步的,而HashMap是非同步的,效率上比hashTable要高。
  • hashMap允許空鍵值,而hashTable不允許。
  • HashMap沒有考慮同步,是線程不安全的;Hashtable使用了synchronized關鍵字,是線程安全的;
  • HashMap繼承自AbstractMap類;而Hashtable繼承自Dictionary類;

3. HashMap的數據結構是什麼樣子的?

答:HashMap其實也是一個線性的數組實現的,所以可以理解爲其存儲數據的容器就是一個線性數組。這可能讓我們很不解,一個線性的數組怎麼實現按鍵值對來存取數據呢?這裏HashMap有做一些處理。

4. HashMap的存取實現

​ 既然是線性數組,爲什麼能隨機存取?這裏HashMap用了一個小算法,大致是這樣實現:

//存儲時:
int hash = key.hashCode();// 這個hashCode方法這裏不詳述,只要理解每個key的hash是一個固定的int值
int index = hash % Entry[].length;
Entry[index] = value;

//取值時:
int hash = key.hashCode();
int index = hash % Entry[].length*;
return Entry[index];

5. Collection和Collections的區別?

Collection是一個接口,它是Set、List等容器的父接口;

Collections是個一個工具類,提供了一系列的靜態方法來輔助容器操作,這些方法包括對容器的搜索、排序、線程安全化等等。

6. List、Map、Set三個接口存取元素時,各有什麼特點?

  • List以特定索引來存取元素,可以有重複元素。
  • Set不能存放重複元素(用對象的equals()方法來區分元素是否重複)。
  • Map保存鍵值對(key-value pair)映射,映射關係可以是一對一或多對一

7. List、Set、Map 之間的區別是什麼?

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-2UHkZeXv-1593392615161)(C:\Users\33066\AppData\Roaming\Typora\typora-user-images\image-20200617133142870.png)]

8. 數組和鏈表的特點及區別

數組存儲區間是連續的,佔用內存嚴重,故空間複雜的很大。但數組的二分查找時間複雜度小,爲O(1);

​ 數組的特點是:尋址容易,插入和刪除困難;

鏈表存儲區間離散,佔用內存比較寬鬆,故空間複雜度很小,但時間複雜度很大,達O(N)。

​ 鏈表的特點是:尋址困難,插入和刪除容易。

9.遍歷集合的方法

List:

遍歷List方法一:普通for循環

for(int i=0;i<list.size();i++){//list爲集合的對象名
    String temp = (String)list.get(i);
    System.out.println(temp);
}

遍歷List方法二:增強for循環(使用泛型!)

for (String temp : list) {
	System.out.println(temp);
}

遍歷List方法三:使用Iterator迭代器(1)

for(Iterator iter= list.iterator();iter.hasNext();){
    String temp = (String)iter.next();
    System.out.println(temp);
}

遍歷List方法四:使用Iterator迭代器(2)

Iterator  iter =list.iterator();
while(iter.hasNext()){
    Object  obj =  iter.next();
    iter.remove();//如果要遍歷時,刪除集合中的元素,建議使用這種方式!
    System.out.println(obj);
}

Set:

遍歷Set方法一:增強for循環

for(String temp:set){
	System.out.println(temp);
}

遍歷Set方法二:使用Iterator迭代器

for(Iterator iter = set.iterator();iter.hasNext();){
    String temp = (String)iter.next();
    System.out.println(temp);
}

Map:

遍歷Map方法一:根據key獲取value

Map<Integer, Man> maps = new HashMap<Integer, Man>();
Set<Integer>  keySet =  maps.keySet();
for(Integer id : keySet){
	System.out.println(maps.get(id).name);
}

遍歷Map方法二:使用entrySet

Set<Entry<Integer, Man>>  ss = maps.entrySet();
for (Iterator iterator = ss.iterator(); iterator.hasNext();) {
    Entry e = (Entry) iterator.next(); 
    System.out.println(e.getKey()+"--"+e.getValue());
}

線程面試題

並行和併發

並行:在同一個時刻,有多個指令在單個CPU同時執行
併發:在同一個時刻,有多個指令在單個CPU交替執行

進程和線程

進程:正在運行的軟件(就是操作系統中正在運行的一個應用程序)

獨立性:進程是一個能獨立運行的基本單位,同時也是系統分配資源和調度的獨立單位
動態性:進程的實質是程序的一次執行過程,進程是動態產生的,動態消亡的
併發性:任何進程都可以同其他進程一起併發執行(CPU在多個進程之間進行一個動態的切換)

線程:是進程中的單個順序控制流,是一條執行路徑(就是應用程序中做的事情)

  單線程:一個線程如果只有一條執行路徑,則稱爲單線程程序 	
  
  多線程:一個進程如果有多條執行路徑,則成爲多線程程序

1.線程和進程的區別?

一個程序下至少有一個進程,一個進程下至少有一個線程,一個進程下也可以有多個線程來增加程序的執行速度。

2.編寫多線程程序有幾種實現方式?

一種是繼承Thread類;

另一種是實現Runnable接口。

還有一種是callable

兩種方式都要通過重寫run()方法來定義線程的行爲,推薦使用後者,因爲Java中的繼承是單繼承,一個類有一個父類,如果繼承了Thread類就無法再繼承其他類了,顯然使用Runnable接口更爲靈活。

runnable 沒有返回值,callable 可以拿到有返回值,callable 可以看作是 runnable 的補充。

3. synchronized關鍵字的用法?

synchronized關鍵字可以將對象或者方法標記爲同步,

以實現對對象和方法的互斥訪問,可以用synchronized(對象) { … }定義同步代碼塊,或者在聲明方法時將synchronized作爲方法的修飾符。

4.多線程中 synchronized 鎖升級的原理是什麼?

synchronized 鎖升級原理:在鎖對象的對象頭裏面有一個 threadid 字段,在第一次訪問的時候 threadid 爲空,

jvm 讓其持有偏向鎖,並將 threadid 設置爲其線程 id,再次進入的時候會先判斷 threadid 是否與其線程 id 一致,如果一致則可以直接使用此對象,如果不一致,則升級偏向鎖爲輕量級鎖,通過自旋循環一定次數來獲取鎖,執行一定次數之後,如果還沒有正常獲取到要使用的對象,此時就會把鎖從輕量級升級爲重量級鎖,此過程就構成了 synchronized 鎖的升級。

鎖的升級的目的:在 Java 6 之後優化 synchronized 的實現方式,使用了偏向鎖升級爲輕量級鎖再升級到重量級鎖的方式,從而減低了鎖帶來的性能消耗。

5.什麼是死鎖?

當線程 A 持有獨佔鎖a,並嘗試去獲取獨佔鎖 b 的同時,線程 B 持有獨佔鎖 b,並嘗試獲取獨佔鎖 a 的情況下,就會發生 AB 兩個線程由於互相持有對方需要的鎖,而發生的阻塞現象,我們稱爲死鎖。

5.1 死鎖的四個必要條件

互斥條件:一個資源每次只能被一個進程使用,即在一段時間內某 資源僅爲一個進程所佔有。此時若有其他進程請求該資源,則請求進程只能等待。

請求與保持條件:進程已經保持了至少一個資源,但又提出了新的資源請求,而該資源 已被其他進程佔有,此時請求進程被阻塞,但對自己已獲得的資源保持不放。

不可剝奪條件:進程所獲得的資源在未使用完畢之前,不能被其他進程強行奪走,即只能 由獲得該資源的進程自己來釋放(只能是主動釋放)。

循環等待條件: 若干進程間形成首尾相接循環等待資源的關係

這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之一不滿足,就不會發生死鎖。

5.2. 死鎖避免

  • 加鎖順序(線程按照一定的順序加鎖)

  • 加鎖時限(線程嘗試獲取鎖的時候加上一定的時限,超過時限則放棄對該鎖的請求,並釋放自己佔有的鎖)

  • 死鎖檢測

6.在 Java 程序中怎麼保證多線程的運行安全?

  • 方法一:使用安全類,比如 Java. util. concurrent 下的類。
  • 方法二:使用自動鎖 synchronized。
  • 方法三:使用手動鎖 Lock。

7.sleep() 和 wait() 有什麼區別?

  • 類的不同:sleep() 來自 Thread,wait() 來自 Object。

  • 釋放鎖:sleep() 不釋放鎖;wait() 釋放鎖。

  • 用法不同:sleep() 時間到會自動恢復;wait() 可以使用 notify()/notifyAll()直接喚醒。

  • sleep()方法(休眠)是線程類(Thread)的靜態方法,調用此方法會讓當前線程暫停執行指定的時間,將執行機會讓給其他線程,但是對象的鎖依然保持,因此休眠時間結束後會自動恢復。

  • wait()是Object類的方法,調用對象的wait()方法導致當前線程放棄對象的鎖(線程暫停執行),進入對象的等待池,只有調用對象的notify()方法(或notifyAll()方法)時才能喚醒等待池中的線程進入等鎖池(lock pool),如果線程重新獲得對象的鎖就可以進入就緒狀態。

8.線程的sleep()方法和yield()方法有什麼區別?

① sleep()方法給其他線程運行機會時不考慮線程的優先級,因此會給低優先級的線程以運行的機會;yield()方法只會給相同優先級或更高優先級的線程以運行的機會;
② 線程執行sleep()方法後轉入阻塞(blocked)狀態,而執行yield()方法後轉入就緒(ready)狀態;
③ sleep()方法聲明拋出InterruptedException,而yield()方法沒有聲明任何異常;

④ sleep()方法比yield()方法(跟操作系統CPU調度相關)具有更好的可移植性。

9.線程的實現

三種方式的對比:
在這裏插入圖片描述


方式1.繼承Thread類

1> 定義一個類MyThread繼承Thread類
2> 在MyThread類中從寫run()方法
3> 創建MyThread類的對象
4> 啓動線程

/**
 * @program: javase
 * @description: 繼承Thread類實現多線程
 * @Author: 小白白
 * @create: 2019/12/06 - 12:54
 **/
public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++){
            System.out.println("線程開啓了" + i);
        }
    }
}
/**
 * @program: javase
 * @description: 繼承Thread類的測試類
 * @Author: 小白白
 * @create: 2019/12/06 - 12:56
 **/
public class MyThread_text {
    public static void main(String[] args) {
        //創建一個線程對象
        MyThread t1 = new MyThread();
        //創建一個線程對象
        MyThread t2 = new MyThread();
        //開啓一個線程
        t1.start();
        //開啓第二條線程
        t2.start();
    }
}

運行結果爲兩個線程交替運行,交替去爭搶CPU的資源

思考:

爲什麼要重寫run()方法?
----------因爲run()用來封裝被線程執行的代碼

run()方法和start()方法的區別?
---------run:封裝線程執行的代碼,直接調用,相當於普通方法的調用,並沒有開啓線程
---------start:啓動線程,然後由JVM調用此線程的run()方法

start方法的底層源碼。-----------native表示調取本地方法
在這裏插入圖片描述

方式2.實現Runnable接口

1.定義一個類MyRunnable實現Runnable接口
2.在MyRunnable類中重寫run()方法
3.創建Runnable類的對象
4.創建Thread類的對象,把MyRunnbable獨享作爲構造方法的參數
5.啓動線程
package com.zxh.Thread.Demo;

/**
 * @program: javase
 * @description: 實現Runnable接口
 * @Author: 小白白
 * @create: 2019/12/06 - 14:01
 **/
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        //線程啓動後執行的代碼
        for (int i = 0; i < 100;i++){
            System.out.println("第二種方法實現多線程" + i);
        }
    }
}
/**
 * @program: javase
 * @description: 實現Runnable接口的測試類
 * @Author: 小白白
 * @create: 2019/12/06 - 14:03
 **/
public class MyRunnable_test {
    public static void main(String[] args) {
        //創建了一個參數的對象
        MyRunnable myRunnableTest = new MyRunnable();
        //創建了一個線程對象,並把參數傳遞給這個線程
        //在線程啓動之後,執行的就是參數裏面的run方法
        Thread t= new Thread(myRunnableTest);
        //開啓線程
        t.start();
    }
}

方式3:通過Callable接口進行實現

1.定義一個類MyCallable實現Callable接口
2.在MyCallable中重寫call()方法
3.創建MyCallable類的對象
4.創建Future的是實現類FutureTask對象,把MyCallable對象作爲構造方法的參數
5.創建Thread類的對象,把FutureTask對象作爲構造方法的參數
6.啓動線程

/**
 * @program: javase
 * @description: 實現Callable接口
 * @Author: 小白白
 * @create: 2019/12/06 - 18:59
 **/
public class MyCallable implements Callable<Object> {

    //在MyCallable中重寫call()方法
    @Override
    public Object call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println("跟女孩表白" + i);
        }
        //返回值表示線程運行完畢之後的結果
        return "答應";
    }
}
import java.util.concurrent.*;

/**
 * @program: javase
 * @description: 實現Callable接口的測試類
 * @Author: 小白白
 * @create: 2019/12/06 - 19:03
 **/
public class MyCallable_text {
    public static void main(String[] args) {
        //創建MyCallable類的對象
        MyCallable mc = new MyCallable();
        //創建Future的是實現類FutureTask對象,把MyCallable對象作爲構造方法的參數
        FutureTask<Object> ft = new FutureTask<Object>(mc);
        //創建Thread類的對象,把FutureTask對象作爲構造方法的參數
        Thread t1 = new Thread(ft);
        //啓動線程
        t1.start();
    }
}


jvm面試題

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-vwqnG36f-1593392615163)(C:\Users\33066\AppData\Local\YNote\data\qq1510077B57FCC357FB1D93412160B8DD\406a7d00b17e49379c10e79f6ed61b17\clipboard.png)]

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-OhSIpFF6-1593392615164)(C:\Users\33066\AppData\Local\YNote\data\qq1510077B57FCC357FB1D93412160B8DD\215c102c7e76422d8f52648d9a1fac23\clipboard.png)]

1.Java中JVM,JRE和JDK的區別

jvm

JVM是Java Virtual Machine(Java虛擬機)的縮寫,它是整個Java實現跨平臺的最核心的部分,所有的java程序會首先被編譯爲.class的類文件,這種類文件可以在虛擬機上執行,也就是說class並不直接與機器的操作系統相對應,而是經過虛擬機間接 與操作系統交互,由虛擬機將程序解釋給本地系統執行。JVM是Java平臺的基礎,和實際的機器一 樣,它也有自己的指令集,並且在運行時操作不同的內存區域。 JVM通過抽象操作系統和CPU結構,提供了一種與平臺無關的代碼執行方法,即與特殊的實現方法、主機硬件、主機操作系統無關。JVM的主要工作是解釋自己的指令集

(即字節碼)到CPU的指令集或對應的系統調用,保護用戶免被惡意程序騷擾。 JVM對上層的Java源文件是不關心的,它關注的只是由源文件生成的類文件(.class文件)。

jre

JRE是java runtime environment(java運行環境)的縮寫。光有JVM還不能讓class文件執行,因爲在解釋class的時候JVM需要調用解釋所需要的類庫lib。在JDK的安裝目錄裏你可以找到jre目錄,裏面有兩個文件夾bin和lib,在這裏可以 認爲bin裏的就是jvm,lib中則是jvm工作所需要的類庫,而jvm和lib和起來就稱爲jre。所以,在你寫完java程序編譯 成.class之後,你可以把這個.class文件和jre一起打包發給朋友,這樣你的朋友就可以運行你寫程序了(jre裏有運

行.class的java.exe)。JRE是Sun公司發佈的一個更大的系統,它裏面就有一個JVM。JRE就與具體的CPU結構和操作系統有關,是運行Java程序必不可少的(除非用其他一些編譯環境編譯成.exe可執行文件……),JRE的地位就象一臺PC機一樣,我們寫好的Win32應用程序需要操作系統幫我們運行,同樣的,我們編寫的Java程序也必須要JRE才能運行。

jdk

JDK是java development kit(java開發工具包)的縮寫。每個學java的人都會先在機器上裝一個JDK,那 讓我們看一下JDK的安裝目錄。在目錄下面有六個文件夾、一個src類庫源碼壓縮包、和其他幾個聲明文件。其中,真正在運行java時起作用的是以下四個文件夾:bin、include、lib、jre。現在我們可以看出這樣一個關係,JDK包含JRE,而JRE 包含JVM。

*bin:* 最主要的是編譯器(javac.exe) *include:* java和JVM交互用的頭文件****lib********:****類庫

*jre:* java運行環境

(注意:這裏的bin、lib文件夾和jre裏的bin、lib是不同的)

總的來說JDK是用於java程序的開發,而jre則是隻能運行class而沒有編譯的功能。eclipse、idea等其他IDE有自己的編 譯器而不是用JDK bin目錄中自帶的,所以在安裝時你會發現他們只要求你選jre路徑就ok了。

三者關係概括如下:

jdk是JAVA程序開發時用的開發工具包,其內部也有JRE運行環境JRE。JRE是JAVA程序運行時需要的運行環境,就是說 如果你光是運行JAVA程序而不是去搞開發的話,只安裝JRE就能運行已經存在的JAVA程序了。JDk、JRE內部都包含JAVA虛擬機JVM,JAVA虛擬機內部包含許多應用程序的類的解釋器和類加載器等等。

2. 說一下堆棧的區別?

  • 功能方面:堆是用來存放對象的,棧是用來執行程序的。
  • 共享性:堆是線程共享的,棧是線程私有的。
  • 空間大小:堆大小遠遠大於棧。

3. 描述一下JVM加載class文件的原理機制?

答:JVM中類的裝載是由類加載器(ClassLoader)和它的子類來實現的,Java中的類加載器是一個重要的Java運行時系統組件,它負責在運行時查找和裝入類文件中的類。類的加載是指把類的.class文件中的數據讀入到內存中,通常是創建一個字節數組讀入.class文件

4. Java 中會存在內存泄漏嗎,請簡單描述。

理論上Java因爲有垃圾回收機制(GC)不會存在內存泄露問題(這也是Java被廣泛使用於服務器端編程的一個重要原因);然而在實際開發中,可能會存在無用但可達的對象,這些對象不能被GC回收,因此也會導致內存泄露的發生。例如hibernate的Session(一級緩存)中的對象屬於持久態,垃圾回收器是不會回收這些對象的,然而這些對象中可能存在無用的垃圾對象,如果不及時關閉(close)或清空(flush)一級緩存就可能導致內存泄露

5. 什麼是虛擬機?

一臺虛擬的計算機。是一款軟件,用來執行一系列虛擬計算機指令。

例:

Visual Box,VMware屬於系統虛擬機,完全是對物理計算機的仿真,提供了可運行完整操作系統的軟件平臺

java虛擬機是程序虛擬機,專門爲執行單個計算機程序而設計,在java虛擬機中執行的指令我們成爲java字節碼指令

5.1 什麼是jvm虛擬機?

  • JVM 是 java虛擬機,是用來執行java字節碼(二進制的形式)的虛擬計算機
  • JVM 是運行在操作系統之上的,與硬件沒有任何關係

5.2 jvm虛擬機的作用:

答: java虛擬機就是二進制字節碼的運行環境,負責裝載字節碼到其內部,解釋/編譯爲對應平臺上的機器指令執行。每一條java指令,java虛擬機規範中都有詳細定義,如怎麼取操作數,怎麼處理操作數,結果放在哪裏

5.3 jvm虛擬機特點:

  • 一次編譯,到處運行
  • 自動內存管理
  • 自動垃圾回收功能

5.4 什麼是內存溢出?

答:指程序申請內存時,沒有足夠的內存供申請者使用,或者說,給了你一塊存儲int類型數據的存儲空間,但是你卻存儲long類型的數據,那麼結果就是內存不夠用,此時就會報錯OOM,即所謂的內存溢出。

5.5 什麼是內存泄漏?

指程序中己動態分配的堆內存由於某種原因程序未釋放或無法釋放,

造成系統內存的浪費,導致程序運行速度減慢甚至系統崩潰等嚴重後果。

6. 簡述Java的垃圾回收機制

傳統的C/C++語言,需要程序員負責回收已經分配內存。

顯式回收垃圾回收的缺點:

1) 程序忘記及時回收,從而導致內存泄露,降低系統性能。

2) 程序錯誤回收程序核心類庫的內存,導致系統崩潰。

Java語言不需要程序員直接控制內存回收,是由JRE在後臺自動回收不再使用的內存,稱爲垃圾回收機制,簡稱GC;

1)可以提高編程效率。

2) 保護程序的完整性。

3) 其開銷影響性能。Java虛擬機必須跟蹤程序中有用的對象,確定哪些是無用的。

垃圾回收機制的特點

1) 垃圾回收機制回收JVM堆內存裏的對象空間,不負責回收棧內存數據。

2) 對其他物理連接,比如數據庫連接、輸入流輸出流、Socket連接無能爲力。

3) 垃圾回收發生具有不可預知性,程序無法精確控制垃圾回收機制執行。

4) 可以將對象的引用變量設置爲null,暗示垃圾回收機制可以回收該對象。 現在的JVM有多種垃圾回收實現算法,表現各異。

垃圾回收機制回收任何對象之前,總會先調用它的finalize方法(如果覆蓋該方法,讓一個新的引用變量重新引用該對象,則會重新激活對象)。

程序員可以通過System.gc()或者Runtime.getRuntime().gc()來通知系統進行垃圾回收,會有一些效果,但是系統是 否進行垃圾回收依然不確定。

永遠不要主動調用某個對象的finalize方法,應該交給垃圾回收機制調用。


數據庫面試題

//創建數據庫
CREATE DATABASE booksales;
//創建表格
CREATE TABLE `cb` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '主鍵',
);
//插入數據
insert  into `customer`(`cid`,`cname`,`sex`,`email`,`tel`,`address`) values ('zb01','Jack','男','[email protected]','13100010001','上海市楊浦區國順路288號');
//刪除一行數據
delete  from book where bid = 'b123';
//修改一行數據
UPDATE book SET price = '九折' where press = '清華大學出版社出版';
1.creat table student():在table中建立一個student的表

2.alter  table student add 年齡 number(3):往表中插入一個列

3.alter table student drop column age:刪除表中的age列

4.alter table student rename column 年齡 to age:修改年齡爲age

5.alter table student modify age varchar2(20):修改列名屬性

6.rename student to stu:修改表名

7.drop table stu:刪除表

8.instert into teachers values(1,'小小'):插入數據

9.update teachers set name = '託尼' where ids = 9:修改name爲託尼
1.select * from emp where deptno in(10,20) : 查詢某一區間

2.select * from emp where ename like '%A%':查詢是否含有某個字符

3.select * from emp where ename like '__':查詢是否含有兩個字符的名字

4.select * from emp where sal > 2000 or sal < 1000:只要有一個成立即可

5.select sal + nvl(comm,0) 總工資 from emp:查詢相加運算

6.select rownum,E.* from emp e where rownum < 15:進行排序(僞列)

7.select * from emp order by sal desc:將工資進行降序排列(ASC是默認升序)

1. 數據庫的三範式是什麼?

  • 第一範式:強調的是列的原子性,即數據庫表的每一列都是不可分割的原子數據項。

  • 第二範式:屬性完全依賴於主鍵

  • 第三範式:任何非主屬性不依賴於其它非主屬性。

    ​ 數據不能存在傳遞關係,即每個屬性都跟主鍵有直接關係而不是間接關係。從而建立冗餘較小、結構合理的數據庫。

2. 一張自增表裏面總共有 7 條數據,刪除了最後 2 條數據,重啓 MySQL 數據庫,又插入了一條數據,此時 id 是幾?

  • 表類型如果是 MyISAM ,那 id 就是 8。

  • 表類型如果是 InnoDB,那 id 就是 6。

    InnoDB 表只會把自增主鍵的最大 id 記錄在內存中,所以重啓之後會導致最大 id 丟失。

3. MySQL 的內連接、左連接、右連接有什麼區別?

內連接關鍵字:inner join;左連接:left join;右連接:right join。

內連接是把匹配的關聯數據顯示出來;

左連接是左邊的表全部顯示出來,右邊的表顯示出符合條件的數據;

右連接正好相反。

4. MySQL 索引是怎麼實現的?

索引是滿足某種特定查找算法的數據結構,而這些數據結構會以某種方式指向數據,從而實現高效查找數據。

具體來說 MySQL 中的索引,不同的數據引擎實現有所不同,但目前主流的數據庫引擎的索引都是 B+ 樹實現的,B+ 樹的搜索效率,可以到達二分法的性能,找到數據區域之後就找到了完整的數據結構了,所有索引的性能也是更好的。

5. 說一下數據庫的事務隔離?

ySQL 的事務隔離是在 MySQL. ini 配置文件裏添加的,在文件的最後添加:

transaction-isolation = REPEATABLE-READ

可用的配置值:READ-UNCOMMITTED、READ-COMMITTED、REPEATABLE-READ、SERIALIZABLE。

  • READ-UNCOMMITTED:未提交讀,最低隔離級別、事務未提交前,就可被其他事務讀取(會出現幻讀、髒讀、不可重複讀)。
  • READ-COMMITTED:提交讀,一個事務提交後才能被其他事務讀取到(會造成幻讀、不可重複讀)。
  • REPEATABLE-READ:可重複讀,默認級別,保證多次讀取同一個數據時,其值都和事務開始時候的內容是一致,禁止讀取到別的事務未提交的數據(會造成幻讀)。
  • SERIALIZABLE:序列化,代價最高最可靠的隔離級別,該隔離級別能防止髒讀、不可重複讀、幻讀。

髒讀 :表示一個事務能夠讀取另一個事務中還未提交的數據。比如,某個事務嘗試插入記錄 A,此時該事務還未提交,然後另一個事務嘗試讀取到了記錄 A。

不可重複讀 :是指在一個事務內,多次讀同一數據。

幻讀 :指同一個事務內多次查詢返回的結果集不一樣。比如同一個事務 A 第一次查詢時候有 n 條記錄,但是第二次同等條件下查詢卻有 n+1 條記錄,這就好像產生了幻覺。發生幻讀的原因也是另外一個事務新增或者刪除或者修改了第一個事務結果集裏面的數據,同一個記錄的數據內容被修改了,所有數據行的記錄就變多或者變少了。

6. 說一下樂觀鎖和悲觀鎖?

  • 樂觀鎖:每次去拿數據的時候都認爲別人不會修改,所以不會上鎖,但是在提交更新的時候會判斷一下在此期間別人有沒有去更新這個數據。
  • 悲觀鎖:每次去拿數據的時候都認爲別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會阻止,直到這個鎖被釋放。

數據庫的樂觀鎖需要自己實現,在表裏面添加一個 version 字段,每次修改成功值加 1,這樣每次修改的時候先對比一下,自己擁有的 version 和數據庫現在的 version 是否一致,如果不一致就不修改,這樣就實現了樂觀鎖。

7. Redis 有哪些功能?

  • 數據緩存功能
  • 分佈式鎖的功能
  • 支持數據持久化
  • 支持事務
  • 支持消息隊列

8. 什麼是緩存穿透?怎麼解決?

緩存穿透:指查詢一個一定不存在的數據,由於緩存是不命中時需要從數據庫查詢,查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要到數據庫去查詢,造成緩存穿透。

解決方案:最簡單粗暴的方法如果一個查詢返回的數據爲空(不管是數據不存在,還是系統故障),我們就把這個空結果進行緩存,但它的過期時間會很短,最長不超過五分鐘。

9. Redis 支持的數據類型有哪些?

Redis 支持的數據類型:string(字符串)、list(列表)、hash(字典)、set(集合)、zset(有序集合)。

10. 怎麼保證緩存和數據庫數據的一致性?

  • 合理設置緩存的過期時間。
  • 新增、更改、刪除數據庫操作時同步更新 Redis,可以使用事物機制來保證數據的一致性。

11. Redis 怎麼實現分佈式鎖?

Redis 分佈式鎖其實就是在系統裏面佔一個“坑”,其他程序也要佔“坑”的時候,佔用成功了就可以繼續執行,失敗了就只能放棄或稍後重試。

佔坑一般使用 setnx(set if not exists)指令,只允許被一個程序佔有,使用完調用 del 釋放鎖。

12.簡述mysql主從複製原理?

(1) master將改變記錄到二進制日誌(binary log)中(這些記錄叫做二進制日誌事件,binary log events);
(2) slave將master的binary log events拷貝到它的中繼日誌(relay log);
(3) slave重做中繼日誌中的事件,將改變反映它自己的數據。

13.數據庫優化的幾點:

  1. 建立和優化使用索引

  2. 減少子查詢和聯表查詢

  3. 主從分離

  4. 用臨時表代替大表插入

14.redis是什麼?

Redis是一個開源的基於內存的,key-value數據結構的緩存數據庫,支持數據持久化,m-s複製,常用數據類型有string set hash list,
最佳應用場景:適用於數據變化快且數據庫大小可遇見(適合內存容量)的應用程序。
例如:股票價格、數據分析、實時數據蒐集、實時通訊。
Redis只能使用單線程,性能受限於CPU性能,故單實例CPU最高才可能達到5-6wQPS每秒(取決於數據結構,數據大小以及服務器硬件性能,日常環境中QPS高峯大約在1-2w左右)


框架面試題

1 . Servlet的運行過程?

Web容器加載Servlet並將其實例化後,Servlet生命週期開始,

容器運行其init()方法進行Servlet的初始化;

請求到達時調用Servlet的service()方法,

service()方法會根據需要調用與請求對應的doGet或doPost等方法;

當服務器關閉或項目被卸載時服務器會將Servlet實例銷燬,此時會調用Servlet的destroy()方法。

2.講解JSP中的四種作用域。

  • page代表與一個頁面相關的對象和屬性。
  • request代表與Web客戶機發出的一個請求相關的對象和屬性。一個請求可能跨越多個頁面,涉及多個Web組件;需要在頁面顯示的臨時數據可以置於此作用域。
  • session代表與某個用戶與服務器建立的一次會話相關的對象和屬性。跟某個用戶相關的數據應該放在用戶自己的session中。
  • application代表與整個Web應用程序相關的對象和屬性,它實質上是跨越整個Web應用程序,包括多個頁面、請求和會話的一個全局作用域。

3.forward 和 redirect 的區別?

forward 是轉發 和 redirect 是重定向:

  • 地址欄 url 顯示:foward url 不會發生改變,redirect url 會發生改變;
  • 數據共享:forward 可以共享 request 裏的數據,redirect 不能共享;
  • 效率:forward 比 redirect 效率高。

4.動態代理是什麼?有哪些應用?

動態代理是運行時動態生成代理類。

動態代理的應用有 spring aop、hibernate 數據查詢、測試框架的後端 mock、rpc,Java註解對象獲取等。

5.爲什麼要使用 spring?

  • spring 提供 ioc 技術,容器會幫你管理依賴的對象,從而不需要自己創建和管理依賴對象了,更輕鬆的實現了程序的解耦。
  • spring 提供了事務支持,使得事務操作變的更加方便。
  • spring 提供了面向切片編程,這樣可以更方便的處理某一類的問題。
  • 更方便的框架集成,spring 可以很方便的集成其他框架,比如 MyBatis、hibernate 等。

6.解釋一下什麼是 aop?

aop 是面向切面編程,通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。

簡單來說就是統一處理某一“切面”(類)的問題的編程思想,比如統一處理日誌、異常等。

7.解釋一下什麼是 ioc?

ioc:Inversionof Control(中文:控制反轉)是 spring 的核心,對於 spring 框架來說,就是由 spring 來負責控制對象的生命週期和對象間的關係。

簡單來說,控制指的是當前對象對內部成員的控制權;控制反轉指的是,這種控制權不由當前對象管理了,由其他(類,第三方容器)來管理。

8.spring 常用的注入方式有哪些?

  • setter 屬性注入
  • 構造方法注入
  • 註解方式注入

9.spring 中的 bean 是線程安全的嗎?

spring 中的 bean 默認是單例模式,spring 框架並沒有對單例 bean 進行多線程的封裝處理。

實際上大部分時候 spring bean 無狀態的(比如 dao 類),所有某種程度上來說 bean 也是安全的,但如果 bean 有狀態的話(比如 view model 對象),那就要開發者自己去保證線程安全了,最簡單的就是改變 bean 的作用域,把“singleton”變更爲“prototype”,這樣請求 bean 相當於 new Bean()了,所以就可以保證線程安全了。

  • 有狀態就是有數據存儲功能。
  • 無狀態就是不會保存數據。

10.spring 中的 bean 是線程安全的嗎?

spring 中的 bean 默認是單例模式,spring 框架並沒有對單例 bean 進行多線程的封裝處理。

實際上大部分時候 spring bean 無狀態的(比如 dao 類),所有某種程度上來說 bean 也是安全的,但如果 bean 有狀態的話(比如 view model 對象),那就要開發者自己去保證線程安全了,最簡單的就是改變 bean 的作用域,把“singleton”變更爲“prototype”,這樣請求 bean 相當於 new Bean()了,所以就可以保證線程安全了。

  • 有狀態就是有數據存儲功能。
  • 無狀態就是不會保存數據。

11.spring 支持幾種 bean 的作用域?

spring 支持 5 種作用域,如下:

  • singleton:spring ioc 容器中只存在一個 bean 實例,bean 以單例模式存在,是系統默認值;
  • prototype:每次從容器調用 bean 時都會創建一個新的示例,既每次 getBean()相當於執行 new Bean()操作;
  • Web 環境下的作用域:
  • request:每次 http 請求都會創建一個 bean;
  • session:同一個 http session 共享一個 bean 實例;
  • global-session:用於 portlet 容器,因爲每個 portlet 有單獨的 session,globalsession 提供一個全局性的 http session。

12.spring 事務實現方式有哪些?

  • 聲明式事務:聲明式事務也有兩種實現方式,
  • 基於 xml 配置文件的方式和註解方式(在類上添加 @Transaction 註解)。
  • 編碼方式:提供編碼的形式管理和維護事務。

13.說一下 spring 的事務隔離

spring 有五大隔離級別,默認值爲 ISOLATION_DEFAULT(使用數據庫的設置),其他四個隔離級別和數據庫的隔離級別一致:

ISOLATION_DEFAULT:用底層數據庫的設置隔離級別,數據庫設置的是什麼我就用什麼;

ISOLATIONREADUNCOMMITTED:未提交讀,最低隔離級別、事務未提交前,就可被其他事務讀取(會出現幻讀、髒讀、不可重複讀);

ISOLATIONREADCOMMITTED:提交讀,一個事務提交後才能被其他事務讀取到(會造成幻讀、不可重複讀),SQL server 的默認級別;

ISOLATIONREPEATABLEREAD:可重複讀,保證多次讀取同一個數據時,其值都和事務開始時候的內容是一致,禁止讀取到別的事務未提交的數據(會造成幻讀),MySQL 的默認級別;

ISOLATION_SERIALIZABLE:序列化,代價最高最可靠的隔離級別,該隔離級別能防止髒讀、不可重複讀、幻讀。

髒讀 :表示一個事務能夠讀取另一個事務中還未提交的數據。比如,某個事務嘗試插入記錄 A,此時該事務還未提交,然後另一個事務嘗試讀取到了記錄 A。

不可重複讀 :是指在一個事務內,多次讀同一數據。

幻讀 :指同一個事務內多次查詢返回的結果集不一樣。比如同一個事務 A 第一次查詢時候有 n 條記錄,但是第二次同等條件下查詢卻有 n+1 條記錄,這就好像產生了幻覺。發生幻讀的原因也是另外一個事務新增或者刪除或者修改了第一個事務結果集裏面的數據,同一個記錄的數據內容被修改了,所有數據行的記錄就變多或者變少了。

14.說一下 spring mvc 運行流程?

  • spring mvc 先將請求發送給 DispatcherServlet。
  • DispatcherServlet 查詢一個或多個 HandlerMapping,找到處理請求的 Controller。
  • DispatcherServlet 再把請求提交到對應的 Controller。
  • Controller 進行業務邏輯處理後,會返回一個ModelAndView。
  • Dispathcher 查詢一個或多個 ViewResolver 視圖解析器,找到 ModelAndView 對象指定的視圖對象。
  • 視圖對象負責渲染返回給客戶端。

15.spring mvc 有哪些組件?

  • 前置控制器 DispatcherServlet。
  • 映射控制器 HandlerMapping。
  • 處理器 Controller。
  • 模型和視圖 ModelAndView。
  • 視圖解析器 ViewResolver。

16. MyBatis 中 #{}和 ${}的區別是什麼?

\#{}是預編譯處理,${}是字符替換。 在使用 #{}時,MyBatis 會將 SQL 中的 #{}替換成“?”,配合 PreparedStatement 的 set 方法賦值,這樣可以有效的防止 SQL 注入,保證程序的運行安全。

17. MyBatis 有幾種分頁方式?

分頁方式:邏輯分頁和物理分頁。

邏輯分頁: 使用 MyBatis 自帶的 RowBounds 進行分頁,它是一次性查詢很多數據,然後在數據中再進行檢索。

物理分頁: 自己手寫 SQL 分頁或使用分頁插件 PageHelper,去數據庫查詢指定條數的分頁數據的形式。

18.MyBatis 邏輯分頁和物理分頁的區別是什麼?

  • 邏輯分頁是一次性查詢很多數據,然後再在結果中檢索分頁的數據。這樣做弊端是需要消耗大量的內存、有內存溢出的風險、對數據庫壓力較大。
  • 物理分頁是從數據庫查詢指定條數的數據,彌補了一次性全部查出的所有數據的種種缺點,比如需要大量的內存,對數據庫查詢壓力較大等問題。

19. MyBatis 是否支持延遲加載?延遲加載的原理是什麼?

MyBatis 支持延遲加載,設置 lazyLoadingEnabled=true 即可。

延遲加載的原理的是調用的時候觸發加載,而不是在初始化的時候就加載信息。比如調用 a. getB(). getName(),這個時候發現 a. getB() 的值爲 null,此時會單獨觸發事先保存好的關聯 B 對象的 SQL,先查詢出來 B,然後再調用 a. setB(b),而這時候再調用 a. getB(). getName() 就有值了,這就是延遲加載的基本原理。

20.說一下 MyBatis 的一級緩存和二級緩存?

  • 一級緩存:基於 PerpetualCache 的 HashMap 本地緩存,它的聲明週期是和 SQLSession 一致的,有多個 SQLSession 或者分佈式的環境中數據庫操作,可能會出現髒數據。當 Session flush 或 close 之後,該 Session 中的所有 Cache 就將清空,默認一級緩存是開啓的。
  • 二級緩存:也是基於 PerpetualCache 的 HashMap 本地緩存,不同在於其存儲作用域爲 Mapper 級別的,如果多個SQLSession之間需要共享緩存,則需要使用到二級緩存,並且二級緩存可自定義存儲源,如 Ehcache。默認不打開二級緩存,要開啓二級緩存,使用二級緩存屬性類需要實現 Serializable 序列化接口(可用來保存對象的狀態)。

開啓二級緩存數據查詢流程:二級緩存 -> 一級緩存 -> 數據庫。

緩存更新機制:當某一個作用域(一級緩存 Session/二級緩存 Mapper)進行了C/U/D 操作後,默認該作用域下所有 select 中的緩存將被 clear。

21.爲什麼要用 spring boot?

  • 配置簡單
  • 獨立運行
  • 自動裝配
  • 無代碼生成和 xml 配置
  • 提供應用監控
  • 易上手
  • 提升開發效率

22. spring boot 核心配置文件是什麼?

spring boot 核心的兩個配置文件:

  • bootstrap (. yml 或者 . properties):boostrap 由父 ApplicationContext 加載的,比 applicaton 優先加載,且 boostrap 裏面的屬性不能被覆蓋;
  • application (. yml 或者 . properties):用於 spring boot 項目的自動化配置。

23. spring boot 有哪些方式可以實現熱部署?

  • 使用 devtools 啓動熱部署,添加 devtools 庫,在配置文件中把 spring. devtools. restart. enabled 設置爲 true;
  • 使用 Intellij Idea 編輯器,勾上自動編譯或手動重新編譯。

25.說說你對 Spring 的理解,非單例注入的原理?它的生命週期?循環注入的原理, aop 的實現原理,說說 aop 中的幾個術語,它們是怎麼相互工作的。

AOP與IOC的概念(即spring的核心)

a) IOC:Spring是開源框架,使用框架可以使我們減少工作量,提高工作效率並且它是分層結構,即相對應的層處理對應的業務邏輯,減少代碼的耦合度。而spring的核心是IOC控制反轉和AOP面向切面編程。IOC控制反轉主要強調的是程序之間的關係是由容器控制的,容器控制對象,控制了對外部資源的獲取。而反轉即爲,在傳統的編程中都是由我們創建對象獲取依賴對象,而在IOC中是容器幫我們創建對象並注入依賴對象,正是容器幫我們查找和注入對象,對象是被獲取,所以叫反轉。

b) AOP:面向切面編程,主要是管理系統層的業務,比如日誌,權限,事物等。AOP是將封裝好的對象剖開,找出其中對多個對象產生影響的公共行爲,並將其封裝爲一個可重用的模塊,這個模塊被命名爲切面(aspect),切面將那些與業務邏輯無關,卻被業務模塊共同調用的邏輯提取並封裝起來,減少了系統中的重複代碼,降低了模塊間的耦合度,同時提高了系統的可維護性。

核心組件:bean,context,core,單例注入是通過單例beanFactory進行創建,生命週期是在創建的時候通過接口實現開啓,循環注入是通過後置處理器,aop其實就是通過反射進行動態代理,pointcut,advice等。


計算機網絡面試題

1.什麼是SQL注入攻擊

攻擊者在HTTP請求中注入惡意的SQL代碼,服務器使用參數構建數據庫SQL命令時,惡意SQL被一起構造,並在數據庫中執行。
用戶登錄,輸入用戶名 lianggzone,密碼 ‘ or ‘1’=’1 ,如果此時使用參數構造的方式,就會出現
select * from user where name = ‘lianggzone’ and password = ‘’ or ‘1’=‘1’

不管用戶名和密碼是什麼內容,使查詢出來的用戶列表不爲空。

如何防範SQL注入攻擊使用預編譯的PrepareStatement是必須的,但是一般我們會從兩個方面同時入手。
Web端
1)有效性檢驗。
2)限制字符串輸入的長度。
服務端
1)不用拼接SQL字符串。
2)使用預編譯的PrepareStatement。
3)有效性檢驗。(爲什麼服務端還要做有效性檢驗?第一準則,外部都是不可信的,防止攻擊者繞過Web端請求)
4)過濾SQL需要的參數中的特殊字符。比如單引號、雙引號。

​ prepareStatement對象防止sql注入的方式是把用戶非法輸入的單引號用\反斜槓做了轉義,從而達到了防止sql注入的目的

img

2.什麼是XSS攻擊

答; 跨站點腳本攻擊,指攻擊者通過篡改網頁,嵌入惡意腳本程序,在用戶瀏覽網頁時,控制用戶瀏覽器進行惡意操作的一種攻擊方式。如何防範XSS攻擊
1)前端,服務端,同時需要字符串輸入的長度限制。
2)前端,服務端,同時需要對HTML轉義處理。將其中的”<”,”>”等特殊字符進行轉義編碼。
防 XSS 的核心是必須對輸入的數據做過濾處理。

3.什麼是CSRF攻擊

答; 跨站點請求僞造,指攻擊者通過跨站請求,以合法的用戶的身份進行非法操作。可以這麼理解CSRF攻擊:攻擊者盜用你的身份,以你的名義向第三方網站發送惡意請求。CRSF能做的事情包括利用你的身份發郵件,發短信,進行交易轉賬,甚至盜取賬號信息。

如何防範CSRF攻擊:

安全框架,例如Spring Security。
token機制。在HTTP請求中進行token驗證,如果請求中沒有token或者token內容不正確,則認爲CSRF攻擊而拒絕該請求。
驗證碼。通常情況下,驗證碼能夠很好的遏制CSRF攻擊,但是很多情況下,出於用戶體驗考慮,驗證碼只能作爲一種輔助手段,而不是最主要的解決方案。
referer識別。在HTTP Header中有一個字段Referer,它記錄了HTTP請求的來源地址。如果Referer是其他網站,就有可能是CSRF攻擊,則拒絕該請求。但是,服務器並非都能取到Referer。很多用戶出於隱私保護的考慮,限制了Referer的發送。在某些情況下,瀏覽器也不會發送Referer,例如HTTPS跳轉到HTTP。
1)驗證請求來源地址;
2)關鍵操作添加驗證碼;
3)在請求地址添加 token 並驗證。

4.DDos 攻擊

客戶端向服務端發送請求鏈接數據包,服務端向客戶端發送確認數據包,客戶端不向服務端發送確認數據包,服務器一直等待來自客戶端的確認
沒有徹底根治的辦法,除非不使用TCP
DDos 預防:
1)限制同時打開SYN半鏈接的數目
2)縮短SYN半鏈接的Time out 時間
3)關閉不必要的服務

img

5.什麼是三次握手四次揮手?tcp爲什麼要三次握手?

第一次握手:建立連接時,客戶端發送syn包(syn=j)到服務器,並進入SYN_SEND狀態,等待服務器確認;
第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。
完成三次握手,客戶端與服務器開始傳送數據
img

  1. 客戶端先發送FIN,進入FIN_WAIT1狀態,用來關閉Client到Server的數據傳送

  2. 服務端收到FIN,發送ACK,進入CLOSE_WAIT狀態,客戶端收到這個ACK,進入FIN_WAIT2狀態

  3. 服務端發送FIN,進入LAST_ACK狀態,用來關閉Server到Client的數據傳送

  4. 客戶端收到FIN,發送ACK,進入TIME_WAIT狀態,服務端收到ACK,進入CLOSE狀態(等待2MSL時間,約4分鐘。主要是防止最後一個ACK丟失。)

    第一次揮手:主動關閉方發送一個FIN,用來關閉主動方到被動關閉方的數據傳送,也就是主動關閉方告訴被動關閉方:我已經不 會再給你發數據了(當然,在fin包之前發送出去的數據,如果沒有收到對應的ack確認報文,主動關閉方依然會重發這些數據),但是,此時主動關閉方還可 以接受數據。
    第二次揮手:被動關閉方收到FIN包後,發送一個ACK給對方,確認序號爲收到序號+1(與SYN相同,一個FIN佔用一個序號)。
    第三次揮手:被動關閉方發送一個FIN,用來關閉被動關閉方到主動關閉方的數據傳送,也就是告訴主動關閉方,我的數據也發送完了,不會再給你發數據了。
    第四次揮手:主動關閉方收到FIN後,發送一個ACK給被動關閉方,確認序號爲收到序號+1,至此,完成四次揮手。

在這裏插入圖片描述

5.1爲什麼連接的時候是三次握手,關閉的時候卻是四次握手?

答:因爲當Server端收到Client端的SYN連接請求報文後,可以直接發送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。但是關閉連接時,當Server端收到FIN報文時,很可能並不會立即關閉SOCKET,所以只能先回復一個ACK報文,告訴Client端,“你發的FIN報文我收到了”。只有等到我Server端所有的報文都發送完了,我才能發送FIN報文,因此不能一起發送。故需要四步握手。49.簡述 tcp 和 udp的區別?

tcp 和 udp 是 OSI 模型中的運輸層中的協議。tcp 提供可靠的通信傳輸,而 udp 則常被用於讓廣播和細節控制交給應用的通信傳輸。

兩者的區別大致如下:

  • tcp 面向連接,udp 面向非連接即發送數據前不需要建立鏈接;

  • tcp 提供可靠的服務(數據傳輸),udp 無法保證;

  • tcp 面向字節流,udp 面向報文;

  • tcp 數據傳輸慢,udp 數據傳輸快;

6.get 和 post 請求有哪些區別?

  • get 請求會被瀏覽器主動緩存,而 post 不會。
  • get 傳遞參數有大小限制,而 post 沒有。
  • post 參數傳輸更安全,get 的參數會明文限制在 url 上,post 不會。

7.如何實現跨域?

實現跨域有以下幾種方案:

  • 服務器端運行跨域 設置 CORS 等於 *;
  • 在單個接口使用註解 @CrossOrigin 運行跨域;
  • 使用 jsonp 跨域;

8.http和https有什麼區別?

  1. https協議需要到ca申請證書,一般免費證書較少,因而需要一定費用。

  2. http是超文本傳輸協議,信息是明文傳輸,https則是具有安全性的ssl加密傳輸協議。

  3. http和https使用的是完全不同的連接方式,用的端口也不一樣,前者是80,後者是443。

  4. http的連接很簡單,是無狀態的;HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網絡協議,比http協議安全。

8.1 HTTPS的工作原理

客戶端在使用HTTPS方式與Web服務器通信時有以下幾個步驟,如圖所示。

(1)客戶使用https的URL訪問Web服務器,要求與Web服務器建立SSL連接。

(2)Web服務器收到客戶端請求後,會將網站的證書信息(證書中包含公鑰)傳送一份給客戶端。

(3)客戶端的瀏覽器與Web服務器開始協商SSL連接的安全等級,也就是信息加密的等級。

(4)客戶端的瀏覽器根據雙方同意的安全等級,建立會話密鑰,然後利用網站的公鑰將會話密鑰加密,並傳送給網站。

(5)Web服務器利用自己的私鑰解密出會話密鑰。

(6)Web服務器利用會話密鑰加密與客戶端之間的通信。

img

數據結構與算法面試題

1.二分查找。

非遞歸實現:

public static int biSearch(int []array,int a){
    int lo=0;
    int hi=array.length-1;
    int mid;
    while(lo<=hi){
        mid=(lo+hi)/2;
        if(array[mid]==a){
            return mid+1;
        }else if(array[mid]<a){
            lo=mid+1;
        }else{
            hi=mid-1;
        }
    }
    return -1;
}

遞歸實現:

public static int sort(int []array,int a,int lo,int hi){
        if(lo<=hi){
            int mid=(lo+hi)/2;
            if(a==array[mid]){
                return mid+1;
            }
            else if(a>array[mid]){
                return sort(array,a,mid+1,hi);
            }else{
                return sort(array,a,lo,mid-1);
            }
        }
        return -1;
    }

排序方式

冒泡排序

public class Bubbling {
    public static void BubblingSort(int[] arr){
        if (arr == null || arr.length < 2){
            return;
        }
        for (int end = arr.length - 1;end > 0;end--){
            for (int i = 0;i < end;i++){
                if (arr[i] > arr[i+1]){
                    swap(arr, i ,i +1);
                }
            }
        }
    }
    public static void swap(int[] arr,int i,int j){
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}

選擇排序:

public class Choose {
    public static  void select(int[] arr){
        if (arr == null || arr.length < 2){
            return;
        }
        for (int i = 0;i < arr.length - 1;i++){
            int minIndex = i;
            for (int j = i + 1;j < arr.length;j++){
                minIndex = arr[j] < arr[minIndex] ? j : minIndex;
            }
            swap(arr,i,minIndex);
        }
    }
    public static void swap(int[] arr,int i,int j){
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}

插入排序:

private static int[] insertSort(int[] arr) {
	int temp;
    if (arr == null || arr.length < 2){
            return;
    }
    for (int i=1;i<arr.length;i++){
        //待排元素小於有序序列的最後一個元素時,向前插入
        if (arr[i]<arr[i-1]){
            temp = arr[i];
            for (int j=i;j>=0;j--){
                if (j>0 && arr[j-1]>temp) {
                    arr[j]=arr[j-1];
                }else {
                    arr[j]=temp;
                    break;
                }
            }
        }
    }
    return arr;
}

快速排序:

private static int[] quickSort(int[] arr, int low, int high) {
    	if (arr == null || arr.length < 2){
            return;
        }
		if (low < high) {
			int middle = getMiddle(arr, low, high);
			//對左子序列進行排序
			quickSort(arr, low, middle - 1);
			//對右子序列進行排序
			quickSort(arr, middle + 1, high);
		}
		return arr;
}
private static int getMiddle(int[] arr, int low, int high) {
	int temp = arr[low];
	while (low < high) {
		while (low < high && temp <= arr[high]) {
			high--;
		}
		arr[low] = arr[high];
           while (low < high && temp >= arr[low]) {
                low++;
           }
           arr[high] = arr[low];	
	}
	arr[low] = temp;
	return low;
}

堆排序:

 public static void heapSort(int[] arr) {
        if (arr == null || arr.length < 2){
            return;
        }
        //構造大根堆
        heapInsert(arr);
        int size = arr.length;
        while (size > 1) {
            //固定最大值
            swap(arr, 0, size - 1);
            size--;
            //構造大根堆
            heapify(arr, 0, size);
        }
    }
    public static void heapInsert(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            //當前索引
            int currentIndex = i;
            //父結點索引
            int fatherIndex = (currentIndex - 1) / 2;
            //如果當前插入的值大於其父結點的值,則交換值,並且將索引指向父結點
            //然後繼續和上面的父結點值比較,直到不大於父結點,則退出循環
            while (arr[currentIndex] > arr[fatherIndex]) {
                swap(arr, currentIndex, fatherIndex);
                currentIndex = fatherIndex;
                fatherIndex = (currentIndex - 1) / 2;
            }
        }
    }
//剩餘的數構造成大根堆
public static void heapify(int[] arr, int index, int size) {
    int left = 2 * index + 1;
    int right = 2 * index + 2;
    while (left < size) {
        int largestIndex;
        if (arr[left] < arr[right] && right < size) {
            largestIndex = right;
        } else {
            largestIndex = left;
        }
        //比較父結點的值與孩子中較大的值,並確定最大值的索引
        if (arr[index] > arr[largestIndex]) {
            largestIndex = index;
        }
        //爲大根堆時
        if (index == largestIndex) {
            break;
        }
        swap(arr, largestIndex, index);
        index = largestIndex;
        left = 2 * index + 1;
        right = 2 * index + 2;
    }
}
//交換數組中兩個元素的值
public static void swap(int[] arr, int i, int j) {
     int temp = arr[i];
     arr[i] = arr[j];
     arr[j] = temp;
}

遞歸應用題

題目 : 使用遞歸算法輸出某個目錄下及其子目錄下所有文件.

遞歸 : 自動調用自己 , 需要定義遞歸出口.

題目分析 : 參數爲一個指定的目錄 . 輸出所有文件列表;

public class TestPrintDirAndFiles {

    public static void main(String[] args) {
        print(new File("E:/"));
    }
    
    private static void print(File file) {
        System.out.println(file.getAbsolutePath());
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                print(f);
            }
        }
    }
}

前端面試題

1.比較一下Java和JavaSciprt。

基於對象和麪向對象:Java是一種真正的面向對象的語言,即使是開發簡單的程序,必須設計對象;JavaScript是種腳本語言,它可以用來製作與網絡無關的,與用戶交互作用的複雜軟件。它是一種基於對象(Object-Based)和事件驅動(Event-Driven)的編程語言,因而它本身提供了非常豐富的內部對象供設計人員使用。
- 解釋和編譯:Java的源代碼在執行之前,必須經過編譯。JavaScript是一種解釋性編程語言,其源代碼不需經過編譯,由瀏覽器解釋執行。(目前的瀏覽器幾乎都使用了JIT(即時編譯)技術來提升JavaScript的運行效率)
- 強類型變量和類型弱變量:Java採用強類型變量檢查,即所有變量在編譯之前必須作聲明;JavaScript中變量是弱類型的,甚至在使用變量前可以不作聲明,JavaScript的解釋器在運行時檢查推斷其數據類型。

什麼是ajax請求?

$.ajax({
     url:"/handle_Ajax/",
     type:"POST",
     data:{username:"Alex",password:123},
     success:function(data){
         console.log(data)
     },
     error: function (jqXHR, textStatus, err) {
         console.log(arguments);
     },
     complete: function (jqXHR, textStatus) {
         console.log(textStatus);
     },
     statusCode: {
        '403': function (jqXHR, textStatus, err) {
               console.log(arguments);
          },
         '400': function (jqXHR, textStatus, err) {
             console.log(arguments);
         }
      }
 })

簡述jsonp及實現原理?

JSONP 是json用來跨域的一個東西。原理是通過script標籤的跨域特性來繞過同源策略。

JsonP解決跨域只能發送get請求,並且實現起來需要前後端交互比較多。

JSONP的簡單實現:創建一個回調函數,然後在遠程服務上調用這個函數並且將JSON數據作爲參數傳遞,完成回調。

列舉Http請求中常見的請求方式?

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-DVWZR8Ax-1593392615170)(C:\Users\33066\AppData\Roaming\Typora\typora-user-images\image-20200629084508965.png)]

列舉Http請求中的狀態碼?

1XX:指示信息–表示請求已接收,繼續處理。

2XX Success(成功狀態碼):成功–表示請求已被成功接收、理解、接受。

200 表示從客戶端發來的請求在服務器端被正常處理
204 該狀態碼錶示服務器接收的請求已成功處理,但在返回的響應報文中不含實體的主體部分
206 該狀態碼錶示客戶端進行了範圍請求,而服務器成功執行了這部分的GET請求

3XX Redirection(重定向狀態碼):重定向–要完成請求必須進行更進一步的操作。

301 永久性重定向
302 臨時性重定向

4XX Client Error(客戶端錯誤狀態碼):客戶端錯誤–請求有語法錯誤或請求無法實現。

400 該狀態碼錶示請求報文中存在語法錯誤
401 該狀態碼錶示發送的請求需要有通過HTTP認證的認證信息
403 該狀態碼錶明對請求資源的訪問被服務器拒絕了
404 該狀態碼錶明服務器上無法找到請求的資源

5XX Server Error(服務器錯誤狀態碼):服務器端錯誤–服務器未能實現合法的請求。

500 該狀態碼錶明服務器端在執行請求時發生了錯誤。
503 該狀態碼錶明服務器暫時處於超負載或正在進行停機維護,現在無法處理請求。

2.vue優點?

答:輕量級框架:只關注視圖層
簡單易學:國人開發,中文文檔,不存在語言障礙 ,易於理解和學習;
雙向數據綁定:保留了angular的特點,在數據操作方面更爲簡單;
組件化:保留了react的優點,實現了html的封裝和重用,在構建單頁面應用方面有着獨特的優勢;
視圖,數據,結構分離:使數據的更改更爲簡單,不需要進行邏輯代碼的修改,只需要操作數據就能完成相關操作;

3.vue中的路由的攔截器的作用?

攔截器可以在請求發送前和發送請求後做一些處理。

vue路由攔截,針對要先登錄才能進入的頁面,判斷是否有Token值,如果有則next(),否則跳轉到登錄頁面。

在這裏插入圖片描述

列舉vue的常見指令

  1. 文本插值:{{ }} Mustache
  2. DOM屬性綁定: v-bind
  3. 指令綁定一個事件監聽器:v-on
  4. 實現表單輸入和應用狀態之間的雙向綁定:v-model
  5. 控制切換一個元素的顯示:v-if 和 v-else
  6. 列表渲染:v-for
  7. 根據條件展示元素:v-show

3.什麼是 vue 生命週期?有什麼作用?

答:每個 Vue 實例在被創建時都要經過一系列的初始化過程——例如,需要設置數據監聽、編譯模板、將實例掛載到 DOM 並在數據變化時更新 DOM 等。同時在這個過程中也會運行一些叫做 生命週期鉤子 的函數,這給了用戶在不同階段添加自己的代碼的機會。(ps:生命週期鉤子就是生命週期函數)例如,如果要通過某些插件操作DOM節點,如想在頁面渲染完後彈出廣告窗, 那我們最早可在mounted 中進行。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Ex9s9kW6-1593392615173)(C:\Users\33066\AppData\Roaming\Typora\typora-user-images\image-20200628174125918.png)]

Linux面試題

1.Linux常用操作。

1:man rm———————————————查看命令幫助

2:mkdir———————————————-創建目錄

3:touch———————————————-創建文件

4:cd—————————————————切換。

5:ls—————————————————查看目錄

6:ls -lh————————————————查看目錄詳細

7:pwd————————————————-查看當前目錄

8:vim————————————————-添加內容

9:echo————————————————追加內容

10:cat————————————————查看文件內容

11:mv————————————————-移動

12:cp————————————————-拷貝

13:mv————————————————重命名

15:find———————————————-搜索

16:rm————————————————-刪除數據

17:ping———————————————-查看能不能上網

19:tar cf ————————————————打壓縮

20:tar xf——————————————-解壓縮

2.git常用命令

  • 1:git init—————————初始化

  • 2:git add .————————-從工作區,添加到版本庫

  • 3:git commit -m”xxx”————從暫存區,添加到分支

  • 4:git status————————查看狀態

  • 5:git log —————————查看版本庫的日誌

  • 6:git reflog————————查看所有日誌

  • 7:git reset —head 版本號—-切換

  • 8:git stash————————-保存

  • 9:git stash————————-將第一個記錄從“某個地方”重新拿到工作區(可能有衝突)

  • git stash list——————————————————————————查看“某個地方”存儲的所有記錄

  • git stash clear—————————————————————————-清空“某個地方”

  • git stash pop——————————————————————————-將第一個記錄從“某個地方”重新拿到工作區(可能有衝突)

  • git stash apply —————————————————————————編號,將指定編號記錄從“某個地方”重新拿到工作區(可能有衝突)

  • git stash drop —————————————————————————編號 ,刪除指定編號的記錄

  • 10:git branch dev—————創建分支

  • 11:git branch -d dev———-刪除分支

  • 12:git checkout dev————切換分支

  • 13:git merge dev—————-合併分支

  • 14:git branch———————查看所有分支

  • 15:git clone https:xxx——-克隆

  • 16:git add origin https:xxx-起個別名

  • 17:git push origin dev ——添加到dev分支

  • 18:git pull origin master—拉代碼

  • 19:git fetch origin master-去倉庫獲取

  • 20:git merge origin/master-和網上下的master分支合併

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