剖析JDK源码-String-(3)

剖析JDK源码-String-(3)

一、简述

  • String类代表字符串,在 Java 编程中是最常被使用的,Java程序中的所有字符串文字都是该类的实例,从而进行创建和操作字符串。
  • 它们的值在创建后不能被更改,因此允许被共享。
  • 字符串缓冲区支持可变字符串(通过操作StringBuffer改变值)。

二、类声明和属性

为了提高程序的运行性能,String对象的内部实现方式被不断的优化,在版本上会有些许不用,这里以java8为参考点。

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
 	private final char value[];
	private int hash;
	private static final long serialVersionUID = -6849794470754667710L;
	private static final ObjectStreamField[] serialPersistentFields =
        new ObjectStreamField[0];
   public static final Comparator<String> CASE_INSENSITIVE_ORDER
                                         = new CaseInsensitiveComparator();
        /**
        略...
        */
 private static class CaseInsensitiveComparator
            implements Comparator<String>, java.io.Serializable{
 		/**
        略...
        */
	}
	
		 /**
        略...
        */
 public native String intern();//返回字符串对象的规范表示。
}

从以上可以看到:

  • String被final修饰,所以是线程安全的(其实String并不涉及多线程问题,只是非要把他归类的话,就只能说他是安全的咯,毕竟人家的值是怎么都无法修改的)。
  • String类实现了Serializable(序列化)、Comparable (一个比较器,该接口只有一个方法comparaTo ,就是进行对象的比较,并返回一个int的值[返回0表示两者相等;返回整数表示大于;返回负数表示小于];在这里实现Comparable就是对字符串进行比较,从而可以实现排序)、CharSequence(字符序列)。
  • 有两个变量属性[value[]、hash]和三个常量属性[serialVersionUID 、serialPersistentFields 、CASE_INSENSITIVE_ORDER]。
  • 内部类CaseInsensitiveComparator,实现忽略大小的比较。
附:
  • 在java6以前,String对象还有两个变量【offset、count】,以此来定位char[]数组,达到高效、快速地共享对象,但这种方式很大可能会导致内存泄漏,在java7后就不再使用这种方式了。
  • 在java9后,char[]数组被byte[]数组所替换,这是为了将储存单位减小,以此避免存储空间的浪费;除此还新增了一个属性coder,标识对象的编码格式,默认值为0(单字节编码Latin-1)或者1(UTF-16编码)。

三、构造方法

1、初始化新创建String对象,以使其表示空字符序列。(这个构造方法在某种角度来说是无意义的,因为String的值是不可变的)

 public String() {
        this.value = "".value;
    }

2、初始化新创建String对象,使其表示与参数相同的字符序列; 换句话说,新创建的字符串是参数字符串的副本。

   public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

3、分配一个新的 String以便它表示当前包含在字符数组参数中的字符序列。

  public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }

4、分配一个新的 String ,其中包含字符数组参数的子阵列中的字符。

 public String(char value[], int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {
            if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
            }
            if (offset <= value.length) {
                this.value = "".value;
                return;
            }
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > value.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }
        this.value = Arrays.copyOfRange(value, offset, offset+count);
    }

5、分配一个新的 String ,其中包含 Unicode code point数组参数的子阵列中的 字符 。

@Contract(pure = true)
public String(int[] codePoints, int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {
            if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
            }
            if (offset <= codePoints.length) {
                this.value = "".value;
                return;
            }
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > codePoints.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }

        final int end = offset + count;

        // Pass 1: Compute precise size of char[]
        int n = count;
        for (int i = offset; i < end; i++) {
            int c = codePoints[i];
            if (Character.isBmpCodePoint(c))
                continue;
            else if (Character.isValidCodePoint(c))
                n++;
            else throw new IllegalArgumentException(Integer.toString(c));
        }

        // Pass 2: Allocate and fill in char[]
        final char[] v = new char[n];

        for (int i = offset, j = 0; i < end; i++, j++) {
            int c = codePoints[i];
            if (Character.isBmpCodePoint(c))
                v[j] = (char)c;
            else
                Character.toSurrogates(c, v, j++);
        }

        this.value = v;
    }

6、构造一个新的 String通过使用指定的字符集解码指定的字节子阵列。

@Contract(pure = true)
public String(byte bytes[], int offset, int length, String charsetName)
            throws UnsupportedEncodingException {
        if (charsetName == null)
            throw new NullPointerException("charsetName");
        checkBounds(bytes, offset, length);
        this.value = StringCoding.decode(charsetName, bytes, offset, length);
    }

7 、构造一个新的String通过使用指定的指定字节子阵列解码charset 。

@Contract(pure = true)
public String(byte bytes[], int offset, int length, Charset charset) {
        if (charset == null)
            throw new NullPointerException("charset");
        checkBounds(bytes, offset, length);
        this.value =  StringCoding.decode(charset, bytes, offset, length);
    }

@Contract(pure = true)
public String(byte bytes[], String charsetName)
            throws UnsupportedEncodingException {
        this(bytes, 0, bytes.length, charsetName);
    }
@Contract(pure = true)
 public String(byte bytes[], Charset charset) {
        this(bytes, 0, bytes.length, charset);
    }

8、通过使用平台的默认字符集解码指定的字节子阵列来构造新的 String 。

@Contract(pure = true)
 public String(byte bytes[], int offset, int length) {
        checkBounds(bytes, offset, length);
        this.value = StringCoding.decode(bytes, offset, length);
    }

 private static void checkBounds(byte[] bytes, int offset, int length) {
        if (length < 0)
            throw new StringIndexOutOfBoundsException(length);
        if (offset < 0)
            throw new StringIndexOutOfBoundsException(offset);
        if (offset > bytes.length - length)
            throw new StringIndexOutOfBoundsException(offset + length);
    }  

9、分配一个新的字符串,其中包含当前包含在字符串缓冲区参数中的字符序列。

@Contract(pure = true)
public String(StringBuffer buffer) {
        synchronized(buffer) {
            this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
        }
    }

10、分配一个新的字符串,其中包含当前包含在字符串构建器参数中的字符序列。

  public String(StringBuilder builder) {
        this.value = Arrays.copyOf(builder.getValue(), builder.length());
    }

11、因为该方法无法将字节正确转换为字符,所以被弃用。

 @Deprecated
    public String(byte ascii[], int hibyte, int offset, int count) {
        checkBounds(ascii, offset, count);
        char value[] = new char[count];

        if (hibyte == 0) {
            for (int i = count; i-- > 0;) {
                value[i] = (char)(ascii[i + offset] & 0xff);
            }
        } else {
            hibyte <<= 8;
            for (int i = count; i-- > 0;) {
                value[i] = (char)(hibyte | (ascii[i + offset] & 0xff));
            }
        }
        this.value = value;
    }
    
  @Deprecated
    public String(byte ascii[], int hibyte) {
        this(ascii, hibyte, 0, ascii.length);
    }

四、内部方法

1、public int length(); 返回字符串长度。
2、public boolean isEmpty(); 返回判断字符串是否为空。
3、public char charAt(int index); 返回指定索引的值。
4、 public int codePointAt(int index);返回指定索引处的字符(Unicode代码点)。
5、public int codePointBefore(int index); 返回指定索引之前的字符(Unicode代码点)。
6、public int codePointCount(int beginIndex, int endIndex); 返回此 String指定位置范围内的Unicode代码点数。
7、public int offsetByCodePoints(int index, int codePointOffset);返回此 String内的指数,与 index codePointOffset代码点。
8、 void getChars(char dst[], int dstBegin);将此字符串中的字符复制到目标字符数组中。
9、public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) ;将此字符串中指定段的字符复制到目标字符数组中。
10、 public byte[] getBytes(String charsetName);使用命名的字符集将此 String编码为字节序列,将结果存储到新的字节数组中。
11、public byte[] getBytes(Charset charset);使用给定的charset将该String编码为字节序列,将结果存储到新的字节数组中。
12、public byte[] getBytes();使用平台的默认字符集将此 String编码为字节序列,将结果存储到新的字节数组中。
13、public boolean equals(Object anObject);将此字符串与指定对象进行比较。
14、public boolean contentEquals(StringBuffer sb);将此字符串与指定的StringBuffer(构造器)进行 比较。
15、 public boolean equalsIgnoreCase(String anotherString);将此 String与其他 String比较。
16、 public int compareTo(String anotherString);实现Comparable接口的方法,按字典顺序比较两个字符串。
17、public int compareToIgnoreCase(String str);按字典顺序比较两个字符串,忽略大小差异,调用内部类CASE_INSENSITIVE_ORDER实现。
18、public boolean regionMatches(int toffset, String other, int ooffset, int len);测试两个字符串区域是否相等。
19、public boolean regionMatches(boolean ignoreCase, int toffset,String other, int ooffset, int len);测试两个字符串区域是否相等。与18多一个ignoreCase参数,是否忽略大小的比较。
20、public int indexOf(int ch);public int indexOf(int ch, int fromIndex);返回指定字符第一次出现的字符串内的索引,可以从指定的索引开始搜索。
21、public boolean startsWith(String prefix);public boolean startsWith(String prefix, int toffset);测试在指定索引处开始的此字符串的子字符串是否以指定的前缀开头。
22、public boolean endsWith(String suffix);判断此字符串的子字符串是否以指定的后缀结尾。
23、 public int hashCode();返回此字符串的哈希码。
24、public int lastIndexOf(int ch);返回指定字符的最后一次出现的字符串中的索引。
25、public int lastIndexOf(int ch, int fromIndex) ;返回指定字符的最后一次出现的字符串中的索引,从指定的索引开始向后搜索。
26、public int indexOf(String str);public int indexOf(String str, int fromIndex);返回指定子字符串的第一次出现的字符串中的索引,可以指定开始索引向后搜索。
27、public int lastIndexOf(String str) ;public int lastIndexOf(String str, int fromIndex);返回指定子字符串的最后一次出现的字符串中的索引,可以指定开始索引向后搜索。
28、 public String substring(int beginIndex);public String substring(int beginIndex, int endIndex);返回一个字符串,该字符串是此字符串的子字符串,指定开始和结束位置。
29、public CharSequence subSequence(int beginIndex, int endIndex);返回一个字符序列,该序列是该序列的子序列。
30、public String concat(String str);将指定的字符串连接到该字符串的末尾。
31、public String replace(char oldChar, char newChar);返回从替换所有出现的导致一个字符串 oldChar在此字符串 newChar 。
32、public boolean matches(String regex);判断字符串是否匹配指定字符串。
33、public boolean contains(CharSequence s);当且仅当此字符串包含指定的char值序列时才返回true。
34、public String replaceFirst(String regex, String replacement);用给定的替换替换与给定的 regular expression匹配的此字符串的第一个子字符串。
35、public String replaceAll(String regex, String replacement);用给定的替换替换与给定的 regular expression匹配的此字符串的每个子字符串。
36、public String replace(CharSequence target, CharSequence replacement);将与字面目标序列匹配的字符串的每个子字符串替换为指定的字面替换序列。
37、public String[] split(String regex, int limit);将这个字符串拆分为给定的字符的数组。
38、public static String join(CharSequence delimiter, CharSequence… elements);返回一个新的字符串,由 CharSequence elements的副本组成,并附有指定的delimiter的 delimiter 。
39、public static String join(CharSequence delimiter,Iterable<? extends CharSequence> elements); 返回一个新 String的副本组成 CharSequence elements与指定的副本一起加入 delimiter 。
40、public String toLowerCase(Locale locale);转换小写。
41、public String toUpperCase(Locale locale);转换大写。
42、public String trim();返回一个字符串,其值为此字符串,并删除任何前导和尾随空格。
42、 public char[] toCharArray();将此字符串转换为新的字符数组。
43、public static String format(String format, Object… args);使用指定的格式字符串和参数返回格式化的字符串。
44、public static String valueOf(Object object);以字符串的形式返回参数;

五、常见面试题

1、判断字符串是否相等?

	String str1 = "abc";
	String str2 = "abc";
	System.out.println(str1 ==str2);
	System.out.println(str1 .equals(str2));

输出结果:true true
String类代表字符串,前面说到字符串对象是常量final,常量需要进入到内存中的方法区的常量池(进入常量池规则:如果常量池中没有这个常量,就创建一个,如果有就不再创建了)。所以str1首先会在常量池中创建“abc”对象,“abc”对象是有地址值的,“abc”对象将地址值赋给str1。当定义str2的时候,此时常量池中已经存在了“abc”这个对象,所以就不需要再创建了,str2的引用也指向了常量池中的“abc”对象,两个引用指向了同一个对象,str1和str2地址值一样,所以==号比较的时候返回true;String类重写了equals方法,比较的是属性值,str1和str2的属性值都是“abc”,所以是true。

2、代码在内存中创建就几个对象?

String str = new String("abc");

答案:2个
程序首先会在常量池创建“abc”对象,当new的时候会在堆中创建一个String对象,此时把常量池的对象“abc”拷贝副本给堆内存的String对象,然后把堆内存的String对象地址赋值给str;
此时若:

String str1 = "abc";
String str2 = new String("abc");
System.out.println(str1 == str2);
System.out.println(str1 .equals(str2));

输出:false true
常量池创建一个对象“abc”,堆内存创建一个String对象;但是str1的地址是常量池的对象“abc”的地址,而str2的地址是堆内存的String对象地址。

3、字符串拼接后比较相等:

String str1 = "a";
String str4 = "abc";
String str5 = str1 + "bc";
String str6 = "a" + "b"+"c";
System.out.println(str4 == str6);
System.out.println(str4 .equals(str6));
System.out.println(str4 == str5);
System.out.println(str4 .equals(str5));

输出:true true false true
因为str1+“bc”不是常量,所以地址不能用常量优化机制来分析;str5在相加时会使用StringBuffer构造器进行操作;此时得到的地址不是常量池的地址,而是被拷贝出去操作的副本地址。

4、String是基本数据类型吗?
String不是基本数据类型。
5、switch中可以使用String吗?
在java中switch后的表达式的类型只能为以下几种:byte、short、char、int(在Java1.6中是这样),在java1.7后支持了对string的判断。
6、String、StringBuffer、StringBuilder有什么区别?
String、StringBuffer、StringBuilder最大的不同是String不可变,StringBuffer和StringBuilder可变。StringBuffer是线程安全的,StringBuilder线程不安全,操作速度较快。
7、String.trim()方法去掉的是哪些字符?
trim去掉字符串首尾的空白字符。
8、String可以被子类继承吗?
既然String是final的,所以不能被继承。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值