Java反射小结

本文详细介绍了Java反射的概念,包括其用途、优缺点,以及如何获取类的属性和方法、创建对象、访问父类属性。通过实例展示了反射如何从配置文件中调用方法和修改对象属性,强调了反射在实际开发中的应用及其潜在的安全问题。

一、概念

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值