1. 尽管不能构造接口对象,却能声明接口的变量:
Comparable x;//OK
接口变量必须引用实现了接口的类对象
x = new Employee(…); // OK provided Employee implements Comparable
2. 如果希望自己设计的类拥有克隆和比较的能力,只要实现Cloneable和Comparable。
3. 如果对象的属性全是基本类型的,那么可以使用浅拷贝(clone),但是如果对象有引用属性,那就要基于具体的需求来选择浅拷贝还是深拷贝。
下面是一个建立深拷贝clone方法的一个示例:
class Employee implements Cloneable {
…
public Employeeclone() throws CloneNotSupportException {
//callobject.clone()
Employeecloned = (Employee) super.clone();
//clonemutable fields
cloned.hireDay= (Date) hireDay.clone();
returncloned;
<span style="white-space:pre"> </span>}
}
4. 接口与回调
class aa implementsActionListener {
@Override
public void actionPerformed(ActionEvent arg0) {
Date now = new Date();
System.out.println("The time is "+ now);
}
}
publicclassMain {
public static void main(String[] args) {
ActionListener listener = new aa();
Timer t = new Timer(1000, listener);
t.start();
JOptionPane.showMessageDialog(null, "退出?");
}
}
5. 内部类:
内部类既可以访问自身的数据域,也可以创建它的外围类对象的数据域。内部类的对象总有一个隐式引用,它指向了创建它的外部类对象。
public class TalkingClock {
private intinterval;
private booleanbeep;
public TalkingClock(int interval, boolean beep) {…..}
public voidstart() {…..}
public classTimePrinter implements ActionListener {
…… // an inner class
}
如果一个TimePrinter类是一个常规类,它就需要通过TalkingClock类的公有方法访问beep标志,而使用内部类可以不必提供仅用于访问其他类的访问器。
TimePrinter类声明为私有的,这样只有TalkingClock的方法能够构造TimePrinter对象。只有内部类可以是私有类,而常规类只可以具有包可见性,或公有可见性。
6. 可以采用下列语法格式更加明确地编写内部对象的构造器。
outerObject.newInnerClass(construction parameters)
例如:
ActionListenerlistener = this.new TimePrinter();
如果TimePrinter是一个公有内部类,对于任意的语音时钟都可以构造一个TimePrinter
TalkingClockjabberer = new TalkingClock(1000,true);
TalkingClock.TimePrinterlistener = jabberer.new TimePrinter();
需要注意,在外围类的作用域之外,可以这样引用内部类:
OuterClass.InnerClass
7. 局部内部类:定义在方法内部
局部类不能用public或private访问说明符进行声明。它的作用域被限定在声明这个局部类的块中。
局部类有一个优势,即对外部世界可以完全隐藏起来。即使TalkingClock类中的其他代码也不能访问它。除start方法外,没有任何方法知道TimePrinter类的存在。
class TalkingClock {
private int interval;
private Boolean beep;
public TalkingClock(int interval, Boolean beep) {
this.interval = interval;
this.beep = beep;
}
public void start() {
class TimePrinter implements ActionListener {
@Override
public void actionPerformed(ActionEvent event) {
Date now = new Date();
System.out.println("the time is "+ now);
if (beep)
Toolkit.getDefaultToolkit().beep();
}
}
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval, listener);
t.start();
}
}
public class Main {
public static void main(String[] args) {
TalkingClock clock = new TalkingClock(1000, true);
clock.start();
JOptionPane.showMessageDialog(null, "Quit programme?");
System.exit(0);
}
}
8. 局部类不仅能够访问包含它们的类,还可以访问局部变量。不过那些局部变量必须被 声明为final。这使得局部变量与在局部类内建立的拷贝一致。下面是一个典型的示例。 这里,将TalkingClock构造器的参数interval和beep移至start方法中。
class TalkingClock {
public void start(int interval, finalbooleanbeep){
class TimePrinter implements ActionListener {
@Override
public voidactionPerformed(ActionEvent event) {
Date now = new Date();
System.out.println("the time is "+ now);
if (beep)
Toolkit.getDefaultToolkit().beep();
}
}
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval, listener);
t.start();
}
}
为了能够清楚的看到内部的问题,让我们仔细看一下控制流程。
a) 调用start方法
b) 调用内部类TimePrinter的构造器,以便初始化对象变量listener
c) 将listener引用传递给Timer构造器,定时器开始计时,start方法结束。此时,start方法的beep变量不复存在。
d) 然后,actionPeerformed方法执行if(beep)
编译器必须检测对局部变量的访问,为每一个变量建立相应的数据域,并将局部变量拷贝到构造器中,以便将这些数据域初始化为局部变量的副本。
9. 匿名内部类
将局部内部类的使用再深入一步。假如只创建这一个类的对象就不必命名了。
classTalkingClock {
public void start(int interval, final boolean beep) {
ActionListener listener = new ActionListener() {
@Override
publicvoidactionPerformed(ActionEvent event) {
Date now = new Date();
System.out.println("the time is " + now);
if (beep)
Toolkit.getDefaultToolkit().beep();
}
};
Timer t= newTimer(interval,listener);
t.start();
}
}
10. 下面的技巧称为“双括号初始化” (double brace initialization)。
ArrayList<String> friends = newArrayList<>();
favorites.add(“harry”);
favorites.add(“Tony”);
invite(friends);
如果不再需要这个数组列表,最好让它作为一个匿名列表。方法如下:
invite(new ArrayList<String>() {{ add(“Harry”);add(“Tony”); }})
注意这里的双括号。外层括号建立了ArrayList的一个匿名子类。内层括号则是一个对象构造块。
11.静态内部类
有时候,使用内部类只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外围类的对象。为此可以将内部类声明为static,以便取消产生的引用。
/**
* This programdemonstrates the use of static inner classes.
*
* @version 1.01 2004-02-27
* @author Cay Horstmann
*/
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());
}
}
class ArrayAlg {
public static 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;
}
}
public static Pair minmax(double[] values) {
double min = Double.MAX_VALUE;
double max = Double.MIN_VALUE;
for (double v : values) {
if (min > v)
min = v;
if (max < v)
max = v;
}
return new Pair(min, max);
}
}
12.代理
import java.lang.reflect.*;
import java.util.*;
/**
* This programdemonstrates the use of proxies.
* @version 1.00 2000-04-13
* @author Cay Horstmann
*/
public class ProxyTest
{
public static void main(String[] args)
{
Object[] elements = new Object[1000];
// fill elements with proxies for the integers 1 ... 1000
for (int i = 0; i < elements.length; i++)
{
Integer value = i + 1;
InvocationHandler handler = new TraceHandler(value);
Object proxy = Proxy.newProxyInstance(null, new Class[] { Comparable.class } , handler);
elements[i] = proxy;
}
// construct a random integer
Integer key = new Random().nextInt(elements.length) + 1;
// search for the key
int result = Arrays.binarySearch(elements, key);
// print match if found
if (result >= 0) System.out.println(elements[result]);
}
}
/**
* An invocationhandler that prints out the method name and parameters, then
* invokes theoriginal method
*/
class TraceHandler implementsInvocationHandler
{
private Object target;
/**
* Constructs aTraceHandler
* @param t the implicitparameter of the method call
*/
public TraceHandler(Object t)
{
target = t;
}
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable
{
// print implicit argument
System.out.print(target);
// print method name
System.out.print("." + m.getName() + "(");
// print explicit arguments
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(")");
// invoke actual method
return m.invoke(target, args);
}
}
代理类的特性:
1) 代理类是再程序运行过程中创建的。然而,一旦被创建,就变成了常规类。
2) 所有的代理类都扩展于Proxy类。一个代理只有一个实例域——调用处理器,它定义在Proxy的超类中。为了履行代理对象的职责,所需的任何附加数据都必须存储在调用处理器中。
3) 所有的代理都覆盖了Object类中的toString、equals、和hashCode。如同所有的代理方法一样,这些方法仅仅调用了调用处理器的invoke。Object类中的其他方法(如clone和getClass)没有被重新定义。
4) 没有定义代理类的名字,Sun虚拟机vs的Proxy类将生成一个以字符串$Proxy开头的类名。
5) 代理类一定是public和final。
6) 对于特定的类加载器和预设的一组借口来说,只能有一个代理类。
- Object invoke(Object proxy,Method method, Object[] args)
定义了代理对象调用方法时希望执行的动作。
- static Class getProxyClass(ClassLoaderloader, Class[] interfaces)
返回实现指定接口的代理类。
- static OnjectnewProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandlerhandler)
构造一个实现指定接口的代理类实例。所有方法都将调用给定处理器对象的invoke方法。