内部类初识

可以将一个类的定义放在另一个类的定义内部,这就是内部类。

内部类是一个非常有用的特性但又比较难理解使用的特性(鄙人到现在都没有怎么使用过内部类,对内部类也只是略知一二)。

第一次见面

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

在这里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。

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

 1 public class OuterClass {
 2     private String name ;
 3     private int age;
 4
 5     /**省略getter和setter方法**/
 6
 7     public class InnerClass{
 8         public InnerClass(){
 9             name = "chenssy";
10             age = 23;
11         }
12
13         public void display(){
14             System.out.println("name:" + getName() +"   ;age:" + getAge());
15         }
16     }
17
18     public static void main(String[] args) {
19         OuterClass outerClass = new OuterClass();
20         OuterClass.InnerClass innerClass = outerClass.new InnerClass();
21         innerClass.display();
22     }
23 }

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

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

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


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

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();

    }

}

-------------

Output:

OuterClass...

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

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

三、成员内部类

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

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


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

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();

    }

}

--------------------

chenssy...

outerClass...

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

四、局部内部类

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

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

定义在方法里:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

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");

    }

}

定义在作用域内:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

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();

    }

}

五、匿名内部类

在做Swing编程中,我们经常使用这种方式来绑定事件


1

2

3

4

5

6

button2.addActionListener( 

                new ActionListener(){ 

                    public void actionPerformed(ActionEvent e) { 

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

                    

                });

我们咋一看可能觉得非常奇怪,因为这个内部类是没有名字的,在看如下这个例子:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

public class OuterClass {

    public InnerClass getInnerClass(final int num,String str2){

        return new InnerClass(){

            int number = num + 3;

            public int getNumber(){

                return number;

            }

        };        /* 注意:分号不能省 */

    }

    public static void main(String[] args) {

        OuterClass out = new OuterClass();

        InnerClass inner = out.getInnerClass(2, "chenssy");

        System.out.println(inner.getNumber());

    }

}

interface InnerClass {

    int getNumber();

}

----------------

Output:

5

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

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

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

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

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

PS:由于篇幅有限,对匿名内部类就介绍到这里,有关更多关于匿名内部类的知识,我就会在下篇博客(java提高篇—–详解匿名内部类)做详细的介绍,包括为何形参要定义成final,怎么对匿名内部类进行初始化等等,敬请期待……

六、静态内部类

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

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

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

 1 public class OuterClass {
 2     private String sex;
 3     public static String name = "chenssy";
 4
 5     /**
 6      *静态内部类
 7      */
 8     static class InnerClass1{
 9         /* 在静态内部类中可以存在静态成员 */
10         public static String _name1 = "chenssy_static";
11
12         public void display(){
13             /*
14              * 静态内部类只能访问外围类的静态成员变量和方法
15              * 不能访问外围类的非静态成员变量和方法
16              */
17             System.out.println("OutClass name :" + name);
18         }
19     }
20
21     /**
22      * 非静态内部类
23      */
24     class InnerClass2{
25         /* 非静态内部类中不能存在静态成员 */
26         public String _name2 = "chenssy_inner";
27         /* 非静态内部类中可以调用外围类的任何成员,不管是静态的还是非静态的 */
28         public void display(){
29             System.out.println("OuterClass name:" + name);
30         }
31     }
32
33     /**
34      * @desc 外围类方法
35      * @author chenssy
36      * @data 2013-10-25
37      * @return void
38      */
39     public void display(){
40         /* 外围类访问静态内部类:内部类. */
41         System.out.println(InnerClass1._name1);
42         /* 静态内部类 可以直接创建实例不需要依赖于外围类 */
43         new InnerClass1().display();
44
45         /* 非静态内部的创建需要依赖于外围类 */
46         OuterClass.InnerClass2 inner2 = new OuterClass().new InnerClass2();
47         /* 方位非静态内部类的成员需要使用非静态内部类的实例 */
48         System.out.println(inner2._name2);
49         inner2.display();
50     }
51
52     public static void main(String[] args) {
53         OuterClass outer = new OuterClass();
54         outer.display();
55     }
56 }

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

到这里内部类的介绍就基本结束了!对于内部类其实本人认识也只是皮毛,逼近菜鸟一枚,认知有限!我会利用这几天时间好好研究内部类!

时间: 2024-10-06 01:06:03

内部类初识的相关文章

16.初识内部类

内部类: 内部类就是在一个类的内部再定义一个类,比如,A类中定义了一个B类,那么B类相对于A类来说就成为内部类了,而A类相对于B类来说就是一个外部类了. 成员内部类:获得外部类的私有属性.私有方法... 1 package com.oop.demo11; 2 3 public class Outer { 4 5 private int id = 10; 6 7 public void out() { 8 System.out.println("这是外部类的方法"); 9 } 10 11

计算机程序的思维逻辑 (21) - 内部类的本质

内部类 之前我们所说的类都对应于一个独立的Java源文件,但一个类还可以放在另一个类的内部,称之为内部类,相对而言,包含它的类称之为外部类. 为什么要放到别的类内部呢?一般而言,内部类与包含它的外部类有比较密切的关系,而与其他类关系不大,定义在类内部,可以实现对外部完全隐藏,可以有更好的封装性,代码实现上也往往更为简洁. 不过,内部类只是Java编译器的概念,对于Java虚拟机而言,它是不知道内部类这回事的, 每个内部类最后都会被编译为一个独立的类,生成一个独立的字节码文件. 也就是说,每个内部

初识Python,望君多多关照

在学习Python之前,我们接触过数据结构和网页制作.前者让我们学习如何把C语言运用的更加整齐规范,而后者让我们亲身学习如何运用所学,制作一个静态网页.通过这些课程的学习,让我对C语言产生了比较大的压力,以至于对编程.对这学期的Python课程都有一种如临大敌的感觉. 但是真的学习了这门课程,体会了编码过程中的一些固定运用方法和套路之后,也许过程中对这门课程隐隐约约产生了一点点朦胧的感觉,仿佛他也并没有想象中的那么困难,起码现在的学习让我认为,他可能没有C语言那么繁琐和麻烦.当然,以一个初学者的

修饰符-包-内部类-代码块执行顺序

1.访问权限修饰符     从大到小的顺序为:public--protected--default--private     private--只能在同一类中使用;     default--不用写出来,默认不加.可以被同一包中的类使用     protected--可以被不同包的子类使用     public--可以被不同包的其它类使用 2.各种修饰符的修饰对象(可修饰哪些:类/接口/方法/属性)(多个修饰符连用是可以没有顺序的!)     1)访问权限修饰符:public/default--

初识数组排序!!!!

<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>初识数组排序</title> <!--调试成功--> <style type="text/css"> *{ padding:0; margin: 0; } li,ul{ list-style: none; } #p

初识操作系统和linux

初识操作系统和linux 1.计算机系统由硬件系统和软件系统两大部分组成:是一种能接收和存储信息,并按照存储在其内部的程序对海量数据进行自动.高速地处理,然后把处理结果输出的现代化智能电子设备. 2.世界上第一台计算机是1946年诞生在美国宾州大学. 3.冯·诺依曼体系结构:1946年数学家冯·诺依曼于提出计算机硬件系统由运算器.控制器.存储器.输入设备.输出设备.摩根定律:当价格不变时,集成电路上可容纳的元器件的数目,约每隔18-24个月便会增加一倍,性能也将提升一倍.现在计算机技术进本很难遵

内部类实现接口

帮人解答题目,顺便提升思维. 原题目: 于是有了下面的代码: interface Interf{//1.定义接口 abstract public void print(); } public class tt { public static void main(String[] args) { fun(); //6.返回的接口引用,有一个print方法,可以调用fun().print(); } public static Interf fun(){//2.写一个函数 class inner im

JAVA 初识类加载机制 第13节

JAVA 初识类加载机制 第13节 从这章开始,我们就进入虚拟机类加载机制的学习了.那么什么是类加载呢?当我们写完一个Java类的时候,并不是直接就可以运行的,它还要编译成.class文件,再由虚拟机解释给当前的操作系统去执行.这些过程都是我们看不见的,我们能看见的也就是一个.class文件.既然虚拟机要解释这些.class文件给当前的操作系统听,那么他怎么获得这些.class文件呢?虚拟机获得这些.class文件的过程就是类加载了. 所以,总结来说就是:虚拟机将.class文件从磁盘或者其他地

包、修饰符、内部类、匿名内部类(java基础知识十)

1.package关键字的概述及作用 * A:为什么要有包     * 将字节码(.class)进行分类存放  * B:包的概述     *   * C:包的作用     * 包名要定义在第一行,     * 包名.*这个包下所有的类都可见     * 具体的类比较好,能提高效率 2.包的定义及注意事项 * A:定义包的格式 * package 包名; * 多级包用.分开即可* B:定义包的注意事项 * A:package语句必须是程序的第一条可执行的代码 * B:package语句在一个jav