面試題彙總一 Java 語言基礎篇

前言

題目彙總來源 史上最全各類面試題彙總,沒有之一,不接受反駁

春招的進度有些太快了,不知道現在開始學還來不來得及。跟着上面那篇公衆號文章裏的問題學的,順便做個記錄。

 


目錄

前言

Java基礎知識

面向對象三大特徵

面向對象六大原則

Java可見性修飾符

Java設計模式

Java中==和equals和hashCode的區別 

Object類的equal和hashCode方法重寫,爲什麼?

Java基本類型及其佔用空間

int與integer的區別

類變量初始化順序

抽象類的意義

接口和抽象類的區別

能否創建一個包含可變對象的不可變對象?

談談對java多態的理解

Java中String的瞭解

String、StringBuffer、StringBuilder區別

你對String對象的intern()熟悉麼?

String爲什麼要設計成不可變的?

泛型中extends和super的區別

進程和線程的區別

final,finally,finalize的區別

序列化的方式

如何將一個Java對象序列化到文件裏?

String 轉換成 integer的方式及原理

靜態屬性和靜態方法是否可以被繼承?是否可以被重寫?以及原因?

成員內部類、靜態內部類、局部內部類和匿名內部類的理解,以及項目中的應用

講一下常見編碼方式?

如何格式化日期?

Java的異常體系

什麼是異常鏈

throw和throws的區別

說說你對Java反射的理解

反射的原理,反射創建類實例的三種方式是什麼

反射中ClassLoader.loadClass和class.ForName區別

java當中的四種引用

深拷貝和淺拷貝的區別是什麼?

什麼是編譯期常量?使用它有什麼風險?

a=a+b與a+=b有什麼區別嗎?

靜態代理和動態代理的區別,什麼場景使用?

Java中實現多態的機制是什麼?

說說你對Java註解的理解

說說你對依賴注入的理解

說一下泛型原理,並舉例說明


 

Java基礎知識

面向對象三大特徵

面象對象的三大特徵

  • 繼承:子類繼承父類的特徵和行爲
  • 封裝:隱藏內部實現,暴露公共行爲
  • 多態:不同對象對一個動作有不同表現

 

面向對象六大原則

設計模式:面向對象設計的六大原則 (絕對詳細)

  • 單一職責原則 SRP:一個類只擔負一個職責
  • 開閉原則 OCP:類對擴展開放,對修改關閉
  • 裏式替換原則 LSP:引用基類的地方必須能透明地使用其子類對象
  • 依賴倒置原則 DIP:依賴抽象接口,不依賴具體實現
  • 接口隔離原則 ISP:客戶端不應該依賴它不需要的接口;一個類對另一個類的依賴應該建立在最小的接口上
  • 迪米特原則 LOD:低耦合,高內聚

 

Java可見性修飾符

Java 修飾符

public > protected > default > private

 

Java設計模式

設計模式 | 菜鳥教程

Java之美[從菜鳥到高手演變]之設計模式

Java之美[從菜鳥到高手演變]之設計模式二

Java之美[從菜鳥到高手演變]之設計模式三

Java之美[從菜鳥到高手演變]之設計模式四

 

Java中==和equals和hashCode的區別 

java中equals,hashcode和==的區別

1. 基本數據類型用==進行比較。

2. 引用使用==判斷是否指向同一個對象,也可覆蓋equals方法實現自定義判斷。

3. hashCode生成hash碼。同一對象一定生成相同的hash碼

4. hashCode和equals的關係

  • 同一對象上多次調用hashCode()方法,總是返回相同的整型值。

  • 如果a.equals(b),則一定有a.hashCode() 一定等於 b.hashCode()。 

  • 如果!a.equals(b),則a.hashCode() 不一定不等於 b.hashCode()。此時如果a.hashCode() 總是不等於 b.hashCode(),會提高hashtables的性能。

  • a.hashCode() == b.hashCode() 則 a.equals(b)可真可假

  • a.hashCode() != b.hashCode() 則 a.equals(b)爲假。 

 

Object類的equal和hashCode方法重寫,爲什麼?

  1. 如果兩個對象equals,Java運行時環境會認爲他們的hashcode一定相等。 
  2. 如果兩個對象不equals,他們的hashcode有可能相等。 
  3. 如果兩個對象hashcode相等,他們不一定equals。 
  4. 如果兩個對象hashcode不相等,他們一定不equals。 
  • 若重寫 equals(Object obj) 方法,有必要重寫 hashcode() 方法,確保通過 equals(Object obj) 方法判斷結果爲true的兩個對象具備相等的 hashcode() 返回值。說得簡單點就是:“如果兩個對象相同,那麼他們的 hashcode 應該相等”。不過請注意:這個只是規範,如果你非要寫一個類讓 equals(Object obj) 返回 true 而 hashcode() 返回兩個不相等的值,編譯和運行都是不會報錯的。不過這樣違反了Java規範,程序也就埋下了BUG。 
  •  一個很常見的錯誤根源在於沒有覆蓋 hashCode 方法。在每個覆蓋了 equals 方法的類中,也必須覆蓋 hashCode 方法。如果不這樣做的話,就會違反 Object.hashCode 的通用約定,從而導致該類無法結合所有基於散列的集合一起正常運作,這樣的集合包括 HashMap、HashSet 和 Hashtable。

 

Java基本類型及其佔用空間

byte 1字節
short 2字節
char 2字節
int 4字節
float 4字節
boolean 4字節
long 8字節
double 8字節

 

int與integer的區別

java面試題之int和Integer的區別

1. Integer是int的包裝類,int則是java的一種基本數據類型 

2. Integer變量必須實例化後才能使用,而int變量不需要 

3. Integer實際是對象的引用,當new一個Integer時,實際上是生成一個指針指向此對象;而int則是直接存儲數據值 

4. Integer的默認值是null,int的默認值是0

 

類變量初始化順序

  1. 父類顯式靜態初始化代碼塊
  2. 子類顯式靜態初始化代碼塊
  3. 父類非靜態實例初始化代碼塊
  4. 父類構造函數
  5. 子類非靜態實例初始化代碼塊
  6. 子類構造函數

 

抽象類的意義

一個類中如果包含抽象方法,這個類應該用abstract關鍵字聲明爲抽象類。

意義:

1. 爲子類提供一個公共的類型;

2. 封裝子類中重複內容(成員變量和方法);

3. 定義有抽象方法,子類雖然有不同的實現,但該方法的定義是一致的。

 

接口和抽象類的區別

深入理解Java的接口和抽象類

抽象類:

包含抽象方法的類要用abstract修飾(雖然沒有抽象方法也可以)。

和普通類一樣可以有普通成員變量和方法。

接口:

接口中所有的方法不能有具體的實現。

變量會被隱式地指定爲public static final變量

 

能否創建一個包含可變對象的不可變對象?

原公衆號放答案了,自己看吧?

Java面試題(二):你真的懂這幾道題了嗎?

 

談談對java多態的理解

java提高篇(四)-----理解java的三大特性之多態

JAVA重寫和重載的區別

概念:

同一操作作用於不同的對象,可以有不同的解釋,產生不同的執行結果,這就是多態性。

父類引用指向子類對象,可以提高代碼的靈活性和可擴展性。

實現條件:

繼承、重寫、向上轉型。

實現形式:

繼承和接口。

重載規則:必須具有不同的參數列表; 可以有不同的返回類型;可以有不同的訪問修飾符;可以拋出不同的異常。

重寫規則:參數列表必須完全相同;返回類型必須相同;訪問修飾符的限制一定要大於等於被重寫方法的訪問修飾符;重寫方法一定不能拋出新的檢查異常或者比被重寫方法申明更加寬泛的檢查型異常。

 

Java中String的瞭解

emmm有點泛,隨便貼個鏈接

深入理解Java中的String

 

String、StringBuffer、StringBuilder區別

圖析:String,StringBuffer與StringBuilder的區別

探祕Java中的String、StringBuilder以及StringBuffer

String:不可變,每次修改都會生成新的類

StringBuffer:可變,線程安全

StringBuilder:可變,線程不安全,速度快

 

你對String對象的intern()熟悉麼?

String的Intern方法詳解

intern() 對 String s = new String(...) 這樣的形式有效,要保證此時字符串在堆中而不在常量池中。

intern() 會先判斷字符串是否在常量池中,如果不是,則將字符串放入常量池中;如果在常量池中,則直接返回自身。

 

String爲什麼要設計成不可變的?

java中String類設計成不可變的原因

總結就是兩點:安全和性能

安全:不可變對象線程安全;作爲最常用的數據類型設置爲不可變可保證數據安全。

性能:不變性可引入常量池概念,複用字符串,節省空間,提高性能。

 

泛型中extends和super的區別

Java 泛型 <? super T> 中 super 怎麼 理解?與 extends 有何不同?

<? extends T>:

上界通配符(Upper Bounds Wildcards)

只能輸出T及其父類,也就是返回值爲T及其父類(只取不存)

<? super T>:

下界界通配符(Lower Bounds Wildcards)

只能輸入T及其子類,也就是傳入參數爲T及其子類(只存不取)

<?>:

既不能用於入參也不能用於返參

PECS原則:

Producer Extends Consumer Super

頻繁往外讀取內容的,適合用上界Extends。

經常往裏插入的,適合用下界Super。

 

進程和線程的區別

進程和線程的區別

進程:是併發執行的程序在執行過程中分配和管理資源的基本單位,是一個動態概念,競爭計算機系統資源的基本單位。

線程:是進程的一個執行單元,是進程內的調度實體。比進程更小的獨立運行的基本單位。線程也被稱爲輕量級進程。

一個程序至少一個進程,一個進程至少一個線程。

 

final,finally,finalize的區別

final、finally與finalize的區別

final:修飾符,可修飾類、方法、成員變量

finally:用於try/catch語句後,若try語句被執行則finally一定會執行

finalize:在java.lang.Object裏定義的,gc時調用,且只調用一次

 

序列化的方式

如何將一個Java對象序列化到文件裏?

Java中實現序列化的兩種方式 Serializable 接口和 Externalizable接口

Serializable:

一個對象的所有屬性包括私有屬性都可以序列化,可以使用 transient 關鍵詞排除序列化的屬性。

serialVersionUID 保證對象的一致性。

使用 ObjectInputStream 和 ObjectOutputStream 實現對象的序列化和反序列化。

Externalizable:

Serializable 接口的子類。

writeExternal() 和 readExternal() 方法可以指定序列化哪些屬性。

更多序列化方式:幾種Java序列化方式的實現

 

String 轉換成 integer的方式及原理

string轉換成integer的方式及原理

Integer.parseInt(String str)方法,具體思路其實就是一個字符一個字符的轉換。

要注意的就是邊界值的處理。

 

靜態屬性和靜態方法是否可以被繼承?是否可以被重寫?以及原因?

Java靜態屬性與靜態方法能否被繼承的問題

能被繼承,不能被重寫。

誰調用靜態函數,執行的靜態函數就屬於誰。

 

成員內部類、靜態內部類、局部內部類和匿名內部類的理解,以及項目中的應用

Java內部類詳解--成員內部類,局部內部類,匿名內部類,靜態內部類

成員內部類:

定義於一個類內部,依附於外部類存在。

可訪問外部類所有成員變量和成員方法,包括 private 和 static。

外部類訪問內部類需通過內部類對象的引用。

修飾符 private, protected, public。

局部內部類:

定義於方法或作用域內。

不能有修飾符。

只能訪問 final 局部變量。

匿名內部類:

在實現父類或接口時生成對象,沒有構造器。

不能有修飾符。

只能訪問 final 局部變量。

靜態內部類:

定義於外部類,有static關鍵字,不依附於外部類。

未持有外部類引用,不能使用外部類的非static成員變量或者方法。

 

講一下常見編碼方式?

Java幾種常見的編碼方式

常見的有 ASCII、ISO-8859-1、GB2312、GBK、UTF-8、UTF-16 等

 

如何格式化日期?

Java date format 時間格式化

通過 java.text.DateFormat 格式化日期。

// 2019年2月23日 星期六
String s1 = DateFormat.getDateInstance(DateFormat.FULL).format(new Date());
// 2019-02-23 10:59:29
String s2 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());

 

Java的異常體系

Java異常體系結構

你現在就必須知道的Java異常體系

異常類型:

Throwable 可分爲 Error 和 Exception。

Exception 可分爲 RuntimeException(不受檢異常) 和其他異常(受檢異常)。

異常捕獲:

try-catch-finally體系。

異常拋出:

throw 和 throws 關鍵詞。

異常轉譯與異常鏈

 

什麼是異常鏈

關於JAVA異常鏈的學習

捕獲一個異常後拋出另外一個異常,並且把異常原始信息保存下來,這被稱爲異常鏈。

Throwable及其子類的構造器可以接收一個cause參數追蹤到異常最初發生的位置。

 

throw和throws的區別

throw關鍵字是用於方法體內部,用來拋出一個Throwable類型的異常。如果拋出了檢查異常, 則還應該在方法頭部聲明方法可能拋出的異常類型。該方法的調用者也必須檢查處理拋出的異常。 如果所有方法都層層上拋獲取的異常,最終JVM會進行處理,處理也很簡單,就是打印異常消息和堆棧信息。 如果拋出的是Error或RuntimeException,則該方法的調用者可選擇處理該異常。有關異常的轉譯會在下面說明。

throws關鍵字用於方法體外部的方法聲明部分,用來聲明方法可能會拋出某些異常。僅當拋出了檢查異常, 該方法的調用者才必須處理或者重新拋出該異常。當方法的調用者無力處理該異常的時候,應該繼續拋出, 而不是囫圇吞棗一般在catch塊中打印一下堆棧信息做個勉強處理。

 

說說你對Java反射的理解

反射的原理,反射創建類實例的三種方式是什麼

java中的反射

【類反射】類反射原理和獲取Class對象的三種方式

反射:

Reflection(反射)是Java被視爲動態語言的關鍵,反射機制允許程序在執行期藉助於Reflection API取得任何類的內部信息,並能直接操作任意對象的內部屬性及方法。

Class類:

描述類的類,封裝了描述方法的 Method描述字段的 Filed,,描述構造器的 Constructor 等屬性。

Java高級特性——反射

Class類加載的三種方式:

try {
    Class c1 = new Food().getClass();
    Class c2 = Food.class;
    Class c3 = Class.forName("GenericTest.Food");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

 

反射中ClassLoader.loadClass和class.ForName區別

forName()會初始化類,將.class文件加載到jvm中,還會執行類中的static塊,還會執行給靜態變量賦值的靜態方法。

loadClass()只將class文件加載到jvm中,不會執行static中的內容,只有在newInstance纔會去執行static塊。

 

java當中的四種引用

詳解 Java 中的四種引用

強引用 - FinalReference:

最常見的引用。

String str1 = new String("str");

軟引用 - SoftReference:

在快報 OutOfMemoryError 異常時GC回收。

SoftReference<String> str2 = new SoftReference<>(new String("str"));

弱引用 - WeakReference:

只能活到下一次GC前,一旦發生GC就會被回收。

WeakReference<String> str3 = new WeakReference<>(new String("str"));

虛引用 - PhantomReference:

完全不影響對象的生命週期,爲一個對象設置虛引用關聯的唯一目的就是能在這個對象被收集器回收時收到一個系統通知

必須配合引用隊列使用。

ReferenceQueue<String> rq = new ReferenceQueue<>();
PhantomReference<String> str4 = new PhantomReference<>(new String("str"), rq);

引用隊列 - ReferenceQueue:

軟引用(SoftReference)和引用隊列(ReferenceQueue)

軟引用、弱引用和虛引用通過 get() 方法獲得對象的強引用,若對象已被回收,則返回 null。

若爲這三種引用指定引用隊列,則在對象回收後,會將空的引用放入引用隊列中以供後續處理。

 

深拷貝和淺拷貝的區別是什麼?

【Java深入】深拷貝與淺拷貝詳解

對象要克隆需要實現 Cloneable 接口。

淺拷貝只複製了對象本身,使得對象內部的引用可能指向同一個對象;

深拷貝將拷貝對象內部引用的對象也都複製一遍。

 

什麼是編譯期常量?使用它有什麼風險?

Java編譯期常量解釋及其使用風險

在編譯時就能確定這個常量的具體值,與運行時常量對應。

由於編譯期常量在編譯時就確定了值,編譯時會直接替換成對應的值。

對應到實際業務中,可能是我們的程序中使用了一個第三方庫中公有的編譯期常量時,如果對方更新了該常量的值,而我們隨後也只更新依賴的jar包,那麼我們的程序中該常量就是老值,就會產生隱患。爲了避免這種情況,在更新依賴的jar文件時,應該重新編譯我們的程序。

 

a=a+b與a+=b有什麼區別嗎?

[短文速讀] a=a+b和a+=b的區別

若 a 的範圍比 b 小,則 a += b 有自動的類型轉換,而 a =  a + b 沒有類型轉換,會報錯,需要強制轉換。除此之外基本相同。

 

靜態代理和動態代理的區別,什麼場景使用?

Java靜態代理與動態代理 理解與應用場景

動態代理與靜態代理區別

概念:

爲某個對象提供一個代理,以控制對這個對象的訪問。 代理類和委託類有共同的父類或父接口,這樣在任何使用委託類對象的地方都可以用代理對象替代。代理類負責請求的預處理、過濾、將請求分派給委託類處理、以及委託類執行完請求後的後續處理。 

角色:

接口

委託類:真正執行任務的類

代理類:代理類持有一個委託類的對象引用

靜態代理:

編譯期就確定代理關係

動態代理:

在代碼運行期間加載被代理的類

Proxy.newProxyInstance(接口類的ClassLoader, 接口類的class, 實現了InvocationHandler的類)

// 接口 java.lang.Runnable

// 委託類
class Peer implements Runnable {
    @Override
    public void run() {
        System.out.println("This is peer!");
    }
}

// 代理類
class Ih implements InvocationHandler {
    Object o;

    public Object bind(Object o) {
        this.o = o;
        return Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 方法執行前操作
        System.out.println("before");

        Object result = method.invoke(o, args);

        // 方法執行後
        System.out.println("after");

        return result;
    }
}

// 運行
Runnable p = (Runnable) new Ih().bind(new Peer());
p.run();

// 輸出
before 
This is peer! 
after 

 

Java中實現多態的機制是什麼?

方法的重寫 Overriding 和重載 Overloading 是Java多態性的不同表現。

重寫 Overriding 是父類與子類之間多態性的一種表現。

重載 Overloading 是一個類中多態性的一種表現。

 

說說你對Java註解的理解

Java註解基本原理

什麼是註解(Annotation):

Annotation(註解)就是Java提供了一種元程序中的元素關聯任何信息和着任何元數據(metadata)的途徑和方法。Annotion(註解)是一個接口,程序可以通過反射來獲取指定程序元素的Annotion對象,然後通過Annotion對象來獲取註解裏面的元數據。

Java SE5內置標準註解:

@Override,表示當前的方法定義將覆蓋超類中的方法。

@Deprecated,使用了註解爲它的元素編譯器將發出警告,因爲註解@Deprecated是不贊成使用的代碼,被棄用的代碼。

@SuppressWarnings,關閉不當編譯器警告信息。

用於創建註解的註解:

@Target

表示該註解可以用於什麼地方,可能的ElementType參數有:

CONSTRUCTOR:構造器的聲明

FIELD:域聲明(包括enum實例)

LOCAL_VARIABLE:局部變量聲明

METHOD:方法聲明

PACKAGE:包聲明

PARAMETER:參數聲明

TYPE:類、接口(包括註解類型)或enum聲明

@Retention

表示需要在什麼級別保存該註解信息。可選的RetentionPolicy參數包括:

SOURCE:註解將被編譯器丟棄

CLASS:註解在class文件中可用,但會被VM丟棄

RUNTIME:VM將在運行期間保留註解,因此可以通過反射機制讀取註解的信息。

@Document

將註解包含在Javadoc中

@Inherited

允許子類繼承父類中的註解

註解的創建:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface TestAnnotation {
    int id();
    String name() default "TestAnnotation";
}

註解的讀取:

@TestAnnotation(id = 1)
class AnnotationTest {
    @TestAnnotation(id = 2,name = "MethodAnnotation")
    void testFun() {}
}

// ----------------------------

Class cls = AnnotationTest.class;
TestAnnotation clsAnnotation = (TestAnnotation) cls.getAnnotation(TestAnnotation.class);
System.out.println("clsAnnotation " + clsAnnotation.id() + " " + clsAnnotation.name());

Method[] methods = cls.getDeclaredMethods();
for (Method m : methods) {
    TestAnnotation mAnnotation = m.getAnnotation(TestAnnotation.class);
    System.out.println("mAnnotation " + mAnnotation.id() + " "+ mAnnotation.name());
}

// 輸出
clsAnnotation 1 TestAnnotation
mAnnotation 2 MethodAnnotation

 

說說你對依賴注入的理解

依賴注入的簡單理解

這部分我對很多概念的印象還是糊的,以下請謹慎參考。

還是採用上文中的人開車的例子。

第一步:

一個人開豐田車,最基本的寫法就是人持有豐田車的引用。

class Person {
    Toyota toyota;

    Person(Toyota toyota) {
        this.toyota = toyota;
    }

    void drive() {
        toyota.run();
    }
}

第二步:

如果這個人不開豐田車了,改開Audi,則需改動代碼。改進這一點,就涉及到依賴倒置原則

  • 高層模塊不應該依賴低層模塊,兩者都應該依賴抽象
  • 抽象不應該依賴細節
  • 細節應該依賴抽象
class GoOut {
    void go() {
        // Toyota類實現了Car接口
        Toyota t = new Toyota();
        Person person = new Person(t);
        person.drive();
    }
}

class Person {
    Car car;

    Person(Car car) {
        this.car = car;
    }

    void drive() {
        car.run();
    }
}

第三步:

經過上一步改進,實現了Person和具體車型的解耦,只需改動GoOut類的代碼就可以更換Person的車型。如果要更進一步,可將依賴關係交由專門容器管理,即依賴注入。Spring實現IoC首先需要配置xml文件:

<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN"
    "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
   <bean id="oneCar" class="Toyota"> <!-- Toyota類是ICar的一個實現-->
   </bean>
   <bean id="onePerson" class="Person"> <!--本例以屬性方式注入爲例 -->
       <property name="car"> 
           <ref bean="oneCar"></ref>
       </property>
   </bean>
</beans>

然後使用 BeanFactroy 類調用:

class GoOut {
    void go() {
        BeanFactory factory=new XmlBeanFactory("bean.xml");
        Person boy=(Person)factory.getBean("onePerson");
        boy.drive();
    }
}

控制反轉和依賴注入的理解(通俗易懂)

 

說一下泛型原理,並舉例說明

java泛型(二)、泛型的內部原理:類型擦除以及類型擦除帶來的問題

類型擦除

 

 

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