JAVA 内部类

概述

在 类内部 也可以 定义另一个类。

如果在类 Outer 的内部定义类 Inner,此时类 Inner 就称为内部类(或称为嵌套类),而类 Outer 则称为 外部类(或称为宿主类)。

如果有多层嵌套,例如类 A 中有内部类 B,而类 B 中还有内部类 C,那么通常将最外层的类称为顶层类(或者顶级类)。

内部类的作用

内部类可以很好地 实现隐藏,内部类拥有外部类的所有元素的访问权限。

内部类的种类

  • 实例内部类
  • 静态内部类
  • 局部内部类
  • 匿名类

每种内部类都有它特定的一些特点,本节先详细介绍一些和内部类相关的知识。

实例内部类

实例内部类是指 没有用 static 修饰的内部类,有的地方也称为 非静态内部类。

格式

public class Outer {
    class Inner {
        // 实例内部类
    }
}

特点

1)在外部类的静态方法和外部类以外的其他类中,必须通过外部类的实例创建内部类的实例。

public class Outer {
    class Inner1 {
    }
    Inner1 i = new Inner1(); // 不需要创建外部类实例
    public void method1() {
        Inner1 i = new Inner1(); // 不需要创建外部类实例
    }
    public static void method2() {
        Inner1 i = new Outer().new inner1(); // 需要创建外部类实例
    }
    class Inner2 {
        Inner1 i = new Inner1(); // 不需要创建外部类实例
    }
}
class OtherClass {
    Outer.Inner i = new Outer().new Inner(); // 需要创建外部类实例
}

2)在实例内部类中,可以访问外部类的所有成员。

public class Outer {
    public int a = 100;
    static int b = 100;
    final int c = 100;
    private int d = 100;
    public String method1() {
        return "实例方法1";
    }
    public static String method2() {
        return "静态方法2";
    }
    class Inner {
        int a2 = a + 1; // 访问public的a
        int b2 = b + 1; // 访问static的b
        int c2 = c + 1; // 访问final的c
        int d2 = d + 1; // 访问private的d
        String str1 = method1(); // 访问实例方法method1
        String str2 = method2(); // 访问静态方法method2
    }
    public static void main(String[] args) {
        Inner i = new Outer().new Inner(); // 创建内部类实例
        System.out.println(i.a2); // 输出101
        System.out.println(i.b2); // 输出101
        System.out.println(i.c2); // 输出101
        System.out.println(i.d2); // 输出101
        System.out.println(i.str1); // 输出实例方法1
        System.out.println(i.str2); // 输出静态方法2
    }
}

提示:如果有多层嵌套,则内部类可以访问所有外部类的成员。

3)在外部类中不能直接访问内部类的成员,而必须通过内部类的实例去访问。如果类 A 包含内部类 B,类 B 中包含内部类 C,则在类 A 中不能直接访问类 C,而应该通过类 B 的实例去访问类 C。

4)外部类实例与内部类实例是一对多的关系,也就是说一个内部类实例只对应一个外部类实例,而一个外部类实例则可以对应多个内部类实例。

如果实例内部类 B 与外部类 A 包含有同名的成员 t,则在类 B 中 t 和 this.t 都表示 B 中的成员 t,而 A.this.t 表示 A 中的成员 t。

public class Outer {
    int a = 10;
    class Inner {
        int a = 20;
        int b1 = a;
        int b2 = this.a;
        int b3 = Outer.this.a;
    }
    public static void main(String[] args) {
        Inner i = new Outer().new Inner();
        System.out.println(i.b1); // 输出20
        System.out.println(i.b2); // 输出20
        System.out.println(i.b3); // 输出10
    }
}

5)在实例内部类中不能定义 static 成员,除非同时使用 final 和 static 修饰。

静态内部类

静态内部类是指使用 static 修饰的内部类。

格式

public class Outer {
    static class Inner {
        // 静态内部类
    }
}

特点

1)在创建静态内部类的实例时,不需要创建外部类的实例。

public class Outer {
    static class Inner {
    }
}
class OtherClass {
    Outer.Inner oi = new Outer.Inner();
}

2)静态内部类中可以定义静态成员和实例成员。
外部类以外的其他类需要通过完整的类名访问静态内部类中的静态成员,如果要访问静态内部类中的实例成员,则需要通过静态内部类的实例。

public class Outer {
    static class Inner {
        int a = 0;    // 实例变量a
        static int b = 0;    // 静态变量 b
    }
}
class OtherClass {
    Outer.Inner oi = new Outer.Inner();
    int a2 = oi.a;    // 访问实例成员
    int b2 = Outer.Inner.b;    // 访问静态成员
}

3)静态内部类可以直接访问外部类的静态成员,如果要访问外部类的实例成员,则需要通过外部类的实例去访问。

public class Outer {
    int a = 0;    // 实例变量
    static int b = 0;    // 静态变量
    static class Inner {
        Outer o = new Outer;
        int a2 = o.a;    // 访问实例变量
        int b2 = b;    // 访问静态变量
    }
}

局部内部类

局部内部类是指 在方法中 定义的内部类。

格式

public class Test {
    public void method() {
        class Inner {
            // 局部内部类
        }
    }
}

特点

1)局部内部类与局部变量一样,不能使用访问控制修饰符(public、private 和 protected)和 static 修饰符修饰。

2)局部内部类只在当前方法中有效。

public class Test {
    Inner i = new Inner();    // 编译出错
    Test.Inner ti = new Test.Inner();    // 编译出错
    Test.Inner ti2 = new Test().new Inner();    // 编译出错
    public void method() {
        class Inner{

        }
        Inner i = new Inner();
    }
}

3)局部内部类中不能定义 static 成员。

4)局部内部类中还可以包含内部类,但是这些内部类也不能使用访问控制修饰符(public、private 和 protected)和 static 修饰符修饰。

5)在局部内部类中可以访问外部类的所有成员。

6)在局部内部类中只可以访问 当前方法中 final 类型的参数与变量。
如果方法中的成员与外部类中的成员同名,则可以使用 <OuterClassName>.this.<MemberName> 的形式访问外部类中的成员。

public class Test {
    int a = 0;
    int d = 0;
    public void method() {
        int b = 0;
        final int c = 0;
        final int d = 10;
        class Inner {
            a2 = a;    // 访问外部类中的成员
            // int b2 = b;    // 编译出错
            int c2 = c;    // 访问方法中的成员
            int d2 = d;    // 访问方法中的成员
            int d3 = Test.this.d;    //访问外部类中的成员
        }
        Inner i = new Inner();
        System.out.println(i.d2);    // 输出10
        System.out.printIn(i.d3);    // 输出0
    }
    public static void main(String[] args) {
        Test t = new Test();
        t.method();
    }
}

匿名类

匿名类是指没有类名的内部类,必须在创建时使用 new 关键字,也就是说:创建了匿名类的对象。

格式

new <类或接口>() {
    // 类的主体
};

作用

这种形式的 new 语句声明一个新的匿名类,它对一个 给定的类 进行扩展 ,或者 实现一个给定的接口

使用匿名类可使代码更加 简洁、紧凑,模块化程度更高。

定义匿名类方式

  • 继承一个类,重写其方法。
  • 实现一个接口(可以是多个),实现其方法。
    实现接口的方式与实现类的方式相同

例子:

public class Out {
    void show() {
        System.out.println("调用 Out 类的 show() 方法");
    }
}
public class TestAnonymousInterClass {
    // 在这个方法中构造一个匿名内部类
    private void show() {
        Out anonyInter = new Out() {
            // 获取匿名内部类的实例
            void show() {
                System.out.println("调用匿名类中的 show() 方法");
            }
        };
        anonyInter.show();
    }
    public static void main(String[] args) {
        TestAnonymousInterClass test = new TestAnonymousInterClass();
        test.show();
    }
}

执行结果:

调用匿名类中的 show() 方法

从输出结果可以看出,匿名内部类有自己的实现。

特点

1)匿名类和局部内部类一样,可以访问外部类的所有成员。如果匿名类位于一个方法中,则匿名类只能访问方法中 final 类型的局部变量和参数。

public static void main(String[] args) {
    int a = 10;
    final int b = 10;
    Out anonyInter = new Out() {
        void show() {
            // System.out.println("调用了匿名类的 show() 方法"+a);    // 编译出错
            System.out.println("调用了匿名类的 show() 方法"+b);    // 编译通过
        }
    };
    anonyInter.show();
}

从 Java 8 开始添加了 Effectively final 功能,在 Java 8 及以后的版本中代码第 6 行不会出现编译错误

2)匿名类中允许使用非静态代码块进行成员初始化操作。

Out anonyInter = new Out() {
    int i;
    {    // 非静态代码块
        i = 10;    //成员初始化
    }
    public void show() {
        System.out.println("调用了匿名类的 show() 方法"+i);
    }
};

3)匿名类的非静态代码块会在父类的构造方法之后被执行。

public class Test15 {
    public void test(){
        new Out() {
            int i;
            {    // 非静态代码块
                i = 10;    //成员初始化
                System.out.println("Out非静态代码块");
            }
        };
    }
    public static void main(String[] args) {
        Test15 t=new Test15();
        t.test();
    }
}

class Out{
    public Out(){
        System.out.println("out构造方法");
    }
}

执行结果:

out构造方法
Out非静态代码块

执行顺序与普通类的代码块不同

参见:JAVA 构造代码块和static代码块、普通代码块


原文出处:/show_1EF4rpIt7GIi.html