JAVASE 注解与反射

2023-11-14

注解与反射都是框架的基础

注解

  • 注解的格式:@注释名(参数名=参数值)

  • 可以使用在:package、class、method、field上,作为辅助信息

内置注解

  1. @Override:重写方法,会检测方法名称
  2. @Deprecated:表明该方法已过时,通常会存在更好的替代方法,调用时会有删除线:test()
  3. @SupressWarnings:镇压警告,可以放在类上也可以在方法上等等

元注解 meta-notation

负责注解其他注解,有四种元注解:

  1. @Target:使用范围 ,@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
  2. @Retention:生命周期,SOURCE源码注解生效<CLASS生成类时有效<RUNTIME运行时有效(常用)
  3. @Document:生成文档
  4. @Inherited:子类继承注解
//测试元注解
public class Test02 {

	@MyAnnotation("param")
	public void test() {
		
	}
}

//定义一个注解

@Target(value= {ElementType.METHOD,ElementType.TYPE})//用在方法、类上
@Retention(value=RetentionPolicy.RUNTIME)  //运行时也有效
@Documented //是否声称在JavaDoc文档中
@Inherited //子类可以继承父类的注解
@interface MyAnnotation{
	String[] value();  //设定参数
}

自定义注解

  1. @Interface:自定义注解

  2. 参数格式:参数类型+参数名();

//自定义注解
public class Test03 {
	
	@MyAnnotation2(name="周逸",schools= {"南京邮电大学"}) //只有一个可以省略value
	public void test() {}
}

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
	//注解参数编写格式: 参数类型+参数名();
	String name() default ""; //默认为空
	int age() default 0;
	int id() default -1; //如果找不到,返回-1
	String[] schools() default {"清华大学"};
}

反射

静态Vs动态语言

动态语言:运行时可以改变结构的语言,Python、Javascript、C#

静态语言:结构不可变的语言,Java(反射机制——准动态语言)、C、C++

Reflection

借助反射API获取任何类的内部信息,并操作任何属性和方法。

反射是什么呢?当我们的程序在运行时,需要动态的加载一些类这些类可能之前用不到所以不用加载到jvm,而是在运行时根据需要才加载》

获取类的几种方式

注意:一个加载的类在JVM种只有一个Class实例

//通过反射获取Class对象
public class Test01 {
	public static void main(String[] args) throws ClassNotFoundException {
		//forname
		Class c1=Class.forName("com.bigdata.reflection.User");
		System.out.println(c1.hashCode());  //User
		
		//对象
		User user = new Student();
		Class c2=user.getClass(); 
		System.out.println(c2.hashCode());  //Student
		
		//类
		Class c3=Student.class;
		System.out.println(c3.hashCode());// 一个对象在JVM中只有一个Class
		
		//基本类型的包装类
		Class c4=Integer.TYPE;
		System.out.println(c4);  //int
		
		//父类
		Class c5=c2.getSuperclass();
		System.out.println(c5);   //com.bigdata.reflection.User
	}
}

类加载内存分析(加载、链接、初始化)

  • 在类加载时,Class对象就已经生成。

  • 主动引用/反射,都会产生类的初始化,如果父类的静态资源没有被初始化,则先初始化父类的静态资源。

类加载器

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

  • 引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用C++代码来实现的,并不继承自 java.lang.ClassLoaderrt.jar
  • 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。jre/lib/ext下的jar
  • 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。java-classpath

双亲委派:保证安全性,多重检测,如果在rt包中已经存在java.lang.String,手写的String包则失效。

获取类的运行时结构

运行时可以通过反射机制,获取类的代码信息:属性、方法、构造器

public class Test04 {
	public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, NoSuchMethodException {
		Class clz=Class.forName("com.bigdata.reflection.User");
		System.out.println(clz.getName());
		System.out.println(clz.getSimpleName());
		
		//获得属性
		Field[] fields=clz.getDeclaredFields();
		for(Field field:fields) {
			System.out.println(field);
		}
		Field field=clz.getDeclaredField("name");  //declared可查询private属性
		System.out.println(field);
		
		//获得方法
		Method[] methods=clz.getDeclaredMethods();  //获得本类的所有方法
		for(Method method:methods) {
			System.out.println("declared:"+method);
		}
		methods=clz.getMethods();    //本类及父类的public方法
		for(Method method:methods) {
			System.out.println("method:"+method);
		}
		Method getName=clz.getMethod("getName", null);
		Method setName=clz.getMethod("setName", String.class);  //由于java有重载,需要参数类型来确定方法
		System.out.println(getName);
		System.out.println(setName);
		
		//获得指定构造器
		Constructor[] constructors=clz.getConstructors();
		for(Constructor constructor:constructors)
			System.out.println(constructor);
		Constructor constructor=clz.getConstructor(String.class,int.class);
		System.out.println(constructor);
	}
}

获取了这些属性有什么作用?通过反射来创建一个对象

//构建一个对象
User user=(User)clz.newInstance();  //本质:调用类的无参构造器
System.out.println(user);

//通过构造器来创建对象
Constructor constructor=clz.getConstructor(String.class,int.class);
User user2=(User) constructor.newInstance("周逸",18);
System.out.println(user2);

//调用方法
User user3=(User)clz.newInstance();
Method setName=clz.getMethod("setName", String.class);
setName.invoke(user3, "carmine");
Method getName=clz.getMethod("getName", null);
System.out.println(getName.invoke(user3, null));

//操作属性
User user4=(User)clz.newInstance();
Field name=clz.getDeclaredField("name");
name.setAccessible(true);//关闭权限检测来访问私有属性(不安全!)
name.set(user4, "carmineYi");
System.out.println(user4.getName());

setAccessible关闭检测

反射方式效率较低,关闭检测可以提升性能效率

反射操作泛型

Type[] genericParameterTypes=method.getGenericParameterTypes() //获取泛型参数
Type[] genericReturnTypes=method.getGenericReturnType() //获取泛型返回值

总结

综合反射和注解打代码,也是Spring框架的底层原理,利用反射操作注解:

public class Test05 {

	public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException {
		Class clz=Class.forName("com.bigdata.reflection.Student2");
		//获得注解
		Annotation[] annotations=clz.getAnnotations();
		for(Annotation annotation:annotations)
			System.out.println(annotation);
		
		//获得注解的value值
		TableCamine tablecarmine=(TableCamine) clz.getAnnotation(TableCamine.class);
		String value=tablecarmine.value();
		System.out.println(value);
		
		//获得name属性的指定注解
		Field field=clz.getDeclaredField("name");
		FieldCamine fieldcarmine=field.getAnnotation(FieldCamine.class);
		System.out.println(fieldcarmine.columnName());
		System.out.println(fieldcarmine.type());
		System.out.println(fieldcarmine.length());

	}
}

//类名注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableCamine{
	String value();
}

//模拟数据库属性
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldCamine{
	String columnName();  //列名
	String type();  //类型
	int length();   //长度
}

//模拟数据库建表
@TableCamine("db_student")
class Student2
{
	@FieldCamine(columnName="db_id",type="int",length=10)
	private int id;
	@FieldCamine(columnName="db_age",type="int",length=10)
	private int age;
	@FieldCamine(columnName="db_name",type="varchar	",length=3)
	private String name;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "Student [id=" + id + ", age=" + age + ", name=" + name + "]";
	}
	
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JAVASE 注解与反射 的相关文章

随机推荐

  • AIX使用RPM

    RPM安装 rpm ivh rpmname rpm 下载地址 aix的rpm下载地址一 AIX Toolbox for Linux Applications Downloads alpha aix的rpm下载地址二 找到自己版本的aix文件
  • 一个局外SEO人看360搜索

    最近国内搜索行业最大的事莫过于360搜索的推出 不少人在微博和博客留言里问我对360搜索有什么评论 作为一个以网络为生却无法感性体验国内互联网氛围的SEO 我其实对几个搜索引擎的厮杀 又有谁加入战局 各自使用了什么招数之类的事情不太关心 谁
  • 阿里巴巴 CTO 程立:开源是基础软件的源头!

    开源盛世下 无数企业拥抱开源技术并迅速向数字化转型 阿里巴巴便是国内走在前列的大型企业之一 7 月 28 日 阿里巴巴集团 CTO 程立在 2022 开放原子全球开源峰会上带来了 共建共享数字世界的根 主题演讲 从全球开源发展史下看阿里巴巴
  • 各种颜色代码

    网页颜色代码对照表 FFFFFF FFFFF0 FFFFE0 FFFF00 FFFAFA FFFAF0 FFFACD FFF8DC FFF68F FFF5EE FFF0F5 FFEFDB FFEFD5 FFEC8B FFEBCD FFE7B
  • I2C实验

    参考 I2C 总线协议详解 作者 一只青木呀 发布时间 2020 09 21 11 41 25 网址 https blog csdn net weixin 45309916 article details 108705297 目录 I2C
  • java构造器为什么不能被继承

    网上的解释都不太令人信服 所以写下这篇小文 构造器为什么不能被继承 这是由于构造器的特殊规定决定的 构造器的定义和普通方法相比 首先构造器不需要返回类型 其次构造器和类名相同 如果构造器可以被子类继承 其具备的地位有两种可能 一是作为子类的
  • Spring MVC 拦截器

    如何实现session共享问题 1 session都在内存里面存储的 只要有session对象我都分发出去 让其他应用都可以拿到 同步 缺点 只要有session对象都要广播出去 而且用户应用比较多了就会导致服务压力大 2 使用token
  • 【面试宝典】美团二面:Redis与MySQL双写一致性如何保证?

    前言 四月份的时候 有位好朋友去美团面试 他说 被问到Redis与MySQL双写一致性如何保证 这道题其实就是在问缓存和数据库在双写场景下 一致性是如何保证的 本文将跟大家一起来探讨如何回答这个问题 谈谈一致性 一致性就是数据保持一致 在分
  • VSCODE显示服务器输出的图片

    1 使用matplotlib库 import matplotlib pyplot as plt plt 用于显示图片 import matplotlib image as mpimg mpimg 用于读取图片 import numpy as
  • 一个 8 年 PhpStorm 使用者的配置分享

    我使用 PhpStorm 很长时间了 差不多 8 年 更准确地说是从 2012 年开始 那时候是第三版 那段时间发生了许多事 也发生了很大的改变 当然 你每天都会学到很多 这篇文章是我在 PhpStorm 的 8 年经验总结 我的这些最佳设
  • 交互式前景提取使用GrabCut算法(opencv_python学习)

    交互式前景提取使用GrabCut算法 cv grabCut 是 OpenCV 中用于执行 GrabCut 算法的函数 该函数可以将输入图像分割为前景和背景 下面是 cv grabCut 函数的基本语法 cv grabCut img mask
  • 模拟器显示图片,而真机不显示

    记录一个小bug 图片能在模拟器显示 但是在真机上显示不了 原因 图片的url有问题 真机有安全性限制 导致无法展示 1 首先 拿到图片的地址 将其拿到浏览器测试 可以看到浏览器显示的不安全 http www xxxxx com 9000
  • git最简单回滚并推送到远程

    1 代码回退 首先你要用git reflog查看你要回到的那个版本 然后用 git reset hard HEAD 回退到上个版本 git reset hard commit id 退到 进到 指定commit id 来把你的本地代码回到你
  • UDP与TCP的对比

    1 报头 1 TCP协议报头 TCP指传输控制协议 其报头格式如下 1 源 目的端口号 表示数据是从哪个进程来 到哪个进程去 2 32位序号 32确认号 用于可靠传输 3 4位TCP报头长度 表示该TCP头部有多少个32位bit 有多少个4
  • 忽视日志吃大亏,手把手教你玩转 SpringBoot 日志!

    一 日志重要吗 程序中的日志重要吗 在回答这个问题前 笔者先说个事例 笔者印象尤深的就是去年某个同事 收到了客户反馈的紧急bug 尽管申请到了日志文件 但因为很多关键步骤没有打印日志 导致排查进度很慢 数个小时都没能排查到问题 也无法给出解
  • Flutter 3.3 正式发布

    Flutter 3 是我们正式为全平台提供支持的一个重量级里程碑 距离它的发布仅过去了三个月 今天让我们有请 Flutter 3 3 正式版 近三个月我们并没有放慢更新迭代的速度 自 Flutter 3 发布以来 我们已经为 Flutter
  • 如何不借助新的变量交换两个变量的值

    在很多问题的解决中都会遇到 需要交换两个变量的值 这种情况 下面的方法 三变量法 想必是大家常用的吧 include
  • 【数据结构与算法】内排序算法全解析(附C语言代码)

    导览 0 预备知识 0 1 排序的概念 0 2 排序的稳定性 0 3 内排序与外排序 0 4 排序算法的性能 0 5 常见排序算法的性能 1 比较排序 1 1 插入排序 1 1 1 直接插入排序 1 1 2 折半插入排序 1 1 3 希尔排
  • Redis缓存与数据库的双写一致性(先更新数据库再删除缓存并结合RabbitMQ消息队列)

    实现双写一致性 通常会选择先更新数据库 然后再删除缓存的策略 并结合 RabbitMQ 的消息队列机制 主要有以下几个原因 保证数据一致性 在应用程序中 同一个数据可能存在于多个缓存服务节点中 这样会对数据的一致性造成很大影响 为了避免出现
  • JAVASE 注解与反射

    注解与反射都是框架的基础 注解 注解的格式 注释名 参数名 参数值 可以使用在 package class method field上 作为辅助信息 内置注解 Override 重写方法 会检测方法名称 Deprecated 表明该方法已过