科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道内部类学习(二)

内部类学习(二)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

内部类是否真的有用,有没有存在的必要?我们首先来看看内部的工作原理。

作者:CMTobby 来源:CSDN 2008年3月19日

关键字: 学习 内部类 java

  • 评论
  • 分享微博
  • 分享邮件

许多人认为内部类的语法十分复杂,尤其是匿名内部类,这与Java所一直奉行的“简单”原则相背离的,有人甚至怀疑java中加入这么一个“特征”(feature),是不是已经开始走向“灭亡”?就像许多其它语言一样走向“灭亡”?内部类是否真的有用,有没有存在的必要?我们首先来看看内部的工作原理。

先指明一点,内部类如何工作是由编译器来负责的,与java虚拟机无关,它对这个是一无所知的。仔细留意一下上篇中编译后产生的class文件,你会发现有一个class文件的名字是Court$TimerPrinter,它的基本格式是:外部类名称$内部类名称。当碰到内部类时,编译器会自动根据内部类的代码生成一个class文件并按照上述规则命名,那么编译器到底对它做了什么呢?我们可以使用Java的反射(reflection)机制来“偷窥”它,嘿嘿。具体的代码如下所示:

* Created on 2006-09-24

 *

 * TODO To change the template for this generated file go to

 * Window - Preferences - Java - Code Style - Code Templates

 */

package cn.edu.hust.cm.access;

 

import java.lang.reflect.*;

import javax.swing.*;

/**

 * @author Demon

 *

 * TODO To change the template for this generated type comment go to

 * Window - Preferences - Java - Code Style - Code Templates

 */

public class ReflectionTest {

 

       public static void main(String[] args) {

              String name="";

              if(args.length>0)

                     name=args[0];

              else

                     name=JOptionPane.showInputDialog("Class name (e.g. java.util.Date): ");

              try{

                     Class c1=Class.forName(name);

                     Class c2=c1.getSuperclass();

                     System.out.print("class " + name);

                     if(c2!=null&&c2!=Object.class)

                            System.out.print(" extends " + c2.getName());

                     System.out.print("\n{\n");

                     printConstructors(c1);

                     System.out.println();

                     printMethods(c1);

                     System.out.println();

                     printFields(c1);

                     System.out.println("}");

            }

              catch(ClassNotFoundException e) { e.printStackTrace(); }

              System.exit(0);

                                              }

       public static void printConstructors(Class c1){

              Constructor[] constructors=c1.getDeclaredConstructors();

              for(int i=0;i<constructors.length;i++){

                     Constructor c=constructors[i];

                     String name =c.getName();

                     System.out.print(Modifier.toString(c.getModifiers()));

                     System.out.print("   " + name + "(");

                    

                     Class[] paramTypes = c.getParameterTypes();

                     for(int j=0;j<paramTypes.length;j++){

                            if (j > 0) System.out.print(", ");

                            System.out.print(paramTypes[j].getName());

                                                 }

                     System.out.println(");");

 

}

                                                        }

       public static void printMethods(Class c1){

              Method[] methods=c1.getDeclaredMethods();

              for(int i=0;i<methods.length;i++){

                     Method m=methods[i];

                     String name=m.getName();

                     Class type=m.getReturnType();

                     System.out.print(Modifier.toString(m.getModifiers())+"  "+type.getName()+"  "+name+"(");

                     Class[] paramTypes=m.getParameterTypes();

                     for(int j=0;j<paramTypes.length;j++){

                            if(j>0) System.out.print(",");

                            System.out.print(paramTypes[j].getName());

                                                            }

                     System.out.println(");");

                                                    }

                                                   }

      

       public static void printFields(Class c1){

              Field fields[]=c1.getDeclaredFields();

              for(int i=0;i<fields.length;i++){

                     System.out.print(Modifier.toString(fields[i].getModifiers()));

                     System.out.print("  ");

                     Class type=fields[i].getType();

                     System.out.print(type.getName());

                     System.out.println("  "+fields[i].getName()+";");

                                                 }

                                                  }

       }

运行该程序,在对话框中输入cn.edu.hust.cm.test.Court$TimerPrinter,将会得到如下输出:

}

  cn.edu.hust.cm.test.Court$TimerPrinter(cn.edu.hust.cm.test.Court);

public  void  actionPerformed(java.awt.event.ActionEvent);

final  cn.edu.hust.cm.test.Court  this$0;

}

如上所示,编译器自动为我们加上了一个域this$0,它指向一个外部类,另外自动给构造方法增加了一个Court型别参数,用来设置this$0的值,注意this$0是编译器自己合成的,不能直接引用。

既然编译器能够自动进行转化,为什么我们不直接自己进行转换,把TimerPrinter改写成普通的class呢?如下所示:

class Court
{
   . . .
 
   public void start()
   {
      ActionListener listener = new TimePrinter(this);
      Timer t = new Timer(interval, listener);
      t.start();
   }
}
 
class TimePrinter implements ActionListener
{
   public TimePrinter(TalkingClock clock)
   {
       outer = clock;
   }
   . . .
   private TalkingClock outer;
 }

问题来了,我们在实现actionPerformed方法的时候要用到访问outer.beep,但是beepprivate类型的,在TimerPrinter中是不能直接访问的。这样内部类的一个优点就显示出来了:内部类能够访问其所属外部类中的私有域而其它普通的类则不行

那么内部类是通过什么样的机制访问它所属的外部类中的私有数据的呢?联想前面讲私有域的时候,我们都是通过方法来间接访问私有域的,那么这里是不是这样的呢?我们还是对外部类Court进行一下反射,结果如下所示:

class cn.edu.hust.cm.test.Court

{

public   cn.edu.hust.cm.test.Court(int, boolean);

static  boolean  access$0(cn.edu.hust.cm.test.Court);

public  void  start();

private  int  interval;

private  boolean  beep;

}

我们看到新增了一个方法access$0,它的返回值就是传递过来的Court对象的beep域,这样actionPerformed方法中的if(beep)就相当于if(access$0(outer)),内部类就是通过这种机制来访问外部类的私有数据。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章