當前位置: 華文星空 > 新聞

阿裏出品!2022最新的基礎面試題,快學起來

2022-04-25新聞

Java概述
何為編程
編程就是讓電腦為解決某個問題而使用某種程式設計語言編寫程式程式碼,並最終得到結果的過程。
為了使電腦能夠理解人的意圖,人類就必須要將需解決的問題的思路、方法、和手段透過電腦能夠理解的形式告訴電腦,使得電腦能夠根據人的指令一步一步去工作,完成某種特定的任務。這種人和電腦之間交流的過程就是編程。
什麽是Java
Java是一門物件導向程式語言,不僅吸收了C++語言的各種優點,還摒棄了C++裏難以理解的多繼承、指標等概念,因此Java語言具有功能強大和簡單易用兩個特征。Java語言作為靜態物件導向程式語言的代表,極好地實作了物件導向理論,允許程式設計師以優雅的思維方式進行復雜的編程 。
jdk1.5之後的三大版本
Java SE(J2SE,Java 2 Platform Standard Edition,標準版) Java SE 以前稱為 J2SE。它允許開發和部署在桌面、伺服器、嵌入式環境和即時環境中使用的 Java 應用程式。Java SE 包含了支持 Java Web 服務開發的類,並為Java EE和Java ME提供基礎。 Java EE(J2EE,Java 2 Platform Enterprise Edition,企業版) Java EE 以前稱為 J2EE。企業版本幫助開發和部署可移植、健壯、可伸縮且安全的伺服器端Java 應用程式。Java EE 是在 Java SE 的基礎上構建的,它提供 Web 服務、元件模型、管理和通訊 API,可以用來實作企業級的面向服務體系結構(service-oriented architecture,SOA)和 Web2.0應用程式。2018年2月,Eclipse 宣布正式將 JavaEE 更名為 JakartaEE Java ME(J2ME,Java 2 Platform Micro Edition,微型版) Java ME 以前稱為 J2ME。Java ME 為在流動通訊器材和嵌入式器材(比如手機、PDA、電視機頂盒和印表機)上執行的應用程式提供一個健壯且靈活的環境。Java ME 包括靈活的使用者介面、健壯的安全模型、許多內建的網絡協定以及對可以動態下載的連網和離線應用程式的豐富支持。基於 Java ME 規範的應用程式只需編寫一次,就可以用於許多器材,而且可以利用每個器材的本機功能。
JVM、JRE和JDK的關系
JVM Java Virtual Machine是Java虛擬機器,Java程式需要執行在虛擬機器上,不同的平台有自己的虛擬機器,因此Java語言可以實作跨平台。
JRE Java Runtime Environment包括Java虛擬機器和Java程式所需的核心類別庫等。核心類別庫主要是java.lang包:包含了執行Java程式必不可少的系統類,如基本數據類別、基本數學函數、字串處理、執行緒、例外處理類等,系統缺省載入這個包
如果想要執行一個開發好的Java程式,電腦中只需要安裝JRE即可。
JDK Java Development Kit是提供給Java開發人員使用的,其中包含了Java的開發工具,也包括了JRE。所以安裝了JDK,就無需再單獨安裝JRE了。其中的開發工具:編譯工具(javac.exe),打包工具(jar.exe)等
JVM&JRE&JDK關系圖


image.png
什麽是跨平台性?原理是什麽
所謂跨平台性,是指java語言編寫的程式,一次編譯後,可以在多個系統平台上執行。
實作原理:Java程式是透過java虛擬機器在系統平台上執行的,只要該系統可以安裝相應的java虛擬機器,該系統就可以執行java程式。
Java語言有哪些特點
簡單易學(Java語言的語法與C語言和C++語言很接近)
物件導向(封裝,繼承,多型)
平台無關性(Java虛擬機器實作平台無關性)
支持網絡編程並且很方便(Java語言誕生本身就是為簡化網絡編程設計的)
支持多執行緒(多執行緒機制使應用程式在同一時間並列執行多項任)
健壯性(Java語言的強類別機制、例外處理、垃圾的自動收集等)
安全性
什麽是字節碼?采用字節碼的最大好處是什麽
字節碼:Java原始碼經過虛擬機器編譯器編譯後產生的檔(即擴充套件為. class的檔),它不面向任何特定的處理器,只面向虛擬機器。
采用字節碼的好處:
Java語言透過字節碼的方式,在一定程度上解決了傳統解釋型語言執行效率低的問題,同時又保留了解釋型語言可移植的特點。所以Java程式執行時比較高效,而且,由於字節碼並不專對一種特定的機器,因此,Java程式無須重新編譯便可在多種不同的電腦上執行。
先看下java中的編譯器和直譯器:
Java中引入了虛擬機器的概念,即在機器和編譯程式之間加入了一層抽象的虛擬機器器。這台虛擬的機器在任何平台上都提供給編譯程式一個的共同的介面。編譯程式只需要面向虛擬機器,生成虛擬機器能夠理解的程式碼,然後由直譯器來將虛擬機器程式碼轉換為特定系統的機器碼執行。在Java中,這種供虛擬機器理解的程式碼叫做字節碼(即擴充套件為. class的檔),它不面向任何特定的處理器,只面向虛擬機器。每一種平台的直譯器是不同的,但是實作的虛擬機器是相同的。Java源程式經過編譯器編譯後變成字節碼,字節碼由虛擬機器解釋執行,虛擬機器將每一條要執行的字節碼送給直譯器,直譯器將其轉譯成特定機器上的機器碼,然後在特定的機器上執行,這就是上面提到的Java的特點的編譯與解釋並存的解釋。
Java原始碼---->編譯器---->jvm可執行的Java字節碼(即虛擬指令)---->jvm---->jvm中直譯器----->機器可執行的二進制機器碼---->程式執行。
什麽是Java程式的主類?應用程式和小程式的主類有何不同?
一個程式中可以有多個類,但只能有一個類是主類。在Java應用程式中,這個主類是指包含main()方法的類。而在Java小程式中,這個主類是一個繼承自系統類JApplet或Applet的子類別。應用程式的主類不一定要求是public類,但小程式的主類要求必須是public類。主類是Java程式執行的入口點。
Java應用程式與小程式之間有那些差別?
簡單說應用程式是從主執行緒啟動(也就是main()方法)。applet小程式沒有main方法,主要是嵌在瀏覽器頁面上執行(呼叫init()執行緒或者run()來啟動),嵌入瀏覽器這點跟flash的小遊戲類似。
Java和C++的區別
我知道很多人沒學過C++,但是面試官就是沒事喜歡拿咱們Java和C++比呀!沒辦法!!!就算沒學過C++,也要記下來!
都是物件導向的語言,都支持封裝、繼承和多型 Java不提供指標來直接存取記憶體,程式記憶體更加安全 Java的類是單繼承的,C++支持多重繼承;雖然Java的類不可以多繼承,但是介面可以多繼承。 Java有自動記憶體管理機制,不需要程式設計師手動釋放無用記憶體
Oracle JDK 和 OpenJDK 的對比
Oracle JDK版本將每三年釋出一次,而OpenJDK版本每三個月釋出一次;
OpenJDK 是一個參考模型並且是完全開源的,而Oracle JDK是OpenJDK的一個實作,並不是完全開源的;
Oracle JDK 比 OpenJDK 更穩定。OpenJDK和Oracle JDK的程式碼幾乎相同,但Oracle JDK有更多的類和一些錯誤修復。因此,如果您想開發企業/商業軟件,我建議您選擇Oracle JDK,因為它經過了徹底的測試和穩定。某些情況下,有些人提到在使用OpenJDK 可能會遇到了許多應用程式崩潰的問題,但是,只需切換到Oracle JDK就可以解決問題;
在響應性和JVM效能方面,Oracle JDK與OpenJDK相比提供了更好的效能;
Oracle JDK不會為即將釋出的版本提供長期支持,使用者每次都必須透過更新到最新版本獲得支持來獲取最新版本;
Oracle JDK根據二進制程式碼特許協議獲得特許,而OpenJDK根據GPL v2特許獲得特許。
基礎語法
數據類別
Java有哪些數據類別
定義:Java語言是強類別語言,對於每一種數據都定義了明確的具體的數據類別,在記憶體中分配了不同大小的記憶體空間。
分類
基本數據類別

  • 數值型
  • 整數類別(byte,short,int,long)
  • 浮點類別(float,double)
  • 字元型(char)
  • 布爾型(boolean)
    參照數據類別
  • 類( class)
  • 介面(interface)
  • 陣列([])
  • Java基本數據類別圖


    image.png
    switch 是否能作用在 byte 上,是否能作用在 long 上,是否能作用在 String 上
    在 Java 5 以前,switch(expr)中,expr 只能是 byte、short、char、int。從 Java5 開始,Java 中引入了列舉類別,expr 也可以是 enum 類別,從 Java 7 開始,expr 還可以是字串(String),但是長整型(long)在目前所有的版本中都是不可以的。
    用最有效率的方法計算 2 乘以 8
    2 << 3(左移 3 位相當於乘以 2 的 3 次方,右移 3 位相當於除以 2 的 3 次方)。
    Math.round(11.5) 等於多少?Math.round(-11.5)等於多少
    Math.round(11.5)的返回值是 12,Math.round(-11.5)的返回值是-11。四舍五入的原理是在參數上加 0.5 然後進行下取整。
    float f=3.4;是否正確
    不正確。3.4 是雙精度數,將雙精度型(double)賦值給浮點型(float)屬於下轉型(down-casting,也稱為窄化)會造成精度損失,因此需要強制類別轉換float f =(float)3.4; 或者寫成 float f =3.4F;。
    short s1 = 1; s1 = s1 + 1;有錯嗎?short s1 = 1; s1 += 1;有錯嗎
    對於 short s1 = 1; s1 = s1 + 1;由於 1 是 int 類別,因此 s1+1 運算結果也是 int型,需要強制轉換類別才能賦值給 short 型。
    而 short s1 = 1; s1 += 1;可以正確編譯,因為 s1+= 1;相當於 s1 = (short(s1 + 1);其中有隱含的強制類別轉換。
    編碼
    Java語言采用何種編碼方案?有何特點?
    Java語言采用Unicode編碼標準,Unicode(標準碼),它為每個字元制訂了一個唯一的數值,因此在任何的語言,平台,程式都可以放心的使用。
    註釋
    什麽Java註釋
    定義:用於解釋說明程式的文字
    分類
    單行註釋 格式: // 註釋文字 多行註釋 格式: /* 註釋文字 /
    文件註釋
    格式:/* 註釋文字 */
    作用
    在程式中,尤其是復雜的程式中,適當地加入註釋可以增加程式的可讀性,有利於程式的修改、偵錯和交流。註釋的內容在程式編譯的時候會被忽視,不會產生目標碼,註釋的部份不會對程式的執行結果產生任何影響。
    註意事項:多行和文件註釋都不能巢狀使用。
    存取修飾詞
    存取修飾詞 public,private,protected,以及不寫(預設)時的區別
    定義:Java中,可以使用存取修飾詞來保護對類、變量、方法和構造方法的存取。Java 支持 4 種不同的存取許可權。
    分類
    private : 在同一類內可見。使用物件:變量、方法。 註意:不能修飾類(外部類) default (即缺省,什麽也不寫,不使用任何關鍵字): 在同一包內可見,不使用任何修飾詞。使用物件:類、介面、變量、方法。 protected : 對同一包內的類和所有子類別可見。使用物件:變量、方法。 註意:不能修飾類(外部類)。 public : 對所有類可見。使用物件:類、介面、變量、方法
    存取修飾詞圖


    image.png
    運算子
    &和&&的區別
    &運算子有兩種用法:(1)按位與;(2)邏輯與。
    &&運算子是短路與運算。邏輯與跟短路與的差別是非常巨大的,雖然二者都要求運算子左右兩端的布爾值都是true 整個運算式的值才是 true。&&之所以稱為短路運算,是因為如果&&左邊的運算式的值是 false,右邊的運算式會被直接短路掉,不會進行運算。
    註意:邏輯或運算子(|)和短路或運算子(||)的差別也是如此。
    關鍵字
    Java 有沒有 goto
    goto 是 Java 中的保留字,在目前版本的 Java 中沒有使用。
    final 有什麽用?
    用於修飾類、內容和方法;
    被final修飾的類不可以被繼承 被final修飾的方法不可以被重寫 被final修飾的變量不可以被改變,被final修飾不可變的是變量的參照,而不是參照指向的內容,參照指向的內容是可以改變的
    final finally finalize區別
    final可以修飾類、變量、方法,修飾類表示該類不能被繼承、修飾方法表示該方法不能被重寫、修飾變量表 示該變量是一個常量不能被重新賦值。 finally一般作用在try-catch程式碼塊中,在處理異常的時候,通常我們將一定要執行的程式碼方法finally程式碼塊 中,表示不管是否出現異常,該程式碼塊都會執行,一般用來存放一些關閉資源的程式碼。 finalize是一個方法,屬於Object類的一個方法,而Object類是所有類的父類,該方法一般由垃圾回收器來調 用,當我們呼叫System.gc() 方法的時候,由垃圾回收器呼叫finalize(),回收垃圾,一個物件是否可回收的 最後判斷。
    this關鍵字的用法
    this是自身的一個物件,代表物件本身,可以理解為:指向物件本身的一個指標。
    this的用法在java中大體可以分為3種:
    1.普通的直接參照,this相當於是指向當前物件本身。
    2.形參與成員名字重名,用this來區分:
    public Person(String name, int age) { this.name = name; this.age = age; }
    3.參照本類的建構函式
    class Person{ private String name; private int age; public Person() { } public Person(String name) { this.name = name; } public Person(String name, int age) { this(name); this.age = age; } }
    super關鍵字的用法
    super可以理解為是指向自己超(父)類物件的一個指標,而這個超類指的是離自己最近的一個父類。
    super也有三種用法:
    1.普通的直接參照
    與this類似,super相當於是指向當前物件的父類的參照,這樣就可以用http:// super.xxx 來參照父類的成員。
    2.子類別中的成員變量或方法與父類中的成員變量或方法同名時,用super進行區分
    class Person{ protected String name; public Person(String name) { this.name = name; } } class Student extends Person{ private String name; public Student(String name, String name1) { super(name); this.name = name1; } public void getInfo(){ System.out.println(this.name); //Child System.out.println(super.name); //Father } } public class Test { public static void main(String[] args) { Student s1 = new Student("Father","Child"); s1.getInfo(); } }
    3.參照父類建構函式

  • super(參數):呼叫父類中的某一個建構函式(應該為建構函式中的第一條語句)。
  • this(參數):呼叫本類中另一種形式的建構函式(應該為建構函式中的第一條語句)。
  • this與super的區別
    super: 它參照當前物件的直接父類中的成員(用來存取直接父類中被隱藏的父類中成員數據或函數,基礎類別與衍生類別中有相同成員定義時如:super.變量名 super.成員函數據名(實參)
    this:它代表當前物件名(在程式中易產生二義性之處,應使用this來指明當前物件;如果函數的形參與類中的成員數據同名,這時需用this來指明成員變量名)
    super()和this()類似,區別是,super()在子類別中呼叫父類的構造方法,this()在本類內呼叫本類的其它構造方法。
    super()和this()均需放在構造方法內第一行。
    盡管可以用this呼叫一個構造器,但卻不能呼叫兩個。
    this和super不能同時出現在一個建構函式裏面,因為this必然會呼叫其它的建構函式,其它的建構函式必然也會有super語句的存在,所以在同一個建構函式裏面有相同的語句,就失去了語句的意義,編譯器也不會透過。
    this()和super()都指的是物件,所以,均不可以在static環境中使用。包括:static變量,static方法,static語句塊。
    從本質上講,this是一個指向本物件的指標, 然而super是一個Java關鍵字。
    static存在的主要意義
    static的主要意義是在於建立獨立於具體物件的域變量或者方法。以致於即使沒有建立物件,也能使用內容和呼叫方法!
    static關鍵字還有一個比較關鍵的作用就是 用來形成靜態程式碼塊以最佳化程式效能。static塊可以置於類中的任何地方,類中可以有多個static塊。在類初次被載入的時候,會按照static塊的順序來執行每個static塊,並且只會執行一次。
    為什麽說static塊可以用來最佳化程式效能,是因為它的特性:只會在類載入的時候執行一次。因此,很多時候會將一些只需要進行一次的初始化操作都放在static程式碼塊中進行。
    static的獨特之處
    1、被static修飾的變量或者方法是獨立於該類的任何物件,也就是說,這些變量和方法不屬於任何一個例項物件,而是被類的例項物件所共享。
    怎麽理解 「被類的例項物件所共享」 這句話呢?就是說,一個類的靜態成員,它是屬於大夥的【大夥指的是這個類的多個物件例項,我們都知道一個類可以建立多個例項!】,所有的類物件共享的,不像成員變量是自個的【自個指的是這個類的單個例項物件】…我覺得我已經講的很通俗了,你明白了咩?
    2、在該類被第一次載入的時候,就會去載入被static修飾的部份,而且只在類第一次使用時載入並進行初始化,註意這是第一次用就要初始化,後面根據需要是可以再次賦值的。
    3、static變量值在類載入的時候分配空間,以後建立類物件的時候不會重新分配。賦值的話,是可以任意賦值的!
    4、被static修飾的變量或者方法是優先於物件存在的,也就是說當一個類載入完畢之後,即便沒有建立物件,也可以去存取。
    static套用場景
    因為static是被類的例項物件所共享,因此如果某個成員變量是被所有物件所共享的,那麽這個成員變量就應該定義為靜態變量。
    因此比較常見的static套用場景有:
    1、修飾成員變量 2、修飾成員方法 3、靜態程式碼塊 4、修飾類【只能修飾內部類也就是靜態內部類】 5、靜態導包
    static註意事項 1、靜態只能存取靜態。 2、非靜態既可以存取非靜態的,也可以存取靜態的。
    流程控制語句
    break ,continue ,return 的區別及作用
    break 跳出總上一層迴圈,不再執行迴圈(結束當前的迴圈體)
    continue 跳出本次迴圈,繼續執行下次迴圈(結束正在執行的迴圈 進入下一個迴圈條件)
    return 程式返回,不再執行下面的程式碼(結束當前的方法 直接返回)
    在 Java 中,如何跳出當前的多重巢狀迴圈
    在Java中,要想跳出多重迴圈,可以在外面的迴圈語句前定義一個標號,然後在裏層迴圈體的程式碼中使用帶有標號的break 語句,即可跳出外層迴圈。例如:
    public static void main(String[] args) { ok: for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { System.out.println("i=" + i + ",j=" + j); if (j == 5) { break ok; } } } }
    物件導向
    物件導向概述
    物件導向和程序導向的區別
    程序導向:
    優點:效能比物件導向高,因為類呼叫時需要例項化,開銷比較大,比較消耗資源;比如微控制器、嵌入式開發、Linux/Unix等一般采用程序導向開發,效能是最重要的因素。
    缺點:沒有物件導向易維護、易復用、易擴充套件
    物件導向:
    優點:易維護、易復用、易擴充套件,由於物件導向有封裝、繼承、多型性的特性,可以設計出低耦合的系統,使系統更加靈活、更加易於維護
    缺點:效能比程序導向低
    程序導向是具體化的,流程化的,解決一個問題,你需要一步一步的分析,一步一步的實作。
    物件導向是模型化的,你只需抽象出一個類,這是一個封閉的盒子,在這裏你擁有數據也擁有解決問題的方法。需要什麽功能直接使用就可以了,不必去一步一步的實作,至於這個功能是如何實作的,管我們什麽事?我們會用就可以了。
    物件導向的底層其實還是程序導向,把程序導向抽象成類,然後封裝,方便我們使用的就是物件導向了。
    物件導向三大特性
    物件導向的特征有哪些方面
    物件導向的特征主要有以下幾個方面:
    抽象:抽象是將一類物件的共同特征總結出來構造類的過程,包括數據抽象和行為抽象兩方面。抽象只關註物件有哪些內容和行為,並不關註這些行為的細節是什麽。
    封裝
    封裝把一個物件的內容私有化,同時提供一些可以被外界存取的內容的方法,如果內容不想被外界存取,我們大可不必提供方法給外界存取。但是如果一個類沒有提供給外界存取的方法,那麽這個類也沒有什麽意義了。
    繼承
    繼承是使用已存在的類的定義作為基礎建立新類的技術,新類的定義可以增加新的數據或新的功能,也可以用父類的功能,但不能選擇性地繼承父類。透過使用繼承我們能夠非常方便地復用以前的程式碼。
    關於繼承如下 3 點請記住:
    子類別擁有父類非 private 的內容和方法。
    子類別可以擁有自己內容和方法,即子類別可以對父類進行擴充套件。
    子類別可以用自己的方式實作父類的方法。(以後介紹)。
    多型
    所謂多型就是指程式中定義的參照變量所指向的具體類別和透過該參照變量發出的方法呼叫在編程時並不確定,而是在程式執行期間才確定,即一個參照變量到底會指向哪個類的例項物件,該參照變量發出的方法呼叫到底是哪個類中實作的方法,必須在由程式執行期間才能決定。
    在Java中有兩種形式可以實作多型:繼承(多個子類別對同一方法的重寫)和介面(實作介面並覆蓋介面中同一方法)。
    其中Java 物件導向編程三大特性:封裝 繼承 多型
    封裝:隱藏物件的內容和實作細節,僅對外提供公共存取方式,將變化隔離,便於使用,提高復用性和安全性。
    繼承:繼承是使用已存在的類的定義作為基礎建立新類的技術,新類的定義可以增加新的數據或新的功能,也可以用父類的功能,但不能選擇性地繼承父類。透過使用繼承可以提高程式碼復用性。繼承是多型的前提。
    關於繼承如下 3 點請記住:
    子類別擁有父類非 private 的內容和方法。
    子類別可以擁有自己內容和方法,即子類別可以對父類進行擴充套件。
    子類別可以用自己的方式實作父類的方法。
    多型性:父類或介面定義的參照變量可以指向子類別或具體實作類的例項物件。提高了程式的拓展性。
    在Java中有兩種形式可以實作多型:繼承(多個子類別對同一方法的重寫)和介面(實作介面並覆蓋介面中同一方法)。
    方法多載(overload)實作的是編譯時的多型性(也稱為前繫結),而方法重寫(override)實作的是執行時的多型性(也稱為後繫結)。
    一個參照變量到底會指向哪個類的例項物件,該參照變量發出的方法呼叫到底是哪個類中實作的方法,必須在由程式執行期間才能決定。執行時的多型是物件導向最精髓的東西,要實作多型需要做兩件事:
    方法重寫(子類別繼承父類並重寫父類中已有的或抽象的方法); 物件造型(用父類別參照子類別型物件,這樣同樣的參照呼叫同樣的方法就會根據子類別物件的不同而表現出不同的行為)。
    什麽是多型機制?Java語言是如何實作多型的?
    所謂多型就是指程式中定義的參照變量所指向的具體類別和透過該參照變量發出的方法呼叫在編程時並不確定,而是在程式執行期間才確定,即一個參照變量倒底會指向哪個類的例項物件,該參照變量發出的方法呼叫到底是哪個類中實作的方法,必須在由程式執行期間才能決定。因為在程式執行時才確定具體的類,這樣,不用修改源程式程式碼,就可以讓參照變量繫結到各種不同的類實作上,從而導致該參照呼叫的具體方法隨之改變,即不修改程式程式碼就可以改變程式執行時所繫結的具體程式碼,讓程式可以選擇多個執行狀態,這就是多型性。
    多型分為編譯時多型和執行時多型。其中編輯時多型是靜態的,主要是指方法的多載,它是根據參數列的不同來區分不同的函數,透過編輯之後會變成兩個不同的函數,在執行時談不上多型。而執行時多型是動態的,它是透過動態繫結來實作的,也就是我們所說的多型性。
    多型的實作
    Java實作多型有三個必要條件:繼承、重寫、向上轉型。
    繼承:在多型中必須存在有繼承關系的子類別和父類。
    重寫:子類別對父類中某些方法進行重新定義,在呼叫這些方法時就會呼叫子類別的方法。
    向上轉型:在多型中需要將子類別的參照賦給父類物件,只有這樣該參照才能夠具備技能呼叫父類的方法和子類別的方法。
    只有滿足了上述三個條件,我們才能夠在同一個繼承結構中使用統一的邏輯實作程式碼處理不同的物件,從而達到執行不同的行為。
    對於Java而言,它多型的實作機制遵循一個原則:當超類物件參照變量參照子類別物件時,被參照物件的類別而不是參照變量的類別決定了呼叫誰的成員方法,但是這個被呼叫的方法必須是在超類中定義過的,也就是說被子類別覆蓋的方法。
    物件導向五大基本原則是什麽(可選)
    單一職責原則SRP(Single Responsibility Principle) 類的功能要單一,不能包羅萬象,跟雜貨鋪似的。
    開放封閉原則OCP(Open-Close Principle) 一個模組對於拓展是開放的,對於修改是封閉的,想要增加功能熱烈歡迎,想要修改,哼,一萬個不樂意。
    裏式替換原則LSP(the Liskov Substitution Principle LSP) 子類別可以替換父類出現在父類能夠出現的任何地方。比如你能代表你爸去你姥姥家幹活。哈哈~~
    依賴倒置原則DIP(the Dependency Inversion Principle DIP) 高層次的模組不應該依賴於低層次的模組,他們都應該依賴於抽象。抽象不應該依賴於具體實作,具體實作應該依賴於抽象。就是你出國要說你是中國人,而不能說你是哪個村子的。比如說中國人是抽象的,下面有具體的xx省,xx市,xx縣。你要依賴的抽象是中國人,而不是你是xx村的。
    介面分離原則ISP(the Interface Segregation Principle ISP) 設計時采用多個與特定客戶類有關的介面比采用一個通用的介面要好。就比如一個手機擁有打電話,看影片,玩遊戲等功能,把這幾個功能拆分成不同的介面,比在一個介面裏要好的多。
    類與介面
    抽象類和介面的對比
    抽象類是用來捕捉子類別的通用特性的。介面是抽象方法的集合。
    從設計層面來說,抽象類是對類的抽象,是一種樣版設計,介面是行為的抽象,是一種行為的規範。
    相同點
    介面和抽象類都不能例項化 都位於繼承的頂端,用於被其他實作或繼承 都包含抽象方法,其子類別都必須覆寫這些抽象方法
    不同點
    參數 抽象類 介面 聲明 抽象類使用abstract關鍵字聲明 介面使用interface關鍵字聲明 實作 子類別使用extends關鍵字來繼承抽象類。如果子類別不是抽象類的話,它需要提供抽象類中所有聲明的方法的實作 子類別使用implements關鍵字來實作介面。它需要提供介面中所有聲明的方法的實作 構造器 抽象類可以有構造器 介面不能有構造器 存取修飾詞 抽象類中的方法可以是任意存取修飾詞 介面方法預設修飾詞是public。並且不允許定義為 private 或者 protected 多繼承 一個類最多只能繼承一個抽象類 一個類可以實作多個介面 欄位聲明 抽象類的欄位聲明可以是任意的 介面的欄位預設都是 static 和 final 的 備註:Java8中介面中引入預設方法和靜態方法,以此來減少抽象類和介面之間的差異。
    現在,我們可以為介面提供預設實作的方法了,並且不用強制子類別來實作它。
    介面和抽象類各有優缺點,在介面和抽象類的選擇上,必須遵守這樣一個原則:
    行為模型應該總是透過介面而不是抽象類別定義,所以通常是優先選用介面,盡量少用抽象類。 選擇抽象類的時候通常是如下情況:需要定義子類別的行為,又要為子類別提供通用的功能。
    普通類和抽象類有哪些區別?
    普通類不能包含抽象方法,抽象類可以包含抽象方法。 抽象類不能直接例項化,普通類可以直接例項化。
    抽象類能使用 final 修飾嗎?
    不能,定義抽象類就是讓其他類繼承的,如果定義為 final 該類就不能被繼承,這樣彼此就會產生矛盾,所以 final 不能修飾抽象類
    建立一個物件用什麽關鍵字?物件例項與物件參照有何不同?
    new關鍵字,new建立物件例項(物件例項在堆記憶體中),物件參照指向物件例項(物件參照存放在棧記憶體中)。一個物件參照可以指向0個或1個物件(一根繩子可以不系氣球,也可以系一個氣球);一個物件可以有n個參照指向它(可以用n條繩子系住一個氣球)
    變量與方法
    成員變量與局部變量的區別有哪些
    變量:在程式執行的過程中,在某個範圍內其值可以發生改變的量。從本質上講,變量其實是記憶體中的一小塊區域
    成員變量:方法外部,類內部定義的變量
    局部變量:類的方法中的變量。
    成員變量和局部變量的區別
    作用域
    成員變量:針對整個類有效。 局部變量:只在某個範圍內有效。(一般指的就是方法,語句體內)
    儲存位置
    成員變量:隨著物件的建立而存在,隨著物件的消失而消失,儲存在堆記憶體中。 局部變量:在方法被呼叫,或者語句被執行的時候存在,儲存在棧記憶體中。當方法呼叫完,或者語句結束後,就自動釋放。
    生命周期
    成員變量:隨著物件的建立而存在,隨著物件的消失而消失 局部變量:當方法呼叫完,或者語句結束後,就自動釋放。
    初始值
    成員變量:有預設初始值。
    局部變量:沒有預設初始值,使用前必須賦值。
    使用原則
    在使用變量時需要遵循的原則為:就近原則 首先在局部範圍找,有就使用;接著在成員位置找。
    在Java中定義一個不做事且沒有參數的構造方法的作用
    Java程式在執行子類別的構造方法之前,如果沒有用super()來呼叫父類特定的構造方法,則會呼叫父類中「沒有參數的構造方法」。因此,如果父類中只定義了有參數的構造方法,而在子類別的構造方法中又沒有用super()來呼叫父類中特定的構造方法,則編譯時將發生錯誤,因為Java程式在父類中找不到沒有參數的構造方法可供執行。解決辦法是在父類裏加上一個不做事且沒有參數的構造方法。
    在呼叫子類別構造方法之前會先呼叫父類沒有參數的構造方法,其目的是?
    幫助子類別做初始化工作。
    一個類的構造方法的作用是什麽?若一個類沒有聲明構造方法,改程式能正確執行嗎?為什麽?
    主要作用是完成對類物件的初始化工作。可以執行。因為一個類即使沒有聲明構造方法也會有預設的不帶參數的構造方法。
    構造方法有哪些特性?
    名字與類名相同;
    沒有返回值,但不能用void聲明建構函式;
    生成類的物件時自動執行,無需呼叫。
    靜態變量和例項變量區別
    靜態變量: 靜態變量由於不屬於任何例項物件,屬於類的,所以在記憶體中只會有一份,在類的載入過程中,JVM只為靜態變量分配一次記憶體空間。
    例項變量: 每次建立物件,都會為每個物件分配成員變量記憶體空間,例項變量是屬於例項物件的,在記憶體中,建立幾次物件,就有幾份成員變量。
    靜態變量與普通變量區別
    static變量也稱作靜態變量,靜態變量和非靜態變量的區別是:靜態變量被所有的物件所共享,在記憶體中只有一個副本,它若且唯若在類初次載入時會被初始化。而非靜態變量是物件所擁有的,在建立物件的時候被初始化,存在多個副本,各個物件擁有的副本互不影響。
    還有一點就是static成員變量的初始化順序按照定義的順序進行初始化。
    靜態方法和例項方法有何不同?
    靜態方法和例項方法的區別主要體現在兩個方面:
    在外部呼叫靜態方法時,可以使用"類名.方法名"的方式,也可以使用"物件名.方法名"的方式。而例項方法只有後面這種方式。也就是說,呼叫靜態方法可以無需建立物件。 靜態方法在存取本類的成員時,只允許存取靜態成員(即靜態成員變量和靜態方法),而不允許存取例項成員變量和例項方法;例項方法則無此限制
    在一個靜態方法內呼叫一個非靜態成員為什麽是非法的?
    由於靜態方法可以不透過物件進行呼叫,因此在靜態方法裏,不能呼叫其他非靜態變量,也不可以存取非靜態變量成員。
    什麽是方法的返回值?返回值的作用是什麽?
    方法的返回值是指我們獲取到的某個方法體中的程式碼執行後產生的結果!(前提是該方法可能產生結果)。返回值的作用:接收出結果,使得它可以用於其他的操作!
    內部類
    什麽是內部類?
    在Java中,可以將一個類的定義放在另外一個類的定義內部,這就是內部類。內部類本身就是類的一個內容,與其他內容定義方式一致。
    內部類的分類有哪些 內部類可以分為四種:成員內部類、局部內部類、匿名內部類和靜態內部類。
    靜態內部類
    定義在類內部的靜態類,就是靜態內部類。
    public class Outer { private static int radius = 1; static class StaticInner { public void visit() { System.out.println("visit outer static variable:" + radius); } } }
    靜態內部類可以存取外部類所有的靜態變量,而不可存取外部類的非靜態變量;靜態內部類的建立方式,new 外部類.靜態內部類(),如下:
    Outer.StaticInner inner = new Outer.StaticInner(); inner.visit();
    成員內部類
    定義在類內部,成員位置上的非靜態類,就是成員內部類。
    public class Outer { private static int radius = 1; private int count =2; class Inner { public void visit() { System.out.println("visit outer static variable:" + radius); System.out.println("visit outer variable:" + count); } } }
    成員內部類可以存取外部類所有的變量和方法,包括靜態和非靜態,私有和公有。成員內部類依賴於外部類的例項,它的建立方式外部類例項.new 內部類(),如下:
    Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); inner.visit();
    局部內部類
    定義在方法中的內部類,就是局部內部類。
    public class Outer { private int out_a = 1; private static int STATIC_b = 2; public void testFunction class(){ int inner_c =3; class Inner { private void fun(){ System.out.println(out_a); System.out.println(STATIC_b); System.out.println(inner_c); } } Inner inner = new Inner(); inner.fun(); } public static void testStaticFunction class(){ int d =3; class Inner { private void fun(){ // System.out.println(out_a); 編譯錯誤,定義在靜態方法中的局部類不可以存取外部類的例項變量 System.out.println(STATIC_b); System.out.println(d); } } Inner inner = new Inner(); inner.fun(); } }
    定義在例項方法中的局部類可以存取外部類的所有變量和方法,定義在靜態方法中的局部類只能存取外部類的靜態變量和方法。局部內部類的建立方式,在對應方法內,new 內部類(),如下:
    public static void testStaticFunction class(){ class Inner { } Inner inner = new Inner(); }
    匿名內部類
    匿名內部類就是沒有名字的內部類,日常開發中使用的比較多。
    public class Outer { private void test(final int i) { new Service() { public void method() { for (int j = 0; j < i; j++) { System.out.println("匿名內部類" ); } } }.method(); } } //匿名內部類必須繼承或實作一個已有的介面 interface Service{ void method(); }
    除了沒有名字,匿名內部類還有以下特點:
    匿名內部類必須繼承一個抽象類或者實作一個介面。 匿名內部類不能定義任何靜態成員和靜態方法。 當所在的方法的形參需要被匿名內部類使用時,必須聲明為 final。 匿名內部類不能是抽象的,它必須要實作繼承的類或者實作的介面的所有抽象方法。
    匿名內部類建立方式:
    new 類/介面{ //匿名內部類實作部份 }
    內部類的優點
    我們為什麽要使用內部類呢?因為它有以下優點:
    一個內部類物件可以存取建立它的外部類物件的內容,包括私有數據! 內部類不為同一包的其他類所見,具有很好的封裝性; 內部類有效實作了「多重繼承」,最佳化 java 單繼承的缺陷。 匿名內部類可以很方便的定義回呼。
    內部類有哪些套用場景
    一些多演算法場合 解決一些非物件導向的語句塊。 適當使用內部類,使得程式碼更加靈活和富有擴充套件性。 當某個類除了它的外部類,不再被其他的類使用時。 局部內部類和匿名內部類存取局部變量的時候,為什麽變量必須要加上final? 局部內部類和匿名內部類存取局部變量的時候,為什麽變量必須要加上final呢?它內部原理是什麽呢?
    先看這段程式碼:
    public class Outer { void outMethod(){ final int a =10; class Inner { void innerMethod(){ System.out.println(a); } } } }
    以上例子,為什麽要加final呢?是因為生命周期不一致, 局部變量直接儲存在棧中,當方法執行結束後,非final的局部變量就被銷毀。而局部內部類對局部變量的參照依然存在,如果局部內部類要呼叫局部變量時,就會出錯。加了final,可以確保局部內部類使用的變量與外層的局部變量區分開,解決了這個問題。
    內部類相關,看程式說出執行結果 public class Outer { private int age = 12; class Inner { private int age = 13; public void print() { int age = 14; System.out.println("局部變量:" + age); System.out.println("內部類變量:" + this.age); System.out.println("外部類變量:" + Outer.this.age); } } public static void main(String[] args) { Outer.Inner in = new Outer().new Inner(); in.print(); } } 執行結果: 局部變量:14 內部類變量:13 外部類變量:12
    重寫與多載
    構造器(constructor)是否可被重寫(override) 構造器不能被繼承,因此不能被重寫,但可以被多載。
    多載(Overload)和重寫(Override)的區別。多載的方法能否根據返回類別進行區分?
    方法的多載和重寫都是實作多型的方式,區別在於前者實作的是編譯時的多型性,而後者實作的是執行時的多型性。
    多載:發生在同一個類中,方法名相同參數列不同(參數類別不同、個數不同、順序不同),與方法返回值和存取修飾詞無關,即多載的方法不能根據返回類別進行區分
    重寫:發生在父子類別中,方法名、參數列必須相同,返回值小於等於父類,投擲的異常小於等於父類,存取修飾詞大於等於父類(黎克特制代換原則);如果父類方法存取修飾詞為private則子類別中就不是重寫。
    物件相等判斷
    == 和 equals 的區別是什麽
    == : 它的作用是判斷兩個物件的地址是不是相等。即,判斷兩個物件是不是同一個物件。(基本數據類別 == 比較的是值,參照數據類別 == 比較的是記憶體地址)
    equals() : 它的作用也是判斷兩個物件是否相等。但它一般有兩種使用情況:
    情況1:類沒有覆蓋 equals() 方法。則透過 equals() 比較該類的兩個物件時,等價於透過「==」比較這兩個物件。
    情況2:類覆蓋了 equals() 方法。一般,我們都覆蓋 equals() 方法來兩個物件的內容相等;若它們的內容相等,則返回 true (即,認為這兩個物件相等)。
    舉個例子:
    public class test1 { public static void main(String[] args) { String a = new String("ab"); // a 為一個參照 String b = new String("ab"); // b為另一個參照,物件的內容一樣 String aa = "ab"; // 放在常量池中 String bb = "ab"; // 從常量池中尋找 if (aa == bb) // true System.out.println("aa==bb"); if (a == b) // false,非同一物件 System.out.println("a==b"); if (a.equals(b)) // true System.out.println("aEQb"); if (42 == 42.0) { // true System.out.println("true"); } } }
    說明:
    String中的equals方法是被重寫過的,因為object的equals方法是比較的物件的記憶體地址,而String的equals方法比較的是物件的值。 當建立String類別的物件時,虛擬機器會在常量池中尋找有沒有已經存在的值和要建立的值相同的物件,如果有就把它賦給當前參照。如果沒有就在常量池中重新建立一個String物件。
    hashCode 與 equals (重要)
    HashSet如何檢查重復
    兩個物件的 hashCode() 相同,則 equals() 也一定為 true,對嗎?
    hashCode和equals方法的關系
    面試官可能會問你:「你重寫過 hashcode 和 equals 麽,為什麽重寫equals時必須重寫hashCode方法?」
    hashCode()介紹
    hashCode() 的作用是獲取哈希碼,也稱為雜湊碼;它實際上是返回一個int整數。這個哈希碼的作用是確定該物件在哈希表中的索引位置。hashCode() 定義在JDK的Object.java中,這就意味著Java中的任何類都包含有hashCode()函數。
    雜湊表儲存的是鍵值對(key-value),它的特點是:能根據「鍵」快速的檢索出對應的「值」。這其中就利用到了雜湊碼!(可以快速找到所需要的物件)
    為什麽要有 hashCode
    我們以「HashSet 如何檢查重復」為例子來說明為什麽要有 hashCode:
    當你把物件加入 HashSet 時,HashSet 會先計算物件的 hashcode 值來判斷物件加入的位置,同時也會與其他已經加入的物件的 hashcode 值作比較,如果沒有相符的hashcode,HashSet會假設物件沒有重復出現。但是如果發現有相同 hashcode 值的物件,這時會呼叫 equals()方法來檢查 hashcode 相等的物件是否真的相同。如果兩者相同,HashSet 就不會讓其加入操作成功。如果不同的話,就會重新雜湊到其他位置。(摘自我的Java啟蒙書【Head first java】第二版)。這樣我們就大大減少了 equals 的次數,相應就大大提高了執行速度。
    hashCode()與equals()的相關規定
    如果兩個物件相等,則hashcode一定也是相同的
    兩個物件相等,對兩個物件分別呼叫equals方法都返回true
    兩個物件有相同的hashcode值,它們也不一定是相等的
    因此,equals 方法被覆蓋過,則 hashCode 方法也必須被覆蓋
    hashCode() 的預設行為是對堆上的物件產生獨特值。如果沒有重寫 hashCode(),則該 class 的兩個物件無論如何都不會相等(即使這兩個物件指向相同的數據)
    物件的相等與指向他們的參照相等,兩者有什麽不同?
    物件的相等 比的是記憶體中存放的內容是否相等而 參照相等 比較的是他們指向的記憶體地址是否相等。
    值傳遞
    當一個物件被當作參數傳遞到一個方法後,此方法可改變這個物件的內容,並可返回變化後的結果,那麽這裏到底是值傳遞還是參照傳遞
    是值傳遞。Java 語言的方法呼叫只支持參數的值傳遞。當一個物件例項作為一個參數被傳遞到方法中時,參數的值就是對該物件的參照。物件的內容可以在被呼叫過程中被改變,但對物件參照的改變是不會影響到呼叫者的
    為什麽 Java 中只有值傳遞
    首先回顧一下在程式設計語言中有關將參數傳遞給方法(或函數)的一些專業術語。按值呼叫(call by value)表示方法接收的是呼叫者提供的值,而按參照呼叫(call by reference)表示方法接收的是呼叫者提供的變量地址。一個方法可以修改傳遞參照所對應的變量值,而不能修改傳遞值呼叫所對應的變量值。 它用來描述各種程式設計語言(不只是Java)中方法參數傳遞方式。
    Java程式設計語言總是采用按值呼叫。也就是說,方法得到的是所有參數值的一個拷貝,也就是說,方法不能修改傳遞給它的任何參數變量的內容。
    下面透過 3 個例子來給大家說明
    example 1
    public static void main(String[] args) { int num1 = 10; int num2 = 20; swap(num1, num2); System.out.println("num1 = " + num1); System.out.println("num2 = " + num2); } public static void swap(int a, int b) { int temp = a; a = b; b = temp; System.out.println("a = " + a); System.out.println("b = " + b); } 結果: a = 20 b = 10 num1 = 10 num2 = 20
    解析:


    image.png
    在swap方法中,a、b的值進行交換,並不會影響到 num1、num2。因為,a、b中的值,只是從 num1、num2 的復制過來的。也就是說,a、b相當於num1、num2 的副本,副本的內容無論怎麽修改,都不會影響到原件本身。
    透過上面例子,我們已經知道了一個方法不能修改一個基本數據類別的參數,而物件參照作為參數就不一樣,請看 example2.
    example 2
    public static void main(String[] args) { int[] arr = { 1, 2, 3, 4, 5 }; System.out.println(arr[0]); change(arr); System.out.println(arr[0]); } public static void change(int[] array) { // 將陣列的第一個元素變為0 array[0] = 0; } 結果: 1 0 1 2
    解析:


    image.png
    array 被初始化 arr 的拷貝也就是一個物件的參照,也就是說 array 和 arr 指向的時同一個陣列物件。 因此,外部對參照物件的改變會反映到所對應的物件上。
    透過 example2 我們已經看到,實作一個改變物件參數狀態的方法並不是一件難事。理由很簡單,方法得到的是物件參照的拷貝,物件參照及其他的拷貝同時參照同一個物件。
    很多程式設計語言(特別是,C++和Pascal)提供了兩種參數傳遞的方式:值呼叫和參照呼叫。有些程式設計師(甚至本書的作者)認為Java程式設計語言對物件采用的是參照呼叫,實際上,這種理解是不對的。由於這種誤解具有一定的普遍性,所以下面給出一個反例來詳細地闡述一下這個問題。
    example 3
    public class Test { public static void main(String[] args) { // TODO Auto-generated method stub Student s1 = new Student("小張"); Student s2 = new Student("小李"); Test.swap(s1, s2); System.out.println("s1:" + s1.getName()); System.out.println("s2:" + s2.getName()); } public static void swap(Student x, Student y) { Student temp = x; x = y; y = temp; System.out.println("x:" + x.getName()); System.out.println("y:" + y.getName()); } } 結果: x:小李 y:小張 s1:小張 s2:小李
    解析:
    交換之前:


    image.png
    交換之後:


    image.png
    透過上面兩張圖可以很清晰的看出: 方法並沒有改變儲存在變量 s1 和 s2 中的物件參照。swap方法的參數x和y被初始化為兩個物件參照的拷貝,這個方法交換的是這兩個拷貝
    總結
    Java程式設計語言對物件采用的不是參照呼叫,實際上,物件參照是按值傳遞的。
    下面再總結一下Java中方法參數的使用情況:

  • 一個方法不能修改一個基本數據類別的參數(即數值型或布爾型】
  • 一個方法可以改變一個物件參數的狀態。
  • 一個方法不能讓物件參數參照一個新的物件。
  • 值傳遞和參照傳遞有什麽區別
    值傳遞:指的是在方法呼叫時,傳遞的參數是按值的拷貝傳遞,傳遞的是值的拷貝,也就是說傳遞後就互不相關了。
    參照傳遞:指的是在方法呼叫時,傳遞的參數是按參照進行傳遞,其實傳遞的參照的地址,也就是變量所對應的記憶體空間的地址。傳遞的是值的參照,也就是說傳遞前和傳遞後都指向同一個參照(也就是同一個記憶體空間)。
    Java包
    JDK 中常用的包有哪些
    java.lang:這個是系統的基礎類; http:// java.io :這裏面是所有輸入輸出有關的類,比如檔操作等; java.nio:為了完善 io 包中的功能,提高 io 包中效能而寫的一個新包; http:// java.net :這裏面是與網絡有關的類; java.util:這個是系統輔助類,特別是集合類; java.sql:這個是數據庫操作的類。
    import java和javax有什麽區別
    剛開始的時候 JavaAPI 所必需的包是 java 開頭的包,javax 當時只是擴充套件 API 包來說使用。然而隨著時間的推移,javax 逐漸的擴充套件成為 Java API 的組成部份。但是,將擴充套件從 javax 包移動到 java 包將是太麻煩了,最終會破壞一堆現有的程式碼。因此,最終決定 javax 包將成為標準API的一部份。
    所以,實際上java和javax沒有區別。這都是一個名字。
    IO流
    java 中 IO 流分為幾種?
    按照流的流向分,可以分為輸入流和輸出流; 按照操作單元劃分,可以劃分為字節流和字元流; 按照流的角色劃分為節點流和處理流。 Java Io流共涉及40多個類,這些類看上去很雜亂,但實際上很有規則,而且彼此之間存在非常緊密的聯系, Java I0流的40多個類都是從如下4個抽象類基礎類別中衍生出來的。
    InputStream/Reader: 所有的輸入流的基礎類別,前者是字節輸入流,後者是字元輸入流。 OutputStream/Writer: 所有輸出流的基礎類別,前者是字節輸出流,後者是字元輸出流。 按操作方式分類結構圖:


    image.png
    按操作物件分類結構圖:


    image.png
    BIO,NIO,AIO 有什麽區別?
    簡答
    BIO:Block IO 同步阻塞式 IO,就是我們平常使用的傳統 IO,它的特點是模式簡單使用方便,並行處理能力低。 NIO:Non IO 同步非阻塞 IO,是傳統 IO 的升級,客戶端和伺服器端透過 Channel(通道)通訊,實作了多路復用。 AIO:Asynchronous IO 是 NIO 的升級,也叫 NIO2,實作了異步非堵塞 IO ,異步 IO 的操作基於事件和回呼機制。
    詳細回答
    BIO (Blocking I/O): 同步阻塞I/O模式,數據的讀取寫入必須阻塞在一個執行緒內等待其完成。在活動連線數不是特別高(小於單機1000)的情況下,這種模型是比較不錯的,可以讓每一個連線專註於自己的 I/O 並且編程模型簡單,也不用過多考慮系統的過載、限流等問題。執行緒池本身就是一個天然的漏鬥,可以緩沖一些系統處理不了的連線或請求。但是,當面對十萬甚至百萬級連線的時候,傳統的 BIO 模型是無能為力的。因此,我們需要一種更高效的 I/O 處理模型來應對更高的並行量。 NIO (New I/O): NIO是一種同步非阻塞的I/O模型,在Java 1.4 中引入了NIO框架,對應 java.nio 包,提供了 Channel , Selector,Buffer等抽象。NIO中的N可以理解為Non-blocking,不單純是New。它支持面向緩沖的,基於通道的I/O操作方法。 NIO提供了與傳統BIO模型中的 Socket 和 ServerSocket 相對應的 SocketChannel 和 ServerSocketChannel 兩種不同的套接字通道實作,兩種通道都支持阻塞和非阻塞兩種模式。阻塞模式使用就像傳統中的支持一樣,比較簡單,但是效能和可靠性都不好;非阻塞模式正好與之相反。對於低負載、低並行的應用程式,可以使用同步阻塞I/O來提升開發速率和更好的維護性;對於高負載、高並行的(網絡)套用,應使用 NIO 的非阻塞模式來開發 AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改進版 NIO 2,它是異步非阻塞的IO模型。異步 IO 是基於事件和回呼機制實作的,也就是套用操作之後會直接返回,不會堵塞在那裏,當後台處理完成,作業系統會通知相應的執行緒進行後續的操作。AIO 是異步IO的縮寫,雖然 NIO 在網絡操作中,提供了非阻塞的方法,但是 NIO 的 IO 行為還是同步的。對於 NIO 來說,我們的業務執行緒是在 IO 操作準備好時,得到通知,接著就由這個執行緒自行進行 IO 操作,IO操作本身是同步的。查閱網上相關資料,我發現就目前來說 AIO 的套用還不是很廣泛,Netty 之前也嘗試使用過 AIO,不過又放棄了。
    Files的常用方法都有哪些?
    Files. exists():檢測檔路徑是否存在。 Files. createFile():建立檔。 Files. createDirectory():建立資料夾。 Files. delete():刪除一個檔或目錄。 Files. copy():復制檔。 Files. move():移動檔。 Files. size():檢視檔個數。 Files. read():讀取檔。 Files. write():寫入檔。
    反射
    什麽是反射機制?
    JAVA反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有內容和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和內容;這種動態獲取的資訊以及動態呼叫物件的方法的功能稱為java語言的反射機制。
    靜態編譯和動態編譯
    靜態編譯: 在編譯時確定類別,繫結物件 動態編譯: 執行時確定類別,繫結物件
    反射機制優缺點
    優點: 執行期類別的判斷,動態載入類,提高程式碼靈活度。 缺點: 效能瓶頸:反射相當於一系列解釋操作,通知 JVM 要做的事情,效能比直接的java程式碼要慢很多。
    反射機制的套用場景有哪些?
    反射是框架設計的靈魂。
    在我們平時的專案開發過程中,基本上很少會直接使用到反射機制,但這不能說明反射機制沒有用,實際上有很多設計、開發都與反射機制有關,例如模組化的開發,透過反射去呼叫對應的字節碼;動態代理設計模式也采用了反射機制,還有我們日常使用的 Spring/Hibernate 等框架也大量使用到了反射機制。
    舉例:①我們在使用JDBC連線數據庫時使用 class.forName()透過反射載入數據庫的驅動程式;②Spring框架也用到很多反射機制,最經典的就是xml的配置模式。Spring 透過 XML 配置模式裝載 Bean 的過程:1) 將程式內所有 XML 或 Properties 配置檔載入入記憶體中; 2)Java類裏面解析xml或properties裏面的內容,得到對應實體類的字節碼字串以及相關的內容資訊; 3)使用反射機制,根據這個字串獲得某個類的 class例項; 4)動態配置例項的內容
    Java獲取反射的三種方法
    1.透過new物件實作反射機制 2.透過路徑實作反射機制 3.透過類名實作反射機制
    public class Student { private int id; String name; protected boolean sex; public float score; }
    public class Get { //獲取反射機制三種方式 public static void main(String[] args) throws classNotFoundException { //方式一(透過建立物件) Student stu = new Student(); class classobj1 = stu.get class(); System.out.println( classobj1.getName()); //方式二(所在透過路徑-相對路徑) class classobj2 = class.forName("fanshe.Student"); System.out.println( classobj2.getName()); //方式三(透過類名) class classobj3 = Student. class; System.out.println( classobj3.getName()); } }
    常用API
    String相關
    字元型常量和字串常量的區別
    形式上: 字元常量是單引號引起的一個字元 字串常量是雙引號引起的若幹個字元 含義上: 字元常量相當於一個整形值(ASCII值),可以參加運算式運算 字串常量代表一個地址值(該字串在記憶體中存放位置) 占記憶體大小 字元常量只占一個字節 字串常量占若幹個字節(至少一個字元結束標誌)
    什麽是字串常量池?
    字串常量池位於堆記憶體中,專門用來儲存字串常量,可以提高記憶體的使用率,避免開辟多塊空間儲存相同的字串,在建立字串時 JVM 會首先檢查字串常量池,如果該字串已經存在池中,則返回它的參照,如果不存在,則例項化一個字串放到池中,並返回其參照。
    String 是最基本的數據類別嗎
    不是。Java 中的基本數據類別只有 8 個 :byte、short、int、long、float、double、char、boolean;除了基本類別(primitive type),剩下的都是參照類別(referencetype),Java 5 以後引入的列舉類別也算是一種比較特殊的參照類別。
    這是很基礎的東西,但是很多初學者卻容易忽視,Java 的 8 種基本數據類別中不包括 String,基本數據類別中用來描述文本數據的是 char,但是它只能表示單個字元,比如 ‘a’,‘好’ 之類的,如果要描述一段文本,就需要用多個 char 類別的變量,也就是一個 char 類別陣列,比如「你好」 就是長度為2的陣列 char[] chars = {‘你’,‘好’};
    但是使用陣列過於麻煩,所以就有了 String,String 底層就是一個 char 類別的陣列,只是使用的時候開發者不需要直接操作底層陣列,用更加簡便的方式即可完成對字串的使用。
    String有哪些特性
    不變性:String 是唯讀字串,是一個典型的 immutable 物件,對它進行任何操作,其實都是建立一個新的物件,再把參照指向該物件。不變模式的主要作用在於當一個物件需要被多執行緒共享並頻繁存取時,可以保證數據的一致性。
    常量池最佳化:String 物件建立之後,會在字串常量池中進行緩存,如果下次建立同樣的物件時,會直接返回緩存的參照。
    final:使用 final 來定義 String 類,表示 String 類不能被繼承,提高了系統的安全性。
    String為什麽是不可變的嗎?
    簡單來說就是String類利用了final修飾的char類別陣列儲存字元,源碼如下圖所以:
    /** The value is used for character storage. */ private final char value[];
    String真的是不可變的嗎?
    我覺得如果別人問這個問題的話,回答不可變就可以了。 下面只是給大家看兩個有代表性的例子:

    1. String不可變但不代表參照不可以變

    String str = "Hello"; str = str + " World"; System.out.println("str=" + str);
    結果:
    str=Hello World
    解析:
    實際上,原來String的內容是不變的,只是str由原來指向"Hello"的記憶體地址轉為指向"Hello World"的記憶體地址而已,也就是說多開辟了一塊記憶體區域給"Hello World"字串。

    1. 透過反射是可以修改所謂的「不可變」物件

    // 建立字串"Hello World", 並賦給參照s String s = "Hello World"; System.out.println("s = " + s); // Hello World // 獲取String類中的value欄位 Field valueFieldOfString = String. class.getDeclaredField("value"); // 改變value內容的存取許可權 valueFieldOfString.setAccessible(true); // 獲取s物件上的value內容的值 char[] value = (char[]) valueFieldOfString.get(s); // 改變value所參照的陣列中的第5個字元 value[5] = '_'; System.out.println("s = " + s); // Hello_World
    結果:
    s = Hello World s = Hello_World
    解析:
    用反射可以存取私有成員, 然後反射出String物件中的value內容, 進而改變透過獲得的value參照改變陣列的結構。但是一般我們不會這麽做,這裏只是簡單提一下有這個東西。
    是否可以繼承 String 類
    String 類是 final 類,不可以被繼承。
    String str="i"與 String str=new String(「i」)一樣嗎?
    不一樣,因為記憶體的分配方式不一樣。String str="i"的方式,java 虛擬機器會將其分配到常量池中;而 String str=new String(「i」) 則會被分到堆記憶體中。
    String s = new String(「xyz」);建立了幾個字串物件
    兩個物件,一個是靜態區的"xyz",一個是用new建立在堆上的物件。
    String str1 = "hello"; //str1指向靜態區 String str2 = new String("hello"); //str2指向堆上的物件 String str3 = "hello"; String str4 = new String("hello"); System.out.println(str1.equals(str2)); //true System.out.println(str2.equals(str4)); //true System.out.println(str1 == str3); //true System.out.println(str1 == str2); //false System.out.println(str2 == str4); //false System.out.println(str2 == "hello"); //false str2 = str1; System.out.println(str2 == "hello"); //true
    如何將字串反轉?
    使用 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
    陣列有沒有 length()方法?String 有沒有 length()方法
    陣列沒有 length()方法 ,有 length 的內容。String 有 length()方法。JavaScript中,獲得字串的長度是透過 length 內容得到的,這一點容易和 Java 混淆。
    String 類的常用方法都有那些?
    indexOf():返回指定字元的索引。 charAt():返回指定索引處的字元。 replace():字串替換。 trim():去除字串兩端空白。 split():分割字串,返回一個分割後的字串陣列。 getBytes():返回字串的 byte 類別陣列。 length():返回字串長度。 toLowerCase():將字串轉成小寫字母。 toUpperCase():將字串轉成大寫字元。 substring():截取字串。 equals():字串比較。
    在使用 HashMap 的時候,用 String 做 key 有什麽好處?
    HashMap 內部實作是透過 key 的 hashcode 來確定 value 的儲存位置,因為字串是不可變的,所以當建立字串時,它的 hashcode 被緩存下來,不需要再次計算,所以相比於其他物件更快。
    String和StringBuffer、StringBuilder的區別是什麽?String為什麽是不可變的
    可變性
    String類中使用字元陣列保存字串,private final char value[],所以string物件是不可變的。StringBuilder與StringBuffer都繼承自AbstractStringBuilder類,在AbstractStringBuilder中也是使用字元陣列保存字串,char[] value,這兩種物件都是可變的。
    執行緒安全性
    String中的物件是不可變的,也就可以理解為常量,執行緒安全。AbstractStringBuilder是StringBuilder與StringBuffer的公共父類,定義了一些字串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。StringBuffer對方法加了同步鎖或者對呼叫的方法加了同步鎖,所以是執行緒安全的。StringBuilder並沒有對方法進行加同步鎖,所以是非執行緒安全的。
    效能
    每次對String 類別進行改變的時候,都會生成一個新的String物件,然後將指標指向新的String 物件。StringBuffer每次都會對StringBuffer物件本身進行操作,而不是生成新的物件並改變物件參照。相同情況下使用StirngBuilder 相比使用StringBuffer 僅能獲得10%~15% 左右的效能提升,但卻要冒多執行緒不安全的風險。
    對於三者使用的總結
    如果要操作少量的數據用 = String
    單執行緒操作字串緩沖區 下操作大量數據 = StringBuilder
    多執行緒操作字串緩沖區 下操作大量數據 = StringBuffer
    Date相關
    包裝類相關
    自動裝箱與拆箱
    裝箱:將基本類別用它們對應的參照類別包裝起來;
    拆箱:將包裝類別轉換為基本數據類別;
    int 和 Integer 有什麽區別
    Java 是一個近乎純潔的物件導向程式語言,但是為了編程的方便還是引入了基本數據類別,但是為了能夠將這些基本數據類別當成物件操作,Java 為每一個基本數據類別都引入了對應的包裝類別(wrapper class),int 的包裝類就是 Integer,從 Java 5 開始引入了自動裝箱/拆箱機制,使得二者可以相互轉換。
    Java 為每個原始類別提供了包裝類別:
    原始類別: boolean,char,byte,short,int,long,float,double
    包裝類別:Boolean,Character,Byte,Short,Integer,Long,Float,Double
    Integer a= 127 與 Integer b = 127相等嗎
    對於物件參照類別:==比較的是物件的記憶體地址。 對於基本數據類別:==比較的是值。
    如果整型字面量的值在-128到127之間,那麽自動裝箱時不會new新的Integer物件,而是直接參照常量池中的Integer物件,超過範圍 a1==b1的結果是false
    public static void main(String[] args) { Integer a = new Integer(3); Integer b = 3; // 將3自動裝箱成Integer類別 int c = 3; System.out.println(a == b); // false 兩個參照沒有參照同一物件 System.out.println(a == c); // true a自動拆箱成int類別再和c比較 System.out.println(b == c); // true Integer a1 = 128; Integer b1 = 128; System.out.println(a1 == b1); // false Integer a2 = 127; Integer b2 = 127; System.out.println(a2 == b2); // true }