Java 字符串处理的一些小细节

  1. 云栖社区>
  2. 博客>
  3. 正文

Java 字符串处理的一些小细节

王亟亟 2015-11-24 16:50:00 浏览807
展开阅读全文

转载请注明出处:王亟亟的大牛之路

讲今天的内容之前温故一些理论知识?(部分理论知识来源于网上,谢谢开源大家庭)

1.什么是指针?
—指针是指向内存中的地址,该地址就是存储变量的值。

2.Java中没有了指针,那以什么东西来替代指针相应的功能?
—java中我们所谓的对象引用就是指针,只是没有像C/C++中给出了一个明确的定义。java是为了避免指针带来的使用上的麻烦,所以就使用对象的引用来代替了指针。

3.那么这些对象的引用又存放在哪?
—java中的内存分为堆内存(heap)和栈内存(stack)。堆就是用来存放对象的,而栈则是存放一些数据基本类型的值。


一.引用

平时字面量创建就是简单的

String wjj = "Hello";

JVM检测这个字面量,这里我们认为没有内容为Hello的对象存在。JVM通过字符串常量池查找不到内容为Hello的字符串对象存在,那么会创建这个字符串对象,然后将刚创建的对象的引用放入到字符串常量池中,并且将引用返回给变量wjj。

然后又给第二个对象赋值,如:

String wjj1 = "Hello";

因为在我们的常量池中已经有了hello这个对象,所以把已经存在的字符串对象的引用返回给变量wjj1。

测试下这两个对象指向同一个Hello:

 String Wjj= "Hello";
 String Wjj2="Hello";
 System.out.println(Wjj==Wjj2);
 System.out.println(Wjj.equals(Wjj2));
 结果:    
 true
 true

那除了直接 String Wjj= “Hello”;我们还用 new String的 方式,像这样:

  String Wjj2=new String("Hello");

结果还是一样的给我们的Wjj2这个对象赋值了Hello,那么打印的结果会有区别么?

  String Wjj= "Hello";
  String Wjj2=new String("Hello");
  System.out.println(Wjj==Wjj2);
  System.out.println(Wjj.equals(Wjj2));

  结果:
  false
  true

“==”和equals的区别只后会再加解释,基础还是很重要的哈哈!

我们可以看到当我们使用了new来构造字符串对象的时候,这两个变量指向的为不同的对象,不管字符串常量池中有没有相同内容的对象的引用,新的字符串对象都会创建


二.修改值相同但地址不同的引用

对于上面使用new创建的字符串对象,如果想将这个对象的引用加入到字符串常量池,可以使用intern方法。

调用intern后,首先检查字符串常量池中是否有该对象的引用,如果存在,则将这个引用返回给变量,否则将引用加入并返回给变量。

String Wjj1=Wjj.intern();
System.out.println(Wjj1==Wjj);

结果:
true

-概念:
1.字符串常量池中存放的时引用还是对象,这个问题是最常见的。字符串常量池存放的是对象引用,不是对象。在Java中,对象都创建在堆内存中。

2.因为字符串常量池中持有了共享的字符串对象的引用,这就是说是不是会导致这些对象无法回收?
首先问题中共享的对象一般情况下都比较小。据我查证了解,在早期的版本中确实存在这样的问题,但是随着弱引用的引入,目前这个问题应该没有了。


那么String Wjj3="a"+"b"+"c";又等于"abc"么?

   String Wjj= "abc";
   String Wjj2="a"+"b"+"c";
   System.out.println(Wjj==Wjj2);
   System.out.println(Wjj.equals(Wjj2));
   结果:
   true
   true

其实是在编译之前就已经进行了优化所以他们指向了同一个对象


三.String对象

在Java中,String对象是不可变的(Immutable)。在代码中,可以创建多个某一个String对象的别名。但是这些别名都是的引用是相同的。
比如wjj2和wjj都是”hello”对象的别名,别名保存着到真实对象的引用。所以wjj2 = wjj

 String Wjj= "hello";
 String Wjj2=Wjj;
 System.out.println(Wjj==Wjj2);
 System.out.println(Wjj.equals(Wjj2));
 结果:
 true
 true

在Java中,唯一被重载的运算符就是字符串的拼接相关的。+,+=。除此之外,Java设计者不允许重载其他的运算符。

字符串操作的性能问题

  String s1="a" ;
  String s2="b";
  String s3="c";
  String s4=s1+s2+s3;
  System.out.println(s4);
  结果:
  abc

要得到上面的S4,就会S1和S2拼接生成临时一个String对象t1,内容为ab,然后有t1和s3拼接生成最终我们需要的s4对象,这其中,产生了一个中间的t1,而且t1创建之后,没有主动回收,势必会占一定的空间。如果是一个很多(假设上百个,多见于对对象的toString的调用)字符串的拼接,那么代价就更大了,性能一下会降低很多。

编译器的优化处理

真的会有上面的性能代价么,字符串拼接这么常用,没有特殊的处理优化么,答案是有的,这个优化进行在编译器编译.java到bytecode时。

一个Java程序如果想运行起来,需要经过两个时期,编译时和运行时。在编译时,Java 编译器(Compiler)将java文件转换成字节码。在运行时,Java虚拟机(JVM)运行编译时生成的字节码。通过这样两个时期,Java做到了所谓的一处编译,处处运行。

Java编译器做的优化,当Java编译器遇到字符串拼接的时候,会创建一个StringBuilder对象,后面的拼接,实际上是调用StringBuilder对象的append方法。这样就不会有我们上面担心的问题了。

SO,我们来测试一下:

    public static void main(String[] asa) {
        String value="";
        long a=System.currentTimeMillis();
        for(int k=0;k<9000;k++){
            value=value+k;
        }
        System.out.println("a的时间差= "+(System.currentTimeMillis()-a));

        StringBuilder stringBuilder=new StringBuilder();
        long b=System.currentTimeMillis();
        for(int f=0; f<9000; f++){
            stringBuilder.append(f);
        }
        System.out.println("b的时间差="+(System.currentTimeMillis()-b));
    }

结果:
a的时间差= 1137
b的时间差=4

性能差距明显哦!!!

网友评论

登录后评论
0/500
评论
王亟亟
+ 关注