您的位置:首页 > 编程语言 > Java开发

Java核心技术:卷1笔记[4] 接口和内部类

2011-12-13 11:49 549 查看
1.接口中的任何方法都自动是public类型的,无需提供public关键字。不能在接口中放置静态方法,接口中也绝不会去实现方法。在实现接口时必须把方法声明为public。

2.接口中可以提供常量。

3.接口中决不能有实例字段。接口中的字段总是默认为public static final的,无需提供关键字说明。

4.接口不是类,不能构造接口对象,但还是可以声明接口变量。

5.也可以用instanceof来检查对象是否实现了某个接口。

6. 使用=号拷贝一个变量时,原始值和拷贝指向同一个对象。如果需要copy成一个新对象,需要使用clone方法。clone方法只是定义在Object中的一个protect方法,它只会按照字段逐一拷贝。

7. 如果源对象和克隆对象共享的子对象是不可改变的,则浅拷贝没有问题。有两种情况可能发生子对象不可改变的现象:子对象属于不能改变的类,如String。或者子对象在其生命周期内只保存一些常量,在其上没有更改方法,也没有方法生成对它的引用。

8.当子对象是可变的时候,必须重新定义clone方法进行深拷贝以同时克隆子对象。

9.Cloneable接口是一个标记接口,跟clone方法没有关系(该方法是在Object类中定义的)。如果对象要求克隆,但没有实现这个接口,那么会产生一个已检查异常。

10.建立深拷贝的clone方法:

Class Employee implements Cloneable{

public Object clone(){

try{

//调用Object.clone()

//clone方法总是返回Object,需要进行类型转换。

Employee cloned = (Employee)super.clone(); cloned.hireDay=(Date)hireDay.clone();

return cloned;

}

catch(ClonNotSupportedException e) { return null;}

}

}

11.使用内部类的原因主要有四个:

1)内部类对象能够访问创建它的对象的实现,包括其私有数据;

2)内部类能够隐藏起来,不为同一包中的其他类所见;

3)匿名内部类可以方便地定义运行时回调;

4)使用内部类在编写事件驱动的程序时很方便

12.内部类的对象实例含有一个隐式引用,指向那个实例化它的外部类对象。通过这个指针,内部类对象可以访问外部对象的全部状态。

13.内部类对象并不是外部类的实例字段,而是外部类方法中的局部变量;

14.只有内部类才是私有的,普通类总是具有包可见性或者公有可见性。

15.局部内部类不会使用访问修饰符来指示,它们的范围总是限定在声明它们的程序块中。它们能够对外部世界完全隐藏起来,除了定义局部内部类所在的方法,没有方法知道其存在。它不仅能访问外部类中的字段,甚至还能访问局部变量,不过那些局部变量必须被声明为final的:

public class BankAccount{



public void start(final double rate){

class InterestAdder implements ActionListener{

public void actionPerformed(ActionEvent event){

double interest = balance*rate/100;

balance+=interest;



}

}

ActionListener adder = new InterestAdder();

Timer t= new Timer(1000,adder);

t.start();

}

}

为了让actionPerformed方法中的代码能够工作,InterestAdder类必然在释放start方法的局部变量rate之前给它做了一份拷贝。

16.匿名内部类:创建一个实现了ActionListener接口的类的新对象。

public class BankAccount{



public void start(final double rate){

ActionListener adder = new ActionListener (){

public void actionPerformed(ActionEvent event){

double interest = balance*rate/100;

balance+=interest;



}

};

Timer t= new Timer(1000,adder);

t.start();

}

}

17.由于构造器名必须和类名相同,而匿名类没有名字,所以匿名内部类不能有构造器。取而代之的是,构造器参数被送到超类的构造器中,用于构造对象的任何参数都要放在超类型名字后的括号中,一般情况下语法如下:

new SuperType(construction parameters)

{

内部类方法和数据

}

这里,超类型可以是接口,那么内部类实现该接口;也可以是类,那么内部类扩展这个类。例如:

Person queen = new Person(“Mary”); //一个Person对象

Person count = new Person(“Dracula”){…}; //一个扩展了Person的内部类的对象

如果内部类实现的是接口,那么该内部类没有任何构造参数,不仅如此,还必须按照如下语法提供一组括号:

new InterfaceType(){methods and data}

18.有时候使用内部类只是为了把一个类隐藏在另一个类中,并不需要内部类具有对外部类对象的引用。在这种情况下可以把内部类声明为static以去掉生成的引用。如果内部类是在一个静态方法中构造的,则必须使用静态内部类。

19.代理类能够在运行时创建新的类,这样的代理类能够实现你指定的接口,尤其是代理类具有:指定接口所要求的所有方法,Object类定义的所有方法。但是不能在运行时为这些方法定义新的代码,必须提供一个调用处理器。

20.调用处理器:是实现了InvocationHandler接口的任意类的对象,该接口只有一个方法:

Object invoke(Object proxy,Method method,Object[] args)

只要调用了代理对象上的任意一个方法,调用处理器的invoke方法就会带着Method对象和原调用的参数被调用,随后调用处理器必须指出如何处理调用。

21.要创建一个代理对象,需要使用Proxy类中的newProxyInstance方法。该方法有三个参数:

1)一个类加载器;

2)一个Class对象数组,每个元素都是需要实现的接口;

3)一个调用处理器;

22.使用代理和调用处理器来跟踪方法调用,打印被调用方法的名字和参数,然后使用包装过的对象作为隐含参数来调用方法:

import java.lang.reflect.*;

import java.util.*;

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 = new Integer(i + 1);

InvocationHandler handler = new TraceHandler(value);

//为所有接口构造代理

Class[] interfaces = value.getClass().getInterfaces();

Object proxy = Proxy.newProxyInstance(null, interfaces, handler);

elements[i] = proxy;

}

// construct a random integer

Random generator = new Random();

int r = generator.nextInt(elements.length);

Integer key = new Integer(r + 1);

// binarySearch方法这样调用:if(elements[i].compareTo(key)<0)…

int result = Arrays.binarySearch(elements, key);

// print match if found

if (result >= 0)

System.out.println(elements[result]);

}

}

class TraceHandler implements InvocationHandler

{

public TraceHandler(Object t)

{

target = t;

}

public Object invoke(Object proxy, Method m, Object[] args) throws Throwable

{

// 在实际调用之前添加操作:打印调用对象及方法的信息

System.out.print(target);

System.out.print("." + m.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(")");

// invoke actual method

return m.invoke(target, args);

}

private Object target; //包装的对象

}

23.代理类是在程序运行时创建的,然而一旦被创建,也就成了常规类,同虚拟机中的任何其他类没有区别;

24.所有的代理类都扩展了Proxy类。一个代理类只有一个实例变量:定义在Proxy超类中的调用处理器。执行代理对象的任务所需的任何附加数据必须存储在调用处理器中。

25.代理类的名字是没有定义的,Java SDK中的proxy类会自动生成类名,这些名字以字符串$开始。

26.对于一个特定的类加载器和预设的接口组来说,只有唯一的一个代理类。如果使用同一个类加载器和接口组对newInstance方法进行两次调用的话,将会得到同一个类的两个对象。可以通过getProxyClass方法得到该类:

Class proxyClass = proxy.getProxyClass(null,interfaces);

27.代理类总是public和final的。如果代理类实现的所有接口都是public的,那么此代理类不属于任何特定的包。否则,所有的非公有的接口必须属于同一个包,代理类也属于那个包。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: