黑马程序员---4、字符串

来源:互联网 发布:超市管理系统c语言 编辑:程序博客网 时间:2024/05/17 07:55

字符串

 

        字符串是一个字符序列,Java提供了一个内置的String类来处理字符串。一个String对象代表一个字符串,而且有时候它可以作为字符串被引用。Java的标准包java.lang中封装了类StringStringBuffer(类名都是大写字母打头),分别用来处理都可以用来处理不变字符串和可变字符串。

1、String类的介绍

        Java语言中,String是一个非可变类(immutable)。什么是非可变类呢?简单说来,非可变类的实例是不能被修改的,每个实例中包含的信息都必须在该实例创建的时候就提供出来,并且在对象的整个生存周期内固定不变。

1.字符串常量

    字符串常量是用双引号括住的一串字符。如:"Hello World!"

2.字符串声明

声明格式为:

String stringName;

如:String s1;

3.字符串创建

String类的构造方法主要有:

String( char chars[ ] );

String( char chars[ ], int startIndex, int numChars );

String( byte ascii[ ], int hiByte );

如:

String s = new String();  //创建一个空的字符串

char chars[]={‘a’,’b’,’c’}

s=new String(chars);         //用字符数组创建字符串

下面用不同方法生成字符串"abc"

char chars1[]={'a','b','c'};

char chars2[]={'a','b','c','d','e'};

String s1=new String(chars1);

String s2=new String(chars2,0,3);

byte ascii1[]={97,98,99};

byte ascii2[]={97,98,99,100,101};

String s3=new String(ascii1,0);

String s4=new String(ascii2,0,0,3);

4String的常用方法

String中提供了length( )charAt( )indexOf( )lastIndexOf( )getChars( )getBytes( )toCharArray( )等方法,下面只介绍一些常用方法,更多相关方法介绍请参考API文档。

l length()方法

此方法返回字符串的字符个数。如:

String s = “abc”;

String s1 = Java语言”;

int len = s.length();  //变量len的值是3

int len1 = s1.length();  //变量len1的值是6

l charAt()方法

该方法的作用是按照索引值(规定字符串中第一个字符的索引值是0,第二个字符的索引值是1,依次类推),获得字符串中的指定字符。例如:

String s = “abc”;

char c = s.chatAt(1);   //变量c的值是’b’。

l compareTo()方法

        该方法的作用是比较两个字符串的大小,比较的原理是依次比较每个字符的字符编码。首先比较两个字符串的第一个字符,如果第一个字符串的字符编码大于第二个的字符串的字符编码,则返回大于0的值,如果小于则返回小于0的值,如果相等则比较后续的字符,如果两个字符串中的字符编码完全相同则返回0。例如:

String s = “abc”;

String s1 = “abd”;

int value = s.compareTo(s1);          //value的值是小于0的值,即-1

String类中还存在一个类似的方法compareToIgnoreCase,这个方法是忽略字符的大小写进行比较,比较的规则和compareTo一样。例如:

String s = “aBc”;

String s1 = “ABC”;

int value = s. compareToIgnoreCase (s1);    //value的值是0,即两个字符串相等。

l concat方法

该方法的作用是进行字符串的连接,将两个字符串连接以后形成一个新的字符串。例如:

String s = “abc”;

String s1 = “def”;

String s2 = s.concat(s1);

则连接以后生成的新字符串s2的值是”abcdef”,而字符串ss1的值不发生改变。如果需要连接多个字符串,可以使用如下方法:

String s = “abc”;

String s1 = “def”;

String s2 = “1234”;

String s3 = s.concat(s1).concat(s2);

则生成的新字符串s3的值为”abcdef1234”。

其实在实际使用时,语法上提供了一种更简单的形式,就是使用“+”进行字符串的连接。例如:

String s = “abc” + “1234”;

则字符串s的值是”abc1234”,这样书写更加简单直观。

        而且使用“+”进行连接,不仅可以连接字符串,也可以连接其他类型。但是要求进行连接时至少有一个参与连接的内容是字符串类型。而且“+”匹配的顺序是从左向右,如果两边连接的内容都是基本数字类型则按照加法运算,如果参与连接的内容有一个是字符串才按照字符串进行连接。

例如:

int a = 10;

String s = “123” + a + 5;

       则连接以后字符串s的值是“123105”,计算的过程为首先连接字符串”123”和变量a的值,生成字符串”12310”,然后使用该字符串再和数字5进行连接生成最终的结果。

而如下代码:

int a = 10;

String s = a + 5 + “123”;

        则连接以后字符串s的值是”15123”,计算的过程为首先计算a和数字5,由于都是数字型则进行加法运算或者数字值15,然后再使用数字值15和字符串”123”进行连接获得最终的结果。

而下面的连接代码是错误的:

int a = 12;

String s = a + 5 + ‘s’;

因为参与连接的没有一个字符串,则计算出来的结果是数字值,在赋值时无法将一个数字值赋值给字符串s

l endsWith方法

该方法的作用是判断字符串是否以某个字符串结尾,如果以对应的字符串结尾,则返回true。例如:

String s = “student.doc”;

boolean b = s.endsWith(“doc”);

则变量b的值是true

l equals方法

该方法的作用是判断两个字符串对象的内容是否相同。如果相同则返回true,否则返回false。例如:

String s = “abc”;

String s1 = new String(“abc”);

boolean b = s.equals(s1);

而使用“==”比较的是两个对象在内存中存储的地址是否一样。例如上面的代码中,如果判断:

boolean b = (s == s1);

则变量b的值是false,因为s对象对应的地址是”abc”的地址,而s1使用new关键字申请新的内存,所以内存地址和s的”abc”的地址不一样,所以获得的值是false

String类中存在一个类似的方法equalsIgnoreCase,该方法的作用是忽略大小写比较两个字符串的内容是否相同。例如:

String s = “abc”;

String s1 =”ABC”;

boolean b = s. equalsIgnoreCase (s1);

则变量b的值是true

equals( )方法,String类重写了Object类的equals()方法,用来比较两个字符串的内容是否相同。

l replace( )方法,用来把串中出现的所有特定字符替换成指定字符以生成新串。

l substring( )方法,用来得到字符串中指定范围内的子串。

l toLowerCase( ) 方法,把串中所有的字符变成小写。

l toUpperCase( ) 方法,把串中所有的字符变成大写。

2字符串内存引用分析

String是引用类型,是一个不可变的类,有着特殊的作用,String 只能复制一次,复制后不可改变。如:

String str1 = new String("abc");

Stirng str2 = "abc";

         虽然两个语句都是返回一个String对象的引用,但是jvm对两者的处理方式是不一样的。对于第一种,jvm会马上在heap中创建一个String对象,然后将该对象的引用返回给用户。对于第二种,jvm首先会在内部维护的strings pool中通过String的 equels 方法查找是对象池中是否存放有该String对象,如果有,则返回已有的String对象给用户,而不会在heap中重新创建一个新的String对象;如果对象池中没有该String对象,jvm则在heap中创建新的String对象,将其引用返回给用户,同时将该引用添加至strings pool中。注意:使用第一种方法创建对象时,jvm是不会主动把该对象放到strings pool里面的,除非程序调用 Stringintern方法。

【例】代码分析

String str1 = new String("abc"); 

Stirng str2 = "abc";

if(str1 == str2){

System.out.println("str1 == str2");

}else{

System.out.println("str1 != str2");

}

        第一句代码,jvm 在堆上创建一个String对象,jvm strings pool中找不到值为“abc”的字符串,因此在堆上创建一个String对象,并将该对象的引用加入至strings pool中,此时堆上有两个String对象,一个是“abc”,这是一个匿名对象;一个是指向“abc”的引用对象str1,这个是将匿名对象的地址赋给了s。最后打印结果是 str1 != str2,因为它们是堆上两个不同的对象。

【例】上述代码继续,

String str3 = "abc";

if(str2 == str3){

System.out.println("str2 == str3");

}else{

System.out.println("str2 != str3");

}

       第一句代码执行是,jvm发现strings pool中已有“abc”对象了,因为“abcequels abc”,因此直接返回str2指向的对象给str3,也就是说str2str3是指向同一个对象的引用,打印结果为 str2 == str3

        在对字符串的相等判断,==判断的是地址是否相同,equal()判断的是字符值是否相同。大多数时候==equal()的结果都是相同的。这是因为String对象是不变模式的,如果不是明确地new一个String对象,Java对于String对象的保存默认的是会把新生成的String 对象放到一个缓冲区,然后每次判断缓冲区中是否已经有了这个对象,如果有了,那么后建立的同样字符值的String对象也会指向最初建立是该字符值对象的地址。也就是说字符值相同的时候,大多数情况下地址也是相同的。==equal()效果是相同的。但是当对象是str = new String(abc)生成的而不是直接str = abc”这样赋值生成,或者经过了一些字符串连接处理,或者通过StringBuffer等对象生成,都会在内存中开辟新的地址的,这个时候==和 equal()结果是不同的。 

【例】字符串比较,测试Stringequals()方法

public class Example1_23{

 public static void main(String[] args) {

  String s = new String("abc");

  String t = new String("abc");

  String ss = "abc";

  String str = "abc";

  if (s == t)

     System.out.println("s == t");

  else

     System.out.println("s != t");

  if (s.equals(t))

     System.out.println("s.equals(t)");

  else

     System.out.println("s not equals(t)");

  if (ss == str)

     System.out.println("ss == str");

  else

     System.out.println("ss != str"); 

  if (ss.equals(str))

     System.out.println("ss.equals(str)");

  else

     System.out.println("ss not equals(str)"); 

 }

}

程序运行结果为:

s != t

s.equals(t)

ss == str

ss.equals(str)

3、 StringBuffer类的介绍

        StringBuffer类和String一样,也用来代表字符串,只是由于StringBuffer的内部实现方式和String不同,所以StringBuffer在进行字符串处理时,不生成新的对象,在内存使用上要优于String类。 

        所以在实际使用时,如果经常需要对一个字符串进行修改,例如插入、删除等操作,使用StringBuffer要更加适合一些。

        在StringBuffer类中存在很多和String类一样的方法,这些方法在功能上和String类中的功能是完全一样的。但是有一个最显著的区别在于,对于StringBuffer对象的每次修改都会改变对象自身,这点是和String类最大的区别。

1.StringBuffer对象的初始化

        StringBuffer对象的初始化不像String类的初始化一样,Java提供的有特殊的语法,而通常情况下一般使用构造方法进行初始化。例如:

StringBuffer s = new StringBuffer();

这样初始化出的StringBuffer对象是一个空的对象。

如果需要创建带有内容的StringBuffer对象,则可以使用:

StringBuffer s = new StringBuffer(“abc”);

这样初始化出的StringBuffer对象的内容就是字符串”abc”。

需要注意的是,StringBuffer和String属于不同的类型,也不能直接进行强制类型转换,下面的代码都是错误的:

StringBuffer s = “abc”;      //赋值类型不匹配

StringBuffer s = (StringBuffer)”abc”;    //不存在继承关系,无法进行强转

StringBuffer对象和String对象之间的互转的代码如下:

String s = “abc”;

StringBuffer sb1 = new StringBuffer(“123”);

StringBuffer sb2 = new StringBuffer(s);   //String转换为StringBuffer

String s1 = sb1.toString();     //StringBuffer转换为String

2、StringBuffer的常用方法

StringBuffer类中的方法主要偏重于对于字符串的变化,例如追加、插入和删除等,这个也是StringBuffer和String类的主要区别。

(1) append方法

该方法的作用是追加内容到当前StringBuffer对象的末尾,类似于字符串的连接。调用该方法以后,StringBuffer对象的内容也发生改变,例如:

StringBuffer sb = new StringBuffer(“abc”);

sb.append(true);

则对象sb的值将变成”abctrue”。

使用该方法进行字符串的连接,将比String更加节约内容,例如应用于数据库SQL语句的连接,例如:

StringBuffer sb = new StringBuffer();

String user = “test”;

String pwd = “123”;

sb.append(“select * from userInfo where username=“)

.append(user)

.append(“ and pwd=”)

append(pwd);

这样对象sb的值就是字符串“select * from userInfo where username=test and pwd=123”。

(2) deleteCharAt方法

public StringBuffer deleteCharAt(int index)

该方法的作用是删除指定位置的字符,然后将剩余的内容形成新的字符串。例如:

 StringBuffer sb = new StringBuffer(“Test”);

sb. deleteCharAt(1);

该代码的作用删除字符串对象sb中索引值为1的字符,也就是删除第二个字符,剩余的内容组成一个新的字符串。所以对象sb的值变为”Tst”。

还存在一个功能类似的delete方法:

public StringBuffer delete(int start,int end)

该方法的作用是删除指定区间以内的所有字符,包含start,不包含end索引值的区间。例如:

StringBuffer sb = new StringBuffer(“TestString”);

sb. delete (1,4);

该代码的作用是删除索引值1(包括)到索引值4(不包括)之间的所有字符,剩余的字符形成新的字符串。则对象sb的值是”TString”。

(3) insert方法

public StringBuffer insert(int offset, boolean b)

该方法的作用是在StringBuffer对象中插入内容,然后形成新的字符串。例如:

StringBuffer sb = new StringBuffer(“TestString”);

sb.insert(4,false);

该示例代码的作用是在对象sb的索引值4的位置插入false值,形成新的字符串,则执行以后对象sb的值是”TestfalseString”。

reverse方法

public StringBuffer reverse()

该方法的作用是将StringBuffer对象中的内容反转,然后形成新的字符串。例如:

StringBuffer sb = new StringBuffer(“abc”);

sb.reverse();

经过反转以后,对象sb中的内容将变为”cba”。

setCharAt方法

public void setCharAt(int index, char ch)

该方法的作用是修改对象中索引值为index位置的字符为新的字符ch。例如:

StringBuffer sb = new StringBuffer(“abc”);

sb.setCharAt(1,’D’);

则对象sb的值将变成”aDc”。 

总之,在实际使用时,String和StringBuffer各有优势和不足,可以根据具体的使用环境,选择对应的类型进行使用。。

4、String类和StringBuffer类的比较

        String 对一串字符进行操作,不可变类。StringBuffer 也是对一串字符进行操作,但是可变类。String是对象不是原始类型,为不可变对象,一旦被创建,就不能修改它的值。对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去。String final类,即不能被继承。StringBuffer是一个可变对象,当对它进行修改的时候不会像String那样重新建立对象。它只能通过构造函数来建立,不能通过赋值符号对他进行赋值。如:

sb = "welcome to here!";//error

对象被建立以后,在内存中就会分配内存空间,并初始保存一个null。向StringBuffer

中赋值的时候可以通过它的append方法,字符串连接操作中StringBuffer的效率要比String高。如:

String str = new String("welcome to ");

str += "here";

       上面语句的处理步骤实际上是通过建立一个StringBuffer,调用append(),最后再将StringBuffer toSting();这样的话String的连接操作就比StringBuffer多出了一些附加操作,当然效率上要打折扣。并且由于String 对象是不可变对象,每次操作Sting 都会重新建立新的对象来保存新的值。这样原来的对象就没用了,就要被垃圾回收。这也是要影响性能的。

【例】将26个英文字母重复加了5000次,

public class Example1_27{

 public static void main(String[] args) {

       String tempstr = "abcdefghijklmnopqrstuvwxyz"; 

        int times = 5000; 

        long lstart1 = System.currentTimeMillis(); 

        String str = ""; 

        for (int i = 0; i < times; i++) { 

            str += tempstr; 

        } 

        long lend1 = System.currentTimeMillis(); 

        long time = (lend1 - lstart1); 

        System.out.println(time);

 }

}

程序运行结果为 3719

【例修改程序,改为StringBuffer

public class Example1_28{

 public static void main(String[] args) {

       String tempstr = "abcdefghijklmnopqrstuvwxyz"; 

        int times = 5000; 

        long lstart2 = System.currentTimeMillis(); 

        StringBuffer sb = new StringBuffer(); //改为StringBuffer

        for (int i = 0; i < times; i++) { 

            sb.append(tempstr); 

        } 

        long lend2 = System.currentTimeMillis(); 

        long time2 = (lend2 - lstart2); 

        System.out.println(time2);

 }

}

程序运行结果为 0

       所以结论很明显,StringBuffer 的速度几乎是String 上万倍。当然这个数据不是很准确。因为循环的次数在100000次的时候,差异更大。所以,如果在程序中需要对字符串进行频繁的修改连接操作的话。使用StringBuffer性能会更高。

 Java基本的输入输出

        Java语言的输入输出功能是十分强大而灵活的,美中不足的是看上去输入输出的代码并不是很简洁,因为需要包装许多不同的对象,对于输入输出将在后面的章节中详细介绍,现在只对在命令行窗口输入输出数据进行介绍。

1、数据的输出

Java中数据输出使用的最多的是下面两个静态方法:

System.out.print();

System.out.println();

这两个函数支持Java的任意基本类型作为参数。

2、数据的输入

Scanner类是JDK1.5新增的一个类,可以使用该类创建一个对象:

Scanner reader=new Scanner(System.in);

然后,reader对象调用下列方法,读取用户在命令行输入的各种数据类型:nextByte()nextDouble()nextFloat()nextInt()nextLine()nextLong()nextShort()

上述方法执行时会等待用户在命令行输入数据并回车确认。

【例】从键盘输入字符串并显示

import java.io.*;

import java.util.*;

public class Example1_29{

public static void main( String[] args){

       Scanner input = new Scanner( System.in ); // System.in初始化Scanner

       String message = input.next();  // 从控制台读取输入

       System.out.println( message ); // ok

}

}

【例】例如,从键盘输入若干个数,求所输入数的和与平均值,输入过程以任何非数字为结束。

import java.io.*;

import java.util.*;

public class Example1_30{

public static void main( String[] args){

      Scanner reader = new Scanner( System.in ); // System.in初始化Scanner

      double sum=0;

      int m=0;

      while(reader.hasNextDouble()){

       double x=reader.nextDouble();

       m=m+1;

       sum=sum+x;

      }

      System.out.println(m+"个数的和为"+sum); 

      System.out.println(m+"个数的平均值为"+sum/m); 

}

}

【例】从键盘上读入一行文本,识别其中单词的个数并分别输出它们。通过系统提供的StringTokenizer类自动实现单词的识别。

import java.util.Scanner;

import java.util.StringTokenizer;

public class Example1_31{

    public static void main(String args[ ]) {

        String line;

        StringTokenizer  token; 

        line=new Scanner(System.in).nextLine();

        token=new StringTokenizer(line," ");

        System.out.print("元素个数:"+ token.countTokens( )+"\n符号是 :");

        while(token.hasMoreTokens( ))

             System.out.print(token.nextToken( )+"\t");

    }