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

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

  • 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对象有三种获取方式:

  1. Class.forName(“全类名”)
    • 将字节码文件加载进内存,返回Class对象。
    • 多用于配置文件,将类名定义在配置文件中,读取文件,加载类。
  2. 类名.class
    • 通过类名的属性class获取。
    • 多用于参数的传递。
  3. 对象.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);
    }
}

运行结果:

img

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");
    }
}

运行结果:

img

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

A minor

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值