欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何在 Java 中實(shí)現(xiàn)不可變類

 更新時(shí)間:2019年06月12日 15:02:27   作者:Neal Ford  
這篇文章主要介紹了如何在 Java 中實(shí)現(xiàn)不可變類,不變性是函數(shù)式編程的關(guān)鍵,因?yàn)樗c盡量減少變化部分的這一目標(biāo)相一致,這使得對(duì)這些部分的推斷更為容易一些。,需要的朋友可以參考下

前言

面向?qū)ο蟮木幊掏ㄟ^(guò)封裝可變動(dòng)的部分來(lái)構(gòu)造能夠讓人讀懂的代碼,函數(shù)式編程則是通過(guò)最大程度地減少 可變動(dòng)的部分來(lái)構(gòu)造出可讓人讀懂的代碼。
— Michael Feathers,Working with Legacy Code 一文的作者

在這一部分中,我討論的是函數(shù)式編程的基石之一:不變性。一個(gè)不可變對(duì)象的狀態(tài)在其構(gòu)造完成之后就不可改變,換句話說(shuō),構(gòu)造函數(shù)是唯一一個(gè)您可以改變對(duì)象的狀態(tài)的地方。如果您想要改變一個(gè)不可變對(duì)象的話,您不會(huì)改變它,而是使用修改后的值來(lái)創(chuàng)建一個(gè)新的對(duì)象,并把您的引用指向它。(String 就是構(gòu)建在 Java 語(yǔ)言內(nèi)核中的不可變類的一個(gè)典型例子。)不變性是函數(shù)式編程的關(guān)鍵,因?yàn)樗c盡量減少變化部分的這一目標(biāo)相一致,這使得對(duì)這些部分的推斷更為容易一些。

在 Java 中實(shí)現(xiàn)不可變類

諸如 Java、Ruby、Perl、Groovy 和 C# 一類的現(xiàn)代面向?qū)ο笳Z(yǔ)言都擁有一些內(nèi)置的便利機(jī)制,這些機(jī)制使得以可控方式來(lái)修改狀態(tài)變得很容易。然而,狀態(tài)對(duì)于計(jì)算來(lái)說(shuō)是如此基礎(chǔ)的信息,因此您永遠(yuǎn)也無(wú)法預(yù)料它會(huì)在哪個(gè)地方出紕漏。例如,由于大量可變化機(jī)制的存在,因此用面向?qū)ο蟮恼Z(yǔ)言編寫高性能的、正確的多線程代碼會(huì)很困難。因?yàn)?Java 已針對(duì)操縱狀態(tài)進(jìn)行了優(yōu)化,因此您不得不繞過(guò)這樣的一些機(jī)制來(lái)獲得的不變性的一些好處。不過(guò)一旦您了解了要避免的一些陷阱之后,在 Java 中構(gòu)建不可變類這件事情就會(huì)變得非常容易。

定義不可變類

要將一個(gè) Java 類構(gòu)造成不可變的類,您必須執(zhí)行以下操作:

  • 把所有的域聲明成 final。

在 Java 中將域定義成 final 之后,您必須在聲明的時(shí)候初始化它們,或是在構(gòu)造函數(shù)中初始化它們。如果您的 IDE 抱怨您沒有在聲明的時(shí)候初始化它們,別緊張;當(dāng)您在構(gòu)造函數(shù)中寫入適當(dāng)?shù)拇a后,他們就會(huì)意識(shí)到您知道自己在做什么。

  • 將類聲明為 final,這樣就不會(huì)重寫它。

如果可以重寫類的話,則可以重寫它的方法的行為,因此您最安全的選擇就是不允許將類子類化。注意,這就是 Java 的 String 類使用的策略。

  • 不要提供一個(gè)無(wú)參數(shù)的構(gòu)造函數(shù)。

如果您有一個(gè)不可變對(duì)象,則必須要在構(gòu)造函數(shù)中設(shè)置該對(duì)象將包含的任何狀態(tài)。如果沒有狀態(tài)要設(shè)置,那么要一個(gè)對(duì)象來(lái)干什么?無(wú)狀態(tài)類的靜態(tài)方法一樣可以起到很好的作用;因此,您永遠(yuǎn)都不該為一個(gè)不可變類提供一個(gè)無(wú)參數(shù)的構(gòu)造函數(shù)。如果您正在使用的框架因?yàn)槟承┰蛐枰褂眠@樣的構(gòu)造函數(shù)的話,那么您可以了解以下能否通過(guò)提供一個(gè)私有的無(wú)參數(shù)構(gòu)造函數(shù)(這是經(jīng)由反射可見的)來(lái)滿足這一要求。

需要注意的一點(diǎn)是,無(wú)參數(shù)構(gòu)造函數(shù)的缺失違反了 JavaBeans 的標(biāo)準(zhǔn),該標(biāo)準(zhǔn)堅(jiān)持要有一個(gè)默認(rèn)的構(gòu)造函數(shù)。不過(guò) JavaBeans 無(wú)論如何都不可能是不可變的,這是 setXXX 方法的工作方式所決定的。

  • 至少提供一個(gè)構(gòu)造函數(shù)。

如果您沒有提供一個(gè)無(wú)參數(shù)構(gòu)造函數(shù)的話,那么這是您給對(duì)象添加一些狀態(tài)的最后機(jī)會(huì)!

  • 除構(gòu)造函數(shù)之外,不再提供任何的可變方法。

您不僅要避免典型的受 JavaBeans 啟發(fā)的 setXXX 方法,還必須注意不要返回可變的對(duì)象引用。對(duì)象引用被聲明為 final,這是實(shí)情,但這并不意味這您無(wú)法更改它所指向的內(nèi)容。因此,您需要確保您已經(jīng)防御性地復(fù)制了從 getXXX 方法中返回的任何對(duì)象引用。

“傳統(tǒng)的” 不可變類

清單 1 中列出了一個(gè)滿足上述要求的不可變類:

清單 1. Java 中的不可變的 Address 類

public final class Address {
private final String name;
private final List<String> streets;
private final String city;
private final String state;
private final String zip;
public Address(String name, List<String> streets, 
String city, String state, String zip) {
this.name = name;
this.streets = streets;
this.city = city;
this.state = state;
this.zip = zip;
}
public String getName() {
return name;
}
public List<String> getStreets() {
return Collections.unmodifiableList(streets);
}
public String getCity() {
return city;
}
public String getState() {
return state;
}
public String getZip() {
return zip;
}
}

需要注意的一點(diǎn)是,可以使用 清單 1 中的 Collections.unmodifiableList() 方法對(duì) streets 列表進(jìn)行防御性復(fù)制。您應(yīng)該始終使用集合而不是數(shù)組來(lái)創(chuàng)建不可變列表,盡管防御性的數(shù)組復(fù)制也是可行的,但這會(huì)帶來(lái)一些不希望見到的副作用。考慮一下清單 2 中的代碼:

清單 2. 使用數(shù)組而非集合的 Customer 類

public class Customer {
public final String name;
private final Address[] address;
public Customer(String name, Address[] address) {
this.name = name;
this.address = address;
}
public Address[] getAddress() {
return address.clone();
}
}

在您嘗試著在從 getAddress() 方法調(diào)用中返回的克隆數(shù)組上進(jìn)行任何操作的時(shí)候,清單 2 中的代碼問(wèn)題就暴露出來(lái)了,如清單 3 所示:

清單 3. 展示了正確但非直觀結(jié)果的測(cè)試

public static List<String> streets(String... streets) {
return asList(streets);
}
public static Address address(List<String> streets, 
String city, String state, String zip) {
return new Address(streets, city, state, zip);
}
@Test public void immutability_of_array_references_issue() {
Address [] addresses = new Address[] {
address(streets("201 E Washington Ave", "Ste 600"), "Chicago", "IL", "60601")};
Customer c = new Customer("ACME", addresses);
assertEquals(c.getAddress()[0].city, addresses[0].city);
Address newAddress = new Address(
streets("HackerzRulz Ln"), "Hackerville", "LA", "00000");
// doesn't work, but fails invisibly
c.getAddress()[0] = newAddress;
// illustration that the above unable to change to Customer's address
assertNotSame(c.getAddress()[0].city, newAddress.city);
assertSame(c.getAddress()[0].city, addresses[0].city);
assertEquals(c.getAddress()[0].city, addresses[0].city);
}

在返回一個(gè)克隆數(shù)組的時(shí)候,您保護(hù)了底層的數(shù)組,但您交還的數(shù)組看起來(lái)就像是一個(gè)普通的數(shù)組,也就是說(shuō),您可以修改該數(shù)組的內(nèi)容(即使持有該數(shù)組的變量是 final,因?yàn)檫@只在數(shù)組引用自身上起作用,在非數(shù)組的內(nèi)容上不起作用)。在使用 Collections.unmodifiableList() (以及 Collections 中用于其他類型的一系列方法)時(shí),您會(huì)收到一個(gè)對(duì)象引用,它沒有改變方法的可用性。

更清晰的不可變類

您可能經(jīng)常聽到這樣的說(shuō)法:您還應(yīng)該將不可變域聲明為私有域。在聽到有人以一種不同的、但明確的看法來(lái)澄清一些根深蒂固的臆斷之后,我不再同意這樣的觀點(diǎn)了。在 Michael Fogus 對(duì) Clojure 的創(chuàng)建者 Rich Hickey 所做的訪談中),Hickey 談到了 Clojure 的許多核心部分都缺少數(shù)據(jù)隱藏式的封裝。Clojure 在這一方面一直困擾著我,因?yàn)槲沂侨绱顺撩曰跔顟B(tài)的思考方式。

但在那之后,我意識(shí)到了,如果域是不可變的話,則無(wú)需擔(dān)心它們被暴露出來(lái)。許多我們用在封裝中的保障措施實(shí)際上就是為了防止發(fā)生改變,一旦我們梳理清楚了這兩個(gè)概念,一種更清晰的 Java 實(shí)現(xiàn)就浮現(xiàn)了出來(lái)。

請(qǐng)考慮清單 4 中的 Address 類版本:

清單 4. 使用了公共不可變域的 Address 類

public final class Address {
private final List<String> streets;
public final String city;
public final String state;
public final String zip;
public Address(List<String> streets, String city, String state, String zip) {
this.streets = streets;
this.city = city;
this.state = state;
this.zip = zip;
}
public final List<String> getStreets() {
return Collections.unmodifiableList(streets);
}
}

在您想要隱藏底層表示形式的時(shí)候,只有為不可變域聲明公共的 getXXX() 方法才會(huì)帶一些好處,但在重構(gòu)期間會(huì)有一些顯而易見的好處,比如可以很容易地發(fā)現(xiàn)細(xì)微的改變。通過(guò)將域聲明成公共的或是不可變的,就能夠直接在代碼中訪問(wèn)它們,無(wú)需擔(dān)心不小心更改它們的情況發(fā)生。

一開始的時(shí)候,使用不可變域似乎有些不自然,如果您聽過(guò) 憤怒的猴子 這個(gè)故事的話,就會(huì)知道這種不同其實(shí)是有好處的:您還不習(xí)慣于處理 Java 中的不可變類,這看起來(lái)像是一種新的類型,如清單 5 中所示:

清單 5. Address 類的單元測(cè)試

@Test (expected = UnsupportedOperationException.class)
public void address_access_to_fields_but_enforces_immutability() {
Address a = new Address(
streets("201 E Randolph St", "Ste 25"), "Chicago", "IL", "60601");
assertEquals("Chicago", a.city);
assertEquals("IL", a.state);
assertEquals("60601", a.zip);
assertEquals("201 E Randolph St", a.getStreets().get(0));
assertEquals("Ste 25", a.getStreets().get(1));
// compiler disallows
//a.city = "New York";
a.getStreets().clear();
}

對(duì)公有不可變域的訪問(wèn)避免了一系列 getXXX() 調(diào)用所帶來(lái)的可見開銷,還要注意的是,編譯器不會(huì)允許您給這些原始類型中的任一個(gè)賦值,如果您試著調(diào)用 street 集合上的可變方法的話,您就會(huì)收到一個(gè) UnsupportedOperationException (方式是在測(cè)試的頂部捕獲)。這種代碼風(fēng)格的使用從視覺上給出了一種強(qiáng)烈的指示:該類是一個(gè)不可變類。

不利的方面

這種更清晰的語(yǔ)法的一個(gè)可能缺點(diǎn)是需要花一些精力來(lái)學(xué)習(xí)這種新的編程技法,不過(guò)我覺得這樣做是值得的:這一過(guò)程會(huì)促進(jìn)您在創(chuàng)建類的時(shí)候想著不變性,因?yàn)轭惖娘L(fēng)格是如此明顯不同,并且刪除了不必要的樣板代碼。不過(guò) Java 中的這種代碼風(fēng)格也有著一些缺點(diǎn)(說(shuō)句公道話,Java 的直接目的從來(lái)都不是為了迎合不變性):

1.正如 Glenn Vanderburg 向我指出的那樣,最大的缺點(diǎn)是這一風(fēng)格違反了 Bertrand Meyer(Eiffel 編程語(yǔ)言的創(chuàng)建者)所說(shuō)的統(tǒng)一訪問(wèn)原則 (Uniform Access Principle):模塊提供的所有服務(wù)應(yīng)該是通過(guò)一種統(tǒng)一的標(biāo)記法來(lái)使用的,無(wú)論服務(wù)是通過(guò)存儲(chǔ)還是通過(guò)計(jì)算來(lái)實(shí)現(xiàn)的,都不能違背這種標(biāo)記法。換句話說(shuō),對(duì)域的訪問(wèn)不應(yīng)該暴露出該域是一個(gè)域還是一個(gè)返回值的方法。Address 類的 getStreets() 方法與其他域沒有保持統(tǒng)一。這一問(wèn)題在 Java 中不可能得到真正的解決;但在其他的一些 JVM 語(yǔ)言中已經(jīng)通過(guò)實(shí)現(xiàn)不變性解決了這個(gè)問(wèn)題。

2.一些重度依賴反射的框架無(wú)法使用這種編程技法來(lái)工作,因?yàn)樗麄冃枰粋€(gè)默認(rèn)的構(gòu)造函數(shù)。

3.因?yàn)槟莿?chuàng)建新的對(duì)象而不是改變?cè)械膶?duì)象,因此有著大量更新的系統(tǒng)可能就會(huì)導(dǎo)致以為垃圾收集而帶來(lái)的效率低下。Clojure 一類的語(yǔ)言內(nèi)置了一些工具,通過(guò)使用不可變引用將這種情況變得更高效一些,這是這些語(yǔ)言中的默認(rèn)做法。

Groovy 中的不可變性

可以使用 Groovy 來(lái)構(gòu)建公共的不可變域版本的 Address 類,這帶來(lái)的是一種非常清晰的實(shí)現(xiàn),如清單 6 所示:

清單 6. 使用 Groovy 編寫的不可變的 Address 類

class Address {
def public final List<String> streets;
def public final city;
def public final state;
def public final zip;
def Address(streets, city, state, zip) {
this.streets = streets;
this.city = city;
this.state = state;
this.zip = zip;
}
def getStreets() {
Collections.unmodifiableList(streets);
}
}

一如既往,Groovy 需要的樣板代碼要比 Java 的少,并且還提供了其他方面的一些好處。因?yàn)?Groovy 允許您使用熟悉的 get/set 語(yǔ)法來(lái)創(chuàng)建屬性,因此您可以為對(duì)象引用創(chuàng)建真正受保護(hù)的屬性??紤]一下清單 7 中給出的單元測(cè)試:

清單 7. 單元測(cè)試展示了 Groovy 中的統(tǒng)一訪問(wèn)

class AddressTest {
@Test (expected = ReadOnlyPropertyException.class)
void address_primitives_immutability() {
Address a = new Address(
["201 E Randolph St", "25th Floor"], "Chicago", "IL", "60601")
assertEquals "Chicago", a.city
a.city = "New York"
}
@Test (expected=UnsupportedOperationException.class)
void address_list_references() {
Address a = new Address(
["201 E Randolph St", "25th Floor"], "Chicago", "IL", "60601")
assertEquals "201 E Randolph St", a.streets[0]
assertEquals "25th Floor", a.streets[1]
a.streets[0] = "404 W Randoph St"
}
}

可以注意到,在這兩個(gè)用例中,測(cè)試會(huì)在拋出異常時(shí)終止,這是因?yàn)橛姓Z(yǔ)句違反了不可變性合約。不過(guò)在 清單 7 中,streets 屬性看起來(lái)就像是原始類型,但實(shí)際上它是用自己的 getStreets() 方法來(lái)保護(hù)其自身。

Groovy 的 @Immutable 注釋

本文章系列所持的一個(gè)基本宗旨就是,函數(shù)式語(yǔ)言應(yīng)該為您處理更多低層面的細(xì)節(jié)。一個(gè)很好的例子就是 Groovy 的 1.7 版本增加了 @Immutable 注解,該注解使得 清單 6 中的編碼方式變得不再重要了。清單 8 給出了一個(gè)使用了該注解的 Client 類:

清單 8. 不可變的 Client 類

@Immutable
class Client {
String name, city, state, zip
String[] streets
}

因?yàn)橛玫搅?@Immutable 注解,該類具有以下一些特點(diǎn):

  • 它是最終的。
  • 屬性自動(dòng)擁有了私有的、合成了 get 方法的域。
  • 任何更新屬性的企圖都會(huì)導(dǎo)致拋出 ReadOnlyPropertyException 異常。
  • Groovy 既創(chuàng)建了有序的構(gòu)造函數(shù),又創(chuàng)建了基于映射的構(gòu)造函數(shù)。
  • 集合類被封裝在適當(dāng)?shù)陌b器中,數(shù)組(及其他可克隆的對(duì)象)被克隆。
  • 自動(dòng)生成默認(rèn)的 equals、hashcode 和 toString 方法。

一句注解提供了這么多的作用!它的行為也正如您所期望的那樣,如清單 9 所示:

清單 9. @Immutable 注解正確地處理了預(yù)期的情況

@Test (expected = ReadOnlyPropertyException)
void client_object_references_protected() {
def c = new Client([streets: ["201 E Randolph St", "Ste 25"]])
c.streets = new ArrayList();
}
@Test (expected = UnsupportedOperationException)
void client_reference_contents_protected() {
def c = new Client ([streets: ["201 E Randolph St", "Ste 25"]])
c.streets[0] = "525 Broadway St"
}
@Test
void equality() {
def d = new Client(
[name: "ACME", city:"Chicago", state:"IL",
zip:"60601",
streets: ["201 E Randolph St", "Ste 25"]])
def c = new Client(
[name: "ACME", city:"Chicago", state:"IL",
zip:"60601",
streets: ["201 E Randolph St", "Ste 25"]])
assertEquals(c, d)
assertEquals(c.hashCode(), d.hashCode())
assertFalse(c.is(d))
}

試圖重置對(duì)象引用的操作會(huì)導(dǎo)致拋出 ReadOnlyPropertyException 異常。如果試圖改變其中的一個(gè)被封裝起來(lái)的對(duì)象引用所指向的內(nèi)容,則會(huì)導(dǎo)致拋出 UnsupportedOperationException 異常。該注解還創(chuàng)建了適當(dāng)?shù)?equals 和 hashcode 方法,如最后一個(gè)測(cè)試中所示,對(duì)象內(nèi)容是相同的,但它們沒有指向同一個(gè)引用。

當(dāng)然,Scala 和 Clojure 都支持并促進(jìn)了不變性,且都有著清晰的不變性語(yǔ)法,接下來(lái)的文章會(huì)不時(shí)地談到它們所帶來(lái)的影響。

不變性的好處

在像函數(shù)式編程者那樣思考的方法列表中,維護(hù)不變性處于列表的較高位置。盡管用 Java 來(lái)構(gòu)建不可變對(duì)象前期帶來(lái)了更多的復(fù)雜性,但由這種抽象帶來(lái)的后期簡(jiǎn)易性很容易補(bǔ)償前面所做的工作。

不可變類擯棄了 Java 中許多一些典型的令人煩心的缺陷。轉(zhuǎn)向函數(shù)式編程的好處之一是讓人們意識(shí)到,測(cè)試的存在是為了檢查代碼中成功發(fā)生的轉(zhuǎn)變。換句話說(shuō),測(cè)試的真正目的是驗(yàn)證改變,改變?cè)蕉啵托枰蕉嗟臏y(cè)試來(lái)確保您的做法是正確的。如果您通過(guò)嚴(yán)格限制改變來(lái)隔離變化的發(fā)生,那么您為錯(cuò)誤的發(fā)生制造了更小的空間,需要測(cè)試的地方也就更少。因?yàn)樽兓粫?huì)發(fā)生構(gòu)造函數(shù)中,因此不可變類會(huì)將編寫單元測(cè)試變成了一件微不足道的事情。

您不需要使用復(fù)制構(gòu)造函數(shù),并且永遠(yuǎn)也不需要大汗淋漓地去實(shí)現(xiàn) clone() 方法的那些令人慘不忍睹的細(xì)節(jié)。將不可變對(duì)象用作 Map 或是 Set 中的鍵值是也一種很不錯(cuò)的選擇;因?yàn)?Java 的字典集合中的鍵不能更改值,因此,在將不可變對(duì)象用作鍵時(shí),它是非常好用的鍵。

不可變對(duì)象也是自動(dòng)線程安全的,不存在同步問(wèn)題。它們也不可能因?yàn)楫惓5陌l(fā)生而處于一種未知的或是無(wú)法預(yù)期的狀態(tài)中。因?yàn)樗械某跏蓟及l(fā)生在構(gòu)造階段,這在 Java 中是一個(gè)原子過(guò)程,所有異常都發(fā)生在擁有對(duì)象實(shí)例之前。Joshua Bloch 將這稱作失敗的原子性:在已經(jīng)構(gòu)建對(duì)象后,這種基于不可變性的成功或是失敗就是一錘定音的了

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • MyBatis3源碼解析之如何獲取數(shù)據(jù)源詳解

    MyBatis3源碼解析之如何獲取數(shù)據(jù)源詳解

    用myBatis3與spring整合的時(shí)候,我們可以通過(guò)多種方式獲取數(shù)據(jù)源,下面這篇文章主要給大家介紹了關(guān)于MyBatis3源碼解析之如何獲取數(shù)據(jù)源的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-06-06
  • elasticsearch啟動(dòng)警告無(wú)法鎖定JVM內(nèi)存

    elasticsearch啟動(dòng)警告無(wú)法鎖定JVM內(nèi)存

    今天小編就為大家分享一篇關(guān)于elasticsearch啟動(dòng)警告無(wú)法鎖定JVM內(nèi)存,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-03-03
  • springboot通過(guò)注解、接口創(chuàng)建定時(shí)任務(wù)詳解

    springboot通過(guò)注解、接口創(chuàng)建定時(shí)任務(wù)詳解

    使用SpringBoot創(chuàng)建定時(shí)任務(wù)其實(shí)是挺簡(jiǎn)單的,這篇文章主要給大家介紹了關(guān)于springboot如何通過(guò)注解、接口創(chuàng)建這兩種方法實(shí)現(xiàn)定時(shí)任務(wù)的相關(guān)資料,需要的朋友可以參考下
    2021-07-07
  • Java如何利用return結(jié)束方法調(diào)用

    Java如何利用return結(jié)束方法調(diào)用

    這篇文章主要介紹了Java如何利用return結(jié)束方法調(diào)用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • SpringBoot整合RabbitMQ 手動(dòng)應(yīng)答(簡(jiǎn)單demo)

    SpringBoot整合RabbitMQ 手動(dòng)應(yīng)答(簡(jiǎn)單demo)

    這篇文章主要介紹了SpringBoot整合RabbitMQ 手動(dòng)應(yīng)答 簡(jiǎn)單demo,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • idea版本控制忽略.idea文件和.iml文件的問(wèn)題

    idea版本控制忽略.idea文件和.iml文件的問(wèn)題

    這篇文章主要介紹了idea版本控制忽略.idea文件和.iml文件,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • 如何解決idea安裝插件后報(bào)錯(cuò)打不開問(wèn)題

    如何解決idea安裝插件后報(bào)錯(cuò)打不開問(wèn)題

    這篇文章主要介紹了如何解決idea安裝插件后報(bào)錯(cuò)打不開問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • POI對(duì)Excel自定義日期格式的讀取(實(shí)例代碼)

    POI對(duì)Excel自定義日期格式的讀取(實(shí)例代碼)

    下面小編就為大家?guī)?lái)一篇POI對(duì)Excel自定義日期格式的讀取(實(shí)例代碼)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-11-11
  • 解決mybatis-plus3.4.1分頁(yè)插件PaginationInterceptor和防止全表更新與刪除插件SqlExplainInterceptor過(guò)時(shí)失效問(wèn)題

    解決mybatis-plus3.4.1分頁(yè)插件PaginationInterceptor和防止全表更新與刪除插件SqlE

    這篇文章給大家介紹了在Spring.xml文件中配置mybatis-plus3.4.1分頁(yè)插件PaginationInterceptor和防止全表更新與刪除插件SqlExplainInterceptor過(guò)時(shí)失效問(wèn)題解決方案,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2020-12-12
  • java 使用線程監(jiān)控文件目錄變化的實(shí)現(xiàn)方法

    java 使用線程監(jiān)控文件目錄變化的實(shí)現(xiàn)方法

    這篇文章主要介紹了java 使用線程監(jiān)控文件目錄變化的實(shí)現(xiàn)方法的相關(guān)資料,希望通過(guò)本文能幫助到大家,需要的朋友可以參考下
    2017-10-10

最新評(píng)論