欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

前言

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

那我們先來(lái)看一看試卷里的原題;  

原題

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

兩個(gè)類的具體代碼如下:

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());
    }
}

運(yùn)行結(jié)果:

Nice:Nice:Nice
true
false
true
2

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

static

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

簡(jiǎn)介

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

被 static 修飾的成員變量和成員方法獨(dú)立于該類的任何對(duì)象,也就是說(shuō),它不依賴類特定的實(shí)例,被類的所有實(shí)例共享;

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

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

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

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

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

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

使用

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

// 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,然后通過(guò)下斷點(diǎn)調(diào)試可以獲知,兩個(gè)對(duì)象 mcl1 和 mcl2 被分配到了兩個(gè)不同的地址;

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

接下來(lái)我們?cè)僮鰝€(gè)試驗(yàn)驗(yàn)證一下:

//修改前
private int count;

//修改后
private static int count;

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

當(dāng)然也可以通過(guò)身份碼進(jìn)行驗(yàn)證:

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;

接下來(lái)講一些關(guān)于 String 的小知識(shí);  

String

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

== 與 equals()

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

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

這里就不得不提第二個(gè)知識(shí)點(diǎn)了,String 常量與非常量的區(qū)別;  

常量與非常量

那什么是常量,什么是非常量呢,簡(jiǎn)單了解就是,String name = "sid10t." 這個(gè)是常量,屬于是對(duì) name 進(jìn)行賦值,直接存儲(chǔ)在常量池中,而 String name = new String("sid10t.") 這個(gè)就是非常量,因?yàn)橹匦聞?chuàng)建了一個(gè)對(duì)象,這會(huì)將字符串 sid10t. 存儲(chǔ)在常量池中,然后在 Heap 中創(chuàng)建對(duì)象指向 name

那這里為什么要提這個(gè)呢?當(dāng)然是因?yàn)樗麄冇休^大的區(qū)別;

在 Java 語(yǔ)言規(guī)范(JavaSE 1.8版本)章節(jié)3.10.5 中有做規(guī)范,所有的 Java 語(yǔ)言編譯、運(yùn)行時(shí)環(huán)境實(shí)現(xiàn)都必須依據(jù)此規(guī)范來(lái)實(shí)現(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ù),都要引用同一個(gè)字符串對(duì)象,換句話說(shuō)就是內(nèi)存地址相同;

因?yàn)槠渲禐槌A康淖址?,都?huì)通過(guò) String.intern() 函數(shù)被限定為共享同一個(gè)對(duì)象;

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

回到正題,看一下語(yǔ)言規(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"; }

以及另一個(gè)包中的類:

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

運(yùn)行結(jié)果:

true true true true false true

結(jié)論:

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

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

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” ,因此第一個(gè)輸出中 hello 實(shí)際就是 “Hello”,所以 "Hello" == "Hello" 為 true,前三個(gè)輸出都是同理的;

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

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

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

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

intern()

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

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;  
}  

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

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

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

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

使用

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

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

來(lá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 之前兩個(gè)都是 false,在 JDK7 之后輸出分別是 falsetrue;

接下來(lái)根據(jù) JDK7 進(jìn)行分析,

先來(lái)看 part1 部分:

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

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

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

接下來(lái)看看 part2 部分:

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

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

這時(shí)候,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對(duì)象中使用 static 和 String 基礎(chǔ)探究的文章就介紹到這了,更多相關(guān)JAVA static和String內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MyBatis通用Mapper實(shí)現(xiàn)原理及相關(guān)內(nèi)容

    MyBatis通用Mapper實(shí)現(xiàn)原理及相關(guān)內(nèi)容

    今天小編就為大家分享一篇關(guān)于MyBatis通用Mapper實(shí)現(xiàn)原理及相關(guān)內(nèi)容,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-12-12
  • 使用@Service注解出現(xiàn)No bean named 'xxxx' available]錯(cuò)誤的解決

    使用@Service注解出現(xiàn)No bean named 'xxxx'&

    這篇文章主要介紹了使用@Service注解出現(xiàn)No bean named 'xxxx' available]錯(cuò)誤的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • SpringBoot實(shí)現(xiàn)優(yōu)雅停機(jī)的流程步驟

    SpringBoot實(shí)現(xiàn)優(yōu)雅停機(jī)的流程步驟

    優(yōu)雅停機(jī)(Graceful Shutdown) 是指在服務(wù)器需要關(guān)閉或重啟時(shí),能夠先處理完當(dāng)前正在進(jìn)行的請(qǐng)求,然后再停止服務(wù)的操作,本文給大家介紹了SpringBoot實(shí)現(xiàn)優(yōu)雅停機(jī)的流程步驟,需要的朋友可以參考下
    2024-03-03
  • SpringBoot應(yīng)用快速部署到K8S的詳細(xì)教程

    SpringBoot應(yīng)用快速部署到K8S的詳細(xì)教程

    這篇文章主要介紹了SpringBoot應(yīng)用快速部署到K8S的詳細(xì)教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • Java線程同步的四種方式詳解

    Java線程同步的四種方式詳解

    這篇文章主要介紹了Java線程同步的四種方式詳解,需要的朋友可以參考下
    2023-02-02
  • 解決idea services窗口不見的一種特殊情況(小白采坑系列)

    解決idea services窗口不見的一種特殊情況(小白采坑系列)

    這篇文章主要介紹了解決idea services窗口不見的一種特殊情況(小白采坑系列),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-09-09
  • 解決mybatis執(zhí)行SQL語(yǔ)句部分參數(shù)返回NULL問題

    解決mybatis執(zhí)行SQL語(yǔ)句部分參數(shù)返回NULL問題

    這篇文章主要介紹了mybatis執(zhí)行SQL語(yǔ)句部分參數(shù)返回NULL問題,需要的的朋友參考下吧
    2017-06-06
  • 聊聊spring boot的WebFluxTagsProvider的使用

    聊聊spring boot的WebFluxTagsProvider的使用

    這篇文章主要介紹了聊聊spring boot的WebFluxTagsProvider的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • 淺談maven的jar包和war包區(qū)別 以及打包方法

    淺談maven的jar包和war包區(qū)別 以及打包方法

    下面小編就為大家分享一篇淺談maven的jar包和war包區(qū)別 以及打包方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助
    2017-11-11
  • SpringCloud斷路器Hystrix原理及用法解析

    SpringCloud斷路器Hystrix原理及用法解析

    這篇文章主要介紹了SpringCloud斷路器Hystrix原理及用法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01

最新評(píng)論