在学习io之前我么先来学一下File类,因为io免不了跟文件打交道
File
// File file1 = new File("C:\\aaa\\a.txt");//绝对路径,文件存在
//File file2 = new File("demo17.txt");//相对路径(项目根目录),文件不存在
// File file3 = new File("C:\\aaa","a.txt");//作用跟:file1相同
// File file4 = new File("C:\\aaa");
// File file5 = new File(file4,"a.txt");//跟file3和file1的效果相同
//public boolean createNewFile():创建文件(如果成功创建返回true,否则(文件已经存在)返回false)
//public boolean mkdir():创建单级目录
//public boolean mkdirs():创建多级目录
//public boolean delete():可以删除文件、可以删除目录(一定要是空目录)
//判断功能
// public boolean isDirectory():判断是否是一个目录;
// public boolean isFile():判断是否是一个文件;
// public boolean exists():判断文件/目录是否存在;
// public boolean canRead():判断是否可读;
// public boolean canWrite():判断是否可写;
// public boolean isHidden():判断是否隐藏;
//File类的基本获取功能:
// public String getAbsolutePath():获取绝对路径
//public String getPath():获取File封装的路径
//public String getName():获取文件/目录的名称
//public long length():获取文件的大小(单位:字节)
//public long lastModified():最后修改时间(单位:毫秒)
//File类的高级获取功能:
//public String[] list():如果当前File表示的是一个目录,则返回此目录下所有的子文件/子目录的名称的String[]数组;
//public File[] listFiles():如果当前File表示的一个目录,则返回此目录下所有的子文件/子目录的File[]数组;
//区别:如果仅仅想获取文件名,使用第一种;
//如果需要对子文件/子目录进行进一步的操作,可以使用第二种;
//重命名功能
//public boolean renameTo(File dest):将当前File对象,重命名到dest表示的路径下;
//注意:
//1.如果dest和原文件不在同目录下,重命名操作将会相当于"剪切"操作;
//2.如果dest和原文件在同目录下,相当于"重命名"
io的分类
1.字节流:
1).输出流:OutputStream(抽象类):
输出的方法:
1).write(int n):输出一个字节
2).write(byte[] b):输出一个字节数组;
3).write(byte[] b, int off,int len):输出字节数组的一部分;
|--FileOutputStream(类):
|--FilterOutputStream(不学):
|--BufferedOutputStream(缓冲流)
2).输入流:InputStream(抽象类):
读取的方法:
1).int read():读取一个字节
2).int read(byte[] b):读取一个字节数组;
|--FileInputStream(类):
|--FilterInputStream(不学):
|--BufferedInputStream(缓冲流);
2.字符流:
1).输出流:Writer:
输出的方法:
1).write(int n):输出一个字符;
2).write(char[] c):输出一个字符数组;
3).write(char[] c ,int off,int len):输出字符数组的一部分;
4).write(String s):输出一个字符串;
5).write(String s,int off,int len):输出字符串的一部分;
|--OutputStreamWriter(转换流)
|--FileWriter(字符流)
|--BufferedWriter(缓冲流):
void newLine():输出一个换行符;
2).输入流:Reader:
读取的方法:
1).int read():读取一个字符;
2).int read(char[] c):读取一个字符数组;
|--InputStreamReader(转换流);
|--FileReader(字符流)
|--BufferedReader(缓冲流):
String readLine():读取一行数据;
FileOutputStream
java.io.FileOutputStream(类):
* 构造方法:文件可以不存在,会自动创建
* FileOutputStream(String fileName) :创建一个向具有指定名称的文件中写入数据的输出文件流。
* FileOutputStream(File file) : 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
* FileOutputStream(String name, boolean append) 创建一个向具有指定 name 的文件中写入数据的输出文件流。
* FileOutputStream(File file, boolean append)创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
*
* 输出的方法:
* write(int n):输出一个字节;
* write(byte[] b):输出一个字节数组;
* write(byte[] b,int off,int len):输出一个字节数组的一部分;
* 成员方法啊:
* close():关闭流
FileInputStream
java.io.FileInputStream(类):
* 构造方法:文件必须存在,否则运行时异常;
* FileInputStream(String name) 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。
* FileInputStream(File file) :通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
* 读取的方法:
* int read():读取一个字节
* int read(byte[] byteArray):读取若干个字节,填充参数的byte[]数组;
练习字节流复制文件
/**
* 字节流复制文件
*/
private void fileDemo(String path) {
try {
//输入流读取文件信息
FileInputStream in = new FileInputStream(path);
//输出流吧文件信息输出到新的path
FileOutputStream out = new FileOutputStream(path);
//一次读取一个字节
//int n = 0;
//read ==-1 说明没有读取到字节
//while ((n = in.read()) != -1) {
//一次输出一个字节
//out.write(n);
// }
//一次读写一个字节数组
byte[] bytes = new byte[1024];
int n = 0;
while ((n=in.read(bytes))!=-1){
out.write(bytes,0,n);
}
in.close();
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
字节缓冲流
无特有方法,用法和上面一样
BufferedInputStream in = new BufferedInputStream(new FileInputStream(path));
编码
编码:将看得懂的,变为看不懂
* String-->getBytes():使用平台默认的字符集
* getBytes(String charset):指定编码方式
* 解码:将看不懂的,变为看得懂的:
* String-->构造方法:String(byte[] b):使用平台默认字符集解码
* String(byte[] b,String charset):指定解码方式
*
*
* 总之:对于文本文件,写入和读取使用相同的"编码方式",就会避免乱码的情况;
转换流
1).输出流:Writer
* |--OutputStreamWriter(类--转换流):是字符流通向字节流的桥梁
* 构造方法:
* OutputStreamWriter(OutputStream s):使用平台默认字符集
* 输出的方法(五种):
* 1).write(int n):输出一个字符
* 2).write(char[] c):输出一个字符数组
* 3).write(char[] c ,int off, int len):输出字符数组的一部分;
* 4).write(String str):输出一个字符串
* 5).write(String str,int off, int len):输出字符串的一部分;
* 2).输入流:Reader
* |--InputStreamReader(类--转换流):是字节流通向字符流的桥梁
* 构造方法:
* InputStreamReader(InputStream in):使用平台默认字符集
* 读取的方法(两个):
* int read():读取一个字符:返回值:读取的"字符"
* int read(char[] c):读取一个字符数组;返回值:读取的"字符数量"
InputStreamReader in = new InputStreamReader(new FileInputStream(path));
字符流
* 字符流:
* 1.输出流:Writer
* |--OutputStreamWriter(类--转换流):
* |--FileWriter(类--纯字符流):
* 构造方法:
* 1).FileWriter(File file)根据给定的 File 对象构造一个 FileWriter 对象。
* 2).FileWriter(File file, boolean append) 根据给定的 File 对象构造一个 FileWriter 对象。
* 3).FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象。
* 4).FileWriter(String fileName, boolean append) 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。
输出的方法:
(无特有方法,都是继承:五种)
*
* 2.输入流:Reader
* |--InputStreamReader(类--转换流):
* |--FileReader(类--纯字符流):
* 构造方法:
* 1).FileReader(File file) 在给定从中读取数据的 File 的情况下创建一个新 FileReader。
* 2).FileReader(String fileName) 在给定从中读取数据的文件名的情况下创建一个新 FileReader
读取的方法:
(无特有方法,都是继承:两种)
public void filedemo1(String path) {
try {
FileReader fileReader = new FileReader(path);
FileWriter fileWriter = new FileWriter("");
//一次读取一个字符
int n = 0;
while ((n = fileReader.read()) != -1) {
fileWriter.write(n);
}
//一次读取一个字节数组
char[] chars = new char[2];
while ((n = fileReader.read(chars)) != -1) {
fileWriter.write(chars,0,n);
}
fileReader.close();
fileWriter.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
字符缓冲流
2.字符流:
* 1).输出流:Writer(抽象类):
* |--BufferedWriter(字符流--缓冲流):
* 构造方法:
* 1).BufferedWriter(Writer out):创建一个使用默认大小输出缓冲区的缓冲字符输出流。
* 输出方法:
* (无特有方法,都是从父类继承的:五种)
* 特有成员方法:
* void newLine():输出一个换行符;
* 2).输入流:Reader(抽象类):
* |--BufferedReader(字符流--缓冲流):
* 构造方法:
* 1).BufferedReader(Reader in) :创建一个使用默认大小输入缓冲区的缓冲字符输入流。
* 读取的方法:
* (继承父类:两种)
* String readLine():读取一行数据(不读取换行符)
public void fileDemo3(String path) {
try {
BufferedReader bufferedReader = new BufferedReader(new FileReader(path));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(""));
//一次读一行
String row = null;
while ((row = bufferedReader.readLine()) != null) {//不读取换行符
bufferedWriter.write(row);
//添加一个换行符
bufferedWriter.newLine();
}
bufferedReader.close();
bufferedWriter.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
LineNumberReader
/*
* LineNumberReader类:
*
* 1.跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int) 和 getLineNumber(),
* 它们可分别用于设置和获取当前行号。 默认情况下,行编号从 0 开始。
数据输入输出流
数据输入输出流:
*
* 1.DataInputStream:可以读取Java的"基本数据类型";readXxxx()
* DataOutputStream:可以输出Java的"基本数据类型";writeXxxx()
*
* 此类,可以读写Java基本数据类型,是按"写入",同时要按"字节"读取;
字节数组缓冲流
* 字节数组缓冲流:
*
* ByteArrayOutputStream:此类实现了一个输出流,其中的数据被写入一个 byte 数组
* ByteArrayInputStream:从"byte[]数组缓存区"中读取数据;它可以模仿字节输入流的方式,一次读取一个字节或一个字节数组;
*
public static void main(String[] args) throws IOException {
FileInputStream in = new FileInputStream("demo04.txt");
//一次读取一个字节数组
byte[] byteArray = new byte[4];
int n = 0;
//先准备一个字节数组缓存区
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
while((n = in.read(byteArray)) != -1){
//以前我们这里要么向控制台显示读取的内容;
//要么写入到其它文件中(文件复制)
//假如,这里暂时什么都不做,可以先将已经读取的字节数组,先缓存起来
byteOut.write(byteArray , 0 , n);//输出到:内存的byte[]数组缓存区
}
in.close();
//一次性的,将缓存区的内容取出
System.out.println("*****************************");
byte[] allByteArray = byteOut.toByteArray();
String str = new String(allByteArray);
System.out.println(str);
//ByteArrayInputStream
ByteArrayInputStream byteIn = new ByteArrayInputStream(allByteArray);
byte[] bArray = new byte[4];
n = 0;
while((n = byteIn.read(bArray)) != -1){
System.out.println("读取的内容:" + new String(bArray,0,n));
}
}
RandomAccessFile
* RandomAccessFile:
* RandomAccessFile类不属于流,是Object类的子类。但它融合了InputStream和OutputStream的功能。支持对随机访问文件的读取和写入。
*
* getFilePointer():获取文件指针;
* seek():设置文件指针
打印流
* 1.字节打印流:PrintStream:
* 2.字符打印流:PrintWriter:
*
* 打印流的特点:
* 只能操作目的地,不能操作数据(只有输出流,没有输入流)
可以操作任意类型的数据。
如果启动了自动刷新,能够自动刷新。--PrintWriter
可以操作文件的流
/*
* PrintWriter:,如果启用了自动刷新,则只有在调用 println、printf 或 format 的其中一个方法时才可能完成此操作
*/
public class Demo {
public static void main(String[] args) throws IOException {
PrintWriter out = new PrintWriter(new FileWriter("demo07.txt"),true);
// out.write("Hello");
out.println("hello");//write() + flush() + newLine()
out.println("world");
out.close();
}
}
JAVA NIO
JDK 1.4 的java.nio* 包引入了一种新的java I/O类库,其目的在于提高速度,实际上旧的IO包已经用新的NIO重新实现过,以便充分利用这种速度的提高,因此即使我们不显示的编写NIO,也能从中受益,速度的提高在文件IO和网络IO,都有可能发生,这里我们只研究前者。
速度的提高来源于,它使用的结构方式更加接近操作系统执行IO的方式:通道和缓冲器,我们可以把它想象成一个煤矿,通道是包含煤层(数据)的矿藏,而缓冲器则是,派往矿藏的卡车,卡车载满煤矿而归,我们再从卡车上获得煤炭,也就是说我们并没有直接和通道进行交互,我们只是和缓冲器交互,并把缓冲器派送到通道,通道要么从缓冲器获得数据,要么向缓冲器发送数据
唯一和通道交互的缓冲器是ByteBuffer,也就是说可以储存未加工的缓冲器,ByteBuffer是一个非常基础的类,通过告知分配多少储存空间,来创建一个ByteBuffer,并且还有一个方法选集,用以原始的字节形式或基本数据类型,输出和读取数据,但是没有办法输出或读取对象,即使字符串也不行,这种处理虽然很低级,但是正好,因为这是大多数操作系统更有效的操作方式
旧的IO库有三个被修改,可以用来产生通道FileChannel,这三个被修改的类是,FileInputStream,FileOutputStream,RandomAccessFile,注意这些是字节操纵流,和底层的NIO一致,Reader和Writer不能用于产生通道,但是java.nio.channels.Channels,提供了实用方法,可以在通道中产生Reader和Writer。
public static void main(String[] args) {
try {
FileOutputStream outputStream = new FileOutputStream("data.txt");
FileChannel channel1 = outputStream.getChannel();
channel1.write(ByteBuffer.wrap("你好".getBytes()));
channel1.close();
RandomAccessFile rw = new RandomAccessFile("data.txt", "rw");
FileChannel channel2 = rw.getChannel();
channel2.position(channel2.size());//移到文件底部
channel2.write(ByteBuffer.wrap("哈哈".getBytes()));
channel2.close();
FileInputStream fileInputStream = new FileInputStream("data.txt");
FileChannel channel = fileInputStream.getChannel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
channel.read(byteBuffer);
byteBuffer.flip();
while (byteBuffer.hasRemaining()) {
System.out.println((char) byteBuffer.get());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
对于这里的任何流类,getchannel都会产生一个FileChannel,通道是一种相当基础的东西,你可以向他传送用于读写的ByteBuffer,并且可以锁定文件某些区域用于独占式访问
将字节放于ByteBuffer方法之一put方法,直接对他进行填充,填入一个或多个字节,或者基本数据 类型的值,也可以用wrap方法,将已存在的字节数组包装到ByteBuffer中,一旦如此就不在复制底层数组,而是把它作为产生ByteBuffer的储存器,我们称之为数组支持的ByteBuffer
data.txt 被RandomAccessFile打开,我们可以在文件内移动FileChannel,这里我们把它移动到文件末尾
对于只读访问,我们必须显示的用静态allocate方法来分配ByteBuffer,nio的目标就是快速的移动大量数据,因此ByteBuffer大小就显得尤为重要,这里的1K比我们通常使用的会小一点
一旦调用read来告知FileChannel向ByteBuffer储存数据,就必须调用缓冲器上的filp方法,让他做好让别人读取数据的准备,这是很拙劣的,但是适用于获得最大速度,如果我们使用缓冲器进行进一步的read操作,我们必须调用clear,来为每个read做好准备,例如下边的复制文件
public static void main(String[] args) {
try {
FileChannel in = new FileInputStream("data.txt").getChannel();
FileChannel out = new FileOutputStream("text.txt").getChannel();
ByteBuffer byteBuffer1 = ByteBuffer.allocate(1024);
while (in.read(byteBuffer1) != -1) {
byteBuffer1.flip();//准备写
out.write(byteBuffer1);
byteBuffer1.clear();//准备读
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
每次read之后,数据就会流入缓冲器中,filp则是准备缓冲器,以便信息又write提取,write之后信息还存在缓冲器中,clear对缓冲器进行清空,以便下一个read可以正常储存数据
然而上方的方法并不是理想的形式,transferFrom和transferTo,允许俩个通道直接相连
public static void main(String[] args) {
try {
FileChannel in = new FileInputStream("data.txt").getChannel();
FileChannel out = new FileOutputStream("text.txt").getChannel();
ByteBuffer byteBuffer1 = ByteBuffer.allocate(1024);
in.transferTo(0,in.size(),out);
//或者
// out.transferFrom(in,0,in.size());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}