Java反射机制(四)—番外篇,实例化方法深入

反射机制这几篇博客写下来发现涉及到Java类的加载机制,这部分的内容也比较独立的一部分,因此单另一篇来写。在JAVA中任何的类都是需要加载到JVM中才能运行的。之前Class Loader介绍了类的加载机制,那么这里要说的是不同加载方式之间的对比,好能对JAVA类的实例化过程有更深刻的体会。

new和Class.newInstance

我们说代码里出现new关键字意味着对于可能变动的代码,耦合过高了。遇到这种情况我们会用反射机制来去除new关键字,这在代理模式里我们见过了。实际上也就是用了Class.newInstance来代替。这说明这两种方式都可以得到相同的对象实例,但是它们之间存在区别,耦合度不同。

实际上在理解上我们可以认为,Class.newInstanc方式来实例化对象是对new关键字的拆分成两步了。因为,Class.newInstance的使用是有前提的,要保证类已经加载到JVM中,并且已经链接。看如下代码:

<span style="font-family:FangSong_GB2312;font-size:18px;"><span style="font-family:FangSong_GB2312;">public static void main(String[] arg) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
    	//从当前线程取得正在运行的加载器
    	ClassLoader cl=Thread.currentThread().getContextClassLoader();
    	cl.loadClass("com.zjj.ClassTest.Test");    //加载测试类到JVM
    	Class c2=cl.getClass();         //得到类的Class对象
    	c2.newInstance();               //实例化对象
    }
}</span></span>

这里不用Class.forName来得到Class对象是为了保证类被加载了但是没有被链接。 这段代码看着貌似没什么错,编译也没有问题,但是运行的时候就出错了。也就是说通过如上方法加载的类是没有被链接的,因此newInstance方法无法执行。

前面说理解上可以简单的认为是通过Class.Instance方式是new拆分的两步,但是事实上new要比Class.Instance做的多。Class.Instance方法只能访问无参数的构造函数,new则都可以访问。建立一个有两个构造函数的测试类,看客户端调用代码:

<span style="font-family:FangSong_GB2312;font-size:18px;"><span style="font-family:FangSong_GB2312;">public static void main(String[] arg) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
    	Class c=Class.forName("com.zjj.ClassTest.Test");
       c.newInstance();
    	new Test("ni");
    }
}</span></span>

输出结果为:

无参数的构造函数

带参数的构造函数

如果在newInstance中传入参数去调用带参数的构造函数的话是会报错的,无法通过编译。相对来说newInstance是弱类型,new是强类型。

Class.forName和classLoad.loadClass

讲这两个的区别之前我们先要了解,JVM会执行静态代码段,要记住一个概念,静态代码是和class绑定的,class装载成功就表示执行了静态代码了,以后也就不会再走这段静态代码了。 也就是说静态代码段是只会执行一次的,在类被加载的时候。另外我们还需要知道,类的加载过程分为装载、连接、初始化。还有就是,JVM遇到类请求时它会先检查内存中是否存在,如果不存在则去加载,存在则返回已存在的Class对象。

那么这两个方法的区别就在于执行的这三个过程不一样。forName有两个函数(多态),三个参数时forName(String className, boolean initialize, ClassLoader loader)第二个参数为True时则类会链接,会初始化。为False时,如果原来不存在则一定不会连接和初始化,如果原来存在被连接的Class对象,则返回该对象但是依然不会初始化。单参数时,默认initialize是为True的。

loadClass也是多态loadClass(String name)单参数时, resolve=false。如果该类已经被该类装载器所装载,那么,返回这个已经被装载的类型的Class的实例,否则,就用这个自定义的类装载器来装载这个class,这时不知道是否被连接。绝对不会被初始化!这时唯一可以保证的是,这个类被装载了。但是不知道这个类是不是被连接和初始化了。

loadClass(String name, boolean resolve)resolve=true时,则保证已经装载,而且已经连接了。 resolve=falses时,则仅仅是去装载这个类,不关心是否连接了,所以此时可能被连接了,也可能没有被连接。下面通过测试来验证以上说的内容,代码如下:

Test类:

<span style="font-family:FangSong_GB2312;font-size:18px;"><span style="font-family:FangSong_GB2312;">public class Test {
   static {
	   System.out.println("静态初始化");
   }
   public Test(){
	   System.out.println("无参数的构造函数");
   }
   public Test(String str){
	   System.out.println("带参数的构造函数");
   }
   {
	   System.out.println("非静态初始化");
   }
}</span></span>

测试一:客户端调用代码

<span style="font-family:FangSong_GB2312;font-size:18px;"><span style="font-family:FangSong_GB2312;">public static void main(String[] arg) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
    	Class c=Class.forName("com.zjj.ClassTest.Test");
     }
}</span></span>

输出结果为:静态初始化

说明:Class.forName时类执行了装载、连接、初始化三个步骤。

测试二:客户端代码改为

<span style="font-family:FangSong_GB2312;font-size:18px;"><span style="font-family:FangSong_GB2312;">public static void main(String[] arg) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
    	ClassLoader cl=Thread.currentThread().getContextClassLoader();
    	Class c=Class.forName("com.zjj.ClassTest.Test", false, cl);
     }
}</span></span>

输出结果为:initialize=true时输出,静态初始化。为false时没有输出

说明:为true时类执行了装载、连接、初始化三个步骤。为false时没有初始化,为知是不是连接。

测试三:客户端代码改为

<span style="font-family:FangSong_GB2312;font-size:18px;"><span style="font-family:FangSong_GB2312;">public static void main(String[] arg) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
    	ClassLoader cl=Thread.currentThread().getContextClassLoader();
    	Class c=Class.forName("com.zjj.ClassTest.Test", false, cl);
               c.newInstance();
     }
}</span></span>

输出结果为:

静态初始化

非静态初始化

无参数的构造函数

说明:为了保证JVM中不存在之前加载过的类,特地清理了JVM内存。但是输出结果不变,说明为false时执行了装载和链接,否则newInstance是无法执行的(前面说过了newInstance的执行条件)。但是资料说可能还存在不连接的情况!!有待考证。

测试四:客户端代码改为

<span style="font-family:FangSong_GB2312;font-size:18px;"><span style="font-family:FangSong_GB2312;">public static void main(String[] arg) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
               Class c=Class.forName("com.zjj.ClassTest.Test");
    	ClassLoader cl=Thread.currentThread().getContextClassLoader();
    	Class c=Class.forName("com.zjj.ClassTest.Test", true, cl);
             }
}</span></span>

输出结果为:静态初始化

说明:如果原来存在加载过的类,那么第二次执行加载请求时返回存在的。因为,静态初始化只执行了一次。

测试五:客户端代码改为

<span style="font-family:FangSong_GB2312;font-size:18px;"><span style="font-family:FangSong_GB2312;">public static void main(String[] arg) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
               //从当前线程取得正在运行的加载器
    	ClassLoader cl=Thread.currentThread().getContextClassLoader();
    	cl.loadClass("com.zjj.ClassTest.Test");    //加载测试类到JVM
    	Class c2=cl.loadClass("com.zjj.ClassTest.Test").getClass();         //得到类的Class对象
    	c2.newInstance();               //实例化对象
             }
}</span></span>

输出结果:报错

说明:此时loadClass方法加载到内存中的类是未连接的,当然不会初始化。因此也就没有“静态初始化”的输出。

测试六:不知道为什么没有发现代码中的ClassLoader存在两个参数的loadClass方法。

总结:至此方法对比结束,这篇博客主要是更细致的了解了JVM加载类的过程和不同方式之间的区别。其实际上只是封装的程度不一样,也就是方法的粒度的差别。当然,有一点内容还没有通过自己的测试得到验证,可能是我的方法不对或者是资料有问题。权且记下这个问题!下篇博客再见!

时间: 2024-08-26 02:55:53

Java反射机制(四)—番外篇,实例化方法深入的相关文章

java反射机制之Method invoke执行调用方法例子

昨天在群里跟大家讨论了下java反射调用可变参数的问题,这个问题起因是我们需要反射调用另一个部门提供的方法,我同事说java不能反射调用可变参数的方法,于是我写了个demo证明了他这个观点的错误.但是测试过程中,有一点我不明白,就是反射调用可变参数的方法时,为什么一定要保证传入的参数数组长度为1,在群里跟大家讨论了很多,没有得到确切的答案,参照网上大牛写的东西和我自己跟源码的过程,记录如下: 1.两个类,一个父类,一个子类 [java] view plain copy print? packag

java 反射机制获取类名、属性、方法、构造器和反射动态使用

被反射的类: @Table("tb_student") public class Student { @Fields(columnName="id",type="int",length=10) private int id; @Fields(columnName="studentName",type="varchar",length=10) private String studentName; @Fiel

java反射机制(一)—— 利用反射机制实例化对象

一.Java有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载.探知.使用编译期间完全未知的classes.换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体.或对其fields设值.或唤起其methods.(度娘文库是这么说的) 二.这篇文章主要介绍一下通过反射机制去实例化一个类的对象,然后调用其方法.本文主要介绍两种方式,第一种就是通过构造函数来实例化,第二种就是通过Cl

Java反射机制demo(四)—获取一个类的父类和实现的接口

Java反射机制demo(四)—获取一个类的父类和实现的接口 1,Java反射机制得到一个类的父类 使用Class类中的getSuperClass()方法能够得到一个类的父类 如果此 Class 表示 Object 类.一个接口.一个基本类型或 void,则返回 null.如果此对象表示一个数组类,则返回表示该 Object 类的 Class 对象. 测试代码: package com.aaron.reflect; public class Demo4 { public static void

Java反射机制剖析(四)-深度剖析动态代理原理及总结

动态代理类原理(示例代码参见java反射机制剖析(三)) a)  理解上面的动态代理示例流程 a)  理解上面的动态代理示例流程 b)  代理接口实现类源代码剖析 咱们一起来剖析一下代理实现类($Proxy0)的源代码和整个动态代理的流程. $Proxy0生成的代码如下: import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; impo

Java微信公众平台开发--番外篇,对GlobalConstants文件的补充

转自:http://www.cuiyongzhi.com/post/63.html 之前发过一个[微信开发]系列性的文章,也引来了不少朋友观看和点评交流,可能我在写文章时有所疏忽,对部分文件给出的不是很完全所以导致部分同学在有些地方做开发的时候遇到了一些阻力,收到这些朋友同学们的咨询反馈之后我也做了一些反思和总结,其中一部分同学说少了GlobalConstants这个文件(这个真心占的不少),还有一部分就是说源码的问题,所以今天特意抽了时间补充下这两点! (一)关于GlobalConstants

java 反射机制(我的第一篇博客)

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制. JAVA反射(放射)机制:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”.从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言.但是JAVA有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时

编程珠玑番外篇

1.Plan 9 的八卦 在 Windows 下喜欢用 FTP 的同学抱怨 Linux 下面没有如 LeapFTP 那样的方便的工具. 在苹果下面用惯了 Cyberduck 的同学可能也会抱怨 Linux 下面使用 FTP 和 SFTP 是一件麻烦的事情. 其实一点都不麻烦, 因为在 LINUX 系统上压根就不需要用 FTP. 为什么呢? 因为一行简单的配置之后, 你就可以像使用本机文件一样使用远程的任何文件. 无论是想编辑, 查看还是删除重命名, 都和本机文件一样的用. 这么神奇的功能到底如何

Java反射机制的原理及在Android下的简单应用

花了几天时间,研究了一下Java的反射机制.在这里总结一下这几天学习的成果,一来分享自己的学习过程和在学习中遇到的问题,二来是给像我一样不太了解Java反射机制的同学做一个简单的介绍.在文章后面会链接一个Android反射机制的应用程序. 一.反射的概念及在Java中的类反射 反射主要是指程序可以访问.检测和修改它本身状态或行为的一种能力.在计算机科学领域,反射是一类应用,它们能够自描述和自控制.这类应用通过某种机制来实现对自己行为的描述和检测,并能根据自身行为的状态和结果,调整或修改应用所描述

Reflect Java反射机制

// 参考:http://blog.csdn.net/stevenhu_223/article/details/9286121 最近发现好多框架底层的实现与Java的reflect和cglib有关,看过原理后找了几篇经典的文档,以供后来复习使用 前言:我们知道,类和类的成员变量及方法都是要求有权限控制的(public.protected.private):而当类中的信息封装为私有时,外部对该类中私有的信息是没有访问权限的,也就是说当该类里的内容信息均受private权限控制时,外部想要获取和处理