`

局部类访问外部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是否代理一个代理类.
分享到:
评论

相关推荐

    Java岗面试核心MCA版

    一小块区域 成员变量:方法外部,类内部定义的变量 局部变量:类的方法中的变量。 成员变量和局部 变量的区别 作用域 成员变量:针对整个类有效。 局部变量:只在某个范围内有效。(一般指的就是方法,语句体内)

    【05-面向对象(下)】

    •对一个final变量来说,不管它是类变量、实例变量,还是局部变量,只要该变量满足3个条件,这个final变量就 不再是一个变量,而是相当于一个直接量。  –使用final修饰符修饰;  –在定义该final变量时指定...

    21天学通Java-由浅入深

    240 12.2.1 创建局部内部类 240 12.2.2 在局部内部类中访问外部类成员变量 240 12.2.3 在局部内部类中访问外部类的局部变量 241 12.2.4 静态方法中的局部内部类 243 12.3 静态内部类 244 12.3.1 创建静态内部类 244 ...

    JAVA入门1.2.3:一个老鸟的JAVA学习心得 PART1(共3个)

    9.1.2 加餐:不可改变的final变量 211 9.1.3 多维数组的长度 212 9.1.4 一维数组的clone()方法 212 9.1.5 当数组类型不再是基本数据类型 214 9.1.6 多维数组的clone()方法 217 9.2 老朋友String类 220 9.2.1 ...

    Java入门1·2·3:一个老鸟的Java学习心得.PART3(共3个)

    9.1.2 加餐:不可改变的final变量 211 9.1.3 多维数组的长度 212 9.1.4 一维数组的clone()方法 212 9.1.5 当数组类型不再是基本数据类型 214 9.1.6 多维数组的clone()方法 217 9.2 老朋友String类 220 9.2.1 ...

    疯狂JAVA讲义

    6.4.1 final变量 177 6.4.2 final方法 181 6.4.3 final类 182 6.4.4 不可变类 182 6.4.5 缓存实例的不可变类 186 6.5 抽象类 188 6.5.1 抽象方法和抽象类 188 6.5.2 抽象类的作用 191 6.6 更彻底的抽象:...

    javaSE代码实例

    15.2.2 局部变量与局部内部类 329 15.2.3 静态方法中的局部内部类 331 15.3 静态内部类 332 15.3.1 语法规则 332 15.3.2 创建静态内部类的对象 332 15.3.3 静态/非静态内部类的区别 333 15.4 匿名内部...

    java 面试题 总结

    java.lang.String类是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类 3、int 和 Integer 有什么区别 Java 提供两种不同的类型:引用类型和原始类型(或内置...

    北邮高级语言程序设计(基于Java)第三次阶段作业.docx

    ] 得分: [5] 试题分值: 5.0 提示: 被声明为private,protected及public的类成员,在类外部____ 只能访问声明为public的成员 只能访问到声明为protected和public的成员 都可以访问 都不可以访问 知识点: 第三单元过关...

    整理后java开发全套达内学习笔记(含练习)

    ORACLE_SID=oral10g\ --变局部变量 export ORACLE_SID --变全局变量 unset ORACLE_SID --卸载环境变量 ORACLE_HOME=... --安装路径;直接用一句语句也可以,如下 export ORACLE_HOME=/oracledata/.../bin: ...

    深入理解C++11:C++11新特性解析与应用

    402.10 final/override控制 442.11 模板函数的默认模板参数 482.12 外部模板 502.12.1 为什么需要外部模板 502.12.2 显式的实例化与外部模板的声明 522.13 局部和匿名类型作模板实参 542.14 本章小结 55第3章 通用为...

Global site tag (gtag.js) - Google Analytics