栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JavaSE - 反射

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JavaSE - 反射

JavaSE - 反射

本节学习目标:

  • 了解Java反射的概念与机制;
  • 了解并掌握Class类的概念与获取Class实例的方法;
  • 了解并掌握创建运行时类的对象的方法;
  • 了解并掌握运行时类的对象的成员的获取与调用的方法;
1. Java 反射概述 1.1 Java 反射简介

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。

JAVA反射机制 - 百度百科

反射(Reflection)是被视为动态语言的关键,反射机制允许程序在执行期间借助Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性和方法。

类加载完毕后,在堆内存的方法区中就产生了一个和这个类对应的Class类型的对象(一个类只有一个Class对象),Class对象包含了对应类完整的内部信息,用户可以使用Class对象获取类的任何成员(甚至私有成员)。

  • 动态语言:在运行时可以改变其结构的语言。可以加入新的函数,新的对象,甚至可以引入新的代码。如Object-C、C#、Javascript、PHP、Python等语言。
  • 静态语言:与动态语言相对的,在运行时不可改变其结构的语言。如Java、C、C++等语言。

Java语言不是动态语言,但是Java语言的反射机制让其拥有一定的动态性。用户可以利用反射机制、字节码操作等让Java代码获得类似动态语言的特性。所以Java语言可以称之为“准动态语言”。

1.2 Java 反射结构

与反射有关的类都在java.lang.reflect包下,除了Class类在java.lang包下:

全限定类名说明
java.lang.Class描述一个结构(类,接口等)
java.lang.reflect.Constructor描述类的构造方法
java.lang.reflect.Field描述结构的属性(成员属性或静态属性)
java.lang.reflect.Method描述结构的方法(成员方法或静态方法)
java.lang.reflect.Modifier描述修饰符(修饰类、属性或方法的关键字,如public,final等)
java.lang.reflect.Parameter描述方法的参数
2. Class 类

Class类位于java.lang包下。它可以看做是加载到Java虚拟机中每个结构(包括类,接口,数组,甚至基本数据类型)的类。加载到Java虚拟机中的每一个类,都可以看做是Class类的一个实例。
通过一个类的Class实例,我们可以访问这个类的内部信息。

为了限制Class实例指代的类型,Java使用泛型T来指定某个类的Class实例。

2.1 Class 实例的获取方式

Class类由于其特殊性,Java不允许直接使用new关键字创建Class实例(其唯一的构造方法被private关键字修饰)。

Java中获取Class实例主要有四种方式:

  1. 使用类的类字面常量(.class);
    • 此方式需要显式调用具体某个类,需要用户明确知道此类,如果此类不存在将无法通过编译。
Class stringClass1 = String.class;
  1. 调用类的对象的getClass()方法;
    • 和第一种方式相似,需要借助某个类的对象获取此类的Class实例,但无需用户明确知道此类。
Class stringClass2 = new String().getClass();
  1. 调用Class类提供的静态方法forName(String className):
    • 参数className为要获取Class实例的类的全限定类名;
    • 如果指定的类不存在,则抛出ClassNotFoundException异常;
    • 常用方式,无需确认类也无需对象,在运行时才会确认加载,体现了动态性。
Class stringClass3 = Class.forName("java.lang.String");
  1. 使用类加载器(ClassLoader)获取Class实例:
    • 不常用,仅做了解。
ClassLoader classLoader = Test.class.getClassLoader();
Class stringClass4 = classLoader.loadClass("java.lang.String");

对上述四种方式进行测试:

System.out.println(stringClass1);
System.out.println(stringClass2);
System.out.println(stringClass3);
System.out.println(stringClass4);
System.out.println(stringClass1 == stringClass2);
System.out.println(stringClass2 == stringClass3);
System.out.println(stringClass3 == stringClass4);
System.out.println(stringClass4 == stringClass1);


通过运行结果可知,上述四种方式获取的都是同一个String类的Class实例。

2.2 对 Class 类的理解

Java源码经过javac命令编译后,会生成一个或多个字节码文件(.class)。接着使用java命令解释运行某个字节码文件时,Java虚拟机将会把这个字节码文件加载进内存中。加载到内存中的类被称为运行时类(Runtime class),每个运行时类就作为Class类的一个实例。

不是只有类(class关键字)才有对应的Class实例,像接口(注解)、枚举类、内部类(包括局部内部类和匿名内部类)、数组、甚至是基本数据类型和void关键字等结构都有它们对应的Class实例:

class A {
    static class B {
    }
}
interface C {
}
enum D {
}
public class Test {
    public static void main(String[] args) {
        System.out.println(A.class);
        System.out.println(A.B.class);
        System.out.println(C.class);
        System.out.println(D.class);
        System.out.println(int[].class);
        System.out.println(Override.class);
        System.out.println(int.class);
        System.out.println(void.class);
    }
}


当运行某个字节码文件时,由Java虚拟机(或通过类加载器的defineClass()方法)自动创建对应的Class实例,对于每个结构只会创建一个对应的Class实例。
所以使用四种获取方式获取的都是同一个Class实例。

2.3 描述运行时类的相关方法

Class类提供了以下方法获取运行时类的相关信息:

3. 反射访问运行时类及其对象

编写JavaBean类Person(为了进行演示,这里不按照规范编写JavaBean):

public class Person {
    private String name;
    public int age;
    public Person() {
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // 省略成员变量的getter和setter方法
    private String toWork() {
        return "Person去工作";
    }
    public void work() {
        System.out.println(toWork());
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + ''' +
                ", age=" + age +
                '}';
    }
}

编写JavaBean类Student,并继承Person类:

public class Student extends Person {
    public int stuID;
    String tel;
    private String address;
    private Student() {
        super();
    }
    public Student(String name, int age, int stuID, String tel, String address) {
        super(name, age);
        this.stuID = stuID;
        this.tel = tel;
        this.address = address;
    }
    // 省略成员变量的getter和setter方法
    protected String toStudy() {
        return "Student去学习";
    }
    @Override
    public void work() {
        System.out.println(toStudy());
    }
}
3.1 访问构造方法(创建对象)

对于无参构造方法,可以直接使用Class类提供的newInstance()方法获取运行时类的对象:

Class personClass = Person.class;
Person person = personClass.newInstance();
System.out.println(person);


使用Class实例的newInstance()方法的要求:

  • 运行时类必须是一个类(class),像接口则不能直接实例化;
  • 运行时类必须提供无参构造方法,且无参构造方法必须被public关键字修饰。

对于有参构造方法,可以使用Class类提供的下列方法获取运行时类的构造方法(Constructor):

方法返回值功能
getConstructor(Class... parameterTypes)Constructor获取运行时类的含有指定参数的构造方法
(仅被public关键字修饰)
getConstructors()Constructor[]获取运行时类的所有构造方法
(仅被public关键字修饰)
getDeclaredConstructor(Class... parameterTypes)Constructor获取运行时类的含有指定参数的构造方法
getDeclaredConstructors()Constructor[]获取运行时类的所有构造方法

Constructor类位于java.lang.reflect包下,用于描述运行时类的构造方法,它使用泛型T来限制对应的运行时类。它的常用方法:

方法返回值功能
getModifiers()int获取构造方法的权限修饰符
getName()String获取构造方法的标识符
getParameterCount()int获取构造方法的参数个数
getParameterTypes()Class[]获取构造方法的参数列表
newInstance(Object ... initargs)T使用此构造方法创建运行时类的一个实例,
initargs为传入构造方法的参数

编写代码进行测试:

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.Arrays;
public class Test {
    public static void main(String[] args) throws Exception {
        Class personClass = Person.class;
        System.out.println(Arrays.toString(personClass.getConstructors()));
        System.out.println(Arrays.toString(personClass.getDeclaredConstructors()));
        Constructor personConstructor = personClass.getDeclaredConstructor(String.class, int.class);
        System.out.println(personConstructor);
        System.out.println(Modifier.toString(personConstructor.getModifiers()));
        System.out.println(personConstructor.getName());
        System.out.println(Arrays.toString(personConstructor.getParameterTypes()));
        System.out.println(personConstructor.newInstance("张三", 28));
    }
}


3.2 访问成员属性及其属性

Class类提供以下方法获取运行时类的属性(Field):

方法返回值功能
getField(String name)Field获取运行时类及其父类的指定属性(仅被public关键字修饰)
getFields()Field[]获取运行时类及其父类的全部属性(仅被public关键字修饰)
getDeclaredField(String name)Field获取运行时类的指定属性
getDeclaredFields()Field[]获取运行时类的全部属性

Field类位于java.lang.reflect包下,用于描述结构中的属性(成员变量,静态变量与常量)。它的常用方法:

方法返回值功能
getModifiers()int获取该属性的权限修饰符
getName()String获取该属性的标识符
getType()Class获取该属性的数据类型
get(Object obj)Object获取运行时类的某个对象obj的此属性的值
set(Object obj, Object value)void将指定值value赋给运行时类的某个对象obj的此属性

编写代码进行测试:

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class Test {
    public static void main(String[] args) throws Exception {
        Class studentClass = Student.class;
        showFieldInfo(studentClass.getFields());
        System.out.println("--------");
        showFieldInfo(studentClass.getDeclaredFields());
        Constructor studentConstructor = studentClass.getDeclaredConstructor();
        studentConstructor.setAccessible(true);
        Student student = studentConstructor.newInstance();
        Field stuID = studentClass.getField("stuID");
        stuID.set(student, 1001);
        System.out.println(stuID.get(student));
        System.out.println(student.toString());
    }
    private static void showFieldInfo(Field[] fields) {
        for (Field field : fields) {
            System.out.print(Modifier.toString(field.getModifiers()) + " ");
            System.out.print(field.getType() + " ");
            System.out.print(field.getName());
            System.out.println();
        }
    }
}


3.3 访问成员方法及其属性

Class类提供以下方法获取运行时类的方法(Method):

方法返回值功能
getMethod(String name, Class... parameterTypes)Method获取运行时类及其父类的指定名称指定参数的方法
(仅被public关键字修饰)
getMethods()Method[]获取运行时类及其父类的所有方法
(仅被public关键字修饰)
getDeclaredMethod(String name, Class... parameterTypes)Method[]获取运行时类的指定名称指定参数的方法
getDeclaredMethods()Method获取运行时类的所有方法

Method类位于java.lang.reflect包下,用于描述结构中的方法,它的常用方法:

方法返回值功能
getModifiers()int获取该方法的权限修饰符
getName()String获取该方法的标识符
getReturnType()Class获取该方法的返回值类型
getParameterTypes()Class[]获取该方法的所有参数类型
getParameterCount()int获取该方法的参数个数
getExceptionTypes()Class[]获取该方法抛出的所有异常类型
invoke(Object obj, Object... args)Object传入参数args并执行运行时类的某个对象obj的此方法

编写代码进行测试:

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
public class Test {
    public static void main(String[] args) throws Exception {
        Class studentClass = Student.class;
        showMethodInfo(studentClass.getMethods());
        System.out.println("-------");
        showMethodInfo(studentClass.getDeclaredMethods());
        Constructor studentConstructor = studentClass.getDeclaredConstructor();
        studentConstructor.setAccessible(true);
        Student student = studentConstructor.newInstance();
        Method work = studentClass.getDeclaredMethod("work", String.class);
        Method toWork = studentClass.getDeclaredMethod("toStudy");
        String str = (String) toWork.invoke(student);
        work.invoke(student, str);
    }
    private static void showMethodInfo(Method[] methods) {
        for (Method method : methods) {
            System.out.print(Modifier.toString(method.getModifiers()) + " ");
            System.out.print(method.getReturnType() + " ");
            System.out.print(method.getName() + " ");
            System.out.print(Arrays.toString(method.getParameterTypes()) + " ");
            System.out.print(Arrays.toString(method.getExceptionTypes()) + " ");
            System.out.println();
        }
    }
}

运行结果:

public class java.lang.String toString [] [] 
public class java.lang.String getAddress [] [] 
public void work [class java.lang.String] [] 
public class java.lang.String getTel [] [] 
public int getStuID [] [] 
public void setTel [class java.lang.String] [] 
public void setStuID [int] [] 
public void setAddress [class java.lang.String] [] 
public class java.lang.String getName [] [] 
public void setName [class java.lang.String] [] 
public int getAge [] [] 
public void setAge [int] [] 
public final void wait [long, int] [class java.lang.InterruptedException] 
public final native void wait [long] [class java.lang.InterruptedException] 
public final void wait [] [class java.lang.InterruptedException] 
public boolean equals [class java.lang.Object] [] 
public native int hashCode [] [] 
public final native class java.lang.Class getClass [] [] 
public final native void notify [] [] 
public final native void notifyAll [] [] 
-------
public class java.lang.String toString [] [] 
public class java.lang.String getAddress [] [] 
public void work [class java.lang.String] [] 
public class java.lang.String getTel [] [] 
protected class java.lang.String toStudy [] [] 
public int getStuID [] [] 
public void setTel [class java.lang.String] [] 
public void setStuID [int] [] 
public void setAddress [class java.lang.String] [] 
Student去学习
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/605532.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号