【JVM】堆(Heap)上有什么?Class对象到反射

it2026-02-26  4

在文章开头,我们先看看反射是什么?

Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法 可以简单理解为,在运行状态下,控制台输入一个(全)类名就可以得到一个该类对象,或者获取到该类的信息可以实现类的动态加载对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。 而这也是Java被视为动态或准动态语言的一个关键性质。(为啥要说是准动态,因为一般而言的动态语言定义是程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。)

既然反射这么nb,那么Java是怎么实现反射的呢?答:依靠Class对象。

Java有两种对象,第一种是实例对象,第二种是Class对象,每一个类运行的类型信息就是用Class对象表示的,每一个对象都有一个到 java.lang.Class(用于描述对象的结构)的实例的引用。

Class类没有公共的构造方法,Class对象是在类加载的时候由Java虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。

1.如何获取Class对象?

Class对象有三种获取方式:

Class.forName(“全类名”) 将字节码文件加载进内存,返回Class对象。多用于配置文件,将类名定义在配置文件中,读取文件,加载类。 类名.class 通过类名的属性class获取。多用于参数的传递。 对象.getClass() getclass方法在object类中定义。多用与对象的获取字节码的方式。

这里再强调一次,同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,所以无论通过哪一种方式获取的class对象都是一个。三种方式的代码示例如下:

public class Demo { public static void main(String[] args) throws ClassNotFoundException { // 1.通过Class.forName获取Class对象 Class aClass = Class.forName("com.xupt.yzh.entity.Persion"); // 2.通过类名.class获取Class对象 Class bClass = Persion.class; // 3.通过对象.getClass获取Class对象 Persion persion = new Persion(); Class cClass = persion.getClass(); Persion persion1= new Persion(); Class dClass = persion.getClass(); persion1.getClass(); System.out.println(persion==persion1); // false System.out.println(aClass==bClass); // true System.out.println(aClass==dClass); // true System.out.println(aClass==cClass); // true System.out.println(cClass==dClass); // true } }

2.Class对象有什么用?

Class中的方法有很多,但是最常用的就三种:获取字段(Filed对象),获取构造器(Constructor对象),获取方法(Method对象)。下面就通过代码示例来逐个说明…

// Person的Class对象是后面示例的主角 public class Person { public String name; private int age; // 两个构造方法,带参和空参 public Person(String name, int age) { this.name = name; this.age = age; } public Person() { } // get、set public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } // 两个额外方法 public void eat(){ System.out.println("eat...."); } public void hello(String a){ System.out.println("say"+ a); } }

可以看到Person有两个成员变量,还有一个是private私有的,但是在反射面前,没有什么私有共有之分,向下看!

3.1 获取Filed,然后设置值

// Class获取Filed对象: Field[] getFields() // 获取public的所有成员变量 Field getField(String name) // 根据属性名获取指定Field Field[] getDeclaredFields() // 获取private的所有成员变量 Field getDeclaredField(String name) // 根据属性名获取指定private的Field --------------------------------------------------------------------- // Filed的核心方法: Object get(Object obj) // 获取指定对象(obj)当前Filed的值 void set(Object obj, Object value) // 设置指定对象(obj)当前Filed为value void setAccessible(boolean flag) // 在对private变量操作前,必须设置Accessible为true(该方法在父类AccessibleObject中)

示例代码如下:

public class TestField { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException{ // 通过类名拿到Class对象 Class p = Person.class; // Class#getFields:获取public 字段Filed[] Field[] fields = p.getFields(); for (Field f:fields) { System.out.println(f); } System.out.println("-------------------------"); // Class#getField(name): 获取指定字段Field Field field = p.getField("name"); Person person = new Person(); // Field#get(Obj):获取指定对象的当前字段值 Object name = field.get(person); System.out.println(name); // Field#set(obj,value):设置一个对象的当前字段值 field.set(person,"zhangsan"); System.out.println(person); // Class#getDeclaredFields:获取所有成员变量,不考虑修饰符Field[] Field[] declaredFields = p.getDeclaredFields(); for (Field field1:declaredFields) { System.out.println(field1); } System.out.println("-------------------------"); // Class#getDeclaredField(name):获取指定private字段Field Field field1 = p.getDeclaredField("age"); // Filed#setAccessible:暴力反射,在对private字段操作前必须设置Accessible为true field1.setAccessible(true); Person person1 =new Person(); Object o = field1.get(person1); System.out.println(o); } }

运行结果:

3.2 获取Constructor ,然后构造新对象

// Class获取Constructor对象 Constructor<?>[] getConstructors() // 获取所有public构造器 Constructor<T> getConstructor(Class<?>... parameterTypes) // 根据参数返回public构造器 Constructor<?>[] getDeclaredConstructors() // 获取所有private构造器 Constructor<?>[] getDeclaredConstructors() // 根据参数返回private构造器 T newInstance() // 通过空参构造创建一个对象(注:类中必须要有空参构造) ------------------------------------------------------------------------------------------------------ // Constructor的核心方法: T newInstance(Object ... initargs) // 通过Constructor构造一个对象(注:可变参数,所以可以空参构造时不用传参数)

示例代码:

public class TestConstructor{ public static void main(String[] args) throws Exception { // 通过对象获取Class对象 Class personClass = Person.class; // Class#getConstructor(Class paramType...):获取指定构造器Constructor,要传入参数类型的Class对象 Constructor constructor = personClass.getConstructor(String.class, int.class); System.out.println(constructor); // Constructor#newInstance(param):用构造方法创建一个对象 Object asd = constructor.newInstance("asd", 10); System.out.println(asd); // Class#newInstance:空参构造方法创建对象 Object o = personClass.newInstance(); System.out.println(o); } }

运行结果:

3.3 获取Method,然后执行

// Class获取Method对象: Method[] getMethods() // 获取所有public方法 Method getMethod(String name, Class<?>... parameterTypes) // 根据方法名和类型获取public方法(注:只传入类型或方法名并不能唯一确定一个参数) Method[] getDeclaredMethods() // 获取所有private方法 Method getDeclaredMethod(String name, Class<?>... parameterTypes) // 根据方法名和类型获取private方法 ------------------------------------------------------------------------------------------ // Method的核心方法: Object invoke(Object obj, Object... args) // 执行方法,传入执行的对象(obj)和参数(可变参数),返回执行结果

示例代码:

public class TestMethod{ public static void main(String[] args) throws Exception { // 获取Class对象 Class<Person> personClass = Person.class; // Class#getMethod(name):同过方法名获取方法Method Method method = personClass.getMethod("eat"); Person person = new Person(); // Method#invoke(obj):执行方法 method.invoke(person); System.out.println("----------------"); Method hello = personClass.getMethod("hello", String.class); // Method#invoke(obj,param):执行方法,并传入参数 hello.invoke(person,"hello"); } }

运行结果:

最新回复(0)