10.9 内部类
10.9.1 四种内部类的介绍
-
基本介绍
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class), 嵌套其他类的类称为外部类(outer class)。
是我们类的第五大成员【思考:类的五大 成员是哪些?
属性、方法、构造器、代码块、内部类
】,内部类最大的特点就是可 以直接访问私有属性,并且可以体现类与类之间的包含关系,
注意:内部类是学习的 难点,同时也是重点,后面看底层源码时,有大量的内部类.
-
基本语法
class Outer{ //外部类 class Inner{//内部类 } } class Other{//外部其他类 }
-
快速入门案例 代码:
public class InnerClass01 {//外部其他类
public static void main(String[] args) {}
}
class Outer{
private int n1 =100;//属性
public Outer(int n1) {//构造器
this.n1 = n1;
}
public void m1(){//方法
System.out.println("m1");
}
{//代码块
System.out.println("代码块");
}
class Inner{//内部类,在Outer的内部
}
}
- 内部类的分类
- 定义在外部类局部位置上(比如方法内)
(1) 局部内部类(有类名)
(2) 匿名内部类(没有类名,重点!!) - 定义在外部类的成员位置上
(1) 成员内部类(没用static修饰)
(2) 静态内部类(使用static修饰)
源代码:InnerClass01.java
10.9.2 局部内部类的使用
-
局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
class Outer02{//外部类 public void m1(){//方法 class Inner02{ } } }
-
可以直接访问外部类的所有成员,包含私有的
class Outer02{//外部类 private int n1=100; private void m2(){ } public void m1(){ class Inner02{ //访问外部类的私有属性n1 public void f1(){ System.out.println(n1); m2();//访问外部类的私有方法m1() } } } }
-
不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final
class Outer02{//外部类 public void m1(){//方法 final String name="xxx"; final class Inner02{ } } }
10.9.3 匿名内部类的使用
(1)本质是类(2)内部类(3)该类没有名字 (4)同时还是一个对象
说明:匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名
-
匿名内部类的基本语法
new类或接口(参数列表){类体};
-
快速入门:
基于接口的匿名内部类:
-
需求:想使用IA接口,并创建对象
测试主方法:
public class AnonymouslnnerClass { public static void main(String[] args) { Outer04 outer04 = new Outer04(); outer04.method(); } }
-
传统方式,写一个类,实现接口,并创建对象(缺点:每次创建不同的对象都要再写一个类)
class Outer04{//外部类 public void method(){//方法 //创建对象 IA tiger1 =new Tiger1(); tiger1.cry(); } } interface IA{//接口 public void cry(); } class Tiger1 implements IA{ @Override public void cry() { System.out.println("老虎叫"); } }
-
增加需求,Tiger只是使用一次后面不再使用
-
可以使用匿名内部类来简化开发
class Outer04{//外部类 public void method(){//方法 //基于接口的匿名内部类 IA tiger2=new IA(){ @Override public void cry() { System.out.println("老虎叫"); } }; tiger2.cry(); } } interface IA{//接口 public void cry(); }
-
解读匿名内部类的底层原理
-
tiger2的编译类型:IA
-
tiger2的运行类型:是匿名内部类 Outer04$1
class Outer04{ public void method(){ IA tiger2=new IA(){ @Override public void cry() { System.out.println("老虎叫"); } }; tiger2.cry(); System.out.println("tiger2的运行类型是:"+tiger2.getClass());//tiger2的运行类型是:Outer04{ } }
-
底层
底层分配的名字:外部类名 $1 ( Outer04$1)
class Outer$1 implements IA{ @Override public void cry() { System.out.println("老虎叫"); } }
-
jdk底层在创建了匿名内部类,立即马上创建了匿名对象实例,并且把地址返回管理tiger2
-
匿名内部类使用一次就不能再使用
但是匿名内部类创建的实例对象是可以不断调用的
tiger2.cry(); tiger2.cry(); tiger2.cry();
-
基于类的匿名内部类:
-
基于普通类
class Father{ private String name; public Father(String name) {//构造器 this.name = name; } public void test(){ } }
class Outer04{//外部类 public void method(){ //演示基于类的匿名内部类 Father father = new Father("jack"){ }; System.out.println("father对象的运行类型:"+father.getClass()); }
-
father的编译类型:Father
-
father的运行类型: Outer04$2
-
底层创建匿名内部类,同时也直接返回了匿名内部类2的对象
class Outer04$2 entends Father{ }
-
匿名内部类
Father father = new Father("jack"){};
里的"jack"
,会传递给Father类的有参构造器public Father(String name) {...}
-
-
基于抽象类
-
匿名内部类必须去实现抽象类方法
abstract class Animal{ public abstract void eat(); } class Outer04{//外部类 public void method(){ // Animal dog = new Animal(){ @Override public void eat() { System.out.println("小狗吃骨头"); }; }
-
源代码:AnonymousInnerClass.java
匿名内部类的细节
-
-
匿名内部类的语法比较奇特,请大家注意。因为匿名内部类既是一个类的定义, 同时它本身也是一个对象。因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法。
class Person{ public Person(){ } public void hi(){ System.out.println("Person hi()"); }; public void ok(String str){ System.out.println("Person ok:"+str); } } class Outer05{ public void f1(){ Person person=new Person(){}; person.hi();//动态绑定 new Person(){ }.hi(); new Person(){ }.ok("tom"); } }
-
调用方法一
Person person=new Person(){}; person.hi();//动态绑定
-
调用方法二
new Person(){ }.hi(); new Person(){ }.ok("tom");
-
-
可以直接访问外部类的所有成员,包含私有的
-
不能添加访问修饰符,因为它的地位就是一个局部变量。
-
作用域:仅仅在定义它的方法或代码块中。
-
匿名内部类———访问————>外部类成员[访问方式:直接访问]
-
外部其他类——不能访问———>匿名内部类(因为匿名内部类地位是一个局部变量)
-
如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则, 如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
Outer05.this 就是调用 f1的对象
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 ); //Outer05.this 就是调用 f1的对象 System.out.println("Outer05.this hashcode=" + Outer05.this); } }; p.hi(); } }
源代码:AnonymouslnnerClassDetail.java
匿名内部类的最佳实践
-
当作实参直接传递,简洁高效
-
问题:在InnerClassExercise01类主方法里实现 调用f1()
//接口 interface IL{ void show(); }
public class InnerClassExercise01 { public static void main(String[] args) { } //静态方法,形参是接口类型 public static void f1(IL il){ il.show(); } }
-
传统方法:专门写一个Picture类传给f1,在Picture类里实现接口IL
public class InnerClassExercise01 { public static void main(String[] args) { //传统,专门写一个Picture类传给f1 f1(new Picture()); } //静态方法,形参是接口类型 public static void f1(IL il){ il.show(); } } //类 class Picture implements IL{ @Override public void show() { System.out.println("这是一幅名画..."); } }
-
使用匿名内部类当作实参直接传递,简洁高效
public class InnerClassExercise01 { public static void main(String[] args) { //当作实参直接传递,简洁高效 f1(new IL() { @Override public void show() { System.out.println("这是一幅名画..."); } } ); //静态方法,形参是接口类型 public static void f1(IL il){ il.show(); } }
源代码:InnerClassExercise01.java
-
-
课堂练习InnerClassExercise02.java
1.有一个铃声接口Bell,里面有个ring方法。
2.有一个手机类Cellphone,具有闹钟功能alarmclock,参数是Bell类型
3.测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了
4.再传入另一个匿名内部类(对象),打印:小伙伴上课了
答案:
public class InnerClassExercise02 {
public static void main(String[] args) {
Cellphone cellphone = new Cellphone();
//
//1.传递的是一个想象力Bell接口的匿名内部类
//2.匿名内部类里重写了ring()方法
//3.相当于
cellphone.alarmclock(new Bell() {
@Override
public void ring() {
System.out.println("懒猪起床了");
}
});
cellphone.alarmclock(new Bell() {
@Override
public void ring() {
System.out.println("小伙伴上课了");
}
});
}
}
interface Bell{
void ring();
}
class Cellphone{
public void alarmclock(Bell bell){//形参是接口类型
bell.ring();
}
}
10.9.4 成员内部类的使用
MemberlnnerClass01.java
-
成员内部类是定义在外部类的成员位置,并且没有static修饰。
class Outer08{//外部类 private int n1 =10; public String name="张三"; //成员内部类定义在外部类的成员位置上 class Inner08{}//成员内部类 }
-
可以直接访问外部类的所有成员,包含私有的
class Outer08{//外部类 private int n1 =10; public String name="张三"; class Inner08{//成员内部类 public void say(){ System.out.println(n1+name); } } }
-
可以添加任意访问修饰符(public, protected、默认、private),因为它的地位就是一个成员。
-
作用域 MemberlnnerClass01.java 和外部类的其他成员一样,为整个类体 比如前面案例,在外部类的成员方法中创 建成员内部类对象,再调用方法.
-
成员内部类———访问———>外部类成员(比如: 属性)[访问方式:直接访问]
-
外部类———访问——>成员内部类(说明) 访问方式:创建对象,再访问
class Outer08{//外部类 class Inner08{//成员内部类 public void say(){ System.out.println("say"); } } //写方法 public void t1(){ //使用成员内部类:创建对象,再访问 Inner08 inner08 = new Inner08(); inner08.say(); } }
-
外部其他类———访问———>成员内部类
-
创建一个外部类的对象去调用
因为 Inner08这个类实际上就是 Outer08的成员
Outer08.Inner08 inner08 = outer08.new Inner08();
public class MemberlnnerClass01 { public static void main(String[] args) { Outer08 outer08 = new Outer08();//创建外部类对象outer08 Outer08.Inner08 inner08 = outer08.new Inner08();//创建外部类成员Inner08的对象inner08 inner08.say(); } } class Outer08{//外部类 class Inner08{//成员内部类 public void say(){ System.out.println("say..."); } } }
-
在外部类中编写一个方法,可以返回一个Inner08的对象实例
public class MemberlnnerClass01 { public static void main(String[] args) { Outer08 outer08 = new Outer08();//创建外部类对象outer08 Outer08.Inner08 inner08Instance = outer08.getInner08Instance();//outer08对象调用方法返回了inner08Instance对象 inner08Instance.say(); } } class Outer08{ class Inner08{//成员内部类 public void say(){ System.out.println("say..."); } } //方法,返回Inner08的实例 public Inner08 getInner08Instance() { return new Inner08(); } }
-
-
如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如 果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
源代码: MemberInnerClass01.java
10.9.5 静态内部类的使用
-
静态内部类是定义在外部类的成员位置,并且有static修饰
class Outer10 { //外部类 private int n1 = 10; private static String name = "张三"; //Inner10就是静态内部类 static class Inner10 {} }
-
可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
class Outer10 { //外部类 private int n1 = 10; private static String name = "张三"; private static void cry() {} static class Inner10 { public void say() { System.out.println(name); cry(); } } }
-
可以添加任意访问修饰符(public, protected、默认、private),因为它的地位就是 一个成员。
-
作用域:同其他的成员,为整个类体
-
静态内部类——访问———>外部类(比如:静态属性)[访问方式:直接访问所有静态成员]
-
外部类——访问———>静态内部类访问方式:创建对象,再访问
class Outer10 { //外部类 static class Inner10 { public void say() { System.out.println("say"); } } //方法 public void m1() { Inner10 inner10 = new Inner10(); inner10.say(); } }
-
外部其他类——访问————>静态内部类
-
和访问其他静态成员一样:类名.静态内部类(前提有访问权限)
Outer10.Inner10 inner10 = new Outer10.Inner10();
public class StaticInnerClass01 { public static void main(String[] args) { Outer10.Inner10 inner10 = new Outer10.Inner10(); inner10.say(); }
-
在外部类编写一个方法,可以返回静态内部类的对象实例.
-
编写普通方法返回静态内部类对象
public class StaticInnerClass01 { public static void main(String[] args) { Outer10 outer10 = new Outer10();//创建外部类对象 Outer10.Inner10 inner101 = outer10.getInner10();//外部类对象调用方法返回内部类对象 inner101.say(); } } class Outer10 { static class Inner10 {; public void say() { System.out.println("say..."); } } public Inner10 getInner10() { return new Inner10(); } }
-
编写静态方法返回静态内部类对象(不用创建外部类对象)
public static Inner10 getInner10_(){return new Inner10_}
public class StaticInnerClass01 { public static void main(String[] args) { Outer10.Inner10 inner10_ = Outer10.getInner10_(); inner10_.say(); } } class Outer10 { static class Inner10 {; public void say() { System.out.println("say..."); } } public static Inner10 getInner10_() { return new Inner10(); } }
-
-
-
如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问(不用this,注意)
Outer10.name
class Outer10 { //外部类 private static String name = "张三"; static class Inner10 { private static String name = "韩顺平"; public void say() { System.out.println(name + Outer10.name); } }
源代码:StaticlnnerClass01.java
课堂测试题
分析:
1. Test t= new Test() 到Test构造器
1. s1.a=5
2. s1.a=10
3. s2.a=5
4. 输出:5
2. Inner构造器
r.a=5
3. 输出5