在java中,类最常见的定义位置是文件中,一个文件中可以定义多个类,但是只能有一个public的类,而且java文件名必须和这个public类相同。看看下面代码
package com.senmu.pack_a
//TestA.java
public class TestA{}
class TestB{}
class TestC{}
这里有一个TestA.java的源文件,里面定义了三个class。可以看出一个源文件里能定义多个class,但是有且只能有一个public类,非public类的名字只要符合java标识符规则就可以,public类的名字必须和源文件名一致。至于为什么有这个规定,很多网上的帖子都说是为了方便JVM根据文件名找到main函数入口,个人觉得这种说法不太可信也不太合理。原因如下,JVM读取的是编译后的.class文件而不是.java源文件,而定义在一个源文件中的多个类编译后都生成了由各自类名命名的.class文件。在我看来当初java的设计者这样规定的主要目应该是为了给源码阅读提供便利,试想一下如果一个源文件中可以有多个public的class而且名字与源文件名不一致,那么当我们在阅读代码时想要了解某个引用类的定义情况应该去哪找这个类的源代码呢?当有了这个规定之后我们就可以根据import的包名和类名准确找到源文件的位置。至于源文件中的其他非public类,他们只能被本包内的类使用,使用范围有限,命名也就没有严格要求了。
类除了可以定义在文件中还可以定义在其他类中。定义在其他类中的类成为内部类,内部类又可以分成成员内部类和内嵌内部类,其区别在于是否被static修饰符修饰。成员内部类可以访问外部类的所有成员属性和方法,内嵌内部类和普通类没有什么区别只是加了外部类命名限定而已。如下面代码。
package com.senmu.pack_a
//TestA.java
public class TestA{
private int f1;
private int f2;
public TestA(int f1,int f2){
this.f1 = f1;
this.f2 = f2;
}
public int cal{
return new TestAInner1().cal();//在外部类中可以像普通类一样使用
}
public class TestAInner1{
public int cal(){
return f1+f2;//成员内部类可以直接访问外部类的所有公有及私有属性。
}
}
public static class TestInner2{
public int cal(){
//return f1+f2 编译错误,因为内嵌内部类不能访问外部类的成员属性。
return 0;
}
}
}
package com.senmu.pack_b
import com.senmu.pack_a.TestA;
//TestB.java
public class TestB{
private TestA.TestAInner ti = new TestA.TestAInner();//TestA.TestInner是内嵌内部类,可以和普通类一样使用
public void test(){
TestA ta = new TestA();
ta.new TestInner1().cal();//在其他类中使用TestA的成员内部类。
}
}
最后,java中的类还可以定义在方法中,定义在方法中的类只能在方法中使用,可以看到所有对方法可见的变量。
package com.senmu.pack_a
//TestA.java
public class TestA{
private int f1;
private int f2;
public TestA(int f1,int f2){
this.f1 = f1;
this.f2 = f2;
}
public int cal(int a){
class MethodInner{
public int test(){
return a + f1 +f2;//方法中内部类可以访问对方法可见所以变量
}
}
return new MethodInner().test();//方法中的内部类只能在本方法中使用。
}