注解和反射

 

注解

注解和反射是所有框架的基础

注解关键字:Annotation

概述

  • 历史:JDK5.0引入
  • 作用:不是程序本身,可以对程序作出解释(注解给程序看的,注释是给程序员看的)。可以被程序读取
  • 格式:以"@注解名"在代码中存在,还可以添加一些参数值
  • 使用:可以在Package,Class,Method,Field等上面,相当于给他们添加了额外的辅助信息,可以通过反射机制编程实现对这些元数据的访问

元注解

元注解的作用就是负责注解其他注解

  • @Target:用于描述注解的使用范围,即注解可以用在什么地方
  • @Retention:表示需要在什么级别保存该注解信息,用于描述注解的生命周期(SOURCE < CLASS < RUNTIME)
  • @Document:说明该注解将被包含在javadoc中
  • @Inherited:说明子类可以继承父类中的该注解

自定义注解

使用@Interface自定义注解是,自动继承了java.lang.Annotation接口

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

示例

 
 
 
x
 
 
 
 
1
@Target({ElementType.TYPE,ElementType.METHOD})
2
@Retention(RetentionPolicy.RUNTIME)    //元注解
3

4
@MyAnnotation(value="wu",type=ElementType.TYPE,classes=ElementType.class) //使用
5
public @interface MyAnnotation {
6

7
//注解的参数:参数类型 + 参数名
8
String value() default "";  //String类型
9

10
ElementType[] type() default ElementType.TYPE ; //枚举类型
11

12
Class<?>[] classes() default {}; //Class类型
13
    
14
    int num() default 0;
15
}
 
 

 

反射

类加载概述

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对类的初始化

类加载过程

  • 加载 指将class文件读入内存,并为之创建一个Class对象 任何类被使用时系统都会建立一个Class对象
  • 链接 验证 是否有正确的内存结构,并和其它类协调一致 准备 负责为类的静态成员分配内存,并设置默认初始值 解析 将类的二进制数据中的符号引用替换为直接引用
  • 初始化 通过类构造器,对数据进行初始化

image-20210816235653493

 

类的初始化

  • 类的主动引用(一定会发生类的初始化)

    • 当JVM启动,先初始化main方法所在的类
    • new一个类的对象
    • 调用类的静态成员(出来final常量)和静态方法
    • 使用java.lang.reflect包的方法对类进行反射调用
    • 当初始化一个类,如果器父类没有被初始化,则向初始化他的父类
  • 类的被动引用(不会发生类的初始化)

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

类加载器

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

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

image-20210816164106153

 

分类

  • Bootstrap ClassLoader 根类加载器(引导类加载器)

    用C++编写,是JVM自带的类加载器

    负责Java核心类的加载 比如System类,String类,在JDK中JRE的lib目录下rt.jar文件中。该加载无法直接获取。

  • Extension ClassLoader 扩展类加载器

    负责JRE的扩展目录中jar包的加载 在JDK中JRE的lib目录下ext目录

  • System ClassLoader 系统类加载器

    负责在JVM启动时加载来自java命令的class文件以及 classpath环境变量所指定的jar包和类路径

    是最常用的加载器。

image-20210816164720050

 
 
 
xxxxxxxxxx
24
 
 
 
 
1
public class Load {
2
public static void main(String[] args) throws ClassNotFoundException {
3

4
//系统加载器
5
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
6
System.out.println(systemClassLoader);
7

8
//扩展类加载器
9
ClassLoader ext = systemClassLoader.getParent();
10
System.out.println(ext);
11

12
//根类加载器
13
ClassLoader parent = ext.getParent();
14
System.out.println(parent);
15

16
//查看某个类是用那个加载器加载的
17
ClassLoader classLoader = Class.forName("java.lang.String").getClassLoader();
18
System.out.println(classLoader);
19

20
//查看系统加载器可以加载的路径
21
String property = System.getProperty("java.class.path");
22
System.out.println(property);
23
}
24
}
 
 

动态代理

实质:动态代理实质是通过反射来生成一个代理对象

理解:本来应该自己做的事情,却请了别人来做,被请的人就是代理对象

代理方式

  • JDK提供的代理只能针对接口代理

    Proxy类和InvoactionHandler接口 通过使用这个类和接口就可以生成动态代理对象

  • 框架中的动态代理

    cglib

动态代理类的使用

 
 
 
xxxxxxxxxx
9
 
 
 
 
1
//Proxy类
2
public static Object newProxyInstance(ClassLoader
3
loader,Class<?>[] interfaces,InvocationHandler h
4
)
5
//返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序
6

7
//InvoactionHandler接口
8
Object invoke(Object proxy,Method method,Object[] args)
9
//在代理实例上处理方法调用并返回结果
 
 

反射

动态语言VS静态语言

  • 动态语言
  • 是一类在运行时可以改变器结构的语言,例如新的函数,对象甚至代码可以被引进,已有函数可以被删除或是其他结构上的变化。即在运行时代码可以根据某些条件改变自身结构
  • 动态语言:Object-C,C#,JavaScript,PHP,Python等
  • 静态语言
  • 与动态语言相对应,运行时结构不可变得语言。如Java,C,C++
  • Java不是动态语言,但是Java可以称为"准动态语言"。即Java有一定的动态性。可以利用反射机制获得类似动态语言的特性。Java的动态性让编程更加灵活。

定义

反射:Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个类对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称之为Java语言的反射机制。

为什么称为反射:在加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类结构信息。可通过这个对象看到类的结构,这个对象就像一面镜子,透过这个镜子看到类的结构,所以形象的称之为:反射

优点:可以实现动态创建对象和编译,体现出很大的灵活

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

 

 

image-20210816142821474

 

Java内存分析

image-20210816150931127

 

 

Class类

必须先要获取 到该类的字节码文件对象,使用的就是Class类中的方法

获取class文件对象的方式

  1. Object类的getClass()方法

  2. 数据类型的静态属性class(如:int.class,String.class)

  3. Class类中的静态方法

    public static Class forName(String className) className 必须是类的全路径,带包名的

  4. 基本类型的包装类都有一个Type属性(如:Integer.Type)

开发中一般使用第三种 Why? 第三种是一个字符串,而不是一个具体的类名 这样我们就可以吧这样的字符串串配置到配置文件中

那些对象可以有Class对象(几乎所有的对象都有Class对象)

  • 外部类
  • interface 接口
  • 数组
  • enum
  • annotation
  • primitive type
  • void

...................

使用步骤

  1. 获取对象的字节码文件对象
  2. 通过字节码文件对象获取对相应的构造方法对象,成员变量对象或成员方法对象
  3. 成员变量对象(set)调用类对象设置值 成员方法对象(invoke)调用对象,调用方法传递参数,如果有返回值,需要接收

应用场景

  1. 创建类的对象,调用Class对象的newInstance()方法

    前提:类必须有一个无参构造器,类的构造器的访问权限足够

  2. 调用指定的方法

image-20210816144850884

反射的基本使用

获取构造方法
 
 
 
xxxxxxxxxx
27
 
 
 
 
1
public Constructor<?>[] getConstructors()
2
//返回一个包含某些Constructor对象的数组
3
//返回的公共的构造方法
4

5
public Constructor<?>[] getDeclaredConstructors()
6
//返回Constructor对象的一个数组,
7
//返回的事所有的构造方法
8

9
public Constructor<T> getConstructor(Class<?>... parameterTypes)
10
//返回一个Constructor对象(公共的)
11
//传递的参数是数据类型对应的class(字节码文件对象)
12
//参数为需要传递的参数的字节码对象形式
13

14
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
15
//返回一个Constructor对象(包含私有的)
16
//必须配合
17
void setAccessible(true)使用
18
//将次对的accessible标志设置为指示的布尔值
19
//值为true则指示反射的对象在使用时应该取消java
20
//语言访问检查
21

22

23
//使用构造方法新建实例:
24
public newInstance(Object...initargs)
25
//使用此Constructor对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
26
//(实际是new了个对象)
27
// 参数为传递的参数
 
 
获取成员变量
 
 
 
xxxxxxxxxx
16
 
 
 
 
1
Field[] getFields()
2
//获取所有的公共成员变量
3

4
Field[] getDeclareFields()
5
//获取所有的成员变量
6

7
Field getField()
8
//获取单个成员变量(受访问权限限制)
9

10
Field getDeclareFields()
11
//获取单个成员变量(不受访问权限限制)
12
//必须配合setAccessible(true)使用
13

14

15
void set(Object obj,Object value)
16
//将指定对象变量上此Field对象表示的字段设置为指定的新值
 
 
获取成员方法
 
 
 
xxxxxxxxxx
19
 
 
 
 
1
Method[] getMethods()
2
//获取所有的公共成员方法(自己的包括父类的公共方法)
3

4
Method[] getDeclaredMethods()
5
//获取所有的成员方法    (获取自己的所有的方法)
6

7
Method getMethod(String name,Class<?>...parameterTypes)
8
//获取单个方法(受访问权限限制)
9
//参数一:方法名称
10
//参数二:方法参数的class类型
11

12
Method getDeclaredMethod(
13
String name,Class<?>...parameterTypes)
14
//获取单个方法(不受访问权限限制)
15
//配合setAccessible(true)使用
16

17
Object invoke(Object obj,Object... args)
18
//参数一:表示对象是谁
19
//参数二:表示调用该方法的实际参数
 
 

反射操作泛型

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

3
public void start(Map<String,Boolean> map,List<Integer> list){
4

5
}
6
public static void main(String[] args) throws Exception {
7
Method method = Genericity.class.getMethod("start", Map.class,List.class);
8

9
Type[] type = method.getGenericParameterTypes();
10
for(Type t: type){
11
System.out.println(t);
12
if(t instanceof ParameterizedType){
13
Type[] actual = ((ParameterizedType) t).getActualTypeArguments();
14
for(Type ty: actual){
15
System.out.println(ty);
16
}
17
}
18
}
19
}
20
}
 
 

反射与注解

核心方法:

getAnnotation()

getAnnotations()

示例

 
 
 
xxxxxxxxxx
49
 
 
 
 
1
public class Synthesize {
2

3
public static void main(String[] args) throws Exception{
4

5
Table table = UserTable.class.getAnnotation(Table.class);
6
System.out.println(table);
7

8
Field field = UserTable.class.getDeclaredField("id");
9
Annotation[] annotations = field.getAnnotations();
10
for(Annotation one : annotations){
11
System.out.println(one);
12
}
13
}
14

15
}
16

17
@Table("userTable")
18
class UserTable{
19

20
@Verify
21
@Column(columnName = "user_id",type="varchar",size=32)
22
private String id;
23
@Column(columnName = "user_name",type="varchar",size=32)
24
private String userName;
25
@Column(columnName = "pass_word",type="varchar",size=32)
26
private String password;
27

28

29
}
30

31
@Target(ElementType.TYPE)
32
@Retention(RetentionPolicy.RUNTIME)
33
@interface Table{
34
String value();
35
}
36

37
@Target(ElementType.FIELD)
38
@Retention(RetentionPolicy.RUNTIME)
39
@interface Column{
40
String columnName();
41
String type();
42
int size();
43
}
44

45
@Target(ElementType.FIELD)
46
@Retention(RetentionPolicy.RUNTIME)
47
@interface Verify{
48
boolean value() default true;
49
}
 
 

 

posted @ 2021-08-19 18:04  携兮  阅读(162)  评论(0)    收藏  举报