【Java 基礎】枚舉、包裝類、Math、Radnom、UUID、格式化、DecimalFormat、高精度計算(BigDecimal)

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

字符串與數字互轉

字符串轉數字:使用包裝類的 valueOfparseXX 方法

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

高精度計算

floatdouble 存儲的只是小數的近似值,並非精確值。因此不適合用來進行高精度計算

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,因爲 floatdouble 存儲的是近似值,不是精確值
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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章