博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

JavaEE - 09常用类

Posted on 2020-12-07 21:51  Kingdomer  阅读(106)  评论(0编辑  收藏  举报

JavaEE - 09常用类 

(1)字符串相关类

(1.1)String

(1.1.1)String特性

> String类: 代表字符串。Java程序中的所有字符串字面值(如"abc")都作为此类的实例实现。
> String是一个final类,不可被继承。代表不可变的字符序列
  不可变性: 1. 当对字符串重新赋值时,需要重新指定内存区域赋值,不能使用原有的value进行赋值。
           2. 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的。
           3. 当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值。
> 字符串是常量,用双引号引起来表示。它们的值在创建之后不能更改。
> String对象的字符内容是存储在一个字符数组final char[] value中的。
> 通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
> 字符串常量池中是不会存储相同内容的字符串的。
> String实现了Serializable,表示支持序列化;实现了Comparable,表示可以比较大小。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;

(1.1.2)String对象创建

String str = "hello";
// 本质上 this.value = "".value;
String s1 = new String();
// this.value = original.value;
String s2 = new String(String original);
// this.value = Arrays.copyOf(value, value.length);
String s3 = new String(char[] a);
// this.value = Arrays.copyOfRange(value, offset, offset+count);
String s4 = new String(char[] a, int startIndex, int count);

面试题: String str1 = "abc" 与 String str2 = new String("abc") 的区别

字符串常量存储在字符串常量池,目的是共享; 字符串非常量对象存储在中。

   @Test
    public void test(){
        String s1 = "abc";
        String s2 = "abc";
        String s3 = new String("abc");
        String s4 = new String("abc");
        // s1 和 s2 的数据"abc",字符串常量池;s3 和 s4,在堆中开辟空间后对应的地址值。
        System.out.println(s1 == s2);   // true
        System.out.println(s1 == s3);   // false
        System.out.println(s1 == s4);   // false
        System.out.println(s3 == s4);   // false
    }
    @Test
    public void test2(){
        Person p1 = new Person("Tom",12);
        Person p2 = new Person("Tom",12);
        System.out.println(p1.name == p2.name);  // true
System.out.println(p1.name.equals(p2.name)); // true }

面试题:String s = new String("abc");方式创建对象,内存中创建了几个对象?

两个: 一个是堆空间中new结构,另一个是char[]对应的常量池中数据:"abc"。

面试题: String类是否可以被继承?

不可以被继承。继承后子类可重写父类的方法,那么 

(1.1.3)字符串拼接计算

> 字符串拼接时常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。
> 字符串拼接时只要其中有一个是变量,结果就在中。
> 如果拼接的结果调用intern()方法,返回值就在常量池中。
    @Test
    public void test3(){
        String s1 = "abc";
        String s2 = "DEF";
        String s3 = "abcDEF";
        String s4 = "abc" + "DEF";
        
        String s5 = s1 + "DEF";
        String s6 = "abc" + s2;
        String s7 = s1 + s2;
        String s8 = (s1 + s2).intern();
        System.out.println(s3 == s4);  // true
        System.out.println(s3 == s5);  // false
        System.out.println(s3 == s6);  // false
        System.out.println(s5 == s6);  // false
        System.out.println(s3 == s7);  // false
        System.out.println(s5 == s7);  // false
System.out.println(s3 == s8); // true
}

测试题:

public class StringTest {

    String str = new String("good");
    char[] ch = {'t','e','s','t'};
    public void change(String str, char ch[]){
        str = "test ok";
        ch[0]= 'b';
    }
    public static void main(String[] args) {
        StringTest st = new StringTest();
        st.change(st.str, st.ch);
        System.out.println(st.str);  // good
        System.out.println(st.ch);   // best
    }
}

测试题:

   @Test
    public void test4(){
        String s = "0";
        for(int i =1; i<=5;i++){
            s += i;
        }
        System.out.println(s);  // 常量池:"0" ,堆空间: "01","012","0123","01234","012345"

        String str = new String("0");
        for(int i =1; i<=5;i++){
            str += i;
        }
        System.out.println(str);  // 常量池:"0", 堆空间: "0" ,"01","012","0123","01234","012345"
}

(1.1.4)String常用方法

  • int length():           返回字符串的长度: return value.length
  • char charAt(Int index): 返回某索引处的字符  return value[index]
  • boolean isEmpty():      判断字符串是否为空。return value.length == 0
  • String toLowerCase():   使用默认语言环境的规则,将此 String 中的所有字符都转换为小写。
  • String toLowerCase(Locale locale): 使用给定 Locale 的规则将,此 String 中的所有字符都转换为小写。
  • String toUpperCase():   使用默认语言环境的规则,将此 String 中的所有字符都转换为大写。
  • String toUpperCase(Locale locale): 使用给定 Locale 的规则,将此 String 中的所有字符都转换为大写。
  • String trim():          返回字符串的副本,忽略前导空白和尾部空白。
  • boolean equals(Object obj):         比较字符串的内容是否相同。
  • boolean equalsIgnoreCase(String anotherString): 与equals方法相似,忽略大小写。
  • String concat(String str):           将指定字符串连接到此字符串的结尾。等价于用"+"。
  • int compareTo(String anotherString): 比较两个字符串的大小
  • String substring(int beginIndex):    返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
  • String substring(int beginIndex, int endIndex):  返回一个新字符串,它是此字符串的从beginIndex开始截取到endIndex(不包含)的一个子字符串。
  • boolean endsWith(String suffix):    测试此字符串是否以指定的后缀结束。
  • boolean startsWith(String prefix):  测试此字符串是否以指定的前缀开始。
  • boolean startsWith(String prefix, int toffset):  测试此字符串从指定索引开始的子字符串是否以指定前缀开始。
  • boolean contains(CharSequence chars): 判断是否包含指定的字符系列。包含返回true。
  • int indexOf(String str):                    返回指定子字符串在此字符串中第一次出现处的索引。未找到返回-1。
  • int indexOf(String str, int fromIndex):     返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始。
  • int lastIndexOf(String str):                返回指定子字符串在此字符串中最右边出现处的索引。未找到返回-1。
  • int lastIndexOf(String str, int fromIndex): 返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索。
  • String replace(char oldChar, char newChar): 返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
  • String replace(CharSequence target, CharSequence replacement): 使用指定的字面值替换字符串中所有匹配的子字符串。
  • String replaceAll(String regex, String replacement): 使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
  • String replaceFirst(String regex, String replacement):使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
  • boolean matches(String regex): 告知此字符串是否匹配给定的正则表达式。
  • String[] split(String regex): 根据给定正则表达式的匹配拆分此字符串。
  • String[] split(String regex, int limit): 根据匹配给定的正则表达式来拆分此字符串。最多不超过limit个,超过的话,剩余全部放在最后一个元素中。

replace() / replaceAll()

    @Test
    public void test6(){
        String str1 = "北京厂面积大";
        String str3 = str1.replace("北京","上海");
        System.out.println(str1);     // 北京厂面积大
        System.out.println(str3);     // 上海厂面积大

        String str = "12hello34world56java78mysql34";
        String string = str.replaceAll("\\d+",",").replaceAll("^,|,$","");
        System.out.println(string);    // hello,world,java,mysql 
    }

matches() / split()

    @Test
    public void test7(){
        String str = "123456";
        boolean matches = str.matches("\\d+");
        System.out.println(matches);  // true
        String tel = "0571-54789999";
        boolean matches1 = tel.matches("0571-\\d{7,8}");
        System.out.println(matches1);  //true
    }

    @Test
    public void test8(){
        String str = "abc|def|ghi";
        String[] strs = str.split("\\|");
        for(int i =0;i < strs.length; i++){
            System.out.println(strs[i]);
        }
        String str2 = "abc.def.ghi";
        String[] strs2 = str2.split("\\.");
        for(int i =0;i < strs2.length; i++){
            System.out.println(strs2[i]);
        }
    }

(1.1.5)String 与基本数据类型、数组转换

  • 字符串 -> 基本数据类型、包装类
    • Integer包装类的public static int parseInt(String s): 可以将由"数字"组成的字符串转换成整型。
    • 类似地,使用java.lang包中的Byte、Short、Long、Float、Double类使用相应的方法,将字符串转换为对应基本数据类型。
  • 基本数据类型、包装类 -> 字符串
    • 调用String类的 public String valueOf(int n) 可将int型转换为字符串。
    • 相应的valueOf(byte b)、valueOf(long l)、valueOf(float f)、valueOf(double d)、valueOf(boolean b)可进行由数据类型到字符串的转换
    • static String valueOf(primitive data type x): 返回给定data type类型x参数的字符串表示形式。
  • 字符串 <-> 字符数组
    • char[] toCharArray():  将此字符串转换为一个新的字符数组。
    • 数组转字符串:  调用String的构造器
  • 字符串 <-> 字节数组(编码、解码)
    • 字符串转数组:  调用String的getBytes();字符集
    • 数组转字符串:  调用String的构造器
parseInt() / valueOf() / toCharArray()
    @Test
    public void test(){
        String str1 = "123";
        int num = Integer.parseInt(str1);
        String str2 = String.valueOf(num);

        String str3 = "abc123";
        char[] charArray = str3.toCharArray();    // String -> char[]
        for (int i = 0; i<charArray.length; i++){
            System.out.println(charArray[i]);
        }
        
        char[] arr = new char[]{'h','e','l','l','o'};
        String str4 = new String(arr);           // char[] -> String
        System.out.println(str4);
    }

getBytes() / Arrays.toString()

    @Test
    public void test3(){
        String str1 = "abc123";
        byte[] bytes = str1.getBytes();
        System.out.println(Arrays.toString(bytes));  // [97, 98, 99, 49, 50, 51]
    }

getBytes(字符集) 

    @Test
    public void test3() throws UnsupportedEncodingException {
        String str1 = "abc123中国";
        byte[] bytes = str1.getBytes();  // 使用默认的字符集进行转换, 字符串-> 二进制 【utf8 一个汉字三个字节; gbk 一个汉字两个字节
System.out.println(Arrays.toString(bytes));
//[97, 98, 99, 49, 50, 51, -28, -72, -83, -27, -101, -67]
byte[] gbks = str1.getBytes("gbk");
System.out.println(Arrays.toString(gbks)); //[97, 98, 99, 49, 50, 51, -42, -48, -71, -6]

String str2 = new String(bytes); // 使用默认的字符集进行解码, 二进制 -> 字符串
System.out.println(str2);
String str3 = new String(gbks,"gbk"); // 不指定字符集,编码和解码字符集不一致,导致乱码
System.out.println(str3);
    }

字符串拼接比较

    @Test
    public void test4(){
        String s1 = "javaEESpring";
        String s2 = "javaEE";
        String s3 = s2 + "Spring";
        System.out.println(s1 == s3);  //false

        final String s4 = "javaEE";  // 常量
        String s5 = s4 + "Spring";
        System.out.println(s1 == s5);  //true
    }

(1.1.6)字符串是如何存储的

JDK 1.6(JDK 6.0, Java 6.0): 字符串常量池存储在方法区(永久区)

JDK 1.7: 字符串常量池存储在 堆空间

JDK 1.8: 字符串常量池存储在方法区(元空间)

(1.2)StringBuffer、StringBuilder

(1.2.1)String、StringBuffer、StringBuilder三者的异同

  • String:        不可变的字符序列;底层使用char[]存储
  • StringBuffer:  可变的字符序列;线程安全,效率低;底层使用char[]存储
  • StringBuilder: 可变的字符序列;jdk5.0新增,线程不安全,效率高。底层使用char[]存储
  • String 与 StringBuffer、StringBuilder 转换: 构造器
  • 都是final类,都不允许被继承;
  • String长度是不可变的,StringBuffer和StringBuilder长度是可变的。
  • StringBuffer是线程安全的,StringBuilder是线程不安全的,但他们两个中的所有方法都是相同的,StringBuffer在StringBuilder的方法之上加了synchronized修饰,保证线程安全。
  • StringBuilder比StringBuffer拥有更好的性能。
  • 如果一个String类型的字符串,在编译时就可以确定是一个字符串常量,则编译完成之后,字符串会自动拼接成一个常量。此时String的速度比StringBuilder和StringBuffer的性能更好。

 

        String s = new String();
        StringBuilder stringBuilder = new StringBuilder(s);
        StringBuffer stringBuffer = new StringBuffer(s);
        s = new String(stringBuilder);
        s = new String(stringBuffer);

 

    @Test
    public void test(){
        StringBuffer sb1 = new StringBuffer("abc");
        sb1.setCharAt(0,'m');
        System.out.println(sb1);  // mbc
    } 

 

    @Test
    public void test4(){
        String str = null;
        StringBuffer sb = new StringBuffer();
        sb.append(str);
        System.out.println(sb.length());  //4
        System.out.println(sb);           //"null"
        StringBuffer sb1 = new StringBuffer(str);
        System.out.println(sb1);          //java.lang.NullPointerException
    }

 

 

 

(1.2.2)StringBuffer源码分析

    @Test
    public void test1(){
        String str = new String();  // char[] value = new char[0];
        String str1 = new String("abc");// char[] value = new char[]{'a','b','c'};

        StringBuffer sb1 = new StringBuffer();// char[] value = new char[16]; 底层创建一个长度为16的字符数组
        sb1.append('a');// value[0] = 'a';
        sb1.append('b');// value[1] = 'b';
        System.out.println(sb1.length());// 2   sb1.capacity(); 16
        StringBuffer sb2 = new StringBuffer("abc");// char[] value = new char["abc".length() + 16]
        System.out.println(sb2.length());// 3   sb2.capacity(); 19
    }

StringBuffer源码 

 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
    public StringBuffer() {
        super(16);
    }

    public StringBuffer(String str) {
        super(str.length() + 16);
        append(str);
    }

    @Override
    public synchronized int length() {
        return count;
    }

    @Override
    public synchronized int capacity() {
        return value.length;
    }

}

扩容问题: 底层数组扩容: 默认情况下,扩容为原来容量的2倍+2, 同时将原有数组的元素复制到新的数组中。

abstract class AbstractStringBuilder implements Appendable, CharSequence {

    public void ensureCapacity(int minimumCapacity) {
        if (minimumCapacity > 0)
            ensureCapacityInternal(minimumCapacity);
    }

    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }

    private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int newCapacity = (value.length << 1) + 2;
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
            ? hugeCapacity(minCapacity)
            : newCapacity;
    }

    private int hugeCapacity(int minCapacity) {
        if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
            throw new OutOfMemoryError();
        }
        return (minCapacity > MAX_ARRAY_SIZE)
            ? minCapacity : MAX_ARRAY_SIZE;
    }
}
(1.2.3)StringBuffer类常用方法
  • StringBuffer append(String s): 将指定的字符串追加到此字符序列。
  • StringBuffer reverse(): 将此字符序列用其反转形式取代。
  • StringBuffer delete(int start, int end): 移除此序列的子字符串中的字符。
  • StringBuffer insert(int offset, xxx): 在指定位置插入xxx。
  • StringBuffer replace(int start, int end, String str): 使用给定 String 中的字符替换此序列的[start,end)子字符串中的字符。

> 当append和insert时, 如果原来value数组长度不够,可扩容。

> 如上这些方法支持方法链操作。

> 方法链原理:

    @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }
  • public int indexOf(String str)
  • public String substring(int start, int end): 返回一个从start开始到end结束的左闭右开区间的子字符串。
  • public int lenth()
  • public char charAt(int n)
  • public void setCharAt(int n, char ch)
    @Test
    public void test2(){
        StringBuffer sb1 = new StringBuffer("abc");
        sb1.append(1);
        sb1.append('2');
        System.out.println(sb1);     // abc12
//        sb1.delete(2,4);           // ab2
//        sb1.replace(2,4,"hello");  // abhello2
//        sb1.insert(2,false);       // abfalsec12
//        sb1.reverse();             // 21cba
        String s2 = sb1.substring(1,3); //bc
        System.out.println(sb1.length());  //5
    }

String、StringBuffer、StringBuilder 性能测试

 @Test
    public void test3(){
        long startTime = 0L;
        long endTime = 0L;

        String text = "";
        StringBuffer buffer = new StringBuffer("");
        StringBuilder builder = new StringBuilder("");

        startTime = System.currentTimeMillis();
        for (int i=0; i<20000; i++){
            buffer.append(String.valueOf(i));
        }
        endTime = System.currentTimeMillis();
        System.out.println("StringBuffer执行时间:" + (endTime - startTime));

        startTime = System.currentTimeMillis();
        for (int i=0; i<20000; i++){
            builder.append(String.valueOf(i));
        }
        endTime = System.currentTimeMillis();
        System.out.println("StringBuilder执行时间:" + (endTime - startTime));

        startTime = System.currentTimeMillis();
        for (int i=0; i<20000; i++){
            text = text + i;
        }
        endTime = System.currentTimeMillis();
        System.out.println("String执行时间:" + (endTime - startTime));

    }
从高到低:  StringBuilder > StringBuffer > String
StringBuffer执行时间:5
StringBuilder执行时间:4
String执行时间:1474

 

(1.3)场景题目

1. 模拟一个trim方法,去除字符串两端的空格。

2. 将一个字符串进行反转。将字符串中指定部分进行反转。如"abcdefg" 反转为"abfedcg"。

    //将一个字符串进行反转。将字符串中指定部分进行反转。如"abcdefg" 反转为"abfedcg"。
    public String reverse(String str, int startIndex, int endIndex){
        char[] c = str.toCharArray();
        int mid = (endIndex - startIndex) / 2 + 1;
        for(int i = 0; i< mid ; i ++){
            char tmp = c[endIndex-i];
            c[endIndex - i] = c[startIndex+i];
            c[startIndex + i] = tmp;
        }
        return new String(c);
    }

    @Test
    public void testReverse(){
        String str = "abcdefg";
        String newStr = reverse(str,1,5);
        System.out.println("原字符串:" + str);        //原字符串:abcdefg
        System.out.println("新字符串:" + newStr);     //新字符串:afedcbg
    }
        for(int x = startIndex,y=endIndex; x<y; x++,y--){
            char tmp = c[y];
            c[y] = c[x];
            c[x] = tmp;
        }
    // 使用String 拼接; 
    public String reverse1(String str, int startIndex, int endIndex){
        if(str != null){
            String newStr = str.substring(0,startIndex);
            for(int i = endIndex; i>= startIndex; i--){
                newStr += str.charAt(i);
            }
            newStr += str.substring(endIndex +1);
            return newStr;
        }
        throw new RuntimeException("字符串为空");
    }

 

    //使用StringBuffer/StringBuilder替换String
    public String reverse2(String str, int startIndex, int endIndex){
        if(str != null){
            StringBuilder builder = new StringBuilder(str.length());
            builder.append(str.substring(0,startIndex));
            for(int i = endIndex; i>= startIndex; i--){
                builder.append(str.charAt(i)) ;
            }
            builder.append(str.substring(endIndex +1));
            return builder.toString();
        }
        throw new RuntimeException("字符串为空");
    }

3. 获取一个字符串在另一个字符串中出现的次数。如:获取"ab"在"abkkcadabkebkskab"中出现的次数。

    public int subCount(String str, String subStr){
        int subLen = subStr.length();
        int count=0;
        for(int i=0; i< str.length(); ){
            if(i+subLen > str.length()){
                break;
            }
            if(str.substring(i,i+subLen).equals(subStr)){
                count++;
                i = i + subLen;
            }else {
                i++;
            }
        }
        return count;
    }

    @Test
    public void test(){
        String str = "aabcabrwasdaab";
        String substr = "ab";
        int count = subCount(str, substr);
        System.out.println(count);
    }
    public int subCount2(String mainStr, String subStr){
        int mainLength = mainStr.length();
        int subLength = subStr.length();
        int count = 0;
        int index;
        if(mainLength >= subLength){
            while((index = mainStr.indexOf(subStr)) != -1){
                count++;
                mainStr = mainStr.substring(index + subLength);
            }
            return count;
        }
        return count;
    }
            while((index = mainStr.indexOf(subStr, index)) != -1){
                count++;
                index += subLength;
            }

 

4. 获取两个字符串中最大相同子串。如: str1="abcwethyuiodef" str2="chyuodef"

思路: 将短的那个串进行长度依次递减的子串与较长的串比较。

    //获取两个字符串中最大相同子串。如: str1="abcwethyuiodef" str2="chyuodef"
    public String getMaxSubStr(String str1, String str2){
        String maxStr = (str1.length() >= str2.length()) ?  str1 : str2;
        String minStr = (str1.length() < str2.length()) ?  str1 : str2;
        String maxSubStr = "";

        for(int i = 0; i < minStr.length(); i++){
            for(int j = i; j <= minStr.length(); j++){
                String compareStr = minStr.substring(i,j);
                if(maxStr.contains(compareStr)){
                    maxSubStr = (maxSubStr.length() <= compareStr.length() ? compareStr : maxSubStr);
                }
            }
        }
        return maxSubStr;
    }

    @Test
    public void test(){
        String s1 = "abcdefg1212abc";
        String s2 = "e2abc4d";
        String maxStr = getMaxSubStr(s1,s2);
        System.out.println(maxStr);
    }

字符串长度从len、len-1、len-2......进行查找

    public String getMaxSubStr2(String str1, String str2){
        if(str1 != null && str2 != null){
            String maxStr = (str1.length() >= str2.length()) ?  str1 : str2;
            String minStr = (str1.length() < str2.length()) ?  str1 : str2;
            int length = minStr.length();
            for(int i =0 ; i< length; i++){
                for(int x = 0, y = length -i; y <= length; x++,y++){
                    String subStr = minStr.substring(x,y);
                    System.out.println(subStr);
                    if(maxStr.contains(subStr)){
                        return subStr;
                    }
                }
            }
        }
        throw new RuntimeException("字符串为空");
    }

结果:

e2abc4d
e2abc4
2abc4d
e2abc
2abc4
abc4d
e2ab
2abc
2abc

多个相同长度的最大相同子串

   // 如果存在多个长度相同的最大相同子串,返回String[]
    public String[] getMaxSubStr3(String str1, String str2){
        if(str1 != null && str2 != null){
            StringBuffer buffer = new StringBuffer();
            String maxStr = (str1.length() >= str2.length()) ?  str1 : str2;
            String minStr = (str1.length() < str2.length()) ?  str1 : str2;
            int length = minStr.length();
            for(int i =0 ; i< length; i++){
                for(int x = 0, y = length -i; y <= length; x++,y++){
                    String subStr = minStr.substring(x,y);
//                    System.out.println(subStr);
                    if(maxStr.contains(subStr)){
                        buffer.append(subStr + ",");
                    }
                }
                if(buffer.length() != 0){
                    break;
                }
            }
            String[] split = buffer.toString().replaceAll(",$","").split("\\,");
            return split;
        }
        throw new RuntimeException("字符串为空");
    }

 

5. 对字符串中字符进行自然排序。

思路:1) 字符串变成字符数组。 2) 对数组排序,选择,冒泡,Arrays.sort(); 3) 将排序后的数组变成字符串。