Java反射总结

原创
2013/11/27 07:21
阅读数 1.3K

Java 反射总结

类装载器工作机制 

类装载器就是寻找类的节码文件并构造出类在JVM 内部表示对象的组件。在Java 中, 
类装载器把一个类装入JVM 中,要经过以下步骤: 
1.装载:查找和导入Class 文件; 
通过一个类的全限定名来获取定义此类的二进制字节流.然后将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构.最后在Java堆中生成一个代表这个类的java.lang.class对像,作为方法区的数据入口. 

2.链接:执行校验、准备和解析步骤,其中解析步骤是可以选择的: 
a)校验:检查载入Class 文件数据的正确性; 
b)准备:给类的静态变量分配存储空间; 
c)解析:将符号引用转成直接引用; 

3.初始化:对类的静态变量、静态代码块执行初始化工作。 

类装载工作由ClassLoader 及其子类负责,ClassLoader 是一个重要的Java 运行时系统组件,它负责在运行时查找和装入Class 字节码文件。JVM 在运行时会产生三个 
ClassLoader: 
BootstrapClassLoader 
Extension ClassLoader(扩展类装载器) 
Application ClassLoader(系统类装载器)。 

其中,BootstrapClassLoader不是ClassLoader 的子类,它使用C++编写,因此我们在Java 中看不到它,BootstrapClassLoader负责装载JRE 的核心类库,如JRE 目标下的rt.jar、charsets.jar 等。 

Extension ClassLoader 和Application ClassLoader 都是ClassLoader 的子类。其中Extension ClassLoader 负责装载 JRE 扩展目录ext 中的JAR 类包;Application 负责装载Classpath 路径下的类包。 

getParent()
返回该类加载器的父类加载器。
loadClass(String name)
加载名称为 name 的类,返回的结果是 java.lang.Class 类的实例。
findClass(String name) 
查找名称为 name 的类,返回的结果是 java.lang.Class 类的实例。
findLoadedClass(String name) 
查找名称为 name 的已经被加载过的类,返回的结果是 java.lang.Class 类的实例。
defineClass(String name, byte[] b, int off, int len)
把字节数组 b 中的内容转换成 Java 类,返回的结果是 java.lang.Class 类的实例。这个方法被声明为 final 的。
resolveClass(Class<?> c)
链接指定的 Java 类。


public class ClassLoaderTest {  
public static void main(String[] args) {  
ClassLoader loader = Thread.currentThread().getContextClassLoader();  
System.out.println("current loader:"+loader);  
System.out.println("parent loader:"+loader.getParent());  
System.out.println("grandparent loader:"+loader.getParent(). getParent());  
}  
}



current loader:sun.misc.Launcher$AppClassLoader@131f71a  
parent loader:sun.misc.Launcher$ExtClassLoader@15601ea  
//①根装载器在Java中访问不到,所以返回null  
grandparent loader:null



通过以上的输出信息,我们知道当前的ClassLoader 是AppClassLoader,父ClassLoader是ExtClassLoader,祖父ClassLoader 是根类装载器,因为在Java 中无法获得它的句柄,所以仅返回null。 


JVM 装载类时使用“全盘负责委托机制”,“全盘负责”是指当一个ClassLoader 装载一个类的时,除非显式地使用另一个ClassLoader,该类所依赖及引用的类也由这个ClassLoader 载入;“委托机制”是指先委托父装载器寻找目标类,只有在找不到的情况下才从自己的类路径中查找并装载目标类。这一点是从安全角度考虑的,试想如果有人编写了一个恶意的基础(如java.lang.String)并装载到JVM 中将会引起多么可怕的后果。但是由于有了“全盘负责委托机制”,java.lang.String 永远是由根装载器来装载的,这样就避免了上述事件的发生。

获取当前线程的ClassLoader:

ClassLoader loader = Thread.currentThread().getContextClassLoader(); 
Class.getClassLoader() ;
实例化Class类对象的三种方式:
Class.forName("Reflect.Demo"); 
new Demo().getClass(); 
Demo.class;
Class.forName(className)实际上是调用Class.forName(className, true, this.getClass().getClassLoader())。注意第二个参数,是指Class被loading后是不是必须被初始化。

通过Class实例化其他类的对象
Class<?>  demo=Class.forName("Reflect.Person"); 
Constructor<?> cons[]=demo.getConstructors(); 
Object object = cons[0].newInstance(args);
通过Class实例化接口 
Class<?> intes[]=demo.getInterfaces();
取得其他类中的父类
Class<?> superClass=demo.getSuperclass();

调用其他类中的方法
demo = Class.forName("Reflect.Demo"); 
Method method=demo.getMethod("toString"); 
method.invoke(demo.newInstance()); 


Method[] methods = demo.getDeclaredMethods();
通过反射操作属性: 
Field field = demo.getDeclaredField("sex"); 
field.setAccessible(true); 
field.set(obj, "男"); 


Field[] fields = Demo.class.getDeclaredFields(); //类中任何可见性的属性不包括基类 
fields = Demo.class.getFields();  //只能获得public属性包括基类的
使用数组

Class string = Class.forName("java.lang.String");  
Object object= Array.newInstance(string, 10);  
Array.set(object, 5, "this is a test");  
 String s = (String) Array.get(arr, 5);

=============================应用==========================
JDK动态代理:
与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。


public class MyHandler implements InvocationHandler {   
    private Object target;   
       
    public Object bind(Object target) {   
        this.target = target;    
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),   
                target.getClass().getInterfaces(), this);   
    }    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {   
        System.out.println("事物开始");    
        Object result = method.invoke(target, args);   
        System.out.println("事物结束");   
        return result;   
    }     
   
}
Cglib动态代理 
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。 

public class MyCglib implements MethodInterceptor {   
    private Object target;   
    
    public Object getInstance(Object target) {   
        this.target = target;   
        Enhancer enhancer = new Enhancer();   
        enhancer.setSuperclass(this.target.getClass());   
        // 回调方法   
        enhancer.setCallback(this);   
        // 创建代理对象   
        return enhancer.create();   
    }   
    @Override   
    // 回调方法   
    public Object intercept(Object obj, Method method, Object[] args,   
            MethodProxy proxy) throws Throwable {   
        System.out.println("事物开始");   
        proxy.invokeSuper(obj, args);   
        System.out.println("事物结束");   
        return null;   
    }   
}




展开阅读全文
打赏
1
29 收藏
分享
加载中
ZooKeeper博主

引用来自“Cwind001”的评论

博主总结得不错,请问通过反射调用类的方法与直接调用在性能上是否有较大的差异呢

因为需要动态编译,所以会消耗较多的资源.所以使用时也要注意,
最好运行一次,可以多次使用. 比如Spring,Hibernate也大量使用了反射.
为了提高开发和维护上的效率,牺牲一点点是可以接受的.
2013/12/16 18:17
回复
举报
博主总结得不错,请问通过反射调用类的方法与直接调用在性能上是否有较大的差异呢
2013/12/16 17:27
回复
举报
更多评论
打赏
2 评论
29 收藏
1
分享
返回顶部
顶部