Java 語言具有哪些特點?
- Java 為純面向對象的語言。它能夠直接反應現實生活中的對象。
-
具有平台無關性。Java 利用 Java 虛拟機運作位元組碼,無論是在 Windows、Linux 還是 MacOS 等其它平台對 Java
程式進行編譯,編譯後的程式可在其它平台運作。
- Java 為解釋型語言,編譯器把 Java 代碼編譯成平台無關的中間代碼,然後在 JVM 上解釋運作,具有很好的可移植性。
- Java提供了很多内置類庫。如對多線程支援,對網絡通信支援,最重要的一點是提供了垃圾回收器。
- Java 具有較好的安全性和健壯性。
- Java提供了異常處理和垃圾回收機制,去除了 C++中難以了解的指針特性。
JDK 與 JRE 有什麼差別?
- JDK:Java 開發工具包(Java Development Kit),提供了 Java 的開發環境和運作環境。
- JRE:Java 運作環境(Java Runtime Environment),提供了 Java 運作所需的環境。
- JDK 包含了 JRE。如果隻運作 Java 程式,安裝 JRE 即可。要編寫 Java 程式需安裝 JDK。
簡述 Java 基本資料類型
- byte: 占用 1 個位元組,取值範圍-128 ~ 127
- short: 占用 2 個位元組,取值範圍-215 ~ 215-1
- int:占用 4 個位元組,取值範圍-231 ~ 231-1
- long:占用 8 個位元組
- float:占用 4 個位元組
- double:占用 8 個位元組
- char: 占用 2 個位元組
- boolean:占用大小根據實作虛拟機不同有所差異
簡述自動裝箱拆箱
- 對于 Java 基本資料類型,均對應一個包裝類。
- 裝箱就是自動将基本資料類型轉換為包裝器類型,如 int->Integer
- 拆箱就是自動将包裝器類型轉換為基本資料類型,如 Integer->int
簡述 Java 通路修飾符
- default: 預設通路修飾符,在同一包内可見
- private: 在同一類内可見,不能修飾類
- protected : 對同一包内的類和所有子類可見,不能修飾類
- public: 對所有類可見
構造方法、成員變量初始化以及靜态成員變量三者的初始化順序?
- 先後順序:靜态成員變量、成員變量、構造方法。
- 詳細的先後順序:父類靜态變量、父類靜态代碼塊、子類靜态變量、子類靜态代碼塊、父類非靜态變量、父類非靜态代碼塊、父類構造函數、子類非靜态變量、子類非靜态代碼塊、子類構造函數。
Java 代碼塊執行順序
- 父類靜态代碼塊(隻執行一次)
- 子類靜态代碼塊(隻執行一次)
- 父類構造代碼塊
- 父類構造函數
- 子類構造代碼塊
- 子類構造函數
- 普通代碼塊
面向對象的三大特性?
- 繼承:對象的一個新類可以從現有的類中派生,派生類可以從它的基類那繼承方法和執行個體變量,且派生類可以修改或新增新的方法使之更适合特殊的需求。
- 封裝:将客觀事物抽象成類,每個類可以把自身資料和方法隻讓可信的類或對象操作,對不可信的進行資訊隐藏。
- 多态:允許不同類的對象對同一消息作出響應。不同對象調用相同方法即使參數也相同,最終表現行為是不一樣的。
為什麼 Java 語言不支援多重繼承?
-
為了程式的結構能夠更加清晰進而便于維護。假設 Java 語言支援多重繼承,類 C 繼承自類 A 和類 B,如果類 A 和 B
都有自定義的成員方法 f(),那麼當代碼中調用類 C 的 f() 會産生二義性。
-
Java 語言通過實作多個接口間接支援多重繼承,接口由于隻包含方法定義,不能有方法的實作,類 C 繼承接口 A 與接口 B
時即使它們都有方法f(),也不能直接調用方法,需實作具體的f()方法才能調用,不會産生二義性。
- 多重繼承會使類型轉換、構造方法的調用順序變得複雜,會影響到性能。
簡述 Java 的多态
- Java 多态可以分為編譯時多态和運作時多态。
- 編譯時多态主要指方法的重載,即通過參數清單的不同來區分不同的方法。
- 運作時多态主要指繼承父類和實作接口時,可使用父類引用指向子類對象。
-
運作時多态的實作:主要依靠方法表,方法表中最先存放的是 Object
類的方法,接下來是該類的父類的方法,最後是該類本身的方法。如果子類改寫了父類的方法,那麼子類和父類的那些同名方法共享一個方法表項,都被認作是父類的方法。是以可以實作運作時多态。
Java 提供的多态機制?
Java 提供了兩種用于多态的機制,分别是重載與覆寫。
- 重載:重載是指同一個類中有多個同名的方法,但這些方法有不同的參數,在編譯期間就可以确定調用哪個方法。
- 覆寫:覆寫是指派生類重寫基類的方法,使用基類指向其子類的執行個體對象,或接口的引用變量指向其實作類的執行個體對象,在程式調用的運作期根據引用變量所指的具體執行個體對象調用正在運作的那個對象的方法,即需要到運作期才能确定調用哪個方法。
重載與覆寫的差別?
- 覆寫是父類與子類之間的關系,是垂直關系;重載是同一類中方法之間的關系,是水準關系。
- 覆寫隻能由一個方法或一對方法産生關系;重載是多個方法之間的關系。
- 覆寫要求參數清單相同;重載要求參數清單不同。
- 覆寫中,調用方法體是根據對象的類型來決定的,而重載是根據調用時實參表與形參表來對應選擇方法體。
- 重載方法可以改變傳回值的類型,覆寫方法不能改變傳回值的類型。
接口和抽象類的相同點和不同點?
相同點:
- 都不能被執行個體化。
- 接口的實作類或抽象類的子類需實作接口或抽象類中相應的方法才能被執行個體化。
不同點:
- 接口隻能有方法定義,不能有方法的實作,而抽象類可以有方法的定義與實作。
- 實作接口的關鍵字為 implements,繼承抽象類的關鍵字為 extends。一個類可以實作多個接口,隻能繼承一個抽象類。
- 當子類和父類之間存在邏輯上的層次結構,推薦使用抽象類,有利于功能的累積。當功能不需要,希望支援差别較大的兩個或更多對象間的特定互動行為,推薦使用接口。使用接口能降低軟體系統的耦合度,便于日後維護或添加删除方法。
簡述抽象類與接口的差別
- 抽象類:展現的是 is-a 的關系,如對于 man is a person,就可以将 person 定義為抽象類。
- 接口:展現的是 can 的關系。是作為模闆實作的。如設定接口 fly,plane 類和 bird 類均可實作該接口。
- 一個類隻能繼承一個抽象類,但可以實作多個接口。
簡述内部類及其作用
-
成員内部類:作為成員對象的内部類。可以通路 private
及以上外部類的屬性和方法。外部類想要通路内部類屬性或方法時,必須要建立一個内部類對象,然後通過該對象通路内部類的屬性或方法。外部類也可通路private 修飾的内部類屬性。
- 局部内部類:存在于方法中的内部類。通路權限類似局部變量,隻能通路外部類的 final 變量。
- 匿名内部類:隻能使用一次,沒有類名,隻能通路外部類的 final 變量。
- 靜态内部類:類似類的靜态成員變量。
Java 語言中關鍵字 static 的作用是什麼?
static 的主要作用有兩個:
- 為某種特定資料類型或對象配置設定與建立對象個數無關的單一的存儲空間。
- 使得某個方法或屬性與類而不是對象關聯在一起,即在不建立對象的情況下可通過類直接調用方法或使用類的屬性。
具體而言 static 又可分為 4 種使用方式:
- 修飾成員變量。用 static關鍵字修飾的靜态變量在記憶體中隻有一個副本。隻要靜态變量所在的類被加載,這個靜态變量就會被配置設定空間,可以使用“類.靜态變量”和“對象.靜态變量”的方法使用。
-
修飾成員方法。static 修飾的方法無需建立對象就可以被調用。static 方法中不能使用 this 和 super 關鍵字,不能調用非
static 方法,隻能通路所屬類的靜态成員變量和靜态成員方法。
- 修飾代碼塊。JVM 在加載類的時候會執行 static 代碼塊。static 代碼塊常用于初始化靜态變量。static代碼塊隻會被執行一次。
- 修飾内部類。static内部類可以不依賴外部類執行個體對象而被執行個體化。靜态内部類不能與外部類有相同的名字,不能通路普通成員變量,隻能通路外部類中的靜态成員和靜态成員方法。
為什麼要把 String 設計為不可變?
- 節省空間:字元串常量存儲在 JVM 的字元串池中可以被使用者共享。
- 提高效率:String 可以被不同線程共享,是線程安全的。在涉及多線程操作中不需要同步操作。
- 安全:String 常被用于使用者名、密碼、檔案名等使用,由于其不可變,可避免黑客行為對其惡意修改。
簡述 String/StringBuffer 與 StringBuilder
- String 類采用利用 final 修飾的字元數組進行字元串儲存,是以不可變。如果對 String類型對象修改,需要建立對象,将老字元和新增加的字元一并存進去。
- StringBuilder,采用無 final 修飾的字元數組進行儲存,是以可變。但線程不安全。
- StringBuffer,采用無 final 修飾的字元數組進行儲存,可了解為實作線程安全的 StringBuilder。
判等運算符==與 equals 的差別?
== 比較的是引用,equals 比較的是内容。
- 如果變量是基礎資料類型,== 用于比較其對應值是否相等。
- 如果變量指向的是對象,== 用于比較兩個對象是否指向同一塊存儲空間。
equals 是 Object 類提供的方法之一,每個 Java 類都繼承自 Object 類,是以每個對象都具有 equals 這個方法。Object 類中定義的 equals 方法内部是直接調用 == 比較對象的。但通過覆寫的方法可以讓它不是比較引用而是比較資料内容。
簡述 Object 類常用方法
- hashCode:通過對象計算出的散列碼。用于 map 型或 equals 方法。需要保證同一個對象多次調用該方法,總傳回相同的整型值。
- equals:判斷兩個對象是否一緻。需保證 equals 方法相同對應的對象 hashCode 也相同。
- toString: 用字元串表示該對象
- clone:深拷貝一個對象
Java 中一維數組和二維數組的聲明方式?
一維數組的聲明方式:
type arrayName[]
type[] arrayName
二維數組的聲明方式:
type arrayName[][]
type[][] arrayName
type[] arrayName[]
其中 type 為基本資料類型或類,arrayName 為數組名字
簡述 Java 異常的分類
Java 異常分為 Error(程式無法處理的錯誤),和 Exception(程式本身可以處理的異常)。這兩個類均繼承 Throwable。
Error 常見的有 StackOverFlowError、OutOfMemoryError 等等。
Exception 可分為運作時異常和非運作時異常。對于運作時異常,可以利用 try catch 的方式進行處理,也可以不處理。對于非運作時異常,必須處理,不處理的話程式無法通過編譯。
簡述 throw 與 throws 的差別
- throw 一般是用在方法體的内部,由開發者定義當程式語句出現問題後主動抛出一個異常。
- throws 一般用于方法聲明上,代表該方法可能會抛出的異常清單。
出現在 Java 程式中的 finally 代碼塊是否一定會執行?
當遇到下面情況不會執行。
- 當程式在進入 try 語句塊之前就出現異常時會直接結束。
- 當程式在 try 塊中強制退出時,如使用 System.exit(0),也不會執行 finally 塊中的代碼。
- 其它情況下,在 try/catch/finally 語句執行的時候,try 塊先執行,當有異常發生,catch 和 finally 進行處理後程式就結束了,當沒有異常發生,在執行完 finally 中的代碼後,後面代碼會繼續執行。值得注意的是,當 try/catch 語句塊中有 return 時,finally 語句塊中的代碼會在 return 之前執行。如果 try/catch/finally 塊中都有 return 語句,finally 塊中的 return 語句會覆寫 try/catch 子產品中的 return 語句。
final、finally 和 finalize 的差別是什麼?
- final 用于聲明屬性、方法和類,分别表示屬性不可變、方法不可覆寫、類不可繼承。
- finally 作為異常處理的一部分,隻能在 try/catch 語句中使用,finally 附帶一個語句塊用來表示這個語句最終一定被執行,經常被用在需要釋放資源的情況下。
- finalize 是 Object 類的一個方法,在垃圾收集器執行的時候會調用被回收對象的 finalize()方法。當垃圾回收器準備好釋放對象占用空間時,首先會調用 finalize()方法,并在下一次垃圾回收動作發生時真正回收對象占用的記憶體。
簡述泛型
泛型,即“參數化類型”,解決不确定對象具體類型的問題。在編譯階段有效。在泛型使用過程中,操作的資料類型被指定為一個參數,這種參數類型在類中稱為泛型類、接口中稱為泛型接口和方法中稱為泛型方法。
簡述泛型擦除
Java 編譯器生成的位元組碼是不包涵泛型資訊的,泛型類型資訊将在編譯處理是被擦除,這個過程被稱為泛型擦除。
簡述注解
Java 注解用于為 Java 代碼提供中繼資料。作為中繼資料,注解不直接影響你的代碼執行,但也有一些類型的注解實際上可以用于這一目的。
其可以用于提供資訊給編譯器,在編譯階段時給軟體提供資訊進行相關的處理,在運作時處理寫相應代碼,做對應操作。
簡述元注解
元注解可以了解為注解的注解,即在注解中使用,實作想要的功能。其具體分為:
- @Retention: 表示注解存在階段是保留在源碼,還是在位元組碼(類加載)或者運作期(JVM 中運作)。
- @Target:表示注解作用的範圍。
- @Documented:将注解中的元素包含到 Javadoc 中去。
- @Inherited:一個被@Inherited 注解了的注解修飾了一個父類,如果他的子類沒有被其他注解修飾,則它的子類也繼承了父類的注解。
- @Repeatable:被這個元注解修飾的注解可以同時作用一個對象多次,但是每次作用注解又可以代表不同的含義。
簡述 Java 中 Class 對象
java 中對象可以分為執行個體對象和 Class 對象,每一個類都有一個 Class 對象,其包含了與該類有關的資訊。
擷取 Class 對象的方法:
- Class.forName(“類的全限定名”)
- 執行個體對象.getClass()
- 類名.class
Java 反射機制是什麼?
Java 反射機制是指在程式的運作過程中可以構造任意一個類的對象、擷取任意一個類的成員變量和成員方法、擷取任意一個對象所屬的類資訊、調用任意一個對象的屬性和方法。反射機制使得 Java 具有動态擷取程式資訊和動态調用對象方法的能力。可以通過以下類調用反射 API。
- Class 類:可獲得類屬性方法
- Field 類:獲得類的成員變量
- Method 類:擷取類的方法資訊
- Construct 類:擷取類的構造方法等資訊
序列化是什麼?
序列化是一種将對象轉換成位元組序列的過程,用于解決在對對象流進行讀寫操作時所引發的問題。序列化可以将對象的狀态寫在流裡進行網絡傳輸,或者儲存到檔案、資料庫等系統裡,并在需要的時候把該流讀取出來重新構造成一個相同的對象。
簡述 Java 序列化與反序列化的實作
序列化:将 java 對象轉化為位元組序列,由此可以通過網絡對象進行傳輸。
反序列化:将位元組序列轉化為 java 對象。
具體實作:實作 Serializable 接口,或實作 Externalizable 接口中的 writeExternal()與 readExternal()方法。
簡述 Java 的 List
List 是一個有序隊列,在 Java 中有兩種實作方式:
- ArrayList 使用數組實作,是容量可變的非線程安全清單,随機通路快,集合擴容時會建立更大的數組,把原有數組複制到新數組。
- LinkedList 本質是雙向連結清單,與 ArrayList 相比插入和删除速度更快,但随機通路元素很慢。
Java 中線程安全的基本資料結構有哪些
- HashTable: 哈希表的線程安全版,效率低
- ConcurrentHashMap:哈希表的線程安全版,效率高,用于替代 HashTable
- Vector:線程安全版 Arraylist
- Stack:線程安全版棧
- BlockingQueue 及其子類:線程安全版隊列
簡述 Java 的 Set
Set 即集合,該資料結構不允許元素重複且無序。Java 對 Set 有三種實作方式:
- HashSet 通過 HashMap 實作,HashMap 的 Key 即 HashSet 存儲的元素,Value 系統自定義一個名為 PRESENT 的 Object 類型常量。判斷元素是否相同時,先比較 hashCode,相同後再利用 equals 比較,查詢 O(1)
- LinkedHashSet 繼承自 HashSet,通過 LinkedHashMap 實作,使用雙向連結清單維護元素插入順序。
- TreeSet 通過 TreeMap 實作的,底層資料結構是紅黑樹,添加元素到集合時按照比較規則将其插入合适的位置,保證插入後的集合仍然有序。查詢 O(logn)
簡述 Java 的 HashMap
- JDK8 之前底層實作是數組 + 連結清單,JDK8 改為數組 + 連結清單/紅黑樹。主要成員變量包括存儲資料的 table 數組、元素數量 size、加載因子 loadFactor。
- HashMap 中資料以鍵值對的形式存在,鍵對應的 hash 值用來計算數組下标,如果兩個元素 key 的 hash 值一樣,就會發生哈希沖突,被放到同一個連結清單上。
- table 數組記錄 HashMap 的資料,每個下标對應一條連結清單,所有哈希沖突的資料都會被存放到同一條連結清單,Node/Entry 節點包含四個成員變量:key、value、next 指針和 hash 值。
- 在 JDK8 後連結清單超過 8 會轉化為紅黑樹。
- 若目前資料/總資料容量>負載因子,Hashmap 将執行擴容操作。預設初始化容量為 16,擴容容量必須是 2 的幂次方、最大容量為 1<< 30 、預設加載因子為 0.75。
為何 HashMap 線程不安全
- 在 JDK1.7 中,HashMap 采用頭插法插入元素,是以并發情況下會導緻環形連結清單,産生死循環。
- 雖然 JDK1.8 采用了尾插法解決了這個問題,但是并發下的 put 操作也會使前一個 key 被後一個 key 覆寫。
- 由于 HashMap 有擴容機制存在,也存在 A 線程進行擴容後,B 線程執行 get 方法出現失誤的情況。
簡述 Java 的 TreeMap
TreeMap 是底層利用紅黑樹實作的 Map 結構,底層實作是一棵平衡的排序二叉樹,由于紅黑樹的插入、删除、周遊時間複雜度都為 O(logN),是以性能上低于哈希表。但是哈希表無法提供鍵值對的有序輸出,紅黑樹可以按照鍵的值的大小有序輸出。
ArrayList、Vector 和 LinkedList 有什麼共同點與差別?
- ArrayList、Vector 和 LinkedList 都是可伸縮的數組,即可以動态改變長度的數組。
- ArrayList 和 Vector 都是基于存儲元素的 Object[] array 來實作的,它們會在記憶體中開辟一塊連續的空間來存儲,支援下标、索引通路。
- 但在涉及插入元素時可能需要移動容器中的元素,插入效率較低。當存儲元素超過容器的初始化容量大小,ArrayList 與 Vector 均會進行擴容。
- Vector 是線程安全的,其大部分方法是直接或間接同步的。
- ArrayList 不是線程安全的,其方法不具有同步性質。LinkedList 也不是線程安全的。
- LinkedList 采用雙向清單實作,對資料索引需要從頭開始周遊,是以随機通路效率較低,但在插入元素的時候不需要對資料進行移動,插入效率較高。
HashMap 和 Hashtable 有什麼差別?
- HashMap 是 Hashtable 的輕量級實作,HashMap 允許 key 和 value 為 null,但最多允許一條記錄的 key 為 null.而 HashTable 不允許。
- HashTable 中的方法是線程安全的,而 HashMap 不是。在多線程通路 HashMap 需要提供額外的同步機制。
- Hashtable 使用 Enumeration 進行周遊,HashMap 使用 Iterator 進行周遊。
如何決定使用 HashMap 還是 TreeMap?
如果對 Map 進行插入、删除或定位一個元素的操作更頻繁,HashMap 是更好的選擇。如果需要對 key 集合進行有序的周遊,TreeMap 是更好的選擇。
HashSet 中,equals 與 hashCode 之間的關系?
- equals 和 hashCode 這兩個方法都是從 object 類中繼承過來的,equals 主要用于判斷對象的記憶體位址引用是否是同一個位址;
- hashCode 根據定義的哈希規則将對象的記憶體位址轉換為一個哈希碼。
- HashSet 中存儲的元素是不能重複的,主要通過 hashCode 與 equals 兩個方法來判斷存儲的對象是否相同:
- 如果兩個對象的 hashCode 值不同,說明兩個對象不相同。
- 如果兩個對象的 hashCode 值相同,接着會調用對象的 equals 方法,如果 equlas 方法的傳回結果為 true,那麼說明兩個對象相同,否則不相同。
fail-fast 和 fail-safe 疊代器的差別是什麼?
fail-fast 直接在容器上進行,在周遊過程中,一旦發現容器中的資料被修改,就會立刻抛出 ConcurrentModificationException 異常進而導緻周遊失敗。常見的使用 fail-fast 方式的容器有 HashMap 和 ArrayList 等。
fail-safe 這種周遊基于容器的一個克隆。是以對容器中的内容修改不影響周遊。常見的使用 fail-safe 方式周遊的容器有 ConcurrentHashMap 和 CopyOnWriteArrayList。
Collection 和 Collections 有什麼差別?
Collection 是一個集合接口,它提供了對集合對象進行基本操作的通用接口方法,所有集合都是它的子類,比如 List、Set 等。
Collections 是一個包裝類,包含了很多靜态方法、不能被執行個體化,而是作為工具類使用,比如提供的排序方法:Collections.sort(list);提供的反轉方法:Collections.reverse(list)。