• Java反射详解,还有什么理由学不会


    简述

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

    反射基础

    RTTI(Run-Time Type Indentification) 运行时类型识别,就是在运行时识别使用一个对象/类的信息,不同于编译时已经确定好的类型

    反射就是将一个类中的各种属性和方法等解剖为一个个对象

    Class类

    Class(java.lang)类 Java应用里每一个类或接口都是Class类的实例,每个Java类在jvm都表现为一个Class对象,基本数据类型和void数组也是Class类的对象

    1. public final class Class implements java.io.Serializable,
    2. GenericDeclaration,
    3. Type,
    4. AnnotatedElement {
    5. private static final int ANNOTATION= 0x00002000;
    6. private static final int ENUM = 0x00004000;
    7. private static final int SYNTHETIC = 0x00001000;
    8. private static native void registerNatives();
    9. static {
    10. registerNatives();
    11. }
    12. /*
    13. * Private constructor. Only the Java Virtual Machine creates Class objects.
    14. * This constructor is not used and prevents the default constructor being
    15. * generated.
    16. */
    17. private Class(ClassLoader loader) {
    18. // Initialize final field for classLoader. The initialization value of non-null
    19. // prevents future JIT optimizations from assuming this final field is null.
    20. classLoader = loader;
    21. }
    22. 复制代码

    阅读源码可得:

    • Class也是类,区别于class关键字
    • Class类构造方法是private,仅由JVM调用创建和加载

    类加载

    类加载机制图解

    • Java文件编译后会生成对应的class文件,里面记录着类一切的信息,jvm创建类都是依据Class对象来创建的
    • class文件在加载到内存中后,会生成唯一的Class对象,一个类全局仅存一个对应的Class对象

    反射的基本使用

    在Java中,Class类和java.lang.reflect类库共同支持反射技术

    其中,Constructor类表示Class对象所表示类的构造方法,Field类表示成员变量,Method类表示成员方法等

    方法介绍

    方法说明
    forName()(1)获取Class对象的一个引用,但引用的类还没有加载(该类的第一个对象没有生成)就加载了这个类。
    (2)为了产生Class引用,forName()立即就进行了初始化。
    Object-getClass()获取Class对象的一个引用,返回表示该对象的实际类型的Class引用。
    getName()取全限定的类名(包括包名),即类的完整名字。
    getSimpleName()获取类名(不包括包名)
    getCanonicalName()获取全限定规范 的类名(包括包名,一般在log时使用)
    isXXX()判断是否为某一类型,比如isInterface()是否为接口,isEnum()等
    getXXX()获取类的某一个信息,比如getFiled(String name)获取名字为name的属性,getMethod(),getAnnotation()等
    getXXXs()获取某个信息的数组,比如getInterfaces(),getMethods,getFileds() 注意:后两个方法会获取到包括父类所拥有的全部信息
    getDeclredXXX/s()获取已经声明的信息,仅包含本类,不包含父类 ,比如:getDeclaredAnnotations() ,getDeclaredMethod()
    newInstance()返回一个Oject对象,是实现“虚拟构造器”的一种途径。使用该方法创建的类,必须带有无参的构造器

    Class对象的读取

    在类加载的时候,JVM会创建一个class对象 class对象是可以说是反射中最常用的,获取class对象的方式的主要有三种

    • 根据类名:类名.class
    • 根据对象:对象.getClass()
    • 根据全限定类名:Class.forName(全限定类名)
      tips:如果是加载内部类,格式为package.ClassName$InnerClass
    1. logger.info("根据类名: \t"+test.class); //根据类名: class zjamss.test
    2. logger.info("根据对象: \t"+this.getClass()); //根据对象: class zjamss.test
    3. logger.info("根据全限定类名: \t"+Class.forName("zjamss.test")); //根据全限定类名: class zjamss.test
    4. Class test = zjamss.test.class;
    5. // 常用的方法
    6. logger.info("获取全限定类名:\t" + test.getName()); //获取全限定类名: zjamss.test
    7. logger.info("获取类名:\t" + test.getSimpleName()); //获取类名: test
    8. logger.info("实例化:\t" + test.newInstance()); //实例化: zjamss.test@17d10166
    9. 复制代码

    Construct类及使用方法

    Constructor类存在于反射包(java.lang.reflect)中,反映的是Class 对象所表示的类的构造方法

    1. //Constructor使用
    2. @Test
    3. public void t2() throws Exception {
    4. Class clazz = Class.forName("zjamss.reflect.User");
    5. User user = (User) clazz.newInstance(); //调用无参构造函数
    6. System.out.println(user); //User{name='null', age=0}
    7. Constructor cs1 = clazz.getConstructor(int.class);
    8. User user1 = (User) cs1.newInstance(10); //调用一个参数的构造函数
    9. System.out.println(user1); //User{name='null', age=10}
    10. Constructor cs2 = clazz.getDeclaredConstructor(String.class,int.class);
    11. User user2 = (User) cs2.newInstance("12", 12); //调用两个参数的构造函数
    12. System.out.println(user2); //User{name='12', age=12}
    13. Constructor[] constructors = clazz.getDeclaredConstructors(); //获取User类里所有的构造方法
    14. for(Constructor constructor : constructors){
    15. System.out.print(constructor.toGenericString+":\t");
    16. Class[] parameterTypes = constructor.getParameterTypes(); //获取构造方法的所有形参类型
    17. for(Class param : parameterTypes){
    18. System.out.print(param.getCanonicalName()+"\t");
    19. }
    20. System.out.println();
    21. }
    22. }
    23. //public zjamss.reflect.User(int): int
    24. //public zjamss.reflect.User(java.lang.String,int): java.lang.String int
    25. //public zjamss.reflect.User():
    26. 复制代码
    1. public class User{
    2. private String name;
    3. public int age;
    4. public User() {
    5. }
    6. public User(String name, int age) {
    7. this.name = name;
    8. this.age = age;
    9. }
    10. public User(int age) {
    11. this.age = age;
    12. }
    13. @Override
    14. public String toString() {
    15. return "User{" +
    16. "name='" + name + '\'' +
    17. ", age=" + age +
    18. '}';
    19. }
    20. }
    21. 复制代码

    Field类及其用法

    Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类静态字段或实例字段

    1. //Field
    2. @Test
    3. public void t3() throws Exception{
    4. Class clazz = User.class;
    5. //获取指定字段名称且修饰符为public的字段 包括父类
    6. Field age = clazz.getField("age");
    7. System.out.println("age: "+age); //age: public int zjamss.reflect.User.age
    8. //获取所有修饰符为public的字段 包括父类
    9. Field[] fields = clazz.getFields();
    10. for(Field field : fields){
    11. System.out.println(field.getName()+": "+field.getDeclaringClass());
    12. //age: class zjamss.reflect.User
    13. //score: class zjamss.reflect.User
    14. }
    15. //获取当前类所有字段
    16. Field[] declaredFields = clazz.getDeclaredFields();
    17. for(Field field : declaredFields){
    18. System.out.println(field.getName()+": "+field.getDeclaringClass());
    19. //name: class zjamss.reflect.User
    20. //tel: class zjamss.reflect.User
    21. //age: class zjamss.reflect.User
    22. //score: class zjamss.reflect.User
    23. }
    24. //获取当前类指定字段,任意修饰符都可
    25. Field name = clazz.getDeclaredField("name");
    26. System.out.println(name.getName()+": "+name); //name: private java.lang.String zjamss.reflect.User.name
    27. }
    28. 复制代码

    上述方法需要注意的是,如果我们不期望获取其父类的字段,则需使用Class类的getDeclaredField/getDeclaredFields方法来获取字段即可,倘若需要连带获取到父类的字段,那么请使用Class类的getField/getFields,但是也只能获取到public修饰的的字段,无法获取父类的私有字段。

    设置字段

    1. Class clazz = User.class;
    2. final Field name = clazz.getDeclaredField("name");
    3. final Constructor constructor = clazz.getConstructor(int.class);
    4. final User user = (User) constructor.newInstance(10);
    5. System.out.println(user); //User{name='null', age=10}
    6. name.setAccessible(true); //解除private
    7. name.set(user,"name");
    8. System.out.println(user); //User{name='name', age=10}
    9. System.out.println(name.get(user)); //name
    10. 复制代码

    Method类及其用法

    1. Class clazz = User.class;
    2. //获取所有public的方法,包括父类
    3. Method[] methods =clazz.getMethods();
    4. for (Method m:methods){
    5. System.out.println("m::"+m);
    6. }
    7. //m::public int zjamss.reflect.User.getAge()
    8. //m::public java.lang.String zjamss.reflect.User.toString()
    9. //m::public final void java.lang.Object.wait() throws java.lang.InterruptedException
    10. //m::public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
    11. //m::public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
    12. //m::public boolean java.lang.Object.equals(java.lang.Object)
    13. //m::public native int java.lang.Object.hashCode()
    14. //m::public final native java.lang.Class java.lang.Object.getClass()
    15. //m::public final native void java.lang.Object.notify()
    16. //m::public final native void java.lang.Object.notifyAll()
    17. //获取当前类的方法包含private,该方法无法获取继承自父类的method
    18. Method method1 = clazz.getDeclaredMethod("display");
    19. System.out.println("method1::"+method1); //method1::private void zjamss.reflect.User.display()
    20. method1.setAccessible(true); //修改访问权限
    21. method1.invoke(clazz.newInstance()); //调用方法 User{name='null', age=0} 12
    22. Method method2 = clazz.getDeclaredMethod("getAge");
    23. System.out.println("method2:"+method2); //method2:public int zjamss.reflect.User.getAge()
    24. int age = (int) method2.invoke(new User(12)); //获取方法返回值
    25. System.out.println(age);
    26. //获取当前类的所有方法包含private,该方法无法获取继承自父类的method
    27. Method[] methods1=clazz.getDeclaredMethods();
    28. for (Method m:methods1){
    29. System.out.println("m1::"+m);
    30. }
    31. //m1::private void zjamss.reflect.User.display()
    32. //m1::public int zjamss.reflect.User.getAge()
    33. //m1::public java.lang.String zjamss.reflect.User.toString()
    34. 复制代码

    反射的使用案例

    Spring框架想必大家都耳熟能详了,是一个提供IOC和AOP支持等等的框架,那里面的IOC又是怎么实现的呢?其实就是反射,比如最使用的@Autowired注解,自动类型注入

    简单来说,当spring扫描bean的时候,会获取一个bean的Class对象,然后获取所有的field,遍历查找属性上是否有@Autowired注解,如果有,则先设置Accessiletrue,接着就将字段赋值为对应的bean对象

  • 相关阅读:
    spring security(二)--授权
    anaconda安装tensorflow遇到的问题
    Shiro介绍及其功能
    防御第六次作业-笔记整理
    三维模型几何坐标偏差修正(纠正)的常用方法分析
    JS 会有变量提升和函数提升
    在MM32F5微控制器上使用外扩SRAM作为主内存
    React源码分析3-render阶段(穿插scheduler和reconciler)
    【Django 04】Serialization 序列化的高级使用
    线程状态及线程停止
  • 原文地址:https://blog.csdn.net/java_beautiful/article/details/126300204