String的常用方法,易错点总结(Java基础一)

部分参考:知乎:如何理解 String 类型值的不可变?胖君

一.String基本

1.1 内部存储结构

拿jdk1.8来说,它的内部本质是:String 内部实际存储结构为 char 数组

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** char数组存储字符 */
    private final char value[];

    /** 存放字符串的hashcode */
    private int hash; // Default to 0

String为什么是不可变的?

  1. String自身final修饰,不可继承
  2. private final 修饰的 char 类型数组存储字符(但是只是引用不可变,但是里面存的值是可变的)
  3. String的方法里很小心的没有去动Array里的元素,没有暴露内部成员字段
  4. 就是对于数据的封装是很完善的

String是什么不可变的?

  1. String的不可变指的是:无法在原内存地址上修改数据
  2. 但是对象的指向是可以变的
  3. 通过反射是可以修改所谓的“不可变”对象,但是一般不用
    用反射可以访问私有成员, 然后反射出 String 对象中的 value 属性, 进而改变通过获得的 value 引用改变数组的结构。

意义,好处

  1. 不可变对象不能被写,所以线程安全
    作为HashMap、HashTable等hash型数据key的必要
  2. 我们知道直接赋值,会产生在常量池中,是可以被多个对象一起指向的,
    这样在大量使用字符串的情况下,可以节省内存空间,提高效率 ,要是内存里字符串内容能改来改去,这么做就完全没有意义了。
12. final 因为它能够缓存结果,当你在传参时不需要考虑谁会修改它的值;如果是可变类的话,则有可能需要重新拷贝出来一个新值进行传参,这样在性能上就会有一定的损失。
13. 安全,当你在调用其他方法时,比如调用一些系统级操作指令之前,可能会有一系列校验,如果是可变类的话,最后的结果可能会发生偏差,造成崩溃

1.2 重要的4个构造方法

  1. String为参数
public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }
  1. char型数组为参数
public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }
  1. StringBuffer 未参数
public String(StringBuffer buffer) {
        synchronized(buffer) {
            this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
        }
    }
  1. StringBuilder为参数
public String(StringBuilder builder) {
        this.value = Arrays.copyOf(builder.getValue(), builder.length());
    }

二. String的equals 与 ==

== 对于基本数据类型来说,是用于比较 “值”是否相等的;而对于引用类型来说,是用于比较引用地址是否相同的。
equals 比较的是字符串 的 字符 是否完全 相同

2.1 String产生位置的常识:

jdk1.8,这里只是这样说明,具体看2.4

  1. 右边直接用字符串赋值的产生在常量池中(常量池中的字符串不会重复),(包括用纯字符串相加的(就是用双引号引+起来的))
  2. 其他赋值情况均是在new(存在堆空间中)
  3. 而在比较时直接使用字符串(s4==“ab”),带双引号的"ab"是在堆空间的
得出结论:加号的
1.  string对象名 + string对象名是在 new字符串;
2.  string对象名 + string字符串是在 new字符串;
3.  string字符串 + string字符串是在 常量池中(如果没有,在常量池中创建,如果有则不用创建);

例子及答案

		String st1 = "a";
        String st2 = "b";
        String st0 = "ab";
        String st3 = st1+st2;
        String st4 = "a"+"b";
        String st5 = st1+"b";
        String st6 = "ab";
        System.out.println(st0 == st3); // f
        System.out.println(st0 == st4); // t
        System.out.println(st0 == st5); // f
        System.out.println(st3 == st4); // f
        System.out.println(st0 == st6); // t
        System.out.println(st6 == "ab"); // t

2.2 易错点

  1. String a1 = "";
  2. String a2 = new String();
    关于这一步,看源码,发现创建出的String 本质是 " " 与 a1相同
Public   String(){
      this.value="".value;
}
  1. String a3 = null ;两个字符串都为 null , 那么这两个字符串==
    例子答案:
		String a1 ="";
        String a2 =new String();
        String a3 =null;
        String a4 =null;
        System.out.println(a1 == a2); // f
        System.out.println(a1 == a3); // f
        System.out.println(a2 == a3); // f
        System.out.println(a4 == a3); // t
        System.out.println("-------");
        System.out.println(a1.equals(a2)); // t
        System.out.println(a1.equals(a3)); // f
        System.out.println(a2.equals(a3)); // f
        System.out.println(Objects.equals(a3,a4)); // t

用equals比较时,推荐使用Objects.equals,可以防止抛出NullPointException异常。

System.out.println(Objects.equals(a3,a4)); // t

final的易错点

		String str0 = "ab";
        final String str1 = "a";
        final String str2 = "b";
        String str3 = str1+str2;
        System.out.println(str0 == str3);

根据上面的总结:这是在new 对象,答案应该是 false
但那是错误的,答案是true
因为final修饰的对象编译后会直接替换成对应的值,就是你使用的str1,其实已经变为 "a"

	String str3 = str1+str2;
	// 等价为
	String str3 = "a"+"b";
	// 所以是在常量池中的"ab"

2.3 Object的equals

equals()比较的是对象的 内存地址是否相等

public boolean equals(Object obj) {
        return (this == obj);
    }
  1. Object 中的 equals() 方法其实就是==
  2. 而 String 重写了 equals() 方法把它修改成比较两个字符串的值是否相等

2.4 String两种创建方式的差别,内存分配(1.6,1.7及以后的区别)

理解java字符串内存分配及常用操作:作者:一刻动画
String 对象创建方式有哪几种?有什么区别?:本文出自水货程序员的腾讯云博客
String内存分配和intern方法:本文出自zhh_happig的简书博客

		String s1 = new String("hello");
        String sIntern = s1.intern();    // 入池操作
        String s2 ="hello";
        System.out.println(s1 == s2);       // f
        System.out.println(s2 == sIntern);  // t

        String s3 = new String("hello") + new String(" world");
        String intern = s3.intern();
        String s4 = "hello world";
        System.out.println(s3 == s4);     // t
        System.out.println(s3 == intern); // t

        String s5 = new String("ja") + new String("va");
        String intern1 = s5.intern();     
        String s6 = "java";
        System.out.println(s5 == s6);       // f
        System.out.println(s5 == intern1);  // f
        /**
         * 常量池在初始化的时候会内置一些字符串常量进去,
         * 在 rt.jar 里面已经用到了 "java" 这个字符串
         */

优化性能

把一个基本数据类型转为一般有三种方式,一个Integer型数据i,可以使用i.toStringString.valueOf(i)i+””三种方式,三种方式的效率如何;
i.toString>String.valueOf(i)>i+””

  1. String.valueOf方法底层调用了Integer.toString方法,会在调用前做空判断
  2. i + “”底层使用了StringBuilder实现,先用append方法拼接,再用toString方法获取字符串

更多优化可以看这篇文章总结

三.常用方法的解释

在这里插入图片描述

四. 易错方法的解释总结

在这里插入图片描述

split

		String s1 = "333111333111333";
        String [] arr = s1.split("3");
        System.out.println(Arrays.toString(arr));
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i].length());
        }
[, , , 111, , , 111]
0
0
0
3
0
0
3

画图理解分割
在这里插入图片描述

发布了22 篇原创文章 · 获赞 2 · 访问量 761

猜你喜欢

转载自blog.csdn.net/qq_43542795/article/details/105486841