關于Java面試題2021 Java面試題大彙總系列基本收集整理完成了,最新常Java面試題彙總(含答案解析)發現網上很多Java面試題都沒有答案,所有Java面試題目都是經過精心挑選的,很基礎又考驗求職者的基本功,應該說被面試到的幾率很大。這裡整理挑選出來2021年面試題供大家面試前拿來看一看,所有題目整理自網絡,有一些錯誤和筆誤,感謝讀者的熱心糾錯,在聲明中已經改正過來
其實,部落客還整理了,更多大廠面試題,直接下載下傳吧
下載下傳連結:高清172份,累計 7701 頁大廠面試題 PDF
一鍵直達:https://www.souyunku.com/?p=67
1、什麼是ORM?
對象關系映射(Object-Relational Mapping,簡稱ORM)是一種為了解決程式的面向對象模型與資料庫的關系模型互不比對問題的技術
2、樂觀鎖和悲觀鎖的了解及如何實作,有哪些實作方式?
悲觀鎖:
總是假設最壞的情況,每次去拿資料的時候都認為别人會修改,是以每次在拿資料的時候都會上鎖,這樣别人想拿這個資料就會阻塞直到它拿到鎖。傳統的關系型資料庫裡邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。再比如Java裡面的同步原語synchronized關鍵字的實作也是悲觀鎖。
樂觀鎖:
顧名思義,就是很樂觀,每次去拿資料的時候都認為别人不會修改,是以不會上鎖,但是在更新的時候會判斷一下在此期間别人有沒有去更新這個資料,可以使用版本号等機制。樂觀鎖适用于多讀的應用類型,這樣可以提高吞吐量,像資料庫提供的類似于write_condition機制,其實都是提供的樂觀鎖。在Java中Java.util.concurrent.atomic包下面的原子變量類就是使用了樂觀鎖的一種實作方式CAS實作的。
樂觀鎖的實作方式:
1、 使用版本辨別來确定讀到的資料與送出時的資料是否一緻。送出後修改版本辨別,不一緻時可以采取丢棄和再次嘗試的政策。
2、 Java中的Compare and Swap即CAS ,當多個線程嘗試使用CAS同時更新同一個變量時,隻有其中一個線程能更新變量的值,而其它線程都失敗,失敗的線程并不會被挂起,而是被告知這次競争中失敗,并可以再次嘗試。 CAS 操作中包含三個操作數 —— 需要讀寫的記憶體位置(V)、進行比較的預期原值(A)和拟寫入的新值(B)。如果記憶體位置V的值與預期原值A相比對,那麼處理器會自動将該位置值更新為新值B。否則處理器不做任何操作。
CAS缺點:
1、 ABA問題:
比如說一個線程one從記憶體位置V中取出A,這時候另一個線程two也從記憶體中取出A,并且two進行了一些操作變成了B,然後two又将V位置的資料變成A,這時候線程one進行CAS操作發現記憶體中仍然是A,然後one操作成功。盡管線程one的CAS操作成功,但可能存在潛藏的問題。從Java1.5開始JDK的atomic包裡提供了一個類AtomicStampedReference來解決ABA問題。
2、 循環時間長開銷大:
對于資源競争嚴重(線程沖突嚴重)的情況,CAS自旋的機率會比較大,進而浪費更多的CPU資源,效率低于synchronized。
3、 隻能保證一個共享變量的原子操作:
當對一個共享變量執行操作時,我們可以使用循環CAS的方式來保證原子操作,但是對多個共享變量操作時,循環CAS就無法保證操作的原子性,這個時候就可以用鎖。
3、如何在jsp頁面上顯示一些特定格式的數字或者日期
使用jstl标簽庫,使用numberformat或者dateformat标簽
4、請說出與線程同步以及線程排程相關的方法。
1、 wait():使一個線程處于等待(阻塞)狀态,并且釋放所持有的對象的鎖;
2、 sleep():使一個正在運作的線程處于睡眠狀态,是一個靜态方法,調用此方法要處理InterruptedException異常;
3、 notify():喚醒一個處于等待狀态的線程,當然在調用此方法的時候,并不能确切的喚醒某一個等待狀态的線程,而是由JVM确定喚醒哪個線程,而且與優先級無關;
4、 notityAll():喚醒所有處于等待狀态的線程,該方法并不是将對象的鎖給所有線程,而是讓它們競争,隻有獲得鎖的線程才能進入就緒狀态;
提示:關于Java多線程和并發程式設計的問題,建議大家看我的另一篇文章《關于Java并發程式設計的總結和思考》。
補充:Java 5通過Lock接口提供了顯式的鎖機制(explicit lock),增強了靈活性以及對線程的協調。Lock接口中定義了加鎖(lock())和解鎖(unlock())的方法,同時還提供了newCondition()方法來産生用于線程之間通信的Condition對象;此外,Java 5還提供了信号量機制(semaphore),信号量可以用來限制對某個共享資源進行通路的線程的數量。在對資源進行通路之前,線程必須得到信号量的許可(調用Semaphore對象的acquire()方法);在完成對資源的通路後,線程必須向信号量歸還許可(調用Semaphore對象的release()方法)。
下面的例子示範了100個線程同時向一個銀行賬戶中存入1元錢,在沒有使用同步機制和使用同步機制情況下的執行情況。
銀行賬戶類:
/
* 銀行賬戶
* @author 駱昊
*
*/
public class Account {
private double balance; // 賬戶餘額
/
* 存款
* @param money 存入金額
*/
public void deposit(double money) {
double newBalance = balance + money;
try {
Thread.sleep(10); // 模拟此業務需要一段處理時間
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
balance = newBalance;
}
/
* 獲得賬戶餘額
*/
public double getBalance() {
return balance;
}
}
存錢線程類:
/
* 存錢線程
* @author 駱昊
*
*/
public class AddMoneyThread implements Runnable {
private Account account; // 存入賬戶
private double money; // 存入金額
public AddMoneyThread(Account account, double money) {
this.account = account;
this.money = money;
}
@Override
public void run() {
account.deposit(money);
}
}
測試類:
import Java.util.concurrent.ExecutorService;
import Java.util.concurrent.Executors;
public class Test01 {
public static void main(String[] args) {
Account account = new Account();
ExecutorService service = Executors.newFixedThreadPool(100);
for(int i = 1; i <= 100; i++) {
service.execute(new AddMoneyThread(account, 1));
}
service.shutdown();
while(!service.isTerminated()) {}
System.out.println("賬戶餘額: " + account.getBalance());
}
}
在沒有同步的情況下,執行結果通常是顯示賬戶餘額在10元以下,出現這種狀況的原因是,當一個線程A試圖存入1元的時候,另外一個線程B也能夠進入存款的方法中,線程B讀取到的賬戶餘額仍然是線程A存入1元錢之前的賬戶餘額,是以也是在原來的餘額0上面做了加1元的操作,同理線程C也會做類似的事情,是以最後100個線程執行結束時,本來期望賬戶餘額為100元,但實際得到的通常在10元以下(很可能是1元哦)。解決這個問題的辦法就是同步,當一個線程對銀行賬戶存錢時,需要将此賬戶鎖定,待其操作完成後才允許其他的線程進行操作,代碼有如下幾種調整方案:
在銀行賬戶的存款(deposit)方法上同步(synchronized)關鍵字
/
* 銀行賬戶
* @author 駱昊
*
*/
public class Account {
private double balance; // 賬戶餘額
/
* 存款
* @param money 存入金額
*/
public synchronized void deposit(double money) {
double newBalance = balance + money;
try {
Thread.sleep(10); // 模拟此業務需要一段處理時間
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
balance = newBalance;
}
/
* 獲得賬戶餘額
*/
public double getBalance() {
return balance;
}
}
線上程調用存款方法時對銀行賬戶進行同步
/
* 存錢線程
* @author 駱昊
*
*/
public class AddMoneyThread implements Runnable {
private Account account; // 存入賬戶
private double money; // 存入金額
public AddMoneyThread(Account account, double money) {
this.account = account;
this.money = money;
}
@Override
public void run() {
synchronized (account) {
account.deposit(money);
}
}
}
通過Java 5顯示的鎖機制,為每個銀行賬戶建立一個鎖對象,在存款操作進行加鎖和解鎖的操作
import Java.util.concurrent.locks.Lock;
import Java.util.concurrent.locks.ReentrantLock;
/
* 銀行賬戶
*
* @author 駱昊
*
*/
public class Account {
private Lock accountLock = new ReentrantLock();
private double balance; // 賬戶餘額
/
* 存款
*
* @param money
* 存入金額
*/
public void deposit(double money) {
accountLock.lock();
try {
double newBalance = balance + money;
try {
Thread.sleep(10); // 模拟此業務需要一段處理時間
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
balance = newBalance;
}
finally {
accountLock.unlock();
}
}
/
* 獲得賬戶餘額
*/
public double getBalance() {
return balance;
}
}
按照上述三種方式對代碼進行修改後,重寫執行測試代碼Test01,将看到最終的賬戶餘額為100元。當然也可以使用Semaphore或CountdownLatch來實作同步。
5、堆
JVM記憶體管理最大的一塊,對被線程共享,目的是存放對象的執行個體,幾乎所欲的對象執行個體都會放在這裡,當堆沒有可用空間時,會抛出OOM異常.根據對象的存活周期不同,JVM把對象進行分代管理,由垃圾回收器進行垃圾的回收管理
6、你所了解的資料源技術有那些?使用資料源有什麼好處?
Dbcp,c3p0等,用的最多還是c3p0,因為c3p0比dbcp更加穩定,安全;通過配置檔案的形式來維護資料庫資訊,而不是通過寫死。當連接配接的資料庫資訊發生改變時,不需要再更改程式代碼就實作了資料庫資訊的更新。
7、Java對象建立過程
1、 JVM遇到一條建立對象的指令時首先去檢查這個指令的參數是否能在常量池中定義到一個類的符号引用。然後加載這個類(類加載過程在後邊講)
2、 為對象配置設定記憶體。一種辦法“指針碰撞”、一種辦法“空閑清單”,最終常用的辦法“本地線程緩沖配置設定(TLAB)”
3、 将除對象頭外的對象記憶體空間初始化為0
4、 對對象頭進行必要設定
8、為什麼HashTable是線程安全的?
因為HasTable的内部方法都被synchronized修飾了,是以是線程安全的。其他的都和HashMap一樣
1、 HashMap添加方法的源碼

2、 HashTable添加方法的源碼
9、常用io類有那些?
**File
FileInputSteam,FileOutputStream
BufferInputStream,BufferedOutputSream
PrintWrite
FileReader,FileWriter
BufferReader,BufferedWriter
ObjectInputStream,ObjectOutputSream**
10、線程的排程政策
線程排程器選擇優先級最高的線程運作,但是,如果發生以下情況,就會終止線程的運作:
1、 線程體中調用了yield方法讓出了對cpu的占用權利
2、 線程體中調用了sleep方法使線程進入睡眠狀态