String类

1. String 的常用方法

        String是一个引用类型遍历,和之前我们创建的类(自定义的引用类型)一样

        1.1 字符串的构造

        常用的三种构造方式

        1>  直接构造

        2> 通过new关键字构造

        3> 使用字符数组进行构造

public class Test {

    public static void main1(String[] args) {
        //TODO 1.字符串构造:
        //String 是一个引用类型变量,和之前我们创建的类(自建的引用类型)
        String string = "xiabo";//这个是简写
        String string1 = new String("world");//这个是完整写法
        char[] array = {'a','b','c'};//字符数组
        String string2 = new String(array);//把字符数组合并成一个字符串
        System.out.println(string2);//因为String类重写了toString方法,那么就可以直接输出值而不是地址
        //TODO 对比字符串和普通类型的数组
        int[] a = {1,2,3,4};
        System.out.println(a.length);
        System.out.println(string.length());


    }
}

【注意】

1. String是引用类型,内部并不存储字符串本身,在String类的实现源码中,String类实例变量如下
 由图可见,String里面有俩个东西,value和hash

           获取字符串长度:字符串对象.length()

           判断字符串是否为空: 字符串对象.isEmpty()

public class Test {

    public static void main(String[] args) {
        //TODO 判断字符串是否是空的
        String str = "hello";
        System.out.println(str.isEmpty());//这个是判断字符串是不是空的
        System.out.println("==========");
        String str2 = "";
        System.out.println(str2.isEmpty());
        String str1 = null;//这个表示 str这个引用不指向任何对象
        //还可以这么求字符串长度
        System.out.println("hello".length());

    }
}
//运行结果
false
==========
true
5

注意:  

String str1 = null; 这个表示 str这个引用不指向任何对象

而String str2 = "";这个是指向一片内存里面什么都没有的对象

 在Java中“”引起来的也是String类型对象。

System.out.println("hello".length());
 

        再次申明String是引用类型:

public static void main(String[] args) {
// s1和s2引用的是不同对象 s1和s3引用的是同一对象
    String s1 = new String("hello");
    String s2 = new String("world");
    String s3 = s1;
    System.out.println(s1.length()); // 获取字符串长度---输出5
    System.out.println(s1.isEmpty()); // 如果字符串长度为0,返回true,否则返回false
}

以下是具体的图:value里面的值其实就是指向一个字符数组的地址,hash是保存的位置信息  

    1.2 String对象的比较

        java中String提供了四种比较方法

        1>  == 比较是否引用同一个对象(对于内置类型,==比较的是变量中的值;对于引用类型==比较的是引用中的地址。

    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        int c = 10;
// 对于基本类型变量,==比较两个变量中存储的值是否相同
        System.out.println(a == b); // false
        System.out.println(a == c); // true
// 对于引用类型变量,==比较两个引用变量引用的是否为同一个对象
        String s1 = new String("hello");
        String s2 = new String("hello");
        String s3 = new String("world");
        String s4 = s1;
        System.out.println(s1 == s2); // false
        System.out.println(s2 == s3); // false
        System.out.println(s1 == s4); // true
    }

String,包装类,自定义类型都是引用类型,其他都是基本数字类型

        2> 通过boolean equals(Object anObject) 方法来比较里面的值是否相等(按照字典序比较,字符大小写)

        因为String重写了Object中的equals方法,所以可以进行值的对比

  public static void main(String[] args) {
        String s1 = new String("hello");
        String s2 = new String("hello");
        String s3 = new String("Hello");
// s1、s2、s3引用的是三个不同对象,因此==比较结果全部为false
        System.out.println(s1 == s2); // false
        System.out.println(s1 == s3); // false
// equals比较:String对象中的逐个字符
// 虽然s1与s2引用的不是同一个对象,但是两个对象中放置的内容相同,因此输出true
// s1与s3引用的不是同一个对象,而且两个对象中内容也不同,因此输出false
        System.out.println(s1.equals(s2)); // true
        System.out.println(s1.equals(s3)); // false
}

我们也可以来看看String里面的equals方法的源码 

if (this == anObject) 表示当前对象和anOject是否是引用同一个对象

if (anObject instanceof String)表示检测anObject是否是String类型的对象,是就向下转型

if (n == anotherString.value.length)比较字符串长度

while (n-- != 0) { if (v1[i] != v2[i])

return false; i++;} 这个是按照字典序排序进行比较

        3> 通过int compareTo(String s)方法:按照字典序进行比较

        这个和刚刚equals不同的是 compareTo返回的是一个int(表示差值),而equals是返回true或者false.具体的比较方法:

        1. 先按照字典次序大小比较,如果出现不等的字符,直接返回这两个字符的大小差值

        2. 如果前k个字符相等(k为两个字符长度最小值),返回值两个字符串长度差值

  public static void main(String[] args) {
        String s1 = new String("abc");
        String s2 = new String("ac");
        String s3 = new String("abc");
        String s4 = new String("abcdef");
        System.out.println(s1.compareTo(s2)); // 不同输出字符差值-1
        System.out.println(s1.compareTo(s3)); // 相同输出 0
        System.out.println(s1.compareTo(s4)); // 前k个字符完全相同,输出长度差值 -3
    }

        4> int compareToIgnoreCase(String str)这个是在3>的基础上忽略大小写比较

   public static void main(String[] args) {
        String s1 = new String("abc");
        String s2 = new String("ac");
        String s3 = new String("ABc");
        String s4 = new String("abcdef");
        System.out.println(s1.compareToIgnoreCase(s2)); // 不同输出字符差值-1
        System.out.println(s1.compareToIgnoreCase(s3)); // 相同输出 0
        System.out.println(s1.compareToIgnoreCase(s4)); // 前k个字符完全相同,输出长度差值 -3
    }

接下来是总的放在一起进行对比:

 public static void main(String[] args) {
        //TODO 3.字符串对象的比较
        String str1 = new String("abcD");
        String str2 = new String("abcd");
        System.out.println(str2 == str1);//因为地址不一样所以是false
        //通过 equals来比较值一不一样
        System.out.println(str1.equals(str2));//这个调用的是String继承Object重写的equals方法
        System.out.println("=========");
        String str3 = "abcd";
        String str4 = "abcd";
        System.out.println(str3 == str4);//字符串常量池(存的是双引号引起来的字符串)会解释,堆中会有一个常量池,里面会保存没有的值,如果另一个变量也是这个值,就会把第一个常量池的值的地址给那个变量,因此地址就是一样的
        //s2 > s1 则返回正数
        //s2 = s1 则返回0
        //s2 < s1 则返回负数
        System.out.println(str2.compareTo(str1));
        //忽略大小写进行比较(验证码)
        System.out.println(str1.compareToIgnoreCase(str2));

    }
//运行结果:
false
false
=========
true
32
0

 注意:

System.out.println(str3 == str4);字符串常量池(存的是双引号引起来的字符串),表示堆中会有一个常量池,里面会保存没有的值,如果另一个变量在常量池里面发现也是这个值,就会把第一个常量池的值的地址给那个变量,因此地址就是一样的.

        1.3 字符串的查找

        

public class Test {

    public static void main(String[] args) {
        //TODO 4.字符串查找
        //通过下标获取字符
        String s1 = "hello";
        char ch = s1.charAt(1);//获得1下标的值
        System.out.println(ch);
        //通过字符获取下标
        int index = s1.indexOf('l');//默认第一次出现的l
        System.out.println(index);
        int index1 = s1.indexOf('l',3);//从3下标开始
        System.out.println(index1);
        //也可以查询子串在主串的位置(kmp算法)
        String s2 = "ababcabcd";
        int index3 = s2.indexOf("abc",3);
        System.out.println(index3);
        //倒着找
        int index4 = s2.lastIndexOf("abc",4);//从4下标开始往后找
        System.out.println(index4);

    }
}
//结果
e
2
3
5
2

 char ch = s1.charAt(1);表示获取s1里面1下标的值

 int index = s1.indexof('l');表示获取第一次出现的'l'下标

 int index = s1.indexof('l',3);表示从s1的3下标开始寻找'l'的下标

 int index3 = s2.indexof("abc",3);表示查询从3下标开始往后找"abc"的子串

 int index4 = s3.lastIndexof("abc",4);表示从4小标开始从后往前找"abc"的子串

        1.4 转换

        其他类型的数值和字符串之间的相互转换

        1> 其他类型的数值->字符串

                我们使用String.valueof()来进行其他类型的数组到字符串之间的转换

这是不同的重载方法

  public static void main(String[] args) {
// 其他类型转字符串
        String s1 = String.valueOf(1234);
        String s2 = String.valueOf(12.34);
        String s3 = String.valueOf(true);
        String s4 = String.valueOf(new Student("Hanmeimei", 18));
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s3);
        System.out.println(s4);
        System.out.println("=================================");
    }

        2> 字符串->其他数值:

        我们通过 包装类型.parseInt()来实现字符串转换为其他数值的转换

  public static void main(String[] args) {
/ 字符串转数字
// 注意:Integer、Double等是Java中的包装类型,这个后面会讲到
        int data1 = Integer.parseInt("1234");
        double data2 = Double.parseDouble("12.34");
        System.out.println(data1);
        System.out.println(data2);
    }

        3> 字符串大小写转换          

   public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "HELLO";
// 小写转大写
        System.out.println(s1.toUpperCase());
// 大写转小写
        System.out.println(s2.toLowerCase());
    }

        4> 字符串和字符数组的转换

    public static void main(String[] args) {
        String s = "hello";
// 字符串转数组
        char[] ch = s.toCharArray();
        for (int i = 0; i < ch.length; i++) {
            System.out.print(ch[i]);
        } 
        System.out.println();
// 数组转字符串
        String s2 = new String(ch);
        System.out.println(s2);
    }

        5> 格式化输出         

public static void main(String[] args) {
        String s = String.format("%d-%d-%d", 2019, 9,14);
        System.out.println(s);
    }

以下是弄在一起做对比:

 

public class Test {
    public static void main(String[] args) {
        //TODO 5.其他值和字符串的转换
        //数值转换成字符
        String s = String.valueOf(19.9);
        System.out.println(s);
        //字符串转化为其他值(也是引用类型)
        int data = Integer.parseInt("198");
        System.out.println(data);
        //字符串大小写转换
        String s1 = "hello";
        String ret = s1.toUpperCase();
        System.out.println(ret);//转变为大写,是一个新的对象不是在原来上改变
        String s2 = "Hello";
        ret = s2.toLowerCase();
        System.out.println(ret);
        //字符串转数组
        char[] array = s1.toCharArray();
        System.out.println(Arrays.toString(array));
        //数组转字符串
        char[] array1 = {'a','b','c'};
        String str1 = new String(array1);
        System.out.println(str1);
        //格式化输出
        s = String.format("%d-%d-%d",2019,9,29);
        System.out.println(s);

    }
    
}
//运行结果
19.9
198
HELLO
hello
[h, e, l, l, o]
abc
2019-9-29

        总结:

        string.valueOf(其他类型的值);这个可以实现把其他类型的值转化为字符串

        Interger.pasrseInt("整形数值");这个可以把字符串的值转为包装类型Interger

        字符串名字.toUpperCase();表示把该字符串里面的值转换为大写

        字符串名字.toLowerCase();表示把该字符串里面的值转换为小写

        char[] array = s1.toCharArray()表示把字符串转化为字符数组

        char[] array1 = {'a','b','c'};String str1 = new String(array1);通过字符数组构造字符串

        String.formate("%d-%d-%d",2000,10,20);表示格式化输出年月日

        1.5 字符串的替换

                        用一个指定的新字符串替换已经有的字符串数据

方法功能
String replaceAll(被替换字符,用来替换的字符)替换所有的指定内容
String replaceFirst(被替换字符,用来替换的字符)替换首个内容

public class Test{
    public static void main(String[] args) {
        //TODO 字符串的替换
        String str = "ababcabcabababa";
        String ret = str.replace("ab","999");//所有的String操作都是产生一个新的对象
        System.out.println(ret);
        ret = str.replace('a','8');//只能替换单个,不能替换成多个
        System.out.println(ret);
        //替换第一个
        ret = str.replaceFirst("ab","ooo");
        System.out.println(ret);
        //替换所有的
        ret = str.replaceAll("ab","oo");
        System.out.println(ret);

    }
}
//运行结果
999999c999c999999999a
8b8bc8bc8b8b8b8
oooabcabcabababa
oooocoocooooooa

        总:

        str.replace('a','8');这个只能替换单个字符

        str.replace("ab","889");这个是字符串的替换

        str.replaceFirst("ab","ooo");这个是替换第一个

        str.replaceAll("ab","pp");这个表示替换的是所有的

        1.6 字符串拆分

                可以将一个完整的字符串按照指定的分隔符划分为若干个子字符串

方法功能
String[] split(以申明符号拆分)把字符串全部拆分
String[] split(以这个符号拆分,拆分成几个)把字符串以指定格式,拆分成limit组
publif class Test{
public static void main(String[] args) {
        //TODO 字符串的拆分
        String str = "hello abc world";
        String[] ret = str.split(" ");//以空格来分割
        System.out.println(ret);//这个打印的是地址
        for (int i = 0; i < ret.length; i++) {
            System.out.println(ret[i]);
        }
        System.out.println("==========");
         ret = str.split(" ",2);//以空格来分割,分成俩份
        for (int i = 0; i < ret.length; i++) {
            System.out.println(ret[i]);
        }
        System.out.println("==========");

        String s = "192.168.1.1";//以.等特殊符号,我们需要用转义字符
        ret = s.split("\\.");// \.才是一个点,\\.表示转义
        for (int i = 0; i < ret.length; i++) {
            System.out.println(ret[i]);
        }
        System.out.println("==========");
        s = "192\\168\\1\\1";//以.等特殊符号,我们需要用转义字符
        ret = s.split("\\\\");//以俩个斜杠来分割
        for (int i = 0; i < ret.length; i++) {
            System.out.println(ret[i]);
        }
        System.out.println("==========");
        s = "name=zhangsan&age=15";
        ret = s.split("=|&");//用|来实现多个分隔符的连接
        for (int i = 0; i < ret.length; i++) {
            System.out.println(ret[i]);
        }
        System.out.println("==========");
         ret = s.split("&");//等价为上面
        for (int i = 0; i < ret.length; i++) {
            String x = ret[i];
            String[] ret2 = x.split("=");//可以在第一次的基础上进行第二次分割
            for (int j = 0; j < ret2.length; j++) {
                System.out.println(ret2[j]);
            }

        }

    }
}
//运行结果:
[Ljava.lang.String;@1540e19d
hello
abc
world
==========
hello
abc world
==========
192
168
1
1
==========
192
168
1
1
==========
name
zhangsan
age
15
==========
name
zhangsan
age
15

        总: 

1. 字符"|","*","+"都得加上转义字符,前面加上 "\\" .

2. 而如果是 "\\" ,那么就得写成 "\\\\" .

3. 如果一个字符串中有多个分隔符,可以用"|"作为连字符

        str.split(" ");这个是以空格来分割

        分割出来的东西是个字符数组

        str.split(" ",2);这个是一空格来分割,分成俩份

        s.tr.split("\\.");\.才表示一个点,\\.表示转义

        s.split("\\\\");表示以俩个斜杠来分割

        s.split("=|&'');表示以等号和取地址符来进行分割,|实现多个分隔符的连接

        拆分后的结果还是可以再进行一轮拆分:

        1.7  字符串截取

方法功能
String substring(开始截取的地方)从指定索引截取导末尾
String substring(截取开始地方,截取结束地方)截取部分内容
public class Test {
   public static void main(String[] args) {
        //TODO 截取字符串
        String str = "ababc";
        String ret = str.substring(0,3);//[0,3)
        System.out.println(ret);
        System.out.println("====");
        ret = str.substring(2);//从2下标开始
        System.out.println(ret);
        System.out.println("====");

    }
}
//运行结果:
aba
====
abc
====

        注意事项:

        1. 索引从0开始

        2. 注意前闭后开区间的写法, substring(0, 5) 表示包含 0 号下标的字符, 不包含 5 号下标

        str.substring(0,3);表示截取的是[0,3)区间的字符串

        str.substring(2);表示从2下标开始截取到末尾

        1.8 取出左右俩边空格

        我们使用String trim();去掉字符串开头和结尾的空白字符(空格, 换行, 制表符等)
 

public class Test {
public static void main(String[] args) {
        //TODO 其他方法
        String ret;
        String str2 = "  sdfdsafd asffadg  ";
        System.out.println(str2);
        ret = str2.trim();
        System.out.println(ret);//去掉左右空格,中间无法去掉
    }
}
//运行结果:
  sdfdsafd asffadg  
sdfdsafd asffadg

        1.9 字符串的不可变性

                学习了上述字符串的一些常用方法之后,我们发现一个特点,也就是每次修改字符串的操作都不会修改最开始的那个字符串对象,而是修改之后返回一个修改后的新对象.这说明了String是一种不可变对象.字符串的内容是不可以被改变的,这是因为:

        1.String类在设计的时候就是不可改变的,String类是被final修饰的,里面的value值也是被final修饰的,又没有提供set或者get方法来获得里面的值,因此我们修改不了里面的value

        2. 所有涉及到可能修改字符串内容的操作都是创建一个新对象,改变的是新对象

比如 replace 方法:

        final修饰类表明该类不想被继承,final修饰引用类型表明该引用变量不能引用其他对象,但是其引用对象中的内容是可以修改的。
        public static void main(String[] args) { final int array[] = {1,2,3,4,5};

        array[0] = 100; System.out.println(Arrays.toString(array))

        // array = new int[]{4,5,6}; // 编译报错:Error:(19, 9) java: 无法为最终变量array分配值

}

2.StringBuilder和StringBuffer

        引入: 我们看一个代码,关于String,StringBuilder和StringBuffer进行多次拼接操作的计时比较.

public class Test {
 public static void main(String[] args) {
        long start = System.currentTimeMillis();
        String s = "";
        for(int i = 0; i < 10000; ++i){
            s += i;//每次拼接会产生一个新的对象,所以会耗时
        }
        long end = System.currentTimeMillis();
        System.out.println("String: "+(end - start));
        start = System.currentTimeMillis();
        StringBuffer sbf = new StringBuffer("");//StringBuffer里面的append方法是被sychronized修饰的保证线程安全的方法,所有会比StringBuilder慢

        for(int i = 0; i < 10000; ++i){
            sbf.append(i);//直接在这个对象的后面拼接
        }
        end = System.currentTimeMillis();
        System.out.println("StringBuffer: "+(end - start));
        start = System.currentTimeMillis();
        StringBuilder sbd = new StringBuilder();//不是线程安全的方法,所以快一点

        for(int i = 0; i < 10000; ++i){
            sbd.append(i);
        }
        end = System.currentTimeMillis();
        System.out.println("StringBuilder: "+(end - start));

    }
}
//
String: 254
StringBuffer: 1
StringBuilder: 0

 在这个代码中我们分别堆这三个类型对字符串进行拼接操作并且计算他们的计算时间

String 的拼接操作,每次拼接都会产生一个新的对象,因此在不停的进行产生对象和销毁对象的操作,因此耗时最大.

StringBuffer和StringBuilder一直都是在一个对象里面进行操作

但是俩者的区别就是StringBuffer里面多了个sychronized关键字,这代表着线程安全,也就涉及了加锁和解锁的操作,这也会消耗时间,因此在效率上SringBuffer小于StringBuilder但是在安全性上StringBuffer大于StringBuilder.

        2.1 StringBuilder的介绍

        由于String的不可更改特性,为了方便字符串的修改,Java中又提供StringBuilder和StringBuffer类。这两个类大部分功能是相同的,这里介绍 StringBuilder常用的一些方法,

 这个是具体的代码:

public static void main(String[] args) {
            StringBuilder sb1 = new StringBuilder("hello");
            StringBuilder sb2 = sb1;
        // 追加:即尾插-->字符、字符串、整形数字
        sb1.append(' '); // hello
        sb1.append("world"); // hello world
        sb1.append(123); // hello world123
        System.out.println(sb1); // hello world123
        System.out.println(sb1 == sb2); // true
        System.out.println(sb1.charAt(0)); // 获取0号位上的字符 h
        System.out.println(sb1.length()); // 获取字符串的有效长度14
        System.out.println(sb1.capacity()); // 获取底层数组的总大小
        sb1.setCharAt(0, 'H'); // 设置任意位置的字符 Hello world123
        sb1.insert(0, "Hello world!!!"); // Hello world!!!Hello world123
        System.out.println(sb1);
        System.out.println(sb1.indexOf("Hello")); // 获取Hello第一次出现的位置
        System.out.println(sb1.lastIndexOf("hello")); // 获取hello最后一次出现的位置
        sb1.deleteCharAt(0); // 删除首字符
        sb1.delete(0,5); // 删除[0, 5)范围内的字符
        String str = sb1.substring(0, 5); // 截取[0, 5)区间中的字符以String的方式返回
        System.out.println(str);
        sb1.reverse(); // 字符串逆转
        str = sb1.toString(); // 将StringBuffer以String的方式返回
        System.out.println(str);
    }

        从上述例子可以看出:String和StringBuilder最大的区别在于String的内容无法修改,而StringBuilder的内容可以修改。频繁修改字符串的情况考虑使用StringBuilder。

注意:

String和StringBuilder类不能直接转换。如果要想互相转换,可以采用如下原则:

String变为StringBuilder: 利用StringBuilder的构造方法或append()方法

StringBuilder变为String: 调用toString()方法

        2.2 相关的面试题

         1. String、StringBuffer、StringBuilder的区别

        String的内容不可修改,StringBuffer与StringBuilder的内容可以修改. StringBuffer与StringBuilder大部分功能是相似的

        StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值