Java筆試面試-常見翻車總結

1.去掉 main 方法的 static 修飾符,程序會怎樣?

A:程序無法編譯

B:程序正常編譯,正常運行

C:程序正常編譯,正常運行一下馬上退出

D:程序正常編譯,運行時報錯

答:D

題目解析:運行時異常如下:

錯誤: main 方法不是類 xxx 中的 static, 請將 main 方法定義爲:

public static void main(String[] args)

2.以下程序運行的結果是?

public class TestClass {
    public static void main(String[] args) {
        System.out.println(getLength());
    }
    int getLength() {
        private String s = "xyz";
        int result = s.length();
        return result;
    }
}

A:3

B:2

C:4

D:程序無法編譯

答:D

題目解析:成員變量 s 不能使用任何修飾符(private/protected/public)修飾,否則編譯會報錯。

3.以下程序有幾處錯誤?

abstract class myAbstractClass
    private abstract String method(){};
}

A:1

B:2

C:3

D:4

答:C

題目解析:類少一個“{”類開始標籤、抽象方法不能包含方法體、抽象方法訪問修飾符不能爲 private,因此總共有 3 處錯誤。

4.以下程序執行的結果是?

class A {
    public static int x;
    static {
        x = B.y + 1;
    }
}
public class B {
    public static int y = A.x + 1;
    public static void main(String[] args) {
        System.out.println(String.format("x=%d,y=%d", A.x, B.y));
    }
}

A:程序無法編譯

B:程序正常編譯,運行報錯

C:x=1,y=2

D:x=0,y=1

答:C

5.switch 語法可以配合 return 一起使用嗎?return 和 break 在 switch 使用上有何不同?

答:switch 可以配合 return 一起使用。return 和 break 的區別在於 switch 結束之後的代碼,比如以下代碼:

String getColor(String color) {
    switch (color) {
        case "red":
            return "紅";
        case "blue":
            return "藍";
    }
    return "未知";
}

String getColor(String color) {
    String result = "未知";
    switch (color) {
        case "red":
            result = "紅";
            break;
        case "blue":
            result = "藍";
    }
    return result;
}

對於以上這種 switch 之後沒有特殊業務處理的程序來說,return 和 break 的效果是等效的。然而,對於以下這種代碼:

String getColor(String color) {
    switch (color) {
        case "red":
            return "紅";
        case "blue":
            return "藍";
    }
    return "未知";
}

String getColor(String color) {
    String result = "未知";
    switch (color) {
        case "red":
            result = "紅";
            break;
        case "blue":
            result = "藍";
    }
    if (result.equals("未知")) {
        result = "透明";
    } else {
        result += "色";
    }
    return result;
}

如果 switch 之後還有特殊的業務處理,那麼 return 和 break 就有很大的區別了。

6.一個棧的入棧順序是 A、B、C、D、E 則出棧不可能的順序是?

A:E D C B A

B:D E C B A

C:D C E A B

D:A B C D E

答:C

題目解析:棧是後進先出的,因此:

  • A 選項:入棧順序 A B C D E 出棧順序就是 E D C B A 是正確的;
  • B 選項:A B C D 先入棧,D 先出棧,這個時候 E 在入棧,E 在出棧,順序 D E C B A 也是正確的;
  • C 選項:D 先出棧,說明 A B C 一定已入棧,因爲題目說了入棧的順序是 A B C D E,所以出棧的順序一定是 C B A,而 D C E A B 的順序 A 在 B 前面是永遠不可能發生的,所以選擇是 C;
  • D 選項 A B C D E 依次先入棧、出棧,順序就是 A B C D E。

7.可以在 finally 塊中使用 return嗎?

答:不可以,finally 塊中的 return 返回後方法結束執行,不會再執行 try 塊中的 return 語句。

8.FileInputStream 可以實現什麼功能?

A:文件夾目錄獲取

B:文件寫入

C:文件讀取

D:文件夾目錄寫入

答:C

題目解析:FileInputStream 是文件讀取,FileOutputStream 纔是用來寫入文件的,FileInputStream 和 FileOutputStream 很容易搞混。

9.以下程序打印的結果是什麼?

Thread t1 = new Thread(){
    @Override
    public void run() {
        System.out.println("I'm T1.");
    }
};
t1.setPriority(3);
t1.start();
Thread t2 = new Thread(){
    @Override
    public void run() {
        System.out.println("I'm T2.");
    }
};
t2.setPriority(0);
t2.start();

答:程序報錯 java.lang.IllegalArgumentException,setPriority(n) 方法用於設置程序的優先級,優先級的取值爲 1-10,當設置爲 0 時,程序會報錯。

10.如何設置守護線程?

答:設置 Thead 類的 setDaemon(true) 方法設置當前的線程爲守護線程。

守護線程的使用示例如下:

Thread daemonThread = new Thread(){
    @Override
    public void run() {
        super.run();
    }
};
// 設置爲守護線程
daemonThread.setDaemon(true);
daemonThread.start();

11.以下說法中關於線程通信的說法錯誤的是?

A:可以調用 wait()、notify()、notifyAll() 三個方法實現線程通信

B:wait() 必須在 synchronized 方法或者代碼塊中使用

C:wait() 有多個重載的方法,可以指定等待的時間

D:wait()、notify()、notifyAll() 是 Object 類提供的方法,子類可以重寫

答:D

題目解析:wait()、notify()、notifyAll() 都是被 final 修飾的方法,不能再子類中重寫。選項 B,使用 wait() 方法時,必須先持有當前對象的鎖,否則會拋出異常 java.lang.IllegalMonitorStateException。

12.ReentrantLock 默認創建的是公平鎖還是非公平鎖?

答:默認創建的是非公平鎖,看以下源碼可以得知:

/**
 * Creates an instance of {@code ReentrantLock}.
 * This is equivalent to using {@code ReentrantLock(false)}.
 */
public ReentrantLock() {
    sync = new NonfairSync();
}

Nonfair 爲非公平的意思,ReentrantLock() 等同於代碼 ReentrantLock(false)。

13.ReentrantLock 如何在一段時間內無阻塞嘗試訪問鎖?

答:使用 tryLock(long timeout, TimeUnit unit) 方法,就可以在一段時間內無堵塞的訪問鎖。

14.枚舉比較使用 equals 還是 ==?

答:枚舉比較調用 equals 和 == 的結果是一樣,查看 Enum 的源碼可知 equals 其實是直接調用了 ==,源碼如下:

public final boolean equals(Object other) {
    return this==other;
}

15.在 Spring 中使用 @Value 賦值靜態變量爲什麼 null?怎麼解決?

答:因爲在 Springframework 框架中,當類加載器加載靜態變量時,Spring 上下文尚未加載,因此類加載器不會在 bean 中正確注入靜態類,導致了結果爲 null。可使用 Setter() 方法給靜態變量賦值,代碼如下:

@Component
public class ConfigValue {
    private static String accessKey;
    public String getAccessKey() {
        return accessKey;
    }
    @Value("${accessKey}")
    public void setAccessKey(String accessKey) {
        ConfigValue.accessKey = accessKey;
    }
}
/*
 * 調用賦值變量
 */
@Component
public class TestClass {
    @Autowired
    private ConfigValue configValue;
    public void method() {
        // 讀取配置文件
        configValue.getAccessKey();
    }
}

16.如何自己實現一個定時任務?

答:啓動一個後臺線程,循環執行任務。代碼示例如下:

Thread getTocketThread = new Thread(new Runnable() {
    @Override
    public void run() {
        while (true) {
            try {
                // 執行業務方法
                TimeUnit.HOURS.sleep(2); // 每兩小時執行一次
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
});
if (!getTocketThread.isAlive()) {
    System.out.println("啓動線程");
    getTocketThread.start();
}

17.如何定義一個不定長度的數組?

答:在 Java 中使用數組必須要指定長度,如果長度不固定可使用 ArrayList、LinkedList 等容器接收完數據,再使用 toArray() 方法轉換成固定數組。

18.如何優雅的格式化百分比小數?

答:使用數字格式化類 DecimalFormat 來處理,具體實現代碼如下:

double num = 0.37500;
DecimalFormat df = new DecimalFormat("0.0%");
System.out.println(df.format(num)); // 執行結果:37.5%

19.什麼是跨域問題?爲什麼會產生跨域問題?

答:跨域問題指的是不同站點直接,使用 ajax 無法相互調用的問題。跨域問題是瀏覽器的行爲,是爲了保證用戶信息的安全,防止惡意網站竊取數據,所做的限制,如果沒有跨域限制就會導致信息被隨意篡改和提交,會導致不可預估的安全問題,所以也會造成不同站點間“正常”請求的跨域問題。

20.跨域的解決方案有哪些?

答:常見跨域問題的解決方案如下:

  • jsonp(只支持 get 請求);
  • nginx 請求轉發,把不同站點應用配置到同一個域名下;

  服務器端設置運行跨域訪問,如果使用的是 Spring 框架可通過 @CrossOrigin 註解的方式聲明某個類或方法運行跨域訪問,或者配置全局跨域配置,請參考以下代碼:

// 單個跨域配置
@CrossOrigin(origins = "http://xxx", maxAge = 3600)
@RestController
public class testController{
}

// 全局配置
@Configuration
public class CorsConfiguration {
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/api/**").allowedOrigins("https://xxx");
            }
        };
    }
}

21.什麼原因會導致 Nginx 轉發時丟失部分 header 信息?該如何解決?

答:部分 header 信息丟失的原因是,丟失的 header 的 key 值中有下劃線,因爲 Nginx 轉發時,默認會忽略帶下劃線的 header 信息。

解決方案有兩個,一是去掉 key 值中的下劃線,二是在 Nginx 的配置文件 http 中添加“underscoresinheaders on;” 不忽略有下劃線的 header 信息。

22.如何設計一個高效的系統?

答:要設計一個高效的系統,通常要包含以下幾個方面。

(1)優化代碼

  代碼優化分爲兩種情況:

  • 代碼問題導致系統資源消耗過多的問題,比如,某段代碼導致內存溢出,往往是將 JVM 中的內存用完了,這個時候系統的內存資源消耗殆盡了,同時也會引發 JVM 頻繁地發生垃圾回收,導致 CPU 100% 以上居高不下,這個時候又消耗了系統的 CPU 資源。這種情況下需要使用相應的排查工具 VisualVM 或 JConsole,找到對應的問題代碼再進行優化;
  • 還有一種是非問題代碼,這種代碼不容易發現,比如,LinkedList 集合如果使用 for 循環遍歷,則它的效率是很低的,因爲 LinkedList 是鏈表實現的,如果使用 for 循環獲取元素,在每次循環獲取元素時,都會去遍歷一次 List,這樣會降低讀的效率,這個時候應該改用 Iterator (迭代器)迭代循環該集合。

(2)設計優化

  有很多問題可以通過我們的設計優化來提高程序的執行性能,比如,使用單例模式來減少頻繁地創建和銷燬對象所帶來的性能消耗,從而提高了程序的執行性能。

(3)參數調優

  JVM 和 Web 容器的參數調優,對系統的執行性能也是也很大幫助的。比如,我們的業務中會創建大量的大對象,我們可以通過設置,將這些大對象直接放進老年代,這樣可以減少年輕代頻繁發生小的垃圾回收(Minor GC),減少 CPU 佔用時間,從而提升了程序的執行性能。Web 容器的線程池設置以及 Linux 操作系統的內核參數設置,也對程序的運行性能有着很大的影響,我們根據自己的業務場景優化這兩項內容。

(4)使用緩存

  緩存的使用分爲前端和後端:

  • 前端可使用瀏覽器緩存或者 CDN,CDN 的全稱是 Content Delivery Network,即內容分發網絡。CDN 是構建在現有網絡基礎之上的智能虛擬網絡,依靠部署在各地的邊緣服務器,通過中心平臺的負載均衡、內容分發、調度等功能模塊,使用戶就近獲取所需內容,降低網絡擁塞,提高用戶訪問響應速度和命中率。CDN 的關鍵技術主要有內容存儲和分發技術;
  • 後端緩存,使用第三方緩存 Redis 或 Memcache 來緩存查詢結果,以提高查詢的響應速度。

(5)優化數據庫

  數據庫是最寶貴的資源,通常也是影響程序響應速度的罪魁禍首,它的優化至關重要,通常分爲以下六個方面:

  • 合理使用數據庫引擎
  • 合理設置事務隔離級別,合理使用事務
  • 正確使用 SQL 語句和查詢索引
  • 合理分庫分表
  • 使用數據庫中間件實現數據庫讀寫分離
  • 設置數據庫主從讀寫分離

(6)屏蔽無效和惡意訪問

  前端禁止重複提交:用戶提交之後按鈕置灰,禁止重複提交;用戶限流,在某一時間段內只允許用戶提交一次請求,比如,採取 IP 限流。

(7)搭建分佈式環境,使用負載分發

  可以把程序部署到多臺服務器上,通過負載均衡工具,比如 Nginx,將併發請求分配到不同的服務器,從而提高了系統處理併發的能力。

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