Java 12:接口、克隆、内部类、代理

来源:互联网 发布:js窗口高度 编辑:程序博客网 时间:2024/06/09 17:54

接口:描述类具有什么功能而不具体实现,一个类可以实现多个接口

克隆:创建一个新对象,和原来的状态相同,但是对新对象进行修改时不影响原始对象的状态。

内部类:定义在类的内部,其中的方法可以访问外部类的域

代理:能够实现任意接口的对象


1、接口

接口不是类,是对类的一组需求描述,无法new,但是可以声明接口变量并引用实现了接口的类对象

接口的所有方法默认为public,但实现接口时还是要显示声明

接口不能包含实例域、不能实现方法,可以将接口看成没有实例域的抽象类

接口中的域默认为public static final

抽象类和接口:抽象类可以代替接口的作用,但是无法多继承


2、克隆

当拷贝对象时,原始变量和拷贝变量引用同一个对象,两者互相影响。

clone是protected方法,不能在外部调用。Object类实现了clone方法,能对各个域进行相应的拷贝。如果所有域属于数值或基本类型,这个拷贝没有问题。

基本数值

Double a=(double) 1;Double b=a;//a==bDouble c=new Double(a);//a!=ca=new Double(2);//a!=bc=(double)3;

结果a=2、b=1、c=3,同样的Integer也是,注意String、Double这些对象里的数值、字符数组都是fianl形式


Manager类里有一个类变量A,A有一个int域m,

Manager a=new Manager(1);Manager c=a.cloneManager();System.out.println(a==c);//a!=c,但是两者的类变量指向相同的引用a.set(3);a.pring();//输出A.m=3c.pring();//3
新老两个对象的域引用同一个子对象,共享信息。

默认的克隆是浅拷贝,没有克隆包含在对象中的内部对象(只是简单地拷贝了引用),不可变的基本类型,数值类型、String等不受影响


一般情况下,接口里定义了需要实现的方法,但是Cloneable接口并没有指定clone方法,它是空的,这个方法是从Object继承而来,接口在这里仅作为一个标记(Cloneable接口是Java提供的几个标记接口之一,比较接口没有方法,使用它的唯一目的是检查if(obj instanceof Cloneable),自己编程的时候,不要采取这样的方法)

《Java核心技术》建议,即使默认的浅拷贝能满足需求,也建议实现Cloneable接口并将clone重定义为public,并调用super.clone();

public Manager clone() throws CloneNotSupportedException{return (Manager) super.clone();}
这样声明为public,也能在外部直接调用clone了。老版本Java中返回的是Object,现在可以指定具体的类型

为了能够实现深度克隆,必须克隆所有的实例域

public Manager clone() throws CloneNotSupportedException{Manager m=(Manager) super.clone();m.date=(Date) this.date.clone();return m;}
之所以clone声明为protected还可以防止子类的滥用,比如Employee声明了public的clone,则外部可以用来克隆它的子类Manager,但是Employee的clone并不对Manager的可变对象奏效。

需要注意的是,虽然clone是显示声明在基类中,但是Manager中的成员还是得以拷贝,不过是基本类型正常,而可变类类型还是指向同一处(浅拷贝)
因此,到底怎么使用clone,声明为protected还是public要根据实际考虑


3、回调

回调是一种常见的设计模式,可以用来指定某个特定事件发生时应该采取的动作,比如Java的Timer到达固定的时间间隔采取一定的行为。

  • 对普通函数的调用:调用程序发出对普通函数的调用后,程序执行立即转向被调用函数执行,直到被调用函数执行完毕后,再返回调用程序继续执行。从发出调用的程序的角度看,这个过程为“调用-->等待被调用函数执行完毕-->继续执行”

  • 对回调函数调用:调用程序发出对回调函数的调用后,不等函数执行完毕,立即返回并继续执行。这样,调用程序执和被调用函数同时在执行。当被调函数执行完毕后,被调函数会反过来调用某个事先指定函数,以通知调用程序:函数调用结束。这个过程称为回调(Callback),这正是回调函数名称的由来。



4、内部类

为什么使用内部类:

1:内部类方法可以访问外部类的所有数据,包括私有数据

2:内部类可以对同一包中的其他类隐藏起来

3:想要定义一个回调函数,但是不想编写大量代码是,使用匿名内部类比较快

内部类有一个隐式引用,指向外部类对象,也可以显示调用:OutClass.this。。。。

。。暂时不想看内部类了。。。跳过


5、代理

代理可以在运行时创建一个实现了一组给定接口的新类,该功能只在编译时无法确定需要实现那些接口时才用得到。

代理类可以在运行时创建全新的类(接口是不能实例化的),它具有:指定接口的所有方法+Object的所有方法。但是不能在运行时定义这些方法的新代码,而是提供一个调用处理器。无论何时调用代理对象的方法,调用处理器的invoke方法都会被调用,并向其传递Method对象和原始的调用参数,

要想创建一个代理对象,需要使用Proxy类的newProxyInstance方法,有三个参数(1、类加载器,可以用null表示默认的    2、一个lass对象数组  3、一个调用处理器)


使用代理的原因可能是:

路由对远程服务器的方法调用

在程序运行期间,将用户接口事件和动作关联起来

为调试,跟踪方法调用


代理类是程序运行中创建的,一旦被创建,就变成了常规类,和虚拟机中其他任何类没区别

所有的代理类都扩展于proxy,一个代理类只有一个实例域——调用处理器,定义在Proxy中,为了履行代理对象的职责,所有需要的附加数据都存储在调用处理器中。

所有的代理类都覆盖了Object类中的:toString、equals、hashCode,和所有的代理方法一样,这些方法仅调用了调用处理器的invoke,Object中其他的clone、getClass没有被重新定义

代理类没有定义名字,有的虚拟机中将生成一份以$Proxy开头的类名

对于特定的类加载器和预设的一组接口来说,只能有一个代理类。如果使用同样的调用两次newProxyInstance,只能获得同一个类的两个对象(也可以直接获取这个类Class)


代理类一定是public+final

如果代理类实现的所有接口都是public,则代理类不属于特定的包,否则所有非public接口必须属于同一个包,同时代理类也属于这个包。






原创粉丝点击