与用户互动与系统相关(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);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_43215734/article/details/85848382
7.2