内部类

一个定义在另一个类中的类,叫作内部类

1. 概述

内部类允许你把一些逻辑相关的类组织在一起,并控制位于内部的类的可见性,这么看来,内部类就像是一种代码隐藏机制:将类置于其他类的内部,从而隐藏名字与组织代码的模式。

2. 创建内部类

创建内部类的方式就如同你想的一样,把类的定义置于外部类里面

public class Outer {// 其他一些成员声明...public class Inner {// 其他一些成员声明...}
}

3. 链接外部类

内部类与其外部类之间存在一种联系,通过这个联系可以访问外部类的所有成员,而不需要任何特殊条件,可以这么说,内部类拥有其外部类的所有元素的访问权。

这是如何做到的呢?当某个外部类的对象创建一个内部类对象时,该内部类对象会秘密地捕获一个指向外部类对象的引用。在你访问外部类成员时,可以通过这个引用选择外部类的成员。所有这些实现的细节由编译器帮忙处理,大多数时候都无需程序员关心。

4. 使用 .this 和 .new

如果你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟圆点和 this

public class Outer {public class Inner {public Outer getOuter() {return Outer.this;}}public static void main(String[] args) {Outer outer = new Outer();Outer outer2 = outer.getOuter();}
}

如果你想创建某个内部类对象,必须在 new 表达式中提供其外部类的引用,这时需要使用 .new 语法

public class Outer {public class Inner {}public static void main(String[] args) {Outer outer = new Outer();Outer.Inner inner = outer.new Inner();}
}

如果从外部类的非静态方法之外的位置创建某个内部类对象,要具体指明这个内部类对象的类型:OuterClassName.InnerClassName(在外部类的静态方法中也可以直接指明类型 InnerClassName

5. 向上转型

当将内部类向上转型为其基类(接口),可以很方便地隐藏实现细节,因为外界所得到的只是指向基类或接口的引用。

// 这是一个接口
public interface Content {...}class Outer {// 注意这里将访问修饰符设为 privateprivate class PContent implements Content {...}public Content getContent() {return new PContent();}
}

在 Outer 类中,内部类 PContent 是私有的,除了 Outer 外没有人可以访问。这意味着,客户端程序员如果想了解这些内部类成员,那是要受限制的。客户端程序员无法访问任何新增的、不属于公共接口的方法,也不能向下转型,因为不能访问其名字。

6. 多重嵌套内部类

无论一个内部类被嵌套多少层,它都能透明地访问所有它所嵌入的外部类的所有成员

class MNA {private void f() {}class A {private void g(){}public class B {void h() {g();f();}}}
}public class TestClass {public static void main(String[] args) {MNA mna = new MNA();MNA.A mnaa = mna.new A();MNA.A.B mnaab = mnaa.new B();mnaab.h();}
}

成员内部类

成员内部类是最普通的内部类,它和外部类的成员变量一样,定义在类的内部,可以声明访问修饰符。成员内部类有如下特性:

  • 不可以定义静态成员和方法,因为内部类是外部类的一个成员,只有当外部类初始化时,内部类才能初始化,静态变量属于类级别,在类加载的时候就初始化,所以两者本身在语法上就有矛盾。但可以定义常量。
  • 可以无条件访问外部类的所有成员属性和成员方法(包括 private 成员和静态成员)。当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问:OuterClassName.this.innerClassMember

局部内部类

在方法的作用域内(而不是在其他类的作用域内)创建一个完整的类,这个类被称作局部内部类。由于是定义在方法内,所以不能声明访问修饰符,作用范围仅在声明类的代码块中,但这并不意味着一旦方法作用域执行完毕,局部内部类就不可用了。局部内部类有如下特性:

  • 局部内部类同样不可以定义静态成员和方法
  • 可以无条件访问外部类的所有成员属性和成员方法,但不能访问方法体内的局部变量,除非定义为 final 常量(仔细想想就知道了,内部类是依靠对外部类的引用进行访问的)
public class Outer {public Outer method() {class Inner {...} }
}

匿名内部类

只用一次的没有名字的类,具体看下面的例子:

public class Outer {public Content method() {return new Content() {private int i = 11;@Overridepublic int getValue() {return i}}}
}

这种奇怪的语法指的是:创建一个继承自 Content 的匿名类的对象。通过 new 表达式返回的这个引用被自动向上转型为 Content 的引用。上述匿名内部类是下述形式的一种简化形式,也就是说,匿名内部类可以看作对成员内部类或局部内部类的一种简写:

public class Outer {class MyContent implements Content {private int i = 11;@Overridepublic int getValue() {return i}}public Content method() {return new MyContent();}
}

匿名内部类没有构造器(因为它根本没有名字),如果我们希望为匿名内部类做一些初始化操作,那该怎么办呢?如果该匿名内部类的基类构造器有参数,则可以直接使用,但编译器会要求其参数引用是 final(即使你不加,Java8 也会为我们自动加上 final)。或者通过代码块,模仿构造器的行为来实现初始化。

public class Outer {public Content method() {return new Content(final int a, final int b) {private int i = a;private int j;{j = b;}}}
}

静态内部类

如果不需要内部类对象与其外部类对象之间有联系,可以将内部类声明为 static。既然静态内部类不需要与外部类有联系,那么也就没有指向外部类的 this 引用,更像一个独立的类,这意味着:

  • 要创建静态内部类的对象,并不需要其外部类的对象
  • 不能访问非静态的外部类对象,只能访问外部类中的 static 所修饰的成员变量或者是方法
public class Outer {public static class Inner {private String label;public String method1() {...}static int x = 10;public static void method2() {...}}
}

静态内部类可以作为接口的一部分,因为你放到接口中的任何类都自动是 public 和 static 的,甚至可以在内部类中实现其外部类接口。

public interface ClassInterface {void method1();class InnerClass implements ClassInterface {@Overridepublic void method1() {...}}
}

为什么需要内部类?

内部类的第一个用途就是可以操作创建它的外部类的对象,典型的例子就是集合中常使用的迭代器 iterator。这只是其中之一,使用内部类最吸引人的原因莫过于:

每个内部类都能独立地继承自某个类或实现某个接口,无论外部类是否继承了某个类或实现某个接口

基于这个特性,使得多重继承成为可能,我们可以让多个内部类以不同的方式实现不同的接口,或继承不同的类。下面这个例子使用了匿名内部类,可能你们已经习以为常,但别忘了匿名内部类其实就是内部类的一种简写形式。

class D {}abstract class E {}class Z extend D {E getE() {return new E() {};}
}public class TestClass {static void setD(D d){}static void setE(E e){}public static void main(String[] args) {Z z = new Z();setD(z);setE(z.getE);}
}

另外,内部类也是实现回调的绝佳选择。通过内部类可以实现一个闭包,所谓闭包就是一个可调用的对象,它记录了一些信息。在需要提供闭包来实现回调时,我们可以采用匿名内部类的方式。

继承内部类

因为内部类的构造器必须连接到其外部类对象的引用,所以在继承内部类时,那个指向外部类对象的引用必须被初始化,而在派生类中又不再存在这个引用了。要解决这个问题,必须使用特殊的语法来明确说清它们之间的关系。

class WithInner {class Inner {}
}public class InheritInner extends WithInner.Inner {InheritInner(WithInner wi) {wi.super();}public static void main(String[] args) {WithInner wi = new WithInner();InheritInner li = new InheritInner(wi);}
}

构造器要传递一个指向外部类对象的引用,此外,还必须显式地调用基类构造器。

内部类可以被覆盖吗?答案是不能。如果创建一个内部类,然后继承其外部类并重新定义此内部类,并不会覆盖该内部类。两个内部类是完全独立的,各自在自己的命名空间。

内部类标识符

每个类编译后都会产生一个 .class 文件,内部类也会生成一个 .class 文件,但其命名有严格的规则:外部类的名字 + $ + 内部类的名字,例如 OuterClass.java 生成的 .class 文件包括:

OuterClass.class
OuterClass$InnerClass.class

如果内部类是匿名的,编译器会简单地生成一个数字作为其标识符。如果内部类嵌套在别的内部类之中,只需直接将它们的名字在外部类标识符与 $ 的后面。

Java 内部类详解相关推荐

  1. [转] Java内部类详解

    作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置 ...

  2. 接口与继承系列教材 (十)- Java 内部类详解

    接口与继承系列教材 (十)- Java 内部类详解 内部类分为四种: 非静态内部类 静态内部类 匿名类 本地类 步骤1:非静态内部类 步骤2:静态内部类 步骤3:匿名类 步骤4:本地类 步骤5:在匿名 ...

  3. Java内部类详解(使用场景和好处、相关内部类的笔试面试题)

    此篇文章作者为:Matrix海子 出处:http://www.cnblogs.com/dolphin0520/ 最近也是在学习java,看的是<java核心技术卷一>jdk8,看到内部类的 ...

  4. (转)java内部类详解

    本文转自http://www.cnblogs.com/dolphin0520/p/3811445.html,谢谢作者 说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能 ...

  5. Java内部类详解(转)

    说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就来一探究竟.下面是本 ...

  6. Java提高篇 —— Java内部类详解

    一.简介 内部类是一个非常有用的特性但又比较难理解使用的特性. 内部类我们从外面看是非常容易理解的,无非就是在一个类的内部在定义一个类. public class OuterClass {privat ...

  7. android访问静态内部类,Java 内部类详解

    一 内部类是什么 Java类中不仅可以定义变量和方法,还可以定义类,这样定义在类内部的类就被称为内部类.根据定义的方式不同,内部类分为静态内部类,成员内部类,局部内部类,匿名内部类四种. Java为什 ...

  8. Java——内部类详解

    说起内部类,大家肯定感觉熟悉又陌生,因为一定在很多框架源码中有看到别人使用过,但又感觉自己使用的比较少,今天我就带你具体来看看内部类. 内部类基础 所谓内部类就是在类的内部继续定义其他内部结构类. 在 ...

  9. Java内部类详解(含:成员内部类、局部内部类、匿名内部类、静态内部类)

    内部类是什么? 字面意思,在类的内部编写的类就叫内部类!即一个类的内部又完整的嵌套了另一个类结构,被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class). ...

最新文章

  1. WPF绘制自定义窗口
  2. Android基于IIS的APK下载(五)IIS的配置
  3. python send 案例_python socket编程入门(编写server实例)+send 与sendall的区别与使用方法...
  4. HDOJ HDU 2058 The sum problem ACM 2058 IN HDU
  5. BZOJ 3144 [Hnoi2013]切糕
  6. 2018-11-01 专栏一岁了-我为什么投身于普及用中文编程
  7. php在线考试系统模板下载,PHPEMS在线模拟考试系统 v6.1
  8. Sublime Text 3 添加当前时间的制作方法
  9. 神州信息预计2020年全年净利润4.35亿至6亿元
  10. 你可能不知道的Chrome-devtools用法总结
  11. 河南科技大学计算机专业专升本,河南科技大学成人高考计算机应用技术专业的课程有哪些...
  12. 重装mysql 失败_重装MySQL最后一步失败的解决办法
  13. 受力分析软件_管桁架结构的受力特点是什么?如何计算?
  14. 类的说明补充,对象的学习
  15. 基于Qiskit——《量子计算编程实战》读书笔记(一)
  16. 简述网卡的作用和工作原理_网卡的工作原理(要简短的)
  17. 对比阿里云服务器和腾讯云服务器两者的不同之处
  18. angular7.0+ngx-weui公众号开发,开发及框架搭建(一)
  19. 刨根问底学Blog(转)
  20. python获取命令行输出_python获取命令行输出结果

热门文章

  1. XTU 1256 湘潭大学
  2. 语法俱乐部2:名词短语与冠词
  3. 给中国学生的第三封信:成功、自信、快乐
  4. 【远程桌面控制】远程控制其软件 对比
  5. 使用高德地图绘制矩形网格,显示行政区域。
  6. Flutter进阶—通用布局控件
  7. UMLChina建模答题赛第二赛季最终排行榜-刘京城、第五元素、龙龙
  8. 如何设计一个通用的查询接口
  9. Android API Level一览表
  10. 期刊论文和会议论文的区分与识别