第二十章 反射

1. 反射机制概述

1.1 反射机制有什么用?

通过java语言中的反射机制可以操作字节码文件,优点类似于黑客;(可以读和修改字节码文件。)通过反射机制可以操作代码片段。(class文件。)

1.2 反射机制的相关类在哪个包下?

java.lang.reflect.*;

1.3 反射机制相关的重要的类有哪些?

java.lang.Class:代表整个字节码,代表一个类型,代表整个类
java.lang.reflect.Method:代表字节码中的方法字节码,代表类中的方法
java.lang.reflect.Constructor:代表字节码中的构造方法字节码,代表类中的构造方法
java.lang.reflect.Field:代表字节码中的属性字节码,代表类中的成员变量(静态变量+实例变量)

2. 获取class的三种方式

  1. import java.util.Date;
  2. /*
  3. 要操作一个类的字节码,需要首先获取到这个类的字节码,怎么获取java.lang.Class实例?
  4. 三种方式
  5. 第一种:Class c = Class.forName("完整类名带包名");
  6. 第二种:Class c = 对象.getClass();
  7. 第三种:Class c = 任何类型.class;
  8. */
  9. public class ReflectTest01 {
  10. public static void main(String[] args) {
  11. /*
  12. Class.forName()
  13. 1、静态方法
  14. 2、方法的参数是一个字符串。
  15. 3、字符串需要的是一个完整类名。
  16. 4、完整类名必须带有包名。java.lang包也不能省略。
  17. 该方法适用于只想让一个类的静态代码块执行,对该方法的返回值不感兴趣,主要是为了使用‘类加载’这个动作
  18. */
  19. Class c1 = null;
  20. Class c2 = null;
  21. try {
  22. c1 = Class.forName("java.lang.String"); // c1代表String.class文件,或者说c1代表String类型。
  23. c2 = Class.forName("java.util.Date"); // c2代表Date类型
  24. Class c3 = Class.forName("java.lang.Integer"); // c3代表Integer类型
  25. Class c4 = Class.forName("java.lang.System"); // c4代表System类型
  26. } catch (ClassNotFoundException e) {
  27. e.printStackTrace();
  28. }
  29. // java中任何一个对象都有一个方法:getClass()
  30. String s = "abc";
  31. Class x = s.getClass(); // x代表String.class字节码文件,x代表String类型。
  32. System.out.println(c1 == x); // true(==判断的是对象的内存地址。)
  33. Date time = new Date();
  34. Class y = time.getClass();
  35. System.out.println(c2 == y); // true (c2和y两个变量中保存的内存地址都是一样的,都指向方法区中的字节码文件。)
  36. // 第三种方式,java语言中任何一种类型,包括基本数据类型,它都有.class属性。
  37. Class z = String.class; // z代表String类型
  38. Class k = Date.class; // k代表Date类型
  39. Class f = int.class; // f代表int类型
  40. Class e = double.class; // e代表double类型
  41. System.out.println(x == z); // true
  42. }
  43. }

复制类的路径(含包名,类名右键“Copy reference”)

jdk1.8 可以找到jdk的class文件:在jre下面的lib里有一个rt.jar的文件 jdk\jre\lib

3. 通过反射实例化对象

  1. import com.bjpowernode.java.bean.User;
  2. /*
  3. 获取到Class,能干什么?
  4. 通过Class的newInstance()方法来实例化对象。
  5. 注意:newInstance()方法内部实际上调用了无参数构造方法,必须保证无参构造存在才可以。
  6. */
  7. public class ReflectTest02 {
  8. public static void main(String[] args) {
  9. // 这是不使用反射机制,创建对象
  10. User user = new User();
  11. System.out.println(user);
  12. // 下面这段代码是以反射机制的方式创建对象。
  13. try {
  14. // 通过反射机制,获取Class,通过Class来实例化对象
  15. Class c = Class.forName("com.bjpowernode.java.bean.User"); // c代表User类型。
  16. // newInstance() 这个方法会调用User这个类的无参数构造方法,完成对象的创建。
  17. // 重点是:newInstance()调用的是无参构造,必须保证无参构造是存在的!
  18. Object obj = c.newInstance();
  19. System.out.println(obj); // com.bjpowernode.java.bean.User@10f87f48
  20. } catch (ClassNotFoundException e) {
  21. e.printStackTrace();
  22. } catch (IllegalAccessException e) {
  23. e.printStackTrace();
  24. } catch (InstantiationException e) {
  25. e.printStackTrace();
  26. }
  27. }
  28. }

4. 通过配置文件实例化对象

  1. import java.io.FileReader;
  2. import java.util.Properties;
  3. /*
  4. 验证反射机制的灵活性。
  5. java代码写一遍,再不改变java源代码的基础之上,可以做到不同对象的实例化。
  6. 非常之灵活。(符合OCP开闭原则:对扩展开放,对修改关闭。)
  7. 后期你们要学习的是高级框架,而工作过程中,也都是使用高级框架,
  8. 包括: ssh ssm
  9. Spring SpringMVC MyBatis
  10. Spring Struts Hibernate
  11. ...
  12. 这些高级框架底层实现原理:都采用了反射机制。所以反射机制还是重要的。
  13. 学会了反射机制有利于你理解剖析框架底层的源代码。
  14. */
  15. public class ReflectTest03 {
  16. public static void main(String[] args) throws Exception{
  17. // 这种方式代码就写死了。只能创建一个User类型的对象
  18. //User user = new User();
  19. // 以下代码是灵活的,代码不需要改动,可以修改配置文件,配置文件修改之后,
  20. //可以创建出不同的实例对象。
  21. // 通过IO流读取classinfo.properties文件
  22. FileReader reader = new FileReader("chapter25/classinfo2.properties");
  23. // 创建属性类对象Map
  24. Properties pro = new Properties(); // key value都是String
  25. // 加载
  26. pro.load(reader);
  27. // 关闭流
  28. reader.close();
  29. // 通过key获取value
  30. String className = pro.getProperty("className");
  31. //System.out.println(className);
  32. // 通过反射机制实例化对象
  33. Class c = Class.forName(className);
  34. Object obj = c.newInstance();
  35. System.out.println(obj);
  36. }
  37. }

4.1 只让静态代码块执行

  1. /*
  2. 研究一下:Class.forName()发生了什么?
  3. 记住,重点:
  4. 如果你只是希望一个类的静态代码块执行,其它代码一律不执行,
  5. 你可以使用:
  6. Class.forName("完整类名");
  7. 这个方法的执行会导致类加载,类加载时,静态代码块执行。
  8. 提示:
  9. 后面JDBC技术的时候我们还需要。
  10. */
  11. public class ReflectTest04 {
  12. public static void main(String[] args) {
  13. try {
  14. // Class.forName()这个方法的执行会导致:类加载。
  15. Class.forName("com.bjpowernode.java.reflect.MyClass");
  16. } catch (ClassNotFoundException e) {
  17. e.printStackTrace();
  18. }
  19. }
  20. }

5. 获取一个文件的绝对路径

  1. import java.io.FileReader;
  2. /*
  3. 研究一下文件路径的问题。
  4. 怎么获取一个文件的绝对路径。以下讲解的这种方式是通用的。
  5. 但前提是:文件需要在类路径下。才能用这种方式。
  6. */
  7. public class AboutPath {
  8. public static void main(String[] args) throws Exception{
  9. // 这种方式的路径缺点是:移植性差,在IDEA中默认的当前路径是project的根。
  10. // 这个代码假设离开了IDEA,换到了其它位置,可能当前路径就不是project的根了,这时这个路径就无效了。
  11. //FileReader reader = new FileReader("chapter25/classinfo2.properties");
  12. // 接下来说一种比较通用的一种路径。即使代码换位置了,这样编写仍然是通用的。
  13. // 注意:使用以下通用方式的前提是:这个文件必须在类路径下。
  14. // 什么类路径下?凡是在src下的都是类路径下。【记住它】
  15. // src是类的根路径。
  16. /*
  17. 解释:
  18. Thread.currentThread() 当前线程对象
  19. getContextClassLoader() 是线程对象的方法,可以获取到当前线程的类加载器对象。
  20. getResource() 【获取资源】这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源。
  21. */
  22. String path = Thread.currentThread().getContextClassLoader()
  23. .getResource("classinfo2.properties").getPath(); // 这种方式获取文件绝对路径是通用的。
  24. // 采用以上的代码可以拿到一个文件的绝对路径。
  25. // /C:/Users/Administrator/IdeaProjects/javase/out/production/chapter25/classinfo2.properties
  26. System.out.println(path);
  27. }
  28. }

5.1 以流的形式直接返回

  1. import java.io.FileReader;
  2. import java.io.InputStream;
  3. import java.util.Properties;
  4. public class IoPropertiesTest {
  5. public static void main(String[] args) throws Exception{
  6. // 获取一个文件的绝对路径了!!!!!
  7. /*
  8. String path = Thread.currentThread().getContextClassLoader()
  9. .getResource("classinfo2.properties").getPath();
  10. FileReader reader = new FileReader(path);
  11. */
  12. // 直接以流的形式返回。
  13. InputStream reader = Thread.currentThread().getContextClassLoader()
  14. .getResourceAsStream("classinfo2.properties");
  15. Properties pro = new Properties();
  16. pro.load(reader);
  17. reader.close();
  18. // 通过key获取value
  19. String className = pro.getProperty("className");
  20. System.out.println(className);
  21. }
  22. }

6. 资源绑定器

  1. import java.util.ResourceBundle;
  2. /*
  3. java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容。
  4. 使用以下这种方式的时候,属性配置文件xxx.properties必须放到类路径下。
  5. */
  6. public class ResourceBundleTest {
  7. public static void main(String[] args) {
  8. // 资源绑定器,只能绑定xxx.properties文件。并且这个文件必须在类路径下。文件扩展名也必须是properties
  9. // 并且在写路径的时候,路径后面的扩展名不能写。
  10. //ResourceBundle bundle = ResourceBundle.getBundle("classinfo2");
  11. ResourceBundle bundle = ResourceBundle.getBundle("com/bjpowernode/java/bean/db");
  12. String className = bundle.getString("className");
  13. System.out.println(className);
  14. }
  15. }

7. JDK中自带的类加载器

7.1 什么是类加载器?

专门负责加载类的命令/工具( ClassLoader)

7.2 JDK中自带了3个类加载器

启动类加载器:rt.jar
扩展类加载器:ext/*.jar
应用类加载器:classpath

7.3 类加载分析:

String s = “abc”;
代码在开始执行之前,会将所需要类全部加载到JVM当中,通过类加载器加载,看到以上代码类加载器会找String.class文件,找到就加载,没找到则报异常

那么是怎么进行加载的呢? 首先通过“启动类加载器”加载,启动类加载器专门加载:C:\Program Files\Java\jdk1.8.0_101\jre\lib\rt.jar;(rt.jar中都是JDK最核心的类库)如果通过“启动类加载器”加载不到的时候,会通过”扩展类加载器”加载,扩展类加载器专门加载:C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext*.jar;如果“扩展类加载器”没有加载到,那么会通过“应用类加载器”加载,应用类加载器专门加载:classpath中的类

7.4 双亲委派机制

java中为了保证类加载的安全,使用了双亲委派机制。优先从启动类加载器中加载,这个称为“父”,“父”无法加载到,再从扩展类加载器中加载,这个称为“母”,双亲委派;如果都加载不到,才会考虑从应用类加载器中加载,直到加载到为止

8. 获取Field

  1. import java.lang.reflect.Field;
  2. import java.lang.reflect.Modifier;
  3. /*
  4. 反射Student类当中所有的Field(了解一下)
  5. */
  6. public class ReflectTest05 {
  7. public static void main(String[] args) throws Exception{
  8. // 获取整个类
  9. Class studentClass = Class.forName("com.bjpowernode.java.bean.Student");
  10. //com.bjpowernode.java.bean.Student
  11. String className = studentClass.getName();
  12. System.out.println("完整类名:" + className);
  13. String simpleName = studentClass.getSimpleName();
  14. System.out.println("简类名:" + simpleName);
  15. // 获取类中所有的public修饰的Field
  16. Field[] fields = studentClass.getFields();
  17. System.out.println(fields.length); // 测试数组中只有1个元素
  18. // 取出这个Field
  19. Field f = fields[0];
  20. // 取出这个Field它的名字
  21. String fieldName = f.getName();
  22. System.out.println(fieldName);
  23. // 获取所有的Field
  24. Field[] fs = studentClass.getDeclaredFields();
  25. System.out.println(fs.length); // 4
  26. System.out.println("==================================");
  27. // 遍历
  28. for(Field field : fs){
  29. // 获取属性的修饰符列表
  30. int i = field.getModifiers(); // 返回的修饰符是一个数字,每个数字是修饰符的代号!!!
  31. System.out.println(i);
  32. // 可以将这个“代号”数字转换成“字符串”吗?
  33. String modifierString = Modifier.toString(i);
  34. System.out.println(modifierString);
  35. // 获取属性的类型
  36. Class fieldType = field.getType();
  37. //String fName = fieldType.getName();
  38. String fName = fieldType.getSimpleName();
  39. System.out.println(fName);
  40. // 获取属性的名字
  41. System.out.println(field.getName());
  42. }
  43. }
  44. }

8.1 反编译filed

  1. //通过反射机制,反编译一个类的属性Field(了解一下)
  2. import java.lang.reflect.Field;
  3. import java.lang.reflect.Modifier;
  4. public class ReflectTest06 {
  5. public static void main(String[] args) throws Exception{
  6. // 创建这个是为了拼接字符串。
  7. StringBuilder s = new StringBuilder();
  8. //Class studentClass = Class.forName("com.bjpowernode.java.bean.Student");
  9. Class studentClass = Class.forName("java.lang.Thread");
  10. s.append(Modifier.toString(studentClass.getModifiers()) + " class " + studentClass.getSimpleName() + " {\n");
  11. Field[] fields = studentClass.getDeclaredFields();
  12. for(Field field : fields){
  13. s.append("\t");
  14. s.append(Modifier.toString(field.getModifiers()));
  15. s.append(" ");
  16. s.append(field.getType().getSimpleName());
  17. s.append(" ");
  18. s.append(field.getName());
  19. s.append(";\n");
  20. }
  21. s.append("}");
  22. System.out.println(s);
  23. }
  24. }

8.2 通过反射机制访问一个java对象的属性

  1. import com.bjpowernode.java.bean.Student;
  2. import java.lang.reflect.Field;
  3. /*
  4. 必须掌握:
  5. 怎么通过反射机制访问一个java对象的属性?
  6. 给属性赋值set
  7. 获取属性的值get
  8. */
  9. public class ReflectTest07 {
  10. public static void main(String[] args) throws Exception{
  11. // 我们不使用反射机制,怎么去访问一个对象的属性呢?
  12. Student s = new Student();
  13. // 给属性赋值
  14. s.no = 1111; //三要素:给s对象的no属性赋值1111
  15. //要素1:对象s
  16. //要素2:no属性
  17. //要素3:1111
  18. // 读属性值
  19. // 两个要素:获取s对象的no属性的值。
  20. System.out.println(s.no);
  21. // 使用反射机制,怎么去访问一个对象的属性。(set get)
  22. Class studentClass = Class.forName("com.bjpowernode.java.bean.Student");
  23. Object obj = studentClass.newInstance(); // obj就是Student对象。(底层调用无参数构造方法)
  24. // 获取no属性(根据属性的名称来获取Field)
  25. Field noFiled = studentClass.getDeclaredField("no");
  26. // 给obj对象(Student对象)的no属性赋值
  27. /*
  28. 虽然使用了反射机制,但是三要素还是缺一不可:
  29. 要素1:obj对象
  30. 要素2:no属性
  31. 要素3:2222值
  32. 注意:反射机制让代码复杂了,但是为了一个“灵活”,这也是值得的。
  33. */
  34. noFiled.set(obj, 22222); // 给obj对象的no属性赋值2222
  35. // 读取属性的值
  36. // 两个要素:获取obj对象的no属性的值。
  37. System.out.println(noFiled.get(obj));
  38. // 可以访问私有的属性吗?
  39. Field nameField = studentClass.getDeclaredField("name");
  40. // 打破封装(反射机制的缺点:打破封装,可能会给不法分子留下机会!!!)
  41. // 这样设置完之后,在外部也是可以访问private的。
  42. nameField.setAccessible(true);
  43. // 给name属性赋值
  44. nameField.set(obj, "jackson");
  45. // 获取name属性的值
  46. System.out.println(nameField.get(obj));
  47. }
  48. }

9. 反射method

  1. /*
  2. 作为了解内容(不需要掌握):
  3. 反射Method
  4. */
  5. public class ReflectTest08 {
  6. public static void main(String[] args) throws Exception{
  7. // 获取类了
  8. Class userServiceClass = Class.forName("com.bjpowernode.java.service.UserService");
  9. // 获取所有的Method(包括私有的!)
  10. Method[] methods = userServiceClass.getDeclaredMethods();
  11. //System.out.println(methods.length); // 2
  12. // 遍历Method
  13. for(Method method : methods){
  14. // 获取修饰符列表
  15. System.out.println(Modifier.toString(method.getModifiers()));
  16. // 获取方法的返回值类型
  17. System.out.println(method.getReturnType().getSimpleName());
  18. // 获取方法名
  19. System.out.println(method.getName());
  20. // 方法的修饰符列表(一个方法的参数可能会有多个。)
  21. Class[] parameterTypes = method.getParameterTypes();
  22. for(Class parameterType : parameterTypes){
  23. System.out.println(parameterType.getSimpleName());
  24. }
  25. }
  26. }
  27. }

9.1 反编译一个类的方法

  1. import java.lang.reflect.Method;
  2. import java.lang.reflect.Modifier;
  3. /*
  4. 了解一下,不需要掌握(反编译一个类的方法。)
  5. */
  6. public class ReflectTest09 {
  7. public static void main(String[] args) throws Exception{
  8. StringBuilder s = new StringBuilder();
  9. //Class userServiceClass = Class.forName("com.bjpowernode.java.service.UserService");
  10. Class userServiceClass = Class.forName("java.lang.String");
  11. s.append(Modifier.toString(userServiceClass.getModifiers()) + " class "+userServiceClass.getSimpleName()+" {\n");
  12. Method[] methods = userServiceClass.getDeclaredMethods();
  13. for(Method method : methods){
  14. //public boolean login(String name,String password){}
  15. s.append("\t");
  16. s.append(Modifier.toString(method.getModifiers()));
  17. s.append(" ");
  18. s.append(method.getReturnType().getSimpleName());
  19. s.append(" ");
  20. s.append(method.getName());
  21. s.append("(");
  22. // 参数列表
  23. Class[] parameterTypes = method.getParameterTypes();
  24. for(Class parameterType : parameterTypes){
  25. s.append(parameterType.getSimpleName());
  26. s.append(",");
  27. }
  28. // 删除指定下标位置上的字符
  29. s.deleteCharAt(s.length() - 1);
  30. s.append("){}\n");
  31. }
  32. s.append("}");
  33. System.out.println(s);
  34. }
  35. }

9.2 通过反射调用一个对象的方法

  1. import com.bjpowernode.java.service.UserService;
  2. import java.lang.reflect.Method;
  3. /*
  4. 重点:必须掌握,通过反射机制怎么调用一个对象的方法?
  5. 五颗星*****
  6. 反射机制,让代码很具有通用性,可变化的内容都是写到配置文件当中,
  7. 将来修改配置文件之后,创建的对象不一样了,调用的方法也不同了,
  8. 但是java代码不需要做任何改动。这就是反射机制的魅力。
  9. */
  10. public class ReflectTest10 {
  11. public static void main(String[] args) throws Exception{
  12. // 不使用反射机制,怎么调用方法
  13. // 创建对象
  14. UserService userService = new UserService();
  15. // 调用方法
  16. /*
  17. 要素分析:
  18. 要素1:对象userService
  19. 要素2:login方法名
  20. 要素3:实参列表
  21. 要素4:返回值
  22. */
  23. boolean loginSuccess = userService.login("admin","123");
  24. //System.out.println(loginSuccess);
  25. System.out.println(loginSuccess ? "登录成功" : "登录失败");
  26. // 使用反射机制来调用一个对象的方法该怎么做?
  27. Class userServiceClass = Class.forName("com.bjpowernode.java.service.UserService");
  28. // 创建对象
  29. Object obj = userServiceClass.newInstance();
  30. // 获取Method
  31. Method loginMethod = userServiceClass.getDeclaredMethod("login", String.class, String.class);
  32. //Method loginMethod = userServiceClass.getDeclaredMethod("login", int.class);
  33. // 调用方法
  34. // 调用方法有几个要素? 也需要4要素。
  35. // 反射机制中最最最最最重要的一个方法,必须记住。
  36. /*
  37. 四要素:
  38. loginMethod方法
  39. obj对象
  40. "admin","123" 实参
  41. retValue 返回值
  42. */
  43. Object retValue = loginMethod.invoke(obj, "admin","123123");
  44. System.out.println(retValue);
  45. }
  46. }

10. 反射constructor

  1. import java.lang.reflect.Constructor;
  2. import java.lang.reflect.Modifier;
  3. /*
  4. 反编译一个类的Constructor构造方法。
  5. */
  6. public class ReflectTest11 {
  7. public static void main(String[] args) throws Exception{
  8. StringBuilder s = new StringBuilder();
  9. Class vipClass = Class.forName("java.lang.String");
  10. s.append(Modifier.toString(vipClass.getModifiers()));
  11. s.append(" class ");
  12. s.append(vipClass.getSimpleName());
  13. s.append("{\n");
  14. // 拼接构造方法
  15. Constructor[] constructors = vipClass.getDeclaredConstructors();
  16. for(Constructor constructor : constructors){
  17. //public Vip(int no, String name, String birth, boolean sex) {
  18. s.append("\t");
  19. s.append(Modifier.toString(constructor.getModifiers()));
  20. s.append(" ");
  21. s.append(vipClass.getSimpleName());
  22. s.append("(");
  23. // 拼接参数
  24. Class[] parameterTypes = constructor.getParameterTypes();
  25. for(Class parameterType : parameterTypes){
  26. s.append(parameterType.getSimpleName());
  27. s.append(",");
  28. }
  29. // 删除最后下标位置上的字符
  30. if(parameterTypes.length > 0){
  31. s.deleteCharAt(s.length() - 1);
  32. }
  33. s.append("){}\n");
  34. }
  35. s.append("}");
  36. System.out.println(s);
  37. }
  38. }

10.1 通过反射调用构造方法实例化对象

  1. import com.bjpowernode.java.bean.Vip;
  2. import java.lang.reflect.Constructor;
  3. /*
  4. 比上一个例子(ReflectTest11)重要一些!!!
  5. 通过反射机制调用构造方法实例化java对象。(这个不是重点)
  6. */
  7. public class ReflectTest12 {
  8. public static void main(String[] args) throws Exception{
  9. // 不使用反射机制怎么创建对象
  10. Vip v1 = new Vip();
  11. Vip v2 = new Vip(110, "zhangsan", "2001-10-11", true);
  12. // 使用反射机制怎么创建对象呢?
  13. Class c = Class.forName("com.bjpowernode.java.bean.Vip");
  14. // 调用无参数构造方法
  15. Object obj = c.newInstance();
  16. System.out.println(obj);
  17. // 调用有参数的构造方法怎么办?
  18. // 第一步:先获取到这个有参数的构造方法
  19. Constructor con = c.getDeclaredConstructor(int.class, String.class, String.class,boolean.class);
  20. // 第二步:调用构造方法new对象
  21. Object newObj = con.newInstance(110, "jackson", "1990-10-11", true);
  22. System.out.println(newObj);
  23. // 获取无参数构造方法
  24. Constructor con2 = c.getDeclaredConstructor();
  25. Object newObj2 = con2.newInstance();
  26. System.out.println(newObj2);
  27. }
  28. }

11. 获取一个类的父类、接口

  1. /*
  2. 重点:给你一个类,怎么获取这个类的父类,已经实现了哪些接口?
  3. */
  4. public class ReflectTest13 {
  5. public static void main(String[] args) throws Exception{
  6. // String举例
  7. Class stringClass = Class.forName("java.lang.String");
  8. // 获取String的父类
  9. Class superClass = stringClass.getSuperclass();
  10. System.out.println(superClass.getName());
  11. // 获取String类实现的所有接口(一个类可以实现多个接口。)
  12. Class[] interfaces = stringClass.getInterfaces();
  13. for(Class in : interfaces){
  14. System.out.println(in.getName());
  15. }
  16. }
  17. }

第二十一章 注解

1. 注解基本概述

  • 注解,或者叫做注释类型,英文单词是:Annotation

  • 注解Annotation是一种引用数据类型。编译之后也是生成xxx.class文件。

  • 怎么自定义注解呢?语法格式?

[修饰符列表] @interface 注解类型名{}

  • 注解怎么使用,用在什么地方?

第一:注解使用时的语法格式是:
@注解类型名
第二:注解可以出现在类上、属性上、方法上、变量上、注解类型上……

  • JDK内置了哪些注解呢?

java.lang包下的注释类型:
掌握:
Deprecated
用 @Deprecated 注释的程序元素,不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择
掌握:
Override
表示一个方法声明打算重写超类中的另一个方法声明
不用掌握:
SuppressWarnings
指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告

1.1 关于JDK lang包下的Override注解

  1. /*
  2. 关于JDK lang包下的Override注解
  3. 源代码:
  4. public @interface Override {
  5. }
  6. 标识性注解,给编译器做参考的。
  7. 编译器看到方法上有这个注解的时候,编译器会自动检查该方法是否重写了父类的方法。
  8. 如果没有重写,报错。
  9. 这个注解只是在编译阶段起作用,和运行期无关!
  10. */
  11. // @Override这个注解只能注解方法。
  12. // @Override这个注解是给编译器参考的,和运行阶段没有关系。
  13. // 凡是java中的方法带有这个注解的,编译器都会进行编译检查,如果这个方法不是重写父类的方法,编译器报错。
  14. //@Override
  15. public class AnnotationTest02 {
  16. //@Override
  17. private int no;
  18. @Override
  19. public String toString() {
  20. return "toString";
  21. }
  22. }

1.2 Deprecated

  1. // 表示这个类已过时。
  2. @Deprecated
  3. public class AnnotationTest03 {
  4. @Deprecated
  5. private String s;
  6. public static void main(String[] args) {
  7. AnnotationTest03 at = new AnnotationTest03();
  8. at.doSome();
  9. }
  10. @Deprecated
  11. public void doSome(){
  12. System.out.println("do something!");
  13. }
  14. // Deprecated这个注解标注的元素已过时。
  15. // 这个注解主要是向其它程序员传达一个信息,告知已过时,有更好的解决方案存在。
  16. @Deprecated
  17. public static void doOther(){
  18. System.out.println("do other...");
  19. }
  20. }
  21. class T {
  22. public static void main(String[] args) {
  23. AnnotationTest03 at = new AnnotationTest03();
  24. at.doSome();
  25. AnnotationTest03.doOther();
  26. try {
  27. Class c = Class.forName("java.util.Date");
  28. Object obj = c.newInstance();
  29. } catch (Exception e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. }

1.3 注解的属性

  1. public @interface MyAnnotation {
  2. /**
  3. * 我们通常在注解当中可以定义属性,以下这个是MyAnnotation的name属性。
  4. * 看着像1个方法,但实际上我们称之为属性name。
  5. * @return
  6. */
  7. String name();
  8. /*
  9. 颜色属性
  10. */
  11. String color();
  12. /*
  13. 年龄属性
  14. */
  15. int age() default 25; //属性指定默认值
  16. }
  17. public class MyAnnotationTest {
  18. // 报错的原因:如果一个注解当中有属性,那么必须给属性赋值。(除非该属性使用default指定了默认值。)
  19. /*@MyAnnotation
  20. public void doSome(){
  21. }*/
  22. //@MyAnnotation(属性名=属性值,属性名=属性值,属性名=属性值)
  23. //指定name属性的值就好了。
  24. @MyAnnotation(name = "zhangsan", color = "红色")
  25. public void doSome(){
  26. }
  27. }

1.4 属性为value的省略问题

  1. public @interface MyAnnotation {
  2. /*
  3. 指定一个value属性。
  4. */
  5. String value();
  6. //String email();
  7. }
  8. /*
  9. 如果一个注解的属性的名字是value,并且只有一个属性的话(或者其他属性都有默认值),
  10. 在使用的时候,该属性名可以省略。
  11. */
  12. public class MyAnnotationTest {
  13. // 报错原因:没有指定属性的值。
  14. /*@MyAnnotation
  15. public void doSome(){
  16. }*/
  17. @MyAnnotation(value = "hehe")
  18. public void doSome(){
  19. }
  20. @MyAnnotation("haha")
  21. public void doOther(){
  22. }
  23. }

仅限属性名为value才存在省略问题

1.5 注解当中的属性可以是哪一种类型

  1. public @interface MyAnnotation {
  2. /*
  3. 注解当中的属性可以是哪一种类型?
  4. 属性的类型可以是:
  5. byte short int long float double boolean char String Class 枚举类型
  6. 以及以上每一种的数组形式。
  7. */
  8. int value1();
  9. String value2();
  10. int[] value3();
  11. String[] value4();
  12. Season value5();
  13. Season[] value6();
  14. Class parameterType();
  15. Class[] parameterTypes();
  16. }
  17. public enum Season {
  18. SPRING,SUMMER,AUTUMN,WINTER
  19. }

1.7 属性为数组

  1. public @interface OtherAnnotation {
  2. /*
  3. 年龄属性
  4. */
  5. int age();
  6. /*
  7. 邮箱地址属性,支持多个
  8. */
  9. String[] email();
  10. /**
  11. * 季节数组,Season是枚举类型
  12. * @return
  13. */
  14. Season[] seasonArray();
  15. }
  16. public class OtherAnnotationTest {
  17. // 数组是大括号
  18. @OtherAnnotation(age = 25, email = {"zhangsan@123.com", "zhangsan@sohu.com"},
  19. seasonArray = Season.WINTER)
  20. public void doSome(){
  21. }
  22. // 如果数组中只有1个元素:大括号可以省略。
  23. @OtherAnnotation(age = 25, email = "zhangsan@123.com", seasonArray = {Season.SPRING,
  24. Season.SUMMER})
  25. public void doOther(){
  26. }
  27. }

2. 元注解

  • 什么是元注解?

用来标注“注解类型”的“注解”,称为元注解。
常见的元注解有哪些?
Target
Retention

2.1 关于Target注解:

这是一个元注解,用来标注“注解类型”的“注解”,这个Target注解用来标注“被标注的注解”可以出现在哪些位置上
@Target(ElementType.METHOD):表示“被标注的注解”只能出现在方法上;@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER,TYPE})表示该注解可以出现在:构造方法上、字段上、局部变量上、方法上….. 类上…..

2.2 关于Retention注解

这是一个元注解,用来标注“注解类型”的“注解”,这个Retention注解用来标注“被标注的注解”最终保存在哪里
@Retention(RetentionPolicy.SOURCE):表示该注解只被保留在java源文件中
@Retention(RetentionPolicy.CLASS):表示该注解被保存在class文件中
@Retention(RetentionPolicy.RUNTIME):表示该注解被保存在class文件中,并且可以被反射机制所读取

2.2.1 Retention的源代码

  1. //RetentionPolicy的源代码:
  2. //元注解
  3. public @interface Retention {
  4. //属性
  5. RetentionPolicy value();
  6. }
  7. public enum RetentionPolicy {
  8. SOURCE,
  9. CLASS,
  10. RUNTIME
  11. }
  12. //@Retention(value=RetentionPolicy.RUNTIME)
  13. @Retention(RetentionPolicy.RUNTIME)
  14. public @interface MyAnnotation{}

3. 反射注解

  1. import java.lang.annotation.ElementType;
  2. import java.lang.annotation.Retention;
  3. import java.lang.annotation.RetentionPolicy;
  4. import java.lang.annotation.Target;
  5. @Retention(RetentionPolicy.RUNTIME)
  6. @Target(ElementType.METHOD)
  7. public @interface MyAnnotation {
  8. /*
  9. username属性
  10. */
  11. String username();
  12. /*
  13. password属性
  14. */
  15. String password();
  16. }
  17. import java.lang.reflect.Method;
  18. public class MyAnnotationTest {
  19. @MyAnnotation(username = "admin", password = "456456")
  20. public void doSome(){
  21. }
  22. public static void main(String[] args) throws Exception{
  23. // 获取MyAnnotationTest的doSome()方法上面的注解信息。
  24. Class c = Class.forName("com.bjpowernode.java.annotation6.MyAnnotationTest");
  25. // 获取doSome()方法
  26. Method doSomeMethod = c.getDeclaredMethod("doSome");
  27. // 判断该方法上是否存在这个注解
  28. if(doSomeMethod.isAnnotationPresent(MyAnnotation.class)) {
  29. MyAnnotation myAnnotation = doSomeMethod.getAnnotation(MyAnnotation.class);
  30. System.out.println(myAnnotation.username());
  31. System.out.println(myAnnotation.password());
  32. }
  33. }
  34. }
  1. import java.lang.annotation.ElementType;
  2. import java.lang.annotation.Retention;
  3. import java.lang.annotation.RetentionPolicy;
  4. import java.lang.annotation.Target;
  5. //只允许该注解可以标注类、方法
  6. @Target({ElementType.TYPE, ElementType.METHOD})
  7. // 希望这个注解可以被反射
  8. @Retention(RetentionPolicy.RUNTIME)
  9. public @interface MyAnnotation {
  10. /*
  11. value属性。
  12. */
  13. String value() default "北京大兴区";
  14. }
  15. @MyAnnotation("上海浦东区")
  16. public class MyAnnotationTest {
  17. //@MyAnnotation
  18. int i;
  19. //@MyAnnotation
  20. public MyAnnotationTest(){
  21. }
  22. @MyAnnotation
  23. public void doSome(){
  24. //@MyAnnotation
  25. int i;
  26. }
  27. }
  28. public class ReflectAnnotationTest {
  29. public static void main(String[] args) throws Exception{
  30. // 获取这个类
  31. Class c = Class.forName("com.bjpowernode.java.annotation5.MyAnnotationTest");
  32. // 判断类上面是否有@MyAnnotation
  33. //System.out.println(c.isAnnotationPresent(MyAnnotation.class)); // true
  34. if(c.isAnnotationPresent(MyAnnotation.class)){
  35. // 获取该注解对象
  36. MyAnnotation myAnnotation = (MyAnnotation)c.getAnnotation(MyAnnotation.class);
  37. //System.out.println("类上面的注解对象" + myAnnotation); // @com.bjpowernode.java.annotation5.MyAnnotation()
  38. // 获取注解对象的属性怎么办?和调接口没区别。
  39. String value = myAnnotation.value();
  40. System.out.println(value);
  41. }
  42. // 判断String类上面是否存在这个注解
  43. Class stringClass = Class.forName("java.lang.String");
  44. System.out.println(stringClass.isAnnotationPresent(MyAnnotation.class)); // false
  45. }
  46. }

4. 注解在开发中有什么用

  • java 注解,从名字上看是注释,解释。但功能却不仅仅是注释那么简单。注解(Annotation) 为我们在代码中添加信息提供了一种形式化的方法,是我们可以在稍后 某个时刻方便地使用这些数据(通过解析注解 来使用这些数据),常见的作用有以下几种:

1.生成文档。这是最常见的,也是java 最早提供的注解;常用的有@see @param @return 等

2.跟踪代码依赖性,实现替代配置文件功能;比较常见的是spring 2.5 开始的基于注解配置,作用就是减少配置,现在的框架基本都使用了这种配置来减少配置文件的数量

3.在编译时进行格式检查;如@Override放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出