一文彻底了解Java反射(为了反序列化漏洞挖掘基础准备)

反序列化基础之反射机制
Java反射机制
java反射机制的引入

为什么要存在反射机制?如果没有反射机制那么Java这门语言就不会动态化,也就不会有所谓的Spring SpringMvc SpringBoot等等这些框架。

我们都知道使用SpringBoot的时候需要大量的配置文件,比如Log4j配置文件,数据库配置文件等等。

比如说如果我们更改了数据库配置文件,账号密码以及数据源等等。我们就可以直接操作数据库了。他底层其实就是使用的反射。

现在看如下的一个例子。

在这里我创建了一个properties配置文件。

classfullpath=com.reflex.pojo.Cat
method=cry

在pojo包下创建了一个Cat类。

package com.reflex.pojo;

public class Cat {
    private String name = "招财猫";
    public void hi(){
        System.out.println("hi");
    }

    public void cry(){
        System.out.println("cry");
    }
}

我们在想如果需要调用Cat类的hi方法,我们传统的方式是不是直接就new对象然后调用hi方法即可。

但是我们如果可以通过配置文件的方式来进行调用的话,只需要修改配置文件直接可以调用的话,那么是不是就不需要更改代码了,这样就实现了解耦。

我们在想我们是不是可以通过IO流来读取这个配置文件来new这个对象是不是也是一样的?

如果这么想的话那你绝对是疯了。

 				Properties properties = new Properties();
        properties.load(new FileInputStream("src/main/resources/re.properties"));
        String classfullpath = properties.get("classfullpath").toString();
        String method = properties.get("method").toString();
        System.out.println("classfullpath=" + classfullpath);
        System.out.println("method=" + method);

例如上面这个代码,如果我们通过IO流的方式读取这个配置文件,拿到这个类名,返回的字符串你确定是可以new的吗?

所以我们就需要用到反射。

看如下代码,我们通过反射进行实例化类以及调用方法,这样的话我们就不需要更改代码,只需要更改配置文件他就可以调用不同类的不同方法。

//使用反射机制解决
//(1)加载类 返回一个Class类型的对象
Class<?> cls = Class.forName(classfullpath);
//(2) 通过cls得到你加载的类 com.reflex.Cat的对象实例
Object o = (Cat) cls.newInstance();
System.out.println(o.getClass()); //运行类型
System.out.println("=======================");
//(3)通过cls得到你加载的类 com.reflex.pojo.Cat的MethodName的方法对象
// 即: 在反射中 可以吧方法视为对象 (万物皆对象的概念)
Method method1 = cls.getMethod(method);
//(4)通过method1 调用方法 即通过方法的对象来调用方法
method1.invoke(o); //传统方法 对象.方法 反射机制里面 方法.invoke(对象)
反射的原理

Java对象调用分为3个阶段。

1.编译阶段

在这一阶段我们的类通过javac进行编译生成Class字节码文件,我们可以看到字节码中存在我们的构造器以及成员变量方法等等。
在这里插入图片描述
2.加载阶段

在加载阶段也是最重要的阶段,当我们去new对象的时候他就会导致加载字节码,他会将字节码加载到内存的堆中,他会生成Class类对象,这个Class类对象中包含成员变量,方法,构造器等等。那么他去加载的时候就用到了类的加载器也就是ClassLoader。在这里就体现了反射机制,他底层会把成员变量映射为Field[] fields,构造器映射为constructor[] cs,方法映射为:Method[] ms,那么当我们加载完成之后就会生成对象。该对象知道他是属于那个Class对象的。从这句话可以看到这个对象他是知道我和那个Class对象是有映射关系的。

所以可以通过这个对象可以拿到他所关联的Class对象了。如果我们拿到Class对象就可以做很多事情了,比如创建对象,调用方法,修改属性等等。

借用博客的如下图
在这里插入图片描述
3.运行阶段

反射相关的类
小试牛刀

1.getField方法:获取属性

Properties properties = new Properties();
properties.load(new FileInputStream("src/main/resources/re.properties"));
String classfullpath = properties.get("classfullpath").toString();
String method = properties.get("method").toString();
System.out.println("classfullpath=" + classfullpath);
System.out.println("method=" + method);


Class<?> cls = Class.forName(classfullpath);
Object o = (Cat) cls.newInstance();
//getField不能得到私有的属性
Field nameField = cls.getField("age");
System.out.println(nameField.get(o)); //传统写法 对象.成员变量 反射: 成员变量.get(对象)

在这里插入图片描述
2.getConstructor方法:获取构造器

Properties properties = new Properties();
properties.load(new FileInputStream("src/main/resources/re.properties"));
String classfullpath = properties.get("classfullpath").toString();
String method = properties.get("method").toString();
System.out.println("classfullpath=" + classfullpath);
System.out.println("method=" + method);


Class<?> cls = Class.forName(classfullpath);
Object o = (Cat) cls.newInstance();
//java.lang.reflect.Constructor:代表类的构造器  Constructor对象表示构造器
Constructor<?> constructor = cls.getConstructor(); //()中可以指定构造器参数类型 如果不写 那么返回无参构造器
System.out.println(constructor);

在这里插入图片描述3.getConstructor方法:获取有参构造方法

Properties properties = new Properties();
properties.load(new FileInputStream("src/main/resources/re.properties"));
String classfullpath = properties.get("classfullpath").toString();
String method = properties.get("method").toString();
System.out.println("classfullpath=" + classfullpath);
System.out.println("method=" + method);

Class<?> cls = Class.forName(classfullpath);
Object o = (Cat) cls.newInstance();
Constructor<?> constructor1 = cls.getConstructor(String.class); //这里传入String.class 就是String类的对象
System.out.println(constructor1);

在这里插入图片描述

反射性能调优

这里测试一个通过传统方式调用方法,一个通过反射方式调用方法看这两个的时间对比。

package com.reflex.question;

import com.reflex.pojo.Cat;

import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

public class ReflexQuestion2 {
    public static void main(String[] args) throws Exception{
        c1();
        c2();
    }
    //传统调用
    public static void c1(){
        Cat cat = new Cat();
        long l = System.currentTimeMillis();
        for (int i = 0; i < 90000000 ; i++) {
            cat.hi();
        }
        long l1 = System.currentTimeMillis();
        System.out.println("传统调用耗时:" + (l1 - l));
    }

    //反射调用
    public static void c2() throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {

        Class<?> cls = Class.forName("com.reflex.pojo.Cat");
        Object o = cls.newInstance();
        Method method = cls.getMethod("hi");

        long l = System.currentTimeMillis();
        for (int i = 0; i < 90000000 ; i++) {
            method.invoke(o);
        }
        long l1 = System.currentTimeMillis();
        System.out.println("反射调用耗时:" + (l1 - l));
    }
}

明显反射是比较慢的。
在这里插入图片描述

解决方案

Method和Field以及Constructor都有setAccessible方法。开启启动和禁用安全检查的开关。

参数为true的时候表示使用时取消访问检查,提高反射的效率,参数值为false的时候则表示反射的对象执行安全检查。

当设置为true的时候发现时间明显减少了。
在这里插入图片描述

Class类
Class类图

1.Class也是一个类,因此也会继承Object类。
在这里插入图片描述
通过反射方式创建对象。这里debug的时候记得把上一步new Cat的那个对象哪一步需要注销掉,因为如果不注销的话那么就只会加载一次,第二次再进行加载的时候就不会到loadClass方法了。总的来说就是同一个类只会加载一次。

  //1.传统的new对象的方法
//  Cat cat = new Cat();
  //2.通过反射方式创建对象
  Class<?> forName = Class.forName("com.reflex.pojo.Cat");

在这里插入图片描述然后跟进forName0方法。这里暂时不要管,然后返回到forName方法,继续跟进去forName0方法。
在这里插入图片描述
可以看到这里就调用了ClassLoader的loadClass方法。
在这里插入图片描述

Class类对象内存中只存在一份

3.对于某个类的Class类对象在内存中只有一份,因为类只加载一次。

无论你new多少次他在内存中只会在内存中存在一份。

4.通过Class对象可以完整的得到一个类的完整结构。

这里的API就相当于说我们上面使用的forName getMethod getConstructor 等等这些API

5.Class对象是存放在堆上面的。

Class常用方法
public static void main(String[] args) throws Exception{
    String classAllPath = "com.reflex.pojo.Car";

    //1.获取到Car类 对应的Class对象
    //<?>表示不确定的Java类型
    Class<?> cls = Class.forName(classAllPath);
    //2.输出Class
    System.out.println(cls); //显示cls对象 是那个类的Class对象 com.reflex.pojo.Car
    System.out.println(cls.getClass()); //输出cls对象的运行类型
    //3.得到包名
    System.out.println(cls.getPackage().getName()); //获取包名 获取Class这个对象 对应的那个类在那个包下
    //4.获取到全类名
    System.out.println(cls.getName()); //获取全类名 获取Class这个对象对应的那个类 的类名
    //5.通过cls创建对象实例
    Car car = (Car) cls.newInstance();
    System.out.println(car);
    //6.通过反射获取到属性 brand
    Field brand = cls.getField("brand");
    Object o = brand.get(car);
    System.out.println(o);

    //7.通过反射给属性赋值
    brand.set(car,"宝马");
    System.out.println(brand.get(car));
    //8.得到所有的属性
    Field[] fields = cls.getFields();
    for (Field field : fields) {
        System.out.println(field.get(car));
        System.out.println(field);
    }
}
获取Class类对象的四种方式
public static void main(String[] args) throws Exception{
    //1.Class.forName("")
    String className = "com.reflex.pojo.Car";
    Class<?> cls1 = Class.forName(className);
    System.out.println(cls1);
    //2.类名.class 应用场景多用于参数的传递
    Class<?> cls2 = Car.class;
    System.out.println(cls2);

    //3.如果已知对象的实例,就是这个对象已经被new出来了,可以调用他的getClass方法获取Class对象
    Car car = new Car();
    Class<? extends Car> cls3 = car.getClass();
    System.out.println(cls3);

    //4.通过类加载器来获取到类的Class对象
    //(1)先得到类加载器 car
    ClassLoader classLoader = car.getClass().getClassLoader();
    //(2)通过类加载器得到Class对象
    Class<?> cls4 = classLoader.loadClass(className);
    System.out.println(cls4);

    //cls1 cls2 cls3 cls4 其实是同一个对象
    System.out.println(cls1 == cls2);
    System.out.println(cls1 == cls3);
    System.out.println(cls1 == cls4);

    //5.基本数据类型(int char boolean float double type long short) 按如下方式获取Class对象
    Class<Integer> cls5 = int.class;
    Class<Boolean> cls6 = boolean.class;
    System.out.println(cls5); //int
    System.out.println(cls6); //boolean

    //6.基本数据类型对应的包装类 可以通过.TYPE得到Class类对象
    Class<Integer> cls7 = Integer.TYPE;
    System.out.println(cls7);
}
那些类型有Class对象

1.外部类和内部类

2.接口: interface

3.数组

4.枚举

5.annotation注解

6.基本数据类型

7.void

 public static void main(String[] args) {
        Class<String> cls1 = String.class; //外部类
        Class<Serializable> cls2 = Serializable.class; //接口
        Class<Integer[]> cls3 = Integer[].class; //数组
        Class<Override> cls4 = Override.class; //注解
        Class<Thread.State> cls5 = Thread.State.class; //枚举
        Class<Void> cls6 = void.class; //void
        Class<Class> cls7 = Class.class; //class
        System.out.println(cls1);
        System.out.println(cls2);
        System.out.println(cls3);
        System.out.println(cls4);
        System.out.println(cls5);
        System.out.println(cls6);
        System.out.println(cls7);
}

在这里插入图片描述

类加载
1.静态加载

编译时加载相关的类,如果没有则报错,依赖性太强

例如如下代码,这里的Dog这个类是不存在的,当我们去运行的时候他会直接在编译的时候就会报错,这就体现出了编译时会加载相关的类。

public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    System.out.println("输入key:");
    String next = scanner.next();
    switch (next){
        case "1":
            Dog dog = new Dog();
            dog.cry();
            break;
        case "2":
            System.out.println("ok");
        default:
            System.out.println("no");
    }
}

在这里插入图片描述

2.动态加载

运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性

例如如下代码: 当我们输入其他数字的时候他会输出no,当我们输入2的时候他才会报错,也就是说他在编译器是没有报错的,只会在运行时用这个类的时候才会报错。

 public static void main(String[] args) throws Exception {
        Scanner scanner = new Scanner(System.in);
        System.out.println("输入key:");
        String next = scanner.next();
        switch (next){
            case "1":
//                Dog dog = new Dog();
//                dog.cry();
                break;
            case "2":
                Class<?> cls = Class.forName("Person"); //加载一个Person类
                Object o = cls.newInstance();
                Method hello = cls.getMethod("hello");
                hello.invoke(o);
                System.out.println("ok");
            default:
                System.out.println("no");
        }

在这里插入图片描述

总的来说就是静态类加载他会在编译时进行加载,动态类加载会在运行时进行类的加载。

Dog类是静态加载的,因此必须编写Dog类。

Person类是动态加载的,所以没有编写Person类也不会报错。

所以通过反射进行类加载都是动态类加载的。

类什么时候会加载

1.当创建对象的时候(new)也就是new对象的时候。(静态加载)

2.当子类被加载的时候,父类也会被加载

3.调用类中的静态成员的时候

4.通过反射 (动态加载)

类加载流程

1.源码进行编译得到字节码文件 javac test.java

2.通过类加载器进行加载字节码文件,这个过程是通过类加载器来完成的。

3.连接。验证,准备,解析等等。

4.初始化

加载阶段

JVM在该阶段的主要目的就是将字节码从不同的数据源(可能是Class文件,也可能是jar包,甚至是网络)转换为二进制字节码流加载到内存中。并生成一个代表该类的Class对象。

连接阶段-验证

1.目的为了确保Class文件的字节流中包含的信息复合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

2.包括:文件格式验证(是否以魔数oxcafebabe开头) 元数据验证,字节码验证和符号引用验证。

3.可以考虑使用-Xverify:none 参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间。

连接阶段-准备

1.Jvm会在该阶段对静态变量,分配内存并默认初始化(对应数据类型的默认初始化 如0 0L null false等) 这些变量所使用的内存都将在方法区中进行分配。

class A{
    //属性-成员变量-字段
    //加载连接阶段-准备 属性如何处理
    //1.n1 是实例属性 不是一个静态变量 因此在准备阶段是不会分配内存的。
    //2.n2 是静态变量,分配内存 n2是默认初始化 0 而不是20
    //3.n3 是static final是常量,他和类变量不一样,因为一但赋值就不会变化了。 n3 = 30
    public int n1 = 10;
    public static int n2 = 20;
    public static final int n3 = 30;
}
连接阶段-解析

1.虚拟机将常量池的符号引用替换为直接引用的过程。

初始化

在初始化阶段的时候他会依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并合并。

这就是为什么我们new对象或者调用静态变量的时候会导致静态方法直接触发的原因。调用静态变量也会导致类的加载。

class B{
    static {
        System.out  .println("B 静态代码块被执行");
        num = 300;
    }
    static int num = 100;
    public B(){ //构造器
        System.out.println("构造器被执行");
    }
}
public class ClassLoader03 {
    public static void main(String[] args) {
        //1.加载B类 并生成B对应的Class对象
        //2.连接 num = 0
        //3.初始化阶段
            //4.依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并合并
        /**
         * clinit(){
         *    System.out  .println("B 静态代码块被执行");
         *    //num = 300;
         *    num = 100;
         * }
         * 合并: num = 100
         *
         */
        System.out.println(B.num); //100 这里调用静态变量会导致类加载 总的来说类加载会调用静态代码块中的代码
    }
}
反射获取类结构信息
第一组
package com.reflex.question;

import org.junit.Test;

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

public class ReflecitonUtils {
    public static void main(String[] args) throws Exception{

    }
    //第一组
    @Test
    public void method1() throws ClassNotFoundException, NoSuchFieldException {
        //getName: 获取全类名
        Class<?> cls = Class.forName("com.reflex.question.Person");
        String Classname = cls.getName();
        System.out.println("全类名:" + Classname);
        System.out.println("================================");
        //获取简单类名getSimpleName()
        System.out.println("简单类名:" + cls.getSimpleName());
        System.out.println("================================");
        //getFields:获取所有public修饰的属性 包含本类以及父类
        Field[] fields = cls.getFields();
        for (Field field : fields) {
            System.out.println("获取所有public修饰的属性:" + field.getName());
        }
        //getDeclaredFields:获取本类中的所有属性
        System.out.println("================================");
        Field[] declaredFields = cls.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("本类中的所有属性:" + declaredField);
        }
        System.out.println("================================");
        //getMethods:获取所有public修饰的方法 包含本类以及父类的。
        Method[] methods = cls.getMethods();
        for (Method method : methods) {
            System.out.println("获取所有public修饰的方法:" + method);
        }
        System.out.println("================================");
        //getDeclaredMethods:获取本类中的所有方法
        Method[] declaredMethods = cls.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("本类中的所有方法:" + declaredMethod);
        }
        System.out.println("================================");
        //getConstructors:获取所有public的构造方法
        Constructor<?>[] constructors = cls.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println("所有public构造方法:" + constructor);
        }
        System.out.println("================================");
        //getDeclaredConstructors:获取本类中的所有构造方法
        Constructor<?>[] declaredConstructors = cls.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("获取所有的构造方法:" + declaredConstructor);
        }
        System.out.println("================================");
        //getPackage 以Package形式返回 包信息
        Package aPackage = cls.getPackage();
        System.out.println("包信息:" + aPackage);

        System.out.println("================================");
        //getSuperClass: 以Class形式返回父类信息
        Class<?> superclass = cls.getSuperclass();
        System.out.println("父类的Class对象:" + superclass);

        //getInterfaces:以Class[]形式返回接口信息
        Class<?>[] interfaces = cls.getInterfaces();
        for (Class<?> interfaceClass : interfaces) {
            System.out.println("接口的Class对象:" + interfaceClass);
        }
        System.out.println("================================");
        //getAnnotations: 以Annotation[] 形式返回注解信息
        Annotation[] annotations = cls.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println("所有注解信息:" + annotation);
        }
    }
}


class A{
    public String hobby;
    public A(){

    }
    public A(String hobby){
        this.hobby = hobby;
    }
}

class Person extends A{
    //属性
    public String name;
    protected int age;
    String job;
    private Double sal;

    public Person(){

    }
    public Person(String name){
        this.name = name;
    }


    //方法
    public void m1(){

    }
    protected void m2(){

    }
    void m3(){

    }
    private void m4(){

    }
}

输出:

全类名:com.reflex.question.Person
================================
简单类名:Person
================================
获取所有public修饰的属性:name
获取所有public修饰的属性:hobby
================================
本类中的所有属性:public java.lang.String com.reflex.question.Person.name
本类中的所有属性:protected static int com.reflex.question.Person.age
本类中的所有属性:java.lang.String com.reflex.question.Person.job
本类中的所有属性:private java.lang.Double com.reflex.question.Person.sal
================================
获取所有public修饰的方法:public void com.reflex.question.Person.m1()
获取所有public修饰的方法:public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
获取所有public修饰的方法:public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
获取所有public修饰的方法:public final void java.lang.Object.wait() throws java.lang.InterruptedException
获取所有public修饰的方法:public boolean java.lang.Object.equals(java.lang.Object)
获取所有public修饰的方法:public java.lang.String java.lang.Object.toString()
获取所有public修饰的方法:public native int java.lang.Object.hashCode()
获取所有public修饰的方法:public final native java.lang.Class java.lang.Object.getClass()
获取所有public修饰的方法:public final native void java.lang.Object.notify()
获取所有public修饰的方法:public final native void java.lang.Object.notifyAll()
================================
本类中的所有方法:public void com.reflex.question.Person.m1()
本类中的所有方法:protected void com.reflex.question.Person.m2()
本类中的所有方法:private void com.reflex.question.Person.m4()
本类中的所有方法:void com.reflex.question.Person.m3()
================================
所有public构造方法:public com.reflex.question.Person()
所有public构造方法:public com.reflex.question.Person(java.lang.String)
================================
获取所有的构造方法:public com.reflex.question.Person()
获取所有的构造方法:public com.reflex.question.Person(java.lang.String)
================================
包信息:package com.reflex.question
================================
父类的Class对象:class com.reflex.question.A
================================
第二组
Field类

1.getModifiers()方法 以int方式返回修饰符,默认修饰符为0,public是1,private是2,protected是4,static是8,final是16

Public(1) + static(8) = 9

Class<?> cls = Class.forName("com.reflex.question.Person");
Field[] declaredFields = cls.getDeclaredFields();
for (Field declaredField : declaredFields) {
    System.out.println("本类中的所有属性:" + declaredField + "该属性的修饰符为:" +declaredField.getModifiers());
}

输出:

本类中的所有属性:public java.lang.String com.reflex.question.Person.name该属性的修饰符为:1
本类中的所有属性:protected int com.reflex.question.Person.age该属性的修饰符为:4
本类中的所有属性:java.lang.String com.reflex.question.Person.job该属性的修饰符为:0
本类中的所有属性:private java.lang.Double com.reflex.question.Person.sal该属性的修饰符为:2

2.getType:以Class形式返回类型

Class<?> cls = Class.forName("com.reflex.question.Person");
Field[] declaredFields = cls.getDeclaredFields();
for (Field declaredField : declaredFields) {
    System.out.println("本类中的所有属性:" + declaredField + "该属性的修饰符为:"
            +declaredField.getModifiers() + "属性类型:" + declaredField.getType());
}

输出:

本类中的所有属性:public java.lang.String com.reflex.question.Person.name该属性的修饰符为:1属性类型:class java.lang.String
本类中的所有属性:protected static int com.reflex.question.Person.age该属性的修饰符为:12属性类型:int
本类中的所有属性:java.lang.String com.reflex.question.Person.job该属性的修饰符为:0属性类型:class java.lang.String
本类中的所有属性:private java.lang.Double com.reflex.question.Person.sal该属性的修饰符为:2属性类型:class java.lang.Double

3.getName返回属性名

Class<?> cls = Class.forName("com.reflex.question.Person");
Field[] declaredFields = cls.getDeclaredFields();
for (Field declaredField : declaredFields) {
    System.out.println("本类中的所有属性:" + declaredField + "该属性的修饰符为:"
            +declaredField.getModifiers() + "属性类型:" + declaredField.getType()
            + "属性名为:" + declaredField.getName());
}

输出:

本类中的所有属性:public java.lang.String com.reflex.question.Person.name该属性的修饰符为:1属性类型:class java.lang.String属性名为:name
本类中的所有属性:protected static int com.reflex.question.Person.age该属性的修饰符为:12属性类型:int属性名为:age
本类中的所有属性:java.lang.String com.reflex.question.Person.job该属性的修饰符为:0属性类型:class java.lang.String属性名为:job
本类中的所有属性:private java.lang.Double com.reflex.question.Person.sal该属性的修饰符为:2属性类型:class java.lang.Double属性名为:sal
Method类

1.getModifiers:以int形式返回修饰符,默认修饰符为0,public是1,private是2,protected是4,static是8,final是16

Public(1) + static(8) = 9

2.方法的返回类型 getReturnType()

3.方法的方法名 getName()

4.方法的参数类型 getParameterTypes()

 Method[] declaredMethods = cls.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("本类中所有方法:" + declaredMethod + "该方法的修饰符为:" + declaredMethod.getModifiers()
                    + "该方法的返回类型为:" + declaredMethod.getReturnType()
                    + "该方法的方法名为:" + declaredMethod.getName());
            Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
            for (Class<?> parameterType : parameterTypes) {
                System.out.println("该方法的参数类型为:" + parameterType);
            }
        }

输出:

本类中所有方法:public void com.reflex.question.Person.m1(java.lang.String)该方法的修饰符为:1该方法的返回类型为:void该方法的方法名为:m1
该方法的参数类型为:class java.lang.String
本类中所有方法:protected void com.reflex.question.Person.m2(int)该方法的修饰符为:4该方法的返回类型为:void该方法的方法名为:m2
该方法的参数类型为:int
本类中所有方法:private void com.reflex.question.Person.m4()该方法的修饰符为:2该方法的返回类型为:void该方法的方法名为:m4
本类中所有方法:void com.reflex.question.Person.m3()该方法的修饰符为:0该方法的返回类型为:void该方法的方法名为:m3
Constructor类

1.getModifers 以int形式返回修饰符

2.getName() 返回构造器名

3.getParameterTypes: 以Class[]返回参数类型数组

Constructor<?>[] declaredConstructors = cls.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("获取所有的构造器:" + declaredConstructor
                    + "获取构造器的权限修饰符:" + declaredConstructor.getModifiers() +
                    "获取构造器的名字:" + declaredConstructor.getName());
            Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
            for (Class<?> parameterType : parameterTypes) {
                System.out.println("该构造器的形参类型为:" + parameterType);
            }
        }

输出:

获取所有的构造器:public com.reflex.question.Person()获取构造器的权限修饰符:1获取构造器的名字:com.reflex.question.Person
获取所有的构造器:public com.reflex.question.Person(java.lang.String)获取构造器的权限修饰符:1获取构造器的名字:com.reflex.question.Person
该构造器的形参类型为:class java.lang.String
反射创建对象

1.方式一:调用类中的public修饰的无参构造器

2.方式二:调用类中的指定构造器

3.Class类相关方法

​ newInstance 调用类中的无参构造器,获取对应类的对象

​ getConstructor(Clazz…clazz):根据参数列表 获取对应的public构造器对象

​ getDecalaredConstructor(Class…clazz)根据参数列表 获取对应的构造器对象

4.Constructor类相关方法

​ setAccessible:爆破

​ newinstance(Object obj):调用构造器

例子:

package com.reflex.question;

import java.lang.reflect.Constructor;

public class ReflecCreateInstance {
    public static void main(String[] args) throws Exception{
        Class<?> cls = Class.forName("com.reflex.question.User");
        //1.通过publicd的无参构造器创建实例
        Object o = cls.newInstance(); //获取空参构造器
        System.out.println(o);

        //2.通过public的有参构造器创建实例
        Constructor<?> constructor = cls.getConstructor(String.class);
        Object o1 = constructor.newInstance("张三");
        System.out.println(o1);

        //3.通过非public的有参构造器创建实例
        Constructor<?> declaredConstructor = cls.getDeclaredConstructor(int.class, String.class);
        declaredConstructor.setAccessible(true);
            Object o2 = declaredConstructor.newInstance(1, "李四");
            System.out.println(o2);
    }
}

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

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


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

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}
反射操作属性

1.根据属性名获取Field对象

Field f = clazz对象.getDeclaredField(属性名)

2.访问私有属性

setAccessible(true) //f 就是field

3.访问

f.set(o,值) //o表示对象

f.get(o); //o表示对象

4.注意 如果是静态属性 则set和get中的参数o 可以写成null

package com.reflex.question;

import java.lang.reflect.Field;

public class ReflecField {
    public static void main(String[] args) throws Exception{
        Class<?> cls = Class.forName("com.reflex.question.Student");
        Object o = cls.newInstance();
        Field age = cls.getField("age");
        age.set(o,18);
        System.out.println(o);

        Field name = cls.getDeclaredField("name");
        name.setAccessible(true);
        name.set(null,"张三");
        System.out.println(name.get(null));
    }

}
class Student{
    public int age;
    private static String name;
    public Student(){

    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                '}';
    }
}
反射操作方法

1.根据方法名和参数列表获取Method方法对象:Method m = clazz.getDeclaredMethod(方法名,参数.clss)

2.获取对象:Object o = cls.newInstance();

3.访问私有属性: m.setAccessible(true)

4.访问:Object returnValue = m.invoke(o,实参列表)

5.注意:如果是静态方法,则invoke的参数为o 可以写成null

package com.reflex.question;

import java.lang.reflect.Method;

public class RefecAccessMethod {
    public static void main(String[] args) throws Exception{
        Class<?> cls = Class.forName("com.reflex.question.Boos");
        Object o = cls.newInstance();
        //调用public的方法
        Method hi = cls.getMethod("hi", String.class);
        hi.invoke(o,"张三");


        //调用私有方法
        Method say = cls.getDeclaredMethod("say", int.class, String.class, char.class);
        say.setAccessible(true);
        System.out.println(say.invoke(null, 100, "李四", '男'));
    }
}

class Boos{
    public int age;
    private static String name;
    public Boos(){

    }
    private static String say(int n,String s,char c){
        return n + "" + s + "" + c;
    }
    public void hi(String s){
        System.out.println("hi" + s);
    }
}

到这里基本上就结束了。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值