注解与反射

注解与反射

一、注解

1.1 概念

Annotation(注解):是JDK5开始引入的新特性,可以看作是一种特殊的注释,主要用于修饰类、方法或者变量,在框架中大量使用。

注解是一种能被添加到java代码中的元数据,包、类、字段、方法、局部变量、方法参数都可以用注解来修饰,注解对于它所修饰的代码并没有直接的影响。

注解可以通过反射获取标记的内容,编译器生成类字节码文件时,标记也可以嵌入到字节码中

1.2 应用

功能1:生成文档:生成文档是最常见的,也是java最早提供的注解

生成文档测试

package com.coder;
/**
* @author teacher_shi
* @version 1.0
* @since 1.8
* 这是一个学生类
*/
public class Student {
    /**
    * 计算成绩的方法
    * @param x 成绩数组
    * @return 总成绩
    */
    public int calcScore(int[] x){
    	int sum=0;
        for (int x1 : x) {
        	sum+=x1;
        }
    	return sum;
    }
}

请添加图片描述

请添加图片描述

功能2:在编译时进行格式检查,@Override:放在方法前面,如果这个方法并不是覆盖类方法,则编译时就会检查报错。

功能3:跟踪代码依赖性,实现替代配置文件功能。比较常见spring、mybatis开源框架,使用注解作用就是减少配置;在反射的Class,Method,Field这些方法中,有许多使用Annotation的相关处理。

1.3 注解分类

1.3.1 内置注解

@Override: 表示当前的方法定义将会覆盖父类中的方法,如果拼写错误或者方法签名不匹配,编译器就会提示出错。Retention级别RetentionPolicy.SOURCE

@Deprecated:作用是对不应该再使用的类、类成员、方法添加注解,标明已经废弃、过时了,不应该再使用。当编程人员在使用这些方法时,将会在编译器上显示过时信息提示。它和javadoc中的@deprecated标记具有相同的功能。

@SuppressWarnings:关闭对类、方法、成员编译时产生的特定警告

参考代码:

//压制静态访问警告
@SuppressWarnings("static-access")
    public static void main(String[] args) {
    Student student=new Student();
    student.calcScore(new int[]{1,2,3});
    student.calcScores(1,2,3);
    student.test();
}
//压制多类型警告,使用数组
@SuppressWarnings({"deprecation","static-access"})
    public static void main(String[] args) {
    Student student=new Student();
    student.calcScore(new int[]{1,2,3});
    student.calcScores(1,2,3);
    student.test();
}
//压制所有警告
@SuppressWarnings("all")
public static void main(String[] args) {
    Student student=new Student();
    student.calcScore(new int[]{1,2,3});
    student.calcScores(1,2,3);
    student.test();
}

//@Override-检查方法是否被重写
public class Test extends AnnotationDemo {
      @Override
      public void test() {
          super.test();
      }
  }


//@Deprecated-标记过时
@Deprecated
public String toLocaleString() {
        DateFormat formatter = DateFormat.getDateTimeInstance();
}
  

//@SuppressWarnings-忽略注解中声明的警告
 

请添加图片描述

@FunctionalInterface:用来定义一个函数式接口,如果在接口中有超过一个以上的抽象方法,则报错

1.3.2 元注解

@Target:用于定义注解修饰的目标,指定了目标后,自定义的注解就可以声明在目标上。标记注解的使用目标

public enum ElementType {
    /** 标明该注解可以用于类、接口(包括注解类型)或enum声明 */
    TYPE,
    /** 标明该注解可以用于字段(属性)声明,包括enum实例 */
    FIELD,
    /** 标明该注解可以用于方法声明 */
    METHOD,
    /** 标明该注解可以用于参数声明 */
    PARAMETER,
    /** 标明注解可以用于构造方法声明 */
    CONSTRUCTOR,
    /** 标明注解可以用于局部变量声明 */
    LOCAL_VARIABLE,
    /** 标明注解可以用于另一个注解上声明 */
    ANNOTATION_TYPE,
    /** 标明注解可以用于包声明 */
    PACKAGE,
    /**
    * 标明注解可以用于类型参数声明
    *
    * @since 1.8
    */
    TYPE_PARAMETER,
    /**
    * 类型使用声明
    *
    * @since 1.8
    */
    TYPE_USE,
    /**
    * 模块声明.
    *
    * @since 9
    */
    MODULE
}

由上述源码可见,注解可以接收一个数组,因此可以接收多个常量值,已有的常量都是定义在ElementType中。

@Target({ElementType.TYPE,ElementType.METHOD,ElementType.PARAMETER,ElementType.LOCAL_VARIABLE,ElementType.TYPE_PARAMETER,
         ElementType.TYPE_USE})
public @interface Anno3 {
}
@Anno3//ElementType.TYPE
public class Player {
    @Anno3 //ElementType.METHOD
    public void test(@Anno3 int x){//ElementType.PARAMETER
    	@Anno3 int y;//ElementType.LOCAL_VARIABLE
    }
}
//ElementType.TYPE_PARAMETER
class XX<@Anno3 T>{
}
//ElementType.TYPE_USE
class YY implements @Anno3 Serializable{
    public void test(){
    	String s=new @Anno3 String();
    }
}

@Retention:用于定义注解的生命周期,值只有三种类型 注解保存的时间

public enum RetentionPolicy {
    /**
    * 注解的信息只会记录在源文件中,编译时会被编译器丢弃,不会保存在编译好的类信息中
    */
    SOURCE,
    /**
    * 编译器将注解记录在类文件中,但不会加载到虚拟机中,如果一个注解声明没有指定范围,则
    系统默认值就是CLASS
    */
    CLASS,
    /**
    * 注解信息会保留在源文件、类文件中,在执行时也加载到虚拟机中,可以通过反射机制进行读
    取
    */
    RUNTIME
}

通过反射机制获取Annotation

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Anno4 {
	String value();
}
public class Team {
    @Anno4("hello")
    public void test(){
    }
}
public class TestGetMethodAnnotation {
    public static void main(String[] args) throws NoSuchMethodException {
        Class<Team> teamClass = Team.class;
        Method method=teamClass.getDeclaredMethod("test");
        System.out.println("methodName:"+method.getName());
        /*Annotation[] annotations = method.getAnnotations();
        for (Annotation annotation : annotations) {
        System.out.println(annotation);
        }*/
        Anno4 anno4 = method.getDeclaredAnnotation(Anno4.class);
        String value = anno4.value();
        System.out.println(value);
    }
}

@Documented:用来做标识,使用了该注解,在生成javaDoc文档的时候,就会把@Documented注解标识的显示出来。 标记这个注解是否包含在用户文档中

@Inherited:如果某个注解是被标注了Inherited,表明可以被继承。如果一个使用@Inherited修饰的annotation类型被用于一个类,则这个annotation将被用于该类的子类。 标记注解是继承哪个注解类(默认是没有继承的)

当父类中的注解被@Inherited标注,会有如下情况

  • 如果父类的注解是定义在类上面的,子类是可以继承过来的。
  • 如果父类的注解是定义在方法上面的,子类直接继承了父类的方法,则注解是可以继承过来的
  • 如果父类的注解是定义在方法上面的,子类重写了父类定义了注解的方法,则子类将无法继承父类方法的注解,将方法连带上面的注解一并覆盖掉。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
@Inherited
public @interface AnnoDoc {
}
@AnnoDoc
public class DataBase {
@AnnoDoc
public void test(){
}
public static void main(String[] args) throws NoSuchMethodException {
    /*Class<DataBase> dataBaseClass = DataBase.class;
    Annotation[] annotations = dataBaseClass.getAnnotations();
    for (Annotation annotation : annotations) {
    System.out.println(annotation);
    }*/
    Class<Sub> subClass = Sub.class;
    //Annotation[] annotations = subClass.getAnnotations();
    // annotations = subClass.getDeclaredAnnotations();
    /*for (Annotation annotation : annotations) {
    System.out.println(annotation);
    }*/
    Method test = subClass.getMethod("test");
    System.out.println("test = " + test);
    Annotation[] annotations = test.getAnnotations();
        for (Annotation annotation : annotations) {
        	System.out.println(annotation);
        }
    }
}
class Sub extends DataBase{
    public void test(){
    }
}

1.3.3 自定义注解

格式

元注解

public @interface 注解名{ }

使用方式

@注解名称

本质:

注解本质就是一个接口,类似于新创建一个接口文件,但是为了和接口做区分,声明为@interface

public interface com.coder.Anno1 extends java.lang.annotation.Annotation {
}

在定义注解时,不能继承其他的注解或接口。

@interface用来声明一个注解,其中的每一个方法实际上声明了一个配置参数。方法的名称,就是参数的名称,方法的返回值类型,就是参数的类型 返回值类型只能是以下数据类型

  • String
  • 基本数据类型: byte short int long float double char boolean
  • Class类型
  • enum类型
  • Annotation类型
  • 以上所有类型的数组

不允许有void方法存在

定义了方法的类型,在使用时要给赋值,也可以在定义时使用default给赋默认值。方法不能有参数

如果只有一个参数成员,一般参数名为value,这时value可以不显式地写出来

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "MyAnnotation";
}

@MyAnnotation("eat1")
public void eat(){
//s
}

二、反射

  1. 问题:Object obj = new String(“abc”);
    • 需求:如何调用 String 中的length 方法
      • 使用强制类型转换
    • 如果不知道真实的类型怎么办?
  2. 问题:一切皆对象,**类这种事物是啥对象?**使用什么类来表示这种对象?
    • Class :表示所有的类
    • Constructor:表示所有的构造器
    • Method :表示所有的方法
    • Field:表示所有的字段
    • 通过反射:得到类的元数据信息(构造器,方法,字段,内部类)
  3. 类加载进内存,变成 Class 对象,也叫字节码对象

2.1 反射机制简介

被视为动态语言的关键,允许程序在执行期间,借助于ReflectionAPI取得任何类的内部信息。在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个类对象所属的类,可以了解任意一个类的 成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为java反射机制。

java反射机制提供的功能 :

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时查看任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法
  • 在运行时给任意一个对象的属性赋值
  • 生成动态代理

2.2 动态语言和静态语言

动态语言:在运行时可以改变其结构的语言,比如新的函数、对象、甚至代码可以被引进,已有的函数 可以被删除或是其他结构上的变化。也就是说在运行时代码可以根据某些条件改变自身结构。主要的动 态语言:C#、javaScript、PHP、Python等

静态语言:运行时结构不可变的语言,就是静态语言。包括Java、C、C++等。Java有一定的动态性,可 以利用反射机制、字节码操作获得类似动态语言的特性。

2.3 Class类

java在将.class字节码文件载入时,JVM会产生一个java.lang.Class对象代表该.class字节码文件。Class 是一个比较特殊的类,是java反射机制的基础。Class类的对象表示正在运行的java程序中的类或接口。 也就是任何一个类被加载时,即将类的.class文件读入内存的同时,都自动为其创建一个java.lang.Class 对象。Class类没有公共构造方法,其对象是JVM在加载类时通过调用类加载器中的defineClass()方法创建的,因此不能显式地创建一个Class对象。通过Class对象,才可以获取这个类对象的其他信息。

每个类被加载之后,系统都会为该类生成一个对应的Class对象,一旦类被加载到JVM中,同一个类将不 会被再次载入。

2.4 如何获得Class对象

方式一:使用Class类的静态方法 forName(String className),参数className表示所需类的全路径, 如果给的参数类找不到,会抛出ClassNotFoundException异常。

Class<?> clazz=Class.forName("com.coder.Student");

方式二:用类名调用class属性来获取该类对应的Class对象,“类名.class"

Class<?> clazz2=Student.class

方式三:使用该类的对象调用getClass()方法,来获取该类对应的Class对象。

Student student=new Student();
Class<?> clazz3=student.getClass();

方式四:使用类的装载器

ClassLoader loader=Student.class.getClassLoader();
Class<?> clazz4=loader.loadClass("com.coder.Student");

2.5 那些类型可以有Class对象

  • class :外部类、成员内部类、静态内部类、局部内部类、匿名内部类
  • interface:接口
  • 数组
  • enum:枚举
  • annotation:注解
  • 基本数据类型
  • void
Class 类和 Class 实例
  1. Class 表示所有的类

  2. Class 实例表示:一份份的字节码(类,接口,注解)

  3. 各种类如何表示?

    • String Class<java.lang.String>
    • HashMap Class<java.util.HashMap>
  4. 如何获取Class 的实例

    //1:使用类名.class
    Class<String> stringClass = String.class;
    //2:通过对象调用 getClass();
    String str = "abc";
    Class<? extends String> aClass = str.getClass();
    //3:使用类的全限定类名 java.lang.String
    Class<?> aClass1 = Class.forName("java.lang.String");
    Class<?> stuClass = Class.forName("cn.sycoder.ClassObjectDemo.Student");
    System.out.println(stuClass);
    
    • 注意:同一个JVM中,只存在一个类的一份字节码和一份字节码对象
    • 使用最多的 Class.forName(类的全限定类名);
  5. 基本数据类型的字节码如何表示:

    • jvm 已经提前内置好基本数据类型字节码实例

      Class<Integer> aClass2 = int.class;
      
    • 包装类型提供了一个 TYPE 常量,也是可以去获取基本类型的类实例

      Class<Integer> type = Integer.TYPE;
      
    • 如何证明 Integer 和 int 不是同一种数据类型

      Integer.class == int.class
      
类加载
  1. 直接使用类.class

    Class<String> stringClass = String.class;
    
  2. 使用对象调用getClass()

    String str = "abc";
    Class<? extends String> aClass = str.getClass();
    
  3. 使用类全限定类名

    Class<?> aClass1 = Class.forName("java.lang.String");
    Class<?> stuClass = Class.forName("cn.sycoder.ClassObjectDemo.Student");
    
反射操作构造器
  1. Class 去操作 Constructor 类

  2. 常用的方法

    //获取构造器对象
    public Constructor<T> getConstructor(Class<?>... parameterTypes)
    //获取全部构造器对象
    public Constructor<?>[] getConstructors()
    //获取全部构造器包括私有
    public Constructor<?>[] getDeclaredConstructors()
    
  3. 常用获取构造器方法

    //获取单个
    Class<?> aClass = Class.forName("cn.sycoder.ConstructorDemo.User");
    Constructor<?> constructor = aClass.getConstructor();
    //获取非私有全部
    Constructor<?>[] constructors = aClass.getConstructors();
    //获取私有的单个
    Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
    //获取带私有的全部
    Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
    //带参数的
    Constructor<?> constructor1 = aClass.getConstructor(String.class);
    
反射创建对象
  1. 问题:为啥使用反射创建对象,不直接使用 new 创建对象

    • 框架中,多半使用反射创建对象,使用类全限定类名操作
  2. 如何使用反射创建对象

    • 获取类字节码对象

    • 获取构造器对象

    • 通过反射获取的构造器创建对象

      Class<?> aClass = Class.forName("cn.sycoder.ConstructorNewObject.Person");
      Constructor<?> constructor = aClass.getConstructor();
      //通过构造器创建对象
      Object o = constructor.newInstance();
      
    • 如果构造器是私有的,还需要去设置可以访问

      constructor.setAccessible(true);
      
  3. 常见错误:

    • cn.sycoder.ConstructorNewObject.Person with modifiers “private”

      constructor.setAccessible(true);
      
    • Exception in thread “main” java.lang.IllegalArgumentException: wrong number of arguments

       Constructor<?> constructor1 = aClass.getConstructor(String.class, int.class);
      Object o1 = constructor1.newInstance("上云",18);
      
反射操作方法
  1. 实例方法

    • 获取字节码对象

    • 使用构造器获取对象

    • 获取方法对象

    • 反射调用方法

      • 调用方法

        • Object invoke(Object obj,Object…args)
        Class<?> aClass = Class.forName("cn.sycoder.MethedDemo.MethodDemo");
        Constructor<?> constructor = aClass.getConstructor();
        Object o = constructor.newInstance();
        
        
        Method eat = aClass.getMethod("eat");
        Object invoke = eat.invoke(o);
        
        Method eat1 = aClass.getMethod("eat", String.class);
        Object obj = eat1.invoke(o, "鱼");
        
  2. 静态方法

    • 静态方法不属于对象,属于类本身

    • 如何执行:

      • 获取类字节码对象

      • 获取方法对象

      • 执行(invoke)不需要传对象

        Method getName = aClass.getMethod("getName", String.class);
        Object object = getName.invoke(null, "上云");
        System.out.println(object);
        
反射操作字段
  1. 获取类字节码对象

  2. 获取对象

  3. 操作字段

    Class<?> aClass = Class.forName("cn.sycoder.FiledDemo.FiledDemo");
    Constructor<?> constructor = aClass.getConstructor();
    Object o = constructor.newInstance();
    Field age = aClass.getDeclaredField("age");
    age.setAccessible(true);
    
    age.set(o,19);
    
    Object o1 = age.get(o);
    System.out.println(o1);
    
类加载器加载 Properties
  1. 使用绝对路径

    //使用绝对路径
    Properties properties = new Properties();
    FileInputStream in = new FileInputStream(path);
    properties.load(in);
    System.out.println(properties);
    
  2. 使用相对路径从根目录开始找

    Properties properties = new Properties();
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    InputStream resourceAsStream = classLoader.getResourceAsStream(path);
    properties.load(resourceAsStream);
    System.out.println(properties);
    
  3. 使用相对路径(相对于我们使用的类的文件夹开始)

    Properties properties = new Properties();
    InputStream inputStream = Test.class.getResourceAsStream(path);
    properties.load(inputStream);
    System.out.println(properties);
    

2.6 ClassLoader类加载器

2.6.1 简介

一个用来加载类文件的类 java源代码通过javac编译器编译成类文件,然后Jvm通过类文件中的字节码来执行程序,类加载器负责加载文件系统、网络或其他来源的类文件

请添加图片描述

jvm 和类的关系

  • 运行带有main方法的类(主方法),启动 jvm 进程,并加载字节码,同一个jvm 所有线程已经变量都处于同一个进程中
  • jvm 何时退出?
    • 程序正常运行结束
    • 出现异常,没有捕获异常时
    • System.exit() 方法
    • 强制杀进程的时候
  • 重点:jvm 进程一旦结束,进程中的内存中的数据会丢失
2.6.2 机制

Java类装载器的作用就是在运行时加载类。基于三个机制:

  • 委托机制:将加载一个类的请求交给父类加载器,如果这个父类加载器找不到要加载的类,那么子 类再加载它。比如加载一个String类

  • 可见性机制:子类的加载器可以看见所有的父类加载器加载的类,但是父类加载器看不到子类加载 器加载的类

  • 单一性机制:加载一个类,仅加载一次,可以确保在委托机制中,如果父类加载器已经加载过这个 类了,子类加载器不会再次加载

2.6.3 加载的两种方式

隐式加载:程序在运行过程中,通过new等方式生成对象时,隐式调用类加载器加载对应的类进入到 JVM中

显式加载:通过Class.forName()等方法,显式地加载需要的类

2.6.4 类加载初始化具体步骤(加载,连接,初始化)

  • 类的加载
    • 类加载器(ClassLoader),将字节码文件(class 文件)加载进内存中,并且创建一个字节码对象(java.lang.Class),类加载器由JVM提供,或者自定义
  • 类的连接(类加载进内存之后,生成 Class 对象,把类的二进制数据合并到 JRE 中)
    • 验证:检测被加载的类是否有正确的内部结构
    • 准备:负责为类的 static 变量分配内存,设置默认值(并不是初始化操作)
    • 解析:把类的二进制数据中的符号引用替换为直接引用(深入分析 jvm)
  • 类的初始化(jvm 负责对类进行初始化,主要是对 static 变量进行初始化)
    • 如果该类未被加载和连接,则程序先加载并连接该类(类还可以动态加载)groovy
    • 如果该类的父类未被初始化,则优先初始化父类
    • 如果该类有初始化语句(静态代码块),系统依次执行这些初始化语句
2.6.5 类加载器的分类

JDK默认提供三种ClassLoader

BootStrapClassLoader:根装载器,它使用C++编写,所以在Java中看不到它,负责装载核心类库

ExtClassLoader:(PlatformClassLoader(JDK9版本))扩展类装载器,装载扩展目录ext中的jar类

AppClassLoader:系统类装载器(应用类装载器),负责classpath类的加载

public class TestClassLoader {
    public static void main(String[] args) {
        //jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc
        ClassLoader classLoader = TestClassLoader.class.getClassLoader();
        System.out.println(classLoader);
        //jdk.internal.loader.ClassLoaders$PlatformClassLoader@5594a1b5
        ClassLoader parent = classLoader.getParent();
        System.out.println(parent);
        //null
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);
    }

JVM装载类时使用“全盘负责委托机制",当一个ClassLoader一个类的时候,除非显式地使用另一个ClassLoader,不然该类所依赖及引用的类也是由这个ClassLoader载入。

一个应用程序总是由很多个类组成,java程序启动时,并不是一次把所有的类加载再运行。总是先把保 证程序运行的基础类一次性加载到JVM中,其他类等到JVM用到的时候再加载,这样可以节省内存的开销。

2.7 Constructor类

public class TestConstructor {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("com.coder.Student");
        //调用无参构造方法
        Constructor<?> constructor = aClass.getConstructor();
        // Object o = constructor.newInstance();
        //调用有参数的构造方法
        /*Constructor<?> constructor = aClass.getConstructor(String.class,
        int.class);
        Object o = constructor.newInstance("李白", 20);
        Student student= (Student) o;
        System.out.println(student);*/
        //访问私有构造方法
        /*Constructor<?> constructor = aClass.getDeclaredConstructor();
        //setAccessible(true) 取消java语言对访问的检查
        //Student student=new Student();
        constructor.setAccessible(true);
        Object o = constructor.newInstance();
        Student student= (Student) o;
        student.setName("杜甫");
        student.setAge(22);
        System.out.println(student);*/
        //获取构造方法数组
        /* Constructor<?>[] declaredConstructors =
        aClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
        System.out.println(declaredConstructor.getName());
        System.out.println(declaredConstructor.getParameterCount());
        Class<?>[] parameterTypes =
        declaredConstructor.getParameterTypes();
        for (Class<?> parameterType : parameterTypes) {
        System.out.println("\t"+parameterType.getName());
        }
        }*/
        //以下调用方式,从jdk9版本后被弃用
        aClass.newInstance();
    }
}

2.8 Field类

Class<?> aClass = Class.forName("com.coder.Student");
//获取public修饰的属性
//如果父类有公有的属性,可以读取
Field[] fields = aClass.getFields();
//获取所有private default protected public修饰的属性
//不读取父类的属性
fields=aClass.getDeclaredFields();

获取访问修饰符

for (Field field : fields) {
    System.out.print(field.getModifiers());//访问修饰符
    System.out.println(" "+field.getName());
}

请添加图片描述

public class TestField {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("com.coder.Student");
        //获取public修饰的属性
        //如果父类有公有的属性,可以读取
        Field[] fields = aClass.getFields();
        //获取所有private default protected public修饰的属性
        //不读取父类的属性
        /*fields=aClass.getDeclaredFields();
        for (Field field : fields) {
        System.out.print(getModifiers(field.getModifiers()));
        Class<?> type = field.getType();
        System.out.print(" "+type.getSimpleName());
        System.out.println(" "+field.getName());
        }*/
        //只获取一个属性
        Field name = aClass.getDeclaredField("name");
        System.out.println(name.getName()+"\t"+name.getType().getSimpleName());
        //通过反射机制为对象的属性赋值
        Object obj = aClass.getDeclaredConstructor().newInstance();
        //取消java语言访问检查
        name.setAccessible(true);
        //为obj对象的属性赋值
        name.set(obj,"李白");
        //获取属性值
        //Object value = name.get(obj);
        //System.out.println(value);
        Student student = (Student) obj;
        System.out.println(student);
}
    public static String getModifiers(int modifiers){
        switch (modifiers){
            case 1:
                return "public";
            case 2:
                return "private";
            case 4:
                return "protected";
            case 10:
                return "private static";
            default:
                return "";
        }
	}
}    

2.9 Method类

public class TestMethod {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("com.coder.Student");
        //获取本类的所有公共方法和父类的公共方法
        Method[] methods = aClass.getMethods();
        //获取本类的所有方法
        /* methods=aClass.getDeclaredMethods();
        for (Method method : methods) {
        System.out.print(method.getModifiers()+" ");
        System.out.print(method.getName()+" ");
        //获取方法的返回值类型
        Class<?> returnType = method.getReturnType();
        System.out.println(returnType.getSimpleName());
        //方法的参数
        Class<?>[] parameterTypes = method.getParameterTypes();
        for (Class<?> parameterType : parameterTypes) {
        System.out.println("\t"+parameterType.getSimpleName());
        }
        //获取方法异常列表
        Class<?>[] exceptionTypes = method.getExceptionTypes();
        for (Class<?> exceptionType : exceptionTypes) {
        System.out.println("\t\t"+exceptionType.getSimpleName());
        }
        //获取注解
        //获取@Retention(RetentionPolicy.RUNTIME)的注解
        Annotation[] annotations = method.getAnnotations();
        for (Annotation annotation : annotations) {
        System.out.println("\t\t\t"+annotation);
        }
        }*/
        Object obj = aClass.getConstructor().newInstance();
        //获取一个方法,并调用
        Method addMethod = aClass.getDeclaredMethod("add", int.class,
        int.class);
        Object result = addMethod.invoke(obj, 10, 20);
        System.out.println(result);
        Method keep2 = aClass.getDeclaredMethod("keep2", double.class);
        keep2.invoke(obj, 3.1455);
        //System.out.println(invoke);
    }
}

2.10 反射获得泛型

可以通过反射获取泛型的场景

  • 成员变量的泛型
  • 方法参数的泛型
  • 方法返回值的泛型
  • 获取带有泛型的超类或带有泛型的实现的接口

不可以通过反射获取泛型的场景

  • 局部变量的泛型

参考代码

成员变量泛型

public class GenericDemo {
    public static void main(String[] args) throws Exception {
        getField();
     }
    public static void getField() throws Exception {
        Class<MyDemo> myDemoClass = MyDemo.class;
        Field listField=myDemoClass.getDeclaredField("list");
        //Class<?> type = listField.getType();
        //System.out.println(type.getName());
        //获取带有泛型的类型
        Type genericType = listField.getGenericType();
        System.out.println(genericType.getTypeName());
        //判断获取到的Type是不是参数化类型(泛型)
        if (genericType instanceof ParameterizedType){
            ParameterizedType parameterizedType= (ParameterizedType)genericType;
            //获取泛型真实参数类型
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                //System.out.println(actualTypeArgument.getTypeName());
                Class clazz= (Class) actualTypeArgument;
                System.out.println(clazz.getSimpleName());
            }
        }
    }
}
class MyDemo{
        //成员变量带有泛型
        private List<String> list=new ArrayList<>();
        private int x;
        //方法参数带有泛型
        public void filter(List<String> list){
        }
        //方法返回值带有泛型
        public Map<String,Double> getScore(){
        return null;
    }
}

方法参数泛型

public static void getMethodParameter() throws Exception{
    Class<MyDemo> myDemoClass = MyDemo.class;
    Method
    method=myDemoClass.getDeclaredMethod("filter",List.class,Map.class);
    Type[] genericParameterTypes = method.getGenericParameterTypes();
    for (Type genericParameterType : genericParameterTypes) {
        if (genericParameterType instanceof ParameterizedType){
        ParameterizedType parameterizedType= (ParameterizedType)
        genericParameterType;
        Type[] actualTypeArguments =
        parameterizedType.getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument.getTypeName());
            }
        }
    }
}
class MyDemo{
    //成员变量带有泛型
    private List<String> list=new ArrayList<>();
    private int x;
    //方法参数带有泛型
    public void filter(List<Double> list, Map<String,Student> map){
    }
    //方法返回值带有泛型
    public Map<String,Double> getScore(){
    return null;
    }
}

方法返回值的泛型

public static void getMethodReturn() throws Exception {
    Class<MyDemo> myDemoClass = MyDemo.class;
    Method getScore = myDemoClass.getDeclaredMethod("getScore");
    // getScore.getReturnType();
    //获取带有泛型的返回值类型
    Type genericReturnType = getScore.getGenericReturnType();
    if (genericReturnType instanceof ParameterizedType){
         ParameterizedType parameterizedType= (ParameterizedType)genericReturnType;
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        for (Type actualTypeArgument : actualTypeArguments) {
            System.out.println(actualTypeArgument.getTypeName());
        }
    }
}
   

2.11 获取接口和超类

public class TestInterface {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("com.coder.UserServiceImpl");
        //获取当前类实现的带有泛型的接口的泛型类型
        /* Type[] genericInterfaces = aClass.getGenericInterfaces();
        for (Type genericInterface : genericInterfaces) {
        if (genericInterface instanceof ParameterizedType){
        ParameterizedType parameterizedType= (ParameterizedType)
        genericInterface;
        Type[] actualTypeArguments =
        parameterizedType.getActualTypeArguments();
        for (Type actualTypeArgument : actualTypeArguments) {
        System.out.println(actualTypeArgument.getTypeName());
        }
        }
        }*/
        //获取当前类的所有接口
        /*Class<?>[] interfaces = aClass.getInterfaces();
        for (Class<?> anInterface : interfaces) {
        System.out.println(anInterface.getName());
        }*/
        //获取父类
        Class<?> superclass = aClass.getSuperclass();
        System.out.println(superclass.getName());
        //获取带有泛型的父类
        Type genericSuperclass = aClass.getGenericSuperclass();
        if (genericSuperclass instanceof ParameterizedType){
            ParameterizedType parameterizedType= (ParameterizedType)
            genericSuperclass;
            Type[] actualTypeArguments =
            parameterizedType.getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument.getTypeName());
            }
        }
    }
}

三、基于JDK动态代理

3.1 简介

动态代理是通过创建代理对象,可以实现在不修改原有代码的情况下,为程序增加新的功能,实现程序 功能的增加。

动态代理的实现分成两种

  • JDK动态代理
  • CGLIB动态代理

3.2 JDK动态代理API

InvocationHandler接口

Method类

Proxy类

JDK的动态代理目标类必须要有接口。

实现动态代理的步骤

  1. 创建接口,定义目标类要完成的功能

    public interface UserService {
        void saveUser();
        void deleteUser();
    }
    
  2. 创建目标类实现接口,编写对应方法

    public class UserServiceImpl implements UserService{
        @Override
        public void saveUser() {
        	System.out.println("保存用户");
        }
        @Override
        public void deleteUser() {
        	System.out.println("删除用户");
        }
    }
    
    
  3. 创建InvocationHandler接口的实现类,在invoke方法中完成代理的功能

    • 调用目标类的方法

    • 加入增强功能

      //表示程序的代理要做什么,怎么做
      public class MyHandler implements InvocationHandler {
          //被代理的目标对象
          private Object object;
          public MyHandler(Object object) {
          	this.object = object;
          }
          /**
          *
          * @param proxy jdk创建的代理对象,无需赋值
          * @param method 目标类中的方法,jdk提供拦截的目标类正在调用的方法
          * @param args 目标类中方法的参数
          * @return 调用方法后的返回的结果
          * @throws Throwable
          */
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws
          Throwable {
              boolean b = checkPermission();
              if (b){
                  Object result = method.invoke(object, args);
                  log();
                  return result;
              }
              return null;
          }
          private boolean checkPermission(){
              System.out.println("校验用户权限");
              return true;
          }
          private void log(){
          	System.out.println("处理日志");
          }
      }
      
  4. 使用Proxy类的静态方法newProxyInstance,创建代理对象,并把返回值转为接口类型

  5. 调用接口的相关方法

    public class Test {
        public static void main(String[] args) {
            /* UserService service=new UserServiceImpl();
            service.saveUser();
            service.deleteUser();*/
            UserServiceImpl userService = new UserServiceImpl();
            //创建了一个代理对象
            //ClassLoader 类装载器
            //Class[] 获取目标对象的所有接口
            //InvocationHandler
            Object o =
            Proxy.newProxyInstance(userService.getClass().getClassLoader(),
            userService.getClass().getInterfaces(),
            new MyHandler(userService));
            UserService service= (UserService) o;
            service.saveUser();
            System.out.println("___________________");
            service.deleteUser();
        }
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小郑在努力ing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值