首頁 Java java教程 4 中最有趣的 Java 錯誤

4 中最有趣的 Java 錯誤

Jan 01, 2025 am 09:19 AM

2024 年,我們分析了大量項目,並在部落格上分享我們的發現。現在是除夕夜-是時候講喜慶故事了!我們收集了在開源專案中檢測到的最有趣的 Java 錯誤,現在將它們帶給您!

Top most intriguing Java errors in 4

前言

我們長期以來一直秉承發布 PVS-Studio 檢測到的最有趣的 bug 的傳統,但自 2020 年以來就沒有出現與 Java 相關的置頂!這次,我嘗試復興復古風格。我希望您手邊有一條舒適的毯子和一杯熱茶,因為我專門為您挑選了 10 多種有趣的昆蟲。以下是他們的排名:

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

準備好用自己的程式設計智慧迎接 10 個有趣的故事吧——除夕夜很快就要到來了:)

第10名。回到未來

第十位,第一個程式碼片段張開雙臂歡迎我們。

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

我忍不住把它放在除夕夜的頂部,因為這段程式碼中的一個錯誤可以讓我們更快地到達下一年:)猜猜這個錯誤是從哪裡來的?

讓我們來看看傳遞給 SimpleDateFormat 建構子的參數。看起來有效嗎?如果我們幾乎傳遞任何日期,例如撰寫本文的日期 (10/12/2024),程式碼將傳回正確的值 20241210。

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

發生這種情況是因為 SimpleDateFormat 參數中的 Y 字元代表基於週數的年份。簡而言之,當一周至少包含新年的四天時,該一周被視為第一週。所以,如果我們的一周從週日開始,我們就可以提前三天進入新的一年。

要修復此問題,只需將大寫 Y 替換為小寫 y 即可。想了解更多嗎?我們特別寫了一整篇文章來討論這個主題。

這是針對此錯誤的 PVS-Studio 警告:

V6122 偵測到使用「Y」(週年)模式:它可能打算使用「y」(年)。 SkeinParameters.java 246

由於週數的具體情況,因此測試並不是發現此錯誤的最佳方法。那麼為什麼這樣一個話題性的錯誤會出現在最後呢?原因是該警告不是來自 Bouncy Castle 的實際版本,而是來自我們的測試基地。舊的資源仍然存在,而且這個錯誤已經修復了很長時間。這是來自過去的致敬,又是一次時光旅行:)

第9名。 “看來不行”

第九位,我們收到 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」透過呼叫相同函數進行初始化。這可能是錯誤或未最佳化的程式碼。 DataAccessRuleDAOTest.java 110、DataAccessRuleDAOTest.java 111

這樣的錯誤有什麼有趣的?讓我來揭示這四個點背後隱藏的是什麼:

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

有評論稱該程式碼由於某種原因無法運行。說實話,我第一次看到的時候就笑了。

不過,這篇評論相當含糊。測試很可能是故意以這種方式編寫的,以防止在比較失敗時出現故障。然而,該程式碼已經處於這種狀態十多年了,這引發了一些問題。這種模糊性就是我沒有將其排名更高的原因。

第8名。腳中彈

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

@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 警告來發現錯誤所在。無論如何,以防萬一,這裡是:

V6026 該值已指派給「proxy1」變數。 HashedOverlappingPairCache.java 233

是的,這是一個令人尷尬的簡單錯誤。不過,這種簡單性讓它變得更加搞笑。儘管如此,它還是有自己的故事。

JBullet 庫是 C/C 子彈庫的移植,那裡有類似的功能:

@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 來看,原來寫的是正確的。原來是程式碼從一種語言移植到另一種語言時出現了錯誤。

由於其驚人的樸實加上豐富的歷史,我將這個警告評為第八名。我希望你喜歡這個搬起石頭砸自己腳的 bug 原來是與 C 語言相關的。

第七名。即使是偉大的數學家也會犯錯

誠然,下一個警告出於多種原因溫暖了我的心。以下是 GeoGebra 檢查的程式碼片段:

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

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

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

V6107 正在使用常數 0.7071067811865。結果值可能不準確。考慮使用 Math.sqrt(0.5)。 DrawAngle.java 303

事實上,0.7071067811865 並不是什麼神奇的數字——它只是 0.5 平方根的四捨五入結果。但這種精度損失有多嚴重呢? GeoGebra 是一款為數學家量身訂製的軟體,額外的精確度似乎並沒有什麼壞處。

為什麼我這麼喜歡這個bug?

首先,在會議上,我經常要求與會者在其他程式碼片段中找到類似的錯誤。當錯誤隱藏在常數中時,看著他們仔細分析程式碼總是很有趣。

其次,這是我為 Java 分析器實作的第一個診斷規則。這就是為什麼我無法抗拒將它放在頂部的原因——即使意識到了偏見——我把它放在第七位:)

第六名。這個模式不起作用

以下警告是我從基於 DBeaver 檢查的第一篇文章中獲取的,可能不會立即引起我們的注意。這是一個程式碼片段:

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 分析器偵測到的內容:

V6082 不安全的雙重檢查鎖定。該欄位應聲明為易失性的。 TaskImpl.java 59、TaskImpl.java317

雖然這個特定的警告沒有什麼特別的,但我仍然覺得它非常有趣。關鍵是所應用的雙重檢查鎖定模式不起作用。有什麼訣竅呢?這在 20 年前是相關的:)

如果您想了解有關該主題的更多信息,我建議您閱讀全文。但現在,讓我給您一個快速總結。

雙重檢查鎖定模式用於在多執行緒環境中實現延遲初始化。在「重量級」檢查之前,會在沒有同步區塊的情況下執行「輕量級」檢查。只有當兩項檢查都通過時才會建立資源。

但是,在這種方法中,物件建立是非原子,並且處理器和編譯器可以更改操作順序。因此,另一個執行緒可能會意外收到部分建立的物件並開始使用它,這可能會導致不正確的行為。這個錯誤可能很少發生,因此調試對開發人員來說將是一個巨大的挑戰。

這裡有一個變化:這種模式直到 JDK 5 才起作用。從 JDK 5 開始,由於 happens-before 原則,引入了 volatile 關鍵字來解決重新排序操作的潛在問題。分析器警告我們應該要添加此關鍵字。

但是,無論如何,最好避免這種模式。從那時起,硬體和 JVM 效能已經取得了長足的進步,並且同步操作不再那麼慢了。然而,不正確地實現 DCL 模式仍然是一個常見的陷阱,可能會產生上述的嚴重後果。這證實了我們的分析器在舊專案中仍然發現此類疏忽錯誤的事實。

第5名。微觀優化

第五名是另一個 DBeaver 警告,我們專門寫了一篇文章。我們來看看:

@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);
  }
}
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

這裡有個解釋:

V6030 無論左側運算元的值為何,都會呼叫「&」運算子右邊的方法。也許,最好使用“&&”。 ExasolTableColumnManager.java 79、DB2TableColumnManager.java 77

開發人員將邏輯 && 與位元 & 混淆了。它們具有不同的行為:表達式中的條件在位元 AND 之後不會終止。 短路求值 不適用於位元 AND。因此,即使 exasolTableBase != null 將傳回 false,執行緒也會到達 exasolTableBase.getClass() 並導致 NPE。

好吧,這只是一個錯字,讓我們繼續吧,好嗎? DBeaver 有很多這樣的警告。很多。許多都是相對無害的,但對於好奇的讀者,我在下面留下了一些例子:

使用不會導致錯誤的位元運算
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);
  }
}
登入後複製
登入後複製
登入後複製

深入挖掘後,我的團隊假設開發人員可能一直在嘗試對效能進行微觀最佳化。想了解完整情況,你可以看看我們的文章——這裡我總結一下。

關鍵點是位元運算不依賴分支預測,與邏輯運算相比,可能允許更快的執行。

令我們驚訝的是,一個本土基準測試支持了這個說法:

Top most intriguing Java errors in 4

圖表說明了每種操作類型所需的時間。如果我們相信它,位元運算似乎比邏輯運算快 40%。

我為什麼要提出這個主題?強調潛在微觀優化的成本。

首先,開發人員發明分支預測是有原因的-放棄它的成本太高。因此,基準測試可能運行得更快,因為值具有常態分佈,而在實際情況下不太可能觀察到。

第二,放棄短路評估機制會導致成本大幅上升。如果我們看一下上面劇透中的第三個範例,我們可以看到最快的 contains 操作並不是一直執行而不是立即停止。

第三,我們從本章開始就全權處理這類錯誤。

總體而言,我發現微優化價格的警示故事足以進入我們的前五名。

第四名。不行的話測試也不會落下

自動化測試通常被認為是防止各種錯誤的最終保障。然而,時不時地,我很想問:「誰自己測試這些測試?」來自 GeoServer 檢查的另一個警告再次證明了這一點。這是一個程式碼片段:

@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 在驗證「e」引用是否為 null 之前,已使用該引用。 ResourceAccessManagerWCSTest.java 186、ResourceAccessManagerWCSTest.java 193

乍一看,這個警告似乎不是分析器最令人興奮的警告,因為 V6060 經常是針對冗餘程式碼發出的。然而,我承諾我會根據他們的吸引力來選擇提名。所以,這個案例遠比看起來有趣。

最初,測試邏輯可能看起來是錯誤的,因為 e 變數是從 catch 運算子取得的,並且進一步保持不變,因此它永遠不會為 null。我們可以進行雜散編輯,並將 if(e == nul) 條件的 then 分支刪除為無法到達。然而,那是完全錯誤的。你找到竅門了嗎?

關鍵在於包含異常物件的程式碼多了一個變量,它就是一個se。它的值會在循環體內改變。所以,我們很容易猜測,條件中應該要有se變量,而不是e。

這個錯誤會導致then分支永遠不會被執行,所以我們不知道有沒有異常。更糟的是,在程式碼審查中很難注意到這樣的錯誤,因為變數名稱非常相似。

從這個故事可以汲取兩個智慧:

  1. 清楚地命名變量,即使在測試中也是如此。不然更容易犯這樣的錯誤;
  2. 測試不足以保證專案質量,因為它們也可能包含錯誤。因此,它會在應用程式內留下漏洞。

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

第三名。祝各位調試愉快

前三名獲勝者屬於 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));
    ....
}
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

最後一次,試著自己找錯誤-我會等...

Top most intriguing Java errors in 4

正在尋找?

不錯!那些只在表達式 iDesc.neighbor != null || 中發現錯誤的人iDesc.index == iDesc.index,很遺憾,你輸了:)

當然,有一個問題,但對於排名第一的問題來說還不夠有趣。是的,這裡有兩個錯誤,我欺騙了你一點。但是沒有一點惡作劇的假期怎麼算呢? :)

分析器偵測到此處的 i^i 表達式有錯誤,並發出以下警告:

V6001 在「^」運算子的左邊和右邊有相同的子運算式「i」。 LayoutFeeder.java 3897

異或運算沒有任何意義,因為兩個相同值的異或將永遠為零。為了快速回顧一下,這裡是 XOR 的真值表:

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

換句話說,只有當運算元不同時,運算才會為真。我們將擁有相同的所有位,因為值是相同的。

為什麼我這麼喜歡這個bug?有 i^1 操作,看起來與 i^i 幾乎相同。因此,在程式碼審查中很容易錯過這個錯誤,因為我們已經在上面看到了正確的 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));
    ....
}
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

否則,很難解釋它是如何進入程式碼的——除非我們用一個簡單的拼字錯誤來忽略這個無聊的版本。如果您確實發現了該錯誤,請拍拍自己的背,或在評論中分享您的偵探技巧:)

第二名。當模式失敗時

我已經顯示了第一篇和第三篇 DBeaver 文章中的錯誤,跳過第二篇文章。我糾正了——以下內容僅來自第二篇文章。

PVS-Studio 分析器不喜歡從 TextWithOpen 類別的建構函式呼叫 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);
  }
}
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

那又怎樣?它被覆蓋了——不過,沒什麼大不了的。這看起來像是代碼味道,沒什麼關鍵的。至少,我以前是這麼認為的。我專門寫了一篇文章來闡述我與這個錯誤的鬥爭。

TextWithOpen 有許多子類,其中之一就是 TextWithOpenFile。在那裡,該方法實際上被重寫,它會傳回一個超類別沒有的字段,而不是 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);
  }
}
登入後複製
登入後複製
登入後複製

還有疑問嗎?這個類別的建構子是什麼樣的?

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

注意到了嗎?呼叫超類別建構函式後,將初始化二進位欄位。然而,有一個對 isBinaryContents 方法的調用,它引用了子類別欄位!

Top most intriguing Java errors in 4

這是 PVS-Studio 警告:

V6052 在「TextWithOpen」父類別建構子中呼叫重寫的「isBinaryContents」方法可能會導致使用未初始化的資料。檢查字段:二進制。 TextWithOpenFile.java(77), TextWithOpen.java 59

這是一張相當有趣的圖片。乍一看,開發人員似乎遵循了最佳實踐:避免無法維護的義大利麵條式程式碼,並嘗試透過模板方法模式實現規範的 OOP。但是,即使在實現這樣簡單的模式時,我們也可能會犯錯誤,而這就是所發生的情況。在我看來,這種(錯誤的)簡單之美是穩居第二的。

第一名。一個錯誤抵消了另一個錯誤

高興吧!舞台第一名!競爭很激烈,但必須做出選擇。經過深思熟慮,我決定接受 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));
    ....
}
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

我確信不可能一眼就能發現這樣的錯誤──當然,除非你自己犯過這個錯誤。我不會讓您久等的——這是 PVS-Studio 警告:

V6009 緩衝區容量使用字元值設定為「47」。最有可能的是,“/”符號應該放置在緩衝區中。忽略UnignoreCommand.java 107

事實上,這個錯誤非常簡單:StringBuilder 建構子沒有接受 char 的重載。那麼呼叫什麼構造函數呢?開發者顯然認為會呼叫一個接受 String 的重載,然後 StringBuilder 的初始值就是這個斜線。

但是,會發生隱式型別轉換,並呼叫接受 int 的型別建構函式。在我們的例子中,它代表 StringBuilder 的初始大小。將 char 作為參數傳遞不會在功能上影響任何內容,因為它不會包含在最終字串中。如果超出初始大小,它只會自行增加,不會導致異常或其他副作用。

但是等等,我提到了兩個錯誤,不是嗎?第二個在哪裡,它們是如何連結的?為了發現這一點,我們必須讀入方法體並了解這段程式碼的作用。

它產生檔案或目錄的絕對路徑。根據程式碼,產生的路徑應如下所示:

  • 對於檔案:/folder1/file
  • 對於目錄:/folder1/folder/.

程式碼看起來非常正確。這就是問題所在。程式碼確實可以正常工作:)但是如果我們透過用字串替換字元來修復錯誤,我們將得到這個而不是正確的結果:

  • /資料夾1/檔案/;
  • /資料夾1/資料夾//

換句話說,我們會在字串末尾得到一個額外的斜杠。它將位於末尾,因為上面的程式碼每次都會將新文字添加到行的開頭。

因此,第二個錯誤是這個斜槓根本作為參數傳遞給建構子。但是,我不會低估這樣的錯誤,因為如果有人決定在不檢查的情況下用字串替換字符,可能會出現問題。

這就是錯誤頂部的第一個位置轉到正確工作的程式碼的方式。新年奇蹟,你期待什麼? :)

結論

我希望您喜歡閱讀我的錯誤故事。如果您有任何特別的故事讓您印象深刻,或者您有調整排名的建議,請隨時在評論中分享您的想法,我會在下次記住它們:)

如果您對其他語言感興趣,我邀請您在此處查看 2024 年最熱門的 C# 錯誤 - 請繼續關注新的熱門錯誤!

所有這些錯誤都是用PVS-Studio分析器偵測到的,最新版本(7.34)剛剛發布!您可以透過此連結嘗試。

要繼續關注程式碼品質的新文章,我們邀請您訂閱:

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

新年快樂!

以上是4 中最有趣的 Java 錯誤的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1655
14
CakePHP 教程
1414
52
Laravel 教程
1307
25
PHP教程
1253
29
C# 教程
1227
24
公司安全軟件導致應用無法運行?如何排查和解決? 公司安全軟件導致應用無法運行?如何排查和解決? Apr 19, 2025 pm 04:51 PM

公司安全軟件導致部分應用無法正常運行的排查與解決方法許多公司為了保障內部網絡安全,會部署安全軟件。 ...

如何將姓名轉換為數字以實現排序並保持群組中的一致性? 如何將姓名轉換為數字以實現排序並保持群組中的一致性? Apr 19, 2025 pm 11:30 PM

將姓名轉換為數字以實現排序的解決方案在許多應用場景中,用戶可能需要在群組中進行排序,尤其是在一個用...

IntelliJ IDEA是如何在不輸出日誌的情況下識別Spring Boot項目的端口號的? IntelliJ IDEA是如何在不輸出日誌的情況下識別Spring Boot項目的端口號的? Apr 19, 2025 pm 11:45 PM

在使用IntelliJIDEAUltimate版本啟動Spring...

如何優雅地獲取實體類變量名構建數據庫查詢條件? 如何優雅地獲取實體類變量名構建數據庫查詢條件? Apr 19, 2025 pm 11:42 PM

在使用MyBatis-Plus或其他ORM框架進行數據庫操作時,經常需要根據實體類的屬性名構造查詢條件。如果每次都手動...

如何使用MapStruct簡化系統對接中的字段映射問題? 如何使用MapStruct簡化系統對接中的字段映射問題? Apr 19, 2025 pm 06:21 PM

系統對接中的字段映射處理在進行系統對接時,常常會遇到一個棘手的問題:如何將A系統的接口字段有效地映�...

Java對像如何安全地轉換為數組? Java對像如何安全地轉換為數組? Apr 19, 2025 pm 11:33 PM

Java對象與數組的轉換:深入探討強制類型轉換的風險與正確方法很多Java初學者會遇到將一個對象轉換成數組的�...

電商平台SKU和SPU數據庫設計:如何兼顧用戶自定義屬性和無屬性商品? 電商平台SKU和SPU數據庫設計:如何兼顧用戶自定義屬性和無屬性商品? Apr 19, 2025 pm 11:27 PM

電商平台SKU和SPU表設計詳解本文將探討電商平台中SKU和SPU的數據庫設計問題,特別是如何處理用戶自定義銷售屬...

如何利用Redis緩存方案高效實現產品排行榜列表的需求? 如何利用Redis緩存方案高效實現產品排行榜列表的需求? Apr 19, 2025 pm 11:36 PM

Redis緩存方案如何實現產品排行榜列表的需求?在開發過程中,我們常常需要處理排行榜的需求,例如展示一個�...

See all articles