Java中的常用类

1.包装类

  • 概念:针对八种基本数据类型相应的引用类型为包装类(Wrapper),具有了类的特点
基本数据类型包装类
booleanBoolean
charCharacter
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
  • 装箱和拆箱:

    • 装箱:基本类型->包装类型
    • 拆箱:包装类型->基本类型
    • 手动装箱/拆箱:JDK5之前为手动装箱/拆箱,如下代码:
    int i = 10;
    // 手动装箱的两种方式
    Integer i1 = new Integer(i);
    Integer i2 = Integer.valueOf(i);
    // 手动拆箱
    int j = i1.intValue();
    
    • 自动装箱/拆箱:JDK5后为自动装箱/拆箱,自动装箱底层调用valueOf()方法,自动拆箱底层调用intValue()方法,如下代码:
    int i = 10;
    // 自动装箱
    Integer i1 = i;
    // 自动拆箱
    int j = i1;
    // 包装类->String
    Integer i2 = 100;
    String str1 = i2 + "";  // 方式1
    String str2 = i2.toString()  // 方式2
    String str3 = String.valueOf(i)  // 方式3
    // String->包装类
    String s = "123";
    Integer i3  = Integer.parseInt(s);
    Integer i4 = new Integer(s);
    

tips:

  • 除了Boolean和Character类,其他都是继承Number类
  • 三元运算符是一个整体:
Object o1 = true ? new Integer(1) : new Double(2.0);
System.out.println(o1);  // 输出1.0而不是1,因为三元运算符是一个整体,所以遇到new Double进行了类型提升
  • Integer.valueOf源码涉及到面试题:
Integer i1 = new Integer(100);
Integer i2 = new Integer(100);
Integer m = 1;  // 自动装箱,底层运行Integer.valueOf(),在源码中没有new Integer(1),直接从数组返回
Integer n = 1;  // 自动装箱,底层运行Integer.valueOf(),在源码中没有new Integer(1),直接从数组返回
Integer x = 128;  // 自动装箱,底层运行Integer.valueOf(),在源码中运行new Integer(128)
Integer y = 128;  // 自动装箱,底层运行Integer.valueOf(),在源码中运行new Integer(128)
Integer i3 = 128;
int i4 = 128;
System.out.println(i3 == i4);  // true,只要有基本数据类型,则==判断的是值是否相等
System.out.println(i1 == i2); // false
System.out.println(m == n);  // true
System.out.println(x == y);  // false
// ---------valueOf源码---------
public static Integer valueOf(int i) {
    // low=-128,high=127
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);

2.String类

  • 创建方式:

    • 直接赋值:String s1 = "psj";。先从常量池看是否有“psj”数据空间,有就直接指向,没有就重新创建然后指向(对象s1的地址为0x222)
    • 使用构造器:String s2 = new String("psj");。先在中创建空间,里面维护了value属性且该属性指向常量池的“psj”数据空间(对象s2的地址为0x111不是0x222)

    在这里插入图片描述

tips:

  • 字符串的字符使用Unicode编码,即一个字符(无论字母还是汉字)占两个字节
  • String类实现了Serializable接口,说明可以串行化,即可以在网络传输
  • String类是final类,不能被继承,同时代表不可变的字符序列,即一个字符串对象一旦被分配,内容不可变:
String s1 = "hello";
s1 = "haha";
// 上面代码并不是将hello所在地址的内容改为haha,而是在常量池中创建了haha,s1再指向haha所在地址(创建了两个对象)
String s2 = "hello" + "world";
// 上面代码只在常量池中创建了helloworld(假设地址为0x222),并不会额外创建hello和world(编译器做了优化,如果额外创建了那两个对象会造成浪费)
String a = "hello";
String b = "world";
String c = a + b;
// 上面代码中String c = a + b实际执行了:
// 1. 创建StringBuilder对象sb
// 2. 执行sb.append("hello")
// 3. 执行sb.append("world")
// 4. String c = sb.toString(),toString方法中执行new String(...)(创建的String对象假设地址为0x333,但是里面的value属性还是指向0x222)
// 最终还是创建了三个对象:常量池中的hello、world以及堆中的0x333
System.out.println(s2 == c);  // false,c是指向堆0x333而s2指向常量池0x222
  • String类有属性private final byte[] value,用于存放字符串内容(这里为final类型表示value在堆中的地址无法被修改,但是指向常量池的地址可以被修改):
final int a = 1;
a = 2;  // 报错
final char[] value = {'a'};
final char[] value2 = {'b'};
value[0] = 'b';  // 不会报错
value = value2;  // 报错
  • String类的直接赋值和包装类的直接赋值是有区别的:
String a = "psj";
String b = "psj";
// 指向常量池的同一个地址,所以为true。如果为包装类的直接赋值,实际上是自动装箱,涉及到创建对象
System.out.println(a == b);  // true
  • String类的replace、concat等方法执行后对于原来的字符串是没有影响的
  • 相同的字符串调用hashCode方法,得到的值是一样的,与内存地址、进程、机器无关:
String s1 = new String("psj");
String s2 = new String("psj");
System.out.println(s1 == s2);  // false
// 两个字符串对象的hashcode值相同
System.out.println(s1.hashCode());  
System.out.println(s2.hashCode());

3.StringBuffer类

  • String vs StringBuffer

    • string保存的是字符串常量(即value指向的内容在常量池),里面的值不能更改,每次更新实际上是更改指向常量池的地址(实际上是在常量池中创建了新的对象),效率较低
    • StringBuffer保存的是字符串变量(即value指向的内容在堆中),里面的值可以更改,每次更新实际上是更新内容,不用每次更新地址(创建新对象),效率较高
  • StringStringBuffer之间的转换:

    • String->StringBuffer

      • 使用构造器:
      String s = new String("psj");
      StringBuffer sb = new StringBuffer(s);
      
      • 使用append方法:
      String s = new String("psj");
      StringBuffer sb = new StringBuffer();
      sb.append(s);
      
    • StringBuffer->String

      • 使用toString方法:
      StringBuffer sb = new StringBuffer("psj");
      String s = sb.toString();
      
      • 使用构造器:
      StringBuffer sb = new StringBuffer("psj");
      String s = new String(sb);
      
  • 特点:

    • StringBuffer类的父类是AbstractStringBuilder类。该父类中有属性byte[] value,用于存放字符串内容,且由于没有final修饰,所以字符串存放在堆中不是常量池中(即value指向的是堆,不再是常量池)

    • StringBuffer类实现了Serializable接口,说明可以串行化,即可以在网络传输

    • StringBuffer类是final类,不能被继承

tips:

  • StringBuffer()方法构造不带字符的字符串缓冲区时,初始容量为16个字符。StringBuffer("psj")方法构造的字符串缓冲区容量为字符串的长度加上16
  • 当字符串为空时使用StringBufferappend方法不会报错,但是直接在构造器中使用会出错:
String s = null;
StringBuffer sb = new StringBuffer();
sb.append(s);
System.out.println(sb);  // 打印"null",因为底层调用的是AbstractStringBuilder类的appendNull方法
StringBuffer sb1 = new StringBuffer(s);  // 报错,底层源码为super(str.length() + 16),执行str.length()会出现空指针异常

4.StringBuilder类

  • 特点:
    • 可变的字符序列
    • 方法没有做互斥处理(即不是线程安全),用在字符串缓冲区被单个线程使用。单线程时优先采用该类,它比StringBuffer类要快
    • StringBuilder类是final类,不能被继承
    • StringBuilder类的父类是AbstractStringBuilder类,和StringBuffer类一样字符串存放在堆中不是常量池中
    • StringBuilder类实现了Serializable接口,说明可以串行化,即可以在网络传输
  • String vs StringBuffer vs StringBuilder:*
    • String是不可变字符序列,效率低,但是复用率高(比如有多个字符串对象赋值psj,指向的都是常量池的同一个对象)
    • StringBuffer是可变字符序列,线程安全,效率较高
    • StringBuilder是可变字符序列,线程不安全,效率最高
    • 对字符串做大量修改时不要使用String;字符串有大量修改操作且单线程情况下使用StringBuilder,多线程情况下就使用StringBuffer,比如执行String s = "a";s+="b";,实际上原来的a字符串对象被丢弃,并且产生一个字符串ab,多次执行会产生大量的副本留着内存降低效率。

5.其他类

tips:

  • 返回[a,b]之间的随机整数:(int)(Math.random()*(b-a+1)+a),因为Math.random()返回的是[0,1),右边是开区间,所以在代码中需要改为b-a+1
  • Arrays.sort()定制排序(只针对包装类):
Integer[] integers = {1, 2, 5, 0};
Arrays.sort(integers, new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2 - o1;
    }
});
System.out.println(Arrays.toString(integers));  // [5, 2, 1, 0]

​ 上述方法底层调用的是binarySort方法,如果要底层实现排序的方法为冒泡排序代码如下:

int[] arr = {1, 5, -1, 9};
bubble(arr, new Comparator() {
    @Override
    public int compare(Object o1, Object o2) {
        int i1 = (Integer)o1;  // 涉及到自动拆箱
        int i2 = (Integer)o2;
        return i1 - i2;
    }
});
// bubble方法实现
public static void bubble(int[] arr, Comparator c) {
    int temp = 0;
    for (int i = 0; i < arr.length - 1; i++) {
        for (int j = 0; j < arr.length - 1 - i; j++) {
            // 数组排序由c.compare()返回值决定
            if (c.compare(arr[j], arr[j + 1]) > 0) {
                temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}
  • System.arraycopy(...)比较适合底层调用,一般使用Arrays.copyOf(...)完成复制数组(底层代码调用前者)
  • 处理很大的整数时,如果long类型不够用,可以使用BigInteger类:
BigInteger b1 = new BigInteger("151549494949494848488");
BigInteger b2 = new BigInteger("1518181151");
BigInteger b3 = b1.add(b2);  // 不能直接使用加号等符号进行运算
System.out.println(b3);
  • 需要保存一个精度很高的数时,double类型的精度不够使用,可以使用BigDecimal类:
BigDecimal b1 = new BigDecimal("121.15151881811818181");
BigDecimal b2 = new BigDecimal("12.81811818181");
BigDecimal b3 = b1.add(b2);  // 不能直接使用加号等符号进行运算
System.out.println(b3);
BigDecimal b1 = new BigDecimal("19.15151881811818181");
BigDecimal b2 = new BigDecimal("1.1");
// 使用除法时可以会报错(无限循环等),此时指定精度即可,结果会保留分子的精度
BigDecimal b3 = b1.divide(b2, BigDecimal.ROUND_CEILING);
  • Date类默认输出的是国外的日期格式,需要使用SimpleDateFormat类进行格式转换:
Date date = new Date();  // 获取系统当前时间
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");  // 指定格式
String format = sdf.format(date);
System.out.println(format);  // 2022年02月09日 10:21:07 周三
// 可以把格式化的字符串转换成对应的Date(字符串的格式需要和SimpleDateFormat中指定的格式一致)
Date parse = sdf.parse("2022年02月09日 10:21:07 周三");
System.out.println(parse);  // Wed Feb 09 10:21:07 CST 2022
  • 日期类Canlendar是一个抽象类,且构造器由protected修饰,需要通过getInstance方法获取实例,不能直接new:
Calendar calendar = Calendar.getInstance();
// Calender类没有专门的格式化方法,需要自己组合
System.out.println(calendar.get(Calendar.YEAR) + "年");

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值