资讯详情

资讯详情

43面向对象(高级)-内部类

1.定义一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class)嵌套其他类的类称为外部类(outer class)。内部类是类的第五大成员。属性方法构造器代码块内部类内部类可以访问外部类的成员包括私有成员注意:内部类是学习的难点同时也是重点后面看底层源码时有大量的内部类.2.内部类的分类1.定义在外部类局部位置上比如方法内1局部内部类(有类名)局部内部类(本质仍然是一个类)是定义在外部类的局部位置通常在方法中并且有类名。注意1.可以直接访问外部类的所有成员包含私有的2.不能添加访问修饰符,因为它的地位就是1个局部变量。局部变量是不能使用修饰符的。但是可以使用 final 修饰因为局部变量也可以使用final。内部类也是一个类所以能被其他类继承如果不想让其他类继承就加上final修饰。3.作用域仅仅在定义它的方法或代码块中。就相当于局部的变量4.局部内部类可以直接访问外部类的成员5.外部类在方法中可以创建内部类的对象实例然后即可调用内部类方法6.外部其他类 不能访问 局部内部类因为 局部内部类 是一个局部变量7.如果 外部类 和 局部内部类 的成员重名时默认遵循就近原则如果想访问外部类的成员则可以使用(外部类名.this.成员)去访问解读外部类名.this 本质就是外部类的对象即哪个对象调用了外部成员外部类名.this 就指向哪个对象2匿名内部类(没有类名*重点*)匿名内部类是定义在外部类的局部位置比如方法中并且没有类名。说明(1)本质是类(2)内部类(3)该类没有名字系统会分配名字自己看不到(4)同时还是一个对象1.基本语法new 类或接口(参数列表){ 类体 };2.为什么要使用匿名内部类//基于接口的匿名内部举例 public class AnonymousInnerClass { public static void main(string[] args){ Outer04 outer04 new Outer04(); outer04.method(); } } public void 0uter04(){//方法 //基于接口的匿名内部类 //解读 //1.需求: 想使用IA接口,并创建对象 //2.传统方式写一个类(tiger)实现该接口并创建对象 IA tiger new IA(); tiger.cry(); //3.但是被创建的类(tiger)需求是只是使用一次后面不会再使用如果每次都要写一类来实现接口会显得代码很冗余 //4.可以使用匿名内部来简化开发 IA tiger new IA(){//语法细节上比上面传统多态方式多了{}是类的写法 0verride public void cry(){ //相当于下面创建类重写方法 System.out.println(老虎叫唤...); } }; tiger.cry(); //5.tiger的编译类型是 IA //6.tiger的运行类型是 就是匿名内部类 0uter04$1。如果不是类的写法就是多态里提到的IA /* 看底层 jdk会分配一个类名—— 0uter04$1 ,只是我们看不到 class 0uter04$1 implements IA { 0verride public void cry(){ System.out.println(老虎叫唤...); } }*/ //7.jdk底层创建匿名内部类 0uter04$1 , 立即马上就创建了 0uter04$1 实例并且把地址返回给 tiger //8.匿名内部类使用一次就不能再使用。不是说tigertiger是对象可以反复调用 class 0uter04{//外部类 private int n1 10; //属性 public void method(){ //方法 } } interface IA {//接口 public void cry(); } class Tiger implements IA { 0verride public void cry(){ System.out.println(老虎叫唤...); } } class Dog implements IA { 0verride public void cry(){ System.out.println(小狗叫唤...); } }3.注意事项1匿名内部类的语法比较奇特请大家注意因为匿名内部类既是一个类的定义同时它本身也是一个对象因此从语法上看它既有定义类的特征也有创建对象的特征对前面代码分析可以看出这个特点因此可以调用匿名内部类方法有以下两种2可以直接访问外部类的所有成员包含私有的3不能添加访问修饰符,因为它的地位就是1个局部变量4作用域仅仅在定义它的方法或代码块中。就相当于局部的变量5匿名内部类可以直接访问外部类的成员6外部其他类 不能访问 局部内部类因为 局部内部类 是一个局部变量7如果 外部类 和 局部内部类 的成员重名时默认遵循就近原则如果想访问外部类的成员则可以使用(外部类名.this.成员)去访问4.使用场景1.把匿名内部类 当做实参 直接传递因为可以当做对象简洁高效public class InnerClassExercise01 { public static void main(string[] args){ //当做实参直接传递f1函数形参要求传入IL接口 f1(new IL(){ 0verride public void show(){ System.out.println(这是一副名画...) } ); //传统方法先实现接口再创建实例传递 f1(new picture()); } public static void f1(IL il){//静态方法形参是接口类型 il.show(); } } interfere IL(){//接口 void show(); } /*---------------------------------------------------------*/ //类实现IL —— 编程领域硬编码只用一次的情况下不推荐硬编码 class picture implements IL{ 0verride public void show(){ System.out.println(这是一副名画...); } }2.定义在外部类的成员位置上1成员内部类(没用static修饰)成员内部类是定义在外部类的成员位置不在方法中地位与属性、方法是同一等级的并且没有static修饰注意1.可以直接访问外部类的所有成员包含私有的2.可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。3.作用域和外部类的其他成员一样为整个类体。4.成员内部类 访问 外部类成员 (比如:属性)访问方式直接访问5.外部类成员 访问 成员内部类 访问方式创建成员内部类的对象然后使用相关的方法6.外部其他类 访问 成员内部类 访问方式方式1方式27.2静态内部类(使用static修饰)静态内部类是定义在外部类的成员位置并且有static修饰注意1.可以直接访问外部类的所有静态成员包含私有的但不能直接访问非静态成员2.可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员。3.作用域同其他的成员为整个类体4.静态内部类 访问 外部类成员 (比如:静态属性)访问方式直接访问5.外部类 访问 静态内部类 访问方式创建对象然后使用相关的方法6.如果外部类和静态内部类的成员重名时静态内部类访问的时默认遵循就近原则如果想访问外部类的成员则可以使用(外部类名.成员)去访问3.为什么需要内部类内部类不是 Java “多此一举” 的语法它是专门为了解决封装边界、单继承限制、代码内聚、临时实现这几类核心问题而设计的。很多 Java 核心特性比如迭代器、集合内部结构都依赖内部类才能优雅实现。下面我们逐个拆解它存在的核心价值每个点都对应一个不用内部类就很难解决的问题。1. 最不可替代突破封装限制直接访问外部类私有成员这是内部类最核心的作用。Java 的封装规则是private私有成员只能在本类内部访问。如果两个类逻辑强绑定、需要互相访问私有数据不用内部类的话就只能把属性改成public、或者加public的 get/set 方法这会直接破坏封装。而内部类本身就定义在外部类的 “内部”属于外部类的一部分因此可以无条件直接访问外部类的所有私有属性和方法同时外部类也能访问内部类的私有成员。最经典的实战例子就是ArrayList 的迭代器 ArrayList 存数据的数组elementData是private的绝不能对外暴露。但迭代器需要遍历这个数组于是 JDK 就把迭代器实现类Itr做成了 ArrayList 的私有内部类public class ArrayListE { private Object[] elementData; // 私有数组对外完全不可见 private int size; // 私有内部类迭代器实现只有ArrayList自己能用 private class Itr implements IteratorE { int cursor; public E next() { // 直接访问外部类的私有数组不需要任何getter方法 return (E) elementData[cursor]; } } // 对外只返回Iterator接口隐藏内部的Itr实现类 public IteratorE iterator() { return new Itr(); } }这样既保证了核心数组不对外泄露又让迭代器能直接操作数据兼顾了封装性和运行效率。2. 弥补短板间接实现 “多继承”Java 的类只能单继承一个类只能有一个父类这是 Java 的设计选择但也带来了能力限制。而内部类可以独立继承一个类不受外部类继承关系的影响。一个外部类可以包含多个内部类每个内部类分别继承不同的父类这样就间接突破了单继承的限制。abstract class Animal { abstract void breathe(); } abstract class Machine { abstract void run(); } // 外部类只能继承一个父类 public class Robot extends Machine { Override void run() { System.out.println(机器运行); } // 内部类继承第二个父类间接拥有两套继承体系 private class AnimalPart extends Animal { Override void breathe() { System.out.println(模拟生物呼吸); } } }这种场景不用内部类的话Java 单继承机制是完全做不到的。3. 代码组织强关联类归类提升内聚性如果一个类只服务于另一个类其他地方完全不会用到那把它作为内部类放进宿主类里是最合理的代码组织方式。好处非常直观逻辑内聚一看就知道这个类是专门给外部类服务的从属关系清晰避免包污染不会在外层包下产生大量只给一个类用的 “工具类”让包结构更干净。最典型的例子就是Map.Entry 每个键值对 Entry 是 Map 的基本组成单元它只属于 Map单独拿出来没有任何意义。所以 Java 把Entry定义为 Map 接口的内部接口每个 Map 实现类比如 HashMap再用内部类去实现它。4. 访问控制更精细的隐藏实现细节普通的顶级类访问权限只能是public或者「包私有」不写修饰符。 而内部类可以用private、protected修饰实现更强的访问控制private内部类只有外部类自己能访问包内其他类都看不到完全隐藏实现细节对外只暴露接口实际返回内部类对象这也是 “面向接口编程” 的经典用法。就像上面 ArrayList 的例子你作为使用者只能拿到Iterator接口根本不知道有Itr这个类的存在。未来 JDK 想换迭代器的实现直接改内部类就行外部代码完全不受影响。5. 开发效率简化临时接口的实现匿名内部类这是我们日常开发用得最多的场景当你需要临时用一次某个接口 / 抽象类的实现不想专门新建一个.java 文件的时候用匿名内部类可以就地实现代码非常简洁。比如启动线程、事件监听、回调这些场景// 不用单独写一个类实现Runnable直接匿名内部类就地实现 new Thread(new Runnable() { Override public void run() { System.out.println(线程执行); } }).start();这本质上也是多态的一种便捷写法接口引用指向匿名内部类的对象和单独写实现类效果完全一样但省去了大量冗余代码。最后总结内部类不是 “嵌套着玩” 的语法糖它的每种形态都对应明确的问题场景要访问私有成员又不破坏封装 → 成员内部类要突破单继承限制 → 成员内部类要把强关联类组织在一起 → 静态内部类 / 成员内部类要隐藏实现细节 → 私有内部类要临时实现接口 / 抽象类 → 匿名内部类简单说当两个类强绑定、或者需要突破普通类的访问 / 继承限制时内部类就是最优雅的解决方案。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →