目录
1. String概述
- 字符串就是多个
字符
连起来组成的一串数据. - 字符串的本质是一维
字符数组
. - 字符串
字面值
( 例如"abc" ) 可以看作一个字符串对象
. - 字符串是
常量
, 一旦赋值, 值不能改变, 但是引用可以改变.
(StringBuffer和String差不多, 但是StringBuffer的值是可变的, 解决了String浪费内存的问题. )
#2. String的常用构造方法
- 此处只讨论常用构造, 实际上String的构造不只下面例举的.
public String():
无参构造.public String(byte[] bytes):
字节数组转字符串.public String(byte[] bytes,int offset,int length):
字节数组的一部分转字符串. 将字节数组bytes从索引offset开始的length个字节转换成字符串.public String(char[] value):
字符数组转字符串.public String(char[] value,int offset,int count):
字符数组的一部分转字符串. 将字符数组value从索引offset开始的count个字符转换成字符串.public String(String original):
字符串常量转字符串.public String(StringBuffer buffer):
StringBuffer 转 String.public String(StringBuilder builder):
StringBuilder 转 String .
/**
* public String ():空构造。
*/
@Test
public void test01() {
String s = new String();
System.out.println(s); // 输出 空
System.out.println(s.length()); // 0
}
/**
* String(byte[] bytes):把字节数组转换成字符串。
*/
@Test
public void test02() {
// 字节的取值是 -128~127
byte[] bytes = {97, 98, 99, 100, 101};
String s = new String(bytes);
System.out.println(s.toString()); // abcde
System.out.println(s.length()); // 5
/*
* 我们写的是97, 98, 99, 100, 101, 输出的却是abcde, 这是因为97代表ASCII码中的a.
* 将byte数组转换为字符串, 需要先把byte转换成字符.
*
* 另外,
* 48 0
* 65 A
* 97 a
*/
}
/**
* String(byte[] bytes, int offset, int length):把字节数组的一部分转换成字符串。
*/
@Test
public void test03() {
byte[] bytes = {97, 98, 99, 100, 101};
String s = new String(bytes, 2, 2);
System.out.println(s); // cd
System.out.println(s.length()); // 2
}
/**
* String(char[] value):把字符数组转换成字符串。
*/
@Test
public void test04() {
char[] chars = {'a', 'b', 'c', 'd', '你', '好'};
String s = new String(chars);
System.out.println(s); // abcd你好
System.out.println(s.length()); // 6
}
/**
* String(char[] value, int offset, int count):把字符数组的一部分转换成字符串。
*/
@Test
public void test05() {
char[] chars = {'a', 'b', 'c', 'd', '你', '好'};
String s = new String(chars, 2, 3);
System.out.println(s); // cd你
System.out.println(s.length()); // 3
}
/**
* String(String original):把字符串常量转换成字符串。
*/
@Test
public void test06() {
// 在java中,字符串字面值"abc"也可以看成是一个字符串对象.
String s = "abc";
System.out.println(s); // abc
System.out.println(s.length()); // 3
String s1 = new String("abc");
System.out.println(s1); // abc
System.out.println(s1.length()); // 3
// 实际上, String s1 = new String("abc");这个构造的意义不大.
}
3. 字符串一旦赋值就不能被改变
@Test
public void test01(){
String s1 = "abc";
s1 = "def";
/*
* 上述两行实际上是没错的.
* "字符串一旦赋值不能被改变"指的是
* 字符串对象不能变, 但是这个对象的引用变量能变.
*
* 上述代码中, 字符串对象是"abc"和"def", 对象的引用是s1.
*
* s1相当于是绑气球的线, 先绑到"abc"这个气球上,
* 又绑到了"def"这个气球上. 气球本身并没有改变.
*/
}
注意, 只有使用直接赋值的方式创建字符串对象
( 例如: String s = “abc”; )才能对应上面的内存描述.
3. 字符串字面值对象和构造方法创建的对象的区别
-
问题:
String s=new String ("abc");
和String s="abc";
的区别。
-
String s1 = new String(“hello”); 时, 先在常量池创建"hello"对象, 因为"hello"是字符串字面量, 字面量形式声明的字符串都是字符串对象,这个对象存储在常量池中. 接着, 在堆内存中new String(); 然后堆内存中的该对象指向常量池中的"hello"对象, 相当于使用常量池中的"hello"给堆内存中的new String();的成员变量赋值.
-
String s2 = “hello”;时, 由于是字面量形式创建的String对象, 那么先去常量池中找是否有"hello"对象, 如果有, 就将常量池中"hello"对象的地址值赋值给引用变量s2. 如果常量池中没有"hello"对象, 那么就在常量池中创建一个"hello"对象之后再将该对象的地址值赋值给引用变量s2.
-
综上
/** * String s1 = new String("hello"); 这句实际上是先在常量池中创建"hello"对象, * 然后在堆内存中创建new String();对象, * 然后将常量池中的"hello"赋值给new String();的成员变量. 因此这句实际上创建了两个对象. */ @Test public void test03(){ String s1 = new String("hello"); String s2 = "hello"; }
/** * String s1 = new String("hello"); * 这句实际上是在堆内存创建了一个new String()对象,然后该对象指向了常量池中的 * "hello"对象. 在new String("hello");之前, 常量池中就已经存在"hello"对象了. */ @Test public void test03() { String s2 = "hello"; String s1 = new String("hello"); }
-
String s1 = "abc";
和直接写"abc"
的区别
-
小结
- 字面值常量是存放到方法区的常量池中的. 字符串的字面值常量是字符串对象.
4. 常量优化机制
- String s1 = “hello”; 中, "hello"是字符串字面量, String s1 = “hello”; 整体是变量.
// 不只字符串常量有常量优化机制.所有数据类型的常量都有常量优化机制
@Test
public void test04(){
byte b = 10 + 20;
/*
* 上述的代码正确.
* 因为在编译时走了常量优化机制,也就是最终优化的结果为byte result = 30; 符合数据类型范围
*/
byte b1 = 10;
byte b2 = 20;
byte result1 = b1 + b2; // 这句编译报错
/*
* 上述的 byte result1 = b1 + b2; 编译错误.
* 俩个变量在编译时不确定里面的值,因为变量有可能发生改变,所以无法再编译阶段时对变量进行优化
*/
}
@Test
public void test10() {
String s1 = "ab";
System.out.println(s1 + "c" == "abc"); // false
// 其中s1不是常量而是对象, 因此不会走常量优化机制. 返回false.
/*
* 字符串中有变量相加, 先开辟一块内存, 再拼接字符串,
* 都开辟新的内存了, 肯定返回false
*/
}
@Test
public void test11() {
String s1 = "ab";
String s2 = "c";
System.out.println(s1 + s2 == "abc"); // false
// 由于s1和s2都不是常量, 都是对象, 因此不走常量优化机制. 返回false.
/*
* 字符串中有变量相加, 先开辟一块内存, 再拼接字符串,
* 都开辟新的内存了, 肯定返回false
*/
}
- 两个字面值常量相加, 例如
"ab"+"c"="abc"
, 实际上是将"ab"+"c"
的结果"abc"
存储到常量池中, 这就是常量优化机制. 因此System.out.println("ab" + "c" == "abc");
返回true.
上述说的将结果存储到常量池中的过程, 也是先到常量池中找有没有这个结果, 如果有就直接引用这个结果, 如果没有, 就在常量池中创建这个结果.
5. String中的常用方法
5.1 String的判断功能
boolean equals (Object obj);
比较字符串内容是否相同,区分大小写.常用
boolean equalsIgnoreCase(String str);
比较字符串内容是否相同,忽略大小写.常用
boolean contains(String str);
判断大字符串中是否包含小字符串.常用
boolean startsWith(String str);
判断字符串是否以某个指定的字符串开头.boolean endsWith(String str);
判断字符串是否以某个指定的字符串结尾.boolean isEmpty();
判断字符串数据是否为空.常用
/**
* boolean contains(String str);
* 判断大字符串中是否包含小字符串
*/
@Test
public void test01() {
String s1 = "helloworld";
System.out.println(s1.contains("hello"));//t
System.out.println(s1.contains("hw"));//f
}
/**
* boolean startsWith(String str);
* 判断字符串是否以某个指定的字符串开头
*/
@Test
public void test02(){
String s1="helloworld";
System.out.println(s1.startsWith("hel"));//t
System.out.println(s1.startsWith("rld"));//f
}
/**
* boolean isEmpty();
* 判断字符串数据是否为空。
*
* 字符串内容为空:对象存在,没有数据。String s="";
* 字符串对象为空:对象都不存在。String s=null;
*/
@Test
public void test03(){
String s1="";
String s2="hello";
String s3=null;
System.out.println(s1.isEmpty());//t
System.out.println(s2.isEmpty());//f
System.out.println(s3.isEmpty());//空指针异常
}
5.2 String的获取功能
int length();
获取字符串的长度.char charAt(int index);
获取指定索引位置的字符.int indexOf(int ch);
返回指定字符
在此字符串中第一次出现的索引.
方法的参数, 写 ‘a’ 或 97 都可以. Ascii码中, 97就代表’a’.int indexOf(String str);
返回指定字符串
在此字符串中第一次出现的索引.int indexOf(int ch,int fromIndex);
返回指定字符在此字符串中从指定位置后第一次出现的索引.int indexOf(String str,int fromIndex);
返回指定字符串在此字符串中从指定位置后第一次出现的索引.String substring(int start);
从指定位置截取字符串,默认到末尾.String substring(int start,int end);
从指定位置截取字符串,从指定位置结束截取.包左不包右
/**
* int length(); 获取字符串的长度.
*/
@Test
public void test01() {
String s1 = new String();
System.out.println(s1.length()); // 0
s1 = "qwerty";
System.out.println(s1.length()); // 6
String s2 = null;
System.out.println(s2.length()); // java.lang.NullPointerException
String s3 = "abc";
System.out.println(s3.length()); // 3
System.out.println("hello world".length()); // 11
/*
* 注意,
* String s1 = new String();
* s1的长度是0.
*
* String s2 = null;
* 获取s2的长度将报错空指针异常.
*/
}
/**
* @author: wjl@king.cn
* @createTime: 2019/12/3 14:44
* @param: []
* @return: void
* @description: int indexOf(int ch); 返回指定字符在此字符串中第一次出现的索引.
*/
@Test
public void test01() {
String s1 = "qwerty";
System.out.println(s1.indexOf('e')); // 2
System.out.println(s1.indexOf(101)); // 2
}
/**
* @author: wjl@king.cn
* @createTime: 2019/12/3 14:44
* @param: []
* @return: void
* @description: int indexOf(int ch); 返回指定字符在此字符串中第一次出现的索引.
* 为什么参数需要的是int类型而不是char类型呢? 这是由于在ASCII码中'a'和97都能表示'a'.
* 实际上, 参数中既能传入字符类型, 也能传入int类型.
*/
@Test
public void test01() {
String s1 = "qwerty";
System.out.println(s1.indexOf('e')); // 2
System.out.println(s1.indexOf(101)); // 2
System.out.println("a"); // -1
System.out.println(s1.indexOf(10000000)); // -1
}
/**
* @author: wjl@king.cn
* @createTime: 2019/12/3 15:00
* @param: []
* @return: void
* @description: int indexOf(String str)
* 返回指定字符串在此字符串中第一次出现的索引
*/
@Test
public void test01() {
String s="helloworld";
System.out.println(s.indexOf("or")); // 6
System.out.println(s.indexOf("abc")); // -1
}
/**
* @author: wjl@king.cn
* @createTime: 2019/12/3 15:06
* @param: []
* @return: void
* @description: int indexOf(int ch,int fromIndex);
* 返回指定字符在此字符串中从指定位置后第一次出现的索引
*/
@Test
public void test01() {
String s = "helloworld";
// 在索引为4的字符后, 查找字符'l'第一次出现的索引
System.out.println(s.indexOf('l', 4)); // 8
// 在索引为4的字符后, 查找字符'k'第一次出现的索引
System.out.println(s.indexOf('k', 4)); // -1
// 在索引为40的字符后, 查找字符'h'第一次出现的索引
System.out.println(s.indexOf('h', 40)); // -1
}
/**
* @author: wjl@king.cn
* @createTime: 2019/12/3 15:33
* @param: []
* @return: void
* @description: String substring(int start,int end);
* 从指定位置截取字符串,从指定位置结束截取; (包左不包右)
*/
@Test
public void test01(){
String s="helloworld";
System.out.println(s.substring(5)); // world
System.out.println(s.substring(5,8)); // wor
}
- 练习
/*================================字符串的遍历================================*/
/**
* 字符串的遍历方式1:
* int length(); 方法 + char charAt(int index); 方法
*/
@Test
public void test01() {
String s = "helloworld";
for (int i = 0; i < s.length(); i++) {
System.out.print(s.charAt(i) + "\t");
}
// h e l l o w o r l d
}
/**
* 字符串的遍历方式2:
* byte[]getByte();方法
*/
@Test
public void test02() {
String s = "helloworld";
byte[] bytes = s.getBytes();
for (int i = 0; i < bytes.length; i++) {
System.out.print(bytes[i] + "\t");
}
// 输出: 104 101 108 108 111 119 111 114 108 100
for (int i = 0; i < bytes.length; i++) {
System.out.print((char) bytes[i] + "\t");
}
// 输出: h e l l o w o r l d
}
/*=======================统计字符串中的大写,小写,数字各有几个=======================*/
@Test
public void test01() {
String s = "HelloWorld123";
int bigCount = 0,
smallCount = 0,
numberCount = 0;
for (int a = 0; a < s.length(); a++) {
char x = s.charAt(a);
if (x >= '0' && x <= '9') numberCount++;
if (x >= 'a' && x <= 'z') smallCount++;
if (x >= 'A' && x <= 'Z') bigCount++;
}
System.out.println(
"大写: "+bigCount+"个; \n " +
"小写: "+smallCount+"个; \n " +
"数字: "+numberCount+"个; "
);
}
5.3 String的转换功能
byte[] getBytes();
把字符串转换为字节数组.char[] toCharArray();
把字符串转换为字符数组.static String valueOf(char[] chs);
字符数组转字符串.static String valueOf(数据类型 变量名);
任意数据类型转字符串.String toLowerCase();
字符串转小写.(产生了新的全小写字符串,原字符串未变)String toUpperCase();
字符串转大写.(产生了新的全大写字符串,原字符串未变)String concat(String str);
拼接字符串.
/**
* @author: wjl@king.cn
* @createTime: 2019/12/3 16:57
* @param: []
* @return: void
* @description:
* toUpperCase(); 字符串转大写;
* toLowerCase(); 字符串转小写;
*/
@Test
public void test01() {
String s = "QQqqQQqqQq";
String newStr1 = s.toUpperCase();
String newStr2 = s.toLowerCase();
System.out.println(newStr1); // QQQQQQQQQQ
System.out.println(newStr2); // qqqqqqqqqq
System.out.println(s); // QQqqQQqqQq
}
/**
* @author: wjl@king.cn
* @createTime: 2019/12/3 17:02
* @param: []
* @return: void
* @description: String concat(String str): 拼接字符串
*/
@Test
public void test01() {
System.out.println("-------拼接字符串的第一种方式-------");
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("hello");
stringBuffer.append("world");
System.out.println(stringBuffer.toString());
System.out.println("------------------------");
System.out.println("-------拼接字符串的第二种方式-------");
String s1 = "hello";
String s2 = "world";
System.out.println(s1 + s2);
System.out.println("------------------------");
System.out.println("-------拼接字符串的第三种方式-------");
String s3 = s1.concat(s2);
System.out.println(s3);
System.out.println("------------------------");
}
// 推荐只使用第一种方式.
- 练习
/**
* @author: wjl@king.cn
* @createTime: 2019/12/3 17:08
* @param: []
* @return: void
* @description: 把字符串的首字母转换成大写, 其他的字符全部转换成小写.
* 步骤:
* 1、获取第一个字符
* 2、获取除了第一个字符以外的字符
* 3、把1转大写
* 4、把2转小写
* 5、3拼4
*/
@Test
public void test() {
String target = "helloJavaWorldPHP";
String first = target.substring(0, 1);
String other = target.substring(1);
String newFirst = first.toUpperCase();
String newOther = other.toLowerCase();
String newStr = newFirst + newOther;
System.out.println(newStr);
}
5.4 String的其他功能
5.4.1 替换功能
String replace(char old,char new);
String replace(String old,String new);
String replaceAll(String regex,String replacement);
5.4.2 去除字符串首尾空格
String trim();
5.4.3 按字典顺序比较两个字符串
int compareTo(String str);
int compareToIgnoreCase(String str);
public static void main(String[] args) {
// h: 104
// e: 101
// l: 108
// o: 111
// q: 113
// w: 119
// r: 114
// c: 99
String s1 = "hello";
String s2 = "hello";
String s3 = "qwer";
String s4 = "hcllo";
/*A*/
System.out.println(s1.compareTo(s2));//0
/*B*/
System.out.println(s1.compareTo(s3));//-9
/*C*/
System.out.println(s1.compareTo(s4));//2
}
//A:相同字符的ASCII码值相减。
//B:h和q的ASCII码值相减。
//C:e和c的ASCII码值相减。
/*
从第一个字符开始比较,他们的ASCII码值相减,如果都一样就输出0,
发现第一个不一样的字符输出他们的ASCII码值的差。
*/
public static void main(String[] args) {
String s1 = "hello";
String s2 = "hel";
System.out.println(s1.compareTo(s2)); // 2
//2
}
// 这种类型的比较返回的是长度相减
5.5 String和字符集编码相关的方法
String(byte[] bytes, String charsetName);
使用指定的字符集,将字节数组变成字符串. 这是String的一个构造方法.getBytes(String charsetName);
使用指定的字符集,将字符串变成字节数组. 该方法有多个重载的方法.
5.6 String和正则表达式相关的方法
- String中有3个和正则表达式相关的方法
boolean matches(String regex);
判断当前字符串是否和参数正则表达式匹配.String[] split(String regex);
使用指定的正则表达式切割当前字符串.String replaceAll(String regex, String newStr);
将调用者字符串中的所有匹配regex正则的子串,全部替换成newStr新串.
- public boolean matches(String regex) 方法的使用
package cn.king.demo01;
import java.util.Scanner;
/*
* public boolean matches(String regex):
* 判断当前字符串是否和参数正则表达式匹配。
*/
/**
* 使用正则表达式校验qq号码:
* 1、要求必须是5-15位数字
* 2、0不能开头
*
* 分析:
* a:键盘录入一个qq
* b:写一个功能校验qq
* c:调用功能输出结果
*/
public class Demo01 {
public static void main(String[] args) {
System.out.println("输入QQ: ");
String qq = new Scanner(System.in).nextLine();
System.out.println(checkQQ(qq));
}
public static boolean checkQQ(String qq) {
return qq.matches("[1-9][0-9]{4,14}");
}
}
/*
* 同理, 校验手机号只需要更换正则表达式即可,
* String regexForPhone="1[38]\\d{9}";
* 同理, 校验邮箱只需要更换正则表达式即可,
* String regexForMail = "\\w+@\\w{2,6}(\\.\\w{2,3})+";
*/
- public String[] split(String regex) 方法的使用
package cn.king.demo01;
/**
* public String[] split(String regex):
* 使用指定的正则表达式切割当前字符串。
*/
public class Demo02 {
public static void main(String[] args) {
String s1 = "aa,bb,cc";
// 将字符串按照逗号分隔, 分隔出的每一部分都放到字符串数组中
String[] str1Array = s1.split(",");
for (int x = 0; x < str1Array.length; x++) {
System.out.println(str1Array[x]);
}
System.out.println("---------------------");
// 将字符串按照点分隔, 分隔出的每一部分都放到字符串数组中
String s2 = "aa.bb.cc";
String[] str2Array = s2.split("\\.");
for (String item : str2Array) {
System.out.println(item);
}
System.out.println("---------------------");
String s3 = "aa bb cc";
// 将字符串按照点分隔, 分隔出的每一部分都放到字符串数组中
String[] str3Array = s3.split(" +");
for (String value : str3Array) {
System.out.println(value);
}
System.out.println("---------------------");
// 硬盘上的路径,我们应该用\\替代\
String s4 = "E:\\JavaSE\\day14\\avi";
// 将字符串按照\\分隔, 分隔出的每一部分都放到字符串数组中
String[] str4Array = s4.split("\\\\");
for (String s : str4Array) {
System.out.println(s);
}
System.out.println("---------------------");
}
}
// 注意下面这种方式, 我们给split();方法的参数传递空字符串, 那么返回的是字符串数组.
public static void main(String[] args) {
String str = "hello world";
String[] strArr = str.split("");
// [h, e, l, l, o, , w, o, r, l, d]
System.out.println(Arrays.toString(strArr));
}
package cn.king.demo01;
import java.util.Arrays;
/*
* 我有如下一个字符串:"91 27 46 38 50"
* 请写代码实现最终输出结果是:"27 38 46 50 91"
*
* 分析:
* A:定义一个字符串
* B:把字符串进行分割,得到一个字符串数组
* C:把字符串数组变换成int数组
* D:对int数组排序
* E:把排序后的int数组在组装成一个字符串
* F:输出字符串
*/
public class Demo03 {
public static void main(String[] args) {
// 定义一个字符串
String s = "91 27 46 38 50";
// 把字符串进行分割,得到一个字符串数组
String[] strArray = s.split(" ");
// 把字符串数组变换成int数组
int[] arr = new int[strArray.length];
for (int x = 0; x < arr.length; x++) {
arr[x] = Integer.parseInt(strArray[x]);
}
// 对int数组排序
Arrays.sort(arr);
// 把排序后的int数组在组装成一个字符串
StringBuilder sb = new StringBuilder();
for (int x = 0; x < arr.length; x++) {
sb.append(arr[x]).append(" ");
}
//转化为字符串
String result = sb.toString().trim();
//输出字符串
System.out.println("result:" + result);
}
}
- String replaceAll(String regex, String newStr) 方法的使用
package cn.king.demo01;
/**
* String replaceAll(String regex, String newStr) :
* 将调用者字符串中的所有匹配regex正则的子串,全部替换成newStr新串
*/
public class Demo04 {
public static void main(String[] args) {
String s = "helloqq12345worldkh622112345678java";
// 使用"*"替换所有的数字.
// 输出: helloqq*worldkh*java
System.out.println(s.replaceAll("\\d+", "*"));
// 输出: helloqq*****worldkh************java
System.out.println(s.replaceAll("\\d","*"));
// 直接把数字删除. 使用空字符串替换数字即可.
String result = s.replaceAll("\\d+", "");
System.out.println(result);
}
}
/**
* 模拟简单的敏感词过滤.
* 如果parameter中包含list中的敏感词, 那么将parameter的这些敏感词替换为"*"
*/
public static void main(String[] args) {
String parameter = "史蒂夫笨蛋史蒂夫笨蛋史蒂夫笨蛋史蒂夫笨蛋史蒂夫笨蛋";
List<String> list = new ArrayList<>();
list.add("笨蛋");
list.add("狗屎");
for (String s: list){
if (parameter.contains(s)) {
StringBuilder newStr = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
newStr.append("*");
}
parameter = parameter.replaceAll(s, newStr.toString());
}
}
// 史蒂夫**史蒂夫**史蒂夫**史蒂夫**史蒂夫**
System.out.println(parameter);
}
6. String相关练习
- 把数组中的数据按照指定格式拼接成一个字符串
/**
* 数组:String[] strArr = {"java", "php", "python"};
* 输出:[java,php,python]
*/
@Test
public void test() {
String[] strArr = {"java", "php", "python"};
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("[");
for (int i = 0; i < strArr.length; i++) {
if (i == strArr.length - 1) {
stringBuffer.append(strArr[i]);
} else {
stringBuffer.append(strArr[i]);
stringBuffer.append(",");
}
}
stringBuffer.append("]");
String s = stringBuffer.toString();
System.out.println(s); // [java,php,python]
}
- 字符串翻转
/**
* 字符串反转.
* 只需要倒序遍历即可.
*/
@Test
public void test01() {
String s = "abc";
char[] charArray = s.toCharArray();
StringBuffer stringBuffer = new StringBuffer();
for (int i = charArray.length - 1; i >= 0; i--) {
stringBuffer.append(charArray[i]);
}
String str = stringBuffer.toString();
System.out.println(str); // cba
}
- 统计大串中小串出现的次数
package cn.king.demo01;
import org.junit.Test;
//统计maxString 中minString出现的次数
/*
* 前提:知道了大串和小串
* A:定义一个统计变量,初始化为0
* B:先在大串中查找一次小串第一次出现的位置
* a:索引是-1,说明不存在,返回统计变量
* b:索引不是-1,说明存在,统计变量++
* C:把刚才的索引+小串的长度作为开始截取上一次的大串,返回一个新的字符串,并把该字符串重新赋值给大串
* B:回到B
*/
public class Demo08 {
// 原始版
@Test
public void test01() {
String maxString = "woaijavawoaijavawoaijava";
String minString = "java";
int count = 0;
int index = maxString.indexOf(minString);
while (index != -1) {
count++;
int startIndex = index + minString.length();
maxString = maxString.substring(startIndex);
index = maxString.indexOf(minString);
}
System.out.println(count);
}
// 优化版
@Test
public void test02() {
String maxString = "woaijavawoaijavawoaijava";
String minString = "java";
int count = 0;
int index;
while ((index = maxString.indexOf(minString)) != -1) {
count++;
maxString = maxString.substring(index + minString.length());
index = maxString.indexOf(minString);
}
System.out.println(count);
}
}
- 把字符串中的数字排序
package cn.king.demo01;
import org.junit.Test;
import java.util.Arrays;
/*
* 我有如下一个字符串:"91 27 46 38 50"
* 请写代码实现最终输出结果是:"27 38 46 50 91"
*
* 分析:
* A:定义一个字符串
* B:把字符串进行分割,得到一个字符串数组
* C:把字符串数组变换成int数组
* D:对int数组排序
* E:把排序后的int数组在组装成一个字符串
* F:输出字符串
*/
public class Demo09 {
@Test
public void test01() {
// 定义一个字符串
String str = "91 27 46 38 50";
// 把字符串按照空格进行分割, 得到一个字符串数组
String[] strArr = str.split(" ");
// 把字符串数组转换成int数组
int[] intArr = new int[strArr.length];
for (int i = 0; i < intArr.length; i++) {
intArr[i] = Integer.parseInt(strArr[i]);
}
// 对int数组进行排序
Arrays.sort(intArr);
// 把排序后的int数组再拼装成一个字符串
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < intArr.length; i++) {
stringBuffer.append(intArr[i]).append(" ");
}
// 转换成字符串
System.out.println(stringBuffer.toString());
}
}
7. 基本数据类型转字符串的3三种方式
@Test
public void test01(){
int i = 10086;
// 基本数据类型转字符串的第一种方式 => 使用包装类的toString()方法
Integer integer = Integer.valueOf(i);
String s1 = integer.toString();
// 基本数据类型转字符串的第二种方式 => 使用String类的valueOf()方法
String s2 = String.valueOf(i);
// 基本数据类型转字符串第第三种方式 => 使用空字符串拼接基本数据类型
String s3 = i + "";
}
8. String相关面试题
8.1 String类能不能被继承,为什么?这种设计有什么好处?
-
String是final类型,final类不能被继承. Java之所以被设计成final类是有⼀定的考虑在的,主要在以下
⼏个⽅⾯。 -
缓存Hashcode
Java中经常会⽤到字符串的哈希码(hashcode)。例如,在HashMap中,字符串的不可变能保证其hashcode永远保持⼀致,这样就可以避免⼀些不必要的⿇烦。这也就意味着每次在使⽤⼀个字符串的hashcode的时候不⽤重新计算⼀次,这样更加⾼效。 -
使其他类的使⽤更加便利
在介绍这个内容之前,先看以下代码:HashSet<String> set = new HashSet<String>(); set.add(new String("a")); set.add(new String("b")); set.add(new String("c")); for(String a: set) a.value = "a";
在上面的例子中,如果字符串可以被改变,那么以上用法将有可能违反Set的设计原则,因为Set要求其中的元素不可以重复。上面的代码只是为了简单说明该问题,是伪代码,其实上面的代码也无法编译通过,String的value字段并无法从外部访问。
-
安全性
String被⼴泛的使⽤在其他Java类中充当参数。⽐如⽹络连接、打开⽂件等操作。如果字符串可变,那么类似操作可能导致安全问题。因为某个⽅法在调⽤连接操作的时候,他认为会连接到某台机器,但是实际上并没有(其他引⽤同⼀String对象的值修改会导致该连接中的字符串内容被修改)。可变的字符串也可能导致反射的安全问题,因为他的参数也是字符串。 -
不可变对象天⽣就是线程安全的
因为不可变对象不能被改变,所以他们可以⾃由地在多个线程
之间共享。不需要任何同步处理。 -
总之,String被设计成不可变的主要⽬的是为了安全和⾼效。所以,使String是⼀个不可变类是⼀个很好的设计。
8.2 String有没有长度限制,为什么?如果有,超过限制会发生什么?
- String类有一个构造方法:
public String(int[] codePoints,int offset,int count);
可见理论上String的最大长度就是int的最大值:Integer.MAX_VALUE;
但是实际上, String类的长度达不到这么长, 受堆内存和常量池的内存大小限制.
8.3 String的"+"是如何实现的?
-
- String s = “a” + “b”,编译器会进行常量折叠(因为两个都是编译期常量,编译期可知),即变成 String s = “ab”; 这就是常量优化机制. java会拿着"a"+“b"的结果"ab"到常量池中找, 如果有, 就引用这个"ab”, 如果没有, 就在常量池中创建一个"ab"对象再把该对象的地址值赋给String s.
- 对于能够进行优化的(String s = “a” + 变量 等)用 StringBuilder 的 append() 方法替代,最后调用 toString() 方法 (底层就是一个 new String())
/**
问:String的“+”是如何实现的?
1. String s = "a" + "b",
编译器会进行常量折叠(因为两个都是编译期常量,编译期可知),即变成 String s = "ab"
2. 对于能够进行优化的(String s = "a" + 变量 等)
用 StringBuilder 的 append() 方法替代,
最后调用 toString() 方法 (底层就是一个 new String())
*/
public class Demo02 {
public static void main(String[] args) {
/*
* 对应反编译的代码:
* String s1 = "ab";
* String s2 = "b1";
*/
String s1 = "a" + "b";
String s2 = "b" + 1;
/*
* String s3 = (new StringBuilder()).append("a").append(s1).toString();
*/
String s3 = "a" + s1;
/*
* String s4 = (new StringBuilder()).append("a").append(new Integer(1)).toString();
*/
String s4 = "a" + new Integer(1);
/*
* String s5 = (new StringBuilder()).append(s2).append(s3).toString();
*/
String s5 = s2 + s3;
/*
* String s6 = new String();
* for (int i = 0; i < 2; i++) {
* s6 = (new StringBuilder()).append(s6).append(i).toString();
*
* }
*/
String s6 = new String();
for (int i = 0; i < 2; i++) {
s6 += i;
}
}
}
9. StringBuffer类概述
详见另一篇博客: StringBuffer类