1. 内部类基本介绍
如果定义类在局部位置(方法中/代码块) :(1) 局部内部类 (2) 匿名内部类
定义在成员位置 (1) 成员内部类 (2) 静态内部类。
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。 是我们类的第五大成员[思考:类的五大成员是哪些?[属性、方法、构造器、代码块、内部类],内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系, 注意:内部类是学习的难点,同时也是重点,后面看底层源码时,有大量的内部类.
2. 基本语法 412
2.1 快速入门
代码在com.stulzl.innerclass_.包中
InnerClass01
package com.stulzl.innerclass_;
//内部类演示 412
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("代码块……");
}
//内部类,在Outer类的内部
class Inner{
}
}
3. 内部类的分类 412
➢定义在外部类局部位置上(比如方法内) :
1)局部内部类(有类名)
2)匿名内部类(没有类名,重点!!!!)
➢定义在外部类的成员位置上:
1)成员内部类(没用static修饰)
2)静态内部类(使用static修饰)
4. 局部内部类的使用
说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
1.可以直接访问外部类的所有成员,包含私有的
2.不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final
3.作用域:仅仅在定义它的方法或代码块中。
4.局部内部类---访向----外部类的成员[访问方式:直接访问
5.外部类--访问---局部内部类的成员访问方式创建对象,再访问(注意:必须在作用域内)
记住:(1)局部内部类定义在方法中/代码块
(2)作用域在方法体或者代码块中
(3)本质仍然是一个类
6.外部其他类---不能---->局部内部类(因为局部内部类地位是一一个局部变量)
7.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问[演示]
System. out.println(”外部类的n2=”+外部类名this.n2);
5. 局部内部类 413-414
说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
1.可以直接访问外部类的所有成员,包含私有的
2.不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。 但是可以使用final修饰,因为局部变量也可以使用final
3. 作用域:仅仅在定义它的方法或代码块中
4. 局部内部类--- 访向--->外部类的成员[访问方式 直接访问]
5.外部类--访向---局部内部类的成员
访问方式:创建对象,再访问(注意:必须在作用域内)
记住:(1 )局部内部类定义在方法中/代码块
(2)作用域在方法体或者代码块中
(3)本质仍然是一个类
6.外部其他类--不能访向---->局部内部类(因为局部内部类地位是一个局部变量)
7.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访向外部类的成员,则可以使用(外部类名.this.成员) 去访问
代码在com.stulzl.local_innerclass.包中
Local_InnerClass
package com.stulzl.local_innerclass;
//局部内部类 413-414
public class Local_InnerClass {
public static void main(String[] args) {
Outer02 outer02 = new Outer02();
outer02.m1();
}
}
class Outer02{//外部类
private int n1 = 100;
private void m2(){//私有方法
System.out.println("Outer02 m2()");
}
public void m1(){//方法
//1.局部内部类是定义在外部类的局部位置,通常在方法中
//3.不能添加访问修饰符,但是可以使用final修饰
//4.作用域 : 仅仅在定义它的方法或代码块中
final class Inner02{//局部内部类(本质仍然是一个类)
private int n1 = 800;
//2.可以直接访问外部类的所有成员,包含私有的
public void f1(){
//5. 局部内部类可以直接访问外部类的成员,比如下面 外部类 n1 和 m2()
//如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,
//使用 外部类名.this.成员)去访问
//解读 Outer02.this 本质就是外部类的对象, 即哪个
// 对象调用了 m1, Outer02.this 就是哪个对象
System.out.println("n1="+n1+" 外部类的n1="+Outer02.this.n1);//输出800 100
m2();
}
}
//class Inner03 extends Inner02{ }//此时Inner02有final不能被继承
//5. 外部类在方法中,可以创建 Inner02 对象,然后调用方法即可
Inner02 inner02 = new Inner02();
inner02.f1();
}
//代码块中也可以定义局部内部类
{
class Inner04{}
}
}
5. 匿名内部类 415-416
(1)本质是类 (2)内部类 (3)该类没有名字 (4)同时还是一个对象
说明:匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名
代码在com.stulzl.anonymous_innerclass.包中
Anonymous_InnerClass
package com.stulzl.anonymous_innerclass;
//匿名内部类 415
public class Anonymous_InnerClass {
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 tiger = new Tiger();
// tiger.cry();
//3. 需求是 Tiger类只是使用一次,后面再不使用
//4. 可以使用匿名内部类来简化开发
//5. tiger 的编译类型 ? IA
//6. tiger 的运行类型 ? 就是匿名内部类 匿名为 Outer04$1
/*
我们看底层 会分配 类名 Outer04$1
class Outer04$1 implements IA {
@Override
public void cry() {
System.out.println("老虎叫唤...");
}
}
*/
//7.jdk 底层在创建匿名内部类 Outer04$1,立即马上就创建了Outer04$1实例,并且把地址返回给tiger
//8. 匿名内部类使用一次,就不能再使用(意思是匿名内部类只有一次,但是根据他创建的实例tiger可以一直使用)
IA tiger = new IA(){
@Override
public void cry() {
System.out.println("老虎叫唤……");
}
};//注意分号
tiger.cry();
tiger.cry();//匿名内部类只有一次,但是根据他创建的实例tiger可以一直使用
tiger.cry();
//二、演示基于类的匿名内部类
//分析
//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 方法");
}
};//别忘了分号
father.test();
//三、基于抽象类的匿名内部类
Animal animal = new Animal(){
@Override
void eat() {//抽象类不许重写方法
System.out.println("动物吃骨头……");
}
};//别忘了分号
animal.eat();
}
}
interface IA{//接口
public void cry();
}
//class Tiger implements IA{
// @Override
// public void cry() {
// System.out.println("老虎叫唤……");
// }
//}
class Father{//普通类
//构造器
public Father(String name){
}
//方法
public void test(){
}
}
//抽象类
abstract class Animal{
abstract void eat();
}
6. 匿名内部类细节 417
2.匿名内部类的语法比较奇特,请大家注意,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法。
3.可以直接访问外部类的所有成员,包含私有的[案例演示]
4.不能添加访问修饰符,因为它的地位就是一个局部变量。[过]
5.作用域:仅仅在定义它的方法或代码块中。[过]
6.匿名内部类---访向---->外部类成员[访问方式:直接访问]
7.外部其他类---不能访问的----匿名内部类(因为匿名内部类地位是一个局部变量)
8.如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
代码在com.stulzl.anonymous_innerclass_detail.包中
Anonymous_InnerClass_Detail
package com.stulzl.anonymous_innerclass_detail;
//匿名内部类细节417
public class Anonymous_InnerClass_Detail {
public static void main(String[] args) {
Outer05 outer05 = new Outer05();
outer05.f1();
//打印hashcode证明 匿名内部类调用外部类属性时使用的 Outer05.this 就是调用 f1 的 对象即outer05
System.out.println("Outer05.this hashcode=" + outer05);
}
}
class Outer05{//外部类
private int n1 = 99;
public void f1(){
//创建一个基于类的匿名内部类
//不能添加访问修饰符,因为它的地位就是一个局部变量
//作用域 : 仅仅在定义它的方法或代码块中
Person person = new Person(){
private int n1 = 88;
@Override
public void hi() {
//可以直接访问外部类的所有成员,包含私有的
//System.out.println("匿名内部类重写了hi()方法 n1="+n1);
//如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,
// 如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
System.out.println("匿名内部类重写了hi()方法 n1="+n1+"外部类的n1="+Outer05.this.n1);
//Outer05.this 就是调用 f1 的 对象
System.out.println("Outer05.this hashcode=" + Outer05.this);//打印hashcode
//证明Outer05.this 就是调用 f1 的 对象即outer05
}
};
person.hi();//涉及动态绑定,运行类型Outer05$1
//也可以直接调用, 匿名内部类本身也是返回对象
// class 匿名内部类 继承了 extends Person {}
Person person1 = new Person(){
@Override
public void hi() {
System.out.println("匿名内部类重写了hi()方法,哈哈……");
}
@Override
public void ok(String name) {
super.ok(name);
}
};
person1.hi();
person1.ok("jack");
}
}
class Person{
public void hi(){
System.out.println("Person hi()");
}
public void ok(String name){
System.out.println("ok()="+name);
}
}
7. 匿名内部类最佳实践
7.1 练习01 418
当作实参直接传递,简洁高效
代码在com.stulzl.anonymous_innerclass_exercise01.包中
Anonymous_InnerClass_Exercise01
package com.stulzl.anonymous_innerclass_exercise01;
//匿名内部类最佳实践 418
public class Anonymous_InnerClass_Exercise01 {
public static void main(String[] args) {
//使用匿名内部类 当作实参直接传递,简洁高效(自我理解是相当于把运行类型当参数传走了)
f1(new IL() {
@Override
public void show() {
System.out.println("这是一副名画~~……");
}
});
//写完全就是这样的
// IL il = new IL(){ //其实匿名内部类就是系统自己写了一个类和show方法,然后将这个实例对象指向了il
// @Override //这个对象实例就相当于普通方法的Picture,只是名字(系统取得)不一样而已
// public void show() {
// System.out.println("这是一副名画……");
// }
// };
// f1(il);
//传统方法
Picture picture = new Picture();
f1(picture);
}
//写一个方法,调用接口方法
public static void f1(IL il){//这里涉及多态和动态绑定,编译类型 IL 运行类型是 Picture
il.show();//调用接口方法
}
}
interface IL{//接口
void show();
}
//传统方法,实现接口方法 (这种比较死板的方法编程领域叫硬编码)
class Picture implements IL{
@Override
public void show() { //实现接口方法
System.out.println("这是一副名画……");
}
}
7.2 练习02 418
1.有一个铃声接口Bell,里面有个ring方法。如图
2.有一个手机类Cellphone,具有闹钟功能alarmclock,参数是Bell类型(右图)
3.测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了
4.再传入另个匿名内部类(对象),打印:小伙伴上课了
匿名内部涉及(1)继承(2)多态(3)动态绑定(4)内部类
代码在com.stulzl.anonymous_innerclass_exercise02.包中
Anonymous_InnerClass_Exercise02
package com.stulzl.anonymous_innerclass_exercise02;
//匿名内部类最佳实践 练习02 418
/*
1.有一个铃声接口 Bell,里面有个 ring 方法。(右图)
2.有一个手机类 Cellphone,具有闹钟功能 alarmClock,参数是 Bell 类型(右图)
3.测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了
4.再传入另一个匿名内部类(对象),打印:小伙伴上课了
*/
public class Anonymous_InnerClass_Exercise02 {
public static void main(String[] args) {
Cellphone cellphone = new Cellphone();
//解读
//1. 传递的是实现了 Bell 接口的匿名内部类 InnerClassExercise02$1
//2. 重写了 ring
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();//动态绑定
}
}