软件编程
位置:首页>> 软件编程>> java编程>> java上乘武功入门--反射

java上乘武功入门--反射

作者:二当家的白帽子  发布时间:2021-06-08 21:32:27 

标签: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));
   }
}

java上乘武功入门--反射

二当家的第一次看到这个执行结果觉得很有意思。明明应该是"abc"怎么就变成了"def"呢?

反射机制是个什么玩意儿?

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。

构造任意一个类的对象

一般情况下,我们如果想要创建一个类的对象,应该要用到new关键字。但是像spring这样的框架,我们只需要配置类名,就可以得到类的实例。他是怎么做到的呢?


import java.util.List;
public class Test {
   /**
    * 根据类名取得类实例
    * @author 二当家的白帽子 https://le-yi.blog.csdn.net/
    * @param className
    * @param <T>
    * @return
    * @throws InstantiationException
    * @throws IllegalAccessException
    * @throws ClassNotFoundException
    */
   public static <T> T getInstance(String className) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
       Class<T> clazz = (Class<T>) Class.forName(className);
       return clazz.newInstance();
   }
   public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
       List<String> 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<>());
   }
}

java上乘武功入门--反射

了解任意一个类的成员变量和方法

我们一般要使用一个类,先要知道有什么方法和属性,先了解,后使用。但是像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;
   }
}

java上乘武功入门--反射

调用任意一个对象的属性和方法

像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;
   }
}

java上乘武功入门--反射

魔法揭秘

是时候揭秘魔法的真面目了,没错,也是利用了反射。


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是不变类。但是反射 * 却可以打破一切禁忌。

来源:https://blog.csdn.net/leyi520/article/details/118676211

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com