黑马程序员——字符串1:概述与常用方法介绍

------- android培训java培训、期待与您交流! ----------

        在实际开发过程中,有一类数据是我们经常需要操作和处理的,那就是字符串。无论是在论坛发帖、写博客还是QQ聊天,这些行为中都会涉及到语言文字。那么为了遵循面向对象的思想,Java标准类库中就定义了一个用于描述语言文字这类事物的类——String类,并且该类对外提供了非常丰富的方法来更为便捷的操作字符串。

1. String类概述

1) String类API文档描述

        String类在Java标准类库中的位置为:java.lang。API文档中对该类的概括性描述为:String类代表字符串。Java 程序中的所有字符串字面值(如"abc")都作为此类的实例实现,也就是说,从语法角度,任何被双引号括起来的字母组合,都是一个独立的字符串对象。

2) 创建字符串

        我们通过API文档可以知道,该类对外提供了丰富的构造方法,其中包括将字节数组、字符数组或者其他字符串对象等等转化为字符串对象的构造方法。而如果想要凭空创建一个新的字符串对象可以采用下面的两种方法:

//方法一
String str1 = new String("Hello World!");
//方法二:
String str2 = "Hello World!";
这两种方法的作用是相同的,但是第二种更为方便一些。

3) 字符串是常量

字符串对象最大的特点是:字符串对象在创建以后不能更改,也就是说是常量。我们来看一个小例子,

代码1:

class StringDemo
{
	public static void main(String[] args)
	{
		String str= “apple”;
		str = “peach”;
	}
}

        上面例子中,两次赋值动作看似改变了字符串对象的值。但是,大家要注意,str是String类类型变量,它是一个引用,而不是字符串对象本身,该引用的值是字符串对象在内存中的地址。那么两次赋值动作仅仅是改变了该引用的指向,而“apple”和“peach”作为字符串对象本身,是没有也不会发生变化的。

        既然说到字符串对象是一个常量,那么这里就涉及到了常量池的概念。在Java程序中,有很多内容是不会改变的,比如类名、成员变量名及所属类型、方法名、返回类型、参数名及其所属类型、常量、还有在程序中出现的大量的字面值(字符串)。编译器将源代码程序编译成字节码文件以后,会用一部分字节分类存储这些内容,并在虚拟机加载字节码文件以后,在方法区中为这些内容开辟一块内存空间,那么这块内存空间就称为常量池。

        对于字符串来说,在常量池中的存储形式是将字符串分解为一个一个字符存储在一个数组中,如果某个字符串用到了常量池数组中的字符,就会将这些字符拼到一起形成一个字符串对象,如果常量池中没有,就创建新的字符存入数组中。通过这样的方式在需要重复创建相同变量时节省了很多时间,使得程序执行更为高效。由于上述的常量池机制,下面代码的执行结果也就不难理解了,

代码3:

String str1 = “Hello World!”;
String str2 = “Hello World!”;
System.out.println(str1 == str2);
        运行结果为true。对于其他类来说,即使通过相同的构造方法,甚至传入相同的初始化参数来创建两个对象,上述比较的结果通常都是false——两对象在对内存中的地址值不同。但是比较两字符串对象为什么会出现相反的结果呢?这是因为第二次创建“Hello World!”字符串时,由于该字符串已经存在于常量池中,而且由于字符串不可改变的特性,索性就让str2也指向与str1相同的字符串对象,以此节省内存空间。

4) 字符串对象的比较

先来看一段代码,

代码2:

class StringDemo2
{
	public static void main(String[] args)
	{
		//分别通过两种方式创建字符串
		String str1 = "Hello World!";
		String str2 = newString("Hello World!");
 
		//首先使用“==”比较运算符符号进行比较
		System.out.println(str1 == str2);
		//再使用字符串对象的equals方法进行比较
		System.out.println(str1.equals(str2));
	}
}
运行结果为:

false

true

        对于一个Java新手来说,上面的结果比较令人意外。第一个比较结果之所以是false是因为,str1和str2引用指向了两个对象,其中“Hello World!”是一个字符串对象,而new String(“Hello World!”)是以“Hello World!”字符串对象为初始化值的另一个字符串对象,而“==”比较运算符,是比较的两个对象的地址值,对象不同地址值当然也不同。那么第二个比较结果为什么又是true呢?大家都知道,Object类的equals方法比较的是两个对象的地址值,返回的就是使用“==”运算符的结果。而String类在继承Object类的同时复写了其equals方法,比较原理不再是比较地址值而是,而是比较两个字符串的内容。

 

小知识点1:

        比较字符串的小面试题:代码2中str1和str2有什么区别?

        答:str1仅指向了内存中的一个对象,而str2指向了内存中的两个对象。就像上文所述,str1引用直接指向了一个字面值为“Hello Worlde!”字符串,而str2指向了一个以“Hello World!”为初始化值为另一个字符串对象,就好像用一个字符串对象将另一个字符串包装了起来。


2. String类常用方法介绍

        在实际开发中,对字符串最频繁的操作动作分别是获取判断。因此,我们首先介绍与这两个动作相关的方法及其使用。之后再分别介绍对字符串的转换、切割、替换以及其他常用的方法。

1) 获取

在介绍方法时我将使用下面的格式:

方法编号 返回值类型 方法名及参数列表:方法说明

 

(1) int length():获取该字符串对象的长度,注意不要和数组的字段length混淆。

(2) char charAt(int index):获取指定位置上的字符。index表示在字符串中位置的脚标。

(3) int indexOf(int ch):获取指定字符在字符串中第一次出现的位置。这里需要说明一点,传入参数并不是字符类型变量,而是整型变量,这是因为该方法接收的是字符对应的ASCII值。该方法还有三个重载形式,其中indexOf(int ch, int fromIndex),表示从指定位置开始寻找指定字符,fromIndex代表开始搜索位置脚标。其他两个重载形式indexOf(Stringstr)、indexOf(String str, int fromIndex),用于寻找字符串中指定子字符串的位置。

(4) int lastIndexOf(int ch):反向索引一个字符在字符串中第一次出现的位置。这个方法的搜索顺序与indexOf方法相反,但是不会颠倒角标序号。该方法用于在预先知道指定字符在靠近字符串尾部的情况下,提高查找效率。该方法的三个重载形式与indexOf类似,请大家自行查阅API文档。

我们先通过下面的代码演示这四个方法的使用,

代码3:

class StringDemo3
{
	public static void main(String[] args)
	{
		String str = "abcdefabef";
 
		//获取长度
		System.out.println("length= "+str.length());
		//根据索引获取字符
		System.out.println("4号字符:"+str.charAt(4));
		//根据字符获取索引
		System.out.println("字符a的序号:"+str.indexOf('a'));
		//根据字符获取索引,从指定位置开始
		System.out.println("字符a的序号(从序号5开始):"+str.indexOf('a',5));
		//反向索引指定字符的位置
		System.out.println("字符a的序号:"+str.lastIndexOf('a'));
	}
}
运行结果为:

length = 10

4号字符:e

字符a的序号:0

字符a的序号(从序号5开始):6

字符a的序号:6

 

关于上述代码再说明4点:

(a) 字符串的序号是从0开始的,这与数组是相同的。

(b) 调用charAt方法时如果传入非法数值——负数和大于字符串长度的数,虚拟机会抛出StringIndexOutOfBoundsException异常。

(c) 调用indexOf方法时,指定字符或字符串不存在时,返回-1。

(d) lastIndexOf方法仅仅是查找顺序与indexOf相反,因此如果查找同一个字符或子串,两者返回的角标值是相同的。

2) 判断

(1) boolean contains(CharSequence s):判断字符串中是否包含某个子串。注意到参数列表中所传参数并非是String类型的。CharSequence表示字符序列,是String类实现的一个接口,这里可以多态地接受String对象。

(2) boolean isEmpty():判断字符中是否包含任何内容。原理其实就是判断字符串长度是否为零。这里提醒大家注意,“""”和“null”是有区别的,前者表示没有内容的字符串,后别表示没有指向。

(3) boolean startsWith(String prefix):字符串是否是以指定前缀开头,prefix表示指定前缀。该方法的重载形式startsWith(Stringprefix, inttoffset),表示从指定位置开始索引,判断是否以指定前缀开头。

(4) boolean endsWith(String suffix):字符串是否是以指定后缀结尾。

(5) boolean equals(Object anObject):判断字符串的内容是否相同。复写了Object类中的equals方法,将方法原理从比较对象在堆内存中的地址改为,比较两字符串的内容。

(6) boolean equalsIgnoreCase(StringanothorString):在忽略大小写的情况下,判断两字符串内容是否相同。

我们通过下面的代码演示上述六个方法的使用,

代码4:

class StringDemo4
{
	public static void main(String[] args)
	{
		String str = "StringDemo.java";
 
		//判断指定文件名是否以“String”开头
		System.out.println("是否以String开头:"+str.startsWith("String"));
		//判断指定文件是否是“.java”文件,也即文件名是否以“.java”结尾
		System.out.println("是否是.java文件:"+str.endsWith(".java"));
		//判断指定文件名是否包含“Demo”
		System.out.println("是否包含Demo:"+str.contains("Demo"));
 
		String str1 = "Hello World!";
		String str2 = "HELLO WORLD!";
 
		//判断两个字符串的内容是否完全相同
		System.out.println(str1.equals(str2));
		//判断两个字符串的内容在忽略大小写的情况,是否相同
		System.out.println(str1.equalsIgnoreCase(str2));
	}
}
运行结果为:

是否以String开头:true

是否是.java文件:true

是否包含Demo:true

false

true

        上述三个方法有很多实际的应用场景,比如,在某个文件夹内搜索,所有指定格式的文件、所有符合某一文件名命名方式的文件、或者通过某个软件打开某个文件时,判断该文件格式是否可被识别等等。这里要提醒大家一下,对于上述代码中的文件名,判断其格式使用endsWith和contains方法是一样的,但是如果文件名是诸如“StringDemo.java.txt”等格式时,使用contains就会发生错误。

        可能细心的朋友会发现,使用indexOf和contains方法的效果是一样的,调用indexOf方法返回的数为非负,那么就表示该字符串包含指定字符或子串,否则就不包含。实际上,contains方法的底层实现原理就是利用了indexOf,可以查阅API文档考证。那么在实际应用中,如果既要判断字符或子串是否存在于当前字符串,而且还要获取字符或子串的序号,就可以使用indexOf,若仅需要作出判断,使用contains方法即可。

3) 转换

(a) 将字符数组转换成字符串

方法一:构造方法。

String(char[]value),将一个字符数组的全部转换为字符串。

String(char[] value, int offset, int count),将一个字符数组的一部分转换为字符串。其中offset表示部分字符数组的起始序号,count表示需要转换的字符个数。

方法二:静态方法

static String copyValueOf(char[]value),作用与String(char[] value)相同,但使用方法不同。

static String copyValueOf(char[]value, int offset, int count),同上。

static StringvalueOf(char[] value),使用方法和效果同copyValueOf。

static String valueOf(char[]value, int offset, int count),同上。

就这六个方法,我们通过下面的代码进行演示,

代码5:

class StringDemo5
{
	public static void main(String[] args)
	{
		char[]arr = {'I',' ','l','o','v','e',' ','t','h','i','s',' ','g','a','m','e','!'};
 
		//通过构造方法将一个字符数组的全部转换为字符串
		String str1 = new String(arr);
		//通过构造方法将一个字符数组的一部分转换为字符串
		Strings tr2 = new String(arr, 2, 4);
		System.out.println(str1);
		System.out.println(str2);
 
		//通过copyValueOf将字符数组的全部转换为字符串
		String str3 = String.copyValueOf(arr);
		//通过copyValueOf将字符数组的一部分转换为字符串
		String str4 = String.copyValueOf(arr, 2, 4);
		System.out.println(str3);
		System.out.println(str4);
 
		//通过valueOf将字符数组的全部转换为字符串
		String str5 = String.valueOf(arr);
		//通过valueOf将字符数组的全部转换为字符串
		String str6 = String.valueOf(arr, 2, 4);
		System.out.println(str5);
		System.out.println(str6);
	}
}
运行结果为:

I love this game!

love

I love this game!

love

I love this game!

love

(b) 将字符串转换成字符数组

char[] toCharArray():将调用该方法的字符串对象转换为字符数组。

演示代码,

代码6:

class StringDemo6
{
	public static void main(String[] args)
	{
		String str = "Hello World!";
		//将字符串转换为字符数组
		char[] chs = str.toCharArray();
 
		//打印字符数组
		System.out.print('[');
		for(int x = 0; x<chs.length; x++)
		{
			if(x == chs.length-1)
			{    
				System.out.print(chs[x]+"]");
				break;
			}
			System.out.print(chs[x]+",");
		}
	}
}
运行结果为:

[H,e,l,l,o,,W,o,r,l,d,!]

(c) 将字节数组转换成字符串

由于涉及到以后要重点介绍的字符集(或称为编码表,下同)和编码的知识,这里仅作简单介绍。

String(byte[] bytes),将指定字节数组按照平台默认的字符集转换为字符串。

String(byte[] bytes, int offset, int length),将指定字节数组的一部分按照平台默认的字符集转换为字符串。

(d) 将字符串转换成字节数组

这里同样由于涉及到字符集和编码,仅作简要介绍。

byte[] getBytes():使用平台默认的字符集将调用该方法的字符串对象转换为字节数组。

(e) 将基本数据类型转换为字符串

static String valueOf(boolean b):将布尔型变量转换为字符串。

static String valueOf(char c):将字符型变量转换为字符串

。。。

valueOf方法还有很多重载方法,可以将6种基本数据类型、字符数组和其他对象变量转换为字符串,有兴趣的朋友可以自行查找API文档。

4) 替换

(a) 将字符串中所有指定的字符替换为指定的新字符。

String replace(char olChar, char newChar):由于字符串无法被改变的特性,调用该方法并      且成功替换以后,将会返回一个新的字符串,而原字符串将保持不变。另外,如果指定    被替换的字符不存在,将会返回原字符串。

代码演示,

代码7:

class StringDemo7
{
	public static void main(String[] args)
	{
		String str1 = "Hello World!";
 
		//获得一个新的字符串,将原字符窜的所有字母l,替换为m
		String str2 = str1.replace('l', 'm');
 
		//打印原字符串和新字符串
		System.out.println("str1= "+str1);
		System.out.println("str2= "+str2);
 
		//如果指定被替换的字符不存在,将返回原字符串
		String str3 = str1.replace('a', 'b');
 
		System.out.println("str3= "+str3);
	}
}
运行结果为:

str1 = Hello World!

str2 = Hemmo Wormd!

str3 = Hello World!

(b) 将字符串中指定的子串替换为新子串

String replace(CharSequencetarget, CharSequence replacement):使用方法和作用同上。

代码演示,

代码8:

class StringDemo8
{
	public static void main(String[] args)
	{
		//将“Java”替换为“World”
		String oldStr = "Hello Java!";
		String newStr = oldStr.replace("Java", "World");
       
		System.out.println("oldStr ="+oldStr);
		System.out.println("newStr= "+newStr);
	}
}
运行结果为:

oldStr = HelloJava!

newStr = HelloWorld!

        细心的朋友可能在API文档中看到了replaceAll和replaceFirst方法,这两个方法也是可以实现对字符串的替换操作,而这两个方法与上述两个方法不同之处在于,可以按照指定的规则进行替换。但是由于涉及到另一部分的知识——正则表达式,我们将在后面的内容中详细介绍这两个方法。

5) 切割

按照某一规则将原字符串切割为多个子串,并返回包含多个子串的字符串数组。

String[] split(String regex):其中regex的原意是代表用字符串表示的正则表达式,由于

我们还没有介绍,因此大家只要将其理解为某个字符串即可。返回的字符串数组中不再包含,用来切割原字符串的子串。

演示代码,

代码9:

class StringDemo9
{
	public static void main(String[] args)
	{
		//将下面表示个人信息的字符串通过逗号分割为单独的项目
		String str = "姓名,年龄,籍贯";
 
		String[] arr = str.split(",");
		for(int x = 0; x<arr.length;x++)
			System.out.println(arr[x]);
	}
}
运行结果为:

姓名

年龄

籍贯

就像上面说到的那样,返回的字符串数组中的所有元素中已经不包含用于切割字符串的“,”,因此split方法与“获取”是有区别的。

6) 获取子串

该方法本可以并到第一部分“获取”中介绍,但是因其一定的特殊性,需单独说明。

String substring(int beginIndex):返回从指定角标位开始截取原字符串而得的子字符串。其中beginIndex表示开始截取的角标值。

String substring(int beginIndex,int endIndex):返回通过头尾两角标值截取原字符串获得的子字符串。注意,包含头,不包含尾,也就是说,子串包括原

串从beginIndex角标位开始直到endIndex-1的角标位对应的字符。这样定义的原因是因为,通常认为头角标值为0,尾角标值为字符串长度。

演示代码,

代码10

class StringDemo10 
{
	public static void main(String[] args) 
	{
		String str = "abcdefg";
		//从2号角标开始截取原字符串
		String subStr1 = str.substring(2);
		//从2号角标直到4号角标截取原字符串
		String subStr2 = str.substring(2, 5);


		System.out.println(subStr1);
		System.out.println(subStr2);
	}
}
运行结果:

cdefg
cde
如前所述,虽然获取subStr2时指定了从2号角标到5号角标,但是子串中仅包含2、3、4号角标对应的字符。也即,包含头,不包含尾。
这里再提醒大家两点:
(a) 实际上,substring(int beginIndex)方法底层的实现形式是:substring(0, this.length())。
(b) 如果所传参数大于字符串长度或者传入负数,将抛出IndexOutOfBoundsException异常。

7) 其他方法
(1) 转换大小写
String toLowerCase():返回将指定字符串中的字母转换为大写后的字符串。这里要提一点是,实际上equalsIgnoreCase方法的执行首先会调用toLowerCase将所有字符串全部转换为小写后,调用equals方法。
String toUpperCase():返回将指定字符串中的字母转换为小写后的字符串。
代码演示,

代码11:

class StringDemo11
{
	public static void main(String[] args) 
	{
		String str = "Hello World!";


		//大写
		String upperCase = str.toUpperCase();
		//小写
		String lowerCase = str.toLowerCase();
		System.out.println(upperCase);
		System.out.println(lowerCase);
	}
}
(2) 去除空格
String trim():返回不包括指定字符串前后空格的子串。
演示代码,

代码12:

class StringDemo12
{
	public static void main(String[] args) 
	{
		String str = "   Hello World!   ";
		System.out.println("oldStr: ("+str+")");


		String newStr = str.trim();
		System.out.println("newStr: ("+newStr+")");
	}
}
运行结果为:

(   Hello World!   )
(Hello World!)
   当用户输入账号密码登陆某个软件时,如果不小心在账号前多加了空格,人性化的做法是,自动将空格去除,否则,用户可能会因为无法登陆而怀疑账号被盗,给用户造成不必要的麻烦。

(3) 自然顺序比较
int compareTo(String anotherString):按照字典顺序和长度大小对两字符串进行比较。返回值类型为整型,按照该值的的正负和是否为0,来比较两个字符串。具体的比较方法:两字符串如果不相同,要么内容不同,要么长度不同,要么两者皆有。如果内容不同,比如该字符串(调用compareTo方法的字符串)的第k个字符和参数字符串的第k个字符不同,返回值就是这两个字符ASCII值之差(也就是说,这里可以比较所有包含在ASCII表中的字符);如果仅仅是两字符串的长度不同,返回值就是长度只差;如果内容和长度都不同,就以内容不同为准。该方法的一个重要应用是对字符串的排序,我们将在后面集合框架部分重点介绍。
演示代码,

代码13:

class StringDemo13
{
	public static void main(String[] args) 
	{
		//按照字典顺序比较两字符串
		String str1 = "bbc";
		String str2 = "abc";
		String str3 = "bbc";
		String str4 = "cbc";


		System.out.print(str1.compareTo(str2)+", ");
		System.out.print(str1.compareTo(str3)+", ");
		System.out.println(str1.compareTo(str4));


		//按照长度比较两字符串
		String str5 = "aaa";
		String str6 = "aaaaaa";
		System.out.println(str5.compareTo(str6));


		//内容和长度均不同
		String str7 = "abcdef";
		String str8 = "abb";
		System.out.println(str7.compareTo(str8));
	}
}
运行结果为:
1, 0, -1
-3
1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值