String 是 final 类不能被其他的类继承 , 并且String 类实现了 一些接口
String对象是不可变的长度字符串对象 , 不可变是说双引号里面的字符串对象一旦创建不可变 , 不是说指向字符串对象的变量不可变
//jdk9之前
private final char value[];
//jdk9之后
private final byte value[];
public class StringTest04 {
public static void main(String[] args) {
//"abc"放到了字符串常量池当中,"abc"不可变
String s = "abc";
// 字符串不可变说的是"abc"这个对象不可变,s变量是可变的可以指向其它对象的
// "xyz"放到了字符串常量池当中,"xyz"不可变
s = "xyz";
}
}
String表示字符串类型属于引用数据类型, 不属于基本数据类型
public class StringExercise10 {
public static void main(String[] args) {
Test1 ex = new Test1();
ex.change(ex.str, ex.ch);
System.out.print(ex.str + " and ");
//hspandhava
System.out.println(ex.ch);
}
}
class Test1 {
String str = new String("hsp");
final char[] ch = {'j', 'a', 'v', 'a'};
// 引用数据类型传参
public void change(String str, char ch[]) {
str = "java";
ch[0] = 'h';
}
}
在 java中 "== "双等号比较的是变量中保存的内存地址 , String 类重写的equals方法比较的是内容
public class StringTest {
public static void main(String[] args) {
// i变量中保存的是100这个值
int i = 100;
// s引用中保存的不是"abc",是"abc"字符串对象在“字符串常量池”当中的内存地址
String s = "abc";
// "hello"存储在方法区的字符串常量池当中 , 再次使用时这个"hello"不会新建(因为这个对象已经存在了)
String s1 = "hello";
String s2 = "hello";
// true
System.out.println(s1 == s2);
// 使用new的方式创建的字符串对象。new对象的时候一定会在堆内存当中开辟空间 , 但是"xyz"是同一个字符串对象
String x = new String("xyz");
String y = new String("xyz");
//false
System.out.println(x == y);
// String类已经重写了equals方法,以下的equals方法调用的是String重写之后的equals方法
// true
System.out.println(x.equals(y));
// 如何避免空指针异常
String k = new String("testString");
System.out.println("testString".equals(k));
// 存在空指针异常的风险。不建议这样写
System.out.println(k.equals("testString"));
}
}
String a = “hello” + "abc"指向常量池中字符串对象的内存地址
// 这两行代码表示底层创建了3个字符串对象,都在字符串常量池当中
String s1 = "abcdef";
String s2 = "abcdef" + "xy";
String c = a + b指向堆中String对象的内存地址
//StringBuilder的toString()方法源码
public String toString() {
return new String(value,0,count)
}
两个常量相加变量最终指向的是常量池中的地址 , 两个变量相加变量最终指向的是堆中的字符串对象的空间地址
public class StringExercise08 {
public static void main(String[] args) {
String a = "hello"; //创建 a对象
String b = "abc";//创建 b对象
//1. 先 创建一个 StringBuilder sb = StringBuilder()
//2. 执行 sb.append("hello")和sb.append("abc");
//3. String c= sb.toString() , 最后引用c指向堆中的String对象,value属性指向常量池中的 "helloabc"
String c = a + b;
String d = "helloabc";
System.out.println(c == d);// false
//e指向常量池中的 "helloabc"
String e = "hello" + "abc";
System.out.println(d == e);//true
}
}
String类已经重写了Object的toString()方法 , 输出字符串对象的话,输出的不是对象的内存地址,而是字符串本身
方法名 | 功能 |
---|---|
直接使用"" | 先看字符串常量池有没有 , 有则变量直接指向 , 没有则在常量池中创建后再指向 , 变量最终指向的是常量池的空间地址 |
String(“”) | 先在堆中创建一个String对象(含有value属性) , 然后看字符串常量池有没有 , 有则让 value 属性直接指向 , 没有则创建后再指向 ,字符串的value属性指向的常量池的空间地址 , 变量最终指向的是堆中的空间地址 |
String(byte数组) | 将byte数组的全部转换成字符串 |
String(byte数组,起始下标,长度) | 将byte数组中的一部分转换成字符串 |
String(char数组) | 将char数组全部转换成字符串 |
String(char数组,起始下标,长度) | 将char数组的一部分转换成字符串 |
String(StringBuffer sb) | 将StringBuffer对象转换成String对象 |
public class StringTest {
public static void main(String[] args) {
// 使用双引号创建字符串对象最常用
String s1 = "hello world!";
// hello world!
System.out.println(s1);
// 参数传一个字符串
String s2 = new String("helloworld!");
// helloworld!
System.out.println(s2);
// 将byte数组的全部转换成字符串
byte[] bytes = {97, 98, 99};
String s3 = new String(bytes);
// abc
System.out.println(s3);
// 将byte数组中的一部分转换成字符串
String s4 = new String(bytes, 1, 2);
// bc
System.out.println(s4);
// 将char数组全部转换成字符串
char[] chars = {'我','是','中','国','人'};
String s5 = new String(chars);
System.out.println(s5);
// 将char数组的一部分转换成字符串
String s6 = new String(chars, 2, 3);
System.out.println(s6);
}
}
返回堆中String对象中value属性指向的字符串在常量池的空间地址
方法名 | 功能 |
---|---|
String intern() | 返回和字符串内容相同的在常量池中的字符串的空间地址 , 如果没有相同的字符串常量会先在常量池中创建后返回 , 最终返回的是字符串在常量池的空间地址 |
public class StringExercise03 {
public static void main(String[] args) {
String a = "hsp"; //a 指向常量池的 “hsp”
String b =new String("hsp");//b 指向堆中的字符串对象
System.out.println(a.equals(b)); //T
System.out.println(a==b); //F
//b.intern()方法返回字符串在常量池的空间地址
System.out.println(a==b.intern()); //T
System.out.println(b==b.intern()); //F
}
}
方法名 | 功能 |
---|---|
byte[] getBytes() | 将字符串对象转换成字节数组 |
char[] toCharArray() | 将字符串转换成char数组 |
// byte[] getBytes(),将字符串对象转换成字节数组
byte[] bytes = "abcdef".getBytes();
for(int i = 0; i < bytes.length; i++){
System.out.println(bytes[i]);
}
// char[] toCharArray(),将字符串转换成char数组
char[] chars = "我是中国人".toCharArray();
for(int i = 0; i < chars.length; i++){
System.out.println(chars[i]);
}
String的父接口就是CharSequence , 可以接收一个字符串
方法名 | 功能 |
---|---|
String toLowerCase() | 将字符串全部转换为小写 |
String toUpperCase() | 将字符串转换为大写 |
String trim() | 去除字符串前后空白 , 中间空白不会去除 |
String replace(CharSequence target, CharSequence replacement) | 从头开始将字符串中的某个字符串全部替换为指定字符串 , 返回新的字符串 , 原先的字符串不变 |
String replaceAll(String regex, String replacement) | 从头开始将字符串中符合正则表达式的字符串全部替换为指定字符串, 返回新字符串 |
String[] split(String regex) | 按照字符串中的某个符号将字符串拆分成多个字符串 , 支持正则表达式 , 如果有特殊字符 , 需要加入转义字符 |
String substring(int beginIndex) | 从起始下标开始截取字符串 , 默认截取到字符串的末尾 |
String substring(int beginIndex, int endIndex) | 从起始下标开始截取字符串 , 截取到指定下标减1的位置 |
String concat(String other) | 在字符串的后面拼接指定的字符串 |
// String toLowerCase(),将字符串转换为小写
System.out.println("ABCDefKXyz".toLowerCase());
// String toUpperCase(),将字符串转换为大写
System.out.println("ABCDefKXyz".toUpperCase());
// String trim(),去除字符串前后空白,中间空白不会去除
System.out.println(" hello world ".trim());
// String replace(CharSequence target, CharSequence replacement),将字符串中的某个字符串全部替换为指定字符串,返回新的字符串
String newString = "name=zhangsan&password=123&age=20".replace("=", ":");
// 把字符串中的“=”替换成“:”后为name:zhangsan&password:123&age:20
System.out.println(newString);
//String[] split(String regex),按照字符串中的某个符号将字符串拆分成多个字符串
String param = "name=zhangsan&password=123&age=20";
String[] params = param.split("&");
for(int i = 0; i < params.length; i++){
System.out.println(params[i]);
// 可以继续向下拆分,如可以通过“=”拆分
}
// String substring(int beginIndex),从起始下标开始截取字符串,默认截取到字符串的末尾
System.out.println("http://www.baidu.com".substring(7)); // www.baidu.com
// String substring(int beginIndex, int endIndex),从起始下标开始截取字符串,截取到指定下标的位置但不包括
System.out.println("http://www.baidu.com".substring(7, 10)); //www
//String concat(String other),在字符串的后面拼接指定的字符串
String s1 = "宝玉";
s1 = s1.concat("林黛玉").concat("薛宝钗");
System.out.println(s1);// 宝玉林黛玉薛宝钗
方法名 | 功能 |
---|---|
boolean isEmpty() | 判断某个字符串是否为“空字符串” , 底层源代码调用的是字符串的length()方法 |
int length() | 判断字符串长度(判断数组长度是length属性) |
boolean startsWith(String prefix) | 判断某个字符串是否以某个子字符串开始 |
boolean contains(CharSequence s) | 判断前面的字符串中是否包含某个子字符串 |
boolean endsWith(String suffix) | 判断当前字符串是否以某个子字符串结尾 |
int indexOf(String str) | 判断某个子字符串在当前字符串中第一次出现处的索引(下标),索引从0开始,如果找不到返回-1 |
int lastIndexOf(String str) | 判断某个子字符串在当前字符串中最后一次出现的索引(下标) ,索引从0开始,如果找不到返回-1 |
matches(String regex) | 判断此字符串是否匹配给定的正则表达式 |
// boolean isEmpty(),判断某个字符串是否为“空字符串”
String s = "";
System.out.println(s.isEmpty());//true
// int length(),判断数组长度是length属性,判断字符串长度是length()方法
System.out.println("abc".length()); // 3
System.out.println("".length()); // 0
// boolean contains(CharSequence s),判断前面的字符串中是否包含某个子字符串
System.out.println("HelloWorld.java".contains(".java")); // true
System.out.println("http://www.baidu.com".contains("https://")); // false
// boolean startsWith(String prefix),判断某个字符串是否以某个子字符串开始
System.out.println("http://www.baidu.com".startsWith("http")); // true
System.out.println("http://www.baidu.com".startsWith("https")); // false
// boolean endsWith(String suffix),判断当前字符串是否以某个子字符串结尾
System.out.println("test.txt".endsWith(".java")); // false
System.out.println("test.txt".endsWith(".txt")); // true
System.out.println("fdsajklfhdkjlsahfjkdsahjklfdss".endsWith("ss")); // true
// int indexOf(String str),判断某个子字符串在当前字符串中第一次出现处的索引(下标)
System.out.println("oraclejavac++.netc#phppythonjavaoraclec++".indexOf("java")); // 6
// int lastIndexOf(String str),判断某个子字符串在当前字符串中最后一次出现的索引(下标)
System.out.println("oraclejavac++javac#phpjavapython".lastIndexOf("java")); //22
方法名 | 功能 |
---|---|
char charAt(int index) | 返回指定索引处的字符 , 索引从0开始 |
deleteCharAt(int index) | 删除指定索引处的字符 , 索引从0开始 , 比截取功能好使 |
// char charAt(int index),返回指定索引处的字符索引从0开始
char c = "中国人".charAt(1);
System.out.println(c); // 国
//删除指定索引处的字符,索引从0开始
c.deleteCharAt(0);
%s由字符串来替换, %c由char类型字符来替换, %d由整数来替换 , %f由小数来替换(%.2f表示只会保留小数点两位, 并且进行四舍五入的处理)
方法名 | 功能 |
---|---|
static String format(替换对应占位符的变量) | 静态方法利用占位符格式化字符串 |
//传统方法将所有的信息都拼接在一个字符串
String name = "john";
int age = 10;
double score = 56.857;
char gender = '男';
String info ="我的姓名是" + name + "年龄是" + age + ",成绩是" + score + "性别是" + gender + "。希望大家喜欢我!";
System.out.println(info);
//使用占位符拼接字符串
String formatStr = "我的姓名是%s 年龄是%d,成绩是%.2f 性别是%c.希望大家喜欢我!";
String info2 = String.format(formatStr, name, age, score, gender);
System.out.println("info2=" + info2);
equals方法只能看出字符串是否相等 , compareTo方法不仅可以看出是否相等,而且还可以看出具体谁大谁小
方法名 | 功能 |
---|---|
int compareTo(String anotherString) | 对两个字符串按照字典顺序进行大小的比较 , 拿着字符串第一个字母和后面字符串的第一个字母比较,能分胜负就不再比较。遇到不同的就拿前者字符的Ascll码减后者字符的AscII码, 长字符串和短字符串都相同就拿前者的长度减去后面的长度 ,等于0表示前后一致,小于0表示前小后大,大于0前大后小 |
boolean equals(Object anObject) | 判断两个字符串是否相等, JDK13中equals方法没有调用compareTo方法 |
boolean equalsIgnoreCase(String anotherString) | 判断两个字符串是否相等,并且同时忽略大小写 |
// int compareTo(String anotherString),对字符串按照字典顺序进行比较
String a = "jcck";
String b = "jack";
// 返回值是'c'-'a'=2的ASCII码的差值
System.out.println(a.compareTo(b));
String c = "abc";
String b = "abcd";
// 返回值是3-4=-1长度的差值
System.out.println(c.compareTo(d));
// boolean equals(Object anObject),判断两个字符串是否相等
System.out.println("abc".equals("abc")); // true
// boolean equalsIgnoreCase(String anotherString),判断两个字符串是否相等,并且同时忽略大小写
System.out.println("ABc".equalsIgnoreCase("abC"));
String类的compareTo源码
public int compareTo(String anotherString){
int len1 = value.length;
int len2 = anotherString.value.length;
//取出两个字符串中最小的长度
int lim = Math.min(len1,len2);
char v1[] = value;
char v2[] = anotherString.value;
int k= 0;
while(k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
K++;
}
//说明长字符串和短字符串前面部分都相同
return len1 - len2;
}
方法名 | 功能 |
---|---|
static String valueOf(Object object) | 将“非字符串”转换成“字符串” , 若参数是一个自定义对象字符串的内容由该对象的toString()方法的输出内容决定 |
String类的valueOf方法的源码底层调用了对象的toString()方法
public static String valueOf(Object obj){
return (obj == null) ? "null" : obj.toString;
}
// 将“非字符串”转换成“字符串”
String s1 = String.valueOf(true);
String s1 = String.valueOf(100);
String s1 = String.valueOf(3.14);
// 参数是一个对象的时候,会自动调用该对象的toString()方法
String s1 = String.valueOf(new Customer());
System.out.println(s1); // 我是一个VIP客户
class Customer {
// 重写toString()方法
@Override
public String toString() {
return "我是一个VIP客户";
}
}
System.out.println()源码: 先调用String的valueOf方法最后调用对象的toString()方法, 在输出任何数据的时候都是先转换成字符串再输出
// 其他基本数据类型构成方法重载
public void println(Object x){
//将“非字符串”转换成“字符串” , 如果参数是一个对象的时候,会自动调用该对象的toString()方法
String s = String.valueOf(x);
//.....
}
System.out.println(100);
System.out.println(3.14);
System.out.println(true);
Object obj = new Object();
System.out.println(obj);
//我是一个VIP客户
System.out.println(new Customer());
实际开发中会有大量的字符串的拼接,但java中的字符串是不可变的,每一次拼接都会产生新字符串。这样会占用大量的方法区内存,造成内存空间的浪费
JDK中自带的StringBuilder/StringBuffer是可变长度的字符串对象 , 可以对字符串的内容进行增删更适合于使用字符串的频繁拼接操作
StringBuffer/StringBuilde的直接父类是AbstractStringBuilder , 实现了Serializable接口 , 即可以串行化(对象可以在网络上传输 , 也可以保存到文件中)
StringBuilder有与 StringBuffer相同的方法 , 在单线程环境下建议使用StringBuilder , 因为在大多数实现中他比StringBuffer要快
//以下两行代码,就导致在方法区字符串常量池当中创建了3个字符串对象: "abc" , "hello" ,"abchello"
String s = "abc";
s += "hello";
方法 | 功能 |
---|---|
StringBuffer/StringBuilde() | 创建一个不带字符的字符缓冲区 , 初始化容量为16 |
StringBuffer/StringBuilde(int capacity) | 创建一个不带字符的字符缓冲区 , 具有指定的初始化容量 |
StringBuffer/StringBuilde(String str) | 创建一个带指定字符的字符缓冲区 , 初始化容量为16 + str的长度 , 将String 转换成StringBuffer/StringBuilde对象 , String 不能为 null (需要调用length方法) |
public class StringBuilderTest{
public static void main(String[] args) {
// 使用StringBuilder也是可以完成字符串的拼接
StringBuilder sb = new StringBuilder();
sb.append(100);
sb.append(true);
sb.append("hello");
sb.append("kitty");
System.out.println(sb);
}
}
优化性能: 在创建StringBuffer/StringBuilde时候尽可能给一个合适的初始化容量 , 底层数组的扩容次数越少程序的执行效率越高
public class StringBufferTest02 {
public static void main(String[] args) {
// 创建一个字符串缓冲区对象(初始化容量为16的 byte[] 数组)
StringBuffer stringBuffer = new StringBuffer();
// 指定初始化容量的StringBuffer对象(字符串缓冲区对象)
StringBuffer sb = new StringBuffer(100);
sb.append("hello");
sb.append("world");
sb.append("hello");
sb.append("kitty");
System.out.println(sb);
}
}
StringBuffer中的方法都有synchronized关键字修饰 , 表示StringBuffer在多线程环境下运行是安全的
方法名 | 功能 |
---|---|
String toString() | 将StringBuffer对象的中的字符串内容转换成一个新的String对象 , 返回一个字符串 |
StringBuffer append(任意类型) | append方法构成方法重载可以拼接字符串 , 追加的时候如果底层的 byte 数组满了会自动扩容, 可以将String 转换成StringBuffer对象 , 如果Stirng为null底层调用的是AbstractStringBuilder的appendNull将null 转成 字符串"null" |
StringBuffe delete(start , end) | 删除索引为>=start&& |
StringBuffe replace(start, end , String str) | 使用指定字符替换索引为>=start&& |
StringBuffe insert(int index , String str) | 在指定索引处插入某个字符串,原来索引处的内容自动后移 |
StringBuffe indexOf(String str) | 查找指定的子字符串在字符串第一次出现的索引,如果找不到返回-1 |
int length() | 获取字符串的长度 |
public class StringBufferMethod {
public static void main(String[] args) {
StringBuffer s = new StringBuffer("hello");
// append是追加的意思可以拼接字符串 , 追加的时候如果byte数组满了,会自动扩容
s.append(',');// "hello,"
s.append("张三丰");//"hello,张三丰"
s.append("赵敏").append(100).append(true).append(10.5);//"hello,张三丰赵敏100true10.5"
//输出一个引用的时候会自动调用对象的toString方法
System.out.println(s);//"hello,张三丰赵敏100true10.5"
//删除索引为>=start&&
s.delete(11, 14);
System.out.println(s);//"hello,张三丰赵敏true10.5"
//使用周芷若替换索引9-11的字符 [9,11)
s.replace(9, 11, "周芷若");
System.out.println(s);//"hello,张三丰周芷若true10.5"
//查找指定的子字符串在字符串第一次出现的索引,如果找不到返回-1
int indexOf = s.indexOf("张三丰");
System.out.println(indexOf);//6
//在索引为9的位置插入 "赵敏",原来索引为9的内容自动后移
s.insert(9, "赵敏");
System.out.println(s);//"hello,张三丰赵敏周芷若true10.5"
//获取字符串的长度
System.out.println(s.length());//22
System.out.println(s);
}
}
定义一个Scanner 对象,接收用户输入的价格(String类型) , 将价格的小数点前面每三位用逗号隔开后再输出
public class StringBufferExercise02 {
public static void main(String[] args) {
String price = "123564.59";
StringBuffer sb = new StringBuffer(price);
//找到小数点的索引,然后在该位置的前3位插入,根据条件循环判断
//先判断下标减3后能不能插入
for (int i = sb.lastIndexOf(".") - 3; i > 0; i -= 3) {
sb = sb.insert(i, ",");
}
//123,564.59
System.out.println(sb);
}
}
String转成StringBuffer/StringBuilde: 使用StringBuffer/StringBuilde的构造器或者使用它们的append方法将String对象作为方法的参数
StringBuffer转成String: 使用String的构造器来搞定或者使用StringBuffer/StringBuilde提供的toString方法将它们作为方法的参数
public class StringAndStringBuffer {
public static void main(String[] args) {
//String——>StringBuffer/StringBuilde
String str = "hello tom";
//使用StringBuffer的构造器,返回的才是StringBuffer对象,对str本身没有影响
StringBuffer stringBuffer = new StringBuffer(str);
//使用StringBuffer的append方法
StringBuffer stringBuffer1 = new StringBuffer();
stringBuffer1 = stringBuffer1.append(str);
//StringBuffer/StringBuilde-->String
StringBuffer stringBuffer3 = new StringBuffer("java");
//使用StringBuffer提供的toString方法
String s = stringBuffer3.toString();
//使用String的构造器来搞定
String s1 = new String(stringBuffer3);
}
}
调用StringBuffer/StringBuilde的append方法 , Stirng可以为null , 底层调用的是AbstractStringBuilder的appendNull将null 转成 “null”
使用StringBuffe/StringBuilder的构造器 , String不能为null , 底层会执行super(str.length() + 16), 若String为null会报空指针异常
public class StringBufferExercise01 {
public static void main(String[] args) {
String str = null;
StringBuffer sb = new StringBuffer();
//如果Stirng 为 null , 底层调用的是 AbstractStringBuilder 的 appendNull 将 null 转成 "null"
sb.append(str);
System.out.println(sb);//null
//抛出NullpointerException , 底层会执行 super(str.length() + 16)
StringBuffer sb1 = new StringBuffer(str);
System.out.println(sb1);
}
}
字符串存在大量的修改操作 , 在单线程环境下建议使用StringBuilder , 多线程环境下建议使用StringBuffer
字符串很少的修改 , 并且经常被多个对象引用使用String , 如获取属性文件中的配置信息
public class StringVsStringBufferVsStringBuilder {
public static void main(String[] args) {
//StringBuffer 拼接 20000次
long startTime = 0L;
long endTime = 0L;
StringBuffer buffer = new StringBuffer("");
startTime = System.currentTimeMillis();
for (int i = 0; i < 80000; i++) {
buffer.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuffer的执行时间:" + (endTime - startTime));
//StringBuilder 拼接 20000次
StringBuilder builder = new StringBuilder("");
startTime = System.currentTimeMillis();
for (int i = 0; i < 80000; i++) {
builder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder的执行时间:" + (endTime - startTime));
//String 拼接 20000
String text = "";
startTime = System.currentTimeMillis();
for (int i = 0; i < 80000; i++) {
text = text + i;
}
endTime = System.currentTimeMillis();
System.out.println("String的执行时间:" + (endTime - startTime));
}
}