Java注解与反射

1.注解

注解(Annotation)是从JDK5.0开始引入的新技术.


Annotation作用:
可以被其它程序读取(比如:编译器等)

Annotation格式:

@注解名存在 比如:@Documented、@Inherited

Annotation在哪里使用:

类、属性、方法等等

2.常用的内置注解

@Override:重写父类方法
@Deprecated:表示方法已过时不推荐使用,但是方法仍然可以使用
@SuppressWarnings抑制警告不显示代码中警告提示


public class Member {

/*
  @Override:重写父类方法
 */
@Override
public String toString() {
	return super.toString();
}
/*
   @Deprecated
  表示方法已过时,不推荐使用。
  但是方法仍然可以使用
 */
@Deprecated
public static void outSys(){
	System.out.println("Deprecated");
}

/*
 @SuppressWarnings抑制警告
 不显示代码中警告提示
*/
@SuppressWarnings("all")
public void bbaSys(){
	List list = null;
}

public static void main(String[] args) {
   //调用此方式会出现删除符 表示该方法已经过期,不推荐使用
   outSys(); 
}
}

3.元注解

元注解的作用就是负责注解其它注解,Java定义了4个标准的meta-annotation类型,它们用来
提供对其它annotation类型说明。


@Target:描述注解的使用范围(即:被描述的注解可以用在什么地址。比如:类、属性、方法等)

/** Class, interface (including annotation type), or enum declaration */
TYPE,// 接口、类、枚举

/** Field declaration (includes enum constants) */
FIELD,//字段、枚举的常量

/** Method declaration */
METHOD,//方法

/** Formal parameter declaration */
PARAMETER, //方法参数

/** Constructor declaration */
CONSTRUCTOR, //构造函数

/** Local variable declaration */
LOCAL_VARIABLE,//局部变量

/** Annotation type declaration */
ANNOTATION_TYPE,//注解

/** Package declaration */
PACKAGE,//包   

/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER // 方法参数

    



@Retention:表示注解生命周期范围
RunTime>Class>Source
RunTime:运行期间注解有效
Class:Class文件中注解有效
Source:原文件中注解有效

@Documented:将注解生成在JavaDoc文档中

@Inherited:子类可以继承该注解
@Target(value = {ElementType.METHOD} )
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyAnnotation {

}





@interface用来修改注解 表示这是一个注解
@MyAnnotation
public void test01(){

}

4.自定义注解

使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口

注意:
1.@interface用来声明一个注解,格式:public @interface 注解名{定义内容}

2.方法是声明了一个配置参数,方法名称就是参数的名称

3.返回值类型就是参数的类型

4.可以通过default声明参数的默认值

5.如果只有一个参数成员,一般参数名为value
@Target(value = {ElementType.METHOD} )
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyAnnotation {
    
    /*
      name为参数名 类型是String
      如果没用default修饰,使用注解时必须传入参数
     */
    String name() default "默认值";

    int age() default 0;
    
    String[] items() default {"张三"};
    
    String value();
}
@MyAnnotation(value = "睡觉")
public void test01(){

}

5.注解的使用

1.先定义注解


package com.sl.demo.annotation;


import java.lang.annotation.*;

@Target(value = {ElementType.FIELD,ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyAnnotation {

    int minLength() default 2;

    int maxLength() default 4;

    String message() default "名称不符合规则";
}
2.使用注解

package com.sl.demo.bean;

import com.sl.demo.annotation.MyAnnotation;

public class User {

   @MyAnnotation(minLength = 2,maxLength = 4,message = "名字长度在2~4字符")
   private String userName;

   private int age;


    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
3.注解校验

package com.sl.demo.run;

import com.sl.demo.annotation.MyAnnotation;
import com.sl.demo.bean.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;

public class ApplicationRun {

    private static final String FIELD_TYPE = "class java.lang.String";

    private static Logger logger = LoggerFactory.getLogger(ApplicationRun.class);

    public static void main(String[] args) throws IllegalAccessException {
        User user = new User();
        user.setUserName("请叫我司总");
        user.setAge(23);

        valid(user);
    }

    public static void valid(Object object) throws IllegalAccessException {
        Class<?> clazz = object.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field:fields) {
            //获取属性上MyAnnotation注解
            MyAnnotation myAnnotation = field.getAnnotation(MyAnnotation.class);
            if(null != myAnnotation){
                //关闭安全检测 便可以操作私有属性
                field.setAccessible(true);
                //注解只作用于String类型
                if(FIELD_TYPE.equals(field.getGenericType().toString())){
                    String value = (String)field.get(object);
                    if(null != value && (value.length() < myAnnotation.minLength() || value.length() > myAnnotation.maxLength())){
                        logger.error(myAnnotation.message());
                    }
                }
            }
        }
    }
}


输出:
[main] ERROR com.sl.demo.run.ApplicationRun - 名字长度在2~4字符

*SpringBoot结合自定义注解使用

1.导入pom包

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2.创建自定义注解

package com.sl.clear_tomcat.annotation;

import com.sl.clear_tomcat.validator.MyIsNotNullValidator;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

@Target(value = {ElementType.FIELD,ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@Inherited
@Documented
//指定注解处理逻辑类
@Constraint(validatedBy = {MyIsNotNullValidator.class})  
public @interface MyIsNotNull {

    //此处参数只能定义为message~~~
    String message() default "名称不能为null";

    //必须要有 我也不清楚为什么必须要有 ~~~
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
3.创建自定义注解逻辑处理类

package com.sl.clear_tomcat.validator;

import com.sl.clear_tomcat.annotation.MyIsNotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
//ConstraintValidator<MyIsNotNull,String>  第一个参数 为注解类 第二个参数是需要校验的数据类型 
public class MyIsNotNullValidator implements ConstraintValidator<MyIsNotNull,String> {

    Logger logger = LoggerFactory.getLogger(MyIsNotNullValidator.class);
    
    //第一次访问处理时调用,后面多次调用注解不会触发初始化方法
    @Override
    public void initialize(MyIsNotNull constraintAnnotation) {
        logger.info("MyIsNotNullValidator~~~~~~~~~初始化");
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if(null != value && value.length() >0){
            return true;
        }else{
            //注解校验未通过
            return false;
        }
    }
}
4.实体类使用到自定义注解

package com.sl.clear_tomcat.bean;

import com.sl.clear_tomcat.annotation.MyIsNotNull;
import lombok.Data;

@Data
public class User {

    @MyIsNotNull(message = "userName不能为null")
    private String userName;

}
5.模拟测试

@RestController
@RequestMapping("/index")
public class IndexController {

    Logger logger = LoggerFactory.getLogger(MyIsNotNullValidator.class);

    @PostMapping("/")
    public String index(@RequestBody @Validated User user,  BindingResult bindingResult){
        if(bindingResult.hasErrors()){
            List<ObjectError> allErrors = bindingResult.getAllErrors();
            allErrors.forEach( v ->{
                logger.error(v.getObjectName()+"======"+v.getDefaultMessage());
            });
            return "数据校验未通过";
        }
        return "数据校验通过";
    }
}


模拟入参:

{
	"userName":""
}

输出日志:
MyIsNotNullValidator~~~~~~~~~初始化
user======userName不能为null

、6.反射概述

反射(Reflection):
反射机制允许程序在执行期间借助于反射API取得任何类的内部信息,并能直接操作任意对象的内部属性
及方法。


Class类:

JRE为每个类都保留了一个不变的Class类型对象,一个Class对象包含了特定的信息。Class本身也是一个
类,一个加载的类在JVM中只会有一个Class实例。


Class类|实例常用方法:如下表格

方法名

c1.getDeclaredFields();

作用

获取Class对象里的属性

Class.forName("com.ldd.test.User");获取Class对象
c1.newInstance();创建Class对象的一个实例
c1.getName();Class对象对应的包名+类名
c1.getSimpleName();Class对象对应的类名
c1.getClassLoader();Class对象的类加载器
c1.getInterfaces();获取Class对象的接口
c1.getSuperclass();获取Class对象父类Class对象
c1.getConstructor();获取对象的构造函数数组
c1.getMethods();获取Class对象里的方法
获取Class类的实例:

1.Class claszz = User.class;
通过已知类的class属性获取,最为安全可靠,程序性能最高


2.User user = new User(); Class claszz = user.getClass();
通过类的实例,调用实例的getClass()方法获取Class对象  


3.Class clazz = Class.forName("com.example.execlpoi.bean.User");
通过类的全路径名获取,可能会抛出ClassNotFoundException异常

7.常见Class类

//接口
Class c0 = Comparable.class;
//一维数组
Class c1 = String[].class;
//二维数据
Class c2 = String[][].class;
//注解
Class c3 = Override.class;
//枚举
Class c4 = ElementType.class;
//void
Class c5 = void.class;

interface java.lang.Comparable
class [Ljava.lang.String;
class [[Ljava.lang.String;
interface java.lang.Override
class java.lang.annotation.ElementType
void

8.类加载内存

加载:将Class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,
然后生成一个代表这个类的java.lang.Class对象。


链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
    验证:确保加载的类信息符合JVM规范,没有安全方面的问题 
    准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
    解析:虚拟机常量池内的符合引用(常量名)替换为直接引用(地址)的过程。
初始化:
   1.执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译期自动收集类中所有
类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象
的构造器)

   2.当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化


   3.虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。
   

9.反射获取类的属性

public static void main(String[] args) throws Exception {
	Class c1 = Class.forName("com.example.execlpoi.bean.User");
	System.out.println(c1.getName());//包名+类名
	System.out.println(c1.getSimpleName());//类名
	//getFields() 只能获取public属性
	Field[] fields = c1.getFields();
	for (Field field : fields) {
		System.out.println(field.getName());
	}
	System.out.println("================");
	//getDeclaredFields() 获取所有属性 包含private
	fields = c1.getDeclaredFields();
	for (Field field : fields) {
		System.out.println(field.getName());
	}
	System.out.println("================");
	//获取指定属性
	Field car = c1.getField("car");
	Field name = c1.getDeclaredField("name");
	System.out.println(car);
	System.out.println(name);
	System.out.println("================");
	//获取public方法
	Method[] methods = c1.getMethods();
	for (Method method : methods) {
		System.out.println("public:"+method);
	}
	//获取全部方法 包含私有的
	methods = c1.getDeclaredMethods();
	for (Method method : methods) {
		System.out.println("all:"+method);
	}
	System.out.println("================");
	//获取指定方法
	Method daye = c1.getDeclaredMethod("daye", null);
	Method setCar = c1.getMethod("setCar", String.class);
	System.out.println(daye);
	System.out.println(setCar);
	System.out.println("================");
	//获取public构造
	Constructor[] constructors = c1.getConstructors();
	for (Constructor constructor : constructors) {
		System.out.println("publuc:"+constructor);
	}
	//获取全部构造
	constructors = c1.getDeclaredConstructors();
	for (Constructor constructor : constructors) {
		System.out.println("all:"+constructor);
	}
}

10.反射获取对象

public static void main(String[] args) throws Exception {
	Class c1 = Class.forName("com.example.execlpoi.bean.User");
	//c1.newInstance() 默认调用的是对象的无参构造方法
	//所以 对象需要存在无参构造
	User u1 = (User) c1.newInstance();
	System.out.println(u1);

	//使用指定的构造器构造
	Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class, String.class);
	User u2 = (User) constructor.newInstance("司总", 20, 88, "BMG");
	System.out.println(u2);

	//调用方法
	Method setCar = c1.getDeclaredMethod("setCar", String.class);
	//invoke 激活的意思 第一个参数 挂载的对象  第二参数 方法的入参内容
	setCar.invoke(u2,"司总的BBA");
	System.out.println(u2.getCar());

	//操作属性
	Field name = c1.getDeclaredField("name");

	//不能直接操作私有属性,设置true关闭安全检测 可以直接操作私有属性
	name.setAccessible(true);
	name.set(u2,"我是司总");
	System.out.println(u2.getName());
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值