国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁(yè) > news >正文

網(wǎng)站是自己做還是讓別人仿愛(ài)鏈接網(wǎng)如何使用

網(wǎng)站是自己做還是讓別人仿,愛(ài)鏈接網(wǎng)如何使用,做網(wǎng)站犯法,phpcms手機(jī)網(wǎng)站怎么做目錄 Java面向?qū)ο笥心男┨卣?amp;#xff0c;如何應(yīng)用 Java基本數(shù)據(jù)類(lèi)型及所占字節(jié) Java中重寫(xiě)和重載有哪些區(qū)別 jdk1.8的新特性有哪些 內(nèi)部類(lèi) 1. 成員內(nèi)部類(lèi)(Member Inner Class): 2. 靜態(tài)內(nèi)部類(lèi)(Static Nested Class&#…

目錄

Java面向?qū)ο笥心男┨卣?#xff0c;如何應(yīng)用

Java基本數(shù)據(jù)類(lèi)型及所占字節(jié)

Java中重寫(xiě)和重載有哪些區(qū)別

jdk1.8的新特性有哪些

內(nèi)部類(lèi)

1. 成員內(nèi)部類(lèi)(Member Inner Class):

2.? ?靜態(tài)內(nèi)部類(lèi)(Static Nested Class):

靜態(tài)內(nèi)部類(lèi)的特點(diǎn):

靜態(tài)內(nèi)部類(lèi)和非靜態(tài)內(nèi)部類(lèi)的區(qū)別:

3. **局部?jī)?nèi)部類(lèi)(Local Inner Class)**:

4. **匿名內(nèi)部類(lèi)(Anonymous Inner Class)**:

泛型

final和static的區(qū)別

接口和抽象類(lèi)有哪些區(qū)別

怎樣聲明一個(gè)類(lèi)不會(huì)被繼承,什么場(chǎng)景下會(huì)用

深拷貝和淺拷貝

序列化

反射介紹

反射的步驟反射的步驟如下。

創(chuàng)建對(duì)象的幾種方式

@Contended注解有什么用

Java中有四種引用類(lèi)型

虛引用

Java中鎖的分類(lèi)

Java中==和equals有哪些區(qū)別

String、StringBuffer、StringBuilder區(qū)別及使用場(chǎng)景

String類(lèi)和常量池

String對(duì)象的兩種創(chuàng)建方式

3.2:String類(lèi)型的常量池比較特殊。

Java代理的幾種實(shí)現(xiàn)方式

靜態(tài)代理

第二種:動(dòng)態(tài)代理,包含JDK代理和CGLIB動(dòng)態(tài)代理

JDK代理

CGLIB動(dòng)態(tài)代理

JDK動(dòng)態(tài)代理和CGLIB兩種動(dòng)態(tài)代理的比較

hashcode和equals如何使用

異常分類(lèi)

Java異常處理方式

throw,throws的區(qū)別

自定義異常在生產(chǎn)中如何應(yīng)用

過(guò)濾器與攔截器的區(qū)別

Integer常見(jiàn)面試題

值傳遞和引用傳遞有什么區(qū)別

集合

?集合和數(shù)組的區(qū)別

集合框架底層數(shù)據(jù)結(jié)構(gòu)

線(xiàn)程安全的集合

HashMap的put方法的具體流程?

HashMap原理是什么,在jdk1.7和1.8中有什么區(qū)別

HashMap和HashTable的區(qū)別及底層實(shí)現(xiàn)

HashMap和HashTable對(duì)比

HashMap擴(kuò)容優(yōu)化:

為什么hashmap擴(kuò)容的時(shí)候是兩倍?

hashmap線(xiàn)程安全的方式?

說(shuō)一下 HashSet 的實(shí)現(xiàn)原理? - HashSet如何檢查重復(fù)?HashSet是如何保證數(shù)據(jù)不可重復(fù)的?

ArrayList和LinkedList有什么區(qū)別

ArrayList擴(kuò)容

Array和ArrayList的區(qū)別

List和數(shù)組之間的轉(zhuǎn)換

數(shù)組類(lèi)型和集合

高并發(fā)中的集合有哪些問(wèn)題

ConcurrentHashMap底層原理是什么?



Java面向?qū)ο笥心男┨卣?#xff0c;如何應(yīng)用

  1. 封裝(Encapsulation):封裝是指將數(shù)據(jù)和對(duì)數(shù)據(jù)的操作封裝在對(duì)象內(nèi)部,隱藏其具體實(shí)現(xiàn)細(xì)節(jié),并通過(guò)公共接口進(jìn)行訪(fǎng)問(wèn)。封裝可以提高代碼的安全性、可維護(hù)性和可復(fù)用性。

  2. 繼承(Inheritance):繼承是指允許一個(gè)類(lèi)繼承另一個(gè)類(lèi)的屬性和方法。通過(guò)繼承,子類(lèi)可以獲得父類(lèi)的屬性和方法,并可以在此基礎(chǔ)上進(jìn)行擴(kuò)展或修改。繼承實(shí)現(xiàn)了代碼的重用和層次化組織。

  3. 多態(tài)(Polymorphism):多態(tài)是指同一個(gè)類(lèi)型的對(duì)象在不同的情況下表現(xiàn)出不同的行為。通過(guò)多態(tài),可以在編譯時(shí)不確定具體的對(duì)象類(lèi)型,而在運(yùn)行時(shí)確定調(diào)用的方法。多態(tài)使得代碼具有靈活性和擴(kuò)展性。

  4. 抽象(Abstraction):抽象是指從對(duì)象的共同特征中提取出抽象類(lèi)或接口,用來(lái)描述一組相關(guān)的對(duì)象。抽象類(lèi)和接口定義了對(duì)象的共同行為和規(guī)范,可以通過(guò)繼承和實(shí)現(xiàn)來(lái)實(shí)現(xiàn)具體的功能。

如何應(yīng)用Java面向?qū)ο蟮奶卣?#xff1a;

  1. 封裝:將相關(guān)的數(shù)據(jù)和行為封裝在對(duì)象內(nèi)部,通過(guò)合適的訪(fǎng)問(wèn)修飾符(例如private、protected、public)限制訪(fǎng)問(wèn)權(quán)限。同時(shí),提供合適的公共方法來(lái)操作對(duì)象的數(shù)據(jù)。

  2. 繼承:通過(guò)使用extends關(guān)鍵字來(lái)實(shí)現(xiàn)繼承關(guān)系,讓子類(lèi)繼承父類(lèi)的屬性和方法??梢允褂美^承來(lái)實(shí)現(xiàn)代碼的重用和層次化組織。

  3. 多態(tài):通過(guò)使用多態(tài),可以根據(jù)不同的實(shí)際對(duì)象類(lèi)型來(lái)調(diào)用相應(yīng)的方法,實(shí)現(xiàn)不同的行為??梢酝ㄟ^(guò)方法的重寫(xiě)(Override)和接口的實(shí)現(xiàn)(Implement)來(lái)實(shí)現(xiàn)多態(tài)。

  4. 抽象:當(dāng)遇到一組有共同特征的對(duì)象時(shí),可以使用抽象類(lèi)或接口來(lái)定義這些對(duì)象的共同行為和規(guī)范。通過(guò)繼承和實(shí)現(xiàn)來(lái)實(shí)現(xiàn)具體的功能。

以上是Java面向?qū)ο蟮奶卣骱腿绾螒?yīng)用的簡(jiǎn)要介紹。在實(shí)際開(kāi)發(fā)中,根據(jù)具體情況靈活應(yīng)用這些特征,可以使代碼更加有組織、可擴(kuò)展和易維護(hù)。

Java基本數(shù)據(jù)類(lèi)型及所占字節(jié)

Java中重寫(xiě)和重載有哪些區(qū)別

在Java中,重寫(xiě)(Override)和重載(Overload)是兩個(gè)常用的概念,用于實(shí)現(xiàn)多態(tài)性。它們之間的區(qū)別如下:

  1. 重寫(xiě)(Override):

    • 重寫(xiě)指的是子類(lèi)重新定義了父類(lèi)中已有的方法,具有相同的方法名、參數(shù)列表和返回類(lèi)型。

    • 重寫(xiě)方法必須在繼承關(guān)系中存在,即子類(lèi)覆蓋父類(lèi)的方法。

    • 重寫(xiě)方法的訪(fǎng)問(wèn)修飾符不能比父類(lèi)更嚴(yán)格,可以更寬松或相同。

    • 重寫(xiě)方法不能拋出比父類(lèi)更寬泛的異常。

    • 在運(yùn)行時(shí),根據(jù)對(duì)象的實(shí)際類(lèi)型調(diào)用對(duì)應(yīng)的重寫(xiě)方法,實(shí)現(xiàn)多態(tài)性。

  2. 重載(Overload):

    • 重載指的是在同一個(gè)類(lèi)中定義了多個(gè)具有相同名字但參數(shù)列表不同的方法。

    • 重載方法的返回類(lèi)型可以相同也可以不同,但不能僅僅通過(guò)返回類(lèi)型來(lái)區(qū)分方法。

    • 重載方法的訪(fǎng)問(wèn)修飾符可以相同也可以不同。

    • 重載方法可以?huà)伋鋈我獾漠惓!?/p>

    • 在編譯時(shí),根據(jù)方法調(diào)用時(shí)提供的參數(shù)類(lèi)型和數(shù)量來(lái)確定調(diào)用哪個(gè)重載方法。

總結(jié)來(lái)說(shuō),重寫(xiě)用于子類(lèi)重新定義父類(lèi)方法的實(shí)現(xiàn),而重載用于同一個(gè)類(lèi)中根據(jù)參數(shù)的不同來(lái)定義多個(gè)方法。重寫(xiě)是實(shí)現(xiàn)多態(tài)性的關(guān)鍵,而重載則提供了更多的靈活性和便利性。

jdk1.8的新特性有哪些

Java 8 在發(fā)布時(shí)引入了許多新的語(yǔ)言特性和 API 改進(jìn)。以下是 JDK 1.8 中一些主要的新特性:

1. **Lambda 表達(dá)式**:Lambda 表達(dá)式是 Java 8 中引入的一項(xiàng)重要特性,它簡(jiǎn)化了匿名內(nèi)部類(lèi)的使用,使代碼更加簡(jiǎn)潔、易讀。Lambda 表達(dá)式可以在函數(shù)式接口中使用,通過(guò)箭頭符號(hào) "->" 將參數(shù)和方法體分隔開(kāi)。

2. **Stream API**:Stream 是 Java 8 中引入的用于處理集合數(shù)據(jù)的 API,提供了豐富的中間操作和結(jié)束操作,可以使代碼更具表現(xiàn)力和可讀性,并且支持并行操作。

3. **方法引用**:方法引用是一種簡(jiǎn)化 Lambda 表達(dá)式的語(yǔ)法,可以直接引用已有方法作為 Lambda 表達(dá)式的實(shí)現(xiàn)。

4. **接口的默認(rèn)方法和靜態(tài)方法**:在 Java 8 中,接口可以定義默認(rèn)方法和靜態(tài)方法,使接口可以包含具體實(shí)現(xiàn)而不僅僅是抽象方法,這樣可以更好地支持接口的擴(kuò)展和演進(jìn)。

5. **Optional 類(lèi)**:Optional 類(lèi)是一個(gè)容器類(lèi),用于處理可能為空的值,避免空指針異常,并鼓勵(lì)更好的代碼實(shí)踐。

6. **新的日期和時(shí)間 API**:Java 8 引入了新的日期時(shí)間 API(java.time 包),提供了更好的日期和時(shí)間處理方式,包括不可變性、線(xiàn)程安全性和清晰的設(shè)計(jì)。

7. **CompletableFuture 類(lèi)**:CompletableFuture 是 Java 8 中引入的用于異步編程的類(lèi),通過(guò)它可以更容易地實(shí)現(xiàn)并發(fā)和異步操作。

8. **重復(fù)注解**:Java 8 允許在相同的地方多次使用同一個(gè)注解,這樣可以避免代碼中出現(xiàn)大量相同的注解。

9. **Java 類(lèi)庫(kù)的改進(jìn)**:Java 8 中還做了許多類(lèi)庫(kù)的改進(jìn)和增強(qiáng),包括新的工具類(lèi)、函數(shù)式接口、默認(rèn)方法等。

Java 8 的這些新特性使得 Java 編程變得更加現(xiàn)代化、高效和簡(jiǎn)潔,提升了開(kāi)發(fā)人員的編碼體驗(yàn)和生產(chǎn)效率。

內(nèi)部類(lèi)

在 Java 中,有四種類(lèi)型的內(nèi)部類(lèi),它們分別是:成員內(nèi)部類(lèi)(Member Inner Class)、靜態(tài)內(nèi)部類(lèi)(Static Nested Class)、局部?jī)?nèi)部類(lèi)(Local Inner Class)和匿名內(nèi)部類(lèi)(Anonymous Inner Class)。下面我會(huì)分別介紹這四種內(nèi)部類(lèi),并為每種內(nèi)部類(lèi)舉一個(gè)簡(jiǎn)單的代碼示例:

1. 成員內(nèi)部類(lèi)(Member Inner Class):


? ?- 成員內(nèi)部類(lèi)是定義在另一個(gè)類(lèi)中的普通類(lèi),可以訪(fǎng)問(wèn)外部類(lèi)的實(shí)例成員和方法。
? ?
```java
public class Outer {
? ? private int outerField;

? ? public class Inner {
? ? ? ? public void display() {
? ? ? ? ? ? System.out.println("OuterField: " + outerField);
? ? ? ? }
? ? }
}
```

2.? ?靜態(tài)內(nèi)部類(lèi)(Static Nested Class):


? ?- 靜態(tài)內(nèi)部類(lèi)是嵌套在外部類(lèi)中并被聲明為 static 的類(lèi),可以直接通過(guò)外部類(lèi)訪(fǎng)問(wèn)靜態(tài)內(nèi)部類(lèi)。
? ?
```java
public class Outer {
? ? private static int outerStaticField;

? ? public static class StaticInner {
? ? ? ? public void display() {
? ? ? ? ? ? System.out.println("OuterStaticField: " + outerStaticField);
? ? ? ? }
? ? }
}
```

靜態(tài)內(nèi)部類(lèi)是嵌套在外部類(lèi)中并被聲明為 static 的類(lèi),它和非靜態(tài)內(nèi)部類(lèi)有一些特點(diǎn)和區(qū)別:

靜態(tài)內(nèi)部類(lèi)的特點(diǎn):


1. 靜態(tài)內(nèi)部類(lèi)可以直接通過(guò)外部類(lèi)訪(fǎng)問(wèn),無(wú)需實(shí)例化外部類(lèi)。
2. 靜態(tài)內(nèi)部類(lèi)不能訪(fǎng)問(wèn)外部類(lèi)的非靜態(tài)成員,但可以訪(fǎng)問(wèn)外部類(lèi)的靜態(tài)成員。
3. 靜態(tài)內(nèi)部類(lèi)的實(shí)例可以獨(dú)立存在,不依賴(lài)于外部類(lèi)的實(shí)例。
4. 靜態(tài)內(nèi)部類(lèi)通常用來(lái)作為外部類(lèi)的幫助類(lèi),或者與外部類(lèi)相關(guān)但又不依賴(lài)于外部類(lèi)實(shí)例的邏輯。

靜態(tài)內(nèi)部類(lèi)和非靜態(tài)內(nèi)部類(lèi)的區(qū)別:


1. **訪(fǎng)問(wèn)外部類(lèi)成員**:靜態(tài)內(nèi)部類(lèi)不能直接訪(fǎng)問(wèn)外部類(lèi)的非靜態(tài)成員變量和方法,而非靜態(tài)內(nèi)部類(lèi)可以直接訪(fǎng)問(wèn)外部類(lèi)的所有成員。
2. **實(shí)例化**:靜態(tài)內(nèi)部類(lèi)的實(shí)例不依賴(lài)于外部類(lèi)的實(shí)例,可以直接使用outerClass.StaticInnerClass的方式實(shí)例化,而非靜態(tài)內(nèi)部類(lèi)需要通過(guò)外部類(lèi)的實(shí)例來(lái)創(chuàng)建。
3. **靜態(tài)性**:靜態(tài)內(nèi)部類(lèi)本身就是靜態(tài)的,因此可以包含靜態(tài)成員,而非靜態(tài)內(nèi)部類(lèi)無(wú)法包含靜態(tài)成員。
4. **使用場(chǎng)景**:靜態(tài)內(nèi)部類(lèi)適合作為獨(dú)立實(shí)體存在,或者與外部類(lèi)無(wú)關(guān)但又需要在同一文件中定義的類(lèi);非靜態(tài)內(nèi)部類(lèi)通常用于與外部類(lèi)有關(guān)聯(lián)的邏輯,需要訪(fǎng)問(wèn)外部類(lèi)的實(shí)例成員。

總的來(lái)說(shuō),靜態(tài)內(nèi)部類(lèi)和非靜態(tài)內(nèi)部類(lèi)都有各自的優(yōu)點(diǎn)和適用場(chǎng)景,選擇哪種方式取決于需求和設(shè)計(jì)目的。靜態(tài)內(nèi)部類(lèi)通常用于幫助類(lèi)或獨(dú)立實(shí)體,而非靜態(tài)內(nèi)部類(lèi)通常用于與外部類(lèi)相關(guān)聯(lián)的邏輯。

3. **局部?jī)?nèi)部類(lèi)(Local Inner Class)**:


? ?- 局部?jī)?nèi)部類(lèi)是定義在方法內(nèi)部的類(lèi),只能在包含它的方法中使用,通常用于解決特定問(wèn)題或局部邏輯。
??
```java
public class Outer {
? ? public void display() {
? ? ? ? class LocalInner {
? ? ? ? ? ? public void show() {
? ? ? ? ? ? ? ? System.out.println("Local Inner Class");
? ? ? ? ? ? }
? ? ? ? }
? ? ? ??
? ? ? ? LocalInner localInner = new LocalInner();
? ? ? ? localInner.show();
? ? }
}
```

4. **匿名內(nèi)部類(lèi)(Anonymous Inner Class)**:

匿名內(nèi)部類(lèi)是一種沒(méi)有顯示定義類(lèi)名的內(nèi)部類(lèi),通常在創(chuàng)建對(duì)象的同時(shí)定義類(lèi)并實(shí)例化對(duì)象,適用于只需要一次性使用的情況。以下是匿名內(nèi)部類(lèi)的特點(diǎn):

1. **沒(méi)有類(lèi)名**:匿名內(nèi)部類(lèi)沒(méi)有類(lèi)名,通常直接在使用的地方通過(guò) new 關(guān)鍵字創(chuàng)建對(duì)象并定義類(lèi)。
? ?
2. **實(shí)現(xiàn)接口或繼承父類(lèi)**:匿名內(nèi)部類(lèi)通常用于實(shí)現(xiàn)接口或繼承父類(lèi),并在創(chuàng)建對(duì)象時(shí)直接實(shí)現(xiàn)接口方法或重寫(xiě)父類(lèi)方法。

3. **可以訪(fǎng)問(wèn)外部類(lèi)的成員**:匿名內(nèi)部類(lèi)可以訪(fǎng)問(wèn)外部類(lèi)的成員變量和方法,但是需要這些成員變量和方法是 final 或是 effectively final 的(Java 8 之后允許訪(fǎng)問(wèn)非 final 的局部變量)。

4. **一次性使用**:匿名內(nèi)部類(lèi)適用于只需要一次性使用、不需要長(zhǎng)期保存引用的情況,可以簡(jiǎn)化代碼結(jié)構(gòu)。

5. **可以引用外部類(lèi)的局部變量**:Java 8 之后,匿名內(nèi)部類(lèi)可以訪(fǎng)問(wèn)外部方法中的局部變量,前提是這些局部變量需要是 final 或 effectively final 的。

6. **簡(jiǎn)化代碼**:匿名內(nèi)部類(lèi)可以減少編寫(xiě)類(lèi)定義的代碼量,并且可以更直觀地展現(xiàn)代碼邏輯。

雖然匿名內(nèi)部類(lèi)在某些情況下能夠帶來(lái)便利,但也應(yīng)該注意避免濫用匿名內(nèi)部類(lèi),特別是在邏輯復(fù)雜或需要復(fù)用的情況下,最好還是考慮使用具名的內(nèi)部類(lèi)或獨(dú)立類(lèi)來(lái)實(shí)現(xiàn)相應(yīng)的功能。

```java
public class Outer {
? ? public void display() {
? ? ? ? Runnable runnable = new Runnable() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? System.out.println("Anonymous Inner Class");
? ? ? ? ? ? }
? ? ? ? };
? ? ? ??
? ? ? ? new Thread(runnable).start();
? ? }
}
```

以上是四種內(nèi)部類(lèi)的簡(jiǎn)單介紹和代碼示例,通過(guò)使用不同類(lèi)型的內(nèi)部類(lèi),可以實(shí)現(xiàn)更靈活的代碼設(shè)計(jì)和結(jié)構(gòu)化。每種內(nèi)部類(lèi)都有不同的應(yīng)用場(chǎng)景和特性,可以根據(jù)實(shí)際需求選擇合適的內(nèi)部類(lèi)類(lèi)型。

泛型

Java中的泛型是一種類(lèi)型參數(shù)化的機(jī)制,允許在類(lèi)、接口和方法中使用參數(shù)化類(lèi)型。通過(guò)使用泛型,可以將類(lèi)型的具體實(shí)例化延遲到使用時(shí),提高代碼的靈活性、可復(fù)用性和類(lèi)型安全性。

Java的泛型主要包括以下幾個(gè)方面:

  1. 泛型類(lèi)(Generic Class): 使用泛型類(lèi)可以在定義類(lèi)時(shí)指定一個(gè)或多個(gè)類(lèi)型參數(shù),這些參數(shù)可以在類(lèi)內(nèi)部作為類(lèi)型的占位符使用。使用泛型類(lèi)可以創(chuàng)建具有不同類(lèi)型參數(shù)的實(shí)例,從而提供了更靈活的數(shù)據(jù)類(lèi)型支持。

    例如,在定義一個(gè)List時(shí)可以使用泛型參數(shù)來(lái)指定列表元素類(lèi)型,如List<String>表示元素類(lèi)型為字符串的列表。

  2. 泛型接口(Generic Interface): 泛型接口與泛型類(lèi)類(lèi)似,可以在接口定義中使用類(lèi)型參數(shù)。通過(guò)泛型接口,可以創(chuàng)建實(shí)現(xiàn)指定類(lèi)型參數(shù)的接口的實(shí)例。

    例如,Comparable<T> 是一個(gè)泛型接口,用于實(shí)現(xiàn)可比較的對(duì)象。其中的類(lèi)型參數(shù)T表示待比較的對(duì)象的類(lèi)型。

  3. 泛型方法(Generic Method): 泛型方法可以在方法內(nèi)部獨(dú)立地使用泛型類(lèi)型,可以有自己的類(lèi)型參數(shù)。使用泛型方法可以在方法調(diào)用時(shí)指定不同的類(lèi)型,并在方法內(nèi)部進(jìn)行參數(shù)和返回類(lèi)型的類(lèi)型推斷。

    例如,Collections類(lèi)中的sort方法就是一個(gè)泛型方法,可以對(duì)不同類(lèi)型的數(shù)組進(jìn)行排序。它根據(jù)方法調(diào)用時(shí)傳入的參數(shù)類(lèi)型進(jìn)行類(lèi)型推斷。

  4. 通配符和上界(Wildcard and Upper Bound): 在使用泛型時(shí),可以使用通配符?來(lái)表示未知的類(lèi)型,通常用于讀取操作。通過(guò)使用上界,可以限制通配符所代表的類(lèi)型的范圍。

    例如,List<?>表示一個(gè)未知類(lèi)型的列表,可以獲取列表中的元素,但無(wú)法添加任何元素。而List<? extends Number>表示一個(gè)類(lèi)型為Number及其子類(lèi)的列表,限制了可以添加的元素類(lèi)型。

泛型的優(yōu)勢(shì)包括代碼復(fù)用性高、提高代碼的類(lèi)型安全性、減少類(lèi)型轉(zhuǎn)換的錯(cuò)誤以及提供更強(qiáng)大的編譯時(shí)類(lèi)型檢查。通過(guò)在Java中使用泛型,可以編寫(xiě)更靈活和可維護(hù)的代碼,并提高代碼的可讀性和可擴(kuò)展性。

final和static的區(qū)別

finalstatic是Java中兩個(gè)關(guān)鍵字,它們有不同的用途和含義:

  1. final關(guān)鍵字:

    • 修飾變量:final修飾的變量表示一個(gè)最終的常量,即不可再改變的值。一旦被賦初值后,該變量的值不能再被修改。final變量通常用大寫(xiě)字母命名,并在聲明時(shí)或構(gòu)造函數(shù)中進(jìn)行初始化。

    • 修飾方法:final修飾的方法表示該方法是最終方法,子類(lèi)無(wú)法對(duì)其進(jìn)行重寫(xiě)。該方法在繼承關(guān)系中起到穩(wěn)定和約束的作用。

    • 修飾類(lèi):final修飾的類(lèi)表示該類(lèi)是最終類(lèi),不能被繼承。該類(lèi)一般是不希望被修改或繼承的基礎(chǔ)類(lèi)。

  2. static關(guān)鍵字:

    • 修飾變量:static修飾的變量是靜態(tài)變量(類(lèi)變量),它屬于類(lèi)而不屬于對(duì)象。靜態(tài)變量在內(nèi)存中只有一個(gè)副本,被所有對(duì)象共享??梢酝ㄟ^(guò)類(lèi)名直接訪(fǎng)問(wèn)靜態(tài)變量,無(wú)需創(chuàng)建實(shí)例。

    • 修飾方法:static修飾的方法是靜態(tài)方法(類(lèi)方法),它屬于類(lèi)而不屬于對(duì)象。靜態(tài)方法不依賴(lài)對(duì)象的實(shí)例,無(wú)法訪(fǎng)問(wèn)非靜態(tài)成員變量,只能訪(fǎng)問(wèn)類(lèi)的靜態(tài)成員??梢灾苯邮褂妙?lèi)名調(diào)用靜態(tài)方法。

    • 修飾代碼塊:static修飾的代碼塊是靜態(tài)代碼塊,它在類(lèi)初始化時(shí)執(zhí)行,且只執(zhí)行一次。

主要區(qū)別:

  • final關(guān)鍵字表示最終性,用于修飾不可變的變量、最終方法以及不可繼承的類(lèi),強(qiáng)調(diào)不可修改或擴(kuò)展的特性。

  • static關(guān)鍵字表示靜態(tài)性,用于修飾類(lèi)級(jí)別的變量、方法和代碼塊,強(qiáng)調(diào)共享和類(lèi)級(jí)別的訪(fǎng)問(wèn)方式。

總之,finalstatic在Java中有不同的用途和含義,final修飾的是最終性和不可修改的特性,而static修飾的是靜態(tài)性和共享性的特性。

雖然finalstatic在Java中的用途和含義不同,但它們也有一些相同點(diǎn):

  1. 共享性:無(wú)論是final還是static修飾的成員(變量、方法或代碼塊),它們都是類(lèi)級(jí)別的,即在類(lèi)的所有實(shí)例之間共享。

  2. 靜態(tài)訪(fǎng)問(wèn):final修飾的成員以及static修飾的成員,都可以通過(guò)類(lèi)名直接訪(fǎng)問(wèn),不需要實(shí)例化對(duì)象。

  3. 聲明周期:finalstatic修飾的成員都在類(lèi)初始化時(shí)創(chuàng)建,并且在整個(gè)程序的生命周期中保持不變。

  4. 常量:final修飾的變量可以用來(lái)表示常量,而靜態(tài)常量常常使用finalstatic一起修飾,用于表示類(lèi)級(jí)別的常量。

雖然這些相同點(diǎn)存在,但要注意的是,finalstatic的主要作用是不同的。final主要用于表示最終性和不可修改性,而static主要用于表示靜態(tài)性和共享性。它們的使用場(chǎng)景和語(yǔ)義上仍然有所區(qū)別。

接口和抽象類(lèi)有哪些區(qū)別

接口(Interface)和抽象類(lèi)(Abstract Class)是面向?qū)ο缶幊讨械膬蓚€(gè)重要概念,它們之間有以下區(qū)別:

  1. 定義方式:

    • 接口:接口只能定義抽象方法和常量,不能包含具體的方法實(shí)現(xiàn)。接口中的方法默認(rèn)為public abstract,常量默認(rèn)為public static final,不需要顯式聲明。

    • 抽象類(lèi):抽象類(lèi)可以包含抽象方法和具體方法的聲明,也可以包含成員變量。抽象類(lèi)通過(guò)使用abstract關(guān)鍵字來(lái)聲明抽象方法,不需要顯式標(biāo)識(shí)成員變量和具體方法。

  2. 繼承關(guān)系:

    • 接口:一個(gè)類(lèi)可以實(shí)現(xiàn)(implement)多個(gè)接口,通過(guò)關(guān)鍵字implements來(lái)實(shí)現(xiàn)接口。接口之間可以實(shí)現(xiàn)多繼承,一個(gè)接口可以繼承多個(gè)其他接口。一個(gè)類(lèi)實(shí)現(xiàn)接口時(shí),必須實(shí)現(xiàn)接口中定義的所有方法。

    • 抽象類(lèi):一個(gè)類(lèi)可以繼承(extends)一個(gè)抽象類(lèi),通過(guò)關(guān)鍵字extends來(lái)繼承抽象類(lèi)。抽象類(lèi)之間只能實(shí)現(xiàn)單繼承,一個(gè)抽象類(lèi)只能繼承一個(gè)其他類(lèi)或抽象類(lèi)。子類(lèi)繼承抽象類(lèi)時(shí),必須實(shí)現(xiàn)抽象類(lèi)中的抽象方法。

  3. 實(shí)例化對(duì)象:

    • 接口:接口不能直接被實(shí)例化,即不能通過(guò)new關(guān)鍵字來(lái)創(chuàng)建接口的對(duì)象。但可以通過(guò)實(shí)現(xiàn)接口的類(lèi)來(lái)創(chuàng)建對(duì)象,并將其賦給接口類(lèi)型的引用。

    • 抽象類(lèi):抽象類(lèi)不能直接被實(shí)例化,即不能通過(guò)new關(guān)鍵字來(lái)創(chuàng)建抽象類(lèi)的對(duì)象。但可以通過(guò)實(shí)現(xiàn)抽象類(lèi)的子類(lèi)來(lái)創(chuàng)建對(duì)象,并將其賦給抽象類(lèi)類(lèi)型的引用。

  4. 特殊功能:

    • 接口:接口可以用于實(shí)現(xiàn)多態(tài),通過(guò)接口類(lèi)型的引用來(lái)調(diào)用實(shí)現(xiàn)類(lèi)的方法。

    • 抽象類(lèi):抽象類(lèi)可以包含抽象方法和具體方法的實(shí)現(xiàn),從而提供默認(rèn)行為給子類(lèi)使用。子類(lèi)可以選擇性地實(shí)現(xiàn)抽象方法,對(duì)于不需要修改的方法,可以繼承抽象類(lèi)中的具體實(shí)現(xiàn)。

總的來(lái)說(shuō),接口和抽象類(lèi)都是用來(lái)實(shí)現(xiàn)多態(tài)和約束子類(lèi)的機(jī)制,但在定義方式、繼承關(guān)系、實(shí)例化對(duì)象和特殊功能等方面存在一些區(qū)別。根據(jù)具體的需求和設(shè)計(jì)場(chǎng)景,可以選擇使用接口或抽象類(lèi)來(lái)實(shí)現(xiàn)代碼的靈活性和重用性。

相同:

1.不能夠?qū)嵗?/p>

2.可以將抽象類(lèi)和接口類(lèi)型作為引用類(lèi)型

3.一個(gè)類(lèi)如果繼承了某個(gè)抽象類(lèi)或者實(shí)現(xiàn)了某個(gè)接口都需要對(duì)其中的抽象方法全部進(jìn)行實(shí)現(xiàn),否則該類(lèi)仍然需要

被聲明為抽象類(lèi)

怎樣聲明一個(gè)類(lèi)不會(huì)被繼承,什么場(chǎng)景下會(huì)用

如果一個(gè)類(lèi)被final修飾,此類(lèi)不可以有子類(lèi),不能被其它類(lèi)繼承,如果一個(gè)中的所有方法都沒(méi)有重寫(xiě)的需要,當(dāng)前類(lèi)沒(méi)有子類(lèi)也罷,就可以使用final修飾類(lèi)。

深拷貝和淺拷貝

Java中的拷貝操作分為深拷貝和淺拷貝兩種方式,它們的區(qū)別在于拷貝過(guò)程中是否創(chuàng)建新的對(duì)象以及如何復(fù)制對(duì)象的成員。

淺拷貝(Shallow Copy): 淺拷貝是一種簡(jiǎn)單的拷貝方式,它創(chuàng)建一個(gè)新的對(duì)象,然后將原始對(duì)象的字段值復(fù)制到新對(duì)象中。但是,如果字段值是引用類(lèi)型,淺拷貝只會(huì)復(fù)制引用,而不是創(chuàng)建一個(gè)新的引用對(duì)象。因此,新對(duì)象和原始對(duì)象會(huì)共享相同的引用對(duì)象,對(duì)其中一個(gè)對(duì)象所做的修改會(huì)影響另一個(gè)對(duì)象。

淺拷貝(Shallow Copy)是指在拷貝對(duì)象時(shí),只復(fù)制對(duì)象本身和對(duì)象中的基本數(shù)據(jù)類(lèi)型成員,而不復(fù)制對(duì)象中的引用類(lèi)型成員。簡(jiǎn)單來(lái)說(shuō),淺拷貝只是拷貝了對(duì)象的引用,而不是創(chuàng)建一個(gè)新的獨(dú)立對(duì)象。

以下是一個(gè)Java代碼示例,展示了如何進(jìn)行淺拷貝:

class Person implements Cloneable {private String name;private int age;private Address address; // 引用類(lèi)型成員變量
?public Person(String name, int age, Address address) {this.name = name;this.age = age;this.address = address;}
?public void setAddress(Address address) {this.address = address;}
?@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}
?@Overridepublic String toString() {return "Person [name=" + name + ", age=" + age + ", address=" + address + "]";}
}
?
class Address {private String city;
?public Address(String city) {this.city = city;}
?@Overridepublic String toString() {return "Address [city=" + city + "]";}
}
?
public class ShallowCopyExample {public static void main(String[] args) throws CloneNotSupportedException {Address address = new Address("New York");Person person1 = new Person("John", 25, address);
?// 淺拷貝Person person2 = (Person) person1.clone();
?// 修改person2的成員變量person2.setName("Mike");person2.setAddress(new Address("London"));
?System.out.println("person1: " + person1);System.out.println("person2: " + person2);}
}

在上述示例中,Person類(lèi)包含了一個(gè)引用類(lèi)型的成員變量address,而Address類(lèi)只有一個(gè)簡(jiǎn)單的city屬性。通過(guò)調(diào)用clone()方法進(jìn)行淺拷貝,將person1對(duì)象的內(nèi)容復(fù)制到person2對(duì)象。當(dāng)修改person2對(duì)象的成員變量時(shí),person1對(duì)象的成員變量也會(huì)發(fā)生變化,因?yàn)樗鼈児蚕硗粋€(gè)引用類(lèi)型的成員變量。

輸出結(jié)果如下:

person1: Person [name=John, age=25, address=Address [city=London]]
person2: Person [name=Mike, age=25, address=Address [city=London]]

可以看到,person2對(duì)象修改了address引用的內(nèi)容,導(dǎo)致person1對(duì)象的address也發(fā)生了變化。這就是淺拷貝的特點(diǎn),只復(fù)制了引用,而沒(méi)有創(chuàng)建新的獨(dú)立對(duì)象。

深拷貝(Deep Copy): 深拷貝是一種更為復(fù)雜的拷貝方式,它不僅創(chuàng)建一個(gè)新的對(duì)象,還會(huì)遞歸復(fù)制對(duì)象的所有引用類(lèi)型字段,包括它們所引用的對(duì)象,以保證復(fù)制后的對(duì)象與原始對(duì)象完全獨(dú)立。因此,新對(duì)象和原始對(duì)象擁有各自獨(dú)立的引用對(duì)象,互不影響。

在Java中,實(shí)現(xiàn)深拷貝的方式有多種,包括:

  1. 使用實(shí)現(xiàn)了Cloneable接口的clone方法來(lái)實(shí)現(xiàn)深拷貝。需要在被拷貝的類(lèi)中重寫(xiě)clone方法,并在該方法中對(duì)引用類(lèi)型字段進(jìn)行深度拷貝。

  2. 使用序列化和反序列化來(lái)實(shí)現(xiàn)深拷貝。通過(guò)將對(duì)象序列化為字節(jié)流,然后再進(jìn)行反序列化,可以創(chuàng)建一個(gè)新的獨(dú)立對(duì)象。

  3. 使用第三方庫(kù),比如Apache Commons Lang中的SerializationUtils類(lèi)或者Google Gson,它們提供了更便捷的深拷貝方式。

需要注意的是,并非所有的類(lèi)都是可深拷貝的,如果類(lèi)的字段包含不可變對(duì)象或者其他具有深度狀態(tài)的對(duì)象,可能需要特殊處理來(lái)確保新對(duì)象的獨(dú)立性。 同時(shí),在進(jìn)行對(duì)象拷貝時(shí),還需要考慮性能和內(nèi)存使用的問(wèn)題,因?yàn)樯羁截惪赡苄枰f歸地復(fù)制整個(gè)對(duì)象圖,可能會(huì)導(dǎo)致性能和內(nèi)存開(kāi)銷(xiāo)的增加。因此,在選擇拷貝方式時(shí),需要根據(jù)具體需求和場(chǎng)景來(lái)決定使用淺拷貝還是深拷貝。

序列化

Java序列化是指將對(duì)象轉(zhuǎn)化為字節(jié)流的過(guò)程,可以將對(duì)象保存到文件、傳輸?shù)骄W(wǎng)絡(luò)或者在進(jìn)程間進(jìn)行通信。反序列化則是將字節(jié)流轉(zhuǎn)化為對(duì)象的過(guò)程。Java的序列化機(jī)制主要通過(guò)ObjectOutputStream和ObjectInputStream來(lái)實(shí)現(xiàn)。

在以下情況下,我們通常需要實(shí)現(xiàn)Java序列化:

  1. 對(duì)象持久化:當(dāng)我們需要將對(duì)象保存到磁盤(pán)或數(shù)據(jù)庫(kù)中,以便之后重新讀取和恢復(fù)時(shí),可以使用Java序列化。通過(guò)將對(duì)象轉(zhuǎn)為字節(jié)流,我們可以將其寫(xiě)入文件或數(shù)據(jù)庫(kù)中。這對(duì)于需要長(zhǎng)期保存對(duì)象狀態(tài)的應(yīng)用場(chǎng)景非常有用,比如緩存或數(shù)據(jù)存儲(chǔ)。

  2. 進(jìn)程間通信:當(dāng)我們需要在不同的Java進(jìn)程之間進(jìn)行通信時(shí),可以使用Java序列化來(lái)傳遞對(duì)象。通過(guò)將對(duì)象轉(zhuǎn)為字節(jié)流,我們可以將其傳輸給其他進(jìn)程,并在接收端進(jìn)行反序列化恢復(fù)為對(duì)象。這在分布式系統(tǒng)、遠(yuǎn)程調(diào)用以及消息傳遞等場(chǎng)景下有廣泛應(yīng)用。

需要注意的是,為了使對(duì)象可以被序列化,相關(guān)的類(lèi)需要實(shí)現(xiàn)Serializable接口,這是一個(gè)標(biāo)記接口,僅起到標(biāo)識(shí)該類(lèi)可以被序列化的作用。同時(shí),類(lèi)中的所有域也必須是可序列化的,即要么是基本類(lèi)型,要么是實(shí)現(xiàn)了Serializable接口的對(duì)象。

然而,并不是所有的場(chǎng)景都適合使用Java序列化。在一些需要高性能、傳輸大量數(shù)據(jù)或數(shù)據(jù)結(jié)構(gòu)頻繁改變的情況下,可能不適合使用序列化來(lái)傳輸對(duì)象,而選擇其他的序列化方法或者數(shù)據(jù)交換格式。此外,需要特別注意序列化對(duì)版本升級(jí)的兼容性問(wèn)題,因?yàn)樾蛄谢膶?duì)象需要保證版本一致,否則可能導(dǎo)致反序列化失敗。

反射介紹

反射(Reflection)是指在程序運(yùn)行時(shí)動(dòng)態(tài)地獲取、操作和修改類(lèi)或?qū)ο蟮膶傩?、方法和?gòu)造函數(shù)等信息的能力。通過(guò)反射,我們可以在運(yùn)行時(shí)檢查類(lèi)、實(shí)例化對(duì)象、調(diào)用方法、獲取和修改字段的值,以及操作構(gòu)造函數(shù)等。

Java中的反射API位于java.lang.reflect包下,提供了一組類(lèi)和接口,用于實(shí)現(xiàn)反射功能。常用的反射類(lèi)和接口包括以下幾個(gè):

  • Class類(lèi):表示一個(gè)類(lèi)或接口的運(yùn)行時(shí)對(duì)象,可以獲取類(lèi)的構(gòu)造函數(shù)、方法、字段等信息。

  • Constructor類(lèi):表示類(lèi)的構(gòu)造函數(shù),用于創(chuàng)建類(lèi)的實(shí)例對(duì)象。

  • Method類(lèi):表示類(lèi)的方法,可以用于調(diào)用方法并獲取方法的信息。

  • Field類(lèi):表示類(lèi)的字段,可以用于獲取和修改字段的值。

反射的主要應(yīng)用場(chǎng)景包括:

  1. 動(dòng)態(tài)加載類(lèi):在運(yùn)行時(shí)通過(guò)類(lèi)名字符串來(lái)動(dòng)態(tài)加載并實(shí)例化對(duì)象。

  2. 運(yùn)行時(shí)獲取類(lèi)的信息:獲取類(lèi)的構(gòu)造函數(shù)、方法、字段等信息,包括注解、修飾符等。

  3. 動(dòng)態(tài)調(diào)用方法:在運(yùn)行時(shí)通過(guò)方法名和參數(shù)類(lèi)型,動(dòng)態(tài)調(diào)用類(lèi)的方法。

  4. 對(duì)私有成員的訪(fǎng)問(wèn):通過(guò)反射可以獲取和修改類(lèi)的私有字段和方法。

  5. 生成動(dòng)態(tài)代理:使用反射可以在運(yùn)行時(shí)生成代理對(duì)象,并在代理對(duì)象中增加額外的邏輯。

使用反射需要注意以下幾點(diǎn):

  • 反射操作相對(duì)于直接調(diào)用代碼的執(zhí)行效率較低,因?yàn)樯婕暗讲檎?、解析和?zhí)行步驟。

  • 反射破壞了封裝性,可以訪(fǎng)問(wèn)和修改原本無(wú)法訪(fǎng)問(wèn)的成員,因此需要謹(jǐn)慎使用。

  • 由于反射在編譯期無(wú)法進(jìn)行類(lèi)型檢查,可能會(huì)在運(yùn)行時(shí)拋出未檢查的異常,需要進(jìn)行異常處理和類(lèi)型判斷。

總結(jié)來(lái)說(shuō),反射是一種強(qiáng)大而靈活的機(jī)制,提供了在運(yùn)行時(shí)動(dòng)態(tài)操作類(lèi)和對(duì)象的能力。它在某些情況下能夠簡(jiǎn)化代碼編寫(xiě)和提供更大的靈活性,但需要慎重使用,并考慮其可能帶來(lái)的性能和安全性方面的影響。

反射的步驟反射的步驟如下。

使用反射的步驟主要包括以下幾個(gè):

  1. 獲取類(lèi)的Class對(duì)象:首先需要獲取目標(biāo)類(lèi)的Class對(duì)象,可以通過(guò)類(lèi)名、對(duì)象實(shí)例或者Class類(lèi)的forName()方法來(lái)獲取。

    // 通過(guò)類(lèi)名獲取Class對(duì)象
    Class<?> clazz = MyClass.class;
    ?
    // 通過(guò)對(duì)象實(shí)例獲取Class對(duì)象
    MyClass obj = new MyClass();
    Class<?> clazz = obj.getClass();
    ?
    // 通過(guò)類(lèi)的全限定名獲取Class對(duì)象
    Class<?> clazz = Class.forName("com.example.MyClass");
  2. 獲取構(gòu)造函數(shù)對(duì)象(可選):如果需要通過(guò)構(gòu)造函數(shù)創(chuàng)建對(duì)象,可以通過(guò)Class對(duì)象的getConstructor()、getDeclaredConstructor()方法獲取目標(biāo)構(gòu)造函數(shù)對(duì)象。

    // 獲取指定參數(shù)類(lèi)型的公共構(gòu)造函數(shù)對(duì)象
    Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
    ?
    // 獲取所有參數(shù)類(lèi)型的構(gòu)造函數(shù)對(duì)象(包括私有構(gòu)造函數(shù))
    Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class);
    ?
    // 禁用訪(fǎng)問(wèn)檢查,允許訪(fǎng)問(wèn)私有構(gòu)造函數(shù)
    constructor.setAccessible(true);
  3. 創(chuàng)建對(duì)象(可選):如果獲取了構(gòu)造函數(shù)對(duì)象,可以使用Constructor對(duì)象的newInstance()方法創(chuàng)建目標(biāo)類(lèi)的實(shí)例。

    // 使用構(gòu)造函數(shù)對(duì)象創(chuàng)建對(duì)象實(shí)例
    MyClass obj = (MyClass) constructor.newInstance("example", 123);
  4. 獲取方法對(duì)象:通過(guò)Class對(duì)象的getMethod()、getDeclaredMethod()方法獲取目標(biāo)方法對(duì)象。

    // 獲取指定名稱(chēng)和參數(shù)類(lèi)型的公共方法對(duì)象
    Method method = clazz.getMethod("methodName", int.class, String.class);
    ?
    // 獲取所有名稱(chēng)和參數(shù)類(lèi)型的方法對(duì)象(包括私有方法)
    Method method = clazz.getDeclaredMethod("methodName", int.class, String.class);
    ?
    // 禁用訪(fǎng)問(wèn)檢查,允許訪(fǎng)問(wèn)私有方法
    method.setAccessible(true);
  5. 調(diào)用方法:通過(guò)方法對(duì)象的invoke()方法調(diào)用目標(biāo)方法。

    // 調(diào)用方法
    Object result = method.invoke(obj, 123, "example");
  6. 獲取和設(shè)置字段的值:通過(guò)Class對(duì)象的getField()getDeclaredField()方法獲取目標(biāo)字段對(duì)象。

    // 獲取公共字段對(duì)象
    Field field = clazz.getField("fieldName");
    ?
    // 獲取所有字段對(duì)象(包括私有字段)
    Field field = clazz.getDeclaredField("fieldName");
    ?
    // 禁用訪(fǎng)問(wèn)檢查,允許訪(fǎng)問(wèn)私有字段
    field.setAccessible(true);
    ?
    // 獲取字段的值
    Object value = field.get(obj);
    ?
    // 設(shè)置字段的值
    field.set(obj, newValue);

注意:在使用反射時(shí),需要注意訪(fǎng)問(wèn)修飾符(public、private等),需禁用訪(fǎng)問(wèn)檢查才能訪(fǎng)問(wèn)和修改私有成員。此外,還需要處理可能拋出的異常,如找不到構(gòu)造函數(shù)、方法或字段等。

創(chuàng)建對(duì)象的幾種方式

在Java中,我們可以使用以下幾種方式來(lái)創(chuàng)建對(duì)象:

  1. 使用new關(guān)鍵字:

ClassName obj = new ClassName();

這是最常見(jiàn)的創(chuàng)建對(duì)象的方式。通過(guò)使用new關(guān)鍵字,我們可以在堆中分配內(nèi)存,并創(chuàng)建一個(gè)新的對(duì)象。

  1. 使用Class的newInstance()方法:

ClassName obj = (ClassName) Class.forName("ClassName").newInstance();

Class.forName("ClassName")會(huì)返回一個(gè)代表ClassName類(lèi)的Class對(duì)象,然后通過(guò)調(diào)用newInstance()方法來(lái)創(chuàng)建該類(lèi)的對(duì)象。需要注意的是,這種方式要求ClassName類(lèi)有一個(gè)無(wú)參的構(gòu)造函數(shù),否則會(huì)拋出InstantiationException異常。

  1. 使用Constructor類(lèi)的newInstance()方法:

Constructor<ClassName> constructor = ClassName.class.getConstructor();
ClassName obj = constructor.newInstance();

這種方式使用反射的方式來(lái)創(chuàng)建對(duì)象。首先,我們獲取到ClassName類(lèi)的Constructor對(duì)象,然后使用newInstance()方法來(lái)創(chuàng)建對(duì)象。同樣需要注意,這種方式要求ClassName類(lèi)有一個(gè)無(wú)參的構(gòu)造函數(shù)。

  1. 使用clone()方法:

ClassName obj = (ClassName) otherObj.clone();

這種方式是通過(guò)對(duì)象的clone()方法來(lái)創(chuàng)建一個(gè)對(duì)象的副本。需要注意的是,類(lèi)必須實(shí)現(xiàn)Cloneable接口并重寫(xiě)clone()方法,否則會(huì)拋出CloneNotSupportedException異常。

  1. 使用反序列化:

ObjectInputStream in = new ObjectInputStream(new FileInputStream("filename"));
ClassName obj = (ClassName) in.readObject();

通過(guò)將對(duì)象序列化到文件中,然后再反序列化回來(lái)來(lái)創(chuàng)建對(duì)象。需要注意的是,類(lèi)必須實(shí)現(xiàn)Serializable接口。

這些是創(chuàng)建對(duì)象的常見(jiàn)方式,在不同的場(chǎng)景下可以選擇適合的方式來(lái)創(chuàng)建對(duì)象。每種方式都有其適用的情況和注意事項(xiàng)。

@Contended注解有什么用

這個(gè)注解是為了解決偽共享問(wèn)題而存在的

Java緩存?zhèn)喂蚕?#xff08;Cache False Sharing)是指多個(gè)線(xiàn)程同時(shí)訪(fǎng)問(wèn)不同變量,但這些變量被存儲(chǔ)在相鄰的緩存行中,導(dǎo)致在多線(xiàn)程并發(fā)更新變量時(shí),由于緩存一致性協(xié)議的原因,會(huì)頻繁地使緩存行無(wú)效,降低了性能。

這個(gè)問(wèn)題通常出現(xiàn)在多線(xiàn)程環(huán)境中,當(dāng)多個(gè)線(xiàn)程同時(shí)修改一個(gè)共享的數(shù)據(jù)結(jié)構(gòu)中的不同變量時(shí),由于緩存行的對(duì)齊以及緩存一致性的機(jī)制,每個(gè)線(xiàn)程更新變量時(shí),可能會(huì)同時(shí)使得其他線(xiàn)程緩存的行無(wú)效,導(dǎo)致額外的緩存同步開(kāi)銷(xiāo)。

(出現(xiàn)在緩存L1上)

這個(gè)注解會(huì)讓當(dāng)前類(lèi)的屬性,獨(dú)占一個(gè)緩存行。在共享數(shù)據(jù)結(jié)構(gòu)的變量之間增加一些無(wú)意義的填充變量,使得相鄰的變量在不同的緩存行中,從而避免偽共享。

Java中有四種引用類(lèi)型

  1. 強(qiáng)引用(Strong Reference):最常見(jiàn)的引用類(lèi)型,也是默認(rèn)的引用類(lèi)型。使用強(qiáng)引用,一個(gè)對(duì)象不會(huì)被垃圾回收器回收,只有在沒(méi)有任何強(qiáng)引用指向它時(shí),才會(huì)被回收。

  2. 軟引用(Soft Reference):通過(guò)軟引用,可以讓對(duì)象在內(nèi)存不足時(shí)被回收。垃圾回收器在進(jìn)行回收時(shí),通常會(huì)保留軟引用對(duì)象,只有當(dāng)內(nèi)存不足時(shí),才會(huì)回收這些對(duì)象。

    Object referent = new Object();

    SoftReference<Object> softReference = new SoftReference<>(referent);

  3. 弱引用(Weak Reference):使用弱引用,可以讓對(duì)象在下一次垃圾回收時(shí)被回收。垃圾回收器在回收時(shí),不論內(nèi)存是否充足,都會(huì)回收掉只有弱引用指向的對(duì)象。

  4. 虛引用(Phantom Reference):虛引用是最弱的一種引用類(lèi)型,它的存在幾乎沒(méi)有實(shí)際的意義??梢杂锰撘脕?lái)跟蹤對(duì)象被垃圾回收器回收的過(guò)程,無(wú)法通過(guò)虛引用訪(fǎng)問(wèn)對(duì)象,需要配合引用隊(duì)列(ReferenceQueue)一起使用。

這四種引用類(lèi)型的關(guān)系是:強(qiáng)引用 > 軟引用 > 弱引用 > 虛引用。對(duì)象在沒(méi)有任何引用指向時(shí),會(huì)被回收。軟引用和弱引用可以讓對(duì)象在內(nèi)存不足時(shí)被回收,虛引用可以讓對(duì)象在被回收的同時(shí)收到通知。

使用不同的引用類(lèi)型,可以更靈活地控制對(duì)象的生命周期和回收時(shí)機(jī),適應(yīng)不同的內(nèi)存管理需求。需要注意的是,虛引用的使用相對(duì)較少,一般在某些高級(jí)的內(nèi)存管理場(chǎng)景中才會(huì)涉及。

虛引用

虛引用(Phantom Reference)是Java中最弱的一種引用類(lèi)型。與其他引用類(lèi)型不同,虛引用的存在幾乎沒(méi)有實(shí)際的意義,它主要用于跟蹤對(duì)象被垃圾回收器回收的過(guò)程。

以下是虛引用的一些特點(diǎn)和使用場(chǎng)景:

  1. 虛引用的創(chuàng)建:虛引用可以通過(guò)創(chuàng)建PhantomReference對(duì)象來(lái)實(shí)現(xiàn)。虛引用對(duì)象需要傳入一個(gè)引用隊(duì)列(ReferenceQueue),用于在對(duì)象被回收時(shí)接收通知。

    Object referent = new Object();
    ReferenceQueue<Object> queue = new ReferenceQueue<>();
    PhantomReference<Object> phantomReference = new PhantomReference<>(referent, queue);

  2. 無(wú)法通過(guò)虛引用訪(fǎng)問(wèn)對(duì)象:與其他引用不同,虛引用無(wú)法通過(guò)get()方法獲得對(duì)應(yīng)的對(duì)象。任何時(shí)候,使用虛引用的get()方法都會(huì)返回null。

    Object obj = phantomReference.get(); // 返回null

  3. 接收回收通知:當(dāng)對(duì)象被垃圾回收器回收時(shí),虛引用所關(guān)聯(lián)的對(duì)象將被放入引用隊(duì)列中??梢酝ㄟ^(guò)引用隊(duì)列來(lái)獲取被回收的對(duì)象信息,進(jìn)行相關(guān)的處理操作。

    ReferenceQueue<Object> queue = new ReferenceQueue<>();
    // ...
    PhantomReference<Object> phantomReference = new PhantomReference<>(referent, queue);
    // ...
    Reference<?> reference = queue.poll();
    if (reference != null) {// 執(zhí)行相關(guān)處理操作
    }

  4. 虛引用的應(yīng)用場(chǎng)景:虛引用的應(yīng)用場(chǎng)景比較少見(jiàn),一般在一些高級(jí)的內(nèi)存管理場(chǎng)景中使用。例如,你可以使用虛引用來(lái)實(shí)現(xiàn)一些本地資源的釋放,在對(duì)象被垃圾回收時(shí)進(jìn)行清理操作,比如關(guān)閉文件句柄、釋放網(wǎng)絡(luò)連接等。

    class ResourceCleaner {private ReferenceQueue<Object> queue = new ReferenceQueue<>();
    ?// 注冊(cè)虛引用,關(guān)聯(lián)清理操作public void register(Object resource, Runnable cleanupAction) {PhantomReference<Object> phantomReference = new PhantomReference<>(resource, queue);// ...}
    ?// 在適當(dāng)?shù)臅r(shí)機(jī)執(zhí)行清理操作public void cleanup() {Reference<?> reference = queue.poll();while (reference != null) {// 執(zhí)行相關(guān)清理操作reference.clear();// ...reference = queue.poll();}}
    }

需要注意的是,因?yàn)樘撘玫拇嬖趲缀鯖](méi)有實(shí)際的意義,開(kāi)發(fā)中使用虛引用的場(chǎng)景較少,而且需要謹(jǐn)慎使用。錯(cuò)誤使用虛引用可能會(huì)導(dǎo)致一些不可預(yù)測(cè)的問(wèn)題,因此在使用虛引用時(shí)應(yīng)仔細(xì)評(píng)估和規(guī)劃。

Java中鎖的分類(lèi)

在Java中,鎖可以按照以下幾種分類(lèi)標(biāo)準(zhǔn)來(lái)進(jìn)行劃分:

  1. 公平鎖與非公平鎖: 公平鎖是指多個(gè)線(xiàn)程按照請(qǐng)求的順序獲取鎖,而非公平鎖則沒(méi)有這樣的保證。在公平鎖中,線(xiàn)程們按照先來(lái)先服務(wù)的原則排隊(duì)獲取鎖;而在非公平鎖中,鎖會(huì)傾向于允許當(dāng)前已拿到鎖的線(xiàn)程再次獲取鎖。

  2. 互斥鎖與共享鎖: 互斥鎖(Exclusive Lock)是一種獨(dú)占鎖,它只允許一個(gè)線(xiàn)程在同一時(shí)間獲取鎖,并阻止其他線(xiàn)程訪(fǎng)問(wèn)被保護(hù)資源。而共享鎖(Shared Lock)允許多個(gè)線(xiàn)程同時(shí)獲取鎖,并共享被保護(hù)資源的訪(fǎng)問(wèn)權(quán)限?;コ怄i用于保護(hù)臨界區(qū),而共享鎖用于并發(fā)讀操作。

  3. 寫(xiě)鎖與讀寫(xiě)鎖: 寫(xiě)鎖與讀寫(xiě)鎖適用于對(duì)讀寫(xiě)操作進(jìn)行區(qū)分的場(chǎng)景。寫(xiě)鎖(Write Lock)是獨(dú)占鎖,只允許一個(gè)線(xiàn)程進(jìn)行寫(xiě)操作,并且阻塞其他線(xiàn)程的讀寫(xiě)操作。讀寫(xiě)鎖(ReadWrite Lock)允許多個(gè)線(xiàn)程同時(shí)進(jìn)行讀操作,但只允許一個(gè)線(xiàn)程進(jìn)行寫(xiě)操作。讀操作之間不會(huì)互斥,讀與寫(xiě)操作之間互斥。

  4. 悲觀鎖與樂(lè)觀鎖: 悲觀鎖(Pessimistic Locking)是一種保守策略,它假設(shè)會(huì)有其他線(xiàn)程對(duì)共享資源進(jìn)行修改,因此在訪(fǎng)問(wèn)共享資源之前進(jìn)行加鎖。悲觀鎖的典型例子就是 synchronized 關(guān)鍵字和 ReentrantLock 類(lèi)。相反,樂(lè)觀鎖(Optimistic Locking)假設(shè)并發(fā)沖突很少發(fā)生,不主動(dòng)加鎖,而是在更新操作時(shí)檢查數(shù)據(jù)是否被其他線(xiàn)程修改過(guò)。

請(qǐng)注意,這些分類(lèi)標(biāo)準(zhǔn)并不是嚴(yán)格獨(dú)立的,而是相互關(guān)聯(lián)的,同一個(gè)鎖可能涵蓋不同分類(lèi)標(biāo)準(zhǔn)的特性。在實(shí)際應(yīng)用中,根據(jù)具體需求,可以選擇合適的鎖類(lèi)型來(lái)實(shí)現(xiàn)線(xiàn)程同步和資源訪(fǎng)問(wèn)控制。

Java中==和equals有哪些區(qū)別

equals 和== 最大的區(qū)別是一個(gè)是方法一個(gè)是運(yùn)算符。

==:如果比較的對(duì)象是基本數(shù)據(jù)類(lèi)型,則比較的是數(shù)值是否相等;如果比較的是引用數(shù)據(jù)類(lèi)型,則比較的是對(duì)象

的地址值是否相等。

equals():用來(lái)比較方法兩個(gè)對(duì)象的內(nèi)容是否相等。

注意:equals 方法不能用于基本數(shù)據(jù)類(lèi)型的變量,如果沒(méi)有對(duì) equals 方法進(jìn)行重寫(xiě),則比較的是引用類(lèi)型的變量所指向的對(duì)象的地址。

String、StringBuffer、StringBuilder區(qū)別及使用場(chǎng)景

String、StringBuffer和StringBuilder都是Java中用于處理字符串的類(lèi),它們?cè)谛阅?、線(xiàn)程安全性和可變性方面有所不同。

  1. String(不可變字符串):

    • String對(duì)象是不可變的,一旦創(chuàng)建就不能被修改。每次對(duì)字符串進(jìn)行操作(連接、替換等),都會(huì)創(chuàng)建一個(gè)新的String對(duì)象。

    • 因?yàn)樽址遣豢勺兊?#xff0c;所以String對(duì)象是線(xiàn)程安全的。

    • 適合于字符串不經(jīng)常變化的場(chǎng)景,例如作為方法參數(shù)、類(lèi)屬性等。

  2. StringBuffer(可變字符串,線(xiàn)程安全):

    • StringBuffer對(duì)象是可變的,可以進(jìn)行字符串的修改、追加、插入和刪除等操作。它是線(xiàn)程安全的,因此適用于多線(xiàn)程環(huán)境。

    • 每次對(duì)StringBuffer的操作都是在原有對(duì)象的基礎(chǔ)上進(jìn)行的,不會(huì)創(chuàng)建新的對(duì)象。

    • 適合于字符串經(jīng)常需要變化、需要線(xiàn)程安全的場(chǎng)景,例如在多線(xiàn)程環(huán)境下進(jìn)行字符串處理的情況。

  3. StringBuilder(可變字符串,非線(xiàn)程安全):

    • StringBuilder對(duì)象也是可變的,可以進(jìn)行字符串的修改、追加、插入和刪除等操作。與StringBuffer不同的是,StringBuilder是非線(xiàn)程安全的。

    • 每次對(duì)StringBuilder的操作都是在原有對(duì)象的基礎(chǔ)上進(jìn)行的,不會(huì)創(chuàng)建新的對(duì)象。

    • 適合于字符串經(jīng)常需要變化,且在單線(xiàn)程環(huán)境下進(jìn)行字符串處理的場(chǎng)景,例如在循環(huán)中進(jìn)行大量字符串拼接的情況。

  1. String類(lèi)是不可變的,一旦創(chuàng)建就不能修改,每次修改都會(huì)創(chuàng)建一個(gè)新的對(duì)象;

  2. StringBuffer和StringBuilder類(lèi)是可變的,可以隨意修改其中的內(nèi)容,不會(huì)創(chuàng)建新的對(duì)象。

  3. StringBuffer類(lèi)是線(xiàn)程安全的,而StringBuilder類(lèi)是非線(xiàn)程安全的。

String類(lèi)和常量池

String對(duì)象的兩種創(chuàng)建方式

String str1 = "abcd";
String str2 = new String("abcd");
System.out.println(str1==str2);//false

這兩種不同的創(chuàng)建方法是有區(qū)別的,第一種方式是在常量池中拿對(duì)象,第二種直接在堆內(nèi)存中創(chuàng)建一個(gè)新對(duì)象(如果常量池中沒(méi)有的話(huà)會(huì)在常量池里創(chuàng)建一個(gè))。

記住:只要使用new方法,便需要?jiǎng)?chuàng)建新的對(duì)象。

3.2:String類(lèi)型的常量池比較特殊。

它的主要使用方法有兩種:

1、直接使用雙引號(hào)聲明出來(lái)的對(duì)象會(huì)直接存儲(chǔ)到常量池中。

2、如果不是雙引號(hào)聲明的String對(duì)象,可以使用String提供的intern方法。String.intern()是一個(gè)Native方法,它的作用是:如果運(yùn)行時(shí)常量池中已經(jīng)包含一個(gè)等于此String對(duì)象內(nèi)容的字符串,則返回常量池中該字符串的引用;如果沒(méi)有則在常量池中創(chuàng)建與此String內(nèi)容相同的字符串,并返回常量池中創(chuàng)建字符串的引用。

JDK6和JDK7的區(qū)別:

JDK6:

1、如果常量池中有,則不會(huì)放入。返回已有的常量池中的對(duì)象地址

2、如果沒(méi)有,則將對(duì)象復(fù)制一份,并將放入到常量池中,并放回對(duì)象地址

JDK7之后:

1、如果常量池中有,則不會(huì)放入。返回已有的常量池中的對(duì)象地址

2、如果沒(méi)有,則將對(duì)象的引用地址復(fù)制一份,放入到常量池中,并返回常量池中的引用地址

public class StringTest2 {public static void main(String[] args) {String s  = new String("a")+new String("b");String s2 =s.intern();System.out.println(s2 =="ab");System.out.println(s =="ab");}
}

DK6下輸出:true false

JDK7之后輸出:true true

看到上面的結(jié)果可能還存在疑慮,我們接著分析一下1、String s = "ab";創(chuàng)建了一個(gè)對(duì)象,在編譯已經(jīng)確定要放入常量池 2、String s = “a”+ “b”;常量字符串拼接,底層優(yōu)化為“ab”,和上面一樣也生成一個(gè)對(duì)象。 3、String s = new String("ab");創(chuàng)建了兩個(gè)對(duì)象,通過(guò)查看字節(jié)碼文件:

一個(gè)對(duì)象時(shí)new出來(lái)的另外一個(gè)對(duì)象是字符串常量池中的對(duì)象“ab”,字節(jié)碼指令:ldc 4、String s = new String("a") + new String("b");字節(jié)碼顯示創(chuàng)建了6個(gè)對(duì)象

1、new StringBuilder對(duì)象

2、new String("a")

3、常量池中的a4、new String("b")

5、常量池中的b深入刨析StringBuilder的toString,調(diào)用的是new String(char[])

6、new String("ab"),此時(shí)常量池中并沒(méi)有ab這個(gè)字符串強(qiáng)調(diào)一下toString()的調(diào)用,

先從常量池中找,沒(méi)有在常量池中生成“ab” 再看看相關(guān)字符串的內(nèi)容代碼

String s1 = new String("計(jì)算機(jī)");
String s2 = s1.intern();
String s3 = "計(jì)算機(jī)";
System.out.println(s2);//計(jì)算機(jī)
System.out.println(s1 == s2);//false,因?yàn)橐粋€(gè)是堆內(nèi)存中的String對(duì)象一個(gè)是常量池中的String對(duì)象,
System.out.println(s3 == s2);//true,因?yàn)閮蓚€(gè)都是常量池中的String對(duì)象String str1 = "str";
String str2 = "ing";String str3 = "str" + "ing";//常量池中的對(duì)象
String str4 = str1 + str2; //在堆上創(chuàng)建的新的對(duì)象	  
String str5 = "string";//常量池中的對(duì)象
System.out.println(str3 == str4);//false
System.out.println(str3 == str5);//true
System.out.println(str4 == str5);//false

盡量避免多個(gè)字符串拼接,因?yàn)檫@樣會(huì)生成新對(duì)象。如果需要改變字符串的話(huà)可以使用StringBuffer和StringBuilder

Java代理的幾種實(shí)現(xiàn)方式

靜態(tài)代理

,只能靜態(tài)的代理某些類(lèi)或者某些方法,不推薦使用,功能比較弱,但是編碼簡(jiǎn)單

// 定義一個(gè)共同的接口
interface Calculator {int add(int a, int b);
}
?
// 實(shí)現(xiàn)真正的計(jì)算類(lèi)
class CalculatorImpl implements Calculator {@Overridepublic int add(int a, int b) {return a + b;}
}
?
// 創(chuàng)建代理類(lèi),并實(shí)現(xiàn)共同的接口
class CalculatorProxy implements Calculator {private Calculator calculator;
?// 在構(gòu)造函數(shù)中傳入真正的計(jì)算類(lèi)對(duì)象public CalculatorProxy(Calculator calculator) {this.calculator = calculator;}
?@Overridepublic int add(int a, int b) {// 在調(diào)用真正對(duì)象的方法之前執(zhí)行額外的邏輯System.out.println("Before calculation...");
?// 調(diào)用真正對(duì)象的方法int result = calculator.add(a, b);
?// 在調(diào)用真正對(duì)象的方法之后執(zhí)行額外的邏輯System.out.println("After calculation...");
?return result;}
}
?
public class Main {public static void main(String[] args) {// 創(chuàng)建真正的計(jì)算類(lèi)對(duì)象Calculator calculator = new CalculatorImpl();
?// 創(chuàng)建代理類(lèi)對(duì)象,將真正的計(jì)算類(lèi)對(duì)象傳入Calculator proxy = new CalculatorProxy(calculator);
?// 調(diào)用代理對(duì)象的方法int result = proxy.add(5, 3);System.out.println("Result: " + result);}
}

第二種:動(dòng)態(tài)代理,包含JDK代理和CGLIB動(dòng)態(tài)代理

JDK代理

JDK動(dòng)態(tài)代理是Java提供的一種動(dòng)態(tài)創(chuàng)建代理對(duì)象的機(jī)制。它基于Java反射機(jī)制,在運(yùn)行時(shí)動(dòng)態(tài)生成代理類(lèi)和代理實(shí)例。JDK動(dòng)態(tài)代理只能針對(duì)接口進(jìn)行代理,它通過(guò)Proxy類(lèi)和InvocationHandler接口來(lái)實(shí)現(xiàn)。

以下是JDK動(dòng)態(tài)代理的基本步驟:

  1. 定義一個(gè)接口:首先需要定義一個(gè)共同的接口,該接口包含被代理對(duì)象的方法。

  2. 創(chuàng)建一個(gè)InvocationHandler對(duì)象:InvocationHandler接口是JDK動(dòng)態(tài)代理的核心,它包含一個(gè)invoke方法,用于處理代理對(duì)象方法的調(diào)用。自定義一個(gè)類(lèi)來(lái)實(shí)現(xiàn)InvocationHandler接口,并在invoke方法中編寫(xiě)處理邏輯。

  3. 使用Proxy類(lèi)創(chuàng)建代理對(duì)象:使用Proxy類(lèi)的newProxyInstance方法動(dòng)態(tài)創(chuàng)建代理對(duì)象。該方法需要傳入三個(gè)參數(shù):ClassLoader,代理接口數(shù)組和InvocationHandler對(duì)象。

  4. 通過(guò)代理對(duì)象調(diào)用方法:通過(guò)代理對(duì)象調(diào)用接口中的方法,實(shí)際上會(huì)觸發(fā)InvocationHandler的invoke方法,并在該方法中執(zhí)行具體的代理邏輯。

下面是一個(gè)簡(jiǎn)單的示例代碼:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
?
// 定義接口
interface Calculator {int add(int a, int b);
}
?
// 實(shí)現(xiàn)InvocationHandler接口
class CalculatorInvocationHandler implements InvocationHandler {private Calculator target;
?public CalculatorInvocationHandler(Calculator target) {this.target = target;}
?@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 在方法調(diào)用之前添加額外邏輯System.out.println("Before calculation...");
?// 調(diào)用真正對(duì)象的方法Object result = method.invoke(target, args);
?// 在方法調(diào)用之后添加額外邏輯System.out.println("After calculation...");
?return result;}
}
?
public class Main {public static void main(String[] args) {// 創(chuàng)建真正的計(jì)算類(lèi)對(duì)象Calculator target = new CalculatorImpl();
?// 創(chuàng)建InvocationHandler對(duì)象,將真正的計(jì)算類(lèi)對(duì)象傳入InvocationHandler handler = new CalculatorInvocationHandler(target);
?// 使用Proxy類(lèi)創(chuàng)建代理對(duì)象Calculator proxy = (Calculator) Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class<?>[]{Calculator.class},handler);
?// 調(diào)用代理對(duì)象的方法int result = proxy.add(5, 3);System.out.println("Result: " + result);}
}

在上述代碼中,我們定義了一個(gè)接口Calculator,并實(shí)現(xiàn)了InvocationHandler接口的CalculatorInvocationHandler類(lèi)。在invoke方法中,我們可以在方法調(diào)用前后添加額外的邏輯。在Main類(lèi)中,我們創(chuàng)建了真正的計(jì)算類(lèi)對(duì)象,并使用Proxy類(lèi)的newProxyInstance方法創(chuàng)建代理對(duì)象。通過(guò)代理對(duì)象調(diào)用方法時(shí),實(shí)際上會(huì)調(diào)用invoke方法,并在其中執(zhí)行代理邏輯。

運(yùn)行以上代碼,你將看到額外的邏輯在方法調(diào)用前后被執(zhí)行,并獲得正確的計(jì)算結(jié)果。這就是JDK動(dòng)態(tài)代理的基本原理。與靜態(tài)代理相比,JDK動(dòng)態(tài)代理更加靈活,可以適用于各種接口的代理場(chǎng)景。

CGLIB動(dòng)態(tài)代理

CGLIB(Code Generation Library)是一個(gè)強(qiáng)大的第三方類(lèi)庫(kù),用于在運(yùn)行時(shí)擴(kuò)展Java類(lèi)的功能。它通過(guò)生成繼承被代理類(lèi)的子類(lèi),并重寫(xiě)父類(lèi)的方法來(lái)實(shí)現(xiàn)動(dòng)態(tài)代理。相比JDK動(dòng)態(tài)代理,CGLIB動(dòng)態(tài)代理不需要接口的支持,可以代理類(lèi)而不僅僅是接口。

以下是使用CGLIB動(dòng)態(tài)代理的基本步驟:

  1. 引入相關(guān)依賴(lài):在項(xiàng)目中加入CGLIB的依賴(lài),例如Maven項(xiàng)目可以添加以下依賴(lài):

<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>

  1. 定義一個(gè)被代理的類(lèi):不需要實(shí)現(xiàn)接口的普通類(lèi)。

  2. 創(chuàng)建MethodInterceptor對(duì)象:MethodInterceptor是CGLIB提供的核心接口,包含一個(gè)intercept方法,在該方法中編寫(xiě)處理邏輯。

  3. 使用Enhancer創(chuàng)建代理對(duì)象:Enhancer是CGLIB提供的用于創(chuàng)建代理對(duì)象的類(lèi)。通過(guò)設(shè)置父類(lèi)、接口、攔截器等參數(shù),調(diào)用create方法動(dòng)態(tài)生成代理對(duì)象。

  4. 通過(guò)代理對(duì)象調(diào)用方法:通過(guò)代理對(duì)象調(diào)用方法,實(shí)際上會(huì)觸發(fā)MethodInterceptor的intercept方法,并在該方法中執(zhí)行具體的代理邏輯。

下面是一個(gè)簡(jiǎn)單的示例代碼:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
?
import java.lang.reflect.Method;
?
// 定義被代理的類(lèi)
class Calculator {public int add(int a, int b) {return a + b;}
}
?
// 實(shí)現(xiàn)MethodInterceptor接口
class CalculatorMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {// 在方法調(diào)用之前添加額外邏輯System.out.println("Before calculation...");
?// 調(diào)用真正對(duì)象的方法Object result = proxy.invokeSuper(obj, args);
?// 在方法調(diào)用之后添加額外邏輯System.out.println("After calculation...");
?return result;}
}
?
public class Main {public static void main(String[] args) {// 創(chuàng)建Enhancer對(duì)象Enhancer enhancer = new Enhancer();
?// 設(shè)置父類(lèi)(被代理類(lèi))enhancer.setSuperclass(Calculator.class);
?// 設(shè)置攔截器enhancer.setCallback(new CalculatorMethodInterceptor());
?// 創(chuàng)建代理對(duì)象Calculator proxy = (Calculator) enhancer.create();
?// 調(diào)用代理對(duì)象的方法int result = proxy.add(5, 3);System.out.println("Result: " + result);}
}

在上述代碼中,我們定義了一個(gè)被代理的類(lèi)Calculator,并實(shí)現(xiàn)了CGLIB的MethodInterceptor接口來(lái)編寫(xiě)代理邏輯。通過(guò)設(shè)置父類(lèi)和攔截器,使用Enhancer類(lèi)創(chuàng)建代理對(duì)象。通過(guò)代理對(duì)象調(diào)用方法時(shí),實(shí)際上會(huì)觸發(fā)MethodInterceptor的intercept方法,并在其中執(zhí)行代理邏輯。

運(yùn)行以上代碼,你將看到額外的邏輯在方法調(diào)用前后被執(zhí)行,并獲得正確的計(jì)算結(jié)果。這就是CGLIB動(dòng)態(tài)代理的基本原理。與JDK動(dòng)態(tài)代理不同,CGLIB動(dòng)態(tài)代理不需要接口的支持,可以代理普通類(lèi)。然而,由于使用了繼承機(jī)制,CGLIB不能代理被標(biāo)記為final的類(lèi)和方法。

JDK動(dòng)態(tài)代理和CGLIB兩種動(dòng)態(tài)代理的比較

JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理是兩種常用的代理實(shí)現(xiàn)方式,它們具有不同的特點(diǎn)和適用場(chǎng)景。下面是它們的區(qū)別以及各自的優(yōu)缺點(diǎn):

JDK動(dòng)態(tài)代理:

  • 基于接口:JDK動(dòng)態(tài)代理只能代理接口,需要目標(biāo)類(lèi)實(shí)現(xiàn)一個(gè)或多個(gè)接口。

  • 使用Java反射機(jī)制:JDK動(dòng)態(tài)代理是通過(guò)Proxy類(lèi)和InvocationHandler接口實(shí)現(xiàn)的,利用Java反射機(jī)制生成代理類(lèi)和代理實(shí)例。

  • 平臺(tái)獨(dú)立性:JDK動(dòng)態(tài)代理是Java標(biāo)準(zhǔn)庫(kù)的一部分,因此具有很好的平臺(tái)獨(dú)立性,不依賴(lài)第三方庫(kù)。

  • 性能較低:相比CGLIB動(dòng)態(tài)代理,JDK動(dòng)態(tài)代理在生成代理類(lèi)和調(diào)用方法時(shí)的性能較差。這是由于JDK動(dòng)態(tài)代理在生成代理類(lèi)時(shí)需要使用反射,以及在代理時(shí)涉及到方法調(diào)用的轉(zhuǎn)發(fā)。

  • 無(wú)法代理final類(lèi)和方法:JDK動(dòng)態(tài)代理由于基于接口,因此無(wú)法代理被標(biāo)記為final的類(lèi)和方法。

CGLIB動(dòng)態(tài)代理:

  • 基于繼承:CGLIB動(dòng)態(tài)代理可以直接代理普通類(lèi),不需要實(shí)現(xiàn)接口。它通過(guò)繼承目標(biāo)類(lèi)的方式實(shí)現(xiàn)代理。

  • 使用ASM字節(jié)碼操作庫(kù):CGLIB動(dòng)態(tài)代理使用ASM庫(kù)操作字節(jié)碼,在運(yùn)行時(shí)動(dòng)態(tài)生成代理類(lèi)。

  • 性能較高:相對(duì)于JDK動(dòng)態(tài)代理,CGLIB動(dòng)態(tài)代理在生成代理類(lèi)和調(diào)用方法時(shí)的性能更高。這是因?yàn)镃GLIB動(dòng)態(tài)代理直接繼承目標(biāo)類(lèi),省去了方法調(diào)用的轉(zhuǎn)發(fā)。

  • 無(wú)法代理final方法:由于CGLIB動(dòng)態(tài)代理是通過(guò)繼承實(shí)現(xiàn)的,因此無(wú)法代理被標(biāo)記為final的方法。但是,可以代理被final修飾的類(lèi)。

綜合來(lái)說(shuō),JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理各有優(yōu)缺點(diǎn):

  • JDK動(dòng)態(tài)代理適用于代理接口的場(chǎng)景,具有很好的平臺(tái)獨(dú)立性,但性能較差。

  • CGLIB動(dòng)態(tài)代理適用于代理普通類(lèi)的場(chǎng)景,性能較高,但對(duì)final方法和類(lèi)的代理受限。

因此,在選擇動(dòng)態(tài)代理方式時(shí),需根據(jù)具體的需求和場(chǎng)景來(lái)選擇適合的代理方式。

hashcode和equals如何使用

hashCode()和equals()是Java中的兩個(gè)重要方法,都源自于java.lang.Object,用于對(duì)象的比較和哈希映射。下面是它們的使用方法:

  1. hashCode()方法:

    • hashCode()方法用于計(jì)算對(duì)象的哈希碼(hash code),返回一個(gè)int類(lèi)型的值。

    • hashCode()方法的常規(guī)約定是,對(duì)于相等的對(duì)象,調(diào)用hashCode()方法應(yīng)該返回相同的值。然而,對(duì)于不相等的對(duì)象,hashCode()方法返回相同的值并不是必需的。

    • 在重寫(xiě)equals()方法時(shí),通常也需要同時(shí)重寫(xiě)hashCode()方法,以保證在存儲(chǔ)對(duì)象的哈希集合(如HashMap、HashSet)中能正常工作。

    • 重寫(xiě)hashCode()方法時(shí),應(yīng)遵循以下原則:

      • 如果兩個(gè)對(duì)象通過(guò)equals()方法比較是相等的,則它們的hashCode()方法的返回值必須相等。

      • 如果兩個(gè)對(duì)象通過(guò)equals()方法比較不相等(即對(duì)象不相等),它們的hashCode()方法的返回值可以相等,也可以不相等。

  2. equals()方法:

    • equals()方法用于比較兩個(gè)對(duì)象是否相等,返回一個(gè)boolean類(lèi)型的值。

    • 默認(rèn)情況下,equals()方法比較的是對(duì)象的引用,即判斷兩個(gè)對(duì)象是否指向同一個(gè)內(nèi)存地址。但是,可以根據(jù)需要重寫(xiě)equals()方法,以便自定義對(duì)象的相等條件。

    • 重寫(xiě)equals()方法時(shí),應(yīng)遵循以下原則:

      • 對(duì)稱(chēng)性:如果a.equals(b)返回true,則b.equals(a)也應(yīng)返回true。

      • 自反性:對(duì)于任何非null的引用值x,x.equals(x)都應(yīng)返回true。

      • 傳遞性:如果a.equals(b)返回true,且b.equals(c)返回true,則a.equals(c)也應(yīng)返回true。

      • 一致性:對(duì)于任何非null的引用值x和y,多次調(diào)用x.equals(y)應(yīng)始終返回相同的結(jié)果,前提是對(duì)象上沒(méi)有修改導(dǎo)致equals()比較的結(jié)果發(fā)生變化。

      • 對(duì)于任何非null的引用值x,x.equals(null)都應(yīng)返回false。

異常分類(lèi)

在Java中,異常分為三種不同的類(lèi)型:

  1. 受檢異常(Checked Exception): 受檢異常是指在代碼中明確需要進(jìn)行處理的異常,在方法聲明中通過(guò)throws關(guān)鍵字聲明,或者在方法內(nèi)部通過(guò)try-catch語(yǔ)句進(jìn)行捕獲和處理。受檢異常通常表示程序可能面臨的外部環(huán)境異常,需要程序員在代碼中顯式處理,否則編譯時(shí)會(huì)報(bào)錯(cuò)。例如,IOException、SQLException等。

  2. 運(yùn)行時(shí)異常(Runtime Exception): 運(yùn)行時(shí)異常是指在程序執(zhí)行過(guò)程中可能出現(xiàn)的異常,通常是由程序錯(cuò)誤或異常情況引起的。與受檢異常不同的是,運(yùn)行時(shí)異常不要求在代碼中顯式處理,并且也不需要在方法聲明中聲明throws關(guān)鍵字。當(dāng)發(fā)生運(yùn)行時(shí)異常時(shí),如果沒(méi)有進(jìn)行顯式處理,則會(huì)沿著方法調(diào)用棧向上拋出,直到被捕獲或?qū)е鲁绦蚪K止。例如,NullPointerException、ArrayIndexOutOfBoundsException等。

  3. 錯(cuò)誤(Error): 錯(cuò)誤是指無(wú)法通過(guò)代碼來(lái)處理的嚴(yán)重問(wèn)題,通常是由虛擬機(jī)或系統(tǒng)錯(cuò)誤引起的。錯(cuò)誤表示JVM或系統(tǒng)發(fā)生了嚴(yán)重的問(wèn)題,無(wú)法恢復(fù)和處理,一般不需要程序員進(jìn)行處理。例如,OutOfMemoryError、StackOverflowError等。

Java異常類(lèi)繼承自Throwable類(lèi),其中受檢異常繼承自Exception,運(yùn)行時(shí)異常繼承自RuntimeException,錯(cuò)誤繼承自Error。通過(guò)了解和正確處理異常,可以增加程序的可靠性,并提供適當(dāng)?shù)腻e(cuò)誤處理和容錯(cuò)機(jī)制。

Java異常處理方式

在Java中,有三種主要的異常處理方式:

  1. try-catch塊: 使用try-catch塊可以捕獲和處理異常。try塊用于包含可能拋出異常的代碼,catch塊用于捕獲并處理try塊中拋出的異常。語(yǔ)法如下:

    try {// 可能拋出異常的代碼
    } catch (ExceptionType1 e1) {// 處理異常類(lèi)型 1
    } catch (ExceptionType2 e2) {// 處理異常類(lèi)型 2
    } finally {// 可選的finally塊,用于無(wú)論是否發(fā)生異常都會(huì)執(zhí)行的代碼
    }

    在try塊中,如果發(fā)生異常,則會(huì)跳轉(zhuǎn)到與異常類(lèi)型匹配的catch塊,執(zhí)行相應(yīng)的處理代碼。如果沒(méi)有匹配的catch塊,異常會(huì)傳播到調(diào)用棧的上一層。無(wú)論是否發(fā)生異常,finally塊中的代碼都會(huì)被執(zhí)行。

  2. throws聲明: 使用throws關(guān)鍵字可以在方法的聲明中指定該方法可能拋出的異常。將異常以throws聲明的方式拋出,可以將異常的處理責(zé)任交給調(diào)用該方法的地方。示例代碼如下:

    public void methodName() throws ExceptionType1, ExceptionType2 {// 可能拋出異常的代碼
    }

    當(dāng)方法中的代碼拋出了異常,調(diào)用該方法的地方可以選擇捕獲異常并處理,或者繼續(xù)將異常上拋到更高層調(diào)用棧中進(jìn)行處理。

  3. 使用finally塊: finally塊用于在try-catch塊中的代碼執(zhí)行完畢后,無(wú)論是否發(fā)生異常,都會(huì)執(zhí)行的代碼塊。finally塊通常用于釋放資源或進(jìn)行必要的清理操作,例如關(guān)閉文件、釋放資源等。語(yǔ)法如下:

    try {// 可能拋出異常的代碼
    } catch (ExceptionType e) {// 處理異常
    } finally {// 無(wú)論是否發(fā)生異常,都會(huì)執(zhí)行的代碼
    }

    注意,finally塊可以省略,try塊和catch塊可以單獨(dú)存在。在沒(méi)有catch塊的情況下,try塊中拋出的異常會(huì)被上層調(diào)用棧處理或繼續(xù)上拋。

通過(guò)合理地使用這些異常處理方式,可以增加代碼的健壯性和容錯(cuò)性,更好地處理異常情況,提高程序的穩(wěn)定性。

throw,throws的區(qū)別

throwthrows是Java中異常處理的兩個(gè)關(guān)鍵字,它們有以下區(qū)別:

  1. throw關(guān)鍵字: throw關(guān)鍵字用于手動(dòng)拋出一個(gè)異常對(duì)象。它通常用于方法內(nèi)部,用來(lái)拋出指定的異常,使得異常在方法內(nèi)部被捕獲或在調(diào)用棧中傳播。例如:

    public void method() {if (condition) {throw new ExceptionType("Error occurred");}
    }

    在上述代碼中,如果滿(mǎn)足某個(gè)條件,throw語(yǔ)句會(huì)拋出一個(gè)指定的異常對(duì)象,使得異常在方法內(nèi)部被捕獲或在調(diào)用棧中傳播。

  2. throws關(guān)鍵字: throws關(guān)鍵字用于方法的聲明中,用于指定該方法可能拋出的異常類(lèi)型。它提供了一種聲明異常的機(jī)制,使得調(diào)用該方法的代碼可以采取相應(yīng)的異常處理措施。例如:

    public void method() throws ExceptionType1, ExceptionType2 {// 可能拋出這兩種異常類(lèi)型的代碼
    }

    在上述代碼中,throws關(guān)鍵字后面列出了方法可能拋出的異常類(lèi)型。當(dāng)調(diào)用該方法時(shí),調(diào)用者可以選擇捕獲這些異常并處理,或者將異常進(jìn)一步上拋。

總結(jié):

  • throw關(guān)鍵字用于手動(dòng)拋出異常,表示在代碼的某個(gè)條件成立時(shí),主動(dòng)地拋出異常對(duì)象。

  • throws關(guān)鍵字用于方法的聲明中,指定該方法可能拋出的異常類(lèi)型,并將異常處理的責(zé)任轉(zhuǎn)移給調(diào)用該方法的代碼。

  • throw拋出的異常是通過(guò)關(guān)鍵字new創(chuàng)建的對(duì)象,而throws聲明的異常是指定的異常類(lèi)型。

  • throw用于方法內(nèi)部,throws用于方法的聲明中。

需要注意的是,throwthrows關(guān)鍵字并不直接處理異常,它們只是在異常處理時(shí)的一種機(jī)制,實(shí)際的異常處理通過(guò)try-catch塊或者上層調(diào)用棧來(lái)完成。

自定義異常在生產(chǎn)中如何應(yīng)用

Java雖然提供了豐富的異常處理類(lèi),但是在項(xiàng)目中還會(huì)經(jīng)常使用自定義異常,其主要原因是Java提供的異常類(lèi)在某些情況下還是不能滿(mǎn)足實(shí)際需球。例如以下情況: 1、系統(tǒng)中有些錯(cuò)誤是符合Java語(yǔ)法,但不符合業(yè)務(wù)邏輯。

2、在分層的軟件結(jié)構(gòu)中,通常是在表現(xiàn)層統(tǒng)一對(duì)系統(tǒng)其他層次的異常進(jìn)行捕獲處理。

過(guò)濾器與攔截器的區(qū)別

過(guò)濾器(Filter)和攔截器(Interceptor)都是用于在Web應(yīng)用中對(duì)請(qǐng)求進(jìn)行處理和攔截的組件,但它們之間有一些區(qū)別:

  1. 含義:

    • 過(guò)濾器(Filter):過(guò)濾器是在Servlet容器中執(zhí)行的功能組件,對(duì)請(qǐng)求和響應(yīng)進(jìn)行預(yù)處理和后處理。它可以修改請(qǐng)求和響應(yīng)的內(nèi)容,或者對(duì)請(qǐng)求進(jìn)行驗(yàn)證、安全性檢查、日志記錄等操作。

    • 攔截器(Interceptor):攔截器也是用于對(duì)請(qǐng)求進(jìn)行預(yù)處理和后處理的組件,但是攔截器是在Spring MVC框架內(nèi)部執(zhí)行的。它可以在請(qǐng)求被調(diào)度到處理器之前和之后進(jìn)行一些公共的任務(wù),如身份驗(yàn)證、權(quán)限檢查、日志記錄等。

  2. 使用場(chǎng)景:

    • 過(guò)濾器(Filter):過(guò)濾器主要用于對(duì)HTTP請(qǐng)求和響應(yīng)進(jìn)行處理,可以對(duì)請(qǐng)求的URL、參數(shù)、頭部等進(jìn)行過(guò)濾和處理。

    • 攔截器(Interceptor):攔截器主要用于對(duì)Controller的請(qǐng)求進(jìn)行預(yù)處理和后處理,在請(qǐng)求到達(dá)Controller之前和離開(kāi)Controller之后執(zhí)行一些公共的任務(wù)、處理業(yè)務(wù)邏輯。

  3. 執(zhí)行順序:

    • 過(guò)濾器(Filter):過(guò)濾器在Servlet容器中配置,并以鏈?zhǔn)浇Y(jié)構(gòu)執(zhí)行。對(duì)于一個(gè)請(qǐng)求,過(guò)濾器按照配置的順序依次執(zhí)行,可以有多個(gè)過(guò)濾器配置,并且可以跨越多個(gè)Web應(yīng)用。

    • 攔截器(Interceptor):攔截器是在Spring MVC的上下文中配置的,并且只對(duì)DispatcherServlet的請(qǐng)求進(jìn)行攔截。在一個(gè)請(qǐng)求中,攔截器的執(zhí)行順序由配置的順序決定,同一個(gè)攔截器鏈上的多個(gè)攔截器按照配置的順序依次執(zhí)行。

總之,過(guò)濾器適合處理通用的URL級(jí)別的請(qǐng)求處理,例如編碼轉(zhuǎn)換、安全性驗(yàn)證等。攔截器更加適合對(duì)Controller級(jí)別的請(qǐng)求進(jìn)行處理,例如權(quán)限檢查、日志記錄等。通過(guò)合理配置過(guò)濾器和攔截器,可以實(shí)現(xiàn)對(duì)請(qǐng)求的不同層面的處理和攔截,以滿(mǎn)足不同業(yè)務(wù)需求。

過(guò)濾器(Filter)和攔截器(Interceptor)是在Web應(yīng)用程序中用于處理和攔截請(qǐng)求的組件,它們之間有以下詳細(xì)區(qū)別:

  1. 執(zhí)行時(shí)機(jī):

    • 過(guò)濾器:過(guò)濾器是在Servlet容器中執(zhí)行的,對(duì)請(qǐng)求和響應(yīng)進(jìn)行預(yù)處理和后處理。它們?cè)谡?qǐng)求進(jìn)入Servlet容器之前被調(diào)用,并在請(qǐng)求離開(kāi)容器后執(zhí)行。過(guò)濾器可以在請(qǐng)求到達(dá)Servlet之前修改請(qǐng)求和響應(yīng)內(nèi)容,以及在響應(yīng)返回給客戶(hù)端之前對(duì)其進(jìn)行處理。

    • 攔截器:攔截器是在Spring MVC框架內(nèi)部執(zhí)行的,主要用于對(duì)Controller的請(qǐng)求進(jìn)行預(yù)處理和后處理。攔截器在請(qǐng)求到達(dá)Controller之前和離開(kāi)Controller之后執(zhí)行,可以在請(qǐng)求處理之前做一些通用的準(zhǔn)備工作,以及在請(qǐng)求處理完成后進(jìn)行一些公共的收尾工作。

  2. 作用范圍:

    • 過(guò)濾器:過(guò)濾器是在Servlet容器中配置的,對(duì)請(qǐng)求進(jìn)行過(guò)濾處理。過(guò)濾器可以作用于多個(gè)Servlet和多個(gè)Web應(yīng)用程序,可以配置在web.xml中,并通過(guò)URL模式指定對(duì)哪些請(qǐng)求生效。

    • 攔截器:攔截器是在Spring MVC的上下文中配置的,主要對(duì)DispatcherServlet的請(qǐng)求進(jìn)行攔截處理。攔截器只作用于Spring MVC中的請(qǐng)求,并且只對(duì)DispatcherServlet的請(qǐng)求生效。

  3. 觸發(fā)條件:

    • 過(guò)濾器:過(guò)濾器可以對(duì)所有的請(qǐng)求進(jìn)行過(guò)濾處理,包括靜態(tài)資源請(qǐng)求。它們是基于URL模式進(jìn)行匹配,可以以鏈?zhǔn)浇Y(jié)構(gòu)依次執(zhí)行多個(gè)過(guò)濾器。

    • 攔截器:攔截器只在DispatcherServlet中執(zhí)行,并且只對(duì)具體的Controller請(qǐng)求進(jìn)行攔截。攔截器是基于HandlerMapping進(jìn)行匹配,只有當(dāng)請(qǐng)求與某個(gè)Controller匹配成功時(shí),相關(guān)的攔截器才會(huì)觸發(fā)執(zhí)行。

  4. 依賴(lài)框架:

    • 過(guò)濾器:過(guò)濾器是Servlet容器的一部分,獨(dú)立于其他框架。它們可以用于任何基于Servlet規(guī)范的Web應(yīng)用程序,如JavaEE等。

    • 攔截器:攔截器是Spring MVC框架的一部分,依賴(lài)于Spring MVC框架。它們可以利用Spring MVC框架提供的功能,如依賴(lài)注入、AOP等。

總的來(lái)說(shuō),過(guò)濾器和攔截器都是用于對(duì)請(qǐng)求進(jìn)行處理和攔截的組件,但它們所處的執(zhí)行時(shí)機(jī)、作用范圍、觸發(fā)條件和依賴(lài)框架等方面存在一些差異。根據(jù)具體的需求和場(chǎng)景,可以選擇合適的過(guò)濾器或攔截器來(lái)實(shí)現(xiàn)請(qǐng)求的處理和攔截邏輯。

5,。配置文件不同

  1. 過(guò)濾器(Filter)配置:過(guò)濾器的配置是在web.xml文件中進(jìn)行的,屬于Servlet容器的配置。在web.xml中,可以通過(guò)<filter><filter-mapping>元素來(lái)配置過(guò)濾器。其中,<filter>用于聲明過(guò)濾器的類(lèi)和名稱(chēng),<filter-mapping>用于指定過(guò)濾器的名稱(chēng)和要過(guò)濾的URL模式或Servlet名稱(chēng)。

  2. 攔截器(Interceptor)配置:攔截器的配置是在Spring MVC的配置文件中進(jìn)行的,屬于Spring MVC框架的配置。要配置攔截器,需要在配置文件中聲明攔截器,并將其添加到攔截器鏈中??梢允褂?code><mvc:interceptor>元素或在Java配置中使用addInterceptor()方法來(lái)配置攔截器。在配置攔截器時(shí),需要指定攔截器類(lèi)、要攔截的URL模式、排除的URL模式等。

Integer常見(jiàn)面試題


1.介紹一下自動(dòng)裝箱和自動(dòng)拆箱
java的八種基本類(lèi)型都對(duì)應(yīng)著相應(yīng)的包裝類(lèi)型
總的來(lái)說(shuō):裝箱就是自動(dòng)將基本數(shù)據(jù)類(lèi)型轉(zhuǎn)換為包裝器類(lèi)型;拆箱就是自動(dòng)將包裝器類(lèi)型轉(zhuǎn)換為基本數(shù)據(jù)類(lèi)型。所以在運(yùn)算賦值過(guò)程中,會(huì)自動(dòng)進(jìn)行拆箱和裝箱。
拆箱裝箱的過(guò)程 :
1)拆箱:Integer total = 99
實(shí)際上是調(diào)用了Integer total = Integer.valueOf(99) 這句代碼
2)裝箱:nt totalprim = total;
實(shí)際上行是調(diào)用了 int totalprim = total.intValue();這句代碼
但是實(shí)際上拆箱裝箱需要考慮常量池的存在!(下面會(huì)講到)
2. Integer創(chuàng)建對(duì)象的幾種方式和區(qū)別
在JVM虛擬機(jī)中有一塊內(nèi)存為常量池,常量池中除了包含代碼中所定義的各種基本類(lèi)型(如int、long等等)和對(duì)象型(如String及數(shù)組)的常量值還,還包含一些以文本形式出現(xiàn)的符號(hào)引用
對(duì)于基本數(shù)據(jù),常量池對(duì)每種基本數(shù)據(jù)都有一個(gè)區(qū)間,在此區(qū)間中的數(shù),都從常量池中存取共享!但是除了new創(chuàng)建對(duì)象的方式除外。
以Integer為例:
(-128——127為一個(gè)區(qū)間)
Integer total = 99
這句賦值的確是會(huì)是自動(dòng)裝箱,但是返回的地址卻不是在堆中,而是在常量池中,因?yàn)?9屬于【-128,,127】區(qū)間。也就是說(shuō)以這種方式創(chuàng)建的對(duì)象,都是取的一個(gè)地址!
??? ??? ?Integer t1 = 99;//常量池
? ? ? ? Integer t2 = 99;//常量池
? ? ? ? System.out.println(t1 == t2);//true ?

Integer total = 128;
這句賦值也會(huì)進(jìn)行自動(dòng)裝箱,但是由于不在區(qū)間內(nèi),所以取到的對(duì)象地址是在堆中。不會(huì)進(jìn)行對(duì)象共享!每次都會(huì)創(chuàng)建新的對(duì)象
??? ??? ?Integer t3 = 128;//堆
? ? ? ? Integer t4 = 128;//堆
? ? ? ? System.out.println(t3 == t4);//false
Integer total = Integer.valueOf(99) ,Integer total= Integer.valueOf(128)
這兩種創(chuàng)建方式和上面的賦值是一樣的,因?yàn)樯厦娴淖詣?dòng)裝箱源碼調(diào)用的就是這個(gè)方法!
? ?Integer tt1 = Integer.valueOf(99);//常量池
? ? Integer tt2 = Integer.valueOf(99);//常量池
? ? System.out.println(tt1 == tt2);//true ?

? ? Integer tt3 = Integer.valueOf(128);//堆
? ? Integer tt4 = Integer.valueOf(128);//堆
? ? System.out.println(tt3 == tt4);//fasle ?

Integer total = new Integer(99)
使用new關(guān)鍵字創(chuàng)建對(duì)象的時(shí)候,就不需要考慮常量池的問(wèn)題,無(wú)論數(shù)值大小,都從堆中創(chuàng)建!
?? ??? ?Integer n1 = new Integer(99);//堆
? ? ? ? Integer n2 = new Integer(99);//堆
? ? ? ? System.out.println(n1 == n2);//fasle

總結(jié):
1)一共三種創(chuàng)建方式:
前兩種是看似不同,其實(shí)內(nèi)部機(jī)制完全相同,因?yàn)闀?huì)自動(dòng)裝箱!但是一定要注意到常量池的問(wèn)題。
?? ??? ?Integer t1 = 99;//常量池
? ? ? ? Integer t4 = 128;//堆

? ? ? ? Integer tt2 = Integer.valueOf(99);//常量池
? ? ? ? Integer tt4 = Integer.valueOf(128);//堆
??

? ? ? ? Integer n1 = new Integer(99);//堆
? ? ? ? Integer n2 = new Integer(99);//堆

2)在面試過(guò)程中如果遇到考查Integer的情況,基本都會(huì)給一段代碼,判斷輸出是true還是fasle,這時(shí)候只要仔細(xì)分析對(duì)象的創(chuàng)建方式,以及返回的地址來(lái)源即可!

3.常見(jiàn)考查代碼
總結(jié):

兩個(gè)數(shù)都是用==或者Integer.valueOf()方法賦值的話(huà),只要比較數(shù)的大小,在【-128,127】之間就相同,不在就不同
兩個(gè)數(shù)都是用new關(guān)鍵字創(chuàng)建的話(huà),無(wú)論數(shù)值大小,一定不同
一個(gè)數(shù)用new,一個(gè)數(shù)用==或者Integer.valueOf(),也一定不同!
Integer in= new Integer(127);
Integer in2 = new Integer(127);
System.out.println(in==in2);//false
System.out.println(in.equals(in2));//true


Integer in3= new Integer(128);
Integer in4 = new Integer(128);
System.out.println(in3==in4);//false
System.out.println(in3.equals(in4));//true


Integer in5= 128;
Integer in6 = 128;
System.out.println(in5==in6);//false
System.out.println(in5.equals(in6));//true


Integer in7= 127;
Integer in8 = 127;
System.out.println(in7==in8);//true
System.out.println(in7.equals(in8));//true

值傳遞和引用傳遞有什么區(qū)別

值傳遞和引用傳遞是傳遞參數(shù)時(shí)的兩種不同方式,它們之間的區(qū)別主要在于傳遞的是什么。

1. **值傳遞**:
? ?- 值傳遞是指將變量的值復(fù)制一份傳遞給函數(shù)或方法。
? ?- 在值傳遞中,傳遞的是變量的實(shí)際值,而不是變量本身。
? ?- 當(dāng)函數(shù)或方法使用傳遞的參數(shù)時(shí),會(huì)操作參數(shù)值的副本,原始變量不受影響。
? ?- 在 Java 中,傳遞基本數(shù)據(jù)類(lèi)型時(shí)是值傳遞的方式。

2. **引用傳遞**:
? ?- 引用傳遞是指將變量的引用(內(nèi)存地址)傳遞給函數(shù)或方法。
? ?- 在引用傳遞中,傳遞的是變量的實(shí)際引用,函數(shù)或方法可以通過(guò)該引用訪(fǎng)問(wèn)和修改原始變量。
? ?- 當(dāng)函數(shù)或方法使用傳遞的引用時(shí),操作的是原始變量的值,可以改變?cè)甲兞康臓顟B(tài)。
? ?- 在某些語(yǔ)言中支持引用傳遞,比如 C++,但在 Java 中并不存在“引用傳遞”的概念。

在 Java 中,雖然對(duì)象引用作為參數(shù)傳遞給方法時(shí)傳遞的是引用的副本(即地址的副本),但實(shí)際上 Java 是使用值傳遞的方式。因?yàn)閭鬟f的是引用的值(地址的副本),而不是引用本身。這意味著在方法內(nèi)雖然可以改變對(duì)象狀態(tài),卻無(wú)法改變引用指向的對(duì)象。

總的來(lái)說(shuō),Java 中只有值傳遞這一種傳遞參數(shù)的方式,但對(duì)于對(duì)象引用的處理方式與傳統(tǒng)的值傳遞有一些微妙的區(qū)別。希望這個(gè)解答對(duì)你有所幫助。如有任何問(wèn)題,請(qǐng)繼續(xù)提問(wèn)。

集合

?集合和數(shù)組的區(qū)別

集合(Collection)和數(shù)組(Array)是在編程中常用的數(shù)據(jù)結(jié)構(gòu),它們有以下幾點(diǎn)區(qū)別:

1. **數(shù)據(jù)類(lèi)型**:
? ?- 數(shù)組是一種固定大小的、存儲(chǔ)相同數(shù)據(jù)類(lèi)型元素的連續(xù)內(nèi)存區(qū)域。
? ?- 集合是一種動(dòng)態(tài)大小的、可以存儲(chǔ)不同數(shù)據(jù)類(lèi)型對(duì)象的數(shù)據(jù)結(jié)構(gòu)。

2. **長(zhǎng)度/大小**:
? ?- 數(shù)組的長(zhǎng)度是固定的,一旦創(chuàng)建就無(wú)法改變。
? ?- 集合是動(dòng)態(tài)的,可以根據(jù)需要?jiǎng)討B(tài)添加或刪除元素,大小是可變的。

3. **類(lèi)型**:
? ?- 數(shù)組可以包含基本數(shù)據(jù)類(lèi)型和對(duì)象類(lèi)型。
? ?- 集合一般是針對(duì)對(duì)象類(lèi)型的,可以存儲(chǔ)任意類(lèi)型的對(duì)象。

4. **語(yǔ)法**:
? ?- 數(shù)組的聲明和初始化方式比較簡(jiǎn)單,如 `int[] arr = new int[5]`。
? ?- 集合的聲明和初始化需要使用相關(guān)的集合類(lèi),如 `List<String> list = new ArrayList<>()`。

5. **功能**:
? ?- 集合提供了豐富的方法和功能,如增刪改查、排序、遍歷等。
? ?- 數(shù)組的功能相對(duì)簡(jiǎn)單,主要是通過(guò)索引訪(fǎng)問(wèn)元素,沒(méi)有內(nèi)置的方法來(lái)進(jìn)行常見(jiàn)操作。

6. **擴(kuò)展性**:
? ?- 集合比數(shù)組更具擴(kuò)展性和靈活性,可以更方便地進(jìn)行元素的增刪改查操作。
? ?- 數(shù)組在大小固定和數(shù)據(jù)類(lèi)型一致的情況下使用更加高效。

總的來(lái)說(shuō),集合更加靈活和功能豐富,適用于動(dòng)態(tài)數(shù)據(jù)結(jié)構(gòu)的場(chǎng)景,而數(shù)組更適合于靜態(tài)、大小固定的數(shù)據(jù)集合。在實(shí)際編程中,根據(jù)需要選擇合適的數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ)和操作數(shù)據(jù),常常會(huì)根據(jù)特定的場(chǎng)景來(lái)選擇使用數(shù)組或集合。希望以上區(qū)別對(duì)你有所幫助,如有任何問(wèn)題或需要進(jìn)一步了解,請(qǐng)隨時(shí)提出。

集合框架底層數(shù)據(jù)結(jié)構(gòu)

Java 集合框架中的不同集合類(lèi)底層使用不同的數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn),下面是一些常見(jiàn)的集合類(lèi)及其底層數(shù)據(jù)結(jié)構(gòu):

1. **ArrayList**:
? ?- ArrayList 使用數(shù)組作為底層數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ)元素。
? ?- 當(dāng)數(shù)組空間不足時(shí),會(huì)進(jìn)行擴(kuò)容操作(通常是當(dāng)前容量的 1.5 倍),以保證能夠繼續(xù)添加元素。

2. **LinkedList**:
? ?- LinkedList 使用雙向鏈表來(lái)存儲(chǔ)元素。
? ?- 鏈表的每個(gè)節(jié)點(diǎn)都保存了元素值以及指向前一個(gè)節(jié)點(diǎn)和后一個(gè)節(jié)點(diǎn)的引用。

3. **HashMap**:
? ?- HashMap 使用哈希表(數(shù)組 + 鏈表/紅黑樹(shù))來(lái)存儲(chǔ)鍵值對(duì)。
? ?- 哈希表通過(guò)鍵的哈希值來(lái)計(jì)算存儲(chǔ)位置,解決哈希沖突的方法有拉鏈法和開(kāi)放定址法。

4. **HashSet**:
? ?- HashSet 內(nèi)部使用 HashMap 來(lái)存儲(chǔ)元素。
? ?- HashSet 中的元素存儲(chǔ)在 HashMap 的 key 中,value 則使用一個(gè)靜態(tài)常量。

5. **TreeMap**:
? ?- TreeMap 使用紅黑樹(shù)(Red-Black Tree)作為底層數(shù)據(jù)結(jié)構(gòu)。
? ?- 紅黑樹(shù)是一種自平衡二叉搜索樹(shù),可以保證元素按照 key 的自然順序(或自定義比較器)排列。

6. **LinkedHashMap**:
? ?- LinkedHashMap 繼承自 HashMap,使用哈希表和雙向鏈表來(lái)維護(hù)元素的順序。
? ?- 可以保持元素插入順序或訪(fǎng)問(wèn)順序不變。

這些是 Java 集合框架中一些常見(jiàn)集合類(lèi)的底層數(shù)據(jù)結(jié)構(gòu),不同的數(shù)據(jù)結(jié)構(gòu)在不同場(chǎng)景下有著各自的優(yōu)劣勢(shì)。了解集合類(lèi)底層數(shù)據(jù)結(jié)構(gòu)有助于更好地理解集合類(lèi)的特性和性能表現(xiàn),從而更好地選擇適合的集合類(lèi)來(lái)滿(mǎn)足需求。希望以上信息能夠幫助你理解集合框架中常見(jiàn)集合類(lèi)的底層數(shù)據(jù)結(jié)構(gòu)。如有任何問(wèn)題或需要進(jìn)一步了解,請(qǐng)隨時(shí)提出。

線(xiàn)程安全的集合

在 Java 中,部分集合類(lèi)是線(xiàn)程安全的,也就是說(shuō)它們?cè)诙嗑€(xiàn)程環(huán)境下可以安全地進(jìn)行并發(fā)操作而無(wú)需額外的同步措施。以下是一些常見(jiàn)的線(xiàn)程安全集合類(lèi):

1. **Vector**:Vector 是一個(gè)線(xiàn)程安全的動(dòng)態(tài)數(shù)組,與 ArrayList 類(lèi)似,但所有的方法都是同步的。

2. **Stack**:Stack 是一個(gè)基于 Vector 實(shí)現(xiàn)的棧,也是線(xiàn)程安全的。

3. **Hashtable**:Hashtable 是一個(gè)線(xiàn)程安全的哈希表,與 HashMap 類(lèi)似,但所有的方法都是同步的。

4. **Collections.synchronizedList(List<T> list)**:通過(guò) Collections 工具類(lèi)的 synchronizedList 方法可以創(chuàng)建一個(gè)線(xiàn)程安全的 List。

5. **ConcurrentHashMap**:ConcurrentHashMap 是 Java 并發(fā)包中提供的線(xiàn)程安全的哈希表實(shí)現(xiàn),使用分段鎖技術(shù)來(lái)提高并發(fā)性能。

6. **CopyOnWriteArrayList**:CopyOnWriteArrayList 是一個(gè)線(xiàn)程安全的動(dòng)態(tài)數(shù)組,采用寫(xiě)時(shí)復(fù)制(Copy-On-Write)策略,在寫(xiě)操作時(shí)會(huì)復(fù)制一份新的數(shù)組,因此讀操作不會(huì)阻塞寫(xiě)操作,適合讀多寫(xiě)少的場(chǎng)景。

7. **CopyOnWriteArraySet**:CopyOnWriteArraySet 是 CopyOnWriteArrayList 的 Set 實(shí)現(xiàn),也是線(xiàn)程安全的。

這些線(xiàn)程安全的集合類(lèi)提供了在多線(xiàn)程環(huán)境下安全地操作集合的方法,避免了線(xiàn)程競(jìng)態(tài)條件和并發(fā)修改異常。在選擇集合類(lèi)時(shí),根據(jù)具體的需求和場(chǎng)景來(lái)考慮是否需要線(xiàn)程安全的集合類(lèi)。需要注意的是,雖然線(xiàn)程安全集合類(lèi)可以提供基本的線(xiàn)程安全性,但在特定復(fù)雜場(chǎng)景下可能仍需要額外的同步控制。希望以上信息對(duì)你有所幫助,如有任何問(wèn)題或需要進(jìn)一步了解,請(qǐng)隨時(shí)提出。

HashMap的put方法的具體流程?

HashMap 的 put 方法是向 HashMap 中添加鍵值對(duì)的方法,在 Java 中實(shí)現(xiàn)了哈希表的功能,其具體流程如下:

1. **計(jì)算鍵的哈希值**:首先,HashMap 會(huì)根據(jù)鍵的 hashCode 方法計(jì)算鍵的哈希值。如果鍵為 null,則哈希值為 0。

2. **計(jì)算存儲(chǔ)位置**:接著,HashMap 根據(jù)哈希值以及 HashMap 的容量進(jìn)行計(jì)算,確定鍵值對(duì)在數(shù)組中的存儲(chǔ)位置(也稱(chēng)為桶(Bucket))。

3. **查找是否存在相同鍵**:在確定的存儲(chǔ)位置上,HashMap 需要檢查是否已經(jīng)存在相同哈希值的鍵,如果存在相同哈希值的鍵,則需要繼續(xù)比較鍵的 equals 方法來(lái)確定是否是同一個(gè)鍵。

4. **插入/替換鍵值對(duì)**:如果沒(méi)有找到相同的鍵,則直接插入鍵值對(duì);如果找到了相同的鍵,則會(huì)替換相同鍵的值。

5. **檢查是否需要進(jìn)行擴(kuò)容**:在插入后,HashMap 會(huì)檢查當(dāng)前已存儲(chǔ)的鍵值對(duì)數(shù)量是否超過(guò)了負(fù)載因子乘以容量(負(fù)載因子用于控制 HashMap 擴(kuò)容的時(shí)機(jī)),如果超過(guò),則會(huì)觸發(fā)擴(kuò)容操作。

6. **進(jìn)行擴(kuò)容**:擴(kuò)容操作會(huì)創(chuàng)建一個(gè)新的數(shù)組,將現(xiàn)有的鍵值對(duì)重新計(jì)算存儲(chǔ)位置后插入到新數(shù)組中,同時(shí)更新 HashMap 的容量和閾值等屬性。

總的來(lái)說(shuō),HashMap 的 put 方法首先根據(jù)鍵的哈希值確定存儲(chǔ)位置,然后根據(jù)鍵的 equals 方法比較鍵是否相同,最后進(jìn)行插入或替換操作。在插入過(guò)程中會(huì)根據(jù)負(fù)載因子是否超過(guò)閾值來(lái)觸發(fā)擴(kuò)容操作。這樣可以保證 HashMap 的高效性和動(dòng)態(tài)擴(kuò)容能力。

希望以上信息對(duì)你有所幫助,如有任何問(wèn)題或需要進(jìn)一步了解,請(qǐng)隨時(shí)提出。

HashMap原理是什么,在jdk1.7和1.8中有什么區(qū)別

HashMap 根據(jù)鍵的 hashCode 值存儲(chǔ)數(shù)據(jù),大多數(shù)情況下可以直接定位到它的值,因而具有很快的訪(fǎng)問(wèn)速度,但遍歷順序卻是不確定的。 HashMap最多只允許一條記錄的鍵為null,允許多條記錄的值為 null。HashMap 非線(xiàn)程安全,即任一時(shí)刻可以有多個(gè)線(xiàn)程同時(shí)寫(xiě) HashMap,可能會(huì)導(dǎo)致數(shù)據(jù)的不一致。如果需要滿(mǎn)足線(xiàn)程安全,可以用 Collections 的 synchronizedMap 方法使 HashMap 具有線(xiàn)程安全的能力,或者使用 ConcurrentHashMap。我們用下面這張圖來(lái)介紹

HashMap 的結(jié)構(gòu)。

JAVA7 實(shí)現(xiàn)

大方向上,HashMap 里面是一個(gè)數(shù)組,然后數(shù)組中每個(gè)元素是一個(gè)單向鏈表。上圖中,每個(gè)綠色

的實(shí)體是嵌套類(lèi) Entry 的實(shí)例,Entry 包含四個(gè)屬性:key, value, hash 值和用于單向鏈表的 next。

  1. capacity:當(dāng)前數(shù)組容量,始終保持 2^n,可以擴(kuò)容,擴(kuò)容后數(shù)組大小為當(dāng)前的 2 倍。

  2. loadFactor:負(fù)載因子,默認(rèn)為 0.75。

  3. threshold:擴(kuò)容的閾值,等于 capacity * loadFactor

JAVA8實(shí)現(xiàn)

Java8 對(duì) HashMap 進(jìn)行了一些修改,最大的不同就是利用了紅黑樹(shù),所以其由 數(shù)組+鏈表+紅黑樹(shù) 組成。

根據(jù) Java7 HashMap 的介紹,我們知道,查找的時(shí)候,根據(jù) hash 值我們能夠快速定位到數(shù)組的具體下標(biāo),但是之后的話(huà),需要順著鏈表一個(gè)個(gè)比較下去才能找到我們需要的,時(shí)間復(fù)雜度取決

于鏈表的長(zhǎng)度,為 O(n)。為了降低這部分的開(kāi)銷(xiāo),在 Java8 中,當(dāng)鏈表中的元素超過(guò)了 8 個(gè)以后,會(huì)將鏈表轉(zhuǎn)換為紅黑樹(shù),在這些位置進(jìn)行查找的時(shí)候可以降低時(shí)間復(fù)雜度為 O(logN)。紅黑樹(shù)的插入和查找性能更好。

  • JDK 1.8中,對(duì)于哈希碰撞的處理采用了尾插法,新的鍵值對(duì)會(huì)添加到鏈表末尾而不是頭部,以減少鏈表的倒置。

#

HashMap和HashTable的區(qū)別及底層實(shí)現(xiàn)

HashMap和HashTable對(duì)比

HashMap和HashTable是Java中兩個(gè)常用的鍵值對(duì)存儲(chǔ)的類(lèi),它們之間有幾個(gè)主要的區(qū)別和底層實(shí)現(xiàn)方式:

  1. 線(xiàn)程安全性:

    • HashMap是非線(xiàn)程安全的,不保證在多線(xiàn)程環(huán)境下的并發(fā)操作的正確性。

    • HashTable是線(xiàn)程安全的,通過(guò)在關(guān)鍵方法上添加synchronized關(guān)鍵字來(lái)保證線(xiàn)程安全性。但這也導(dǎo)致了在多線(xiàn)程環(huán)境下的性能相對(duì)較低。

  2. 鍵值對(duì)的null值:

    • HashMap允許鍵和值都為null。即可以插入null鍵,也可以插入null值。

    • HashTable不允許鍵或者值為null,如果插入null鍵或者值會(huì)拋出NullPointerException。

  3. 初始容量和擴(kuò)容:

    • HashMap的初始容量默認(rèn)為16,加載因子默認(rèn)為0.75。當(dāng)HashMap的元素個(gè)數(shù)超過(guò)容量和加載因子的乘積時(shí),會(huì)進(jìn)行擴(kuò)容,擴(kuò)容為原來(lái)容量的兩倍。

    • HashTable的初始容量默認(rèn)為11,加載因子默認(rèn)為0.75。當(dāng)元素個(gè)數(shù)超過(guò)容量和加載因子的乘積時(shí),會(huì)進(jìn)行擴(kuò)容,擴(kuò)容為原來(lái)容量的兩倍再加1。

  4. 底層實(shí)現(xiàn):

    • HashMap底層使用數(shù)組和鏈表/紅黑樹(shù)的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)。當(dāng)鏈表長(zhǎng)度超過(guò)閾值(8)時(shí),鏈表會(huì)轉(zhuǎn)換為紅黑樹(shù),以提高查找效率。

    • HashTable底層使用數(shù)組和單向鏈表的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)。

總的來(lái)說(shuō),HashMap相對(duì)于HashTable來(lái)說(shuō)更常用,它在性能上表現(xiàn)更好,允許null鍵和null值,但不是線(xiàn)程安全的。HashTable適用于舊版本的Java或者需要在多線(xiàn)程環(huán)境下進(jìn)行操作時(shí),但需要注意它的性能相對(duì)較低。

6.HashMap鏈表插入節(jié)點(diǎn)的方式 在Java1.7中,插入鏈表節(jié)點(diǎn)使用頭插法。Java1.8中變成了尾插法

7.Java1.8的hash()中,將hash值高位(前16位)參與到取模的運(yùn)算中,使得計(jì)算結(jié)果的不確定性增強(qiáng),降低發(fā)生哈希碰撞的概率

image-20211018214936478

HashMap擴(kuò)容優(yōu)化:

擴(kuò)容以后,1.7對(duì)元素進(jìn)行rehash算法,計(jì)算原來(lái)每個(gè)元素在擴(kuò)容之后的哈希表中的位置,1.8借助2倍擴(kuò)容機(jī)制,元素不需要進(jìn)行重新計(jì)算位置

JDK 1.8 在擴(kuò)容時(shí)并沒(méi)有像 JDK 1.7 那樣,重新計(jì)算每個(gè)元素的哈希值,而是通過(guò)高位運(yùn)算(e.hash & oldCap)來(lái)確定元素是否需要移動(dòng),比如 key1 的信息如下:

使用 e.hash & oldCap 得到的結(jié)果,高一位為 0,當(dāng)結(jié)果為 0 時(shí)表示元素在擴(kuò)容時(shí)位置不會(huì)發(fā)生任何變化,而 key 2 信息如下

高一位為 1,當(dāng)結(jié)果為 1 時(shí),表示元素在擴(kuò)容時(shí)位置發(fā)生了變化,新的下標(biāo)位置等于原下標(biāo)位置 + 原數(shù)組長(zhǎng)度hashmap,**不必像1.7一樣全部重新計(jì)算位置**

為什么hashmap擴(kuò)容的時(shí)候是兩倍?

查看源代碼

在存入元素時(shí),放入元素位置有一個(gè) (n-1)&hash 的一個(gè)算法,和hash&(newCap-1),這里用到了一個(gè)&位運(yùn)算符

當(dāng)HashMap的容量是16時(shí),它的二進(jìn)制是10000,(n-1)的二進(jìn)制是01111,與hash值得計(jì)算結(jié)果如下

下面就來(lái)看一下HashMap的容量不是2的n次冪的情況,當(dāng)容量為10時(shí),二進(jìn)制為01010,(n-1)的二進(jìn)制是01001,向里面添加同樣的元素,結(jié)果為

可以看出,有三個(gè)不同的元素進(jìn)過(guò)&運(yùn)算得出了同樣的結(jié)果,嚴(yán)重的hash碰撞了

只有當(dāng)n的值是2的N次冪的時(shí)候,進(jìn)行&位運(yùn)算的時(shí)候,才可以只看后幾位,而不需要全部進(jìn)行計(jì)算

在翻倍擴(kuò)容的情況下,原來(lái)的N個(gè)元素將被分布到新數(shù)組的2N個(gè)位置上,這種分布方式可以有效地減少哈希沖突發(fā)生的可能性,提高了HashMap的查詢(xún)和插入性能。

hashmap線(xiàn)程安全的方式?

HashMap本身是非線(xiàn)程安全的,也就是說(shuō)在并發(fā)環(huán)境中同時(shí)讀寫(xiě)HashMap可能會(huì)導(dǎo)致數(shù)據(jù)不一致的問(wèn)題。如果在多線(xiàn)程環(huán)境中需要使用HashMap,可以使用以下幾種方式來(lái)確保線(xiàn)程安全性:

  1. 使用Collections工具類(lèi)的synchronizedMap方法,將HashMap包裝成一個(gè)線(xiàn)程安全的Map。示例代碼如下:

    Map<Object, Object> synchronizedMap = Collections.synchronizedMap(new HashMap<>());

    這種方式會(huì)對(duì)整個(gè)Map進(jìn)行同步,保證每個(gè)操作的原子性和互斥性,但是會(huì)降低并發(fā)性能。

  2. 使用ConcurrentHashMap類(lèi),它是Java提供的線(xiàn)程安全的哈希表實(shí)現(xiàn)。ConcurrentHashMap采用了鎖分段技術(shù),在不同的段上實(shí)現(xiàn)了獨(dú)立的鎖,并發(fā)性能比使用Collections.synchronizedMap要好。示例代碼如下:

    Map<Object, Object> concurrentHashMap = new ConcurrentHashMap<>();

    ConcurrentHashMap允許多個(gè)線(xiàn)程同時(shí)讀取,且讀操作不需要加鎖。只有寫(xiě)操作需要加鎖,并且寫(xiě)操作只鎖定當(dāng)前操作的段,不會(huì)導(dǎo)致整個(gè)Map被鎖定。

  3. 使用并發(fā)工具類(lèi)來(lái)控制對(duì)HashMap的訪(fǎng)問(wèn),例如使用讀寫(xiě)鎖(ReentrantReadWriteLock)來(lái)保證讀寫(xiě)操作的安全性。在讀多寫(xiě)少的場(chǎng)景下,讀取操作可以同時(shí)進(jìn)行,而寫(xiě)入操作會(huì)獨(dú)占鎖。示例代碼如下:

    ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    Map<Object, Object> map = new HashMap<>();
    ?
    // 寫(xiě)操作
    lock.writeLock().lock();
    try {// 更新或者添加操作map.put(key, value);
    } finally {lock.writeLock().unlock();
    }
    ?
    // 讀操作
    lock.readLock().lock();
    try {// 讀取操作Object value = map.get(key);
    } finally {lock.readLock().unlock();
    }

    使用讀寫(xiě)鎖可以提高并發(fā)性能,因?yàn)樽x操作可以同時(shí)進(jìn)行,讀線(xiàn)程之間不會(huì)互斥。

請(qǐng)注意,在多線(xiàn)程環(huán)境中使用HashMap時(shí),僅僅通過(guò)加鎖來(lái)保證線(xiàn)程安全性可能不足以滿(mǎn)足高并發(fā)的需求,還需要根據(jù)具體的業(yè)務(wù)場(chǎng)景來(lái)選擇合適的方式。

說(shuō)一下 HashSet 的實(shí)現(xiàn)原理? - HashSet如何檢查重復(fù)?HashSet是如何保證數(shù)據(jù)不可重復(fù)的?

HashSet 是 Java 中的一種集合類(lèi),它基于哈希表實(shí)現(xiàn)。下面是 HashSet 的實(shí)現(xiàn)原理和它如何保證數(shù)據(jù)不可重復(fù)的方式:

1. **HashSet 的實(shí)現(xiàn)原理**:
? ?- HashSet 內(nèi)部是通過(guò) HashMap 來(lái)實(shí)現(xiàn)的,實(shí)際上 HashSet 只是對(duì) HashMap 中 key 集合的一種包裝。
? ?- 在 HashSet 內(nèi)部使用 HashMap 存儲(chǔ)元素,以元素作為 key,value 則為一個(gè)固定的對(duì)象(比如 `Object`)。
? ?- 當(dāng)向 HashSet 中添加元素時(shí),實(shí)際上是將元素作為 key 放入 HashMap 中,value 則為一個(gè)固定的對(duì)象。
? ?- HashSet 利用 HashMap 的 key 值不能重復(fù)的特性,保證元素不可重復(fù)。

2. **HashSet 如何檢查重復(fù)**:
? ?- 當(dāng)向 HashSet 中添加元素時(shí),首先會(huì)調(diào)用元素的 `hashCode()` 方法得到哈希碼,然后根據(jù)哈希碼計(jì)算出在數(shù)組中的位置。
? ?- 如果該位置上已經(jīng)存儲(chǔ)了元素(存在哈希沖突),則會(huì)調(diào)用元素的 `equals()` 方法來(lái)比較新元素和已有元素是否相等。
? ?- 如果新元素和已有元素相等(`equals()` 返回 true),則將新元素覆蓋原有元素;否則將新元素插入到數(shù)組中。
? ?- HashSet 通過(guò)哈希碼和 equals 方法來(lái)檢查重復(fù)元素,并確保數(shù)據(jù)不可重復(fù)。

通過(guò)利用哈希表的特性,HashSet 能夠?qū)崿F(xiàn)高效地檢查重復(fù)元素,并保證集合中不包含重復(fù)數(shù)據(jù)。在使用 HashSet 時(shí),需要保證集合中元素正確實(shí)現(xiàn)了 `hashCode()` 和 `equals()` 方法,以確保 HashSet 能夠正確地工作。

ArrayList和LinkedList有什么區(qū)別

ArrayList和LinkedList是Java中常用的兩種集合類(lèi),它們?cè)趯?shí)現(xiàn)上有以下區(qū)別:

  1. 數(shù)據(jù)結(jié)構(gòu):ArrayList是基于數(shù)組實(shí)現(xiàn)的動(dòng)態(tài)數(shù)組,而LinkedList是基于雙向鏈表實(shí)現(xiàn)的。

  2. 隨機(jī)訪(fǎng)問(wèn):ArrayList支持高效的隨機(jī)訪(fǎng)問(wèn),可以通過(guò)索引直接訪(fǎng)問(wèn)元素,時(shí)間復(fù)雜度為O(1)。而LinkedList需要從頭節(jié)點(diǎn)或尾節(jié)點(diǎn)開(kāi)始遍歷,時(shí)間復(fù)雜度為O(n)。

  3. 插入和刪除:LinkedList在插入和刪除元素時(shí),其時(shí)間復(fù)雜度是O(1),因?yàn)橹恍枰薷墓?jié)點(diǎn)的指針即可。而ArrayList在插入和刪除元素時(shí),需要移動(dòng)其他元素,時(shí)間復(fù)雜度為O(n)。

  4. 內(nèi)存占用:由于ArrayList是基于數(shù)組實(shí)現(xiàn)的,它需要分配一塊連續(xù)的內(nèi)存空間來(lái)存儲(chǔ)元素。而LinkedList需要額外的空間來(lái)存儲(chǔ)節(jié)點(diǎn)之間的指針關(guān)系。因此,如果需要存儲(chǔ)大量的元素,ArrayList的內(nèi)存占用通常比LinkedList更小。

根據(jù)上述區(qū)別,可以得出一些適用場(chǎng)景:

  • 當(dāng)需要高效的隨機(jī)訪(fǎng)問(wèn)和修改元素時(shí),使用ArrayList更合適。

  • 當(dāng)需要頻繁執(zhí)行插入和刪除操作,而對(duì)隨機(jī)訪(fǎng)問(wèn)性能要求較低時(shí),使用LinkedList更合適。

  • LinkedList可以作為棧和隊(duì)列使用

需要根據(jù)具體的場(chǎng)景和需求來(lái)選擇使用ArrayList還是LinkedList。在實(shí)際開(kāi)發(fā)中,可以根據(jù)數(shù)據(jù)訪(fǎng)問(wèn)和操作的特點(diǎn)選擇最適合的集合類(lèi)。

ArrayList擴(kuò)容

每個(gè)ArrayList實(shí)例都有一個(gè)容量,該容量是指來(lái)存儲(chǔ)列表元素的數(shù)組的大小,該容量至少等于列表數(shù)組的大小,隨著ArrayList的不斷添加元素,其容量也在自動(dòng)增長(zhǎng),自動(dòng)增長(zhǎng)會(huì)將原來(lái)數(shù)組的元素向新的數(shù)組進(jìn)行copy。如果提前預(yù)判數(shù)據(jù)量的大小,可在構(gòu)造ArrayList時(shí)指定其容量。

  1. 創(chuàng)建新數(shù)組:根據(jù)當(dāng)前數(shù)組的容量和擴(kuò)容策略(一般是當(dāng)前容量的1.5倍或2倍),創(chuàng)建一個(gè)新的數(shù)組。

  2. 復(fù)制元素:將當(dāng)前數(shù)組中的元素逐個(gè)復(fù)制到新數(shù)組中。

  3. 更新引用:將ArrayList內(nèi)部的引用指向新數(shù)組,以便后續(xù)的操作使用新數(shù)組。

沒(méi)有指定初始容量時(shí),初始數(shù)組容量為10

4.垃圾回收:舊的數(shù)組因?yàn)闆](méi)有被引用,會(huì)由垃圾回收器進(jìn)行回收。

Array和ArrayList的區(qū)別

Array(數(shù)組)和ArrayList(數(shù)組列表)在以下幾個(gè)方面有區(qū)別:

  1. 大小固定 vs 可變大小:

    • 數(shù)組的大小是固定的,在創(chuàng)建時(shí)需要指定長(zhǎng)度,并且不能動(dòng)態(tài)地改變數(shù)組的大小。

    • ArrayList的大小是可變的,可以動(dòng)態(tài)地添加、刪除和修改元素,它會(huì)根據(jù)需要自動(dòng)增加或減少內(nèi)部存儲(chǔ)空間。

  2. 數(shù)據(jù)類(lèi)型:

    • 數(shù)組可以存儲(chǔ)任意類(lèi)型的元素,包括基本數(shù)據(jù)類(lèi)型(如int、char等)和引用數(shù)據(jù)類(lèi)型(如對(duì)象、字符串等)。

    • ArrayList只能存儲(chǔ)引用數(shù)據(jù)類(lèi)型的元素,不能直接存儲(chǔ)基本數(shù)據(jù)類(lèi)型,需要使用對(duì)應(yīng)的包裝類(lèi)(如Integer、Character等)進(jìn)行包裝。

  3. 內(nèi)存分配和訪(fǎng)問(wèn):

    • 數(shù)組在內(nèi)存中是連續(xù)分配的,可以通過(guò)索引直接訪(fǎng)問(wèn)元素,訪(fǎng)問(wèn)速度更快。

    • ArrayList內(nèi)部使用數(shù)組作為存儲(chǔ)結(jié)構(gòu),但是它還包含了額外的邏輯來(lái)支持動(dòng)態(tài)調(diào)整大小和其他操作。訪(fǎng)問(wèn)ArrayList中的元素需要通過(guò)方法調(diào)用。

  4. 功能和操作:

    • 數(shù)組提供了一組基本操作,如讀取和修改元素,通過(guò)索引查找元素等。但數(shù)組沒(méi)有提供高級(jí)的集合操作,需要手動(dòng)編寫(xiě)代碼來(lái)實(shí)現(xiàn)例如過(guò)濾、映射等功能。

    • ArrayList實(shí)現(xiàn)了Java的List接口,提供了一組豐富的方法來(lái)操作其中的元素,如添加、刪除、查找、排序等,同時(shí)還支持集合操作(如集合交并補(bǔ)、過(guò)濾、映射等)。

總結(jié)起來(lái),數(shù)組適合在大小固定且需要高效訪(fǎng)問(wèn)的情況下使用,而ArrayList適用于需要?jiǎng)討B(tài)大小和更多操作的場(chǎng)景。如果頻繁進(jìn)行插入、刪除等操作,并且不需要直接訪(fǎng)問(wèn)元素的具體索引位置,使用ArrayList更加方便。

List和數(shù)組之間的轉(zhuǎn)換

在Java中,可以使用以下方法進(jìn)行List和數(shù)組之間的轉(zhuǎn)換:

  1. List轉(zhuǎn)換為數(shù)組:

    • 使用List的toArray()方法將List轉(zhuǎn)換為數(shù)組。示例代碼如下:

      List<String> list = new ArrayList<>();
      // 添加元素到List
      list.add("Hello");
      list.add("World");
      ?
      // 轉(zhuǎn)換為數(shù)組
      String[] array = list.toArray(new String[0]);

      注意:在將List轉(zhuǎn)換為數(shù)組時(shí),需要提供一個(gè)指定類(lèi)型和大小的數(shù)組作為參數(shù)。如果指定的數(shù)組大小小于List的大小,則方法內(nèi)部會(huì)創(chuàng)建一個(gè)新的數(shù)組,并將List中的元素復(fù)制到新數(shù)組中。

  2. 數(shù)組轉(zhuǎn)換為L(zhǎng)ist:

    • 使用Arrays類(lèi)的asList()方法將數(shù)組轉(zhuǎn)換為L(zhǎng)ist。注意,這種方式返回的是一個(gè)固定大小的List,不能進(jìn)行添加、刪除操作。示例代碼如下:

      String[] array = { "Hello", "World" };
      ?
      // 轉(zhuǎn)換為L(zhǎng)ist
      List<String> list = Arrays.asList(array);

      通過(guò)asList()得到的List是一個(gè)固定大小的List,對(duì)其進(jìn)行添加或刪除操作會(huì)拋出UnsupportedOperationException異常。

    • 另一種方式是使用ArrayList的構(gòu)造方法,將數(shù)組中的元素逐個(gè)添加到ArrayList中。示例代碼如下:

      String[] array = { "Hello", "World" };
      ?
      // 轉(zhuǎn)換為L(zhǎng)ist
      List<String> list = new ArrayList<>(Arrays.asList(array));

      這種方式得到的是一個(gè)可操作的ArrayList,可以對(duì)其進(jìn)行添加、刪除等操作。

需要注意的是,在進(jìn)行List和數(shù)組之間的轉(zhuǎn)換時(shí),數(shù)組中的數(shù)據(jù)類(lèi)型必須與List中的元素類(lèi)型一致。

數(shù)組類(lèi)型和集合

##

高并發(fā)中的集合有哪些問(wèn)題

第一代線(xiàn)程安全集合類(lèi)

Vector、Hashtable

是怎么保證線(xiàn)程安排的: 使用synchronized修飾方法*

缺點(diǎn):效率低下

第二代線(xiàn)程非安全集合類(lèi)

ArrayList、HashMap

線(xiàn)程不安全,但是性能好,用來(lái)替代Vector、Hashtable ???????????

使用ArrayList、HashMap,需要線(xiàn)程安全怎么辦呢?

使用 Collections.synchronizedList(list); Collections.synchronizedMap(m);

底層使用synchronized代碼塊鎖 雖然也是鎖住了所有的代碼,但是鎖在方法里邊,并所在方法外邊性能可以理解為稍有提高吧。畢竟進(jìn)方法本身就要分配資源的

第三代線(xiàn)程安全集合類(lèi)

在大量并發(fā)情況下如何提高集合的效率和安全呢?

java.util.concurrent.*

ConcurrentHashMap:

CopyOnWriteArrayList :

CopyOnWriteArraySet: 注意 不是CopyOnWriteHashSet*

底層大都采用Lock鎖(1.8的ConcurrentHashMap不使用Lock鎖),保證安全的同時(shí),性能也很高。

ConcurrentHashMap底層原理是什么?

1.7 數(shù)據(jù)結(jié)構(gòu): 內(nèi)部主要是一個(gè)Segment數(shù)組,而數(shù)組的每一項(xiàng)又是一個(gè)HashEntry數(shù)組,元素都存在HashEntry數(shù)組里。因?yàn)槊看捂i定的是Segment對(duì)象,也就是整個(gè)HashEntry數(shù)組,所以又叫分段鎖。

1.8 數(shù)據(jù)結(jié)構(gòu): 與HashMap一樣采用:數(shù)組+鏈表+紅黑樹(shù)

底層原理則是采用鎖鏈表或者紅黑樹(shù)頭結(jié)點(diǎn),相比于HashTable的方法鎖,力度更細(xì),是對(duì)數(shù)組(table)中的桶(鏈表或者紅黑樹(shù))的頭結(jié)點(diǎn)進(jìn)行鎖定,這樣鎖定,只會(huì)影響數(shù)組(table)當(dāng)前下標(biāo)的數(shù)據(jù),不會(huì)影響其他下標(biāo)節(jié)點(diǎn)的操作,可以提高讀寫(xiě)效率。 putVal執(zhí)行流程:

  1. 判斷存儲(chǔ)的key、value是否為空,若為空,則拋出異常

  2. 計(jì)算key的hash值,隨后死循環(huán)(該循環(huán)可以確保成功插入,當(dāng)滿(mǎn)足適當(dāng)條件時(shí),會(huì)主動(dòng)終止),判斷table表為空或者長(zhǎng)度為0,則初始化table表

  3. 根據(jù)hash值獲取table中該下標(biāo)對(duì)應(yīng)的節(jié)點(diǎn),如果該節(jié)點(diǎn)為空,則根據(jù)參數(shù)生成新的節(jié)點(diǎn),并以CAS的方式進(jìn)行更新,并終止死循環(huán)。

  4. 如果該節(jié)點(diǎn)的hash值是MOVED(-1),表示正在擴(kuò)容,則輔助對(duì)該節(jié)點(diǎn)進(jìn)行轉(zhuǎn)移。

  5. 對(duì)數(shù)組(table)中的節(jié)點(diǎn),即桶的頭結(jié)點(diǎn)進(jìn)行鎖定,如果該節(jié)點(diǎn)的hash大于等于0,表示此桶是鏈表,然后對(duì)該桶進(jìn)行遍歷(死循環(huán)),尋找鏈表中與put的key的hash值相等,并且key相等的元素,然后進(jìn)行值的替換,如果到鏈表尾部都沒(méi)有符合條件的,就新建一個(gè)node,然后插入到該桶的尾部,并終止該循環(huán)遍歷。

  6. 如果該節(jié)點(diǎn)的hash小于0,并且節(jié)點(diǎn)類(lèi)型是TreeBin,則走紅黑樹(shù)的插入方式。

  7. 判斷是否達(dá)到轉(zhuǎn)化紅黑樹(shù)的閾值,如果達(dá)到閾值,則鏈表轉(zhuǎn)化為紅黑樹(shù)。

http://aloenet.com.cn/news/39928.html

相關(guān)文章:

  • 用java做網(wǎng)站要學(xué)什么谷歌獨(dú)立站推廣
  • 微信網(wǎng)站開(kāi)發(fā)教程視頻貴陽(yáng)網(wǎng)絡(luò)推廣排名
  • 隆昌市住房和城鄉(xiāng)建設(shè)廳網(wǎng)站可以引流推廣的app
  • 建設(shè)網(wǎng)站硬件千鋒教育地址
  • layui wordpressseo實(shí)戰(zhàn)密碼電子書(shū)
  • 注冊(cè)網(wǎng)站賬號(hào)違法嗎軟文廣告經(jīng)典案例300字
  • 做網(wǎng)站書(shū)面報(bào)告申請(qǐng)深圳谷歌推廣公司
  • 冀州網(wǎng)站建設(shè)價(jià)格2023網(wǎng)站推廣入口
  • 網(wǎng)站備案管理系統(tǒng)北京今日重大新聞
  • 優(yōu)化大師app下載汕頭seo建站
  • 合肥建設(shè)網(wǎng)絡(luò)賭博網(wǎng)站網(wǎng)站移動(dòng)端優(yōu)化工具
  • 縉云建設(shè)局網(wǎng)站網(wǎng)絡(luò)營(yíng)銷(xiāo)課程思政
  • 韋博在上面做課件的網(wǎng)站叫什么搜狗推廣登錄
  • 佛山倫教網(wǎng)站設(shè)計(jì)千鋒教育官網(wǎng)
  • app下載網(wǎng)站模板品牌推廣外包
  • 化妝品網(wǎng)站建設(shè)網(wǎng)站開(kāi)發(fā)流程有哪幾個(gè)階段
  • 中國(guó)南昌企業(yè)網(wǎng)站制作seo修改器
  • asp網(wǎng)站做安全全球搜
  • 企業(yè)網(wǎng)站怎么自適應(yīng)網(wǎng)站推廣的營(yíng)銷(xiāo)策劃方案
  • 順義哪里有做網(wǎng)站設(shè)計(jì)的萬(wàn)網(wǎng)官網(wǎng)登錄
  • 南通網(wǎng)站建設(shè)方案外包免費(fèi)的網(wǎng)站申請(qǐng)
  • php網(wǎng)站怎么做302未來(lái)網(wǎng)絡(luò)營(yíng)銷(xiāo)的發(fā)展趨勢(shì)
  • 網(wǎng)站都有什么類(lèi)型十五種常見(jiàn)的銷(xiāo)售策略
  • 網(wǎng)站備案時(shí)間有效期荊門(mén)剛剛發(fā)布的
  • 做網(wǎng)站獨(dú)立云服務(wù)器什么意思網(wǎng)絡(luò)推廣方法大全
  • 自己做網(wǎng)站網(wǎng)站資源哪里來(lái)百度關(guān)鍵詞推廣怎么收費(fèi)
  • 可以做熱圖的在線(xiàn)網(wǎng)站網(wǎng)站關(guān)鍵詞在哪里看
  • 電子商務(wù)網(wǎng)站建設(shè)前的分析網(wǎng)站排名優(yōu)化手機(jī)
  • iis7.5網(wǎng)站權(quán)限配置知了seo
  • 2015年做啥網(wǎng)站能致富網(wǎng)絡(luò)推廣運(yùn)營(yíng)推廣