区分java中String+String和String+char_java

我们来考虑一个关于java中String的问题: "abc" + '/'和 "abc" + "/"的区别. 通过这个例子, 我们可以顺便练习一下JDK工具中javap的用法, 原问题是这样的:

把斜杠/当作字符或字符串有什么区别呢?
一个是当作基本数据类型char,一个是对象String。具体有什么区别呢?
当作字符效率会更高吗?
String str = "abc" + '/';

String str = "abc" + "/";

1、编译器优化

首先大家应该知道, 上面那两句效果是一样的, 因为编译器会把上面那两句都优化成下面的样子:

String str = "abc/";

我们可以通过javap证明这一点. 关于javap, 可以参考《每个Java开发者都应该知道的5个JDK工具》. 我们首先创建一个类: StringOne, 在主方法中填入下面的代码:

StringOne.java
String str1 = "abc" + '/';
String str2 = "abc" + "/";
System.out.println(str1 == str2);

编译并运行, 输出结果为true. 接下来, 该我们的javap登场了, 在命令行中输入下面的命令:

javap -v -l StringOne.class > StringOne.s

然后查看生成的StringOne.s文件. 就会发现其中有这么几行

StringOne.s
#2 = String       #20      // abc/
...
#20 = Utf8        abc/
...
0: ldc      #2         // String abc/
2: astore_1
3: ldc      #2         // String abc/
5: astore_2

说明str1和str2都引用字符串"abc\".

2、使用javap分析差异

现在我们换一个问法, 下面的代码中stringAddString和stringAddChar方法有什么区别?

StringTwo
public static String stringAddString(String str1, String str2){
  return str1 + str2;
}

public static String stringAddChar(String str, char ch){
  return str + ch;
}

这次再使用javap进行反编译, 生成文件的部分内容如下所示

StringTwo.s
public java.lang.String stringAddString(java.lang.String, java.lang.String);
 descriptor: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
 flags: ACC_PUBLIC
 Code:
  stack=2, locals=3, args_size=3
    0: new      #2         // class java/lang/StringBuilder
    3: dup
    4: invokespecial #3         // Method java/lang/StringBuilder."< init>":()V
    7: aload_1
    8: invokevirtual #4         // Method java/lang/StringBuilder. append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   11: aload_2
   12: invokevirtual #4         // Method java/lang/StringBuilder. append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   15: invokevirtual #5         // Method java/lang/StringBuilder. toString:()Ljava/lang/String;
   18: areturn

public java.lang.String stringAddChar(java.lang.String, char);
 descriptor: (Ljava/lang/String;C)Ljava/lang/String;
 flags: ACC_PUBLIC
 Code:
  stack=2, locals=3, args_size=3
    0: new      #2         // class java/lang/StringBuilder
    3: dup
    4: invokespecial #3         // Method java/lang/StringBuilder."<init>":()V
    7: aload_1
    8: invokevirtual #4         // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   11: iload_2
   12: invokevirtual #6         // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
   15: invokevirtual #5         // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   18: areturn

现在, 我们已经可以很清楚的看出这两个方法执行的流程了:

stringAddString

  • 创建一个StringBuilder对象
  • 使用append方法, 依次将两个参数添加到刚才创建的StringBuilder中.
  • 调用toString方法.
  • return toString方法的返回值.

stringAddChar的过程和stringAddString一样, 只是在第二次调用append方法时stringAddString的参数是String类型, 而stringAddChar的参数是char类型.

3、StringBuilder类的append(char)方法和append(String)方法

这里,我们直接查看源码就好了(我的是jdk1.8.0_60附带的源码)。 注意,虽然文档上显示StringBuilder继承自Object, 但是从源码来看, 它是继承自抽象类AbstractStringBuilder的。而且append方法是由AbstractStringBuilder实现的。

AbstractStringBuilder.java

public AbstractStringBuilder append(char c) {
  ensureCapacityInternal(count + 1);  // 确保数组能够容纳count+1个字符
  value[count++] = c;
  return this;
}

public AbstractStringBuilder append(String str) {
  if (str == null)
    return appendNull();
  int len = str.length();
  ensureCapacityInternal(count + len);
  str.getChars(0, len, value, count); // 拷贝字符串中的字符数组到本对象的字符数组中
  count += len;
  return this;
}

剩下的就不再贴出来了。String.getChars(int, int, char[], int)最终依赖于public static native void arraycopy(Object, int, Object, int, int)。也就是说有可能是C语言写的,在拷贝大型数组时效率应该会比java写的程序好一些。 那么,现在说说我的理解:

从直接内存来说, 由于String中包含char数组, 而数组应该是有长度字段的, 同时String类还有一个int hash属性, 再加上对象本身会占用额外的内存存储其他信息,所以字符串会多占用一些内存. 但是如果字符串非常长, 那么这些内存开销差不多就可以忽略了; 而如果像"/"这种情况, 字符串比较(非常)短,那么就很可能有许多个共享引用来分担这些内存开销, 那么多余的内存开销还是可以忽略的.

从调用堆栈上, 由于这里String只比char多了一两层函数调用,所以如果不考虑函数调用开销(包括时间和空间), 应该差不多;考虑函数调用开销, 应该 "abc" + '/'更好一些; 但是当需要连接若干个字符时(感觉这种情况应该更常见吧?), 由于使用char需要循环好多次才能完成连接, 调用的函数次数只会比使用String更多. 同时拷贝也不会比String直接拷贝一个数组更快. 所以这个时候就变成了"abc" + "/"吞吐量更大.
现在感觉这个问题像是在问: 读写文件时使用系统调用效率高, 还是使用标准函数库中的IO库效率高. 个人感觉, 虽然标准IO库最后还得调用系统调用, 而且这之间会产生一些临时变量, 以及更深层次的调用堆栈, 但是由于IO库的缓冲等机制, 所以IO库的吞吐量会更大, 而系统调用的实时性会好一些. 同样, 虽然String类会多几个字段, 有更深层次的函数堆栈, 但是由于缓存以及更直接的拷贝, 吞吐量应该会更好一些.

新的问题

从上面javap的反编译代码来看, 两个String相加, 会变成向StringBuilder中append字符串. 那么理论上, 下面哪段代码的效率好呢?

String str1 = "abc" + "123";  // 1

StringBuilder stringBuilder = new StringBuilder(); // 2
stringBuilder.append("abc");
stringBuilder.append("123");
String str2 = stringBuilder.toString();

这个问题就留给大家思考吧!

以上就是本文的全部内容,帮助大家更好的区分java中String+String和String+char,希望对大家的学习有所帮助。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索java
, String+String
String+char
java char 转string、java string char、java char to string、java中char转string、java string转换char,以便于您获取更多的相关知识。

时间: 2024-08-01 12:42:31

区分java中String+String和String+char_java的相关文章

Java中构造函数File(String)未定义

问题描述 Java中构造函数File(String)未定义 package nmd; import java.io.*; public class FileHello { public static void main(String[] args) { try { Runtime ec=Runtime.getRuntime(); File file=new File("file.txt"); ec.exec(file.getAbsolutePath()); } catch (Excep

Java中处理日文时String equals不等的问题

String japChar = "オン"; String japGetFromOther : japGetFromOther.equals(japChar);  之前遇到一个问题, 在一个日文系统里, 由JDK返回的一个日文的值,在做equals对比时无法与java代码中已经写好的日文进行比较, 一直都是返回false. 研究了一下, 将代码改成以下: japChar = new String("オン".getBytes("SJIS"),&qu

Java中byte[]转换成String有数据丢失现象,原byte[]长度为6714转换成String被截断成6400,请教各位高手

问题描述 该问题是在HTTP通信过程中发现的,本地接收到的字符数没问题,但是在转换成String过程中出问题了.字符串发送时采用UTF-8的格式,客户端默认也是采用UTF-8,以下方法试过,不管加不加字符集限制都是一样String bString = new String(byets);主要代码BufferedHttpEntity bhe = new BufferedHttpEntity(httpResponse.getEntity());BufferedInputStream buffered

java中按字节截取String字符串.

字符串 /** * 取字符串的前toCount个字符 * * @param str 被处理字符串 * @param toCount 截取长度 * @param more 后缀字符串 * @version 2004.11.24 * @author zhulx * @return String */ public static String substring(String str, int toCount,String more) { int reInt = 0; String reStr = "

关于Java中bytes到String的转换

    为什么想要写这个,是因为在上周,表格存储的一个客户,告知我们在将数据通过DataX从OTS导出到ODPS后,发现数据『丢失』了.而在调查过后,发现数据并不是所谓的『丢失』了,而是数据被『改变』了.     什么原因导致数据发生了『改变』呢?却是因为一个大部分Java程序员都会忽略的问题导致的,所以我觉得有必要单独拿出来讲讲. 首先看下如下代码: byte[] original1 = new byte[]{(byte)0xef, (byte)0x8f, (byte)0x8f}; byte[

Java中的String与常量池

string是java中的字符串.String类是不可变的,对String类的任何改变,都是返回一个新的String类对象.下面介绍java中的String与常量池. 1. 首先String不属于8种基本数据类型,String是一个对象. 因为对象的默认值是null,所以String的默认值也是null:但它又是一种特殊的对象,有其它对象没有的一些特性. 2. new String()和new String("")都是申明一个新的空字符串,是空串不是null: 3. String st

string-java中object转换成String类型问题

问题描述 java中object转换成String类型问题 class a122{ public static void main(String[] args){ Object d=4; String g=(String)d; } } 为什么运行时这个小程序会出错,按说object不是可以转化成String的吗 解决方案 你这种理解是错误的,因为你定义的Object类是Integer 类型,跟String类型没有关系,不能转换. 虽然可以编译通过,但是会报运行时异常java.lang.Integ

Java中去除字符串中所有空格的几种方法

JAVA中去掉空格 1. String.trim() trim()是去掉首尾空格 2.str.replace(" ", ""); 去掉所有空格,包括首尾.中间 复制代码 代码如下: String str = " hell o "; String str2 = str.replaceAll(" ", ""); System.out.println(str2); 3.或者replaceAll(" +&

Java中字符串 I

什么是 Java 中的字符串 在程序开发中字符串无处不在,如用户登陆时输入的用户名.密码等使用的就是字符串.其实,在前面的章节中我们就已经使用了字符串,例如我们在控制台中输出的 "Hello World" . "imooc" ."爱慕课"等. 在 Java 中,字符串被作为 String 类型的对象处理. String 类位于 java.lang 包中.默认情况下,该包被自动导入所有的程序. 创建 String 对象的方法: . Java 中字符