Reputation: 501
Just recently I looket at the source code of the java.lang.String class (http://www.docjar.com/html/api/java/lang/String.java.html) and I noticed that the constructor used (or at least one specific constructor) relies on a parameter of type String. How is that possible? Assuming following code
String s = new String("String!");
How does the compiler do this? Inside the parenthesis there is passed a string object, right? But this needs to be somehow instantiated (by the compiler I guess?) as well? Or how does that work?
I on purpose used the instantiation of a String object instead of a string literal since string literals are allocated differently on a memory pool instead of string objects (heap).
All the best, David
Upvotes: 0
Views: 108
Reputation: 15146
The String
class has multiple constructors. The instance being passed in can be created using a different constructor:
String string = new String();
new String(string);
You don't need to understand how string literals create/reuse objects to understand how String
can have the constructor String(String)
Upvotes: 1
Reputation: 2319
Let's assume you have a program like this:
public class Test {
public static void main(String[] args) {
String s = new String("String!");
System.out.println(s);
}
}
When compiled, you'll get a .class file containing the bytecode for this program. You can view the bytecode by invoking javap -c -v -l -s Test.class
(javap
is part of the JDK), and it'll print out something like this:
Classfile /C:/Test.class
Last modified 08.03.2015; size 454 bytes
MD5 checksum 0649bf262c557480c276e268762a1dd5
Compiled from "Test.java"
public class Test
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #8.#17 // java/lang/Object."<init>":()V
#2 = Class #18 // java/lang/String
#3 = String #19 // String!
#4 = Methodref #2.#20 // java/lang/String."<init>":(Ljava/lang/String;)V
#5 = Fieldref #21.#22 // java/lang/System.out:Ljava/io/PrintStream;
#6 = Methodref #23.#24 // java/io/PrintStream.println:(Ljava/lang/String;)V
#7 = Class #25 // Test
#8 = Class #26 // java/lang/Object
#9 = Utf8 <init>
#10 = Utf8 ()V
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 main
#14 = Utf8 ([Ljava/lang/String;)V
#15 = Utf8 SourceFile
#16 = Utf8 Test.java
#17 = NameAndType #9:#10 // "<init>":()V
#18 = Utf8 java/lang/String
#19 = Utf8 String!
#20 = NameAndType #9:#27 // "<init>":(Ljava/lang/String;)V
#21 = Class #28 // java/lang/System
#22 = NameAndType #29:#30 // out:Ljava/io/PrintStream;
#23 = Class #31 // java/io/PrintStream
#24 = NameAndType #32:#27 // println:(Ljava/lang/String;)V
#25 = Utf8 Test
#26 = Utf8 java/lang/Object
#27 = Utf8 (Ljava/lang/String;)V
#28 = Utf8 java/lang/System
#29 = Utf8 out
#30 = Utf8 Ljava/io/PrintStream;
#31 = Utf8 java/io/PrintStream
#32 = Utf8 println
{
public Test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=2, args_size=1
0: new #2 // class java/lang/String
3: dup
4: ldc #3 // String String!
6: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V
9: astore_1
10: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
13: aload_1
14: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
17: return
LineNumberTable:
line 3: 0
line 4: 10
line 5: 17
}
SourceFile: "Test.java"
While this looks very confusing (and I am not very deep into bytecode), I'll try to cut it down to this:
The Constant Pool is the part of the class file containing "fixed" Information like method signatures or texts (yes: String texts). The code section contains the actual code that should be executed, and it uses references to the Constant Pool.
So, how is the text "String!"
actually loaded? In the code block on ldc #3
the value with index #3
is loaded and put on the stack. #3
is the reference to #19
which is the actual text String!
.
The next bytecode instruction then does an invokespecial
using index #4
which is the String
constructor with another String
as parameter - a "copy constructor" as described in other answers (<init>
refers to the constructor, and the other part is the parameter type and return type).
While this might sum up at least part of what happens, one detail still remains somehow unclear: how does the runtime convert the literal text String!
from #19
into a String
object?
After playing around with some similar codes and their bytecode representations I can only make an educated guess: the runtime has hardcoded support for converting string literals into String
objects (e.g. in a similar way the code long c = 42L;
gets splitted into #2 = Long 42l
and ldc2_w #2
, you can test that by modifying the example, and then compile and decompile it).
Further links:
Upvotes: 0
Reputation: 501
That's what the compiler does under the hood.
The compiler could e.g. convert the string literal ("abc"
) to a character array and use the ctor which accepts the char[]
.
Upvotes: 0
Reputation: 17454
How does the compiler do this? Inside the parenthesis there is passed a string object, right?
Not really. If you look at the API for String constructors, it actually receive the argument as char array
.
String str = "abc";
is equivalent to:
char data[] = {'a', 'b', 'c'};
String str = new String(data);
Upvotes: 0
Reputation: 109547
The java compiler will compile much and afterwards do some checks. This is possible as a class is like a .obj or .o, containing names later on "linked".
In the case mentioned that in fact poses no challenge to the compilation, or run-time use. Also for non-standard classes: one could alway pas null
.
There are more uglier things to be seen:
public interface IFC {
public static final IFC SOME_CONSTANT = new IMPL();
}
public class IMPL implements IFC {
}
About that example. The constructor String(String)
stems from the beginning of Java. In C++ one would call it a copy constructor. As String is immutable, it servers no purpose: s.equals(new String(s))
so it is really bad style. (Maybe it was added for disabling ==
.)
Upvotes: 0