黑马程序员技术交流社区

标题: 【上海校区】Java之路:String类 [打印本页]

作者: 不二晨    时间: 2018-12-14 09:51
标题: 【上海校区】Java之路:String类
Java中定义了String和StringBuffer两个类来封装对字符串的各种操作,它们都被放到了java. lang包中,不需要用import java.lang这个语句导入该包就可以直接使用它们。
String类用于比较两个字符串,查找和抽取串中的字符或子串,进行字符串与其他类型之间的相互转换等。String类对象的内容一旦被初始化就不能再改变,对于String类的每次改变(例如字符串连接等)都会生成一个新的字符串,比较浪费内存。
StringBuffer类用于内容可以改变的字符串,可以将其他各种类型的数据增加、插入到字符串中,也可以转置字符串中原来的内容。一旦通过StringBuffer生成了最终想要的字符串,就应该使用StringBuffer.toString()方法将其转换成String类,随后,就可以使用String类的各种方法操纵这个字符串了。StringBuffer每次都改变自身,不生成新的对象,比较节约内存。
1、String类的两种实例化方式:

实例化字符串对象可以采用两种方式完成,不同方式在内存中呈现方式不同的分配形式。

String str1 = "abcd";
String str2 = new String("efg");
1
2
(1)直接赋值方式
如果现在采用直接赋值的形式,那么就好比将一个字符串的常量赋给了指定的字符串变量,而且每一个字符串常量都属于String的匿名对象。
String str=“Hello”;           //直接赋值
1


以直接赋值的方式创建字符串对象str,仅开辟一块堆内存和一块栈内存空间。若采用直接赋值的方式定义多个字符串对象呢?
public class CompareString {
        public static void main(String[] args) {
                String str1 = "Hello",        // 直接赋值
                           str2 = "Hello",        // 直接赋值
                           str3 = str1;        // 引用传递,把1的地址赋给3
                System.out.println("str1==str2 : " + (str1 == str2));
                System.out.println("str1==str3 : " + (str1 == str3));
                System.out.println("str2==str3 : " + (str2 == str3));
        }
}
1
2
3
4
5
6
7
8
9
10
【结果】


在Java中,若字符串对象使用直接赋值方式完成,如strA,那么首先在第一次定义字符串的时候,会自动的在堆内存之中定义一个新的字符串常量"Hello",如果后面还有其他字符串的对象(如:strB)采用的是直接赋值的方式实例化,并且此内容已经存在,那么就不会开辟新的字符串常量,而是让其指向了已有的字符串内容,即strA,strB指向同一块内存,所以strA == strB比较的结果是true。这样的设计在开发模式上讲称为共享设计模式。

所谓的共享设计模式指的是 : 在JVM底层(如果是用户自己实现就是依靠动态数组)准备出一个对象池(多个对象),如果现在按照某一个特定方式进行对象实例化的操作,那么此对象的内容会保存到对象池之中,而后如果还有其他的对象也采用了固定的方式声明了与之同样的内容,则此时将不会重新保存新对象到对象池之中,而是从对象池中取出已有的对象内容继续使用,这样一来可以有效的减少垃圾空间的产生。
(2)构造方法实例化
String str = new String("Hello");
此时会开辟两块堆内存空间,其中有一块堆内存将成为垃圾,通过构造方法进行的String类对象,也无法进行自动入池的操作,即:数据无法共享。

在String类中提供了一个方法,可以帮助用户手工入池。
手工入池:public String intern();
public class CompareString {
        public static void main(String[] args) {
                String strA = "Hello";        // 直接赋值
                String strB = new String("Hello").intern();// 构造并手工入池
                String strC = "Hello";        // 直接赋值
                String strD = new String("Hello");        // 构造
                System.out.println("strA==strB : " + (strA == strB));
                System.out.println("strA==strC : " + (strA == strC));
                System.out.println("strB==strC : " + (strB == strC));
                System.out.println("strA==strD : " + (strA == strD));
        }
}
1
2
3
4
5
6
7
8
9
10
11
12
【结果】



String类的对象在实例化时采用不同的方式,开辟的内在空间也不同。
直接赋值: 只开辟一块堆内存空间,而且保存的字符串内容可以自动入池,以供其他内容相同的字符串对象使用;
构造方法: 开辟两块堆内存空间,有一块将成为垃圾,并且字符串的内容无法自动入池,但是可以使用String类中的intern()方法手工入池。
2、String内容比较

(1)用equals()


(2)使用比较运算符“==”
public class CompareString {
        public static void main(String[] args) {
                String str1 = "Hello";
                String str2 = "Hello";
                String str3 = new String("Hello");
                System.out.println("str1==str2 : " + (str1 == str2));
                System.out.println("str1==str3 : " + (str1 == str3));
        }
}
1
2
3
4
5
6
7
8
9
【结果】


在此之前先说明一下:
① 如果说String是一个类,那么str1一定是这个类的对象,对象名称一定要保存在栈内存之中,那么字符串“Hello”一定保存在堆内存之中;

② 任何情况下使用关键字new都一定会开辟一个新的堆内存空间;

③ String本身是一个类,所以String类的对象是一定可以进行引用传递的,引用传递的最终结果就是不同的栈内存将保存同一块堆内存空间的地址;

④ 栈内存像int型数据,里面保存的是数值,每一个栈内存只能够保存一块堆内存的物理地址数值。

根据上例可发现,针对于“==”在本次操作之中实际上是完成了它的相等判断功能,只是它完成的是两个对象的堆内存地址的相等判断,属于地址的数值相等比较,并不是真正意义上的字符串内容的比较。
如果现在要想进行字符串内容的比较,可以使用equals( ):

public class CompareString {
        public static void main(String[] args) {
                String str1 = "Hello";
                String str2 = "Hello";
                String str3 = new String("Hello");
                System.out.println("str1.equals(str2) : " + (str1.equals(str2)));
                System.out.println("str1.equals(str3) : " + (str1.equals(str3)));
        }
}
1
2
3
4
5
6
7
8
9
【结果】


3、字符串大小比较

如果希望知道字符串大小情况,需要调用compareTo( )方法:
字符串对象小于给定字符串:compareTo( )方法返回小于零的值;
字符串对象等于给定字符串:compareTo( )方法返回小于零的值;
字符串对象大于给定字符串:compareTo( )方法返回大于零的值;
比较是根据字母顺序,严格来讲是根据字符的ASCII码值进行比较的,返回结果是第一个不同字符ASCII码的差值。
public class CompareString {
        public static void main(String[] args) {
                String str1 = "This is a string!",
                           str2 = new String("this is a string!");
                int result1 = str1.compareToIgnoreCase("That is another string!"),
                        result2 = str1.compareTo("This is a string!"),
                        result3 = str1.compareTo(str2);
                System.out.println("result1:" + result1);
                System.out.println("result2:" + result2);
                System.out.println("result3:" + result3);
        }
}
1
2
3
4
5
6
7
8
9
10
11
12
【结果】


字符串对象str1,compareTo( )运行结果是数值,存放结果的变量的类型定义为int。
str1.compareTo (“That is another string”);中str1和参数字符串前两个字符“Th”相同,“i”的ASCII码为105,“a”的ASCII码为97,结果为第一个不同字符ASCII值的差,result1=8。

str1.compareTo(“This is a string”);,str1和参数字符串一致,result1=0。

str1.compareTo(str2);比较时区分大小写,“T”的ASCII码值为84,“t”的ASCII码值为116, result2=-32。

由上知,compareTo( )是按字符串中逐个字符的ASCII码值进行比较。
4、字符串常量是String类的匿名对象

任何的语言实际上都不会提供字符串类型,但是在Java里面为了简化用户的开发难度,专门提供了String类和使用“"”定义的字符串,但是实际上每一个字符串严格来讲都是String类的匿名对象。

匿名对象的特点:没有名字,而且可以调用类中的相关方法。
String str="Hello";           //直接赋值
System.out.println(str.equals("Hello"));    //true,字符串对象调用equals()
System.out.println("Hello".equals(str)) ;
//true,字符串常量调用equals( ),“Hello”是String类的匿名对象
1
2
3
4
判断某一个用户输入的字符串内容是否等于指定的字符串内容,若采用字符串对象.equals(“内容”)的方式,如果用户没输入字符串,会出现NullPointerException警告,可以采用"字符串".equals(字符串对象)”的方式解决这个问题。
String str=null;           //假设这个字符串由用户输入
if(str.equals("Hello")) {       //若没输入字符串str的内容,出现NullPointerException
        System.out.println("验证通过。");
}
if("Hello".equals(str)) {      //equals()可自动处理null问题,正常判断
        System.out.println("验证通过。");
}
1
2
3
4
5
6
7
5、字符串一旦声明则不可改变

字符串的内容用户一旦定义,就表示开辟好了指定的空间,那么字符串的内容就不能够被改变了。
先来看一个例子:

public class StringChange {
        public static void main(String[] args) {
                String str = "Hello";        // 直接赋值
                str += "World";        // 字符串连接
                str += "!!!";        // 字符串连接
                System.out.println(str);
        }
}
1
2
3
4
5
6
7
8
【结果】

程序运行结果显示,字符串str的内容改变了,但事实并非如此,看下图:


由上图可以发现,字符串的内容没有改变,改变的只是String类对象的引用,并且会产生大量字符串垃圾。 因此,应该尽量避免出现不断修改字符串内容的现象,以免出现大量垃圾。
6、String类的常用方法

(1)字符与字符串

public class CharToString {
        public static void main(String[] args) {
                char[] data = "HelloJava!".toCharArray();        // 将字符串转换成字符数组
                // 将全部字符数组转换成字符串
                String str = new String(data);        // 构造类型方法
                System.out.println(str);
                // 将部分字符数组转换成字符串时,要设置起点和长度
                str = new String(data,5,4);        // 构造类型方法,起始为5,长度为4
                System.out.println(str);
               
                char c = "HelloJava!".charAt(5);
                System.out.println(c);
        }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
【结果】


(2)字节与字符串
序号        方法名称        类型        描述
1        public String(byte[ ] bytes)        构造        将全部字节数组变为字符串
2        public String(byte[ ] bytes, int offset, int length)        构造        将部分字节数组变为字符串
3        public byte[ ] getBytes()        普通        将字符串变为字节数组
4        public byte[ ] getBytes(String charsetName) throws UnsupportedEncodingException        普通        将字符串转码
public class StringBytes {
        public static void main(String[] args) {
                String str = "hellojava";
                byte[] data = str.getBytes();        // 将字符串变为字节数组
                for(int i = 0; i < data.length; i++) {
                        System.out.print(data + " ");       
                }
                System.out.println();
                for(int i = 0; i < data.length; i++) {
                        data -= 32;        // 将字符数组变大写
                }
                System.out.println(new String(data));        // 将全部byte字节数组变成字符串
                System.out.println(new String(data, 5, 4));        // 将部分byte字节数组变成字符串
        }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
【结果】


(3)字符串查找方法
由一个字符串中若干个字符按顺序形成的连续字符片段,就是字符串的子串。
从一个指定的字符串之中查找某一个子字符串是否存在的操作,称为字符串查找,查找的操作方法如下:

public class StringSearch {
        public static void main(String[] args) {
                String str = "**hello$$world##";       
                if(str.contains("hello")) { // 判断字符串str是否包含hello
                        System.out.println("内容存在!");
                }
                // 查找r的位置,并返回索引值,若是找不到返回-1
                if(str.indexOf("r") != -1) {
                        System.out.println("内容存在!字符串位置:" + str.indexOf("r"));
                }
                // 从指定位置开始查找o的位置,并返回索引值,若是找不到返回-1
                if(str.indexOf("o", 6) != -1) {
                        System.out.println("内容存在!字符串位置:" + str.indexOf("o", 6));
                }
                // 由指定位置从后向前开始查找w的位置,并返回索引值,若是找不到返回-1
                if(str.lastIndexOf("w", 12) != -1) {
                        System.out.println("内容存在!字符串位置:" + str.lastIndexOf("w", 12));
                }
               
                System.out.println(str.startsWith("**"));        // 判断字符串起始内容
                System.out.println(str.startsWith("$$", 7));        // 判断字符串起始内容
                System.out.println(str.endsWith("##"));        // 判断字符串结尾内容
        }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
【结果】


(4)字符串替换方法

public class StringReplace {
        public static void main(String[] args) {
                String str = "Hello World!";
                // 将字符串中的o全部替换成***
                System.out.println(str.replaceAll("o", "***"));
                // 替换第一次出现的l
                System.out.println(str.replaceFirst("l", "_"));               
        }
}
1
2
3
4
5
6
7
8
9
【结果】


(5)字符串截取方法

public class SubString {
        public static void main(String[] args) {
                String str = "Hello World!";
                // 截取从指定位置到结尾
                System.out.println(str.substring(6));
                // 截取从开始位置到结束位置
                System.out.println(str.substring(0, 5));
        }
}
1
2
3
4
5
6
7
8
9
【结果】

字符串中索引值从0开始,substring(int beginIndex, int endIndex),beginIndex表示开始处的索引(包括),endIndex表示结束处的索引(不包括)。截取从指定的位置beginIndex 处开始,一直到索引 endIndex - 1 处的字符。因此,该子字符串的长度为 endIndex-beginIndex。

若str.substring(0,13),会出现IndexOutOfBoundsException错误。取子串时,如果 beginIndex为负,或 endIndex 大于此 String 对象的长度,或 beginIndex 大于 endIndex,则出现错误。
(6)字符串拆分方法
所谓的字符串拆分,指的是将一个字符串按照指定的字符串regex拆分为若干个字符串,返回的是一个字符型数组,regex不作为任何数组元素的部分返回,操作方法如下:

public class StringSplit {
        public static void main(String[] args) {
                String str = "Hello world! Hello java!";
                String[] str1 = str.split(" "); // 按照空格拆分
                for(int i = 0; i < str1.length; i++) {
                        System.out.println(str1);
                }
                System.out.println("-----------------------------");
                String[] str2 = str.split(" ", 3);        // 按照空格拆分为2个子串
                for(int i = 0; i < str2.length; i++) {
                        System.out.println(str2);
                }
        }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
【结果】


注意事项
(1)如果用".“作为分隔的话,必须是如下写法:String.split(”\."),这样才能正确地分隔开,不能用String.split(".");

String str = “192.168.1l.2” ;
String data [] = str.split("\.") ;
data字符数组中的元素分别为:192,168,11,2

(2)如果用"|“作为分隔的话,必须是如下写法:String.split(”\|"),这样才能正确的分隔开,不能用String.split("|");

(3)如果用"“作为分隔,就得写成这样:String.split(”\\"),因为在Java中是用"\“来表示”“的, “.”、”|“和"都是转义字符,必须得加”\";

(4)如果在一个字符串中有多个分隔符,可以用"|“作为连接符,比如:str=“acount=8 and uu =45 or n=25”,把三个都分隔出来,可以用Str.split(“and|or”),即用"and"或者"or"进行分隔,分隔结果为"acount=8”," uu =45"," n=25"。
(7)其他方法


---------------------
【转载】仅作分享,侵删
作者:星云999
原文:https://blog.csdn.net/qq_43555323/article/details/84930124



作者: 不二晨    时间: 2018-12-18 17:45
奈斯
作者: lelehahalee    时间: 2018-12-19 11:15
不错!总结得好!
作者: 梦缠绕的时候    时间: 2018-12-20 16:53

作者: 一个人一座城0.0    时间: 2018-12-24 08:48
看一看。




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2