首页 > 编程语言 >Java反射机制

Java反射机制

时间:2023-01-14 23:45:05浏览次数:59  
标签:反射 Java String Class class reflect User 机制 public

概念

反射是Java的特征之一,是一种间接操作目标对象的机制

在JVM运行的时候会动态加载类,对于任意一个类都能获取到该类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就是Java的反射机制

阅读建议:

  • 如果您是Java反射机制的初学者,我觉得应该优先阅读示例反射常用API

反射机制

Java类在执行过程中的变化如下(借大佬的图一用

过程我是这样理解的:

Class类文件 ⇒ Class类字节码文件 ⇒ Class类对象 ⇒ Class类对象实例

在类加载器去加载一个类的时候,反射机制允许程序在执行期间借助ReflectionAPI取得任意类的内部信息(属性,方法),并能操作对象的属性及方法。

在加载完成后,在堆中就会产生一个Class类对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。

在运行时Java反射机制可以完成:

  1. 判断任意一个对象所属的类
  2. 构造任意类的对象
  3. 得到任意类所具有的属性和方法
  4. 调用任意一个对象的属性和方法
  5. 动态代理

反射相关的主要类:

  1. Java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
  2. Java.lang.reflect.Method:代表类的方法
  3. Java.lang.reflect.Field:代表类的成员变量
  4. Java.lang.reflect.Constructor:代表类的构造方法

示例

在使用Java反射之前,我们先来构造一个类User.java

package com.reflect;

public class User {
    public String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void Hello() {
        System.out.println(this.name + "'s age is " + this.age + "!");
    }
}

先按照正常思路去实现一个User类对象并调用Hello方法,再写一个Normal.java

package com.reflect;

public class Normal {
    public static void main(String[] args) {
        User user = new User("xiaoming", 18);
        user.Hello();
    }
}

然后我们再使用反射的方式去实现User类对象并调用Hello方法,写一个Reflect.java

package com.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class Reflect {
    public static void main(String[] args) throws Exception{
        Class clazz = Class.forName("com.reflect.User");
        Constructor constructor = clazz.getConstructor(String.class, int.class);
        Object xiaogao = constructor.newInstance("xiaogao", 18);
        Method hello = clazz.getMethod("Hello");
        hello.invoke(xiaogao);
    }
}

先来简单理解一下上述代码,在阅读过反射常用API后将有进一步的认识:

  • 通过Class.forName方法获取指定的Class类对象

    通过getConstructor方法获得类对象的构造器

    通过newInstance方法获得类对象实例

    通过getMethod方法获得类对象的方法

    通过invoke方法执行该类对象实例的该方法

反射常用API

Class类对象的获取

类的class属性

package com.reflect;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class clazz = User.class;
        System.out.println(clazz);
    }
}
// OUT:class com.reflect.User

这种方法是一种比较简单的方法,只需要知道Class的名称就可以

实例化对象的getClass()方法

package com.reflect;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        User xiaojun = new User("xiaojun", 18);
        Class clazz = xiaojun.getClass();
        System.out.println(clazz);
    }
}
// OUT:class com.reflect.User

这种方法是先获得了类对象实例然后获得Class类对象,所以用的比较少

Class.forName(String className)方法:动态加载类

package com.reflect;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class clazz = Class.forName("com.reflect.User");
        System.out.println(clazz);
    }
}
// OUT:class com.reflect.User

构造方法Constructor的获取

User类新增构造方法

public User() {
        this.name = "";
        this.age = 0;
    }
protected User(String name) {
        this.name = name;
        this.age = 0;
    }

getConstructors():返回public构造方法

返回了User类所有的public构造方法

package com.reflect;

import java.lang.reflect.Constructor;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class clazz = User.class;
        Constructor[] constructors = clazz.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
    }
}
// OUT:
// public com.reflect.User()
// public com.reflect.User(java.lang.String,int)

getConstructor(Class<?>…parameterTypes):返回匹配参数类型的public构造方法

返回了指定了参数类型的public构造方法

package com.reflect;

import java.lang.reflect.Constructor;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class clazz = User.class;
        Constructor xiaoming = clazz.getConstructor(String.class, int.class);
        System.out.println(xiaoming);
    }
}
// OUT:public com.reflect.User(java.lang.String,int)

getDeclaredConstructors():返回所有的构造方法

返回了所有的构造方法

package com.reflect;

import java.lang.reflect.Constructor;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class clazz = User.class;
        Constructor[] constructors = clazz.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
    }
}
// OUT:
// public com.reflect.User()
// public com.reflect.User(java.lang.String,int)
// protected com.reflect.User(java.lang.String)

getDeclaredConstructor(Class<?>…parameterTypes):返回匹配参数类型的构造方法

返回了指定了参数类型的构造方法

package com.reflect;

import java.lang.reflect.Constructor;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class clazz = User.class;
        Constructor xiaoming = clazz.getDeclaredConstructor(String.class);
        System.out.println(xiaoming);
    }
}
// OUT:protected com.reflect.User(java.lang.String)

对象实例的获取

通过Class对象的newInstance()方法

通过该种方法只能获得Class对象的无参构造方法并创建对象实例

package com.reflect;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class clazz = User.class;
        User user = (User)clazz.newInstance();
        user.Hello();
    }
}
// OUT:'s age is 0!

通过Constructor对象的newInstance()方法

通过getConstructor()方法获取Constructor对象可以获取到Class对象的特定构造方法,并创建对象实例

package com.reflect;

import java.lang.reflect.Constructor;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class clazz = User.class;
        Constructor xiaoming = clazz.getConstructor(String.class, int.class);
        User user = (User)xiaoming.newInstance("xiaoming", 20);
        user.Hello();
    }
}
// OUT:xiaoming's age is 20!

成员变量Field的获取

getFields():获取所有public修饰的成员变量

package com.reflect;

import java.lang.reflect.Field;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class clazz = User.class;
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
    }
}
// OUT:public java.lang.String com.reflect.User.name

getField(String name):获取指定名称的public修饰的成员变量

package com.reflect;

import java.lang.reflect.Field;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class clazz = User.class;
        Field name = clazz.getField("name");
        System.out.println(name);
    }
}
// OUT:public java.lang.String com.reflect.User.name

getDeclaredFields():获取所有的成员变量

package com.reflect;

import java.lang.reflect.Field;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class clazz = User.class;
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }
    }
}
// OUT:
// public java.lang.String com.reflect.User.name
// private int com.reflect.User.age

getDeclaredField():获取指定的成员变量

package com.reflect;

import java.lang.reflect.Field;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class clazz = User.class;
        Field name = clazz.getDeclaredField("age");
        System.out.println(name);
    }
}
// OUT:private int com.reflect.User.age

成员方法Method的获取

User类新增方法

public void Hello(int age) {
    System.out.println(this.name + "'s age is " + age + "!");
}
private void Hi(String name) {
    System.out.println(name + "'s age is " + this.age + "!");
}

getMethods():获取所有的public方法

获取包括自身和父类声明的public方法,以及实现的接口方法

package com.reflect;

import java.lang.reflect.Method;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class clazz = User.class;
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
    }
}

getMethod(String name, Class<?>…parameterTypes):获取指定名称的public方法

第一个参数为该public方法的名称,第二个参数为该方法的参数类型

package com.reflect;

import java.lang.reflect.Method;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class clazz = User.class;
        Method hello = clazz.getMethod("Hello", int.class);
        System.out.println(hello);
    }
}

getDeclaredMethods():获取所有方法

仅仅只是获取该类所声明的所有方法(不包括父类以及实现的接口方法)

package com.reflect;

import java.lang.reflect.Method;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class clazz = User.class;
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
    }
}

getDeclaredMethod(String name, Class<?>…parameterTypes):获取指定名称的方法

package com.reflect;

import java.lang.reflect.Method;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class clazz = User.class;
        Method hello = clazz.getDeclaredMethod("Hi", String.class);
        System.out.println(hello);
    }
}

invoke方法执行对象的目标方法

invoke方法位于java.lang.reflect.Method类中,用于执行某个对象的目标方法

public Object invoke(Object obj, Object... args)

第一个参数为类的实例,第二个参数为相应函数中的参数

User类新增静态方法

public static void Bye(String name, int age) {
    System.out.println(name + "'s age is " + age + "!");
}

如果调用的目标方法是普通方法,第一个参数是Class对象实例

package com.reflect;

import java.lang.reflect.Method;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class clazz = User.class; // 获取Class对象
        User user = (User)clazz.newInstance(); // 获取Class对象实例
        Method hello = clazz.getMethod("Hello", int.class);  // 获取Hello(int age)怕普通方法
        hello.invoke(user, 20); // 执行方法并传入参数
    }
}

如果调用的目标方法是静态方法,第一个参数就是Class对象(这里还可以使用null空置该参数,也可以执行当前Class对象的方法)

package com.reflect;

import java.lang.reflect.Method;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class clazz = User.class;  // 获取Class对象
        Method hello = clazz.getMethod("Bye", String.class, int.class);  // 获取Bye(String name, int age)静态方法
        hello.invoke(clazz, "xiaomei", 20); // 这里的第一个参数为Class对象
    }
}

通过反射构造Runtime类执行

我们先来正常使用Runtime类执行一条命令并弹出计算器

package com.reflect;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Runtime.getRuntime().exec("calc");
    }
}

接下来我们通过反射的方式来执行命令

package com.reflect;

import java.lang.reflect.Method;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class clazz = Class.forName("java.lang.Runtime");
        Object o = clazz.newInstance();
        Method getRuntime = clazz.getMethod("getRuntime");
        Method exec = clazz.getMethod("exec", String.class);
        exec.invoke(getRuntime.invoke(clazz), "calc");
    }
}

我们先通过forName方法获取Runtime类对象,然后再通过newInstance方法创造实例,结果发现在newInstance这里报了错!

经过读报错和查询我们发现Runtime类的构造方法是private权限的

当使用的类构造函数不是public的,或者没有无参构造函数的话,是不可以用newInstance方法直接创造实例的

通过查看getRuntime方法,发现该方法为静态方法,所以我们并不需要创建实例也可以执行该方法

故我们修改代码如下:

package com.reflect;

import java.lang.reflect.Method;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class clazz = Class.forName("java.lang.Runtime");
        Method getRuntime = clazz.getMethod("getRuntime");
        Method exec = clazz.getMethod("exec", String.class);
        exec.invoke(getRuntime.invoke(clazz), "calc");
    }
}

我们先获得Runtime对象的exec方法和Runtime方法,然后通过Class对象执行getRuntime方法获得返回的实例化对象后再执行exec方法

设置暴力访问权限

在利用反射机制时,有的时候会遇到私有private成员变量或方法,这个时候我们通过之前的方法则会报错

添加私有private构造方法:

    private User() {
        this.name = "private";
        this.age = 0;
    }

我们使用之前的反射获取该构造器并进行创建类对象实例

import java.lang.reflect.Constructor;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class<?> aClass = Class.forName("com.reflect.User");
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
        User xiaozhu = (User) declaredConstructor.newInstance();
        xiaozhu.Hello();
    }
}

这个时候我们就要想办法绕过私有权限来访问,通过调用AccessibleObject上的setAccessible方法来允许访问。

import java.lang.reflect.Constructor;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        Class<?> aClass = Class.forName("com.reflect.User");
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        User xiaozhu = (User) declaredConstructor.newInstance();
        xiaozhu.Hello();
    }
}

这里的Java.lang.reflect.AccessibleObject类是FieldMethodConstructor类对象的基类,它提供了将反射对象标记为在使用它时抑制默认Java语言访问控制检查的功能。

所以对于私有private的成员变量或方法,都可以通过setAccessible来恢复访问!

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectClass {
    public static void main(String[] args) throws Exception{
        // 获取私有private构造器创建实例化对象
        Class<?> aClass = Class.forName("com.reflect.User");
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        User xiaozhu = (User) declaredConstructor.newInstance();
        // 获取私有private成员变量age并修改
        Field field = aClass.getDeclaredField("age");
        field.setAccessible(true);
        field.set(xiaozhu, 8);
        // 获取私有private成员方法Hi(String name)并执行
        Method declaredMethod = aClass.getDeclaredMethod("Hi", String.class);
        declaredMethod.setAccessible(true);
        declaredMethod.invoke(xiaozhu, "xiaozhu");
    }
}

参考文章

JAVA安全基础(二)-- 反射机制 - 先知社区

大白话说Java反射:入门、使用、原理

Java安全|反射看这一篇就够了 - FreeBuf网络安全行业门户

标签:反射,Java,String,Class,class,reflect,User,机制,public
From: https://www.cnblogs.com/seizer/p/17052833.html

相关文章

  • java中的基本类型
    引入我们前面使用过了输出语句System.out.println();知道了它能够输出()里的东西但是它到底能够输出一些什么东西呢,或者直奔主题它能够输出什么类型呢可以尝试一下,如果我......
  • Java JDK1.8的安装详细教程
    转载:https://www.jb51.net/article/243119.htmjdk1.8又称jdk8.0,是目前相对比较稳定的版本,不建议下载最新的jdk版本,因为最新版的jdk不稳定,在Java的学习中可能会出现各种各......
  • spark任务报错java.io.IOException: Failed to send RPC xxxxxx to xxxx:xxx, but got
    ##日志信息如下```Attemptedtogetexecutorlossreasonforexecutorid17atRPCaddress192.168.48.172:59070,butgotnoresponse.Markingasslavelost.......
  • 【JavaScript】使用WdatePicker.js插件限选一个时间范围(例如一个月)
    需求:公司处理的业务数据比较大,单张表就是几十上百万的。如果不加入指定的条件,指定的时间,限定条数的查。经过多张表的关联查询sql执行速度将会变得特别慢。之前限定时间都是......
  • Java集合之LinkedList源码分析
    LinkedList文章目录​​LinkedList​​​​LinkedList介绍​​​​LinkedList的方法总结​​​​LinkedList源码分析​​​​GetElement​​​​RemoveElement​​​​......
  • JavaDoc
      网址:https://docs.oracle.com/en/java/javase/17/docs/api/   在base文件夹打开,cmd然后输入下面这一行 然后base文件夹会出现很多东西,点击进去会出现bas......
  • Java基础之 Integer 类源码分析
    Integer类源码说明Java中Integer是基本数据类型int的包装类。也就是每一个Integer对象包含一个int类型的属性,是抽象类Number类的子类,位于java.lang包下。部分源码:publicfi......
  • java CountDownLatch用法 主线程等待子线程执行完后再执行
    这里记录一下下面这种情况:主线程需要等待多个子线程执行完后再执行。我们先看一下下面的场景:packagecom.java4all.mypoint;importjava.util.concurrent.CountDownLatch;/*......
  • java Integer值比较
    所有相同类型的包装类对象之间的值比较,应该使用equals方法比较。先看个简单实例:publicstaticvoidmain(String[]args)throwsException{Integera=-121;......
  • 包机制
      1packagebase;23importjava.util.Date;4//importbase.Demo01;引入本地文件的格式,不包括src56publicclassDemo07{7publicstatic......