注解与反射

一、注解(Annotation)

1、内置注解

(1) @Override:重写父类方法

(2)@Deprecated:不鼓励程序员使用,表示废弃的

(3)@SuppressWarnings:抑制命名的编译警告

2、元注解

(1)概念:负责注解其他注解,meta-annotation

(2)4个标准的meta-annotation:

a、Target表示我们的注解可以用在哪些地方:

@Target(value={ElementType.METHOD,ElementType.TYPE}),其中ElementType.METHOD表示可用于方法上,ElementType.TYPE表示可用于类上。

b、Retention表示我们的注解在什么地方有效:@Retention(value=RetentionPolicy.RUNTIME),其中RetentionPolicy.RUNTIME表示在运行时有效,是默认值,runtime(运行时)>class(编译后)>sources(源文件时)。

c、Documented表示是否将注解生成在JAVADOC中,@Documented。

d、Inherited表示子类可以继承父类的注解,@Inherited。

3、自定义注解

(1)使用@interface自定义注解,格式:public @interface 注解名 {定义内容}

示例:

//把自定义@MyAnnotation注解用在test方法上
//注解可以显示赋值,如果没有默认值,就必须给注解辅助
@MyAnnotation(age=18,name="liqing")
public void test(){}
//自定义@MyAnnotation注解
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation(){
    String name();
    int age();
    int id() default -1;//如果默认值为-1,表示不存在
    String[] schools default{"清华","北大"};
}

(2)如果只有一个参数成员时,一般参数名为value,用注解时,可以不写" value=" " "即@MyAnnotation(value="all")或@MyAnnotation("all")都可以。当参数名不为value时,则必须写出@注解名(参数名=参数值)。

二、反射机制(Reflection)

1、动态语言与静态语言

(1)动态语言:在运行时代码可以根据某些条件改变自身结构,例如:Object-C、C#、JavaScript、PHP、Python等。

(2)静态语言:运行时,结构不可改变,例如:Java、C、C++。

(3)java被称“准动态语言”,因为可以通过反射机制获得类似动态语言的特性。

2、反射说明

(1)加载完类后,在堆内存的方法区中就产生一个Class类型的对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息。

(2)正常方式:引入需要的“包类”名称--->通过new实例化--->取得实例化对象

(3)反射方式:实例化对象--->getClass()方法--->得到完整的"包类"名称

3、Java反射机制提供的功能:

(1)在运行时判断任意一个对象所属的类

(2)在运行时构造任意一个类的对象

(3)在运行时判断任意一个类所具有的成员变量和方法

(4)在运行时获取泛型信息

(5)在运行时调用任意一个对象的成员变量和方法

(6)在运行时处理注解

(7)生成动态代理

4、反射的主要API

(1)java.lang.Class:代表一个类

(2)java.lang.reflect.Method:代表类的方法

(3)java.lang.reflect.Field:代表类的成员变量

(4)java.lang.reflect.Constructor:代表类的构造器

注:一个类在内存中只有一个Class对象,一个类被加载后,类的整个结构都会被封装在Class对象中。

5、Class类的常用方法

(1)static Class forName(String name):返回指定类名name的Class对象

(1)getName():获取实体类名称

(1)getSupperClass():获取父类的Class对象

(1)getInterfaces():获取接口

(1)getClassLoader():获取类加载器

(1)getConstructors():获取构造器

(1)getMethod():获取方法

(1)getDeclaredFields():获取属性

6、获取Class类实例的几种方式

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

Class c = Person.class;//Person为类名

(2)通过类的实例名

Class c = person.getClass();//person为类实例

(3)通过Class类的forName()静态方法获取

Class c = Class.forName("com.liqing.Person");//com.liqing.Person为Person类的全限定名即类的全路径

(4)基本内置类型的包装类都有一个Type属性

Class c = Integer.TYPE;

7、可以有Class对象的类型

(1)class:类

(2)interface:接口

(3)[]:数组

(4)enum:枚举

(5)annotation:注解(@interface)

(6)primitive type:基本数据类型

(7)void:空

注:只要元素类型与维度一样,就是同一个Class。

8、Java内存分析

(1)类的加载过程:

类的加载(Load)———>类的链接(Link)———>类的初始化(Initialize)

a、类的加载:将类的class文件读入内存,并为之创建一个java.lang.Class对象,此过程由类加载器完成。

b、类的链接:将类的二进制数据合并到JRE中。

c、类的初始化:JVM负责对类进行初始化

(2)详细过程:

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

b、链接:将Java类的二进制代码合并到JVM的运动状态之中的过程。

验证:确保加载的类信息符合JVM规范,没有安全方面的问题。

准备:正式为类变量(static)分配内存并设置类变量默认初始值得阶段,这些内存都将在方法区中进行分配。

解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。

c、初始化:<clinit>()方法是由JVM提供的。

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

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

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

d、图解分析:

![image-20231124150236024](C:\Users\19231\AppData\Roaming\Typora\typora-user-images\image-20231124150236024.png)

9、发生类初始化情况:

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

a、当虚拟机启动,先初始化main方法所在的类;

b、new一个类的对象;

c、调用类的静态成员(除了final(常量)和静态方法);

d、使用java.lang.reflect包的方法对类进行反射调用;

e、当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类;

(2)类的被动引用(不会发生类的初始化)

a、当通过子类引用父类的静态变量,不会导致子类初始化;

    b、通过数据定义类引用,不会触发此类的初始化;

c、引用常量不会触发此类的初始化(常量在链接阶段就存入类的常量池中);

10、类加载器的作用

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

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

(3)图解分析:

image-20231124153558066

(4)示例:

//获取系统类的加载器
Classloader systemClassLoader = ClassLoader.getSystemClassLoader();
//获取系统类加载器的父类加载器———>扩展类加载器
ClassLoader extClassLoader = systemClassLoader.getParent();
//获取扩展类加载器的父类加载器———>根加载器、
ClassLoader bootClassLoader = extClassLoader.getParent();//返回为null
//获取当前类的加载器
ClassLoader classLoader = Class.forName("com.liqing.reflection.Test").getClassLoader();
//JDK内置类的加载器
ClassLoader classLoader = Class.forName("java.lang.Object").getClassLoader();
//获得系统类加载器可以加载的路径
System.getProperty("java.class.path");

(5)双亲委派机制:

双亲委派机制(Parent Delegation Mechanism)是Java中的一种类加载机制。在Java中,类加载器负责加载类的字节码并创建对应的Class对象。双亲委派机制是指当一个类加载器收到类加载请求时,它会先将该请求委派给它的父类加载器去尝试加载。只有当父类加载器无法加载该类时,子类加载器才会尝试加载。

11、获取运行时类的完整结构

(1)类的Class对象c:c.getName();//获取包名+类名

c.getSimpleName();//获取类名

(2)获取类的属性:

c.getFields();//只能找到public属性

c.getDeclaredFields();//找到全部属性

c.getDeclaredField("name");//获取指定属性的值(获得该类的name属性)

(3)获取类的方法:

c.getMethods();//获取本类及其父类的全部public方法

c.getDeclaredMethods();//获取本类的所有方法

c.getMethod("getName",null);//获取该类的getName方法,null表示该方法无形参

c.getMethod("setName",String.class);//获取该类的setName方法,String.class表示该方法的形参为String类型

(4)获得构造器:

c.getConstructors();//获取public构造方法

c.getDeclaredConstructors();//获取本类的全部构造方法

c.getDeclaredConstructor(String.class,int.class,int.class);//获取指定构造器,其中String.class,int.class,int.class均为构造方法的形参。

12、动态创建对象的执行方法

(1)类的Class对象c:

User user = (user)c.newInstance();//本质是调用了类的无参构造器

(2)创建类的对象:调用Class对象的newInstance()方法

a、类必须由一个无参构造器

b、类的构造器的访问权限需要足够

(3)通过构造器创建对象:

Constructor constructor = c.getDeclaredConstructor(String.class,int.class,int.class);

User user = (User)constructor.newInstance("liqing",001,18);

(4)通过反射调用普通方法:

User user = (User)c.newInstance();

Method method = c.getDeclaredMethod("setName",String.class);

//invoke(对象,"方法参数的值"):激活该类的方法

method.invoke(user,"liqing");

(5)通过反射操作属性:

User user = (User)c.newInstance();

Field name = c.getDeclaredField("name");

//不能直接操作私有属性,需要关闭程序的安全检测,所以需要将属性或者方法的setAccessible(true);默认为false。

name.setAccessible(true);

//set(对象,"属性值")

name.set(user,"liqing");

(6)setAccessible方法:

a、方法、属性、构造器都有setAccessible()方法。

b、setAccessible作用是启动和仅用访问安全检查的开关。参数值为true则指示反射的对象在使用时,应该取消Java语言访问检查。参数值为false则应该实施java语言访问检查,默认为false。

13、反射操作泛型

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

(2)示例:获取方法参数的泛型

Type [] genericParameterTypes = method.getGenericParameterTypes();//获取方法参数
for(Type genericParameterType : genericParameterTypes){
    if(genericParameterType instanceof ParameterizedType){
        Type [] actualTypeArguments = ((ParameterizedType)genericParameterType).getActualTypeArguments();//获取方法参数的泛型
        for(Type actualTypeArgument : actualTypeArguments){
            
        }
    }
}

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

获得方法返回值的泛型:将getGenericParameterTypes()换成getGenericReturnType()即可。

14、反射操作注解

(1)ORM:Object Relationship Mapping,即对象关系映射,类和表结构对应;属性和字段对应;对象和记录对应。

(2)通过反射获取注解:

Annotation [] annotations = c.getAnnotations();

for(Annotation annotation : annotations){

}

(3)获取注解的value值:

TableName tableName = (TableName)c.getAnnotation(TableName.class);//TableName为类的注解名

String value = tableName.value();//TableName注解中的value方法。

(4)获取类指定的注解:

Field f = c.getDeclaredField("id");

FieldName fieldName = f.getAnnotation(FieldName.class);//FieldName为属性的注解名

fieldName.columnName();//FieldName注解的方法

fieldName.type();//FieldName注解的方法

fieldName.length();//FieldName注解的方法

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值