自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

七大陷阱!99%的Java開發(fā)者都會(huì)遇到

開發(fā) 前端
如果目的是將所有出現(xiàn)的 A 都替換為 X,那么使用 replaceAll 方法似乎很直觀。方法名本身就清楚地表明了它的用途。于是問題來了:replace 方法會(huì)替換所有匹配的字符嗎?

環(huán)境:SpringBoot3.2.5

1. replace是否會(huì)替換所有字符?

在處理字符串時(shí),我們經(jīng)常需要替換字符串中的字符,比如在字符串 "ACDAB$%^&A*Y" 中將 A 替換為 X。首先想到的方法可能就是使用 replace 方法。

如果目的是將所有出現(xiàn)的 A 都替換為 X,那么使用 replaceAll 方法似乎很直觀。方法名本身就清楚地表明了它的用途。

于是問題來了:replace 方法會(huì)替換所有匹配的字符嗎?

JDK文檔說明:

圖片圖片

翻譯:該方法將此字符串中每個(gè)與目標(biāo)字面量序列相匹配的子字符串替換為指定的替換字面量序列。替換操作從字符串的開頭到結(jié)尾依次進(jìn)行,例如,在字符串 "aaa" 中將 "aa" 替換為 "b" 將得到 "ba" 而不是 "ab"。

那么 replace 與 replaceAll 的區(qū)別?

replace 方法有2個(gè)重載的方法:

String str = "ACDAB$%^&A*Y" ;
System.err.println(str.replace('A', 'X')) ;
System.err.println(str.replace("A", "X")) ;

replaceAll 方法簽名:

public String replaceAll(String regex, String replacement)

可以通過正則表達(dá)式的方式進(jìn)行替換。如下示例:

String str = "ACDAB$%^&A*Y" ;
// 簡單字符串替換
System.err.println(str.replaceAll("A", "X")) ;
// 正則替換,替換 '*' 字符,需要轉(zhuǎn)義
System.err.println(str.replaceAll("\\*", "XO")) ;

如果僅僅是將 '*' 進(jìn)行替換,那么使用replace更簡單

System.err.println(str.replace("*", "XO")) ;

以上都是替換整個(gè)字符串中匹配的,如果你只希望替換第一個(gè)出現(xiàn)的,那么可以使用如下方法:

System.err.println(str.replaceFirst("A", "-")) ;

第一個(gè)參數(shù)接受的是正則表達(dá)式。

2. Integer類型不要用 "==" 判斷

這不是絕對的,需要看情況,你比較的數(shù)值大小了。如下示例:

Integer a = 1 ;
Integer b = 1 ;
System.err.printf("a == b ? %s%n", a == b) ;


a = 128 ;
b = 128 ;
System.err.printf("a == b ? %s%n", a == b) ;


a = -128 ;
b = -128 ;
System.err.printf("a == b ? %s%n", a == b) ;


a = -129 ;
b = -129 ;
System.err.printf("a == b ? %s%n", a == b) ;

輸出結(jié)果:

a == b ? true
a == b ? false
a == b ? true
a == b ? false

為什么這樣?通過javap反編譯后

圖片圖片

當(dāng)我們將值賦給Integer類型變量時(shí)調(diào)用的是Integer#valueOf靜態(tài)方法,該方法簽名如下:

public static Integer valueOf(int i) {
  if (i >= IntegerCache.low && i <= IntegerCache.high)
    return IntegerCache.cache[i + (-IntegerCache.low)] ;
  return new Integer(i);
}

這里的low與high取值如下:

圖片圖片

默認(rèn)[low, high] = [-128, 127],也就是說默認(rèn)Integer緩存了這個(gè)范圍的數(shù)字,只要取值在這個(gè)范圍,那么都將返回緩存的數(shù)據(jù)。這也就是上面輸出結(jié)果的原因了。

通過上面的源碼我們也看到了,我們是可以通過jvm參數(shù)來改變這默認(rèn)緩存大小的,運(yùn)行程序時(shí)添加如下的jvm參數(shù):

-Djava.lang.Integer.IntegerCache.high=128

這樣設(shè)置后,我們在運(yùn)行上面程序,128的比較將打印true 。

3. 使用BigDecimal能否避免精度損失?

通常,對于涉及小數(shù)(例如金額)的字段,我們會(huì)將它們定義為BigDecimal而不是Double,以避免精度損失??紤]以下使用Double的場景:

double a = 0.02;
double b = 0.03;
System.out.println(a - b);

最終結(jié)果我們期望的是0.01,但實(shí)際是:

0.009999999999999998

這是因?yàn)閮蓚€(gè)double值的減法運(yùn)算會(huì)被轉(zhuǎn)換為二進(jìn)制形式,而double的有效數(shù)字精度限制為16位,這可能導(dǎo)致小數(shù)位的存儲(chǔ)不足,從而產(chǎn)生誤差。

那么使用BigDecimal是否能解決呢?

BigDecimal a1 = new BigDecimal(0.02) ;
BigDecimal b1 = new BigDecimal(0.03) ;
System.err.println(b1.subtract(a1)) ;

執(zhí)行結(jié)果

0.0099999999999999984734433411404097569175064563751220703125

為什么?我們先看看BigDecimal的構(gòu)造函數(shù)說明:

圖片圖片

我們看上面的第一點(diǎn)即可:

翻譯:這個(gè)構(gòu)造函數(shù)的結(jié)果可能會(huì)有些不可預(yù)測。人們可能會(huì)認(rèn)為,在Java中寫new BigDecimal(0.1)會(huì)創(chuàng)建一個(gè)完全等于0.1的BigDecimal(未縮放值為1,精度為1),但實(shí)際上它等于0.1000000000000000055511151231257827021181583404541015625。這是因?yàn)?.1在雙精度浮點(diǎn)數(shù)(或者任何有限長度的二進(jìn)制小數(shù))中無法被精確表示。因此,盡管表面上看起來如此,但傳遞給構(gòu)造函數(shù)的值并不完全等于0.1。

這也說明了,我們直接通過構(gòu)造函數(shù)傳入的double類型進(jìn)行計(jì)算是有風(fēng)險(xiǎn)的。

接著我們看第二點(diǎn):

翻譯:String 構(gòu)造函數(shù)是完全可預(yù)測的:寫 new BigDecimal("0.1") 會(huì)創(chuàng)建一個(gè)完全等于 0.1 的 BigDecimal,正如人們所期望的那樣。因此,通常建議優(yōu)先使用 String 構(gòu)造函數(shù)而不是這個(gè)(指直接使用 double 值的)構(gòu)造函數(shù)。

我們將上面的代碼改為如下:

BigDecimal aa = new BigDecimal("0.02") ;
BigDecimal bb = new BigDecimal("0.03") ;
System.err.println(bb.subtract(aa)) ;
// 0.01

輸出正確

我們還可以通過如下的方式:

aa = BigDecimal.valueOf(0.02) ;
bb = BigDecimal.valueOf(0.03) ;
System.err.println(bb.subtract(aa)) ;
// 0.01

此種方式是不是更加方便。其BigDecimal#valueOf內(nèi)如如下:

圖片圖片

關(guān)于BigDecimal更多內(nèi)容請查看下面文章:

不想被坑?快來了解BigDecimal的陷阱。

4. 是否真的不能使用 "+" 拼接字符串?

字符串值被視為不可變的序列。這意味著一旦定義了字符串對象,其數(shù)據(jù)就不能被修改。如果需要進(jìn)行修改,則會(huì)創(chuàng)建一個(gè)新的對象。如下示例:

String a = "123" ;
String b = "456" ;
String c = a + b ;
System.out.println(c) ;

在涉及大量字符串拼接的場景中,使用String對象會(huì)創(chuàng)建許多不必要的中間對象。這不僅浪費(fèi)內(nèi)存空間,還會(huì)降低效率。

在這種情況下,我們可以使用更高效的可變字符序列,如StringBuilder或StringBuffer來定義對象。

那么,StringBuilder和StringBuffer有什么區(qū)別呢?

主要區(qū)別在于,StringBuffer在其主要方法上添加了synchronized關(guān)鍵字,而StringBuilder則沒有。因此:

  • StringBuffer是線程安全的。
  • StringBuilder不是線程安全的。

在大多數(shù)情況下,建議使用StringBuilder進(jìn)行字符串拼接,觸發(fā)你需要在多線程環(huán)境下進(jìn)行字符串的操作。

StringBuilder中的append方法可以在不創(chuàng)建中間對象的情況下拼接字符串,因此它更高效,而且它不是同步的。

String a = "123";
String b = "456";
StringBuilder c = new StringBuilder();
c.append(a).append(b);
System.out.println(c);

那么使用String進(jìn)行字符串拼接是否總是比使用StringBuilder效率低?

首先,我們通過javap反編譯上面使用StringBuilder的代碼:

圖片圖片

通過反編譯,定義了2個(gè)String變量,創(chuàng)建一個(gè)StringBuilder對象,最后使用了2次append方法。

最后,我們再反編譯使用 "+" 操作符的方式:

圖片圖片

對比下,基本一樣啊。

注意:從JDK 5開始,Java對String類型的字符串的+操作進(jìn)行了優(yōu)化。這個(gè)操作在編譯成字節(jié)碼文件時(shí),+操作會(huì)被轉(zhuǎn)換成StringBuilder的append方法調(diào)用,以提高效率。

5. isEmpty & isBlank區(qū)別

當(dāng)我們執(zhí)行字符串操作時(shí),經(jīng)常需要檢查字符串是否為空。如果我們不使用任何工具,通常會(huì)像這樣進(jìn)行檢查:

public static void check(String source) {
  if (null != source && !"".equals(source)) {
    System.out.println("not empty");
  }
}

如果我們每次都需要進(jìn)行這樣的檢查,那可能會(huì)非常繁瑣。推薦使用Apache Commons Lang 3中的StringUtils類,它包含了許多有用的空值檢查方法:isEmpty、isBlank、isNotEmpty、isNotBlank,以及其他字符串處理方法。

接下來, 我們來看看isEmpty與isBlank的區(qū)別。

StringUtils.isEmpty(null) ;
StringUtils.isEmpty("") ;
StringUtils.isEmpty(" ") ;
StringUtils.isEmpty("bob") ;
StringUtils.isEmpty("  bob  ") ;

使用isBlank

StringUtils.isBlank(null)      = true
StringUtils.isBlank("")        = true
StringUtils.isBlank(" ")       = true
StringUtils.isBlank("bob")     = false
StringUtils.isBlank("  bob  ") = false

這兩種方法的關(guān)鍵區(qū)別在于,對于空字符串 " " 的情況,isEmpty 返回 false,而 isBlank 返回 true。

6. Mapper返回的集合List是否進(jìn)行Null檢查?

如下代碼,是否需要進(jìn)行null檢查?

List<User> list = userMapper.query(search);
if (CollectionUtils.isNotEmpty(list)) {
  List<Long> idList = list.stream().map(User::getId).collect(Collectors.toList());
}

注:CollectionUtils使用的是commons-collections4包。內(nèi)部如下調(diào)用

public static boolean isEmpty(final Collection<?> coll) {
  return coll == null || coll.isEmpty();
}

現(xiàn)在我們要確定的是如果基于MyBatis查詢返回的集合是否需要進(jìn)行null檢查呢?

查看MyBatis源碼,DefaultResultSetHandler#handleResultSets方法。

圖片圖片

collapseSingleResultList方法

private List<Object> collapseSingleResultList(List<Object> multipleResults) {
  return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;
}

通過查看源碼得知,我們沒有必要進(jìn)行null的檢查,得到結(jié)果后可以直接進(jìn)行使用。

7. 正確使用indexOf方法

首先,我們先來看看下面的代碼:

String source = "#ABBXXXOOO*pack";
if (source.indexOf("#") > 0) {
  System.out.println("success") ;
  // TODO
}

此代碼并不會(huì)輸出任何東西。indexOf如果不存在,那么返回 -1。該方法的說明:

圖片圖片

指定子字符串第一次出現(xiàn)的索引,如果沒有這樣的出現(xiàn),則返回-1。

indexOf方法返回指定元素在字符串中的位置,從0開始計(jì)數(shù)。在上面的例子中,#位于字符串的第一個(gè)位置,所以indexOf方法返回的值實(shí)際上是0。

所以,這里我們應(yīng)該這樣判斷

if (source.indexOf("#") > -1) {
  // ...
}

但是,我覺得下面的方法更好:

if (source.contains("#")) {
  System.err.println("contains success") ;
}

責(zé)任編輯:武曉燕 來源: Spring全家桶實(shí)戰(zhàn)案例源碼
相關(guān)推薦

2020-07-07 10:24:15

華為服務(wù)技術(shù)

2014-08-25 09:41:22

GMGDC

2019-07-28 21:29:40

2023-11-07 15:03:56

2009-08-31 16:28:35

程序開發(fā)語言

2022-04-12 15:49:46

IT領(lǐng)導(dǎo)者技能

2025-03-10 00:14:00

C#開發(fā)者技術(shù)

2025-02-11 08:00:00

閉包JavaScript開發(fā)

2023-12-06 07:36:27

前端開發(fā)

2013-09-10 09:35:53

移動(dòng)開發(fā)者全能開發(fā)者技能

2019-12-26 09:00:27

云計(jì)算悖論智能

2015-07-08 08:51:11

SDN

2020-12-22 09:55:55

IT首席信息官CIO

2022-05-23 08:09:42

物聯(lián)網(wǎng)IOT

2020-12-18 10:35:27

IT技術(shù)領(lǐng)導(dǎo)者

2018-04-11 14:13:29

物聯(lián)網(wǎng)信息技術(shù)互聯(lián)網(wǎng)

2017-09-04 18:02:58

應(yīng)用程序APP移動(dòng)設(shè)備

2018-09-10 06:00:12

2012-06-13 01:23:30

開發(fā)者程序員

2009-12-01 14:35:06

Linux忠告
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)