String求求你別秀了

来源:https://www.cnblogs.com/zhuoqingsen/archive/2018/11/17/String.html
-Advertisement-
Play Games

小魯班今年電腦專業大四了,在學校可學了不少軟體開發的東西,也自學了一些JAVA的後臺框架,躊躇滿志,一心想著找個好單位實習。當投遞了無數份簡歷後,終於收到了一個公司發來的面試通知,小魯班欣喜若狂。 到了人家單位後,前臺小姐姐給了小魯班一份筆試題目,要求在一個小時內完成,小魯班雙手接過題目後,粗略的 ...


  小魯班今年電腦專業大四了,在學校可學了不少軟體開發的東西,也自學了一些JAVA的後臺框架,躊躇滿志,一心想著找個好單位實習。當投遞了無數份簡歷後,終於收到了一個公司發來的面試通知,小魯班欣喜若狂。

  到了人家單位後,前臺小姐姐給了小魯班一份筆試題目,要求在一個小時內完成,小魯班雙手接過題目後,粗略的看了一下題目,心裡暗喜,嘻嘻這個還不簡單。一頓操作猛如虎,做完了感覺也沒什麼錯誤。就交卷了,等待片刻後,小姐姐親切的說需要一周內等通知哦。於是呢,小魯班就回去耐心的等待了。可是半個月都快過去了,什麼消息都沒有,小魯班就納悶了,明明我做的挺好的呀,為什麼連面試的機會都不給我。

  小魯班於是找到了他表哥魯班大師,正準備吐槽這件事,並把一些當時面試的題目重現了一些,並把自己對題目的理解也說了遍,魯班大師一看他填的答案就開始嘲諷他,前5道題目關於String類的判斷題可真是完全避開了正確答案呀,而且後邊的題目也是大部分都是錯了,人家當然不給你機會呀。

  小魯班你可要虛心學習了,就拿下邊最簡單的一題來說,你怎麼連==對於非基本數據類型是比較引用而不是比較值的都不知道呀 

String str1 = new String("AA");
String str2 = new String("AA");
System.out.println(str1 == str2);
這裡的正確答案是false

 

  魯班大師:感覺你的JAVA基礎不咋地呀,你說說你在學校學習你所掌握的關於String類的知識點,你表哥今天有空幫你惡補一波吧。

  小魯班垂頭喪氣的說到:

  • String類有如下這些特點

  1. String類是final類,也即意味著String類不能被繼承,並且它的成員方法都預設為final方法。
  2. String類其實是通過char數組來保存字元串的。
  3. String對象一旦被創建就是固定不變的了,對String對象的任何改變都不影響到原對象,相關的任何change操作都會生成新的對象。

 

  魯班大師:嗯,不錯嘛,那有沒有深入一點的理解呢,比如關於字元串常量池

  小魯班:這個我~~忘記了!

  魯班大師:沒關係,那你得認真聽講了

  小魯班:emmm

 

  • 字元串常量池

  1. 我們知道字元串的分配和其他對象分配一樣,是需要消耗高昂的時間和空間的,而且字元串我們使用的非常多。JVM為了提高性能和減少記憶體的開銷,在實例化字元串的時候進行了一些優化:使用字元串常量池。每當我們創建字元串常量時,JVM會首先檢查字元串常量池,如果該字元串已經存在常量池中,那麼就直接返回常量池中的實例引用。如果字元串不存在常量池中,就會實例化該字元串並且將其放到常量池中。由於String字元串的不可變性我們可以十分肯定常量池中一定不存在兩個相同的字元串。
  2. 字元串池的出現避免了相同內容的字元串的創建,節省了記憶體,省去了創建相同字元串的時間,同時提升了性能;另一方面,字元串池的缺點就是犧牲了JVM在常量池中遍歷對象所需要的時間,不過其時間成本相比而言比較低。

  String a="AA";

  String b="AA";

  String c=new String("AA");

  a、b和堆中創建的AA都是指向JVM字元串常量池中的"AA"對象,他們指向同一個對象。

  new關鍵字一定會產生一個對象AA,同時這個對象是存儲在堆中。所以String c=new String("AA")這一句應該產生了兩個對象:保存在方法區中字元串常量池的AA和保存堆中AA。但是在Java中根本就不存在兩個完全一模一樣的字元串對象。故堆中的AA應該是引用字元串常量池中AA。所以c、堆AA、池AA的關係應該是:c--->堆AA--->池AA。

  雖然a、b、c是不同的引用,但是從String的內部結構我們是可以理解上面的。String c = new String("AA");雖然c的內容是創建在堆中,但是他的內部value還是指向JVM常量池的AA的value,它構造AA時所用的參數依然是AA字元串常量。所以a==b是ture,因為記憶體地址是一樣的 a==c是false,因為c的記憶體地址指向是在堆中new的是新的地址,而不是在常量池的地址。

  魯班大師又問了:我看你還挺懵的,你知道==和equals嗎

  小魯班:這個我知道。

  

  • 對於==,如果作用於基本數據類型的變數(byte,short,char,int,long,float,double,boolean ),則直接比較其存儲的"值"是否相等;如果作用於引用類型的變數(String),則比較的是所指向的對象的地址(即是否指向同一個對象)。
  • 對於equals方法,註意:equals方法不能作用於基本數據類型的變數。如果沒有對equals方法進行重寫,則比較的是引用類型的變數所指向的對象的地址;而String類對equals方法進行了重寫,用來比較指向的字元串對象所存儲的字元串是否相等。其他的一些類諸如Double,Date,Integer等,都對equals方法進行了重寫用來比較指向的對象所存儲的內容是否相等。

  魯班大師:嗯,答的不錯,但是要應付一些面試題,你還要知道這些。

  1. 單獨使用""引號創建的字元串都是常量,編譯期就已經確定存儲到String Pool中;
  2. 使用new String("")創建的對象會存儲到heap中,是運行期新創建的;
  3. 使用只包含常量的字元串連接符如"aa" + "aa"創建的也是常量,編譯期就能確定,已經確定存儲到String Pool中;
  4. 使用包含變數(引用)的字元串連接符如"aa" + s1創建的對象是運行期才創建的,存儲在heap中;
  5. 但是如果s1是被final修飾的話,則s1是屬於常量。結果存在String Pool,但是 final修飾的是一個方法返回的值也是在編譯器確定。

  好了,這些你都知道了,那你把剛那份題目在做一次看看

  

String str1 = "aaa";
String str2 = "aaa";
System.out.println(str1 == str2);// true 因為String有常量池

String str3 = new String("aaa");
String str4 = new String("aaa");
System.out.println(str3 == str4);// false 可以看出用new的方式是生成不同的對象,比較堆上的
        
String s0="helloworld";  
String s1="helloworld";  
String s2="hello"+"world";  
System.out.println(s0==s1); //true 可以看出s0跟s1是指向同一個對象   
System.out.println(s0==s2); //true 可以看出s0跟s2是指向同一個對象   
        
String st0="helloworld";   
String st1=new String("helloworld");   
String st2="hello" + new String("world");   
System.out.println( st0==st1 ); //false    用new String() 創建的字元串不是常量,不能在編譯期就確定
System.out.println( st0==st2 ); //false   st2地址存在堆中,不可能相同
System.out.println( st1==st2 ); //false  
        
        
String stri1="abc";     
String stri2="def";     
String stri3=stri1+stri2;  
System.out.println(stri3=="abcdef"); //false    變數相+是在的堆記憶體中創建
        
String strin0 = "a1";   
String strin1 = "a" + 1;   //這種不是變數,是常量
System.out.println((strin0 == strin1)); //result = true    

String strin2 = "atrue";   
String strin3= "a" + "true";   
System.out.println((strin2 == strin3)); //result = true    

String strin4 = "a3.4";   
String strin5 = "a" + 3.4;   
System.out.println((strin4 == strin5)); //result = true  
        
        
String string0 = "ab";   
String string1 = "b";   
String string2 = "a" + string1;   
System.out.println((string0 == string2)); //result = false  在字元串的"+"連接中,有字元串引用存在,而引用的值在程式編譯期是無法確定的
        
        
String test="javalanguagespecification";  
String test2="java";  
String test3="language";  
String test4="specification";         
System.out.println(test == "java" + "language" + "specification");  //true 字元串字面量拼接操作是在Java編譯器編譯期間就執行了
System.out.println(test == test2 + test3 + test4);  //false  字元串引用的"+"運算是在Java運行期間執行的
        
        
String ss0 = "ab";   
final String ss1 = "b";   
String ss2 = "a" + ss1;          
System.out.println((ss0 == ss2)); //result = true  對於final修飾的變數,它在編譯時被解析為常量值的一個本地拷貝存儲到自己的常量池中或嵌入到它的位元組碼流中。所以此時的"a" + s1和"a" + "b"效果是一樣的
        
String ss10 = "ab";   
final String ss11 = getS1();   
String ss12 = "a" + ss11;         
System.out.println((ss10 == ss12)); //result = false   這裡面雖然將s1用final修飾了,但是由於其賦值是通過方法調用返回的,那麼它的值只能在運行期間確定
    
public static String getS1(){
     return "b";     
}   

 

  魯班大師:優秀呀,小魯班!不過呢我們既然都研究了String,那麼關於StringBuffer和StringBuilder也得知道,給你佈置個作業,把他們3者的區別寫一下自己的見解,發到我的郵箱,今天就到此為止了,表哥得去開黑了。

  小魯班:謝謝表哥,我一定好好整理的!小魯班回到家後,立馬奮筆疾書。。。

  send to 魯班大師@qq.com 

  String、StringBuffer、StringBuilder的區別?

  • 可變與不可變:String是不可變字元串對象,StringBuilder和StringBuffer是可變字元串對象(其內部的字元數組長度可變)。
  • 是否多線程安全:String中的對象是不可變的,也就可以理解為常量,顯然線程安全。StringBuffer 與 StringBuilder 中的方法和功能完全是等價的,只是StringBuffer 中的方法大都採用了synchronized 關鍵字進行修飾,因此是線程安全的,而 StringBuilder 沒有這個修飾,可以被認為是非線程安全的。
  • String、StringBuilder、StringBuffer三者的執行效率如下:
  • StringBuilder > StringBuffer > String 當然這個是相對的,不一定在所有情況下都是這樣。比如String str = "hello"+ "world"的效率就比 StringBuilder st  = new   StringBuilder().append("hello").append("world")要高。因此,這三個類是各有利弊,應當根據不同的情況來進行選擇使用:
  • 當字元串相加操作或者改動較少的情況下,建議使用 String str="hello"這種形式;
  • 當字元串相加操作較多的情況下,建議使用StringBuilder,如果採用了多線程,則使用StringBuffer。

 

  好累呀,因為虛心的小魯班除了整理了這些之外,同時自己繼續學習封裝類的比較,因為也被其他筆試題坑慘了呀!

1.兩個基本類型的只能用 ==
2.基本型和封裝型用==,封裝型將會自動拆箱變為基本型後再進行比較
3.用==來比較兩個封裝類的話,比較的是地址。(其中-127到127之間的Integer地址相同,超出這個範圍則不同)
4.至少有一個封裝型的建議使用.equals。用==對基本型和封裝性比較必須保證封裝型不為null。如果為null則不能轉化為基本型就會報錯。
5.兩個封裝型進行equals()比較,首先equals()會比較類型,如果類型相同,則繼續比較值,如果值也相同,返回true
6.封裝類型調用equals(),但是參數是基本類型,這時候,先會進行自動裝箱,基本型轉換為其封裝類型,若類型不同返回false,若裝箱後類型相同,則比較值,如果值相同,則返回true

  

int a=128;
int a2=127;
Integer b=128;
Integer b2=127;
Integer c=128;
Integer c2=127;
Integer d=new Integer(a);
Integer d2=new Integer(a);

Integer b2=57;
Integer c2=57;

System.out.println(a==b);//true        

System.out.println(b==c);//false
System.out.println(b2==c2);//3true

System.out.println(a==d);//true
System.out.println(b==d);//false
System.out.println(d==d2);//false

//由強類型向弱類型轉換需要強制轉換,而由弱類型向強類型轉換則系統自動轉換。
//double 類型相比int類型是屬於強類型,則由double類型的數據向int類型數據轉換就需要強制轉換,反之則自動轉換。數據類型的強弱關係如下:
//byte<short=char<int<long<float<double,同級之間相互轉換也需要強制轉換。
//對於未聲明數據類型的整形,其預設類型為int型。 //在浮點類型(float/double)中,對於未聲明數據類型的浮點型,預設為double型。 System.out.println(b.equals(128.0));//false

 

  寫完這些後小魯班,小魯班關了臺燈,合上了電腦,休息一下準備明天的面試~

  轉載請註明出處謝謝

 

 

 

  

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • JavaScript基礎(一) JavaScript是一種弱類型的腳本語言;根據賦值決定變數的類型 var a = 3; (變數聲明之前必須用var聲明,編程規範!) 變數的命名規範 變數聲明 全局變數 : 1.在方法外部聲明的變數 2.方法內部,沒有加var關鍵字聲明的變數 局部變數 1.方法內部 ...
  • 內置對象,宿主對象,自定義對象的區別? 內置對象: 系統所提供的對象:Object,Array,Math,Date等等。 宿主對象: JS所運行的環境提供的對象:BOM中的window,DOM中的document 自定義對象: 自定義構造函數所創建的對象 。 Array類型對象: 數組創建方式: 方 ...
  • 設計模式很重要,重要性我就不再覆述了。最主要的是,通常我們在寫一定量代碼後,常用的方法什麼的都熟悉後,想再提高代碼能力,我找到的最好的方法還是去學習,理解設計模式。不理解設計模式,看一些開源框架和java源碼會很難懂。 剛學java時就從各種文章,也聽很多人說面向對象編程很重要。但是在編程時雖然會建 ...
  • 安裝 SmartSql.ZooKeeperConfig Demo 工具使用 "ZooKeeper Admin" ...
  • 一.Actor模型介紹 在單核 CPU 發展已經達到一個瓶頸的今天,要增加硬體的速度更多的是增加 CPU 核的數目。而針對這種情況,要使我們的程式運行效率提高,那麼也應該從併發方面入手。傳統的多線程方法又極其容易出現 Bug 而難以維護,不過別擔心,今天將要介紹另一種併發的模式能一定程度解決這些問題 ...
  • Map 遍歷: ...
  • Java中return用於方法,兩個作用: (1)返回方法指定類型的值(這個值總是確定的),也可以是對象 (2)方法的結束 兩種形式: (1)有返回類型 例如:return i; (2)無返回類型 return; 一般方法前有void代表無返回值,沒有void有返回值。 “break”語句用來結束循 ...
  • 有一段時間沒寫博客了,雖然是菜鳥一枚但畢竟總要有東西記錄學習的,我相信有志者事竟成。今天在工作中使用Jackson轉換了一個javabean,傳到測試服上之後發現日期少了一天,使用的是@JsonFormat註解。這裡寫了一個簡單的小demo記錄一下: 表數據: 實體類屬性: controller層就 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...