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。当然这里只是说明匿名内部类的好处,线程的使用推荐线程池。