【搞定Java基础】第4篇:Java 中的 String 类详解 【解惑篇】

本文深入探讨Java中的String类,从内存模型、常量池、不可变性、对象创建方式、字符串连接、三大字符串类(String、StringBuilder、StringBuffer)以及正则表达式和克隆等方面全面解析。强调String的不可变性,解释为何String对象不可变,并通过实例展示String对象创建和常量池的工作原理,还对比了String、StringBuilder和StringBuffer的使用场景和效率差异。
摘要由CSDN通过智能技术生成

本文转发自:

1、https://blog.csdn.net/justloveyou_/article/details/52556427

2、https://blog.csdn.net/justloveyou_/article/details/60983034

本文目录:

一、Java 内存模型与常量池

二、常量与变量

三、String 类的定义与基础

四、String 的不可变性

五、String 对象的创建方式

六、字符串常量池

七、三大字符串类:String、StringBuilder 和 StringBuffer

八、字符串与正则表达式:匹配、替换和验证

九、String 与 (深)克隆

十、String 总结

本篇文章篇幅较长,但是对于 Java 中的 String 类进行了全方位的讲解,非常适合扫盲知识点阅读。


摘要:

  Java 中的 String 类 是我们日常开发中使用最为频繁的一个类,但要想真正掌握的这个类却不是一件容易的事情。笔者为了还原 String 类的真实全貌。笔者从 Java 内存模型展开,结合 JDK 中 String 类的源码进行深入分析,特别就 String 类与享元模式,String 常量池,String 不可变性,String 对象的创建方式,String 与正则表达式,String 与克隆,String、StringBuffer 和 StringBuilder 的区别等几个方面对其进行详细阐述、总结,力求能够对String类的原理和使用作一个最全面和最准确的介绍。 


一、Java 内存模型与常量池

1、Java 内存模型 

  • 程序计数器

  多线程时,当线程数超过 CPU 数量或 CPU 内核数量,线程之间就要根据时间片轮询抢夺 CPU 时间资源。因此,每个线程要有一个独立的程序计数器,记录下一条要运行的指令,其为线程私有的内存区域。如果执行的是 Java 方法,计数器记录正在执行的 Java 字节码地址,如果执行的是 native 方法,则计数器为空。

  • 虚拟机栈

线程私有的,与线程在同一时间创建,是管理 Java 方法执行的内存模型。

栈中主要存放一些基本类型的变量数据(byte, int, short, long, float, double, boolean, char)和对象引用。每个方法执行时都会创建一个桢栈来存储方法的的变量表、操作数栈、动态链接方法、返回值、返回地址等信息。栈的大小决定了方法调用的可达深度(递归多少层次,或嵌套调用多少层其他方法,-Xss参数可以设置虚拟机栈大小)。栈的大小可以是固定的,或者是动态扩展的。如果请求的栈深度大于最大可用深度,则抛出:stackOverflowError;如果栈是可动态扩展的,但没有内存空间支持扩展,则抛出:OutofMemoryError。使用 jclasslib 工具可以查看 class 类文件的结构。下图为栈帧结构图:

  • 本地方法区

和虚拟机栈功能相似,但管理的不是 Java 方法,是本地方法,本地方法是用 C 实现的。

  • Java 堆

线程共享的,存放所有对象实例和数组,是垃圾回收的主要区域。

堆是一个运行时数据区,类的对象从中分配空间,这些对象通过 new、newarray、 anewarray 和 multianewarray 等指令建立,它们不需要程序代码来显式的释放。堆可以分为新生代和老年代(tenured)。新生代用于存放刚创建的对象以及年轻的对象,如果对象一直没有被回收,生存的足够长,老年对象就会被移入老年代。新生代又可进一步细分为 eden(伊甸园)、survivorSpace0 (s0,from space)、survivorSpace1 (s1,tospace)。刚创建的对象都放入 eden区, s0 和 s1 都至少经过一次 GC 并幸存。如果幸存对象经过一定时间仍存在,则进入老年代(tenured)。

  • 方法区

线程共享的,用于存放被虚拟机加载的类的元数据信息,如:常量、静态变量、即时编译器编译后的代码,也成为永久代。如果 hotspot 虚拟机确定一个类的定义信息不会被使用,也会将其回收。回收的基本条件至少有:所有该类的实例被回收,而且装载该类的 ClassLoader 被回收。

2、常量池

关于常量池可以看博主的另一篇文章:Java 中的常量池:字符串常量池、class 常量池、运行时常量池

常量池属于类信息的一部分,而类信息反映到 JVM 内存模型中对应于方法区,也就是说,常量池位于方法区常量池主要存放两大常量:字面量(Literal)符号引用(Symbolic References)。其中:

字面量主要包括字符串字面量,整型字面量 和 声明为 final 的常量值等。

而符号引用则属于编译原理方面的概念,包括了下面三类常量:类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。


二、常量与变量

我们一般把内存地址不变,值可以改变的东西称为变量,换句话说,在内存地址不变的前提下内存的内容是可变的,例如:

public class String_2 {  
    public static void f(){  
        Human_1 h = new Human_1(1,30);  
        Human_1 h2 = h; 
        System.out.printf("h: %s\n", h.toString());   
        System.out.printf("h2: %s\n\n", h.toString());   

        h.id = 3;  
        h.age = 32;  
        System.out.printf("h: %s\n", h.toString());   
        System.out.printf("h2: %s\n\n", h.toString());   

        System.out.println( h == h2 );   // true : 引用值不变,即对象内存底子不变,但内容改变
    }
}  

我们一般把若内存地址不变,则值也不可以改变的东西称为常量。典型的 String 就是不可变的,所以称之为 常量(constant)。此外,我们可以通过 final 关键字来定义常量,但严格来说,只有基本类型被其修饰后才是常量(对基本类型来说是其值不可变,而对于对象变量来说其引用不可再变)。 

例如:

final int i = 5;

String a = "abc";

三、String 类的定义与基础

1、String 的声明

由 JDK 中关于String的声明可以知道:

1、不同字符串可能共享同一个底层 char 数组,例如字符串 String s=”abc” 与 s.substring(1) 就共享同一个char数组:char[ ] c = {‘a’,’b’,’c’}。其中,前者的 offset 和 count 的值分别为 0 和 3,后者的 offset 和 count 的值分别为 1 和 2。

2、offset 和 count 两个成员变量不是多余的,比如,在执行substring操作时。

2、JDK 中关于 String 的描述

The String class represents character strings. All string literals(字符串字面值) in Java programs, such as “abc”, are implemented as instances of this class. Strings are constant(常量); their values cannot be changed after they are created. String buffers【StringBuilder OR StringBuffer】 support mutable strings. Because String objects are immutable, they can be shared ( 享元模式 ).

3、String 类所内置的操作

The class String includes methods for examining individual characters of the sequence ,for examining individual characters of the sequence, for comparing strings , for searching strings , for extracting substrings and for creating a copy of a string with all characters translated to uppercase or to lowercase. Case mapping is based on the Unicode Standard version specified by the java.lang.Character class.

4、字符串串联符号(“+”)以及将其他对象转换为字符串的特殊支持

The Java language provides special support for the string concatenation operator (+), and for conversion of other objects to strings. String concatenation is implemented through the StringBuilder(JDK1.5 以后) OR StringBuffer(JDK1.5 以前) class and its append method. String conversions(转化为字符串) are implemented through the method toString, defined by class Object and inherited by all classes in Java.

5、注意:

  • String 不属于八种基本数据类型,String 的实例是一个对象

因为对象的默认值是 null,所以 String 的默认值也是null。但它又是一种特殊的对象,有其它对象没有的一些特性(String 的不可变性导致其像八种基本类型一样,比如,作为方法参数时,像基本类型的传值效果一样)。 例如,以下代码片段:

package com.zju.temp;

public class Demo {

	public static void main(String[] args) {
		
		String str = "1234";
		changeStr(str);
		System.out.println(str);
	}

	private static void changeStr(String str) {
		String s = str;
		str += "abcde";
		System.out.println(s);
	}
}

运行结果:

  • new String() 和 new String(“”) 都是声明一个新的空字符串,是空串不是 null。

四、String 的不可变性

1、什么是不可变对象?

  众所周知,在 Java 中,String 类是不可变类 (基本类型的包装类都是不可改变的) 的典型代表,也是 Immutable 设计模式的典型应用。String 变量一旦初始化后就不能更改,禁止改变对象的状态,从而增加共享对象的坚固性、减少对象访问的错误,同时还避免了在多线程共享时进行同步的需要。那么,到底什么是不可变的对象呢? 可以这样认为:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的。不能改变状态指的是不能改变对象内的成员变量,包括:

1、基本数据类型的值不能改变;

2、引用类型的变量不能指向其他的对象;

3、引用类型指向的对象的状态也不能改变。

除此之外,还应具有以下特点&#

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值