Java語言具有哪些特點?
- Java為純面向對象的語言。它能夠直接反應現實生活中的對象。
- 具有平台無關性。java利用Java虛擬機運行字節碼,無論是在Windows、Linux還是MacOS等其它平台對Java程序進行編譯,編譯后的程序可在其它平台運行。
- Java為解釋型語言,編譯器把Java代碼編譯成平台無關的中間代碼,然后在JVM上解釋運行,具有很好的可移植性。
- Java提供了很多內置類庫。如對多線程支持,對網絡通信支持,最重要的一點是提供了垃圾回收器。
- Java具有較好的安全性和健壯性。Java提供了異常處理和垃圾回收機制,去除了C++中難以理解的指針特性。
- Java語言提供了對Web應用開發的支持。
面向對象的三大特性?
- 繼承:對象的一個新類可以從現有的類中派生,派生類可以從它的基類那繼承方法和實例變量,且派生類可以修改或新增新的方法使之更適合特殊的需求。
- 封裝:將客觀事物抽象成類,每個類可以把自身數據和方法只讓可信的類或對象操作,對不可信的進行信息隱藏。
- 多態:允許不同類的對象對同一消息作出響應。不同對象調用相同方法即使參數也相同,最終表現行為是不一樣的。
字節序定義以及Java屬於哪種字節序?
字節序是指多字節數據在計算機內存中存儲或網絡傳輸時個字節的存儲順序。通常由小端和大端兩組方式。
- 小端:低位字節存放在內存的低地址端,高位字節存放在內存的高地址端。
- 大端:高位字節存放在內存的低地址端,低位字節存放在內存的高地址端。
Java語言的字節序是大端。
JDK與JRE有什么區別?
- JDK:Java開發工具包(Java Development Kit),提供了Java的開發環境和運行環境。
- JRE:Java運行環境(Java Runtime Environment),提供了Java運行所需的環境。
JDK包含了JRE。如果只運行Java程序,安裝JRE即可。要編寫Java程序需安裝JDK.
簡述Java訪問修飾符
- default: 默認訪問修飾符,在同一包內可見
- private: 在同一類內可見,不能修飾類
- protected : 對同一包內的類和所有子類可見,不能修飾類
- public: 對所有類可見
構造方法、成員變量初始化以及靜態成員變量三者的初始化順序?
先后順序:靜態成員變量、成員變量、構造方法。 詳細的先后順序:父類靜態變量、父類靜態代碼塊、子類靜態變量、子類靜態代碼塊、父類非靜態變量、父類非靜態代碼塊、父類構造函數、子類非靜態變量、子類非靜態代碼塊、子類構造函數。
接口和抽象類的相同點和區別?
相同點:
- 都不能被實例化。
- 接口的實現類或抽象類的子類需實現接口或抽象類中相應的方法才能被實例化。
不同點:
- 接口只能有方法定義,不能有方法的實現,而抽象類可以有方法的定義與實現。
- 實現接口的關鍵字為implements,繼承抽象類的關鍵字為extends。一個類可以實現多個接口,只能繼承一個抽象類。
- 當子類和父類之間存在邏輯上的層次結構,推薦使用抽象類,有利於功能的累積。當功能不需要,希望支持差別較大的兩個或更多對象間的特定交互行為,推薦使用接口。使用接口能降低軟件系統的耦合度,便於日后維護或添加刪除方法。
為什么Java語言不支持多重繼承?
- 為了程序的結構能夠更加清晰從而便於維護。假設Java語言支持多重繼承,類C繼承自類A和類B,如果類A和B都有自定義的成員方法f(),那么當代碼中調用類C的f()會產生二義性。Java語言通過實現多個接口間接支持多重繼承,接口由於只包含方法定義,不能有方法的實現,類C繼承接口A與接口B時即使它們都有方法f(),也不能直接調用方法,需實現具體的f()方法才能調用,不會產生二義性。
- 多重繼承會使類型轉換、構造方法的調用順序變得復雜,會影響到性能。
Java提供的多態機制?
Java提供了兩種用於多態的機制,分別是重載與覆蓋。
- 重載:重載是指同一個類中有多個同名的方法,但這些方法有不同的參數,在編譯期間就可以確定調用哪個方法。
- 覆蓋:覆蓋是指派生類重寫基類的方法,使用基類指向其子類的實例對象,或接口的引用變量指向其實現類的實例對象,在程序調用的運行期根據引用變量所指的具體實例對象調用正在運行的那個對象的方法,即需要到運行期才能確定調用哪個方法。
重載與覆蓋的區別?
- 覆蓋是父類與子類之間的關系,是垂直關系;重載是同一類中方法之間的關系,是水平關系。
- 覆蓋只能由一個方法或一對方法產生關系;重載是多個方法之間的關系。
- 覆蓋要求參數列表相同;重載要求參數列表不同。
- 覆蓋中,調用方法體是根據對象的類型來決定的,而重載是根據調用時實參表與形參表來對應選擇方法體。
- 重載方法可以改變返回值的類型,覆蓋方法不能改變返回值的類型。
final、finally和finalize的區別是什么?
- final用於聲明屬性、方法和類,分別表示屬性不可變、方法不可覆蓋、類不可繼承。
- finally作為異常處理的一部分,只能在try/catch語句中使用,finally附帶一個語句塊用來表示這個語句最終一定被執行,經常被用在需要釋放資源的情況下。
- finalize是Object類的一個方法,在垃圾收集器執行的時候會調用被回收對象的finalize()方法。當垃圾回收器准備好釋放對象占用空間時,首先會調用finalize()方法,並在下一次垃圾回收動作發生時真正回收對象占用的內存。
出現在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語句。
Java語言中關鍵字static的作用是什么?
static的主要作用有兩個:
- 為某種特定數據類型或對象分配與創建對象個數無關的單一的存儲空間。
- 使得某個方法或屬性與類而不是對象關聯在一起,即在不創建對象的情況下可通過類直接調用方法或使用類的屬性。
具體而言static又可分為4種使用方式:
- 修飾成員變量。用static關鍵字修飾的靜態變量在內存中只有一個副本。只要靜態變量所在的類被加載,這個靜態變量就會被分配空間,可以使用''類.靜態變量''和''對象.靜態變量''的方法使用。
- 修飾成員方法。static修飾的方法無需創建對象就可以被調用。static方法中不能使用this和super關鍵字,不能調用非static方法,只能訪問所屬類的靜態成員變量和靜態成員方法。
- 修飾代碼塊。JVM在加載類的時候會執行static代碼塊。static代碼塊常用於初始化靜態變量。static代碼塊只會被執行一次。
- 修飾內部類。static內部類可以不依賴外部類實例對象而被實例化。靜態內部類不能與外部類有相同的名字,不能訪問普通成員變量,只能訪問外部類中的靜態成員和靜態成員方法。
Java代碼塊執行順序
- 父類靜態代碼塊(只執行一次)
- 子類靜態代碼塊(只執行一次)
- 父類構造代碼塊
- 父類構造函數
- 子類構造代碼塊
- 子類構造函數
- 普通代碼塊
Java中一維數組和二維數組的聲明方式?
一維數組的聲明方式:
- type arrayName[]
- type[] arrayName
二維數組的聲明方式:
- type arrayName[][]
- type[][] arrayName
- type[] arrayName[]
其中type為基本數據類型或類,arrayName為數組名字
String和StringBuffer有什么區別?
String用於字符串操作,屬於不可變類。String對象一旦被創建,其值將不能被改變。而StringBuffer是可變類,當對象創建后,仍然可以對其值進行修改。
判等運算符==與equals的區別?
== 比較的是引用,equals比較的是內容。
- 如果變量是基礎數據類型,== 用於比較其對應值是否相等。如果變量指向的是對象,== 用於比較兩個對象是否指向同一塊存儲空間。
- equals是Object類提供的方法之一,每個Java類都繼承自Object類,所以每個對象都具有equals這個方法。Object類中定義的equals方法內部是直接調用 == 比較對象的。但通過覆蓋的方法可以讓它不是比較引用而是比較數據內容。
為什么要把String設計為不變量?
- 節省空間:字符串常量存儲在JVM的字符串池中可以被用戶共享。
- 提高效率:String會被不同線程共享,是線程安全的。在涉及多線程操作中不需要同步操作。
- 安全:String常被用於用戶名、密碼、文件名等使用,由於其不可變,可避免黑客行為對其惡意修改。
序列化是什么?
序列化是一種將對象轉換成字節序列的過程,用於解決在對對象流進行讀寫操作時所引發的問題。序列化可以將對象的狀態寫在流里進行網絡傳輸,或者保存到文件、數據庫等系統里,並在需要的時候把該流讀取出來重新構造成一個相同的對象。
簡述Java中Class對象
java中對象可以分為實例對象和Class對象,每一個類都有一個Class對象,其包含了與該類有關的信息。
獲取Class對象的方法:
- Class.forName(“類的全限定名”)
- 實例對象.getClass()
- 類名.class
Java反射機制是什么?
Java反射機制是指在程序的運行過程中可以構造任意一個類的對象、獲取任意一個類的成員變量和成員方法、獲取任意一個對象所屬的類信息、調用任意一個對象的屬性和方法。反射機制使得Java具有動態獲取程序信息和動態調用對象方法的能力。可以通過以下類調用反射API。
- Class類:可獲得類屬性方法
- Field類:獲得類的成員變量
- Method類:獲取類的方法信息
- Construct類:獲取類的構造方法等信息
簡述注解
Java 注解用於為 Java 代碼提供元數據。作為元數據,注解不直接影響你的代碼執行,但也有一些類型的注解實際上可以用於這一目的。
其可以用於提供信息給編譯器,在編譯階段時給軟件提供信息進行相關的處理,在運行時處理寫相應代碼,做對應操作。
簡述元注解
元注解可以理解為注解的注解,即在注解中使用,實現想要的功能。其具體分為:
- @Retention: 表示注解存在階段是保留在源碼,還是在字節碼(類加載)或者運行期(JVM中運行)。
- @Target:表示注解作用的范圍。
- @Documented:將注解中的元素包含到 Javadoc 中去。
- @Inherited:一個被@Inherited注解了的注解修飾了一個父類,如果他的子類沒有被其他注解修飾,則它的子類也繼承了父類的注解。
- @Repeatable:被這個元注解修飾的注解可以同時作用一個對象多次,但是每次作用注解又可以代表不同的含義。
簡述Java異常的分類
Java異常分為Error(程序無法處理的錯誤),和Exception(程序本身可以處理的異常)。這兩個類均繼承Throwable。
Error常見的有StackOverFlowError,OutOfMemoryError等等。
Exception可分為運行時異常和非運行時異常。對於運行時異常,可以利用try catch的方式進行處理,也可以不處理。對於非運行時異常,必須處理,不處理的話程序無法通過編譯。
簡述throw與throws的區別
throw一般是用在方法體的內部,由開發者定義當程序語句出現問題后主動拋出一個異常。
throws一般用於方法聲明上,代表該方法可能會拋出的異常列表。
簡述泛型
泛型,即“參數化類型”,解決不確定對象具體類型的問題。在編譯階段有效。在泛型使用過程中,操作的數據類型被指定為一個參數,這種參數類型在類中稱為泛型類、接口中稱為泛型接口和方法中稱為泛型方法。
簡述泛型擦除
Java編譯器生成的字節碼是不包涵泛型信息的,泛型類型信息將在編譯處理是被擦除,這個過程被稱為泛型擦除。
簡述Java基本數據類型
- byte: 占用1個字節,取值范圍-128 ~ 127
- short: 占用2個字節,取值范圍-2^15^ ~ 2^15^-1
- int:占用4個字節,取值范圍-2^31^ ~ 2^31^-1
- long:占用8個字節
- float:占用4個字節
- double:占用8個字節
- char: 占用2個字節
- boolean:占用大小根據實現虛擬機不同有所差異
簡述自動裝箱拆箱
對於Java基本數據類型,均對應一個包裝類。
裝箱就是自動將基本數據類型轉換為包裝器類型,如int->Integer
拆箱就是自動將包裝器類型轉換為基本數據類型,如Integer->int
簡述重載與重寫的區別
重寫即子類重寫父類的方法,方法對應的形參和返回值類型都不能變。
重載即在一個類中,方法名相同,參數類型或數量不同。
簡述java的多態
Java多態可以分為編譯時多態和運行時多態。
編譯時多態主要指方法的重載,即通過參數列表的不同來區分不同的方法。
運行時多態主要指繼承父類和實現接口時,可使用父類引用指向子類對象。
運行時多態的實現:主要依靠方法表,方法表中最先存放的是Object類的方法,接下來是該類的父類的方法,最后是該類本身的方法。如果子類改寫了父類的方法,那么子類和父類的那些同名方法共享一個方法表項,都被認作是父類的方法。因此可以實現運行時多態。
簡述抽象類與接口的區別
抽象類:體現的是is-a的關系,如對於man is a person,就可以將person定義為抽象類。
接口:體現的是can的關系。是作為模板實現的。如設置接口fly,plane類和bird類均可實現該接口。
一個類只能繼承一個抽象類,但可以實現多個接口。
簡述==與equals方法的區別
對於==,在基本數據類型比較時,比較的是對應的值,對引用數據類型比較時,比較的是其內存的存放地址。
對於equals方法,在該方法未被重寫時,其效果和==一致,但用戶可以根據對應需求對判斷邏輯進行改寫,比如直接比較對象某個屬性值是否相同,相同則返回true,不同則返回false。需保證equals方法相同對應的對象hashCode也相同。
簡述Object類常用方法
- hashCode:通過對象計算出的散列碼。用於map型或equals方法。 需要保證同一個對象多次調用該方法,總返回相同的整型值。
- equals:判斷兩個對象是否一致。需保證equals方法相同對應的對象hashCode也相同。
- toString: 用字符串表示該對象
- clone:深拷貝一個對象
簡述內部類及其作用
- 成員內部類:作為成員對象的內部類。可以訪問private及以上外部類的屬性和方法。外部類想要訪問內部類屬性或方法時,必須要創建一個內部類對象,然后通過該對象訪問內部類的屬性或方法。外部類也可訪問private修飾的內部類屬性。
- 局部內部類:存在於方法中的內部類。訪問權限類似局部變量,只能訪問外部類的final變量。
- 匿名內部類:只能使用一次,沒有類名,只能訪問外部類的final變量。
- 靜態內部類:類似類的靜態成員變量。
簡述String/StringBuffer與StringBuilder
String類采用利用final修飾的字符數組進行字符串保存,因此不可變。如果對String類型對象修改,需要新建對象,將老字符和新增加的字符一並存進去。
StringBuilder,采用無final修飾的字符數組進行保存,因此可變。但線程不安全。
StringBuffer,采用無final修飾的字符數組進行保存,可理解為實現線程安全的StringBuilder。
簡述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),所以性能上低於哈希表。但是哈希表無法提供鍵值對的有序輸出,紅黑樹可以按照鍵的值的大小有序輸出。
Collection和Collections有什么區別?
- Collection是一個集合接口,它提供了對集合對象進行基本操作的通用接口方法,所有集合都是它的子類,比如List、Set等。
- Collections是一個包裝類,包含了很多靜態方法、不能被實例化,而是作為工具類使用,比如提供的排序方法: Collections.sort(list);提供的反轉方法:Collections.reverse(list)。
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是更好的選擇。
fail-fast和fail-safe迭代器的區別是什么?
- fail-fast直接在容器上進行,在遍歷過程中,一旦發現容器中的數據被修改,就會立刻拋出ConcurrentModificationException異常從而導致遍歷失敗。常見的使用fail-fast方式的容器有HashMap和ArrayList等。
- fail-safe這種遍歷基於容器的一個克隆。因此對容器中的內容修改不影響遍歷。常見的使用fail-safe方式遍歷的容器有ConcurrentHashMap和CopyOnWriteArrayList。
HashSet中,equals與hashCode之間的關系?
equals和hashCode這兩個方法都是從object類中繼承過來的,equals主要用於判斷對象的內存地址引用是否是同一個地址;hashCode根據定義的哈希規則將對象的內存地址轉換為一個哈希碼。HashSet中存儲的元素是不能重復的,主要通過hashCode與equals兩個方法來判斷存儲的對象是否相同:
- 如果兩個對象的hashCode值不同,說明兩個對象不相同。
- 如果兩個對象的hashCode值相同,接着會調用對象的equals方法,如果equlas方法的返回結果為true,那么說明兩個對象相同,否則不相同。