java中内部类的浅析 总结

内部类的位置

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class A {  
  2.     class B {  
  3.   
  4.     }  
  5.   
  6.     public void pint() {  
  7.         class C {  
  8.         }  
  9.         new C();  
  10.     }  
  11.   
  12.     public void pint(boolean b) {  
  13.         if (b) {  
  14.             class D {  
  15.             }  
  16.             new D();  
  17.         }  
  18.     }  
  19. }  
从代码中可以看出,内部类可以定义到很多地方,常用的是成员变量中 (B) ,方法中也叫局部内部类 (C) ,作用域中 (D)

从上面来看似乎没有用到过在方法中和作用域中的情况啊,这就错了;再来看看这个:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public interface AInterface {  
  2.     void show();  
  3. }  
  4. public class B {  
  5.   
  6.     public void show() {  
  7.         class Man implements AInterface {  
  8.             @Override  
  9.             public void show() {  
  10.   
  11.             }  
  12.   
  13.         }  
  14.         Man man = new Man();  
  15.         man.show();  
  16.     }  
  17.   
  18. }  
其中我们定义了两个文件,一个文件是一个接口类,一个是 B 文件;在 B 类中,的  show() 方法中我们使用了局部内部类的方式创建了类  Man  , Man class 继承接口并实现方法,随后使用该类。

内部类的权限

为什么要有内部类的存在?

在我看来类主要的就是封装、继承、多态;当然其中的回调思想我认为是很重要的;而内部类的出现就是为了简化多重继承的问题;一个A类,并不能继承多个其他类,但是在使用中又需要使用到其他类的方法,这个时候内部类就发挥作用了;典型的就是事件点击的回调实现。

那么内部类的权限究竟有多大?

至于答案是什么,代码上看看就知道了。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class C {  
  2.     int a = 1;  
  3.     private int b = 2;  
  4.     protected int c = 3;  
  5.     public int d = 4;  
  6.   
  7.     void a() {  
  8.         System.out.println("A:" + a);  
  9.     }  
  10.   
  11.     private void b() {  
  12.         System.out.println("B:" + b);  
  13.     }  
  14.   
  15.     protected void c() {  
  16.         System.out.println("C:" + c);  
  17.     }  
  18.   
  19.     public void d() {  
  20.         System.out.println("D:" + d);  
  21.     }  
  22.   
  23.     class D {  
  24.   
  25.         void show() {  
  26.             int max = a + b + c + d;  
  27.             a();  
  28.             b();  
  29.             c();  
  30.             d();  
  31.             System.out.println("Max:" + max);  
  32.         }  
  33.     }  
  34.   
  35.     public static void main(String[] args) {  
  36.         D d = new C().new D();  
  37.         d.show();  
  38.     }  
  39. }  
运行结果:


可以看出,内部类 D 对类 C 具有完整的访问权限,等于全身脱光了给你看。

那要是反过来呢?

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class C {  
  2.     class D {  
  3.         private int a = 20;  
  4.         private void a(){  
  5.             System.out.println("D.A:" + a);  
  6.         }  
  7.     }  
  8.       
  9.     void show(){  
  10.         D d = new D();  
  11.         d.a();  
  12.           
  13.         System.out.println("D.A:" + d.a);  
  14.     }  
  15.   
  16.     public static void main(String[] args) {  
  17.         new C().show();  
  18.     }  
  19. }  
运行结果:


可见也是完全可行的,也能直接访问私有属性 私有方法,在这里似乎私有的限制已经失效了一般,这个让我想起了以前看见过一个面试:在 Java 中 private 修饰何时会失效。

这完全是两个人互相脱光光了啊~

匿名内部类

这个非常常见,特别是在按钮点击事件绑定中。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class D {  
  2.     void initButton() {  
  3.         Button b1 = new Button();  
  4.         b1.setOnClickListener(new OnClickListener() {  
  5.   
  6.             @Override  
  7.             public void onClick(Button v) {  
  8.   
  9.             }  
  10.         });  
  11.   
  12.         Button b2 = new Button();  
  13.         b2.setOnClickListener(new OnClickListener() {  
  14.   
  15.             @Override  
  16.             public void onClick(Button v) {  
  17.   
  18.             }  
  19.         });  
  20.     }  
  21.   
  22. }  
其中的:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1.   new OnClickListener() {  
  2.   
  3.     @Override  
  4.     public void onClick(Button v) {  
  5.   
  6.     }  
  7. }  
就是匿名内部类的使用方式,OnClickListener 是一个接口类,接口类是无法直接new 一个实例的;这里也并不是那样,而是new 了一个其他的类,该类是匿名的,也就是没有名字,只不过该类实现了 OnClickListener接口类中的方法。

上面的添加回调部分可等同于:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class D {  
  2.     void initButton1() {  
  3.         Button b1 = new Button();  
  4.         b1.setOnClickListener(new Listener1());  
  5.   
  6.         Button b2 = new Button();  
  7.         b2.setOnClickListener(new Listener2());  
  8.     }  
  9.   
  10.     class Listener1 implements OnClickListener {  
  11.   
  12.         @Override  
  13.         public void onClick(Button v) {  
  14.   
  15.         }  
  16.     }  
  17.   
  18.     class Listener2 implements OnClickListener {  
  19.   
  20.         @Override  
  21.         public void onClick(Button v) {  
  22.   
  23.         }  
  24.     }  
  25.   
  26. }  
这里就是先建立类,继承自接口;而后赋值到  Button  中。

要说两者的区别与好处,这个其实看具体的使用情况吧;如果你的按钮很多,但是为了避免建立太多类;那么可以建立一个回调类,然后都赋值给所有的按钮,不过最后就是需要在 onClick方法中进行判断是那个按钮进行的点击。

匿名内部类的使用地方很多;具体的使用应视使用情况而定~

静态内部类/静态嵌套类

这个其实并不应该叫做内部类了,因为其并不具备内部类的完全权限,在使用上与一般的类基本一样;那为什么会有这个的存在?

在我看来这个类的存在是为其包括类服务;意思是可以单独服务,不被外面的类所知晓;如这样:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class E {  
  2.     private void show(){  
  3.         new A();  
  4.     }  
  5.       
  6.     private static class A{  
  7.           
  8.     }  
  9. }  
其中类  A  使用了  static  ,所以是 静态嵌套类 ,在这里使用 private  修饰;那么该类只能在  E  类中进行实例化;无法在 其他文件中实例化。

这样的情况使用外面的类能行么?不行吧?也许你会说在 E.java 文件夹中建立 A.java ,并使用protected修饰;但是在同样的包下,或者继承的类中同样能访问了;这也只是其中一个较为特殊的情况。

我们来看看权限

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class E {  
  2.     int a1 = 0;  
  3.     private int a2 = 0;  
  4.     protected int a3 = 0;  
  5.     public int a4 = 0;  
  6.       
  7.     private void show(){  
  8.         A a =new A();  
  9.         System.out.print("b1:"+a.b1);  
  10.         System.out.print("b2:"+a.b2);  
  11.         System.out.print("b3:"+a.b3);  
  12.         System.out.print("b4:"+a.b4);  
  13.           
  14.     }  
  15.       
  16.     private static class A{  
  17.         int b1 = 0;  
  18.         private int b2 = 0;  
  19.         protected int b3 = 0;  
  20.         public int b4 = 0;  
  21.           
  22.         private void print(){  
  23.             System.out.print("a1:"+a1);  
  24.             System.out.print("a2:"+a2);  
  25.             System.out.print("a3:"+a3);  
  26.             System.out.print("a4:"+a4);  
  27.   
  28.         }  
  29.     }  
  30. }  
在这个中的结果是怎样?


从图片中可以看出,其权限级别是单方向的;静态嵌套类 A 对其包含类 E 完全透明;但 E 并不对 A 透明。

再来看看方法:


可以看出同样的情况;这个是为什么呢?为什么就是多一个 static 的修饰就这么完全不同?其是很好理解,两个独立的类;本来就无法直接使用,必须有引用才能调用其属性与方法。

我们或许可以这么调整一下就OK

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class E {  
  2.     int a1 = 0;  
  3.     private int a2 = 0;  
  4.     protected int a3 = 0;  
  5.     public int a4 = 0;  
  6.   
  7.     private void show() {  
  8.         A a = new A();  
  9.         System.out.print("b1:" + a.b1);  
  10.         System.out.print("b2:" + a.b2);  
  11.         System.out.print("b3:" + a.b3);  
  12.         System.out.print("b4:" + a.b4);  
  13.   
  14.         a.b1();  
  15.         a.b2();  
  16.         a.b3();  
  17.         a.b4();  
  18.     }  
  19.   
  20.     void a1() {  
  21.   
  22.     }  
  23.   
  24.     private void a2() {  
  25.   
  26.     }  
  27.   
  28.     protected void a3() {  
  29.   
  30.     }  
  31.   
  32.     public void a4() {  
  33.   
  34.     }  
  35.   
  36.     private static class A {  
  37.         int b1 = 0;  
  38.         private int b2 = 0;  
  39.         protected int b3 = 0;  
  40.         public int b4 = 0;  
  41.   
  42.         void b1() {  
  43.   
  44.         }  
  45.   
  46.         private void b2() {  
  47.   
  48.         }  
  49.   
  50.         protected void b3() {  
  51.   
  52.         }  
  53.   
  54.         public void b4() {  
  55.   
  56.         }  
  57.   
  58.         private void print(E e) {  
  59.             System.out.print("a1:" + e.a1);  
  60.             System.out.print("a2:" + e.a2);  
  61.             System.out.print("a3:" + e.a3);  
  62.             System.out.print("a4:" + e.a4);  
  63.   
  64.             e.a1();  
  65.             e.a2();  
  66.             e.a3();  
  67.             e.a4();  
  68.         }  
  69.     }  
  70. }  
在其静态类中传递一个  E  的引用进去就能解决问题了:


可以看出其中现在并没有报错了;能正常运行。

两者之间的隐藏区别

但是最开始上面的内部类是怎么回事?难道是闹鬼了?上面的内部类没有传递引用的啊;为啥加上一个 static 就不行了?

在这里我们需要看看字节码,我们先建立一个简单的内部类:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class F {  
  2.       
  3.     class A{  
  4.           
  5.     }  
  6. }  
这个够简单吧?别说这个都难了;汗~

然后我们找到 class 文件,然后查看字节码:

在这里分别查看了 F 类的字节码和 F$A 类的字节码。

其中有这样的一句: final F this$0; 这句是很重要的一句,这句出现的地方在其内部类中,意思是当你 new 一个内部类的时候就同时传递了当前类进去;所以在内部类中能具有当前类的完全权限,能直接使用所有的东西;就是因为在隐藏情况下已经传递了当前类进去。

那么我们再看看一个简单的静态内部类:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class G {  
  2.     static class A {  
  3.   
  4.     }  
  5. }  
与上面的区别唯一就是在于添加了一个  static  。此时我们看看字节码:



可以看出其中无论是 G 类,还是 G$A 类的初始化中都没有其他多余的部分,也没有进行隐藏的传递进去当前类;所以这样的情况下并不具备访问权限,需要我们传递引用进去,可以通过接口也可以完全传递进去,具体取决于个人。所以加了static类的内部类除了在权限上比一般的类更加开放(与其包含类)外,与一般的类在使用上是一样的;所以准确的说应该叫做静态嵌套类。

初始化的区别

一个类中,同时包含了内部类与静态内部类,那么其初始化应该是怎么样的呢?

都是直接 new ?还是看看代码:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class H {  
  2.     int a = 1;  
  3.   
  4.     public class A {  
  5.         public void Show() {  
  6.             System.out.print("a:" + a);  
  7.         }  
  8.     }  
  9.   
  10.     public static class B {  
  11.         public void Show(H h) {  
  12.             System.out.print("a:" + h.a);  
  13.         }  
  14.     }  
  15.   
  16.     public static void main(String[] args) {  
  17.         H h = new H();  
  18.         //A a = new A();  
  19.         A a1 = h.new A();  
  20.         B b = new B();  
  21.         //B b1 = h.new B();  
  22.         B b3 = new H.B();  
  23.     }  
  24. }  
其中注释了的两种方式是不允许的方式,也就是无法正常运行。

A 因为有一个隐藏的引用,所以必须是H 的实例才能进行初始化出A 类;而B 类则是因为是在H 类中以静态方式存在的类,所以需要 new H.B();之所以能直接使用new B(),与该 main 方法在 H 类中有关,因为本来就在 H类中,所以直接使用 H类的静态属性或者方法可以不加上:“H.”  在前面。

内部类的继承

直接继承的情况:


可以看出报错了,为什么?因为需要传递一个 H 类进去,所以我们在继承的时候需要显示的指明:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class I extends H.A{  
  2.     public I(H h){  
  3.         h.super();  
  4.     }  
  5. }  
也就是在构造方法中,传递一个 H 的引用进去,并调用 H 实例的 super() 方法,才能进行实例化。
使用的话应该这样:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public static void main(String[] args) {  
  2.        H h = new H();  
  3.        I i = new I(h);  
  4.    }  

而,如果是继承其静态嵌套类,则不需要这样:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class J extends H.B{  
  2.   
  3. }  

就这样就OK。

总结的很好,和大家分享一下,也顺便收藏了哈哈

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值