Day21——反射

文章目录

  • 反射
    • 1. 动态语言
    • 2. 静态语言
    • 3. 反射(Reflection):
      • 1. 概述
      • 2. 理解
      • 3. 反射机制提供的功能
      • 4. 反射的优缺点
      • 5. 反射相关的常用 API
      • 6. Class 类
        • 1. 概述
        • 2. Class 的创建方式(获取 Class 类的实例)
        • 3. Class 类常用方法
        • 4. 可以有 Class 对象的类型
        • 5. Java 内存
        • 6. 类的加载过程
          • 1. 类的初始化
          • 2. 类加载器的作用
          • 3. 类加载器的类型
        • 7. 获取运行时类的完整结构
      • 7. Class对象的作用
        • 1. 创建类的对象
        • 2. 创建时需求
        • 3. 对于没有无参构造器的类创建对象
        • 4. 调用指定的方法
      • 8. 性能对比分析
      • 9. 获取泛型信息:
    • 4. 反射操作注解
      • 1. ORM

反射

1. 动态语言

  • 是一类在运行时可以改变其结构的语言:例如细腻的函数、对象、甚至代码可以被引进,已有的函数可以被 删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构;
  • 主要动态语言:Object-C、C#、JavaScript、PHP、Python等;

2. 静态语言

  • 与动态语言相对应,运行时结构不可变的语言就是静态语言。如Java、C、C++;
  • Java不是动态语言,但Java可以称为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活;

3. 反射(Reflection):

1. 概述

Java 的反射机制是指在运行状态中:

  • 对任意一个类,都能够知道这个类的方法和属性;
  • 对任意一个对象,都能够调用它的任意一个方法和属性;

2. 理解

反射中包含了一个“反”字,那么肯定就有所谓的“正”字;

“正射”: 引入需要的“包类”名称 --> 通过new实例化 --> 取得实例化对象;

从 Java 入门到现在,我们已经了解了什么是类,当我们使用这个类(Person 类)的时候,我们要理解这个类中的属性(年龄、姓名、性别…)和方法(技能,比如唱歌、跳舞、写代码…),在了解了属性和方法之后,我们对这个类进行实例化(理解为创造一个游戏角色),之后使用这个类对象(创建好的角色)操作(命名,定义性别,年龄,定义技能等);

Person person = new Person();  //“正”

person.skill("伐木");

“反射”:实例化对象 --> getClass()方法 --> 得到完整的"包类"名称;

反射则是一开始我们并不知道我们初始化的类对象是什么,我们通过所谓的反射机制,创建对象,以及其调用含有的方法;

Class c1 = Class.forName("com.sd.day25.Person");
Method method = c1.getMethod("skill", String.class);
Person person = (Person)c1.newInstance();
person.skill("伐木");

以上代码的意思,稍后会将,但是验证了反射和正射得到的结果是相同的,但是思路却完全不同正射代码在未运行时已经知道了要运行的类(Person);而反射代码则是在运行时通过字符串值才知道要运行的类(com.sd.day25.Person);

3. 反射机制提供的功能

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法;
  • 在运行时获取泛型信息;
  • 在运行时调用任意一个对象的成员变量和方法;
  • 在运行时处理注解;
  • 生成动态代理;

4. 反射的优缺点

  • 优点:可以实现动态创建对象和编译,体现出很大的灵活性;
  • 缺点:对性能有影响;使用反射基本上是一种解释操作,我们可以告诉 JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作;

5. 反射相关的常用 API

java.lang.Class  //代表一个类
java.lang.reflect.Method    //代表类的方法
java.lang.reflect.Field    //代表类的成员变量
java.lang.reflect.Constructor   //代表类的构造器

代码演示:

/*
反射
 */
public class Demo03Reflection {
    public static void main(String[] args) throws ClassNotFoundException {
        //通过反射获取类的Class对象
        Class aClass = Class.forName("com.sd.day25_Annotation.user");
        System.out.println(aClass);  //class com.sd.day25_Annotation.user


        Class Class1 = Class.forName("com.sd.day25_Annotation.user");
        Class Class2 = Class.forName("com.sd.day25_Annotation.user");
        Class Class3 = Class.forName("com.sd.day25_Annotation.user");

        //一个类在内存中只有一个Class对象(哈希值相同)
        //一个类被加载后,类的整个结构都会被封装在Class对象中
        System.out.println(Class1.hashCode());  //356573597
        System.out.println(Class2.hashCode()); //356573597
        System.out.println(Class3.hashCode()); //356573597
    }
}

//定义实体类
class user {
    private int age;
    private String name;
    private String password;

    public user(int age, String name, String password) {
        this.age = age;
        this.name = name;
        this.password = password;
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

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

6. Class 类

1. 概述

  • 在 Object 类中定义了以下的方法,此方法将被所有子类继承;

    public final Class getClass()

  • 以上方法返回值的类型是一个 Class 类,此类是 Java 反射的源头,实际上所谓反射从程序的运行结果看就是:可以通过对象反射求出类的名称;

  • 对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口;

  • 对于每个类而言,jre 都为其保留了一个不变的Class 类型的对象;

  • 一个 Class 对象包含了特定的某个结构(class / interface / enum / annotation / primitive type / void / [])的有关信息;

    Class 本身也是一个类;
    Class 对象只能由系统建立对象;
    一个加载的类在 JVM 中只会有一个 Class 实例;
    一个 Class 对象对应的是一个加载到 JVM 中的一个 .class 文件;
    每个类的实例都会记得自己是由哪个 Class 实例所生成;
    通过 Class 可以完整地得到一个类中的所有被加载的结构;
    Class 类是 Reflection 的根源,针对任何你想动态加载、运行的类,唯有先获得相应的 Class 对象;
    

2. Class 的创建方式(获取 Class 类的实例)

  1. 若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高;

    Class aclass = Person.class;

  2. 已知某个类的实例,代用该实例的 getClass() 方法获取 Class 对象;

    Class aclass = person.getClass();

  3. 已知一个类的全类名,且该类在类路径下,可通过 Class 类的静态方法 forName() 获取,可能抛出 ClassNotFoundException;

    Class aclass = Class.forName("Demo01.Student");

  4. 内置基本数据类型可以直接用类名 .Type;

  5. 还可以利用 ClassLoader;

代码演示:

/*
Class 类创建方式
 */
public class Demo04CreatClass {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person = new Student();
        System.out.println("This person is a " + person.name); //This person is a Student

        //方式1:通过对象获得
        Class c1 = person.getClass();
        System.out.println(c1.hashCode());  //356573597

        //方式2:forname获得
        Class c2 = Class.forName("com.sd.day25_Annotation.Student");
        System.out.println(c2.hashCode());  //356573597

        //方式3:通过类名.class获得
        Class c3 = Student.class;
        System.out.println(c3.hashCode());   //356573597

        //方式4:基本内置类型的包装类都有一个Type属性
        Class c4 = Integer.TYPE;
        System.out.println(c4);  //int

        //获得父类类型
        Class c5 = c1.getSuperclass();
        System.out.println(c5);   //class com.sd.day25_Annotation.Person
    }
}

class Person {
    public String name;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

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

class Student extends Person {
    public Student() {
        this.name = "Student";
    }
}

class Teacher extends Person {
    public Teacher() {
        this.name = "Teacher";
    }
}

3. Class 类常用方法

static ClassforName(String name)    //返回指定类名name的Class对象
    
Object newInstance()    //调用缺省构造函数,返回Class对象的一个实例
    
getName()      //返回此Class对象所表示的实体(类,接口,数组类或void)的名称
    
Class getSuperClass()   //返回当前Class对象的父类的Class对象
    
Class[] getinterfaces()   //获取当前Class对象的接口
    
ClassLoader getClassLoader()   //返回该类的类加载器
    
Constructor[] getConstructors()  //返回一个包含某些Constructor对象的数组
    
Method getMothed(String name, Class.. T)  //返回一个Method对象,此对象的形参类型为paramType
    
Field[] getDeclareFields()   //返回Field对象的一个数组;

4. 可以有 Class 对象的类型

  1. class:外部类、成员(成员内部类、静态内部类)、局部内部类、匿名内部类;
  2. interface:接口;
  3. [] :数组;
  4. enum:枚举;
  5. annotation:注解 @interface;
  6. primitive type:基本数据类型;
  7. void

代码演示:

import java.io.ObjectOutputStream;
import java.lang.annotation.ElementType;
/*
所有类型的class
 */
public class Demo05Class {
    public static void main(String[] args) {
        Class c1 = Object.class;   // 类
        Class c2 = Comparable.class;  //接口
        Class c3 = String[].class;  // 一维数组
        Class c4 = int[][].class;  //二维数组
        Class c5 = Override.class;  //注解
        Class c6 = ElementType.class;  //枚举
        Class c7 = Integer.class;  //基本数据类型
        Class c8 = void.class;  //空类型
        Class c9 = Class.class;   //Class
        System.out.println(c1);  //class java.lang.Object
        System.out.println(c2);  //interface java.lang.Comparable
        System.out.println(c3);  //class [Ljava.lang.String;
        System.out.println(c4);  //class [[I
        System.out.println(c5);  //interface java.lang.Override
        System.out.println(c6);  //class java.lang.annotation.ElementType
        System.out.println(c7);  //class java.lang.Integer
        System.out.println(c8);  //void
        System.out.println(c9);  //class java.lang.Class

        //只要元素类型与维度一样,就是同一个Class
        int[] a = new int[10];
        int[] b = new int[1000];
        System.out.println(a.getClass().hashCode());  //356573597
        System.out.println(b.getClass().hashCode()); //356573597
	}
}

5. Java 内存

  1. 堆:存放 new 的对象和数组;可以被所有的线程共享,不会存放别的对象引用;
  2. 栈:存放基本变量类型(包含这个基本类型的具体数值);引用对象的变量(会存放这个引用在堆里面的具体地址);
  3. 方法区:可以被所有的线程共享;包含了所有的 class 和 static 变量;

6. 类的加载过程

当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤对该类进行初始化:

  1. 类的加载(Load):将类的 Class 文件读入内存,并为之创建一个 java.lang.Class 对象;此过程由类加载器完成;
    • 将 Class 文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的 java.lang.Class 对象;
  2. 类的链接(Link):将类的二进制数据合并到 JRE 中;
    • 验证:确保加载的类信息符合 JVM 规范,没有安全方面的问题;
    • 准备:正式为类变量(static)分配内存并设置变量默认初始值的阶段,这些内存都将在方法区进行分配;
    • 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程;
  3. 类的初始化(Initialize):JVM 负责对类进行初始化;
    • 执行类构造器 () 方法的过程。类构造器 () 方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器);
    • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化;
    • 虚拟机会保证一个类的 () 方法在多线程环境中被正确加锁和同步;

代码演示:

public class Demo06ClassLoad {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(a.m);
        /*
        运行过程分析:
        1.加载到内存,会产生一个类对应的 Class 对象;
        2. 链接,链接结束后 m = 0;(默认值,个人理解是系统规定)
        3. 初始化
            调用(){
                  System.out.println("A 类静态代码块初始化");
                  	 m = 300;
                  	 m = 100
             }
                     m = 100;
         */
        
        /*
        运行结果:
        A 类静态代码块初始化
        A 类的无参构造函数初始化
        100

         */
    }
}

class A {

    static {
        System.out.println("A 类静态代码块初始化");
        m = 300;
    }

    static int m = 100;


    public A() {
        System.out.println("A 类的无参构造函数初始化");
    }
}
1. 类的初始化
  1. 类的主动引用(一定会发生类的初始化):

    • 当虚拟机启动,先初始化 main 方法所在的类;
    • new 一个类的对象;
    • 调用类的静态成员(除了 final 常量)和静态方法;
    • 使用 java.lang.reflect 包的方法对类进行反射调用;
    • 当初始化一个类,如果其父类没有被初始化,则会先初始化它的父类;
  2. 类的被动引用(不会发生类的初始化):

    • 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化;
    • 通过数组定义类的引用,不会触发此类的初始化;
    • 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了);

代码演示:

/*
测试类什么时候会初始化
 */
public class Demo07ClassInit {
    static {
        System.out.println("main被加载");
    }

    public static void main(String[] args) throws ClassNotFoundException {
        //1.主动引用 :极大消耗资源
     //   Son son = new Son();
        /*
        运行结果:
                main被加载
                父类被加载
                子类被加载
         */

        //2.反射也会产生主动引用
       // Class.forName("com.sd.day25_Annotation.Son");
         /*
        运行结果:
                main被加载
                父类被加载
                子类被加载
         */

        //3.不会产生类的引用方法
   //     System.out.println(Son.b);
        /*
        运行结果:
                main被加载
                父类被加载
                  2
         */

        //4.通过数组定义类的引用,不会触发此类的初始化;
        Son[] array = new Son[5];
        /*
        运行结果:
                main被加载
         */

        //5.引用常量不会触发此类的初始化
        System.out.println(Son.M);
        /*
        运行结果:
               main被加载
                1
         */
    }
}

class Father{
    static  int b =2;
    static {
        System.out.println("父类被加载");
    }
}

class Son extends Father{
    static {
        System.out.println("子类被加载");
        m = 300;
    }
    static int m = 100;
    static final int M = 1;
}
2. 类加载器的作用
  1. 类加载的作用:将 class 文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后再对堆中生成一个代表这个类的 java.lang.Class 对象,作为方法区中类数据的访问入口;
  2. 类缓存:标准的 Java SE 类加载器可以按照要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过 JVM 辣鸡回收机制可以回收这些Class对象;
3. 类加载器的类型
  1. 引导类加载器:用 C++ 编写,是 JVM 自带的类加载器,负责 Java 平台核心库(rt.jar),用来装载核心库。该加载器无法直接获取;

  2. 扩展类加载器:负责 jre/lib/ext 目录下的 jar 包 -D java.ext.dirs 指定目录下的 jar 包装入工作库;

  3. 系统类加载器:负责 java -classpath 或 -D java.class.path 所指的目录下的类与 jar 包装入工作,是最常用的加载器;

代码演示:

public class Demo08ClassLoader {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取系统类的加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);   //sun.misc.Launcher$AppClassLoader@18b4aac2
        
        //获取系统类加载器的父类加载器--->扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);   //sun.misc.Launcher$ExtClassLoader@1540e19d
        
        //获取扩展类加载器的父类加载器---->根加载器(C/C++) 获取不到会返回null
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);  //null

        //测试当前类的类加载器
        ClassLoader classLoader = Class.forName("com.sd.day25_Annotation.Demo08ClassLoader").getClassLoader();
        System.out.println(classLoader);  //sun.misc.Launcher$AppClassLoader@18b4aac2

        //测试 JDK 内置类的类加载器  ------> 根加载器
        classLoader = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader);    //null

        //如何获得系统类加载器可以加载的路径
        System.out.println(System.getProperty("java.class.path"));
        /*
        D:\Program Files\java\jdk\jre\lib\charsets.jar;
        D:\Program Files\java\jdk\jre\lib\deploy.jar;
        D:\Program Files\java\jdk\jre\lib\ext\access-bridge-64.jar;
        D:\Program Files\java\jdk\jre\lib\ext\cldrdata.jar;
        D:\Program Files\java\jdk\jre\lib\ext\dnsns.jar;
        D:\Program Files\java\jdk\jre\lib\ext\jaccess.jar;
        D:\Program Files\java\jdk\jre\lib\ext\jfxrt.jar;
        D:\Program Files\java\jdk\jre\lib\ext\localedata.jar;
        D:\Program Files\java\jdk\jre\lib\ext\nashorn.jar;
        D:\Program Files\java\jdk\jre\lib\ext\sunec.jar;
        D:\Program Files\java\jdk\jre\lib\ext\sunjce_provider.jar;
        D:\Program Files\java\jdk\jre\lib\ext\sunmscapi.jar;
        D:\Program Files\java\jdk\jre\lib\ext\sunpkcs11.jar;
        D:\Program Files\java\jdk\jre\lib\ext\zipfs.jar;
        D:\Program Files\java\jdk\jre\lib\javaws.jar;
        D:\Program Files\java\jdk\jre\lib\jce.jar;
        D:\Program Files\java\jdk\jre\lib\jfr.jar;
        D:\Program Files\java\jdk\jre\lib\jfxswt.jar;
        D:\Program Files\java\jdk\jre\lib\jsse.jar;
        D:\Program Files\java\jdk\jre\lib\management-agent.jar;
        D:\Program Files\java\jdk\jre\lib\plugin.jar;
        D:\Program Files\java\jdk\jre\lib\resources.jar;
        D:\Program Files\java\jdk\jre\lib\rt.jar;
        H:\2019Java-西开\Sd\out\production\HelloWorld;
        H:\2019Java-西开\Sd\lib\kotlin-stdlib-js.jar;
        C:\Program Files\JetBrains\IntelliJ IDEA 2018.1.6\lib\idea_rt.jar
         */
    }
}

7. 获取运行时类的完整结构

  1. 通过反射获取运行时类的完整结构
  2. Field、method、Constructor、Superclass、Interface、Annotation
  3. 实现的全部接口;
  4. 所继承的父类;
  5. 全部的构造器;
  6. 全部的方法;
  7. 全部的Field
  8. 注解;

代码演示:

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

//获取类的信息
public class Demo09ClassInformation {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class aClass = Class.forName("com.sd.day25_Annotation.user");

        //获得类的名字:包名+类名
        System.out.println(aClass.getName());  //com.sd.day25_Annotation.user
        //获得类的简单名字:类名
        System.out.println(aClass.getSimpleName());  //user


        /*
        获得类的属性
         */
        //找到public属性
        Field[] fields = aClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        //找到全部属性
        Field[] fields1 = aClass.getDeclaredFields();
        for (Field field : fields1) {
            System.out.println(field);
        }
        /*
        private int com.sd.day25_Annotation.user.age
        private java.lang.String com.sd.day25_Annotation.user.name
        private java.lang.String com.sd.day25_Annotation.user.password
         */

        //获得指定属性的值
        Field name = aClass.getDeclaredField("name");
        System.out.println(name);  //private java.lang.String com.sd.day25_Annotation.user.name

        /*
        获得类的方法
         */
        //获取本类及其父类的全部public方法
        Method[] methods = aClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        /*
        public java.lang.String com.sd.day25_Annotation.user.toString()
        public java.lang.String com.sd.day25_Annotation.user.getName()
        public void com.sd.day25_Annotation.user.setName(java.lang.String)
        public java.lang.String com.sd.day25_Annotation.user.getPassword()
        public int com.sd.day25_Annotation.user.getAge()
        public void com.sd.day25_Annotation.user.setAge(int)
        public void com.sd.day25_Annotation.user.setPassword(java.lang.String)
        public final void java.lang.Object.wait() throws java.lang.InterruptedException
        public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
        public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
        public boolean java.lang.Object.equals(java.lang.Object)
        public native int java.lang.Object.hashCode()
        public final native java.lang.Class java.lang.Object.getClass()
        public final native void java.lang.Object.notify()
        public final native void java.lang.Object.notifyAll()
         */

        //获得本类的所有方法
        System.out.println("---------------------------");
        methods = aClass.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        /*
        public java.lang.String com.sd.day25_Annotation.user.toString()
        public java.lang.String com.sd.day25_Annotation.user.getName()
        public void com.sd.day25_Annotation.user.setName(java.lang.String)
        public java.lang.String com.sd.day25_Annotation.user.getPassword()
        public int com.sd.day25_Annotation.user.getAge()
        public void com.sd.day25_Annotation.user.setAge(int)
        public void com.sd.day25_Annotation.user.setPassword(java.lang.String)
         */

        /*
        获得指定的方法
         */
        Method getName = aClass.getMethod("getName", null);
        Method setName = aClass.getMethod("setName", String.class);
        System.out.println(getName);  //public java.lang.String com.sd.day25_Annotation.user.getName()
        System.out.println(setName);  //public void com.sd.day25_Annotation.user.setName(java.lang.String)

        /*
        获得类的构造器
         */
        //获得public 方法
        Constructor[] constructors = aClass.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        /*
        public java.lang.String com.sd.day25_Annotation.user.getName()
        public void com.sd.day25_Annotation.user.setName(java.lang.String)
         */

        //获得所有的方法
        constructors = aClass.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        /*
        public com.sd.day25_Annotation.user(int,java.lang.String,java.lang.String)
        public com.sd.day25_Annotation.user(int,java.lang.String,java.lang.String)
         */
        
        /*
        获得指定的构造器
         */
        //欲获得user方法,里边的参数为 int age, String name, string password
        Constructor declaredConstructor = aClass.getDeclaredConstructor(int.class, String.class, String.class);
        System.out.println("指定的构造器:" + declaredConstructor);
        //指定的构造器:public com.sd.day25_Annotation.user(int,java.lang.String,java.lang.String)
    }
}

7. Class对象的作用

1. 创建类的对象

调用 Class 对象的 newInstance() 方法;

2. 创建时需求

  1. 类必须有一个无参数的构造器;
  2. 类的构造器的访问权限需要足够;

3. 对于没有无参构造器的类创建对象

需要在操作的时候明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作;

步骤如下:

  1. 通过 Class 类的 getDeclaredConstructor(Class … parameter Types) 取得本类的指定形参类型的构造器;
  2. 向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数;
  3. 通过 Constructor 实例化对象;

4. 调用指定的方法

  1. 通过反射,调用类中的方法,通过 Method 类完成;
  2. 通过 Class 类的 getMethod(String name, Class … parameterTypes)方法取得一个 Method 对象,并设置此方法操作时所需要的参数类型;
  3. 之后使用 Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的 obj 对象的参数信息;
  4. Object invoke(Object obj, Object[] args);
    • Object 对应原方法的返回值,若原方法无返回值,此时返回 null ;
    • 若原方法若为静态方法,此时形参 Object obj 可为 null;
    • 若原方法形参列表为空, 则 Object[] args 为 null;
    • 若原方法声明为 private, 则需要在调用此 invoke() 方法前,显式调用方法对象的 setAccessible(true)方法,将可访问 private() 的方法;
  5. setAccessible
    • Method 和 Field、Constructor 对象都有 setAccessible() 方法;
    • setAccessible 作用是启动和禁止访问安全检查的开关;
    • 参数值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查;
      • 提高反射的效率,如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为 true;
      • 使得原本无法访问的私有成员也可以访问;
    • 参数值为 false 则指示反射的对象应该实时 Java 语言访问检查;

代码演示:

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/*
动态的创建对象,通过反射
 */
public class Demo10 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        /*
        构造一个对象:含有无参构造器
         */
        //获得 Class 对象
        Class aClass = Class.forName("com.sd.day25_Annotation.Person");
        //调用了无参构造器
        Person person = (Person) aClass.newInstance();
        System.out.println(person);  //Person{name='null'}

        /*
        通过反射调用普通方法
         */
        
        Person person1 = (Person) aClass.newInstance();
        //通过放射获取一个方法
        //invoke :激活的意思
        //invoke(对象,“方法的值”)
        Method setName = aClass.getDeclaredMethod("setName", String.class);
        setName.invoke(person1, "灭霸");
        System.out.println(person1.getName());  //灭霸

        /*
        通过反射操作属性
            如属性为私有,采用setAccessible(true);关掉安全警告
         */
        Person person2 = (Person) aClass.newInstance();
        Field name = aClass.getDeclaredField("name");

        name.set(person2, "灭绝");
        System.out.println(person2.getName());  //灭绝

        /*
        构造一个对象:没有无参构造器
         */
        
        //获得 Class 对象
        Class aClass1 = Class.forName("com.sd.day25_Annotation.user");
        // 获得构造器
        Constructor constructor = aClass1.getDeclaredConstructor(int.class, String.class, String.class);
        //创建对象
        user userNo1 = (user) constructor.newInstance(25, "李四", "123456");
        System.out.println(userNo1);  //user{age=25, name='李四', password='123456'}
    }
}

8. 性能对比分析

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/*
分析性能问题
 */
public class Demo11PerformanceContrast {
    //普通方式调用
    public static void test01() {
        Person person = new Person();

        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 100000000; i++) {
            person.getName();
        }

        long endTime = System.currentTimeMillis();

        System.out.println("普通方式执行10亿次时间:" + (endTime - startTime) + "ms");
    }

    //反射方式调用
    public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Person person = new Person();
        Class c1 = person.getClass();
        Method getName = c1.getDeclaredMethod("getName", null);

        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 100000000; i++) {
            getName.invoke(person, null);
        }

        long endTime = System.currentTimeMillis();

        System.out.println("反射方式执行10亿次时间:" + (endTime - startTime) + "ms");
    }

    //反射方式调用 关闭检测
    public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Person person = new Person();
        Class c1 = person.getClass();
        Method getName = c1.getDeclaredMethod("getName", null);
        getName.setAccessible(true);

        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 100000000; i++) {
            getName.invoke(person, null);
        }

        long endTime = System.currentTimeMillis();

        System.out.println("关闭检测后反射方式执行10亿次时间:" + (endTime - startTime) + "ms");
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        test01();  //普通方式执行10亿次时间:7ms
        test02();  //反射方式执行10亿次时间:455ms
        test03();  // 关闭检测后反射方式执行10亿次时间:177ms
    }
}

9. 获取泛型信息:

反射操作泛型:

  1. Java 采用泛型擦除的机制来引入泛型,Java 中的泛型仅仅是给编辑器 javac 使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除;
  2. 为了通过反射操作这些类型,Java 新增了 ParameterizedType,GenericArrayType,TypeVariable 和 WildcardType 几种类型来代表不能被归一到 Class 类中的类型但是又和原始类型齐名的类型;
  3. ParameterizedType:表示一种参数化类型,比如 Collection;
  4. GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型;
  5. TypeVariable :是各种类型变量的公共父接口;
  6. WildcardType :代表一种通配符类型表达式;

代码演示:

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

/*
通过反射获取泛型
 */
public class Demo12 {

    //泛型参数
    public void test01(Map<String, user> map, List<user> list) {
        System.out.println("test01");
    }

    //泛型返回值
    public Map<String, user> test02() {
        System.out.println("test02");
        return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {
        Method method = Demo12.class.getMethod("test01", Map.class, List.class);

        //获得泛型参数信息
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println("泛型参数信息:" + genericParameterType);
            if(genericParameterType instanceof ParameterizedType){
                Type[] actualTypeArguments =((ParameterizedType)genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println("真实泛型参数信息:" + actualTypeArgument);
                }
            }
        }
        /*
        泛型参数信息:java.util.Map
        真实泛型参数信息:class java.lang.String
        真实泛型参数信息:class com.sd.day25_Annotation.user
        泛型参数信息:java.util.List
        真实泛型参数信息:class com.sd.day25_Annotation.user
         */

        method = Demo12.class.getMethod("test02", null);
        Type genericReturnType = method.getGenericReturnType();
        if(genericReturnType instanceof ParameterizedType){
            Type[] actualTypeArguments =((ParameterizedType)genericReturnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println( actualTypeArgument);
            }
        }
        /*
        class java.lang.String
        class com.sd.day25_Annotation.user
         */
    }
}

4. 反射操作注解

1. ORM

对象映射关系(Object relationship Mapping, ORM);
Day21——反射_第1张图片

  • 类和表结构对应;
  • 属性和字段对应;
  • 对象和记录对应;

代码演示:

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;

/*
反射操作注解
 */
public class Demo13ReflectionAndAnnotation {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("com.sd.day25_Annotation.Student1");

        //通过反射获得注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);  //@com.sd.day25_Annotation.TableTest(value=db_Student)
        }

        //获得注解value的值
        TableTest tableTest = (TableTest) c1.getAnnotation(TableTest.class);
        String value = tableTest.value();
        System.out.println(value);  //db_Student

        //获得指定类的注解
        Field f = c1.getDeclaredField("name"); //可将 name 换位 id 或者 age
        FieldTest annotation = f.getAnnotation(FieldTest.class);
        System.out.println(annotation.columnName());  //db_name
        System.out.println(annotation.type());  //int
        System.out.println(annotation.length());  //3
    }


}

//创建类
@TableTest("db_Student")
class Student1 {
    @FieldTest(columnName = "db_id", type = "int", length = 10)
    private int id;
    @FieldTest(columnName = "db_age", type = "int", length = 10)
    private int age;
    @FieldTest(columnName = "db_name", type = "int", length = 3)
    private String name;

    public Student1() {
    }

    public Student1(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

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

//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableTest {
    String value();
}

//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldTest {
    String columnName();
    String type();
    int length();
}

你可能感兴趣的:(Day21——反射)