【Java 基础语法】万字解析 Java 的 String

那么如果实例化之前字符串常量池不存在又会出现什么结果呢?看情况七

3.7 情况七:


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 的引用相同

3.8 情况八:


String str1 = new String(“Hello Java”);

str1.intern();

String str2 = “Hello Java”;

System.out.println(str1 == str2);

结果是:false,为什么呢?

其实写看这种题目就是要细心,我们直接上图理清思绪在这里插入图片描述

3.9 情况九:


String s1 = new String(“1”) + new String(“1”);

String s2 = “11”;

s1.intern();

System.out.println(s1 == s2);

结果是:false,为什么呢?

这个情况其实和情况七就一点不同,直接上图理解在这里插入图片描述

3.10 情况十:


String str1 = “Hello Java”;

String str2 = str1;

str2 = “Hello World”;

System.out.println(str1 == “Hello Java”);

结果其实是:true,为什么呢?

str1 = "Hello World" 其实是将 str1 这个引用指向了一个新的 String 对象,即上述代码的整个过程可以理解为这个图在这里插入图片描述

3.11 情况十一:


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 的用法吧!

3.12 == 和 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));

4. 理解字符串不可变

===============================================================================

4.1 分析


字符串是一种不可变的对象,它的内容不可以改变

定义 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号线一样的步骤。所以最后其实是开辟了五个内存。原有的字符串没有改变,而是增加了新的字符串

4.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 中

5. 字符、字节、字符串

================================================================================

5.1 字符串与字符


之前我们就介绍到字符串内部含一个数组,即 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(“都是由字母组成”);

思路:

将字符串变为字符数组,然后判断每一位是否为数字

5.2 字符串与字节


字节常用于数据传输以及编码转换的处理,字符串 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 的构造方法那张图里面就有这个方法,也被表明被弃用

5.3 字节数组和字符数组使用场景


对于字节数组:

byte[] 是把 String 按照一个字节一个字节的方式处理,适合用在网络传输数据存储针对二进制数据操作的场景

对于字符数组:

char [] 是把 String 按照一个字符一个字符的方式处理,适合用在对文本数据操作尤其是包含中文的时候

那啥又是文本数据和二进制数据呢?其实可以这样记忆

看的懂得就是文本数据(例如 .java 文件),看不懂的就是二进制数据(例如 .class 文件

6. 字符串常见操作

==============================================================================

6.1 字符串比较


上述介绍到 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,这个结果是怎么来的呢?

字符串的大小比较,其实是“字典序”的比较,将两个字符串从第一个字母开始,一一比较,如果相等就比较下一个,如果不等就停止比较,结果返回的是较大的字母减去较小字母的差值

6.2 字符串查找


我们做算法题经常碰到某个字符串中是否存在一个指定的内容,而 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开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

最后

终极手撕架构师的学习笔记:分布式+微服务+开源框架+性能优化

image

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

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、安卓逆向、云计算

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值