Java常用类(二)String类详解
前言
在我们开发中经常会用到很多的常用的工具类,这里做一个总结。他们有很多的方法都是我们经常要用到的。所以我们一定要把它好好的掌握起来!
一、String简介
1.1、String(字符串常量)概述
在API中是这样描述:
String 类代表字符串。Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例实现。
字符串是常量;它们的值在创建之后不能更改。字符串缓冲区支持可变的字符串。因为 String 对象是不可变的,所以可以共享。
java.lang.String:
1.2、分析String源码
1)String的成员变量
/** String的属性值 */
private final char value[]; /** The offset is the first index of the storage that is used. */
/**数组被使用的开始位置**/
private final int offset; /** The count is the number of characters in the String. */
/**String中元素的个数**/
private final int count; /** Cache the hash code for the string */
/**String类型的hash值**/
private int hash; // Default to 0 /** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
/**
* Class String is special cased within the Serialization Stream Protocol.
*
* A String instance is written into an ObjectOutputStream according to
* <a href="{@docRoot}/../platform/serialization/spec/output.html">
* Object Serialization Specification, Section 6.2, "Stream Elements"</a>
*/ private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[];
String的成员变量
从源码看出String底层使用一个字符数组来维护的。
成员变量可以知道String类的值是final类型的,不能被改变的,所以只要一个值改变就会生成一个新的String类型对象,存储String数据也不一定从数组的第0个元素开始的,而是从offset所指的元素开始。
2)String的构造方法
String()
初始化一个新创建的 String 对象,使其表示一个空字符序列。
String(byte[] bytes)
通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。
String(byte[] bytes, Charset charset)
通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。
String(byte[] bytes, int offset, int length)
通过使用平台的默认字符集解码指定的 byte 子数组,构造一个新的 String。
String(byte[] bytes, int offset, int length, Charset charset)
通过使用指定的 charset 解码指定的 byte 子数组,构造一个新的 String。
String(byte[] bytes, int offset, int length, String charsetName)
通过使用指定的字符集解码指定的 byte 子数组,构造一个新的 String。
String(byte[] bytes, String charsetName)
通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。
String(char[] value)
分配一个新的 String,使其表示字符数组参数中当前包含的字符序列。
String(char[] value, int offset, int count)
分配一个新的 String,它包含取自字符数组参数一个子数组的字符。
String(int[] codePoints, int offset, int count)
分配一个新的 String,它包含 Unicode 代码点数组参数一个子数组的字符。
String(String original)
初始化一个新创建的 String 对象,使其表示一个与参数相同的字符序列;换句话说,新创建的字符串是该参数字符串的副本。
String(StringBuffer buffer)
分配一个新的字符串,它包含字符串缓冲区参数中当前包含的字符序列。
String(StringBuilder builder)
分配一个新的字符串,它包含字符串生成器参数中当前包含的字符序列。
二、创建字符串对象两种方式的区别
2.1、直接赋值方式创建对象
直接赋值方式创建对象是在方法区的常量池
String str="hello";//直接赋值的方式
2.2、通过构造方法创建字符串对象
通过构造方法创建字符串对象是在堆内存
String str=new String("hello");//实例化的方式
2.3、两种实例化方式的比较
1)编写代码比较
public class TestString {
public static void main(String[] args) {
String str1 = "Lance";
String str2 = new String("Lance");
String str3 = str2; //引用传递,str3直接指向st2的堆内存地址
String str4 = "Lance";
/**
* ==:
* 基本数据类型:比较的是基本数据类型的值是否相同
* 引用数据类型:比较的是引用数据类型的地址值是否相同
* 所以在这里的话:String类对象==比较,比较的是地址,而不是内容
*/
System.out.println(str1==str2);//false
System.out.println(str1==str3);//false
System.out.println(str3==str2);//true
System.out.println(str1==str4);//true
} }
2)内存图分析
可能这里还是不够明显,构造方法实例化方式的内存图:String str = new String("Hello");
首先:
当我们再一次的new一个String对象时:
3)字符串常量池
在字符串中,如果采用直接赋值的方式(String str="Lance")进行对象的实例化,则会将匿名对象“Lance”放入对象池,每当下一次对不同的对象进行直接赋值的时候会直接利用池中原有的匿名对象,
这样,所有直接赋值的String对象,如果利用相同的“Lance”,则String对象==返回true;
比如:对象手工入池
public class TestString {
public static void main(String args[]){
String str =new String("Lance").intern();//对匿名对象"hello"进行手工入池操作
String str1="Lance";
System.out.println(str==str1);//true
}
}
4)总结:两种实例化方式的区别
1)直接赋值(String str = "hello"):只开辟一块堆内存空间,并且会自动入池,不会产生垃圾。
2)构造方法(String str= new String("hello");):会开辟两块堆内存空间,其中一块堆内存会变成垃圾被系统回收,而且不能够自动入池,需要通过public String intern();方法进行手工入池。
在开发的过程中不会采用构造方法进行字符串的实例化。
5)避免空指向
首先了解: == 和public boolean equals()比较字符串的区别
==在对字符串比较的时候,对比的是内存地址,而equals比较的是字符串内容,在开发的过程中,equals()通过接受参数,可以避免空指向。
举例:
String str = null;
if(str.equals("hello")){//此时会出现空指向异常
...
}
if("hello".equals(str)){//此时equals会处理null值,可以避免空指向异常
...
}
6)String类对象一旦声明则不可以改变;而改变的只是地址,原来的字符串还是存在的,并且产生垃圾
三、String常用的方法
3.1、String的判断功能
1)常用方法
boolean equals(Object obj):比较字符串的内容是否相同
boolean equalsIgnoreCase(String str): 比较字符串的内容是否相同,忽略大小写
boolean startsWith(String str): 判断字符串对象是否以指定的str开头
boolean endsWith(String str): 判断字符串对象是否以指定的str结尾
2)代码测试
public class TestString {
public static void main(String[] args) {
// 创建字符串对象
String s1 = "hello";
String s2 = "hello";
String s3 = "Hello"; // boolean equals(Object obj):比较字符串的内容是否相同
System.out.println(s1.equals(s2));
System.out.println(s1.equals(s3));
System.out.println("-----------"); // boolean equalsIgnoreCase(String str):比较字符串的内容是否相同,忽略大小写
System.out.println(s1.equalsIgnoreCase(s2));
System.out.println(s1.equalsIgnoreCase(s3));
System.out.println("-----------"); // boolean startsWith(String str):判断字符串对象是否以指定的str开头
System.out.println(s1.startsWith("he"));
System.out.println(s1.startsWith("ll"));
} }
测试
结果:
3.2、String类的获取功能
1)常用方法
int length():获取字符串的长度,其实也就是字符个数
char charAt(int index):获取指定索引处的字符
int indexOf(String str):获取str在字符串对象中第一次出现的索引
String substring(int start):从start开始截取字符串
String substring(int start,int end):从start开始,到end结束截取字符串。包括start,不包括end
2)代码测试
public class TestString {
public static void main(String[] args) {
String str1 = "Lance";
String str2 = new String("Lance");
String str3 = "LANCE"; // boolean equals(Object obj):比较字符串的内容是否相同
System.out.println(str1.equals(str2));
System.out.println(str1.equals(str3));
System.out.println("-----------------------"); // boolean equalsIgnoreCase(String str):比较字符串的内容是否相同,忽略大小写
System.out.println(str1.equalsIgnoreCase(str3));
System.out.println("-----------------------"); // boolean startsWith(String str):判断字符串对象是否以指定的str开头
// boolean endsWith(String str): 判断字符串对象是否以指定的str结尾
System.out.println(str1.startsWith("La"));
System.out.println(str3.endsWith("CE")); } }
结果: 3.2、String类的获取功能 )常用方法 int length():获取字符串的长度,其实也就是字符个数
char charAt(int index):获取指定索引处的字符
int indexOf(String str):获取str在字符串对象中第一次出现的索引
String substring(int start):从start开始截取字符串
String substring(int start,int end):从start开始,到end结束截取字符串。包括start,不包括end
)测试代码 public class TestString {
public static void main(String[] args) {
// 创建字符串对象
String s = "helloworld"; // int length():获取字符串的长度,其实也就是字符个数
System.out.println(s.length());
System.out.println("--------"); // char charAt(int index):获取指定索引处的字符
System.out.println(s.charAt());
System.out.println(s.charAt());
System.out.println("--------"); // int indexOf(String str):获取str在字符串对象中第一次出现的索引
System.out.println(s.indexOf("l"));
System.out.println(s.indexOf("owo"));
System.out.println(s.indexOf("ak"));
System.out.println("--------"); // String substring(int start):从start开始截取字符串
System.out.println(s.substring());
System.out.println(s.substring());
System.out.println("--------"); // String substring(int start,int end):从start开始,到end结束截取字符串
System.out.println(s.substring(, s.length()));
System.out.println(s.substring(, )); } }
测试
结果:
3.3、String的转换功能
1)常用方法
char[] toCharArray():把字符串转换为字符数组
String toLowerCase():把字符串转换为小写字符串
String toUpperCase():把字符串转换为大写字符串
2)核心代码
public class TestString {
public static void main(String[] args) {
// 创建字符串对象
String s = "abcde"; // char[] toCharArray():把字符串转换为字符数组
char[] chs = s.toCharArray();
for (int x = ; x < chs.length; x++) {
System.out.println(chs[x]);
} System.out.println("-----------"); // String toLowerCase():把字符串转换为小写字符串
System.out.println("HelloWorld".toLowerCase());
// String toUpperCase():把字符串转换为大写字符串
System.out.println("HelloWorld".toUpperCase()); } }
测试
结果:
注意:
字符串的遍历有两种方式:一是ength()加上charAt()。二是把字符串转换为字符数组,然后遍历数组。
3.4、其他常用方法
1)常用方法
去除字符串两端空格:String trim()
按照指定符号分割字符串:String[] split(String str)
2)核心代码
public class TestString {
public static void main(String[] args) {
// 创建字符串对象
String s1 = "helloworld";
String s2 = " helloworld ";
String s3 = " hello world ";
System.out.println("---" + s1 + "---");
System.out.println("---" + s1.trim() + "---");
System.out.println("---" + s2 + "---");
System.out.println("---" + s2.trim() + "---");
System.out.println("---" + s3 + "---");
System.out.println("---" + s3.trim() + "---");
System.out.println("-------------------"); // String[] split(String str)
// 创建字符串对象
String s4 = "aa,bb,cc";
String[] strArray = s4.split(",");
for (int x = ; x < strArray.length; x++) {
System.out.println(strArray[x]);
}
} }
测试
结果:
四、String的不可变性
当我们去阅读源代码的时候,会发现有这样的一句话:
意思就是说:String是个常量,从一出生就注定不可变。
我想大家应该就知道为什么String不可变了,String类被final修饰,官方注释说明创建后不能被改变,但是为什么String要使用final修饰呢?
4.1、前言
了解一个经典的面试题:
public class Apple {
public static void main(String[] args) {
String a = "abc";
String b = "abc";
String c = new String("abc");
System.out.println(a==b); //true
System.out.println(a.equals(b)); //true
System.out.println(a==c); //false
System.out.println(a.equals(c)); //true
}
}
内存图:
4.2、分析
因为String太过常用,JAVA类库的设计者在实现时做了个小小的变化,即采用了享元模式,每当生成一个新内容的字符串时,他们都被添加到一个共享池中,当第二次再次生成同样内容的字符串实例时,
就共享此对象,而不是创建一个新对象,但是这样的做法仅仅适合于通过=符号进行的初始化。
需要说明一点的是,在object中,equals()是用来比较内存地址的,但是String重写了equals()方法,用来比较内容的,即使是不同地址,只要内容一致,也会返回true,这也就是为什么a.equals(c)返回true的原因了。
4.3、String不可变的好处
可以实现多个变量引用堆内存中的同一个字符串实例,避免创建的开销。
我们的程序中大量使用了String字符串,有可能是出于安全性考虑。
大家都知道HashMap中key为String类型,如果可变将变的多么可怕。
当我们在传参的时候,使用不可变类不需要去考虑谁可能会修改其内部的值,如果使用可变类的话,可能需要每次记得重新拷贝出里面的值,性能会有一定的损失。
五、字符串常量池
5.1、字符串常量池概述
1)常量池表(Constant_Pool table)
Class文件中存储所有常量(包括字符串)的table。
这是Class文件中的内容,还不是运行时的内容,不要理解它是个池子,其实就是Class文件中的字节码指令。
2)运行时常量池(Runtime Constant Pool)
JVM内存中方法区的一部分,这是运行时的内容
这部分内容(绝大部分)是随着JVM运行时候,从常量池转化而来,每个Class对应一个运行时常量池
上一句中说绝大部分是因为:除了 Class中常量池内容,还可能包括动态生成并加入这里的内容
3)字符串常量池(String Pool)
这部分也在方法区中,但与Runtime Constant Pool不是一个概念,String Pool是JVM实例全局共享的,全局只有一个
JVM规范要求进入这里的String实例叫“被驻留的interned string”,各个JVM可以有不同的实现,HotSpot是设置了一个哈希表StringTable来引用堆中的字符串实例,被引用就是被驻留。
5.2、亨元模式
其实字符串常量池这个问题涉及到一个设计模式,叫“享元模式”,顾名思义 - - - > 共享元素模式
也就是说:一个系统中如果有多处用到了相同的一个元素,那么我们应该只存储一份此元素,而让所有地方都引用这一个元素
Java中String部分就是根据享元模式设计的,而那个存储元素的地方就叫做“字符串常量池 - String Pool”
5.3、详细分析
举例:
int x = ;
String y = "hello";
1)首先,10
和"hello"
会在经过javac(或者其他编译器)编译过后变为Class文件中constant_pool table
的内容
2)当我们的程序运行时,也就是说JVM运行时,每个Classconstant_pool table
中的内容会被加载到JVM内存中的方法区中各自Class的Runtime Constant Pool。
3)一个没有被String Pool包含的Runtime Constant Pool中的字符串(这里是"hello")会被加入到String Pool中(HosSpot使用hashtable引用方式),步骤如下:
一是:在Java Heap中根据"hello"字面量create一个字符串对象
二是:将字面量"hello"与字符串对象的引用在hashtable中关联起来,键 - 值 形式是:"hello" = 对象的引用地址。
另外来说,当一个新的字符串出现在Runtime Constant Pool中时怎么判断需不需要在Java Heap中创建新对象呢?
策略是这样:会先去根据equals来比较Runtime Constant Pool中的这个字符串是否和String Pool中某一个是相等的(也就是找是否已经存在),如果有那么就不创建,直接使用其引用;反之,如上3
如此,就实现了享元模式,提高的内存利用效率。
举例:
使用String s = new String("hello");会创建几个对象
会创建2个对象
首先,出现了字面量"hello",那么去String Pool中查找是否有相同字符串存在,因为程序就这一行代码所以肯定没有,那么就在Java Heap中用字面量"hello"首先创建1个String对象。
接着,new String("hello"),关键字new又在Java Heap中创建了1个对象,然后调用接收String参数的构造器进行了初始化。最终s的引用是这个String对象.
喜欢就点个“推荐”!
Java常用类(二)String类详解的更多相关文章
- 【转】Java魔法堂:String.format详解
Java魔法堂:String.format详解 目录 一.前言 二.重载方法 三.占位符 四.对字符.字符串进行格式化 五.对整数进行格式化 六. ...
- Java网络编程二:Socket详解
Socket又称套接字,是连接运行在网络上两个程序间的双向通讯的端点. 一.使用Socket进行网络通信的过程 服务端:服务器程序将一个套接字绑定到一个特定的端口,并通过此套接字等待和监听客户端的连接 ...
- Java一维数组二维数组详解API
所谓数组,是有序的元素序列. 若将有限个类型相同的变量的集合命名,那么这个名称为数组名.组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量.用于区分数组的各个元素的数字编号称为下标 ...
- Java魔法堂:String.format详解
目录 一.前言 二.重载方法 三.占位符 四.对字符.字符串进行格式化 五.对整数进行格式化 六.对浮点数进行格式化 七.对日期时间进行格式化 ...
- Java基础-DButils工具类(QueryRunner)详解
Java基础-DButils工具类(QueryRunner)详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 如果只使用JDBC进行开发,我们会发现冗余代码过多,为了简化JDBC ...
- Java AtomicInteger类的使用方法详解_java - JAVA
文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 首先看两段代码,一段是Integer的,一段是AtomicInteger的,为以下: public class Samp ...
- Java的常用类——Object类、String类
Java的JDK中提供给我们很多封装好的类就是Java API,可以直接调用. Java 的API(API: Application(应用) Programming(程序) Interface(接口) ...
- Java Object类 和 String类 常见问答 6k字+总结
写在最前面 这个项目是从20年末就立好的 flag,经过几年的学习,回过头再去看很多知识点又有新的理解.所以趁着找实习的准备,结合以前的学习储备,创建一个主要针对应届生和初学者的 Java 开源知识项 ...
- Kotlin——中级篇(一):类(class)详解
在任何一门面向对象编程的语言里,类(class)是非常基础.但也是非常重要的一项组成,通俗的说就是万般皆对象,而所说的对象就是我们生成的类.Kotlin也是如此,下面详细为大家介绍Kotlin中的类的 ...
- 二叉搜索树详解(Java实现)
1.二叉搜索树定义 二叉搜索树,是指一棵空树或者具有下列性质的二叉树: 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值: 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根 ...
随机推荐
- Huawei AP3030DN固件升级
进入uboot: 上电,当出现Press f of F stop Auto-Boot in 3 seconds: 0 时按键盘上的F键 Password for uboot cmd line : 密码 ...
- zabbix_agentd在windows上安装
zabbix_agentd在Windows环境内客户端的安装与管理 1) 在目标机器上C:\windows目录下新建一个目录,如zabbix_agent: 2) 将zabbix_agent软件 ...
- Zabbix之Python脚本端口自动发现
Zabbix监控服务端口,可以手动添加配置,但是如果一个服务器上需要监控的端口数较多,那么利用Zabbix自动发现,比较高效,下面是自动发现port的Python脚本. #!/usr/bin/pyth ...
- STM32——项目需求之低功耗的停机模式
在说低功耗之前,先要明白一个东西,那就是stm32中的事件和中断. 事件是中断的触发源,开放了对应的中断屏蔽位,则事件可以触发相应的中断.在STM32中,中断与事件不是等价的,一个中断肯定对应一个事件 ...
- 针对后台TCP服务F5健康检查配置
1.TCP-HALF-OPEN方式 TCP-HALF-OPEN的探测方式,实际是F5每隔一个固定的时间,发送一个SYN包给资源池pool中的一个member,等待服务器返回SYN_ACK,在收到SYN ...
- Apache重写RewriteCond
RewriteCond就像我们程序中的if语句一样,表示如果符合某个或某几个条件则执行RewriteCond下面紧邻的RewriteRule语句,这就是RewriteCond最原始.基础的功能,为了方 ...
- PHP 图片处理类 错误处理方法:
call an undefined function exif_imagetype() 打开扩展php.ini 将 ; 去掉: extension=php_exif.dll 并将extension=p ...
- 基于jQuery和CSS3超酷Material Design风格滑动菜单导航特效
分享一款效果非常炫酷的谷歌 Material Design 风格jQuery和CSS3滑动选项卡特效.该选项卡特效集合了扁平风格设计和按钮点击波特效.是一款设计的非常不错的Material Desig ...
- 使用 WebSphere ILOG JRules 开发保险应用系统
使用 WebSphere ILOG JRules 开发保险应用系统 概述 保险行业在国内是一个充分竞争的行业,竞争的加剧导致保险公司的业务管理等各项费用在增长.而保险公司业务支撑系统的先进性与灵活支撑 ...
- APMServ5.2.6 升级php5.2 到 5.3版本及Memcache升级!
一.如何选择PHP5.3的VC9版本和VC6版本 VC6是什么?VC6就是legacy Visual Studio 6 compiler,就是使用这个编译器编译的.VC9是什么?VC9就是the V ...