在局部类, 我们要更新封闭作用域用的变量, 这一般来说是不容易做到了. 因为局部类要访问封闭作用域的类, 要用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是否代理一个代理类.