<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java 基礎(chǔ)常見知識點&面試題總結(jié)(下),2022 最新版!

          共 13896字,需瀏覽 28分鐘

           ·

          2022-06-27 22:29

          《Java 面試指北》來啦!這是一份教你如何更高效地準備面試的小冊,涵蓋常見八股文(系統(tǒng)設(shè)計、常見框架、分布式、高并發(fā) ......)、優(yōu)質(zhì)面經(jīng)等內(nèi)容。


          JavaGuide 在線閱讀網(wǎng)站:https://javaguide.cn/

          你好,我是 Guide。秋招即將到來,我對 JavaGuide 的內(nèi)容進行了重構(gòu)完善,公眾號同步一下最新更新,希望能夠幫助你。

          你也可以在網(wǎng)站(javaguide.cn)上在線閱讀,閱讀體驗會更好!

          前兩篇:

          異常

          Java 異常類層次結(jié)構(gòu)圖概覽

          Exception 和 Error 有什么區(qū)別?

          在 Java 中,所有的異常都有一個共同的祖先 java.lang 包中的 Throwable 類。Throwable 類有兩個重要的子類:

          • Exception :程序本身可以處理的異常,可以通過 catch 來進行捕獲。Exception 又可以分為 Checked Exception (受檢查異常,必須處理) 和 Unchecked Exception (不受檢查異常,可以不處理)。
          • ErrorError 屬于程序無法處理的錯誤 ,我們沒辦法通過 catch 來進行捕獲不建議通過catch捕獲 。例如 Java 虛擬機運行錯誤(Virtual MachineError)、虛擬機內(nèi)存不夠錯誤(OutOfMemoryError)、類定義錯誤(NoClassDefFoundError)等 。這些異常發(fā)生時,Java 虛擬機(JVM)一般會選擇線程終止。

          Checked Exception 和 Unchecked Exception 有什么區(qū)別?

          Checked Exception 即 受檢查異常 ,Java 代碼在編譯過程中,如果受檢查異常沒有被 catch或者throws 關(guān)鍵字處理的話,就沒辦法通過編譯。

          比如下面這段 IO 操作的代碼:

          除了RuntimeException及其子類以外,其他的Exception類及其子類都屬于受檢查異常 。常見的受檢查異常有:IO 相關(guān)的異常、ClassNotFoundExceptionSQLException...。

          Unchecked Exception不受檢查異常 ,Java 代碼在編譯過程中 ,我們即使不處理不受檢查異常也可以正常通過編譯。

          RuntimeException 及其子類都統(tǒng)稱為非受檢查異常,常見的有(建議記下來,日常開發(fā)中會經(jīng)常用到):

          • NullPointerException(空指針錯誤)
          • IllegalArgumentException(參數(shù)錯誤比如方法入?yún)㈩愋湾e誤)
          • NumberFormatException(字符串轉(zhuǎn)換為數(shù)字格式錯誤,IllegalArgumentException的子類)
          • ArrayIndexOutOfBoundsException(數(shù)組越界錯誤)
          • ClassCastException(類型轉(zhuǎn)換錯誤)
          • ArithmeticException(算術(shù)錯誤)
          • SecurityException (安全錯誤比如權(quán)限不夠)
          • UnsupportedOperationException(不支持的操作錯誤比如重復創(chuàng)建同一用戶)
          • ......

          Throwable 類常用方法有哪些?

          • String getMessage(): 返回異常發(fā)生時的簡要描述
          • String toString(): 返回異常發(fā)生時的詳細信息
          • String getLocalizedMessage(): 返回異常對象的本地化信息。使用 Throwable 的子類覆蓋這個方法,可以生成本地化信息。如果子類沒有覆蓋該方法,則該方法返回的信息與 getMessage()返回的結(jié)果相同
          • void printStackTrace(): 在控制臺上打印 Throwable 對象封裝的異常信息

          try-catch-finally 如何使用?

          • try塊 :用于捕獲異常。其后可接零個或多個 catch 塊,如果沒有 catch 塊,則必須跟一個 finally 塊。
          • *catch塊 :用于處理 try 捕獲到的異常。
          • finally 塊 :無論是否捕獲或處理異常,finally 塊里的語句都會被執(zhí)行。當在 try 塊或 catch 塊中遇到 return 語句時,finally 語句塊將在方法返回之前被執(zhí)行。

          代碼示例:

          try {
              System.out.println("Try to do something");
              throw new RuntimeException("RuntimeException");
          catch (Exception e) {
              System.out.println("Catch Exception -> " + e.getMessage());
          finally {
              System.out.println("Finally");
          }

          輸出:

          Try to do something
          Catch Exception -> RuntimeException
          Finally

          注意:不要在 finally 語句塊中使用 return! 當 try 語句和 finally 語句中都有 return 語句時,try 語句塊中的 return 語句會被忽略。這是因為 try 語句中的 return 返回值會先被暫存在一個本地變量中,當執(zhí)行到 finally 語句中的 return 之后,這個本地變量的值就變?yōu)榱?finally 語句中的 return 返回值。

          jvm 官方文檔中有明確提到:

          If the try clause executes a return, the compiled code does the following:

          1. Saves the return value (if any) in a local variable.
          2. Executes a jsr to the code for the finally clause.
          3. Upon return from the finally clause, returns the value saved in the local variable.

          代碼示例:

          public static void main(String[] args) {
              System.out.println(f(2));
          }

          public static int f(int value) {
              try {
                  return value * value;
              } finally {
                  if (value == 2) {
                      return 0;
                  }
              }
          }

          輸出:

          0

          finally 中的代碼一定會執(zhí)行嗎?

          不一定的!在某些情況下,finally 中的代碼不會被執(zhí)行。

          就比如說 finally 之前虛擬機被終止運行的話,finally 中的代碼就不會被執(zhí)行。

          try {
              System.out.println("Try to do something");
              throw new RuntimeException("RuntimeException");
          catch (Exception e) {
              System.out.println("Catch Exception -> " + e.getMessage());
              // 終止當前正在運行的Java虛擬機
              System.exit(1);
          finally {
              System.out.println("Finally");
          }

          輸出:

          Try to do something
          Catch Exception -> RuntimeException

          另外,在以下 2 種特殊情況下,finally 塊的代碼也不會被執(zhí)行:

          1. 程序所在的線程死亡。
          2. 關(guān)閉 CPU。

          相關(guān) issue:https://github.com/Snailclimb/JavaGuide/issues/190。

          ???? 進階一下:從字節(jié)碼角度分析try catch finally這個語法糖背后的實現(xiàn)原理。

          如何使用 try-with-resources 代替try-catch-finally

          1. 適用范圍(資源的定義): 任何實現(xiàn) java.lang.AutoCloseable或者 java.io.Closeable 的對象
          2. 關(guān)閉資源和 finally 塊的執(zhí)行順序:try-with-resources 語句中,任何 catch 或 finally 塊在聲明的資源關(guān)閉后運行

          《Effective Java》中明確指出:

          面對必須要關(guān)閉的資源,我們總是應(yīng)該優(yōu)先使用 try-with-resources 而不是try-finally。隨之產(chǎn)生的代碼更簡短,更清晰,產(chǎn)生的異常對我們也更有用。try-with-resources語句讓我們更容易編寫必須要關(guān)閉的資源的代碼,若采用try-finally則幾乎做不到這點。

          Java 中類似于InputStreamOutputStreamScannerPrintWriter等的資源都需要我們調(diào)用close()方法來手動關(guān)閉,一般情況下我們都是通過try-catch-finally語句來實現(xiàn)這個需求,如下:

          //讀取文本文件的內(nèi)容
          Scanner scanner = null;
          try {
              scanner = new Scanner(new File("D://read.txt"));
              while (scanner.hasNext()) {
                  System.out.println(scanner.nextLine());
              }
          catch (FileNotFoundException e) {
              e.printStackTrace();
          finally {
              if (scanner != null) {
                  scanner.close();
              }
          }

          使用 Java 7 之后的 try-with-resources 語句改造上面的代碼:

          try (Scanner scanner = new Scanner(new File("test.txt"))) {
              while (scanner.hasNext()) {
                  System.out.println(scanner.nextLine());
              }
          catch (FileNotFoundException fnfe) {
              fnfe.printStackTrace();
          }

          當然多個資源需要關(guān)閉的時候,使用 try-with-resources 實現(xiàn)起來也非常簡單,如果你還是用try-catch-finally可能會帶來很多問題。

          通過使用分號分隔,可以在try-with-resources塊中聲明多個資源。

          try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new File("test.txt")));
               BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))) {
              int b;
              while ((b = bin.read()) != -1) {
                  bout.write(b);
              }
          }
          catch (IOException e) {
              e.printStackTrace();
          }

          異常使用有哪些需要注意的地方?

          • 不要把異常定義為靜態(tài)變量,因為這樣會導致異常棧信息錯亂。每次手動拋出異常,我們都需要手動 new 一個異常對象拋出。
          • 拋出的異常信息一定要有意義。
          • 建議拋出更加具體的異常比如字符串轉(zhuǎn)換為數(shù)字格式錯誤的時候應(yīng)該拋出NumberFormatException而不是其父類IllegalArgumentException
          • 使用日志打印異常之后就不要再拋出異常了(兩者不要同時存在一段代碼邏輯中)。
          • ......

          泛型

          什么是泛型?有什么作用?

          Java 泛型(Generics) 是 JDK 5 中引入的一個新特性。使用泛型參數(shù),可以增強代碼的可讀性以及穩(wěn)定性。

          編譯器可以對泛型參數(shù)進行檢測,并且通過泛型參數(shù)可以指定傳入的對象類型。比如 ArrayList<Persion> persons = new ArrayList<Persion>() 這行代碼就指明了該 ArrayList 對象只能傳入 Persion 對象,如果傳入其他類型的對象就會報錯。

          ArrayList<E> extends AbstractList<E>

          并且,原生 List 返回類型是 Object ,需要手動轉(zhuǎn)換類型才能使用,使用泛型后編譯器自動轉(zhuǎn)換。

          泛型的使用方式有哪幾種?

          泛型一般有三種使用方式:泛型類泛型接口泛型方法

          1.泛型類

          //此處T可以隨便寫為任意標識,常見的如T、E、K、V等形式的參數(shù)常用于表示泛型
          //在實例化泛型類時,必須指定T的具體類型
          public class Generic<T>{

              private T key;

              public Generic(T key) {
                  this.key = key;
              }

              public T getKey(){
                  return key;
              }
          }

          如何實例化泛型類:

          Generic<Integer> genericInteger = new Generic<Integer>(123456);

          2.泛型接口

          public interface Generator<T{
              public T method();
          }

          實現(xiàn)泛型接口,不指定類型:

          class GeneratorImpl<Timplements Generator<T>{
              @Override
              public T method() {
                  return null;
              }
          }

          實現(xiàn)泛型接口,指定類型:

          class GeneratorImpl<Timplements Generator<String>{
              @Override
              public String method() {
                  return "hello";
              }
          }

          3.泛型方法

             public static < E > void printArray( E[] inputArray )
             
          {
                   for ( E element : inputArray ){
                      System.out.printf( "%s ", element );
                   }
                   System.out.println();
              }

          使用:

          // 創(chuàng)建不同類型數(shù)組:Integer, Double 和 Character
          Integer[] intArray = { 123 };
          String[] stringArray = { "Hello""World" };
          printArray( intArray  );
          printArray( stringArray  );

          注意: public static < E > void printArray( E[] inputArray ) 一般被稱為靜態(tài)泛型方法;在 java 中泛型只是一個占位符,必須在傳遞類型后才能使用。類在實例化時才能真正的傳遞類型參數(shù),由于靜態(tài)方法的加載先于類的實例化,也就是說類中的泛型還沒有傳遞真正的類型參數(shù),靜態(tài)的方法的加載就已經(jīng)完成了,所以靜態(tài)泛型方法是沒有辦法使用類上聲明的泛型的。只能使用自己聲明的 <E>

          項目中哪里用到了泛型?

          • 自定義接口通用返回結(jié)果 CommonResult<T> 通過參數(shù) T 可根據(jù)具體的返回類型動態(tài)指定結(jié)果的數(shù)據(jù)類型
          • 定義 Excel 處理類 ExcelUtil<T> 用于動態(tài)指定 Excel 導出的數(shù)據(jù)類型
          • 構(gòu)建集合工具類(參考 Collections 中的 sort, binarySearch 方法)。
          • ......

          反射

          何為反射?

          如果說大家研究過框架的底層原理或者咱們自己寫過框架的話,一定對反射這個概念不陌生。

          反射之所以被稱為框架的靈魂,主要是因為它賦予了我們在運行時分析類以及執(zhí)行類中方法的能力。通過反射你可以獲取任意一個類的所有屬性和方法,你還可以調(diào)用這些方法和屬性。

          反射機制優(yōu)缺點

          • 優(yōu)點 :可以讓咱們的代碼更加靈活、為各種框架提供開箱即用的功能提供了便利
          • 缺點 :讓我們在運行時有了分析操作類的能力,這同樣也增加了安全問題。比如可以無視泛型參數(shù)的安全檢查(泛型參數(shù)的安全檢查發(fā)生在編譯時)。另外,反射的性能也要稍差點,不過,對于框架來說實際是影響不大的。Java Reflection: Why is it so slow?

          反射的應(yīng)用場景

          像咱們平時大部分時候都是在寫業(yè)務(wù)代碼,很少會接觸到直接使用反射機制的場景。

          但是,這并不代表反射沒有用。相反,正是因為反射,你才能這么輕松地使用各種框架。像 Spring/Spring Boot、MyBatis 等等框架中都大量使用了反射機制。

          這些框架中也大量使用了動態(tài)代理,而動態(tài)代理的實現(xiàn)也依賴反射。

          比如下面是通過 JDK 實現(xiàn)動態(tài)代理的示例代碼,其中就使用了反射類 Method 來調(diào)用指定的方法。

          public class DebugInvocationHandler implements InvocationHandler {
              /**
               * 代理類中的真實對象
               */

              private final Object target;

              public DebugInvocationHandler(Object target) {
                  this.target = target;
              }

              public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
                  System.out.println("before method " + method.getName());
                  Object result = method.invoke(target, args);
                  System.out.println("after method " + method.getName());
                  return result;
              }
          }

          另外,像 Java 中的一大利器 注解 的實現(xiàn)也用到了反射。

          為什么你使用 Spring 的時候 ,一個@Component注解就聲明了一個類為 Spring Bean 呢?為什么你通過一個 @Value注解就讀取到配置文件中的值呢?究竟是怎么起作用的呢?

          這些都是因為你可以基于反射分析類,然后獲取到類/屬性/方法/方法的參數(shù)上的注解。你獲取到注解之后,就可以做進一步的處理。

          注解

          Annotation (注解) 是 Java5 開始引入的新特性,可以看作是一種特殊的注釋,主要用于修飾類、方法或者變量。

          注解本質(zhì)是一個繼承了Annotation 的特殊接口:

          @Target(ElementType.METHOD)
          @Retention(RetentionPolicy.SOURCE)
          public @interface Override {

          }

          public interface Override extends Annotation{

          }

          注解只有被解析之后才會生效,常見的解析方法有兩種:

          • 編譯期直接掃描 :編譯器在編譯 Java 代碼的時候掃描對應(yīng)的注解并處理,比如某個方法使用@Override 注解,編譯器在編譯的時候就會檢測當前的方法是否重寫了父類對應(yīng)的方法。
          • 運行期通過反射處理 :像框架中自帶的注解(比如 Spring 框架的 @Value@Component)都是通過反射來進行處理的。

          JDK 提供了很多內(nèi)置的注解(比如 @Override@Deprecated),同時,我們還可以自定義注解。

          I/O

          什么是序列化?什么是反序列化?

          如果我們需要持久化 Java 對象比如將 Java 對象保存在文件中,或者在網(wǎng)絡(luò)傳輸 Java 對象,這些場景都需要用到序列化。

          簡單來說:

          • 序列化:將數(shù)據(jù)結(jié)構(gòu)或?qū)ο筠D(zhuǎn)換成二進制字節(jié)流的過程
          • 反序列化:將在序列化過程中所生成的二進制字節(jié)流轉(zhuǎn)換成數(shù)據(jù)結(jié)構(gòu)或者對象的過程

          對于 Java 這種面向?qū)ο缶幊陶Z言來說,我們序列化的都是對象(Object)也就是實例化后的類(Class),但是在 C++這種半面向?qū)ο蟮恼Z言中,struct(結(jié)構(gòu)體)定義的是數(shù)據(jù)結(jié)構(gòu)類型,而 class 對應(yīng)的是對象類型。

          維基百科是如是介紹序列化的:

          序列化(serialization)在計算機科學的數(shù)據(jù)處理中,是指將數(shù)據(jù)結(jié)構(gòu)或?qū)ο鬆顟B(tài)轉(zhuǎn)換成可取用格式(例如存成文件,存于緩沖,或經(jīng)由網(wǎng)絡(luò)中發(fā)送),以留待后續(xù)在相同或另一臺計算機環(huán)境中,能恢復原先狀態(tài)的過程。依照序列化格式重新獲取字節(jié)的結(jié)果時,可以利用它來產(chǎn)生與原始對象相同語義的副本。對于許多對象,像是使用大量引用的復雜對象,這種序列化重建的過程并不容易。面向?qū)ο笾械膶ο笮蛄谢⒉桓爬ㄖ霸紝ο笏P(guān)系的函數(shù)。這種過程也稱為對象編組(marshalling)。從一系列字節(jié)提取數(shù)據(jù)結(jié)構(gòu)的反向操作,是反序列化(也稱為解編組、deserialization、unmarshalling)。

          綜上:序列化的主要目的是通過網(wǎng)絡(luò)傳輸對象或者說是將對象存儲到文件系統(tǒng)、數(shù)據(jù)庫、內(nèi)存中。

          https://www.corejavaguru.com/java/serialization/interview-questions-1

          Java 序列化中如果有些字段不想進行序列化,怎么辦?

          對于不想進行序列化的變量,使用 transient 關(guān)鍵字修飾。

          transient 關(guān)鍵字的作用是:阻止實例中那些用此關(guān)鍵字修飾的的變量序列化;當對象被反序列化時,被 transient 修飾的變量值不會被持久化和恢復。

          關(guān)于 transient 還有幾點注意:

          • transient 只能修飾變量,不能修飾類和方法。
          • transient 修飾的變量,在反序列化后變量值將會被置成類型的默認值。例如,如果是修飾 int 類型,那么反序列后結(jié)果就是 0
          • static 變量因為不屬于任何對象(Object),所以無論有沒有 transient 關(guān)鍵字修飾,均不會被序列化。

          獲取用鍵盤輸入常用的兩種方法

          方法 1:通過 Scanner

          Scanner input = new Scanner(System.in);
          String s  = input.nextLine();
          input.close();

          方法 2:通過 BufferedReader

          BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
          String s = input.readLine();

          Java 中 IO 流分為幾種?

          • 按照流的流向分,可以分為輸入流和輸出流;
          • 按照操作單元劃分,可以劃分為字節(jié)流和字符流;
          • 按照流的角色劃分為節(jié)點流和處理流。

          Java IO 流共涉及 40 多個類,這些類看上去很雜亂,但實際上很有規(guī)則,而且彼此之間存在非常緊密的聯(lián)系, Java IO 流的 40 多個類都是從如下 4 個抽象類基類中派生出來的。

          • InputStream/Reader: 所有的輸入流的基類,前者是字節(jié)輸入流,后者是字符輸入流。
          • OutputStream/Writer: 所有輸出流的基類,前者是字節(jié)輸出流,后者是字符輸出流。

          按操作方式分類結(jié)構(gòu)圖:

          IO-操作方式分類

          按操作對象分類結(jié)構(gòu)圖:

          IO-操作對象分類

          既然有了字節(jié)流,為什么還要有字符流?

          問題本質(zhì)想問:不管是文件讀寫還是網(wǎng)絡(luò)發(fā)送接收,信息的最小存儲單元都是字節(jié),那為什么 I/O 流操作要分為字節(jié)流操作和字符流操作呢?

          回答:字符流是由 Java 虛擬機將字節(jié)轉(zhuǎn)換得到的,問題就出在這個過程還算是非常耗時,并且,如果我們不知道編碼類型就很容易出現(xiàn)亂碼問題。所以, I/O 流就干脆提供了一個直接操作字符的接口,方便我們平時對字符進行流操作。如果音頻文件、圖片等媒體文件用字節(jié)流比較好,如果涉及到字符的話使用字符流比較好。

          ··········  END  ··············

          歡迎加入我的知識星球獲取更多面試干貨《Java 面試指北》持續(xù)更新完善中!

          《Java 面試指北》目錄

          近期文章精選 :

          走近作者 :

          如果本文對你有幫助的話,歡迎點贊&在看&分享,這對我繼續(xù)分享&創(chuàng)作優(yōu)質(zhì)文章非常重要。感謝????

          瀏覽 64
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久撸在线视频 | 香蕉网站啊好硬 | 久久先锋| 亚洲色无码A片一区二小说 | 熟妇性爱视频 |