注解和反射

注解和反射

注解

Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。

java内置注解

Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。

  • @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
  • @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
  • @SuppressWarnings - 指示编译器去忽略注解中声明的警告。
package org.reflect;

import java.util.ArrayList;
import java.util.List;

public class  reflect {
    @Override//重写的注解,可不写
    public String toString() {
        return "reflect{}";
    }
    @Deprecated//废弃的,标记方法,表示此方法已过时,但是可以使用,只是不推荐使用
    public static void test1(){
        System.out.println("废弃的Deprecated");
    }
    @SuppressWarnings("all")//@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
    //镇压警告,需要参数,使所有警告消失,抑制编译时的警告信息,可以放在类和方法上
    public static void test2(){
        List<String> list = new ArrayList<>();
    }

    public static void main(String[] args) {
        test1();
    }
}

元注解

  • @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
  • @Documented - 标记这些注解是否包含在用户文档中。
  • @Target - 标记这个注解应该是哪种 Java 成员。
  • @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
@Target(ElementType.METHOD)
public enum ElementType {
    //类,接口,或者枚举类型
    TYPE,
    /*字段声明(包括枚举常量)*/
    FIELD,
    /*方法生名*/
    METHOD,
    /*形式参数声明 */
    PARAMETER,
    /*Constructor declaration */
    CONSTRUCTOR,
    /*Local variable declaration*/
    LOCAL_VARIABLE,
    /** Annotation type declaration */
    ANNOTATION_TYPE,
    /** Package declaration */
    PACKAGE,
    /*类型参数声明*/
    TYPE_PARAMETER,
    /*类型的使用*/
    TYPE_USE
}
@Retention(RetentionPolicy.RUNTIME)
public enum RetentionPolicy {
  //源码,编译时有效
    SOURCE,
    //class,编译后,class有效
    CLASS,
	//运行时有效
    RUNTIME
}
package org.reflect;

import javax.xml.bind.Element;
import java.lang.annotation.*;

public class Demo1 {
    @MyAnnotation
public void test(){ }
}
@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE})//注解作用域,此时可以放在方法上,value可加可不加
@Retention(value = RetentionPolicy.RUNTIME)//注解有效周期,表示我们的注解再什么时候有效
@Documented//表示是否将文档生成再javadoc中
@Inherited//说明子类可以继承父类中的该注解
@interface MyAnnotation{
}
//@interface自定义注解,自动继承java.lang.annotation.Annotation接口

package org.reflect;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class Demo2 {
    @MyAnnotation1(name = "sang",age = 18,id = 2019,school = {"school","13"})
    public void test(){
    }
}
@Target({ElementType.TYPE,ElementType.ANNOTATION_TYPE,ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation1{
    //注解参数:参数类型 + 参数名 + () 可以加默认值使用default关键字
    String name() default "";//注解参数,可以设置default,后接默认值
    int age() default 0;
    int id() default  -1;//如果默认值为-1,代表不存在,indexof,如果找不到就返回-1
    String[] school() default "";
}
  • @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
  • @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
  • @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

Annotation 架构

1 个 Annotation 和 1 个 RetentionPolicy 关联。

1 个 Annotation 和 1~n 个 ElementType 关联。

Annotation 有许多实现类,包括:Deprecated, Documented, Inherited, Override 等等

反射

Java反射技术的作用:主要是在是在程序运行的时候动态的改变运行结构

静态语言和动态语言

动态语言的意思就是在运行的时候可以更改编译好的class文件,可以对其方法,属性或者属性类型做改变。但是静态语言在运行的时候不能够更改已经编译好的class文件的内容。java虽然是静态语言但是却可以通过反射机制编程动态语言,也就是说通过反射可以在运行的时候更改java中已经编译好的class文件,对其中的属性,属性类型,方法做一些修改。

package org.reflect;

import java.lang.reflect.Constructor;

public class Test1 {
    public static void main(String[] args) throws ClassNotFoundException {
       //通过反射获取class对象
        Class<?> forName = Class.forName("org.reflect.Demo");
        Class<?> forName1 = Class.forName("org.reflect.Demo");
        Class<?> forName2 = Class.forName("org.reflect.Demo");
        System.out.println(forName);
        //一个类再内存中只能有一个Class对象
        //一个类,在被加载后,类的整个结构都会被封装再class对象中
        System.out.println(forName.hashCode());
        System.out.println(forName1.hashCode());
        System.out.println(forName2.hashCode());
        System.out.println(forName.getName());
        System.out.println(forName.getAnnotations());
        System.out.println(forName.getClass().getMethods());
    }
}

​ Person student = new Student();
​ //方式一:通过对象获得
​ Class aClass1 = student.getClass();
​ //方式2:通过forname获得
​ Class<?> aClass2 = Class.forName("org.entity.Student");
​ //方式3,通过类名获得
​ Class aClass3 = Student.class;

package org.reflect;

import org.entity.Person;
import org.entity.Student;

public class Test2 {
    public static void main(String[] args) throws ClassNotFoundException {
        Person student = new Student();
        System.out.println(student.name);
        //方式一:通过对象获得
        Class aClass1 = student.getClass();
        System.out.println(aClass1.hashCode());
        //方式2:通过forname获得
        Class<?> aClass2 = Class.forName("org.entity.Student");
        System.out.println(aClass2.hashCode());
        //方式3,通过类名获得
        Class aClass3 = Student.class;
        System.out.println(aClass3.hashCode());
        //方式4,基本内置类型的包装类都有一个Type属性
        Class type = Integer.TYPE;
        //public static final Class<Integer>  TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
        System.out.println(type);
        //获得父类类型
        Class superclass = aClass1.getSuperclass();
        Class<?> superclass1 = aClass2.getSuperclass();
        Class superclass2 = aClass3.getSuperclass();
        System.out.println(superclass);
        System.out.println(superclass.hashCode()
                + "\t" + superclass1.hashCode() +
                "\t" + superclass2.hashCode());
    }
}

那些类可以用Class对象呢?

package org.reflect;

import java.lang.annotation.ElementType;

public class Test3 {
    public static void main(String[] args) {
        Class c1 = Object.class;
        Class c2 = Runnable.class;
        Class c3 = MyAnnotation.class;
        Class c4 = String[].class;
        Class c5 = int[][].class;
        Class c6 = Integer.class;
        Class c7 = void.class;
        Class c8 = Class.class;
        Class c9 = ElementType.class;

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);
        int[] a = new int[10];
        int[] b = new int[200];
        System.out.println(a.getClass().hashCode());//一个类型的对象只能有
        System.out.println(b.getClass().hashCode());//一个class对象
    }
}
/*
class java.lang.Object
interface java.lang.Runnable
interface org.reflect.MyAnnotation
class [Ljava.lang.String;
class [[I
class java.lang.Integer
void
class java.lang.Class
class java.lang.annotation.ElementType
 */

java内存分析

方法区就是一个特殊的堆

类的加载过程

当程序使用某个类时,如果该类还未被加载到内存中,则系统会通过:

  1. 类的加载(Load):将类的class文件读入内存,并为之创建一个java.lang.Class对象。此过程由类加载器完成
  2. 类的链接(Link):将类的二进制数据合并到jre中
  3. 类的初始化(Initialize):JVM负责对类进行初始化

package org.reflect;
public class Test4 {
    public static void main(String[] args) {
        System.out.println(A.m);
        A a = new A();
    }
}
class A{
    static int m = 10;
    static {
        System.out.println("static");
        System.out.println(A.m);
        m = 100;
    }
    public A(){
        System.out.println("A类的无参构造");
    }
}

什么时候会发生类的初始化

package org.reflect;

public class Test5 {
    static {
        System.out.println("Main类静态代码块被初始化了");
    }

    public static void main(String[] args) throws ClassNotFoundException {
       //虚拟机启动时,main方法所在类先被初始化
        //Son son = new Son();//new一个类时父类静态先被初始化之后是子类
        //System.out.println(Son.m);//子类调用静态变量时主类,父类,子类静态代码块先加载,之后是静态变量被打印
        //System.out.println(Son.a);//子类调用父类静态变量时,子类不会被加载,只有父类被加载
        //System.out.println(Son.B);//子类调用父类常量时,都不会被加载
        //System.out.println(Son.M);//子类调用自己常量时,都不会被加载
        //Class.forName("org.reflect.Son");//反射时都会被加载
        //Son[] sons = new Son[20];//定义此类型数组时不会被加载
    }
}
class Father{
    static int a = 10;
    static {
        System.out.println("Father静态代码块被初始化了");
    }
    static final int B = 20;
}
class Son extends Father{
    static {
        System.out.println("Son静态代码块被初始化了");
        m = 100;
    }
    static int m = 20;
    static final int M = 666;
}

类加载器

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

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

package org.reflect;

public class Demo3 {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取系统类的加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);//AppClassLoader
        //获取系统类加载器的父类加载器--》扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);//ExtClassLoader
        //获取扩展类加载器的父类加载器--》根(引导类)加载器(C/C++)
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);//null,根加载器是由C/C++写的,获取不到
        //测试当前类是由那个加载器加载的
        Class<?> aClass = Class.forName("org.reflect.Demo3");
        ClassLoader classLoader1 = aClass.getClassLoader();
        System.out.println(classLoader1);//AppClassLoader,系统类加载器,
        //测试JDK内置的类是由谁加载的
        ClassLoader classLoader = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader);//null,根加载器
        //如何获得系统类加载器可以加载的路径
        System.out.println(System.getProperty("java.class.path"));
        /*可以加载的类路径,只有再这些路径中的类拆可以被加载
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\charsets.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\deploy.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\access-bridge-64.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\cldrdata.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\dnsns.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\jaccess.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\jfxrt.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\localedata.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\nashorn.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\sunec.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\sunjce_provider.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\sunmscapi.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\sunpkcs11.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\zipfs.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\javaws.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\jce.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\jfr.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\jfxswt.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\jsse.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\management-agent.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\plugin.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\resources.jar;
        C:\Program Files\Java\jdk1.8.0_333\jre\lib\rt.jar;
        D:\ideaProject\Test\reflection\target\classes;
        E:\idea\IntelliJ IDEA 2020.2.1\lib\idea_rt.jar
        双亲委派机制
        自己定义的类若要加载,会一级一级向上找,先看系统类加载器里是否由此jar包,
        再向上找,看扩展类加载器是否有此jar包,再向上找,看根加载器路径中是否有此类
        若根加载器路径中有此jar包,则会用根加载器路径下的jar包,而不会用自己定义的
        类,如果自己定义类一个和系统类加载器路径下的jar包,而不会用我们自己定义的jar包

         */
    }
}
package org.reflect;

import org.entity.Student;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class DemoTest3 {
    public static void main(String[] args) throws Exception {
        Class aClass = Class.forName("org.entity.Student");
//        Student student = new Student();
//        aClass = student.getClass();
        System.out.println(aClass.getName());//包名  + 类名
        System.out.println(aClass.getSimpleName());//类名
        Field[] fields = aClass.getFields();//获得public修饰的属性,连同父类public属性也会被获得
        for (Field f : fields) {//public修饰的属性会被打印出来,如果该类父类有public修饰的属性,则也会被打印出来
            System.out.println(f);//private修饰的属性不会被打印出来,如果该类的父类属性也为private修饰,则不会被打印出来
        }
        Field[] fields1 = aClass.getDeclaredFields();//获得类的私有属性
        for (Field f1 : fields1) {//不会获得子类中父类的私有属性,只能获得子类的私有属性
            System.out.println(f1);//和public属性
        }
        //获得指定属性的值,无该字段会报错NoSuchFieldException
        System.out.println(aClass.getDeclaredField("id"));
        //获得类的方法
        Method[] methods = aClass.getMethods();//获得本类包括父类的所有public方法
        for (Method method : methods) {
            System.out.println("正常的:" + method);
        }
        Method[] declaredMethods = aClass.getDeclaredMethods();//只获取本类的所有方法包括private方法
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }
        //获得指定方法
        Method getName = aClass.getDeclaredMethod("getName", null);
        System.out.println(getName);
        Method setName = aClass.getDeclaredMethod("setName", String.class);
        System.out.println(setName);
        //获得构造器
        Constructor[] constructors = aClass.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);//获得本类的public方法
        }
        constructors = aClass.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);//获得本类的所有构造方法
        }//获得指定的构造方法
        Constructor constructor = aClass.getDeclaredConstructor(String.class, int.class, String.class);
        System.out.println(constructor);
    }
}

动态创建对象执行方法

package org.reflect;

import org.entity.Student;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Demo4 {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("org.entity.Student");
//        Object o = aClass.newInstance();//调用的无参构造,打印的toString方法
//        System.out.println(o);//Student{name='null', age=0',id = 2019121762}
//        Student student = (Student) aClass.newInstance();
//        System.out.println(student);//使用的是无参构造器的构造方法,打印的toString方法
        //System.out.println(student.getId());//可以通过此实例对象使用方法
        //通过构造器创建对象
        //Constructor<?> constructor = aClass.getDeclaredConstructor(String.class, int.class, String.class);
        //Object student = constructor.newInstance("zhang", 12, "2012222");
        //System.out.println(student);//使用有参构造,创建对象
        Student o = (Student) aClass.newInstance();
        //通过反射获取方法
        Method setName = aClass.getDeclaredMethod("setName", String.class);
        setName.invoke(o,"sang");//激活此方法,传入对象及参数
        System.out.println(o.getName());
        //反射操作属性
        Field id = aClass.getDeclaredField("id");
        id.setAccessible(true);//修改权限,关闭程序的安全访问
        id.set(o,"20191217");//若字段修饰为private,则会报错,需要用方法修改权限1
        System.out.println(o.getId());
    }
}

setAccessible

性能对比分析

反射会影响执行效率,反射关闭安全检查会有所提高

package org.reflect;

import org.entity.Student;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Demo5 {
    public static void main(String[] args) {
        test1();//7ms
        try {
            test2();//46ms
            test3();//27ms
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }//普通方法调用
    public static void test1(){
        Student student = new Student();
        long l1 = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            student.getId();
        }
        long l2 = System.currentTimeMillis();
        System.out.println(l2 - l1 + "ms");
    }//反射方法调用
    public static void test2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> aClass = Class.forName("org.entity.Student");
        Student student =(Student) aClass.newInstance();
        Method getName = aClass.getDeclaredMethod("getName");
        long l1 = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            getName.invoke(student,null);
        }
        long l2 = System.currentTimeMillis();
        System.out.println(l2 - l1 + "ms");
    }//反射方法关闭安全检查调用
    public static void test3() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> aClass = Class.forName("org.entity.Student");
        Student student =(Student) aClass.newInstance();
        Method getName = aClass.getDeclaredMethod("getName");
        getName.setAccessible(true);
        long l1 = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
           getName.invoke(student,null);
        }
        long l2 = System.currentTimeMillis();
        System.out.println(l2 - l1 + "ms");
    }
}

反射操作泛型

获得方法中参数类型泛型信息

package org.reflect;

import org.entity.Student;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
//通过反射获得泛型信息
public class Demo6 {
    public static void main(String[] args) throws NoSuchMethodException {
        Class aClass = Demo6.class;//获取Demo6类,通过反射获取方法
        //获取里面的test1()方法
        Method test1 = aClass.getMethod("test1", Map.class, List.class);
        //通过getGenericParameterTypes()方法,获得方法的参数列表,返回参数数组
        Type[] parameterTypes = test1.getGenericParameterTypes();
        //遍历参数数组,打印参数信息
        for (Type parameterType : parameterTypes) {
            System.out.println(parameterType + "++++++++++");
            //如果,参数信息属于一种参数化类型,将此参数信息强制转换为参数化类型,然后
            if (parameterType instanceof ParameterizedType){//通过getActualTypeArguments()方法,获得真实参数
                Type[] actualTypeArguments = ((ParameterizedType) parameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);//遍历真实参数列表
                }//打印信息,获得参数中的,参数类型信息。
            }//获得,方法中的参数类型信息
        }
    }
    public void test1(Map<String, Student> map, List<Student> list){
        System.out.println("test1");
    }
}

获得返回值类型中的泛型参数

package org.reflect;

import org.entity.Student;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;

public class Demo7 {
    public static void main(String[] args) {
        Class<Demo7> demo7Class = Demo7.class;
        try {
            Method test2 = demo7Class.getMethod("test2",null);
            //获得返回值类型
            Type genericReturnType = test2.getGenericReturnType();
            //如果返回值类型为参数类型
            if (genericReturnType instanceof ParameterizedType){
                //获得真实的返回值类型中的参数类型
                Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
                ///遍历结果,查看真实参数信息
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public Map<String, Student> test2(){
        System.out.println("test2");
        return null;
    }
}

反射获得注解

package org.reflect;
import org.entity.MyField;
import org.entity.Sang;
import org.entity.User;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

public class Demo8 {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("org.entity.User");
        User user =(User) aClass.newInstance();
        Annotation[] annotations = aClass.getDeclaredAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }//获得注解的value值
        Sang annotation = aClass.getAnnotation(Sang.class);
        System.out.println(annotation.value());

        //获得指定的注解
        Field name = aClass.getDeclaredField("name");
        MyField annotation1 = name.getAnnotation(MyField.class);
        System.out.println(annotation1);
    }
}

posted @ 2023-01-12 18:23  小国哥哥  阅读(12)  评论(0编辑  收藏  举报