DeBe
DeBe

Reputation: 501

Class constructor including parameter of that exact same class

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

Answers (5)

Vince
Vince

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

jCoder
jCoder

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

tomse
tomse

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

user3437460
user3437460

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

Joop Eggen
Joop Eggen

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

Related Questions