Java之String重点解析,Javaweb资料视频

前言

在面经之前,我想从个人感受上说两点很重要的场外因素:面试节奏和心理准备!

作为一名即将求职的程序员,面对一个可能跟近些年非常不同的2021 年,你的就业机会和风口会出现在哪里?在这种新环境下,工作应该选择大厂还是小公司?已有几年工作经验的老兵,又应该如何保持和提升自身竞争力,转被动为主动?

就目前大环境来看,跳槽成功的难度比往年高很多。一个明显的感受:今年的面试,无论一面还是二面,都很考验Java程序员的技术功底。

String string = “abcd”;

String string1 = string.replace(“a”,“b”);

System.out.println(string);

System.out.println(string1);

输出:

abcd

bbcd

string.replace(“a”,“b”) 这个方法把 “abcd” 中的 a 换成了 b 。通过输出可以发现,原字符串string 并没有发生任何改变, replace 方法构造了一个新的字符串 “bbcd” 并赋值给了string1 变量。这就是String的不可变性。

再举个栗子:把 “abcd” 的最后一个字符 d 改成 a ,在c/c++语言中,直接修改最后一个字符即可;而在java中,需要重新创建一个String对象: abca ,因为 “abcd” 本身是不可变的,不能被修改。

String对象值是不可变的,一切操作都不会改变String的值,而是通过构造新的字符串来实现字符串操作。

很多时候很难理解,为什么Java要如此设计,这样不是会导致性能的下降吗?回顾一下我们日常使用String的场景,更多的时候并没有直接去修改一个string,而是使用一次,则被抛弃。但下次,很可能,又再一次使用到相同的String对象。例如日志打印:

Log.d(“MainActivity”,string);

前面的 “MainActivity” 我们并不需要去更改他,但是却会频繁使用到这个字符串。Java把String设计为不可变,正是为了保持数据的一致性,使得相同字面量的String引用同个对象。例如:

String s1 = “hello”;

String s2 = “hello”;

s1 与 s2 引用的是同个String对象。如果String可变,那么就无法实现这个设计了。因此,我们可以重复利用我们创建过的String对象,而无需重新创建他。

基于重复使用String的情况比更改String的场景更多的前提下,Java把String设计为不可变,保持数据一致性,使得同个字面量的字符串可以引用同个String对象,重复利用已存在的String对象。

在《Java编程思想》一书中还提到另一个观点。我们先看下面的代码:

public String allCase(String s){

return string.toUpperCase();

}

allCase 方法把传入的String对象全部变成大写并返回修改后的字符串。而此时,调用者的期望是传入的String对象仅仅作为提供信息的作用,而不希望被修改,那么String不可变的特性则非常符合这一点。

使用String对象作为参数时,我们希望不要改变String对象本身,而String的不可变性符合了这一点。

存储原理

====

由于String对象的不可变特性,在存储上也与普通的对象不一样。我们都知道对象创建在 上,而String对象其实也一样,不一样的是,同时也存储在 常量池 中。处于堆区中的String对象,在GC时有极大可能被回收;而常量池中的String对象则不会轻易被回收,那么则可以重复利用常量池中的String对象。也就是说, 常量池是String对象得以重复利用的根本原因

常量池不轻易垃圾回收的特性,使得常量池中的String对象可以一直存在,重复被利用。

往常量池中创建String对象的方式有两种: 显式使用双引号构造字符串对象、使用String对象的 intern() 方法 。这两个方法不一定会在常量池中创建对象,如果常量池中已存在相同的对象,则会直接返回该对象的引用,重复利用String对象。其他创建String对象的方法都是在堆区中创建String对象。举个栗子吧。

当我们通过 new String() 的方法或者调用String对象的实例方法,如 string.substring() 方法,会在堆区中创建一个String对象。而当我们使用双引号创建一个字符串对象,如 String s = “abc” ,或调用String对象的 intern() 方法时,会在常量池中创建一个对象,如下图所示:

Java之String重点解析

还记得我们文章开头的问题吗?

  • String s = new String(“abc”) ,这句代码创建了几个对象? “abc” 在常量池中构造了一个对象, new String() 方法在堆区中又创建了一个对象,所以一共是两个。

  • s==“abc” 的结果是false。两个不同的对象,一个位于堆中,一个位于常量池中。

  • s.substring(0,2).intern()==“ab” intern方法在常量池中构建了一个值为“ab"的String对象,"ab"语句不会再去构建一个新的String对象,而是返回已经存在的String对象。所以结果是true。

只有 显式使用双引号构造字符串对象、使用String对象的 intern() 方法 这两种方法会在常量池中创建String对象,其他方法都是在堆区创建对象。每次在常量池创建String对象前都会检查是否存在相同的String对象,如果是则会直接返回该对象的引用,而不会重新创建一个对象。

关于intern方法还有一个问题需要讲一下,在不同jdk版本所执行的具体逻辑是不同的。在jdk6以前,方法区是存放在永生代内存区域中,与堆区是分割开的,那么当往常量池中创建对象时,就需要进行深拷贝,也就是把一个对象完整地复制一遍并创建新的对象,如下图:

Java之String重点解析

永生代有一个很严重的缺点: 容易发生OOM 。永生代是有内存上限的,且很小,当程序大量调用intern方法时很容易就发生OOM。在JDK7时将常量池迁移出了永生代,改在堆区中实现,jdk8以后使用了本地空间实现。jdk7以后常量池的实现使得在常量池中创建对象可以进行浅拷贝,也就是不需要把整个对象复制过去,而只需要复制对象的引用即可,避免重复创建对象,如下图:

Java之String重点解析

观察这个代码:

String s = new String(new char[]{‘a’});

s.intern();

System.out.println(s==“a”);

在jdk6以前创建的是两个不同的对象,输出为false;而jdk7以后常量池中并不会创建新的对象,引用的是同个对象,所以输出是true。

jdk6之前使用intern创建对象使用的深拷贝,而在jdk7之后使用的是浅拷贝,得以重复利用堆区中的String对象。

《一线大厂Java面试真题解析+Java核心总结学习笔记+最新全套讲解视频+实战项目源码》开源

Java优秀开源项目:

  • ali1024.coding.net/public/P7/Java/git

最后

由于篇幅原因,就不多做展示了
9565925650)]

[外链图片转存中…(img-35hKFcDh-1649565925651)]

[外链图片转存中…(img-zzgNLKx1-1649565925651)]

[外链图片转存中…(img-br4SS5Qo-1649565925652)]

[外链图片转存中…(img-wKeWmNwL-1649565925652)]

由于篇幅原因,就不多做展示了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值