Java反射机制

2018-06-27  本文已影响0人  雨中独奏

1.反射机制的概念

Java不允许在运行时改变程序结构或类型变量的结构,但它允许在运行时去探知、加载、调用在编译期完全未知的class,可以在运行时加载该class,生成实例对象(instance object),调用method,或对field赋值,这就是Java的反射机制。
获取Class对象的三种方式:
1.通过Object类的getClass()方法(需要先实例化一个对象)

A a = new A();
Class classA = a.getClass();

2.通过类名获得(需要有java文件存在)

Class classA = A.class;

3.通过路径名获取

Class classA = Class.forName("路径名");

总结:三种方式均能够获得Class对象,区别是方法一需要创建对象,静态块和动态构造块均会执行,方法二不执行静态块和动态构造块,方法三执行静态块、不执行动态构造块;

注意:静态块仅在类加载时执行一次,若类已加载便不再重复执行;而动态构造块在每次new对象时均会执行

2.利用反射机制能做什么

1.动态加载类
假设有一个场景:在类A中一个方法methodA中需要调用另一个类B的方法methodB,而后有需求变更,methodA中的业务逻辑需要改变,而方法methodB也不能轻易改动,因为在其他类中也有调用到。此时就可以利用反射来动态加载一个类C,让方法methodA能使用类C中的方法methodB。

public class A {

    private static String className = "com.nongmo.B";//可通过配置文件来读取

    public void methodA() throws Exception{
        Class classT = Class.forName(className);
        T t = (T)classT.newInstance();
        t.methodB();
        System.out.print("执行A的业务逻辑");
    }
}

public interface T {

    void methodB();
}

public class B implements T {

    public void methodB(){
        System.out.println("使用B的方法");
    }
}

public class C implements T {

    public void methodB(){
        System.out.println("使用C的方法");
    }
}

这里定义了接口T来规范所需调用的方法methodB。
这个例子太简单,更深入应用在java动态代理,后续再写。
2.属性赋值
有时候某个类的属性值需要接收其他类中的相同属性名的值计算后来赋值,如果对于每一个其他类来单独写赋值方法会显得很繁琐,这里就可以通过反射来写一个通用方法。
Equipment为接收属性的类,AppDTO是一个接口,所有要计算属性赋值给Equipment的类都实现这个接口。

public static void addProperty(Equipment equipment, AppDTO dto){
        //获取dto的所有属性,私有公有属性都可以
        Field[] fields = dto.getClass().getDeclaredFields();
        Field propertyField = null;
        String property1Type = null;
        String property2Type = null;
        for(Field field: fields) {
            try {
                //根据dto的属性名获取到equipment的属性
                propertyField = equipment.getClass().getDeclaredField(field.getName());
                //设置属性可获取可变
                field.setAccessible(true);
                propertyField.setAccessible(true);
                property1Type = propertyField.getType().toString();
                property2Type = field.getType().toString();
                //如果dto的属性不为int类型则直接过滤掉
                if (!property2Type.endsWith("int")) {
                   continue;
                }
                //获取dto属性的值
                int value2 = field.getInt(dto);
                //判断equipment的属性是包装类Integer
                if (property1Type.endsWith("Integer")) {
                    //计算赋值
                    int value1 = propertyField.get(equipment) == null ? 0 : ((Integer)propertyField.get(equipment)).intValue();
                    propertyField.set(equipment,value1 + value2);
                } else if (property1Type.endsWith("int")) { //是数值类型int
                    //计算赋值
                    int value1 = propertyField.getInt(equipment);
                    propertyField.setInt(equipment,value1 + value2);
                }
            } catch (Exception e) {
                continue;
            }
        }
    }

3.java反射常用API

以下概述直接来自于Class这个类的API,当然专注于反射类的Constructor也有与之相类似的方法,基本上可以互相对应:

对Class简单的获取信息

Class<? extends U> asSubclass(Class<U> clazz) 
强制转换该 Class 对象,以表示指定的 class 对象所表示的类的一个子类。 

T cast(Object obj) 
将一个对象强制转换成此 Class 对象所表示的类或接口。 

static Class<?> forName(String className) 
返回与带有给定字符串名的类或接口相关联的 Class 对象。 

static Class<?> forName(String name, boolean initialize, ClassLoader loader) 
使用给定的类加载器,返回与带有给定字符串名的类或接口相关联的 Class 对象。 

Constructor<T> getConstructor(Class<?>... parameterTypes) 
返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。 

Constructor<?>[] getConstructors() 
返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。

注解相关

<A extends Annotation> A getAnnotation(Class<A> annotationClass) 
如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。 

Annotation[] getAnnotations() 
返回此元素上存在的所有注释。 

String getCanonicalName() 
返回 Java Language Specification 中所定义的底层类的规范化名称。 

Class<?>[] getClasses() 
返回一个包含某些 Class 对象的数组,这些对象表示属于此 Class 对象所表示的类的成员的所有公共类和接口。 
ClassLoader getClassLoader() 
返回该类的类加载器。 

Class<?> getComponentType() 
返回表示数组组件类型的 Class。

获取对应方法

Method getMethod(String name, Class<?>... parameterTypes) 
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。 

Method[] getMethods() 
返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。 

Class<?>[] getDeclaredClasses() 
返回 Class 对象的一个数组,这些对象反映声明为此 Class 对象所表示的类的成员的所有类和接口。

Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 
返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。 

Constructor<?>[] getDeclaredConstructors() 
返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。 

Method getDeclaredMethod(String name, Class<?>... parameterTypes) 
返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。

Method[] getDeclaredMethods() 
返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。 

Class<?> getDeclaringClass() 
如果此 Class 对象所表示的类或接口是另一个类的成员,则返回的 Class 对象表示该对象的声明类。

获取字段

Field getField(String name) 
返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。 

Field[] getFields() 
返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。 

Field getDeclaredField(String name) 
返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。 

Field[] getDeclaredFields() 
返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。 

Type[] getGenericInterfaces() 
返回表示某些接口的 Type,这些接口由此对象所表示的类或接口直接实现。 

获取接口以及简单信息

Class<?>[] getInterfaces() 
确定此对象所表示的类或接口实现的接口。 

String getName() 
以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。 

Package getPackage() 获取此类的包。 

Class<? super T> getSuperclass() 
返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。 

TypeVariable<Class<T>>[] getTypeParameters() 
按声明顺序返回 TypeVariable 对象的一个数组,这些对象表示用此 GenericDeclaration 对象所表示的常规声明来声明的类型变量。 

boolean isAnnotation() 如果此 Class 对象表示一个注释类型则返回 true。 

反射调用私有方法示例

public class PrivateClass{

        private String sayHello (String name) {
            System.out.println("我是"+name);
        }
}
public static void main(String[] args) throws Exception
    {
    
        Class classType =Class.forName("PrivateClass");

        // 获取Method对象
        Method method = classType.getDeclaredMethod("sayHello",
                new Class[] { String.class });

        method.setAccessible(true); // 抑制Java的访问控制检查
        // 如果不加上上面这句,将会Error: TestPrivate can not access a member of class PrivateClass with modifiers "private"
        String str = (String) method.invoke(p, new Object[] { "zhangsan" });

        System.out.println(str);
    }
上一篇下一篇

猜你喜欢

热点阅读