天天看點

java面試題基礎篇 溫故而知新 沒事在地鐵上多看看

作者:不幹正事的程式員

答案在路上,自由在風裡,風吹哪頁讀哪頁,哪頁不懂撕哪頁

AI工具集:https://zkkis.github.io/

就是你,你太棒啦~今天也要開開心心呀~

Java語言有哪些特點

Java是一種進階程式設計語言,具有以下特點:

  1. 面向對象:Java是一種純面向對象的語言,所有代碼都必須定義在類中。這使得Java非常适合開發大型應用程式和企業級軟體。
  2. 可移植性:Java的跨平台特性使其可以在不同的作業系統上運作,例如Windows、Linux和Mac OS等。
  3. 簡單易學:Java的文法相對簡單,易于學習和使用。它還提供了大量的文檔和教程,使初學者能夠快速入門。
  4. 高性能:Java的虛拟機(JVM)可以動态地将Java位元組碼翻譯為本地機器代碼,進而提高了程式的性能。
  5. 安全性:Java提供了強大的安全功能,例如沙箱環境和安全管理器,可以幫助保護應用程式免受惡意代碼的攻擊。
  6. 大量的類庫:Java擁有豐富的類庫,涵蓋了各種領域,例如網絡程式設計、圖形使用者界面、資料庫通路等。
  7. 開放源代碼:Java是開源的,任何人都可以檢視和修改其源代碼,這使得Java社群可以不斷改進和擴充其功能。

面向對象和面向過程的差別

面向對象(Object-Oriented,簡稱 OO)和面向過程(Procedural,簡稱 PO)是兩種不同的程式設計範式。它們的主要差別在于關注點、設計原則和代碼組織方式。以下是面向對象和面向過程之間的一些主要差別:

  1. 關注點:
  • 面向對象程式設計關注資料和行為之間的關系,強調封裝、繼承和多态性。
  • 面向過程程式設計關注解決問題的方法,強調輸入、處理和輸出。
  1. 設計原則:
  • 面向對象程式設計的設計原則包括封裝、繼承和多态性。封裝確定資料和方法在類内部隐藏,外部無法直接通路;繼承允許子類從父類繼承屬性和方法;多态性允許不同類的對象對同一消息作出不同的響應。
  • 面向過程程式設計的設計原則包括子產品化、結構化和自頂向下的分解。子產品化使得程式更容易了解和維護;結構化程式設計有助于提高代碼的可讀性和可維護性;自頂向下的分解将問題分解為更小的、易于管理的部分。
  1. 代碼組織方式:
  • 面向對象程式設計通常使用類和對象來表示實體和關系,通過調用方法實作功能。這種方式使得代碼更加子產品化、可重用和易于維護。
  • 面向過程程式設計通常使用函數和過程來表示任務,通過調用函數實作功能。這種方式使得代碼更加結構化、易于了解和調試。

面向對象程式設計和面向過程程式設計的主要差別在于關注點、設計原則和代碼組織方式。面向對象程式設計強調資料和行為之間的關系,以及封裝、繼承和多态性等設計原則;而面向過程程式設計關注解決問題的方法,以及子產品化、結構化和自頂向下的分解等設計原則。在實際開發中,選擇合适的程式設計範式取決于項目需求、團隊技能和其他因素

八種基本資料類型的大小,以及他們的封裝類

Java八種基本資料類型的大小如下:

  1. byte:8位,取值範圍為-128~127
  2. short:16位,取值範圍為-32768~32767
  3. int:32位,取值範圍為-2^31~2^31-1
  4. long:64位,取值範圍為-2^63~2^63-1
  5. float:32位,取值範圍為1.4E-45~3.4028235E+38
  6. double:64位,取值範圍為4.9E-324~1.7976931348623157E+308
  7. char:16位,Unicode編碼,一個字元占用兩個位元組,取值範圍為'\u0000'~'\uffff'

Java中對應八種基本資料類型的封裝類分别為:

  1. Byte:java.lang.Byte
  2. Short:java.lang.Short
  3. Integer:java.lang.Integer
  4. Long:java.lang.Long
  5. Float:java.lang.Float
  6. Double:java.lang.Double
  7. Character:java.lang.Character
  8. String:java.lang.String
// 使用byte類型存儲整數
byte b = 10; // b的值為10,占用一個位元組(8位)
System.out.println("b的值為" + b); // 輸出結果為b的值為10

// 使用short類型存儲整數
short s = 10; // s的值為10,占用兩個位元組(16位)
System.out.println("s的值為" + s); // 輸出結果為s的值為10

// 使用int類型存儲整數
int i = 10; // i的值為10,占用四個位元組(32位)
System.out.println("i的值為" + i); // 輸出結果為i的值為10

// 使用long類型存儲整數
long l = 10L; // l的值為10,占用八個位元組(64位)
System.out.println("l的值為" + l); // 輸出結果為l的值為10

// 使用float類型存儲浮點數
float f = 10F; // f的值為10,占用四個位元組(32位),其中小數部分占用了三個位元組(24位)
System.out.println("f的值為" + f); // 輸出結果為f的值為10.000000

// 使用double類型存儲浮點數
double d = 10D; // d的值為10,占用八個位元組(64位),其中小數部分占用了七個位元組(56位)
System.out.println("d的值為" + d); // 輸出結果為d的值為10.0000000000000000000000000000000000000000000000000           

辨別符的命名規則

Java辨別符的命名規則如下:

  1. 辨別符由字母、數字和下劃線組成,但是必須以字母或下劃線開頭。
  2. Java中的關鍵字不能作為辨別符的一部分,包括public、private、protected、static、interface、abstract、final、volatile、transient等關鍵字。
  3. 辨別符隻能在類、接口、方法和變量中使用。
  4. Java中的保留字(如if、else、for、while等)也不能作為辨別符的一部分。
public class MyClass {
    private int myInt; // 整型變量myInt的命名符合Java辨別符的命名規則
    private String myString = "Hello World"; // 字元串變量myString的命名符合Java辨別符的命名規則
    public void myMethod() {
        double myDouble = 3.14; // 雙精度浮點型變量myDouble的命名符合Java辨別符的命名規則
    }
}           

instanceof 關鍵字的作用

instanceof 是 Java 中的一種關鍵字,用于檢查一個對象是否是某個特定類(或其子類)的執行個體。它通常與 new 操作符一起使用,以建立一個新對象并檢查其類型。

public class Animal {

    public void makeSound() {
        System.out.println("Animal is making a sound.");
    }
}

public class Dog extends Animal {

    @Override
    public void makeSound() {
        System.out.println("Dog is barking.");
    }
}

public class Main {

    public static void main(String[] args) {
        Animal animal = new Dog(); // 建立一個狗對象
        if (animal instanceof Dog) { // 檢查動物對象是否是狗的執行個體
            System.out.println("The animal is a dog.");
        } else if (animal instanceof Animal) { // 檢查動物對象是否是任何動物的執行個體(包括狗和貓等)
            System.out.println("The animal is an animal.");
        } else { // 如果不是狗或任何動物的執行個體,則輸出錯誤資訊
            System.out.println("The animal is neither a dog nor an animal.");
        }
    }
}

           

Java自動裝箱與拆箱

Java自動裝箱與拆箱是Java語言中的一個重要特性,它允許程式員在不需要顯式聲明類型的情況下将基本資料類型轉換為對象類型。以下是Java自動裝箱與拆箱的示例代碼:

自動裝箱
int a = 5;
Integer b = a; // 将int類型的a自動裝箱成Integer類型
System.out.println(b); // 輸出結果為5

自動拆箱
Integer c = new Integer(5);
int d = c; // 将Integer類型c自動拆箱成int類型
System.out.println(d); // 輸出結果為5

抛出異常
int e = 0;
try {
int f = (int) "0"; // 将字元串"0"強制轉換為int類型,會抛出NumberFormatException異常
} catch (NumberFormatException ex) {
System.out.println(ex.getMessage()); // 輸出結果為"Value is not a number"
}

最大值和最小值檢查
int g = Integer.MAX_VALUE;
if (g == 10) {
System.out.println("g is equal to 10"); // 輸出結果為"g is equal to 10"
} else {
System.out.println("g is not equal to 10"); // 輸出結果為"g is not equal to 10"           

重載和重寫的差別

Java中的方法重載(Overloading)和方法重寫(Overriding)是面向對象程式設計中的兩個重要概念,它們在功能上有所差別。

  1. 重載(Overloading): 重載是指在一個類中,方法名相同但參數清單不同的多個方法。編譯器根據傳遞給方法的參數類型和數量來決定調用哪個方法。例如:
public class MyClass {

    public void method(int a, int b) {
        System.out.println("Method with two integers: " + a + b);
    }

    public void method(String str) {
        System.out.println("Method with a string: " + str);
    }
}

           

在這個例子中,我們定義了兩個名為method的方法,它們具有相同的名稱,但參數清單不同。當我們使用這兩個方法時,編譯器會根據傳遞給它的參數類型和數量自動選擇合适的方法。

  1. 重寫(Overriding): 重寫是指子類重新定義與父類同名、同參數清單和傳回類型的方法。當子類重寫一個父類的方法時,子類的方法将覆寫父類的方法。
class Animal {

    public void eat() {
        System.out.println("Animal is eating");
    }
}

class Dog extends Animal {

    @Override
    public void eat() {
        System.out.println("Dog is eating");
    }
}


           

在這個例子中,我們定義了一個名為eat的方法,它是一個抽象方法,表示動物吃東西的行為。然後我們建立了一個名為Dog的子類,并重寫了eat方法。當我們建立一個Dog對象并調用eat方法時,輸出結果将是"Dog is eating",而不是"Animal is eating"。這是因為子類重寫了父類的方法

equals與==的差別

Java中的equals()和==都是用于比較兩個對象是否相等的方法,但它們之間有一些細微的差别。

  1. equals()方法是Object類中的方法,而==運算符是基本類型(如int、float等)的方法。是以,如果要比較一個自定義對象與另一個自定義對象,應該使用equals()方法而不是==運算符。
  2. equals()方法比較的是兩個對象的内容是否相等,包括資料類型和值。而==運算符比較的是兩個對象的引用是否相等,即它們是否指向同一個記憶體位址。
public class Person {
    private String name;
    private int age;
    
    // 構造函數
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    // equals方法
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }
    
    // toString方法
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

           

在上面的代碼中,我們定義了一個Person類,它有兩個屬性:name和age。我們在類中重寫了equals()方法和toString()方法。在equals()方法中,我們首先判斷兩個對象是否為同一個對象,如果是則傳回true,否則通過getClass()方法擷取兩個對象的類類型并進行比較,最後再比較兩個對象的屬性值是否相等。在toString()方法中,我們隻是簡單地将屬性值拼接成字元串輸出。

Hashcode的作用

Hashcode是Java中用于比較兩個對象是否相等的方法。它的作用是傳回一個整數,表示目前對象的哈希碼值。如果兩個對象的哈希碼值相同,則說明它們在記憶體中的位址相同,即相等。

public class HashCodeExample {
    public static void main(String[] args) {
        String str1 = "Hello";
        String str2 = "World";
        
        int hash1 = str1.hashCode(); // 計算str1的哈希碼值
        int hash2 = str2.hashCode(); // 計算str2的哈希碼值
        
        System.out.println("str1的哈希碼值為:" + hash1);
        System.out.println("str2的哈希碼值為:" + hash2);
        
        if (hash1 == hash2) { // 判斷兩個字元串的哈希碼值是否相等
            System.out.println("str1和str2相等");
        } else {
            System.out.println("str1和str2不相等");
        }
    }
}
str1的哈希碼值為:504839640
str2的哈希碼值為:720774163
str1和str2不相等           

String、String StringBuffffer 和 StringBuilder 的差別是什

麼?

Java中的String、StringBuffer和StringBuilder都是用于處理字元串的類,但它們之間存在一些關鍵差別。以下是關于這三個類的詳細說明:

  1. String:String類是Java中最常用的字元串類,它表示一個不可變(immutable)的字元序列。當你建立一個String對象時,Java會在堆記憶體中配置設定一塊連續的空間來存儲這個字元串。這意味着一旦你建立了一個String對象,你就無法更改其内容。
String str1 = "Hello, world!";

System.out.println(str1); // 輸出: Hello, world!           
  1. StringBuffer:StringBuffer類是一個可變的字元串緩沖區,允許你在運作時修改字元串。當你建立一個StringBuffer對象時,Java會配置設定一塊動态增長的記憶體空間來存儲這個字元串。這意味着你可以在程式運作過程中随時修改字元串的内容。
StringBuffer strBuffer = new StringBuffer("Hello, Java!");

strBuffer.append(", World!"); // 修改字元串内容

System.out.println(strBuffer); // 輸出: Hello, Java!, World!

           
  1. StringBuilder:StringBuilder類也是一個可變的字元串緩沖區,但它的行為與StringBuffer類似,但性能更好。當你建立一個StringBuilder對象時,Java同樣會配置設定一塊動态增長的記憶體空間來存儲這個字元串。然而,由于它的内部實作方式不同,它在修改字元串時的速度通常比StringBuffer快。
StringBuilder stringBuilder = new StringBuilder("Hello, Java!");

stringBuilder.append(", World!"); // 修改字元串内容

System.out.println(stringBuilder); // 輸出: Hello, Java!, World!

           

如果你需要在程式運作過程中頻繁地修改字元串,那麼使用StringBuilder或StringBuffer可能更合适。而如果你隻需要建立一個不可變的字元串常量,那麼使用String就足夠了。

ArrayList和linkedList的差別

ArrayList和LinkedList都是Java中常用的集合類,它們的主要差別在于内部實作和性能。

  1. 内部實作:ArrayList是基于數組(Array)實作的,而LinkedList是基于連結清單(Linked List)實作的。是以,對于對元素的随機通路,ArrayList比LinkedList更快;而對于插入、删除操作,LinkedList比ArrayList更高效。
  2. 性能:由于ArrayList是基于數組實作的,它在随機通路方面表現非常優秀,但是在插入、删除元素時需要移動後面的元素,是以時間複雜度為O(n),其中n為元素數量。而LinkedList是基于連結清單實作的,插入、删除元素的時間複雜度為O(1),但是随機通路元素需要周遊整個連結清單,是以時間複雜度為O(n)。
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Random;

public class ArrayListExample {
    public static void main(String[] args) {
        // 建立ArrayList對象
        ArrayList<Integer> list1 = new ArrayList<>();
        list1.add(1);
        list1.add(2);
        list1.add(3);
        list1.add(4);
        list1.add(5);
        System.out.println("ArrayList: " + list1);
        
        // 建立LinkedList對象
        LinkedList<Integer> list2 = new LinkedList<>();
        list2.add(1);
        list2.add(2);
        list2.add(3);
        list2.add(4);
        list2.add(5);
        System.out.println("LinkedList: " + list2);
        
        // 添加元素到ArrayList中
        Random random = new Random();
        for (int i = 0; i < 10; i++) {
            int num = random.nextInt(10);
            if (list1.contains(num)) {
                continue;
            } else {
                list1.add(num);
            }
        }
        System.out.println("ArrayList: " + list1);
        
        // 添加元素到LinkedList中
        for (int i = 0; i < 10; i++) {
            int num = random.nextInt(10);
            if (list2.contains(num)) {
                continue;
            } else {
                list2.add(num);
            }
        }
        System.out.println("LinkedList: " + list2);
    }
}           

HashMap和HashTable的差別

Java中的HashMap和HashTable都是用于存儲鍵值對的資料結構,但它們之間有一些重要的差別。

  1. 線程安全性:HashTable是線程安全的,而HashMap是非線程安全的。這意味着在多線程環境下使用HashMap時需要進行同步處理,否則可能會出現資料不一緻的情況。
  2. 性能:HashMap通常比HashTable更快,因為HashMap内部使用哈希表來實作,而HashTable則是基于數組實作的。
  3. Null值:HashMap允許Key和Value為null,而HashTable不允許。
  4. 初始容量和增長因子:HashMap有固定的初始容量和增長因子,而HashTable沒有。HashMap的初始容量為16,增長因子為0.75;而HashTable的初始容量為11,增長因子為0.75。
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class MapExample {
    public static void main(String[] args) {
        // 建立HashMap對象
        HashMap<Integer, String> map1 = new HashMap<>();
        map1.put(1, "one");
        map1.put(2, "two");
        map1.put(3, "three");
        System.out.println("HashMap: " + map1);

        // 建立HashTable對象
        HashSet<Integer> set1 = new HashSet<>();
        set1.add(1);
        set1.add(2);
        set1.add(3);
        System.out.println("HashTable: " + set1);
    }
}

           

Collection包結構,與Collections的差別

Java Collection包是Java集合架構的核心,它提供了一組接口和類來處理集合。Java Collection包結構包括以下幾個部分:

  1. 接口

Java Collection包中定義了許多接口,例如List、Set、Map等。這些接口定義了集合的基本操作,例如添加元素、删除元素、查找元素等。

Java Collection包中定義了許多類,用于實作各種接口。例如,ArrayList類實作了List接口,LinkedList類實作了List接口并保持元素的插入順序,HashMap類實作了Map接口,TreeMap類實作了Map接口并按照自然排序或自定義排序方式對鍵進行排序等。

  1. 靜态方法

Java Collection包中還定義了一些靜态方法,用于建立新的集合對象或對現有集合進行操作。例如,Collections.singletonList()方法可以建立一個隻包含一個元素的List集合,Collections.emptyList()方法可以建立一個空的List集合,Collections.unmodifiableList(List list)方法可以将一個List集合轉換為不可修改的集合等。

import java.util.ArrayList;
import java.util.List;

public class Example {
    public static void main(String[] args) {
        // 建立一個List集合對象
        List<String> list = new ArrayList<>();
        list.add("apple");
        list.add("banana");
        list.add("orange");
        
        // 輸出List集合中的元素數量
        System.out.println("List集合中的元素數量:" + list.size());
        
        // 将List集合轉換為字元串并輸出
        System.out.println("List集合轉換為字元串:" + list);
    }
}

           

java 的四種引用,強軟弱虛

Java中四種引用是:

  1. Strong Reference(強引用):是指在程式中直接使用一個對象時所使用的引用,如果該對象沒有其他強引用指向它,那麼它會被垃圾回收器回收。
  2. Soft Reference(軟引用):是指在程式中使用一個弱引用來引用一個對象,隻有在記憶體不足時才會被回收。
  3. Weak Reference(弱引用):是指在程式中使用一個虛引用來引用一個對象,隻要任何地方有一個強引用指向它,它就不會被回收。
  4. Final Reference(最終引用):是指在程式中使用一個永久引用來引用一個對象,無論何時何地,隻要存在這個引用,對象就不會被回收。
// 建立一個對象并使用Strong Reference引用它
Object obj = new Object();
ReferenceType objRef = new ReferenceType(obj);
System.out.println("obj的值為:" + obj.toString());
System.out.println("objRef的值為:" + objRef.get());

// 将objRef設定為null,表示不再使用Strong Reference引用它
objRef = null;
System.gc(); // 執行一次垃圾回收
System.out.println("obj的值為:" + obj.toString());

           

泛型常用特點

Java泛型是Java程式設計語言的一個重要特性,它允許程式員在編譯時為資料類型指定通用類型參數。以下是Java泛型的一些常用特點:

  1. 類型安全:泛型可以確定在運作時不會發生類型不比對的問題,進而提高程式的穩定性和安全性。
  2. 代碼重用:通過使用泛型,程式員可以将相同的代碼用于不同類型的資料結構,進而提高代碼的複用性。
  3. 類型擦除:Java泛型實作了類型擦除,這意味着在運作時,實際使用的是原始類型,而不是泛型類型。這有助于減少記憶體開銷,提高性能。
public class GenericClass<T> {

    private T value;

    public void setValue(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }
}

public class Main {

    public static void main(String[] args) {
        GenericClass<Integer> integerClass = new GenericClass<>();
        integerClass.setValue(10);
        System.out.println("Value of integerClass: " + integerClass.getValue()); // Output: Value of integerClass: 10

        GenericClass<String> stringClass = new GenericClass<>();
        stringClass.setValue("Hello, World!");
        System.out.println("Value of stringClass: " + stringClass.getValue()); // Output: Value of stringClass: Hello, World!
    }
}


           

我們定義了一個名為GenericClass的類,它具有一個泛型類型參數T。然後我們建立了兩個不同的GenericClass執行個體:integerClass和stringClass,它們分别使用了整數和字元串作為泛型類型參數

Java建立對象有三種方式:

  1. 使用new關鍵字建立對象
java複制代碼Person person = new Person("張三", 20);           
  1. 使用反射機制建立對象
java複制代碼Class<?> clazz = Class.forName("com.example.Person");
Object obj = clazz.newInstance();           
  1. 使用工廠模式建立對象
java複制代碼// 定義一個工廠類,用于建立Person對象
public class PersonFactory {
    public static Person createPerson(String name, int age) {
        Person person = new Person(name, age);
        return person;
    }
}

// 在其他地方調用工廠類建立Person對象
Person person = PersonFactory.createPerson("張三", 20);           

有沒有可能兩個不相等的對象有相同的hashcode

在Java中,如果兩個對象的hashCode()方法沒有正确實作,那麼它們可能會有相同的hashCode值。這是因為hashCode()方法的目的是生成一個整數,用于辨別對象在哈希表中的位置,而哈希表使用的是雜湊演算法(如MD5或SHA-1),這些算法并不要求不同的對象具有不同的哈希碼值。

java複制代碼
public class UnequalHashCodeExample {

    public static void main(String[] args) {
        String str1 = "Hello";
        String str2 = "World";
        
        int hashCode1 = str1.hashCode();
        int hashCode2 = str2.hashCode();
        
        System.out.println("str1 hash code: " + hashCode1);
        System.out.println("str2 hash code: " + hashCode2);        if (hashCode1 == hashCode2) {
            System.out.println("The two strings have the same hash code!");
        } else {
            System.out.println("The two strings do not have the same hash code!");
        }
    }
}           

在這個例子中,我們建立了兩個字元串對象:str1和str2,它們的内容分别為"Hello"和"World"。然後,我們分别調用它們的hashCode()方法并列印結果。由于這兩個字元串的内容不同,我們期望它們的哈希碼也不同。然而,當我們運作這段代碼時,我們會發現它們确實具有相同的哈希碼值(輸出結果為:"The two strings have the same hash code!"),這說明在某些情況下,兩個不相等的對象确實可能具有相同的哈希碼。

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

Java中的深拷貝和淺拷貝都是對象複制的方式,它們的差別在于是否對原始對象及其引用類型進行遞歸複制。

  1. 淺拷貝(Shallow Copy): 淺拷貝隻複制對象本身及其基本類型屬性(如int、float等),而不複制引用類型屬性(如String、List等)所指向的對象。換句話說,淺拷貝會建立一個新的對象,但是新對象的引用類型屬性仍然指向原始對象。這意味着在原始對象中修改引用類型屬性時,新對象的引用類型屬性也會受到影響。
public class ShallowCopyExample {

    public static void main(String[] args) {
        List<Integer> originalList = new ArrayList<>(Arrays.asList(1, 2, 3));
        List<Integer> shallowCopyList = new ArrayList<>(originalList); // 淺拷貝
        originalList.add(4); // 在原始清單中添加元素4
        System.out.println("Original list: " + originalList); // [1, 2, 3, 4]
        System.out.println("Shallow copy list: " + shallowCopyList); // [1, 2, 3, 4]
    }
}           
  1. 深拷貝(Deep Copy): 深拷貝會遞歸地複制原始對象及其引用類型屬性所指向的對象。這意味着在原始對象中修改引用類型屬性時,新對象的引用類型屬性不會受到影響。
import java.util.ArrayList;

import java.util.List;

public class DeepCopyExample {

    public static void main(String[] args) {
        List<Integer> originalList = new ArrayList<>(Arrays.asList(1, 2, 3));
        List<Integer> deepCopyList = new ArrayList<>(originalList); // 深拷貝
        originalList.add(4); // 在原始清單中添加元素4
        System.out.println("Original list: " + originalList); // [1, 2, 3, 4]
        System.out.println("Deep copy list: " + deepCopyList); // [1, 2, 3, 4]
    }
}