一.注解Annotation
1.1 基本介绍
- 注解Annotation是JDK5.0开始引入的技术
- 作用:可以对程序进行解释,且能被其他程序识别读取
- 格式:注解以“@注解名”,并可以添加一些参数,例如:@SuppressWarnings(value = “unchecked”),@Override
- 作用域:可以附加再package,class,method,field等上面。我们可以通过反射机制实现对这些元数据的访问。
1.2 内置注解
- @Override:表示一个方法声明打算重写超类中的另一个方法声明
- @Deprecated:表示不鼓励程序员使用这样的元素
- @SuppressWarnings:用来抑制编译时的警告信息,需要添加参数
- @SuppressWarnings(“all”)
- @SuppressWarnings(“unchecked”)
- @SuppressWarnings(value={“unchecked”,“deprecation”})
1.3 元注解
元注解用来对定义的注解进行说明
- @Target:描述注解使用范围,可以用在什么范围,例如:方法上或类上
- @Retention:用于描述注解的生命周期(SOURCE<CLASS<RUNTIME)
- @Document:说明该注解将被包含在Javadoc中
- @Inherited:说明子类可以继承父类的注解
1.4 自定义注解
- 使用@interface声明注解,格式:public @interface 注解名 {定义内容}
- 其中的每一个方法实际上是声明了一个配置参数
- 方法的名称就是参数的名称
- 返回值类型就是参数的类型(返回值只能是基本类型,Class,String,enum)
- 可以通过 default 来声明参数的默认值
- 如果只有一个参数成员,一般参数名为 value
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0 作为默认值
二.反射Reflection
2.1 基本介绍
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
2.2 Class类
- Class 本身也是一个类
- Class 对象只能由系统建立对象
- 一个加载的类在 JVM 中只会有一个 Class 实例
- 一个 Class 对象对应的是一个加载到 JVM 中的一个 .class 文件
- 每个类的实例都会记得自己是由哪个 Class 实例所生成
- 通过 Class 可以完整地得到一个类中的所有被加载的结构
- Class 类是 Reflection 的根源,针对任何你想动态加载、运行的类、唯有先获得响应的 Class 对象
哪些类型可以有Class类:
class外部类,成员,局部内部类,匿名内部类;interface接口;[]数组;enum枚举;annotation注解;primitive type基本数据类型;void
2.3 获取Class类的几种方式
(1)已知具体的类,通过类的class属性获取,该方法最安全可靠,程序性能最高。
Class clazz = Person.class;
(2)已知某个类的实例,调用getClass()方法获取Class对象
Class clazz = person.getClass();
(3)已知一个类的全类名,且该类在类的路径下,可通过forName()获取,可能抛出ClassNotFoundException
Class clazz = Class.forNmae("com.example.User2");
(4)内置基本数据类型可以直接用类名.Type
Integer.Type;
(5)利用ClassLoader
2.4 获取类的运行时结构
首先设定一个User2类
(1)获取类的属性
package com.example;
import java.lang.reflect.Field;
public class TextReflection {
public static void main(String[] args) throws ClassNotFoundException {
//通过反射获取类的Class对象
Class c1 = Class.forName("com.example.User2");
//获取类的名字
System.out.println(c1.getName()); //获得包名+类名
System.out.println(c1.getSimpleName()); //获得类名
System.out.println("----------------------------------");
//获取类的属性
Field[] fields = c1.getFields(); //getFields只能找到public属性
for (Field field : fields) {
System.out.println(field);
}
System.out.println("==================================");
fields = c1.getDeclaredFields(); //getDeclaredFields能找到全部属性
for (Field field : fields) {
System.out.println(field);
}
//获取指定属性的值
Field name = c1.getDeclaredField("name");
System.out.println(name);
}
}
(2)获取类的方法
package com.example;
import java.lang.reflect.Field;
public class TextReflection {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
//通过反射获取类的Class对象
Class c1 = Class.forName("com.example.User2");
//获取类的方法
Method[] methods = c1.getMethods(); //获得本类和父类全部public方法
for (Method method : methods) {
System.out.println("原本"+method);
}
methods = c1.getDeclaredMethods(); //获得本类所有方法
for (Method method : methods) {
System.out.println("Declared:"+method);
}
//获取指定方法
Method getName = c1.getMethod("getName", null);
Method setName = c1.getMethod("setName", String.class);
System.out.println(getName);
System.out.println(setName);
}
}
(3)获取类的构造器
package com.example;
import java.lang.reflect.Field;
public class TextReflection {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
//通过反射获取类的Class对象
Class c1 = Class.forName("com.example.User2");
//获取类的构造器
Constructor[] constructors = c1.getConstructors(); //获取public构造方法
for (Constructor constructor : constructors) {
System.out.println("原:"+constructor);
}
constructors = c1.getDeclaredConstructors(); //获取全部构造方法
for (Constructor constructor : constructors) {
System.out.println("后:"+constructor);
}
//获取指定构造器
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
System.out.println("指定"+declaredConstructor);
}
}
2.5 动态创建对象执行方法
动态创建对象只能通过反射
(1)动态创建对象
package com.example;
public class TextReflection02 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//获取Class对象
Class c1 = Class.forName("com.example.User2");
//构造一个对象
User2 user2= (User2) c1.newInstance();//本质是调用了无参构造函数
System.out.println(user2);
}
}
此处修改了User2中无参构造函数为public,否注会报错
同时java9之后不推荐使用newInstance(),而是clazz.getDeclaredConstructor().newInstance()
package com.example;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class TextReflection02 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//获取Class对象
Class c1 = Class.forName("com.example.User2");
//通过构造器创建对象
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
User2 user = (User2) constructor.newInstance("bandit", 001, 18);
System.out.println(user);
}
}
(2)通过反射调用方法
package com.example;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TextReflection02 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//获取Class对象
Class c1 = Class.forName("com.example.User2");
User2 user2 = (User2) c1.newInstance();
//通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);
//invoke:激活
//(对象,“方法的值”)
setName.invoke(user2,"bandit");
System.out.println(user2.getName());
}
}
(3)通过反射操作属性
package com.example;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TextReflection02 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//获取Class对象
Class c1 = Class.forName("com.example.User2");
User2 user2 = (User2) c1.newInstance();
Field name = c1.getDeclaredField("name");
name.set(user2,"BANDIT");
System.out.println(user2.getName());
}
}
需要注意的是,上面会报错,因为name属性为private,所以需要关闭权限检测setAccessible(true);
关闭权限检测后:
package com.example;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TextReflection02 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//获取Class对象
Class c1 = Class.forName("com.example.User2");
User2 user2 = (User2) c1.newInstance();
Field name = c1.getDeclaredField("name");
name.setAccessible(true); //关闭权限检测
name.set(user2,"BANDIT");
System.out.println(user2.getName());
}
}
成功输出
2.6 三种调用方法性能分析
package com.example;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class text {
//普通方法调用
public static void test01(){
User2 user2 = new User2();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
user2.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方式10亿次:"+(endTime-startTime)+"ms");
}
//反射方法调用
public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User2 user2 = new User2();
Class c1 = user2.getClass();
Method getName = c1.getDeclaredMethod("getName", null);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user2,null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射方式10亿次:"+(endTime-startTime)+"ms");
}
//关闭权限检测反射
public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User2 user2 = new User2();
Class c1 = user2.getClass();
Method getName = c1.getDeclaredMethod("getName", null);
getName.setAccessible(true);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user2,null);
}
long endTime = System.currentTimeMillis();
System.out.println("关闭检测后10亿次:"+(endTime-startTime)+"ms");
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
test01();
test02();
test03();
}
}
setAccessible(true);可以提高反射效率
三.通过反射获取注解
首先定义两个注解
//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableBandit{
String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldBandit{
String columnName();
String type();
int length();
}
将注解应用到Student2类上
@TableBandit("db_Student")
class Student2{
@FieldBandit(columnName = "db_id",type = "int",length = 10)
private int id;
@FieldBandit(columnName = "db_age",type = "int",length = 10)
private int age;
@FieldBandit(columnName = "db_name",type = "varchar",length = 3)
private String name;
public Student2() {
}
public Student2(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = 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 "Student2{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
获得指定注解值
package com.example;
import java.lang.annotation.*;
import java.lang.reflect.Field;
public class text01 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("com.example.Student2");
//通过反射获取注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获取注解的value值
TableBandit tableBandit = (TableBandit) c1.getAnnotation(TableBandit.class);
System.out.println(tableBandit.value());
//获得类指定注解
Field field = c1.getDeclaredField("name");
FieldBandit annotation = field.getAnnotation(FieldBandit.class);
System.out.println(annotation.columnName());
System.out.println(annotation.length());
System.out.println(annotation.type());
}
}