在Java中,你可以在一个文件中定义多个顶级类,只要其中最多有一个是公共的(参见JLS§7.6)。请看下面的例子。

这种技术是否有一个整齐的名称(类似于内部的、嵌套的、匿名的)? JLS表示,系统可能会强制限制这些二级类不能被包的其他编译单元中的代码引用,例如,它们不能被视为包私有。这真的是Java实现之间的变化吗?

例如,PublicClass.java:

package com.example.multiple;

public class PublicClass {
    PrivateImpl impl = new PrivateImpl();
}

class PrivateImpl {
    int implementationData;
}

我建议将这种技术(包括在一个源文件中包含多个顶级类)命名为“mess”。严肃地说,我不认为这是个好主意——在这种情况下我会使用嵌套类型。然后仍然很容易预测它在哪个源文件中。不过,我不认为这种方法有一个官方术语。

至于这是否会在不同的实现之间发生变化-我非常怀疑,但如果你一开始就避免这样做,你就永远不需要关心:)


Javac并没有主动禁止这样做,但它确实有一个限制,这在很大程度上意味着您永远不想从另一个文件引用顶级类,除非它与所在文件具有相同的名称。

假设您有两个文件,Foo.java和Bar.java。

Foo.java包含:

公共类Foo

Bar.java包含:

公共类酒吧 类巴兹

我们还假设所有的类都在同一个包中(文件在同一个目录中)。

如果Foo指向Baz而不是Bar,我们试图编译Foo.java会发生什么?编译失败,出现如下错误:

Foo.java:2: cannot find symbol
symbol  : class Baz
location: class Foo
  private Baz baz;
          ^
1 error

仔细想想,这是有道理的。如果Foo引用Baz,但没有bazz .java(或bazz .class), javac如何知道要查找哪个源文件?

如果您告诉javac同时编译Foo.java和Bar.java,或者您之前编译过Bar.java(将bazz .class留在javac可以找到它的地方),或者即使Foo碰巧引用了Bar而不是Baz,那么这个错误就会消失。然而,这会使您的构建过程感觉非常不可靠和不可靠。

Because the actual limitation, which is more like "don't refer to a top-level class from another file unless it either has the same name as the file it's in or you're also referring to another class that's named the same thing as that file that's also in that file" is kind of hard to follow, people usually go with the much more straightforward (though stricter) convention of just putting one top-level class in each file. This is also better if you ever change your mind about whether a class should be public or not.

新版本的javac也可以在这种情况下使用-Xlint:all生成警告:

auxiliary class Baz in ./Bar.java should not be accessed from outside its own source file

有时候确实有一个很好的理由,为什么每个人都以一种特定的方式做某事。


我相信您可以将PrivateImpl定义为:一个非公共顶级类。您还可以声明非公共顶级接口。

例如,SO上的其他地方:非公共顶级类vs静态嵌套类

至于不同版本之间的行为变化,有关于在1.2.2中“完美工作”的讨论。但是在sun的论坛:Java编译器1.4中停止工作-无法在文件中声明非公共顶级类。


1.这种技术是否有一个整齐的名称(类似于内部的、嵌套的、匿名的)?

多类单文件演示。

2.JLS表示,系统可能会强制限制这些二级类不能被包的其他编译单元中的代码引用,例如,它们不能被视为包私有。这真的是Java实现之间的变化吗?

我不知道有哪个没有这个限制——所有基于文件的编译器都不允许你引用与类名不同的文件中的源代码类。(如果你编译一个多类文件,并把类放在类路径上,那么任何编译器都能找到它们)


你想上多少课就可以上多少课

public class Fun {
    Fun() {
        System.out.println("Fun constructor");
    }
    void fun() {
        System.out.println("Fun mathod");
    }
    public static void main(String[] args) {
        Fun fu = new Fun();
        fu.fun();
        Fen fe = new Fen();
        fe.fen();
        Fin fi = new Fin();
        fi.fin();
        Fon fo = new Fon();
        fo.fon();
        Fan fa = new Fan();
        fa.fan();
        fa.run();
    }
}

class Fen {
    Fen() {
        System.out.println("fen construuctor");

    }
    void fen() {
        System.out.println("Fen method");
    }
}

class Fin {
    void fin() {
        System.out.println("Fin method");
    }
}

class Fon {
    void fon() {
        System.out.println("Fon method");
    } 
}

class Fan {
    void fan() {
        System.out.println("Fan method");
    }
    public void run() {
        System.out.println("run");
    }
}

根据Effective Java第二版(第13项):

如果包私有顶级类(或接口)仅被 一个类,考虑将顶层类设置为私有嵌套类 唯一使用它的类(第22项)。这就减少了 从其包中的所有类到一个类的可访问性 它使用了它。但更重要的是降低可访问性 一个免费的公共类而不是包私有的顶级类: ……”

嵌套类可以是静态的,也可以是非静态的,这取决于成员类是否需要访问外围实例(第22项)。


是的,你可以,在一个外部公共类上使用公共静态成员,就像这样:

public class Foo {

    public static class FooChild extends Z {
        String foo;
    }

    public static class ZeeChild extends Z {

    }

}

和另一个文件引用上述:

public class Bar {

    public static void main(String[] args){

        Foo.FooChild f = new Foo.FooChild();
        System.out.println(f);

    }
}

把它们放在同一个文件夹里。编译:

javac folder/*.java

并运行:

 java -cp folder Bar

不。你不能。但在Scala中这是非常可能的:

class Foo {val bar = "a"}
class Bar {val foo = "b"}

仅供参考,如果你使用Java 11+,这条规则有一个例外:如果你直接运行你的Java文件(没有编译)。在这种模式下,每个文件对单个公共类没有限制。但是,带有main方法的类必须是文件中的第一个类。