String类位于java.lang包中,具有丰富方法
计算字符串的长度、比较字符串、连接字符串、提取字符串
GBK:国标编码
乱码的产生:编解码方式不同,二进制的转换不同
unicode:字符对应唯一二进制的编码,把各国之间每个二进制只表示唯一的值
在java中保留了unicode中2个字节之前的六万多个字符,包含了大多数语言编码
python中第一行一般设置编码格式
# -*- coding: utf-8 -*-
UTF-8 编码:万国码,在unicode中加了一个算法,几位算一个字符,目前最常用的
package string;
/**
* String是不变对象
* 即:字符串对象创建后,表示字符内容不可变
* 若改变必定创建新对象
* @author Tian
*
*/
public class StringDemo {
public static void main(String[] args) {
//字符串是唯一一个不用new,和基本类型一样直接赋值的
String str = "hello world";
String str1 = new String("hello world");
//改变的是引用,这里会创建一个新对象,将引用修改为新对象的
str = "world hellow";
System.out.println(str);
//unicode
}
}
java对于字符串有一个优化
即:字符串常量池
这是在堆内存中开辟的一块空间,用于保存所有使用字面量形式创建的字符串对象,
当再次使用该自面量创建新的字符串时会直接重用而不会再创建新的,来节省内存开销
public class StringDemo1 {
public static void main(String[] args) {
String s1 = "123abc";
//s2会重用s1创建的字符串对象
String s2 = "123abc";
//两个字符串对象引用相同,指向同一个对象
System.out.println(s1 == s2); //true
//通过new创建的是创建了新对象,与普通对象相同
//字符串常量池用于保存所有使用字面量形式创建的字符串对象
String s3 = new String("123abc");
System.out.println(s2 == s3); //false
//对其中一个字符串改变,会重新创建一个对象,并修改该字符串的引用指向
s1 = s1+"!";
//修改后,s1与s2的引用指向了不同对象
System.out.println(s1 == s2); //false
}
}
String类提供了length()方法,确定字符串长度
基本数据类型之间的比较可以用==
但是引用类型之间的比较不能用
因为基本数据类型的变量名指向值,变量值是存在栈内存中
而引用数据类型的变量名为引用,引用存放在栈内存中的是该引用指向的地址,这个地址为堆内存中的相应对象的存储位置,真正的值存在对象里
==只能对引用类型的地址进行比较,而不能比较地址指向的值
String 是引用类型,其比较应该用equels()
equals方法用来比较两个字符串对象所表示的内容是否相同
代码:
//字符串常量池概念
char[] a = {'a','b','c','d'};
String s1 = new String(a);
String s2 = "abcd";
String s3 = "abcd";
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
System.out.println(s1 == s2);//比较地址
System.out.println(s2 == s3);//比较地址
System.out.println(s1.equals(s2));//比较值
//字符串不可改变
笔试题常考:
编译器在编译时,对于可以直接运算出来的值会在编译时就完成计算,并编译到class文件中
变量是不可以直接计算的,会在运行时才计算
/*
* 所以下面的s5在编译时就变成了"123abc",运行时就相当于赋值为字面量"abc123"
* 导致s5与s4指向对象相同
* s6是变量与字符串相加,变量的运算要在运行时才行,
* 那时候赋值是字符串的追加,会产生新的对象
*/
String s4 = "123abc";
String s5 = "123"+"abc";
String s = "123";
String s6 = s+"abc";
System.out.println(s4 == s5);
System.out.println(s4 == s6);
+: 可以将字符串和其他类型相连接,含隐式强转,
但是字符串是不可变类型,字符串+号连接会创建新的实例,如果多次连接效率低
concat():连接效率高,但是只能连接字符串,连接以字符串的返回值体现
代码:
//字符串是不可变类型,所以导致+号连接效率低
//字符串+号连接会创建新的实例,如果多次连接效率低
//字符串连接效率测试
String s0 = "abcdefghijklmnopqrstuvwxyz";
String s = "";
//获得系统当前时间点,毫秒值(1970-1-1 0点开始)
long time = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
s += s0;
}
time = System.currentTimeMillis()-time;
System.out.println(time);
//字符串更高效的连接:concat
//字符串1.concat(字符串2) 将字符串2连接到字符串1后面
String s1 = "abcdefghijklmnopqrstuvwxyz";
String s2 = "";
long time1 = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
s2.concat(s1);
}
time1 = System.currentTimeMillis()-time1;
System.out.println(time1);
indexOf() 搜索第一个出现的字符或字符串下标位置,返回int型,如果是搜索字符串则返回这个字符串的第一个字母出现的位置;若当前字符串不包含搜索的给定内容,则返回值为-1
indexOf(查找内容,开始查找的下标位置) 重载的方法,可以传两个参数
lastIndexOf() 搜索最后一个出现的字符或字符串下标位置,其他与indexOf()相同
public class IndexOfDemo {
public static void main(String[] args) {
String str = "thinking in java";
int index = str.indexOf("in");
System.out.println(index); //2
//indexOf重载的方法,可以传两个参数(查找内容,开始查找的下标位置)
index = str.indexOf("in",4);
System.out.println(index); //5
//查找最后一次出现指定字符串的位置
index = str.lastIndexOf("in");
System.out.println(index); //9
}
}
可以通过前查和后查,判断一个字符串有没有同样的字符
String substring(int start,int end)
截取当前字符串中指定范围的字符串,返回值为String,
传一个参数表示:从指定位置截取到字符串末尾
传两个参数表示:从参数1(下标)截取到参数2(下标)
注:Java API中,通常使用两个数字表示范围,都是含头不含尾
public class SubstringDemo {
public static void main(String[] args) {
String line = "www.yxt.com";
String sub = line.substring(4,7);
System.out.println(sub);
//从指定位置截取到字符串末尾
sub = line.substring(4);
System.out.println(sub);
}
}
练习:实现方法:完成截取指定位置的域名
public class Test1 {
public static void main(String[] args) {
String name1 = getHostName("www.yxt.com");
String name2 = getHostName("https://www.yxt.com");
String name3 = getHostName("doc.yxt.com.cn");
System.out.println(name1);
System.out.println(name2);
System.out.println(name3);
}
public static String getHostName(String line){
int index1 = line.indexOf(".")+1;
int index2 = line.indexOf(".",index1);
line = line.substring(index1,index2);
return line;
}
}
String trim() 返回值为string
去除当前字符串前后的空格,不能去除中间的空格
public class TrimDemo {
public static void main(String[] args) {
String str = " hellow world ";
String trim = str.trim();
System.out.println(str);
//注意:中间的空格不会被去除
System.out.println(trim);
}
}
char charAt(int index)
获取当前字符串中指定位置对应的字符
public class CharAtDemo {
public static void main(String[] args) {
String str = "thinking in java";
char c = str.charAt(3);
System.out.println(c);
//常用来遍历字符串中的每个字符
for(int i=0;i<str.length();i++){
char cstr = str.charAt(i);
System.out.println(cstr);
}
}
}
练习:判断回文(正读反读一样)
public class Test2 {
public static void main(String[] args) {
//方法1:
// String line = "上海自来水来自海上";
// for(int i=0;i
// char c1 = line.charAt(i);
// char c2 = line.charAt(line.length()-1-i);
// if(c1 != c2){
// System.out.println("不是回文");
// return; //return终止方法,运行到这里主函数不再运行
// }
// }
// System.out.println("是回文");
//方法2:(有点取巧)
// String line = "上海自来水来自海上";
// for(int i=0;i
// char c1 = line.charAt(i);
// char c2 = line.charAt(line.length()-1-i);
// if(c1 != c2){
// System.out.print("不");
// break;
// }
// }
// System.out.println("是回文");
//方法3(略繁琐)
// String line = "上海自来水来自海上";
// boolean b = true; //立旗帜
// for(int i=0;i
// char c1 = line.charAt(i);
// char c2 = line.charAt(line.length()-1-i);
// if(c1 != c2){
// b = false;
// break;
// }
// }
// if(b)
// System.out.println("是回文");
// else
// System.out.println("不是回文");
//自己想的方法
String line = "上海自来水来自海上";
String str = "";
for(int i=line.length()-1;i>=0;i--){
str += line.charAt(i);
}
System.out.println(str);
if(line.equals(str))
System.out.println("是回文");
else
System.out.println("不是回文");
}
}
boolean startsWith(String str)
boolean endsWith(String str)
判断字符串是否是以给定的字符串开始或结尾
public class StartsWithDemo {
public static void main(String[] args) {
String str = "thinking in java";
boolean starts = str.startsWith("th");
System.out.println(starts);
boolean ends = str.endsWith("java");
System.out.println(ends);
}
}
返回值类型为String
public class ToUpperCaseDemo {
public static void main(String[] args) {
String str = "我爱Java";
//转换为大写
String upper = str.toUpperCase();
System.out.println(upper);
//转换为小写
String lower = str.toLowerCase();
System.out.println(lower);
}
}
字符串提供了若干的重载valueOf方法,它们都是静态方法
static String valueOf(类型 xxx)
作用是将给定的内容转换为字符串
使用String类名来调用该静态方法
String.valueOf(转换的变量)
public class ValueOfDemo {
public static void main(String[] args) {
//int类型转为String
int a = 123;
String str1 = String.valueOf(a);
System.out.println(str1);
//double类型转为String
double b = 123.123;
String str2 = String.valueOf(b);
System.out.println(str2);
//也可以通过“+”号连接,强转为字符串类型
//效率低,不建议大范围使用
int i = 123456;
String str3 = i+"";
System.out.println(str3);
}
}
注意:利用字符串和其他类型相加会强转为字符串的原理,我们通过“+”号连接空字符串也可以转换
但是效率低,不建议大范围使用
int i = 123;
String str = i+"";
String 的优化措施仅照顾重用性,因此频繁修改字符串会带来内存开销大,运行效率差的结果
对此,java提供了一个专门用于修改字符串的API
java.lang.StringBuilder
其内部维护一个可变char数组,所有修改都是在这个数组中进行的
因此对内存的开销小,性能好
并且其提供了便于修改字符串的一系列方法,包括了:增删改插等基本操作
public class StringBuilderDemo {
public static void main(String[] args) {
//定义原字符串
String str = "好好学习Java";
System.out.println(str);
//1.把字符串传参给StringBuilder类型
StringBuilder builder = new StringBuilder(str);//可传参
//2.对字符串进行修改操作
//append() 追加字符串
builder.append(",为了美好未来!");
System.out.println(builder);
//replace(start,end,str); 改变给定范围内的字符串为str
builder.replace(8,26,",为了改变世界!");
System.out.println(builder);
//delete(start,end); 删除指定范围的字符串
builder.delete(0,9);
System.out.println(builder);
//insert(start,str) 插入字符串,从start位置开始插入str
builder.insert(0, "活着,");
System.out.println(builder);
//以上操作可以合并为
// builder.append(",为了美好未来!").replace(8,26,",为了改变世界!").delete(0,9).insert(0, "活着,");
// System.out.println(builder);
//3.修改完后,将修改好的字符串传回给字符串类型
str = builder.toString();
System.out.println(str);
}
}
StringBuilder 是可变类型,修改不会创建新对象,效率会更高
所以,以后如果以后有频繁修改字符串的操作,我们一般采用StringBuilder类型
StringBuffer是元老,和String一起出生
功能一样
区别:StringBuffer类相对于StringBuilder在多线程更安全,
但是字符串一般不用于多线程,若是需要在多线程中使用,还是用StringBuffer