字符串类(String)
概述
- 字符串都是对象,是一种特殊的对象。
- String类对象是一种常量,一经初始化就不可更改。
- 可以通过String类的构造器,将其他的字符数组,字节数组或字符按指定编码转换为字符串。
注意
- 应使用equals()方法比较两个字符串的值是否相等,而不是使用“==”比较符,因为后者比较的是字符串对象地址而非字符串本身。
建议使用:
"content".equals(str)
方法,避免空指针异常。 - ""为长度为0的字符串对象,而null代表字符串变量没有获取任何字符串对象的引用。要判断字符串既不是null也不是空串可以使用:
if (str != null && str.length() != 0)
而且尽量避免在null串上使用方法,将会报空指针异常。
构造器
- 字节数组(开始位置和长度可选,编码可选)。
- 字符数组(开始位置和长度可选,编码可选)。
- StringBuffer或StringBuilder对象。
创建字符串的方法和区别
- String str = "java";
- String str = new String("java");
- String str = "Hello" + str;
- 方法1:直接使用等号创建对象,此时JVM首先查找方法区字符串常量池是否有"java"字符串,如果有则让变量str持有该字符串的引用,如果没有则将字符串"java"驻留在方法区字符串常量池中,并让变量str持有该字符串的引用。
- 方法2:使用new创建字符串对象,此时创建对象的位置和普通对象一样,在堆区,变量str持有堆区String对象的引用。
- 方法3:使用字符串连接符“+”创建对象(其中两边至少要有一个是字符串变量),其实相当于使用StringBuffer的append方法创建对象,该字符串存在堆区。
所以当使用方法1创建两个一样字符串的String对象时,其引用的地址是一样的。而使用方法2创建两个相同字符串对象时,其引用的地址是不一样的。
程序
String s1 = "java";
String s2 = "java"; // s1和s2是同一个字符串内存中的引用,String类型字符串为常量
String s3 = new String( "java");
String s4 = new String( "java"); // s3和s4是堆区不同对象的引用,而对象中又持有方法区字符串常量池中"java"的引用
System.out.println( s1 == s2); // true
System.out.println( s1.equals( s2)); // true
System.out.println( s3 == s4); // false
System.out.println( s3.equals( s4)); // true
在java中,当同一个字符串字面值出现多次时,在内存中只会保存该字符串的一份拷贝,即s1指向的"java"跟s2指向的"java"在内存中是同一个对象。这种方法叫做“字符串驻留(string interning)”。这样编译器就让字符串共享的方式来提高使用效率,该思想称之为享元模式。
而new运算符的作用是在堆内存中创建一个新的对象。因而这里两次new就会在堆中创建两个String对象,尽管它们的字面值是相同的,但其实是两个同不的对象,占两份空间,而对象中实际的字符串"java"是在方法区常量池中的,所以这两个对象也持有该字符串的引用。因此s3 == s4自然会返回false了。
如图
所以,当String类使用了字符串连接符“+”,是会创建额外的对象的*,如下代码将创建三个字符串对象。
String str = "Hello";
str += " java";
这三个对象分别是"Hello"," java","Hello java"。
*该处仍然有异议,经过验证,通过“+”创建的对象和其他两种方式创建的对象有本质区别。
注意
在Java 7以及更新版本中,常量池已经从方法区(永久代,the PermGen)移动到了堆区中,以解决方法区无法扩展内存的窘境。
方法
1. 获取字符串长度
int length()
2. 获取指定字符或字符串的位置
int indexOf(int ch)
int indexOf(int ch, int fromIndex)
int indexOf(String str)
int indexOf(String str, int fromIndex)
获取最后一个指定字符或字符串的位置
int lastIndexOf(int ch)
int lastIndexOf(int ch, int fromIndex)
int lastIndexOf(String str)
int lastIndexOf(String str, int fromIndex)
3. 获取指定位置上的字符
char charAt(int index)
4. 获取子串(包含头,不包含尾)
String substring(int beginIndex)
String substring(int beginIndex, int endIndex)
示例:
String str = "HeiMa,Cheng,Xu,Yuan";
// 1. 获取长度
int length = str.length(); // 19
// 2. 获取指定字符的位置,如果没有找到,则返回-1。
int index = str.indexOf('e'); // 1
int nextindex = str.indexOf('e', index + 1); // 8
int lastindex = str.lastIndexOf( 'a'); // 17
// 3. 获取指定位置的字符,如果给定索引位置超出字符串长度,则抛出StringIndexOutOfBoundsException异常。
char ch = str.charAt(3); // M
// 4. 获取子串,包含头,不包含尾。
String substr1 = str.substring(6); // Cheng ,Xu,Yuan
String substr2 = str.substring(6, 14); // Cheng ,Xu
5. 判断字符串是否以指定字符或字符串开始和结束
boolean startsWith(String prefix)
boolean startsWith(String prefix, int toffset)
boolean endsWith(String suffix)
6. 判断字符串是否包含指定子串
boolean contains(CharSequence s)
7. 字符串替换(regex为正则表达式)
String replace(char oldChar, char newChar)
String replace(CharSequence target, CharSequence replacement)
String replaceAll(String regex, String replacement)
String replaceFirst(String regex, String replacement)
8. 字符串分割(regex为正则表达式)
String[] split(String regex)
String[] split(String regex, int limit)
9. 字符串格式化
static String format(Locale l, String format, Object... args)
static String format(String format, Object... args)
10. 转换字符串大小写
String toLowerCase()
String toLowerCase(Locale locale)
String toUpperCase()
String toUpperCase(Locale locale)
示例:
String str = "HeiMa,Cheng,Xu,Yuan";
// 5. 判断字符串是否以指定字符串开头或结尾
boolean b1 = str.startsWith( "HeiMa" ); // true
boolean b2 = str.endsWith( "HeiMa" ); // false
// 6. 判断字符串是否包含指定字符串
boolean b3 = str.contains( "Cheng,Xu" ); // true
// 7. 将字符串中指定字符串替换成另一字符串
String placedstr = str.replace("Yuan", "Ren" ); // HeiMa,Cheng,Xu ,Ren
// 8. 将字符串按指定字符分解
String[] strs = str.split(","); // [HeiMa][Cheng ][ Xu][Yuan ]
// 9. 将格式化字符串
String fstr = String.format("%8.2f" , 12345.6789012); // 12345.68
// 10. 转换字符串大小写
String ustr = str.toUpperCase(); // HEIMA,CHENG,XU,YUAN
String lstr = str.toLowerCase(); // heima ,cheng ,xu, yuan
11. 将字符串转换为数组
byte[] getBytes()
byte[] getBytes(Charset charset)
byte[] getBytes(String charsetName)
char[] toCharArray()
12. 去除字符串首尾的空白字符
String trim()
13. 判断字符串是否为空
boolean isEmpty()
14. 将基本数据类型,字符数组或对象转换为字符串
static String valueOf(boolean b)
static String valueOf(char c)
static String valueOf(char[] data)
static String valueOf(char[] data, int offset, int count)
static String valueOf(double d)
static String valueOf(float f)
static String valueOf(int i)
static String valueOf(long l)
static String valueOf(Object obj)
15. 匹配正则表达式
boolean matches(String regex)
示例:
String str = " heima\t";
// 11. 将字符串转换为数组
byte[] bstr = str.getBytes();
char[] cstr = str.toCharArray();
// 12. 去除字符串首尾空白字符
String tstr = str.trim(); // "heima"
// 13. 判断字符串是否为空
boolean b = str.isEmpty(); // false
// 14. 将基本数据类型转换为字符串
String floatStr = String. valueOf(12345.678912); // "12345.678912"
String objStr = String. valueOf(new Date()); // "Sat Dec 13 04:00:57 CST 2014"
// 15. 是否匹配正则表达式
boolean rb = str.matches( "\\w+" ); // false
重写Object的方法
1. 判断字符串是否相等(系统的默认比较方式)
boolean equals(Object anObject)
2. 按字典顺序比较字符串(系统的默认比较方式)
int compareTo(String anotherString)
3. 获取字符串的哈希值(系统的默认比较方式)
int hashCode()
4. 获取字符串本身(系统默认的字符串表现形式)
String toString()
案例
package string;
import java.util.Arrays;
public class StringText {
public static void main(String[] args) {
// Case 1:对字符串数组进行字典顺序排序。
String strs[] = {"hei", "ma" , "cheng" , "xu" , "yuan" };
printArray(strs );
sortStrings(strs , 0, strs .length - 1);
printArray(strs );
printLine();
// Case 2:获取字符串中指定字串出现的次数
String str = "woiheimatbxqheimaydydheimaxxheimadjscheimabyzdq" ;
String key = "heima";
int count = getKeyCount(str , key );
System.out.println( str + "\n\"" + key + "\" COUNT = " + count );
printLine();
// Case 3:将字符串中所有字串按长短依次打印
str = "itheima";
printAllSubstring(str );
printLine();
// Case 4:获取两个字符串的最大相同字串
String str1 = "woiheimatbxqheimaydydheimaxxheimadjscheimabyzdq" ;
String str2 = "woaiheimadanshiwogengaiheimaxxheimahaha" ;
String same_substring = getSameSubstring(str1 , str2 );
System.out.println( "same substring = " + same_substring );
printLine();
// Case 5:对字符串中字符进行自然顺序排序
str = "woiheimatbxqheimaydydheimaxxheimadjscheimabyzdq" ;
String sortstr = sortString(str );
System.out.println( "source:\t\t" + str + "\ndestination:\t" + sortstr );
printLine();
// Case 6:trim()方法的使用
str = " hei ma Java ";
System.out.println( str.trim());
System.out.println( myTrim(str));
}
/**
* 去除字符串首尾的空格
* @param str
* @return
*/
public static String myTrim(String str ) {
int start = 0;
int end = str.length() - 1;
// 循环判断首尾的字符是否是空格,如果是则移动索引继续判断
while ( start <= end && str.charAt( start) == ' ') {
start++;
}
while ( start <= end && str.charAt( end) == ' ') {
end--;
}
// 返回没有空格的子串
return str.substring( start, end + 1);
}
/**
* 将字符串中字符按字典顺序排序
* @param str
* @return
*/
public static String sortString(String str ) {
char[] cs = str.toCharArray();
Arrays.sort(cs );
return new String( cs);
}
/**
* 获取两个字符串中最大公共子串
* @param str1
* @param str2
* @return
*/
public static String getSameSubstring(String str1 , String str2 ) {
// 先判断长串和短串
String max;
String min;
max = str1.length() > str2.length() ? str1 : str2;
min = max.equals(str1) ? str2 : str1;
for (int i = 0; i < min.length(); i++) {
for (int start = 0, end = min.length() - i; end <= min .length(); start ++, end ++) {
// 由长到短循环获取短串的所有子串,并判断长串中是否包含,如果包含,则返回这个子串
String str = min.substring( start, end);
if ( max.indexOf( str) >= 0) {
return str ;
}
}
}
return null;
}
/**
* 将字符串中所有子串按长短依次打印
* @param str
*/
public static void printAllSubstring(String str) {
for (int i = 0; i < str.length(); i++) {
// i:整串长度 - 子串长度
for (int start = 0, end = str.length() - i; end <= str .length(); start ++, end ++) {
// 索引从0~end开始,每次循环均右移一个字符,直到碰到字符串末尾为止
System. out .print(str .substring(start , end ) + "\t" );
}
System.out.println();
}
}
/**
* 获取字符串 str中指定字串key出现的次数
* @param str
* @param key
* @return
*/
public static int getKeyCount(String str, String key) {
int index = 0;
int count = 0;
while (( index = str.indexOf( key, index)) != -1) {
index += key.length();
count++;
}
return count;
}
/**
* 使用快速排序算法对字符串数组排序
* @param strs为
* @param a
* @param b
*/
public static void sortStrings(String[] strs, int a, int b ) {
if (a >= b) return;
int i = a;
int j = b;
String str = strs[i];
while ( i < j) {
while ( strs[ j].compareTo( str) >= 0 && i < j) {
j--;
}
if (i < j) {
strs[ i] = strs[ j];
i++;
}
while ( strs[ i].compareTo( str) <= 0 && i < j) {
i++;
}
if (i < j) {
strs[ j] = strs[ i];
j--;
}
}
strs[i] = str;
sortStrings(strs , a , i - 1);
sortStrings(strs , i + 1, b );
}
/**
* 打印字符串数组
* @param strs
*/
public static void printArray(String[] strs) {
System.out.print( "[" );
for (int i = 0; i < strs.length - 1; i++) {
System.out.print( strs[ i] + ", " );
}
System.out.println( strs[ strs. length - 1] + "]" );
}
/**
* 打印分隔线
*/
public static void printLine() {
System.out.println( "-----------------------------" );
}
}
运行结果
[hei, ma, cheng, xu, yuan]
[cheng, hei, ma, xu, yuan]
-----------------------------
woiheimatbxqheimaydydheimaxxheimadjscheimabyzdq
"heima" COUNT = 5
-----------------------------
itheima
itheim theima
ithei theim heima
ithe thei heim eima
ith the hei eim ima
it th he ei im ma
i t h e i m a
-----------------------------
source1: woiheimatbxqheimaydydheimaxxheimadjscheimabyzdq
source2: woaiheimadanshiwogengaiheimaxxheimahaha
same substring = heimaxxheima
-----------------------------
source: woiheimatbxqheimaydydheimaxxheimadjscheimabyzdq
destination: aaaaabbcddddeeeeehhhhhiiiiiijmmmmmoqqstwxxxyyyz
-----------------------------
hei ma Java
hei ma Java
字符串构建类(StringBuffer/StringBuilder)
概述
由于String类没有提供用于修改字符串的方法,如果希望修改,一个替代的方式是使用subString方法和字符串连接符“+”联合使用来达到目的。但是如果是源自于文件或键盘的单个字符或较短字符串汇成较长字符串,这样就需要这样频繁拼接,在方法区字符串池中就会产生大量的无用的零碎的字符串。为此,应该使用字符串构建类StringBuffer/StringBuilder,该类的特点是在堆区完成字符串的拼接和修改,所以不会产生大量的碎片。
特点
1. 是一个字符串缓冲区,其实就是一个容器。
2. 长度是可变,可将任意数据都转成字符串进行存储。
3. 容器对象提供很多对容器中数据的操作功能,比如:添加,删除,查找,修改。
4. 必须所有的数据最终变成一个字符串。
5. StringBuffer和Stringbuilder类只有一个区别:前者是线程安全的,而后者效率较高,优先使用后者。
与数组比较
- 数组存储完可以单独操作每一个元素,每一个元素都是独立的。
- 字符串缓冲区,所有存储的元素都被转成字符串,而且最后拼成了一个大的字符串。
- 长度可变,内部维护了一个数组,有自动扩展数组的功能。
构造器(以StringBuilder为例)
StringBuilder()
StringBuilder(String str)
方法(以StringBuilder为例)
1. 尾部添加(支持所有基本数据类型和有toString()方法的类)
StringBuffer append(content)
2. 删除子串
StringBuilder delete(int start, int end)
StringBuilder deleteCharAt(int index)
3. 获取指定字符或字符串的位置
int indexOf(String str)
int indexOf(String str, int fromIndex)
4. 获取指定位置上的字符
char charAt(int index)
5. 插入(支持所有基本数据类型、字符数组和有toString()方法的类)
StringBuilder insert(int offset, content)
6. 逆序
StringBuilder reverse()
7. 修改
void setCharAt(int index, char ch)
void setLength(int newLength)
StringBuilder replace(int start, int end, String str)
8. 获取字符串
String toString()
示例
// 创建字符串缓冲区
StringBuilder buffer = new StringBuilder();
// 1. 添加数据
buffer.append( true).append( " Hello Java ").append(3.14); // "true Hello Java 3.14"
// 2. 删除数据
buffer.delete(5, 11); // "true Java 3.14"
// 3. 获取指定字符或字符串的位置
int index = buffer.indexOf( "Java" ); // 5
// 4. 获取指定位置上的字符
char ch = buffer.charAt(5); // 'J'
// 5. 插入数据
buffer.insert(4, " Hi" ); // "true Hi Java 3.14"
// 6. 逆序
buffer.reverse(); // "41.3 avaJ iH eurt"
// 7. 替换数据
buffer.replace(0, buffer.length(), "Hello Java" ); // "Hello Java"
// 8. 修改长度
buffer.setLength(5); // "Hello"
System.out.println( buffer.toString());