🕕 Java 常见类

吞佛童子2022年10月10日
  • Java
  • 常见类
大约 13 分钟

🕕 Java 常见类

1. Object

package java.lang;

/**
 * Object 类为所有类的父类,所有对象(包括数组)都需要实现该方法
 */
public class Object {

    private static native void registerNatives();
    static {
        registerNatives();
    }

    /**
     * 返回该 Object 对象运行时的类,返回的类是 被表示类的静态方法锁定的 对象
     * 示例如下:无需进行强制转换
     *
     * <p>
     * {@code Number n = 0;                             }<br>
     * {@code Class<? extends Number> c = n.getClass(); }
     * </p>
     */
    public final native Class<?> getClass();

    /**
     * 返回对象的 Hash 值
     * 在存入与 Hash 值相关,例如 HashMap、HashSet 等时会用到该方法
     * 遵循如下协议规定:
     * 1. 在同一 Java 运行程序,同一对象每次调用该方法得到的 hash 值均相同
     * 2. 若两个对象满足 equals(),那么 hashCode() 结果也必相同
     * 3. 若俩个对象不满足 equals(),那么 hashCode() 结果也必不相同
     * 
     * 通常情况下,不同对象通过 hashCode() 得到的结果值不相同,一般通过将对象的 内部地址转换为 int 实现
     */
    public native int hashCode();

    /**
     * 重写该方法时,需要保证 上述协议:即
     * 若两个对象满足 equals(),那么 hashCode() 结果也必相同
     */
    public boolean equals(Object obj) {
        return (this == obj);
    }

    /**
     * 默认浅拷贝
     * 任何类要实现该方法,必须 implements Cloneable,否则会抛出异常
     */
    protected native Object clone() throws CloneNotSupportedException;

    /**
     * 返回字符串形式,建议所有子类实现该方法
     */
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

    /**
     * 唤醒任意一个正在等待该 对象锁 的线程
     */
    public final native void notify();

    /**
     * 唤醒所有正在等待该 对象锁 的线程
     */
    public final native void notifyAll();

    /**
     * 让当前线程等待,直到被其他线程 notify() | notifyAll() | 被其他线程中断 | 等待时间达到
     * 当前线程必须获取该对象的 monitor
     * 
     * 伪唤醒:线程不处于以上情况也可能恢复,因此有时需要加 while(),如下所示
     * <pre>
     *     synchronized (obj) {
     *         while (&lt;condition does not hold&gt;)
     *             obj.wait(timeout);
     *         ... // Perform action appropriate to condition
     *     }
     *
     * @param      timeout   the maximum time to wait in milliseconds.
     * @throws  IllegalArgumentException      超时时间 < 0 不合法
     * @throws  IllegalMonitorStateException  当前线程并没有获取 该对象锁
     * @throws  InterruptedException 线程被中断,会清空中断标志位
     */
    public final native void wait(long timeout) throws InterruptedException;

    public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException("nanosecond timeout value out of range");
        }

        if (nanos > 0) {
            timeout++;
        }

        wait(timeout);
    }

    public final void wait() throws InterruptedException {
        wait(0);
    }

    /**
     * JVM 垃圾回收相关
     * 在回收前,有一次逃脱的机会,该方法只能执行 一次
     */
    protected void finalize() throws Throwable { }
}

1) 为什么重写 equals() 时需要重写 hashCode() 方法

  • 当我们需要将 对象 添加到 HashSet | HashMapHash 相关数据结构时,
  • 首先会根据对象的 hashCode() 确定存入下标
  • 然后在该下标处,判断 equals() 添加是否满足,
    • 如果满足,则说明 两个对象相等,已经存在该对象
    • 若不满足,说明 两个对象不相等
  • 因此,若我们没有 重写 hashCode(),那么两个对象的 hashCode() 与对象地址有关,几乎不可能 hashCode() 相等
    • 那么,存入下标也不一定能够保证是相等的
    • 这样这两个对象,很大概率会放在不同下标的桶中,表示 两个对象 不相等
    • 而我们预期是,这两个对象 应该是相等的,放在同一数组下标,且在查找时,发现 equals() 满足条件
  • 因此,在此种情况下,我们首先要满足相同对象的 hashCode() 相等,然后才会判断 equals()
    • 只重写了 equals() 让两个对象相等,是无法满足条件的

2. 包装类

1) Integer

  • 缓存区间 [-128, 127]
  • 可通过 java.lang.Integer.IntegerCache.high 设置 右区间边界值
public final class Integer extends Number implements Comparable<Integer> {
    @Native public static final int   MIN_VALUE = 0x80000000;
    @Native public static final int   MAX_VALUE = 0x7fffffff;
    
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i); // 不存在于缓存中,新生成一个 包装类
    }
    
    // 缓存内部类
    private static class IntegerCache {
        static final int low = -128;
        static final int high; // 默认 = 127,也可以自定义大小
        static final Integer cache[]; // 缓存数组

        static {
            int h = 127;
            // 获取用户自定义的 high 值,如果存在的化,但是最终会取 max{自定义 high, 127}
            String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // high >= 127
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }
}

2) Byte

  • 缓存区间 [-128, 127]
  • 全部缓存
public final class Byte extends Number implements Comparable<Byte> {
    public static final byte   MIN_VALUE = -128;
    public static final byte   MAX_VALUE = 127;
    
    // 缓存数组存放所有 byte 值
    public static Byte valueOf(byte b) {
        final int offset = 128;
        return ByteCache.cache[(int)b + offset];
    }
    
    // 缓存内部类,无法自定义缓存区间
    private static class ByteCache {
        private ByteCache(){}

        static final Byte cache[] = new Byte[-(-128) + 127 + 1];

        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Byte((byte)(i - 128));
        }
    }
}

3) Boolean

  • 直接缓存的 TRUE & FALSE 两个变量,无所谓缓存不缓存
public final class Boolean implements java.io.Serializable, Comparable<Boolean> {
    public static final Boolean TRUE = new Boolean(true);
    public static final Boolean FALSE = new Boolean(false);
    
    // 每次返回的 都是一个 新的 Boolean 对象
    public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }
}

4) Character

  • 缓存区间 [0, 127]
  • 无法修改缓存区间大小
// Character 本身范围就是 非负数
public final class Character implements java.io.Serializable, Comparable<Character> {
    public static final char MIN_VALUE = '\u0000';
    public static final char MAX_VALUE = '\uFFFF';
    
    public static Character valueOf(char c) {
        if (c <= 127) { // 缓存范围 - [0, 127]
            return CharacterCache.cache[(int)c];
        }
        return new Character(c);
    }
    
    // 缓存类
    private static class CharacterCache {
        private CharacterCache(){}

        static final Character cache[] = new Character[127 + 1]; // [0, 127]

        static {
            for (int i = 0; i < cache.length; i++)
                cache[i] = new Character((char)i);
        }
    }
}

5) Short

  • 缓存区间 [-128, 127]
  • 无法修改缓存区间大小
public final class Short extends Number implements Comparable<Short> {
    public static final short   MIN_VALUE = -32768;
    public static final short   MAX_VALUE = 32767;
    
    public static Short valueOf(short s) {
        final int offset = 128;
        int sAsInt = s;
        if (sAsInt >= -128 && sAsInt <= 127) { // must cache
            return ShortCache.cache[sAsInt + offset];
        }
        return new Short(s);
    }

    private static class ShortCache { // [-128, 127]
        private ShortCache(){}

        static final Short cache[] = new Short[-(-128) + 127 + 1];

        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Short((short)(i - 128));
        }
    }
}

6) Float

  • 无缓存,直接返回新对象
    public static Float valueOf(float f) {
        return new Float(f); // 无缓存,直接返回新对象
    }

7) Double

  • 无缓存,直接返回新对象
    public static Double valueOf(double d) {
        return new Double(d); // 无缓存,直接返回新对象
    }

8) Long

  • 缓存区间 [-128, 127]
  • 无法修改缓存区间大小
    public static Long valueOf(long l) {
        final int offset = 128;
        if (l >= -128 && l <= 127) { // [-128, 127]
            return LongCache.cache[(int)l + offset];
        }
        return new Long(l);
    }
    
    // 缓存池 - [-128, 127]
    private static class LongCache {
        private LongCache(){}

        static final Long cache[] = new Long[-(-128) + 127 + 1];

        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Long(i - 128);
        }
    }

3. 字符串类

1) String

// final 修饰类 - 无法被继承
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
    // 存储 char[] 数组,final 修饰一旦初始化不可再更改
    // JDK 9 开始转换为 byte[] 存储,节省了存储空间
    private final char value[]; 
    private int hash; // 默认 hash 值,当为 0 时,hashCode() 时需要根据 val[] 元素重新计算
    
    // 重写 equals,依次比较每个元素
    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
    
    // 重写 hashCode(),保证 equals 成立条件下,hashCode 肯定相等
    public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i]; // 与每个元素有关,取决于元素个数 & 元素顺序
            }
            hash = h;
        }
        return h;
    }
    
    // 字符串池,最初为空,由类 {@code String} 私下维护。 
    // 当调用 intern 方法时,如果池中已经包含一个等于 {@link equals(Object)} 方法确定的此 {@code String} 对象的字符串,则返回池中的字符串。
    // 否则,将此 {@code String} 对象添加到池中,并返回对此 {@code String} 对象的引用。 
    // 因此对于任何两个字符串 s & t,当且仅当 s.equals(t) 满足条件时,s.intern() == t.intern() 
    public native String intern();
}

2) StringBuilder

public final class StringBuilder extends AbstractStringBuilder
                                  implements java.io.Serializable, CharSequence
{
    static final long serialVersionUID = 4383685877147921099L;

    /**
     * 默认创建长度为 16 的空数组
     */
    public StringBuilder() {
        super(16);
    }

    /**
     * 创建长度为 capacity 的空数组
     */
    public StringBuilder(int capacity) {
        super(capacity);
    }

    /**
     * 在指定字符串长度的基础上,再加上 16 的初始容量
     */
    public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }
    
    // 插入元素
    // 在插入过程中,若 新的总长度 > 数组已有长度,则扩容为 2n + 2 --> 来自抽象父类
    public StringBuilder append(StringBuffer sb) {
        super.append(sb);
        return this;
    }
    
    @Override
    public StringBuilder insert(int offset, Object obj) {
            super.insert(offset, obj);
            return this;
    }
    
    // 删除元素
    @Override
    public StringBuilder delete(int start, int end) { // 删除区间 [start, end) | [start, len)
        super.delete(start, end);
        return this;
    }

    @Override
    public StringBuilder deleteCharAt(int index) {
        super.deleteCharAt(index);
        return this;
    }
    
    // 查找元素
    @Override
    public int indexOf(String str, int fromIndex) {
        return super.indexOf(str, fromIndex);
    }
    
    // 反转字符串
    @Override
    public StringBuilder reverse() {
        super.reverse();
        return this;
    }
    
    // 并没有重写 equal & hashCode

3) StringBuffer

  • 方法同 StringBuilder
  • 所有方法加上 synchronized 关键字修饰

String 的 + 操作

  1. String 的 + 操作实际调用的是 StringBuilder.append().toString()
  2. 但是,在循环体中,不宜使用 + 拼接字符串
    • 每次循环都产生一个 StringBuilder 对象,有多少次循环就会产生多少个 StringBuilder 对象,可能造成 内存溢出

4. Arrays.sort()

  1. 针对 byte char shortlen > 29 | len > 3200 时,优先选择 计数排序
    • 计数排序,遍历数组获得 min & max,建立对应长度的数组
    • 再次遍历,每个数组下标处记录元素的出现次数
    • 最后遍历,填充结果集
    • 由于 以上三种数据类型 值的取值范围较小,因此可以通过建立这么长的数组,且 len 越大,每个下标处频次越高,效果越好
  2. 针对所有基本数据类型len < 47 时,优先选择 插排
  3. 针对所有基本数据类型len < 286 时,优先选择 快排
  4. 其余情况,针对所有基本数据类型 & 对象类型,使用 归并排序,算法稳定
  5. 针对所有基本数据类型,采用的是 DualPivotQuicksort 类;针对 对象类型,使用的是 ComparableTimSort 类的算法
final class DualPivotQuicksort {

    private DualPivotQuicksort() {} // 私有构造函数,防止实例化

    private static final int MAX_RUN_COUNT = 67; // 归并排序最大允许次数
    private static final int MAX_RUN_LENGTH = 33; // 归并排序中运行的最大长度

    // 如果要排序的数组的长度小于 286,则优先使用快速排序而不是归并排序
    private static final int QUICKSORT_THRESHOLD = 286; 
    // 如果要排序的数组的长度小于 47,则使用插入排序优先于快速排序
    private static final int INSERTION_SORT_THRESHOLD = 47; 
    
    // 如果要排序的字节数组的长度大于 29,则优先使用计数排序而不是插入排序
    private static final int COUNTING_SORT_THRESHOLD_FOR_BYTE = 29; 
    // 如果要排序的 short | char 数组的长度大于 3200,则优先使用计数排序而不是快速排序
    private static final int COUNTING_SORT_THRESHOLD_FOR_SHORT_OR_CHAR = 3200; 
}

5. 异常类

1) 类图

img.png

2) Throwable

public class Throwable implements Serializable {
    // 返回详细信息
    public String getMessage() {
        return detailMessage;
    }
    
    //
    public String toString() {
        String s = getClass().getName();
        String message = getLocalizedMessage();
        return (message != null) ? (s + ": " + message) : s;
    }
    
    //
    public void printStackTrace() {
        printStackTrace(System.err);
    }
}

3) try-catch-finally

① 若 finally 中存在 return,则会直接返回 finally 中的结果

  • 如果在 finally 中存在 return 语句,那么 try-catch 中的 return 值都会被覆盖
    • 如果有返回值,会把 try 中 return 的变量当前值 保存到 局部变量表 中,压入操作数栈
    • 然后跳转到 finally 中继续执行
    • 执行完 finally 语句后,会返回之前保存在 局部变量表 里的值
public static void main(String[] args) throws FileNotFoundException {
    System.out.println("执行结果:" + test());
}

private static int test() {
    int num = 0;
    try {
        num++; // num = 1
        return num;
    } catch (Exception e) {
        e.getMessage();
    } finally {
        num++; // num = 2
        return num; // 返回 2
    }
}

② finally 中的代码不执行

  • 在 try-catch 语句中执行了 System.exit(0)
  • 在 try-catch 语句中出现了死循环
  • 在 finally 执行之前掉电或者 JVM 崩溃了

6. BigDecimal

上次编辑于: 2022/10/10 下午8:43:48
贡献者: liuxianzhishou