r到目前为止我们已经知道了当一个class文件被类加载器加载进内存时,会在JVM中将形成一份描述该class文件结构的元信息对象Class,通过该对象JVM就可以获知class文件的结构信息:如构造器,字段,方法等。
由面向对象引发的思考:
既然万物皆对象,那么类名、构造器、字段、方法等这些信息当然也需要封装成一个对象,这就是Class类、Constructor类、Field类、Method类。
而通过Class类、Constructor类、Method类、Field类等类的实例对象就可以得相应的信息,甚至可以不用new关键字就创建一个实例,并设置或获取字段的值,执行对象中的方法,这就是反射技术。
反射定义
反射就是在运行时期,动态的获取类中成员信息(构造器,字段,方法)的过程!
获取类的字节码对象(Class类型对象)的三种方式
要想获取和操作类中的内容,首先要获取类的字节码对象
获取字节码对象的方式:
1、对象名.getClass():获取到的是那个真正用来创建对象的子类的字节码对象。
2、类名.class:如果已经有了类名,可以通过.class的方式获取这个类的字节码对象。
3、通过Class.forName(String className):Class类中的一个静态方法,可以根据一个类的全类名,动态的加载某个类型。
Class的newInstance()方法创建对象
反射构造方法
获取多个
public Constructor<?>[] getConstructors():
该方法只能获取当前Class所表示类的public修饰的构造器
public Constructor<?>[] getDeclaredConstructors():
获取当前Class所表示类的所有的构造器,和访问权限无关
获取单个
public ConstructorgetConstructor(Class<?>…parameterTypes) :
获取当前Class所表示类中指定参数类型的public的构造器
如果不存在对应的构造方法,则会抛出 java.lang.NoSuchMethodException 异常。
public Constructor getDeclaredConstructor(Class<?>… parameterTypes) :
获取当前Class所表示类中指定参数类型的构造器
如果不存在对应的构造方法,则会抛出 java.lang.NoSuchMethodException 异常。
调用私有的构造器前需要设置开启暴力访问:constructor.setAccessible(true);
使用构造器创建对象
构造器对象的newinstance()创建对象
package demo0911;
/**
* @creat 2020-09-10-20:35
*
*/
public class Person {
// 成员变量
private Long id;
public String name;
public int age;
// 构造方法
public Person() {
System.out.println("无参数public构造器被执行");
}
public Person(String name) {
this.name = name;
System.out.println("带有String的public构造器被执行");
}
// 私有的构造方法
private Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("带有String, int的private构造器被执行");
}
public Person(Long id, String name, int age) {
super();
this.id = id;
this.name = name;
this.age = age;
System.out.println("带有Long, String, int的public构造器被执行");
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}
package demo0911;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.util.Arrays;
/**
* @creat 2020-09-10-20:36
*/
public class TestPerson {
@Test
public void test1() throws Exception {
Class<Person> personClass = Person.class;
//获取public的构造器
Constructor<?>[] constructors = personClass.getConstructors();
System.out.println(Arrays.toString(constructors));
System.out.println();
//获取全部构造器
Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
System.out.println(Arrays.toString(declaredConstructors));
System.out.println();
//获取一个无参的public构造器
Constructor<Person> constructor = personClass.getConstructor();
Person person = constructor.newInstance();
System.out.println(person);
//获取一个私有的
Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(String.class, int.class);
//私有构造器不能直接访问,需要开启暴力访问
declaredConstructor.setAccessible(true);
Person tom = declaredConstructor.newInstance("tom", 12);
System.out.println(tom);
}
}
反射方法
获取多个:
public Method[] getMethods():获取本类和继承过来的所有的public方法
public Method[] getDeclaredMethods():获取本类中所有的方法(包括private,不包括继承的,和访问权限无关)
获取单个:
public Method getMethod(String methodName, Class<?>… parameterTypes):获取指定public的方法(包括继承的)
methodName: 表示方法名
parameterTypes:表示方法参数的Class类型如String.class
public Method getDeclaredMethod(String name,Class<?>… parameterTypes):获取指定方法(包括private,不包括继承的)
methodName: 表示方法名字
parameterTypes:表示方法参数的Class类型如String.class
调用方法
public Object invoke(Object obj,Object… args):表示调用当前Method所表示的方法
参数:
obj: 表示被调用方法所属对象
args:表示调用方法是传递的实际参数
返回:
方法的返回结果
在调用私有方法之前开启暴力访问:Method.setAccessible(true);
使用反射调用静态方法:
静态方法不属于任何对象,静态方法属于类本身.
此时把invoke方法的第一个参数设置为null即可.
使用反射调用数组参数方法(可变参数):
调用方法的时候把实际参数统统作为Object数组的元素即可.
Method对象.invoke(方法所属对象,new Object[]{所有实参 });
package demo09111;
import java.util.Arrays;
/**
* @creat 2020-09-10-20:52
*/
public class Person {
// 无参无返回的方法
public void method1() {
System.out.println("无参无返回的public方法被执行");
}
// 有参无返回的方法
public void method2(String name) {
System.out.println("有参无返回的public方法被执行 name= " + name);
}
// 无参有返回的方法
public int method3() {
System.out.println("无参有返回的public方法被执行");
return 123;
}
// 有参有返回方法
public String method4(String name) {
System.out.println("有参有返回方法的public方法被执行");
return "哈哈" + name;
}
// 私有方法
private void method5() {
System.out.println("private私有方法被执行");
}
//静态方法
public static void method6(String name) {
System.out.println("静态方法被执行 name= " + name);
}
//参数是基本数据类型的数组
public void method7(int... arr) {
System.out.println("参数是基本数据类型的数组的方法被执行arr= " + Arrays.toString(arr));
}
//参数是引用数据类型的数组
public void method8(String... arr) {
System.out.println("参数是引用数据类型的数组的方法被执行arr= " + Arrays.toString(arr));
}
}
package demo09111;
import org.junit.Test;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* @creat 2020-09-10-20:53
*/
public class TestMethod {
@Test
public void test() throws Exception {
Class<Person> personClass = Person.class;
//获得所有公共方法
Method[] methods = personClass.getMethods();
System.out.println(Arrays.toString(methods));
System.out.println();
//获得所有方法
Method[] methods1 = personClass.getDeclaredMethods();
System.out.println(Arrays.toString(methods1));
System.out.println();
//获得无参无返回指公共方法
Method method11 = personClass.getMethod("method1");
Person person = personClass.newInstance();
Object invoke = method11.invoke(person);
System.out.println(invoke);
//获得无参无返回值的私有方法
Method method5 = personClass.getDeclaredMethod("method5");
//开启暴力访问
method5.setAccessible(true);
method5.invoke(person);
//获得有参无返回值的静态方法
Method method6 = personClass.getDeclaredMethod("method6", java.lang.String.class);
///静态方法不属于任何对象,所以第一个参数给null也可以
method6.invoke(null,"tom");
//获得有参有返回值的公共方法
Method method4 = personClass.getMethod("method4", String.class);
Object lili = method4.invoke(person, "lili");
System.out.println(lili);
//获得参数是数组的无返回值方法
Method method8 = personClass.getMethod("method8", String[].class);
String[] str = {"1", "2", "a"};
method8.invoke(person,new Object[]{str});//引用数据类型必须放在object数组中
Method method7 = personClass.getMethod("method7", int[].class);
method7.invoke(person,new int[]{1,2,3,4});
}
}
反射属性
获取多个
public Field[] getFields() 获取所有public 修饰的变量
public Field[] getDeclaredFields() 获取所有的 变量 (包含私有)
获取单个
public Field getField(String name) 获取指定的 public修饰的变量
public Field getDeclaredField(String name) 获取指定的任意变量(包含私有)
给指定对象的指定成员变量赋值或者获取值
public void set(Object obj, Object value)在指定对象obj中,将此Field设置为指定值
public Object get(Object obj)返回指定对象obj中,此 Field的值
私有成员变量,通过setAccessible(true)方法暴力访问
package demo09112;
/**
* @creat 2020-09-10-21:29
*/
public class Person {
// 成员变量
private Long id;
public String name;
public int age;
}
package demo09112;
import org.junit.Test;
import java.lang.reflect.Field;
import java.util.Arrays;
/**
* @creat 2020-09-10-21:30
* private Long id;
* public String name;
* public int age;
*/
public class TestPerson {
@Test
public void test() throws Exception {
Class<Person> personClass = Person.class;
Field[] fields = personClass.getFields();
System.out.println(Arrays.toString(fields));
Field[] declaredFields = personClass.getDeclaredFields();
System.out.println(Arrays.toString(declaredFields));
Field name = personClass.getField("name");
Person person = personClass.newInstance();
name.set(person,"tom");
System.out.println(name.get(person));
Field id = personClass.getDeclaredField("id");
id.setAccessible(true);
id.set(person,11l);
System.out.println(id.get(person));
}
}
反射应用—泛型擦除
//有如下集合
ArrayList list = new ArrayList<>();
list.add(666);
//设计代码,将字符串类型的"abc",添加到上述带有Integer泛型的集合list中
public class Demo11_反射练习_泛型擦除 {
public static void main(String[] args) throws Exception{
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(666);
// 要把 "abc" 也加入到list中
//list.add("abc"); // 编译期检查泛型——"abc" 不能加入到list中。可以在运行期加入
/**
* 在编译阶段,检查add方法的实际参数,如果在编译阶段,不要调用add方法,
* 就会避免掉在编译阶段,对实际参数数据类型的检查
* 在运行阶段,调用add方法
* 使用反射的方式,调用某个方法,在写代码的阶段,根本不知道将来调用哪个方法
* 编译器也就没有办法在编译阶段对代码进行检查
*
* 这种方式叫做“泛型擦除”
* 在java中,只会在编译阶段,对泛型进行检查,到了运行阶段,对泛型不检查
* 称这种泛型为:伪泛型
*/
Class clazz = list.getClass();
Method method = clazz.getMethod("add",Object.class);
method.invoke(list, "abc");
System.out.println(list);
}
}