在 Java 中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。
广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类。
主要参考博客(这个博主写的非常好,深入理解内部类部分未拜读)

成员内部类

成员内部类是最普通的内部类,成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括 private 成员和静态成员)。
它的定义为位于另一个类的内部,形如下面的形式:

  1. class Circle {
  2. private double radius = 0;
  3. public Circle(double radius) {
  4. this.radius = radius;
  5. Draw draw = new Draw();
  6. draw.drawRadius();
  7. }
  8. // 该类像是类 Circle 的一个成员,Circle 称为外部类
  9. class Draw {
  10. public void drawRadius() {
  11. // 外部类的 private 成员
  12. System.out.println(radius);
  13. System.out.println(Circle.this.radius);
  14. }
  15. }
  16. }

当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问

外部类.this.成员变量 外部类.this.成员方法


虽然成员内部类可以无条件地访问外部类的成员,但是外部类想访问成员内部类的成员却不是这么随心所欲了
在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问,代码示例如上。


成员内部类是依附外部类而存在的,也就是说:如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。创建成员内部类对象的一般方式如下:

  1. public class Test {
  2. public static void main(String[] args) {
  3. // 第一种方式:通过Outter对象来创建
  4. Outter outter = new Outter();
  5. Outter.Inner inner0 = outter.new Inner();
  6. //第二种方式:
  7. Outter.Inner inner1 = outter.getInnerInstance();
  8. }
  9. }
  10. class Outter {
  11. private Inner inner = null;
  12. public Outter() {
  13. }
  14. public Inner getInnerInstance() {
  15. if(inner == null)
  16. inner = new Inner();
  17. return inner;
  18. }
  19. class Inner {
  20. public Inner() {
  21. }
  22. }
  23. }

内部类可以拥有四种访问权限,而外部类智能被 public 修饰和 默认访问权限。
上面示例中,成员内部类 Inner,
如果用 private 修饰,则只能在外部类的内部访问。
如果用 public 修饰,则任何地方都能访问。
如果用 protected 修饰,则只能在同一个包下或者继承外部类的情况下访问。
如果是默认访问权限,则只能在同一个包下访问。
成员内部类中不可以包含任何静态成分,否则会造成内外部类初始化问题(报错)。


成员内部类的继承问题
一般来说,内部类是很少用来作为基类用的。但是当用来继承的话,要注意两点:

  • 成员内部类的引用方式必须为 Outter.Inner
  • 构造器中必须有指向外部类对象的引用,并通过这个引用调用 super()。这段代码摘自《Java编程思想》

    1. class WithInner {
    2. class Inner{
    3. }
    4. }
    5. class InheritInner extends WithInner.Inner {
    6. // InheritInner() 是不能通过编译的,一定要加上形参
    7. InheritInner(WithInner wi) {
    8. // 必须有这句调用
    9. wi.super();
    10. }
    11. public static void main(String[] args) {
    12. WithInner wi = new WithInner();
    13. InheritInner obj = new InheritInner(wi);
    14. }
    15. }

    静态内部类

    静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个 static 关键字。
    静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非 static 成员,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非 static 成员必须依附于具体的对象。 ```java public class Test { public static void main(String[] args) {

    1. Outter.Inner inner = new Outter.Inner();

    } }

class Outter { public Outter() {

  1. }
  2. static class Inner {
  3. public Inner() {
  4. }
  5. }

}

  1. <a name="tJZim"></a>
  2. # 局部内部类
  3. 局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于:局部内部类的访问仅限于方法内或者该作用域内。<br />局部内部类就像是方法里面的一个局部变量一样,是不能有 public、protected、private 以及 static 修饰符的。
  4. ```java
  5. class People{
  6. public People() {
  7. }
  8. }
  9. class Man{
  10. public Man(){
  11. }
  12. public People getWoman(){
  13. // 局部内部类
  14. class Woman extends People{
  15. int age =0;
  16. }
  17. return new Woman();
  18. }
  19. }

匿名内部类

匿名内部类可以使代码更简洁,可以在定义一个类的同时对其进行实例化。
使用匿名内部类能够在实现父类或者接口中的方法情况下同时产生一个相应的对象,但是前提是这个父类或者接口必须先存在才能这样使用。
它与局部类很相似,不同的是它没有类名,如果某个局部类你只需要用一次,那么你就可以使用匿名内部类。
匿名内部类是唯一一种没有构造器的类。
正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。
匿名内部类在编译的时候由系统自动起名为 name$1.class ,name 为匿名内部类所在类的类名。


官方文档
官网文档中的示例:

  1. public class HelloWorldAnonymousClasses {
  2. /**
  3. * 包含两个方法的 HelloWorld 接口
  4. */
  5. interface HelloWorld {
  6. public void greet();
  7. public void greetSomeone(String someone);
  8. }
  9. public void sayHello() {
  10. // 局部内部类 EnglishGreeting 实现了 HelloWorld 接口
  11. // 可以其他的 Greeting 比如:frenchGreeting、SpanishGreeting
  12. class EnglishGreeting implements HelloWorld {
  13. String name = "world";
  14. @Override
  15. public void greet() {
  16. greetSomeone("world");
  17. }
  18. @Override
  19. public void greetSomeone(String someone) {
  20. name = someone;
  21. System.out.println("Hello " + name);
  22. }
  23. }
  24. HelloWorld englishGreeting = new EnglishGreeting();
  25. // 2、匿名类实现HelloWorld接口
  26. HelloWorld frenchGreeting = new HelloWorld() {
  27. String name = "tout le monde";
  28. @Override
  29. public void greet() {
  30. greetSomeone("tout le monde");
  31. }
  32. @Override
  33. public void greetSomeone(String someone) {
  34. name = someone;
  35. System.out.println("Salut " + name);
  36. }
  37. };
  38. englishGreeting.greet();
  39. englishGreeting.greetSomeone("Fred");
  40. }
  41. public static void main(String... args) {
  42. HelloWorldAnonymousClasses myApp = new HelloWorldAnonymousClasses();
  43. myApp.sayHello();
  44. }
  45. }

匿名内部类可以访问外部类的所有成员(匿名内部类写在类内部,作为成员时可以。写在方法内部不可以)
匿名内部类不能访问外部类未加 final 修饰的变量(JDK1.8 即使没有用 final 修饰也可以访问
匿名内部类中不可以定义静态属性、方法
匿名内部类中可以有常量属性(static final 修饰的属性)
匿名内部内中可以定义属性
匿名内部内中可以可以有额外的方法(父接口、类中没有的方法)
匿名内部内中可以定义内部类
匿名内部内中可以对其他类进行实例化

  1. public class ShadowTest {
  2. public int x = 0;
  3. interface FirstLevel {
  4. void methodInFirstLevel(int x);
  5. }
  6. FirstLevel firstLevel = new FirstLevel() {
  7. public int x = 1;
  8. public static final String finalStr = "Hello World";
  9. @Override
  10. public void methodInFirstLevel(int x) {
  11. System.out.println("x = " + x);
  12. System.out.println("this.x = " + this.x);
  13. System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
  14. }
  15. };
  16. public static void main(String... args) {
  17. ShadowTest st = new ShadowTest();
  18. ShadowTest.FirstLevel fl = st.firstLevel;
  19. fl.methodInFirstLevel(23);
  20. }
  21. }

内部类的好处

每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个接口的实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整。
方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。
方便编写事件驱动程序 (Android 开发)。
方便编写线程代码。