Java基础之内部类介绍

内部类

在一个Java源文件中,只能定义一个与类名完全一致的公开的类,这个类,我们称为外部类。在一个外部类中,我们可以在内部定义一个或者多个类,我们把在内部定义出来的类称为内部类。内部类的定义形式,和普通类的定义方式是一致的:

1 [访问权限控制符] [类型] [类名] {
2 
3 }  

①访问权限控制符:取值可以是private、无、protected、public。注意“无”表示不用任何控制符来修饰,也不能用default来修饰。

②类型:取值可以是class、enum、interface。但是实际运用中,往往不用interface。因为内部接口不允许被其他外部类所继承。

③类名:这个内部类的类名称,这个名称不能和外部类的类名称相同,同时也不能和该外部类中的其他内部类同名称。

内部类可以是静态或者是非静态,也可以出现在属性的定义、方法体和表达式中,或者是匿名出现。因此我们把内部类分为四种。

  • 静态内部类
  • 成员内部类
  • 局部内部类
  • 匿名内部类

静态内部类

使用static修饰的一个内部类,就叫作静态内部类。

 1 public class MyOuter {
 2 
 3     private static int a = 10;
 4     private int b = 20;
 5 
 6     private static void say () {
 7 
 8         System.out.println("hello world!");
 9 
10     }
11 
12     static class MyStaticInner {
13 
14         private void test1() {
15             System.out.println("a = " + a);
16             //System.out.println("b = " + b);
17             say();
18         }
19 
20         private static void test2() {
21 
22         }
23     }
24 }

1.在一个静态内部类中,可以有静态方法,也可以存在非静态方法。

2.在一个静态内部类中,只能够访问外部类的静态成员属性和静态成员方法。如上代码中在静态类的方法中访问 外部非静态的成员属性b,编译不通过。

3.如果要访问静态内部类的静态方法,不需要拿到外部类和内部类的实例,通过 外部类.内部类.内部静态方法的方式访问。如上代码中需要访问内部类的静态方法test2。可以表示为MyOuter.MyStaticInner.test2();

4.如果要方位静态内部类的非静态方法,不需要拿到外部类的实例,但是需要拿到内部类的实例,通过内部类的实例.非静态内部方法的方式来访问。如上代码中可以表示为MyOuter.MyStaticInner oi = new MyOuter.MyStaticInner();  oi.test1();

总结一下,静态内部类的实例获取方式:外部类.静态内部类   实例的名称 = new 外部类.静态内部类();我们再来看一个问题。实例代码如下所示:

public class MyOuter {

    private static int a = 10;
    private static int b = 20;
    private static int c = 30;

    private static void say () {

        System.out.println("hello world!");
    }

    public static class MyInner {

        private int b = 200;
        private int c = 300;

        public void test1() {
            int c = 3000;
            System.out.println("a = " + a);
            System.out.println("b = " + b);
            System.out.println("c = " + c);
        }

        public static void test2() {

        }
    }
    public static void main(String[] args) {
        MyOuter.MyInner m = new MyOuter.MyInner();
        m.test1();
    }
}

 当外部类和内部类拥有相同的成员属性,同时内部类的方法中也拥有相同的局部变量,内部类在访问时,优先访问的是内部局部变量,内部成员变量还是外部成员变量?我们来看一下运行结果

a = 10
b = 200
c = 3000 

于是我们得出结论:在一个静态内部类的内部方法,内部成员属性,外部成员属性中变量相同,优先访问局部变量,再访问内部成员变量,最后是外部静态成员变量。

成员内部类

成员内部类定义的位置和静态内部类有点相似,但是成员内部类没有static关键字修饰。

 1 public class MyOuter {
 2 
 3     private  int a = 10;
 4     private static int b = 20;
 5     private static int c = 30;
 6 
 7     private static void say () {
 8 
 9         System.out.println("hello world!");
10     }
11 
12     public class MyInner {
13 
14         private int b = 200;
15         private int c = 300;
16         //private static int param = 111; 
17         public void test1() {
18             int c = 3000;
19             System.out.println("a = " + a);
20             System.out.println("b = " + b);
21             System.out.println("c = " + c);
22         }
23 
24         //public static void test2() {
25         //成员内部类中不允许存在静态方法
26         //}
27     }
28     public static void main(String[] args) {
29         MyOuter myOuter = new MyOuter();
30         MyInner myInner = myOuter.new MyInner();
31 
32         myInner.test1();
33 
34     }
35 }

1.成员内部类中不允许存在静态成员属性,和静态成员方法。第16行和第24行编译不通过。

2.成员内部类是外部类的实例的一个成员,因此想要访问成员内部类,必须先要拥有外部类的实例。

3.成员内部类无论是声明为public或者private还是内部类成员属性或者方法声明为public和private。都可以通过外部类的实例,再产生内部类的实例,最后访问内部类的成员属性或者方法。

4.和静态内部类一样,如果在成员内部类方法中局部变量,内部成员属性,外部成员属性相同时,优先访问局部变量,内部类成员属性,外部成员属性。

局部内部类

局部内部类是存在方法体之中的,实例代码如下图所示:

 1 public class MyOuter {
 2 
 3     private  int a = 10;
 4     private  int b = 20;
 5     private  int c = 30;
 6     private  int d = 40;
 7 
 8     private static void say () {
 9 
10         System.out.println("hello world!");
11     }
12 
13 
14     public  void abc () {
15         final int b = 100;
16 
17         class MyInner {
18             private int c = 300;
19 
20             private void test () {
21                 int d = 4000;
22                 System.out.println("a = " + a);
23                 System.out.println("b = " + b);
24                 System.out.println("c = " + c);
25                 System.out.println("d = " + d);
26                 say();
27             }
28         }
29 
30 
31         MyInner inner = new MyInner();
32         inner.test();
33     }
34 
35     public static void main(String[] args) {
36         MyOuter myOuter = new MyOuter();
37         myOuter.abc();
38 
39     }
40 }

1.局部内部类只存在方法体中,也只能在该方法中通过获取这个类的实例类访问成员属性和方法。

2.局部内部类的使用必须要在局部内部类的声明之后。

3.局部内部类只能访问方法体中的final修饰的变量或者对象。也可以访问外部类的成员属性和方法。

4.和成员内部类一样,如果在局部内部类方法中局部变量,内部成员属性,外部成员属性相同时,优先访问局部变量,内部类成员属性,外部成员属性。

 

匿名内部类

 匿名内部类用的比较常见,比如在线程中,

1 public static void main(String[] args) {
2        (new Thread(){}).start();
3 
4 }

 匿名内部存在于方法体中,是一个唯一没有构造函数的内部类。

 

编译后的内部类

我们首先来看一段代码:

public class MyOuter {

    //成员内部类
    private class Inner1 {

    }


    //静态内部类
    private static class Inner2 {

    }

    public static void main(String[] args) {
        //匿名内部类
        (new Thread(){}).start();
        (new Thread(){}).start();

        //局部内部类
        class Inner3{}
        class Inner4{}

    }
}

我们将这一段代码保存为MyOuter.java并另存到D:\test\目录下。打开cmd;执行javac MyOuter.java。

由上图可知改文件的编码问题,因此需要更改文件的编码方式为ANSI。

 

继续执行javac MyOuter.java。在相同目录下出现编译好的class文件。如下图所示;

编译好的class文件,简单总结一下:

1.外部类命名为[外部类.class]

2.内部类的命名,采用外部类与内部类之间使用$分隔。

3.成员内部类的命名,采用[外部类]$[内部类].class命名。如上图MyOuter$Inner1.class。

4.静态内部类的命名和成员内部类一样。如上图MyOuter$Inner2.class。

5.局部内部类的命名,采用[外部类]$[编号][内部类].class命名。这里的编号是为了是为了区分是哪个方法。这也说明了,为什么同一个外部类,的多个方法中,存在的局部内部类的类名可以相同。如上图的MyOuter$1Inner3.class和MyOuter$1Inner4.class

6.匿名内部类的命名,采用[外部类]$[编号].class命名。这里的编号是为了区分多个匿名类。如上图的MyOuter$1.class和MyOuter$2.class

 

 

为什么要使用内部类

1.使用内部类,能够使代码程序变得更简单。比如有一段业务逻辑可能有且只有一处会使用,那可以考虑在业务类上增加以下内部类。

2.普通类只能继承一个超类,当我们想要做到多继承时,可以考虑使用成员内部类来继承某一个原有的类,使用原有来非私有成员属性和方法。这就到达了多继承的效果。

3.使得代码更加简单,使用匿名内部类,能够使用少量的代码去做更多的事情,比如Thead。当然这里只是说明匿名内部类的好处,线程的使用推荐线程池。

 

posted @ 2018-10-14 15:13  冰糖小城  阅读(182)  评论(0编辑  收藏  举报