真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Java字符串的構(gòu)造和拼接

這篇文章主要講解了“Java字符串的構(gòu)造和拼接”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Java字符串的構(gòu)造和拼接”吧!

創(chuàng)新互聯(lián)專(zhuān)注于網(wǎng)站建設(shè)|網(wǎng)站建設(shè)維護(hù)|優(yōu)化|托管以及網(wǎng)絡(luò)推廣,積累了大量的網(wǎng)站設(shè)計(jì)與制作經(jīng)驗(yàn),為許多企業(yè)提供了網(wǎng)站定制設(shè)計(jì)服務(wù),案例作品覆蓋公路鉆孔機(jī)等行業(yè)。能根據(jù)企業(yè)所處的行業(yè)與銷(xiāo)售的產(chǎn)品,結(jié)合品牌形象的塑造,量身建設(shè)品質(zhì)網(wǎng)站。

2.1 構(gòu)造字符串

字符串在Java里是不可變的,無(wú)論是構(gòu)造,還是截取,得到的總是一個(gè)新字符串。看一下構(gòu)造一個(gè)字符串源碼

private final char value[];
public String(String original) {
  this.value = original.value;
  this.hash = original.hash;
}

原有的字符串的value數(shù)組直接通過(guò)引用賦值給新的字符串value,也就是倆個(gè)字符串共享一個(gè)char數(shù)組,因此這種構(gòu)造方法有著最快的構(gòu)造。Java里的String對(duì)象被設(shè)計(jì)為不可變。意思是指一旦程序獲得了字符串對(duì)象引用,不必?fù)?dān)心這個(gè)字符串在別的地方被修改,不可變意味著線(xiàn)程安全,在第三章對(duì)不可變對(duì)象線(xiàn)程安全性又說(shuō)明。

構(gòu)造字符串更多的情況構(gòu)造字符串是通過(guò)一個(gè)字符串?dāng)?shù)組,或者在某些框架的反序列化,使用byte[] 來(lái)構(gòu)造字符串,這種情況下性能會(huì)非常低。 如下是通過(guò)char[]數(shù)組構(gòu)造一個(gè)新的字符串源碼

public String(char value[]) {
  this.value = Arrays.copyOf(value, value.length);
}

Arrays.copyOf 會(huì)重新拷貝一份新的數(shù)組,方法如下

public static char[] copyOf(char[] original, int newLength) {
  char[] copy = new char[newLength];
  System.arraycopy(original, 0, copy, 0,
                   Math.min(original.length, newLength));
  return copy;
}

可以看到通過(guò)數(shù)組構(gòu)造字符串實(shí)際上是會(huì)創(chuàng)建一個(gè)新的字符串?dāng)?shù)組。如果不這樣,還是直接引用char數(shù)組,那么外部如果更改char數(shù)組,則這個(gè)新的字符串就被改變了。

char[] cs = new char[]{'a','b'};
String str = new String(cs);
cs[0] ='!'

上面的代碼最后一行,修改了cs數(shù)組,但不會(huì)影響str。因?yàn)閟tr實(shí)際上是新的字符串?dāng)?shù)組構(gòu)成

通過(guò)char數(shù)組構(gòu)造新的字符串是最長(zhǎng)用的方法,我們后面看到幾乎每個(gè)字符串API,都會(huì)調(diào)用這個(gè)方法構(gòu)造新的字符串,比如subString,concat等方法。如下代碼驗(yàn)證了通過(guò)字符串構(gòu)造新的字符串,以及使用char數(shù)組構(gòu)造字符串性能比較

String str= "你好,String";
char[] chars = str.toCharArray();

[@Benchmark](https://my.oschina.net/u/3268003)
public String string(){
  return new String(str);
}

[@Benchmark](https://my.oschina.net/u/3268003)
public String stringByCharArray(){
  return new String(chars);
}

輸出按照ns/op來(lái)輸出,既每次調(diào)用所用的納秒數(shù),可以看到通過(guò)char構(gòu)造字符串還是先當(dāng)耗時(shí)的,特別如果是數(shù)組特別長(zhǎng),那更加耗時(shí)

Benchmark                                  Mode     Score    Units     
c.i.c.c.NewStringTest.string               avgt     4.235    ns/op     
c.i.c.c.NewStringTest.stringByCharArray    avgt    11.704    ns/op

通過(guò)字節(jié)構(gòu)造字符串,是一種非常常見(jiàn)的情況,尤其現(xiàn)在分布式和微服務(wù)流行,字符串在客戶(hù)端序列化成字節(jié)數(shù)組,并發(fā)送給你給

測(cè)試結(jié)果可以看到byte構(gòu)造字符串太耗時(shí)了,尤其是當(dāng)要構(gòu)造的字符串非常長(zhǎng)的時(shí)候

Benchmark                                  Mode    Score    Units       
c.i.c.c.NewStringTest.string               avgt    4.649    ns/op       
c.i.c.c.NewStringTest.stringByByteArray    avgt   82.166    ns/op       
c.i.c.c.NewStringTest.stringByCharArray    avgt   12.138    ns/op

通過(guò)字節(jié)數(shù)組構(gòu)造字符串,主要涉及到轉(zhuǎn)碼過(guò)程,內(nèi)部會(huì)調(diào)用 StringCoding.decode轉(zhuǎn)碼

this.value = StringCoding.decode(charsetName, bytes, offset, length);

charsetName表示字符集,bytes是字節(jié)數(shù)組,offset和length表示字節(jié)數(shù)組

實(shí)際負(fù)責(zé)轉(zhuǎn)碼的是Charset子類(lèi),比如sun.nio.cs.UTF_8的decode方法負(fù)責(zé)實(shí)現(xiàn)字節(jié)轉(zhuǎn)碼,如果在深入到這個(gè)類(lèi),你會(huì)發(fā)現(xiàn),你看到的是冰上一角,冰上下面這是一個(gè)相當(dāng)耗CPU計(jì)算轉(zhuǎn)碼的工作,屬于無(wú)法優(yōu)化的部分.

在我多次的系統(tǒng)性能優(yōu)化過(guò)程中,都會(huì)發(fā)現(xiàn)通過(guò)字節(jié)數(shù)據(jù)組構(gòu)造字符串總是排在消耗CPU比較靠前的位置,轉(zhuǎn)碼消耗的系統(tǒng)性能抵得上百行的業(yè)務(wù)代碼。 因此我們系統(tǒng)在設(shè)計(jì)到分布式的,需要仔細(xì)設(shè)計(jì)需要傳輸?shù)淖侄?,盡量避免用String。比如時(shí)間可以用long類(lèi)型來(lái)表示,業(yè)務(wù)狀態(tài)也可以用int來(lái)表示。如下需要序列化的對(duì)象

public class OrderResponse{
  //訂單日期,格式'yyyy-MM-dd'
  private String createDate;
  //訂單狀態(tài),"0"表示正常
  private String status;
}

可以改進(jìn)成更好的定義,以減小序列化和反序列化負(fù)擔(dān)。

public class OrderResponse{
  //訂單日期
  private long  createDate;
  //訂單狀態(tài),0表示正常
  private int status;
}

關(guān)于在微服務(wù)中,序列化和反序列化傳輸對(duì)象,會(huì)在第四章和五章再次介紹對(duì)象的序列化

2.2 字符串拼接

JDK會(huì)自動(dòng)將使用+號(hào)做的字符串拼接自動(dòng)轉(zhuǎn)化為StringBuilder,如下代碼:

String a="hello";
String b ="world "
String str=a+b;

虛擬機(jī)會(huì)編譯成如下代碼

String str = new StringBuilder().append(a).append(b).toString();

如果你運(yùn)行JMH測(cè)試這倆段代碼,性能其實(shí)一樣的,因?yàn)槭褂?連接字符串是一個(gè)常見(jiàn)操作,虛擬機(jī)對(duì)如上倆個(gè)代碼片段都會(huì)做一些優(yōu)化,虛擬使用-XX:+OptimizeStringConcat 打開(kāi)字符串拼接優(yōu)化,(默認(rèn)情況下是打開(kāi)的)。 如果采用以下代碼,雖然看是跟上面的代碼片段差不多,但虛擬機(jī)無(wú)法識(shí)別這種字符串拼接模式,性能會(huì)下降很多

StringBuilder sb = new StringBuilder();
sb.append(a);
sb.append(b);

運(yùn)行StringConcatTest類(lèi),代碼如下

String a = "select u.id,u.name from user  u";
String b="  where u.id=? "   ;
[@Benchmark](https://my.oschina.net/u/3268003)
public String concat(){
  String c = a+b;
  return c ;

}

[@Benchmark](https://my.oschina.net/u/3268003)
public String concatbyOptimizeBuilder(){
  String c = new StringBuilder().append(a).append(b).toString();
  return c;
}


@Benchmark
public String concatbyBuilder(){
  //不會(huì)優(yōu)化
  StringBuilder sb = new StringBuilder();
  sb.append(a);
  sb.append(b);
  return sb.toString();
}

有如下結(jié)果說(shuō)明了虛擬機(jī)優(yōu)化起了作用

Benchmark                                           Mode    Score    Units         
c.i.c.c.StringConcatTest.concat                     avgt   25.747    ns/op         
c.i.c.c.StringConcatTest.concatbyBuilder            avgt   90.548    ns/op         
c.i.c.c.StringConcatTest.concatbyOptimizeBuilder    avgt   21.904    ns/op

可以看到concatbyBuilder是最慢的,因?yàn)闆](méi)有被JVM優(yōu)化

這里說(shuō)的JVM優(yōu)化,指的是虛擬機(jī)JIT優(yōu)化,我們會(huì)在第8章JIT優(yōu)化說(shuō)明

讀者可以自己驗(yàn)證一下a+b+c這種字符串拼接性能,看一下是否被優(yōu)化了

同StringBuilder類(lèi)似的還有StringBuffer,主要功能都繼承AbstractStringBuilder, 提供了線(xiàn)程安全方法,比如append方法,使用了synchronized關(guān)鍵字

@Override
public synchronized StringBuffer append(String str) {
  //忽略其他代碼
  super.append(str);
  return this;
}

幾乎所有場(chǎng)景字符串拼接都不涉及到線(xiàn)程同步,因此StringBuffer已經(jīng)很少使用了,如上的字符串拼接例子使用StringBuffer,

  @Benchmark
  public String concatbyBuffer(){
    StringBuffer sb = new StringBuffer();
    sb.append(a);
    sb.append(b);
    return sb.toString();
  }

輸出如下

Benchmark                                           Mode      Score   Units
c.i.c.c.StringConcatTest.concatbyBuffer             avgt    111.417   ns/op
c.i.c.c.StringConcatTest.concatbyBuilder            avgt     94.758   ns/op

可以看到,StringBuffer拼接性能跟StringBuilder相比性能并不差,這得益于虛擬機(jī)的"逃逸分析",也就是JIT在打開(kāi)逃逸分析情況以及鎖消除的情況下,有可能消除該對(duì)象上的使用synchronzied限定的鎖。

逃逸分析 -XX:+DoEscapeAnalysis和 鎖消除-XX:+EliminateLocks,詳情參考本書(shū)第8章JIT優(yōu)化

如下是一個(gè)鎖消除的例子,對(duì)象obj只在方法內(nèi)部使用,因此可以消除synchronized

void foo() {
  //創(chuàng)建一個(gè)對(duì)象
  Object obj = new Object(); 
  synchronized (obj) {
    doSomething();
  }
}

程序不應(yīng)該依賴(lài)JIT的優(yōu)化,盡管打開(kāi)了逃逸分析和鎖消除,但不能保證所有代碼都會(huì)被優(yōu)化,因?yàn)殒i消除是在JIT的C2階段優(yōu)化的,作為程序員,應(yīng)該在無(wú)關(guān)線(xiàn)程安全情況下,使用StringBuilder。

使用StringBuilder 拼接其他類(lèi)型,尤其是數(shù)字類(lèi)型,則性能會(huì)明顯下降,這是因?yàn)閿?shù)字類(lèi)型轉(zhuǎn)字符在JDK內(nèi)部,需要做很多工作,一個(gè)簡(jiǎn)單的Int類(lèi)型轉(zhuǎn)為字符串,需要至少50行代碼完成。我們?cè)诘谝徽乱呀?jīng)看到過(guò)了,這里不再詳細(xì)說(shuō)明。當(dāng)你用StringBuilder來(lái)拼接字符串,拼接數(shù)字的時(shí)候,你需要思考,是否需要一個(gè)這樣的字符串。

2.10 BigDecimal

我們都知道浮點(diǎn)型變量在進(jìn)行計(jì)算的時(shí)候會(huì)出現(xiàn)丟失精度的問(wèn)題。如下一段代碼

System.out.println(0.05 + 0.01);
System.out.println(1.0 - 0.42);

輸出: 0.060000000000000005 0.5800000000000001

可以看到在Java中進(jìn)行浮點(diǎn)數(shù)運(yùn)算的時(shí)候,會(huì)出現(xiàn)丟失精度的問(wèn)題。那么我們?nèi)绻谶M(jìn)行商品價(jià)格計(jì)算的時(shí)候,就會(huì)出現(xiàn)問(wèn)題。很有可能造成我們手中有0.06元,卻無(wú)法購(gòu)買(mǎi)一個(gè)0.05元和一個(gè)0.01元的商品。因?yàn)槿缟纤?,他們兩個(gè)的總和為0.060000000000000005。這無(wú)疑是一個(gè)很?chē)?yán)重的問(wèn)題,尤其是當(dāng)電商網(wǎng)站的并發(fā)量上去的時(shí)候,出現(xiàn)的問(wèn)題將是巨大的??赡軙?huì)導(dǎo)致無(wú)法下單,或者對(duì)賬出現(xiàn)問(wèn)題。

通常有倆個(gè)方法來(lái)解決這種問(wèn)題,如果能用long來(lái)表示賬戶(hù)余額以分為單位,這是效率最高的。如果不能,則只能使用BigDecimal類(lèi)來(lái)解決這類(lèi)問(wèn)題。

BigDecimal a = new BigDecimal("0.05");
BigDecimal b = new BigDecimal("0.01");
BigDecimal ret = a.add(b);
System.out.println(ret.toString());

通過(guò)字符串來(lái)構(gòu)造BigDecimal,才能保證精度不丟失,如果使用new BigDecimal(0.05),則因?yàn)?.05本身精度丟失,使得構(gòu)造出來(lái)的BigDecimal也丟失精度。

BigDecimal能保證精度,但計(jì)算會(huì)有一定性能影響,如下是測(cè)試余額計(jì)算,用long表示分,用BigDecimal表示元的性能對(duì)比

BigDecimal a = new BigDecimal("0.05");
BigDecimal b = new BigDecimal("0.01");
long c = 5;
long d = 1;

@Benchmark
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public long addByLong() {
  return (c + d);
}
@Benchmark
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public BigDecimal addByBigDecimal() {
  return a.add(b);
}

在我的機(jī)器行,上面代碼都能進(jìn)行精確計(jì)算,通過(guò)JMH,測(cè)試結(jié)果如下

Benchmark                                 Mode   Score    Units    
c.i.c.c.BigDecimalTest.addByBigDecimal    avgt   8.373    ns/op    
c.i.c.c.BigDecimalTest.addByLong          avgt   2.984    ns/op

感謝各位的閱讀,以上就是“Java字符串的構(gòu)造和拼接”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)Java字符串的構(gòu)造和拼接這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!


當(dāng)前文章:Java字符串的構(gòu)造和拼接
轉(zhuǎn)載來(lái)于:
http://weahome.cn/article/godddg.html

其他資訊

在線(xiàn)咨詢(xún)

微信咨詢(xún)

電話(huà)咨詢(xún)

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部