1. 程式人生 > >與使用者互動與系統相關(7.1 ,7.2)

與使用者互動與系統相關(7.1 ,7.2)

參考《瘋狂java講義》
與使用者互動

實際上,大部分程式都需要處理使用者動作,包括接受使用者的鍵盤輸入,滑鼠動作等。本章未涉及圖形使用者介面(GUI)程式設計,故本節主要介紹程式如何獲得使用者的鍵盤輸入。

1. 執行Java程式的引數
回憶Java程式入口——main()方法的方法簽名

//Java程式入口,main()方法
public static void main(String[] args)

為什麼要用上面的方法簽名呢

public修飾符:Java類由JVM呼叫,為了讓JVM可自由呼叫這個main方法,所以使用public方法把這個方法暴露出來
static修飾符:JVM呼叫這個主方法時,不會先建立該主類物件,然後通過物件呼叫main方法。JVM通過該類來呼叫main方法,因此用static修飾該方法
void返回值:因為主方法被JVM呼叫,該方法的返回值將返回給JVM,這沒有任何意義,因此main方法返回值沒有任何意義
字串陣列形參:根據方法呼叫的規則,誰呼叫方法,誰就負責為形參賦值,也就是說,main方法由JVM呼叫,即args形參應該由JVM負責賦值。但JVM怎麼知道如何為args資料賦值呢?

public class MainTest{
	public static void main(String[] args){
		//輸出陣列長度
		System.out.println(args.length);//輸出0
		//輸出args陣列的每個元素
		for(String arg : args){
			System.out.println(arg);//沒有輸出
		}
	}
}

該為如下命令來執行結果

//如果在執行時在類名後緊跟一個或多個字串
//(多個字串用空格隔開,若一個字串中有空格,應將其用“ ”括起來)
//輸出2,分兩行輸出java ,String
java MainTest java String

1. 使用Scanner獲取鍵盤輸入
Scanner是一個基於正則表示式的文字掃描器,它可以從檔案,輸入流,字串中解析出基本型別值和字串值,Scanner類提供了多個構造器,不同構造器可以接收檔案,輸入流,字串作為資料來源,用於從檔案,輸入流,字串中解析資料。
Scanner主要提供了兩個方法來掃描輸入

  • hasNextXxx():是否還有下一個輸入項,其中Xxx可以是,Int,Long等代表基本資料型別的字串,如果只是判斷是否包含下一個字串,則直接使用hasNext()。
  • nextXxx():獲取下一個輸入項

注意: 在預設情況下,Scanner使用空白字元(包括空格,Tab,回車)作為多個輸入項之間的分隔符

public class ScannerKeyBoardTest{
	public static void main(String[] args){
		//System.in代表標準輸入,就是鍵盤輸入
		Scanner sc = new Scanner(System.in);
		//增加下面一行將只把回車作為分隔符
		//sc.useDelimiter("\n");
		//判斷是否還有下一個輸入項
		while(sc.hasNext()){
			//輸出輸入項
			System.out.println(sc.next());
		}
	}
}

Scanner的讀取操作可能被阻塞(當執行順序流暫停)來等待資訊的輸入。如果輸入源沒有結束,Scanner又讀不到更多輸入項時(尤其在鍵盤輸入時比較常見),Scanner的hasNext和next()方法都有可能被阻塞,hasNext是否阻塞與next方法是否阻塞無關。

為Scanner設定分隔符使用useDelimiter(String pattern)方法即可,該方法引數是一個正則表示式

Scanner提供了兩個簡單的方法來逐行讀取

  • boolean hasNextLine():返回輸入源是否還有下一行
  • String nextLIne():返回輸入源中下一行的字串
public class ScannerLongTest{
	public static void main(String[] args){
		Scanner sc = new Scanner(System.in);
		while(sc.hasNextLong()){
			System.out.println(sc.nextLomg());
		}
	}
}

2. Scanner讀取檔案輸入

只要在建立Scanner物件時傳入一個File物件作為引數,就可以讓Scanner讀取該檔案的內容

public class ScannerFileTest{
	public static void main(String[] args){
		//將一個File物件作為Scanner構造器的引數,Scanner讀取檔案內容
		Scanner sc = new Scanner(new File("ScannerFileTest.java"));
		System.out.println("檔案內容如下:");
		while(sc.hasNextLine())
		{
			//輸出檔案的下一行
			Systemout.println("sc.nextLine()");
		}
	}
}

系統相關

Java程式在不同作業系統上執行時,可能需要取得平臺的相關屬性,或者呼叫平臺命令來完成特定功能。Java提供了System類和Runtime類來與程式執行的執行平臺進行交叉。

1. System類
System代表Java程式的執行平臺,程式不能建立System類的物件,System類提供了一些類變數和類方法,允許直接通過System類來呼叫這些類變數和類方法。
System類提供了代表標準輸入,標準輸出和錯誤輸出的類變數,並提供了一些靜態方法用於訪問環境變數,系統屬性的方法,還提供了載入檔案和動態連結庫的方法。

載入檔案和動態連結庫主要用對native方法有用,對於一些特殊的功能(如訪問作業系統底層硬體裝置等)Java程式無法實現,必須藉助C來完成,此時需要使用C為Java提供方法實現,實現步驟如下:
1 Java程式中宣告native修飾的方法,類似於abstract方法,只有方法簽名,沒有實現,編譯該Java程式,生成一個class檔案。
2 用javah編譯第一步生成的class檔案,將產生一個.h檔案
3 寫一個.cpp檔案實現native方法,這一步需要包含第2步產生的.h檔案(這個.h檔案又包含了JDK帶的jni.h檔案)。
4 將第3步產生的.cpp檔案編譯成動態連結庫檔案
5 在Java中用System類的loadLibrary…()方法或Runtime類的loadLibrary()方法載入第4步產生的動態連結庫,Java程式中就可以呼叫這個native方法。

public class SystemTest{
	public static void main(String[] args){
		//獲取系統所有的環境變數
		Map<String,String>env = System.getenv();
		for(String name : env.keySet()){
			System.out.println(name + "---->" + env.get(name))
		}
		//獲取指定環境變數的值
		System.out.println(System.getenv(  "JVAV_HOME"));
		//獲取所有系統屬性
		Properties props = System.getProperties();
		//將系統所有屬性儲存在props.txt檔案中
		props.store( "new FileOutputStream( "props.txt" )","System.Properties" );
		//輸出特定的系統屬性
		System.out.println(System.getProperty( "os.name" ));//輸出 Windows 10
	}
}

提示:System提供了通知系統進行垃圾回收的gc()方法,以及通知系統進行資源清理的runFinalization()方法

System還提供了兩個獲取系統時間的方法:currentTimeMillis()和nanoTime(),它們都返回一個long型整數。實際上它們都返回當前時間與UTC 1970年1月1日午夜的時間差,前者以毫秒作為單位,後者以納秒為單位。必須指出的是,這兩個方法返回的時間粒度取決於底層作業系統,可能所在作業系統根本不支援以毫秒和納秒作為計時單位。例如好多作業系統都以及時毫秒作為單位測量時間,currentTimeMillis方法根本不可能返回精確的毫秒數:而nanoTime方法很少用,因為大部分作業系統都不支援一納秒為計時單位

除此之外,System類的in , out , err分別代表系統的標準輸入(通常是鍵盤),標準輸出(通常是顯示器)和錯誤輸出流,並提供了setIn,setOut和setErr方法來改變系統的標準輸入,標準輸出和標準錯誤輸出流。

System還提供了一個identityHashCode(Object x)方法,該方法返回指定物件的精確hashCode值,也就是根據物件地址計算得到的HashCode值。當某個類的hashCode方法被重寫後,該類例項的hashCode值就不能唯一標識該物件:但通過identifyHashCode方法返回的hashCode值,依然是根據物件地址計算得到的hashCode值。所以如果兩個物件的identifyHasCode值相同,則兩個物件絕對是同一個物件。

public class IdentifyHashCodeTest{	
	public static void main(String[] args){
		//下面程式中s1和s2是兩個不同的物件
		String s1 = new String("Hello");
		String s2 = new String("Hello");
		//String類重寫了equals方法和hashCode方法,只要兩字串序列相同equals方法就返回true,
		//hashCode方法是根據字串序列計算的hashCode值。
		System.out.println(s1.hashCode()==s2.hashCode());//輸出true
		//s1和s2是不同的物件,所以返回的IdentifyHashCode的值不同
		System.out.println(System.identityHashCode(s1)==System.identityHashCode(s2));//輸出false
		String s3 = "java";
		String s4 = "java";
		//s3和s4是同一物件
		System.out.println(System.identityHashCode(s3)==System.identityHashCode(s4));//輸出true
	}
}

1. Runtime類與Java 9 的ProcessHandle
Runtime代表Java執行時環境,可以訪問JVM的相關資訊,如處理器數量,記憶體資訊等。每個Java程式都有一個與之對應的Runtime例項,應用程式通過該物件與其執行時環境連線,應用程式不能建立自己的Runtime例項,但可以通過getRuntime()方法獲取與之關聯的Runtime物件。
與System類似的是,Runtime類也提供了gc()方法和runFinalization()方法來通知系統進行垃圾回收,清理系統資源。並提供了load(String filename)和loadLibrary(String libname)載入檔案和動態連線庫.

public calss RuntimeTest{
	public static void main(String[] args){
	//獲取Java程式關聯的執行時物件
	Runtime rt = Runtime.getRuntime();
	System.out.println("處理器數量:"+ rt.availableProcessors());//輸出 8
	System.out.println("空間記憶體數:"+rt.freeMemory() );//輸出 125561504
	System.out.println("總記憶體數:"+ rt.totalMemory());//輸出 128974848
	System.out.println("可用最大記憶體數:"+rt.maxMemory());//輸出 1886912512
	}
}

Runtime類還有一個功能——可以直接單獨啟動一個程序來執行作業系統的命令

public class ExecTest{	
	public static void main(String[] args) throws Exception{
		Runtime rt = Runtime.getRuntime();
		//執行記事本程式
		rt.exec("notepad.exe");
	}
}

通過exec啟動平臺上的命令之後,它就變成了一個程序,java使用Process來代表程序。Java 9還新增了一個ProcessHandle.Info類,用於獲取程序的命令,引數,啟動時間,累計執行時間,使用者等資訊,下面示範了通過ProcessHandle獲取程序的相關資訊。

public class ProcessHandleTest{
	public static void main(String[] args)throws Exception{
		Runtime rt = Runtime.getRuntime();
		//執行記事本程式
		Process p = rt.exec("notepad.exe");
		ProcessHandle ph = p.toHandle();
		System.out.println("程序是否執行:" + ph.isAlive());
		System.out.println("程序ID:" + ph.pid());
		System.out.println("父程序:" + ph.parent());
		//獲取ProcessHandle.info資訊
		ProcessHandle.info info = ph.info();
		//通過ProcessHandle.info;資訊獲取程序相關資訊
		System.out.println("程序命令:" + info.command());
		System.out.println("程序引數:"+ info.arguments());
		System.out.println("程序啟動時間:"+info.startInstant()");
		System.out,println("程序累計執行時間:" + info.totalCpuDuration());
		//通過CompletableFuture在程序結束時執行某個任務
		CompletableFuture<ProcessHandle>cf = ph.onExit();
		cf.thenRunAsync(()->{
			System.out.println("程式退出");	
		})
		Thread.sleep(5000);
	}
}