Java基礎總結,超級全的面試題

1. static關鍵字是什麼意思?Java 中是否可以覆蓋(override)一個 private 或者是 static 的方法?是否可以在 static 環境中訪問非static 變量?

static關鍵字表明一個成員變量或者是成員方法可以在沒有所屬的類的實例變量的情況下被訪問。
  Java中static方法不能被覆蓋,因爲方法覆蓋(override)是基於運行時動態綁定的,而 static 方法是編譯時靜態綁定的。static 方法跟類的任何實例都不相關,所以概念上不適用。
  不可以在static 環境中訪問非static 變量,static變量在Java中是屬於類的,它在所有的實例中的值是一樣的當類被 Java 虛擬機載入的時候,會對 static 變量進行初始化(與類同生死)。非static變量,只有類被創建時,纔會分配空間(如果是方法內的局部變量,那麼在方法被調用的時候才創建)。

2. Java中接口和抽象類的區別
  • 接口比抽象類更加抽象,因爲抽象類中可以定義構造器,可以有抽象方法和具體方法,而接口中不能定義構造器而且其中的方法全部都是抽象方法。
  • 類可以實現很多個接口,但是隻能繼承一個抽象類。一個類如果繼承了某個抽象類或者實現了某個接口都需要對其中的抽象方法全部進行實現, 否則該類仍然需要被聲明爲抽象類。
  • 抽象類可以在不提供接口方法實現的情況下實現接口。
  • Java 接口中聲明的變量默認都是final的。抽象類可以包含非final的變量。
  • Java 接口中的成員函數默認是 public 的。抽象類的成員函數可以是 private,protected 或者是 public。
  • 接口是絕對抽象的,不可以被實例化。抽象類也不可以被實例化,但是,如果它包含 main方法的話是可以被調用的。(抽象類和接口都不能夠實例化,但可以定義抽象類和接口類型的引用)
  • 接口可以繼承接口。抽象類可以實現(implements)接口,抽象類可繼承具體類,但前提是具體類必須有明確的構造函數
  • 有抽象方法的類必須被聲明爲抽象類,而抽象類未必要有抽象方法。
    接口可以繼承接口,而且支持多重繼承。抽象類可以實現(implements)接口,抽象類可繼承具體類也可以繼承抽象類。
3. 進程和線程
  • 進程是程序的一次動態執行過程,每個進程都有自己獨立的內存空間。一個應用程序可以同時啓動多個進程(比如瀏覽器可以開多個窗口,每個窗口就是一個進程),進程是執行着的應用程序,而線程是進程內部的一個執行序列。一個進程可以有多個線程。線程又叫做輕量級進程。
  • 多進程操作系統能夠運行多個進程,每個進程都能夠循環利用所需要的CPU時間片,使的所有進程看上去像在同時運行一樣。
  • 線程是進程的一個執行流程,是CPU調度和分 派的基本單位。一個進程可以由多個線程組成,也就是一個進程可以同時運行多個不同的線程,每個線程完成不同的任務。
  • 線程的併發運行:就是一個進程內若干個線程同時運行。(比如:word的拼寫檢查功能和首字母自動大寫功能是word進程中的線程)
  • 線程進程的關係是一個局部整體的關係,每個進程都由操作系統分配獨立的內存地址空間,而同一進程的所有線程都在同一地址空間工作。(進程在執行時通常擁有獨立的內存單元,而線程之間可以實現內存共享
  • 雖然使用多線程的編程通常能夠帶來更好的性能和用戶體驗,但是多線程佔用更多的CPU資源,對於其他的進程並不友好(可能佔用了更多的CPU 資源),也不是線程越多, 程序的性能就越好,因爲線程之間的調度和切換也會浪費CPU 時間。在這裏插入圖片描述
4. 線程的生命週期

一個線程的完整生命週期要經歷5中狀態:新建、就緒、運行、阻塞、死亡
在這裏插入圖片描述

  • 新建狀態:使用new和某種線程的構造方法來創建線程對象,該線程就會進入新建狀態,系統爲該線程對象分配內存空間。處於新建狀態的線程可以通過調用start()方法進入就緒狀態。
  • 就緒狀態(Runnable):此時線程已經具備了運行的條件,進入了線程隊列,等待系統分配CPU資源,一旦獲得CPU資源,該線程就會進入運行狀態。(線程準備運行,不一定立馬就能開始執行)
  • 運行狀態(Running):進入運行在狀態,線程會執行自己的run()方法中的代碼。就是進程正在執行線程的代碼。
  • 阻塞狀態(Blocked):一個正在執行的線程,如果執行了suspend、join或sleep方法,或等待io設備的使用權,那麼該線程將會讓出自己的CUP控制權並暫時中止自己的執行,進入阻塞狀態。阻塞的線程,不能夠進入就緒隊列,只有當阻塞原因被消除的時候,線程才能進入就緒狀態,重新進入線程隊列中排隊等待CPU資源,然後繼續執行。
  • 死亡狀態(Dead):一個線程完成了全部工作或者被提前強制性的中止,該線程就處於死亡狀態。
  • 睡眠狀態(Sleeping):線程通過Thread.sleep(線程睡眠)、Object.wait(線程等待)、LockSupport.park(線程暫停)三種方式 使線程進入休眠狀態。
    同步阻塞(Blocked on Synchronization):sleep( ) 使線程在一定的時間內進入阻塞狀態(不會釋放鎖資源)、wait( ) 使線程進入阻塞狀態,(釋放自己佔有的鎖資源,搭配notify( )使用)、suspend( ) 使線程進入阻塞狀態(必須其對應的resume( )被調用,才能使線程重新進入可執行狀態)
5. 線程同步以及線程調度相關的方法

wait():使一個線程處於等待(阻塞)狀態,並且釋放所持有的對象的鎖
sleep():使一個正在運行的線程處於睡眠狀態,是一個靜態方法,調用此方法要處理InterruptedException異常;
notify():喚醒一個處於等待狀態的線程,當然在調用此方法的時候,並不能確切的喚醒某一個等待狀態的線程,而是由JVM 確定喚醒哪個線程,而且與優先級無關;
notityAll():喚醒所有處於等待狀態的線程,該方法並不是將對象的鎖給所有線程,而是讓它們競爭,只有獲得鎖的線程才能進入就緒狀態;

6. Thread類的sleep()方法和對象的wait()方法有什麼區別?線程的sleep()方法和yield()方法有什麼區別?

Thread類的sleep()方法和對象的wait()方法

sleep()*方法(休眠)是*線程類( Thread)*的*靜態方法,調用此方法讓當前線程暫停執行指定的時間, 將執行機會( CPU)讓給其他線程, 但是對象的鎖依然保持,因此休眠時間結束後會自動恢復(線程回到就緒狀態)。
*wait()* 方法是Object 類的方法,調用對象的wait()方法導致當前線程 放棄對象的鎖(線程暫停執行), 進入對象的等待池(wait pool),只有調用對象的notify()方法(或notifyAll()方法)時才能喚醒等待池中的線程進入等鎖池(lock pool), 如果線程重新獲得對象的鎖就可以進入就緒狀態

線程的sleep()方法和yield()方法

1)sleep()方法給其他線程運行機會時不考慮線程的優先級,因此會給低優先級的線程以運行的機會;yield()方法只會給相同優先級或更高優先級的線程以運行的機會;
2)線程執行sleep()方法後轉入阻塞( blocked)狀態, 而執行yield()方法後轉入就緒( ready)狀態;
3)sleep()方法聲明拋出InterruptedException,而yield()方法沒有聲明任何異常;
4)sleep()方法比yield()方法(跟操作系統CPU調度相關)具有更好的可移植性。

7. 什麼是線程池(thread pool)

在面向對象編程中,創建和銷燬對象是很費時間的,因爲創建一個對象要獲取內存資源或者其它更多資源。在Java 中,虛擬機將試圖跟蹤每一個對象,以便能夠在對象銷燬後進行垃圾回收。所以提高服務程序效率的一個手段就是儘可能減少創建和銷燬對象的次數,特別是一些很耗資源的對象創建和銷燬,這就是** 池化資源技術**產生的原因。
線程池顧名思義就是事先創建若干個可執行的線程放入一個池(容器)中,需要的時候從池中獲取線程不用自行創建, 使用完畢不需要銷燬線程而是放回池中,從而減少創建和銷燬線程對象的開銷。
Java 5+中的Executor接口定義一個執行線程的工具。它的子類型即線程池接口是ExecutorService。工具類Executors面提供了一些靜態工廠方法,生成一些常用的線程池。
工具類Executors生成線程池的常用方法:

1)newSingleThreadExecutor:創建一個單線程的線程池。這個線程池只有一個線程在工作,也就是相當於單線程串行執行所有任務。如果這個唯一的線程因爲異常結束,那麼會有一個新的線程來替代它。此線程池保證所有任務的執行順序按照任務的提交順序執行。
2) newFixedThreadPool:創建固定大小的線程池。每次提交一個任務就創建一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,如果某個線程因爲執行異常而結束,那麼線程池會補充一個新線程(返回到線程池中)。【推薦使用】
3)newCachedThreadPool:創建一個可緩存的線程池。如果線程池的大小超過了處理任務所需要的線程,那麼就會回收部分空閒(60 秒不執行任務)的線程,當任務數增加時,此線程池又可以智能的添加新線程來處理任務。此線程池不會對線程池大小做限制,線程池大小完全依賴於操作系統(或者說JVM)能夠創建的最大線程大小。
4)newScheduledThreadPool:創建一個大小無限的線程池。此線程池支持定時以及週期性執行任務的需求。
5)newSingleThreadExecutor:創建一個單線程的線程池。此線程池支持定時以及週期性執行任務的需求

如果希望在服務器上使用線程池,強烈建議使用newFixedThreadPool方法來創建線程池,這樣能獲得更好的性能。

8. Java中守護線程和本地線程區別

java 中的線程分爲兩種:守護線程( Daemon)和用戶線程( User)。
  j任何線程都可以設置爲守護線程和用戶線程,通過方法Thread.setDaemon(boolon);true則把該線程設置爲守護線程,false則設置爲用戶線程Thread.setDaemon()
必須在Thread.start()之前調用,否則運行時會拋出異常。

守護線程和本地線程兩者的區別

唯一的區別是判斷虛擬機(JVM)何時離開,守護線程Daemon是爲其他線程提供服務,如果全部的用戶現場Thread 已經撤離, Daemon 沒有可服務的線程,JVM 撤離。也可
以理解爲守護線程是JVM 自動創建的線程( 但不一定),用戶線程是程序創建的線程;比如JVM 的垃圾回收線程是一個守護線程,當所有線程已經撤離,不再產生垃圾,守護線程自然就沒事可幹了,當垃圾回收線程是Java 虛擬機上僅剩的線
程時,Java 虛擬機會自動離開。

9. synchronized關鍵字的用法,當一個線程進入一個對象的synchronized方法A之後,其它線程是否可進入此對象的synchronized方法B?

1)synchronized 關鍵字可以將對象或者方法標記爲同步,以實現對對象和方法的互斥訪問,可以用synchronized(對象) { }定義同步代碼塊,或者在聲明方法時將synchronized作爲方法的修飾符
2)不能進入。
  其它線程只能訪問該對象的非同步方法,同步方法則不能進入。因爲非靜態方法上的synchronized修飾符要求執行方法時要獲得對象的鎖,如果已經進入A方法說明對象鎖已經被取走,那麼試圖進入B 方法的線程就只能在等鎖池( 注意不是等待池)中等待對象的鎖。

10. 簡述synchronized和java.util.concurrent.locks.Lock的異同

Lock是JDK1.5 以後引入的新的API,和關鍵字synchronized 的異同:
相同點:Lock 能完成synchronized 所實現的所有功能;
主要不同點:Lock有比synchronized 更精確的線程語義和更好的性能,而且不強制性的要求一定要獲得鎖。synchronized會自動釋放鎖,而Lock一定要求程序員手工釋放,並且最好在finally 塊中釋放(這是釋放外部資源的最好的地方)。

11. 死鎖、活鎖、飢餓?產生死鎖的必要條件是什麼?如何確保 N 個線程可以訪問N 個資源同時又不導致死鎖?
  1. 死鎖、活鎖、飢餓 的概念,之間的區別

1)死鎖:兩個進程都在等待對方執行完畢才能繼續往下執行的時候就發生了死鎖。結果就是兩個進程都陷入了無限的等待中。因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去
2)活鎖:任務或者執行者沒有被阻塞,由於某些條件沒有滿足,導致一直重複嘗試,失敗,嘗試, 失敗。
3)飢餓:一個或者多個線程因爲種種原因無法獲得所需要的資源,導致一直無法執行的狀態。

活鎖和死鎖的區別
  處於活鎖的實體是在不斷的改變狀態,所謂的“ 活”, 而
處於死鎖的實體表現爲等待;活鎖有可能自行解開,死鎖則不能。

Java 中導致飢餓的原因
1)高優先級線程吞噬所有的低優先級線程的CPU 時間。
2)線程被永久堵塞在一個等待進入同步塊的狀態,因爲其他線程總是能在它之前
持續地對該同步塊進行訪問。
3)線程在等待一個本身也處於永久等待完成的對象(比如調用這個對象的wait 方
法),因爲其他線程總是被持續地獲得喚醒。

  1. 產生死鎖的必要條件:

1)互斥條件:所謂互斥就是進程在某一時間內獨佔資源。
2)
請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
3)不剝奪條件:進程已獲得資源,在末使用完之前,不能強行剝奪。
4)循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關係。

  1. 如何確保 N 個線程可以訪問N 個資源同時又不導致死鎖

使用多線程的時候,一種非常簡單的避免死鎖的方式就是:指定獲取鎖的順序,並強制線程按照指定的順序獲取鎖。因此,如果所有的線程都是以同樣的順序加鎖和釋放鎖,就不會出現死鎖了。

多線程的上下文

多線程會共同使用一組計算機上的CPU,而線程數大於給程序分配的CPU 數量時,爲了讓各個線程都有執行的機會,就需要輪轉使用CPU。不同的線程切換使用CPU發生的切換數據等就是上下文切換。

12. 內存中的棧(stack)、堆(heap)和方法區(method area)的用法
  • 堆:存放方法對象(通過new 關鍵字和構造器創建的對象)
  • 棧:存放方法以及方法中的局部變量
  • 方法區:(也叫共享區)存放代碼片段、靜態屬性、常量池(比如常量池中存放字符串的值)

通常我們定義一個基本數據類型的變量,一個對象的引用,函數調用的現場保存都使用JVM 中的棧空間;而通過new 關鍵字和構造器創建的對象則放在堆空間堆是垃圾收集器管理的主要區域,由於現在的垃圾收集器都採用分代收集算法,所以堆空間還可以細分爲新生代和老生代,再具體一點可以分爲Eden、Survivor(又可分爲From Survivor 和To Survivor)、Tenured;方法區和堆都
  是各個線程共享的內存區域,用於存儲已經被JVM 加載的類信息、常量、靜態變
量、JIT 編譯器編譯後的代碼等數據;程序中的字面量(literal)如直接書寫的100、”
hello” 和常量都是放在常量池中, 常量池是方法區的一部分。棧空間操作起來
最快但是棧很小,通常大量的對象都是放在堆空間,棧和堆的大小都可以通過JVM
的啓動參數來進行調整,棧空間用光了會引發StackOverflowError,而堆和常量
池空間不足則會引發OutOfMemoryError

String str = new String("HelloWorld");,這個代碼中,變量str 存放在棧上,用new 創建出來的字符串對象放在堆上,”HelloWorld” 這個字面量(String)是放在方法區中。

13. switch 的參數類型

JDK1.5 以前,switch()的參數中,只能是byte、short、char、int
從JDK1.5 開始, Java 中引入了枚舉類型與byte short char int的包裝類
從JDK 1.7 開始,參數還可以是字符串( String)類型,但是長整型( long)在目前所有的版本中都是不可以的。
也就是現在switch支持byte、short、char、int、String、枚舉

JDK1.5,對四個包裝類的支持是因爲java編譯器在底層手動進行拆箱,而對枚舉類的支持是因爲枚舉類有一個ordinal方法,該方法實際上是一個int類型的數值。
jdk1.7開始支持String類型,但實際上String類型有一個hashCode算法,結果也是int類型.而byte short char類型可以在不損失精度的情況下向上轉型成int類型,所以總的來說,可以認爲switch中只支持int.

14. 兩個對象值相同(x.equals(y) == true),但卻可有不同的hash code,這句話對不對?【***】

不對,如果兩個對象x 和y 滿足x.equals(y) == true,它們的哈希碼(hash code)
應當相同。
Java 對於eqauls 方法和hashCode 方法是這樣規定的:

1)如果兩個對象相同(equals 方法返回true),那麼它們的hashCode 值一定要相同
2)如果兩個對象的hashCode 相同,它們並不一定相同。當然,你未必要按照要求去做,但是(重寫hashCode)如果你違背了上述原則就會發現在使用容器時,相同的對象可以出現在Set 集合中,同時增加新元素的效率會大大下降(對於使用哈希存儲的系統,如果哈希碼頻繁的衝突將會造成存取性能急劇下降)。

關於equals方法,有以下內容必須滿足

首先equals 方法必須滿足
1)自反性( x.equals(x)必須返回true)
2)對稱性x.equals(y)返回true 時, y.equals(x)也必須返回true
3)傳遞性(x.equals(y)和y.equals(z)都返回true 時, x.equals(z)也必須返回true
4)一致性(當x和y引用的對象信息沒有被修改時,多次調用x.equals(y)應該得到同樣的返回值) ,而且對於任何非null值的引用x,x.equals(null)必須返回false

實現高質量的equals 方法的訣竅包括

1)使用==操作符檢查”參數是否爲這個對象的引用”;
2)使用instanceof 操作符檢查”參數是否爲正確的類型”;
3) 對於類中的關鍵屬性,檢查參數傳入對象的屬性是否與之相匹配;
4)重寫equals方法後,問自己它是否滿足自反性、對稱性、傳遞性、一致性;
5) 重寫equals 時總是要重寫hashCode;
6) 不要將equals 方法參數中的Object 對象替換爲其他的類型,在重寫時不要忘掉@Override 註解。

15. String 和StringBuilder、StringBuffer 的區別【***】

Java平臺提供了兩種類型的字符串: String 和StringBuffer/StringBuilder,它們可以儲存和操作字符串。
其中String 是隻讀字符串,也就意味着String 引用的字符串內容是不能被改變的
而StringBuffer/StringBuilder類表示的字符串對象可以直接進行修改。StringBuilder 是JDK 1.5 中引入的,它和StringBuffer 的方法完全相同, 區別在於StringBuffer因爲被synchronized 修飾,是線程安全的StringBuilder是線程不安全的(所有方面都沒有被synchronized 修飾)
因爲StringBuilder沒有被synchronized同步修飾,所以StringBuilder

【面試題】什麼情況下用+運算符進行字符串連接比調用StringBuffer/StringBuilder 對象的append 方法連接字符串性能更好?

首先要清楚以下兩點:
1)String 對象的intern方法會得到字符串對象在常量池中對應的版本的引用(如果常量池中有一個字符串與String 對象的equals結果是true),如果常量池中沒有對應的字符串,則該字符串將被添加到常量池中, 然後返回常量池中字符串的引用;
2)字符串的+操作其本質是創建了StringBuilder對象進行append操作,然後將拼接後的StringBuilder 對象用toString方法處理成String 對象

16. 重載(Overload)和重寫(Override)的區別。重載的方法能否根據返回類型進行區分?

方法的重載和重寫都是實現多態的方式,區別在於重載實現的是編譯時的多態性,而重寫實現的是運行時的多態性
  所謂的重載是編譯時多態性,就是根據實際的參數列表,在編譯的時候就能夠確定執行重載方法中的哪一個了。
  所謂的重寫是運行時多態性,就比如父類對象引用子類實例時,調用方法只有在運行時才知道到底執行了哪個方法。
重載發生在一個類中同名的方法如果有不同的參數列表( 參數類型不同、參數個數不同或者二者都不同)則視爲重載;(重載對返回類型沒有特殊的要求,只考慮參數列表)
重寫發生在子類與父類之間(需要繼承),重寫要求子類被重寫方法與父類被重寫方法有相同的返回類型,比父類被重寫方法更好訪問,不能比父類被重寫方法聲明更多的異常( 里氏代換原則)。

17. JVM是什麼?爲什麼 Java 被稱作是“平臺無關的編程語言”?JVM 加載class 文件的原理機制【***】

Java 虛擬機是一個可以執行 Java 字節碼的虛擬機進程。Java 源文件被編譯成能被 Java 虛擬機執行的字節碼文件。

Java 被設計成允許應用程序可以運行在任意的平臺,而不需要程序員爲每一個平臺單獨重寫或者是重新編譯。Java 虛擬機讓這個變爲可能,因爲它知道底層硬件平臺的指令長度和其他特性。

Java代碼在JVM中的執行流程
在這裏插入圖片描述
JVM的類加載原理圖
在這裏插入圖片描述
JVM 中類的裝載是由類加載器( ClassLoader)和它的子類來實現的, Java 中的類加載器是一個重要的Java 運行時系統組件,它負責在運行時查找和裝入類文件中的類。
由於Java 的跨平臺性,經過編譯的Java 源程序並不是一個可執行程序,而是一個或多個類class文件。當Java 程序需要使用某個類時,JVM 會確保這個類已經被加載、連接( 驗證、準備和解析)和初始化。類的加載是指把類的.class 文件中的數據讀入到內存中,通常是創建一個字節數組讀入.class 文件,然後產生與所加載類對應的Class 對象。加載完成後,Class 對象還不完整,所以此時的類還不可用。當類被加載後就進入連接階段,這一階段包括驗證、準備( 爲靜態變量分配內存並設置默認的初始值)和解析( 將符號引用替換爲直接引用)三個步驟。最後JVM 對類進行初始化,包括:
1)如果類存在直接的父類並且這個類還沒有被初始化,那麼就先初始化父類;
2)如果類中存在初始化語句, 就依次執行這些初始化語句。

類的加載是由類加載器完成的
類加載器包括:根加載器(BootStrap)、擴展加載器(Extension)、系統加載器( System)和用戶自定義類加載器(java.lang.ClassLoader 的子類)
從Java 2( JDK 1.2)開始, 類加載過程採取了父親委託機制(PDM)。PDM 更好的保證了Java 平臺的安全性,在該機制中, JVM 自帶的Bootstrap 是根加載器, 其他的加載器都有且僅有一個父類加載器。類的加載首先請求父類加載器加載,父類加載器無能爲力時才由其子類加載器自行加載。JVM 不會向Java 程序提供對Bootstrap 的引用。

  • Bootstrap:一般用本地代碼實現,負責加載JVM 基礎核心類庫(rt.jar);
  • Extension:從java.ext.dirs 系統屬性所指定的目錄中加載類庫,它的父加載器是Bootstrap;
  • System:又叫應用類加載器,其父類是Extension。它是應用最廣泛的類加載器。它從環境變量classpath 或者系統屬性java.class.path 所指定的目錄中記載類,是用戶自定義加載器的默認父加載器。
18. Java 中會存在內存泄漏嗎?

理論上,Java因爲有垃圾回收機制( GC)不會存在內存泄露問題( 這也是Java 被廣泛使用於服務器端編程的一個重要原因);
然而在實際開發中,可能會存在無用但可達的對象,這些對象不能被GC 回收,因此也會導致內存泄露的發生。
例如:Hibernate 的Session( 一級緩存)中的對象屬於持久態,垃圾回收器是不會回收這些對象的,然而這些對象中可能存在無用的垃圾對象,如果不及時關閉(close)或清空( flush)一級緩存就可能導致內存泄露。

//實現了一個棧(先進後出(FILO))結構代碼
import java.util.Arrays;
import java.util.EmptyStackException;

public class MyStack<T> {
	private T[] elements;
	private int size = 0;
	private static final int INIT_CAPACITY = 16;
	
	public MyStack() {
		elements = (T[]) new Object[INIT_CAPACITY];
	}
	
	public void push(T elem) {
		ensureCapacity();
		elements[size++] = elem;
	}
	
	public T pop() {
		if(size == 0)
			throw new EmptyStackException();
			return elements[--size];
		}

	private void ensureCapacity() {
		if(elements.length == size) {
			elements = Arrays.copyOf(elements, 2 * size + 1);
		}
	}
}
123456789101112131415161718192021222324252627282930

上述代碼是實現了一個棧的先進後出(FILO)結構,但是其中的pop 方法卻存在
內存泄露的問題

當我們用pop 方法彈出棧中的對象時,該對象不會被當作垃圾回收,即使使用棧的程序不再引用這些對象, 因爲棧內部維護着對這些對象的過期引用(obsolete reference)。
在支持垃圾回收的語言中,內存泄露是很隱蔽的,這種內存泄露其實就是無意識的對象保持。如果一個對象引用被無意識的保留起來了,那麼垃圾回收器不會處理這個對象,也不會處理該對象引用的其他對象,即使這樣的對象只有少數幾個,也可能會導致很多的對象被排除在垃圾回收之外,從而對性能造成重大影響,極端情況下會引發Disk Paging( 物理內存與硬盤的虛擬內存交換數據),甚至造成OutOfMemoryError。

19. 抽象方法(abstract)是否可以被靜態的(static)、本地方法(native)和同步(synchronized)修飾。

答案是都不能。
1)抽象方法需要子類重寫,而靜態的方法是無法被重寫的,因此二者是矛盾的。
2)本地方法是由本地代碼(如C代碼)實現的方法,而抽象方法是沒有實現的,也是矛盾的。
3)synchronized 和方法的實現細節有關,抽象方法不涉及實現細節, 因此也是相互矛盾的。

20. 靜態變量和實例變量的區別

靜態變量是被static修飾符修飾的變量,也稱爲類變量, 它屬於類(與類同生死),不屬於類的任何一個對象,一個類不管創建多少個對象, 靜態變量在內存中有且僅有一個拷貝;靜態變量可以實現讓多個對象共享內存。
實例變量必須依存於某一實例,需要先創建對象然後通過對象才能訪問到它。
(在Java 開發中, 上下文類和工具類中通常會有大量的靜態成員。)

21. 實現對象克隆的2種方法【***】

對象是引用數據類型,對象的賦值僅僅是吧對象的引用賦值給另一個對象,他們的堆空間是相同的。
克隆就是需要有一個完全相同的對象,而且兩者之間完全互不影響,克隆對象和原對象是兩個完全對立的對象。(java.lang.Object類的clone()方法,對象克隆就是對象的複製,即完整的複製出一個對象。)

1)實現Cloneable接口並重寫Object 類中的clone()方法;(淺克隆對象的克隆默認是淺度克隆,也就是包含在對象內部的對象是不會被克隆的),【通過clone()實現深度克隆:讓克隆對象所包含的類也實現clone功能(實現cloneable接口,重載clone()方法)】
2)實現Serializable接口,通過對象的序列化和反序列化實現克隆,可以實現真正的深度克隆

基於序列化和反序列化(Serializable)實現的克隆不僅僅是深度克隆, 更重要的是通過泛型限定,可以檢查出要克隆的對象是否支持序列化,這項檢查是編譯器完成的,不是在運行時拋出異常,這種是方案明顯優於使用Object 類的clone 方法克隆對象。讓問題在編譯的時候暴露出來總是好過把問題留到運行時。

22. Java 中如何實現序列化Serializable,有什麼意義?

Serializable序列化就是一種用來處理對象流的機制,所謂對象流也就是將對象的內容進行流化。可以對流化後的對象進行讀寫操作,也可將流化後的對象傳輸於網絡之間。
序列化是爲了解決對象流讀寫操作時可能引發的問題( 如果不進行序列化可能會存在數據亂序的問題) 。
要實現序列化,需要讓一個類實現Serializable接口,該接口是一個標識性接口,標註該類對象是可被序列化的,然後使用一個輸出流來構造一個對象輸出流並通過writeObject(Object)方法就可以將實現對象寫出(即保存其狀態);
如果需要反序列化則可以用一個輸入流建立對象輸入流,然後通過readObject 方法從流中讀取對象。序列化除了能夠實現對象的持久化之外,還能夠用於對象的深度克隆(上面所說的)。

23. GC垃圾回收器,爲什麼需要它。【***】

java中由JVM的垃圾回收管理器(GC)來負責回收不需要再使用的堆內存和棧內存。GC 功能可以自動監測對象是否超過作用域從而達到自動回收內存的目的。
JVM的垃圾回收採用動態存儲管理技術,它能夠自己檢測內存的使用情況自動地釋放不再被程序引用的對象,按照特定的垃圾收集算法來實現內存資源自動回收功能。
JVM垃圾回收管理器處理釋放沒用的對象外,還可以清除內存記錄的碎片(因爲創建對象和垃圾收集齊全不斷釋放對其對象所佔用的空間,因此內存會出現碎片)。碎片整理將所佔用的堆內存移動到堆的一端,JVM將整理出的內存分配給新的對象。
JVM垃圾回收器有一個潛在的缺點 : 增加了系統的開銷,影響了程序的性能。因爲JVM必須追蹤運行程序中有用的對象,到最終釋放沒用的對象,這個過程需要花費處理器的時間。
一個對象如果不再被任何棧內存所引用,則該對象成爲垃圾對象。垃圾收集器對垃圾對象的收集時間是不確定的,也可以直接使用System.gc()方法Runtime.getRuntime().gc()回收垃圾對象,但是這種強制執行垃圾回收程序對系統性能會產生負面影響。(JVM 可以屏蔽掉顯示的垃圾回收調用,就是顯示調用方法,JVM的GC會自動進行管理)雖然你可以調用System.gc() 或者Runtime.gc(),但是沒有辦法保證GC的執行。

垃圾回收可以有效的防止內存泄露, 有效的使用可以使用的內存。垃圾回收器通常是作爲一個單獨的低優先級的線程運行,不可預知的情況下對內存堆中已經死亡的或者長時間沒有使用的對象進行清除和回收,程序員不能實時的調用垃圾回
收器對某個對象或所有對象進行垃圾回收。在Java 誕生初期,垃圾回收是Java
最大的亮點之一,因爲服務器端的編程需要有效的防止內存泄露問題,然而時過
境遷,如今Java 的垃圾回收機制已經成爲被詬病的東西。移動智能終端用戶通常
覺得iOS 的系統比Android 系統有更好的用戶體驗,其中一個深層次的原因就在
於Android 系統中垃圾回收的不可預知性。

補充:垃圾回收機制有很多種,包括:分代複製垃圾回收、標記垃圾回收、增量
垃圾回收等方式。標準的Java 進程既有棧又有堆。棧保存了原始型局部變量,堆
保存了要創建的對象。Java 平臺對堆內存回收和再利用的基本算法被稱爲標記和
清除,但是Java 對其進行了改進,採用“ 分代式垃圾收集”。這種方法會跟Java
對象的生命週期將堆內存劃分爲不同的區域, 在垃圾收集過程中,可能會將對象
移動到不同區域:

24. String和基本數據類型之間的轉換

1)將字符串轉換爲基本數據類型
  調用基本數據類型對應的包裝類中的方法parseXXX(String)或valueOf(String)即可返回相應基本類型;
2)將基本數據類型轉換爲字符串
  一種方法是將基本數據類型與空字符串 "" 通過 + 連接即可獲得其所對應的字符串;
  另一種方法是調用String 類中的valueOf()方法返回相應字符串。String.valueOf()

25. Java和JavaScript區別

Java和JavaScript介紹

JavaScript 與Java 是兩個公司開發的不同的兩個產品。Java 是原Sun Microsystems 公司推出的面向對象的程序設計語言,特別適合於互聯網應用程序開發;而JavaScript 是Netscape 公司的產品,爲了擴展Netscape瀏覽器的功能而開發的一種可以嵌入Web 頁面中運行的基於對象和事件驅動的解釋性語言
JavaScript 的前身是LiveScript;而Java 的前身是Oak 語言。

Java和JavaScript的區別

1)Java是面向對象的而JavaScript是基於對象的
  Java 是一種真正的面向對象的語言,即使是開發簡單的程序,必須設計對象;JavaScript 是種腳本語言,它可以用來製作與網絡無關的,與用戶交互作用的複雜軟件。它是一種基於對象(Object-Based)和事件驅動(Event-Driven)的編程語言,因而它本身提供了非常豐富的內部對象供設計人員使用。
2)Java是編譯運行,而JavaScript是解釋執行的
  Java 的源代碼在執行之前,必須經過編譯。JavaScript 是一種解釋性編程語言,其源代碼不需經過編譯,由瀏覽器解釋執行。(目前的瀏覽器幾乎都使用了JIT(即時編譯)技術來提升JavaScript 的運行效率
3)Java變量必須先聲明後使用,JavaScript是弱類型語言,直接使用
  Java採用強類型變量檢查,即所有變量在編譯之前必須作聲明;JavaScript 中變量是弱類型的,甚至在使用變量前可以不作聲明,JavaScript 的解釋器在運行時檢查推斷其數據類型。
4)Java是靜態語言,JavaScript是動態語言

26. try{}裏有一個return 語句,finally是否執行,執行位置。

try{}裏有一個return語句返回,那麼緊跟在這個try後的finally{}裏的代碼也是會被執行的,是在方法返回調用前(return前執行)
如果存在finally代碼塊,try中的return語句不會立馬返回調用者,而是記錄下返回值待finally代碼塊執行完畢之後再向調用者返回其值(執行return)。
在finally中改變返回值的做法是不好的,如果在finally 中修改了返回值,就會返回修改後的值。在finally 中返回或者修改返回值會對程序造成很大的困擾,C#中直接用編譯錯誤的方式來阻止程序員幹這種齷齪的事情,Java 中也可以通過提升編譯器的語法檢查級別來產生警告或錯誤(idea、eclipse也能自己設置)

27. 運行時異常和受檢查異常

異常表示程序運行過程中可能出現的非正常狀態。
運行時異常:表示虛擬機的通常操作中可能遇到的異常,是一種常見運行錯誤,只要程序設計得沒有問題通常就不會發生。(在運行是拋出)
受檢查異常跟程序運行的上下文環境有關,即使程序設計無誤, 仍然可能因使用的問題而引發。Java 編譯器要求方法必須聲明拋出可能發生的受檢查異常,但是並不要求必須聲明拋出未被捕獲的運行時異常。
常見的運行時異常:

ArithmeticException(算術異常)
ClassCastException (類轉換異常)
IllegalArgumentException (非法參數異常)
IndexOutOfBoundsException (下標越界異常)
NullPointerException (空指針異常)
SecurityException (安全異常)

28. final、finally、finalize 的區別

final關鍵字

一個基本數據類型聲明爲final,就只能進行一次賦值(初始化),編譯器會自動檢查代碼,如果需要修改final的初始化,就會編譯報錯。final聲明的引用數據類型,當前引用是不能改變的,但是可以改變引用指向的內存空間的值。final一般和static搭配使用作爲常量。
final關鍵字的三種用法:
1)修飾類:表示該類不能被繼承;
2)修飾方法:表示方法不能被重寫;
3)修飾變量: 表示變量只能一次賦值以後值不能被修改(常量)(final修飾成員變量必須在聲明時初始化或者再構造器中初始化,否則報編譯錯誤,而且final修飾的變量通常都是常量,常量名全部大寫)
  如果一個類被聲明爲final,意味着它不能再派生出新的子類,即不能被繼承,因此它和abstract 是反義詞。將變量聲明爲final,可以保證它們在使用中不被改變,被聲明爲final 的變量必須在聲明時給定初值,而在以後的引用中只能讀取不可修改。被聲明爲final 的方法也同樣只能使用,不能在子類中被重寫。

final優勢
1)final關鍵字可以提高性能,JVM和Java應用都會緩存final變量。
2)final變量可以在安全的多線程環境下進行資源共享,而不需要額外的同步開銷

finally

通常放在try…catch…的後面構造總是執行代碼塊,這就意味着程序無論正常執行還是發生異常,這裏的代碼只要JVM 不關閉都能執行,可以將釋放外部資源的代碼寫在finally 塊中(關閉數據庫連接、釋放資源)。

finalize

Object 類中定義的方法,Java 中允許使用finalize()方法在垃圾收集器將對象從內存中清除出去之前做必要的清理工作。這個方法是由垃圾收集器在銷燬對象時調用的,通過重寫finalize()方法可以整理系統資源或者執行其他清理工作。

29. List、Map、Set 三個接口存取元素時,各有什麼特點?

List以特定索引來存取元素,可以有重複元素
Set 不能存放重複元素(用對象的equals()*方法來區分元素是否重複)。
**Map**保存*
鍵值對( key-value)映射
映射關係可以是一對一或多對一。(Map不支持一對多,但是可以用Map<Integer, List>這種格式來達到一對多的系,多對多類似)

Set和Map容器都有基於哈希存儲和排序樹的兩種實現版本,基於哈希存儲的版本理論存取時間複雜度爲O(1),而基於排序樹版本的實現在插入或刪除元素時會按照元素或元素的鍵( key)構成排序樹從而達到排序和去重的效果。

30. ArrayList、Vector、LinkedList 的存儲性能和特性【***】

ArrayList和Vector都是使用數組方式存儲數據,此數組元素數大於實際存儲的數據以便增加和插入元素,它們都允許直接按序號索引元素,但是插入元素要涉及數組元素移動等內存操作,所以索引查詢數據快而插入數據慢,
Vector 中的方法由於添加了synchronized 修飾,因此Vector 是線程安全的容器,但性能上較ArrayList 差,因此已經是Java 中的遺留容器
LinkedList 使用雙向鏈表實現存儲( 將內存中零散的內存單元通過附加的引用關聯起來,形成一個可以按序號索引的線性結構,這種鏈式存儲方式與數組的連續存儲方式相比, 內存的利用率更高) ,按序號索引數據需要進行前向或後向遍歷,但是插入數據時只需要記錄本項的前後項即可,所以插入速度較快
Vector 屬於遺留容器(Java 早期的版本中提供的容器, 除此之外,Hashtable、Dictionary、BitSet、Stack、Properties都是遺留容器),已經不推薦使用, 但是由於ArrayList和LinkedListed都是非線程安全的, 如果遇到多個線程操作同一個容器的場景,則可以通過工具Collections 中的synchronizedList 方法將其轉換成線程安全的容器後再使用( 這是對裝潢模式的應用, 將已有對象傳入另一個類的構造器中創建新的對象來增強實現)。

31. Collection 和Collections 的區別,Collections 工具類中的sort()方法如何比較元素,TreeMap和TreeSet 在排序時如何比較元素

Collection是一個接口, 它是Set、List 等容器的父接口。
Collections是個一個工具類,提供了一系列的靜態方法來輔助容器操作,這些方法包括對容器的搜索、排序、線程安全化等等。

Collections工具類的sort方法有兩種重載的形式,第一種要求傳入
的待排序容器中存放的對象比較實現Comparable接口以實現元素的比較;第二種不強制性的要求容器中的元素必須可比較, 但是要求傳入第二個參數, 參數是Comparator接口的子類型(需要重寫compare 方法實現元素的比較),相當於一個臨時定義的排序規則,其實就是通過接口注入比較元素大小的算法, 也是對回調模式的應用( Java 中對函數式編程的支持)。

TreeSet要求存放的對象所屬的類必須實現Comparable接口,該接口提供了比較元素的compareTo()方法,當插入元素時會回調該方法比較元素的大小。
TreeMap要求存放的鍵值對映射的鍵必須實現Comparable 接口從而根據鍵對元素進行排序。

32. XML 文檔定義有幾種形式?它們之間有何本質區別?解析XML 文檔有哪幾種方式?

1)XML文檔定義分爲DTD和Schema兩種形式,二者都是對XML語法的約束。
2)DTD和Schema兩種形式的本質區別在於Schema本身也是一個XML文件,可以被XML 解析器解析,而且可以爲XML 承載的數據定義類型,約束能力較之DTD更強大。
3)對XML的解析主要有DOM(文檔對象模型,Document Object Model)、SAX( Simple API forXML)和StAX(JDK1.6 中引入的新的解析XML的方式,Streaming API for XML)。

其中DOM處理大型文件時其性能下降的非常厲害,這個問題是由DOM 樹結構佔用的內存較多造成的,而且DOM 解析方式必須在解析文件之前把整個文檔裝入內存,適合對XML 的隨機訪問( 典型的用空間換取時間的策略);
SAX是事件驅動型的XML解析方式,它順序讀取XML 文件,不需要一次全部裝載整個文件。當遇到像文件開頭,文檔結束,或者標籤開頭與標籤結束時,它會觸發一個事件,用戶通過事件回調代碼來處理XML文件,適合對XML 的順序訪問;顧名思義,
StAX 把重點放在流上,實際上StAX與其他解析方式的本質區別就在於應用程序能夠把XML作爲一個事件流來處理。SAX 也是這樣做的,但不同之處在於StAX 允許應用程序代碼把這些事件逐個拉出來,而不用提供在解析器方便時從解析器中接收事件的處理程序。

33. XML的主要作用

XML的主要作用有兩個方面:數據交換和信息配置
在做數據交換時,XML將數據用標籤組裝成起來, 然後壓縮打包加密後通過網絡傳送給接收者,接收解密與解壓縮後再從XML文件中還原相關信息進行處理,XML曾經是異構系統間交換數據的事實標準,但此項功能幾乎已經被JSON( JavaScript Object Notation)取而代之。當然,目前很多軟件仍然使用XML 來存儲配置信息,我們在很多項目中
通常也會將作爲配置信息的硬代碼寫在XML 文件中,Java 的很多框架也是這麼做的, 而且這些框架都選擇了dom4j 作爲處理XML 的工具,(因爲Sun 公司的官方API 實在不怎麼好用。)

34. JDBC操作數據庫的步驟(MySQL)
//1. 加載JDBC驅動程序(加載MySQL驅動類)
Class.forName("com.mysql.jdbc.Driver");
//2. 提供JDBC連接的URL來創建連接
	//databaseName數據庫名稱,useUnicode=true:表示使用Unicode字符集,characterEncoding=UF-8字符編碼方式utf-8, udrtnsmr和password是mysql連接用戶名和密碼
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/databaseName?useUnicode=true&characterEncoding=UF-8;",username,password);
	// 也可以將mysql驅動寫到DriverManager.getConnection()中

//3. PreparedStatement預編譯sql
String sql = "select * from dept where id =  ? and name = ?";
PreparedStatement ps = con.prepareStatement(sql);
ps.setInt(1, 10);
ps.setInt(2, "研究員");
//4. 執行SQL語句
ResultSet rs = ps.executeQuery();
//5. 處理結果
while(rs.next()) {
	System.out.println(rs.getInt("id"));
	System.out.println(rs.getInt("name"));
}
//6. 在finally裏面進行釋放資源(關閉外部資源的順序應該和打開的順序相反)
if(rs !=null){
	rs.close;
}
if(ps != null){
	ps.close();
}
if(con != null) {
	con.close();
}
1234567891011121314151617181920212223242526272829
35. Statement和PreparedStatement哪個性能更好?

PreparedStatement性能更好
1)PreparedStatement接口代表預編譯的語句,它主要的優勢在於可以減少SQL 的編譯錯誤並增加SQL的安全性(減少SQL 注射攻擊的可能性)
2) PreparedStatement 中的SQL 語句是可以帶參數的,避免了用字符串連接拼接SQL 語句的麻煩和不安全;
3)當批量處理SQL 或頻繁執行相同的查詢時,PreparedStatement 有明顯的性能上的優勢,由於數據庫可以將編譯優化後的SQL 語句緩存起來,下次執行相同結構的語句時就會很快(不用再次編譯和生成執行計劃)。

36. JDBC 能否處理Blob 和Clob

Blob是指二進制大對象(Binary Large Object)
Clob是指大字符對象(Character Large Objec)
Blob是爲存儲大的二進制數據而設計的,而Clob 是爲存儲大的文本數據而設計的。JDBC 的PreparedStatement和
ResultSet都提供了相應的方法來支持Blob和Clob操作(流的操作)。

37. 數據庫編程時,連接池的優勢

由於創建連接和釋放連接都有很大的開銷(尤其是數據庫服務器不在本地時,每次建立連接都需要進行TCP的三次握手,釋放連接需要進行四次揮手,造成很大的開銷),爲了提升系統訪問數據庫的性能,可以事先創建若干連接置於連接池中,需要時直接從連接池獲取, 使用結束時歸還連接池而不必關閉連接,從而避免頻繁創建和釋放連接所造成的開銷,這是典型的用空間換時間的策略(浪費了空間存儲連接,但節省了創建和釋放連接的時間)。
池化技術在Java 開發中是很常見的,在使用線程時創建線程池的道理與此相同。基於Java 的開源數據庫連接池主要有: C3P0、Proxool、DBCP、BoneCP、Druid等。
其實大型網站性能優化的一個關鍵就是使用緩存,,而緩
存和連接池非常類似, 也是使用空間換時間的策略。可以將熱點數據置於緩存中,當用戶查詢這些數據時可以直接從緩存中得到, 避免頻繁的訪問數據庫造成大量的開銷(現在主要用Redis實現緩存)

38. Java分層,Dao模式是什麼?

DAO( Data Access Object)是一個爲數據庫或其他持久化機制提供了抽象接口的對象,在不暴露底層持久化方案實現細節的前提下提供了各種數據訪問操作。在實際的開發中,應該將所有對數據源的訪問操作進行抽象化後封裝在一個公共API 中。
用程序設計語言來說, 就是建立一個接口,接口中定義了此應用程序中將會用到的所有事務方法。在這個應用程序中,當需要和數據源進行交互的時候則使用這個接口,並且編寫一個單獨的類來實現這個接口,在邏輯上該類對應一個特定的數據存儲。DAO 模式實際上包含了兩個模式,一是Data Accessor(數據訪問器),二是Data Object(數據對象),前者要解決如何訪問數據的問題,而後者要解決的是如何用對象封裝數據。

39. 事務的特性ACID,事務隔離級別,事務的併發問題【****】
  1. 事務具有ACID四個特性:

1)原子性(Atomicity):事務是一個不可分割的工作單位,事務中的操作要麼都發生,要麼都不發生
2)一致性(Consistency):事務在完成後數據的完整性必須保持一致
3)隔離性(Isolation):多個用戶併發訪問數據庫時,一個用戶的事務不能被其他用戶的事務所幹擾,多個併發事務之間的數據要相互隔離
4)持久性(Durability):一個事務一旦被提交,它對數據庫中數據的改變應該是永久性的,即使數據庫發生故障也不應該對其有任何影響

如果整個事務執行過程中,有任何一個地方出現異常/錯誤,那麼都會進行事務回滾,回滾之後數據的狀態將和事務執行之前完全一致。

  1. 事務的隔離級別:

數據庫爲用戶提供了自動鎖機制,只要用戶指定會話的事務隔離級別, 數據庫就會通過分析SQL語句然後爲事務訪問的資源加上合適的鎖。
隔離級別是指若干個併發的事務之間的隔離程度。TransactionDefinition 接口中定義有五個表示隔離級別的常量(用於解決併發問題)一般情況下使用中間兩種就行。

TransactionDefinition 接口事務隔離級別 描述
READ_UNCOMMITTED 該隔離級別表示一個事務可以讀取另一個事務修改但還沒有提交的數據。該級別不能防止髒讀,不可重複讀和幻讀,因此很少使用該隔離級別。
READ_COMMITTED 系統默認值,該隔離級別表示一個事務只能讀取另一個事務已經提交的數據。可以防止髒讀,是大多數情況下的推薦值。
REPEATABLE_READ 該隔離級別表示一個事務在整個過程中可以多次重複執行某個查詢,並且每次返回的記錄都相同。該級別可以防止髒讀和不可重複讀
SERIALIZABLE 所有的事務依次逐個執行,事務之間就完全不可能產生干擾。該級別可以防止髒讀、不可重複讀以及幻讀。但是這將嚴重影響程序的性能。通常情況下也不會用到該級別。
隔離級別(√:允許出現 ×:不允許出現) 髒讀 不可重複讀 幻讀 第一類丟失更新 第二類丟失更新
READ_UNCOMMITTED ×
READ_COMMITTED × ×
REPEATABLE_READ × × × ×
SERIALIZABLE × × × × ×

事務隔離級別和數據訪問的併發性是對立的,事務隔離級別越高併發性就越差。所以要根據具體的應用來確定合適的事務隔離級別,這個地方沒有萬能的原則。

  1. 事務的併發問題

首先要知道,只有存在併發數據訪問時才需要事務(提交/回滾就結束事務),當多個事務訪問同一數據時,可能會存在5類併發問題,包括3 類數據讀取問題( 髒讀、不可重複讀和幻
讀和2類數據更新問題(第1 類丟失更新和第2 類丟失更新)
3類數據讀取問題

1)髒讀(Dirty Read): 一個事務讀到了另一個事務的還沒有提交數據. (比如A事務讀取B事務尚未提交的數據並在此基礎上操作,而B事務執行回滾,那麼A 讀取到的數據就是髒數據。)(很嚴重的行爲,必須處理,不然可能有很大的影響,比如轉賬事務)
2)不可重複讀(Unrepeatable Read): 一個事務中多次讀到的數據不一致,一個事務讀到了另一個事務修改後的數據。(不可重複讀,保證數據修改後,不會出現兩次一樣的數據)
3) 幻讀(虛讀Phantom Read):一個事務讀到了另一個事務insert提交的數據。(比如事務A 重新執行一個查詢,返回一系列符合查詢條件的行,發現其中插入了被事務B 提交的行)(不可能出現在MySQL中,只會出現在Oracle中)

兩類丟失更新問題

1)第一類丟失更新:
  事務A撤銷時, 把已經提交的事務B的更新數據覆蓋了(比如 取款事務A開啓事務查詢餘額1000元,轉賬事務B開啓事務轉賬100給A,A取出100,提交事務之後,再查詢餘額還是1000元)
2)事務A覆蓋事務 已經提交的數據,造成事務B 所做的操作丟失(比如:取款事務A和轉賬B先後開啓事務,先後查詢餘額都是1000元,取款事務A取出100,餘額變成900,提交事務,但是此時轉賬事務B存入100,將餘額修改爲1100元,提交事務,然後再查詢帳戶餘額就是1100,取款事務A的操作丟失)

JDBC如何進行事務處理:

Connection提供了事務處理的方法,通過調用setAutoCommit(false)可以設置手動提交事務。當事務完成後用commit()顯式提交事務;如果在事務處理過程中發生異常則通過rollback()進行事務回滾。除此之外, 從JDBC 3.0 中還引入了Savepoint( 保存點)的概念,允許通過代碼設置保存點並讓事務回滾到指定的保存點。

40. 正則表達式是什麼,Java中如何支持正則表達式。

在編寫處理字符串的程序時,經常會有查找符合某些複雜規則的字符串的需要。正則表達式就是用於描述這些規則的工具。換句話說, 正則表達式就是記錄文本規則的代碼。正則表達式就是在進行字符串匹配和處理的時候最爲強大的工具,絕大多數語言都提供了對正則表達式的支。
  Java中的String類提供了支持正則表達式操作的方法,包括: matches()、replaceAll()、replaceFirst()、split() 此外,Java 中可以用Pattern類表示正則表達式對象, 它提供了豐富的API 進行各種正則表達式操作。

41. 獲取Class對象的三種方法,通過反射創建對象的方法

獲取class對象的三種方法:

1)每個通過class屬性獲取。【類名.class
2) 每個對象通過getClass()方法獲取。【對象.getClass()
3)通過Class.forName(“類的完整名”)獲取,需要捕獲異常。【Class.forName("類的完整名")

第一種方式,類名.class 不會將類加載到內存,第三種Class.forName()會將類加載到內存,對象.getClass()(創建對象了)會加載到內存中

通過反射創建對象的兩種方法:

1)通過類對象(class)調用1newInstance()1方法,例如創建String對象:
String.class.newInstance()
2)通過類對象(class)的getConstructor()getDeclaredConstructor()方法獲得構造器(Constructor)對象並調用其newInstance()方法創建對象,例如:
·String.class.getConstructor(String.class).newInstance(“Hello”);·

42. 23種經典設計模式
類型 設計模式
創建型 工廠方法模式(FactoryMethod)、抽象工廠模式(AbstractFactory)、建造者模式(Builder)、原型模式(Prototype)、單例模式(Singleton)
結構型 適配器模式(Adapter)、橋接模式(Bridge)、組合模式(Composite)、裝飾器模式(Decorator)、門面模式(Facade)、享元模式(Flyweight)、代理模式(Proxy)
行爲型 解釋器模式(Interpreter)、模板方法模式(TemplateMethod)、責任鏈模式(ChainofResponsibility)、命令模式(Command)、迭代器模式(Iterator)、調解者模式(Mediator)、備忘錄模式(Memento)、觀察者模式(Observer)、狀態模式(State)、策略模式(Strategy)、訪問者模式(Visitor)

單例模式:指一個類只有一個實例,且該類能自行創建這個實例的一種模式。(懶漢式、餓漢式寫法)
原型模式:用一個已經創建的實例作爲原型,通過複製該原型對象來創建一個和原型相同或相似的新對象。
策略模式:定義了一系列算法,並將每個算法封裝起來,使它們可以相互替換,且算法的變化不會影響使用算法的客戶。策略模式屬於對象行爲模式,它通過對算法進行封裝,把使用算法的責任和算法的實現分割開來,並委派給不同的對象對這些算法進行管理。
工廠模式:定義一個創建產品對象的工廠接口,將產品對象的實際創建工作推遲到具體子工廠類當中。工廠類可以根據條件生成不同的子類實例,這些子類有一個公共的抽象父類並且實現了相同的方法,但是這些方法針對不同的數據進行了不同的操作(多態方法)。當得到子類的實例後,開發人員可以調用基類中的方法而不必考慮到底返回的是哪一個子類的實例。
代理模式:給一個對象提供一個代理對象,並由代理對象控制原對象的引用。實際開發中,按照使用目的的不同,代理可以分爲:遠程代理、虛擬代理、保護代理、Cache 代理、防火牆代理、同步化代理、智能引用代理。
適配器模式:把一個類的接口變換成客戶端所期待的另一種接口,從而使原本因接口不匹配而無法在一起使用的類能夠一起工作。
模板方法模式:提供一個抽象類,將部分邏輯以具體方法或構造器的形式實現,然後聲明一些抽象方法來迫使子類實現剩餘的邏輯。不同的子類可以以不同的方式實現這些抽象方法(多態實現),從而實現不同的業務邏輯。
狀態模式:對有狀態的對象,把複雜的“判斷邏輯”提取到不同的狀態對象中,允許狀態對象在其內部狀態發生改變時改變其行爲。
裝飾者模式:指在不改變現有對象結構的情況下,動態地給該對象增加一些職責(即增加其額外功能)的模式,它屬於對象結構型模式。
瞭解其他的可以從這裏面看:23種設計模式詳解

43. 面向對象的七大設計原則
原則 描述
開閉原則(OCP) 軟件實體應當對擴展開放,對修改關閉
里氏替換原則(LSP) 闡述了有關繼承的一些原則(什麼時候使用繼承),里氏替換原是繼承複用的基礎,反映了基類與子類之間的關係,是對開閉原則的補充,是對實現抽象化的具體步驟的規範。可以這麼理解:子類可以擴展父類的功能,但不能改變父類原有的功能,也就是子類繼承父類時,除添加新的方法完成新增功能外,儘量不要重寫父類的方法。
依賴倒置原則(DIP) 核心思想是:要面向接口編程,不要面向實現編程。(抽象層相對穩定,因此以抽象爲基礎搭建起來的架構要比以細節(實現類)爲基礎搭建起來的架構要穩定得多)
單一職責原則(SRP) 提出對象不應該承擔太多職責。單一職責同樣也適用於方法。一個方法應該儘可能做好一件事情。如果一個方法處理的事情太多,其顆粒度會變得很粗,不利於重用。
接口隔離原則(ISP) 要求程序員儘量將臃腫龐大的接口拆分成更小的和更具體的接口,讓接口中只包含客戶感興趣的方法。接口要小而專,絕不能大而全。
迪米特法則(LoD) 又叫作最少知識原則(LKP),一個對象應當對其他對象有儘可能少的瞭解。如果兩個軟件實體無須直接通信,那麼就不應當發生直接的相互調用,可以通過第三方轉發該調用。其目的是降低類之間的耦合度,提高模塊的相對獨立性。
合成複用原則(CRP) 叫組合/聚合複用原則(CARP)要求在軟件複用時,要儘量先使用組合或者聚合等關聯關係來實現,其次才考慮使用繼承關係來實現。

概述:
開閉原則是總綱,它告訴我們要對擴展開放,對修改關閉;
里氏替換原則告訴我們不要破壞繼承體系;
依賴倒置原則告訴我們要面向接口編程;
單一職責原則告訴我們實現類要職責單一;
接口隔離原則告訴我們在設計接口的時候要精簡單一;
迪米特法則告訴我們要降低耦合度;
合成複用原則告訴我們要優先使用組合或者聚合關係複用,少用繼承關係複用。

44. UML概念,常用UML圖

UML是統一建模語言( Unified Modeling Language)的縮寫,它發表於1997年, 綜合了當時已經存在的面向對象的建模語言、方法和過程, 是一個支持模型化和軟件系統開發的圖形化語言,爲軟件開發的所有階段提供模型化和可視化支持。使用UML 可以幫助溝通與交流,輔助應用設計和文檔的生成,還能夠闡釋系統的結構和行爲。
常用UML圖
用例圖(use case diagram)、類圖(class diagram)、時序圖(sequencediagram)、協作圖(collaboration diagram)、狀態圖(statechart diagram)、活動圖(activity diagram)、構件圖(component diagram)、部署圖(deploymentdiagram)
用例圖:用來捕獲需求,描述系統的功能,通過該圖可以迅速的瞭解系統的功能模塊及其關係
類圖:描述類以及類與類之間的關係,通過該圖可以快速瞭解系統
時序圖:描述執行特定任務時對象之間的交互關係以及執行順序, 通過該圖可以瞭解對象能接收的消息也就是說對象能夠向外界提供的服務。

45. HashMap實現原理

JDK1.8中,HashMap採用位桶+鏈表+紅黑樹實現,當鏈表長度超過閾值(8)時,將鏈表轉換爲紅黑樹,這樣大大減少了查找時間。
HashCode是jdk根據對象的地址或字符串或者數字利用hash算法計算出的int類型的數值。
HashMap實現原理:

首先有一個每個元素都是鏈表的數組,當添加一個元素(key-value)時,就首先計算元素key的hash值,以此確定插入數組中的位置,但是可能存在同一hash值的元素已經被放在數組同一位置了,這時就添加到同一hash值的元素的後面,他們在數組的同一位置,但是形成了鏈表,同一各鏈表上的Hash值是相同的,所以說數組存放的是鏈表。而當鏈表長度太長時,鏈表就轉換爲紅黑樹,這樣大大提高了查找的效率。
在jdk8中,HashMap處理“碰撞”增加了紅黑樹這種數據結構,當碰撞結點較少時,採用鏈表存儲,當較大時(>8個),採用紅黑樹(特點是查詢時間是O(logn))存儲(有一個閥值控制,大於閥值(8個),將鏈表存儲轉換成紅黑樹存儲)

紅黑樹是一種自平衡二叉查找樹

紅黑樹是每個節點都帶有顏色屬性的二叉查找樹,顏色或紅色或黑色。
對於任何有效的紅黑樹都有以下要求:
1)節點是紅色或黑色。
2)根節點是黑色。
3)每個葉節點是黑色的。
4)每個紅色節點的兩個子節點都是黑色。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點)
5從任一節點到其每個葉子的所有路徑都包含相同數目的黑色節點。

46. 同步和異步

如果系統中存在臨界資源(資源數量少於競爭資源的線程數量的資源), 例如正在寫的數據以後可能被另一個線程讀到,或者正在讀的數據可能已經被另一個線程寫過了,那麼這些數據就必須進行同步存取(數據庫操作中的排他鎖就是最好的例子)。
當應用程序在對象上調用了一個需要花費很長時間來執行的方法,並且不希望讓程序等待方法的返回時, 就應該使用異步編程,在很多情況下采用異步途徑往往更有效率。
事實上,所謂的同步就是指阻塞式操作, 而異步就是非阻塞式操作。(同步必須等待返回結果才能繼續執行,異步就是瀏覽器發送請求,不管服務器是否返回結果,都可以繼續執行)

47. Servlet的生命週期

Web容器加載Servlet並將其實例化後,Servlet生命週期開始,容器運行其init()方法進行Servlet的初始化;請求到達時調用Servlet的service方法,service方法會調用與請求對應的doGet或doPost等方法;當服務器關閉會項目被卸載時服務器會將Servlet實例銷燬,此時會調用Servlet的destroy方法。

48. get和post請求的區別

1)get請求用來從服務器上獲得資源,而post是用來向服務器提交數據
2)get將表單中數據按照name=value的形式,添加到action 所指向的URL 後面,並且兩者使用“?”連接,而各個變量之間使用“&”連接;post是將表單中的數據放在HTML頭部(header),傳遞到action所指向URL
3)get傳輸的數據要受到URL長度限制(1024字節);而post可以傳輸大量的數據,上傳文件只能使用post方式
4)使用get時參數會顯示在地址欄上,如果這些數據不是敏感數據,那麼可以使用get;對於敏感數據還是應用使用post

49. HttpServlet容器響應Web客戶請求流程?

1)Web客戶向Servlet容器發出Http請求;
2)Servlet容器解析Web客戶的Http請求;
3)Servlet容器創建一個HttpRequest對象,在這個對象中封裝Http請求信息;
4)Servlet容器創建一個HttpResponse對象;
5)Servlet容器調用HttpServlet的service方法,這個方法中會根據request的Method來判斷具體是執行doGet還是doPost,把HttpRequest和HttpResponse對象作爲service方法的參數傳給HttpServlet對象;
6)HttpServlet調用HttpRequest的有關方法,獲取HTTP請求信息;
7)HttpServlet調用HttpResponse的有關方法,生成響應數據;
8)Servlet容器把HttpServlet的響應結果傳給Web客戶

50. 什麼是Callable 和Future?

Callable 接口類似於Runnable,但是Runnable 不會返回結果,並且無法拋出返回結果的異常,而Callable 功能更強大一些,被線程執行後,可以返回值,這個返回值可以被Future 拿到,也就是說,Future 可以拿到異步執行任務的返回值。可以認爲是帶有回調的Runnable。
Future 接口表示異步任務,是還沒有完成的任務給出的未來結果。所以說Callable用於產生結果,Future 用於獲取結果。

JAVA一些題目和Java的一些方法

  1. Math.round(11.5) 等於多少?Math.round(-11.5)等於多少?

Math.round(11.5)的返回值是12, Math.round(-11.5)的返回值是-11。四舍五
入的原理是在參數上加0.5 然後進行下取整

  1. short s1 = 1; s1 = s1 + 1;有錯嗎? short s1 = 1; s1 += 1;有錯嗎?

1)short s1 = 1; s1 = s1 + 1;
  編譯錯誤。由於1 是int 類型,因此s1+1運算結果也是int型, 需要強制轉換類型才能賦值給short 型。
2)short s1 = 1; s1 += 1;
  編譯正確,因爲s1+= 1;相當於s1 = (short)(s1 + 1);其中有隱含的強制類型轉換(自增長自動轉換,除非越界)

  1. 用最有效率的方法計算2乘以8(運算符<<>>的概念)

2 << 3(左移3 位相當於乘以2 的3 次方,右移3 位相當於除以2 的3 次方。

關於運算符<<>>
x >> n就是 先將x轉成二進制,不讀後面的n位
x << n就是 先將x轉成二進制,往二進制數據後面加n個0

案例:int x = 16;
x >> 1 輸出x=8(先將x轉成二進制 10000, 不讀最後一位0, 輸出 1000, 轉爲10進制即爲8)
x << 1 輸出x=32(先將x轉成二進制 10000,,往最後再讀取一位0( 或根據是否已經有移位), 輸出 100000, 轉爲10進制即爲32)

  1. 數組有沒有length()方法?String 有沒有length()方法?

數組沒有length()方法,只有有length 的屬性。String 有length()方法。但是在JavaScript中 ,獲得字符串的長度是通過length 屬性得到的(非常容易和Java搞混)

  1. 當一個對象被當作參數傳遞到一個方法後,此方法可改變這個對象的屬性,並可返回變化後的結果,那麼這裏到底是值傳遞還是引用傳遞?

值傳遞。Java 語言的方法調用只支持參數的值傳遞。當一個對象實例作爲一個參數被傳遞到方法中時,參數的值就是對該對象的引用。對象的屬性可以在被調
用過程中被改變,但對對象引用的改變是不會影響到調用者的。C++和C#中可以
通過傳引用或傳輸出參數來改變傳入的參數的值。在C#中可以編寫如下所示的代
碼, 但是在Java 中卻做不到。

  1. String str = new String(“xyz”);創建了幾個字符串對象?

兩個對象,一個是靜態區(方法區常量池)的”xyz“字符串,一個是用new 創建在堆上的對象str。

  1. Java 中有幾種類型的流,它們的關係

Java中主要是字節流和字符流。字節流繼承於InputStream、OutputStream,字符流繼承於Reader、Writer
在java.io 包中還有許多其他的流,主要是爲了提高性能和使用方便
關於Java 的I/O 需要注意的有兩點:

1)兩種對稱性( 輸入和輸出的對稱性,字節和字符的對稱性)
2)兩種設計模式(適配器模式和裝潢模式)。

  1. 編程實現文件拷貝的兩種方式
public static void fileCopy(String source, String target) throws
IOException {
	try (InputStream in = new FileInputStream(source)) {
		try (OutputStream out = new FileOutputStream(target)) {
			byte[] buffer = new byte[4096];
			int bytesToRead;
			while((bytesToRead = in.read(buffer)) != -1) {
				out.write(buffer, 0, bytesToRead);
			}
		}
	}
}
123456789101112

NIO方式

public static void fileCopyNIO(String source, String target) throws
IOException {
	try (FileInputStream in = new FileInputStream(source)) {
		try (FileOutputStream out = new FileOutputStream(target)) {
			FileChannel inChannel = in.getChannel();
			FileChannel outChannel = out.getChannel();
			ByteBuffer buffer = ByteBuffer.allocate(4096);
			while(inChannel.read(buffer) != -1) {
				buffer.flip();
				outChannel.write(buffer);
				buffer.clear();
			}
		}
	}
}

12345678910111213141516
  1. 寫一個方法,輸入一個文件名和一個字符串,統計這個字符串在這個文件中出現的次數。
//統計這個字符串在這個文件中出現的次數
public static int countWordInFile(String filename, String word) {
	int counter = 0;	//統計數
	try (FileReader fr = new FileReader(filename)) {
		try (BufferedReader br = new BufferedReader(fr)) {
			String line = null;
			while ((line = br.readLine()) != null) {
				int index = -1;
				while (line.length() >= word.length() && (index = line.indexOf(word)) >= 0) {
					counter++;
					line = line.substring(index + word.length());
				}
			}
		}
	} catch (Exception ex) {
		ex.printStackTrace();
	}
	return counter;
}
12345678910111213141516171819
  1. 如何用Java 代碼列出一個目錄下所有的文件?
    1)只列出當前文件夾下的文件
public static void main(String[] args) {
	File f = new File("/Users/Downloads");
	for(File temp : f.listFiles()) {
		if(temp.isFile()) {
			System.out.println(temp.getName());
		}
	}
}
12345678

2)對於當前文件夾下的文件夾繼續展開顯示所有文件

private static void queryDirectory(File f, int level) {
	if(f.isDirectory()) {
		for(File temp : f.listFiles()) {
			queryDirectory(temp, level + 1);	//遞歸
		}
	}else {
		for(int i = 0; i < level - 1; i++) {
			System.out.print("\t");	//\t四個空格
		}
		System.out.println(f.getName()); 	//輸出文件名
	}
}

public static void main(String[] args) {
	queryDirectory(new File("/Users/Downloads"),0);
}
12345678910111213141516

JDK1.7之後可以使用NIO.2的API實現

public static void main(String[] args) throws IOException {
	Path initPath = Paths.get("/Users/Downloads");
	Files.walkFileTree(initPath, new SimpleFileVisitor<Path>() {
		@Override
		public FileVisitResult visitFile(Path file, BasicFileAttributesattrs) throws IOException {
			System.out.println(file.getFileName().toString());
			return FileVisitResult.CONTINUE;
		}
	});
}
12345678910
  1. TCP編程通過Socket套接字編程實現服務端向客戶端發送信息
    服務端:
import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

//socket,TCP編程,服務端程序
public class Server {

    public static void serverInfo(){
        ServerSocket server = null;
        Socket client = null;
        PrintStream out = null;

        try {
            //在服務器8000端口等待客戶連接
            server = new ServerSocket(8000);
            System.out.println("服務器正在等待客戶端的連接......");
            //程序阻塞,等待客戶端的連接
            client = server.accept();

            System.out.println("連接客戶端成功!!");
            //實例化打印流對象,用於向客戶端發送輸出信息
            out = new PrintStream(client.getOutputStream());

            System.out.println("請輸入您要向客戶端發送的信息:");
            Scanner scan = new Scanner(System.in);	//獲取輸入流
            //準備向客戶端發送的信息
            String info = "服務端向客戶端發送信息:" + scan.nextLine();

            //輸出信息
            out.println(info);

			scan.close();   //關閉輸入流
            out.close();    //關閉輸出打印流
            client.close(); //關閉客戶端
            server.close(); //關閉服務器端的練級
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        serverInfo();//開啓服務端
    }
}
12345678910111213141516171819202122232425262728293031323334353637383940414243444546

客戶端:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

//socket編程,客戶端,要與服務端的端口號一致
public class Client {

    public static void clientInfo(){
        //聲明socket對象
        Socket client = null;

        try {
            //實例化socket對象,指定連接的主機名稱和端口號
            client = new Socket("localhost",8000);
            System.out.println("客戶端連接成功");
            //聲明緩存字符流,用來接收信息
            BufferedReader buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
            //讀取信息
            String info = buf.readLine();
            //輸出讀取的信息
           System.out.println("客戶端收到服務器("+ client.getInetAddress() +")端發來的信息:【" + info + "】");
            client.close();
            buf.close();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        clientInfo();
    }
}

123456789101112131415161718192021222324252627282930313233343536

運行:

1、 開啓服務端Server

Server:服務器正在等待客戶端的連接…

2.、開啓客戶端Client

Server:(輸出內容)
  服務器正在等待客戶端的連接…
  連接客戶端成功!!
  請輸入您要向客戶端發送的信息:
Client:(輸出內容)
  客戶端連接成功

3、Server輸入內容HelloWorld

客戶端Client接收到信息:
  客戶端收到服務器(localhost/127.0.0.1)端發來的信息:【服務端向客戶端發送信息:Hello World!!!】

  1. 餓漢式單例模式和懶漢式單例模式

1) 將構造器私有,不允許外界通過構造器
創建對象;
2) 通過公開的靜態方法向外界返回類的唯一實例

餓漢式單例模式:

//餓漢式  不延遲實例化的做法,直接靜態實例化創建 保證線程安全
public class HungarySingleton {
    private static HungarySingleton instance=new HungarySingleton();
    private HungarySingleton(){   
    }
    public static HungarySingleton getInstance(){
        return instance;//返回唯一實例
    }
}
123456789

懶漢式單例模式:

public class LazySingleton {
	private static LazySingleton instance = null;
	private Singleton() {}
	public static synchronized LazySingleton getInstance(){
		if (instance == null) instance = new LazySingleton ();
		return instance;
	}
}
12345678

volatile關鍵字,DCL機制實現懶漢式,提高性能

//懶漢式 DCL機制
//T1先從主存拷貝數據到自己的線程內存,進行處理數據,處理完數據更新到主存
//T2從主存拷貝數據到自己的線程內存
public class LazySingleton {
    private static volatile LazySingleton instance = null;
    private LazySingleton(){
    }
    //unlock happen-before lock 語義
    //T1,T2 ---可見性  T1 happen-before T2

    /**
     * A->B,B->C===A->C
     * T2調用getInstance()和調用getName()
     * T1調用getInstance()
     * a++
     * T1 ->T2
     * 構造器內存操作->T1 ->T2
     */
    public static  LazySingleton getInstance(){
      //T2
       if(instance==null){
            //T1  同步化 所有爲null的只能進入一個,等待執行
            synchronized(LazySingleton.class){
                if(instance==null){
                    instance=new LazySingleton();
                    //JSR-133
                    //1.開闢空間
                    //2.初始化對象
                    //3.把地址給isntance變量---CPU
                }
            }
        }
        return instance;
    }
}
1234567891011121314151617181920212223242526272829303132333435
  1. 能將int強制轉換爲byte類型的變量嗎?如果該值
    大於byte 類型的範圍,將會出現什麼現象?

可以做強制轉換,但是Java中int 是32位(4byte)的,而byte 是8位(1byte)的,所以,如果強制轉化是,int 類型的高24 位將會被丟棄,byte 類型的範圍是從-128 到128。

  1. 哪個類包含clone 方法?是Cloneable 還是Object?

java.lang.Cloneable 是一個標示性接口,不包含任何方法, clone 方法在object 類中定義。並且clone() 方法是一個本地方法,這意味着它是由c 或c++ 或其他本地語言實現。

  1. Java 中++ (- -)操作符是線程安全的嗎?

不是線程安全的操作。它涉及到多個指令,如讀取變量值,增加/減少,然後存儲回內存,這個過程可能會出現多個線程交差。

  1. a = a + ba += b 的區別

+=隱式的將加操作的結果類型強制轉換爲持有結果的類型(比如a += b 會將運算結果a+b轉換爲需要a對應的類型)。如果兩這個整型相加,如byte、short 或者int,首先會將它們提升到int 類型,然後在執行加法操作,最後再轉換爲接收的數據的類型。
a = a + b不會進行類型轉換,如果越界就拋出異常

// byte取值 -128到128,超過就越界要轉換類型
byte a = 120;
byte b = 120;
a = a + b;	//編譯報錯:cannot convert from int to byte,因爲不會將結果(a+b)自動強制類型轉換
a += b;		//編譯正確,b = -16
//隱式的將加操作的結果類型強制轉換爲持有結果的類型(a+b的時候隱式轉換爲int相加
//結果強制轉換爲接收結果a的類型byte,a+b=240(int類型)轉換爲a的類型byte=-16)
1234567
  1. int 和Integer 哪個會佔用更多的內存

Integer包裝類會佔用更多的內存。Integer 是一個對象,需要存儲對象的元數據。但是int 是一個原始類型的數據,所以佔用的空間更少

  1. “a==b”和”a.equals(b)”有什麼區別?

如果a和b都是對象,則a==b 是比較兩個對象的引用,只有當a和b指向的是堆中的同一個對象纔會返回true,a.equals(b) 是進行邏輯比較,所以通常需要重寫該方法來提供邏輯一致性的比較。例如,String 類重寫equals() 方法, 所以可以用於兩個不同對象,但是包含的字母相同的比較

  1. a.hashCode() 有什麼用?與a.equals(b) 有什麼關係?

hashCode()方法是相應對象整型的hash值。它常用於基於hash 的集合類,如Hashtable、HashMap、LinkedHashMap 等等。它與equals() 方法關係特別緊密。根據Java 規範,兩個使用equal() 方法來判斷相等的對象,必須具有相同的hash code

  1. Java集合框架

poll()方法和remove()方法的區別

poll() 和remove() 都是從隊列中取出一個元素,但是poll() 在獲取元素失敗的時候會返回空,但是remove() 失敗的時候會拋出異常。

LinkedHashMap 和PriorityQueue 的區別

PriorityQueue保證最高或者最低優先級的的元素總是在隊列頭部,但是LinkedHashMap 維持的順序是元素插入的順序。當遍歷一個PriorityQueue時,沒有任何順序保證,但是LinkedHashMap 課保證遍歷順序是元素插入的順序。

ArrayList 與LinkedList 的主要區別

ArrrayList 底層的數據結構是數組,支持隨機訪問,而
LinkedList的底層數據結構是鏈表(雙向鏈表),不支持隨機訪問(但是優化了更新操作)。使用下標訪問一個元素,ArrayList 的時間複雜度是O(1),而LinkedList 是O(n)。

Hashtable 與HashMap 有什麼不同

1)Hashtable 是JDK 1 遺留下來的類,而HashMap 是後來增加的。
2)Hashtable 是同步的,比較慢,但HashMap 沒有同步策略,所以會更快
3)Hashtable不允許有個空的key,但是HashMap允許出現一個null key。

Java中的HashSet,內部是如何工作

HashSet 的內部採用HashMap 來實現。由於Map 需要key 和value,所以所有key 的都有一個默認value。類似於HashMap, HashSet 不允許重複的key,只允許有一個null key,意思就是HashSet 中只允許存儲一個null 對象。

ArrayList 和HashMap 的默認大小

ArrayList 的默認大小是10 個元素, HashMap的默認大小是16 個元素(必須是2的冪次)

兩個相同的對象的hash code一定相同,兩個對象有相同的hash Code,但兩個對象不一定相同。

兩個不相等的對象可能會有相同的hashcode 值, 這就是爲什麼在hashmap 中會有衝突。相等hashcode 值的規定只是說如果兩個對象相等, 必須有相同的hashcode 值, 但是沒有關於不相等對象的任何規定

Java中的TreeMap是使用紅黑樹實現的。

本文作者:strive_day
本文鏈接:https://blog.csdn.net/qq_40542534/article/details/109241330

更多信息請關注公衆號:「軟件老王」,關注不迷路,軟件老王和他的IT朋友們,分享一些他們的技術見解和生活故事。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章