Skip to content

常用类

常用类是比较重要的,在面试的时候也是问的比较多的


包装类

针对八种基本的数据类型,定义了相应的引用类型,这些基本数据的引用类型叫做包装类

这些基本的数据类型,有了类的特点,就可以调用类中的方法

基本数据类型包装类
booleanBoolean
charCharacter
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble

其中,前面两个包装类的父类是Object类型;后面六个包装类的父类是NumberNumer在继承Object基类

包装类和基本数据类型的转换

  • jdk5之前的手动装箱和拆箱方式,装箱:基本数据类型转换为包装类型,反之拆箱为包装类型转换为基本数据类型
  • jdk5以后(含jdk5)的自动装箱和拆箱方式,自动装箱的底层调用的是valueOf方法,如:Interger.valueOf()
java
public class Wrapper {
    public static void main(String[] args) {
        // 演示int <---> Integer 的装箱和拆箱
        // jdk5之前是手动装箱和拆箱
        int n1 = 100;
        // 手动装箱 int ---> Integer
        Integer integer1 = new Integer(n1);//或者使用 Integer integer1=Integer.valueOf(n1);
        // 手动拆箱 int <--- Integer
        int n = integer1.intValue();
        
        // 在jdk5以后(包括5),就可以自动装箱和自动拆箱
        int n2 = 200;
        // 自动装箱 int ---> Integer
        Integer integer2 = n2;   // 底层调用的是Integer.valueOf方法
        // 自动拆箱 int <--- Integer
        int n = integer2;   // 底层还是调用integer2.intValue()方法
    }
}

上述代码以int来举例,其他的包装类的装箱和拆箱方式类似

经典面试题:

java
// 下面输出的内容是什么:
Object obj1 = ture ? new Integer(1) : new Double(2.0);
System.out.println(obj1);   // 1.0
// ture ? new Integer(1) : new Double(2.0)这一块是一个整体,其精度最高的是Double,因此会提升优先级
// 即三元运算符是一个整体

Integer类的经典面试题:

java
// 下面代码输出什么
public void method() {
    Integer i = new Integer(1);
    Integer j = new Integer(1);
    System.out.println(i == j);   // false  两个不同的对象,只要是new,出来的就是不同对象
    
    // 主要是看范围,如果传入的值为-128到127之间,是直接返回的,没有创建对象
    Integer m = 1;   // 底层是Integer.valueOf(1);  我们需要看源码
    Integer n = 1;   // 底层是Integer.valueOf(1);
    System.out.println(m == n);   // true
    
    Integer x = 128;
    Integer y = 128;
    System.out.println(x == y);   // false  创建了对象,所有不相等
    
    Integer a = 127;
    Integer b = new Integer(127);  
    System.out.println(a == b);   // false  b是对象
    
    // 只要有基本数据类型的,就只要判断值是否相等
    Integer c = 127;
    int d = 127;
    System.out.println(c == d);   // true  
    Integer h = 128;
    int j = 128;
    System.out.println(h == j);   // true  
}

对于Integer.valueOf()的源码:

java
public static Integer valueOf(int i) {
    // 如果传入的i在以下的范围内,就直接从数组返回(这个数组是在类加载的时候就创建好了的,类似于将缓存中的值直接给你),没有真正的创建对象,这个范围是-128 到 127,如果不在这个范围内,就new一个对象
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

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

演示案例以Integer包装类和String类转换为例:

java
public class WrapperVSString {
    public static void main(String[] args) {
        // 包装类 ---> String类型
        Integer i = 100;  // 自动装箱
        // 方式一:
        String str1 = i + "";   // 这种情况对原先的i的数据类型没有影响
        // 方式二:
        String str2 = i.toString();
        // 方式三:
       	String str3 = String.valueOf(i);
        
        // 包装类 <--- String类型
        String str = "123";
        // 方式一
        Integer i2 = Integer.parseInt(str);   // 自动装箱
        // 方式二:通过包装类中的构造器方法
        Integer i3 = new Integer(str);
    }
}

包装类的常用方法

Integer包装类的常用方法:

  • Integer.MIN_VALUE:返回最小值
  • Integer.MAX_VALUE:返回最大值

Character包装类的常用方法:

  • Character.isDigit():判断是不是数字
  • Character.isLetter():判断是不是字母
  • Character.isUpperCase():判断是不是大写字母
  • Character.isLowerCase():判断是不是小写字母
  • Character.isWhitespace():判断是不是空格
  • Character.toUpperCase():转成大写
  • Character.toLowerCase():转成小写

String

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

字符串常量对象是用双引号括起来的,如:"你好"

java
String name = "jlc";
// name为字符串变量
// "jlc"为字符串常量,是一个具体的值

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

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);
  • String s5 = new String(byte[] b);

String类的继承关系:

image-20250417074657877

String类继承于Object基类,同时实现了三个接口:

  • Serializable:实现了这个接口,表示对象可以串行化,意味着String对象可以在网络中传输
  • Comparable:实现了这个接口,说明了String对象可以进行比较
  • CharSequence:字符序列接口

String类是finaly类,即不能被其他类继承

String类中有属性:private final char value[]; 用于存放字符串内容,value是一个final类型,一旦被赋值了,就不可以进行改变(是指地址不可修改,指向了一个空间后,就不可以修改指向另一个空间)(同一个空间中的字符串内容是可以进行修改的)

String对象的两种创建方式:两种创建对象方式的机制是不一样的

  • 直接赋值:String s = "jlc";

    创建机制:先从常量池查看是否有"jlc"s数据空间,如果有,直接指向;如果没有则重新创建,然后指向。s最终指向的是常量池的空间地址

  • 调用构造器:String s = new String("jlc");

    先在堆中创建空间,里面维护了value属性,指向常量池的"jlc"空间。如果常量池没有"jlc",重新创建,如果有,直接通过value指向,最终指向的是堆中的空间地址

具体内存图:

image-20250417081011582

java
String a = "abc";
String b = "abc";
System.out.println(a.equals(b));   // true   比较字符内容是否一样
System.out.println(a == b);     // true   指向的地址是一样的,指向的是同一个对象

String a = new String("abc");
String b = new String("abc");
System.out.println(a.equals(b));   // true   比较字符内容是否一样
System.out.println(a == b);     // false   指向的地址是不一样,指向堆中的不同对象

String a = "abc";
String b = new String("abc");
System.out.println(a.equals(b));   // true   比较字符内容是否一样
System.out.println(a == b); // false  指向的地址是不一样,指向堆中的不同对象,一个指向的常量池中的对象
System.out.println(a == b.intern());  // true  b.intern()返回的是常量池中的地址
System.out.println(b == b.intern());  // false  b是指向堆的,b.intern()返回的是常量池中的地址
// 知识点:当调用intern方法时,如果池已经包含一个等于此String对象的字符串(用equals(Object)方法确定),则返回常量池中的字符串。否则,将此String对象添加到常量池中,并返回此String对象的引用
// 总结:b.intern()方法最终返回的是常量池的地址(对象)
java
Person p1 = new Person();
p1.name = "jlc";
Person p2 = new Person();
p2.name = "jlc";

System.out.println(p1.name.equals(p2.name));   // True  字符串内容一样
System.out.println(p1.name == p2.name);   // True  p1.name 和 p2.name 都是指向常量池中的"jlc"
System.out.println(p1.name == "jlc");   // True  "jlc"的地址就是池中的具体值的地址 p1.name也指向池中的具体值的地址

对象特性

  • String是一个final类,代表不可变的字符序列
  • 字符串是不可变的,一个字符串对象一旦被分配,其内容是不可变的

经典面试题:

java
String s1 = "hello";
s1 = "haha";

创建了两个对象(字符串常量对象),先指向了"hello",后指向了"haha"

内存分布图:

image-20250417084431031

java
String a = "hello" + "abc";
// 底层编译器会进行优化(判断创建的常量池对象,是否有引用指向),优化后等价于:String a = "helloabc";
// 所以创建了一个字符串常量对象
java
String a = "hello";
String b = "abc";
// 1. 先创建一个 StringBuilder  sb = StringBuilder()
// 2. 执行 sb.append("hello");  sb.append("abc");
// 3. 执行 String c = sb.toString();  将字符串返回给c
// 最后其实是c指向堆中的对象(String)value[] --> 指向池中的"helloabc"
String c = a + b;

底层是StringBuilder sb = new StringBuilder(); sb.append(a); sb.append(b);

sb是在堆中,并且append是在原来字符串的基础上进行追加的

一个创建了三个对象,内存分布图:

image-20250417090145061

重要规则:

  • String c1 = "ab" + "cd"; 常量相加,看的是池
  • String c1 = a + b; 变量相加,是在堆中
java
public class Test1 {
    String str = new String("jlc");
    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) {
    Test1 ex = new Test1();
    ex.change(ex.str, ex.ch);
    System.out.print(ex.str + "and");   // jlcand
    System.out.println(ex.ch);   // hava
}

内存分析:

image-20250417104823638

  • 堆中有一个对象为str,字符串对象指向堆中的一个value空间,这个地址指向池中的具体数据
  • 字符串数组,是放在堆中的,ch属性指向堆中的空间
  • 生成的ex对象指向堆中的空间
  • 在调用方法的时候,会在栈中开辟新栈change,将实参ex.str赋值给了形参str;将实参ex.ch赋值给了形参ch,刚开始传递过来时,str也指向原先的地址空间
  • str = "java";执行后,断掉原先的指向,重新指向到常量池中的java地址中
  • ch一开始也是指向ex.ch指向的地址空间(堆中的数组)
  • ch[0] = 'h';执行后,将第一个元素进行修改(修改字符数组的某个元素是合理的)
  • 当方法执行完后,change栈就销毁了,就回到了主栈
  • ex.str没有发生任何的变化,其值还是jlc;但是ex.ch的属性值第一个被修改了

常用方法

String类的方法是非常多大,我们需要掌握常用方法:

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

    java
    String str1 = "hello";
    String str2 = "Hello";
    System.out.println(str1.equals(str2));   // false
  • equalsIgnoreCase:判断内容是否相等(不区分大小写)

  • length:获取字符串长度(获取字符的个数)

    java
    String str = "Hello";
    System.out.println(str.length());  // 5
  • indexOf:获取某个字符在字符串中第一次出现的索引,索引从0开始,如果找不到,返回-1

    java
    String str = "jlc@sdfds@efs";
    int index = str.indexOf('@');
    System.out.println(index);   // 3
    // 该方法除了是单个字符,还可以是字符串
    System.out.println(str.indexOf("lc"));   // 1
  • lastIndexOf:获取某个字符在字符串中最后一次出现的索引,索引从0开始,如果找不到,返回-1

  • substring:截取指定范围的子串

    java
    String name = "hello,jlc";
    System.out.println(name.substring(6));   // jlc  从索引为6的位置开始截取,到最后的字符
    System.out.println(name.substring(0, 5));   // hello  截取指定开始索引和结束索引的字符(开始索引包括,结束索引不包括)
  • trim:去掉前后空格

  • charAt:获取某索引处的字符,注意不能使用Str[index]这个方式(不能将字符串当数组使用)cahrAt(0)表示获取字符串的第一个字符

  • toUpperCase:将字符串全部转换成大写

  • toLowerCase:将字符串全部转换成小写

  • concat:拼接字符串(拼接字符串也可以使用加号来替代)

    java
    String str = "jlc";
    str = str.concat("年龄25");
    System.out.println(str);   // jlc年龄25
  • replace:替换字符串中的字符

    java
    String str = "jlc年龄25";
    // 将字符串中所有的jlc字符串替换为jinLinC字符串
    System.out.println(str.replace("jlc", "jinLinC"));   // jinLinC年龄25
    // str.replace() 返回的是执行完替换过的结果,本身的str字符串是没有任何影响的(即str字符串不改变)
  • split:通过指定的字符来分割字符串,对于某些分割字符,我们需要使用转义字符

    java
    String poem = "abc ABC def DEF";
    String[] split = poem.split(" ");   // 以空格进行分割字符串,返回的是一个数组
    for(int i = 0; i < split.length; i++) {
        System.out.println(split[i]);
    }
    
    // 分割时,有特殊字符,需要加入转义字符
    String load = "D:\\aaa\\bbb";
    String[] load1 = poem.split("\\\\");  // 根据\\进行分割,要使用到转义字符两个斜杠表示一个斜杠
  • compareTo:比较两个字符串的大小(该方法继承于接口Comparable

    java
    String a = "jchn";
    String b = "jack";
    // 先比较长度,如果长度一样(长度不一样返回长度相减后的值),在比较第一个字符,依次类推
    System.out.println(a.compareTo(b));  // 2  'c' - 'a' = 2  正数表示前面的字符大,反之,后面大
    // 如果比较的两个字符串完全相等,则返回0
  • toCharArry:将字符串转化为字符数组

    java
    String str = "hello";
    char[] ch = str.toCharArry();
    for(int i = 0; i < ch; i++) {
        System.out.println(ch[i]);
    }
  • format:格式字符串

    java
    String str = String.format("我的姓名是%s 年龄是%d 成绩是%.2f", name, age, score);

    其中%d等称为占位符,用后续的变量进行填充

    • %s表示后面由字符串进行替换
    • %d表示后面由整数进行替换
    • %.2f表示使用小数进行替换,替换后保留小数点后两位(进行了四舍五入的操作)
    • %c表示使用char字符类型进行替换

StringBuffer

String类是保存字符串常量的,每次更新都需要重新开辟空间,效率较低,因此java设计者还提供了StringBuilderStringBuffer来增强String的功能,提高了效率

java.lang.StringBuffer表示可变的字符序列,可以对字符串内容进行增删,其许多方法与String类中的方法相同,但StringBuffer是可变长度的,简而言之,StringBuffer是一个容器

StringBuffer类的继承关系:

image-20250417134142579

StringBuffer的直接父类是AbstractStringBuilder,在父类AbstractStringBuilder中,有属性char[] value,该value数组来存放字符串内容,但是这个数组不是final类型,该数组存放在堆中,而不是像字符串的value存放在常量池中

同时StringBuffer实现了Serializable,即StringBuffer的对象可以串行化(对象可以网络传输和保存到文件)

StringBuffer类是一个final类,不能被继承

String类与StringBuffer类的区别:

  • String保存的是字符串常量,里面的值不能更改,每次String类的更新实际上就是更改地址,效率低(值放在常量池中)
  • StringBuffer保存的是字符串变量,里面的值可以更改,每次StringBuffer的更新可以更新内容,一般不会更新地址,即创建新对象(只有当原先的地址空间不够了,会统一的扩充一次,将原先的内容拷贝过来),效率高(值放在堆中)
构造器
  • StringBuffer():构造一个其中不带字符的字符串缓冲区,其初始容量为16个字符,用于存放字符内容
  • StringBuffer(CharSequence seq):构造一个字符串缓冲区,它包含与指定的CharSequence相同的字符
  • StringBuffer(int capacity):构造一个不带字符,但具有指定初始容量的字符串缓冲区,即对char[]的的大小进行指定
  • StringBuffer(String str):构造一个字符串缓冲区,并将内容初始化为指定的字符串内容,容量为当前字符串的长度再加上16个字符
StringStringBuffer的互相转换

在开发中,我们经常将StringStringBuffer进行转换

  • StringStringBuffer

    java
    String s = "hello";
    // 方式一
    StringBuffer b1 = new StringBuffer(s);  // 返回的b1是StringBuffer对象,s还是String对象
    // 方式二  先开辟空间,再进行内容的追加
    StringBuffer b2 = new StringBuffer();
    b2.append(s);
  • StringBufferString

    java
    // 方式一
    String s1 = b1.toString();
    // 方式二
    String s2 = new String(b1);
常用方法

StringBuffer类的常用方法有:

  • append:增

    java
    StringBuffer s = new StringBuffer("hello");
    System.out.println(s.append('.'));   // hello.   结果返回的还是StringBuffer类型
  • delect(start, end):删,删除索引大于等于start,小于end处的字符

  • replace(start, end, string):将startend间的内容用string替换掉,不含end

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

  • insert:插入,在指定的位置直接插入字符串

    java
    StringBuffer s = new StringBuffer("hello");
    s.insert(2, "abc");    // 在索引为2的位置插入abc字符串,原来索引为2的内容往后移动
    System.out.println(s);   // heabcllo
  • length:获取长度

经典案例:

java
String str = null;
StringBuffer sb = new StringBuffer();
sb.append(str);   // 根据源码,底层调用的是AbstractStringBuffer的appendNull方法,将null转换为null字符串
System.out.println(sb.length());   // 4
System.out.println(sb);   // null

StringBuffer sb1 = new StringBuffer(str);  // 查看底层源码,该构造器 super(str.length() + 16)
// 因此这里会出现空指针异常

格式化输出小案例:

java
// 商品的价格,要求输出的形式为:23,123,456.59  要求价格的小数点前面每三位用逗号隔开,在输出

// 思路分析:
// 2. 将String转成StringBuffer,使用StringBuffer的insert方法插入逗号
// 3. 使用相关方法进行字符串的处理
String price = "23123456.59";
StringBuffer sb = new StringBuffer(price);
// 找到小数点的索引,然后在该位置的前三位插入逗号即可
for(int i = sb.lastIndexOf(".") - 3; i > 0; i -= 3) {
    sb = sb.insert(i, ",");   // 插入逗号
}
System.out.println(sb);   // 23,123,456.59

StringBuilder

StringBuilder类是一个可变的字符序列,此类提供一个与StringBuffer兼容的API(大多数的方法是一样的,可以参考StringBuffer类),但不保证同步(StringBuilder不是线程安全的)。该类被设计用作StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候。如果可能,单线程的时候建议优先采用该类,因为在大多数实现中,它比StringBuffer要快

StringBuilder上的主要操作是appendinsert方法,可以重载这些方法,以接收任意类型的数据

继承关系:

image-20250417153305904

StringBuilder类是final类,不能被继承

StringBuilder对象的字符序列仍然存放在其父类AbstractStringBuilderchar[] value;中,因此,字符序列是存放在堆中的

StringBuilder的方法,没有做互斥的处理,即没有synchronized关键字,因此,只推荐在单线程的情况下使用

三大类的比较

  • StringBuilderStringBuffer类非常相似,均代表可变字符序列,而且方法也一样

  • String:不可变字符序列,效率低,但是复用率高(可以有很多对象指向常量池中的同一个常量)

  • StringBuffer:可变字符序列,效率较高(增删),线程安全(有synchronized关键字,当一个线程在操作的时候,其他线程就不能操作了)

  • StringBuilder:可变字符序列,效率最高,线程不安全

  • String使用的注意事项说明:

    java
    String s = "a";   // 创建了一个字符串
    s += "b";   // 实际上原来的"a"字符串对象已经丢弃了,现在又产生了一个字符串"ab",如果多次执行这些改变字符串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能,因此,如果我们对String做大量修改,不要使用String类型

    结论:如果我们对String做大量修改,不要使用String类型,应该使用StringBuffer

三大类的使用原则

三大类的使用原则非常重要,一定要记下来

  • 如果字符串存在大量的修改操作,一般使用StringBuilderStringBuffer
  • 如果字符串存在大量的修改操作,并且在单线程的情况,使用StringBuilder
  • 如果字符串存在大量的修改操作,并且在多线程的情况,使用StringBuffer
  • 如果我们的字符串很少修改,被多个对象引用,使用String类,如配置信息等

字符串练习题

  • 将字符串中指定部分进行翻转:如将abcdef中的bcde翻转为aedcbf

    java
    public class StringHomeWork {
        public static void main(String[] args) {
            String str = "abcdef";
            try {
                str = reverse(str, 1, 4);
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }
        
        public static String reverse(String str, int start, int end) {
            // 对输入的参数做一个验证,思路:写出正确的情况,然后取反
            if(!(str != null && start >= 0 && and > start && end < str.length())) {
                throw new RuntimeException("参数不正确");
            }
            // 思路:先将String转成char[],因为char[]的元素是可以交换的
            char[] chars = str.toCharArray();
            char temp = ' ';
            for(int i = start, j = end; i < j; i++, j--) {
                temp = chars[i];
                chars[i] = chars[j];
                chars[j] = temp;
            }
            // 使用chars重新构建一个字符串返回
            return new String(chars);
        }
    }

Math

Math类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数,这些方法在Math类中一般都是静态static方法,常用的方法有:

  • abs:取绝对值

    java
    int abs = Math.abs(-9);
    System.out.println(abs);   // 9
  • pow:求幂

    java
    int pow = Math.pow(2, 4);  // 2的4次方
    System.out.println(abs);   // 16
  • ceil:向上取整(返回大于等于该参数的最小整数)

    java
    double ceil = Math.ceil(3.9);
    System.out.println(ceil);   // 4.0
  • floor:向下取整(返回小于等于该参数的最大整数)

    java
    double floor = Math.floor(4.001);
    System.out.println(floor);   // 4.0
  • round:四舍五入

    java
    long round = Math.round(-5.001);
    System.out.println(round);   // -5
  • sqrt:开方(根号2),如果被开方的数是负数,则结果返回NaN

  • random:取随机数(返回的是0到1之间的随机小数,0可以取到,1取不到)

    java
    // 编写一个[a, b]之间的随机整数
    System.out.println((int)(a + Math.random() * (b - a + 1)));
  • max:求两个数的最大值

    java
    System.out.println(Math.max(1, 9));  // 9
  • min:求两个数的最小值


Arrays

Arrays类里面包含了一系列的静态方法,用于管理或操作数组(比如排序和搜索)

常见的方法有:

  • toString:返回数组的字符串形式(用中括号[]包裹)将数组中的内容拼接到一个字符串中

    java
    Integer[] integers = {1, 20, 90};
    // 之前的输出方式是通过for循环进行输出,现在可以使用Arrays类中的toString方法,以字符串的方式进行输出
    System.out.println(Arrays.toString(integers));  // [1, 20, 90]

    toString底层的方法也是进行一个for循环,只是在前后都追加一个中括号

  • sort:排序(自然排序和定制排序)排序可以使用冒泡排序,也可以使用Arrays提供的排序方法(这样更加简单)

    java
    Integer arr[] = {1, -1, 7, 0, 89};
    // 因为数组是引用类型,所有通过sort排序后,会直接影响到实参arr
    
    // 自然排序
    Arrays.sort(arr);   // 默认是从小到大进行排序的
    
    // 定制排序
    // 可以重载sort,可以通过传入一个接口Comparator实现定制排序(比较器,是从大到小排序还是从小到大排序)
    // 调用定制排序时,第一个参数是排序的数组;第二个参数是实现了Comparator接口的匿名内部类,要求实现compare()方法,这里体现了接口编程的方式
    Arrays.sort(arr, new Comparator() {  // 使用了匿名内部类的概念
        @Override
        public int compare(Object o1, Object o2) {
            // 将传入的参数进行向下转型
            Integer i1 = (Integer) o1;
            Integer i2 = (Integer) o2;
            // i2 - i1 实现了从大到小的排序;  如果改为i1 - i2就是默认排序,即从小到大的排序
            return i2 - i1;
        }
    });
    // 体现了接口编码+动态绑定机制+匿名内部类的综合使用

    binarySort方法底层,会通过匿名内部类的compare方法来决定排序的顺序

    java
    while (left < right) {
     int mid = (left + right) >>> 1;
     if (c.compare(pivot, a[mid]) < 0)
         right = mid;     // 小于0,从小到大排序
     else
         left = mid + 1;  // 大于0,从大到小排序
    }
    assert left == right;
  • binarySearch:通过二分搜索法进行查找,要求必须是排序好的

    java
    Integer[] arr = {1, 2, 90, 123, 300};   // 已经排序好的数组
    // 查找数组中是否有2这个元素值,返回的是对应元素的索引
    int index = Arrays.binarySearch(arr, 2);   
    // 如果查找的元素不存在,则返回-(low + 1)应该存在的位置加1,前面再加一个负号
    // 总之,如果返回的是负值,就表示没有找到
  • copyOf:数组元素的复制

    java
    // 从数组中arr拷贝arr.length个元素到新数组newArr中
    Integer[] newArr = Arrays.copyOf(arr, arr.length);
    // 这个拷贝的数组长度是可以进行变化的
    // arr.length - 1,就是少拷贝一个元素到新数组中,将原数组的最后一个不进行拷贝
    // arr.length + 1,多拷贝一个元素到新数组中,但是原数组又没有多余的元素了,就给新数组加null
    // 如果拷贝的长度小于0,就会抛出异常
  • fill:数组元素的填充

    java
    Integer[] num = new Integer[]{9, 3, 2};
    // 用99去填充num数组中的所有数,即用99将数组中的元素值全部进行替换
    Arrays.fill(num, 99);   // 填充后数组的元素值全部变成了99   {99, 99, 99}
  • equals:比较两个数组元素内容是否完全一致

    java
    Integer[] arr1 = {1, 2, 90, 123, 300};  
    Integer[] arr2 = {1, 2, 90, 123, 300};  
    boolean equals = Arrays.equals(arr1, arr2);   // true
  • asList:将一组值,转换成list

    java
    // 将(2, 3, 4)数据转成一个List集合
    List asList = Arrays.asList(2, 3, 4);
    // asList的编译类型是List(接口) 运行类型是java.util.Arrays#ArrayList(Arrays类中的静态内部类)

System

System类常见的方法:

  • exit:退出当前程序

    java
    System.out.println("ok1");
    System.exit(0);  // 退出程序  0表示正常退出
    System.out.println("ok2");  // 不会执行
  • arraycopy:复制数组元素,使用Arrays.copyOf完成复制数组的底层就是System.arraycopy()

    java
    int[] src = {1, 2, 3};
    int[] dest = new int[3];  // dest数组当前为 {0, 0, 0}
    System.arraycopy(src, 0, dest, 0, 3);  // {1, 2, 3}
    // 其中src表示源数组(待拷贝的数组,提供数据),0表示从源数组中的哪个索引位置开始拷贝
    // dest表示目标数组(数组拷贝到哪个数组中),0表示拷贝到目标数组的哪个索引(哪个索引开始拷贝),3表示从源数组拷贝多少个数据到目标数组(一般写成src.length)
    
    System.arraycopy(src, 0, dest, 1, 2);  // {0, 1, 2}
  • currentTimeMillens:返回当前时间距离1970-1-1的毫秒数

    java
    System.out.println(System.currentTimeMillens());
  • gc:运行垃圾回收机制


大数据类

BigInteger类和BigDecimal

BigIntegerBigDecimal两个类通常用于大数据的处理方案,应用场景:

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

    当我们编程中,需要处理很大的整数,这个时候可能long就不够用了,我们可以使用BigInteger类来处理

    java
    BigInteger bigInteger = new BigInteger("2322432645467845654654646535654656");
    System.out.println(bigInteger);  // 2322432645467845654654646535654656
    BigInteger bigInteger2 = new BigInteger("23");
    // 在对BigInteger进行加减乘除的时候,需要使用对应的方法,不能使用传统的+-*/
    // 加add()
    System.out.println(bigInteger.add(bigInteger2));
    // 减subtract()
    System.out.println(bigInteger.subtract(bigInteger2));
    // 乘multiply()
    System.out.println(bigInteger.multiply(bigInteger2));
    // 除divide()  这里的除是指整除
    System.out.println(bigInteger.divide(bigInteger2));
  • BigDecimal适合保存精度更高的浮点型(小数)

    在编程中,对于小数,如果位数特别大,double往往不够用,系统会进行精度的缩减,如果我们不希望精度的缩减,我们可以使用BigDecimal类保存精度更高的小数

    java
    BigDecimal bigDecimal = new BigDecimal("232243.2645467845654654646535654656");

    其加减乘除的运算和BigInteger类似,但是使用除法时,可能会出现除不尽的问题(会出现无限循环,从而抛出异常)如果我们不想让其抛出异常,我们只需要给定精度即可

    java
    // 如果有无限循环的小数,指定了BigDecimal.ROUND_CEILING精度,就会保留到分子的精度,不会再抛异常
    System.out.println(bigDecimal.divide(bigDecimal2, BigDecimal.ROUND_CEILING));

日期类

在编程过程中一般会涉及到日期,Java中有三种日期类,分别称为第一代、第二代和第三代

第一代日期类Date

第一代日期类Date是最早出现的日期类,可以精确到毫秒,代表特定的瞬间

Date类配套使用的有SimpleDateFormat:用于格式和解析日期的具体类,允许进行格式化(日期-->文本)、解析(文件-->日期)和规范化

Date类的继承关系:

image-20250418095930249

第一代日期类Date目前有些方法已经过时了,使用的时候需要注意

IDEA代码编译器的类图中,是可以展示类的属性,展示的属性不单单局限于我们声明的具体属性,对于getXxxsetXxx方法中的xxx内容,查看属性时,也会被放到类图中的属性中,当作该类的属性

基本使用

使用之前需要引入时间相关的包:import java.util.Date;

java
import java.util.Date;

public class Date01 {
    public static void main(String[] args) throws ParseException {
        // 获取当前的系统时间
		Date d1 = new Date();   // Thu Mar 04 17:19:30 CST 2025
        
        // 我们需要对日期的格式进行转换,可以指定我们需要的时间形式,注意,这里使用的字母是规定好的
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");
        String format = sdf.format(d1);   // 2025年03月04日 05:19:30 星期二
        
        // 通过指定的毫秒数来获取时间
        Date d2 = new Date(9234567);  // Thu Jan 01 10:33:54 CST 1970
        
        // 将一个格式化的字符串转换成对应的Date
        String s = "2025年03月04日 05:19:30 星期二";
        // 这里需要抛出异常,防止报错,这里使用的sdf格式需要和给定的字符串格式一样,否会抛出转换异常
        Date parse = sdf.parse(s);  // Thu Mar 04 17:19:30 CST 2025
    }
}

格式化时间的内容的特定字母常用的有:

y:年;M:月;w:年中的周数;W:月份中的周数;D:年中的天数;d:月份中的天数;F:月份中的星期;E:星期中的天数;aam/pm标记;H:一天中的小时数(0-23);k:一天中的小时数(1-24);Kam/pm中的小时数(0-11);ham/pm中的小时数(0-12);m:小时中的分钟数;s:分钟中的秒数;S:毫秒数;

第二代日期类Calendar

第二代日期类Calendar类(日历),Calendar类是一个抽象类,它为特定瞬间与一组如YEARMONTHDAY_OF_MONTHHOUR等日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法

Calendar类的继承关系:

image-20250418170914820

Calendar类是一个抽象类,其构造器是私有的,我们要获取Calendar类的实例,需要使用该类提供的getInstance()方法

基本使用

使用之前需要引入时间相关的包:import java.util.Calendar;

java
import java.util.Calendar;

public class Calendar01 {
    public static void main(String[] args) {
        // 获取一个容日历类的对象,不能使用new方法,因为构造器是私有的
        Calendar c = Calendar.getInstance();  // c输出的内容,将时间信息藏在了相应的字段中
        // 获取日历对象的某个日历字段
        c.get(Calendar.YEAR);   // 获取当前时间的年
        c.get(Calendar.MONTH) + 1;   // 获取当前时间的月,注意要加一,因为是按照0开始编号的
        c.get(Calendar.DAY_OF_MONTH);  // 获取当前时间的日
        // 如果要通过24小时的进制来获取时间,使用HOUR_OF_DAY
        c.get(Calendar.HOUR);  // 获取当前时间的小时
        c.get(Calendar.MINUTE);  // 获取当前时间的分钟
        c.get(Calendar.SECOND);  // 获取当前时间的秒
        // Calendar类没有提供对应的格式化的类,需要自己进行拼接组合,可以更加灵活化
        System.out.println(c.get(Calendar.YEAR)+"年" + (c.get(Calendar.MONTH) + 1)+"月");
    }
}

第三代日期类

对于第一代日期类,在jdk1.0就有了,但是它的大多数方法已经在jdk1.1时引入了Calender类之后就被弃用了,然而,Calender也存在一些问题:

  • 可变性问题:像日期和时间这样的类应该是不可变的,但是Calender类型是可变的
  • 偏移性:Date中的年份是从1900年开始的,而月份都是从0月开始的
  • 格式化:格式化只对Date有用,Calendar不能使用格式化
  • 此外,第一代和第二代时间类的线程都不是安全的,不能处理闰秒等(每隔两天,多出1秒)

针对于上述的情况,Java设计者在jdk8.0之后,引入了三个日期类:

  • LocalDate:日期类,返回年月日
  • LocalTime:时间类,返回时分秒
  • LocalDateTime:日期时间类。返回年月日和时分秒,用这个即可

第三代日期的格式日期类DateTimeFormatter,类似于第一代日期类中的格式日期类SimpleDateFormat

java
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(格式);
String str = dtf.format(日期对象);

时间戳Instant:类似于Date,提供了一系列和Date类转换的方式:

  • Instant--->DateDate date = Date.from(instant);
  • Date--->InstantInstant instant = date.toInstant();
java
// 通过静态方法now()  获取表示当前时间戳的对象
Instant instant = Instant.now();   // 2021-03-04T10:37:19.564Z
// 通过from可以把Instant对象转成Date对象
Date date = Date.from(instant);   
// 通过Date的toInstant() 又可以将Date对象重新转换成Instant对象
Instant instant = date.toInstant();
基本使用
java
import java.util.Calendar;

public class DateTime {
    public static void main(String[] args) {
        // 第三代日期
        // 使用now返回当前日期时间的对象
        LocalDateTime ldt = LocalDateTime.new();   // 2025-04-18T18:56:12.082
        ldt.getYear();  // 获取年
        ldt.getMonthValue();  // 获取月。返回的是数字,如3
        ldt.getMonth();   // 获取月,返回的是英文的月份,如MARCH
        ldt.getDayOfMonth();  // 获取日
        ldt.getHour();   // 获取时
        ldt.getMinute();    // 获取分
        ldt.getSecond();   // 获取秒
        
        // 使用DateTimeFormatter对象来进行格式化
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
        String str = dtf.format(ldt);   // 2025年04月18日 18时56分12秒
    }
}

第三代日期类还有许多的方法:(其他方法查看API手册)

  • 提供了plusminus方法,可以对当前时间进行加或减

    java
    LocalDateTime ldt = LocalDateTime.new();  // 获取当前时刻的年月日时分秒
    // 900天之后的年月日时分秒
    LocalDateTime localDateTime = ldt.plusDays(900);   // plusDays表示加天,也可以加年等
    // 300分钟前的年月日时分秒
    LocalDateTime localDateTime2 = ldt.minusMinutes(300);

Released under the MIT License.