从B站上学习了java反射的相关知识,做个笔记记录一下,基础概念不再赘述,只记录一些重点
(1)【Source源代码阶段】 Class.forName("全类名"):将字节码文件加载进内存,返回Class对象 。多用于配置文件,将类名定义在配置文件中。读取文件,加载类。
(2)【Class类对象阶段】 类名.class:通过类名的属性class获取。 多用于参数的传递
(3)【Runtime运行时阶段】对象.getClass():getClass()方法是定义在Objec类中的方法 。 多用于对象的获取字节码的方式
结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,无论通过哪一种方式获取的Class对象都是同一个。
@Test
public void reflect1() throws ClassNotFoundException {
//方式一:Class.forName("全类名");
Class cls1 = Class.forName("com.test.domain.User"); //自定义实体类
System.out.println("cls1 = " + cls1);
//方式二:类名.class
Class cls2 = User.class;
System.out.println("cls2 = " + cls2);
//方式三:对象.getClass();
User user= new User();
Class cls3 = user.getClass();
System.out.println("cls3 = " + cls3);
// == 比较三个对象
System.out.println("cls1 == cls2 : " + (cls1 == cls2)); //true
System.out.println("cls1 == cls3 : " + (cls1 == cls3)); //true
//结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,无论通过哪一种方式获取的Class对象都是同一个。
}
Field[] getFields() :获取所有public修饰的成员变量
Field getField(String name) 获取指定名称的 public修饰的成员变量
Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
Field getDeclaredField(String name)
Method[] getMethods()
Method getMethod(String name, 类... parameterTypes)
Method[] getDeclaredMethods()
Method getDeclaredMethod(String name, 类... parameterTypes)
Constructor[] getConstructors()
Constructor getConstructor(类... parameterTypes)
Constructor[] getDeclaredConstructors()
Constructor getDeclaredConstructor(类... parameterTypes)
String getName()
- (1)设置值 void set(Object obj, Object value)
- (2)获取值 get(Object obj)
- (3)忽略访问权限修饰符的安全检查 setAccessible(true):暴力反射
注意:操作private修饰的变量,需要添加下面代码,否则set和get会报错
代码示例:公共代码User类
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User {
public String name;
private String sex;
private int age;
public String desc;
public void eat(){
System.out.println("eat...");
}
public void eat(String food){
System.out.println("eat"+food);
}
}
public void test01() throws Exception{
Class clazz = Class.forName("com.ang.reflection.domain.User");
//获取到public的字段
Field[] fieldsPublic = clazz.getFields();
Arrays.stream(fieldsPublic).forEach(f->{
System.out.println(f);
});
//获取指定的public字段
Field fieldPublic = clazz.getField("name");
System.out.println("-----------------------------");
System.out.println(fieldPublic);
//获取所有的任意修饰符的字段
Field[] fieldAll = clazz.getDeclaredFields();
System.out.println("-----------------------------");
Arrays.stream(fieldAll).forEach(f->{
System.out.println(f.toString());
});
//获取指定的任意修饰符的字段
Field fieldAny = clazz.getDeclaredField("age");
System.out.println("-----------------------------");
System.out.println(fieldAny);
}
打印结果:
/**
* 获取并设置成员变量的值
*/
public void test02() throws Exception {
User user = getUser();
Class clazz = User.class;
//获取user对象中name的值
Field name = clazz.getDeclaredField("sex");
//sex是私有变量,直接get/set会报错,需要下面一行代码
name.setAccessible(false);
//get
Object value1 = name.get(user);
System.out.println(value1.toString());
//set
name.set(user,"女");
System.out.println(user.getName());
}
打印结果:
创建对象:T newInstance(Object... initargs)
注意:如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
/**
* 无参构造
*/
public void test01() throws Exception {
Class clazz = Class.forName("com.ang.reflection.domain.User");
//获取无参构造
Constructor constructor = clazz.getConstructor();
System.out.println(constructor);
System.out.println("---------------------");
//使用无参构造创建对象
Object user = constructor.newInstance();
System.out.println(user);
//也可以直接使用反射的对象来创建实例
Object user1 = clazz.newInstance();
System.out.println(user1);
}
打印结果:
/**
* 有参构造
*/
public void test02() throws Exception{
Class clazz = Class.forName("com.ang.reflection.domain.User");
Constructor[] constructors = clazz.getConstructors();
Arrays.stream(constructors).forEach(c->{
System.out.println(c);
});
System.out.println("------------------------");
//获取有参构造方法
Constructor constructor = clazz.getConstructor(String.class,String.class,int.class,String.class);
System.out.println(constructor);
//构造对象
Object user = constructor.newInstance("李四","男",20,"喜欢打游戏");
System.out.println(user);
}
打印结果:
对于多出个Declared关键词的两个方法,与不带这个词的两个方法的对比。与之前3.2叙述的一样,getDeclaredConstructor方法可以获取到任何访问权限的构造器,而getConstructor方法只能获取public修饰的构造器。具体不再测试。此外在构造器的对象内也有setAccessible(true);方法,并设置成true就可以操作了。
- 执行方法:Object invoke(Object obj, Object... args)
- 获取方法名称:String getName();
/**
* 获取成员方法
*/
public void test01() throws Exception {
Class clazz = Class.forName("com.ang.reflection.domain.User");
Method[] methods = clazz.getMethods();
Arrays.stream(methods).forEach(m->{
System.out.println(m);
System.out.println(m.getName());
System.out.println("--------------------------");
});
}
/**
* 无参方法
*/
public void test02() throws Exception{
Class clazz = Class.forName("com.ang.reflection.domain.User");
Object user = new User();
Method method = clazz.getMethod("eat");
method.invoke(user);
}
打印结果:
/**
* 有参构造
*/
public void test03() throws Exception{
Class clazz = Class.forName("com.ang.reflection.domain.User");
Object user = new User();
Method method = clazz.getMethod("eat", String.class);
method.invoke(user, " Apple");
}
打印结果:
实现一个功能,从配置文件中,根据配置的不同类名和方法名,通过反射来运行类中的方法
(1)将需要创建的对象的全类名和需要执行的方法定义在配置文件中
(2)在程序中加载读取配置文件
(3)使用反射技术来加载类文件进内存
(4)创建对象
(5)执行方法
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User {
public String name;
private String sex;
private int age;
public String desc;
public void eat(){
System.out.println("eat...");
}
public void eat(String food){
System.out.println("eat"+food);
}
}
className = com.ang.reflection.domain.User
methodName = eat
/**
* 一个小案例,从配置文件读取class和method,实现方法的执行
*/
public class DemoTest {
public static void main(String[] args) throws Exception{
//获取类加载器
ClassLoader classLoader = DemoTest.class.getClassLoader();
//加载文件
InputStream is = classLoader.getResourceAsStream("reflection.properties");
//获取值
Properties properties = new Properties();
properties.load(is);
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
//反射获取class
Class clazz = Class.forName(className);
//反射获取实例
Object object = clazz.newInstance();
//反射获取无参方法
Method method1 = clazz.getDeclaredMethod(methodName);
method1.invoke(object);
//反射获取有参方法
Method method2 = clazz.getDeclaredMethod(methodName, String.class);
method2.invoke(object, " orange");
}
}
打印结果: