目录
一、概念
Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。
二、反射的用途
1)反编译:.class–>.java
2)通过反射机制访问java对象的属性,方法,构造方法等
3)反射最重要的用途就是开发各种通用框架。运行时动态加载需要的加载的对象。
三、反射的优缺点
1)优点:java代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。
2)缺点:反射会消耗一定的系统资源;反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
四、反射的具体实现
反射的具体实现包括,获取类的类名、属性、方法、创建对象、获取父类的属性及方法等。
以下是一个实体类Person
package com.ys.reflex;
public class Person {
//私有属性
private String name = "Tom";
//公有属性
public int age = 18;
//构造方法
public Person() {
}
//私有方法
private void say(){
System.out.println("private say()...");
}
//公有方法
public void work(){
System.out.println("public work()...");
}
}
4.1 获取类的属性及方法
首先需要获得这个类的类名,获取类名的方式有三种:
//1、通过对象调用 getClass() 方法来获取,通常应用在:比如你传过来一个 Object
// 类型的对象,而我不知道你具体是什么类,用这种方法
Person p1 = new Person();
Class c1 = p1.getClass();
//2、直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高
// 这说明任何一个类都有一个隐含的静态成员变量 class
Class c2 = Person.class;
//3、通过 Class 对象的 forName() 静态方法来获取,使用包名.类名,用的最多
//但可能抛出 ClassNotFoundException 异常
Class c3 = Class.forName("com.ys.reflex.Person");
得到类名后,调用Class的API,常见的API如下:
getName():获得类的完整名字。
getFields():获得类的public类型的属性。
getDeclaredFields():获得类的所有属性。包括private 声明的和继承类
getMethods():获得类的public类型的方法。
getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类
getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。
getConstructors():获得类的public类型的构造方法。
getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
调用相应的API即可
//获得类完整的名字
String className = c2.getName();
System.out.println(className);//输出com.ys.reflex.Person
//获得类的public类型的属性。
Field[] fields = c2.getFields();
for(Field field : fields){
System.out.println(field.getName());//age
}
//获得类的所有属性。包括私有的
Field [] allFields = c2.getDeclaredFields();
for(Field field : allFields){
System.out.println(field.getName());//name age
}
//获得类的public类型的方法。这里包括 Object 类的一些方法
Method [] methods = c2.getMethods();
for(Method method : methods){
System.out.println(method.getName());//work waid equls toString hashCode等
}
//获得类的所有方法。
Method [] allMethods = c2.getDeclaredMethods();
for(Method method : allMethods){
System.out.println(method.getName());//work say
}
//获得指定的属性
Field f1 = c2.getField("age");
System.out.println(f1);
//获得指定的私有属性
Field f2 = c2.getDeclaredField("name");
//启用和禁用访问安全检查的开关,值为 true,则表示反射的对象在使用时应该取消 java 语言的访问检查;反之不取消
f2.setAccessible(true);
System.out.println(f2);
//获取构造方法
Constructor [] constructors = c2.getConstructors();
for(Constructor constructor : constructors){
System.out.println(constructor.toString());//public com.ys.reflex.Person()
}
4.2 创建对象
//创建这个类的一个对象
Object p2 = c2.newInstance();
//将 p2 对象的 f2 属性赋值为 Bob,f2 属性即为 私有属性 name
f2.set(p2,"Bob");
//使用反射机制可以打破封装性,导致了java对象的属性不安全。
System.out.println(f2.get(p2)); //Bob
4.3 获取父类的属性
直接通过反射获取子类的对象是不能得到父类的属性值的,必须根据反射获得的子类 Class 对象在调用 getSuperclass() 方法获取父类对象,然后在通过父类对象去获取父类的属性值。
父类 Parent
public class Parent {
public String publicField = "parent_publicField";
protected String protectField = "parent_protectField";
String defaultField = "parent_defaultField";
private String privateField = "parent_privateField";
}
子类 Son
public class Son extends Parent {
}
测试类
public class ReflectionTest {
@Test
public void testGetParentField() throws Exception{
Class c1 = Class.forName("com.ys.model.Son");
//获取父类私有属性值
System.out.println(getFieldValue(c1.newInstance(),"privateField"));
}
public static Field getDeclaredField(Object obj,String fieldName) {
Field field = null;
Class c = obj.getClass();
for(; c != Object.class ; c = c.getSuperclass()){
try {
field = c.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
}catch (Exception e){
//这里甚么都不要做!并且这里的异常必须这样写,不能抛出去。
//如果这里的异常打印或者往外抛,则就不会执行c = c.getSuperclass(),最后就不会进入到父类中了
}
}
return null;
}
public static Object getFieldValue(Object object,String fieldName) throws Exception{
Field field = getDeclaredField(object,fieldName);
return field.get(object);
}
}
五、反射实践
5.1 从配置文件中调用方法
创建一个简单的类
public class Boy {
public void play(){
System.out.println("男孩子喜欢足球");
}
}
创建一个配置文件(注意路径)
className=reflect.frame01.Boy
methodName=play
测试类
public class FrameTest {
public static void main(String[] args) throws Exception {
//加载配置文件
Properties properties = new Properties();
ClassLoader classLoader = FrameTest.class.getClassLoader();
//路径,相对于src
InputStream in = classLoader.getResourceAsStream("reflect/frame01/frame.properties");
properties.load(in);
//获取配置文件的属性
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
//反射,获取该类
Class cls = Class.forName(className);
//创建该类的对象,newInstance被弃用了
Object obj = cls.getDeclaredConstructor().newInstance();
//获取该类的方法对象
Method method = cls.getMethod(methodName);
//执行方法对象
method.invoke(obj);
}
}
执行结果
男孩子喜欢足球
通过修改配置文件,可以调用不同的类的方法。
5.2 调用方法、修改属性
User实体类
package reflect.ex03;
public class User {
private String name;
private String sex;
private int age;
public User() {
}
public User(String name, String sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void showResult(int res){
System.out.println("最终计算结果为:"+res);
}
private int id(int a,int b){
System.out.println("调用了私有的id函数");
return a+b;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
'}';
}
}
测试类
package reflect.ex03;
import java.io.File;
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{
String path = "reflect.ex03.User";
User user = null;
//获取反射类
Class cls = Class.forName(path);
//调用构造器,自定义对象
//调用无参构造器,创建默认对象
// User user = cls.getDeclaredConstructor().newInstance();
Constructor constructor = cls.getDeclaredConstructor(String.class,String.class,int.class);
user = (User) constructor.newInstance("Salar","男",26);
System.out.println(user);
//修改属性
Field f1 = cls.getDeclaredField("name");
f1.setAccessible(true);
f1.set(user,"MANLAKA");
System.out.println(user);
//调用方法,注意指定参数类型,方法有重载
Method m1 = cls.getDeclaredMethod("id",int.class,int.class);
m1.setAccessible(true);
//方法执行结果类型转换
int res = (int) m1.invoke(user,1,2);
Method m2 = cls.getDeclaredMethod("showResult", int.class);
m2.setAccessible(true);
m2.invoke(user,res);
}
}
参考资料
https://www.cnblogs.com/ysocean/p/6516248.html
https://blog.youkuaiyun.com/qq_36957885/article/details/89599470
本文详细介绍了Java反射的概念,包括其用途、优缺点,以及如何获取类的属性和方法、创建对象、访问父类属性。通过实例展示了反射如何从配置文件中调用方法和修改对象属性,强调了反射在实际开发中的应用及其潜在的安全问题。
1万+

被折叠的 条评论
为什么被折叠?



