Java速通6:枚举、包装类

120 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情

1.枚举:Enum Type

  1. 如果一个变量的取值只可能是固定的几个值,可以考虑使用枚举类型。(季节、性别、方位)

  2. 枚举由一组预定义的常量构成。

  3. 枚举的本质是类,所有枚举类型都隐式继承自java.lang.Enum。

    public enum Session{
        // 最后一个常量不用加分号
        SPRING, SUMMER, FALL, WINTER
    }
    
    // 使用
    Session s = Session.WINTER;
    print(s.name());    // WINTER
    print(s.ordinal()); // 3
    
    
    // 如果在switch里,可不用写 Session.XXX,直接写XXX
    Session session;
    switch(session){
        case SPRING:
            xxx;
            break;
        case SUMMER:
            xxx;
            break;
    }
    
  4. 枚举定义完常量后,还可再定义成员变量,方法等。(这时最后一个枚举常量要以分号结束)

  5. 枚举的构造方法权限必须是 无修饰符 或 private

    public enum Session{
        SPRING, SUMMER, FALL, WINTER;
        
        public int sum = 10;
        
        //构造方法私有化,防止外界通过new 方式创建枚举类
        private Session(){  
            xxxx
        }
    }
    
  6. java会主动调用构造方法 来初始化每个常量,用户不能主动调用构造方法(因为编译器把构造方法用peivate修饰了)。

    //上述的枚举 大致实现如下
    public class Session{
        
        private Session(){}  // 构造方法
        
        // 调用构造方法 来初始化每个常量
        public static final Session SPRING = new Session();
        public static final Session SUMMER = new Session();
        public static final Session FALL = new Session();
        public static final Session WINTER = new Session();
    }
    
    
    // 调用
    Session s = Session.SPRING
        
    if(s == Session.SPRING){
        
    }
    

1.1.自定义了构造方法 的枚举

public enum Session{
    SPRING(5, 15),  // 参数分别是 最低、最高温度
    SUMMER(25, 35), 
    FALL(13, 25), 
    WINTER(-5, 5);
    
    private int min;
    private int max;
    
    Session(int min, int max){
        this.min = min;
        this.max = max;
    }
    
    
    // 提供getter方法
    public int getMin(){
        return min;
    }
    
    public int getMax(){
        return max;
    }
}

//使用温度
Session s = Session.SPRING;
print(s.getMax()); // 15
print(s.getMin()); // 5

2.包装类

2.1.基本类型的缺陷:

  1. 无法表示不存在的值(null)
  2. 不能利用面向对象的方式操作基本类型(如直接用基本类型调用方法)
  3. 当方法参数是引用类型时,基本类型无法传递。

解决方案:可以自己将基本类型包装成引用类型。

public class IntObject{
    private int value;
    
    public IntObject(int value){
        this.value = value;
    }
    
    //提供Setter/Getter方法
}


//调用
IntObject num = null;
IntObject age = new IntObject(110);

2.2.包装类:Wrapper Class

  1. java内置了基本数据类型的包装类(java.lang包下)
  2. 自动装箱:java编译器会自动调用xxx.valueOf()方法,将基本类型转换为包装类(装包)。
  3. 自动拆箱:java编译器会自动调用xxxValue()方法,将包装类转换为基本类型(拆包)。
Integer num = Integer.valueOf(10);
Integer num = 10;  // 自动装箱


Integer max = 10;
int min = max.intValue();
int min = max;     // 自动拆箱

2.3.包装类的判等(不要使用 ==、!=,应该使用equals方法。)

DK1.5后, 包装类型Integer、Short、Byte、Character、Long都使用了缓存技术。

// 1.IntegerCache 类中缓存了[-128, 127]范围的Integer对象
// 2.Integer.valueOf方法会优先去IntegerCache缓存中获取Integer对象
// 3. == 判断两边的对象是不是同一个对象
// 直接使用new构造一个长整型的时候,会生成一个新的对象。
// 而使用 valueOf 的时候,如果参数位于[-128, 127]这个区间内,则直接从cache数组中取一个对象。
// 如果不在区间里,则新建对象。

// 自动装箱时,Java调用了valueOf函数,拆箱时,调用 xxxValue 函数。
Integer d1 = 88;
Integer d2 = Integer.valueOf(88); // 与Integer d1 = 88;等价
Integer d3 = new Integer(88);

Long e = 1000L;
Long f = 1000L;

println(d1 == d2); // true 两者都是从缓存中拿数据
println(d1 == d3); // false d3是new对象,数据在堆内存
println(e == f);   // false 和Integer一样,未走缓存


// 举例2:
println(((Long)1L) == 1);        // true
println(new Long(1).equals(1));  // false
println(new Long(1).equals(1L)); // true

println(((Long)1000L) == 1000);               // true
println(((Long)1000L) == Long.valueOf(1000)); // false

// == 操作符的左边是一个Long型的对象,为了比较就使用了自动拆箱,左边拆箱成了整数1,所以与右边的值是相等的。
// 第二行:equals参数接受一个Object对象,所以1就被自动装箱成了Integer.valueOf(1)。
// 而Long的equals的实现:如果传进来的是Long型的,那么就比较值,如果传进来的是其他类型,直接返回false。

2.4.【基本类型数组】与【包装类数组】之间是不能自动装箱、拆箱的。

void test1(Integer[] nums){省略方法体...}
int[] nums1 = {11, 22};
test1(nums1); //error

Integer[] nums2 = {11, 22};
int[] nums3 = nums2; //error

2.5.高精度计算:

floatdouble 存储的只是小数的近似值,并非精确值,因此不适合用来做高精度计算。

double d1 = 0.7;
double d2 = 0.7;

//浮点数转化为二进制会出现以下错误
println(d1 * d2); //0.4899999999

建议使用java.math.BigDecimal来进行高精度计算。

new BigDecimal(0.7); //浮点数本来就不准确,这么传,结果依然不准确

//建议使用字符串初始化BigDecimal
//里面有个字符数组,把每个字符都单独存{'0', '.', '7'}
BigDecimal v1 = new BigDecimal("0.7"); 
BigDecimal v2 = new BigDecimal("0.7");

println(v1.add(v2));       // 加,1.4
println(v1.subtract(v2)); // 减,0.0
println(v1.multiply(v2)); // 乘,0.49
println(v1.divide(v2));    // 除,1
println(v1.setScale(3));   // 保留3位小数 0.700

字符串与数字互相转化:

  1. 字符串转数字:使用包装类的valueOf()parseXX()方法

    Integer i1 = Integer.valueOf("123");  // 123
    int i2 = Integer.parseInt("123");      // 123
    int i3 = Integer.parseInt("FF", 16);   // 255 (十六进制解析FF)
    
    Float i1 = Float.valueOf("12.3");      // 12.3
    float i1 = Float.parseFloat("12.3");   // 12.3
    
  2. 数字转字符串:使用字符串的valueOf()方法,包装类的toString()方法

    String str1 = String.valueOf(12.3);       // 12.3
    String str2 = Integer.toString(123);      // 255
    String str1 = Integer.toString(255, 16); // FF
    String str1 = Float.toString(12.3f);      // 12.3