Java反射与注解

注解

注解不同于注释,注解可以加在类的上面,以@注解名实现。这样的注解可以被编译器读到,从而对这个类进行一些操作。也可以在反射的时候,被其它的类的方法所读到,通过反射从而创建对象或者改变这个类模板的一些状态。

常见的4个元注解:
元注解的作用就是负责注解其他注解, Java定义了4个标准的meta annotation类型,他们被用来提供对其他annotation类型作说明.

这些类型和它们所支持的类在java.lang.annotation包中可以找到

@Target , @Retention@Documented , @Inherited

@Target :用于描述**注解的使用范围(**即:被描述的注解可以用在什么地方)

@Retention: 表示需要在什么级别保存该注释信息,用于描述注解的生命周期,(SOURCE < CLASS < RUNTIME) 越高级范围越大

@Document:说明该注解将被包含在javadoc中

@Inherited: 说明子类可以继承父类中的该注解

注意:所有注解都有一个共同父类Annotation!!!

import java.lang.annotation.*;
@MyAnnotation
public class Demo02 {
    void test(){
    }
}
//定义一个注解
//Target 表示我们的注解可以用在哪些地方.
@Target(value = {ElementType.METHOD, ElementType.TYPE})
//Retention表示我们的注解在什么地方还有效。
// runtime>class>sources
@Retention(value = RetentionPolicy.RUNTIME)
//Documented表示是否将我们的注解生成在Javadoc中
@Documented
//Inherited子类可以继承父类的注解
@Inherited
@interface MyAnnotation{ 
	String value() default ""; //这里注解的定义参考下面的说明
}

使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口

分析:

  • @ interface用来声明一个注解,格式: public @ interface注解名{定义内容}
    其中的每一个方法实际 上是声明了一个配置参数.
  • 方法的名称就是参数的名称.
  • 返回值类型就是参数的类型(返回值只能是基本类型,Class , String , enum ).
  • 可以通过default来声明参数的默认值
  • 如果只有一个参数成员, 一般参数名为value 只有一个参数时 运用注解时参数名可省
  • 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值.

内置的一些注解:
在这里插入图片描述

反射

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

例如:获得String对象的类模板

Class C= Class.forName("java.lang String");

为什么叫反射?

看图
在这里插入图片描述
正常new类的方式和反射完全相反。因此才叫反射。

反射的功能
运行时判断任意对象所属类
运行时构造任意一个类的对象
运行时判断任意一个类所具有的成员变量以及方法
运行时获取泛型信息
运行时调用任意一个对象变量成员以及方法
运行时处理注解
生成动态代理

主要的类
java.lang.Class
java.lang.reflect.Method
java.lang.reflect.Field
java.lang.reflect.Construct

Class类

public static ClassforName(String name); //返回指定类名的Class对象
public Object newInstance(); //调用无参构造函数,返回该Class对象的一个实例
public String getName(); //返回此Class对象所表示的实体(类,接口,数组,或者void)的名称
public Class getSuperClass(); //返回当前Class对象的父类Class对象
public Class[] getInterfaces(); //获取当前Class对象的接口
public ClassLoader getClassLoader(); //返回该类的类加载器
public Constructor[] getConstructors(); //返回包含某些Constructor对象的数组
public Method getMethod(String name, Class... T);//返回一个Method对象,此对象形参类型为paramType
Field[] getDeclaredFields(); //返回Field对象的一个数组

获取Class实例

  • 已知具体的类,通过类的Class属性获取
Class clazz = Person.class;
  • 已知某个类的实例,调用getClass()方法
Class clazz = person.getClass();
  • 已知类全名,则可以通过forName()方法调用。但是可能抛出ClassNotFoundException异常。
Class clazz = Class.forName("xxx");
  • 内置基本数据类型可以直接用类名.Type
  • 利用ClassLoader

注意:在需要获得接口的class对象时,需要接口名.Class,不能对象.Class。因为后者是获取到的是实现类的Class对象。

.class和getClass()区别

类名.class叫做“类字面量”,因class是关键字, 所以类名.class编译时确定。

getclass()运行时根据实际实例确定,getClass()是动态而且是final的。

String.class 是能对类名的引用 取得在内存中该类型class对象的引用,

new String().getClass() 是通过实例对象取得在内存中该实际类型class对象的引用。

可以有Class对象的类型:

  • class: 外部类成员(成员内部类,静态内部类),
  • 局部内部类,匿名内部类。
  • interface: 接口
  • []:数组
  • enum:枚举
  • annotation:注解@interface
  • primitive type:基本数据类型
  • void
  • Class

类加载的过程
在这里插入图片描述
举例:

public class Demo04 {
    public static void main(String[] args) {
        A a=new A();
        System.out.println("初始化完打印的值"+A.m);//100
        /*
        1.加载到内存,产生一个类对应Class对象
        2.链接,链接结束后m=0
        3.初始化
        <clinit>(){
        按顺序收集所有静态语句
            System. out . println( "A类静态代码块初始化") ;
            m = 300;
            m=100;
         }
         m=100
         */
        }
}
class A{
    static int m ;
    static{
        System.out.println("静态代码块初始化");
        System.out.println("类变量分配的初始值"+m);
        m=1000;
        System.out.println(m);
    }
    public A(){
        System.out.println("无参构造初始化");
    }
}

在这里插入图片描述
什么时候发生类初始化?

1.类主动引用

1.1 当虚拟机启动时,会初始化main方法所在的类
1.2 new一个类的对象
1.3 调用类的静态成员
1.4 使用java.lang.reflect包方法对类进行反射调用
1.5当初始化一个类,如果父类未被初始化,则先初始化父类。

不会发生类的初始化

2.类的被动引用

2.1 当访问一个静态域时候,只有真正声明这个域的类才会被初始化。比如通过子类引用父类的静态变量,只会让父类初始化。
2.2 通过数组定义类引用,不会触发此类的初始化。因为还没有真正构造对象,只是声明。
2.3 引用类变量常量不会触发此类的初始化 (常量在准备阶段被赋值了,而不是因为在常量池)

注意:

如果自己定义和jdk同名的类,运行时虚拟机会在系统的类加载器中寻找,再去扩展类加载器中寻找,再去根加载器中寻找,如果存在同名的类,会使用根加载器中的类,而不使用自己定义的类。

创建运行时类的对象

这些功能包括:

  • 通过反射获取运行时类的完整结构Field、Method, Constructor、 Superclass、 Interface、 Annotation
  • 实现的全部接口
  • 所继承的父类
  • 全部的构造器
  • 全部的方法
  • 全部的Field
  • 注解

示例代码:

Class c1 = Class.forName("com.reflection.User");
//获得包名+类名
User user = new User();
Class c2 = user.getClass();
//获得类的信息
System.out.println(c1.getName());//获得包名+类名
System.out.println(c1.getSimpleName());//获得类名
//获得类的属性
System.out.println("=======================");
Field[] fields = c1.getFields();//获取类的公开属性和父类的公开属性
fields = c1.getDeclaredFields();//获取类的任何属性只能本类的无父类 
for (Field field : fields) {
    System.out.println(field);
}
//获得指定属性 只能未本类的无父类 
Field name = c1.getDeclaredField("name");
System.out.println(name);
//获得类的方法
System.out.println("=========================");
Method[] methods = c1.getMethods();//获得本类和父类的所有public方法 无构造方法
for (Method method : methods) {
    System.out.println("methods " + method);
}
System.out.println("=========================");
Method[] decmethods = c1.getDeclaredMethods();//获得本类的所有方法包括私有
for (Method method : decmethods) {
    System.out.println("decmethods " + method);
}
//获得指定方法(不包含private)
//需要传参数的原因:存在重载,参数可找到指定的方法
System.out.println("=========================");
Method getName = c1.getMethod("getName", null);
Method setName = c1.getMethod("setName", String.class);
System.out.println(getName);
System.out.println(setName);
//获得本类构造器
System.out.println("=========================");
//public
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {
    System.out.println("getConstructors " + constructor);
}
System.out.println("=========================");
//包含private
Constructor[] constructors1 = c1.getDeclaredConstructors();
for (Constructor constructor : constructors1) {
    System.out.println("getDeclaredConstructors " + constructors1);
}
//获得指定的构造器
Constructor getDeclaredConstructor = c1.getDeclaredConstructor(String.class,int.class);
System.out.println("指定构造器" + getDeclaredConstructor);

通过类对象创建对应类的实例

有了对应的Class对象,我们也可以创建所对应的类实例了。

  • 如果类有无参构造,我们则可以通过newInstance()方法调用创建出一个实例
  • 如果有有参构造,我们则需要获得其有参构造的Construct对象,通过Construct对象的newInstance()方法来进行new获取。

调用指定的方法

我们可以通过反射来调用指定类实例中指定的方法。其步骤为:

①通过Class类的getMethod(String name, Class… parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。

②之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。

  • Object对应原方法的返回值,若原方法无返回值,此时返回null
  • 若原方法若为静态方法,此时形参0bject obj可为null
  • 若原方法形参列表为空,则Object[] args为null
  • 若原方法声明为private,则需要在调用此invoke()方法前**(getDeclaredXxx只是可以获取对应私有的方法、属性、构造方法,要使用需setAccessible为true),显式调用方法对象的setAccessible(true)方法,将可访问private的方法。

如果该方法为private、protected方法,则调用时需要取消语法访问检查。Method和Field、Constructor对象都有setAccessible()方法。设置为true之后,该方法、类成员则可随意调用。

public void setAccessible(boolean flag)

例子:狂神说Java—动态创建对象,通过反射。

//动态创建对象,通过反射
public class Demo08 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        //获得class对象
        Class c1 = Class.forName("com.reflection.User");
        //创建一个对象
        System.out.println("============================");
        User user = (User)c1.newInstance();//本质是调用了类的无参构造器
        System.out.println(user);
        //通过构造器创建对象
        System.out.println("============================");
        Constructor constructor = c1.getDeclaredConstructor(String.class,int.class);
        User user2 = (User)constructor.newInstance("打爆",22);
        System.out.println(user2);
        //通过反射调用普通方法
        //通过反射获取一个方法
        System.out.println("============================");
        Method setName = c1.getDeclaredMethod("setName",String.class);
        //invoke:激活的意思
        //参数:对象,方法参数的值
        setName.invoke(user,"立良");
        System.out.println(user.getName());
        System.out.println("============================");
        //通过反射操作属性
        User user3 = (User)c1.newInstance();
        //通过Class对象获取对应属性信息
        Field name = c1.getDeclaredField("name");
        //不能直接操作私有属性,我们需要关闭程序的安全监测,属性或方法的setAccessible(true)
        name.setAccessible(true);
        name.set(user3,"小宝");//找到实例变量属性并设置值 静态属性时第一个参数Object为null
        System.out.println(user3.getName());
    }
}


public class Demo09 {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        test1();//5ms
        test2();//4114ms
        test3();//1483ms
    }
    public static void test1(){
        User user = new User();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            user.getName();
        }
        long end = System.currentTimeMillis();
        System.out.println(end-start+"ms");
    }
    public static void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user = new User();
        Class c1 = user.getClass();
        Method getName = c1.getDeclaredMethod("getName",null);

        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user,null);
        }
        long end = System.currentTimeMillis();
        System.out.println(end-start+"ms");
    }
    public static void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user = new User();
        Class c1 = user.getClass();
        Method getName = c1.getDeclaredMethod("getName",null);
        getName.setAccessible(true);//使用了setAccessible(true)后速度会快很多
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user,null);
        }
        long end = System.currentTimeMillis();
        System.out.println(end-start+"ms");
    }
}

反射来操作泛型

Java中针对反射中的形参的反射设置了四个类。这四个类ParameterizedType , GenericArrayType ,TypeVariable和WildcardType几种类型不能被归一到Class类中。但是又和原始类型齐名。

  • ParameterizedType :表示一种参数化类型,比如Collection ,具有<>符号的变量是参数化类型即泛型
  • GenericArrayType :表示一种元素类型是参数化类型或者类型变量的数组类型
  • TypeVariable :是各种类型变量的公共父接口
  • WildcardType :代表一种通配符类型表达式
  • java.lang.reflect.Type:java语言中所有类型的公共父接口
  • Type所有类型指代的有:原始类型 (raw types)【对应Class】,参数化类型 (parameterizedtypes)【对应ParameterizedType】, 数组类型 (array types)【对应GenericArrayType】,类型变量 (type variables)【对应TypeVariable】,基本数据类型(primitivetypes)【仍然对应Class】

狂神说Java反射操作泛型的例子:

public class Demo10 {
    public void test01 (Map<String,User> map, List<User> list){
        System.out.println("test01");
    }
    public Map<String, User> test02(){
        System.out.println("test02");
        return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {
        Method method = Demo10.class.getMethod("test01",Map.class,List.class);
        Type[] genericParameterTypes= method.getGenericParameterTypes();//获得方法中形参类型 返回Type类型的数组 Type[].
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println("参数范型"+genericParameterType);
            if (genericParameterType instanceof ParameterizedType //如果是泛型){
                Type[] actualTypeAnguments=(ParameterizedType)genericParameterType.getActualTypeArguments();//getActualTypeArguments() 获取参数化类型<>中的实际类型
                for (Type actualTypeAngument : actualTypeAnguments) {
                    System.out.println("实际参数范型"+actualTypeAngument);
                }
            }
        }
        Method method1 = Demo10.class.getMethod("test02",null);
        Type getGenericReturnType= method1.getGenericReturnType();//获取返回值泛型
        if (getGenericReturnType instanceof  ParameterizedType) {
            Type[] actualTypeArguments = (ParameterizedType) getGenericReturnType.getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println("返回值范型" + actualTypeArgument);
            }
        }
    }
}

getGenericParameterTypes() : 返回Type类型的数组 Type[].
getParameterTypes() : 返回Class类型的数组: Class<?>[].

①如果方法参数不是参数化类型(泛型),那么getParameterTypes和getGenericParameterTypes返回的结果是一样的。
②如果方法参数是泛型,这时就有区别了,getGenericParameterTypes会返回完整的信息(List),而getParameterTypes只会返回参数类型(List),参数化类型无法得到。

反射获得注解
具体用法看下面的例子:

import java.lang.annotation.*;

public class Test10 {
    public static void main(String[] args) throws NoSuchFieldException {
        Class<Student1> student1Class = Student1.class;
        Annotation[] annotations = student1Class.getAnnotations();//Annotation为所有注解的父类 获取类上所有注解
        for (Annotation annotation : annotations) {
            System.out.println(annotation);//@reflect.Table(value=db_student)
        }
        Table annotation = student1Class.getAnnotation(Table.class);//获取class对象标注在类上指定的注解
        System.out.println(annotation);//@reflect.Table(value=db_student)
        String value = annotation.value();//获取class对象中指定的注解的值 即注解的值
        System.out.println(value);
        java.lang.reflect.Field name = student1Class.getDeclaredField("name");//获取class对象中的属性
        Field annotation1 = name.getAnnotation(Field.class);//获取class对象中的标注在属性上的注解
        System.out.println(annotation1.columnName());//获取class对象中的属性上注解的值
        System.out.println(annotation1.length());
        System.out.println(annotation1.type());
    }
}
//类上注解
@Table("db_student")
class Student1{
    //属性上注解
    @Field(columnName = "db_id",type = "int",length = 10)
    private int id;
    @Field(columnName = "db_age",type = "int",length = 10)
    private int age;
    @Field(columnName = "db_name",type = "varchar",length = 10)
    private String name;
    public Student1() {
    }

    public Student1(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.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 Table{
    String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Field{
    String columnName();
    String type();
    int length();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值