反射
类加载器
类的初始化
- 创建类的实例
- 类的静态变量,或者为静态变量赋值
- 类的静态方法
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
/*
类的初始化(类什么时候进入到内存中)
*/
public class Demo01 {
public static void main(String[] args) {
//1.使用java.exe运行某个类的时候
//2.创建对象
new Person();
//3.访问静态成员变量,给静态成员变量赋值
System.out.println(Person.a);
//4.访问静态成员方法
Person.method();
//5.创建类的子类对象的时候
new Student();
//6.使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
}
}
public class Person {
public Person() {
System.out.println("Person类的构造方法");
}
public static int a = 10;
public static void method(){
System.out.println("静态方法!");
}
}
public class Student extends Person {
public Student() {
super();
}
}
类加载器概念
类加载器:负责加载类的对象。将class文件(硬盘)加载到内存中生成class对象。
类加载器的组成
-
BootstrapClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载
比如System,String等。
-
ExtClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录
-
AppClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。
-
使用 类.class.getClassLoader() 获得加载自己的类加载器
-
类加载器加载机制:全盘负责委托机制
全盘负责:A类如果要使用B类(不存在),A类加载器必须负责加载B类。
- 委托机制:A类加载器如果要加载资源B,必须询问父类加载是否加载。
如果加载,将直接使用。
如果没有机制,自己再加载。
- 委托机制:A类加载器如果要加载资源B,必须询问父类加载是否加载。
-
采用全盘负责委托机制保证一个class文件只会被加载一次,形成一个Class对象。
类加载器的获取
/*
ClassLoader:类加载器,负责把类加载到内存中,并为类生成一个class文件对象
获取方式:
可以使用Class类中的方法getClassLoader();
ClassLoader getClassLoader() 返回该类的类加载器。
ClassLoader类中的方法:
ClassLoader getParent() 返回委托的父类加载器。
AppClassLoader extends ExtClassLoader extends BootstrapClassLoader extends ClassLoader
*/
public class Demo {
public static void main(String[] args) {
show03();
}
/*
直接获取根类加载器
*/
private static void show03() {
ClassLoader cl = String.class.getClassLoader();
System.out.println(cl);//null
}
/*
直接获取扩展类加载器
*/
private static void show02() {
ClassLoader cl = SunEC.class.getClassLoader();
System.out.println(cl);//sun.misc.Launcher$ExtClassLoader@7ea987ac
}
//类名.class属性获取class文件对象
private static void show01() {
ClassLoader c1 = Demo.class.getClassLoader();
System.out.println(c1);//sun.misc.Launcher$AppClassLoader@18b4aac2
ClassLoader c2 = c1.getParent();
System.out.println(c2);//sun.misc.Launcher$ExtClassLoader@4554617c
ClassLoader c3 = c2.getParent();
System.out.println(c3);//null 根类加载器,不是类(C)
}
}
获取class文件对象的方式(重点)
/*
获取class文件对象的方式(重点)
class文件对象是由类加载器创建的,我们无权创建和销毁,class文件对象是唯一的,无论我们使用那种方式获取都是同一个
1.使用Object类中的方法getClass
Class<?> getClass() 返回此 Object 的运行时类。
2.java会为每种数据类型都赋予一个class属性,这个class属性返回的就是class文件对象
基本数据类型:int.class,double.class,char.class,boolean.class...
引用数据类型:int[].class,ArrayList<String>.class,String.class,Person.class
3.可以使用Class类中的静态方法forName
static Class<?> forName(String className) 返回与带有给定字符串名的类或接口相关联的 Class 对象
参数:
String className:全类名(包名+类名) 可以确定类的唯一性
com.itheima.demo03Reflect.Person
*/
public class Demo01Reflect {
public static void main(String[] args) throws ClassNotFoundException {
//1.使用Object类中的方法getClass:Person类默认继承了Object类,所以可以使用Object类中的getClass方法
Person p = new Person();
Class c1 = p.getClass();
System.out.println(c1);//class com.llz.demo03Reflect.Person
//2.java会为每种数据类型都赋予一个class属性,这个class属性返回的就是class文件对象
System.out.println(int.class);
System.out.println(double.class);
System.out.println(String.class);
Class c2 = Person.class;
System.out.println(c2);//class com.llz.demo03Reflect.Person
//3.可以使用Class类中的静态方法forName
Class c3 = Class.forName("com.llz.demo03Reflect.Person");
System.out.println(c3);//class com.llz.demo03Reflect.Person
//class文件对象是由类加载器创建的,我们无权创建和销毁,class文件对象是唯一的,无论我们使用那种方式获取都是同一个
System.out.println(c1==c2);//true 引用数据类型==比较的是对象的地址值
System.out.println(c1==c3);//true
System.out.println(c2==c3);//true
}
}
Class类常用方法(了解)
/*
Class类常用方法(了解)
String getSimpleName() 获取类名
String getName() 获取全类名(包名+类名)
-------------------------------------
使用Class类中的forName方法,获取class文件对象,会执行类中的静态代码块(数据库:JDBC)
*/
public class Demo03ClassMethod {
public static void main(String[] args) throws ClassNotFoundException {
//获取Class文件对象
Class clazz = Class.forName("com.llz.demo03Reflect.Person");
String simpleName = clazz.getSimpleName();
System.out.println(simpleName);//Person
String name = clazz.getName();
System.out.println(name);//com.llz.demo03Reflect.Person
}
}
反射概述
反射准备工作
public class Person {
/*static {
System.out.println("Person类的静态代码块!");
}*/
private String name;
private int age;
public String sex;
public Person() {
System.out.println("Person类的空参数构造方法!");
}
public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
System.out.println("Person类的满参数构造方法!");
}
private Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Person类的私有参数构造方法!");
}
private void method(){
System.out.println("Person类的私有method方法!");
}
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 String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
使用反射技术获取类中的构造方法,并使用获取到的构造方法实例化对象
/*
使用反射技术获取类中的构造方法,并使用获取到的构造方法实例化对象
实现步骤:
1.获取Person类的class文件对象
2.使用class文件对象中的方法getConstructor|getConstructors或者类中的(公共)构造方法
3.使用构造方法Constructor类中的方法newInstance实例化(创建)对象
*/
public class Demo04Constructor {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//1.获取Person类的class文件对象
Class clazz = Class.forName("com.llz.demo03Reflect.Person");
//2.使用class文件对象中的方法getConstructor|getConstructors或者类中的(公共)构造方法
/*
Class类中的方法:获取多个构造方法
Constructor<?>[] getConstructors() 获取类的所有公共构造方法
Constructor<?>[] getDeclaredConstructors() 获取类声明的所有构造方法(包含公共的,受保护的,默认的,私有的)
*/
Constructor[] cons = clazz.getConstructors();
for (Constructor con : cons) {
System.out.println(con);
}
System.out.println("------------------------");
Constructor[] declaredCons = clazz.getDeclaredConstructors();
for (Constructor con : declaredCons) {
System.out.println(con);
}
System.out.println("------------------------");
/*
Class类中的方法:获取指定的构造方法
Constructor<T> getConstructor(Class<?>... parameterTypes) 获取类的指定公共构造方法
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取类的指定构造方法(包含公共的,受保护的,默认的,私有的)。
参数:
...:可变参数,调用的方法参数是可变参数,可以传递任意个参数(不传,1,多个...)
Class<?>... parameterTypes:传递构造方法参数的class文件对象
(String.class,int.class,double.class,...)
注意:
类中没有指定的构造方法,会抛出NoSuchMethodException:没哟这个方法异常
*/
//public Person()
Constructor con1 = clazz.getConstructor();
System.out.println(con1);//public com.llz.demo03Reflect.Person()
//public Person(String name, int age, String sex)
Constructor con2 = clazz.getConstructor(String.class, int.class, String.class);
System.out.println(con2);//public com.llz.demo03Reflect.Person(java.lang.String,int,java.lang.String)
//private Person(String name, int age)
Constructor con3 = clazz.getDeclaredConstructor(String.class, int.class);
System.out.println(con3);//private com.llz.demo03Reflect.Person(java.lang.String,int)
System.out.println("----------------------------");
//3.使用构造方法Constructor类中的方法newInstance实例化(创建)对象
/*
java.lang.reflect.Constructor<T>类
T newInstance(Object... initargs) 实例化对象的方法(创建对象)
参数:
Object... initargs:传递创建对象的实际参数 new Person("张三",18,"女");
返回值:
T:返回的及时创建好的对,类型是Object类型
Object obj = new Person();
*/
//public Person()
Object obj1 = con1.newInstance();//此方法相当于 new Person();
System.out.println(obj1);//com.llz.demo03Reflect.Person@4554617c Person{name='null', age=0, sex='null'}
//public Person(String name, int age, String sex)
Object obj2 = con2.newInstance("张三", 18, "女");//此方法相当于 new Person("张三",18,"女");
System.out.println(obj2);//Person{name='张三', age=18, sex='女'}
/*
private Person(String name, int age)
私有的方法是没有权限使用的,要使用私有构造方法会抛出IllegalAccessException:非法访问异常
解决:
可以使用Constructor的父类AccessibleObject类中的方法解决
java.lang.reflect.AccessibleObject类
void setAccessible(boolean flag) 将此对象的 accessible 标志设置为指示的布尔值。
值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
值为 false 则指示反射的对象应该实施 Java 语言访问检查。
注意:
暴力反射不推荐,破坏了类的封装性(私有,不让外界使用)
*/
con3.setAccessible(true);//取消java语言访问检查==>暴力反射
Object obj3 = con3.newInstance("李四",20);
System.out.println(obj3);//Person{name='李四', age=20, sex='null'}
}
}
使用反射技术获取构造方法并实例化对象的简化方式(重点)
/*
使用反射技术获取构造方法并实例化对象的简化方式(重点)
java.lang.Class类
T newInstance() 创建此 Class 对象所表示的类的一个新实例。
实现步骤:
1.获取Person类class文件对象
2.使用class文件对象中的方法newInstance实例好对象
注意:
1.类中必须有空参数构造方法
2.空参数构造方法修饰符是public,不能是private
*/
public class Demo05Constructor {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//1.获取Person类class文件对象
Class clazz = Class.forName("com.llz.demo03Reflect.Person");
//2.使用class文件对象中的方法newInstance实例好对象
Object obj = clazz.newInstance();//多态
System.out.println(obj);//Person{name='null', age=0, sex='null'}
Person p = (Person)obj;
System.out.println(p.getName()+"\t"+p.getAge());
}
}
使用反射技术获取类中的成员方法,并运行(重点)
/*
使用反射技术获取类中的成员方法,并运行获取到成员方法(重点)
实现步骤:
1.获取Person类的class文件对象
2.使用class文件对象中的方法getMethod|getMethods获取类中(公共)的成员方法
3.使用成员方法Method类中的方法invoke,运行获取到的成员方法
*/
public class Demo06Method {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
//1.获取Person类的class文件对象
Class clazz = Class.forName("com.llz.demo03Reflect.Person");
//2.使用class文件对象中的方法getMethod|getMethods获取类中(公共)的成员方法
/*
Class类中的方法:获取多个成员方法
Method[] getMethods() 获取本类和父类|接口继承的,所有公共成员方法
Method[] getDeclaredMethods() 获取声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
Method类中有一个方法getName,获取方法的名称
String getName() 以 String 形式返回此 Method 对象表示的方法名称。
*/
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("------------------------");
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method method : declaredMethods) {
System.out.println(method.getName());
}
System.out.println("-------------------------");
/*
Class类中的方法:获取指定的成员方法
Method getMethod(String name, Class<?>... parameterTypes) 获取指定公共成员方法。
Method getDeclaredMethod(String name, Class<?>... parameterTypes) 指定已声明方法(包括公共、保护、默认(包)访问和私有方法)。
参数:
String name:方法的名称
Class<?>... parameterTypes:方法参数的class文件对象
注意:
获取指定的成员方法不存在,会抛出NoSuchMethodException:没有这个方法异常
*/
//public String getName()
Method getNameMethod = clazz.getMethod("getName");
System.out.println(getNameMethod);//public java.lang.String com.llz.demo03Reflect.Person.getName()
//public void setName(String name)
Method setNameMethod = clazz.getMethod("setName", String.class);
System.out.println(setNameMethod);
//private void method()
Method privateMethod = clazz.getDeclaredMethod("method");
System.out.println(privateMethod);
//3.使用成员方法Method类中的方法invoke,运行获取到的成员方法
/*
java.lang.reflect.Method类
Object invoke(Object obj, Object... args) 运行成员方法
参数:
Object obj:方法的运行需要对象的支持,运行哪个类的方法,就需要传递哪个类的对象
运行Person类中的方法,就需要传递Person对象(快速的方式获取)
Object... args:运行方法传递的实际参数
返回值:
Object:方法的返回值
方法的返回值类型不是void,返回的就是方法的返回值
方法的返回值类型是void,返回null(没有返回值)
*/
Object obj = clazz.newInstance();
//public String getName()
Object v1 = getNameMethod.invoke(obj);//就相当于使用getName方法获取name的值
System.out.println("v1:"+v1);//v1:null name的默认值
//public void setName(String name)
Object v2 = setNameMethod.invoke(obj, "柳岩");//就相当于使用setName方法给name赋值
System.out.println("v2:"+v2);//v2:null 方法没有返回值
v1 = getNameMethod.invoke(obj);//就相当于使用getName方法获取name的值
System.out.println("v1:"+v1);//v1:柳岩
/*
private void method()
私有方法没有权限运行,会抛出非法访问异常:IllegalAccessException
使用暴力反射解决:使用Method的父类AccessibleObject中的方法setAccessible取消java的语言访问检查
*/
privateMethod.setAccessible(true);
privateMethod.invoke(obj);//Person类的私有method方法!
}
}
使用反射技术获取实现类所实现的所有接口(了解)
/*
使用反射技术获取实现类所实现的所有接口:学习动态代理的时候使用
class类中的方法:
Class<?>[] getInterfaces() 确定此对象所表示的类或接口实现的接口。
*/
public class Demo01 {
public static void main(String[] args) throws ClassNotFoundException {
//获取接口实现类AandBImpl的class文件对象
Class clazz = Class.forName("com.llz.demo04Reflect.AandBImpl");
//使用反射技术获取实现类所实现的所有接口
Class[] interfaces = clazz.getInterfaces();
for (Class in : interfaces) {
/*
interface com.llz.demo04Reflect.A
interface com.llz.demo04Reflect.B
*/
System.out.println(in);
}
}
}
注解
注解概述
- 注解是JDK1.5的新特性。
- 标记(注解)可以加在包,类,字段,方法,方法参数以及局部变量上。
- 注解是给编译器或JVM看的,编译器或JVM可以根据注解来完成对应的功能。
注解的作用:
1.使用javadoc生成帮助文档:里边可以包含注解**@author和@version**
2.编译检查:@Override @FunctionalInterface
3.框架的配置(框架=代码+配置):框架的时候讲
自定义注解(重点)
定义没有属性的注解
/*
自定义注解:定义一个没有属性的注解
格式:
public @interface 注解名 {
}
注意:
注解使用的也是.java文件,编译生成的也是.class文件
注解和类和接口和枚举都是同一个层次
*/
public @interface MyAnnotation01 {
}
定义有属性的注解
/*
自定义注解:定义一个含有属性的注解
注解的属性,可以看成是抽象方法,但是有默认值
定义格式:
public @interface 注解名 {
修饰符 数据类型 属性名();
修饰符 数据类型 属性名() default 默认值;
}
注意:
1.属性的修饰符固定使用public abstract,可以省略不写,不写默认也是;建议写出,可以增强语句的可读性
2.属性的数据类型
a.基本数据类型(4类8种):byte,short,int,long,float,double,char,boolean
b.引用数据类型:String类型,反射类型(Class),注解类型,枚举类型
c.以及以上所有数据类型的一维数组
*/
public @interface MyAnnotation02 {
//定义一个int类型的属性
public abstract int a();
//定义一个double类型的书写,并给属性赋默认值8.8
public abstract double d() default 8.8;
//定义一个String数组类型的属性
public abstract String[] arr();
//定义反射类型的属性(了解)
//public abstract Class clazz();
//定义注解类型的属性(了解)
//public abstract MyAnnotation01 my01();
//定义枚举类型的属性(了解)
//public abstract Color c() default Color.RED;
}
定义只有一个属性的注解,属性名叫value
//定义只有一个属性的注解,属性名叫value
//注解也可以有其他的属性,但是必须有默认值
public @interface MyAnnotation03 {
public abstract int value();
public abstract String aaa() default "hello";
}
定义枚举(了解)
/*
定义枚举
枚举中成员变量都是常量
*/
public enum Color {
/*
public static final Color RED = new Color();
public static final Color GREEN = new Color();
*/
RED,GREEN
}
使用自定义注解(重点)
/*
使用自定义注解
注解可以使用的位置:
包上,类上|接口上,成员变量上,成员方法上,构造方法上,局部变量上,方法的参数上...
注意:
1.同一个位置,同名的注解只能使用一次
2.不同的位置,同名的注解可以使用多次
注解的使用格式:
1.没有属性的注解,通过@注解名可以直接使用
2.有属性的注解必须使用键值对的方式,给注解中的所有属性都赋值之后,才能使用
格式:
@注解名(属性名=属性值,属性名=属性值,属性名=属性值...属性名=属性值)
a.有默认值的属性,可以不用赋值,使用默认值
b.给多个属性赋值中间要使用逗号分隔开
c.属性是数组类型,属性值需要使用{ }包裹起来;数组只有一个值,可以省略{ }
arr = {"a","b","c"} arr={"a"} ==> arr="a"
d.注解中只有一个属性,属性名叫value,那么赋值的时候可以省略属性名,直接写属性值
或者注解有其他的属性,但是必须有默认值
value=10==>10
*/
@MyAnnotation01
@MyAnnotation02(a = 10,arr={"a","b","c"})
public class UseMyAnnotation {
@MyAnnotation01
@MyAnnotation02(a=100,d=1.1,arr="a")
private String name;
@MyAnnotation01
@MyAnnotation03(value = 10)
public UseMyAnnotation(String name) {
this.name = name;
}
@MyAnnotation01
@MyAnnotation03(100)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
例子——Book注解
/*
练习:
定义一个注解:Book
包含属性:String value() 书名
包含属性:double price() 价格,默认值为 100
包含属性:String[] authors() 多位作者
分别在类上,方法上,变量上使用Book注解
*/
public @interface Book {
//书名
String value();
//价格,默认值为 100
double price() default 100;
//多位作者
String[] authors();
}
@Book(value = "java编程思想",authors = "老外")
public class UseBook {
@Book(value = "红楼梦",price = 88.8,authors ={"曹雪芹","吴承恩"} )
private String name;
@Book(value = "水浒传",authors = "施耐庵")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
元注解(重点)
/*
元注解:java已经定义好的注解,可以用来修饰自定义的注解
1.@Target
作用:用来标识注解使用的位置,如果没有使用该注解标识,则自定义的注解可以使用在任意位置
属性:
ElementType[] value :只有一个属性叫value,使用的时候,给属性赋值可以省略属性名
java.lang.annotation.ElementType:是一个枚举,枚举中定义的变量都是常量
TYPE,类,接口
FIELD, 成员变量
METHOD, 成员方法
PARAMETER, 方法参数
CONSTRUCTOR, 构造方法
LOCAL_VARIABLE, 局部变量
2.@Retention
作用:用来标识注解的生命周期(有效范围)
属性:
RetentionPolicy value:只有一个属性叫value,使用的时候,给属性赋值可以省略属性名
java.lang.annotation.RetentionPolicy:是一个枚举,枚举中定义的变量都是常量
SOURCE:注解只作用在源码阶段(.java),生成的字节码文件(.class)中不存在
CLASS:注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值
RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段
*/
//声明自定义注解能使用在方法上和变量上
//@Target(value = {ElementType.METHOD,ElementType.FIELD})
@Target({ElementType.METHOD,ElementType.FIELD})
//声明自定义注解的声明周期:在.java文件中,.class文件中和内存中都有效
@Retention(RetentionPolicy.RUNTIME)
public @interface Book {
//书名
String value();
//价格,默认值为 100
double price() default 100;
//多位作者
String[] authors();
}
注解解析(使用)
/*
注解解析:获取注解的属性值
注解解析底层使用的都是反射技术
java.lang.reflect.AnnotatedElement接口:在接口中定义了注解解析的相关方法
所有已知实现类:实现类都重写接口中的方法,都可以使用
AccessibleObject, Class, Constructor, Field, Method, Package
接口中的常用方法:
boolean isAnnotationPresent(Class annotationClass) 判断指定对象上(Class,Method...),是否有指定的注解
参数:
Class annotationClass:判断哪个注解,就传递哪个注解的class文件对象
判断类上,方法上...有没有Book注解,就需要传递Book.class
返回值:
有指定的注解,返回true
没有指定的注解,返回false
T getAnnotation(Class<T> annotationClass) 获取对象上(Class,Method...)指定的注解
参数:
Class<T> annotationClass:获取哪个注解,就传递哪个注解的class文件对象
获取类上,方法上...的Book注解,就需要传递Book.class
返回值:
T:返回获取到的注解,获取不到返回null
了解:
Annotation[] getAnnotations() 获取对象上所有公共的注解
Annotation[] getDeclaredAnnotations() 获取对象上所有声明的注解
*/
@MyAnnotation01
@Book(value = "java编程思想",authors = "老外")
public class Demo01ParseAnnotaion {
@Book(value = "水浒传",price = 66.6,authors = "施耐庵")
public void method(){}
/*
解析类上的注解
实现步骤:
1.获取当前类Demo01ParseAnnotaion的class文件对象
2.使用class文件对象中的方法isAnnotationPresent判断类上是否有指定的Book注解
3.如果类上有Book注解,使用class文件对象中的方法getAnnotation获取Book注解
4.使用注解名.属性名(),获取属性的值
*/
@Test
public void show01(){
//1.获取当前类Demo01ParseAnnotaion的class文件对象
Class clazz = Demo01ParseAnnotaion.class;
//2.使用class文件对象中的方法isAnnotationPresent判断类上是否有指定的Book注解
boolean b = clazz.isAnnotationPresent(Book.class);
System.out.println(b);
//3.如果类上有Book注解,使用class文件对象中的方法getAnnotation获取Book注解
if(b){
Book book = (Book)clazz.getAnnotation(Book.class);
//4.使用注解名.属性名(),获取属性的值
String value = book.value();
System.out.println(value);
double price = book.price();
System.out.println(price);
String[] authors = book.authors();
System.out.println(Arrays.toString(authors));
}
}
/*
解析方法上的注解
实现步骤:
1.获取当前类的class文件对象
2.使用class文件对象中的方法getMethods获取类中所有的成员方法,返回一个Method数组
3.遍历Method数组,获取每一个Method对象
4.使用Method对象上的方法isAnnotationPresent判断方法上是否有指定的Book注解
5.如果方法上有Book注解,使用Method对象中的方法getAnnotation获取Book注解
6.使用注解名.属性名(),获取属性值
*/
@Test
public void show02() throws ClassNotFoundException {
//1.获取当前类的class文件对象
Class clazz = Class.forName("com.llz.demo08Annotaion.Demo01ParseAnnotaion");
//2.使用class文件对象中的方法getMethods获取类中所有的成员方法,返回一个Method数组
Method[] methods = clazz.getDeclaredMethods();
//3.遍历Method数组,获取每一个Method对象
for (Method method : methods) {
//4.使用Method对象上的方法isAnnotationPresent判断方法上是否有指定的Book注解
boolean b = method.isAnnotationPresent(Book.class);
//System.out.println(method.getName()+"==>"+b);
if(b){
//5.如果方法上有Book注解,使用Method对象中的方法getAnnotation获取Book注解
Book book = method.getAnnotation(Book.class);
//6.使用注解名.属性名(),获取属性值
System.out.println(book.value());
System.out.println(book.price());
System.out.println(Arrays.toString(book.authors()));
}
}
}
}
注解和反射的综合案例(重点)
//1.定义一个注解叫MyTest,使用元注解修饰MyTest注解(只能在方法上使用,运行期有效)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
}
//2.定义一个测试类,在测试类中定义多个方法,让部分方法使用MyTest注解修饰
public class DemoMyTest {
public void show01(){
System.out.println("show01方法");
}
@MyTest
public void show02(){
System.out.println("show02方法");
}
public void show03(){
System.out.println("show03方法");
}
@MyTest
public void show04(){
System.out.println("show04方法");
}
}
/*
注解和反射综合练习题
需求:
模拟Junit测试的@Test
添加了@Test注解的方法,可以运行
没有添加@Test注解的方法,不可以运行
分析:
1.定义一个注解叫MyTest,使用元注解修饰MyTest注解(只能在方法上使用,运行期有效)
2.定义一个测试类,在测试类中定义多个方法,让部分方法使用MyTest注解修饰
3.获取测试类的class文件对象
4.使用class文件对象中的方法newInstance实例化对象
5.使用class文件对象中的方法getDeclaredMethods,获取类中所有的成员方法,返回一个Method数组
6.遍历数组,获取每一个Method对象
7.使用Method对象中的方法isAnnotationPresent判断Method对象上是否有指定的MyTest注解
8.如果Method对象上有MyTest注解,使用invoke运行方法
*/
public class Demo01Test {
public static void main(String[] args) throws Exception {
//3.获取测试类的class文件对象
Class clazz = Class.forName("com.llz.demo09Test.DemoMyTest");
//4.使用class文件对象中的方法newInstance实例化对象
Object obj = clazz.newInstance();
//5.使用class文件对象中的方法getDeclaredMethods,获取类中所有的成员方法,返回一个Method数组
Method[] methods = clazz.getDeclaredMethods();
//6.遍历数组,获取每一个Method对象
for (Method method : methods) {
//7.使用Method对象中的方法isAnnotationPresent判断Method对象上是否有指定的MyTest注解
if(method.isAnnotationPresent(MyTest.class)){
//8.如果Method对象上有MyTest注解,使用invoke运行方法
method.invoke(obj);
}
}
}
}