反射

反射

概述

反射就是把Java类中的各个组成部分进行解剖,并映射成一个个的Java对象,拿到这些对象后可以做一些事情。
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意属性和方法;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。

我们拿到映射后的构造方法,可以用它来生成对象;拿到映射后的方法,可以调用它来执行对应的方法;拿到映射后的字段,可以用它来获取或改变对应字段的值。

而且我们也可以通过反射去创建对象。这也算是创建对象的一种方式吧。这里说一下创建对象的几种方式。

  1. 直接new一个对象。
  2. 使用Clone的方法:无论何时我们调用一个对象的clone方法,JVM就会创建一个新的对象,将前面的对象的内容全部拷贝进去。
  3. 使用反序列化:当我们序列化和反序列化一个对象,JVM会给我们创建一个单独的对象。
  4. 利用反射,获取构造方法,调用newInstance(Object … initargs)方法创建对象。

反射就是框架设计的灵魂

Java反射机制

Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。

通过反射获取Class类对象的三种方式
Class.forName("全类名");
Object.class;
Object.getClass();

第一种方式(源码):通过对象的全类名,获取Class对象

@CallerSensitive
public static Class<?> forName(String className) 
				throws ClassNotFoundException {
    Class<?> caller = Reflection.getCallerClass();
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

java.lang包下的Class<?>对象中的静态方法forName(String className),这里的className参数就是我们传递的对象的全类名,该方法会返回给我们一个Class<?>对象。

第三种方式(源码):通过对象.getClass()方法获取Class对象

public final native Class<?> getClass();

Object对象中的getClass()方法,返回一个Class<?>对象。

示例代码

package com.xue.util;

import com.xue.domain.Employee;

/**
 * 反射
 */
public class ReflexionUtil {
    public static void main(String[] args) {
        try {
            // 1.通过对象的全类名,获取Class对象
            Class<?> clazz = Class.forName("com.xue.domain.Employee");
            System.out.println(clazz);
            // 2.通过类名.class获取Class对象
            Class<Employee> employeeClass = Employee.class;
            System.out.println(employeeClass);
            // 3.通过对象.getClass()方法获取Class对象
            Class<? extends Employee> aClass = new Employee().getClass();
            System.out.println(aClass);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
"C:\Program Files\Java\jdk1.8.0_281\bin\java.exe"
class com.xue.domain.Employee
class com.xue.domain.Employee
class com.xue.domain.Employee

可以看到,通过反射,我们可以获取到Class对象。

当我们通过反射获取到Class对象以后,就可以进行获取该对象的一些属性和方法,执行该对象的方法,获取和设置对象的属性等一系列的操作。

反射获取构造方法

// 获取该对象的所有的公共的构造方法
Constructor<?>[] constructors = clazz.getConstructors();
// 获取单个该对象的指定的公共的构造方法,参数和构造方法中的形参一致
Constructor<?> constructor = clazz.getConstructor(String.class, Integer.class);
// 获取当前对象的所有构造方法
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
// 获取单个该对象的指定的构造方法,参数和构造方法中的形参一致
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(String.class, Integer.class);

示例代码

package com.xue.util;

import java.lang.reflect.Constructor;

/**
 * 反射
 */
public class ReflexionUtil {
    public static void main(String[] args) {
        try {
            // 通过对象的全类名,获取Class对象
            Class<?> clazz = Class.forName("com.xue.domain.Employee");
            // 获取该对象的所有的公共的构造方法
            Constructor<?>[] constructors = clazz.getConstructors();
            // 获取单个该对象的指定的公共的构造方法,参数和构造方法中的形参一致
            Constructor<?> constructor = clazz.getConstructor(String.class, Integer.class);
            // 获取当前对象的所有构造方法
            Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
            // 获取单个该对象的指定的构造方法,参数和构造方法中的形参一致
            Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(String.class, Integer.class);
            System.out.println("所有的公共的构造方法");
            for (Constructor con : constructors) {
                System.out.println(con);
            }
            System.out.println("指定的公共的构造方法");
            System.out.println(constructor);
            System.out.println("所有构造方法");
            for (Constructor declared : declaredConstructors) {
                System.out.println(declared);
            }
            System.out.println("指定的构造方法");
            System.out.println(declaredConstructor);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
"C:\Program Files\Java\jdk1.8.0_281\bin\java.exe"
所有的公共的构造方法
public com.xue.domain.Employee(java.lang.String,java.lang.Integer)
public com.xue.domain.Employee()
指定的公共的构造方法
public com.xue.domain.Employee(java.lang.String,java.lang.Integer)
所有构造方法
public com.xue.domain.Employee(java.lang.String,java.lang.Integer)
private com.xue.domain.Employee(java.lang.String,java.lang.String,java.lang.Integer,java.lang.String,java.lang.String,java.math.BigDecimal)
public com.xue.domain.Employee()
指定的构造方法
public com.xue.domain.Employee(java.lang.String,java.lang.Integer)

我们可以通过反射拿到对象的构造方法。
通过反射获取到该对象的构造方法以后,我们就可以创建该对象。

// 直接创建public对象
newInstance(Object ... initargs);
// 暴力反射 临时取消检查,再创建对象.(非public的)
setAccessible(boolean flag);

示例代码

package com.xue.util;

import java.lang.reflect.Constructor;

/**
 * 反射
 */
public class ReflexionUtil {
    public static void main(String[] args) {
        try {
            // 通过对象的全类名,获取Class对象
            Class<?> clazz = Class.forName("com.xue.domain.Employee");
            // 获取单个该对象的指定的公共的构造方法,参数和构造方法中的形参一致
            Constructor<?> constructor = clazz.getConstructor(String.class, Integer.class);
            // 获取单个该对象的指定的构造方法,参数和构造方法中的形参一致
            Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(String.class, Integer.class);
            // 获取该对象的无参构造方法
            Constructor<?> noConstructor = clazz.getConstructor();
            // 调用newInstance()方法,创建无参对象
            Object noInstance = noConstructor.newInstance();
            Object instance = constructor.newInstance("张三", 23);
            System.out.println("无参对象:" + noInstance);
            System.out.println("有参对象:" + instance);
            // 暴力反射,取消临时检查,创建对象
            declaredConstructor.setAccessible(true);
            Object newInstance = declaredConstructor.newInstance("张三", 23);
            System.out.println("暴力反射创建私有构造方法的对象:" + newInstance);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
"C:\Program Files\Java\jdk1.8.0_281\bin\java.exe"
无参对象:Employee{id='null', name='null', age=null, gender='null', post='null', salary=null}
有参对象:Employee{id='null', name='张三', age=23, gender='null', post='null', salary=null}
暴力反射创建私有构造方法的对象:Employee{id='null', name='张三', age=23, gender='null', post='null', salary=null}

调用newInstance()方法创建对象。
如果当前对象的构造方法是私有的,需要对获取的私有构造方法进行暴力反射,取消临时检查。然后才能创建对象。

操作Class类的成员变量的方法
// 获取所有的public修饰的成员变量
Field[] getFields();	
// 获取指定的public修饰的成员变量	
Field getField(String name);
// 获取所有的成员变量(包含private)
Field[] getDeclaredFields();
// 获取指定的成员变量(包含private)
Field getDeclaredField(String name);
// 获取"obj对象"中"当前Field成员变量"的值
Object get(Object obj);
// 把"obj对象"中"当前Field成员变量"的值设置为"value"
void set(Object obj, Object value);
其他方法
// 获取当前类的全路径类名
String getName()
// 获取当前类的类名
String getSimpleName()
// 获取当前类的属性名
String getName()
// 获取当前类所有的成员变量,包括修饰符和成员变量的全路径名称
Field[] getDeclaredFields()
// 获取成员变量以后,只获取当前成员变量的名称
String getName()
// 获取指定的成员变量的全路径名称
Field getDeclaredField(String name)

示例代码

package com.xue.util;

import com.xue.domain.Employee;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.UUID;

public class ReflexionUtil2 {
    public static void main(String[] args) {
        try {
            // 通过对象的全类名,获取Class对象
            Class<?> clazz = Class.forName("com.xue.domain.Employee");
            Employee employee = new Employee();
            employee.setId(UUID.randomUUID().toString());
            employee.setName("张三");
            employee.setAge(23);
            employee.setGender("男");
            employee.setPost("程序员");
            employee.setSalary(BigDecimal.valueOf(10000));
            System.out.println(employee);
            // 获取当前类的全路径类名
            String name = clazz.getName();
            System.out.println("全路径类名:" + name);
            // 获取当前类的类名
            String simpleName = clazz.getSimpleName();
            System.out.println("类名:" + simpleName);
            // 获取所有的成员变量
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                // 取消访问检查
                field.setAccessible(true);
                // 获取当前成员变量的名称
                String fieldName = field.getName();
                // 获取当前成员变量的值
                Object value = field.get(employee);
                System.out.println("key:" + fieldName + ";value:" + value);
                if ("name".equals(fieldName)) {
                    // 设置对象当前成员变量的属性值
                    field.set(employee, "李四");
                }
            }
            System.out.println(employee);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
"C:\Program Files\Java\jdk1.8.0_281\bin\java.exe"
Employee{id='ca75a29d-34b2-44d9-81dc-b72c91354845', name='张三', age=23, gender='男', post='程序员', salary=10000}
全路径类名:com.xue.domain.Employee
类名:Employee
key:id;value:ca75a29d-34b2-44d9-81dc-b72c91354845
key:name;value:张三
key:age;value:23
key:gender;value:男
key:post;value:程序员
key:salary;value:10000
Employee{id='ca75a29d-34b2-44d9-81dc-b72c91354845', name='李四', age=23, gender='男', post='程序员', salary=10000}

以上示例代码可以看出,通过反射我们可以获取到当前对象的属性和属性值,并且我们可以对当前对象的属性值进行操作。
当我们的类的属性是被private修饰时,我们在获取属性名称和属性值的时候,同样需要取消访问检查。这里和私有构造方法创建对象是一样的。

获取成员方法的方法
// 得到所有的public修饰的成员方法
Method[] getMethods();
// 得到指定的public修饰的成员方法
Method getMethod(String name, Class... parameterTypes);
// 得到所有的成员方法(包含私有)
Method[] getDeclaredMethods();
// 得到指定的成员方法(包含私有)
Method getDeclaredMethod(String name, Class... parameterTypes);
// 调用成员方法并运行
Object invoke(Object obj, Object... args);// 参数一:用obj对象调用该方法;参数二:调用方法传递的参数

示例代码

package com.xue.util;

import com.xue.domain.Employee;

import java.lang.reflect.Method;

public class ReflexionUtil3 {
    public static void main(String[] args) {
        try {
            Class<?> clazz = Class.forName("com.xue.domain.Employee");
            Employee employee = new Employee();
            System.out.println(employee);
            // 获取所有的public修饰的成员方法
            Method[] allMethods = clazz.getMethods();
            // 获取所有的成员方法(包含私有)
            Method[] allDeclaredMethods = clazz.getDeclaredMethods();
            // 获取指定public修饰的成员方法
            Method method = clazz.getMethod("setName", String.class);
            // 获取指定的成员方法(包含私有)
            Method declaredMethod = clazz.getDeclaredMethod("setName", String.class);
            // 获取该方法的方法名
            String methodName = method.getName();
            // 调用该方法
            declaredMethod.invoke(employee, "张三");
            System.out.println("方法名:" + methodName);
            System.out.println(employee);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
"C:\Program Files\Java\jdk1.8.0_281\bin\java.exe"
Employee{id='null', name='null', age=null, gender='null', post='null', salary=null}
方法名:setName
Employee{id='null', name='张三', age=null, gender='null', post='null', salary=null}

通过反射可以获取到该对象的所有的方法,包括私有的和公共的。还可以对获取到的方法进行调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值