1 String类的初始化、与StringBuffer类和StringBuilder类三者的区别
1.1 String类的初始化两种方式
-
字符串在底层的存储方式是char[]数组。
-
String类是final的,没有子类。
-
“new String”和直接赋值这两种方式的区别
看到了segmentfault的大神@Seven_Nee的总结:
拓展:
字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价,作为最基础的数据类型,大量频繁的创建字符串,极大程度地影响程序的性能。
JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化,为字符串在方法区开辟一个字符串常量池,类似于缓存区。
创建字符串常量时,首先查看字符串常量池是否存在该字符串,如果存在该字符串,返回引用实例,不存在,实例化该字符串并放入池中。
常量池中存储着String类型的字面值。
//常量池 : 先查找常量池中是否有“OK”,没有就先创建对应的实例对象
//直接比较常量池中的值
String x = "OK";
String y = "OK";
System.out.println(x == y);//true
//new的方式比较的是堆内存中的地址
String a = new String("OK");
String b = new String("OK");
System.out.println(a == b);//地址:false
//注意:
String i = "O";
String j = "K";
String k = "OK";
String m = i + k;
//false,一旦i+k进行字符串连接,底层涉及到new
//结果得到地址,与常量池的值不相等
System.out.println(m == k);
- 无论直接赋给String字面值还是new一个新的String对象,值都会去常量池中搜索,如果常量池中搜索到这个值则返回对应的引用实例,如果没有则创建对应的实例对象。只是因为用到“new”,后者会在堆中创建String对象。关系如下图:
-
所以,它们两个的区别是:同样创建一个值为"OK"的变量,直接赋值可能创建一个或不创建对象,如果"OK"字符串在常量池中不存在,会在常量池创建一个实例对象;如果已经存在,直接引用这个常量池中的对象。“new String”至少创建一个对象。起码要在堆new一个String对象存放了“OK”,同时,若"OK"这个字符串在常量池里不存在,在常量池创建实例对象。
-
上面的“==”的意思也不同,直接赋值的意义是比较值是否相同;“new String”的含义是比较地址。
-
注意代码中的第三个“+”操作符的例子。
1.2 String类、StringBuffer类和StringBuilder类三者的区别
String、StringBuffer和StringBuilder都表示Java中的字符串。
- 区别(面试题这么答)
-
String和StringBuffer/StringBuilder之间的区别(StringBuffer的append方法是为了提高字符串拼串的效率,就不要再在里面写"+"拼串了):
- String str = new String(“OK”);
//底层char数组开辟2块空间,如果要追加连接,底层需要再次新建char[]类型数组,并依次赋连接的值。 虽然效率低,省内存。(str+=“需要连接的值”)
//底层char数组开辟2块空间,并附16块缓冲区(字符串的缓存)。 缓冲区的作用是便于追加连接。如果需要频繁变更字符串,这个效率更高。(str.append(“需要连接的值”))
当StringBuffer的缓冲区装完之后,需要扩容,扩容大小为(数组大小 + 字符串大小)*2,容量如果还不够,可直接扩充到需要的容量大小。
StringBuffer buffer = new StringBuffer(“OK”);//OK+16块缓冲区 总长18
buffer.append(“OKOK”);//(原数组长度 + 连接的字符串长度)*2,即(18 + 4) * 2 - String str = new String(“OK”);
-
StringBuffer和StringBuilder之间的区别:
StringBuffer同一时间允许一个线程进行访问 ,效率较低,但是 不会 出现并发错误。
StringBuilder同一时间允许多个线程进行访问,效率较高,但是可能 会 出现并发错误。
-
2 String类常用的20个方法
2.1 与长度相关的方法
返回类型 | 方法签名 | 作用 |
---|---|---|
int | length() | 得到一个字符串的字符个数。 |
- 无论是中文还是英文,只要一个退格键能删除的都是一个字符。转义字符占一个字符,不论进行了什么操作有什么输出,最后总是输出字符串定义时文本字符的长度与转义字符的一个字符的长度的和。
例如:
String str = "ABCD\bEFGH";
System.out.println(str);//-->ABCEFGH
//虽然输出时D被转义字符退格,但是长度还算
System.out.println(str.length());//-->9
2.2 与数组相关的方法
返回类型 | 方法签名 | 作用 |
---|---|---|
byte[] | getBytes() | 将一个字符串全部转换成字节数组 |
char[] | toCharArray() | 将一个字符串全部转换成字符数组 |
String[] | split(String) | 将一个字符串按照指定的内容劈开 |
- getBytes()方法,若字符串中含有转义字符,算一个字符(元素)存于数组,存储的字节数组值为ascii码。toCharArray()中相同,只是元素按char的字面值存储。
- 常用于打碎字符串并取出其中符合要求的字符。
- 如果想利用split()匹配关键词来看关键词在字符串中出现了几次:如果字符串在开头,这时数组第0个会截取到一个空串,对结果不会产生影响;但是如果这个关键词出现在结尾,此时结尾不会多出一个空串,判断时会少算一个。所以使用split来查找关键词出现几次时, 一定用endwith判断结尾是否为要查找的关键词,或者先在结尾追加一个字符,使其不以匹配的关键字结尾 。另外注意当以点号".“分割时,要写”\\."。
2.3 与判断相关的方法
返回类型 | 方法签名 | 作用 |
---|---|---|
boolean | equals(String) | 区分大小写地比较两个字符串的内容是否完全一致 |
boolean | equalsIgnoreCase(String) | 忽略大小写地比较两个字符串的内容是否完全一致 |
boolean | contains(String) | 判断一个字符串里面是够出现指定内容 |
boolean | startsWith(String) | 判断一个字符串是否以指定的内容开头 |
boolean | endsWith(String) | 判断一个字符串是否以指定的内容结尾 |
- equals()对于new String获得的字符串,比较两个字符串的内容是否相同,当比较地址时使用“==”。使用equals()时要用一定存在值的String变量调用这个方法,避免空指针。
2.4 与改变内容相关的方法
返回类型 | 方法签名 | 作用 |
---|---|---|
String | toUpperCase() | 将一个字符串全部转换成大写 |
String | toLowerCase() | 将一个字符串全部转换成小写 |
String | replace(String,String) | 将一个字符串里面出现的某个内容全部替换成指定的内容 |
String | replaceAll(String,String) | 将一个字符串里面出现的某个内容全部替换成指定的内容(支持使用正则表达式) |
String | replaceFirst(String,String) | 将一个字符串里面第一次出现的某个内容替换成指定的内容 |
String | trim() | 将一个字符串的前后空格删除 |
String | substring(int x,int y) | 从下标x截取到下标y-1对应的元素 |
String | substring(int x) | 从下标x截取到字符串的最后 |
- 和改变内容有关的方法,都不会直接的操作原本的字符串,只是返回符合条件的字符串,所以注意使用变量接收。
- 需要注意的replace的三种方法,这里replaceFirst与replaceAll都是基于规则表达式的替换,因“\”在java中是一个转义字符,所以需要用两个代表一个。但是“\”也是正则表达式中的转义字符,也需要用两个代表一个。所以:\\被java转换成\,\又被正则表达式转换成\。如果要用replaceFirst和replaceAll替换“\”为"\",就要用replaceFirst/All("\\","\\\\"),而replace则replace("\","\\")。 replaceFirst与replaceAll的区别显而易见,前者只替换第一次出现的符合第一个参数中的值,第二个替换所有的。这一点replaceAll与replace作用相同。另外replace还支持字符的替换。
2.5 与位置相关的方法
返回类型 | 方法签名 | 作用 |
---|---|---|
char | charAt(int x) | 找到指定下标对应的元素 |
int | indexOf(String) | 找到指定内容第一次出现的下标 |
int | lastIndexOf(String) | 找到指定内容最后一次出现的下标 |
- 这里的第几个,指的是数组中的序号。找第n个元素,传入参数n-1;第二、三个方法返回n,则字符位于字符串中的第n+1个。
2.6 综合应用例题
//1 定义一个方法 判断传进去的内容是不是纯数字
//如果是 return true
//如果不是 return false
public static boolean check(String str){
//方法1
if(str.replaceAll("[0-9]","").length() == 0){
return true;
}else{
return false;
}
//方法2
return str.replaceAll("[0-9]","").length() == 0;
//方法3
char[] data = str.toCharArray();
int count = 0;
for(char x : data){
if('0' <= x && x <= '9'){
count++;
}
}
if(count == str.length()){
return true;
}else{
return false;
}
}
//2 按照首字母从小到大排序
//Animal Bird Cat Dog
public class Test2{
public static void main(String[] args){
String[] data = {"Cat","Animal","Bird","Dog"};
for(int x = 0; x < data.length -1; x++){
for(int y = 0; y < data.length -1 -x; y++){
//获取前一个元素的首字母
char a = data[y].charAt(0);
//获取后一个元素的首字母
char b = data[y + 1].charAt(0);
if(a > b){
String z = data[y];
data[y] = data[y + 1];
data[y + 1] = z;
}
}
}
for(String x : data){
System.out.println(x);//Animal Bird Cat Dog(换行输出)
}
}
}
//3 根据输入的身份证号判断性别
public class Test3{
public static void main(String[] args){
String id = "341101199808062065";
char gender = str.charAt(16);
//if("02468".contains(gender)){
if(gender % 2 == 0){//0-9与对应的ascii码奇偶相同,也可以用ascii码模运算
System.out.println("female");
}else{
System.out.println("male");
}
}
}
//4 截取字符串中地点、最低和最高气温并输出
//济南今天最低气温:2
//济南今天最高气温:18
public class Test4{
public static void main(String[] args){
String str = "济南今天气温:2℃ - 18℃";
//使用:indexOf/lastIndexOf + substring截取字符串
//截取地方名:
String cityName = str.substring(0,str.indexOf("今"));
//截取最低温度
String min = str.substring(str.indexOf(":") + 1,str.indexOf("℃"));
//截取最高温度
String max = str.substring(str.lastIndexOf(" ") + 1,str.lastIndexOf("℃"));
System.out.println(cityName + "今天最低气温:" + min);
System.out.println(cityName + "今天最高气温:" + max);
}
}
//5 使用至少4种方式计算葡萄出现了几次
public class Test5{
public static void main(String args[]){
//方法1
String str = "吃葡萄不吐葡萄皮,不吃葡萄倒吐葡萄皮。";
String[] data = str.split("葡萄");
System.out.println("次数为:" + (data.length - 1));
//方法2
String str = "吃葡萄不吐葡萄皮,不吃葡萄倒吐葡萄皮。";
char[] strCharArray = str.toCharArray();
int count = 0;
//如果条件不减一,最后一个字是葡的情况下会越界。如果不出现葡,if中的&&会有短路效果,不判断x+1所以不会出现越界现象
for(int x = 0; x < strCharArray.length - 1; x++){
if(strCharArray[x] == '葡' && strCharArray[x + 1] == '萄') count++;
}
System.out.println("次数为:" + count);
//方法3
String str = "吃葡萄不吐葡萄皮,不吃葡萄倒吐葡萄皮。";
String strReplace = str.replaceAll("葡萄", "");
System.out.println("次数为:" + ((str.length() - strReplace.length()) / 2));
//方法4
String str = "吃葡萄不吐葡萄皮,不吃葡萄倒吐葡萄皮。";
int count = 0;
String twoWords;
while(str.contains("葡萄")){
int x = str.indexOf("葡萄");
str = str.substring(x + 2);
count++;
}
System.out.println("次数为:" + count);
//方法5
String str = "吃葡萄不吐葡萄皮,不吃葡萄倒吐葡萄皮。";
int count = 0;
while(str.contains("葡萄")){
str = str.replaceFirst("葡萄", "核桃");
count++;
}
System.out.println("次数为:" + count);
}
}
//6 只输出第一次出现的字母
public class Test6{
public static void main(String args[]){
String str = "aaaaaa";
char[] strArray = str.toCharArray();
//方法1
String strNew = "";
for(char x : strArray){
if(!strNew.contains(x + "")){
strNew += x;
}
}
System.out.println(strNew);//--->a
//方法2
while(str.length() != 0){
//char x = str.charAt(0);
String x = str.substring(0, 1);
System.out.print(x);
//删除这个值
str = str.replace(x, "");
}
System.out.println(str);//--->a
//方法3
String newStr = "";
for(int i = 0; i < strArray.length; i++){
newStr += strArray[i];
for(int j = i+1; j < strArray.length; j++){
if(strArray[i] == strArray[j]) {
str = str.replace(strArray[i] + "", "");
//去重之后刷新数组
strArray = str.toCharArray();
}
}
}
System.out.println(newStr);//--->输出a
}
}
//7 将第一个非重复元素打印出来
public class Test7{
public static void main(String[] args){
String str = "swwiss";
char[] strArray = str.toCharArray();
//初始化一个字符类型
char first = 0;
for(char x : strArray){
String strNew = str.replace(x + "", "");
if((str.length() - strNew.length())== 1){
first = x;
break;
}
}
System.out.println(first);//--->i
}
}
//8 统计两个字符串里面重复的元素有多少个
public class Test8{
public static void main(String[] args){
String str1 = "ok";
String str2 = "hello";
char[] str1Array = str1.toCharArray();
char[] str2Array = str2.toCharArray();
int count = 0;
for(int i = 0; i < str1Array.length; i++){
for(int j = i; j < str2Array.length; j++){
if(str1Array[i] == str2Array[j]){
count++;
}
}
}
System.out.println(count);//--->1
}
}
//9 对当前的字符串进行货币价值的格式化,例如输入1234567
//输出1,234,567
public class Test9{
public static void main(String[] args){
String str = "000123456776543210";
String newStr = "";
//如果左端有0,除掉
char[] strArray = str.toCharArray();
int j = 0;
while(strArray[j] == '0'){
str = str.replaceFirst(strArray[j] + "", "");
j++;
}
//将处理后的str先留存,在while中判断是否应该首先加“,”
String strBefore = str;
//对最左端先进行格式化,将剩下的数字变为三的倍数便于循环
if(str.length() % 3 != 0){
//将左端数值开始放入新的字符串
newStr += str.substring(0, str.length());
//原字符串缩短
str = str.replaceFirst(str.substring(0, str.length()), "");
}
while(str.length() > 0){
//如果循环前恰好是三的倍数,第一位不加“,”,其余位数每三位加一个
if(str.length() != strBefore.length()) newStr += ",";
//左端继续放入新数组,每三位一放
newStr += str.substring(0, 3);
//原字符串持续缩短,每三位一短
str = str.replaceFirst(str.substring(0, 3), "");
}
//打印转化好的新数组
System.out.println(newStr);//--->123,456,776,543,210
}
}
3 StringBuffer类的常用方法
3.1 reverse()
意为字符串字符串的翻转。
public class Test10{
public static void main(String[] args){
StringBuffer buffer = new StringBuffer("OK");
//不需要变量来接,引用buffer自己可以接收
buffer.reverse();
System.out.println(buffer);//--->KO
}
}
3.2 append()
意为追加连接,类似于String的“+=”。但不需要再将值赋值回去,append()的效率比"+="高。
//输入数组{"张三","王五","李四","赵六"}
//输出[张三,王五,李四,赵六]
public class Test11{
public static void main(String[] args){
String[] data = {"张三","王五","李四","赵六"};
StringBuffer buffer = new StringBuffer("[");
//x:下标 data[x]:元素 -> name
for(int x = 0;x < data.length;x++){
//str += data[x];
buffer.append(data[x]);
if(x != data.length -1){
//str += ",";
buffer.append(",");
}
}
//str += "]";
buffer.append("]");
System.out.println(buffer);
}
}