文章目录
1 -基本概念
java.lang.String类由final关键字修饰,表示该类不能被继承。
该类用于描述字符串,使用该类创建的对象可以描述java中的所有字符串字面值;如:“abc” ,"123";
java中规定,双引号括起来的字符串,是不可变的,也就是说“abc”自出生到最终死亡,不可变(重点),不能变成“abcd”
注意 String s1 = null; 与 String s1 = ""; 有何区别?
解释
前面的形式表示没有字符串对象,后面的形式表示有字符串对象但是没有内容。
2 -常用的构造方法
方法名 | 说明 |
---|---|
String() | 使用无参的形式构造对象 |
String(byte[] bytes) | 使用参数指定字节数组来构造对象 |
String(byte[] bytes,int offset,int length) | 使用参数指定字节数组的一部分来构造对象 |
/**
* @author Mr.乐
* @data 2022/8/1 22:33
*/
public class Demo3 {
public static void main(String[] args) {
byte[] bytes = {97,98,99,100,101,102,103};//定义字节数组
String s = new String();//创建空字符串对象
String s1 = null;
System.out.println(s);//有对象,但是没有内容
System.out.println(s1);//没有对象 null
String s2 = new String(bytes);//将参数的byte数组转换成String类型的对象
System.out.println(s2);//abcdefg
String s3 = new String(bytes, 4, 3);
System.out.println(s3);//efg
}
}
3 -字符串常量池
Java为了避免大量字符串对象,设计了字符串常量池的概念,通过初始化方式创建的字符串都会存储在字符串常量池中,且字符串不能重复,以便共性使用,提高存储效率。
在JDK当中双引号括起来的字符串,例如:“abc”都是直接存储在方法区的字符串常量池当中
如图所示:
使用初始化方式创建字符串对象时
String str = "Hello";
JVM会先检查字符串常量池中是否存在相同的字符串,如果存在,则直接返回该字符串引用地址,如果不存在,则字符串存放到常量池中并返回字符串引用地址。图中s1与s2指向一个字符串常量池中的“hello”。例如 String s1="hello",String s2="hello",所以运算符s1==s2为ture
使用new关键字创建对象时,JVM同样会先到常量池中检查字符串是否存在,存在则在内存中创建一个字符串对象并返回对象引用地址,如果不存在,则在常量池和堆中依次创建字符串对象,返回该对象的引用地址。
图中x与y指向不同的堆中的对象,虽然堆中的对象指向字符串常量池中同一个对象,但是堆中两对象内存地址不同,如:String x=new String("xxx"),String y=new String("xxx")所以x==y为false
所以为了保险起见,我们对比字符串时,会用字符串的equals方法来比较(字符串含有重写的equlas方法,因为字符串属于引用类对象)。不能用Object的equlas方法,Object中的equlas没有重写。
/**
* @author Mr.乐
* @data 2022/8/1 22:46
*/
public class Demo04 {
public static void main(String[] args) {
//使用初始化的方式创建了两个相同的字符串。
String s1 = "张三";//第一次创建,字符串常量池中没有,则会创建字符串并返回地址
String s2 = "张三";//第二次创建,字符串常量池中有,则直接返回该对象的地址。
String s3 = new String("张三");//在堆中创建对象
//java把String类的equals方法重写了,可以直接用
System.out.println(s1.equals(s2));//地址相同,所以true
System.out.println(s1.equals(s3));//true
}
}
4 - StringBuffer、StringBuilder
String类型与StringBuffer类型的主要区别在于String类是不可改变的对象,因此,每次对String类型进行改变都想相当于创建一个新的对象,然后将原引用地址指向新的对象,这样不仅效率低,而且浪费大量的内存空间,特别是当内存中没有引用的对象多了以后,JVM中的GC会自动开始工作,程序的运行效率就会大大的降低。
和String类不同的是,StringBuffer和StringBuilder类的对象能够实现被多次修改,并且不产生新的未使用对象。
区别对比
类名 | 区别 |
---|---|
String | 值不可变,修改就创建新的对象,占用内存空间大 |
StringBuffer | 值可变,不会创建新的对象,占用内存空间小,线程安全,多线程 |
StringBuilder | 值可变,不会创建新的对象,占用内存空间小,线程不安全,单线程 |
/**
* @author Mr.乐
* @data 2022/8/2 16:59
*/
public class Demo05 {
public static void main(String[] args) {
String str = new String("Hello");//原来对象不可改变
String newStr = str + "Java";//创建新对象
//System.out.println(str == newStr);//false,说明连个引用指向的不是同一个对象
//创建StringBuilder对象
StringBuilder ss = new StringBuilder("Hello");
StringBuilder newSs = ss.append(" Java");//在原字符串对象中追加字符串
System.out.println(ss == newSs);//都是针对同一个对象进行操作 ture
System.out.println(ss);//打印字符串 Hello Java
ss.append("!~");//表示在原对象基础之上进行追加
System.out.println(ss);
ss.insert(10,"SE");//在指定位置插入字符串
System.out.println(ss);//Hello JavaSE!~
ss.replace(6,12,"MySql");//用来替换字符串
System.out.println(ss);
ss.delete(6,11);//通过指定参数的位置删除字符串
System.out.println(ss);
System.out.println(ss.length());//获取字符串长度,8个长度
}
}
在大量字符串重复拼接时,StringBuilder可以增加拼接效率。
如图为默认拼接100000字符串,耗时1227毫秒
/**
* @author Mr.乐
* @data 2022/8/2 17:23
*/
public class String01 {
public static void main(String[] args) {
String a="";
long start=System.currentTimeMillis();
for(int i=0;i<100000;i++){
a+="a";
}
long end=System.currentTimeMillis();
System.out.println(end-start);//1227
}
}
如图为StringBuilder拼接100000字符串,耗时7毫秒
/**
* @author Mr.乐
* @data 2022/8/2 17:23
*/
public class String01 {
public static void main(String[] args) {
StringBuilder a=new StringBuilder();
long start=System.currentTimeMillis();
for(int i=0;i<100000;i++){
a.append("a");
}
long end=System.currentTimeMillis();
System.out.println(end-start);//7
}
}
两者效率差别很明显。因为在使用普通字符串拼接时,jvm会在每次拼接的时候new一个StringBuilder对象,那么普通的拼接100000次,就会new100000个StringBuilder对象,相比于使用一个StringBuilder对象拼接,效率就会大大降低。所以在字符串拼接的时候,要使用StringBuilder。
5 -常用方法
方法名 | 说明 |
---|---|
char charAt(int index) | 用于根据参数指定的下标返回对应位置的字符 |
int length() | 用于返回字符串长度 |
int compareTo(String anotherString) | 用于按照字典顺序来比较两个字符串大小 |
int compareToIgnoreCase(String str) | 比较字符串的大小,不考虑大小写。 |
boolean equals(Object anObject) | 用于判断调用字符串和参数字符串是否相等 |
boolean equalsIgnoreCase(String anString) | 用于判断是否相等,忽略大小写 |
contains(CharSequence s) | 判断调用字符串中是否包含参数字符串 |
String concat(String str) | 返回参数对象与调用对象的拼接 |
boolean endsWith(String suffix) | 判断当前字符串是否以suffix为结尾 |
boolean startsWith(String prefix) | 判断当前字符串是否以profix为开头 |
String toLowerCase() | 用于将所有字符串转换成小写 |
String toUpperCase() | 用于将所有字符串转换为大写 |
byte[] getBytes() | 用于将字符串内容转换为byte数组并返回 |
int indexOf(String str) | 用于查找指定参数第一次出现的下标,不存在则返回-1 |
int lastIndexOf(String str) | 用于查找指定参数最后一次出现的下标 |
String substring(int beginIndex) | 用于获取从参数指定位置开始的字符串 |
String substring(int beginIndex,int endIndex) | 用于获取从beginIndex位置开始到endIndex位置之间的字符串 |
import java.util.Arrays;
/**
* @author Mr.乐
* @data 2022/8/2 12:42
*/
public class Demo01 {
public static void main(String[] args) {
//初始化方式定义字符串
String str01 = "Hello String";
String str02 = "hello String";
System.out.println(str01.charAt(4));//o,起始位置为0
System.out.println(str01.length());//返回字符串的长度12
//比较字符串的大小,调用对象与参数对象进行比较 compareTo()
System.out.println(str01.compareTo(str02));//按照字符编码进行比较
//-32 H的字符编码 - h的字符编码为32
System.out.println(str01.compareToIgnoreCase(str02));//忽略大小写 0
//equals 用于判断调用字符串和参数字符串是否相等 此处为String类的equals方法
System.out.println(str01.equals(str02));//false
System.out.println(str01.equalsIgnoreCase(str02));//true,忽略大小写比较字符串内容
//contains 判断调用字符串中是否包含参数字符串
System.out.println(str01.contains("String"));//字符串中有String字符串
System.out.println(str01.contains("string"));//false
//concat 返回参数对象与调用对象的拼接
System.out.println(str01.concat("!~")); //Hello String!~
//endsWith 是否以某字符串为结尾 startsWith 是否以某字符串为开头
System.out.println(str01.endsWith("ing"));//true
System.out.println(str01.startsWith("Hello"));//true
//toLowerCase toUpperCase
System.out.println(str02.toLowerCase());//将调用对象的字符串全部变成小写
System.out.println(str02.toUpperCase());//将调用对象的字符串全部变成大写
//getBytes
byte[] bytes = str02.getBytes();
System.out.println(Arrays.toString(bytes));//[104, 101, 108, 108, 111, 32, 83, 116, 114, 105, 110, 103]
//indexOf 查找指定参数第一次出现的下标,不存在则返回-1
//lastIndexOf 用于查找指定参数最后一次出现的下标
System.out.println(str02.indexOf("t"));//7
System.out.println(str02.lastIndexOf("l"));//3
//substring 用于获取从beginIndex位置开始到endIndex位置之间的字符串
System.out.println(str02.substring(6));//String 从下标为6开始至结束
System.out.println(str02.substring(6,10));//Stri 前闭后开
}
}
5 -总结
String类中最重要的是底层的结构,希望大家可以理解底层。方法忘了是可以查文档的,底层做你重要。