歡迎關注文章系列,一塊兒學習
《提高能力,漲薪可待篇》
《面試知識,工做可待篇》
《實戰演練,拒絕996篇》
也歡迎關注微信公衆號【Ccww筆記】,原創技術文章第一時間推出
若是此文對你有幫助、喜歡的話,那就點個讚唄,點個關注唄!java
是否是感受找工做面試是那麼難呢?程序員
在找工做面試應在學習的基礎進行總結面試知識點,工做也指日可待,歡迎一塊兒學習【面試知識,工做可待】系列 《面試知識,工做可待篇》面試
【重要】Java 有自動內存管理機制,不須要程序員手動釋放無用內存。數據庫
JDK 即爲 Java 開發工具包,包含編寫 Java 程序所必須的編譯、運行等開發工具以及 JRE。開發工具如:編程
JRE 即爲 Java 運行環境,提供了運行 Java 應用程序所必須的軟件環境,包含有 Java 虛擬機(JVM)和豐富的系統類庫。系統類庫即爲 Java 提早封裝好的功能類,只需拿來直接使用便可,能夠大大的提升開發效率。數組
JVM 即爲 Java 虛擬機,提供了字節碼文件(.class)的運行環境支持。緩存
這個問題,面試官能夠衍生提問,Java 是編譯執行的語言,仍是解釋執行的語言。 Java 中引入了虛擬機的概念,即在機器和編譯程序之間加入了一層抽象的虛擬的機器。這臺虛擬的機器在任何平臺上都提供給編譯程序一個的共同的接口。安全
編譯程序只須要面向虛擬機,生成虛擬機可以理解的代碼,而後由解釋器來將虛擬機代碼轉換爲特定系統的機器碼執行。在 Java 中,這種供虛擬機理解的代碼叫作字節碼(即擴展名爲 .class 的文件),它不面向任何特定的處理器,只面向虛擬機。性能優化
每一種平臺的解釋器是不一樣的,可是實現的虛擬機是相同的。Java 源程序通過編譯器編譯後變成字節碼,字節碼由虛擬機解釋執行,虛擬機將每一條要執行的字節碼送給解釋器,解釋器將其翻譯成特定機器上的機器碼,而後在特定的機器上運行。這也就是解釋了 Java 的編譯與解釋並存的特色。bash
=> 編譯器 => JVM 可執行的 Java 字節碼(即虛擬指令)=> JVM => JVM 中解釋器 => 機器可執行的二進制機器碼 => 程序運行
Java 語言經過字節碼的方式,在必定程度上解決了傳統解釋型語言執行效率低的問題,同時又保留了解釋型語言可移植的特色。因此 Java 程序運行時比較高效,並且,因爲字節碼並不專對一種特定的機器,所以,Java程序無須從新編譯即可在多種不一樣的計算機上運行。
解釋型語言:解釋型語言,是在運行的時候將程序翻譯成機器語言。解釋型語言的程序不須要在運行前編譯,在運行程序的時候才翻譯,專門的解釋器負責在每一個語句執行的時候解釋程序代碼。這樣解釋型語言每執行一次就要翻譯一次,效率比較低 例如:Python、PHP 。
面向對象是一種思想,萬事萬物抽象成一個對象,這裏只討論面向對象編程(OOP),Java 是一個支持併發、基於類和麪向對象的計算機編程語言。
類是抽象的概念,是萬事萬物的抽象,是一類事物的共同特徵的集合。 用計算機語言來描述類,是屬性和方法的集合。
對象是類的具象,是一個實體。 對於咱們每一個人這個個體,都是抽象概念人 類 的不一樣的 實體 。
面向對象軟件開發具備如下優勢:
封裝,給對象提供了隱藏內部特性和行爲的能力。對象提供一些能被其餘對象訪問的方法來改變它內部的數據。
在 Java 當中,有 4 種修飾符: default、public、private 和 protected 。每一種修飾符給其餘的位於同一個包或者不一樣包下面對象賦予了不一樣的訪問權限,權限以下:
訪問權限 | 類 | 包 | 子類 | 其餘包 |
---|---|---|---|---|
public | ∨ | ∨ | ∨ | ∨ |
protect | ∨ | ∨ | ∨ | × |
default | ∨ | ∨ | × | × |
private | ∨ | × | × | × |
封裝好處:
繼承,使對象基於基類字段和方法,新增自定義的的方法和屬性。繼承提供了代碼的重用行,也能夠在不修改類的狀況下給現存的類添加新特性。
繼承屬性:
多態,程序中定義的引用變量所指向的具體類型和經過該引用變量發出的方法調用在編程時並不肯定,而是在程序運行期間才肯定,即一個引用變量到底會指向哪一個類的實例對象,該引用變量發出的方法調用究竟是哪一個類中實現的方法,必須在由程序運行期間才能決定。
Java中有兩種形式能夠實現多態:
面向過程:就是分析出解決問題所須要的步驟,而後用函數把這些步驟一步一步實現,使用的時候一個一個依次調用就能夠了,
面向對象:是把構成問題事務分解成各個對象,創建對象的目的不是爲了完成一個步驟,而是爲了描敘某個事物在整個解決問題的步驟中的行爲。
整數型:默認 int 型,小數默認是 double 型。Float 和 Long 類型的必須加後綴。
好比:float f = 100f 。
引用類型聲明的變量是指該變量在內存中實際存儲的是一個引用地址,實體在堆中。
引用類型指向一個對象,不是原始值,指向對象的變量是引用變量
在java裏面除去基本數據類型的其餘類型都是引用類型,本身定義的class類都是引用類型,能夠像基本類型同樣使用。
引用類型常見的有:String、StringBuffer、ArrayList、HashSet、HashMap等
特別注意,String 是引用類型不是基本類型。
引用類型 | 對象是否可引用 | 回收時間 | 使用場景 |
---|---|---|---|
強引用 | 能夠 | 從不回收 | 廣泛對象的狀態 |
軟引用 | 能夠 | 內存不足時 | 內存敏感的高速緩存 |
弱引用 | 能夠 | 下一次GC | 對象緩存 |
虛引用 | 不能夠 | 下一次GC | 通常用於追蹤垃圾收集器的回收動做 |
值傳遞,是對基本型變量而言的,傳遞的是該變量的一個副本,改變副本不影響原變量。
引用傳遞,通常是對於對象型變量而言的,傳遞的是該對象地址的一個副本,並非原對象自己。
通常認爲,Java 內的傳遞都是值傳遞,Java 中實例對象的傳遞是引用傳遞。
例如 String 類,兩個引用所指向的 String 都是 "abc" ,但可能出現他們實際對應的對象並非同一個(和 JVM 實現方式有關),所以用 == 判斷他們可能不相等,但用 equals 方法判斷必定是相等的。
類是對事物的抽象,抽象類是對類的抽象,接口是對抽象類的抽象。
從面向對象的角度來說,咱們知道全部的對象都是經過類來描繪的,可是反過來卻不是這樣,並非 全部的類都是用來描繪對象的,若是一個類中沒有包含足夠的信息來描繪一個具體的對象,這樣的類就能夠認爲是抽象類。
抽象類除了不能實例化對象以外,類的其它功能依然存在,成員變量、成員方法和構造方法的訪問方式和普通類同樣。因爲抽象類不能實例化對象,因此抽象類必須被繼承,才能被使用。
接口,在JAVA編程語言中是一個抽象類型,主要是抽象方法的集合,接口中的變量定義必須爲public static final類型。接口一般以interface來聲明。
參數 | 抽象類 | 接口 |
---|---|---|
默認的方法實現 | 它能夠有默認的方法實現 | 接口徹底是抽象的。它根本不存在方法的實現 |
實現 | 子類使用extends關鍵字來繼承抽象類。若是子類不是抽象類的話,它須要提供抽象類中全部聲明的方法的實現。 | 子類使用關鍵字implements來實現接口。它須要提供接口中全部聲明的方法的實現 |
構造器 | 抽象類能夠有構造器 | 接口不能有構造器 |
與正常Java類的區別 | 除了你不能實例化抽象類以外,它和普通Java類沒有任何區別 | 接口是徹底不一樣的類型 |
訪問修飾符 | 抽象方法能夠有public、protected和default這些修飾符 | 接口方法默認修飾符是public。你不可使用其它修飾符。 |
main方法 | 抽象方法能夠有main方法而且咱們能夠運行它 | 接口沒有main方法,所以咱們不能運行它。(java8之後接口能夠有default和static方法,因此能夠運行main方法) |
多繼承 | 抽象方法能夠繼承一個類和實現多個接口 | 接口只能夠繼承一個或多個其它接口 |
速度 | 它比接口速度要快 | 接口是稍微有點慢的,由於它須要時間去尋找在類中實現的方法。 |
添加新方法 | 若是你往抽象類中添加新的方法,你能夠給它提供默認的實現。所以你不須要改變你如今的代碼 | 若是你往接口中添加方法,那麼你必須改變實現該接口的類。 |
初始化順序以下:
---->父類靜態變量
--------->父類靜態代碼塊
------------->子類靜態變量、
----------------->子類靜態代碼塊
--------------------->父類非靜態變量(父類實例成員變量)
------------------------->父類構造函數
----------------------------->子類非靜態變量(子類實例成員變量)
--------------------------------->子類構造函數
簡單的說,就是在一個類、接口或者方法的內部建立另外一個類。這樣理解的話,建立內部類的方法就很明確了。固然,詳細的能夠看看 《Java 內部類總結(吐血之做)》 文章。
內部類能夠很好的實現隱藏(通常的非內部類,是不容許有 private 與protected權限的,但內部類能夠)
內部類擁有外圍類的全部元素的訪問權限
但是實現多重繼承
能夠避免修改接口而實現同一個類中兩種同名方法的調用。
能夠繼承其餘類或實現其餘接口,在 Java 集合的流式操做中,咱們經常這麼幹。
一個內部類對象能夠訪問建立它的外部類對象的成員,包括私有成員。
若是你把靜態嵌套類看成內部類的一種特例,那在這種狀況下不能夠訪問外部類的普通成員變量,而只能訪問外部類中的靜態成員,例如:
class Outer {
static int x;
static class Inner {
void test() {
syso(x);
}
}
}
複製代碼
當新對象被建立的時候,構造方法會被調用。每個類都有構造方法。在程序員沒有給類提供構造方法的狀況下,Java 編譯器會爲這個類建立一個默認的構造方法。
Java 中構造方法重載和方法重載很類似。能夠爲一個類建立多個構造方法。每個構造方法必須有它本身惟一的參數列表。
父類的 equals ,通常狀況下是沒法知足子類的 equals 的需求。
好比全部的對象都繼承 Object ,默認使用的是 Object 的 equals 方法,在比較兩個對象的時候,是看他們是否指向同一個地址。可是咱們的需求是對象的某個屬性相同,就相等了,而默認的 equals 方法知足不了當前的需求,因此咱們要重寫 equals 方法。
若是重寫了 equals 方法,就必須重寫 hashCode 方法,不然就會下降 Map 等集合的索引速度。
理解答案與4.8.1差很少,
equals 方法,用於比較對象的內容是否相等。
當覆蓋了 equals 方法時,比較對象是否相等將經過覆蓋後的 equals 方法進行比較(判斷對象的內容是否相等)。 hashCode 方法,大多在集合中用到。
將對象放入到集合中時,首先判斷要放入對象的 hashCode 值與集合中的任意一個元素的 hashCode 值是否相等,若是不相等直接將該對象放入集合中。 若是 hashCode 值相等,而後再經過 equals 方法判斷要放入對象與集合中的任意一個對象是否相等,若是 equals 判斷不相等,直接將該元素放入到集合中,不然不放入。
可能會發生,這個被稱爲哈希碰撞。固然,相等的對象,即咱們重寫了 equals 方法,必定也要重寫 hashCode 方法,不然將出現咱們在 HashMap 中,相等的對象做爲 key ,將找不到對應的 value 。
equals 不相等,hashCode 可能相等。
equals 相等,請重寫 hashCode 方法,保證 hashCode 相等。
通常來講,hashCode 方法的重寫,能夠看看 《科普:爲何 String hashCode 方法選擇數字31做爲乘子》 方法。
可變性:
String 類中使用 final 關鍵字字符數組保存字符串,代碼:
private final char value[],
複製代碼
因此string對象是不可變的。
StringBuilder與StringBuffer都繼承自AbstractStringBuilder類,在 AbstractStringBuilder 中也是使用字符數組保存字符串 char[] value ,可是沒有用 final 關鍵字修飾,代碼:
char[]value
複製代碼
這兩種對象都是可變的。
線程安全性:
AbstractStringBuilder是StringBuilder與StringBuffer的公共父類,定義了一些字符串的基本操做,如expandCapacity、append、insert、indexOf等公共方法。
性能: 每次對String 類型進行改變的時候,都會生成一個新的String對象,而後將指針指向新的String 對象。
StringBuffer每次都會對StringBuffer對象自己進行操做,而不是生成新的對象並改變對象引用。相同狀況下使用StirngBuilder 相比使用StringBuffer 僅能得到10%~15% 左右的性能提高,但卻要冒多線程不安全的風險。
首先,在 String 池內找,找到 "xyz" 字符串,不建立 "xyz" 對應的 String 對象,不然建立一個對象。 而後,遇到 new 關鍵字,在內存上建立 String 對象,並將其返回給 s ,又一個對象。 因此,總共是 1 個或者 2 個對象
StringTokenizer ,是一個用來分割字符串的工具類。
示例代碼以下:
StringTokenizer st = new StringTokenizer(」Hello World」);
while (st.hasMoreTokens()) {
System.out.println(st.nextToken());
}
複製代碼
輸出以下:
Hello World
自動裝箱和拆箱,就是基本類型和引用類型之間的轉換。
若是你在 Java5 下進行過編程的話,你必定不會陌生這一點,你不能直接地向集合( Collection )中放入原始類型值,由於集合只接收對象。
一般這種狀況下你的作法是,將這些原始類型的值轉換成對象,而後將這些轉換的對象放入集合中。使用 Integer、Double、Boolean 等這些類,咱們能夠將原始類型值轉換成對應的對象,可是從某些程度可能使得代碼不是那麼簡潔精煉。
爲了讓代碼簡練,Java5 引入了具備在原始類型和對象類型自動轉換的裝箱和拆箱機制。 可是自動裝箱和拆箱並不是完美,在使用時須要有一些注意事項,若是沒有搞明白自動裝箱和拆箱,可能會引發難以察覺的 Bug 。
int 是基本數據類型。
Integer 是其包裝類,注意是一個類。
須要注意下 Integer 的緩存策略
final ,是修飾符關鍵字。
Class 類:若是一個類被聲明爲 final ,意味着它不能再派生出新的子類,不能做爲父類被繼承。所以一個類不能既被聲明爲 abstract 的,又被聲明爲 final 的。
變量或方法聲明爲 final ,能夠保證它們在使用中不被改變。被聲明爲 final 的變量必須在聲明時給定初值,而在之後的引用中只能讀取,不可修改。被聲明爲 final 的方法也一樣只能使用,不能重寫。
另外,在早期的 Java 實現版本中,會將 final 方法轉爲內嵌調用。可是若是方法過於龐大,可能看不到內嵌調用帶來的任何性能提高(如今的 Java 版本已經不須要使用 final 方法進行這些優化了)。類中全部的private 方法都隱式地指定爲 final 。
在異常處理時提供 finally 塊來執行任何清除操做。若是拋出一個異常,那麼相匹配的 catch 子句就會執行,而後控制就會進入 finally 塊(若是有的話)。
在如下 4 種特殊狀況下,finally塊不會被執行:
在 finally 語句塊中發生了異常。
在前面的代碼中用了 System.exit() 退出程序。
程序所在的線程死亡。
關閉 CPU 。
finalize ,是方法名。
Java 容許使用 #finalize() 方法,在垃圾收集器將對象從內存中清除出去以前作必要的清理工做。這個方法是由垃圾收集器在肯定這個對象沒有被引用時對這個對象調用的。
它是在 Object 類中定義的,所以全部的類都繼承了它。 子類覆蓋 finalize() 方法,以整理系統資源或者執行其餘清理工做。
finalize() 方法,是在垃圾收集器刪除對象以前對這個對象調用的。 通常狀況下,咱們在業務中不會本身實現這個方法,更可能是在一些框架中使用。
不能,由於 String 是 final 修飾。
static 變量在 Java 中是屬於類的,它在全部的實例中的值是同樣的。當類被 Java 虛擬機載入的時候,會對 static 變量進行初始化。若是你的代碼嘗試不用實例來訪問非 static 的變量,編譯器會報錯,由於這些變量尚未被建立出來,尚未跟任何實例關聯上。
因爲靜態方法能夠不經過對象進行調用,所以在靜態方法裏,不能調用其餘非靜態變量,也不能夠訪問非靜態變量成員。
若是你的代碼嘗試不用實例來訪問非 static 的變量,編譯器會報錯,由於這些變量尚未被建立出來,尚未跟任何實例關聯上。
生命週期的不一樣:
調用方式不一樣:
別名不一樣:
數據存儲位置不一樣:
成員變量數據存儲在堆內存的對象中,因此也叫對象的特有數據。
靜態變量數據存儲在方法區(共享數據區)的靜態區,因此也叫對象的共享數據。
-->父類靜態變量
----->父類靜態代碼塊
--------->子類靜態變量
------------->子類靜態代碼塊
------------------>父類普通變量
----------------------->父類普通代碼塊
----------------------------->父類構造函數
------------------------------------>子類普通變量
------------------------------------------>子類普通代碼塊
------------------------------------------------->子類構造函數
transient聲明一個實例變量,當對象存儲時,它的值不須要維持。換句話來講就是,用transient關鍵字標記的成員變量不參與序列化過程,
transient 只能修飾變量,不能修飾類和方法。
對於不想進行序列化的變量,使用 transient 關鍵字修飾,
volatile 關鍵字用在多線程同步中,可保證讀取的可見性,JVM只是保證從主內存加載到線程工做內存的值是最新的讀取值,而非 cache 中
不能 , 多個線程對 volatile 的寫操做,沒法保證線程安全。例如假如線程 1,線程 2 在進行 read,load 操做中,發現主內存中 count 的值都是 5,那麼都會加載這個最新的值,在線程 1 堆 count 進行修改以後,會 write 到主內存中,主內存中的 count 變量就會變爲 6;線程 2 因爲已經進行 read,load 操做,在進行運算以後,也會更新主內存 count 的變量值爲 6;致使兩個線程及時用 volatile 關鍵字修改以後,仍是會存在併發的狀況
Java IO 相關的類,在 java.io 包下,具體操做分紅面向字節(Byte)和麪向字符(Character)兩種方式。以下圖所示:
序列化就是一種用來處理對象流的機制,所謂對象流也就是將對象的內容進行流化。
能夠對流化後的對象進行讀寫操做,也可將流化後的對象傳輸於網絡之間。 序列化是爲了解決在對對象流進行讀寫操做時所引起的問題。 反序列化的過程,則是和序列化相反的過程。
咱們不能將序列化侷限在 Java 對象轉換成二進制數組,好比,將一個 Java 對象轉換成 JSON 字符串等,這也能夠理解爲是序列化。
將須要被序列化的類,實現 Serializable 接口,該接口沒有須要實現的方法,implements Serializable 只是爲了標註該對象是可被序列化的。
序列化
反序列化
實際場景下,咱們使用的克隆比較少,更可能是對象之間的屬性克隆。例如說,將 DO 的屬性複製到 DTO 中,又或者將 DTO 的屬性複製到 VO 中。此時,咱們通常使用 BeanUtils 工具類。
異常機制是指當程序出現錯誤後,程序如何處理。具體來講,異常機制提供了程序退出的安全通道。當出現錯誤後,程序執行的流程發生改變,程序的控制權轉移到異常處理器。
程序錯誤分爲三種:
Throwable 類圖
Throwable有兩個重要的子類 :
兩者都是 Java 異常處理的重要子類,各自都包含大量子類
Error(錯誤),表示系統級的錯誤和程序沒必要處理的異常,是 Java 運行環境中的內部錯誤或者硬件問題。
Exception(異常),表示須要捕捉或者須要程序進行處理的異常,它處理的是由於程序設計的瑕疵而引發的問題或者在外的輸入等引發的通常性問題,是程序必須處理的。Exception 又分爲運行時異常,受檢查異常。
Error:Error類對象由 Java 虛擬機生成並拋出,大多數錯誤與代碼編寫者所執行的操做無關 。好比:
Exception : 在Exception分支中有一個重要的子類RuntimeException(運行時異常),該類型的異常自動爲你所編寫的程序定義 異常,好比:
ArrayIndexOutOfBoundsException(數組下標越界)
NullPointerException(空指針異常)
ArithmeticException(算術異常)
MissingResourceException(丟失資源)
ClassNotFoundException(找不到類)
BufferOverflowException
ClassCastException
Effective Java中對異常的使用給出瞭如下指導原則 :
不要將異常處理用於正常的控制流(設計良好的API不該該強迫它的調用者爲了正常的控制流而使用異常) 對能夠恢復的狀況使用受檢異常,對編程錯誤使用運行時異常 避免沒必要要的使用受檢異常(能夠經過一些狀態檢測手段來避免異常的發生) 優先使用標準的異常 每一個方法拋出的異常都要有文檔 保持異常的原子性 不要在catch中忽略掉捕獲到的異常
無論程序是否發生了異常, finally 語句塊都會被執行,甚至當沒有catch 聲明但拋出了一個異常時, finally 語句塊也會被執行。
finally 語句塊一般用於釋放資源, 如 I/O 緩衝區, 數據庫鏈接等等。
UnsupportedOperationException ,是用於代表操做不支持的異常。
在 JDK 類中已被大量運用,在集合框架java.util.Collections.UnmodifiableCollection 將會在全部 add 和 remove 操做中拋出這個異常。
當程序運行時,容許改變程序結構或變量類型,這種語言稱爲動態語言。咱們認爲 Java 並非動態語言,可是它卻又一個很是突出的動態相關的機制
Java 反射機制主要提供瞭如下功能:
斷言,在軟件開發中是一種經常使用的調試方式,不少開發語言中都支持這種機制。
通常來講,斷言用於保證程序最基本、關鍵的正確性。斷言檢查一般在開發和測試時開啓。爲了保證程序的執行效率,在軟件發佈後斷言檢查一般是關閉的。 斷言是一個包含布爾表達式的語句,在執行這個語句時假定該表達式爲true;若是表達式的值爲 false ,那麼系統會報告一個AssertionError 錯誤。斷言的使用以下面的代碼所示:
assert(a > 0); // throws an AssertionError if a <= 0
複製代碼
斷言能夠有兩種形式:
assert Expression1; 。 assert Expression1 : Expression2;
Expression1 應該老是產生一個布爾值。 Expression2 能夠是得出一個值的任意表達式;這個值用於生成顯示更多調試信息的字符串消息。
要在運行時啓用斷言,能夠在啓動 JVM 時使用 -enableassertions 或者 -ea 標記。要在運行時選擇禁用斷言,能夠在啓動 JVM 時使用 -da 或者 -disableassertions 標記。要在系統類中啓用或禁用斷言,可以使用 -esa 或 -dsa 標記。還能夠在包的基礎上啓用或者禁用斷言。
也歡迎關注微信公衆號【Ccww筆記】,原創技術文章第一時間推出
若是此文對你有幫助、喜歡的話,那就點個讚唄,點個關注唄!