JavaSE_注解与反射完整笔记(反射:Class类和Class对象+反射概念的引入、Java内存+类的加载、Class对象获取类方法字段信息+创建对象调用方法操作属性、ORM+反射读取注解)

注解与反射

image-20210720103518119

1.注解

1.1什么是注解

Java.Annotation是从JDK5.0开始引入的新技术

  • 注解和反射是所有框架的底层实现机制

  • 注释是给人看的,注解不仅给人看,还能给程序看

Annotation的作用:

  • 不是程序本身,可以对程序做出解释(这一点和注释comment没什么区别)
  • 可以被其他程序(比如:编译器等)读取

Annotation的格式:

  • @注解名
  • 还可以添加参数值,例如@SuppressWarnings(value=“unchecked”)

Annotation在哪里使用:

  • 可以附加在package、class、method、field等上面,相当于给他们添加了额外的辅助信息
  • 我们可以通过反射机制编程 实现对这些元数据的访问

1.2内置注解

Java.Annotation有3个内置注解

  1. @Override:定义在java.lang.Override中,只能修饰method,表示子类打算覆写父类的方法的声明

  2. @Deprecated:定义在java.lang.Deprecated中,可以修饰class、method、field,表示不鼓励程序员使用这样的元素,通常是因为它很危险或者存在更好的替代实现

  3. @SuppressWarnings:定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息

    其需要参数,但是这些参数都是定义好的:

    @SuppressWarnings("all")
    @SuppressWarnings("unchecked")
    @SuppressWarnings(value={"unchecked","deprecation"})
    等等...
    

测试:

public class Test01 extends Object{
    @Override
    public String toString(){
        return super.toString();
    }
    
    @Deprecated
    public static void test02(){
        System.out.println("Deprecated");
    }

    @SuppressWarnings("all")
    public void test01(){
        System.out.println("SuppressWarnings");
    }

    public static void main(String[] args) {
        test02();
    }
}

1.3元注解

  • Java.Annotation定义了4个标准的元注解meta-annotation类型,他们被用来解释对其他注解

    @Target: 注解可以被用在哪些地方,如class、method、field;
    @Retention:注解的生命周期,在什么级别保存该注解信息 source<class<runtime;
    
    @Document:说明该注解将被包含在javadoc中;
    @Inherited:说明子类可以继承父类的注解;
    
  • 4个元注解+@interface 可以自定义注解

    public class Test02 {
        
        @Myannotation
        public void test01(){
            System.out.println("自定义注解");
        }
    }
    
    //定义一个注解,一般只要@Target和@Retention
    @Target(value={ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
    @Retention(value= RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @interface Myannotation{
    
    }
    

1.4自定义注解

使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口

分析:

  • @interface用来声明一个注解,格式为:public @interface 注解名{…} 在类里面声明需要把public去掉
  • 其中的配置参数,写成了方法的形式,但是仍是参数。
    • 参数类型只能是基本类型,Class,String,enum
    • 可以通过default来声明参数的默认值,如空字符串、0
    • 如果只有一个参数成员,一般参数名为value
  • 注解参数如果没有默认值,就必须有值

测试:

public class Test02 {
    
    //去掉哪一个注解的默认值,就只用给那一个注解的默认值赋值就可以了,注解没有顺序
    //注解可以显示赋值,如果没有默认值,我们就必须给注解赋值
    @Myannotation1
    public void test01(){
        System.out.println("自定义注解1");
    }

    @Myannotation2("joseph")
    public void test02(){
        System.out.println("自定义注解2");
    }
}

//定义一个注解,一般只要@Target和@Retention
@Target(value={ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(value= RetentionPolicy.RUNTIME)
@Documented
@Inherited
@interface Myannotation1{
    //注解的参数:参数类型 + 参数名();
    //加了default之后,@MyAnnotation()就可以不在里面加参数 也可以赋值
    String name() default "";
    int age() default 0;
    int id() default -1; //如果默认值为-1,代表不存在;与indexof类似,如果找不到就返回-1;

    String[] scholls() default {"qinghua","peking"};
}


//定义一个注解,一般只要@Target和@Retention
@Target(value={ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(value= RetentionPolicy.RUNTIME)
@Documented
@Inherited
@interface Myannotation2{
    //如果只有一个参数,则名字使用value;在使用注解时,名字可省略
    String value();
}

2.反射

Java.Reflection

静态语言和动态语言:

  • 动态语言:

    • 是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗讲就是在运行时代码可以根据某些条件改变自身结构

    • 常见动态语言:JavaScript、PHP、Python、C#、Object-C

      //体现动态语言的代码
      function test() {
          var x = "var a=3;var b=5;alert(a+b)";
          eval(x);//在运行时改变函数代码
      }
      
  • 静态语言:

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

2.1反射引入

  • Reflection(反射)是Java被视为动态语言的关键

  • 反射机制 允许程序在执行期间 借助Reflection API 取得任何类的内部信息并能直接操作任意对象的的内部属性及方法

Class c = Class.forName("java.lang.String")

1. Class类及Class类的对象:

  1. Java中存在一个Class类型的类,即Java中的类分为Class类和其他类;

  2. 某个类加载完之后,在堆内存的方法区中就产生了这个类的Class类型的对象

    区分“类”和这个“类的Class对象”,通过类的Class对象可以得到这个类的全部信息

  3. 每个类在内存中都只有一个Class对象,我们可以通过“类的对象”(以及类名等)得到这个类对应的Class对象。通过这个对象我们可以看到类的结构。

    这个Class对象就像是一面镜子,通过这面镜子可以看到类的结构,所以我们形象的称之为反射。

**反射定义:**每个类都唯一对应一个Class对象,通过这个对象我们可以得到这个类的所有信息。获取某个类Class对象的过程——我们称之为反射。

2.正常方式和反射方式new对象:

image-20210717221530753

//正常方式
Student stu1=new Student();

//反射方式
Student stu2=Class.forName("java.lang.String").newInstance();
Student stu3=stu1.getClass().newInstance();

3.举例:一个类只存在一个Class对象

实体类User:

public class User {
    private int id;
    private String name;
    private int age;

    //无参构造
    public User() {
    }

    //有参构造
    public User(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    //getter和setter
    public int getId() {
        return id;
    }

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

    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;
    }

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

测试:

public class Test01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //通过反射获取类对应的Class对象

        //一个类被加载后,类的整个结构信息会被放到对应的Class对象中
        Class<?> c1 = Class.forName("com.kuang.reflection.User");
        System.out.println(c1);

        //每个类都有一个Class对象
        Class<?> c2 = Class.forName("com.kuang.reflection.User");
        System.out.println(c1.hashCode());
        System.out.println(c2.hashCode()); //两次获取的User的Class对象hashCode一致,表明是同一个Class对象
    }
}

image-20210717223326563

4.反射机制提供的功能

在运行时:

  • 判断任意一个对象所属的类

  • 判断任意一个类所具有的的成员变量和方法

  • 调用任意一个对象对应类的成员变量和方法

  • 构造任意一个类的对象

  • 获取泛型信息

  • 处理注解

  • 生成动态代理……

5.反射的优点和缺点

优点:

  • 可以实现动态创建对象和编译,体现出很大的灵活性。

缺点:

  • 对性能有影响
  • 使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它能满足我们的要求
  • 这类操作总是慢于 直接执行相同的操作

6.反射相关的主要API

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

……

2.2Class类及Class对象

1.区分Class类和其他类,每个类都有唯一对应的Class对象

  • Object类被所有类继承,其中定义了public final Class getClass()方法,因此所有的类的对象都能调用该方法获取 自己对应类 对应的Class对象。

  • 该方法返回类型是一个Class类,此类是Java反射的源头

  • 实际上所谓反射 从程序的运行结果来看 也很好理解,即:可以通过类的对象 反射得到 类对应Class对象,从而得到类的所有信息

image-20210717225223803

  • 对象照镜子(反射)以后可以得到的信息:某个类的属性、方法、构造器;实现了哪些接口;
  • 没有每个类而言,JRE都为其保留一个不变的Class对象,一个Class对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。

2.Class对象理解:

  • Class本身也是一个类,只不过是比较特殊的类。

  • Class对象只能由系统建立

  • 一个加载的类在JVM中只会有一个Class实例对象

  • 一个Class对象对应的是一个加载到JVM中的 .class文件

  • 通过Class对象可以完整地得到一个类中所有被加载的结构

  • 每个类的实例都会记得自己是由哪个Class实例所生成

  • Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

3.获取Class对象的方式

每个类都继承了Object类,Object类中有一个静态成员变量class,和getClass()方法

  • 通过对象获得

    Class<? extends Person> c1 = p1.getClass();
    
  • 通过Class.forName(“包名+类名”)获得

    Class<?> c2 = Class.forName("com.kuang.reflection.Student");
    
  • 通过类的静态成员class获得

    Class<?> c3 =Person.class;
    
  • 针对内置的基本数据类型

    Class<?> c4 =Integer.TYPE;
    
  • 还可以利用ClassLoader,后面讲解

测试:

public class Person {
    private String name;

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

    public String getName() {
        return name;
    }

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

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

public class Student extends Person{
    public Student(){
        super.setName("学生");
    }
}

public class Teacher extends Person{
    public Teacher(){
        super.setName("老师");
    }
}

//**************************************
public class Test02 {
    public static void main(String[] args) throws ClassNotFoundException {
        Person p1=new Student();
        System.out.println("这个人是:"+p1.getName());

        //获取Class对象方法一:通过对象获得,如果是子类的实现,赋值给父类,得到的时子类对应的Class对象
        Class<? extends Person> c1 = p1.getClass();

        //获取Class对象方法二:通过Class.forName("类名+包名")获得
        Class<?> c2 = Class.forName("com.kuang.reflection.Student");

        //获取Class对象方法三:通过类的静态成员变量class获得
        Class<?> c3 =Person.class;

        //获取Class对象方法四:只针对内置的基本数据类型
        Class<?> c4 =Integer.TYPE;


        //获取父类的Class
        Class<?> superc1 = c1.getSuperclass();

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(superc1);
    }
}

image-20210717232215934

4.Class类的常用方法

  • Object newInstance() 调用类的缺省构造函数,返回类的一个对象实例

  • getName() 返回Class对象所表示的实体 的名称(类的名称)

  • Class getSuperClass() 得到父类的Class对象

  • Class[] getinterfaces() 得到当前Class对象对应的类 的所有接口 对应的Class对象

  • ClassLoader getClassLoader() 返回该类的类加载器

  • Constructor[] getConstructors() 返回一个包含某些Constructor对象的数组

  • method getMothed(String name, Class.. T) 返回一个Method对象,此对象的形参类型为paramType

  • Filed[] getDeclaredFields() 返回Field对象的一个数组

5.哪些类型可以有Class对象

在Java中,万物皆类,万物皆对象

  • class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
  • interface:接口
  • []:数组
  • enum:枚举
  • annotation:注解
  • primitive type:基本数据类型
  • void

测试:

public class Test03 {
    public static void main(String[] args) {
        //测试哪些类型有Class对象
        Class c1=Object.class; //类
        Class c2=Comparable.class; //接口
        Class c3=int[].class; //一维数组
        Class c4=int[][].class; //二维数组
        Class c5= ElementType.class; //枚举
        Class c6=Override.class; //注解
        Class c7=Integer.class; //基本数据类型
        Class c8=void.class; //void
        Class c9=Class.class;

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);

        //只有元素类型和维度一样,就对应一个Class对象
        int[] a=new int[10];
        int[] b=new int[100];
        Class Ca=a.getClass();
        Class Cb=b.getClass();
        System.out.println(Ca==Cb);

    }
}

image-20210717234603866

2.3Java内存分析

1.Java内存

  • 栈:

    • 一般用来存放用关键字new出来的数据
  • 堆:

    • 基本数据类型

    • 局部变量(在方法代码段中定义的变量),方法调用完后JVM回收

    • 引用数据类型——即需要用关键字new出来的对象所对应的引用 也是存放在栈空间中。

      此时JVM在栈空间中给对象引用分配了一个地址空间,存储引用变量,

      指向,在堆空间给该引用指向的实际对象分配的地址空间。

  • 方法区:

    • 用于存放已被加载的类信息、常量、static静态变量、即时编译器编译后的代码等数据

2.4类的加载

1.类的加载过程

  • 类加载是一个将.class字节码文件读入内存,并实例化为Class对象进行相关初始化的过程

  • java的类使用时才被加载,且类的加载过程只发生一次

java类的加载过程可分为三个步骤:加载、链接、初始化

区分“类的加载过程”和其步骤“加载”

“类的初始化”只是“类的加载过程”的一个步骤

  • 加载:

    • 将.class字节码文件读入内存,将静态数据转换成方法区的运行时数据结构

    • 生成这个类对应的Class对象

      Class对象是加载到内存中才会产生的,由JVM创建,我们只能获取,不能生成

      通过反射动态进行的

  • 链接:将Java类的二进制代码 合并到 JVM的运行状态之中

    • 验证:确保类信息符合JVM规范
    • 准备:为static变量分配内存并设置初始值(在类初始化之前做),在内存的方法区进行
    • 解析:JVM常量池内的符号引用(常量名)替换为直接引用(地址)
  • 初始化:

    • JVM执行类构造器<clinit>()方法的过程。

      类构造器<clinit>()方法是由编译期 自动收集类中所有类变量的赋值动作static代码块中的语句合并产生的。

      类构造器是构造类信息的,不是构造该类对象的构造器。

    • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。

    • JVM会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。

代码测试:

public class Test05 {
    public static void main(String[] args) {
        A a=new A();
        System.out.println(A.m);
    }
}

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


    public A(){
        System.out.println("A类的无参构造初始化");
    }
}

image-20210718172415458

image-20210718080743211

过程:

  1. 加载阶段:加载Test05类和A类,在方法区分别生成其对应的Class对象

  2. 链接阶段:为static变量m,在方法区分配内存空间,并进行默认初始化

    static int m=0;
    
  3. 初始化阶段:类的<clinit>()方法将static变量赋值和static代码合并,再执行赋值

    System.out.println("A类静态代码快初始化");
    m=300;
    m=100;
    //定义阶段已经在链接阶段完成
    

2.类加载时机和初始化时机

  • **类加载时机:**从执行包含main函数的类开始加载,涉及到下面情况的类就加载

    • 使用类的静态变量或静态方法
    • 使用反射方式来强制创建某个类或接口 对应的java.lang.Class对象
    • 创建类的实例
    • 初始化某个类的子类
    • (直接使用java。exe命令来运行某个主类)
  • **类的初始化理解:**类的初始化 主要就是对类的static变量进行初始化

    区分实例变量和静态变量,实例变量在new对象时初始化

    • JVM执行类构造器<clinit>()方法的过程。

      类构造器<clinit>()方法是由编译期 自动收集类中所有类变量的赋值动作static代码块中的语句合并产生的。

      类构造器是构造类信息的,不是构造该类对象的构造器。

    • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。

    • JVM会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。

  • 会发生类初始化的情况(类的主动引用):

    1. 当JVM启动,先初始化main方法所在的类
    2. 使用new关键字创建一个类的对象
    3. 调用该类的static变量(final的常量除外)和static方法
    4. 使用java.lang.reflect包对该类进行反射调用
    5. 当初始化一个类时,如果其父类没有被初始化,则会先初始化其父类
  • 不会发生类初始化的情况(类的被动引用):

    1. 引用static常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)

    2. 当访问一个静态成员变量时,只有真正声明这个静态成员的类才会被初始化,当通过子类引用父类的静态成员变量时,不会导致子类的初始化

    3. 通过数组定义类引用,不会触发此类的初始化。

      数组只是一个名字和一片空间

测试:

//测试类什么时候会初始化
public class Test06 {
    static {
        System.out.println("Main类被加载");
    }
    public static void main(String[] args) throws ClassNotFoundException {
        //1.主动引用,会发生类的初始化
        Son son = new Son();
        /*
        Main类被加载
        父类被加载
        子类被加载
         */

        //反射也会产生主动引用
        Class.forName("com.kuang.reflection.Son");
        /*
        Main类被加载
        父类被加载
        子类被加载
         */

        //2.被动引用,不会触发类的初始化
        //通过子类调用父类static变量或者方法
        System.out.println(Son.b);
        /*
        Main类被加载
        父类被加载
        2
         */

        //通过数组定义类引用
        Son[] array=new Son[5];
        /*
        Main类被加载
         */

        //类的常量
        System.out.println(Son.M);
        /*
        Main类被加载
        1
         */
    }
}

class Father{
    static final int M=1;
    static int b=2;
    static{
        System.out.println("父类被加载");
    }
}
class Son extends Father{
    static{
        System.out.println("子类被加载");
        m=300;
    }
    static int m=100;
}

3.类加载器

什么是类加载器?

类加载器 就是 加载字节码文件(.class)的类

image-20210719084626724

Java程序是一种具有动态性的解释性语言,类(class)只有被加载到JVM中后才能运行。

当运行指定程序时,JVM会将编译生成的.class文件按照需求和一定的规则加载到内存中,组织成为一个完整的Java应用程序。

这个加载的过程是由类加载器来完成的。具体来说,就是由ClassLoader和它的子类来实现的。

类加载器本身也是一个类,其实质是把类文件从硬盘读取到内存中

类加载的方式:

  • 隐式加载:使用new等方式创建对象,会隐式调用类加载器把对应的类加载到JVM中
  • 显式加载:通过反射直接调用Class.forName()方法把所需的类加载到JVM中。

类加载器分类:

在Java语言中,类的加载是动态的,它并不会一次性将所有类全部加载后再运行。而是保证程序运行的基础类(例如基类)完全加载到JVM中。

其他类,在需要时才加载。

  • 引导类加载器:BootStapLoader,加载最基础的文件
    • 用C++编写,是JVM自带的类加载器,负责Java平台核心类库
    • 该加载器程序无法直接获取。
    • jre/lib/rt.jar包下
  • 扩展类加载器:ExtClassLoader,加载基础的文件
    • jre/lib/ext/*.jar包下
  • 应用类加载器:AppClassLoader,加载三方jar包和自己编写的java文件
    • CLASSPATH指定的所有jar和目录

image-20210719090832845

类加载器的协调工作:双亲委派机制

3种类加载器是通过委托方式实现:

当加载一个类,JVM会自底向上去找是否已经加载;

且不能存在同名,如我们不能定义java.lang.Spring类;

双亲委派机制会检测安全性,保证创建出的类的唯一性

  • 为了防止同名包、类与jdk中相冲突
  • 实际加载类的时候,先通知appClassLoader看appClassLoader是否已经缓存;没有的话,appClassLoader又委派给他的父类加载器(extClassLoader)询问,看他是不是已经缓存;没有的话,exClassLoader又委派给他的父类BootStapLoader询问,看他是不是已经缓存或者能加载,有就加载;没有再返回extClassLoader,exClassLoader能加载就加载,不能的话再返回给自己的appClassLoader加载。在返回的路上,谁能加载,加载的同时也放到缓存当中去。
  • 正是由于刚开始时自底向上不停地找自己的父级,所以才有Parents加载机制,翻译过来叫双亲委派机制。

加载时:

  • 自底向上检查类是否已经装载

  • 自顶向下尝试加载类

获取测试:

public class Test07 {
    public static void main(String[] args) {
        //获取AppClassLoader
        ClassLoader appClassLoader=ClassLoader.getSystemClassLoader();        
        System.out.println(appClassLoader);

        //获取ExtClassLoader
        ClassLoader extClassLoader=appClassLoader.getParent();
        System.out.println(extClassLoader);

        //获取BootStapClassLoader
        ClassLoader bootStapClassLoader=extClassLoader.getParent();
        System.out.println(bootStapClassLoader);
    }
    	//测试当前变量是由那个加载器加载的
    	ClassLoader curClassLoader=Class.forName("com.kuang.reflection.Test07").getClassLoader();
}

image-20210719092301830

可以看出:

Test07类是由AppClassLoader加载的。

因为BootStapLoader先搜索指定目录找不到,其次ExtClassLoader也找不到,最后AppClassLoader在ClassPath中找到了Test07类。

【注意】BootStapLoader是用C++语言实现的,所以在Java语言中看不到它,输出null

类加载器 加载过程 分为以下3步:

  • 装载(将.class文件放入内存,生成Class对象)
  • 链接(1.检查:检查待加载.class文件的正确性;2.准备:给类中静态变量分配存储空间并默认初始化;3.解释:将符号引用转换成直接引用)
  • 初始化(对静态变量和静态代码块执行初始化工作)

2.5利用反射获取Class对象 获取运行时类的完整结构

  • Field、Method、Constructor、Superclass、Interface、Annotation

  • 在实际操作中,取得类的信息的操作代码,并不会经常开发

  • 一定要熟悉java.lang.reflect包的作用,反射机制

  • 如何取得一个类的属性、方法、构造器名称,修饰符等

测试:

public class Test08 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class<?> c1 = Class.forName("com.kuang.reflection.User");

        User user=new User();
        c1=user.getClass();

        //1.获得类的名字
        System.out.println(c1.getName()); //获得包名+类名
        System.out.println(c1.getSimpleName()); //获得类名

        //2.获得类的属性
        System.out.println("========================");
        Field[] fields = c1.getFields(); //只能找到public属性

        fields=c1.getDeclaredFields(); //找到当前类的所有属性
        for (Field field : fields) {
            System.out.println(field);
        }
        /*
        private int com.kuang.reflection.User.id
        private java.lang.String com.kuang.reflection.User.name
        private int com.kuang.reflection.User.age
         */

        //找到指定属性
        Field name = c1.getDeclaredField("name");
        System.out.println(name);

        //3.获得类的方法
        System.out.println("========================");
        Method[] methods = c1.getMethods(); //获得本类及其父类的所有public方法
        for (Method method : methods) {
            System.out.println("正常的:"+method);
        }
        methods = c1.getDeclaredMethods(); //获得本类的所有方法
        for (Method method : methods) {
            System.out.println("Declare:"+method);
        }

        Method getName = c1.getMethod("getName", null); //获取指定方法
        Method setName = c1.getMethod("setName", String.class);
        System.out.println("指定的:"+getName);
        System.out.println("指定的:"+setName);

        //4.获得类的构造方法
        System.out.println("========================");
        Constructor<?>[] constructors = c1.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println("正常的:"+constructor);
        }

        constructors = c1.getDeclaredConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println("Declare:"+constructor);
        }

        Constructor<?> declaredConstructor = c1.getDeclaredConstructor(int.class, String.class, int.class);
        System.out.println("指定的:"+declaredConstructor);

    }
}

结果:

com.kuang.reflection.User
User
========================
private int com.kuang.reflection.User.id
private java.lang.String com.kuang.reflection.User.name
private int com.kuang.reflection.User.age
private java.lang.String com.kuang.reflection.User.name
========================
正常的:public java.lang.String com.kuang.reflection.User.toString()
正常的:public java.lang.String com.kuang.reflection.User.getName()
正常的:public int com.kuang.reflection.User.getId()
正常的:public void com.kuang.reflection.User.setName(java.lang.String)
正常的:public void com.kuang.reflection.User.setId(int)
正常的:public int com.kuang.reflection.User.getAge()
正常的:public void com.kuang.reflection.User.setAge(int)
正常的: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()
Declare:public java.lang.String com.kuang.reflection.User.toString()
Declare:public java.lang.String com.kuang.reflection.User.getName()
Declare:public int com.kuang.reflection.User.getId()
Declare:public void com.kuang.reflection.User.setName(java.lang.String)
Declare:public void com.kuang.reflection.User.setId(int)
Declare:public int com.kuang.reflection.User.getAge()
Declare:public void com.kuang.reflection.User.setAge(int)
指定的:public java.lang.String com.kuang.reflection.User.getName()
指定的:public void com.kuang.reflection.User.setName(java.lang.String)
========================
正常的:public com.kuang.reflection.User()
正常的:public com.kuang.reflection.User(int,java.lang.String,int)
Declare:public com.kuang.reflection.User()
Declare:public com.kuang.reflection.User(int,java.lang.String,int)
指定的:public com.kuang.reflection.User(int,java.lang.String,int)

2.6反射有了Class对象以后 能做什么?

1.创建对象

  • 直接利用newInstance()创建一个对象,适用于存在无参构造

    User user = (User) c1.newInstance();
    
  • 反射获取构造器,进而创建对象,适用于有参构造

    Constructor<?> constructor = c1.getDeclaredConstructor(int.class, String.class, int.class);
    User user2 = (User) constructor.newInstance(1, "kuangshen", 18);
    

2.反射调用类中方法

User user3=(User)c1.newInstance();
//通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);

//invoke使用方法(对象,方法的参数)
setName.invoke(user3,"kuangshen2");
System.out.println("user3:"+user3);

3.反射操作属性

//3.利用反射操作属性
User user4=(User)c1.newInstance();
Field name = c1.getDeclaredField("name");

//不能直接操作私有属性,需要开启允许访问,关闭安全检测。方法、字段、构造器都可以设置开启允许访问
name.setAccessible(true);

//操作属性(对象,属性值)
name.set(user4,"kuangshen3");
System.out.println("user4:"+user4);

测试:

public class Test09 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        //获得Class对象
        Class<?> c1 = Class.forName("com.kuang.reflection.User");

        //1.1直接利用newInstance()创建一个对象,仅适用于存在无参构造
        User user = (User) c1.newInstance();
        System.out.println("user:"+user);

        //1.2反射获取构造器,进而创建对象,使用于有参构造
        Constructor<?> constructor = c1.getDeclaredConstructor(int.class, String.class, int.class);
        User user2 = (User) constructor.newInstance(1, "kuangshen", 18);
        System.out.println("user2:"+user2);

        //2.通过反射调用方法
        User user3=(User)c1.newInstance();
        //通过反射获取一个方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        //invoke使用方法(对象,方法的值)
        setName.invoke(user3,"kuangshen2");
        System.out.println("user3:"+user3);

        //3.利用反射操作属性
        User user4=(User)c1.newInstance();
        Field name = c1.getDeclaredField("name");

        //不能直接操作私有属性,需要开启允许访问,关闭安全检测。方法、字段、构造器都可以设置开启允许访问
        name.setAccessible(true);

        name.set(user4,"kuangshen3");
        System.out.println("user4:"+user4);

    }
}

image-20210719124212410

setAccessible:

  • Method和Field、Constructor对象都有setAccessible方法。
  • 开启允许利用反射访问私有字段、方法
  • 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,请设置为true

普通方法调用 和反射调用方法 性能对比:

public class Test10 {
    //普通方法调用
    public static void test01(){
        User user=new User();
        long startTime=System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            user.getName();
        }
        long endTime=System.currentTimeMillis();
        System.out.println("普通方式执行10亿次:"+(endTime-startTime)+"ms");
    }

    //反射调用方法
    public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user=new User();
        Class<? extends User> c1 = user.getClass();
        Method getName = c1.getDeclaredMethod("getName", null);

        long startTime=System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user,null);
        }
        long endTime=System.currentTimeMillis();
        System.out.println("反射方式执行10亿次:"+(endTime-startTime)+"ms");
    }

    //反射调用方法
    public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user=new User();
        Class<? extends User> c1 = user.getClass();
        Method getName = c1.getDeclaredMethod("getName", null);
        getName.setAccessible(true);

        long startTime=System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user,null);
        }
        long endTime=System.currentTimeMillis();
        System.out.println("关闭权限检测方式执行10亿次:"+(endTime-startTime)+"ms");
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        test01();
        test02();
        test03();
    }
}

image-20210719131449638

4.反射操作获取泛型

  • Java采用泛型擦除机制 来引入泛型

    Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,Java新增了ParameterizedType、GenericArrayType、TypeVariable和WildcardType几种类型来代表不能被归一到Class类中但是又能和原始类型齐名的类型。

  • ParameterizedType 表示参数化类型,比如Collection

  • GenericArrayType 表示一种元素类型是参数化类型或者类型变量的的数组类型

  • TypeVariable 是各种类型变量的公共父接口

  • WildcardType 代表一种通配符类型表达式

测试:

public class Test11 {
    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 = Test11.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);
                }
            }
        }
        
        //获得返回类型及其内部泛型
        method = Test11.class.getMethod("test02", null);
        Type genericReturnType = method.getGenericReturnType();

        System.out.println("返回类型:"+genericReturnType);
        if(genericReturnType instanceof ParameterizedType){
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println("返回类型内部泛型:"+actualTypeArgument);
            }
        }
    }
    
}

image-20210719135425989

2.7反射读取注解

**ORM:**object relationship Mapping 对象关系映射

image-20210719235227156

类对应表

属性对应字段

对象对应记录

要求:利用注解和反射完成类和表结构的映射关系

  • 框架会在类里面定义大量的注解,通过反射去读取,生成相应的信息

  • 假设数据库有一张表,可以通过注解去定义和表对应的类型,通过注解生成一些数据库的语言,然后进行增删改查,自动创建表

反射读取注解实例:

package com.kuang.reflection;

import java.lang.annotation.*;
import java.lang.reflect.Field;

//测试ORM:对象关系映射

//使用反射读取注解信息三步:
//1.定义注解
//2.在实体类中实用注解
//3.使用反射获取注解,一般都是现成框架实现,我们手动实现
public class Test12 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        //反射,Class对象可以获取类的全部信息
        Class<?> c1 = Class.forName("com.kuang.reflection.Student");
        //1.获得这个类的注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }

        //2.获得类的指定注解,指出属性名称,只有一个属性value
        TableKuang tableKuang = c1.getAnnotation(TableKuang.class);
        System.out.println(tableKuang.value());

        //2.获取类指定注解的值,指出属性名称,三个属性
        Field name = c1.getDeclaredField("name");
        FieldKuang fieldKuang = name.getAnnotation(FieldKuang.class);
        System.out.println(fieldKuang.columnName()+"-->"+fieldKuang.type()+"-->"+fieldKuang.length());

        //我们可以根据得到的类的信息,通过JDBC生成相关的SQL语句,执行就可以动态生成数据库表
    }
}

@TableKuang("db_student")
class Student{
    @FieldKuang(columnName = "db_id",type = "int",length = 10)
    private int id;
    @FieldKuang(columnName = "db_name",type = "varchar",length = 10)
    private String name;
    @FieldKuang(columnName = "db_age",type = "int",length = 3)
    private int age;

    public Student() {
    }

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

    public int getId() {
        return id;
    }

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

    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;
    }

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

//表名注解,只有一个参数,建议使用value命名
@Target(value={ElementType.TYPE})
@Retention(value= RetentionPolicy.RUNTIME)
@interface TableKuang{
    String value();
}

//属性注解
@Target(value={ElementType.FIELD})
@Retention(value=RetentionPolicy.RUNTIME)
@interface FieldKuang{
    String columnName();//列名
    String type();
    int length();
}

image-20210719235506220

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值