“someObject.new”在Java中有什么作用?

2022-08-31 10:22:49

在Java中,我刚刚发现以下代码是合法的:

KnockKnockServer newServer = new KnockKnockServer();                    
KnockKnockServer.receiver receive = newServer.new receiver(clientSocket);

仅供参考,接收方只是具有以下签名的帮助程序类:

public class receiver extends Thread {  /* code_inside */  }

我以前从未见过这种符号。这是如何运作的?有没有办法更常规地编码?XYZ.new


答案 1

这是从包含类体外部实例化非静态内部类的方法,如 Oracle 文档中所述。

每个内部类实例都与其包含类的实例相关联。当您从其包含类中的内部类时,默认情况下,它使用容器的实例:newthis

public class Foo {
  int val;
  public Foo(int v) { val = v; }

  class Bar {
    public void printVal() {
      // this is the val belonging to our containing instance
      System.out.println(val);
    }
  }

  public Bar createBar() {
    return new Bar(); // equivalent of this.new Bar()
  }
}

但是,如果要在 Foo 外部创建 Bar 的实例,或者将新实例与包含实例相关联,则必须使用前缀表示法。this

Foo f = new Foo(5);
Foo.Bar b = f.new Bar();
b.printVal(); // prints 5

答案 2

看看这个例子:

public class Test {

    class TestInner{

    }

    public TestInner method(){
        return new TestInner();
    }

    public static void main(String[] args) throws Exception{
        Test t = new Test();
        Test.TestInner ti = t.new TestInner();
    }
}

使用javap,我们可以查看为此代码生成的指令

主要方法:

public static void main(java.lang.String[])   throws java.lang.Exception;
  Code:
   0:   new     #2; //class Test
   3:   dup
   4:   invokespecial   #3; //Method "<init>":()V
   7:   astore_1
   8:   new     #4; //class Test$TestInner
   11:  dup
   12:  aload_1
   13:  dup
   14:  invokevirtual   #5; //Method java/lang/Object.getClass:()Ljava/lang/Class;
   17:  pop
   18:  invokespecial   #6; //Method Test$TestInner."<init>":(LTest;)V
   21:  astore_2
   22:  return
}

内部类构造函数:

Test$TestInner(Test);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield        #1; //Field this$0:LTest;
   5:   aload_0
   6:   invokespecial   #2; //Method java/lang/Object."<init>":()V
   9:   return

}

一切都很简单 - 当调用 TestInner 构造函数时,java 将 Test 实例作为第一个参数 main:12 传递。不看那个TestInner应该有一个没有参数的构造函数。TestInner反过来只是保存对父对象Test$TestInner:2的引用。从实例方法调用内部类构造函数时,将自动传递对父对象的引用,因此您不必指定它。实际上,它每次都通过,但是当从外部调用时,它应该显式传递。

t.new TestInner();- 只是向 TestInner 构造函数指定第一个隐藏参数的一种方法,而不是类型

方法() 等于:

public TestInner method(){
    return this.new TestInner();
}

TestInner 等于:

class TestInner{
    private Test this$0;

    TestInner(Test parent){
        this.this$0 = parent;
    }
}

推荐