• 11. 常用类


    1. 包装类(wrapper)

    包装类:八种基本数据类型对应的引用类型即包装类,包装类有了类的特点,就可以调用类中的方法。

    1.1 八大包装类

    在这里插入图片描述

    各个包装类的继承和实现关系:
    (1)Boolean
    在这里插入图片描述

    (2)Character
    在这里插入图片描述

    (3)Byte、Short、Integer、Long、Float、Double
    在这里插入图片描述

    idea 中显示上面关系图的方法(以 Integer 为例):
    (1)在 Integer 上右键,依次选择 Diagram ➡ Show Diagram
    在这里插入图片描述
    (2)进去之后就能看到下图
    在这里插入图片描述
    (3)若想添加其他类,就选中相关父类,按下空格,输入要添加的类。如添加 Byte 包装类,就在 Number 类上按下空格,输入 Byte。
    在这里插入图片描述
    (4)添加 Byte 类后的效果
    在这里插入图片描述
    (5)拓展1:显示类中的方法,按下图中的图标
    在这里插入图片描述
    拓展2:显示类中的属性,按下图中的图标
    在这里插入图片描述

    1.2 装箱和拆箱(包装类与基本数据类型的转换)

    装箱:基本类型 ➡ 包装类型
    拆箱:包装类型 ➡ 基本类型

    Jdk5 前采用手动装箱、拆箱的方式,Jdk5 及以后可以采用自动装箱、拆箱的方式。

    下面演示装箱和拆箱,以 int 和 Integer 为例,其他包装类的用法类似。

    int n1 = 100;
    //Jdk5之前手动装箱: int -> Integer
    Integer integer = new Integer(n1);
    Integer integer1 = Integer.valueOf(n1);
    //Jdk5之前手动拆箱: Integer -> int
    int i = integer.intValue();
    //Jdk5及之后自动装箱:底层调用的是valueOf方法
    Integer integer2 = n1;
    //Jdk5及之后自动装箱:底层调用的是intValue方法
    int n2 = integer2;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    【例】
    (1)下面代码是否正确?

    Double d = 100d;//ok,自动装箱Double.valueOf(100d);
    Float f = 1.5f;//ok,自动装箱Float.valueOf(1.5f);
    
    • 1
    • 2

    (2)下面两段代码输出结果分别是什么?

    // 三元运算符中,精度最高的是double,所以整体会提高精度
    // obj1为1.0
    Object obj1 = true? new Integer(1) : new Double(2.0);
    System.out.println(obj1);//1.0
    
    • 1
    • 2
    • 3
    • 4
    Object obj2;
    if(true)
        obj2 = new Integer(1);
    else
        obj2 = new Double(2.0);
    System.out.println(obj2);//1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.3 包装类方法

    1.3.1 包装类型与 String 类型相互转换

    以 Integer 和 String 转换为例,其它类似:
    (1)包装类(Integer) ➡ String

    Integer i = 100;//自动装箱
    // 方法1:
    String str1 = i + "";
    // 方法2:
    String str2 = i.toString();
    // 方法3:
    //String类的valueOf方法,参数类型是Object
    //其他包装类的valueOf方法,参数类型都是对应的基本数据类型
    //String不在八大包装类范围内,没有对应的基本数据类型,所以valueOf方法的参数不太一样
    String str3 = String.valueOf(i);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    (2)String ➡ 包装类(Integer)

    String str4 = "123456";
    // 方法1:
    //Integer.parseInt返回的是int,自动装箱后赋值给i1
    Integer i1 = Integer.parseInt(str4);
    // 方法2:Integer类有两个构造方法,参数分别是int和String类型
    Integer i2 = new Integer(str4);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.3.2 Integer 类和 Character 类的常用方法

    System.out.println(Integer.MIN_VALUE);//返回最小值
    System.out.println(Integer.MAX_VALUE);//返回最大值
    System.out.println(Character.isDigit('a'));//判断是不是数字
    System.out.println(Character.isLetter('a'));//判断是不是字母
    System.out.println(Character.isUpperCase('a'));//判断是不是大写
    System.out.println(Character.isLowerCase('a'));//判断是不是小写
    System.out.println(Character.isWhitespace('a'));//判断是不是空格
    System.out.println(Character.toUpperCase('a'));//转成大写
    System.out.println(Character.toLowerCase('A'));//转成小写
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    输出结果:

    -2147483648
    2147483647
    false
    true
    false
    true
    false
    A
    a
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    1.4 练习

    看看下面代码,输出什么结果?

    Integer i = new Integer(1);
    Integer j = new Integer(1);
    //"=="判断对象地址是否相等,即是否是同一个对象
    System.out.println(i == j); //false
    
    //自动装箱,底层调用Integer.valueOf
    //若被装箱的值在-128~127,就直接返回(不太严谨,下面细讲)
    //若被装箱的值不在-128~127,就会像上面一样new Integer()
    Integer m = 1;
    Integer n = 1;
    System.out.println(m == n); //true
    
    Integer x = 128;
    Integer y = 128;
    System.out.println(x == y);//false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    可以下断点追踪源码来查看 valueOf 方法,也可以在 ”Integer x = 128“ 的 Integer 上 Ctrl + B,并打开类的 Structure,找到 valueOf 方法。
    在这里插入图片描述
    源码中,IntegerCache.low 是 -128,IntegerCache.high 是 127,可以通过 Ctrl + B 查看。
    将鼠标放在 catch 上,点击 “+”,可以查看 catch 数组元素,catch 数组元素是 -128 ~ 127:
    在这里插入图片描述
    在这里插入图片描述

    在源码中,若被装箱的值在-128~127 时,会从 catch 数组中返回对应元素,看到 catch 数组中的内容后,我们就能理解源码中的处理方式了。

    理解了上面的内容后,来看下面几个例子:

    //示例一
    //只要都是new的,一定不相等
    Integer i1=new Integer(127);
    Integer i2=new Integer(127);
    System.out.println(i1==i2);//F
    //示例二
    Integer i3=new Integer(128);
    Integer i4=new Integer(128);
    System.out.println(i3==i4);//F
    //示例三
    //-128~127,都是catch数组中的
    Integer i5=127;
    Integer i6=127;
    System.out.println(i5==i6);//T
    //示例四
    //-128~127以外的,都是new出来的
    Integer i7=128;
    Integer i8=128;
    System.out.println(i7==i8);//F
    //示例五
    //一个是从catch数组中的,一个是new出来的,是不同的对象
    Integer i9=127; 
    Integer i10=new Integer(127);
    System.out.println(i9==i10);//F
    //示例六
    //只要有基本数据类型,判断的就是“值是否相等”
    Integer i11=127;
    int i12=127;
    System.out.println(i11==i12);//T
    //示例七
    Integer i13=128;
    int i14=128;
    System.out.println(i13==i14);//T
    
    • 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

    2. String 类

    2.1 String 类的结构

    • String 对象用于保存字符串,也就是一组字符序列。
      字符串常量对象是用双引号括起的字符序列。例如:“你好”、“12.97”、“boy" 等。
      字符串的字符使用 Unicode 编码,一个字符占两个字节(字母、汉字都是)。

    • 在前面介绍包装类时,很多类都实现了 Serializable 和 Comparable 接口。String 类同样也实现了这两个接口。那么,实现这两个接口的作用是什么呢?
      在这里插入图片描述
      (1)实现了 Serializable 接口,说明可以串行化 / 序列化,即:可以在网络上传输、保存到文件。
      (2)实现了 Comparable 接口,说明对象可以相互比较。

    • String类有很多构造器(构造器的重载),常用的有:

      String s1 = new String();
      String s2 = new String(String 初始化字符串);
      String s3 = new String(char[] a);
      String s4 = new String(char[] a,int startIndex,int count)
      String s5 = new String(byte[] b)
      
      • 1
      • 2
      • 3
      • 4
      • 5
    • String 是 final 类,不能被其他的类继承。

    • String 类有属性 private final char value[],用于存放字符串内容,要注意:value 是 final 类型,不可以修改(指的是value的指向不能改变,但是单个字符可以改变)。

      final char[] value = {'a', 'b', 'c'};
      value[0] = 'A';//正确
      char[] value2 = {'c', 'd', 'e'};
      value = value2;//错误
      
      • 1
      • 2
      • 3
      • 4

    2.2 String 创建过程及内存布局

    创建 String 对象的两种方式:

    方式一:直接赋值 String s = "hsp";
    先从常量池查看是否有 “hsp” 。若有,直接指向;若没有,重新创建,然后指向。s 最终指向的是常量池的空间地址。

    方式二:调用构造器 String s2 = new String("hsp");
    先在堆中创建对象,对象内含 value 属性,用于指向常量池的 hsp 空间。若常量池没有 “hsp",重新创建;若有,直接通过 value 指向。s2 最终指向的是堆中的对象地址。

    在这里插入图片描述

    2.3 练习

    在比较对象时,”==“ 比较是地址是否相等,即是否是同一个对象;equals 比较的是对象的内容是否相等。

    【例1】

    String a = "abc";
    String b ="abc";
    System.out.println(a.equals(b));//true
    System.out.println(a==b);//true
    
    • 1
    • 2
    • 3
    • 4

    【例2】

    String a = "hsp";
    String b =new String("hsp");
    System.out.println(a.equals(b));//true
    System.out.println(a==b);//false
    System.out.println(a==b.intern());//true
    System.out.println(b==b.intern());//false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注:对象 . intern() 时,会根据对象的内容去常量池中查找该内容的地址,并返回该地址。
    【例3】

    String s1 = "hspedu";
    String s2 = "java";
    String s4 = "java";
    String s3 = new String("java");
    System.out.println(s2 == s3);//false
    System.out.println(s2 == s4);//true
    System.out.println(s2.equals(s3));//true
    System.out.println(s1 == s2);//false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    【例4】

    Person p1 = new Person();
    p1.name = "hspedu";
    Person p2 = new Person();
    p2.name = "hspedu";
    System.out.println(p1.name.equals(p2.name));//true
    System.out.println(p1.name == p2.name);//true
    System.out.println(p1.name == "hspedu");//true
    String s1 = new String("bcde");
    String s2 = new String("bcde");
    System.out.println(s1==s2);//false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    2.4 String 对象特性

    【例1】画出内存

    String s = "hello";
    s = "haha";
    
    • 1
    • 2

    在这里插入图片描述

    【例2】下面的代码创建了几个对象?

    String a = "hello"+"abc";
    
    • 1

    答:只创建了一个对象。编译器底层做了优化,会判断创建的常量池对象,是否有引用指向。所以不会将 “hello” 与 “abc” 放入常量池。上面的代码会被优化为String a = "helloabc;"

    【例3】下面的代码创建了几个对象?画出内存图。

    String a = "hello"; 
    String b = "abc"; 
    String c = a + b;
    
    • 1
    • 2
    • 3

    (1)先创建对象 StringBuilder sb = new StringBuilder()
    (2)执行 sb.append(a);
    (3)再执行 sb.append(b);
    (4)最后 String c= sb.toString()
    在 toString 源码中可以看到,最后返回的是一个新创建的字符串对象。
    在这里插入图片描述
    所以,内存结构图为:
    在这里插入图片描述
    【例4】分析下面代码的输出

    String s1="hspedu";//s1指向池中的"hspedu"
    String s2="java";//s2指向池中的"java”
    String s5="hspedujava";//s5指向池中的“hspedujava"
    String s6=(s1+s2).intern();//s6指向池中的 "hspedujava"
    System.out.println(s5 == s6); //true
    System.out.println(s5.equals(s6));//true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    【例5】画出下列代码的内存结构图

    public class Test1 {
        String str = new String("hsp");
        final char[] ch = {'j', 'a', 'v', 'a'};
    
        public void change(String str, char ch[]) {
            str = "java";
            ch[0] = 'h';
        }
        public static void main(String[] args){
            //在静态方法中访问本类的非静态成员,要先创建对象再访问
            //创建对象时,调用顺序:静态代码块和静态属性初始化,普通代码块和普通属性的初始化,调用构造方法
            //该类中只依次进行静态属性初始化、调用隐式构造方法(可不分析)
            //所以创建对象时,先为str的初始化创建对象,再初始化ch数组
            Test1 ex = new Test1();
            ex.change(ex.str, ex.ch);
            System.out.print(ex.str + " and ");
            System.out.println(ex.ch);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    2.5 String 类的常用方法

    String类是保存字符串常量的。每次更新都需要重新开辟空间,效率较低,因此 java 设计者还提提供了 StringBuilder 和 StringBuffer 来增强 String 的功能,并提高效率(后面我们还会详细介绍 StringBuilder 和 StringBuffer)。

    看一段代码:

    String s = new String("");
    for(int i = 0;i < 80000;i++) {
        s += "hello";
    }
    
    • 1
    • 2
    • 3
    • 4

    这段代码一直不停第地给 s 重新赋值,一直创建对象,效率低下。

    (1) equals():区分大小写,判断内容是否相等。

    String str1 = "hello";
    String str2 = "Hello";
    System.out.println(str1.equals(str2));//false
    
    • 1
    • 2
    • 3

    (2) equalslgnoreCase():忽略大小写,判断内容是否相等。

    boolean res = "john".equalsIgnoreCase("John");
    System.out.println(res);//true
    
    • 1
    • 2

    (3) length():获取字符串长度。(注意:获取数组长度用的是 arr.length

    System.out.println("韩顺平".length());//3
    
    • 1

    (4) indexOf():获取字符或子串在字符串中第一次出现的位置索引(从0开始),若找不到,返回 -1。

    String s1 = "wer@terwe@g";
    System.out.println(s1.indexOf('@'));//3
    System.out.println(s1.indexOf("we"));//0
    
    • 1
    • 2
    • 3

    (5) lastlndexOf():获取字符或子串在字符串中最后一次出现的位置索引(从0开始),若找不到,返回 -1。

    String s2 = "wer@terwe@g@";
    System.out.println(s2.lastIndexOf('@'));//11
    System.out.println(s2.lastIndexOf("we"));//7
    
    • 1
    • 2
    • 3

    (6) substring():截取指定范围的子串。str.substring(n)从索引为 n 的字符开始,截取到最后;str.substring(n1,n2)从索引为 n1 的字符开始,截取到索引为 n2 -1 的字符。

    String name="hello,张三";
    System.out.println(name.substring(6));//输出“张三”
    System.out.println(name.substring(2,5));//输出“llo”
    
    • 1
    • 2
    • 3

    (7) trim():去掉字符串前后的空白(空格、\t、\n)

    String str = " \t  hello \n ";
    System.out.println(str);
    System.out.println(str.trim());
    
    • 1
    • 2
    • 3

    输出结果:

     	  hello 
     
    hello
    
    • 1
    • 2
    • 3

    (8) charAt():获取某索引处的字符,注意不能使用 str[index] 这种方式.。

    String str = "0123456";
    System.out.println(str.charAt(4));//4
    
    • 1
    • 2

    (9) toUpperCase():将字符串中的字母全部转变为大写

    String str = "HeLlowOrld";
    System.out.println(str.toUpperCase());//HELLOWORLD
    
    • 1
    • 2

    (10) toLowerCase():将字符串中的字母全部转变为小写

    String str = "HeLlowOrld";
    System.out.println(str.toLowerCase());//helloworld
    
    • 1
    • 2

    (11) concat():字符串拼接

    String str1 = "hello ";
    String str2 = "world";
    System.out.println(str1.concat(str2).concat(" today"));
    //输出:hello world today
    
    • 1
    • 2
    • 3
    • 4

    (12) replace()s.replace(str1, str2) 将字符串 s 中的所有 str1 替换为 str2

    String str = "jack tom jerry tom jerry";
    System.out.println(str.replace("tom","smith"));
    //输出:jack smith jerry smith jerry
    
    • 1
    • 2
    • 3

    (13) split():使用分割符对字符串进行分割,某些分割符需要转义,比如 “\” 等。
    【例1】

    String str = "锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦";
    String[] strArr = str.split(",");
    for (int i=0; i<strArr.length; i++){
        System.out.println(strArr[i]);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    输出结果:

    锄禾日当午
    汗滴禾下土
    谁知盘中餐
    粒粒皆辛苦
    
    • 1
    • 2
    • 3
    • 4

    【例2】

    String filePath = "F:\\aaa\\bbb";
    String[] fileArr = filePath.split("\\\\");
    for (int i = 0; i < fileArr.length; i++) {
        System.out.println(fileArr[i]);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    输出结果:

    F:
    aaa
    bbb
    
    • 1
    • 2
    • 3

    (14) toCharArray():将字符串转成字符数组

    String str = "hello";
    char[] arr = str.toCharArray();
    for (int i = 0; i < arr.length; i++) {
        System.out.println(arr[i]);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    输出结果:

    h
    e
    l
    l
    o
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (15) compareTo(): 比较两个字符串的大小。若前者大,返回正数;若后者大,返回负数;若相等,返回 0。
    细节:

    • 若两个字符串完全相同,就返回 0;
    • 若逐字符比较时,在某对字符处可以比较出两字符串的大小,就返回这对字符的差值。
    • 若比较到最后,发现前面字符都相同,就返回 str1.length()-str2.length()。
    String str1 = "jack";
    String str2 = "tom";
    String str3 = "jackabc";
    System.out.println(str1.compareTo(str2));//-10
    System.out.println(str1.compareTo(str3));//-3
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (16) format():将字符串格式化
    %s、%d、%.nf、%c 等称为占位符,这些占位符会由后面变量来替换。其中,%.nf 可以被 float 或 double 类型来替换,且会四舍五入。

    String name = "john";
    int age = 10;
    double score = 56.847;
    char gender='男';
    String info =
            "我的姓名是"+name+",年龄是"+age+",成绩是"+score+",性别是"+gender;
    System.out.println("info = " + info);
    String formatstr = "我的姓名是%s,年龄是%d,成绩是%.2f,性别是%c";
    String info2 = String.format(formatstr, name, age, score, gender);
    System.out.println("info2 = " + info2);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    输出结果:

    info = 我的姓名是john,年龄是10,成绩是56.847,性别是男
    info2 = 我的姓名是john,年龄是10,成绩是56.85,性别是男
    
    • 1
    • 2

    3. StringBuffer 类

    3.1 StringBuffer 类基本介绍

    StringBuffer 类的继承和实现关系:
    在这里插入图片描述

    • StringBuffer 实现了 Serializable 接口,所以 StringBuffer 的对象是可串行化 / 序列化的,可以在网络上传输、保存到文件。
    • 父类 AbstractStringBuilder 中,有一个用于存放字符串内容的属性 char[] value,且不是 final 的。
    • StringBuffer 是一个 final 类,不能被继承。

    3.2 String 与 StringBuffer 的对比

    • String 保存的是字符串常量,里面的值不能更改,每次 String 的更新实际上就是更改指向的地址(创建新对象),所以会产生很多废弃的对象,效率较低。(private final char value[] 的内容存放在常量池中)注:value 不可以更改指向,但可以更改所指位置的内容,但这个是源码层面的,程序员利用不了这个 “可更改内容” 的特点,所以只能当 String 是不可修改的。
    • StringBuffer 保存的是字符串变量,里面的值可以更改,每次 StringBuffer 的更新只更新内容,不更新地址(不创建新对象),效率较高。(char[] value 的内容存放放在堆中)例外的情况:只有 value 原来指向的位置不够用时,才会更改地址。
      在这里插入图片描述

    3.3 StringBuffer 构造器

    StringBuffer() :指定父类 AbstractStringBuilder 中的 value 字符数组大小为默认值16,内容为空。

    StringBuffer stringBuffer = new StringBuffer();
    
    • 1

    指定父类 AbstractStringBuilder 中的 value 字符数组大小为100,内容为空。

    StringBuffer stringBuffer1 = new StringBuffer(100);
    
    • 1

    指定父类 AbstractStringBuilder 中的 value 字符数组大小为 “指定的字符串长度”+16,内容为 “指定的字符串”。

    StringBuffer stringBuffer2 = new StringBuffer("hello");
    
    • 1

    java.lang.StringBuffer 代表可变的字符序列,可以对字符串内容进行增删。
    很多方法与 String 相同,但 StringBuffer 是可变长度的。
    StringBuffer 是一个容器。

    3.4 String 与 StringBuffer 的转换

    (1) String ➡ StringBuffer

    //方式1
    String str = "hello tom";
    StringBuffer stringBuffer = new StringBuffer(str);
    //方式2
    StringBuffer stringBuffer1 = new StringBuffer();
    stringBuffer1.append(str);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    (2) StringBuffer ➡ String

    StringBuffer stringBuffer2 = new StringBuffer("hello");
    //方式1
    String str1 = stringBuffer2.toString();
    //方式2
    String str2 = new String(stringBuffer2);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.5 StringBuffer 类的常用方法

    (1) append():追加

    StringBuffer s = new StringBuffer("hello");
    s.append(',');
    s.append("jack").append(100).append(true).append(10.5);
    System.out.println(s);//hello,jack100true10.5
    
    • 1
    • 2
    • 3
    • 4

    (2) delete():删除 [start,end) 的字符

    StringBuffer s1 = new StringBuffer("01234567");
    s1.delete(2, 5);
    System.out.println(s1);//01567
    
    • 1
    • 2
    • 3

    (3) replace():替换[start,end) 的字符

    StringBuffer s2 = new StringBuffer("01234567");
    s2.replace(2,5,"aaaa");
    System.out.println(s2);//01aaaa567
    
    • 1
    • 2
    • 3

    (4) indexOf():查找指定的子串在字符串第一次出现的索引,如果找不到返回 -1

    StringBuffer s3 = new StringBuffer("01234567");
    System.out.println(s3.indexOf("456"));//4
    
    • 1
    • 2

    (5) insert():在指定索引处插入字符串

    StringBuffer s4 = new StringBuffer("01234567");
    s4.insert(5, "aa");
    System.out.println(s4);//01234aa567
    
    • 1
    • 2
    • 3

    (6) length():长度

    StringBuffer s5 = new StringBuffer("01234567");
    System.out.println(s5.length());//8
    
    • 1
    • 2

    3.6 练习

    【例1】下面代码输出什么

    String str = null;
    StringBuffer sb = new StringBuffer();
    sb.append(str);//把str直接当成”null“字符串追加到了sb的尾部
    System.out.println(sb.length());//4
    System.out.println(sb);//null
    
    • 1
    • 2
    • 3
    • 4
    • 5

    【例2】下面代码是否会抛出异常

    String str = null;
    StringBuffer sb1 = new StringBuffer(str);
    System.out.println(sb1);
    
    • 1
    • 2
    • 3

    上面的代码会报空指针异常,原因是第二行在创建 StringBuffer 对象时,会将父类中的 value 字符数组的大小置为str.length()+16,而此时的 str 为空,所以报空指针异常。在 StringBuffer 类的构造器中可以看到:
    在这里插入图片描述
    【例3】给定一个数字,小数点前每 3 位用逗号隔开。

    String str = "87123564.59";
    StringBuffer stringBuffer = new StringBuffer(str);
    for (int i = stringBuffer.indexOf(".")-3; i > 0 ; i-=3) {
        stringBuffer.insert(i, ",");
    }
    System.out.println(stringBuffer);//87,123,564.59
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    4. StringBuilder 类

    4.1 StringBuilder 基本介绍

    • StringBuilder 用法与 StringBuffer 相同,但不保证同步(不是线程安全的),可以在单线程时用作 StringBuffer 的简易替换。如果可能,建议优先采用该类,因为它在大多数实现中比 StringBuffer 快。
    • StringBuilder 上的主要操作是 append 和 insert 方法,可以重载这些方法,来接受任意类型的数据。
    • StringBuilder 和 StringBuffer 均代表可变的字符序列,使用方法与 StringBuffer 相同。

    StringBuilder 可以在一些场景下替换 StringBuffer 的原因是:两者的继承、实现关系相同:
    在这里插入图片描述

    4.2 StringBuilder 结构剖析

    • StringBuilder 继承 AbstractStringBuilder 类。
    • StringBuilder 实现了 Serializable 接口,说明 StringBuilder 对象是可串行化 / 序列化的(对象可以网络传输或保存到文件)
    • StringBuilder 是 final 类,不能被继承。
    • StringBuilder 对象字符序列仍存放在其父类 AbstractStringBuilder 的 char[] value中。因此,字符序列是存放在堆中的。
    • StringBuilder 的方法,没有做互斥处理,即没有 synchronized 关键字,因此只能在单线程的情况下使用。

    4.3 String、StringBuffer、StringBuilder 的比较

    • String:不可变字符序列,效率低,但是复用率高(不同变量可以指向常量池中同一个字符串常量)。

    • StringBuffer:可变字符序列、效率较高(增删)、线程安全

    • StringBuilder:可变字符序列、效率最高、线程不安全(多个线程同时操作时,产生错误的结果)
      在这里插入图片描述

    • String 使用注意事项,先看一段代码:

      string s = "a";
      s += "b";
      
      • 1
      • 2

      上面代码中,先初始化 s 为 ”a“,之后又更新 s。这样 s 之前指向的字符串就被丢弃。如果是在循环中,就会导致大量废弃字符串对象占用空间,影响程序的性能。因此,如果要对 String 做大量修改,就不要使用 String。

    • 效率:StringBuilder > StringBuffer > String

    String、StringBuffer和StringBuilder 的适用场景总结:

    • 如果字符串存在大量的修改操作,一般使用 StringBuffer 或 StringBuilder。
    • 如果字符串存在大量的修改操作,并在单线程的情况,使用 StringBuilder。
    • 如果字符串存在大量的修改操作,并在多线程的情况,使用 StringBuffer。
    • 如果我们字符串很少修改,被多个对象引用,使用 String,比如配置信息等(数据库的用户名、密码、id 等)。
    • StringBuilder 的方法使用和StringBuffer 一样,不再说。

    5. Math类

    5.1 基本介绍

    Math 类有许多与数学运算有关的方法,如:指数、对数、平方根和三角函数。这些方法一般都是静态方法,直接使用即可。

    5.2 方法一览(均为静态方法)

    关于下列方法的返回类型,可以 Ctrl + B 定位到源码中去查看。

    (1) abs(): 求绝对值。

    int abs = Math.abs(-9);
    System.out.println(abs);//9
    
    • 1
    • 2

    (2).pow(): 求幂,返回 double 类型。

    double pow = Math.pow(-2, 4);
    System.out.println(pow);//16.0
    
    • 1
    • 2

    (3) ceil():向上取整,返回 double 类型。

    double ceil = Math.ceil(-3.0001);
    System.out.println(ceil);//-3.0
    
    • 1
    • 2

    (4) floor(): 向下取整,返回 double 类型。

    double floor = Math.floor(-4.999);
    System.out.println(floor);//-5.0
    
    • 1
    • 2

    (5) round(): 四舍五入,返回 double 类型。相当于Math.floor(该参数+0.5)

    long round = Math.round(-5.001);
    System.out.println(round);//-5
    
    • 1
    • 2

    (6) sqrt():开平方。

    double sqrt = Math.sqrt(9);
    System.out.println(sqrt);//3.0
    System.out.println(Math.sqrt(-9));//NAN(not a number)
    
    • 1
    • 2
    • 3

    (7) random():生成随机数。Math.random()生成的是 [0, 1) 的随机数,生成 [a, b] 的随机整数:(int) (a + Math.random() * (b - a + 1))

    int a = 2, b = 7;
    for (int i = 0; i < 10; i++) {
        System.out.print((int) (a + Math.random() * (b - a + 1)) + " ");
    }
    
    • 1
    • 2
    • 3
    • 4

    输出结果:

    2 2 6 4 2 7 4 2 4 7 
    
    • 1

    (8) max():求两个数的最大值。有重载的方法,可以求 int、float、double、long 类型的两个数的较大值。

    int max = Math.max(2, 9);
    System.out.println(max);//9
    
    • 1
    • 2

    (9) min():求两个数的最小值。有重载的方法,可以求 int、float、double、long 类型的两个数的较小值。

    int min = Math.min(2, 9);
    System.out.println(min);//2
    
    • 1
    • 2

    6. Arrays 类

    6.1 Arrays 类的常用方法

    Arrays 类包含一些静态方法,用于管理或操作数组(如:排序、搜索)。
    (1) toString():返回数组的字符串形式

    Integer[] arr = {1, 2, 3, 4, 5};
    System.out.println(Arrays.toString(arr));//输出:[1, 2, 3, 4, 5]
    
    • 1
    • 2

    为什么这么输出?看 Arrays.toString 方法的源码:
    在这里插入图片描述
    源码的含义是:将数组中的每个元素先转成字符串,再追加到一个 StringBuilder 对象中。在所有元素追加之前和之后,还追加了 “[”“]” 。最后将这个 StringBuilder 对象转成 String 返回。

    (2) sort():排排序(默认排序和定制排序)

    默认排序

    Integer[] arr = {5, 1, 3, 2, 4};
    //因为数组是引用类型,所以通过sort排序后,会直接影响到实参arr
    Arrays.sort(arr);
    System.out.println(Arrays.toString(arr));//[1, 2, 3, 4, 5]
    
    • 1
    • 2
    • 3
    • 4

    定制排序
    sort 方法传入两个参数:① 待排序的数组 arr;② 实现了Comparator 接口的匿名内部类,该匿名内部类要实现 compare方法。

    从小到大排序:

    Integer[] arr = {5, 1, 3, 2, 4};
    Arrays.sort(arr, new Comparator() {
        @Override
        public int compare(Object o1, Object o2) {
            Integer i1 = (Integer) o1;
            Integer i2 = (Integer) o2;
            return i1 - i2;
        }
    });
    System.out.println(Arrays.toString(arr));
    //[1, 2, 3, 4, 5]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    从大到小排序:将 compare 方法中的 return 语句改为return i2 - i1;

    (3) binarySearch(): 在升序数组中,用二分查找的方式查找指定元素,并返回索引;若元素不存在,返回 -(low + 1)low 是该元素按照大小顺序应该所在的位置。

    Integer[] arr = {1, 2, 3, 4, 5};
    System.out.println(Arrays.binarySearch(arr, 4));//3
    System.out.println(Arrays.binarySearch(arr, 100));//-6=-(5+1)
    
    • 1
    • 2
    • 3

    (4) copyOf():数组元素的复制
    newArr = Arrays.copyOf(arr, n)表示从 arr 数组中,拷贝 n 个元素到 newArr 数组中。
    若 n > arr.length,就在新数组后面补 null;
    若 n < 0,就抛出异常 “NegativeArraySizeException”。

    该方法的底层使用的是 System.arraycopy()。

    Integer[] arr = {1, 2, 3, 4, 5};
    Integer[] newArr1 = Arrays.copyOf(arr, arr.length);
    System.out.println(Arrays.toString(newArr1));
    Integer[] newArr2 = Arrays.copyOf(arr, arr.length-1);
    System.out.println(Arrays.toString(newArr2));
    Integer[] newArr3 = Arrays.copyOf(arr, arr.length+1);
    System.out.println(Arrays.toString(newArr3));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    输出结果:

    [1, 2, 3, 4, 5]
    [1, 2, 3, 4]
    [1, 2, 3, 4, 5, null]
    
    • 1
    • 2
    • 3

    拷贝元素的个数 < 0 时,抛出 NegativeArraySizeException 异常:

    Integer[] arr = {1, 2, 3, 4, 5};
    Integer[] newArr4 = Arrays.copyOf(arr, -1);
    System.out.println(Arrays.toString(newArr4));
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    (5) fill():数组元素的填充,可以理解为:替换原来的所有元素

    Integer[] arr = new Integer[]{9, 3, 2};
    Arrays.fill(arr, 99);
    System.out.println(Arrays.toString(arr));
    //输出:[99, 99, 99]
    
    • 1
    • 2
    • 3
    • 4

    (6) equals():比较两个数组元素内容是否完全一致,完全一致返回 true;否则返回false。

    Integer[] arr1 = {1, 2, 3, 4, 5};
    Integer[] arr2 = {1, 2, 3, 4, 5};
    System.out.println(Arrays.equals(arr1, arr2));//true
    
    • 1
    • 2
    • 3

    (7) asList():将一组值,转换成 list。

    下面代码中,asList 方法会将(2, 3, 4, 5, 6, 1)数据转成一个 List 集合。
    返回的 asList 编译类型为 List(一个接口),该接口下有很多实现子类,asList 运行类型就是其中一个实现子类 ArrayList(Arrays 类中的一个静态内部类)。

    List asList = Arrays.asList(2,3,4,5,6,1);
    System.out.println("asList="+ asList);
    System.out.println("asList的运行类型:"+asList.getClass());
    
    • 1
    • 2
    • 3

    输出结果:

    asList=[2, 3, 4, 5, 6, 1]
    asList的运行类型:class java.util.Arrays$ArrayList
    
    • 1
    • 2

    6.2 练习

    自定义 Book 类,里面包含 name 和 price 属性。有 4 个书对象,按 price 降序排序。

    public class Exercise {
        public static void main(String[] args) {
            Book[] books = new Book[4];
            books[0] = new Book("红楼梦",100);
            books[1] = new Book("金瓶梅",90);
            books[2] = new Book("青年文摘",5);
            books[3] = new Book("Java从入门到放弃",300);
            Arrays.sort(books, new Comparator() {
                @Override
                public int compare(Object o1, Object o2) {
                    Book book1 = (Book) o1;
                    Book book2 = (Book) o2;
                    //方法返回值已经固定为int,按原来的思路直接返回book1.getPrice() - book2.getPrice()不可行,因为这是double值
                   	//可以转换思路,先用一个double变量接收该double值,根据double值的正负返回一个正整数、负整数或0
                    double priceDiff = book1.getPrice() - book2.getPrice();
                    //如果发现输出顺序与预期相反,调整返回的-1与1的顺序就行
                    if(priceDiff > 0){
                        return -1;//只要是负数就行
                    }else if(priceDiff < 0){
                        return 1;//只要是正数就行
                    }else {
                        return 0;
                    }
                }
            });
            System.out.println(Arrays.toString(books));
        }
    }
    class Book{
        private String name;
        private double price;
    
        public Book(String name, double price) {
            this.name = name;
            this.price = price;
        }
    
        public String getName() {
            return name;
        }
    
        public double getPrice() {
            return price;
        }
    
        @Override
        public String toString() {
            return "Book{" +
                    "name='" + name + '\'' +
                    ", price=" + price +
                    '}';
        }
    }
    
    • 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

    输出结果:

    [Book{name='Java从入门到放弃', price=300.0}, Book{name='红楼梦', price=100.0}, Book{name='金瓶梅', price=90.0}, Book{name='青年文摘', price=5.0}]
    
    • 1

    7. System 类

    (1) exit():一般就用 exit(0) 表示程序退出。0表示一个状态,正常的状态。

    System.out.println("ok1");
    System.exit(0);
    System.out.println("ok2");
    
    • 1
    • 2
    • 3

    输出结果:

    ok1
    
    • 1

    (2) arraycopy():复制数组元素,比较适合底层调用。我们一般使用 Arrays.copyOf 完成复制数组(底层用的就是 System.arraycopy())。
    用法:System.arraycopy(源数组, 源数组起始位置, 目标数组, 目标数组起始位置, 待拷贝的元素个数),如果拷贝的元素个数过多,就报数组越界异常。

    int[] src={1,2,3};
    int[] dest = new int[3];//初始时为{0, 0, 0}
    System.arraycopy(src, 0, dest, 1, 2);
    System.out.println(Arrays.toString(dest));
    
    • 1
    • 2
    • 3
    • 4

    输出结果:

    [0, 1, 2]
    
    • 1

    (3) currentTimeMillens():返回当前时间距离 1970 年 1 月 1 日 0 时的毫秒数。

    System.out.println(System.currentTimeMillis());//1665405672194
    
    • 1

    (4) gc():运行垃圾回收机制 System.gc();

    8. Biglnteger 和 BigDecimal

    8.1 Biglnteger 类

    当需要处理很大的整数时,long 会不够用:
    在这里插入图片描述
    这时,可以用 Biglnteger 类来解决(无论多大的数都可以)。

    BigInteger bigInteger = new BigInteger("10000000000000000000");
    
    • 1

    BigInteger 不能直接加减乘除,要使用对应的方法。

    BigInteger bigInteger1 = new BigInteger("10000000000000000000");
    BigInteger bigInteger2 = new BigInteger("100");
    System.out.println(bigInteger1.add(bigInteger2));
    System.out.println(bigInteger1.subtract(bigInteger2));
    System.out.println(bigInteger1.multiply(bigInteger2));
    System.out.println(bigInteger1.divide(bigInteger2));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    输出结果:

    10000000000000000100
    9999999999999999900
    1000000000000000000000
    100000000000000000
    
    • 1
    • 2
    • 3
    • 4

    BigInteger 底层处理的是字符串,处理完之后再转成 BigInteger。

    8.2 BigDecimal 类

    当小数要求位数较多时,double 就不能满足需求了:

    double num = 12.12345628746872354395483d;
    System.out.println(num);
    
    • 1
    • 2

    输出结果:

    12.123456287468724
    
    • 1

    这时可以用 BigDecimal 类来解决。

    BigDecimal bigDecimal = new BigDecimal("12.12345628746872354395483");
    System.out.println(bigDecimal);
    
    • 1
    • 2

    输出结果:

    12.12345628746872354395483
    
    • 1

    BigDecimal 也不能直接加减乘除,要使用对应的方法。

    BigDecimal bigDecimal1 = new BigDecimal("12.12345628746872354395483");
    BigDecimal bigDecimal2 = new BigDecimal("2");
    
    System.out.println(bigDecimal1.add(bigDecimal2));
    System.out.println(bigDecimal1.subtract(bigDecimal2));
    System.out.println(bigDecimal1.multiply(bigDecimal2));
    //除法直接这样写,可能会撇出异常ArithmeticException
    System.out.println(bigDecimal1.divide(bigDecimal2));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    输出结果:

    14.12345628746872354395483
    10.12345628746872354395483
    24.24691257493744708790966
    6.061728143734361771977415
    
    • 1
    • 2
    • 3
    • 4

    上面代码中,除法可能会抛出异常。原因是:可能出现除不尽的情况。若除数换成 3 ,就会抛出异常。解决办法是:在 divide 方法的第二个参数处指定精度 BigDecimal.ROUND_CEILING。这样的话,如果有除不尽的,就会保留分子的精度(小数位数)。

    BigDecimal bigDecimal1 = new BigDecimal("12.12345628746872354395483");
    BigDecimal bigDecimal2 = new BigDecimal("3");
    System.out.println(bigDecimal1.divide(bigDecimal2, BigDecimal.ROUND_CEILING));
    
    • 1
    • 2
    • 3

    输出结果:

    4.04115209582290784798495
    //小数点后的位数与bigDecimal1相等
    
    • 1
    • 2

    BigDecimal 底层处理的是字符串,处理完之后再转成 BigDecimal。

    9. 日期类

    9.1 题外话:idea 中类的结构图

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    9.2 第一代日期类

    (1) 获取当前系统时间

    //不要引入 java.sql.Date,要引入 java.util.Date
    Date d1 = new Date();
    System.out.println("当前日期:" + d1);
    
    • 1
    • 2
    • 3

    输出结果:

    当前日期:Mon Oct 10 22:12:37 CST 2022
    
    • 1

    默认输出的日期格式是国外的方式,因此通常需要对格式进行转换:

    Date d1 = new Date();
    SimpleDateFormat sdf= new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");
    String format =sdf.format(d1);//format:将日期转换成指定格式的字符串
    System.out.println("当前日期:"+format);
    
    • 1
    • 2
    • 3
    • 4

    输出结果:

    当前日期:2022101010:15:58 星期一
    
    • 1

    (2) 通过指定毫秒数得到时间

    Date d2 = new Date(9234567);
    System.out.println("d2:" + d2);
    
    • 1
    • 2

    输出结果:

    d2=Thu Jan 01 10:33:54 CST 1970
    
    • 1

    (3) 把一个格式化的 String 转成对应的 Date

    String s ="1996年01月01日 10:20:30 星期一";
    //在把String->Date时,使用的sdf格式需要和你给的string的格式一样,否则会抛出转换异常
    SimpleDateFormat sdf= new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");
    Date parse = sdf.parse(s);
    System.out.println("parse:"+ parse);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    输出结果:

    parse:Mon Jan 01 10:20:30 CST 1996
    
    • 1

    9.3 第二代日期类

    第二代日期类,主要就是 Calendar 类(日历)。继承和实现关系如下图:
    在这里插入图片描述

    • Calendar 类是一个抽象类

    • Calendar 类的构造器是 protected,可以通过 getInstance()来获取实例(是Calendar子类的实例吗,Calendar 作为一个抽象类不能直接实例化啊)

      Calendar c = Calendar.getInstance();
      System.out.println(c);
      
      • 1
      • 2

      输出结果:

      java.util.GregorianCalendar[time=1667034489446,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai"...
      
      • 1
    • Calendar 类提供了大量的方法和字段,但没有专门的格式化方法,所以需要自行组合显示。

      Calendar c = Calendar.getInstance();
      System.out.println("年:"+c.get(Calendar.YEAR));
      //月份从0开始
      System.out.println("月:"+(c.get(Calendar.MONTH)+1));
      System.out.println("日:"+ c.get(Calendar.DAY_OF_MONTH));
      //HOUR_OF_DAY是24进制,HOUR是12进制
      System.out.println("小时:"+c.get(Calendar.HOUR_OF_DAY));
      System.out.println("分钟:"+c.get(Calendar.MINUTE));
      System.out.println("秒:"+c.get(Calendar.SECOND));
      System.out.println(c.get(Calendar.YEAR) + "-" + (c.get(Calendar.MONTH) + 1) + "-" + c.get(Calendar.DAY_OF_MONTH));
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10

      输出结果:

      :2022:10:29
      小时:17
      分钟:20
      秒:36
      2022-10-29
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

    9.4 第三代日期类

    9.4.1 前两代日期类的不足

    JDK 1.0 中包含了一个 java.util.Date 类,但是它的大多数方法已经在 JDK1.1 引入 Calendar 类之后被弃用了。而 Calendar 也存在问题:
    (1) 可变性:日期和时间这样的类应该是不可变的,但 Calendar 是可变的。
    (2) 偏移性:月份都从 0 开始,比较奇怪。
    (3) 格式化:没有用于格式化的方法。
    (4) 不是线程安全的;不能处理闰秒等(每隔2天,多出1s)。

    9.4.2 第三代日期类常见方法(JDK8 引入)

    **(1) LocalDate、LocalTime、LocalDateTime **

    LocalDate:获取日期字段,只包含年月日
    LocalTime:获取时间字段,只包含时分秒
    LocalDateTime:获取日期+时间

    LocalDateTime ldt = LocalDateTime.now();
    System.out.println(ldt);
    System.out.println("年:"+ldt.getYear());
    System.out.println("月:"+ldt.getMonth());//输出月份的英文
    System.out.println("月:"+ldt.getMonthValue());//输出月份的值
    System.out.println("日:"+ldt.getDayOfMonth());
    System.out.println("时:"+ldt.getHour());
    System.out.println("分:"+ldt.getMinute());
    System.out.println("秒:"+ldt.getSecond());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    输出结果:

    2022-10-29T17:52:42.107
    年:2022
    月:OCTOBER
    月:10
    日:29
    时:17
    分:52
    秒:42
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    LocalDate 与 LocalTime 的使用与 LocalDateTime 相同,方法调用方式也一样。

    LocalDate ld = LocalDate.now();
    LocalTime lt = LocalTime.now();
    
    • 1
    • 2

    (2) DateTimeFormatter 格式日期类

    LocalDateTime ldt = LocalDateTime.now();
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
    String strDate = dtf.format(ldt);
    System.out.println(strDate);
    
    DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    String strDate2 = dtf2.format(ldt);
    System.out.println(strDate2);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    输出结果:

    202210291847592022-10-29 18:47:59
    
    • 1
    • 2

    (3) Instant 时间戳
    Instant 时间戳与 Date 类转换的方式:

    //通过静态方法now()获取当前的时间戳对象
    Instant now = Instant.now();
    System.out.println(now);
    //Instant → Date
    Date date = Date.from(now);
    System.out.println(date);
    //date → Instant
    Instant instant = date.toInstant();
    System.out.println(instant);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    输出结果:

    2022-10-29T10:57:23.719Z
    Sat Oct 29 18:57:23 CST 2022
    2022-10-29T10:57:23.719Z
    
    • 1
    • 2
    • 3

    (4) plus / minus 增加 / 减少日期的某个部分

    LocalDateTime ldt = LocalDateTime.now();
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    
    LocalDateTime plusLdt = ldt.plusDays(890);
    System.out.println("890天后:"+dtf.format(plusLdt));
    
    LocalDateTime minusLdt = ldt.minusMinutes(3456);
    System.out.println("3456分钟前:"+dtf.format(minusLdt));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    输出结果:

    890天后:2025-04-06 19:09:55
    3456分钟前:2022-10-27 09:33:55
    
    • 1
    • 2

    10. 本章练习

    【例1】将字符串中的指定部分进行反转。比如:将 ”abcdef“ 反转为 “aedcbf“。
    通过编写方法public static String reverse(String str,int start,int end)实现。

    public class StrReverse {
        public static void main(String[] args) {
    		String str = "abcdef";
    		str = reverse(str, 1, 4);
    		System.out.println(str);
        }
        public static String reverse(String str, int start, int end){
        	//String转字符数组
            char[] charArr = str.toCharArray();
            for (int i = start, j = end; i < j; i++, j--) {
                char temp = charArr[i];
                charArr[i] = charArr[j];
                charArr[j] = temp;
            }
            //字符数组转String
            return new String(charArr);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    输出结果:

    aedcbf
    
    • 1

    增加异常处理机制后的代码:

    public class StrReverse {
        public static void main(String[] args) {
            String str = "abcdef";
            try {
                str = reverse(str, 1, 4);
            } catch (Exception e) {
                System.out.println(e.getMessage());
                return;
            }
            System.out.println(str);
        }
        public static String reverse(String str, int start, int end){
            if (!(str!=null && start >= 0 && start < end && end < str.length())){
                throw new RuntimeException("参数不正确");
            }
            //如果上面抛出异常,下面的代码就不再执行
            char[] charArr = str.toCharArray();
            for (int i = start, j = end; i < j; i++, j--) {
                char temp = charArr[i];
                charArr[i] = charArr[j];
                charArr[j] = temp;
            }
            return new String(charArr);
        }
    }
    
    • 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

    【例2】输入用户名、密码、邮箱,如果信息录入正确,则提示注册成功,否则生成异常对象。要求:
    (1)用户名长度为 2 或 3 或 4
    (2)密码的长度为 6,要求全是数字
    (3)邮箱中包含 @ 和 . 并且 @ 在 . 的前面

    public class Register {
        public static void main(String[] args) {
            boolean res = false;
            try {
                //该句出现异常时,try中的剩余语句不再执行
                regs("tom", "123456", "111@qq.com");
                System.out.println("注册成功");
            } catch (Exception e) {
                System.out.println(e.getMessage());//输出错误信息
            }
        }
        public static void regs(String userName, String pwd, String email){
            //过关斩将法
            if (userName==null || pwd==null || email==null){
                throw new RuntimeException("信息不能为空");
            }
            if (!(userName.length()>=2 && userName.length()<=4)){
                throw new RuntimeException("用户名不正确");
            }
            if (!(pwd.length() == 6 && isDigit(pwd))){
                throw new RuntimeException("密码不正确");
            }
            int atIndex = email.indexOf('@');
            int dotIndex = email.indexOf('.');
            if (!(atIndex!=-1 && dotIndex!=-1 && atIndex<dotIndex)){
                throw new RuntimeException("邮箱不正确");
            }
        }
    
        private static boolean isDigit(String pwd) {
            char[] charArray = pwd.toCharArray();
            for (int i = 0; i < pwd.length(); i++) {
                if (charArray[i]<'0' || charArray[i]>'9'){
                    return false;
                }
            }
            return true;
        }
    }
    
    • 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

    【例3】编写 Java 程序,输入形式为:Willian Jefferson Clinton 的人名,以 Clinton,Willian.J 的形式打印出来。其中 .J 是中间单词的首字母。

    public class OptName {
        public static void main(String[] args) {
            String str = "Willian Jefferson Clinton";
            try {
                printName(str);
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }
    
        private static void printName(String str) {
            if (str == null){
                throw new RuntimeException("名字不能为空");
            }
            String[] strArr = str.split(" ");
            if (strArr.length != 3){
                throw new RuntimeException("名字应为三个单词");
            }
            String format = String.format("%s,%s.%c", strArr[2], strArr[0], strArr[1].toUpperCase().charAt(0));
            System.out.println(format);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    【例4】输入字符串,判断里面有多少个大写字母,多少个小写字母,多少个数字。

    public class Homework4 {
        public static void main(String[] args) {
            String str = null;
            try {
                printNum(str);
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }
    
        private static void printNum(String str) {
            if (str == null){
                throw new RuntimeException("字符串不能为null");
            }
            int upperCount = 0;
            int lowerCount = 0;
            int digitCount = 0;
            for (int i = 0; i < str.length(); i++) {
                if (str.charAt(i)>='A' && str.charAt(i)<='Z'){
                    upperCount++;
                }else if (str.charAt(i)>='a' && str.charAt(i)<='z'){
                    lowerCount++;
                }else if (str.charAt(i)>='0' && str.charAt(i)<='9'){
                    digitCount++;
                }
            }
            System.out.println(upperCount);
            System.out.println(lowerCount);
            System.out.println(digitCount);
        }
    }
    
    • 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

    小结 String 的遍历:

    • 只是单纯地遍历:
      (1)先用 str.toCharArray() 将 String 转成字符数组,再遍历字符数组;
      (2)用 str.charAt(i)
    • 遍历并作出改动:只能用(1)
  • 相关阅读:
    java计算机毕业设计水星家纺网站源码+系统+数据库+lw文档+mybatis+运行部署
    【初识 Docker | 基础篇】 Docker 搭建仓库
    Docker Toolbox下载安装运行镜像
    有什么手机软件能分离人声和音乐?
    【网络安全】网络安全协议和防火墙
    Java 多线程:并发编程的三大特性
    [附源码]SSM计算机毕业设计商场日常维修管理系统JAVA
    查找算法.
    新的“HTTP/2 快速重置”零日攻击打破了 DDoS 记录
    Docker搭建MySQL主从复制
  • 原文地址:https://blog.csdn.net/qq_44378854/article/details/127154724