枚举、包装类、常用类
Java笔记目录可以点这里:Java 强化笔记(适合有基础的童鞋,不适合小白)
枚举(Enum)
自定义类实现枚举效果
如果我们要实现一个季节类,并且变量取值只能是春夏秋冬,应该这么写:
public class Season {
public static final Season SPRING = new Season();
public static final Season SUMMER = new Season();
public static final Season FALL = new Season();
public static final Season WINTER = new Season();
private Season() {} // 构造方法私有是为了防止外面主动创建新的对象
}
public class Main {
public static void main(String[] args) {
//这样写是不行的, 对象取值只能是类中定义的常量
// Season season = new Seaon();
Season spring = Season.SPRING;
Season summer = Season.SUMMER;
Season fall = Season.FALL;
Season winter = Season.WINTER;
test(spring); // 春天
test(summer); // 夏天
test(fall); // 秋天
test(winter); // 冬天
}
// 自定义类不可以使用 switch 语句, 只能使用 if else 语句来判断
public static void test(Season season) {
if (season == Season.SPRING) {
System.out.println("春天");
} else if (season == Season.SUMMER) {
System.out.println("夏天");
} else if (season == Season.FALL) {
System.out.println("秋天");
} else if (season == Season.WINTER) {
System.out.println("冬天");
}
}
}
枚举类型(Enum Type)
如果一个变量的取值只可能是固定的几个值,可以考虑使用枚举类型
- 枚举由一组预定义的常量构成
枚举的本质实际上就跟我们上面的写法差不多,但是在Java中的使用更加简单:
public enum Season {
SPRING, SUMMER, FALL, WINTER
}
package com.mj;
public class Main {
public static void main(String[] args) {
Season s = Season.WINTER;
System.out.println(s.name()); // WINTER
System.out.println(s.ordinal()); // 该枚举元素的索引, 3
test(s); // 冬天
}
// 枚举类型可以使用 switch 语句, 自定义类不可以
public static void test(Season season) {
switch (season) {
case SPRING:
System.out.println("春天");
break;
case SUMMER:
System.out.println("夏天");
break;
case FALL:
System.out.println("秋天");
break;
case WINTER:
System.out.println("冬天");
break;
}
}
}
枚举的使用注意
枚举的本质是类,所有枚举类型最终都隐式继承自 java.lang.Enum
枚举定义完常量后,可以再定义成员变量、方法等内容(这时最后一个常量要以分号结束)
枚举的构造方法权限必须是 无修饰符 或者 private
- Java 会主动调用构造方法初始化每一个常量,你不能主动调用构造方法
自定义构造方法的枚举:
public enum Season {
SPRING(5, 15),
SUMMER(25, 35),
FALL(15, 25),
WINTER(-5, 5);
private int min; // 最低气温
private int max; // 最高气温
Season(int min, int max) {
this.min = min;
this.max = max;
}
public int getMin() {
return min;
}
public int getMax() {
return max;
}
}
public static void main(String[] args) {
Season s = Season.SUMMER;
System.out.println(s.getMin()); // 25
System.out.println(s.getMax()); // 35
}
包装类(Wrapper Class)
对比引用类型,基本类型存在的一些缺陷:
- 无法表示不存在的值(
null
值) - 不能利用面向对象的方式去操作基本类型(比如直接用基本类型调用方法)
- 当方法参数是引用类型时,基本类型无法传递
解决方案:可以自己将基本类型包装成引用类型
自动装箱、拆箱(Autoboxing and Unboxing)
- 自动装箱:Java 编译器会自动将基本类型转换为包装类(调用
valueOf
方法) - 自动拆箱:Java 编译器会自动将包装类转换为基本类型(调用
xxxValue
方法)
自动装箱示例:Java 编译器会帮我们做很多工作,过一遍下面的示例即可。
Integer i1 = 10; // 自动装箱
// 等价于 Integer i1 = Integer.valueOf(10);
Object num = 10; // 自动装箱
// 等价于: Object num1 = Integer.valueOf(10);
public static void main(String[] args) {
add(20); // 自动装箱
// 等价于 add(Integer.valueOf(20));
}
static void add(Integer num) {}
自动拆箱示例:平时写代码这些都是不需要我们操心的,了解原理即可。
Integer i1 = 10;
int i2 = i1; // 自动拆箱
// 等价于: int i2 = i1.intValue();
System.out.println(i1 == 10); // 自动拆箱
// 等价于: System.out.println(i1.intValue() == 10);
Integer[] array = { 11, 22, 33, 44 }; // 包装类数组
int result = 0;
for (Integer i : array) {
// i.intValue() % 2 == 0
if (i % 2 == 0) { // 自动拆箱
// result += i.intValue();
result += i; //自动拆箱
}
}
包装类的判等
- 包装类的判等,不要使用
==
、!=
运算符,应该使用equals
方法
// 缓存范围: [-128, 127]
Integer i1 = 88;
Integer i2 = 88;
Integer i3 = 888;
Integer i4 = 888;
// 不推荐, 比较的是内存地址
System.out.println(i1 == i2); // true, 为什么呢? 缓存, 具体看下面
System.out.println(i3 == i4); // false
// 推荐, 先比内存地址, 再比值
System.out.println(i1.equals(i2)); // true
System.out.println(i3.equals(i4)); // true
你可能会好奇为什么 i1 == i2
会返回 true
,是因为:
IntegerCache
类中缓存了 [-128, 127] 范围的Integer
对象(因为很常用)Integer.valueOf
方法会优先去IntegerCache
缓存中获取Integer
对象
// 缓存范围: [-128, 127]
Integer i1 = 88; // 从IntegerCache缓存中取值
Integer i2 = Integer.valueOf(88); // 从IntegerCache缓存中取值
Integer i3 = new Integer(88); // new 出来的对象必然是一个新的地址
System.out.println(i1 == i2); // true
System.out.println(i2 == i3); // false
查看 Java 源码可以知道 valueOf
方法与 new
对象不完全等价, valueOf
是先判断缓存中是否有该值,没有再去new
。
包装类使用注意
- 【基本类型数组】与【包装类数组】之间是不能自动装箱、拆箱
public static void main(String[] args) {
// 基本类型数组不会自动装箱
int[] nums1 = { 11, 22 };
// test1(nums1); // error
// Integer[] nums2 = nums1; // error
// 包装类数组不会自动拆箱
Integer[] nums3 = { 11, 22 };
// test2(num3); // error
// int[] nums4 = num3; //error
}
static void test1(Integer[] nums) {}
static void test2(int[] nums) {}
Math
java.lang.Math
类提供了常见的数学计算功能
Math 类中提供了两个常用的数学常量:
// 自然常数,自然对数函数的底数
public static final double E = 2.7182818284590452354;
// 圆周率
public static final double PI = 3.14159265358979323846;
常用方法:
Math.abs(-100); // 求绝对值: 100
Math.max(100, 200); // 求最大值: 200
Math.min(100, 200); // 求最小值: 100
Math.floor(3.9); // 向下取整: 3.0
Math.ceil(3.1); // 向上取整: 4.0
Math.round(3.5); // 四舍五入: 4
Math.pow(4, 2); // 4的2次方: 16.0
Math.sqrt(16); // 16的平方根: 4.0
//角度转为弧度
double degree = 90; // 角度
double radian = Math.toRadians(degree); // 弧度
//三角函数
System.out.println(Math.sin(radian));
System.out.println(Math.cos(radian));
System.out.println(Math.tan(radian));
Math.random(); // 生成[0.0, 1.0)范围的随机数
Random
java.util.Random
可以更方便地生成各种随机数
常用方法:
// 生成各种随机数
Random r = new Random();
r.nextBoolean();
r.nextInt();
r.nextLong();
r.nextFloat();
// 生成[0, 99]范围的整数
int num1 = (int)(Math.random() * 100); // [0, 100)
int num2 = new Random().nextInt(100); // [0, 100)
// 生成[10, 99]范围的整数
// [0, 89] + 10
int num3 = (int)(Math.random() * 90) + 10; // [0, 89) + 10
int num4 = new Random().nextInt(90) + 10; // [0, 89) + 10
输出4位的大写字母验证码:
// 输出4位的大写字母验证码
Random r = new Random();
for (int i = 0; i < 4; i++) {
char c = (char)('A' + r.nextInt(26));
System.out.print(c);
}
System.out.println();
UUID
UUID(Universally Unique Identifier),通用唯一标识符
- UUID 的目的是让分布式系统中的所有元素都能有唯一的标识符,而不需要通过中央控制端来做标识符的指定
可以利用 java.util.UUID
类的 randomUUID
方法生成一个 128 bit(32 位 16 进制数)的随机 UUID
// 3b6d07be-6d4e-4c30-b5e8-8e57fedaa342
System.out.println(UUID.randomUUID());
数字格式化(printf、format)
可以使用 System.out.printf
或者 System.out.format
输出格式化的字符串
可以使用 String.format
创建格式化的字符串
long n = 461012;
System.out.format("%d%n", n); // "461012"
System.out.format("%08d%n", n); // "00461012"
System.out.format("%+8d%n", n); // " +461012"
System.out.format("%,8d%n", n); // " 461,012"
System.out.format("%+,8d%n%n", n); // "+461,012"
double pi = Math.PI;
System.out.format("%f%n", pi); // "3.141593"
System.out.format("%.3f%n", pi); // "3.142"
System.out.format("%8.3f%n", pi); // " 3.142"
System.out.format("%08.3f%n", pi); // "0003.142"
System.out.format("%-8.3f%n", pi); // "3.142"
String str = String.format("The PI is %.2f", Math.PI);
System.out.println(str); // The PI is 3.14
DecimalFormat
使用 java.text.DecimalFormat
可以更好地控制前 0、后 0、前缀、后缀、分组分隔符、十进制分隔符等
public class Main {
public static void main(String[] args) {
customFormat("###,###.###", 123456.789); // 123,456.789
customFormat("###.###", 123456.789); // 123456.789
customFormat("000000.000", 123.78); // 000123.780
customFormat("$###,###.###", 12345.67); // $12,345.67
}
static void customFormat(String pattern, double value) {
DecimalFormat fmt = new DecimalFormat(pattern);
System.out.println(fmt.format(value));
}
}
字符串与数字互转
字符串转数字:使用包装类的 valueOf
、parseXX
方法
Integer i1 = Integer.valueOf("12"); // 12
int i2 = Integer.parseInt("12"); // 12
int i3 = Integer.parseInt("FF", 16); // 255(十六进制解析FF)
Float f1 = Float.valueOf("12.34"); // 12.34
Float f2 = Float.parseFloat("12.34"); // 12.34
数字转字符串:使用字符串的 valueOf
方法、包装类的 toString
方法
String str1 = String.valueOf(12.34); // 12.34
String str2 = Integer.toString(255); // 255
String str3 = Integer.toString(255, 16); // ff(255转为16进制), 可以解析任何进制
String str4 = Float.toString(12.34f); // 12.34
高精度计算
float
、double
存储的只是小数的近似值,并非精确值。因此不适合用来进行高精度计算
double d1 = 0.7;
double d2 = 0.7;
System.out.println(d1 * d2); // 0.48999999999999994
为什么会这样呢?因为计算机底层都是二进制运算,0.7转二进制过程如下:结果是无限循环
0.7 = 0b0.101100110...
0.7 * 2 = 1.4 取出整除部分1
0.4 * 2 = 0.8 取出整除部分0
0.8 * 2 = 1.6 取出整除部分1
0.6 * 2 = 1.2 取出整除部分1
0.2 * 2 = 0.4 取出整除部分0
0.4 * 2 = 0.8 取出整除部分0
0.8 * 2 = 1.6 取出整除部分1
0.6 * 2 = 1.2 取出整除部分1
0.2 * 2 = 0.4 取出整除部分0
...
建议使用 java.math.BigDecimal
来进行高精度计算
- 一般使用字符串初始化
BigDecimal
,因为float
、double
存储的是近似值,不是精确值
BigDecimal v1 = new BigDecimal("0.7");
BigDecimal v2 = new BigDecimal("0.7");
System.out.println(v1.add(v2)); // 加, 1.4
System.out.println(v1.subtract(v2)); // 减, 0.0
System.out.println(v1.multiply(v2)); // 乘, 0.49
System.out.println(v1.divide(v2)); // 除, 1
System.out.println(v1.setScale(3)); // 保留3位小数, 0.700