`
anysky131
  • 浏览: 177180 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

java内部类

    博客分类:
  • Java
阅读更多
内部类学习

所谓内部类(Inner Class),顾名思义,就是指定义在另外一个类中的类,我们为什么要这么做呢?为什么不直接定义它而要在别的类中定义一个内部类呢?这样做主要有如下三个原因:
1.  内部类的方法可以访问它所在的外部类中的所有域,包括私有型别的;
2.  对于同一个包中的其它类它是隐藏的;
3.  匿名的内部类可以让我们很方便的定义事件响应(call back),这在GUI编程中很常见;
一.内部类(inner class)如何访问外部类(outer class)中的域
因为安全机制的原因,内部类通常声明为private类别,因此,只有在内部类所在的外部中才能够创建内部类的对象,对其它类而言它是隐藏的。另外,只有内部类才会用到private修饰符,一般的类如果用private修饰符则会报错。
下面看如下的代码:

package cn.edu.hust.cm.test;
 
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.Toolkit;
import javax.swing.JOptionPane;
import javax.swing.Timer;
public class InnerClassTest {
	public InnerClassTest() {
		super();
		// TODO Auto-generated constructor stub
	}
	public static void main(String[] args) {
        Court court=new Court(10000,true);
        court.start();
        JOptionPane.showMessageDialog(null,"停止么,CMTobby?");
        System.exit(0);
	}
}
 
class Court{
	public Court(int interval,boolean beep){
		this.interval=interval;
		this.beep=beep;
	}
	public void start(){
		TimerPrinter action=new TimerPrinter();
		Timer t=new Timer(interval,action);
		t.start();
	}
	private int interval;
	private boolean beep;
 
	private class TimerPrinter implements ActionListener{
		public void actionPerformed(ActionEvent e){
			System.out.println("Cindyelf,would you be my mm?");
			if(beep) Toolkit.getDefaultToolkit().beep();
		}
	}
}



注意上面红色加粗部分的代码,如你所见beep这个变量在内部类TimerPrinter中我们并没有声明,那么它引用自何处呢?显然是来自于外部类。一般来说,一个方法可直接refer to调用它的对象中的所有域,而一个内部类的方法则可以直接refer to它所在类以及创建它的外部类中的所有域,如上例所示。
事实上,在每一个内部类中都存在一个默认的隐式的reference,它指向创建了这个内部类的实例的那个对象,我们假设它叫outer,这样上面红色加粗部分就相当于:
if(outer.beep) Toolkit.getDefaultToolkit().beep();。Ok,既然存在这样一个reference,那么outer的值又是如何设置的?实际上编译器会合成一个构造方法来设置它,如下面代码所示:
public TimePrinter(Court court) {
          outer = clock;
}
    这段代码是编译时自动产生的,就像前面我们讨论的自动拆箱装箱中一样,编译器自
己会添加一些代码。然后当我们在Court类的start()方法中创建TimerPrinter实例的时
候,编译器会自动把this作为参数传递过去,效果如下面代码所示:
  
 public void start(){
		TimerPrinter action=new TimerPrinter(this);//编译器自动加上的
		Timer t=new Timer (interval,action);
		t.start ();
	}


    参数是编译器自动给加上的,不用我们来管。
二.内部类的一些特殊语法规则
    前面我们说在每一个内部类中都存在一个默认的隐式的reference,它指向创建了这个内
部类的实例的那个对象,并且我们以outer来带指它,如果我们想显式的指明该按照如下的
语法,OuterClass.this,例如if(Court.this.beep) Toolkit.getDefaultToolkit().beep();。
    此外,因为我们定义的内部类通常是private,所以它通常是通过外部类的方法创建,如
本例中的start()方法,这样那个隐式的reference就指向调用了start()方法的对象,就是this。
如果内部类声明为public,那么我们就可以在任何地方实例化一个内部类,如下面代码:
    Court court=new Court(10000,true);
    Court.TimerPrinter test=court.new TimerPrinter();
    上述代码在InnerClassTest的main方法中,这时候那个隐式的reference就直接指向了
court对象。注意语法是:OuterClassName.InnerClassName

许多人认为内部类的语法十分复杂,尤其是匿名内部类,这与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,但是beep是private类型的,在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)),内部类就是通过这种机制来访问外部类的私有数据。

这里我们介绍一种特殊的内部类:局部内部类(Local Inner Classes)。
在前面的例子中,我们可以发现TimerPrinter仅在start方法中创建一个新的对象时出现过一次,其它地方都没再用到过,这个情况下我们可以把TimerPrinter就定义在start方法中,如下面代码所示:
public void start(){
		class TimerPrinter implements ActionListener{
			//private boolean beep;
			public void actionPerformed(ActionEvent e){
				System.out.println("Bigface,would you be my boy?");
				if(beep) Toolkit.getDefaultToolkit().beep();
			}
		}
		TimerPrinter action=new TimerPrinter();
		Timer t=new Timer(interval,action);
		t.start(); 
	}



注意,局部内部类不需要任何access modifier,否则编译出错,它的作用域一般都被限制在它所在的block中。此时,编译会产生一个名字叫Court$1TimerPrinter的class文件。局部内部类有如下两个优点:
1.  它对外面的所有类来说都是隐藏的,即时是它所属的外部类,仅有它所在的方法知道它;
2.  它不仅可以访问它所属外部类中的数据,还可以访问局部变量,不过局部变量必须生命为final类型,看下面的例子:

package cn.edu.hust.cm.test;
 
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.Toolkit;
 
import javax.swing.JOptionPane;
import javax.swing.Timer;
public class InnerClassTest {
	public InnerClassTest() {
		super();
	}
	public static void main(String[] args) {
        Court court=new Court();
        court.start(5000,true);
        JOptionPane.showMessageDialog(null,"停止么,CMTobby?");
	}
}
 
class Court{
	public void start(int interval,final boolean beep){
		class TimerPrinter implements ActionListener{
			public void actionPerformed(ActionEvent e){
				System.out.println("Cindyelf,are you crazy?");
				if(beep) Toolkit.getDefaultToolkit().beep();
			}
		}
		TimerPrinter action=new TimerPrinter();//编译器会自动更改
		Timer t=new Timer(interval,action);
		t.start(); 
	}
}



注意,局部内部类所要访问的局部变量必须声明为final类型,如上例中红色粗体部分。那么局部内部类是如何访问这个局部变量的呢?我们再对Court$1TimerPrinter反射一下,会得到如下的输出:
class cn.edu.hust.cm.test.Court$1TimerPrinter
{
cn.edu.hust.cm.test.Court$1TimerPrinter(cn.edu.hust.cm.test.Court, boolean);
public  void  actionPerformed(java.awt.event.ActionEvent);
final  cn.edu.hust.cm.test.Court  this$0;
private final  boolean  val$beep;
}



编译器又给我们增加了一个域val$beep,同时构造方法增加了一个boolean类型的参数,因此我们猜想如下的实现过程:
TimerPrinter action=new TimerPrinter(this,beep);//编译器自动增加
然后构造方法中会有:val$beep=beep;这样就成功的把局部变量的值复制过来了。局部变量必须为final就是为了保证成功的复制值,因为final类型的变量一经赋值就不能再发生变化了。
这里再介绍一种特殊的内部类――匿名内部类(Anonymous Inner Class),顾名思义,就是没有名字的内部类,这是Java为了方便我们编写程序而设计的一个机制。因为有时候有的内部类只需要创建一个它的对象就可以了,以后再不会用到这个类,这时候使用匿名内部类就比较合适,而且也免去了给它取名字的烦恼,:)。
匿名类的语法是什么样的呢?如下所示:
new SuperType(){
    内部类的方法和域;
}
注意,这里的SuperType指超类,它可以是一个接口(interface)或者是一个类(Class),当它是一个接口的时候就不能有构造参数(Construction parameters)了。匿名类的语法相对于Java的其他部分来说稍微复杂一点,我们可以按如下方式理解:
1.SuperType为接口
例子:Interface1() test=new Interface1(){
          要实现的的方法;
          Interface1的域;
          内部类的域以及方法;
}
我们可以如下理解:
先声明了如下一个类,
      class Anonymous1 implements Interface1{
          要实现的的方法;
          Interface1的域;
          内部类的域以及方法;
}
然后,Interface1() test=new Anonymouse1();
这样就比较容易理解了。
2.SuperType为类
例子:Class2 test=new Class2(Construction parameters){
          内部类的域以及方法;
}
我们可以如下理解:
先声明了如下一个类,
      class Anonymous2 extends Class2{
          public Anonymous2(Construction parameters){
    super(Construction parameters);
}
内部类的域以及方法;
}
然后,Class2 test=new Anonymouse2(Construction parameters);
本节测试代码如下:




package cn.edu.hust.cm.test;
 
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.Toolkit;
 
import javax.swing.JOptionPane;
import javax.swing.Timer;
public class InnerClassTest {
	public InnerClassTest() {
		super();
	}
	public static void main(String[] args) {
        Court court=new Court(10000,true);
        court.start();
        JOptionPane.showMessageDialog(null,"停止么,CMTobby?");
        System.exit(0);
   	}
 
}
 
class Court{
	
	public Court(int interval,boolean beep){
		this.interval=interval;
		this.beep=beep;
	}
	
	public void start(){
		ActionListener action=new ActionListener(){
			public void actionPerformed(ActionEvent e){
				System.out.println("Cindyelf,wouly you be my girl?");
			}
		};
		Timer t=new Timer(interval,action);
		t.start();
		
	}
	private int interval;
	private boolean beep;
}
 


这里介绍最后一种比较特殊的内部类――静态内部类(Static Inner Class),即在内部类的前面增加了static修饰符(modifier)。注意,仅仅只有内部类能够被声明为static类型,通常我们声明一个普通类的时候不能使用static,否则编译出错。
那么为什么我们要使用静态的内部类呢?在什么情况下我们需要使用静态的内部类呢?我们前面讲过,编译器会自动给内部类加上一个reference,指向产生它的那个外部类的对象,如果不想要或者说不需要这个reference,那么我们就可以把这个内部类声明为static,禁止这个reference的产生。除此之外静态类的使用与普通的静态类是一样的。例子如下:

package cn.edu.hust.cm.test;
public class StaticInnerClassTest {
	public StaticInnerClassTest() {
		super();
		// TODO Auto-generated constructor stub
	}
 
	public static void main(String[] args) {
		// TODO Auto-generated method stub
        double d[]=new double[20];
        for(int i=0;i<d.length;i++)
        	d[i]=100*Math.random();
        //FindMinMax test=new FindMinMax();
        FindMinMax.Pair pair=FindMinMax.getMinMax(d);
        System.out.println("最小值是:"+pair.getFirst());
        System.out.println("最大值是:"+pair.getSecond());
	}
 
}
 
class FindMinMax{
	static double min=Double.MAX_VALUE;
	static double max=Double.MIN_VALUE;
	
	public static Pair getMinMax(double d[]){
		for(double value:d){
			if(min>value) min=value;
			if(max<value) max=value;
		}
		return new Pair(min,max);
	}
	
	public static class Pair{
		public Pair(double first,double second){
			this.first=first;
			this.second=second;
		}
		
		public double getFirst(){
			return this.first;
		}
		
		public double getSecond(){
			return this.second;
		}
		private double first;
		private double second;
	}
}



在这个例子中之所以要用静态内部类,主要是因为getMinMax这个方法是静态的,由类直接调用。而前面说过创建内部类的时候语法是这样的:
OuterClassObject.new InnerClassName(),如果省略了OuterClassObject则是
this. new InnerClassName(),OuterClassObject或者this指代创建这个内部类对象的一个外部类对象,是一个隐式参数,它将传入内部类的构造方法(见前所述)。但是现在这个内部类对象是由“类”直接创建的,不会产生这样的一个隐式参数传入内部类构造方法,因此内部类也就不需要“编译器自动给内部类加上一个reference,指向产生它的那个外部类的对象”,所以我们把这个内部类声明为static。上面的代码中如果我们去掉static将会报错,除非我们把getMinMax的static去掉,同时通过FindMinMax的一个实例来调用这个方法,如下:
FindMinMax test=new FindMinMax();
test.getMinMax(d);
我们下面来对内部类所产生的class文件反射(reflection)一下,结果如下:

class cn.edu.hust.cm.test.FindMinMax$Pair
{
public   cn.edu.hust.cm.test.FindMinMax$Pair(double, double);
public  double  getSecond();
public  double  getFirst();
private  double  first;
private  double  second;
}


很清楚的看到,编译器没有给我们自动加上一个reference,指向产生它的那个外部类的对象,也没有给构造方法加上一个FindMinMax型别的参数。


分享到:
评论
2 楼 muyiou 2012-03-26  
这篇文章讲的真好!
1 楼 guji528 2009-03-17  

讲解很详细

相关推荐

    java内部类详解

    Java 内部类详解 Java 内部类是一种高级特性,允许在一个类的定义内部创建另一个类。这种设计模式提供了更灵活的代码组织方式,同时还可以实现特定的封装和访问控制。内部类主要分为四种类型:静态内部类、成员内部...

    Java内部类总结

    ### Java内部类总结 在Java编程语言中,内部类是一个重要的概念,它允许开发者在一个类的内部定义另一个类。这种特性极大地增强了代码的封装性和复用性,同时也为解决特定问题提供了灵活的方法。本文将围绕Java内部...

    java内部类的讲解

    ### Java内部类详解 #### 一、内部类的分类与概念 Java的内部类机制是其强大特性之一,它允许类作为另一个类的成员存在,从而增强了代码的封装性和复用性。根据定义和作用域的不同,Java内部类主要分为四类: 1. ...

    Java 内部类

    Java 内部类是Java语言特性中的一个重要组成部分,它允许我们在一个类的内部定义另一个类。内部类可以访问外部类的所有成员,包括私有成员,这使得内部类在实现复杂逻辑和封装上具有很大的灵活性。下面我们将详细...

    浅谈Java内部类的四个应用场景

    ### Java内部类的应用场景 #### 场景一:当某个类除了它的外部类,不再被其他的类使用时 内部类的使用场景之一是当某个类仅需被其外部类使用时。这种情况下,将此类定义为内部类可以提高代码的封装性和可维护性。 ...

    java内部类使用例子

    Java内部类是Java语言提供的一种独特特性,它允许我们在一个类的定义内部定义另一个类。这种内部类可以是成员内部类、局部内部类、匿名内部类或静态内部类,每种都有其特定的用途和使用场景。在这个"java内部类使用...

    12.java内部类.zip

    12.java内部类.zip12.java内部类.zip12.java内部类.zip12.java内部类.zip12.java内部类.zip12.java内部类.zip12.java内部类.zip12.java内部类.zip12.java内部类.zip12.java内部类.zip12.java内部类.zip12.java内部类...

    java 内部类应用

    Java内部类是Java语言的一个特色特性,它允许我们在一个类的内部定义另一个类。这种设计模式在处理一些特定情况时非常有用,例如实现匿名回调、封装特定逻辑或创建与外部类有紧密关系的类。本篇文章将深入探讨Java...

    JAVA 内部类 PPT

    Java内部类是Java语言的一个独特特性,它允许我们在一个类的内部定义另一个类。这种设计提供了更高级别的封装和组织代码的方式。以下是关于内部类及其相关知识点的详细说明: 1. **内部类基本语法** - **实例内部...

    JAVA内部类总结

    ### JAVA内部类总结 在Java编程语言中,内部类(Inner Classes)是一种非常重要的特性,它允许我们在一个类的内部定义另一个类。这种结构不仅能够提高代码的组织性,还能帮助我们更好地处理类与类之间的关系。根据...

    java 内部类使用(内部匿名类)

    Java内部类是Java语言提供的一种独特机制,它允许在一个类的内部定义另一个类。这种设计模式使得代码结构更紧凑,可以更好地封装和隐藏实现细节,同时也增强了代码的复用性。内部类分为几种类型,包括成员内部类、...

    java内部类总结(含概念和实例)

    Java 内部类总结 Java 内部类是指在一个外部类的内部再定义一个类。内部类作为外部类的一个成员,并且依附于外部类而存在的。内部类可为静态,可用 protected 和 private 修饰(而外部类只能使用 public 和缺省的包...

    java 内部类 局部内部类 匿名类 实例代码

    Java内部类是Java语言特性中一个独特而强大的部分,它允许在一个类的内部定义另一个类。内部类可以访问外部类的所有成员,包括私有成员,这使得内部类在实现某些特定设计模式时非常有用。本篇文章将深入探讨Java中的...

    Java语法总结 - 内部类

    Java内部类详解 Java内部类是Java语言中的一种特殊类别,它是指定义在另外一个类内部的类。内部类可以访问外部类的所有成员变量和方法,包括私有的变量和方法。内部类可以分为四种:成员内部类、静态嵌套类、方法...

    Java内部类的四个应用场景

    谈Java内部类的四个应用场景

    java内部类使用总结

    Java内部类是Java语言中一个独特且强大的特性,它允许我们在一个类的内部定义另一个类。内部类提供了增强封装的能力,使得内部类可以被隐藏在外部类中,仅对外部类可见,从而增加了代码的隐私性和安全性。同时,内部...

    Java内部类的作用

    ### Java内部类的作用 #### 一、定义及概念 内部类,顾名思义,指的是一个定义在另一个类内部的类。这样的结构让内部类能够访问外部类的成员(包括私有成员),并且允许内部类拥有不同的访问级别,如`private`、`...

    Java内部类_动力节点Java学院整理

    Java内部类是Java语言的一个重要特性,它允许我们在一个类的内部定义另一个类。这种设计模式使得代码结构更加紧凑,同时也提供了更高级的封装和抽象。内部类主要有两种类型:成员内部类和局部内部类。 1. 成员内部...

Global site tag (gtag.js) - Google Analytics