知其然,知其所以然之Java基础系列(二)

String是Java语言中最重要的数据类型,但它非基本类型。

1、String对象具有三个基本特点:不变型、常量池的优化、类的final定义。

下面来介绍一下这三个基本特点:

①不变性:

不变性指的是String对象一旦创建,则不能对其进行修改。

②常量池优化

当两个String对象拥有相同的值时,它们只引用常量池中的同一个拷贝。

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

结果如下:
true
false
true
str1和str2是两个对象引用,引用了同一个对象,故他们的内存地址是相同的,str3重新开辟了一块内存空间,虽然内存地址不同,但str1和str3在常量池中的位置是相同的。

如下图所示:

③类的final定义

我们都知道String类是用final修饰的,是不被继承的,使用final定义,有助于帮助虚拟机寻找机会,内联所有的final方法提高系统效率。


2、String类的内部结构

String类的底层数据结构是char数组,这是结构的第一部分,第二部分是offset偏移量,第三部分是长度


3、String类中的常见方法介绍

3.1、substring

截取子字符串是字符串中很常见的的操作之一。String类提供了如下两个方法:

public String substring(int beginIndex);
public String substring(int beginIndex, int endIndex);

以第二个方法为例,JDK5和6这个方法存在严重的内存泄漏问题,查看具体代码如下:

    public String substring(int beginIndex, int endIndex) {
	if (beginIndex < 0) {
	    throw new StringIndexOutOfBoundsException(beginIndex);
	}
	if (endIndex > count) {
	    throw new StringIndexOutOfBoundsException(endIndex);
	}
	if (beginIndex > endIndex) {
	    throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
	}
	return ((beginIndex == 0) && (endIndex == count)) ? this :
	    new String(offset + beginIndex, endIndex - beginIndex, value);
    }
红色部分的String构造函数,如下:

    String(int offset, int count, char value[]) {
	this.value = value;
	this.offset = offset;
	this.count = count;
    }

为何说会带来内存泄漏呢?那是因为String的原声char数组内容被原封不动的复制到了子串中,如果原始字符串很大,截取的字符串很小,原始字符串还要占用相应的内存空间,这种空间换时间的方式,浪费了不少的内存空间。

大家再来看看JDK7此方法,发现有所改变:

    public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }
在红色代码片段,返回了一个新建的String对象,查看该方法的构造函数:

    public String(char value[], int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count < 0) {
            throw new StringIndexOutOfBoundsException(count);
        }
        // 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);
    }
并未保存原始字符串的内容,而是直接截取所需要的字符串内容。

3.2、String的split和StringTokenizer字符串分割

字符串的分割是字符串最常用的处理之一,String中提供了:

public String[] split(String regex)
它提供了非常强大的字符分割功能,传入的参数可以为正则表达式,进行复杂分割。

有时候量大了,它的分割性能并不是太近如意,可以考虑使用高效率的StringTokenizer

  public StringTokenizer(String str, String delim) {
        this(str, delim, false);
    }

3.3、高效率的charAt

在软件开发过程中,很多时候我们需要判断一个字符串是否以某个子串开头或者结尾,通常会使用startsWith和endsWith来判断,使用者两个函数的好处是可读性强、维护起来容易,如果对于性能要求比较高的系统来讲,这种做法不好的地方是性能差,可以考虑使用charAt

例如:

int len = str.length;
if (str.charAt(0) == 'a' &&
     str.charAt(1) == 'b')  {
     //do something
}

if (str.charAt(len-1) == 'a' &&
     str.charAt(len-2) == 'b') {
     //do somethig
}

使用startsWith和endsWith:
str.startsWith("ab");
str.endsWith("ab"):



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值