目录
一、内部类基本介绍
(一)内部类定义
(二)内部类基本语法
(三)内部类代码示例
(四)内部类的分类
二、局部内部类
三、匿名内部类(重要)
(一)基本介绍
(二)基于接口的匿名内部类
(三)基于类的匿名内部类
(四)注意事项与使用细节
(五)匿名内部类的最佳实践——当做实参直接传递
(六)小练习
四、成员内部类
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); // 4554617couter02.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);// 4554617cSystem.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 {@Overridepublic void cry() {System.out.println("老虎叫唤...");}}*/// 8.JDK底层在创建匿名内部类 Outer04$1,立即就创建了 Outer04$1对象// 并且把地址返回给tiger后,该匿名内部类Outer04$1 就立即消失了// 9.匿名内部类只能使用一次,但是tiger是一个对象,可以反复调用IA tiger = new IA() {@Overridepublic void cry() {System.out.println("老虎叫唤...");}};// 使用getClass()获取对象的运行类型:System.out.println("tiger的运行类型="+tiger.getClass()); // Outer04$1tiger.cry();tiger.cry();tiger.cry();}
}interface IA {void cry();
}/*
传统方式:定义类实现IA接口:
class Tiger implements IA {@Overridepublic 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{@Overridepublic void test() {System.out.println("匿名内部类重写了test方法...");}}*/// 4.同时也直接返回了 匿名内部类 Outer04$2的对象,即:father// 5.注意:("jack")参数列表会传递给Father构造器Father father = new Father("jack") {@Overridepublic void test() {System.out.println("匿名内部类重写了test方法...");}};System.out.println("father对象的运行类型:" + father.getClass()); // Outer04$2father.test(); // 匿名内部类重写了test方法...father.test(); // 匿名内部类重写了test方法...father.test(); // 匿名内部类重写了test方法...// 6.注意:如果new Father("jack")后面没有{},那么father的运行类型就是FatherFather 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 {@Overridepublic void eat() {System.out.println("小狗吃骨头...");}}*/Animal animal = new Animal() {// 这里要注意:因为Animal中的eat方法是抽象的,所以eat方法必须被实现@Overridevoid eat() {System.out.println("小狗吃骨头...");}};System.out.println("animal的运行类型:" + animal.getClass()); // Outer04$3animal.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;@Overridepublic 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() {@Overridepublic void hi() {System.out.println("匿名内部类重写了 hi方法,哈哈...");}@Overridepublic 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 {@Overridepublic void show() {System.out.println("传统方式实现接口中的方法...");}
}
使用匿名内部类传参:
public class InnerClassExercise02 {public static void main(String[] args) {f1(new IL() {@Overridepublic 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() {@Overridepublic void ring() {System.out.println("懒猪起床了");}});cellPhone.alarmClock(new Bell01() {@Overridepublic 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();// 5System.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;}
}