String为什么需要final修饰 String类作为一个在Java世界使用最常用的类,我们往往忽视它的一些特点,今天我们来研究一下String为什么需要final修饰,当前研究的String类基于jdk9.0.1版本。 1、使用final修饰是为了安全 我们先来做三个简单的逻辑推理: 第一:Sun公司设置String Final是不可变的一定是对的 第二:因此,如果String是可变,一定的是错误的 第三:StringBuilder是可变的,那么我们接下来看看StringBuilder有什么问题 演示StringBuilder的问题: [AppleScript] 纯文本查看 复制代码 @org.junit.Test public void test1(){ //设置一个不重复的集合 HashSet<StringBuilder> hs = new HashSet<>(); //设置两个字符串缓冲区 StringBuilder sb1 = new StringBuilder("aaa"); StringBuilder sb2 = new StringBuilder("aaabbb"); //将两个缓冲区存入集合 hs.add(sb1); hs.add(sb2); // 这时候HashSet里是{"aaa","aaabbb"} System.out.println(hs); //创建第三个字符串缓冲区 StringBuilder sb3=sb1; //添加数据 sb3.append("bbb"); //这时候HashSet里是{"aaabbb","aaabbb"} System.out.println(hs); } 执行效果: [aaa, aaabbb][aaabbb, aaabbb]这个结果是惊悚的!!!! 因为Set集合是不重复的——但是,现在重复了!!!!! 我们在替换成String来测试一下: [AppleScript] 纯文本查看 复制代码 @org.junit.Test public void test2() { //设置一个不重复的集合 HashSet<String> hs = new HashSet<String>(); //设置两个字符串 String sb1 = new String("aaa"); String sb2 = new String("aaabbb"); hs.add(sb1); hs.add(sb2); // 这时候HashSet里是{"aaa","aaabbb"} System.out.println(hs); //创建第三个字符串 String sb3=sb1; //添加数据 sb3 = sb3+"bbb"; //这时候HashSet里是{"aaabbb","aaabbb"} System.out.println(hs); } 效果: [aaa, aaabbb][aaa, aaabbb]使用String不会破坏set集合的唯一性,因此,String设置final是合理的。 在使用StringBuilder的时候,因为StringBuilder没有final,所以我们在将数据添加进集合之后,使用引用数据类型的特点,绕过了set集合保存数据的校验,将数据修改之后,出现了重复的情况。 2、 使用final修饰是为了效率 2.1 从线程安全考虑 Final数据不可改变,没有改写操作,多线程操作数据,在只有读取的情况下没有线程安全问题 2.2 从节省内存考虑 演示代码: [AppleScript] 纯文本查看 复制代码 @org.junit.Test public void test3() { String haha = "上海传智播客"; System.out.println(haha.hashCode()); String hehe = "上海传智播客"; System.out.println(hehe.hashCode()); } 执行效果: 14620333081462033308相同内容的String字符串,在内存中只保存一份,后期所以的变量都获取这一份字符串。 3、Final修饰的字符串是否有漏洞 需求:修改String字符串的底层字节数组 代码演示: [AppleScript] 纯文本查看 复制代码 @org.junit.Test public void test4() throws Exception { String haha = "哈哈"; System.out.println(haha.hashCode()); String hehe = "哈哈"; System.out.println(hehe.hashCode()); //通过反射获取当前String底层保存数据的数组value Field field = hehe.getClass().getDeclaredField("value"); //打开暴力访问 field.setAccessible(true); //获取haha与hehe字符串底层数据 byte[] value = (byte[]) field.get(haha); String str = "嘻嘻"; //通过反射获取当前str字符串底层保存数据的数组value Field strfield = str.getClass().getDeclaredField("value"); //打开暴力访问 strfield.setAccessible(true); //获取String底层数据 byte[] strvalue = (byte[]) field.get(str); //查看两者是否长度一致 System.out.println(value.length+"=="+strvalue.length); //将str数字复制给hehe与haha,打破String的不可修改。 for (int i = 0; i < value.length; i++) { value[i] = strvalue[i]; } System.out.println(haha); } 效果: 4==4嘻嘻测试发现,我们通过修改String底层的数组是可以修改String字符串的内容的。
|