注解与反射
Posted tanshishi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了注解与反射相关的知识,希望对你有一定的参考价值。
注解
为什么要学注解与反射
? 所有的框架底层实现机制都是注解与反射,框架中有许多的注解,通过反射读取注解的值,来简化操作.
? 比如利用反射读取注解的值,通过值拼成SQL语句,就可以动态地生成表,或者其他高级的功能.
什么是注解(Annotation)
Annotation的作用:
可以被其他程序(比如: 编译器等)读取
Annotation在哪里使用
可以附加在package,class,method,field等上面,相当于给他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问
内置注解
//什么是注解
public class Test01 extends Object{
//@Override 重写的注解
@Override
public String toString() {
return super.toString();
}
//@Deprecated 不推荐程序员使用,但是可以使用,或者存在更好的方式
@Deprecated
public static void test() {
System.out.println("Deprecated");
}
//@SuppressWarnings镇压警告
@SuppressWarnings("all")
public void test02() {
List list = new ArrayList();
}
public static void main(String[] args) {
test();
}
}
//@Override 重写的注解
//@Deprecated 不推荐程序员使用,但是可以使用,或者存在更好的方式
//@SuppressWarnings镇压警告
元注解
点进注解里可以看到注解的定义方式:
//定义一个注解
//@Target 定义作用域
@Target(value = {ElementType.METHOD, ElementType.TYPE})
//@Retention 表示注解在何时有效
//runtime>class>sources
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation {
}
自定义注解
略
反射机制
-
反射是什么: 反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法.
-
反射机制允许程序在执行期借助于Reflection API取得
任何类的内部信息
,并能直接操作对象的内部属性及方法
-
反射获取类信息的原理: 加载完类之后,在堆内存的方法区中就产生了一个
Class类型
的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息.我们可以通过这个对象看到类的结构.
java反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解(框架的底层实现机制)
- 生成动态代理
- ......
后面的笔记就是具体的代码实现
获得反射对象
//什么叫反射
public class test02 {
public static void main(String[] args) throws ClassNotFoundException {
//通过分反射获取类的Class对象
Class c1 = Class.forName("com.tan.reflection.User");
System.out.println(c1);
Class c2 = Class.forName("com.tan.reflection.User");
Class c3 = Class.forName("com.tan.reflection.User");
Class c4 = Class.forName("com.tan.reflection.User");
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
System.out.println(c4.hashCode());
}
}
//实体类:与数据库字段做对应时实体类包一般命名: pojo 或 entity
class User {
private String name;
private int id;
private int age;
public User() {
}
public User(String name, int id, int age) {
this.name = name;
this.id = id;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
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;
}
@Override
public String toString() {
return "User{" +
"name=‘" + name + ‘‘‘ +
", id=" + id +
", age=" + age +
‘}‘;
}
}
输出:
hashCode相同,代表获取的确实是同一个类.
获取User类的Class对象语句:
Class c1 = Class.forName("com.tan.reflection.User");
获取Class对象,即代表获取了User类里面的所有东西,甚至是私有的属性,私有的方法等.
Class类
在Object类中定义了此方法: public final Class getClass() ,此方法被所有子类继承
所以此类是Java反射的源头.
此时我们再来分析一下这个语句:
Class.forName("com.tan.reflection.User");
Class
是反射的源头,forName
是Class的方法,通过String类型的路径"com.tan.reflection.User"
,找到User类并获取User类的所有内容.
Class的对象保存在哪: 对于每个类而言,在运行时JRE都为其保留一个不变的Class类型的对象,一个Class对象对应的是一个加载到 JVM 中的一个.class文件.
Class类的常用方法(不用看)
常见的Class类的创建方式
被获取的对象:
class Person {
public String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name=‘" + name + ‘‘‘ +
‘}‘;
}
}
class Student extends Person {
public Student() {
this.name = "学生";
}
}
class Teacher extends Person {
public Teacher() {
this.name = "老师";
}
}
//方式一: 通过对象获得
Class c1 = person.getClass();
System.out.println(c1.hashCode());
//方式二: forname获取,需要抛出异常throws ClassNotFoundException
Class c2 = Class.forName("com.tan.reflection.Student");
System.out.println(c2.hashCode());
//方法三: 通过类名.class获得
Class c3 = Student.class;
System.out.println(c3.hashCode());
扩充:
//基本内置类型的包装类都有一个Type属性
Class c4 = Integer.TYPE;
System.out.println(c4);
//获取父类类型
Class c5 = c1.getSuperclass();
System.out.println(c5);
输出:
189568618
189568618
189568618
int
class com.tan.reflection.Person
所有类型的Class对象
public class Test04 {
public static void main(String[] args) {
Class<Object> c1 = Object.class; //类
Class<Comparable> c2 = Comparable.class; //接口
Class<String> c3 = String.class; //一维数组
Class<String[]> c4 = String[].class; //二维数组
Class<Override> c5 = Override.class; //注解
Class<ElementType> c6 = ElementType.class; //枚举
Class<Integer> c7 = Integer.class; //基本引用类型
Class<Void> c8 = void.class; //void
Class<Class> c9 = Class.class; //Class
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
}
}
class java.lang.Object
interface java.lang.Comparable
class java.lang.String
class [Ljava.lang.String;
interface java.lang.Override
class java.lang.annotation.ElementType
class java.lang.Integer
void
class java.lang.Class
了解类的加载过程
类在被加载时会生成一个Class对象副本(我也不确定能不能叫做副本),反射就是获取在类加载时生成的这个Class对象副本,而这个副本包括了class文件里的所有信息.
什么时候会发生初始化(不用看)
子类调用父类的静态方法,子类不会被初始化
获取运行时类的完整结构
获取类名: (实体类User在前面写了)
Class c1 = Class.forName("com.tan.reflection.User");
//获得类的名字与路径,获得类名
System.out.println(c1.getName());
System.out.println(c1.getSimpleName());
输出:
com.tan.reflection.User
User
前面写了类名现在又用这个获取是不是有点蠢,但如果类是这样创建的就不知道类的名字:
User user = new User();
c1 = user.getClass(); //这样创建就不知道类的路径
获取类的属性
//获得类的属性
System.out.println("===================================");
Field[] fields1 = c1.getFields(); //只能找到public属性
for (Field field : fields1) {
System.out.println(field);
}
输出为空
由于user的属性为私有,无法用getFields()方法获取到.
使用getDeclaredFields()方法可以获得私有的属性
//获得类的所有属性
Field[] fields1 = c1.getDeclaredFields();//可以获得私有的属性
for (Field field : fields1) {
System.out.println(field);
}
输出:
private java.lang.String com.tan.reflection.User.name
private int com.tan.reflection.User.id
private int com.tan.reflection.User.age
获得类的方法
本类与父类的public方法:
System.out.println("===================================");
Method[] methods1 = c1.getMethods();
for (Method method : methods1) {
System.out.println("本类与父类的public方法: "+method);
}
本类的所有的方法:
Method[] methods2 = c1.getDeclaredMethods();
for (Method method2 : methods2) {
System.out.println("本类的所有的方法: "+method2);
}
输出:
本类与父类的public方法: public java.lang.String com.tan.reflection.User.getName()
本类与父类的public方法: public java.lang.String com.tan.reflection.User.toString()
本类与父类的public方法: public void com.tan.reflection.User.setName(java.lang.String)
本类与父类的public方法: public int com.tan.reflection.User.getId()
本类与父类的public方法: public int com.tan.reflection.User.getAge()
本类与父类的public方法: public void com.tan.reflection.User.setId(int)
本类与父类的public方法: public void com.tan.reflection.User.setAge(int)
本类与父类的public方法: public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
本类与父类的public方法: public final void java.lang.Object.wait() throws java.lang.InterruptedException
本类与父类的public方法: public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
本类与父类的public方法: public boolean java.lang.Object.equals(java.lang.Object)
本类与父类的public方法: public native int java.lang.Object.hashCode()
本类与父类的public方法: public final native java.lang.Class java.lang.Object.getClass()
本类与父类的public方法: public final native void java.lang.Object.notify()
本类与父类的public方法: public final native void java.lang.Object.notifyAll()
本类的所有的方法: public java.lang.String com.tan.reflection.User.getName()
本类的所有的方法: public java.lang.String com.tan.reflection.User.toString()
本类的所有的方法: public void com.tan.reflection.User.setName(java.lang.String)
本类的所有的方法: public int com.tan.reflection.User.getId()
本类的所有的方法: public int com.tan.reflection.User.getAge()
本类的所有的方法: public void com.tan.reflection.User.setId(int)
本类的所有的方法: public void com.tan.reflection.User.setAge(int)
获得指定方法
//获得指定方法
System.out.println("===================================");
Method getName = c1.getMethod("getName", null);
Method setName = c1.getMethod("setName", String.class);
System.out.println(getName);
System.out.println(setName);
输出:
public java.lang.String com.tan.reflection.User.getName()
public void com.tan.reflection.User.setName(java.lang.String)
获得构造器
//获得构造器
System.out.println("===================================");
Constructor[] constructors = c1.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
输出:
public com.tan.reflection.User()
public com.tan.reflection.User(java.lang.String,int,int)
获得指定构造器
//获得指定构造器
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
System.out.println("指定: " + constructor);
输出:
指定: public com.tan.reflection.User(java.lang.String,int,int)
通过反射动态的创建对象
此方法居然过时了??
获得User的Class对象
Class c1 = Class.forName("com.tan.reflection.User");
构造一个对象
//构造一个对象
User user = (User) c1.newInstance();
System.out.println(user);
输出:User{name=‘null‘, id=0, age=0}
很明显调用的是无参构造器,于是将User的无参构造器删除,编译器报错
但是可能每个方法都有无参构造器,那么没有无参构造器就不能传参了吗?
通过构造器创建对象
//通过构造器创建对象
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
User user2 = (User) constructor.newInstance("tan", 01, 20);
System.out.println(user2);
输出:
User{name=‘tan‘, id=1, age=20}
通过反射获取一个方法,传参并调用方法
//通过反射获取一个方法,传参并调用方法
User user3 = (User) c1.newInstance();
Method setName = c1.getDeclaredMethod("setName", String.class);
setName.invoke(user3,"tan");
System.out.println(user3.getName());
通过反射操作属性
//通过反射操作属性
User user4 = (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
name.set(user4,"tan2");
System.out.println(user4.getName());
输出:
错误 class com.tan.reflection.Test09 cannot access a member of class com.tan.reflection.User with modifiers "private"
在前面可以得知getDeclaredField()可以获取Class的私有属性,但是报错中任显示无法操作私有属性.
但是可以关闭权限检测name.setAccessible(true);
User user4 = (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
//不能直接操作私有属性,需要关闭程序的安全检测
name.setAccessible(true); //关闭程序的安全检测
name.set(user4,"tan2");
System.out.println(user4.getName());
输出:
tan2
重点方法:
.newInstance()
//反射中的实例化方法
.invoke(对象,"方法的值")
//激活的意思,传参并调用方法
.setAccessible(true);
//关闭程序的安全检测
反射性能检测
package com.tan.reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author null
* @date 2020/5/28
*/
public class Test10 {
//普通方式调用
public static void test01() {
User user = new User();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方式执行10亿次: " + (endTime - startTime) + "ms");
}
//反射方式调用
public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();//获取User对象
Class c1 = user.getClass();//获取对象的Class对象
long startTime = System.currentTimeMillis();
Method getName = c1.getDeclaredMethod("getName",null);//得到getName方法
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user, null);//执行getName方法
}
long endTime = System.currentTimeMillis();
System.out.println("反射方式执行10亿次: " + (endTime - startTime) + "ms");
}
//反射方式调用 关闭检测
public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
Class c1 = user.getClass();
long startTime = System.currentTimeMillis();
Method getName = c1.getDeclaredMethod("getName",null);
getName.setAccessible(true);
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user, 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();
}
}
输出:
普通方式执行10亿次: 4ms
反射方式执行10亿次: 2360ms
关闭检测的反射方式执行10亿次: 1045ms
至于为什么反射性能低这么多,涉及底层水平不足,没办法详细解释.??
反射操作泛型
- Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除
- 为了通过反射操作这些类型,Java新增了ParameterizedType,GenericArrayType,TypeVariable 和WildcardType 几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型.
通过反射获取泛型
获取Test01的泛型的参数类型:
public class Test11 {
public void test01(Map<String,User> map,List<User> list) {
System.out.println("test01");
}
public Map<String, User> test02() {
System.out.println("test01");
return null;
}
public static void main(String[] args) throws NoSuchMethodException {
Method method = Test11.class.getMethod("test01", Map.class, List.class);//获取test01方法
Type[] genericParameterTypes = method.getGenericParameterTypes();//获取通用参数类型
for (Type genericParameterType : genericParameterTypes) {
System.out.println("泛型参数类型: " + genericParameterType);
}
}
}
输出:
泛型参数类型: java.util.Map<java.lang.String, com.tan.reflection.User>
泛型参数类型: java.util.List<com.tan.reflection.User>
这样只能获得Map和List那么如何输出,如何把泛型取出来呢?
代码优化: 在for循环中再遍历genericParameterType
public static void main(String[] args) throws NoSuchMethodException {
Method method = Test11.class.getMethod("test01", Map.class, List.class);//获取test01方法
Type[] genericParameterTypes = method.getGenericParameterTypes();//获取通用参数类型
for (Type genericParameterType : genericParameterTypes) {
System.out.println("泛型参数类型: " + genericParameterType);
if (genericParameterType instanceof ParameterizedType) {//如果这个通用的参数类型是一个参数化类型
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();//获取真实的参数类型
for (Type actualTypeArgument : actualTypeArguments) {//遍历输出
System.out.println(actualTypeArgument);
}
}
}
}
输出:
泛型参数类型: java.util.Map<java.lang.String, com.tan.reflection.User>
class java.lang.String
class com.tan.reflection.User
泛型参数类型: java.util.List<com.tan.reflection.User>
class com.tan.reflection.User
获取Test02的泛型的返回值类型:
Method method = Test11.class.getMethod("test02");
Type genericReturnType = method.getGenericReturnType();//获取通用返回值类型
if (genericReturnType instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
输出:
class java.lang.String
class com.tan.reflection.User
反射操作注解
package com.tan.reflection;
import java.lang.annotation.*;
import java.lang.reflect.Field;
/**
* @author null
* @date 2020/5/28
*/
public class Test12 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("com.tan.reflection.Student2");
//通过反射获得类注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获得注解的value的值
TableTan annotation = (TableTan) c1.getAnnotation(TableTan.class);
String value = annotation.value();
System.out.println(value);
//获得类属性的注解
System.out.println("===============================");
Field id = c1.getDeclaredField("id");
Annotation[] annotations1 = id.getAnnotations();
for (Annotation annotation1 : annotations1) {
System.out.println(annotation1);
}
//获得类属性注解的值
FieldTan annotation2 = id.getAnnotation(FieldTan.class);
System.out.println(annotation2.columnName());
System.out.println(annotation2.type());
System.out.println(annotation2.length());
}
}
@TableTan("db_student")
//实体类
class Student2 {
//假装注解可以操作表
@FieldTan(columnName = "db_id", type = "int", length = 10)
private int id;
@FieldTan(columnName = "db_age", type = "int", length = 10)
private int age;
@FieldTan(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 "Test12{" +
"id=" + id +
", age=" + age +
", name=‘" + name + ‘‘‘ +
‘}‘;
}
}
//类名的注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface TableTan {
String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldTan {
String columnName();
String type();
int length();
}
输出:
@com.tan.reflection.TableTan("db_student")
db_student
===============================
@com.tan.reflection.FieldTan(columnName="db_id", type="int", length=10)
db_id
int
没什么好讲的,就是方法的调用.
单例模式加强对反射的理解
以上是关于注解与反射的主要内容,如果未能解决你的问题,请参考以下文章