`

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

相关推荐

    局部变量用final的讨论

    这是因为内部类可以访问外部类的final或effectively final变量,而这些变量被视为常量。 3. **代码优化**:JVM可能会对`final`局部变量进行优化,如逃逸分析,将它们存储在栈上而不是堆上,提高运行效率。 4. **...

    Java中局部内部类可以访问它所在方法中定义的final修饰的局部变量的合理解释.doc

    这是因为 JVM 在编译时会将 final 修饰的局部变量复制一份给局部内部类,这样当局部内部类访问该变量时,实际上是访问的复制后的副本,而不是原始的局部变量。 在上面的代码中,我们定义了一个外部类 Outer,内部...

    局部内部类和匿名内部类使用局部变量为什么要final1

    因此,内部类访问的"局部变量"实际上是内部类自身的成员,而不是原始的局部变量。 例如: ```java public class Test { public static void main(String[] args) { Change c = method(); c.change(); // 访问...

    final类,方法,变量

    如果这个引用是`final`的,那么内部类就能安全地访问这个引用,因为外部类的状态不会在内部类的生命周期内改变。同样,在Java 8及更高版本中,`final`局部变量可以作为lambda表达式的捕获变量,保证了lambda表达式...

    Java中final的深度剖析

    在某些情况下,JVM会进行逃逸分析,如果发现final变量不会被外部访问,可能会将其优化为栈上的局部变量,从而提高性能。同时,final变量的编译时替换也有助于提高代码执行速度。 6. **final和异常处理** 在异常...

    Java基础权限控制与final,内部类

    default即没有修饰符时,表示成员仅能被同一个包内的其他类访问;private表示成员只能在其所在类内部访问。这些权限修饰符的选择取决于你希望其他代码对这些成员的访问程度。 接下来,关于final关键字,它是Java中...

    day11【final、权限、内部类】.pdf

    - **final修饰变量**:基本数据类型的局部变量,一旦被`final`修饰后,其值就不能再被改变。引用类型的`final`变量是指不能指向另一个对象,但是其指向的对象的成员变量是可以改变的。 ### 权限修饰符 Java中通过...

    Java内部类访问规则.doc

    - 如果局部内部类要访问其所在方法的局部变量,该变量必须声明为`final`,否则无法访问。 5. **匿名内部类**: - 匿名内部类是没有名称的内部类,常用于简洁地实现单个接口或继承单个类的情况。 - 匿名内部类...

    java嵌套类

    这类嵌套类的特点是,它们可以访问外部类的成员变量和方法,同时也可以访问定义它们的方法中的局部变量。但是,这些局部变量必须是final类型,或者事实上的final(即未被修改的局部变量)。局部嵌套类可以被声明为...

    java入门教程:数据类型_类变量(静态变量).pdf

    通常,为了方便外部类访问,静态变量被声明为public。 8. **常量**:公共、静态和最终变量(`public static final`)被称为常量。它们在声明时初始化并且之后不能更改。常量通常用大写字母表示,比如`DEPARTMENT`。...

    Java中的final关键字

    - 在创建匿名内部类时,如果需要访问外部类的局部变量,该变量必须是`final`或实际上等效于`final`的。这是因为匿名内部类可能在外部方法执行完成后仍然存在,所以需要确保引用的变量不会在内部类的生命周期内改变...

    JAVA内部类总结

    4. **内部类访问外部类**:非静态成员内部类可以通过`this`关键字访问外部类中的成员变量和方法。如果外部类的方法中存在同名变量,则可以通过`外部类.this.变量`的方式访问外部类的成员变量。 #### 三、静态成员...

    Java常见笔试、面试题目深度剖析 final详解

    1. **final变量** - `final`修饰的变量一旦赋值后,就不能再改变。这可以确保变量的值在整个程序运行期间保持不变,常用于创建常量。 - 对于基本类型,`final`变量在声明时必须初始化,或在构造器中初始化。 - ...

    内部类详解,看了他就不用看其他资料了

    - 外部类访问内部类成员需通过内部类的对象,且成员内部类不能包含静态成员。 - 当外部类和内部类有同名成员时,可通过`&lt;外部类类名&gt;.this`来区分。 4. **局部内部类**: - 定义在方法内部的类称为局部内部类,...

    Java中的静态变量静态方法静态块与静态类.docx

    在一个静态方法中,除了局部变量外,能够访问的外部变量也必须是静态的,能够访问的外部方法也必须是静态的,实例变量和实例方法均不能访问。 静态方法通常是 public 的,作为工具类的工具方法出现。 例如: ```...

    final用法........

    - 对于方法内部类(匿名内部类或局部内部类),如果需要访问方法的局部变量,这些变量必须声明为 `final`,因为内部类会持有一个对外部作用域的引用,而 `final` 变量在内部类中相当于常量。 5. **final 在匿名...

    深入解析Java编程中final关键字的作用

    这意味着匿名内部类可以重写其外部类或接口定义的方法。 6. **final与反射**: 反射API中的`Modifier.isFinal()`方法可以用来检查一个类或方法是否声明为final。例如: ```java Modifier.isFinal(anonymous....

    final关键字和static_用法

    静态内部类的特点是不依赖于外部类的实例就可以创建自己的实例,同时它不能访问外部类的非静态成员。 示例代码: ```java public class OuterClass { private int num = 10; public static class ...

    Java内部类.pdf

    内部类可以访问外部类的成员变量和方法,但是外部类访问内部类的成员变量和方法,需要通过内部类的实例进行。内部类持有外部类的引用,这个引用在Java中被隐藏地表示为“外部类名.this”。 3. 内部类的实例化 在...

Global site tag (gtag.js) - Google Analytics