目录
一、内部类基本介绍
(一)内部类定义
(二)内部类基本语法
(三)内部类代码示例
(四)内部类的分类
二、局部内部类
三、匿名内部类(重要)
(一)基本介绍
(二)基于接口的匿名内部类
(三)基于类的匿名内部类
(四)注意事项与使用细节
(五)匿名内部类的最佳实践——当做实参直接传递
(六)小练习
四、成员内部类
1.成员内部类是定义在外部类的成员位置,并且没有static修饰。
2.成员内部类可以添加任意的访问修饰符,因为它的地位就是一个成员
3.成员内部类可以直接访问外部类的所有成员,包含私有的
4.作用域:成员内部类的作用域为整个类体
6.外部类和成员内部类的成员重名访问规则
五、静态内部类
1.静态内部类是定义在外部类的成员位置,并且有static修饰
2.可以添加任意访问修饰符,因为它的地位就是一个成员
3.可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
4.作用域:同其他的成员一样,为整个类体
5.外部其他类访问静态内部类的三种方式
6.如果外部类和静态内部类的成员重名访问规则
7.小练习
一、内部类基本介绍
(一)内部类定义
一个类的内部,又完整地嵌套了另一个类结构,被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。内部类最大的特点就是,可以直接访问私有属性,并且可以体现类与类之间的包含关系。内部类也是重点内容,因为底层源码中有大量内部类。
类的五大成员:属性、方法、构造器、代码块、内部类。
(二)内部类基本语法
class Outer{ // 外部类
class Inner{ // 内部类
}
}
class Other{ // 外部其它类
}
(三)内部类代码示例
// 外部类
class Outer {
// 属性
private int n1 = 10;
// 构造器
public Outer(int n1) {
this.n1 = n1;
}
// 方法
public void hi() {}
// 代码块
{
System.out.println("这是一个代码块...");
}
// 内部类,在Outer类的内部
class Inner {}
}
// 外部其它类
class Other {}
(四)内部类的分类
- 定义在外部类局部位置上(比如方法内):
- 局部内部类(有类名)
- 匿名内部类(没有类名,非常常用!)
- 定义在外部类的成员位置上:
- 成员内部类(没有static修饰)
- 静态内部类(使用static修饰)
二、局部内部类
1.局部内部类定义在外部类的局部位置,比如方法体,并且有类名。
class Outer02 {
public void m1() {
class Inner02 { // 局部内部类可以定义在方法中
}
}
{ // 局部内部类也可以定义在代码块中
class Inner03{}
}
}
2.局部内部类的作用域:仅在定义它的方法或代码块中
3.局部内部类可以直接访问外部类的所有成员,包含私有的。
class Outer02 {
private int n1 = 100;
private void m2() {
System.out.println("m2方法被执行...");
}
public void m1() {
class Inner02 { // 局部内部类
public void f1() {
// 局部内部类可以直接访问外部类的私有属性
System.out.println("n1=" + n1);
// 局部内部类可以直接访问外部类的私有方法
m2();
}
}
}
}
4.外部类访问局部内部类的成员,必须先创建局部内部类对象,再调用方法
注意:必须在作用域中。
public class LocalInnerClass {
public static void main(String[] args) {
Outer02 outer02 = new Outer02();
outer02.m1();
// f2方法被执行...
// n1=100
// m2方法被执行...
}
}
class Outer02 {
private int n1 = 100;
private void m2() {
System.out.println("m2方法被执行...");
}
public void m1() {
class Inner02 { // 局部内部类
public void f1() {
System.out.println("n1=" + n1);
m2();
}
}
Inner02 inner02 = new Inner02();
inner02.f1();
}
{
class Inner03 {
public void f2() {
System.out.println("f2方法被执行...");
}
}
Inner03 inner03 = new Inner03();
inner03.f2();
}
}
5.局部内部类不能添加访问修饰符,因为它本质上就是一个局部变量,且仍然是一个类。
局部变量是不能使用修饰符的,只有属性可以。但是可以使用final修饰,因为局部变量也可以使用final,当使用final修饰局部内部类后,表示该局部内部类不能被继承,是一个最终类。
6.外部其它类不能访问局部内部类,因为局部内部类的地位,是一个局部变量
7.如果外部类和局部内部类的成员重名时,默认遵循就近原则;
如果想访问外部类的成员,就要使用 外部类名.this.成员去访问
public class LocalInnerClass {
public static void main(String[] args) {
Outer02 outer02 = new Outer02();
// Outer02.this 本质就是外部类的对象
// 即:哪个对象调用了局部内部类所在的m1方法,Outer02.this就是哪个对象
System.out.println("Outer02 hashCode=" + outer02); // 4554617c
outer02.m1();
}
}
class Outer02 {
private int n1 = 100;
private void m2() {
System.out.println("m2方法被执行...");
}
public void m1() {
class Inner02 { // 局部内部类
private int n1 = 900;
public void f1() {
// Outer02.this 本质就是外部类的对象
// 即:哪个对象调用了局部内部类所在的m1方法,Outer02.this就是哪个对象
System.out.println("Outer02.this hashCode=" + Outer02.this);// 4554617c
System.out.println("n1=" + n1 + " 外部类的n1=" + Outer02.this.n1);
m2();
}
}
Inner02 inner02 = new Inner02();
inner02.f1();
}
}
三、匿名内部类(重要)
(一)基本介绍
定义:匿名内部类是定义在外部类的局部位置,比如方法、代码块中,并且没有类名。
匿名内部类特点:
- 本质是类
- 是一个内部类
- 该类没有名字(代码中虽然无法看到名字,但实际上JVM会给它分配一个名字)
- 同时还是一个对象
基本语法:
new 类或接口(参数列表){
类体
};
(二)基于接口的匿名内部类
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer04 outer04 = new Outer04();
outer04.method();
}
}
class Outer04 {
private int n1 = 10;
public void method() {
// 基于接口的匿名内部类
// 1.需求:想使用IA接口,并创建对象
// 2.传统方式:写一个类,实现该IA接口,并创建对象
// 3.但是创建的类只是使用一次,后面不再使用,有些浪费和繁琐
// 4.可以使用匿名内部类来简化开发
// Tiger tiger = new Tiger();
// tiger.cry();
// 5.我们知道,接口是不能被new的,但是后面的{}里面的内容,相当与实现了接口中的方法
// 6.tiger的编译类型:IA
// 7.tiger的运行类型:就是匿名内部类,系统分配的Outer04$1
/*
* 底层是会创建一个类 Outer04$1 实现IA接口,即:
class Outer04$1 implements IA {
@Override
public void cry() {
System.out.println("老虎叫唤...");
}
}
*/
// 8.JDK底层在创建匿名内部类 Outer04$1,立即就创建了 Outer04$1对象
// 并且把地址返回给tiger后,该匿名内部类Outer04$1 就立即消失了
// 9.匿名内部类只能使用一次,但是tiger是一个对象,可以反复调用
IA tiger = new IA() {
@Override
public void cry() {
System.out.println("老虎叫唤...");
}
};
// 使用getClass()获取对象的运行类型:
System.out.println("tiger的运行类型="+tiger.getClass()); // Outer04$1
tiger.cry();
tiger.cry();
tiger.cry();
}
}
interface IA {
void cry();
}
/*
传统方式:定义类实现IA接口:
class Tiger implements IA {
@Override
public void cry() {
System.out.println("老虎叫唤...");
}
}*/
(三)基于类的匿名内部类
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer04 outer04 = new Outer04();
outer04.method();
}
}
class Outer04 {
private int n1 = 10;
public void method() {
// 基于类的匿名内部类
// 分析:
// 1.father的编译类型:Father
// 2.father的运行类型:系统按照顺序分配的 Outer04$2
// 3.底层会创建匿名内部类:
/*
class Outer04$2 extends Father{
@Override
public void test() {
System.out.println("匿名内部类重写了test方法...");
}
}
*/
// 4.同时也直接返回了 匿名内部类 Outer04$2的对象,即:father
// 5.注意:("jack")参数列表会传递给Father构造器
Father father = new Father("jack") {
@Override
public void test() {
System.out.println("匿名内部类重写了test方法...");
}
};
System.out.println("father对象的运行类型:" + father.getClass()); // Outer04$2
father.test(); // 匿名内部类重写了test方法...
father.test(); // 匿名内部类重写了test方法...
father.test(); // 匿名内部类重写了test方法...
// 6.注意:如果new Father("jack")后面没有{},那么father的运行类型就是Father
Father father1 = new Father("jack");
System.out.println("father1对象的运行类型:" + father1.getClass()); // Father
//-----------------------------------------------------------------------------------
// 基于抽象类的匿名内部类
// 1.animal编译类型:Animal
// 2.animal的运行类型:系统按照顺序分配的 Outer04$3
// 3.底层会创建匿名内部类:
/*
* 底层是会创建一个类 Outer04$3 实现IA接口,即:
class Outer04$3 implements Animal {
@Override
public void eat() {
System.out.println("小狗吃骨头...");
}
}
*/
Animal animal = new Animal() {
// 这里要注意:因为Animal中的eat方法是抽象的,所以eat方法必须被实现
@Override
void eat() {
System.out.println("小狗吃骨头...");
}
};
System.out.println("animal的运行类型:" + animal.getClass()); // Outer04$3
animal.eat(); // 小狗吃骨头...
animal.eat(); // 小狗吃骨头...
animal.eat(); // 小狗吃骨头...
}
}
interface IA {
void cry();
}
class Father {
public Father(String name) {
System.out.println("接收到name=" + name);
}
public void test() {}
}
abstract class Animal {
abstract void eat();
}
(四)注意事项与使用细节
- 匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象。因此从语法上看,它既有定义类的特征,也有创建对象的特征,因此可以调用匿名内部类方法。
- 匿名内部类可以直接访问外部类的所有成员,包含私有的。
- 匿名内部类不能添加访问修饰符,因为它的地位就是一个局部变量。
- 作用域:匿名内部类的作用域仅在定义它的方法或代码块中。
- 外部其它类不能访问匿名内部类,因为匿名内部类地位是一个局部变量。
- 如果外部类和匿名内部类成员重名时,匿名内部类访问的话,默认就近原则,如果想访问外部类成员,则可以使用(外部类名.this.成员)去访问。
public class AnonymousInnerClassDetail {
public static void main(String[] args) {
Outer05 outer05 = new Outer05();
outer05.f1();
//外部其他类---不能访问----->匿名内部类
System.out.println("main outer05 hashcode=" + outer05); // 4554617c
}
}
class Outer05 {
private int n1 = 99;
public void f1() {
//创建一个基于类的匿名内部类
//不能添加访问修饰符,因为它的地位就是一个局部变量
//作用域 : 仅仅在定义它的方法或代码块中
Person p = new Person() {
private int n1 = 88;
@Override
public void hi() {
//可以直接访问外部类的所有成员,包含私有的
//如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,
//默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问
System.out.println("匿名内部类重写了 hi方法 n1=" + n1 +
" 外部内的n1=" + Outer05.this.n1);
// 匿名内部类重写了 hi方法 n1=88 外部内的n1=99
//Outer05.this 就是调用 f1的 对象
System.out.println("Outer05.this hashcode=" + Outer05.this); // 4554617c
}
};
/**
* 调用匿名内部类方式一:动态绑定
*/
p.hi();//动态绑定, 运行类型是 Outer05$1
/**
* 调用匿名内部类方式二:直接调用
*/
//也可以直接调用, 匿名内部类本身也是返回对象
// class 匿名内部类 extends Person {}
new Person() {
@Override
public void hi() {
System.out.println("匿名内部类重写了 hi方法,哈哈...");
}
@Override
public void ok(String str) {
super.ok(str);
}
}.ok("jack");
}
}
class Person {//类
public void hi() {
System.out.println("Person hi()");
}
public void ok(String str) {
System.out.println("Person ok() " + str);
}
}
(五)匿名内部类的最佳实践——当做实参直接传递
将匿名内部类当做实参传递,简洁高效。
代码示例:
传统方式实现接口中的方法:
public class InnerClassExercise01 {
public static void main(String[] args) {
f1(new Picture());
// 传统方式实现接口中的方法...
}
public static void f1(IL il) {
il.show();
}
}
// 接口
interface IL {
void show();
}
// 定义类Picture实现IL接口
// 类->实现IL=>编程领域(硬编码)
class Picture implements IL {
@Override
public void show() {
System.out.println("传统方式实现接口中的方法...");
}
}
使用匿名内部类传参:
public class InnerClassExercise02 {
public static void main(String[] args) {
f1(new IL() {
@Override
public void show() {
System.out.println("匿名内部类作为参数传递的方法...");
}
});
}
public static void f1(IL il) {
il.show();
}
}
interface IM {
void show();
}
相比与传统定义类实现接口中的方法的硬编码方式,将匿名内部类作为参数传递的方式,更加简洁高效,前提是该抽象方法只使用一次。
(六)小练习
需求:
- 有一个铃声接口Bell,里面有个ring方法。
- 有一个手机类Cellphone,具有闹钟功能alarmClock,参数是Bell类型
- 测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了
- 再传入另一个匿名内部类(对象),打印:小伙伴上课了
代码实现:
public class InnerClassExercise04 {
public static void main(String[] args) {
CellPhone cellPhone = new CellPhone();
//1. 传递的是实现了 Bell接口的匿名内部类 InnerClassExercise02$1
//2. 重写了 ring
//3. Bell bell = new Bell() {
// @Override
// public void ring() {
// System.out.println("懒猪起床了");
// }
// }
cellPhone.alarmClock(new Bell01() {
@Override
public void ring() {
System.out.println("懒猪起床了");
}
});
cellPhone.alarmClock(new Bell01() {
@Override
public void ring() {
System.out.println("小伙伴上课了");
}
});
}
}
interface Bell { //接口
void ring();//方法
}
class CellPhone {//类
public void alarmClock(Bell01 bell01) {//形参是Bell接口类型
System.out.println(bell01.getClass());
bell01.ring();//动态绑定
}
}
运行结果:
四、成员内部类
1.成员内部类是定义在外部类的成员位置,并且没有static修饰。
public class MemberInnerClass01 {
public static void main(String[] args) {
}
}
class Outer08 { // 外部类
private int n1 = 10;
public String name = "张三";
private void hi() {
System.out.println("hi()方法...");
}
//1.注意: 成员内部类,是定义在外部内的成员位置上
class Inner08 { // 成员内部类
}
}
2.成员内部类可以添加任意的访问修饰符,因为它的地位就是一个成员
访问修饰符:public、protected、默认、private
3.成员内部类可以直接访问外部类的所有成员,包含私有的
public class MemberInnerClass01 {
public static void main(String[] args) {
Outer08 outer08 = new Outer08();
outer08.t1();
// n1 = 10 name = 张三 外部类的n1=10
// hi()方法...
}
}
class Outer08 { // 外部类
private int n1 = 10;
public String name = "张三";
private void hi() {
System.out.println("hi()方法...");
}
class Inner08 { // 成员内部类
public void say() {
// 4.成员内部类可以直接访问外部类的所有成员,包含私有的
System.out.println("n1 = " + n1 + " name = " + name);
hi();
}
}
public void t1() {
Inner08 inner08 = new Inner08();
inner08.say();
}
}
4.作用域:成员内部类的作用域为整个类体
成员内部类的作用域和外部类的其他成员一样,为整个类体。
外部类方位成员内部类:在外部类的成员方法中,创建成员内部类对象,再调用相关的方法。
public class MemberInnerClass01 {
public static void main(String[] args) {
Outer08 outer08 = new Outer08();
outer08.t1();
// n1 = 10 name = 张三 外部类的n1=10
// hi()方法...
// 400.56
}
}
class Outer08 { // 外部类
private int n1 = 10;
public String name = "张三";
private void hi() {
System.out.println("hi()方法...");
}
class Inner08 { // 成员内部类
private double salary = 400.56;
private void say() {
System.out.println("n1 = " + n1 + " name = " + name);
hi();
}
}
// 3.外部类访问成员内部类:
// 说明:在外部类的成员方法中创建成员内部类对象,再使用相关的方法
public void t1() {
Inner08 inner08 = new Inner08();
inner08.say();
System.out.println(inner08.salary);
}
}
5.外部其它类访问成员内部类的两种方式
- 方式一:new 成员内部类(); 将成员内部类当做一个普通的成员来访问,即:对象.成员
- 方式二:在外部类中定义一个方法,返回一个成员内部类实例
public class MemberInnerClass01 {
public static void main(String[] args) {
// 外部其它类访问成员内部类的两种方式
// 方式一:new Inner08(); 将成员内部类当做一个普通的成员来访问,即:对象.成员
Outer08 outer08 = new Outer08();
Outer08.Inner08 inner08 = outer08.new Inner08();
inner08.say();
// 方式二:在外部类中定义一个方法,返回一个Inner08实例
Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
inner08Instance.say();
}
}
class Outer08 { // 外部类
private int n1 = 10;
public String name = "张三";
private void hi() {
System.out.println("hi()方法...");
}
public class Inner08 { // 成员内部类
private double salary = 400.56;
public void say() {
System.out.println("n1 = " + n1 + " name = " + name);
hi();
}
}
// 在外部类中定义一个方法,返回一个Inner08实例
public Inner08 getInner08Instance() {
return new Inner08();
}
}
运行结果:
6.外部类和成员内部类的成员重名访问规则
如果外部类和成员内部类的成员重名时,成员内部类访问的时候,默认就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
class Outer08 { // 外部类
private int n1 = 10;
public String name = "张三";
private void hi() {
System.out.println("hi()方法...");
}
public class Inner08 { // 成员内部类
private double salary = 400.56;
private int n1 = 66;
public void say() {
// 6.如果外部类和成员内部类的成员重名时,成员内部类访问的时候,默认就近原则,
// 如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
System.out.println("n1 = " + n1 + " name = " + name +
" 外部类的n1=" + Outer08.this.n1);
hi();
}
}
}
五、静态内部类
1.静态内部类是定义在外部类的成员位置,并且有static修饰
class Outer01{
private int n1 = 10;
private static String name = "张三";
static class Inner10{ // 静态内部类
}
}
2.可以添加任意访问修饰符,因为它的地位就是一个成员
访问修饰符:public、protected、默认、private
class Outer01{
private int n1 = 10;
private static String name = "张三";
public static class Inner10{ // 静态内部类
}
}
3.可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
class Outer01 {
private int n1 = 10;
private static String name = "张三";
private static void m1() {
System.out.println("外部类的静态方法被执行...");
}
public static class Inner10 { // 静态内部类
public void say() {
System.out.println(name);
m1();
}
}
}
4.作用域:同其他的成员一样,为整个类体
外部类访问静态内部类:要先创建对象,再访问
public class StaticInnerClass01 {
public static void main(String[] args) {
Outer10 outer10 = new Outer10();
outer10.t1();
// 张三
// 外部类的静态方法被执行...
}
}
class Outer10 {
private int n1 = 10;
private static String name = "张三";
private static void m1() {
System.out.println("外部类的静态方法被执行...");
}
public static class Inner10 { // 静态内部类
public void say() {
System.out.println(name);
m1();
}
}
public void t1(){
Inner10 inner10 = new Inner10();
inner10.say();
}
}
5.外部其他类访问静态内部类的三种方式
public class StaticInnerClass01 {
public static void main(String[] args) {
// 外部其他类 使用静态内部类
// 方式一:
// 静态内部类,是可以通过类名直接访问(前提是满足访问权限)
Outer10.Inner10 inner10 = new Outer10.Inner10();
inner10.say();
System.out.println("-------------------");
// 方式二:
// 编写一个方法,可以返回静态内部类的对象实例.
Outer10 outer10 = new Outer10();
Outer10.Inner10 inner101 = outer10.getInner10();
inner101.say();
System.out.println("-------------------");
// 方式三:
// 外部类.静态方法
Outer10.Inner10 inner10_ = Outer10.getInner10_();
inner10_.say();
}
}
class Outer10 {
private int n1 = 10;
private static String name = "张三";
private static void m1() {
System.out.println("外部类的静态方法被执行...");
}
public static class Inner10 { // 静态内部类
public void say() {
System.out.println(name);
m1();
}
}
public Inner10 getInner10() {
return new Inner10();
}
public static Inner10 getInner10_() {
return new Inner10();
}
}
6.如果外部类和静态内部类的成员重名访问规则
如果外部类和静态内部类的成员重名时,静态内部类访问的时候,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问。
public class StaticInnerClass01 {
public static void main(String[] args) {
Outer10 outer10 = new Outer10();
outer10.t1();
// 静态内部类 name=李四 外部类 name=张三
// 静态内部类的静态方法被执行...
// 外部类的静态方法被执行...
}
}
class Outer10 {
private int n1 = 10;
private static String name = "张三";
private static void m1() {
System.out.println("外部类的静态方法被执行...");
}
public static class Inner10 { // 静态内部类
private static String name = "李四";
private static void m1() {
System.out.println("静态内部类的静态方法被执行...");
}
public void say() {
System.out.println("静态内部类 name=" + Inner10.name +
" 外部类 name=" + Outer10.name);
m1(); // 或者是Inner10.m1();
Outer10.m1();
}
}
public void t1() {
Inner10 inner10 = new Inner10();
inner10.say();
}
}
7.小练习
判断下面代码的输出结果:
public class InnerClassExercise {
public static void main(String[] args) {
Test t = new Test();
Test.Inner r = t.new Inner();// 5
System.out.println(r.a);// 5
}
}
class Test {// 外部类
public Test() {// 构造器
Inner s1 = new Inner();
s1.a = 10;
Inner s2 = new Inner();
System.out.println(s2.a);
}
class Inner { // 成员内部类
public int a = 5;
}
}