java 反射详解

反射作用引入

  1. 首先创建几个实体类:微信支付,支付宝支付,银行卡支付。模拟支付方法。这些方法继承一个支付接口。

接口

public interface Pay {
    /**
     * 在线支付方法
     */
    void payOnline();
}

微信支付

public class WeChat implements Pay {
    @Override
    public void payOnline() {
        System.out.println("正在使用微信支付");
    }
}

支付宝支付

public class AliPay implements Pay {
    @Override
    public void payOnline() {
        System.out.println("正在使用支付宝支付");
    }
}

银行卡支付

public class Card implements Pay {
    @Override
    public void payOnline() {
        System.out.println("正在使用银行卡支付");
    }
}
  1. 创建完成后我们模拟购物软件进行支付

测试方法

public class Application {
    public static void main(String[] args) {
//        模拟前台传入的支付方式
        String payMethond = "支付宝";

//        支付方式判断
        switch (payMethond) {
            case "支付宝" -> pay(new AliPay());
            case "微信" -> pay(new WeChat());
            case "银行卡" -> pay(new Card());
            default -> pay(new AliPay());
        }
    }

    /**
     * 支付功能
     */
    public static void pay(Pay p) {
        p.payOnline();
    }
}

经过测试发现在支付判断的时候方法比较冗长,在接下来对支付方式的添加或是删除比较麻烦

  1. 使用反射解决
    测试方法
public class Application2 {
    public static void main(String[] args) throws Exception{
//        模拟前端传过来的支付方式,传入值时类的全限定路径
        String payMethod="Shininess.entity.AliPay";
//        创建传入过来的支付方式Class类
        Class<?> aClass = Class.forName(payMethod);
//        获取构造器并创建对象
        Object o = aClass.getDeclaredConstructor().newInstance();
//        获取o对象的payOnline方法
        Method payOnline = aClass.getMethod("payOnline");
//        o调用payOnline方法
        payOnline.invoke(o);
    }
}

使用反射之后我们发现只需要前端给我们传过来一个全限定路径便可以解决代码冗长问题

反射的概念

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,

都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

在编译后产生字节码文件的时候,类加载器子系统通过二进制字节流,负责从文件系统加载class文件。

在执行程序(java.exe)时候,将字节码文件读入JVM中--->这个过程叫做类的加载。然后在内存中对应创建一个java.lang.Class对象-->这个对象会被放入字节码信息中,这个Class对象,就对应加载那个字节码信息,这个对象将被作为程序访问方法区中的这个类的各种数据的外部接口。
所以:我们可以通过这个对象看到类的结构,这个对象就好像是一面镜子,透过镜子看到类的各种信息,我们形象的称之为反射

简单来说就是在我们运行对.java文件进行编译时会产生一个.class字节码文件,之后字节码文件会被jvm中的了类加载器进行加载,在加载中会创建一个class对象放在字节码文件class中,这个class就可以去获取或是调用运行时代码类中的属性方法等资源

java 反射创建带泛型的对象 java反射用法_字节码

反射常用方法

测试用类

student类

//Student作为子类
@MyAnnotation(value="hello")
public class Student extends Person implements MyInterface {
    //属性:
    private int sno;//学号
    double height;//身高
    protected double weight;//体重
    public double score;//成绩
    //方法:
    @MyAnnotation(value="himethod")
    public String showInfo(){
        return "我是一名三好学生";
    }
    public String showInfo(int a,int b){
        return "重载方法====我是一名三好学生";
    }
    private void work(){
        System.out.println("我以后会找工作--》成为码农  程序员 程序猿  程序媛");
    }

    private void work(int a){
        System.out.println(a+"我以后会找工作--》成为码农  程序员 程序猿  程序媛");
    }

    void happy(){
        System.out.println("做人最重要的就是开心每一天");
    }
    protected int getSno(){
        return sno;
    }
    //构造器
    public Student(){
        System.out.println("空参构造器");
    }

    private Student(int sno){
        this.sno = sno;
    }
    Student(int sno,double weight){
        this.weight = weight;
    }
    protected Student(int sno,double height,double weight){
        this.sno = sno;
    }
    @Override
    @MyAnnotation(value="hellomyMethod")
    public void myMethod() {
        System.out.println("我重写了myMethod方法。。");
    }
    @Override
    public String toString() {
        return "Student{" +
                "sno=" + sno +
                ", height=" + height +
                ", weight=" + weight +
                ", score=" + score +
                '}';
    }
}

preson类

public class Person {
    //属性
    private int age;
    public String name;
    //方法
    private void eat(){
        System.out.println("Person---eat");
    }
    public void sleep(){
        System.out.println("Person---sleep");
    }
}

自定义注解

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value();
}

自定义接口

public interface MyInterface {
    void myMethod();
}

1.class的创建方法

//        创建Class的四种方法方法
//        1.通过getClass()方法获取,不常用
        Class<? extends Person> aClass = new Person().getClass();
//        2.通过内置class属性获取,不常用
        Class<Person> aClass1 = Person.class;
//        3.通过forName()获取,比较常用
        Class<?> aClass2 = Class.forName("Shininess.entity.Person");
//        4.利用类加载器获取,不常用
        ClassLoader classLoader = Demo.class.getClassLoader();
        Class<?> aClass3 = classLoader.loadClass("Shininess.entity.Person");
//        通过四种方法创建的class都是一样的
        System.out.println(aClass == aClass1 && aClass1 == aClass2 && aClass2 == aClass3);//true

2.通过class对象获取并使用构造器

//        获取字节码信息中的构造器
        Class<?> aClass7 = Class.forName("Shininess.entity.Student");
//        获取当前类被public修饰的构造器
        Constructor<?>[] constructor = aClass7.getConstructors();

//        获取当前运行时的全部修饰符的构造器
        Constructor<?>[] declaredConstructor = aClass7.getDeclaredConstructors();

//        获取指定public无参构造器
        Constructor<?> constructor1 = aClass7.getConstructor();

//        获取指定private带参构造器:getDeclaredConstructor(参数类型的class)
        Constructor<?> constructor2 =  aClass7.getDeclaredConstructor(int.class);

//        使用构造器
        Object o = constructor1.newInstance();

3.通过class对象获取并设置属性

Class<?> aClass8 = Class.forName("Shininess.entity.Student");
//        getFields获取运行时类和父类被public修饰的属性
        Field[] fields = aClass8.getFields();

//        getDeclaredFields:获取运行时类中的所有属性
        Field[] declaredFields = aClass8.getDeclaredFields();

        //获取指定的属性:getField("属性名")
        Field score = aClass8.getField("score");
        Field sno = aClass8.getDeclaredField("sno");

//        获取参数结构的方法:
//        .getModifiers()获取修饰符,返回一个数字,每一个修饰符对应了一个数字可以使用Modifiers。toString转换
        System.out.println(aClass8.getModifiers());//1
        System.out.println(Modifier.toString(aClass8.getModifiers()));//public

//        getTypeName获取参数类型
        System.out.println(aClass8.getTypeName());
//        getName获取参数名称
        System.out.println(aClass8.getName());

//        属性赋值
        Object o1 = aClass8.getConstructor().newInstance();
        Field score1 = aClass8.getField("score");
        score1.set(o1,100);
        System.out.println(o1);//Student{sno=0, height=0.0, weight=0.0, score=100.0}

4.通过class对象获取并使用方法

//获取字节码信息:
        Class cls = Student.class;
        //获取方法:
        //getMethods:获取运行时类的方法还有所有父类中的方法(被public修饰)
        Method[] methods = cls.getMethods();

        //getDeclaredMethods:获取运行时类中的所有方法:
        Method[] declaredMethods = cls.getDeclaredMethods();

        //获取指定的方法:
        Method showInfo1 = cls.getMethod("showInfo");
        Method showInfo2 = cls.getMethod("showInfo", int.class, int.class);
        Method work = cls.getDeclaredMethod("work",int.class);

        //获取方法的具体结构:
        /*
        @注解
        修饰符 返回值类型  方法名(参数列表) throws XXXXX{}
         */
        //名字:
        System.out.println(work.getName());
        //修饰符:
        int modifiers = work.getModifiers();
        System.out.println(Modifier.toString(modifiers));
        //返回值:
        System.out.println(work.getReturnType());
        //参数列表:
        Class[] parameterTypes = work.getParameterTypes();
        //获取注解:
        Method myMethod = cls.getMethod("myMethod");
        Annotation[] annotations = myMethod.getAnnotations();
        //获取异常:
        Class[] exceptionTypes = myMethod.getExceptionTypes();
        //调用方法:
        Object o3 = cls.newInstance();
        myMethod.invoke(o3);//调用o对象的mymethod方法
        System.out.println(showInfo2.invoke(o3,12,45));

代码仓库