@

/——————————————————字符串类型—————————————————/

Java中用于处理字符串常用的有三个类:

1、java.lang.String

2、java.lang.StringBuffer

3、java.lang.StrungBuilder

相同点: 都是final类, 不允许被继承;

不同点:

  • StringBuffered/StringBuilder 都继承自抽象类AbstractStringBuilder

    (实现了Appendable, CharSequence接口),可以通过append()、indert()进行字符串的操作
  • String实现了三个接口: Serializable、Comparable、CarSequence,

    String的实例可以通过compareTo方法进行比较

    StringBuilder/StringBuffer只实现了两个接口Serializable、CharSequence
  • StringBuffer是线程安全的(Synchronized 加锁),可以不需要额外的同步用于多线程中

    StringBuilder不是线程安全的,但是效率比StringBuffer高

/—————————————本篇主要讨论String类型————————————/

1.字符串的比较

1. 1 字符串常量池

字符串常量池(以下简称常量池/字符串池)的存在意义:实际开发中,String类是使用频率非常高的一种引用对象类型。但是不断地创建新的字符串对象,会极大地消耗内存。因此,JVM为了提升性能和减少内存开销,内置了一块特殊的内存空间即常量池,以此来避免字符串的重复创建。JDK 1.8 后,常量池被放入到堆空间中。

字符串池中维护了共享的字符串对象,这些字符串不会被垃圾收集器回收。

1.2 String类型的比较方式

若直接使用“==”进行比较对象,则比较的是两个对象的引用地址;

若使用str1.equals(str2)方法进行比较,由于String类内部已经覆盖Object类中的equals()方法,实际比较的是两个字符串的值。

  • 比较原理:

    先判断对象地址是否相等,若相等则直接返回true;

    若不相等再去参数判断括号内传入的参数是否为String类型的:若不是字符串将最终返回false;若是字符串,再依次比较所有字符是否一样。
// 源码
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String aString = (String)anObject;
if (coder() == aString.coder()) {
return isLatin1() ? StringLatin1.equals(value, aString.value) : StringUTF16.equals(value, aString.value);
}
}
return false;
}

1.3 String的创建方式

1.3.1 直接使用“=”进行赋值

String str_01 = "aa";
String str_02 = "aa";
System.out.println(str_01 == str_02);

使用这种方式创建字符串,会先在栈中创建一个引用变量str_01,再去常量池中寻找是否已存在值为"aa"的字符串:

  1. 如果不存在这样的字符串,则会在常量池中新建一个"aa"字符串对象,并把这个字符串对象的引用地址赋值给对象str_01;
  2. 如果常量池中寻找已存在这样的字符串,则不会再创建新的对象,直接返回已存在的对象地址,并将其赋值给对象str_02;
// result
true

1.3.2 使用“new”关键字创建新对象

String str_01 = new String("xyz");
String str_02 = new String("xyz");
System.out.println(str_01 == str_02);

这种方式至少会创建一个对象,因为本质是调用了String类的构造器方法public String(String original){...},在堆中一定会创建一个字符串对象。

使用"new"关键字创造对象主要分为三步:

  1. 在堆中会创建一个字符串对象;
  2. 判断常量池是否存在与构造器参数中的字符串值相等的常量;
  3. 如果常量池中已有这样的字符串存在,则直接返回堆中的字符串对象引用地址,赋值给栈中的变量;如果不存在,会先创建一个字符串对象在常量池中,然后返回堆中的对象引用地址,赋值给栈中的变量。
// result
false

1.3.3 intern()方法返回的引用地址

String str_01 = new String("abc").intern();
String str_02 = "abc";
String str_03 = new String("abc");
System.out.println(str_01 == str_02);
System.out.println(str_02 == str_03); String str_04 = new String("cba");
String str_05 = new String("cba").intern();
System.out.println(str_04 == str_05);

当使用构造器创建字符串调用 intern()方法时,如果常量池中已经存在一个值相同的字符串(内部使用equals()方法来确定),则返回常量池中的字符串对象的引用地址;否则,将堆中新创建的字符串对象添加到常量池中,并返回池中字符串对象的引用地址。

// result
true
false
false

2. 字符串类的可变性与不可变性

字符串的本质:char类型数组 private final char[] str

String类实现了CharSequence接口

String类型的不可变性指的是内存地址不可变,如果将一个对象重新赋值,则本质上是改变了其引用对象。

String a = "hello";
System.out.println(a.hashCode());
a = "hey";
System.out.println(a.hashCode());
// result
99162322
103196

StringBuffer类型和StringBuilder类型的字符串定义好后可以进行值改变,并且不会创建新的内存地址。

StringBuilder a = new StringBuilder();
System.out.println(a.hashCode());
a.append("Hello");
a.append("World");
System.out.println(a.hashCode());
// result
1395089624
1395089624

3. 字符串的相加/拼接

3.1 字符串与非字符串类型的相加/拼接

String类中的valueOf(Object obj)方法可以将任意一个对象转换为字符串类型。

// 源码
public static String valueOf(Object obj) {
  return (obj == null) ? "null" : obj.toString();
}

String类中,重载了+与+=运算,这也是Java中唯一重载的两个运算符。

两个字符串相加即是字符串的拼接,在进行拼接时,会先调用valueOf(Object obj)方法将其为字符串类型,再进行拼接。从源码可以看出,如果字符串为null,会将其转换为字面值为"null"的字符串。

String s = null;
s = s + "World";
System.out.println("Hello " +s);
// result: Hello nullWorld

因此在进行字符串拼接时,初始字符串应该设置成空字符串"",而非null。

3.2 两个String类型对象相加/拼接原理

在字符串间使用加法运算时:

  • 若是常量字符串相加,如: "AB"+"CD",则是编译优化。

    凡是单独使用双引号" "引用起来的内容直接拼接时,均会被编译优化,编译时就已经确定其值,即为拼接后的值。
  • 若是字符串变量相加,如:

    String temp1 = "AB";

    String temp2 = "CD";

    String str = temp1 + temp2;

    则是在底层调用了StringBuilder类中的构造方法及append()方法来辅助完成:

    String str = new StringBuilder().append(temp1).append(temp2).toString();
		String str1 = "ABCD";
String str2 = "AB" + "CD";
String str3 = "A" + "B" + "C" + "D";
String temp1 = "AB";
String temp2 = "CD";
String str4 = temp1 + temp2;
// String str4 = new StringBuilder().append(temp1).append(temp2).toString(); String temp = "AB";
String str5 = temp + "CD";
// String str4 = new StringBuilder(String.valueOf(temp)).append("CD").toString(); System.out.println(str1 == str2);
System.out.println(str1 == str3);
System.out.println(str1 == str4);
System.out.println(str1 == str5);
// result
true
true
false
false

4. final类型的String类字符串

public class test {
public static final String str1 = "abc";
public static final String str2 = "def";
public static void main(String[] args) {
String str3 = str1 + str2;
String str4 = "abcdef";
System.out.println(str3 == str4);
}
}

str1和str2都是final类型的,并且在编译阶段都是已经被赋值了,相当于一个常量,当执行Strings str3 = str1 + str2 的时候,str3已经是"abcdef"常量了,已被创建在常量池中,所以地址是相等的。

// result
true
public class test {
public static final String s1;
public static final String s2;
static{
s1 = "ab";
s2 = "cd";
}
public static void main(String[] args) {
String s3 = s1 + s2;
String s4 = "abcd";
System.out.println(s3 == s4);
}
}

虽然s1和s2都是final类型,但是起初并没有初始化,在编译期还不能确定具体的值,此处是变量,所以这里会调用StringBuilder类中的构造方法及append()方法来创建新的字符串s3,返回的新字符串s3在堆中的地址,所以与s4不相等。

// result
false

参考内容:

  1. java-String常量池的知识点你知道多少?-结合jdk版本变更 by hz90s
  2. java中String、StringBuffer和StringBuilder的区别(简单介绍) by 韦邦杠
  3. Java String:字符串常量池(转)by 暖暖-木木

If you have any question, please let me know, your words are always welcome.*

新人入坑,如有错误/不妥之处,欢迎指出,共同学习。

Java 字符串比较、拼接问题的更多相关文章

  1. 【Java】字符拼接成字符串的注意点

    这两天敲代码的时候,偶然间发现一个好玩的事情,分享一下,记录一下. 该段代码主要是:先产生的几个整数,把整数转换成对应的字符,最后的字符拼接成字符串,在把字符拼接成字符串的时候,个人因为偷懒使用+号进 ...

  2. 树形菜单的json字符串的拼接

    最近在学习权限管理, 要用到树形按钮, 但是字符串的拼接是一个难理解的问题, 然后从网上找了一个从前台用js来遍历组成这个json字符串, 很好! 但是没看懂... var data = [ {&qu ...

  3. 关于java字符串编译优化问题

    情景一:不好的字符串拼接习惯    起因是这样的:一个大牛在写了一篇关于java字符串优化问题的讲解,他提到:不要使用strObj+otherValue的方法将otherValue转换为字符串形式,因 ...

  4. 【JAVA零基础入门系列】Day6 Java字符串

    字符串,是我们最常用的类型,每个用双引号来表示的串都是一个字符串.Java中的字符串是一个预定义的类,跟C++ 一样叫String,而不是Char数组.至于什么叫做类,暂时不做过多介绍,在之后的篇章中 ...

  5. java将map拼接成“参数=值&参数=值”

    Java将Map拼接成"参数=值&参数=值" 把一个map的键值对拼接成"参数=值&参数=值"即"username=angusbao& ...

  6. Java开发知识之Java字符串类

    Java开发知识之Java字符串类 一丶简介 任何语言中.字符串都是很重要的.都涉及到字符串的处理. 例如C++中. 字符串使用内存. 并提供相应的函数进行处理 strcmp strcat strcp ...

  7. java 字符串笔记

    java字符串当中有三个关于字符串对象的类. String 首先谈论下他们各自的含义: 1.String含义为引用数据类型,是字符串常量.是不可变的对象,(显然线程安全)在每次对string类型进行改 ...

  8. java笔记 -- java字符串

    概念: Java字符串就是Unicode字符序列, Java没有内置的字符串类型, 而是在标准Java类库中提供了一个预定义类. 每个用双引号括起来的字符串都是String类的一个实例.String ...

  9. Java字符串String

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

随机推荐

  1. 使用PD(Power Designer)设计数据库,并且生成可执行的SQL文件创建数据库(本文以SQL Server Management Studio软件执行为例)

    下载和安装PD: 分享我的软件资源,里面包含了对PD汉化包(链接出问题时可以留言,汉化包只能对软件里面部分菜单栏汉化) 链接:https://pan.baidu.com/s/1lNt1UGZhtDV8 ...

  2. 描述一下 JVM 加载 class 文 件的原理机制?

    JVM 中类的装载是由 ClassLoader 和它的子类来实现的, Java ClassLoader 是一个重要的 Java 运行时系统组件.它负责在运行时查找和装入类文件的类.

  3. 浏览器端如何使用VConsole.js调试代码?

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. 在CentOS7上源码安装OpenResty

    您必须将这些库perl 5.6.1+libreadlinelibpcrelibssl安装在您的电脑之中. 对于 Linux来说, 您需要确认使用 ldconfig 命令,让其在您的系统环境路径中能找到 ...

  5. AlertController的使用

    UIAlertView 随着苹果上次iOS 5的发布,对话框视图样式出现在了我们面前,直到现在它都没有发生过很大的变化.下面的代码片段展示了如何初始化和显示一个带有“取消”和“好的”按钮的对话框视图. ...

  6. 基于docker-compose部署jumpserver

    基于docker-compose部署jumpserver 组件说明 Jumpserver 为管理后台, 管理员可以通过 Web 页面进行资产管理.用户管理.资产授权等操作, 用户可以通过 Web 页面 ...

  7. 初识MQ消息队列

    MQ 消息队列 消息队列(Message Queue)简称MQ,是阿里巴巴集团中间件技术部自主研发的专业消息中间件. 产品基于高可用分布式集群技术,提供消息发布订阅.消息轨迹查询.定时(延时)消息.资 ...

  8. FreeSql.Generator命令行代码生成器是如何实现的

    目录 FreeSql介绍 FreeSql.Generator RazorEngine.NetCore 源码解析 FreeSql.Tools FreeSql FreeSql 是功能强大的对象关系映射技术 ...

  9. JAVA SOCKET 通信总结 BIO、NIO、AIO ( NIO 2) 的区别和总结

    1 同步 指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪 自己上街买衣服,自己亲自干这件事,别的事干不了.2 异步 异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已 ...

  10. Git在windows使用git时出现:warning: LF will be replaced by CRLF

    $ rm -rf .git  // 删除.git $ git config --global core.autocrlf false  //禁用自动转换 $ git init $ git add