第10章控制台应用程序设计——v512工作室 编辑:玄玉
注解 | 系统属性 | 给我发邮件 | 我的博客 | |
过时API | 命令行参数 | 标准输入输出 | v512工作室 | |
归档工具 | 可变参数方法 | 文件输入输出 | 中科院新科海学校 |
命令行参数
概述:在启动Java控制台应用程序时,可以一次性地向程序中传递零至多个字符串参数,这些参数被称为命令行参数
语法:java <应用程序类名> [<命令行参数>]*
命令行参数将被系统接受并静态初始化为一个一维String数组对象,然后将之作为实参传给应用程序入口方法main()
命令行参数须使用空格符及双引号分隔。使用命令行参数可以增强用户和程序之间的交互性
举例:public class TestCommandLineArgs{public static void main(String[] args){
for(int i=0;i<args.length;i++){System.out.println(args[i]);}}}
使用该命令运行程序:java TestCommandLineArgs lisa "Billy" "Mr Brown" "a""b"
输出结果:lisa Billy Mr Brown a"b
说明:该类中main()方法已经声明了一个形参:String[] args
在方法体中就可以将形参args作为一个局部变量和其它的本地声明并创建的数组一样的对待
比如说在这里用for循环遍历args数组中的每一个元素,然后打印输出每一个元素的值到屏幕上
在命令行中输入的三个参数被系统自动封装成一个String数组类型的对象并作为实参传给了main()方法的形参args
方法main()是启动程序时由系统自动调用的,在方法体中for循环遍历args数组时便三个元素并把它们输出到屏幕上
需要注意的是:输入的第三个参数不单加了空格,还用了英文双引号做分隔。那时因为这里面包含了一个特殊的符号,即空格
缺省情况下,若此处不加双引号,那么将以空格作为分隔符将它切割成两个参数
当然对于其它参数,也可以加上英文双引号来分隔
双引号的转义:如果命令行参数中包含双引号,为了避免它被解析成分隔符,则需要用两个双引号对其进行转义
解析的时候,两个挨着的双引号代表一个真正的双引号被输出
需要说明的是:启动程序时,如果没有给出任何命令行参数,系统也会创建一个长度为0的一维数组对象
注意它仍然是一个对象,只不过里面包含了零个元素。然后将它传给了main()中的形参args
这样做是为了避免出现空指针异常。此时参数args的值并不是null
比如System.out.println(args.length())则输出结果为0。这是在不给出任何命令行参数时执行的结果
系统属性
概述:Java系统属性以键-值对的形式存在。由属性名称和属性值两部分组成,均为字符串形式
系统属性记录了当前OS和JVM等相关的环境信息
说明:可使用System.getProperties()方法获得Properties类的对象,其中包含了所有可用的系统属性信息
可使用System或Properties类的getProperty(String)方法获得特定系统属性的属性值。此时需要指定属性名
可使用System或Properties类的setProperty(String,String)方法添加新的系统属性信息。此时需要指定属性名和属性值两部分
在命令行运行Java程序时可使用-D选项添加新的系统属性。格式为java -D属性名=属性值 类名
举例:import java.util.Properties; //属性类型Properties可以封装多条(包括属性名属性值)系统属性信息
import java.util.Enumeration; //Enumeration是一个接口。在后面的集合框架中还会再次介绍到
public class TestSystemProperties{public static void main(String[] args){
Properties ps=System.getProperties(); //释① //声明Properties类型的变量。注意用的不是它的new方式,而是System的静态方法
ps.setProperty("myName","myValue");Enumeration pn=ps.propertyNames(); //释② //声明了一个接口类型的变量
while (pn.hasMoreElements()){ //如果pn中还有下一个元素的话。如果有,那么返回值为true,否则返回false
String pName=(String)pn.nextElement(); //释③
String pValue=ps.getProperty(pName); //释④ //根据指定的属性名返回指定的系统属性值
System.out.println(pName + "--" + pValue);}}}
释①:虽然也可以用new Properties()创建该类的对象,但这样所创建的Properties对象里面是空的,包含了零条属性
而我们用System类的静态方法getProperties()创建对象,能够返回一个封装了当前所有系统属性信息的Properties对象
释②:显然不可能用new调用它的构造方法真的去创建一个接口类型的对象。我们用的是Properties类型对象的成员方法propertyNames()
propertyNames()能够创建一个Enumeration接口的实现类的对象并返回,该对象封装了ps所封装的所有系统属性名,相当于一半的信息
接下来我们遍历这个Enumeration接口类型或者说接口实现类型的对象pn
释③:当含有下一个元素时,我们取用下一个元素,即nextElement()。nextElement()的返回类型是Object类型
所以在这里造型成它所封装的元素真正的类型,即String类型。然后保存在变量pName中,这样就得到了一个系统属性的名字
释④:知道了名字之后,可以调用Properties类的getProperty()方法。当然这里也可以调用System类的getProperty()方法
补充:运行时添加系统属性:java -Dmmmm=nnnn TestSystemProperties注意D与属性名之间是挨着的
添加系统属性成功。所添加的系统属性名为mmmm,添加的系统属性值为nnnn
实际上它不是命令行参数,因为它并不是放在类名的后面,而是放在了java和应用程序类名中间
这个我们也称之为虚拟机参数。也就是说是传给java虚拟机的一个参数
用户自己添加的系统属性(或者说属性)只是在本次运行过程中有效,JVM一旦关闭了的话,如果再由其它的程序启动
我们就找不到用户自定义的那些属性了。这时可以使用Properties类的load()和store()方法将属性导入或导出了
用途:系统属性在应用开发中,直接用到它的次数并不多。后面学到JDBC的时候,可以通过系统属性的方式来加载某一种数据库的驱动程序
在JavaWeb编程的时候,我们可能会用到系统属性来指定收发邮件的代理服务器的IP地址和端口号。除此之外我们很少直接用到系统属性
标准输入输出
控制台程序:以键盘作为标准输入设备,以屏幕或字符型窗口(25行80列黑屏白字的窗口)作为默认标准输出设备
来进行数据的显示和输入的一种程序的运行方式,我们称之为控制台程序
控制台输入输出(也称为标准输入输出)是应用程序的基本功能
主要的用法:System.in提供从标准输入读入数据的功能(java.io.InputStream类型)
System.out提供向标准输出写出数据的功能(java.io.PrintStream类型)
System.err提供向标准错误输出写出数据的功能(java.io.PrintStream类型)。默认自动关联到显示器或字符型窗口
PrintStream:print()/println()方法被进行了多次重载。包括boolean,char,int,long,float,double以及char[],Object和String
printf()方法(3.8.3.节)提供增强的数据格式化输出功能
传统的方法:此前我们曾经接触过用util包中的Scanner类的nextLine()方法读取键盘输入的信息
实际上在JDK1.5之前我们一直使用的都是下面所要介绍的传统的方法读取控制台输入
源代码如下:String s; //声明一个变量s作为中间变量 //注意isr这个对象封装了System.in参数
InputStreamReader isr=new InputStreamReader(System.in); //声明并创建InputStreamReader类型的对象
BufferedReader br=new BufferedReader(isr); //通过new调用构造方法创建BufferedReader类型的变量,并封装isr对象
try{s=br.readLine(); while(!s.equals("")){ //判断通过键盘读取的字符长度是否大于0
System.out.println("Read: "+s); //若在键盘上空回车,s则接收到一个长度为0的字符串对象,但它仍然是一个对象,不等同于null空值
s=br.readLine();} //故此时在键盘上空回车,循环即终止。若写成!s.equals("exit"),则运行时仅在输入exit后程序才退出
br.close();}catch(IOException e){e.printStackTrace();} //这里只是简单的打印输出出错时候的堆栈轨迹
源代码分析:这里需要讲解下Java语言IO流的工作原理。键盘输入的数据将会以字节的形式由操作系统接收,然后转发给相应的当前运行着的应用程序
JDK中的System类的静态in属性,其实它本身也提供了读取键盘输入数据的功能。比如它有Read方法。它每次能读一个或多个字节
也就是说System.in能够以字节为单位读取键盘输入的数据。但是我们并不喜欢这种方式,因为以字节的方式读取数据,并不方便
如果我们输入的字符中包含了中文信息或多字节的字符等,那么这些字符将会被切碎成多个字节,可能会出现乱码
所以我们将它封装成一个功能较强的InputStreamReader类型的对象,它里面定义的Read方法能够实现以字符为单位读取它所相关联的数据源
这里的isr并没有直接关联到数据源,而是通过它所封装的System.in关联到键盘
也就是说isr并不直接读取键盘输入的数据,它会多次调用System.in对象的Read方法,读取一个或多个字节,然后将它们组成字符并返回
但我们仍然不喜欢以字符为单位直接单个字符的去读取键盘输入,我们希望以更大的颗粒,更有效的方式来实现这样的数据输入功能
那么比较推荐的一种方式就是把它再一次封装,功能进行增强,BufferedReader类提供了增强的readLine()方法
BufferedReader对象br可以调用readLine()方法每次从它所关联的数据源中读取一行字符串,显然功能得到了增强
但是需要说明的是br不是直接关联到数据源(比如这里的键盘),它是封装了isr对象,利用isr间接关联到键盘
它多次调用isr的读取字符的功能连续读取多个字符,直到遇到换行或换行加回车符为止,然后将它们组装成一个字符串并返回
所以在这里进行了两个层次的封装,目的是通过高层次类型的对象调用或者封装了低层的对象的比较弱的功能
可以理解成是一种功能或者任务的分解,然后低层对象isr又调用了更低级的System.in对象的读字节的功能
最终实现了将读取的字节组装成字符,再将字符组装成字符串,并以行为单位读取键盘输入数据的效果
最后的br.close()关闭了br对象,实际上是关闭br和isr的关联,同时也关闭了isr和System.in的关联以及System.in和键盘的关联
当前程序只是实现了简易的打字的功能,我们并没有把接收到的数据保存到文件中或者数据库中,而是直接简单的回显到屏幕上
文件输入输出
操作概述:java.io包中定义多个与数据输入输出功能有关的类,也包括提供文件操作功能的File类
java.io.FileReader类:提供read()方法以字符为单位从文件中读入数据
java.io.FileWrite类:提供write()方法以字符为单位向文件写出数据
java.io.PrintWriter类:提供print()和println()方法以行为单位写出数据
java.io.BufferedReader类:提供readLine()方法以行为单位读入一行字符。它是具有缓冲功能的读取工具
如果BufferedReader关联到键盘,就是从键盘读取数据。如果它间接关联到文本文件,就是从文件读取数据
目录管理:在Java中,将目录也当作文件处理,所以File类中也提供了实现目录管理功能的方法
File path=new File("E:\\ex\\"); //创建一个对象,E盘的ex目录,意味着当前对应的只是一个目录,一个路径,而不是文件
File f=new File(path,"Test.java");
创建File:File f; //声明一个变量 //所谓的相对路径,这里指的是当前路径,也就是程序的class文件所在的路径
f=new File("Test.java"); //调用File类中定义过的构造方法,方法的实参对应的是一个相对路径下的文件名
f=new File("E:\\ex\\","Test.java"); //由于斜杠在Java字符串常量中被用作转义符来使用,所以若想使用斜杠字符则需要双斜杠进行转义
File方法:目录:boolean mkdir():创建新目录,由参数指定子目录的名字,创建成功返回true
String[] list():参阅API文档
File[] listFiles():参阅API文档
测试操作:boolean exists():判断当前File对象所对应的文件或目录是否存在,存在则返回true
boolean canWrite():判断当前File对象所对应的文件是否是可写的
boolean canRead():判断当前File对象所对应的文件是否是只读的
boolean isFile():判断当前File对象所对应的东西是否是一个文件
boolean isDirectory():判断当前File对象所对应的东西是否是一个路径
boolean isAbsolute():判断当前File对象是否是以绝对路径标识的
设置和修改操作:boolean delete():删除当前File对象所对应的文件或者目录,删除成功则返回true
void deleteOnExit():在当前应用程序或JVM退出时删除当前文件,比如程序中为保存临时信息而创建的临时文件的删除
boolean creatNewFile():创建临时文件或当前File对象对应的文件不存在的时候调用该方法进行创建,创建成功则返回true
setReadOnly():设定当前文件或者目录为只读
boolean renameTo(File dest):即移动并且改名到其它的地方,由参数File对象标明它将复制的位置及要改成的名字
文件或目录名操作:String getName():获得文件或目录名
String getPath():获得文件或目录所在的路径
String getAbsolutePath():获得文件或目录的绝对路径
String getParent():获得文件或目录的上一层路径
获取常规文件信息:long lastModified():返回文件或目录最后一次被访问或修改的时间,注意返回的不是Date型数据,而是long型整数
long length():获取当前文件的长度,单位是字节
补充说明:File对象一经创建,只是内存中的一团数据
它封装的所谓的文件名,或者有时会给出绝对的路径名,都只是一厢情愿的想法,并不一定存在它所标明路径或者文件名
那么我们如何判断一个File对象所封装的文件名或目录名是否真的存在呢?这时可以调用File对象的exists()判断
系统时间:实际上在计算机系统底层记录时间,并不是分别记录它的年月日时分秒等不同的时间段
而是会指定一个时间基准点,比如1900年1月1日0点0分0秒0毫秒或者后来JDK中指明的1970年1月1日0点0分0秒0毫秒
以后的任何一个时刻,其实真正保存的是到指定的基准时刻过了多少毫秒数
而Date类或者Calengar类等都能够很方便的将long型毫秒数换算成具体的时刻,再获取年月日时分秒星期几等等信息
读取文件信息:String fname="test.txt"; //声明一个局部变量fname用来记录将要读取的文件名,这里也可以给出它的绝对路径
File f=new File(fname); //将它封装成File类型的对象,因为后面有的方法能够接纳的是File类型
try{FileReader fr=new FileReader(f); //声明并创建FileReader对象,构造方法中封装了File对象,其实就是指定所要读取的源文件
BufferedReader br=new BufferedReader(fr); //这里的br封装了fr,所以间接的关联到了文件,即test.txt
String s=br.readLine(); //从文件中读取下一行字符串。每调用一次,它的指针或者叫游标会自动的移向下面的一行
while(s!=null){ //需要注意读取文本文件的readLine()方法,在到达了文本文件的结尾时会返回null
System.out.println("读入: "+s);s=br.readLine();}
br.close(); //关闭打开的文件输入流。实际关闭的是br和fr的关联,进而关闭了fr和文件的关联,并将文件置于关闭的状态
}catch(FileNotFoundException e1){System.err.println("File not found: "+fname); //此处System.err和System.out效果是相同的
}catch(IOException e2){e2.printStackTrace();}
读取说明:实际上FileReader类的构造方法也被重载过,也能够接受直接以String形式给出的文件名,学习者可自行查阅API文档
也可通过给出文件绝对路径和文件名的方式指定所要读取的文本文件,如String fname="D:\\ex\\test.txt"程序中需用双斜杠
当然更好的选择是使用命令行参数,在程序启动时动态的指定所要读取的文本文件的名称或者路径
比如将String fname="test.txt"; File f=new File(fname);两行代码改写成一行File f=new File(args[0]);
这里就是约定程序运行时应该提供一个命令行参数。运行时输入如:java ReadFile D:\ex\a.txt命令行状态下用单斜杠即可
这种做法并不严谨,即用户很可能由于不知道程序是通过命令行参数来指定文件的,最后运行时由于没有加命令行参数导致出错
此时可以在程序中做出适当的检查,比如程序运行时可以检查一下数组中是否包含有效的元素或者说用户是否输入了所需的命令行参数
如if(args.length==0){System.out.println("本程序需要使用一个命令行参数,来指定要读取的文件名");return;}
这里args数组长度不可能小于零。然后为用户输出提示信息并执行return后便退出了main()方法,当然整个程序也就跟着结束了
另外一种表现形式是if(args.length!=1),这样一来就比刚才的要求更严格了一下,即我们只接收一个命令行参数
还有一个选择,就是不使用命令行参数而是在程序运行的过程中读取控制台或者说键盘输入的数据,以此来指定所要读取的文本文件
可以采用循环的方式利用Scanner或读取键盘输入的传统方法,循环的读取用户指定的文件名,然后逐个进行文件的处理
输出信息到文件:File file=new File("tt.txt"); //创建一个File对象关联到所要写出数据的文件
try {InputStreamReader is=new InputStreamReader(System.in); //封装并读取标键盘输入数据
BufferedReader in=new BufferedReader(is);
PrintWriter out=new PrintWriter(new FileWriter(file)); //创建PrintWriter对象,它封装了一个FileWriter类型的对象
String s=in.readLine(); //这里的in是声明过的局部变量,而不是System的in
while(!s.equals("")){ //当从键盘中读入了大于零个字符的时候,返回true
out.println(s); //通过PrintWriter对象out调用它的println()方法将新读入的字符串写出到out所关联到的file中
s=in.readLine();} //继续读取下一行键盘输入的数据,直到键盘输入空回车时程序退出
in.close(); //关闭BufferedReader输入流。断开了与键盘之间的关联
out.close(); //关闭连接文件的PrintWriter输出流。断开了与file之间的关联,并且将文本文件置于关闭的状态
}catch(IOException e){e.printStackTrace();}
输出说明:数据传输的过程其实就是0101的字节型数据从一个地方像水流一样流向另一个地方,因此我们称之为数据流
程序运行过程中,如果要写出数据所到的文件不存在,那么系统会在该程序目录下自动创建所需要的文件
当写出数据到文件中时,最后若没有关闭文件,比如注释掉out.close()时,会发现后果很严重。即写入的数据都已经丢失了
所以写出数据到文件的时候,必须显式的关闭所打开的输出流或者说关闭所打开的文件
读取文件的时候如果忘记了关闭输出流,似乎最后的差别还不是很明显,但还是要记得关闭它
FileWriter类的构造方法进行了多次重载,因此它不仅仅能接收File类型的参数所制定的目标文件,还可以接收String类型的参数等
并且可以通过附加的参数来指定对文件的写出方式是追加还是覆盖
比如FileWriter fw=new FileWriter("D:\\ex\\a.txt"); //这时是以覆盖的方式写出到文件的,即覆盖掉原a.txt中的所有内容
再如FileWriter fw=new FileWriter(file,true); //这时则是以追加的方式将数据写出到文件的。具体请参阅API文档
可变参数方法
概述:Java语言允许在定义方法时指定使用任意数量的参数,其格式是在参数类型后加...号
可变长度参数必须放在参数列表的最后,而且一个方法最多只能包含一个可变长度参数
举例:tv.myprint("china",5000,new Integer(54),new Date(),5,7.89); //调用方法
public void myprint(String s,int i,Object... objs){ //定义方法
System.out.println(s.toUpperCase());System.out.println(100 * i);for(Object o: objs){System.out.println(o);}}
说明:new Integer(54)是显式的创建了封装类Integer的对象,new Date()是创建Date类对象,因为只用一次,所以也就没有起名字
后面5和7.8乍看是基本数据类型,但由于封装类的自动封装的机制,系统会将二者自动封装成Integer和Double封装类型之后再传进来
所以后面的这四个参数都对应的是Object可变参数。程序中是使用for-each循环笼统的处理可变参数的
之所以可以这样做,是因为Java语言实际上是采用了一维数组的方式来接收和解析可变参数的
因此这段for-each循环代码也可以改为:fot(int n=0;n<objs.length;n++){System.out.println(objs[n]);}
为了更直观,也可改变可变参数类型。如改成double...d,则意味着将来传进来的可变参数只能是double型的且数量不限
实际上可变参数在真实的开发中应用并不是很广泛,由于参数的数目不定,通常我们不会进行特别有针对性的区别对待
而一般都会通过循环进行逐个的遍历和这种笼统的处理
过时API
概述:过时API是指那些过去定义的,现已不提倡使用的API,包括类、属性和方法等
说明:过时API均存在相应的替代物,这些替代者可能采用了更标准化的命名惯例或功能更适用
在移植Java代码时,可使用-Xlint:deprecation选项进行编译以获得相关详细信息
编译时使用附加的选项检查是否使用了过时API,如果有则应进行替换成主流的API
举例:import java.util.*;public class TestDeprecation{ public static void main(String[] args){
Date now=new Date();int hour=now.getHours();System.out.println(hour);}}
假如当前采用的是JDK1.5或更高版本,那么编译时就会发现屏幕输出这样的一个提示信息
编译:注意:TestDeprecation.java 使用或覆盖了已过时的 API
注意:要了解详细信息,请使用 -Xlint:deprecation 重新编译
重编:用该命令重新编译程序:javac -Xlint:deprecation TestDeprecation.java
输出更详细的说明信息:TestDeprecation.java:6: 警告:[deprecation] java.util.Date中的getHours()已过时
说明:这里的注意信息不同于编译错误,因为此时也生成了class文件
大多数情况下系统还是支持过时的API的,也许将来更高版本以后就不再支持了
自定义:在过时的成分前添加第三种形式的注释,并在其中使用@deprecated标记标明此成分已过时,同时还可以给出简要的说明信息
如class A{/**@deprecated本方法已过时,不推荐使用*/public void ma(){System.out.println("过时方法测试");}}
这样在其它类中调用A的ma()方法时就会出现过时的两个注意的提示了。如果输入-Xlint:deprecation后会发现提示ma()已过时
注解
概述:注解不直接影响程序的语义,然而开发和部署工具可以对其读取并以某种形式处理。它是JDK1.5中开始提供的一种新成分
说明:注解类型采用@interface标记来声明。本质上,注解就是可以添加到代码中的一种类似于修饰符的成分
Java语言采用了一类新的数据类型来描述注解,即注解类型,可以用于声明包、类、构造方法、方法、属性、参数和变量等场合
注解类型相当于类或接口,每一条注解相当于该类的一个实例。我们通常在开发中并不需要定义自己的注解类型
有用注解:public @interface Deprecated
public @interface Override
public @interface SuppressWarnings
Override注解:java.lang.Override类型注解用于指明被注解的方法重写了父类中的方法,如果不是合法的方法重写则编译报错
Override举例:@Override //这里是重写Object的toString()方法。若没有Override注解,编译器就当它是新添加的方法而不会报错
public String tostring(){...} //如果该方法不是合法的方法重写,比如写成tostring(),那么编译时将报错
//实际上Override注解在真正的开发中是比较有用的,它可以确保重写方法的正确
Deprecated注解:它的作用也是标记过时API。与前述使用第三种注释形式来标记的效果相同
Deprecated举例:@Deprecated public void ma(){...} //将来如果使用了ma()方法,编译时将报错,出现两个注意的提示信息
SuppressWarnings注解:使用SuppressWarnings注解可以关闭编译器对指定的一种或多种问题的提示或警告功能
SuppressWarnings语法:@SuppressWarnings(value={"deprecation"})
@SuppressWarnings(vale={"deprecation","unchecked"})
@SuppressWarnings("deprecation")
@SuppressWarnings({"deprecation","unchecked"})
SuppressWarnings举例:@SuppressWarnings(vale={"deprecation"}) //注意该注解后面同样不需要分号结束
括号里value后面给出的类似于静态初始化一个一维String数组
里面给出了我们要关闭的或者说要抑制的某一种问题的提示信息的类型。这里给出的是deprecation类型
意思是说该注解下面的源代码在编译的过程中如果发现使用了deprecation或者过时的API
对于这一类问题,则关闭警告或者说关闭这种提示的功能。这样在编译的时候,编译器就没有什么意见可提了
归档工具
概述:Java归档工具是JDK中提供的一种多用途的存档及压缩工具
可以将多个文件或目录合并或压缩为单个的Java归档文件(Jar,java archive)
使用:可以利用jar文件发布和使用类库。作为程序组件或者插件程序的基本部署单位。用于打包与组件相关联的资源文件
jar工具的基本语法:jar {ctxui}[vfmOMe][jar-file][manifest-file][entry-point][-C dir] files...
举例:命令行中输入jar回车,就可以看到jar命令的使用格式。通常使用的是-c和-f,如果愿意的话,也可以使用-v
命令如jar -cvf test.jar * 即打包该文件夹下所有文件,包括子目录中,也包括子目录的子目录等等
也可以把待打包的class文件一一列举或者根据需要自定义所要打包的文件,如*.class或者*.*等多种方式
jar文件在默认情况下采用zip压缩格式。我们可以将之关联到WinZip或WinRAR中看到它里面的所有文件的内容
压缩过程中或者叫归档过程中,jar工具还自动生成一个平时基本用不到的META-INF清单文件,同样可以通过WinRAR查看它的内容
说明:将来在如果要使用Jar中的某个类文件时,首先就需要把Jar文件加入到CLASSPATH环境变量中
由于Jar文件本身相当于一层目录,而它内部又包含多个class文件,甚至包含多个子目录
所以CLASSPATH变量值在设置时就不仅要把Jar路径加进去,还必须要加到Jar文件,如D:\ex\test.jar;
设置完环境变量之后还需要重启命令行窗口,它才能初始化新设置的环境变量的信息
重新打开命令行窗口后,JVM就可以在CLASSPATH指明的路径下找到所需要的class文件了,Jar里的class文件就可以被其它程序使用了
如果要使用的class文件是在jar文件中的某个包里,那么在程序中就需要引入相应的包,引用方式为import 包名.类名(无后缀);
实际上JDK中所带有的多个class文件在发布前都已经采用了同样的方式归档成单个的jar文件了
这样便于复制和传输,它在机器中的位置是D:\Program Files\Java\jdk1.6.0_13\jre\lib\rt.jar