JAVA反射简介及应用

反射简介及应用

    • 反射简介
      • 什么是反射
      • java反射的作用
      • 反射机制有什么用
      • 哪里会用到反射机制
      • 反射机制的应用场景有哪些
        • JDBC重点(Class.forName导致类加载)
        • Spring 通过 XML 配置模式装载 Bean 的过程
          • 可查看数据库连接和Spring的反射案例:
      • 反射机制的优缺点
      • 反射机制的相关类在哪个包下
    • 反射使用步骤(获取 Class 对象、调用对象方法)
      • 获取 Class 对象的三种方法
        • 代码示例
      • 获取构造器对象,通过构造器new出一个对象
        • 通过反射 class对象创建一个实例对象
          • 通过Class对象的newInstance()方法
          • 代码示例
        • 通过构造方法实例化对象,代码示例
      • 通过class对象获得一个属性对象
        • 通过class对象获得一个方法对象
        • 让方法执行
      • Java反射创建对象效率高还是通过new创建对象的效率高?
      • 反射机制相关的重要的类有哪些
        • Class代码示例
    • 反射Filed【反射/反编译一个类的属性】
      • Class类方法
        • 获取一个类的父类以及所有实现的接口
      • Field类方法
        • 反编译一个类的属性Field(本类的所有属性,不包含父类和接口), 代码示例
        • 取得实现的接口里的所有属性,代码示例
      • 给属性赋值三要素
      • 读属性值两个要素
        • 通过反射机制访问一个java对象的属性,代码示例
        • set()可以访问私有属性嘛?
      • Field方法
        • 代码示例
    • 反射Method【反射/反编译一个类的方法】
      • Method类方法
      • 反编译一个类的方法Method
      • 调用方法四要素
      • 取得一个类的所有方法(包括父类和实现的接口)
        • 代码示例
      • 取得一个类的所有方法(不包括父类和实现的接口)
      • 通过反射机制调用一个对象的方法
        • 代码示例
        • 代码示例
      • 通过方法名和方法参数类型取得对象的方法
    • 反射Constructor【反射/反编译一个类的构造方法】
      • Constructor类方法
      • 取得一个类的所有构造函数
      • 根据参数类型取得构造函数
      • 反编译一个类的构造方法Constructor
        • 代码示例
      • 反射机制创建对象两步骤
      • 通过反射机制调用构造方法实例化java对象
        • 代码示例
      • 获取一个类的父类以及实现的接口
        • 代码示例
      • 反射操作注解
        • 通过反射获取类上的注解,类属性上的注解,代码示例
      • 反射实例应用
        • 使用反射向一个int集合添加一个String元素
        • 代码示例
        • 通过反射修改数组元素的值
          • 代码示例
          • 扩展方法
        • 通过反射修改数组的大小
        • 通过反射实现通用工厂模式
      • 总结注意
      • 参考文献

反射简介

什么是反射

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

java反射的作用

在运行时判断任意一个对象所属的类

在运行时构造任意一个类的对象

在运行时判断任意一个类所具有的成员变量和方法

在运行时调用任意一个对象的方法

反射机制有什么用

通过java语言中的反射机制可以操作字节码文件(可以读和修改字节码文件。)

通过反射机制可以操作代码片段。(class文件。)

哪里会用到反射机制

Web服务器中利用反射调用了Sevlet的服务方法。

Eclispe等开发工具利用反射动态刨析对象的类型与结构,动态提示对象的属性和方法。

模块化的开发,通过反射去调用对应的字节码

动态代理设计模式也采用了反射机制

Spring/Hibernate 等框架,也是利用CGLIB 反射机制才得以实现:

  1. JDBC中,利用反射动态加载了数据库驱动程序。

  2. 很多框架都用到反射机制,注入属性,调用方法,如Spring。

反射机制的应用场景有哪些

  1. 我们在使用JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序;
  2. Spring框架也用到很多反射机制,最经典的就是xml的配置模式。

JDBC重点(Class.forName导致类加载)

如果你只是希望一个类的静态代码块执行,其它代码一律不执行,可以使用:

Class.forName(“完整类名”);

这个方法的执行会导致类加载,类加载时,静态代码块执行。

Spring 通过 XML 配置模式装载 Bean 的过程

  1. 将程序内所有 XML 或 Properties 配置文件加载入内存中;
  2. Java类里面解析xml或properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息;
  3. 使用反射机制,根据这个字符串获得某个类的Class实例;
  4. 动态配置实例的属性
可查看数据库连接和Spring的反射案例:

https://zhuanlan.zhihu.com/p/394766551

反射机制的优缺点

优点: 运行期类型的判断,动态加载类(在运行期间根据业务功能动态执行方法、访问属性),提高代码灵活度。

缺点: 性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的java代码要慢很多。

反射机制的相关类在哪个包下

java.lang.reflect.*;

反射使用步骤(获取 Class 对象、调用对象方法)

获取 Class 对象的三种方法

1.通过new对象实现反射机制: 对象.getClass()

2.通过路径实现反射机制: Class.forName(“完整类名带包名”) 静态方法

例如:com.mysql.jdbc.Driver Driver类已经被加载到 jvm中,并且完成了类的初始化工作就行了

3.通过类名实现反射机制: 任何类型(类名).class。 获取Class<?> clz 对象

代码示例

public class Student {
	private int id;
	String name;
 	protected boolean sex;
	public float score;
}

public class Get {
	//获取反射机制三种方式
	public static void main(String[] args) throws ClassNotFoundException {
        //方式一(通过建立对象)
        Student stu = new Student();
        Class classobj1 = stu.getClass();
        System.out.println(classobj1.getName());

        //方式二(所在通过路径-相对路径)
        Class classobj2 = Class.forName("fanshe.Student");
        System.out.println(classobj2.getName());

        //方式三(通过类名)
        Class classobj3 = Student.class;
        System.out.println(classobj3.getName());
	}
}

获取构造器对象,通过构造器new出一个对象

Clazz.getConstructor([String.class]);

Con.newInstance([参数]);

通过反射 class对象创建一个实例对象

相当与new类名()无参构造器

Cls.newInstance(); //对象.newInstance()

注:newInstance()方法内部实际上调用了无参数构造方法,必须保证无参构造存在才可以。

否则会抛出java.lang.InstantiationException异常。

通过Class对象的newInstance()方法

Object instance = clazz1.newInstance()

代码示例
class ReflectTest02{
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        // 下面这段代码是以反射机制的方式创建对象。
        // 通过反射机制,获取Class,通过Class来实例化对象
        Class c = Class.forName("javase.reflectBean.User");
        // newInstance() 这个方法会调用User这个类的无参数构造方法,完成对象的创建。
        // 重点是:newInstance()调用的是无参构造,必须保证无参构造是存在的!
        Object obj = c.newInstance();
        System.out.println(obj);
    }
}

通过构造方法实例化对象,代码示例

//取得无参构造
Constructor<?> constructor2 = clazz1.getConstructor();
//通过无参构造创建一个对象
Object child1 = constructor2.newInstance();

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

通过class对象获得一个属性对象

获得某个类的所有的公共(public)的字段,包括父类中的字段

Field c=cls.getFields()

获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的声明字段

Field c=cls.getDeclaredFields()

通过class对象获得一个方法对象

只能获取公共的

Cls.getMethod(“方法名”,class……parameaType);

获取任意修饰的方法,不能执行私有

Cls.getDeclareMethod(“方法名”);

让私有的方法可以执行

M.setAccessible(true);

让方法执行

Method.invoke(obj实例对象,obj可变参数); //是有返回值的

Java反射创建对象效率高还是通过new创建对象的效率高?

new创建对象的效率高

Java代码是需要编译后在虚拟机里面运行的。

一般都是通过一个前端编辑器,比如javac,把java文件转为class文件。程序运行期间,可能会通过一个JIT,即时编译器将字节码文件转换为计算机认识的机器码文件。

另外一种可能是通过一个AOT编译器,直接把java文件编译为本地机器码文件。

其中JIT在程序运行期会对程序进行优化,但是反射是通过动态解析的方式,因此可能无法执行某些java虚拟机的优化。

下方博客有代码示例说明

Java反射和new效率对比_编程爱好者jony的博客-CSDN博客

反射机制相关的重要的类有哪些

含义
java.lang.Class 代表整个字节码。代表一个类型,代表整个类。
java.lang.reflect.Method 代表字节码中的方法字节码。代表类中的方法。
java.lang.reflect.Constructor 代表字节码中的构造方法字节码。代表类中的构造方法。
java.lang.reflect.Field 代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)。

注:必须先获得Class才能获取Method、Constructor、Field。

Class代码示例

java.lang.Class:
public class User{
    // Field
    int no;
    
    // Constructor
    public User(){
	}

    public User(int no){
        this.no = no;
    }
    
    // Method
    public void setNo(int no){
        this.no = no;
	}

    public int getNo(){
        return no;
    }
}

反射Filed【反射/反编译一个类的属性】

Class类方法

方法名 备注
public T newInstance() 创建对象
public String getName() 返回完整类名带包名
public String getSimpleName() 返回类名
public Field[] getFields() 返回类中public修饰的属性
public Field[] getDeclaredFields() 返回类中所有的属性
public Field getDeclaredField(String name) 根据属性名name获取指定的属性
public native int getModifiers() 获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Method[] getDeclaredMethods() 返回类中所有的实例方法
public Method getDeclaredMethod(String name, Class… parameterTypes) 根据方法名name和方法形参获取指定方法
public Constructor[] getDeclaredConstructors() 返回类中所有的构造方法
public Constructor getDeclaredConstructor(Class… parameterTypes) 根据方法形参获取指定的构造方法

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

public native Class getSuperclass() 返回调用类的父类
public Class[] getInterfaces() 返回调用类实现的接口集合

Field类方法

方法名 备注
public String getName() 返回属性名
public int getModifiers() 获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Class getType() 以Class类型,返回属性类型【一般配合Class类的getSimpleName()方法使用】
public void set(Object obj, Object value) 设置属性值
public Object get(Object obj) 读取属性值

反编译一个类的属性Field(本类的所有属性,不包含父类和接口), 代码示例

//通过反射机制,反编译一个类的属性Field(了解一下)
class ReflectTest06{
    public static void main(String[] args) throws ClassNotFoundException {
		Class studentClass = Class.forName("javase.reflectBean.Student");
		Modifier.toString(studentClass.getModifiers();
		// Class类的getName方法
		studentClass.getSimpleName()
    
        //获取所有的属性
		Field[] declaredFields = clazz1.getDeclaredFields();
        for (int i = 0; i < declaredFields.length; i++) {
            //属性名称
            String fieldName = declaredFields[i].getName();
            //属性的类型 
            // declaredFields[i]..getType().getSimpleName()
            String fieldType = declaredFields[i].getType().getName();
            //获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号
            //用Modifier类的toString转换成字符串
            String fieldModifier = Modifier.toString(declaredFields[i].getModifiers());
            System.out.println(fieldModifier + " " + fieldType + " " + fieldName + ";");
        }
    }
}

取得实现的接口里的所有属性,代码示例

//通过属性名取得属性
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));

给属性赋值三要素

示例:给s对象的no属性赋值1111

要素1:对象 s

要素2:no 属性

要素3:1111 值

读属性值两个要素

示例:获取s对象的no属性的值

要素1:对象 s

要素2:no属性

注: Field类中set()、get()使用注意事项:

  1. 属性.set(对象, 值);

  2. 属性.get(对象);

通过反射机制访问一个java对象的属性,代码示例

/*
必须掌握:
怎么通过反射机制访问一个java对象的属性?
给属性赋值set
获取属性的值get
 */
class ReflectTest07{
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        // 不使用反射机制给属性赋值
        Student student = new Student();
        // 给属性赋值三要素:给s对象的no属性赋值1111
        student.no = 1111;
        // 读属性值两个要素:获取s对象的no属性的值
        System.out.println(student.no);

        // 使用反射机制给属性赋值
        Class studentClass = Class.forName("javase.reflectBean.Student");

		// obj就是Student对象。(底层调用无参数构造方法)
        Object obj = studentClass.newInstance();

        // 获取no属性(根据属性的名称来获取Field)
        Field noField = studentClass.getDeclaredField("no");

        // 给obj对象(Student对象)的no属性赋值
        noField.set(obj, 22222);

        // 读取属性的值
        // 两个要素:获取obj对象的no属性的值。
        System.out.println(noField.get(obj));
	}
}

set()可以访问私有属性嘛?

不可以,需要打破封装,才可以

Field方法

方法 备注
public void setAccessible(boolean flag) 默认false,设置为true为打破封装

代码示例

// 可以访问私有的属性吗?
Field nameField = studentClass.getDeclaredField("name");
// 打破封装(反射机制的缺点:打破封装,可能会给不法分子留下机会!!!)
// 这样设置完之后,在外部也是可以访问private的。
nameField.setAccessible(true);
// 给name属性赋值
nameField.set(obj, "xiaowu");
// 获取name属性的值
System.out.println(nameField.get(obj));

反射Method【反射/反编译一个类的方法】

Method类方法

方法名 备注
public String getName() 返回方法名
public int getModifiers() 获取方法的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Class getReturnType() 以Class类型,返回方法类型【一般配合Class类的getSimpleName()方法使用】
public Class[] getParameterTypes() 返回方法的修饰符列表(一个方法的参数可能会有多个。)【结果集一般配合Class类的getSimpleName()方法使用】
public Object invoke(Object obj, Object… args) 调用方法

反编译一个类的方法Method

class ReflectTest09{
    public static void main(String[] args) throws ClassNotFoundException {
        Class userServiceClass = Class.forName("java.lang.String");

StringBuilder s = new StringBuilder();
        s.append(Modifier.toString(userServiceClass.getModifiers()));
        s.append(" class ");
        s.append(userServiceClass.getSimpleName());
        s.append(" {\n");

        // 获取所有的Method(包括私有的!)
        Method[] methods = userServiceClass.getDeclaredMethods();
        for (Method m : methods){
            s.append("\t");
            // 获取修饰符列表
            s.append(Modifier.toString(m.getModifiers()));
            s.append(" ");
            // 获取方法的返回值类型
            s.append(m.getReturnType().getSimpleName());
            s.append(" ");
            // 获取方法名
            s.append(m.getName());
            s.append("(");
            // 方法的修饰符列表(一个方法的参数可能会有多个。)
            Class[] parameterTypes = m.getParameterTypes();
            for (int i = 0; i < parameterTypes.length; i++){
                s.append(parameterTypes[i].getSimpleName());
                if (i != parameterTypes.length - 1) s.append(", ");
            }
            s.append(") {}\n");
        }
        s.append("}");
        System.out.println(s);
    }
}

调用方法四要素

示例:调用对象userService的login方法

  1. 对象 userService

  2. login 方法名

  3. 实参列表

  4. 返回值

注: Method类中invoke()使用注意事项:

方法.invoke(对象, 实参);

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

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);
}

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

Method[] declaredMethods = clazz1.getDeclaredMethods();

通过反射机制调用一个对象的方法

代码示例

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);
}

代码示例

/*
重点:必须掌握,通过反射机制怎么调用一个对象的方法?
 */
class ReflectTest10{
    public static void main(String[] args) throws Exception {
        // 不使用反射机制,怎么调用方法
        // 创建对象
        UserService userService = new UserService();
        // 调用方法
        System.out.println(userService.login("admin", "123") ? "登入成功!" : "登入失败!");

        //使用反射机制调用方法
        Class userServiceClass = Class.forName("javase.reflectBean.UserService");
        // 创建对象
        Object obj = userServiceClass.newInstance();
        // 获取Method
        Method loginMethod = userServiceClass.getDeclaredMethod("login", String.class, String.class);
//        Method loginMethod = userServiceClass.getDeclaredMethod("login");//注:没有形参就不传
        // 调用方法
        // 四要素:loginMethod方法, obj对象,"admin","123" 实参,retValue 返回值
        Object resValues = loginMethod.invoke(obj, "admin", "123");//注:方法返回值是void 结果是null
        System.out.println(resValues);
    }
}

通过方法名和方法参数类型取得对象的方法

Method method = clazz1.getDeclaredMethod(“setName”, String.class);

反射Constructor【反射/反编译一个类的构造方法】

Constructor类方法

方法名 备注
public String getName() 返回构造方法名
public int getModifiers() 获取构造方法的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Class[] getParameterTypes() 返回构造方法的修饰符列表(一个方法的参数可能会有多个。)【结果集一般配合Class类的getSimpleName()方法使用】
public T newInstance(Object … initargs) 创建对象【参数为创建对象的数据】

取得一个类的所有构造函数

Class clazz1 = Class.forName(“xxxx”);

Constructor[] constructors = clazz1.getConstructors();

Constructor[] constructors1 = clazz1.getDeclaredConstructors();

根据参数类型取得构造函数

取得只有一个参数且参数类型为String的构造方法

Constructor constructor1 = clazz1.getDeclaredConstructor(String.class);

System.out.println(constructor1);

反编译一个类的构造方法Constructor

代码示例

/*
反编译一个类的Constructor构造方法。
*/
class ReflectTest11{
    public static void main(String[] args) throws ClassNotFoundException {
        StringBuilder s = new StringBuilder();

        Class vipClass = Class.forName("javase.reflectBean.Vip");

        //public class UserService {
        s.append(Modifier.toString(vipClass.getModifiers()));
        s.append(" class ");
        s.append(vipClass.getSimpleName());
        s.append("{\n");

        Constructor[] constructors = vipClass.getDeclaredConstructors();
        for (Constructor c : constructors){
            //public Vip(int no, String name, String birth, boolean sex) {
            s.append("\t");
            s.append(Modifier.toString(c.getModifiers()));
            s.append(" ");
//            s.append(c.getName());//包名+类名
            s.append(vipClass.getSimpleName());//类名
            s.append("(");
            Class[] parameterTypes = c.getParameterTypes();
            for (int i = 0; i < parameterTypes.length; i++){
                s.append(parameterTypes[i].getSimpleName());
                if (i != parameterTypes.length - 1 ) s.append(", ");
            }
            s.append("){}\n");
        }

        s.append("}");
        System.out.println(s);
    }
}

反射机制创建对象两步骤

先获取到这个有参数的构造方法【用ClassgetDeclaredConstructor()方法获取】

调用构造方法new对象【用Constructor类的newInstance()方法new对象】

通过反射机制调用构造方法实例化java对象

代码示例

/*
通过反射机制调用构造方法实例化java对象。(这个不是重点)
*/
class ReflectTest12{
    public static void main(String[] args) throws Exception {
        //不适用反射创建对象
        Vip vip1 = new Vip();
        Vip vip2 = new Vip(123, "zhangsan", "2001-10-19", false);

        //使用反射机制创建对象(以前)
        Class vipClass = Class.forName("javase.reflectBean.Vip");
        // 调用无参数构造方法
        Object obj1 = vipClass.newInstance();//Class类的newInstance方法
        System.out.println(obj1);

        //使用反射机制创建对象(现在)
        // 调用有参数的构造方法怎么办?
        // 第一步:先获取到这个有参数的构造方法
  Constructor c1 = vipClass.getDeclaredConstructor(int.class, String.class, String.class, boolean.class);
        // 第二步:调用构造方法new对象
        Object obj2 = c1.newInstance(321, "lsi", "1999-10-11", true);//Constructor类的newInstance方法
        System.out.println(obj2);

        // 获取无参数构造方法
        Constructor c2 = vipClass.getDeclaredConstructor();
        Object obj3 = c2.newInstance();
        System.out.println(obj3);
    }
}

注: 如果需要调用无参构造方法,getDeclaredConstructor()方法形参为空即可【和Class类的newInstance()方法一样的效果】

获取一个类的父类以及实现的接口

两个方法【Class类中的】

public native Class getSuperclass()

public Class[] getInterfaces()

代码示例

/**
 *重点:给你一个类,怎么获取这个类的父类,已经实现了哪些接口?
 */
class ReflectTest13{
    public static void main(String[] args) throws Exception{
        // String举例
        Class vipClass = Class.forName("java.lang.String");
		// 获取String的父类
		Class superclass = vipClass.getSuperclass();
		// 获取String类实现的所有接口(一个类可以实现多个接口。)
		Class[] interfaces = vipClass.getInterfaces();
		System.out.println(superclass.getName());
		for (Class i : interfaces) {
			System.out.println(i.getName());
		}
	}
}

反射操作注解

通过反射获取类上的注解,类属性上的注解,代码示例

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();
}

反射实例应用

使用反射向一个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));

通过反射修改数组元素的值

原理:其实就是通过反射中的 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));

通过反射修改数组的大小

原理:其本质就是通过反射得到原数组的类型,然后通过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;
}

通过反射实现通用工厂模式

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();
}

总结注意

属性最重要的是名字

实例方法最重要的是名字和形参列表

构造方法最重要的是形参列表

参考文献

综合了好几篇反射相关的博客整理的一份全集,找不到那些博客地址了,仅供学习笔记使用。

你可能感兴趣的:(java,spring,开发语言)