java反射机制

Posted _瞳孔

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java反射机制相关的知识,希望对你有一定的参考价值。

一:反射的基本认识

在java开发中有一个非常重要的概念就是java反射机制,也是java的重要特征之一。反射的概念是由Smith在1982年首次提出的,在学习 Java 反射机制前,应该先了解两个概念,编译期运行期

编译期是指把源码交给编译器编译成计算机可以执行的文件的过程。在 Java 中也就是把 Java 代码编成 class 文件的过程。编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作,比如检查错误。

运行期是把编译后的文件交给计算机执行,直到程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来。

Java 反射机制主要是指程序可以访问、检测和修改它本身状态或行为的一种能力,通过反射可以调用私有方法和私有属性。对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。在 Java 中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。

Java 反射机制在服务器程序和中间件程序中得到了广泛运用。在服务器端,往往需要根据客户的请求,动态调用某一个对象的特定方法。此外,在 ORM 中间件的实现中,运用 Java 反射机制可以读取任意一个 JavaBean 的所有属性,或者给这些属性赋值。

反射机制主要提供以下功能:

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法;
  • 在运行时调用任意一个对象的方法;
  • 生成动态代理。

Java 反射机制的优缺点

优点:

  • 能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性。
  • 与 Java 动态编译相结合,可以实现无比强大的功能。
  • 对于 Java 这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。

缺点:

  • 反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;
  • 反射调用方法时可以忽略权限检查,获取这个类的私有方法和属性,因此可能会破坏类的封装性而导致安全问题。
反射可访问的常用信息
类型 访问方法 返回值类型 说明
包路径 getPackage() Package 对象 获取该类的存放路径
类名称 getName() String 对象 获取该类的名称
继承类 getSuperclass() Class 对象 获取该类继承的类
实现接口 getlnterfaces() Class 型数组 获取该类实现的所有接口
构造方法 getConstructors() Constructor 型数组 获取所有权限为 public 的构造方法
getDeclaredContruectors() Constructor 对象 获取当前对象的所有构造方法
方法 getMethods() Methods 型数组 获取所有权限为 public 的方法
getDeclaredMethods() Methods 对象 获取当前对象的所有方法
成员变量 getFields() Field 型数组 获取所有权限为 public 的成员变量
getDeclareFileds() Field 对象 获取当前对象的所有成员变量
内部类 getClasses() Class 型数组 获取所有权限为 public 的内部类
getDeclaredClasses() Class 型数组 获取所有内部类
内部类的声明类 getDeclaringClass() Class 对象 如果该类为内部类,则返回它的成员类,否则返回 null

二:Class类

java.lang.Class类是实现反射的核心类

  • Class类只能由系统建立对象
  • 一个加载的类在内存(JVM)中只有一个Class对象
  • 一个类被加载后,类的整个结构都会被封装在Class对象中
  • 每个类的实例都会记得自己是由哪个Class实例所生成
  • Class类是反射的根源,针对任何你想动态加载,运行的类,唯有获得相应的Class对象
package reflection;

public class Test 
    public static void main(String[] args) throws ClassNotFoundException 
        // 通过反射获取类的class对象
        Class<?> c1 = Class.forName("reflection.User");
        System.out.println(c1);

        Class<?> c2 = Class.forName("reflection.User");
        Class<?> c3 = Class.forName("reflection.User");

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


// 实体类
class User 


Class类的常用方法:

方法名功能说明
static ClssforName(String name)返回指定类名namedeClass对象
Obiect newInstance()调用无参构造函数,返回Class对象的一个实例
getName()返回此Class对象所表示的实体(类,接口,数组类或void)的名称
Class getSuperClass()返回当前Class对象的父类的Class对象
Class[] getinterfaces()返回当前Class对象的接口
ClassLoader getClassLoader()返回该类的类加载器
Method getMethod(name,String .class)返回对象Method一个数组,此对象的形参类型为paramType
Field getDeclaredFields()返回对象的Field(属性)一个数组
Constructor[] getConstructors()返回一个包含某些Constructor(构造器)对象的数组

以下为五种获取class类的方式:

package reflection;

public class Test 
    public static void main(String[] args) throws ClassNotFoundException 
        Person person = new Student();
        System.out.println(person.name);

        //方式一:调用Class类的静态方法
        Class<?> c1 = Class.forName("reflection.Student");
        System.out.println(c1.hashCode());

        //方式二 已知某个类的实例,调用该实例的getClass()方法,getClass是Object类中的方法、因为所有类都继承Object类。
        Class<? extends Person> c2 = person.getClass();
        System.out.println(c2.hashCode());

        //方式三 已知具体类,通过类的class属性获取,该方法最安全可靠,程序性能最高
        Class<Student> c3 = Student.class;
        System.out.println(c3.hashCode());

        //方式四:通过基本内置类型的包装类的TYPE属性获得CLass实例
        //以int的包装类Integer类为例   源码:public static final Class<Integer>  TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
        Class<Integer> c4 = Integer.TYPE;
        System.out.println(c4);

        //方式五:通过当前子类的Class对象获得父类的Class对象
        Class<?> c5 = c1.getSuperclass();//c1为子类的CLass对象
        System.out.println(c5);
    


// 实体类
class Person 
    public String name;

    public Person() 

    public Person(String name) 
        this.name = name;
    


class Student extends Person 
    public Student() 
        this.name = "学生123";
    


class Teacher extends Person 
    public Teacher() 
        this.name = "老师";
    

所有类型的Class对象:

package reflection;

import java.lang.annotation.ElementType;

public class Test 
    public static void main(String[] args) throws ClassNotFoundException 
        Class<Object> c1 = Object.class;  // 类
        Class<Comparable> c2 = Comparable.class; // 接口
        Class<String[]> c3 = String[].class;  // 一维数组
        Class<int[][]> c4 = int[][].class;  // 二维数组
        Class<Override> c5 = Override.class;  // 注解
        Class<ElementType> c6 = ElementType.class;  // 枚举
        Class<Integer> c7 = Integer.class;  // 基本数据类型
        Class<Void> c8 = void.class;  // void
        Class<Class> c9 = Class.class;  // 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);
    


三:类的内存分析


  • 加载:将class字节码文件内容加载到内存中,并将这些数据装换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象
  • 链接:将Java类的二进制代码合并到JVM的运行状态之中的过程
    • 验证:确保加载的类信息符合JVM规范,没有安全方面的问题
    • 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法去中进行分配
    • 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
  • 初始化:
    • 执行类构造器()方法的过程。类构造器()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。
    • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
    • 虚拟机会保证一个类的在多线程环境中被正确加锁和同步。

四:反射获取注解信息

所有 Java 注解类型都继承于 java.lang.annotation.Annotation 接口,该接口代表程序中可以接收注解的程序元素。该接口主要有如下几个实现类。

  • Class:类定义。
  • Constructor:构造方法定义。
  • Field:类的成员变量定义。
  • Method:类的方法定义。
  • Package:类的包定义。

java.lang.reflect 包下主要包含一些实现反射功能的工具类,从 Java 5 开始,java.lang.reflect 包所提供的反射 API 增加了读取运行时注解的能力。只有当定义注解时使用了 @Retention(RetentionPolicy.RUNTIME) 修饰,该注解才会在运行时可见。

AnnotatedElement 接口是所有程序元素(如 Class、Method、Constructor 等)的父接口,所以程序通过反射获取了某个类的 AnnotatedElement 对象(如 Class、Method、 Constructor 等)之后,程序就可以调用该对象的如下几个方法来访问注解信息,方法名称及作用如下表所示。

方法名 作用
<A extends Annotation> A getAnnotation(Class<A> annotationClass) 如果该元素存在 annotationClass 类型的注解,则返回注解,否则返回 null
<A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass) 这是 Java 8 新增的方法,该方法尝试获取直接修饰该程序元素、指定类型的注解。如果该类型的注解不存在,则返回 null
Annotation[] getAnnotations() 返回该元素上存在的所有注解
Annotation[] getDeclaredAnnotations() 返回直接存在于该元素的所有注解(和 getAnnotations() 的区别在于该方法将不返回继承的注释)
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 判断该元素上是否存在 annotationClass 类型的注解,如果存在则返回 true,否则返回 false。
<A extends Annotation> A[] getAnnotationsByType(Class<A> annotationClass) 该方法与前面介绍的 getAnnotation() 方法基本相似。但由于 Java 8 增加了重复注解功能,因此需要使用该方法获取该元素存在 annotationClass 类型的多个注解。
<A extends Annotation> A[] getDeclaredAnnotationsByType(Class<A> annotationClass) 该方法与前面介绍的 getDeclaredAnnotations() 方法基本相似。但由于 Java 8 增加了重复注解功能,因此需要使用该方法获取该元素存在 annotationClass 类型的多个注解。

如果有兴趣了解更多相关内容,欢迎来我的个人网站看看:瞳孔的个人空间

以上是关于java反射机制的主要内容,如果未能解决你的问题,请参考以下文章

Java反射机制用反射改进简单工厂模式设计

Java反射与类加载过程会擦出什么样的火花

Java反射与类加载过程会擦出什么样的火花

Java基础java类加载过程与反射机制

类加载机制与反射

如何利用java的反射机制动态创建对象