在 Java 这个面向对象的编程语言中,我们几乎离不开“类”这个概念,在博主之前发布的文章中,全部都是在某一个类中,定义一些成员属性或者是成员方法,实例属性(方法)也好、静态属性(方法)也好,最终都是可以通过规范的代码形式去实现的。那么,我们能否在一个类中创建另一个类呢?答案是:肯定的,这就是这篇博文要讲的内容:内部类!!!
在 Java 语言中,内部类是指在一个外部类的内部再定义一个类。而对于这个内部类来说,它可以是静态 static 的,也可以用(访问修饰符)public,protected,default 和 private 来修饰。(而包含这个内部类的外部类只能使用 public 和 default来进行修饰)。而在字节码语言中,只有类的概念,没有外部类和内部类的概念,类只能使用 public 和 default 进行访问控制。
注意:内部类是一个编译器现象,JVM 虚拟机并不知道内部类与常规类有什么不同。对于源文件中一个名为 Outer 的外部类和其内部定义的名为 Inner 的内部类,编译成功后会出现 Outer.class 和 Outer$Inner.class 两个字节码文件以及文件中的 Outer 和 Outer$Inner 两个类,编译器会把内部类翻译成用 $ (美元符号)分隔外部类名与内部类名的独立的常规类名,而虚拟机则对此一无所知,虚拟机在运行的时候,会把 Outer$Inner 作为一种常规类来处理的。所以内部类的成员变量/方法名可以和外部类的相同。
上图展示了在 Java 语言中,内部类的具体分类,下面,我主要为大家介绍一下对这四种内部类的理解以及应用,即:成员内部类、静态内部类、局部内部类和匿名内部类。
在外部类的内部,定义的非静态的内部类,叫成员内部类。
① 外部类的所有实例成员对成员内部类可见。成员内部类的实例对象,有外部类的实例对象的引用,所以可以访问外部类实例的所有成员(包括 private 私有的)。② 外部类的静态成员对成员内部类可见。成员内部类可以访问外部类的所有静态成员(包括 private 私有的)。③ 外部类对成员内部类可见。在内部类中可以使用 new 生成外部类的实例对象。
④ 成员内部类对外部类可见。因为编译后的内部类至少是包内,其构造器至少是包内,所以在外部类中可以使用 new 生成内部类的实例对象。外部类按常规的类访问方式使用内部类,唯一的差别是外部类可以访问成员内部类的所有方法与属性,包括私有方法与私有属性。
⑤ 成员内部类可以使用 public、protected、default或private 访问修饰符进行访问控制,内部类能够隐藏起来,不为同一包的其它类访问。成员内部类一般当做成员变量设置为 private。⑥ 成员内部类是非静态的。所以在成员内部类中,不能定义静态字段、静态方法和静态内部类,因为成员内部类需要先创建外部类的实例对象,才能创建自己的对象;但是可以定义非静态字段、非静态方法和非静态内部类。
假设外部类为 Cow,其成员内部类为 CowLeg。
①在内部类实例对象的方法中引用外部类实例对象的私有数据域:Cow. this.weight
②在外部类实例对象的方法中创建部类实例对象:CowLeg cl = this.new CowLeg(1.12, "黑白相间");
③如果内部类在外部类作用域之外是可见的,则可以这样引用内部类:Cow.CowLeg
将成员内部类的使用再深入限制一步,假如内部类的实例对象不需要引用外部类的实例对象,只是将一个类隐藏在另外一个类的内部,那么就可以将该内部类静态化。在外部类的内部,定义的静态的内部类,叫静态内部类(或叫嵌套类)。提示:静态内部类在实际工作中用的并不是很多。
① 外部类的静态成员对静态内部类可见。静态内部类可以访问外部类的所有静态成员(包括 private 私有的)。② 外部类对静态内部类可见。在内部类中可以使用 new 生成外部类的实例对象。③ 外部类的实例成员对静态内部类不可见。静态内部类的实例对象,没有外部类的实例对象的引用,所以不能访问外部类的实例。
④ 静态内部类对外部类可见。因为编译后的内部类至少是包内,其构造器至少是包内,所以在外部类中可以使用 new 生成内部类的实例对象。
⑤ 静态内部类可以使用 public、protected、default或private 访问修饰符进行访问控制,内部类能够隐藏起来,不为同一包的其它类访问。一般当做成员变量设置为 private。⑥ 静态内部类是静态的。所以在静态内部类中,可以定义静态字段、静态方法和静态内部类;也可以定义非静态字段、非静态方法和非静态内部类。
与成员内部类相比,静态内部类没有编译器自动添加的实例字段和构造器参数。即静态内部类的实例是不可以访问外部类的实例成员。
在外部类的方法中定义的非静态的内部类,叫局部内部类。(因为内部类可以访问外部类方法的形参和局部变量而得此名)可以分为:①在外部类的实例方法内部的局部内部类;②在外部类的静态方法内部的局部内部类。 提示:在实际开发中很少使用局部内部类,只是因为局部内部类的作用域很小,只能在当前方法中使用。
① 实例方法的形参对局部内部类可见。局部内部类的实例对象,可以有外部类的实例方法的形参的字段,可以访问外部类实例方法的形参。这些形参在 JDK8 之前必须被声明为 final,但在 JDK8 中就不需要了。② 实例方法的局部变量对局部内部类可见。局部内部类的实例对象,可以有外部类的实例方法的局部变量的字段,可以访问外部类实例方法的局部变量。这些局部变量在 JDK8 之前必须被声明为 final,但在 JDK8 中就不需要了。
③ 外部类的实例成员对局部内部类可见。局部内部类的实例对象,有外部类的实例对象的引用,所以可以访问外部类实例的所有成员(包括 private 私有的)。④ 外部类的静态成员对局部内部类可见。局部内部类可以访问外部类的所有静态成员(包括 private 私有的)。⑤ 外部类对局部内部类可见。在内部类中可以使用 new 生成外部类的实例对象。
⑥ 局部内部类对外部类的实例方法可见。因为编译后的内部类至少是包内,其构造器至少是包内,所以在外部类的实例方法中可以使用 new 生成局部内部类的实例对象。
⑦ 局部内部类不能使用 public、protected或private 访问修饰符进行访问控制,它的作用域被限定在声明该局部内部类的方法块中,所以在外部类的方法中不可以使用 new 生成局部内部类的实例对象。除了局部内部类所在的外部类方法,没有任何方法知道内部类的存在。
⑧ 局部内部类是非静态的。所以在局部内部类中,不能定义静态字段、静态方法和静态内部类;但是可以定义非静态字段、非静态方法和非静态内部类。
外部类静态方法中的局部内部类除了不能访问外部类的实例外(因为它没有外部类的实例对象的引用),与实例方法中的局部内部类相同。
① 静态方法的形参对局部内部类可见。局部内部类的实例对象,可以有外部类的静态方法的形参的字段,可以访问外部类静态方法的形参。这些形参在 JDK8 之前必须被声明为final,但在 JDK8 中就不需要了。② 静态方法的局部变量对局部内部类可见。局部内部类的实例对象,可以有外部类的静态方法的局部变量的字段,可以访问外部类静态方法的局部变量。这些局部变量在 JDK8 之前必须被声明为 final,但在 JDK8 中就不需要了。
③ 外部类的静态成员对局部内部类可见。局部内部类可以访问外部类的所有静态成员(包括 private 私有的)。④ 外部类对局部内部类可见。在局部内部类中可以使用 new 生成外部类的实例对象。⑤ 外部类的实例成员对局部内部类不可见。局部内部类的实例对象,没有外部类的实例对象的引用,所以不可以访问外部类实例的所有成员。
⑥ 局部内部类对外部类的静态方法可见。因为编译后的内部类至少是包内,其构造器至少是包内,所以在外部类的静态方法中可以使用 new 生成内部类的实例对象。
⑦ 局部内部类不能使用public、protected或private访问修饰符进行访问控制,它的作用域被限定在声明该局部内部类的方法块中。
⑧ 局部内部类是非静态的。所以在局部内部类中,不能定义静态字段、静态方法和静态内部类;但是可以定义非静态字段、非静态方法和非静态内部类。
1. 将局部内部类的使用再深入一步,假如只创建这个类的一个对象,就不必命名了。从使用上讲,匿名内部类和局部内部类的区别是:一个是匿名的,另一个是命名的,其它均相同。(匿名的含义是由编译器自动给内部类起一个内部名称) 2. 在外部类的方法中,定义的非静态的没有类名的内部类,叫匿名内部类。 3. 匿名内部类适合只需要使用一次的类,当创建一个匿名内部类时会立即创建该类的一个实例对象,匿名类不能重复使用。可以分为:在外部类的实例方法内部的匿名内部类,在外部类的静态方法内部的匿名内部类。
① 实例方法的形参对匿名内部类可见。匿名内部类的实例对象,可以有外部类的实例方法的形参的字段,可以访问外部类实例方法的形参。这些形参在 JDK8 之前必须被声明为 final,但在 JDK8 中就不需要了。② 实例方法的局部变量对匿名内部类可见。匿名内部类的实例对象,可以有外部类的实例方法的局部变量的字段,可以访问外部类实例方法的局部变量。这些局部变量在 JDK8 之前必须被声明为 final,但在 JDK8 中就不需要了。
③ 外部类的实例成员对匿名内部类可见。匿名内部类的实例对象,有外部类的实例对象的引用,所以可以访问外部类实例的所有成员(包括 private 私有的)。④ 外部类的静态成员对匿名内部类可见。匿名内部类可以访问外部类的所有静态成员(包括 private 私有的)。⑤ 外部类对局部内部类可见。在匿名内部类中可以使用 new 生成外部类的实例对象。
⑥ 匿名内部类对外部类该实例方法可见。因为编译后的内部类至少是包内,其构造器至少是包内,所以在外部类中实例方法可以使用 new 生成内部类的实例对象。
⑦ 匿名内部类不能使用 public、protected或private 访问修饰符进行访问控制,它的作用域被限定在声明该匿名内部类的方法块中。
⑧ 由于构造器的名字必须与类名相同,而匿名类无类名,所以匿名内部类不能有构造器。取而代之的是将构造器参数传递给父类构造器。⑨ 匿名内部类是非静态的。所以在匿名内部类中,不能定义静态字段、静态方法和静态内部类;但是可以定义非静态字段、非静态方法和非静态内部类。
静态方法中的匿名内部类除了不能访问外部类的实例外(因为它没有外部类的实例对象的引用),与实例方法中的匿名内部类相同。
① 静态方法的形参对匿名内部类可见。匿名内部类的实例对象,可以有外部类的静态方法的形参的字段,可以访问外部类静态方法的形参。这些形参在 JDK8 之前必须被声明为 final,但在 JDK8 中就不需要了。② 静态方法的局部变量对匿名内部类可见。匿名内部类的实例对象,可以有外部类的静态方法的局部变量的字段,可以访问外部类静态方法的局部变量。这些局部变量在 JDK8 之前必须被声明为 final,但在 JDK8 中就不需要了。
③ 外部类的静态成员对匿名内部类可见。匿名内部类可以访问外部类的所有静态成员(包括 private 私有的)。④ 外部类对匿名局部内部类可见。在局部内部类中可以使用 new 生成外部类的实例对象。⑤ 外部类的实例成员对匿名内部类不可见。匿名内部类的实例对象,没有外部类的实例对象的引用,所以不可以访问外部类实例的所有成员(包括 private 私有的)。
⑥ 内部类对外部类的静态方法可见。因为编译后的内部类至少是包内,其构造器至少是包内,所以在外部类的静态方法中可以使用 new 生成内部类的实例对象。
⑦ 匿名内部类不能使用 public、protected或private 访问修饰符进行访问控制,它的作用域被限定在声明该匿名内部类的方法块中。
⑧ 匿名内部类不能有构造方法。因为匿名类无类名,所以无法写构造方法。取而代之的是将构造器参数传递给超类构造器。⑨ 匿名内部类是非静态的。所以在匿名内部类中,不能定义静态字段、静态方法和静态内部类;但是可以定义非静态字段、非静态方法和非静态内部类。
以上,就是博主在学习过程中,总结的 Java 语言中,四种内部类(成员内部类、静态内部类、局部内部类和匿名内部类)的相关内容,希望对大家的学习和理解有所帮助!!!😁😁😁