0%

java-Day9——api

String类

类的声明

  • final:String是不可以被继承

  • Serializable:可序列化的接口。凡是实现此接口类的独显就可以通过网络或本地流进行数据的传输

  • Comparable:凡是实现此接口类,其对象都可以比较大小

内部声明的属性

jdk8:

private final char value[]:存储字符串数据的容器

​ final:指明此数组一旦初始化,其地址就不可变

jdk9节省内存空间

private final char value[]:存储字符串数据的容器

字符串常量的存储位置

  • 字符串常量都存储在字符串常量池(StringTable)中

  • 字符串常量池不允许存放两个相同的字符串常量

  • 字符串常量池,在不同的jdk版本中,存放位置不同

    jdk7前:字符串常量池在方法区

    jdk7之后:字符串常量池存放在堆空间

String不可变性的理解

  1. 当对字符串重新赋值时,需要重新制定一个字符串常量的位置进行赋值,不能在原有的位置修改
  2. 当对现有的字符串进行拼接操作时,需要重新开辟空间保存拼接以后的字符串,不能在原有的位置修改
  3. 当调用字符串的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); //true
System.out.println(s3 == s5); //false
System.out.println(s3 == s6); //false
System.out.println(s3 == s7); //false
System.out.println(s5 == s6); //false
System.out.println(s5 == s7); //false

String s8 = s5.intern() //返回的是字符串常量池中字面量的地址
System.out.println(s3 == s8); //true
}
}
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); //false
System.out.println(s8 == s10);//false
System.out.println(s9 == s10);//false

构造器

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
/*
* utf8中,一个汉字3字节,一个字母1字节
* gbk中,一个汉字2字节,一个字母1字节
* 编码:String -->字节或字节数组
* 解码: 字节或字节数组 ---> String
* */

// String与char[]之间的转换
@Test
public void test5(){
String str = "hello";
// String-->char[]:调用String的toCharArray();
char[] arr = str.toCharArray();
for(int i =0;i<arr.length;i++){
System.out.println(arr[i]);
}
//char[] -->String:调用String的构造器
String str1 = new String(arr);
System.out.println(str1);
}


// String与byte[]之间的转换
@Test
public void test6(){
String str = new String("hello");
//String -->byte[]:调用String的getBytes()
byte[] arr = str.getBytes();//使用默认字符集utf-8
for(int i =0;i<arr.length;i++){
System.out.println(arr[i]);
}

//byte[] --> String
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()

  • 获取当前时间对应的毫秒数,long类型,时间戳

  • 当前时间与1970年1月1日0时0分0秒之间的毫秒数

  • 常用来计算时间差

SimpleDateFormat

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); //2023-10-09 23:12:19

//解析:字符串--》日期
Date date2 = sdf.parse("2023-10-09 23:12:11");
System.out.println(date2);//Mon Oct 09 23:12:11 CEST 2023
}

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); //2023-10-09
System.out.println(localTime); //23:45:05.662967400
System.out.println(localDateTime); //2023-10-09T23:45:05.662967400

//of():获取指定时间
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);//当前时间至1970年的毫秒数
}

DateTimeFormatter

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); //2023/10/10 12:08:02

//解析:字符串 -》日期、时间
TemporalAccessor temporalAccessor = dateTimeFormatter.parse("2023/11/25 15:29:23");
LocalDateTime localDateTime1 = LocalDateTime.from(temporalAccessor);
System.out.println(localDateTime1);

}

比较器

两种方法:自然排序、定制排序

实现comparable接口-自然排序

实现步骤:

  1. 具体的类A实现Comparable接口
  2. 重写Comparable接口中的compareTo(Object obj)方法,在此方法中指明比较类A的对象的大小的标准
  3. 创建类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 +
'}';
}

/*
*当前的类需要实现Comparable中的抽象方法
* 在此方法中,指明如何判断当前类的大小。
*如果返回值是正,则当前对象大
* 如果为0,则一样大
*比较标准:价格(大到小),名字排序从小到大
*/
@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-定制排序

实现步骤:

  1. 创建一个实习了Comparator接口的实现类
  2. 实现类要求重写Comparator接口中的抽象方法compare(Object o1, Object o2)在此方法中指明要比较大小对象的大小关系(比如,String类、Product类)
  3. 创建此实现类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 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的区别和使用场景

-------------本文结束感谢您的阅读-------------