Java=反射 (Reflect),注解(Annotation)

反射 :反射基本上是所有框架的底层

注解

 

一。反射

1.类加载器:

类的加载:当我们的程序在运行后,第一次使用某个类的时候,会将此类的class文件读取到内存,并将此类的所有信息存储到一个Class对象中。

注:

Class对象指的是 java.lang.Class类的对象,此类由Java类库提供,专门用于存储类的信息

我们程序中可以通过 类名.class 或者 对象.getClass 方法获取这个Class对象

2.类的加载时机:

1. 创建类的实例。
2. 调用类的静态变量,或者为静态变量赋值。
3. 调用类的静态方法。
4. 使用反射方式来强制创建某个类或接口对应的 java.lang.Class 对象。
5. 初始化某个类的子类。
6. 直接使用 java.exe 命令来运行某个主类。
以上几种情况的任何一种,都可以让 JVM 将一个类加载到方法区。

3.类加载器:是负责将磁盘上的某个class文件读取到内存并生成Class的对象

Java中有三种类加载器,他们分别用于加载不同种类的class;

启动类加载器 (Bootstrap ClassLoader) :用于加载系统类库 \bin 目录下的
class ,例如: rt.jar
扩展类加载器 (Extension ClassLoader) :用于加载扩展类库 \lib\ext 目录下的
class
应用程序类加载器 (Application ClassLoader) :用于加载我们自定义类的加载器。
 
4.双亲委派机制

上图展示了 类加载器 的层次关系,这种关系称为 类加载器 的 双亲委派模型

双亲委派模型中,除了顶层的启动类加载器外,其余的类加载器都应当有自己的父级类加载器

这种关系不能通过 继承 实现。通常是通过 组合 实现的,通过 组合  来表示父级类加载器

双亲委派模型 的工作过程:

某个"类加载器"收到类加载的请求,它首先不会尝试自己去加载这个类,而是把请求交给父
级类加载器。
因此,所有的类加载的请求最终都会传送到顶层的"启动类加载器"中。
如果"父级类加载器"无法加载这个类,然后子级类加载器再去加载

5.双亲委派机制的好处

双亲委派机制的一个显而易见的好处是: Java 的类随着它的类加载器一起具备了一种带有优先级的层次
关系。例如: java.lang.Object 。它存放在 rt.jar 中。无论哪一个类加载器要加载这个类,最终都是委派
给处于顶端的 " 启动类加载器 " 进行加载,因此 java.lang.Object 类在程序的各种类加载器环境中都是同一
个类。
相反,如果没有 " 双亲委派机制 " ,如果用户自己编写了一个 java.lang.Object ,那么当我们编写其它类
时,这种隐式的继承使用的将会是用户自己编写的 java.lang.Object 类,那将变得一片混乱。

(2)反射

通过反射技术对象类进行 解剖 得到了 类的所有成员

反射 是一种机制,利用该机制可以在程序运行过程中对类进行解剖 并操作类中的所有成员--成员变量,成员方法,构造方法

使用反射操作类成员的前提

要获得 该类字节码文件对象,就是Class对象

反射在实际开发中的应用:

开发 IDEA ( 集成开发环境 ) ,比如 IDEA , Eclipse
各种框架的设计和学习 比如 Spring Hibernate Struct Mybaits ....

1.Class对象的获取方法

三种获取方法:

方式 1 : 通过类名 . class 获得
方式 2 :通过对象名 . getClass () 方法获得
方式 3 :通过 Class 类的静态方法获得: static Class forName ( " 类全名 " )
每一个类的 Class 对象都只有一个。
Java定义了三种获取Class对象的方式
public class GetClassDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取一个类的Class对象的三种方式
        //1.通过类的一个静态成员 class
        Class clazz1 = Dog.class;
        System.out.println(clazz1);
        //2.通过该类的一个对象,获取该类的Class对象
        Dog dd = new Dog();
        Class clazz2 = dd.getClass();
        System.out.println(clazz2);
        //3.通过反射强制加载该类,并获取该类的Class对象
        Class clazz3 = Class.forName("com.wsl.demo02_GetClass.Dog");
        System.out.println(clazz3);
        //注意:以上是三种获取Dog类Class对象的方式,并不是获取三个Class对象,实际上他们获取的是同一个Class对象
        System.out.println(clazz1 == clazz2);
        System.out.println(clazz1 == clazz3);
        System.out.println(clazz2 == clazz3);
    }
}
输出结果:
class com.wsl.demo02_GetClass.Dog
class com.wsl.demo02_GetClass.Dog
class com.wsl.demo02_GetClass.Dog
true
true
true

2.Class类中常用方法:

String getSimpleName (); 获得类名字符串:类名
String getName (); 获得类全名:包名 + 类名
T newInstance () ; 创建 Class 对象关联类的对象
public class ClassMethodDemo {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        Class clazz = Dog.class;
        //Class对象中三个方法
        //1.获取全限定类名
        System.out.println(clazz.getName());
        //2.获取类名
        System.out.println(clazz.getSimpleName());
        //3.创建Class对象所代表的那个类的对象,底层实际上使用Dog的无参构造
        Dog dog = (Dog) clazz.newInstance();
        System.out.println(dog);
    }
}
输出结果:
com.wsl.demo03_ClassMathod.Dog
Dog
Dog{age=0, name='null'}

4.反射 操作构造方法

反射之操作构造方法的目的
获得Constructor对象来创建类的对象。
Constructor类概述
类中的每一个构造方法都是一个Constructor类的对象

Class类中与Constructor相关方法“

1. Constructor getConstructor(Class... parameterTypes)
 * 根据参数类型获得对应的Constructor对象。 
* 只能获得public修饰的构造方法 
2. Constructor getDeclaredConstructor(Class... parameterTypes)
 * 根据参数类型获得对应的Constructor对象 
* 可以是public、protected、(默认)、private修饰符的构造方法。 
3. Constructor[] getConstructors() 
* 获得类中的所有构造方法对象,只能获得public的 
4. Constructor[] getDeclaredConstructors()
 * 获得类中的所有构造方法对象 
* 可以是public、protected、(默认)、private修饰符的构造方法。

Constructor对象常用方法:

1. T newInstance(Object... initargs)
根据指定的参数创建对象
 2. void setAccessible(true) 
设置"暴力反射"——是否取消权限检查,true取消权限检查,false表示不取消

反射获取构造方法:

public Constructor getConstructor(Class... parameterTypes); 获取单个"public"构造
public Constructor getDeclaredConstructor(Class... parameterTypes);获取单个"任意修饰"构造
    
    
public Constructor[] getConstructors(); 获取所有"public"构造
public Constructor[] getDeclaredConstructors();获取所有"任意修饰"构造    
    
public class GetConstructorDemo {
    public static void main(String[] args) throws NoSuchMethodException {
        //1.反射第一步,先获取Class对象
        Dog dd = new Dog();
        Class clazz = dd.getClass();
        //2.获取单个"public"的构造方法
        Constructor con1 = clazz.getConstructor();
        System.out.println(con1);

//        Constructor con2 = clazz.getConstructor(int.class, String.class);
//        System.out.println(con2);

        //3.获取单个"任意修饰符"的构造方法
        Constructor con3 = clazz.getDeclaredConstructor(int.class, String.class);
        System.out.println(con3);
        System.out.println("========================");
        //4.获取所有"public"构造
        Constructor[] cons = clazz.getConstructors();
        System.out.println(cons.length);
        for (Constructor con : cons) {
            System.out.println(con);
        }
        //5.获取所有"任意修饰符"构造
        System.out.println("========================");

        Constructor[] conss = clazz.getDeclaredConstructors();
        System.out.println(conss.length);
        for (Constructor con : conss) {
            System.out.println(con);
        }
    }
}    

使用构造方法创建对象

语法:
	构造方法对象.newInstance(参数);
    @Test
    public void test02() throws Exception {
        //使用构造
        //1.反射第一步,先获取Class对象
        Dog dd = new Dog();
        Class clazz = dd.getClass();
        //2.获取单个"public"的构造方法
        Constructor con1 = clazz.getConstructor();
        System.out.println(con1);

        Constructor con2 = clazz.getConstructor(int.class,String.class);
        System.out.println(con2);
        //3.使用构造创建对象
        Dog dog1 = (Dog)con1.newInstance();
        System.out.println(dog1);

        Dog dog2 = (Dog)con2.newInstance(10,"旺财");
        System.out.println(dog2);
    }
输出结果:
public com.wsl.demo04_GetConstructor.Dog()
public com.wsl.demo04_GetConstructor.Dog(int,java.lang.String)
Dog{age=0, name='null'}
Dog{age=10, name='旺财'}

如果是私有构造怎么办

私有构造必须先设置暴力权限,然后才能正常使用,否则抛出IllegalAccessException异常
    @Test
    public void test02() throws Exception {
        //使用构造
        //1.反射第一步,先获取Class对象
        Dog dd = new Dog();
        Class clazz = dd.getClass();
        //2.获取单个"public"的构造方法
        Constructor con1 = clazz.getConstructor();
        System.out.println(con1);

        Constructor con2 = clazz.getDeclaredConstructor(int.class,String.class);
        System.out.println(con2);
        //3.使用构造创建对象
        Dog dog1 = (Dog)con1.newInstance();
        System.out.println(dog1);
        //私有构造,不能直接使用,因为没有权限
        //设置暴力访问权限
        con2.setAccessible(true);
        Dog dog2 = (Dog)con2.newInstance(10,"旺财");
        System.out.println(dog2);
    }
 输出结果:
    public com.wsl.demo04_GetConstructor.Dog()
    private com.wsl.demo04_GetConstructor.Dog(int,java.lang.String)
    Dog{age=0, name='null'}
    Dog{age=10, name='旺财'}

通过反射获取成员方法&&调用成员方法

反射之操作成员方法的目的
操作 Method 对象来调用成员方法
Method 类概述
每一个成员方法都是一个 Method 类的对象。
 
 Method getMethod(String name,Class...args);

* 根据方法名和参数类型获得对应的构造方法对象,只能获得public的

 Method getDeclaredMethod(String name,Class...args);

* 根据方法名和参数类型获得对应的构造方法对象,包括public、protected、(默认)、private的

 Method[] getMethods();

* 获得类中的所有成员方法对象,返回数组,只能获得public修饰的且包含父类的

 Method[] getDeclaredMethods();

* 获得类中的所有成员方法对象,返回数组,只获得本类的,包括public、protected、(默认)、
private的 


Object invoke(Object obj, Object... args)
 * 调用指定对象obj的该方法 * args:调用方法时传递的参数
 void setAccessible(true) 
* 设置"暴力访问"——是否取消权限检查,true取消权限检查,false表示不取消

反射获取成员方法

public Method getMethod(String name,Class...args);获取"public"方法
public Method getDeclaredMethod(String name,Class...args);获取"任意修饰"方法
    
public Method[] getMethods(); 获取所有"public"方法,包括父类继承的 
public Method[] getDeclaredMethods(); 获取所有"任意修饰"方法,不包含父类继承的     
    @Test
    public void test01() throws NoSuchMethodException {
        //1.获取Class对象
        Dog dd = new Dog();
        Class clazz = dd.getClass();
        //2.获取单个"public"成员方法
        Method eat1 = clazz.getMethod("eat");
        System.out.println(eat1);

        Method eat2 = clazz.getMethod("eat", String.class, String.class);
        System.out.println(eat2);
        //3.获取单个"任意修饰"成员方法
        Method eat3 = clazz.getDeclaredMethod("eat", String.class);
        System.out.println(eat3);
        System.out.println("=====================");
        //4.获取所有的"public"成员方法,包括父类继承的
        Method[] methods = clazz.getMethods();
        System.out.println(methods.length);
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("=====================");
        //5.获取所有的"任意修饰"的成员方法,但是不包含父类继承的
        Method[] methodss = clazz.getDeclaredMethods();
        System.out.println(methodss.length);
        for (Method method : methodss) {
            System.out.println(method);
        }
    }    

调用成员方法

语法格式:
	成员方法对象.invoke(对象名,参数);

@Test
public void test02() throws Exception {
    //调用成员方法
    //1.获取Class对象
    Dog dd = new Dog();
    Class clazz = dd.getClass();
    //2.获取单个"public"成员方法
    Method eat1 = clazz.getMethod("eat");
    System.out.println(eat1);

    Method eat2 = clazz.getMethod("eat", String.class, String.class);
    System.out.println(eat2);
    //3.使用成员方法
    eat1.invoke(dd);
    eat2.invoke(dd,"好","哈");
}
输出结果:
public void com.wsl.demo05_GetMethod.Dog.eat()
public void com.wsl.demo05_GetMethod.Dog.eat(java.lang.String,java.lang.String)
狗爱吃...
狗爱吃,爱喝

如果是私有的成员方法怎么调用呢?

私有成员方法不能直接调用,必须先设置暴力访问权限,否则抛出IllegalAccessException异常
    
    @Test
    public void test02() throws Exception {
        //调用成员方法
        //1.获取Class对象
        Dog dd = new Dog();
        Class clazz = dd.getClass();
        //2.获取单个"public"成员方法
        Method eat1 = clazz.getMethod("eat");
        System.out.println(eat1);

        Method eat2 = clazz.getMethod("eat", String.class, String.class);
        System.out.println(eat2);

        Method eat3 = clazz.getDeclaredMethod("eat", String.class);
        //3.使用成员方法
        eat1.invoke(dd);
        eat2.invoke(dd,"哈","哈");
        //私有方法不能直接调用,必须先设置暴力访问权限
        eat3.setAccessible(true);
        eat3.invoke(dd,"哈");
    }
输出结果
public void com.wsl.demo05_GetMethod.Dog.eat()
public void com.wsl.demo05_GetMethod.Dog.eat(java.lang.String,java.lang.String)

6.通过反射获取成员属性

Field 类概述:

反射之操作成员变量的目的
  通过 Field 对象给对应的成员变量赋值和取值
Field 类概述
每一个成员变量都是一个 Field 类的对象。
Class类中与Field相关的方法

* Field getField(String name);
 * 根据成员变量名获得对应Field对象,只能获得public修饰
 * Field getDeclaredField(String name); 
* 根据成员变量名获得对应Field对象,包括public、protected、(默认)、private的 
* Field[] getFields();
 * 获得所有的成员变量对应的Field对象,只能获得public的
 * Field[] getDeclaredFields(); 
* 获得所有的成员变量对应的Field对象,包括public、protected、(默认)、private的

Field对象常用方法

void set(Object obj, Object value)
 void setInt(Object obj, int i)
 void setLong(Object obj, long l)
 void setBoolean(Object obj, boolean z) 
void setDouble(Object obj, double d)
 Object get(Object obj)
 int getInt(Object obj) 
long getLong(Object obj) 
boolean getBoolean(Object ob) 
double getDouble(Object obj)
 void setAccessible(true);
// 暴力反射,设置为可以直接访问私有类型的属性。 
Class getType(); 
// 获取属性的类型,返回Class对象。
public class ReflectDemo05 {
 /* Field[] getFields(); 
* 获得所有的成员变量对应的Field对象,只能获得public的 
Field[] getDeclaredFields();
 * 获得所有的成员变量对应的Field对象,包含private的 */

 @Test public void test02() throws Exception {
 // 获得Class对象
 Class c = Student.class;
 // 获得所有的成员变量对应的Field对象
 // Field[] fields = c.getFields(); 
// 获得所有的成员变量对应的Field对象,包括private
 Field[] fields = c.getDeclaredFields(); 
for (Field f: fields) { 
System.out.println(f);
 } }
/* Field getField(String name); 根据成员变量名获得对应Field对象,只能获得public修饰 Field getDeclaredField(String name); * 根据成员变量名获得对应Field对象,包含private修饰的 */ 

@Test public void test01() throws Exception { 
// 获得Class对象 
Class c = Student.class; 
// 创建对象 
Object obj = c.newInstance(); 
// 获得成员变量name对应的Field对象 
Field f = c.getField("name"); 
// 给成员变量name赋值
 // 给指定对象obj的name属性赋值为jack
 f.set(obj,"jack"); 
// 获得指定对象obj成员变量name的值
System.out.println(f.get(obj));
 // jack // 获得成员变量的名字

System.out.println(f.getName()); 
// name 
// 给成员变量gender赋值 
// 获得成员变量gender对应的Field对象
 Field f1 = c.getDeclaredField("gender");
 // 暴力反射
 f1.setAccessible(true);
 // 给指定对象obj的gender属性赋值为男
 f1.set(obj,"男"); System.out.println(obj);
 } 
}

二。注解

注解的概念

注解是jdk1.5的新特性

注解相当一种标记,是类的组成部分,可以给类携带一些额外的信息

标记(注解)可以加在 包,类,字段,方法,方法参数以及局部变量上。

注解是给编译器或jvm看的,编译器或jvm可以根据注解来完成对应的功能。

注解Annotation相当于一种标记,在程序中加入注解就等于为程序打上某种标记,以后,javac编译器,开发工具和其他程序可以通过反射来了解你的类以及各种元素上有无何种标记,看你的程序有什么标记,就去干相应的事,标记可以加在包,类,属性,方法,方法的参数以及局部变量上。

注解的作用:

注解的作用就是给程序带入参数

1. 生成帮助文档:@author和@version
@author:用来标识作者姓名。
@version:用于标识对象的版本号,适用范围:文件、类、方法。
使用@author和@version注解就是告诉Javadoc工具在生成帮助文档时把作者姓名和
版本号也标记在文档中。
2. 编译检查:@Override
@Override:用来修饰方法声明。
用来告诉编译器该方法是重写父类中的方法,如果父类不存在该方法,则编译失败。
3. 框架的配置(框架=代码+配置)
 常见注解
1. @author:用来标识作者名,eclipse开发工具默认的是系统用户名。
2. @version:用于标识对象的版本号,适用范围:文件、类、方法。
3. @Override :用来修饰方法声明,告诉编译器该方法是重写父类中的方法,如果父类不存在该方
法,则编译失败。

2.自定义注解

自定义注解(Annotation)

格式:

自定义类: public class 类名
自定义接口: public interface 接口
自定义枚举: public enum 枚举名
自定义注解: public @interface 注解名
    
格式:
	public @interface 注解名{
        
    }

给自定义注解添加属性

格式:
	public @interface 注解名{
        //注解内部只有属性,没有别的!!
        数据类型 属性名();
        数据类型 属性名() [default 默认值];
    }
注解中并不是所有数据类型都可以的!!!
    只能是以下三大类型:
		a.八大基本类型(byte,short,char,int,long,float,double,boolean)
        b.String,Class,注解类型,枚举类 
        c.以上12具体数据类型的数组 

自定义注解练习

需求:
	 定义一个注解:Book
        包含属性:String value() 书名
        包含属性:double price() 价格,默认值为 100
        包含属性:String[] authors() 多位作者   
/**
 * 自定义的注解
 */
public @interface Book {
    //属性
    String value(); //书名
    double price() default 100.0; //价格
    String[] authors();
}      

7、使用注解时的注意事项

使用格式:
	@注解名(属性名=属性值,属性名=属性值)
        
@Book(value = "三国演义",price = 150.0,authors = {"罗贯中","杨洋"})
public class Demo {
    @Book(value = "红楼梦",authors = {"曹雪芹"})
    private int age;

    private String name;

    public Demo() {
    }

    @Book(value = "水浒传",authors = "吴承恩")
    public Demo(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public void show(int age){
        String name = "张三";
        System.out.println(age);
    }
}

注意:
	a.使用注解时保证注解的每个属性都必须有值(有默认值我们可以不再赋值,没有默认值我们必须赋值)
    b.如果是数组需要使用{}把值括起来,如果数组的值只有一个,那么大括号可以省略 

自定义注解中的特殊属性名value

a.如果注解中"只有一个属性",并且名字叫做"value",那么使用时可以直接写属性的值,省略属性名
b.如果注解中有value之外的其他属性,那么其他属性都有默认值,
					且使用注解时只给value赋值,那么直接写属性的值,省略属性名.  

/**
 * 特殊属性Value
 */
public @interface Book {
    //属性
    String value(); //书名
    double price() default 100.0;
}

public class Demo {
    private int age;

    private String name;

    public Demo() {
    }
    @Book("三国演义")
    public Demo(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public void show(int age){
        String name = "张三";
        System.out.println(age);
    }
}

注解的注解--元注解(

a.元注解是Java官方提供的注解
b.元注解用来修饰我们定义的注解(注解的注解)

两个元注解

元注解概述
 * Java官方提供的注解 
* 用来定义注解的注解 
* 任何官方提供的非元注解的定义都使用到了元注解。
 常用的元注解
 * @Target * 作用:用来标识注解使用的位置,如果没有使用该注解标识,则自定义的注解可以使用在任意位 置。
 * 可使用的值定义在ElementType枚举类中,常用值如下
 TYPE,类,接口
 FIELD, 成员变量 
METHOD, 成员方法 
PARAMETER, 方法参数 
CONSTRUCTOR, 构造方法 
LOCAL_VARIABLE, 局部变量 * 


@Retention * 作用:用来标识注解的生命周期(有效范围) 
* 可使用的值定义在RetentionPolicy枚举类中,常用值如下 
* SOURCE:注解只作用在源码阶段,生成的字节码文件中不存在
 * CLASS:注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值
 * RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段
元注解常见的有两个:
	@Target 元注解
        作用:用来标识注解使用的位置,如果没有标识,那么我们的注解在各种地方都可以使用
        取值:必须使用ElementType枚举下的值:
			TYPE,类,接口 
            FIELD, 成员变量
            METHOD, 成员方法 
            PARAMETER, 方法参数 
            CONSTRUCTOR, 构造方法 
            LOCAL_VARIABLE, 局部变量
    //元注解@Target
    //@Target(ElementType.CONSTRUCTOR)
    //@Target(ElementType.FIELD)
    //@Target(ElementType.LOCAL_VARIABLE)
    
    //使用元注解修饰我们定义的注解            
    @Target({ElementType.FIELD,ElementType.METHOD})
    public @interface MyAnno {
    }

	//使用我们定义的注解
	@MyAnno
    public class Demo {
        @MyAnno
        private int age;
        @MyAnno
        private String name;

        @MyAnno
        public Demo() {
        }
        @MyAnno
        public Demo(@MyAnno int age,@MyAnno String name) {
            this.age = age;
            this.name = name;
        }
        @MyAnno
        public void show(@MyAnno int age){
            @MyAnno
            String name = "张三";
            System.out.println(age);
        }
    }

       
    @Retention 元注解    
        作用:用来标识我们注解的生命周期(有效范围) 
        取值:必须是RetentionPolicy枚举中的下面三个值之一
            SOURCE 表示我们的注解只在源码阶段存在,编译成字节码文件后删除
            CLASS 表示我们的注解在源码阶段和字节码阶段存在,加载到内存后删除
            RUNTIME 表示我们的注解在源码阶段,字节码阶段,运行时都存在(永远不删除)  

注解的解析:

通过写代码获取出来某个注解中的那些属性值

注解解析的步骤

什么是注解解析
 * 使用Java技术获得注解上数据的过程则称为注解解析。 
与注解解析相关的接口 
* Annotation: 注解类,该类是所有注解的父类。 
* AnnotatedElement:该接口定义了与注解解析相关的方法

 T getAnnotation(Class<T> annotationClass) 
根据注解类型获得对应注解对象 
Annotation[] getAnnotations()
 * 获得当前对象上使用的所有注解,返回注解数组,包含父类继承的

 Annotation[] getDeclaredAnnotations()
 * 获得当前对象上使用的所有注解,返回注解数组,只包含本类的 
boolean isAnnotationPresent(Class<Annotation> annotationClass)
 * 判断当前对象是否使用了指定的注解,如果使用了则返回true,否则false 

获取注解数据的原理 
* 注解作用在哪个成员上就会得该成员对应的对象来获得注解
 * 比如注解作用成员方法,则要获得该成员方法对应的Method对象 
* 比如注解作用在类上,则要该类的Class对象
 * 比如注解作用在成员变量上,则要获得该成员变量对应的Field对象。 
* Field,Method,Constructor,Class等类都是实现了AnnotatedElement接口
a.获取注解所在的那个类的Class对象
b.获取注解所在的对象(可能Field,Method,Constructor) 
c.判断获取的对象中是否有该注解存在  
d.如果有我们要的注解,取出我们要的注解即可
e.从注解中取出属性值即可   
    
与之相关的API:
	Annotation: 注解类,Java中所有的注解的父类(了解)
    我们学过的Field,Method,Constructor,Class等类都是实现了AnnotatedElement接口
    public boolean isAnnotationPresent(Class annotationClass);判断是否存在某个注解
    Annotation getAnnotation(Class annotationClass);获取指定类型的注解   

注解解析代码实现

a.获取注解所在的那个类的Class对象
b.获取注解所在的对象(可能Field,Method,Constructor) 
c.判断获取的对象中是否有该注解存在  
d.如果有我们要的注解,取出我们要的注解即可
e.从注解中取出属性值即可
    
@Retention(RetentionPolicy.RUNTIME)
public @interface Student {
    int age();
    String name();
    String[] boyFriends();
}

public class Demo {

    @Student(age = 18, name = "小花", boyFriends = {"张三", "李四", "王五"})
    public void show() {

    }
}

public class TestStudent {
    public static void main(String[] args) throws Exception {
//        a.获取注解所在的那个类的Class对象
        Class clazz = Demo.class;
//        b.获取注解所在的对象(可能Field,Method,Constructor)
        Method showMethod = clazz.getMethod("show");
//        c.判断获取的对象中是否有该注解存在
        if (showMethod.isAnnotationPresent(Student.class)) {
            System.out.println("有该注解");
            //        d.如果有我们要的注解,取出我们要的注解即可
            Student anno = showMethod.getAnnotation(Student.class);
            //        e.从注解中取出属性值即可
            int age = anno.age();
            String name = anno.name();
            String[] friends = anno.boyFriends();
            System.out.println("姓名:"+name);
            System.out.println("年龄:"+age);
            System.out.println("男友们:"+ Arrays.toString(friends));
        } else {
            System.out.println("没有该注解");
        }

    }
}    

注解解析案例

需求:
	模拟Junit的@Test注解

/**
 * 自定义的注解,用于模拟@Test注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTest {

}
 
public class Demo {
    @MyTest
    public void test01(){
        System.out.println("11111");
    }

    @MyTest
    public void test02(){
        System.out.println("22222");
    }

    @MyTest
    public void test03(){
        System.out.println("33333");
    }

    @MyTest
    public void test04(){
        System.out.println("44444");
    }
}

public class TestMyTestDemo {
    public static void main(String[] args) throws Exception {
        //当这个main方法执行时,要求Demo类中有@MyTest注解的方法会执行
        //没有@MyTest注解的方法不会执行
        //思考:步骤
        //1.获取@MyTest注解所在的类
        Class clazz =  Demo.class;
        //2.获取所有方法
        Method[] methods = clazz.getMethods();
        //3.循环
        for (Method method : methods) {
            //4.判断
            if (method.isAnnotationPresent(MyTest.class)) {
                //有该注解
                method.invoke(new Demo());
            }else{
                //没有该注解
            }
        }
    }
}

other===========================================================

===============================================================

注解的作用
    a。给程序带入参数  b。编译检查	c。给框架做配置文件
自定义注解和使用注解
    自定义注解:
    	public @interface 注解名{
    		数据类型 属性名();
    		数据类型 属性名() default 默认值;
    		数据类型[] 属性名() default {默认值1,默认值2,...}
		}
		这里的数据类型,只能是三大类(a.8大基本类型 b.四大引用类型 c.以上12中的数组)
    特殊的属性:value(怎么特殊???)        
常用的元注解及其作用 
     @Target
     @Retension   
解析注解并获取注解中的数据 
     写代码把某个注解上的那些属性值获取打印出来
     a.获取注解所在的那个类的Class对象
     b.获取注解所在的对象(可能Field,Method,Constructor) 
     c.判断获取的对象中是否有该注解存在  
     d.如果有我们要的注解,取出我们要的注解即可
     e.从注解中取出属性值即可     
   
需求:

1. 现有集合:ArrayList<Integer>list = new ArrayList(); 
2. 利用反射机制在这个泛型为Integer的ArrayList中存放一个String类型的对象。

    public static void main(String[] args) throws  Exception{
        ArrayList<Integer> list = new ArrayList<>();
        list.add(129);
        //获取ArrayList对象
        Class<? extends ArrayList> aClass = list.getClass();
        //初始化ArrayList对象
        //ArrayList list1 = aClass.newInstance();
        Method add = aClass.getMethod("add", Object.class);
        add.invoke(list,"我是中国人");
        System.out.println(list);

    }
需求:按要求完成下面两个方法的方法体 

  1.写一个方法,此方法可将obj对象中名为propertyName的属性的值设置为value. public void setProperty(Object obj, String propertyName, Object value){}

1. 写一个方法,此方法可以获取obj对象中名为propertyName的属性的值 

public Object getProperty(Object obj, String propertyName){} 

  public static void main(String[] args) throws Exception{
        Student s = new Student("张三");
        setProperty(s,"name","网名");
        System.out.println(s);
        Object name = getProperty(s, "name");
        System.out.println(name);
    }
    public static void setProperty(Object obj, String propertyName, Object value) throws Exception{
        Class<?> aClass = obj.getClass();
        Object oj = aClass.newInstance();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField.getName());
            if (declaredField.getName().equals(propertyName)){
                declaredField.setAccessible(true);
                declaredField.set(oj,value);
                break;
            }
        }
    }
    public static Object getProperty(Object obj, String propertyName) throws Exception{
        Class<?> aClass = obj.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (declaredField.getName().equals(propertyName)){
                declaredField.setAccessible(true);
                Object o = declaredField.get(obj);
                return o;
            }
        }
        return null;
    }
}
需求:

1. 定义一个Person类,包含私有属性name、age,getXxx和setXxx方法和有参满参构造方法。
2. 使用反射的方式创建一个实例、调用构造函数初始化name、age。使用反射方式调用setName方法对姓名进行设置,不使用setAge方法直接使用反射方式对age

 public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("com.wsl.zuoye.Person");
        Constructor<?> constructor = aClass.getConstructor(String.class, int.class);
        Person person = (Person) constructor.newInstance("网名", 25);
        System.out.println(person);
        Method[] methods = aClass.getMethods();
        for (Method method : methods) {
            if (method.getName().equals("setAge")){
                method.invoke(person,12);
            }
        }

        System.out.println(person);


    }
需求:已知一个类,定义如下 

package com.wsl.test07; 
public class DemoClass { 
	public void run() { 
		System.out.println("welcome to china"); 
	} 
}

(1) 写一个Properties格式的配置文件,配置文件内容如下: 

 className=com.wsl.test07.DemoClass

(2)写一个程序,读取这个Properties配置文件,获得类的完整名称并加载这个类, 

(3)用反射的方式运行run方法。

    public static void main(String[] args) throws Exception{
        ResourceBundle bundle = ResourceBundle.getBundle("com.wsl.zuoye.zy2_6");
        String className = bundle.getString("className");

        Class<?> aClass = Class.forName(className);

        Object oj = aClass.newInstance();

        Method run = aClass.getMethod("run");

        run.invoke(oj);
    }




className=com.wsl.zuoye.DemoClass

发布了117 篇原创文章 · 获赞 20 · 访问量 27万+

猜你喜欢

转载自blog.csdn.net/u010581811/article/details/105166417