java基礎——(針對面試)
基礎
-
1 switch支援的型別:byte, short, int, char, enum,
注意:不支援long,double,JDK7之後,開始支援String。
//簡單示例 public class MyDemo { public static void main(String... args) { Demo demo = Demo.A; switch (demo) { case A: break; case B: break; } } enum Demo { A, B, C } }
-
2 if和switch的區別:
if :1.對具體的值進行判斷 2.對區間判斷 3.對運算結果是boolean型別的表示式進行判斷
switch :1.對具體的值進行判斷;2.值的個數通常是固定的。
對於幾個固定值的判斷,建議使用switch語句,因為switch語句會將具體的答案載入進記憶體,相對高效一點。
-
過載和重寫的區別
- 過載:允許存在一個以上的同名函式,只要它們的引數型別不同即可。
- 重寫:當子類繼承父類,沿襲了父類的功能到子類中,子類雖具備該功能,但功能內容不一致,這是使用覆蓋特性,保留父類的功能定義,並重寫功能內容。
-
單例模式
-
餓漢式
private static Single s = new Single ( ) ; private Single ( ) { } public static Single getInstance () { return s ; }
-
懶漢式
class Single { public static Single getInstance (){ if ( s== null ){ synchronized (Single.class){//鎖不讀可以提高效率 if ( s== null ){ s = new Single () ; } } return s ; } }
-
-
特殊關鍵字:final
1. 可以修飾類、函式、變數; 2. 被final修飾的類不可以被繼承。為了避免被繼承,被子類複寫。final class Demo { } 3. 被final修飾的方法不可以被複寫。final void show () { } 4. 被final 修飾的變數是一個常量,只能賦值一次。 5. 內部類定義在類中的區域性位置上時,只能訪問該區域性被final修飾的區域性變數。
-
異常:
try{}catch(){}finally{} 1.在catch中return(),finally{}會不會執行? 答:會,會在return之後執行。 2.finally()在什麼情況下不會執行 答:只有一種情況不會執行,當執行到System.exit(0)時,finally不會執行。
public class Test {
public static void main(String[] args) {
System.out.println("haha:" + haha(true));
}
private static boolean haha(boolean isTrue) {
try {
int i = 1 / 0;
return isTrue ? System.out.printf("return try !null ", "test") != null : System.out.printf("return try null ", "test") == null;
} catch (Exception e) {
System.out.println("catch");
return isTrue ? System.out.printf("return catch !null ", "test") != null : System.out.printf("return catch null ", "test") == null;
} finally {
System.out.println("");
System.out.println("finally");
}
}
}
```java
//列印結果:
catch
return catch !null
finally
haha:true
-
常見Runtime異常:
ArithmeticException, ClassCastException, IllegalArgumentException, IndexOutOfBoundsException, NullPointerException,
-
訪問許可權
許可權.png
Java靜態程式碼塊、建構函式、構造程式碼塊
-
先看下面一段程式碼,執行Test,會列印什麼?
package test; public class Test { public static void main(String... args) { TestA a; a = new TestA(); a = new TestA(); TestA aa = new TestA(); } } class TestA { { System.out.println("TestA code create"); } private TestB b = new TestB(); private static TestC c = new TestC(); public TestA() { System.out.println("TestA create"); } static { System.out.println("TestA static create"); } } class TestB { public TestB() { System.out.println("TestB create"); } } class TestC { public TestC() { System.out.println("TestC create"); } }
列印結果:
TestC create
TestA static create
TestA code create
TestB create
TestA create
TestA code create
TestB create
TestA create
TestA code create
TestB create
TestA create
-
static特點:
1. 隨著類的載入而載入(隨著類的消失而消失,生命週期最長) 2. 優先於物件存在 3. 被所有物件所共享 4. 可以直接被類所呼叫 5. static是一個修飾符,用於修飾成員
-
構造程式碼塊
作用:給物件進行初始化,物件一建立就執行,而且優先於建構函式執行。 和建構函式的區別: 構造程式碼塊是給所有物件進行統一初始化;而建構函式是給對應的物件初始化 構造程式碼塊中定義的是不同物件共性的初始化內容
-
靜態程式碼塊
static { 靜態程式碼塊中的執行語句; } 特點:隨著類的載入而執行,只執行一次(再new一個物件也不會執行,類只加載一次), 如果定義在有主函式的類中,則優先於主函式執行。用於給類進行初始化。 有些類不用建立物件,無法用建構函式初始化,就通過靜態程式碼塊初始化。 執行順序:靜態程式碼塊先執行,如果有物件,構造程式碼塊先執行,然後是建構函式。 如果沒有物件,則構造程式碼塊和建構函式都不會執行。
Java-封裝、繼承、多型
-
抽象類的特點:
1. 抽象方法一定在抽象類中。 2. 抽象方法和抽象類都必須被abstract關鍵字修飾。 3. 抽象類不可以用new建立物件,因為呼叫抽象方法沒有意義。 4. 抽象類中的抽象方法要被使用,必須由子類複寫所有的抽象方法後,建立子類物件呼叫。如果子類只覆蓋了部分抽象方法,那麼該子類還是一個抽象類。強迫子類複寫,強迫子類做一些事。 5. 抽象類中可以不定義抽象方法,如果不定義抽象方法,那麼抽象類的功能就是為了不讓該類建立物件。
-
抽象關鍵字不可以和哪些關鍵字共存?
答:(1)private不能:抽象方法就是給子類覆蓋的,私有了就不能覆蓋了。 (2)static不能:static可以直接用類名呼叫,而呼叫抽象方法沒有意義。 (3)final 不能:final修飾的方法不可以被複寫,修飾的類不可以被繼承。與abstract衝突。
-
介面的特點
● 介面是對外暴露的規則。 ● 介面是程式的功能擴充套件。 ● 介面可以多實現。 ● 類與介面直接是實現關係,而且類可以繼承一個類的同時實現多個介面。 ● 介面與介面之間可以有繼承關係,可以多繼承。 因為介面沒有方法體,不會存在兩個父類出現同一個方法但是方法體不同的情況, 不會引起衝突,如下: public class Test implements d{ public static void main(String... args) { } @Override public void as() { } } interface d extends e,f { } interface f{ void as(); } interface e{ void as(); }
-
介面和抽象類的異同點:
相同點:都是不斷向上抽取而來的。不可以被例項化 不同點: 1. 抽象類需要被繼承,而且只能單繼承;介面需要被實現,而且可以多實現 2. 抽象類中可以定義抽象方法和非抽象方法,子類繼承後,可以直接使用非抽象方法; 介面只能定義抽象方法,必須有子類實現。 3. 抽象類的繼承,是is a關係,在定義該體系的基本共性內容; 介面的實現是like a 關係,在定義體系額外功能。
-
繼承
子類的例項化過程: 結論:子類的所有的建構函式,預設都會訪問父類中空引數建構函式,因為子類中每一個建構函式內的第一行都有一句隱式的super() ; 當父類中沒有空引數的建構函式時,子類必須手動通過super或者this語句形式來指定要訪問的建構函式。 當然:子類的建構函式第一行也可以手動指定this語句來訪問本類中的建構函式, 子類中至少會有一個建構函式會訪問到父類中的建構函式。
-
物件的初始化過程,見下圖:
物件初始化過程.png
列印結果:
列印結果.png
執行緒
-
關於執行緒這塊,後期有時間會寫一個完整的深入的文章,這裡寫的都是比較簡單基礎的執行緒的一些知識。
-
建立執行緒的兩種方式:
1 繼承Thread類。
①.定義類繼承Thread;
②.複寫父類中的方法;目的:將自定義程式碼儲存在run方法中,讓執行緒執行。
③.呼叫執行緒的start方法,該方法有兩個作用:啟動執行緒,呼叫run方法
2 實現Runnable介面
1. 定義類實現Runnable介面。
2. 覆蓋Runnable介面中的run方法。
3. 通過Thread類建立執行緒物件。
4. 將Runnable介面的子類物件作為實際引數傳遞給Thread類的建構函式。
5. 呼叫Thread類的start方法開啟執行緒並呼叫Runnable介面子類的run方法。
-
實現方式和繼承方式有什麼區別?
1. 實現方式相比繼承方式的好處: 避免了單繼承的侷限性(單繼承只能繼承一個父類)。在定義執行緒時,建議使用實現方式。 2.存放程式碼的位置不一樣: 繼承Thread:執行緒程式碼存放Thread子類的run方法中 實現Runnable,執行緒程式碼存在介面的子類的run方法。
-
實現Runnable介面的好處:
1,將執行緒的任務從執行緒的子類中分離出來,進行了單獨的封裝。 按照面向物件的思想將任務的封裝成物件。 2,避免了java單繼承的侷限性。
-
同步的兩種表現形式:
-
1 同步程式碼塊
synchronized(物件){ 需要被同步的程式碼; }
-
2 同步函式。
將synchronized關鍵字作為修飾符放在函式上。
public synchronized void add()
-
* 同步函式用的是哪一個鎖:函式需要被物件呼叫,那麼該函式都有一個所屬物件引用,就是this,所以同步函式使用的鎖是this(物件)
* JDK1.5中提供了多執行緒升級解決方案,將同步synchronized替換成實現Lock操作,將Object中的wait,notify,notifyAll,替換成了Condition物件的await(),signal(),signalAll(),該物件可以通過Lock鎖進行獲取。
-
停止執行緒
原理:run方法結束 1. 使用intrrupt()方法。該方法是結束執行緒的凍結狀態,使執行緒回到執行狀態中來。 當執行緒處於凍結狀態,就不會結束讀取到標記,那麼執行緒就不會結束。 當沒有指定的方式讓凍結的執行緒恢復到執行狀態時,這時需要對凍結進行清除。 強制讓執行緒恢復到執行狀態中來,這樣就可以操作標記讓執行緒結束。 2. 定義迴圈結束標記。執行緒執行程式碼一般都是迴圈,只要控制了迴圈即可。
-
執行緒常見方法
1 setDeamon() 守護執行緒:setDaemon(ture) ; 也稱後臺執行緒,當前臺執行緒執行時後臺執行緒也在執行,但是當前臺執行緒全部執行完關閉時, 後臺執行緒也會跟著自動關閉,jvm退出。 !!該方法必須在啟動執行緒前呼叫。 2 join()等待該執行緒終止:一般用於臨時加入執行緒。 當A執行緒執行到了B執行緒的.join()方法時,A就會等待,等B執行緒都執行完,A才會執行 3 yield()方法:釋放執行權,讓其他執行緒執行。 暫停當前正在執行的執行緒物件,並執行其他執行緒。
-
一個死鎖的demo
class Test implements Runnable { private boolean flag; Test(boolean flag) { this.flag = flag; } public void run() { if (flag) { while (true) synchronized (MyLock.locka) { System.out.println(Thread.currentThread().getName() + "..if locka...."); synchronized (MyLock.lockb) { System.out.println(Thread.currentThread().getName() + "..if lockb...."); } } } else { while (true) synchronized (MyLock.lockb) { System.out.println(Thread.currentThread().getName() + "..else lockb...."); synchronized (MyLock.locka) { System.out.println(Thread.currentThread().getName() + "..else locka...."); } } } } } class MyLock { public static final Object locka = new Object(); public static final Object lockb = new Object(); } class DeadLockTest { public static void main(String[] args) { Test a = new Test(true); Test b = new Test(false); Thread t1 = new Thread(a); Thread t2 = new Thread(b); t1.start(); t2.start(); } }
-
wait和sleep的區別
1. wait 可以指定時間也可以不指定。sleep必須指定時間。 2. 在同步中,對CPU的執行權和鎖的處理不同: wait:釋放執行權,釋放鎖 sleep:釋放執行權,不釋放鎖
-
StringBuffer和StringBuilder的區別
StringBuffer是執行緒同步(安全)。如果是單執行緒,效率就比較低 StringBuilder是執行緒不同步。
集合
Java集合關係圖.png
Collection:單列集合
-
List 和 set
List:元素是有序的,元素可以重複,因為該集合體繫有索引 Set:元素是無序的,元素不可以重複(存入和取出的順序不一定一致)。 List特有方法:凡是可以操作角標的方法都是該體系特有的方法
-
List中常見的三個子類
1. ArrayList :底層的資料使用的是陣列結構。 特點:查詢速度很快,但是增刪稍慢。執行緒不同步,效率高 。 可變長度陣列,預設容量為10的空列表,如果超過了,則50%的增加 2. LinkedList :底層的資料使用的是連結串列資料結構。 特點:增刪數度很快,但是查詢稍慢。 3. Vector:底層使用的是陣列結構。列舉是Vector特有的取出方式 是同步的,效率較低,被ArrayList替代。最早出現的。 預設容量為10的空列表,如果超過了,則100%的增加.
-
LinkedList
JDK1.6版本出現的:pollFirst(),pollLast(),peekFirst() ,peekLast(),offerFirst(),offerLast() (如果連結串列為空,返回null )。 分別替代了remove 和 get 和add (如果連結串列為空,則丟擲異常)。
-
set常見子類
1. HashSet:底層資料結構是雜湊表。 HashSet是如何保證元素的唯一性的: 是通過元素的兩個方法,hashCode和equals來完成,如果元素的hashCode值相同, 才會判斷equals是否為true,如果元素的hashCode值不同,不會呼叫equals 。 開發時描述事物,需要往集合裡面存時,一般都要複寫hashCode和equals。
-
TreeSet底層的資料結構:二叉樹
保證資料元素唯一性的依據compareTo方法return 0,為0則表示是相同元素 ; 排序的兩種方式: TreeSet排序的第一種方式: 讓元素自身具備比較性。元素需要實現Comparable介面,覆蓋compareTo方法。這種方式也稱為元素的自然順序,或者叫做預設順序。 TreeSet的第二種排序方式: 當元素自身不具備比較性時,或具備的比較性不是所需要的,這是就需要讓集合自身具備比較性。 定義一個比較器,將比較器物件作為引數傳遞給TreeSet集合的建構函式。 定義一個類,實現Comparator介面,覆蓋compare方法 當兩種排序都存在時,以比較器為主。
-
泛型
泛型技術是給編譯器使用的技術,用於編譯時期。確保了型別的安全。 執行時,會將泛型去掉,生成的class檔案中是不帶泛型的,這個稱為泛型的擦除。 為什麼擦除呢?因為為了相容執行的類載入器。 泛型的補償:在類載入器原有基礎上,編寫一個補償程式。在執行時,通過反射, 獲取元素的型別進行轉換動作。不用使用者在強制轉換了。
Map:雙列集合
-
常見子類
Hashtable:底層是雜湊表資料結構,不可以存入null鍵null值,該集合是執行緒同步的。jdk1.0 ,效率低 。 HashMap:底層是雜湊表資料結構,並允許使用null鍵null值,該集合不是同步的,jdk1.2 ,效率高。 TreeMap :底層是二叉樹資料結構,執行緒不同步,可以給map集合中的鍵進行排序 。 Map 和 Set很像 :其實,Set底層使用了Map集合 。
-
map集合的兩種取出方式:
1.Set<K> KeySet: 將Map中所有的Key存到了Set集合中,因為Set集合具備迭代器。 所有可以迭代方式取出所有的鍵,再根據get方法,獲取每一個鍵對應的值 Map集合的取出原理:將Map集合轉成Set集合,再通過迭代器取出 2.Set<Map.Entry<K,V>> entrySet:將Map集合中的對映關係存入到了Set集合中,而這個關係的資料型別就是:Map.Entry。 Map.Entry :其實Entry也是一個介面,它是Map介面中的一個內部介面。 先有Map,才有對映關係,所有Entry類定義在Map內部
-
Math類:
double d = Math.ceil(12.56);// 13.0 。ceil返回大於指定整數的最小整數 double d1 =Math.floor(12.34);//12.0 。floor返回小於指定資料的最大整數 long l = Math.round(12.64);//四捨五入 double d2 = Math.pow(2,3);//冪運算 :2^3 = 8
io
-
位元組流:InputStream(讀) OutputStream(寫)
-
RandomAccessFile(斷點下載會用到的類):
隨機訪問檔案,自身具備讀寫的方法。 通過skipBytes(int x),seek(int x)來達到隨機訪問。
seek(int x):調整物件中指標,指標跳轉,可以實現對資料指定位置的讀取和寫入。
-
IO流體系:
字元流: Reader |--BufferedReader: |--LineNumberReader |--CharArrayReader |--StringReader |--InputStreamReaer |--FileReader Writer |--BufferedWriter |--CharArrayWriter |--StringWriter |--OutputStreamWriter |--FileWriter |--PrintWriter 位元組流: InputStream |--FileInputStream: |--FilterInputStream |--BufferedInputStream |--DataInputStream |--ByteArrayInputStream |--ObjectInputStream |--SequenceInputStream |--PipedInputStream OutputStream |--FileOutputStream |--FilterOutputStream |--BufferedOutputStream |--DataOutputStream |--ByteArrayOutputStream |--ObjectOutputStream |--PipedOutputStream |--PrintStream
-
示例:讀出C盤下txt檔案
public static void listDemo_2() { File dir = new File("c:\\"); String[] names = dir.list(new SuffixFilter(".txt")); for(String name : names){ System.out.println(name); } } public class SuffixFilter implements FilenameFilter { private String suffix ; public SuffixFilter(String suffix) { super(); this.suffix = suffix; } @Override public boolean accept(File dir, String name) { return name.endsWith(suffix); } }
-
示例:深度遞迴,讀出制定目錄下的所有檔案和資料夾,包括子目錄。
public class FileTest { public static void main(String[] args) { File dir = new File("D:\\me\\mime\\RuntimePermissions"); listAll(dir,0); } /** * * @param dir * @param spaceLevel 這個是為了列印結果好看,與空格有關的引數 */ public static void listAll(File dir,int spaceLevel) { System.out.println(getSpace(spaceLevel)+dir.getName()); //獲取指定目錄下當前的所有資料夾或者檔案物件 spaceLevel++; File[] files = dir.listFiles(); for(int x=0; x<files.length; x++){ if(files[x].isDirectory()){ listAll(files[x],spaceLevel); } else System.out.println(getSpace(spaceLevel)+files[x].getName()); } } private static String getSpace(int spaceLevel) { StringBuilder builder = new StringBuilder(); builder.append("|--"); for(int x=0; x<spaceLevel; x++){ builder.insert(0,"| "); } return builder.toString(); } }