0基础学java有多难模式中提到的“委让”,什么是委让

走进java_类加载器 与 双亲委派模式 - 麻-雀的博客 - CSDN博客
走进java_类加载器 与 双亲委派模式
http://blog.csdn.net/itermeng/article/details/
虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到虚拟机外部实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码块被称为“类加载器”。
类加载器可以说是Java语言的一项创新,也是Java语言流行的重要原因之一,它最初是为了满足 Applet 的需求而开发出来的,但类加载器却在类层次划分、OSGi、热部署、代码加密等领域大放异彩,成为Java技术体系的一块重要基石。
本博文接下来要介绍的知识点:
类加载器概念、作用启动、扩展、应用程序类加载器的工作原理双亲委派模式概念、工作原理、逻辑自定义类加载器源码探究双亲委派模式
一. 类与类加载器
1. 类加载器作用
类加载器虽然只用于实现类的加载动作,但它在Java程序起到的作用却远大于类加载阶段。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。通俗而言:比较两个类是否“相等”,只有在这两个类时由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。
2. 类加载器 与 instanceof 关键字
上文所说的“相等”,包括代表类的,对象的 equals() 方法、isAssignableFrom()方法、isInstance() 方法的返回结果,也包括使用instanceof 关键字做对象所属关系判定等情况。
如果没有注意到类加载器的影响,在某些情况下可能会产生具有迷惑性的结果,以下代码演示了不同的类加载器对 instanceof 关键字运算结果的影响:
* 类加载器与instanceof关键字演示
public class ClassLoaderTest {
public static void main(String[] args) throws Exception {
ClassLoader myLoader = new ClassLoader() {
public Class&?& loadClass(String name) throws ClassNotFoundException {
String fileName = name.substring(name.lastIndexOf(&.&) + 1) + &.class&;
InputStream is = getClass().getResourceAsStream(fileName);
if (is == null) {
return super.loadClass(name);
byte[] b = new byte[is.available()];
is.read(b);
return defineClass(name, b, 0, b.length);
} catch (IOException e) {
throw new ClassNotFoundException(name);
Object obj = myLoader.loadClass(&org.fenixsoft.classloading.ClassLoaderTest&).newInstance();
System.out.println(obj.getClass());
System.out.println(obj instanceof org.fenixsoft.classloading.ClassLoaderTest);
}123456789101112131415161718192021222324252627282930313233123456789101112131415161718192021222324252627282930313233
运行结果:
以上代码中构造了一个简单的类加载器,它可以加载与自己在同一路径下的Class文件。使用这个类加载器去加载一个名为“org.fenixsoft.classloading.ClassLoaderTest”的类,并实例化这个类的对象。
从输出结果的第一行可以看出,此对象确实是类“org.fenixsoft.classloading.ClassLoaderTest”实例化出的对象,但从第二句看出,此对象与类“org.fenixsoft.classloading.ClassLoaderTest”做所属类型检查的时候却返回了false,这是因为虚拟机中存在了两个ClassLoaderTest
类,一个是由系统应用程序类加载器加载的,另外一个是由我们自定义的类加载器加载的,虽然都来自同一个Class文件,但依然是两个独立的类,做对象所属类型检查时结果自然返回false。
二. 双亲委派模式
1. 虚拟机角度中的类加载器
从虚拟机的角度来讲,只存在两种不同的类加载器:
启动类加载器(Bootstrap ClassLoader):此类加载器使用C++实现,是虚拟机自身的一部分。所有其他类加载器:由Java语言实现,独立于虚拟机外部,并且全都继承自抽象类 java.lang.ClassLoader。
2. Java开发人员角度中的类加载器
从Java开发人员的角度来看,类加载器还可以划分得更细致一些,绝大部分Java程序都会使用到以下3种系统提供的类加载器:
(1) 启动类加载器(Bootstrap ClassLoader)
此类加载器负责将存放在 &JAVA_HOME&\lib 目录中的,或者被 -Xbootclasspath 参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如 rt.jar,名字不符合的类库即使放在lib 目录中也不会被加载)类库加载到虚拟机内存中。
启动类加载器无法被 Java 程序直接引用,用户在编写自定义类加载器时,如果需要把加载请求委派给引导类加载器,直接使用null代替即可。如下代码java.lang.ClassLoader.getClassLoader() 方法的代码片段:
【ClassLoader.getClassLoader() 方法的代码片段】
* Returns the class loader for the class.
Some implementations may use null to represent the bootstrap class loader. This method will return null in such implementations if this class was loaded by the bootstrap class loader.
public ClassLoader getClassLoader() {
ClassLoader cl = getClassLoader0();
if (cl == null)
return null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
ClassLoader cc1 = ClassLoader.getCallerClassLoader();
if(cc1 != null && cc1 != c1 && !c1.isAncestor(cc1)){
sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
}12345678910111213141516171234567891011121314151617
(2)扩展类加载器(Extension ClassLoader)
是由ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。它负责将Java_Home /lib/ext或者由系统变量java.ext.dir指定位置中的类库加载到内存中,开发者可以直接使用标准扩展类加载器。如下图示例:
(3)应用程序类加载器(Application ClassLoader)
是由 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,因此一般称为系统类加载器。
它负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
3. 双亲委派模型
我们的应用程序都是由这3 种类加载器互相配合进行加载的,如果有必要,还可以加入自己定义的类加载器。这些类加载器之间的关系如下图:
上图展示的类加载器之间的层次关系,称为类加载器的双亲委派模型(Parents Delegation Model)。该模型要求除了顶层的启动类加载器外,其余的类加载器都应该有自己的父类加载器,而这种父子关系一般通过组合(Composition)关系来实现,而不是通过继承(Inheritance)。
类加载器的双亲委派模型在JDK 1.2期间被引入并被广泛应用于之后几乎所有的Java程序中,但它并不是一个强制性的约束模型,而是Java设计者推荐给开发者的一种类加载器实现方式。
(2)工作过程
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载,而是把这个请求委派给父类加载器,每一个层次的加载器都是如此,依次递归,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成此加载请求(它搜索范围中没有找到所需类)时,子加载器才会尝试自己加载。
(3)模式优点
使用双亲委派模型来组织类加载器之间的关系,好处在于Java类随着它的类加载器一起具备了一种带有优先级的层次关系。
例如类java.lang.Object,它存在在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。
相反,如果没有双亲委派模型而是由各个类加载器自行加载的话,用户编写了一个java.lang.Object的类,并放在程序的ClassPath中,那系统中将会出现多个不同的Object类,程序将混乱。因此,如果开发者尝试编写一个与rt.jar类库中已有类重名的Java类,将会发现可以正常编译,但是永远无法被加载运行。
(4)双亲委派模型的系统实现
双亲委派模型对于保证Java程序的稳定运作很重要,但它的实现却很简单,实现集中在java.lang.ClassLoader的loadClass()
方法中,在其方法中,主要判断逻辑如下:先检查是否已经被加载过,
若没有被加载过,则接着判断父加载器是否为空。
若不为空,则调用父类加载器的loadClass()方法。若父加载器为空,则默认使用启动类加载器作为父加载器。
如果父加载失败,则抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。
【双亲委派模型的实现】
protected synchronized Class&?& loadClass(String name,boolean resolve)throws ClassNotFoundException{
Class c = findLoadedClass(name);
if(c == null){
if(parent != null){
c = parent.loadClass(name,false);
c = findBootstrapClassOrNull(name);
}catch(ClassNotFoundException e){
if(c == null){
c = findClass(name);
if(resolve){
resolveClass(c);
}123456789101112131415161718192021222324123456789101112131415161718192021222324
(5)注意(findClass方法)
在查看学习以上ClassLoader的实现后,注意一个地方,即“如果父加载失败,则抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载”这一步逻辑,进一步查看findClass()方法:
protected Class&?& findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
没错!此方法没有具体实现,只是抛了一个异常,而且访问权限是protected。这充分证明了:这个方法就是给开发者重写用的,即自定义类加载器时需实现此方法!
4. 破坏双亲委派模型
双亲委派模型并不是一个强制性的约束模型,而是Java设计者推荐给开发者的类加载器的实现方式。大多数的类加载器都遵循这个模型,但是JDK中也有3次较大规模破坏双亲模型的情况,以下只着重介绍第一次“被破坏”,与后续的讲解有关。
双亲委派模型的第一次“被破坏”发生在此模型出现之前——JDK1.2发布前,此模型在JDK 1.2之后才引入,而类加载器和抽象类java.lang.ClassLoader 添加了一个新的protected限定的findClass()方法,而在此之前,用户去继承 java.lang.ClassLoader 的唯一目的是重写loadClass()
方法,因为虚拟机在进行类加载的时候会调用加载器的私有方法loadClassInternal() ,此方法唯一逻辑就是去调用自己的loadClass() 方法。
☆☆☆☆☆
在上一点中已经介绍过loadClass() 方法的代码,而双亲委派模型的具体逻辑也在此方法中,在JDK 1.2之后已不提倡用户再去覆盖loadClass() 方法,而应当把自己的类加载逻辑写到findClass() 方法中,在loadClass() 方法逻辑里:如果父类加载失败,则会调用自己的findClass()
方法来完成加载,这样可以保证用户自定义的类加载器时符合双亲委派规则的!
三. 实例验证双亲委派模式
在了解了以上多种类加载器和双亲委派模式的理论实现后,对这个“双亲委派模式”运作,自我感觉还是有点迷糊,所以接下来自定义一个类加载器来亲自验证“双亲委派模式”的运行实现,从实践角度出发来一探究竟。
在了解完以上双亲委派模型、破坏双亲委派模型后,我们得知自定义类加载器有以下两种方式:
采用双亲委派模型:只需要重写ClassLoader的findClass()方法即可
破坏双亲委派模型:重写ClassLoader的整个loadClass()方法(因为双亲委派模型的逻辑主要实现就在此方法中,若我们重写即可破坏掉。)
不过此次实践就是为了来探究验证双亲委派模型,多以我们当然是采取第一种方法来自定义类加载器。
1. 自定义类加载器
首先第一步我们需要自定义一个简单实现的类加载器,通过第二大点最后的讲解后对自定义类加载器的过程稍有了解,构建重点:自定义的MyClassLoader继承自java.lang.ClassLoader,就像上面说的,只需实现findClass()方法即可。
注意:此类里面主要是一些IO和NIO操作,其中defineClass方法可以把二进制流字节组成的文件转换为一个java.lang.Class,只要二进制字节流的内容符合Class文件规范即可。
public class MyClassLoader extends ClassLoader{
public MyClassLoader(){
public MyClassLoader(ClassLoader parent){
super(parent);
public Class&?& findClass(String name) throws ClassNotFoundException {
System.out.println(&Use myclassloader findClass method.&);
String fileName = name.substring(name.lastIndexOf(&.&)+1)+&.class&;
byte[] bytes = loadClassData(&E:\\test_eclipse\\JvmProject\\&+fileName);
return defineClass(name, bytes, 0, bytes.length);
public byte[] loadClassData(String name) {
FileInputStream fileInput = null;
ByteArrayOutputStream bytesOutput = null;
fileInput = new FileInputStream(new File(name));
bytesOutput = new ByteArrayOutputStream();
int b = 0;
while ((b = fileInput.read()) != -1) {
bytesOutput.write(b);
return bytesOutput.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
if(fileInput != null)
fileInput.close();
} catch (IOException e) {
e.printStackTrace();
return null;
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849501234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
注意:此时是使用MyClassLoader 自定义加载器来加载 EasyTest类(空实现,只是一个空壳),需要将该类编译后生成的EasyTest.class文件放到E:\test_eclipse\JvmProject\ 路径中,而测试的main 方法如下:
public static void main(String[] args){
MyClassLoader myClassLoader = new MyClassLoader();
Class&? extends Object& testClass = myClassLoader.loadClass(&org.fenixsoft.classloading.EasyTest&);
Object obj = testClass.newInstance();
System.out.println(obj.getClass().getClassLoader().toString());
} catch (Exception e) {
e.printStackTrace();
1234567891011121312345678910111213
输出结果:
查看上图红框显示结果可得,虽然程序的确使用自定义类加载器加载的,可是显示并非是 MyClassLoader而是应用程序类加载器 AppClassLoader 加载的,而这种结果的造成原因正是因为双亲委派模式。后面通过实例来探究双亲委派模式的源码实现。
2. 源码探究
回顾一下在讲解双亲委派模式中的相关知识点及层次图,启动类加载器只是JVM的一个类加载工具,处于层次图的最上层,严格来说它并无遵守此模式,所以我们从它的下层 ——扩展类加载器和应用程序类加载器开始分析。
查看其输出结果可知,其中涉及到了 sun.misc.Launcher类,查看此类结构图:
(1)sun.misc.Launcher类构造方法
点进去 sun.misc.Launcher类进行查看,你会发现在它的构造方法中创建了扩展类加载器 ExtClassLoader的实例,并用该实例创建应用程序类加载器 AppClassLoader的实例:
public Launcher() {
extcl = ExtClassLoader.getExtClassLoader();
} catch (IOException e) {
throw new InternalError(&Could not create extension class loader&);
loader = AppClassLoader.getAppClassLoader(extcl);
} catch (IOException e) {
throw new InternalError(&Could not create application class loader&);
}12345678910111213141516171234567891011121314151617
(2)扩展类加载器的实例化
由上可知,在Launcher类构造方法中先实例化了扩展类加载器,查看其实现过程:
static class ExtClassLoader extends URLClassLoader {
public static ExtClassLoader getExtClassLoader() throws IOException{
final File[] dirs = getExtDirs();
return new ExtClassLoader(dirs);
private static File[] getExtDirs() {
String s = System.getProperty(&java.ext.dirs&);
public ExtClassLoader(File[] dirs) throws IOException {
super(getExtURLs(dirs), null, factory);
this.dirs =
12345678910111213141516171819201234567891011121314151617181920
注意:查看ExtClassLoader 的构造方法中,调用了父类构造方法,其中传入的第二个参数为null,代表扩展类加载器没有上级类加载器!
(3)应用程序类加载器的实例化
紧接着来看应用程序类加载器的实例化过程:
static class AppClassLoader extends URLClassLoader {
public static ClassLoader getAppClassLoader(final ClassLoader extcl)
throws IOException{
final String s = System.getProperty(&java.class.path&);
final File[] path = (s == null) ? new File[0] : getClassPath(s);
return new AppClassLoader(urls, extcl);
AppClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent, factory);
12345678910111213141516171234567891011121314151617
注意:查看AppClassLoader
的构造方法中,调用了父类构造方法,其中传入的第二个参数为parent,也就是扩展类加载器extcl,直接从代码的角度证明
扩展类加载器是应用程序加载器的上级!
扩展类加载器是应用程序加载器的上级(已证明)
(4)ClassLoader的构造方法
在我们创建自定义类加载器时,继承了ClassLoader类,所以程序在运行时会先调用父类——ClassLoader的构造函数,来查看其实现:
private ClassL
private static ClassL
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
private ClassLoader(Void unused, ClassLoader parent) {
this.parent =
public static ClassLoader getSystemClassLoader() {
initSystemClassLoader();
private static synchronized void initSystemClassLoader() {
if (!sclSet) {
if (scl != null)
throw new IllegalStateException(&recursive invocation&);
sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
if (l != null) {
Throwable oops = null;
scl = l.getClassLoader();
sclSet = true;
123456789101112131415161718192021222324252627282930313233123456789101112131415161718192021222324252627282930313233
由以上可证明一个级别关系:
应用程序类加载器是自定义类加载器的上级。
我们在之前介绍过双亲委派模式的工作原理,通过前面一系列的分析后,再次叙述一遍,感受更深:如果一个类加载器收到了加载类的请求,它首先不会自己去尝试加载这个类,而是把这个请求委托给上级类加载器去完成,每一个层次的类加载器都是如此,所有加载器的加载请求最终都应该传送至最顶层的类加载器中(扩展类加载器),只有当上级类加载器反馈自己无法完成这个加载请求(它的类加载范围中没有找到所需的类)时,下级类加载器才会去尝试自己加载这个类。
由以上一系列源码探究可窥得这双亲委派模式的工作原理,并且清楚了为何最终加载 EasyTest类的是应用程序类加载器而并非是我们自定义的类加载器。
3. 使用自定义类加载器 加载
如今以上的道理算是理解了,可现在偏偏需要使用自定义的类加载器加载,应该如何修改呢?
在测试main() 方法中创建自定义类加载器的代码:
MyClassLoader myClassLoader = new MyClassLoader();
MyClassLoader myClassLoader = new MyClassLoader(ClassLoader.getSystemClassLoader().getParent())11
即把自定义ClassLoader的父加载器设置为扩展类加载器,这样父加载器加载不到EasyTest.class文件,就交由子加载器MyClassLoader来加载了(别忘了在自定义类加载器中要写对应的构造方法)。
运行结果如下:
4. .class和getClass()
这两者看起来类似,但其实有很大区别,如下:
.class用于类名,getClass()是一个final native的方法,因此用于类实例。.class在编译期间就确定了一个类的java.lang.Class对象,但是getClass()方法在运行期间确定一个类实例的java.lang.Class对象
5. ClassLoader.getResourceAsStream(String name)
不知道细心的朋友有没有注意到此方法,在本博文的第一个例子中出现过。在我们自定义的类加载器中所占篇幅最大的就是一个loadClassData 方法,将文件数据转换为字节数组,但是还有第二种方法,就是采用系统提供的ClassLoader.getResourceAsStream(String name) 方法,根据此方法获取到数据输入流,通过此输入流获取字节数组,最终传入defineClass 方法即可,代码如下:
【自定义类加载器中的 findClass方法】
public Class&?& findClass(String name) throws ClassNotFoundException {
String fileName = name.substring(name.lastIndexOf(&.&) + 1) + &.class&;
InputStream is = getClass().getResourceAsStream(fileName);
if (is == null) {
return super.loadClass(name);
byte[] b = new byte[is.available()];
is.read(b);
return defineClass(name, b, 0, b.length);
} catch (IOException e) {
throw new ClassNotFoundException(name);
1234567891011121314151612345678910111213141516
此方法是用来读入指定的资源的输入流,并将该输入流返回给用户用的,资源可以是图像、声音、.properties文件等,资源名称是以”/”分隔的标识资源名称的路径名称。有兴趣可查看其源码,实现也很简单。
本篇博文的内容并不少,首先第一点初步介绍了类加载器的概念、作用,用一个简单实现的类加载器来证明两个类之间的“相等”比较。接着在第二点中详细介绍了三种不同的类加载器,并学习了双亲委派模式,将启动、扩展、应用程序类加载器的层次归纳到双亲委派模型中,理解了此模型的优点、工作原理、源码逻辑等。
如果说以上两点比较偏理论的话,那么在第三点中采用自定义类加载器实例来探究双亲委派模式的工作原理,从源码的角度来分析ClassLoader的实现及类加载情况,此部分尤为重要。
相关文章推荐深入java虚拟机中类装载器解释中 为什么是双亲委派模式 而不叫单亲委派模式呢?类装载器中提到_百度知道
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。
深入java虚拟机中类装载器解释中 为什么是双亲委派模式 而不叫单亲委派模式呢?类装载器中提到
命名空间中的每一个命名都关联到方法区中的一个类型,既然方法区中存放的是类型
,为何不叫类型区而叫方法区,有什么特殊的用意吗?求高手指点小弟一二
我有更好的答案
这命名空间在这里指的是所有 类 的命名空间。只所以叫方法区我想不仅仅是因为存储了类型信息,而是为索引或者其它必要的操作提供了很多方法,而这些方法对开发人员来说几乎接触不到罢了,开发人员也没有办法去强行指定类实例放在哪个命名空间中。不需要关心,因为不需要关心具体的内存资源释放,双亲单亲仅仅是个名称而已。方法区中存储着的 类 型信息,透明的,不同于类实例的命名空间深入java虚拟机中类装载器解释中 为什么是双亲委派模式 而不叫单亲委派模式呢?这就是名称问题而已,不管是继承自一个类还是实现多个接口,本质都要用到invoke指令
采纳率:27%
为您推荐:
其他类似问题
java虚拟机的相关知识
换一换
回答问题,赢新手礼包java几种模式_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
java几种模式
阅读已结束,下载文档到电脑
想免费下载更多文档?
定制HR最喜欢的简历
下载文档到电脑,方便使用
还剩4页未读,继续阅读
定制HR最喜欢的简历
你可能喜欢         
您现在的位置:&&>&&>&&>&&>&&>&正文
Groovy探索之Delegate模式
来源:()  【】 
Delegate即是委派的意思。在Java语言中,很少提到委派或者委派模式,即使用到了也不说,这是因为Java语言的特点,实现起委派来相当的繁琐,而且只能适用于一些简单的场合。&& 但现实的需求是,由于敏捷编程的兴起,重构技术也得到了大家的广泛使用;要使用重构,其中的一个重要原则是:使用组合代替聚合,这样才能方便重构。使用组合,最终是要使用到委派技术了。我们经常有这样的一些需求,我们只想继承一个类的部分功能,而另外一部分功能是我们不想要、甚至是对我们有害的功能,这时候,我们只能使用委派技术,而不能使用继承了。&& 基于上面的一些分析,我们知道,继承和委派技术都能扩展一个类,使它拥有另外一个类的部分或者全部功能。但委派技术更为灵活,它可以使得一个类可以全部或部分的扩展得到另一个类的功能,而继承只能是全盘接收。&& 说了这么多,您可能要说了,我想看一些例子,或者你举出一些例子来证明你的观点啊。这正是下面要做的事。&& 比方说,有下面的一个简单类:&& class Foo {&&& deftest()&&& {&&&& println‘have a test‘&&& }&&& def foo()&&& {&&&& println‘foo...‘&&& }&& }&& 现在有一个新的类要实现它的全部功能,即“test”和“foo”方法。当然,我们首先想到的是继承:&& class FooSon extends Foo{&&& def testAgain()&&& {&&&& println‘test it again‘&&& }&& }&& 这样,我们就可以在FooSon中使用Foo的功能了,比如&&& def foo = new FooSon()&&& foo.test()&&& foo.foo()&&& foo.testAgain()&& 它的运行结果为:&& have a test&& foo...&& test it again&& 这样的继承无疑是完美的,子类不用写太多代码,就完全实现了父类的功能。但它最明显的缺点是,如果我有这样的需求,我只想在子类中要Foo类的“test”方法,不想要“foo”方法,在这里继承能办到吗?&& 如果您的答案是否定的,请您跟着我往下看。&& 下面,我将使用委派技术来产生一个新类,同样继承Foo类的所有方法:&& class Foo2 {&&& private delegate = new Foo()&&& deftest()&&& {&&&& this.delegate.test()&&& }&&& def foo()&&& {&&&& this.delegate.foo()&&& }&&& def testAgain()&&& {&&&& println‘test it again‘&&& }&& }&& 通过这个例子,您可以看到,所谓“委派”的意思是:一个类要实现的功能,比如Foo2类要实现的“test”和“foo”方法,但它并未真正去实现这些功能,而是把这些功能委派给另一个类去实现,如Foo2类的“test”和“foo”方法,实际上是交给Foo类去实现了。&& 可以测试一下上面的例子&&& def foo = new Foo2()&&& foo.test()&&& foo.foo()&& foo.testAgain()&& 运行结果为:&& have a test&& foo...&& test it again&& 当然,我还记得上面提出来的需求,新的类只想实现Foo类的“test”方法,不想要“foo”方法。使用委派技术就很好实现了:&& class Foo2 {&&& private delegate = new Foo()&&& deftest()&&& {&&&& this.delegate.test()&&& }&&& def testAgain()&&& {&&&& println‘test it again‘&&& }&& }&& 看了上面的例子,您可能会说,委派技术很简单,也很好理解,但就是实现起来太繁琐了,我要想在子类中实现父类的方法,非得在子类中把这些方法重写一遍不可,远远没有继承来得方便。是的,这就是在Java语言中很少提到委派技术的原因。&& 但是,我们应该知道的是,Groovy语言是基于Java语言的,这使得它必定有超过Java语言之处,而对委派技术的动态实现正是Groovy语言动态的一个重要方面。下面,我将举出一个例子来实现上面的功能的动态委派技术。&& class Foo3 {&&& private delegate = new Foo()&&& def invokeMethod(String name,Object args)&&& {&&&& this.delegate.invokeMethod(name,args)&&& }&&& def testAgain()&&& {&&&& println‘test it again‘&&& }&& }&& 是了,我们又通过“invokeMethod”方法来实现动态委派技术,我已经记不清“invokeMethod”方法实现了我们的多少功能。&& 一个“invokeMethod”方法,就不管你要实现父类的多少功能,统统通过这一个方法就能实现了。下面来测试一下&&& def foo = new Foo3()&&& foo.test()&&& foo.foo()&& foo.testAgain()&& 结果为:&& have a test&& foo...&& test it again&& 如果你在动态实现中,不想要父类的“foo”方法,也是可以实现的:&& class Foo3 {&&& private delegate = new Foo()&&& def invokeMethod(String name,Object args)&&& {&&&& if(name != ‘foo‘)&&&& {&&&&&& this.delegate.invokeMethod(name,args)&&&& }&&& }&&& def testAgain()&&& {&&&& println‘test it again‘&&& }&& } 1&&&
文章责编:gaoxiaoliang& 看了本文的网友还看了
?&&( 15:51:47)?&&( 14:16:20)?&&( 14:14:40)?&&( 14:13:00)?&&( 14:10:55)?&&( 14:02:35)
? ?   ? ?   ? ?   ? ?   ? ?
? ?   ? ?   ?
?   ? ?    ? ?   ? ?   ? ?   ? ?
? ?   ? ?
实用工具 |
| 大全 | 大全
     |
版权声明:如果网所转载内容不慎侵犯了您的权益,请与我们联系,我们将会及时处理。如转载本内容,请注明出处。
Copyright & 2004-
 网 All Rights Reserved 
中国科学院研究生院权威支持(北京) 电 话:010- 传 真:010-

我要回帖

更多关于 0基础学java有多难 的文章

 

随机推荐