内部类(成员内部类、静态内部类、局部内部类、匿名内部类)

关于内部类的部分内容,正在深入了解中。
每天都在修改更新中。

一、成员内部类

/**
 *  需要知道两个前提:
 *  1.成员内部类是外围类的一个成员,它两关系特别好,推心置腹,抵足而眠,都是最好的亲兄弟
 *  所以访问权限什么的,根本不存在
 *
 *  2.内部类对象依赖于外围类对象存在
 *  思考:
 *      a,在成员内部类的成员方法中,有没有外围类对象呢? 有
 *
 *      成员内部类的成员方法必然是成员内部类的对象去调用的【理解】
 *      既然都已经有成员内部类的对象了,【为什么???】所以这里一定有外围类对象
 *
 *      b,在外围类的成员方法中,有没有内部类对象? 没有
 *      内部类对象依赖于外围类对象存在,外围类的对象虽然存在,但是内部类的对象就不一定了
 *==========================================================================================
 * - 【1】成员内部类内部访问外围类
 * 1,可以直接访问,不论访问权限
 * 2,如果访问外围类的静态成员,建议加上外围类类名(这样可读性更好)
 * 3,内部类的成员方法中隐含了传参EnclosedClazz.this,表示当前内部类的外围类对象
 * 可以用这个来解决外围类和内部类的同名成员问题
 * 当然 也隐含了一个this,代表当前内部类对象
 *
 * 这个this可以用来解决局部变量和成员变量同名的问题
 *
 * - 【2】外围类访问成员内部类成员
 * (1)在外围类的普通成员方法中 要自己手动创建内部类对象    可以用这个对象任意访问内部类成员,不论访问权限
 * (2)如果在外围类的静态方法中 访问内部类成员。必须创建两个对象 外围类对象的基础上创建内部类对象
 *
 * - 【3】外部类访问成员内部类成员
 *  必须要先创建外围类对象 再创建内部类对象
 *          注意:外部类访问内部类 受访问权限限制
 *          1,如果内部类是私有的,外部类无法创建对象
 *          2,即便是能够创建对象,内部类中的私有成员也不能访问
 *
 * - 【4】成员内部类访问外部类成员(了解)
 * > 在成员内部类中访问外部类成员,和在普通类中访问其它类成员别无二致
 *
 * - 静态成员直接类名点访问
 * - 普通成员需创建外部类对象
 * - 都受访问权限控制
 */
public class OutsideClazz {
    
     //外部类
    public static void main(String[] args) {
    
    
        //创建内部类对象
        //这里有没有外围类对象
        //EnclosedClazz enclosedClazz = new EnclosedClazz();
        //
        //EnclosedClazz.InnerClazz innerClazz = enclosedClazz.new InnerClazz();
        //
        //下面的格式 是创建成员内部类对象的万能格式
        //二合一
        EnclosedClazz.InnerClazz innerClazz = new EnclosedClazz().new InnerClazz();
        //System.out.println(innerClazz.a);

    }
}
class EnclosedClazz{
    
     //外围类
    //外围类中成员变量
    int a;
    private int b = 10;
    static int c = 20;
    static final int D = 30;
    //(1)外围类中的成员方法
    public void test(){
    
    
        //访问内部类成员必须要有内部类对象
        //这里有没有? 显然是没有的 所以要自己创建对象

        //这里已经存在了一个外围类对象【为什么???】 ,用this指示   所以直接创建内部类对象即可
        InnerClazz innerClazz = new InnerClazz();
        System.out.println(innerClazz.a);//不受访问权限限制
        //访问内部类的静态常量,直接用内部类类名就可以了
        System.out.println(InnerClazz.D);
        //EnclosedClazz.InnerClazz innerClazz2 = new EnclosedClazz().new InnerClazz();

    }
    //(2)外围类的静态方法中
    public static void testStatic(){
    
    
        //外围类的静态方法 这里有没有对象?     这里没有任何对象 包括外围类对象
        //所以在这里访问成员内部类成员 必须创建两个对象

        //1创建外围类对象
        EnclosedClazz enclosedClazz = new EnclosedClazz();
        //2创建内部类对象
        InnerClazz innerClazz = enclosedClazz.new InnerClazz();

        //3上面可以二合一      //有对象后,访问就一样了 不受访问权限控制
        InnerClazz innerClazz1 = new EnclosedClazz().new InnerClazz();
        //EnclosedClazz.InnerClazz innerClazz2 = new EnclosedClazz().new InnerClazz();

    }

    class InnerClazz{
    
     //成员内部类
        //思考:如果内部类中有和外围类同名的静态变量咋办? 错了 成员内部类没有静态变量
        //static int c = 200;
        //思考:如果内部类中有和外围类同名的静态常量咋办? 就用类名区分

        //思考:如果内部类中有和外围类同名的普通变量咋办? 普通类的成员方法中隐含了this传参 代表当前对象
        //而成员内部类的成员方法中也隐含了一个参数 代表当前内部类的外围类对象 用EnclosedClazz.this标记它
        static final int D = 300;
        private int a = 10;
        //依赖方法访问外围类成员   成员内部类的成员方法必然是成员内部类的对象去调用的【理解】
        public void testInner(){
    
    
            System.out.println(EnclosedClazz.this.a);//访问外围类的同名普通成员变量
            System.out.println(b);//成员内部类是外围类的一个成员,不存在访问权限
            System.out.println(EnclosedClazz.c);//访问外围类的静态成员,建议加上外围类类名(这样可读性更好)
            System.out.println(EnclosedClazz.D);//同上        这里D为外围类中的30
            System.out.println(this.D);//也隐含了一个this,代表当前内部类对象       这里D为300

        }
    }

}

二、静态内部类

/**
 * 前提:
 *  1,只要是内部类,外围类都是亲兄弟,都没有访问权限限制
 *  2,外围类和内部类对象就不会互相影响了 各干各的
 *
 * 需求:不希望依赖外围类,又不希望外部类能够访问,只能用静态内部类
 * - 【1】静态内部类内部访问外围类
 *  必须要创建外围类对象      静态成员推荐类名访问      不受访问权限控制
 *
 * - 【2】外围类访问静态内部类成员
 * 无论是什么方法,直接创建内部类对象完事      不受访问权限限制        如果是静态成员推荐使用类名访问
 *
 * - 【3】外部类访问静态内部类成员
 * 这里静态内部类是一个可以独立的类
 * 唯一的区别 就是需要类的声明访问权限限制
 * 语法:
 * EnclosedClazz.InnerStaticClazz ecisc = new EnclosedClazz.InnerStaticClazz();
 *
 * - 【4】静态内部类访问外部类成员(了解)
 * > 在静态内部类中,访问外部类成员,和在普通类中访问其他类成员别无二致
 *
 * - 静态成员,类名点直接访问
 * - 普通成员需创建对象访问
 * - 受访问权限控制
 *
 */
public class OutsideClazz {
    
     //外部类
    public static void main(String[] args) {
    
    
        //创建内部类对象
        //可以直接创建 但是它和普通类不同
        //需要告诉编译器 你这个内部类在哪里
        //EnclosedClazz.InnerClazz ic = new EnclosedClazz.InnerClazz();
        //System.out.println(ic.privateVar);
    }
}

class EnclosedClazz {
    
     //外围类
    int aEn;
    private int aEnPrivate = 10;
    static int b = 10;
    //成员方法
    public void test(){
    
    
        //这里没有静态内部类对象,需要创建
        //不受访问权限限制
    }
    public static void testStatic(){
    
    
        //需要创建外围类对象吗?
        InnerClazz innerClazz = new InnerClazz();
    }


  private    static class InnerClazz  {
    
     //静态内部类
        //虽然这个类加了static 但是你不要往静态成员考虑 这里static表示这个内部类是独立的
        //定义成员变量
        int aEn;
        private int privateVar = 100;
        private static int b = 10;
        static int c = 20;
        static final int D = 30;
        //定义成员方法
        public void test(){
    
    
            //访问外围类成员 需要外围类对象
            //思考:这里有没有外围类对象?
            //这里没有外围类对象,需要创建外围类对象
            EnclosedClazz ec = new EnclosedClazz();
            System.out.println(ec.aEn);             //外围类的就用外围类对象方法
            System.out.println(ec.aEnPrivate);
            //思考:这里如果内部类和外围类同名变量
            //外围类的就用外围类对象方法,自己的就用this访问
            System.out.println(this.aEn); //用this增加区分度      自己的就用this访问
            System.out.println(InnerClazz.b);//静态成员推荐类名访问


        }
        public static void testStatic(){
    
    }

    }
    class InnerStatic{
    
    }
}

三、局部内部类

public class Demo {
    
     //外部类
    public static void main(String[] args) {
    
    
        //int a  = 10;
        for (int i = 0; i < 1; i++) {
    
    
            class T{
    
    }
        }
        if (true){
    
    
            class T{
    
    }
        }
        //switch
    }
   static  {
    
    
        class S{
    
    }
    }

}

class EnclosedClazz {
    
     //外围类

    public void test() {
    
     //普通的成员方法
        //定义局部内部类
         class InnerLocalClazz{
    
    
            int a;
            private int b = 10;
            //Inner classes cannot have static declarations
            //static int c =100;
             static final  int D = 100; //静态常量是不需要类加载
             //Inner classes cannot have static declarations
             //static final  Demo DEMO = new Demo();//静态的引用类型常量 只有String不触发类加载
             public void test(){
    
    }
             //public static void test2(){}
        }
    }
}

=====================================================================================
public class Demo {
    
    
    public static void main(String[] args) {
    
    

    }
}

class EnclosedClazz {
    
     //外围类
    int a;
    private int b = 20;
    static int c = 10;

    public static void main(String[] args) {
    
    
        EnclosedClazz enclosedClazz = new EnclosedClazz();
        enclosedClazz.test();
    }

    public void test() {
    
     //普通成员方法
        //可以直接访问外围类成员,因为普通成员方法隐含this指向当前对象 所以已经有对象
        //能够直接访问
        class InnerLocalClazz {
    
     //局部内部类
            //局部内部类的成员方法
            public void test() {
    
    
                System.out.println(a);   //0
                System.out.println(b);   //20
                System.out.println(EnclosedClazz.c);   //10
            }
        }
        //在方法中创建对象
        //触发类加载 也只有这一种方式 只能在这里创建对象
        InnerLocalClazz innerLocalClazz = new InnerLocalClazz();
        //调用局部内部类的方法
        innerLocalClazz.test();    //这里输出3个值
    }

    public static void testStatic() {
    
     //静态成员方法
        class InnerLocalClazz{
    
    
            //思考:这里能够直接访问外围类成员吗?
            //不能,原因是静态的方法没有当前对象
            public void test(){
    
    
                //System.out.println(b);
            }
        }

    }
}

局部内部类对象和方法局部变量的生命周期对比

局部内部类对象要比局部变量活得更久	jvm放的副本啥时候消失:跟对象同生共死
在Java8之前,这个方法的局部变量中的final必须由程序员手动加上去的,
但是8以后,Java开发者把这个final隐藏到了代码底层

public class Demo {
    
     //外部类
    public static void main(String[] args)  {
    
    
        EnclosedClazz enclosedClazz = new EnclosedClazz();
      Father f =   enclosedClazz.test(); //这里返回的实际是一个InnerSon对象
        //父类引用指向子类对象 多态调用

        f.testInner(); //这里已经没有a了
        //但是仍然能够打印a的值,说明jvm偷偷的帮我们做了一件事:
        // 把方法的局部变量a 塞到了对象当中

    }
}

class EnclosedClazz {
    
     //外围类
    public Father test() {
    
    
        //定义局部内部类 让它继承Father
        int a = 10; //当test调用结束 a就销毁了 无了
        class InnerSon extends Father{
    
    

            @Override
            public void testInner() {
    
    
                System.out.println("2");
                //a = 20;
                //System.out.println(a);
            }
        }
        //再次用a
        //创建局部内部类对象
        InnerSon is = new InnerSon();
        //我直接把这个对象作为返回值返回
        return is; //子类可以看成父类 向上转型 自动
        //is.test(); //2
    }

}

class Father {
    
    
    public void testInner() {
    
    
        System.out.println("1");
    }
}
=====================================================================================
局部内部类的经典使用
/**
 *写一个方法去返回接口或者抽象类的具体子类
 *
 * 用局部内部类返回一个接口实现类 适合用于想偷懒 不想写接口实现类的适合
 * 适合于 只使用一次的实现类 或者就想调用一次接口的方法
 * 但是对于多次调用,这种方式就不适用
 *
 */
public class Demo {
    
    
    public static void main(String[] args) {
    
    
        //经典写法调用
        I i = getIInstance();
        i.test();

        //内部类的高端写法 调用
        I i2 = getIInstance2();
        i2.test();


    }
    public static I getIInstance(){
    
    
        //经典写法
        //先用一个类实现这个接口
        //然后创建这个类的对象
        return new IImpl(); //匿名对象
    }

    //高端的写法,使用局部内部类
    public static I getIInstance2(){
    
    
        //定义局部内部类
        class IInner implements I{
    
    
            @Override
            public void test() {
    
    
                System.out.println("222222");
            }
        }
        //创建局部内部类对象
        //IInner iInner =
        return new IInner();
    }
}

interface I{
    
    
     void test();
}
class IImpl implements I{
    
    
    @Override
    public void test() {
    
    
        System.out.println("1111111");
    }
}


四、匿名内部类

/**
 * 匿名内部类:是匿名的(局部)内部类,也就是说匿名内部类本质上是局部内部类
 * 匿名对象:没有名字的对象
 * 匿名内部类:没有名字的(局部)内部类
 *
 * 匿名内部类的使用优缺点:
 * ​	1,当我们只使用一次某类或者某接口的子类对象时,使用匿名内部类,会稍微方便一点
 * ​	2,如果需要多次访问子类对象的成员,匿名内部类很麻烦。
 * ​	3,如果访问匿名子类中的独有方法,必须用匿名对象去访问
 *
 */
public class Demo {
    
    
    public static void main(String[] args) {
    
    
        //老花样:创建接口的实现类
        I i = new IA();
        i.test();

        //新花样:局部内部类创建接口实现类
        class InnerLocalClazz implements I{
    
    
            @Override
            public void test() {
    
    
                System.out.println("高端的写法,总是这么朴实无华,枯燥!");
            }
        }
        //直接创建局部内部类对象
        InnerLocalClazz innerLocalClazz = new InnerLocalClazz();
        innerLocalClazz.test();

        //新新花样:在局部内部类的基础上,我干脆连你这个内部类我都不要了,直接创建局部内部类对象,也就是匿名局部类
        //语法: 直接创建对象: new 接口或者类(){};
        //方式一
        I i2 = new I() {
    
    
            @Override
            public void test() {
    
    
                System.out.println("我是匿名内部类对象中的test方法");
            }

            public void test2(){
    
    

            }
        };//new加上后面一块就是对象,可以用父类去接收它。实际上想接收 也只能用父类去接收
        i2.test();
        //i2.test2();  因为编译看左边,所以调用不了子类独有方法
        //((I) i2).test2(); //因为这个子类是匿名的,也没有办法强转它
        //总结,这种方式 打死了 也访问不了子类的独有方法

        //方式二
        //以上匿名内部类的对象也可以不接收
        new I(){
    
    
            @Override
            public void test() {
    
    
                System.out.println("不用父类接收");
            }public void test2() {
    
    
                System.out.println("子类独有");
            }
        }.test2();

        //以上两种方式,各有什么优缺点
        //方式一用接口去接收后,这个对象就有引用,可以反复使用,但是方式一是用父类引用去接收的,这样就用不了子类中独有的方法
        //方式二没有去接收,这个对象没有引用,只能用一次,用完后就成为垃圾了。但是方式二 可以调用子类独有方法
        //方式一 方式二如果都能用 优先使用方式二 因为方式二省事


        //用匿名内部类创建类的子类
        //语法:new 类(){};
        new A(){
    
    
            @Override
            public void test() {
    
    
                System.out.println("我是普通类的匿名内部类子类对象");
            }
        }.test();

        System.out.println(new AbstractA() {
    
    
            @Override
            public int test() {
    
    
                return 10;
            }
        }.test());
//这一块相当于就是10 所以就可以对他进行 10 的操作 比如赋值 比如计算 比如打印

        A a = new A() {
    
    
        }; //它如果能够做自己,那它到底是什么类型呢? 如果它做儿子,还能用父类来接收 隐含的继承
    }
}
interface I{
    
    
    void test();
}
class IA implements I{
    
    
    @Override
    public void test() {
    
    
        System.out.println("我是IA I接口的实现类");
    }
}
class A{
    
    
    public void test(){
    
    }
}
abstract class AbstractA{
    
    
    public abstract int test();
}

猜你喜欢

转载自blog.csdn.net/AC_872767407/article/details/113705849