浅谈 Java String

引言

自2020新年伊始,全球经济进入衰退期了。 程序员也是被重锤的较惨啊~
从过往的面试经历中都有被或多或少的问及 String。

just like :

  • String 如何保证不变的?
  • 你能说说String的常用方法吗?
  • 你能说说对“字符串常量池”的理解吗?
  • 你知道 intern 方法吗?他的用法一般用在什么地方?
  • String、StringBuilder、StringBuffer(线程安全)有什么区别吗?

String 如何保证不变的。

在String类对象的定义中

>public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
   
    private final char value[];   
    private int hash; // Default to 0
    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;
    ...
    }

从源码中看出 String 保证不变是通过 final 关键字修饰类使得类不会被继承。以及final 修饰String中对应的属性值,使得String的属性保持不变。

常用方法

subString \ replace \ indexof \ intern …

字符串常量池

运行时常量池(RunTime Constant Pool) 是方法区的一部分(JDK1.7)。用于存放 【编译期】生成的各种字面量和符号引用,这些内容将在类加载后 进入方法区的 运行时常量池中存放。

举个栗子:

public class A{
public static String flag = "hello world";

private String username;
private String name;
// getter  and setter
}

在这个类A中 flag,在类A首次加载后对应的 字符串值将会存储在字符串常量池中。
为何这么设计呢?这么设计有什么好处呢?

好处:
假如我们会初始化很多类A对象,其中 flag 若不存储在 字符串常量池中。则会出现在堆中,使得每个对象堆中都有一个副本的数据存在。倘若我们需要更新flag的值,则需要一个一个更新。这是非常不好的。 所以在java 设计中 通过JVM的模型设计使得字符串常量池能很好的发挥其作用。

由于String一旦 赋值,其对应的值不会改变。意味着其内存地址不会变,也就说明 其hash值不会变。因此 在hashMap中常常用String 作为 key。

public static void TestStringHashValue(){
        String a="abc";
        String b = "abc";
        String c = "abc";

        System.out.println("------test  hash value -----------");
        System.out.println(String.valueOf(a.hashCode()));
        System.out.println("-----------------");
        System.out.println(String.valueOf(b.hashCode()));
        System.out.println("-----------------");
        System.out.println(String.valueOf(c.hashCode()));
        System.out.println("-----------------");
        /**
         * 执行结果
         * ------test  hash value -----------
         96354
         -----------------
         96354
         -----------------
         96354
         -----------------
         * */

    }
上述方法执行结果,则说明 不同对象 a\b\c. 对应的hashcode一样,也说明他们对应到 字符串常量池中的值一样。 还记得上面说的字符串常量池存储的是 【编译期】定义的一些字面量信息内容。

意思便是,形如 String a = “xxxxxx”;这种声明字符串变量,在编译之后 相应的字符串信息最后便会进入 字符串常量池中。这里的编译期 则是,程序员在编写时便可确定的字符串内容。

那 【String b = new String(“aaaaaa”);】这种变不同。这种通过new方式创建的对象则是 在堆上开辟空间存储。那这岂不是和上面说的 编译期能确定的字符串会存入字符串常量池中。

为何new 则不会放入字符串常量池呢。

我要开始装逼了
究其原因是这么做会有风险 存在OOM 内存溢出风险。
可以从2个方面进行说明

假设你堆jvm有一定的认识,正如上面所说 字符串常量池是属于方法区的一部分,且是唯一不变的特性。即声明一个字符串 “a”,那么字符串常量池中便会存在一个“a“。然而方法区是有容量大小限制的,也意味着字符串常量池空间也有大小限制。 意思就是 字符串常量池 不会直接加载 可能超过自身大小的字符串。

不会直接放在 字符串常量池 还有一个原因便是 会破坏new 的语义。都知道new 是在堆开辟一个内存空间创建一个对象,若new String(“xxxx”); 会变成在字符串常量池中 开辟一个内存空间 ‘’‘创建’‘’一个字符串对象。这样是不可取的,所以new String(“xxxx”); 不会直接进入字符串常量池。

如果你还是觉得不清楚。再举个栗子吧

我们知道 String a = “xxxxx”; 会进入字符串常量池 是由于编译器在编译期的时候知道肯定会初始化一个字符串 a,并且对应的值已经确定了。所以会将a 放入字符串常量池中。
如果我们在使用String b = new String(“xxxxx”);这种的时候为何不放入呢?上面2个原因综合一下便可知道。在实际运行当中我们某些String 的体量会非常庞大,例如我们调用某个接口返回一个超大的字符串;从某文件中读取一些字符串信息;面对这些无法确定其长度的字符串,假如直接进去组成常量池。则在后续的形如String a = “xxxxx”;初始化 将直接OOM。

那怎么样才能让new 出来的对象 进入字符串常量池呢?

intern 方法 可以担此大任!!!

String.intern() 是一个Native方法,它的作用是:如果字符串常量池中已经包含一个等于此String 对象的字符串,则返回代表池中String对象;否则,将此String对象包含的字符串添加到字符串中,并且返回此String对象的引用。

你说说 intern 的实际使用场景,或者说你什么时候会用到intern。

它的用途主要是用于一些 内存优化上面。
例如:

public class Person{
String name;
	public void setName(String paramString)
	{
		String str = paramString.intern();
	}
}

这里是一个能展现出inern()实际作用的场景,首先假设我从数据库里读了一个人的信息出来,然后把这个人的名字赋值给这个Person对象.
那么,从数据库读数据,毫无疑问得创建一个字符串对象出来,假定读了10个人的数据,其中三个都叫小明。3个person对应的小明在内存结构中将引用同一份。这大大节省了内存空间,当然在使用的时候 还是需要注意不要造成OOM。

综上其实还有好多相关的知识点是没有说的。例如:重写hash、equals的场景?字节字符?

面试是一个由点及面的过程,好的面试官会试着引导你的。避免错过一个有内涵的程序员。

对String的暂时先写道这里,后续有感了。再来补充

若有写的不对的地方,欢迎评论 私信留言。

大家共勉~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值