class 反射机制:
JAVA 的反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息及动态调用对象方法的功能称为 java 语言的反射机制。
框架与反射
- 框架:半成品软件,可以在框架的基础上进行开发,而框架本身不能运行
- 反射:反射是框架的灵魂,将类的各个组成部分封装为其他对象
- 反射的好处:可以在程序运行的过程中去操作对象,字节码文件,不需要重新编译,提高程序扩展性,复用性,解耦
代码练习:
public class Test {
public static void main(String[] args) throws Exception {
// 获取一个Class
Class<User> userClass=User.class;
// getName 获取类的完整路径的名字
System.out.println(userClass.getName());
// 获取不带包名的全类名
System.out.println(userClass.getSimpleName());
// 获取class 方法2 根据类名获取类的对象
Class<?> userClass2=Class.forName("com.example.demo.clazz.User");
System.out.println(userClass2.getSimpleName());
// 获取class 方法3
User user=new User();
Class<? extends User> userClass3 = user.getClass();
System.out.println(userClass3.getSimpleName());
System.out.println("-------------------------------------------");
Class<User> userClass1 = User.class;
// 获取类的包名,注意这里的包名是包含 package的
System.out.println(userClass1.getPackage());
// 创建实例
User user1 = userClass1.newInstance();
System.out.println(user1);
// 获得类加载器
ClassLoader classLoader = userClass.getClassLoader();
// 获取该类中的公共类
Class<?>[] classes = userClass1.getClasses();
for (Class<?> cls:classes){
System.out.println(cls.getSimpleName());
}
System.out.println("---------------------------------");
// 获取该类中的类 包括非公共类
Class<?>[] declaredClasses = userClass1.getDeclaredClasses();
for (Class<?> cls:declaredClasses){
System.out.println(cls.getSimpleName());
}
System.out.println("---------------------------------");
// 把传递的对象转换成代表其子类的对象
Class<Brid> brid = Brid.class;
Class<? extends Animal> aClass = brid.asSubclass(Animal.class);
System.out.println(aClass.getName());
// 获得当前继承的父类的名字
Class<? super Brid> superclass = brid.getSuperclass();
System.out.println(superclass.getName());
// 获得当前类实现的类或是接口
Class<?>[] interfaces = brid.getInterfaces();
for (Class<?> inf:interfaces){
System.out.println(inf.getName());
}
System.out.println("---------------------------------");
Class<User> userField=User.class;
// 获取类中的所有公共字段
Field[] fields = userField.getFields();
for (Field fid:fields){
System.out.println(fid.getName());
}
// 获取类中某个公共的字段
Field name = userField.getField("idNumber");
System.out.println(name.getName());
System.out.println("----------------------");
// 获取类中的所有字段
Field[] declaredFields = userField.getDeclaredFields();
for (Field fid:declaredFields){
System.out.println(fid.getName());
}
System.out.println("--------------");
// 获取类中某个字段
Field name1 = userField.getDeclaredField("name");
System.out.println(name1.getName());
System.out.println("---------------------------------");
// 获取指定的注解 可以根据此来判断,根据注解是否存在,去执行相关的逻辑
Logger annotation = userField.getAnnotation(Logger.class);
System.out.println(annotation);
System.out.println("---------------------------------");
// 获取类中的所有注解
Annotation[] annotations = userField.getAnnotations();
for (Annotation an:annotations){
System.out.println(an);
}
System.out.println("---------------------------------");
// 获取该类中所有公共构造方法
Constructor<?>[] constructors = userField.getConstructors();
for (Constructor<?> con:constructors) {
System.out.println("获取到"+con.getParameterCount()+"个公共构造方法"+con.getName());
}
System.out.println("---------------------------------");
// 获取类中所有构造方法
Constructor<?>[] declaredConstructors = userField.getDeclaredConstructors();
for (Constructor<?> con:declaredConstructors) {
System.out.println(con.getName());
}
System.out.println("---------------------------------");
// 获取该类中与参数类型匹配的公有构造方法 默认是无参构造方法
Constructor<User> constructor = userField.getConstructor();
System.out.println(constructor);
System.out.println("---------------------------------");
// 获取该类中与参数类型匹配的构造方法 注意参数个数应与构造方法一致
Constructor<User> declaredConstructor = userField.getDeclaredConstructor(String.class, Integer.class,String.class);
System.out.println(declaredConstructor);
System.out.println("---------------------------------");
// 获取该类所有公有的方法
Method[] methods = userField.getMethods();
for (Method method:methods){
System.out.println(method);
}
System.out.println("---------------------------------");
// 获取该类中的某个公有方法 参数 方法名称 和 方法的参数
Method method1 = userField.getMethod("getName");
System.out.println(method1);
System.out.println("---------------------------------");
// 获取该类中的所有方法
Method[] declaredMethods = userField.getDeclaredMethods();
for (Method method:methods){
System.out.println(method);
}
System.out.println("---------------------------------");
// 获取该类中的某个方法 根据名称和参数类型
Method method2 = userField.getDeclaredMethod("setName", String.class);
System.out.println(method2);
System.out.println("---------------------------------");
// 如果是注解类型则返回true
System.out.println(Logger.class.isAnnotation());
System.out.println(User.class.isAnnotation());
// 如果是接口则返回true
System.out.println(MyAbility.class.isInterface());
System.out.println(User.class.isInterface());
// 如果是枚举则返回true
System.out.println(User.class.isEnum());
// 如果是数组则返回 true
System.out.println(User.class.isArray());
// 如果 obj是该类的实例则返回true
System.out.println(User.class.isInstance(user));
}
}
//@Logger
//如果属性名称是Value 时可以直接赋值
@MyAnnotation("我是用户类")
class User{
@MyAnnotation("张三")
private String name;
protected Integer age;
public String idNumber;
public User() {
}
private static class Bike{
private String name;
}
public static class Clothes{
private String name;
}
protected User(String name, Integer age, String idNumber) {
this.name = name;
this.age = age;
this.idNumber = idNumber;
}
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
protected Integer getAge() {
return age;
}
protected void setAge(Integer age) {
this.age = age;
}
private String getIdNumber() {
return idNumber;
}
private void setIdNumber(String idNumber) {
this.idNumber = idNumber;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", idNumber='" + idNumber + '\'' +
'}';
}
}
class Animal{
private String name;
private String age;
private String ability;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getAbility() {
return ability;
}
public void setAbility(String ability) {
this.ability = ability;
}
}
class Brid extends Animal implements MyAbility{
@Override
public void method() {
System.out.println("小鸟的本领是偷东西");
}
}
interface MyAbility{
void method();
}
常用方法:
获取一个class
Class<User> userClass=User.class;
获取类的完整路径
userClass.getName()
获取类名
userClass.getSimpleName()
获取类的包名
userClass.getPackage()
创建类的实例
User user = userClass.newInstance();
获取类中包含的公共类
Class<?>[] classes = userClass.getClasses();
获取类中所有类
Class<?>[] declaredClasses = userClass.getDeclaredClasses();
把传递的对象转换成代表其子类的对象
Class<Brid> brid = Brid.class;
Class<? extends Animal> aClass = brid.asSubclass(Animal.class);
获取类中所有公共字段
Field[] fields = userField.getFields();
获取某个公共字段
Field name = userField.getField("idNumber");
获取类中所有字段
Field[] declaredFields = userField.getDeclaredFields();
获取指定注解
Logger annotation = userField.getAnnotation(Logger.class);
获取类中的所有注解
Annotation[] annotations = userField.getAnnotations();
获取类中所有公共构造方法
Constructor<?>[] constructors = userField.getConstructors();
获取类中所有构造方法
Constructor<?>[] declaredConstructors = userField.getDeclaredConstructors();
获取该类所有公有的方法
Method[] methods = userField.getMethods();
获取该类中的所有方法
Method[] declaredMethods = userField.getDeclaredMethods();
Field 代表类的成员变量。成员变量和成员属性是两个概念。用get/set方法来区分,如果一个类里面有name变量,没有get/set 方法,则这个类只有name这个字段,如果有get/set方法时,则这个类拥有name属性
代码如下:
public class FieldTest {
public static void main(String[] args) throws Exception{
Class<User> userClass=User.class;
User user=new User("张三",23,"123456");
Field field = userClass.getDeclaredField("name");
// 获取 obj中对应的公共方法的属性值
// System.out.println(field.get(user));
// 暴力反射 ,忽略访问权限修饰符
field.setAccessible(true);
System.out.println(field.get(user));
// 设置obj中对应属性值
field.set(user,"李四");
System.out.println(field.get(user));
}
}
Method 代表类的方法(不包括构造方法)
public class MethodTest {
public static void main(String[] args) throws Exception{
Class<User> userClass=User.class;
User user=new User("张三",23,"123456");
// Method method = userClass.getDeclaredMethod("getName");
Method method = userClass.getDeclaredMethod("getIdNumber");
// 暴力反射 ,忽略访问权限修饰符
method.setAccessible(true);
// 传递obj对象及参数调用该对象对应的方法 执行指定对象的方法
Object invoke = method.invoke(user);
System.out.println(invoke);
// set方法
Method method2 = userClass.getDeclaredMethod("setIdNumber", String.class);
method2.setAccessible(true);
Object invoke1 = method2.invoke(user, "666");
System.out.println(user);
}
}
Constructor: 代表类的构造方法
public class ConstructorTest {
public static void main(String[] args) throws Exception{
Class<User> userClass = User.class;
// 调用共有的构造方法 创建对象
User user = userClass.newInstance();
System.out.println(user);
// Constructor 可以创建有参构造方法,并且无视方法访问权限
Constructor<User> declaredConstructor = userClass.getDeclaredConstructor(String.class, Integer.class, String.class);
User user1 = declaredConstructor.newInstance("张三", 23, "123456");
System.out.println(user1);
}
}
验证通过反射擦除泛型
public class ListTest {
public static void main(String[] args) throws Exception{
List<User> userList = new ArrayList<User>();
userList.add(new User("张三",24,"12324"));
userList.add(new User("李四",24,"12334"));
userList.add(new User("王五",24,"32324"));
Class<? extends List> userListClass = userList.getClass();
Method method = userListClass.getDeclaredMethod("add", Object.class);
// 泛型会在运行阶段被擦除,这个时候集合一类的对象就可以强制往里面放入非指定泛型的数据
method.invoke(userList,"hhh");
method.invoke(userList,"123");
System.out.println(userList);
}
}
工具类,根据具体的方法名,执行次方法
/**
* 执行指定对象的指定方法
* @param obj 指定的对象
* @param methodName 指定的方法名称
* @param params 传输的参数
* @return
*/
public static Object invokeMethod(Object obj,String methodName,Object... params){
Class<?> objectClass=obj.getClass();
try {
if(params.length==0||params==null){
Method declaredMethod = objectClass.getDeclaredMethod(methodName);
declaredMethod.setAccessible(true);
Object result = declaredMethod.invoke(obj);
return result;
}else{
int size= params.length;
Class<?>[] classes=new Class[size];
Object[] objects=new Object[size];
for (int i=0;i<size;i++){
classes[i]=params[i].getClass();
objects[i]=params[i];
}
Method declaredMethod = objectClass.getDeclaredMethod(methodName, classes);
declaredMethod.setAccessible(true);
Object result = declaredMethod.invoke(obj, objects);
return result;
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
验证如下:
public static void main(String[] args) {
User user=new User("张三",23,"123456");
Object getName = invokeMethod(user, "getName");
System.out.println(getName);
Object method = invokeMethod(user, "setName", "李四");
System.out.println(user);
}
注解
- 注释:用于文字描述,给程序员看的
- 定义:用来说明程序的一个标识,给计算机看的,注解也叫元数据,是一种代码级别的说明,它是jdk1.5之后引入的一个特性,是一种特殊的接口,可以用在字段,类
- 方法,包,参数等上面
- 注意:注解本身并没有任何功能,仅仅起到一个标识性作用,我们是通过反射获取到注解,再根据是否有这个注解,注解中的一些属性,去判断执行那种业务逻辑
作用分类:
- 编写文档:通过代码里的注解标识去生成API文档(Swagger)
- 代码分析:通过注解去对代码进行逻辑上的分析(通过反射去操作业务)
- 编译检查:通过编辑器实现基本的编译检查(Override,SuppressWarning)
Jdk中预定的一些注解
- Override:检测注解标识的方法是否是继承自父类
- Deprecated:标识该内容以及过时,后续的版本可能会进行移除
- SuppressWarnings:压制警告,指定警告级别,这个级别下的警告全都不显示
自定义注解:
在实际开发中,可能存在一些代码及其复杂或者复用性很低的业务逻辑,比如:导出Excel,缓存,将返回值转json,事务等等,这个时候就可以使用自定义注解
格式
- 元注解
public @interface 注解名称{
属性列表;
}
本质就是一个接口,该接口继承 Annotation接口
属性
本质:接口中的抽象方法
属性只能是以下几种数据类型:
- 1.基本数据类型
- 2.String
- 3.枚举
- 4.注解
- 5.以上类型的数组
如果定义了属性,在使用的时候需要给属性赋值,如果有默认值则可以不附值,如果只有1个属性需要赋值,并且这个属性叫Value 那么就可以省略属性名,数组赋值的时候,用{}包裹,如果只有1个值,那么大括号可以省略
元注解
@Target 表示该注解用于什么地方,可能的值在枚举类 ElemenetType 中,包括:
-
ElemenetType.CONSTRUCTOR-----------------------------构造器声明
-
ElemenetType.FIELD ----------------------------------域声明(包括 enum 实例)
-
ElemenetType.LOCAL_VARIABLE------------------------- 局部变量声明
-
ElemenetType.METHOD ---------------------------------方法声明
-
ElemenetType.PACKAGE --------------------------------包声明
-
ElemenetType.PARAMETER ------------------------------参数声明
-
ElemenetType.TYPE----------------------------------- 类,接口(包括注解类型)或enum声明
@Retention 表示在什么级别保存该注解信息。可选的参数值在枚举类型 RetentionPolicy 中,包括:
-
RetentionPolicy.SOURCE-------------注解将被编译器丢弃
-
RetentionPolicy.CLASS -------------注解在class文件中可用,但会被VM丢弃
-
RetentionPolicy.RUNTIME ---------VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。
@Documented 将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同。相当与@see,@param 等。
@Inherited 允许子类继承父类中的注解。
注解测试代码如下:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
/**
* 值
* @return
*/
String value();
/**
* 长度
* @return
*/
int num() default 0;
}
public class AnnotationTest {
public static void main(String[] args) throws Exception{
// 获取用户反射
Class<User> userClass = User.class;
// 获取注解
MyAnnotation annotation = userClass.getAnnotation(MyAnnotation.class);
if(annotation!=null){
System.out.println(annotation.value());
}
System.out.println("--------------------------------");
User user=new User();
//获取所有字段
Field[] declaredFields = userClass.getDeclaredFields();
for (Field field:declaredFields){
//获取字段注解
MyAnnotation annotation1 = field.getAnnotation(MyAnnotation.class);
if(annotation1!=null){
// 获取注解值
String value= annotation1.value();
// 暴力反射
field.setAccessible(true);
// 设置字段值为注解值
field.set(user,value);
}
}
System.out.println(user);
}
}
实现缓存注解
/**
* 缓存注解
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Cache {
/**
* 大key
* @return
*/
String key();
/**
* 过期时间
* @return
*/
int timeOut() default 10;
/**
* 过期时间单位
* @return
*/
TimeUnit timeUnit() default TimeUnit.MINUTES;
}
public class UserController {
private User[] user={
new User("张三"),
new User("李四"),
new User("王五")
};
@Cache(key = "user")
public User getUserById(Integer id){
System.out.println("根据缓存查询用户数据");
return user[id];
}
}
验证方法
public class UserController {
private User[] user={
new User("张三"),
new User("李四"),
new User("王五")
};
@Cache(key = "user")
public User getUserById(Integer id){
System.out.println("根据缓存查询用户数据");
return user[id];
}
}
工具类方法
public final class MethodUtil {
private static Map<String,Object> cacheMap=new ConcurrentHashMap<String,Object>();
public MethodUtil() {
}
/**
* 缓存注解
* @param obj 指定的对象
* @param methodName 指定的方法名称
* @param params 传输的参数
* @return
*/
public static Object CacheMethod(Object obj,String methodName,Object... params){
Class<?> objectClass=obj.getClass();
try {
if(params.length==0||params==null){
//没有参数的情况下,以大KEY为缓存的key
Method declaredMethod = objectClass.getDeclaredMethod(methodName);
declaredMethod.setAccessible(true);
Cache annotation = declaredMethod.getAnnotation(Cache.class);
//方法上面有cache注解进行缓存
if(annotation!=null){
String key=annotation.key();
Object value= cacheMap.get(key);
if(value != null){
return value;
}
}
Object result = declaredMethod.invoke(obj);
//将数据放入map进行缓存
if(annotation != null){
String key=annotation.key();
cacheMap.put(key,result);
}
}else{
int size= params.length;
Class<?>[] classes=new Class[size];
Object[] objects=new Object[size];
for (int i=0;i<size;i++){
classes[i]=params[i].getClass();
objects[i]=params[i];
}
Method declaredMethod = objectClass.getDeclaredMethod(methodName, classes);
declaredMethod.setAccessible(true);
Cache annotation = declaredMethod.getAnnotation(Cache.class);
//方法上面有cache注解进行缓存
if(annotation!=null){
//获取大 key
String key=annotation.key();
//获取 小 key
Object param = params[0];
// 拼接缓存key
String cacheKey = key +":"+param;
//从缓存中查找数据
Object value = cacheMap.get(cacheKey);
if(value != null){
return value;
}
}
Object result = declaredMethod.invoke(obj, objects);
//将数据放入map进行缓存
if(annotation != null){
//获取大 key
String key=annotation.key();
//获取 小 key
Object param = params[0];
// 拼接缓存key
String cacheKey = key +":"+param;
cacheMap.put(cacheKey,result);
}
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
测试类
public class CacheTest {
public static void main(String[] args) throws Exception {
UserController userController= new UserController();
Object user1 = MethodUtil.CacheMethod(userController, "getUserById", 1);
Object user2 = MethodUtil.CacheMethod(userController, "getUserById", 1);
}
}
调用两个相同ID的方法,该方法只输出了1遍