String类
类的声明
内部声明的属性
jdk8:
private final char value[]:存储字符串数据的容器
final:指明此数组一旦初始化,其地址就不可变
jdk9节省内存空间
private final char value[]:存储字符串数据的容器
字符串常量的存储位置
String不可变性的理解
- 当对字符串重新赋值时,需要重新制定一个字符串常量的位置进行赋值,不能在原有的位置修改
- 当对现有的字符串进行拼接操作时,需要重新开辟空间保存拼接以后的字符串,不能在原有的位置修改
- 当调用字符串的replace()替换现有的某个字符时,需要重新开辟空间保存修改以后的字符串,不能在原有的位置修改
String实例化的两种方式
String s1 = “hello”;
String s2 = new String(“hello”);
Question:
String s2 = new String(“hello”) 在内存中创建了几个对象?
两个。一个在堆空间中new的对象。另一个是在字符串常量池中生成的字面量。
String连接操作
Case1:常量+常量:结果仍存储在字符串常量池中.此时的常量可能是字面量,可能是final修饰的常量。
Case2: 常量+变量 或变量+变量:都会通过new的方式创建一个新的字符串,返回堆空间此字符串对象的地址
Case3:调用字符串的intern():返回的是字符串常量池中字面量的地址
Concat():不管常量调用此方法,还是变量调用,同样不管参数是常量还是变量,总之。调用完concat()方法都返回一个新new的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class StringDemo { @Test public void test1(){ Person p1 = new Person(); Person p2 = new Person(); p1.name = "Tom"; p2.name = "Tom"; } @Test public void test2(){ String s1 = "hello"; String s2 = "world"; String s3 = "helloworld"; String s4 = "hello" +"world"; String s5 = s1 +"world"; String s6 = "hello" + s2; String s7 = s1 +s2;
System.out.println(s3 == s4); System.out.println(s3 == s5); System.out.println(s3 == s6); System.out.println(s3 == s7); System.out.println(s5 == s6); System.out.println(s5 == s7); String s8 = s5.intern() System.out.println(s3 == s8); } }
|
1 2 3 4 5 6 7
| String s8 = s1.concat(s2); String s9 = "hello".concat("world"); String s10 = s1.concat("world");
System.out.println(s8 == s9); System.out.println(s8 == s10); System.out.println(s9 == s10);
|
构造器
public String(), 初始化新创建的String对象,以使其表示空字符序列
public String(String original) 初始化一个新创建的String对象,使其表示一个与参数相同的字符序列
public String(char[] value):通过当前参数中的字符数组来构造新的String
public String(byte[] bytes):通过使用平台的默认字符集解码当前参数中的字节数组来构造新String
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
@Test public void test5(){ String str = "hello"; char[] arr = str.toCharArray(); for(int i =0;i<arr.length;i++){ System.out.println(arr[i]); } String str1 = new String(arr); System.out.println(str1); }
@Test public void test6(){ String str = new String("hello"); byte[] arr = str.getBytes(); for(int i =0;i<arr.length;i++){ System.out.println(arr[i]); }
String str1 = new String(arr); System.out.println(str1);
}
|
String、StringBuffer、StringBuilder
- String:不可变字符序列.底层使用Byte[]
- StringBuffer:可变字符序列;JDK1.0,线程安全,效率低。底层使用char[]
- StringBuilder:可变的字符序列; JDK5.0,线程不安全,效率高,底层使用char[]
源码分析:
String s1 = new String();//Char[] value =new char[0];
String s2 = new String(“abc”);// char[] value = new char[]{‘a’,’b’, ‘c’};
StringBuilder
内部属性:
char[] value //存储字符序列
int count; //实际存储的字符个数
StringBuilder sBuffer1 = new StringBuilder(); //char[] value = new char[16];
StringBuilder sBuffer1 = new StringBuilder(“abc”);//char[] value = new char[16+”abc”.length];
sBuffer1.append(‘ac’)
…不断的添加,一旦count要超过value.length时,就需要扩容:默认扩容为原有容量的2倍+2;并将原有Value数组中的元素复制到新的数组中。
启示
如果开发中需要频繁的针对于字符串的增删改,建议使用StringBuffer或StringBuilder替换String因为使用String效率低
如果不涉及线程安全,则使用StringBuilder
如果大体确定操作的字符个数,建议使用带int capacity参数的构造器避免底层多次扩容。
常用方法
增:
append
删:
delete(inrt start, int end)
deleteCharAt(int index)
改:
replace(int start, int end, String str)
setCharAt(int index, char c)
查:
charAt(int index)
插:
insert(int index,xx)
长度:
length()
三者效率对比:
Stringbuilder> StringBuffer>String
Date类
Jdk8之前:
System类的currentTimesMillis()
1 2 3 4 5 6 7 8 9 10 11
| public void test2() throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date1 = new Date(); String strDate = sdf.format(date1); System.out.println(strDate);
Date date2 = sdf.parse("2023-10-09 23:12:11"); System.out.println(date2); }
|
Jdk8之后:
LocalDate\LocalTime\LocalDateTime
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Test public void test4(){ LocalDate localdate = LocalDate.now(); LocalTime localTime = LocalTime.now(); LocalDateTime localDateTime = LocalDateTime.now(); System.out.println(localdate); System.out.println(localTime); System.out.println(localDateTime); LocalDate localDate1 = LocalDate.of(2021, 5, 23); LocalDateTime localDateTime1 = LocalDateTime.of(2022,12,23,23,34,45); System.out.println(localDate1); System.out.println(localDateTime1); } }
|
常用方法:
getxxxx()/withxxx()/plusxxx()/minusXXX()
Instant 类似于Date
时间线上的一个瞬时点。可能被用来记录应用程序中的事件时间戳
1 2 3 4 5 6
| public void test5(){ Instant instant = Instant.now(); System.out.println(instant); long milliTime = instant.toEpochMilli(); System.out.println(milliTime); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Test public void test6(){ DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime localDateTime = LocalDateTime.now(); String strDateTime = dateTimeFormatter.format(localDateTime); System.out.println(strDateTime);
TemporalAccessor temporalAccessor = dateTimeFormatter.parse("2023/11/25 15:29:23"); LocalDateTime localDateTime1 = LocalDateTime.from(temporalAccessor); System.out.println(localDateTime1);
}
|
比较器
两种方法:自然排序、定制排序
实现comparable接口-自然排序
实现步骤:
- 具体的类A实现Comparable接口
- 重写Comparable接口中的compareTo(Object obj)方法,在此方法中指明比较类A的对象的大小的标准
- 创建类A多个实例,进行大小的比较和排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| @Test public void test2(){ Product[] arr = new Product[6]; arr[0] =new Product("aaa",5555); arr[1] =new Product("bbb",1111); arr[2] =new Product("ccc",3333); arr[3] =new Product("ddd",8888); arr[4] =new Product("eee",1111); arr[5] =new Product("fff",2222); Arrays.sort(arr); for(int i =0; i<arr.length;i++){ System.out.println(arr[i]); } }
}
class Product implements java.lang.Comparable { private String name; private double price;
public Product(String name, double price) { this.name = name; this.price = price; }
@Override public String toString() { return "Product{" + "name='" + name + '\'' + ", price=" + price + '}'; }
@Override public int compareTo(Object o) { if(o == this){ return 0; } if(o instanceof Product){ Product p = (Product) o; int value = Double.compare(this.price, p.price); if(value != 0){ return -value; }
return this.name.compareTo(p.name); } throw new RuntimeException("类型不匹配"); } }
|
Comparator-定制排序
实现步骤:
- 创建一个实习了Comparator接口的实现类
- 实现类要求重写Comparator接口中的抽象方法compare(Object o1, Object o2)在此方法中指明要比较大小对象的大小关系(比如,String类、Product类)
- 创建此实现类A的对象,并将此对象传入到相关方法的参数位置即可(比如说:Arrays.sort(…,类A的实例)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class ComparatorTest { @Test public void test1(){ Product[] arr = new Product[6]; arr[0] =new Product("aaa",5555); arr[1] =new Product("bbb",1111); arr[2] =new Product("ccc",3333); arr[3] =new Product("ddd",8888); arr[4] =new Product("eee",1111); arr[5] =new Product("fff",2222); Comparator comparator = new Comparator() { @Override public int compare(Object o1, Object o2) { if(o1 instanceof Product && o2 instanceof Product){ Product p1 = (Product) o1; Product p2 = (Product) o2; return Double.compare(p1.getPrice(), p2.getPrice()); } throw new RuntimeException("类型不匹配"); } }; Arrays.sort(arr,comparator);
} }
|
对比两种方法:
角度1:
- 自然排序:单一的,唯一的
- 定制排序:灵活的,多样的
角度2:
角度3:
- 自然排序:对应接口是Comparable,对应的抽象方法compareTo(Object obj)
- 定制排序:对应接口是Comparator,对应的抽象方法compare(Object obj1, Object obj2)
Other
System类
Runtime类
对应着java进程的内存使用的运行时环境,是单例
Math类
凡是与数学运算相关的操作
BigInteger
商业计算值,要求数字精度比较高
Random类
常见问题:
两种创建String对象的方式有什么不同?
String的+怎么实现?
常量+常量:略
变量+常量、变量+变量:创建一个StringBuilder的实例,通过append()添加字符串,最后调用toString()返回一个字符串。
Java中String是不是final?
String为什么不可变,在内存中具体形态
规定不可变
String:提供字符串常量池
String可以在switch中使用
可以.从Jdk7开始使用
String中有哪些方法?
subString()底层做了什么?
底层是new的方式返回一个subStr
操作字符串的类有哪些?有什么区别?
String线程安全问题
线程不安全的
StringBuilder和StringBuffer的线程安全
Comparable和Comparator的区别和使用场景