System:这个类是final类,不能被继承
System类中的方法都是静态方法,不需要被实例化。
API介绍如图1.1 所示。
图1.1 System类介绍
我们平时使用的System.out.println其实就是使用的System中的一个静态方法,获取到一个类,再调用这个类中的println方法进行打印
看一下System类中的一些常用方法,如图1.2所示。
图1.2 System类的方法
获取系统属性信息,代码如图1.3所示。
图1.3 获取系统的属性信息
获取指定属性的值,代码如图1.4所示。
图1.4 获取系统信息
这个类可以与windows底层进行交互,api介绍如图1.5所示。
图1.5 api介绍
Runtime类不可以被实例化,但是自身还有非静态的方法,所以该类一定对外提供了获取其对象的功能,而且该功能是静态的。
所以Runtime类其实是用单例设计模式设计出来的,保证了对象在内存中的唯一性。
Runtime里面有一个exec方法,是比较重要的,如图1.6所示。
图1.6 方法介绍
我们到计算机中找到计算器的运行程序的exe文件名称,如图1.7所示。
图1.7 计算器
下面我们尝试在代码中执行代码调用这个计算器的exe执行程序。
代码如图1.8所示。
这个exec命令执行的时候会抛异常。所以需要使用throws向外抛出
注意:路径中的反斜线,需要使用两个,第一个是负责转义后面一个的。
这样执行的时候就可以把计算器打开了。
图1.8 打开计算器
Math类是提供了数学元素的工具类,该类中的成员都是静态的
double ceil(double a):获取大于指定参数的最小整数
double floor(double a):获取小于指定参数的最大整数
long round(double a):四舍五入
static int abs(int a) :获取绝对值
random():随机数,伪随机数。 随机值是从0到1
还有一个可以获取随机数的类是Random,这个类提供了nextInt()等方法可以随机获取一些数字。
Math类的应用代码如图1.9所示。
Math.random(); 随机值是从0到1
图1.9 Math类的应用
还可以使用Random获取一些随机数,这个类提供的方法使用起来更加方便一些,如图1.10所示,可控性也比较好。
图1.10 Random类
Random的应用代码如图1.11所示。
图1.11 所示。
Random的一个案例实现,如图1.12所示。
图1.12 Random案例实现
时间类在工作中还是比较常见的,比如获取当前时间,以及对日期进行加减操作。
Date类的解释如图1.13所示。
图1.13 Date类解释
获取当前日期对象,如图1.14所示。
图1.14 获取当前时间
将毫秒值转为日期对象,如图1.15所示。
图1.15 毫秒值转Date
将日期对象转为一个毫秒值,如图1.16所示。
图1.16 date转毫秒值
日期对比,如图1.17所示。
图1.17 日期对比
前面Date输出的日期格式不是我们平时使用的,所以我们想把日期格式化为我们常用的格式,例如 年-月-日 时:分:秒
Date下面有一个时间格式化类DateFormat,使用这个类可以对日期进行格式化,这个类在text包下。
Dateformat这个类是一个抽象类,所以需要找他的子类,有一个SimpleDataFormat
在newSimpleDateFormat的时候需要传一个模式,这个模式就是指定需要把时间转化为什么格式,如图1.18所示。
图1.18 模式
代码案例如图1.19所示。
图1.19 日期格式转换
如何将一个给定的日期字符串转成一个日期对象呢?
可以使用dateFormat中的parse方法完成。
代码实现如图1.20所示。
为了解析指定格式的日期字符串,所以需要我们自己指定模式进行解析
这样执行时会抛异常的,java.text.ParseException,如果我们写的字符串有问题话就会报错了,在这对异常处理一下。
图1.20 字符串转date
总结:
将日期对象转为指定格式的字符串,使用dateFormat类中的format方法
将具备具体格式的日期字符串转为日期对象,使用dataFormat类中的parse方法
将日期对象转为毫秒值,使用date对象中的gettime方法,
将毫秒值转为日期对象,可以使用date类中的构造函数,或者settime方法。
需求:
获取两个时间间隔的天数
2019-2-10
2019年2月12日
代码实现如图1.21所示。
图1.21 代码
Date对象里面很多方法都过时了,那是不是需要有一个替代品啊,从jdk1.1开始由Calender替代了。如图1.22所示。
图1.22 API中文文档
这里面有一个getInstance方法可以获取一个日历实例
怎么获取Calender实例中的指定信息呢?,如图1.23所示。
这样打印出来之后发现,星期是有问题的,这个星期是外国的星期,外国每个星期的第一天是星期日,对应的数值是1。
月份也是有问题的,因为这里的月份是从0开始计算的,所以需要在获取的月份上加1。
图1.23 calendar的使用
对月份和周几进行改进之后如下图1.24所示:
图1.24 calendar的使用
设置日期:注意:月份是从0开始计数的,代码如图1.25所示。
图1.25 设置日期
还有更厉害的方法,还可以对时间进行增减,代码如图1.26所示。
图1.26 日期加减
如何获取任意一年的二月有多少天呢?
因为每年2月份具体多少天是不确定的,所以我们可以先获取任意一年的三月份的第一天,再往前推一天就可以了。代码如图1.27所示。
图1.27 案例代码
接着我们类学习一下java中的另外一块技术,io技术
我们电脑的硬盘上有一些文件,我们可以使用java程序把这些文件中的数据读取出来
我们还可以使用java代码把程序输出的结果保存到一个文件中。
这些操作其实我们也是可以操作的,但是现在我们要使用java代码来操作,所以java就对这些功能进行了封装,并且提供了相对应的对象。
为了方便管理,java把这些包都存放在了io包中。
流按照操作数据分为两种:字节流和字符流
早期只有字节流,所有数据都是以字节存在的,后期才产生的字符流。因为字符数据涉及到了编码问题,所以在字符流对象中加入了编码机制,字节流是没有编码的。
如果处理的数据都是字符数据,这时建议使用使用字符流对象来处理。
流按照流向分为:输入流和输出流
输入流:负责读操作
输出流:负责写操作
在IO流这个体系中有很多类可以使用。
字节流的抽象基类:InputStream ,OutputStream。
字符流的抽象基类:Reader ,Writer。
这些基类里面会有很多子类的实现,如何区分是属于哪个派系里面的呢?
注意:由这四个基类派生出来的子类名称都是以其父类名作为子类名的后缀。
如:InputStream的子类FileInputStream。
如:Reader的子类FileReader。
IO流程序的基本开发流程如下:
1. 导入IO包中的类
2. 进行IO异常处理
3. 在finally中对流进行关闭
这就是IO流的基本概念及体系。
下面就感受下字符流的基本操作。
需求:使用java程序将一个字符串数据保存到硬盘上。
分析:操作字符数据,就可以用字符流对象,写到硬盘其实就是操作文件。
既然需要写,就需要找具备写功能的输出流。
字符流体系中有两个基类:
Reader和Writer
到api中找到writer类,在他下面有一个write方法,
就可以使用这个方法进行写入,找到方法之后就需要找具体的子类对象了,如图3.1所示。
图3.1 API文档信息
然后看一下这个类的构造参数,发现,都是需要构造参数的。大家可以想一下,这个类可以向外面写数据,写数据的时候肯定要有目的位置,所以需要先初始化一个文件。如图3.2所示。
图3.2 构造函数
第一步,先导io包,new一个FileWriter类,然后就需要处理异常了,这是因为使用io类的时候一般都会抛这个异常,如果我们传入一个不存在的磁盘目录的话,操作肯定是失败的,针对这种情况是需要提前定义好处理手段的。在这我们先throws抛出去,后面再处理
这个代码执行的时候会在D盘根目录下看到一个demo.txt文件。
代码如图3.3所示。
图3.3 创建字符流写入对象
第一步的详细解释:
FileWriter fw = newFileWriter("D:\\demo.txt");
创建该对象做的事情:
1. 在堆内存中产生了一个实体
2. 调用了系统底层资源,其实是调用了windows的功能,在指定位置创建了一个文件,建立数据存储的目的地,用于存放即将写入的数据。
3. 因为目的地可能因为路径错误而导致失败,所以抛出了IOException,需要对其处理。
注意:如果要创建的文件已经存在,那么会产生覆盖。
下面开始通过代码向文件中写入数据,,如图3.4所示。,代码执行之后发现文件中还是没有数据,
注意了,这个数据现在只是写到了内存中。
图3.4 写入数据
这个对象里面还有一个flush方法,,通过这个flush方法可以刷新内存缓冲区,将流中的数据刷到目的地中。如图3.5所示。
图3.5 操作文件
再执行一下代码,就可以看到文件中的内容了。如图3.6所示。
图3.6 查看文件内容
其实咱们这写数据是调用了windows的资源进行操作的,所以还需要把这个资源关闭掉。
这就需要用到close方法。
下面演示一个特殊情况,调用write之后,再调用close。这样就不需要调用flush了。
如图3.7所示。
图3.7 操作文件
如果在close后面再调用write方法写数据,是会报IO异常的,因为流已经关闭了。
flush()和close()的区别:
flush:只刷新缓冲区,流依然存在,并可以继续使用,可以用多次。
close:也会刷新缓冲区(调用flush),但是刷新后,立刻关闭流资源,流不可以继续使用,只能用一次。
建议有多次写入的时候,写一次,刷新一下,最后的时候调用close关闭流。
我们刚才写的代码都会抛出IO异常。
刚才我们没处理,现在就看一下处理方式
这几行代码都是会抛异常的,是不是每一行都写一个try catch呢?
直接放一个trycatch里面就可以了,如图3.8所示。
图3.8 try-catch处理
这样执行是没问题的,如果在执行write()的时候抛异常的话,会出现资源没法关闭的情况,
所以需要写一个finally代码块关闭流。
代码如图3.9所示。
图3.9 finally代码块
如何在一个已有的文件后面进行追加呢?
使用FileWriter的另一个构造函数,一个带有boolean类型参数的构造函数。
只要boolean类型值为true,就可以完成追加。代码如图3.10所示。
图3.10 文件内容追加
注意:续写的时候如果指定的文件不存在怎么办?
这个时候程序会先创建一个文件,再向里面写数据。
现在写入的数据和之前文件中的数据是在一行的,如图3.11所示。
图3.11 文件内容
如果想要在文件中的下一行写入一些数据,这个时候需要写入一个回车换行符。如图3.12所示。
注意:这里的换行符使用\n或者\r\n都可以,因为从windows10版本开始,记事本已经可以识别\n作为换行符了。
图3.12 换行符的使用
刚才我们完成了一个文件的写入动作,文件写完之后就可以把文件内容读出来了。
需求:
读取一个硬盘上已有的文件中的数据,并打印在控制台上。
分析:读取文件需要先查看字符读取流的基类对象Reader的子类。如图3.13所示。
图3.13 字符读取流子类
想要读取文件的话,这个文件一定要存在的,所以这个子类会抛出文件找不到异常。
实现读取的代码,如图3.14所示。
执行代码,发现打印的结果是数字,这个返回的是字符对应的ascll码的数值。
图3.14 读取文件
如果想要转换为字符的话,需要强转为char类型就可以了,如图3.15所示。
图3.15 读取文件
int ch = fr.read();// 这个read方法可以自动向下读取
但是我们想要读取文件中的所有内容的时候也不能写很多行read代码,这样代码就有点重复了,所以可以使用循环处理,但是什么时候停止呢?
看这个read方法的API,只要判断返回值为-1就表示读取完了。如图3.16所示。
图3.16 read方法介绍
下面就使用这个来实验一下,代码如图3.17所示。
图3.17 读取文件
我们发现读取的方式还有多个,还有一个可以接收数组得
这个时候,读取的字符就存到了char数组中了。
使用newString 把这个字符数组转化成字符串打印一下,代码如图3.18所示。
图3.18 读取操作
但是需要注意,这种方式来取数据是不合理的,因为现在缓冲区数组的大小是我们自己随机设置的,假设现在文本中一共有5个字符【把demo.txt这个文件的内容修改一下,只保5个字符abcde】,那么我们第一次取2个字符是ab,第二次去2个字符是cd,第三次取的时候其实就剩余1个字符了,但是会发现,打印的时候还是会打印出来2个字符ed,如图3.19所示。
图3.19 读取操作
分析图3.19中的结果,其实第三次打印的len是1,这个表示读取到的字符个数,这个值是对的,但是为什么却打印了两个字符呢?
下面画图分析一下,如图3.20所示。
图3.20 数据读取流程
我们在取数组得时候,有可能数组会装满,也有可能没装满,我们要取的是数组中的有效位。
这样改造就可以了,根据返回的长度进行读取就可以了。如图3.21所示。
图3.21 读取数据
这个代码还想需要改造的,如果文件中的内容很多,不能全部手写,需要用到循环来实现,如图3.22所示。
图3.22 读取数据
char[] buf = new char[2];
这个数组到底定义多少合适呢?,如果数据非常多,数组长度为2的话,需要读取很多次,如果定义一个非常大的也不合适,开辟空间比较慢
一般情况,数组(缓冲区)的长度建议为1024的整数倍。
所以,如图3.23中的写法才是实际工作中的常见写法。
图3.23 读取数据
需求:复制文本文件
复制原理:
对源文件进行读取,并将读取的数据存储到目的文件中。
一次读一个字符的方式进行复制,代码如图3.24所示。
图3.24 复制文本文件
现在一次操作一堆,使用字符数组操作
在这也加上异常处理,代码如图3.25所示。
图3.25 复制文本文件
从刚才的代码我们发现,使用缓冲区之后,代码的执行效率会有很大提高。
基于此,流对象也考虑到了这一点,它发现这个缓冲区确实可以提高效率,所以将缓冲区这种技术作了封装,所以针对写数据提供了BufferedWriter类,查看API的介绍,如图3.26所示。
图3.26 BufferedWriter介绍
这里面多了一个newLine方法,可以很方便的时候写入换行符。
看一下如何创建这个对象,构造函数中必须要指定一个writer参数,如图3.27,这个可以这样理解,可以把writer理解为水,把bufferWriter理解为杯子,没有水的话拿杯子有用吗?
所以缓冲区的存在必须要有流。缓冲区是为了操作流,没有流,他就没有意义了。
图3.27 构造函数
BufferedWriter的操作代码如图3.28所示。
缓冲区对象的存在是为了给流提高效率,所以缓冲区对象的建立必须先要将流初始化进来。
图3.28 缓冲区的用法
有了写入缓冲区,对应的针对读操作也有读取缓冲区。
Reader有一个子类,BufferReader,解释如图3.29所示。
图3.29 BufferReader介绍
里面多了一个方法,一次读一行,readLine()。代码如图3.30所示。
但是需要注意,如果数据已经读取完了还继续执行readLine方法则会返回null。
图3.30 读取数据
使用while循环改造一下代码,这样使用起来就很方便了,如图3.31所示。
图3.31 代码优化
使用BufferedReader和BufferedWriter缓冲区实现文本复制。代码如图3.32所示。
图3.32 使用缓冲区复制文件
在这一章我们针对字符流操作主要学习了4个类,
FileReader
FileWriter
BufferedReader
BufferedWriter
凡是直接操作数据的流对象,或者操作文件的流对象,都是最常用的流对象。
而操作流就需要提高效率,所以带缓冲的流对象,也是常用对象。
下面来学习一下字节流,里面有两个基类,
InputStream和OutputStream
需求:通过字节流完成一个文件数据的写入。
发现OutputStream下面有一个操作文件的FileOutputStream类
注意:
字符流的write方法接收的是一个字符数组
字节流的write方法接收的是一个字节数组
看这个例子,如图4.1所示。
注意:在这里不调用close方法,发现也可以把数据写入到文件中,因为字节流对象中不存在缓冲区。
那还需要调用close吗? 是需要的,在这还是需要关闭资源的。
图4.1 字节流的使用
下面再写个例子,读一个文件
这种方式效率有点低,代码如图4.2所示。
图4.2 字节流读取文件
可以按照下面这样改造,如图4.3所示。
图4.3 字节流缓冲区读取文件
注意:图片是媒体文件,不能用字符流操作,只能用字节流操作。
代码如图4.3所示。
图4.3 复制图片
注意:
只要操作数据是文本,建议使用字符流,除此之外,都用字节流。
再读取一个大的压缩包文件,flink-1.6.1-bin-hadoop27-scala_2.11.tgz
FileInputStream的父类InputStream中有一个类avaliable方法,可以获取文件有多大
所以想要复制一个文件,就简单了,直接创建一个和文件大小一样的字节数组就可以。
代码如图4.4所示。
图4.4 复制文件
注意:这样实现是不太好的,如果这个文件很大,一开始就创建了很大的一个数组,这样new数组过大的话,创建是很慢的,并且还容易出现内存溢出。
int len = fis.available();
// 定义一个大小正好的数组
byte[] buf = new byte[len];
这种方式可以操作一些小文件是没问题的,大文件不建议这样用,容易出问题。
再写一个拷贝代码,使用缓冲流的方式。
针对字节流java也提供了包含缓冲区的类,BufferedInputStream和BufferedOutputStream
代码如图4.5所示。
虽然这个也是一次读一个,但是都是在内存中操作的。速度也是很快的。
在这里拷贝了一个287M的文件,大致消耗9秒左右。
图4.5 拷贝文件
看下面这个代码,这个代码没有使用缓冲区,一次读一个,发现,复制一个287M左右的文件,需要花费很长时间,至少需要10分钟以上。所以,工作中就不要使用这种方式了,效率太低。代码如图4.6所示。
图4.6 拷贝文件
总结一下,现在我们已经学了4个字符流和4个字节流
字符流:
FileReader
FileWriter
BufferedReader
BufferedWriter
字节流:
FileInputStream
FileOutputStream
BufferedInputStream
BufferedOutputStream
需求:
以前数据都是来源于硬盘,现在由用户来输入数据,数据来自于键盘,如何对数据进行处理呢?
在这可以使用system里面的in方法,接收键盘的输入。代码如图5.1 所示。
图5.1 接收键盘输入
我们想实现一个功能
需求:
通过键盘录入数据,将数据转成大写打印到屏幕上,当录入的数据是end时,结束打印程序。
思路:
1. 通过Systen.in.read()方法获取键盘录入的字节
2. 通过循环不断的获取录入的字节
3. 定义一个临时存储容器,将录入的数据存储起来
4. 判断行标记,当满足一行时,将存储的数据转成字符串 并且判断是否是end,不是就转成大写打印,是的话就结束程序。
代码如图5.2所示。
想要停止程序的话需要执行end,或者点击eclipse中的Terminate红色按钮。
图5.2 接收键盘输入
我们通过键盘接收的用户输入其实都是字符,使用字节流操作的时候是比较麻烦的,所以想使用字符流操作。
字节流不能直接传给BufferReader,这个时候就需要把字节流转换成字符流来用了。
转换流:
InputStreamReader:字节流通向字符流的桥梁
OutputStreamWriter:字符流通向字节流的桥梁
字节流转字符流代码如图5.3所示。
图5.3 字节流转字符流
在这还可以把输出的代码也改造一下,再把输出的字符流转换成字节流写出去,代码如图5.4所示。
图5.4 转换流的应用
把前面写的代码调整一下,这样看起来更清晰。
代码如图5.5所示。
图5.5 代码调整
后期需求变了,想要把数据输出到文件中,这样就保存到文件中了,代码如图5.6所示。
图5.6 把数据写入到文件
将一个已有文件的内容转成大写,保存到另一个文件中,代码如图5.7所示。
图5.7 转换并且拷贝文件内容
转换流还有一个应用,看API介绍,如图5.8所示。
图5.8 转换流介绍
Writer
|--OutputStreamWriter
|--FileWriter
Reader
|--InputStreamReader
|--FileReader
注意:FileWriter和FileReader都是转换流的子类
注意:
转换流其实就是将字节流和编码表相结合,将字节流中的字节数据,去查了具体的编码表,所以转换流才可以获取一个中文字符。
转换流的子类,用于操作文件的对象FileReader其实就是直接使用父类的具有转换功能的read方法,就可以一次读一个字符。
FileReader 该类会使用本机默认的字符编码表,对于简体中文版的机器默认编码表是GBK。
看如图5.9中的代码。
注意:这里面这个两行代码的功能是一模一样的,
区别:
1.第一行代码可以指定编码表
2.第二行代码只能使用本机默认的编码表
如果操作文本数据,仅使用本机默认编码表,那么第二行代码写起来更方便
如果操作文本数据,需要自己指定编码表,那么必须要使用第一行代码,而且要将指定码表作为字符串传递到构造函数中。
图5.9 编码的使用
下面针对一个中文文本内容进行拷贝,演示编码的应用,具体代码如图5.10所示。
图5.10 拷贝中文文本
在这里注意区分两个编码,在这里以notepad++这个文件工具为例:
如图5.11所示,针对UTF-8这个编码可以细化为两种,
一个是UTF-8,还有一个是UTF-8 BOM
也就是这两种编码的文件,UTF-8是不带BOM头的 UTF-8 BOM是带BOM头的。
我们在使用这种文件编辑器保存文件的时候注意编码问题,建议使用UTF-8这种不带BOM头的就行了。
如果你在两个文件中都保存 你好 这个词语,但是一个是使用UTF-8编码,另一个是使用UTF-8 BOM编码,你在使用代码获取到这两个文件内容进行比较的时候会发现不相等。这个现象需要注意。
图5.11 notepad文本工具的编码设置
计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字。就将各个国家的文字用数字来表示,并一一对应,形成一张表。
这就是编码表。
这些编码表作为了解,混个脸熟即可,针对中文主要使用GBK、UTF-8这两种编码,老外喜欢使用ISO8859-1这个编码,所以有时候会涉及到一些编码之间的转换。
ASCII:美国标准信息交换码。用一个字节的7位表示。
ISO8859-1:拉丁码表。欧洲码表用一个字节的8位表示。
GB2312:中国的中文编码表。
GBK:中国的中文编码表升级,融合了更多的中文文字符号。
Unicode:国际标准码,融合了多种文字。所有文字都用两个字节来表示,Java语言使用的就是unicode
UTF-8:最多用三个字节来表示一个字符。
流是用来处理数据的,处理数据时,一定要先明确数据源,与数据目的地。
数据源可以是文件,可以是键盘。
数据目的地可以是文件、显示器或者其他设备。
而流只是在帮助数据进行传输,并对传输的数据进行处理,比如过滤处理、转换处理等。
流的操作规律:
因为io包中的对象很多,最重要的是要知道完成数据处理时,要使用哪个对象最合适。
如何判断要使用哪些对象呢?
通过几个明确来判断对象的使用。
1.明确数据源,和 数据目的地
数据源:InputStream Reader
数据目的:OutputStream Writer
2.明确数据的内容是否是纯文本。只要是纯文本数据,就使用字符流。
数据源: 是: Reader。
数据目的:是:Writer
如果不是,就使用InputStream或者OutputStream
如果数据不能明确,只有使用字节流。
这样就可以将四个基类,进行确定,要使用哪一个。
3.明确具体设备。
数据源:键盘(System.in) ,内存(数组), 硬盘(File开头的流对象)。
数据目的: 控制台(System.out),内存(数组),硬盘(File开头的流对象)。
4.明确是否需要提高效率?
是:使用带Buffer对象。
5.是否需要一些特殊场景的操作,来完成数据的特殊处理。
字符流继承体系如图6.1所示。
图6.1 字符流继承体系
字节流继承体系如图6.2所示。
图6.2 字节流继承体系
接下来学习一下io中一个特殊的对象,File
因为数据最直接的体现形式就是文件,文件的属性又比较多,例如:文件名称、路径、大小等属性,所以为了方便操作,java就把文件也封装成了一个对象。
通过File对其进行描述,File提供了多个属性和行为,便于我们对文件进行操作。
前面讲的流对象只能用于操作文件中的数据,对于文件的基本属性信息,只能通过File对象来完成。
File既可以是文件对象,也可以是文件夹对象。
查看api中针对File类的描述,如图7.1所示。
图7.1 Flie类文档描述
如何构建File对象呢?
如图7.2所示。
图7.2 构建File对象
注意,这个分隔符在linux服务器上是不通用的。
所以想要实现在不同的服务器上使用不同的分隔符,需要使用file下面的一个静态属性,如图7.3所示。
这样就可以在任何服务器上(Linux\windows)通用了。
图7.3 通用文件分隔符
下面列出的是File的所有功能
常见功能:
1.创建:
booleancreateNewFile();
booleanmkdir()
booleanmkdirs()
2.删除:
booleandelete():
3.判断。
booleanisFile();
booleanisDirectory();
booleanexists();//判断file对象封装的内容是否存在。
booleanisHidden():判文件是否是隐藏文件。
4.获取。
StringgetAbsolutePath();
StringgetPath();
StringgetParent();
StringgetName();
5.重命名。
booleanremoveTo(File)
6.获取文件列表:
String[]list():获取的是当前目录下文件或者文件夹的名称。
File[]listFiles():获取的是当前目录下文件或者文件夹对应的对象。
如果仅获取文件名称,就用list()方法。如果还要获取文件的其他信息,最好使用listFiles()因为它可以获取到文件对象。这样就可以通过文件对象的方法,获取其他的内容。比如;文件大小,文件名称。修改时间等信息。
代码实现如图7.4所示。
图7.4 File常用功能代码
list()方法里面还可以接收一个过滤器FileNameFilter,在获取文件列表的时候可以根据指定的规则进行过滤。
获取D盘根目录下所有以.txt结尾的文件,代码如图7.5所示。
图7.5 文件过滤功能
刚才我们用File类实现了创建,查找等功能,针对File这个常用类衍生出了一些工具类,使用起来非常方便,下面我们来演示几种操作。
代码如图7.6所示。
图7.6 FileUtils工具类
这个对象比较重要,他属于HashTable的子类
Map
|--HashTable
|--Properties
Properties:该集合中存储的键和值都是字符串类型的数据,通常用于配置文件的定义
演示Properties集合基本的操作,代码如图8.1所示。
图8.1 properties集合的基本操作
Properties通常用于操作配置文件,配置文件内容如图8.2所示。
注意:abc.txt文件中的数据必须是 key=value这种格式,并且每一个key=value都是单独的一行
图8.2 文件
使用properties集合来操作abc.txt这个文件,代码如图8.3所示。
图8.3 prop操作集合
将硬盘上的数据读取到集合之后,进行了一系列运算,这个时候集合中的数据值发生了改变,我们希望将改变后的结果,重新存回配置文件中。
这个时候可以使用properties中的store方法实现,代码如图8.4所示。
图8.4 store方法
最终修改后的abc.txt内容如下:如图8.5所示。
图8.5 修改后的文件
对象使用完毕之后会从堆内存中消失,现在我们想要实现一个功能,当重新使用这个对象的时候还使用它之前的那些属性。
本来对象在内存中,现在我想把对象持久化到磁盘上。
下面来看下,如何实现对象的持久化存储。
对象的持久化存储
其实就是将对象中封装的数据保存到持久化的存储设备上。例如:磁盘
那么其他应用程序都需要建立该对象,直接读取设备上的对象即可。
先写一个person类,代码如图9.1所示:
图9.1 Person类代码
使用ObjectStream把对象写入文件,代码如9.2所示。
执行,发现报错,错误信息提示 未序列化异常,所以需要先序列化,实现序列化需要实现一个接口,Serializable。
Serializable这个接口没有需要被实现的方法,这种接口我们称为标记接口。
图9.2 对象写入文件报错
给person类实现Serializable这个接口。代码如图9.3所示。
注意:如果一个对象要被保存,必须要具备序列化功能,也就是说必须要实现Serializable接口。
图9.3 实现序列化接口。
这个时候再执行图9.2中的代码就不报错了。并且会产生obj.txt这个文件。这个文件直接用记事本查看是乱码,这是正常的,这个内容不是为了让我们来看,主要是为了让代码去读取。
obj.txt文件内容如图9.4所示。
图9.4 obj.txt文件内容
下面演示一下如何读取文件中的对象,代码如图9.5所示。
图9.5 读取文件中的对象信息
把内存中的对象存储到文件的过程可以称为序列化
把文件中的读写信息读取到内存中可以称为反序列化
联系客服