Java反射机制

得意时要看淡,失意时要看开。不论得意失意,切莫大意;不论成功失败,切莫止步。志得意满时,需要的是淡然,给自己留一条退路;失意落魄时,需要的是泰然,给自己觅一条出路Java反射机制,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

反射

Java 反射机制是 Java 语言的一个重要特性。在学习 Java 反射机制前,先介绍两个概念,编译期和运行期。

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

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

Java 反射机制是在运行状态中,通过 Java 的反射机制,可以控制程序的运行过程。例如通过类名,动态获取类拥有的构造方法、属性和类方法,调用任意类的任意方法。这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。

在这里插入图片描述

JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class的所有信息。这种通过Class实例获取class信息的方法称为反射(Reflection)。

Java 反射机制主要提供了以下功能,这些功能都位于java.lang.reflect包。

  • Constructor类:提供类的构造方法信息。
  • Field类:提供类或接口中成员变量信息。
  • Method类:提供类或接口成员方法信息。
  • Array类:提供了动态创建和访问 Java 数组的方法。
  • Modifier类:提供类和成员访问修饰符信息。

要想知道一个类的属性和方法,必须先获取到该类的字节码文件对象。获取类的信息时,使用的就是 Class类中的方法。所以先要获取到每一个字节码文件(.class)对应的 Class 类型的对象。

Class类

Class类是由JVM在执行过程中动态加载的。对于任意Object类,当JVM加载Object类时,它首先读取Object.class文件到内存,然后,为Object类创建一个Class实例。JVM在第一次读取到一种Class类型时,将其加载进内存。

java.lang.Class类是实现反射的关键所在,Class 类的一个实例表示 Java 的一种数据类型,包括类、接口、枚举、注解(Annotation)、数组、基本数据类型和 void。Class 没有公有的构造方法,Class 实例是由 JVM 在类加载时自动创建的。

每一种类型包括类和接口等,都有一个 class 静态变量可以获得 Class 实例。另外,每一个对象都有 getClass() 静态方法可以获得 Class 实例,该方法是由 Object 类提供的实例方法。Class类还提供了forNmae()静态方法通过类的全限定名获取Class类。

三种获取Class类方法

  1. 通过类名.class静态变量获取Class类

Class class=[类名].class

public class Test {
    Class studentClass=Student.class;

    public static void main(String[] args) {
        System.out.println("获取Student类的类名:"+new Test().studentClass.getName());
        System.out.println("获取Student类的包名:"+new Test().studentClass.getPackage());
    }

}

在这里插入图片描述

  1. 通过实例对象的getClass()静态方法获取Class类
String string =new String();      
Class  stringClass=string.getClass();
class TestOne{
    private Student student=new Student();
    Class studentClass=student.getClass();

}

public class Test {
    public static void main(String[] args) {
        System.out.println("获取Student类的类名:"+new TestOne().studentClass.getName());
        System.out.println("获取Student类的包名:"+new TestOne().studentClass.getPackage());
    }

}
  1. 通过全类名为参数的的静态方法Class.forName()获取Class类
class TestThree{
    TestThree() throws ClassNotFoundException {
        Class studentClass=Class.forName("com.company.reflecttion.Student");
    }
}
public class Test {
    public static void main(String[] args) throws ClassNotFoundException {

        System.out.println("获取Student类的类名:"+new TestThree().getClass().getName());
        System.out.println("获取Student类的包名:"+new TestThree().getClass().getPackage());
    }

}

反射获取的都是同一实例,实例在JVM中是唯一的,只有new才会为新实例分配内存空间。

instanceof判断类型,instanceof不但匹配指定类型,还匹配指定类型的子类。而用“`==“判断class实例可以精确地判断数据类型,但不能作子类型比较。

public class TaskOne {
    public static void main(String[] args) throws ClassNotFoundException {
        Student student=new Student();
        Class class1=Student.class;
        Class class2=student.getClass();
        Class class3=Class.forName("com.company.reflecttion.Student");

        System.out.println(class1==class2 && class1==class3 && class2==class3);
        
    }
}
//结果:
true

访问Class对象的信息

对任意的一个Object实例,只要我们获取了它的Class,就可以获取它的一切信息。在这里插入图片描述

Field是 Java 编程语言中类的一个成员,主要用来存储对象的状态,有时也可称为成员字段或成员变量。
Package是Java的一个存放包路径的对象。

获取成员变量

  • Field getField(String name):根据字段名获取某个public的Field类
  • Field getDeclaredField(String name):根据字段名获取当前类的某个Field类
  • Field[] getFields():获取所有public的Field类
  • Field[] getDeclaredFields():获取当前类的所有Fieldl类
    在这里插入图片描述
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class clazz=Class.forName("com.company.reflecttion.School");
        Field field=clazz.getField("schoolName");
        System.out.println(field);

    }

/*结果
public java.lang.String com.company.reflecttion.School.schoolName
返回的是public修饰的 schoolName的成员变量
*/


public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
    Class clazz=Class.forName("com.company.reflecttion.School");
    Field[] fields=clazz.getFields();
    System.out.println(fields);

}

/*结果
[Ljava.lang.reflect.Field;@6d6f6e28
返回结果是所有public修饰的成员变量,返回的是地址
*/

一个Field对象包含了一个字段的所有信息
在这里插入图片描述

如果成员变量是private私有修饰,需要忽略权限:
在这里插入图片描述
将setAccessible()方法参数设为true。出现IllegalAccessException错误就是访问权限错误。

  • getName():返回字段名称;
  • getType():返回字段类型,也是一个Class实例,例如,String.class;
  • getModifiers():返回字段的修饰符,它是一个int,不同的数字表示不同的含义。
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class clazz=Class.forName("com.company.reflecttion.School");
        Field field=clazz.getField("schoolName");
        System.out.println(field.getName());
        System.out.println(field.getType());
        System.out.println(field.getModifiers());

    }
/*结果
schoolName
class java.lang.String
1
*/    

获取实例字段的值

想要获取对象成员变量的值,必须有实例,不能通过静态方法Class.forName类名.class获取。

通过Class获取Field就可以得到成员变量的全部信息,但实际得到变量的值的意义更大。如何通过Field获取实例成变量的值呢?

  • get(Object obj)获取实例对象的成员变量的值
  • set(Object obj,Object value)修改实例对象的成员变量的值
  • setAccessible(boolean flag)解除私有访问权限

实例作为参数,get,set方法获取实例的成员变量的值。所以参数为类的实例。

 public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        School school=new School();
        Class schoolClass=school.getClass();
        Field field=schoolClass.getField("phone");
        System.out.println("获取的Filed成员为:"+field);
        System.out.println("Filed实例的值为:"+field.get(school)); //参数为School的实例化对象
        field.set(school,987654321);
        System.out.println("修改实例对象的值为987654321后的值"+field.get(school));
    }

在这里插入图片描述

//School类
package com.company.reflecttion;

public class School {
    public String schoolName="某某学校";
    public int phone=123456789;

    private String idCard;
    private int core;

    private String IdCardSet(){
        return this.idCard="0000000";
    }

    private int coreSet(){
        return this.core=900000;
    }

    public String getIdCard(){
        return IdCardSet();
    }
    public int getCore(){
        return coreSet();
    }
}

访问构造方法

为了能够动态获取对象构造方法的信息,首先需要通过下列方法之一创建一个 Constructor 类型的对象或者数组。

  • getConstructors()获取修饰词为public的所有构造方法
  • getConstructor(Class<?>…parameterTypes)获取public修饰的某个构造方法
  • getDeclaredConstructors()获取所有构造方法
  • getDeclaredConstructor(Class<?>...parameterTypes)获取某个构造方法

在这里插入图片描述

有参构造方法的参数为Class<T> ... paramterTyes表示形参的Class类,如String.class

实例化这些类构造一个构造方法对象存储实例的构造方法信息。创建的每个 Constructor 对象表示一个实例的构造方法,然后利用 Constructor 对象的方法操作实例的构造方法。
在这里插入图片描述
调用Class.newInstance()的局限是,它只能调用该类的public参数构造方法。如果构造方法带有参数,或者不是public,就无法直接通过Class.newInstance()来调用。

为了调用任意的构造方法,Java的反射API提供了Constructor对象,它包含一个构造方法的所有信息,可以创建一个实例。Constructor对象和Method非常类似,不同之处仅在于它是一个构造方法。

Constructor类的方法的形参是构造方法参数的Class类(字节码文件),没有就不写。newInstance()方法用于构造方法的实例化,形参是实际要传输的参数。

例如:构造方法的Person(String name,int age)的形参是Stringint,那么getConstructors()方法的参数就为String的Class类,int的Class类即Constructorconstructor=Person.class.getConstructors(String.class,int.class)
newInstance()方法中传递参数如:constructor.newInstance("张三",20)

//创建类
public class SchoolEx extends School{
    public String name="李华";
    private String studentId="000000";

    SchoolEx(){
        System.out.println(this.name+"-----"+this.studentId);
    }

    SchoolEx(String name,String studentId){
        System.out.println(name+"-----"+studentId);
    }

}

//获取并调用构造方法
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取构造方法
        Constructor constructor=SchoolEx.class.getDeclaredConstructor();
        //调用构造方法
        constructor.newInstance();

        Constructor constructor1=SchoolEx.class.getDeclaredConstructor(String.class,String.class);
        constructor1.newInstance("张三","111111");

    }
/*
获取构造方法随着newInstance()的实例化自动调用
*/

在这里插入图片描述

访问方法

通过Class实例可以获取所有Field对象,同样的,java.lang.reflect.Method类可以通过Class实例获取所有Method信息即成员方法的全部信息。

Class类提供了以下几个方法来获取Method:

  • getMethod(String name,Class<?> …parameterTypes)获取某个public修饰的Method
  • getMethods()获取所有public修饰的Method
  • getDeclaredMethod(String name,Class<?> …parameterTypes)获取某个Method
  • getDeclaredMethods()获取所有Method

在这里插入图片描述

有参数的方法中第一个参数是方法名,第二个参数是形参的Class类。

在这里插入图片描述
invoke
在这里插入图片描述

Method类中有众多方法获取从Class类获取的方法的信息,其中最重要的是invoke(Object obj,Object agrs)方法,该方法的作用是实例方法的实例化(调用实例方法)。第一个参数是实例对象,第二个参数是形参的Class类(字节码文件类)。

对Method实例调用invoke就相当于调用该方法,invoke的第一个参数是对象实例,即在哪个实例上调用该方法,后面的可变参数要与方法参数一致。如果获取到的Method表示一个静态方法,调用静态方法时,由于无需指定实例对象,所以invoke方法传入的第一个参数永远为null。

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    Person person=new Person();
    //获取public修饰的无参方法
    Method method=person.getClass().getMethod("getName");
    //调用无参反方法
    System.out.println(method.invoke(person));

    //获取private修饰的有参方法
    Method method1=person.getClass().getDeclaredMethod("setName", String.class);
    //解除private限制
    method1.setAccessible(true);
    method1.invoke(person,"张三");
    //调用有参方法
    System.out.println(method.invoke(person));
}

在这里插入图片描述

需要注意的是通过Class类构造Method对象时getMethod(String name,Object Class<T> parameterTypes)第一个参数时方法名为String类型,第二个参数是参数的Class类对象。

在这里插入图片描述

调用将得到一个IllegalAccessException。为了调用非public方法,我们通过Method.setAccessible(true)允许其调用。

访问修饰符

通过 java.lang.reflect.Modifier 类可以解析出 getMocMers()方法的返回值所表示的修饰符信息。在该类中提供了一系列用来解析的静态方法,既可以查看是否被指定的修饰符修饰,还可以字符串的形式获得所有修饰符。
在这里插入图片描述
每个反射中都有一个getMocMers()方法,但是该方法返回的是一个int类型,并不知道修饰符到底是publicdefaultprivateprotected或类的修饰符等。
在这里插入图片描述
Modifier类提供了众多方法来辨别修饰符类型,返回值是boolean类型。如1public那么:

public static void main(String[] args) {
     System.out.println(Modifier.isPublic(1));
}
//结果:
true

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/156293.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!