# 内部类详解
[TOC]
## 内部类基本概念
在程序开发中为了更加准确的描述结构体的作用,通常拥有各种嵌套结构。而程序**类**也是允许嵌套的!
内部类(内部定义普通类,抽象类,接口的统称)是指一种嵌套的结构关系,即在一个类的内部除了定义属性和方法外还可以定义一个类结构,这样的形式使得程序的结构定义更加灵活。
>[info]内部类是一种常见的嵌套结构,利用这样的结构可以使内部类与外部类共存,并且方便的进行使用操作的访问。内部类也可以进一步扩展到匿名内部类的使用,在jdk 1.8后所提供的Lambda表达式与方法引用也可以简化代码结构
**示例:**
~~~java
package lamda.dodoke.demo1;
class Outer {//外部类
private String msg = "Hello,World";//定义私有成员属性
public void fun() {//定义普通方法
Inner in = new Inner();//实例化内部类对象
in.print();//调用内部类方法
}
class Inner {//在Outer类的内部定义Inner内部类
public void print() {//定义内部类的方法
System.out.println(Outer.this.msg);//调用Outer类的属性
}
}
}
public class JavaDemo {//在一个Java文件中可以有多个类,但是只能有一个public修饰的类
public static void main(String[] args) {
Outer out = new Outer();//实例化外部类对象
out.fun();//执行外部类方法
}
}
~~~
准确来说,内部类会使一个类的内部充斥着其他的类结构,所以内部类在整体设计中最大的缺点就是破坏了良好的程序结构,造成代码结构的混乱。但他最大的优点在于可以方便的访问外部类的私有成员。所以我们使用内部类,更多的时候是希望**某一个类只为单独一个类服务**。
## 内部类相关注意点
* 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的`.class`文件,前面冠以外部类的类名和`$`符号。
* 内部类不能用普通的方式访问。内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否为 private 的。
* 内部类声明成静态的,就不能随便访问外部类的成员变量,仍然是只能访问外部类的静态成员变量。
## 成员内部类
成员内部类(实例内部类)是指没有用 static 修饰的内部类,有的地方也称为非静态内部类。示例如下:
~~~
public class Out {
class Inner {}
}
~~~
* 在外部类的静态方法和外部类以外的其他类中,必须通过外部类的实例创建内部类的实例。
~~~
public class Outer {
class Inner {}
Inner inner = new Inner(); // 不需要创建外部类实例
public void method1() {
Inner i = new Inner(); // 不需要创建外部类实例
}
public static void method2() {
Inner i = new Outer().new Inner(); // 需要创建外部类实例
}
}
~~~
* 在实例内部类中,可以访问外部类的所有成员。
~~~
public class Outer {
public int a = 100;
static int b = 100;
final int c = 100;
private int d = 100;
public String method3() {
return "实例方法";
}
public static String method4() {
return "静态方法";
}
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 in = new Outer().new Inner();
System.out.println(in.a2); // 输出 101
System.out.println(in.b2); // 输出 101
System.out.println(in.c2); // 输出 101
System.out.println(in.d2); // 输出 101
System.out.println(in.str1); // 输出实例方法
System.out.println(in.str2); // 输出静态方法
}
}
~~~
* 在外部类中不能直接访问内部类的成员,而必须通过内部类的实例去访问。如果类 A 包含内部类 B,类 B 中包含内部类 C,则在类 A 中不能直接访问类 C,而应该通过类 B 的实例去访问类 C。
* 外部类实例与内部类实例是一对多的关系,也就是说一个内部类实例只对应一个外部类实例,而一个外部类实例则可以对应多个内部类实例。
~~~
public class Outer {
int a = 10;
class Inner {
int a = 20;
int a1 = this.a;
int b3 = Outer.this.a;
}
}
~~~
* 在实例内部类中不能定义 static 成员,除非同时使用 final 和 static 修饰。
## 静态内部类
静态内部类是指使用 static 修饰的内部类。示例代码如下:
~~~
public class Outer {
static class Inner {} // 静态内部类
}
~~~
* 在创建静态内部类的实例时,不需要创建外部类的实例。
~~~
class OtherClass {
Outer.Inner oi = new Outer.Inner();
}
~~~
* 静态内部类中可以定义静态成员和实例成员。外部类以外的其他类需要通过完整的类名访问静态内部类中的静态成员,如果要访问静态内部类中的实例成员,则需要通过静态内部类的实例。
~~~
public class Outer {
static class Inner {
int a = 0;
static int b = 0;
}
}
class OtherClass {
Outer.Inner oi = new Outer.Inner();
int a2 = oi.a; // 访问实例成员
int b2 = Outer.Inner.b; // 访问静态成员
}
~~~
* 静态内部类可以直接访问外部类的静态成员,如果要访问外部类的实例成员,则需要通过外部类的实例去访问。
~~~
public class Outer {
int a = 0;
static int b = 0;
static class Inner {
Outer o = new Outer();
int a2 = o.b; // 访问实例变量
int b2 = b; // 访问静态变量
}
}
~~~
## 方法内部类
局部内部类(方法内部类)是指在一个方法中定义的内部类。示例代码如下:
~~~
public class Test {
public void method() {
class Inner {} // 局部内部类
}
}
~~~
* 局部内部类与局部变量一样,不能使用访问控制修饰符(public、private 和 protected)和 static 修饰符修饰。
* 局部内部类只在当前方法中有效。
~~~
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();
}
}
~~~
* 局部内部类中不能定义 static 成员。
* 局部内部类中还可以包含内部类,但是这些内部类也不能使用访问控制修饰符(public、private 和 protected)和 static 修饰符修饰。
* 在局部内部类中可以访问外部类的所有成员。
* 在局部内部类中可以直接访问当前方法中的参数与变量。如果方法中的成员与外部类中的成员同名,则可以使用`外部类.this.变量`的形式访问外部类中的成员。
~~~
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 {
int 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.println(i.d3); // 输出 0
}
}
~~~
## 匿名内部类
匿名类是指没有类名的内部类,必须在创建时使用 new 语句来声明类。其语法形式如下:
~~~
new <类或接口>() {
// ....
}
~~~
这种形式的 new 语句声明一个新的匿名类,它对一个给定的类进行扩展,或者实现一个给定的接口。使用匿名类可使代码更加简洁、紧凑,模块化程度更高。
匿名类有两种实现方式:
* 继承一个类,重写其方法。
* 实现一个接口(可以是多个),实现其方法
~~~
public class Out {
void show() {
System.out.println("调用 Out 类的 show() 方法");
}
}
public class Test {
public static void main(String[] args) {
Out o = new Out() {
void show() {
System.out.println("调用匿名类中的 show() 方法");
}
}
o.show(); // 调用匿名类中的 show() 方法
}
}
~~~
* 匿名类和局部内部类一样,可以访问外部类的所有成员。
~~~
public static void main(String[] args) {
int a = 10;
final b = 10;
Out o = new Out() {
void show() {
System.out.println(a); // 编译通过
System.out.println(b); // 编译通过
}
}
o.show();
}
~~~
* 匿名类中允许使用非静态代码块进行成员初始化操作。
~~~
Out o = new Out() {
int i;
{
i = 10;
}
public void show() {
System.out.println("i");
}
}
~~~
* 匿名类的非静态代码块会在父类的构造方法之后被执行。