java反射知识总结大全:反射概念、反射机制原理及作用、类加载流程、Class类特点与常用方法、反射爆破实现对私有属性及方法的操作等

目录

反射的概念:

反射的作用:

反射基础引入:

反射机制原理及运行性能优化:

Class类:

JVM的内存中对象的存在形式吧:

java 程序在计算机中历经的三个阶段吧:

 Class类特点的梳理:

Class 类的常用方法:

得到Class 对象的各种方式(6种类型):

哪些类型有Class 类对象:

类加载各阶段:

类加载的流程图吧:

类加载的连接阶段 - 解析:

类加载的初始化阶段 - 静态变量的赋值动作和静态代码块中的语句,并进行合井:

通过反射获取类的结构信息:

反射爆破:

调用非public 类型有参构造器创建对象:

修改private等非public 类型相关属性:

调用非public 类型有参方法:


反射的概念:

       大家都知道,要让java程序能够运行,那么就得让java类要被java虚拟机加载。java类如果不被java虚拟机加载,是不能正常运行的。现在我们运行的所有的程序都是在编译期的时候就已经知道了你所需要的那个类的已经被加载了。
        java的反射机制是在编译并不确定是哪个类被加载了,而是在程序运行的时候才加载、探知、自审。使用在编译期并不知道的类。这样的特点就是反射。

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

反射的作用:

        假如我们有两个程序员,一个程序员在写程序的时候,需要使用第二个程序员所写的类,但第二个程序员并没完成他所写的类。那么第一个程序员的代码能否通过编译呢?这是不能通过编译的。
利用Java反射的机制,就可以让第一个程序员在没有得到第二个程序员所写的类的时候,来完成自身代码的编译。

java的反射机制它知道类的基本结构,这种对Java类结构探知的能力,我们称为java类的“自审”。
大家都用过Jcreator和eclipse。当我们构建出一个对象的时候,去调用该对象的方法和属性的时候。一按点,编译工具就会自动的把该对象能够使用的所有的方法和属性全部都列出来,供用户进行选择。这就是利用了Java反射的原理,是对我们创建对象的探知、自审。

        其次,假设我们定义了很多类,有Animal、Person、Car… ,如果我想要一个Animal实例,那我就new Animal(),如果另一个人想要一个Person实例,那么他需要new Person(),当然,另一个说,我只要一个Car实例,于是它要new Car()…这样一来就导致,每个用户new的对象需求不相同,因此他们只能修改源代码,并重新编译才能生效。这种将new的对象写死在代码里的方法非常不灵活,因此,为了避免这种情况的方法,java提供了反射机制,我们不再需要去修改执行文件,也不需要再次去编译执行文件,也就是说不需要中断主服务(加载的类信息可以在系统运行过程中动态添加),我们只需要把这个新写的类编译一下,然后就可以直接使用即可。

反射基础引入:

那通过以上对java 反射的概念及作用的理解,让我们通过一段代码来看看反射的具体实现操作:

package fanshe.com.reflection.question;

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

//反射问题的引入
@SuppressWarnings({"all"})
public class ReflectionQuestion {
    public static void main(String[] args) throws IOException, ClassNotFoundException,
            InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        // 根据配置文件 re.properties 指定信息,创建Cat 对象并调用方法hi

        // 传统方法 new ->对象 调用方法
       /* Cat cat = new Cat();
        cat.hi(); */

        // 我们尝试着试一试-> 理解反射
        // 1. 使用Properties 类,可以读写配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String  classfullpath = properties.get("classfullpath").toString();
        String method = properties.get("method").toString();
        System.out.println("classfullpath="+classfullpath);
        System.out.println("method="+method);

        //2. 创建对象
       // Cat cat2 = new fanshe.com.Cat();// new classfullpath 字符串类型,这样做是不行的,传统的方法无法做到

        // 这样的需求在学习框架时特别多,即通过外部文件配置,在不修改源代码的情况下,
        // 来控制程序,也符合设计模式的 opc 原则(开闭原则:不修改源码来扩展功能)

        // 3.使用反射机制解决
        //(1) 加载class 类
        Class cls = Class.forName(classfullpath);
        //(2) 通过得到加载的类 fanshe.com.Cat 的对象实例
        Object o = cls.newInstance();
        System.out.println("o的运行类型"+o.getClass());// 得到运行类型
        //(3) 通过 cls 得到你加载的类 fanshe.com.Cat 的 methodName"hi" 的方法对象
        // 即在反射中,可以把方法视为对象(java 万物皆对象的思想)
       // Cat cat = (Cat)o;
       // cat.hi(); 这样是不对的,因为在需求时你并不知道方法名就是hi(),万一是其他的那就错了
        Method method1 = cls.getMethod(method);// 方法也可以是一个对象
        //(4) 通过 method1 来调用方法,即通过方法对象来调用方法
        System.out.println("=========================================");
        method1.invoke(o);// 传统方法: 对象.方法(); 反射机制: 方法.invoke(对象);
    }
}

让我们来看看 src\\re.properties 以及Cat 类里的内容:

classfullpath=fanshe.com.Cat
method=cry
package fanshe.com;

public class Cat {

   private String name ="招财猫";
   public int age =10;

   public Cat(){
   }

   public Cat(String name){
       this.name=name;
   }
   public void hi(){// 常用方法
      // System.out.println("hi"+name);
   }

   public void cry(){// 添加一个cry方法要求在不修改源码的情况下,调用调用该方法
       // 只需修改配置文件 hi为cry 即可
       System.out.println(name+" 喵喵叫...");
   }
}

那么通过以上与传统方法的比较,我们可以发现通过反射我们可以在不更改源代码的基础上调用类的属性及方法,虽然以上操作不能充分体现,那让我们来进一步学习吧。

反射机制原理及运行性能优化:

下面让我们来初步认识一下反射机制调用类创建对象、调用方法、获得public属性的例子:

package fanshe.com.reflection.question;

/**
 * 1.反射机制允许程序在执行期借助于 Reflection API取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及
 * 方法。反射在设计模式和框架底层都会用到。
 * 2.加载完类之后,在堆中就产生了一个 Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得
 * 到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为:反射
 */

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

/**
 * 反射相关的主要类:
 * 1.java.lang. Class:代表一个类, Class对表示某个类加载后在堆中的对象
 * 2.java.lang. reflect. Method:代表类的方法 Method对象表示某个类的方法
 * 3.java.lang. reflect. Field:代表类的成员变量
 * 4.java.lang. reflect. Constructor:代表类的构造方法
 * 这些类在java.lang.reflection
 */
@SuppressWarnings({"all"})
public class Reflection01 {
    public static void main(String[] args) throws Exception{
        // 1. 使用Properties 类,可以读写配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String  classfullpath = properties.get("classfullpath").toString();
        String method = properties.get("method").toString();
        System.out.println("classfullpath="+classfullpath);
        System.out.println("method="+method);

        // 3.使用反射机制解决
        //(1) 加载class 类
        Class cls = Class.forName(classfullpath);
        //(2) 通过得到加载的类 fanshe.com.Cat 的对象实例
        Object o = cls.newInstance();
        System.out.println("o的运行类型"+o.getClass());// 得到运行类型
        //(3) 通过 cls 得到你加载的类 fanshe.com.Cat 的 methodName"hi" 的方法对象
        // 即在反射中,可以把方法视为对象(java 万物皆对象的思想)

        Method method1 = cls.getMethod(method);// 方法也可以是一个对象
        //(4) 通过 method1 来调用方法,即通过方法对象来调用方法
        System.out.println("=========================================");
        method1.invoke(o);// 传统方法: 对象.方法(); 反射机制: 方法.invoke(对象);

        //java.lang. reflect. Field:代表类的成员变量
        // 得到name 字段
      //  Field name = cls.getField("name");报错,因为不能得到私有属性
        Field age = cls.getField("age");
        System.out.println(age.get(o)+"\n\n\n");// 传统写法: 对象.成员变量,反射:成员变量对象.get(对象)

        //java.lang. reflect. Constructor:代表类的构造方法
        Constructor constructor = cls.getConstructor();//()中可以指定构造器参数的类型,没写则返回无参构造器
        System.out.println(constructor+"\n\n");
        Constructor constructor1 = cls.getConstructor(String.class);
        //这里传入的 String.class 就是String 类的Class对象
        System.out.println(constructor1);
    }
}

这里为什么只说了关于public 属性及无参方法的调用呢,关于这些我当然会在后面为大家详细解说,我们只是先初步理解一下反射机制的原理。

这里告诉大家一个关于反射调用方法的缺点,调用效率远不如传统方法,那该如何提高效率呢,下面一个例子将为大家介绍:

package fanshe.com.reflection.question;

import fanshe.com.Cat;

import java.lang.reflect.Method;

// 测试反射调用的性能及优化方案
@SuppressWarnings({"all"})
public class Reflection02 {
    public static void main(String[] args) throws Exception{
        m1();
        m2();
    }
    // 普通方法调用hi
    public static void m1(){
        Cat cat = new Cat();
        long start = System.currentTimeMillis();
        for (int i=0;i<90000000;i++){
            cat.hi();
        }
        long end = System.currentTimeMillis();
        System.out.println("传统方法调用时间:"+(end-start)+"毫秒");
    }
    // 反射方法调用hi
    public static void m2() throws Exception {
        Class cls = Class.forName("fanshe.com.Cat");// 知道在哪里这样也可以的
        Object o = cls.newInstance();
        Method method = cls.getMethod("hi");
        method.setAccessible(true);// 关闭访问检查
        long start = System.currentTimeMillis();
        for (int i=0;i<90000000;i++){
           method.invoke(o);
        }
        long end = System.currentTimeMillis();
        System.out.println("反射方法调用时间:"+(end-start)+"毫秒");
    }
    /**
     * 通过以上结果可以看出时间上的差距还是很大的
     * 反射调用优化-关闭访问检查
     * 1. Method 和Field、Constructor 对象都有setAccessible()方法
     * 2. setAccessible 的作用是启动和禁用访问安全的开关
     * 3. 参数为true表示反射的对象在使用时取消访问检查,提高反射的效率
     * 参数值为false 则表示反射的对象执行访问检查
     * 因此对以上代码进行改进, method.setAccessible(true); 可以发现执行效率有所提高
     */
}

Class类:

在学习Class类之前我们先来看看

JVM的内存中对象的存在形式吧:

 这是java 面向对象的知识大家不要忘了哦。

另外我们再来看看

java 程序在计算机中历经的三个阶段吧:

 Class类特点的梳理:

package fanshe.com.Class_;

import fanshe.com.Cat;

/**
 * 对Class类特点的梳理
 *1. Class是类,因此也继承 Object类[类图]
 * 2 Class类对象不是new出来的,而是系统创建的演示
 * 3.对于某个类的 Class类对象,在内存中只有一份,因为类只加载一次[演示]
 * 4.每个类的实例都会记得自己是由哪个 Class实例所生成
 * 5.通过 Class可以完整地得到一个类的完整结构通过一系列API
 * 6. Class对象是存放在堆的
 * 7.类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括方法代码,
 * 变量名,方法名,访问权等等 )
 */
public class Class01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //1. Class是类,因此也继承 Object类[类图]

        //2 Class类对象不是new出来的,而是系统创建的演示
        //(1)传统new 对象
        Cat cat = new Cat();
        /*
        ClassLoader 类
         public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
          }
         */
        //(2)反射方式
        Class cls1 = Class.forName("fanshe.com.Cat");
         /*
        ClassLoader 类,仍是通过 ClassLoader 类加载CatClass 对象
         public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
          }
         */
        //3.对于某个类的 Class类对象,在内存中只有一份,因为类只加载一次[演示]
        // 从上面追源码的过程中就可以发现,另外证明:
        Class cls2 = Class.forName("fanshe.com.Cat");
        System.out.println(cls1.hashCode()+"\n"+cls2.hashCode());// 可以发现两个对象的hashCode 相同

        // 4.每个类的实例都会记得自己是由哪个 Class实例所生成
        System.out.println(cls1.getClass()+"\n"+cls2.getClass());

        // 5.通过 Class可以完整地得到一个类的完整结构通过一系列API
        //  6. Class对象是存放在堆的
        // 7.类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括方法代码,
        // 变量名,方法名,访问权等等 )

    }
}

Class 类的常用方法:

package fanshe.com.Class_;

import fanshe.com.Car;

import java.lang.reflect.Field;

// 演示Class 类的常用方法
public class Class02 {
    private static Class<?> cls;

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        String classAllPath ="fanshe.com.Car";
        //1. 获取到Car 类对应的Class对象
        // <?> 表示不确定的Java类型
        Class<?> cls = Class.forName(classAllPath);
        //2. 输出cls
        System.out.println(cls);// 显示 cls 是哪一个类的Class 对象  fanshe.com.Car
        System.out.println(cls.getClass());//输出其运行类型 java.lang.Class
        //3.得到包名
        System.out.println(cls.getPackage().getName());// 得到包名: fanshe.com
        //4. 得到全类名
        System.out.println(cls.getName());//fanshe.com.Car
        //5.通过 cls 来创建一个对象实例
        Car car =(Car) cls.newInstance();// 此时创建的对象其运行类型为 Car
        System.out.println(car);
        //6.通过反射得到属性 brand
        Field brand = cls.getField("brand");
        System.out.println(brand.get(car));// 不能为私有属性
        //7. 通过反射给属性赋值
        brand.set(car,"宝马跑车");
        System.out.println(brand.get(car));
        //8. 我希望得到所有的属性
        Field[] fields = cls.getFields();// 得到了一个数组
        for (Field f : fields){
            System.out.println(f.getName()+" = "+f.get(car));
        }
        /*
        输出:
        brand = 宝马跑车
        price = 1000000
        color = 黑色
         */
    }
}

让我们看看Car类中的内容:

package fanshe.com;

public class Car {
    public String brand="玛莎拉蒂";
    public int price=1000000;
    public String color="黑色";

    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + '\'' +
                ", price=" + price +
                ", color='" + color + '\'' +
                '}';
    }
}

得到Class 对象的各种方式(6种类型):

package fanshe.com.Class_;

import fanshe.com.Car;

// 演示得到Class 对象的各种方式(6)
public class GetClass {
    public static void main(String[] args) throws ClassNotFoundException {
        /**
         * 1.前提:已知一个类的全类名,且该类在类路径下,可通过 Class类的静态方法
         *  forName()获取,可能抛出 ClassNotFoundException,实例: Class cls1 =
         *  Class.forName( "java.lang. Cat");
         * 应用场景:多用于配置文件读取类全路径,加载类
         */
        String classAllPath = "fanshe.com.Car";
        Class<?> cls1 = Class.forName(classAllPath);
        System.out.println("cls1=" + cls1);

        /**
         * 2.前提:若已知具体的类,通过类的 class获取,该方式最为安全可靠,程序性能最高
         * 实例: Class cls2 = Cat.class
         * 应用场景:多用于参数传递,比如通过反射得到对应构造器对象
         */
        Class cls2 = Car.class;
        System.out.println("cls2=" + cls2);

        /**
         * 3.前提:已知某个类的实例,调用该实例的 getClass()方法获取Class对象,实例
         *  Class cls3 = 对象.getClass();// 运行类型
         * 应用场景:通过创建好的对象,获取 Class对象
         */
        Car car = new Car();
        Class cls3 = car.getClass();
        System.out.println("cls3=" + cls3);

        /**
         * 4.其他方式, 通过类加载器(4)种,来获取到类的Class 对象
         *  ClassLoader cl= 对象.getClass().getClassLoader();
         *  Class cls4 = cl.loadClass(类的全类名”);
         */
        ClassLoader cl = car.getClass().getClassLoader();
        Class<?> cls4 = cl.loadClass(classAllPath);
        System.out.println("cls4=" + cls4);

        // 其实 cls1,cls2,cls3,cls4 是同一个对象,因为:对于某个类的 Class类对象,在内存中只有一份,因为类只加载一次
        //验证
        System.out.println("cls1->"+cls1.hashCode()+"\ncls2->"+cls2.hashCode()+"\ncls3->"
                +cls3.hashCode()+"\ncls4->"+cls4.hashCode());

        //另外还有两种特殊的方式
        /**
         * 5.基本数据类型(int,char,boolean,float,double,byte,long,short) 按如下方式得到Class 类对象
         * Class cls = 基本数据类型.class;
         */
        Class<Integer> integerClass = int.class;
        Class<Boolean> booleanClass = boolean.class;
        Class<Byte> byteClass = byte.class;
        System.out.println(integerClass+"\n"+booleanClass+"\n"+byteClass);
        // 自动装箱的过程

        /**
         * 6.基本数据类型的包装类,可以通过 .type 得到Class 类对象
         * Class cls = 包装类.TYPE;
         */
        Class<Double> type = Double.TYPE;
        Class<Character> type1 = Character.TYPE;
        Class<Integer> type2 = Integer.TYPE;
        System.out.println(type+"\n"+type1+"\n"+type2);

        // 另外 integerClass 和 type2 是同一个对象,原因还是类只加载一次
        System.out.println(integerClass.hashCode()+"\n"+type2.hashCode());
    }
}

哪些类型有Class 类对象:

除了上面的基本数据类型及其封装类的Class类对象,还有那些类型可以有Class类对象呢?

package fanshe.com.Class_;

import java.io.Serializable;

//演示那些类型有Class 类对象
public class AllTypeClass {
    public static void main(String[] args) {
        Class<String> cls1 = String.class;//外部类
        Class<Serializable> cls2 = Serializable.class;//接口
        Class<Integer[]> cls3 = Integer[].class;//一维数组
        Class<float[][]> cls4 = float[][].class;//二维数组
        Class<Deprecated> cls5 = Deprecated.class;//注解
        // 枚举
        Class<Thread.State> cls6 = Thread.State.class;
        Class<Long> cls7 = long.class;
        Class<Void> cls8 = void.class;
        Class<Class> cls9 = 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);
        System.out.println(cls8);
        System.out.println(cls9);
    }
}

类加载各阶段:

那让我们先来看看

类加载的流程图吧:

 当然这里还有另一张简洁易懂的图:

类加载的连接阶段 - 解析:

package fanshe.com.ClassLoad_;

/**
 * 1.加载阶段
 * JVM在该阶段的主要目的是将字节码从不同的数据源(可能是class文件、也可能是jar包,甚至网络)
 * 转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang. Class对象
 *  2.连接阶段
 *  JVM会在该阶段对静态变量,分配内存井默认初始化(对应数据类型的默认初始
 * 值,如0、0L、null、 false等)这些变量所使用的内存都将在方法区中进行分配
 *  3.初始化
 */
//我们说明一下一个类加载的连接阶段 - 准备

/**另外简单说一下类加载的连接阶段 - 解析
 * 虚拟机将常量池内的符号引用替换为直接引用的过程
 */
public class ClassLoad01 {
    public static void main(String[] args) {

    }
}

class A{
    //属性-成员变量-字段
    // 分析 类加载的链接阶段 - 准备 属性是如何处理的
    // 1. n1 是实例变量,不是静态变量,与类加载无关,即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 = 3;
}

类加载的初始化阶段 - 静态变量的赋值动作和静态代码块中的语句,并进行合井:

package fanshe.com.ClassLoad_;

/**
 *Initialization(初始化)
 * 1.到初始化阶段,才真正开始执行类中定义的jav程序代码,此阶段是执行
 * <clinit>()方法的过程
 * 2.<clinit>()方法是由译器按语句在源文件中出现的顺序,依次自动收集类中的所有
 * 静态变量的赋值动作和静态代码块中的语句,并进行合井
 * 3.虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果
 * 多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方
 * 法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕[debug源码]
 */
// 演示类加载的初始化阶段 - 静态变量的赋值动作和静态代码块中的语句,并进行合井
public class ClassLoad02 {
    public static void main(String[] args) {
       // 分析:
        // 1. 加载B类,并生成 B的class 对象
        // 2. 连接 num =0;
        // 3. 初始化
        /**
         * 依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合井
         *
         *      clinit(){
         *       System.out.println("B 的静态代码块被执行");
         *         num = 300;
         *         num = 100;
         *         }
         *         合并:  num = 100  因为按代码顺序收集合并
         */
        System.out.println(B.num+"\n\n\n");//使用类的静态属性 - 类会被加载
        B b = new B();//也要进行类加载的
    }
}

class B{
    static {
        System.out.println("B 的静态代码块被执行");
        num = 300;
    }
    static int num = 100;
    public B(){
        System.out.println("B 的构造器被执行!!!");
    }
}

通过反射获取类的结构信息:

接下来我就为大家解决通过反射获取非public 属性、方法等信息:

package fanshe.com.ClassLoad_;

import org.junit.jupiter.api.Test;

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

/**
 * 通过反射获取类的结构信息
 * 第一组:java.lang. Class类(常用)
 * 1. getName获取全类名
 * 2. getSimpleName:获取简单类名
 * 3. getFields:获取所有 public修饰的属性,包含本类以及父类的
 * 4. getDeclaredFields:获取本类中所有属性
 * 5. getMethods:获取所有 public修饰的方法,包含本类以及父类的
 * 6. getDeclaredMethods:获取本类中所有方法
 * 7. getConstructors:获取所有 public修饰的构造器,只包含本类的
 * 8. getDeclaredConstructors:获取本类中所有构造器
 * 9. getPackage:以 Package形式返回包信息
 * 10.getSuperClass:以 Class形式返回父类信息
 * 11.getInterfaces:以 Class[]形式返回接口信息
 * 12.getAnnotations:以 Annotation[]形式返回注解信息
 *第二组:java.lang. reflect. Field类
 * 1. getModifiers:以int形式返回修饰符
 * [说明:默认修饰符是0, public是1, private是2, protected是4,
 *  static是8, final是16, public(1)+ static(8)=9]
 * 2. getType:以 Class形式返回类型
 * 3. getName返回属性名
 * 第三组:java.lang.reflect.Method类
 * 1. getModifiers:以int形式返回修饰符[
 * 说明:默认修饰符是0 public是1, private是2, protected是4
 *  static是8, final是16]
 * 2 getReturnType:以 Class形式获取返回类型
 * 3. getName返回方法名
 * 4. getParameterTypes:以 Class[]返回参数类型数组
 * 第四组:java.lang.reflect.Constructor类
 * 1. getModifiers:以int形式返回修饰符[
 * 说明:默认修饰符是0 public是1, private是2, protected是4
 * static是8, final是16]
 * 2. getName返回方法名
 * 3. getParameterTypes:以 Class[]返回参数类型数组
 */
// 演示如何使用反射获取类的结构信息
public class ClassLoad03 {
    public static void main(String[] args) {

    }

    // 第一组API
    @Test
    public void api01() throws Exception {
        Class<?> personsCls = Class.forName("fanshe.com.ClassLoad_.Persons");
        // 1. getName获取全类名
        System.out.println(personsCls.getName());
       // 2. getSimpleName:获取简单类名
        System.out.println(personsCls.getSimpleName());
      //  3. getFields:获取所有 public修饰的属性,包含本类以及父类的
        Field[] fields = personsCls.getFields();
        for (Field field : fields) {//快捷键 fields.for+回车
            System.out.println("本类及父类的属性="+field.getName());
        }
        //  4. getDeclaredFields:获取本类中所有属性
        Field[] fields1 = personsCls.getDeclaredFields();
        for (Field field : fields1) {
            System.out.println("本类属性="+field.getName());
        }
        //  5. getMethods:获取所有 public修饰的方法,包含本类以及父类的(包括Object类,即所有父类)
        Method[] methods = personsCls.getMethods();
        for (Method method : methods) {
            System.out.println("本类及父类方法="+method.getName());
        }
        //  6. getDeclaredMethods:获取本类中所有方法
        Method[] methods1 = personsCls.getDeclaredMethods();
        for (Method method : methods1) {
            System.out.println("本类方法="+method.getName());
        }
        //  7. getConstructors:获取所有 public修饰的构造器,只包含本类的
        Constructor<?>[] constructors = personsCls.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println("本类及父类构造器="+constructor.getName());
        }
        //  8. getDeclaredConstructors:获取本类中所有构造器
        Constructor<?>[] constructors1 = personsCls.getDeclaredConstructors();
        for (Constructor<?> constructor : constructors1) {
            System.out.println("本类构造器="+constructor.getName());
        }
        //  9. getPackage:以 Package形式返回包信息
        System.out.println(personsCls.getPackage());
      //  10.getSuperClass:以 Class形式返回父类信息
        System.out.println(personsCls.getSuperclass());
      //  11.getInterfaces:以 Class[]形式返回接口信息
        Class<?>[] interfaces = personsCls.getInterfaces();
        for (Class<?> Interface : interfaces) {
            System.out.println(Interface);
        }
        //  12.getAnnotations:以 Annotation[]形式返回注解信息
        Annotation[] annotations = personsCls.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
    }

    //第二、三、四、组API
    @Test
    public void api02() throws Exception{
        Class<?> personsCls = Class.forName("fanshe.com.ClassLoad_.Persons");
        // getDeclaredFields:获取本类中所有属性
        Field[] fields1 = personsCls.getDeclaredFields();
        for (Field field : fields1) {
            System.out.println("本类属性="+field.getName()+"该属性的修饰符:"+field.getModifiers()+
                    "该属性的类型:"+field.getType());
        }
        // getDeclaredMethods:获取本类中所有方法
        Method[] methods1 = personsCls.getDeclaredMethods();
        for (Method method : methods1) {
            System.out.println("本类方法="+method.getName()+"该方法的访问修饰符"+method.getModifiers()+
                    "该方法返回类型"+method.getReturnType());
            Class<?>[] types = method.getParameterTypes();
            for (Class<?> type : types) {
                System.out.println("该方法参数类型"+type);
            }
        }
        //getDeclaredConstructors:获取本类中所有构造器
        Constructor<?>[] constructors1 = personsCls.getDeclaredConstructors();
        for (Constructor<?> constructor : constructors1) {
            System.out.println("本类构造器="+constructor.getName()+"该构造器访问修饰符"+constructor.getModifiers());
            Class<?>[] types = constructor.getParameterTypes();
            for (Class<?> type : types) {
                System.out.println("该构造器的形参类型:"+type);
            }
        }
    }
}

class Per{
    public String hobby ;
    public void hi(){

    }
    public Per(){
        hobby ="打篮球";
    }
}

interface IA{}
interface IB{}
@Deprecated
@SuppressWarnings({"all"})
class Persons extends Per implements Serializable,IA,IB {
    //属性
    public static String name ;
    protected int age ;
    String job ;
    private double sal ;

    private Persons(){
        name ="jack";
        age = 18;
        job ="程序员";
        sal =6000;
    }
    public Persons(String name){

    }
    protected Persons(int age){

    }
    //方法
    public String m1(String name,int age){
        return "tom";
    }
    protected void m2(int age){

    }
    int m3(String job){
        return 0;
    }
    private void m4(double sal){

    }
}

反射爆破:

接下来为大家演示如何调用非public 类型有参构造器创建对象、修改private等非public 类型相关属性以及调用非public 类型有参方法

调用非public 类型有参构造器创建对象:

package fanshe.com.ClassLoad_;
//反射爆破实例

import java.lang.reflect.Constructor;

/**
 * 通过反射创建对象
 * 1.方式一:调用类中的 public修饰的无参构造器
 * 2.方式二:调用类中的指定构造器
 * 3. Class类相关方法
 *  newInstance:调用类中的无参构造器,获取对应类的对象
 *  getConstructor(Class.clazz)根据参数列表,获取对应的public构造器对象
 *  getDeclaredConstructor(Class...clazz)根据参数列表,获取对应的构造器对象
 * 4. Constructor类相关方法
 *  setAccessible:暴破
 *  newInstance(Object.ob):调用构造器
 */
public class ClassLoad04 {
    public static void main(String[] args) throws Exception{
        //1. 先获取到User Class对象
        Class<?> userCls = Class.forName("fanshe.com.ClassLoad_.User");
        //2. 通过public 的无参构造器创建对象
        Object o = userCls.newInstance();
        System.out.println(o);
        //3. 通过public 的有参构造器创建对象
        Constructor<?> constructor = userCls.getConstructor(String.class);//返回public 构造器
        /*
        此时的 constructor 对象就是
         public User(String name){//有参 public 构造器
             this.name=name;
        }
         */
        Object tom = constructor.newInstance("tom");
        System.out.println("tom="+tom);
        //4. 通过非public 的有参构造器创建对象
        //4.1 得到 private 的构造器
        Constructor<?> constructor1 = userCls.getDeclaredConstructor(int.class, String.class);
        //4.2 创建对象
        constructor1.setAccessible(true);//爆破【暴力破解】 -> 使用反射可以访问private(非public)构造器
        //会破坏封装性
        Object o1 = constructor1.newInstance(19, "风煞");
        System.out.println("o1="+o1);
    }
}

class User{
    private int age = 10;
    private String name="jack";

    public User(){//无参 public 构造器

    }
    public User(String name){//有参 public 构造器
        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 + '\'' +
                '}';
    }
}

修改private等非public 类型相关属性:

package fanshe.com.ClassLoad_;
//反射爆破操作属性

import java.lang.reflect.Field;

/**
 * 通过反射访问类中的成员
 * 1.根据名获取 Field对象
 *  Field clazz getDeclared Field(属性名)
 * 2.暴破:setAccessible(true)://是 Field
 * 3.访同
 * Field对象.set(o,值) o表示对象
 * System.out.println(age.get(o));//返回age 属性的值, o表示对象
 * 4.如果是静态属性,则set和get中的参数o,可以写成nul
 */
public class ClassLoad05 {
    public static void main(String[] args) throws Exception{
        //1.得到Student类对应的Class类对象
        Class<?> cls = Class.forName("fanshe.com.ClassLoad_.Student");
        //2.创建对象
        Object o = cls.newInstance();// o的运行类型就是Student 类
        System.out.println(o.getClass());
        //3.使用反射得到age 属性
        Field age = cls.getField("age");
        age.set(o,19);
        System.out.println(o);
        System.out.println(age.get(o));//返回age 属性的值

        //4. 使用反射操作name属性
        Field name = cls.getDeclaredField("name");
        name.setAccessible(true);//对属性爆破
        name.set(null,"风煞");//因为name 是 static 属性,因此 o 也可以是null
        System.out.println(o);
        System.out.println(name.get(null));//获取属性值,要求name 为static属性
    }
}

class Student{
    public int age =10;
    private static String name ;

    public Student(){

    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +",name="+name+
                '}';
    }
}

调用非public 类型有参方法:

package fanshe.com.ClassLoad_;

import java.lang.reflect.Method;

/**
 * 通过反射访问类中的成员
 * 调用方法
 * 1.根据方法名和参数列表获取 Method方法对象: Method m = cls.getDeclaredMethod(方法名,xx.class);
 * 2.获取对象:0 Object clazz = newInstance();
 * 3.爆破 m.setAccessible(true);// 非public ,想调用
 * 4.访问: Object returnValue = invoke(实参列表);
 * 5.注意:如果是静态方法,则 invoke参数o,可以写成null
 */
public class ClassLoad06 {
    public static void main(String[] args) throws Exception{
        //1. 得到Boss 类对应的Class 类对象
        Class<?> cls = Class.forName("fanshe.com.ClassLoad_.Boss");
        //2. 创建对象
        Object o = cls.newInstance();
        //3.调用public hi()方法
        //3.1 得到hi()方法对象
        Method hi = cls.getMethod("hi",String.class);//只能拿到public 修饰的方法
        //3.2 调用
        hi.invoke(o,"风煞");

        //4. 调用 say()方法
        // 4.1 得到say()方法对象
        Method say = cls.getDeclaredMethod("say", int.class, String.class, char.class);//OK
        //4.2 调用
        say.setAccessible(true);//爆破 非public修饰
        Object invoke = say.invoke(null, 26, "jack", '男');// 只有被static 修饰时才能null
        System.out.println(invoke);

        //5. 返回值类型,在反射中如果方法有返回值,统一返回Object 类,但它的运行类型和定义的返回值类型相同
        Object invoke1 = say.invoke(o, 100, "王五", '男');
        System.out.println("运行类型为:"+invoke1.getClass());
    }
}

class Boss{
    public int age;
    private static String name;

    public Boss(){

    }

    private static String say(int n, String s, char c){
        return n+" "+s+" "+c;
    }

    public void hi(String s){
        System.out.println("hi "+s);
    }
}

到这里呢,我关于java反射的内容就介绍完了,有问题还望各位大佬评论指出哦,谢谢!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风煞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值