字符串
字符串是一个字符序列,Java提供了一个内置的String类来处理字符串。一个String对象代表一个字符串,而且有时候它可以作为字符串被引用。Java的标准包java.lang中封装了类String和StringBuffer(类名都是大写字母打头),分别用来处理都可以用来处理不变字符串和可变字符串。
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);
4.String的常用方法
类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”,而字符串s和s1的值不发生改变。如果需要连接多个字符串,可以使用如下方法:
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里面的,除非程序调用 String的intern方法。
【例】代码分析
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”对象了,因为“abc”equels “abc”,因此直接返回str2指向的对象给str3,也就是说str2和str3是指向同一个对象的引用,打印结果为 str2 == str3
在对字符串的相等判断,==判断的是地址是否相同,equal()判断的是字符值是否相同。大多数时候==跟equal()的结果都是相同的。这是因为String对象是不变模式的,如果不是明确地new一个String对象,Java对于String对象的保存默认的是会把新生成的String 对象放到一个缓冲区,然后每次判断缓冲区中是否已经有了这个对象,如果有了,那么后建立的同样字符值的String对象也会指向最初建立是该字符值对象的地址。也就是说字符值相同的时候,大多数情况下地址也是相同的。==与equal()效果是相同的。但是当对象是str = new String(“abc”)生成的而不是直接str = “abc”这样赋值生成,或者经过了一些字符串连接处理,或者通过StringBuffer等对象生成,都会在内存中开辟新的地址的,这个时候==和 equal()结果是不同的。
【例】字符串比较,测试String的equals()方法
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");
}
}