Java反射

93 阅读8分钟

一、概念

反射:是Java独有的技术。是Java技术显著的特点。

反射是指对于任何一个类,在"运行时"都可直接得到这个类全部成分。

在运行时,可直接得到这个类的构造器对象。(Constructor)

在运行时,可直接得到这个类的成员变量对象。(Field)

在运行时,可直接得到这个类的成员方法对象。(Method)

反射的核心思想和关键就是得到:编译以后的class文件对象。

反射是通过先得到编译以后的Class类对象:字节码文件。

然后才可得到类中的全部成分,进行一些功能设计。

反射为一个类的全部成分都设计了一个类型来代表这个对象:
    Class : 字节码文件的类型
    Constructor : 构造器的类型
    Field : 成员变量的类型
    Method : 方法的类型

反射提供了一个Class类型,就是可得到编译以后的class类对象。

HelloWorld.java -> javac -> HelloWorld.class

Class c = HelloWorld.class;

注意:反射是工作在运行时的技术,因为只有运行之后才会有class类对象。

小结:

反射的核心思想和关键就是得到:编译以后的class文件对象。

反射是在运行时获取类的字节码文件对象:然后可解析类中的全部成分。

二、为啥要用反射

1.可提高代码的灵活度,同时降低了代码的耦合性,体现了java的多态。反射是动态编译的代表。

2.在Java运行时环境中,对于任意一个类,可知道这个类有哪些属性和方法。对于任意一个对象,可调用它的任意一个方法。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制。

3.反射是的最主要的目的就是分析类的能力,或者是类的构造。换句话说,也就是分析类中有哪些构造函数、通过构造函数创建对象;类中有哪些方法,通过反射怎么调用这些方法。

在IDE中,在类名后输入.后,IDE就会自动补全出它的各种方法以及成员变量,这个就是利用反射实现的

三、反射使用步骤

1.获取Class类对象

1)Class.forName

若知道一个class的完整类名,可通过静态方法Class.forName()获取

Class cls = Class.forName("java.lang.String");

2)类名.Class

Class cls = String.class;

3)类的对象.getClass()

若有一个实例变量,可通过该实例变量提供的getClass()方法获取

String s = "Hello";
Class cls = s.getClass();

2.通过Class对象获取Constructor类对象、Method类对象、Field对象

/**


    反射中Class类型获取构造器提供了很多的API:
         1. Constructor getConstructor(Class... parameterTypes)
            根据参数匹配获取某个构造器,只能拿public修饰的构造器,几乎不用!
         2. Constructor getDeclaredConstructor(Class... parameterTypes)
            根据参数匹配获取某个构造器,只要申明就可定位,不关心权限修饰符,建议使用!
         3. Constructor[] getConstructors()
            获取所有的构造器,只能拿public修饰的构造器。几乎不用!!太弱了!
         4. Constructor[] getDeclaredConstructors()
            获取所有申明的构造器,只要你写我就能拿到,无所谓权限。建议使用!!
    小结:
        获取类的全部构造器对象: Constructor[] getDeclaredConstructors()
            -- 获取所有申明的构造器,只要你写我就能拿到,无所谓权限。建议使用!!
        获取类的某个构造器对象:Constructor getDeclaredConstructor(Class... parameterTypes)
            -- 根据参数匹配获取某个构造器,只要申明就可定位,不关心权限修饰符,建议使用!


 */
public class TestStudent {
    // 1. getConstructors:
    // 获取全部的构造器:只能获取public修饰的构造器。
    // Constructor[] getConstructors()
    @Test
    public void getConstructors(){
        // a.反射第一步先得到Class类对象
        Class c = Student.class ;
        // b.getConstructors():定位全部构造器,只能拿public修饰的!
        Constructor[] cons = c.getConstructors();
        // c.遍历这些构造器
        for (Constructor con : cons) {
            System.out.println(con.getName()+"===>"+con.getParameterCount());
        }
    }


    // 2.getDeclaredConstructors():
    // 获取全部的构造器:只要你敢写,这里就能拿到,无所谓权限是否可及。
    @Test
    public void getDeclaredConstructors(){
        // a.反射第一步先得到Class类对象
        Class c = Student.class ;
        // b.getDeclaredConstructors():定位全部构造器,只要申明了就可拿到
        Constructor[] cons = c.getDeclaredConstructors();
        // c.遍历这些构造器
        for (Constructor con : cons) {
            System.out.println(con.getName()+"===>"+con.getParameterCount());
        }
    }

Constructor的API:
     1. T newInstance(Object... init args)
            创建对象,注入构造器需要的数据。
     2. void setAccessible(true)
            修改访问权限,true代表暴力攻破权限,false表示保留不可访问权限(暴力反射)
小结:
    可通过定位类的构造器对象。
    如果构造器对象没有访问权限可通过:void setAccessible(true)打开权限
    构造器可通过T newInstance(Object... initargs)调用自己,传入参数!

反射_获取Field成员变量对象。

/**
    目标:反射获取成员变量: 取值和赋值。
    Field的方法:给成员变量赋值和取值
        void set(Object obj, Object value):给对象注入某个成员变量数据
        Object get(Object obj):获取对象的成员变量的值。
        void setAccessible(true);暴力反射,设置为可直接访问私有类型的属性。
        Class getType(); 获取属性的类型,返回Class对象。
        String getName(); 获取属性的名称。
 */
public class FieldDemo02 {
    @Test
    public void setField() throws Exception {
        // a.反射的第一步获取Class类对象
        Class c = Dog.class ;
        // b.定位name成员变量
        Field nameF = c.getDeclaredField("name");
        // c.为这个成员变量赋值!
        Dog taiDi = new Dog();
        nameF.setAccessible(true); // 暴力反射!
        /**
         * 参数一:被赋值的对象。
         * 参数二:该成员变量的值。
         */
        nameF.set(taiDi , "勇敢的泰迪");
        System.out.println(taiDi);
        // d.获取成员变量的值
        String value = nameF.get(taiDi)+"";
        System.out.println(value);
    }
}

获取Method方法对象

/**
    目标:反射——获取Method方法对象


    反射获取类的Method方法对象:
         1、Method getMethod(String name,Class...args);
             根据方法名和参数类型获得对应的方法对象,只能获得public的


         2、Method getDeclaredMethod(String name,Class...args);
             根据方法名和参数类型获得对应的方法对象,包括private的


         3、Method[] getMethods();
             获得类中的所有成员方法对象,返回数组,只能获得public修饰的且包含父类的


         4、Method[] getDeclaredMethods();
            获得类中的所有成员方法对象,返回数组,只获得本类申明的方法。


    Method的方法执行:
        Object invoke(Object obj, Object... args)
          参数一:触发的是哪个对象的方法执行。
          参数二: args:调用方法时传递的实际参数
 */
public class MethodDemo01 {
    /**
     * 1.获得类中的所有成员方法对象
     */
    @Test
    public void getDeclaredMethods(){
        // a.先获取class类对象
        Class c = Dog.class ;
        // b.获取全部申明的方法!
        Method[] methods = c.getDeclaredMethods();
        // c.遍历这些方法
        for (Method method : methods) {
            System.out.println(method.getName()+"====>"
                    + method.getParameterCount()+"===>" + method.getReturnType());
        }


    }
    /**
     * 2. 获取某个方法对象
     */
    @Test
    public void getDeclardMethod() throws Exception {
        // a.先获取class类对象
        Class c = Dog.class;
        // b.定位它的某个方法
        Method run = c.getDeclaredMethod("run");
        // c.触发方法执行!
        Dog jinMao = new Dog();
        Object rs = run.invoke(jinMao); // 触发jinMao对象的run()方法执行!
        System.out.println(rs);// 如果方法没有返回值,结果是null


        /**
         * 参数一:方法名称
         * 参数二:方法的参数个数和类型(可变参数!)
         */
        Method eat = c.getDeclaredMethod("eat",String.class);
        eat.setAccessible(true); // 暴力反射!
        /**
         * 参数一:被触发方法所在的对象
         * 参数二:方法需要的入参值
         */
        Object rs1 = eat.invoke(jinMao,"肉");
        System.out.println(rs1);// 如果方法没有返回值,结果是null
    }
}

通过Class方式获取成员变量

getDeclaredFields

获取所有变量

getFields

获取所有public变量

通过Class获取构造函数

getDeclaredConstructors

获取所有构造函数

getFields

获取所有public构造函数

通过Class对象获取方法

getDeclaredMethods

获取所有方法

getMethods

获取所有public方法

3.通过Constructor类对象、Method类对象和Field类对象分别获取类的构造函数、方法、属性和具体信息,并进行后续操作。

四、举个栗子

//1、首先定义一个Student类,在类中定义私有构造方法、public构造方法、私有函数、public函数等
package reflect;
import java.lang.reflect.*;
class Student{
  private String studentName;
  public int studentAge;
  public Student(){}
  private Student(String studentName){
    this.studentName=studentName;
  }
  void setStudentAge(int studentAge){
    this.studentAge=stuntAge;
  }
  private String show(String message){
    System.out.pringln("show: " + studentName + " , " + message);
    return "testReturnValue";
  }
  @Override
  public String toString(){
    return " "
  }

2.通过三种方式获取到Class对象

public class Main{
  public static void main(String[] args){
    //获取Class对象的三种方式
    //方式1:通过类名,要加完整路径
    Class studentClass=Class.forName("reflect.Student");
    //通过类名加.class
    Class studentClass2=Student.calss;
    //通过实例对象的getClass
    Student studentObject=new Student();
    Class studentClass3=studentObject.getClass();
  }
}

暴力攻击集合泛型

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;


/**
     拓展
        1.反射可破坏面向对象的封装性(暴力反射)。
        2.同时可破坏泛型的约束性。
 */
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        // 泛型只能工作在编译阶段,运行阶段泛型就消失了,
        // 反射工作在运行时阶段。
        List<Double> scores = new ArrayList<>();
        scores.add(99.3);
        scores.add(199.3);
        scores.add(89.5);


        // 拓展:通过反射暴力的注入一个其他类型的数据进去。
        // a.先得到集合对象的Class文件对象
        Class c = scores.getClass();
        // b.从ArrayList的Class对象中定位add方法
        Method add = c.getDeclaredMethod("add", Object.class);
        // c.触发scores集合对象中的add执行(运行阶段,泛型不能约束了)
        add.invoke(scores,"波仔");


        System.out.println(scores);




    }
}

反射的作用

/**
     拓展:反射的作用


     可在运行时得到一个类的全部成分然后操作。
     可破坏封装性。
     也可破坏泛型的约束性。


     更重要的用途是适合:做Java高级框架,基本上主流框架都会基于反射设计一些通用技术功能。


     Mybatis框架:
         你给任何一个对象数据我都可直接帮你解析字段并且把对应数据保存起来。
         Student (注册,把信息字段都存储起来)
         Teacher (注册,把信息字段都存储起来)
         Manager (注册,把信息字段都存储起来)


         我现在用反射技术开发一个框架实现:
         任何对象只要给我,我就可把信息和字段都解析并存储起来。
     小结:
        反射适合做通用技术框架的底层实现
 */
public class ReflectDemo01 {
    public static void main(String[] args) throws Exception {
        Student s1 = new Student(1,"赵敏",26,'女' ,"光明顶","110");
        Mybatis.save(s1);


        Pig peiQi = new Pig("佩奇",500.0 , "粉色","小红","母猪");
        Mybatis.save(peiQi);
    }
}