JAVA学习遇到的那些事儿~

叁叁肆2018-09-19 13:27

本文来自网易云社区

作者:石晓琴


最近学习了Java内部类,为我打开了另一片空间。让我对Java面向对象的思想认识更深刻了。

Java内部类是和普通Java类相对来说的。由字面意思理解,内部类是定义在另一个Java内部的类。事实上也正是这样。我通过内部类的分类及其定义、创建内部类对象、内部类和外部类的数据交换以及内部类的使用场景这四个角度来学习内部类。


一、内部类的分类及其定义

内部类按有没有static修饰符分为静态内部类和非静态内部类。静态内部类可以通过类名被外部类调用。而非静态内部来在被使用的时候,必须通过对象调用。定义方式如下图所示。

      

图1 静态内部类和非静态内部类

上图中,Weather是外部类,Temprature是Weather的静态内部类,Humidity是Weather的非静态内部类。

根据访问权限修饰符,内部类可以分为私有的private内部类,包内访问的内部类,继承访问的protected内部类和对所有类公开的public内部类。定义方式如下如所示,

 

   图2 不同访问修饰符修饰的内部类

上图中,Animal是外部类,Bone是Animal的允许公开访问的内部类,Fur是Animal的允许继承访问的内部类,Skin是包可见的内部类,Sound是Animal的私有内部类。

再根据访问权限关键字和静态修饰组合就可以把各种内部类分为静态形式和非静态形式。在此不一一举例了。

上述几种内部类定义时都是有class关键字,并且后面跟了类名的内部类,还有一种特殊的内部类允许不加类名,即匿名内部类。如下图所示,定义一个匿名内部类,

图3 定义一个接口

图4 定义一个匿名内部类

在图3中,定义了一个叫Action的接口,该接口有一个run的方法。而在图4中有一个叫Dog的外部类,里面直接new了一个Action接口的实例。而这种方法正是使用了匿名内部类的方法。为什么图4中的一个匿名内部类,而不是理解成new了一个叫Action的类呢?因为Action是一个接口,是不能直接实例化的,只有某各类实现了Action的接口方法才可以被实例化。所以new Action实际上一个隐藏了名字的匿名内部类。我在声明这个类的时候没有定义名字。是匿名的。

内部类和外部类一样,也可以有默认的构造方法和带参数的构造方法。如果在定义内部类是设定了一个有参数的构造方法,而没有定义无参的构造方法,那么该内部类和外部类一样也不能用无参的形式来创建一个内部类对象。如下图所示:

     

  图5 内部类的构造方法

上图中,Temprature是Weather的内部类,该内部类有两个构造方法:默认不带参数的构造方法和带一个degree参数的构造方法。 


二、创建内部类对象

内部类不同于外部类,在不同的地方,不同的内部类创建对象的方式是不同的。从大的方向出发,内部类的创建方式分为在外部类中创建内部类的方式、在其他类中创建内部类的方式和匿名内部类创建对象的方式。而在其他类中创建内部类的方式又具体分为静态内部类创建对象的方式和非静态内部类创建对象的方式。现在举例说明如下:

1、外部类中创建静态内部类和非静态内部类的方式。可以直接使用内部类类名 对象名 = new 内部类类名(参数);

来创建对象,如下图所示:

   

      图6 静态内部类和非静态内部类在外部类中创建对象的方法

上图中,Weather是Temprature(静态)和Humidity(非静态)两个内部类的共同外部类。createTemprature方法中有外部类中创建内部类对象的语句。两者创建对象的方式一致。

2、在其他类中创建静态内部类的方法

           

  图7 其他类中创建静态内部类的方式

上图中,WeatherReport不在外部类Weather中,当在其中创建Weather类中静态内部类Temprature时,只有

外部类.内部类 对象名 = new 外部类.内部类(参数);

是正确的,其他两种方式都是错误的。

3、在其他类中创建非静态内部类的方法

                              

图8 其他类中创建非静态内部类的方式

上图中,WeatherReport2不在外部类Weather中,当在其中创建Weather类中非静态内部类Humidity时,只有

外部类.内部类 对象名 = 外部类实例.new 内部类(参数);

是正确的,其他方式都是错误的。

4、匿名内部类的创建对象方式

正如匿名内部类创建对象的方式,匿名内部类在定义是就是顺带产生实例对象的。因为匿名内部类没有名字,所以无法在外部类之外创建匿名内部类的对象。即使在外部类中,匿名内部类也只有一次机会创建对象。所以匿名内部类创建对象的方式相对简单,只是形式少许复杂。可以参考图3和图4。

三、内部类与外部类的数据交换        

要想使用内部类,主要是要解决内部类和外部类的数据交换的问题。那么如何进行数据交换的?

1、有类名的内部类是可以多次创建类对象的,所以在创建内部类对象的时,将外部类的数据传入内部类中。如下如所示,

          

       图9 创建内部类对象时传入外部类中的参数

2、在外部类调用内部类的方法,可以设置或者获得内部类的数据。

          

         图10 通过调用内部类的get、set方法来获取和设置内部类的属性值

图11 执行结果

3、在内部类中通过外部类类名加this调用外部类的成员变量或者方法

        

图12 在内部类中调用外部类的方法

图13 执行结果

4、将外部类的this作为参数传入内部类中,在内部类中调用该对象的方法       

                                                

 图14 内部类直接获取外部类成员变量的值

图15 执行结果

 

四、内部类是使用场景

内部类特别适合类比较小,相对独立,而在逻辑上又非常依赖外部类的地方。使用时不能想用内部类而使用内部类,而必须按实际情况来。比如一个类有几十个字段,就不太适合做内部类使用。不然会显得外部类过于庞大臃肿。不方便后期回复。所以内部类使用原则就是适用于短小精悍。对于匿名内部类,通常只能创建一个对象。现实中是否会存在这样的场景呢?答案是肯定的。显示场景中会出现使用一个只有一两个方法的接口的对象。而且这个对象只使用一次就够了。那么这种情况就非常适合于匿名内部类。既省了命名资源,又方便维护。

虽然对内部类进行了学习,但是学习没有涉及内部类的方方面面,只是针对重要的部分进行学习整理。出错在所难免。如果遇到错误和不足的地方,欢迎大家指正~


网易云免费体验馆0成本体验20+款云产品!

更多网易研发、产品、运营经验分享请访问网易云社区