第一部分:语言基础
第二部分:面向对象
第三部分:容器
第四部分:IO
第十章 IO技术
10.1 基本概念和IO入门
对于任何程序设计语言而言,输入输出(Input/Output)系统都是非常核心的功能。程序运行需要数据,数据的获取往往需要跟外部系统进行通信,外部系统可能是文件、数据库、其他程序、网络、IO设备等等。外部系统比较复杂多变,那么我们有必要通过某种手段进行抽象、屏蔽外部的差异,从而实现更加便捷的编程。
输入(Input)指的是:可以让程序从外部系统获得数据(核心含义是“读”,读取外部数据)。常见的应用:
Ø 读取硬盘上的文件内容到程序。例如:播放器打开一个视频文件、word打开一个doc文件。
Ø 读取网络上某个位置内容到程序。例如:浏览器中输入网址后,打开该网址对应的网页内容;下载网络上某个网址的文件。
Ø 读取数据库系统的数据到程序。
Ø 读取某些硬件系统数据到程序。例如:车载电脑读取雷达扫描信息到程序;温控系统等。
输出(Output)指的是:程序输出数据给外部系统从而可以操作外部系统(核心含义是“写”,将数据写出到外部系统)。常见的应用有:
Ø 将数据写到硬盘中。例如:我们编辑完一个word文档后,将内容写到硬盘上进行保存。
Ø 将数据写到数据库系统中。例如:我们注册一个网站会员,实际就是后台程序向数据库中写入一条记录。
Ø 将数据写到某些硬件系统中。例如:导弹系统导航程序将新的路径输出到飞控子系统,飞控子系统根据数据修正飞行路径。
java.io包为我们提供了相关的API,实现了对所有外部系统的输入输出操作,这就是我们这章所要学习的技术。
10.1.1 数据源
数据源data source,提供数据的原始媒介。
常见的数据源有:数据库、文件、其他程序、内存、网络连接、IO设备。如图10-1所示。
数据源分为:源设备、目标设备。
1.源设备:为程序提供数据,一般对应输入流。
2.目标设备:程序数据的目的地,一般对应输出流。
10.1.2 流的概念
流是一个抽象、动态的概念,是一连串连续动态的数据集合。
对于输入流而言,数据源就像水箱,流(stream)就像水管中流动着的水流,程序就是我们最终的用户。我们通过流(A Stream)将数据源(Source)中的数据(information)输送到程序(Program)中。
对于输出流而言,目标数据源就是目的地(dest),我们通过流(A Stream)将程序(Program)中的数据(information)输送到目的数据源(dest)中。
菜鸟雷区:输入/输出流的划分是相对程序而言的,并不是相对数据源。
一切以程序为中心。
输入流:数据源到程序(InputStream、Reader)读进来
输出流:程序到目的地(OutputStream、Writer)写出去
10.1.3 第一个简单的IO流程序及深入理解
当程序需要读取数据源的数据时,就会通过IO流对象开启一个通向数据源的流,通过这个IO流对象的相关方法可以顺序读取数据源中的数据。
【示例10-1】使用流读取文件内容(不规范的写法,仅用于测试)
import java.io.*;
public class TestIO1 {
public static void main(String[] args) {
try {
//创建输入流
FileInputStream fis = new FileInputStream("d:/a.txt"); // 文件内容是:abc
//一个字节一个字节的读取数据
int s1 = fis.read(); // 打印输入字符a对应的ascii码值97
int s2 = fis.read(); // 打印输入字符b对应的ascii码值98
int s3 = fis.read(); // 打印输入字符c 对应的ascii码值99
int s4 = fis.read(); // 由于文件内容已经读取完毕,返回-1
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
System.out.println(s4);
// 流对象使用完,必须关闭!不然,总占用系统资源,最终会造成系统崩溃!
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果如图10-3所示:
通过示例10-1我们要注意以下几点:
1.在示例10-1中我们读取的文件内容是已知的,因此可以使用固定次数的“int s= fis.read();”语句读取内容,但是在实际开发中通常我们根本不知道文件的内容,因此我们在读取的时候需要配合while循环使用。
2.为了保证出现异常后流的正常关闭,通常要将流的关闭语句要放到finally语句块中,并且要判断流是不是null。
IO流的经典写法如示例10-2所示。
【示例10-2】使用流读取文件内容(经典代码,一定要掌握)
import java.io.*;
public class TestIO2 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("d:/a.txt"); // 内容是:abc
StringBuilder sb = new StringBuilder();
int temp = 0;
//当temp等于-1时,表示已经到了文件结尾,停止读取
while ((temp = fis.read()) != -1) {
sb.append((char) temp);
}
System.out.println(sb);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
//这种写法,保证了即使遇到异常情况,也会关闭流对象。
if (fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
执行结果如图10-4所示:
老鸟建议:如上代码是一段非常典型的IO流代码,其他流对象的使用也基本是同样的模式!
10.1.4 Java中流的概念细分
按流的方向分类:
1.输入流:数据流向是数据源到程序(以InputStream、Reader结尾的流)。
2.输出流:数据流向是程序到目的地(以OutPutStream、Writer结尾的流)。
按处理的数据单元分类:
1.字节流:以字节为单位获取数据,命名上以Stream结尾的流一般是字节流,如FileInputStream、FileOutputStream。
2.字符流:以字符为单位获取数据,命名上以Reader/Writer结尾的流一般是字符流,如FileReader、FileWriter。因为文件编码的不同,从而有了对字符进行高效操作的字符流对象。
原理:底层还是基于字节流操作自动搜寻了指定的码表。
字符集:相当于一个大字典
按处理对象不同分类:
1.节点流:可以直接从数据源或目的地读写数据,如FileInputStream、FileReader、DataInputStream等。
有File/Byte开头的是节点流
有Buffered开头的是处理流
2.处理流:不直接连接到数据源或目的地,是”处理流的流”。通过对其他流的处理提高程序的性能,如BufferedInputStream、BufferedReader等。处理流也叫包装流。
装饰设计模式(用的舒服,提高性能)
节点流处于IO操作的第一线,所有操作必须通过它们进行;
处理流可以对节点流进行包装,提高性能或提高程序的灵活性。
File类_API使用规则和学习
类的继承体系
不代表文件本身,而代表路径名。
常量
路径分隔符
名称分隔符;,
常量不是大写么?这里没有大写,只是不符合命名规范。
构造器
1.有构造器:new 构造器()
2.无构造器 (1)是一种工具类 直接类.方法()
(2)静态方法返回了Runtime类 getRuntime() 单例设计模式的实现
方法
关注方法的含义、形参、返回值
File_文件夹创建_遍历
只是下一级,不是子孙级,子孙级需要递归
/**
*创建目录
*1、mkdir():确保上级目录存在,不存在创建失败
*2、mkdirs():上级目录可以不存在,不存在一同来创建
*列出下一级
*1.list():列出下级名称
*2.listFiles():列出下级File对象
*列出所有盘符listRoots()
*/
public class DirDemo01{
File dir=new File("D:/test");
boolean flag=dir.mkdirs();
System.out,println(flag);
dir=new File("D:/test2");
flag=dir.mkdirs();
System.out,println(flag);
String[] subNames=dir.list();
for(String s:subNames){
System.out.println(s);
}
File[] subFiles=dir.listFiles();
for(File s:subFiles){
System.out.println(s.getAbsolutePath());
}
File[] roots=dir.listRoots();
for(File r:roots){
System.out.println(r.getAbsolutePath());
}
}
递归列出所有的子孙文件
递归:方法自己调用自己
递归头:何时结束递归
递归体:重复调用
递归占用内存
public class DirDemo01{
public static void main(String[] args){
printTen(1);
}
//打印1~10的数字
public static void printTen(int n){
if(n>10){//递归头:结束递归
return;
}
System.out.println(n);
printTen(n+1);//递归体:方法自己调用自己
}
}
File_文件夹统计
使用递归打印子孙级目录和文件的名称
public static void main(String[] args){
File src=new File("D:\\test");
printName(src,0);
}
public static void printName(File src,int deep){
//控制前面层次
for(int i=0;i<deep;i++){
System.out.println("-");
}
System.out.println(src.getName());
if(null==src||!src.exists()){//递归头
return;
}else if(src.isDirectory()){
for(File s:src.listFiles()){
printName(s,deep+1);//递归体
}
}
}
如何统计文件夹大小?
public static void main(String[] args){
File src=new File("D:\\test");
count(src);
}
private static long len=0;
public static void count(File src){ //递归头
if(null!=src&&src.exists()){
if(src.isFile()){//大小 递归体
len+=src.length();
}else{//子孙级
for(File s:src.listFiles()){
count(s);
}
}
}
DirCount.java
统计文件夹大小使用面向对象思想
public class DirCount{
private long len;
private String path;
private File src;
private int FileSize;//文件的多少
private int DirSize;//文件夹的多少
get/set方法
public DirCount(String path){
this.path=path;
this.src=new File(path);
count(this.src);
}
public satic void main(String[] args){
DirCount dir=new DirCount("D:\\test");
System.out.println(dir.getLen()+"--->"+dir.getFileSize()+"-->"+dir.getDirSize());
}
private void count(File src){
if(null!=src&&src.exists()){
if(src.isFile()){//大小 递归体
len+=src.length();
this.fileSize++;
}else{//子孙级
this.dirSize++;
for(File s:src.listFiles()){
count(s);
}
}
}
}
File_字符集乱码
编码: 中(101) a(97)
解码:
变长节约了空间
ContentEncode.java
//编码:字符串--》字节getBytes()
public static void main(String[] args){
String msg="性命生命使命a";
//编码:字节数组
byte[] datas=msg.getBytes();//默认使用工程的字符集UTF-8
System.out.println(datas.length);//19,中文3个字节,英文一个字节
//编码:其他字符集
datas=msg.getBytes("UTF-16LE');
System.out.println(datas.length);//14,每一个都是两个字节
datas=msg.getBytes("GBK');
System.out.println(datas.length);//13, 中文2个字节英文1个字节
//解码:字节--》字符串new String()四个参数
msg=new String(datas,0,datas,length,"utf-8");
System.out.println(msg);//性命生命使命a
//乱码1.字节数不够2.字符集不统一
msg=new String(datas,0,datas,length-2,"utf-8");
System.out.println(msg);//乱码
msg=new String(datas,0,datas,length,"gbk");
System.out.println(msg);//乱码
}
File类的全部源代码
1.PathDemo01
package com.sxt.io;
import java.io.File;
public class PathDemo01 {
/**
* \ / 名称分隔符 separator
* @param args
*/
public static void main(String[] args) {
String path ="D:\\java300\\IO_study01\\IO.png";
System.out.println(File.separatorChar);
//建议
//1、/
path = "D:/java300/IO_study01/IO.png";
System.out.println(path);
//2、常量拼接
path ="D:"+File.separator+"java300"+File.separator+"IO_study01"+File.separator+"IO.png";
System.out.println(path);
}
}
2.ContentDecode.java
package com.sxt.io;
import java.io.UnsupportedEncodingException;
/**
* 解码: 字节->字符串
* @author 裴新
*
*/
public class ContentDecode {
public static void main(String[] args) throws UnsupportedEncodingException {
String msg ="性命生命使命a";
//编码: 字节数组
byte[] datas = msg.getBytes(); //默认使用工程的字符集
//解码: 字符串 String(byte[] bytes, int offset, int length, String charsetName)
msg = new String(datas,0,datas.length,"utf8");
System.out.println(msg);
//乱码:
//1)、字节数不够
msg = new String(datas,0,datas.length-2,"utf8");
System.out.println(msg);
msg = new String(datas,0,datas.length-1,"utf8");
System.out.println(msg);
//2)、字符集不统一
msg = new String(datas,0,datas.length-1,"gbk");
System.out.println(msg);
}
}
3.ContentEncode
package com.sxt.io;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* 编码: 字符串-->字节
* @author 裴新
*
*/
public class ContentEncode {
public static void main(String[] args) throws IOException {
String msg ="性命生命使命a";
//编码: 字节数组
byte[] datas = msg.getBytes(); //默认使用工程的字符集
System.out.println(datas.length);
//编码: 其他字符集
datas = msg.getBytes("UTF-16LE");
System.out.println(datas.length);
datas = msg.getBytes("GBK");
System.out.println(datas.length);
}
}
4.DirCount
package com.sxt.io;
import java.io.File;
/**
* 使用面向对象: 统计文件夹的大小
* @author 裴新
*
*/
public class DirCount {
//大小
private long len;
//文件夹路径
private String path;
//文件的个数
private int fileSize;
//文件夹的个数
private int dirSize;
//源
private File src;
public DirCount(String path) {
this.path = path;
this.src = new File(path);
count(this.src);
}
//统计大小
private void count(File src) {
//获取大小
if(null!=src && src.exists()) {
if(src.isFile()) { //大小
len+=src.length();
this.fileSize++;
}else { //子孙级
this.dirSize++;
for(File s:src.listFiles()) {
count(s);
}
}
}
}
public long getLen() {
return len;
}
public int getFileSize() {
return fileSize;
}
public int getDirSize() {
return dirSize;
}
public static void main(String[] args) {
DirCount dir = new DirCount("D:\\java300\\IO_study01");
System.out.println(dir.getLen()+"-->"+dir.getFileSize()+"-->"+dir.getDirSize());
DirCount dir2 = new DirCount("D:/java300/IO_study01/src");
System.out.println(dir2.getLen()+"-->"+dir2.getFileSize()+"-->"+dir2.getDirSize());
}
}
5.DirDemo01
package com.sxt.io;
import java.io.File;
/**
* 创建目录
* 1、mkdir() : 确保上级目录存在,不存在创建失败
* 2、mkdirs(): 上级目录可以不存在,不存在一同来创建
* @author 裴新
*
*/
public class DirDemo01 {
public static void main(String[] args) {
File dir = new File("D:/java300/IO_study01/dir/test");
//创建目录 mkdirs()
boolean flag = dir.mkdirs();
System.out.println(flag);
//创建目录 mkdir()
dir = new File("D:/java300/IO_study01/dir/test2");
flag = dir.mkdirs();
System.out.println(flag);
}
}
6.DirDemo02
package com.sxt.io;
import java.io.File;
/**
* 列出下一级
* 1、list() :列出下级名称
* 2、listFiles():列出下级File对象
*
* 列出所有的盘符: listRoots()
* @author 裴新
*
*/
public class DirDemo02 {
public static void main(String[] args) {
File dir = new File("D:/java300/IO_study01");
//下级名称 list
String[] subNames = dir.list();
for(String s:subNames) {
System.out.println(s);
}
//下级对象 listFiles()
File[] subFiles = dir.listFiles();
for(File s:subFiles) {
System.out.println(s.getAbsolutePath());
}
//所有盘符
File[] roots = dir.listRoots();
for(File r:roots) {
System.out.println(r.getAbsolutePath());
}
}
}
7.DirDemo03
package com.sxt.io;
/**
* 递归: 方法自己调用自己
* 递归头: 何时结束递归
* 递归体: 重复调用
* @author 裴新
*
*/
public class DirDemo03 {
public static void main(String[] args) {
printTen(1);
}
//打印1-10的数
public static void printTen(int n) {
if(n>10) { //递归头: 结束递归
return ;
}
System.out.println(n);
printTen(n+1);//递归体:方法自己调用自己
}
}
8.DirDemo04
package com.sxt.io;
import java.io.File;
/**
* 递归: 方法自己调用自己
* 打印子孙级目录和文件的名称
* @author 裴新
*
*/
public class DirDemo04 {
public static void main(String[] args) {
File src = new File("D:\\java300\\IO_study01");
printName(src,0);
}
//打印打印子孙级目录和文件的名称
public static void printName(File src,int deep) {
//控制前面层次
for(int i=0;i<deep;i++) {
System.out.print("-");
}
//打印名称
System.out.println(src.getName());
if(null ==src || !src.exists()) { //递归头
return ;
}else if(src.isDirectory()) { //目录
for(File s:src.listFiles()) {
printName(s,deep+1); //递归体
}
}
}
}
9.DirDemo05
package com.sxt.io;
import java.io.File;
/**
* 统计文件夹的大小
* @author 裴新
*
*/
public class DirDemo05 {
public static void main(String[] args) {
File src = new File("D:\\java300\\IO_study01");
count(src);
System.out.println(len);
}
private static long len =0;
public static void count(File src) {
//获取大小
if(null!=src && src.exists()) {
if(src.isFile()) { //大小
len+=src.length();
}else { //子孙级
for(File s:src.listFiles()) {
count(s);
}
}
}
}
}
10.FileDemo01
package com.sxt.io;
import java.io.File;
/**
*
* @author 裴新
*
*/
public class FileDemo01 {
/**
* 构建File对象
* @param args
*/
public static void main(String[] args) {
String path ="D:/java300/IO_study01/IO.png";
//1、构建File对象
File src = new File(path);
System.out.println(src.length());
//2、构建File对象
src = new File("D:/java300/IO_study01","IO.png");
src = new File("D:/java300/","IO_study01/IO.png");
System.out.println(src.length());
//3、构建File对象
src = new File(new File("D:/java300/IO_study01"),"IO.png");
System.out.println(src.length());
}
}
11.FileDemo02
package com.sxt.io;
import java.io.File;
/**
*
* @author 裴新
*
*/
public class FileDemo02 {
/**
* 构建File对象
* 相对路径与绝对路径
* 1)、存在盘符: 绝对路径
* 2)、不存在盘符:相对路径 ,当前目录 user.dir
* @param args
*/
public static void main(String[] args) {
String path ="D:/java300/IO_study01/IO.png";
//绝对路径
File src = new File(path);
System.out.println(src.getAbsolutePath());
//相对路径
System.out.println(System.getProperty("user.dir"));
src = new File("IO.png");
System.out.println(src.getAbsolutePath());
//构建一个不存在的文件
src = new File("aaa/IO2.png");
System.out.println(src.getAbsolutePath());
}
}
12.FileDemo03
package com.sxt.io;
import java.io.File;
/**
* 名称或路径
* getName() : 名称
* getPath() : 相对、绝对
getAbsolutePath() :绝对
getParent(): 上路径 返回null
* @author 裴新
*
*/
public class FileDemo03 {
public static void main(String[] args) {
File src = new File("IO_study01/IO.png");
//基本信息
System.out.println("名称:"+src.getName());
System.out.println("路径:"+src.getPath());
System.out.println("绝对路径:"+src.getAbsolutePath());
System.out.println("父路径:"+src.getParent());
System.out.println("父对象:"+src.getParentFile().getName());
}
}
14FileDemo04
package com.sxt.io;
import java.io.File;
/**
* 文件状态
* 1、不存在: exists
* 2、存在
* 文件: isFile
* 文件夹: isDirectory
* @author 裴新
*
*/
public class FileDemo04 {
public static void main(String[] args) {
File src = new File("IO_study01/IO.png");
System.out.println(src.getAbsolutePath());
System.out.println("是否存在:"+src.exists());
System.out.println("是否文件:"+src.isFile());
System.out.println("是否文件夹:"+src.isDirectory());
src = new File("IO.png");
System.out.println("----------");
System.out.println("是否存在:"+src.exists());
System.out.println("是否文件:"+src.isFile());
System.out.println("是否文件夹:"+src.isDirectory());
src = new File("D:\\java300\\IO_study01");
System.out.println("----------");
System.out.println("是否存在:"+src.exists());
System.out.println("是否文件:"+src.isFile());
System.out.println("是否文件夹:"+src.isDirectory());
//文件状态
src = new File("xxx");
if(null == src || !src.exists()) {
System.out.println("文件不存在");
}else {
if(src.isFile()) {
System.out.println("文件操作");
}else {
System.out.println("文件夹操作");
}
}
}
}
15FileDemo05
package com.sxt.io;
import java.io.File;
/**
* 其他信息
* length() : 文件的字节数
*
*
* @author 裴新
*
*/
public class FileDemo05 {
public static void main(String[] args) {
File src = new File("D:/java300/IO_study01/IO.png");
System.out.println("长度:"+ src.length());
src = new File("D:/java300/IO_study01");
System.out.println("长度:"+ src.length());
src = new File("D:/java300/IO_study02");
System.out.println("长度:"+ src.length());
}
}
16
package com.sxt.io;
import java.io.File;
import java.io.IOException;
/**
* 其他信息:
* createNewFile() : 不存在才创建,存在创建成功
* delete():删除已经存在的文件
*
*
* @author 裴新
*
*/
public class FileDemo06 {
public static void main(String[] args) throws IOException {
File src = new File("D:/java300/IO_study01/io.txt");
boolean flag =src.createNewFile();
System.out.println(flag);
flag = src.delete();
System.out.println(flag);
System.out.println("----------");
//不是文件夹
src = new File("D:/java300/IO_study02");
flag =src.createNewFile();
System.out.println(flag);
flag = src.delete();
System.out.println(flag);
//补充: con com3... 操作系统的设备名,不能正确创建
src = new File("D:/java300/IO_study01/con");
src.createNewFile();
}
}
10.1.5 Java中IO流类的体系
Java为我们提供了多种多样的IO流,我们可以根据不同的功能及性能要求挑选合适的IO流,如图10-7所示,为Java中IO流类的体系。
注:这里只列出常用的类,详情可以参考JDK API文档。粗体标注为常用!
从上图发现,很多流都是成对出现的,比如:FileInputStream/FileOutputStream,显然是对文件做输入和输出操作的。我们下面简单做个总结:
1.InputStream/OutputStream
字节流的抽象类。
2.Reader/Writer
字符流的抽象类。
3.FileInputStream/FileOutputStream
节点流:以字节为单位直接操作“文件”。
4.ByteArrayInputStream/ByteArrayOutputStream
节点流:以字节为单位直接操作“字节数组对象”。
5.ObjectInputStream/ObjectOutputStream
处理流:以字节为单位直接操作“对象”。
6.DataInputStream/DataOutputStream
处理流:以字节为单位直接操作“基本数据类型与字符串类型”。
7.FileReader/FileWriter
节点流:以字符为单位直接操作“文本文件”(注意:只能读写文本文件)。
8.BufferedReader/BufferedWriter
处理流:将Reader/Writer对象进行包装,增加缓存功能,提高读写效率。
9. BufferedInputStream/BufferedOutputStream
处理流:将InputStream/OutputStream对象进行包装,增加缓存功能,提高 读写效率。
10.InputStreamReader/OutputStreamWriter
处理流:将字节流对象转化成字符流对象。
11.PrintStream
处理流:将OutputStream进行包装,可以方便地输出字符,更加灵活。
老鸟建议:上面的解释,一句话就点中了流的核心作用。大家在后面学习的时候,用心体会。
10.1.6 四大IO抽象类
InputStream/OutputStream和Reader/writer类是所有IO流类的抽象父类,我们有必要简单了解一下这个四个抽象类的作用。然后,通过它们具体的子类熟悉相关的用法。
把握父类!
文件只是建立了一个联系,与操作系统打交道。
closeable是通知操作系统
·InputStream
此抽象类是表示字节输入流的所有类的父类。InputSteam是一个抽象类,它不可以实例化。 数据的读取需要由它的子类来实现。根据节点的不同,它派生了不同的节点流子类 。
继承自InputSteam的流都是用于向程序中输入数据,且数据的单位为字节(8 bit)。
常用方法:
int read():读取一个字节的数据,并将字节的值作为int类型返回(0-255之间的一个值)。如果未读出字节则返回-1(返回值为-1表示读取结束)。
void close():关闭输入流对象,释放相关系统资源。
· OutputStream
此抽象类是表示字节输出流的所有类的父类。输出流接收输出字节并将这些字节发送到某个目的地。
常用方法:
void write(int n):向目的地中写入一个字节。
void close():关闭输出流对象,释放相关系统资源。
· Reader
Reader用于读取的字符流抽象类,数据单位为字符。
int read(): 读取一个字符的数据,并将字符的值作为int类型返回(0-65535之间的一个值,即Unicode值)。如果未读出字符则返回-1(返回值为-1表示读取结束)。
void close() : 关闭流对象,释放相关系统资源。
· Writer
Writer用于写入的字符流抽象类,数据单位为字符。
void write(int n): 向输出流中写入一个字符。
void close() : 关闭输出流对象,释放相关系统资源。
10.1.7 IO标准步骤
1.创建源
2.选择流
3.操作
4.释放
public class IOTest01{
public static void main(String[] args){
//1.创建源
File src=new File("abc.txt");
//2.选择流
InputStream is=new FileInputStream(src);
//3.读取
int data1=is.read();//第一个数据h
int data2=is.read();//第二个数据e
System.out.println(data1);//104
System.out.println(data2);//101
System.out.println((char)data1);//h
System.out.println((char)data2);//e
//文件的末尾返回-1
//4.释放
is.close();
}
}
抛出异常
public class IOTest01{
public static void main(String[] args){
//1.创建源
File src=new File("abc.txt");
//2.选择流
InputStream is=null;
try{
is=new FileInputStream(src);
//3.读取
int temp;
while((temp=is.read())!=-1){
System.out.println(char(temp));
}
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
//文件的末尾返回-1
//4.释放
try{
if(null!=is){
is.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
10.2常见的流
10.2.1 文件字节流
FileInputStream通过字节的方式读取文件,适合读取所有类型的文件(图像、视频、文本文件等)。Java也提供了FileReader专门读取文本文件。
FileOutputStream 通过字节的方式写数据到文件中,适合所有类型的文件。Java也提供了FileWriter专门写入文本文件。
【示例10-3】将文件内容读取到程序中
参考【示例10-2】即可。
一个一个字节的读,通过循环一个一个字节的读。
一段一段的读
通过缓冲区
read(byte[] b)
public class IOTest01{
public static void main(String[] args){
//1.创建源
File src=new File("abc.txt");
//2.选择流
InputStream is=null;
try{
is=new FileInputStream(src);
//3.操作(读取,分段读取)
byte[] car=new byte[3];//缓冲容器 [1024]1k1k的读
int len=-1;//接收长度
while((len=is.read(car))!=-1){
//字节数组如何还原为字符串.字节数组--》字符串(解码)
String str=new String(car,0,len);
System.out.println(str);
}
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
//文件的末尾返回-1
//4.释放
try{
if(null!=is){
is.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
【示例10-4】将字符串/字节数组的内容写入到文件中
文件字节输出流
1创建源
2选择流
3操作
4释放资源
public class IOTest01{
public static void main(String[] args){
//1.创建源
File desc=new File("dest.txt");//inputstream中你要读取字节必须存在,而这里输出可以不存在系统自动创建
//2.选择流
OutputStream os=null;
try{
os=new FileOutputStream(dest,true);//默认是true表明以追加的形式写入文件,若为false则覆盖
//3.操作(写出)
String msg="IO is so easy";
byte[] datas=msg.getBytes();//字符串--》字节数组(编码)
os.write(datas,0,datas.length);
os.flush();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
//4.释放
try{
if(null!=os){
os.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
import java.io.FileOutputStream;
import java.io.IOException;
public class TestFileOutputStream {
public static void main(String[] args) {
FileOutputStream fos = null;
String string = “北京尚学堂欢迎您!”;
try {
// true表示内容会追加到文件末尾;false表示重写整个文件内容。
fos = new FileOutputStream(“d:/a.txt”, true);
//该方法是直接将一个字节数组写入文件中; 而write(int n)是写入一个字节
fos.write(string.getBytes());
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
在示例10-4中,用到一个write方法:void write(byte[ ] b),该方法不再一个字节一个字节地写入,而是直接写入一个字节数组;另外其还有一个重载的方法:void write(byte[ ] b, int off, int length),这个方法也是写入一个字节数组,但是我们程序员可以指定从字节数组的哪个位置开始写入,写入的长度是多少。
执行结果如图10-8所示:
现在我们已经学习了使用文件字节流分别实现文件的读取与写入操作,接下来我们将两种功能综合使用就可以轻松实现文件的复制了。
程序作为一个中转,读一个内容,写一个内容,即可实现复制。
public class IOTest01{
public static void main(String[] args){
copy("src/com.zcr.java.Copy.java","copy.txt");
}
public static void copy(String srcPath,String destPath){
//1.创建源
File src=new File("srcPath");//源头
File desc=new File("destPath");//目的地 //inputstream中你要读取字节必须存在,而这里输出可以不存在系统自动创建
//2.选择流
IntputStream is=null;
OutputStream os=null;
try{
is=new FileInputStream(src);
os=new FileOutputStream(dest,true);//默认是true表明以追加的形式写入文件,若为false则覆盖
//3.操作(读取,分段读取)
byte[] flush=new byte[1024];//缓冲容器 [1024]1k1k的读
int len=-1;
while((len=is.read(flush))!=-1){
//3.操作(写出)
//读的存在了字节中,那么就直接将这个字节进行写出
os.write(flush,0,len);
}
os.flush();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
//4.释放,分别关闭,先打开的后关闭
try{
if(null!=is){
is.close();
}
}catch(IOException e){
e.printStackTrace();
}
try{
if(null!=os){
os.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
【示例10-5】利用文件流实现文件的复制
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class TestFileCopy {
public static void main(String[] args) {
//将a.txt内容拷贝到b.txt
copyFile("d:/a.txt", "d:/b.txt");
}
/**
* 将src文件的内容拷贝到dec文件
* @param src 源文件
* @param dec 目标文件
*/
static void copyFile(String src, String dec) {
FileInputStream fis = null;
FileOutputStream fos = null;
//为了提高效率,设置缓存数组!(读取的字节数据会暂存放到该字节数组中)
byte[] buffer = new byte[1024];
int temp = 0;
try {
fis = new FileInputStream(src);
fos = new FileOutputStream(dec);
//边读边写
//temp指的是本次读取的真实长度,temp等于-1时表示读取结束
while ((temp = fis.read(buffer)) != -1) {
/*将缓存数组中的数据写入文件中,注意:写入的是读取的真实长度;
*如果使用fos.write(buffer)方法,那么写入的长度将会是1024,即缓存
*数组的长度*/
fos.write(buffer, 0, temp);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//两个流需要分别关闭
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
注意
在使用文件字节流时,我们需要注意以下两点:
1.**为了减少对硬盘的读写次数,提高效率,通常设置缓存数组。**相应地,读取时使用的方法为:read(byte[] b);写入时的方法为:write(byte[ ] b, int off, int length)。
2.程序中如果遇到多个流,每个流都要单独关闭,防止其中一个流出现异常后导致其他流无法关闭的情况。
10.2.2 文件字符流
前面介绍的文件字节流可以处理所有的文件,但是字节流不能很好的处理Unicode字符,经常会出现“乱码”现象。所以,我们处理文本文件,一般可以使用文件字符流,它以字符为单位进行操作。
FileReader:通过字符的方式读取文件,仅适合字符文件
FileWriter:通过字符的方式写出或追加数据到文件中,仅适合字符文件
之前处理的是字节数组
这里可以用来处理字符数组,即字符串
特殊之处:append
字符输入流
public class IOTest01{
public static void main(String[] args){
//1.创建源
File src=new File("abc.txt");
//2.选择流
Reader reader=null;
try{
reader=new FileReader(src);
//3.操作(读取,分段读取)
char[] flush=new char[1024];//缓冲容器 [1024]1k1k的读
int len=-1;//接收长度
while((len=reader.read(flush))!=-1){
//字符数组--》字符串
String str=new String(flush,0,len);
System.out.println(str);
}
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
//文件的末尾返回-1
//4.释放
try{
if(null!=reader){
reader.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
字符输出流
public class IOTest01{
public static void main(String[] args){
//1.创建源
File desc=new File("dest.txt");//inputstream中你要读取字节必须存在,而这里输出可以不存在系统自动创建
//2.选择流
Writer writer=null;
try{
writer=new FileOutputStream(dest);//默认是true表明以追加的形式写入文件,若为false则覆盖
//3.操作(写出)
/*写法一
String msg="很简单";
char[] datas=msg.toCharArray();//字符串--》字符数组
writer.write(datas,0,datas.length);
*/
/*写法二
String msg="很简单";
writer.write(msg);
writer.write(“的”);
*/
//写法三
writer.append(“很简单”).append(“的”);
writer.flush();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
//4.释放
try{
if(null!=writer){
writer.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
【示例10-6】使用FileReader与FileWriter实现文本文件的复制
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class TestFileCopy2 {
public static void main(String[] args) {
// 写法和使用Stream基本一样。只不过,读取时是读取的字符。
FileReader fr = null;
FileWriter fw = null;
int len = 0;
try {
fr = new FileReader("d:/a.txt");
fw = new FileWriter("d:/d.txt");
//为了提高效率,创建缓冲用的字符数组
char[] buffer = new char[1024];
//边读边写
while ((len = fr.read(buffer)) != -1) {
fw.write(buffer, 0, len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fw != null) {
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fr != null) {
fr.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
10.2.3 缓冲字节流
注意:看完装饰模式之后再返回来看缓冲字节流和缓冲字符流
装饰模式的具体:
对字节流的缓冲,底层一定要有一个节点流,否则就成了无本之木、无源之水
提高了读写的性能,IO
释放的时候,只需要释放外部的节点流,内部会自动释放
Java缓冲流本身并不具有IO流的读取与写入功能,只是在别的流(节点流或其他处理流)上加上缓冲功能提高效率,就像是把别的流包装起来一样,因此缓冲流是一种处理流(包装流)。
当对文件或者其他数据源进行频繁的读写操作时,效率比较低,这时如果使用缓冲流就能够更高效的读写信息。因为缓冲流是先将数据缓存起来,然后当缓存区存满后或者手动刷新时再一次性的读取到程序或写入目的地。
因此,缓冲流还是很重要的,我们在IO操作时记得加上缓冲流来提升性能。
BufferedInputStream和BufferedOutputStream这两个流是缓冲字节流,通过内部缓存数组来提高操作流的效率。
下面我们通过两种方式(普通文件字节流与缓冲文件字节流)实现一个视频文件的复制,来体会一下缓冲流的好处。
文件字节输入流 加入缓冲流
1创建源
2选择流
3操作
4释放资源
public class IOTest01{
public static void main(String[] args){
//1.创建源
File src=new File("abc.txt");
//2.选择流
InputStream is=null;
BufferedInputStream bis=null;
try{
is=new FileInputStream(src);
bis=new BufferedInputStream(is);
//3.操作(读取,分段读取)
byte[] car=new byte[3];//缓冲容器 [1024]1k1k的读
int len=-1;//接收长度
while((len=is.read(car))!=-1){
//字节数组如何还原为字符串.字节数组--》字符串(解码)
String str=new String(car,0,len);
System.out.println(str);
}
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
//文件的末尾返回-1
//4.释放
try{
if(null!=is){
is.close();
}
}catch(IOException e){
e.printStackTrace();
}
try{
if(null!=bis){
bis.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
简写:直接套上去就可以了
public class IOTest01{
public static void main(String[] args){
//1.创建源
File src=new File("abc.txt");
//2.选择流
InputStream is=null;
try{
is=new BufferedInputStream(new FileInputStream(src));
//3.操作(读取,分段读取)
byte[] car=new byte[3];//缓冲容器 [1024]1k1k的读
int len=-1;//接收长度
while((len=is.read(car))!=-1){
//字节数组如何还原为字符串.字节数组--》字符串(解码)
String str=new String(car,0,len);
System.out.println(str);
}
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
//文件的末尾返回-1
//4.释放
try{
if(null!=is){
is.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
文件字节输出流 加入缓冲流
1创建源
2选择流
3操作
4释放资源
public class IOTest01{
public static void main(String[] args){
//1.创建源
File desc=new File("dest.txt");//inputstream中你要读取字节必须存在,而这里输出可以不存在系统自动创建
//2.选择流
OutputStream os=null;
try{
os=new BufferedOutputStream(new FileOutputStream(dest));//默认是true表明以追加的形式写入文件,若为false则覆盖
//3.操作(写出)
String msg="IO is so easy";
byte[] datas=msg.getBytes();//字符串--》字节数组(编码)
os.write(datas,0,datas.length);
os.flush();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
//4.释放
try{
if(null!=os){
os.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
复制
public class IOTest01{
public static void main(String[] args){
long t1=System.currentTimeMillis();
copy("src/com.zcr.java.Copy.java","copy.txt");
long t2=System.currentTimeMillis();
System.out.println(t2-t1);//147ms
//加上缓冲流
//try(InputStream is=new BufferedInputStream(new FileInputStream(src));OutputStream os=new BufferedOutputStream(new FileOutputStream(dest));)//50ms
}
public static void copy(String srcPath,String destPath){
//1.创建源
File src=new File("srcPath");//源头
File desc=new File("destPath");//目的地 //inputstream中你要读取字节必须存在,而这里输出可以不存在系统自动创建
//2.选择流
try(InputStream is=new FileInputStream(src);OutputStream os=new FileOutputStream(dest);){//声明一定是要放在里面
//3.操作(读取,分段读取)
byte[] flush=new byte[1024];//缓冲容器 [1024]1k1k的读
int len=-1;
while((len=is.read(flush))!=-1){
//3.操作(写出)
//读的存在了字节中,那么就直接将这个字节进行写出
os.write(flush,0,len);
}
os.flush();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
//4.释放,分别关闭,先打开的后关闭
try{
if(null!=is){
is.close();
}
}catch(IOException e){
e.printStackTrace();
}
try{
if(null!=os){
os.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
【示例10-7】使用缓冲流实现文件的高效率复制
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class TestBufferedFileCopy1 {
public static void main(String[] args) {
// 使用缓冲字节流实现复制
long time1 = System.currentTimeMillis();
copyFile1("D:/电影/华语/大陆/尚学堂传奇.mp4", "D:/电影/华语/大陆/尚学堂越
"+"来越传奇.mp4");
long time2 = System.currentTimeMillis();
System.out.println("缓冲字节流花费的时间为:" + (time2 - time1));
// 使用普通字节流实现复制
long time3 = System.currentTimeMillis();
copyFile2("D:/电影/华语/大陆/尚学堂传奇.mp4", "D:/电影/华语/大陆/尚学堂越
"+"来越传奇2.mp4");
long time4 = System.currentTimeMillis();
System.out.println("普通字节流花费的时间为:" + (time4 - time3));
}
/**缓冲字节流实现的文件复制的方法*/
static void copyFile1(String src, String dec) {
FileInputStream fis = null;
BufferedInputStream bis = null;
FileOutputStream fos = null;
BufferedOutputStream bos = null;
int temp = 0;
try {
fis = new FileInputStream(src);
fos = new FileOutputStream(dec);
//使用缓冲字节流包装文件字节流,增加缓冲功能,提高效率
//缓存区的大小(缓存数组的长度)默认是8192,也可以自己指定大小
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
while ((temp = bis.read()) != -1) {
bos.write(temp);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//注意:增加处理流后,注意流的关闭顺序!“后开的先关闭!”
try {
if (bos != null) {
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bis != null) {
bis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**普通节流实现的文件复制的方法*/
static void copyFile2(String src, String dec) {
FileInputStream fis = null;
FileOutputStream fos = null;
int temp = 0;
try {
fis = new FileInputStream(src);
fos = new FileOutputStream(dec);
while ((temp = fis.read()) != -1) {
fos.write(temp);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
执行结果如图10-13所示:
注意
1.在关闭流时,应该先关闭最外层的包装流,即“后开的先关闭”。
2.缓存区的大小默认是8192字节,也可以使用其它的构造方法自己指定大小。
10.2.4 缓冲字符流
BufferedReader/BufferedWriter增加了缓存机制,大大提高了读写文本文件的效率,同时,提供了更方便的按行读取的方法:readLine(); 处理文本时,我们一般可以使用缓冲字符流。
不要发生多态,很多新增的方法你用不了。
字符输入流 加入缓冲流
public class IOTest01{
public static void main(String[] args){
//1.创建源
File src=new File("abc.txt");
//2.选择流
Reader reader=null;
try{
reader=new BufferedReader(new FileReader(src));
//3.操作(读取,分段读取)
char[] flush=new char[1024];//缓冲容器 [1024]1k1k的读
int len=-1;//接收长度
while((len=reader.read(flush))!=-1){
//字符数组--》字符串
String str=new String(flush,0,len);
System.out.println(str);
}
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
//文件的末尾返回-1
//4.释放
try{
if(null!=reader){
reader.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
字符输出流 加入缓冲流
public class IOTest01{
public static void main(String[] args){
//1.创建源
File desc=new File("dest.txt");//inputstream中你要读取字节必须存在,而这里输出可以不存在系统自动创建
//2.选择流
Writer writer=null;
try{
writer=new BufferedWriter(new FileWriter(dest));//默认是true表明以追加的形式写入文件,若为false则覆盖
//3.操作(写出)
/*写法一
String msg="很简单";
char[] datas=msg.toCharArray();//字符串--》字符数组
writer.write(datas,0,datas.length);
*/
/*写法二
String msg="很简单";
writer.write(msg);
writer.write(“的”);
*/
//写法三
writer.append(“很简单”).append(“的”);
writer.flush();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
//4.释放
try{
if(null!=writer){
writer.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
BufferedReader新增的功能是逐行读取readLine()
BufferedWriter新增的功能是逐行写入newLine()
字符输入流 加入缓冲流
public class IOTest01{
public static void main(String[] args){
//1.创建源
File src=new File("abc.txt");
//2.选择流
BufferedReader reader=null;
try{
reader=new BufferedReader(new FileReader(src));//要用到新增方法,不要用多态
//3.操作(读取,分段读取)
/*
char[] flush=new char[1024];//缓冲容器 [1024]1k1k的读
int len=-1;//接收长度
*/
String line=null;
while((line=reader.readLine())!=null){
/*//字符数组--》字符串
String str=new String(flush,0,len);
*/
System.out.println(line);
}
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
//文件的末尾返回-1
//4.释放
try{
if(null!=reader){
reader.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
字符输出流 加入缓冲流
public class IOTest01{
public static void main(String[] args){
//1.创建源
File desc=new File("dest.txt");//inputstream中你要读取字节必须存在,而这里输出可以不存在系统自动创建
//2.选择流
BufferedWriter writer=null;
try{
writer=new BufferedWriter(new FileWriter(dest));//默认是true表明以追加的形式写入文件,若为false则覆盖
//3.操作(写出)
/*写法一
String msg="很简单";
char[] datas=msg.toCharArray();//字符串--》字符数组
writer.write(datas,0,datas.length);
*/
/*写法二
String msg="很简单";
writer.write(msg);
writer.write(“的”);
*/
/*
//写法三
writer.append(“很简单”).append(“的”);
*/
writer.newLine();
writer.append("好好休息");//换行符就不需要再加\r\n了,直接用这个
writer.flush();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
//4.释放
try{
if(null!=writer){
writer.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
复制
public class IOTest01{
public static void main(String[] args){
long t1=System.currentTimeMillis();
copy("src/com.zcr.java.Copy.java","copy.txt");
long t2=System.currentTimeMillis();
System.out.println(t2-t1);//147ms
}
public static void copy(String srcPath,String destPath){
//1.创建源
File src=new File("srcPath");//源头
File desc=new File("destPath");//目的地 //inputstream中你要读取字节必须存在,而这里输出可以不存在系统自动创建
//2.选择流
try(BufferedReader br=new BufferedReader(new FileReader(src));
BufferedWriter bw=new BufferedWriter(new FileWriter(dest));){//声明一定是要放在里面
//3.操作(读取,逐行读取)
String line=null;
while((line=br.readLine()!=null){
//3.操作(写出)
br.write(line);//逐行写出
be.newLine();
}
os.flush();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
//4.释放,分别关闭,先打开的后关闭
try{
if(null!=is){
is.close();
}
}catch(IOException e){
e.printStackTrace();
}
try{
if(null!=os){
os.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
【示例10-8】使用BufferedReader与BufferedWriter实现文本文件的复制
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class TestBufferedFileCopy2 {
public static void main(String[] args) {
// 注:处理文本文件时,实际开发中可以用如下写法,简单高效!!
FileReader fr = null;
FileWriter fw = null;
BufferedReader br = null;
BufferedWriter bw = null;
String tempString = "";
try {
fr = new FileReader("d:/a.txt");
fw = new FileWriter("d:/d.txt");
//使用缓冲字符流进行包装
br = new BufferedReader(fr);
bw = new BufferedWriter(fw);
//BufferedReader提供了更方便的readLine()方法,直接按行读取文本
//br.readLine()方法的返回值是一个字符串对象,即文本中的一行内容
while ((tempString = br.readLine()) != null) {
//将读取的一行字符串写入文件中
bw.write(tempString);
//下次写入之前先换行,否则会在上一行后边继续追加,而不是另起一行
bw.newLine();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bw != null) {
bw.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
try {
if (br != null) {
br.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
try {
if (fw != null) {
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fr != null) {
fr.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
注意
1.readLine()方法是BufferedReader特有的方法,可以对文本文件进行更加方便的读取操作。
2.写入一行后要记得使用newLine()方法换行。
10.2.5 字节数组流
之前我们处理的源头都是文件,这个文件有一个特点存储在硬盘上,java虚拟机无权直接访问硬盘,必须借助操作系统。
所以我们借完操作系统后要通知操作系统释放。
现在我们把源头换为内存,不再是文件了,这个内存可以是你电脑上的内存、网络上的内存、远程服务器的内存。
java虚拟机可以直接访问内存,这里是由我们的垃圾回收机制gc释放的。
ByteArrayInputStream和ByteArrayOutputStream经常用在需要流和数组之间转化的情况!
说白了,FileInputStream是把文件当做数据源。ByteArrayInputStream则是把内存中的”某个字节数组对象”当做数据源。
字节数组:任何东西都可以转化为字节数组,包括数字、字符、对象都可以
文件可以无限制的往进加内容,而内存呢速度快量小有限制。
之前的构造丢的是一个文件,现在是一个字节数组
字节数组输入流
public class IOTest01{
public static void main(String[] args){
//1.创建源:字节数组 不要太大
byte[] src="talk is cheap show me the code".getBytes();
//2.选择流
InputStream is=null;
try{
is=new ByteArrayInputStream(src);
//3.操作(读取,分段读取)
byte[] flush=new char[1024];//缓冲容器 [1024]1k1k的读
int len=-1;//接收长度
while((len=is.read(flush))!=-1){
//字符数组--》字符串
String str=new String(flush,0,len);
System.out.println(str);
}
}catch(IOException e){
e.printStackTrace();
}//释放资源可以不用处理
}
}
构造方法不需要目的地
写完以后要去数组中拿一下
字节数组输出流
public class IOTest01{
public static void main(String[] args){
//1.创建源:源头内部来维护
byte[] desc=null;
//2.选择流(新增方法)(我们这里需要用它的toByteArray()新增方法,所以不要发生多态。)不关联源
//新增方法就是父类没有子类有,如果要使用新增方法不能发生多态
//父亲有的子类没有 都有同名的方法(多态) 子类有的父类没有
ByteArrayOutputStream baos=null;
try{
baos=new ByteArrayOutputStream ();
//3.操作(写出)
String msg="show me the code";
byte[] datas=msg.getBytes();//字符串--》字节数组
baos.write(datas,0,datas.length);
baos.flush();
//获取数据toByteArray()
dest=baos.toByteArray();
System.out.println(dest.length+"-->"+new String(dest,0,baos.size()));//16-->show me the code
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}
}
}
【示例10-9】简单测试ByteArrayInputStream 的使用
import java.io.ByteArrayInputStream;
import java.io.IOException;
public class TestByteArray {
public static void main(String[] args) {
//将字符串转变成字节数组
byte[] b = "abcdefg".getBytes();
test(b);
}
public static void test(byte[] b) {
ByteArrayInputStream bais = null;
StringBuilder sb = new StringBuilder();
int temp = 0;
//用于保存读取的字节数
int num = 0;
try {
//该构造方法的参数是一个字节数组,这个字节数组就是数据源
bais = new ByteArrayInputStream(b);
while ((temp = bais.read()) != -1) {
sb.append((char) temp);
num++;
}
System.out.println(sb);
System.out.println("读取的字节数:" + num);
} finally {
try {
if (bais != null) {
bais.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
执行结果如图10-14所示:
思考:对接流
将一张图片读到字节数组中,然后将它还原。
pic(文件) 写入 文件输入流 字节数组
文件输出流 读取
借用程序来中转
public class IOTest01{
public static void main(String[] args){
//图片转成字节数组
byte[] datas= fileToByteArray("p.png");
System.out.println(datas.length);
//字节数组转成图片
byteArrayToFile(datas,"pc.png")
}
//图片读取到字节数组中,字节数组还原为文件
/*1.图片读取到字节数组
1)图片到程序FileInputStream
2)程序到 字节数组ByteArrayOutPutStream
*/
public static byte[] fileToByteArray(String filePath){
//字节数组的目的地可以不用管
//1.创建源和目的地
File src=new File("filePath");//源头
byte[] desc=null;//目的地
//2.选择流
InputStream is=null;
ByteArrayOutputStream baos=null;
try{
is=new FileInputStream(src);
baos=new ByteArrayOutputStream ();
//3.操作(读取,分段读取)
byte[] flush=new byte[1024*10];//缓冲容器 [1024]1k1k的读
int len=-1;//接收长度
while((len=is.read(flush))!=-1){
//程序到 字节数组ByteArrayOutPutStream
//数据我们已经有了,即字节数组我们已经有了
baos.write(flush,0,len);//写出到字节数组中
}
baos.flush();
return baos.toByteArray();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
//文件的末尾返回-1
//4.释放
try{
if(null!=is){
is.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
return null;
}
}
/*2.字节数组写出到图片
1)字节数组到程序ByteArrayInputStream
2)程序到文件 FileOutputStream
*/
public static void byteArrayToFile(byte[] src,String filePath){
//1.创建源:字节数组 不要太大
//byte[] src=src;多此一举
File desc=new File(filePath);
//2.选择流
InputStream is=null;
OutputStream os=null;
try{
is=new ByteArrayInputStream(src);
os=new FileOutputStream(dest);
//3.操作(读取,分段读取)
byte[] flush=new char[1024];//缓冲容器 [1024]1k1k的读
int len=-1;//接收长度
while((len=is.read(flush))!=-1){
os.write(flush,0,len);
}
os.flush();
}catch(IOException e){
e.printStackTrace();
}finally{
//4.释放
try{
if(null!=os){
os.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
工具类
从文件到文件
文件到字节数组
字节数组到文件
大量的输入流输出流的对接
这节课我们将它们进行进一步的封装
将释放资源也封装
FileUtils.java
/**1.封装拷贝
*2.封装释放资源
*/
public class FileUtils{
public static void main(String[] args){
//文件到文件
try{
InputStream is=new FileInputStream(“abc.txt”);
OutputStream os=new FileOutputStream("abccopy.txt");
copy(is,os);
}catch(IOException e){
e.printStackTrace();
}
//文件到字节数组
byte[] datas=null;
try{
InputStream is=new FileInputStream(“p.png”);
ByteArrayOutputStream os=new ByteArrayOutputStream();
copy(is,os);
datas=os.toByteArray();
System.out.println(datas.length);
}catch(IOException e){
e.printStackTrace();
}
//字节数组到文件
try{
InputStream is=new ByteArrayInputStream(datas);
OutputStream os=new FileOutputStream("p-copy.png");
copy(is,os);
}catch(IOException e){
e.printStackTrace();
}
}
/**
*对接输入输出流
*/
public static void copy(IntputStream is,OutputStream os){
try{
is=new FileInputStream(src);
os=new FileOutputStream(dest,true);//默认是true表明以追加的形式写入文件,若为false则覆盖
//3.操作(读取,分段读取)
byte[] flush=new byte[1024];//缓冲容器 [1024]1k1k的读
int len=-1;
while((len=is.read(flush))!=-1){
//3.操作(写出)
//读的存在了字节中,那么就直接将这个字节进行写出
os.write(flush,0,len);
}
os.flush();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
close(is,os);
}
/**
*释放资源一
*/
public staic void close(IntputStream is,OutputStream os){
//4.释放,分别关闭,先打开的后关闭
try{
if(null!=is){
is.close();
}
}catch(IOException e){
e.printStackTrace();
}
try{
if(null!=os){
os.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
/**
*释放资源二
*前面我们讲过IntputStream OutputStream 都实现了closeable这个类
*/
public staic void close(Closeable... ios){//...表示多个参数
for(Closeable io:ios){
try{
if(null!=io){
io.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
FileUtils2.java
/**1.封装拷贝
*2.封装释放资源
*/
public class FileUtils{
public static void main(String[] args){
//文件到文件
try{
InputStream is=new FileInputStream(“abc.txt”);
OutputStream os=new FileOutputStream("abccopy.txt");
copy(is,os);
}catch(IOException e){
e.printStackTrace();
}
//文件到字节数组
byte[] datas=null;
try{
InputStream is=new FileInputStream(“p.png”);
ByteArrayOutputStream os=new ByteArrayOutputStream();
copy(is,os);
datas=os.toByteArray();
System.out.println(datas.length);
}catch(IOException e){
e.printStackTrace();
}
//字节数组到文件
try{
InputStream is=new ByteArrayInputStream(datas);
OutputStream os=new FileOutputStream("p-copy.png");
copy(is,os);
}catch(IOException e){
e.printStackTrace();
}
}
/**
*对接输入输出流
*try...with...resource//自动做释放,多个流之间用分号
*/
public static void copy(IntputStream is,OutputStream os){
try(is;os){
is=new FileInputStream(src);
os=new FileOutputStream(dest,true);//默认是true表明以追加的形式写入文件,若为false则覆盖
//3.操作(读取,分段读取)
byte[] flush=new byte[1024];//缓冲容器 [1024]1k1k的读
int len=-1;
while((len=is.read(flush))!=-1){
//3.操作(写出)
//读的存在了字节中,那么就直接将这个字节进行写出
os.write(flush,0,len);
}
os.flush();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
close(is,os);
}
}
这是把前面的最原始的copy方法改为try…with…resourse
直接在try()中进行声明
10.2.6 数据流
另外一种装饰模式
数据流将“基本数据类型与字符串类型”作为数据源,从而允许程序以与机器无关的方式从底层输入输出流中操作Java基本数据类型与字符串类型。
DataInputStream和DataOutputStream提供了可以存取与机器无关的所有Java基础类型数据(如:int、double、String等)的方法。
DataInputStream和DataOutputStream是处理流,可以对其他节点流或处理流进行包装,增加一些更灵活、更高效的功能。
数据流
1.写出后读取
2.读取的顺序与写出保持一致
public class DataTest{
public static void main(String[] args){
//写出
ByteArrayOutputStream baos=new ByteArrayOutputStream();//这个字节流(字节数组流)用新增的方法不用多态
DataOutputStream dos=new DataOutputStream(new BufferedOutptStream(baos));
//操作数据类型+数据
dos.writeUTF("啥似的");
dos.writerInt(18);
dos,writerBoolean(false);
dos.writeChar('a');
dos.flush();
byte[] datas=baos.toByteArray();
//读取
DataInputStream dis=new DataInputStream(new BufferedInputStream(new ByteArrayInputStream(datas)));
//顺序要一致
String msg=dis.readUTF();
int age=dis.readInt();
boolean flag=dis.readBoolean();
char ch=dis.readChar();
System.out.println(flag);
}
}
【示例10-10】DataInputStream和DataOutputStream的使用
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class TestDataStream {
public static void main(String[] args) {
DataOutputStream dos = null;
DataInputStream dis = null;
FileOutputStream fos = null;
FileInputStream fis = null;
try {
fos = new FileOutputStream("D:/data.txt");
fis = new FileInputStream("D:/data.txt");
//使用数据流对缓冲流进行包装,新增缓冲功能
dos = new DataOutputStream(new BufferedOutputStream(fos));
dis = new DataInputStream(new BufferedInputStream(fis));
//将如下数据写入到文件中
dos.writeChar('a');
dos.writeInt(10);
dos.writeDouble(Math.random());
dos.writeBoolean(true);
dos.writeUTF("北京尚学堂");
//手动刷新缓冲区:将流中数据写入到文件中
dos.flush();
//直接读取数据:读取的顺序要与写入的顺序一致,否则不能正确读取数据。
System.out.println("char: " + dis.readChar());
System.out.println("int: " + dis.readInt());
System.out.println("double: " + dis.readDouble());
System.out.println("boolean: " + dis.readBoolean());
System.out.println("String: " + dis.readUTF());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(dos!=null){
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(dis!=null){
dis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fos!=null){
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fis!=null){
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
执行结果如图10-15所示:
菜鸟雷区:使用数据流时,读取的顺序一定要与写入的顺序一致,否则不能正确读取数据。
10.2.7 对象流
内存:字节数组
不是所有的对象都可以序列化
我们前边学到的数据流只能实现对基本数据类型和字符串类型的读写,并不能读取对象(字符串除外),如果要对某个对象进行读写操作,我们需要学习一对新的处理流:ObjectInputStream/ObjectOutputStream。
ObjectInputStream/ObjectOutputStream是以“对象”为数据源,但是必须将传输的对象进行序列化与反序列化操作。
序列化与反序列化的具体内容,请见<10.3 Java对象的序列化和反序列化>。示例10-11仅演示对象流的简单应用。
1.写出后读取
2.读取的顺序与写出保持一致
3.不是所有的对象都可以序列化
public class ObjectTest{
public static void main(String[] args){
//写出---->序列化
ObjectOutputStream dos=new ObjectOutputStream(new BufferedOutptStream(new ByteArrayInputStream()));//写出到字节数组
ObjectOutputStream dos=new ObjectOutputStream(new BufferedOutptStream(new FileOutptStream("abc.txt")));//写出到文件
//操作数据类型+数据
dos.writeUTF("啥似的");
dos.writerInt(18);
dos,writerBoolean(false);
dos.writeChar('a');
dos.writeObject("师大哈佛hiu");//字符串也是对象
dos.writeObject(new Date());
Employee emp=new Employee(12,"ad");
dos.writeObject(emp);
dos.flush();
byte[] datas=baos.toByteArray();
//读取--反序列化
//ObjectInputStream dis=new ObjectInputStream(new BufferedInputStream(new ByteArrayInputStream(datas)));
ObjectInputStream dis=new ObjectInputStream(new BufferedInputStream(new FileInputStream("obj.ser")));
//顺序要一致
String msg=dis.readUTF();
int age=dis.readInt();
boolean flag=dis.readBoolean();
char ch=dis.readChar();
System.out.println(flag);
Object str=dis.readObject();
Object date=dis.readObject();
Object employee=dis.readObject();
if(str instanceof String){
String strObject=(String)str;
System.out.println(strObject);
}
if(date instanceof Date){
Date dataObject=(Date)date;
System.out.println(dataObject);
}
if(employee instanceof Employee){
Employee empObject=(Employee)employee ;
System.out.println(empObject.getName()+"--->"empObject.getAge());
}
}
//javabean
class Employee implement java.io.Serilizable{
private int age;
private transient String name;//该数据不需要序列化
构造方法、set/get/toString
【示例10-11】ObjectInputStream/ObjectOutputStream的使用
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.Date;
public class TestObjectStream {
public static void main(String[] args) throws IOException, ClassNotFoundException {
write();
read();
}
/**使用对象输出流将数据写入文件*/
public static void write(){
// 创建Object输出流,并包装缓冲流,增加缓冲功能
OutputStream os = null;
BufferedOutputStream bos = null;
ObjectOutputStream oos = null;
try {
os = new FileOutputStream(new File("d:/bjsxt.txt"));
bos = new BufferedOutputStream(os);
oos = new ObjectOutputStream(bos);
// 使用Object输出流
//对象流也可以对基本数据类型进行读写操作
oos.writeInt(12);
oos.writeDouble(3.14);
oos.writeChar('A');
oos.writeBoolean(true);
oos.writeUTF("北京尚学堂");
//对象流能够对对象数据类型进行读写操作
//Date是系统提供的类,已经实现了序列化接口
//如果是自定义类,则需要自己实现序列化接口
oos.writeObject(new Date());
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭输出流
if(oos != null){
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bos != null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(os != null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**使用对象输入流将数据读入程序*/
public static void read() {
// 创建Object输入流
InputStream is = null;
BufferedInputStream bis = null;
ObjectInputStream ois = null;
try {
is = new FileInputStream(new File("d:/bjsxt.txt"));
bis = new BufferedInputStream(is);
ois = new ObjectInputStream(bis);
// 使用Object输入流按照写入顺序读取
System.out.println(ois.readInt());
System.out.println(ois.readDouble());
System.out.println(ois.readChar());
System.out.println(ois.readBoolean());
System.out.println(ois.readUTF());
System.out.println(ois.readObject().toString());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭Object输入流
if(ois != null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bis != null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
执行结果如图10-16所示:
注意
1.对象流不仅可以读写对象,还可以读写基本数据类型。
2.使用对象流读写对象时,该对象必须序列化与反序列化。
3.系统提供的类(如Date等)已经实现了序列化接口,自定义类必须手动实现序列化接口。
10.2.7 打印流
也是装饰流
PrintTest.java
public class PrintTest{
public static void main(String[] args){
PrintStream ps=System.out;
ps.println("打印流");
ps.println(true);
ps=new PrintStream(new BufferedOutputStream(new FileOutputStream("print.txt")));
ps.println("打印流");
ps.println(true);
ps.flush();//也提供了自动刷新
//重定向输出端
System.setOut(ps);
System.out.println("change");
//重定向回控制台
System.setOut(new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.out))););
System.out.println("i back");
}
}
10.2.8 转换流
出了字符缓冲流和字节缓冲流,另外的一个装饰模式的流。
InputStreamReader/OutputStreamWriter用来实现将字节流转化成字符流。比如,如下场景:
System.in是字节流对象,代表键盘的输入,如果我们想按行接收用户的输入时,就必须用到缓冲字符流BufferedReader特有的方法readLine(),但是经过观察会发现在创建BufferedReader的构造方法的参数必须是一个Reader对象,这时候我们的转换流InputStreamReader就派上用场了。
而System.out也是字节流对象,代表输出到显示器,按行读取用户的输入后,并且要将读取的一行字符串直接显示到控制台,就需要用到字符流的write(String str)方法,所以我们要使用OutputStreamWriter将字节流转化为字符流。
解码:字节转成字符
字符集要统一,所以我们可以指定字符集。
处理纯文本内容
reader解码
writer编码
转换流InputStreamReader OutputStreamWriter
1.以字符流的形式操作字节流(纯文本的)
2.指定字符集
public class ConvertTest{
public static void main(String[] args){
//操作System.in和System.out
//InputStreamReader isr=new InputStreamReader(System.in);
//OutputStreamWriter osw=new OutputStreamWriter(System.out);
//传过来的都是字符串,凡是用到字符串的地方我们可以都加一个缓冲流
try(BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(System.out))?{
//循环获取键盘的输入(exit退出),输出此内容
String msg="";
while(!msg.equals(“exit”)){
msg=reader.readLine();//循环读取
writer.write(msg);//循环写出
writer.newLine();
writer.flush();//强制刷新一下,要不然根本进入不了下一次循环
}
}catch(IOException e){
System.out.println(“操作异常”);
}
}
处理一个字符集
public class ConvertTest{
public static void main(String[] args){
//操作网络流,下载百度的源代码
//InputStreamReader isr=new InputStreamReader(System.in);
//OutputStreamWriter osw=new OutputStreamWriter(System.out);
//传过来的都是字符串,凡是用到字符串的地方我们可以都加一个缓冲流
try(InputStream is=new URL(“http://www.baidu.com”).openStream()?{
int temp;
while(temp=is.read()!=-1){
System.out.print((char)temp);
}
}catch(IOException e){
System.out.println("操作异常");
}
}
以上乱码了!
public class ConvertTest{
public static void main(String[] args){
//操作网络流,下载百度的源代码
try(InputStreamReader is=new InputStreamReader(new URL(“http://www.baidu.com”).openStream(),“UTF-8”)? {
int temp;
while(temp=is.read()!=-1){
System.out.print((char)temp);//乱码的问题解决了
}
}catch(IOException e){
System.out.println("操作异常");
}
}
上面还不是完整的代码
public class ConvertTest{
public static void main(String[] args){
//操作网络流,下载百度的源代码
try(BufferedReader reader=new BufferedReader(//缓冲一下
new InputStreamReader(//转化成字符流
new URL(“http://www.baidu.com”).openStream(),“UTF-8”))?{
String msg;
while(msg=reader.readLine()!=null){
System.out.print(msg);
}
}catch(IOException e){
System.out.println("操作异常");
}
}
OutputStreamWriter可以指定输出内容的字符集
public class ConvertTest{
public static void main(String[] args){
//操作网络流,下载百度的源代码
try(BufferedReader reader=new BufferedReader(//缓冲一下
new InputStreamReader(//转化成字符流
new URL(“http://www.baidu.com”).openStream(),“UTF-8”));
try(BufferedWriter writer=new BufferedWriter(//缓冲一下
new OutputStreamWriter(
new FileOutputStream(“baidu.html”)“UTF-8”))?{//指定输出的字符集
String msg;
while(msg=reader.readLine()!=null){
//System.out.print(msg);
writer.write(msg);
writer.newLine();
}
}catch(IOException e){
System.out.println("操作异常");
}
}
【示例10-12】使用InputStreamReader接收用户的输入,并输出到控制台
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class TestConvertStream {
public static void main(String[] args) {
// 创建字符输入和输出流:使用转换流将字节流转换成字符流
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new InputStreamReader(System.in));
bw = new BufferedWriter(new OutputStreamWriter(System.out));
// 使用字符输入和输出流
String str = br.readLine();
// 一直读取,直到用户输入了exit为止
while (!“exit”.equals(str)) {
// 写到控制台
bw.write(str);
bw.newLine();// 写一行后换行
bw.flush();// 手动刷新
// 再读一行
str = br.readLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭字符输入和输出流
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
执行结果如图10-17所示:
10.3 序列化
10.3.1 序列化和反序列化是什么
当两个进程远程通信时,彼此可以发送各种类型的数据。 无论是何种类型的数据,都会以二进制序列的形式在网络上传送。比如,我们可以通过http协议发送字符串信息;我们也可以在网络上直接发送Java对象。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象才能正常读取。
把Java对象转换为字节序列的过程称为对象的序列化。把字节序列恢复为Java对象的过程称为对象的反序列化。
对象序列化的作用有如下两种:
1. 持久化: 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中,比如:休眠的实现。以后服务器session管理,hibernate将对象持久化实现。
2. 网络通信:在网络上传送对象的字节序列。比如:服务器之间的数据通信、对象传递。
10.3.2 序列化涉及的类和接口
ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
只有实现了Serializable接口的类的对象才能被序列化。 Serializable接口是一个空接口,只起到标记作用。
10.3.3 序列化/反序列化的步骤和实例
【示例10-14】将Person类的实例进行序列化和反序列化
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
//Person类实现Serializable接口后,Person对象才能被序列化
class Person implements Serializable {
// 添加序列化ID,它决定着是否能够成功反序列化!
private static final long serialVersionUID = 1L;
int age;
boolean isMan;
String name;
public Person(int age, boolean isMan, String name) {
super();
this.age = age;
this.isMan = isMan;
this.name = name;
}
@Override
public String toString() {
return "Person [age=" + age + ", isMan=" + isMan + ", name=" + name + "]";
}
}
public class TestSerializable {
public static void main(String[] args) {
FileOutputStream fos = null;
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
FileInputStream fis = null;
try {
// 通过ObjectOutputStream将Person对象的数据写入到文件中,即序列化。
Person person = new Person(18, true, “高淇”);
// 序列化
fos = new FileOutputStream(“d:/c.txt”);
oos = new ObjectOutputStream(fos);
oos.writeObject(person);
oos.flush();
// 反序列化
fis = new FileInputStream(“d:/c.txt”);
// 通过ObjectInputStream将文件中二进制数据反序列化成Person对象:
ois = new ObjectInputStream(fis);
Person p = (Person) ois.readObject();
System.out.println(p);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
执行结果如图10-19所示:
注意
1.static属性不参与序列化。
2.对象中的某些属性如果不想被序列化,不能使用static,而是使用transient修饰。
3.为了防止读和写的序列化ID不一致,一般指定一个固定的序列化ID。
10.4 装饰模式
10.4.1 装饰器模式简介
节点流:与源点直接进行交互
处理流:
设计模式是解决复杂代码的一种策略
单例、工厂、装饰
装饰器模式是GOF23种设计模式中较为常用的一种模式。它可以实现对原有类的包装和装饰,使新的类具有更强的功能。
我这里有智能手机iphone, 我们可以通过加装投影组件,实现原有手机功能的扩展。这就是一种“装饰器模式”。 我们在未来给普通人加装“外骨骼”装饰,让普通人具有力扛千斤的能力,也是一种“装饰器模式”。
DecorateTest01.java
实现放大镜对声音的放大功能
public class DecorateTest01{
public static void main(String[] args){
Person p=new Person();
p.say();
//装饰
Amplifier am=new Amplifier§;
am.say();
}
}
interface Say{
void say();
}
class Person implements Say{
private int voice=10;
public void say(){
System.out.println(“人的声音为:”+this.getVoice());
}
public int getVoice(){
return voice;
}
public void setVoice(int voice){
this.voice=voice;
}
}
//装饰类:放大器
class Amplifier implements Say{
private Person p;
Amplifier(Person p){
this.p=p;
}
public void say(){
System.out.println(“人的声音为:”+p.getVoice()*100);
System.out.println(“噪音!”);
}
}
DecorateTest02.java
模拟咖啡
1.抽象组件:需要装饰的抽象对象(接口或抽象父类)
2.具体组件:需要装饰的对象
3.抽象装饰类:包含了对抽象组建的引用以及装饰着共有的方法
4.具体装饰类:被装饰的对象
public class DecorateTest02{
public static void main(String[] args){
Drink coffee=new Coffee();
Drink suger=new Suger(coffee);//装饰
System.out.println(suger.info()+"–>"+suger.cost());//原味咖啡加入了蔗糖–>20.0
Drink milk=new Milk(coffee);//装饰
System.out.println(milk.info()+"–>"+milk.cost());//原味咖啡加入了牛奶–>40.0
milk=new Milk(suger);//装饰
System.out.println(milk.info()+"-->"+milk.cost());//原味咖啡加入了蔗糖加入了牛奶-->80.0
//在不改变原来代码的基础上直接到里面丢就可以达到新的功能
}
}
//抽象组件
interface Drink{
double cost();//费用
String info();//说明
}
//具体组件
class Coffee implements Drink{
private String name=“原味咖啡”;
public double cost(){
return 10;
}
public String info(){
return name;
}
}
//抽象装饰类
abstract class Decorate implements Drink{
//对抽象组件的引用
private Drink drink;
public Decorate(Drink drink){
this.drink=drink;
}
public double cost(){
return this.drink;
}
public String info(){
return this.drink.info();
}
}
//具体装饰类
class Milk extends Decorate{
public Milk(Drink drink){
super(drink);
}
public double cost(){
return super.cost()*4;
}
public String info(){
return super.info()+“加入了牛奶”;
}
}
//具体装饰类
class Suger extends Decorate{
public Suger(Drink drink){
super(drink);
}
public double cost(){
return super.cost()*2;
}
public String info(){
return super.info()+“加入了蔗糖”;
}
}
【示例10-15】装饰器模式演示
class Iphone {
private String name;
public Iphone(String name) {
this.name = name;
}
public void show() {
System.out.println("我是" + name + ",可以在屏幕上显示");
}
}
class TouyingPhone {
public Iphone phone;
public TouyingPhone(Iphone p) {
this.phone = p;
}
// 功能更强的方法
public void show() {
phone.show();
System.out.println("还可以投影,在墙壁上显示");
}
}
public class TestDecoration {
public static void main(String[] args) {
Iphone phone = new Iphone("iphone30");
phone.show();
System.out.println("===============装饰后");
TouyingPhone typhone = new TouyingPhone(phone);
typhone.show();
}
}
执行结果如图10-21所示:
10.4.2 IO流体系中的装饰器模式
IO流体系中大量使用了装饰器模式,让流具有更强的功能、更强的灵活性。比如:
FileInputStream fis = new FileInputStream(src);
BufferedInputStream bis = new BufferedInputStream(fis);
显然BufferedInputStream装饰了原有的FileInputStream,让普通的FileInputStream也具备了缓存功能,提高了效率。 大家举一反三,可以翻看本章代码,看看还有哪些地方使用了装饰器模式。
IO文件分割
IO文件分割_面向过程核心版
既可以支持读,又可以支持写
通过模式进行切换
package com.sxt.io;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
- 随机读取和写入流 RandomAccessFile
- @author 裴新
*/
public class RandTest01 {
public static void main(String[] args) throws IOException {
//分多少块
File src = new File("src/com/sxt/io/Copy.java");
//总长度
long len = src.length();
//每块大小
int blockSize =1024;
//块数: 多少块
int size =(int) Math.ceil(len*1.0/blockSize);
System.out.println(size);//135
//起始位置和实际大小
int beginPos = 0;
int actualSize = (int)(blockSize>len?len:blockSize);
for(int i=0;i<size;i++) {
beginPos = i*blockSize;
if(i==size-1) { //最后一块
actualSize = (int)len;
}else {
actualSize = blockSize;
len -=actualSize; //剩余量
}
System.out.println(i+"-->"+beginPos +"-->"+actualSize);
split(i,beginPos,actualSize);
}
}
/**
* 指定第i块的起始位置 和实际长度
* @param i
* @param beginPos
* @param actualSize
* @throws IOException
*/
public static void split(int i,int beginPos,int actualSize ) throws IOException {
RandomAccessFile raf =new RandomAccessFile(new File("src/com/sxt/io/Copy.java"),"r");
//随机读取
raf.seek(beginPos);
//读取
//3、操作 (分段读取)
byte[] flush = new byte[1024]; //缓冲容器
int len = -1; //接收长度
while((len=raf.read(flush))!=-1) {
if(actualSize>len) { //获取本次读取的所有内容
System.out.println(new String(flush,0,len));
actualSize -=len;
}else {
System.out.println(new String(flush,0,actualSize));
break;
}
}
raf.close();
}
//分开思想: 起始、实际大小
public static void test2() throws IOException {
RandomAccessFile raf =new RandomAccessFile(new File("src/com/sxt/io/Copy.java"),"r");
//起始位置
int beginPos =2+1026;
//实际大小
int actualSize = 1026;
//随机读取
raf.seek(beginPos);
//读取
//3、操作 (分段读取)
byte[] flush = new byte[1024]; //缓冲容器
int len = -1; //接收长度
while((len=raf.read(flush))!=-1) {
if(actualSize>len) { //获取本次读取的所有内容
System.out.println(new String(flush,0,len));
actualSize -=len;
}else {
System.out.println(new String(flush,0,actualSize));
break;
}
}
raf.close();
}
//指定起始位置,读取剩余所有内容
public static void test1() throws IOException {
RandomAccessFile raf =new RandomAccessFile(new File("src/com/sxt/io/Copy.java"),"r");
//随机读取
raf.seek(2);
//读取
//3、操作 (分段读取)
byte[] flush = new byte[1024]; //缓冲容器
int len = -1; //接收长度
while((len=raf.read(flush))!=-1) {
System.out.println(new String(flush,0,len));
}
raf.close();
}
}
文件分割面向对象终极版以及文件的合并
package com.sxt.io;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
- 随机读取和写入流 RandomAccessFile
- @author 裴新
*/
public class RandTest02 {
public static void main(String[] args) throws IOException {
//分多少块
File src = new File("p.png");
//总长度
long len = src.length();
//每块大小
int blockSize =1024;
//块数: 多少块
int size =(int) Math.ceil(len*1.0/blockSize);
System.out.println(size);
//起始位置和实际大小
int beginPos = 0;
int actualSize = (int)(blockSize>len?len:blockSize);
for(int i=0;i<size;i++) {
beginPos = i*blockSize;
if(i==size-1) { //最后一块
actualSize = (int)len;
}else {
actualSize = blockSize;
len -=actualSize; //剩余量
}
System.out.println(i+"-->"+beginPos +"-->"+actualSize);
split(i,beginPos,actualSize);
}
}
/**
* 指定第i块的起始位置 和实际长度
* @param i
* @param beginPos
* @param actualSize
* @throws IOException
*/
public static void split(int i,int beginPos,int actualSize ) throws IOException {
RandomAccessFile raf =new RandomAccessFile(new File("p.png"),"r");
RandomAccessFile raf2 =new RandomAccessFile(new File("dest/"+i+"p.png"),"rw");
//随机读取
raf.seek(beginPos);
//读取
//3、操作 (分段读取)
byte[] flush = new byte[1024]; //缓冲容器
int len = -1; //接收长度
while((len=raf.read(flush))!=-1) {
if(actualSize>len) { //获取本次读取的所有内容
raf2.write(flush, 0, len);
actualSize -=len;
}else {
raf2.write(flush, 0, actualSize);
break;
}
}
raf2.close();
raf.close();
}
}
SplitFile.java
package com.sxt.io;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
/**
- 面向对象思想封装 分割
- @author 裴新
*/
public class SplitFile {
//源头
private File src;
//目的地(文件夹)
private String destDir;
//所有分割后的文件存储路径
private List destPaths;
//每块大小
private int blockSize;
//块数: 多少块
private int size;
public SplitFile(String srcPath,String destDir) {
this(srcPath,destDir,1024);
}
public SplitFile(String srcPath,String destDir,int blockSize) {
this.src =new File(srcPath);
this.destDir =destDir;
this.blockSize =blockSize;
this.destPaths =new ArrayList<String>();
//初始化
init();
}
//初始化
private void init() {
//总长度
long len = this.src.length();
//块数: 多少块
this.size =(int) Math.ceil(len*1.0/blockSize);
//路径
for(int i=0;i<size;i++) {
this.destPaths.add(this.destDir +"/"+i+"-"+this.src.getName());
}
}
/**
* 分割
* 1、计算每一块的起始位置及大小
* 2、分割
* @throws IOException
*/
public void split() throws IOException {
//总长度
long len = src.length();
//起始位置和实际大小
int beginPos = 0;
int actualSize = (int)(blockSize>len?len:blockSize);
for(int i=0;i<size;i++) {
beginPos = i*blockSize;
if(i==size-1) { //最后一块
actualSize = (int)len;
}else {
actualSize = blockSize;
len -=actualSize; //剩余量
}
splitDetail(i,beginPos,actualSize);
}
}
/**
* 指定第i块的起始位置 和实际长度
* @param i
* @param beginPos
* @param actualSize
* @throws IOException
*/
private void splitDetail(int i,int beginPos,int actualSize ) throws IOException {
RandomAccessFile raf =new RandomAccessFile(this.src,"r");
RandomAccessFile raf2 =new RandomAccessFile(this.destPaths.get(i),"rw");
//随机读取
raf.seek(beginPos);
//读取
//3、操作 (分段读取)
byte[] flush = new byte[1024]; //缓冲容器
int len = -1; //接收长度
while((len=raf.read(flush))!=-1) {
if(actualSize>len) { //获取本次读取的所有内容
raf2.write(flush, 0, len);
actualSize -=len;
}else {
raf2.write(flush, 0, actualSize);
break;
}
}
raf2.close();
raf.close();
}
/**
* 文件的合并
* @throws IOException
*/
public void merge(String destPath) throws IOException {
//输出流
OutputStream os =new BufferedOutputStream( new FileOutputStream(destPath,true));
Vector<InputStream> vi=new Vector<InputStream>();
SequenceInputStream sis =null;
//输入流
for(int i=0;i<destPaths.size();i++) {
vi.add(new BufferedInputStream(new FileInputStream(destPaths.get(i))));
}
sis =new SequenceInputStream(vi.elements());
//拷贝
//3、操作 (分段读取)
byte[] flush = new byte[1024]; //缓冲容器
int len = -1; //接收长度
while((len=sis.read(flush))!=-1) {
os.write(flush,0,len); //分段写出
}
os.flush();
sis.close();
os.close();
}
public static void main(String[] args) throws IOException {
SplitFile sf = new SplitFile("src/com/sxt/io/SplitFile.java","dest") ;
sf.split();
sf.merge("aaa.java");
}
}
10.5 Apache IOUtils和FileUtils的使用
JDK中提供的文件操作相关的类,但是功能都非常基础,进行复杂操作时需要做大量编程工作。实际开发中,往往需要你自己动手编写相关的代码,尤其在遍历目录文件时,经常用到递归,非常繁琐。 Apache-commons工具包中提供了IOUtils/FileUtils,可以让我们非常方便的对文件和目录进行操作。 本文就是让大家对IOUtils/FileUtils类有一个全面的认识,便于大家以后开发与文件和目录相关的功能。
Apache IOUtils和FileUtils类库为我们提供了更加简单、功能更加强大的文件操作和IO流操作功能。非常值得大家学习和使用。
10.5.1 Apache基金会介绍
Apache软件基金会(也就是Apache Software Foundation,简称为ASF),是专门为支持开源软件项目而办的一个非盈利性组织。在它所支持的Apache项目与子项目中,所发行的软件产品都遵循Apache许可证(Apache License)。 官方网址为:www.apache.org 。
很多著名的Java开源项目都来源于这个组织。比如:commons、kafka、lucene、maven、shiro、struts等技术,以及大数据技术中的:hadoop(大数据第一技术)、hbase、spark、storm、mahout等。
10.5.2 FileUtils的妙用
·jar包下载和介绍
首先,我们要下载FileUtils相关的Apache-commons-io jar包以及api文档。FileUtils类库的下载页面在:
http://commons.apache.org/proper/commons-io/download_io.cgi
点击进入下载页面:
API文档的页面:http://commons.apache.org/proper/commons-io/javadocs/api-2.5/index.html
我们本次示例,下载了最新的2.5版本,作为测试和示范。
·eclpise项目如何导入外部的jar包操作
1.在eclipse项目下新建lib文件夹
2.解压下载后的版本,找到commons-io-2.5.jar包,并拷贝到lib文件夹下
3. 设置jar包进入项目的classpath中。
项目名上右击,依次选择【Build Path】–>【Configure Build Path…】,在打开的窗口中,先选中【Libraries】页,再从右边的按钮中点击 【add JARs…】; 在打开的窗口中,我们依次展开本项目的项目和lib文件夹,然后选中我们刚才复制到项目中的jar包,然后点击【Apply】使刚才的操作生效,最后点击【OK】关闭窗口。
项目结构如下:
新手雷区:很多初学者会忘记配置项目的classpath,从而项目找不到相关的jar包。大家可以在此处多配置几次,直到足够熟练!
· FieUtils类中常用方法的介绍
打开FileUtils的api文档,我们抽出一些工作中比较常用的方法,进行总结和讲解。总结如下:
cleanDirectory:清空目录,但不删除目录。
contentEquals:比较两个文件的内容是否相同。
copyDirectory:将一个目录内容拷贝到另一个目录。可以通过FileFilter过滤需要拷贝的 文件。
copyFile:将一个文件拷贝到一个新的地址。
copyFileToDirectory:将一个文件拷贝到某个目录下。
copyInputStreamToFile:将一个输入流中的内容拷贝到某个文件。
deleteDirectory:删除目录。
deleteQuietly:删除文件。
listFiles:列出指定目录下的所有文件。
openInputSteam:打开指定文件的输入流。
readFileToString:将文件内容作为字符串返回。
readLines:将文件内容按行返回到一个字符串数组中。
size:返回文件或目录的大小。
write:将字符串内容直接写到文件中。
writeByteArrayToFile:将字节数组内容写到文件中。
writeLines:将容器中的元素的toString方法返回的内容依次写入文件中。
writeStringToFile:将字符串内容写到文件中。
·代码演示
统计文件大小和目录大小
package com.sxt.commons;
import java.io.File;
import org.apache.commons.io.FileUtils;
/**
* 大小
*/
public class CIOTest01 {
public static void main(String[] args) {
//文件大小
long len =FileUtils.sizeOf(new File("src/com/sxt/commons/CIOTest01.java"));
System.out.println(len);
//目录大小
len = FileUtils.sizeOf(new File("D:/java300/IO_study04"));
System.out.println(len);
}
}
列出子孙级
package com.sxt.commons;
import java.io.File;
import java.util.Collection;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.apache.commons.io.filefilter.EmptyFileFilter;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.SuffixFileFilter;
/**
* @author 裴新
*
*/
public class CIOTest02 {
public static void main(String[] args) {
Collection<File> files =FileUtils.listFiles(new File("D:\\java300\\IO_study04"),
EmptyFileFilter.NOT_EMPTY, null);//过滤空的文件
for (File file : files) {
System.out.println(file.getAbsolutePath());
}
System.out.println("---------------------");
files =FileUtils.listFiles(new File("D:\\java300\\IO_study04"),
EmptyFileFilter.NOT_EMPTY, DirectoryFileFilter.INSTANCE);//操作子孙级
for (File file : files) {
System.out.println(file.getAbsolutePath());
}
System.out.println("---------------------");
files =FileUtils.listFiles(new File("D:\\java300\\IO_study04"),
new SuffixFileFilter("java"), DirectoryFileFilter.INSTANCE);//只看后缀是.java的
for (File file : files) {
System.out.println(file.getAbsolutePath());
}
System.out.println("---------------------");
files =FileUtils.listFiles(new File("D:\\java300\\IO_study04"),
FileFilterUtils.or(new SuffixFileFilter("java"),
new SuffixFileFilter("class"),EmptyFileFilter.EMPTY), DirectoryFileFilter.INSTANCE);
for (File file : files) {
System.out.println(file.getAbsolutePath());
}
System.out.println("---------------------");
files =FileUtils.listFiles(new File("D:\\java300\\IO_study04"),
FileFilterUtils.and(new SuffixFileFilter("java"),
EmptyFileFilter.NOT_EMPTY), DirectoryFileFilter.INSTANCE);
for (File file : files) {
System.out.println(file.getAbsolutePath());
}
}
}
读取内容
package com.sxt.commons;
import java.io.File;
import java.io.IOException;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.LineIterator;
/**
* 读取内容
* @author 裴新
*
*/
public class CIOTest03 {
public static void main(String[] args) throws IOException {
//读取文件
String msg =FileUtils.readFileToString(new File("emp.txt"),"UTF-8");
System.out.println(msg);//读取内容
byte[] datas = FileUtils.readFileToByteArray(new File("emp.txt"));//读到字节数组中
System.out.println(datas.length);
//逐行读取
List<String> msgs= FileUtils.readLines(new File("emp.txt"),"UTF-8");//把每一行丢到容器里面
for (String string : msgs) {
System.out.println(string);
}
LineIterator it =FileUtils.lineIterator(new File("emp.txt"),"UTF-8");//用迭代器逐行读取
while(it.hasNext()) {
System.out.println(it.nextLine());
}
}
}
写出内容
package com.sxt.commons;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.FileUtils;
/**
* 写出内容
* @author 裴新
*
*/
public class CIOTest04 {
public static void main(String[] args) throws IOException {
//写出文件
FileUtils.write(new File("happy.sxt"), "学习是一件伟大的事业\r\n","UTF-8");
FileUtils.writeStringToFile(new File("happy.sxt"), "学习是一件辛苦的事业\r\n","UTF-8",true);//是否追加文件
FileUtils.writeByteArrayToFile(new File("happy.sxt"), "学习是一件幸福的事业\r\n".getBytes("UTF-8"),true);//操作字节数组
//写出列表
List<String> datas =new ArrayList<String>();
datas.add("马云");
datas.add("马化腾");
datas.add("弼马温");
FileUtils.writeLines(new File("happy.sxt"), datas,"。。。。。",true);//第三个参数是间隔符
}
}
拷贝
package com.sxt.commons;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
/**
* 拷贝
* @author 裴新
*
*/
public class CIOTest05 {
public static void main(String[] args) throws IOException {
//复制文件
//FileUtils.copyFile(new File("p.png"),new File("p-copy.png"));
//复制文件到目录
//FileUtils.copyFileToDirectory(new File("p.png"),new File("lib"));
//复制目录到目录
//FileUtils.copyDirectoryToDirectory(new File("lib"),new File("lib2"));
//复制目录
//FileUtils.copyDirectory(new File("lib"),new File("lib2"));
//拷贝URL内容
//String url = "https://pic2.zhimg.com/v2-7d01cab20858648cbf62333a7988e6d0_qhd.jpg";
//FileUtils.copyURLToFile(new URL(url), new File("marvel.jpg"));
String datas =IOUtils.toString(new URL("http://www.163.com"), "gbk");
System.out.println(datas);
}
}
【示例10-16】读取文件内容,并输出到控制台上(只需一行代码!)
import java.io.File;
import org.apache.commons.io.FileUtils;
public class TestUtils1 {
public static void main(String[] args) throws Exception {
String content = FileUtils.readFileToString(new File("d:/a.txt"), "gbk");
System.out.println(content);
}
}
执行结果如图10-26所示:
【示例10-17】目录拷贝,并使用FileFilter过滤目录和以html结尾的文件
import java.io.File;
import java.io.FileFilter;
import org.apache.commons.io.FileUtils;
public class TestUtils2 {
public static void main(String[] args) throws Exception {
FileUtils.copyDirectory(new File("d:/aaa"), new File("d:/bbb"), new FileFilter() {
@Override
public boolean accept(File pathname) {
// 使用FileFilter过滤目录和以html结尾的文件
if (pathname.isDirectory() || pathname.getName().endsWith("html")) {
return true;
} else {
return false;
}
}
});
}
}
执行结果如图10-27所示:
10.5.3 IOUtils的妙用
打开IOUtils的api文档,我们发现它的方法大部分都是重载的。所以,我们理解它的方法并不是难事。因此,对于方法的用法总结如下:
1.buffer方法:将传入的流进行包装,变成缓冲流。并可以通过参数指定缓冲大小。
2.closeQueitly方法:关闭流。
3.contentEquals方法:比较两个流中的内容是否一致。
4.copy方法:将输入流中的内容拷贝到输出流中,并可以指定字符编码。
5.copyLarge方法:将输入流中的内容拷贝到输出流中,适合大于2G内容的拷贝。
6 lineIterator方法:返回可以迭代每一行内容的迭代器。
7.read方法:将输入流中的部分内容读入到字节数组中。
8. readFully方法:将输入流中的所有内容读入到字节数组中。
9.readLine方法:读入输入流内容中的一行。
10.toBufferedInputStream,toBufferedReader:将输入转为带缓存的输入流。
11.toByteArray,toCharArray:将输入流的内容转为字节数组、字符数组。
12.toString:将输入流或数组中的内容转化为字符串。
13.write方法:向流里面写入内容。
14. writeLine方法:向流里面写入一行内容。
我们没有必要对每个方法做测试,只是演示一下读入d:/a.txt文件内容到程序中,并转成String对象,打印出来。
【示例10-18】IOUtils的方法
import java.io.*;
import org.apache.commons.io.IOUtils;
public class TestUtils3 {
public static void main(String[] args) throws Exception {
String content = IOUtils.toString(new FileInputStream("d:/a.txt"),"gbk");
System.out.println(content);
}
}
执行结果如图10-28所示:
第十章 总结
1.按流的方向分类:
输入流:数据源到程序(InputStream、Reader读进来)。
输出流:程序到目的地(OutPutStream、Writer写出去)。
2.按流的处理数据单元分类:
字节流:按照字节读取数据(InputStream、OutputStream)。
字符流:按照字符读取数据(Reader、Writer)。
3.按流的功能分类:
节点流:可以直接从数据源或目的地读写数据。
处理流:不直接连接到数据源或目的地,是处理流的流。通过对其他流的处理提高程序的性能。
4.IO的四个基本抽象类:InputStream、OutputStream、Reader、Writer
5.InputStream的实现类:
FileInputStream
ByteArrayInutStream
BufferedInputStream
DataInputStream
ObjectInputStream
6.OutputStream的实现类:
FileOutputStream
ByteArrayOutputStream
BufferedOutputStream
DataOutputStream
ObjectOutputStream
PrintStream
7.Reader的实现类
FileReader
BufferedReader
InputStreamReader
8.Writer的实现类
FileWriter
BufferedWriter
OutputStreamWriter
9.把Java对象转换为字节序列的过程称为对象的序列化。
10.把字节序列恢复为Java对象的过程称为对象的反序列化。
11创建源、选择流、操作、释放
byte[]