This is the 23rd day of my participation in the August More Text Challenge
The problem summary
Static constants can be recompiled to determine literals, but constants are not always determined at compile time and can also be determined at run time, so Java has constant optimization mechanisms for certain situations.
Constant optimization mechanism
- To assign a value to a variable, if there is a constant expression to the right of the equals sign and there is no variable, the result of the expression is evaluated at compile time.
- Then determine whether the result of the expression is in the range represented by the type on the left.
- If yes, the assignment succeeds; if no, the assignment fails.
Note that if once a variable is involved in the expression, there is no constant optimization at compile time.
Combined with the problem, we can roughly guess that if constants can be determined at compile time there will be optimizations, and if not there will be none.
Let’s take a look at this mechanism in detail. There are two main aspects to constant pool optimization in Java
Constant optimization for byte/short/char
First post a Java eight data types size range table for reference:
Take the following program for example
byte b1 = 1 + 2;
System.out.println(b1);
// Output result 3
Copy the code
Explanation of running results:
1 and 2 are constants. Java has a constant optimization mechanism, which allows constant results to be determined at compile time, so b1 is assigned to the results of 1 and 2 directly. (Same as assigning 3 directly)
Let’s do it another way. Let’s change the constant on the right-hand side to a variable
byte b1 = 3;
byte b2 = 4;
byte b3 = b1 + b2;
System.out.println(b3); // The program reported an error
Copy the code
Type mismatch: cannot convert int to byte
Explain the reasons from two aspects:
-
The operation between byte and byte (or short char) promotes int. The sum of two ints is also an int
-
B1 and B2 are two variables, variables store changes, and at compile time you can’t tell what the values are, and you can add them up beyond the value of byte, which is why there is no constant optimization at compile time once a variable is involved in an expression.
Let’s try adding final to the variable and changing it back to constant, and see what happens
final byte b1 = 1;
final byte b2 = 2;
byte b3 = b1 + b2;
System.out.println(b3);
Copy the code
It is found that the program is running properly and the output is 3, so we know that the constant optimization mechanism must be for constants.
Now let’s look at another program
byte b1 = 127 + 2;
System.out.println(b4);
Copy the code
Error: failed to convert from int to byte. The value of byte ranges from -128 to 127. It is obvious whether the result of the right-hand expression is in the range indicated by the left-hand type, which is why this error occurs.
In some cases, large data types (int) can be assigned to small data types (byte, shor, char), and only certain int can be assigned to byte/short/char. Other basic data types cannot be assigned, as shown in the following figure.
int num1 = 10;
final int num2 = 10;
byte var1 = num1 + 20; // There is a variable, compilation error
byte var2 = num2 + 20; // The compiler passes
Copy the code
This is also part of the constant optimization mechanism
So let’s summarize the byte/short/char constant optimization mechanisms here
- Determine if the value is constant, and then see if it is in the range of values for that data type
- Int (short, char, short); int (short, short); int (short, short, char); This means that only byte, short, and char can use constant optimization
int a = 1;
int b = 2;
int c = a + b;
System.out.println(c);
Copy the code
Expand (easy point) :
byte var = 10;
var = var + 20; // Compiling error, variable in operation
var+ =20; Var = (short) (var + 20); Instead of constant optimization, type conversions are performed
Copy the code
String optimization for the compiler
String s1 = "abc";
String s2 = "a"+"b"+"c";
System.out.println(s1 == s2);
Copy the code
-
What is the result of this output? One would think that “a “+” b “+” c” would generate a new object “ABC”, but this object is different from String s2 = “ABC”, (a == b) is a comparison object reference, so it is not equal, the result is false.
-
If so, you’re familiar with Java strings, but you’re not familiar with Java constant pool optimization.
This code correctly outputs true!!
So why? Here’s why:
String s2 = “a” + “b” + “c”; The compiler takes this “a” + “b” + “c” as a constant expression and optimizes it at compile time by taking the result” ABC “directly. Instead of creating a new object, the compiler retrieves a preexisting” ABC “object from the JVM string constant pool. So a,b have references to the same string, both references are equal, and the result is true.
This means that by optimizing first, the code is simplified to
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);
Copy the code
Based on the JVM’s handling of strings, the conclusion is true.
What String + expressions are considered constant expressions by the compiler?
String b = "a" + "b";
Copy the code
This String + String is officially OK, but what about String + primitive?
String a = "a1";
String b = "a" + 1;
System.out.println((a == b)); //result = true
Copy the code
String a = "atrue";
String b = "a" + true;
System.out.println((a == b)); //result = true
Copy the code
String a = "a3.4";
String b = "a" + 3.4;
System.out.println((a == b)); //result = true
Copy the code
As you can see, the compiler optimizes String + primitives as if they were constant expressions.
Now that we’re done with constants, let’s try variables instead
String s1 = "ab";
String s2 = "abc";
String s3 = s1 + "c"; System.out.println(s3 == s2); The output is zerofalse
Copy the code
Here we can see that constant optimization is only for constants and cannot be optimized if there are variables
Operation principle
-
String s3 = s1 + “c”; In this sentence, you create a StringBuffer object in a StringBuffer buffer and add the two.
-
If the address of s3 is different from that of S2, false will be printed. If the address of s3 is different from that of S2, false will be printed.
We can also extend this by changing s1 with the final modifier to a constant
final String s1 = "ab";
String s2 = "abc";
String s3 = s1 + "c";
System.out.println(s2 == s3);
Copy the code
Constant optimization is possible for constants that enter the constant pool
Go a little further in and observe the procedure
private static String getS(a) {
return "b";
}
String s1 = "abc";
String s2 = "a"+getS();
System.out.println((s1 == s2));
Copy the code
The result, again unexpected, is false
Operation principle
S2 = “a” + getS(); s2 = “a” + getS(); s2 = “a” + getS(); s2 = “a” + getS(); S2 and s1 refer to two different memory addresses, so the comparison result of == is false.
It seems that the so-called “object” of String cannot be regarded as an ordinary object at all. Java treats String almost as a basic type, optimizing as much as possible.
So let’s summarize all of this with an example
public static void main(String[] arge) {
/ / 1
String str1 = new String("1234");
String str2 = new String("1234");
System.out.println("The new String () = =" + (str1 == str2));
/ / 2
String str3 = "1234";
String str4 = "1234";
System.out.println("Constant string == :" + (str3 == str4));
/ / 3
String str5 = "1234";
String str6 = "12" + "34";
System.out.println("Constant expression == :" + (str5 == str6));
/ / 4
String str7 = "1234";
String str8 = "12";
String str9 = str8 + "34";
System.out.println("String and variable add expression == :" + (str7 == str9));
/ / 5
final String val = "34";
String str10 = "1234";
String str11 = "12" + val;
System.out.println("String plus constant expression == :" + (str10 == str11));
/ / 6
String str12 = "1234";
String str13 = "12" + 34;
System.out.println("Expression for adding strings and numbers == :" + (str12 == str13));
/ / 7
String str14 = "12true";
String str15 = "12" + true;
System.out.println("String and Boolen add expression == :" + (str14 == str15));
/ / 8
String str16 = "1234";
String str17 = "12" + getVal();
System.out.println("String and function constants add expression == :" + (str16 == str17));
}
private static String getVal(a)
{
return "34";
}
Copy the code
Run output:
newString () = = :falseConstant string == :trueConstant expression == :trueString and variable add expression == :falseString and constant expression == :trueString and number add expression == :trueString and Boolen add expression == :trueString and function constants add expression == :false
Copy the code
Code analysis:
In Java, String is a reference type; == is a relational operator that compares two reference types based on whether they refer to the same memory address.
-
(1) String is a reference type, str1 and str2 are newly instantiated objects, respectively pointing to different memory addresses. For example 1, the value of == is false.
-
(2) For the second example, when the compiler compiles the code, it treats “1234” as a constant, stores it in the JVM’s constant pool, and compiles String str3= “1234”; String str4= “1234”; The compiler looks for constants in the constant pool with the same value, and assigns the existing constants to STR4. The result is that str3 and STR4 both point to constants in the constant pool.
-
(3) In the third example, the compiler finds that it can calculate the value of “12” + “34”, which is a constant, as in the second example, and ends up pointing to the same memory address as str5 and STR6. So the == comparison is true;
-
String str9 = str8 + “34”; This sentence, the value of STR9 is determined at run time by creating a StringBuffer object in the StringBuffer buffer and adding the two. If the address of s3 is different from that of S2, false will be printed. If the address of s3 is different from that of S2, false will be printed.
-
== == == == == == == == == == == == == == == == =
-
String str17= “12” + getVal(); String str17= “12” + getVal(); Str13 and STR1 point to two different memory addresses, so the comparison result is false;
To summarize
The Java language provides special support for the string concatenation operator (+) and for converting other objects to strings. String concatenation is implemented through the StringBuilder (or StringBuffer) class and its Append method. String conversions are implemented through methods toString (JDK1.8 API documentation). The toString method returns a String, so it returns a String object. Because of the immutability of strings, it is much less efficient to operate on them, but the “+” operator is optimized by the compiler so that, in general terms, you can get the final String if you can get the final String directly at compile time, so as to avoid the waste of creating objects in the middle.
String str = "a" + "b" + "c"; // directly equivalent to STR = "ABC ";
// This explains all the cases where it is true above
Copy the code
If the final string cannot be computed directly, as in example 4 above, str17 obviously requires a call to the function to compute the result, and the result must be determined at runtime, then memory must be cleared to create a new object. Specifically through the method described in yellow font