day12

包装类

Java号称纯面向对象语言,除了基本8种基数据类型,其他的都是类。每一个种类型都有对应的包装类型。

  1. byte -> Byte
  2. short -> Short
  3. int -> Integer
  4. long -> Long
  5. float -> Float
  6. double -> Double
  7. char -> Character
  8. boolean -> Boolean

装箱和拆箱

包装类型封装了一些常用操作,比如从字符串解析到对应的类型可以通过包装类.parse类型进行转换。

  1. int i = Integer.parseInt("123");
  2. byte b = Byte.parseByte("1");
  3. long l = Long.parseLong("222");
  4. ....

还可以通过包装类.ValueOf来创建一个包装类型对象。

  1. Integer i = Integer.valueOf(123);
  2. Float f = Float.valueOf(123.00);

从JDK1.5开始支持简写,上面这种写法,简写之后为:

  1. Integer i = 123;
  2. Float f = 2.00;

Java会自动做valueOf来转换为对应的对象,这个过程称之为装箱;反之通过包装类型转换为基本类型称为拆箱

  1. Integer i = 123;
  2. int i1 = i.intValue();

从包装类转换为基本数据类型可以通过数据类型Value()来实现,同样的,上面这种写法也可以简写。

  1. Integer i = 123;
  2. int i1 = i;

Integer类

Integer类内部有一个缓存器,当通过常量或者valueOf()赋值给一个Integer对象时候,这时候会现在缓存器中查找常量或者valueOf()传递的参数是否存在于缓存中(缓存的范围是-128-127即byte的长度),如果存在的话将直接返回。否则通过new Integer()返回新的引用。

  1. Integer integer2 = new Integer(3);
  2. Integer integer3 = new Integer(3);
  3. // false
  4. System.out.println(integer2 == integer3);
  5. // true
  6. System.out.println(integer2.equals(integer3));
  7. // 缓存器 -128-127 如果存在返回引用
  8. // 否则返回new Integer(value)
  9. Integer integer = 1;
  10. Integer integer1 = 1;
  11. Integer integer4 = 128;
  12. Integer integer5 = 128;
  13. // true
  14. System.out.println(integer == integer1);
  15. // true
  16. System.out.println(integer.equals(integer1));
  17. // false
  18. System.out.println(integer4 == integer5);
  19. // false
  20. System.out.println(integer4.equals(integer5));

String

String类型所有常量都是字符串对象,因此可以通过字符串常量调用方法。对于等值比较推荐将字符串常量放在equals左边,而未确定值的变量作为参数传递。如果是未确定值的变量调用equals方法,在变量的值为null的情况下,会抛出NullPointerException

在内存中方法区中有一个常量池,所有的字符串常量都存放在其中,类似于一个缓存器。当有新的字符串常量的时候,如果常量池存存在与新字符串常量的内容相等的情况下,将直接返回常量池存在的内容引用,而不会创建新的字符串常量。

  1. // 由于常量池的原因,两个对象指向同一个常量池引用
  2. String s = "abc";
  3. String s1 = "abc";
  4. // 在堆中创建一个对象,对象的属性存放"abc"由于常量池的存在 三个对象共享同一个引用
  5. String s2 = new String("abc");
  6. // true
  7. System.out.println(s == s1);
  8. // false
  9. System.out.println(s.equals(s1));
  10. // false
  11. System.out.println(s1 == s2);
  12. // true
  13. System.out.println(s1.equals(s2));

上述代码String s2 = new Stirng("abc");首先在常量池存放内容”abc”,接着在堆中开辟一块空间存放String对象;最后在栈中通过s2指向堆中String对象,总共创建了3个对象,如果有new String("abc")的情况下,那么会开辟两个对象,因为常量池已经存在字符常量”abc”。

比较判断

在实际业务过程中,我们经常要用到比较判断,对于基本数据类型==判断的是值,而对于引用数据类型==判断的是引用地址。Object类有一个方法equals用于判断两个引用类型的数据是否相等。

  1. public boolean equals(Object obj){
  2. return (this == obj);
  3. }

Objectequals方法默认实现是判断两个引用类型的引用是否相等。而有有些场景需要判断引用类型的某个属性相等的情况下返回true。而String类重写了Object的equals实现了属性value的比较,如果两个String类型的属性values值相等,那么会返回true

  1. @Override
  2. public boolean equals(Object anObject){
  3. if(this == anObject){
  4. return true;
  5. }
  6. if(anObject instanceof String){
  7. String anotherString = (String)anObject;
  8. int n = value.length;
  9. if(n == anotherString.value.length){
  10. char[] v1 = value;
  11. char[] v2 = anotherString.value;
  12. int i = 0;
  13. while(n-- != 0){
  14. if(v1[i] != v2[i]){
  15. return false;
  16. }
  17. i++;
  18. }
  19. return true;
  20. }
  21. return false;
  22. }
  23. return false;
  24. }

上述代码中,首先判断当前类和anObject是指向同一个引用,如果指向同一个引用那么属性值必然相等,所以返回true。然后判断anObject是不是String类的实例,如果不是的话就可以返回false,然后判断当前类的内容与anotherString的内容长度是否一致,如果不一致那么返回false。接着循环比较每一个值如果其中一个值不相等那么返回false,最后在while语句返回true

根据String重写equals的源码,我们可以分析出,重写equals进行属性值比较有以下几个步骤:

  1. 判断是否为同一个引用
  2. 判断是否为当前对象的类的实例
  3. 进行强转
  4. 进行属性比较

假如有以下类,在所有属性值相等的情况下返回true,请重写该equals方法。

  1. public class Student {
  2. private int age;
  3. private String name;
  4. private double score;
  5. private String[] hobby;
  6. @Override
  7. public boolean equals(Object anObject) {
  8. if (this == anObject) {
  9. return true;
  10. }
  11. if (anObject instanceof Student) {
  12. Student anotherStudent = (Student) anObject;
  13. if (anotherStudent.age != this.age) {
  14. return false;
  15. }
  16. if (!anotherStudent.name.equals(this.name)) {
  17. return false;
  18. }
  19. if (anotherStudent.score != this.score) {
  20. return false;
  21. }
  22. int hobbyArrayLength = this.hobby.length;
  23. if (anotherStudent.hobby.length != hobbyArrayLength) {
  24. return false;
  25. }
  26. String[] currentStudentHobbyArray = this.hobby;
  27. String[] anotherStudentHobbyArray = anotherStudent.hobby;
  28. int i = 0;
  29. while (hobbyArrayLength-- != 0) {
  30. if (!currentStudentHobbyArray[i].equals(anotherStudentHobbyArray[i])) {
  31. return false;
  32. }
  33. i++;
  34. }
  35. }
  36. return false;
  37. }
  38. }

==和equals的区别

==是比较运算符对于基本数据类型比较的是值,对于引用数据类型比较的是内存地址。而equals是Object类的方法,默认实现是比较内存地址,像数据类型的类equals重写了equals,比较的是值。

字符序列

可变与不可变

String是不可变字符序列,内部value使用final修饰。不可变不是指每次修改字符串内容会报错,而是会指向一个新的引用,特别是对于字符串常量拼接,开销很大。

  1. System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
  2. String s = "";
  3. for (int i = 0; i < 100000; i++){
  4. s += "a";
  5. }
  6. System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
  7. System.out.println("-------");
  8. System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
  9. StringBuilder sb = new StringBuilder();
  10. for (int i = 0; i < 100000; i++){
  11. sb.append("a");
  12. }
  13. System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
  14. // 2020-12-01 20:02:15
  15. // 2020-12-01 20:02:21
  16. // -------
  17. // 2020-12-01 20:02:21
  18. // 2020-12-01 20:02:21

对于频繁使用字符串拼接且不用考虑线程问题可以使用StringBuilder,而需要考虑线程问题可以使用StringBuffer

由于String的不可变性,导致很多修改内容的方法如:拼接,裁剪(concat、subString)等都是返回一个新的字符串。而对于可变字符串很多修改内容不会返回新的字符串而是在本身的内容上修改。

String,StringBuffer,StringBuilder的区别

  1. String是不可变字符序列,StringBufferStringBuilder是不可变字符序列
  2. StringBuffer是线程安全的,StringBuilder是非线程安全的

常用类

Date SimpleDateFormat

Date表示日期,有如下几个public修饰的构造方法

  1. public Date(){
  2. this(System.currentTimeMillis);
  3. }
  4. public Date(long date){
  5. fastTime = date;
  6. }

这两个构造方法有一个重要参数:date表示从标志时间到现在的毫秒数,会加上时区。而默认的构造方法会返回当前的日期。

如果日期格式看着不习惯可以使用SimpleDateFormat

  1. SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
  2. Date now = new Date();
  3. System.out.println(sdf.format(now));

具体差异请查看API。

BigDecimal

由于double运算精度的问题,可以使用BigDecimal,创建两个对象,然后调用方法进行运算。BigDecimal只支持字符串类型。

Objects

Arrays

正则表达式

自己百度