java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java注解与反射

快速掌握Java中注解与反射

作者:楠寻寻

本文详细介绍了Java中注解和反射的概念及应用,注解是用于给代码添加元数据的标记,如@Override、@Deprecated等,反射机制则是在运行时获取和操作类的内部信息,提高了代码的灵活度,感兴趣的朋友跟随小编一起看看吧

注解和反射

一、注解

什么是注解?Annotation

 @Override :表示一个方法声明打算重写超类的另一个方法声明
 @Deprecated:表示不鼓励程序员使用这样的元素,(此注释可以用于修饰方法,属性,类)
 @SupperssWarnings():用来抑制编译时的警告信息
 "all", "unchecked", value={"unchecked","deprecation"})]

元注解

元注解的作用就是负责注解其他注解,Java定义了4个标准的meta-annotation类型,它们被用来对其他annotation类型作说明。

表示我们的注解可以用在哪些地方(字段、方法等)

表示我们的注解在什么地方还有效

表示是否将我们的注解生成在javadoc中

子类可以继承父类的注解

二、反射机制

动态语言:

静态语言:

Java不是动态语言,但是Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性。

Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期间于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

Class c = Class.forName("java.lang.String")

加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息(构造器、方法、字段、包括私有字段等)。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射

Java反射机制提供的功能

Java反射优缺点

 java.lang.Class; 代表一个类
 java.lang.reflect.Method; 代表类的方法
 java.lang.reflect.Field; 代表类的成员变量
 java.lang.reflect.Constructor; 代表类的构造器  

Class类

在Object类中定义了以下的方法,此方法将被所有子类继承

public final Class getClass()

此方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓从程序中的运行结果来看也是很好理解,即:可以通过对象反射类的名称。

对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留了Class类型的对象。一个Class对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。

Class类的常用方法

Class类中部分源码

 public class Class<T> {
      /**
      * 根据类的全限定名称获取 Class 对象
      *
      * @param className 类的全限定名称
      * @return Class 对象
      * @throws ClassNotFoundException 如果找不到指定名称的类
      */
     public static Class<?> forName(String className)
         throws ClassNotFoundException {
         return Class.forNameImpl(className, false);
     }
 ​
     // 实现细节
     private static Class<?> forNameImpl(String name, boolean initialize)
         throws ClassNotFoundException {
         ClassLoader cl = null;
         try {
             if (cl == null) {
                 cl = Thread.currentThread().getContextClassLoader();
             }
             return cl.loadClass(name);
         } catch (ClassNotFoundException e) {
             // 尝试使用当前类加载器
             cl = ClassLoader.getSystemClassLoader();
             return cl.loadClass(name);
         }
     }
     // 构造器是私有的,不允许外部直接创建 Class 对象
     private Class() {}
 ​
     // 返回类的全限定名称
     public native String getName();
 ​
     // 返回类的规范名称
     public String getCanonicalName() {
         return null; // 实际实现可能返回具体的规范名称
     }
 ​
     // 返回类的简单名称
     public String getSimpleName() {
         int lastDot = name.lastIndexOf('.');
         return (lastDot < 0) ? name : name.substring(lastDot + 1);
     }
 ​
     // 创建并返回此 Class 对象所表示的类的一个新实例
     @Deprecated(since = "9")
     public T newInstance() throws InstantiationException, IllegalAccessException {
         return getDeclaredConstructor().newInstance();
     }
 ​
     // 获取此 Class 对象所表示的类声明的所有方法
     public Method[] getDeclaredMethods() {
         Method[] result = declaredMethods;
         if (result == null) {
             // 实际实现会从类信息中获取方法
             // 这里简化处理
             result = new Method[0];
         }
         return result;
     }
 ​
     // 获取此 Class 对象所表示的类声明的所有字段
     public Field[] getDeclaredFields() {
         Field[] result = declaredFields;
         if (result == null) {
             // 实际实现会从类信息中获取字段
             // 这里简化处理
             result = new Field[0];
         }
         return result;
     }
 ​
     // 测试指定对象是否分配了此 Class 对象所表示的类或接口
     public boolean isInstance(Object obj) {
         return this.equals(obj.getClass());
     }
 ​
     // 获取此 Class 对象所表示的类的直接超类
     public native Class<?> getSuperclass();
 }
 ​
 ​

获取Class类的实例

Class c1 = Person.class;

Class c2 = person.getClass();

Class c3 = Class.forName("Deom1.Student");

Class c4 = Integer.Type;

哪些类型可以有Class对象

class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类。

Interface:接口

[]:数组

enum:枚举

annotation:注解@interface

primitive type:基本数据类型

void

package com.briup.chap13;
 ​
 import java.lang.annotation.ElementType;
 ​
 /**
  * @author 35329
  */
 public class Test04 {
     public static void main(String[] args) {
         Class c1 = Object.class;  // 类
         Class c2 = Comparable.class;// 接口
         Class c3 = String[].class;//一维数组
         Class c4 = int[][].class;//二维数组
         Class c5 = Override.class;// 注解
         Class c6 = ElementType.class;// 枚举
         Class c7 = Integer.class; //基本数据类型
         Class c8 = void.class;// void
         Class c9 = Class.class;// Class
 ​
         System.out.println("c1 = " + c1);
         System.out.println("c2 = " + c2);
         System.out.println("c3 = " + c3);
         System.out.println("c4 = " + c4);
         System.out.println("c5 = " + c5);
         System.out.println("c6 = " + c6);
         System.out.println("c7 = " + c7);
         System.out.println("c8 = " + c8);
         System.out.println("c9 = " + c9);
 ​
         // 只要元素类型与维度一样,就是同一个Class
         int[] a = new int[10];
         int[] b = new int[100];
         System.out.println("a.getClass().hashCode() = " + a.getClass().hashCode());
         System.out.println("b.getClass().hashCode() = " + b.getClass().hashCode());
 ​
         // 运行结果:
         //c1 = class java.lang.Object
         //c2 = interface java.lang.Comparable
         //c3 = class [Ljava.lang.String;
         //c4 = class [[I
         //c5 = interface java.lang.Override
         //c6 = class java.lang.annotation.ElementType
         //c7 = class java.lang.Integer
         //c8 = void
         //c9 = class java.lang.Class
 ​
         //a.getClass().hashCode() = 21685669
         //b.getClass().hashCode() = 21685669
     }
 }

类加载内存分析:

类的加载过程:

package com.briup.chap13;
 ​
 /**
  * @author 35329
  */
 public class Test05 {
     public static void main(String[] args) {
         A a = new A();
         System.out.println(A.m);
         /**
          * 1、加载到内存,会产生一个类对应Class对象
          * 2、链接,链接结束后 m = 0;
          * 3、初始化
          *   <clinit>(){
          *         System.out.println("A类静态代码块初始化");
          *         m = 300;
          *         m = 100;
          *   }
          *   m = 100;
          */
     }
 }
 ​
 class A{
     static {
         System.out.println("A类静态代码块初始化");
         m = 300;
     }
     static int m = 100;
     /*
     * m = 300
     * m = 100
     * 后面的会覆盖前面的
     * */
     public A(){
         System.out.println("A类的无参构造初始化");
     }
 }
 ​
 // 运行结果:
 //A类静态代码块初始化
 //A类的无参构造初始化
 //100

什么时候会发生类初始化

package com.briup.chap13;
 ​
 /**
  * @author 35329
  */
 // 测试类什么时候会初始化
 public class Test06 {
     static {
         System.out.println("mian类被加载");
     }
 ​
     public static void main(String[] args) throws ClassNotFoundException {
         // 1、主动被加载
         // Son son = new Son();
 ​
         // 反射也会产生主动引用
         // Class.forName("com.briup.chap13.Son");
 ​
         // 不会产生类的引用的方法
         // System.out.println("Son.b = " + Son.b);
 ​
         // Son[] arr = new Son[5];
 ​
         System.out.println("Son.M = " + Son.M);
     }
 }
 ​
 class Father{
     static int b = 2;
     static {
         System.out.println("父类被加载");
     }
 }
 class Son extends Father{
     static {
         System.out.println("子类被加载");
         m = 300;
     }
 ​
     static int m = 100;
     static final int M = 1;
 }
 ​

类加载器的作用:

类加载器的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。

类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。

1c3526c321964cd2827f703e5d296b30.png

类加载器作用是用来把类(class)装载进内存的。JVM规范定义了如下类型的类的加载器。

package com.briup.chap13;
 ​
 import com.briup.chap07.test.bean.Cat;
 ​
 /**
  * @author 35329
  */
 public class Test07 {
     public static void main(String[] args) throws ClassNotFoundException {
         // 获取系统类的加载器
         ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
         System.out.println("systemClassLoader = " + systemClassLoader);
 ​
         // 获取系统类加载器的父类加载器 --> 扩展类加载器
         ClassLoader parent = systemClassLoader.getParent();
         System.out.println("parent = " + parent);
 ​
         // 获取扩展类加载器 --> 根加载器(C/c++)
         ClassLoader parent1 = parent.getParent();
         System.out.println("parent1 = " + parent1);
 ​
         // 运行结果:
         //systemClassLoader = sun.misc.Launcher$AppClassLoader@18b4aac2
         //parent = sun.misc.Launcher$ExtClassLoader@14ae5a5
         //parent1 = null
 ​
         // 测试当前类是那个加载器加载的
         ClassLoader classLoader = Class.forName("com.briup.chap13.Test07").getClassLoader();
         System.out.println("classLoader = " + classLoader);
 ​
         // 测试JDK内置的类是谁加载的
         System.out.println("Class.forName(\"java.lang.Object\").getClassLoader() = " + Class.forName("java.lang.Object").getClassLoader());
 ​
         // 运行结果:
         //classLoader = sun.misc.Launcher$AppClassLoader@18b4aac2
         //Class.forName("java.lang.Object").getClassLoader() = null
 ​
         // 如何获得系统类加载器可以加载的路径
         System.out.println(System.getProperty("java.class.path"));
 ​
         // 运行结果:
         /*
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar;
         * C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar;
         * D:\JDProject\corejava\out\production\corejava;
         * C:\Users\35329\.m2\repository\junit\junit\4.12\junit-4.12.jar;
         * C:\Users\35329\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;
         * D:\idea\ideaIU-2020\lib\idea_rt.jar
         * */
     }
 }
 ​

获取运行时类的完整结构

通过反射获取运行时类的完整结构

Field、Method、Constructor、Superclass、Interface、Annotation

package com.briup.chap13;
 ​
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 ​
 /**
  * 通过反射获得类的信息
  * @author 35329
  */
 public class Test08 {
     public static void main(String[] args) throws Exception {
         Class c1 = Class.forName("com.briup.chap13.user");
 ​
         // 获得类的名字
         System.out.println("c1.getName() = " + c1.getName()); // 获得包名 + 类名
         System.out.println("c1.getSimpleName() = " + c1.getSimpleName()); // 获得类名
 ​
         System.out.println("-----------------------------------");
         // 获得类的属性
         Field[] fields = c1.getFields(); // 只能找到public属性
 ​
         fields = c1.getDeclaredFields(); // 可以找到全部属性
         for (Field field : fields) {
             System.out.println(field);
         }
 ​
         // 获得指定属性的值
         Field name = c1.getDeclaredField("name");
         System.out.println("name = " + name);
 ​
         System.out.println("-----------------------------------------");
         // 获得类的方法
         Method[] methods = c1.getMethods(); // 获得本类及其父类的全部public方法
         for (Method method : methods) {
             System.out.println("正常的:" + method);
         }
 ​
         Method[] declaredMethods = c1.getDeclaredMethods(); // 获得本类的所有方法(包括私有的)
         for (Method declaredMethod : declaredMethods) {
             System.out.println(declaredMethod);
         }
 ​
         // 获得指定方法
         // 重载
         Method getName = c1.getMethod("getName", null);
         Method setName = c1.getMethod("setName", String.class);
         System.out.println("getName = " + getName);
         System.out.println("setName = " + setName);
 ​
         System.out.println("--------------------------------");
         // 获得类的构造器
         Constructor[] constructors = c1.getConstructors(); // 获得public修饰的构造方法
         for (Constructor constructor : constructors) {
             System.out.println(constructor);
         }
         constructors = c1.getDeclaredConstructors();// 获得全部构造方法
         for (Constructor constructor : constructors) {
             System.out.println("#" + constructor);
         }
 ​
         //获得指定的构造器
         Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
         System.out.println("指定构造器:" + declaredConstructor);
     }
 }
 ​

小结:

有了Class对象,能做什么?

创建一个类的对象:调用Class对象的newInstance()方法

思考?难道没有无参的构造器就不能创建对象了吗?只要在操作的时候明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作。

步骤如下:

通过指定的方法

通过反射,调用类中的方法,通过Method类完成

Object invoke(Object obj, Object ...args)

setAccessible

package com.briup.chap13;
 ​
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.Map;
 ​
 /**
  * 分析性能问题
  * @author 35329
  */
 public class Test10 {
     // 通过普通方法调用
     public static void test01(){
         user user = new user();
         long startTime = System.currentTimeMillis();
         for (int i = 0; i < 1000000000; i++) {
             user.getName();
         }
 ​
         long endTime = System.currentTimeMillis();
         System.out.println("普通方法执行10亿次:" + (endTime - startTime) + "ms");
     }
     // 反射方法调用
     public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
         user user = new user();
         Class c1 = user.getClass();
         Method getName = c1.getDeclaredMethod("getName", null);
 ​
         long startTime = System.currentTimeMillis();
         for (int i = 0; i < 1000000000; i++) {
             getName.invoke(user,null);
         }
         long endTime = System.currentTimeMillis();
 ​
         System.out.println("反射方法执行10亿次:" + (endTime - startTime) + "ms");
     }
     // 反射方法调用 关闭检测
     public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
         user user = new user();
         Class c1 = user.getClass();
         Method getName = c1.getDeclaredMethod("getName", null);
         getName.setAccessible(true);
 ​
         long startTime = System.currentTimeMillis();
         for (int i = 0; i < 1000000000; i++) {
             getName.invoke(user,null);
         }
         long endTime = System.currentTimeMillis();
 ​
         System.out.println("关闭检测执行10亿次:" + (endTime - startTime) + "ms");
     }
 ​
     public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
         test01();
         test02();
         test03();
     }
 }
 ​

反射操作泛型(了解即可)

反射操作注解

练习:什么是ORM?

package com.briup.chap13;
 ​
 import java.lang.annotation.*;
 import java.lang.reflect.Field;
 ​
 /**
  * 练习反射操作注解
  * @author 35329
  */
 public class Test12 {
     public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
         Class<?> c1 = Class.forName("com.briup.chap13.Student2");
 ​
         // 通过反射获取注解
         Annotation[] annotations = c1.getAnnotations();
         for (Annotation annotation : annotations) {
             System.out.println(annotation);
         }
 ​
         // 获得注解的Value值
         TableBriup tableBriup = c1.getAnnotation(TableBriup.class);
         String value = tableBriup.value();
         System.out.println(value);
 ​
         // 获得类指定的注解
         Field f = c1.getDeclaredField("name");
         FieldBriup annotation = f.getAnnotation(FieldBriup.class);
         System.out.println(annotation.columnName());
         System.out.println(annotation.type());
         System.out.println(annotation.length());
 ​
     }
 }
 ​
 @TableBriup("db_student")
 class Student2{
 ​
     @FieldBriup(columnName = "db_id", type = "int", length = 10)
     private int id;
     @FieldBriup(columnName = "db_age", type = "int", length = 10)
     private int age;
     @FieldBriup(columnName = "db_name", type = "varchar", length = 3)
     private String name;
 ​
     public Student2() {
     }
 ​
     public Student2(int id, int age, String name) {
         this.id = id;
         this.age = age;
         this.name = name;
     }
 ​
     @Override
     public String toString() {
         return "Test12{" +
                 "id=" + id +
                 ", age=" + age +
                 ", name='" + name + '\'' +
                 '}';
     }
 ​
     public int getId() {
         return id;
     }
 ​
     public void setId(int id) {
         this.id = id;
     }
 ​
     public int getAge() {
         return age;
     }
 ​
     public void setAge(int age) {
         this.age = age;
     }
 ​
     public String getName() {
         return name;
     }
 ​
     public void setName(String name) {
         this.name = name;
     }
 }
 ​
 // 类名注解
 @Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
 @interface TableBriup{
     String value();
 }
 ​
 // 属性的注解
 @Target(ElementType.FIELD)
 @Retention(RetentionPolicy.RUNTIME)
 @interface FieldBriup{
     String columnName();
     String type();
     int length();
 }

到此这篇关于Java中注解与反射的详细介绍的文章就介绍到这了,更多相关Java注解与反射内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文