Java疯狂作图之剖释String类之妈见夸之作

Posted 富春山居_ZYY

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java疯狂作图之剖释String类之妈见夸之作相关的知识,希望对你有一定的参考价值。

前言

字符串类型广泛应用在 Java 编程当中,在之前 【Java】数据类型和运算符 这一小节里面有简单介绍过一点 String 类型,但是那不过是 String 类型的相关内容的九牛一毛
在本小节当中将向大家深刻剖析 String 类,了解其创建方法,内存存储,各项基本操作以及深刻理解 StringBuffer 和 StringBuilder 类从而对字符串做出更多的改变。

一、创建字符串

常见的创建字符串的方式:

//方法一
String str1 = "Hello world";
//方法二
String str2 = new String("Hello");
//方法三
char[] array = {'W','o','r','l','d'};
String str3 = new String(array);

内存布局:

💬代码解释:

  • String 是引用类型,其变量是引用变量,存放的是堆里面的一块地址
  • “Hello” 是字符串字面值常量,也是 String 类型
  • 方法一是直接赋值方法,直接让 str1 指向堆里面 “Hello world” 这一字符串
  • 方法二是 String 的构造方法,先实例化一个对象,让该对象指向堆里面“Hello“ 这一字符串
  • 方法三是将一个字符数组转化为一个字符串。向实例化的 String 类里传一个字符数组 array ,调用其中这样类型的构造方法,该构造方法中将 array 数组的内容拷贝了一份,让 String 类里的value 数组接收了新拷贝的内容
  • 一般来说,方法一使用的更加的多

另外,在C语言的同学知道字符串是以’\\0’结尾的,但是在 Java 中并没有这样的说法。

二、字符串常量池

📑代码示例:

String str1 = "hello" ;
String str2 = "hello" ;

System.out.println(str1 == str2);

💬代码解释:

将该代码放入main 函数中,会发现结果为 true

  • String 类型是引用类型,因此使用 == 并不是在比较字符串的内容是否相等,而是在比较 str1 和str2 这两个引用是否指向同一个对象
  • 结果为 true 说明,str1 和 str2 指向的 “Hello” 字符串是同一个 ,并没有在堆中开辟两块不一样的空间分别存放两个 “Hello”

实际上,为了避免每次都创建相同的字符串对象,多余的进行内存的分配,JVM内部对字符串对象的创建做了一定的优化,在堆中有一块区域用来存储字符串,该区域就是字符串常量池。当直接赋值时,字符串内容若在字符串常量池中就直接进行引用,若字符串常量池中没有,则将字符串内容自动保存到字符串常量池中

三、字符串比较相等

📑代码示例:

//例1
String str1 = "Hello";
String str2 = "Hello";
System.out.println(str1 == str2);
//例2
String str3 = new String("Hello");
String str4 = new String("Hello");
System.out.println(str3 == str4);

💬代码解释:

将该代码放入main 函数中,会发现例1结果为 true,例2结果为 false

在字符串常量池这一小节中有说到 == 是在比较 str1 和str2 这两个引用是否指向同一个对象,那么。。。

内存布局:

  • str1 和 str2 采取的是直接赋值法,因此两者指向的是同一个对象,比较之后结果为 true
  • str3 和 str4 采取的是 new String 的方法,相当于在堆中由开辟了两块新的空间来存储 “Hello” 的内容,比较后结果为 false

若真的想要对字符串的内容进行比较,就需要采用 equals 方法

📑代码示例:

String str3 = new String("Hello");
String str4 = new String("Hello");

System.out.println(str3.equals(str4));
//实例:
//System.out.println("Hello".equals(str3));

💬代码解释:

将该代码放入main 函数中,结果为 true

注意:

  • 在使用该方法时一定要确保 str3 不是 null ,如果是就会报空指针异常。但是 str4 可以是 null
  • 用实例的方法来比较 “Hello” 和 str3 的值的内容是否相等是比较好的,因为 “Hello” 也是 String 对象,也可以使用 equals 方法,且不存在会报错的风险

四、实例分析(🔴)

实例一:

📑代码示例:

public static void main(String[] args) {
    //例一
    String str1 = "Hello world";
    String str2 = "Hello" + " world"; 
    System.out.println(str1 == str2)
    //例二
    String str3 = "Hello world";
    String str4 = "Hello";
    String str5 = str4 + " world";
    System.out.println(str3 == str5);
}

💬代码解释:

例一的结果为 true,例二的结果为 false

  • 在例一中,“Hello” 和 " world" 都是字符串常量,在编译的时候会进行优化,自动拼接。因此实际上,str2 指向的对象的内容就是 “Hello world”
  • 在例二中,str4 是一个变量,在编译的时候并不知道它的值,运行时才会知晓,因此 str5 指向的是 “Hello” 和 “ world” 拼接后产生的新的对象

内存布局:

实例二:

📑代码示例:

public static void main(String[] args) {
    //例一
    String str1 = "Hello world";
    String str2 = "Hello" + new String(" world");
    System.out.println(str1 == str2);
    // 例二
    String str3 = "Hello world";
    String str4 = new String("Hello") +new String(" world");
    System.out.println(str3 == str4);
}

💬代码解释:

例一和例二的结果都是 false

内存布局:

例一:

例二:

实例三:

📑代码示例:

public static void main(String[] args) {
    //例一
    String str1 = new String("Hello") + new String(" world");
    str1.intern();
    String str2 = "Hello world";
    System.out.println(str1 == str2);
    //例二
    String str3 = new String("Hello") + new String(" world");
    String str4 = "Hello world";
    str3.intern();
    System.out.println(str3 == str4);
}

💬代码解释:

例一的结果为 true,例二的结果为 false

inter()的作用是手动将字符串入池(字符串常量池)

  • 判断 str1 目前指向的对象在常量池中有没有,发现没有,就将这个对象放入到常量池中

  • 判断 str3 目前指向的对象在常量池中有没有,发现有,就无需有所行动

内存布局:

例一:

例二:

inter方法的优点:

如果用构造方法创建字符串,每次都会在堆上开辟两块内存空间,传入的字符串参数是一个匿名对象,使用一次后将不会被使用,将会成为垃圾空间。还有就是同一个字符串可能会被存储很多次,浪费空间,使用 inter 方法,将池里没有的字符串手动导入池中,就可以避免这一麻烦。

实例四:

📑代码示例:

public static void main(String[] args) {
    String str1 = "Hello";
    String str2 = str1;
    str1 = "world";
    System.out.println(str1 == str2);
}

💬代码解释:

结果为 false

str1 和 str2 都是引用类型,str2 指向了 str1 指向的对象,但是却没有办法通过 str1 去改变 str2 指向的对象,因此就算这两个引用指向同一片空间,也是没有办法通过其中一个修改另一个的内容

实例五:

众所周知,字符串常量是没有办法进行修改的,若是想要将字符串 “Hello” 改成 “hello”,应当怎么操作呢?

📑代码示例1:

public static void main(String[] args) {
    String str = "Hello";
    str = "h" + str.substring(1);
    System.out.println(str);
}

💬代码解释:

结果为 hello

  • 该方法实际上就是借助原来的字符串,创建新的字符串,并没有将原来的 “Hello” 真正的改成 “hello”
  • substring(1) 表示从 str 字符串偏移量为1开始提取字符串

📑代码示例2:

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
	String str = "Hello";
    Class c = String.class;
    //获取String类中的value字段
    Field field = c.getDeclaredField("value");
    //权限修改了
    field.setAccessible(true);
    //把str中的value属性获取到.
    char[] vals  = (char[]) field.get(str);
    //修改value的值
    vals[0] = 'h';
    //hello
    System.out.println(str);
}

💬代码解释:

结果为 hello

  • 这里用到了反射的操作(对于该操作以后会进行仔细的讲解),该操作指的就是在程序运行过程中,获取或修改某个对象的详细信息,会破坏封装

实例六:

📑代码示例:

public static void main(String[] args) {
    String str = "hello" ;
    for(int x = 0; x < 1000; x++) {
        str+= x ;
    }
    System.out.println(str);
}

💬代码解释:

结果为一字符串,“hello01234…998999”

这样的代码不应该出现在开发当中,每次循环都会产生新的对象,效率低下

五、字符串与字符 & 字符串与字节

5.1 字符串与字符

字符串内部包含一个字符数组 value,String 可以和 char[] 相互转换

📑代码示例:

public static void main(String[] args) {
    //例一
    char[] value = {'h','e','l','l','o'};
    String str1 = new String(value,1,4);
    System.out.println(str1);
    //例二
    String str2 = "hello";
    char ch = str2.charAt(1);
    System.out.println(ch);
    //例三
    String str3 = "hello";
    char[] chars = str3.toCharArray();
    System.out.println(Arrays.toString(chars));
}

🏸 代码结果:

💬代码解释:

  • 例一 public String(char[] value,int offset,int count)

    是 String 类的一个构造方法,表示从偏移量为1的位置开始取4个字符来构造String对象,将取出的部分变成字符串,切记偏移量和取的字符个数不可超出数组,否则会数组越界异常

  • 例二 public char charAt(int index)

    是取得指定索引位置的字符,切记索引的大小不可超过字符串中字符的个数减一,否则会数组越界异常

  • 例三 public char[] toCharArray()

    是将字符串以字符数组的方式进行存储

📑代码示例:

判断给定的字符串其是否全部由数字所组成

public class TestDemo {
    public static boolean isNumber (String str ) {
        if(str == null) return false;//str不指向任何对象
        if(str.length() == 0) return false;//空串
        char[] chars = str.toCharArray();
        for (char ch :chars) {
            if(ch  < '0' || ch >'9') {
                return false;
            }
        }
        return true;
    }
    public static void main(String[] args) {
        String str = "";
        if(isNumber(str)) {
            System.out.println("全都是数字字符!");
        }else {
            System.out.println("不全都是数字字符!");
        }
    }
}

注意:

一定要考虑完善,空串以及引用变量不指向任何对象多需要考虑到

5.2 字符串与字节

字节常用于数据传输以及编码转换的处理之中,String 也能方便的和 byte[] 相互转换

📑代码示例:

public class TestDemo {
    public static void main(String[] args) throws UnsupportedEncodingException {
        //例一
        byte[] bytes = {97,98,99,100};
        String str = new String(bytes);
        System.out.println(str);
        //例二
        String str3 = new String(bytes,1,3);
        System.out.println(str3);
        //例三
        String str4 = "abcd";
        byte[] bytes5 = str4.getBytes();
        System.out.println(Arrays.toString(bytes5));
        //例四
        String str5 = "你好";
        byte[] bytes3 = str5.getBytes("utf-8");
        System.out.println(Arrays.toString(bytes3));
        byte[] bytes4 = str5.getBytes("gbk");
        System.out.println(Arrays.toString(bytes4));
    }
}

🏸 代码结果:

💬代码解释:

  • 例一 public String(byte[ ] bytes)

    是将字节数组中的每一个元素变成对应的字符,字符组成字符串

  • 例二 public String(byte[ ] bytes,int offset,int length)

    是 String 类的一个构造方法,表示从偏移量为1的位置开始取3个字节数组的元素来构造String对象,将取出的部分变成字符串,切记偏移量和取的字符个数不可超出数组,否则会数组越界异常

  • 例三 pulic byte[ ] getBytes( )

    是将字符串以字节数组的形式返回

  • 例四 public byte[ ] getBytes(String charsetName) throws UnsupportedEncodingException

    是编码转换处理,以不同的字节码去获取字符串,将其转换为 byte 数组(一个汉字 utf-8 占3个字节,gbk 占2个字节)

📑示例:

使用该方法 String 类型上会出现一道横线,点进去发现有 @Deprecated 这一注解,说明该方法已经被弃用

六、String类常见的操作(🔴)

6.1 字符串比较

📑代码示例:

public class TestDemo {
    public static void main(String[] args) {
        String str1 = "abc";
        String str2 = "aBc";
		//例一(比较相等)
        System.out.println(str1.equals(str2));
        //例二(比较相等)
        System.out.println(str1.equalsIgnoreCase(str2));
        //例三(比较大小)
        System.out.println(str1.compareTo(str2));
    }
}

🏸 代码结果:

💬代码解释:

  • 例一 public boolean equals(Object anObject)

    在之前的字符串比较相等中有介绍过,作用就是比较两个字符串是否相等(区分大小写)

  • 例二 public boolean equalsIgnoreCase(String anotherString)

    作用也是比较两个字符串是否相等(不区分大小写)

  • 例三 public int compareTo(String anotherString)

    作用为比较两个字符串的大小关系

    str1 大于 str2 返回正数,等于 str2 返回 0,小于 str2 返回负数。

    在两比较的字符串从后往后找的过程中,找到的第一个不相同的字符,这俩字符的大小关系就是整个字符串的大小关系。例如 str1 中的字符 b 比 str2 中的字符 B 大32,就返回正数32

    如果两个字符串前面的字符都相等,但长度有所不一样,就返回长度的差值

6.2 字符串查找

📑代码示例:

public class TestDemo {
    public static void main(String[] args) {
        String str1 = "ccabbabbcc";
      	//例一(是否存在)
        System.out.println(str1.contains("abb"));
		//例二(从头查找指定字符串)
        System.out.println(str1.indexOf("abb"));
		//例三(从指定位置查找指定字符串)
        System.out.println(str1.indexOf("abb", 3));
		//例四(从后向前查找指定字符串)
        System.out.println(str1.lastIndexOf("abb"));
		//例五(从指定位置从后向前查找指定字符串)
        System.out.println(str1.lastIndexOf("abb", 5));
		//例六(是否以指定字符串开头)
        System.out.println(str1.startsWith("cca"));
		//例七(从指定位置判断是否以指定字符串开头)
        System.out.println(str1.startsWith("cab", 1));
		//例八(是否以指定字符串结尾)
        System.out.println(str1.endsWith("bcc"));
    }
}

🏸 代码结果:

💬代码解释:

  • 例一 public boolean contains(CharSequence s)

    作用为判断一个子字符串是否存在,当且仅当字符串包含指定的 char 值序列时才返回 true。String 类有实现了 CharSequence 这个接口,因此可以直接传 String 类型的参数

  • 例二 public int indexOf(String str)

    作用为从头开始查找指定的字符串的位置,返回指定字符第一次出现的字符串内的索引,查找不到返回-1

  • 例三 public int indexOf(String str, int fromIndex)

    作用为从指定的索引开始,返回指定字符第一次出现的字符串内的索引,查找不到返回-1

  • 例四 public int lastIndexOf(String str)

    作用为返回指定子字符串最后一次出现的字符串中的索引,查找不到返回-1

  • 例五 public int lastIndexOf(String str, int fromIndex)

    作用为从指定的索引开始从后向前搜索,返回指定子字符串最后一次出现的字符串中的索引,查找不到返回-1

  • 例六 public boolean startsWith(String prefix)

    作用为测试此字符串是否以指定的前缀开头

  • 例七 public boolean startsWith(String prefix, int toffset)

    作用为测试在指定索引处开始的此字符串的子字符串是否以指定的前缀开头

  • 例八 public boolean endsWith(String suffix)

    作用为测试此字符串是否以指定的后缀结尾

6.3 字符串替换

📑代码示例:

public class Tes

以上是关于Java疯狂作图之剖释String类之妈见夸之作的主要内容,如果未能解决你的问题,请参考以下文章

Java高级编程--常用类之String类的常用方法

java常见类之String类

091Java中String类之使用“==”比较

107Java中String类之判断开头或结尾

088Java中String类之对象直接赋值

120Java中String类之实现首字母大写