String类的特点
- String 类代表字符串。Java 程序中的所有字符串字面值(如 “abc” )都作为此类的实例实现。解释:凡是在代码中出现"",不管""中有没有字符,它都是String的对象。
- 字符串是常量。字符串对象不可变。
- 因为字符串对象不可变。所以字符串对象可以共享。只要是字符串常量对象就可以共享。这些共享(就是指使用同一个对象)的字符串存储在字符串常量池中。
- 字符串常量池在哪呢?笼统的说在方法区
细说: JDK1.6以及之前:方法区 JDK1.7:挪到了堆中,即在堆中单开一块空间存储字符串常量,和其他的对象分开的
JDK1.8之后:元空间(类似于方法区的一个逻辑分区)
- java.lang.String类本身是final修饰,不能被继承。String类没有子类,父类是Object。
- String类的对象是如何设计为不可变的呢?
(1)首先,String中用一个private final char[] value,来存储字符串内容。
因为这里value数组使用了final,所以value数组不能扩容,即value不能指向新数组了。
因为这里value数组使用了private,所以外面不能直接操作String对象的value的
(2)其次,String类中提供了一些操作字符串对象的方法,但是只要涉及到修改字符串内容,都会返回新对象
- JDK1.9之前,String内部时char[],JDK1.9之后把char[]数组换成了byte[]数组。
为什么呢?
因为一个char字符,在JVM内存中使用Unicode编码值表示,需要栈2个字节。
无论什么范围的编码值,JVM都给你分配2个字节。
但是我们程序中大量使用ASCII表范围内的字符。这些字符的Unicode编码值是在[0,127]范围,按理说1个字节够了,所以会有点浪费。
JDK1.9就用byte[]数组,如果一个字节能搞定的,用1个字节,不能的,再用2个字节。
字符串举例
@Test
public void test(){
//凡是在代码中出现"",不管""中有没有字符,它都是String的对象。
String str1= "";//str1是一个空字符串
String str2= "hello";//str2是hello字符串
System.out.println("hello");//打印一个hello字符串
System.out.println("");//打印一个空字符串
}
@Test
public void test(){
//字符串对象不可变。
String s = "hello";
change(s);
System.out.println("s = " + s);//hello 但是s仍然执行hello字符串
}
public void change(String str){ //形参是String类型
str += "atguigu";
//对象形参做了修改,但是因为String类型的对象是不可变对象,只要修改,str就会指向新的新的字符串对象,就和原来实参对象无关了。
//此时str指向了 helloatguigu字符串,不再指向hello字符串了
}
@Test
public void test(){
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2);//地址相同,因为它们指向同一个字符串常量对象
String s3 = new String("hello");
String s4 = new String("hello");
System.out.println(s3 == s4);//false 内存地址不一样,因为s3是新new的
System.out.println(s3 == s1);//false
/*
虽然字符串常量对象是共享的,使用==比较也会返回true,
但是实际开发中,无论字符串是否是常量对象,只要涉及到比较字符串内容是否相同,都用equals方法,不要用==
*/
}
字符串不能修改
@Test
public void test(){
String str = "hello";
// str.value[0] = 'H';//无法操作,value是private的
System.out.println(str);
}
@Test
public void test(){
String str = "hello";
str.replace('h','H');//把h字符替换为H
System.out.println(str);//hello
//因为replace方法,如果修改了字符串的内容,就会返回一个新的字符串对象,不是在原来hello字符串基础上直接修改的
//如果没有接收新字符串对象,那么str是不变的
}
字符串修改后必须返回新对象
@Test
public void test6(){
String str = "hello";
str = str.replace('h','H');//把h字符替换为H
System.out.println(str);//Hello
//str变量重新指向新的字符串对象
}
创建字符串对象
在Java程序中,如何得到一个字符串对象?
1、直接""把字符串内容引起来,就是一个字符串对象
2、任意对象调用toString都可以得到一个字符串对象
3、任意类型的数据 和 字符串拼接结果都是字符串
4、String类有静态方法valueOf可以把多种数据类型转换为字符串
5、String类有很多构造器
@Test
public void test5(){
String s1 = new String();
String s2 = new String("hello");
char[] arr = {'a','b','c'};
String s3 = new String(arr);
byte[] bytes = {97,98,99,100};
String s4 = new String(bytes);
System.out.println("s1 = " + s1);
System.out.println("s2 = " + s2);
System.out.println("s3 = " + s3);
System.out.println("s4 = " + s4);
}
@Test
public void test4(){
String s1 = String.valueOf(12);
System.out.println("s1 = " + s1);
String s2 = String.valueOf(13);
System.out.println("s2 = " + s2);
System.out.println(s1 + s2);//1213
}
@Test
public void test3(){
System.out.println('a' + "," + 'b');//a,b
}
@Test
public void test2(){
Date d = new Date();
String s = d.toString();
System.out.println("s = " + s);
}
@Test
public void test1(){
String s = "hello";
System.out.println(s.length());//求s字符串对象的长度
System.out.println("Aa".length());//"Aa"一个字符串对象,length()求字符串对象的长度
System.out.println("Aa".equals(s));//表示判断"Aa"字符串和s字符串对象是否相等
System.out.println("".equals(s));//判断s字符串对象和空字符串对象是否相等
}
字符串对象的个数
@Test
public void test3(){
String s1 = "hello";
String s = new String("hello");
//上面的代码,也是2个字符对象
//其中"hello"是共享同一个
}
@Test
public void test2(){
String s1 = "hello";
String s2 = "hello";
//上面的代码,有几个字符串对象呢?1个
//这里虽然声明了2个字符串类型的变量
//但是字符串对象只有一个,它们共享同一个字符对象
}
@Test
public void test1(){
String s = new String("hello");
//上面这句代码有几个字符串对象?
//2个
//一个是字符串常量对象"hello",一个是新new的字符串对象
//两个字符串对象的关系是 s对象的value数组,和"hello"对象的value是共享。
}
字符串对象的内存分析
public static void main(String[] args) {
// String s; //在堆内存,或字符串常量值都没有对应的字符串对象,因为s未初始化,相当于只有一个局部变量s,它在栈中
// String s = null;//在堆内存,或字符串常量值也都没有对应的字符串对象,但是和上面的区别时已经初始化了
// String s = ""; //有一个字符串对象,只不过是空字符串对象,字符串对象没有内容,即内部的value数组的长度为0
// String s = new String();//有一个字符串对象,只不过是空字符串对象,字符串对象没有内容,即内部的value数组的长度为0
// String s = new String("");//有一个字符串对象,只不过是空字符串对象,字符串对象没有内容,即内部的value数组的长度为0
// String s = "abc";//直接指向常量池中字符串对象
// String s = new String("abc");//有2个对象,一个是堆中的实例,一个是常量池中的实例
char[] arr = {'a','b'};
String s = new String(arr); //思考:String类中的value数组和arr数组什么关系?
//String类中value数组是基于arr数组又复制了一个新数组。
//否则修改了arr,会导致s对象内容变了。
// char[] arr = {'a','b','c','d','e'};
// String s = new String(arr,0,3);
}
字符串拼接问题
1、字符串拼接有几种方式?
(1) +
只要+左右两边有String类型,那么就是字符串拼接,结果都是字符串。
(2)调用String类的concat方法
public String concat(String str):必须有字符串对象调用,传入的参数也必须是字符串对象,结果也是字符串对象。当+两边都是 字符串常量池中的字符串(或者说是字面量字符串,即直接""引起来的字符串),结果一定在字符串常量池, 而且编译器会优化处理,相当于直接是拼接后的结果字符串。 即 “hello” + “world” 等价于 "helloworld"但是concat不会优化。concat拼接完的结果,一定是在堆中的新字符串对象。
2、+操作 哪些字符串的拼接结果在字符串常量池呢? 当+两边都是
字符串常量池中的字符串(或者说是字面量字符串,即直接""引起来的字符串),结果一定在字符串常量池 当+两边都是
指向字符串常量池中的字符串的final声明的变量,结果一定在字符串常量池 当+两边都是
指向字符串常量池中的字符串的final声明的变量,或字符串常量池中的字符串 结果一定在字符串常量池
结果如果在字符串常量池的话,只要内容相同,就是同一个对象,因为字符串常量对象共享。当+两边有一边是 变量(而且这个变量不是指向字符串常量池中的字符串的final声明的变量),那么结果都是在堆, 当+两边有一边是 new
String对象,那么结果都是在堆, 结果如果在堆中的话,每一个结果都是独立的字符串对象。只要是xx.intern(),得到的字符串结果一定是在字符串常量池。
结论:
(1)"?" + “?” 结果一定在字符串常量池
(2)xx.intern() 结果一定在字符串常量池 xx是一个拼接操作
(3) final String s1 = “?”;
final String s2 = “?”;
s1 + s2 结果一定在字符串常量池
只有以上三种拼接结果才会在字符串常量池,在字符串常量池的话,只要内容相同,就是同一个对象,因为字符串常量对象共享,==比较返回true。其他的字符串拼接结果比较用==,都是false。
@Test
public void test6(){
String s1 = new String("hello");
String s2 = new String("world");
String s3 = "hello" + "world";
String s4 = "helloworld";
String s5 = (s1 + s2).intern();
String s6 = (s1 + "world").intern();
String s7 = (s1.concat(s2)).intern();
System.out.println(s3 == s4);//true
System.out.println(s4 == s5);//true
System.out.println(s4 == s6);//true
System.out.println(s4 == s7);//true
}
@Test
public void test5(){
final String s1 = new String("hello");
final String s2 = new String("world");
String s3 = "hello" + "world";
String s4 = "helloworld";
String s5 = s1 + s2;
String s6 = s1 + "world";
System.out.println(s3 == s4);//true
System.out.println(s4 == s5);//false
System.out.println(s4 == s6);//false
}
@Test
public void test4(){
String s1 = new String("hello");
String s2 = new String("world");
String s3 = "hello" + "world";
String s4 = "helloworld";
String s5 = s1 + s2;
String s6 = s1 + "world";
System.out.println(s3 == s4);//true
System.out.println(s4 == s5);//false
System.out.println(s4 == s6);//false
}
@Test
public void test3(){
final String s1 = "hello";
final String s2 = "world";
String s3 = "hello" + "world";
String s4 = "helloworld";
String s5 = s1 + s2;
String s6 = s1 + "world";
System.out.println(s3 == s4);//true
System.out.println(s4 == s5);//true
System.out.println(s4 == s6);//true
}
@Test
public void test2(){
String s1 = "hello";
String s2 = "world";
String s3 = "hello" + "world";
String s4 = "helloworld";
String s5 = s1 + s2;
String s6 = s1 + "world";
System.out.println(s3 == s4);//true
System.out.println(s4 == s5);//false
System.out.println(s4 == s6);//false
}
@Test
public void test(){
String s3 = "hello" + "world";
String s4 = "helloworld";
String s5 = "hello".concat("world");
System.out.println(s3 == s4);//比较地址 true
System.out.println(s4 == s5);//比较地址 false
}
字符串的比较操作
1、字符串的比较操作有哪几种?
(1)==:比较两个字符串的地址,只有两个字符串常量池中的字符串比较结果才会为true,否则都是false
(2)equals:比较两个字符串的内容,只要字符串内容相同,都是true,但是严格大小写
(3)compareTo:比较两个字符串的大小,默认按照字符串中每一个字符的Unicode编码值比较大小,大小写字母的编码值是不同的
(4)equalsIgnoreCase:比较两个字符串的内容,只要字符串内容相同,都是true,不区分大小写,认为’a’和’A’内容相同
(5)compareToIgnoreCase:比较两个字符串的大小,认为大写字母和小写字母一样大
(6) 字符串对象的比较器.compare(字符串1, 字符串2)
字符串对象的比较器必须是java.util.Comparator接口
(7)java.text.Collator:Collator 类执行区分语言环境的 String 比较。
调用它的静态的getInstance()方法的得到一个对象,然后调用compare方法比较两个字符串大小,
因为Collator实现了java.util.Comparator接口,重写了compare方法,重写时,是按照具体的语言环境的自然语言顺序排列的
思考:
比较用户名和密码输入是否正确? equals
比较验证码输入是否正确? equalsIgnoreCase
对用户信息按照姓名排序显示结果? 字符串对象的比较器.compare(字符串1, 字符串2)
@Test
public void test6(){
String[] arr = {"张三","李四","王五","柴林燕"};
Arrays.sort(arr, Collator.getInstance(Locale.CHINA));
System.out.println(Arrays.toString(arr));
/*
[柴林燕, 李四, 王五, 张三] 按照汉语的自然语言顺序,拼音的顺序比较大小
*/
}
@Test
public void test5(){
String[] arr = {"张三","李四","王五","柴林燕"};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
/*
[张三, 李四, 柴林燕, 王五] 按照每一个汉字的Unicode编码值比较大小
对于普通用户来说,不懂什么编码值
*/
}
@Test
public void test4(){
String[] arr = {"hello","java","world","chai","Bob","atguigu"};
//字符串排序
Arrays.sort(arr, new Comparator(){
@Override
public int compare(Object o1, Object o2) {
String s1 = (String) o1;
String s2 = (String) o2;
return s1.compareToIgnoreCase(s2);
}
});
System.out.println(Arrays.toString(arr));
//[atguigu, Bob, chai, hello, java, world]
/*
Arrays.sort(arr, Comparator接口的实现类对象)随着这里是匿名内部类的对象,也是实现了Comparator接口的。
指定了字符串元素比较时,忽略大小写。
*/
}
@Test
public void test3(){
String[] arr = {"hello","java","world","chai","Bob","atguigu"};
//字符串排序
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
//[Bob, atguigu, chai, hello, java, world]
/*
Arrays.sort方法,比较两个元素的大小时,是用元素对象的compareTo方法比较大小的,即要求元素实现java.lang.Comparable接口。
String类实现java.lang.Comparable接口,重写了compareTo方法,重写的是依据字符串的每一个字符的Unicode编码值比较大小的。
Bob的B 比 atguigu的a要小,排在前面
*/
}
@Test
public void test2(){
String s1 = "hello";
String s2 = "hello";
String s3 = "Hello";
String s4 = new String("hello");
/*
String类型实现了java.lang.Comparable接口,所以有compareTo方法
*/
System.out.println(s1.compareTo(s2));//0
System.out.println(s1.compareTo(s3));//32 正整数 表示s1 > s3 确实s1中h比s3中的H大
System.out.println(s1.compareTo(s4));//0
System.out.println(s1.compareToIgnoreCase(s2));//0
System.out.println(s1.compareToIgnoreCase(s3));//0
System.out.println(s1.compareToIgnoreCase(s4));//0
}
@Test
public void test01(){
String s1 = "hello";
String s2 = "hello";
String s3 = "Hello";
String s4 = new String("hello");
System.out.println(s1 == s2);//true
System.out.println(s1 == s3);//false 大小写不一样
System.out.println(s1 == s4);//false 一个在字符串常量池,一个在堆中
System.out.println(s1.equals(s2));//true
System.out.println(s1.equals(s3));//false 大小写不一样
System.out.println(s1.equals(s4));//true 大小写内容一样
System.out.println(s1.equalsIgnoreCase(s2));//true
System.out.println(s1.equalsIgnoreCase(s3));//true 不区分大小写
System.out.println(s1.equalsIgnoreCase(s4));//true 不区分大小写
}
空字符的比较
1、什么是空字符串?
语法层面的空字符串是指字符串对象的内容为空,字符串的长度为0。
2、如何判断某个字符串变量是否是空字符串?
当我们通过某个方法,或者计算得到一个字符串结果,如何判断它是否是空字符串
3、逻辑意义上的空字符串?
" "里面除了的空格等空白字符串(空格,\t等)没有其他实质性的文本,也可以归到空字符串
@Test
public void test4(){
String str = " ";
System.out.println("".equals(str));//false
System.out.println("".equals(str.trim()));//true
}
@Test
public void test3(){
String str = getString();
/*
在这里如何判断str是否是一个空字符串
*/
if(str != null && str.length()==0){
System.out.println("str是空字符串");
}else{
System.out.println("str是非空字符串");
}
if(str != null && str.isEmpty()){
System.out.println("str是空字符串");
}else{
System.out.println("str是非空字符串");
}
if(str != null && str.equals("")){
System.out.println("str是空字符串");
}else{
System.out.println("str是非空字符串");
}
if("".equals(str)){//开发中推荐这种写法
System.out.println("str是空字符串");
}else{
System.out.println("str是非空字符串");
}
}
public String getString(){
String str = null;
//...省略了计算
return str;
}
@Test
public void test2(){
String s1 = "";
String s2 = new String("");
String s3 = new String();
char[] arr = new char[0];
String s4 = new String(arr);
System.out.println(s1 != null && s1.length()==0);//true
System.out.println(s2 != null && s2.length()==0);//true
System.out.println(s3 != null && s3.length()==0);//true
System.out.println(s4 != null && s4.length()==0);//true
}
@Test
public void test1(){
//这些都不是空字符串
String s1 ; //未初始化 没有字符串对象
String s2 = null; //初始化为null 没有字符串对象
String s3 = " "; //有字符串对象,长度不是0,里面有空格
// System.out.println(s1 != null && s1.length()==0);//编译报错,s1没有初始化
// System.out.println(s2 != null & s2.length()==0);//编译不报错,但是运行报错NullPointerException &不发生短路,左边不成立,右边继续
System.out.println(s3 != null && s3.length()==0);//编译不报错,运行结果是false
}
字符串的常用方法
(1)boolean isEmpty():字符串是否为空
(2)int length():返回字符串的长度
(3)String concat(xx):拼接,等价于+
(4)boolean equals(Object obj):比较字符串是否相等,区分大小写
(5)boolean equalsIgnoreCase(Object obj):比较字符串是否相等,不区分大小写
(6)int compareTo(String other):比较字符串大小,区分大小写,按照Unicode编码值比较大小
(7)int compareToIgnoreCase(String other):比较字符串大小,不区分大小写
(8)String toLowerCase():将字符串中大写字母转为小写
(9)String toUpperCase():将字符串中小写字母转为大写
(10)String trim():去掉字符串前后空白符
(11)public String intern():结果在常量池中共享
常用方法
@Test
public void test2(){
String str = " hello world ";
System.out.println("[" + str + "]");//[ hello world ]
str = str.trim();
System.out.println("[" + str + "]");//[hello world]
}
@Test
public void test1(){
String str = "heLLOWorld";
System.out.println(str.toLowerCase());//helloworld 直接输出新字符串对象
str.toLowerCase();//没有接收变化后的新字符串对象
System.out.println(str);//heLLOWorld 打印的是原来的字符串对象
str = str.toLowerCase();
System.out.println(str);//helloworld str指向了新的字符串对象
}
查找
boolean contains(xx):是否包含xx
int indexOf(xx):从前往后找当前字符串中xx,即如果有返回第一次出现的下标,要是没有返回-1
int lastIndexOf(xx):从后往前找当前字符串中xx,即如果有返回最后一次出现的下标,要是没有返回-1
@Test
public void test(){
String str = "xxx是一家靠谱的培训机构,xxx可以说是IT培训的小清华,JavaEE是xxx的当家学科,xxx的大数据培训是行业独角兽。xxx的前端和运维专业一样独领风骚。";
//判断str中是否包含 尚硅谷
System.out.println(str.contains("xxx"));//true
//尚硅谷在str中的下标
System.out.println(str.indexOf("xxx"));//0
System.out.println(str.lastIndexOf("xxx"));//62
}
字符串截取
String substring(int beginIndex) :返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
String substring(int beginIndex, int endIndex):返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。
@Test
public void test2(){
String fileName = "atguigu/java/HelloWorld.java";
//截取出文件名
//先找到最后一个/的下标,以及最后一个.的位置
int diagonalIndex = fileName.lastIndexOf("/");
int dotIndex = fileName.lastIndexOf(".");
//从最后一个.截取到字符串最后
String name = fileName.substring(diagonalIndex+1, dotIndex);
System.out.println("name = " + name);
}
@Test
public void test(){
String fileName = "java/HelloWorld.java";
//截取出文件的后缀名
//先找到最后一个.的下标
int index = fileName.lastIndexOf(".");
//从最后一个.截取到字符串最后
String ext = fileName.substring(index);
System.out.println("ext = " + ext);
}
和字符相关
char charAt(index):返回[index]位置的字符
char[] toCharArray(): 将此字符串转换为一个新的字符数组返回
String(char[] value):返回指定数组中表示该字符序列的 String。
String(char[] value, int offset, int count):返回指定数组中表示该字符序列的 String。
static String copyValueOf(char[] data): 返回指定数组中表示该字符序列的 String
static String copyValueOf(char[] data, int offset, int count):返回指定数组中表示该字符序列的 String
static String valueOf(char[] data, int offset, int count) :返回指定数组中表示该字符序列的 String
static String valueOf(char[] data) :返回指定数组中表示该字符序列的 String
@Test
public void test3(){
char[] arr = {'a','b','c'};
String s1 = new String(arr);
String s2 = String.valueOf(arr);
System.out.println("s1 = " + s1);
System.out.println("s2 = " + s2);
}
@Test
public void test2(){
String str = "hello";
char[] chars = str.toCharArray();//把字符串转为字符数组
Arrays.sort(chars);
System.out.println(chars);//ehllo
}
@Test
public void test1(){
String name = "张三";
//得到第一个字符
char first = name.charAt(0);
System.out.println("first = " + first);
char second = name.charAt(1);
System.out.println("second = " + second);
}
@Test
public void test(){
Scanner input = new Scanner(System.in);
System.out.print("请输入性别:");
char gender = input.next().charAt(0);
/*
先调用input对象的next()得到一个字符串对象,
然后调用字符串对象的charAt()方法,返回第1个字符
*/
System.out.println("gender = " + gender);
input.close();
}
编码与解码
byte[] getBytes():编码,把字符串变为字节数组,按照平台默认的字符编码方式进行编码
----------------------------------- byte[] getBytes(字符编码方式):按照指定的编码方式进行编码
new String(byte[] ) 或 new String(byte[], int, int):解码,按照平台默认的字符编码进行解码
new String(byte[],字符编码方式 ) 或 new String(byte[], int, int,字符编码方式):解码,按照指定的编码方式进行解码
编码:把字符串转为字节数组 -->很多的IO操作都是基于字节
解码:把字节数组转为字符串 -->使用字节IO流读取的数据,怎么用字符串显示出来
char:占2个字节 ==>JVM内存中,Unicode编码值占2个字节
char->byte ==>用了环境编码 UTF-8,GBK等
不同的编码对char表示方式不同。
GBK->1个汉字2个字节
UTF8->1个汉字3个字节
ISO8859-1 ->所有字符都是1个字节
....
如果编码方式和解码方式使用不同的字符集,会乱码。
//IO流操作FileInputStream和FileOutputStream后面IO流章节学习,大家先不着急
@Test
public void test5()throws IOException{
FileInputStream fis = new FileInputStream("d:\\1.txt");
byte[] data = new byte[1024];
int len;
while((len = fis.read(data)) != -1){
System.out.println(new String(data, 0, len,"GBK"));//以GBK解码文件内容
}
fis.close();
}
@Test
public void test4() throws IOException {
String str = "尚硅谷";
FileOutputStream fos = new FileOutputStream("d:\\1.txt");
fos.write(str.getBytes());//以字节的方式输出字符串
fos.close();
}
@Test
public void test3() throws UnsupportedEncodingException {
byte[] data = {-55, -48, -71, -24, -71, -56};
String str1 = new String(data);//按照平台默认的编码进行解码,这里按照UTF8进行解码
System.out.println(str1);
String str2 = new String(data, "GBK");
System.out.println(str2);//尚硅谷
}
@Test
public void test2() throws UnsupportedEncodingException {
String s1 = "尚硅谷";
byte[] bytes = s1.getBytes("GBK");
System.out.println(Arrays.toString(bytes));
//[-55, -48, -71, -24, -71, -56]
/*
GBK中1个汉字2个字节
*/
}
@Test
public void test1(){
String s1 = "尚硅谷";
byte[] bytes = s1.getBytes();
/*
getBytes()表示按照平台默认的编码处理字符串。
平台默认的编码,当前IDEA设置为UTF-8。
UTF-8会汉字处理,一般的汉字都是1个汉字3个字节
*/
System.out.println(Arrays.toString(bytes));
/*
[-27, -80, -102, -25, -95, -123, -24, -80, -73]
*/
}
开头与结尾
boolean startsWith(xx):是否以xx开头
boolean endsWith(xx):是否以xx结尾
@Test
public void test2(){
/*
需求:判断某个用户是否是姓张的
*/
String name = "章三丰";
System.out.println(name.startsWith("张"));
}
@Test
public void test1(){
/*
需求:判断某个文件是否是Java的源文件。
Java源文件是以“.java”结尾的文件
*/
String filename = "HelloWorld.class";
System.out.println(filename.endsWith(".java"));//true
}
正则表达式
boolean matches(正则表达式):判断当前字符串是否匹配某个正则表达式
正则表达式,又称规则表达式(英语:Regular Expression,在代码中常简写为regex、regexp或RE)。
正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,
这个“规则字符串”用来表达对字符串的一种过滤逻辑。通常被用来检索、替换那些符合某个模式(规则)的文本。
@Test
public void test3(){
//验证用户名和密码,要求第一个字必须为字母,一共6~16位字母数字下划线组成
String str = "8chai123456";
System.out.println(str.matches("^[a-zA-Z]\\w{5,15}$"));
// ^:字符串的开头
//$ :字符串的结尾
//[a-zA-Z]:第一个字符是字母,大小或小写
//\w:单词字符,[a-zA-Z_0-9] 字母、数字、下划线
//{5,15}:至少5位,最多15位,因为第一个字符单独判断了,后面再加5~15位
}
@Test
public void test2(){
Scanner input = new Scanner(System.in);
System.out.print("请输入一个字符串:");
String words = input.next();
/*
判断用户输入的字符串是否是一个6位数的纯数字组成的字符串,例如:123456 ,例如银行密码啥的必须是纯数字组成。
*/
//如果不用正则,就麻烦点
//如果使用正则,就比较简单
//字符串是1个或多个数字组成
//正则:\d{6} \在正则有特殊意义,在Java中也是转义
System.out.println(words.matches("\\d{6}")); // \\表示一个\
input.close();
}
@Test
public void test(){
Scanner input = new Scanner(System.in);
System.out.print("请输入一个字符串:");
String words = input.next();
/*
判断用户输入的字符串是否是一个纯数字组成的字符串,例如:123456 ,例如银行密码啥的必须是纯数字组成。
*/
//如果不用正则,就麻烦点
//如果使用正则,就比较简单
//字符串是1个或多个数字组成
//正则:\d+ \在正则有特殊意义,在Java中也是转义
System.out.println(words.matches("\\d+")); // \\表示一个\
input.close();
}
替换
String replace(xx,xx):不支持正则
String replaceFirst(正则,value):替换第一个匹配部分
String replaceAll(正则, value):替换所有匹配部分
@Test
public void test2(){
String str = "熊大最棒";
str = str.replace("熊大","xx");
System.out.println("str = " + str);
}
@Test
public void test1(){
String str = "hello562wor8ld8901java";
//去掉里面的数字,保留所有的字母
str = str.replaceAll("\\d","");//把所有数字替换为空字符串,相当于去掉数字
System.out.println("str = " + str);
}
@Test
public void test(){
String str = "hello562wor8ld8901java";
//去掉里面的数字,保留所有的字母
str = str.replaceFirst("\\d","");//把第一个数字替换为空字符串,相当于去掉数字
System.out.println("str = " + str);
}
拆分
String[] split(正则):按照某种规则进行拆分
@Test
public void test3(){
String str = "张三.23|李四.24|王五.25";
//把上面的数据拆处理,放到学生对象中
/*
先按照|拆分为多个学生信息
*/
String[] strings = str.split("\\|");
for (int i = 0; i < strings.length; i++) {
System.out.println(strings[i]);
}
//创建一个学生的对象数组
Student[] students =new Student[strings.length];
/*
对每一个学生,按照.拆分出姓名和成绩
张三.23
李四.24
王五.25
张三 23 分别放到nameAndScore数组的[0]和[1]元素中
*/
for (int i = 0; i < strings.length; i++) {
String[] nameAndScore = strings[i].split("\\.");
students[i] = new Student(nameAndScore[0],Integer.parseInt(nameAndScore[1]));
}
for (int i = 0; i < students.length; i++) {
System.out.println(students[i]);
}
}
@Test
public void test2(){
String str = "1Hello2World3java4atguigu";
str = str.replaceFirst("^\\d","");
System.out.println("str = " + str);
String[] strings = str.split("\\d");
System.out.println("单词个数:" + strings.length);
for (int i = 0; i < strings.length; i++) {
System.out.println(strings[i]);
}
}
@Test
public void test(){
String str = "Hello,World,java,atguigu";
//想要把这些单词一个一个拆分出来,存到一个字符串数组中
//它们都是以,分割的
String[] strings = str.split(",");
for (int i = 0; i < strings.length; i++) {
System.out.println(strings[i]);
}
}
Student类
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
StringBuilder-StringBuffer-普通字符
@Test
public void testString(){
long start = System.currentTimeMillis();//获取当前系统时间
//接下来拼接了10000次的字符串,从0拼接到10000
String s = new String("0");
for(int i=1; i<=10000; i++){
s += i;//字符串拼接
}
long end = System.currentTimeMillis();//又获取一次当前系统时间
System.out.println("String拼接+用时:"+(end-start));//前后两次时间的差值,就相当于上面循环用的总时间 ,例如336毫秒
/*
Runtime.getRuntime():获取JVM运行环境
totalMemory():总内存
freeMemory():空闲内存
totalMemory() - freeMemory():已使用内存
*/
long memory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
System.out.println("String拼接+memory占用内存: " + memory);//例如:472427216字节
}
@Test
public void testStringBuilder(){
long start = System.currentTimeMillis();
StringBuilder s = new StringBuilder("0");//使用StringBuilder类代替String类
for(int i=1; i<=10000; i++){
s.append(i);//字符串拼接,用的是StringBuilder的append方法
}
long end = System.currentTimeMillis();
System.out.println("StringBuilder拼接+用时:"+(end-start));//4毫秒
long memory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
System.out.println("StringBuilder拼接+memory占用内存: " + memory);//13421960字节
}
@Test
public void testStringBuffer(){
long start = System.currentTimeMillis();
StringBuffer s = new StringBuffer("0");//这里StringBuffer和StringBuilder是亲兄弟,和String是堂兄弟
for(int i=1;i<=10000;i++){
s.append(i);
}
long end = System.currentTimeMillis();
System.out.println("StringBuffer拼接+用时:"+(end-start));//3
long memory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
System.out.println("StringBuffer拼接+memory占用内存: " + memory);//13422632
}
StringBuilder-StringBuffer区别
因为String类的对象是不可变的,只要修改就会产生新对象。
那么如果程序中,涉及到对字符串的频繁的处理,有时候需要借助StringBuffer和StringBuilder来提高处理效率。
StringBuilder和StringBuffer是API方法完全一致的,差别就在于。
StringBuilder是线程不安全的,方法没有加synchronized修饰。
StringBuffer是线程安全的,方法前面加synchronized修饰。
因为大多数的字符串内容,不会被多个线程同时访问,是单个线程使用的,所以没必要加锁。加锁反而麻烦了。
StringBuilder类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。
如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。
StringBuffer和StringBuilder是可变字符串,称为字符串缓冲区。
要修改字符串内容,是在StringBuffer和StringBuilder原对象基础上直接修改的,不会返回新对象。
它们都支持:append(追加)、delete(删除)、insert(插入)、reverse(反转)等等操作。
为什么说它们是可变字符串缓冲区?
因为它们内部使用了一个非final修饰的char[]来存储字符串内容。
相当于StringBuffer和StringBuilder是一个动态数组,只不过里面的数组是char[],不是Object[].
@Test
public void test4(){
StringBuilder s = new StringBuilder();
s.append("hello")
.append("world")
.append("chailinyan");
}
@Test
public void test3(){
StringBuilder s = new StringBuilder("hello");
s.reverse();//String类没有这个方法
System.out.println(s);//olleh
}
@Test
public void test2(){
StringBuilder s = new StringBuilder();
s.append("hello").append("world").append(1).append(true);
System.out.println(s);//helloworld1true
}
@Test
public void test1(){
StringBuilder s1 = new StringBuilder();
s1.append("hello");
StringBuffer s2 = new StringBuffer();
s2.append("hello");
}
正则表达式构造摘要
(1)字符类
[abc]
:a
、b
或 c
(简单类)
[^abc]
:任何字符,除了 a
、b
或 c
(否定)
[a-zA-Z]
:a
到 z
或 A
到 Z
,两头的字母包括在内(范围)
(2)预定义字符类
.
:任何字符(与行结束符可能匹配也可能不匹配)
\d
:数字:[0-9]
\D
:非数字: [^0-9]
\s
:空白字符:[ \t\n\x0B\f\r]
\S
:非空白字符:[^\s]
\w
:单词字符:[a-zA-Z_0-9]
\W
:非单词字符:[^\w]
(3)POSIX 字符类(仅 US-ASCII)
\p{Lower}
小写字母字符:[a-z]
\p{Upper}
大写字母字符:[A-Z]
\p{ASCII}
所有 ASCII:[\x00-\x7F]
\p{Alpha}
字母字符:[\p{Lower}\p{Upper}]
\p{Digit}
十进制数字:[0-9]
\p{Alnum}
字母数字字符:[\p{Alpha}\p{Digit}]
\p{Punct}
标点符号:!"#$%&’()*+,-./:;<=>?@[]^_`{|}~
\p{Blank}
空格或制表符:[ \t]
(4)边界匹配器
^
:行的开头
$
:行的结尾
(5)Greedy 数量词
X?
:X,一次或一次也没有
X*
:X,零次或多次
X+
:X,一次或多次
X{
n}
:X,恰好 n 次
X{
n,}
:X,至少 n 次
X{
n,
m}
:X,至少 n 次,但是不超过 m 次
(6)Logical 运算符
XY:X 后跟 Y
X|
Y:X 或 Y
(
X)
:X,作为捕获组
(7)特殊构造(非捕获)
(?:X) :X,作为非捕获组
(?>X): X,作为独立的非捕获组
(?=X) :X,通过零宽度的正 lookahead
(?<=X) :X,通过零宽度的正 lookbehind
(?!X) :X,通过零宽度的负 lookahead
(?<!X) :X,通过零宽度的负 lookbehind
2、常见的正则表达式示例
- 验证用户名和密码,要求第一个字必须为字母,一共6~16位字母数字下划线组成:(1\w{5,15}$)
- 验证电话号码:xxx/xxxx-xxxxxxx/xxxxxxxx:(^(\d{3,4}-)\d{7,8}$)
- 验证手机号码:( ^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$ )
- 验证身份证号: (\d{15}$)|(^\d{18}$)|(\d{17}(\d|X|x)$)
- 验证Email地址:(^\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)*$)
- 只能输入由数字和26个英文字母组成的字符串:(2+$)
- 整数或者小数:(3+(.[0-9]+){0,1}$)
- 中文字符的正则表达式:([\u4e00-\u9fa5])
- 金额校验(非零开头的最多带两位小数的数字):(^([1-9][0-9]*)+(.[0-9]{1,2})?$)
- IPV4地址:(((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))