当前位置:网站首页 > Java基础 > 正文

java语言基础与输入输出初步



本文转自公众号:编程攻略,如有不当,请联系删除。

零基础学Java第三节(基本输入输出)

Java程序的命令行参数

我们可以利用Java程序执行时的命令行参数进行数据的输入。所谓命令行参数,是在执行Java类的命令中,跟在Java类后面的若干由空格分隔的字符序列。如下。

 

释义

第一,在这段程序中,我们用到了所有三种注释形式。

第二,在用方法输出数据的时候,我们用到了、,这里我们可以看到,这种形式同C语言中的数组的使用是不是很像?事实上,args就是数组变量,它是的形式参数,数组中元素的类型为String(Java中的字符串类型名,它是一个类,属于引用类型,不是基本类型)。

第三,println方法的实参为: 这是一个表达式(关于表达式我们见附录1),其中的不是算术上的加法,我们看到左右都为字符串,那么这里的作用就是连接它左右的字符串,构成新的字符串,它是一个字符串连接运算。

既然是表达式,那么就必须进行计算并产生结果,那么参与运算的的值又是从哪里来的呢?

它们的值从命令行上来,如图所示执行该类:

该图例中,有三个命令行参数,所以args数组的大小为3。因为Java的下标从0开始,所以,args的最后一个元素的下标为2。与C语言相比较,C语言不对数组下标是否越界进行检查,而Java则要求下标不得越界。在本程序中args的最大下标为1,那么,如果我们在执行这个java程序的时候,命令行参数少于2个,则会出错,如图:

这里只有一个命令行参数,而我们在程序中需要2个命令行参数,所以会产生如图所示的异常:,我们看lang后面的标识符,它是java.lang这个包(包是若干相关类的集合)中的一个类,这个类代表数组下标越界异常。

第三行的提示:,表示错误发生在这个类的方法中,在源文件文件的第11行。

大家在以后学习Java的过程中,要学会看出错提示。

使用BufferedReader进行输入

类是一种Java标准类中的一个负责从字符输入流中读取文本的类。为了提高效率,对流中的字符进行缓冲,从而实现字符、数组和行的高效读取的一个类。这个类的全称为,其中是一个包(package)名,顾名思义,这个包是同java的输入输出相关的 类及子包的集合。

什么是包呢?这个问题,我们这里要略知一二。简单的讲,你可以把包想象为一个箱子,这个箱子中放着功能相关的东西(类),还可以在大箱子中放小箱子(子包)。所以,大家可以知道上面的中是大箱子,为小箱子,这个类是这个子包中的一个功能类。这些包以何种形式保存的呢?答案是,以文件夹的形式保存,如图:

在JDK的安装目录中,有个压缩文件:,所有的JDK的源代码均在这里,有志向研究java的同学,可以读读这些代码,看看大师和菜鸟之间的区别。我们用解压软件打开可以看到整个JDK的包结构,就本图,我们可以看到这个类的源码就在那里,而且下再无子包。

大家可能说,源代码是无法执行的,那可以执行的.class文件在哪里呢?在JDK的 文件夹下有个文件,大家用解压软件打开它,就可以看到JDK中所有的文件均在这里。如图:

这里大家要注意包名的命名规则,包名中所有的字母均为小写字母,这是大家约定俗成的规则。

BufferedReader

原理

这个类的功能很强大,它的功能不仅仅局限在从键盘中输入数据。目前,我们先只学习利用该类如何从键盘输入数据。

首先,如果我们希望这个类为我们服务,就必须创建这个类的对象(不是所有的类都必须创建对象才能用,比如我们常用的类,就是直接用了,怎么区分,我们以后慢慢了解,这里我们记着我们学过的每个类的使用方式),如下代码:

 

这行代码中,我们通过 创建了一个对象(什么?实参?难道是方法调用?不错,这里就是方法调用,只不过这里的这个方法是BufferedReader的构造方法。所谓构造方法,我们先简单理解为创建对象时会执行的方法,该方法不同于普通方法,它没有返回值类型,甚至连都没有,构造方法只能由使用,不能把构造方法当作普通方法调用),这个对象的引用放在了  这个变量中,注意不是这个对象放在中,而是这个对象的引用放在了中。

那么大家一定疑问了,什么是引用?如果对比C语言,这里引用相当于C中的指针,只不过Java中的引用值本身是不能像C中的指针一样进行数值的操作的,比如不能像C一样对指针进行值的加减等。如果以内存存储的角度来看,变量是保存在栈中的,而对象是保存在堆中的,所以,中只是保存了对象的地址,这里我们把它叫做引用。引用就代表那个对象。我们可以很自然的通过引用对对象进行操作。这就像电视机与遥控器的关系,电视机就是对象,遥控器就是引用,遥控器丢了就不能操纵电视机了,虽然电视机还在那里,所以引用应该保存起来,本例中新建对象的引用就是保存在br中。

大家可能还有疑问,“类”和“对象”又是什么关系?我们再做个类比,“类”就是盖楼的图纸,“对象”就是盖成的大楼。有了图纸,不代表这个“类”就可以用了,只有楼盖好了,我们才能使用楼(实体存在的楼)的功能。所以,我们在写Java程序的时候都是先设计类,再创建该类的对象(还是那句话,有些类不需要创建对象就可使用)。

那么,这行语句中的 又是什么用呢?我们看到new就知道是创建对象,这里创建的是 这个类的对象,创建完成的对象的引用,作为实参传递给创建类对象的构造方法,然后这个对象就由引用的对象使用了(那到底怎么用的呢?由封装起来了,那是个黑盒子,大家没必要知道)。

为什么必须有个对象呢?这是因为在Java系统中,数据传递的方式是以“流”的形式进行的,流是在Java系统中看待数据传递的一种模式,就像水管中的水,只不过这根水管很细,在水管中流动的数据排队流过。在Java中有两种数据流,一种是“字节流”,一种是“字符流”,也就是说这根水管中流过的数据是字节还是字符。处理的数据必须是字符流,而代表标准输入设备-键盘的,它处理的数据是字节流输入数据,怎么对接呢?这就需要进行字节流向字符流的转换,到这里大家是不是明白了呢?如图:

那至于是怎么转换的,我们就不需要知道了,我们只要知道它能转换就行了。

类的全称为,前面的是包名不消说了。

接下来我们就可以使用的对象来对数据进行处理了。怎么做呢?就是通过对象来调用对象中的方法来完成相应的功能,这里我们先看看这个类中常用的方法。

返回值类型方法名及参数方法的功能可能发生的异常无BufferedReader(Reader in)它是构造方法,创建一个使用默认大小输入缓冲区的缓冲字符输入流对象。参数为字符流对象无无BufferedReader(Reader in,int sz)它是构造方法,创建一个由sz指定大小输入缓冲区的缓冲字符输入流对象。无voidclose()关闭该流并释放与之关联的所有资源。IOExceptionintread()读取单个字符,读取到的字符编码以int值返回,因为字符的编码为两个字节,所以,返回值的范围为0~65535(0x0000 ~ 0xffff),如果返回值为-1,则表示已到达流的末尾,流中再没有数据。IOExceptionintread(char[] cbuf, int off, int len)将字符依次读入到 cbuf 数组的 off 下标(数组的下标从0开始)开始处,期望都len个字符,但实际因为流中可能没有那么多字符,实际读取的字符个数以方法的返回值返回。返回值如果是-1,表示已到达流的末尾。IOExceptionStringreadLine()在流中读取一个文本行,行是以换行 (' ')、回车 (' ') 或回车后直接跟着换行终止的。这个文本行以字符串的形式返回,注意字符串中不含有行结束符。返回值为null表示到达流的末尾IOExceptionbooleanready()判断此流是否已准备好被读取。IOExceptionvoidmark(int readAheadLimit)标记流中的当前读取位置。readAheadLimit表示在做过该标记以后,可读取字符的数量,如果标记以后再读取字符的数量超出了改数值以后,执行reset方法可能会失败。readAheadLimit的值不应大于输入缓冲区的大小,如果大于输入缓冲区的大小了,系统会重新再分配一个新的缓冲区。所以应该小心使用readAheadLimit这个实参值,不要太大。如果发生I/O错误,会抛出IOException;如果readAheadLimit<0,会抛出IllegalArgumentException,该异常不需处理。booleanmarkSupported()判断此流是否支持 mark() 操作(它一定支持)。无voidreset()将流的输入位置重置到最后一次做的标记处。IOExceptionlongskip(long n)从当前读取位置跳过n个字符,n为期望值,实际跳过的字符数以返回值的形式返回。n的值应该为非负数。如果发生I/O错误,会抛出IOException;如果n为负数,则抛出IllegalArgumentException,该异常不需处理。

程序示例

功能需求1:

从键盘上输入一串字符,以回车作为结束,然后把输入的字符再回显到屏幕上

  • 需求分析:从需求,我们知道,我们只需要输入一行内容,而且输入数据为字符,我们利用前面提到的来进行实现。

  • 程序编码:

 

大家把上面的代码用Sublime保存为,在解决好字符编码问题以后,进行编译,会怎么样?出错了?!出错信息是:

怎么办?

我们要仔细看出错信息显示的是什么,找不到符号...,找不到哪些呢?图中的那些数字表示出错的行号,指定的为出错的具体位置,分别是。

OMG!怎么办?

大家还记得我们用的这两个类的全称是什么?回忆一下。好了,我们在用这两个类的时候,如果不加上包名,java编译器就不认识,那我们加上试试,代码如下:

 

编译结果如图:

原来的找不到符号的错误没有了,但是怎么又多出一个“未报告的异常错误”问题呢?什么时候才能没有问题啊?!

大家稍安勿躁,遇到问题,我们不怕,对于初学者来讲,遇到越多的问题,我们应该越高兴,因为,每修正一个错误,我们就学到了一点东西。好吧,我们来看看错误提示,在代码的第六行有个“ 未报告的异常错误IOException; 必须对其进行捕获或声明以便抛出”错误,问题给你指出来了,而且还有解决方案,挺好。但是怎么“捕获”或者“声明”呢?

 

编译后,如图:

怎么又成,这次是找不到,结合前面的经验,我们知道,这里还是需要用全名:,好了,我们再改改:

 

编译后,如图:

 我们经常说:“没有消息就是好消息”,这里没有任何提示信息,就表明,我们的源代码编译通过了。我们可以测试看看:

可以运行了。

 

 我们从编译提示信息,我们知道  可能会出现异常,而这个异常,我们没有捕获。那我们就用上面的异常处理语句来做一下,代码改成:

 

编译执行,没有问题。

等等,现在是不是大功告成了呢?还有收尾工作没有做完:对于流的操作,我们在不使用流的时候,一定记得把流关闭了,这就像用水,不用水的时候,要记得把水龙头拧住。怎么做呢?在操作流的最后一步,执行方法,我们一次性把代码补齐,代码如下(我们用异常捕获来做):

 

大家是不是有点疑问,为什么把放在子句中,而且本身又套了一层语句?首先,为了保证不管有没有发生异常,我们的流都要关闭,所以,必须放在子句中,以保证必须执行;其次,方法也可能发生异常,所以外面也要再套一层语句。

到这里程序的编译到运行已经没有问题了,但是,我们有没有觉得总是在类名前加包名是不是太累赘?有没有办法呢?办法总是比问题多。我们可以在源文件的最开始加上若干 或者 ,比如上面的代码,我们可以这么做:

 

我们还可以这么做:

 

这两种形式上的区别在于,后者使用了通配符,这代表相应子包中的所有类,注意只是这个子包中的所有类,是不包含子包中的子包java语言基础与输入输出初步的。比如上面的  是不能写作  的。因为后者的  只是代码这个包中的所有类,而不包含它的子包  。

大家是不是觉得用 * 挺不错的,省事。但是我们建议大家使用第一种方案,这是因为使用 * 会一股脑地把相应子包中的所有类都引入了,而不管你的程序用不用那些类;而不用 * 的方式,我们只是用什么才引入什么,我们可以很好的限制我们代码出错的几率。

细心的同学会发现一个问题,那就是也是类,怎么就没有用全名呢?的全名为: ,从全名我们知道类在这个包中,JDK对这个包总是默认引入的,也就是说对于所有的java源代码,相当于在源代码的最开始都有个,这条语句是省略的。

  1. 那这两种方式有什么区别呢?我们从异常处理语句,我们可以看到,一旦发生异常,我们可以主动由来进行捕获处理。所以,这两种处理方式,第一种是不主动的方式,就是说,写这段代码的程序员准备把发生的异常提交给调用这个方法的代码来进行处理,在方法内部不处理;第二种方式是比较主动的,一旦发生异常,这个异常在本方法内部就进行了处理,不再提交给调用它的代码来处理了。这就好比一个工厂中的一个车间,在车间的生成过程中,如果发生一些生产事故,有些事故车间主任就可以处理,有些事故必须上报到厂长那里,有些可能厂长也无法处理,就需要进一步上报。那么,自己处理就用catch,上报的话,就在方法的后面加上  (后面可以有若干异常类名,中间用隔开),自己处理的异常就不要上报了。

  2. 我们再来看看怎么捕获异常:这个麻烦一点,我们需要使用异常捕获语句,它的一般写法如下:

  3. 我们先看看怎么声明:我们在main方法的后面加上(这里throws是加字母s的哦),试试看,代码如下:

功能需求2:

前面的代码只能输入一行文字,如果输入多行呢?每输入一行就回显一行。

需求分析:从需求,我们知道,我们需要输入多行内容,每输入一行都是以回车结束来这行的。我们想到可以用循环来实现。循环是需要结束的,我们怎么来结束输入呢?总不能让这个程序永远运行下去吧。对于键盘的输入,如果要结束这个输入流,我们在最后输入,再输入回车就可以了。而这个方法在遇到流的末尾的时候,它的返回值为。

程序编码:

 

这里只要我们编译成功以后再运行,都没有发生异常的情况,我们怎么才能看到异常呢?我们如果希望java的标准输入系统发生异常,这种概率实在太小了。有一种办法,我们自己生成异常,来验证一下异常的处理,代码如下:

 

编译的时候,会出现如图所示的错误:

也就是说在编译的时候,java系统就已经探知到throw后面的语句是无论如何都不会执行到的。

那我们把它去掉再试试。如图

框中的信息是 e.printStackTrace(); 的执行结果。

从这个示例,我们知道,异常的产生也可能是程序在运行过程中出现问题,由系统被动产生用于代表相关异常的异常对象,也可以由代码主动产生异常。

如果我们将上面的代码再修改一下,把的参数由改为,会怎么样呢?大家就会发现,没有任何问题,依然可以捕获。这是什么道理呢?后的参数不是同发生的异常精确匹配吗?我们知道Java语言是面向对象的语言,面向对象的概念中,类之间是可以有继承关系的,在同一个继承链中,处于继承关系底层的类可以看作是一个处于高层的类类型,就本例来讲,我们可以说。其实,这很好理解,就如同:不管松树、柳树、杨树,它们都是树 是一个道理的。不在一个继承链中的类不能这么讲,比如,我们不能说:石头是树 一样。在JDK中,所有的类都有一个根类:,所有的类都继承自该类。

使用Scanner进行输入

类是一个可以使用正则表达式来解析基本类型和字符串的简单文本扫描器。它的输入流不局限于键盘输入。该类的功能很强大,我们这里只涉及我们要用到的东西,完整的说明大家参考JDK说明书。

介绍

 使用分隔符将其输入分解为各个不同的部分(称为),默认情况下使用空白作为分隔符。我们可以使用该类中的方法来设置不同的分隔符。

在类中有很多不同的为前缀的方法,我们可以使用不同的 将分解得到的转换为不同类型的值。比如,我们可以使用将下一个要被处理的转换为数据。

如何判断我们要处理的是不是还有呢?类中也提供了一系列以为前缀的方法来判断是否还有要处理的,如果还有,方法的返回结果为,否则就是。

在使用的时候,也是需要创建该类的对象,然后利用该对象来完成工作的。

使用该类的一般流程如下:

  • 创建对象,同时设置输入源

  • 设置分隔符(如果使用默认的空白符的话,这步是可以省略的)

  • 对输入的数据进行操作

  • 关闭扫描器

示例1

我们设置一个字符串,它的内容为: 。我们把这个字符串用进行扫描来看看结果如何。代码如下:

 

运行结果如图:

从运行结果,我们可以看到原始的字符串,通过扫描器,按默认的空白符为分隔符,把它分成了7个,每个由方法以字符串进行处理。

示例2

我们还按上面的数据源,这次,我们改变一下分隔符,改为,看看效果。代码如下:

 

运行结果如图:

以“,”为分隔符,将原始数据源分隔成了两个。那么能不能设置多种分隔符呢?答案当然是可以的,我们只需要在设置方法参数字符串的时候,把分隔符用分隔开就行了,比如上例:我们把空格和逗号都作为分隔符,我们把上面的设置分隔符语句改为 即可(注意: 前面有个空格)。大家自己测试一下吧。

示例3

还按上面的数据源,这次,我们把数据源中所有的整数提取出来,我们把代码修改如下:

版权声明


相关文章:

  • 零基础学java韩顺平2024-10-15 13:02:04
  • 没有java基础直接学框架2024-10-15 13:02:04
  • 学习大数据需要java基础吗2024-10-15 13:02:04
  • 中级 java 基础面试题2024-10-15 13:02:04
  • java基础常用方法2024-10-15 13:02:04
  • 有java基础学c难吗2024-10-15 13:02:04
  • java jvm基础面试题2024-10-15 13:02:04
  • java零基础入门教程 王勇2024-10-15 13:02:04
  • java画基础图形图文2024-10-15 13:02:04
  • java基础篇三大特性继承2024-10-15 13:02:04