中国万网网站空间申请,wordpress wp list categories,全国信息企业公示系统,企业网站建设的总体架构其实在Java中#xff0c;String类被final修饰#xff0c;主要是为了保证字符串的不可变性#xff0c;进而保证了它的安全性。那么final到底是怎么保证字符串安全性的呢#xff1f;接下来就让我们一起来看看吧。
一. final的作用
1. final关键词修饰的类不可以被其他类继…其实在Java中String类被final修饰主要是为了保证字符串的不可变性进而保证了它的安全性。那么final到底是怎么保证字符串安全性的呢接下来就让我们一起来看看吧。
一. final的作用
1. final关键词修饰的类不可以被其他类继承但是该类本身可以继承其他类通俗地说就是这个类可以有父类但不能有子类。 1 2 3 final class MyTestClass1 { // ... }
2. final关键词修饰的方法不可以被覆盖重写但可以被继承使用。 1 2 3 4 5 class MyTestClass2 { final void myMethod() { // ... } } 3. final关键词修饰的基本数据类型被称为常量只能被赋值一次。 1 2 3 class MyTestClass3 { final int number 100; } 4. final关键词修饰的引用数据类型变量其值为地址值该地址值不能改变但该地址对应的数据对象可以被改变其实这一点就和我们今天要说的内容有关了在后面我会结合案例跟大家重点解释大家一定要打起精神仔细学习哦。 5. final关键词修饰的成员变量需要在创建对象前就赋值否则会报错(即需要在定义时直接赋值)。 综上所述我们可以知道final在Java中是一个非常有用的关键字主要可以提高我们代码的稳定性和可读性。当然我们今天要讲解的重点是被final修饰的String类所以接下来我们还是把目光转回到String身上来看看String都有哪些特性吧 二. 被final修饰的String类
为了让大家更好地理解String的不可变性首先我要给各位简要地讲一下String的源码设计。从下面的这段源码中我们可以搞清楚很多底层的设计思路接下来就请大家跟着我一起来看看String的核心源码吧。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 /** * ......其他略...... * * Strings are constant; their values cannot be changed after they * are created. String buffers support mutable strings. * Because String objects are immutable they can be shared. For example: * * ......其他略...... * */ public final class String implements java.io.Serializable, ComparableString, CharSequence { ...... 我先把上面的源码及其注释给大家作一个简单的解释
● final请参考第1小节对final特点的介绍
● Serializable用于序列化
● ComparableString默认的比较器
● CharSequence: 提供对字符序列进行统一、只读的操作。
从这段源码及其注释中我们可以得到下面这些结论
● String类用final关键字修饰说明String不可被继承
● String字符串是常量字符串的值一旦被创建就不能被改变
● String字符串缓冲区支持可变字符串
● String对象是不可变的它们是可以被共享的。 三. String的不可变性
在学习了上面的这些核心源码之后接下来我们可以通过一个案例来实践验证一番看看String字符串的内容到底能不能改变。这里有个代码案例如下图所示 在上述的案例结果中大家可以看出s的内容竟然发生了改变但我们不是一直说String是不可变的吗这是咋回事大家先别急我们继续往下看。
要想弄明白这个问题我们首先得知道一个知识点引用和值的区别
在上面的代码中我们先是创建了一个 yiyige 为内容的字符串引用s如下图 s其实先是指向了value对象而value对象又指向了存储 yiyige 字符的字符数组。但因为value被final修饰所以value的值不可被更改。因此上面代码中改变的其实是s的引用指向而不是改变了String对象的值
换句话说上面实例中s的值其实只是value的引用地址并不是String的内容本身。当我们执行 s yyg 语句时Java会创建一个新的字面量对象 yyg而原来的 yiyige 字面量对象其实依然存在于内存的intern缓存池中。
在这里String对象的改变实际上是通过内存地址的“断开-连接”变化来完成的。在这个过程中原字符串中的内容并没有发生任何的改变。String s yiyige 和 s yyg这两行代码实质上是开辟了2个内存空间s只是由原来指向 yiyige 变为指向 yyg 而已而其原来的字符串内容是没有发生改变的如下图所示。 因此我们在以后的开发中如果要经常修改字符串的内容请尽量少用String因为如果字符串的指向经常的“断开-连接”就会大大降低性能我建议大家使用StringBuilder 或 StringBuffer 进行替换。
我们继续把上面的代码深入地分析一下。在Java中因为数组也是对象 所以value中存储的也只是一个引用它指向一个真正的数组对象。在执行了String s “yiyige”; 这句代码之后真正的内存布局应该是下图这样的 因为value是String封装的字符数组value中所有的字符都属于String这个对象。而由于value是private的没有提供setValue等公共方法来修改这个value值所以我们在String类的外部是无法修改value值的也就是说字符串一旦初始化就不能再被修改。
此外value变量是final修饰的也就是说在String类内部一旦这个值初始化了value这个变量所引用的地址就不会改变了即一直引用同一个对象。正是基于这一层我们才说String对象是不可变的对象。
所以String的不可变其实是指value在栈中的引用地址不可变而不是说常量池中value字符数组里的数据元素不可变。也就是说value所引用的数组对象里的内容其实是可以发生改变的。
那么我们又如何改变它呢这就要通过反射来消除String类对象的不可变性啦 四. String真的不可变吗
在上述内容中我们重点给大家解释了String字符串的可变性。现在大家应该已经知道了String字符串的内容其实是可变的不可改变的只是String字符串的对象地址。那么我们到底该怎么让String字符串的内容发生改变呢在上述我们给大家提到了反射接下来我们就来看看如何通过反射改变String字符串的内容吧。代码案例如下所示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 try { String str yyg; System.out.println(str str , 唯一性hash值 System.identityHashCode(str)); Class stringClass str.getClass(); //获取String类中的value属性 Field field stringClass.getDeclaredField(value); //设置私有成员的可访问性,进行暴力反射 field.setAccessible(true); //获取value数组中的内容 char[] value (char[]) field.get(str); System.out.println(value Arrays.toString(value)); value[1] z; System.out.println(str str , 唯一性hash值 System.identityHashCode(str)); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } 执行结果如下图所示 从上面的结果中我们可以看到String字符串的字符数组通过反射进行修改后字符串的“内容”真的发生了变化
并且我们又利用底层的java.lang.System#identityHashCode()方法(不管是否重写了hashCode方法)来获取到了该字符串对象的唯一哈希值该方法获取的hash值与hashCode()方法是一样的。
从结果中我们可以看到两个字符串的唯一hash值是一样的这就证明字符串的引用地址没有发生改变。
所以这就说明我们并不是像之前那样创建了一个新的String字符串而是真的改变了原有String的内容。
这个代码案例进一步证明了我们上面的结论String字符串的不可变指的其实是value对象在栈中的引用地址不可变而不是说常量池中value里的数据元素不可变简单地说就是String字符串的内容其实是可以改变的不能改表的是它的对象地址而已。 所以这也就是我们上述所说的final的作用之一final关键词修饰的引用数据类型的变量其值为地址值地址值不能改变但是地址内的数据对象可以被改变 五. 总结
至此我们就把今天的面试题分析完了现在你明白了吗最后我再来给大家总结一下今天的重点内容吧
1. 为什么要用final修饰java中的String类呢
核心因为它确保了字符串的安全性和可靠性。
2. java中的String真的不可变吗
核心String字符串的内容其实是可变的但要通过特殊手段进行实现不可改变的是String字符串对象的地址。
3. 如何消除String类对象的不可变性
核心利用反射来消除String类对象的不可变性。
4. 如果想要保证String的不可变要注意哪些
● 首先将 String 类声明为 final类型。这意味着String类是不可被继承的防止程序员通过继承重写String类的某些方法使得String类出现“可变的”的情况
● 然后重要的字符数组value属性要被private 和 final修饰。它是String的底层数组用于存贮字符串内容。又因为数组是引用类型所以只能限制引用不被改变也就是说数组元素的值是可以改变的这在上面的案例中已经证明过了
● 接着所有修改的方法都返回新的字符串对象保证修改时不会改变原始对象的引用
● 最后不同的字符串对象都可以指向缓存池中的同一个字符串字面量。
当然“Java中的String类使用final修饰”这个概念非常重要因为它确保了字符串的安全性和可靠性。但是我们也要清楚不可改变的只是它的地址而不是它的内容它的内容是可以利用反射来改变的只不过在一般的描述中大家都会说String内容不可改变毕竟很多时候是不允许利用反射这种特殊的功能去进行这样的操作的。