欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

深入理解Java框架机制(注解和反射)

程序员文章站 2022-07-03 20:43:13
版权声明:本文为博主ExcelMann的原创文章,未经博主允许不得转载。Java基础——注解和反射作者:ExcelMann,转载需注明。该篇文章的内容来源于B站视频的总结(up主:遇见狂神说)。内容目录:什么是注解内置注解元注解自定义注解反射概述获得反射对象得到Class类的几种方式所有类型的Class对象类加载内存分析分析类初始化类加载器获取类的运行时结构动态创建对象执行方法性能对比分析获取泛型信息获取注解信息一、什么是注解Annotation是从JDK5...

版权声明:本文为博主ExcelMann的原创文章,未经博主允许不得转载。

Java基础——注解和反射

作者:ExcelMann,转载需注明。

该篇文章的内容来源于B站视频的总结(up主:遇见狂神说)

内容目录:

  • 什么是注解
  • 内置注解
  • 元注解
  • 自定义注解
  • 反射概述
  • 获得反射对象
  • 得到Class类的几种方式
  • 所有类型的Class对象
  • 类加载内存分析
  • 分析类初始化
  • 类加载器
  • 获取类的运行时结构
  • 动态创建对象执行方法
  • 性能对比分析
  • 获取泛型信息
  • 获取注解信息

一、什么是注解

  1. Annotation是从JDK5.0开始引入的新技术;
  2. Annotation的作用:
    1)不是程序的本身,但是可以对程序作出解释(与注释相同);
    2)可以被其它的程序(比如编译器)读取,使得具有检错作用;
  3. Annotation在哪里使用?
    可以附加在package、class、method、field等上面,相当于给他们添加了额外的辅助信息,可以通过反射机制编程实现对这些元数据的访问;

二、内置注解

  1. Java常用的三个内置注解:
    深入理解Java框架机制(注解和反射)

三、元注解

  1. 元注解的作用:负责注解其他注解,Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型做说明;
  2. 四个:
    深入理解Java框架机制(注解和反射)
@MyAnnotation
public class Test {

}

//自定义一个注解,其中用到了四个元注解
@Target(value = {ElementType.TYPE,ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME) //一般自定义注解,都选择RUNTIME;
@Documented
@Inherited
@interface MyAnnotation{

}

四、自定义注解

  1. 如何自定义注解:使用@interface自定义注解,自动继承了java.lang.annotation.Annotation接口;

  2. 分析:
    深入理解Java框架机制(注解和反射)

  3. 代码实例:

@MyAnnotation(name = "") //此时可以省略age参数的传参
@MyAnnotation2("value") //此时可以省略"value="
public class Test {
}

//自定义注解
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
    //注解的参数:参数类型+参数名()
    //记住不是方法,是注解的参数
    String name();
    int age() default 0; //default定义默认值,此时使用注解的时候可以不用赋予该参数;
}

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
    String value(); //特殊情况:当参数名为value时,此时使用注解的时候可以省略value;
}

五、反射概述

  1. 动态语言和静态语言
    动态语言是一类在运行的时候可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进或者删除已有函数等。
    静态语言在运行时结构不可变,如Java、C和C++。
    Java不是动态语言,不过称为“准动态语言”,因为可以利用反射机制获得类似动态语言的特性。

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

  3. 在加载了类之后,在堆内存的方法区中,就产生了一个Class类型的对象(一个类只有一个Class对象),该对象包含了完整的类的结构信息。
    我们可以通过该对象来看到类的完整的结构,这个对象就像是一个镜子一样,透过镜子看到类的结构,因此成为反射;

  4. Java反射机制提供的功能:
    深入理解Java框架机制(注解和反射)

六、获得反射对象

  1. 代码如下:
public class ReflectionTest {

    public static void main(String[] args) throws ClassNotFoundException {
        //通过反射获取类的Class对象
        Class c1 = Class.forName("AnnotationAndReflection.User");
        System.out.println(c1); //printf class AnnotationAndReflection.User
        //后面就可以通过该对象,来反射求出类
    }
}
class User{
    private String name;
    private int id;
    private int age;

    public User() {
    }
    ...
}

七、得到Class类的几种方式

  1. Class类:对于每个类而言,JRE都为其保留了一个不变的Class类型的对象。一个Class对象包含了特定某个结构的有关信息;
    深入理解Java框架机制(注解和反射)

  2. Class类的常用方法深入理解Java框架机制(注解和反射)

  3. 获取Class类的多种方式
    1)若已知是具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高:
    Class clazz = Person.class;
    2)已知某个类的实例,调用该实例的getClass()方法获取Class对象:
    Class clazz = person.getClass();
    3)已知一个类的全名,且该类在类路径中,可通过Class类的静态方法forName()获取:
    Class clazz = Class.forName(…);
    4)内置基本数据类型的包装类可以直接用类名.Type;
    5)还可以利用ClassLoader加载器。。。

八、所有类型的Class对象

  1. 哪些类型可以有Class对象
    class(外部类,成员,局部内部类,匿名内部类)、interface、数组、枚举、注解、基本数据类型包装器、void;

九、(★)类加载内存分析

  1. Java内存划分(其中的方法区就是特殊的堆):
    深入理解Java框架机制(注解和反射)

  2. 类的加载过程:当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过以下三个步骤来对该类进行初始化。
    深入理解Java框架机制(注解和反射)

  3. 实例代码与解析

public class Test{
	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类的无参数构造初始化");
	}
}

运行结果:
深入理解Java框架机制(注解和反射)
画内存图解析:
1.加载到内存,会产生一个类对应的Class对象
2.链接,链接结束后m=0
3.初始化,执行clinit方法,故最后m=100

<clinit>(){
	//static代码块中的代码
	System.out.println("A类静态代码块初始化");
	m = 300;
	//类变量初始化代码
	m = 100;
}

深入理解Java框架机制(注解和反射)

十、分析类初始化

  1. (★)什么时候会发生类初始化
    深入理解Java框架机制(注解和反射)

十一、类加载器

  1. java程序运行图
    深入理解Java框架机制(注解和反射)

  2. 类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象;

  3. 类加载器的作用
    深入理解Java框架机制(注解和反射)

  4. 获取类加载器的代码:

//获取系统类的加载器
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++)
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);//null

//测试当前类是哪个加载器加载的:系统类加载器
ClassLoader classLoader = Class.forName("AnnotationAndReflection.ClassLoaderTest").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"));//各种jdk/jre/jar包路径,以及E:\Java学习\Java核心技术\out\production\Java核心技术等

十二、获取类的运行时结构

  1. 通过反射获取运行时类的完整结构:Field,Method,Constructor,Superclass,Interface,Annotation;
  2. 代码如下:
Class aClass = Class.forName("AnnotationAndReflection.User");
//获得类的名字
System.out.println(aClass.getName());
System.out.println(aClass.getSimpleName());
System.out.println("==============================");
//获得类的属性
Field[] fields = aClass.getFields(); //只能找到类的public属性
fields = aClass.getDeclaredFields(); //找到全部属性
for(Field field:fields) {
    System.out.println(field);
}
//获得类的指定属性
Field field = aClass.getDeclaredField("name");
System.out.println(field);
System.out.println("==============================");
//获得类的方法
Method[] methods = aClass.getMethods(); //获得本类以及父类的所有public方法
for(Method method:methods){
    System.out.println("所有的:"+method);
}
methods = aClass.getDeclaredMethods(); //获得本类的所有方法(包含私有方法)
for(Method method:methods){
    System.out.println("本类的:"+method);
}
//获取指定方法
Method getName = aClass.getDeclaredMethod("getName",null);
Method setName = aClass.getDeclaredMethod("setName", String.class);
System.out.println(getName);
System.out.println(setName);
System.out.println("==============================");
//获取类的构造器
Constructor[] constructors = aClass.getConstructors(); //获得public的构造器
for (Constructor constructor : constructors) {
    System.out.println(constructor);
}
constructors = aClass.getDeclaredConstructors(); //获得所有构造器
for (Constructor constructor : constructors) {
    System.out.println("#"+constructor);
}
//获得指定的构造器
Constructor constructor = aClass.getDeclaredConstructor(String.class, int.class, int.class);
System.out.println(constructor);

十三、动态创建对象执行方法

  1. 有了Class对象之后,能做什么:可以通过Class对象创建该类的对象,然后执行该类的方法。
  2. 如何利用反射机制动态创建对象
    1)调用Class对象的newInstance()方法,要求类必须有一个无参数的构造器,而且类的构造器的访问权限足够;
    2)当类没有无参的构造器的时候,只要在操作的过程中明确指定类中的构造器即可创建对象,步骤如下:首先,通过Class对象获取本类的指定形参类型的构造器;第二步,向构造器的形参中传递一个对象数组进去,里面包含了构造器所需的各个参数;第三步,通过Constructor实例化对象;
  3. 通过Object invoke(Object obj,Object[] args)调用指定的方法:
    1)Object对应原方法的返回值,若原方法无返回值,此时返回null;
    2)若原方法为静态方法,则obj参数可以为null;
    3)若原方法形参列表为空,则args参数可以为null;
    4)(★)若原方法声明为private,则在调用invoke方法之前,需要先调用setAccessible(true)方法,即可访问private的方法;
  4. setAccessible:Method和Field、Constructor对象都有setAccessible()方法;

十四、性能对比分析

  1. 由实验结果得出,反射执行的速度比普通方式要慢很多。如果非得要用反射,而且是多次用的情况下,可以通过关闭检测来提高速度;
    深入理解Java框架机制(注解和反射)

十五、获取泛型信息

  1. 注意点:对于泛型,Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的问题,但是,一旦编译成功,所有和泛型有关的类型全部擦除;
    但是泛型的信息,还是会在加载的过程中存储在Class中;
  2. 代码实例:
//通过反射获取方法参数泛型
//第一步:获取指定的方法
Method method = ReflectionTest.class.getDeclaredMethod("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);
       }
   }
}
System.out.println("=============");
//通过反射获取方法返回值泛型
method = ReflectionTest.class.getDeclaredMethod("test02",null);
Type genericReturnType = method.getGenericReturnType();//通过方法获得返回值泛型

if(genericReturnType instanceof ParameterizedType){
   Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
   for (Type actualTypeArgument : actualTypeArguments) {
       System.out.println(actualTypeArgument);
   }
}

十六、获取注解信息

该节中,通过一个练习,利用注解和反射完成类和表结构的映射关系。

  1. 了解什么是ORM
    深入理解Java框架机制(注解和反射)
  2. 通过反射获取类、方法或字段的注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface tabel{
    String value();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface attribute{
    String columnName();
    String type();
    int length();
}

@tabel("db_student")
class Student1{
    @attribute(columnName = "id",type = "int",length = 10)
    private int id;

    @attribute(columnName = "age",type = "int",length = 10)
    private int age;

    @attribute(columnName = "name",type = "String",length = 10)
    private String name;
}

public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
    Class c1 = Class.forName("AnnotationAndReflection.Student1");

     //通过反射获取类的注解
     Annotation[] annotations = c1.getAnnotations();
     for (Annotation annotation : annotations) {
         System.out.println(annotation);//@AnnotationAndReflection.tabel(value=db_student)
     }

     //获得指定注解的value值
     tabel tabelAnnotation = (tabel)c1.getAnnotation(tabel.class);
     String value = tabelAnnotation.value();
     System.out.println(value);//db_student

     //获得类内部(方法或字段)的指定注解
     Field name = c1.getDeclaredField("name");
     attribute attributeAnnotation = name.getAnnotation(attribute.class);
     System.out.println(attributeAnnotation.columnName());//name
     System.out.println(attributeAnnotation.length());//10
     System.out.println(attributeAnnotation.type());//String

    }

本文地址:https://blog.csdn.net/a602389093/article/details/110244910