课前回顾:1.通信三要素:IP:每一台计算机的唯一标识,用于两台计算机连接协议:通信时要遵循的规则TCP:面向连接协议三次握手断开连接时:四次挥手数据安全,但是传输效率慢UDP:面向无连接协议,传输速度快,数据不安全端口号:是应用程序的唯一标识2.两个对象Socket对象->代表的是客户端对象ServerSocket->代表的是服务端对象Socket accept()->自带阻塞效果,接收的是连接服务器的客户端对象今日重点:1.掌握单元测试的使用2.知道反射是干啥的->获取Class对象,以及操作Class对象中的成员3.怎么学今天的内容:把反射看成一套API去学通过反射练习体会反射的好处4.会获取Class对象5.会根据Class对象,获取Class对象中的构造方法,成员方法并操作6.会使用注解7.知道元注解的作用
第一章 Junit单元测试
1.Junit介绍
1.概述:Junit是一个Java语言的单元测试框架,简单理解为在一定程度上用于取代main方法.Junit属于第三方工具,需要导入Jar包后再使用2.作用:如果想单独执行一个方法,就不需要在main方法中调用了3.导jar包->导入Junit的jar包a.在当前模块下创建一个lib文件夹b.将Junit的jar包粘贴到这个文件夹下:junit-4.9.jarc.右键jar包,选择 add as libraryd.level选择module,点ok4.导完jar包之后怎么用:注解: @Test使用位置: 在方法上使用@Test双击选中要执行的方法名->点击右键直接点击要执行方法左边的绿色小箭头
2.Junit的基本使用(重点)
注解:@Test -> 使用在要执行的方法上
public class Demo01Junit {@Testpublic void insert(){System.out.println("我是添加功能");}@Testpublic void delete(){System.out.println("我是删除功能");}}
3.Junit的注意事项
1.@Test修饰的方法,不能有参数2.@Test修饰的方法,不能有返回值3.@Test修饰的方法,不能是静态的public class Demo01Junit {@Testpublic void insert(){System.out.println("我是添加功能");}@Testpublic void delete(){System.out.println("我是删除功能");}public void login(int i){System.out.println("登录功能"+i);}@Testpublic void method(){login(1);}}
经验值:
使用完了单元测试运行,下面视图如果是绿色的,证明代码没啥问题
如果是红色的,证明代码有问题
4.Junit相关注解
@After:在@Test修饰的方法之后执行,有多少个@Test方法一起执行,@After修饰的方法就执行多少次@Before:在@Test修饰的方法之前执行,有多少个@Test方法一起执行,@Before修饰的方法就执行多少次
public class Demo02Junit {@Testpublic void insert(){System.out.println("我是添加功能");}@Testpublic void delete(){System.out.println("我是删除功能");}@Beforepublic void before(){System.out.println("我是before方法");}@Afterpublic void after(){System.out.println("我是after方法");}}
扩展注解:@BeforeClass:用于运行static的方法,在@Test之前执行,只执行一次@AfterClass:用于运行static的方法,在@Test之后执行,只执行一次
public class Demo03Junit {@Testpublic void insert() {System.out.println("我是添加用户方法");}@BeforeClasspublic static void before(){System.out.println("我是Before");}@AfterClasspublic static void after(){System.out.println("我是After");}@Testpublic void delete(){System.out.println("我是删除功能");}}
小结:1.Junit单元测试作用:测试某一个功能,代替main方法2.常用的注解@Test@Before:在@Test之前执行,有多少个@Test就执行几次@After:在@Test之后执行,有多少个@Test就执行几次3.扩展注解:@BeforeClass:修饰静态的方法,在@Test之前执行,只执行一次@AfterClass:修饰静态的方法,在@Test之后执行,只执行一次
第二章.类的加载时机
1.new 类的对象2.new 子类对象(new子类对象先初始化父类对象)3.类名调用静态成员4.利用反射技术5.利用java命令运行main方法
![day21[Junit单元测试_反射_注解] - 图2](/uploads/projects/liuye-6lcqc@vk53cd/c7da3a75f6d936615c24c54427959296.png)
1.类加载器(了解)
1.概述:在jvm中,负责将本地上的class文件加载到内存的对象2.分类:BootStrapClassLoader:根类加载器->C语言写的,我们是获取不到的也称之为引导类加载器,负责Java的核心类加载的比如:System,String等jre/lib/rt.jar下的类都是核心类ExtClassLoader:扩展类加载器负责jre的扩展目录中的jar包的加载在jdk中jre的lib目录下的ext目录AppClassLoader:系统类加载器负责在jvm启动时加载来自java命令的class文件(自定义类),以及classPath环境变量所指定的jar包(第三方jar包)不同的类加载器负责加载不同的类3.三者的关系:AppClassLoader的父类加载器是ExtClassLoaderExtClassLoader的父类加载器是BootStrapClassLoader但是:他们从代码级别上来看,没有子父类继承关系->他们都有一个共同的父类->ClassLoader4.获取类加载器对象类名.class.getClassLoader()5.获取类加载器对象对应的父类加载器ClassLoader类中的方法:ClassLoader6.双亲委派(全盘负责委托机制)->谁用谁加载a.Person类中有一个StringPerson本身是AppClassLoader加载String是BootStrapClassLoader加载b.加载顺序:Person本身是App加载,按道理来说String也是App加载但是App加载String的时候,先问一问Ext,说:Ext你加载这个String吗?Ext说:我不加载,我负责加载的是扩展类,但是app你别着急,我问问我爹去->bootExt说:boot,你加载String吗?boot说:正好我加载核心类,行吧,我加载吧!=======================================================================比如:class Test{new Person()}a.Test是app加载,person按理来说也是app加载,但是app先问ext要不要加载ext说不负责加载自定义类,我找boot去,boot一看,我不负责加载自定义类->perosnapp一看,两个爹都不加载,我自己加载b.结论:两个双亲都不加载,app才自己加载比如:如果来了一个DNSNameService,我就想直接加载DNSNameService(扩展类),本身ext要加载,但是先问boot,如果boot不加载,ext再加载=======================================================================7.类加载器的cache(缓存)机制(扩展):一个类加载到内存之后,缓存中也会保存一份儿,后面如果再使用此类,如果缓存中保存了这个类,就直接返回他,如果没有才加载这个类.下一次如果有其他类在使用的时候就不会重新加载了,直接去缓存中拿,所以这就是为什么每个类只加载一次,内存中只有一份儿的原因8.所以:类加载器的双亲委派和缓存机制共同造就了加载类的特点:每个类只在内存中加载一次
总结:类加载器:BootStrapClassLoader:加载核心类ExtClassLoader:加载扩展类AppClassLoader:加载自定义类以及第三方jar包加载顺序:先找自己的父类加载器,如果boot和Ext都不加载,app会自己加载类加载的机制:双亲委派+缓存机制类加载的机制造就了一个类加载的特点:每个类就加载一次
public class Test01_ClassLoader {public static void main(String[] args) {app();System.out.println("========================");ext();System.out.println("=======================");boot();}private static void boot() {ClassLoader classLoader = String.class.getClassLoader();System.out.println(classLoader);}private static void ext() {ClassLoader classLoader = DNSNameService.class.getClassLoader();System.out.println(classLoader);System.out.println(classLoader.getParent());}private static void app() {ClassLoader classLoader = Test01_ClassLoader.class.getClassLoader();System.out.println(classLoader);System.out.println(classLoader.getParent());}}
![day21[Junit单元测试_反射_注解] - 图3](/uploads/projects/liuye-6lcqc@vk53cd/41073fd026e4665af69089367a0fa232.png)
第三章.反射
1.class类的以及class对象的介绍以及反射介绍
![day21[Junit单元测试_反射_注解] - 图4](/uploads/projects/liuye-6lcqc@vk53cd/675d82f58a592538c45d5a34fe2d2697.png)
1.当Class文件被加载到内存中时,jvm会为其在堆内存中创建一个对应的Class对象
(被加载到内存中的Class携带很多成员,所以对象的Class对象也有很多成员)
2.反射就是解剖Class对象的,从而操作Class对象中的各个成员(构造,方法,变量,包括私有成员)
3.反射作用:
解剖Class对象让代码更灵活4.如何学反射:
a.先学API,知道如何操作Class对象中的成员
b.根据反射的代码理解反射的厉害之处
5.玩反射最重要的是先获取到Class对象
2.反射之获取Class对象
1.三种获取Class对象的方式a.new 对象 -> 调用Object类中的getClass方法b.类名.class -> jvm为基本数据类型和引用数据类型提供了一个静态的成员变量 classc.Class类中的静态方法static Class<?> forName(String className)参数:className:传递的是被反射类的全限定名(包名.类名)
public class Test01 {public static void main(String[] args) throws Exception{/*a.new 对象 -> 调用Object类中的getClass方法*/Person person = new Person();Class aClass = person.getClass();System.out.println(aClass);System.out.println("======================");/*类名.class -> jvm为基本数据类型和引用数据类型提供了一个静态的成员变量 class*/Class<Person> aClass1 = Person.class;System.out.println(aClass1);System.out.println("====================");/*Class类中的静态方法static Class<?> forName(String className)参数:className:传递的是被反射类的全限定名(包名.类名)*/Class<?> aClass2 = Class.forName("com.atguigu.c_reflect.Person");System.out.println(aClass2);/*由于类只加载一次,所以,三个Class对象比较地址值时是true*/System.out.println(aClass==aClass1);//trueSystem.out.println(aClass1==aClass2);//true}}
2.1.三种获取Class对象的方式最常用的一种
1.static Class<?> forName(String className)2.为啥会用第三种:forName传递的是字符串,可以直接和配置文件结合使用
在模块下创建properties文件,文件中写上:className=com.atguigu.c_reflect.Person
public class Test02 {public static void main(String[] args)throws Exception {//创建Properties集合Properties properties = new Properties();//创建FileInputStreamFileInputStream in = new FileInputStream("day21\\javabean.properties");//调用properties中的load方法,将流中的数据加载到集合中properties.load(in);String className = properties.getProperty("className");//获取class对象Class<?> aClass = Class.forName(className);System.out.println(aClass);}}
3.获取Class对象中的构造方法
3.1.获取所有public的构造方法
Constructor<?>[] getConstructors() -> 获取所有public的构造方法
public class Test01 {public static void main(String[] args)throws Exception {//获取Class对象Class<?> aClass = Class.forName("com.atguigu.d_reflect.Person");Constructor<?>[] constructors = aClass.getConstructors();//遍历数组for (Constructor<?> constructor : constructors) {System.out.println(constructor);}}}
3.2.获取空参构造
Constructor<T> getConstructor(Class<?>... parameterTypes)->获取指定的public的构造方法parameterTypes:可变参数->可以传递0个或者多个参数参数类型:Class类型如果想获取空参构造:getConstructor方法的参数不用写如果想获取有参构造:getConstructor方法的参数传递构造方法参数类型的class对象->比如:getConstructor(String.class,int.class)利用获取出来的构造方法创建对象:Constructor类中的方法T newInstance(Object... initargs)initargs:可变参数->需要传递实参如果根据获取出来的空参构造new对象,参数不用写如果根据获取出来的有参构造new对象,传递实参,为属性赋值
public class Test02 {public static void main(String[] args)throws Exception {//获取Class对象Class<?> aClass = Class.forName("com.atguigu.d_reflect.Person");//获取空参构造Constructor<?> constructor = aClass.getConstructor();System.out.println(constructor);//根据空参构造创建对象/*相当于:Person p = new Person()*/Object o = constructor.newInstance();/*相当于直接输出了p,默认调用了重写的toString方法*/System.out.println(o);}}
3.3.利用空参构造创建对象的快捷方式
1.利用Class类中的方法:T newInstance() -> 根据空参构造创建对象2.注意:使用Class类中的newInstance方法时,被反射的类中必须有public的空参构造
public class Test03 {public static void main(String[] args)throws Exception {//获取Class对象Class<?> aClass = Class.forName("com.atguigu.d_reflect.Person");/*相当于Person p = new Person()*/Object o = aClass.newInstance();/*相当于输出p.默认调用重写的toString方法*/System.out.println(o);}}
3.4.利用反射获取有参构造并创建对象
Constructor<T> getConstructor(Class<?>... parameterTypes)->获取指定的public的构造方法parameterTypes:可变参数->可以传递0个或者多个参数参数类型:Class类型如果想获取空参构造:getConstructor方法的参数不用写如果想获取有参构造:getConstructor方法的参数传递构造方法参数类型的class对象->比如:getConstructor(String.class,int.class)利用获取出来的构造方法创建对象:Constructor类中的方法T newInstance(Object... initargs)initargs:可变参数->需要传递实参如果根据获取出来的空参构造new对象,参数不用写如果根据获取出来的有参构造new对象,传递实参,为属性赋值
public class Test04 {public static void main(String[] args)throws Exception {//获取Class对象Class<?> aClass = Class.forName("com.atguigu.d_reflect.Person");//获取有参构造Constructor<?> constructor = aClass.getConstructor(String.class, int.class);System.out.println(constructor);//根据有参构造创建对象/*相当于:Person p = new Person("柳岩",36)*/Object o = constructor.newInstance("柳岩", 36);/*相当于直接输出p,默认调用重写toString方法*/System.out.println(o);}}
3.5.利用反射获取私有构造(暴力反射)
1.Constructor<?>[] getDeclaredConstructors()->获取所有的构造,包括public的以及private的2.Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) ->获取指定的构造,包括public以及private的参数:parameterTypes->可变参数,可以传递0个或者多个参数传递构造方法参数类型的class对象3.暴力反射(解除成员的私有权限)-> Constructor的父类AccessibleObject中的方法void setAccessible(boolean flag) ->flag:为true -> 解除私有权限 -> 暴力反射4.利用获取出来的构造方法创建对象:Constructor类中的方法T newInstance(Object... initargs)initargs:可变参数->需要传递实参如果根据获取出来的空参构造new对象,参数不用写如果根据获取出来的有参构造new对象,传递实参,为属性赋值
public class Test05 {public static void main(String[] args)throws Exception {// method01_Constructor();method02_Constructor();}/*获取指定的私有构造*/private static void method02_Constructor()throws Exception {//获取Class对象Class<?> aClass = Class.forName("com.atguigu.d_reflect.Person");Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class);System.out.println(declaredConstructor);/*解除私有权限->暴力反射*/declaredConstructor.setAccessible(true);//根据获取出来的构造创建对象Object o = declaredConstructor.newInstance("柳岩");System.out.println(o);}/*获取所有的构造方法:包括public以及private的*/public static void method01_Constructor() throws ClassNotFoundException {//获取Class对象Class<?> aClass = Class.forName("com.atguigu.d_reflect.Person");Constructor<?>[] constructors = aClass.getDeclaredConstructors();//遍历for (Constructor<?> constructor : constructors) {System.out.println(constructor);}}}
4.反射方法
4.1.利用反射获取所有成员方法
Method[] getMethods() -> 获取所有的public的成员方法->包括继承的
public class Test01 {public static void main(String[] args) throws Exception {Class<?> aClass = Class.forName("com.atguigu.e_reflect.Person");Method[] methods = aClass.getMethods();for (Method method : methods) {System.out.println(method);}}}
4.2.反射之获取方法(有参,无参)
Class类中的方法:Method getMethod(String name, Class<?>... parameterTypes)name:传递的是要反射的方法名parameterTypes:可变参数,传递的是被反射方法参数的class对象如果反射的方法没有参数,那么parameterTypes就不写了如果反射的方法有参数的,就传递参数类型的class对象Method类中的方法:Object invoke(Object obj, Object... args)参数:obj:传递的是根据反射出来的构造方法创建出来的对象args:可变参数->如果执行的是空参的方法,args不写如果执行的是有参的方法,args处传递被反射方法的实际参数返回值:Object->接收被反射方法的返回值如果执行的方法没有返回值,不用Object接收如果指定的方法有返回值,Object用于接收被执行方法的返回值
public class Test02 {public static void main(String[] args) throws Exception {Class<?> aClass = Class.forName("com.atguigu.e_reflect.Person");//获取对象/*Person p = new Person()*/Object o = aClass.newInstance();/*获取setName*/Method setName = aClass.getMethod("setName", String.class);/*执行setName由于setName没有返回值,所以调用invoke之后,不同invoke的返回值Object接收相当于 p.setName("柳岩")*/setName.invoke(o,"柳岩");System.out.println("==================================");/*获取指定的成员方法->反射getName()由于getName有返回值,所以调用invoke之后,用invoke的返回值Object接收*/Method getName = aClass.getMethod("getName");//执行方法/*相当于String name = p.getName()*/Object o1 = getName.invoke(o);System.out.println(o1);}}
5.反射练习(编写一个小框架)
利用反射,解析配置文件中的信息:properties文件类的全限定名: className=包名.类名方法名: methodName=方法名需求:利用反射解析properties文件,获取配置信息根据配置信息,执行配置文件中的方法步骤:1.创建配置文件->properties文件properties文件放在什么位置:a.我们将来开发完毕,给用户的是out路径下的class文件,如果将配置文件放在当前模块下,那么out目录 下是没有配置文件的,那么用户一执行,由于需要这个配置文件中的信息,那么用户执行起来会有问题b.如何解决a的问题:我们只需要将配置文件放在src下,程序一编译,配置文件自然会在out路径下出现c.如果将配置文件放在src下面那么我们读取配置文件代码怎么写?之前写法:直接 new FileInputStream("模块名\\src\\配置文件名")不行因为这样写,我们带了src路径,但是我们给用户的out目录下是没有src路径,所以这样写死不行用户一执行,由于用户这边用的没有src的out路径,那么就会报错d.解决:使用类加载器->扩展ClassLoader类中的方法:InputStream getResourceAsStream(String name) ->name直接写文件名,返回字节流此方法可以自动扫描src下的配置文件当前类.class->获取class对象class对象.getClassLoader()->获取了类加载器类加载器.getResourceAsStream(String name)->扫描到src下的配置文件了链式调用:当前类名.class.getClassLoader().getResourceAsStream(String name)2.利用IO读取配置文件,将配置文件信息放在Properties集合当中3.获取Properties中对应的值获取类的全限定名方法名4.利用反射获取类的Class对象->根据解析出来的类的全限定名获取Class对象利用反射执行配置文件中的方法
在src下创建一个properties文件className=cn.atguigu.PersonmethodName=eat
public class Test {public static void main(String[] args)throws Exception {//1.解析配置文件Properties properties = new Properties();//2.读取配置文件InputStream in= Test.class.getClassLoader().getResourceAsStream("pojo.properties");//3.调用load方法,将流中的文件信息加载到Properties集合中properties.load(in);System.out.println(properties);String className = properties.getProperty("className");String methodName = properties.getProperty("methodName");//4.根据获取出来的className(类的全限定名)获取对应的class对象Class<?> aClass = Class.forName(className);//5.利用反射创建对象Object o = aClass.newInstance();//6.根据获取出来的methodName获取对应的方法Method method = aClass.getMethod(methodName);//7.执行方法method.invoke(o);}}
第四章.注解
1.注解的介绍
1.jdk1.5版本的新特性->一个引用数据类型和类,接口,枚举是同一个层次的2.作用:说明:对代码进行说明,生成doc文档(API文档)(不会用)检查:检查代码是否符合条件 @Override(会用) @FunctionalInterface分析:对代码进行分析,起到了代替配置文件的作用(会用)3.JDK中的注解:@Override -> 检测此方法是否为重写方法jdk1.5版本,支持父类的方法重写jdk1.6版本,支持接口的方法重写@Deprecated -> 方法已经过时,不推荐使用调用方法的时候,方法上会有横线,但是能用@SuppressWarnings->消除警告 @SuppressWarnings("all")
2.注解的定义以及属性的定义格式
1.格式:public @interface 注解名{}2.定义属性:加大注解的作用数据类型 属性名() -> 在使用注解的时候,必须要给属性赋值数据类型 属性名() default 值-> 属性是带默认值的,在使用注解的时候,无需给此属性赋值也不用写,如果需要改变默认值,重新赋值3. 注解中都可以定义什么类型的属性呢?a.8种基本数据类型(byte short int long char float double boolean)b.String类型 Class类型 枚举类型 注解类型c.以及以上类型的一维数组
public @interface Book {//书名String name();//价格double price();//作者String[] author();//数量//Integer count();不能是包装类型int count() default 10;}
3.注解的使用(重点)
1.所谓的注解使用:为注解中的属性赋值2.使用的位置:类上 方法上 成员变量上 局部变量上 参数上3.如何使用:@注解名(属性名 = 属性值,属性名 = 属性值...)4.如果属性是数组:@注解名(属性名 = {元素1,元素2...})
@Book(name = "三国",price = 23,author = {"罗贯中","涛哥"})public class BookSelf {@Book(name = "三国",price = 23,author = {"罗贯中","涛哥"})public void method(){}}
注解注意事项:1.空注解可以直接使用->空注解就是注解中没有任何的属性2.不同的位置可以使用一样的注解,但是同样的位置不能使用一样的注解3.使用注解时,如果此注解中有属性,注解中的属性一定要赋值,如果有多个属性,用,隔开如果注解中的属性有数组,那么如果数组只有一个元素值,那么{}不用写,反之用写4.如果注解中的属性值有默认值,那么我们不必要写,也不用重新赋值,反之必须写上5.如果注解中只有一个属性,并且属性名叫value,那么使用注解的时候,属性名不用写,直接写值(包括单个类型,还包括数组)
4.注解解析的方法->AnnotatedElement接口
解析注解:获取注解中的属性值AnnotatedElement接口的实现类:Class Method Constructor等接口:AnnotatedElement->专门用于注解解析的boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)判断当前Class对象上是否有指定的注解,如果返回false,证明没注解,否则证明有注解比如:判断BookShelf类上是否有Book注解Class class = BookShelf.class;class.isAnnotationPresent(Book.class)->判断BookShelf类上有没有Book注解==========================================================================<T extends Annotation> T getAnnotation(Class<T> annotationClass) ->获取当前Class对象上的指定注解比如:Class class = BookShelf.class;class.isAnnotationPresent(Book.class)->判断BookShelf类上有没有Book注解如果有,返回true,就要获取这个注解了:class.getAnnotation(Book.class)->获取BookShelf上的Book注解=============================================================================注解的解析思想:解析类上的注解:1.反射带有注解的类,获取类的Class对象2.判断这类上有没有注解:isAnnotationPresent3.获取这个注解4.获取注解中的属性值:getAnnotation解析方法上的注解1.反射带有注解的类->Class对象2.获取方法:Method3.判断方法上有没有注解:method.isAnnotationPresent4.获取这个方法上的注解:method.getAnnotation5.获取注解中的属性值
public @interface Book {//书名String name();//价格double price();//作者String[] author();}@Book(name = "三国",price = 23,author = {"罗贯中","涛哥"})public class BookSelf {}
public class Test01 {public static void main(String[] args) {//1.获取BookSelf的Class对象Class<BookSelf> bookSelfClass = BookSelf.class;//2.判断BookSelf上有没有Book注解boolean b = bookSelfClass.isAnnotationPresent(Book.class);//3.判断,如果b是true,证明BookSelf上有Book注解,就获取这个注解,在获取注解上的属性值if (b){//4.获取Book注解Book book = bookSelfClass.getAnnotation(Book.class);//5.获取Book注解中的属性值System.out.println(book.name());System.out.println(book.price());System.out.println(Arrays.toString(book.author()));}}}以上代码,解析失败
涛哥猜想:我们的解析代码是在内存中运行的,如果Book注解在内存中出现了,我一定能获取到Book注解
但是,我们这个代码就是没有获取到Book注解,我们猜想,Book注解就没有在内存中
第五章.元注解
1.概述:管理注解的注解,可以理解为是自定义注解的总管2.问题:元注解从哪些方面去管理了自定义的注解?方面1:可以控制自定义注解的编写位置可以控制,自定义注解是否能在类上使用可以控制,自定义注解是否能在方法上使用可以控制,自定义注解是否能在成员变量上使用可以控制,自定义注解是否能在参数位置上使用可以控制,自定义注解是否能在局部变量上使用方面2:可以控制自定义注解的生命周期可以控制自定义注解是否只存在在源码中可以控制自定义注解是否存在在Class文件中可以控制自定义注解是否只存在在内存中3.使用元注解@Target:控制自定义注解的编写位置a.属性:ElementType[] value();b.ElementType:枚举,成员都是静态的-> 使用可以类名直接调用TYPE:允许自定义注解在类上使用FIELD:允许自定义注解在成员变量上使用METHOD:允许自定义注解在方法上使用PARAMETER:允许自定义注解在参数上使用CONSTRUCTOR:允许自定义注解在构造上使用LOCAL_VARIABLE:允许自定义注解在局部变量上使用@Retention:控制自定义注解的生命周期a.属性:RetentionPolicy value();b.RetentionPolicy:枚举,成员都是静态的-> 使用可以类名直接调用SOURCE:允许自定义注解只存在在源码中->默认的CLASS:允许自定义注解存在在Class文件中RUNTIME:允许自定义注解存在在内存中
@Target({ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface Book {//书名String name();//价格int price();//作者String[] author();//数量->不能写包装类//Integer count();int count() default 10;}
1.注解再次解析
public class Test01 {public static void main(String[] args) {//1.获取BookSelf的Class对象Class<BookSelf> bookSelfClass = BookSelf.class;//2.判断BookSelf上有没有Book注解boolean b = bookSelfClass.isAnnotationPresent(Book.class);System.out.println(b);//3.判断,如果b是true,证明BookSelf上有Book注解,就获取这个注解,在获取注解上的属性值if (b){//4.获取Book注解Book book = bookSelfClass.getAnnotation(Book.class);//5.获取Book注解中的属性值System.out.println(book.name());System.out.println(book.price());System.out.println(Arrays.toString(book.author()));}}}
第一次解析由于我们自己的注解Book默认是SOURCE,只能出现在源码中所以我们没有解析出来
第二次我们将SOURCE变成了RUNTIME,证明注解Book能出现在内存中了,所以我们解析出来了
![day21[Junit单元测试_反射_注解] - 图1](/uploads/projects/liuye-6lcqc@vk53cd/224c9c75e35abf277835e95c38109290.png)
