1. 程式人生 > >java基礎——(針對面試)

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();
          }
      }