系列文章目录
🌈座右铭🌈:人的一生这么长、你凭什么用短短的几年去衡量自己的一生!
文章目录
目录
[2、空字符串' " " '](#2、空字符串' " " ')
前言
这篇文章我会为大家介绍String类当中最重要的一些概念,这篇文章更注重理论层面,从String的构造方法到String常量池以及一些拓展知识帮助大家一次性理清什么是String类,String类当中的一些常用方法反倒是次要的,我们只需要了解即可不需要专门去记忆。
一、String构造的四种方式
在Java当中有多种方式可以创建字符串对象,接下来我为大家展示我们常用的四种方式
1、字符串字面量
使用双引号括起来的字符序列被称为字符串字面量,Java会自动再内存当中创建一个字符串对象,并且讲该字面量赋值给这个对象。
javaString str1 = "Hello, World!";
这里的"Hello World"就是我们所说的字符串字面量,虚拟机会在内存当中开辟一个字符串对象并且将这个对象赋值给str1。稍后我就会为大家详细地讲解字面量的相关知识,如果这方面知识有不了解的同学先不要着急。
2、使用new关键字
我们也可以直接使用new关键字来创建一个字符串对象,代码如下:
javaString str2 = new String("Hello, World!");
通过new关键字来创建对象的话会与字面量创建对象有一定的区别,这种方式会直接在堆内存当中创建一个新的对象,而不是字符串的常量池,通常情况下直接使用字符串字面量的方式会更加地高效,也是我们推荐使用的一种写法使用new关键字我们不推荐使用。
3、空字符串
创建一个空字符串可以使用空的字符串的字面量或者可以使用Sting类的无参构造函数。
javaString emptyStr1 = ""; String emptyStr2 = new String();
这两种方式都创建了一个空字符串,两者表示相同的内容为空的字符串对象。但是在这里我们需要区分一个重要的知识点那么就是null字符串和" "空字符串的区别。
拓展:
接下来我为大家详细地介绍一下null字符串和空字符串的区别是什么。
1、null字符串
null表示变量没有初始化也就是这个引用没有引用任何的引用对象。
如果一个字符串变量被赋值为null表示该变量不引用任何的字符串对象
使用null表示缺少对象,即没有指向任何有效的字符串
当我们使用null字符串的时候如果调用任何关于字符串内容的方法,将会引发NullPointerException 异常,因为没有实际的字符串对象(我们可以参考C语言当中的指针,一个空指针我们能够对他进行解引用吗?显然是不可以的,如果有对于Java当中的引用没有具体了解的小伙伴可以去看我之前写过的文章,相信你会有不小的收获。)
javaString nullString = null; System.out.println(nullString); // 输出 null System.out.println(nullString.length()); // 会引发 NullPointerException
2、空字符串' " " '
空字符串是一个包含零个字符的字符串
通过" "表示一个有效的但是内容为空的字符串对象
调用空字符串对象不会引发异常,因为它指向的是一个有效的字符串对象
javaString emptyString = ""; System.out.println(emptyString); // 输出空字符串 System.out.println(emptyString.length()); // 输出 0
这里我要提醒一下空字符串当中是没有空格的,表达形式为"",我之前为了书写美观加上了空格,这里特别强调一下,因为空格也是一个字符,他也能代表一个长度,这里大家注意一下。
javapackage Yangon; public class mystring { public static void main(String[] args) { String string = getString(); if(string != null){ System.out.println(string.length()); }else{ System.out.println("String is null"); } String nonNullString = (string != null) ? string : ""; System.out.println(nonNullString);//使用空字符串作为默认值避免引发异常 } public static String getString(){ return null; } }
总结:
null字符串引用没有指向任何一个内存当中的字符串,可以对比C语言当中的空指针,但是空字符串引用是的的确确指向了内存当中的一个字符串的,只不过这个字符串为空而已。
4、通过字符数组创建
我们可以使用字符数组来创建一个字符串
javachar[] charArray = {'H', 'e', 'l', 'l', 'o'}; String str3 = new String(charArray);
我们这里需要特别地注意字符串在Java当中是不可变的,一旦创建了一个字符串对象其内容就不能被修改,引用也不可以修改,也就是说一个字符串对象引用指向了一个字符串就不可以再指向另外一个字符串了,内容不可以被修改的意思是,这个字符串对象引用指向的内存当中的某个字符串当中的内容是不可以修改的,任何对于字符串修改的操作,实际上都是创建了另外一个字符串对象,这种不可变性对有助于确保字符串的安全性和线程安全性,这些内容我后面会为大家介绍。下面我为大家详细介绍字符数组构造字符串的方法。
1、使用字符数组的构造方法
javachar[] charArray = {'H', 'e', 'l', 'l', 'o'}; String str1 = new String(charArray); // 构造整个字符数组 String str2 = new String(charArray, 0, 3); // 构造部分字符数组,从索引0开始,长度为3
在这个例子中,str1 包含整个字符数组 "Hello",而 str2 包含字符数组的前三个字符 "Hel"。
2、使用字符串的valueOf方法
String类还提供了一个ValueOf方法,该方法可以接受字符数组作为参数,并且返回对应的字符串对象。
javachar[] charArray = {'W', 'o', 'r', 'l', 'd'}; String str = String.valueOf(charArray);
两种方式大同小异,以上就是四种构造String类对象的方式,全部为大家介绍完毕。
二、字面常量与new的构造的区别
1、字符串常量池
在Java中,使用字符串字面量创建字符串和使用 new 关键字创建字符串对象有一些重要的区别,主要涉及到字符串池(String Pool)和内存分配的方式:
1、字符串字面量
javaString str1 = "Hello, World!";
2、使用new关键字
javaString str2 = new String("Hello, World!");
2、内存分配
1、字符串字面量
字符串字面量常量池是位于方法区内部的一个内存区域,所以字面量会在编译时候就被确定,存储在常量池当中,而常量池是在类加载时被创建的,因此这种方式的字符串对象在程序运行时就已经确定,不会占用额外的堆内存。
2、new关键字
使用new关键字创建的字符串对象存储在堆内存当中,每一次使用new关键字都会创建一个新的对象来接受这个字符串,都会分配一个新的内存。
总结:
总的来说,使用字符串字面量的方式更为常见和高效,因为它允许字符串的重复使用,节省了内存,并且利用了字符串常量池的优势,而使用new关键字创建字符串对象通常需要特定的内存管理情境,但是一般而言推荐使用字符串字面量的方式。
三、常量池
下面我为大家详细地介绍一下什么时常量池,当我们谈到Java当中的常量池的时候通常是指存储在方法区内部的一块内存区域,常量池在Java当中扮演着非常重要的角色,它主要用于存储以下类型的变量。
1、类和接口的全限定名:
存储编译时生成的类和接口的全限定名,用于在运行时定位类和接口。
2、字段的名称和描述符:
包括类中声明的静态变量(static fields)。
3、方法的名称和描述符:
存储类中声明的方法信息,包括方法名和参数列表。
4、字符串字面量:
存储在程序中直接使用的字符串常量。
5、常量值引用:
存储常量值的引用,例如final修饰的常量字段。
常量池的一些关键特点包括:
- 不同类型的常量:
常量池中可以包含各种不同类型的常量,如整数、浮点数、字符串等。
- 不同的存储位置:
一部分常量是在编译时期确定的,另一部分是在运行时期动态生成的。
- 字符串常量池:
字符串常量池是常量池的一个子集,用于存储字符串字面量。当我们使用字符串字面量创建字符串对象时,Java会先检查字符串常量池,如果已经存在相同内容的字符串,则直接返回池中的引用,否则创建新的字符串对象并添加到池中。
- 常量池中的共享:
常量池中的常量是可以被多个对象共享的,这有助于提高内存利用率。
我们可以参考如下代码:
javaString str1 = "Hello"; String str2 = "Hello"; String str3 = new String("Hello");
在这个例子中,str1 和 str2 都指向字符串常量池中的同一个 "Hello" 字符串。而 str3 则是通过 new 关键字在堆内存中创建了一个新的字符串对象,与字符串常量池中的 "Hello" 不同。
所以当我们以后看到了这样的一段代码的时候应该就明白是为什么了:
javapublic class mystring { public static void main(String[] args) { String str1 = "Hello"; String str2 = "Hello"; String str3 = new String("Hello"); String str4 = new String("Hello"); System.out.println(str1 == str2); System.out.println(str3 == str4); } }
大家可以尝试着运行以下,看看结果会是什么样呢?这里面就运用到了常量池的概念。
上面的内容大家了解即可,如果有小伙伴想要详细了解关于方法区的知识的话可以去看我之前写过的一篇文章叫做《从JVM看Java》
6、常量池的生成方式
1、静态生成的常量
1、字符串字面量:
字符串字面量(String literal)是在编译时期就确定的,它们会被直接存储在字符串常量池中。例如:
javaString str = "Hello"; // "Hello"是字符串字面量,静态生成
2、final修饰的基本数据类型和字符串变量
当基本数据类型(如int、double等)或字符串变量被使用final关键字修饰时,它们在编译时期就被确定,并被视为常量。这样的常量也会在编译时被静态生成。
2、运行时生成的常量
1、通过运算生成的常量
javafinal int y = getX(); // getX()方法的返回值在运行时确定
2、new关键字创建的对象
javafinal MyClass obj = new MyClass(); // obj是通过new关键字创建的对象,在运行时动态生成
需要注意的是,尽管final修饰的变量通常被认为是常量,但在运行时动态生成的情况下,它们的值可能在对象初始化之后发生变化。因此,虽然引用不可变,但对象的状态仍然可以发生改变。
拓展:final的作用
1、对于变量:
- 不可变的常量:
用 final 修饰的变量表示常量,其值一旦被赋予就不能再次改变。这对于定义常量非常有用,提高代码的可读性和可维护性。
javafinal int MAX_VALUE = 100;
- 保护引用不可变性:
如果 final 修饰的变量是引用类型,意味着该变量引用的对象不能被重新分配。然而,对象内部的状态可能是可变的,因此 final 仅确保引用不会指向其他对象。
javafinal List<String> list = new ArrayList<>(); list.add("Item 1"); // 可以修改list对象的内部状态
2、对于方法:不可被重写:
用 final 修饰的方法不能被子类重写。这在设计中有时用于防止子类修改或覆盖特定的行为。
javapublic final void doSomething() { // 方法体 }
3、对于类:不可被继承:
用 final 修饰的类不能被其他类继承。这种做法常见于设计中的不可变类或工具类。
javapublic final class UtilityClass { // 类的定义 }
总体来说,final 的作用主要是为了实现不可变性、安全性、可靠性和代码设计的目的。使用 final 可以在一定程度上提高代码的稳定性,减少错误发生的可能性,并使得代码更容易理解和维护。
总结
关于String类的基础知识点就为大家讲解到这里,String类当中还有很多关于对字符串操作的方法,我认为这部分内容是整个String类当中最不重要的内容,如果我们将来需要用到某些方法的时候如果有遗忘到时候查阅文档就可以了,我们需要做的是掌握String类最核心的一部分知识,希望我的文章能够帮助到大家。