java上乘武功入门--反射
目录
- 先来看一段魔法吧
- 反射机制是个什么玩意儿?
- 构造任意一个类的对象
- 了解任意一个对象所属的类
- 了解任意一个类的成员变量和方法
- 调用任意一个对象的属性和方法
- 魔法揭秘
- 总结
先来看一段魔法吧
public class Test {private static void changeStrValue(String str, char[] value) {// 只要执行魔法代码就可以达到下面的效果// 施展魔法的代码稍后揭秘}public static void main(String[] args) {changeStrValue("abc", new char[]{'d','e','f'}); String abc = "abc"; System.out.println("abc"); System.out.println(abc); System.out.println("abc".equals(abc)); }}
文章图片
二当家的第一次看到这个执行结果觉得很有意思。明明应该是"abc"怎么就变成了"def"呢?
反射机制是个什么玩意儿?
Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。以上就是百科的解释。可能有点抽象,接着看二当家的给你秀起来解释一下。
构造任意一个类的对象 一般情况下,我们如果想要创建一个类的对象,应该要用到new关键字。但是像spring这样的框架,我们只需要配置类名,就可以得到类的实例。他是怎么做到的呢?
import java.util.List; public class Test {/*** 根据类名取得类实例* @author 二当家的白帽子 https://le-yi.blog.csdn.net/* @param className* @param* @return* @throws InstantiationException* @throws IllegalAccessException* @throws ClassNotFoundException*/public static T getInstance(String className) throws InstantiationException, IllegalAccessException, ClassNotFoundException {Class clazz = (Class ) Class.forName(className); return clazz.newInstance(); }public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {List list = getInstance("java.util.ArrayList"); list.add("abc"); list.add("def"); for (String v : list) {System.out.println(v); }}}
类名可以在程序运行中从配置文件获取,甚至是从网络获取,然后动态创建一个类的实例。
了解任意一个对象所属的类
import java.util.ArrayList; public class Test {/*** 打印对象的类名* @author 二当家的白帽子 https://le-yi.blog.csdn.net/* @param o*/public static void printClass(Object o) {System.out.printf(o.getClass().getName()); }public static void main(String[] args) {printClass(new ArrayList<>()); }}
文章图片
了解任意一个类的成员变量和方法 我们一般要使用一个类,先要知道有什么方法和属性,先了解,后使用。但是像spring那样的框架为什么可以为我们自动注入呢?他怎么知道我们一个对象里有什么属性呢?
import java.lang.reflect.Field; import java.lang.reflect.Method; public class Test {/*** 打印类的属性* @author 二当家的白帽子 https://le-yi.blog.csdn.net/* @param clazz*/public static void printFields(Class clazz) {System.out.println(clazz.getName() + "包含如下属性:"); for (Field f : clazz.getDeclaredFields()) {System.out.println(f); }}/*** 打印类的方法* @author 二当家的白帽子 https://le-yi.blog.csdn.net/* @param clazz*/public static void printMethods(Class clazz) {System.out.println(clazz.getName() + "包含如下方法:"); for (Method m : clazz.getDeclaredMethods()) {System.out.println(m); }}public static void main(String[] args) {printFields(MyClass.class); printMethods(MyClass.class); }}class MyClass {private String name; public String getName() {return name; }public void setName(String name) {this.name = name; }}
文章图片
调用任意一个对象的属性和方法 像spring这样的框架,即使一个属性是私有属性并且没有set方法,一样可以注入。
import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Test {/*** 调用一个对象的方法* @author 二当家的白帽子 https://le-yi.blog.csdn.net/* @param o* @param methodName* @throws NoSuchMethodException* @throws InvocationTargetException* @throws IllegalAccessException*/public static void callMethod(Object o, String methodName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {Method m = o.getClass().getDeclaredMethod(methodName); m.setAccessible(true); m.invoke(o); }/*** 修改一个对象的属性* @author 二当家的白帽子 https://le-yi.blog.csdn.net/* @param o* @param fieldName* @param value* @throws IllegalAccessException*/public static void changeFieldValue(Object o, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {Field f = o.getClass().getDeclaredField(fieldName); f.setAccessible(true); f.set(o, value); }public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException, NoSuchFieldException {MyClass o = new MyClass(); // 修改任意属性,即使是私有的changeFieldValue(o, "name", "二当家的白帽子"); // 调用任意方法,即使是私有的callMethod(o, "printName"); }}class MyClass { // 私有属性,只可以调用set方法修改private String name; private void printName() {// 私有方法,只有本类自己的实例可以调用System.out.println("My name is " + name); }public String getName() {return name; }public void setName(String name) {this.name = name; }}
文章图片
魔法揭秘 是时候揭秘魔法的真面目了,没错,也是利用了反射。
import java.lang.reflect.Field; public class Test {/*** 修改字符串内部的值* @author 二当家的白帽子 https://le-yi.blog.csdn.net/* @param str* @param value*/private static void changeStrValue(String str, char[] value) {try {Field f = str.getClass().getDeclaredField("value"); f.setAccessible(true); f.set(str, value); } catch (Exception e) {e.printStackTrace(); }}public static void main(String[] args) {changeStrValue("abc", new char[]{'d','e','f'}); // 这里的"abc"字符串和上面调用changeStrValue的参数"abc"会指向同一块内存String abc = "abc"; System.out.println("abc"); System.out.println(abc); System.out.println("abc".equals(abc)); }}
要理解这段代码,除了反射机制还需要了解java对于字符串的处理。"字符串常量池"已经超出本文的范围,是另一个话题,本文就不多说了。
原本字符串内容是"abc",我们正常情况下无法修改这个内容,因为String是不变类。但是反射大法却可以打破一切禁忌。
总结 一般的程序可能用不到写反射的代码。但是像spring这样的框架,如果没有反射,我真的想不出如何实现呢。哪怕永远不需要用反射,了解机制对我们都有着莫大的好处。
【java上乘武功入门--反射】本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注脚本之家的更多内容!
推荐阅读
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 事件代理
- Java|Java OpenCV图像处理之SIFT角点检测详解
- java中如何实现重建二叉树
- 数组常用方法一
- 【Hadoop踩雷】Mac下安装Hadoop3以及Java版本问题
- Java|Java基础——数组
- RxJava|RxJava 在Android项目中的使用(一)
- java之static、static|java之static、static final、final的区别与应用
- Java基础-高级特性-枚举实现状态机