首页 > 编程语言 >Java学习之String

Java学习之String

时间:2022-11-01 10:32:41浏览次数:78  
标签:字符 Java String 对象 学习 str 字符串


概述

写在前面,工作第四年,重新把基础抓起来吧。String可以说是JDK中最基础的一个类。就记录一些日常开发中最常用的方法。

String类是非可变类,其对象一旦创建,就不可销毁。String类那些看似修改字符序列的方法实际上都是返回新创建的String对象,而不是修改自身对象。由于String对象是不可改变的,因此具有线程安全性,可以自由地实现共享。在String类内部,是使用一个字符数组(char[])来维护字符序列的。String的最大长度也就是字符数组的最大长度,理论上最大长度为int类型的最大值,即2147483647。在实际中,一般可获取的最大值小于理论最大值。

实战

UnknownFormatConversionException

代码很简单:

return String.format("#iview%s监控#\n"
+ "\n预警信息:" + msg
+ "\n\n数据异常,请及时处理", typeName);

将​​typeName​​​替换掉监控前面的​​%s​​​,表示某种类型的监控,并拼接​​msg​​​。而​​String​​实际上的执行逻辑是:先拼接后替换。报错的原因是​​msg​​​里面也含有​​%s​​,于是报错。解决方法:

return String.format("#iview%s监控#\n", typeName)
+ "\n预警信息:" + msg
+ "\n\n数据异常,请及时处理";

先替换后拼接。
类似的错误:​​​stackoverflow​

String&StringBuilder&StringBuffer

StringBuilder是可变的,这就意味你在创建对象之后还可以去修改它的值。
StringBuffer是同步的,意味着它是线程安全的,会比StringBuilder慢些。

用空格去分隔字符串?

用正则表达式:"\s"即表示空格,其他还有如"","\t","\r","\n"。
​​​String[] strArray = "johnny 233".split("\\s+");​

trim()

问题背景:
之前公司开发时,遇到的一个问题。docker环境下,启动镜像注册应用时输入用户名和密码,然后密码会在容器里有shell脚本去做一层加密处理,再次登录应用时,shell脚本会去解密密文,然后应用层判断一下登录时输入的密码和保存的密码(经过加密后)是否一样,一样则验证通过,准许登录应用。大致如此。现在的问题是,明明记得相当清楚密码就是一个数字1,可是怎么都验证不通过,反复两次都是这样。一开始还怀疑他人写的加密解密shell脚本的问题,或者是存储的问题(密文存储到PostgresSQL中)。后来想着把错误推到别人头上之前,先确认一下自己的问题,就增加一个logger记录:

password from FE [1], password from shell [1
]

此时才恍然大悟。在Java程序里面调用shell脚本时返回字符串包含\t这样的特殊字符,加上trim解决问题。之前一直以为trim()方法用于去掉字符串前后的空格。google之后:trim()方法去掉编码小于等于\u0020的字符,也就是在ASCII码里面十六进制20以前的字符。要保留\t,\n的要慎用trim()。

split()

两种重载形式:
​​​public String[] split(String regex)​​​等价于​​public String[] split(String regex, int limit)​​​其中limit=0,limit用来限制返回数组中的元素个数
regex为空字符串时,返回包含整个字符串的单一元素数组;
“.“需要加转义字符变成”\.”;
如果在一个字符串中有多个分隔符,可以用"|“作为连字符,即”\.|\。"。(有中英文句号两个分隔符)
当字符串只包含分隔符时,返回数组没有元素;
当字符串不包含分隔符时,返回数组只包含一个元素(该字符串本身);
字符串最尾部出现的分隔符可以看成不存在,不影响字符串的分隔;
字符串最前端出现的分隔符将分隔出一个空字符串以及剩下的部分的正常分隔;

intern()

Java源码只有一行:​​public native String intern();​​文档:

Returns a canonical representation for the string object. A pool of strings, initially empty, is maintained privately by the class. When the intern method is invoked, if the pool already contains a string equal to this object as determined by the equals method, then the string from the pool is returned. Otherwise, this object is added to the pool and a reference to this object is returned. All literal strings and string-valued constant expressions are interned. String literals are defined in section 3.10.5 of the The Java Language Specification.
@return a string that has the same contents as this string, but is guaranteed to be from a pool of unique strings.

对于两个字符串s和t,​​s.intern() == t.intern()​​​当且仅当​​s.equals(t)​​​。
intern()方法的调用会先去字符串常量池中查找相应的字符串,如果字符串不存在,就会在字符串常量池中创建该字符串然后再返回。
字符串常量池是一个固定大小的HashMap,桶的数量默认是1009, 从Java7u40开始,该默认值增大到60013。在Java6当中,字符串常量池是放在Perm空间的,从Java7开始字符串常量池被移到Heap空间。

replaceAll() & replace()

需求:
拿到一个JSON字符串:​​​{"number":5,"vendorType":1,"refresh":null}​​​,现在期望对JSON字符串里面的引号进行转义,得到这样的输出:​​{\"number\":5,\"vendorType\":1,\"refresh\":null}​​​。
看String的API,理所当然地使用replaceAll么,因为是想要对所有的双引号进行替换:​​​sss.replaceAll("\"", "\\\"")​​​。但是这个输出并不是自己期望的。一脸懵逼。然后看到​​Java 引号为什么要这样转义?​​​。
即有两种方法实现:

sss.replace("\"", "\\\"");
sss.replaceAll("\"", "\\\\\"");

实际上,无论是replaceAll()还是replace()方法,都是使用正则表达式匹配,是全文替换的。然后replaceAll()源码文档里面写有两种情况下可能会有意想不到的情况发生,另一个字符是​​$​​​。
至于为什么是5个斜杠,参考上面的链接:
需要一个 \ 来给替换函数转义
还需要在每个 \ 前再加一个 \ 来给 Java 编译器转义
再还需要一个 \ 来给 Java 编译器转义 "

另外,带有引号的JSON字符串copy到IDEA时,IDEA会自动进行转义。

“a”+"b"会产生几个对象

​String str="a"+"b";​​​产生几个对象?面试考察,很变态是吧。
只能从字节码分析入手:
用IDEA看class文件,看到的是"ab",反编译器是直接从class文件中取"ab",即在javac编译,已经加以优化。
​​​javap -v AB1.class​​看字节码指令只有一个对象。

拓展:

String a = "a";
String b = "b";
String result = a + b ;

产生几个对象?
使用StringBuilder进行拼接。此时有三个对象 a、b、result,以及StringBuilder对象,共四个对象。

​new String("ab")​​​产生几个对象?
new String时会额外强制新建一个对象,共两个对象。
​​​参考​

快速重复构造一段字符串

无论是使用guava还是使用Apache的commons-lang3都可以实现:
​​​StringUtils.repeat("johnny", 233); Strings.repeat("johnny", 233)​

统计一个字符在某个字符串中出现的次数

​StringUtils.countMatches("11112222", "1");​

字符串用于switch表达式

从JDK7开始,可以在switch条件表达式中使用字符串:

switch (str.toLowerCase()) {
case "a":
value = 1;
break;
case "b":
value = 2;
break;
}

substring()方法

在JDK6中,这个方法只会在标识现有字符串的字符数组上 给一个窗口来表示结果字符串,但是不会创建一个新的字符串对象。如果需要创建个新字符串对象,可以这样在结果后面+一个空的字符串:
str.substring(m, n) + “”

这么写的话就会创建一个新的字符数组来表示结果字符串。同时,这么写也有一定的几率让你的代码跑的更快,因为垃圾回收器会吧没有在使用的大字符串回收而留下子字符串。
Oracle JDK7中的substring()方法会创建一个新的字符数组,而不用之前存在的。看看这张图就会明白substring()方法在JDK6和JDK7中的区别。

将时间格式的字符串转换成date对象

String str = "Sep 17, 2013";
Date date = new SimpleDateFormat("MMMM d, yy", Locale.ENGLISH).parse(str);
System.out.println(date);//Tue Sep 17 00:00:00 EDT 2013

创建字符串对象时,使用字面值和new String()构造器的区别

当使用new String构造器来创建字符串的时候,字符串的值会在堆中创建,而不会加入JVM的字符串池中。相反,使用字面值创建的String对象会被放入堆的PermGen段中。例如:​​String str=newString(“Test”);​​​这句代码创建的对象str不会放入字符串池中,需要显式调用String.intern()方法来将它放入字符串池中。仅仅当你使用字面值创建字符串时,Java才会自动将它放入字符串池中,如:String s=”Test”。将参数“Test”传入构造器的时候,这个参数是个字面值,因此它也会在字符串池中保存另外一份。这篇​​文章​​。

下图很好地解释这种差异。

Java学习之String_java

参考

​Java中“a”+“b”究竟会产生几个对象?​


标签:字符,Java,String,对象,学习,str,字符串
From: https://blog.51cto.com/u_15851118/5811980

相关文章

  • Java学习之JDK9新特性
    写在前面:现在(2019-01-12)绝大多数的公司或者个人都在使用JDK8,这一点毋庸置疑,但是不排除那些需要自我反省一下的落后者还在使用JDK5~7。毕竟JDK12都出来了。参考​​​JDK12......
  • 面试之基础算法题:求一个数字在给定的已排序数组中出现的起始、终止索引号(Java版)
    题目给定一个升序的整数数组,查找某一个值在数组中出现的索引号,例如,输入数组​​[2,3,3,4,4,5]​​​;查找的数是3,则返回​​[1,2]​​。时间复杂度要求为O(logN)。思路......
  • Vue3学习(四)
    连接数据库实现数据防抖shake.jsexportfunctionshake(fn,delay){lettime=null;returnfunction(){letparameter=arguments;if(time){......
  • Vue3学习(五)
    设置全局参数,调用参数三种方法,阻止默认事件,阻止冒泡,capture捕获设置全局参数 main.ts import{createApp}from'vue'importAppfrom'./App.vue'importrouter......
  • java操作http请求的三种方式
    java操作http请求的三种方式一、HttpClient步骤:1.获取一个Http客户端CloseableHttpClienthttpClient=HttpClients.createDefault();2.创建一个请求HttpGethttpGet......
  • Vue3学习(二)
     <template><h1>方法</h1><inputtype="text"v-model="input3"@input="meth()">+<inputtype="text"v-model="input4"@input="meth()">=......
  • Vue3学习(三)
    <template><h2>姓名:{{user.name}}</h2><h2>姓名:{{user.age}}</h2><h2>姓名:{{user.job.salsry}}k</h2><button@click="user.name+='%'">修改姓名</bu......
  • Python学习笔记
    20221031:对比python的列表数组与numpy插件在数据处理效率上的差别,importtime导入了常用的模板,importnumpyasnp导入numpy模块,别名为np。当时在操作时,pycharm没有安装num......
  • Java 基于 SpringCloud 数据中台 ETL 工具,可以进行多种常见数据库之间的数据或结构迁
    基于SpringCloud数据中台ETL工具,可以进行多种常见数据库之间的数据或结构迁移提供源端数据库向目的端数据库的批量迁移同步功能,支持数据的全量和增量方式同步。包括:......
  • node 学习笔记
    1.通过node执行js1.复制文件所在路径2.node+路径2.node读写文件1.引入file模块读取文件2.fs.readFile(‘./read/hell.txt’,function(error,data){if(error){con......