Java 字符串比较、拼接问题
@
/——————————————————字符串类型—————————————————/
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"的字符串:
- 如果不存在这样的字符串,则会在常量池中新建一个"aa"字符串对象,并把这个字符串对象的引用地址赋值给对象str_01;
- 如果常量池中寻找已存在这样的字符串,则不会再创建新的对象,直接返回已存在的对象地址,并将其赋值给对象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"关键字创造对象主要分为三步:
- 在堆中会创建一个字符串对象;
- 判断常量池是否存在与构造器参数中的字符串值相等的常量;
- 如果常量池中已有这样的字符串存在,则直接返回堆中的字符串对象引用地址,赋值给栈中的变量;如果不存在,会先创建一个字符串对象在常量池中,然后返回堆中的对象引用地址,赋值给栈中的变量。
// 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
参考内容:
- java-String常量池的知识点你知道多少?-结合jdk版本变更 by hz90s
- java中String、StringBuffer和StringBuilder的区别(简单介绍) by 韦邦杠
- Java String:字符串常量池(转)by 暖暖-木木
If you have any question, please let me know, your words are always welcome.*
新人入坑,如有错误/不妥之处,欢迎指出,共同学习。
Java 字符串比较、拼接问题的更多相关文章
- 【Java】字符拼接成字符串的注意点
这两天敲代码的时候,偶然间发现一个好玩的事情,分享一下,记录一下. 该段代码主要是:先产生的几个整数,把整数转换成对应的字符,最后的字符拼接成字符串,在把字符拼接成字符串的时候,个人因为偷懒使用+号进 ...
- 树形菜单的json字符串的拼接
最近在学习权限管理, 要用到树形按钮, 但是字符串的拼接是一个难理解的问题, 然后从网上找了一个从前台用js来遍历组成这个json字符串, 很好! 但是没看懂... var data = [ {&qu ...
- 关于java字符串编译优化问题
情景一:不好的字符串拼接习惯 起因是这样的:一个大牛在写了一篇关于java字符串优化问题的讲解,他提到:不要使用strObj+otherValue的方法将otherValue转换为字符串形式,因 ...
- 【JAVA零基础入门系列】Day6 Java字符串
字符串,是我们最常用的类型,每个用双引号来表示的串都是一个字符串.Java中的字符串是一个预定义的类,跟C++ 一样叫String,而不是Char数组.至于什么叫做类,暂时不做过多介绍,在之后的篇章中 ...
- java将map拼接成“参数=值&参数=值”
Java将Map拼接成"参数=值&参数=值" 把一个map的键值对拼接成"参数=值&参数=值"即"username=angusbao& ...
- Java开发知识之Java字符串类
Java开发知识之Java字符串类 一丶简介 任何语言中.字符串都是很重要的.都涉及到字符串的处理. 例如C++中. 字符串使用内存. 并提供相应的函数进行处理 strcmp strcat strcp ...
- java 字符串笔记
java字符串当中有三个关于字符串对象的类. String 首先谈论下他们各自的含义: 1.String含义为引用数据类型,是字符串常量.是不可变的对象,(显然线程安全)在每次对string类型进行改 ...
- java笔记 -- java字符串
概念: Java字符串就是Unicode字符序列, Java没有内置的字符串类型, 而是在标准Java类库中提供了一个预定义类. 每个用双引号括起来的字符串都是String类的一个实例.String ...
- Java字符串String
Java字符串String 我们知道Java的字符窜是Immutable(不可变)的,一旦创建就不能更改其内容了:平常我们对字符串的操作是最多的,其实对字符串的操作,返回的字符串都是新建的字符串对象, ...
随机推荐
- 使用PD(Power Designer)设计数据库,并且生成可执行的SQL文件创建数据库(本文以SQL Server Management Studio软件执行为例)
下载和安装PD: 分享我的软件资源,里面包含了对PD汉化包(链接出问题时可以留言,汉化包只能对软件里面部分菜单栏汉化) 链接:https://pan.baidu.com/s/1lNt1UGZhtDV8 ...
- 描述一下 JVM 加载 class 文 件的原理机制?
JVM 中类的装载是由 ClassLoader 和它的子类来实现的, Java ClassLoader 是一个重要的 Java 运行时系统组件.它负责在运行时查找和装入类文件的类.
- 浏览器端如何使用VConsole.js调试代码?
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 在CentOS7上源码安装OpenResty
您必须将这些库perl 5.6.1+libreadlinelibpcrelibssl安装在您的电脑之中. 对于 Linux来说, 您需要确认使用 ldconfig 命令,让其在您的系统环境路径中能找到 ...
- AlertController的使用
UIAlertView 随着苹果上次iOS 5的发布,对话框视图样式出现在了我们面前,直到现在它都没有发生过很大的变化.下面的代码片段展示了如何初始化和显示一个带有“取消”和“好的”按钮的对话框视图. ...
- 基于docker-compose部署jumpserver
基于docker-compose部署jumpserver 组件说明 Jumpserver 为管理后台, 管理员可以通过 Web 页面进行资产管理.用户管理.资产授权等操作, 用户可以通过 Web 页面 ...
- 初识MQ消息队列
MQ 消息队列 消息队列(Message Queue)简称MQ,是阿里巴巴集团中间件技术部自主研发的专业消息中间件. 产品基于高可用分布式集群技术,提供消息发布订阅.消息轨迹查询.定时(延时)消息.资 ...
- FreeSql.Generator命令行代码生成器是如何实现的
目录 FreeSql介绍 FreeSql.Generator RazorEngine.NetCore 源码解析 FreeSql.Tools FreeSql FreeSql 是功能强大的对象关系映射技术 ...
- JAVA SOCKET 通信总结 BIO、NIO、AIO ( NIO 2) 的区别和总结
1 同步 指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪 自己上街买衣服,自己亲自干这件事,别的事干不了.2 异步 异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已 ...
- Git在windows使用git时出现:warning: LF will be replaced by CRLF
$ rm -rf .git // 删除.git $ git config --global core.autocrlf false //禁用自动转换 $ git init $ git add