1. 什么是内部类
类的五大成员:
属性、方法、构造方法、代码块、内部类
在一个类的里面,再定义一个类。
例如:在A类的内部定义B类,B类就被称为内部类,A就相应的被称为外部类
2. 为什么要学习内部类
内部类表示的事物是外部类的一部分
内部类单独出现没有任何意义
内部类的访问特点:
① 内部类可以直接访问外部类的成员,包括私有
② 外部类要访问内部类的成员,必须创建对象
3. 内部类的分类
① 成员内部类
② 静态内部类
③ 局部内部类
④ 匿名内部类
前三种一般只会在看源代码时看到,自己在通常情况下是不会写的
需要重点掌握的是成员内部类
成员内部类
写在成员位置的,属于外部类的成员
例如:
public class Car{ // 外部类
String carName;
int carAge;
int carColor;
class Engine{ // 成员内部类
String engineName;
int engineAge;
}
}
① 成员内部类的代码如何书写
a. 跟成员一样,可以被权限修饰符和static等修饰
b. 成员内部类里面在jdk16之前是不能定义静态变量的,在jdk16开始才能定义
② 如何创建成员内部类的对象
a. 方式一:在外部类中编写方法,对外提供内部类的对象
b. 方式二:直接创建格式:外部类名.内部类名 对象名 = new 外部类对象.new 内部类对象;
③ 成员内部类如何获取外部类的成员变量
package innerclass;
public class Outer {
private int a = 10;
class Inner{
private int a = 20;
public void show(){
int a = 30;
System.out.println(a); // 30
System.out.println(this.a); // 20
System.out.println(Outer.this.a); // 10
}
}
}
编译后生成的.class文件的内部类:
class Outer$Inner {
private int a;
Outer$Inner(Outer var1) {
this.this$0 = var1;
this.a = 20;
}
public void show() {
byte var1 = 30;
System.out.println(var1);
System.out.println(this.a);
System.out.println(this.this$0.a);
}
}
外部类:
public class Outer {
private int a = 10;
public Outer() {
}
class Inner {
private int a = 20;
Inner() {
}
public void show() {
byte var1 = 30;
System.out.println(var1);
System.out.println(this.a);
System.out.println(Outer.this.a);
}
}
}
调用外部类属性使用Outer.this.a的原因:
在非静态内部类的隐藏着final的Outer类型的属性 this$0
可是隐藏的属性是不能被显示调用的,也就是说不能使用this.this$0这种方式调用
在官方文档上https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.8.4
Any lexically enclosing instance (§8.1.3) can be referred to by explicitly qualifying the keyword this
.
任何词法封闭的实例(第 8.1.3 节)都可以通过显式限定关键字 this 来引用。
Let T be the type denoted by TypeName. Let n be an integer such that T is the n'th lexically enclosing type declaration of the class or interface in which the qualified this
expression appears.
令 T 为由 TypeName 表示的类型。令 n 为整数,使得 T 是出现限定 this 表达式的类或接口的第 n 个词法封闭类型声明。
The value of an expression of the form TypeName.
this
is the n'th lexically enclosing instance of this
.
TypeName.this 形式的表达式的值是 this 的第 n 个词法封闭实例。
The type of the expression is T.
表达式的类型是 T。
It is a compile-time error if the expression occurs in a class or interface which is not an inner class of class T or T itself.
如果表达式出现在不是类 T 或 T 本身的内部类的类或接口中,则是编译时错误。
代码里的类型是Outer,所以用显示Outer.this来表示Outer
静态内部类 静态内部类使用static修饰
静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问外部类中非静态的需要创建外部类对象
创建内部类对象:
外部类名.内部类名 对象名 = new 外部类名.内部类名();
调用非静态方法的格式:先创建对象,用对象调用
调用静态方法的格式:外部类名.内部类名.方法名();
局部内部类
1. 将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量
2. 外界是无法直接使用,需要在方法内部创建对象并使用
2. 该类可以直接访问外部类的成员,也可以访问方法内的局部变量
匿名内部类
什么是匿名内部类:
隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置
匿名内部类的语法规则:
new 父类构造器(参数列表)|实现接口() {
代码块 重写方法
}
格式细节:
包含了继承或实现,方法重写,创建对象。
整体就是一个类的子类对象或者接口的实现类对象。
注意:
① 匿名内部类我们必须要继承一个父类或者实现一个接口,当然也仅能只继承一个父类或者实现一个接口,且两者不可兼得
② 匿名内部类中不能定义构造函数
③ 匿名内部类中不能存在任何静态变量成员和静态方法
④ 匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效
⑤ 匿名内部类不能是抽象的,他必须要实现继承的类或者实现接口中所有的抽象方法
AnonymousInner.java
public class AnonymousInner {
public int anInt = 1;
/**
* 包含两个方法的HelloWorld接口
*
*/
interface HelloWord{
void greet();
}
protected HelloWord a = new HelloWord() {
@Override
public void greet() {
System.out.println(anInt);
}
};
public void sayHello(){
// 1. 局部内部类EnglishGreeting实现了HelloWorld接口
class EnglishGreeting implements HelloWord{
@Override
public void greet() {
System.out.println("EnglishGreeting");
}
}
HelloWord englishGreeting = new EnglishGreeting();
// 2. 匿名类实现HelloWold接口
HelloWord frenchGreeting = new HelloWord() {
@Override
public void greet() {
System.out.println("frenchGreeting");
}
}; // 需要分号
englishGreeting.greet();
frenchGreeting.greet();
this.a.greet();
}
}
AnonymousInnerTest.java
public class AnonymousInnerTest {
public static void main(String[] args) {
AnonymousInner anonymousInner = new AnonymousInner();
anonymousInner.sayHello();
}
}
执行javac AnonymousInner命令后生成的.class文件
AnonymousInner.class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
public class AnonymousInner {
public int anInt = 1;
protected AnonymousInner.HelloWord a = new AnonymousInner.HelloWord() {
public void greet() {
System.out.println(AnonymousInner.this.anInt);
}
};
public AnonymousInner() {
}
public void sayHello() {
class EnglishGreeting implements AnonymousInner.HelloWord {
EnglishGreeting() {
}
public void greet() {
System.out.println("EnglishGreeting");
}
}
new EnglishGreeting();
AnonymousInner.HelloWord var10000 = new AnonymousInner.HelloWord() {
public void greet() {
System.out.println("frenchGreeting");
}
};
}
interface HelloWord {
void greet();
}
}
AnonymousInner$1.class 对标AnonymousInner.java文件中的 属性a指向的内部类
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import AnonymousInner.HelloWord;
class AnonymousInner$1 implements HelloWord {
AnonymousInner$1(AnonymousInner var1) {
this.this$0 = var1;
}
public void greet() {
System.out.println(this.this$0.anInt);
}
}
AnonymousInner$1EnglishGreeting.class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import AnonymousInner.HelloWord;
class AnonymousInner$1EnglishGreeting implements HelloWord {
AnonymousInner$1EnglishGreeting(AnonymousInner var1) {
this.this$0 = var1;
}
public void greet() {
System.out.println("EnglishGreeting");
}
}
AnonymousInner$2.class 对标 AnonymousInner.java文件中frenchGreeting所指向的匿名内部类
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import AnonymousInner.HelloWord;
class AnonymousInner$2 implements HelloWord {
AnonymousInner$2(AnonymousInner var1) {
this.this$0 = var1;
}
public void greet() {
System.out.println("frenchGreeting");
}
}
AnonymousInner$HelloWord.class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
interface AnonymousInner$HelloWord {
void greet();
}
使用场景:
当方法的参数是接口或者类时,以接口为例,可以传递这个接口的实现类对象,如果实现类只要使用一次,就可以用匿名内部类见划代码