文章目录
一、什么是Java内部类?
在类内部可定义成员变量和方法,且在类内部也可以定义另一个类。如果在类 Outer 的内部再定义一个类 Inner,此时类 Inner 就称为内部类(或称为嵌套类),而类 Outer 则称为外部类(或称为宿主类)
内部类可以很好地实现隐藏,一般的非内部类是不允许有 private 与 protected 权限的,但内部类可以。内部类拥有外部类的所有元素的访问权限。
在类 A 中定义类 B,那么类 B 就是内部类,也称为嵌套类,相对而言,类 A 就是外部类。如果有多层嵌套,例如类 A 中有内部类 B,而类 B 中还有内部类 C,那么通常将最外层的类称为顶层类(或者顶级类)。
内部类可以分为:成员内部类(包括实例内部类、静态内部类)和局部内部类(匿名内部类),如图所示:
另外,也会介绍局部内部类中的的匿名内部类.每种内部类都有它特定的一些特点,下面我们就一一来讲解!
1.1内部类特点
1.内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,冠以外部类的类名和$符号(Outer $ Inner.class)。
2.内部类不能用普通的方式访问。内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否为 private 的。
3.内部类声明成静态的,就不能随便访问外部类的成员变量,仍然是只能访问外部类的静态成员变量。
1.2有关内部类的说明
1.外部类只有两种访问级别:public 和默认;内部类则有 4 种访问级别:public、protected、 private 和默认。
2.在外部类中可以直接通过内部类的类名访问内部类。
3.在外部类以外的其他类中则需要通过内部类的完整类名访问内部类。
4.内部类与外部类不能重名。
提示:内部类的很多访问规则可以参考变量和方法。另外使用内部类可以使程序结构变得紧凑,但是却在一定程度上破坏了 Java 面向对象的思想。
二、实例内部类
实例内部类是指没有用 static 修饰的内部类,有的地方也称为非静态内部类。示例代码如下:
public class Outer {
// 实例内部类
class Inner {
}
}
2.1重要特点
1.在实例内部类中不能定义 static 成员,除非同时使用 final 和 static 定义一个静态常量。
2.外部类实例与内部类实例是一对多的关系,也就是说一个内部类实例只对应一个外部类实例,而一个外部类实例则可以对应多个内部类实例。
3.在外部类的静态方法和外部类以外的其他类中,必须通过外部类的实例创建内部类的实例。
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 {
//要先有一个外部类对象的引用new Outer
Outer.Inner1 i = new Outer().new Inner1(); // 需要创建外部类实例
}
4.在实例内部类中,可以访问外部类的所有成员。如果实例内部类InnerClass 与外部类 OuterClass包含有同名的成员 t,则在类InnerClass 中 t 和 this.t 都表示 InnerClass 中的成员 t,而OuterClass.this.t 表示OuterClass 中的成员 t。
class OuterClass {
public int data1 = 1;
private int data2 = 2;
public static int data3 = 3;
class InnerClass {
public int data1 = 999999;
public int data4 = 4;
//不能定义静态成员变量,但是可以定义一个静态常量
public static final int data6 = 6;
public void test() {
//如何在实例内部类当中访问和外部类同名的变量
System.out.println(OuterClass.this.data1);
System.out.println(this.data1);
System.out.println(data2);
System.out.println(data3);
System.out.println(data4);
System.out.println(data6);
}
}
三、静态内部类
静态内部类是指使用 static 修饰的内部类。示例代码如下:
public class Outer {
// 静态内部类
static class Inner {
}
}
3.1重要特性
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 {
// 局部内部类
}
}
}
4.1重要性质
局部内部类有如下特点:
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 类型的参数与变量。如果方法中的成员与外部类中的成员同名,则可以使用 .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
}
public static void main(String[] args) {
Test t = new Test();
t.method();
}
}
4.2匿名内部类
匿名内部类是局部内部类的一种,因为该类没有名字,所以称为匿名内部类。匿名内部类只能使用一次,它通常用来简化代码编写。
匿名类是指没有类名的内部类,必须在创建时使用 new 语句来声明类。其语法形式如下:
new <类或接口>() {
// 类的主体
};
这种形式的 new 语句声明一个新的匿名类,它对一个给定的类进行扩展或者实现一个给定的接口。使用匿名类可使代码更加简洁、紧凑,模块化程度更高。
4.1前提条件
使用匿名类的前提:
继承一个类,重写其方法。
实现一个接口(可以是多个),实现其方法。
代码实例如下:
public class Out {
void show() {
System.out.println("调用 Out 类的 show() 方法");
}
}
public class TestAnonymousInterClass {
// 在这个方法中构造一个匿名内部类
private void show() {
// 获取匿名内部类的实例
Out anonyInter =new Out() {
@Override
void show() {
System.out.println("我是重写后外部类的show() 方法");
}
};
anonyInter.show();
}
public static void main(String[] args) {
TestAnonymousInterClass test = new TestAnonymousInterClass();
test.show();
}
}
程序的输出结果如下:
我是重写后外部类的 show() 方法
从输出结果可以看出,匿名内部类有自己的实现。
提示:匿名内部类实现一个接口的方式与实现一个类的方式相同
4.2特点
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();
}
2)匿名类中允许使用非静态代码块进行成员初始化操作。
Out anonyInter = new Out() {
int i; { // 非静态代码块
i = 10; //成员初始化
}
public void show() {
System.out.println("调用了匿名类的 show() 方法"+i);
}
};
3)匿名类的非静态代码块会在父类的构造方法之后被执行。