关于String这个特殊类,我到现在都还没搞明白,我觉得可能主要是由于没有搞懂JVM的内存结构引起的,但还是对其进行以下记录。
字符串一旦初始化就不可以被改变。
String str = "abcd";
以上这句代码的意思就是——JVM一启动,字符串就已经开始存储了,存储在了方法区中的常量池当中。
这里还有一个说烂了的面试题:对于以下代码
String str = "abcd"; // str是一个类类型变量,"abcd"是一个对象
String str1 = new String("abcd");
str和str1有什么区别?
答:str在内存中只有一个对象,str1在内存中有两个对象。
下面我们再来看以下代码会输出什么?
System.out.println(str == str1);
System.out.println(str.equals(str1));
会发现第一句输出false
,第二句输出true
,得出结论:String类复写了Object类中的equals(),该方法用于判断字符串的内容是否相同。
String类是用于描述字符串事物的,那么它就提供了多个方法对字符串进行操作。常见的操作有哪些呢?
获取
int length();----获取长度
char charAt(int index);
int indexOf(int ch);
返回的是ch在字符串中第一次出现的位置int indexOf(int ch, int fromIndex);
从fromIndex指定位置开始,获取ch在字符串中出现的位置int indexOf(String str);
返回的是str在字符串中第一次出现的位置int indexOf(String str, int fromIndex);
从fromIndex指定位置开始,获取str在字符串中出现的位置int lastIndexOf(int ch);
反向索引一个字符出现的位置例,
public static void sop(Object obj) {
System.out.println(obj);
}
public static void method_get() {
String str = "abcdeakpf";
// 长度
sop(str.length());
// 根据索引获取字符
sop(str.charAt(4)); // 当访问到字符串中不存在的角标时,会发生StringIndexOutOfBoundsException。
// 根据字符获取索引
sop(str.indexOf('m', 3)); // 如果没有找到返回-1
// 反向索引一个字符出现的位置
sop(str.lastIndexOf("a"));
}
判断
boolean contains(str);
indexOf(str)
:可以索引str第一次出现的位置,如果返回-1,表示该str不在字符串中存在,所以,也可以用于对指定字符串判断是否包含,而且该方法既可以判断,又可以获取出现的位置。 boolean isEmpty();
原理就是判断长度是否为0boolean startsWith(str);
boolean endsWith(str);
boolean equals(str);
boolean equalsIgnoreCase(str);
例,
public static void sop(Object obj) {
System.out.println(obj);
}
public static void method_is() {
String str = "ArrayDemo.java";
// 判断文件名称是否是以Array单词开头
sop(str.startsWith("Array"));
// 判断文件名称是否是.java文件
sop(str.endsWith(".java"));
// 判断文件名称是否包含Demo
sop(str.contains("Demo"));
}
转换
String(char[])
String(char[], offset, count);
将字符数组中的一部分转成字符串 ,count是个数static String copyValueOf(char[]);
static String copyValueOf(char[] data, int offset, int count);
static String valueOf(char[]);
char[] toCharArray();
String(byte[]);
String(byte[], offset, count);
将字节数组中的一部分转成字符串byte[] getBytes();
将基本数据类型转成字符串
static String valueOf(int);
static String valueOf(double);
将基本数据类型转成字符串有一个更简单的方法,那就是:
3+""; // String.valueOf(3); int--->String
例,
public static void sop(Object obj) {
System.out.println(obj);
}
public static void method_trans() {
char[] arr = {'a','b','c','d','e','f'};
String s = new String(arr,1,3);
sop("s="+s);
String s1 = "zxcvbnm";
char[] chs = s1.toCharArray();
for (int x = 0; x < chs.length; x++) {
sop("chs="+chs[x]);
}
}
特殊:字符串和字节数组在转换过程中,是可以指定编码表的。
替换
String replace(oldchar, newchar);
例,
public static void sop(Object obj) {
System.out.println(obj);
}
public static void method_replace() {
String s = "hello java";
// String s1 = s.replace('q', 'n'); // 如果要替换的字符不存在,返回的还是原串
String s1 = s.replace("java", "world");
sop("s="+s);
sop("s1="+s1);
}
切割
String[] split(regex);
例,
public static void sop(Object obj) {
System.out.println(obj);
}
public static void method_split() {
String s = "zhangsan,lisi,wangwu";
String[] arr = s.split(",");
for (int x = 0; x < arr.length; x++) {
sop(arr[x]);
}
}
子串。获取字符串中的一部分
String substring(begin);
String substring(begin, end);
例,
public static void sop(Object obj) {
System.out.println(obj);
}
public static void method_sub() {
String s = "abcdef";
sop(s.substring(20)); //从指定位置开始到结尾,如果角标不存在,会出现字符串角标越界异常
sop(s.substring(2,4));//包含头,不包含尾。获取整个字符串:s.substring(0, s.length());
}
转换,去除空格,比较
String toUpperCase();
String toLowerCase();
String trim();
int compareTo(String);
例,
public static void sop(Object obj) {
System.out.println(obj);
}
public static void method_7() {
String s = " Hello Java ";
sop(s.toLowerCase());
sop(s.toUpperCase());
sop(s.trim());
String s1 = "a1c";
String s2 = "aaa";
sop(s1.compareTo(s2));
}
练习一、模拟一个trim()方法,去除字符串两端的空格。
解:
思路:
1. 判断字符串第一个位置是否是空格,如果是继续向下判断,直到不是空格为止。结尾处判断空格也是如此
2. 当开始和结尾都判断到不是空格时,就是要获取的字符串
public class StringTest {
public static void sop(String str) {
System.out.println(str);
}
public static String myTrim(String str) {
int start = 0, end = str.length()-1;
while(start <= end && str.charAt(start) == ' ')
start++;
while(start <= end && str.charAt(end) == ' ')
end--;
return str.substring(start, end+1);
}
public static void main(String[] args) {
String s = " ab cd ";
sop("("+s+")");
s = myTrim(s);
sop("("+s+")");
}
}
练习二、将字符串中指定的部分进行反转,"abcdefg"--->"abfedcg"
。
解:
思路:
1. 将字符串变成数组
2. 对数组反转
3. 将数组变成字符串
class StringTest {
public static void sop(String str) {
System.out.println(str);
}
public static String reverseString(String s, int start, int end) {
// 字符串变成数组
char[] chs = s.toCharArray();
// 反转数组
reverse(chs, start, end);
// 将数组变成字符串
return new String(chs);
}
public static String reverseString(String s) {
return reverseString(s, 0, s.length());
}
private static void reverse(char[] arr, int x, int y) {
for (int start = x, end = y - 1; start < end; start++, end--) {
swap(arr, start, end);
}
}
private static void swap(char[] arr, int x, int y) {
char temp = arr[x];
arr[x] = arr[y];
arr[y] = temp;
}
public static void main(String[] args) {
String s = "ab cd ";
sop("("+s+")");
sop("("+reverseString(s)+")");
sop("("+reverseString(s, 0, 3)+")");
}
}
练习三、获取一个字符串在另一个字符串中出现的次数。例如,”kk”在”abkkcdkkefkkskk”出现的次数。
解:
思路:
1. 定义一个计数器
2. 获取”kk”第一次出现的位置
3. 从第一次出现的位置后剩余的字符串中继续获取”kk”出现的位置,每获取一次就计数一次
4. 当获取不到时,计数完成
public class StringTest {
public static void main(String[] args) {
String str = "nbadfnbaghjnbaklnba";
String key = "nba";
int count = getKeyCount(str, key);
System.out.println("count = " + count);
}
public static int getKeyCount(String str, String key) {
// 1,定义变量计数
int count = 0;
// 2,定义变量,记录每次找到的角标
int index = 0;
// 2,循环。条件是indexOf查找方法返回的结果不是-1,而且要明确下次查找的位置,indexOf(String, fromIndex);
while ((index = str.indexOf(key, index)) != -1) {
count++;
// 每找完一次,都要确定下次要找的起始位置。上次位置+key的长度
index += key.length();
}
return count;
}
}
练习四、获取两个字符串中最大相同子串。例如,”abcwerthelloyuiodef”和”cvhellobnm”。
解:
思路:
1. 以短的字符串为主,到长的字符串中去判断是否存在,如果存在,已找到。
2. 如果没有找到,将短的字符串的长度递减,获取子串,继续到长的串中查找,只要找到就结束。
3. 没有找到,说明没有相同的
public class StringTest {
public static void main(String[] args) {
String s1 = "sadfcctvghjkl";
String s2 = "zxcctvcv";
String maxSub = getMaxSubString(s1, s2);
System.out.println("maxSub = " + maxSub);
}
public static String getMaxSubString(String s1, String s2) {
// 确定哪个是长的哪个是短的
String longStr, shortStr;
longStr = s1.length() > s2.length() ? s1 : s2;
shortStr = s1.equals(longStr) ? s2 : s1;
// System.out.println("long:" + longStr);
// System.out.println("short:" + shortStr);
// 对短的字符串操作,从短串中取子串,到长字符串中判断,是否存在
for (int x = 0; x < shortStr.length(); x++) {
for (int y = 0, z = shortStr.length() - x; z <= shortStr.length(); y++, z++) {
// 根据y、z获取子串
String temp = shortStr.substring(y, z);
// System.out.println(temp);
if (longStr.contains(temp)) {
return temp;
}
}
}
return null;
}
}
练习五、字符串可以比较大小吗?如果有!将字符串数组排序。
解:基本类型数值可以通过比较运算符(>、==、<)比较大小和相等。对象亦可以比较是否相等,谁大谁小,都是通过方法完成的。对象比较相同用的是Object类的equals()方法,子类一般情况下都会复写,建立自己判断相同的依据。对象比大小,用的也是方法,该功能有3种情况,所以使用int类型——正数、负数、0。前者大于后者返回正数,前者小于后者返回负数,前者等于后者返回零。
示例代码如下:
public class StringTest {
public static void main(String[] args) {
String[] strs = {"haha", "nba", "abc", "cba", "haha", "qq", "hiahia"};
printArray(strs);
// 对字符串数组排序
sort(strs);
printArray(strs);
}
public static void sort(String[] strs) {
for (int x = 0; x < strs.length - 1; x++) {
for (int y = x + 1; y < strs.length; y++) {
if (strs[x].compareTo(strs[y]) > 0) {
swap(strs, x, y);
}
}
}
}
private static void swap(String[] strs, int x, int y) {
String temp = strs[x];
strs[x] = strs[y];
strs[y] = temp;
}
public static void printArray(String[] strs) {
for (int i = 0; i < strs.length; i++) {
if (i != strs.length - 1) {
System.out.print(strs[i] + ",");
} else {
System.out.println(strs[i]);
}
}
}
}
练习六、对字符串中的字符进行自然排序。
解:
思路:
1. 要排序,我会!但是只会数组排序。
2. 怎么能把字符串转成数组呢?
3. 到字符串中找方法
4. 排序我熟
5. 将排序后的数组变成字符串
public class StringTest {
public static void main(String[] args) {
String str = "jbdsakncv";
String sortString = sortChar(str);
System.out.println(sortString);
}
/**
* 对给定的字符串中的字符进行自然排序,并返回排序后的字符串
* @param str
* @return
*/
public static String sortChar(String str) {
// 1,将字符串转成字符数组
char[] chs = stringToArray(str);
// 2,对数组排序
sort(chs);
// 3,将数组转成字符串
return toString(chs);
}
/*
* 将字符数组转成字符串
*/
private static String toString(char[] chs) {
return new String(chs);
}
/*
* 对字符数组进行升序排序
*/
private static void sort(char[] chs) {
Arrays.sort(chs);
}
/*
* 将字符串转成字符数组
*/
private static char[] stringToArray(String str) {
return str.toCharArray();
}
}
StringBuffer是字符串缓冲区,是一个容器。特点有:
试看以下代码:
String str = "a" + 4 + "c";
如果我们分析以上代码在内存中的变化情况,可得出以下结论:
用代码来表示就是:
str = new StringBuffer().append("a").append(4).append("c").toString();
存储
StringBuffer append(data);
—将指定的数据作为参数添加到已有数据的结尾处StringBuffer insert(index, data);
—可以将数据插入到指定index位置例,
public static void sop(String str) {
System.out.println(str);
}
public static void method_add() {
StringBuffer sb = new StringBuffer();
sb.append("abc").append(true).append(34); // 方法调用链
// StringBuffer sb1 = sb.append(34);
// sop("sb==sb1:"+(sb == sb1));
sb.insert(1, "qq");
sop(sb.toString()); // aqqbctrue34
// sop(sb1.toString());
}
关于输出打印println()
和print()
方法,我们应注意:它俩会将所有要打印的数据先转成字符串再输出,对于对象会自动调用toString()方法。
删除
StringBuffer delete(start, end);
—删除缓冲区中的数据,包含start,不包含endStringBuffer deleteCharAt(index);
—删除指定位置的字符例,
public static void sop(String str) {
System.out.println(str);
}
public static void method_delete() {
StringBuffer sb = new StringBuffer("abcde");
// sb.delete(1, 3);
// 清空缓冲区
// sb.delete(0, sb.length());
// sb.delete(2, 3);
sb.deleteCharAt(2);
sop(sb.toString());
}
char charAt(int index);
int indexOf(String str);
int lastIndexOf(String str);
int length();
String substring(int start, int end);
修改
StringBuffer replace(start, end, string);
void setCharAt(int index, char ch);
例,
public static void sop(String str) {
System.out.println(str);
}
public static void method_update() {
StringBuffer sb = new StringBuffer("abcde");
// sb.replace(1, 4, "java");
sb.setCharAt(2, 'k');
sop(sb.toString());
}
StringBuffer reverse();
将缓冲区中的指定数据存储到指定字符数组中
void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin);
例,
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("abcdef");
char[] chs = new char[6];
sb.getChars(1, 4, chs, 1);
for (int x = 0; x < chs.length; x++) {
sop("chs["+x+"]="+chs[x]+";");
}
}
public static void sop(String str) {
System.out.println(str);
}
查看API帮助文档,我们发现StringBuffer类有一个空参构造方法,如下:
API帮助文档对其的解释是:
构造一个其中不带字符的字符串缓冲区,其初始容量为16个字符。
我的理解是:字符串缓冲区中维护了一个”可变长度的数组”,所谓”可变长度的数组”其实就是超出内部数组长度后,新建数组长度要是原数组的1.x(1.5或者1.75等)倍数,并将原数组的数据复制到新数组中,并将新的元素也添加到新数组中。
练习一、通过缓冲区,将要打印的矩形组成元素*进行存储后,一次性返回,并输出。
解:题目比较简单,直接贴出代码。
public class StringBufferTest {
public static void main(String[] args) {
String rec = draw(8, 5);
System.out.println(rec);
}
/*
* 画矩形的功能
* 将需要组成矩形的元素进行临时存储
*/
public static String draw(int row, int col) {
// 定义一个临时容器
StringBuffer sb = new StringBuffer();
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
sb.append("*");
}
sb.append("\n");
}
return sb.toString();
}
}
练习二、将int数组的元素转成字符串,格式为:[34, 12, 67]。
解:此题也比较简单,我们直接贴出代码。
public class StringBufferTest {
public static void main(String[] args) {
int[] arr = {34, 12, 67, 43, 69};
String s = toString(arr);
System.out.println(s);
}
/*
* int数组转成字符串,通过StringBuffer
*/
public static String toString(int[] arr) {
StringBuffer sb = new StringBuffer();
sb.append("[");
for (int i = 0; i < arr.length; i++) {
if (i != arr.length - 1)
sb.append(arr[i] + ",");
else
sb.append(arr[i] + "]");
}
return sb.toString();
}
}
练习三、以下代码,会打印输出什么?
public class StringBufferTest {
public static void main(String[] args) {
StringBuffer buf1 = new StringBuffer("hello");
StringBuffer buf2 = new StringBuffer("java");
test(buf1, buf2);
System.out.println(buf1 + "..." + buf2);
}
public static void test(StringBuffer buf1, StringBuffer buf2) {
buf1.append(buf2);
buf1 = buf2;
}
}
解:好久都没分析对象内存结构图了,瞬间就懵逼了,画了如下的一张图之后,才醒悟过来,惭愧!!!
所以,以上代码运行会在控制台中输出:
hellojava…java
JDK1.5版本之后,出现了StringBuilder
,它和StringBuffer
用法一样,所以就不再过多讲解了。
StringBuffer
与StringBuilder
的区别:
StringBuffer
是线程同步的StringBuilder
是线程不同步的一般可以建议选择StringBuilder
,因为其速度快。但是将StringBuilder
的实例用于多个线程时是不安全的。如果需要这样的同步,则建议使用StringBuffer
。
JDK版本升级的要素无非就是:
将基本数据值封装成了对象,带来的好处就是可以在对象中定义更多的属性和行为对基本数据类型进行操作。
基本数据类型包装类的最常见作用:就是用于基本数据类型和字符串类型之间做转换。
基本数据类型的包装类.toString(基本数据类型值)
Integer.toString(34);// 将整数34变成字符串"34";
字符串转成基本数据类型:
xxx a = Xxx.parseXxx(String);
例,
int a = Integer.parseInt("123");
double b = Double.parseDouble("12.23");
boolean b = Boolean.parseBoolean("true");
// 还有一种形式:
Integer i = new Integer("123");
int num = i.intValue();
// 将一个字符串转成整数
int num = Integer.parseInt("a123");//NumberFormatException,必须传入数字格式的字符串
System.out.println("num="+(num+4));
十进制转成其他进制
toBinaryString();
toHexString();
toOctalString();
例,
System.out.println(Integer.toBinaryString(6));
System.out.println(Integer.toBinaryString(-6));
System.out.println(Integer.toHexString(60));
其他进制转成十进制
parseInt(String, radix);
例,
int x = Integer.parseInt("3c", 16);
System.out.println("x="+x);
JDK1.5版本以后出现的新特性——自动装箱与自动拆箱。
JDK1.5以后,简化了定义方式:如
Integer i = new Integer(4);
可以直接写成:
Integer i = 4; // 自动装箱。Integer.valueOf(4);
接着自动拆箱:
i = i + 6; // 右边的i自动拆箱了。i.intValue() + 6,运算完的结果又一次装箱赋值给i。
需要注意:在使用时,Integer x = null;
上面的代码就会出现NullPointerException
。
运行以下代码,看会输出什么?
class IntegerDemo {
public static void sop(String str) {
System.out.println(str);
}
public static void main(String[] args) {
Integer m = 128;
Integer n = 128;
sop("m==n:"+(m==n));
Integer a = 127;
Integer b = 127;
sop("a==b:"+(a==b));
}
}
答案是:false
和true
。以下是解析:
Integer m = 128;
Integer n = 128;
sop("m==n:"+(m==n)); // false,因为m和n指向了不同Integer对象,
Integer a = 127;
Integer b = 127;
// 结果为true。因为a和b指向了同一个Integer对象,
// 因为当数值在byte范围内时,对于新特性,如果该数值已经存在则不会再开辟新的空间
sop("a==b:"+(a==b));
得出结论:
JDK1.5以后,自动装箱的值如果在byte范围之内,相同的值不会单独开辟空间,而是重复使用。
练习:有这样一个字符串:”23 10 -8 0 3 7 108”,请对字符串中的数值进行升序排序后,生成一个数值有序的字符串——”-8 0 3 7 10 23 108”。
解:
思路:
1. 排序,而且对整数数值排序。
2. 排序的元素都在字符串中,如何取出?
3. 找String类的功能,而且我发现,数字之间的间隔都是相同的空格,有规律。如果有这个功能,结果是返回多个字符串,找到方法:String[] split(String)
4. 将获取到的数字格式的字符串转成数字存储到数组中
5. 对数组排序
6. 将数组转成字符串
public class Test {
private static final String SEPARATOR = " ";
public static void main(String[] args) {
String numStr = "23 10 -8 0 3 7 108";
String sortStr = sortNumberString(numStr);
System.out.println(sortStr);
}
/**
* 对一个有多个数值的字符串,进行数值的排序
* @param numStr
* @return
*/
public static String sortNumberString(String numStr) {
// 1,将给定的字符串分解成多个数字格式的字符串
String[] numStrs = toStringArray(numStr);
// 2,将字符串数组转成int数组
int[] nums = toIntArray(numStrs);
// 3,对数组排序
sort(nums);
// 4,将int数组转成字符串
return toString(nums);
}
/*
* 将int数组转成字符串
*/
private static String toString(int[] nums) {
// 1,定义一个StringBuilder字符串缓冲区
StringBuilder sb = new StringBuilder();
for (int i = 0; i < nums.length; i++) {
if (i != nums.length)
sb.append(nums[i] + SEPARATOR);
else
sb.append(nums[i]);
}
return sb.toString();
}
/*
* 对int数组进行升序排序
*/
private static void sort(int[] nums) {
Arrays.sort(nums);
}
/*
* 将字符串数组转成int类型数组
*/
private static int[] toIntArray(String[] numStrs) {
// 1,创建一个int类型的数组,长度和字符串数组的长度一致
int[] nums = new int[numStrs.length];
// 2,对字符串数组进行遍历
for (int i = 0; i < numStrs.length; i++) {
// 3,将字符串数组中的元素通过parseInt()方法转换后,赋值给int类型的数组
nums[i] = Integer.parseInt(numStrs[i]);
}
return nums;
}
/*
* 将字符串按照指定的分隔,转成字符串数组
*/
private static String[] toStringArray(String numStr) {
// 使用字符串的split(regex)
return numStr.split(SEPARATOR);
}
}