注解与反射

注解与反射

注解

1.什么是注解

Annotation是从JDK5.0开始引入的新技术
Annotation的作用:
  >不是程序本身,可以对程序作出解释(这一点和注释(comment)没什么区别
  >可以被其他程序(比如:编译器等)读取.

Annotation的格式:
  >注解是以@注释名"在代码中存在的,还可以添加一些参数值,例如:@SuppressWarnings(value="unchecked").

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

2.内置注解

>@Override :定义在java.lang.Override 中,此注释只适用于修辞方法,表示一个方法声明打算重写超类中的另一个方法声明.

>@Deprecated :定义在javalang Deprecated中,此注释可以用于修辞方法,属性,类,表示不鼓励程序员使用这样的元素,通常是因为它很危险或者存在更好的选择.

>@suppressWarnings :定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息.与前两个注释有所不同,你需更添加一个参数才能正确使用.这些参数都是已经定义好了的.我们选择性的使用就好了
	@SuppressWamings("all")
	@SuppressWamings("unchecked")
	@SuppressWarnings(value=("unchecked","deprecation")
	等等...

练习

import java.util.ArrayList;
import java.util.List;

/**
 * @className: Test01
 * @description: 内置注解
 * @author: penghailan
 * @create: 2021-05-26 11:03
 **/

public class Test01 extends Object {
    /*
    *
    * @override :定义在java.lang.Override 中,此注释只适用于修辞方法,
    * 表示一个方法声明打算重写超类中的另一个方法声明.
    * */
    @Override
    public String toString() {
        return super.toString();
    }

    /*
    *@Deprecated :定义在java.lang.Deprecated中,此注释可以用于修辞方法,属性,类,
    * 表示不鼓励程序员使用这样的元素,通常是因为它很危险或者存在更好的选择
    * */
    @Deprecated
    public static void test(){
        System.out.println("Deprecated");
    }

    /*
    *@SuppressWarnings :定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息.
    * 与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数都是已经定义好了的,
    * 我们选择性的使用就好了
        @suppressWarnings("all")
        @SuppressWarnings("unchecked")
        @SuppressWarnings(value=("unchecked","deprecation")
        等等 ..
    *
    * */
    @SuppressWarnings("all")
    public static void test02(){
       List list = new ArrayList<String>();
    }

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

3.元注解

元注解的作用就是负责注解其他注解
Java定义了4个标准的meta-annotation类型他们被用来提供对其他annotation类型作说明.
这些类型和它们所支持的类在java.lang.annotation包中可以找到(@Target ,@Retentlon ,@Documented, @Inherited)

@Target:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

@Retention :表示需要在什么级别保存该注释信息,用于描述注解的生命周期 (SOURCE<CLASS < RUNTIME)

@Document:说明该注解将被包含在javadoc中

@Inherited:说明子类可以继承父类中的该注解

练习

import java.lang.annotation.*;

/**
 * @className: Test02
 * @description: 元注解 (注解其他注解)
 * @author: penghailan
 * @create: 2021-05-26 11:18
 **/
@MyAnnotation
public class Test02 {
    public static void main(String[] args) {

    }

    @MyAnnotation
    public void test(){

    }
}

//定义一个注解
@Target({ElementType.METHOD,ElementType.TYPE})//表示我们的注解可以用在哪些地方
@Retention(RetentionPolicy.RUNTIME)//用于描述注解的使用范围 runtime>class>sources
@Documented//表示将注解生在在javadoc中
@Inherited //表示子类 可以继承父类中的该注解
@interface MyAnnotation{

}

4.自定义注解

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

>分析:
    @ interface用来声明一个注解,格式: public @ interface 注解名{定义内容}
    其中的每一个方法实际上是声明了一个配置参数.
    方法的名称就是参数的名称
    返回值类型就是参数的类型(返回值只能是基本类型.Class , String , enum ).
    可以通过default来声明参数的默认值
    如果只有一个参数成员,一般参数名为value
    注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值

练习

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @className: Test03
 * @description: 自定义注解
 * @author: penghailan
 * @create: 2021-05-26 11:49
 **/

public class Test03 {
    public static void main(String[] args) {

    }
    //注解可以显示赋值, 如果没有默认值,我们就必须给注解赋值
    @MyAnnotation2(name = "world",schools = {"北京大学","清华大学"})
    public void test(){

    }

    // 如果注解只有一个值,并且参数为value,可以不用写value,默认就是value
    @MyAnnotation3("hellowrold")
    public void test2(){

    }
}

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
    //注解的参数 参数类型 +参数名 ();

   // String name();
    String name() default "hello";
    int age() default 0;
    int id() default -1;// 如果默认值为-1,代表不存在
    String[] schools();

}
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
    String value();
}

反射

> Reflection (反射)Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
Class c = Class.forName(java.lang.String")
                        
>加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象) ,这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射
                        
正常方式: 引入需要的"包类"名称-->通过new实例化 -->取得实例化对象
反射方式: 实例化对象-->getClass()方法-->得到完整的“包类”名称

1.Java反射机制提供的功能

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

2.Java反射优点和缺点优点

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

3.反射相关的主要API

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

4.Class类

在这里插入图片描述

对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。
对于每个类而言, JRE都为其保留一个不变的Class类型的对象。Class对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。
Class本身也是一个类
Class对象只能由系统建立对象
一个加载的类在JVM中只会有一个Class实例
一个Class对象对应的是一个加载到JVM中的一个.class文件
每个类的实例都会记得自己是由哪个Class实例所生成
通过Class可以完整地得到一个类中的所有被加载的结构
Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

在这里插入图片描述

5.获取Class类的实例

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

Class clazz = Person.class

b)已知某个类的实例,调用该实例的getClass()方法获取Class对象

Class clazz = person.getClass();

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

Class clazz = Class.forName("demo01.Student');

d) 内置基本数据类型可以直接用类名.Typee

e)还可以利用ClassLoader

练习:

/**
 * @className: Test02
 * @description: Class类的创建方式有哪些
 * @author: penghailan
 * @create: 2021-05-26 15:16
 **/

public class Test02 {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person = new Student();
        System.out.println("这个人是:"+person.name);
        //方式一: 通过对象获得Class对象
        Class<? extends Person> c1 = person.getClass();
        System.out.println(c1);//class com.demo.reflection.Student
        //方式二:forname()获得
        Class c2 = Class.forName("com.demo.reflection.Student");
        System.out.println(c2);//class com.demo.reflection.Student
        //方式三:通过类名.class获得
        Class c3 = Student.class;
        System.out.println(c3);//class com.demo.reflection.Student
        System.out.println(c1.hashCode());//460141958
        System.out.println(c2.hashCode());//460141958
        System.out.println(c3.hashCode());//460141958

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

        //获得父类类型
        Class c5 = c1.getSuperclass();
        System.out.println(c5);//class com.demo.reflection.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="学生";
    }
}

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

6.哪些类型可以有Class对象?

>class:外部类,成员(成员内部类,态内部类),局部内部类,E名内部类.
> interface:接口
>[]:数组
>enum:枚举
>annotation: @interface
>primitive type:基本数据类型
> void

练习

import java.lang.annotation.ElementType;

/**
 * @className: Test03
 * @description: 获取所有类型的Class
 * @author: penghailan
 * @create: 2021-05-26 15:36
 **/

public class Test03 {
    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;//void
        Class c9 = 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[100];
        System.out.println(a.getClass().hashCode());//460141958
        System.out.println(b.getClass().hashCode());//460141958
    }
}

7.类的加载与ClassLoader的理解

>加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象.

>链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
  >验证:确保加载的类信息符合JVM规范,没有安全方而的问题
  >准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配.
  >解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。

>初始化:执行类构造器<clinit>()方法的过程,类构造器<clinit-()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。 (类构造器是构造类信息的,不是构造该类对象的构造器).

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

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

8.什么时候会发生类初始化?

1.类的主动引用(一定会发生类的初始化)
>当虚拟机启动,先初始化main方法所在的类
>new一个类的对象
>调用类的静态成员(除了final常量)和静态方法
>使用java.lang.reflect包的方法对类进行反射调用
>当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
2.类的被动引用(不会发生类的初始化)
>当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化
>通过数组定义类引用,不会触发此类的初始化
>引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)

练习

/**
 * @className: Test05
 * @description: 测试类什么时候会初始化
 * @author: penghailan
 * @create: 2021-05-26 16:52
 **/

public class Test05 {

    //1.当虚拟机启动,先初始化main方法所在的类
    static {
        System.out.println("main方法所在类被加载");
    }

    public static void main(String[] args) throws ClassNotFoundException {
        //主动引用
        //2..new 一个类的对象,如果其父类没有被初始化,则先会初始化它的父类
        //Son son = new Son();
        //3.调用类的静态成员变量和成员方法
        //System.out.println(Son.m);
        //4.反射也会产生主动引用
        //Class.forName("com.demo.reflection.Son");


        //不会产生类的引用的方法
        //1.通过子类引用父类的静态变量,不会导致子类初始化(子类不会被加载)
        // System.out.println(Son.b);
        //2.通过数组定义类引用,不会触发此类的初始化
        //Son[] sons = new Son[5];
        //引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
        System.out.println(Son.M);
    }
}

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

9.类加载器

在这里插入图片描述

练习

/**
 * @className: Test06
 * @description: 类的加载器
 * @author: penghailan
 * @create: 2021-05-26 17:38
 **/

public class Test06 {
    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@1b6d3586

        //获取拓展类加载器的父类加载器-->根加载器(引导类加载器)(c/c++),无法直接获取
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);//null

        //测试当前类是那个加载器加载的
        ClassLoader classLoader = Class.forName("com.demo.reflection.Test06").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:\develop\JDK\jdk1.8\jre\lib\charsets.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\deploy.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\ext\access-bridge-64.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\ext\cldrdata.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\ext\dnsns.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\ext\jaccess.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\ext\jfxrt.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\ext\localedata.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\ext\nashorn.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\ext\sunec.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\ext\sunjce_provider.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\ext\sunmscapi.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\ext\sunpkcs11.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\ext\zipfs.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\javaws.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\jce.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\jfr.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\jfxswt.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\jsse.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\management-agent.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\plugin.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\resources.jar;
        * D:\develop\JDK\jdk1.8\jre\lib\rt.jar;
        * D:\practice\demo\annotationAndReflection\target\classes;
        * D:\develop\idea\IntelliJIDEA2019\lib\idea_rt.jar
         *
        * */

    }
}

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

通过反射获取运行时类的完整结构
Field, Method, Constructor, Superclass, Interface, Annotation,
>实现的全部接口
>所继承的父类
>全部的构造器
>全部的方法
>全部的Field
>注解;

练习

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

/**
 * @className: Test07
 * @description: 获取类的信息
 * @author: penghailan
 * @create: 2021-05-27 15:06
 **/

public class Test07 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        /* Producer producer = new Producer();
        Class c2= producer.getClass();
        //获取类的名字
        System.out.println(c1.getName());//获得包名+类名   com.demo.reflection.Producer
        System.out.println(c1.getSimpleName());//获得类名  Producer*/

        Class c1 = Class.forName("com.demo.reflection.Producer");
        //获取类的名字
        System.out.println("获取类的名字");
        System.out.println(c1.getName());//获得包名+类名   com.demo.reflection.Producer
        System.out.println(c1.getSimpleName());//获得类名  Producer

        //获得类的属性 getFields()
        System.out.println("获得类的所有public权限的属性------> getFields()");
        Field[] fields = c1.getFields();//只能找到public权限的属性
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("获得类的所有属性 getDeclaredFields()====================");
        //获得类的属性  getDeclaredFields()
        Field[] declaredFields = c1.getDeclaredFields();//找到全部属性
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

        //获得类的指定属性
      /*  Field name = c1.getField("name");
        System.out.println(name);*/
        /*
        * Exception in thread "main" java.lang.NoSuchFieldException: name,原因是name属性是private权限,
        * 而 getField()方法只能获取到public权限的
        * */

        //获取指定属性的值
        System.out.println("获取指定属性");
        Field name = c1.getDeclaredField("name");
        System.out.println(name);

        System.out.println("获得本类及其父类的全部public权限的方法----------->getMethods()");
        //获得类的方法
        Method[] methods = c1.getMethods();//获得本类及其父类的全部public权限的方法
        for (Method method : methods) {
            System.out.println(method);
        }

        System.out.println("获取本类的所有方法----------->getDeclaredMethods()");
        Method[] declaredMethods = c1.getDeclaredMethods();//获取本类的所有方法
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }

        //获得指定的方法
        System.out.println("获得指定的方法------------>getMethod()");
        Method getName = c1.getMethod("getName", null);//getName无参数,所以参数的类型为null
        Method setName = c1.getMethod("setName", String.class);//setName是有参数的,所以参数的类型为对应的参数类型
        System.out.println(getName);
        System.out.println(setName);

        //获得类的构造器
        System.out.println("获得public权限的构造器------------>getConstructors()");
        Constructor[] constructors = c1.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        System.out.println("获得本类所有的构造器------------>getDeclaredConstructors()");
        Constructor[] declaredConstructors = c1.getDeclaredConstructors();
        for (Constructor declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }

        //获得类的指定构造器
        System.out.println("获得类的public权限的指定构造器------------>getConstructor");
        Constructor constructor = c1.getConstructor(null);
        System.out.println("获取类的无参构造器"+constructor);
        Constructor constructor1 = c1.getConstructor(int.class, String.class, String.class);
        System.out.println("获取类的有参构造器"+constructor1);
       /* Constructor constructor2 = c1.getConstructor(int.class, String.class);//java.lang.NoSuchMethodException: com.demo.reflection.Producer.<init>(int, java.lang.String)
        System.out.println(constructor2);*/

        System.out.println("获得本类的任意一个指定构造器------------>getConstructor");
        Constructor declaredConstructor = c1.getDeclaredConstructor(int.class, String.class);
        System.out.println(declaredConstructor);


    }
}

class Producer{

    private int age;
    private String name;
    private String address;
    public String sex;

    public Producer() {
    }

    public Producer(int age, String name, String address) {
        this.age = age;
        this.name = name;
        this.address = address;
    }

    private Producer(int age,String address){
        this.age = age;
        this.address = address;
    }

    private void test(){
        System.out.println("123");
    }

    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 getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

11.动态创建对象执行方法

有了Class对象,能做什么?
>创建类的对象:调用Class对象的newlnstance()方法
    > 1)类必须有一个无参数的构造器。
    > 2)类的构造器的访问权限需要足够,
    
思考?难道没有无参的构造器就不能创建对象了吗?只要在操作的时候明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作。

>步骤如下:
    1)通过Class类的getDeclaredConstructor(Class ... parameterTypes)取得本类的指定形参类型的构造器
    2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
   3)通过Constructor实例化对象
调用指定的方法
通过反射,调用类中的方法,通过Method类完成。
1.通过Class类的getMethod(String name, Class...parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
    
2.之后使用Object invoke(Object obj, Object] args)进行调用,并向方法中传递要设置的obj对象的参数信息。
   
Object invoke(Object obj, Object] args):
> Object对应原方法的返回值,若原方法无返回值,此时返回null
>若原方法若为静态方法,此时形参Object obj可为null
>若原方法形参列表为空,则Object[] args为null
>若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。
setAccessible(true):
> 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;

/**
 * @className: Test08
 * @description: 动态的创建对象 ,通过反射
 * @author: penghailan
 * @create: 2021-05-27 16:37
 **/

public class Test08 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        //获得Class对象
        Class c1 = Class.forName("com.demo.reflection.Customer");


        /*
        * 创建类的对象方式一:调用Class对象的newlnstance()方法
             > 1)类必须有一个无参数的构造器。
             > 2)类的构造器的访问权限需要足够,
        * */
        Customer customer = (Customer) c1.newInstance();//本质上是调用了类的无参构造器,如果没有无参构造,则会报错
        System.out.println(customer);

        //创建类的对象方式二:通过构造器创建对象
        /*
        *   1)通过Class类的getDeclaredConstructor(Class ... parameterTypes)取得本类的指定形参类型的构造器
            2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
            3)通过Constructor实例化对象
        * */
        Constructor constructor = c1.getDeclaredConstructor(int.class, String.class, String.class);
        Customer customer2 = (Customer) constructor.newInstance(10, "杨幂", "北京");
        System.out.println(customer2);

        //通过反射调用普通方法
        /*
        *   调用指定的方法
            通过反射,调用类中的方法,通过Method类完成。
                1.通过Class类的getDeclaredMethod(String name, Class...parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
                2.之后使用Object invoke(Object obj, Object] args)进行调用,并向方法中传递要设置的obj对象的参数信息。
        * */
        Customer customer3 = (Customer) c1.newInstance();//获取类的对象
        Method setName = c1.getDeclaredMethod("setName", String.class);
        //invoke :激活的意思
        //(对象,"方法参数的值")
        setName.invoke(customer3,"热巴");
        System.out.println(customer3.getName());

        //通过反射操作属性
        Customer customer4 = (Customer) c1.newInstance();
        Field name = c1.getDeclaredField("name");
        //不能直接操作私有属性,women需要关闭程序的安全检测,属性/方法的setAccessible(true)
        name.setAccessible(true);//取消安全检测
        name.set(customer4,"杨紫");
        System.out.println(customer4.getName());

    }
}

class Customer{

    private int age;
    private String name;
    private String address;

    public Customer() {
    }

    public Customer(int age, String name, String address) {
        this.age = age;
        this.name = name;
        this.address = address;
    }

    private Customer(int age,String address){
        this.age = age;
        this.address = address;
    }

    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 getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

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

12.性能对比分析

练习

package com.demo.reflection;

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

/**
 * @className: Test09
 * @description: 测试性能对比
 * @author: penghailan
 * @create: 2021-05-27 17:20
 **/

public class Test09 {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        test1();
        test2();
        test3();
    }

    //普通方式调用
    public static void test1(){
        User user = new User();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            user.getName();
        }
        long end = System.currentTimeMillis();
        System.out.println("普通方式调用时间:"+(end-start)+"ms");
    }

    //反射方式调用
    public static void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user = new User();
        Class c1 = user.getClass();
        long start = System.currentTimeMillis();
        Method getName = c1.getDeclaredMethod("getName", null);
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user,null);
        }
        long end = System.currentTimeMillis();
        System.out.println("反射方式调用时间:"+(end-start)+"ms");
    }

    //反射方式调用 关闭检测
    public static void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user = new User();
        Class c1 = user.getClass();
        long start = System.currentTimeMillis();
        Method getName = c1.getDeclaredMethod("getName", null);
        getName.setAccessible(true);
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user,null);
        }
        long end = System.currentTimeMillis();
        System.out.println("反射方式调用,关闭检测,时间:"+(end-start)+"ms");
    }

}

/*
* 运行结果
    普通方式调用时间:3ms
    反射方式调用时间:1857ms
    反射方式调用,关闭检测,时间:1010ms
*
* */

13.获取泛型信息

反射操作泛型
Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除

为了通过反射操作这些类型,Java新增了ParameterizedType , GenericArrayType ,TypeVariable和 WildcardType 几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型.

ParameterizedType:表示一种参数化类型,比如Collection<String>

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

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

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

练习

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

/**
 * @className: Test10
 * @description: 获取泛型信息
 * @author: penghailan
 * @create: 2021-05-27 17:53
 **/

public class Test10 {
    public static void main(String[] args) throws NoSuchMethodException {
        Class c1 = Test10.class;
        Method test01 = c1.getMethod("test01", Map.class, List.class);
        Type[] genericParameterTypes = test01.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);//打印真实参数信息
                }

            }
        }

        System.out.println("11111111111111111111111111111111111");
        Method test02 = c1.getMethod("test02", null);
        Type genericReturnType = test02.getGenericReturnType();//获取返回值泛型类型
        System.out.println("-----"+genericReturnType);
        if (genericReturnType instanceof ParameterizedType){//判断是否是参数化类型
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();//获取参数的真实参数类型
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println("==============="+actualTypeArgument);//打印
            }
        }
    }

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

}
/*
输出结果:
    ------java.util.Map<java.lang.String, com.demo.reflection.User>
    ======class java.lang.String
    ======class com.demo.reflection.User
    ------java.util.List<com.demo.reflection.User>
    ======class com.demo.reflection.User
    11111111111111111111111111111111111
    -----java.util.Map<java.lang.String, com.demo.reflection.User>
    ===============class java.lang.String
    ===============class com.demo.reflection.User

* */

14.反射操作注解

getAnnonations();
getAnnotation();

练习:

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

/**
 * @className: Test11
 * @description: 获取注解的信息
 * @author: penghailan
 * @create: 2021-05-28 09:56
 **/


public class Test11 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("com.demo.reflection.Student2");
        Annotation[] annotations = c1.getAnnotations();//获取Class类对象的所有注解
        for (Annotation annotation : annotations) {
            System.out.println(annotation);//遍历注解打印
        }
        //获得注解的value的值
        Table table = (Table) c1.getAnnotation(Table.class);//获取Class类对象的@Table注解对象
        System.out.println(table.value());//获取table注解对象的value()的值

        //获得类指定的注解
//        Field name = c1.getDeclaredField("name");
//        Field name = c1.getDeclaredField("id");
        Field age = c1.getDeclaredField("age");//获取Class类的age对象
        Filed filed = age.getAnnotation(Filed.class);//获取age对象的Filed注解
        System.out.println(filed.colummName());//获取注解的colummName()的值
        System.out.println(filed.type());//获取注解的获取注解的type()的值
        System.out.println(filed.lenth());//获取注解的获取注解的lenth()的值

    }

}

@Table("db_student")
class Student2{
    @Filed(colummName = "db_id",type="int",lenth = 10)
    private int id;
    @Filed(colummName = "db_age",type="int",lenth = 3)
    private int age;
    @Filed(colummName = "db_name",type="varchar",lenth = 10)
    private String name;

    public Student2() {
    }

    public Student2(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 "Student2{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

//类名的注解
@Target(ElementType.TYPE) //作用在类上
@Retention(RetentionPolicy.RUNTIME)//运行时有效
@interface Table{
    String value();
}

//字段的注解
@Target(ElementType.FIELD)//作用在字段上,
@Retention(RetentionPolicy.RUNTIME)//运行时有效
@interface Filed{
    String colummName();
    String type();
    int lenth();
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值