Java基础--------字符串操作

目录

一. 字符串(String类)

1.1 字符串的创建 

1.2 String类中的方法

1.2.1 先查看String类中一些方法实现的源代码

1.2.2 字符串比较

1.2.3 hashCode() 方法

1.2.4 String类中其它的常用方法

二. 字符串缓冲类(StringBuilder和StringBuffer)

2.1 深入理解String、StringBuilder、StringBuffer 

2.2 不同场景下String、StringBuilder、StringBuffer的性能测试 

三. 字符串分解类(StringTokenizer类) 

3.1 StringTokenizer的功能

3.2 StringTokenizer的方法

四. 正则表达式(Regular Expression) 

4.1 概念

4.2 Java常用正则表达式验证工具类RegexUtils.java


一. 字符串(String类)

字符是一种通用数据类型,是用单引号括起来的一个字符,如 'a'、'A'、'C'。而跟字符密切相关的通用数据类型是字符串。

字符串常量是用双引号括起来的一串字符,如"你好"、"Hello World !\n"。在Java中,字符串常量是作为String类的一个特定的对象来处理的,而不是一个数据。

1.1 字符串的创建 

package cn.test_01;
/*
 * 字符串:就是由多个字符组成的一串数据。也可以看成是一个字符数组。
 * 通过查看API,我们可以知道
 *         1:字符串字面值"abc"也可以看成是一个字符串对象。
 *         2:字符串是常量,一旦被赋值,就不能被改变。 
 * 构造方法:
 *         public String() 无参构造
 *         public String(String original) 把字符串常量值转成字符串
 * 字符串的方法:
 *         public int length() 返回此字符串的长度。字符串中有.length()方法,而数组中有.length属性
 */
public class StringTest {
    public static void main(String[] args) {		
//方法一
        String s1 = "abcde"; //字符串后面的值"abc"可以看成是一个字符串对象
        System.out.println("s1:"+s7);
        System.out.println("s1.length():"+s1.length());
	System.out.println("--------------------------");
		
//方法二
        String s2= new String(); //通过无参构造方法,创建一个字符串对象
        System.out.println("s2:" + s2);
        System.out.println("s2.length():" + s2.length());
        System.out.println("--------------------------");
         
//方法三
        //public String(String original)  把字符串常量值转成字符串
        String s3 = new String("abcde"); //通过有参构造方法,创建一个字符串对象
        System.out.println("s3:" + s3);
        System.out.println("s3.length():" + s3.length());
        System.out.println("--------------------------"); 
		
//方法四
	char [] bunch = {'H','e','l','l','o'};
        String s4 = new String(bunch); //通过使用一个字符数组语句来创建字符串的对象
        System.out.println("s4:" + s4);
        System.out.println("s4.length():" + s4.length()); 
    }
}
字符串是常量,一旦被赋值,就不能被改变。 

想要了解一个类,最好的办法就是看这个类的实现源代码,

String类的源码在\jdk1.6.0_14\src\java\lang\String.java 文件中。 属于java.lang包

String类本质上是字符数组char[] ,private final char value[];,并且其值不可改变。字符串一旦被赋值,就不能被改变。而且String类是final的,不可被继承,完整语句是public final class String。String类对象有个特殊的创建的方式,就是直接指定,比如String x = "abc","abc"就表示一个字符串对象。而x是"abc"对象的地址,也叫做"abc"对象的引用。String对象可以通过“+”串联,串联后会生成新的字符串。比如String s3 = "ab" + "c"; ,也可以通过concat()方法来串联。

Java基础--------字符串操作_第1张图片

字符串被赋值之后,它在对应方法区中字符串常量池里面的数值就不能被改变,但是这个字符串在栈中所指向方法区的地址(引用),是可以改变的。 

1.2 String类中的方法

1.2.1 先查看String类中一些方法实现的源代码

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); //新的字符串
    }
 
 public String concat(String str) {
    int otherLen = str.length();
    if (otherLen == 0) {
        return this;
    }
    char buf[] = new char[count + otherLen];
    getChars(0, count, buf, 0);
    str.getChars(0, otherLen, buf, count);
    return new String(0, count + otherLen, buf); //新的字符串
    }
 
 public String replace(char oldChar, char newChar) {
    if (oldChar != newChar) {
        int len = count;
        int i = -1;
        char[] val = value; /* avoid getfield opcode */
        int off = offset;   /* avoid getfield opcode */
 
        while (++i < len) {
        if (val[off + i] == oldChar) {
            break;
        }
        }
        if (i < len) {
        char buf[] = new char[len];
        for (int j = 0 ; j < i ; j++) {
            buf[j] = val[off+j];
        }
        while (i < len) {
            char c = val[off + i];
            buf[i] = (c == oldChar) ? newChar : c;
            i++;
        }
        return new String(0, len, buf); //新的字符串
        }
    }
    return this;

从上面的三个方法可以看出,无论是sub操作、concat还是replace操作都不是在原有的字符串上进行的,而是重新生成了一个新的字符串对象。也就是说进行这些操作后,最原始的字符串并没有被改变。在这里要永远记住一点:“对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象”。

1.2.2 字符串比较

比较两个字符串的内容是否一样,可以使用String类提供的equals()和equalsIgnoreCase()两个方法。equals()测试两个字符串的内容是否完全一样,而equalsIgnoreCase()是忽略字符串中的大小写来比较两个字符串。因为Java是区分大小写的,所以两者比较的结果是不一样的。

如果要比较两个字符串的大小关系,即它们按字母顺序排列的时候谁在前谁在后。String 类提供了一个compareTo()方法,其方法原型为: public int compareTo(String str);  它的返回值是一个整型的值,即一个整数。如果返回值为 -1,则该字符串位于str的前面;如果返回值为0,则该字符串与str 内容一样;如果返回值为 1,则该字符串在str 后面。  “负在前”

package cn.test_02;  
  
/* 
 * String s = new String(“hello”) 和
 * String s = “hello”;   
 * 两者是有区别的。前者会创建2个对象,后者创建1个对象。 
 *  
 *     ==: 比较引用类型比较的是地址值是否相同 
 * equals: 比较引用类型默认也是比较地址值是否相同,而String类重写了equals()方法 ,比较的是内容是否相同。 
 */  
public class StringDemo2 {  
    public static void main(String[] args) {  
        String s1 = new String("hello");  
        String s2 = "hello";  
  
        System.out.println(s1 == s2);      // false,比较地址  
        System.out.println(s1.equals(s2)); // true ,比较内容  
    }  
}  

Java基础--------字符串操作_第2张图片
 

String s3 = "hello";
String s4 = "hello";
System.out.println(s3 == s4); // true 这两个字符串的字面量,都是直接从内存找,所以true
System.out.println(s3.equals(s4));// true

String s5 = "world";
String s6 = "helloworld";
System.out.println(s6 == s3 + s5);        // false 字符串如果是变量相加,先开空间,再拼接
System.out.println(s6.equals(s3 + s5));// true  
System.out.println(s6 == "hello"+"world"); // true 字符串如果是常量相加,先相加,再在常量池找,如果有就直接返回,否则就创建 
System.out.println(s6.equals("hello"+"world"));// true  

1.2.3 hashCode() 方法

String类提供一个hashCode() 方法,返回int 类型的哈希值,其计算公式为:

hashcode = s[0]*31^(n-1) + s[1]*31^(n-2)+ ... + s[n-2]*31^1+s[n-1]*31^0 

其中s[i]是字符串中的第i 个字符,n是字符串的长度,^ 是幂运算符。规定空字符串的哈希值为0。

public class TestHashCode{
	public TestHashCode(){}
	public static void main(String args[]){
		String str = "abc";
		int code = str.hashcode();
		System.out.println("字符串" + str + "的哈希值为:" + code);
	}
}

equal() 和hashCode() 这两个方法都是Object类的方法,用于判断对象是否相等。一般来说,如果忽略其中任意一个,就必须同时忽略这两个,因为两者之间有着必须维持的至关重要的紧密联系。特殊情况:根据equals() 方法判断,如果两个对象是相等的,则它们必须有相同的hashCode()值(尽管这中情况通常不是真的)。 

1.2.4 String类中其它的常用方法

1. 通过调用length()方法得到String的长度。StringBuffer类的capacity()方法与String类的length()的方法类似,但是它测试是分配给StringBuffer的内存空间的大小,而不是当前被使用了的内存空间。

    String str="This is a String";
    int len =str.length();

2. 如果想确定字符串中指定字符或子字符串在给定字符串的位置,可以用 indexOf()和lastIndexOf()方法。

    String str="This is a String";
    Int index1 =str.indexOf(i); //index=2,下标从0开始
    Int index2=str.indexOf(‘i‘,index+1); //index2=5
    Int index3=str.lastIndexOf(I); //index3=15
    Int index4=str.indexOf(String); //index4=10

3. String对象的访问,方法charAt()用以得到指定位置的字符。

    String str="This is a String";
    char chr=str.charAt(3); //chr=i

4. getChars()方法用以得到字符串的一部分字符串

    public void getChars(int srcBegin,int srcEnd,char[]dst,int dstBegin)
    String str="This is a String";
    Char chr =new char[10];
    Str.getChars(5,12,chr,0); //chr=is a St
    subString()方法是提取字符串的另一种方法,它可以指定从何处开始提取字符串以及何处结束。

5.操作字符串,replace()方法可以将字符串中的一个字符替换为另一个字符。

    String str="This is a String";
    String str1=str.replace(‘T‘,‘t‘); //str1=this is a String

6. concat()方法可以把两个字符串合并为一个字符串。

    String str="This is a String";
    String str1=str.concat(Test); //str1=This is a String Test

7. toUpperCase()和toLowerCase()方法分别实现字符串大小写的转换。

    String str="THIS IS A STRING";
    String str1=str.toLowerCase(); //str1=this is a string;

8. trim()方法可以将字符串中开头和结尾处的空格去掉.

    String str="This is a String" ;
    String str1=str.trim(); // str1=This is a String

9. String 类提供静态方法valueOf(),它可以将任何类型的数据对象转换为一个字符串。如

    System.out.println(String,ValueOf(math,PI));

10.split()方法进行分割字符串 

    public String[] splitString(String str,String sdelimiter)...{
    String[] array=str.split(sdelimiter);
    return array; 
      }

二. 字符串缓冲类(StringBuilder和StringBuffer)

2.1 深入理解String、StringBuilder、StringBuffer 

Java基础--------字符串操作_第3张图片

既然在Java中已经存在了String类,那为什么还需要StringBuilder和StringBuffer类呢?

先看一段代码,如下:

public class Main {
         
    public static void main(String[] args) {
        String string = "";
        for(int i=0;i<10000;i++){
            string += "hello";
        }
    }
}

这句 string += "hello"; 的过程相当于将原有的string变量指向的对象内容取出与"hello"作字符串相加,再存进另一个新的String对象当中,再让string变量指向新生成的对象。如果还有疑问可以反编译其字节码文件便清楚了:

Java基础--------字符串操作_第4张图片

从这段反编译出的字节码文件可以很清楚地看出:从第8行开始到第35行是整个循环的执行过程,并且每次循环会new出一个StringBuilder对象,然后进行append操作,最后通过toString方法返回String对象。也就是说这个循环执行完毕new出了10000个对象,试想一下,如果这些对象没有被回收,会造成多大的内存资源浪费。从上面还可以看出:string+="hello"的操作事实上会自动被JVM优化成:

  StringBuilder str = new StringBuilder(string);
  str.append("hello");
  str.toString();

再看下面这段代码:

public class Main {        
    public static void main(String[] args) {
        StringBuilder stringBuilder = new StringBuilder();
        for(int i=0;i<10000;i++){
            stringBuilder.append("hello");
        }
    }
}

反编译字节码文件得到:

Java基础--------字符串操作_第5张图片

从这里可以明显看出,这段代码的for循环式从13行开始到27行结束,并且new操作只进行了一次,也就是说只生成了一个对象,append操作是在原有对象的基础上进行的。因此在循环了10000次之后,这段代码所占的资源要比上面小得多。

那么有人会问既然有了StringBuilder类,为什么还需要StringBuffer类?查看源代码便一目了然,事实上,StringBuilder和StringBuffer类拥有的成员属性以及成员方法基本相同,区别是StringBuffer类的成员方法前面多了一个关键字:synchronized,不用多说,这个关键字是在多线程访问时起到安全保护作用的,也就是说StringBuffer是线程安全的

下面两段代码分别来自StringBuffer和StringBuilder,insert方法的具体实现:

public StringBuilder insert(int index, char str[], int offset,
                              int len)              //StringBuilder的insert()方法
  {
      super.insert(index, str, offset, len);
  return this;
  }
public synchronized StringBuffer insert(int index, char str[], int offset,
                                            int len)  //StringBuffer的insert()方法,方法前面有synchronized关键字,是线程安全的
    {
        super.insert(index, str, offset, len);
        return this;
    }

2.2 不同场景下String、StringBuilder、StringBuffer的性能测试 

先写出一个测试案例,观察String、StringBuilder、StringBuffer三个类的性能区别,代码如下:

public class Main {
    private static int time = 50000;
    public static void main(String[] args) {
        testString();
        testStringBuffer();
        testStringBuilder();
        test1String();
        test2String();
    }
     
    public static void testString () {
        String s="";
        long begin = System.currentTimeMillis();  //产生一个当前的毫秒,这个毫秒是自1970年1月1日0时起至今的毫秒数。作为开始时间
        for(int i=0; i

Java基础--------字符串操作_第6张图片

上文中提到string+="hello"的操作,事实上会自动被JVM优化,在以下程序中可以达到验证,代码如下:

public class Main {
    private static int time = 50000;
    public static void main(String[] args) {
        testString();
        testOptimalString();
    }
     
    public static void testString () {
        String s="";
        long begin = System.currentTimeMillis();
        for(int i=0; i

Java基础--------字符串操作_第7张图片

对上面的执行结果进行一般性的解释:

  1)对于常量字符串直接相加,String效率很高,因为在编译器便确定了它的值,也就是说形如"I"+"love"+"java"; 的字符串相加,在编译期间便被优化成了"Ilovejava"。这个可以用javap -c命令反编译生成的class文件进行验证。对于间接相加(即包含字符串引用),形如s1+s2+s3; 效率要比直接相加低,因为在编译器不会对引用变量进行优化。

  2)String、StringBuilder、StringBuffer三者的执行效率:

StringBuilder(非线程安全的字符串变量) > StringBuffer(线程安全的字符串变量) > String(字符串常量)

   当然这个是相对的,不一定在所有情况下都是这样。
  比如String str = "hello"+ "world"的效率就比 StringBuilder st  = new StringBuilder().append("hello").append("world")要高。
  因此,这三个类是各有利弊,应当根据不同的情况来进行选择使用:
  当字符串相加操作或者改动较少的情况下,建议使用 String str="hello"这种形式;

  当字符串相加操作较多的情况下,建议使用StringBuilder,如果采用了多线程,则使用StringBuffer。

三. 字符串分解类(StringTokenizer类) 

StringTokenizer类的源码在\jdk1.6.0_14\src\java\util\StringTokenizer.java 文件中。 属于java.util包

3.1 StringTokenizer的功能

String类使用split()方法运用正则表达式分解字符串,而StringTokenizer类的对象可以直接分解字符串。 有时需要分析字符串并将字符串分解成可被独立使用的单词,这些单词叫做语言符号。当分析一个字符串并将字符串分解成可被独立使用的单词时,可以用StringTokenizer类。

3.2 StringTokenizer的方法

构造方法:

1. StringTokenizer(String str) :构造一个用来解析str的StringTokenizer对象。Java默认的分隔符有“空格”、“制表符(‘\t’)”、“换行符(‘\n’)”、“回车符(‘\r’)”。例如:StringTokenizer fenxi=new StringTokenizer("we are student"); //分析器将以空格为分割标记,将字符串中的三个单词分开。
2. StringTokenizer(String str, String delim) :构造一个用来解析str的StringTokenizer对象,并提供一个指定的分隔符。例如:StringTokenizer fenxi=new StringTokenzier("We,are;student",",;"); / /分析器将一,和;为分割标记,将三个单词分开。
3. StringTokenizer(String str, String delim, boolean returnDelims) :构造一个用来解析str的StringTokenizer对象,并提供一个指定的分隔符,同时,指定是否返回分隔符。其中第一个参数就是要分隔的String,第二个是分隔字符集合,第三个参数表示分隔符号是否作为标记返回。
1. int countTokens():返回nextToken方法被调用的次数。如果采用构造函数1和2,返回的就是分隔符数量(例2)。
2. boolean hasMoreTokens() :返回是否还有分隔符。
3. boolean hasMoreElements() :结果同2。
4. String nextToken():返回从当前位置到下一个分隔符的字符串。
5. Object nextElement() :结果同4。
6. String nextToken(String delim):与4类似,以指定的分隔符返回结果。

核心方法:

   import java.util.*;  
     public class FenXiQi{   
         public static void main(String[] args){   
             String s="I love java !";   
             StringTokenizer tokenizer=new StringTokenizer(s);   
             int number=tokenizer.countTokens();   
             while(tokenizer.hasMoreTokens()){   
                String str=tokenizer.nextToken();   
                System.out.println(str);   
                System.out.println("还剩"+tokenizer.countTokens()+"个单词");   
             }   
          }  
      }  

Java基础--------字符串操作_第8张图片

四. 正则表达式(Regular Expression) 

4.1 概念

待续

4.2 Java常用正则表达式验证工具类RegexUtils.java

待续

                  

------------------------------------------------------------------ 我是低调的分隔线  ----------------------------------------------------------------------

                                                                                                                                     吾欲之南海,一瓶一钵足矣...

你可能感兴趣的:(编程,java,jvm,开发语言)