反射-这一篇就让你学会!

在这里插入图片描述

当你看到这篇文章时请您慢慢的将它看完,相信对您一定会有帮助的!


前言

提起反射我想大家第一印象就是牛批!牛批。确实反射技术的确是Java中非常强大的技术了。大多数的框架都是通过反射技术完成的,因此你要想学习一些框架的原理那么反射是必学技术,不多说一起来看看吧!


一、反射的引入

学习反射之前可以首先了解一下反射机制中相关的类

java.lang.Class<T>            类的字节码文件对象
java.lang.reflect.Constructor<T>          字节码中的构造方法字节码
java.lang.reflect.Field          字节码中的属性字节码
java.lang.reflect.Method          字节码中的方法字节码

二、获取Class对象

使用反射技术的前提是获取到某个类的字节码文件,而获取这个Class对象的话有三种方式,依次介绍。
特别提醒:使用反射技术的第一步一定是获取该类的字节码对象。获取不到的话你玩个寂寞?

1.Class.forName()

Class.forNam(“完整类名”) :这里要注意的是一定是完整类名,就是包名加类名

代码如下(示例):

  Class aClass = Class.forName("java.lang.String");
  System.out.println(aClass.getName());

结果:在这里插入图片描述
补充:如果类中有静态代码块的话,Class.forName()会执行静态代码块中的内容
举例:

public class Person{
    //定义一个静态代码块
    static{
            System.out.println("静态代码块执行了");
          }
}
//测试
@Test
public void show(){
    Class.forName("cn.offcn.constructor.Person");
}
//执行结果:
//静态代码块执行了

2.类名.class

类名.class也获取该类的字节码文件对象,注意使用这种方式一定要留意不同包同类名的情况,还有记得有返回值Class对象

代码如下(示例):

Class aClass = Person.class;
System.out.println(aClass);

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

3.对象.getClass()

对象.getClass() 要先创建对象后调用getClass方法

  Person p = new Person();
  Class aClass = p.getClass();
  System.out.println(aClass.getName());

在这里插入图片描述
这种方法使用的较少,但是需要有了解

下边将是反射技术的重点

在这里插入图片描述

三、通过反射实例化对象

我们知道实例化一个对象是通过构造方法进行的,因此我们可以首先要获取到类的构造器。api很多但是技巧性很大容易记住勿偷懒

//我们知道一个类可以是有多个构造方法的
Constructor[]   getConstructors()  获取所有的public修饰的构造方法
Constructor[]   getDeclaredConstructors() 获取所有的构造方法包括private修饰的
Constructor     getConstructor(Class<?> ...parameterType)获取public修饰的指定构造器
Constructor     getDeclaredConstructor(Class<?>...parameterType) 获取指定的构造器(可以是private修饰)

上述api我们可以得出不仅可以获取public修饰的构造方法同时也可以获取到private修饰的构造方法。

获取到Constructor对象后使用newInstance(…)创建对象

newInstance(Object... initargs) 
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。

实例化对象演示:

数据准备:四个构造方法

package cn.offcn.constructor;

public class Person {
    private String name;
    private int age;
    private String gender;

    //为了方便查看都输出一句话
    public Person(){
        System.out.println("无参数构造方法执行");
    }

    public Person(String name){
        this.name = name;
        System.out.println("public修饰的一个参数的构造方法执行了");
    }
    private Person(String name,int age){
        this.name=name;
        this.age=age;
        System.out.println("private修饰的两个参数的构造方法执行了");
    }
    public Person(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
       System.out.println("public修饰的三个参数的构造方法执行了");
    }

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

代码演示:
首先演示通过参数类型指定构造方法

    @Test
    public void reflect()throws  Exception{
        //获取Person类的字节码对象
        Class<?> aClass = Class.forName("cn.offcn.constructor.Person");
        
        //获取public修饰的指定的构造方法
        Constructor<?> constructor = aClass.getConstructor(String.class);
        //返回类型是Object需要强转,newInstance("初始化参数")
        Person person1 = (Person) constructor.newInstance("aaa");
        System.out.println(person1);
        
        //获取private修饰的构造方法
        Constructor constructor2 = aClass.getDeclaredConstructor(String.class, int.class);
        //因为此时的构造方法时private修饰的需要暴力破解
        constructor2.setAccessible(true);
        Person person2 =(Person) constructor2.newInstance("张三", 23);
        System.out.println(person2);
    }

私有方法、构造方法等在初始化时需要暴力反射:setAccessible(true)

在这里插入图片描述
来个花样?
案例要求:一次性通过四个构造方法完成四个对象的创建并完成初始化。

import java.lang.reflect.Constructor;

public class Test {

    public static void main(String[] args) throws Exception{
        Class<?> aClass = Class.forName("cn.offcn.constructor.Person");
        //获取所有的构造方法
        Constructor[] constructors= aClass.getDeclaredConstructors();
        //遍历所有的构造方法
        for (Constructor constructor : constructors) {
           //设置暴力破解
            constructor.setAccessible(true);
            switch (constructor.getParameterCount()){
                case 0:
                    System.out.println(constructor.newInstance());
                    break;
                case 1:
                    System.out.println(constructor.newInstance("lisi"));
                    break;
                case 2:
                    System.out.println(constructor.newInstance("wangwu",22));
                    break;
                case 3:
                    System.out.println(constructor.newInstance("haha",23,"男"));
                    break;
            }

        }
    }
}

结果:
在这里插入图片描述
至于这个顺序我的猜想是:它会使用贪婪模式,就是尽可能的先找到能提供多个参数的构造方法,就像是spring容器使用自动注入一样。好吧这只是我的猜想

四、通过反射设置对象属性

要想设置对象的属性,那么就需要首先获取到Field对象

看一下常用api

 Field      getDeclaredField(String name); //通过属性名获取Field对象
 Field      getField(String name); //通过属性名获取Field对象只能获取public修饰的
 Field[]    getDeclaredFields();  //获取所有的属性对象
 Field[]    getFields();  //获取所有public修饰的对象

获取到Field对象后就可以通过反射为对象设置属性使用:获取到Field对象后使用set(object o,object value)为对象设置属性

此时看这个反射这个词是多么的形象
代码演示:

package cn.offcn.field;

public class Person {

    private String name;
    private int age;
    public  double money;
    
    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 double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

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

测试类

    //创建一个Person对象,使用getClass获取类的字节码对象
    Person p = new Person();
    Class aClass = p.getClass();
    //获取各个属性Field对象
    Field money = aClass.getField("money");
    Field name = aClass.getDeclaredField("name");
    Field age = aClass.getDeclaredField("age");
    //为对象设置值
    money.set(p,12.5);
    //private修饰的属性值设置值需要暴力破解
    age.setAccessible(true);
    age.set(p,12);
    name.setAccessible(true);
    name.set(p,"李四");
    System.out.println(p);

结果显示:
在这里插入图片描述
注意事项:
①在设置属性值时要确保类型一致。就是该属性的类型时int类型就不要传递一个String类型的字符串
在这里插入图片描述
②私有属性设置值时一定要加上setAccessible(true)

不然会报
在这里插入图片描述

五、通过反射调用方法

回想一下我们以往是怎么调用方法的。–>创建对象调用对象的方法
接下来看一下反射是怎么做的
首先获取Method对象

 Method[]   getDeclaredMethods();//获取所有的Method对象不包括父类的
 Mothod[]   getMethods(); //获取public修饰的对象,包括父类的
 Method     getDeclaredMethod(String name,Class<T> ...parameterType);//通过方法名和参数类型获取指定的Method对象
 Methos     getMethos(String name,Class<T>...parameterType)//通过参数名和参数类型获取指定的public修饰的Method对象
 //api中需要留意:
 //getMethods()是可以获取父类中public修饰的Mothod对象的而getDeclaredMethods()可以获取非public修饰的Mothod对象但是获取不到父类的

获取到Method对象后使用invoke(Object o,Object…args)来调用方法

代码演示:

package cn.offcn.field;

public class Person {

    //分别定一个不同权限修饰符修饰的方法
    public void show1(){
        System.out.println("public修饰的无参数方法");
    }
    public void show2(String name){
        System.out.println("public修饰的一个参数的方法"+name);
    }
    private void show3(){
        System.out.println("private修饰的无参数方法");
    }
    private void show4(String name,int age){
        System.out.println("private修饰的两个参数的方法,姓名:"+name+"年龄:"+age);
    }
}

调用方法:

首先创建一个对象->获取Method对象->执行invoke方法

代码演示

public class Test {
    public static void main(String[] args) throws Exception {
        //获取Person类的Class对象
        Class aClass = Class.forName("cn.offcn.field.Person");
        //通过无参数构造方法创建对象
        Constructor constructor = aClass.getConstructor();
        Person person = (Person)constructor.newInstance();
       //通过方法名获取Method对象并执行
        Method show1 = aClass.getMethod("show1");
        show1.invoke(person);
       //通过方法名和参数获取指定的Method对象并执行
        Method show4 = aClass.getDeclaredMethod("show4", String.class, int.class);
        //私有方法需要开启暴力反射
        show4.setAccessible(true);
        show4.invoke(person,"张三",23);
    }
}

运行结果:

在这里插入图片描述

六、反射获取注解

反射获取注解个人使用起来用的不太多了解一下吧

Annotation[]  getAnnotations();//返回所有注解包括父类的
Annotation    getAnnotation(Class<T> Annotation.class);//返回指定的类型的注解,没有返回null
Annotation[]  getDeclaredAnnotations();//返回所有注解不包括父类

了解内容,简单举个栗子

//首先自定义一个注解
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String[] value();
}

//定义一个类添加注解
@MyAnnotation(value ={"值1","值2","值3"})
public class Person {
}
//测试
public class Test {
    public static void main(String[] args) throws Exception {
        Class aClass = Class.forName("cn.offcn.field.Person");
        Annotation annotation = aClass.getAnnotation(MyAnnotation.class);
        System.out.println(annotation.toString());
    }
}
//打印结果:
//@cn.offcn.annotation.MyAnnotation(value=[值1, 值2, 值3])

七、注解获取类的接口

了解内容

Class[]  getInterfaces(); //获取所有接口的Class对象

这个知道就行,后边学习的动态代理中会使用到

//创建第一个接口
public interface A{}
//创建第二个接口
public interface B{}
//创建一个类去实现这两个接口
public class C implements A,B{}
//测试
 @org.junit.Test
 public void test(){
    Class c =  C.class;
    Class[] interfaces = c.getInterfaces();
    for (Class anInterface : interfaces) {
        System.out.println(c.getSimpleName()+"实现了"+anInterface.getSimpleName());
    }
}
//打印结果:
//C实现了A
//C实现了B

八、通过反射完成反编译一个类

接下来我们完成一个综合练习反编译一个类

反编译之前我们要了解Class类中的一些常用方法

----------Class对象中方法--------------------
  String  getSimpleName();    //获取简单名称
  String  getName();         //获取全类名
  int     getModifiers();    //获取修饰符
  String  getTypeName();    //获取类型名
------------Method对象中的方法-----------
 Parameter[]   getParameters(); //获取所有的参数
 Class         getReturnType(); //返回值类型
 Modifier.toString(int i) //将权限修饰符转换为字符串

了解一下简单方法我们直接开始定义一个方法用来反编译类

package cn.offcn.field;

import java.lang.reflect.*;

public class Test {  
    public static void main(String[] args) throws Exception {
       //测试ArrayList类的代码
        String s = "java.util.ArrayList";
        reflect(s);
    }
    //定义一个反编译的方法
    public static void reflect(String name) {
        try {
            //获取类的Class对象
            Class c = Class.forName(name);
            //开始打印:
            System.out.print(Modifier.toString(c.getModifiers()) +" class " + c.getSimpleName()+" ");
            //判断是否继承有父类
            Class superclass = c.getSuperclass();
            if(!superclass.getSimpleName().equals("Object")){
                System.out.print("extends "+superclass.getSimpleName());
            }
            //判断是否有实现接口
            Class[] interfaces = c.getInterfaces();
            if (interfaces.length >0){
                System.out.print(" implements ");
               int k = 0;
                for (Class anInterface : interfaces) {
                    if(++k < interfaces.length){
                        System.out.print(anInterface.getSimpleName()+",");
                    }else{
                        System.out.print(anInterface.getSimpleName());
                    }
                }
                k = 0;
            }
            System.out.println("{");
            //获取所有的属性
            Field[] Fields = c.getDeclaredFields();
            for (Field field : Fields) {
                System.out.println("   "+Modifier.toString(field.getModifiers())+" "+field.getType().getSimpleName()+" "+field.getName()+";");
            }
            System.out.println();
            //打印所有的构造方法
            Constructor[] constructors = c.getDeclaredConstructors();
            int j = 0; //用来判断是否需要加逗号
            for (Constructor constructor : constructors) {
                System.out.print("   "+Modifier.toString(constructor.getModifiers())+" "+c.getSimpleName()+"(");
                Parameter[] parameters = constructor.getParameters();
                int num = constructor.getParameterCount();
                for (Parameter parameter : parameters) {
                    if(++j < num){
                        System.out.print(parameter.getType().getSimpleName()+" "+parameter.getName()+",");
                    }else{
                        System.out.print(parameter.getType().getSimpleName()+" "+parameter.getName());
                    }
                }
                j = 0; //恢复变量值
                System.out.println(");");
            }
            System.out.println();

            //获取所有方法
            Method[] methods = c.getDeclaredMethods();
            int i =0; //定义一个变量用来判断是否需要加逗号
            for (Method method : methods) {
              System.out.print("   "+Modifier.toString(method.getModifiers())+" "+method.getReturnType().getSimpleName()+" "+method.getName()
                        +"(");
                int nums = method.getParameterCount();

                for (Parameter parameter : method.getParameters()) {
                    if(++i < nums)
                    System.out.print(parameter.getType().getSimpleName()+" "+parameter.getName()+",");
                    else{
                        System.out.print(parameter.getType().getSimpleName()+" "+parameter.getName());
                    }
                }
                i = 0;
                System.out.println("){...}");

            }
            System.out.println();
            System.out.println("  }");

        } catch (Exception e) {
            e.toString();
        }
    }
}

结果如下:
太多了,只截取部分截图
在这里插入图片描述
把这个反编译联系一下,反射技术就掌握一大部分了。。。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值