Java基础_字符串

来源:互联网 发布:linux运维简历 编辑:程序博客网 时间:2024/06/06 09:00

Java基础_字符串

标签(空格分隔): java基础


Java核心&Java编程思想读书笔记

Java字符串就是Unicode字符序列。Java没有内置的字符串类型,而是在标准库中提供了一个类 java.lang.String,我们看到的所有被双引号括起来的字符串都是String类的一个实例

创建字符串

创建一个字符串有两种方式

        String str = "this is a string";        String  s = new String("this is a string too");

最后得到的str以及s都是字符串,但是两者稍微有一些不同
对于前者而言

        String str1 = "this is a string";        String str2 = "this is a string";        System.out.println(str1==str2);//true        System.out.println(str1.equals(str2));//true

对于后者而言

        String str1 = new String("this is a string");        String str2 = new String("this is a string");        System.out.println(str1==str2);//false        System.out.println(str1.equals(str2));//true

这是因为前者的创建方式中,JVM会去字符串池(String Pool)查找”this is a string”,这个对象,不存在则创建,存在则直接将地址给予str,这也就是为什么第一种方式即使使用==比较地址也会得到相同的结果,因为它们本身就是指同一个地址

后者的创建方式中使用了new构造器的方式创建字符串,会在堆中创建出”this is a string”对象,再根据string pool是否存在选择性创建,显而易见的是,最后str指向的是堆内存的地址,因此使用==对比时是不相等的。


字符串不可变

String的对象不可变,我们所进行的修改操作其实并没有改变String的对象,而是创建了一个新的String对象。不可变性对于拼接字符串确实带来效率的下降,但是也带来了好处,还是刚才提到的string pool共享池,字符串变量指向池中的对象,大家共享它,这样如果进行复制或者判断之类的操作效率就要高很多。事实证明,对于字符串的修改操作要小于字符串的比较操作。

ps:GC不会回收共享池中的字符串对象


重载+与StringBuilder

String对象不可变,因此我们可以为String对象起任意多的名字(句柄),他们不会影响到String对象本身。
那么回到最开始的问题,假如我们使用+=拼接大量字符串,由于字符串的不可变性,那么就会产生大量的中间对象,而且这些对象始终不会被我们用到,这样一定会大大降低程序的性能,事实真的就是如此么?
创建以下代码

public class StringTest{    public static void main(String args[]){        String str = "my";        String result = str+" name"+" is"+" cjmust.";        System.out.println(result);//my name is cjmust.    } }

ok,根据我们的分析,上述代码至少创建了两个无用的中间对象,接下来我们看一看它到底是怎么运行的。
首先使用javac 命令将.java文件编译为.class 文件

javac StringTest.java

然后使用javap -c 命令生成JVM字节码

javap -c StringTest

获得字节码文件如下

public class StringTest {  public StringTest();    Code:      0: aload_0      1: invokespecial #1                  // Method java/lang/Object."<init>":()V      4: return  public static void main(java.lang.String[]);    Code:      0: ldc          #2                  // String my      2: astore_1      3: new          #3                  // class java/lang/StringBuilder      6: dup      7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V      10: aload_1      11: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;      14: ldc          #6                  // String  name is cjmust.      16: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;      19: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;      22: astore_2      23: getstatic    #8                  // Field java/lang/System.out:Ljava/io/PrintStream;      26: aload_2      27: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V      30: return}

即使不能完全看懂我们也可以猜一猜发生了什么,其实注释写得很清楚了:new指令只出现了一次,也就是说它可能只创建了一个对象,再根据后面的注释 java.lang.StringBuilder,可以认为JVM创建了一个StringBuilder的对象,然后又调用了该对象的append方法,然后又使用toString()方法转换为String类型之后,输出~

以上例子说明,当我们使用String直接拼接的时候,其实JVM会替我们进行某种程度上的优化。但是不要因为JVM会优化就可以随意使用String类型,尤其是在使用循环拼接字符串的时候,因为每次拼接JVM都会新创建出一个StringBuilder对象,而假如我们自己一开始就使用StringBuilder的append方法的话,那么StringBuilder对象就只有一个

补充:还有一个和StringBuilder类似的类,StringBuffer,该类线程安全,不过性能开销会稍微大些。

无意的递归

留个坑:容器类,集合类

补充:一个常识,所有Java类都继承自Object类,容器类当然也源于Object,又因为容器类中都有重写后的toString()方法,因此直接输出容器类对象而得到的是容器被重写后的结果,一般来说是容器中的内容物,比如我们使用ArrayList的toString()方法,其实就是遍历了ArrayList中的所有内容并且调用它们的toString()方法。
其实我们使用Sysout输出对象时,就是调用了它的toString()方法,那么我们创建一个对象,重写toString()方法时使其输出它的地址。

public class Test {    @Override    public String toString() {        // 重写改类的toString方法,使其输出自己的内存地址        return ("address"+this);    }}

当我们创建好对象并输出时,会出现一个Exception
Exception in thread "main" java.lang.StackOverflowError
这是因为在遇到一个字符串使用+ this时发生类型转换,编译器要把this转换为String类型,为了转换,又调用了它的toString方法,而再次调用的话又会出现一个+this,又会调用这个this的toString方法…很显然这样会发生递归。
如果真正想要安全地输出地址的话,使用eclipse自动创建出的形式即可,也就是调用Object的toString方法

    @Override    public String toString() {        // TODO Auto-generated method stub         return super.toString();    }

空串与Null串

空串是长度为0的字符串

String str = "";

长度为0

if(str.length()==0)    //true 

无内容

if(str.equals(""))    //true

还有Null,内容为空,此时调用调用该字符串的方法会产生空指针异常

String str = nullif(str==null)   //true

最为常用的用法,判断一个字符串既不是”“,也不是Null

if(str!=null && str.length()!=0)   

注意两个判断不能换位置,将str本身是否为Null放在前面,因为&&运算符短路的原因可以避免空指针异常,许多对象的判断都应该采用这种思路

码点与代码单元

字符串的本质是char值序列,而char数据类型是一个采用UTF-16编码表示Unicode码点的代码单元。

当我们调用length()方法时,返回的数字是UTF-16编码表示给定字符串所需要的代码单元数量,想要获得真正的长度,即码点数量,需要调用codePointCount(int x,int y)方法

String str = "Hello";int trueLength = str.codePointCount(0,str.length());

格式化输出

Java的格式化输出从JDK5开始得到大家的喜爱

System.out.printf();

可以使用多个参数,格式化输出的内容

以下为坑,更新后修正

String API

正则表达式

十六进制转储工具

原创粉丝点击