最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JAVA對象中使用?static?和?String?基礎(chǔ)探究

 更新時間:2022年09月27日 14:30:52   作者:????????????  
這篇文章主要介紹了JAVA對象中使用static和String基礎(chǔ)探究,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下

前言

跟同學(xué)在討論 JAVA 期末試題時,對于一些 static 和 String 在對象中的使用方法,若有所思,特此記錄一下,也祝沒有對象的友友可以自己 new 一個出來!

那我們先來看一看試卷里的原題;  

原題

主要就是兩個類 MyClass.java 和 TestMyClass.java,填代碼的部分就直接跳過了,然后就是輸出結(jié)果,看看你是否也能全部正確,

兩個類的具體代碼如下:

MyClass.java

public class MyClass {
    private int count;
    String info;
    public static String message = "Good";

    public MyClass increase() {
        count++;
        return this;
    }
    private MyClass() {
        this.count=0;
        this.info = "GoodLuck";
    }
    public int getCount() {
        return count;
    }
    public static MyClass getInstance() {
        return new MyClass();
    }
}

TestMyClass.java

public class TestMyClass {
    public static void main(String[] args) {
        MyClass mc1 = MyClass.getInstance();
        MyClass mc2 = MyClass.getInstance();
        mc1.message = "Great";
        mc2.message = "Excellent";
        MyClass.message = "Nice";

        System.out.println(mc1.message+":"+mc2.message+":"+MyClass.message);
        System.out.println(mc1.info == mc2.info);
        mc2.info = new String("GoodLuck");
        System.out.println(mc1.info == mc2.info);
        System.out.println(mc1.info.equals(mc2.info));
        System.out.println(mc1.increase().increase().getCount());
    }
}

運行結(jié)果:

Nice:Nice:Nice
true
false
true
2

如果你全部答對了,那么恭喜你,基礎(chǔ)很不錯喲;答錯的小伙伴也不要喪氣,接下來聽我娓娓道來,扎實基礎(chǔ);  

static

工欲善其事必先利其器,在開始解析之前,我們先回顧一下一些關(guān)于 static 的知識;

簡介

static 表示 “全局” 或者 “靜態(tài)” 的意思,用來修飾成員變量和成員方法,也可以形成靜態(tài) static 代碼塊,但是 Java 語言中沒有全局變量的概念;

被 static 修飾的成員變量和成員方法獨立于該類的任何對象,也就是說,它不依賴類特定的實例,被類的所有實例共享;

只要這個類被加載,Java 虛擬機就能根據(jù)類名在運行時數(shù)據(jù)區(qū)的方法區(qū)內(nèi)定找到他們,因此,static 對象可以在它的任何對象創(chuàng)建之前訪問,無需引用任何對象;

用 public 修飾的 static 成員變量和成員方法本質(zhì)是全局變量和全局方法,當(dāng)聲明它類的對象時,不生成 static 變量的副本,而是類的所有實例共享同一個 static 變量;

static 變量前可以有 private 修飾,表示這個變量可以在類的靜態(tài)代碼塊中,或者類的其他靜態(tài)成員方法中使用,但是不能在其他類中通過類名來直接引用,這一點很重要;

實際上你需要搞明白,private 是訪問權(quán)限限定,static 表示不要實例化就可以使用,這樣就容易理解多了,static 前面加上其它訪問權(quán)限關(guān)鍵字的效果也以此類推。

static 修飾的成員變量和成員方法習(xí)慣上稱為靜態(tài)變量和靜態(tài)方法,可以直接通過類名來訪問,訪問語法為:

類名.靜態(tài)方法名(參數(shù)列表…)

使用

回顧了 static 相關(guān)知識之后,我們來看一下題目中的使用吧;

// MyClass.java
public static String message = "Good";

// TestMyClass.java
MyClass mc1 = MyClass.getInstance();
MyClass mc2 = MyClass.getInstance();
mc1.message = "Great";
mc2.message = "Excellent";
MyClass.message = "Nice";

System.out.println(mc1.message+":"+mc2.message+":"+MyClass.message);

先是用 static 修飾了成員變量 message,然后通過下斷點調(diào)試可以獲知,兩個對象 mcl1 和 mcl2 被分配到了兩個不同的地址;

在往下調(diào)試時,發(fā)現(xiàn) mc1.message,mc2.messageMyClass.message 三個成員變量的值是一樣的,且都從 Great → Excellent → Nice,這就是剛才所回顧的,被 static 修飾的成員變量成了共享變量,被類的所有實例共享;

接下來我們再做個試驗驗證一下:

//修改前
private int count;

//修改后
private static int count;

可以發(fā)現(xiàn),我們只是對 mcl1 對象進行了操作,但是 mcl2 的成員變量 count 也跟著改變了,這就是因為在 MyClass 類中,成員變量 count 被 static 修飾,已經(jīng)成了該類的共享變量了,但凡是該類的對象,都訪問的是同一個 count 變量;

當(dāng)然也可以通過身份碼進行驗證:

System.out.println("mcl1: " + System.identityHashCode(mc1));
System.out.println("mcl2: " + System.identityHashCode(mc2));
System.out.println("mcl1_count: " + System.identityHashCode(mc1.getCount()));
System.out.println("mcl2_count: " + System.identityHashCode(mc2.getCount()));
mcl1: 940553268
mcl2: 1720435669
mcl1_count: 1020923989
mcl2_count: 1020923989

因此 System.out.println(mc1.message+":"+mc2.message+":"+MyClass.message); 輸出的是 Nice:Nice:Nice;

接下來講一些關(guān)于 String 的小知識;  

String

關(guān)于 String 的話,這里用到啥聊啥,就不全面的進行了;

== 與 equals()

先來講講關(guān)于 String 的比較,一般常見的比較有兩種,即 == 和 equals()

其中,== 比較的是兩個字符串的地址是否為相等(同一個地址),equals() 方法比較的是兩個字符串對象的內(nèi)容是否相同(當(dāng)然,若兩個字符串引用同一個地址,使用 equals() 比較也返回 true);

這里就不得不提第二個知識點了,String 常量與非常量的區(qū)別;  

常量與非常量

那什么是常量,什么是非常量呢,簡單了解就是,String name = "sid10t." 這個是常量,屬于是對 name 進行賦值,直接存儲在常量池中,而 String name = new String("sid10t.") 這個就是非常量,因為重新創(chuàng)建了一個對象,這會將字符串 sid10t. 存儲在常量池中,然后在 Heap 中創(chuàng)建對象指向 name;

那這里為什么要提這個呢?當(dāng)然是因為他們有較大的區(qū)別;

在 Java 語言規(guī)范(JavaSE 1.8版本)章節(jié)3.10.5 中有做規(guī)范,所有的 Java 語言編譯、運行時環(huán)境實現(xiàn)都必須依據(jù)此規(guī)范來實現(xiàn),里面有這么一句話:

Moreover, a string literal always refers to the same instance of class String. This is because string literals - or, more generally, strings that are the values of constant expressions (§15.28) - are "interned" so as to share unique instances, using the method String.intern.

大致意思就是凡是內(nèi)容一樣的字符串常數(shù),都要引用同一個字符串對象,換句話說就是內(nèi)存地址相同;

因為其值為常量的字符串,都會通過 String.intern() 函數(shù)被限定為共享同一個對象;

稍后會解析 intern() 函數(shù),也可以自行參考說明 String (Java Platform SE 8 );

回到正題,看一下語言規(guī)范里的這段代碼:

package testPackage;

class Test {
    public static void main(String[] args) {
        String hello = "Hello", lo = "lo";
        System.out.print((hello == "Hello") + " ");
        System.out.print((Other.hello == hello) + " ");
        System.out.print((other.Other.hello == hello) + " ");
        System.out.print((hello == ("Hel"+"lo")) + " ");
        System.out.print((hello == ("Hel"+lo)) + " ");
        System.out.println(hello == ("Hel"+lo).intern());
    }
}
class Other { static String hello = "Hello"; }

以及另一個包中的類:

package other;
public class Other { public static String hello = "Hello"; }

運行結(jié)果:

true true true true false true

結(jié)論:

  • 同一個包中同一個類中的字符串表示對同一個 String 對象的引用;
  • 同一個包中不同類中的字符串表示對同一個 String 對象的引用;
  • 不同包中不同類中的字符串同樣表示對同一 String 對象的引用;
  • 由常量表達式計算的字符串在編譯時計算,然后將其視為文字;
  • 在運行時通過連接計算的字符串是新創(chuàng)建的,因此是不同的;
  • 顯式地嵌入一個計算出來的字符串的結(jié)果與任何現(xiàn)有的具有相同內(nèi)容的字面值字符串的結(jié)果相同;

如果對結(jié)論的理解不是很深刻的話,那就看看接下來的解釋:

System.out.print((hello == "Hello") + " ");
System.out.print((Other.hello == hello) + " ");
System.out.print((other.Other.hello == hello) + " ");

“Hello” 和 “lo” 是字符串常量,在編譯期就被確定了,先檢查字符串常量池中是否含有 “Hello” 和 “lo”,如果沒有則添加 “Hello” 和 “lo” 到字符串常量池中,并且直接指向它們,所以 hello 和 lo 分別直接指向字符串常量池的 “Hello” 和 “lo”,也就是 hello 和 lo 指向的地址分別是常量池中的 “Hello” 和 “lo” ,因此第一個輸出中 hello 實際就是 “Hello”,所以 "Hello" == "Hello" 為 true,前三個輸出都是同理的;

System.out.print((hello == ("Hel"+"lo")) + " ");

"Hel" 和 "lo" 都是字符串常量,當(dāng)一個字符串多個字符串常量連接而成時,它自己肯定也是字符串常量,在編譯器會被編譯器優(yōu)化成 "Hello",因為 "Hello" 在常量池中了,因此輸出為 true;

System.out.print((hello == ("Hel"+lo)) + " ");
System.out.println(hello == ("Hel"+lo).intern());

JVM 對于字符串引用,由于在字符串的 + 連接中,有字符串引用存在,而引用的值在程序編譯期是無法確定的,即 "Hel"+lo,所以在不執(zhí)行 intern() 方法的前提下,"Hel"+lo 不會存到常量池中,但 "Hel" 會被存到常量池中去,所以輸出一個為 true,一個為 false

intern()

String.intern() 是一個 Native 方法,它的作用是如果字符串常量池已經(jīng)包含一個等于此 String 對象的字符串,則返回字符串常量池中這個字符串的引用, 否則將當(dāng)前 String 對象的引用地址(堆中)添加到字符串常量池中并返回。  

JAVA 源碼

/*
Returns a canonical representation for the string object.
A pool of strings, initially empty, is maintained privately by the class String.
When the intern method is invoked, if the pool already contains a string equal to
this String object as determined by the equals(Object) method, then the string from
the pool is returned. Otherwise, this String object is added to the pool and a
reference to this String object is returned.

It follows that for any two strings s and t, s.intern() == t.intern() is true if and
only if s.equals(t) is true.

All literal strings and string-valued constant expressions are interned. String
literals are defined in section 3.10.5 of the The Java Language Specification.

Returns:
a string that has the same contents as this string, but is guaranteed to be from a
pool of unique strings.
*/
public native String intern();

native 源碼

String.c

Java_java_lang_String_intern(JNIEnv *env, jobject this)  
{  
    return JVM_InternString(env, this);  
}  

jvm.h

/* 
* java.lang.String 
*/  
JNIEXPORT jstring JNICALL  
JVM_InternString(JNIEnv *env, jstring str);   

jvm.cpp

JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str))  
  JVMWrapper("JVM_InternString");  
  JvmtiVMObjectAllocEventCollector oam;  
  if (str == NULL) return NULL;  
  oop string = JNIHandles::resolve_non_null(str);  
  oop result = StringTable::intern(string, CHECK_NULL);
  return (jstring) JNIHandles::make_local(env, result);  
JVM_END   

symbolTable.cpp

oop StringTable::intern(Handle string_or_null, jchar* name,  
                        int len, TRAPS) {  
  unsigned int hashValue = java_lang_String::hash_string(name, len);  
  int index = the_table()->hash_to_index(hashValue);  
  oop string = the_table()->lookup(index, name, len, hashValue);  
  // Found  
  if (string != NULL) return string;  
  // Otherwise, add to symbol to table  
  return the_table()->basic_add(index, string_or_null, name, len,  
                                hashValue, CHECK_NULL);  
}   
oop StringTable::lookup(int index, jchar* name,  
                        int len, unsigned int hash) {  
  for (HashtableEntry<oop>* l = bucket(index); l != NULL; l = l->next()) {  
    if (l->hash() == hash) {  
      if (java_lang_String::equals(l->literal(), name, len)) {  
        return l->literal();  
      }  
    }  
  }  
  return NULL;  
}  

它的大體實現(xiàn)結(jié)構(gòu)就是:JAVA 使用 jni 調(diào)用 c++ 實現(xiàn)的 StringTable 的 intern 方法,StringTable 的 intern 方法跟 Java 中的 HashMap 的實現(xiàn)是差不多的,只是不能自動擴容,默認(rèn)大小是1009。

要注意的是,String 的 String Pool 是一個固定大小的 Hashtable,默認(rèn)值大小長度是1009,如果放進 String Pool 的 String 非常多,就會造成 Hash 沖突嚴(yán)重,從而導(dǎo)致鏈表會很長,而鏈表長了后直接會造成的影響就是當(dāng)調(diào)用 String.intern 時性能會大幅下降(因為要一個一個找)。

在 JDK6 中 StringTable 是固定的,就是1009的長度,所以如果常量池中的字符串過多就會導(dǎo)致效率下降很快。

在 JDK7 中,StringTable 的長度可以通過一個參數(shù)指定:-XX:StringTableSize=99991;  

使用

在 JDK1.7 之前的版本,調(diào)用這個方法的時候,會去常量池中查看是否已經(jīng)存在這個常量了,如果已經(jīng)存在,那么直接返回這個常量在常量池中的地址值,如果不存在,則在常量池中創(chuàng)建一個,并返回其地址值。

但是在 JDK1.7 以及之后的版本中,常量池從 perm 區(qū)搬到了 heap 區(qū)。intern() 檢測到這個常量在常量池中不存在的時候,不會直接在常量池中創(chuàng)建該對象了,而是將堆中的這個對象的引用直接存到常量池中,減少內(nèi)存開銷。

來看這段代碼:

public static void main(String[] args) {
	// part1
    String s1 = new String("sid10t.");
    s1.intern();
    String s2 = "sid10t.";
    System.out.println(s1 == s2);

	// part2
    String s3 = new String("Hello ") + new String("World!");
    s3.intern();
    String s4 = "Hello World!";
    System.out.println(s3 == s4);
}

在 JDK7 之前兩個都是 false,在 JDK7 之后輸出分別是 falsetrue;

接下來根據(jù) JDK7 進行分析,

先來看 part1 部分:

String s1 = new String("sid10t."); 這行代碼生成了兩個最終對象:一個是常量池中的字符串常量 sid10t.,另一個是在堆中的 s1 引用指向的對象;

然后是第二行 s1.intern();,返回常量池中的字符串常量 sid10t.,因為常量池中已經(jīng)存在了該常量,所以這里就直接返回即可,因此,在 part1 的此情此景中,這句話可寫可不寫,對輸出結(jié)果沒有任何影響;

所以最后輸出的肯定是 false,一個地址在堆中,一個在常量池里;

接下來看看 part2 部分:

String s3 = new String("Hello ") + new String("World!"); 這行代碼生成了三個最終對象:兩個分別是常量池中的對象 Hello ,World!,還有一個在堆中的 s3 引用指向的對象;

然后是第二行 s3.intern();,由于現(xiàn)在的常量池中不存在字符串常量 Hello World!,因此它會直接存儲 s3 在堆中的引用地址,而不是拷貝一份;

這時候,String s4 = "Hello World!";,常量池中已經(jīng)有了 Hello World! 常量,也就是 s3 的引用地址,因此 s4 的值就是 s3 的引用地址,所以輸出的是 true;

根據(jù)上述分析,我們將 part2 的代碼略作調(diào)整,如下:

String s3 = new String("Hello ") + new String("World!");
// s3.intern();
String s4 = "Hello World!";
s3.intern();
System.out.println(s3 == s4);

輸出的就是 false 了,但如果是 s3.intern() == s4,則輸出的就是 true;

想必你應(yīng)該理解了!  

總結(jié)

到此這篇關(guān)于JAVA對象中使用 static 和 String 基礎(chǔ)探究的文章就介紹到這了,更多相關(guān)JAVA static和String內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java把list分成多個list的方法示例

    Java把list分成多個list的方法示例

    在編程中將大的list集合拆分為多個小集合可以提高處理效率,主要拆分方法包括等量拆分適用于均勻處理數(shù)據(jù)、?指定數(shù)量拆分按固定子集大小劃分、根據(jù)條件拆分按特定規(guī)則進行,這些方法有助于多線程異步處理,需要的朋友可以參考下
    2024-11-11
  • 基于SpringBoot實現(xiàn)自定義插件的流程詳解

    基于SpringBoot實現(xiàn)自定義插件的流程詳解

    在SpringBoot中,插件是一種擴展機制,它可以幫助我們在應(yīng)用程序中快速地添加一些額外的功能,在本文中,我們將介紹如何使用 SpringBoot實現(xiàn)自定義插件,需要的朋友可以參考下
    2023-06-06
  • java使用des加密解密示例分享

    java使用des加密解密示例分享

    java使用des加密解密示例,適合java語言的所有平臺,與.net等平臺的加密解密兼容
    2014-02-02
  • 淺談s:select 標(biāo)簽中l(wèi)ist存放map對象的使用

    淺談s:select 標(biāo)簽中l(wèi)ist存放map對象的使用

    下面小編就為大家?guī)硪黄獪\談s:select 標(biāo)簽中l(wèi)ist存放map對象的使用。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-11-11
  • logstash將mysql數(shù)據(jù)同步到elasticsearch方法詳解

    logstash將mysql數(shù)據(jù)同步到elasticsearch方法詳解

    這篇文章主要為大家介紹了logstash將mysql數(shù)據(jù)同步到elasticsearch方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12
  • Spring Cloud Feign的文件上傳實現(xiàn)的示例代碼

    Spring Cloud Feign的文件上傳實現(xiàn)的示例代碼

    這篇文章主要介紹了Spring Cloud Feign的文件上傳實現(xiàn)的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03
  • java讀取cvs文件并導(dǎo)入數(shù)據(jù)庫

    java讀取cvs文件并導(dǎo)入數(shù)據(jù)庫

    這篇文章主要為大家詳細(xì)介紹了java讀取cvs文件并導(dǎo)入數(shù)據(jù)庫,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • icePDF去水印的方法(推薦)

    icePDF去水印的方法(推薦)

    下面小編就為大家?guī)硪黄猧cePDF去水印的方法(推薦)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-12-12
  • Elasticsearch QueryBuilder簡單查詢實現(xiàn)解析

    Elasticsearch QueryBuilder簡單查詢實現(xiàn)解析

    這篇文章主要介紹了Elasticsearch QueryBuilder簡單查詢實現(xiàn)解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-08-08
  • Spring Boot使用Value注解給靜態(tài)變量賦值的方法

    Spring Boot使用Value注解給靜態(tài)變量賦值的方法

    這篇文章主要介紹了Spring Boot使用Value注解給靜態(tài)變量賦值的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07

最新評論

东光县| 如东县| 岳普湖县| 洞口县| 张家界市| 罗江县| 民勤县| 沁源县| 铁岭市| 探索| 邮箱| 红原县| 湖南省| 汕头市| 开化县| 沂水县| 九江县| 波密县| 文成县| 运城市| 常德市| 紫云| 泗阳县| 内丘县| 栖霞市| 铁岭县| 焦作市| 灌阳县| 麦盖提县| 集安市| 青州市| 岳池县| 乐安县| 廊坊市| 尤溪县| 大城县| 和顺县| 龙井市| 金门县| 清流县| 蒙城县|