java反射机制的应用场景
Java程序的许多对象在运行时都会出现两种类型:编译时类型和运行时类型,eg:person p=new student() 代码会生成一个p变量,该变量编译时类型为person 运行时为student;除此之外,还有更极端的情况,程序在运行时接收到外部传入的一个对象,该对象的编译时类型是object,但程序又需要调用该对象运行时类型的方法。
程序需要在运行时发现类和对象的真实信息,有两种解决办法:
- 假设在编译时和运行时都知道类和对象的真实信息,在这种情况下,可以使用instanceof运算符进行判断,再利用强制类型转换成其运行时类型的变量即可。
- 编译时根本无法预知该对象和类可能属于哪些类,程序只依靠运行时信息来发现该类和对象的真实信息,这时需要使用反射。
一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。
Apple apple = new Apple(); //直接初始化,正射
apple.setPrice(4);
上面这样子进行类对象的初始化,我们可以理解为「正」。
而反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。
这时候,我们使用 JDK 提供的反射 API 进行反射调用:
Class clz = Class.forName("com.chenshuyi.reflect.Apple");
Method method = clz.getMethod("setPrice", int.class);
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object, 4);
上面两段代码的执行结果,其实是完全一样的。但是其思路完全不一样,第一段代码在未运行时就已经确定了要运行的类(Apple),而第二段代码则是在运行时通过字符串值才得知要运行的类(com.chenshuyi.reflect.Apple)。
反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
(Java反射机制时指在程序的运行状态中,可以任意构造一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法,这种可以动态的获取程序信息以及动态调用对象的功能称为Java语言的反射机制。)
具体使用
获取class对象的方式
1.class.forName(“类名”)
将字节码文件加载进内存,返回Class对象,多用于配置文件,将类名定义在配置文件中,读取文件,加载类
2.类名.class:
通过类名的属性class获取,多用于参数的传递
3.对象.getClass:
getClass方法在object中定义,多用于对象的获取字节码的方式
Class<?> name = Class.forName("fanshe.Person");
System.out.println(name);
Class<Person> aClass = Person.class;
Person person = new Person();
System.out.println(aClass);
Class<? extends Person> aClass1 = person.getClass();
System.out.println(aClass1);
结论:同一个字节码文件(*.class)在一次程序运行中只会被加载一次,无论使用那种反射机制最终的结果一致。
class fanshe.Person
class fanshe.Person
class fanshe.Person
获取Class对象功能:
person类:
public class Person {
private int age;
public Person(){}
public Person(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
'}';
}
public void eat(){
System.out.println("eat");
}
}
获取成员变量
Field[] getField(); 获取某个成员变量(除了私有的成员变量)
Field[] getFields(); 获取所有成员变量(除了私有的成员变量)
Field[] getDeclaredField(); 获取某个成员变量(包括私有的成员变量)
Field[] getDeclaredFields();获取所有成员变量(包括私有的成员变量)
Field age = name.getField("age");
System.out.println(age);
结果:
Field age = name.getDeclaredField("age");
System.out.println(age);
其余不再进行演示
获取构造方法
Constructor<?>[] getConstructors() 获取所有构造方法(除了私有的构造方法)
Constructor getConstructor(类<?>…parameterTypes)获取某个构造方法(除了私有的构造方法)
Constructor<?>[] getDeclaredConstructors() 获取所有构造方法(包括私有的构造方法)
Constructor getDeclaredConstructor(类<?>…parameterTypes)
获取某个构造方法(包括私有的构造方法)
Constructor<?> constructor = name.getConstructor(int.class);//获取以int为参数的构造方法
System.out.println(constructor);
Constructor<?>[] declaredConstructors = name.getDeclaredConstructors();//获取所有的构造方法
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
获取成员方法
Method[] getMethods() 获取所有public修饰的方法
Method[] getMethod(String name,类<?>… parameterTypes)
Method[] getDeclaredMethods()
Method[] getDeclaredMethod(String name,类<?>… parameterTypes)
Method eat = name.getMethod("eat");
System.out.println(eat);
结果:
获取类名
String getName()
Class<?> name = Class.forName("fanshe.Person");
String name1 = name.getName();
System.out.println(name1);
Filed:成员变量
操作:设置值:void set(Object obj,Object value)
获取值: get(Object obj)
忽略访问修饰符的安全检测
setAccessible(true)暴力反射 可以反射为私有的对象
Field age = name.getDeclaredField("age");
age.setAccessible(true);
Person person = new Person();
age.set(person,10);
Object p= age.get(person);
System.out.println(p);
使用构造方法创建对象
T newInstance(Object… initargs)
如果使用空参数构造方法 操作可以简化 Class对象的newInstance方法
Constructor<?> constructor = name.getConstructor(int.class);
Object o = constructor.newInstance(9);
System.out.println(o);
案例
不改变该类的任何代码的前提下,帮助我们创建任意类并可以执行其任何方法
步骤:1.将需要创建的对象的全类名和需要执行的方法定义在配置文件中
2.在程序中加载读取配置文件
3.使用反射技术来加载类文件进内存
4.创建对象
5.执行方法
pro.properties文件内容
className=fanshe.Person
methodName=eat
Properties properties = new Properties();
ClassLoader classLoader = ReflectDemo.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("pro.properties");
properties.load(is);
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
Class cls = Class.forName(className);
Object o = cls.newInstance();
Method method = cls.getMethod(methodName);
method.invoke(o);