JAVA学习记录之String类的常用方法

在编写程序的过程中,不了避免的要用到字符串,所以String类的常用方法的用法是必须掌握的。学习一个类的使用方法最好的措施就是读JAVA类库
的官方文档和阅读源码。jdk1.8 api谷歌翻译中文版下载地址

JDK api 1.8中文版.PNG

  • String类的基本概述
  • 从文档中可以知道String类属于java.lang包,所以使用时不用导包,String用final修饰,故不能有子类,继承了Object类,实现了接口Serializable,Comparable<String>,CharSequence
  • String类代表字符串。 Java程序中的所有字符串文字(例如"abc" )都被实现为此类的实例。字符串不变; 它们的值在创建后不能被更改。 字符串缓冲区支持可变字符串。 因为String对象是不可变的,它们可以被共享。即String str = "abc","abc"就是String的一个对象。而且"abc"不能改变,即使对str再次赋值str = "def",也只是将str指向新的字符串,原来的字符串"abc"任然没有改变,并且变成了内存中的垃圾。
  • 字符串常量被放在内存的常量池中。当创建一个字符串常量后,系统会在常量池中查找是否有与该字符串常量字面值相同的字符串,如果没有,就在常量池中创建该常量。
  • Java语言为字符串连接运算符(+)提供特殊支持,并为其他对象转换为字符串。 字符串连接是通过StringBuilder (或StringBuffer )类及其append方法实现的。 字符串转换是通过方法toString来实现。如""+'a'+10将空串""与后面的字符a和整型10相连将变成"a10"。在介绍了后面的String的构造方法后将对这一机制的实现做进一步的描述。
  • 构造方法
  • 字符串常量直接赋值
String str = "abc";
String str2 = "abc";
System.out.println(str == str2);

输出结果是true。正如概述中所说,通过这种方式赋值给字符串变量(引用类型),字符串常量的字面值就是该变量的实例对象。在常量池中创建了"abc"后,对于语句2,发现"abc"已经在常量池中存在了,所以就将该"abc"也赋给了str2,所以str和str2指向的是同一个对象,地址值是相同的。这一过程的内存分配如图。


字符串内存图.PNG
  • 空参构造
    String()初始化新创建的String对象,使其表示空字符序列。
  • 有参构造
    String(String original)
    初始化新创建的String对象,使其表示与参数相同的字符序列;换句话说,新创建的字符串是参数字符串的副本。String s1 = new String("abc");
    这个语句是在常量池中创建了常量"abc",然后在堆内存中创建了一个字符串对象,将"abc"的值拷贝到这个对象中。所以实际上是常见了两个对象,并且将堆内存的对象的地址赋给s1。这一过程的内存图如下。
    字符串内存图2.PNG

    String(byte[] bytes)
    通过使用平台的默认字符集解码指定的字节数组来构造新的String。通过这个方法可以将数组转换成字符串。
byte[] arr = {97,98,99};
String str3 = new String(arr);
System.out.println(str3);               //abc

将字符数组组成字符串,使用的编码是由平台的编码系统决定的。不同于字符的编码,java的字符编码是固定的unicode,而组成字符串是将硬盘中的字节数据转到内存中,将这些字节流进行编码,使之成为我们能够理解的语言,又成为字节流的解码过程,这个涉及到输入输出流的问题。在以后学习后再进一步阐述。String(byte[] bytes, int offset, int length)
这个构造方法可以选择从byte数组中的哪个字节开始,取几个字节构成字符串。

byte[] arr2 = {97,98,99,100};
    String str4 = new String(arr2, 1, 3);
    System.out.println(str4);        //bcd

其实通过源码可以看到,将整个数组转化成字符串的构造方法底层的实现其实就是这个有参构造方法,只是对其进行了封装,使我们的使用更加方便了。

public String(byte bytes[]) {
        this(bytes, 0, bytes.length);
    }

Sting (char[] value,int offset,int count)
将指定的字符数组从offset开始,长度为count的字符元素连缀成字符串。

char[] arr = {'a','b','c'};
String str = new String(arr,1,2);
System.out.println(str);        //bc

以上便是常用的构造方法,对于这一部分,通过一个小练习来加深对字符串概念的理解。

String str = "ab";
String str2 = "abc";
String str3 = str +"c";
System.out.println(str2 == str3);       //false
System.out.println(str2.equals(str3));  //true

前两条语句很好理解,就是在常量池中创建字符串常量"ab"和"abc"并将他们的地址赋给str和str2,对于第三条语句,就是字符串特有的用过+将字符串与其他数据类型拼接成新的字符串。先创建了一个StringBuffer对象,将字符串的值进行拼接,在通过toString方法将StringBuffer转化成String,再将新的String对象的地址赋给str3,所以str2和str3的地址值是不一样,str2对象地址在常量池,而str3对象地址在堆。内存图如图所示。


字符串拼接内存图.PNG
  • 常用方法
  • char charAt(int index):获取字符串中指定位置的字符。字符串的索引与数组的类似,都是从0开始的。这个方法常用来遍历字符串中的元素。
  • int length():返回字符串的长度
    上面两个方法常常用来遍历字符串的元素
String str = "abcd";
for (int i = 0;i<str.length();i++){
        System.out.print(str.charAt(i)+" ");        //a b c d
}
  • boolean equals(String str);判断两个字符串是否相等
  • boolean equalsIngnoreCase(String str):不区分大小写判断两个字符是否相等
  • boolean startWith(String prefix)判断字符串是否以prefix开始
  • boolean starWith(String prefix , int toffset)判断字符串从toffset位置算起,是否以prefix开始
  • boolean endWith(String suffix):判断字符串是否以suffix结尾
  • boolean contain(CharSequence s)判断字符串是否包含子串s
    以上方法的测试代码
String str = "abc";
        String str2 =  "Abc";
        String str3 =  "ab";
        String str4 =  "bc";
        System.out.println(str.equals(str2));           //false
        System.out.println(str.equalsIgnoreCase(str2));//true   不区分大小写
        System.out.println(str.startsWith(str3));       //true
        System.out.println(str.endsWith(str4));         //true
        System.out.println(str.contains(str3));         //true
  • boolean isEmpty()判断字符串是否为空,注意字符串为空串,和字符串引用为空的区别
String str5 = "";
String str6 = null;
System.out.println(str5.isEmpty()+"   "+str.isEmpty());//true false
System.out.println(str6.isEmpty());     //NullPointerException字符串为空不等于空指针

当字符串指针指向为空时,相当于没有指向对象,不能对其调用任何方法,否则会出现空指针异常

  • int indexOf(String str) 返回str子字符串在该字符串中第一次出现的位置
  • int indexOf(String str,int fromIndex) 返回str子字符串在该字符串中从fromIndex开始后第一次出现的位置
  • int indexOf(char ch) 返回ch字符在该字符串中从fromIndex开始扣第一次出现的位置
  • int index(char ch,int fromIndex)返回字符在该字符串中从fromIndex开始后第一次出现的位置
  • int lastIndexOf(String str, int fromIndex)返回从fromIndex开始向前找子字符串str在该字符中第一次出现的位置
  • String substring(int beginIndex, int endIndex)截取从beginIndex开始到endIndex结束的子串,注意的是这个包含头不包含尾,即不包含索引endIndex处的字符
    关于返回字符串索引的位置的方法代码练习
public class Demo_String4 {

    public static void main(String[] args) {
        demo1();
        demo2();
        demo3();
        demo4();
    }

    private static void demo4() {
        String str = "abcd";
        System.out.println(str.substring(2, 4));//cd,从指定位置截取字符串到指定位置结束,包含头不包含尾
    }

    private static void demo3() {
        String str = "abcde";
        System.out.println(str.indexOf("cd", 1)); // 2,,从指定位置往后找
        
        System.out.println(str.lastIndexOf("bc",4)); //1,从指定位置向前找
    }

    private static void demo2() {
        String str = "abcd";
        int x = str.indexOf('b');       //参数是int,传入字符会自动类型提升
        int y = str.indexOf("cd");
        System.out.println(x);          //1
        System.out.println(y);          //2
        System.out.println(str.indexOf("be"));//-1
    }

    private static void demo1() {
        char[] chararry= {'a','b','c'};
        String str = "abc";
        String str2 = "中文字符串";
        
        System.out.println(chararry.length);                    //数组的length是属性
        System.out.println(str.length()+"..."+str2.length());   //字符串的length是方法,获取的是字符的个数,与字符字节数无关
        
        System.out.println(str.charAt(2));  //c
        System.out.println(str.charAt(3));  //StringIndexOutOfBoundsException字符串指针越界异常
    }   
}
  • byte[] getBytes() 将字符串转化成字节数组,这个过程又成为编码过程,将人们理解的语言转化成计算机识别的语言
String str = "abcd";
        byte[] arr = str.getBytes();                //编码:将字符转换成计算机能识别的语言
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
        
        String str2 = "中文字符串";
        byte[] arr2 = str2.getBytes();
        for (int i = 0; i < arr2.length; i++) {
            System.out.print(arr2[i]+" ");  //-42 -48 -50 -60 -41 -42 -73 -5 -76 -82 
                                            //gbk编码,一个中文两个字节,第一个必为负数
        }

对于gbk编码,一个中文由两个字节组成,所以一个中文对应数组中的两个元素,且第一个元素必定为负数。

  • char[] toCharArray()将字符串转化成字符数组,一个字符对应一个元素,且这些字符都是由unicode编码的
  • static String valueOf(char data[])将字符数组转化成字符串,前面提到一个将字符数组转化成字符串的构造方法,其实从源码上看,这个方法就是通过这个构造方法实现的,只不过将其封装起来,就不需要开发者自己去调用这个构造方法,这个看似简单的方法,其实进一步反映了对面对象的思想,就是尽量交由底层去实现,使用者不需自己去实现,只负责通过对象调用这个方法就行了。

通过一个题目了解关于链式编程
将一个字符串的首字母大写,其余变成小写

public static void main(String[] args) {
        String str2 = "zheSHiYIgecesHi";
        System.out.println(str2.substring(0, 1).toUpperCase().concat(str2.substring(1).toLowerCase()));
    }

代码中只通过一行代码就实现了,通过不断掉调用对象的方法实现。只要一个方法返回的是一个对象,就能不断的调用其方法,称之为链式编程。

  • String replace(char oldChar, char newChar)将字符串中oldChar替换为newChar,如果原字符串中不存在oldChar字符,则不进行修改
  • int compareTo(String anotherString)比较两个字符串的大小。如果两个字符串的字符序列相等,则返回0;不相等时,从两个字符第0个字符开始比较,返回第一个不相等的字符差。另一种情况,较长字符串的前面部分恰好是较短额字符串,则返回他们的长度差。
  • String trim()去除字符串两端的空格,常用语账号密码的读取,因为两端的空格肉眼难以看见,如果在录入账号密码时不去除,常常会产生错误。
    public static void main(String[] arg){
        String str = "abcd";
        System.out.println(str.replace('a', 'z'));
        System.out.println(str.replace('c', 'z'));
        
        System.out.println(str.compareTo("abc"));
        
        String str2 = " abc ";
        System.out.println(str2.trim());    //去除两端空格,应用:录入账号密码两端空格难以识别
        
        System.out.println('中'+0);          //unicode码表
        System.out.println('文'+0);          //字符串与字节的转换是系统的码表,从内存到硬盘的编码
    }

以上就是关于String类的常用方法。
今天的学习的一个比较重要的体会是关于面向对象的思想的应用。由于之前一直在用C语言,习惯性的采用面对过程的方法,即很多都自己去实现每一个步骤,而今天发现很多简单的步骤,如调用另一个方法,在java中都是采取封装成一个新的方法的形式,这样使用者就真正的做到,几乎只需要创建对象,在使用对象的方法就行了,而不用总是通过底层的原理实现。第二是关于字节流,渐渐区分了字节流与字符的区别,字符是一种数据类型,而字节流失一连串的字节数据,即一串01数据,通过不同的编码可以表示不同的数据,包括数字,符号,甚至是其他的视频,图像的数据。而字节流的单位通常是自己,即byte,所以getByte方法有称为编码过程,就是将数据转成01数据,以字节为单位存放在数组中。

推荐阅读更多精彩内容