String的特性
字符串是一个字符序列
类的声明:public final class String implements java.io.Serializable, Comparable
- final:String类不可被继承
- Serializable:字符串支持序列化
- Comparable:对象可比较性
- CharSequence:此接口对许多不同种类的 char 序列提供统一的只读访问
私有成员属性:private final char value[];
,String对象的字符内容是存储在一个 final
修饰的字符数组 value[]
中的,同样也证实了字符串实际上是不可变的字符序列
String 两种赋值方式
方式一:字面量
使用一个字符串时,往往是知道它的字面量(直接量值)的。为方便起见,Java允许在不创建新变量的情况下,使用字符串直接量直接向字符串引用赋值。
通过字面量的方式(!new) 给一个字符串引用赋值,此时的字符串值存放在方法区的字符串常量池中,池中不会存储相同内容的字符串的
方式二:new + 构造器
常见构造方法一览:
String()
初始化一个新创建的 String 对象,使其表示一个空字符序列String(byte[] bytes)
通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 StringString(byte[] bytes, Charset charset)
通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 StringString(char[] value)
分配一个新的 String,使其表示字符数组参数中当前包含的字符序列String(char[] value, int offset, int count)
分配一个新的 String,它包含取自字符数组参数一个子数组的字符String(String original)
初始化一个新创建的 String 对象,使其表示一个与参数相同的字符序列;换句话说,新创建的字符串是该参数字符串的副本String(StringBuffer buffer)
分配一个新的字符串,它包含字符串缓冲区参数中当前包含的字符序列String(StringBuilder builder)
分配一个新的字符串,它包含字符串生成器参数中当前包含的字符序列
不同拼接操作对比
- 常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量
- 只要其中有一个是变量,拼接结果就在堆中
@Test
public void test() {
String s1 = "HelloWorld";
String s2 = "Hello";
String s3 = s2 + "World";
System.out.println(s1 == s3); // false
final String s4 = "Hello"; // 常量!!!
String s5 = s4 + "World";
System.out.println(s2 == s4); // true
System.out.println(s1 == s5); // true!!!
}
- 如果拼接的结果调用
intern()
,则其一定在常量池中 // API:当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用equals(Object)
方法确定),则返回池中的字符串。否则,将此String
对象添加到池中,并返回此String
对象的引用
public class StringTest {
String str = new String("good");
char[] ch = { 't', 'e', 's', 't' };
public void change(String str, char ch[]) {
str = "test ok";
ch[0] = 'b';
}
public static void main(String[] args) {
StringTest ex = new StringTest();
ex.change(ex.str, ex.ch); // 将地址赋给形参
System.out.println(ex.str); // good
System.out.println(ex.ch); // best
}
}
字符串常量池存放位置
一个 JVM 实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行,堆内存分为 3 部分:
- Young Generation Space 新生区【Young】
- Tenure Generation Space 养老区【Old】
- *Permanent Space 永久存储区【Perm】
常用方法
int length()
:返回字符串的长度,return value.length
char charAt(int index)
: 返回某索引处的字符,return value[index]
boolean isEmpty()
:判断是否是空字符串,return value.length == 0
String toLowerCase()
:使用默认语言环境,将 String 中的所有字符转换为小写String toUpperCase()
:使用默认语言环境,将 String 中的所有字符转换为大写String trim()
:返回字符串的副本,忽略前导空白和尾部空白boolean equals(Object obj)
:比较字符串的内容是否相同boolean equalsIgnoreCase(String anotherString)
:与equals()
类似,忽略大小写String concat(String str)
:将指定字符串连接到此字符串的结尾。 等价于用“+”int compareTo(String anotherString)
:比较两个字符串的大小String substring(int beginIndex)
:返回一个新的字符串,它是此字符串的从beginIndex
开始截取到最后的一个子字符串String substring(int beginIndex, int endIndex)
:返回一个新字符串,它是此字符串从beginIndex
开始截取到endIndex
(不包含)的一个子字符串boolean endsWith(String suffix)
:测试此字符串是否以指定的后缀结束boolean startsWith(String prefix)
:测试此字符串是否以指定的前缀开始boolean startsWith(String prefix, int toffset)
:测试此字符串从指定索引开始的子字符串是否以指定前缀开始boolean contains(CharSequence s)
:当且仅当此字符串包含指定的 char 值序列时,返回 trueint indexOf(String str)
:返回指定子字符串在此字符串中第一次出现处的索引int indexOf(String str, int fromIndex)
:返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始int lastIndexOf(String str)
:返回指定子字符串在此字符串中最右边出现处的索引int lastIndexOf(String str, int fromIndex)
:返回指定子字符串在此字符串中最后 一次出现处的索引,从指定的索引开始反向搜索 注:indexOf()
和lastIndexOf()
如果未找到都是返回 -1String replace(char oldChar, char newChar)
:返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的String replace(CharSequence target, CharSequence replacement)
:使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串String replaceAll(String regex, String replacement)
: 使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串String replaceFirst(String regex, String replacement)
: 使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串boolean matches(String regex)
:告知此字符串是否匹配给定的正则表达式String[] split(String regex)
:根据给定正则表达式的匹配拆分此字符串String[] split(String regex, int limit)
:根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中
字符串的转换
String ←→ BasicType
String与基本数据类型、包装类之间的转换
- String → 基本数据类型、包装类
- Integer包装类的
public static int parseInt(String s)
:可以将由"数字"字符组成的字符串转换为整型 - 类似地,使用 java.lang 包中的 Byte、Short、Long、Float、Double 类调相应的类方法可以将由"数字"字符组成的字符串,转化为相应的基本数据类型
- Integer包装类的
- 基本数据类型、包装类 → String
- 调用 String 类的
public String valueOf(int n)
可将 int 型转换为字符串 - 相应的
valueOf(byte b)
、valueOf(long l)
、valueOf(float f)
、valueOf(double d)
、valueOf(boolean b)
可由参数的相应类型到字符串的转换
- 调用 String 类的
String ←→ char[]
- char[] → String
- String 类的构造器1:
String(char[])
用字符数组中的 {全部字符} 创建字符串对象 - String 类的构造器2:
String(char[],int offset,int length)
用字符数组中的 {部分字符} 创建字符串对象
- String 类的构造器1:
- String → char[]
public char[] toCharArray()
:将字符串中的全部字符存放在一个字符数组中的方法public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
:提供了将指定索引范围内的字符串存放到数组中的方法
String ←→ byte[]
- byte[] → String // 解码
String(byte[] bys)
:通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 StringString(byte[] bys,int offset,int length)
:用指定的字节数组的一部分, 即从数组起始位置offset开始取length个字节构造一个字符串对象
- String → byte[] // 编码
public byte[] getBytes()
:使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中public byte[] getBytes(String charsetName)
:使用指定的字符集将此 String 编码到 byte 序列,并将结果存储到新的 byte 数组
练习题
- 模拟一个 trim 方法,去除字符串两端的空格
public String myTrim(String str) { if (str != null) { int start = 0;// 用于记录从前往后首次索引位置不是空格的位置的索引 int end = str.length() - 1;// 用于记录从后往前首次索引位置不是空格的位置的索引 while (start < end && str.charAt(start) == ' ') start++; while (start < end && str.charAt(end) == ' ') end--; if (str.charAt(start) == ' ') return ""; return str.substring(start, end + 1); } return null; }
- 将一个字符串进行反转,将字符串中指定部分进行反转。比如 "abcdefg" 反转为 "abfedcg"
public static String reverseRange(String str, int startIndex, int endIndex) { StringBuilder builder = new StringBuilder((str.length())); // part-1 builder.append(str.substring(0, startIndex)); // part-2 for (int i = endIndex; i >= startIndex; i--) builder.append(str.charAt(i)); // part-3 builder.append(str.substring(endIndex+1)); return builder.toString(); }
- 获取一个字符串在另一个字符串中出现的次数。比如:获取 "ab" 在 "abkkcadkabkebfkabkskab" 中出现的次数
public static int getCount(String mainStr, String subStr) { int mainLength = mainStr.length(); int subLength = subStr.length(); int index = 0, count = 0; if(mainLength >= subLength) { while(true) { if ((index = mainStr.indexOf(subStr, index)) != -1) { count++; index += subLength; } else break; } } return count; }
- 获取两个字符串中最大相同子串。比如: str1 = "abcwerthelloyuiodef";str2 = "cvhellobnm"。提示:将短的那个串进行长度依次递减的子串与较长的串比较
// 如果只存在一个最大长度的相同子串 public String getMaxSameSubString(String str1, String str2) { if (str1 != null && str2 != null) { String maxStr = (str1.length() > str2.length()) ? str1 : str2; String minStr = (str1.length() > str2.length()) ? str2 : str1; int len = minStr.length(); for (int i = 0; i < len; i++) // 0 1 2 3 4 此层循环决定要去几个字符 for (int x = 0, y = len - i; y <= len; x++, y++) if (maxStr.contains(minStr.substring(x, y))) return minStr.substring(x, y); } return null; } // 如果存在多个长度相同的最大相同子串:此时先返回String[] public String[] getMaxSameSubString1(String str1, String str2) { if (str1 != null && str2 != null) { StringBuffer sBuffer = new StringBuffer(); String maxString = (str1.length() > str2.length()) ? str1 : str2; String minString = (str1.length() > str2.length()) ? str2 : str1; int len = minString.length(); for (int i = 0; i < len; i++) { for (int x = 0, y = len - i; y <= len; x++, y++) { String subString = minString.substring(x, y); if (maxString.contains(subString)) { sBuffer.append(subString + ","); } } System.out.println(sBuffer); if (sBuffer.length() != 0) break; } String[] split = sBuffer.toString().replaceAll(",$", "").split("\\,"); return split; } return null; } // 如果存在多个长度相同的最大相同子串:使用ArrayList public List
getMaxSameSubString1(String str1, String str2) { if (str1 != null && str2 != null) { List list = new ArrayList (); String maxString = (str1.length() > str2.length()) ? str1 : str2; String minString = (str1.length() > str2.length()) ? str2 : str1; int len = minString.length(); for (int i = 0; i < len; i++) { for (int x = 0, y = len - i; y <= len; x++, y++) { String subString = minString.substring(x, y); if (maxString.contains(subString)) { list.add(subString); } } if (list.size() != 0) break; } return list; } return null; }
StringBuffer、StringBuilder
- String: 不可变的字符序列
- StringBuffer: 可变的字符序列; 线程安全的(同步方法),效率低
- StringBuilder: 可变的字符序列;JDK5.0新增,线程不安全的,效率高
注意:作为参数传递的话,方法内部 String 不会改变其值,StringBuffer 和 StringBuilder 会改变其值。
可变-源码分析
- String str = new String();
public String() { // private final char value[] this.value = new char[0]; }
- String str1 = new String("abc");
public String(String original) { this.value = original.value; // !!! this.hash = original.hash; }
- StringBuffer sb1 = new StringBuffer(); // new char[16]
// Constructs a string buffer with no characters in it // and an initial capacity of 16 characters. public StringBuffer() { super(16); }
- sb1.append('a'); sb1.append('b');
value[0] = 'a'; // count++ value[1] = 'b'; // count++
- StringBuffer sb2 = new StringBuffer("abc"); // new char["abc.length" + 16]
public StringBuffer(String str) { super(str.length() + 16); append(str); }
- System.out.println(sb2.length()); // 3
// The count is the number of characters used. public synchronized int length() { return count; }
- sb1.append("<16个字符>"); sb1.append('a');
常用方法
StringBuffer append(xxx)
:提供了很多的append()方法,用于进行字符串拼接StringBuffer delete(int start, int end)
:删除指定位置的内容StringBuffer replace(int start, int end, String str)
:把 [start,end) 位置替换为 strStringBuffer insert(int offset, xxx)
:在指定位置插入xxxStringBuffer reverse()
:把当前字符序列逆转
public int indexOf(String str)
public String substring(int start, int end)
这个要注意,本身不会切,返回的结果是子串public int length()
public char charAt(int n)
public void setCharAt(int n, char ch)
String ←→ Buffer/Builder
- String → Buffer/Builder
- 调用 StringBuffer/StringBuilder 的构造器
- 调用 StringBuffer/StringBuilder 的
toString()
- Buffer/Builder → String:调用 String 的构造器
String(StringBuffer buffer)
String(StringBuilder builder)
String str = null;
StringBuffer sb = new StringBuffer();
sb.append(str);
System.out.println(sb.length()); // 4
System.out.println(sb); // "null"
StringBuffer sb1 = new StringBuffer(str);
System.out.println(sb1); // NullPointerException
------------------StringBuffer------------------
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
--------------AbstractStringBuilder--------------
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
private AbstractStringBuilder appendNull() {
int c = count;
ensureCapacityInternal(c + 4);
final char[] value = this.value;
value[c++] = 'n';
value[c++] = 'u';
value[c++] = 'l';
value[c++] = 'l';
count = c;
return this;
}