从一道String面试题到Java反射的学习

    科技2022-09-05  110

    1 面试原题

    “String 可不可变?”

    若回答不可变则需要根据不可变性(后续文章进行分析)进行回答若回答可变则需要进入本文引入的话题通过反射改变 String 对象的内容

    2 代码解释

    String str = new String("Hello the World !"); System.out.println("改变前的值:" + str);//改变前的值:Hello the World ! System.out.println("改变前的hashCode:" + str.hashCode());//改变前的hashCode:808963766 Field value = str.getClass().getDeclaredField("value"); value.setAccessible(true); char[] field = (char[]) value.get(str); field[6] = ' '; field[7] = 'm'; field[8] = 'y'; System.out.println("改变后的值:" + str);//改变后的值:Hello my World ! System.out.println("改变后的hashCode:" + str.hashCode());//改变后的hashCode:808963766

    运行上面代码,在不改变字符串对象 str 的前提下,修改了其属性 value 的内容。主要的核心就是用到了 Class 中的 public Field getDeclaredField(String name)方法,该方法就是根据属性名获取类本身的属性成员并对其进行修改。

    3 反射

    就个人理解来说,反射就是在 Java 运行环境中提供的一种机制,即在运行时可以判断对象所属类以及类具有的成员属性和方法、构造任意一个类的对象、和调用对象的方法等。 本文不阐述反射的原理定义,主要以代码应用为主。

    3.1 获取 Class

    通过 Class.forName(全类名)获取 System.out.println("1、获取Class"); Class<?> c1 = Class.forName("java.lang.Integer"); 通过对象的 getClass()方法获取 Integer i1 = 1; Class<?> c2 = i1.getClass(); 通过静态数据 class 获取 Class<?> c3 = Integer.class;

    3.2 创建实例

    通过 Class 的 newInstance()方法创建 Class<?> class1 = String.class; Object s1 = class1.newInstance(); 通过 getConstructor 构造器创建 Class<?> class2 = String.class; Constructor<?> constructor = class2.getConstructor(String.class); Object s2 = constructor.newInstance("Java");//相当于new String("Java");

    3.3 判断是否为某个类的实例

    通过 instanceof 判断 Object obj = "Java"; System.out.println(obj instanceof String); 通过 Class 对象的 isInstance()判断 Object obj = "Java"; System.out.println(String.class.isInstance(obj));

    3.4 获取对象的属性

    测试类 User

    public class User { private String name; public Integer age; //Getter and Setter } 通过 getField()或 getFields()获取公有属性及其父类的公有属性 Field age = User.class.getField("age"); //此处会报错,因为name为private数.属性 Field name = User.class.getField("name"); Field[] fields = User.class.getFields(); for (Field field : fields) { System.out.println(field.toString()); } 通过 getDeclaredField()或 获取属性及其父类属性,不区分公有私有 Field userName = User.class.getDeclaredField("name"); Field userAge = User.class.getDeclaredField("age"); Field[] declaredFields = User.class.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println(declaredField); }

    3.5 获取对象属性值

    通过 get()方法获取,如果知道属性类型可以使用指定类型进行获取,例如:getInt()等,需要强调的是要在调用时传入的是已经实例化的对象 User user = new User("zs"18); Field userName = user.getClass().getDeclaredField("name"); Field userAge = user.getClass().getDeclaredField("age"); //获取私有属性值需要设置为 true userName.setAccessible(true); System.out.println(user);//User{name='zs', age=18} System.out.println(userName.get(user));//zs System.out.println(userAge.get(user));//18 补充:Accessable 属性是继承自 AccessibleObject 类,功能是默认为(false)启用,如需禁用安全检查则设置为(true),此处并发本文重点,读者只需记住如需访问私有属性则需要禁用安全检查即可(后续文章进行分析)

    3.6 获取方法

    getMethods(),返回该类所有的公共的方法以及继承的类公用方法通过 getDeclaredMethods()获取,返回类或接口声明的所有方法,包括公共、保护、默认访问和私有方法,但不包括继承的方法 Class<?> clazz = User.class; Method[] methods = clazz.getMethods(); //Method[] methods = clazz.getDeclaredMethods();可获取私有方法 for (Method method : methods) { //获取方法的访问权限 int modifiers = method.getModifiers(); System.out.print(Modifier.toString(modifiers) + " "); //获取方法返回类型 Class<?> returnType = method.getReturnType(); System.out.print(returnType.getName() + " " + method.getName() + "( "); //获取方法的所有参数 Parameter[] parameters = method.getParameters(); for (Parameter parameter : parameters) { System.out.print(parameter.getType().getName() + " " + parameter.getName() + ","); } //获取方法抛出的异常 Class<?>[] exceptionTypes = method.getExceptionTypes(); if (exceptionTypes.length == 0) { System.out.println(" )"); } else { for (Class<?> exceptionType : exceptionTypes) { System.out.println(") throws " + exceptionType.getName()); } } } 通过 getMethod()获取,返回一个特定的方法,第一个参数为方法名称,后面的参数是方法参数对应的 Class 对象通过 getDeclaredMethod()获取,与 getMethod()区别在于可以获取私有方法 Method setName = clazz.getMethod("setName", String.class); //Method setName = clazz.getDeclaredMethod("setName", String.class); //setName.setAccessible(true);私有方法需要设置为true System.out.println(user);//User{name='zs', age=18} setName.invoke(user,"张三");//调用该方法 System.out.println(user);//User{name='张三', age=18}

    3.7 调用方法

    ReduceDemo 测试类 public class ReduceDemo { public int reduce(int x, int y) { return x - y; } } invoke()调用执行实例对象的方法,注第一个参数为实例对象,私有方法需要 setAccessible(true) Class<ReduceDemo> reduceDemoClass = ReduceDemo.class; ReduceDemo reduceDemo = reduceDemoClass.newInstance(); //参数:方法名 参数一 参数二 Method reduce = reduceDemoClass.getMethod("reduce"int.classint.class); Object invoke = reduce.invoke(reduceDemo, 108); System.out.println("执行方法 " + reduce.getName()); System.out.println(invoke);//2

    4 小结

    反射是 Java 学习中不可避免的一个难题,又由于工作中很少使用到,对于大部分开发来说都很陌生,但这对于 Java 开发工程来说这又是很重要的知识点,尤其是在源码的学习上,如 Spring、Hadoop 等框架源码学习中都可以看到反射的身影。本文主要讲述 Java 反射的基本操作,并未深入细讲化具体使用方向等,如有需要后续将引入源码进行分析反射在源码框架中的使用。 重要的事情说三遍:代码跟着手敲一遍、代码跟着手敲一遍、代码跟着手敲一遍。

    Processed: 0.013, SQL: 12