java反射原理详解及其用法

介绍

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。首先要了解java就得了解几个相关类和接口

Class代表类的实体,在运行的Java应用程序中表示类和接口
Field代表类的属性,包括静态和非静态
Method代表的是类的成员方法,包括静态和非静态
Constructor代表类的构造方法
后边还会在介绍两个类,是java7之后新增的api,简化了反射的使用。后边会进行详细的讲解

class类常用的方法

 	getName() 获取全限定类型
 	newInstance() 创建类的实例对象
 	getPackage() 获取类所在包
 	getSimpleName() 获取不带包名的类名
 	getInterfaces() 判断该类是接口还是类

class类还有很多获取相关类属性和方法的方法下边进行主要讲解

Field类常用方法

 	get(Object o) 获取实例o的属性
    set(Object o,Object val)设置实例o的属性为val
 	equals(Object o)判断属性是不是与实例o中的属性相等

Method类常用方法

   invoke(Object o,Object ...args)执行实例对象o的方法,参数为args

Constructor类常用方法

	newInstance(Object... args) 根据参数执行构造方法

在讲具体反射之前我们先创建一个具体的实现类

package com.gp.demo.entity;

/**
 * @Description
 * @Author gp
 * @Param 2020/5/21 15:52
 */
public class Person {
    private String name;
    private int age;
    public String sex;
    
    public  Person(){}
    
    private Person(String name){
        this.name = name;
    }
    protected Person(String name,int age,String sex){
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
	public static  void sleep(){
        System.out.println("我要睡觉了");
    }
    private void eat(){
        System.out.println("我吃饭了");
    }

    public void run(){
        System.out.println("我跑步了");
    }

    protected String buy(int money){
        return "car";
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

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

获取class对象的几种途径

  1. 通过对象获取class对象
 Person person = new Person();
 Class clazz1 = person.getClass();
  1. 通过Class.ForName获取class对象
try {
      Class clazz2 = Class.forName("com.gp.demo.entity.Person");
     } catch (ClassNotFoundException e) {
       e.printStackTrace();
     }

这里以会跑出一个类找不到的一个异常,我们可以进行trycatch或者是抛出去

  1. 通过类名获取class对象
Class clazz3 = com.gp.demo.entity.Person.class;
  1. 通过类加载器获取class对象
try {
            Class clazz4 = ClassLoader.getSystemClassLoader().loadClass("com.gp.demo.entity.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

此处需要进行异常的捕获

  1. 基本对象的Class对象获取
    基本类型都有type属性,可以得到这个基本类型的类型
 Class clazz5 = Integer.TYPE;
        Class clazz6 = Double.TYPE;

反射的具体使用

当一个类中声明的方法或者是属性为私有的时候我们一般情况下从外部就不能使用该方法或者是该属性,但是我们今天讲的是反射当然情况就有所不同,我们可以使用类中的所有方法与属性,接下来让我们拭目以待。

一. 获取构造函数并使用构造函数创建对象

getDeclaredConstructor(Class…<?> parameterTypes)获取固定参数列表的构造方法
getDeclaredConstructors()获取所有构造方法
getConstructor(Class…<?> parameterTypes)获取固定参数列表的公有构造方法
getConstructors()获取所有公有构造方法
  1. 遍历类的所有构造方法,包括私有构造
 /**
     * 遍历类的所有构造方法
     * */
    public static void  getConsctrcotrTest(){
        Class clazz = com.gp.demo.entity.Person.class;
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
        for (Constructor constructor:declaredConstructors){
            System.out.println(constructor.getName()+":");
            System.out.println(Modifier.toString(constructor.getModifiers()));
        }

    }
  1. 使用类的私有带参数的构造函数创建类的实例
/**
     * 遍历类的所有构造方法
     * */
    public static void  getConsctrcotrTest1() throws Exception{
        Class clazz = com.gp.demo.entity.Person.class;
        Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class);
        //设置构造函数为外部可见
        declaredConstructor.setAccessible(true);
        Person person = (Person) declaredConstructor.newInstance("zhangsan");
        System.out.println(person.getName());
    }

结果为
在这里插入图片描述
3. 使用类的默认构造器创建实例对象

 /**
     * 用类的默认构造方法创建实例对象
     * */
    public static void  getConsctrcotrTest2() throws Exception{
        Class clazz = com.gp.demo.entity.Person.class;
        Constructor declaredConstructor = clazz.getDeclaredConstructor();
        Person person = (Person) declaredConstructor.newInstance();
        System.out.println(person);
    }

二. 获取类中的属性并修改属性值

getDeclaredMethod(String name)获得某个属性对象
getDeclaredMethods()获取所属性
getMethods()获取所有公有属性
getMethod(String name)获取某个公有属性
  1. 获取所有的类属性并并查看相关信息
/**
     * 遍历类的属性
     * */
    public static void getFieldTest1()throws Exception{

        Class clazz = com.gp.demo.entity.Person.class;
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field:declaredFields){
            System.out.print(field.getName()+":");
            //查看属性的类型
            System.out.println(Modifier.toString(field.getModifiers()));
        }

    }

运行结果为
在这里插入图片描述

  1. 获取类的私有属性并赋值
/**
     * 获取并赋值类的私有属性
     * */
    public static void getFieldTest2()throws Exception{
        Person person  = new Person();
        person.setName("xiaoming");
        Class clazz = com.gp.demo.entity.Person.class;
        Field declaredField = clazz.getDeclaredField("name");
        //设置私有属性外部可见
        declaredField.setAccessible(true);
        String name =(String) declaredField.get(person);
        System.out.println(name);
        declaredField.set(person,"zhangsan");
        System.out.println(person.getName());

    }

执行结果为
在这里插入图片描述
此处放问类的私有属性的时候需要设置属性为可见的具体为setAccessible(true);
3. 获取并赋值类的公有属性

 /**
     * 获取并赋值类的公有属性
     * */
    public static void getFieldTest3()throws Exception{
        Person person  = new Person();
        person.setName("xiaoming");
        person.setSex("男");
        Class clazz = person.getClass();
        Field declaredField = clazz.getDeclaredField("sex");
        String sex =(String) declaredField.get(person);
        System.out.println(sex);
        declaredField.set(person,"女");
        System.out.println(person.getSex());

    }

执行结果为
在这里插入图片描述

三. 获取类中的方法并执行方法

getDeclaredMethod(String name, Class<?>… parameterTypes)获取类中所有的满足参数的方法名和参数列表的方法
getDeclaredMethods()获取所有的成员方法
getMethod(String name, Class<?>… parameterTypes)获取类中所有的满足参数的方法名和参数列表的公有方法
getMethods()获取所有的公有成员方法
  1. 遍历所有的类方法,包括私有方法和静态方法
 /**
   * 遍历类的所有方法
    */
    public static void getMethodTest() throws Exception {
        Class clazz = com.gp.demo.entity.Person.class;
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method method :declaredMethods){
            System.out.print(method.getName()+":");
            //获取方法的类型method.getModifiers()
            System.out.println(Modifier.toString(method.getModifiers()));
        }
    }

执行结果为
在这里插入图片描述

  1. 调用类的私有带参数的方法并含有返回值
/**
     * 调用类的私有带参数方法
     * */
    public void getClassMethod1() throws Exception {
        Class clazz = com.gp.demo.entity.Person.class;
        Class[] classes = {Integer.TYPE};
        Method buyMethod = clazz.getDeclaredMethod("buy",classes);
        //设置方法可以被外部访问
        buyMethod.setAccessible(true);
        String str =(String) buyMethod.invoke(new Person(),3);
        System.out.println(str);
    }

执行结果为
在这里插入图片描述当方法为私有方法的时候需要使用setAccessible(true)将方法设置为可使用模式

  1. 执行类的不带参无返回值的公有方法
 /**
     * 调用类的公有无参方法
     * */
    public static void getClassMethod2() throws Exception {
        Class clazz = com.gp.demo.entity.Person.class;
        Method buyMethod = clazz.getDeclaredMethod("run");
        buyMethod.invoke(new Person());
    }

执行结果为
在这里插入图片描述

反射的优化:方法句柄

java7中引入了新的api即方法句柄
方法句柄包含两个非常重要的类分别是:MethodHandle和MethodType

MethodHandle

通过句柄我们可以直接调用改句柄所引用的底层方法,从作用上来看方法句柄类似于反射中的Method类,是对要执行方法的一个引用,我们也是通过他来调用底层的方法,他调用时有两个方法invokeinvokeExact,后者要求参数类型与底层方法的参数完全匹配,前者则有出入时做修改如包装类型

MethodType

方法签名不可改变对象,是对方法的一个映射。包含返回值和参数类型,在lookup时也是通过它来寻找的,每个方法句柄都对应一个MethodType实例,用来指明方法的返回值类型和参数类型

/**
     * 使用方法句柄来实现反射
     * */
    public  static  void  getMethodHandleTest() throws Throwable {
        //获取类的成员方法
        MethodType methodType = MethodType.methodType(void.class);
        MethodHandle methodHandle = MethodHandles.lookup().findVirtual(Person.class,"run",methodType);
        methodHandle.invoke(new Person());

        //返回类的构造函数
        MethodType constructorMthodType = MethodType.methodType(void.class);
        MethodHandle constructorMethodHandle = MethodHandles.lookup().findConstructor(Person.class,constructorMthodType);
        Person person = (Person) constructorMethodHandle.invoke();
        System.out.println(person);

        //返回类的静态方法
        MethodType staticMthodType = MethodType.methodType(void.class);
        MethodHandle staticMethodHandle = MethodHandles.lookup().findStatic(Person.class,"sleep",staticMthodType);
        staticMethodHandle.invoke();
    }

运行结果为
在这里插入图片描述
经过试验得知通过方法句柄只能获取到类的公有方法,private和protected还有默认的是获取不到的,执行后后就会出现错误提示

总结:

	使用java原生的反射功能要强大与使用方法句柄的反射,但是使用方法句柄的反射性能稍微优于java原生反射
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值