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方法,必须先建一个对象,作为参数传入,再传一个值参数作为赋值