企业制作网站公司,网站建设竞标ppt,wordpress自动变化文字,wordpress织梦seo系列文章目录
例如#xff1a;第一章 Python 机器学习入门之pandas的使用 文章目录 一、为什么需要内部类#xff1f; 1.内部类的作用 2.内部类的使用场景 二、内部类的分类 1.成员内部类#xff08;实例内部类#xff09; 2.静态内部类 3.局部内部类 4.匿名内部类 总结 前…系列文章目录
例如第一章 Python 机器学习入门之pandas的使用 文章目录 一、为什么需要内部类 1.内部类的作用 2.内部类的使用场景 二、内部类的分类 1.成员内部类实例内部类 2.静态内部类 3.局部内部类 4.匿名内部类 总结 前言 这篇文章将为大家讲解Java内部类的四种形式从语法到底层带大家全方位地了解和学习Java的内部类相信您看完这篇文章一定会有很大的收获。 一、为什么需要内部类 1.内部类的作用 Java当中允许在一个类当中定义另外一个类它们允许在一个类的内部声明另外一个类、这样就可以更轻松容易地组织和封装代码、内部类可以分为四种类型、成员内部类也叫做实例内部类、静态内部类、局部内部类和实例内部类。 那么面向对象编程语言当中为什么需要内部类呢机器语言也是为了解决和实现现实生活当中的问题需要内部类有这么几个原因。 1、封装和组织代码内部类运行将相关的类组织在一起、是的代码更加模块化和可读、如果一个类只在另外一个类的内部使用、将其定义为内部类可以减少对外部世界的暴露、提高封装性。 2、访问外部类的成员内部类可以访问外部类的私有成员、这有助于实现封装和隐藏、这样外部类的实现细节可以使用private隐藏起来需要对外部类进行什么操作只需要在内部类当中进行实现即可只有内部类可以访问他们。 3、实现“多重继承”我们都知道Java当中不像C那样可以实现多继承一个类只允许继承一个直接父类但是当我们使用内部类的时候可以在一个类当中声明多个类从而达到类似于多重继承的效果。 针对这个实现多继承的问题我多说几句如果一个类当中定义了内部类的话子类继承了这个类那么子类也同时会继承这个类当中的所有内部类那么内部类的访问权限也是会按照他们在父类当中的定义来实现继承举个例子秒如果父类当中的内部类是public的子类可以直接访问、如果是protected的话子类也可以直接访问他们、如果是默认的default包内可见子类只能在同一包内访问如果是私有的private子类无法直接访问内部类。 4.回调函数的实现内部类常常用于实现回调函数、通过在内部类中实现某个接口或者继承某个类可以轻松实现回调机制这里的回调函数不是我们今天要讲的重点所有这里就一笔带过了后面的文章我会讲到 2.内部类的使用场景 1.事件处理当我们使用图形开发界面的时候也就是我们所说的GUI应用程序的时候内部类通常被应用于处理各种事件例如按钮点击、鼠标移动等等操作因为内部类更容易地访问外部类的组件和数据。 2.迭代器设计模式内部类可以用于实现迭代器帮助遍历集合类当中的元素、这样的内部类可以访问外部类的私有成员。 3.还有回调函数、工厂模式、状态机、并发编程等等场景当然这些都涉及到了Java当中复杂的应用场景这篇文章我们主要介绍内部类所以这些复杂的应用后续有时间为大家讲解这里就不做赘述了。 二、内部类的分类
1.成员内部类实例内部类 1、语法层面成员内部类也叫做实例内部类是定义在另一个类外部类当中内部的一个类外面的叫做外部类、里面的叫做内部类、成员内部类又访问外部类的所有成员包括私有成员的权限。代码如下 package Yangon;public class OuterClass {private int outerVariable;public void outerMethod(){System.out.println(Outer Method!);}public class InnerClass{public void innerMethod(){outerVariable 10;outerMethod();System.out.println(Inner method!);}}public static void main(String[] args) {//内部类的实例化方法OuterClass outerClassObj new OuterClass();OuterClass.InnerClass innerClassObj outerClassObj.new InnerClass();}
}上面这段代码我为大家演示了成员内部类最基本的实现方法和使用场景着这个例子当中成员内部类可以直接访问外部类的私有成员、并且可以通过创建外部类的实例来创建内部类的对象。 大家可以看我写的主函数、这里需要注意的是成员内部类的实例化必须关联到外部类的实例、这是因为内部类实例与外部类实例之间存在一种特殊的关联关系、内部类实例可以直接访问外部类的任何成员。成员内部类通常用于与外部类有密切关联的情况一会我会细说、例如需要标识外部类的重要组成部分、或者实现某个接口、用于实现特定功能的辅助类。 什么情况下必须使用成员内部类而不能使用其他内部类来替代呢我总结了这么四点。 紧密关联的场景当内部类需要直接访问外部类的时候包括私有成员、并且与外部类有着密切的关联的时候、成员内部类是一个非常合适的选择、这种关系通常体现了一种包含或者拥有的关系等我为大家讲解完所有的内部类的时候相信大家经过对比就会明白了。 代码组织和封装如果内部类仅在外部类内部使用并且不需要再外部类之外被访问、成员内部类提供给了更好的封装性它用于组织代码、将相关的类放在一起从而使代码更易读和维护。 实现接口或者继承如果内部类需要实现某个接口或者继承某个类、并且这种实现与外部类相关、成员内部类可以更清晰地表达这种关系。 访问外部类的实例如果内部类需要访问外部类的实例不仅仅是静态成员成员内部类是唯一的选择、静态内部类无法访问外部类的实例。 这些必须使用成员内部类的场景大家理解即可、不需要专门背诵。以后各位再写代码做项目的时候就会感受到。 2.编译层面 接下来我们就从 编译的层面来分析一下内部类和外部类之间的关系。这当中主要涉及到编译后生成的字节码文件和访问规则 1.命名规则编译后内部类的字节码文件通常会使用外部类的类名作为前缀并在类名后添加内部类的名称例如如果外部类是OuterClass内部类是InnerClass那么编译后的文件就是OuterClass$InnerClass.class这就是内部类生成的字节码文件同样也会被纳入符号表编译器会通过这样的方法来识别外部类与内部类之间的关系。 2.访问规则编译后的字节码文件当中、内部类会维护一个对外部类的引用、以便能够访问外部类的成员、这个引用通常是通过生成一个额外的字段来实现的。内部类之所以能够为所欲为的访问外部类的任何字段就是因为内部类当中维护了一个外部类的引用。 3.实例化在编译后的字节码当中、要创建内部类的实现、通常需要提供外部类的实例作为构造函数的参数这是因为内部类的实例需要与外部类实例关联。 注意了实例化这一点非常重要大家一定要掌握代码如下 package Yangon;public class OuterClass {public int OuterVariable;class InnerClass{private int InnerVariable;public InnerClass(int InnerVariable){this.InnerVariable InnerVariable;}public void DisplayValue(){System.out.println(Outer Variable OuterVariable);System.out.println(Inner Variable InnerVariable);}}public static void main(String[] args) {OuterClass outerClassObj new OuterClass();OuterClass.InnerClass innerClassObj outerClassObj.new InnerClass(22);}
} 大家请看这段代码内外部类当中各有一个成员内部类当中定义了一个构造函数这个构造函数需要接受一个参数并且在构造函数内部将这个参数赋值给为内部类的成员变量。但是这仅仅只是我们所看到的参数实际上构造函数其实还会多出一个隐形的参数也就是我们所看不到的参数他的作用类似于this引用接下来我为大家演示一下内部类的构造函数所生成的字节码内容。 // 编译后的 InnerClass 字节码中的构造函数
public InnerClass(OuterClass outer, int innerVariable) {this.this$0 outer; // 将外部类的实例传递给内部类this.innerVariable innerVariable;
}到了这里想必大家也已经明白了内部类构造函数的调用需要接受外部类的实例也就是对象引用作为第一个隐式的参数从而访问内部类在实际创建对象的内部类实例时需要提供外部类的实例作为构造函数的参数这种关联是内部类能够访问外部类成员的基础。每次想要实例化内部类对象的时候必须先实例化外部类对象。因为成员内部类也相当于是一个外部类的成员想要访问一个类的成员必须先对这个类进行实例化只不过这个类的成员它也是一个类而已。 OuterClass outerObject new OuterClass();
OuterClass.InnerClass innerObject outerObject.new InnerClass(42);拓展 我在这里还想再为大家介绍一个知识点就是成员内部类的访问限定符有什么意义,例如这样。 public class OuterClass{public int OuterValue;public(protected\private\default) class InnerClass{public int InnerValue;}
} 接下来我为大家一一介绍一下这些访问限定符都有什么意义。首先提到public我们首先会想到Java当中的一个文件当中只有一个public类而且这个类名于文件名是相同的注意了在内部类当中这里是一个特例内部类出现的时候一个文件是可以允许出现多个public类的成员内部类的访问限定符决定了它的可见性、即在其他类当中如何访问这个内部类这个可见性对于程序的设计和封装有一定的影响。接下来我为大家一一介绍。 1.public成员内部类如果成员内部类声明为public那么它对于所有的类都是可见的其他的类都可以通过外部类的实例来访问内部类这通常用于希望将内部类作为独立的组件对外开放的情况。 2.protected成员内部类这意味着只有外部类的子类和同一个包里面的其他类可以访问这个内部类这种限定符通常用于希望内部类在继承关系当中被子类使用的场景。 3.default成员内部类如果没有指定访问限定符那么只有同一个包当中的类可以对他进行访问其他情况下都不行。 4.private成员内部类这种情况下只有包含这个内部类的外部类可以去访问它其他类都不可以访问它换言之被private修饰的内部类就意味着这个内部类是这个外部类的私有属性只有外部类自己可以访问其它谁都不可以 2.静态内部类 当我们说一个类是静态内部类的时候这意味这这个类被声明为另一个类的静态成员、静态内部类于成员内部类不同、它不依赖于外部类的实例、而是于外部类的类级别相关。静态内部类相当于是外部类的静态成员、因此可以直接通过外部类的类名引用、而不需要创建外部类的实例。 这里需要注意一点静态内部类是不可以访问外部类的非静态成员包括实例变量和实例方法因此它不依赖于外部类的实例但是可以访问外部类的静态成员。创建静态类的实例不需要外部类的实例、可以直接使用外部类的类名来进行初始化例如 package Yangon;public class OuterClass {public int OuterVariable;static class InnerClass{private int InnerVariable;public InnerClass(int InnerVariable){this.InnerVariable InnerVariable;System.out.println(Hello World!);}}public static void main(String[] args) {OuterClass.InnerClass innerClassObj2 new InnerClass(22);}
} 为什么需要静态内部类 静态内部类在Java当中有着独特的优势和用途、它与成员内部类有着很大的区别。 独立性静态内部类是与外部类的实例无关的它可以独立存在静态内部类并不持有外部类实例的引用因此它不依赖于外部类的实例。 命名空间静态内部类有其自己的命名空间不受外部类的影响这使得它可以具有与外部类的名称而不会引起冲突同时不会与外部类的实例变量产生歧义。 访问控制静态内部类可以拥有与外部类相同的访问修饰符但不受外部类的访问控制影响这也就意味着可以在外部类之外的地方随意访问静态内部类而不受外部类的可见性限制。 独立创建由于静态内部类不依赖于外部类的实例因此可以独立地创建静态内部类的实例这样的特性导致了一个类可以在没有外部类实例的情况下直接使用静态内部类的实例。 降低耦合度静态内部类有助于降低外部类和内部类之间的耦合度如果一个内部类不需要访问外部类的实例或者方法或者只需要在特定的情况下需要访问那么可以使用静态内部类来减少对外部类的依赖。 什么场景下需要使用静态内部类 我在上面的文章当中也已经提到过了如果一个内部类不需要访问外部类的实例变量、方法或者只需要在特定的情况下去访问那么这个时候我们就不需要使用成员内部类而直接使用静态内部类即可。 1.帮助类当一个类仅在外部类的内部使用、并且不依赖于外部类的实例时、可以考虑将其设计为静态内部类、这种情况下静态内部类充当外部类的帮助类提供一些辅助的功能。 public class OuterClass2 {public static class InnerClass{public void Help(){System.out.println(Doing something with outer static field);}}
} 2.工厂模式静态内部类可以用于实现工厂模式、其中静态内部类负责创建外部类的实例这种方式将创建实例的逻辑封装在内部类当中提高了代码的模块化和可读性。 public class OuterClass2 {private int value;public static class Factory{public OuterClass2 createInstance(int value){OuterClass2 instance new OuterClass2();instance.value value;return instance;}}
}3.迭代器模式、线程池这里的内容距离这部分内容有些遥远、就不为大家做出详细的介绍了。 从编译器的角度看静态内部类 1.类文件生成当我们编写包含静态内部类的Java源代码的时候、编译器会生成多个类文件、每个类文件对应一个类对于静态内部类、会生成两个类文件、一个是外部类文件一个是内部类文件。 2.命名规则编译器生成类文件的时候会遵循一定的命名规则、静态内部类的类文件通常采用以下命名规则外部类名$内部类名.class例如如果外部类是如果外部类是OuterClass静态内部类是StaticInnerClass那么生成的类文件名就是OuterClass$StaticInnerClass.class。 3.访问外部类的成员静态内部类可以直接访问外部类的静态成员、但是不能直接访问外部类的实例成员。这是因为静态内部类不持有对外部类实例的引用。 4.独立性编译器会确保静态内部类是相对独立的它不依赖于外部类的实例这意味着我们可以在没有外部类实例的情况下使用静态内部类。 3.局部内部类 什么是局部内部类 局部内部类是定义在方法或者代码块当中的内部类其中作用域仅限于包含它的方法或者代码块。局部内部类与成员内部类和静态内部类相比有一些特定的特点和使用场景。 1.作用域局部内部类的作用域仅限于包含他的方法或者代码块这意味着你无法在定义局部内部类的方法或者代码块之外的地方使用它。 2.可访问外方法的局部变量这里要注意一下在Java8之前的版本局部内部类只能访问类外方法体内的final修饰的成员。这一点在Java8之后做出了改进可以允许局部内部类访问外部类事实上的final成员。关于事实上的final我一会会在拓展内容里面去讲。 public class OuterClass2 {private int outerValue;private int number;public void Print(){final int value 10;double num 0;class InnerClass{public void Print(){System.out.println(value);System.out.println(num);}}}3.不能有访问修饰符和static修饰符局部内部类不能有访问修饰符和static修饰符它的可见性和生命周期受限于定义它的方法或者代码块不需要额外的修饰符。其实这一点很好理解static修饰的成员生命周期伴随着整个程序局部内部类的生命周期由定义它的方法的方法体来决定只要一出方法体这个局部内部类的生命周期就会结束所以不管是static修饰还是访问限定符来修饰都会造成生命周期的混淆。 4.使用场景局部内部类通常用于解决一些特定的方法或者问题避免污染外部类的命名空间它可以在方法内部提供一些封装的功能同时又不需要将这个类的定义放到整个类的范围内。 为什么要使用局部内部类 局部内部类通常要在一个方法内部封装一些逻辑、实现某个特定的功能、而又不希望该类在方法外部可见也不想让这个逻辑在方法外被复用所以就会使用局部内部类这些场景。 1.封装辅助功能当一个方法需要一些辅助功能或者实现某种特殊的逻辑、而这些功能对于整个类来说是独立的且不需要暴露给外部时、可以使用局部内部类、这有助于保持代码的整洁性和可读性。 例如下面这段代码 class Calculator{public int addWithLogging(final int a,final int b){class Logger{public void logOperation(){System.out.println(Adding a and b);}}Logger logger new Logger();logger.logOperation();return a b;}
} 2.解决特定的问题局部内部类可用于解决特定的方法内的问题提高代码的内聚性。例如某个方法需要访问特定的局部变量或者实现一些与该方法密切相关的功能。这里涉及到了线程的相关内容这段代码大家理解即可。 class DataProcessor{public void process(int[] data){final int threadHold 5;class DataHandler{public void handleData(){for(int value : data){if (value threadHold){System.out.println(Processing high value value);}}}}DataHandler dataHandler new DataHandler();dataHandler.handleData();}
} 3.减少作用域局部内部类可以将类的作用域限制在一个方法内防止外部类的其他方法或者外部类之外的类访问它从而降低了可见度有助于保持类的简洁性。 class OuterClass{private int outerField 42;public void outerMethod(){final int localVar 10;class LocalInnerClass{public void innerMethod(){System.out.println(Outer field in innerMethod: outerField);}}LocalInnerClass localInnerClassObj new LocalInnerClass();localInnerClassObj.innerMethod();}public static void main(String[] args) {OuterClass outerClassObj new OuterClass();outerClassObj.outerMethod();}
} 局部内部类的实例化只能放在方法体内部不能再方法体外实例化局部内部类的用法大概就这么多。 4.匿名内部类 什么是匿名内部类 匿名内部类是一种在使用过程中定义、实例化并使用的内部类、它没有显示的类名、通常用于创建只需要使用一次的小型类或者接口。以下是匿名内部类的一些关键特点 1.没有显式类名匿名内部类没有在代码中显示地声明一个类名、它在使用的同时定义和实例化。 2.通常用于接口和抽象类匿名内部类通常用于实现接口或者继承抽象类因为这样可以在创建实例的同时提供必要的实现。 3.可以扩展类或者实现接口匿名内部类可以扩展一个类普通类或者抽象类或者实现一个接口但是不能同时做两者。 4.可以访问外部类的成员匿名内部类可以访问外部类的成员变量或者方法但是在访问的时候需要将外部类的成员变量和方法声明为final或者是事实上的final至于什么是事实上的final一会我的拓展里面会将 interface MyInterFace{void doSomething();
}
class OuterClass{private int outerField 42;public void outerMethod(){MyInterFace myInterFace new MyInterFace() {Overridepublic void doSomething() {System.out.println(Hello World);}};myInterFace.doSomething();}public static void main(String[] args) {OuterClass outerClassObj new OuterClass();outerClassObj.outerMethod();}
} 拓展 当局部内部类或者匿名内部类访问外部类的变量的时候Java8 要求这些局部变量要么是final的要么是事实上final的那么什么是事实上final的呢就是没有被修改过就这么简单一个变量只要没有被修改过那么它事实上就是finalJava8之前这些类要想访问外部类的变量的话这些变量必须被final修饰过、Java8之后只要它没有被修改过就可以访问。 那么匿名内部类和局部内部类为什么一定只能访问事实上的final成员或者被final修饰的成员呢 从生命周期的角度来看当这些类引用一个外部类的成员变量的时候、它可能会在外部类方法执行完毕后继续存在。如果这个成员变量是一个局部变量、而且在外部方法执行后发生了变化内部类可能引用了一个已经失效或者不符合预期的值通过要求这些变量是final或者事实final就可以确保内部类引用的是正确的不会改变的值。 总结 Java的内部类成员内部类、静态内部类、局部内部类、匿名内部类就为大家讲解到这里这里面涉及到了很多计算机底层的内容和知识如果大家有兴趣可以去看我之前写的文章。能够帮助到大家就是对我最大的鼓励