22、java中的注解

注解是什么?

       注解可以理解成注释、标记、标签的意思,用来标记类、方法等。就相当于现实生活中的一些事物,上边贴一个标签或者写一些注释性文字来描述它可以用来做什么、怎么用、何时用等信息。Java中的注解也是一样的,用来表示被标记的部分可以做什么、怎么做、何时做等信息。

注解可以用来做什么?

        注解具有生成文档、在编译时期对代码进行检查、运行时期可以动态的实现业务功能,降低耦合度等用途。

注解怎么用?

       Java中的注解可分为三类:内置的可以直接用于功能代码的、内置的元注解、自定义注解,其中元注解用于来注释自定义的注解,使用@interface关键字来声明自定义注解,使用注解时直接@注解名即可。接下来使用看一下实际中的用法:

1、三个内置注解:

//@SuppressWarnings 用于消除警告
@SuppressWarnings("unused")
public void warningTest()
{//@SuppressWarnings("unused")int i = 0;int j = 0;System.out.println("就是不使用i");
}// @Override 用于表示重写方法
@Override
public void run() 
{super.run();
}//@Deprecated 表示被注释的方法已经过时了
@Deprecated
public void deprecatedTest() 
{}

2、自定义注解、元注解:


// 如果没有 @Documented ,定义的注解不会被javadoc写入到文档中,但是我测试着可以哎,了解即可
@Documented //@Retention 注解用于确定注解的保留期限
@Retention(RetentionPolicy.RUNTIME) // @Target 用于标记此注解可以使用的位置是什么:类、方法等
@Target(ElementType.TYPE)public @interface FirstPrintAnnotation {//这里可以定义设置的值,专业名词:注解元素//类型只能是:基本数据类型、String、Class、Enum、Annotation//类似于定义方法似的定义一个值,用于设置数据//String value default “h” 或//String []values() ;//这个值在使用注解值一定要是一个确定的值String value();
}/*** 用来查看AnnontationTest类中用了注解@FirstPrintAnnotation的方法,并获取方法名*/
public class AnnontationTest{public static void main(String[] args) {Class<AnnontationTest> clazz = AnnontationTest.class;Method[] methods = clazz.getMethods();for (Method method : methods) {FirstPrintAnnotation firstPrintAnnotation = method.getAnnotation(FirstPrintAnnotation.class);if (firstPrintAnnotation != null) {System.out.println("使用了注解FirstPrintAnnotationd的方法有:"+firstPrintAnnotation.value());}}
}@FirstPrintAnnotation("test1")
public void test1() {}
@FirstPrintAnnotation("test2")
public void test2() {}
@FirstPrintAnnotation("test3")
public void test3() { }}-----------------------------------------------------------------
//@Retention 源码
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {RetentionPolicy value();
}//RetentionPolicy 代码
//源码解释
//它们与 { Retention } 元注解类型一起使用,以指定注释将被保留多长时间。
public enum RetentionPolicy {//注释将被编译器丢弃,就是只在源码中用以下SOURCE,//注解由编译器记录在类文件中,不需要在运行时被JVM保留,默认情况,可以在class文件中用CLASS,//注释将由编译器记录在类文件中,并在运行时由JVM保留,所以可以通过反射获取,可在运行时类中获取RUNTIME
}------------------------------------------------------------------
//@Target 源码
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {ElementType[] value();
}//ElementType 源码
//这个枚举类型的常量提供了Java程序中可能出现注释的语法位置的简单分类。
public enum ElementType {//类、接口(包括注释类型)或枚举声明TYPE,//字段声明(包括枚举常量)FIELD,//方法声明METHOD,//正式的参数声明PARAMETER,//构造函数声明CONSTRUCTOR,//本地变量声明LOCAL_VARIABLE,//注解类型声明ANNOTATION_TYPE,//包说明PACKAGE,//类型参数声明TYPE_PARAMETER,//类型的使用TYPE_USE
}

注解的实现原理是什么?

       对编译后的注解类进行反编译,就会看到注解会被编译成一个接口类型,并且继承了Annotation接口,这些都是jvm隐式做的工作没必要深究,所以注解不可以像平常类一样使用new进行实例化,但是可以设置它存在于运行时期,所以可以通过反射的方式获取并解析注解。那他到底是怎么工作的呢?通常处理注解都另写一个注解处理器类,这里就不写了,原理就是根据使用了注解的类、方法或者变量来通过反射技术获取到注解,然后对相应代码进行额外功能的添加。简单的看一下如下代码(其实就是上边的实例代码):

public static void main(String[] args) throws InstantiationException, IllegalAccessException {Class<AnnontationTest> clazz = AnnontationTest.class;Method[] methods = clazz.getMethods();for (Method method : methods) {FirstPrintAnnotation firstPrintAnnotation = method.getAnnotation(FirstPrintAnnotation.class);if (firstPrintAnnotation != null) {//所属类型:com.sun.proxy.$Proxy1System.out.println("所属类型:"+firstPrintAnnotation.getClass().getName());Class<?>[] classes = firstPrintAnnotation.getClass().getInterfaces();for (Class<?> class2 : classes) {//实现的接口:interface com.czp.annontation.FirstPrintAnnotationSystem.out.println("实现的接口:"+class2);}Class<?> superclass = firstPrintAnnotation.getClass().getSuperclass();//继承的类:class java.lang.reflect.ProxySystem.out.println("继承的类:"+superclass);//使用注解的值:test1//实际上是 代理对象调用的是AnnotationInvocationHandler 中的invoke方法//在此处打上断点,测试即可firstPrintAnnotation.value();System.out.println("使用注解的值:"+firstPrintAnnotation.value());}}}

        因为此注解可以存在于运行时阶段,所以可以通过反射技术获取到它的运行时类的对象,打印出来是:class com.sun.proxy.$Proxy1。这样我们就可以确定了,注解工作是依赖于java中的动态代理类,通过反射生成注解对应接口的对象,实际上是java的动态代理类,然后通过代理对象去调用注解元素,当使用代理类调用方法时,实际上是调用AnnotationInvocationHandler 的invoke 方法,在成员变量memberValues中获取的值。看一下运行时的源码:

//以根据方法反射注解为例分析//思路:先将被类的运行时期的注解放到一个Map中,然后根据传入的注解类型获取对应的注解的代理类//根据注解类型获取对应的Map集合中的注解对象
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {Objects.requireNonNull(annotationClass);return annotationClass.cast(declaredAnnotations().get(annotationClass));
}
//使用一个 Map<注解的运行时类,注解> 来存放注解信息
private transient Map<Class<? extends Annotation>, Annotation> declaredAnnotations;
private synchronized  Map<Class<? extends Annotation>, Annotation> declaredAnnotations() {//if (declaredAnnotations == null) {Executable root = getRoot();//Excecutable用于注释共享。Method继承自Excecutableif (root != null) {declaredAnnotations = root.declaredAnnotations();} else {declaredAnnotations = AnnotationParser.parseAnnotations(//一个注解分析器getAnnotationBytes(),sun.misc.SharedSecrets.getJavaLangAccess().getConstantPool(getDeclaringClass()),getDeclaringClass());}}return declaredAnnotations;
}//获取Map集合
public static Map<Class<? extends Annotation>, Annotation> parseAnnotations(byte[] var0, ConstantPool var1, Class<?> var2) {if (var0 == null) {return Collections.emptyMap();} else {return parseAnnotations2(var0, var1, var2, (Class[])null);}
}private static Map<Class<? extends Annotation>, Annotation> parseAnnotations2(byte[] var0, ConstantPool var1, Class<?> var2, Class<? extends Annotation>[] var3) {LinkedHashMap var4 = new LinkedHashMap();ByteBuffer var5 = ByteBuffer.wrap(var0);int var6 = var5.getShort() & '?';for(int var7 = 0; var7 < var6; ++var7) {Annotation var8 = parseAnnotation2(var5, var1, var2, false, var3);//这是获取注解具体实现类的操作if (var8 != null) {Class var9 = var8.annotationType();//当注解可以到运行时期使用时,则添加到LinkedHashMap var4中if (AnnotationType.getInstance(var9).retention() == RetentionPolicy.RUNTIME && var4.put(var9, var8) != null) {throw new AnnotationFormatError("Duplicate annotation for class: " + var9 + ": " + var8);}}}return var4;
}AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {Class[] var3 = var1.getInterfaces();if (var1.isAnnotation() && var3.length == 1 && var3[0] == Annotation.class) {//初始化注解数组this.type = var1;this.memberValues = var2;} else {throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");}
}public Object invoke(Object var1, Method var2, Object[] var3) {String var4 = var2.getName();Class[] var5 = var2.getParameterTypes();if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {return this.equalsImpl(var3[0]);} else if (var5.length != 0) {throw new AssertionError("Too many parameters for an annotation method");} else {byte var7 = -1;switch (var4.hashCode()) {case -1776922004 :if (var4.equals("toString")) {var7 = 0;}break;case 147696667 :if (var4.equals("hashCode")) {var7 = 1;}break;case 1444986633 :if (var4.equals("annotationType")) {var7 = 2;}}switch (var7) {case 0 :return this.toStringImpl();case 1 :return this.hashCodeImpl();case 2 :return this.type;default :Object var6 = this.memberValues.get(var4);//在数组中获取对应的值if (var6 == null) {throw new IncompleteAnnotationException(this.type, var4);} else if (var6 instanceof ExceptionProxy) {throw ((ExceptionProxy) var6).generateException();} else {if (var6.getClass().isArray() && Array.getLength(var6) != 0) {var6 = this.cloneArray(var6);}return var6;}}}
}

       简单介绍一下动态代理:就是在运行时期确定代理的类型。使用动态代理时需要使用都的两个类是Proxy和InvacationHandler。做一个简单的例子,代码如下:

//被代理类的公共接口 
public interface Subject {void show();
}-------------------------------------------------------------//被代理类的一个实现 RealSubject
public class RealSubject implements Subject{@Overridepublic void show() {System.out.println("动态代理");}
}//被代理类的一个实现 RealSubject
public class RealSubject implements Subject{@Overridepublic void show() {System.out.println("动态代理");}
}-------------------------------------------------------------//动态代理必须要实现InvocationHandler接口,而不是被代理类的公共接口
public class DynProxy<T> implements InvocationHandler{//被代理类对象private T t;//获取代理对象public T getBlind(T t){this.t = t;return (T) Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), this);}//必须要重写这个方法@Overridepublic Object invoke(Object porxy, Method method, Object[] args) throws Throwable {return method.invoke(t, args);}
}-------------------------------------------------------------//测试
public class Test {public static void main(String[] args) {//确定一下代理的公共接口类型DynProxy<Subject> proxy = new DynProxy<>();//获取代理对象Subject blind = proxy.getBlind(new RealSubject());System.out.println(blind.getClass().getName());//com.sun.proxy.$Proxy0blind.show();}
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/321712.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

谈谈ASP.NET Core中的ResponseCaching

前言前面的博客谈的大多数都是针对数据的缓存&#xff0c;今天我们来换换口味。来谈谈在ASP.NET Core中的ResponseCaching&#xff0c;与ResponseCaching关联密切的也就是常说的HTTP缓存。在阅读本文内容之前&#xff0c;默认各位有HTTP缓存相关的基础&#xff0c;主要是Cache-…

2017西安交大ACM小学期数据结构 [树状数组 离散化]

Problem E 发布时间: 2017年6月28日 12:53 最后更新: 2017年6月29日 21:35 时间限制: 1000ms 内存限制: 64M 描述 给定一个长度为n的序列a1, a2, ..., an给定两个整数L, R输出有多少个二元组(x,y),x≤y, 满足L≤∑yixai≤R9104≤n≤105, −109≤ai≤109, −109≤L≤R≤10…

使用 dynamic 类型让 ASP.NET Core 实现 HATEOAS 结构的 RESTful API

上一篇写的是使用静态基类方法的实现步骤: 使用dynamic (ExpandoObject)的好处就是可以动态组建返回类型, 之前使用的是ViewModel, 如果想返回结果的话, 肯定需要把ViewModel所有的属性都返回, 如果属性比较多, 就有可能造成性能和灵活性等问题. 而使用ExpandoObject(dynamic)就…

23、java中的网编基础

什么是网络编程&#xff1f; 在说网络编程之前要先知道什么是网络&#xff0c;网络是一种实现资源共享和数据传输的系统。而网络编程就是使用代码编写程序来进行网络之间数据的传输。使用java进行网络之间数据的传输是比较简单的&#xff0c;java中提供了一些现成的类供我们使…

2017西安交大ACM小学期数据结构 [又是树状数组、异或]

Problem F 发布时间: 2017年6月28日 10:31 最后更新: 2017年6月29日 21:35 时间限制: 2000ms 内存限制: 64M 描述 给定一个nm的矩形, 初始时所有元素都为0给出q个操作, 操作有三种 对于形如1x的操作, 将第x行的所有元素异或1对于形如2y的操作, 将第y列的所有元素异或1对于…

P2717-寒假作业【逆序对,树状数组】

正题 题目链接:https://www.luogu.com.cn/problem/P2717 题目大意 nnn个数&#xff0c;求有多少个连续子序列的平均值大于等于kkk。 解题思路 因为长度会十分干扰&#xff0c;所以我们将所有数减去kkk。问题就变为了求有多少连续子序列的和非负。用前缀和逆序对求就好了。 co…

使用 BenchmarkDotnet 测试代码性能

先来点题外话&#xff0c;清明节前把工作辞了&#xff08;去 tm 的垃圾团队&#xff0c;各种拉帮结派、勾心斗角&#xff09;。这次找工作就得慢慢找了&#xff0c;不能急了&#xff0c;希望能找到个好团队&#xff0c;好岗位吧。顺便这段时间也算是比较闲&#xff0c;也能学习…

24、jdbc操作数据库(1)

什么是jdbc&#xff1f; 看一下官方怎么说&#xff0c;JDBC 英文名Java DataBase Connectivity&#xff0c;使用java连接数据库的工具&#xff0c;就是一组使用java代码来执行SQL语句的API。 Jdbc有什么用&#xff1f; 数据库有多种&#xff0c;并且不同数据库操作时的方式和…

jzoj3918-蛋糕【二分】

正题 题目链接:https://jzoj.net/senior/#contest/show/2953/0 题目大意 n∗mn*mn∗m的矩阵&#xff0c;有数字&#xff0c;横着三刀竖着三刀分成16份使得最小那份最大。 解题思路 暴力枚举竖着的三刀&#xff0c;然后二分答案判定即可。 codecodecode #include<cstdio&g…

2017西安交大ACM小学期数论 [阅兵式]

阅兵式 发布时间: 2017年6月25日 12:53 最后更新: 2017年7月3日 09:27 时间限制: 1000ms 内存限制: 128M 描述 阅兵式上&#xff0c;将士们排成一个整齐的方阵&#xff0c;每个将士面朝前方。问正中心的将士能向前看到几个将士&#xff1f;注意&#xff0c;一条直线上的将…

25、jdbc操作数据库(2)

说一下使用jdbc时涉及到的一些基本的接口和类 java.sql.Driver 是数据库驱动接口&#xff0c;com.mysql.jdbc.Driver是mysql对应的驱动&#xff0c;由数据库供应商实现&#xff0c;用于提供驱动&#xff0c;实现了java.sql.Driver接口。 java.sql.DriverManager 管理驱动的…

jzoj3919-志愿者【换根法,线段树,树形dp】

正题 题目链接:https://jzoj.net/senior/#main/show/3919 题目大意 nnn个点kkk个需要到达的点&#xff0c;然后求每个点出发经过这些点的最短路径。 解题思路 因为不用回去&#xff0c;答案就是以这点为根链接所有点的树减去离这个点最远点的距离。 我们用线段树维护最远点距…

2017西安交大ACM小学期数论 [水题]

水题 发布时间: 2017年6月25日 14:06 最后更新: 2017年7月3日 09:27 时间限制: 1000ms 内存限制: 128M 描述 平均因数个数的统计对于估算数论题目复杂度具有非常重要的意义。小A同学听了今天的课后&#xff0c;于是想要自己写一个程序&#xff0c;求出1到n的平均因数个数…

26、jdbc操作数据库(3)

1、结果集的元数据操作 ResultSetMetaData 是描述ResultSet的元数据对象&#xff0c;可以通过元数据对象获取结果集的列信息&#xff0c;使用方式如下&#xff1a; public void query() {Connection connection DBUtil.getConnection();String sql "SELECT ID,UNAME u,…

jzoj3920-噪音【贪心,dp】

正题 题目链接:https://jzoj.net/senior/#main/show/3920 题目链接 nnn头牛&#xff0c;mmm个牛棚&#xff0c;可以清空kkk次。 每头牛会指定进入一个牛棚&#xff0c;进入后产生牛棚中牛数量的噪音值&#xff0c;然后清空可以清空一个牛棚的牛&#xff0c;求最少噪音值。 解…

2017西安交大ACM小学期数论 [完全平方数]

完全平方数 发布时间: 2017年6月24日 17:01 最后更新: 2017年7月3日 09:27 时间限制: 1000ms 内存限制: 128M 描述 给定正整数b&#xff0c;求最大的整数a&#xff0c;使a(ab)是完全平方数。 输入 多组测试数据&#xff08;不超过10000组&#xff09;。 每组数据一个正整…

27、jdbc操作数据库(4)

数据库连接池是什么&#xff1f;为什么使用连接池&#xff1f; 当使用jdbc去操作数据库时&#xff0c;需要先获取连接&#xff0c;然后进行具体的数据库操作&#xff0c;最后释放连接。这一系列操作在演示一个demo时还可以使用&#xff0c;但是当访问数据库次数多了的时候&…

28、jdbc操作数据库(5)

介绍一个稍微封装了jdbc的工具类org.apache.commons.dbutils&#xff0c;使用dbutils可以简化对数据库操作程序的开发。 API介绍 接下来通过实例的方式说一下dbutils的具体使用 添加jar包&#xff1a;commons-dbutils-1.7.jar 增、删、改 进行增、删、改操作&#xff0c;在…

jzoj3910-Idiot的间谍网络【倍增,dfs】

正题 题目链接:https://jzoj.net/senior/#main/show/3910 题目大意 nnn个点的森林mmm个操作&#xff0c; 合并两棵树让xxx节点到根节点的路径标记上cntcntcnt。并让cntcntcnt查询一个节点xxx是否有标记yyy。 解题思路 这里考虑离线的算法。我们可以先将这个森林的最终状态构…

2017西安交大ACM小学期数论 [更新学号]

发布时间: 2017年6月24日 20:27 最后更新: 2017年7月3日 09:27 时间限制: 3000ms 内存限制: 128M 描述 某知名高校有n个学生&#xff0c;每个学生有一个唯一ID。但这些ID有7位数字&#xff0c;学生总是记不住&#xff0c;于是学校想了一个办法。它们将所有ID对m取模作为学…