new Test()和new Test() { }的区别

33

这两种实例化一个类的方式有何不同:

Test t1=new Test();
Test t2=new Test(){ };
当我尝试以下代码时,我发现两个对象都可以访问方法foo(),但是t2无法访问变量x(变量x无法解析)。
public class Test
{ 
    int x=0;
    public void foo(){ }

    public static void main (String args[])
    {
        Test t1=new Test();
        Test t2=new Test(){ };
        t1.x=10;
        t2.x=20;
        t1.foo();
        t2.foo();
        System.out.println(t1.x+" "t2.x);
    }
}

3
除了第二种方式可以让你在不需要创建专门的子类的情况下覆盖父类中的方法之外,我认为两种方式都没有区别。 - MadProgrammer
2
如果你将t1.x+" "t2.x转换为t1.x+" "+t2.x,使其可编译,那么它就可以成功编译。 - JB Nizet
1
@Ankur Shanbhag 这是无效的编辑。 - Suresh Atta
2
@AnkurShanbhag 只是分享一下。在我早期编辑的时候,我甚至也收到了Mod的警告 ;) - Suresh Atta
2
此外,还可以说第二种方法不仅仅是“实例化新对象的方式”。它是特定于Java的,即您无法在C#中执行此操作。 - Dims
显示剩余3条评论
7个回答

62

Test t2=new Test();将创建Test类的对象。

但是Test t2=new Test(){ };将创建Test子类的对象(即在此情况下是匿名内部类)。

您可以在那里提供任何方法的实现。

Test t2=new Test(){ 
public void foo(){ System.out.println("This is foo");}
};
当从对象t2调用foo()方法时,它将打印This is foo。 补充 你的代码中的编译时错误是由于缺少连接运算符。
System.out.println(t1.x+" "+t2.x);
                          ###

2
@eatSleepCode 嗯,t2 instanceof Test会返回true,但这并不意味着new Test() { }创建了一个Test对象。它并没有。子类对象始终是其所有超类的instanceof - Rohit Jain
4
不,完全不是这样。尝试编译这个类,你会看到生成了两个类文件。其中一个名为Test$1 - Rohit Jain
1
你可能想阅读一下我在 这里这里这里 的回答。 - Rohit Jain
1
你想将t2.getClass()的输出添加到你的答案中吗?这样可能会更清晰(而不是关于生成了两个.class文件的注释)。 - ashes999
1
@eatSleepCode 为了完整回答,您可能需要解释为什么子类无法访问 x,以及如何使其可访问。 - hopia
显示剩余4条评论

16

这两个引用的运行时类型是不同的。尝试:

System.out.println(t1.getClass());  // class Test
System.out.println(t2.getClass());  // class Test$1

你会看到不同的输出。原因是new Test() { }表达式创建了一个Test匿名子类的实例。所以,Test$1Test的子类。

现在,你得到错误的原因是因为你缺少一个+符号:

System.out.println(t1.x + " " + t2.x);
                              ^

您可以在此篇文章此篇文章中找到更多详细信息。


如果 x 是私有的,那么 t2 就无法合法地看到它了? - Cruncher
1
@Cruncher 是的,它不会。在当前代码中,由于main方法在类本身中,似乎它能够访问,但如果你将它移动到类外面,既t1t2都无法访问x - Rohit Jain
你说得对。在这种情况下,我认为提到 x 具有默认修饰符这一事实并不相关。对于任何修饰符,t1t2 都可以看到它,或者都不能看到。 - Cruncher

5
test t1=new test();

这将创建 test 类的一个新实例。

test t2=new test(){ };  

这是一个匿名内部类,它继承了test类。


5
Test t1=new Test();

在这里,您正在创建一个 Test 类的实例并将其分配给 t1
Test t2=new Test(){ };

在这里,你创建了一个匿名的Test子类,并将其实例化并分配给t2

而且,在接下来的一行中,你犯了一个错误,已经进行了更正,你错过了+

System.out.println(t1.x + " " + t2.x);

4

a)

 Test t1=new Test();

通过这样做,您可以通过调用默认构造函数创建Test类的对象。
b)
Test t2=new Test(){ };

通过这样做,您正在创建一个扩展Test类的类的对象,该类没有名称,因此被称为“匿名内部类”

     Test t2=new Test(){ 
// this is the body of the anonymous(un-named) class 
//you can overide the method foo() here
// you can write more methods here but you will not be able to call them 
// for example
public void doSomething(){}
};

doSomething()方法不能在外部访问,因为t2(指向扩展Test匿名内部类的对象的引用或指针)仅理解foo()方法,因为它是父类的引用。

只有通过以下方式才能调用doSomething()方法:

  Test t2=   new Test(){
            public void foo()
            {
              doSomething();


            }
            public void doSomething(){
                  System.out.println("Do Something");
            }

        };

即在foo()中明确调用doSomething(),并且foo()在外部可访问。
t2.foo();

注意:请正确书写类的名称,类的第一个字母必须大写,例如:
public class Test{}

当你开始编写大量代码时,将有助于你和其他人,因为这会使你的代码易读。


我知道类名总是以大写字母开头。这只是为了测试两种方法之间的区别。无论如何,感谢您详细的回答。 - Crusaderpyro

3

您在下面的代码中缺少+运算符,请尝试使用以下代码:

System.out.println(t1.x+" "t2.x);

使用这个

System.out.println(t1.x+" "+t2.x);

2
Test t2=new Test();` 

将创建Test类的对象。

Test t2=new Test(){ };

将创建一个test子类的对象(即在此情况下为匿名内部类)。
Test t2=new Test(){ 
public void foo(){ System.out.println("foo");}
};

当从对象t2调用foo()方法时,它将打印"foo"。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接