类构造函数,包括完全相同类的参数

时间:2015-03-08 19:48:25

标签: java constructor

就在最近,我看了java.lang.String类(http://www.docjar.com/html/api/java/lang/String.java.html)的源代码,我注意到使用的构造函数(或至少一个特定的构造函数)依赖于String类型的参数。怎么可能?假设以下代码

String s = new String("String!");

编译器如何执行此操作?在括号内传递一个字符串对象,对吧?但这需要以某种方式实例化(我猜是编译器?)?或者它是如何工作的?

我故意使用String对象而不是字符串文字的实例化,因为字符串文字在内存池上的分配方式不同,而不是字符串对象(堆)。

一切顺利,大卫

5 个答案:

答案 0 :(得分:1)

String类有多个构造函数。传入的实例可以使用不同的构造函数创建:

String string = new String();
new String(string);

您不需要了解字符串文字如何创建/重用对象,以了解String如何拥有构造函数String(String)

答案 1 :(得分:0)

java编译器将编译很多,然后进行一些检查。这是可能的,因为类类似于.obj或.o,稍后包含名称"链接"。

在提到的情况下,事实上对编译或运行时使用没有任何挑战。同样适用于非标准课程:一个人可以总是null

还有更多丑陋的事情要看:

public interface IFC {
    public static final IFC SOME_CONSTANT = new IMPL();
}

public class IMPL implements IFC {
}

关于那个例子。构造函数String(String)源于Java的开头。在C ++中,人们会将其称为复制构造函数。由于String是不可变的,因此它没有任何用途:s.equals(new String(s))所以它实际上是错误的样式。 (可能是为了禁用==而添加的。)

答案 2 :(得分:0)

  

编译器如何执行此操作?在括号内传递一个字符串对象,对吗?

不是真的。如果您查看String构造函数的API,它实际上会将参数作为char array接收。

  

String str = "abc";    相当于:

     

char data[] = {'a', 'b', 'c'};

     

String str = new String(data);

答案 3 :(得分:0)

这就是编译器在幕后所做的事情。 编译器可以例如将字符串文字("abc")转换为字符数组,并使用接受char[]的ctor。

答案 4 :(得分:0)

假设你有一个这样的程序:

public class Test {
    public static void main(String[] args) {
       String s = new String("String!");
       System.out.println(s);
    }
}

编译时,您将获得一个包含该程序字节码的.class文件。您可以通过调用javap -c -v -l -s Test.class来查看字节码(javap是JDK的一部分),它会打印出类似这样的内容:

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"

虽然这看起来很混乱(而且我对字节码的处理不是很深),我会尝试将其缩减为

常量池是包含“固定”信息的类文件的一部分,如方法签名或文本(是:字符串文本)。代码部分包含应该执行的实际代码,它使用对常量池的引用。

那么,文本"String!"如何实际加载?在ldc #3上的代码块中,索引#3的值被加载并放在堆栈中。 #3#19的引用,它是实际文本String!

然后,下一个字节码指令使用索引invokespecial执行#4,这是String构造函数,另一个String作为参数 - “复制构造函数”,如其他所述答案(<init>指的是构造函数,另一部分是参数类型和返回类型)。

虽然这可能总结了至少部分发生的事情,但一个细节仍然不明确:运行时如何将文字文本String!#19转换为 String对象

在玩了一些类似的代码及其字节码表示后,我只能做出有根据的猜测:运行时具有硬编码支持将字符串文字转换为String个对象(例如,以类似的方式代码{{1} }被分为long c = 42L;#2 = Long 42l,您可以通过修改示例进行测试,然后对其进行编译和反编译。)

更多链接: