字符串
一.String类
- String对象是不可变的
- String类中每一个看起来会修改String值的方法,实际上都是创建了一个全新的String对象,以包含修改后的字符串内容,而最初的String对象丝毫未动
1.String对象的比较
在比较String对象的时候,不能单纯的用==来比较两个字符串的大小,这样比较的是他们的堆地址 ,在比较两个字符串的时候,要用到String类提供的equals方法,它的返回值是boolean类型 ;
下面看一段示例:
String str1 = "i love you";
String str2 = "i hate you";
System.out.println(str1.equals(str2)); //equals函数的返回值为Boolean类型
这段代码的输出结果为false;
-
小知识:在今后的开发中,如果要判断用户输入的字符串是否等同于特定字符串,一定要将特定字符串写在前面,避免用户输入的字符串为空导致出错;(看如下例子)
String str2 = null; System.out.println(str2.equals("hello"));
会出现以下错误:Exception in thread “main” java.lang.NullPointerException
2.重载“+”与StringBuilder
因为字符串(String对象)是不可变的,所以在对字符串进行拼接时,将会产生大量垃圾空间(虽然java系统会在底层自动优化),带来一定的效率问题;
在进行字符串的拼接时,编译器其实自主使用了StringBuilder类,创建一个StringBuilder对象,用以构造最终的String,并用每个字符串调用一次StringBuilder的append()方法。最后调用toString方法来生成结果。
这样也许你会觉得反正编译器会自主优化成StringBuilder对象,但学过汇编的可以进入到底层源码中查看,会发现尽管这样,当使用拼接次数过大时,还是会产生效率问题;
3.String的匿名对象
- 在任何语言的底层,都不会提供有直接的字符串类型。现在所谓的字符串只是高级语言提供给用户方便开发的支持而已。在java中,所有使用 “ ” 定义的内容本质上来讲都是String的匿名对象。
- 例如:String str = “ i love you ” ;这条语句中的 “ i love you ” 就是String的一个匿名对象,我们可以通过“ i love you ” 加点(.)来调用String类的各种方法,这进一步说明了“ i love you ”是一个对象;
4.String的两种实例化格式以及他们的区别
String类有两种实例化格式,如下所示:
String str1 = "hello"; //直接实例化 方法一
String str2 = new String("hello"); //方法二
我们来看方法一的特点:
String str1 = "hello";
String str2 = "hello";
String str3 = "hello";
System.out.println(str1 == str2);
System.out.println(str2 == str3);
System.out.println(str1 == str3);
这段代码将会输出:
true
true
true
- 这说明了直接实例化的字符串一样时,他们都在同一块堆内存中保存,如下图:
- String类的设计使用了共享设计模式
- 在JVM底层实际上会自动维护一个对象池(字符串对象池),如果现在采用了直接赋值的模式进行String类的对象 实例化操作,那么该实例化对象(字符串内容)将自动保存到这个对象池之中。如果下次继续使用直接赋值的模式 声明String类对象,此时对象池之中如若有指定内容,将直接进行引用;如若没有,则开辟新的字符串对象而后将 其保存在对象池之中以供下次使用
所谓的对象池就是一个对象数组(目的就是减少开销);
下面看看方法二:
String str = new String("hello");
这条语句在内存中分布如下图:
通过如上图可见,通过构造方法实例化就会产生两块堆内存空间,并且一块为垃圾空间(没有引用指向的堆空间为垃圾空间 )。
观察下面一段代码:(字符串共享问题)
String str1 = "hello";
String str2 = new String("hello"); //该字符串常量没有保存到对象池中
System.out.println(str1 == str2); //输出false
代码输出:false
为了解决这一问题,在String类中提供有方法入池操作:public String intern();
如下代码:
//入池操作
String str1 = new String("hello").intern();
//str1 = str1.intern(); //或者这样写,记住intern()方法有返回值哟,为String;
String str2 = "hello";
System.out.println(str1 == str2);
注意:intern()方法返回值为String,所以注释那一句不要写成:str1 . intern( ) ;
5.字符与字符串
- 字符串就是一个字符数组,所以String类中有转换为字符数组的方法;
方法名称 | 类型 | 描述 | |
---|---|---|---|
1 | public String(char value[] ) | 构造 | 将字符数组中所有内容变为字符串 |
2 | public String (char value[],int offset,int count) | 构造 | 将部分字符数组中的内容变为字符串 |
3 | public char charAt(int index) | 普通 | 取得指定索引位置的字符,索引从0开始 |
4 | public char[] toCharArray() | 普通 | 将字符串变为字符数组返回 |
看下面一段练习代码,输出答案已在注释上 :
//字符串--->数组
String str1 = "i love you";
char[] arr = str1.toCharArray();
for(char x:arr){ //foreach语句
System.out.print(x+"、"); //i、 、l、o、v、e、 、y、o、u、
}
//数组--->字符串
System.out.println(new String(arr)); //i love you
System.out.println(new String(arr,2,5)); //love
6.字符串比较常用方法
方法名称 | 类型 | 描述 | |
---|---|---|---|
1 | public boolean equals(object anObject) | 普通 | 区分大小写的比较 |
2 | public boolean equalsIgnoreCase(String anotherString) | 普通 | 不区分大小写的比较 |
3 | public int compareTo(String anotherString) | 普通 | 比较两个字符串的大小关系 |
-
方法一和方法二就不用多介绍,**对于方法三, ** 这是一个非常重要的方法 ,它的返回值是一个整型,规则如下:
相等时:返回0;
小于:返回内容小于0;
大于:返回内容大于0;
看下面这段关于compareTo()方法 的练习代码,来具体认识它:
String str1 = "hEllo";
String str2 = "kello";
System.out.println(str1.compareTo(str2)); //-3
System.out.println("A".compareTo("a")); //-32
System.out.println("b".compareTo("a")); //1
- 从上面这段代码可以看出,返回的数值是字符在ASCII表上对应的差值,所以compareTo()是一个可以区分大小写的方法,是String类中非常重要的一个方法。
7.字符串查找
关于字符串查找有如下方法:
方法名称 | 类型 | 描述 | |
---|---|---|---|
1 | public boolean contains(CharSequence s) | 普通 | 判断一个子字符串是否存在 |
2 | public int indexOf(String str) | 普通 | 从头开始查找指定字符串的位置,查到了返回位置的开始索引,如果查不到返回 -1 |
3 | public int indexOf(String str,int fromIndex) | 普通 | 从指定位置开始查找子字符串的位置 |
4 | public int lastIndexOf(String str) | 普通 | 由后向前查找子字符串的位置 |
5 | public int lastIndexOf(String str,int fromIndex) | 普通 | 由指定位置由后向前查找 |
6 | public boolean startsWith(String prefix) | 普通 | 判断是否以指定字符串开头 |
7 | public boolean startWith(String prefix,int toffset) | 普通 | 从指定位置开始判断是否以指定字符串开头 |
8 | public boolean endsWith(String suffix) | 普通 | 判断是否以指定字符串结尾 |
8.字符串替换
方法名称 | 类型 | 描述 | |
---|---|---|---|
1 | public String replaceAll(String regex,String replacement) | 普通 | 替换所有指定内容 |
2 | public String replaceFirst(String regex,String replacement) | 普通 | 替换首个内容 |
9.字符串拆分
方法名称 | 类型 | 描述 | |
---|---|---|---|
1 | public String[] split(String regex) | 普通 | 将字符串全部拆分 |
2 | public String[] split(String regex,int limit) | 普通 | 将字符串部分拆分,该数组长度就是limit极限 |
注意:
- 有些内容无法正常拆分的时候,需要用到“\\”转义, 例如拆分IP地址;
- 拆分IP地址和多次拆分 ⭐以后在开发中会经常遇到,特别是多次拆分;
练习代码如下:
//拆分IP地址
String str = "123.45.67.8";
String[] arr = str.split("\\.");
for (String x:arr) {
System.out.println(x);
}
输出:
123
45
67
8
//多次拆分
String str = "Bob:19|Alice:20";
String[] result = str.split("\\|");
for (int i = 0; i < result.length; i++) {
String[] temp = result[i].split(":");
System.out.println(temp[0]+"="+temp[1]);
}
输出:
Bob=19
Alice=20
10.字符串截取
方法名称 | 类型 | 描述 | |
---|---|---|---|
1 | public String substring(int beginIndex) | 普通 | 从指定索引截取到结尾 |
2 | public String substring(int beginIndex,int endIndex) | 普通 | 截取部分内容 |
11.其他常用方法
方法名称 | 类型 | 描述 | |
---|---|---|---|
1 | public String toUpperCase() | 普通 | 字符串转大写 |
2 | public String toLowerCase() | 普通 | 字符串转小写 |
3 | public int length() | 普通 | 取得字符串长度 |
4 | public String trim() | 普通 | 去掉字符串中的左右空格,保留中间空格 |
字符串是最普遍最常用的,熟练掌握它的各种用法,才会在开发中不再细枝末叶上耗费时间!
二.StringBuffer类
- StringBuffer类与String类最大的区别就在于:String的内容不可修改,而StringBuffer的内容可以修改 ;
- StringBuffer修改字符串使用append方法;
- 除了append方法,StringBuffer还有一些String类没有的方法,例如字符串反转方法:reverse();
练习代码:
StringBuffer sb = new StringBuffer();
sb.append("hello").append("world").append("\n").append("haha");
System.out.println(sb);
输出:
helloworld
haha
-
面试宝典:
StringBuffer、String、StringBuilder的区别是什么?- String内容不可修改,StringBuffer和StringBuider内容可修改;
- StringBuffer采用同步处理,属于线程安全操作;而StringBuilder采用异步处理,属于线程不安全操作;