国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

首頁(yè) Java java教程 4 中最有趣的 Java 錯(cuò)誤

4 中最有趣的 Java 錯(cuò)誤

Jan 01, 2025 am 09:19 AM

2024 年,我們分析了大量項(xiàng)目,并在博客上分享我們的發(fā)現(xiàn)。現(xiàn)在是除夕夜——是時(shí)候講喜慶故事了!我們收集了在開源項(xiàng)目中檢測(cè)到的最有趣的 Java 錯(cuò)誤,現(xiàn)在將它們帶給您!

Top most intriguing Java errors in 4

前言

我們長(zhǎng)期以來(lái)一直秉承發(fā)布 PVS-Studio 檢測(cè)到的最有趣的 bug 的傳統(tǒng),但自 2020 年以來(lái)就沒(méi)有出現(xiàn)與 Java 相關(guān)的置頂!這一次,我嘗試復(fù)興復(fù)古風(fēng)格。我希望您手邊有一條舒適的毯子和一杯熱茶,因?yàn)槲覍iT為您挑選了 10 多種有趣的昆蟲。以下是他們的排名:

  • 我的個(gè)人意見;
  • 該錯(cuò)誤的有趣背景;
  • 多樣性、可信度和重要性。

準(zhǔn)備好用自己的編程智慧來(lái)迎接 10 個(gè)有趣的故事吧——除夕夜很快就要到來(lái)了:)

第10名?;氐轿磥?lái)

第十位,第一個(gè)代碼片段張開雙臂歡迎我們。

public Builder setPersonalisation(Date date, ....) {
  ....
  final OutputStreamWriter
    out = new OutputStreamWriter(bout, "UTF-8");
  final DateFormat
    format = new SimpleDateFormat("YYYYMMdd");
  out.write(format.format(date));
    ....
}

我忍不住把它放在除夕夜的頂部,因?yàn)檫@段代碼中的一個(gè)錯(cuò)誤可以讓我們更快地到達(dá)下一年:)猜猜這個(gè)錯(cuò)誤是從哪里來(lái)的?

讓我們看一下傳遞給 SimpleDateFormat 構(gòu)造函數(shù)的參數(shù)??雌饋?lái)有效嗎?如果我們傳遞幾乎任何日期,例如撰寫本文的日期 (10/12/2024),代碼將返回正確的值 20241210。

但是,如果我們傳遞 29/12/2024,它將返回 20251229,從而巧妙地提前進(jìn)入新年。順便說(shuō)一句,時(shí)光倒流也是可行的。

發(fā)生這種情況是因?yàn)?SimpleDateFormat 參數(shù)中的 Y 字符代表基于周數(shù)的年份。簡(jiǎn)而言之,當(dāng)一周至少包含新年的四天時(shí),該一周被視為第一周。所以,如果我們的一周從周日開始,我們就可以提前三天進(jìn)入新的一年。

要修復(fù)此問(wèn)題,只需將大寫 Y 替換為小寫 y 即可。想了解更多嗎?我們專門寫了一整篇文章來(lái)討論這個(gè)主題。

這是針對(duì)此錯(cuò)誤的 PVS-Studio 警告:

V6122 檢測(cè)到使用“Y”(周年)模式:它可能打算使用“y”(年)。 SkeinParameters.java 246

由于周數(shù)的具體情況,因此測(cè)試并不是發(fā)現(xiàn)此錯(cuò)誤的最佳方法。那么為什么這樣一個(gè)話題性的錯(cuò)誤會(huì)出現(xiàn)在最后呢?原因是該警告不是來(lái)自 Bouncy Castle 的實(shí)際版本,而是來(lái)自我們的測(cè)試基地。舊的資源仍然存在,并且這個(gè)錯(cuò)誤已經(jīng)修復(fù)了很長(zhǎng)時(shí)間。這是來(lái)自過(guò)去的致敬,又是一次時(shí)光旅行:)

第9名。 “看來(lái)不行”

第九位,我們收到來(lái)自 GeoServer 分析的警告:

@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // ....
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);              // <=
    Assert.assertEquals(newValue, oldValue);
  }
}

這是 PVS-Studio 警告:

V6027 變量“newValue”、“oldValue”通過(guò)調(diào)用同一函數(shù)進(jìn)行初始化。這可能是一個(gè)錯(cuò)誤或未優(yōu)化的代碼。 DataAccessRuleDAOTest.java 110、DataAccessRuleDAOTest.java 111

這樣的錯(cuò)誤有什么有趣的?讓我來(lái)揭示這四個(gè)點(diǎn)背后隱藏的是什么:

public Builder setPersonalisation(Date date, ....) {
  ....
  final OutputStreamWriter
    out = new OutputStreamWriter(bout, "UTF-8");
  final DateFormat
    format = new SimpleDateFormat("YYYYMMdd");
  out.write(format.format(date));
    ....
}

有評(píng)論稱該代碼由于某種原因無(wú)法運(yùn)行。說(shuō)實(shí)話,我第一次看到的時(shí)候就笑了。

不過(guò),這個(gè)評(píng)論相當(dāng)含糊。測(cè)試很可能是故意以這種方式編寫的,以防止在比較失敗時(shí)出現(xiàn)故障。然而,該代碼已經(jīng)處于這種狀態(tài)十多年了,這引發(fā)了一些問(wèn)題。這種模糊性就是我沒(méi)有將其排名更高的原因。

第8名。腳中彈

如果我們不能將 JBullet 中的 bug 稱為“搬起石頭砸自己的腳”,我不知道哪些可以這樣稱呼。這是文章中的一個(gè)錯(cuò)誤:

@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // ....
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);              // <=
    Assert.assertEquals(newValue, oldValue);
  }
}

我認(rèn)為我們甚至不需要 PVS-Studio 警告來(lái)發(fā)現(xiàn)錯(cuò)誤所在。無(wú)論如何,以防萬(wàn)一,這里是:

V6026 該值已分配給“proxy1”變量。 HashedOverlappingPairCache.java 233

是的,這是一個(gè)令人尷尬的簡(jiǎn)單錯(cuò)誤。不過(guò),這種簡(jiǎn)單性讓它變得更加搞笑。盡管如此,它還是有自己的故事。

JBullet 庫(kù)是 C/C 子彈庫(kù)的移植,那里有類似的功能:

@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // properties equality does not seem to work...
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);
    Assert.assertEquals(newValue, oldValue);
  }
}

很容易看出這段代碼寫得正確。從 gitblame 來(lái)看,原來(lái)寫的是正確的。原來(lái)是代碼從一種語(yǔ)言移植到另一種語(yǔ)言時(shí)出現(xiàn)了錯(cuò)誤。

由于其驚人的樸實(shí)加上豐富的歷史,我將這個(gè)警告評(píng)為第八名。我希望你喜歡這個(gè)搬起石頭砸自己腳的 bug 原來(lái)是與 C 語(yǔ)言相關(guān)的。

第七名。即使是偉大的數(shù)學(xué)家也會(huì)犯錯(cuò)誤

誠(chéng)然,下一個(gè)警告出于多種原因溫暖了我的心。以下是 GeoGebra 檢查的代碼片段:

@Override
public BroadphasePair findPair(BroadphaseProxy proxy0, BroadphaseProxy proxy1) {
  BulletStats.gFindPairs++;
  if (proxy0.getUid() > proxy1.getUid()) {
    BroadphaseProxy tmp = proxy0;
    proxy0 = proxy1;
    proxy1 = proxy0;
  }
  ....
}

嘗試自己找出錯(cuò)誤!為了不讓你們偷看,我把警告和解釋隱藏在劇透里了。

答案
首先,我們看一下PVS-Studio的警告:

V6107 正在使用常量 0.7071067811865。結(jié)果值可能不準(zhǔn)確??紤]使用 Math.sqrt(0.5)。 DrawAngle.java 303

事實(shí)上,0.7071067811865 并不是什么神奇的數(shù)字——它只是 0.5 平方根的四舍五入結(jié)果。但這種精度損失有多嚴(yán)重呢? GeoGebra 是一款為數(shù)學(xué)家量身定制的軟件,額外的精度似乎并沒(méi)有什么壞處。

為什么我這么喜歡這個(gè)bug?

首先,在會(huì)議上,我經(jīng)常要求與會(huì)者在其他代碼片段中找到類似的錯(cuò)誤。當(dāng)錯(cuò)誤隱藏在常量中時(shí),看著他們仔細(xì)分析代碼總是很有趣。

其次,這是我為 Java 分析器實(shí)現(xiàn)的第一個(gè)診斷規(guī)則。這就是為什么我無(wú)法抗拒將它放在頂部的原因——即使意識(shí)到了偏見——我把它放在第七位:)

第六名。這個(gè)模式不起作用

以下警告是我從基于 DBeaver 檢查的第一篇文章中獲取的,可能不會(huì)立即引起我們的注意。這是一個(gè)代碼片段:

public Builder setPersonalisation(Date date, ....) {
  ....
  final OutputStreamWriter
    out = new OutputStreamWriter(bout, "UTF-8");
  final DateFormat
    format = new SimpleDateFormat("YYYYMMdd");
  out.write(format.format(date));
    ....
}

以下是 PVS-Studio 分析器檢測(cè)到的內(nèi)容:

V6082 不安全的雙重檢查鎖定。該字段應(yīng)聲明為易失性的。 TaskImpl.java 59、TaskImpl.java317

雖然這個(gè)特定的警告沒(méi)有什么特別的,但我仍然覺得它非常有趣。關(guān)鍵是所應(yīng)用的雙重檢查鎖定模式不起作用。有什么竅門呢?這在 20 年前是相關(guān)的:)

如果您想了解有關(guān)該主題的更多信息,我建議您閱讀全文。但現(xiàn)在,讓我給您一個(gè)快速總結(jié)。

雙重檢查鎖定模式用于在多線程環(huán)境中實(shí)現(xiàn)延遲初始化。在“重量級(jí)”檢查之前,會(huì)在沒(méi)有同步塊的情況下執(zhí)行“輕量級(jí)”檢查。僅當(dāng)兩項(xiàng)檢查都通過(guò)時(shí)才會(huì)創(chuàng)建資源。

但是,在這種方法中,對(duì)象創(chuàng)建是非原子,并且處理器和編譯器可以更改操作順序。因此,另一個(gè)線程可能會(huì)意外收到部分創(chuàng)建的對(duì)象并開始使用它,這可能會(huì)導(dǎo)致不正確的行為。這個(gè)錯(cuò)誤可能很少發(fā)生,因此調(diào)試對(duì)于開發(fā)人員來(lái)說(shuō)將是一個(gè)巨大的挑戰(zhàn)。

這里有一個(gè)變化:這種模式直到 JDK 5 才起作用。從 JDK 5 開始,由于 happens-before 原則,引入了 volatile 關(guān)鍵字來(lái)解決重新排序操作的潛在問(wèn)題。分析器警告我們應(yīng)該添加此關(guān)鍵字。

但是,無(wú)論如何,最好避免這種模式。從那時(shí)起,硬件和 JVM 性能已經(jīng)取得了長(zhǎng)足的進(jìn)步,并且同步操作不再那么慢了。然而,不正確地實(shí)現(xiàn) DCL 模式仍然是一個(gè)常見的陷阱,可能會(huì)產(chǎn)生上述的嚴(yán)重后果。這證實(shí)了我們的分析器在舊項(xiàng)目中仍然發(fā)現(xiàn)此類疏忽錯(cuò)誤的事實(shí)。

第5名。微觀優(yōu)化

第五名是另一個(gè) DBeaver 警告,我們專門寫了一篇文章。我們來(lái)看看:

@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // ....
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);              // <=
    Assert.assertEquals(newValue, oldValue);
  }
}

這里有一個(gè)解釋:

V6030 無(wú)論左側(cè)操作數(shù)的值如何,都會(huì)調(diào)用“&”運(yùn)算符右側(cè)的方法。也許,最好使用“&&”。 ExasolTableColumnManager.java 79、DB2TableColumnManager.java 77

開發(fā)人員將邏輯 && 與按位 & 混淆了。它們具有不同的行為:表達(dá)式中的條件在按位 AND 之后不會(huì)終止。 短路求值 不適用于按位 AND。因此,即使 exasolTableBase != null 將返回 false,執(zhí)行線程也會(huì)到達(dá) exasolTableBase.getClass() 并導(dǎo)致 NPE。

好吧,這只是一個(gè)錯(cuò)字,讓我們繼續(xù)吧,好嗎? DBeaver 有很多這樣的警告。很多。許多都是相對(duì)無(wú)害的,但對(duì)于好奇的讀者,我在下面留下了一些例子:

使用不會(huì)導(dǎo)致錯(cuò)誤的按位運(yùn)算
ExasolSecurityPolicy.java:
public Builder setPersonalisation(Date date, ....) {
  ....
  final OutputStreamWriter
    out = new OutputStreamWriter(bout, "UTF-8");
  final DateFormat
    format = new SimpleDateFormat("YYYYMMdd");
  out.write(format.format(date));
    ....
}

ExasolConnectionManager.java:

@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // ....
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);              // <=
    Assert.assertEquals(newValue, oldValue);
  }
}

ExasolDataSource.java:

@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // properties equality does not seem to work...
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);
    Assert.assertEquals(newValue, oldValue);
  }
}

深入挖掘后,我的團(tuán)隊(duì)假設(shè)開發(fā)人員可能一直在嘗試對(duì)性能進(jìn)行微觀優(yōu)化。想了解完整情況,你可以看看我們的文章——這里我總結(jié)一下。

關(guān)鍵點(diǎn)是按位運(yùn)算不依賴于分支預(yù)測(cè),與邏輯運(yùn)算相比,可能允許更快的執(zhí)行。

令我們驚訝的是,一個(gè)本土基準(zhǔn)測(cè)試支持了這一說(shuō)法:

Top most intriguing Java errors in 4

圖表說(shuō)明了每種操作類型所需的時(shí)間。如果我們相信它,按位運(yùn)算似乎比邏輯運(yùn)算快 40%。

我為什么要提出這個(gè)話題?強(qiáng)調(diào)潛在微觀優(yōu)化的成本。

首先,開發(fā)人員發(fā)明分支預(yù)測(cè)是有原因的——放棄它的成本太高。因此,基準(zhǔn)測(cè)試可能運(yùn)行得更快,因?yàn)橹稻哂姓龖B(tài)分布,而在實(shí)際情況下不太可能觀察到。

第二,放棄短路評(píng)估機(jī)制會(huì)導(dǎo)致成本大幅上升。如果我們看一下上面劇透中的第三個(gè)示例,我們可以看到最快的 contains 操作并不是一直執(zhí)行而不是立即停止。

第三,我們從本章開始就全權(quán)處理此類錯(cuò)誤。

總體而言,我發(fā)現(xiàn)微優(yōu)化價(jià)格的警示故事足以進(jìn)入我們的前五名。

第四名。不行的話測(cè)試也不會(huì)落下

自動(dòng)化測(cè)試通常被認(rèn)為是防止各種錯(cuò)誤的最終保障。然而,時(shí)不時(shí)地,我很想問(wèn):“誰(shuí)自己測(cè)試這些測(cè)試?”來(lái)自 GeoServer 檢查的另一個(gè)警告再次證明了這一點(diǎn)。這是一個(gè)代碼片段:

@Override
public BroadphasePair findPair(BroadphaseProxy proxy0, BroadphaseProxy proxy1) {
  BulletStats.gFindPairs++;
  if (proxy0.getUid() > proxy1.getUid()) {
    BroadphaseProxy tmp = proxy0;
    proxy0 = proxy1;
    proxy1 = proxy0;
  }
  ....
}

PVS-Studio 警告:

V6060 在驗(yàn)證“e”引用是否為 null 之前,已使用該引用。 ResourceAccessManagerWCSTest.java 186、ResourceAccessManagerWCSTest.java 193

乍一看,這個(gè)警告似乎不是分析器最令人興奮的警告,因?yàn)?V6060 經(jīng)常是針對(duì)冗余代碼發(fā)出的。然而,我承諾我會(huì)根據(jù)他們的吸引力來(lái)選擇提名。所以,這個(gè)案例遠(yuǎn)比看上去有趣。

最初,測(cè)試邏輯可能看起來(lái)是錯(cuò)誤的,因?yàn)?e 變量是從 catch 運(yùn)算符獲取的,并且進(jìn)一步保持不變,因此它永遠(yuǎn)不會(huì)為 null。我們可以進(jìn)行雜散編輯,并將 if(e == nul) 條件的 then 分支刪除為無(wú)法到達(dá)。然而,那是完全錯(cuò)誤的。你找到竅門了嗎?

關(guān)鍵在于包含異常對(duì)象的代碼中多了一個(gè)變量,它是一個(gè)se。它的值會(huì)在循環(huán)體內(nèi)發(fā)生變化。所以,我們很容易猜測(cè),條件中應(yīng)該有se變量,而不是e。

這個(gè)錯(cuò)誤會(huì)導(dǎo)致then分支永遠(yuǎn)不會(huì)被執(zhí)行,所以我們不知道有沒(méi)有異常。更糟糕的是,在代碼審查中很難注意到這樣的錯(cuò)誤,因?yàn)樽兞棵Q非常相似。

從這個(gè)故事中可以汲取兩個(gè)智慧:

  1. 清楚地命名變量,即使在測(cè)試中也是如此。不然更容易犯這樣的錯(cuò)誤;
  2. 測(cè)試不足以保證項(xiàng)目質(zhì)量,因?yàn)樗鼈円部赡馨e(cuò)誤。因此,它會(huì)在應(yīng)用程序內(nèi)留下漏洞。

由于提供了如此寶貴的課程,我將此警告授予第四名。

第三名。祝各位調(diào)試愉快

前三名獲勝者屬于 NetBeans 檢查的警告。之前的代碼片段比較緊湊,我們看一下比較長(zhǎng)的代碼片段:

public Builder setPersonalisation(Date date, ....) {
  ....
  final OutputStreamWriter
    out = new OutputStreamWriter(bout, "UTF-8");
  final DateFormat
    format = new SimpleDateFormat("YYYYMMdd");
  out.write(format.format(date));
    ....
}

最后一次,嘗試自己找到錯(cuò)誤——我會(huì)等...

Top most intriguing Java errors in 4

正在尋找?

不錯(cuò)!那些僅在表達(dá)式 iDesc.neighbor != null || 中發(fā)現(xiàn)錯(cuò)誤的人iDesc.index == iDesc.index,很遺憾,你輸了:)

當(dāng)然,有一個(gè)問(wèn)題,但對(duì)于排名第一的問(wèn)題來(lái)說(shuō)還不夠有趣。是的,這里有兩個(gè)錯(cuò)誤,我欺騙了你一點(diǎn)。但是沒(méi)有一點(diǎn)惡作劇的假期怎么算呢? :)

分析器檢測(cè)到此處的 i^i 表達(dá)式存在錯(cuò)誤,并發(fā)出以下警告:

V6001 在“^”運(yùn)算符的左側(cè)和右側(cè)有相同的子表達(dá)式“i”。 LayoutFeeder.java 3897

異或運(yùn)算沒(méi)有任何意義,因?yàn)閮蓚€(gè)相同值的異或?qū)⑹冀K為零。為了快速回顧一下,這里是 XOR 的真值表:

a b a^b
0 0 0
0 1 1
1 0 1
1 1 0

換句話說(shuō),只有當(dāng)操作數(shù)不同時(shí),運(yùn)算才為真。我們將擁有相同的所有位,因?yàn)橹凳窍嗤摹?/p>

為什么我這么喜歡這個(gè)bug?有 i^1 操作,看起來(lái)與 i^i 幾乎相同。因此,在代碼審查中很容易錯(cuò)過(guò)這個(gè)錯(cuò)誤,因?yàn)槲覀円呀?jīng)在上面看到了正確的 i^1。

我不了解你,但這讓我想起了著名的:

public Builder setPersonalisation(Date date, ....) {
  ....
  final OutputStreamWriter
    out = new OutputStreamWriter(bout, "UTF-8");
  final DateFormat
    format = new SimpleDateFormat("YYYYMMdd");
  out.write(format.format(date));
    ....
}

否則,很難解釋它是如何進(jìn)入代碼的——除非我們用一個(gè)簡(jiǎn)單的拼寫錯(cuò)誤來(lái)忽略這個(gè)無(wú)聊的版本。如果您確實(shí)發(fā)現(xiàn)了該錯(cuò)誤,請(qǐng)拍拍自己的背,或者在評(píng)論中分享您的偵探技巧:)

第二名。當(dāng)模式失敗時(shí)

我已經(jīng)顯示了第一篇和第三篇 DBeaver 文章中的錯(cuò)誤,跳過(guò)第二篇文章。我糾正了——以下內(nèi)容僅來(lái)自第二篇文章。

PVS-Studio 分析器不喜歡從 TextWithOpen 類的構(gòu)造函數(shù)調(diào)用 isBinaryContents,該類在子類中被重寫:

@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // ....
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);              // <=
    Assert.assertEquals(newValue, oldValue);
  }
}

那又怎樣?它被覆蓋了——不過(guò),沒(méi)什么大不了的。這看起來(lái)像是代碼味道,沒(méi)什么關(guān)鍵的。至少,我以前是這么認(rèn)為的。我專門寫了一篇文章來(lái)闡述我與這個(gè)錯(cuò)誤的斗爭(zhēng)。

TextWithOpen 有很多子類,其中之一就是 TextWithOpenFile。在那里,該方法實(shí)際上被重寫,它返回一個(gè)超類沒(méi)有的字段,而不是 false:

@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // properties equality does not seem to work...
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);
    Assert.assertEquals(newValue, oldValue);
  }
}

還有疑問(wèn)嗎?這個(gè)類的構(gòu)造函數(shù)是什么樣的?

@Override
public BroadphasePair findPair(BroadphaseProxy proxy0, BroadphaseProxy proxy1) {
  BulletStats.gFindPairs++;
  if (proxy0.getUid() > proxy1.getUid()) {
    BroadphaseProxy tmp = proxy0;
    proxy0 = proxy1;
    proxy1 = proxy0;
  }
  ....
}

注意到了嗎?調(diào)用超類構(gòu)造函數(shù)后,將初始化二進(jìn)制字段。然而,有一個(gè)對(duì) isBinaryContents 方法的調(diào)用,它引用了子類字段!

Top most intriguing Java errors in 4

這是 PVS-Studio 警告:

V6052 在“TextWithOpen”父類構(gòu)造函數(shù)中調(diào)用重寫的“isBinaryContents”方法可能會(huì)導(dǎo)致使用未初始化的數(shù)據(jù)。檢查字段:二進(jìn)制。 TextWithOpenFile.java(77), TextWithOpen.java 59

這是一張相當(dāng)有趣的圖片。乍一看,開發(fā)人員似乎遵循了最佳實(shí)踐:避免無(wú)法維護(hù)的意大利面條式代碼,并嘗試通過(guò)模板方法模式實(shí)現(xiàn)規(guī)范的 OOP。但是,即使在實(shí)現(xiàn)這樣一個(gè)簡(jiǎn)單的模式時(shí),我們也可能會(huì)犯錯(cuò)誤,這就是所發(fā)生的情況。在我看來(lái),這種(錯(cuò)誤的)簡(jiǎn)單之美是穩(wěn)居第二的。

第一名。一個(gè)錯(cuò)誤抵消了另一個(gè)錯(cuò)誤

高興吧!舞臺(tái)第一名!競(jìng)爭(zhēng)很激烈,但必須做出選擇。經(jīng)過(guò)深思熟慮,我決定接受 NetBeans 檢查中的警告。讓我介紹一下最終的代碼片段:

public Builder setPersonalisation(Date date, ....) {
  ....
  final OutputStreamWriter
    out = new OutputStreamWriter(bout, "UTF-8");
  final DateFormat
    format = new SimpleDateFormat("YYYYMMdd");
  out.write(format.format(date));
    ....
}

我確信不可能一眼就能發(fā)現(xiàn)這樣的錯(cuò)誤——當(dāng)然,除非你自己犯過(guò)這個(gè)錯(cuò)誤。我不會(huì)讓您久等的——這是 PVS-Studio 警告:

V6009 緩沖區(qū)容量使用字符值設(shè)置為“47”。最有可能的是,“/”符號(hào)應(yīng)該放置在緩沖區(qū)中。忽略UnignoreCommand.java 107

事實(shí)上,這個(gè)錯(cuò)誤非常簡(jiǎn)單:StringBuilder 構(gòu)造函數(shù)沒(méi)有接受 char 的重載。那么調(diào)用什么構(gòu)造函數(shù)呢?開發(fā)者顯然認(rèn)為會(huì)調(diào)用一個(gè)接受 String 的重載,然后 StringBuilder 的初始值就是這個(gè)斜??杠。

但是,會(huì)發(fā)生隱式類型轉(zhuǎn)換,并調(diào)用接受 int 的類型構(gòu)造函數(shù)。在我們的例子中,它代表 StringBuilder 的初始大小。將 char 作為參數(shù)傳遞不會(huì)在功能上影響任何內(nèi)容,因?yàn)樗粫?huì)包含在最終字符串中。如果超出初始大小,它只會(huì)自行增加,不會(huì)導(dǎo)致異?;蚱渌弊饔?。

但是等等,我提到了兩個(gè)錯(cuò)誤,不是嗎?第二個(gè)在哪里,它們是如何連接的?為了發(fā)現(xiàn)這一點(diǎn),我們必須讀入方法體并了解這段代碼的作用。

它生成文件或目錄的絕對(duì)路徑。根據(jù)代碼,生成的路徑應(yīng)如下所示:

  • 對(duì)于文件:/folder1/file
  • 對(duì)于目錄:/folder1/folder/.

代碼看起來(lái)非常正確。這就是問(wèn)題所在。代碼確實(shí)可以正常工作:)但是如果我們通過(guò)用字符串替換字符來(lái)修復(fù)錯(cuò)誤,我們將得到這個(gè)而不是正確的結(jié)果:

  • /文件夾1/文件/;
  • /文件夾1/文件夾//

換句話說(shuō),我們會(huì)在字符串末尾得到一個(gè)額外的斜杠。它將位于末尾,因?yàn)樯厦娴拇a每次都會(huì)將新文本添加到行的開頭。

因此,第二個(gè)錯(cuò)誤是這個(gè)斜杠根本作為參數(shù)傳遞給構(gòu)造函數(shù)。但是,我不會(huì)低估這樣的錯(cuò)誤,因?yàn)槿绻腥藳Q定在不檢查的情況下用字符串替換字符,可能會(huì)出現(xiàn)問(wèn)題。

這就是錯(cuò)誤頂部的第一個(gè)位置轉(zhuǎn)到正確工作的代碼的方式。新年奇跡,你期待什么? :)

結(jié)論

我希望您喜歡閱讀我的錯(cuò)誤故事。如果您有任何特別的故事讓您印象深刻,或者您有調(diào)整排名的建議,請(qǐng)隨時(shí)在評(píng)論中分享您的想法,我會(huì)在下次記住它們:)

如果您對(duì)其他語(yǔ)言感興趣,我邀請(qǐng)您在此處查看 2024 年最熱門的 C# 錯(cuò)誤 - 請(qǐng)繼續(xù)關(guān)注新的熱門錯(cuò)誤!

所有這些錯(cuò)誤都是用PVS-Studio分析器檢測(cè)到的,最新版本(7.34)剛剛發(fā)布!您可以通過(guò)此鏈接嘗試一下。

要繼續(xù)關(guān)注有關(guān)代碼質(zhì)量的新文章,我們邀請(qǐng)您訂閱:

  • PVS-Studio X(推特);
  • 我們的每月文章摘要;

新年快樂(lè)!

以上是4 中最有趣的 Java 錯(cuò)誤的詳細(xì)內(nèi)容。更多信息請(qǐng)關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

本站聲明
本文內(nèi)容由網(wǎng)友自發(fā)貢獻(xiàn),版權(quán)歸原作者所有,本站不承擔(dān)相應(yīng)法律責(zé)任。如您發(fā)現(xiàn)有涉嫌抄襲侵權(quán)的內(nèi)容,請(qǐng)聯(lián)系admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費(fèi)脫衣服圖片

Undresser.AI Undress

Undresser.AI Undress

人工智能驅(qū)動(dòng)的應(yīng)用程序,用于創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用于從照片中去除衣服的在線人工智能工具。

Clothoff.io

Clothoff.io

AI脫衣機(jī)

Video Face Swap

Video Face Swap

使用我們完全免費(fèi)的人工智能換臉工具輕松在任何視頻中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費(fèi)的代碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

功能強(qiáng)大的PHP集成開發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺化網(wǎng)頁(yè)開發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級(jí)代碼編輯軟件(SublimeText3)

hashmap和hashtable之間的區(qū)別? hashmap和hashtable之間的區(qū)別? Jun 24, 2025 pm 09:41 PM

HashMap與Hashtable的區(qū)別主要體現(xiàn)在線程安全、null值支持及性能方面。1.線程安全方面,Hashtable是線程安全的,其方法大多為同步方法,而HashMap不做同步處理,非線程安全;2.null值支持上,HashMap允許一個(gè)null鍵和多個(gè)null值,Hashtable則不允許null鍵或值,否則拋出NullPointerException;3.性能方面,HashMap因無(wú)同步機(jī)制效率更高,Hashtable因每次操作加鎖性能較低,推薦使用ConcurrentHashMap替

為什么我們需要包裝紙課? 為什么我們需要包裝紙課? Jun 28, 2025 am 01:01 AM

Java使用包裝類是因?yàn)榛緮?shù)據(jù)類型無(wú)法直接參與面向?qū)ο蟛僮?,而?shí)際需求中常需對(duì)象形式;1.集合類只能存儲(chǔ)對(duì)象,如List利用自動(dòng)裝箱存儲(chǔ)數(shù)值;2.泛型不支持基本類型,必須使用包裝類作為類型參數(shù);3.包裝類可表示null值,用于區(qū)分未設(shè)置或缺失的數(shù)據(jù);4.包裝類提供字符串轉(zhuǎn)換等實(shí)用方法,便于數(shù)據(jù)解析與處理,因此在需要這些特性的場(chǎng)景下,包裝類不可或缺。

什么是接口中的靜態(tài)方法? 什么是接口中的靜態(tài)方法? Jun 24, 2025 pm 10:57 PM

StaticmethodsininterfaceswereintroducedinJava8toallowutilityfunctionswithintheinterfaceitself.BeforeJava8,suchfunctionsrequiredseparatehelperclasses,leadingtodisorganizedcode.Now,staticmethodsprovidethreekeybenefits:1)theyenableutilitymethodsdirectly

JIT編譯器如何優(yōu)化代碼? JIT編譯器如何優(yōu)化代碼? Jun 24, 2025 pm 10:45 PM

JIT編譯器通過(guò)方法內(nèi)聯(lián)、熱點(diǎn)檢測(cè)與編譯、類型推測(cè)與去虛擬化、冗余操作消除四種方式優(yōu)化代碼。1.方法內(nèi)聯(lián)減少調(diào)用開銷,將頻繁調(diào)用的小方法直接插入調(diào)用處;2.熱點(diǎn)檢測(cè)識(shí)別高頻執(zhí)行代碼并集中優(yōu)化,節(jié)省資源;3.類型推測(cè)收集運(yùn)行時(shí)類型信息實(shí)現(xiàn)去虛擬化調(diào)用,提升效率;4.冗余操作消除根據(jù)運(yùn)行數(shù)據(jù)刪除無(wú)用計(jì)算和檢查,增強(qiáng)性能。

什么是實(shí)例初始器塊? 什么是實(shí)例初始器塊? Jun 25, 2025 pm 12:21 PM

實(shí)例初始化塊在Java中用于在創(chuàng)建對(duì)象時(shí)運(yùn)行初始化邏輯,其執(zhí)行先于構(gòu)造函數(shù)。它適用于多個(gè)構(gòu)造函數(shù)共享初始化代碼、復(fù)雜字段初始化或匿名類初始化場(chǎng)景,與靜態(tài)初始化塊不同的是它每次實(shí)例化時(shí)都會(huì)執(zhí)行,而靜態(tài)初始化塊僅在類加載時(shí)運(yùn)行一次。

什么是工廠模式? 什么是工廠模式? Jun 24, 2025 pm 11:29 PM

工廠模式用于封裝對(duì)象創(chuàng)建邏輯,使代碼更靈活、易維護(hù)、松耦合。其核心答案是:通過(guò)集中管理對(duì)象創(chuàng)建邏輯,隱藏實(shí)現(xiàn)細(xì)節(jié),支持多種相關(guān)對(duì)象的創(chuàng)建。具體描述如下:工廠模式將對(duì)象創(chuàng)建交給專門的工廠類或方法處理,避免直接使用newClass();適用于多類型相關(guān)對(duì)象創(chuàng)建、創(chuàng)建邏輯可能變化、需隱藏實(shí)現(xiàn)細(xì)節(jié)的場(chǎng)景;例如支付處理器中通過(guò)工廠統(tǒng)一創(chuàng)建Stripe、PayPal等實(shí)例;其實(shí)現(xiàn)包括工廠類根據(jù)輸入?yún)?shù)決定返回的對(duì)象,所有對(duì)象實(shí)現(xiàn)共同接口;常見變體有簡(jiǎn)單工廠、工廠方法和抽象工廠,分別適用于不同復(fù)雜度的需求。

變量的最終關(guān)鍵字是什么? 變量的最終關(guān)鍵字是什么? Jun 24, 2025 pm 07:29 PM

InJava,thefinalkeywordpreventsavariable’svaluefrombeingchangedafterassignment,butitsbehaviordiffersforprimitivesandobjectreferences.Forprimitivevariables,finalmakesthevalueconstant,asinfinalintMAX_SPEED=100;wherereassignmentcausesanerror.Forobjectref

什么是類型鑄造? 什么是類型鑄造? Jun 24, 2025 pm 11:09 PM

類型轉(zhuǎn)換有兩種:隱式和顯式。1.隱式轉(zhuǎn)換自動(dòng)發(fā)生,如將int轉(zhuǎn)為double;2.顯式轉(zhuǎn)換需手動(dòng)操作,如使用(int)myDouble。需要類型轉(zhuǎn)換的情況包括處理用戶輸入、數(shù)學(xué)運(yùn)算或函數(shù)間傳遞不同類型的值時(shí)。需要注意的問(wèn)題有:浮點(diǎn)數(shù)轉(zhuǎn)整數(shù)會(huì)截?cái)嘈?shù)部分、大類型轉(zhuǎn)小類型可能導(dǎo)致數(shù)據(jù)丟失、某些語(yǔ)言不允許直接轉(zhuǎn)換特定類型。正確理解語(yǔ)言的轉(zhuǎn)換規(guī)則有助于避免錯(cuò)誤。

See all articles