1. 程式人生 > >Java面試知識(持續更新)

Java面試知識(持續更新)

(歡迎各位大佬新增新的面試知識,本文純屬博主手打,如有錯誤,歡迎各位大佬批評在下方,小弟一定及時改正)

基礎篇

1、一個".java"原始檔可以有多個類,但只能有一個public類,並且public的類名必須和檔名一致。

2、goto是java中的保留字,現在沒有在java中使用

3、&和&&的區別:

     兩者都是邏輯與的運算子,表示邏輯與。當運算子兩邊的運算結果都為true時,結果才為true,否則,當一方為false時,結果為false。。。。。。其中&&具有短路的功能,如果第一個表示式為false時,則不會計算第二個表示式,結果為false。。如if(x==33&++y>0)y會增長,if(x==33&&++y>0)y不會增長。

4、switch語句可以作用在byte上,不能作用在long和String上。

5、short s1= 1;s1 = s1 + 1;  由於s1+1運算時會自動提升表示式的型別,所以結果是int型。  編譯會報告需要強制轉換的型別。

     short s1= 1;s1 += 1;  正確編譯。

6、char型變數能儲存一箇中文漢字。因為char型變數是用來儲存Unicode編碼的字元的,unicode編碼字元包含了漢字。

7、最有效的方法算出2乘以8等於幾  ,,  2<<3      因為將一個數左移n位,就相當於乘以2的n次方。

8、使用final關鍵字修飾一個變數時,是指引用變數不能變,

引用變數所指的物件中的內容還是可以改變的。

9、"=="和equals()方法區別

     == 比較Stack中的值(引用資料型別stack中存放的是物件的堆記憶體地址), == 比較的是兩個引用在記憶體中指向的是不是同一物件(即同一記憶體空間),也就是說在記憶體空間中的儲存位置是否一致。如果兩個物件的引用相同時(指向同一物件時),“==”操作符返回true,否則返回flase。

    equals()通常被覆寫為比較物件的值。

      1)對於==,比較的是值是否相等

              如果作用於基本資料型別的變數,則直接比較其儲存的 “值”是否相等;

    如果作用於引用型別的變數,則比較的是所指向的物件的地址

  2)對於equals方法,注意:equals方法不能作用於基本資料型別的變數,equals繼承Object類,比較的是是否是同一個物件

    如果沒有對equals方法進行重寫,則比較的是引用型別的變數所指向的物件的地址;

    諸如String、Date等類對equals方法進行了重寫的話,比較的是所指向的物件的內容。
 

 1 public class Test {
 2     public static void main(String[] args) {
 3         String str1 = "abc";
 4         String str2 = "abc";
 5         System.out.println(str1==str2);//true
 6 
 7 
 8         String str3 = new String("abc");
 9         String str4 = new String ("abc");
10         System.out.println(str3==str4);//false
11         System.out.println(str3.equals(str4));//true
12     }
13 }

10、靜態變數和例項變數的區別

     在語法定義上的區別:靜態變數前要加static關鍵字,而例項變數前不加。

     在程式執行時的區別:例項變數屬於某個物件的屬性,必須建立了例項物件,其中的例項物件才會被分配空間,才能使用這個例項變數。靜態變數不屬於某個例項變數,而是屬於類,所以也被稱為類變數,只要程式載入了類的位元組碼,不用建立任務例項物件,靜態變數就會被分配空間,就可以被使用了。

總之,例項變數必須建立物件才可以通過這個物件來使用,靜態變數則可以直接使用類名來引用。

11、Math.round(11.5)等於12   , ,Math.round(-11.5)=-11;

12、privateprotectedpublic和default的區別

      

     public:可以被所有其他類所訪問

     private:只能被自己訪問和修改

     protected:自身、子類及同一個包中類可以訪問

     default:同一包中的類可以訪問,宣告時沒有加修飾符,認為是friendly。

13、Overload和Override的區別  

    Overload是過載的意思,Override是覆蓋的意思,也就是重寫。

    過載Overload:在同一個類中可以有多個名稱相同的方法,但這些方法的引數列表各不相同(即引數個數和型別不同)。

    重寫Override表示子類中的方法可以與父類中的某個方法的名稱和引數完全相同,通過子類建立的例項物件呼叫這個方法時,將呼叫子類中定義的方法,這相當於把父類中定義的那個完全相同的方法給覆蓋掉了,這也是面向物件程式設計的多型的一種表現。子類覆蓋父類方法時只能丟擲父類的異常或者異常的子類或者父類異常的子集,因為子類可以解決父類的一些問題,但不能比父類有更多的問題。還有,子類方法的訪問許可權只能比父類的更大,不能更小。如果父類的方法是private型別,則子類中根本不存在覆蓋,即子類中和父類的private的同名的方法沒有覆蓋的關係,因為private的訪問許可權只限於同一類中,而子類就不會訪問到private的方法,所以是子類中增加的一個全新的方法。

    過載overload的特點就是與返回值無關,只看引數列表,所以過載的方法是可以改變返回值型別的。所以,如果兩個方法的引數列表完全一樣,是不能通過讓他們的返回值型別不同來實現過載的。我們可以用反證法來說明這個問題,因為我們有時候呼叫一個方法時也可以不定義返回結果變數,即不要關心其返回結果,例如,我們呼叫map.remove(key)方法時,雖然remove方法有返回值,但是我們通常都不會定義接收返回結果的變數,這時候假設該類中有兩個名稱和引數列表完全相同的方法,僅僅是返回型別不同,java就無法確定程式設計者倒底是想呼叫哪個方法了,因為它無法通過返回結果型別來判斷。

    

14、構造器Constructor不能被繼承,因為不能重寫Override,但可以被過載Overload。

15、介面可以繼承介面,抽象類可以實現介面,抽象類可以繼承具體類,抽象類可以有靜態的main方法。

      抽象類與普通類唯一的區別就是不能建立例項物件和允許有abstract方法。

     兩者的語法區別:

     抽象類可以有構造方法,介面中不能有構造方法。

     抽象類可以有普通成員變數,介面中不能有普通成員變數。

     抽象類可以包含非抽象的普通方法,介面中的所有方法必須都是抽象的,不能有非抽象的普通方法。

     抽象類的抽象方法的訪問型別可以是public ,protected 和預設型別,但介面中的方法必須是public型別的,預設即為public abstract型別。

     抽象類中可以包含靜態方法,介面中不能包含靜態方法。

     抽象類和介面都可以包含靜態成員變數,抽象類中的靜態成員變數的訪問型別可以任意訪問,但介面中定義的變數只能是public static final 型別,並且預設即為public static final 型別。

     一個類可以實現多個介面,但是隻能繼承一個類。

     兩者在應用上的區別:

     介面更多的是在系統架構設計方法發揮作用,主要用於定義模組之間的通訊契約,而抽象類在程式碼方面發揮作用,實現程式碼的重寫。

16、abstract的method不可以是static的,因為抽象類的方法是要被子類實現的。

17、內部類就是在一個類的內部定義的類,內部類中不能定義靜態成員,內部類可以直接訪問外部類中的成員變數,內部類可以定義在外部類的方法外面,也可以定義在外部類的方法體中。

      內部類完全可以引用它的包含類的成員。如果不是靜態內部類,那沒有什麼限制!靜態內部類不能訪問外部類的成員!!

      匿名內部類可以繼承其他類或者實現其他介面。

18、super.getClass()方法呼叫

import java.util.Date;
public class Test1 extends Date{
	public static void main(String[] args) {
		new Test1().test();
	}
	public void test(){
		System.out.println(super.getClass().getName());
	}
}












//控制檯輸出:
//com.he.test.Test1

這屬於腦筋急轉彎的題目,,在test方法中,直接呼叫了getClass().getName()方法,返回的是Test1類名,,由於getClass()在Object類中定義成了fianl,子類不能覆蓋該方法,所以在test方法中呼叫getClass().getName()方法,其實就是在呼叫從父類繼承的getClass()方法,等效於呼叫super.getClass().getName()方法。。所以super.getClass().getName()方法返回的也是Test1。如果想得到父類的名稱,應該:getClass().getSuperClass().getName();

19、String是不可變的類,所以他的所有物件都是不可變物件。因為是不可變類,所以不可以繼承!

20、String s = new String("xyz"); 建立了兩個或者一個String Object 。。。  "xyz"對應一個物件,這個物件放在字串常量緩衝區,常量"xyz"不管出現多少遍,都是緩衝區中的那一個,New String 每寫一遍,就會建立一個新的物件,它依據那個常量"xyz"物件的內容來建立一個新的String物件。如果以前就用過"xyz",這句代表就不會建立"xyz"自己了,直接從緩衝區拿。

21、String 和StringBuffer

public class Test2 {

	public static void main(String[] args) {
		System.out.println(new String("abc").equals(new String("abc")));//true
		System.out.println(new StringBuffer("abc").equals(new StringBuffer("abc")));//false
	}
}

    String 方法實現了equals 方法,而StringBuffer沒實現equals 方法。

    String是不可變的,而StringBuffer是可變的執行緒安全的。當字元資料要改變的時候使用StringBuffer。

22、把一段逗號分隔的字串轉換成一個數組  

package com.he.test;

import java.util.StringTokenizer;

public class Test2 {
	// 把一段逗號分隔的字串轉換成一個數組
	public static void main(String[] args) {
		String str = "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z";
		String[] strArray = null;
		// 思路1:使用String的split方法
		System.out.println("呼叫convertStrToArray結果:");
		strArray = convertStrToArray(str);
		printArray(strArray);

		// 思路2:使用StringTokenizer實現
		// api說明:StringTokenizer 是出於相容性的原因而被保留的遺留類
		System.out.println("呼叫convertStrToArray2結果:");
		strArray = convertStrToArray2(str);
		printArray(strArray);
	}

	// 使用String的split 方法
	private static String[] convertStrToArray(String str) {
		String[] strArray = null;
		strArray = str.split(","); // 拆分字元為"," ,然後把結果交給陣列strArray
		return strArray;
	}

	// 使用StringTokenizer實現
	private static String[] convertStrToArray2(String str) {
		StringTokenizer st = new StringTokenizer(str, ",");// 把","作為分割標誌,然後把分割好的字元賦予StringTokenizer物件。
		String[] strArray = new String[st.countTokens()];
		int i = 0;
		while (st.hasMoreTokens()) {// 看看此 tokenizer 的字串中是否還有更多的可用標記。
			strArray[i++] = st.nextToken();// 返回此 string tokenizer 的下一個標記。
		}
		return strArray;
	}

	// 輸出陣列
	private static void printArray(String[] array) {
		System.out.print("[");
		for (int i = 0; i < array.length; i++) {
			System.out.print(array[i]);
			if (i == array.length - 1) {
				System.out.print("]");
			} else {
				System.out.print(",");
			}
			
		}
		
	}
}

23、找了一些連結

24、int 和 Integer 有什麼區別

int 是基本資料型別
     Integer是其包裝類,注意是一個類。
     為什麼要提供包裝類呢???
     一是為了在各種型別間轉化,通過各種方法的呼叫。否則 你無法直接通過變數轉化。

     比如,int要轉為String

int a=0;
String result=Integer.toString(a);

     在java中包裝類,比較多的用途是用在於各種資料型別的轉化中。

int num=Integer.valueOf("12");
int num2=Integer.parseInt("12");
double num3=Double.valueOf("12.2");
double num4=Double.parseDouble("12.2");
//其他的類似。通過基本資料型別的包裝來的valueOf和parseXX來實現String轉為XX
String a=String.valueOf("1234");//這裡括號中幾乎可以是任何型別
String b=String.valueOf(true);
String c=new Integer(12).toString();//通過包裝類的toString()也可以
String d=new Double(2.3).toString();

     使用泛型    

List<Integer> nums;

     這裡<>需要類。如果你用int。它會報錯的。

25、說說反射的用途及實現

    Java反射機制主要提供了以下功能:在執行時構造一個類的物件;判斷一個類所具有的成員變數和方法;呼叫一個物件的方法;生成動態代理。反射最大的應用就是框架。     

Java反射的主要功能:

     確定一個物件的類
     取出類的modifiers,資料成員,方法,構造器,和超類.
     找出某個接口裡定義的常量和方法說明.
     建立一個類例項,這個例項在執行時刻才有名字(執行時間才生成的物件).
     取得和設定物件資料成員的值,如果資料成員名是執行時刻確定的也能做到.
     在執行時刻呼叫動態物件的方法.
     建立陣列,陣列大小和型別在執行時刻才確定,也能更改陣列成員的值.

26、說說自定義註解的場景及實現

     登陸、許可權攔截、日誌處理,以及各種Java框架,如Spring,Hibernate,JUnit 提到註解就不能不說反射,Java自定義註解是通過執行時靠反射獲取註解。實際開發中,例如我們要獲取某個方法的呼叫日誌,可以通過AOP(動態代理機制)給方法新增切面,通過反射來獲取方法包含的註解,如果包含日誌註解,就進行日誌記錄。

27、HTTP 請求的 GET 與 POST 方式的區別   

     GET方法會把名值對追加在請求的URL後面。因為URL對字元數目有限制,進而限制了用在客戶端請求的引數值的數目。並且請求中的引數值是可見的,因此,敏感資訊不能用這種方式傳遞。

     POST方法通過把請求引數值放在請求體中來克服GET方法的限制,因此,可以傳送的引數的數目是沒有限制的。最後,通過POST請求傳遞的敏感資訊對外部客戶端是不可見的。

28、session 與 cookie 區別

     Cookie和Session都是會話技術,Cookie是執行在客戶端,Session是執行在伺服器端。
     Cookie有大小限制以及瀏覽器在存cookie的個數也有限制,Session是沒有大小限制和伺服器的記憶體大小有關。
     Cookie有安全隱患,通過攔截或本地檔案找得到你的cookie後可以進行攻擊。
     Session是儲存在伺服器端上會存在一段時間才會消失,如果session過多會增加伺服器的壓力。
     無論客戶端瀏覽器做怎麼樣的設定,session都應該能正常工作。客戶端可以選擇禁用 cookie,但是, session 仍然是能夠工作的,因為客戶端無法禁用服務端的 session。

29、List 和 Set 區別

     List,Set都是繼承自Collection介面

     List特點:元素有放入順序,元素可重複。Set特點:元素無放入順序,元素不可重複,重複元素會覆蓋掉。

     (注意:元素雖然無放入順序,但是元素在set中的位置是有該元素的HashCode決定的,其位置其實是固定的,加入Set 的Object必須定義equals()方法 ,另外list支援for迴圈,也就是通過下標來遍歷,也可以用迭代器,但是set只能用迭代,因為他無序,無法用下標來取得想要的值。)

     Set和List對比:

     Set:檢索元素效率低下,刪除和插入效率高,插入和刪除不會引起元素位置改變。

     List:和陣列類似,List可以動態增長,查詢元素效率高,插入刪除元素效率低,因為會引起其他元素位置改變。

30、List 和 Map 區別

     List是物件集合,允許物件重複。Map是鍵值對的集合,不允許key重複。

31、Arraylist 與 LinkedList 區別

    Arraylist:

    優點:ArrayList是實現了基於動態陣列的資料結構,因為地址連續,一旦資料儲存好了,查詢操作效率會比較高(在記憶體裡是連著放的)。

    缺點:因為地址連續, ArrayList要移動資料,所以插入和刪除操作效率比較低。

    LinkedList:

    優點:LinkedList基於連結串列的資料結構,地址是任意的,所以在開闢記憶體空間的時候不需要等一個連續的地址,對於新增和刪除操作add和remove,LinedList比較佔優勢。LinkedList 適用於要頭尾操作或插入指定位置的場景

    缺點:因為LinkedList要移動指標,所以查詢操作效能比較低。

當需要對資料進行對此訪問的情況下選用ArrayList,當需要對資料進行多次增加刪除修改時採用LinkedList。

32、HashMap 和 Hashtable 的區別

    1.hashMap去掉了HashTable 的contains方法,但是加上了containsValue()和containsKey()方法。

    2.hashTable同步的,而HashMap是非同步的,效率上比hashTable要高。

    3.hashMap允許空鍵值,而hashTable不允許。

33、HashSet 和 HashMap 區別

    set是線性結構,set中的值不能重複,hashset是set的hash實現,hashset中值不能重複是用hashmap的key來實現的。

    map是鍵值對對映,可以空鍵空值。HashMap是Map介面的hash實現,key的唯一性是通過key值hash值的唯一來確定,value值是則是連結串列結構。

    他們的共同點都是hash演算法實現的唯一性,他們都不能持有基本型別,只能持有物件

34、為何Map介面不繼承Collection介面?

    儘管Map介面和它的實現也是集合框架的一部分,但Map不是集合,集合也不是Map。因此,Map繼承Collection毫無意義,反之亦然。 
    如果Map繼承Collection介面,那麼元素去哪兒?Map包含key-value對,它提供抽取key或value列表集合的方法,但是它不適合“一組物件”規範。

35、Iterator和ListIterator的區別是什麼?

    Iterator可用來遍歷Set和List集合,但是ListIterator只能用來遍歷List。 
    Iterator對集合只能是前向遍歷,ListIterator既可以前向也可以後向。 
    ListIterator實現了Iterator介面,幷包含其他的功能,比如:增加元素,替換元素,獲取前一個和後一個元素的索引,等等。

36、Java中的HashMap的工作原理是什麼?

我們知道在Java中最常用的兩種結構是陣列和模擬指標(引用),幾乎所有的資料結構都可以利用這兩種來組合實現,HashMap也是如此。實際上HashMap是一個“連結串列雜湊”,資料結構:最左側是一個數組,陣列中的每一個元素都是一個連結串列,連結串列的每一個元素都是entry。

    HashMap是基於hashing的原理,我們使用put(key, value)儲存物件到HashMap中,使用get(key)從HashMap中獲取物件。當我們給put()方法傳遞鍵和值時,我們先對鍵呼叫hashCode()方法,返回的hashCode用於找到bucket位置來儲存Entry物件。

37、如果兩個鍵的hashcode相同,你如何獲取值物件?

當我們呼叫get()方法,HashMap會使用鍵物件的hashcode找到bucket位置,然後會呼叫keys.equals()方法去找到連結串列中正確的節點,最終找到要找的值物件。

38、HashMap 和 ConcurrentHashMap 的區別

     ConcurrentHashMap是執行緒安全的HashMap的實現。

  (1)ConcurrentHashMap對整個桶陣列進行了分割分段(Segment),然後在每一個分段上都用lock鎖進行保護,相對於HashTable的syn關鍵字鎖的粒度更精細了一些,併發效能更好,而HashMap沒有鎖機制,不是執行緒安全的。

  (2)HashMap的鍵值對允許有null,但是ConCurrentHashMap都不允許。

39、父類與子類之間的呼叫順序(列印結果)

a) 父類靜態程式碼塊

    b) 子類靜態程式碼塊

    c) 父類構造方法

    d) 子類構造方法

    e) 子類普通方法

    f) 重寫父類的方法,則列印重寫後的方法

40、建立執行緒的方式及實現

Java中建立執行緒主要有三種方式:

    一、繼承Thread類建立執行緒類

 (1)定義Thread類的子類,並重寫該類的run方法,該run方法的方法體就代表了執行緒要完成的任務。因此把run()方法稱為執行體。

 (2)建立Thread子類的例項,即建立了執行緒物件。

 (3)呼叫執行緒物件的start()方法來啟動該執行緒。

public class Thread4 extends Thread{

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

     上述程式碼中Thread.currentThread()方法返回當前正在執行的執行緒物件。getName()方法返回呼叫該方法的執行緒的名字。

     二、通過Runnable介面建立執行緒

   (1)定義runnable介面的實現類,並重寫該介面的run()方法,該run()方法的方法體同樣是該執行緒的執行緒執行體。

   (2)建立 Runnable實現類的例項,並依此例項作為Thread的target來建立Thread物件,該Thread物件才是真正的執行緒物件。

   (3)呼叫執行緒物件的start()方法來啟動該執行緒。

public class Thread5 implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for (int i = 0; i < 100; i++) {
			System.out.println(Thread.currentThread().getName()+"   "+i);
		}
	}
	public static void main(String[] args) throws InterruptedException {
		for (int i = 0; i < 100; i++) {
			System.out.println(Thread.currentThread().getName()+"    "+i);
			if(i == 20){
				Thread5 thread5 = new Thread5();
				new Thread(thread5,"新執行緒1").start();
				new Thread(thread5,"新執行緒2").start();
			}
		}
	}
}

     三、通過Callable和Future建立執行緒

   (1)建立Callable介面的實現類,並實現call()方法,該call()方法將作為執行緒執行體,並且有返回值。

  (2)建立Callable實現類的例項,使用FutureTask類來包裝Callable物件,該FutureTask物件封裝了該Callable物件的call()方法的返回值。

  (3)使用FutureTask物件作為Thread物件的target建立並啟動新執行緒。

  (4)呼叫FutureTask物件的get()方法來獲得子執行緒執行結束後的返回值

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

public class CallableThread implements Callable<Integer>{

	@Override
	public Integer call() throws Exception {
		// TODO Auto-generated method stub
		int i = 0;
		for (; i < 100; i++) {
			System.out.println(Thread.currentThread().getName()+"  "+i);
		}
		return i;
	}
	public static void main(String[] args) {
		CallableThread call = new CallableThread();
		FutureTask<Integer> task = new FutureTask<>(call);
		for (int i = 0; i < 100; i++) {
			System.out.println(Thread.currentThread().getName()+" 的迴圈變數i的值"+i);
			if(i == 20){
				new Thread(task,"有返回的執行緒").start();;
			}
		}
		try {
			System.out.println("子執行緒的返回值"+task.get());
		} catch (InterruptedException | ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

     建立執行緒的三種方式的對比

     採用實現Runnable、Callable介面的方式創見多執行緒時,優勢是:

     執行緒類只是實現了Runnable介面或Callable介面,還可以繼承其他類。

     在這種方式下,多個執行緒可以共享同一個target物件,所以非常適合多個相同執行緒來處理同一份資源的情況,從而可以將CPU、程式碼和資料分開,形成清晰的模型,較好地體現了面向物件的思想。

     劣勢是:程式設計稍微複雜,如果要訪問當前執行緒,則必須使用Thread.currentThread()方法。

     使用繼承Thread類的方式建立多執行緒時優勢是:

     編寫簡單,如果需要訪問當前執行緒,則無需使用Thread.currentThread()方法,直接使用this即可獲得當前執行緒。

     劣勢是:執行緒類已經繼承了Thread類,所以不能再繼承其他父類。

41、分散式環境下,怎麼保證執行緒安全

42、sleep() 、join()、yield()有什麼區別

1、sleep()方法

 在指定的毫秒數內讓當前正在執行的執行緒休眠(暫停執行),此操作受到系統計時器和排程程式精度和準確性的影響。 讓其他執行緒有機會繼續執行,但它並不釋放物件鎖。也就是如果有Synchronized同步塊,其他執行緒仍然不能訪問共享資料。注意該方法要捕獲異常。

     比如有兩個執行緒同時執行(沒有Synchronized),一個執行緒優先順序為MAX_PRIORITY,另一個為MIN_PRIORITY,如果沒有Sleep()方法,只有高優先順序的執行緒執行完成後,低優先順序的執行緒才能執行;但當高優先順序的執行緒sleep(5000)後,低優先順序就有機會執行了。
總之,sleep()可以使低優先順序的執行緒得到執行的機會,當然也可以讓同優先順序、高優先順序的執行緒有執行的機會。

     2、join()方法

     Thread的非靜態方法join()讓一個執行緒B“加入”到另外一個執行緒A的尾部。在A執行完畢之前,B不能工作。

     比如在B執行緒呼叫了A的join()方法,直到執行緒A結束後,才會繼續執行執行緒B。

     保證當前執行緒停止執行,直到該執行緒所加入的執行緒完成為止。然而,如果它加入的執行緒沒有存活,則當前執行緒不需要停止。

     3、yield()方法

     yield()方法和sleep()方法類似,也不會釋放“鎖標誌”,區別在於,它沒有引數,即yield()方法只是使當前執行緒重新回到可執行狀態,所以執行yield()的執行緒有可能在進入到可執行狀態後馬上又被執行,另外yield()方法只能使同優先順序或者高優先順序的執行緒得到執行機會,這也和sleep()方法不同。

43、說說 CountDownLatch 原理

44、說說 CyclicBarrier 原理

45、說說 Semaphore 原理

46、說說 Exchanger 原理

47、ThreadLocal 原理分析

48、講講執行緒池的實現原理

     主要是ThreadPoolExecutor的實現原理

49、執行緒池的幾種方式

   newFixedThreadPool(int nThreads)
     建立一個固定長度的執行緒池,每當提交一個任務就建立一個執行緒,直到達到執行緒池的最大數量,這時執行緒規模將不再變化,當執行緒發生未預期的錯誤而結束時,執行緒池會補充一個新的執行緒

     newCachedThreadPool()
     建立一個可快取的執行緒池,如果執行緒池的規模超過了處理需求,將自動回收空閒執行緒,而當需求增加時,則可以自動新增新執行緒,執行緒池的規模不存在任何限制

     newSingleThreadExecutor()
     這是一個單執行緒的Executor,它建立單個工作執行緒來執行任務,如果這個執行緒異常結束,會建立一個新的來替代它;它的特點是能確保依照任務在佇列中的順序來序列執行

     newScheduledThreadPool(int corePoolSize)
     建立了一個固定長度的執行緒池,而且以延遲或定時的方式來執行任務,類似於Timer

50、執行緒的生命週期

(1)生命週期的五種狀態

     新建(new Thread)
     當建立Thread類的一個例項(物件)時,此執行緒進入新建狀態(未被啟動)。
     例如:Thread t1=new Thread();

     就緒(runnable)
     執行緒已經被啟動,正在等待被分配給CPU時間片,也就是說此時執行緒正在就緒佇列中排隊等候得到CPU資源。例如:t1.start();

     執行(running)
     執行緒獲得CPU資源正在執行任務(run()方法),此時除非此執行緒自動放棄CPU資源或者有優先順序更高的執行緒進入,執行緒將一直執行到結束。

     死亡(dead)
     當執行緒執行完畢或被其它執行緒殺死,執行緒就進入死亡狀態,這時執行緒不可能再進入就緒狀態等待執行。

     自然終止:正常執行run()方法後終止

     異常終止:呼叫stop()方法讓一個執行緒終止執行

     堵塞(blocked)
     由於某種原因導致正在執行的執行緒讓出CPU並暫停自己的執行,即進入堵塞狀態。

     正在睡眠:用sleep(long t) 方法可使執行緒進入睡眠方式。一個睡眠著的執行緒在指定的時間過去可進入就緒狀態。

     正在等待:呼叫wait()方法。(呼叫motify()方法回到就緒狀態)

     被另一個執行緒所阻塞:呼叫suspend()方法。(呼叫resume()方法恢復)

 (2)常用方法:

     void run()   建立該類的子類時必須實現的方法

     void start() 開啟執行緒的方法

     static void sleep(long t) 釋放CPU的執行權,不釋放鎖

     static void sleep(long millis,int nanos)

     final void wait()釋放CPU的執行權,釋放鎖

     final void notify()

     static void yied()可以對當前執行緒進行臨時暫停(讓執行緒將資源釋放出來)

51、說說執行緒安全問題

     執行緒安全是指要控制多個執行緒對某個資源的有序訪問或修改,而在這些執行緒之間沒有產生衝突。
     在Java裡,執行緒安全一般體現在兩個方面:
     1、多個thread對同一個java例項的訪問(read和modify)不會相互干擾,它主要體現在關鍵字synchronized。如ArrayList和Vector,HashMap和Hashtable(後者每個方法前都有synchronized關鍵字)。如果你在interator一個List物件時,其它執行緒remove一個element,問題就出現了。
     2、每個執行緒都有自己的欄位,而不會在多個執行緒之間共享。它主要體現在java.lang.ThreadLocal類,而沒有Java關鍵字支援,如像static、transient那樣。

52、悲觀鎖 樂觀鎖
     樂觀鎖 悲觀鎖
     是一種思想。可以用在很多方面。

     比如資料庫方面。
     悲觀鎖就是for update(鎖定查詢的行)
     樂觀鎖就是 version欄位(比較跟上一次的版本號,如果一樣則更新,如果失敗則要重複讀-比較-寫的操作。)

     JDK方面:
     悲觀鎖就是sync
     樂觀鎖就是原子類(內部使用CAS實現)

     本質來說,就是悲觀鎖認為總會有人搶我的。
     樂觀鎖就認為,基本沒人搶。

53、CAS 樂觀鎖

     樂觀鎖是一種思想,即認為讀多寫少,遇到併發寫的可能性比較低,所以採取在寫時先讀出當前版本號,然後加鎖操作(比較跟上一次的版本號,如果一樣則更新),如果失敗則要重複讀-比較-寫的操作。

     CAS是一種更新的原子操作,比較當前值跟傳入值是否一樣,一樣則更新,否則失敗。
     CAS頂多算是樂觀鎖寫那一步操作的一種實現方式罷了,不用CAS自己加鎖也是可以的。

54、ABA 問題

     ABA:如果另一個執行緒修改V值假設原來是A,先修改成B,再修改回成A,當前執行緒的CAS操作無法分辨當前V值是否發生過變化。

55、樂觀鎖的業務場景及實現方式

樂觀鎖(Optimistic Lock):
     每次獲取資料的時候,都不會擔心資料被修改,所以每次獲取資料的時候都不會進行加鎖,但是在更新資料的時候需要判斷該資料是否被別人修改過。如果資料被其他執行緒修改,則不進行資料更新,如果資料沒有被其他執行緒修改,則進行資料更新。由於資料沒有進行加鎖,期間該資料可以被其他執行緒進行讀寫操作。

     樂觀鎖:比較適合讀取操作比較頻繁的場景,如果出現大量的寫入操作,資料發生衝突的可能性就會增大,為了保證資料的一致性,應用層需要不斷的重新獲取資料,這樣會增加大量的查詢操作,降低了系統的吞吐量。

56、執行緒的基本狀態以及狀態之間的關係? 

    

      其中Running表示執行狀態,Runnable表示就緒狀態(萬事俱備,只欠CPU),Blocked表示阻塞狀態,阻塞狀態又有多種情況,可能是因為呼叫wait()方法進入等待池,也可能是執行同步方法或同步程式碼塊進入等鎖池,或者是呼叫了sleep()方法或join()方法等待休眠或其他執行緒結束,或是因為發生了I/O中斷。

57、Java中有幾種型別的流?

 位元組流和字元流。位元組流繼承於InputStream、OutputStream,字元流繼承於Reader、Writer。在java.io 包中還有許多其他的流,主要是為了提高效能和使用方便。關於Java的I/O需要注意的有兩點:一是兩種對稱性(輸入和輸出的對稱性,位元組和字元的對稱性);二是兩種設計模式(介面卡模式和裝潢模式)。另外Java中的流不同於C#的是它只有一個維度一個方向。

58、JSP和Servlet面試題

59、Spring面試題整理

60、ssh框架

61、資料庫SQL優化大總結之 百萬級資料庫優化方案

62、Sql中#和$的區別

     #{} 傳入值時,SQL解析時,引數是帶引號的,而${} 傳入值時,SQL解析,引數是不帶引號的。

     #方式能很大程度防止SQL注入,而$方式無法防止SQL注入。

63、靜態包含和動態包含的區別

靜態包含<%@include file="xxx.jsp"%>和動態包含<jsp:include page="xxx.jsp">

     1.<%@include file="xxx.jsp"%>為jsp中的編譯指令,其檔案的包含是發生在jsp向servlet轉換的時期,

     <jsp:include page="xxx.jsp">為jsp中的動作指令,其檔案的包含是發生在編譯時期,是將java檔案編譯到class檔案的時期

     2.使用靜態包含只能產生一個class檔案,而使用動態包含能產生多個class檔案

     3.使用靜態包含,包含頁面和被包含頁面的request物件為同一物件,因為靜態包含只是將被包含的頁面的內容複製到包含的頁面中去;而動態包含包含頁面和被包含頁面不是同一個頁面,被包含的頁面的request物件可以取到的引數範圍要相對大些,不僅可以取到傳遞到包含頁面的引數,同樣也能取得在包含頁面向下傳遞的引數 。

64、servlet和jsp的區別

  (1)jsp經編譯後就成了servlet,(JSP的本質就是Servlet,JVM只能識別java的類,不能識別JSP的程式碼,Web容器將JSP的程式碼編譯成JVM能夠識別的java類)

      (2)jsp更擅長於頁面顯示,servlet更擅長於邏輯控制

      (3)Servlet中沒有內建物件,Jsp中的內建物件都是必須通過HttpServletRequest物件,HttpServletResponse物件以及HttpServlet物件得到。Jsp是Servlet的一種簡化,使用Jsp只需要完成程式設計師需要輸出到客戶端的內容,Jsp中的Java指令碼如何鑲嵌到一個類中,由Jsp容器完成。 而Servlet則是個完整的Java類,這個類的Service方法用於生成對客戶端的響應。

      (4)JSP是Servlet技術的擴充套件,本質上就是Servlet的簡易方式。JSP編譯後是“類servlet”

      (5)Servlet和JSP最主要的不同點在於: Servlet的應用邏輯是在Java檔案中,並且完全從表示層中的HTML裡分離開來。              而JSP的情況是Java和HTML可以組合成一個副檔名為.jsp的檔案。                                                                                                  JSP側重於檢視,Servlet主要用於控制邏輯 Servlet更多的是類似於一個Controller,用來做控制。

65、session的特點有哪些?

      1.不是執行緒安全的,所以在設計軟體架構的同時,應該避免多個執行緒共享同一個session例項

      2.session例項是輕量級的,所謂輕量級就是指它的建立和銷燬不需要消耗太多的資源。

      3.在session中,每個資料庫操作都是在一個事務中進行的,這樣就可以隔離開不同的操作。

66、session的快取的作用

      1.減少訪問資料庫的頻率。應用程式從記憶體中讀取持久化物件的速度顯然比到資料庫中查詢資料的速度快多了,因此Session的快取可以提高資料訪問的效能。

      2.保證快取中的物件與資料庫中的相關記錄保持同步。當快取中持久化物件的狀態發生了變換,Session並不會立即執行相關的SQL語句,這使得Session能夠把幾條相關的SQL語句合併為一條SQL語句,以便減少訪問資料庫的次數,從而提高應用程式的效能

67、struts2和springMVC的區別:

       1.springMVC入口是一個servlet前端控制器(DispatcherServlet),struts2入口是一個filter過濾器(StrutsPrepareAndExecuteFilter)

       2.struts2通過在action類中定義成員變數接收引數,(屬性驅動和模型驅動),它只能使用多例模式管理action.

          springmvc通過在coontroller方法中定義形參接收引數,springmvc可以使用單例模式管理controller.     

       3.springmvc是基於方法開發的,註解開發中使用requestMapping將url和方法進行 對映,如果根據url找到controller類的方法生成一個handler處理器物件(只包括一個method).
          struts2是基於類開發的,每個請求過來建立一個action例項,例項物件中有若干個方法.
          開發中建議使用springmvc,springmvc方法更類似service業務方法.

       4.truts2採用值棧儲存請求和相應的資料,通過OGNL存取資料,springmvc通過引數繫結期將request請求內容解析,並給方法形參賦值.

       5.struts2和springmvc的速度是相當的,由於struts2的漏洞較多,很多企業使用springmvc

68、攔截器和過濾器的區別

1.攔截器機遇java反射機制的,而過濾器是基於函式回撥的。

       2.過濾器依賴於servlet容器,而攔截器不依賴於servlet容器

       3.攔截器只能對Action請求起作用,而過濾器可以對幾乎所有請求起作用

       4.攔截器可以訪問Action上下文、值棧裡的物件,而過濾器不能

       5.在Aciton的生命週期裡,攔截器可以多次呼叫,而過濾器只能在容器初始化時被呼叫一次。

69、struts2有哪些優點

       1.在軟體設計上struts2的應用可以不依賴於Servlet API和struts API。 Struts2的這種設計屬於無侵入式設計; 

       2.攔截器,實現如引數攔截注入等功能;

       3.類轉換器,可以把特殊的請求引數轉換成需要的型別

       4.多種表現層技術,如:JSP、freeMarker、Velocity等

       5.Struts2的輸入校驗可以對指定某個方法進行校驗;

       6.提供了全域性範圍、包範圍和Action範圍的國際化資原始檔管理實現 

70、ActionContext、ServletContext、pageContext的區別?

       1.ActionContext是當前的Action的上下文環境,通過ActionContext可以獲取到request、session、ServletContext等與Action有關的物件的引用; 

       2.ServletContext是域物件,一個web應用中只有一個ServletContext,生命週期伴隨整個web應用

       3.pageContext是JSP中的最重要的一個內建物件,可以通過pageContext獲取其他域物件的應用,同時它是一個域物件,作用範圍只針對當前頁面,當前頁面結束時,pageContext銷燬。生命週期是JSP四個域物件中最小的。

71、struts2的執行流程

       1.客戶端瀏覽器發出HTTP請求。
       2.根據web.xml配置,該請求被FilterDispatcher接收。
       3.根據struts.xml配置,找到需要呼叫的Action類和方法, 並通過IoC方式,將值注入給Aciton。
       4.Action呼叫業務邏輯元件處理業務邏輯,這一步包含表單驗證。
       5.Action執行完畢,根據struts.xml中的配置找到對應的返回結果result,並跳轉到相應頁面。
       6.返回HTTP響應到客戶端瀏覽器。

72、SpringMVC與Struts2的主要區別

     (1)springmvc的入口是一個servlet即前端控制器,而struts2入口是一個filter過慮器。

     (2)springmvc是基於方法開發,傳遞引數是通過方法形參,可以設計為單例或多例(建議單例),struts2是基於類開發,傳遞引數是通過類的屬性,只能設計為多例。

     (3)Struts採用值棧儲存請求和響應的資料,通過OGNL存取資料, springmvc通過引數解析器是將request物件內容進行解析成方法形參,將響應資料和頁面封裝成ModelAndView物件,最後又將模型資料通過request物件傳輸到頁面。 Jsp檢視解析器預設使用jstl。

73、執行緒同步有七種方式

     synchronized關鍵字

     有synchronized關鍵字修飾的程式碼塊

     特殊變數volatile

     使用重入鎖

     使用區域性變數實現

     使用阻塞佇列

     使用原子變數

74、Linux下檢視檔案內容的命令

檢視檔案內容的命令:

   cat     由第一行開始顯示內容,並將所有內容輸出

     tac     從最後一行倒序顯示內容,並將所有內容輸出

     more    根據視窗大小,一頁一頁的現實檔案內容

     less    和more類似,但其優點可以往前翻頁,而且進行可以搜尋字元

     head    只顯示頭幾行

     tail    只顯示最後幾行

     nl      類似於cat -n,顯示時輸出行號

     tailf   類似於tail -f 

75、內連線和外連線的區別

     1.內連線,顯示兩個表中有聯絡的所有資料;

   2.左連結,以左表為參照,顯示所有資料;

   3.右連結,以右表為參照顯示資料;

     4.全外連線: 包含左、右兩個表的全部行,不管另外一邊的表中是否存在與它們匹配的行。

     5.交叉連線: 生成笛卡爾積-它不使用任何匹配或者選取條件,而是直接將一個數據源中的每個行與另一個數據源的每個行都一一匹配

76、ArrayList list = new ArrayList(20);中的list擴充幾次

77、notify()與notifyAll()的區別

      notify():

      喚醒在此物件監視器上等待的單個執行緒。如果所有執行緒都在此物件上等待,則會選擇喚醒其中一個執行緒。選擇是任意性的,並在對實現做出決定時發生。執行緒通過呼叫其中一個 wait 方法,在物件的監視器上等待。

       直到當前執行緒放棄此物件上的鎖定,才能繼續執行被喚醒的執行緒。被喚醒的執行緒將以常規方式與在該物件上主動同步的其他所有執行緒進行競爭;例如,喚醒的執行緒在作為鎖定此物件的下一個執行緒方面沒有可靠的特權或劣勢。

       notifyAll():

       喚醒在此物件監視器上等待的所有執行緒。執行緒通過呼叫其中一個 wait 方法,在物件的監視器上等待。

        直到當前執行緒放棄此物件上的鎖定,才能繼續執行被喚醒的執行緒。被喚醒的執行緒將以常規方式與在該物件上主動同步的其他所有執行緒進行競爭;例如,喚醒的執行緒在作為鎖定此物件的下一個執行緒方面沒有可靠的特權或劣勢。