JAVA注解和反射

时间:2019-04-18
本文章向大家介绍JAVA注解和反射,主要包括JAVA注解和反射使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

JAVA的注解

什么是注解

注解目前还是非常流行的,我们常用的Sprinng就是利用注解。 添加注解可以代码简洁,阅读方便 ,使用方便。

  1. 首先,先来看一下我们java自带的元注释,在java.lang.annotation包下面,其中@Native@Repeatable 不是元注解(元注解就是注解新建注解的注解)
注释名称 解释
@Target 表示该注解可以用于什么地方,可能的ElementType参数有:CONSTRUCTOR:构造器的声明FIELD:域声明(包括enum实例)LOCAL_VARIABLE:局部变量声明METHOD:方法声明PACKAGE:包声明PARAMETER:参数声明TYPE:类、接口(包括注解类型)或enum声明
@Retention 表示需要在什么级别保存该注解信息。可选的RetentionPolicy参数包括:SOURCE:注解将被编译器丢弃CLASS:注解在class文件中可用,但会被VM丢弃RUNTIME:VM将在运行期间保留注解,因此可以通过反射机制读取注解的信息。
@Document 将注解包含在Javadoc中
@Inherited 允许子类继承父类中的注解
  1. 新建注解可以做什么
    创建一个MyAnnotation.Class的注释
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//@Inherited 允许子类继承父类中的注解
//@Documented 将注解包含在<u>Javadoc</u>中
//@Retention 注解在什么级别保存注解,在反射机制中可以用到
//@Target 创建的注解可以 用在什么地方(方法、类 等)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAnnotation {
}

新建类 Atest.Class来使用注释

@MyAnnotation
public class Atest {
    private int number;
    private String name;
    public int getNumber() {
        return number;
    }
    public void setNumber(int number) {
        this.number = number;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString(){
       return Atest.class.toString();
    }
    Atest(){}
}

新建Btest.Class来测试注释,利用反射

public class Btest {
  public static void main(String[] args) throws {
        Atest atest = new Atest();
        Class<?> clazz = atest.getClass();
        //判断类是否有注释
        if(clazz.isAnnotationPresent(MyAnnotation.class)) {
            System.out.println("有注解");
            System.out.println(clazz.newInstance().toString());
        }
}

显示结果:

有注解
class test.Atest

若果对于注释的元注释,@Retention(RetentionPolicy.RUNTIME)改成@Retention(RetentionPolicy.SOURCE),则JVM识别不到注释,一般使用运行时状态。
综上,注释有什么用处,可以在JVM运行时进行一系列操作,反射一起使用,获取被注释的类进行相关的处理。在Spring中的注释都是这样使用。可以使得一些我们自己完成的工作让框架完成

Class 对象

Java的反射机制中重要的部分就是获取Class对象,对于Class对象描述了类的信息信息,可以通过获取Class队形获取类的成员,方法,注解等

获取Class对象的三种方法:

  1. Class.forName(“类的路径”);
  2. 类名称.getClass();
  3. new Class().class();

那么,获取到的class类可以干什么?
可以获取对应的类的构造方法,一般方法,和成员

Constructor constructors = clazz.getConstructor(null);//拿到构造函数
Field[] fields = clazz.getDeclaredFields();//拿到它定义的所有字段
Method[] methods = clazz.getDeclaredMethods();//拿到它定义的所有方法

还有很多的方法:
getName():获得类的完整名字。
  getFields():获得类的public类型的属性。
  getDeclaredFields():获得类的所有属性。包括private 声明的和继承类
  getMethods():获得类的public类型的方法。
  getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类
  getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。
  getConstructors():获得类的public类型的构造方法。
  getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
  newInstance():通过类的不带参数的构造方法创建这个类的一个对象。

反射

JAVA反射机制是在运行状态中,**对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;**这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

反射机制可以打破类的访问权限的限制,可以运行private方法和访问修改private的属性。
执行私有方法和访问私有属性的时候需要加上一句method.setAccessible(true);打破访问权限的限制

新建一个Ctest的类,里面与私有方法和私有属性

public class Ctest {
	private int code;
	private String name;
	Ctest(int code,String name) {
		this.code = code;
		this.name = name;
	}
	private String printCtest(){
		return "hello"+name + code;
	}
}

在类Btest里面进行访问:

public class Btest {

	public static void main(String[] args){
		
		Atest atest = new Atest();
		Class<?> clazz = atest.getClass();
		Field[] fields = clazz.getDeclaredFields();
		//判断类是否有注释
		if(clazz.isAnnotationPresent(MyAnnotation.class)) {
			System.out.println("有注解");
			
			System.out.println(clazz.newInstance().toString());
		}
		
        Ctest c = new Ctest(1,"htx");
        Class<?> cclazz = Ctest.class;//获取class对象
        Field[] files = cclazz.getDeclaredFields();//获取所有权限的方法属性
        Method[] methods = cclazz.getDeclaredMethods();//获取所有权限的方法
        for(Method method : methods){
        	System.out.println("方法" + method.getName());
        }
        for(Field file: files){
        	System.out.println("属性" + file);
        }
        try {
            //修改属性
        	Field field = cclazz.getDeclaredField("name");
        	field.setAccessible(true);
        	System.out.println("修改前的属性值是:" + field.get(c).toString()); 
        	field.set(c,"world");
        	System.out.println("修改后的属性值是:" + field.get(c).toString()); 
        	//执行方法
            Method method = cclazz.getDeclaredMethod("printCtest");
            method.setAccessible(true);
			Object temp = method.invoke(c);
			System.out.println("执行方法" + temp.toString());
		} catch (Exception e) {
			e.printStackTrace();
		}        
	}
}
输出结果为:
方法printCtest
属性private int test.Ctest.code
属性private java.lang.String test.Ctest.name
修改前的属性值是:htx
修改后的属性值是:world
执行方法helloworld1

结论:

  1. 注解和反射一般是相互使用的,Spring的对象注入就是这样实现的。
  2. 利用反射可以打破原有的访问限制,但是使用不当也会影响运行速度。反射可以修改对象的属性和执行对象的方法。也可以获取类的所有信息。