IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> JAVA学习记录 - Reflect 反射 -> 正文阅读

[Java知识库]JAVA学习记录 - Reflect 反射

(ps: 本文章适用于超级萌新,所以会说一些基础的东西,不喜勿喷)

目录

为什么叫反射??

Java反射基础常用代码的有以下三个方法:

让我们举一个示例:

输出:

反射有什么用?

通过反射优化代码的示例:

运行结果:

反射的所有常用方法

注意

练习

答案


为什么叫反射??

? ? ? ? reflect 可以把某一对象中的变量、方法等等内容映射成一个个的变量对象、方法对象等。打个不恰当的比方,就好比一个 Object (对象)?在照镜子,因为光线反射致使镜中呈现了一个和这个 Object?一模一样的镜像,而镜中的成像便是我们可以操作的对象。因为我们操作的是镜中的对象,并非是原先存在在照镜子的那个对象,我们便可以通过让任意一个对象照镜子,但是我们操作镜中对象的方式却可以不变。

Java反射基础常用代码的有以下三个方法:

// 1.获取 某一对象 的 Class, 有以下两种方法:
        Class<?> c = Object.class;
        Class<?> c = new Object().getClass();

// 2.获取 某一对象 的 Method (方法)[需要抛出异常 NoSuchMethodException]:
        Object.getMethod(String: 方法名, Class<?>[]: 方法参数列表);

// 3.invoke (调用) 某一对象 的 Method (方法)
// [需要抛出异常 InvocationTargetException, IllegalAccessException]:
        Method.invoke(Object: 对象, Object[]: 参数值);

让我们举一个示例:

[ Student.java?]

public class Student {
    private String name;
    private Integer age;

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

心细的同学可以发现,年龄字段 age 是 Integer 而并非是 int ,因为反射操作的是类对象 (Integer 是 int 的封装类),如果使用 int 类型,会出现一些一些奇奇怪怪的错误,但也不是不可以使用 int,只是初学者不建议这么做。

[ Reflect.java?]

import java.lang.reflect.Method;

public class Reflect {
    public static void main(String[] args) {
        Student s = new Student("渣渣辉", 21);

        System.out.println(s.toString());

        try {
            Class<?> c = s.getClass();
            Method m = c.getMethod("setAge", Integer.class);
            m.invoke(s, 16);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(s.toString());
    }
}

输出:

Student{name='渣渣辉', age=21}
Student{name='渣渣辉', age=16}

可以看到,已经成功调用了 Student class 中的 setAge 方法。

反射有什么用?

? ? ? ? 很多同学会好奇,反射这么麻烦我为什么不直接调用原对象的方法呢?

? ? ? ? 反射可以优化代码中的大量 if-else 语句,使得代码更加优雅,而且反射最重要的是可以动态加载,目前市面上热门的框架(如Spring,Mybatis)几乎都需要反射来完成。反射还能实现各种骚操作,这些操作就需要你自己去探索了。

通过反射优化代码的示例:

[ Action.java?]

public class Actions {

    public void login(String username, String password){ // 登录
        System.out.printf("登录: username: %s, password: %s\n", username, password);
    }

    public void regist(String username, String password){  // 注册
        System.out.printf("注册: username: %s, password: %s\n", username, password);
    }
}

[ Reflect.java ]

import java.lang.reflect.Method;

public class Reflect {

    public static void Login(String action, String username, String password){
        try{
            Method c = Actions.class.getMethod(action, String.class, String.class);
            c.invoke(new Actions(), username, password);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

}

[ Login.java?]

public class Login {
    public static void main(String[] args) {
        Reflect.Login("login", "account", "123");
        Reflect.Login("regist", "test", "testPass");
    }
}

运行结果:

登录: username: account, password: 123
注册: username: test, password: testPass

从上面可看出,主类(Login.java)的代码量大大缩减,一行即可实现登录或者注册的操作。而且在实际开发的项目中,我们可以在不修改主类源码的情况下进行更新,只需要在 Action.java 中更新代码即可。比如需要加入一个注销账号的功能,只需要在 Action.java 中加入 public void 注销(String 账号, String 密码){}。

????????在此示例情况下的反射操作可以避免代码臃肿,使得代码更好被维护,但是缺点是反射会比 if-else 语句的速度慢,在小型项目上这个缺点可以忽略不计。

反射的所有常用方法

// Class (类) 的获取
        Class<?> cl = Object.class;  // 获取对象的字节码文件对象
        Class<?> cl2 = 基础数据类型.class;  //  类型包含基本数据类型和引用数据类型
        Class<?> cl3 = Class.forName(String: 字节码全路径);  // 通过字符串获取一个字节码文件,字符串必须是全路径名

// Constructor (构造) 的相关操作
        Object obj = cl.newInstance();  // 无参构造对象, 等效于 new Object [已弃用]
        Object obj2 = cl.getDeclaredConstructor().newInstance();  // 与上一样 [JAVA 1.9+]

        Constructor con = cl.getDeclaredConstructor(class<?>[]: 参数类型);  // 有参构造
        Object obj3 = con.newInstance(Object[]: 参数内容);  // 有参构造实例化对象

        Constructor[] cons = cl.getConstrutors();  // 获取所有构造器
        con.getName();  // 获取构造方法的名称
        con.getParameterCount();  // 获取构造方法的参数数量
        con.getParameterTypes();  // 获取构造方法的参数类型

// Field (属性) 的相关操作
        Field field = cl.getField(String: 属性名称);  // 获取 public 属性
        Field field2 = cl.getDeclaredField(String: 属性名称);  // 获取任意属性
        Field[] fieldPublic = cl.getFields();  // 获取所有 public 属性
        Field[] fields = cl.getDeclaredFields();  // 获取所有属性
        String name = field.getName(); // 获取属性名称 
		Class fieldClass = field.getType() 获取属性类型的字节码文件
        field.setAccessible(Boolean: 是否强制);  // 为 true 时可以强制对该属性进行修改等操作
        field.set(Object: 对象, Object: 值);  // 设置该对象的 field 的值, 等效于 Object.fieldName = value;
        Object obj4 = field.get();  // 获取该属性的值

// Method (方法) 的相关操作
        Method m = cl.getMethod(String: 方法名, Class<?>[]: 方法参数列表);  // 获取 public 方法, getMethod(String methodName, Class<?>... classes)
        Method m2 = cl.getDeclaredMethod(String: 方法名, Class<?>[]: 方法参数列表);  // 获取任意方法

注意

????????在大多数情况下 getDeclaredXXX 与 getXXX 是通用的,但是在属性或者方法的访问修饰符为 protected 的时候,只能使用?getDeclaredXXX。

练习

? ? ? ? 尝试自己写一个反射工具类 ReflectUtils,用来获取或设置一个包含 Getter 与 Setter 方法的类中的属性的值,并且还可以任意调用某一个类中的方法。

答案

[ ReflectUtils ]

import java.lang.reflect.Method;

public class ReflectUtils {
    
    // 反射调用 GET
    public static Object invokeGetter(Object obj, String fieldName){
        System.out.println(joinString("get", fieldName));
        return invokeMethod(obj, joinString("get", fieldName), null, null);
    }

    // 反射调用 SET
    public static void invokeSetter(Object obj, String fieldName, Object value){
        invokeMethod(obj, joinString("set", fieldName), new Class<?>[]{value.getClass()}, new Object[]{value});
    }

    /**
     * 调用对象的方法
     * @param obj 对象
     * @param methodName 方法名
     * @param parameterTypes 参数列表
     * @param args 参数
     * @return 调用的方法返回值
     */
    public static Object invokeMethod(Object obj, String methodName, Class<?>[] parameterTypes, Object[] args){
        try {
            Class<?> c = obj.getClass();
            Method me = c.getDeclaredMethod(methodName, parameterTypes);
            return me.invoke(obj, args);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }

    /**
     * 获取一个标准的 getter 或者 setter 方法名称
     * @param prefix 前缀, get / set
     * @param fieldName 属性名
     * @return 前缀 + 开头大写的属性名
     */
    public static String joinString(String prefix, String fieldName){
        char[] chars = fieldName.toCharArray();
        chars[0] = chars[0] >= 97 ? (char) (chars[0] - 32) : chars[0];  // a 是 97
        return prefix + String.valueOf(chars);
    }
}

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-01-17 11:22:16  更:2022-01-17 11:22:51 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 9:58:07-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码