java反射机制Reflection浅析

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.


以上的总结就是什么是反射 反射就是把java类中的各种成分映射成一个个的Java对象 例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。      (其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述) 如图是类的正常加载过程:反射的原理在与class对象。 熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。

目录

Reflection 反射

反射的定义

反射机制:

  • 在程序运行时可以加载,探知,使用编译期间完全未知的类
  • 程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个加载的类,都能够知道这个类的所有属性和方法;同时任意一个对象也都能够调用它的任意一个方法和属性。同时一个类只有一个反射对象,比如多次调用Class.forName得到的也是相同对象

形象的说:

使用以下反射代码来获取一个类的对象
Class c = Class.forName("com.company.project.ClassName");
当加载完成后,堆内存中就产生了一个ClassName类型的对象,通过这个对象我们就可以看到类的结构,这个就时反射

反射的作用

  • 动态加载类、动态获取类的信息(属性、方法、构造器)
  • 动态构造对象
  • 动态调用类和对象的任意方法、构造器
  • 动态调用和处理属性
  • 获取泛型信息
  • 处理注解

Class类

public final class Class

Class类的类表示正在运行的Java应用程序中的类和接口。 枚举是一种类,一个注解是一种接口。 每个数组也属于一个反映为类对象的类,该对象由具有相同元素类型和维数的所有数组共享。 原始Java类型( boolean , byte , char , short , int , long , float和double ),和关键字void也表示为类对象。Class没有公共构造函数。 相反, Class对象由Java虚拟机自动构建,因为加载了类,并且通过调用类加载器中的defineClass方法。

简单的说就是:

  • Class类十分特殊,用来表示java中类型 (class/interface/enum/annotation/primitive type/void)本身。
  • Class类的对象包含了某个被加载类的结构。一个被加载的类对应一个 Class对象
  • JVM来创建Class对象,当加载器(class loader)的defineClass()被 JVM调用,JVM 便自动产生一个Class 对象
  • Class类是Reflection反射的基础,任何要动态加载,运行的类,都必须先获得对于的Class对象

Class类的对象获取

  • .getClass()方法
  • Class.forName()方法–使用最多
  • .class语法
package JavaCore.Reflection;

/*******************************************************************************
 * @Description: 获取Class对象
 * @Aha-eureka:  Class类是Reflection反射的基础,任何要动态加载,运行的类,都必须先获得对于的Class对象
 ******************************************************************************/
public class Reflection_GetClass {

    public static void main( String[] args ) throws ClassNotFoundException {
        String path = "JavaCore.Reflection.User_Demo";

        //获取Class的几种方式
        {
            //一个类只有一个Class对象
            Class clazz1 = Class.forName(path);
            Class clazz11=Class.forName(path);

            System.out.println(clazz1.hashCode());//相同hashcode
            System.out.println(clazz11.hashCode());//相同hashcode
        }

        {
            User_Demo user_demo = new User_Demo();
            Class clazz2 = user_demo.getClass();

            Class clazz3 = User_Demo.class;

            System.out.println(clazz2 == clazz3);//true
        }
    }
}

获取基本数据类型的Class对象

Class类可以直接获取基本数据类型的Class对象,但是在数组中,多维的数组就是不同的class对象,不同类型的数组也是不同的class对象

    int[] arr0 = new int[5];
    int[] arr01 = new int[15];
    long[] arr02 = new long[15];
    int[][] arr1 = new int[5][5];

    System.out.println(arr0.getClass() + "   " + arr0.getClass().hashCode());//class [I   920011586
    System.out.println(arr01.getClass() + "   " + arr01.getClass().hashCode());//class [I   920011586
    System.out.println(arr02.getClass() + "   " + arr02.getClass().hashCode());//class [J   2017354584
    System.out.println(arr1.getClass() + "   " + arr1.getClass().hashCode());//class [[I   2017354584

Class API

方法含义备注
public String getName()返回由类对象表示的实体的名称(类,接口,数组类,原始类型或void),作为String返回由类对象表示的实体的名称(类,接口,数组类,原始类型或void),作为String
public Field getField(String name)获取指定属性名的属性注意只能获得public属性
public Field[] getFields()获取所有的公共方法的属性数组
public Field getDeclaredField(String name)返回指定的名字的属性包括私有属性
public Field[] getDeclaredFields()返回所有的属性数组包括私有的
同理…Method/Constructor
同理…DeclaredMethods/Constructors
public Mehod getDeclaredMethod(String name,Class<?>…parameterTypes返回指定参数类型的方法,Class<?>…parameterTypes可变参数参入的是对于类型的Class类针对方法的多态性(重载)进行针对的获取,即对于方法有传参则必须传入对于的Class对象
public Constructor获取指定传参的构造器
package JavaCore.Reflection;

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

public class Reflection_Dynamic {

    static Class clazz;
    static{
        String path = "JavaCore.Reflection.User_Demo";
        try {
            clazz=Class.forName(path);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void useReflection() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        //这里newInstance实际就是在调用对于类的无参构造器,如果对于的类没有无参构造器则会抛出InstantiationException
        User_Demo demo = (User_Demo) clazz.newInstance();

        //使用特定的构造器新建对象
        Constructor constructor = User_Demo.class.getDeclaredConstructor(String.class, int.class);
        User_Demo demo1 = (User_Demo) constructor.newInstance("Swagger", 26);
        System.out.println(demo1);

        /**
         * 使用反射调用普通方法
         * 1.先获取到Class对象,并获取一个新的对象A
         * 2.然后通过Class对象获得方法对象Method
         * 3.使用Method.invoke(Object A,args...)方法调用类的方法
         */
        User_Demo A = (User_Demo) clazz.newInstance();
        Method method = clazz.getDeclaredMethod("setName", String.class);
        method.setAccessible(true);
        method.invoke(A, "Swagger1");
        System.out.println(A);

        /**
         * 通过反射操作属性
         *
         */
        User_Demo fDemo = (User_Demo) clazz.newInstance();
        Field f = clazz.getDeclaredField("name");
        f.setAccessible(true);//使用这个setAccessible(true)来设置可以访问私有方法和属性
        f.set(fDemo, "Ranger");//
        System.out.println(fDemo);
    }
}

反射操作泛型

Java采用泛型擦除的机制来引入泛型。Java中的泛型仅仅是给编译器javac使用的,确保数据 的安全性和免去强制类型转换的麻烦。但是,一旦编译完成,所有的和泛型有关的类型全部 擦除。
为了通过反射操作这些类型以迎合实际开发的需要,Java就新增了ParameterizedType, GenericArrayType,TypeVariable 和WildcardType几种类型来代表不能被归一到Class 类中的类型但是又和原始类型齐名的类型

反射操作注解

public static void main(String[] args) {
        try {
            Class clazz = Class.forName("类的相对路径和类名");
            
            //获得类的所有有效注解
            Annotation[] annotations=clazz.getAnnotations();
            for (Annotation a : annotations) {
                System.out.println(a);
            }
            //获得类的指定的注解SxtTable就是一个注解
            SxtTable st = (SxtTable) clazz.getAnnotation(SxtTable.class);
            System.out.println(st.value());
            
            //获得类的属性的注解,columnName(),type(),length()就是SxtTable第一的注解方法或者属性,因为注解就是一种特殊的接口,他的属性值就是调用其方法
            Field f = clazz.getDeclaredField("studentName");
            SxtField sxtField = f.getAnnotation(SxtField.class);
            System.out.println(sxtField.columnName()+"--"+sxtField.type()+"--"+sxtField.length());
            
            //根据获得的表名、字段的信息,拼出DDL语句,然后,使用JDBC执行这个SQL,在数据库中生成相关的表
            
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }

反射的性能问题

反射访问相对正常的访问大概要慢30倍

setAccessible

setAccessible设为true:取消反射时的对象安全检查,false则进行安全检查;
当禁止安全检查时就能:
1.访问私有属性
2.同时也能提高反射的运行速度

如果setAccessible(true)禁用安全检查则能将反射访问效率提升4倍,所以当频繁的调用反射时可以禁用安全检查来提升效率

作者:

喜欢围棋和编程。

 
发布于 分类 编程标签

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注