为什么字符串如此重要

写了多年java的开发应该对String不陌生,只是越来越觉得陌生。 每次学习一门编程语言,都会和关键字串打交道很多。 看来这真的很重要。

字符串是一系列字符串。 如果你写过C/C++,你应该知道有很多用于字符串操作的函数和类来简化代码开发。 一方面是因为代码中经常使用字符串,另一方面是因为字符串的操作非常麻烦。

起初我知道在delphi中对String的特殊处理,因为String在delphi中是关键字,与其他基本类型不同。 那时候,我学到了很多相关的知识。 在java/.net中,字符串也是经过特殊处理的,可见其重要性。

只是因为程序中字符串的使用比较多,操作比较多,这样会带来内存占用和性能问题。 需要特别小心。 想象一下日志系统一天使用多少字符串变量。

了解 Java 中的字符串

Java提供了String类的功能来支持字符串。 毕竟字符串本质上就是字符的组合,那么我们就来看看它的特点吧。

String还是将字符串存储在一个char数组中java如何判断字符串是否为纯数字,数据操作都是围绕着它进行的,但是有一些特别的地方,代码如下

private final char value[];

可以发现这个char value[]是finalized的,也就是说这个值一旦创建就不能再改变了。 这将导致每次创建 String 时只有一个值,然后对其进行字符串操作也必须生成新值。 Java 使用字符串常量池的概念来进行此处理。 就是把字符串丢到一个池子里,如果相同就用同一个。当然这也有一个前提,就是要用下面的方法

String s = "abc";

这样做时,jvm 会在编译时确定它。 在运行时,它会先检查常量池中是否有“abc”。 这样做的好处是不需要为同一个字符串创建重复项。但是如果使用下面的代码

String s1 = new String("abc");

这时,情况发生了变化。 这里jvm会在栈中创建一个对象s1,但是s1中的值也指向“abc”。 稍后在查看字符串比较时,您会发现不同之处。

String s = "abc";
String s1 = "abc";
if (s == s1) {
    System.out.println("s == s1");
}

问题:此时是s==s1吗?

答案是一样的,为什么呢? 其实jvm在创建s1的时候会去常量区找是否有相同值的字符串,如果有就返回给s1,这样s1和s就指向同一个字符串,所以是平等的。

但是还有一种情况不一样,

String s = "abc";
String s3 = new String("abc");
if (s == s3) {
	System.out.println("s == s3");
}
else {
    System.out.println("s != s3");
}

这时候应该会打印出s != s3,因为new一个String对象之后确实会创建一个新的变量。 所以如果用==来比较,自然会返回false。

用equals做比较怎么样?

String s = "abc";
String s2 = new String("abc");
if (s.equals(s2)) {
	System.out.println("s = s2");
}
else {
    System.out.println("s != s2");
}

打印出来的是s = s2,因为==是用来比较两个地址的java如何判断字符串是否为纯数字,equals是用来比较两个变量的值的。可以看看equals的代码

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

equals中先比较地址是否相同,如果不相同再比较值,因为值都是“abc”,自然会返回true。

String中有一个intern方法,我们可以先试试下面的代码。

js判断是否为数字_java如何判断字符串是否为纯数字_c++判断字符是否为ascii

String s = "abc";
String s3 = new String("abc");
if (s.intern() == s3.intern()) {
	System.out.println("s.intern = s3.intern");
}
else {
    System.out.println("s.intern != s3.intern");
}

还是在s和s3上面,如果比较它们各自的intern方法返回的值,就会输出s.intern = s3.intern。 查找资料结合笔记得知,这个intern方法其实是从字符串常量池中返回当前字符串。 如果当前字符串已经存在,则返回当前字符串。 如果当前字符串不存在,则返回当前字符串。 放入常量池并返回。

这么一解释就明白了,s和s3都是intern返回的,所以在常量池中都是“abc”,所以比较起来intern是相等的。

认识 StringBuffer 和 StringBuilder

面试的时候遇到这个问题,突然有点懵。 没怎么关注这两个类,记得java里面只有一个StringBuffer? 回头看代码,原来StringBuffer是线程安全的,即字符串操作的所有方法都是同步的。

于是打开代码注释,发现Jdk1.5开始有了StringBuilder,并且在后面的版本中增加了一个unlocked类。 好像解决了非并发场景下的效率问题。 解锁对于操作大字符串还是很有用的。 性能增强。 好奇的看了下这两个类的代码,和String有些类似,只是此时chat[]不是final,避免调用时生成一堆string对象操作字符串类。 问题。

java如何判断字符串是否为纯数字_js判断是否为数字_c++判断字符是否为ascii

char[] value;

既然我们已经有了String,那这两个家伙有什么用呢? 其实问题还是跟String的原理有关。 因为String是通过常量池来管理的,这样就解决了重复创建同一个字符串的问题,但是大部分的字符串都是不一样的,尤其是在做字符串拼接操作的时候,如果使用String的+来拼接大量的字符串常量会生成,这会消耗大量的性能和空间。

为了解决这个问题,使用了StringBuffer。 本质上,通过一个可变的字符序列,在字符串操作时不需要产生新的对象,从而提高了内存的使用率。

让我们看看 StringBuffer 是如何提高拼接性能的。 查看StringBuffer/StringBuilder(JDK1.5+)的代码,发现都是继承自AbstractStringBuilder。 很多代码其实都是在AbstractStringBuilder中完成的。 因为这个问题是拼接引起的,所以这里重点说一下append方法。

public AbstractStringBuilder append(String str) {
    if (str == null)
        return appendNull();
    int len = str.length();
    ensureCapacityInternal(count + len);//确定容量
    str.getChars(0, len, value, count);//取出str的字符放入到value数组中
    count += len;//count累加
    return this;
}

代码比较清晰。 整个过程中最重要的是使用String的getChars方法将str的值写入到当前对象的值中。 而String的getChars方法如下:

public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
    if (srcBegin  value.length) {
        throw new StringIndexOutOfBoundsException(srcEnd);
    }
    if (srcBegin > srcEnd) {
        throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
    }
    System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}

可以看出最后复制的是一个数组,因为AbstractStringBuilder中的value是一个可变的char数组,所以只需要对char数组进行字符串操作即可。 它不会像String那样产生新的对象,自然就变得更高效了。