1. 程式人生 > >重新開始學Java——Random、Math、BigDecimal、MathContext、System、Runtime

重新開始學Java——Random、Math、BigDecimal、MathContext、System、Runtime

這篇部落格主要描述Random類與Math類,以及一些其他的類。分別講述一些使用方法,以及這個類是做什麼的。

java.util.Random

官方API宣告如下:

public class Random extends Object implements Serializable

可以看到這個類是直接繼承了java.lang.Object類的, 那麼針對於這個類要說的就是以下幾點:

An instance of this class is used to generate a stream of pseudorandom numbers.(此類的例項用於生成偽隨機數流。)
The class uses a 48-bit seed, which is modified using a linear congruential formula.( 該類使用 48 位種子,使用線性等同公式對其進行修改。 )
If two instances of Random are created with the same seed, and the same sequence of method calls is made for each, they will generate and return identical sequences of numbers.(如果使用相同的種子建立兩個 Random 例項,並且對每個例項進行相同的方法呼叫序列,它們將生成並返回相同的數字序列。)
Instances of java.util.Random are threadsafe. However, the concurrent use of the same java.util.Random instance across threads may encounter contention and consequent poor performance. Consider instead using ThreadLocalRandom in multithreaded designs.(例項java.util.Random是執行緒安全的。但是,java.util.Random 跨執行緒同時使用相同例項可能會遇到爭用並因此導致效能不佳。相反ThreadLocalRandom,請考慮 在多執行緒設計中使用。)
Instances of java.util.Random are not cryptographically secure. Consider instead using SecureRandom to get a cryptographically secure pseudo-random number generator for use by security-sensitive applications.( 例項java.util.Random不具有加密安全性。相反SecureRandom,請考慮使用加密安全的偽隨機數生成器供安全敏感應用程式使用。)

其實總結起來無非就是幾點:

1、這個類用於生成對應的一個偽隨機數。
2、其中有一個公式可以進行操作。
3、如果有相同的種子的話,那麼可以生成同樣的隨機數。
4、java.util.Random是執行緒安全的, 但是如果要在多執行緒中使用的話,那麼應該使用ThreadLocalRandom類。
5、java.util.Random是沒有加密功能的(也就是說不安全),建議使用SecureRandom類。

Random 類的構造

Random():建立一個新的隨機數生成器
Random(long seed):使用單個 long 種子建立一個新的隨機數生成器

java.util.Random 類中生成隨機數的方法:

protected int next(int bits) 生成下一個偽隨機數。 注意有 protected 限制, 注
意使用範圍。子類一般應重寫此方法。
int nextInt() 返回下一個 int 型別隨機數, 所生成的隨機數大於或等於 0。
int nextInt(int n) 返回下一個 int 型別隨機數, 所生成的隨機數大於或等於 0 ,
並且小於 n。
long nextLong() 返回下一個 long 型別隨機數, 所生成的隨機數大於或等於 0 ,
並且小於 1.0。
float nextFloat() 返回下一個 float 型別隨機數, 所生成的隨機數在 0.0 和 1.0
之間均勻分佈。
double nextDouble()返回下一個 double 型別的隨機數,所生成的隨機數是在 0.0
和 1.0 之間均勻分佈的 double 值。
boolean nextBoolean(),返回下一個 boolean 型別的隨機數,它是取自此隨機數生
成器序列的均勻分佈的 boolean 值。

java.util.Random 類中生成隨機數的方法:

void setSeed(long seed),使用單個 long 種子設定此隨機數生成器的種子。
void nextBytes(byte[] bytes),生成隨機位元組並將其置於使用者提供的 byte 陣列
中。
double nextGaussian(),返回下一個偽隨機數,它是取自此隨機數生成器序列的、
呈高斯(“正態” ) 分佈的 double 值,其平均值是 0.0, 標準差是 1.0。

示例如下:

import java.util.Random;
/**
* 當種子不變時,多次執行得到的隨機數是相同的
*/
public class TestRandom1 {
	public static void main(String[] args) {
		long seed = 100;
		Random = new Random();
		System.out.println(random.nextInt());
		System.out.println(random.nextDouble());
		System.out.println(random.nextInt(100));// 0~100之間,包括0,包括
		100
		Random random2 = new Random(seed);
		System.out.println(random2.nextInt());
	}
}

java.lang.Math

官方API宣告如下:

public final class Math extends Object

官方API描述如下:

The class Math contains methods for performing basic numeric operations such as the elementary exponential, logarithm, square root, and trigonometric functions.(該類Math包含執行基本數值運算的方法,如基本指數,對數,平方根和三角函式。)

那麼就可以理解成,Math類中聲明瞭大量與數學運算有關的方法,如果想要進行數學運算的話,那麼就可以使用這個類。 Math 類是 final 型別的, 因此不能有子類, Math 類的構造是 private型別的, 因此不能例項化。提供了諸多用於數學計算的靜態方法。提供兩個靜態常量: E 自然對數、 PI 圓周率。具體的方法可以自行檢視官方API。

示例如下:

public class TestMath {
	public static void main(String[] args) {
		System.out.println("自然對數的底數: "+Math.E);
		System.out.println("圓周率: "+ Math.PI);
		double d = 3.14;
		// Math.round(x) 返回最接近引數x 的一個整數值
		System.out.println(d+ "四捨五入: "+ Math.round(d));
		// floor 捨去小數部分後,返回一個double型別的值
		System.out.println(d+ ": "+ Math.floor(d));
		double d1 = 3.50000000000000001;
		// Math.round(x) 返回最接近引數x 的一個整數值
		System.out.println(d1+ "四捨五入: "+ Math.round(d1));
		System.out.println(d1+ ": "+ Math.floor(d1));
		double d4 = Math.pow(5, 2);// 求冪
		System.out.println(d4);
		double d5 = Math.pow(81, 0.5);// 開幾次方
		System.out.println(d5);
	}
}

java.math.BigDecimal

為什麼要使用BigDecimal

float 和 double 的缺陷:浮點數精度難以控制, 不能實現精確計算 (尤其是早期 JDK), 嚴重不能滿足涉及財務、 天文等需要精確計算的行業需求。

BigDecimal 表示不可變的、 任意精度的有符號十進位制數。 BigDecimal 由任意精度的整數非標度值 和 32 位的整數標度組成: 如果為零或正數, 則標度是小數點後的位數;如果為負數, 則將該數的非標度值乘以 10 的負 scale 次冪。 因此,BigDecimal 表示的數值是 unscaledValue × 10 –scale 。(–scale這裡是上標)

選擇 float 、 double 還是 BigDecimal ?

如果允許存在適當誤差, 則可以使用 float 或 double 型別;如果需要進行精確運 算, 則應該使用 java.math.BigDecimal 類。

構造方法

構造方法如下:

BigDecimal( String val )
BigDecimal( long val )
BigDecimal( int val )
BigDecimal( double val )
BigDecimal( char[] in )

以上是常用的構造,但是在API中可以看到構造方法有很多,這裡就不單獨去說了。以後用到哪個就上官方API中進行查詢。

示例如下:

import java.math.BigDecimal;
/**
* 構造BigDecimal 物件
* BigDecimal( double d ):該構造構造出來的BigDecimal 物件,
* 表示double數值val的二進位制浮點值準確的十進位制表示形式
*BigDecimal( double d ) :根據給定的字串闖將BigDecimal物件
* 根據val 來確定非標度值( unscaleValue)和標度值( scale)
* BigDecimal(char[] array, 0,array.length)
* 根據引數傳入的char陣列array,從它的 offse 開始,取length 個來構
造BigDecimal
* BigDecimal(char[] array)
* 根據引數傳入的char陣列array構造BigDecimal物件
*/
public class TestDecimalB {
	public static void main(String[] args) {
		final String s = "2.9";
		double d = Double.parseDouble(s);
		System.out.println("double : "+ d);
		BigDecimal bd = new BigDecimal(d);
		System.out.println("根據double值穿件的BigDecimal 物件 : \n" + bd);
		BigDecimal bd2 = new BigDecimal(s);// 29乘以10的-( 1 )
		System.out.println("根據String 建立的BigDecimal物件"+bd2);
		char[] chares = s.toCharArray();
		for (int i = 0; i < chares.length; i++) {
		System.out.println(chares[i]);
		}
		bd2 = new BigDecimal(chares, 0,chares.length);
		System.out.println("根據char陣列建立的BigDecimal物件: "+ bd2);
	}
}

import java.math.BigDecimal;
import java.math.BigInteger;
/**
* 自己指定非標度值( unscaredValue)和標度值(scale)來構造BigDecimal 物件
* BigDecimal(BigInteger unscaledVal, int scale)
* 將 BigInteger 非標度值和 int 標度轉換為 BigDecimal。
* BigDecimal(BigInteger unscaledVal, int scale, MathContext mc)
* 將 BigInteger 非標度值和 int 標度轉換為 BigDecimal(根據上下文設定進行舍入)
* BigInteger(String val) :引數val 必須是表示十進位制整數的字串
* BigInteger(String val, int radix) : 引數val必須是表示整數的字串(某種進位制形式),引數radix表示基數(多少進位制)
*/
public class TestDecimalC {
	public static void main(String[] args) {
		BigInteger unscaledVal = new BigInteger("9ebb",16);// 確定非標度值
		int scale = 3;// 確定標度值
		// dec 表示的是 unscaleValue 乘以10 的 -scale 方 得到的數值
		BigDecimal dec = new BigDecimal(unscaledVal, scale);
		System.out.println(dec);
	}
}

BigDecimal 中的一些方法

java.math.BigDecimal 類中靜態方法:

static BigDecimal valueOf( double val ): 將 double 轉換為 BigDecimal
static BigDecimal valueOf( long val ): 將 long 轉換為 BigDecimal
static BigDecimal valueOf( long unscaledVal, int scale ): 將 long 非標度值和int 標度轉換為 BigDecimal

java.math.BigDecimal 類中算術計算方法

BigDecimal add( BigDecimal augend ) : 加法(引數是加數)
BigDecimal divide( BigDecimal divisor ) : 除法(引數是除數)
BigDecimal multiply( BigDecimal multiplicand ) : 乘法(引數是乘數)
BigDecimal subtract( BigDecimal subtrahend ) : 減法(引數是減數)
BigDecimal plus() : 返回自身值(符號與本身相同)
BigDecimal negate() : 求相反數
BigDecimal pow(int n) : 計算 this 的 n 次方
BigDecimal abs() : 求取絕對值
BigDecimal max(BigDecimal val) : 求 this 和 vale 中的較大者
BigDecimal min(BigDecimal val) : 求 this 和 vale 中的較小者
BigDecimal movePointLeft(int n) : 把小數點左移 n 位
BigDecimal movePointRight(int n) : 把小數點右移 n 位
int scale() : 返回此 BigDecimal 的標度
int signum() 返回此 BigDecimal 的正負號函式

示例如下:

import java.math.BigDecimal;
public class TestDecimalD {
	public static void main(String[] args) {
		double a =2.2;
		double b =1.1;
		double c = a-b;
		System.out.println(c);
		BigDecimal bd = new BigDecimal("3.9");
		System.out.println("被減數"+bd);
		BigDecimal bd2 = new BigDecimal("1.3");
		System.out.println("減數"+bd2);
		// 減法運算
		BigDecimal bd3 = bd.subtract(bd2);
		System.out.println(bd3);
		BigDecimal bd4 = bd.divide(bd2);
		System.out.println(bd4);
	}
}
import java.math.BigDecimal;
public class TestDecimalE {
	public static void main(String[] args) {
		BigDecimal dec = new BigDecimal("-250");
		System.out.println(dec);// -250
		BigDecimal dec1 = dec.plus();// -250 返回一個與自身符號相同的、絕對值相同的數
		System.out.println(dec1);
		BigDecimal dec2 = dec.negate();//250 返回一個與自身符號相反、絕對值相同的數
		System.out.println(dec2);
		BigDecimal dec3 = dec.pow(2);// 求 n 次方(負數的奇數次是負數,偶數次方是整數)
		System.out.println(dec3);
		BigDecimal dec4 = dec.abs();// 求絕對值
		System.out.println(dec4);
	}
}
/**
*1、建議使用valueOf 來獲得BigDecimal類的例項
*2、移動小數點: movePointLeft( int n )、 movePointRight( int n )
*/
import java.math.BigDecimal;
public class TestDecimalF {
	public static void main(String[] args) {
		BigDecimal dec = BigDecimal.valueOf(314,2);
		System.out.println(dec);
		System.out.println("非標度值: " + dec.unscaledValue());
		System.out.println("標度值: "+ dec.scale());
		BigDecimal d1 = dec.movePointLeft(2);// 小數點向左移動2位,縮小100倍
		System.out.println(d1);
		BigDecimal d2 = dec.movePointRight(3);// 小數點向右移動3位,擴大
		1000倍
		System.out.println(d2);
	}
}
/**
* 實現精確計算,並將精度與舍入模式進行控制
*/
public class TestDecimalG {
	public static void main(String[] args) {
		final int precision = 20;// 精度
		RoundingMode = RoundingMode.HALF_UP;
		// 確定了精度和舍入模式
		final MathContext context = new
		MathContext(precision,roundingMode);
		BigDecimal dec1 = BigDecimal.valueOf(39,2);
		System.out.println("被除數: " + dec1);
		BigDecimal dec2 = BigDecimal.valueOf(11,2);
		System.out.println("被除數: " + dec2);
		// 對於除法,如果除不盡,就要丟擲ArithmeticException異常
		// BigDecimal result = dec1.divide(dec2);// ArithmeticException 算數異常
		// 當指定上下文物件後,就不再丟擲異常了(因為確定了精度和舍入模式)
		BigDecimal result = dec1.divide(dec2,context);
		System.out.println(result);
		// 指定舍入模式( 精度與被除數相同)
		BigDecimal result2 = dec1.divide(dec2,RoundingMode.HALF_UP);
		System.out.println(result2);
	}
}

在這裡需要注意了, MathContext這個類經常是服務於BigDecimal類的。所以經常看到聯合使用。

數字格式化

經常有特定的需求進行數字格式化的操作。比如說金錢的表現方式等。具體使用示例如下:

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
/**
* 數字格式化
* 需求:
* 1、專業學號的後三位 從1開始 到999 注:要用for迴圈來做的話,那麼100以
下的數字,就會有2位或1位
* 2、將特定格式的字串解析為 數字
*/
public class TestNumberFormat {
	public static void main(String[] args) throws ParseException {
	for (int i = 0; i < 1000; i++) {
		String s = "";
		if(i<10){
			s = "00" + i;
		}else if( i<100){
			s = "0"+i;
		}else{
			s = "" + i;
		}
		System.out.print(s +"\t");
	}
	System.out.println();int i = 1; // 出來的形式 要“001”;
	String pattern = "0000";
	NumberFormat nf = new DecimalFormat(pattern);
	String s = nf.format(i); // 將數字格式化為特定格式
	System.out.println(s);
	s = "0010";
	Number n = nf.parse(s);// 將特定格式的字串解析為Number
	System.out.println(n);
	String money = "¥1,000,000";
	NumberFormat parser = new DecimalFormat("¥0,000,000");
	Number m = parser.parse(money);
	System.out.println(m);
	}
}

那麼在使用NumberFormat這個類就是輕鬆解決數字格式化的問題。

System 類

System 類提供了一些靜態屬性和靜態方法用於當前程式和系統的互動;該類是個 final 型別的類, 不是抽象類, 但不能例項化: 因為該類的構造方法被 private 修飾。

System 中定義的靜態常量

static InputStream in : "標準"輸入流
static PrintStream err : "標準"錯誤輸出流
static PrintStream out : "標準"輸出流

但是要注意,這裡的輸出流,依舊是間接繼承了java.io.OutputStream類,在使用上按照正常的流使用就可以了。(前提是會流。) 示例如下:

import java.util.Scanner;
public class TestSystem1 {
	public static void main(String[] args) {
		Scanner s = new Scanner(System.in);// “標準”輸入流
		System.err.println("請輸入你的姓名");// System.err“標準”錯誤輸出流
		String name = s.nextLine();
		System.out.println("你的名字是"+name);// System.out “標準”輸出流
		s.close();
	}
}

System 中的一些方法

System 中定義的靜態方法(現階段可以使用的方法)
long currentTimeMillis() 返回以毫秒為單位的當前時間
void exit(int status) 終止當前正在執行的 Java 虛擬機器
void gc() 執行垃圾回收器
long nanoTime() 返回系統計時器的當前值(以毫微秒為單位)

示例如下:

public class TestSystem2 {
	public static void main(String[] args) {
		long timefirst = System.currentTimeMillis();
		System.out.println(timefirst);
		int[] source = {1,2,3,4,5};System.out.println(source);
		int[] dest = new int[10];
		System.out.println( Arrays.toString( source ) ) ;
		/** 複製陣列*/
		System.arraycopy(source, 0, dest, 3, source.length);
		System.out.println( Arrays.toString( dest ) ) ;
		// 將 dest 的3 開始的 3個元素 複製到 dest的 0 開始的三個位置上去
		System.arraycopy(dest, 3, dest, 0, 3);
		System.out.println( Arrays.toString( dest ) ) ;
		/** 獲得某個物件在記憶體中的地址( 其實就是Object的hashCode返回的那個值 )*/
		String hex = Utils.toHex(System.identityHashCode(source) );
		System.out.println(hex);
		String s = new String("hello,world");
		System.out.println(s.hashCode());
		System.out.println(Utils.toHex(System.identityHashCode(s)));
		s = s.intern();// 規範化表示形式
		System.out.println(s.hashCode());
		System.out.println(Utils.toHex(System.identityHashCode(s)));
		/**獲得程式執行到某一行時 對應的時間(自曆元至此所經歷的毫秒數)
		**/
		long time = System.currentTimeMillis();
		System.out.println(time);/** 返回最準確的可用系統計時器的當前值,以毫微秒為單位 */
		long nano = System.nanoTime();
		System.out.println(nano);
		/** 直接讓 JVM 退出的方法*/
		// System.exit( int staus ) 
		// 如果status 是 0 表示正常退出,否則都表示不正常退出
		for (int i = 0; i < 100; i++) {
			if(i == 81){
				//break; // 跳出迴圈,迴圈之後的程式碼繼續
				System.exit(0);// 直接終止JVM程序,迴圈之後的程式碼沒有機會執行
			}
		}
		System.out.println("GoodBye");
	}
}

當然,這裡提供了很多針對於系統方面的方法,可以自行上API中查閱。這裡僅僅列出了比較常用的方法。

Runtime 類

Runtime 類代表當前 Java 程式的執行時環境,每個 Java 程式都有與之對應的 Runtime 例項,通過該類的例項, 應用程式也可以與系統進行互動,同 System 類, 該類構造也被私有, 因此不能被例項化,其中大多數方法是非靜態的, 因此需要使用例項來訪問。

獲取的 Runtime 例項: static Runtime getRuntime()

訪問執行時環境:

int availableProcessors() : 返回可用的 CPU 的個數
long freeMemory() : 返回 Java 虛擬機器中的空閒記憶體量
long maxMemory() : 返回 Java 虛擬機器試圖使用的最大記憶體量
long totalMemory() : 返回 Java 虛擬機器中的記憶體總量

例子:

public class TestRuntime {
	public static void main(String[] args) {
		// 獲得一個Runtime例項
		Runtime r = Runtime.getRuntime();
		System.out.println("邏輯處理器的個數: "+ r.availableProcessors() );
		System.out.println("freeMemory"+ r.freeMemory() );
		System.out.println("totaMemory: " + r.totalMemory() );
		System.out.println("maxMemory : "+ r.maxMemory());
	}
}

執行系統命令: Process exec(String command):在單獨的程序中執行指定的字串命令

public class TestRuntime2 {
	public static void main(String[] args) throws IOException {
		// 獲得一個Runtime例項
		Runtime r = Runtime.getRuntime();
		//String command = "notepad"; // 執行會導致開啟記事本
		String command = "explorer"; // 開啟資源管理器
		r.exec(command);
	}
}

執行一個 CMD 中的命令

public class TestRuntime3 {
	public static void main(String[] args) throws IOException {
		// 獲得一個Runtime例項
		Runtime r = Runtime.getRuntime();
		// copy 是 在cmd 環境下執行的命令
		// copy 後面的第一個部分是源頭,第二個部分是目的地
		// 在Windows下 路徑必須是 \\ 形式 不能是 / 形式,否則可能命令執行失敗
		String command = "cmd /C copy C:\\Person.java c:\\hello.java"; // 在cmd 執行dos命令
		r.exec(command);
	}
}

關機、重啟、登出命令

public class TestRuntime4 {
	public static void main(String[] args) throws IOException {
		// 獲得一個Runtime例項Runtime r = Runtime.getRuntime();
		String command = "cmd /C start call shutdown -R -f -t 0";
		r.exec(command);
	}
}

僅僅使用於Windows系統

關機命令 cmd /c start call shutdown -S -f -t 0
重啟命令 cmd /c start call shutdown -R -f -t 0 
登出命令 cmd /c start call shutdown