Java知识——基础知识

        本文是我自己在秋招复习时的读书笔记,整理的知识点,也是为了防止忘记,尊重劳动成果,转载注明出处哦!如果你也喜欢,那就点个小心心,文末赞赏一杯豆奶吧,嘻嘻。 让我们共同成长吧……


一、字符串

        Java中的字符串处理主要有下面三个类来处理的:StringStringBufferStringBuilder

1、String

    1、String简介

    初始化:

    一般由String声明的字符串,长度是不可变的,这也是它与StringBuffer和StringBuilder最直观的一个区别。一般初始化方式:String s = "hello world";经过这条语句,JVM的栈内存中产生一个s变量,堆内存中产生hello world字符串对象。s指向了hello world的地址。像上面这种方式产生的字符串属于直接量字符串对象,JVM在处理这类字符串的时候,会进行缓存,产生时放入字符串池,当程序需要再次使用的时候,无需重新创建一个新的字符串,而是直接指向已存在的字符串。看下面程序:

public  class  String  Test4{

    public  static  void  main(String[] args){

        String s ="hello world";

        String s2 ="hello world";

        System.out.println(s == s2);

    }

}

    该程序输出:true 因为s和s2都指向了hello world字符串,它们的地址是同一个。 我们常说,String的一个很大的特点,就是它是一个“不可变的字符串”,就是说,当一个String对象完成创建后,该对象的内容就固定下来了,但是为什么还会有下面这种情况呢?

public  class  String  Init{

    public  static  void  main(String[] args){

        String str ="I like";//---------1--------

        System.out.println(System.identityHashCode(str));

        str = str +"java";//--------2---------

        System.out.println(System.identityHashCode(str));

    }

}

该程序输出:

14576877

12677476

    说明:str似乎是变了,这是为什么呢?其实是这样的:str只是一个引用变量,当程序执行完1后,str指向“I like”。当程序执行完2之后,连接运算符会将两个字符串连在一起,并且让str指向新的串:"I like java",所以,从这里应该可以看得出来,最初的对象确实没有改变,只是str所指向的对象在不断改变。

    String对象的另一种初始化方式,就是采用String类提供的构造方法进行初始化。String类提供了16种构造方法,常用的有五种:

    String() --------- 初始化一个String对象,表示一个空字符序列

    String(String value) --------- 利用一个直接量创建一个新串

    String(char[] value) --------- 利用一个字符数组创建

    String(char[] value,int offset,int count) --------- 截取字符数组,从offset开始count个字符创建

    String(StringBuffer buffer) --------- 利用StringBuffer创建

形如:

    String s = new String();

    String s1 = new String(“hello”);

    char[] c = {'h','e','l','l','o'};

    String s2 = new String(c);

    String s3 = new String(c,1,3);

    以上就是String类的基本初始化方法。

2、String类的一些常用方法

    字符串是最常用的对象,所以,我们有必要彻底的了解下它,下面我会列举常用的字符串里的方法,因为有很多,就不一一列举。

    -------public int length()--------

该方法用于获取字符串的长度,实现如下:

    int  length(){

        return count;

    }

    这是JDK种的原始实现,count在String类里被定义为一个整型常量:private final int count;并且不论采用哪种构造方法,最终都会为count赋值。

使用方法:

    String s ="hello world";

    int length = s.length();

这个比较简单。

-----------public boolean equals(Object anObject)-----------

该方法用于比较给定对象是否与String相等。

JDK里是这样实现的:

public  boolean  equals(Object anObject){

        if(this== anObject) {

        return  true;

}

if(anObjectinstanceofString) {

    String anotherString = (String)anObject;

int n = count;

if(n == anotherString.count) {

char  v1[] = value;//---------1---------

char  v2[] = anotherString.value;//-------2----------

int  i = offset;

int  j = anotherString.offset;

while(n-- !=0) {

if(v1[i++] != v2[j++])

return  false;

}

return  true;

    }

}

return  false;

    }

从1和2处也看出来,String的底层是基于字符数组的。我们可以像下面这种方式使用equals():

String s1 =newString("hello world");

String s2 =newString("hello world");

String s3 =newString("hello");

System.out.println(s1.equals(s2));;

System.out.println(s1.equals(s3));

结果输出:

true

false

此处插入一个很重要的知识点,重写equals()的一般步骤及注意事项:

1. 使用==操作符检查“实参是否为指向对象的一个引用”。

2. 使用instanceof操作符检查“实参是否为正确的类型”。 

3. 把实参转换到正确的类型。 

4. 对于该类中每一个“关键”域,检查实参中的域与当前对象中对应的域值是否匹配。

a.对于既不是float也不是double类型的基本类型的域,可以使用==操作符进行比较

b.对于对象引用类型的域,可以递归地调用所引用的对象的equals方法

 c.对于float类型的域,先使用Float.floatToIntBits转换成int类型的值,然后使用==操作符比较int类型的值

d.对于double类型的域,先使用Double.doubleToLongBits转换成long类型的值,然后使用==操作符比较long类型的值。

5. 当你编写完成了equals方法之后,应该问自己三个问题:它是否是对称的、传递的、一致的?(其他两个特性通常会自行满足)如果答案是否定的,那么请找到这些特性未能满足的原因,再修改equals方法的代码。

稍后我再做说明,请先再看个例子:

public  class  String  Init{

public  static  void  main(String[] args){

String s ="hello world";

String s1 =newString("hello world");

String s2 =newString("hello world");

String s3 =newString("hello");

String s4 ="hello world";

System.out.println(s.equals(s1));;

System.out.println(s1.equals(s2));

System.out.println(s1.equals(s3));

System.out.println("------------------");

System.out.println(s == s1);

System.out.println(s == s3);

System.out.println(s == s4);

}

}

输出:

true

true

false

------------------

false

false

true

此处验证了一个问题,就是比较方法equals()和==的区别,一句话:equals()比较的是对象的内容,也就是JVM堆内存中的内容,==比较的是地址,也就是栈内存中的内容。

如上述代码中,s、s1、s2、s4他们四个String对象的内容都是"hello world",所以,用equals()比较他们,返回的都是true。但是,当s和s1用==比较时,却返回false,因为二者在堆中开辟的地址不一样,所以,返回的肯定是false。而为什么s和s4用==比较时,返回的是true呢,因为上文中提到过,直接量的字符串会产生缓存池,所以,当声明s4的时候,编译器检测到缓存池中存在相同的字符串,所以就直接使用,只要将s4指向s所指向的字符串就行了,二者指向同一字符串,所以地址当然相等!

注意:此处隐藏着一个比较细的编程习惯,尤其是用==进行比较的时候,尽量将常量放在==的左边,因为我们有的时候,会不小心将==写成=,这样的话,如果将常量放在左边,编译器会报错,提醒你,但是,如果将变量放在左边,常量放右边,即使你写成了=,编译器默认为变量赋值了,因此也不会报错。

因为String类实现了public interface Comparable,而Comparable接口里有唯一的方法:public int compareTo(T o)。所以,String类还有另一个字符串比较方法:compareTo()

-----------------public int compareTo(String anotherString)---------------

compareTo()可实现比较两个字符串的大小,源码如下:

publicintcompareTo(String anotherString){

intlen1 = count;

intlen2 = anotherString.count;

intn = Math.min(len1, len2);

charv1[] = value;

charv2[] = anotherString.value;

inti = offset;

intj = anotherString.offset;

if(i == j) {

intk = i;

intlim = n + i;

while(k < lim) {

charc1 = v1[k];

charc2 = v2[k];

if(c1 != c2) {

returnc1 - c2;

}

k++;

    }

}else{

while(n-- !=0) {

charc1 = v1[i++];

charc2 = v2[j++];

if(c1 != c2) {

returnc1 - c2;

}

    }

}

returnlen1 - len2;

    }

compareTo是怎么实现的呢?

首先,会对两个字符串左对齐,然后从左到右一次比较,如果相同,继续,如果不同,则计算不同的两个字符的ASCII值的差,返回就行了。与后面的其他字符没关系。

举个例子:

public  class   CompareToTest{

public  static  void  main(String[] args){

String s ="hallo";

String s2 ="ha";

String s3 ="haeeo";

inta = s.compareTo(s2);

System.out.println("a:"+a);

intb = s.compareTo(s3);

System.out.println("b:"+b);

intc = s2.compareTo(s3);

System.out.println("c:"+c);

}

}

程序输出:

a:3

b:7

c:-3

s和s2相比,前两个相同,如果是这种情况,则直接返回length1-length2

s和s3相比,前两个相同,不用管,直接用第三个字符的ASCII码做差就行了。所以'l'-'a'=7

此处网友“handsomeman_wei”问我源码中的c1-c2理解不了,就是上面红字部分的解释。

s2和s3相比,同第一种情况一样,只是length1比length2小,因此值为负数。

-----------public char charAt(int index)-----------

获取指定位置的字符,比较容易理解,源码为:

public  char   charAt(intindex){

if((index <0) || (index >= count)) {

throw  new  StringIndexOutOfBoundsException(index);

        }

return  value[index + offset];

    }

String s = "hallo";

char a = s.charAt(2);

System.out.println(a);

输出:l

注意:参数index的值从0到字符串的长度-1,所以,如果值不在这个范围内,如下:

String s = "hallo";

char a = s.charAt(8);

System.out.println(a);

则报错:

Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 8

at java.lang.String.charAt(String.java:686)

at com.xtfggef.string.CompareToTest.main(CompareToTest.java:20)

与charAt()相对应的是indexOf():根据给定的字符串,返回他的位置。

indexOf()有多个参数:

public int indexOf(int ch)

public int indexOf(int ch, int fromIndex)

public int indexOf(String str)

public int indexOf(String str, int fromIndex)

static int indexOf(char[] source, int sourceOffset, int sourceCount,

char[] target, int targetOffset, int targetCount,

int fromIndex)

有兴趣的自己去试试,这儿就不多阐述了。

-----------substring()------------

publicStringsubstring(intbeginIndex){

returnsubstring(beginIndex, count);

    }

用于截取字符串,此处与另一个方法对比:

publicStringsubstring(intbeginIndex,intendIndex){

if(beginIndex <0) {

thrownewStringIndexOutOfBoundsException(beginIndex);

}

if(endIndex > count) {

thrownewStringIndexOutOfBoundsException(endIndex);

}

if(beginIndex > endIndex) {

thrownewStringIndexOutOfBoundsException(endIndex - beginIndex);

}

return((beginIndex ==0) && (endIndex == count)) ?this:

newString(offset + beginIndex, endIndex - beginIndex, value);

    }

前者调用后者来实现,前者截取从指定位置到字符串结束的子字符串,后者截取从指定位置开始,到endIndex-1位置的子字符串。

publicclassCompareToTest{

publicstaticvoidmain(String[] args){

String s ="helloworld";

String s1 = s.substring(2);

String s2 = s.substring(2,7);

String s3 = (String) s.subSequence(2,7);

System.out.print("s1:"+s1+"\n"+"s2:"+s2+"\n"+"s3:"+s3);

}

}

输出:

s1:lloworld

s2:llowo

s3:llowo

细心的读者应该看出来,该类里面包含一个subSequence(),而且该方法与substring(int,int)返回的结果一样,观察下源码,不难发现的区别:

publicCharSequencesubSequence(intbeginIndex,intendIndex){

returnthis.substring(beginIndex, endIndex);

    }

    }

其实subSequence()内部就是调用的substring(beginIndex, endIndex),只是返回值不同。

subString返回的是String,subSequence返回的是实现了CharSequence接口的类,也就是说使用subSequence得到的结果,只能使用CharSequence接口中的方法。不过在String类中已经重写了subSequence,调用subSequence方法,可以直接转为String对象,如我们例子中的做法。

-----------------public String replace(char oldChar, char newChar)和public String replaceAll(String regex, String replacement)-------------------

publicStringreplace(charoldChar,charnewChar){

if(oldChar != newChar) {

intlen = count;

inti = -1;

char[] val = value;/* avoid getfield opcode */

intoff = offset;/* avoid getfield opcode */

while(++i < len) {

if(val[off + i] == oldChar) {

break;

}

    }

if(i < len) {

charbuf[] =newchar[len];

for(intj =0; j < i ; j++) {

    buf[j] = val[off+j];

}

while(i < len) {

charc = val[off + i];

    buf[i] = (c == oldChar) ? newChar : c;

    i++;

}

returnnewString(0, len, buf);

    }

}

returnthis;

    }

publicStringreplaceAll(String regex, String replacement){

returnPattern.compile(regex).matcher(this).replaceAll(replacement);

    }

前者参数为两个字符串,用newChar替换原串里的所有oldChar。

后者从第一个参数可以看出,需要替换的东西可以用正则表达式描述。例子如下:

packagecom.xtfggef.string;

publicclassReplaceTest{

publicstaticvoidmain(String[] args){

String s ="hello world";

String s1 = s.replace("l","d");

System.out.println(s1);

String s2 ="a78e5opx587";

String s3 = s2.replaceAll("[0-9]","");//用空串替换原串里所有的0-9的数字

System.out.println(s3);

}

}

输出:

heddo wordd

aeopx

-------------public String[] split(String regex)-----------

该方法用于分割字符串,得到一个String类型的数组,根据regex可知,参数是个正则表达式。请看下面的例子:

packagecom.xtfggef.string;

publicclassSpiltTest{

publicstaticvoidmain(String[] args){

String s ="hello world";

String s1 ="hello.worldd";

String[] s2 = s.split(" ");

String[] s3 = s1.split("\\.");

for(inti=0; i

System.out.print(s2[i]+" ");

}

System.out.println();

for(intj=0; j

System.out.print(s3[j]+" ");

}

}

}

输出:

hello world 

hello worldd

关于spilt()的其他重载方法,可参见JDK的String类的实现。

spilt()需要注意的事项,就是当分隔符为 . 的话,处理起来不一样,必须写成\\.因为.是正则表达式里的一个特殊符号,必须进行转义

--------------------public native String intern();--------------------(补充知识点:经网友java2000_wl提醒,特此补充,欢迎广大读者及时提出建议,我必将虚心接受!)

intern()方法和前面说的equals()方法关系密切,从public native String intern()看出,它是Java的本地方法,我们先来看看Java文档里的描述:

Returns a canonical representationforthe string object.

    A pool of strings, initially empty, is maintained privately by the

classString.Whentheinternmethodisinvoked,ifthepoolalreadycontainsa

stringequaltothisStringobjectasdeterminedby

theequals(Object)method,thenthestringfromthepoolis

returned.Otherwise,thisStringobjectisaddedtothe

poolandareferencetothisStringobjectisreturned.

Itfollowsthatforanytwostringssandt,

s.intern()==t.intern() istrueifand onlyifs.equals(t) istrue.

    All literal strings and string-valued constant expressions are interned.

@returna string that has the same contents asthisstring, but is

    guaranteed to be from a pool of unique strings.

意思就是说,返回字符串一个规范的表示。进一步解释:有两个字符串s和t,s.equals(t),则s.intern()==t.intern().举个例子:

publicclassStringTest{

publicstaticvoidmain(String[] args){

String s =newString("abc");

String s1 ="abc";

String s2 ="abc";

    String s3 = s.intern();

System.out.println(s == s1);//false

System.out.println(s == s2);//false

System.out.println(s == s3);//false

System.out.println(s1 == s3);//true     

}

}

输出结果如注释所示,前两个结果前面已经说的很清楚了,现在拿最后一个说明,首先看看s3 = s.intern()这句,当调用s.intern()这句的时候,先去字符串常量池中找,是否有abc这个串,如果没有,则新增,同时返回引用,如果有,则返回已经存在的引用,此处s1和s2都指向常量池中的abc对象,所以此处是存在的,调用s.intern()后,s3和s1、s2指向同一个对象,所以s1==s3返回的是true。

intern()做到了一个很不寻常的行为:在运行期动态的在方法区创建对象,一般只有像new关键字可以在运行期在堆上面创建对象,所以此处比较特殊。属于及时编译的概念。

一般常见的字符串处理函数就这些,其它的还有很多,就不一一列举。

3、一些常见的问题,处理结果

在我们日常的开发中,总会遇到一些问题,在此我总结一下:

String s = "123" + "456"内存中产生几个字符串对象?

这是个比较有争议的问题,面试的时候,老师还挺喜欢问,论坛上大家说几个的也有,我给大家分析一下,因为我们前面有提到Java字符串的缓存机制,编译器在编译的时候会进行优化,所以在编译的过程中123和456被合成了一个字符串"123456",因此,如果缓存池中目前没有123456这个对象,那么会产生一个,即""123456",且栈中产生一个引用s指向它,如果缓存池中已经存在"123456",那么将产生0个对象,直接用s指向它。

如果spilt()函数的参数在要分割的字符串中没有怎么办?如String s = "helloworld" ,我现在调用String[] s2 = s.spilt("abc"),返回什么?

这个问题是我曾经参加红帽软件面试的时候遇到的相关题,当时懵了,像这样的题目,如果不亲自遇到过,或者看过源代码,很难准确的写出来。

做一个简单的测试,就可以看得出来:

packagecom.xtfggef.string;

publicclassStringSpilt{

publicstaticvoidmain(String[] args){

String s ="helloworld";

String[] s2 = s.split("abc");

for(inti =0; i < s2.length; i++) {

System.out.println(s2[i] +" "+ i);

}

}

}

输出:helloworld 0

说明当遇到源字符串中没有的字符时,会把它整个串放入到数组中。spilt()的内部实现还是挺复杂的,多层嵌套,不便于放到这儿分析。

关于字符串自动类型转换分析

首先看一下题的类型:

inti =2;

intj =3;

String s ="9";

System.out.println(i+j+s);

System.out.println("-----------------------");

System.out.println(i+s+j);

以上运算各输出什么?不妨猜猜

59

-----------------------

293

首先i+j=5,然后5和9自然连接,这里涉及到java的自动类型转换,此处int型的直接转成String类型的。第二个依次连接,都转化为String类型的了。

补充(细节):看下面的程序:

String s ="ab";

String s1 ="a";

String s2 = s1 +"b";

String s3 ="ab";

System.out.println(s == s2);//false

System.out.println(s2 == s3);//false

System.out.println(s2.hashCode() == s3.hashCode());

String s4 ="ad";

String s5 ="a"+"d";

String s6 ="ad";

System.out.println(s4 == s5);//true

System.out.println(s4 == s6);//true

此处主要是想说明:s1+"b"和"a"+"b"的不同,再看一段代码:

                  System.out.println(s1.hashCode());

System.out.println(s2.hashCode());

System.out.println(s3.hashCode());

System.out.println(s4.hashCode());

System.out.println(s5.hashCode());

输出:

97

3105

3105

3107

3107

说明s1+"b"的过程创建了新的对象,所以地址不一样了。所以用==比较的话,返回的是false。

此处继续补充:为什么s1+"b"会产生新的对象?而没有去常量池查找是否已经存在ab对象,以致于s==s2返回false。因为我们说过常量池(下文会讲常量池)是在编译期确定好的,所以如果我们的语句时String s5 = "ab"的话,这个是在编译期确定的,会去常量池查找,而此处我们的语句时s2 = s1+"b",s2的值只有在运行期才能确定,所以不会去常量池查找,也就是产生新串。再次提问:那么这里s2的值是在哪儿分配的呢?堆、JVM栈还是运行时常量池?正确回答:s2在堆上分配,因为+的内部实现是用StringBuilder来实现的。String s2 = s1+"b" 内部是这样实现的:String s2 = new StringBuilder(s1).append("b").toString();所以是在堆上来分配的

此处网友cowmich补充:调用s2.hashCode() == s3.hashCode()返回true。我解释下:

==比较的是他们的地址,s1+"b"会产生一个新的串,所以和s和s2用==比,返回false,如果用equals的话,返回肯定是true,因为equals()比较的是对象的内容(String类是这样的)。至于hashCode,是这样的:如果没有重写Object的hashCode(),那么如果对象调用equals()放回true,则这两个对象调用hashCode()后返回的整数一定相等。此处继续补充:对于Object类而言,原生的equals()方法,必须两个对象的地址和内容都一样才返回true,同时Object类原生的hashCode()是参照对象的地址和内容根据一定的算法生产的。所以原生的hashCode()只有调用equals()返回true才相等。而String类不同,String类重写了Object的equals(),放松了条件,只要对象地址或者内容相等就返回true,我们看看源码:

publicbooleanequals(Object anObject){

if(this== anObject) {

returntrue;

}

if(anObjectinstanceofString) {

    String anotherString = (String)anObject;

intn = count;

if(n == anotherString.count) {

charv1[] = value;

charv2[] = anotherString.value;

inti = offset;

intj = anotherString.offset;

while(n-- !=0) {

if(v1[i++] != v2[j++])

returnfalse;

}

returntrue;

    }

}

returnfalse;

    }

同时,String类重写了hashCode()方法,只要内容相等,则调用hashCode返回的整数值也相等,所以此处:s3和s2虽然地址不等,但是内容相等,所以会有:s2.hashCode() == s3.hashCode()返回true。但是这句话反过来讲就不一定成立了,因为毕竟hashCode()只是一种算法。继续补充:刚刚说了Object类和String类,此处补充下Integer类:Integer类,返回的哈希码就是Integer对象里所包含的那个整数的数值,例如Integer a=new Integer(50),则a.hashCode的值就是50 。由此可见,2个一样大小的Integer对象,返回的哈希码也一样。

补充:应网友  KingBoxing  的要求,我做下关于常量池、字符串常量池、运行时常量池的介绍:

常量池一般就是指字符串常量池,是用来做字符串缓存的一种机制,当我们在程序中写了形如String s = "abc"这样的语句后,JVM会在栈上为我们分配空间,存放变量s和对象”abc“,当我们再次需要abc对象时,如果我们写下:String s1 = "abc"的语句时,JVM会先去常量池中找,如果不存在,则新创建一个对象。如果存在,则直接将s1指向之前的对象”abc“,此时,如果我们用==来判断的话,返回的true。这样做的好处就是节省内存,系统响应的速度加快,(因为省去了对象的创建时间)这也是缓存系统存在的原因。常量池是针对在编译期间就确定下来的常量而言的,如上所说的String类的一些对象。但是,当类被加载后,常量池会被搬到方法区的运行时常量池,此时就不再是静态的了,那么是不是就不能向常量池中添加新的内容了呢(因为我们刚刚说过,常量池是在编译期确定好的)?答案是否定的,我们依然可以在运行时向常量池添加内容!这就是我们说过的String类有个方法叫intern(),它可以在运行时将新的常量放于常量池。因为我在上文中已经详细介绍过intern(),此处不再赘述!

2、StringBuffer、StringBuilder

     1、初始化

        StringBuffer和StringBuilder就是所谓的可变字符串类,共四个构造方法:

        StringBuffer()

        public StringBuffer(int paramInt)

        public StringBuffer(String paramString)

        public StringBuffer(CharSequence paramCharSequence)

        观察其源码发现,使用StringBuffer()时,默认开辟16个字符的长度的空间,使用public StringBuffer(int paramInt)时开辟指定大小的空间,使用public StringBuffer(String paramString)时,开辟paramString.length+16大小的空间。都是调用父类的构造器super()来开辟内存。这方面StringBuffer和StringBuilder都一样,且都实现AbstractStringBuilder类。

     2、主要方法

        二者几乎没什么区别,基本都是在调用父类的各个方法,一个重要的区别就是StringBuffer是线程安全的,内部的大多数方法前面都有关键字synchronized,这样就会有一定的性能消耗,StringBuilder是非线程安全的,所以效率要高些。

publicstaticvoidmain(String[] args)throwsException{

String string ="0";

intn =10000;

longbegin = System.currentTimeMillis();

for(inti =1; i < n; i++) {

string += i;

}

longend = System.currentTimeMillis();

longbetween = end - begin;

System.out.println("使用String类耗时:"+ between+"ms");

intn1 =10000;

StringBuffer sb =newStringBuffer("0");

longbegin1 = System.currentTimeMillis();

for(intj =1; j < n1; j++) {

sb.append(j);

}

longend1 = System.currentTimeMillis();

longbetween1 = end1 - begin1;

System.out.println("使用StringBuffer类耗时:"+ between1+"ms");

intn2 =10000;

StringBuilder sb2 =newStringBuilder("0");

longbegin2 = System.currentTimeMillis();

for(intk =1; k < n2; k++) {

sb2.append(k);

}

longend2 = System.currentTimeMillis();

longbetween2 = end2 - begin2;

System.out.println("使用StringBuilder类耗时:"+ between2+"ms");

}

输出:

使用String类耗时:982ms

使用StringBuffer类耗时:2ms

使用StringBuilder类耗时:1ms

虽然这个数字每次执行都不一样,而且每个机子的情况也不一样,但是有几点是确定的,String类消耗的明显比另外两个多得多。还有一点就是,StringBuffer要比StringBuilder消耗的多,尽管相差不明显。

接下来介绍一些常用的方法。

-----------------------public synchronized int length()--------------------------

-------------------------public synchronized int capacity()---------------------------

二者都是获取字符串的长度,length()获取的是当前字符串的长度,capacity()获取的是当前缓冲区的大小。举个简单的例子:

StringBuffer sb =newStringBuffer();

System.out.println(sb.length());;

System.out.println(sb.capacity());

输出:

0

16

StringBuffer sb =newStringBuffer("hello");

System.out.println(sb.length());;

System.out.println(sb.capacity());

输出:

5

21

因为默认分配16个字符大小的空间,所以不难解释上面的结果。

------------------public boolean equals(Object paramObject)---------------------

StringBuffer sb =newStringBuffer("hello");

StringBuffer sb2 =newStringBuffer("hello");

System.out.println(sb.equals(sb2));

以上程序输出false,是不是有点惊讶?记得之前我们的文章说过,equals()比较的是字符串的内容,按理说此处应该输出的是true才对。

究其原因,String类重写了Object的equals(),所以只需要看内容是否相等即可,但是StringBuffer没有重写equals(),此处的equals()仍然是调用的Object类的,所以,调用StringBuffer类的equals(),只有地址和内容都相等的字符串,结果才会返回true。

另外StringBuffer有一系列追加、插入、删除字符串的方法,首先append(),就是在原来的字符串后面直接追加一个新的串,和String类相比有明显的好处:

String类在追加的时候,源字符串不变(这就是为什么说String是不可变的字符串类型),和新串连接后,重新开辟一个内存。这样就会造成每次连接一个新串后,都会让之前的串报废,因此也造成了不可避免的内存泄露。

//append()

StringBuffer sb =newStringBuffer("helloworld, ");

sb.append("I'm ").append("erqing ").append("who ").append("are you ?");

System.out.println(sb);

//public synchronized StringBuffer insert(int paramInt, Object paramObject)

sb.insert(12,/*9*/"nice! ");

System.out.println(sb);

//public synchronized StringBuffer reverse()

sb.reverse();

System.out.println(sb);

sb.reverse();

System.out.println(sb);

//public synchronized StringBuffer delete(int paramInt1, int paramInt2)

//public synchronized StringBuffer deleteCharAt(int paramInt)

sb.delete(12,18);

System.out.println(sb);

sb.deleteCharAt(5);

System.out.println(sb);

输出:

helloworld, I'm erqing who are you ?

helloworld, nice! I'm erqing who are you ?

? uoy era ohw gniqre m'I !ecin ,dlrowolleh

helloworld, nice! I'm erqing who are you ?

helloworld, I'm erqing who are you ?

helloorld, I'm erqing who are you ?

-----------------public synchronized void trimToSize()---------------------

该方法用于将多余的缓冲区空间释放出来。

StringBuffer sb =newStringBuffer("hello erqing");

System.out.println("length:"+sb.length());

System.out.println("capacity:"+sb.capacity());

sb.trimToSize();

System.out.println("trimTosize:"+sb.capacity());

输出:

length:12

capacity:28

trimTosize:12

StringBuffer类还有很多方法,关于字符查找,截取,替换方面的方法,有兴趣的童鞋可以去研究研究源码,定会学到不少知识!

三、字符串处理类StringTokenizer

StringTokenizer是java.util包下的一个类,用来对字符串做简单的处理。

举个简单的例子:

String s ="Tonight is the answer !";

StringTokenizer st =newStringTokenizer(s," ");

intcount = st.countTokens();

System.out.println("个数为:"+count);

while(st.hasMoreTokens()) {

String token = st.nextToken();

System.out.println(token);

}

输出:

个数为:5

Tonight

is

the

answer

!


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容

  • package cn.itcast_01;/* 字符串:就是由多个字符组成的一串数据。也可以看成是一个字符数组。 ...
    蛋炒饭_By阅读 582评论 0 0
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,598评论 18 399
  • java中String的常用方法 1、length()字符串的长度 例:char chars[]={'a','b'...
    赤赤有名阅读 2,038评论 0 10
  • 从网上复制的,看别人的比较全面,自己搬过来,方便以后查找。原链接:https://www.cnblogs.com/...
    lxtyp阅读 1,345评论 0 9
  • 今天过得很充实,早上卸了一车的电视机,有6000/8000/610的,看到我们康佳的大仓库,都是满满的钱啊 下午去...
    阿立立哥阅读 74评论 0 0