与用户互动
如果一个程序总是按既定的流程运行,无需处理用户动作,这个程序总是比较简单的。实际上,绝大部分程序都需要处理用户动作,包括接受用户的键盘输入、鼠标动作等。因为现在还未涉及图像用户接口(GUI)编程,这里讲述程序如何获得用户的键盘输入。
运行Java程序的参数
会议Java程序的入门——main方法的方法签名:
// Java程序入口: main方法
public static void main(String[] args){....}
这里解释main方法为什么采用这个方法签名:
- public修饰符:Java类由JVM调用,为了让JVM可以自由调用这个main方法,所以使用public修饰符把这个方法暴露出来。
- static修饰符:JVM调用这个主方法时,不会先创建该主类的对象,然后通过对象来调用该主方法。JVM直接通过该类来调用主方法,因此使用static修饰该主方法。
- void返回值:因为主方法被JVM调用,该方法的返回值将返回给JVM,这没有任何意义,因此main方法没有返回值。
上面方法中还包括一个字符串数组形参,根据方法调用的规则:谁调用方法,谁负责为形参赋值。也就是说,main方法有JVM调用,即args形参应该有JVM负责赋值。但JVM怎么知道如何为args数组赋值呢?看下面程序:
public class ArgsTest {
public static void main(String[] args){
//输出args数组的长度
System.out.println(args.length);
//遍历args数组的每个元素
for (String arg : args){
System.out.println(arg);
}
}
}
上面程序几乎是最简单的“HelloWorld”程序,只是这个程序增加了输出args数组的长度,遍历args数组元素的代码。使用java ArgsTest命令运行上面程序,看到程序仅仅输出一个0,这表明args数组是一个长度为0的空数组——这是合理的。因为计算机是没有思考能力的,它只能忠实地执行用户交给它的任务,既然我们没有给args数组设定参数值,那么JVM就不知道args数组的元素,所以JVM将args数组设置成一个长度为0的数组。
如果某参数本身包含了空格,则应该将该参数用双引号("")括起来,否则JVM会把这个空格当成参数分隔符,而不是当成参数本身。例如,采用如下命令来运行上面程序:
java ArgsTest "Java Spring"
看到args数组的长度是1,只有一个数组元素,其值是Java Spring。
使用Scanner获取键盘输入
运行Java程序时传入参数只能在程序开始运行之前就设定几个固定的参数。对于更复杂的情形,程序需要在运行过程中取得输入。
使用Scanner类可以很方便的获取用户的键盘输入,Scanner是一个机遇正则表达式的文本扫面器,它可以从文件、输入流、字符串中解析出基本类型值和就收文件、输入流、字符串作为数据源,用于从文件、输入流、字符串中解析数据。
Scanner主要提供了两个方法来扫描输入:
- hasNextXxx():是否还有下一个输入项,其中Xxx可以是Int、Long等代表基本数据类型的字符串。如果需要判断是否包含下一个字符串,则可以省略Xxx。
- nextXxx():获取下一个输入项。Xxx的含义与前一个方法中的Xxx相同。
在默认情况下,Scanner使用空白(包括空格、Tab空白、回车)作为多个输入项之间的分隔符。下面程序使用Scanner来获得用户的键盘输入:
import java.util.Scanner;
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都把它当成一个输入项。在这种需求下,我们可以把Scanner的分割符设置为回车符,不再使用默认的空白作为分隔符。
Scanner的读取操作可能被阻塞(当前执行顺序流暂停)来等待信息的输入。如果输入源没有结束,Scanner又读不到更多输入项时(尤其在键盘输入时比较常见),Scanner的hasNext()和next()方法都有可能阻塞,hasNext()方法是否阻塞与和其他相关的next()方法是否阻塞无关。
为Scanner设置分割符使用useDelimiter(String pattern)方法即可,该方法的参数应该是一个正则表达式。
事实上,Scanner提供了两个简单的方法来逐行读取。
- boolean hasNextLine():返回输入源中是否还有下一行。
- String nextLine():返回输入源中下一行的字符串。
Scanner不仅可以获取字符串输入项,也可以获取任何基本类型的输入项,如下程序所示:
import java.util.Scanner;
public class ScannerLongTest {
public static void main(String[] args){
//System.in代表标准输入,就是键盘输入
Scanner sc=new Scanner(System.in);
//判断是否有下一个long整数
while(sc.hasNextLong()){
//输出输入项
System.out.println("键盘输入的内容是:"+sc.nextLong());
}
}
}
注意上面程序中粗体字代码部分,正如通过hasNextLong()和nextLong()两个方法,Scanner可以直接从输入流中获得long型整数输入项。与此类似的是,如果需要获取其他基本类型的输入项,则可以使用相应的方法。
Scanner不仅能读取用户的键盘输入,还可以读取文件输入。只要在创建Scanner对象时传入一个File对象作为参数,就可以让Scanner读取该文件的内容:
import java.io.File;
import java.util.Scanner;
public class ScannerFileTest
{
public static void main(String[] args)
throws Exception
{
//将一个File对象作为Scanner的构造器参数,Scanner读取文件内容
Scanner sc=new Scanner(new File("ScannerFileTest.java"));
System.out.println("ScannerFileTest.java文件内容如下:");
//判断是否还有下一行
while(sc.hasNextLine()){
//输出文件中的下一行
System.out.println(sc.nextLine());
}
}
}
上面程序创建Scanner对象时传入一个File对象作为参数。
使用BufferedReader获取键盘输入
Scanner可以非常方便地获取键盘输入,在Java5之前,程序通常使用BufferedReader类来读取键盘输入。
BufferedReader是Java IO流中的一个字符、包装流,它必须建立在另一个字符流的基础之上。但标准输入:System.in是字节流,程序需要使用转换流InputStreamReader将其包装成字符流。
一旦获得了BufferedReader对象之后,就可以调用该对象的readLine()方法来逐行读取键盘输入;如果没有键盘输入,readLine()方法将会阻塞来等待键盘输入。如下程序所示:
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class KeyboardInTest {
public static void main(String[] args)
throws Exception{
//以System.in字节流作为基础,创建一个BufferReader对象
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
String line=null;
//逐行读取键盘输入
while ((line=br.readLine())!=null){
System.out.println("用户键盘输入是:"+line);
}
}
}
使用BufferedReader可以逐行读取用户的键盘输入,用户的每次键盘输入都被BufferedReader当成String对象。与Scanner不同的是,BufferedReader不能读取基本类型输入项,它总是读取String对象。
java实例练习
将数字格式化为货币字符串
数字可以标识货币、百分比、积分、电话号码等。就货币而言,在不同的国家里会以不同的格式来定义。本实例将通过接收用户输入的数字,将这个数字转换为不同国家的货币格式,然后在控制台中输出这些货币的格式。
1.
新建项目CurrencyFormat,并在其中创建一个CurrencyFormat.java文件。在该类的主方法中创建标准输入流的扫描器对象,接收用户输入的数字,通过NumberFormat类的format()方法把接收的数字格式转换为货币字符串:
package CurrencyFormat;
import java.text.NumberFormat;
import java.util.Locale;
import java.util.Scanner;
public class CurrencyFormat {
public static void main(String[] args){
Scanner scan = new Scanner(System.in); //创建标注输入流扫描器
System.out.println("请输入一个数字:");
double number = scan.nextDouble(); //获取用户输入数字
System.out.println("改数字用Locale类的以下常量作为格式化对象的构造参数,将获得不同的货币格式:"); //创建格式化对象
NumberFormat format = NumberFormat.getCurrencyInstance(Locale.CHINA); //输出格式化货币格式
System.out.println("Locale.CHINA:" + format.format(number));
format = NumberFormat.getCurrencyInstance(Locale.ENGLISH);
System.out.println("Locale.ENGLISH:" + format.format(number));
}
}
数字格式化是本案例的关键点,实例中应用NumberFormat类实现了数字格式化,这个类时一个抽象类,但是可以通过其静态方法获取内部实现类的实例对象,本实例获取了货币的格式对象。
格式化对象可以指定语言环境,在Java中使用Local类的对象来表示,在该类中包含了三个语言环境。通过它可以获取国际化的字符串信息,如货币、时间等。
实现不同字符串的连接
字符串连接是程序中经常进行的操作,用来将多个字符串来接在一起形成一个字符串。我们可以使用加法(+)运算符或是append()方法来实现这种连接。
1.
新建项目StringConnection,并在其中创建一个StringConnection.java文件。在该类的主方法中创建三种字符串的连接方法,实现对不同字符串的连接。核心代码如下所示:
package StringConnection;
public class StringConnection {
public static void main(String[] args){
String s1 = "hello" + ", " + "world";
System.out.println("使用加法运算符输出:" + s1);
// 构造StringBuffer对象,并添加一些字符串
StringBuffer sb2=new StringBuffer();
sb2.append("hello"); //使用
sb2.append(",");
sb2.append(' ');
sb2.append("world");
// 将StringBuffer值转换为字符串,并输出
String s2 = sb2.toString();
System.out.println("使用append()方法输出:" + s2);
// 重复上面的工作使用更简单的方法
StringBuffer sb3=new StringBuffer().append("hello").append(',').append(' ').append("world");
System.out.println("append()方法的另一种形式输出:" + sb3.toString());
}
}