第二章 Java语言基础

第二章 Java语言基础

2.1 八大基本数据类型
2.1.1 byte、short、int、long、float、double、boolean、char

对应包装类

Byte、Short、Integer、Long、Float、Double、Boolean、Character

2.1.2 自动装箱和自动拆箱机制 (jdk 1.5开始引入)
复制代码
Integer obj = 100; // 自动装箱 - int 到 Integer 的转换

Integer obj = new Integer(100); int num = obj; // 自动拆箱 - Integer 到 int 的转换
2.1.3 包装类研究
2.1.3.1 探索Serializable接口(支持序列化)
复制代码
public interface Serializable {  
}

为什么实现 Serializable 接口就支持序列化?

  • 标记接口:Serializable 是一个标记接口(Marker Interface),它不包含任何方法声明或常量。它的唯一目的是告诉Java虚拟机(JVM)这个类的对象是可以被序列化的。当一个类实现了 Serializable 接口,就意味着该类及其所有非瞬态(non-transient)和非静态(non-static)字段都可以被序列化。
  • 默认序列化机制:Java提供了默认的序列化机制。当你使用 ObjectOutputStream 来序列化一个对象时,如果该对象所属的类实现了 Serializable 接口,那么JVM会自动调用默认的序列化逻辑来处理对象的所有可序列化字段。同样地,ObjectInputStream 可以用来反序列化对象。
  • 控制序列化过程:虽然Java提供了默认的序列化机制,但你也可以通过实现特定的方法来定制序列化过程:
    • private void writeObject(ObjectOutputStream out) throws IOException: 允许自定义序列化过程。
    • private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException: 允许自定义反序列化过程。
    • readResolve() 和 writeReplace(): 这些方法允许你在序列化和反序列化过程中替换对象。
  • 安全性与版本控制:通过实现 Serializable 接口并提供一个 serialVersionUID 字段,你可以更好地控制类的版本管理。如果你没有显式地定义 serialVersionUID,Java会根据类的各种属性生成一个默认的UID。如果类的结构发生变化而没有更新 serialVersionUID,则可能导致反序列化失败。

demo1

复制代码
package com.test;  
  
import lombok.Data;  
  
import java.io.*;  
  
@Data  // lombok工具,使用该注解生成setter和getter方法
public class demo implements Serializable {  
    private static final long serialVersionUID = 1L; // 建议显式定义  
  
    private String name;  
    private int age;  
    private String sex;  
  
    @Override  
    public String toString() {  
        return "demo{" +  
                "name='" + name + '\'' +  
                ", age=" + age +  
                ", sex='" + sex + '\'' +  
                '}';  
    }  
  
    public static void main(String[] args) {  
        demo d = new demo();  
        d.setName("张三");  
        d.setAge(18);  
        d.setSex("男");  
  
        // 序列化  
        try {  
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/demo.txt"));  
            oos.writeObject(d);  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
  
        // 反序列化  
        try {  
            demo d2 = (demo) new ObjectInputStream(new FileInputStream("d:/demo.txt")).readObject();  
            System.out.println(d2);  
        } catch (IOException | ClassNotFoundException e) {  
            e.printStackTrace();  
        }  
    }  
}

运行结果


demo2

复制代码
package com.test;  
  
import lombok.Data;  
import java.io.*;  
  
@Data  
public class Demo implements Serializable {  
    private static final long serialVersionUID = 1L; // 建议显式定义  
  
    private String name;  
    private int age;  
    private String sex;  
  
    @Override  
    public String toString() {  
        return "Demo{" +  
                "name='" + name + '\'' +  
                ", age=" + age +  
                ", sex='" + sex + '\'' +  
                '}';  
    }  
  
    private void writeObject(ObjectOutputStream oos) throws IOException {  
        // 加密 name 字段后序列化  
        if (this.name != null) {  
            this.name = encrypt(this.name);  
        }  
  
        // 使用默认方式序列化所有非 transient 字段  
        oos.defaultWriteObject();  
  
    }  
  
    // 模拟加密函数  
    private String encrypt(String str) {  
        return new StringBuilder(str).reverse().toString(); // 简单反转字符串作为加密  
    }  
  
    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {  
        // 使用默认方式反序列化所有非 transient 字段  
        ois.defaultReadObject();  
        // 反序列化并解密 name 字段  
  
        if (!this.getName().isEmpty()) {  
            this.name = decrypt(this.getName());  
        } else {  
            this.name = null;  
        }  
    }  
  
    // 模拟解密函数  
    private String decrypt(String str) {  
        return new StringBuilder(str).reverse().toString(); // 同样反转还原  
    }  
  
    public static void main(String[] args) {  
        ObjectOutputStream oos = null;  
        ObjectInputStream ois = null;  
        try {  
            oos = new ObjectOutputStream(new FileOutputStream("demo.txt"));  
            Demo demo = new Demo();  
            demo.setName("张三");  
            demo.setAge(18);  
            demo.setSex("男");  
            // 序列化  
            oos.writeObject(demo);  
  
            // 反序列化  
            ois = new ObjectInputStream(new FileInputStream("demo.txt"));  
            Demo demo1 = (Demo) ois.readObject();  
            System.out.println(demo1);  
        } catch (Exception e) {  
            e.printStackTrace();  
        } finally {  
            try {  
                if (oos != null) {  
                    oos.close();  
                }  
                if (ois != null) {  
                    ois.close();  
                }  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
}

运行结果

总结: 在进行序列化与反序列化时,我们可以自定义一些加密解密逻辑

当执行 oos.writeObject(demo);

会去调用private void writeObject(ObjectOutputStream oos) throws IOException这个方法

当执行 Demo demo1 = (Demo) ois.readObject();

会去调用private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException这个方法

2.1.3.2 探索Number抽象类
复制代码
package java.lang;
public abstract class Number implements java.io.Serializable { // 抽象类
	public abstract int intValue();
	public abstract long longValue();
	public abstract float floatValue();
	public abstract double doubleValue();
	public byte byteValue() {  
	    return (byte)intValue();  
	}
	public short shortValue() {  
	    return (short)intValue();  
	}
	private static final long serialVersionUID = -8742448824652078965L;
}

Number 抽象类是各种数值类型的超类。这是一种适配器模式。

为什么 Number 的方法不全部设置为默认实现,而是部分需要改成抽象方法?

复制代码
因为 每个子类内部存储数据的方式不同,无法用统一的方式来实现这些转换方法。
 例如:
 public final class Double extends Number implements Comparable<Double>{
	...
	private final double value;
	public int intValue() {  
	    return (int)value;  
	}
 }
 public final class Integer extends Number implements Comparable<Integer>{
	...
	private final int value;
	public int intValue() {  
		return value;  
	}
 }
 对于Double和Integer类,这两个类均继承了 Number 类,但是两者底层存储数据的数据类型并不一致,在计算机中的存储结构也不一致。故不能用统一的方式来实现这些转换方法。
2.1.3.3 探索Comparable接口
复制代码
package java.lang;  
import java.util.*;
public interface Comparable<T> {
	public int compareTo(T o);  
}

该接口比较简单,但很常用。通过泛型来实现其通用性。其他类可以通过实现该接口并重写方法来实现比较逻辑。可以规范返回值的含义。比如返回 负数 表示小于,返回 0 表示等于,返回 正数 表示大于

2.1.3.4 探索Byte类

源码

复制代码
package java.lang;
public final class Byte extends Number implements Comparable<Byte> {
	public static final byte   MIN_VALUE = -128;
	public static final byte   MAX_VALUE = 127;
	public static final Class<Byte>     TYPE = (Class<Byte>) Class.getPrimitiveClass("byte");
	public static String toString(byte b) {  
		return Integer.toString((int)b, 10);  
	}
	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));  
	    }  
	}
	public static Byte valueOf(byte b) {  
	    final int offset = 128;  
	    return ByteCache.cache[(int)b + offset];  
	}
	public static byte parseByte(String s, int radix)  
	    throws NumberFormatException {  
	    int i = Integer.parseInt(s, radix);  
	    if (i < MIN_VALUE || i > MAX_VALUE)  
	        throw new NumberFormatException(  
	            "Value out of range. Value:\"" + s + "\" Radix:" + radix);  
	    return (byte)i;  
	}
	public static byte parseByte(String s) throws NumberFormatException {  
	    return parseByte(s, 10);  
	}
	public static Byte valueOf(String s, int radix)  
	    throws NumberFormatException {  
	    return valueOf(parseByte(s, radix));  
	}
	public static Byte valueOf(String s) throws NumberFormatException {  
	    return valueOf(s, 10);  
	}
	public static Byte decode(String nm) throws NumberFormatException {  
	    int i = Integer.decode(nm);  
	    if (i < MIN_VALUE || i > MAX_VALUE)  
	        throw new NumberFormatException(  
	                "Value " + i + " out of range from input " + nm);  
	    return valueOf((byte)i);  
	}
	private final byte value;
	public Byte(byte value) {  
	    this.value = value;  
	}
	public Byte(String s) throws NumberFormatException {  
	    this.value = parseByte(s, 10);  
	}
	public byte byteValue() {  
	    return value;  
	}
	public short shortValue() {  
	    return (short)value;  
	}
	public int intValue() {  
	    return (int)value;  
	}
	public long longValue() {  
	    return (long)value;  
	}
	public float floatValue() {  
	    return (float)value;  
	}
	public double doubleValue() {  
	    return (double)value;  
	}
	public String toString() {  
	    return Integer.toString((int)value);  
	}
	public int hashCode() {  
	    return Byte.hashCode(value);  
	}
	public static int hashCode(byte value) {  
	    return (int)value;  
	}
	public boolean equals(Object obj) {  
	    if (obj instanceof Byte) {  
	        return value == ((Byte)obj).byteValue();  
	    }  
	    return false;  
	}
	public int compareTo(Byte anotherByte) {  
	    return compare(this.value, anotherByte.value);  
	}
	public static int compare(byte x, byte y) {  
	    return x - y;  
	}
	public static int toUnsignedInt(byte x) {  
	    return ((int) x) & 0xff;  
	}
	public static long toUnsignedLong(byte x) {  
	    return ((long) x) & 0xffL;  
	}
	public static final int SIZE = 8;
	public static final int BYTES = SIZE / Byte.SIZE;
	private static final long serialVersionUID = -7183698231559129828L;
}

代码分析

public final class Byte extends Number implements Comparable<Byte>

类定义表明

  • 该类不是抽象类且不能被继承
  • 继承了Number抽象类,必须重写对应的抽象方法
  • 实现了Comparable接口,必须重写里面的compareTo方法

public static final byte MIN_VALUE = -128; // 最小值,不可变,也就是 -2^7

public static final byte MAX_VALUE = 127; // 最大值,不可变,也就是 -1 + 2^7


public static final Class<Byte> TYPE = (Class<Byte>) Class.getPrimitiveClass("byte");
Byte.TYPE 是一个表示原始类型 byte 的 Class 对象

验证: System.out.println(Byte.TYPE == byte.class); // 输出: true


public static String toString(byte b) {

return Integer.toString((int)b, 10);

}

利用Integer类的toString方法进行字符串转换


private static class ByteCache { // 私有静态内部类

private ByteCache(){} // 构造函数私有化,这里将这个类当做工具类处理

复制代码
static final Byte cache[] = new Byte[-(-128) + 127 + 1];  // 缓存 -128 ~ +127 这些数的Byte对象

static {  // 利用静态代码块初始化
    for(int i = 0; i < cache.length; i++)  
        cache[i] = new Byte((byte)(i - 128));  
}  

}


public static Byte valueOf(byte b) {

final int offset = 128;

return ByteCache.cache[(int)b + offset];

}

返回对应的缓存对象


public static byte parseByte(String s, int radix)

throws NumberFormatException {

int i = Integer.parseInt(s, radix);

if (i < MIN_VALUE || i > MAX_VALUE)

throw new NumberFormatException(

"Value out of range. Value:"" + s + "" Radix:" + radix);

return (byte)i;

}

使用Integer类的parseInt方法获取值

判断值是否在byte类型的最大值和最小值之间

不是则抛异常

是则返回转换后的数值


public static byte parseByte(String s) throws NumberFormatException {

return parseByte(s, 10);

}

这个方法是上一个方法的简化版,默认10进制转换


public static Byte valueOf(String s, int radix)

throws NumberFormatException {

return valueOf(parseByte(s, radix));

}

public static Byte valueOf(String s) throws NumberFormatException {

return valueOf(s, 10);

}

使用valueOf的好处就是不用重复创建Byte对象,直接使用了缓存对象


public static Byte decode(String nm) throws NumberFormatException {

int i = Integer.decode(nm);

if (i < MIN_VALUE || i > MAX_VALUE)

throw new NumberFormatException(

"Value " + i + " out of range from input " + nm);

return valueOf((byte)i);

}

这个方法的作用是将字符串数字转化为byte

先通过Integer类的decode方法转换

如果数值不在byte范围内,抛出异常

否则返回缓存对象


private final byte value; // 维护的byte数据

public Byte(byte value) {

this.value = value;

}

public Byte(String s) throws NumberFormatException {

this.value = parseByte(s, 10);

}

两种构造方式。不过推荐使用valueOf方法来获取缓存对象。

在现在的编译器优化下,执行 Byte a = 10;默认走的就是valueOf方法获取缓存对象。


public int hashCode() { // 重写超级父类Object的hashCode方法

return Byte.hashCode(value);

}

public static int hashCode(byte value) {

return (int)value;

}
这里说明我们用Byte对象获取其hashCode时,就是它自身的值。


public boolean equals(Object obj) { // 重写超级父类Object的equals方法

if (obj instanceof Byte) {

return value == ((Byte)obj).byteValue();

}

return false;

}

使用instance关键字进行类型判断,类型匹配则判断值是否相等

类型不匹配直接返回false


public byte byteValue() {

return value;

}

public short shortValue() {

return (short)value;

}

public int intValue() {

return (int)value;

}

public long longValue() {

return (long)value;

}

public float floatValue() {

return (float)value;

}

public double doubleValue() {

return (double)value;

}

这里的方法是重写Number类的方法,直接返回维护的byte类型的value的强转结果


public int compareTo(Byte anotherByte) { // 重写 Comparable接口的方法

return compare(this.value, anotherByte.value);

}

public static int compare(byte x, byte y) {

return x - y;

}

返回负数表示小于

返回 0 表示等于

返回正数表示大于


public static int toUnsignedInt(byte x) {

return ((int) x) & 0xff;

}

public static long toUnsignedLong(byte x) {

return ((long) x) & 0xffL;

}

利用位运算将byte转为无符号int类型和无符号long类型


public static final int SIZE = 8; // 比特数

public static final int BYTES = SIZE / Byte.SIZE; // 字节数

private static final long serialVersionUID = -7183698231559129828L; // 序列号

2.1.3.5 探索Short类

源码

复制代码
package java.lang;
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 final Class<Short>    TYPE = (Class<Short>) Class.getPrimitiveClass("short");
	public static String toString(short s) {  
	    return Integer.toString((int)s, 10);  
	}
	public static short parseShort(String s, int radix)  
	    throws NumberFormatException {  
	    int i = Integer.parseInt(s, radix);  
	    if (i < MIN_VALUE || i > MAX_VALUE)  
	        throw new NumberFormatException(  
	            "Value out of range. Value:\"" + s + "\" Radix:" + radix);  
	    return (short)i;  
	}
	public static short parseShort(String s) throws NumberFormatException {  
	    return parseShort(s, 10);  
	}
	public static Short valueOf(String s, int radix)  
	    throws NumberFormatException {  
	    return valueOf(parseShort(s, radix));  
	}
	public static Short valueOf(String s) throws NumberFormatException {  
	    return valueOf(s, 10);  
	}  
	  
	private static class ShortCache {  
	    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));  
	    }  
	}
	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);  
	}
	public static Short decode(String nm) throws NumberFormatException {  
	    int i = Integer.decode(nm);  
	    if (i < MIN_VALUE || i > MAX_VALUE)  
	        throw new NumberFormatException(  
	                "Value " + i + " out of range from input " + nm);  
	    return valueOf((short)i);  
	}
	private final short value;
	public Short(short value) {  
	    this.value = value;  
	}
	public Short(String s) throws NumberFormatException {  
	    this.value = parseShort(s, 10);  
	}
	public byte byteValue() {  
	    return (byte)value;  
	}
	public short shortValue() {  
	    return value;  
	}
	public int intValue() {  
	    return (int)value;  
	}
	public long longValue() {  
	    return (long)value;  
	}
	public float floatValue() {  
	    return (float)value;  
	}
	public double doubleValue() {  
	    return (double)value;  
	}
	public String toString() {  
	    return Integer.toString((int)value);  
	}
	public int hashCode() {  
	    return Short.hashCode(value);  
	}
	public static int hashCode(short value) {  
	    return (int)value;  
	}
	public boolean equals(Object obj) {  
	    if (obj instanceof Short) {  
	        return value == ((Short)obj).shortValue();  
	    }  
	    return false;  
	}
	public int compareTo(Short anotherShort) {  
	    return compare(this.value, anotherShort.value);  
	}
	public static int compare(short x, short y) {  
	    return x - y;  
	}
	public static final int SIZE = 16;
	public static final int BYTES = SIZE / Byte.SIZE;
	public static short reverseBytes(short i) {  
	    return (short) (((i & 0xFF00) >> 8) | (i << 8));  
	}
	public static int toUnsignedInt(short x) {  
	    return ((int) x) & 0xffff;  
	}
	public static long toUnsignedLong(short x) {  
	    return ((long) x) & 0xffffL;  
	}
	private static final long serialVersionUID = 7515723908773894738L;
}

代码分析

public final class Short extends Number implements Comparable<Short>

类定义表明

  • 该类不是抽象类且不能被继承
  • 继承了Number抽象类,必须重写对应的抽象方法
  • 实现了Comparable接口,必须重写里面的compareTo方法

public static final short MIN_VALUE = -32768; // 最小值,不可变,也就是 -2^15

public static final short MAX_VALUE = 32767; // 最大值,不可变,也就是 -1 + 2^15


public static final Class<Short> TYPE = (Class<Short>) Class.getPrimitiveClass("short");
Short.TYPE 是一个表示原始类型 short 的 Class 对象

验证: System.out.println(Short.TYPE == short.class); // 输出: true


public static String toString(short s) {

return Integer.toString((int)s, 10);

}

使用Integer类的toString方法


public static short parseShort(String s, int radix)

throws NumberFormatException {

int i = Integer.parseInt(s, radix);

if (i < MIN_VALUE || i > MAX_VALUE)

throw new NumberFormatException(

"Value out of range. Value:"" + s + "" Radix:" + radix);

return (short)i;

}

public static short parseShort(String s) throws NumberFormatException {

return parseShort(s, 10);

}

先将字符串转为int

判断是否在范围内,不在抛出异常

在则返回值


public static Short valueOf(String s, int radix)

throws NumberFormatException {

return valueOf(parseShort(s, radix));

}

public static Short valueOf(String s) throws NumberFormatException {

return valueOf(s, 10);

}

本质上还是利用Integer类的进制转换功能


private static class ShortCache { // 静态内部类

private ShortCache(){} // 构造函数私有化,此处看做工具类

复制代码
static final Short cache[] = new Short[-(-128) + 127 + 1];  // -128 ~ + 127 的值对应的Short对象

static {  // 利用静态代码块初始化
    for(int i = 0; i < cache.length; i++)  
        cache[i] = new Short((short)(i - 128));  
}  

}


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);

}

如果在范围内,直接返回缓存对象

否则返回新对象


public static Short decode(String nm) throws NumberFormatException {

int i = Integer.decode(nm);

if (i < MIN_VALUE || i > MAX_VALUE)

throw new NumberFormatException(

"Value " + i + " out of range from input " + nm);

return valueOf((short)i);

}

利用Integer类的decode方法将字符串转换


private final short value; // 维护的short类型数据

public Short(short value) {

this.value = value;

}

public Short(String s) throws NumberFormatException {

this.value = parseShort(s, 10);

}

两种构造方式


public byte byteValue() {

return (byte)value;

}

public short shortValue() {

return value;

}

public int intValue() {

return (int)value;

}

public long longValue() {

return (long)value;

}

public float floatValue() {

return (float)value;

}

public double doubleValue() {

return (double)value;

}

重写的Number类的方法


public String toString() {

return Integer.toString((int)value);

}

利用Integer类的toString方法


public int hashCode() {

return Short.hashCode(value);

}

public static int hashCode(short value) {

return (int)value;

}

重写Object类的hashCode方法

直接返回维护的value值


public boolean equals(Object obj) {

if (obj instanceof Short) {

return value == ((Short)obj).shortValue();

}

return false;

}

重写Object类的equals方法

先判断类型是否匹配,匹配判断维护的short值是否相等

不匹配直接返回false


public int compareTo(Short anotherShort) {

return compare(this.value, anotherShort.value);

}

public static int compare(short x, short y) {

return x - y;

}

重写了Comparable接口的compareTo方法


public static final int SIZE = 16; // 比特数

public static final int BYTES = SIZE / Byte.SIZE; // 字节数

public static short reverseBytes(short i) { // 高八位和第八位互换

return (short) (((i & 0xFF00) >> 8) | (i << 8));

}

public static int toUnsignedInt(short x) { // 转化为 unsignedInt

return ((int) x) & 0xffff;

}

public static long toUnsignedLong(short x) { // 转化为unsignedLong

return ((long) x) & 0xffffL;

}

private static final long serialVersionUID = 7515723908773894738L; // 序列化号

2.1.3.6 探索Integer类

源码

复制代码
package java.lang;  
  
import java.lang.annotation.Native;
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 final Class<Integer>  TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
	final static char[] digits = {  
	    '0' , '1' , '2' , '3' , '4' , '5' ,  
	    '6' , '7' , '8' , '9' , 'a' , 'b' ,  
	    'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,  
	    'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,  
	    'o' , 'p' , 'q' , 'r' , 's' , 't' ,  
	    'u' , 'v' , 'w' , 'x' , 'y' , 'z'  
	};
	public static String toString(int i, int radix) {  
	    if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)  
	        radix = 10;  
	  
	    /* Use the faster version */  
	    if (radix == 10) {  
	        return toString(i);  
	    }  
	  
	    char buf[] = new char[33];  
	    boolean negative = (i < 0);  
	    int charPos = 32;  
	  
	    if (!negative) {  
	        i = -i;  
	    }  
	  
	    while (i <= -radix) {  
	        buf[charPos--] = digits[-(i % radix)];  
	        i = i / radix;  
	    }  
	    buf[charPos] = digits[-i];  
	  
	    if (negative) {  
	        buf[--charPos] = '-';  
	    }  
	  
	    return new String(buf, charPos, (33 - charPos));  
	}
	public static String toUnsignedString(int i, int radix) {  
	    return Long.toUnsignedString(toUnsignedLong(i), radix);  
	}
	static void getChars(int i, int index, char[] buf) {  
	    int q, r;  
	    int charPos = index;  
	    char sign = 0;  
	  
	    if (i < 0) {  
	        sign = '-';  
	        i = -i;  
	    }  
	  
	    // Generate two digits per iteration  
	    while (i >= 65536) {  
	        q = i / 100;  
	    // really: r = i - (q * 100);  
	        r = i - ((q << 6) + (q << 5) + (q << 2));  
	        i = q;  
	        buf [--charPos] = DigitOnes[r];  
	        buf [--charPos] = DigitTens[r];  
	    }  
	  
	    // Fall thru to fast mode for smaller numbers  
	    // assert(i <= 65536, i);    for (;;) {  
	        q = (i * 52429) >>> (16+3);  
	        r = i - ((q << 3) + (q << 1));  // r = i-(q*10) ...  
	        buf [--charPos] = digits [r];  
	        i = q;  
	        if (i == 0) break;  
	    }  
	    if (sign != 0) {  
	        buf [--charPos] = sign;  
	    }  
	}
	final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,  
                                  99999999, 999999999, Integer.MAX_VALUE };
    static int stringSize(int x) {  
	    for (int i=0; ; i++)  
	        if (x <= sizeTable[i])  
	            return i+1;  
	}
	public static int parseInt(String s, int radix)  
            throws NumberFormatException  
	{  
	    /*  
	     * WARNING: This method may be invoked early during VM initialization     
	     * before IntegerCache is initialized. Care must be taken to not use     
	     * the valueOf method.     
	     */  
	    if (s == null) {  
	        throw new NumberFormatException("null");  
	    }  
	  
	    if (radix < Character.MIN_RADIX) {  
	        throw new NumberFormatException("radix " + radix +  
	                                        " less than Character.MIN_RADIX");  
	    }  
	  
	    if (radix > Character.MAX_RADIX) {  
	        throw new NumberFormatException("radix " + radix +  
	                                        " greater than Character.MAX_RADIX");  
	    }  
	  
	    int result = 0;  
	    boolean negative = false;  
	    int i = 0, len = s.length();  
	    int limit = -Integer.MAX_VALUE;  
	    int multmin;  
	    int digit;  
	  
	    if (len > 0) {  
	        char firstChar = s.charAt(0);  
	        if (firstChar < '0') { // Possible leading "+" or "-"  
	            if (firstChar == '-') {  
	                negative = true;  
	                limit = Integer.MIN_VALUE;  
	            } else if (firstChar != '+')  
	                throw NumberFormatException.forInputString(s);  
	  
	            if (len == 1) // Cannot have lone "+" or "-"  
	                throw NumberFormatException.forInputString(s);  
	            i++;  
	        }  
	        multmin = limit / radix;  
	        while (i < len) {  
	            // Accumulating negatively avoids surprises near MAX_VALUE  
	            digit = Character.digit(s.charAt(i++),radix);  
	            if (digit < 0) {  
	                throw NumberFormatException.forInputString(s);  
	            }  
	            if (result < multmin) {  
	                throw NumberFormatException.forInputString(s);  
	            }  
	            result *= radix;  
	            if (result < limit + digit) {  
	                throw NumberFormatException.forInputString(s);  
	            }  
	            result -= digit;  
	        }  
	    } else {  
	        throw NumberFormatException.forInputString(s);  
	    }  
	    return negative ? result : -result;  
	}
	public static int parseInt(String s) throws NumberFormatException {  
	    return parseInt(s,10);  
	}
	public static int parseUnsignedInt(String s, int radix)  
            throws NumberFormatException {  
	    if (s == null)  {  
	        throw new NumberFormatException("null");  
	    }  
	  
	    int len = s.length();  
	    if (len > 0) {  
	        char firstChar = s.charAt(0);  
	        if (firstChar == '-') {  
	            throw new  
	                NumberFormatException(String.format("Illegal leading minus sign " +  
	                                                   "on unsigned string %s.", s));  
	        } else {  
	            if (len <= 5 || // Integer.MAX_VALUE in Character.MAX_RADIX is 6 digits  
	                (radix == 10 && len <= 9) ) { // Integer.MAX_VALUE in base 10 is 10 digits  
	                return parseInt(s, radix);  
	            } else {  
	                long ell = Long.parseLong(s, radix);  
	                if ((ell & 0xffff_ffff_0000_0000L) == 0) {  
	                    return (int) ell;  
	                } else {  
	                    throw new  
	                        NumberFormatException(String.format("String value %s exceeds " +  
	                                                            "range of unsigned int.", s));  
	                }  
	            }  
	        }  
	    } else {  
	        throw NumberFormatException.forInputString(s);  
	    }  
	}
	public static int parseUnsignedInt(String s) throws NumberFormatException {  
	    return parseUnsignedInt(s, 10);  
	}
	public static Integer valueOf(String s, int radix) throws NumberFormatException {  
	    return Integer.valueOf(parseInt(s,radix));  
	}
	public static Integer valueOf(String s) throws NumberFormatException {  
	    return Integer.valueOf(parseInt(s, 10));  
	}
	private static class IntegerCache {  
	    static final int low = -128;  
	    static final int high;  
	    static final Integer cache[];  
	  
	    static {  
	        // high value may be configured by property  
	        int h = 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);  
	                // Maximum array size is Integer.MAX_VALUE  
	                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++);  
	  
	        // range [-128, 127] must be interned (JLS7 5.1.7)  
	        assert IntegerCache.high >= 127;  
	    }  
	  
	    private IntegerCache() {}  
	}
	public static Integer valueOf(int i) {  
	    if (i >= IntegerCache.low && i <= IntegerCache.high)  
	        return IntegerCache.cache[i + (-IntegerCache.low)];  
	    return new Integer(i);  
	}
	private final int value;
	public Integer(int value) {  
	    this.value = value;  
	}
	public Integer(String s) throws NumberFormatException {  
	    this.value = parseInt(s, 10);  
	}
	public byte byteValue() {  
	    return (byte)value;  
	}
	public short shortValue() {  
	    return (short)value;  
	}
	public int intValue() {  
	    return value;  
	}
	public long longValue() {  
	    return (long)value;  
	}
	public float floatValue() {  
	    return (float)value;  
	}
	public double doubleValue() {  
	    return (double)value;  
	}
	public String toString() {  
	    return toString(value);  
	}
	public int hashCode() {  
	    return Integer.hashCode(value);  
	}
	public static int hashCode(int value) {  
	    return value;  
	}
	public boolean equals(Object obj) {  
	    if (obj instanceof Integer) {  
	        return value == ((Integer)obj).intValue();  
	    }  
	    return false;  
	}
	public static Integer getInteger(String nm) {  
	    return getInteger(nm, null);  
	}
	public static Integer getInteger(String nm, int val) {  
	    Integer result = getInteger(nm, null);  
	    return (result == null) ? Integer.valueOf(val) : result;  
	}
	public static Integer getInteger(String nm, Integer val) {  
	    String v = null;  
	    try {  
	        v = System.getProperty(nm);  
	    } catch (IllegalArgumentException | NullPointerException e) {  
	    }  
	    if (v != null) {  
	        try {  
	            return Integer.decode(v);  
	        } catch (NumberFormatException e) {  
	        }  
	    }  
	    return val;  
	}
	public static Integer decode(String nm) throws NumberFormatException {  
	    int radix = 10;  
	    int index = 0;  
	    boolean negative = false;  
	    Integer result;  
	  
	    if (nm.length() == 0)  
	        throw new NumberFormatException("Zero length string");  
	    char firstChar = nm.charAt(0);  
	    // Handle sign, if present  
	    if (firstChar == '-') {  
	        negative = true;  
	        index++;  
	    } else if (firstChar == '+')  
	        index++;  
	  
	    // Handle radix specifier, if present  
	    if (nm.startsWith("0x", index) || nm.startsWith("0X", index)) {  
	        index += 2;  
	        radix = 16;  
	    }  
	    else if (nm.startsWith("#", index)) {  
	        index ++;  
	        radix = 16;  
	    }  
	    else if (nm.startsWith("0", index) && nm.length() > 1 + index) {  
	        index ++;  
	        radix = 8;  
	    }  
	  
	    if (nm.startsWith("-", index) || nm.startsWith("+", index))  
	        throw new NumberFormatException("Sign character in wrong position");  
	  
	    try {  
	        result = Integer.valueOf(nm.substring(index), radix);  
	        result = negative ? Integer.valueOf(-result.intValue()) : result;  
	    } catch (NumberFormatException e) {  
	        // If number is Integer.MIN_VALUE, we'll end up here. The next line  
	        // handles this case, and causes any genuine format error to be        // rethrown.        String constant = negative ? ("-" + nm.substring(index))  
	                                   : nm.substring(index);  
	        result = Integer.valueOf(constant, radix);  
	    }  
	    return result;  
	}
	public int compareTo(Integer anotherInteger) {  
	    return compare(this.value, anotherInteger.value);  
	}
	public static int compare(int x, int y) {  
	    return (x < y) ? -1 : ((x == y) ? 0 : 1);  
	}
	public static int compareUnsigned(int x, int y) {  
	    return compare(x + MIN_VALUE, y + MIN_VALUE);  
	}
	public static long toUnsignedLong(int x) {  
	    return ((long) x) & 0xffffffffL;  
	}
	public static int divideUnsigned(int dividend, int divisor) {  
	    // In lieu of tricky code, for now just use long arithmetic.  
	    return (int)(toUnsignedLong(dividend) / toUnsignedLong(divisor));  
	}
	public static int remainderUnsigned(int dividend, int divisor) {  
	    // In lieu of tricky code, for now just use long arithmetic.  
	    return (int)(toUnsignedLong(dividend) % toUnsignedLong(divisor));  
	}
	@Native public static final int SIZE = 32;
	public static final int BYTES = SIZE / Byte.SIZE;
	public static int highestOneBit(int i) {  
	    // HD, Figure 3-1  
	    i |= (i >>  1);  
	    i |= (i >>  2);  
	    i |= (i >>  4);  
	    i |= (i >>  8);  
	    i |= (i >> 16);  
	    return i - (i >>> 1);  
	}
	public static int lowestOneBit(int i) {  
	    // HD, Section 2-1  
	    return i & -i;  
	}
	public static int numberOfLeadingZeros(int i) {  
	    // HD, Figure 5-6  
	    if (i == 0)  
	        return 32;  
	    int n = 1;  
	    if (i >>> 16 == 0) { n += 16; i <<= 16; }  
	    if (i >>> 24 == 0) { n +=  8; i <<=  8; }  
	    if (i >>> 28 == 0) { n +=  4; i <<=  4; }  
	    if (i >>> 30 == 0) { n +=  2; i <<=  2; }  
	    n -= i >>> 31;  
	    return n;  
	}
	public static int numberOfTrailingZeros(int i) {  
	    // HD, Figure 5-14  
	    int y;  
	    if (i == 0) return 32;  
	    int n = 31;  
	    y = i <<16; if (y != 0) { n = n -16; i = y; }  
	    y = i << 8; if (y != 0) { n = n - 8; i = y; }  
	    y = i << 4; if (y != 0) { n = n - 4; i = y; }  
	    y = i << 2; if (y != 0) { n = n - 2; i = y; }  
	    return n - ((i << 1) >>> 31);  
	}
	public static int bitCount(int i) {  
	    // HD, Figure 5-2  
	    i = i - ((i >>> 1) & 0x55555555);  
	    i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);  
	    i = (i + (i >>> 4)) & 0x0f0f0f0f;  
	    i = i + (i >>> 8);  
	    i = i + (i >>> 16);  
	    return i & 0x3f;  
	}
	public static int rotateLeft(int i, int distance) {  
	    return (i << distance) | (i >>> -distance);  
	}
	public static int rotateRight(int i, int distance) {  
	    return (i >>> distance) | (i << -distance);  
	}
	public static int reverse(int i) {  
	    // HD, Figure 7-1  
	    i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555;  
	    i = (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333;  
	    i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f;  
	    i = (i << 24) | ((i & 0xff00) << 8) |  
	        ((i >>> 8) & 0xff00) | (i >>> 24);  
	    return i;  
	}
	public static int signum(int i) {  
	    // HD, Section 2-7  
	    return (i >> 31) | (-i >>> 31);  
	}
	public static int reverseBytes(int i) {  
	    return ((i >>> 24)           ) |  
	           ((i >>   8) &   0xFF00) |  
	           ((i <<   8) & 0xFF0000) |  
	           ((i << 24));  
	}
	public static int sum(int a, int b) {  
	    return a + b;  
	}
	public static int max(int a, int b) {  
	    return Math.max(a, b);  
	}
	public static int min(int a, int b) {  
	    return Math.min(a, b);  
	}
	@Native private static final long serialVersionUID = 1360826667806852920L;
}

代码分析

public final class Integer extends Number implements Comparable<Integer>

类定义表明

  • 该类不是抽象类且不能被继承
  • 继承了Number抽象类,必须重写对应的抽象方法
  • 实现了Comparable接口,必须重写里面的compareTo方法

public static final int MIN_VALUE = 0x80000000; // 最小值,不可变,也就是 -2^31

public static final int MAX_VALUE = 0x7fffffff; // 最大值,不可变,也就是 -1 + 2^31


public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
Integer.TYPE 是一个表示原始类型 int 的 Class 对象

验证: System.out.println(Integer.TYPE == int.class); // 输出: true


final static char[] digits = {

'0' , '1' , '2' , '3' , '4' , '5' ,

'6' , '7' , '8' , '9' , 'a' , 'b' ,

'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,

'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,

'o' , 'p' , 'q' , 'r' , 's' , 't' ,

'u' , 'v' , 'w' , 'x' , 'y' , 'z'

}

这里其实就是每一位可以使用的数,不同的进制数组范围不同,比如10进制的每一个数位就是digits[0] ~ digits[9]中的一个字符。16进制的每一个数位就是digits[0] ~ digits[15]中的1个字符。

所以从这里可以看出,Integer类进行进制转换,最多支持36进制。因为只有'0' ~ '9' 和 'a' ~ 'z' 一共36个字符表示。


public static String toString(int i, int radix) {

// 范围判断,判断进制数是否在 2~36之间

if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)

radix = 10;

复制代码
/* Use the faster version */  
if (radix == 10) {  
    return toString(i);  // 进制为10时,使用了一种快速转换方法
}  

char buf[] = new char[33]; // 最多33,一个符号位 + int类型完整32位数,不一定全部会用到
boolean negative = (i < 0);  // 判断非负性
int charPos = 32;  // 下标,32~0 对应 低位 ~ 高位

if (!negative) {  // 将数字转为负数统一处理
    i = -i;  
}  

while (i <= -radix) {  // 使用短除法进行进制转换
    buf[charPos--] = digits[-(i % radix)];  // 将余数查表显示字符
    i = i / radix;  
}  
buf[charPos] = digits[-i];  // 最高位处理

if (negative) {  // 带上标志位
    buf[--charPos] = '-';  
}  

return new String(buf, charPos, (33 - charPos));  // 根据实际长度去构造String,而不是完整33位

}

这个方法就是进制转换,但进制为10进制时,提供了一个高效率的方法

public static String toString(int i) { // 进制为10时的toString方法

// 最小值特殊处理,直接返回,因为最小值的相反数比最大值大1,不处理会溢出

if (i == Integer.MIN_VALUE)

return "-2147483648";

int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); // 通过stringSize快速获取数字位数

char[] buf = new char[size];

getChars(i, size, buf); // 核心高效方法

return new String(buf, true);

}

final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,

99999999, 999999999, Integer.MAX_VALUE };

// Requires positive x

static int stringSize(int x) {

for (int i=0; ; i++) // 直接根据数值在哪一个区间返回对应的长度

if (x <= sizeTable[i])

return i+1;

}

final static char [] DigitTens = {

'0', '0', '0', '0', '0', '0', '0', '0', '0', '0',

'1', '1', '1', '1', '1', '1', '1', '1', '1', '1',

'2', '2', '2', '2', '2', '2', '2', '2', '2', '2',

'3', '3', '3', '3', '3', '3', '3', '3', '3', '3',

'4', '4', '4', '4', '4', '4', '4', '4', '4', '4',

'5', '5', '5', '5', '5', '5', '5', '5', '5', '5',

'6', '6', '6', '6', '6', '6', '6', '6', '6', '6',

'7', '7', '7', '7', '7', '7', '7', '7', '7', '7',

'8', '8', '8', '8', '8', '8', '8', '8', '8', '8',

'9', '9', '9', '9', '9', '9', '9', '9', '9', '9',

} ;

final static char [] DigitOnes = {

'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',

'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',

'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',

'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',

'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',

'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',

'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',

'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',

'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',

'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',

} ;

static void getChars(int i, int index, char[] buf) {

int q, r; // 商和余数

int charPos = index;

char sign = 0;

复制代码
if (i < 0) {  // 判断符号位
    sign = '-';  
    i = -i;  
}  

// Generate two digits per iteration  
while (i >= 65536) {  // 这个方法快在一次可以确定两位
    q = i / 100;  // 商
// really: r = i - (q * 100);  
    // 相当于 i - q * 64 - q * 32 - q * 4 ,也就是 i - q * 100
    r = i - ((q << 6) + (q << 5) + (q << 2));  // 余数
    i = q;  // 更新数值
    buf [--charPos] = DigitOnes[r];  // 查个位表填充
    buf [--charPos] = DigitTens[r];  // 查十位表填充
}  

// Fall thru to fast mode for smaller numbers  
// assert(i <= 65536, i);    
for (;;) {  
    q = (i * 52429) >>> (16+3);  // 这里相当于 i / 10,使用位运算快一些
    r = i - ((q << 3) + (q << 1));  // r = i-(q*10) ...  
    buf [--charPos] = digits [r];  // 已经知道是10进制了,直接查 digits表
    i = q;  // 更新数值
    if (i == 0) break;  // 数值为 0 退出
}  
if (sign != 0) {  // 判断是否为负数
    buf [--charPos] = sign;  
}  

}


public static String toUnsignedString(int i, int radix) {

return Long.toUnsignedString(toUnsignedLong(i), radix);

}

利用Long类的方法进行转换


public static String toHexString(int i) {

return toUnsignedString0(i, 4);

}

这里的4代表4个比特位,因为是转16进制,4个比特位代表 1个16进制位

public static String toOctalString(int i) {

return toUnsignedString0(i, 3);

}

这里的3代表3个比特位,因为是转8进制,3个比特位代表 1个8进制位

public static String toBinaryString(int i) {

return toUnsignedString0(i, 1);

}

这里的1代表1个比特位,因为是转2进制,1个比特位代表 1个2进制位


private static String toUnsignedString0(int val, int shift) {

// assert shift > 0 && shift <=5 : "Illegal shift value";

int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val); // 减去前导0的个数

// 计算分组显示个数,比如16进制,shift=4,假设有7个前导0,那chars=7,只需要7个字符显示,因为一个字符代表4位。

int chars = Math.max(((mag + (shift - 1)) / shift), 1);

char[] buf = new char[chars];

复制代码
formatUnsignedInt(val, shift, buf, 0, chars);  // 填充buf数组

// Use special constructor which takes over "buf".  
return new String(buf, true);  

}

// 这个方法用于判断一个数的二进制位的前导0的个数

public static int numberOfLeadingZeros(int i) {

// HD, Figure 5-6

if (i == 0) // 0 那就是32位全是0,直接返回32

return 32;

int n = 1; // 假设最高位是0,不是0的话后面会减一

if (i >>> 16 == 0) { n += 16; i <<= 16; } // 判断高16位是否为0,然后将低16位代替高16位

if (i >>> 24 == 0) { n += 8; i <<= 8; } //判断高8位是否为0,然后将高16位的低8位代替高8位

if (i >>> 28 == 0) { n += 4; i <<= 4; } // 判断高4位是否为0,然后将高8位的低4位代替高4位

if (i >>> 30 == 0) { n += 2; i <<= 2; } // 判断高2位是否为0,然后将高4位的低2位代替高2位

n -= i >>> 31; // 减去最高位,如果前面这个数最高位是1,那if一个都不会中。

return n; // 返回前导0的个数

}

static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {

int charPos = len;

int radix = 1 << shift;

int mask = radix - 1; // 获取掩码

do {

buf[offset + --charPos] = Integer.digits[val & mask]; // 查表

val >>>= shift; // 右移,这样可以控制几个比特位显示一个字符

} while (val != 0 && charPos > 0);

复制代码
return charPos;  

}


// 这个方法的作用是将字符串 s 看做 radix 进制数,然后转换为10进制数返回

public static int parseInt(String s, int radix)

throws NumberFormatException

{

/*

* WARNING: This method may be invoked early during VM initialization

* before IntegerCache is initialized. Care must be taken to not use

* the valueOf method.

*/

if (s == null) {

throw new NumberFormatException("null");

}

复制代码
if (radix < Character.MIN_RADIX) {  
    throw new NumberFormatException("radix " + radix +  
                                    " less than Character.MIN_RADIX");  
}  

if (radix > Character.MAX_RADIX) {  
    throw new NumberFormatException("radix " + radix +  
                                    " greater than Character.MAX_RADIX");  
}  
// 前面是一些合法性判断

int result = 0;  // 存放结果
boolean negative = false;  // 默认非负
int i = 0, len = s.length();  
int limit = -Integer.MAX_VALUE; // 下限,默认条件下防止正数溢出  
int multmin;  // 辅助下限
int digit;  // 每一个数位值

if (len > 0) {  
    char firstChar = s.charAt(0);  
    if (firstChar < '0') { // Possible leading "+" or "-"  
        if (firstChar == '-') {  
            negative = true;  // 更改状态
            limit = Integer.MIN_VALUE;  // 更改下限
        } else if (firstChar != '+')  
            throw NumberFormatException.forInputString(s);  

        if (len == 1) // Cannot have lone "+" or "-"  
            throw NumberFormatException.forInputString(s);  
        i++;  
    }  
    multmin = limit / radix;  
    while (i < len) {  
        // Accumulating negatively avoids surprises near MAX_VALUE  
        digit = Character.digit(s.charAt(i++),radix);  // 获取指定位置数位值
        if (digit < 0) {  // 合法性判断
            throw NumberFormatException.forInputString(s);  
        }  
        if (result < multmin) {  // 溢出判断,如果小于multmin,再左移一次会溢出
            throw NumberFormatException.forInputString(s);  
        }  
        result *= radix;  // 左移
        if (result < limit + digit) {  // 判断左移后的值是否能继续累加
            throw NumberFormatException.forInputString(s);  
        }  
        result -= digit;  // 负数累加
    }  
} else {  
    throw NumberFormatException.forInputString(s);  
}  
return negative ? result : -result;  // 根据正负性返回数值

}

public static int parseInt(String s) throws NumberFormatException {

return parseInt(s,10); // 使用上述方法,默认把 s 的内容当做10进制

}


public static int parseUnsignedInt(String s, int radix)

throws NumberFormatException {

if (s == null) {

throw new NumberFormatException("null");

}

复制代码
int len = s.length();  
if (len > 0) {  
    char firstChar = s.charAt(0);  
    if (firstChar == '-') {  // 无符号数没有符号
        throw new  
            NumberFormatException(String.format("Illegal leading minus sign " +  
                                               "on unsigned string %s.", s));  
    } else {  
        // len <=5 是因为在最大进制36时,36^5 - 1< Integer.MAX_VALUE, 而 36^6 -1 > Integer.MAX_VALUE,如果len > 5会造成溢出
        if (len <= 5 || // Integer.MAX_VALUE in Character.MAX_RADIX is 6 digits  
            (radix == 10 && len <= 9) ) { // Integer.MAX_VALUE in base 10 is 10 digits  
            return parseInt(s, radix);  // 使用parseInt转换
        } else {  // 数据可能超出Integer的最大值,只能用Long类的parseLong方法
            long ell = Long.parseLong(s, radix);  
            if ((ell & 0xffff_ffff_0000_0000L) == 0) {  
                return (int) ell;  
            } else {  
                throw new  
                    NumberFormatException(String.format("String value %s exceeds " +  
                                                        "range of unsigned int.", s));  
            }  
        }  
    }  
} else {  
    throw NumberFormatException.forInputString(s);  
}  

}

public static int parseUnsignedInt(String s) throws NumberFormatException {

return parseUnsignedInt(s, 10); // 直接调用上述方法

}


public static Integer valueOf(String s, int radix) throws NumberFormatException {

return Integer.valueOf(parseInt(s,radix));

}

public static Integer valueOf(String s) throws NumberFormatException {

return Integer.valueOf(parseInt(s, 10));

}

这两个方法都是直接调用已有的方法


private static class IntegerCache {

static final int low = -128; // 缓存最小值

static final int high; // 最大值不确定,但至少为127

static final Integer cache[]; // 缓存数组

复制代码
static {  
    // high value may be configured by property  
    int h = 127;  // 理解为high的默认值
    String integerCacheHighPropValue =  
        sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");  
    if (integerCacheHighPropValue != null) {  // 判断用户是否手动配置最大值
        try {  
            int i = parseInt(integerCacheHighPropValue);  // 解析
            i = Math.max(i, 127);  // 取最大值
            // Maximum array size is Integer.MAX_VALUE  
            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++);  

    // range [-128, 127] must be interned (JLS7 5.1.7)  
    assert IntegerCache.high >= 127;  
}  

private IntegerCache() {}  // 构造函数私有化

}

这里可以配置虚拟机选项 -Djava.lang.Integer.IntegerCache.high

例如配置 -Djava.lang.Integer.IntegerCache.high=256

Integer a = 128;

Integer b = 128;

System.out.println(a == b); // true,如果没配置返回的是false


public static Integer valueOf(int i) {

if (i >= IntegerCache.low && i <= IntegerCache.high)

return IntegerCache.cache[i + (-IntegerCache.low)];

return new Integer(i);

}

如果值在范围内,返回对应值的缓存对象

否则返回新对象


private final int value; // 维护的int类型数据

public Integer(int value) {

this.value = value;

}

public Integer(String s) throws NumberFormatException {

this.value = parseInt(s, 10);

}

两种构造方式


public byte byteValue() {

return (byte)value;

}

public short shortValue() {

return (short)value;

}

public int intValue() {

return value;

}

public long longValue() {

return (long)value;

}

public float floatValue() {

return (float)value;

}

public double doubleValue() {

return (double)value;

}

重写Number类的方法


public String toString() {

return toString(value);

}

直接调的前面的toString方法


public int hashCode() {

return Integer.hashCode(value);

}

public static int hashCode(int value) {

return value;

}

重写Object类的hashCode方法


public boolean equals(Object obj) {

if (obj instanceof Integer) { // 判断是否是Integer实例

return value == ((Integer)obj).intValue(); //判断与维护的值是否相等

}

return false;

}

重写Object类的equals方法


public static Integer getInteger(String nm) { // 根据系统属性值获取对象

return getInteger(nm, null);

}

public static Integer getInteger(String nm, int val) { // 根据系统属性值获取对象,不存在则使用指定值的对象

Integer result = getInteger(nm, null);

return (result == null) ? Integer.valueOf(val) : result;

}

public static Integer getInteger(String nm, Integer val) { // 根据系统属性值获取对象,不存在则使用指定对象

String v = null;

try {

v = System.getProperty(nm); // 获取系统属性

} catch (IllegalArgumentException | NullPointerException e) {

}

if (v != null) {

try {

return Integer.decode(v); // 返回结果

} catch (NumberFormatException e) {

}

}

return val; // 返回指定对象

}


public static Integer decode(String nm) throws NumberFormatException {

int radix = 10; // 默认10进制

int index = 0;

boolean negative = false; // 默认非负

Integer result; // 结果

复制代码
if (nm.length() == 0)  
    throw new NumberFormatException("Zero length string");  
char firstChar = nm.charAt(0);  
// Handle sign, if present  
if (firstChar == '-') {  
    negative = true;  
    index++;  
} else if (firstChar == '+')  
    index++;  

// Handle radix specifier, if present  
if (nm.startsWith("0x", index) || nm.startsWith("0X", index)) {  // 16进制
    index += 2;  
    radix = 16;  
}  
else if (nm.startsWith("#", index)) {  // 16进制
    index ++;  
    radix = 16;  
}  
else if (nm.startsWith("0", index) && nm.length() > 1 + index) {  // 8进制
    index ++;  
    radix = 8;  
}  

if (nm.startsWith("-", index) || nm.startsWith("+", index))  
    throw new NumberFormatException("Sign character in wrong position");  

// 后面都是调用现有方法
try {  
    result = Integer.valueOf(nm.substring(index), radix);  
    result = negative ? Integer.valueOf(-result.intValue()) : result;  
} catch (NumberFormatException e) {  
    // If number is Integer.MIN_VALUE, we'll end up here. The next line  
    // handles this case, and causes any genuine format error to be        // rethrown.        
    String constant = negative ? ("-" + nm.substring(index))  
                               : nm.substring(index);  
    result = Integer.valueOf(constant, radix);  
}  
return result;  

}


public int compareTo(Integer anotherInteger) { // 重写Comparable接口方法

return compare(this.value, anotherInteger.value);

}

public static int compare(int x, int y) {

return (x < y) ? -1 : ((x == y) ? 0 : 1);

}

public static int compareUnsigned(int x, int y) {

return compare(x + MIN_VALUE, y + MIN_VALUE);

}


public static long toUnsignedLong(int x) {

return ((long) x) & 0xffffffffL; // 位运算

}
为啥还要进行与运算?

假设我们有一个 int x = -1;,其二进制是:

11111111 11111111 11111111 11111111 (32 个 1)

当你把它强制转成 long:

11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 (64 位)

所以我们用与运算保留低32位

00000000 00000000 00000000 00000000 11111111 11111111 11111111 11111111


public static int divideUnsigned(int dividend, int divisor) {

// In lieu of tricky code, for now just use long arithmetic.

return (int)(toUnsignedLong(dividend) / toUnsignedLong(divisor));

}

public static int remainderUnsigned(int dividend, int divisor) {

// In lieu of tricky code, for now just use long arithmetic.

return (int)(toUnsignedLong(dividend) % toUnsignedLong(divisor));

}

直接调上述方法


@Native public static final int SIZE = 32; // 比特数

public static final int BYTES = SIZE / Byte.SIZE; // 字节数


// 这是 获取一个整数最高位为 1 的那一位(其余位为 0)的一种高效算法

// 这个方法通过一系列位操作,将输入 int i 中所有比最高位 1 小的位都设置为 1,然后通过减法快速提取出那个最高位的 1。

public static int highestOneBit(int i) {

// HD, Figure 3-1

i |= (i >> 1);

i |= (i >> 2);

i |= (i >> 4);

i |= (i >> 8);

i |= (i >> 16);

return i - (i >>> 1);

}

// Integer.lowestOneBit(i) 通过 i & -i 快速提取出 i 的二进制中最右边的 1,是一个经典的位运算技巧,效率高、用途广。

public static int lowestOneBit(int i) {

// HD, Section 2-1

return i & -i;

}


// 这个方法用来快速计算一个 int 类型整数 二进制表示中最右边的 1 后面有多少个连续的 0。

public static int numberOfTrailingZeros(int i) {

// HD, Figure 5-14

int y;

if (i == 0) return 32;

int n = 31;

y = i <<16; if (y != 0) { n = n -16; i = y; }

y = i << 8; if (y != 0) { n = n - 8; i = y; }

y = i << 4; if (y != 0) { n = n - 4; i = y; }

y = i << 2; if (y != 0) { n = n - 2; i = y; }

return n - ((i << 1) >>> 31);

}


// 这个方法用于 统计一个 int 类型整数中二进制表示中有多少个 1。

public static int bitCount(int i) {

// HD, Figure 5-2

i = i - ((i >>> 1) & 0x55555555);

i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);

i = (i + (i >>> 4)) & 0x0f0f0f0f;

i = i + (i >>> 8);

i = i + (i >>> 16);

return i & 0x3f;

}


// 用于实现 32 位整数的循环左移(rotate left)。

public static int rotateLeft(int i, int distance) {

return (i << distance) | (i >>> -distance);

}

// 用于实现 32 位整数的循环右移(rotate left)。

public static int rotateRight(int i, int distance) {

return (i >>> distance) | (i << -distance);

}


// Integer.reverse(i) 使用了一种高效的"分治+位掩码"算法,通过多轮局部交换,最终完成整个 32 位整数的二进制位反转,是一种典型的高性能位运算技巧。

public static int reverse(int i) {

// HD, Figure 7-1

i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555;

i = (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333;

i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f;

i = (i << 24) | ((i & 0xff00) << 8) |

((i >>> 8) & 0xff00) | (i >>> 24);

return i;

}


public static int signum(int i) {

// HD, Section 2-7

return (i >> 31) | (-i >>> 31);

}

返回 -1,如果输入的整数 i 是负数;

返回 0,如果输入的整数 i 是零;

返回 1,如果输入的整数 i 是正数。


// Integer.reverseBytes(i) 使用位运算技巧,将一个 int 类型的四个字节顺序完全反转,适用于网络传输、文件格式转换、底层协议解析等场景。

public static int reverseBytes(int i) {

return ((i >>> 24) ) |

((i >> 8) & 0xFF00) |

((i << 8) & 0xFF0000) |

((i << 24));

}


public static int sum(int a, int b) { // 和

return a + b;

}

public static int max(int a, int b) { // 最大值

return Math.max(a, b);

}

public static int min(int a, int b) { // 最小值

return Math.min(a, b);

}


@Native private static final long serialVersionUID = 1360826667806852920L; // 序列号

2.1.3.7 探索Long类

public static final Class<Long> TYPE = (Class<Long>) Class.getPrimitiveClass("long");
Long.TYPE 是一个表示原始类型 long 的 Class 对象

验证: System.out.println(Long.TYPE == long.class); // 输出: true

Long类和Integer类的方法实现基本差不多,简单总结一下

总结

  • 维护的数据不同,Long维护long, Integer 维护 int
  • LongCache是对 -128 ~ + 127的值的对象进行预分配,IntegerCache默认也是,但可以通过
    -Djava.lang.Integer.IntegerCache.high 进行扩展
  • 在将long值转无符号时,会用BigInteger这个类进行处理
2.1.3.8 探索Float、Double类

对于单精度浮点数(32位),格式为

虽然指数字段有 8 位(理论上可以表示 0~255),但由于 0 和 255 是保留值,不能用于规范化数,所以实际用于规范化数的指数字段只能是 1~254,对应的实际指数范围就是 -126 ~ +127。

public static final Class<Float> TYPE = (Class<Float>) Class.getPrimitiveClass("float");
Float.TYPE 是一个表示原始类型 float 的 Class 对象

验证: System.out.println(Float.TYPE == float.class); // 输出: true


对于双精度浮点数(64位),格式为

虽然指数字段有 11 位(理论上可以表示 0~2047),但由于 0 和 2047 是保留值,不能用于规范化数,所以实际用于规范化数的指数字段只能是 1~2046,对应的实际指数范围就是 -1022 ~ +1023。

public static final Class<Double> TYPE = (Class<Double>) Class.getPrimitiveClass("double");
Double.TYPE 是一个表示原始类型 double 的 Class 对象

验证: System.out.println(Double.TYPE == double.class); // 输出: true


注意

  • Float.NaN != Float.NaN
  • Double.NaN != Double.NaN
  • 判断两个浮点数是否相等时,最好不要用 == ,而是使用一个较小的数对比差值
2.1.3.9 探索Boolean类

源码

复制代码
package java.lang;
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);
	public static final Class<Boolean> TYPE = (Class<Boolean>) Class.getPrimitiveClass("boolean");
	private final boolean value;
	private static final long serialVersionUID = -3665804199014368530L;
	public Boolean(boolean value) {  
	    this.value = value;  
	}
	public Boolean(String s) {  
	    this(parseBoolean(s));  
	}
	public static boolean parseBoolean(String s) {  
	    return ((s != null) && s.equalsIgnoreCase("true"));  
	}
	public boolean booleanValue() {  
	    return value;  
	}
	public static Boolean valueOf(boolean b) {  
	    return (b ? TRUE : FALSE);  
	}
	public static Boolean valueOf(String s) {  
	    return parseBoolean(s) ? TRUE : FALSE;  
	}
	public static String toString(boolean b) {  
	    return b ? "true" : "false";  
	}
	public String toString() {  
	    return value ? "true" : "false";  
	}
	public int hashCode() {  
	    return Boolean.hashCode(value);  
	}
	public static int hashCode(boolean value) {  
	    return value ? 1231 : 1237;  
	}
	public boolean equals(Object obj) {  
	    if (obj instanceof Boolean) {  
	        return value == ((Boolean)obj).booleanValue();  
	    }  
	    return false;  
	}
	public static boolean getBoolean(String name) {  
	    boolean result = false;  
	    try {  
	        result = parseBoolean(System.getProperty(name));  
	    } catch (IllegalArgumentException | NullPointerException e) {  
	    }  
	    return result;  
	}
	public int compareTo(Boolean b) {  
	    return compare(this.value, b.value);  
	}
	public static int compare(boolean x, boolean y) {  
	    return (x == y) ? 0 : (x ? 1 : -1);  
	}
	public static boolean logicalAnd(boolean a, boolean b) {  
	    return a && b;  
	}
	public static boolean logicalOr(boolean a, boolean b) {  
	    return a || b;  
	}
	public static boolean logicalXor(boolean a, boolean b) {  
	    return a ^ b;  
	}
}

代码分析

public final class Boolean implements java.io.Serializable, Comparable<Boolean>

由于Boolean并不属于数值类型,所以Boolean并没有继承Number类,而是直接实现java.io.Serializable接口。其实也可以联想,因为Boolean和Number的其他类型不能进行转换

语法层面就被限定了。

public static final Boolean TRUE = new Boolean(true);

public static final Boolean FALSE = new Boolean(false);

这两个对象被经常复用,比如valueOf方法等

public static final Class<Boolean> TYPE = (Class<Boolean>) Class.getPrimitiveClass("boolean");
Boolean.TYPE 是一个表示原始类型 boolean 的 Class 对象

验证: System.out.println(Boolean.TYPE == boolean.class); // 输出: true


private final boolean value; // 维护的boolean类型数据

private static final long serialVersionUID = -3665804199014368530L; // 序列号

public Boolean(boolean value) {

this.value = value;

}

public Boolean(String s) {

this(parseBoolean(s));

}

public static boolean parseBoolean(String s) {

return ((s != null) && s.equalsIgnoreCase("true")); // 忽略大小写

}

两种构造方式


public boolean booleanValue() {

return value; // 返回维护值

}

public static Boolean valueOf(boolean b) {

return (b ? TRUE : FALSE); // 返回静态对象

}

public static Boolean valueOf(String s) {

return parseBoolean(s) ? TRUE : FALSE; // 返回静态对象

}


public static String toString(boolean b) {

return b ? "true" : "false";

}

public String toString() {

return value ? "true" : "false";

}

一目了然


public int hashCode() { // 重写的Object类的方法

return Boolean.hashCode(value);

}

public static int hashCode(boolean value) {

return value ? 1231 : 1237; // 返回的是固定值,不看的话心里会有各种猜测,其实很简单

}

public boolean equals(Object obj) { // 重写的Object类的方法

if (obj instanceof Boolean) {

return value == ((Boolean)obj).booleanValue();

}

return false;

}


public static boolean getBoolean(String name) { // 通过系统属性获取值

boolean result = false;

try {

result = parseBoolean(System.getProperty(name));

} catch (IllegalArgumentException | NullPointerException e) {

}

return result;

}


public int compareTo(Boolean b) {

return compare(this.value, b.value);

}

public static int compare(boolean x, boolean y) {

return (x == y) ? 0 : (x ? 1 : -1);

}

Boolean的比较我个人基本没用过


public static boolean logicalAnd(boolean a, boolean b) { // 逻辑与

return a && b;

}

public static boolean logicalOr(boolean a, boolean b) { // 逻辑或

return a || b;

}

public static boolean logicalXor(boolean a, boolean b) { // 逻辑异或

return a ^ b;

}

2.1.3.10 探索Character类

这个类的内容有点多,这里只展示了常用部分。

public final class Character implements java.io.Serializable, Comparable<Character>

这个类维护的是字符类型,所以没继承Number类,直接实现序列化接口

public static final int MIN_RADIX = 2; // 可转换的最小进制

public static final int MAX_RADIX = 36;// 可转换的最大进制

在前面的Integer类的进制转换中用到了这两个常量

public static final char MIN_VALUE = '\u0000';

public static final char MAX_VALUE = '\uFFFF';

这里值得一说的是 java中的char是两个字节,而 c/c++中的char 是一个字节

public static final Class<Character> TYPE = (Class<Character>) Class.getPrimitiveClass("char");
Character.TYPE 是一个表示原始类型 char 的 Class 对象

验证: System.out.println(Character.TYPE == char.class); // 输出: true

private final char value; // 维护的char数据

private static final long serialVersionUID = 3786198910865385080L; // 序列化号


private static class CharacterCache {

private CharacterCache(){}

复制代码
static final Character cache[] = new Character[127 + 1];  

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

}
其实是为Ascii码表的128个字符提供了缓存对象

public static Character valueOf(char c) {

if (c <= 127) { // must cache

return CharacterCache.cache[(int)c];

}

return new Character©;

}

编写 Character a = 'a'; 时,执行的就是这个valueOf方法


判断字符类型

  • isLetter(char ch):判断指定字符是否为字母。
  • isDigit(char ch):判断指定字符是否为数字。
  • isWhitespace(char ch):判断指定字符是否为空白字符。
  • isUpperCase(char ch):判断指定字符是否为大写字母。
  • isLowerCase(char ch):判断指定字符是否为小写字母。
    转换字符大小写
  • toUpperCase(char ch):将字符转换为大写形式。
  • toLowerCase(char ch):将字符转换为小写形式。
    获取字符分类
  • getType(char ch):返回指定字符的Unicode类别。每个类别由一个整数表示,例如Character.UPPERCASE_LETTER代表大写字母。
    检查字符属性
  • isJavaIdentifierStart(char ch):判断字符是否可以作为Java标识符的起始字符。
  • isJavaIdentifierPart(char ch):判断字符是否可以作为Java标识符的一部分(除了第一个字符之外)。
    比较字符
  • compare(char x, char y):比较两个字符的数值顺序,类似于Integer.compare()。
2.2 流程控制(小面经)
2.2.1 小面经问题:switch 能作用在哪些数据类型上?

Java 5 之前,switch语句的表达式只能是byte、short、char和int类型。这是因为在早期版本中,switch语句的设计主要是为了处理整数类型的值。

Java 5 开始,Java引入了枚举类型(enum),并且允许switch语句的表达式为枚举类型。这大大增强了switch语句的灵活性,使其能够处理更复杂的逻辑。

Java 7 开始,switch语句的表达式还可以是字符串类型(String)。这是一个重要的增强,使得switch语句可以用于基于字符串值的分支选择。

2.2.2 小面经问题:break, continue, return 的区别及作用?

break :

作用:跳出当前所在的最内层循环或switch语句,不再执行该循环或switch语句中的剩余代码。如果在嵌套循环中使用,它只会跳出最内层的循环。

continue :

作用:结束本次循环的剩余部分,直接跳到下一次循环的开始条件判断处。也就是说,它会跳过本次循环体中剩余的代码,继续检查循环条件是否满足,以决定是否进行下一次循环。

return :

作用:从当前方法返回,并停止执行该方法中的所有后续代码。对于非void类型的方法,return语句还必须返回一个与方法声明类型相匹配的值。对于void类型的方法,return语句可以省略返回值。

2.3 修饰符(小面经)
2.3.1访问修饰符public、private、protected 和默认(不写任何修饰符)的区别?

public

可见性:对所有类都可见。

使用对象:可以修饰类、接口、变量、方法。

适用范围:当前类、同一包内、子孙类(无论是否在同一包)、其他包。
private

可见性:仅在同一类内可见。

使用对象:可以修饰变量、方法。

注意:不能修饰外部类。

适用范围:仅限于当前类。
protected

可见性:对同一包内的类和所有子类可见。

使用对象:可以修饰变量、方法。

注意:不能修饰外部类。

适用范围:当前类、同一包内、子孙类(无论是否在同一包),但不同包中的子孙类需要通过继承关系访问。
默认 (不写任何修饰符):

可见性:在同一包内可见。

使用对象:可以修饰类、接口、变量、方法。

适用范围:当前类、同一包内。

2.3.2 为什么要用 static 关键字?

内存效率

静态成员在整个程序执行期间只存在一份副本,被所有对象共享。这意味着无论创建了多少个类的实例,静态变量只会占用一块内存,这有助于节省内存。
访问便利性

无需创建类的实例即可访问静态成员,可以直接通过类名调用静态方法或访问静态变量。
组织常量和工具方法

适用于那些不依赖于对象状态的方法和值,如数学计算、辅助函数等。

2.3.3 static 方法是否能被重写?

不能被重写

从技术上讲,static方法不能被子类重写。如果子类中声明了一个与父类中static方法签名相同的方法,那么这个过程被称为方法隐藏而不是重写。这意味着子类中的static方法会遮蔽父类中的同名static方法,但不会像实例方法重写那样具有动态绑定的特性。
方法调用的行为

当你尝试通过一个指向子类对象的父类引用来调用一个静态方法时,编译器会根据引用类型来决定调用哪个版本的静态方法,而不是根据实际的对象类型。这是与实例方法重写的最大区别之一,在实例方法重写的情况下,调用的是运行时对象的实际类型的方法。

2.4 自测
  1. 如何自定义序列化行为?比如对某些字段加密?
  2. 八大数据类型是什么?对应的包装类是什么?
  3. 八大数据类型的包装类哪些具有常量池(缓存对象),常量池的范围?
  4. Integer类型常量池如何调节?
  5. Character类有哪些常用方法?
  6. 如何快速查找二进制数最左边的一个1?最右边一个1?
  7. 面经理解性记忆?
相关推荐
Sandman6z1 小时前
uv python 卸载
开发语言·python·uv
Teacher.chenchong2 小时前
R语言空间分析实战:地理加权回归联合主成份与判别分析破解空间异质性难题
开发语言·回归·r语言
忘了ʷºᵇₐ3 小时前
MapReduce-WordCount实现按照value降序排序、字符小写、识别不同标点
java·大数据·linux·intellij-idea·mapreduce
看到我,请让我去学习3 小时前
数据结构—排序(斐波那契数列,冒泡,选择,插入,快速,归并,图,广度优先算法)
c语言·开发语言·数据结构·后端
jian110583 小时前
java spring -framework -mvc
java·spring·mvc
程序员Bears3 小时前
JSP与JSTL:EL表达式与MVC分层模式的完美结合
java·开发语言·mvc
wktomo5 小时前
GO语言学习(七)
开发语言·学习·golang
自我意识的多元宇宙5 小时前
Java List 接口知识点详解
java·开发语言
zhangxzq5 小时前
JVM 性能问题排查实战10连击
java·运维·jvm·经验分享·docker