This question already has answers here:
String concatenation with Null
(3 answers)
Closed 3 years ago.
Why does the following work? I would expect a NullPointerException to be thrown.
String s = null;
s = s + "hello";
System.out.println(s); // prints "nullhello"
Why must it work?
The JLS 5, Section 15.18.1.1 JLS 8 § 15.18.1 "String Concatenation Operator +", leading to JLS 8, § 5.1.11 "String Conversion", requires this operation to succeed without failure:
...Now only reference values need to be considered. If the reference is null, it is converted to the string "null" (four ASCII characters n, u, l, l). Otherwise, the conversion is performed as if by an invocation of the toString method of the referenced object with no arguments; but if the result of invoking the toString method is null, then the string "null" is used instead.
How does it work?
Let's look at the bytecode! The compiler takes your code:
String s = null;
s = s + "hello";
System.out.println(s); // prints "nullhello"
and compiles it into bytecode as if you had instead written this:
String s = null;
s = new StringBuilder(String.valueOf(s)).append("hello").toString();
System.out.println(s); // prints "nullhello"
(You can do so yourself by using javap -c)
The append methods of StringBuilder all handle null just fine. In this case because null is the first argument, String.valueOf() is invoked instead since StringBuilder does not have a constructor that takes any arbitrary reference type.
If you were to have done s = "hello" + s instead, the equivalent code would be:
s = new StringBuilder("hello").append(s).toString();
where in this case the append method takes the null and then delegates it to String.valueOf().
Note: String concatenation is actually one of the rare places where the compiler gets to decide which optimization(s) to perform. As such, the "exact equivalent" code may differ from compiler to compiler. This optimization is allowed by JLS, Section 15.18.1.2:
To increase the performance of repeated string concatenation, a Java compiler may use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.
The compiler I used to determine the "equivalent code" above was Eclipse's compiler, ecj.
See section 5.4 and 15.18 of the Java Language specification:
String conversion applies only to the
operands of the binary + operator when
one of the arguments is a String. In
this single special case, the other
argument to the + is converted to a
String, and a new String which is the
concatenation of the two strings is
the result of the +. String conversion
is specified in detail within the
description of the string
concatenation + operator.
and
If only one operand expression is of
type String, then string conversion is
performed on the other operand to
produce a string at run time. The
result is a reference to a String
object (newly created, unless the
expression is a compile-time constant
expression (§15.28))that is the
concatenation of the two operand
strings. The characters of the
left-hand operand precede the
characters of the right-hand operand
in the newly created string. If an
operand of type String is null, then
the string "null" is used instead of
that operand.
The second line is transformed to the following code:
s = (new StringBuilder()).append((String)null).append("hello").toString();
The append methods can handle null arguments.
You are not using the "null" and therefore you don't get the exception. If you want the NullPointer, just do
String s = null;
s = s.toString() + "hello";
And I think what you want to do is:
String s = "";
s = s + "hello";
This is behavior specified in the Java API's String.valueOf(Object) method. When you do concatenation, valueOf is used to get the String representation. There is a special case if the Object is null, in which case the string "null" is used.
public static String valueOf(Object obj)
Returns the string representation of the Object argument.
Parameters:
obj - an Object.
Returns:
if the argument is null, then a string equal to "null"; otherwise, the value of obj.toString() is returned.
Related
I am trying to concatenate two strings, one string with some value and another with empty.
Example:
String string1="Great"
String string2="";
and concatenating these two string with concat function and + operator
Example:
String cat=string1.concat(string2)
String operator=string1+string2
As per my understanding, while using empty string in concat function as the string2 is empty no new reference will be created. But while using + operator a new reference will be created in the string pool constant. But in the below code while using the + operator new reference is not created.
public class Main {
public static void main(String[] args) {
String string1="Great",string2="";
String cat=string1.concat(string2);
if(string1==cat)
{
System.out.println("Same");
}
else
{
System.out.println("Not same");
}
String operator=string1+string2;
if(operator==string1)
System.out.println("Same");
else
System.out.println("Not same");
}
}
Output:
string 1 :69066349
cat :69066349
Same
string1 :69066349
operator :69066349
Not same
From the above code, as it's using + operator, the reference for the variable : operator should refer to the new memory, but it's pointing to the string1 reference. Please explain the above code.
It is all in the documentation.
For String.concat, the javadoc states this:
If the length of the argument string is 0, then this String object is returned.
For the + operator, JLS 15.8.1 states:
The result of string concatenation is a reference to a String object that is the concatenation of the two operand strings. The characters of the left-hand operand precede the characters of the right-hand operand in the newly created string.
The String object is newly created (§12.5) unless the expression is a constant expression (§15.29).
As you can see, the results will be different for the case where the 2nd string has length zero and this is not a constant expression.
That is what happens in your example.
You also said:
But while using + operator a new reference will be created in the string pool constant.
This is not directly relevant to your question, but ... actually, no it won't be created there. It will create a reference to a regular (not interned) String object in the heap. (It would only be in the class file's constant pool ... and hence the string pool ... if it was a constant expression; see JLS 15.29)
Note that the string pool and the classfile constant pool are different things.
Can I add a couple of things:
You probably shouldn't be using String.concat. The + operator is more concise, and the JIT compiler should know how to optimize away the creation of unnecessary intermediate strings ... in the few cases where you might consider using concat for performance reasons.
It is a bad idea to exploit the fact that no new object is created so that you can use == rather than equals(Object). Your code will be fragile. Just use equals always for comparing String and the primitive wrapper types. It is simpler and safer.
In short, the fact that you are even asking this question suggests that you are going down a blind alley. Knowledge of this edge-case difference between concat and + is ... pointless ... unless you are planning to enter a quiz show for Java geeks.
public class potpie {
private int month;
private int day ;
private int year;
public potpie(int m,int d,int y) {
month=m;
day=d;
year=y;
System.out.printf("The constructor for this is"+this);
}
public String toString() {
return String.format("%d/ %d/ %d/", month,year,day);
}
}
class apples {
public static void main(String args[])
{
potpie x= new potpie(4,5,6);
}
}
The magic is in the + operator for Strings. When you add a String to an Object using +, the JVM calls the toString method on the Object, to work out what String to concatenate.
Also, this represents the instance on which the current method is being run, so by adding this to a String with the + operator, you get toString called on the current instance.
From the Java Language Specification
15.18.1. String Concatenation Operator +
If only one operand expression is of type String, then string conversion (§5.1.11) is performed on the other operand to produce a string at run time.
and further
5.1.11. String Conversion
Any type may be converted to type String by string conversion.
A value x of primitive type T is first converted to a reference value as if by giving it as an argument to an appropriate class instance creation expression (§15.9):
If T is boolean, then use new Boolean(x).
If T is char, then use new Character(x).
If T is byte, short, or int, then use new Integer(x).
If T is long, then use new Long(x).
If T is float, then use new Float(x).
If T is double, then use new Double(x).
This reference value is then converted to type String by string conversion.
Now only reference values need to be considered:
If the reference is null, it is converted to the string "null" (four ASCII characters n, u, l, l).
Otherwise, the conversion is performed as if by an invocation of the toString method of the referenced object with no arguments; but if the result of invoking the toString method is null, then the string "null" is used instead.
In your case, it's the final bullet point in section 5.1.11 that applies.
If you want to represent any object as a string, toString() method comes into existence.
The toString() method returns the string representation of the object.
If you print any object, java compiler internally invokes the toString() method on the object. So overriding the toString() method, returns the desired output, it can be the state of an object etc. depends on your implementation.
This really doesn't have anything to do with this. It's more about how + works with strings.
The + operator, when one of the operands is a string and the other is not, it will try to convert the other operand into a string. For example:
"My age is: " + 30
It will try to convert 30 into a string. This can be done by
"My age is: " + (new Integer(30)).toString()
The situation is exactly the same in your case. Your expression:
"The constructor for this is"+this
The second operand of + is not a string, so the compiler tries to convert this into a string. So toString is called:
"The constructor for this is"+this.toString()
toString is usually how you convert any reference type to a string.
Converting the value to String in java; There are multiple ways of doing it.
Just wanted to know what's the difference between each other in the following ways.
strValue.toString()
strValue+""
""+strValue
It depends on java version. Java 7 would act a bit smarter using StringBuilder + append().
Generally, you do not want unnecessary allocations. Use first one.
strValue.toString()
will return itself, because the toString() implementation of String (I'm guessing strValue is indeed of type String) returns this.
strValue+""
""+strValue
Will result in the same value (strValue) but won't invoke the toString() method
All Strings contain the same value, try it out:
String strValue = "Hello world"; // not null
String a = strValue.toString();
String b = strValue+"";
String c = ""+strValue;
Measuring its length give all the result 11, because adding an empty String to another one equals the original String itself:
System.out.println(a.length());
...
Try the equality between these Strings:
System.out.println(a.equals(b));
System.out.println(b.equals(c));
System.out.println(c.equals(a));
They are all true, because these Strings have the same value to be compared. All it in the case the strValue is not null.
One major difference is how null is handled.
If strValue is null, strValue.toString() will throw a NullPointerException, while the other two options will return the String "null".
Other differences may be observed if strValue is of a boxed numeric type, and you try to concatenate other numeric variables to it.
For example :
If
Integer a = 5;
Integer strValue = 6;
Then
a+strValue+""
would return
"11"
while
a+""+strValue
or
""+a+strValue
would return
"56"
This is very weird. The following is the code:
public static void main(String [] args) {
double db = 56.00;
String st = String.valueOf(db);
System.out.print(st+3);
}
The output that I get is
56.03
First, how come a String adding an int?Second, how is this possible that 56.00 + 3 is is 56.03?
You're performing string concatenation. The value of st is "56.0", and then you're performing a concatenation of that and the int 3, giving a result of "56.03".
The string concatenation + operator is described in JLS 15.18.1. It starts with:
If only one operand expression is of type String, then string conversion (§5.1.11) is performed on the other operand to produce a string at run time.
And JLS 5.1.11 includes:
A value x of primitive type T is first converted to a reference value as if by giving it as an argument to an appropriate class instance creation expression (§15.9):
[...] If T is byte, short, or int, then use new Integer(x).
[...] Otherwise, the conversion is performed as if by an invocation of the toString method of the referenced object with no arguments; but if the result of invoking the toString method is null, then the string "null" is used instead.
In other words, your program in this case is basically:
double db = 56.00;
String st = String.valueOf(db); // "56.0"
System.out.print(st + new Integer(3).toString()); // "56.0" + "3" = "56.03"
My question is in regard to the way Java handles String literals. It's quite clear from the Java Language Specs (JLS) that String literals are being implicitly interned - in other words, objects that are created in the String constant pool part of the heap, in contrast to the heap-based objects created when calling new String("whatever").
What doesn't seem to line up with what the JLS says is that when creating a new String using String concatenation with a casted constant String type, which should be considered as a constant String as per the JLS, apparently the JVM is creating a new String object rather than interning it implicitly. I appreciate any explanation about this particular behaviour and whether or not this is a platform-specific behaviour. I am running on a Mac OSX Snow Leopard.
public class Test
{
public static void main(String args[])
{
/*
Create a String object on the String constant pool
using a String literal
*/
String hello = "hello";
final String lo = "lo"; // this will be created in the String pool as well
/*
Compare the hello variable to a String constant expression
, that should cause the JVM to implicitly call String.intern()
*/
System.out.println(hello == ("hel" + lo));// This should print true
/*
Here we need to create a String by casting an Object back
into a String, this will be used later to create a constant
expression to be compared with the hello variable
*/
Object object = "lo";
final String stringObject = (String) object;// as per the JLS, casted String types can be used to form constant expressions
/*
Compare with the hello variable
*/
System.out.println(hello == "hel" + stringObject);// This should print true, but it doesn't :(
}
}
Casting to Object is not allowed in a compile time constant expression. The only casts permitted are to String and primitives. JLS (Java SE 7 edition) section 15.28:
> - Casts to primitive types and casts to type String
(There's actually a second reason. object isn't final so cannot possibly by considered a constant variable. "A variable of primitive type or type String, that is final and initialized with a compile-time constant expression (§15.28), is called a constant variable." -- section 4.12.4.)
Seems like because you reference an object here final String stringObject = (String) object;, this is no longer a 'compile-time' constant, but a 'run-time' constant. The first example from here eludes to it with the part:
String s = "lo";
String str7 = "Hel"+ s;
String str8 = "He" + "llo";
System.out.println("str7 is computed at runtime.");
System.out.println("str8 is created by using string constant expression.");
System.out.println(" str7 == str8 is " + (str7 == str8));
System.out.println(" str7.equals(str8) is " + str7.equals(str8));
The string str7 is computed at runtime, because it references another string that is not a literal, so by that logic I assume despite that face that you make stringObject final, it still references an object, so cannot be computed at compile time.
And from the java lang spec here, it states:
"The string concatenation operator + (§15.18.1) implicitly creates a new String object when the result is not a compile-time constant expression (§15.28). "
I cannot find any examples where a cast can be used, except, for this terrible, terrible example:
System.out.println(hello == "hel" + ( String ) "lo");
Which hardly has any logical use, but maybe the part about a string cast was included because of the above case.