一、创建字符串两种方式的比较
1、String a = “abc”
2、String b = new String(“abc”)
通常将内存分为:方法区(包含常量池)、堆(对象)、栈(变量)
String a = “abc” 变量a存放在栈中,值"abc"存放在常量池,a中保存着"abc"的地址;
String b = new String(“abc”) 对象a存放在堆,值"abc"存放在常量池,a中保存着"abc"的地址
public static void main(String[] args) {
String a = "abc";
String d = "abc";
String b = new String("abc");
String c = new String("abc");
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true
System.out.println(c == b); // false
System.out.println(a == d); // true
}
所以:(1)两个对象用判定返回false,但是equals比较的是value值,值相同时返回true
(2)对象和变量用判定返回也是false
(3)两个变量如果值相同,那么保存的地址相同,所以==判定为true
以上这些可以类比实体类对象的属性,见下方代码
public static void main(String[] args) {
Person person1 = new Person("zhang",20);
Person person2 = new Person("zhang",20);
System.out.println(person1.name == person2.name); //true
person1.name = "ming";
System.out.println(person2.name); //zhang
}
两个对象的name属性都是String类型的变量,都指向"zhang"常量的地址。
所以==比较的是地址,equals比较的是内容。
二、String类型的拼接存储
关于String类型的拼接见下方test
@Test
public void test() {
String s1 = "abc";
String s2 = "def";
String s3 = "abcdef";
String s4 = s1 + "def";
String s5 = "abc" + s2;
String s6 = s1 + s2;
String s7 = "abc" + "def";
String s8 = s6.intern();
System.out.println(s3 == s7); //true
System.out.println(s3 == s4); //false
System.out.println(s3 == s5); //false
System.out.println(s3 == s6); //false
System.out.println(s3 == s8); //true
System.out.println("***********************");
System.out.println(s5 == s4); //false
System.out.println(s5 == s6); //false
System.out.println(s5 == s8); //false
}
所以常量与常量的拼接在常量池中,新的变量仍保存对应常量池地址,只要拼接部分含有,结果就在堆中,相当于new的对象,String a = 对象.intern(),则a保存对象内容对应的常量池地址
三、一道面试题
public class StringFirst {
String str = "good";
String str2 = new String("book");
char[] array = {'n','e','s','t'};
void change(String str1, String str2, char[] arr) {
str1 = "fffff";
str2 = "ddddd";
arr[0] = 'b';
}
@Test
public void test() {
StringFirst first = new StringFirst();
first.change(first.str, first.str2, first.array);
System.out.println(first.str); //good
System.out.println(first.str2); //book
System.out.println(first.array); //best
}
}
传参时,基本数据类型传递数据值,引用数据类型传递数据地址
四、String类型的方法
执行方法时,原字符串不改变,返回的结果保存在新的字符串中
@Test
public void function() {
String s1 = " abDDef ";
String s2 = s1.substring(2);
String s3 = s1.toLowerCase();
String s4 = s1.trim();
System.out.println(s2); //abDDef
System.out.println(s3); // abddef
System.out.println(s4); //abDDef
System.out.println(s1); // abDDef
}
五、String、StringBuffer、StringBuilder的区别
1、String是不可变序列,StringBuffer、StringBuider是可变序列,StringBuffer、StringBuilder默认申请16个字符空间,当容量不够时底层实现扩容(新建长度更大的字符数组,将内容复制到新的数组中)
2、StringBuffer线程安全效率低,StringBuilder线程不安全效率高
3、三者底层都是char[]数组存储
三者执行效率比较
@Test
public void function() {
long startTime;
long endTime;
String str = "";
StringBuffer str2 = new StringBuffer("");
StringBuilder str3 = new StringBuilder("");
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
str += String.valueOf(i);
}
endTime = System.currentTimeMillis();
System.out.println(endTime - startTime); //执行结果155
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
str2.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println(endTime - startTime); //执行结果2
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
str3.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println(endTime - startTime); //执行结果1
}
4、String转成StringBuffer或StringBuilder调用StringBuffer或StringBuilder的构造器;StringBuffer或StringBuilder转成String,调用String的构造器或者调用StringBuffer或StringBuilder的toString()
六、java.util.Date 与 java.sql.Date
@Test
public void function() {
//java.util.Date 与 java.sql.Date的两个常用方法
Date date = new Date();
System.out.println(date.toString()); //Fri Aug 14 10:40:26 CST 2020
System.out.println(date.getTime()); //1597372826362(时间戳)
System.out.println(date); //Fri Aug 14 10:40:26 CST 2020
java.sql.Date date2 = new java.sql.Date(1597372826362L);
System.out.println(date2.getTime()); //1597372826362
System.out.println(date2.toString()); //2020-08-14
System.out.println(date2); //2020-08-14
//java.sql.Date 是 java.util.Date的子类
Date date3 = date2;
System.out.println(date3); //2020-08-14
Date date4 = new Date();
java.sql.Date date5 = new java.sql.Date(date4.getTime());
System.out.println(date5); //2020-08-14
}
另外,SimpleDateFormat类可以实现格式化和解析日期
@Test
public void function() throws ParseException {
//实例化SimpleDateFormat对象的格式
SimpleDateFormat sim = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date date = new Date();
//格式化日期返回字符串
String str = sim.format(date);
System.out.println(str); //输出2020-08-14 06:19:55
//参数是要解析的日期对应的字符串,格式要与对象格式一致,返回日期类型
Date date2 = sim.parse("2020-08-14 18:19:20");
System.out.println(date2); //输出Fri Aug 14 18:19:20 CST 2020
}
七、字符串的编码与解码
@Test
public void function() throws UnsupportedEncodingException {
//字符串的编码
/**
* gbk编码中一个汉字占两个字节,utf-8中一个汉字占3个字节
*/
String str = "abc123中国";
byte[] bytes = str.getBytes(); //使用默认的字符集编码utf-8
System.out.println(Arrays.toString(bytes)); //[97, 98, 99, 49, 50, 51, -28, -72, -83, -27, -101, -67]
byte[] bytes2 = str.getBytes("gbk");
System.out.println(Arrays.toString(bytes2)); //[97, 98, 99, 49, 50, 51, -42, -48, -71, -6]
//字符串的解码
String str2 = new String(bytes);
System.out.println(str2); //abc123中国
String str3 = new String(bytes2);
System.out.println(str3); //abc123�й�,出现乱码是因为编码使用gbk,解码使用utf-8
str3 = new String(bytes2,"gbk");
System.out.println(str3); //abc123中国
}
八、Comparable接口和Comparator接口
Arrays.sort() 方法可以实现对String以及基本数据类型数组的排序,是因为String等实现了Comparable接口,所以自定义的实体类可以实现Comparable接口,从而也能排序
//String类因为实现了Comparable接口所以可以使用Arrays.sort()排序
@Test
public void function() {
String[] strs = new String[]{"DD","AA","FF","MM","BB","CC"};
Arrays.sort(strs);
System.out.println(Arrays.toString(strs));
}
//创建的实体类PhoneShop对象实现Comparable接口排序
//先按照name从小到大,再按照price从大到小排序
@Test
public void test() {
PhoneShop phone1 = new PhoneShop("lianxiang", 50.00);
PhoneShop phone2 = new PhoneShop("huawei", 39);
PhoneShop phone3 = new PhoneShop("xiaomi", 60.03);
PhoneShop phone4 = new PhoneShop("microsoft", 100);
PhoneShop phone5 = new PhoneShop("huawei", 55.29);
PhoneShop phone6 = new PhoneShop("sanxing", 66.33);
PhoneShop phone7 = new PhoneShop("huawei", 93.09);
PhoneShop[] shops = new PhoneShop[]{phone1, phone2, phone3, phone4, phone5, phone6, phone7};
//Arrays.sort()实现排序,要求参数Object对象实现Comparable接口
Arrays.sort(shops);
/**
* 1、Arrays.toString()实现将对象数组中的对象.toString()之后的内容拼接[],形成字符串
* 2、toString()是 Object类的方法,默认显示对象的类名,一般可以在实体类中重写内容
*/
System.out.println(Arrays.toString(shops));
}
//通过Comparator接口自定义排序
//先按照price从小到大,再按照name从小到大排序
@Test
public void test2() {
PhoneShop phone1 = new PhoneShop("lianxiang", 80.00);
PhoneShop phone2 = new PhoneShop("huawei", 39);
PhoneShop phone3 = new PhoneShop("xiaomi", 60.03);
PhoneShop phone4 = new PhoneShop("microsoft", 100);
PhoneShop phone5 = new PhoneShop("huawei", 55.29);
PhoneShop phone6 = new PhoneShop("sanxing", 60.03);
PhoneShop phone7 = new PhoneShop("huawei", 93.09);
PhoneShop[] shops = new PhoneShop[]{phone1, phone2, phone3, phone4, phone5, phone6, phone7};
Arrays.sort(shops, new Comparator<PhoneShop>() { //匿名类实现重写compare
@Override
public int compare(PhoneShop o1, PhoneShop o2) {
if (o1.getPrice() == o2.getPrice()) {
return o1.getName().compareTo(o2.getName());
} else {
return Double.compare(o1.getPrice(), o2.getPrice());
}
}
});
System.out.println(Arrays.toString(shops));
}
test3的输出是:
//[PhoneShop{name='huawei', price=39.0}, PhoneShop{name='huawei', price=55.29}, PhoneShop{name='sanxing', price=60.03},
// PhoneShop{name='xiaomi', price=60.03}, PhoneShop{name='lianxiang', price=80.0}, PhoneShop{name='huawei', price=93.09},
// PhoneShop{name='microsoft', price=100.0}]
实体类PhoneShop的两个重要重写方法:
@Override
public String toString() {
return "PhoneShop{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
@Override
public int compareTo(Object o) {
if (o instanceof PhoneShop) {
PhoneShop shop = (PhoneShop)o;
if (this.name.equals(shop.getName())) {
return Double.compare(shop.getPrice(),this.price);
} else {
return this.name.compareTo(shop.getName());
}
}
throw new RuntimeException("类型不是购物类型");
}