在类Outer的内部定一个类Inner,我们把类Inner称作内部类,类Outer称作外部类。内部类可以分为成员内部类、静态内部类、局部内部类和匿名内部类。
成员内部类定义在另一个类的内部。
public class Outer {
// 定义一个成员内部类
class Inner {
}
}
成员内部类的特点:
1) Inner在编译之后以独立的class文件存在,class文件的命名方式为:Outer$Inner.class
。
2) Inner的实例对象不能单独存在,必须依附于Outer的实例对象。(Inner实例对象存在一个隐式引用,指向创建它的Outer类的实例对象)
3) Inner可以访问Outer中属性和方法,包括私有属性和私有方法。
4) Inner类具有默认的包访问权限,只有所在包的类可以访问。
5) Inner类中不能存在static修饰属性和方法(静态常量除外),是Java语法的一种约束。
代码示例:
public class Test {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.test();
}
}
public class Outer {
private String name;
private int age;
private void sayHello() {
System.out.println("Hello World");
}
class Inner {
void test() {
// 访问私有属性
System.out.println(age);
System.out.println(Outer.this.name);
// 访问私有方法
sayHello();
}
}
}
观察上述代码,要实例化一个Inner对象,我们必须首先实例化一个Outer对象,然后通过Outer的实例对象来实例化Inner对象。
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
这是因为Inner实例对象除了持有一个this
指向自己,还隐式的持有一个Outer的实例对象,可以用Outer.this
访问这个实例。
静态内部类定义在另一个类的内部,并用static
关键字修饰。
静态内部类的特点:
1) StaticInner在编译之后以独立的class文件存在,class文件的命名方式为:Outer$StaticInner.class
。
2) StaticInner的实例对象可以独立存在,不再依附于Outer的实例对象。
3) StaticInner只能访问Outer中static修饰属性和方法,包括私有属性和私有方法。
4) StaticInner类具有默认的包访问权限,只有所在包的类可以访问。
5) StaticInner类中可以存在static修饰属性和方法。
代码示例:
public class Test {
public static void main(String[] args) {
Outer.StaticInner staticInner = new Outer.StaticInner();
staticInner.test();
}
}
public class Outer {
private static String name;
static class StaticInner {
private static int age = 18;
void test() {
System.out.println(name);
}
}
}
观察上述代码,StaticInner类的实例对象可以独立存在,不再依赖于Outer类的实例对象。
局部内部类与成员内部类类似,但其定义在方法之中,作用范围仅限于该方法中。
局部内部类的特点:
1) LocalInner在编译之后以独立的class文件存在,class文件的命名方式为:Outer$1LocalInner.class
。
2) LocalInner实例对象存在一个隐式引用,指向创建它的Outer类的实例对象。
3) LocalInner除了可以访问Outer中属性和方法,包括私有属性和私有方法,还有访问所处方法中局部变量,但这些变量实际上是final
修饰,不可再改变。
4) LocalInner只能在定义它的方法中访问
5) LocalInner类中不可以存在static修饰属性和方法,静态常量除外。
代码示例:
public class Outer {
private String name;
private int age;
private void sayHello() {
System.out.println("Hello World");
}
public void localInnerClass() {
String localName = "localInnerClass";
class LocalInner {
public void test() {
System.out.println(localName);
System.out.println(name);
System.out.println(Outer.this.age);
sayHello();
}
}
LocalInner localInner = new LocalInner();
localInner.test();
}
}
为什么叫匿名内部类呢?因为匿名内部类不存在类名。之所以我们要定义匿名内部类,是因为我们在使用的时候通常不关系类名,可以省略掉定义类的过程。
匿名内部类的特点:
1) 匿名内部类在编译之后以独立的class文件存在,class文件的命名方式为:外部类名称$数字编号.class
,编号从1开始,文中生成的class文件名为Outer$1.class
。
2) 匿名内部类实例对象存在一个隐式引用,指向创建它的Outer类的实例对象。
3) 匿名内部类可以访问Outer中属性和方法,包括私有属性和私有方法。
4) 匿名内部类类具有默认的包访问权限,只有所在包的类可以访问。
5) 匿名内部类类中不能存在static修饰属性和方法(静态常量除外),是Java语法的一种约束。
代码示例:
public class Test {
public static void main(String[] args) {
new Outer().sayHello();
}
}
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
public class Outer {
private String name;
private int age;
public void sayHello() {
new Runnable() {
private String anonymousName;
private void test() {
System.out.println(name);
System.out.println(Outer.this.age);
}
@Override
public void run() {
System.out.println("我是一个匿名内部类");
}
}.run();
}
}
观察上述代码,我们使用了并发编程常用的Runnable接口,接口是不能被实例化,我们想要得到一个Runnable接口的实例,但是又不想创建一个类来实现它,就可以采用匿名内部类的方式。