String 类继承自 Object 超类,实现的接口有:Serializable、CharSequence、Comparable<String> 接口,具体如下图:
一、常用的String 类构造方法
1 //1.由字符串original复制得到新的字符串 2 public String(String original) { 3 this.value = original.value; 4 this.hash = original.hash; 5 } 6 //2.得到值等于value的字符串 7 public String(char[] value) { 8 this.value = Arrays.copyOf(value, value.length); 9 } 10 //3.返回字符串从第i个位置起的前j个字符 11 public String(char[] value, int offset, int count) { 12 //判断索引i是否合法 13 if (offset < 0) { 14 throw new StringIndexOutOfBoundsException(offset); 15 } 16 //判断计数值是否合法 17 if (count <= 0) { 18 if (count < 0) { 19 throw new StringIndexOutOfBoundsException(count); 20 } 21 //如果计数值等于0,返回的是空字符 22 if (offset <= value.length) { 23 this.value = "".value; 24 return; 25 } 26 } 27 //索引值>字符串长度-计数值,抛异常 28 if (offset > value.length - count) { 29 throw new StringIndexOutOfBoundsException(offset + count); 30 } 31 //其余情况返回要求值 32 this.value = Arrays.copyOfRange(value, offset, offset+count); 33 }
二、关于equals方法的理解
首先,我们看在Object类中equals方法如下这样写的,this指代调用者对象。
1 public boolean equals(Object var1) { 2 return this == var1; 3 }
接下来,我们瞅一下在String中equals方法是怎样的呢,好的上源代码:
public boolean equals(Object anObject) { //首先,直接比较两个对象是否相等,若相等就直接返回true if (this == anObject) { return true; } //判断传入的参数是否是String类的实例 if (anObject instanceof String) { //如果是String的实例,直接强转为String String anotherString = (String)anObject; int n = value.length; //判断传入的对象与比较的对象的长度是否一样 if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; //逐个遍历两个对象中的对应位置的字符进行比较 while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
由上面我们可以知道了,两个字符串的比较是通过equals方法进行的,比较的顺序是:
首先,去比较两个字符串是否是同一个对象,如果是同一个对象则直接返回true;
如果不是同一个对象,会再判断是否是一个String的实例,如果是就会再比较两个字符串的长度是否相等,若相等了,就会再去对两个字符对应位置的字符逐个进行比较,如果全部相等则返回true。
这里提到了equals方法的比较,就要对 “==”来进行一个区分比较。
“==”是用来进行操作数的值之间的比较,对基本数据类型,变量本身存储值,但是引用类型,变量本身存的是对象的地址,比较的是两个对象的地址是否相等。
String类代表的字符串,Java中所有的字符串都是以它为模板创建的对象。字符串一经创建是不可改变的。也就是说“abc”不可以再变成“abcd”。这就要求我们使用String的时候应该注意:尽量不要做字符串频繁的拼接操作。因为字符串一旦创建不可改变,只要频繁拼接,就会在字符串常量池中创建大量的字符串对象,给垃圾回收带来问题。
为了提升字符串的访问效率,在程序中使用了缓冲技术,所有被双引号括起来的字符串都会在JVM运行时数据区的字符串常量池中新建一份,(字符串常量池存储在方法区中)。如果程序中用到“abc”,会先在字符串常量池中查找有没有该字符串,如果找到直接拿来用,找不到就在字符串常量池中新建一个"abc"。
如下的代码,判断两个字符串是否相等:
String A="a"; String B="a"; System.out.println(A==B); //true
前面提到过“==”是用来比较值相等。程序中第一次出现"a"时,先在字符串常量池中新建一个字符串"a"对象,A指向该对象,当程序中再次出现"a"时,会先在字符串常量池中查找,发现已经创建了"a",于是直接拿来用了,使用B指向,也就是A,B引用指向的是同一对象,所以打印输出true。
再来看看String中的构造方法:
这个方法是初始化新创建的String对象,使其表示与参数相同的字符序列,换句话说,新创建的字符串时参数字符串的副本。
看下面代码,判断是否相等:
String C=new String("a"); String D=new String("a"); System.out.println(C==D); //false
参数字符串“a”对象,创建之前先在字符串常量池中查找有没有该对象,没有则创建;然后构造方法String(String original)以"a"为参数,new出两个对象存在堆中,C和D引用分别指向这两个对象,很明显C和D引用指向的是两个不同的对象,即上面这两行代码创建了三个不同的对象,方法区中一个,堆中两个。
2.字符串的模式匹配
子串在主串中的定位操作称为串的模式匹配,串的模式匹配应该算串的重要操作之一。来看看朴素的模式匹配算法。
根据要求从主串的第i个位置开始遍历。子串从第0个位置开始,如果相应位置的字符相等,则主串子串各向后移动一位判断下一位字符,如果不等,主串回到匹配位置的下一位,子串从0位开始,循环遍历,直到找到子串,最后返回子串在主串中的索引。简单地说,就是对主串的每一个字符作为子串开头,与要匹配的字符串进行匹配,这种匹配方式称为朴素的模式匹配算法。实现代码如下:
package com.java.String; /* * 串的模式匹配 * */ public class IndexTest { public static void main(String[] args){ int s=indexSearch("eateqweapple","app",8); System.out.println(s); } //朴素的模式匹配算法,返回子串T在主串S中第pos个字符之后的位置 public static int indexSearch(String S,String T,int pos){ //默认值规定为0 int index=0; int Slen=S.length(); int Tlen=T.length(); //S位置下标 int i=pos; //T位置下标 int j=0; while((i<Slen)&&(j<Tlen)){ char s=S.charAt(i); char t=T.charAt(j); if(s==t){ //如果当前位置的相等,都各加1比较下一位置 i=i+1; j=j+1; }else{ //否则回到主串S匹配的下一位置,子串从头开始 i=i+1; j=0; } if(j>Tlen-1){ index=i-Tlen; } } return index; } }
算法复杂度分析:
最好的情况,一开始就匹配成功,算法复杂度为O(1); 稍微差一些,每次匹配都是首字母不匹配,但遍历到主串的最后位置匹配成功,算法复杂度为O(n+m);最坏的情况,每次匹配不成功都是发生在子串的最后一个字符,时间复杂度为O((n-m+1)*m)。
朴素的模式匹配算法算很简单的解决子串定位问题的算法了。想一想Java底层那些封装好的方法,我们真是站在了巨人的肩膀上。
具体参考文章地址: