黑马程序员技术交流社区

标题: 【上海校区】String为什么需要final修饰 [打印本页]

作者: 小影姐姐    时间: 2018-4-18 11:04
标题: 【上海校区】String为什么需要final修饰
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 = strvalue; } System.out.println(haha); }
效果:
4==4嘻嘻
测试发现,我们通过修改String底层的数组是可以修改String字符串的内容的。






欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2