首页 > 其他 > 详细

浅析String类

时间:2021-05-07 18:54:49      阅读:22      评论:0      收藏:0      [点我收藏+]

String

源码:

//	String定义源码
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
    //	value[]用于字符串存储
    private final char value[];

    /** Cache the hash code for the string */	
    private int hash; 
//	hash  缓存字符串的哈希
    

从源码可以看出

  • String被final修饰,表示String类不能被继承,并且它的成员方法都默认为final方法

  • String类实现了

    • Serializable(序列化接口)

      • Serializable接口是一个里面什么都没有的接口

        Serializable接口是启用其序列化功能的接口。实现接口的类是可序列化的,它的源代码是public interface Serializable{},即什么都没有。(标识接口)

      • 为什么要序列化 存储对象在存储介质中,以便下次使用的时候,可以很快创建一个副本;没有序列化依旧可以存储(MySQL、Oracle),为什么要序列化存储呢? 便于数据传输,尤其是远程调用的时候

    • CharSequence(字符串)

      • 获取指定索引的字符:public char charAt(int index);
      • 获取字符串长度:public int length();
      • 截取部分字符串:public CharSequence subSequence(int start, int end);
    • Comparable(强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序

  • String类的值是通过char数组存储的,并且char数组被private和final修饰,字符串一旦创建就不能再改变

replace()方法,

?

public String replace(char oldChar, char newChar) {
    if (oldChar != newChar) {
        int len = value.length;
        int i = -1;
        char[] val = value; /* avoid getfield opcode */
        while (++i < len) {
            if (val[i] == oldChar) {
                break;
            }
        }
        if (i < len) {
            char buf[] = new char[len];
            for (int j = 0; j < i; j++) {
                buf[j] = val[j];
            }
            while (i < len) {
              char c = val[i];
              buf[i] = (c == oldChar) ? newChar : c;
              i++;
            }
            //创建一个新的字符串返回
            return new String(buf, true);
        }
    }
    return this;
}

实际上replace方法并没有对原字符串进行修改,而是创建了一个新的字符串进行返回

为什么要用final进行修饰

? 作用:不能被继承,类中的所有方法都会被默认为final方法,也就是不能拥有子类,成员方法不能被重写

? String被final修饰主要是基于安全性和效率

  1. 字符串不可边,所以线程安全,同一个字符串可以被多个线程共享,这样就不用因为线程安全问题而是用同步,字符串自己就是线程安全的
  2. String类被许多的Java类(库)用来做参数,比如路径path,网络连接地址URL,反射机制所需要的String参数等,假设String是可变的,则有安全隐患
  3. 自付出不变保证了hash的唯一性,因此可以放心的进行缓存,不用每次都去进行计算,新的哈希值,也是性能优化的一种手段
  4. 字符串常量池的实现,字符串常量池是Java堆内存的一个特殊存储区域,当创建String对象,假设此字符串值已经存在于常量池中,则不会创建一个新的对象,而是引用已经存在的对象
字符串常量池

常量池:常量池是为了避免 频繁的创建和销毁对象二影响系统性能,其实现了对象的共享;

  1. 例如字符串常量池,常量池中所有的字符串文字放大欧阳哥常量池中
    1. 节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间
    2. 节省运行时间:比较字符串时,比equals快,对于两个引用变量,只用去判断引用是否相等,也就是判断实际值是否相等

? 字符串的的分配和其他对象一样,也需要消耗高昂的时间和空间,而且字符串使用的也特别多。JVM为了提高性能减少内存的消耗,所以在实例化字符串的时候使用了字符串常量池来进行优化。

Java中池也常常用到,字符串常量也是一样。当创建字符串时,JVM会首先检查字符串常量池,如果该字符串已经存在于常量池中,那么直接返回实例的引用,如果字符串不存在于常量池中,就会实例化该字符串并将其放在常量池中。

public static void main(String[] args) throws Exception {
    String s1 = "abc";
    String s2 = "abc";
    System.out.println(s1 == s2);//true
}

技术分享图片

new String("abc")

到底创建几个对象

? 一个或者两个

如果之前"abc"没有被使用过,则创建两个对象,堆中一个String对象,字符串常量池中创建一个,共两个;

String s = new String("abc");
//两个

如果之前使用过"abc"字符串,则不会在字符串常量池中创建对象,而是从字符串缓冲池中获取,只会在堆中创建一个对象

String s1 = "abc";
String s2 = new String("abc");
//s2这行代码,只会创建一个对象
字符串的拼接
public static void main(String[] args) throws Exception {
    String s = "";
    for (int i = 0; i < 10; i++) {
        s+=i;
    }
    System.out.println(s);//0123456789
}

其实String类使用" += "拼接的底层是使用StringBuilder,先初始化一个StringBuilder,然后再使用append()进行拼接,最后toString得到结果

问题在于如果在循环体内使用+=拼接,会创建很多临时的StringBuilder对象,拼接后再调用toString()赋给原String对象。这会生成大量临时对象,严重影响性能。

所以在循环体内进行字符串拼接时,建议使用StringBuilder或者StringBuffer类,例子如下:

public static void main(String[] args) throws Exception {
    StringBuilder s = new StringBuilder();
    for (int i = 0; i < 10; i++) {
        s.append(i);
    }
    System.out.println(s.toString());//0123456789
}

StringBuilder和StringBuffer的区别在于,StringBuffer的方法都被sync关键字修饰,所以是线程安全的,而StringBuilder则是线程不安全的(效率高)。

== 与 equals 区别

? == 比较的是真正意义上的指针操作

1 ==指引用是否相同, equals()指的是值是否相同

2 ==是指对内存地址进行比较 , equals()是对字符串的内容进行比较

3 *==是判断两个变量或实例是不是指向同一个内存空间,equals是判断两个变量或实例所指向的内存空间的值是不是相同 *

技术分享图片

但是equals方法的源码又是进行==判断,

    // 来自基类Object
public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }

所以我们在需要的时候需要对equals()方法进行重写

== 的作用:
  基本类型:比较的就是值是否相同
  引用类型:比较的就是地址值是否相同
equals 的作用:
  引用类型:默认情况下,比较的是地址值。
注:不过,我们可以根据情况自己重写该方法。一般重写都是自动生成,比较对象的成员变量值是否相同

关于String的对象创建

String类在被创建的时候,hashcode就被缓存到hash成员变量中,因为String是不可变的,所以hashcode的值也不会改变,这样每次像是用的时候直接取就可以了,不用重新计算,提高了效率;

String s="abcd"

是一种非常特殊的形式,和new 有本质的区别。它是Java中唯一不需要new 就可以产生对象的途径。

String str = "abc";
	创建一个对象
String a = "abc";
String b = "abc";
	创建一个对象
String s = "ab" + "cd";
	创建三个对象
String a = new String("abc");
	创建两个对象,一个堆上,一个常量池中存储
	
        
        *String类是final修饰*
JVM中的常量池保存很多String对象,可以共享;因此提高了效率,
由于String类的final的,它的值一经创建不可改变,所以不会因为共享对象而混乱,字符串池由String类维护,我们可以调用intern方法来访问字符串池
String s="abcd"

是一种非常特殊的形式,和new 有本质的区别。

它是java中唯一不需要new 就可以产生对象的途径。

以String s="abcd";形式赋值在java中叫直接量,它是在常量池中而不是象new一样放在压缩堆中。 这种形式的字符串,在JVM内部发生字符串拘留,即当声明这样的一个字符串后,JVM会在常量池中先查找有有没有一个值为"abcd"的对象,如果有,就会把它赋给当前引用.即原来那个引用和现在这个引用指点向了同一对象, 如果没有,则在常量池中新创建一个"abcd",下一次如果有String s1 = "abcd";又会将s1指向"abcd"这个对象,即以这形式声明的字符串,只要值相等,任何多个引用都指向同一对象.
   而String s = new String("abcd");和其它任何对象一样.每调用一次就产生一个对象,只要它们调用。
也可以这么理解: String str = "hello"; 先在内存中找是不是有"hello"这个对象,如果有,就让str指向那个"hello".

如果内存里没有"hello",就创建一个新的对象保存"hello". String str=new String ("hello") 就是不管内存里是不是已经有"hello"这个对象,都新建一个对象保存"hello"。

String的几个常用方法

? indexOf(int / string) 返回指定字符在字符串中第一次出现时的索引值,若没有返回 -1

charAt(int) char   返回指定字符在字符串中的位置

concat(string) string	字符串拼接

contains(char) Boolean  判断字符串是否包含某个字符

intern(string) string 返回与字符串相同,当且仅当	 a.equals(b)为true,a.intern() == b.intern()
 

String a = new String("abc");创建过程
在堆中创建一个对象,让a指向这个对象(创建一个对象)
在常量池中看看是否存在 abc
如果有,就让堆中创建好的字符串对象,对常量池的abc进行引用(创建一个对象)
如果没有,就在常量池中添加一个abc的字符串对象,并将堆中的String对象进行引用(创建了两个对象)

浅析String类

原文:https://www.cnblogs.com/ShangStudyJava/p/14741616.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!