java程序员从笨鸟到菜鸟之(九)内部类

来源:互联网 发布:金灿荣奚落公知视频 编辑:程序博客网 时间:2024/05/17 06:48

1 综述

1)概念

  在一个类(外部类)的内部定义的类称为内部类

  举例:在类A中定义一个类B,那么类B就是类A的内部类,同理,类A就是类B的外部类

 2)分类

   成员内部类局部内部类

   成员内部类:在外部类的内部中定义,平行于成员变量、成员方法
   局部内部类:在外部类中成员方法中定义,平行于成员方法中的局部变量

   成员内部类分为:实例内部类(未被static修饰,可创建对象)和静态内部类(被static修饰)

3)外部类的组成部分

        变量、构造方法、普通方法、成员内部类

4) 内部类的组成部分

       变量、构造方法、普通方法

4) 成员方法组成部分:

      局部变量、局部内部类

注意:内部类是一个类内部的类;

特殊性:与我们之前学过的“一个源文件中包含多个类”是有本质区别的,这里的类指的是并列关系,而不是属于与被属于的关系;
对应的面试题:生成几个class文件(有内部类和外部类等)

2  (非静态)成员内部类

实例1成员内部类以代码的形式说明

package memberInnerClass;import memberInnerClass.Outer.Inner;//不需要加Demo1/* * 问题1:Java非静态成员内部类为什么不能有静态成员? * 原因 * 因为:java虚拟机要求所有的静态变量必须在对象创建之前完成(被加载); * 而非静态成员内部类被外部类看作是非静态成员, * 它的初始化必须在外部对象创建之后才能进行 * 要加载非静态成员内部类必须先加载外部类(即:先创建外部类对象)便产生矛盾 * 形象理解:先创建外部类对象,才能加载非静态内部类的静态变量,但与静态变量必须在对象创建之前完成矛盾 *  * 问题2:静态内部类的加载机制? * 静态内部类的加载不依赖于外部类的对象,并不是随着外部类的加载而加载 * 在使用时加载,它没有指向外部类对象的引用; * 可以直接创建外部类对象(不依赖于外部类对象创建而加载类) * 格式:外部类.内部类 类名=new 外部类.内部类() * 问题3:内部类字节码文件形式: *       外部类$内部类名.class *  * 问题4:局部内部类的成员变量只能被final修饰,不能被private,public,protected以及static修饰原因 * 答:对于局部变量,作用域为该方法内,方法执行结束该局部变量也随之消失; * 但内部类可能会产生隐式的“闭包”,闭包使局部变量脱离它所在的方法继续存在; * 在使用Java局部内部类或者匿名内部类时,若该类调用了所在方法的局部变量, * 则该局部变量必须使用final关键字来修饰,否则将会出现编译错误 *  * 问题5:public修饰的内部类(成员和局部)可以访问外部类的成员变量(各种修饰)原因? * 原因有默认的指向外部类对象的引用   方式:通过外部类的成员方法或内部类的成员方法 *  *  *  * 补充:局部内部类的局部变量是位于方法内的,必须被final修饰 * 内部类的位置:成员内部类和局部内部类 * 内部类的修饰:实例内部类和静态内部类,私有内部类后续会提起(很少用) *  * ****//* * 下面的代码主要为了说明: * 1)成员内部类与局部内部类的位置与语法格式 * 2)成员内部类的等效作用(与成员方法、成员变量平行)以及成员内部类的组成 * 3)局部内部类的等效作用(与局部变量平行)以及局部内部类的组成 * 4)非静态成员内部类(这里也包括局部内部类(非静态修饰))可以访问外部类的任何属性和方法 * 5)非静态成员内部类隐藏默认指向外部对象的引用格式:"外部类.this." * 6)当局部变量与成员变量重名时,为访问外部变量的变量要显(明)示引用 * 7)外部类的成员变量和非静态内部类的成员变量以及非静态内部类的成员方法中的局部变量重名时,在此成员方法的输出情况 * 8)Java非静态的成员内部类实例绑定在外部类的实例上:想访问内部类的属性必须创建外部类的对象 * 9)在非静态的成员内部类的成员方法访问外部类中的成员属性:有默认的指向外部类的对象的引用 *10)在外部类中访问非静态内部类:eg:在外部类的成员方法中访问非静态的内部类的成员 *11)通过外部类的外部访问非静态成员内部类的成员(变量和方法):创建内部类的对象 *12)随后再补充...... * *///外部类class Outer {// 各种用修饰符修饰的成员变量private int age = 25;public int height = 183;static String name = "司马懿";// 默认private static String favorite = "乒乓球";public final int other = 10;class Inner { // (非静态)成员内部类:默认为publicprivate int age = 20;// 非静态内部类的成员变量        public String cloor="Yello";public void method() {final int age = 10;// 局部变量必须为finalSystem.out.println(age);// 当内部类与外部类成员名冲突时,先在内部寻找(没有则一层层从外找)System.out.println(this.age);// this是指向本类中的对象的引用,访问本类中的成员变量System.out.println(Outer.this.age);// 指向外部类对象的引用,访问的是外部类的成员变量,如果外部类中没有,则报错System.out.println(name);System.out.println(height);// 隐藏Outer.this.System.out.println(favorite);}}// 非静态成员方法public void method1() {final int a = 10;// 局部变量必须被final修饰// 局部内部类(位于成员方法中),它的组成class LocalInner { private int a = 10;// 成员变量不需要final修饰,它不是局部变量(局部变量是相对而言的)public void method3() {System.out.println(a);System.out.println(name);System.out.println(age);//默认Outer.this.}}}// 非静态成员方法public void method2() {/* * 说明: 在外部类的非静态方法中创建一个内部类的对象,此时 java默认使用this自引用来创建内部类对象 * (this可以省略,java自动提供this) */final String name = "诸葛亮";// 局部变量与成员变量重名Inner inner = new Inner();System.out.println(name);System.out.println(this.name);}// 静态成员方法public static void method3() {        //内容待定}}// 测试类public class Demo1 {public static void main(String[] args) {Outer.Inner temp = new Outer().new Inner();/* * 拆分形式如下(与上等效) 说明: 先创建外部类对象,再创建内部类对象 */Outer a = new Outer();Outer.Inner temp1 = a.new Inner();System.out.println(temp1.cloor);//color是public修饰的/*下面报错原因:说明在外部类的外部是不能直接访问外部类的私有成员 *temp1.age;age是修饰private *而外部类中(通过方法中体现)可以访问内部类的private修饰变量,是由于内部类与外部类是一个整体 *//* * 与上等效: */Outer b = new Outer();Inner temp2 = b.new Inner();// 此时必须import说明Inner属于那个类,负责会报错temp2.method();new Outer().method2();// 匿名对象调用成员方法(外部类中访问内部类成员变量)}}

补充:非静态的(实例)成员内部类不能有静态成员(静态方法和变量)

说明:对于使用private修饰的成员(变量和方法),只能够在类内访问,类内包括(类本身的成员方法和内部类),java允许内部类访问外部类的所有属性(与访问控制符无关)

从理解上,既然成员内部类和成员方法平行,在成员方法中可以访问private修饰的成员变量,当然成员内部类也可以访问(通过一个隐藏的指向外部类的引用使用外部类的变量)

3  静态成员内部类

概念:用static修饰的成员内部类

特点:属于类范畴的元素,与对象无关(所以静态成员内部类中没有指向外部类对象的引用),:不能在静态成员内部类中使用外部类的非静态成员,那么要访问外部类的成员变量,这个变量必须被static修饰

注意:非静态的成员内部类之所以可以访问外部类的非静态成员,是因为其隐含有指向外部类对象的引用

目的:为了方便调用

实例2静态的成员内部类

package innerClass;//import innerClass.Outer2.Inner2_1;import innerClass.Outer2.Inner2;class Outer2 {private int b = 3;//成员变量private static int c = 4;//成员静态变量public static class Inner2 {/*静态内部类(具有普通内部类特性,还有其特殊性) *所以可以定义实例变量和静态变量 */ private int a = 4;private static int d = 20;/* * 静态内部类访问外部类的变量情况 * 例如1: * (下面两行) */int temp = c;  // 静态内部类可以直接访问外部类的成员静态变量int temp1 = new Outer2().b;//必须通过创建对象访问外部类的非静态成员变量public void method() {/* * 静态内部类中非静态的方法(通过创建对象使用)静态成员内部类无法直接访问非静态成员内部类变量和方法; * 但可以在静态内部类中创建非静态内部类的对象(通过引用访问变量或方法)  * 例如2: * (下面3行): */Outer2.Inner2_1 z = new Outer2().new Inner2_1();int m = z.age;System.out.println("在非静态成员内部类访问静态成员内部类的成员" + z.age);}public void method3() {System.out.println("我爱你");}public static void method1() {// 静态方法/* System.out.println(a);//报错 * 静态方法中不能直接访问非静态变量,必须通过创建对象,利用引用访问 * 例如3: * (如下3行): */Inner2 inner2 = new Inner2();// 一般把静态内部类构造方法私有,不让外界创建对象System.out.println(inner2.a);System.out.println(d);//静态内部类中的静态方法直接访问静态变量}}class Inner2_1 { // 实例(非静态)内部类private int age = 20;public void method2() {System.out.println("想睡觉。。。");}/* * 在非静态成员内部类访问静态成员内部类的成员;由于静态成员内部类的对象不会自动指向外部类对象的引用 * 在创建内部类的实例时,不必创建外部类的实例 格式:外部类名.内部类名 对象名 = new 外部类名.内部类名() ; * 例如4: */Outer2.Inner2 i = new Outer2.Inner2();int c = i.a;}}// 测试类public class Demo3 {public static void main(String[] args) {    /*测试1:访问静态成员变量的非静态方法     * 格式:外部类名.内部类  对象名 =外部类对象.内部类对象;     * 具体如下:     *///Outer2.Inner2 z=new Outer2().new Inner2();报错Outer2.Inner2 p=new Outer2.Inner2();p.method3();//访问非静态方法(可能是jdk8的新特性)/*测试2:访问静态成员变量的静态方法(不需要创建外部类的对象) * 格式:外部类名.内部类名 对象名 = new 外部类名.内部类名() ; *具体如下: */Outer2.Inner2 q = new Outer2.Inner2();q.method1();//通过创建对象访问静态方法Outer2.Inner2.method1();//直接类名:应该以静态方式访问类型 Outer2.Inner2 中的静态方法 method1()}}

实例3 静态成员内部类

package staticMemberInnerClass;/** * 静态内部类(静态和类方面)  * 特点:  * 1)静态成员内部类的组成:静态和非静态(成员、方法) * 2)静态成员内部类是属于类范畴的(静态),没有指向外部类对象的引用,不依赖于外部类对象的创建,直接创建内部类对象 格式:外部类名.内部类名 对象名 = * new 外部类名.内部类名(); * 3)在外部类的外部通过类名可以直接访问内部类的静态成员(方法和属性) 格式:外部类.内部类.变量; * 外部类.内部类.方法(参数);  * 4)不能直接在静态成员类中访问外部类的非静态成员(属性和方法) ps:但可以直接访问外部类的静态成员(属性和方法) * 原因:静态成员内部类没有指向外部类对象的引用,外部类的非静态成员(变量和方法)依赖于外部类的具体对象(实例),而此时没有创建外部类的对象(这是与非静态成员内部类的区别) * 解决方案:(在静态内部类的方法中创建外部类的对象,通过指向外部类对象的引用访问)  * 5) 问题1:静态成员内部类 *  *  * 代码说明问题: 1) *  * 2) **/// 外部类class Outer {// 都必须在方法中访问内部或外部类的属性和方法private static int age = 25;public  static String color="Green";public int height = 183;public String name = "阿凡提";// 静态成员内部类static class Inner {// 默认public修饰public String name = "黑猫警长";        public static String flower="牡丹";public void method() {System.out.println(age);// 直接访问外部类的静态成员变量System.out.println(this.name);//指向本类对象的引用// System.out.println(Outer.this.name);//没有指向外部类对象的引用(了解报错的原因:与非静态成员内部类做对比)// System.out.println(height);//就没有指向外部类对象的引用}// 静态方法public static void method1() {// 外部类的静态成员方法(此时使用外部类的static和对应方法中的final变量)// 内容待定/* * 静态成员方法无法访问外部类中的非静态成员---静态方法中的内部类也不可以 静态成员内部类与静态方法中的局部内部类区别: * 静态方法的局部内部类可以访问方法中final修饰的变量和外类的static变量 */class Inner1 {final int age = 28;public void method() {System.out.println(age);// 使用外部类的static变量}}}// 非静态成员方法// 静态内部类使用外部类的非静态方法的解决方法:访问外部类的静态和非静态的成员(方法和变量)public void method2() {Outer outer = new Outer();System.out.println(outer.height);// 访问外部类的非静态成员}}}// 测试类public class Demo1 {public static void main(String[] args) {    Outer.Inner temp=new Outer.Inner();//创建内部类对象  //Outer.Inner temp=new Outer().new Inner();//报错的原因是:静态内部类没有指向外部类对象的引用(与非静态内部类创建对象的区别)    temp.method(); //通过对象访问静态成员内部类中非静态方法    temp.method1();//通过对象访问静态成员内部类中静态方法    String b=temp.name;//无法通过外部类的外部访问静态内部类的私有成员变量(public可以)    Outer.Inner.method1();//直接通过类名访问静态方法(静态特性)    String e=Outer.Inner.flower;//可以通过类名访问静态成员变量(不能被private修饰)(静态特性)  // 补充:不能在外部类的外部以引用的形式访问内部类的private成员(变量和方法)}}

总结:非静态的成员可以访问非静态的成员(通过this【通过外部类的非静态方法】或外部类.this引用【非静态内部类的静态方法内】),也可以访问静态成员(通过类名);静态成员只能访问静态成员(以直接的形式)

区分静态与非静态的内部类的不同:静态成员内部类是类范畴的属性,没有指向外部类对象的引用,不能使用外部类的非静态成员

4 局部内部类:

定义:在一个方法中定义的内部变量,它的可见范围是当前方法

语法要求:类比局部变量的局部特性

1)不能用访问修饰符(public,private,protected)修饰局部变量以及static(没有意义)

2)   局部内部类只能在当前方法中使用

实例4:局部内部类(静态方法的和非静态方法)

package localInnerClass;//局部内部类(局部和类):/*位置:位于方法内或作用域的类 *组成:变量和方法;与局部变量并列 *与成员内部类的区别:访问权限仅限于方法内或作用域内(出了作用域不可见了) *作用域(声明周期):从局部内部类的定义开始到局部内部类的方法结束 *特点: * 1)不能用public,protected,private以及static修饰(原因)ps:类比局部变量 * 原因:局部内部类只能在定义它的方法中实现,访问修饰符对局部内部类是没有意义的,同理static也是 * 可以被final修饰(成员内部类可以被继承) * 2)局部内部类必须先定义才能使用 * 3)局部内部类中成员变量与方法中局部变量重名时,方法中的局部变量被覆盖(java默认使用局部内部类中的变量),即使局部变量是final修饰的,看(1) *  * 问题: * 1)局部内部类访问局部变量会出现问题? * 2)当前局部变量报错,必须用final修饰;为什么用final修饰? * 答:是由于局部变量是随着方法调用而生成的,随着方法的调用完毕消失, * 而现在局部位置有一个局部内部类它要在自己的成员方法位置访问当前的局部变量 * 解决方案:必须把变量变成一个常量,(需要用final:自定义常量),这样的一个变量的值永远是固定的! * 3) *  * 分两类来讨论:静态方法的局部内部类和非静态方法的局部内部类 * 非静态方法的局部内部类(类似非静态成员内部类): * 1)包含一个指向内部类对象的引用 * 引用格式:外部类.this * ***///外部类class Outer {// 外部类的成员变量private int num = 50;private static int age = 24;// 外部类的非静态成员方法public void method() {final int num2 = 200; // 局部变量:实际是自定义常量(细节问题)class Inner {// 局部内部类,只用一个修饰符:final// public int num2=100;//局部内部类的成员变量public void show() {// 局部内部类的成员方法System.out.println(num);// 默认为Outer.this.(隐含了指向外部类对象的引用:访问外部所有成员)// 局部内部类中访问局部变量System.out.println(num2);// (1)// System.out.println(this.num2);/* * jdk1.7才会有这个错误!(视频加深理解)[不加局部内部类的num2变量] 原因: * jdk1.8---->封装好了,所以不会出现错误! */}}Inner i = new Inner();// 说明:局部内部类必须先定义才能使用(还是在方法中)i.show();}public static void method1() {// 外部类的静态成员方法(此时使用外部类的static和对应方法中的final变量)/* * 静态成员方法无法访问外部类中的非静态成员---静态方法中的内部类也不可以 静态成员内部类与静态方法中的局部内部类区别: * 静态方法的局部内部类可以访问方法中final修饰的变量和外类的static变量 */class Inner {final int age = 28;public void method() {System.out.println(age);// 使用外部类的static变量}}}}// 测试类public class Demo1 {public static void main(String[] args) {// 对于局部内部类访问具该类的成员方法:创建外部类对象 使用外部类对象调用外部类的成员方法Outer o = new Outer();o.method();}}
5  匿名内部类

实例5 :通过接口使用匿名内部类

/** * 匿名内部类:(特性:匿名和内部类) 匿名:没有名字,不能显示的通过代码为它添加构造方法; *  * 是内部类的简化版格式 前提条件: 必须存在一个接口或者是一个类(可以是具体类(不常用),也可以是一个抽象类?); 即:必须继承一个父类或实现实现一个接口 *  * 书写的格式: new 父类构造器(参数列表)|接口(){ *               //方法重写(实现抽象类型的抽象方法);  *             } * 注意:1)当接口类型名是接口时,构造方法必须为空,因为接口是沒有构造方法的 *     2)当接口类型名是抽象类型,构造方法参数可以有参,因为抽象类型是有构造方法的{默认的创建父类对象} * 匿名内部类的实质: 继承了该类(抽象类)或者是实现了该接口的子类对象!  * 匿名内部类的语法特点: *  1) 字面上:没有名字的内部类 *  2) 只能使用一次(匿名),创建匿名内部类的时候,它会创建一个该类的实例,该类的定义立即消失  *  3) 不能为匿名内部类添加构造方法(因为匿名内部类是没有类名的)  *  4) 匿名内部类不能"显示"的继承某个类或者实现某个接口(匿名,做好事不留名) *  5) 匿名内部类没有任何修饰符(内部类的,包含abstract)  *  6) 不能是抽象类(所以它必须实现抽象父类或接口的的全部抽象方法)  *  7) 除了自身的规则,外部类内部中使用匿名内部类要符合成员内部类的语法规则,局部内部类内部中使用匿名内部类要符合局部内部类的语法规则  *  8) 定义一个匿名内部类和创建一个匿名内部类的对象的语法是绑定在一起的(捆绑消费) 9) 匿名内部类不能存在任何的静态成员(变量和方法) * */// 定义一个接口(接口组成?,语法要求(原因))interface Inter {//默认abstract// 抽象功能(只有)public abstract void show1();public abstract void show2();}// 外部类class Outer {// 在成员方法中使用内部类:通過接口使用匿名内部类public void method() {/* 方式1:定义一个匿名内部类和创建一个匿名内部类的对象的语法是绑定在一起的 * 这里匿名类:实现接口的子类 * 创建匿名内部类的对象:实际是创建了一个实现接口的子类对象 * 等价于:接口 引用=new 实现接口的子类(); * 补充:匿名内部类的类体必须包含所有的抽象方法 * */new Inter() {@Overridepublic void show1() {System.out.println("调用接口的抽象方法show1");}// 直接Alt+/(补全,选择对应的方法)@Overridepublic void show2() {System.out.println("调用接口的抽象方法show2");}}.show1();// 使用匿名对象调用方法show1():语法格式new Inter() {// new Inter后---快捷键"Alt+/"选第一个Anonymous inner Type---自动补全@Overridepublic void show1() {System.out.println("调用接口的抽象方法show1");}@Overridepublic void show2() {System.out.println("调用接口的抽象方法show2");}}.show2();System.out.println("---------------------------------");// 分割符/* 方式2: * 打出Inner inner=new 后直接Alt+/,自动生成重写的方法 *  * */Inter inner = new Inter() {/*说明: *1)创建一个匿名内部类(实现接口的子类)对象,并让接口的引用指向这个对象 *2)new后面跟着"Inter(){内容}"的不是接口,而是一个沒有名字实现了接口的匿名内部"类","{}"认为是这个匿名内部类的类体; *  理解为"new Inter(){内容};"创建了实现一个接口的子类对象 * */@Overridepublic void show1() {System.out.println("调用接口的抽象方法show1");}@Overridepublic void show2() {System.out.println("调用接口的抽象方法show2");}};//";"表示创建匿名内部类的对象创建结束inner.show1();// 使用对象名调用show1方法inner.show2();// 使用对象名调用show1方法}}// 测试类public class Demo {public static void main(String[] args) {Outer outer = new Outer();outer.method();}}

实例6:通过抽象类使用匿名内部类

package morning;/*代码说明:通过抽象类使用匿名内部类的对象 *核心:对于抽象类或接口的抽象方法,都要在匿名内部类中给予实现 *与接口的不同:匿名类的语法允许调用抽象类的构造方法(注意:不是匿名类的构造方法) *         匿名类的语法不允许调用接口中的构造方法(接口也本身没有构造方法,何谈调用?) *          *匿名内部类的用处(工作): *匿名内部类的(习题和面试题):    *补充:使用类(非抽象类)来使用匿名内部类对象(ps:很少用) *///定义一个抽象类(内部组成[回顾]语法要求(原因?))abstract class AbstractClass {private int a;// 成员变量AbstractClass(int a) {this.a = a;// 含参数构造方法(this:指向内部类对象的引用)}AbstractClass() {// 好习惯:给予一个默认的构造方法}// 成员方法public abstract void method();// 抽象方法public void method1() { // 非抽象的方法AbstractClass temp = new AbstractClass() {@Overridepublic void method() {// 在抽象类的方法内使用匿名内部类System.out.println("在抽象类的方法内使用匿名内部类创建抽象类的子类对象,使用构造抽象类的构造方法:"+a);}};temp.method();//使用抽象方法}}// 外部类(在外部类中实现匿名内部类)class Outer {/* * 在外部类的内部使用匿名内部类,匿名内部类符合成员内部类的语法规则此时,位置相当于与成员内部类平行 */AbstractClass temp = new AbstractClass(3) {@Overridepublic void method() {/* * 说明: 这里的匿名类:继承父类的子类 * 创建匿名内部类的对象:实际是创建了一个继承父类的子类对象 * 等价于:父类 引用=new 子类(); */System.out.println("通过抽象类在类内部使用匿名类,使用了抽象类的构造方法");}};}// 测试类public class Demo {public static void main(String[] args) {/* 说明: * 方法内使用匿名内部类,匿名内部类要符合局部内部类的语法规则 */AbstractClass temp2 = new AbstractClass(3) {@Overridepublic void method() {System.out.println("在方法内使用匿名内部类,匿名内部类要符合局部内部类的语法规则");}};temp2.method();//调用抽象类的构造方法temp2.method1();/*对上面一行代码的疑问?为什么也可以调用非抽象方法? *答:匿名类是继承了父类的子类,而不是仅仅继承了抽象方法 *//*int c=temp2.a; *上面报错的原因: *在外部不能访问私有方法,可以访问public的 *当匿名内部类位于方法中时,要符合局部内部类的语法规则 *其中一点:不能在外部类的外部不能访问private修饰的成员(变量和方法) * */System.out.println("--------------------------");//使用外部类的外部访问内部类的匿名内部类Outer outer=new Outer();outer.temp.method();}}
未完待续......


阅读全文
0 0