java 中反射机制和内省机制的区别是什么?
1.什么是反射
反射就是在运行状态把 Java ?类中的各种成分映射成相应相应的 Java ?类,可以动态得获取所有的属性以及动态调用任意一个方法。
1).一段java代码在程序的运行期间会经历三个阶段:source-->class-->runtime
2).Class对象在java中用一个Class对象来表示一个java类的class阶Class对象封装了一个java类定义的成员变量、成员方法、构造方法、包名、类名等。
2.反射怎么用
1).获得java类的各个组成部分,首先需要获得代表java类的Class对象?获得Class对象有以下三种方式:
Class.forname(className) 用于做类加载
obj.getClass() ?用于获得对象的类型
类名.class 用于获得指定的类型,传参用
2).反射类的构造方法,获得实例
Class clazz = 类名.class;
Constuctor con = clazz.getConstructor(new Class[]{paramClazz1,paramClazz2,.....});
con.newInstance(params....);
内省
什么是内省
通过反射的方式操作JavaBean的属性,jdk提供了PropertyDescription类来操作访问JavaBean的属性,Beantils工具基于此来实现。
2.内省怎么用
1).操作一个属性
Object obj = new Object();
PropertyDescriptor pd = new PropertyDescriptor(propertyName,Class);
声明属性描述对象,一次只可描述一个属性
Method m = pd.getWriterMethod();//获取setter方法
m.invoke(obj,value);
Method m = pd.getReaderMethod();//获取getter方法
Object value = m.invoke(obj);
反射机制并没有什么神奇之处。反射与RTTI的本质区别只是检查一个类的.class文件的时机不同:
反射:.class 文件是在编译时不可获得的,所以在运行时打开和检查未知类的.class文件从而变已知。
RTTI: ?.class 文件是在编译时打开和检查。
官方文档
返回与带有给定字符串名的类或接口相关联的 Class 对象。调用此方法等效于:
Class.forName(className, true, currentLoader)
其中 currentLoader 表示此类的定义类加载器。
例如,以下代码片段返回 java.lang.Thread 类的运行时 Class 描述符。
Class t = Class.forName("java.lang.Thread")
调用 forName("X") 将导致名为 X 的类被初始化。
参数:
className - 所需类的完全限定名。
返回:
具有指定名的类的 Class 对象。
通俗的说就是:获得字符串参数中指定的类,并初始化该类
类装载
类装载就是把一个类或是一个接口的字节码文件,通过解析该字节码来构建代表这个类或是这个接口的实例的过程。 这个字节码文件来源可能是压缩包、网络、运行时编译出的或者自动生成的class文件,jvm spec没有规定必须从什么地方加载。
类装载的两种方式:
1.Class c1 = Class.forName ("java.lang.String");
2.ClassLoader cl = new ClassLoader();
Class cl.loadClass( String name, boolean resolve );
两种装载方法的区别:
不同的类装载器
Class.forName是从指定的classloader中装载类,如果没有指定,也就是一个参数的时候,是从装载当前对象实例所在的classloader中装载类。
而ClassLoader的实例调用loadclass方法,是指从当前ClassLoader实例中调用类,而这个实例与装载当前所在类实例的Classloader也许不是同一个.
说白了就是他们实现装载的时候,使用的类装载器的指定是不同的。那为什么使用不同的ClassLoader来装载类呢?
其实使用多个classloader加载类的情况非常常见,比如说我们的app server都是这样的. 在Web与EJB间, 他们的classLoader就是不同的,这样做的目的就是为了避免两者间类装载的相互干扰。
是否实例化类
Class的装载分了三个阶段,loading(装载),linking(连接)和initializing(实例化)分别定义在The Java Language Specification的12.2,12.3和12.4。
Class.forName(className)实际上是调用Class.forName(className, true, this.getClass().getClassLoader())。注意第二个参数,是指Class被loading后是不是必须被初始化。
ClassLoader.loadClass(className)实际上调用的是ClassLoader.loadClass(name, false),第二个参数指出Class是否被link。
区别就出来了。Class.forName(className)装载的class已经被实例化,而ClassLoader.loadClass(className)装载的class还没有被link,所以就更谈不上实例化了。
简单说,就是通过类名反射出类的对象 。
一般情况下,这两个方法效果一样,都能装载Class。但如果程序需要Class被实例化,就必须用Class.forName(name)了。
例如,在JDBC中加载mysql的驱动类时(关于注册jdbc驱动请参看另外一篇文章,jdbc注册驱动的三种方式),Class.forName("com.mysql.jdbc.Driver"),如果换成getClass().getClassLoader().loadClass("com.mysql.jdbc.Driver"),就不行,因为它只是向jvm装载了Driver并没有实例化,就不能执行响应的操作。
打开com.mysql.jdbc.Driver的源代码看看,
//
// Register ourselves with the DriverManager
//
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
可以看到,Driver在static块中会注册自己到java.sql.DriverManager。而static块就是在Class的初始化中被执行。所以这个地方就只能用Class.forName(className)。
鹏仔微信 15129739599 鹏仔QQ344225443 鹏仔前端 pjxi.com 共享博客 sharedbk.com
图片声明:本站部分配图来自网络。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!