什么是内部类?内部类的作用及分类

内部类我们从外面看是非常容易理解的,无非就是在一个类的内部在定义一个类。

public class OuterClass {

    private String name ;

    private int age;

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public int getAge() {

        return age;

    }

    public void setAge(int age) {

        this.age = age;

    }


    class InnerClass{

        public InnerClass(){

            name = "chenssy";

            age = 23;

        }

    }

}

 在这里InnerClass就是内部类,对于初学者来说内部类实在是使用的不多,鄙人菜鸟一个同样没有怎么使用过(貌似仅仅只在做swing 注册事件中使用过),但是随着编程能力的提高,我们会领悟到它的魅力所在,它可以使用能够更加优雅的设计我们的程序结构。在使用内部类之间我们需要明白为什么要使用内部类,内部类能够为我们带来什么样的好处

一、为什么要使用内部类

为什么要使用内部类?在《Think in java》中有这样一句话:使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。

在我们程序设计中有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。

public interface Father {

}

public interface Mother {

}

public class Son implements Father, Mother {

}

public class Daughter implements Father{

    class Mother_ implements Mother{


    }

}

其实对于这个实例我们确实是看不出来使用内部类存在何种优点,但是如果Father、Mother不是接口,而是抽象类或者具体类呢?这个时候我们就只能使用内部类才能实现多重继承了。

      其实使用内部类最大的优点就在于它能够非常好的解决多重继承的问题,但是如果我们不需要解决多重继承问题,那么我们自然可以使用其他的编码方式,但是使用内部类还能够为我们带来如下特性(摘自《Think in java》):

   1、内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立。

 2、在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。

 3、创建内部类对象的时刻并不依赖于外围类对象的创建。

 4、内部类并没有令人迷惑的“is-a”关系,他就是一个独立的实体。

 5、内部类提供了更好的封装,除了该外围类,其他类都不能访问。

二、内部类基础

在这个部分主要介绍内部类如何使用外部类的属性和方法,以及使用.this与.new。

      当我们在创建一个内部类的时候,它无形中就与外围类有了一种联系,依赖于这种联系,它可以无限制地访问外围类的元素。

public class OuterClass {

    private String name ;

    private int age;

    /**省略getter和setter方法**/


    public class InnerClass{

        public InnerClass(){

            name = "chenssy";

            age = 23;

        }


        public void display(){

            System.out.println("name:" + getName() +"  ;age:" + getAge());

        }

    }


    public static void main(String[] args) {

        OuterClass outerClass = new OuterClass();

        OuterClass.InnerClass innerClass = outerClass.new InnerClass();

        innerClass.display();

    }

}

在这个应用程序中,我们可以看到内部了InnerClass可以对外围类OuterClass的属性进行无缝的访问,尽管它是private修饰的。这是因为当我们在创建某个外围类的内部类对象时,此时内部类对象必定会捕获一个指向那个外围类对象的引用,只要我们在访问外围类的成员时,就会用这个引用来选择外围类的成员。

      其实在这个应用程序中我们还看到了如何来引用内部类:引用内部类我们需要指明这个对象的类型:OuterClasName.InnerClassName。同时如果我们需要创建某个内部类对象,必须要利用外部类的对象通过.new来创建内部类:OuterClass.InnerClass innerClass = outerClass.new InnerClass();。

      同时如果我们需要生成对外部类对象的引用,可以使用OuterClassName.this,这样就能够产生一个正确引用外部类的引用了。当然这点实在编译期就知晓了,没有任何运行时的成本。

public class OuterClass {

    public void display(){

        System.out.println("OuterClass...");

    }


    public class InnerClass{

        public OuterClass getOuterClass(){

            return OuterClass.this;

        }

    }


    public static void main(String[] args) {

        OuterClass outerClass = new OuterClass();

        OuterClass.InnerClass innerClass = outerClass.new InnerClass();

        innerClass.getOuterClass().display();

    }

}

到这里了我们需要明确一点,内部类是个编译时的概念,一旦编译成功后,它就与外围类属于两个完全不同的类(当然他们之间还是有联系的)。对于一个名为OuterClass的外围类和一个名为InnerClass的内部类,在编译成功后,会出现这样两个class文件:OuterClass.class和OuterClass$InnerClass.class。

      在Java中内部类主要分为成员内部类、局部内部类、匿名内部类、静态内部类。

三、成员内部类

成员内部类也是最普通的内部类,它是外围类的一个成员,所以他是可以无限制的访问外围类的所有 成员属性和方法,尽管是private的,但是外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。

在成员内部类中要注意两点,第一:成员内部类中不能存在任何static的变量和方法;第二:成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类。

public class OuterClass {

    private String str;


    public void outerDisplay(){

        System.out.println("outerClass...");

    }


    public class InnerClass{

        public void innerDisplay(){

            //使用外围内的属性

            str = "chenssy...";

            System.out.println(str);

            //使用外围内的方法

            outerDisplay();

        }

    }


    /*推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时 */

    public InnerClass getInnerClass(){

        return new InnerClass();

    }


    public static void main(String[] args) {

        OuterClass outer = new OuterClass();

        OuterClass.InnerClass inner = outer.getInnerClass();

        inner.innerDisplay();

    }

}

四、局部内部类

有这样一种内部类,它是嵌套在方法和作用于内的,对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类,局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效。

      对于局部内部类实在是想不出什么好例子,所以就引用《Think in java》中的经典例子了。

      定义在方法里:

public class Parcel5 {

    public Destionation destionation(String str){

        class PDestionation implements Destionation{

            private String label;

            private PDestionation(String whereTo){

                label = whereTo;

            }

            public String readLabel(){

                return label;

            }

        }

        return new PDestionation(str);

    }


    public static void main(String[] args) {

        Parcel5 parcel5 = new Parcel5();

        Destionation d = parcel5.destionation("chenssy");

    }

}

定义在作用域内:

public class Parcel6 {

    private void internalTracking(boolean b){

        if(b){

            class TrackingSlip{

                private String id;

                TrackingSlip(String s) {

                    id = s;

                }

                String getSlip(){

                    return id;

                }

            }

            TrackingSlip ts = new TrackingSlip("chenssy");

            String string = ts.getSlip();

        }

    }


    public void track(){

        internalTracking(true);

    }


    public static void main(String[] args) {

        Parcel6 parcel6 = new Parcel6();

        parcel6.track();

    }

}

五、匿名内部类

我们经常使用这种方式来绑定事件
button2.addActionListener(

                new ActionListener(){ 

                    public void actionPerformed(ActionEvent e) { 

                        System.out.println("你按了按钮二"); 

                    } 

                });

这里我们就需要看清几个地方

 1、 匿名内部类是没有访问修饰符的。

   2、 new 匿名内部类,这个类首先是要存在的。如果我们将那个InnerClass接口注释掉,就会出现编译出错。

 3、 注意getInnerClass()方法的形参,第一个形参是用final修饰的,而第二个却没有。同时我们也发现第二个形参在匿名内部类中没有使用过,所以当所在方法的形参需要被匿名内部类使用,那么这个形参就必须为final。

 4、 匿名内部类是没有构造方法的。因为它连名字都没有何来构造方法。

六、静态内部类

在java提高篇-----关键字static中提到Static可以修饰成员变量、方法、代码块,其他它还可以修饰内部类,使用static修饰的内部类我们称之为静态内部类,不过我们更喜欢称之为嵌套内部类。静态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。没有这个引用就意味着:

 1、 它的创建是不需要依赖于外围类的。

 2、 它不能使用任何外围类的非static成员变量和方法。

public class OuterClass {

    private String sex;

    public static String name = "chenssy";


    /**

    *静态内部类

    */

    static class InnerClass1{

        /* 在静态内部类中可以存在静态成员 */

        public static String _name1 = "chenssy_static";


        public void display(){

            /*

            * 静态内部类只能访问外围类的静态成员变量和方法

            * 不能访问外围类的非静态成员变量和方法

            */

            System.out.println("OutClass name :" + name);

        }

    }


    /**

    * 非静态内部类

    */

    class InnerClass2{

        /* 非静态内部类中不能存在静态成员 */

        public String _name2 = "chenssy_inner";

        /* 非静态内部类中可以调用外围类的任何成员,不管是静态的还是非静态的 */

        public void display(){

            System.out.println("OuterClass name:" + name);

        }

    }


    /**

    * @desc 外围类方法

    * @author chenssy

    * @data 2013-10-25

    * @return void

    */

    public void display(){

        /* 外围类访问静态内部类:内部类. */

        System.out.println(InnerClass1._name1);

        /* 静态内部类 可以直接创建实例不需要依赖于外围类 */

        new InnerClass1().display();


        /* 非静态内部的创建需要依赖于外围类 */

        OuterClass.InnerClass2 inner2 = new OuterClass().new InnerClass2();

        /* 方位非静态内部类的成员需要使用非静态内部类的实例 */

        System.out.println(inner2._name2);

        inner2.display();

    }


    public static void main(String[] args) {

        OuterClass outer = new OuterClass();

        outer.display();

    }

}

上面这个例子充分展现了静态内部类和非静态内部类的区别。

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

推荐阅读更多精彩内容

  • 转载:https://juejin.im/post/5a903ef96fb9a063435ef0c8 本文将会从以...
    福later阅读 393评论 0 3
  • 小编费力收集:给你想要的面试集合 1.C++或Java中的异常处理机制的简单原理和应用。 当JAVA程序违反了JA...
    八爷君阅读 4,573评论 1 114
  • DAY 05 1、 public classArrayDemo { public static void mai...
    周书达阅读 649评论 0 0
  • 最近在设计公司的用户体系。由于我们公司暂时有三个产品,不同类型。每个类型都承载着完全不同业务领域。因此就需要考虑用...
    枯藤老叔阅读 3,403评论 0 11
  • 1月23日,阴。 阅读书目:《如何把事情做到最好》。 作者:美国作家乔治•伦纳德 ,二战期间学习驾驶战斗机,并留校...
    陈陈_19b4阅读 326评论 0 1