Java反射原理,内存图

Java的反射是一个很抽象的概念

Java当中的User user = new User(),可以被称为从类到对象;

那么反射,大致可以理解为,由对象到类

我们所有的类信息,都是通过Class类的一个对象调用方法获得的,所以被叫做由对象到类

反射是什么,反射可以得到什么

反射机制,可以通俗理解为,一种能够获取类信息的能力

在Java源码中,反射是由java.lang.reflect.*包当中,所有类和方法实现的

类信息 = 类本身的信息+成员变量信息+成员方法信息+构造器信息+父类信息

也就是说,我们通过反射能拿到一个类的全部的这些信息

反射的原理

*.java文件是java源代码,存储在硬盘当中;
经过javac命令转化为*.class字节码文件;
再经过java命令,变成class类对象,存储在方法区中(方法区存储类信息

在这里插入图片描述

如果这个类在程序运行过程中还创建了自己的对象,那么堆中还会存储这个类的对象

在这里插入图片描述

一个.java文件就大致被分为了这三个阶段:硬盘存储阶段,class类对象阶段,java对象阶段

回到我们最开始对反射的定义:获取类信息的能力,

现在所有的类信息都被存储在class类对象当中,我们只需要通过反射,

①从java对象反射到class类对象

②从硬盘中.java的包名 + 类名(实际上也是硬盘中,.java文件存储的路径,只有硬盘存储才能拿到这些信息)反射到class类对象

就可以拿到全部的类信息

在这里插入图片描述

这也是获取Class对象的3种方式
在这里插入图片描述
需要注意的是,同一个类的Class对象,在整个方法区中只存在一份,上述3种方式反射拿到的类对象,其实指向同一块内存地址

反射的使用

类信息包括:类相关信息+构造器相关信息+方法相关信息+变量相关信息

User类

package entity;

import lombok.*;

@Setter
@Getter
public class User extends Human {

    public String name;

    private String password;

    Integer age;

    protected Boolean isDead;

    public User() {

    }

    public User(String name, String password, Integer age, Boolean isDead) {
        this.name = name;
        this.password = password;
        this.age = age;
        this.isDead = isDead;
    }

    @Override
    public void canWalk() {
        System.out.println("User can walk, too!");
    }

    void run() {
        System.out.println("User can run!");
    }

    protected void sing() {
        System.out.println("User can sing!");
    }

    private void dance() {
        System.out.println("User can dance!");
    }


}

父类Human

package entity;

import lombok.Getter;
import lombok.Setter;

@Setter
@Getter
public class Human implements Walkable {

    private Integer height;

    Integer weight;

    protected String faceColor;

    public String address;

    public Human() {
    }

    public Human(Integer height, Integer weight, String faceColor, String address) {
        this.height = height;
        this.weight = weight;
        this.faceColor = faceColor;
        this.address = address;
    }


    @Override
    public void canWalk() {
        System.out.println("Human can walk!");
    }
}

父类的接口Walkable

package entity;

public interface Walkable {

    void canWalk();
}

类相关信息

        System.out.println("======================类的相关信息");
        Class user1 = Class.forName("entity.User");
        User criteria = new User();
        Class user2 = criteria.getClass();
        
        Class userSuper = user1.getSuperclass();
        String modifiers = Modifier.toString(user1.getModifiers());

        if (modifiers.length() > 0) System.out.println("类修饰符:" + modifiers);
        System.out.println("全限定类名:" + user1.getName());
        if (userSuper != null && userSuper != Object.class) {
            System.out.println("继承自:" + userSuper.getName());
        }
        Class<?>[] interfaces = userSuper.getInterfaces();
        for (Class<?> inter : interfaces) {
            System.out.println("类的实现接口名" + inter.getName());
        }

在这里插入图片描述

构造器相关的信息


		System.out.println("======================构造器的相关信息");
        Constructor[] userConstruct = user1.getDeclaredConstructors();
        Constructor[] humanConstruct = userSuper.getDeclaredConstructors();

        for (Constructor c1 : userConstruct) {
            String name = c1.getName();
            System.out.println("User构造器名称" + name);
            String mod = Modifier.toString(c1.getModifiers());
            if (!modifiers.isEmpty())
                System.out.println("User构造器修饰符:" + mod);
            Class[] paramTypes = c1.getParameterTypes();
            for (int j = 0; j < paramTypes.length; j++) {
                System.out.println("User构造器的第" + (j + 1) + "个参数,类型:" + paramTypes[j].getName());
            }

        }

        for (Constructor c1 : humanConstruct) {
            String name = c1.getName();
            System.out.println("Human构造器名称" + name);
            String mod = Modifier.toString(c1.getModifiers());
            if (!modifiers.isEmpty())
                System.out.println("Human构造器修饰符:" + mod);
            Class[] paramTypes = c1.getParameterTypes();
            for (int j = 0; j < paramTypes.length; j++) {
                System.out.println("Human构造器的第" + (j + 1) + "个参数,类型:" + paramTypes[j].getName());
            }

        }

在这里插入图片描述

方法相关信息

 // 这里的方法虽然四种修饰符的方法都能取到,但是private不能invoke运行,必须设置setAccessible(true)才可以
 System.out.println("======================方法的相关信息");
        Method[] methods = user1.getDeclaredMethods();
        Method dance = user1.getDeclaredMethod("dance", null);
        User instance = (User) user1.newInstance();
        dance.setAccessible(true);
        dance.invoke(instance, null);
        for (Method m : methods) {
            Class retType = m.getReturnType();
            String name = m.getName();
            String mod2 = Modifier.toString(m.getModifiers());
            System.out.println("方法修饰符:" + mod2 + ",方法名:" + name + ",返回值类型:" + retType);
            Class[] paramTypes = m.getParameterTypes();
            for (int j = 0; j < paramTypes.length; j++) {
                System.out.println(name + "方法第" + (j + 1) + "个参数名:" + paramTypes[j].getName());
            }
        }

在这里插入图片描述

变量相关信息

	System.out.println("======================变量的相关信息");
	Field[] fields = cl.getDeclaredFields();
        for(Field f:fields){
            Class type = f.getType();
            String name = f.getName();
            System.out.print("    ");
            String modifiers = Modifier.toString(f.getModifiers());
            if(modifiers.length()>0){
                System.out.print(modifiers+" ");
            }
            System.out.println(type.getName()+" "+name+";");
        }
    }

在这里插入图片描述

变量和方法一样,如果想要使用private修饰的变量,就需要加上field.setAccessible(true)

其他细节

使用field.set()方法给变量赋值的时候,为什么需要传一个Object作为参数呢?

        Field password = user1.getDeclaredField("password");
        password.setAccessible(true);
        User forPassword = new User();
        password.set(forPassword, "222222");

因为要想给一个类的非static变量赋值,这个类的变量,必须是储存在对象当中的,储存在JVM的堆内存中,

非static变量的值不可能是空中楼阁,脱离对象而存在。所以这里的set方法,必须先建一个对象,作为参数传入,再传一个值参数作为赋值

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值