局部类访问外部final变量

在局部类, 我们要更新封闭作用域用的变量, 这一般来说是不容易做到了. 因为局部类要访问封闭作用域的类, 要用final去修饰封闭作用域的变量.
例子: 想统计一下在排序过程中调用 compareTo方法的次数
int count = 0;
Date[] dates = new Date[100];
for(int i=0; i < dates.length; i++){
  dates[i] = new Date(){
    public int compareTo(Date other){
      count++; //ERROR
      return super.compareTo(other);
    }
  };
}


由于清楚知道counter需要更新, 所以不能将counter声明为final,由于Integer对象是不可变的, 所以也不能用Integer代替它. 补救的方法是使用一个长度为1的数组
final int[] counter = new int[1];
for(int i=0; i<dates.length; i++){
  dates[i] = new Date(){
    public int compareTo(){
      counter[0]++;
      return super.compareTo(other);
    }
  };
}


这里数组变量仍然被声明为final , 但是这仅仅表示 不可以让它引用另外一个数组. 数组中数据元素可以自由地更改.

===========================================
内部类特殊的语法规则.
/**
 * 外部类 
 */
class TalkingClock{
	private int interval;
	private boolean beep; 
	
	public TalkingClock(int interval, boolean beep){
		this.interval = interval;
		this.beep = beep;
	}
	
	public void start(){
		ActionListener listener = new TimePrinter();
		Timer t = new Timer(interval, listener);
		t.start();
	}

	/**
	 * 内部类,这里用的是public修饰符 
	 */
	public class TimePrinter implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {
			Date now = new Date();
			System.out.println("At the tone, the time is " + now);
			if(beep){
				Toolkit.getDefaultToolkit().beep();
			}
		}
	}
	
}


使用外围类引用的语法表达式
OuterClass.this
表示外围类似引用.
可以像下面这样编写TimePrinter内部类似的actionPerformed方法.
public void actonPerformed(ActionEvent event){
  if(TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep();
}

反过来,可以采用下列语法格式更加明确地编写内部对象的构造器
outerObject.new InnerClass(construction parameters)
例如,
ActionListener listener = this.new TimePrinter();

在这里, 最新构造的TimePrinter对象的外围类引用被设置为创建内部类对象的方法中this引用. 这是一种很常见的情况. 通常this限定词是多余的. 不过,可以通过命名将外围类引用设置为其它对象. 例如, 如果TimePrinter是一个公有内部类, 对于任意的语音时钟都可以构造一个TimePrinter
TalkingClock jabberer = new TalkingClock(1000, true);
TalkingClock.TimePrinter listener = jabberer.new TimePrinter(); //注意这一行


在外围类的作用域之外, 可以这样引用内部类
OuterClass.InnerClass

=====================
有时候,使用内部类只是为了把一个类隐藏在另外 一个类的内部,并不需要内部类引用外围类对象. 为此,可以将内部类声明为static 以便取消产生的引用.


=====================代理
代理类具有下列方法:
. 指定接口所需要的全部方法
. Object类的全部方法,例如: toString, equals等

调用处理器(invocationhandler)是实现了InvocationHandler接口的类对象
无论何时调用代理对象的方法,调用处理器的invoke方法都会被调用,并向其传递Method对象和原始的调用参数 . 调用处理器必须给出处理调用的方式.

创建一个代理对象,要使用Proxy类的newProxyInstance方法. 三个参数:
1. 类加载器, 用null表示默认的类加载器
2. 一个Class对象数组,每个元素都是需要实现的接口
3. 一个调用处理器

使用代理的原因有很多, 例如:
1. 路由对远程服务器的方法调用
2. 在程序运行期间,将用户接口事件与动作关联起来.
3. 为调试跟踪方法调用.

代理对象属于在运行时定义的类(它有一个名字,如$Proxy). 对这个调用的方法都会转为调用代理对象处理器的 invoke方法
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Random;

/**
 * 代理
 */
public class ProxyTest {
	
	public static void main(String[] args) {
		Object[] elements = new Object[1000];
		
		for(int i=0 ; i < elements.length; i++){
			Integer value = i+1;
			InvocationHandler handler = new TraceHandler(value);
			elements[i] = Proxy.newProxyInstance(null, new Class[]{Comparable.class}, handler);
		}
		
		//要查找的内容
		Integer key = new Random().nextInt(elements.length) + 1;
		
		//查找
		int result = Arrays.binarySearch(elements, key);
		
		//把查找到的对象打印出来
		if(result >= 0){
			System.out.println(elements[result]);
		}
	}
}

//调用处理器
class TraceHandler implements InvocationHandler{
	private Object target;
	
	public TraceHandler(Object t){
		target = t;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.print(target);
		
		System.out.print("." + method.getName() + "(");
		if(args != null){
			for(int i=0; i<args.length; i++){
				System.out.print(args[i]);
				if(i < args.length -1) System.out.print(",");
			}
		}
		System.out.println(")");
		
		return method.invoke(proxy, args);
	}
}


代理类的特性
代理类是在程序运行过程中创建的, 然而,一旦被创建,就变成常规类,与虚拟机中的任何其他类没有区别

所有的代理类都扩展Proxy类, 一个代理类只有一个实例域---调用处理器,它定义在Proxy的超类中.为了履行代理对象的职责, 所需要的任务附加数据都必须存储在调用处理器中.

所有的代理方法都一样, 这个方法仅仅调用了调用处理器的invoke. Object类中的其它方法(如clone和getClass没有被重新定义)

没有定义代理类的名字, sun虚拟机中的Proxy类将生成一个以字符串$Proxy开头的类名.

对于特定的类加载器和预设的一组接口来说, 只能有一个代理类, 也就是说, 如果使用同一个类加载器和接口数组调用两次newProxyInstance方法的话, 那么只能得到同一个类的两个对象 , 也可以利用getProxyClass方法获得这个类
Class proxyClass = Proxy.getProxyClass(null, interfaces);

代理类一定是public 和final . 如果代理类实现的所有接口都是public, 代理类就不属于某个特定的包, 否则,所有非公有的接口都必须属于同一个包. 同时, 代理类也属于这个包.

调用Proxy类中的isProxyClass方法检测一个特定的Class是否代理一个代理类.

你可能感兴趣的:(虚拟机,sun)