[JAVA] Some basic exploration of using static and String in objects

foreword

When discussing the JAVA final exam questions with my classmates, I am thinking about the use of static and String in objects, and I hereby record it, and I wish friends who do not have objects newto come out by themselves!

Then let's take a look at the original question in the test paper first;  

original title

There are mainly two classes MyClass.javaand TestMyClass.java, the part of filling in the code is skipped directly, and then the result is output to see if you can get it all correct. The specific codes of the two classes are as follows:

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());
    }
}
复制代码

operation result:

Nice:Nice:Nice
true
false
true
2
复制代码

If you got all the answers right, then congratulations, the foundation is very good; don't be discouraged if you answered incorrectly, then listen to me, and build a solid foundation;  

static

If you want to do a good job, you must first sharpen your tools. Before starting the analysis, let's review some knowledge about static;

Introduction

Static means "global" or "static", which is used to modify member variables and member methods, and can also form static static code blocks, but there is no concept of global variables in the Java language;

The member variables and member methods modified by static are independent of any object of the class, that is, it does not depend on a specific instance of the class and is shared by all instances of the class;

As long as the class is loaded, the Java virtual machine can locate them in the method area of ​​the runtime data area according to the class name, so the static object can be accessed before any of its objects are created, without referencing any objects;

Static member variables and member methods decorated with public are essentially global variables and global methods. When an object of its class is declared, a copy of the static variable is not generated, but all instances of the class share the same static variable;

A static variable can be modified with private, which means that the variable can be used in the static code block of the class or in other static member methods of the class, but cannot be directly referenced by the class name in other classes, which is very important;

In fact, you need to understand that private is the access limit, static means that it can be used without instantiation, so it is easier to understand, and the effect of adding other access keywords in front of static is analogous.

Static modified member variables and member methods are conventionally called static variables and static methods, and can be accessed directly through the class name. The access syntax is:

类名.静态方法名(参数列表…)
复制代码

use

After reviewing the static related knowledge, let's take a look at the use in the topic;

// 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);
复制代码

First, the member variable is modified with static message, and then through the breakpoint debugging, we can know that the two objects mcl1and mcl2are assigned to two different addresses;

When debugging down, it is found mc1.messagethat the values ​​of mc2.messagethe MyClass.messagethree member variables are the same, and they are all from Great → Excellent → Nice, which is just reviewed, the member variables modified by static become shared variables and are shared by all instances of the class;

Next, let's do another test to verify:

//修改前
private int count;

//修改后
private static int count;
复制代码

It can be found that we only operate on the mcl1object , but mcl2the member variables of the countare also changed. This is because in the MyClassclass, the member variable countis modified by static and has become the shared variable of the class, but any object of the class, both access the same countvariable ;

Of course, it can also be verified by an ID code:

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
复制代码

So the System.out.println(mc1.message+":"+mc2.message+":"+MyClass.message);output is Nice:Nice:Nice;

Next, let's talk about some little knowledge about String;  

String

Regarding String, what is used here is not comprehensive;

== and equals()

Let's talk about the comparison of String first. There are two common comparisons, namely ==and equals();

Among them, ==the comparison is whether the addresses of the two strings are equal (the same address), and the equals()method compares whether the contents of the two string objects are the same (of course, if the two strings refer to the same address, using the equals()comparison also returns true);

Here I have to mention the second knowledge point, the difference between String constant and non-constant;  

constant and non-constant

So what is a constant and what is a non-const? A simple understanding is that String name = "sid10t."this is a constant, which belongs nameto assigning to and directly stored in the constant pool, and String name = new String("sid10t.")this is a non-const, because re-creating an object will convert the string sid10t.Store in the constant pool, and then create an object in Heap to point to name;

So why mention this here? Of course, because they are quite different;

There is a specification in Chapter 3.10.5 of the Java Language Specification (JavaSE 1.8 version). All Java language compilation and runtime environment implementations must be implemented according to this specification. There is such a sentence:

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.

The general meaning is that all string constants with the same content must refer to the same string object, in other words, the memory address is the same;

Because the string whose value is constant, will be limited to share the same object through the String.intern()function ;

The intern()function , or you can refer to the description String (Java Platform SE 8) ;

Back to the topic, take a look at this code in the language specification:

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"; }
复制代码

and a class in another package:

package other;
public class Other { public static String hello = "Hello"; }
复制代码

operation result:

true true true true false true
复制代码

in conclusion:

  • Strings in the same class in the same package represent references to the same String object;
  • Strings in different classes in the same package represent references to the same String object;
  • Strings in different classes in different packages also represent references to the same String object;
  • Strings evaluated by constant expressions are evaluated at compile time and then treated as literals;
  • Strings computed by concatenation at runtime are newly created and therefore different;
  • Explicitly embedding a computed string has the same result as any existing literal string with the same content;

If the understanding of the conclusion is not very deep, then look at the following explanation:


System.out.print((hello == "Hello") + " ");
System.out.print((Other.hello == hello) + " ");
System.out.print((other.Other.hello == hello) + " ");
复制代码

"Hello" and "lo" are string constants, which are determined at compile time . First check whether the string constant pool contains "Hello" and "lo", if not, add "Hello" and "lo" to the character In the string constant pool, and directly point to them, so helloand lodirectly point to "Hello" and "lo" of the string constant pool respectively, that is hello, lothe addresses pointed to are "Hello" and "lo" in the constant pool respectively, so the first An output is helloactually "Hello", so "Hello" == "Hello"the truefirst three outputs are the same;


System.out.print((hello == ("Hel"+"lo")) + " ");
复制代码

"Hel" and "lo" are both string constants. When a string is formed by concatenating multiple string constants , it must also be a string constant itself. It will be optimized into "Hello" by the compiler, because "Hello" is in the constant pool, so the output is true;


System.out.print((hello == ("Hel"+lo)) + " ");
System.out.println(hello == ("Hel"+lo).intern());
复制代码

JVM For string references, since there are string references in the +connection of strings, and the value of the reference cannot be determined during program compilation, that is, it will not be stored in the constant pool "Hel"+lowithout executing the intern()method . "Hel"+lo, but "Hel" will be stored in the constant pool, so output one is trueand one is false;


intern()

String.intern()It is a Native method. Its function is to return a reference to the string in the string constant pool if the string constant pool already contains a string equal to this String object, otherwise the reference address of the current String object (in the heap) Add to the string constant pool and return.  

JAVA source code

/*
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 source code

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;  
}  
复制代码

Its general implementation structure is: JAVA uses jni to call the method of StringTable implemented by C++. The internmethod of StringTable internis similar to the implementation of HashMap in Java, but it cannot be automatically expanded, and the default size is 1009.

It should be noted that the String Pool of String is a fixed-size Hashtable, and the default size is 1009. If a lot of Strings are put into the String Pool, it will cause serious Hash conflicts, resulting in a very long linked list and a long linked list. The immediate impact is that when calling String.intern, the performance will drop significantly (because it needs to be searched one by one).

It StringTableis is the length of 1009, so if there are too many strings in the constant pool, the efficiency will drop rapidly.

In JDK7, the length of StringTable can be specified by a parameter: -XX:StringTableSize=99991;  

use

In versions before JDK1.7, when calling this method, it will go to the constant pool to check whether the constant already exists. If it already exists, it will directly return the address value of the constant in the constant pool. Create one in the constant pool and return its address value.

But in JDK1.7 and later versions, the constant pool moved from the perm area to the heap area. intern()When it is detected that the constant does not exist in the constant pool, the object will not be created directly in the constant pool, but the reference of the object in the heap will be directly stored in the constant pool to reduce memory overhead.

Look at this code:

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);
}
复制代码

Both are falsebefore JDK7, and the output after JDK7 is false, respectively true;

Next, analyze according to JDK7,


Let's look at part1 first:

String s1 = new String("sid10t.");This line of code generates two final objects: one is the string constant in the constant pool sid10t., and the other is the object pointed to by the s1 reference in the heap;

Then the second line s1.intern();returns the string constant sid10t.in the constant pool, because the constant already exists in the constant pool, so it can be returned directly here. Therefore, in this situation of part1, this sentence can be written or not . Write , it has no effect on the output result;

So the final output must be falsethat one address is in the heap and one is in the constant pool;


Let's take a look at the part2 part:

String s3 = new String("Hello ") + new String("World!");This line of code generates three final objects: two objects in the constant pool Hello , World!, and an object pointed to by an s3 reference in the heap;

Then there is the second line s3.intern();. Since there is no string constant in the current constant pool Hello World!, it will directly store the reference address of s3 in the heap instead of copying it;

At this time, there is String s4 = "Hello World!";already a Hello World!constant , which is the reference address of s3, so the value of s4 is the reference address of s3, so the output is true;


According to the above analysis, we slightly adjust the code of part2 as follows:

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

The output falseis , but if it is s3.intern() == s4, the output is true;

Presumably you should understand!  

postscript

Helping others is also helping yourself, so you should always help others. In general, you have gained a lot. Not only did you consolidate your knowledge, but you also discovered and understood details that you didn’t know before. as a teacher;

Guess you like

Origin blog.csdn.net/m0_71777195/article/details/127050560