java基础知识温故(面试常问的基础知识二)

本文深入解析String、StringBuffer与StringBuilder的特性与差异,探讨线程的六种状态及其转换机制,阐明为何String不是基本数据类型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

java基础知识温故(面试常问的基础知识二)

1.说说String,StringBuffer和StringBuilder异同
(1)String
String类是不可变类,即一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。可以看下下面的代码

String a = "123";
a = "456";
// 打印出来的a为456
System.out.println(a)

再次给a赋值时,并不是对原来堆中实例对象进行重新赋值,而是生成一个新的实例对象,并且指向“456”这个字符串,a则指向最新生成的实例对象,之前的实例对象仍然存在,如果没有被再次引用,则会被垃圾回收。关于String的知识可以看我之前的文章:

https://blog.csdn.net/qq_34136709/article/details/106995328

(2)StringBuffer
StringBuffer对象则代表一个字符序列可变的字符串,当一个StringBuffer被创建以后,通过StringBuffer提供的append()、insert()、reverse()、setCharAt()、setLength()等方法可以改变这个字符串对象的字符序列。一旦通过StringBuffer生成了最终想要的字符串,就可以调用它的toString()方法将其转换为一个String对象。

StringBuffer b = new StringBuffer("123");
b.append("456");
// b打印结果为:123456
System.out.println(b);

所以说StringBuffer对象是一个字符序列可变的字符串,它没有重新生成一个对象,而且在原来的对象中可以连接新的字符串。

(3)StringBuilder
StringBuilder类也代表可变字符串对象。实际上,StringBuilder和StringBuffer基本相似,两个类的构造器和方法也基本相同。不同的是:StringBuffer是线程安全的,而StringBuilder则没有实现线程安全功能,所以性能略高。
为啥StringBuffer是线程安全的呢,我们可以看看其源码,StringBuffer类中的方法都添加了synchronized关键字,也就是给这个方法添加了一个锁,用来保证线程安全。

拓展:Java9改进了字符串(包括String、StringBuffer、StringBuilder)的实现。在Java9以前字符串采用char[]数组来保存字符,因此字符串的每个字符占2字节;而Java9的字符串采用byte[]数组再加一个encoding-flag字段来保存字符,因此字符串的每个字符只占1字节。所以Java9的字符串更加节省空间,字符串的功能方法也没有受到影响。

(2)线程有哪些状态
针对线程的状态,首先来看下官方的源码(除去了注释):

public enum State {
	NEW,
	RUNNABLE,
	BLOCKED,
	WAITING,
	TIMED_WAITING,
	TERMINATED;
}

所以从官方的角度,线程是有6种状态的,下面就说说这6种状态
NEW:线程被创建出来了,但是还没有start()。

RUNNABLE:可运行状态,这个状态比较特殊,一部分是READY,顾名思义是准备状态,另一部分是RUNNING,即运行状态。

准备状态:只能说明你有资格运行,单只要调度程序没有调度到你,你就永远是准备状态。从图中可以看出,好几个状态都能切换到准备状态,至于切换的方法,我在下文详细给大家整理出来介绍。

运行状态:线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。说白了,就是线程跑起来了。

BLOCKED:阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。

WAITING:等待状态的线程不会被分配 CPU 执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。

TIMED_WAITING:超时等待状态的线程不会被分配 CPU 执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。这是和上面的 WAITING 状态的区别。

TERMINATED:终止状态,顾名思义,线程执行结束了。

线程状态之间的切换函数分析
线程之间的状态切换,主要涉及到以下几个函数:

Thread.sleep、Thread.yeild、Object.wait、Thread.join、Object.notify/notifyAll、LockSupport.park/unpark/parkNanos/parkUtil

Thread.sleep(time):当前线程调用此方法,顾名思义,就是让当前线程进入 TIMED_WAITING 状态,睡眠固定的时间(但是不释放对象锁),到点后自动唤醒,进入准备状态。主要作用是给其他线程执行机会。

Thread.yield():当前线程调用此方法,放弃获取的CPU时间片,但不释放锁资源,由运行状态变为准备状态,让OS再次选择线程。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。它跟 sleep方法的区别在于不能指定暂停多少时间。

Object.wait():当前线程调用对象的wait()方法,当前线程释放对象锁,进入WAITING 状态。依靠notify()/notifyAll()来唤醒;而wait(time)方法的主要区别是进入 TIMED_WAITING 状态,到达时间后,自动唤醒。

Object.notify/notifyAll:notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。

Thread.join():当前线程里调用其它线程 t 的join()方法,当前线程进入 WAITING 状态,当前线程不会释放已经持有的对象锁。线程 t 执行完毕,当前线程进入 RUNNABLE 状态。而join(time)方法的主要区别是当前线程进入 TIMED_WAITING 状态,到达时间后,进入 RUNNABLE 状态。

LockSupport.park():当前线程进入 WAITING 状态,需要通过LockSupport.unpark(thread)来唤醒。

LockSupport.parkNanos(nanos)/parkUntil(deadlines):当前线程进入 TIMED_WAITING 状态,需要通过``LockSupport.unpark(thread)`来唤醒。

相比与wait方法,LockSupport不需要获得锁就可以让线程进入 WAITING 或者 TIMED_WAITING 状态。

3.为啥String不是基本数据类型

java 中String 是个对象,是引用类型 ,基础类型与引用类型的区别是,基础类型只表示简单的字符或数字,引用类型可以是任何复杂的数据结构 ,基本类型仅表示简单的数据类型,引用类型可以表示复杂的数据类型,还可以操作这种数据类型的行为 。

java虚拟机处理基础类型与引用类型的方式是不一样的,对于基本类型,java虚拟机会为其分配数据类型实际占用的内存空间,而对于引用类型变量,他仅仅是一个指向堆区中某个实例的指针。

本文就说到这,后期继续更新,希望对大家有所帮助,欢迎留言与评论,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值