每日一题(2022/1/19)

  Java   8分钟   381浏览   0评论

如以下代码所示,在注释处可以添加N行代码,但必须保证s引用的指向不变,最终输出结果为abcd

package interview.day01;

/**
 * @author: 邹祥发
 * @date: 2022/1/19 21:31
 */
public class ReflexDemo {
    public static void main(String[] args) {
        String s = new String("abc");
        //在这中间可以添加N行代码,但必须保证s引用的指向不变,最终将输出变成abcd
        System.out.println(s);
    }
}

也许你第一时间想到的就是利用StringBuilder中的append()方法,但是显然是不行的。

public class ReflexDemo {
    public static void main(String[] args) {
        String s = new String("abc");
        //在这中间可以添加N行代码,但必须保证s引用的指向不变,最终将输出变成abcd
        StringBuilder builder = new StringBuilder(s);
        builder.append("d");
        System.out.println(s);
    }
}

也许你又会想到的就是利用String类中的replace()方法,但是其实也是不行的。

public class ReflexDemo {
    public static void main(String[] args) {
        String s = new String("abc");
        //在这中间可以添加N行代码,但必须保证s引用的指向不变,最终将输出变成abcd
        s.replace("abc","abcd");
        System.out.println(s);
    }
}

正确的做法是通过反射的方式来修改String中的value属性,这样既能保证s引用的指向不变,也能将结果输出为abcd。

代码如下:

public class ReflexDemo {
    public static void main(String[] args) throws Exception {
        String s = new String("abc");
        //在这中间可以添加N行代码,但必须保证s引用的指向不变,最终将输出变成abcd
        Field value = s.getClass().getDeclaredField("value");
        value.setAccessible(true);
        value.set(s, "abcd".toCharArray());
        System.out.println(s);
    }
}

value.setAccessible(true);:将此对象的 accessible 标志设置为指示的布尔值。
值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查;
值为 false 则指示反射的对象应该实施 Java 语言访问检查。
实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问为false就不能访问。

注:在JDK11下执行该代码会抛出IllegalArgumentException的异常。

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by interview.day01.ReflexDemo (file:/D:/users/idea-workspace/JaveSE/out/production/JaveSE/) to field java.lang.String.value
WARNING: Please consider reporting this to the maintainers of interview.day01.ReflexDemo
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
Exception in thread "main" java.lang.IllegalArgumentException: Can not set final [B field java.lang.String.value to [C
    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
    at java.base/jdk.internal.reflect.UnsafeQualifiedObjectFieldAccessorImpl.set(UnsafeQualifiedObjectFieldAccessorImpl.java:83)
    at java.base/java.lang.reflect.Field.set(Field.java:780)
    at interview.day01.ReflexDemo.main(ReflexDemo.java:15)

简述:在通过反射修改String 的final字段的时候,出现该IllegalArgumentException异常。

IllegalArgumentException - 如果指定对象不是声明底层字段(或者其子类或实现者)的类或接口的实例,或者解包转换失败

因为JVM在编译时期, 就把final类型的String进行了优化, 在编译时期就会把String处理成常量。

如果你觉得文章对你有帮助,那就请作者喝杯咖啡吧☕
微信
支付宝
  0 条评论