目录
5 反射
- 类信息:方法、变量、构造器、继承和实现的类或接口。
- 反射:反射是 Java 中一项强大的特性,它赋予了程序在运行时动态获取类的信息,并能够调用类的方法、访问类的字段以及操作构造函数等的能力。通过反射,我们可以突破编译期的限制,在运行时对类进行灵活的操作。
- 获取类信息的前提:生成类对象
- 类的生命周期与反射关联:
在 Java 中,类的生命周期有着明确的阶段。首先,我们编写的.java源文件通过javac命令进行编译,生成.class字节码文件。之后,类加载器会将.class文件加载到内存中,在元空间(Java 虚拟机专门为存储类的元数据等开辟的内存空间 )中生成类对象。而反射机制的前提就是生成类对象,只有获得了类对象,我们才能进一步获取类的详细信息。
- 元空间:内存专门为Java开辟的空间。
或
- 反射获取类信息的方式
从类对象出发,我们可以从多个维度获取类的信息:
变量相关:
- getDeclaredFields() :该方法能够获取类中所有权限修饰的成员变量,包括私有、受保护、包访问以及公共变量。
- getDeclaredField(name) :根据指定的变量名获取对应的成员变量,同样不受访问权限限制。
- getFields() :用于获取类中所有具有公共访问权限的成员变量。
- getField(name) :根据指定名称获取类中具有公共访问权限的成员变量。
对于私有变量,如果要进行访问和操作,需要通过 setAccessible(true) 进行暴力反射,绕过访问权限修饰符的安全检查,之后便可以利用 set() 方法设置变量值,利用 get() 方法获取变量值 。
方法相关:
- getDeclaredMethods() :可以获取类中所有权限修饰的成员方法。
- getDeclaredMethod(name) :根据指定的方法名获取对应的成员方法。
- getMethods() :获取类中所有具有公共访问权限的成员方法。
- getMethod(name) :根据指定名称获取类中具有公共访问权限的成员方法。
对于私有方法,同样先使用 setAccessible(true) 进行暴力反射,然后通过 invoke() 方法执行方法,也可以使用 getName() 方法获取方法名。
构造方法相关:
- getDeclaredConstructors() :获取类中所有权限修饰的构造方法。
- getDeclaredConstructor() :获取指定的无参构造方法。
- getDeclaredConstructor(xx.class) :获取指定参数类型的有参构造方法。
- getConstructors() :获取类中所有具有公共访问权限的构造方法。
对于私有构造方法,使用 setAccessible(true) 进行暴力反射后,可利用构造方法创建对象,例如通过 newInstance() 方法。如果类有无参构造方法,还可以通过类对象直接调用 class3.newInstance() 来创建实例。
获取类名称的方法
通过反射,还可以获取类的名称。getName() 方法会返回类的全限定名(包名 + 类名 ),而 getSimpleName() 方法则只返回类名本身。
- 实例代码
如下,一共有两个版本,一个是按照上面的思路,先生成类对象,在从变量、方法构造方法的角度分别解释。第二个版本则是按照反射获取类信息的步骤,按照周一步一步来。
- 第一个版本:
package com.qcby.反射;
public class Person {
private String name;
private int age;
public String country;
public int height;
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 Person(String name, int age, String country, int height) {
super();
this.name = name;
this.age = age;
this.country = country;
this.height = height;
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Person() {
super();
}
private Person(String country) {
super();
this.country = country;
}
private void run() {
System.out.println("跑跑跑...");
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", country=" + country + ", height=" + height + "]";
}
}
package com.qcby.反射;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws Exception {
//创建对象
Person person1 = new Person();
Person person2 = new Person("李四",30,"中国",170);
Person person3 = new Person("cici",35,"美国",160);
//反射类里面获取类信息的步骤
//第一步获取 类对象
//对象阶段获取类对象 对象名.getClass()
Person person = new Person();
Class class1 = person.getClass();
//元空间阶段获取类对象 类名.class
Class class2 = Person.class;
//硬盘阶段获取类对象 Class.forName(类全名) 包名+类名
Class class3 = Class.forName("com.qcby.反射.Person");
System.out.println(class1);//com.qcby.反射.Person
System.out.println(class2);//com.qcby.反射.Person
System.out.println(class3);//com.qcby.反射.Person
System.out.println(class1==class2);//true,因为同一个类在类加载器加载文件的时候,只会执行一次。true也说明了他们同时指向同一个内存空间。
System.out.println(class1==class3);//true
//第二步 通过类对象 获取类信息
//类信息存储的形式一般有两种,所以对于获取可以直接获取或者获取相关的集合
//获取变量
System.out.println("========所有访问权限的变量==========");
Field[] fields = class3.getDeclaredFields();
for(Field a:fields) {
System.out.println(a);
}
System.out.println("======指定变量============");
Field height = class3.getDeclaredField("height");
Field name = class3.getDeclaredField("name");
Field country = class3.getDeclaredField("country");
Field age = class3.getDeclaredField("age");
System.out.println(age);
System.out.println("======公共访问权限变量============");
Field[] fields1 = class3.getFields();
for(Field a:fields1) {
System.out.println(a);
}
System.out.println("======指定公共访问权限变量============");
Field height1 = class3.getField("height");//只可以获取public类型的变量,private不可以
//拿到变量后就可以设置值、获取值,如果变量是private权限,直接使用会抛出异常,需要先暴力反射
//通过set()和get()设置和获取值
//设置值
height.set(person1, 180);//name是private,如果通过这种方式会抛出异常,need先暴力反射
name.setAccessible(true);//暴力反射,忽略访问权限修饰符的安全检查
age.setAccessible(true);//暴力反射
name.set(person1, "张三");
System.out.println(person1);
//获取值
System.out.println(height.get(person2));
System.out.println(name.get(person2));
System.out.println(age.get(person2));
System.out.println(country.get(person2));
//获取方法
System.out.println("=========所有访问权限的方法==================");
Method[] methods = class3.getDeclaredMethods();
for(Method a:methods) {
System.out.println(a);
}
System.out.println("=========所有指定的方法==================");
Method run_method = class3.getDeclaredMethod("run");
System.out.println(run_method);
Method getName_method = class3.getDeclaredMethod("getName");
System.out.println(getName_method);
Method setAge_method = class3.getDeclaredMethod("setAge",int.class);//前面获取的方法无形参可以直接用函数名,但是setAge有形参,需要传参数,不然会抛出异常
System.out.println(setAge_method);
System.out.println("=========公共访问权限的方法==================");
Method[] methods1 = class3.getMethods();
for(Method a:methods1) {
System.out.println(a);
}
System.out.println("=========指定公共访问权限的方法==================");
Method getName_method1 = class3.getMethod("getName");
System.out.println(getName_method1);
Method setAge_method1 = class3.getMethod("setAge",int.class);
System.out.println(setAge_method1);
//拿到方法后就可以执行方法
Object name = getName_method.invoke(person3);
System.out.println(name);
setAge_method.invoke(person3, 50);//有行参的要传参
System.out.println(person3);
run_method.setAccessible(true);//private修饰的方法,要先暴力反射
run_method.invoke(person2);
System.out.println(run_method.getName());//获取方法名字,如果不知道方法名字要先获取,知道就可以直接获取方法
//获取构造方法
System.out.println("============获取所有构造方法==============");
Constructor[] constructors =class3.getDeclaredConstructors();
for(Constructor a:constructors) {
System.out.println(a);
}
System.out.println("============获取无参构造方法==============");
Constructor no_parameter = class3.getDeclaredConstructor();
System.out.println(no_parameter);
System.out.println("============获取有参构造方法==============");
Constructor have_two_parameter = class3.getDeclaredConstructor(String.class,int.class);
System.out.println(have_two_parameter);
Constructor have_one_parameter = class3.getDeclaredConstructor(String.class);
System.out.println(have_one_parameter);
System.out.println("============获取公共的构造方法==============");
Constructor[] constructors1 =class3.getConstructors();
for(Constructor a:constructors1) {
System.out.println(a);
}
//获取构造方法后就可以创建对象
Object person1 = no_parameter.newInstance();
System.out.println(person1);
Object person2 = have_two_parameter.newInstance("李华",21);
System.out.println(person2);
have_one_parameter.setAccessible(true);//private修饰的构造方法,需要先暴力反射
Object person3 = have_one_parameter.newInstance("美国");
System.out.println(person3);
//特例,无参构造方法可以通过类对象直接调用
Object person4 = class3.newInstance();
System.out.println(person4);
//获取名称
//System.out.println(class3.getName());//获取包名加类名
//System.out.println(class3.getSimpleName());//只获取类名
}
}
- 第二个版本
package com.qcby.反射;
public class Cat implements Jump,Run{
private int age;
public String name;
protected String color;
double height;
public Cat(int age,String name,String color,double height) {
this.age = age;
this.name = name;
this.color = color;
this.height = height;
}
private Cat() {
}
Cat(String color){
this.color = color;
}
public void run(String name,int age,double height) {
System.out.println("小猫的名字叫"+name);
}
public void run(String name) {
System.out.println("小猫的名字叫"+name);
}
private int setAge(int age) {
return age;
}
void fly() {
System.out.println("猫不会飞");
}
}
package com.qcby.反射;
public interface Jump {
}
package com.qcby.反射;
public interface Run {
}
package com.qcby.反射;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
public class Test2 {
public static void main(String[] args) throws Exception{
//获取类对象
Class clazz = Class.forName("com.qcby.反射.Cat");
Class clazz1 = Class.forName("com.qcby.反射.Cat");
Class clazz2 = Cat.class;
Cat cat = new Cat("黑色");
Class clazz3 = cat.getClass();
System.out.println(clazz1==clazz2);
System.out.println(clazz2==clazz3);
//说明clazz1、clazz2和clazz3指向同一个内存空间
System.out.println("---------------");
//变量
Field[] fields = clazz.getDeclaredFields();
System.out.println(Arrays.toString(fields));
Field ageFiled = clazz.getDeclaredField("age");
System.out.println(ageFiled);
Field nameFiled = clazz.getDeclaredField("name");
System.out.println(nameFiled);
Field colorFiled = clazz.getDeclaredField("color");
System.out.println(colorFiled);
Field heightFiled = clazz.getDeclaredField("height");
System.out.println(heightFiled);
Field[] fields1 = clazz.getFields();
System.out.println(Arrays.toString(fields1));
Field nameFiled1 = clazz.getField("name");
System.out.println(nameFiled);
//Field ageFiled1 = clazz.getField("age");
//System.out.println(ageFiled);
System.out.println("----------------");
//方法
Method[] methods = clazz.getDeclaredMethods();
System.out.println(Arrays.toString(methods));
Method[] methods1 = clazz.getMethods();
System.out.println(Arrays.toString(methods1));
Method runMethod1 = clazz.getDeclaredMethod("run", String.class);
System.out.println(runMethod1);
Method runMethod2 = clazz.getMethod("run",String.class);
System.out.println(runMethod2);
Method flyMethod1 = clazz.getDeclaredMethod("fly");
System.out.println(flyMethod1);
System.out.println("---------------");
//构造函数
Constructor[] constructors = clazz.getConstructors();
System.out.println(Arrays.toString(constructors));
Constructor[] constructors1 = clazz.getDeclaredConstructors();
System.out.println(Arrays.toString(constructors1));
Constructor constructor1 = clazz.getDeclaredConstructor();
System.out.println(constructor1);
Constructor constructor2 = clazz.getDeclaredConstructor(String.class);
System.out.println(constructor2);
//Constructor constructor3 = clazz.getConstructor();
//System.out.println(constructor3);
System.out.println("----------");
//接口
Class[] classes = clazz.getInterfaces();
System.out.println(Arrays.toString(classes));
//拿到类信息后可以进行相应的操作,如果拿到的信息是private修饰,使用时要先进行暴力反射,否则会抛出异常
//拿到构造方法 ->创建对象
Constructor c1 = clazz.getDeclaredConstructor(int.class,String.class,String.class,double.class);
Cat cat1 = (Cat) c1.newInstance(20,"小黑","黑色",240.5);
Constructor c2 = clazz.getDeclaredConstructor();
c2.setAccessible(true);//暴力反射
Cat cat2 = (Cat) c2.newInstance();
Constructor c3 = clazz.getDeclaredConstructor(String.class);
Cat cat3 = (Cat) c3.newInstance("白色");
//拿到变量 ->赋值和获取
ageFiled.setAccessible(true);//暴力反射
ageFiled.set(cat1, 88);
int age = (int) ageFiled.get(cat1);
System.out.println(age);
nameFiled.set(cat1, "小花");
System.out.println(nameFiled.get(cat1));
//拿到方法->使用方法
runMethod1.invoke(cat1, "小花");
Method method2 = clazz.getDeclaredMethod("setAge", int.class);
method2.setAccessible(true);
System.out.println(method2.invoke(cat1, 18));
}
}
- 总结:
反射获取类信息的步骤:
- 生成类对象。
- 通过类对象获取类信息。
- 拿到类信息后可以进行相应的操作,如果拿到的信息是private修饰,使用时要先进行暴力反射,否则会抛出异常。拿到构造方法后可以创建对象;拿到变量后可以赋值和获取值;拿到方法后可以执行方法。