1、第7讲 基于文本的应用程序,命令行参数,7.1 命令行参数 命令行参数是在运行时传递给 Java 应用程序的参数。 每个命令行参数放置在 args 数组中,其传递给 static main 方法。例如:public static void main(String args) 程序示例: public class TestArgspublic static void main(String args)for(int i=0;iargs.length;i+)System.out.println(“args“+i+“ is:“+argsi); 当运行:D:java TestArgs my first
2、 这里my和first就是传递给main的参数。,系统属性,7.2 系统属性 在Java应用程序运行时,特别是需要在跨平台工作环境下运行时,需要确定操作系统类型、用户JDK版本和用户工作目录等随工作平台变化的信息,来保证程序正确运行。一般情况下,可以利用JDK提供的系统属性类(Properties)中的方法,快速地获取工作环境信息。另外,程序开发人员还可以定义与应用程序相关的系统属性文件,在用户程序执行过程中动态地加载程序员定义的属性文件来控制程序运行。 系统属性包括关于当前用户、Java 运行时的当前版本和用来分隔文件路径名称的组成部分的字符的信息。 System.getProperties
3、() 方法返回 Properties 对象。System.getProperty(String) 方法返回表示名称为 property 值的字符串。System.getProperty(String, String) 方法使您提供默认字符串值(第二个参数),其在如果名称为 property 不存在的情况下返回。,控制台 I/O,7.3 控制台I/O 应用程序使用控制台 I/O 与用户进行交互。 Java 2 SDK 支持在 java.lang.System 类中具有三个公共变量的控制台 I/O: 变量 System.out 使您向标准输出写入。它是类型 PrintStream 的对象。 变量
4、System.in 使您从标准输入读取。它是类型 InputStream 的对象。 变量 System.err 使您向标准错误写入。它是类型 PrintStream 的对象。,7.3.1标准输入输出 1、标准输出(System.out) (1) print和println方法 println将常量、变量或表达式的值输出到屏幕。可以有0个或1个参数。0个参数:则输出一回车换行,光标移动到下一行行首;1个参数:该参数可以是各种类型,结果都转换成相应的字符串类型输出。输出给定所有内容后,输出一个回车换行。例如:int i=3,j=4;System.out.println(i+j); /输出为:7Sy
5、stem.out.println(“分别为”+i+j); /输出为:分别为34在参数输出时,“+”符号可以作为加运算符使用,也可以作为多个输出项 的连接符号使用。print方法需要一个参数来输出,可用的参数类型与println相同,区别在 于输出参数的值后不输出回车换行。,(2) printf和format格式输出方法 要控制输出数据的格式,可在这两个方法的参数中对每一输出项,使用格式符进行格式控制。例如: System.out.printf(“a=%dtb=%ftc=%c“,a,b,c); 或:System.out.format(“a=%dtb=%ftc=%c“,a,b,c); 为了能得到正
6、确输出的数据和格式,不同类型的输出项应使用不同的格式符来进行格式控制输出。适用于基本类型和字符串类型输出的常用格式符有:b 布尔型数据 c 字符d 十进制整数 f 十进制实数s 字符串 %n 输出换行符e或g 科学记数法形式的十进制实数 第一个参数为输出格式,字符串形式;后面参数列出要输出的数据变量。,2、标准输入(System.in) (1)read方法 read()从键盘输入整数值在0255之间的byte值(int类型)。若程序运行时需要从键盘输入其他类型的数据,则需要使用Java提供的输入/输出功能。不论需从键盘输入何种类型的数据,Java从键盘接收数据都是以字符串的形式进行,再根据实际
7、数据的需要进行类型转换。 应用本方法时要使用Java的异常处理机制。,例、键盘输入一个字符,并输出它在Unicode字符集中的前一个字符和后一个字符: import java.io.*; / 引入java.io包 class CharDemo public static void main(String args) char c,c1,c2;System.out.println(“请输入一个字符,并按回车键!“);try c=(char)System.in.read(); / 输入字符c1=(char)(c-1); c2=(char)(c+1); System.out.println(“ :
8、“ + c1);System.out.println(“ : “ + c2); catch (IOException e) e.printStackTrace();/打印异常信息 ,(2)使用next方法 若使用Java提供java.util.scanner类中的各种next方法,可从键盘输入各种基本类型和字符串类型的数据。不论需从键盘输入何种类型的数据,Java从键盘接收数据都是以字符串的形式进行,next方法要根据输入数据的类型来选用。有nextByte、nextInt、nextShort、nextLong、nextFloat、nextDouble、nextBoolean和next(以空格
9、和回车为结束标记的字符串输入)等适用于不同类型数据输入的方法。使用next方法进行键盘输入的例子如下。,例、从键盘输入三角形的三个边长,求这个三角形的面积: import java.util.Scanner; class TriArea public static void main(String args)System.out.printf(“请输入三角形的三边长:“);Scanner sc = new Scanner(System.in);double a = sc.nextDouble();double b = sc.nextDouble();double c = sc.nextDoub
10、le();double t=(a+b+c)/2.0;double s=Math.sqrt(t*(t-a)*(t-b)*(t-c);System.out.format(“三角形的面积为%fn“,s);/String s=sc.next();/System.out.format(“三角形的面积为%sn“,s); ,7.3.2对话框输入输出 1、使用showMessageDialog(输出信息对话框)方法 Java中的JOptionPane.showMessageDialog方法是用对话框的形式来进行输出,支持一个输出项,在输出内容较多时,也可用字符串连接的形式来输出。,import javax.s
11、wing.JOptionPane; import static java.lang.Math.*; class DialogDemo public static void main(String args)String s1=“sin30度的值 = “;s1 = s1 + sin(PI*30/180);s1 = s1 + “ncos30度的值 = “;s1 = s1 + cos(PI*30/180);JOptionPane.showMessageDialog(null,s1); ,2使用showInputDialog方法输入信息 使用Java提供的javax.swing.JOptionPane
12、类中的showInputDialog方法,可从输入信息对话框中输入一个字符串对象。需要时,可进行类型转换,得到所需类型的输入数据。 例、使用对话框进行程序的输入输出。输入一个代表年号的整数,求这一年的元旦是星期几? 元旦是星期几的计算公式(设年号为y,按整数运算):s = y + (y-1) / 4 - (y-1) / 100 + (y-1)/ 400; s除以7的余数即为星期数,余数为0表示星期日。,import javax.swing.JOptionPane; class YuanDanpublic static void main(String args)int s,y;y=Intege
13、r.parseInt(JOptionPane.showInputDialog(“请输入一个年号:“);s = y + (y-1) / 4 - (y-1) / 100 + (y-1)/ 400;s = s % 7;JOptionPane.showMessageDialog(null,y + “ 年的元旦是星期 “+ s); /parseInt函数把参数解析为int类型 程序运行的输入输出界面及结果见右图。,集合 API,7.4 集合API 假设要存储许多雇员,不同的雇员的区别仅在于雇员的身份证号。我们可以通过身份证号来顺序存储每个雇员,但是在内存中实现呢?是不是要准备足够的内存来存储1000个雇
14、员,然后再将这些雇员逐一插入?如果已经插入了500条记录,这时需要插入一个身份证号较低的新雇员,该怎么办呢?是在内存中将500条记录全部下移后,再从开头插入新的记录? 还是创建一个映射来记住每个对象的位置?当决定如何存储对象的集合时,必须考虑如下问题。 对于对象集合,必须执行的操作主要以下三种:添加新的对象删除对象查找对象 在内存中建立对象集合后,必须确定如何定位特定对象。可建立一种机制,利用该机制可根据某些搜索条件(例如身份证号)直接定位到目标对象;否则,便需要遍历集合中的每个对象,直到找到要查找的对象为止。,7.4.1 集合框架集合是由一组用来操作对象的接口组成。不同接口描述不同类型的组。
15、在很大程度上,理解了接口,就理解了框架。框架分类: Collection(集合) 对象之间没有指定的顺序,允许元素重复。 Set(集) 对象之间没有指定的顺序,不允许重复元素 List(列表) 对象之间有指定的顺序,允许重复元素,并引入位置下标。 Map (映射) 接口用于保存关键字(Key)和数值(Value)的集合,集合中的每个对象加入时都提供数值和关键字。Map 接口既不继承 Set 也不继承 Collection。,Java 2 框架还引入了六个集合实现,如下表所示。,7.4.2 Collection接口Collection 接口用于表示任何对象或元素组。想要尽可能以常规方式处理一组元
16、素时,就使用这一接口。Collection 是List和Set 的父类。并且它本身也是一个接口。它定义了作为集合所应该拥有的一些方法。 注意:集合必须只有对象,集合中的元素不能是基本数据类型。Collection接口支持如添加和除去等基本操作。设法除去一个元素时,如果这个元素存在,除去的仅仅是集合中此元素的一个实例。boolean add(Object element) boolean remove(Object element) Collection 接口还支持查询操作:int size() boolean isEmpty() boolean contains(Object element)
17、 Iterator iterator(),import java.util.*; public class CollectionToArray public static void main(String args) Collection collection1= new ArrayList(); /创建一个集合对象collection1.add(“000“);/添加对象到Collection集合中collection1.add(“111“);collection1.add(“222“);System.out.println(“集合collection1的大小:“+collection1.si
18、ze();System.out.println(“集合collection1的内容:“+collection1);collection1.remove(“000“);/从集合collection1中移除掉 “000“ 这个对象System.out.println(“集合collection1移除 000 后的内容:“+collection1);System.out.println(“集合collection1中是否包含000 :“+collection1.contains(“000“);System.out.println(“集合collection1中是否包含111 :“+collectio
19、n1.contains(“111“);Collection collection2= new ArrayList();collection2.addAll(collection1); /将collection1 集合中元素全部都加到collection2中System.out.println(“集合collection2的内容:“+collection2);collection2.clear();/清空集合 collection1 中的元素System.out.println(“集合collection2是否为空 :“+collection2.isEmpty();/将集合collection1
20、转化为数组Object s= collection1.toArray();for(int i=0;is.length;i+)System.out.println(si); ,运行结果为: 集合collection1的大小:3 集合collection1的内容:000, 111, 222 集合collection1移除 000 后的内容:111, 222 集合collection1中是否包含000 :false 集合collection1中是否包含111 :true,迭代器,Collection的迭代器 迭代是检索集合中的每个元素的过程。 一组 iterator 是无序的。List 的 List
21、Iterator 可向前扫描(使用 next 方法)或向后扫描(使用 previous 方法)。 List list = new ArrayList(); Iterator elements = list.iterator(); while ( elements.hasNext() ) System.out.println(elements.next(); ,使用增强的 for 循环具有以下特征: 对集合简化的迭代 更短、更清楚和更安全 对数组有效 当使用嵌套的循环时更简单 去除了迭代器的缺点 迭代器更容易出错: 迭代器变量在每个循环中出现三次。 这为代码提供了出错的机会。,迭代器(Itera
22、tor)本身就是一个对象,它的工作就是遍历并选择集合序列中的对象。 Collection 接口的 iterator() 方法返回一个 Iterator。Iterator 和您可能已经熟悉的 Enumeration 接口类似。使用 Iterator 接口方法,可以从头至尾遍历集合,并安全的从底层 Collection 中除去元素。,7.4.3 Set集合 Java中Set的概念和数学中的集合(set)一致,都表示一个集内可以存放的元素是不能重复的。 Set 接口继承Collection 接口,而且它不允许集合中存在重复项。所有原始方法都是现成的,没有引入新方法。具体的 Set 实现类依赖添加的对
23、象的 equals() 方法来检查等同性。,import java.util.*; public class SetExample public static void main(String args) Set set = new HashSet(); set.add(“one“); set.add(“second“); set.add(“3rd“); set.add(new Integer(4); set.add(new Float(5.0F); set.add(“second“); set.add(new Integer(4); System.out.println(set); ,7.4
24、.4 List集合 List是容器的一种,表示列表的意思。当我们不知道存储的数据有多少的情况,我们就可以使用List 来完成存储数据的工作。 List 就是列表的意思,它是Collection 的一种,即继承了 Collection 接口,以定义一个允许重复项的有序集合。 该接口不但能够对列表的一部分进行处理,还添加了面向位置的操作。List 是按对象的进入顺序进行保存对象,而不做排序或编辑操作。它除了拥有Collection接口的所有的方法外还拥有一些其他的方法。,import java.util.*; public class ListExample public static void
25、main(String args) List list = new ArrayList(); list.add(“one“); list.add(“second“); list.add(“3rd“); list.add(new Integer(4); list.add(new Float(5.0F); list.add(“second“); list.add(new Integer(4); System.out.println(list); ,7.4.5 Map映射 数学中的映射关系在Java中就是通过Map来实现的。它表示,里面存储的元素是一个对(pair),我们通过一个对象,可以在这个映射
26、关系中找到另外一个和这个对象相关的东西。 Map 接口不是 Collection 接口的继承。而是从自己的用于维护键-值关联的接口层次结构入手。按定义,该接口描述了从不重复的键到值的映射。 我们可以把这个接口方法分成三组操作:改变、查询和提供可选视图。 改变操作允许您从映射中添加和除去键-值对。键和值都可以为 null。但是,您不能把 Map 作为一个键或值添加给自身。Object put(Object key,Object value):用来存放一个键-值对。Object remove(Object key):根据key(键),移除一个键-值对,并将值返回。,Map的常用实现类,import
27、 java.util.*; public class MapTest public static void main(String args) Map map1 = new HashMap();Map map2 = new HashMap();map1.put(“1“,“aaa1“); map1.put(“2“,“bbb2“); map2.put(“10“,“aaaa10“); map2.put(“11“,“bbbb11“);/根据键 “1“ 取得值:“aaa1“ System.out.println(“map1.get(“1“)=“+map1.get(“1“);/ 根据键 “1“ 移除键值对
28、“1“-“aaa1“System.out.println(“map1.remove(“1“)=“+map1.remove(“1“); System.out.println(“map1.get(“1“)=“+map1.get(“1“);map1.putAll(map2);/将map2全部元素放入map1中map2.clear();/清空map2System.out.println(“map1 IsEmpty?=“+map1.isEmpty();System.out.println(“map2 IsEmpty?=“+map2.isEmpty();System.out.println(“map1 中
29、的键值对的个数size = “+map1.size();System.out.println(“KeySet=“+map1.keySet();/包含的键的 Set 视图 System.out.println(“values=”+map1.values();/包含值的Collection视图System.out.println(“entrySet=”+map1.entrySet();/包含映射关系的set视图System.out.println(“map1 是否包含键:11 = “+map1.containsKey(“11“); System.out.println(“map1 是否包含值:aa
30、a1 = “+map1.containsValue(“aaa1“); ,运行输出结果为: map1.get(“1“)=aaa1 map1.remove(“1“)=aaa1 map1.get(“1“)=null map1 IsEmpty?=false map2 IsEmpty?=true map1 中的键值对的个数size = 3 KeySet=10, 2, 11 values=aaaa10, bbb2, bbbb11 entrySet=10=aaaa10, 2=bbb2, 11=bbbb11 map1 是否包含键:11 = true map1 是否包含值:aaa1 = false,7.5 Ja
31、va泛型,在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是简单安全:在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提
32、高代码的重用率。,7.5.1定义泛型(Generics)类,泛型相当于类中一种特殊的类型,这种类型的特点是在实例化该类时可指定为某个具体的实际类型。 声明包含泛型的类的格式如下:访问修饰符 class 类名泛型1 泛型成员1;泛型2 泛型成员2;/ /称之为泛型类 声明中的泛型1、泛型2等等泛型符号可以是任意合法的Java标识符。,泛型类的声明示例,/*此处声明了一个包含泛型T的泛型类,T代表所有可能的类型,而T的实际类型在Generic类实例化时指定。*/ public class Generic /这里T称为形式类型参数private T f; /f为泛型成员public void set
33、F(T f) /setF方法的参数类型为泛型T this.f = f; public T getF() /getF方法的返回类型为泛型Treturn f; ,创建泛型类的实例时,可以使用一对尖括号指定泛型的真正类型。,/f1中的泛型T在此指定为Boolean类型 Generic f1 = new Generic();/f2中的泛型T在此指定为Integer类型 Generic f2 = new Generic(); /f1的setF方法只能接受Boolean类型数据 f1.setF(new Boolean(true); Boolean b = f1.getF(); System.out.pri
34、ntln(b); /f2的setF方法只能接受Integer类型的数据 f2.setF(new Integer(10); Integer i = f2.getF(); System.out.println(i);,泛型类实例化时,并不一定要指明泛型对应的实际类型,此时会使用Object作为泛型的默认类型编译时编译器会发出警告:,Generic f3 = new Generic(); f3.setF(new Boolean(false);,Note: Generic.java uses unchecked or unsafe operations. Note: Recompile with -X
35、lint:unchecked for details.,实例化时的泛型的默认类型,建立类型为泛型类的数组,如果要建立泛型类的数组,需要注意new关键字后面不要加入泛型的实际类型名,如下所示: Generic gs; /声明泛型类的数组 /先对泛型数组进行初始化 gs=new Generic5; /不要写成new Generic5 /再分别为每一个数组元素进行初始化 gs0=new Generic();/为第一个数组元素赋值 /,public class Generic2 private T1 f1;private T2 f2;/ ,/给出泛型T1,T2的实际类型 Generic f =new
36、Generic ();/没有给出T1,T2的实际类型 Generic f1=new Generic(); /T1,T2将被默认为是Object类型,包含多个泛型的类定义示例,包含有两个泛型定义的类声明和实例化:,泛型成员的使用,在泛型类中的泛型成员不能直接实例化,其实例必须要通过方法的参数传递给泛型成员: public class Generic3 private T array; /此处不能用new T实例化array public void setArray( T array ) this.array = array; public T getArray() return array; ,
37、通过方法的泛型参数,将数组的实例传递给类中的泛型数组: String strs = “caterpillar“, “momor“, “bush“; Generic3 f = new Generic3(); /向泛型成员array传递实际的字符串数组 f.setArray(strs); /读取泛型成员array的值,将其赋给字符串数组变量strs strs = f.getArray(); /此时array的类型为字符串数组,泛型成员的可用方法,由于泛型类型只有在类实例化后才能确定,类中的泛型成员只能使用Object类型中的方法:,class GenericT f;void setF(T f) t
38、his.f=f; /void doSome()/*getClass和toString都是Object中的方法*/System.out.println(f.getClass().getName();System.out.println(f.toString(); ,extends关键字用来指定泛型的上限,在实例化泛型类时,为该泛型指定的实际类型必须是指定类的子类或指定接口的子接口:在限定泛型的类型时,无论要限定的是接口或是类,都要 使用extends关键词。,import java.util.List; public class ListGeneric private T list;public
39、 void setList(T list) this.list = list;public T getList() return list; ,7.5.2 限制泛型上限类型,ListGeneric f1 = new ListGeneric(); ListGeneric f2 = new ListGeneric();,限制泛型上限类型的示例,/如果不是List的类型,编译时就会发生错误 ListGeneric f3 = new ListGeneric();,type parameter java.util.HashMap is not within its bound ListGeneric f
40、3 = new ListGeneric();,默认的泛型限制类型 定义泛型类别时,如果只写以下代码: public class Generic / 相当于下面的定义方式 public class Generic /,限定泛型上限后的成员可用方法,泛型类型的上限一经限定,类中的泛型成员就可使用上限类型中的方法和其他可用成员:,import java.util.List; import static java.lang.System.out;/静态导入public class ListGeneric private T list;public void setList(T list) this.l
41、ist = list;public void doSome()/add、get方法都是List接口中定义的方法list.add(new Integer(0);out.println(list.get(0);/此处省略了System ,import xxxx 和 import static xxxx的区别: 前者一般导入的是类文件如import java.util.Scanner; 后者一般是导入静态的方法,import static java.lang.System.out;,综合说明示例 public class Test 形式类型参数的说明: E - 可以是任何对象类型; S - 要求实际
42、类型参数必须实现Serializable接口; A 要求实际类型参数必须同时实现ObjectInput和ObjectOutput接口; C 要求是一个实现List接口的容器类,并且其所包含的对象类型必须是E。,泛型类之static由于使用不同实际类型参数的泛型类共享同一个类定义,而静态类对象以及静态类方法也是被同一个类定义共享。因此,在泛型对象定义中,静态类对象以及静态类方法中不能出现泛型类的形式类型参数。否则将出现编译错误。例如: public class Testprivate static E invalidType1;private static List invalidType2;p
43、ublic static void invalidMethod(E e)System.out.println(e);private static List validType; 上述所有invalid开头的静态变量和方法,使用了泛型类定 义的形式类型参数,都非法。而validType,则是可以的。,Generic f1 = new Generic(); Generic f2 = new Generic(); f1=f2; /发生编译错误,incompatible types found : Generic required: Generic f1 = f2;,7.5.3 泛型类实例之间的赋值,
44、同一泛型类,如果实例化时给定的实际类型不同,则这些实例的类型是不兼容的,不能相互赋值。又,泛型类相同而实际类型参数不同,即便这两个泛型类的实际类型参数具有继承关系,这两个泛型类也不能相互赋值。,泛型中的Object类型兼容性,Object是所有类的父类,因此,所有的类型的实例都可赋值给声明为Object类型的变量在实例化泛型类时,将泛型指定为Object类型却不存在着和其他类型之间的兼容性:,Generic f1 = new Generic(); Generic f2 = new Generic(); Generic f=f1; /f1和f类型并不兼容,发生编译错误 f=f2; /f2和f类型
45、同样不兼容,也会发生编译错误,Boolean f1 = new Boolean(true); Integer f2 = new Integer(1); Object f=f1; /OKf=f2; /OK,泛型默认类型的实例类型兼容性,实例化泛型类时采用默认泛型类型,此时泛型类的实例和其他给定类型的泛型类实例之间存在着类型兼容性,可以直接相互赋值。使用泛型默认类型虽然可以做到类型的兼容性,但会失去泛型带来的编译时刻类型检查的优点。,Generic f1 = new Generic(); Generic f2 = new Generic(); Generic f = new Generic();/
46、默认泛型类型f = f1; /OK f = f2; /OK f1 = f; /OK f2 = f; /OK,7.5.4 泛型通配字符(Wildcard),泛型类实例之间的不兼容性会带来使用的不便。 使用泛型通配符(?)声明泛型类的变量可以解决这个问题,Generic f1 = new Generic(); Generic f2 = new Generic(); Generic f3 = new Generic();/f可代表Generic所有可能的实例 Generic f; f=f1; /OK f=f2; /OK f=f3; /OK,通配符用作方法的参数,class Util/Collecti
47、on可以匹配任何强类型集合static void printCollection(Collection c)for(Object o : c)System.out.println(o); ,通配符也可以用于方法的参数类型的声明,表示该参数可接受对应泛型类型的任意实例(在理论上来说,通配符实际也应该是用来限定传入函数的参数的 )。 以下类定义中的printCollection方法可以打印任意强类型集合中的内容,Generic f = null; f = new Generic(); /Ok . f = new Generic(); /OK/以下语句会发生编译错误,因为HashMap没有实现Lis
48、t接口 f = new Genric();,为通配符指定匹配上限(高绑定),和限制泛型的上限相似,同样可以使用extends关键字限定通配符匹配类型的上限:,incompatible types found : Generic required: Generic f = new Generic();,/将f限定为只能代表采用java.sql.Date的父类实例化的实例 Generic f = null; f=new Generic(); /Ok/ java.util.Date是java.sql.Date的父类 f=new Generic(); /Ok/错误,因为String不是java.sql
49、.Date的父类 f=new Generic();,限定通配符匹配类型的下限(低绑定),还可以使用super关键词将通配符匹配类型限定为某个类型及其父类型,Generic f = new Generic(); foo.setF(“caterpillar“); Generic immutableF = f; /可读取泛型成员f中保存的字符串实例 System.out.println(immutableF.getF();/可通过传递null参数来移除泛型成员f中保存的字符串实例 immutableF.setF(null);/*不能通过immutableF的setF方法再次传递新的实例给类中泛型成员f,所以下面这行无法通过编译。因为,编译器不能对Generic的类型参数作出足够严密的推理,以确定String传递给Generic的setF()是类型安全的,所以编译器就不允许这么做。*/ immutableF.setF(“wang“);,