课前回顾:
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.jar
c.右键jar包,选择 add as library
d.level选择module,点ok
4.导完jar包之后怎么用:
注解: @Test
使用位置: 在方法上使用@Test
双击选中要执行的方法名->点击右键
直接点击要执行方法左边的绿色小箭头
2.Junit的基本使用(重点)
注解:
@Test -> 使用在要执行的方法上
public class Demo01Junit {
@Test
public void insert(){
System.out.println("我是添加功能");
}
@Test
public void delete(){
System.out.println("我是删除功能");
}
}
3.Junit的注意事项
1.@Test修饰的方法,不能有参数
2.@Test修饰的方法,不能有返回值
3.@Test修饰的方法,不能是静态的
public class Demo01Junit {
@Test
public void insert(){
System.out.println("我是添加功能");
}
@Test
public void delete(){
System.out.println("我是删除功能");
}
public void login(int i){
System.out.println("登录功能"+i);
}
@Test
public void method(){
login(1);
}
}
经验值:
使用完了单元测试运行,下面视图如果是绿色的,证明代码没啥问题
如果是红色的,证明代码有问题
4.Junit相关注解
@After:在@Test修饰的方法之后执行,有多少个@Test方法一起执行,@After修饰的方法就执行多少次
@Before:在@Test修饰的方法之前执行,有多少个@Test方法一起执行,@Before修饰的方法就执行多少次
public class Demo02Junit {
@Test
public void insert(){
System.out.println("我是添加功能");
}
@Test
public void delete(){
System.out.println("我是删除功能");
}
@Before
public void before(){
System.out.println("我是before方法");
}
@After
public void after(){
System.out.println("我是after方法");
}
}
扩展注解:
@BeforeClass:用于运行static的方法,在@Test之前执行,只执行一次
@AfterClass:用于运行static的方法,在@Test之后执行,只执行一次
public class Demo03Junit {
@Test
public void insert() {
System.out.println("我是添加用户方法");
}
@BeforeClass
public static void before(){
System.out.println("我是Before");
}
@AfterClass
public static void after(){
System.out.println("我是After");
}
@Test
public 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方法
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的父类加载器是ExtClassLoader
ExtClassLoader的父类加载器是BootStrapClassLoader
但是:他们从代码级别上来看,没有子父类继承关系->他们都有一个共同的父类->ClassLoader
4.获取类加载器对象
类名.class.getClassLoader()
5.获取类加载器对象对应的父类加载器
ClassLoader类中的方法:ClassLoader
6.双亲委派(全盘负责委托机制)->谁用谁加载
a.Person类中有一个String
Person本身是AppClassLoader加载
String是BootStrapClassLoader加载
b.加载顺序:
Person本身是App加载,按道理来说String也是App加载
但是App加载String的时候,先问一问Ext,说:Ext你加载这个String吗?
Ext说:我不加载,我负责加载的是扩展类,但是app你别着急,我问问我爹去->boot
Ext说:boot,你加载String吗?
boot说:正好我加载核心类,行吧,我加载吧!
=======================================================================
比如:
class Test{
new Person()
}
a.Test是app加载,person按理来说也是app加载,但是app先问ext要不要加载
ext说不负责加载自定义类,我找boot去,boot一看,我不负责加载自定义类->perosn
app一看,两个爹都不加载,我自己加载
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());
}
}
第三章.反射
1.class类的以及class对象的介绍以及反射介绍
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为基本数据类型和引用数据类型提供了一个静态的成员变量 class
c.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);//true
System.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();
//创建FileInputStream
FileInputStream 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.Person
methodName=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.判断这类上有没有注解:isAnnotationPresent
3.获取这个注解
4.获取注解中的属性值:getAnnotation
解析方法上的注解
1.反射带有注解的类->Class对象
2.获取方法:Method
3.判断方法上有没有注解:method.isAnnotationPresent
4.获取这个方法上的注解:method.getAnnotation
5.获取注解中的属性值
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能出现在内存中了,所以我们解析出来了