Java核心基础--注解&反射
一、注解
注解(Annotation) 是Java5引入的一种代码辅助工具,其核心作用是对类、方法、变量、参数和包进行标注,然后通过反射来访问这些标注信息,并在运行时改变被注解对象的行为。
1. 基本概念
Java注解又名Java标注,是Java5开始支持加入源代码的特殊语法元数据,其附加的信息根据需要可以保存到class文件中,甚至运行期加载到Class对象中;其与普通的注释不同在于:普通的注释在编译后的class文件中是不存在的。
Java注解其本身没有任何功能,不会执行,只是起到标注的作用,所以往往需要配合反射去解析并处理被注解标记的代码。
2. JDK内置标准注解
Java SE5内置了三种定义在java.lang中的注解
- @Override: 一个标记注解,表示被它标注的方法将覆盖父类的方法。如果写代码过程中拼写错误或方法签名对不上父类的方法,编译器就会发出错误提示。
- @Deprecated: 一个标记注解,用于修饰一个方法。它表示此方法不推荐使用。无论是继承、覆盖或直接使用此方法,编译器都会给出警告
- @SuppressWarnings: 关闭不当的编译器警告信息,其有一些参数用于表示特定的警告:
- deprecation: 不要给出“不赞成使用的类或方法的警告”;
- unchecked: 不要给出“类型转换时警告”;
- fallthrough: 不要给出”switch语句块没有break的警告”;
- path: 不要给出“不存在的路径”的警告;
- serial: 不要给出“可序列化类缺少serialVersionUID”的警告;
- finally: 不要给出“finally语句块不能正常完成”的警告;
- all: 不要给出以上所有情况的警告
3. 自定义注解
定义注解格式:
public @interface 注解名 {定义体}
看一个定义好的注解:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Column {
String value() default "";
}
上面这个Column注解主要由三部分组成:
3.1. @interface
@interface自定义注解时,自动继承了 java.lang.annotation.Annotation 接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。
3.2. 元注解
Java允许我们自定义注解,并为我们提供四种元注解来负责注解我们自定义的注解。四种元注解分布时:
-
@Retention(RetentionPolicy.RUNTIME):元注解,定义注解的生命周期
- RetentionPolicy.SOURCE:在源文件中有效
- RetentionPolicy.CLASS:在class文件中有效
- RetentionPolicy.RUNTIME: 在运行时有效,也是我们最常用的
-
@Documented:文档注解, 表示自定义注解可以被javadoc之类的工具文档化
-
@Inherited:是否让子类继承该注解
-
@Target({ElementType.TYPE, ElementType.FIELD}):元注解,定义注解的修饰范围,可以设置多个
- TYPE:表示可以用来修饰类、接口、注解类型或枚举类型
- PACKAGE:表示可用来修饰包
- PARAMETER:表示可用来修饰参数
- ANNOTATION_TYPE:可用来修饰注解类型
- METHOD:可用来修饰方法
- FIELD:可用来修饰属性(包括枚举常量)
- CONSTRUCTOR:可用来修饰构造器
- LOCAL_VARIABLE:可用来修饰局部变量
3.3. 注解参数
@interface用来声明一个注解,其中的每一个方法实际上是声明了一个注解的参数。方法的名称就是参数的名称,返回值类型就是参数的类型,可以通过default来声明参数的默认值。
- 注解参数的可支持数据类型:
- 所有基本数据类型(int,float,boolean,byte,double,char,long,short)
- String类型
- Class类型
- enum类型
- Annotation类型
- 以上所有类型的数组
4. 注解使用
@Table(value = "person")
public class Person {
@Column
private int id;
@Column("name")
private String name;
}
二、反射
反射(Reflection):指的是程序在运行期间借助反射 API 取得任何类的内部信息,并通过这些内部信息去操作对应对象的内部属性和方法。
任何一个类,在第一次使用时,就会被 JVM 加载到堆内存的方法区中。JVM 加载类成功后,就会在方法区中产生一个对应的 Class 对象(一个类只要一个 Class 对象),这个 Class 对象包含被加载类的全部结构信息。
1. 获取 Class 对象的常用方式
1.1. 类的 class 属性
Class<Person> cl1 = Person.class;
1.2. Object 对象 的 getClass() 方法
Person person= new Person();
Class<Person> cl2 = (Class<Person>) person.getClass();
1.3. 通过 Class 类的 forName() 方法(最常用)
try {
Class cl3 = Class.forName("com.dkfrog.sqlgenerator.model.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
1.4. 通过 ClassLoader 类(不常用)
ClassLoader cl = Person.class.getClassLoader();
try {
Class cl4 = cl.loadClass("com.dkfrog.sqlgenerator.model.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
2. 反射常用的API
方法名 | 返回值 | 参数描述 |
---|---|---|
clz.getDeclaredFields() | 获取当前类中所有的属性 | |
setAccessible(boolean) | 设置当前属性可见 | |
getMethods | 获取类所有方法 | |
invoke(obj) | 通过反射执行方法 | 类的元信息 |
getAnnotation(class) | 获取类注解 | 需要获取注解的Class |
Class 对象中大多数 get 方法有 Declared 和无 Declared,他们的区别是:
- 无 Declared:只能获取到 public 修饰的,包括当前类和所有父类。
- 有 Declared:获取到当前类所有的(含有 private),但不包括其父类。
3. 实例
public class ReflectionDemo {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Person person = new Person("dkfrog", 20);
// 获取类的元信息
Class<? extends Person> aClass = person.getClass();
// Spring XML配置Bean常用
// Class<?> aClass1 = Class.forName("com.dkfrog.model.Person");
// 实例2:通过反射获取类名、包名
// 包名+类名: com.dkfrog.model.Person
System.out.println("----------- 实例2:通过反射获取类名、包名");
System.out.println(aClass.getName());
// 类名: Person
System.out.println(aClass.getSimpleName());
// 实例3:获取类属性
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
// 实例4:获取类属性具体值
System.out.println("----------- 实例4:获取类属性具体值");
for (Field declaredField : declaredFields) {
// 设置私有属性可见
declaredField.setAccessible(true);
// 通过get获取值,需传入类
System.out.println(declaredField.get(person));
}
// 实例4的另一种写法
// 相当于在反射中的实例化
Object p = aClass.newInstance();
for (Field declaredField : declaredFields) {
declaredField.setAccessible(true);
if ("name".equals(declaredField.getName())) {
declaredField.set(p, "ramos");
} else {
declaredField.set(p, 19);
}
System.out.println(declaredField.get(p));
}
// 实例5:反射获取当前类的方法
System.out.println("----------- 实例5:反射获取当前类的方法 ");
Method[] methods = aClass.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
// 反射执行方法
Method getString = aClass.getMethod("getString");
Object invoke = getString.invoke(p);
System.out.println(invoke);
// 实例6:获得注解
System.out.println("----------- 实例6:获得注解 ");
Study annotation = aClass.getAnnotation(Study.class);
String[] mores = annotation.mores();
for (String more : mores) {
System.out.println(more);
}
String name = annotation.name();
System.out.println("name: " + name );
// 从方法上获取注解
for (Method method : methods) {
Study annotation1 = method.getAnnotation(Study.class);
if (annotation1 == null) {
continue;
}
System.out.println(annotation1.mores());
}
// 从属性上获取注解, 类似于从方法上获取注解
}
}
三、手写简单查询SQL生成器
1. 定义描述用户表的注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
2. 定义描述用户属性的注解:
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Column {
String value() default "";
}
3. 定义映射Bean类User:
@Table(value = "user")
public class User {
@Column
private int id;
@Column("username")
private String username;
@Column("password")
private String password;
public User() {
}
public User(int id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
4. 定义SQL生成器:
import com.dkfrog.sqlgenerator.annotation.Column;
import com.dkfrog.sqlgenerator.annotation.Table;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author ramos_javax@163.com
* @date 2020-05-12 21:02
* @since
*/
public class GenerateSqlUtil {
public String query(Object obj) throws Exception {
StringBuilder str = new StringBuilder();
// 1.获取一个类class
Class c = obj.getClass();
// 2.获取Table的名字
boolean exists = c.isAnnotationPresent(Table.class);
if (!exists) {
return null;
}
Table t = (Table) c.getAnnotation(Table.class);
String tableName = t.value();
str.append("select * from ").append(tableName).append(" where 1=1");
// 3.遍历所有的 字段
Field fArray[] = c.getDeclaredFields();
for (Field field : fArray) {
// 4.处理每个字段对应的sql
// 4.1取到字段名
// 判断是否包含Column类型的注解
boolean fExists = field.isAnnotationPresent(Column.class);
if (!fExists) {
continue;
}
Column column = field.getAnnotation(Column.class);
String columnName = column.value();
// 4.2取到字段的值
String fieldName = field.getName();
// 获取相应字段的getXXX()方法
String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
Object fieldValue=null;
try {
Method getMethod = c.getMethod(getMethodName);
fieldValue = getMethod.invoke(obj);
} catch (Exception e) {
e.printStackTrace();
}
//4.3拼接Sql
if (fieldValue==null||fieldValue instanceof Integer &&(Integer)fieldValue==0) {
continue;
}
str.append(" and ").append(fieldName);
if (fieldValue instanceof String) {
if (((String) fieldValue).contains(",")) {
String[] values=((String) fieldValue).split(",");
str.append(" in (");
for (String s : values) {
str.append("'").append(s).append("'").append(",");
}
str.deleteCharAt(str.length()-1);
str.append(")");
}else{
str.append("=").append("'").append(fieldValue).append("' ");
}
}else {
str.append("=").append(fieldValue);
}
}
return str.toString();
}
}
5. 测试类
import com.dkfrog.sqlgenerator.model.User;
import com.dkfrog.sqlgenerator.util.GenerateSqlUtil;
public class Main {
public static void main(String[] args) throws Exception {
GenerateSqlUtil util = new GenerateSqlUtil();
// 查询id
User user1 = new User();
user1.setId(100);
// 模糊查询用户名
User user2 = new User();
user2.setUsername("dkfrog");
user2.setPassword("******");
// 查询邮箱有任意一个的用户
User user3 = new User();
user3.setEmail("test1@163.com,test2@qq.com");
System.out.println(util.query(user1));
System.out.println(util.query(user2));
System.out.println(util.query(user3));
}
}
输出结果:
select * from user where 1=1 and id=100
select * from user where 1=1 and username='dkfrog' and password='******'
select * from user where 1=1 and email in ('test1@163.com','test2@qq.com')