[Java]String类分析源码阅读

String类可以说是Java中最常使用的类了,但是String所包含的知识也是相当的多。

首先为什么String类一定要用equals()方法来比较字符串呢?还有new 出来的字符串和""双印号的字符串有什么区别呢?

看看下面这个例子

		String s1 = new String("helloworld");
		String s2 = new String("helloworld");
		String s3 = "helloworld";
		String s4 = "helloworld";
		System.out.println(s1 == s2);
		System.out.println(s1 == s3);
		System.out.println(s3 == s4);
		System.out.println("");
		System.out.println(s1.intern() == s3);
		System.out.println(s1.intern() == s3.intern());
		System.out.println("");
		String s5 = new String("hello");
		String s6 = "hello";
		System.out.println(s5 == s6);
		s5 = s5.intern();
		System.out.println(s5 == s6);

首先s1.equals(s2)是肯定是对的,等会我们分析equals的源代码。这里就不写出来了

s1 == s2 肯定是false 因为==只是比较两个String的引用 因为new出来 都是在堆上分配空间,所以是false

s1 == s2 不对 是因为 s3是在运行时放进常量区  也就是方法区里面的常量池,一个是堆上 一个是在常量区 所以false

s3 == s4 是true 是因为 代码经过编译后 认为是同一个常量  所以指向的引用地址相同。

s1.intern() == s3 和 s1.intern() == s3.intern()  返回true 是因为intern()函数查询常量池,如果常量池不存在,则放入常量池,然后返回引用,若是已经存在该常量则返回存在引用,所以相等

第一个s5 == s6  因为s5指向堆而s6指向常量区所以地址不同 false

第二个s5 == s6 因为s5变为常量区的引用 所以true

说到这里我想应该知道new出来的和""创建的String类型有什么不同了吗?一个是在编译时候赋予引用值,一个是在运行时决定。



那么String对象到底是可变的吗?

一般来说String类型是不可变的,大多数情况下,String的正常操作String的子串,拼接都是返回一个新的字串。但是可以通过反射改变其值

		Field field = s1.getClass().getDeclaredField("value");
		field.setAccessible(true);
		char[] value =(char[]) field.get(s5);
		value[1]='c';
		System.out.println(s5);
		System.out.println(s6);

通过反射成功的改变了s5和s6的值。他们都是公用同一个常量引用。

输出为

hcllo

hcllo


还有不要滥用String类的charAt方法

为什么呢?char类型采用unicode编码 即2个字节表示一个字符即 最多能表达 65536个字符 但是目前的字符远远超过这么多

对于特殊字符来说,好吧 这里我上传的代码段应该是非正常字符,所以被过滤导致后面的都被截断,只能重写,上图了



所以对于字符处理使用codePointAt()比较于安全

源码分析

    public int codePointAt(int index) {
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return Character.codePointAtImpl(value, index, value.length);
    }

这里调用了Character的codePointAtImpl()方法转换codePoint编码

    static int codePointAtImpl(char[] a, int index, int limit) {
        char c1 = a[index];
        if (isHighSurrogate(c1) && ++index < limit) {
            char c2 = a[index];
            if (isLowSurrogate(c2)) {
                return toCodePoint(c1, c2);
            }
        }
        return c1;
    }

    public static int toCodePoint(char high, char low) {
        // Optimized form of:
        // return ((high - MIN_HIGH_SURROGATE) << 10)
        //         + (low - MIN_LOW_SURROGATE)
        //         + MIN_SUPPLEMENTARY_CODE_POINT;
        return ((high << 10) + low) + (MIN_SUPPLEMENTARY_CODE_POINT
                                       - (MIN_HIGH_SURROGATE << 10)
                                       - MIN_LOW_SURROGATE);
    }

通过比较适否是正常字符,是则直接返回,不是则将char[i]和char[i+1]合并成新的编码



然后还需要注意的则是一般字符串的编码,这种情况在网络流的情况下遇到比较多

		String base = "why君";
		byte[] utf8s = base.getBytes("utf-8");
		for (int i = 0; i < utf8s.length; i++) {
			System.out.printf("%02x ", utf8s[i]);
		}
		System.out.println();
		byte[] gbks = base.getBytes("gbk");
		for (int i = 0; i < gbks.length; i++) {
			System.out.printf("%2x ", gbks[i]);
		}
		System.out.println();
		byte[] utf16s = base.getBytes("utf-16");
		for (int i = 0; i < utf16s.length; i++) {
			System.out.printf("%02x ", utf16s[i]);
		}
		System.out.println();
对于 why君 不同的编码产生的效果也是不同的

                       w                       h                       y                       君

utf-8               77                      68                     79                      e5 90 9b

gbk                77                      68                     79                      be fd

utf-16(fe ff)    00 77                  00 68                00 79                54 1b

feff为utf头封装

 


最后为String的equals方法分析

    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
是否指向同一个对象引用,是true

是否String  然后比较长度是否相等 相等 则继续逐字符比较






发布了58 篇原创文章 · 获赞 11 · 访问量 4万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章