内部类拥有外围类所有元素的访问权限。仍然是独立的类,编译后会有单独的.class文件,命名是:外部类类名$内部类类名.class。
内部类是外部类的成员,外部类可以直接类名访问,其他类访问内部类要加外部类的名称。内部类和外部类不能重名。
如果把内部类声明成静态的,那么内部类不能随便访问外部类的成员变量,只能访问外部类的静态成员变量。
外部类只有两种访问级别:public和默认,内部类则有四种。
内部类可以嵌套,最外层的成为顶层类。
public class DbApplicationTests {
public static class InnerClass{
public int getSum(int x, int y) {
System.out.println(a);
//System.out.println(b); b不是静态变量,所以无法访问的。
return x+y;
}
}
public int b = 20;
public static final int a = 10;
@Test
public void contextLoads() {
// 测试本类的内部类,直接访问即可
InnerClass i = new InnerClass(); // 10
System.out.println(i.getSum(1, 2)); // 3
// 另一个
Show s = new Show(3, 4);
s.testFather(); // Show.testFather: 3*4
// Square.FatherInClass: 3*4
}
}
由于Shape里width和height是protected,所以Shape的子类Square、Square的子类Show都可以直接使用width和height而不需要加限定符。
在Square里添加一个内部类:
protected class FatherInClass {
public void func() {
System.out.println("Square.FatherInClass: "+width+"*"+height);
}
}
// Show继承了Square:
public class Show extends Square{
public Show(int width, int height) {
super(width, height);
}
public void testFather() {
// 这里是外部访问Square的内部类,需要实例化
Square.FatherInClass fi = new Square(width, height).new FatherInClass();
System.out.println("Show.testFather: "+width+"*"+height);
// 有FatherInClass的实例了
fi.func();
}
}
实例内部类
没有static修饰的内部类。
- 外部类的静态方法和外部类以外的类中,需要先创建外部类实例再创建内部类实例。在外部类的非静态方法和另一个内部类中,都不需要创建外部类实例,直接new内部类实例即可。
- 如果有多层嵌套,内部类是可以访问所有外部类的成员的。
- 外部类中不能直接访问内部类的成员,必须通过内部类的实例去访问。如果类A包含类B,类B包含类C,那么A访问C要通过B的实例。
- 在实例内部类中不能定义static成员,除非同时使用final和static修饰。
静态内部类
用static修饰的内部类。
- 创建静态内部类的实例时,不需要创建外部类的实例(创建内部类实例还是要加外部类的类名的)。
- 静态内部类中可以定义静态成员和实例成员。外部类外的其他类可以通过完整类名访问静态成员,实例成员则需要静态内部类的实例。
- 静态内部类可以访问外部类的静态成员,如果要访问外部类的实例成员,需要外部类的实例去访问。
目录
局部内部类
在一个方法中定义的内部类。
- 不能使用访问控制修饰符和static修饰,只能写class XXX{}。它可以有内部类,但是也不能有这些修饰。
- 不能定义static成员。
- 在局部内部类中可以访问外部类的所有成员。
- 在局部内部类中只能访问当前方法中的final成员。如果方法中的成员与外部类的某成员同名,那么访问外部类中那个成员可以用 外部类名.this.成员名 。
public class Test {
int a=0;
int d=0;
public void method() {
int b=0;
final int c=0; // final型才能在Inner中被访问
final int d=10;
class Inner {
int a2=a; //直接访问外部类中的成员
//intb2=b; //编译出错
int c2=c; //访问方法中的成员
int d2=d; //访问方法中的成员
int d3=Test.this.d; //访问外部类中的成员
}
Inner i=new Inner(); // 需要实例
System.out.println(i.d2); //输出10
System.out.println(i.d3); //输出0
}
public static void main(String[] args){
Test t=new Test();
t.method();
}
}
利用内部类实现多重继承
内部类可以继承一个与外部类无关的类,所以才可以多重继承。
举例:
Father.java
public class Father {
public int strong() {
return 9;
}
}
Mother.java
public class Mother {
public int kind() {
return 8;
}
}
// 现在制造一个Son类去继承这两个类:
public class Son {
class Father1 extends Father { // 此内部类继承了Father
public int strong() {
return super.strong() + 1;
}
}
class Mother1 extends Mother { // 此内部类继承了Mother
public int kind() {
return super.kind() - 2;
}
}
public int getStrong() {
return new Father1().strong(); // 用一个实例去调用成员函数
}
public int getKind() {
return new Mother1().kind();
}
}
// 测试:
Son s = new Son();
System.out.println(s.getStrong()); // 10
System.out.println(s.getKind()); // 6
匿名类
没有类名的内部类,它的任务是继承一个类,重写它的方法;或者实现一个或多个接口。既然是内部类,就可以放在类中也可以放在方法中了。注意各种访问控制。匿名类中允许使用非静态代码块初始化成员。
public class DbApplicationTests {
private void show() { // 这是一个方法
Out ac = new Out() { // 这个匿名类要继承类Out
int i;
{ // 非静态代码块
i = 10; // 成员初始化
}
void show() { // 重写了Out.show()
System.out.println("annoymous.show()");
} // 只能访问DbApplicationTests.show()中的final变量
}; // 这个分号不要漏了
System.out.println("show()");
ac.show(); // 前面只是匿名类的定义,这里还需要调用一下
}
@Test
public void contextLoads() {
show();
}
}
// 匿名类继承的Out类:
public class Out {
void show() {
System.out.println("Out.show()");
}
}
// 所以最后输出是:
show()
annoymous.show()
// 实现了重写。