Java内部类的3种用法(附带实例)
所谓的内部类,是指在某个 Java 类中再定义一个 Java 类,其中的类称为内部类,有时也称为内嵌类。
在涉及多个类时,我们可以按常规的方式分别定义多个类,那么为什么还需要内部类这种内嵌的方式呢?其实最主要的原因就在“内嵌”两个字,有时某个类仅在一个类里面使用,为了更好地组织它们之间的逻辑关系就可以使用内部类。
成员内部类的语法如下,先定义一个类,然后在这个类中定义另一个类:
重点来看如何创建一个成员内部类对象,我们必须先创建一个 MemberInnerClassTest 对象 mict,再通过“mict.new InnerClass()”来创建成员内部类对象,该对象的类型为 MemberInnerClassTest.InnerClass。
同时可以看到,成员内部类的方法能直接访问 MemberInnerClassTest 类的属性,程序运行后输出“hello from inner class.”。
如果一个类包含了一个成员内部类则一共有两个类,这两个类都被我们写到同一个 .java 文件中。然而编译出来的 .class 文件却并非只有一个,而是两个。比如 MemberInnerClassTest.java 编译后有两个 .class 文件,对应的文件名如下图所示。

图 1 编译成员内部类
InnerClass 内部类编译后对应的文件名为“MemberInnerClassTest$InnerClass.class”,其中使用了“$”符号拼接。
通过下面的实例代码来理解静态内部类的使用:
当我们想要访问静态内部类的静态属性和静态方法时,可以通过“StaticInnerClassTest2. InnerClass.info”的方式,通过“.”符号从最外层类到到内部类就能访问到内部类的静态属性和静态方法,实例代码如下所示:
匿名内部类用于在类中继承某个类或者实现某个接口并重写某个方法,它能让我们在定义新类的同时创建对象,大大简化了实现代码。
如果在某个类中要定义一个匿名内部类,可以按照下面的语法:
1) 当要继承某个类时通过:
2) 当要实现某个接口时通过:
再看看实现接口的匿名内部类如何实现,实例程序如下:
以上两个示例编译后产生的 .class 文件如下图所示:

图 2 .class文件
两个匿名内部类对应的文件分别为“AnonymousInnerClassTest$1.class”和“AnonymousInnerClassTest2$1.class”。
在涉及多个类时,我们可以按常规的方式分别定义多个类,那么为什么还需要内部类这种内嵌的方式呢?其实最主要的原因就在“内嵌”两个字,有时某个类仅在一个类里面使用,为了更好地组织它们之间的逻辑关系就可以使用内部类。
Java成员内部类
所谓成员内部类,其重点在“成员”两字,即将内部类作为类成员之一,此时它们与类的其他元素属于同一级别。成员内部类的语法如下,先定义一个类,然后在这个类中定义另一个类:
访问修饰符 class 类名{ 访问修饰符 class 内部类名{ } }下面的实例代码展示了如何使用成员内部类:
public class MemberInnerClassTest { public String str = "hello"; class InnerClass { public void print() { System.out.println(str + " from inner class."); } } public static void main(String[] args) { MemberInnerClassTest mict = new MemberInnerClassTest(); MemberInnerClassTest.InnerClass inner = mict.new InnerClass(); inner.print(); } }可以看到在 MemberInnerClassTest 类中定义了一个 InnerClass 内部类。
重点来看如何创建一个成员内部类对象,我们必须先创建一个 MemberInnerClassTest 对象 mict,再通过“mict.new InnerClass()”来创建成员内部类对象,该对象的类型为 MemberInnerClassTest.InnerClass。
同时可以看到,成员内部类的方法能直接访问 MemberInnerClassTest 类的属性,程序运行后输出“hello from inner class.”。
如果一个类包含了一个成员内部类则一共有两个类,这两个类都被我们写到同一个 .java 文件中。然而编译出来的 .class 文件却并非只有一个,而是两个。比如 MemberInnerClassTest.java 编译后有两个 .class 文件,对应的文件名如下图所示。

图 1 编译成员内部类
InnerClass 内部类编译后对应的文件名为“MemberInnerClassTest$InnerClass.class”,其中使用了“$”符号拼接。
Java静态内部类
所谓静态内部类是指将内部类声明为 static,这样能使内部类更方便地被访问。我们知道被 static 修饰的属性和方法都可以通过类来直接访问,而不必先创建该类的对象,静态内部类也通过类直接进行访问。通过下面的实例代码来理解静态内部类的使用:
public class StaticInnerClassTest { public static String str = "hello"; static class InnerClass { public void print() { System.out.println(str + " from static inner class."); } } public static void main(String[] args) { StaticInnerClassTest.InnerClass inner = new StaticInnerClassTest.InnerClass(); inner.print(); } }我们定义一个内部类并使用 static 来修饰该内部类。如果静态内部类里面需要使用外部变量,则需要将外部变量也声明为 static,比如这里的 str 变量必须修饰为 static。最后可以通过“new StaticInnerClassTest.InnerClass()”的方式来创建静态内部类的对象。
当我们想要访问静态内部类的静态属性和静态方法时,可以通过“StaticInnerClassTest2. InnerClass.info”的方式,通过“.”符号从最外层类到到内部类就能访问到内部类的静态属性和静态方法,实例代码如下所示:
public class StaticInnerClassTest2 { static class InnerClass { public static String info = "hello"; public static void hello() { System.out.println("hello"); } } public static void main(String[] args) { System.out.println(StaticInnerClassTest2.InnerClass.info); StaticInnerClassTest2.InnerClass.hello(); } }
Java匿名内部类
所谓匿名是指没有名称,匿名内部类就是没有名称的内部类,定义类时不需要取类名。匿名内部类用于在类中继承某个类或者实现某个接口并重写某个方法,它能让我们在定义新类的同时创建对象,大大简化了实现代码。
如果在某个类中要定义一个匿名内部类,可以按照下面的语法:
1) 当要继承某个类时通过:
父类名 变量 = new 父类名(){...};来定义一个继承父类的匿名子类并创建该子类的对象,在大括号中可以重写父类的方法,这个子类没有名称。
2) 当要实现某个接口时通过:
接口名 变量 = new 接口名(){...};来定义一个实现了某接口的匿名类并创建一个该类对象,在大括号中实现接口的方法。
我们来看继承父类的匿名内部类如何实现,看下面的实例程序:注意,两种方式都必须以“;”符号结尾。
public class AnonymousInnerClassTest { public static void main(String[] args) { Task task = new Task() { public void run() { System.out.println("running task."); } }; task.run(); } } abstract class Task { abstract void run(); }定义一个 Task 抽象类作为父类,该类定义了一个 run() 抽象方法,子类都必须重写该方法。然后在 AnonymousInnerClassTest 类的主方法中定义一个继承 Task 类的匿名子类并创建对象,对照前面的语法不难理解,我们定义了一个匿名子类,该子类重写了父类的 run() 方法,最终输出“running task.”。
再看看实现接口的匿名内部类如何实现,实例程序如下:
public class AnonymousInnerClassTest2 { public static void main(String[] args) { ITask task = new ITask() { public void run() { System.out.println("running task."); } }; task.run(); } } interface ITask { public void run(); }先定义一个 ITask 接口,该接口定义了一个 run() 方法。现在在 AnonymousInnerClassTest2 类的主方法中定义一个实现 ITask 接口的匿名内部类,并实现对应的方法,最终输出“running task.”。
以上两个示例编译后产生的 .class 文件如下图所示:

图 2 .class文件
两个匿名内部类对应的文件分别为“AnonymousInnerClassTest$1.class”和“AnonymousInnerClassTest2$1.class”。