您的位置:首页 > 编程语言 > Java开发

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

2017-01-16 09:05 274 查看


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


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

1.什么是指针?

—指针是指向内存中的地址,该地址就是存储变量的值。

2.Java中没有了指针,那以什么东西来替代指针相应的功能?

—java中我们所谓的对象引用就是指针,只是没有像C/C++中给出了一个明确的定义。java是为了避免指针带来的使用上的麻烦,所以就使用对象的引用来代替了指针。

3.那么这些对象的引用又存放在哪?

—java中的内存分为堆内存(heap)和栈内存(stack)。堆就是用来存放对象的,而栈则是存放一些数据基本类型的值。


一.引用

平时字面量创建就是简单的
String wjj = "Hello";
1
1
[/code]

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

然后又给第二个对象赋值,如:
String wjj1 = "Hello";
1
1
[/code]

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

测试下这两个对象指向同一个Hello:
String Wjj= "Hello";
String Wjj2="Hello";
System.out.println(Wjj==Wjj2);
System.out.println(Wjj.equals(Wjj2));
结果:
true
true
1
2
3
4
5
6
7
1
2
3
4
5
6
7
[/code]

那除了直接 String Wjj= “Hello”;我们还用 new String的 方式,像这样:
String Wjj2=new String("Hello");
1
1
[/code]

结果还是一样的给我们的Wjj2这个对象赋值了Hello,那么打印的结果会有区别么?
String Wjj= "Hello";
String Wjj2=new String("Hello");
System.out.println(Wjj==Wjj2);
System.out.println(Wjj.equals(Wjj2));

结果:
false
true
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
[/code]

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

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


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

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

调用intern后,首先检查字符串常量池中是否有该对象的引用,如果存在,则将这个引用返回给变量,否则将引用加入并返回给变量。
String Wjj1=Wjj.intern();
System.out.println(Wjj1==Wjj);

结果:
true
1
2
3
4
5
1
2
3
4
5
[/code]

-概念:

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
1
2
3
4
5
6
7
1
2
3
4
5
6
7
[/code]

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


三.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
1
2
3
4
5
6
7
1
2
3
4
5
6
7
[/code]

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

字符串操作的性能问题
String s1="a" ;
String s2="b";
String s3="c";
String s4=s1+s2+s3;
System.out.println(s4);
结果:
abc
1
2
3
4
5
6
7
1
2
3
4
5
6
7
[/code]

要得到上面的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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[/code]

性能差距明显哦!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 字符串