那么如果实例化之前字符串常量池不存在又会出现什么结果呢?看情况七
String s1 = new String(“1”) + new String(“1”);
s1.intern();
String s2 = “11”;
System.out.println(s1 == s2);
结果是:true,为什么呢?
我们先直接上一个图
从 s1 看起,先是两个字符串对象实例化,并将 “1” 存入了字符串变量池。之后拼接成一个新的对象,此时的数值为 “11”。后面由于有 intern,并且字符串 “11” 在变量池中不存在,所以进行入池,故字符串池中存了字符串 “11”(注意:此时存的是字符串 “11” 的地址)。再 s2 进行实例化,因为字符串变量池已经存在 “11”,所以就直接引用,故最终 s1 和 s2 的引用相同
String str1 = new String(“Hello Java”);
str1.intern();
String str2 = “Hello Java”;
System.out.println(str1 == str2);
结果是:false,为什么呢?
其实写看这种题目就是要细心,我们直接上图理清思绪
String s1 = new String(“1”) + new String(“1”);
String s2 = “11”;
s1.intern();
System.out.println(s1 == s2);
结果是:false,为什么呢?
这个情况其实和情况七就一点不同,直接上图理解
String str1 = “Hello Java”;
String str2 = str1;
str2 = “Hello World”;
System.out.println(str1 == “Hello Java”);
结果其实是:true,为什么呢?
str1 = "Hello World"
其实是将 str1 这个引用指向了一个新的 String 对象,即上述代码的整个过程可以理解为这个图
public static void func(String str1){
str1=“abc”
}
public static void main(String[] args){
String str = “Hello Java”;
func(str);
System.out.println(str == “Hello Java”);
}
结果其实还是:true,这是为啥呢?
这个其实和情况十是一种情形,虽然加了个函数,但函数里的形参的引用是指向实参引用的地址,然后将形参指向一个新的对象,对实参其实没有影响
介绍了这么多种情况我们再回顾下 == 和 equals 的用法吧!
String 使用 == 比较并不是在比较字符串的内容,而是比较两个引用是否指向同一个对象(即引用值)
这两种有什么不同吗?
面向对象编程语言中,涉及到对象的比较有三种方式:比较身份(即比较引用值)、比较值、比较类型
一般编程语言中 == 是用来比较值的,但是 Java 中是用来比较身份的(不需要记,看内存的存储就行)
比较值我们好理解,那么这个身份什么意思呢?我们来看下面一张图
诶对,大魔王现在去取快递,它的快递就放在那个红框框里。
我们可以把那个柜子的位置看看成“第二行,从左数第四个”或者是“第二行,从右数第第三个”,由于这两个位置都指向一个柜子,所以就表示身份相同,是我大魔王装快递的柜子
我们也可以在“第一行从左数第一个”柜子和“第一行从左数第二个“柜子都放入同样的物品,虽然它们不是同一个柜子,但是打开都是相同的物品,这就叫值相同,但是这
大家可以自细细感悟下,并且我们还能得到这样的结论:身份相同值一定相同,值相同身份不一定相同
因此比较字符串的时候,如果我们用 == 比较,可能明明它们的对象其实都是同一个字符串,但因为引用值的不同使结果出现错误。
故我们就要使用 equlas 去比较字符串,因为它是比较引用所指向的对象
注意:
当我们使用 equals 去比较字符串的时候,这样写代码要注意
String str1 = null;
String str2 = “abc”;
System.out.println(str1.equals(str2));
上述代码会抛出
java.lang.NullPointerException
异常,所以我们要注意 str1 的引用是非为空,故最好直接使用字面常量字符串的形式去比较,例如
String str1 = “abc”;
String str2 = “abc”;
System.out.println(“abc”.equals(str2));
===============================================================================
字符串是一种不可变的对象,它的内容不可以改变
定义 String 类中的数组我们其实可以看到它是被 final 修饰的,无法修改
这是什么意思嘞,我们先看一段代码
String str = "hello ";
str += “java”;
str += “!!!”;
System.out.println(str);
结果是:"hello java!!!"
,就是你会感觉好像字符串被修改了对吧,就像是小时候的神奇宝贝进化一样,本来是小火龙,后来变成火恐龙,最好进化成喷火龙。始终都是这一只,但小火龙进化后,原本的小火龙就没有了
但是字符串是不会向上述那样,虽然最终输出的是:"hello java!!!"
,但是 "hello "
、"java"
、"!!!"
这几个字符串都没有改变,还存在着。我们可以通过内存去理解一下
因为不是动态的所以看起来没那么顺畅,首先是1号线,表示代码的第一行,代码的第二行就是加了一个 “java”,由于常量是不可以改变的,所以不可能直接加在原有的 “hello” 后面,就新开辟一个内存,将拼接的新的字符串存入,此时 str1 的地址应该变成 0x678。3号线也是和2号线一样的步骤。所以最后其实是开辟了五个内存。原有的字符串没有改变,而是增加了新的字符串
那么如果我们想要修改字符串该怎么办呢?
注意:
字符串是不可以修改的,我这里说的修改其实是得到和原字符串上进行改变的新的字符串,但原字符串是不会改变的,例如将字符串
str = "hello"
改成str = "Hello"
方式一:借助原字符串,创建新的字符串
采用 substring 方法来提取字串
String str = “hello”;
str = “H” + str.substring(1);
System.out.println(str);
结果为:Hello,但是我们要知道
这样修改并没有改变原字符串,只是提取了它的字串,并进行了新的拼接
那么我就是想将字符串真正的改变而不是创建新的字符串有办法吗?有啊!这里需要用到反射
那什么是反射呢?
反射是 Java 类的一种自省的方式。通常情况下:类的内部细节有时候在类外是看不到的,但是通过反射就可以看到。
这其实就可以形象的理解为我们的行李箱接受安检的时候,行李箱通过检查的机器就可以直接看到行李箱内部的物品。
因此如果我们可以拿到 String 内部的字符串将它直接修改,就 🆗。那么怎么做呢?
String str = “hello”;
// 拿到字节码对象
Class c = String.class;
// 获取 String 类的 value 字段
Field field = c.getDeclareField(“value”);
// 修改该字段的权限,使其访问属性为 true
field.setAccessible(true);
// 将 str 中的 value 属性获取到
char[] vals = (char[]) field.get(str);
// 将第一个字符修改成 ‘H’
vals[0] = ‘H’;
System.out.println(str);
结果为:“Hello”,并且是直接将原字符串修改
但是我们为什么要让 String 不可变呢?
- 方便实现字符串常量池。如果可变那么对象池就需要考虑何时深拷贝字符串的问题
- 不可变对象是线程安全的
- 不可变对象更方便缓存 hash code,作为 key 时可以更高效的保存到 HashMap 中
================================================================================
之前我们就介绍到字符串内部含一个数组,即 String 应该可以和 char[] 相互转换
我搜集了下列方法
| No. | 方法名称 | 类型 | 描述 |
| — | — | — | — |
| 1 | public String(char value[])
| 构造 | 将字符数组中的所有内容变为字符串 |
| 2 | public String(char value[], int offset, int count)
| 构造 | 将部分字符数组中的内容变为字符串,offset 为偏移量,从0开始 |
| 3 | public char charAt(int index)
| 普通 | 取得指定索引的字符,索引从0开始 |
| 4 | public char[] toCharArray() | 普通 | 将字符串变为字符数组返回 |
接下来进行一一演示
示例一: 将字符数组中的所有内容变为字符串
char[] value = {‘a’, ‘b’, ‘c’, ‘d’, ‘e’};
String str = new String(value);
System.out.println(str);
结果为:abcde
示例二: 将部分字符数组中的内容变为字符串
char[] value = {‘a’, ‘b’, ‘c’, ‘d’, ‘e’};
String str = new String(value, 1, 3);
System.out.println(str);
结果为:bcd
示例三: 取得指定索引的字符,索引从0开始
String str = “abcde”;
char c = str.charAt(2);
System.out.println©;
结果为:c
示例四: 将字符串变为字符数组返回
String str = “abcde”;
char[] value = str.toCharArray();
System.out.println(Arrays.toString(value));
结果为:[a, b, c, d, e]
练习: 判断一个字符串是否都由数字组成
String str = “12213”;
char[] value = str.toCharArray();
for(int i=0; i<value.length; i++){
if(value[i]<‘0’ || value[i]>‘9’){
System.out.println(“不是都由字母组成”);
return;
}
}
System.out.println(“都是由字母组成”);
思路:
将字符串变为字符数组,然后判断每一位是否为数字
字节常用于数据传输以及编码转换的处理,字符串 String 也能和字节数组 byte[] 相互转换
我搜集了下列方法
| No. | 方法名称 | 类型 | 描述 |
| — | — | — | — |
| 1 | public String(byte bytes[])
| 构造 | 将字节数组变成字符串 |
| 2 | public String(byte bytes[], int offset, int length)
| 构造 | 将部分字节数组中的内容变为字符串 |
| 3 | public byte[] getBytes()
| 普通 | 将字符串以字节数组的形式返回 |
| 4 | public byte[] getBytes(String charsetName)throws java.io.UnsupportedEncodingException
| 普通 | 编码转换处理 |
接下来进行一一演示
示例一: 将字节数组变成字符串
byte[] bytes = {97, 98 ,99 ,100};
String str = new String(bytes);
System.out.println(str);
结果为:abcd
示例二: 将部分字节数组中的内容变为字符串
byte[] bytes = {97, 98 ,99 ,100};
String str = new String(bytes, 1, 2);
System.out.println(str);
结果为:bc
示例三: 将字符串以字节数组的形式返回
public static void main(String[] args) {
String str = “abcde”;
byte[] bytes = str.getBytes();
System.out.println(Arrays.toString(bytes));
}
结果为:[97, 98, 99, 100, 101]
示例四: 编码转换处理
public static void main(String[] args)throws java.io.UnsupportedEncodingException {
String str = “魔王”;
byte[] bytes = str.getBytes(“GBK”);
System.out.println(Arrays.toString(bytes));
}
结果为:[-60, -89, -51, -11]
如果我们将编码方式 “GBK” 改成 “utf-8”,则会有不同的结果
public static void main(String[] args)throws java.io.UnsupportedEncodingException {
String str = “魔王”;
byte[] bytes = str.getBytes(“utf-8”);
System.out.println(Arrays.toString(bytes));
}
结果为:[-23, -83, -108, -25, -114, -117]
注意:
细心的朋友在演示示例二的时候会发现,当我们只输了第二个参数时,String 被打了一个横线
这是为什么呢?通过转到定义
我们看到了
@Deprecated
这样的注解,这个表示这个方法已经被弃用了,这在我们今天介绍 String 的构造方法那张图里面就有这个方法,也被表明被弃用
对于字节数组:
byte[] 是把 String 按照一个字节一个字节的方式处理,适合用在网络传输、数据存储、针对二进制数据操作的场景
对于字符数组:
char [] 是把 String 按照一个字符一个字符的方式处理,适合用在对文本数据操作,尤其是包含中文的时候
那啥又是文本数据和二进制数据呢?其实可以这样记忆
看的懂得就是文本数据(例如
.java
文件),看不懂的就是二进制数据(例如 .class文件
)
==============================================================================
上述介绍到 equals 可以比较字符串是否相等,并且是区分大小写的。而除了它,String 类还有其他比较字符串的方法
我搜集了下列方法
| NO. | 方法名称 | 类型 | 描述 |
| — | — | — | — |
| 1 | public boolean equals(Object anObject)
| 普通 | 区分大小写的比较 |
| 2 | public boolean equalsIgnoreCase(String anotherString)
| 普通 | 不区分大小写的比较 |
| 3 | public int compareTo(String anotherString)
| 普通 | 比较两个字符串大小关系 |
接下来进行一一演示
示例一: 区分大小写的比较
String str1 = “hello”;
String str2 = new String(“Hello”);
System.out.println(str1.equals(str2));
结果为:false
示例二: 不区分大小写的比较
String str1 = “hello”;
String str2 = new String(“Hello”);
System.out.println(str1.equalsIgnoreCase(str2));
结果为:true
示例三: 比较两个字符串大小关系
String str1 = “hello”;
String str2 = new String(“Hello”);
System.out.println(str1.compareTo(str2));
结果为:32,这个结果是怎么来的呢?
字符串的大小比较,其实是“字典序”的比较,将两个字符串从第一个字母开始,一一比较,如果相等就比较下一个,如果不等就停止比较,结果返回的是较大的字母减去较小字母的差值
我们做算法题经常碰到某个字符串中是否存在一个指定的内容,而 String 类中就有专门的查找方法
我搜集了下列方法
| No. | 方法名称 | 类型 | 描述 |
| — | — | — | — |
| 1 | public boolean contains(CharSequence s)
| 普通 | 判断一个字符串是否存在 |
| 2 | public int indexOf(String str)
| 普通 | 从头开始查找指定字符串的位置,查到了返回位置的开始索引,查不到返回-1 |
| 3 | public int indexOf(String str, int fromIndex)
| 普通 | 从指定位置开始查找子字符串位置 |
| 4 | public int lastIndexOf(String str)
| 普通 | 由后向前查找子字符串位置 |
| 5 | public int lastIndexOf(String str, int fromIndex)
| 普通 | 从指定位置由后向前查找 |
| 6 | public boolean startsWith(String prefix)
| 普通 | 判断是否以指定字符串开头 |
| 7 | public boolean startsWith(String prefix, int toffset)
| 普通 | 从指定位置开始判断是否以指定字符串开头 |
| 8 | public boolean endWith(String suffix)
| 普通 | 判断是否以指定字符串结尾 |
接下来进行一一演示
示例一: 判断一个字符串是否存在
String str = “helloworld”;
System.out.println(str.contains(“hello”));
结果为:true
示例二: 从头开始查找指定字符串的位置,查到了返回位置的开始索引,查不到返回-1
String str = “helloworld”;
System.out.println(str.indexOf(“world”));
结果为:5
示例三: 从指定位置开始查找子字符串位置
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
最后
终极手撕架构师的学习笔记:分布式+微服务+开源框架+性能优化
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
学效果低效又漫长,而且极易碰到天花板技术停滞不前!**
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-fpAuGdKz-1712463111051)]
[外链图片转存中…(img-HZdDQbKP-1712463111051)]
[外链图片转存中…(img-vk2qvyIX-1712463111051)]
[外链图片转存中…(img-SC8R5HQ9-1712463111052)]
[外链图片转存中…(img-TINVE36g-1712463111052)]
[外链图片转存中…(img-VlyLAuYh-1712463111052)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-AFhrATEi-1712463111053)]
最后
终极手撕架构师的学习笔记:分布式+微服务+开源框架+性能优化
[外链图片转存中…(img-ndICyx0l-1712463111053)]
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算