1. 课前回顾:
  2. 1.通信三要素:
  3. IP:每一台计算机的唯一标识,用于两台计算机连接
  4. 协议:通信时要遵循的规则
  5. TCP:面向连接协议
  6. 三次握手
  7. 断开连接时:四次挥手
  8. 数据安全,但是传输效率慢
  9. UDP:面向无连接协议,传输速度快,数据不安全
  10. 端口号:是应用程序的唯一标识
  11. 2.两个对象
  12. Socket对象->代表的是客户端对象
  13. ServerSocket->代表的是服务端对象
  14. Socket accept()->自带阻塞效果,接收的是连接服务器的客户端对象
  15. 今日重点:
  16. 1.掌握单元测试的使用
  17. 2.知道反射是干啥的->获取Class对象,以及操作Class对象中的成员
  18. 3.怎么学今天的内容:
  19. 把反射看成一套API去学
  20. 通过反射练习体会反射的好处
  21. 4.会获取Class对象
  22. 5.会根据Class对象,获取Class对象中的构造方法,成员方法并操作
  23. 6.会使用注解
  24. 7.知道元注解的作用

第一章 Junit单元测试

1.Junit介绍

  1. 1.概述:Junit是一个Java语言的单元测试框架,简单理解为在一定程度上用于取代main方法.Junit属于第三方工具,需要导入Jar包后再使用
  2. 2.作用:
  3. 如果想单独执行一个方法,就不需要在main方法中调用了
  4. 3.jar包->导入Junitjar
  5. a.在当前模块下创建一个lib文件夹
  6. b.将Junitjar包粘贴到这个文件夹下:junit-4.9.jar
  7. c.右键jar包,选择 add as library
  8. d.level选择module,点ok
  9. 4.导完jar包之后怎么用:
  10. 注解: @Test
  11. 使用位置: 在方法上使用@Test
  12. 双击选中要执行的方法名->点击右键
  13. 直接点击要执行方法左边的绿色小箭头

2.Junit的基本使用(重点)

  1. 注解:
  2. @Test -> 使用在要执行的方法上
  1. public class Demo01Junit {
  2. @Test
  3. public void insert(){
  4. System.out.println("我是添加功能");
  5. }
  6. @Test
  7. public void delete(){
  8. System.out.println("我是删除功能");
  9. }
  10. }

3.Junit的注意事项

  1. 1.@Test修饰的方法,不能有参数
  2. 2.@Test修饰的方法,不能有返回值
  3. 3.@Test修饰的方法,不能是静态的
  4. public class Demo01Junit {
  5. @Test
  6. public void insert(){
  7. System.out.println("我是添加功能");
  8. }
  9. @Test
  10. public void delete(){
  11. System.out.println("我是删除功能");
  12. }
  13. public void login(int i){
  14. System.out.println("登录功能"+i);
  15. }
  16. @Test
  17. public void method(){
  18. login(1);
  19. }
  20. }

经验值:

使用完了单元测试运行,下面视图如果是绿色的,证明代码没啥问题

如果是红色的,证明代码有问题

day21[Junit单元测试_反射_注解] - 图1

4.Junit相关注解

  1. @After:在@Test修饰的方法之后执行,有多少个@Test方法一起执行,@After修饰的方法就执行多少次
  2. @Before:在@Test修饰的方法之前执行,有多少个@Test方法一起执行,@Before修饰的方法就执行多少次
  1. public class Demo02Junit {
  2. @Test
  3. public void insert(){
  4. System.out.println("我是添加功能");
  5. }
  6. @Test
  7. public void delete(){
  8. System.out.println("我是删除功能");
  9. }
  10. @Before
  11. public void before(){
  12. System.out.println("我是before方法");
  13. }
  14. @After
  15. public void after(){
  16. System.out.println("我是after方法");
  17. }
  18. }
  1. 扩展注解:
  2. @BeforeClass:用于运行static的方法,在@Test之前执行,只执行一次
  3. @AfterClass:用于运行static的方法,在@Test之后执行,只执行一次
  1. public class Demo03Junit {
  2. @Test
  3. public void insert() {
  4. System.out.println("我是添加用户方法");
  5. }
  6. @BeforeClass
  7. public static void before(){
  8. System.out.println("我是Before");
  9. }
  10. @AfterClass
  11. public static void after(){
  12. System.out.println("我是After");
  13. }
  14. @Test
  15. public void delete(){
  16. System.out.println("我是删除功能");
  17. }
  18. }
  1. 小结:
  2. 1.Junit单元测试作用:测试某一个功能,代替main方法
  3. 2.常用的注解
  4. @Test
  5. @Before:在@Test之前执行,有多少个@Test就执行几次
  6. @After:在@Test之后执行,有多少个@Test就执行几次
  7. 3.扩展注解:
  8. @BeforeClass:修饰静态的方法,在@Test之前执行,只执行一次
  9. @AfterClass:修饰静态的方法,在@Test之后执行,只执行一次

第二章.类的加载时机

  1. 1.new 类的对象
  2. 2.new 子类对象(new子类对象先初始化父类对象)
  3. 3.类名调用静态成员
  4. 4.利用反射技术
  5. 5.利用java命令运行main方法

day21[Junit单元测试_反射_注解] - 图2

1.类加载器(了解)

  1. 1.概述:
  2. jvm中,负责将本地上的class文件加载到内存的对象
  3. 2.分类:
  4. BootStrapClassLoader:根类加载器->C语言写的,我们是获取不到的
  5. 也称之为引导类加载器,负责Java的核心类加载的
  6. 比如:System,String
  7. jre/lib/rt.jar下的类都是核心类
  8. ExtClassLoader:扩展类加载器
  9. 负责jre的扩展目录中的jar包的加载
  10. jdkjrelib目录下的ext目录
  11. AppClassLoader:系统类加载器
  12. 负责在jvm启动时加载来自java命令的class文件(自定义类),以及classPath环境变量所指定的jar包(第三方jar包)
  13. 不同的类加载器负责加载不同的类
  14. 3.三者的关系:AppClassLoader的父类加载器是ExtClassLoader
  15. ExtClassLoader的父类加载器是BootStrapClassLoader
  16. 但是:他们从代码级别上来看,没有子父类继承关系->他们都有一个共同的父类->ClassLoader
  17. 4.获取类加载器对象
  18. 类名.class.getClassLoader()
  19. 5.获取类加载器对象对应的父类加载器
  20. ClassLoader类中的方法:ClassLoader
  21. 6.双亲委派(全盘负责委托机制)->谁用谁加载
  22. a.Person类中有一个String
  23. Person本身是AppClassLoader加载
  24. StringBootStrapClassLoader加载
  25. b.加载顺序:
  26. Person本身是App加载,按道理来说String也是App加载
  27. 但是App加载String的时候,先问一问Ext,说:Ext你加载这个String吗?
  28. Ext说:我不加载,我负责加载的是扩展类,但是app你别着急,我问问我爹去->boot
  29. Ext说:boot,你加载String吗?
  30. boot说:正好我加载核心类,行吧,我加载吧!
  31. =======================================================================
  32. 比如:
  33. class Test{
  34. new Person()
  35. }
  36. a.Testapp加载,person按理来说也是app加载,但是app先问ext要不要加载
  37. ext说不负责加载自定义类,我找boot去,boot一看,我不负责加载自定义类->perosn
  38. app一看,两个爹都不加载,我自己加载
  39. b.结论:两个双亲都不加载,app才自己加载
  40. 比如:如果来了一个DNSNameService,我就想直接加载DNSNameService(扩展类),
  41. 本身ext要加载,但是先问boot,如果boot不加载,ext再加载
  42. =======================================================================
  43. 7.类加载器的cache(缓存)机制(扩展):一个类加载到内存之后,缓存中也会保存一份儿,后面如果再使用此类,如果缓存中保存了这个类,就直接返回他,如果没有才加载这个类.下一次如果有其他类在使用的时候就不会重新加载了,直接去缓存中拿,所以这就是为什么每个类只加载一次,内存中只有一份儿的原因
  44. 8.所以:类加载器的双亲委派和缓存机制共同造就了加载类的特点:每个类只在内存中加载一次
  1. 总结:
  2. 类加载器:
  3. BootStrapClassLoader:加载核心类
  4. ExtClassLoader:加载扩展类
  5. AppClassLoader:加载自定义类以及第三方jar
  6. 加载顺序:
  7. 先找自己的父类加载器,如果bootExt都不加载,app会自己加载
  8. 类加载的机制:双亲委派+缓存机制
  9. 类加载的机制造就了一个类加载的特点:每个类就加载一次
  1. public class Test01_ClassLoader {
  2. public static void main(String[] args) {
  3. app();
  4. System.out.println("========================");
  5. ext();
  6. System.out.println("=======================");
  7. boot();
  8. }
  9. private static void boot() {
  10. ClassLoader classLoader = String.class.getClassLoader();
  11. System.out.println(classLoader);
  12. }
  13. private static void ext() {
  14. ClassLoader classLoader = DNSNameService.class.getClassLoader();
  15. System.out.println(classLoader);
  16. System.out.println(classLoader.getParent());
  17. }
  18. private static void app() {
  19. ClassLoader classLoader = Test01_ClassLoader.class.getClassLoader();
  20. System.out.println(classLoader);
  21. System.out.println(classLoader.getParent());
  22. }
  23. }

day21[Junit单元测试_反射_注解] - 图3

第三章.反射

1.class类的以及class对象的介绍以及反射介绍

day21[Junit单元测试_反射_注解] - 图4

1.当Class文件被加载到内存中时,jvm会为其在堆内存中创建一个对应的Class对象

(被加载到内存中的Class携带很多成员,所以对象的Class对象也有很多成员)

2.反射就是解剖Class对象的,从而操作Class对象中的各个成员(构造,方法,变量,包括私有成员)

3.反射作用:

  1. 解剖Class对象
  2. 让代码更灵活

4.如何学反射:

a.先学API,知道如何操作Class对象中的成员

b.根据反射的代码理解反射的厉害之处

5.玩反射最重要的是先获取到Class对象

2.反射之获取Class对象

  1. 1.三种获取Class对象的方式
  2. a.new 对象 -> 调用Object类中的getClass方法
  3. b.类名.class -> jvm为基本数据类型和引用数据类型提供了一个静态的成员变量 class
  4. c.Class类中的静态方法
  5. static Class<?> forName(String className)
  6. 参数:
  7. className:传递的是被反射类的全限定名(包名.类名)
  1. public class Test01 {
  2. public static void main(String[] args) throws Exception{
  3. /*
  4. a.new 对象 -> 调用Object类中的getClass方法
  5. */
  6. Person person = new Person();
  7. Class aClass = person.getClass();
  8. System.out.println(aClass);
  9. System.out.println("======================");
  10. /*
  11. 类名.class -> jvm为基本数据类型和引用数据类型提供了一个静态的成员变量 class
  12. */
  13. Class<Person> aClass1 = Person.class;
  14. System.out.println(aClass1);
  15. System.out.println("====================");
  16. /*
  17. Class类中的静态方法
  18. static Class<?> forName(String className)
  19. 参数:
  20. className:传递的是被反射类的全限定名(包名.类名)
  21. */
  22. Class<?> aClass2 = Class.forName("com.atguigu.c_reflect.Person");
  23. System.out.println(aClass2);
  24. /*
  25. 由于类只加载一次,所以,三个Class对象比较地址值时是true
  26. */
  27. System.out.println(aClass==aClass1);//true
  28. System.out.println(aClass1==aClass2);//true
  29. }
  30. }

2.1.三种获取Class对象的方式最常用的一种

  1. 1.static Class<?> forName(String className)
  2. 2.为啥会用第三种:
  3. forName传递的是字符串,可以直接和配置文件结合使用
  1. 在模块下创建properties文件,文件中写上:
  2. className=com.atguigu.c_reflect.Person
  1. public class Test02 {
  2. public static void main(String[] args)throws Exception {
  3. //创建Properties集合
  4. Properties properties = new Properties();
  5. //创建FileInputStream
  6. FileInputStream in = new FileInputStream("day21\\javabean.properties");
  7. //调用properties中的load方法,将流中的数据加载到集合中
  8. properties.load(in);
  9. String className = properties.getProperty("className");
  10. //获取class对象
  11. Class<?> aClass = Class.forName(className);
  12. System.out.println(aClass);
  13. }
  14. }

3.获取Class对象中的构造方法

3.1.获取所有public的构造方法

  1. Constructor<?>[] getConstructors() -> 获取所有public的构造方法
  1. public class Test01 {
  2. public static void main(String[] args)throws Exception {
  3. //获取Class对象
  4. Class<?> aClass = Class.forName("com.atguigu.d_reflect.Person");
  5. Constructor<?>[] constructors = aClass.getConstructors();
  6. //遍历数组
  7. for (Constructor<?> constructor : constructors) {
  8. System.out.println(constructor);
  9. }
  10. }
  11. }

3.2.获取空参构造

  1. Constructor<T> getConstructor(Class<?>... parameterTypes)->获取指定的public的构造方法
  2. parameterTypes:可变参数->可以传递0个或者多个参数
  3. 参数类型:Class类型
  4. 如果想获取空参构造:getConstructor方法的参数不用写
  5. 如果想获取有参构造:getConstructor方法的参数传递构造方法参数类型的
  6. class对象->比如:getConstructor(String.class,int.class)
  7. 利用获取出来的构造方法创建对象:Constructor类中的方法
  8. T newInstance(Object... initargs)
  9. initargs:可变参数->需要传递实参
  10. 如果根据获取出来的空参构造new对象,参数不用写
  11. 如果根据获取出来的有参构造new对象,传递实参,为属性赋值
  1. public class Test02 {
  2. public static void main(String[] args)throws Exception {
  3. //获取Class对象
  4. Class<?> aClass = Class.forName("com.atguigu.d_reflect.Person");
  5. //获取空参构造
  6. Constructor<?> constructor = aClass.getConstructor();
  7. System.out.println(constructor);
  8. //根据空参构造创建对象
  9. /*
  10. 相当于:Person p = new Person()
  11. */
  12. Object o = constructor.newInstance();
  13. /*
  14. 相当于直接输出了p,默认调用了重写的toString方法
  15. */
  16. System.out.println(o);
  17. }
  18. }

3.3.利用空参构造创建对象的快捷方式

  1. 1.利用Class类中的方法:
  2. T newInstance() -> 根据空参构造创建对象
  3. 2.注意:
  4. 使用Class类中的newInstance方法时,被反射的类中必须有public的空参构造
  1. public class Test03 {
  2. public static void main(String[] args)throws Exception {
  3. //获取Class对象
  4. Class<?> aClass = Class.forName("com.atguigu.d_reflect.Person");
  5. /*
  6. 相当于Person p = new Person()
  7. */
  8. Object o = aClass.newInstance();
  9. /*
  10. 相当于输出p.默认调用重写的toString方法
  11. */
  12. System.out.println(o);
  13. }
  14. }

3.4.利用反射获取有参构造并创建对象

  1. Constructor<T> getConstructor(Class<?>... parameterTypes)->获取指定的public的构造方法
  2. parameterTypes:可变参数->可以传递0个或者多个参数
  3. 参数类型:Class类型
  4. 如果想获取空参构造:getConstructor方法的参数不用写
  5. 如果想获取有参构造:getConstructor方法的参数传递构造方法参数类型的
  6. class对象->比如:getConstructor(String.class,int.class)
  7. 利用获取出来的构造方法创建对象:Constructor类中的方法
  8. T newInstance(Object... initargs)
  9. initargs:可变参数->需要传递实参
  10. 如果根据获取出来的空参构造new对象,参数不用写
  11. 如果根据获取出来的有参构造new对象,传递实参,为属性赋值
  1. public class Test04 {
  2. public static void main(String[] args)throws Exception {
  3. //获取Class对象
  4. Class<?> aClass = Class.forName("com.atguigu.d_reflect.Person");
  5. //获取有参构造
  6. Constructor<?> constructor = aClass.getConstructor(String.class, int.class);
  7. System.out.println(constructor);
  8. //根据有参构造创建对象
  9. /*
  10. 相当于:Person p = new Person("柳岩",36)
  11. */
  12. Object o = constructor.newInstance("柳岩", 36);
  13. /*
  14. 相当于直接输出p,默认调用重写toString方法
  15. */
  16. System.out.println(o);
  17. }
  18. }

3.5.利用反射获取私有构造(暴力反射)

  1. 1.Constructor<?>[] getDeclaredConstructors()->获取所有的构造,包括public的以及private
  2. 2.Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) ->获取指定的构造,包括public以及private
  3. 参数:parameterTypes->可变参数,可以传递0个或者多个参数
  4. 传递构造方法参数类型的class对象
  5. 3.暴力反射(解除成员的私有权限)-> Constructor的父类AccessibleObject中的方法
  6. void setAccessible(boolean flag) ->flag:为true -> 解除私有权限 -> 暴力反射
  7. 4.利用获取出来的构造方法创建对象:Constructor类中的方法
  8. T newInstance(Object... initargs)
  9. initargs:可变参数->需要传递实参
  10. 如果根据获取出来的空参构造new对象,参数不用写
  11. 如果根据获取出来的有参构造new对象,传递实参,为属性赋值
  1. public class Test05 {
  2. public static void main(String[] args)throws Exception {
  3. // method01_Constructor();
  4. method02_Constructor();
  5. }
  6. /*
  7. 获取指定的私有构造
  8. */
  9. private static void method02_Constructor()throws Exception {
  10. //获取Class对象
  11. Class<?> aClass = Class.forName("com.atguigu.d_reflect.Person");
  12. Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class);
  13. System.out.println(declaredConstructor);
  14. /*
  15. 解除私有权限->暴力反射
  16. */
  17. declaredConstructor.setAccessible(true);
  18. //根据获取出来的构造创建对象
  19. Object o = declaredConstructor.newInstance("柳岩");
  20. System.out.println(o);
  21. }
  22. /*
  23. 获取所有的构造方法:包括public以及private的
  24. */
  25. public static void method01_Constructor() throws ClassNotFoundException {
  26. //获取Class对象
  27. Class<?> aClass = Class.forName("com.atguigu.d_reflect.Person");
  28. Constructor<?>[] constructors = aClass.getDeclaredConstructors();
  29. //遍历
  30. for (Constructor<?> constructor : constructors) {
  31. System.out.println(constructor);
  32. }
  33. }
  34. }

4.反射方法

4.1.利用反射获取所有成员方法

  1. Method[] getMethods() -> 获取所有的public的成员方法->包括继承的
  1. public class Test01 {
  2. public static void main(String[] args) throws Exception {
  3. Class<?> aClass = Class.forName("com.atguigu.e_reflect.Person");
  4. Method[] methods = aClass.getMethods();
  5. for (Method method : methods) {
  6. System.out.println(method);
  7. }
  8. }
  9. }

4.2.反射之获取方法(有参,无参)

  1. Class类中的方法:
  2. Method getMethod(String name, Class<?>... parameterTypes)
  3. name:传递的是要反射的方法名
  4. parameterTypes:可变参数,传递的是被反射方法参数的class对象
  5. 如果反射的方法没有参数,那么parameterTypes就不写了
  6. 如果反射的方法有参数的,就传递参数类型的class对象
  7. Method类中的方法:
  8. Object invoke(Object obj, Object... args)
  9. 参数:
  10. obj:传递的是根据反射出来的构造方法创建出来的对象
  11. args:可变参数->如果执行的是空参的方法,args不写
  12. 如果执行的是有参的方法,args处传递被反射方法的实际参数
  13. 返回值:Object->接收被反射方法的返回值
  14. 如果执行的方法没有返回值,不用Object接收
  15. 如果指定的方法有返回值,Object用于接收被执行方法的返回值
  1. public class Test02 {
  2. public static void main(String[] args) throws Exception {
  3. Class<?> aClass = Class.forName("com.atguigu.e_reflect.Person");
  4. //获取对象
  5. /*
  6. Person p = new Person()
  7. */
  8. Object o = aClass.newInstance();
  9. /*
  10. 获取setName
  11. */
  12. Method setName = aClass.getMethod("setName", String.class);
  13. /*
  14. 执行setName
  15. 由于setName没有返回值,所以调用invoke之后,不同invoke的返回值Object接收
  16. 相当于 p.setName("柳岩")
  17. */
  18. setName.invoke(o,"柳岩");
  19. System.out.println("==================================");
  20. /*
  21. 获取指定的成员方法->反射getName()
  22. 由于getName有返回值,所以调用invoke之后,用invoke的返回值Object接收
  23. */
  24. Method getName = aClass.getMethod("getName");
  25. //执行方法
  26. /*
  27. 相当于String name = p.getName()
  28. */
  29. Object o1 = getName.invoke(o);
  30. System.out.println(o1);
  31. }
  32. }

5.反射练习(编写一个小框架)

  1. 利用反射,解析配置文件中的信息:properties文件
  2. 类的全限定名: className=包名.类名
  3. 方法名: methodName=方法名
  4. 需求:利用反射解析properties文件,获取配置信息
  5. 根据配置信息,执行配置文件中的方法
  6. 步骤:
  7. 1.创建配置文件->properties文件
  8. properties文件放在什么位置:
  9. a.我们将来开发完毕,给用户的是out路径下的class文件,如果将配置文件放在当前模块下,那么out目录 下是没有配置文件的,那么用户一执行,由于需要这个配置文件中的信息,那么用户执行起来会有问题
  10. b.如何解决a的问题:我们只需要将配置文件放在src下,程序一编译,配置文件自然会在out路径下出现
  11. c.如果将配置文件放在src下面那么我们读取配置文件代码怎么写?
  12. 之前写法:
  13. 直接 new FileInputStream("模块名\\src\\配置文件名")不行
  14. 因为这样写,我们带了src路径,但是我们给用户的out目录下是没有src路径,所以这样写死不行
  15. 用户一执行,由于用户这边用的没有srcout路径,那么就会报错
  16. d.解决:使用类加载器->扩展
  17. ClassLoader类中的方法:
  18. InputStream getResourceAsStream(String name) ->name直接写文件名,返回字节流
  19. 此方法可以自动扫描src下的配置文件
  20. 当前类.class->获取class对象
  21. class对象.getClassLoader()->获取了类加载器
  22. 类加载器.getResourceAsStream(String name)->扫描到src下的配置文件了
  23. 链式调用:当前类名.class.getClassLoader().getResourceAsStream(String name)
  24. 2.利用IO读取配置文件,将配置文件信息放在Properties集合当中
  25. 3.获取Properties中对应的值
  26. 获取类的全限定名
  27. 方法名
  28. 4.利用反射获取类的Class对象->根据解析出来的类的全限定名获取Class对象
  29. 利用反射执行配置文件中的方法
  1. src下创建一个properties文件
  2. className=cn.atguigu.Person
  3. methodName=eat
  1. public class Test {
  2. public static void main(String[] args)throws Exception {
  3. //1.解析配置文件
  4. Properties properties = new Properties();
  5. //2.读取配置文件
  6. InputStream in
  7. = Test.class.getClassLoader().getResourceAsStream("pojo.properties");
  8. //3.调用load方法,将流中的文件信息加载到Properties集合中
  9. properties.load(in);
  10. System.out.println(properties);
  11. String className = properties.getProperty("className");
  12. String methodName = properties.getProperty("methodName");
  13. //4.根据获取出来的className(类的全限定名)获取对应的class对象
  14. Class<?> aClass = Class.forName(className);
  15. //5.利用反射创建对象
  16. Object o = aClass.newInstance();
  17. //6.根据获取出来的methodName获取对应的方法
  18. Method method = aClass.getMethod(methodName);
  19. //7.执行方法
  20. method.invoke(o);
  21. }
  22. }

第四章.注解

1.注解的介绍

  1. 1.jdk1.5版本的新特性->一个引用数据类型
  2. 和类,接口,枚举是同一个层次的
  3. 2.作用:
  4. 说明:对代码进行说明,生成doc文档(API文档)(不会用)
  5. 检查:检查代码是否符合条件 @Override(会用) @FunctionalInterface
  6. 分析:对代码进行分析,起到了代替配置文件的作用(会用)
  7. 3.JDK中的注解:
  8. @Override -> 检测此方法是否为重写方法
  9. jdk1.5版本,支持父类的方法重写
  10. jdk1.6版本,支持接口的方法重写
  11. @Deprecated -> 方法已经过时,不推荐使用
  12. 调用方法的时候,方法上会有横线,但是能用
  13. @SuppressWarnings->消除警告 @SuppressWarnings("all")

2.注解的定义以及属性的定义格式

  1. 1.格式:
  2. public @interface 注解名{
  3. }
  4. 2.定义属性:加大注解的作用
  5. 数据类型 属性名() -> 在使用注解的时候,必须要给属性赋值
  6. 数据类型 属性名() default 值-> 属性是带默认值的,在使用注解的时候,无需给此属性赋值也不用写,如果需要改变默认值,重新赋值
  7. 3. 注解中都可以定义什么类型的属性呢?
  8. a.8种基本数据类型(byte short int long char float double boolean)
  9. b.String类型 Class类型 枚举类型 注解类型
  10. c.以及以上类型的一维数组
  1. public @interface Book {
  2. //书名
  3. String name();
  4. //价格
  5. double price();
  6. //作者
  7. String[] author();
  8. //数量
  9. //Integer count();不能是包装类型
  10. int count() default 10;
  11. }

3.注解的使用(重点)

  1. 1.所谓的注解使用:为注解中的属性赋值
  2. 2.使用的位置:类上 方法上 成员变量上 局部变量上 参数上
  3. 3.如何使用:
  4. @注解名(属性名 = 属性值,属性名 = 属性值...)
  5. 4.如果属性是数组:
  6. @注解名(属性名 = {元素1,元素2...})
  1. @Book(name = "三国",price = 23,author = {"罗贯中","涛哥"})
  2. public class BookSelf {
  3. @Book(name = "三国",price = 23,author = {"罗贯中","涛哥"})
  4. public void method(){
  5. }
  6. }
  1. 注解注意事项:
  2. 1.空注解可以直接使用->空注解就是注解中没有任何的属性
  3. 2.不同的位置可以使用一样的注解,但是同样的位置不能使用一样的注解
  4. 3.使用注解时,如果此注解中有属性,注解中的属性一定要赋值,如果有多个属性,用,隔开
  5. 如果注解中的属性有数组,那么如果数组只有一个元素值,那么{}不用写,反之用写
  6. 4.如果注解中的属性值有默认值,那么我们不必要写,也不用重新赋值,反之必须写上
  7. 5.如果注解中只有一个属性,并且属性名叫value,那么使用注解的时候,属性名不用写,直接写值
  8. (包括单个类型,还包括数组)

4.注解解析的方法->AnnotatedElement接口

  1. 解析注解:获取注解中的属性值
  2. AnnotatedElement接口的实现类:Class Method Constructor
  3. 接口:AnnotatedElement->专门用于注解解析的
  4. boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
  5. 判断当前Class对象上是否有指定的注解,如果返回false,证明没注解,否则证明有注解
  6. 比如:判断BookShelf类上是否有Book注解
  7. Class class = BookShelf.class;
  8. class.isAnnotationPresent(Book.class)->判断BookShelf类上有没有Book注解
  9. ==========================================================================
  10. <T extends Annotation> T getAnnotation(Class<T> annotationClass) ->获取当前Class对象上的指定注解
  11. 比如:
  12. Class class = BookShelf.class;
  13. class.isAnnotationPresent(Book.class)->判断BookShelf类上有没有Book注解
  14. 如果有,返回true,就要获取这个注解了:
  15. class.getAnnotation(Book.class)->获取BookShelf上的Book注解
  16. =============================================================================
  17. 注解的解析思想:
  18. 解析类上的注解:
  19. 1.反射带有注解的类,获取类的Class对象
  20. 2.判断这类上有没有注解:isAnnotationPresent
  21. 3.获取这个注解
  22. 4.获取注解中的属性值:getAnnotation
  23. 解析方法上的注解
  24. 1.反射带有注解的类->Class对象
  25. 2.获取方法:Method
  26. 3.判断方法上有没有注解:method.isAnnotationPresent
  27. 4.获取这个方法上的注解:method.getAnnotation
  28. 5.获取注解中的属性值
  1. public @interface Book {
  2. //书名
  3. String name();
  4. //价格
  5. double price();
  6. //作者
  7. String[] author();
  8. }
  9. @Book(name = "三国",price = 23,author = {"罗贯中","涛哥"})
  10. public class BookSelf {
  11. }
  1. public class Test01 {
  2. public static void main(String[] args) {
  3. //1.获取BookSelf的Class对象
  4. Class<BookSelf> bookSelfClass = BookSelf.class;
  5. //2.判断BookSelf上有没有Book注解
  6. boolean b = bookSelfClass.isAnnotationPresent(Book.class);
  7. //3.判断,如果b是true,证明BookSelf上有Book注解,就获取这个注解,在获取注解上的属性值
  8. if (b){
  9. //4.获取Book注解
  10. Book book = bookSelfClass.getAnnotation(Book.class);
  11. //5.获取Book注解中的属性值
  12. System.out.println(book.name());
  13. System.out.println(book.price());
  14. System.out.println(Arrays.toString(book.author()));
  15. }
  16. }
  17. }
  18. 以上代码,解析失败

涛哥猜想:我们的解析代码是在内存中运行的,如果Book注解在内存中出现了,我一定能获取到Book注解

  1. 但是,我们这个代码就是没有获取到Book注解,我们猜想,Book注解就没有在内存中

第五章.元注解

  1. 1.概述:管理注解的注解,可以理解为是自定义注解的总管
  2. 2.问题:元注解从哪些方面去管理了自定义的注解?
  3. 方面1:可以控制自定义注解的编写位置
  4. 可以控制,自定义注解是否能在类上使用
  5. 可以控制,自定义注解是否能在方法上使用
  6. 可以控制,自定义注解是否能在成员变量上使用
  7. 可以控制,自定义注解是否能在参数位置上使用
  8. 可以控制,自定义注解是否能在局部变量上使用
  9. 方面2:可以控制自定义注解的生命周期
  10. 可以控制自定义注解是否只存在在源码中
  11. 可以控制自定义注解是否存在在Class文件中
  12. 可以控制自定义注解是否只存在在内存中
  13. 3.使用元注解
  14. @Target:控制自定义注解的编写位置
  15. a.属性:ElementType[] value();
  16. b.ElementType:枚举,成员都是静态的-> 使用可以类名直接调用
  17. TYPE:允许自定义注解在类上使用
  18. FIELD:允许自定义注解在成员变量上使用
  19. METHOD:允许自定义注解在方法上使用
  20. PARAMETER:允许自定义注解在参数上使用
  21. CONSTRUCTOR:允许自定义注解在构造上使用
  22. LOCAL_VARIABLE:允许自定义注解在局部变量上使用
  23. @Retention:控制自定义注解的生命周期
  24. a.属性:RetentionPolicy value();
  25. b.RetentionPolicy:枚举,成员都是静态的-> 使用可以类名直接调用
  26. SOURCE:允许自定义注解只存在在源码中->默认的
  27. CLASS:允许自定义注解存在在Class文件中
  28. RUNTIME:允许自定义注解存在在内存中
  1. @Target({ElementType.METHOD,ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface Book {
  4. //书名
  5. String name();
  6. //价格
  7. int price();
  8. //作者
  9. String[] author();
  10. //数量->不能写包装类
  11. //Integer count();
  12. int count() default 10;
  13. }

1.注解再次解析

  1. public class Test01 {
  2. public static void main(String[] args) {
  3. //1.获取BookSelf的Class对象
  4. Class<BookSelf> bookSelfClass = BookSelf.class;
  5. //2.判断BookSelf上有没有Book注解
  6. boolean b = bookSelfClass.isAnnotationPresent(Book.class);
  7. System.out.println(b);
  8. //3.判断,如果b是true,证明BookSelf上有Book注解,就获取这个注解,在获取注解上的属性值
  9. if (b){
  10. //4.获取Book注解
  11. Book book = bookSelfClass.getAnnotation(Book.class);
  12. //5.获取Book注解中的属性值
  13. System.out.println(book.name());
  14. System.out.println(book.price());
  15. System.out.println(Arrays.toString(book.author()));
  16. }
  17. }
  18. }

第一次解析由于我们自己的注解Book默认是SOURCE,只能出现在源码中所以我们没有解析出来

第二次我们将SOURCE变成了RUNTIME,证明注解Book能出现在内存中了,所以我们解析出来了