java reflection
2012-11-26 09:17 congdepeng 阅读(276) 评论(0) 收藏 举报Friday, 21 September, 2012 10:25:34 AM
1. 有4种方式可以得到Class对象:
- obj.getClass() 从对象获得
 - class literal <比如String.class> 从类上获得
 - Class.forName <> 根据整个字符串获得
 - Main.class.getClasses() 在取得内部类时使用(举例如下)
 
public class Main {
    public static void main(String[] args) {
        Class<?>[] classes = String.class.getClasses();
        System.out.println(classes.length);
        for (Class<?> aClass : classes) {
            System.out.println(aClass);
        }
    }
}
 注意如下的区别:  String.class返回和"".getClass()是不一样的。Class<String> stringClass1 = String.class;
Class<? extends String> aClass = "".getClass(); Class.forName("java.lang.String")又是不一样的。Class<?> aClass1 = Class.forName("java.lang.String"); 有时候可以这样转换:
Class<? extends String> c3 = Class.forName("java.lang.String").asSubclass(String.class);       // OK  另外Class对象本身的Class也是ClassClass<? extends Class> aClass = s.getClass();
System.out.println(aClass);
Class<? extends Class> aClass1 = aClass.getClass();
System.out.println(aClass1); 2. 使用反射API去操作类的Class对象
得到类实现的接口有如下2个方法:
 Class[] interfaces = s.getInterfaces(); // 得到原始接口
Type[] genericInterfaces = s.getGenericInterfaces(); // // 得到接口以及接口的类型参数,
  如图,一个直接返回Class,一个返回在包含泛型参数的时候返回Type,Type是ParameterizedType的接口,ParameterizedType包含了Class  同样,得到类的父类也有2个类似的方案:Class superclass = s.getSuperclass();
Type genericSuperclass = s.getGenericSuperclass();  类里面有如下内容:  - 属性Field   
 - 方法Method   
 - 构造器Constructor   
 - 内部类
 
分别对应如下方法public Constructor[] getConstructors()
public Field[] getFields()
public Method[] getMethods()
public Class[] getClasses() // 必须为public内部类才可以看见 如果想要得到非public的内部定义的类型,需要使用如下方法public Constructor[] getDeclaredConstructors()
public Field[] getDeclaredFields()
public Method[] getDeclaredMethods()
public Class[] getDeclaredClasses() 举个例子:如下代码,只有第2个循环才能打印出Fields信息import java.lang.reflect.Field;
public class Content {
    private int id;
    private String name;
    public static void main(String[] args) {
        Class<Content> c = Content.class;
        Field[] fields = c.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        Field[] declaredFields = c.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
    }
}  当然,你也可以根据字段名去查询                          public Field getField(String name)
public Field getDeclaredField(String       name)  
查询方法public Method getMethod(String name, Class... parameterTypes)
public Method getDeclaredMethod(String name, Class... parameterTypes) 查询构造器public Constructor<T> getConstructor(Class... parameterTypes)
public Constructor<T> getDeclaredConstructor(Class... parameterTypes) 一个完整的现实Class内容的代码如下:  import java.lang.reflect.*;
public class ClassContents {
    public static void main(String[] args) {
        try {
            Class<?> c = Class.forName("java.lang.String");
            System.out.println(c);
            printMembers(c.getDeclaredFields());
            printMembers(c.getDeclaredConstructors());
            printMembers(c.getDeclaredMethods());
            printClasses(c.getDeclaredClasses());
        } catch (ClassNotFoundException e) {
            System.out.println("unknown class: " + args[0]);
        }
    }
    private static void printClasses(Class<?>[] declaredClasses) {
        for (Class<?> declaredClass : declaredClasses) {
            System.out.print("=====");
            System.out.print(declaredClass.toString());
        }
    }
    private static void printMembers(Member[] mems) {
        for (Member m : mems) {
            if (m.getDeclaringClass() == Object.class)
                continue;
            String decl = m.toString();
            System.out.print("    ");
            System.out.println(decl);
        }
        System.out.println("-------------------------------------");
    }
}  类的类名在JVM里面有更简洁的定义,特别是对8个基本类型做了缩写: B byte
C char
D double
F float
I int
J long
Lclassname; class or interface
S short
Z boolean  int数组写成[I, Object数组写成[Ljava.lang.Object;
 在运行期还有如下查询方法可以使用   - public boolean isInstance(Object obj)   
 - public T cast(Object obj)   
 - public boolean isAssignableFrom(Class<?> type)
 
 public class runtime {
    public static void main(String[] args) {
        Class<String> s = String.class;
        boolean instance = s.isInstance("");
        System.out.println(instance);
        String cast = s.cast("new runtime()");
        System.out.println(cast);
        Class<Object> objectClass = Object.class;
        boolean assignableFrom = s.isAssignableFrom(objectClass);
        System.out.println(assignableFrom);      // false
        boolean assignableFrom1 = objectClass.isAssignableFrom(s);
        System.out.println(assignableFrom1); // true
    }
} 如下代码演示在getDeclared获得所有信息的情况下如何查看修饰符:
     Field[] declaredFields = String.class.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                boolean aPrivate = Modifier.isPrivate(declaredField.getModifiers());
                System.out.println(declaredField+" is private:  "+aPrivate);
            } 在为private的情况下,我们可以改变它的修饰符,使用如下方法: 
public void setAccessible(boolean flag)
Sets the accessible flag for this object to the indicated boolean value. A value of true means that the object should suppress language-level access control (and so will always be accessible); false means the object should enforce language-level access control. If you are not allowed to change the accessibility of an object a SecurityException is thrown.
public static void setAccessible(AccessibleObject[] array, boolean flag)
A convenience method that sets the accessible flag for an array of objects. If setting the flag of an object throws a SecurityException only objects earlier in the array will have their flags set to the given value, and all other objects are unchanged.
public boolean isAccessible()
Returns the current value of the accessible flag for this object.
 在已经获得对象的应用的情况下,操作对象属性; 如下方法可以打印对象的public属性的值    public static void printField(Object o, String name)
            throws NoSuchFieldException, IllegalAccessException {
        Field field = o.getClass().getField(name);
        Object o1 = field.get(o);
        System.out.println(o1.toString());
    }设置新值:   public static void setField(Object o, String name, Object nv)
            throws NoSuchFieldException, IllegalAccessException {
        Field field = o.getClass().getField(name);
        field.set(o, nv);
    }  下面来获得Method的信息: public Type getGenericReturnType()
 -- Returns the Type object for the type returned by this method. If the method is declared void the returned object is void.class.
public Type[] getGenericParameterTypes()
 -- Returns an array of Type objects with the type of each parameter of this method, in the order in which the parameters are declared. If the method has no parameters an empty array is returned.
public Type[] getGenericExceptionTypes()
 -- Returns an array of Type objects for each of the exception types listed in the throws clause for this method, in the order they are declared. If there are no declared exceptions an empty array is returned.  那么如何通过反射来调用方法呢:例如如下方法,调用String对象的indexOf方法return str.indexOf(".", 8);   String str = depeng@ccc.com;
// 共分为2步
// 1. 得到Method对象(因为方法重载有同名问题,所以要指明具体参数来避免误解)
// 2. 调用Method对象的invoke方法,(需要原始对象的引用已经对应的参数)
Method indexM = String.class.getMethod("indexOf", String.class, int.class);
Integer invoke = (Integer) indexM.invoke(str, ".", 8);
           那么如何通过反射来新建对象呢??????????????????????有如下2个方案:  - class.newInstance()   
 - invoke the constructor
 
  List list = ArrayList.class.newInstance();
System.out.println(list.toString()); 3. 关于注解(@Annotation)的操作
如果说泛型(generics)是对集合的约束,那么注解是对集合的统一说明。     4. 动态代理
1. 需要一个实现InvocationHandler 的类去处理代理方法调用时候的处理2. Proxy.newProxyInstance方法接受被代理对象和InvocationHandler 的实现类 
package com.iu.www.reflection;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class DebugProxy implements InvocationHandler {
    private final Object obj;           // underlying object
    private final List<Method> methods; // methods invoked
    private final List<Method> history; // viewable history
    public DebugProxy(Object obj) {
        this.obj = obj;
        methods = new ArrayList<Method>();
        history = Collections.unmodifiableList(methods);
    }
    public static synchronized Object proxyFor(Object obj) {
        Class<?> objClass = obj.getClass();
        return Proxy.newProxyInstance(objClass.getClassLoader(), objClass.getInterfaces(), new DebugProxy(obj));
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         methods.add(method); // log the call
        try {
            // invoke the real method
            return method.invoke(obj, args);
        } catch (InvocationTargetException e) {
            throw e.getCause();
        }
    }
    public List<Method> getHistory() {
        return history;
    }
    public static void main(String[] args) {
        List o = (List) DebugProxy.proxyFor(new ArrayList());
        o.add(1);
        o.add(111);
        o.add(222);
        for (Object o1 : o) {
            System.out.println(o1);
        }
        DebugProxy h = (DebugProxy) Proxy.getInvocationHandler(o);
        List<Method> history = h.getHistory();
        for (Method method : history) {
            System.out.println(method);
        }
    }
}   5. 类加载,加载类
系统运行时根据需要加载类,类加载的细节跟具体的JVM实现相关。但是他们普遍使用class path的架构来加载尚未载入的类。class path就是一系列的路径,JVM从这些路径去寻找类。
 然而,有时候你的应用程序也许会使用其他的方式加载自己的类,JVM提供了这个能力!你需要实现ClassLoader来加载字节码?(为什么是字节码?为什么不能直接是源程序?)。 protected Class<?> findClass(String name) throws ClassNotFoundException
   Locates the bytecodes representing the class name and loads them into the virtual machine, returning the Class object created to represent that class.  protected final void resolveClass(Class<?> c)
    Links the specified class if it has not already been linked.    import java.io.IOException;
import java.security.GeneralSecurityException;
public class GoodClassLoader extends ClassLoader{
    public Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
        try {
            // 我们要创建的Class对象
            Class clasz = null;
            // 必需的步骤1:如果类已经在系统缓冲之中,
            // 我们不必再次装入它
            clasz = findLoadedClass( name );
            if (clasz != null)
                return clasz;
            // 下面是定制部分
            byte classData[] = null; //todo /* 通过某种方法获取字节码数据 */;
            if (classData != null) {
                // 成功读取字节码数据,现在把它转换成一个Class对象
                clasz = defineClass( name, classData, 0, classData.length );
            }
            // 必需的步骤2:如果上面没有成功,
            // 我们尝试用默认的ClassLoader装入它
            if (clasz == null)
                clasz = findSystemClass( name );
            // 必需的步骤3:如有必要,则装入相关的类
            if (resolve && clasz != null)
                resolveClass( clasz );
            // 把类返回给调用者
            return clasz;
        } catch(IOException ie) {
            throw new ClassNotFoundException(ie.toString());
        } catch(GeneralSecurityException gse) {
            throw new ClassNotFoundException(gse.toString());
        }
    }
} JVM已经提供的重点的方法如下:  - findLoadedClass:用来进行检查,以便确认被请求的类当前还不存在。loadClass方法应该首先调用它。
   - defineClass:获得原始类文件字节码数据之后,调用defineClass把它转换成一个Class对象。任何loadClass实现都必须调用这个方法。
   - findSystemClass:提供默认ClassLoader的支持。如果用来寻找类的定制方法不能找到指定的类(或者有意地不用定制方法),则可以调用该方法尝试默认的装入方式。这是很有用的,特别是从普通的JAR文件装入标准Java类时。
   - resolveClass:当JVM想要装入的不仅包括指定的类,而且还包括该类引用的所有其他类时,它会把loadClass的resolve参数设置成true。这时,我们必须在返回刚刚装入的Class对象给调用者之前调用resolveClass。
 
                           
                    
                
                
            
        
浙公网安备 33010602011771号