文章目录
- 1.String类的基本用法(1)基本构造方法
- (2)字符串的常见方法
- 2.字符串常量池
- 3.字符串的不可变性(1)字符串为什么不可变
- (2)字符串设计成不可变的原因
- (3)用String拼接字符串的低效性
- 4.认识StringBuffer和StringBuilder(1)常用方法
- (2)StringBuilder和StringBuffer拼接字符串相比于String拼接
- (3)StringBuilder StringBuffer 和String的区别
- 5.String类型底层实现
一.String类的基本用法
1.字符串构造的基本方法
主要有三种方法
public static void main(String[] args) {
String a="hello1";//使用常量串构造
String b=new String("hello2");//直接newString对象
char[]arr={'h','e','l','l','o','3'};//使用字符数组进行构造
String c=new String(arr);
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
String 是引用类型,内部并不存储字符串本身
String s1=new String("hello")
String s2=new String("world")
2.字符串的常见方法:
(1)boolean equals();逐个字符进行比较
public static void main3(String[] args) {
String c="hello1";
String d="iello1";
System.out.println((c.equals(d)));
}
public static void main(String[] args) {
String c="hello1";
String d="hello1";
System.out.println((c.equals(d)));
}
(2)int compareTo(String s)
1.先按照次序进行比较,如果出现不相等的字符,返回这两个字符的大小差值
2.如果k个字符相等(k为两个字符长度的最小值),返回值为两个字符串长度的差值
public static void main(String[] args) {
String j="gho";
String k="ih";
System.out.println(j.compareTo(k));
}
按照次序依次进行比较,我们知道'j'比'i'小2,所以最终结果就为-2.
public static void main(String[] args) {
String j="ghoabc";
String k="gho";
System.out.println(j.compareTo(k));
}
字符串j和字符串k前三个字符都相等了,那么最终结果就是字符串长度的差值也就是3.
(3)char charAt(int i) 返回i位置上的字符
public static void main9(String[] args) {
String k="abcefgdfg";
//char b=k.charAt(0);
System.out.println(k.charAt(1));
System.out.println(k.charAt(3));
}
(4)int indexOf(int ch) 返回ch第一次出现的位置,没有返回-1.
public static void main10(String[] args) {
String k="abcefgdfg";
System.out.println(k.indexOf('e'));
System.out.println(k.indexOf('f'));
System.out.println(k.indexOf('z'));
}
(5)int indexOf(int ch,int from index) 从fromindex 位置上开始找字符ch,没有则返回-1;
public static void main(String[] args) {
String l="rghzbfgcv";
System.out.println(l.indexOf('c',6));
System.out.println(l.indexOf('g',2));
}
(6)int indexOf(String str) 返回str第一次出现的位置,没有则返回-1;
public static void main12(String[] args) {
String l="rghzbfgcv";
System.out.println(l.indexOf("ghz"));
System.out.println(l.indexOf("zbf"));
System.out.println(l.indexOf("gcf"));
}
(7)int indexOf(String str,int fromIndex) 从fromIndex位置上开始找字符串str第一次出现的位置,没有找到就返回-1;
public static void main13(String[] args) {
String l="rghzbfrghzcv";
System.out.println(l.indexOf("ghz",3));
System.out.println(l.indexOf("rgh",6));
}
(8) int lastindexOf(int ch)从后往前找,返回ch第一次出现的位置,没有则返回-1
int lastindexOf(int ch,int a)从a位置开始从后往前找,找到ch第一次出现的位置,没有则返回-1;
int lastindexOf(String str) 从后往前找str第一次出现的位置,没有则返回-1;
int lastindexOf(String str,int a)从a位置从后往前找str第一次出现的位置,没有则返回-1;
(9)数值和字符串的转化
//数字转字符串 String valueOf();
public static void main111(String[] args) {
String b=String.valueOf(1234);
String c=String.valueOf(12.38);
System.out.println(b);
System.out.println(c);
}
//字符串转数字
public static void main(String[] args) {
int a=Integer.parseInt("128");
double b=Double.parseDouble("15.67");
System.out.println(a);
System.out.println(b);
}
字符串大小写的相互转换
public static void main97(String[] args) {
String d="abcdefF1";
String f="vbHELLO4";
System.out.println(d.toUpperCase());
System.out.println(f.toLowerCase());
}
字符串和数组的相互转换
public static void main98(String[] args) {
String d="abcdefF1";
char[]f=d.toCharArray();
for (int i = 0; i < d.length(); i++) {
System.out.print(f[i]+" ");
}
System.out.println();
char[]arr={'a','b','c'};
String l=new String(arr);
System.out.println(l);
//System.out.println(Arrays.toString(d));
}
(10)字符串替换
String replaceAll(String source,String replace);全部替换
String replaceFirst(String source,String replace);部分替换
public static void main55(String[] args) {
String fh="hellollo";
System.out.println(fh.replaceAll("llo","www"));
}
public static void main(String[] args) {
String str="helloworld";
System.out.println(str.replaceAll("l","t"));
System.out.println(str.replaceFirst("l","w"));
}
(11)字符串拆分
String[]split(String regex)以regex为分割符全部拆分
String[]split(String regex,int b)以regex为分割符拆分成两b部分
public static void main58(String[] args) {
String a="hello good study";
String[]arr2=a.split(" ");
for (String x: arr2) {
System.out.println(x);
}
public static void main57(String[] args) {
String fg="hello&world&haha";
String[]arr=fg.split("&",2);
for (String x: arr) {
System.out.println(x);
}
如果分割符是”|","*","+"都得加上转义字符,前面加"\".
public static void main59(String[] args) {
String b="day.day.up";
String[]arr3=b.split("\\.");
for (String x: arr3) {
System.out.println(x);
}
如果分割符是"\\",那么就得写成"\\\\"
public static void main60(String[] args) {
String c="i\\love\\you";
String[]arr4=c.split("\\\\");
for (String x: arr4) {
System.out.println(x);
}
如果一个字符串中有多个分割符,可以用"1"作为连字符
public static void main62(String[] args) {
String f="good,food&day";
String[]arr6=f.split(",|&");
for (String x: arr6) {
System.out.println(x);
}
(12)字符串截取
String substring(int beginindex)从指定位置开始往后截取全部
String substring(int beginindex,int endindecx) 根据指定的前后位置截取一部分,注意是左闭右开,[bejinindex,endindex];
public static void main63(String[] args) {
String c="gouba";
System.out.println(c.substring(2));
}
public static void main(String[] args) {
String b="helloworld";
System.out.println(b.substring(2,5));
}
从下标2位置开始截取,截取到下标为5的位置,但下标为5的位置上的元素并不被截取
(13)String trim() 去掉字符串开头和结尾的空白字符串(空格、换行,制表符等)
public static void main(String[] args) {
String c=" hello1 ";
c=c.trim();
System.out.println(c);
}
二.字符串常量池
大家先来一起看一段代码
public static void main1(String[] args) {
String s1="hello";
String s2="hello";
String s3=new String("hello");
System.out.println(s1==s2);
System.out.println(s2==s3);
}
大家一起来看一下
当s1所指向的常量字符串在常量池当中没有时,会自动地把它保存在常量池当中去,当s2创建对象hello时会直接拿过来用,使s2指向s1创建的对象 ,s1和s2相等,而对于s3因为它是new了一个hello,所以不管常量池当中有没有hello,它都是唯一的,所以s2!=s3.
只要是new的对象,它就是唯一的。
2.intern()的使用 对于像String s1="hello"这样的常量字符串,当常量池当中没有hello,会自动的把它放到常量池当中去,而像String b=new String("hello")这种形式,当常量池当中没有时,并不会自动的添加到常量池当中去,需要我们用intern函数手动添加到常量池当中去。
public static void main(String[] args) {
String c=new String("hello");
// c=c.intern();
String d="hello";
if(c==d)
{
System.out.println("c=d");
}
else
{
System.out.println("c!=d");
}
当intern函数关闭时,常量池当中并没有c的hello,d也就没法直接用,只能自己再创建,所以c就不等d
当我们打开这个函数时 会手动地把c的hello放到常量池当中去,d去常量池当中看到有hello时就不需要自己再创建了,直接引用,也指向c的对象,所以c=d
public static void main(String[] args) {
String c=new String("hello");
c=c.intern();
String d="hello";
if(c==d)
{
System.out.println("c=d");
}
else
{
System.out.println("c!=d");
}
三.字符串的不可变性
String 是不可变对象,字符串中的内容不可以修改。
那么字符串为什么是不可变的呢?
final修饰引用类型表示不能再引用其他的对象,但引用的内容可以修改。
public static void main(String[] args) {
final int[]arr={1,2,3,4};
arr=null;//这行代码会报错,arr已经引用数组,不能再引用其他内容,
arr[0]=2;引用的内容可以变化
System.out.println(arr[0]);
}
字符串的修改
尽量 避免直接对String类进行修改,因为String类是不能修改的,所有的修改都会创建新对象,效率非常低下。
public static void main(String[] args) {
String str="";
int i=0;
for(i=0;i<100;i++)
{
str+=i;
}
System.out.println(str);
}
}
我们将汇编过程用代码展现一下:
public static void main2(String[] args) {
String str="";
int i=0;
for(i=0;i<100;i++)
{
StringBuilder sb=new StringBuilder();
sb.append(str);
sb.append(i);
sb.toString();
}
}
由汇编和代码我们可以看到每次修改字符串都会新建一个StringBuidler对象,而且每次都会产生新的String对象,效率非常低下
像这种字符串的修改,因为String类没法直接修改,每次修改会创建新对象,效率低下
String为什么被设计成不可变的?
(1)线程安全:可以避免线程安全问题
(2)安全性:在某种程度上可以防止意外修改,在一些情况下,可能会导致一些潜在的安全漏洞
(3)重用和缓存:可以将字符串放在常量池中,避免重复创建新的字符串,节省内存,提高效率。
(4)代码设计简化
四.StringBuilder/StringBuffer
常用方法:
像这种字符串的修改我们通常用StringBuilder 和StringBuffer
public static void main8(String[] args) {
StringBuilder a=new StringBuilder("edf");
for (int i = 0; i < 4; i++) {
a.append(i);//对字符串进行追加
}
//System.out.println(a);
String s=a.toString();
System.out.println(s);
}
public static void main(String[] args) {
String s="world";
StringBuffer c=new StringBuffer(s);
c.append(129);
System.out.println(c);
}
我们来看一下用StringBuilder或StringBuffer修改字符串和用String它们的效率之比:
public static void main1(String[] args) {
long start=System.currentTimeMillis();
String str="";
int i=0;
for(i=0;i<10000;i++)
{
str+=i;
}
// System.out.println(str);
long end=System.currentTimeMillis();
System.out.println(end-start);
}
public static void main3(String[] args) {
String str="";
long start=System.currentTimeMillis();
StringBuilder sb=new StringBuilder();
sb.append(str);
int i=0;
for(i=0;i<10000;i++)
{
sb.append(i);
}
long end=System.currentTimeMillis();
System.out.println(end-start);
}
String 和StringBuilder不能直接转换:
1.String变成StringBuilder ,用StingBuilder的构造方法或append()方法
2.StringBuilder变成String,用toString()方法
String和StringBuilder的区别
String中的内容不可以修改,而StringBuilder中的内容可以修改
String、StringBuilder、StringBuffer的区别
String的内容不可以修改,StringBuilder和StringBuffer可以修改
StringBuffer使用synchronized加锁属于线程安全操作,单线程环境下,性能相较于StringBuilder低一些,StringBuilder未加锁,属于线程不安全操作
五.String类的底层是基于数组(字符或字节数组)和字符串常量池,利用不可变性(final)修饰提供了安全、高效的字符串操作
JDK 9之前String类是使用char[](字符数组)存储的,而JDK9之后String使用byte[](字节数组)进行存储,一个char等于两个byte字节,使用byte[]数组可以更好节省内存空间。