Java中特殊的类——String类
Java中特殊的类——String类
1.String类的实例化方式
(1)直接赋值:
String 字符串名 = "要赋值内容";
public class TestString4_12 { public static void main(String[] args) { String str1 = "我是直接赋值进行实例化"; System.out.println("str1"+ str1);
} } |
(2)传统方式(new)
因为String为一个类,所以一定存在自己的构造方法,因此可以通过构造方法实例化对其赋值。
String类中的构造方法:
利用关键字new进行对象的实例化:
public class TestString4_12 { public static void main(String[] args) { String str2 = new String("我是利用构造方式进行实例化"); System.out.println("str2"+str2); } }
|
(3)两种实例化方式的区别
public class TestString4_12 { public static void main(String[] args) { //直接赋值方式实例化 String str1 = "我是直接赋值进行实例化"; String str3 = "我是直接赋值进行实例化"; String str4 = "我是直接赋值进行实例化"; System.out.println("str1"+ str1); System.out.println(str1==str3); System.out.println(str1==str4); System.out.println(str3==str4); //传统方式(new)实例化 String str2 = new String("我是利用构造方式进行实例化"); String str5 = new String("我是利用构造方式进行实例化"); String str6 = new String("我是利用构造方式进行实例化"); System.out.println("str2"+str2); System.out.println(str2==str5); System.out.println(str2==str6); System.out.println(str5==str6); } }
|
运行结果:
|
那么为什么会产生如下的结果呢?那么我们做以下分析:
String类使用了共享设计模式。
在JVM底层实际自动维护了一个对象池(字符串对象池),如果采用直接赋值进行String类的实例化,则该实例化对象(字符串的内容)将自动保存到这个对象池中,若下次继续使用直接赋值方式声明String类型,那么在此时则先看对象池中是否由指定内容,若有则直接引用;若没有,则开辟新的字符串对象并将其保存至对象池*下次使用。
直接赋值实例化内存分析图
如果使用String 构造方式实例化对象就会产生两块堆内存空间,并且其中的一块为垃圾空间。
传统方式实例化内存分析图
那么String两种对象实例化的区别:
1.直接赋值:只会开辟一块内存空间,并且该字符串对象会自动保存在对象池*下次使用。
2.构造方式:会开辟两块堆内存空间,其中一块成为垃圾空间,并且不会保存在对象池中。(但可以使用intern()方法手工入池)
(4)字符串共享问题
针对用构造方法实例化对象并不能将字符串自动入池的问题,String类提供了intern()方法进行手工入池。如下例:
public class TestString4_12 { public static void main(String[] args) { //进行此操作该字符串常量并没有保存在对象池中 String str7 = new String("hello"); String str8 = "hello"; System.out.println(str7==str8); //进行字符串的实例化并利用inter()方法进行手工入池操作 String str9 = new String("hello").intern(); String str10 = "hello"; System.out.println(str9==str10); } }
|
运行结果:
|
手工入池内存分析
2.字符串相等比较
我们熟知用“==”可以判断两个基本数据类型是否相等,上面的例子所示,我们使用了“==”来比较两个String对象的大小,那么究竟是如何作比较呢,我们可根据如下程序得出结论:“==”运算符本身是进行数值比较的,如果用来比较两个对象,则比较的应该是两个对象所保存的地址值,并不是比较对象中的内容;要想比较对象中保存的内容是否相等则应使用String提供的equals方法。
public class TestString4_12_1 { public static void main(String[] args) { String str1 = "hello lemon"; String str2 = new String("hello lemon"); System.out.println(str1==str2); System.out.println(str1.equals(str2)); } }
|
运行结果: |
“==”判断两字符串相等内存分析图
请解释String类中“==”和“equals”的区别
“==”:进行的是数值比较,比较的是两个字符串对象的内存地址数值。
“equals”:进行的是字符串内容的比较。
3.字符串常量是String的匿名对象
在任何语言的底层都不会提供直接的字符串类型。现在所谓的字符串只是高级语言提供给用户方便开发的支持而已。在java本身也没有直接提供字符串常量的概念,所有使用“”定义的内容从本质上来讲都是String的匿名对象。
public class TestString4_12_1 { public static void main(String[] args) { String str1 = "hello lemon"; String str2 = new String("hello lemon"); System.out.println(str1==str2); System.out.println(str1.equals(str2)); System.out.println("hello lemon".equals(str2)); } }
|
|
由此我们可以看出,之前使用的 String str1 = “hello lemon”本质上就是将一个匿名的String类对象设置有名字,而且匿名对象要保存在堆空间中。
提示:若要判断用户输入的字符串是否与特定字符串相等时,一定要将特定字符串写在前面。
public class TestString4_12_1 { public static void main(String[] args) { String str3 = null;//假定该字符串由用户输入 System.out.println("hello".equals(str3)); System.out.println(str3.equals("hello")); } }
|
运行结果:
|
若使用将“用户输入”字符串放前面作比较的话,如果用户没有输入,则会出现NullPointerException异常;但若使用将特定字符串放前面则不会出现此异常(任何的字符串常量都为String的匿名对象,所以该对象永远不会为null).
3.字符串常量不可变更
字符串定义如下:
由此看可以看出:String类是一个final类,即字符串已一旦定义就不可改变。
所有的语言对于字符串的底层实现都是字符数组,数组的最大缺陷就是长度不可改变。在定义字符串常量时,它的内容不可改变。
那么该如何理解内容不可改变呢,我们由下例给出分析:
public class TestString4_12_1 { public static void main(String[] args) { String str1 = "hello"; str1 = str1 + " lemon"; str1 = str1 + "!"; System.out.println(str1); } } |
|
字符串常量不可变更内存分析图
通过上面的分析可以发现字符串上没有发生改变,但是字符串对象的引用一直在发生改变,从而可以导致大量的垃圾空间。
所以在开发中不应该出现如下代码:
public class TestString4_12_1 { public static void main(String[] args) { String str = "lalala"; for(int i = 0;i <= 1000;i++){ str += i; } System.out.println(str); } } 这样的代码若很多用户使用同样的从操作,则会产生垃圾空间的数量相当可观。 |
5.String类中一些常用方法
(1)字符与字符串的互操作
字符串就是一个字符数组,所以在Sring类中提供有字符串与字符互相转换的方法。
字符数组->字符串:public String(char value[])
部分字符数组->字符串:public String(char value[], int offset, int count)
字符串->字符数组:public char[] toCharArray()
取得指定位置的字符:public char charAt(int index)
public class TestString4_12_1 { public static void main(String[] args) { //字符数组转化为字符串 char[] myData = new char[]{'h','e','l','l','o',' ', 'w','o','r','l','d',',', 'h','e','l','l','o',' ', '陕','科','大'}; //将字符数组中所有内容转化成字符串 System.out.println(new String(myData)); //将字符数组中指定位置开始的内容转化成字符串 System.out.println(new String(myData,6,6)); String str = "hello world ,hello 陕科大"; char[] temp = str.toCharArray(); for(int i = 0;i < temp.length;i++){ System.out.print(" "+temp[i]); } System.out.println(); //取得指定位置的字符 System.out.println("str的第10个字符为:"+str.charAt(10)); } }
|
|
例子:现有一个字符串判断其是否由数字组成:
解决思路:因为不知道字符串的长度和内容,所以最好是将字符串转化成字符数组然后判断每一个字符是否是“0”—“9”之间的内容,若是则为数字,反之亦然。
public class TestString4_12_1 { public static void main(String[] args) { String str2 = "1243234adsfs"; String str3 = "902093"; System.out.println(isNumConsist(str2)); System.out.println(isNumConsist(str3)); } //判断字符串是否全部由数字组成 public static boolean isNumConsist(String string){ char[] data = string.toCharArray(); for(int i = 0;i < data.length;i++){ if(data[i]<'0'||data[i]>'9'){ return false; } } return true; } }
|
运行结果:
|
(2)字节与字符串的互操作
(3)字符串比较
在之前提过字符串比较的equals()方法,该方法只可以用来比较区分大小写的相等判断,除此看还有以下比较方法:
区分大小写的比较: public boolean equals(Object anObject)
不区分大小写的比较:public boolean equalsIgnoreCase(String anotherString)
比较两个字符串大小关系(可以区分大小关系): public int compareTo(String anotherString)
public class TestString4_12_1 { public static void main(String[] args) { String str1 = "HELLO "; String str2 = "hello"; //不区分大小写的比较两个字符串是否相等 System.out.println(str1.equals(str2)); //区分大小写比较两个字符串是否相等 System.out.println(str1.equalsIgnoreCase(str2)); //比较两个字符串的大小关系,若两个字符串都为英文字母 //其返回结果为两个字符串不相同的第一个字母所相差的ASCII码值 System.out.println(str1.compareTo(str2)); } } |
|
(4)字符串查找
a.判断一个子串是否存在: public boolean contains(CharSequence s)
b.从头开始查找指定字符串的位置:public int indexOf(String str)
c.从指定位置开始查找子字符串的位置:public int indexOf(String str, int fromIndex)
d.从后向前查找子字符串的位置:public int lastIndexOf(String str)
e.从指定位置开始从后往前查找子字符串的位置:public int lastIndexOf(String str, int fromIndex)
f.判断是否已指定字符串开头:public boolean startsWith(String prefix)
g.从指定位置开始查找是否以指定字符串开头: public boolean startsWith(String prefix, int toffset)
h.判断是否以指定字符串结尾:public boolean endsWith(String suffix)
public class TestString4_12_1 { public static void main(String[] args) { String str1 = "Hello world,Hello 陕科大"; String str2 = "Hello world,hello 陕科大"; String str3 = "Hello world,hello 陕科大,hello lemon"; //判定子串是否存在,若存在返回true,若不存在返回false System.out.println(str1.contains("hello")); System.out.println(str1.contains("Hello")); //从开头查找指定字符串是否存在,若存在返回开始位置的索引值,若不存在返回-1 //若内容重复在,只返回第一次出现时的索引值 System.out.println(str2.indexOf("hello")); //从指定查找指定字符串是否存在,若存在返回开始位置的索引值,若不存在返回-1 //若内容重复在,只返回第一次出现时的索引值 System.out.println(str3.indexOf("hello",13)); //从后向前查找子字符串的位置 System.out.println(str2.lastIndexOf("hello")); //从指定位置向前查找子字符串的位置,若查找str3则返回的是最后一次出现时的索引值 System.out.println(str3.lastIndexOf("hello",27 )); //判断是否以指定字符串开头 System.out.println(str1.startsWith("hello")); System.out.println(str1.startsWith("Hello")); //从指定位置开始判断是否以指定字符串开头 System.out.println(str2.startsWith(str2,10)); //判断是否以指定字符串结尾 System.out.println(str1.endsWith("陕科大")); System.out.println(str3.endsWith("陕科大")); } }
|
运行结果:
|
(5)字符串替换
替换所有指定的内容:public String replaceAll(String regex, String replacement)
替换首次出现的指定内容:public String replaceFirst(String regex, String replacement)
public class TestString4_12_1 { public static void main(String[] args) { String str1 = "Hello world,Hello 陕科大"; String str2 = "Hello world,hello 陕科大"; String str3 = "Hello world,hello 陕科大,hello lemon"; //用指定字符串 替换原字符串中的所有指定要被替换的内容 System.out.println(str3.replaceAll("hello", "hi")); //用指定字符串 替换原字符串中的所有指定要被替换的内容 System.out.println(str3.replaceFirst("hello", "hi")); } } |
|
(6)字符串拆分
可以将一个完整的字符串按照指定的分隔符拆分成多个字符串。
将字符串全部拆分: public String[] split(String regex)
将字符串部分拆分:public String[] split(String regex, int limit)
public class TestString4_12_1 { public static void main(String[] args) { String str1 = "Hello world,Hello 陕科大"; String str2 = "Hello world,hello 陕科大"; String str3 = "Hello world,hello 陕科大,hello lemon"; //将字符串按照指定分隔符全部拆分,拆分后结果为字符数组 String[] result = str3.split(" "); for(int i = 0;i < result.length;i++){ System.out.println(result[i]); } System.out.println(); //将字符串按照指定分隔符按照指定数组的长度部分拆分 String[] result1 = str3.split(" ", 2); for(int i = 0;i < result1.length;i++){ System.out.println(result1[i]); } System.out.println(); //拆分IP地址(若出现有些字符无法拆分时,则用转义字符"\\"进行转义) String str4 = "192.168.237.188"; String[] result2 = str4.split("\\."); for(int i = 0;i <result2.length;i++){ System.out.print(result2[i]+" "); } System.out.println(); } }
|
|
(7)字符串截取
从一个字符串中截取出部分内容
从指定索引位置开始截取到结尾:public String substring(int beginIndex)
截取部分内容(指定开始索引指定结束索引):public String substring(int beginIndex, int endIndex)
public class TestString4_12_1 { public static void main(String[] args) { String str1 = "Hello world,Hello 陕科大"; //从指定位置截取字符串 System.out.println(str1.substring(6)); //从指定位置开始到指定位置结束截取字符串 System.out.println(str1.substring(6, 12)); } }
|
|
(8)其他方法
去掉字符串的左右空格保留中间空格:public String trim()
字符串转大写:public String toUpperCase()
字符串转小字:public String toLowerCase()
字符串连接(等同于+,入池):public String concat(String str)
public class TestString4_12_1 { public static void main(String[] args) { String str1 = " Hello world,Hello 陕科大 "; String str2 = ",Hello lemon"; System.out.println("【"+str1+"】"); //去掉字符串左右空格保留中间空格 System.out.println("【"+str1.trim()+"】"); //字符串长度 System.out.println(str1.length()); //是否为空字符串 System.out.println(str1.isEmpty()); //字符串转大写 System.out.println(str1.toUpperCase()); //字符串转小写 System.out.println(str1.toLowerCase()); //字符串连接 System.out.println(str1.concat(str2)); } }
|
|
范例:实现首字母大写
public class TestString4_12_1 { public static void main(String[] args) { //实现首字母大写 System.out.println(RealizeFirstUpper("hello")); System.out.println(RealizeFirstUpper("")); System.out.println(RealizeFirstUpper("l")); } public static String RealizeFirstUpper(String str){ if("".equals(str)||str == null){ return str; } if(str.length()>1){ //字符串长度对于1时,将第一个字符大写,然后拼接从第二个字符开始截取的字符串 return str.substring(0,1).toUpperCase()+str.substring(1); } //字符串只有一个字符的情况 return str.toUpperCase(); } }
|
|
6.StringBufffer类
String类的特点:
任何的字符串常量都是String类的匿名对象,且String一旦声明就不可改变(内容不可变,若要改变内容,改变的是索引)。
为了方便字符串的修改我们来介绍StringBuffer类:String类中用“+”即可实现字符串的连接,但在StringBuffer类中要使用append()方法。
public synchronized StringBuffer append(Object obj)
public class TestString4_12_1 { public static void main(String[] args) { StringBuffer strBuf = new StringBuffer(); //StringBuffer中字符串的拼接 strBuf.append("hello").append(" world"); fun(strBuf); System.out.println(strBuf); } //实现换行并拼接资格字符串 public static void fun(StringBuffer temp){ temp.append("\n").append("hhahh"); } } |
|
1.String和StringBuffer类的最大区别:
String类的内容不可修改,而StringBuffer类的内容可以修改。(若需要频繁修改字符串的内容则应该使用StringBuffer类)
2.String类和StringBuffer类不能直接转换,要想转换可使用以下原则:
String->StringBuffer: 利用StringBuffer类的构造方法或append()方法。
StringBuffer-> String:调用toString()方法
3.StringBuffer类的一些方法:
(1)字符串反转:public synchronized StringBuffer reverse()
(2)删除指定范围的数据:public synchronized StringBuffer delete(int start, int end)
(3)插入数据: public StringBuffer insert(int offset, 各种数据类型)
public class TestString4_12_1 { public static void main(String[] args) { StringBuffer strBuf = new StringBuffer("hello world"); StringBuffer strBuf1 = new StringBuffer("hello world"); StringBuffer strBuf2 = new StringBuffer("hello world"); //字符串反转 System.out.println(strBuf.reverse()); //删除指定位置的元素 System.out.println(strBuf1.delete(2,8)); //插入数据 System.out.println(strBuf2.insert(5, "你好")); } } |
|
总结:
字符串一般的使用原则:
字符串使用直接赋值。
字符串比较使用equals实现。
字符串不能改变太多。
String、StringBuffer、StringBuinder类的区别:
1、String的内容不可改变,StringBuffer和StringBuilder的内容可以改变。
2、StringBuffer采用同步处理,属于线程安全操作,StringBuilder采用异步处理属于不安全操作。