上篇文章我们介绍过String这个常用类,知道了该类的内部其实是用的一个char数组表示一个字符串对象的,只是该字符数组被final修饰,一旦初始化就不能修改,但是对于经常做字符串修改操作的情况下,String类就需要不断创建新对象,性能极低。StringBuilder内部也是封装的一个字符数组,只不过该数组非final修饰,可以不断修改。所以对于一些经常需要修改字符串的情况,我们应当首选StringBuilder。其实StringBuilder和StringBuffer内部代码几乎一样,只是StringBuffer的所有方法都被关键字synchronized修饰,也就是说它是线程安全的,但是线程安全是需要付出性能代价的,所以在实际使用中,适情况选择。本篇主要介绍StringBuilder,以下是本篇主要内容:

  • 强大的父类AbstractStringBuilder
  • 多重载的构造函数
  • 重要的append方法
  • 其他一些方法的简单介绍

一、强大的父类AbstractStringBuilder

     StringBuilder的大部分方法中都会调用父类方法或属性, 足以见得该父类对其的影响还是很大的,所以我们将从头至尾简单介绍下它的父类AbstractStringBuilder。该类中只有两个属性:

//The value is used for character storage.
char[] value;
//The count is the number of characters used.
int count;

value属性表示的是一个字符数组,该数组的作用和String中的字符数组的作用是一样的,只是此value数组并没有被final修饰,也就是说该数组内部的值是可以动态修改的,这也是StringBuilder存在的意义。count属性表示的不是value数组的长度,它代表的是value数组中实际上存放的字符数目,例如:value长度为10,我存放8个字符,剩下位置为空,此时count的值就为8,而value.length()为10。

两个构造方法都不是public,他们都是被设计出来给子类使用的。

AbstractStringBuilder() {}

//初始化value
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}

还有两个用于返回长度的方法:

public int length() {
return count;
}
public int capacity() {
return value.length;
}

一个返回的是实际存放的字符数目,另一个返回的是内置字符数组的长度。

还有一个用于保证字符数组长度的方法,该方法和我们之前介绍的ArrayList中用于动态扩容的方法具有一样的功效。

void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity);
}

对于一个StringBuilder对象,我们可以不断的追加字符串到其中,这样就会遇到value数组长度不够的时候,该方法就是用于处理这种情况,在我们实际操作value数组之前,大多会调用该方法判断此次操作之后是否会导致数组溢出,如果是则会将原数组长度扩大两倍加上2并拷贝原数组中的内容到新数组中,然后才实际操作value数组。(此时的value数组已经被扩容了)。

这种动态扩容的思想还被用于ArrayList中,成为它和普通数组的核心优势。这种思想其实是一种折中的解决方案,它既避免了一次性创建很大的数组所导致的资源浪费,也解决了那种一旦创建就不能更改的静态局限性。这种思想值得我们学习和应用。

该类还提供一种方法,去除value数组中所有为空的元素:

public void trimToSize() {
if (count < value.length) {
value = Arrays.copyOf(value, count);
}
}

看个例子:

public static void main(String[] args){
StringBuilder sb = new StringBuilder();
sb.append("hello");
System.out.println(sb.length());
System.out.println(sb.capacity()); sb.trimToSize();
System.out.println(sb.length());
System.out.println(sb.capacity());
}

我们看输出结果:

5
16
5
5

需要解释下,没有显式指定value的长度,则会默认为16,程序为value的前5个位置赋值,后面的位置为空,所以我们看到第一次输出结果是有区别的。但是我们的trimToSize方法把没有使用的空位置全部清除,第二次输出结果显示capcity和length是一样的。

该类中其他的一些方法,例如:getChars,charAt等,这些方法都是很String类中对应的方法具有一样功效。下面我们主要看看一个重要的方法,append。该方法也具有相当多的重载,所以我们慢慢看。

public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}

append这个方法是我们使用StringBuilder时最常用到的一个方法,该方法用于追加一个字符串到原StringBuilder对象的尾部。该方法接过来一个String对象,如果为null将会调用appendNull方法把字符串“null”追加到原对象的末尾,否则将会把该字符串追加到原对象的末尾。

其他重载都是以各种各样的形式添加字符串到原StringBuilder对象的末尾,如果传入的是int,long,double,boolean等类型的参数,那么程序会将他们转换为字符串类型添加到末尾。我们也可以指定添加一个字符串的一部分,例如:

public AbstractStringBuilder append(char str[], int offset, int len)

至此,我们就已经完成AbstractStringBuilder这个超类的简单介绍,至于其中还有一些其他方法,我们将从他的实现类StringBuilder中继续介绍。下面我们看看有关StringBuilder类的一些常用方法的介绍。

二、多重载的构造函数

     StringBuilder除了封装了一个版本号,并没有封装任何其他的属性,甚至没有封装字符数组,那是因为它高度依赖他的父类,使用的是父类中封装的字符数组。包括他的构造函数也是调用的父类中的构造函数,例如:

public StringBuilder() {
super(16);
}
public StringBuilder(int capacity) {
super(capacity);
}
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}

这些构造函数会调用父类的一个构造函数为value字符数组初始化长度,如果没有显式传入需要设定的数组长度,则会默认为16,这一点我们在之前的实例中曾演示过。这些构造器主要完成的工作就是初始化一个字符数组。下面我们看StringBuilder的一个重要方法:append。

三、重要的append方法

     StringBuilder中的append方法都是重写了父类中的append方法:

@Override
public StringBuilder append(boolean b) {
super.append(b);
return this;
} @Override
public StringBuilder append(char c) {
super.append(c);
return this;
} @Override
public StringBuilder append(int i) {
super.append(i);
return this;
} @Override
public StringBuilder append(long lng) {
super.append(lng);
return this;
} @Override
public StringBuilder append(float f) {
super.append(f);
return this;
}

其实别看这么多重载,实际上都是相互调用,真正有用的就一两个。他们这些方法重载内部都会调用父类中相对应的方法,虽然没有接受返回值,但是父类方法完成了对value数组的赋值操作,最后调用完成append方法之后返回的是StringBuilder对象,这意味着我们可以连续调用append方法。

其实我们需要始终明白一点,StringBuilder和StringBuffer他们其实和String差不多,内部一样都是封装的字符数组,只不过StringBuilder实现了动态扩容机制,可以动态扩容并且可以动态更改value数组中的元素而已,但本质上都是一样的。

四、有关StringBuilder的一些其他使用细节

     首先我们看一个删除的方法,该方法可以指定删除StringBuilder对象中指范围内的子串。

public AbstractStringBuilder delete(int start, int end) {
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (end > count)
end = count;
if (start > end)
throw new StringIndexOutOfBoundsException();
int len = end - start;
if (len > 0) {
System.arraycopy(value, start+len, value, start, count-end);
count -= len;
}
return this;
}

该方法中最核心的方法是System.arraycopy,这个方法也被广泛使用在数组拷贝转移中。该方法将value数组索引位置为start+len开始以后的所有字符向前移动到start索引位置起,start+count-len终止位置处。需要记住的是,该方法并没有创建一个新数组,而是对原value数组进行移动元素来实现的。实际上该方法就是将即将被删除的子串后面的所有字符整体移动到被删除子串的开始位置。

value数组长度没有发生改变,只是用后面的子串覆盖了将要被删除的子串,然后count -= len;更新count,但是实际上并没有删除。但是count的值指定了该value数组中有效的字符数目,虽然没有具体删除该元素,但是在输出的时候只会把前count个字符作为有效字符输出。这就是delete的底层操作,包括其中的deleteCharAt删除指定位置的字符,原理都是一样的。

该类中还有一些replace,substring等方法,这些方法和我们之前曾介绍过的String类中相对应的方法底层实现都是类似,此处不再赘述了。下面我们看一个String类中没有的方法,insert。

public AbstractStringBuilder insert(int index, char[] str, int offset,int len)
{
if ((index < 0) || (index > length()))
throw new StringIndexOutOfBoundsException(index);
if ((offset < 0) || (len < 0) || (offset > str.length - len))
throw new StringIndexOutOfBoundsException(
"offset " + offset + ", len " + len + ", str.length "+ str.length);
ensureCapacityInternal(count + len);
System.arraycopy(value, index, value, index + len, count - index);
System.arraycopy(str, offset, value, index, len);
count += len;
return this;
}

有了之前delete方法学习的基础之后,这个插入的方法就很简单了。该方法接受四个参数,第一个参数表示要插入的索引位置,第二个参数表示要插入的字符数组或者字符串,第三个参数和第四个参数用于截取该原字符数组。核心方法依然是System.arraycopy,不过这里调用了两次,第一次的调用将index位置之后的所有字符往后移动len个长度(为即将插入的字符串留下空位置),第二次调用将该字符数组插入到预留位置。

该insert方法有很多重载,但是本质上都离不开我们上述介绍的这个方法。所以为了借阅篇幅,此处不再赘述。

至此,我们就简单介绍完成了StringBuilder的基本使用,理解不到之处,望大家指出,相互学习!

深入理解Java常用类-----StringBuilder的更多相关文章

  1. 深入理解Java常用类----String(二)

    上篇介绍了String类的构造器,获取内部属性等方法,最后留下了最常用的局部操作函数没有介绍,本篇将接着上篇内容,从这些最常见的函数的操作说起,看看我们日常经常使用的这些方法的内部是怎么实现的.第一个 ...

  2. 深入理解Java常用类----String

         Java中字符串的操作可谓是最常见的操作了,String这个类它封装了有关字符串操作的大部分方法,从构建一个字符串对象到对字符串的各种操作都封装在该类中,本篇我们通过阅读String类的源码 ...

  3. 深入理解Java常用类-----时间日期

    除了String这个类在日常的项目中比较常用之外,有关时间和日期的操作也是经常遇到的,本篇就讲详细介绍下Java API中对时间和日期的支持.其实在Java 8之前时间日期的API并不是很好用,以至于 ...

  4. Java 常用类总结(SE基础)

    本篇博客对java常用类相关知识进行了归纳总结,比较详细,适用于学习和复习. 1. 字符串相关的类 1.1 String String是一个final类,代表不可变的字符序列.不可被继承. Strin ...

  5. Java基础 —— Java常用类

    Java常用类: java.lang包: java.lang.Object类: hashcode()方法:返回一段整型的哈希码,代表地址. toString()方法:返回父类名+"@&quo ...

  6. Java常用类学习笔记总结

    Java常用类 java.lang.String类的使用 1.概述 String:字符串,使用一对""引起来表示. 1.String声明为final的,不可被继承 2.String ...

  7. Java常用类的使用

    Java常用类 1. Optional 在我们的开发中,NullPointerException可谓是随时随处可见,为了避免空指针异常,我们常常需要进行 一 些防御式的检查,所以在代码中常常可见if( ...

  8. Java常用类之要点总结

    Java常用类之要点总结

  9. Java常用类:包装类,String,日期类,Math,File,枚举类

    Java常用类:包装类,String,日期类,Math,File,枚举类

随机推荐

  1. 解决IE6下 PNG图片有背景问题

    IE6下有时候png格式的图片会存在背景的问题,以下是我常用的解决办法: <!--[if IE 6]> <script src="js/DD_belatedPNG_0.0. ...

  2. DataTable 转实体

    因为Linq的查询功能很强大,所以从数据库中拿到的数据为了处理方便,我都会转换成实体集合List<T>. 开始用的是硬编码的方式,好理解,但通用性极低,下面是控件台中的代码: using ...

  3. 【one day one linux】grep工具

    grep  筛选功能 学习这些命令采用20/80原则,这样,可以达到使用%20的命令选项,处理80%的情况. #grep 的使用格式 grep [option] pattern file 那么接下来看 ...

  4. CTF中怎看phpinfo

    CTF中怎么看phpinfo 在比赛中经常遇到phpinfo,这个页面可以看到很多配置信息,我们需要在这么多信息中,着重看一下几个内容: 1.allow_url_fopen和allow_url_inc ...

  5. python3 selenium 随机选择同一类型下的某一个元素

    使用场景: 如上图所示,有时候,我们测试的时候,不会每个方向都选择一遍,也不能每次都选择一个方向,这个时候就需要每次运行用例的时候,随机选择一个方向来测试 使用方法: random.randint() ...

  6. java集合系列

    工作以来,一直对java集合理解的不够全面,不够深入,就是常用的ArrayList/HashMap/Set/List,有时候会用一下LinkedList.一时兴起,可能对TreeSet,HashSet ...

  7. mysql获取当前日期的周一和周日的日期

    ,,date_format(curdate(),)//获取当前日期 在本周的周一 的日期 ,,date_format(curdate(),)//获取当前日期 在本周的周日 的日期

  8. POJ2635-The Embarrassed Cryptographer 大数求余

    题目链接:http://poj.org/problem?id=2635 题目分析: http://blog.csdn.net/lyy289065406/article/details/6648530

  9. Asp.net mvc 小试牛刀一:多语言支持

    最近因为项目需要又从UWP开发转到了Asp.net mvc 开发,由于也不是什么老手,所以就将项目常见的一些技术问题记录一下自己的解决方案. 第一个需求:用户可以自由切换界面显示语言. 解决方案一:界 ...

  10. 从C++ int类型的变量范围谈起

    从学习C语言开始,int类型所占字节数,以及数值范围就是一个挥之不去的问题.一开始会死记硬背一个char 1个字节,一个字节8个bit.64位机器上面一个int 4个字节,32位机器上面不一样.那时候 ...