Java反射学习:深入学习Java反射机制

时间:2023-03-08 16:15:22

一、Java反射的理解(反射是研究框架的基础之一)

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

二、逐步分析

参考:https://blog.****.net/u012585964/article/details/52011138

1、关于Class

  1、Class是一个类,一个描述类的类(也就是描述类本身),封装了描述方法的Method,描述字段的Filed,描述构造器的Constructor等属性
    2、对象照镜子后(反射)可以得到的信息:某个类的数据成员名、方法和构造器、某个类到底实现了哪些接口。
    3、对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。 一个 Class 对象包含了特定某个类的有关信息。
    4、Class 对象只能由系统建立对象
    5、一个类在 JVM 中只会有一个Class实例

下面创建一个例子:后面的分析均以此案例为基准。

创建一个空接口:(后面会不断补充)

 package com.xfwl.reflection;

 public interface IHuman {

 }

创建一个空基类:(后面会不断补充)

 package com.xfwl.reflection;

 public class Human {

 }

创建一个子类:(后面会不断补充)

 package com.xfwl.reflection;

 public class Person extends Human implements IHuman {
/**
* 默认default修饰
*/
String name;
/**
* private修饰
*/
private int age;
/**
* public修饰
*/
public char sex='M';
/**
* 无参构造
*/
public Person(){
System.out.println("无参构造!!!");
}
/**
* 有参构造
*/
public Person(String name,int age,char sex){
System.out.println("有参构造!!!");
this.name=name;
this.age=age;
this.sex=sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}

2、反射获取类对象的三种方式(通过一个Junit测试来说明)

 package com.xfwl.reflection;

 import org.junit.Test;
/**
* 测试类
* @function
* @author 小风微凉
* @time 2018-6-3 下午12:28:38
*/
public class TestAction {
/**
* 反射机制获取类有三种方法
*/
@Test
public void testGetClass() throws ClassNotFoundException {
Class clazz = null; //1 直接通过类名.Class的方式得到
clazz = Person.class;
System.out.println("通过类名: " + clazz); //2 通过对象的getClass()方法获取,这个使用的少(一般是传的是Object,不知道是什么类型的时候才用)
Object obj = new Person();
clazz = obj.getClass();
System.out.println("通过getClass(): " + clazz); //3 通过全类名获取,用的比较多,但可能抛出ClassNotFoundException异常
clazz = Class.forName("com.xfwl.reflection.Person");
System.out.println("通过全类名获取: " + clazz);
}
}

运行结果:

通过类名: class com.xfwl.reflection.Person
无参构造!!!
通过getClass(): class com.xfwl.reflection.Person
通过全类名获取: class com.xfwl.reflection.Person

特别注意:(以下2中方式不会调用构造方法,因为没有实例化操作)  

//1 直接通过类名.Class的方式得到
clazz = Person.class;
//3 通过全类名获取,用的比较多,但可能抛出ClassNotFoundException异常
clazz = Class.forName("com.xfwl.reflection.Person");

3、利用newInstance创建对象:调用的类必须有无参的构造器

 /**
* Class类的newInstance()方法,创建类的一个对象。
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
*/
@Test
public void testNewInstance()
throws ClassNotFoundException, IllegalAccessException, InstantiationException { Class clazz = Class.forName("com.xfwl.reflection.Person"); //使用Class类的newInstance()方法创建类的一个对象
//实际调用的类的那个 无参数的构造器(这就是为什么写的类的时候,要写一个无参数的构造器,就是给反射用的)
//一般的,一个类若声明了带参数的构造器,也要声明一个无参数的构造器
Object obj = clazz.newInstance();
System.out.println(obj);
}

测试结果:

Java反射学习:深入学习Java反射机制

那么,如果删除Person.java中的无参构造,继续测试,结果如下:

Java反射学习:深入学习Java反射机制

4、ClassLoader类加载器

Java反射学习:深入学习Java反射机制
类加载器详解:
http://blog.****.net/ochangwen/article/details/51473120 
     /**
* ClassLoader类装载器
*/
@Test
public void testClassLoader1() throws ClassNotFoundException, IOException {
//1、获取一个系统的类加载器
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println("系统的类加载器-->" + classLoader); //2、获取系统类加载器的父类加载器(扩展类加载器(extensions classLoader))
classLoader = classLoader.getParent();
System.out.println("扩展类加载器-->" + classLoader); //3、获取扩展类加载器的父类加载器
//输出为Null,无法被Java程序直接引用
classLoader = classLoader.getParent();
System.out.println("启动类加载器-->" + classLoader); //4、测试当前类由哪个类加载器进行加载 ,结果就是系统的类加载器
classLoader = Class.forName("com.xfwl.reflection.Person").getClassLoader();
System.out.println("当前类由哪个类加载器进行加载-->"+classLoader); //5、测试JDK提供的Object类由哪个类加载器负责加载的
//输出为Null,无法被Java程序直接引用
classLoader = Class.forName("java.lang.Object").getClassLoader();
System.out.println("JDK提供的Object类由哪个类加载器加载-->" + classLoader);
}

测试结果:

系统的类加载器-->sun.misc.Launcher$AppClassLoader@18b4aac2
扩展类加载器-->sun.misc.Launcher$ExtClassLoader@614c5515
启动类加载器-->null
当前类由哪个类加载器进行加载-->sun.misc.Launcher$AppClassLoader@18b4aac2
JDK提供的Object类由哪个类加载器加载-->null

5、反射机制通过加载器获取流对象:getResourceAsStream方法

 /**
* 反射机制通过加载器获取流对象:getResourceAsStream方法
* @throws ClassNotFoundException
* @throws IOException
*/
@Test
public void testGetResourceAsStream() throws ClassNotFoundException, IOException {
//调用getResourceAsStream 获取类路径下的文件对应的输入流
/**
* 特别说明:
* getResourceAsStream("path"),path的路径和new Person()的位置有关
*/ InputStream in = new Person().getClass().getClassLoader()
.getResourceAsStream("com/xfwl/reflection/test.properties");
System.out.println("in: " +in); Properties properties = new Properties();
properties.load(in);
System.out.println("文件内容:"+properties);
System.out.println("name: "+properties.getProperty("name"));
System.out.println("age: " + properties.getProperty("age"));
System.out.println("sex: "+properties.getProperty("sex"));
System.out.println("desc: " + properties.getProperty("desc"));
}

test.properties文件内容如下:文件编码格式:ISO-8859-1

name=\u5C0F\u98CE\u5FAE\u51C9\u0087\u0089
age=23
sex=M
desc=\u53CD\u5C04\u673A\u5236\u5B66\u4E60

运行结果:(直接解析会出现乱码问题,这个可以通过new String(乱码格式处理参数)来处理)

无参构造!!!
in: java.io.BufferedInputStream@215be6bb
文件内容:{age=23, name=小风微凉??, sex=M, desc=反射机制学习}
name: 小风微凉??
age: 23
sex: M
desc: 反射机制学习

6、反射机制获取类中的方法:Method: 对应类中的方法

现在给Person类添加一个private 方法、一个public 方法、一个defaut 方法、一个protected方法

 /**
* Java权限有四个,分别为public,protected,默认,private,其开放程度依次降低
* public可供所有类访问
* protected继承可见
* private只能类本身内部的方法可以访问
*/
public void method_public(){
System.out.println("method_public");
}
public void method_public_2(String name,int age,char sex){//public 带参数
System.out.println("method_public_2");
String info="Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
System.out.println(info);
}
protected void method_protected(){
System.out.println("method_protected");
}
protected void method_protected_2(String info){//protected 带参数
System.out.println("method_protected_2:"+info);
}
void method_default(){
System.out.println("method_default");
}
void method_default_2(String info){//默认修饰符 带参数
System.out.println("method_default_2:"+info);
}
private void method_private(){
System.out.println("method_private");
}
private void method_private_2(String info){//private 带参数
System.out.println("method_private_2:"+info);
}

开始测试如何通过反射机制使用这些方法

 /**
* 如何通过反射机制使用这些方法
* @throws ClassNotFoundException
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws InstantiationException
* @throws InvocationTargetException
*/
@Test
public void testMethod() throws ClassNotFoundException, NoSuchMethodException,
IllegalAccessException, InstantiationException, InvocationTargetException {
Class clazz = Class.forName("com.xfwl.reflection.Person"); //1、得到clazz 对应的类中有哪些方法,不能获取private方法
Method[] methods =clazz.getMethods();
System.out.println("通过反射机制可以拿到的方法:clazz.getMethods()");
for (Method method : methods){
System.out.println(method.getName());
}
System.out.println("<-------------------------->"); //2、获取所有的方法(且只获取当着类声明的方法,包括private方法)
Method[] methods2 = clazz.getDeclaredMethods();
System.out.println("通过反射机制可以拿到的方法:clazz.getDeclaredMethods()");
for (Method method : methods2){
System.out.println(method.getName());
}
System.out.println("<-------------------------->");
System.out.println("通过反射机制可以拿到指定的方法:clazz.getDeclaredMethod()");
//3、获取指定的方法
Method method1= clazz.getDeclaredMethod("method_private");
System.out.println("private 无参:"+method1); Method method2 = clazz.getDeclaredMethod("method_private_2",String.class);//第一个参数是方法名,后面的是方法里的参数
System.out.println("private 有参:"+method2); Method method3 = clazz.getDeclaredMethod("method_public_2",String.class,int.class,char.class);//第一个参数是方法名,后面的是方法里的参数
System.out.println("public 有参:"+method2); //4、执行方法!
Object obj = clazz.newInstance();
method3.invoke(obj, "小风微凉", 23,'M'); //执行方法:invoke(类对象)
}

测试结果:

通过反射机制可以拿到的方法:clazz.getMethods():不能获取private/protected/default方法
toString
getName
setName
method_public_2
setAge
method_public
getSex
getAge
setSex
wait
wait
wait
equals
hashCode
getClass
notify
notifyAll
<-------------------------->
通过反射机制可以拿到的方法:clazz.getDeclaredMethods():获取所有修饰权限的方法
toString
getName
setName
method_private_2
method_private
method_public_2
setAge
method_public
method_default
method_default_2
getSex
getAge
setSex
method_protected
method_protected_2
<-------------------------->
通过反射机制可以拿到指定的方法:clazz.getDeclaredMethod()
private 无参:private void com.xfwl.reflection.Person.method_private()
private 有参:private void com.xfwl.reflection.Person.method_private_2(java.lang.String)
public 有参:private void com.xfwl.reflection.Person.method_private_2(java.lang.String)
无参构造!!!
method_public_2
Person{name='小风微凉', age=23, sex='M'}

继续分析一下:

JDK中的获取方法

获取方法:默认只能获取public修饰的方法

 @CallerSensitive
public Method[] getMethods() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return copyMethods(privateGetPublicMethods());
}

获取方法:所有修饰权限的方法都可以获得

@CallerSensitive
public Method[] getDeclaredMethods() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return copyMethods(privateGetDeclaredMethods(false));
}

获取方法:获取指定的方法(所有修饰权限)

     /**
* @jls 8.2 Class Members
* @jls 8.4 Method Declarations
* @since JDK1.1
*/
@CallerSensitive
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
}
return method;
}

  分析一下上面这个方法:

    String name:方法的名称

       Class<?>... parameterTypes:一个或多个方法参数的类型,注意要一一对应,否则会报错的哦

执行方法:invoke(方法对象,方法实际参数)

 @CallerSensitive
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}

7、反射机制获取类中的方法:Method: 对应基类或接口中的方法

上面分析了,如何通过反射拿到当前本类里面的各个修饰权限的方法,下面来继续分析一下,如何读取父类或实现的接口中的方法:

现在,给父类添加一些方法,在接口中定义一些方法:

接口中的方法声明:

 package com.xfwl.reflection;

 public interface IHuman {

     void eat();
void eat(String info);
}

Person.java实现接口方法

     public void eat() {
System.out.println("实现接口的方法:eat()无参:");
}
public void eat(String info) {
System.out.println("实现接口的方法:eat()有参:"+info);
}

父类中的方法定义:

 package com.xfwl.reflection;

 public class Human {
public void play_public(){
System.out.println("public无参:play_public");
}
public void play_public_2(String info){
System.out.println("public有参:play_public2:"+info);
}
protected void play_protected(){
System.out.println("protected无参:play_protected");
}
protected void play_protected_2(String info){
System.out.println("protected有参:play_protected_2:"+info);
}
void play_default(){
System.out.println("默认修饰符无参:play_default");
}
void play_default_2(String info){//默认修饰符 带参数
System.out.println("默认修饰符有参:play_default_2:"+info);
}
private void play_private(){
System.out.println("private无参:play_private");
}
private void play_private_2(String info){
System.out.println("private有参:play_private_2:"+info);
}
}

开始测试:

1、拿到当前Person类反射对象,能否得到接口中的方法

 /**
* 反射机制获取类中的方法:Method: 对应基类或接口中的方法
* @throws ClassNotFoundException
* @throws SecurityException
* @throws NoSuchMethodException
* @throws InstantiationException
* @throws InvocationTargetException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
@Test
public void testInterfaceOrSupperClass() throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException{
Class clazz = Class.forName("com.xfwl.reflection.Person");
//拿到当前Person类反射对象,能否得到接口中的方法
for (Method method : clazz.getMethods()){
System.out.println(method.getName());
}
//获取当前类实现的接口中的方法
Method method1= clazz.getDeclaredMethod("eat");
Method method2= clazz.getDeclaredMethod("eat",String.class);
//执行
method1.invoke(clazz.newInstance());
method2.invoke(clazz.newInstance(),"eat有参数");
}

测试结果:(可以拿到实现的接口中的方法并执行)

toString
getName
setName
eat
eat
method_public_2
setAge
getSex
setSex
getAge
method_public
play_public_2
play_public
wait
wait
wait
equals
hashCode
getClass
notify
notifyAll
无参构造!!!
实现接口的方法:eat()无参:
无参构造!!!
实现接口的方法:eat()有参:eat有参数

2、拿到当前Person类反射对象,能否获取父类中的方法

通过当前反射对象,拿到父类反射对象

 Class clazz = Class.forName("com.xfwl.reflection.Person");
Class superClazz = clazz.getSuperclass();
 /**
* 反射机制获取类中的方法:Method: 对应基类或接口中的方法
* @throws ClassNotFoundException
* @throws SecurityException
* @throws NoSuchMethodException
* @throws InstantiationException
* @throws InvocationTargetException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
@Test
public void testInterfaceOrSupperClass() throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException{
Class clazz = Class.forName("com.xfwl.reflection.Person");
System.out.println("<------------父类中能够使用的公共权限的方法------------------------>");
//拿到当前Person类反射对象,能否获取父类中的方法
Class superClazz = clazz.getSuperclass();
for (Method method : superClazz.getMethods()){
System.out.println(method.getName());
}
System.out.println("<----------------父类中所有的方法:仅仅父类中的方法-------------------->");
//拿到父类中的所有权限修饰符修饰的方法
for (Method method : superClazz.getDeclaredMethods()){
System.out.println(method.getName());
}
}

运行结果:

<------------父类中能够使用的公共权限的方法------------------------>
play_public
play_public_2
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll
<----------------父类中所有的方法:仅仅父类中的方法-------------------->
play_private_2
play_public
play_public_2
play_private
play_protected
play_default
play_protected_2
play_default_2

那么可以执行父类中的方法吗?

 //是否可以通过子类对象拿到父类中的方法
Method method3= clazz.getDeclaredMethod("play_public");
Method method4= clazz.getDeclaredMethod("play_public_2",String.class);
Method method5= clazz.getDeclaredMethod("play_private");
Method method6= clazz.getDeclaredMethod("play_private_2",String.class);

上面代码报错,说明不可以,public修饰的方法也拿不到

       Method  method7= superClazz.getDeclaredMethod("play_public");
Method method8= superClazz.getDeclaredMethod("play_public_2",String.class);
Method method9= superClazz.getDeclaredMethod("play_private");
Method method10= superClazz.getDeclaredMethod("play_private_2",String.class);

上面代码正常执行,说明父类的反射对象可以拿到自己的public或private方法

  //使用子类的反射对象执行方法
method7.invoke(clazz.newInstance());
method8.invoke(clazz.newInstance(), "play_public_2有参数");
method9.invoke(clazz.newInstance()); //无法执行,Junit报错
method10.invoke(clazz.newInstance(), "play_private_2有参数");//无法执行,Junit报错
 //使用父类的反射对象执行方法
method7.invoke(superClazz.newInstance());
method8.invoke(superClazz.newInstance(), "play_public_2有参数");
method9.invoke(superClazz.newInstance()); //无法执行,Junit报错
method10.invoke(superClazz.newInstance(), "play_private_2有参数");//无法执行,Junit报错

上面代码执行部分报错,说明通过子类的反射对象和拿到的父类反射对象,也仅仅只能执行public和protected和default默认修饰的方法,不能执行private修饰的方法

7、反射机制获取类中的字段属性:Field字段

    /**
* 默认default修饰
*/
String name;
/**
* private修饰
*/
private int age;
/**
* public修饰
*/
public char sex='M';
/**
* protected修饰
*/
protected boolean isBeauty=true;

开始测试:如何获取

  /**
* 反射机制获取类中的字段属性:Field字段
* @throws ClassNotFoundException
* @throws SecurityException
* @throws NoSuchFieldException
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
@Test
public void testFiled() throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{
//拿到反射Class对象
Class clazz = Class.forName("com.xfwl.reflection.Person");
//获取Field的数组,私有字段也能获取
Field[] fields = clazz.getDeclaredFields();
System.out.println("<----遍历拿到字段:开始--------------------->");
for (Field field: fields) {
System.out.println(field.getName());
}
System.out.println("<------------获取指定名字的Field以及类型--------------------------->");
//获取指定名字的Field(如果是私有的,见下面的4)
Field field1 = clazz.getDeclaredField("name");
System.out.println("获取指定Field名=: " + field1.getName()+",类型:"+field1.getType());
Field field2 = clazz.getDeclaredField("age");
System.out.println("获取指定Field名=: " + field2.getName()+",类型:"+field2.getType());
Field field3 = clazz.getDeclaredField("sex");
System.out.println("获取指定Field名=: " + field3.getName()+",类型:"+field3.getType());
Field field4 = clazz.getDeclaredField("isBeauty");
System.out.println("获取指定Field名=: " + field4.getName()+",类型:"+field4.getType()); System.out.println("<----------获取指定对象的Field的值 ----------------------------->");
Person person = new Person("小风微凉", 12,'M');
//获取指定对象的Field的值
Object val = field1.get(person);
System.out.println("获取指定对象字段'name'的Field的值=: " + val); System.out.println("<----------设置指定对象的Field的值----------------------------->");
//设置指定对象的Field的值
field1.set(person, "反射学习A");
System.out.println("设置指定对象字段'name'的Field的值=: " + person.name); System.out.println("<----------若该字段是私有的,需要调用setAccessible(true)方法----------------------------->");
//若该字段是私有的,需要调用setAccessible(true)方法
field2 = clazz.getDeclaredField("age");
field2.setAccessible(true);
System.out.println("获取指定私有字段名=: " + field2.getName());
}

测试结果:

<----遍历拿到字段:开始--------------------->
name
age
sex
isBeauty
<------------获取指定名字的Field以及类型--------------------------->
获取指定Field名=: name,类型:class java.lang.String
获取指定Field名=: age,类型:int
获取指定Field名=: sex,类型:char
获取指定Field名=: isBeauty,类型:boolean
<----------获取指定对象的Field的值 ----------------------------->
有参构造!!!
获取指定对象字段'name'的Field的值=: 小风微凉
<----------设置指定对象的Field的值----------------------------->
设置指定对象字段'name'的Field的值=: 反射学习A
<----------若该字段是私有的,需要调用setAccessible(true)方法----------------------------->
获取指定私有字段名=: age

8、反射机制获取类中的构造器:构造器(Constructor)

Person的构造器

 /**
* 无参构造
*/
public Person(){
System.out.println("无参构造!!!");
}
/**
* 有参构造
*/
public Person(String name,int age,char sex){
System.out.println("有参构造!!!");
this.name=name;
this.age=age;
this.sex=sex;
}

@Test测试

 /**
* 构造器:开发用的比较少
*/
@Test
public void testConstructor() throws ClassNotFoundException, NoSuchMethodException,
IllegalAccessException, InvocationTargetException, InstantiationException {
String className = "com.xfwl.reflection.Person";
Class<Person> clazz = (Class<Person>) Class.forName(className); //1.获取Constructor对象
Constructor<Person>[] constructors =
(Constructor<Person>[]) Class.forName(className).getConstructors(); System.out.println("<-------------打印所有的构造器---------------------->");
for (Constructor<Person> constructor: constructors) {
System.out.println(constructor);
}
System.out.println("<------------------------------------------------>");
Constructor<Person> constructor = clazz.getConstructor(String.class, int.class,char.class);
System.out.println("拿到指定的-->" + constructor); //2.调用构造器的newInstance()方法创建对象
Object obj= constructor.newInstance("changwen", 11,'M');
}

运行结果:

<-------------打印所有的构造器---------------------->
public com.xfwl.reflection.Person()
public com.xfwl.reflection.Person(java.lang.String,int,char)
<------------------------------------------------>
拿到指定的-->public com.xfwl.reflection.Person(java.lang.String,int,char)
有参构造!!!

9、反射机制获取类中的注解:注解(Annotation)

 •从 JDK5.0 开始,Java 增加了对元数据(MetaData)的支持,也就是Annotation(注释) 
    •Annotation其实就是代码里的特殊标记,这些标记可以在编译,类加载, 运行时被读取,并执行相应的处理.通过使用Annotation,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息. 
    •Annotation 可以像修饰符一样被使用,可用于修饰包,类,构造器, 方法,成员变量, 参数,局部变量的声明,这些信息被保存在Annotation的 “name=value”对中. 
    •Annotation能被用来为程序元素(类,方法,成员变量等)设置元数据 

基本的 Annotation

•使用 Annotation时要在其前面增加@符号,并把该Annotation 当成一个修饰符使用.用于修饰它支持的程序元素 
•三个基本的Annotation: 
    –@Override:限定重写父类方法,该注释只能用于方法 
    –@Deprecated:用于表示某个程序元素(类,方法等)已过时 
    –@SuppressWarnings:抑制编译器警告. 

自定义 Annotation

    •定义新的 Annotation类型使用@interface关键字 
    •Annotation 的成员变量在Annotation 定义中以无参数方法的形式来声明.其方法名和返回值定义了该成员的名字和类型. 
    •可以在定义Annotation的成员变量时为其指定初始值,指定成员变量的初始值可使用default关键字 
    •没有成员定义的Annotation称为标记;包含成员变量的Annotation称为元数据Annotation 
 package com.xfwl.reflection;

 3 import java.lang.annotation.ElementType;
4 import java.lang.annotation.Retention;
5 import java.lang.annotation.RetentionPolicy;
6 import java.lang.annotation.Target; public class Person extends Human implements IHuman { @Retention(RetentionPolicy.RUNTIME) //运行时检验
@Target(value = {ElementType.METHOD}) //作用在方法上
public @interface AgeValidator { int min();
int max();
}
//......其余部分省略
}
 /**
* 自定义一个注解:检查年龄范围
* @function
* @author 小风微凉
* @time 2018-6-3 下午3:56:03
*/
@Retention(RetentionPolicy.RUNTIME) //运行时检验
@Target(value = {ElementType.METHOD}) //作用在方法上
public @interface AgeValidator {
int min();
int max();
}

@Test测试

  /**
* 通过反射才能获取注解
*/
@Test
public void testAnnotation() throws Exception {
//这样的方式不能使用注解
/*Person person3 = new Person();
person3.setAge(10);*/ //拿到反射Class对象
String className = "com.xfwl.reflection.Person";
Class clazz = Class.forName(className);
Object obj = clazz.newInstance();
//拿到指定方法
Method method = clazz.getDeclaredMethod("setAge",int.class);
int val =40; //获取注解
Annotation annotation = method.getAnnotation(AgeValidator.class);
if (annotation != null){
if (annotation instanceof AgeValidator){
AgeValidator ageValidator = (AgeValidator) annotation; if (val< ageValidator.min() || val>ageValidator.max()){
throw new RuntimeException("数值超出范围");
}
}
}
//执行方法
method.invoke(obj, val);
System.out.println(obj);
}

运行结果:

无参构造!!!
Person{name='null', age=40, sex='M'}

获取指定注解:

 //获取注解
Annotation annotation = method.getAnnotation(AgeValidator.class);

获取所有注解:

  //获取所有注解
Annotation[] arr=clazz.getDeclaredAnnotations();

提取 Annotation信息

•JDK5.0 在 java.lang.reflect包下新增了 AnnotatedElement接口,该接口代表程序中可以接受注释的程序元素
•当一个 Annotation类型被定义为运行时Annotation后,该注释才是运行时可见,当 class文件被载入时保存在 class文件中的 Annotation才会被虚拟机读取
•程序可以调用AnnotationElement对象的如下方法来访问 Annotation信息
–获取 Annotation实例:
getAnnotation(Class<T> annotationClass)

JDK 的元Annotation

•JDK 的元Annotation 用于修饰其他Annotation 定义
•@Retention:只能用于修饰一个 Annotation定义,用于指定该 Annotation可以保留多长时间,@Rentention包含一个RetentionPolicy类型的成员变量,使用 @Rentention时必须为该 value成员变量指定值:
    –RetentionPolicy.CLASS:编译器将把注释记录在 class文件中.当运行 Java程序时,JVM 不会保留注释.这是默认值
    –RetentionPolicy.RUNTIME:编译器将把注释记录在class文件中. 当运行 Java 程序时, JVM 会保留注释. 程序可以通过反射获取该注释
    –RetentionPolicy.SOURCE:编译器直接丢弃这种策略的注释
•@Target: 用于修饰Annotation 定义,用于指定被修饰的 Annotation能用于修饰哪些程序元素.@Target 也包含一个名为 value的成员变量.
•@Documented:用于指定被该元 Annotation修饰的 Annotation类将被 javadoc工具提取成文档.
•@Inherited:被它修饰的 Annotation将具有继承性.如果某个类使用了被@Inherited 修饰的Annotation, 则其子类将自动具有该注释 
后续补充整理:
(1)通过getDeclaredMethod拿到的方法,可以获取个中修饰符修饰的方法名称和对象,但是private的方法无法invoke执行
UserBean.java
 package com.xfwl.reflect;

 public class UserBean {

     private String uname;
private String upwd;
public UserBean(){
this.setUname("xfwl");
this.setUpwd("123456");
}
public UserBean(String uname,String upwd){
this.setUname(uname);
this.setUpwd(uname);
}
public void logIn(UserBean user){
System.out.println("用户登录:uname="+this.getUname()+",upwd="+this.getUpwd());
}
public String getUname() {
return uname;
}
private void logOut(UserBean user){
System.out.println("用户退出:uname="+this.getUname()+",upwd="+this.getUpwd());
}
public void setUname(String uname) {
this.uname = uname;
} public String getUpwd() {
return upwd;
} public void setUpwd(String upwd) {
this.upwd = upwd;
}
}

测试类:ReflectAction.java

 package com.xfwl.reflect;

 import java.lang.reflect.Method;

 public class ReflectAction {
/**
* @param args
*/
public static void main(String[] args) {
UserBean jack=null;
UserBean tom=null;
Class tomC=null;
try{
jack=(UserBean)Class.forName("com.xfwl.reflect.UserBean").newInstance();
//jack.logIn(); tomC=Class.forName("com.xfwl.reflect.UserBean");
tom=(UserBean) tomC.newInstance();
Method[] methods = tomC.getDeclaredMethods();
for (Method method : methods){
if("logOut".equals(method.getName())){
method=tomC.getDeclaredMethod(method.getName(),UserBean.class);
System.out.println(method);
method.invoke(tomC.newInstance(),tom);
}
}
}catch(Exception e){ } } }

运行结果:没有执行:logOut()

修改:

 public void logOut(UserBean user){
System.out.println("用户退出:uname="+this.getUname()+",upwd="+this.getUpwd());
}

即可执行:logOut()