Java字符串String

我们知道Java的字符窜是Immutable(不可变)的,一旦创建就不能更改其内容了;平常我们对字符串的操作是最多的,其实对字符串的操作,返回的字符串都是新建的字符串对象,原来并没有被改动,这跟C#是一模一样的;

既然字符串是不可变量,当我们对字符串进行各种操作时的效率肯定是有影响的,比如我们平时最常用的 + 运算符:

public class ConcatString{
public static void main(String[] args) {
var name = "marson";
var s = "abc" + name + "shine" + 47+ "nancy" + "summer zhu";
print(s);
}
}

这段代码我相信在我们日常开发中很容易遇见,它这里还没开始相加,就开辟了6段字符串对象,然后+起来又形成新的String对象,所以可以想象,当我们遇到大量(长度未可知且预知高于一定值的)字符串拼接时,会产生多少新的对象,对内存,性能造成不小的影响。

所以这时候就有了StringBuilder

StringBuilder

StringBuilder的目的就是为了解决String的不变量的问题的,StringBuilder在内部维护初始容量为16(可动态扩展)的对象,它是一个变量,所以它append字符串时返回的对象是同一个。所以存在大量的字符串拼接时,StringBuilder是可以明显优于String;

在JAVA SE5前,StringBuffer充当StringBuilder的角色,但是StringBuffer是线程安全的,细扣源码就会发现,里面含有大量的关键字synchronized,所以性能开销也比较大。

下面是StringBuilder的demo

public void UsingStringBuilder(){
var sb = new StringBuilder();
sb.append("abc").append("marson").append("shine")
.append("summer").append("zhu");
System.out.println(sb);
}

StringBuilder隐藏的陷阱

下面我们来学习《Java编程思想》一书中提到的一种StringBuilder场景,贴下代码:

public class InfiniteRecursion {
@Override
public String toString() {
return "InfiniteRecursion address: " + this + "\n";
} public static void main(String[] args) {
var v = new ArrayList<InfiniteRecursion>();
for (int i = 0; i < 10; i++) {
v.add(new InfiniteRecursion());
}
System.out.println(v);
}
}

这种情况稍微不注意,就会犯下上面这段代码一样的错误——StackOverflowError

这是由于无限递归导致的堆栈内存溢出的错误,因为InfinitialRecursion类复写了toString,并且返回一个字符串+拼接操作符。尽管拼接的对象是this对象,但是由于是字符串的拼接,所以jvm会自动转型为String类型,从而再次调用toString,最后导致错误出现。

关于字符串池——intern

Java关于字符串对象,其实有一个装载字符串的容器——字符串池(pool of strings),新建的String对象,只要池中不存在,那么就可以存进去,并生成唯一个引用,当我们新建一个内容一样的字符串内容,我们可以直接引用池中的字符串对象,进而减小新建字符串带来的开销提高应用程序性能,而String的实例方法intern就是这个作用:

public class StringIntern {
public static void main(String[] args) {
var s = "MarsonShine";
var ss = new String("MarsonShine");
var sss = ss.intern();
System.out.println("s == ss: " + (s == ss));// false
System.out.println("s == sss: "+(s == sss));// true
System.out.println("ss == sss: "+(ss == sss));// false
}
}

String VS StringBuilder

最后我们来比较一下String与StringBuilder拼接字符串的性能对比来结束我们这个话题

public class StringVsStringBuilder {
private static final String INIT_STRING = "abcdefghijklmn1234567890"; public static void main(String[] args) {
var sw = new Stopwatch();
sw.start();
var str = "";
for (int i = 0; i < 100000; i++) {
str += INIT_STRING;
}
sw.end();
System.out.println("String + 运行时间:" + sw.ElapsedMilliseconds() + " ms"); sw.restart();
var sb = new StringBuilder();
for (int i = 0; i < 100000; i++) {
sb.append(INIT_STRING);
}
sw.end();
System.out.println("StringBuilder append 运行时间:" + sw.ElapsedMilliseconds() + " ms");
}
}

这个类里面分别用String,StringBuilder对定长的字符串对象INIT_STRING多次拼接

测试结果肯定也如大家所料,后者时间要远远小于前者的。但是当拼接的字符串比较少时,其差别就微乎其微了,理论上在少量字符串的拼接过程中,StringBuilder的性能是要逊色于String的,但是在我电脑上经过大量的测试,发现StringBuilder的性能始终要强与String的,我都有些怀疑是不是我Stopwatch辅助类写错了 - -;

最后我来把这个段代码附上吧

package performance;

public class Stopwatch {
private long startTime;
private long endTime;
public void start(){
startTime = System.currentTimeMillis();
}
public void end(){
endTime = System.currentTimeMillis();
}
public void restart(){
startTime = System.currentTimeMillis();
}
public long ElapsedMilliseconds(){
return endTime - startTime;
}
}

后记

因为我想弄清楚Java中的+操作符实际上是怎么调用的,运行的过程是怎么样的,是不是跟C#一样调用的是concat方法?

后来我通过反编译java代码发现string的+操作符在JVM变成了动态指令调用:

invokedynamic 指令去调用java.lang.invoke.makeConcatWithConstants方法,然后根据MethodHandler以及MethodType信息生成CallSite信息去执行具体的函数,但就CallSite调用那个过程我没搞清楚,调试断点也摸不清楚(Idiea玩不转 - -)

有了解的同学希望告诉我下_

Java字符串String的更多相关文章

  1. Java字符串String 集合的迭代器

    Java字符串String 我们知道Java的字符窜是Immutable(不可变)的,一旦创建就不能更改其内容了:平常我们对字符串的操作是最多的,其实对字符串的操作,返回的字符串都是新建的字符串对象, ...

  2. java 字符串String

    在 Java 中,字符串被作为 String 类型的对象处理. String 类位于 java.lang 包中.默认情况下,该包被自动导入所有的程序. 创建 String 对象的方法: 只要是双引号标 ...

  3. Java 字符串 String

    什么是Java中的字符串 在 Java 中,字符串被作为 String 类型的对象处理. String 类位于 java.lang 包中.默认情况下,该包被自动导入所有的程序. 创建 String 对 ...

  4. Java字符串String类操作方法详细整理

    关于String类的基本操作,可分为以下几类: 1.基本操作方法 2.字符串比较 3.字符串与其他数据类型之间的转换 4.字符与字符串的查找 5.字符串的截取与拆分 6.字符串的替换与修改 我觉得在整 ...

  5. java 字符串(String)常用技巧及自建方法模块汇总

    1.String类常用方法汇总 (1)删除字符串的头尾空白符 public String trim() (2)从指定位置截取字符串 public String substring(int beginI ...

  6. [Java学习] Java字符串(String)

    从表面上看,字符串就是双引号之间的数据,例如“微学苑”.“http://www.weixueyuan.net”等.在Java中,可以使用下面的方法定义字符串: String stringName = ...

  7. Java字符串(String)

    从表面上看,字符串就是双引号之间的数据,例如“微学苑”.“http://www.weixueyuan.net”等.在Java中,可以使用下面的方法定义字符串:    String stringName ...

  8. 老司机也晕车--java字符串String晕车之旅

    首先声明,有晕车经历的司机请自备药物,String也可能让你怀疑人生! 第一道 开胃菜 请听题!第一道题: String hello="hello world!"; String ...

  9. Java字符串——String深入

    转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10840495.html 一:字符串的不可变性 1.可变 与 不可变 辨析 Java中的对象按照创建后,对象的 ...

随机推荐

  1. CUDA[3] Samples for accessing shared/global memory

    memory model: programming model: Source:  Udacity Class CS344

  2. python基本数据类型之字符串(二)

    python基本数据类型之字符串(二) 替换方法 python中字符串的替换方法主要有:center.rjust\ljust.expandtabs.format\format_map(格式化).str ...

  3. [solution] JZOJ-5781 秘密通道

    JZOJ-5781[秘密通道 ]solution 题面 Description 有一副$nm$的地图,有$nm$块地,每块是下列四种中的一种: 墙:用#表示,墙有4个面,分别是前面,后面,左面,右面. ...

  4. django创建上下文

    在app中创建context_processes.py(可以是别的名字),然后加载到settings里,这样所有的网页都可以传入变量 from .models import User def app0 ...

  5. kaldi的TIMIT实例二

    ============================================================================ MonoPhone Training & ...

  6. Thinking in Java Chapter 14

    From Thinking in Java 4th Edition RTTI(Run-Time Type Information),运行时类型信息,使得你可以在程序运行时发现和使用类型信息.对RTTI ...

  7. iOS 抓包

    通过tcpdump对iOS进行流量分析(无需越狱 iOS Packet Tracing 将 iOS 设备通过 USB 连接到 Mac 打开 terminal rvictl -s $UDID 运行 tc ...

  8. 从零开始学习渗透Node.js应用程序

    本文来源于i春秋学院,未经允许严禁转载 0x01 介绍 简单的说 Node.js 就是运行在服务端的 JavaScript.Node.js 是一个基于Chrome JavaScript 运行时建立的一 ...

  9. ORACLE 日期加减操作

    无论是DATE还是timestamp都可以进行加减操作. 可以对当前日期加年.月.日.时.分.秒,操作不同的时间类型,有三种方法: 1 使用内置函数numtodsinterval增加小时,分钟和秒2 ...

  10. 从 moment -> nodejs -> sequelize -> postgres,你都得设置好时区

    背景 最近在做报表统计,因为 sequelize 的时区配置没加导致了统计数字对不上的问题. 问:大家都知道时区,但是你清楚 UTC 和 GMT 的区别吗? 答:UTC 是我们现在用的时间标准,GMT ...