Java-Day-16( 常用类 )

发布时间 2023-04-27 22:09:02作者: 朱呀朱~

Java-Day-16

常用类

包装类

( Wrapper )

  • 针对八种基本数据类型定义相应的引用类型 —— 包装类,有了类的特点,就可以调用类中的方法

  • 基本数据类型 包装类
    boolean Boolean
    char Character
    byte Byte
    short Short
    int Integer
    long Long
    float Float
    double Double
    • Byte、Integer、Long、Float、Double、Short 父类都为 Number,Number 父类为 Object

    • Boolean 和 Character 父类为 Object

  • 包装类和基本数据类型的转换新旧方式

    • jdk5 前是手动装箱和拆箱方式

      • 装箱:基本类型 —> 包装类型,反之为拆箱

        int n1 = 100;
        //        手动装箱 int——>Integer
        Integer integer = new Integer(n1);
                Integer integer1 = Integer.valueOf(n1);
        //        手动拆箱 Integer——>int
        int n2 = integer.intValue();
        
    • jdk5 及其以后是自动装箱和拆箱方式

      • 表面直接用,实则在源码仍是手动时的语句

        int n3 = 200;
        //        自动装箱 int——>Integer
        Integer integer2 = n3; // 底层使用的仍是 Integer.valueOf(n2)
        //        自动拆箱 Integer——>int
        int n4 = integer2;  // 底层使用的仍是 intValue()
        
    • 自动装箱底层调用的是 valueOf 方法

    • 其他类型类似

  • 包装类型和 String 类型的相互转换举例

    • 包装类 ( Integer ) —> String

      Integer i = 100;
      //        F1
      String str1 = i + "";
      //        F2 (包装类都有toString方法)
      String str2 = i.toString();
      //        F3
      String str3 = String.valueOf(i);
      
    • String —> 包装类 ( Integer )

      String n1 = "123";
      //        F1:使用到自动装箱
      Integer n2 = Integer.parseInt(n1);
      //        F2:构造器
      Integer n3 = new Integer(n1);
      
  • 包装类中 Integer 类和 Character 类的常用方法

    System.out.println(Integer.MIN_VALUE); // 返回最小值
           System.out.println(Integer.MAX_VALUE); // 返回最大值
    
           System.out.println(Character.isDigit('a')); // 判断是不是数字 Digit
           System.out.println(Character.isLetter('a')); // 判断是不是字母 Letter
           System.out.println(Character.isUpperCase('a')); // 判断是不是大写 Upper
           System.out.println(Character.isLowerCase('a')); // 判断是不是小写 Lower
     System.out.println(Character.isWhitespace('a')); // 判断是不是空格 
    
           System.out.println(Character.toUpperCase('a')); // 转成大写
           System.out.println(Character.toLowerCase('A')); // 转成小写
    
    • 可以在类图中点击粉色方法图标显示

      image-20230424174003557

  • 练习

    • 注:三元运算符是一个整体,前 int 和后 double 类型整体统一化为最大的 double 类型 ( 因为接收类型为 Object )

      Object obj1 = true ? new Integer(1) : new Double(2.0);
      System.out.println(obj1);
      //        输出一
      
      Object obj2;
      if (true)
          obj2 = new Integer(1);
      else
          obj2 = new Double(2.0);
      System.out.println(obj2);
      //        输出二
      
      • 输出一为:1.0

        输出二为:1

    • 查看输出结果

      // T1
      Integer i = new Integer(1);
      Integer j = new Integer(1);
      System.out.println(i == j);
      // T2
      Integer m = 1;
      Integer n = 1;
      System.out.println(m == n);
      // T3
      Integer x = 128;
      Integer y = 128;
      System.out.println(x == y);
      // T4
      Integer n1 = new Integer(128);
      Integer n2 = 128;
      System.out.println(n1 == n2);
      // T5
      Integer n3 = 127;
      int n4 = 127;
      System.out.println(n3 == n4);
      // T6
      Integer n5 = 128;
      int n6 = 128;
      System.out.println(n5 == n6);
      
      • 1:判断是不是一个对象,两个都是 new 的,所以 false
      • 2:底层是 Integer.valueOf(),看不出,所以要看源码得知,如果范围在 -128 ~ 127 就直接返回,若是超出范围就在源码 new Integer(i),所以是 true
      • 3:超出范围,是 false
      • 4:两个都是 new,所以仍为 false
      • 5、6:只要有基本数据类型,判断的就是值是否相等,所以为 true

String 类

  • String 对象用于保存字符串,也就是一组字符序列

    • 进入 String 源码,打开类图可以看到实现了Serializable、Comparable、CharSequence 三个接口
      • 其中 Serializable 说明 String 可以串行化 ( 就可以在网络传输了 )
      • Comparable 说明 String 对象可以相互比较
      • CharSequence 字符序列
  • 字符串常量对象是用双引号括起的字符序列

  • 字符串的字符使用 Unicode 字符编码,一个字符 ( 不区分字母还是汉字 ) 占两个字节

  • String 是 final 类,不可以继承

  • 源码可知:private final char value[] ——> 用于存放字符串内容,即字符串 String 本质还是 char 数组,

    • 且 final 类型表示赋值后就不能再修改 ( 地址不能修改 ):即 value 不能指向新的地址,但是单个字符内容是可以变化的

      final char[] value = {'a','b','c'};
      value[0] = 'A';  // 不会报错
      
      char[] v2 = {'A','B','C'};
      value = v2;  // 报错,不能直接整分配,不能直接指向一个新的数据空间
      
  • String 类较常用构造方法

    String s1 = new String();
    String s2 = new String(String original);
    String s3 = new String(char[] a);
    String s4 = new String(char[] a, int startIndex, int count);
    
  • 创建方式 ( 在 JVM 中 )

    • 直接赋值:

      String s1 = "zhuyazhu";  
      //  ( 栈中 s1 指向一个常量池地址 )
      
      • 直接从常量池中查看是否有 "zhuyazhu" 的数据空间
      • 如果有,直接同地址指向;如果没有则创建,然后指向
      • 最终 s1 指向的是常量池的空间地址
    • 调用构造器:

      String s2 = new String("zhuyazhu");   
      //  ( 栈中 s2 指向堆里的一个地址,堆中地址指向着常量池的 "zhuyazhu" 地址 )
      
      • 先在堆中创建空间,里面维护了 value ( 来自源码 ) 属性,指向常量池中的 “zhuyazhu” 空间
      • 如果常量池没有 "zhuyazhu" 就重新创建,有的话就直接通过 value 指向
      • 最终 s2 指向的是堆中的空间地址
    • 练习

      String a = "abc";  // 先在常量池建
      String b = "abc";  // 直接在常量池找到了
      System.out.println(a.equals(b)); // equals方法比较的是串里每一个字符,是比较内容
      System.out.println(a == b) // 同地址,看栈里的地址
      
      • 输出:true true
      String a = "zhuyazhu"; // 指向常量池的
      String b = new String("zhuyazhu"); // 指向堆中对象
      System.out.println(a.equals(b)); 
      System.out.println(a == b); 
      System.out.println(a == b.intern()); 
      System.out.println(b == b.intern()); 
      System.out.println(a == "zhuyazhu");
      
      • intern 方法:如果池已经包含一个等于此 String 对象的字符串 ( 用 equals(Object) 方法确定 ),则返回池中的字符串的地址;否则,将此 String 对象添加到池中,并返回此 String 对象的引用 ——> 最终返回的是常量池的地址
      • 1:比较的是内容
      • 2:栈中地址,一个堆的,一个常量池的
      • 3:b.intern:看 b 前述语句知在常量池有没有,有的话直接返回常量池地址 ( 没有就建 )
      • 4:b 指向堆 ( 含有指向常量池地址为值的 value ) 地址,而 b.intern 指向的是常量池地址
      • 5:"zhuyazhu" 本身就是在常量池里,== 比较的还是地址
      • 输出:true false true false true
  • 字符串特性

    • final 类,代表不可变的字符序列

    • 一个字符串对象一旦被分配,其内容是不可变的

      String n = "hello";
      n = "hi";
      
      • 新建了 "hello" 后,又找有没有 "hi",没有就新建,然后重新指向 "hi"
      • 一共建立两个对象
      String a = "hello" + "hi";
      
      • 注意:编译器不傻,单独创建 hello 和 hi 的话又没有用的,所以会等价优化成 String a = "hellohi"
      • 先右后左,一共创建了一个对象
    • 重要规则:String c = "a" + "b" 是常量相加,看的是池;String c = a + b 是变量相加,是在堆中的

      String a = "hello";
      String b = "hi";
      String c = a + b; //与上面的代码不同
      
      • 可以 debug 看看,step into、step out 循环

        //1.into,sb是new在堆中的
        StringBuilder sb = new StringBuilder()
        //2.out、into
        sb.append("hello");
        //3.out、into
        sb.append("hi");
        //4.out、into
        String c = sb.toString()
        // 从 0 开始取 7 个值
        String c = a + b; // 结束
        
      • 实际上是 c 指向堆中的对象 ( String ) value[] —> 池中的 "hellohi"

      • 即:a 指向 hello,b 指向 hi,c 指向堆,堆里 value 指向池中的 hellohi

      String d = "hellohi";
      String e = "hello" + "hi";
      System.out.println(d == e);
      // true,都是常量池
      
    • 注意 intern() 返回的是地址

      String n = "zhu";
      String m = "ya";
      String s1 = "zhuya";
      String s2 = (n + m).intern();
      System.out.println(s1 == s2);
      
      • s1 和 s2 都指向池中的 "zhuya",true
    • 学会画 JVM 图分析

      public class test1 {
          public static void main(String[] args) {
              Test ex = new Test(); // new堆中一个对象,内含的str指向同在堆中的value(new String的),
                                    // 内含的数组对象char也指向堆存”java“数组处(默认放堆里)
              ex.change(ex.str,ex.ch); // 调用方法就产生新栈,此栈中str也指向堆中的value,ch也指向堆中的存”java“数组处
              System.out.print(ex.str + " and "); // 方法调用完成,临时的方法栈销毁了,ex.str还是通过堆中对象、指向堆中value的那个,
                                                  // 指向常量池里的”zhu“,并不是已销毁的临时栈指向的”java“,所以为”zhuand“
              System.out.println(ex.ch); // zava
          }
      }
      class Test {
          String str = new String("zhu"); // 堆中
          final char[] ch = {'j','a','v','a'};
          public void change(String str, char ch[]) {
              str = "java"; // 堆中value指向常量池”java“地址(×)str是final的,此处是栈新开辟的方法栈里的str断value线,重新指向常量池的”java“
              ch[0] = 'z'; // 把堆中数组的j换成z,{'z','a','v','a'}
          }
      }
      
      • 输出:zhu and zava
  • String 类的常见方法

    • String 每次更新都要重新开辟新空间,效率低
    • equals:区分大小写,判断内容是否相等
    • equalslgnoreCase:忽略大小写,判断内容是否相等
    • length:获取字符个数,字符串长度
    • indexOf:获取字符 / 字符串 在字符串中第一次出现的索引,索引从零开始,如果找不到就返回负一
    • lastIndexOf:获取字符 / 字符串在字符串中最后一次出现的索引,索引从零开始,如果找不到就返回负一
    • substring:截取指定范围的子串,左闭右开 [ )
      • substring(6):从索引 6 开始截取后面的所以内容
      • substring(0, 5):从索引 0 开始截取到第 5 个字符,即 5 - 1 = 4 位
    • trim:去先后空格
    • charAt:获取某索引处的字符,注意不能使用 str[index] 这种数组的方式
    • 转大写 ( toUpperCase )、转小写 ( toLowerCase )、拼接 ( concat )、字符串全替换 ( replace,但不会影响本身,要接收返回的结果 )、分割字符串 ( split,考虑转义,数组接收 )、比较两个字符串大小 ( 长度同比较的是对应的不同的字符编码值前减后的差,长度不同比较的是长度差 )、字符串转换成数组 ( toCharArray )、格式字符串 ( format,%s、%d、%.2f、%c 占位符 )

StringBuffer 类

  • java.lang.StringBuffer 代表可变的字符序列,可以对字符串内容进行增删

  • 很多方法与 String 相同,但 StringBuffer 是可变长度的

  • StringBuffer 是一个容器

    • StringBuffer 的直接父类是 AbstractStringBuilder
    • StringBuffer 实现了 Serializable,即 StringBuffer 的对象可以串行化
      • 即对象可以实现网络传输,也可以保存到文件
    • 在父类中 AbstractStringBuilder 有属性 char[] value,不是 final ( 存放于堆而非常量池 ),该 value 数组存放字符串的内容
    • StringBuffer 是一个 final 类,不能被继承
  • String VS StringBuffer

    • String 保存的是字符串常量,里面的值不能修改,每次 String 类的更新实际上就是更改地址,效率较低
      • private final char value[]; // 堆中 value 指向常量池
    • StringBuffer 保存的是字符串常量,里面的值可以更改,每次 StringBuffer 的更新实际上可以更新内容,不用每次更新地址,效率较高
      • char value[]; // 堆中 value 指向堆中数组值
  • StringBuffer 构造器

    • StringBuffer():构造一个其中不带字符的字符串缓冲池,其初始容量为 16 个字符

      StringBuffer f1 = new StringBuffer();
      
    • StringBuffer(CharSecuence seq):构造一个字符串缓冲区,它包含与指定的 CgarSequence 相同的字符

    • StringBuffer(int capacity):

      StringBuffer f2 = new StringBuffer(50);
      
    • StringBuffer(String str):通过一个 String 来创建,char[] 大小就是 str.length() + 16

      StringBuffer f3 = new StringBuffer("hi"); // 长18
      
  • String 与 StringBuffer

    • String 转 StringBuffer

      • F1:使用构造器,对 str 本身没有影响

        String str = "hello";
        StringBuffer strb1 = new StringBuffer(str);
        
      • F2:使用 append 方法,改变

        StringBuffer strb2 = new StringBuffer();
        strb2.append(str);
        //        StringBuffer strb3 = strb2.append(str); 
        
    • StringBuffer 转 String

      • F1:使用 StringBuffer 提供的 toString 方法

        StringBuffer strb = new StringBuffer("HELLO");
        
        String str1 = strb.toString();
        
      • F2:使用构造器方式

        StringBuffer strb = new StringBuffer("HELLO");
        
        String str2 = new String(strb);
        
  • StringBuffer 常用方法 ( 皆从下标零开始,方法使用不仅限于下面示例 )

    • 增 ( 追加 ) append

      • append.(xxx).append.(yyy);
    • 删 ( 删除 ) delete

      • delete(1, 3):删除 >= 1,< 3 的字符 [1, 3)
    • 改 ( 替换 ) replace

      • replace(3, 5, "daung"):把 [3, 5) 的字符更改为 " duang "
    • 查 ( int ) indexOf

      • indexOf("xx"):查找 xx 所在序列下标
    • 插 ( 指定 ) insert

      • insert(2, "dada"):在第二个位置处插入 dada
    • 返回长度 length

  • 练习

    • 查看输出

      // 1.
      String str = null;
      StringBuffer strb = new StringBuffer();
      strb.append(str);
      System.out.println(strb);
      
      // 2.
      StringBuffer strb2 = new StringBuffer(str);
      System.out.println(strb2);
      
      • 1:调用 append() 时,看源码得知,底层调用的是父类 AbstractStringBuilder 的 appendNull() 方法,返回的是 null 字符串
      • 2:直接用构造器方法,源码可知,是先看长度,所以,此方法会报错空指针异常
    • 输入某物品价格,把价格小数点前面的数字每三位加一个逗号

      • 例:1235.52 ——> 1,235.52

        String str = "12121235.52";
        StringBuffer strb1 = new StringBuffer(str);
        
        
        //        int i = strb1.lastIndexOf(".");
        //        strb1.insert(i - 3, ",");
        
        
        //        for (int i = strb1.lastIndexOf("."); i > 0; i -= 3) {
        //            strb1 = strb1.insert(i - 3, ",");
        //        }
        //          小数点前面的数不能整减3时,报错: insert里的 i - 3 有可能先减到负数
        //          (for里的i-3是为了换位置,inert里的是为了插入逗号)
        
        
        for (int i = strb1.lastIndexOf(".") - 3; i > 0; i -= 3) {
            strb1 = strb1.insert(i, ",");
        }
        //        所以改善为先找到位置,再insert,再i-3循环
        
        System.out.println(strb1);
        

StringBuilder 类

  • 简介

    • 一个可变的字符序列,此类提供一个与 StringBuffer 兼容的 API,但不保证同类 ( StringBuilder 不是线程安全,存在多线程问题 )。

    • 该类被设计用作 StringBuffer 的一个简易替换

      • 同 StringBuffer 一样的接口 ( ... 对象可以串行化 ) 和父类 ( AbstractStringBuilder )
    • 用在字符串缓冲区被单个线程使用的时候 ( 即单线程时优先考虑此类 ),大多数实现中要比 StringBuffer 要快。

      • StringBuilder 的所有方法都没有做互斥处理,即都没 synchronized ( 同步,线程时细讲 ) 关键字,因此推荐单线程情况下使用 StringBuilder,如果是多线程的话会有风险

      • StringBuffer 才是用在多线程

    • 在 StringBuilder 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据

    • final 类,不能被继承

    • 对象字符序列仍然是存放在其父类

      • AbstractStringBuilder 的 char[] value,存放堆中
  • String、StringBuffer、StringBuilder 比较

    • String:不可变字符序列,效率低,但是复用率高

      • 在常量池里的存放地址可以不限量被指向
    • StringBuffer:可变字符序列,效率较高 ( 尤其是增删 ),线程安全

    • StringBuilder :可变字符序列,效率最高,线程不安全 ( 如幻读脏读 ... )

    • 如果对 String 做大量修改 ( 如循环多次的更改、拼接 ),不要使用 String

      • 而是多线程的时候用 StringBuffer ( 安全 )

      • 单线程的话考虑 StringBuilder ( 最快 )

      • 字符串修改很少,被多个对象引用时使用 String,比如配置信息等

    • 效率:StringBuilder > StringBuffer > String

      • 可以在自设循环,开头和结尾都 System.currentTimeMillis() 获取时间,最后计算执行时间

Math 类

  • 常用方法 Math.xxx

    • abs(n):取 n 的绝对值

    • pow(n, m):求幂,n 的 m 次方

    • ceil(n):向上取整,返回大于等于 n 的最小整数

    • floor(n):向下取整,返回小于等于 n 的最大整数

    • round(n):四舍五入,可看成:n + 0.5 化

    • sqrt(n):对 n 开平方 ( n 是负数的话输出就是 NaN:表示非数值 )

    • random():随机返回 [ 0, 1 ) 范围内的小数

      • 求返回一个 [ 2, 7 ] 范围内的整数

        1. 2 <= x <= 7 ( 取整要加 (int) )

        2. 设为取 [a, b]

        3. 范围:a <= x <= (a + ( b - a ))

        4. x 取值代码化:(a + Math.random() * ( b - a ))

        5. 化右 [a, b) 为 [a, b+1) — 为了取整:

          (a + Math.random() * ( b - a + 1 ))

        6. 取整,区间化开为闭 [a, b]:

          (int)(a + Math.random() * ( b - a + 1 ))

        7. 因此得出获取 [a, b] 随机整数公式

      • 即 [2, 7] 取整就是:

        (int)(2 + Math.random() * 6)

        => (int)( 2 + [0, 6) )

        => (int)[2, 8)

        => [2, 7]

    • Math.min(n, m):返回 n 和 m 里的最小值

    • Math.max(n, m):返回 n 和 m 里的最大值

Arrays 类

  • 一系列用于管理或操作数组的静态方法

    • toString:返回数组的字符串形式

      • Arrays.toString(数组名)
      • 输出样式:[-1, 5, 9]
    • sort:数组排序,引用类型会影响到实参

      • Arrays.sort(数组名); 默认正序排序

      • 自定制:Arrays.sort(数组名, new Comparator() {} );

        接口编程 + 动态绑定 + 匿名内部类

        // 定制——倒序integer数组
        Arrays.sort(integer, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                Integer i1 = (Integer) o1;
                Integer i2 = (Integer) o2;
                return i2 - i1;
            }
        });
        // 用到匿名内部类,要求实现 compare 接口(源码最终到 TimSort 类的 binarySort 方法,动态绑定返回匿名内部类的计算差值),若是返回正数就是正序了
        
        • 推演:( 把核心的判断条件换成了接口,这样就可以自定义判断逻辑,从而决定排序规则了 —— 本来的排序就是比大小 )
        public class test1 {
            public static void main(String[] args) {
                int[] arr = {1, -1, 8, 0, 20};
        //        bubble(arr);
                bubble1(arr, new Comparator() {
                    @Override
                    public int compare(Object o1, Object o2) {
                        int i1 = (Integer) o1;
                        int i2 = (Integer) o2;
                        return i2 - i1;
                    }
                });
        
                System.out.println(Arrays.toString(arr));
            }
        //    冒泡排序
            public static void bubble(int[] arr) {
                int temp = 0;
                for (int i = 0; i < arr.length - 1; i++){
                    for (int j = 0; j < arr.length - i - 1; j++){
                        if (arr[j] > arr[j + 1]){
                            temp = arr[j];
                            arr[j] = arr[j + 1];
                            arr[j + 1] = temp;
                        }
                    }
                }
            }
            
        //    冒泡 + 定制排序
            public static void bubble1(int[] arr, Comparator c) {
                int temp = 0;
                for (int i = 0; i < arr.length - 1; i++){
                    for (int j = 0; j < arr.length - i - 1; j++){
        //                数组排序由 c.compare(arr[j], arr[j + 1])返回值决定
                        if (c.compare(arr[j], arr[j + 1]) > 0){
                            temp = arr[j];
                            arr[j] = arr[j + 1];
                            arr[j + 1] = temp;
                        }
                    }
                }
            }
        }
        
    • binarySearch:通过二分搜索法进行查找,要求必须排好序

      • 要求是排好序的数组,例如在 arr 中查找 n 所在的下标:
      • int index = Arrays.binarySearch(arr, n);
      • 数组中不存在该元素就返回:- ( 应该存在的位置下标 + 1 )
    • copyOf:数组元素的复制

      • 从 arr 数组中拷贝 length 个元素到 newArr 数组中

        Integer[] newArr = Arrays.copyOf(arr, length); 
        
      • 如果拷贝长度 > arr.length 就在新数组的后面加 null ( int 数组的话加 0 )

    • fill:数组元素的填充

      • fill(num, n):把 num 中的所有数值都换成 n

        Integer[] num = new Integer[]{9, 2, 3, 8};
        Arrays.fill(num, 99);
        
    • equals:比较两个数组元素内容是否完全一致

      • 两个数组的元素、顺序等都得一样

        boolean equals = Arrays.equals(arr, arr2);
        
    • asList:将一组值转换成list

      • 转换成一个 List 集合

        List<Integer> asList = Arrays.asList(1, 2, 3, 4, 5);
        
        System.out.println(asList);
        // [1, 2, 3, 4, 5]
        
  • 练习

    • 已给图书,根据图书的价格排序

      Arrays.sort(books, new Comparator() {
      	public int compare(Object o1, Object o2) {
              Book book1 = (book) o1;
              Book book2 = (book) o2;
              double priceVal = book2.getPrice() - book1.getPrice();
              // 强转的话损失精度,有可能差别小的价格强转后返回了零
              if(priceVal > 0){
                  return -1;
              } else if(priceVal < 0){
                  return 1;
              } else {
                  return 0;
              }
          }	 
      });
      

System 类

  • 常见方法
    • exit:退出程序
      • System.exit(0):0 表示正常状态退出
    • arraycopy:复制数组元素,比较适合底层调用
      • 一般还是用 Arrays.copyOf 完成复制数组
      • System.arraycopy( 原数组,从原数组的哪个索引位置开始拷贝,拷贝到的目标数组,原数组的数据拷贝到目标数组的哪个位置,要拷贝多少个数据到目标数组 ),索引都是从零开始
    • currentTimeMillis:返回当前时间距离 1970-1-1 的毫秒数
    • gc:运行垃圾回收机制

BigInteger 和 BigDecimal 类

  • BigInteger 适合保存比较大的整型

    // 整数最大的 long
    long l = 2222222222222222222;
    // 报错:number too large
    
    BigInteger bigInteger = new BigInteger("2222222222222222222");
    System.out.println(bigInteger);
    // 先化成字符串存,但输出的话仍是整数形式
    
    • 要想使用算术运算,就要将两个数都要为 BigInteger,用 BigInteger 的方法

      BigInteger bigInteger = new BigInteger("2222222222222222222");
      
      BigInteger bigInteger2 = new BigInteger("222");
      
    • 加法:add

      BigInteger add = bigInteger.add(bigInteger2);
      
    • 减法:subtract

      BigInteger subtract = bigInteger.subtract(bigInteger2);
      
    • 乘法:multiply

      BigInteger multiply = bigInteger.multiply(bigInteger2);
      
    • 除法:divide

      BigInteger divide = bigInteger.divide(bigInteger2);
      
      
  • BigDecimal 适合保存精度更高的浮点型 ( 小数 )

    // 小数最大的double
    double d = 20.31415926141592604159;
    // 只保留小数点后15位
    
    BigDecimal bigDecimal = new BigDecimal("20.31415926141592604159");
    
    • 进行计算的话仍旧要将操作数与被操作数化为 BigDecimal 类型,使用其方法

    • 加、减、乘都同上

    • 但除法可能会抛出 ArithmeticException 异常,结果产生了非有限的小数

      System.out.println(bigDecimal.divide(bigDecimal1, BigDecimal.ROUND_CEILING));
      
      • 添加 BigDecimal.ROUND_CEILING 的话,就可以保留除数已有的小数位数,即 bigDecimal 多少位小数,结果就保留多少位小数

日期类

  • 第一代日期类

    • Date:精确到毫秒,代表特定的瞬间

      • 接口:Serializable ( 可序列化 ),Cloneable ( 克隆 ),Comparable ( 可比较 )
    • SimpleDateFormat:格式和解析日期的类

      • 允许进行格式化 ( 日期 —> 文本 ) 和规范化 ( 文本 —> 日期 )

      • 常见模式字母

        字母 元素 示例
        y 1996;96
        M 年中月份 July;07
        w 年中周数 27
        D 年中天数 190
        d 月中天数 10
        H 一天中小时 ( 0 - 23 ) 0
        h 半天中的小时 ( 1 - 12 ) 1
        m 小时中的分钟数 30
        s 分钟中的秒数 50
        E 星期中天数 Tuesday;Tue;星期二
    • 代码编写

    // 获取当前系统时间:date,
    Date date = new Date();
    // 但输出的话默认国外格式
    
    // 自设格式
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");
    String format = sdf.format(date);
    System.out.println(format);
    // format 输出即为自设格式后的当前时间
    
    // 通过输入的毫秒数得到过了此毫秒后的时间
    Date date2 = new Date(9234567);
    System.out.println(date2);
    // date2就是1970年过了9234567毫秒后的时间
    
    // 把一个格式化后的 String 转回对应的 Date 默认格式
    String s = "2023年04月26日 05:31:50 星期三";
    Date parse = sdf.parse(s);
    System.out.println(parse);
    // 但要根据要求于所在方法 throws ParseException 抛出异常
    // 想指定格式还是要format转换,前提是自设 sdf 的格式必须符合字符串 s 的格式一致,否则会抛出转换异常
    
  • 第二代日期类:主要就是 Calendar 类 ( 日历 )

    • public abstract class Calendar extends Object implements Serializable, Cloneable, Comparable< Calendar >

    • Calendar 类是一个抽象类,并且构造器是 private,可以通过 getInstance() 来获取实例,提供了大量方法和字段

      • 即获取实例无法 new,要用 Calendar.getInstance() 获取
      • 字段里存放了年月日等等信息
      Calendar c = Calendar.getInstance();
      System.out.println("年:" + c.get(Calendar.YEAR));
      // 月份要加一,因为返回月的时候是按照零开始编号的,加括号,否则就是字符串拼接了
      System.out.println("月:" + (c.get(Calendar.MONTH) + 1));
      System.out.println("日:" + c.get(Calendar.DAY_OF_MONTH));
      System.out.println("小时:" + c.get(Calendar.HOUR_OF_DAY));
      System.out.println("分钟:" + c.get(Calendar.MINUTE));
      System.out.println("秒:" + c.get(Calendar.SECOND));
      
      • 此类没有提供对应的格式化的类,因此需要程序员自己组合来输出 ( 灵活显示 )
  • 第三代日期类

    • 前两代不足之处

      • JDK 1.0 中包含了一个 java.util.Date 类,但是它的大多数方法已经在 JDK 1.1 引入 Calendar 类之后被弃用了。而且 Calendar 也存在问题:
        1. 可变性:像日期和时间这样的类应该是不可变的
        2. 偏移性:Date 中的年份是从 1900 开始的,而月份都从 0 开始
        3. 格式化:格式化只对 Date 有用,Calendar 则只能 get 获取
        4. 不是线程安全的,不能处理闰秒等 ( 每隔两天,多出一秒 ) 问题
    • 第三代日期类常见方法

      • LocalDate:只包含日期 ( 年月日 )

      • LocalTime:时间 ( 时分秒 )

      • LocalDateTime:日期 + 时间 ( 年月日时分秒 )

        //        使用 now() 返回表示当前日期的对象
        //        LocalDate.now(),LocalTime.now()
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);
        System.out.println(ldt.getYear());
        System.out.println(ldt.getMonth()); // 英文显示
        System.out.println(ldt.getMonthValue()); // 数字
        // ...... 要会查方法
        
      • plusDays:增加天数后的日期

        // 20 天后的日期获取
        LocalDateTime localdatetime2 = ldt.plusDays(20);
        
      • minusMinutes:某某分钟前的日期是多少

        // 660 分钟前的日期获取
        LocalDateTime localdatetime3 = ldt.minusMinutes(660);
        // 可以用下面的格式化再输出查验
        
      • 等等 JDK 8 后的 API 帮助文档查看方法

    • DateTimeFormatter 格式日期类

      LocalDateTime ldt =LocalDateTime.now();
      DateTimeFormatter datetimeformatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH小时mm分钟ss秒");
      String format = datetimeformatter.format(ldt);
      System.out.println(format);
      
    • Instant 时间戳

      • 类似于 Date,提供了一系列和 Date 类转换的方式

        //        通过静态方法 now() 获取表示当前时间戳的对象
        Instant now = Instant.now();
        System.out.println(now);
        
      • Instant ——> Date

        //        通过 form 把 Instant 转换成 Date
        Date date = Date.from(now);
        System.out.println(date);
        
      • Date ——> Instant

        //        通过 date 的 toInstant() 把 date 转换成 Instant
        Instant instant = date.toInstant();
        System.out.println(instant);
        

练习

  • 将字符串中指定的部分进行反转

    • 如:“123456” 反转成 “154326”

      public class test1 {
          public static void main(String[] args) throws ParseException {
              String str = "123456";
              try {
                  str = method(str, 6, 4);
              } catch (Exception e) {
                  System.out.println(e.getMessage());
                  return;
              }
              System.out.println(str);
          }
      
          public static String method(String str, int start, int end) {
      //        重要编程技巧思想 之 对范围限定判断
      //        写出正确情况
      //        然后反转正确情况 (因为正确情况易找,但错误情况难找全)
              if (!(str != null && start >= 0 && end > start && end < str.length())) {
                  throw new RuntimeException("所填参数不正确");
              }
      
              char[] chars = str.toCharArray();
              char temp = ' ';
              for (int n = start, m = end; n < m; n++, m--) {
                  temp = chars[n];
                  chars[n] = chars[m];
                  chars[m] = temp;
              }
      //        return chars.toString();
              return new String(chars);
          }
      }
      
      • 取反正确情况即为所有的错误情况
  • 判断设计,要求输入用户名大于 2 位,小于 4 位,密码六位且都为数字,邮箱有 @ 和 . 并且 @ 在前 . 在后

    public class test1 {
        public static void main(String[] args) throws ParseException {
            try {
                userRegister("2424", "242424", "24@.com");
                System.out.println("输入格式都正确~");
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }
    
        public static void userRegister(String name, String pwd, String email) {
            int length = name.length();
            if (!(length >= 2 && length <=4)) {
                throw new RuntimeException("用户名格式不正确~");
            }
            if (!(pwd.length() == 6 && isDigital(pwd))) {
                throw new RuntimeException("输入密码格式不正确~");
            }
            int n1 = email.indexOf('@');
            int n2 = email.indexOf('.');
            if (!(n1 > 0 && n2 > n1)){
                throw new RuntimeException("邮箱格式不正确~");
            }
        }
    
        public static boolean isDigital(String pwd) {
            char[] chars = pwd.toCharArray();
            for (int i = 0; i < chars.length; i++) {
                if (chars[i] < '0' || chars[i] > '9'){
                    return false;
                }
            }
            return true;
        }
    }
    
  • 完成输出格式要求的字符串

    • 例如:输入人名字符串 “Zhu Ya Zhuwa”,以 "Zhuwa,Zhu .Y" 形式输出

      public class test1 {
          public static void main(String[] args) throws ParseException {
              String name = "Zhu Ya Zhuwa";
              printName(name);
          }
      
          public static void printName(String str){
              if (str == null) {
                  System.out.println("str 不能为空");
                  return;
              }
      
              String[] names = str.split(" ");
              if (names.length != 3){
                  System.out.println("名称输入不正确,对不起,我们只转化三位的名称");
                  return;
              }
      
              String format = String.format("%s,%s .%c",names[2], names[0], names[1].toUpperCase().charAt(0));
              System.out.println(format);
          }
      }
      
  • true false 判断

    class Animal {
        String name;
        public Animal(String name) {
            this.name = name;
        }
    }
    
    //main 中
    String s1 = "zhu";
    Animal a = new Animal(s1);
    Animal b = new Animal(s1);
    System.out.println(a == b); // 1
    System.out.println(a.equals(b)); // 2
    System.out.println(a.name == b.name); // 3
    
    String s2 = new String("zhu");
    String s3 = "zhu";
    System.out.println(s1 == s2); // 4
    System.out.println(s1 == s3); // 5
    System.out.println(s2 == s3); // 6
    
    String s4 = "hello" + s1;
    String s5 = "hellozhu";
    System.out.println(s4.intern() == s5); // 7
    
    1. a、b 非一个对象 —— F
    2. 注意 Animal 类并没有重写 equals 方法,所以底层还是看地址:return (this == obj); —— F
    3. 地址指向相同 —— T
    4. new 的是在堆中存放于 value 数组里的,指向的就是堆中地址,另一个是直接指向常量池 —— F
    5. 都是直接指向常量池地址 —— T
    6. 同 4 —— F
    7. 拼接时有变量就是也指向一个堆,但 intern 是直接指向常量池地址 —— T