Java核心技术第6章(4)
来源:互联网 发布:centos企业邮箱搭建 编辑:程序博客网 时间:2024/05/22 04:29
6.4.4 局部内部类
在TalkingClock示例中,TimePrinter这个类名字只在start方法中创建这个类型的对象时使用了一次.当遇到这类情况时,可以在一个方法中定义局部类.
public void start(){ class TimePrinter implements ActionListener { public void actionPerformed(ActionEvent event) { Date now = new Date(); System.out.println("At the tone, the time is " + now); if (beep) Toolkit.getDefaultToolkit().beep(); } } ActionListener listener = new TimePrinter(); Timer t = new Timer(interval, listener); t.start();}局部类不能用 public 或 private 访问说明符进行声明,它的作用域被限定在声明这个局部类的块中.
局部类有一个优势,即对外部世界可以完全地隐藏起来.即使TalkingClock类中的其他代码也不能访问它.
6.4.5 由外部方法访问 final 变量
与其他内部类相比较,局部类还有一个优点,它们不仅能够访问它们的外部类,还可以访问局部变量.不过那些局部变量必须被声明为 final,下面是一个典型的示例.这里,将TalkingClock构造器的参数interval和beep移至start方法中.public void start(int interval, final boolean beep){ class TimePrinter implements ActionListener { public void actionPerformed(ActionEvent event) { Date now = new Date(); System.out.println("At the tone, the time is " + now); if (beep) Toolkit.getDefaultToolkit().beep(); } } ActionListener listener = new TimePrinter(); Timer t = new Timer(interval, listener); t.start();}请注意,TalkingClock类不再需要存储实例变量beep了,它只是引用start方法中的beep参数变量.
这看起来好像没什么值得大惊小怪的,程序
if (beep) ...毕竟在start方法内部,为什么不能访问beep变量的值呢?仔细看一下调用流程:
1.调用start方法
2.调用内部类TimePrinter的构造器,以便初始化对象变量listener
3.将listener引用传递给Timer构造器,定时器开始计时,start方法结束.此时start方法的beep参数变量不复存在.
4.然后,actionPerformed方法执行 if(beep) ...
为了能够让actionPerformed方法工作,TimePrinter类在beep域释放之前将beep域用start方法的局部变量进行备份.实际上也是这样做的.
局部类的方法只可以引用定义为 final 的局部变量.鉴于此情况,在列举的实例中,将beep参数声明为 final,对它进行初始化后不能够再进行修改,因此,就使得局部变量与局部类内建立的拷贝保持一致.
6.4.6 匿名局部类
将局部内部类的使用再深入一步,假如只创建这个类的一个对象,就不必命名了.这种被被称为匿名内部类(annoymous inner class).public void start(int interval, final boolean beep){ ActionListener listener = new ActionListener() { public void actionPerformed(ActionEvent event) { Date now = new Date(); System.out.println("At the tone, the time is " + now); if (beep) Toolkit.getDefaultToolkit().beep(); } }; Timer t = new Timer(interval, listener); t.start();}这种语法的含义是:创建一个实现ActionListener接口的类的新对象,需要实现的方法actionPerformed定义在括号{}内.
通用的语法格式为:
new SuperType(construction parameters){ inner class methods and data}其中,SuperType可以是ActionListener这样的接口,于是内部类就要实现这个接口. SuperType可以是一个类,于是内部类就要扩展它.
由于构造器的名字必须类名相同,而匿名类没有类名,所以,匿名类不能有构造器.取而代之的是,将构造器参数传递给超类的构造器.尤其是在内部类实现接口的时候,不能有任何构造参数.不仅如此,还要像下面这样提供一组括号:
new InterfaceType(){ methods and data}如果构造参数的闭圆括号跟着一个开花括号,正在定义的类就是匿名内部类.
程序6-7包含了用匿名内部类实现语音时钟程序的代码,它与程序6-6相比,它的使用匿名内部类的解决方法比较简短,更切实际.
anonymousInnerClass/AnonymousInnerClassTest.java如下所示:
package anonymousInnerClass;import javax.swing.*;public class AnonymousInnerClass{ public static void main(String[] args) { TalkingClock clock = new TalkingClock(); clock.start(1000, true); // keep program running until user selects "Ok" JOptionPane.showMessageDialog(null, "Quit program?"); System.exit(0); }}AnonymousInnerClass/TalkingClock.java如下所示:
package anonymousInnerClass;import java.awt.*;import java.awt.event.*;import java.util.*;import javax.swing.*;import javax.swing.Timer;public class TalkingClock{ /** * starts the clock * @param interval the interval between messages(in milliseconds) * @param beep true if the clock should beep */ public void start(int interval, final boolean beep) { ActionListener listener = new ActionListener() { public void actionPerformed(ActionEvent event) { Date now = new Date(); System.out.println("At the tone, the time is " + now); if (beep) Toolkit.getDefaultToolkit().beep(); } }; Timer t = new Timer(interval, listener); t.start(); }}运行结果如下所示:
6.4.7 静态内部类
如果使用内部类只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外围类对象.为此,可以将内部类声明为 static,以便取消产生的引用.下面是一个使用静态内部类的典型例子.考虑一下计算数组中最小值和最大值的问题.当然,可以编写两个方法,一个方法用于计算最小值,另一个方法用于计算最大值.在调用这两个方法的时候,数组被遍历两次.如果只是遍历数组一次,并且能够同时计算出最小值和最大值,那么就可以大大提高效率了.
double min = Double.MAX_VALUE;double max = Double.MIN_VALUE;for (double v : values){ if (min > v) min = v; if (min < v) max = v;}然而,这个方法必须返回两个数值,为此,可以定义一个包含两个值的类pair:
class Pair{ private double first; private double second; public Pair*(double f, double s) { first = f; second = s; } public double getFirst() { return first; } public double getSecond() { return second; }}minmax方法可以返回一个Pair类型的对象.
class ArrayAlg{ public static Pair minmax(double[] values) { ... return new Pair(min, max); }}这个方法的调用者可以使用getFirst和getSecond方法获得答案:
Pair p = ArrayAlg.minmax(d);System.out.println("min = " + p.getFirst());System.out.println("max = " + p.getSecond());当然,Pair是一个大众化的名字,容易发生名字冲突.解决这个问题的办法是将Pair定义为ArrayAlg的内部公有类.此后,通过ArrayAlg.Pair调用它:
ArrayAlg.Pair p = ArrayAlg.minmax(d);不过,与前面例子中所使用的内部类不同,在Pair对象中不需要引用任何其他的对象,为此,可以将这个内部类声明为 static:
class ArrayAlg{ public static class Pair { ... }}当然,只有内部类可以声明为 static .静态内部类的对象除了没有对生成它的外围类对象的引用特权外,与其他所有内部类完全一样.在列举的示例中,必须使用静态内部类,这是由于内部类对象是在静态方法中构造的:
public static Pair minmax(double[] d){ ... return new Pair(min, max);}如果没有将Pair类声明为 static,那么编译器将会给出错误的报告:没有可用的隐式ArrayAlg类型对象初始化内部类对象.
注释:在内部类不需要访问外围类对象的时候,应该使用静态内部类.
注释:声明在接口中的内部类自动称为 static 和 public 类.
程序6-8包含ArrayAlg类和嵌套的Pair类的代码,staticInnerClass/StaticInnerClassTest.java如下所示:
package staticInnerClass;/** * This program demonstrates the use of static inner classes */public class StaticInnerClassTest{ public static void main(String[] args) { double[] d = new double[20]; for (int i = 0; i < d.length; i++) d[i] = 100 * Math.random(); ArrayAlg.Pair p = ArrayAlg.minmax(d); System.out.println("min = " + p.getFirst()); System.out.println("max = " + p.getSecond()); }}staticInnerClass/ArrayAlg.java如下所示:
package staticInnerClass;public class ArrayAlg{ /** * A pair of floating-point numbers */ public static class Pair { private double first; private double second; /** * Constructor a pair from two floating-point numbers * @param f the first number * @param s the second number */ public Pair(double f, double s) { first = f; second = s; } /** * Returns the first number of the pair * @return the first number */ public double getFirst() { return first; } /** * Returns the second number of the pair * @return the second number */ public double getSecond() { return second; } } /** * Computes both the minimum and the maximum of an array * @param values an array of floating-point numbers * @return a pair whose first element is the minimum and whose second a element is the maximum */ public static Pair minmax(double[] values) { double max = Double.MIN_VALUE; double min = Double.MAX_VALUE; for (double v : values) { if (min > v) min = v; if (max < v) max = v; } return new Pair(min, max); }}运行结果如下所示:
6.5 代理
利用代理(proxy)可以在运行时创建一个实现了一组给定接口的新类.这个功能只有在编译时无法确定需要实现哪个接口时才有必要使用.对于应用程序设计人员来说,遇到这种情况的机会很少.但对于系统程序设计人员来说,代理带来的灵活性非常重要.假设有一个表示接口的Class对象,它的确切类型在编译时无法知道.这确实有些难度.要想构造一个实现这些接口的类,就需要使用newInstance方法或反射找出这个类的构造器.但是,不能实例化一个接口,需要在程序处于运行状态时定义一个新类.
为了解决这个问题,有些程序将生成代码;将这些代码放置在一个文件中;调用编译器,然后再加载结果类文件.但这样做的速度会比较慢,并且需要将编译器与程序放在一起.而代理机制则是一种更好的解决方案.代理类可以在运行时创建全新的类.这样的代理类能够实现指定的接口.尤其是,它具有下列方法:
指定接口所需要的全部方法
Object类中的全部方法,例如,toString,equals等
然而,不能在运行时定义这些方法的新代码,而是要提供一个调用处理器(invocation handler).调用处理器是实现了InvocationHandler接口的类对象.在这个接口中只有一个方法:
Object invoke(Object proxy, Method method, Object[] args);无论何时调用代理对象的方法,调用处理器的invoke方法都会被调用,并向其传递Method对象和原始的调用参数.
0 0
- Java核心技术第4章(6)
- Java核心技术第6章(4)
- Java核心技术第4章(4)
- Java核心技术第3章(6)
- Java核心技术第5章(6)
- Java核心技术第6章(1)
- Java核心技术第6章(2)
- Java核心技术第6章(3)
- Java核心技术笔记-第6章
- Java-核心技术总结-第4章
- Java核心技术第3章(4)
- Java核心技术第4章(1)
- Java核心技术第4章(2)
- Java核心技术第4章(3)
- Java核心技术第4章(5)
- Java核心技术第4章(7)
- Java核心技术第4章(8)
- Java核心技术第4章(9)
- Codevs 1106 篝火晚会
- JSON服务器的使用和客户端的解析
- 近期感悟
- seajs常用配置
- Android调用基于.net的WebService
- Java核心技术第6章(4)
- js 浏览器兼容问题
- 高精度计时器类
- LeetCode(99) Recover Binary Search Tree
- OpenGL里的的缓冲器的分类和使用
- Android强制让某个控件获得焦点
- request.getRequestURI() 、request.getRequestURL()
- ubuntu SDL加载图片
- onvif-1