欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

你真的了解Java反射吗?

程序员文章站 2024-03-23 14:55:16
...

什么是反射?

原文链接www.kunstudy.com

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

反射的优点?

  • 可以在程序运行的过程中,操作这些对象
  • 可以解耦,提高程序的可扩展性

Java代码在计算机中经历的三个阶段?

  1. 源代码阶段:.java被编译成*.class字节码文件。
  2. 类对象阶段:.class字节码文件被类加载器加载进内存,并将其封装成Class对象(用于在内存中描述字节码文件),Class对象将原字节码文件中的成员变量抽取出来封装成数组Field[],将原字节码文件中的构造函数抽取出来封装成数组Construction[],将成员方法封装成数组Method[]。当然Class类内不止这三个,还封装了很多,我们常用的就这三个。
  3. 运行时阶段:使用new创建对象的过程。

说白了,想要用到反射,必须获取该或者该对象的Class对象

获取Class对象的三种方式:

        Student student = new Student() ;
        //第一种方式
        Class<?> studentClass01 = Class.forName("com.kunstudy.pojo.Student");
        //第二种方式
        Class<Student> studentClass02 = Student.class;
        //第三种方式
        Class<? extends Student> studentClass03 = student.getClass();

通过三种方式获取的Class对象为同一对象.

常用的Class对象里面的方法:

  1. 获取成员变量
Field[] getFields()          //获取所有public修饰的成员变量
Field getField(String name)  //获取指定名称的public修饰的成员变量
Field[] getDeclaredFields()  //获取所有的成员变量,不考虑修饰符
Field getDeclaredField(String name)  //获取指定的成员变量,不考虑修饰符
  1. 获取构造方法
Constructor<?>[] getConstructors() //获取所有public修饰的构造函数
Constructor<T> getConstructor(类<?>... parameterTypes)  //获取指定的public修饰的构造函数
Constructor<?>[] getDeclaredConstructors()  //获取所有的构造函数,不考虑修饰符
Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)  //获取指定的构造函数,不考虑修饰符
  1. 获取成员方法
Method[] getMethods()           //获取所有public修饰的成员方法
Method getMethod(String name, 类<?>... parameterTypes) //获取指定名称的public修饰的成员方法
Method[] getDeclaredMethods()  //获取所有的成员方法,不考虑修饰符
Method getDeclaredMethod(String name, 类<?>... parameterTypes) //获取指定名称的成员方法,不考虑修饰符
  1. 获取全类名
String getName()
  1. 注解相关
public Annotation[] getAnnotations()  //返回此元素上的注释。

Field:成员变量

  • 设置值 void set(Object obj, Object value)
  • 获取值 get(Object obj)
  • 忽略访问权限修饰符的安全检查 setAccessible(true):暴力反射

测试相关方法

定义一个测试实体对象

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {
    private int id ;
    String name ;
    protected String address ;
    public String sex ;
    private User (int id) {
        this.id  = id ;
    }
}

在主方法中测试属性相关的方法

public static void main(String[] args) throws NoSuchFieldException {
        User user = new User() ;
        user.setId(1);
        user.setName("张三");
        user.setAddress("郑州");
        user.setSex("男");
        //获取Class对象
        Class<? extends User> userClass = user.getClass();
        //测试成员变量相关的方法
        //获取所有public修饰的成员变量
        Field[] fields = userClass.getFields();
        System.out.println("获取到的所有public修饰的成员变量如下所示:");
        for (Field field : fields) {
            System.out.println(field.getName() );
        }
        System.out.println("获取指定名称的public修饰的成员变量:");
        Field sex = userClass.getField("sex");
        System.out.println(sex.getName());
        System.out.println("获取所有成员变量,不考虑修饰符:");
        Field[] declaredFields = userClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField.getName());
        }
        System.out.println("获取指定的成员变量,不考虑修饰符:");
        Field name = userClass.getDeclaredField("name");
        System.out.println(name.getName());
    }
测试结果输出如下:
获取到的所有public修饰的成员变量如下所示:
sex
获取指定名称的public修饰的成员变量:
sex
获取所有成员变量,不考虑修饰符:
id
name
address
sex
获取指定的成员变量,不考虑修饰符:
name
进程已结束,退出代码为 0

测试构造方法

public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        User user = new User() ;
        user.setId(1);
        user.setName("张三");
        user.setAddress("郑州");
        user.setSex("男");
        //获取Class对象
        Class<? extends User> userClass = user.getClass();
        //测试构造方法
        Constructor<?>[] constructors = userClass.getConstructors();
        System.out.println("所有public修饰的构造方法如下:");
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
        System.out.println("获取所有构造方法,不考虑修饰符:");
        Constructor<?>[] declaredConstructors = userClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }
        System.out.println("获取指定无参构造方法");
        Constructor<? extends User> constructor = userClass.getConstructor();
        System.out.println(constructor.newInstance());
        System.out.println("获取指定的有参构造方法:");
        Constructor<? extends User> constructor1 = userClass.getConstructor(Integer.class, String.class, String.class, String.class);
        User user1 = constructor1.newInstance(2, "小刘", "上海", "男");
        System.out.println(user1);
        System.out.println("获取私有的构造方法");
        Constructor<? extends User> declaredConstructor = userClass.getDeclaredConstructor(Integer.class);
        declaredConstructor.setAccessible(true);//设置Accessible为true,暴力反射(不然通过私有构造函数常见对象会抛异常)
        System.out.println(declaredConstructor.newInstance(3));//利用私有构造函数创建成功
    }
输出结果:
所有public修饰的构造方法如下:
public com.kunstudy.pojo.User()
public com.kunstudy.pojo.User(java.lang.Integer,java.lang.String,java.lang.String,java.lang.String)
获取所有构造方法,不考虑修饰符:
public com.kunstudy.pojo.User()
public com.kunstudy.pojo.User(java.lang.Integer,java.lang.String,java.lang.String,java.lang.String)
private com.kunstudy.pojo.User(java.lang.Integer)
获取指定无参构造方法
User(id=null, name=null, address=null, sex=null)
获取指定的有参构造方法:
User(id=2, name=小刘, address=上海, sex=男)
获取私有的构造方法
User(id=3, name=null, address=null, sex=null)

测试方法对象:

  public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        User user = new User() ;
        user.setId(1);
        user.setName("张三");
        user.setAddress("郑州");
        user.setSex("男");
        User user1 = new User(2,"里斯","上海","女") ;
        //获取Class对象
        Class<? extends User> userClass = user.getClass();
        System.out.println("获取所有的public方法");
        Method[] methods = userClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("获取指定的public方法");
        Method getName = userClass.getMethod("getName");
        Object invoke = getName.invoke(user);//通过invoke(Obj)指定对象去调用这个方法
        Object invoke1 = getName.invoke(user1);
        System.out.println(invoke);//张三
        System.out.println(invoke1);//里斯
    }
获取所有的public方法
public boolean com.kunstudy.pojo.User.equals(java.lang.Object)
public java.lang.String com.kunstudy.pojo.User.toString()
public int com.kunstudy.pojo.User.hashCode()
public java.lang.String com.kunstudy.pojo.User.getAddress()
public java.lang.String com.kunstudy.pojo.User.getName()
public void com.kunstudy.pojo.User.setName(java.lang.String)
public java.lang.Integer com.kunstudy.pojo.User.getId()
public void com.kunstudy.pojo.User.setAddress(java.lang.String)
public void com.kunstudy.pojo.User.setId(java.lang.Integer)
public void com.kunstudy.pojo.User.setSex(java.lang.String)
public java.lang.String com.kunstudy.pojo.User.getSex()
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
获取指定的public方法
张三
里斯

反射的应用案例

配置文件中配置类的全限定类名,以及每个属性的属性值,完成类的创建

在bean.properties文件中配置如下信息:

className=com.kunstudy.pojo.User
id=18
name=Liudehua
address=Shanghai
sex=nan
   public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        InputStream resourceAsStream = Kunstudy.class.getClassLoader().getResourceAsStream("bean.properties");
        Properties properties = new Properties();
        properties.load(resourceAsStream);
        String className = properties.getProperty("className");
        Integer id = Integer.parseInt(properties.getProperty("id")) ;
        String name = properties.getProperty("name");
        String address = properties.getProperty("address");
        String sex = properties.getProperty("sex");
        //根据全限定类名生成对象
        Class<?> aClass = Class.forName(className);
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Integer.class, String.class, String.class, String.class);
        User user = (User) declaredConstructor.newInstance(id, name, address, sex);
        System.out.println(user);
    }
输出结果:
User(id=18, name=Liudehua, address=Shanghai, sex=nan)

一个简单的Spring自动装配Bean的实例,其实真正的框架的底层也就是用的反射来完成的.不用通过new来实现对象的创建,想要更改对象的属性,在配置文件里面做简单的配置更改即可.