Java 内部类的一些使用与梳理

来源:慕课网

    Java 内部类有许多需要注意的地方 ,相信大家平时都在用,但是有些地方是需要注意的,在这里给大家总结一下。

内部类的位置

    public class A {
        class B {
        }

        public void pint() {
            class C {
            }
            new C();
        }

        public void pint(boolean b) {
            if (b) {
                class D {
                }
                new D();
            }
        }
    }

    从代码中可以看出,内部类可以定义到很多地方,常用的是成员变量中(B),方法中也叫局部内部类(C),作用域中(D)。

从上面来看似乎没有用到过在方法中和作用域中的情况啊,这就错了。再来看看这个:

    public interface AInterface {
        void show();
    }

    public class B {
        public void show() {
            class Man implements AInterface {
                @Override
                public void show() {
                }
            }
            Man man = new Man();
            man.show();
        }
    }

    其中我们定义了两个文件,一个文件是一个接口类,一个是B文件;在B类中,的show()方法中我们使用了局部内部类的方式创建了类ManMan class继承接口并实现方法,随后使用该类。

内部类的权限

为什么要有内部类的存在?

    在我看来类主要的就是封装、继承、多态;当然其中的回调思想我认为是很重要的;而内部类的出现就是为了简化多重继承的问题;一个A类,并不能继承多个其他类,但是在使用中又需要使用到其他类的方法,这个时候内部类就发挥作用了;典型的就是事件点击的回调实现。

那么内部类的权限究竟有多大?

至于答案是什么,代码上看看就知道了。

    public class C {
        int a = 1;
        private int b = 2;
        protected int c = 3;
        public int d = 4;

        void a() {
            System.out.println("A:" + a);
        }

        private void b() {
            System.out.println("B:" + b);
        }

        protected void c() {
            System.out.println("C:" + c);
        }

        public void d() {
            System.out.println("D:" + d);
        }

        class D {
            void show() {
                int max = a + b + c + d;
                a();
                b();
                c();
                d();
                System.out.println("Max:" + max);
            }
        }

        public static void main(String[] args) {
            D d = new C().new D();
            d.show();
        }
    }

运行结果:


可以看出,内部类 D 对类 C 具有完整的访问权限,等于全身脱光了给你看。

那要是反过来呢?

    public class C {
        class D {
            private int a = 20;

            private void a() {
                System.out.println("D.A:" + a);
            }
        }

        void show() {
            D d = new D();
            d.a();
            System.out.println("D.A:" + d.a);
        }

        public static void main(String[] args) {
            new C().show();
        }
    }

运行结果:


  可见也是完全可行的,也能直接访问私有属性 私有方法,在这里似乎私有的限制已经失效了一般,这个让我想起了以前看见过一个面试:在 Java 中 private 修饰何时会失效。

这完全是两个人互相脱光光了啊~

匿名内部类

这个非常常见,特别是在按钮点击事件绑定中。

    public class D {
        void initButton() {
            Button b1 = new Button();
            b1.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(Button v) {
                }
            });
            Button b2 = new Button();
            b2.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(Button v) {
                }
            });
        }
    }

其中的:

    new OnClickListener() {

            @Override
            public void onClick(Button v) {

            }
        }

  就是匿名内部类的使用方式,OnClickListener是一个接口类,接口类是无法直接new 一个实例的;这里也并不是那样,而是new 了一个其他的类,该类是匿名的,也就是没有名字,只不过该类实现了 OnClickListener接口类中的方法。

上面的添加回调部分可等同于:

    public class D {
        void initButton1() {
            Button b1 = new Button();
            b1.setOnClickListener(new Listener1());
            Button b2 = new Button();
            b2.setOnClickListener(new Listener2());
        }

        class Listener1 implements OnClickListener {
            @Override
            public void onClick(Button v) {
            }
        }

        class Listener2 implements OnClickListener {
            @Override
            public void onClick(Button v) {
            }
        }
    }

  这里就是先建立类,继承自接口;而后赋值到 Button 中。
  要说两者的区别与好处,这个其实看具体的使用情况吧;如果你的按钮很多,但是为了避免建立太多类;那么可以建立一个回调类,然后都赋值给所有的按钮,不过最后就是需要在 onClick方法中进行判断是那个按钮进行的点击。
匿名内部类的使用地方很多;具体的使用应视使用情况而定~

静态内部类/静态嵌套类

  这个其实并不应该叫做内部类了,因为其并不具备内部类的完全权限,在使用上与一般的类基本一样;那为什么会有这个的存在?
  在我看来这个类的存在是为其包括类服务;意思是可以单独服务,不被外面的类所知晓;如这样:

    public class E {
        private void show() {
            new A();
        }

        private static class A {
        }
    }

  其中类 A使用了 static ,所以是静态嵌套类,在这里使用private 修饰;那么该类只能在 E类中进行实例化;无法在 其他文件中实例化。
  这样的情况使用外面的类能行么?不行吧?也许你会说在 E.java 文件夹中建立 A.java ,并使用protected修饰;但是在同样的包下,或者继承的类中同样能访问了;这也只是其中一个较为特殊的情况。

我们来看看权限

    public class E {
        int a1 = 0;
        private int a2 = 0;
        protected int a3 = 0;
        public int a4 = 0;

        private void show() {
            A a = new A();
            System.out.print("b1:" + a.b1);
            System.out.print("b2:" + a.b2);
            System.out.print("b3:" + a.b3);
            System.out.print("b4:" + a.b4);
        }

        private static class A {
            int b1 = 0;
            private int b2 = 0;
            protected int b3 = 0;
            public int b4 = 0;

            private void print() {
                System.out.print("a1:" + a1);
                System.out.print("a2:" + a2);
                System.out.print("a3:" + a3);
                System.out.print("a4:" + a4);
            }
        }
    }

在这个中的结果是怎样?


从图片中可以看出,其权限级别是单方向的;静态嵌套类 A 对其包含类 E 完全透明;但 E 并不对 A 透明。

再来看看方法:

  可以看出同样的情况;这个是为什么呢?为什么就是多一个 static 的修饰就这么完全不同?其是很好理解,两个独立的类;本来就无法直接使用,必须有引用才能调用其属性与方法。

我们或许可以这么调整一下就OK:

    public class E {
        int a1 = 0;
        private int a2 = 0;
        protected int a3 = 0;
        public int a4 = 0;

        private void show() {
            A a = new A();
            System.out.print("b1:" + a.b1);
            System.out.print("b2:" + a.b2);
            System.out.print("b3:" + a.b3);
            System.out.print("b4:" + a.b4);
            a.b1();
            a.b2();
            a.b3();
            a.b4();
        }

        void a1() {
        }

        private void a2() {
        }

        protected void a3() {
        }

        public void a4() {
        }

        private static class A {
            int b1 = 0;
            private int b2 = 0;
            protected int b3 = 0;
            public int b4 = 0;

            void b1() {
            }

            private void b2() {
            }

            protected void b3() {
            }

            public void b4() {
            }

            private void print(E e) {
                System.out.print("a1:" + e.a1);
                System.out.print("a2:" + e.a2);
                System.out.print("a3:" + e.a3);
                System.out.print("a4:" + e.a4);
                e.a1();
                e.a2();
                e.a3();
                e.a4();
            }
        }
    }

在其静态类中传递一个 E 的引用进去就能解决问题了:


两者之间的隐藏区别

  但是最开始上面的内部类是怎么回事?难道是闹鬼了?上面的内部类没有传递引用的啊;为啥加上一个 static 就不行了?
在这里我们需要看看字节码,我们先建立一个简单的内部类:

public class F {

    class A{

    }
}

这个够简单吧?别说这个都难了;汗~
然后我们找到 class 文件,然后查看字节码:


在这里分别查看了F 类的字节码和 F$A 类的字节码。

其中有这样的一句: final F this$0; 这句是很重要的一句,这句出现的地方在其内部类中,意思是当你 new 一个内部类的时候就同时传递了当前类进去;所以在内部类中能具有当前类的完全权限,能直接使用所有的东西;就是因为在隐藏情况下已经传递了当前类进去。

那么我们再看看一个简单的静态内部类:

public class G {
    static class A {

    }
}

与上面的区别唯一就是在于添加了一个 static 。此时我们看看字节码:


可以看出其中无论是 G 类,还是 G$A 类的初始化中都没有其他多余的部分,也没有进行隐藏的传递进去当前类;所以这样的情况下并不具备访问权限,需要我们传递引用进去,可以通过接口也可以完全传递进去,具体取决于个人。所以加了static类的内部类除了在权限上比一般的类更加开放(与其包含类)外,与一般的类在使用上是一样的;所以准确的说应该叫做静态嵌套类。

初始化的区别

一个类中,同时包含了内部类与静态内部类,那么其初始化应该是怎么样的呢?

都是直接 new ?还是看看代码:

    public class H {
        int a = 1;

        public class A {
            public void Show() {
                System.out.print("a:" + a);
            }
        }

        public static class B {
            public void Show(H h) {
                System.out.print("a:" + h.a);
            }
        }

        public static void main(String[] args) {
            H h = new H(); 
            //A a = new A(); 
            A a1 = h.new A(); 
            B b = new B(); 
            //B b1 = h.new B(); 
            B b3 = new H.B();
        }
    }

  其中注释了的两种方式是不允许的方式,也就是无法正常运行。

  A 因为有一个隐藏的引用,所以必须是H 的实例才能进行初始化出A 类;而B 类则是因为是在H 类中以静态方式存在的类,所以需要 new H.B();之所以能直接使用new B(),与该 main 方法在 H 类中有关,因为本来就在H类中,所以直接使用 H类的静态属性或者方法可以不加上:“H.” 在前面。

内部类的继承

直接继承的情况:


可以看出报错了,为什么?因为需要传递一个 H 类进去,所以我们在继承的时候需要显示的指明:

public class I extends H.A{
    public I(H h){
        h.super();
    }
}

也就是在构造方法中,传递一个 H 的引用进去,并调用 H 实例的 super() 方法,才能进行实例化。

使用的话应该这样:

    public static void main(String[] args) {
        H h = new H();
        I i = new I(h);
    }

而,如果是继承其静态嵌套类,则不需要这样:

public class J extends H.B{

}

就这样就OK。
哎,差不多了~~整个内部类的东西差不多就是这些了,希望对大家有所帮助。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,577评论 18 399
  • 1 面向对象No6 面向对象 OO Object Oriented 编程时以对象为单元,封装数据和逻辑,以此提...
    征程_Journey阅读 1,124评论 0 2
  • (一)Java部分 1、列举出JAVA中6个比较常用的包【天威诚信面试题】 【参考答案】 java.lang;ja...
    独云阅读 7,066评论 0 62
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,451评论 25 707
  • 我把回忆翻出来,安慰苍白空洞的心。一直以来,我以为我好了,可在某些脆弱时刻忽然发现,我只是把自己封闭起来了,封锁住...
    波罗的海的记忆阅读 436评论 0 0