Java字符串——String深入
转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10840495.html
一:字符串的不可变性
1、可变 与 不可变 辨析
Java中的对象按照创建后,对象的内容是否可以被修改,分为 mutable object 和 immutable object。【注意:是对象的内容不可变,而不是指向该对象的引用变量内容不可变。】
我们常见的不可变对象是几个基本数据类型的包装类——Integer、Double、String等。【想想为什么?——Tips:出于节省内存开销,避免重复创建。】
不可变类有5大基本原则:
1)类定义时,添加final修饰符,保证类不被继承【即:不允许在子类中被修改】
2)类定义时,其成员变量一概使用 final private 修饰,保证变量私有的同时不允许修改
3)不提供可以修改成员变量的方法,包括setter
4)在构造函数中采用deep copy的形式将参数值拷贝给成员变量,而不是直接将参数值赋给成员变量【因为引用类型的参数只是传了一个地址,这样在外部改变该地址的内容会导致不可变对象的成员变量改变】
5)在成员变量的getter方法中,不能直接返回成员变量本身,而是返回成员变量的copy对象【这也是为了防止引用类型的成员变量被外部获取后,改变引用指向的对象值引起不可变对象的内容变化】
2、不可变对象的优缺点
1)字符串常量池的需要——避免每次使用相同的字符串常量都重新创建相同的对象、节省存储空间
2)线程安全:当一个String对象被多个线程共享时,无需担心线程安全问题
3)支持hash和缓存:由于字符串对象是不可变的并且hashcode就缓存在对象中【在下文讲解】了,不需要重新计算,因此很适合作为Map中的键,因为字符串键的哈希处理速度要快过其它的键对象【这就是HashMap中的键往往都使用字符串的原因】
4)使用类加载器时,要用字符串来传递加载的类名,而字符串的不可变性提供了安全性,以保证正确的类被加载。
5) 缺点:当对String变量有重新赋值、修改等操作时,会不断创建大量的String对象。【当修改后的值未出现在字符串常量池的前提下】————【延伸:因此,在代码中涉及大量字符串操作时,使用StringBuilder或StringBuffer来进行】
3、“不可变对象”的非常规手段修改
对于不可变对象,可以通过反射机制的手段改变其值——获取类的字段定义->改变该字段的可见性和可修改性->修改对象的变量值。
例如:
//创建字符串"Hello World", 并赋给引用s
String s = "Hello World";
System.out.println("s = " + s); //Hello World //获取String类中的value字段
Field valueFieldOfString = String.class.getDeclaredField("value");
//改变value属性的访问权限
valueFieldOfString.setAccessible(true); //获取s对象上的value属性的值
char[] value = (char[]) valueFieldOfString.get(s);
//改变value所引用的数组中的第5个字符
value[5] = '_';
System.out.println("s = " + s); //Hello_World 结果为:
s = Hello World
s = Hello_World
二:replaceFirst、replaceAll、replace 区别
我们先来看一下程序:
String s = "my.test.txt";
System.out.println(s.replace(".", "#"));
System.out.println(s.replaceAll(".", "#"));
System.out.println(s.replaceFirst(".", "#"));
它们的结果会一样吗?——No。
my#test#txt
###########
#y.test.txt
这三个函数中,replaceFirst、replaceAll在替换时使用了正则表达式,因此上面三个函数的参数含义是不同的。
replace(src,des):将字符串中的src子串[也是一段字符串]替换成des。
replaceAll(reg,des):将字符串中符合reg模式的内容替换成des。
replaceFirst(reg,des):将字符串中,第一个匹配reg模式的内容替换成des。
所以上面示例代码中,replace函数是将字符串中的“.”字符换成“#”,而replaceAll则是将所有字符[“.”是正则表达式的通配符]替换成“#”,replaceFirst则是将第一个字符替换成“#”。
三:String对“+”运算符的重载
“在Java中是不支持重载运算符的!”
java不支持运算符重载,因为java的语法比较繁杂,会导致使用类对象 像基本数据类型那样 用运算符进行操作时,无法做到像c++一样流畅。因此,Java中针对类对象的运算操作一般都是通过方法来定义,而不是运算符重载。
唯一例外的是String类,它的拼接运算(+) 经过了重载,这个重载是通过jvm编译实现的,具体原理可以 手写一个字符串相+的java类文件并编译,然后通过 javap -c 文件.class 查看具体的过程。
其原理是:String的+会被转化为StringBuilder的append方法,并生成一个新的String对象返回。
四:字符串拼接的5种方式比较
Java中有以下五种方法处理字符串拼接:
1. 加号 “+”:适用于小数据量的操作,代码简洁方便。
2. String contact() 方法:适用于小数据量的操作,代码简洁方便。
3. StringUtils.join() 方法:适用于将ArrayList转换成字符串的情景,可以省掉用for循环读取ArrayList手动拼接的过程。
4. StringBuffer append() 方法:继承自AbstractStringBuilder,效率高,大批量的数据处理的好选择,该方法线程安全,由于加了线程锁,速度会比下面第5中慢一点。
5. StringBuilder append() 方法:继承自AbstractStringBuilder,效率最高,大批量的数据处理的好选择,该方法线程不安全,因此速度最快,如果不涉及多线程操作,优先使用此方法。
五:String.valueOf(val) 和 obj.toString 的异同
同:都返回参数的字符串表示形式。
异: 对空值的调用结果不同:
java.lang.Object类里已有public方法.toString(),所以对任何严格意义上的java对象都可以调用此方法。但在使用时要注意,必须保证object不是null值,否则将抛出NullPointerException异常。
而valueOf(Object obj)对null值进行了处理,不会报任何异常。但当object为null 时,String.valueOf(object)的值是字符串”null”,而不是null。
六:switch 对 String 的支持
Java1.7之前,switch只能局限于int 、short 、byte 、char四类做判断条件,因为在JVM内部实际大部分字节码指令只有int类型的值。
在使用switch的时候,如果是非int型,会先转为int型,再进行条件判断。
但是在Java1.7中,switch增加了对String作为判断条件的支持,可String并不能直接转为int型,这是怎么做到的呢?
原理:switch比较的是字符串常量的哈希值(缓存的int类型值,前文提到过),但是hash值可能会有冲突,所以还需要再调用equals方法将 switch(param)的param 与 case str的str 进行二次比较,二者综合之下达到唯一匹配的目的。
七:字符串池、常量池(运行时常量池、Class 常量池)、intern
全局字符串常量池(string pool):全局常量池在每个JVM中只有一份,存放的是字符串常量值。
string pool功能的是一个StringTable类,它是一个哈希表,里面存的是驻留字符串(也就是我们常说的用双引号括起来的),
字符串在第一次出现时被创建并且把引用放到stringtable中,在后面再出现时就不会重复创建而是直接从stringtable中找到字符串字面值的地址,返回给字符串引用变量。
Class常量池:class常量池是在编译的时候每个class都有的,用于编译阶段,存放的是class文件中的字面量(常量)和符号引用。
字面量就是我们所说的常量,如文本字符串、八种基本类型的值、被声明为final的常量值等。
符号引用是一组符号,用来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。
直接引用是指向方法区的本地指针,相对偏移量或是一个能间接定位到目标的句柄。
运行时常量池:运行时常量池是在类加载完成之后,将每个class常量池中的符号引用值转存到运行时常量池中,将符号引用替换成直接引用,与全局常量池中的引用值保持一致。
运行时常量池更具动态性,在运行期间也可以将新的变量放入常量池中,而不是一定要在编译时确定的常量才能放入。最主要的运用便是String类的intern()方法
str.intern()函数:把字符串对象str加入常量池中,如果常量池中已有该字符串字面值,则返回stringtable中的引用值。(这样做主要是为了避免在堆中不断地创建新的字符串对象)
常量池我们都知道他是存在于方法区的,他是方法区的一部分,而方法区是线程共享的,所以常量池也就是线程共享的,但是他并不是线程不安全的,他其实是线程安全的,因为它让有相同值的引用指向同一个位置。如果引用值变化了,但是常量池中没有新的值,那么就会新开辟一个常量结果来交给新的引用。
详细原理请看:https://www.cnblogs.com/ygj0930/p/6581009.html
八:String的New操作创建了几个对象?——两个
我们以String s1=new String("abc");为例 首先当我们的类Class在被ClassLoader加载时,"abc"被作为常量读入,在String Pool(字符串常量池)创建了一个"abc"的实例。 然后,调用到new String("abc")的时候,会在Heap里面复制一个相同的对象。
(1)类加载对一个类只会进行一次。"abc"在类加载时就已经创建并驻留了(如果该类被加载之前已经有"abc"字符串被驻留过则不需要重复创建用于驻留的"abc"实例)。驻留的字符串引用是放在全局共享的字符串常量池中的。[加载时创建一次]
(2)在这段代码后续被运行的时候,"abc"字面量对应的String实例已经固定了,不会再被重复创建。所以这段代码将常量池中的对象实例复制一份放到heap中,并且把heap中的这个对象的引用交给s1 持有。[运行时复制一次]
因此,这条语句创建了2个对象。【这两个引用,它们的对象实例是不同的。】
九:String的hashCode的缓存和懒加载初始化
String类中定义了一个私有成员变量——hash,它是一个整数,保存String对象的哈希值,也就是说String类型的对象的哈希值不会重复计算,计算过一次后就保存起来了,以后在被hash时直接取该值即可,无需重新计算。
这个值在String对象第一次被调用时进行初始化(懒加载,不是在String对象创建时初始化)。
Java字符串——String深入的更多相关文章
- Java字符串String
Java字符串String 我们知道Java的字符窜是Immutable(不可变)的,一旦创建就不能更改其内容了:平常我们对字符串的操作是最多的,其实对字符串的操作,返回的字符串都是新建的字符串对象, ...
- Java字符串String 集合的迭代器
Java字符串String 我们知道Java的字符窜是Immutable(不可变)的,一旦创建就不能更改其内容了:平常我们对字符串的操作是最多的,其实对字符串的操作,返回的字符串都是新建的字符串对象, ...
- java 字符串String
在 Java 中,字符串被作为 String 类型的对象处理. String 类位于 java.lang 包中.默认情况下,该包被自动导入所有的程序. 创建 String 对象的方法: 只要是双引号标 ...
- Java 字符串 String
什么是Java中的字符串 在 Java 中,字符串被作为 String 类型的对象处理. String 类位于 java.lang 包中.默认情况下,该包被自动导入所有的程序. 创建 String 对 ...
- Java字符串String类操作方法详细整理
关于String类的基本操作,可分为以下几类: 1.基本操作方法 2.字符串比较 3.字符串与其他数据类型之间的转换 4.字符与字符串的查找 5.字符串的截取与拆分 6.字符串的替换与修改 我觉得在整 ...
- java 字符串(String)常用技巧及自建方法模块汇总
1.String类常用方法汇总 (1)删除字符串的头尾空白符 public String trim() (2)从指定位置截取字符串 public String substring(int beginI ...
- [Java学习] Java字符串(String)
从表面上看,字符串就是双引号之间的数据,例如“微学苑”.“http://www.weixueyuan.net”等.在Java中,可以使用下面的方法定义字符串: String stringName = ...
- Java字符串(String)
从表面上看,字符串就是双引号之间的数据,例如“微学苑”.“http://www.weixueyuan.net”等.在Java中,可以使用下面的方法定义字符串: String stringName ...
- 老司机也晕车--java字符串String晕车之旅
首先声明,有晕车经历的司机请自备药物,String也可能让你怀疑人生! 第一道 开胃菜 请听题!第一道题: String hello="hello world!"; String ...
随机推荐
- USACO Spinning Wheels
洛谷 P2728 纺车的轮子 Spinning Wheels https://www.luogu.org/problemnew/show/P2728 JDOJ 1800: Spinning Wheel ...
- Python Django项目部署 Linux 服务器
项目依赖: Linux Centos7 (阿里云轻量级服务器) + Python 3.7.2 + Django 2.2.1 + restframework 3.9.4 + mysql 5.7 1 安装 ...
- 微信小程序单向数据流解决
1. 小程序中没有vue中v-model一样的双向数据机制,并且小程序也不像vue那样的进行 实时的数据驱动视图. 小程序页面数据加载完成后再去改变data中的数据页面是 不会有变化的. 2. 解决: ...
- xBIM初步使用
1.新建一个c#项目,在工具->NuGet程序包管理器->程序包管理控制台 输入如下命令: Install-Package Xbim.Essentials -Version 4.0.29 ...
- [LeetCode] 18. 4Sum 四数之和
Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = tar ...
- 关于Design Complier/Library Compiler的跌坑(坑爹)记录
最近需要用DC做一些事,然后需要转库,中午偷个闲,特来记录一下中间的一些坎坷. 1.首先是要转库.我们只有.lib文件的格式,所以需要把.lib文件转换成.db格式.然后坑来了!!!DC2015及以后 ...
- Comet OJ - Contest #7 C 临时翻出来的题(容斥+状压)
题意 https://www.cometoj.com/contest/52/problem/C?problem_id=2416 思路 这里提供一种容斥的写法(?好像网上没看到这种写法) 题目要求编号为 ...
- SqlServer 通过日志恢复数据库
前期工作 查看数据属性,确保下条件: 1.数据库属性->选项->恢复模式=完整 2.建好库以后.一个数据库完整的数据备份 3.到出事期间日志没有你间断 4.记录出事的准确时间 一.数据准备 ...
- WindowsServer -------------部署软件
1.windowsServer 中创建 lls 在服务器中创建lls 参考 将文件扩展名调出 2.安装数据库 将数据库传递到服务器中 安装sqlserver 数据库 3.在系统中创建文件存 ...
- Window安装AutoCAD
1.运行crack文件夹下的“nlm11.14.1.3_ipv4_ipv6_win64.msi”,根据向导提示完成安装2.同样在“MAGNiTUDE”文件夹中找到“adesk.dat”文件,用记事本打 ...