Java反射API

反射定义:

JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

 

反射最常用到的操作:

取得属性的修饰符,属性名,属性值,设置属性值,取得所有的属性。

操作方法,取得方法的修饰符,方法名,方法参数,方法参数类型,方法返回值,取得一个类的所有方法。

反射可以动态的调用、修改一个对象的任何属性、方法(包括私有属性和方法)。

 

Field
访问字段:
Field getField(name) : 根据字段名获取某个 public 的field(包括父类);
Field getDeclaredField(name) : 根据字段名获取当前类的某个 Field(不包括父类,可以获取private属性)
Field[] getFields() : 获取所有 public 的 field(含父类);
Field[] getDeclaredFields() : 获取当前类的所有 filed (不包括父类);

Field对象包含的信息:

Object get(Object obj) 

getName() :返回字段名称;
getType() : 返回字段类型, 一个 Class 实例;
getAnnotation(xxx.class) : 获取 xxx 注解;
Type getGenericType() # 字段类型(能正确表示出泛型) java.util.List<java.lang.String> ,ParameterizedType的实例;
field.set(Object,Object) # 设置字段值:(指定的实例,待修改的值);
getModifiers() : 字段修饰符,是一个 int;

Method
通过 Class 获取 Method:
​ Method getMethod(name,Class…) # 获取某个 public 的 Method(包括父类);

​ Method getDeclaredMethod(name,Class…) # 获取当前类的某个 Method(不包括父类);

​ Method[] getMethods() # 获取所有 public 的Method(包括父类);

​ Method[] getDeclaredMethods() # 获取当前类的所有Method(不包括父类);

Method 对象包含一个方法的所有信息

​getName() : 返回方法名称;

​getReturnType() : 返回方法的返回值类型,一个 Class 实例;

getParameterTypes() : 返回方法的参数类型,是一个 Class 数组

​getParameterAnnotations() : 返回方法参数的所有注解,是一个Annotation[][]

 

1、取得一个类的Class对象的三种方式

//第一种
Class<?> clazz1 = Class.forName("com.wp.reflect.entity.Child");
//第二种
Class<? extends Child> clazz2 = new Child().getClass();
//第三种
Class<?> clazz3 = Child.class;

2、获取一个类的父类以及所有实现的接口

Class<?> superclass = clazz1.getSuperclass();
Class<?>[] interfaces = clazz1.getInterfaces();
//取得一个类的全类名(包名 + 类名)
clazz1.getName()
//取得类的简单名称
clazz1.getSimpleName();

3、构造函数

1、取得一个类的所有构造函数
Class<?> clazz1 = Class.forName("xxxx");
Constructor<?>[] constructors = clazz1.getConstructors();
Constructor<?>[] constructors1 = clazz1.getDeclaredConstructors();
2、根据参数类型取得构造函数
//取得只有一个参数且参数类型为String的构造方法
Constructor<?> constructor1 = clazz1.getDeclaredConstructor(String.class);
System.out.println(constructor1);

4、通过反射实例化对象

1、通过Class对象的newInstance()方法
Object instance = clazz1.newInstance();
2、通过构造方法实例化对象
//取得无参构造
Constructor<?> constructor2 = clazz1.getConstructor();
//通过无参构造创建一个对象
Object child1 = constructor2.newInstance();

//取得指定参数的构造方法对象
Constructor<?> constructor3 = clazz1.getConstructor(String.class, int.class);
//通过构造方法对象创建一个对象
constructor3.newInstance("wenpan",21);

5、反射操作属性

1、取得本类的所有属性(不包含父类和接口)
Field[] declaredFields = clazz1.getDeclaredFields();
for (int i = 0; i < declaredFields.length; i++) {
    //属性名称
    String fieldName = declaredFields[i].getName();
    //属性的类型
    String fieldType = declaredFields[i].getType().getName();
    //属性修饰符
    String fieldModifier = Modifier.toString(declaredFields[i].getModifiers());
    System.out.println(fieldModifier + " " + fieldType + " " + fieldName + ";");
}
2、取得实现的接口里的所有属性
Field[] fields = clazz1.getFields();
for (int i = 0; i < fields.length; i++) {
    //属性名称
    String fieldName = fields[i].getName();
    //属性的类型
    String fieldType = fields[i].getType().getName();
    //属性修饰符
    String fieldModifier = Modifier.toString(fields[i].getModifiers());
    System.out.println(fieldModifier + " " + fieldType + " " + fieldName + ";");
}
3、通过指定属性名取得属性

//通过属性名取得属性
Field field = clazz1.getDeclaredField("name");
//访问私有属性需要设置Accessible为true才可以更改或读取值(get 或 set)
field.setAccessible(true);
//取得instance对象里面的属性值
System.out.println("更改前的name值:" + field.get(instance));
//更改instance对象里的name属性值
field.set(instance,"文攀啊");
System.out.println("更改后的name属性值:" + field.get(instance));

通过属性名获取属性: Field field = clazz1.getDeclaredField("name"); 返回的是属性对象: Field

6、反射操作方法

1、取得一个类的所有方法(包括父类和实现的接口)

Method[] methods = clazz1.getMethods();

Method[] methods = clazz1.getMethods();
//输出取得的方法
for (int i = 0; i < methods.length; i++) {
    StringBuffer buffer = new StringBuffer();
    //方法修饰符
    String modifier = Modifier.toString(methods[i].getModifiers());
    //返回值类型
    String returnType = methods[i].getReturnType().getSimpleName();
    //方法名
    String name = methods[i].getName();
    //方法参数类型
    Class<?>[] parameterTypes = methods[i].getParameterTypes();
    buffer.append(modifier).append(" ").append(returnType).append(" ").
        append(name).append("(");
    for (int j = 0; j < parameterTypes.length; j++) {
        buffer.append(parameterTypes[j].getSimpleName()).append(" arg").append(j);
        if(j < parameterTypes.length - 1){
            buffer.append(",");
        }
    }
    buffer.append(")");
    //方法抛出的异常信息
    Class<?>[] exceptionTypes = methods[i].getExceptionTypes();
    if(exceptionTypes.length > 0){
        buffer.append(" throws");
    }
    for (int k = 0; k < exceptionTypes.length; k++) {
        buffer.append(exceptionTypes[k].getSimpleName());
        if(k < exceptionTypes.length -1){
            buffer.append(",");
        }
    }
    buffer.append("{ }");
    System.out.println(buffer);
}
2、取得一个类的所有方法(不包括父类和实现的接口)

Method[] declaredMethods = clazz1.getDeclaredMethods();

3、通过反射调用某个对象的方法

要调用某个对象的方法,首先这个对象得存在,所以可以通过反射创建一个对象

public static void main(String[] args) throws Exception {
    //使用反射将Child类的Class对象加载进来
    Class<?> clazz1 = Class.forName("com.wp.reflect.entity.Child");
    //创建一个实例对象(使用反射调用方法必须要有实例对象)
    Object instance = clazz1.newInstance();
    //通过方法名称和指定的参数类型获取指定方法
    Method method = clazz1.getDeclaredMethod("setName", String.class);
    //调用Method对象的invoke方法执行instance对象的方法
    method.invoke(instance,"文攀啊");
    //通过方法名称和指定的参数类型获取指定方法
    Method getNameMethod = clazz1.getMethod("getName");
    //调用Method对象的invoke方法执行方法
    String name = (String)getNameMethod.invoke(instance);
    //输出执行方法返回的结果
    System.out.println(name);
}

通过方法名和方法参数类型取得对象的方法:Method method = clazz1.getDeclaredMethod(“setName”, String.class);

7、反射操作注解

1、通过反射获取类上的注解,类属性上的注解
 public static void main(String[] args) throws Exception{

        User user = new User();
        // 取得类上的所有注解
        Annotation[] annotations = user.getClass().getAnnotations();
        // 获取类上指定类型的注解
        MyAnnotation annotation = user.getClass().getAnnotation(MyAnnotation.class);
        // 获取类的某个属性上的所有注解
        Annotation[] allAnnotations = user.getClass().getDeclaredField("name").getAnnotations();
        // 获取类的某个属性上指定类型的注解
        MyAnnotation annotation1 = user.getClass().getDeclaredField("name").getAnnotation(MyAnnotation.class);
        // 获取注解的属性
        String value = annotation1.value();
        String pattern = annotation1.pattern();
    }

8、反射实例应用

1、使用反射向一个int集合添加一个String元素

**原理:**首先通过反射取得该List集合的add()方法,然后使用反射调用该方法向list集合里面添加一个String类型元素

//创建一个int类型的集合
ArrayList<Integer> list = new ArrayList<Integer>();
//取得集合的添加方法
Method addMethod = list.getClass().getMethod("add", Object.class);
//执行集合的add方法,向集合中添加一个String类型元素
addMethod.invoke(list, "wenpan");
System.out.println("取得元素=========================>" + list.get(0));
2、通过反射修改数组元素的值

**原理:**其实就是通过反射中的 Array.get() 和 Array.set()来读取和修改数组中的元素值。

int temp[] = {1,2,3,4,5};
System.out.println("数组第一个元素:" + Array.get(temp,0));
Array.set(temp,0,100);
System.out.println("修改之后数组第一个元素为:" + Array.get(temp,0));

扩展方法:

int temp[] = {1,2,3,4,5};
//取得数组的类型,该方法为动态扩展数组大小做铺垫
Class<?> componentType = temp.getClass().getComponentType();
System.out.println("数组类型:" + componentType.getName());
System.out.println("数组长度:" + Array.getLength(temp));
3、通过反射修改数组的大小

**原理:**其本质就是通过反射得到原数组的类型,然后通过Array.newInstance()方法根据数组类型创造出一个指定长度的新数组,最后使用System.arraycopy()方法将原数组的值拷贝到新数组中

public static Object arrayInc(Object obj, int length) {
    //取得传入数组的类型,以便于创造出同类型的数组
    Class<?> componentType = obj.getClass().getComponentType();
    //根据传入的数组类型创建出新的指定长度的数组实例
    Object newArr = Array.newInstance(componentType, length);
    //原有的数组长度
    int originArrLen = Array.getLength(obj);
    //将原有的数组数据拷贝到新的数组中去
    System.arraycopy(obj,0,newArr,0,originArrLen);

    //返回修改大小后的数组
    return newArr;
}
4、通过反射实现通用工厂模式
class Factory {

    /**
     * 通过传入的全类名返回该类的对象
     *  但是有一点仍然很麻烦,就是需要知道完整的包名和类名,这里可以使用properties配置文件来完成。
     *  java读取配置文件可实现完全解耦
     * @param className
     * @return
     */
    public static Object getInstance(String className){

        Object instance = null;
        try {
            //通过反射创建对象实例
            instance = Class.forName(className).newInstance();
        }catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }
}
public static void main(String[] args) {
    //反射工厂
    Apple apple = (Apple)Factory.getInstance("com.wp.reflect.Apple");
    apple.method();

    Banana banana = (Banana)Factory.getInstance("com.wp.reflect.Banana");
    banana.method();
}

反射还可以实现动态代理

 

9 安全问题

如下形式enum 枚举类实现单例模式

public enum EnumSingleton {

    INSTANCE;
    private EnumSingleton(){
    }

    public static EnumSingleton getInstance() {
        return INSTANCE;
    }
}

在反射的newInstance()方法里面,会检查该类是否ENUM修饰,如果是则抛出异常,反射失败,枚举类实现单例模式不用怕反射破坏

public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
         //其他代码已删除
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
         //其他代码已删除
       
    }

总结: 只有枚举实现单例模式可以防止反射攻击,其它单例模式无法防止

 

posted @ 2023-09-07 09:16  剑阁丶神灯  阅读(32)  评论(0编辑  收藏  举报