文章目录
注解
注解的概述
1.Java提供了一种源程序中元素和任何信息或者元数据相关联的一种方法或者渠道
2.源程序中元素: Java文件 构造方法、成员变量、成员方法…
3.信息: 就是一些配置数据 【也可以理解为是注释】
4.元数据: 就是元注解,对注解进行注解
5.关联: 元素和注释绑定成为注解
简单来说: 就是相当于一些类似于注释的信息 和 源程序中的元素 绑定的一种技术,这种技术叫做注解这种手段可以通过反射获取到元素对象,在获取到这个元素上面绑定的注解,通过绑定的注解获取里面配置信息,从而达到能够在程序运行阶段通过这些信息动态地改变程序的运行逻辑
注解的分类
按照运行的机制分类
1.源码注解: 注解只在源码中有效,当编译生成字节码文件的时候,注解就不存在了,其实就是注释 APT
2.编译时注解: 注解在源码和编译时期有效,在jvm运行的时候就不存在了
3.运行时注解: 注解在源码和编译有效,同时在程序的执行过程也存在 (反射获取运行时注解,从而获取到注解的配置信息,动态改变程序的逻辑)
4.元注解: 对注解进行注解的注解
按照来源分类:
1.JDK中自带的注解: @Override,@Deprecated
2.第三方注解: @Table,@Column,@Component,@Service,@Bean
3.自定义注解: 自己定义注解自己使用在自己的代码中
4.元注解: 对注解进行注解的注解
Java中常见的注解
1.@Override: 表示必须对方法重写
2.@Deprecated : 表示方法过时
3.@SuppresWarning : 表示忽略警告
4.@FunctionalInterface : 表示函数式接口
5.函数式接口: 如果一个接口只有一个抽象方法,那么该接口就是函数式接口,此接口再使用的时候可以使用Lambda表达式改进
public class Student{
private String name;
private String sex;
private Integer age;
private Integer id;
public Student() {
}
public Student(String name, String sex, Integer age, Integer id) {
this.name = name;
this.sex = sex;
this.age = age;
this.id = id;
//表示必须对方法重写
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((age == null) ? 0 : age.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((sex == null) ? 0 : sex.hashCode());
return result;
}
//表示必须对方法重写
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age == null) {
if (other.age != null)
return false;
} else if (!age.equals(other.age))
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (sex == null) {
if (other.sex != null)
return false;
} else if (!sex.equals(other.sex))
return false;
return true;
}
//表示方法过时在方法名的上会有一条横线
@Deprecated
public String toString() {
return "Student [name=" + name + ", sex=" + sex + ", age=" + age + ", id=" + id + "]";
}
常见的元注解
@Retention
源码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
@interface:java用 @interface 定义一个注解 ,一个注解是一个类
RetentionPolicy:是一个枚举类型有三个属性分别定义注解的生命周期,SOURCE(源码时),CLASS(编译时),RUNTIME(运行时)。
@Target
源码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
@Target:用于设定注解使用范围
ElementType:也是一个枚举类型 常用的属性有:
METHOD 可用于方法上
TYPE 可用于类或者接口上
ANNOTATION_TYPE 可用于注解类型上(被@interface修饰的类型)
CONSTRUCTOR 可用于构造方法上
FIELD 可用于域上
LOCAL_VARIABLE 可用于局部变量上
PACKAGE 用于记录java文件的package信息
PARAMETER 可用于参数上
@Documented:注解表明这个注解应该被 javadoc工具记录. 默认情况下,javadoc是不包括注解的. 但如果声明注解时指定了 @Documented,则它会被 javadoc 之类的工具处理, 所以注解类型信息也会被包括在生成的文档中,是一个标记注解,没有成员。
自定义注解并且使用
四种使用注解的方式
格式一: @注解的名称(属性名1=属性值1,属性名2=属性值2,属性名3=属性值3,…属性名n=属性值n)
注意: 如果有两个以上的属性,有多少个属性就必须给多少个属性赋值
格式二: 适用于注解只有一个成员
@注解的名称(属性值)
格式三: 适用于成员是数组的情况
@注解的名称({属性值1,属性值2,属性值3,…属性值n})
格式四: 使用标记注解
@注解的名称
public class AnnotationDemo01 {
public static void main(String[] args) {
}
}
// 希望当前注解MyAnnotation可以用在类,接口,构造方法,方法上
@Target({ ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD })
// 希望当前注解MyAnnotation可以在运行时有效
@Retention(RetentionPolicy.RUNTIME)
// 希望当前注解可以在子类中生效
@Inherited
// 希望当前注解可以在API文档中生成
@Documented
@interface MyAnnotation {
String desc();
int val() default 0;
String[] hobbys();
}
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@interface Column {
String value();
}
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@interface PrimaryKey {
boolean isAutoIncrease();
String name();
}
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface Fu {
}
@Fu
class Person {
}
@MyAnnotation(desc = "Student Class", val = 18, hobbys = { "篮球", "足球", "乒乓球" })
class Student extends Person {
@Column("sid")
@PrimaryKey(isAutoIncrease = true, name = "sid")
private String id;
@Column("sname")
private String name;
@Column("sage")
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
@MyAnnotation(desc = "1111", val = 18, hobbys = { "篮球", "足球", "乒乓球" })
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
通过反射解析注解
1.使用反射解析类或者接口或者枚举上面的注解,必须先要获取到字节码文件对象
2.解析除了类上的注解外,必须先获取到元素对象
3.获取注解之前一定要做存在性判断,因为如果是源码或者编译时注解,那么反射是在运行时获取,注解在运行时已经不存在就获取不到
常用的通过反射获取注解元素的相关方法
1. A getAnnotation(Class annotationClass)
2.Annotation[] getAnnotations()
3.Annotation[] getDeclaredAnnotations()
4.boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
通过反射获取注解写出的JavaBean和Map集合之间转换的工具类
public class MapBeanUtils {
private MapBeanUtils() {
}
/*
* 将任意的Map集合转换成对应Bean
*/
public static <T> T mapToBean(Map<String, Object> map, Class<T> clazz) throws Exception {
// 通过反射创建Bean对象
T t = clazz.newInstance();
// 通过反射获取到每一个成员变量对象
Field[] fields = clazz.getDeclaredFields();
// 遍历每一个成员
for (Field field : fields) {
if (field.isAnnotationPresent(Column.class)) {
Column column = field.getAnnotation(Column.class);
if (column != null) {
String key = column.value();
Object o = map.get(key);
if (o != null) {
String fieldName = field.getName();
// 获取set方法
String setMethodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
Method setMethod = clazz.getMethod(setMethodName, o.getClass());
setMethod.invoke(t, o);
}
}
}
}
return t;
}
/*
* 将Bean转换成对应的Map集合
*/
public static <T> Map<String, Object> beanToMap(T t) throws Exception {
// 创建一个空的容器来装载Bean的数据
Map<String, Object> map = new HashMap<>();
// 获取bean对象中的字节码文件对象
Class<?> c = t.getClass();
// 获取到所有成员量对象
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
// 获取到每一个成员变量对象
if (field.isAnnotationPresent(Column.class)) {
Column columnAnnotation = field.getAnnotation(Column.class);
if (columnAnnotation != null) {
String key = columnAnnotation.value();
// 通过注解绑定的属性名,去bean中获取值
String fieldName = field.getName();
// 获取到get方法
String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1);
Method getMethod = c.getMethod(getMethodName);
Object getValue = getMethod.invoke(t);
map.put(key, getValue);
}
}
}
return map;
}
}
//学生类
public class Student {
@Column("name")
private String name;
@Column("age")
private Integer age;
@Column("address")
private String address;
public Student() {
super();
}
public Student(String name, Integer age, String address) {
super();
this.name = name;
this.age = age;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", address=" + address + "]";
}
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Column {
String value();
}
//测试类
public class Test {
public static void main(String[] args) throws Exception {
HashMap<String, Object> map = new HashMap<>();
map.put("name", "张三");
map.put("age", 18);
map.put("address", "北京");
Student s = MapBeanUtils.mapToBean(map, Student.class);
System.out.println(s);
Student ss = new Student("张三", 20, "北京西路");
Map<String, Object> map3 = MapBeanUtils.beanToMap(ss);
System.out.println(map3);
}
}
单元测试
针对某个方法进行独立测试,提高了程序的可维护性,自定义单元测试注解,来测试我们写的方法
public class JunitTest {
public static void main(String[] args) {
// 告诉主方法你要测试哪个类的哪个方法
try {
TestHandler.process("sz.lz.junitdemo.MyJunitTest");
} catch (Exception e) {
e.printStackTrace();
}
}
}
/*
* 该类是一个注解提取类: 能够针对某个类中的Testable注解进行提取
*
*/
class TestHandler {
public static void process(String className) throws Exception {
int success = 0;
int faild = 0;
Class<?> c = Class.forName(className);
Object instance = c.newInstance();
Method[] methods = c.getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(Testable.class)) {
try {
method.invoke(instance);
success ++;
} catch (Exception e) {
System.out.println("方法" + method.getName() + "运行异常:" + e);
faild ++;
}
}
}
System.out.println("一共运行了" + (success + faild) + "个方法,"
+ success + "个方法成功了," + faild + "个方法失败了");
}
}
public class Expression {
public int add(int a, int b) {
return a + b;
}
public int substract(int a, int b) {
return a - b;
}
public int multiply(int a, int b) {
return a * b;
}
public int divide(int a, int b) {
if (b == 0) {
throw new ArithmeticException("除数不能为0");
}
return a / b;
}
}
public class MyJunitTest {
@Testable
public void testAdd() {
Expression expression = new Expression();
int add = expression.add(10, 20);
System.out.println(add);
}
// @Testable
public void testSubstract() {
Expression expression = new Expression();
expression.substract(10, 20);
}
// @Testable
public void testMutiply() {
Expression expression = new Expression();
expression.multiply(10, 20);
}
// @Testable
public void testDivide() {
Expression expression = new Expression();
expression.divide(10, 0);
}
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//定义注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Testable {
}
当然除了自定义之外还可以利用框架提供给我们的单元测试进行测试。