[笔记] java注解和反射简要学习

13 篇文章 1 订阅

本文为自学 Java注解和反射机制的个人学习笔记,因个人习惯和时间关系,仅记录了本人为曾了解和和认为重要的知识点,所以如要学习建议观看原视频学习

注解

基础的注解概念的解释可以查看这里

一个简单的自实现的注解如下:

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation1 {
	String name();
}

其中,参数的格式是 类型 名字()

  • @interface 来定义一个注解

  • @Target 描述了注解的使用范围

  • @Retention 表述需要在什么级别保存该注释信息,用于描述注解的生命周期,其中 (SOURCE<CLASS<RUNTIME)

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

(具体注解实际使用方式可以看最后一节章关于 使用反射对象来获取所注解对象的注解信息的介绍。)

反射 reflection

反射概述

反射机制 允许程序执行期间借助 Reflection API 来获取任何类的内部属性和方法。

正常方式 : 引入“包类”的名称->new实例化->获得实例化对象
反射方式: 实例化对象->getClass()方法->获得完整的“包类”名称

获得反射对象

java反射机制提供的功能:

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时获取泛型信息
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时处理 注解
  • 生成动态代理 (AOP)

反射的优缺点:

  • 优点: 可以实现动态创建对象和编译,灵活
  • 缺点:对性能有影响。反射实际上是一种解释操作,需要告诉JVM需要做什么和要求,要慢于直接(new)执行相同的操作。
反射相关的API
  • java.lang.Class 代表一个类
  • java.lang.reflect.Method 代表类的方法
  • java.lang.reflect.Field 代表类的成员变量
  • java.lang.reflect.Constructor 代表类的构造器
获得反射对象
package reflection;

public class Reflection0 {

    public static void main(String[] args) throws ClassNotFoundException {
        //  通过反射来获取类的class对象
        Class cl1 = Class.forName("reflection.User");
        // Class cl2 = Class.forName("reflection.Reflection0");
        Class cl2 = User.class;
        // Class cl3 = Class.forName("reflection.Reflection0");
        Class cl3 = (new User()).getClass();

        // 一个类在内存中只有一个Class对象
        // 一个类被加载后,类的整个结构都会被封装在Class对象中
        System.out.println(cl1.hashCode());
        System.out.println(cl2.hashCode());
        System.out.println(cl3.hashCode());

    }

}


// pojo:

class User{
    String name;
    int age;
    int id;

    public User() {
    }

    public User(String name, int age, int id) {
        this.name = name;
        this.age = age;
        this.id = id;
    }

//    getter setter方法
}

一切类的父类 Object 中的 getClass() 方法返回值即为Java反射的源头,所以可以通过对象反射来求出类的名称。

任何一个对象都有一个自己的class对象,提供了该类的属性、方法、构造器、实现的接口。且对象仅能通过系统创建、在JVM中只有一个Class实例、一个Class对象对应一个加载到JVM中的.class文件、每个类的实例都记着自己是由那个Class对象生成、通过Class对象可以完整的获取到一个类被加载的结构、Class对象是Reflection的根源,获取相应的Class对象后才能动态加载和运行某个类

Class类的常用方法:

那些类型可以有Class对象:
  • class :外部类、成员、局部内部类、匿名内部类
  • interface:
  • []
  • enum
  • annotation
  • primitive type: 基本数据类型
  • void

只要元素类型和维度一致,class就相同

类的初始化

只有在 类的主动引用 会发生类的初始化,如:

  • main
  • new
  • 调用非常量的静态资源
  • reflect包中的反射调用
  • 当初始化一个类时,父类未被初始化会先初始化父类

类的被动引用 不会发生类的初始化, 如果:

  • 访问静态域,仅真正声明该域的类会初始化(通过子类调用父类的静态变量,子类不初始化)
  • 数组引用
  • 引用常量(常量在链接阶段就已存入到调用类的 常量池中了)

类加载器的作用

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

加载的类会进行类缓存

类加载器的类型:

  • 引导类加载器:C++编写,JVM自带加载器,负责Java平台核心库 用来装载核心类库。该加载器无法直接获取
  • 扩展类加载器:负责jre/lib/ext下的jar包或者 -D java.ext.dirs 指定的jar包装入工作库
  • 系统类加载器:负责 java -classpath-D java.class.path 所指目录下的类与jar包装入工作,是最常用的加载器

查看方法:

package reflection;

public class ClassLoaderTest {
    public static void main(String[] args) throws ClassNotFoundException {
        // 获取 系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        // 获取 系统类加载器的父类加载器: 扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);

        // 获取 扩展类的父类加载器:根加载器:引导类加载器(C/C++)
        ClassLoader parentParent = parent.getParent();
        System.out.println(parentParent);


        // 测试当前类是哪个加载器加载的
        ClassLoader classLoader = Class.forName("reflection.ClassLoaderTest").getClassLoader();
        System.out.println(classLoader);
        // 显然,该类是用户自定义类,故为系统类加载器加载

        // 测试JDK内置类是哪个加载器加载的
        ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader1);
        // 显然是核心加载的

        // 系统类加载器可以加载的路径:
        System.out.println(System.getProperty("java.class.path"));
        /*
            D:\JavaDevelopmentKit8\jre\lib\charsets.jar;
            D:\JavaDevelopmentKit8\jre\lib\deploy.jar;
            ...
            D:\JavaDevelopmentKit8\jre\lib\rt.jar;
            G:\learning\spring\annotation&reflection\target\classes;
            D:\JetBrains\IntelliJ IDEA 2020.1.3\lib\idea_rt.jar
         */
        
        // 其次 双亲委派机制 可以保证类的加载的安全有效性
    }
}

sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1b6d3586
null
sun.misc.Launcher$AppClassLoader@18b4aac2
null

通过反射来操作对象:(实例化、调用方法、访问属性等

package reflection;

import pojo.User;

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

/**
 * date: 2021/3/19 14:51
 * author: 31415926535x
 */

// 动态创建对象
public class DynamicInstance {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        // 通过包名路径获得Class对象
        Class cl = Class.forName("pojo.User");

        // 使用newInstance()构造对象
        // 本质是调用类的无参构造器
        User user = (User)cl.newInstance();
        System.out.println(user);

        System.out.println("=================");

        // 通过构造器创建对象

        Constructor constructor = cl.getConstructor(String.class, int.class, int.class);
        user = (User)constructor.newInstance("2333", 233, 666);
        System.out.println(user);

        System.out.println("=================");

        // 通过反射调用普通方法
        // 即 method.invoke(对象, 方法的值) 调用普通方法
        User user2 = (User)cl.newInstance();
        cl.getDeclaredMethod("setName", String.class).invoke(user2, "emmmm");
        System.out.println(user2);

        System.out.println("=================");

        // 通过反射操作属性
        // private属性不能访问
        // 可以通过设置setAccessible为true
        // 参数值为true指示反射的对象在使用时应该取消Java语言访问检查,可以使得提高反射的效率,并能访问私有成员
        
        User user3 = (User)cl.newInstance();
        try {
            cl.getDeclaredField("name").set(user3, "user3");
            System.out.println(user3);
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("不能直接修改private属性");
        }

        System.out.println("=================");

        Field name = cl.getDeclaredField("name");
        name.setAccessible(true);
        name.set(user3, "user3");
        System.out.println(user3);

    }

}
User{name='null', age=0, id=0}
=================
User{name='2333', age=233, id=666}
=================
User{name='emmmm', age=0, id=0}
=================
不能直接修改private属性
=================
User{name='user3', age=0, id=0}
java.lang.IllegalAccessException: Class reflection.DynamicInstance can not access a member of class pojo.User with modifiers ""
	at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
	at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
	at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
	at java.lang.reflect.Field.set(Field.java:761)
	at reflection.DynamicInstance.main(DynamicInstance.java:49)
效率分析

理论上,直接new实例化 > 关闭检测setAcessible(true) > 不关闭的反射方式

package reflection;

import pojo.User;

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

/**
 * date: 2021/3/19 15:18
 * author: 31415926535x
 */
public class SetAccessibleTest {

    // 普通方式调用
    public static void test1(){
        User user = new User();
        long st = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            user.getName();
        }
        long et = System.currentTimeMillis();
        System.out.println("普通方式调用: " + (et - st) + "ms");
    }


    // 反射方式调用
    public static void test2() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user = new User();
        Class cl = Class.forName("pojo.User");
        Method getName = cl.getDeclaredMethod("getName", null);

        long st = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            getName.invoke(user, null);
        }
        long et = System.currentTimeMillis();
        System.out.println("反射方式调用: " + (et - st) + "ms");
    }

    // 关闭检测

    public static void test3() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user = new User();
        Class cl = Class.forName("pojo.User");
        Method getName = cl.getDeclaredMethod("getName", null);
        getName.setAccessible(true);

        long st = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            getName.invoke(user, null);
        }
        long et = System.currentTimeMillis();
        System.out.println("反射方式调用: " + (et - st) + "ms");
    }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        test1();
        test2();
        test3();
    }
}
普通方式调用: 0ms
反射方式调用: 171ms
反射方式调用 关闭检测: 125ms

获取泛型信息

对于反射获取到的一个方法method,可以通过 getGenericParameterTypes等方法来获取到参数、返回值等泛型数据的信息。(泛型数据信息会在编译器被丢弃,故可以通过此方法来重新获取到)

通过反射对象来获取注解信息

通过反射获取的class对象可以获取其身上所标注的注解的值,通过此即可达到使用注解来设定某些对象的属性值,即各种框架中注解的执行机制:

package reflection;

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

/**
 * date: 2021/3/19 15:34
 * author: 31415926535x
 */

// 反射操作注解
public class AnnotationAndReflection {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class cl = Class.forName("reflection.Student");

        // 通过反射的方式获取注解
        Annotation[] annotations = cl.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }

        // 获取注解的值
        TableClassAnnotation tableClassAnnotation = (TableClassAnnotation) cl.getAnnotation(TableClassAnnotation.class);
        System.out.println(tableClassAnnotation.value());
        // 这里的操作即可解释各种框架中的注解所设定的 value 值的作用机制了
        // 即,通过设定注解的值,如 Controller(value="./static") 即制定了Controller这个注解对象的值
        // 相关框架即可通过以上方式获取值,进而实现指定注解值来设置相关类的某个(类、属性)的值


        // 获得类指定的注解
        Field name = cl.getDeclaredField("name");
        TableFieldAnnotation annotation = name.getAnnotation(TableFieldAnnotation.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
    }


}


// 自定义注解

// 类名注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableClassAnnotation{
    String value();
}

// 属性的注解
// 可以理解为对应数据库中每个字段的属性
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface TableFieldAnnotation{
    String columnName();
    String type();
    int length();
}
@TableClassAnnotation("db_studnet")
class Student{
    @TableFieldAnnotation(columnName = "db_name", type = "varchar", length = 20)
    private String name;
    @TableFieldAnnotation(columnName = "db_age", type = "int", length = 10)
    private int age;
    @TableFieldAnnotation(columnName = "db_id", type = "int", length = 10)
    private int id;



    public Student() {
    }

    public Student(String name, int age, int id) {

        this.name = name;
        this.age = age;
        this.id = id;
    }

//     get set tostring方法:
}
@reflection.TableClassAnnotation(value=db_studnet)
db_studnet
db_name
varchar
20
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值