1 输入输出
Reader\Writer中有
OutputStream\InputStream中有
流式部分: IO的主体部分;字节流和字符流
非流式部分: 主要包含一些辅助流式部分的类,如:File类、RandomAccessFile类和FileDescriptor等类;
其他类: 文件读取部分的与安全相关的类,如:SerializablePermission类,以及与本地操作系统相关的文件系统的类,如:FileSystem类和Win32FileSystem类和WinNTFileSystem类。
1.1 File类型
文件的定义:文件可以认为是相关记录或存放在一起的数据的集合
存储位置:文件一般是存放在存储设备上的,如光盘、移动设备
java.io包是JDK内置的包 import output
import.java.io.*
java.io.File类的对象可以表示(描述)文件和目录
注意:File对象并不能直接对文件内容进行读/写操作,只能看文件的属性
//相对路径(当前工程的根目录)
File f1 = new File("chinasoft.java");
//绝对路径(从磁盘根目录开始)
File f2 = new File("D:\\java\\chinasoft.java")
//注意:\在java中是转义,所以要\\才能有\的作用
//常见的文件操作方法:
exists() 文件是否存在
isFile() 是否是一个文件
isDiretory() 是否是一个文件夹
delete() 是否成功删除文件
getName() 获得文件名称
getAbsolutePath() 获取绝对路径
length() 获得是文件的长度(注意是字节数)
createNewFile() throws IOException 新建一个文件
listFiles() 返回文件夹的子文件与子文件的数组
遍历文件
例子: 遍历输出C:\windows\目录下的所有文件,并输出文件名,文件大小,上一次修改时间,是否只读
import java.io.File;
import java.util.Date;
public class TestFiles {
public static void main(String[] args) {
// TODO Auto-generated method stub
File f = new File("C:\\Windows");
//listFiles 返回值是一个Files[]数组
File[] files = f.listFiles();
for (File file: files) {
String str = file.isDirectory()?"目录":"文件";
System.out.println(str+file.getAbsolutePath());
System.out.println("文件名:"+file.getName());
System.out.println("文件大小:"+file.length()+"字节");
System.out.println("最后修改日期:"+ new Date(file.lastModified()));
boolean flag = file.canWrite();
//!canWrite——true 只读 false 可写
//!canRead——true 只写 false 可读
if (!flag) {
System.out.println("只读");
}else {
System.out.println("可写");
}
}
}
}
遍历多级文件
例子: C:\windows\目录下的所有文件,包括我们的子目录,并按照文件的"大小"排序输出[文件名,文件大小,上一次修改时间,是否只读
import java.io.File;
public class TestForeachFile {
// 定义一个方法,有参数,是一个File对象
// 判断file对象时一个文件的话,直接打印这个文件的信息
// 如果file对象是一个目录的话,自己调用自己,再遍历
public static void showFiles(File file) {
// 判断file对象时一个文件的话,直接打印这个文件的信息
if (file.isFile()) {
System.out.println("getAbsolutePath :" + file.getAbsolutePath() + "文件名: " + file.getName());
} else {
// 判断file是否为空
if (file != null && file.listFiles() != null) {
// 如果file对象是一个目录的话,要遍历,自己调用自己,再遍历
for (File f : file.listFiles()) {
showFiles(f);
}
}
}
}
public static void main(String[] args) {
// 得到File对象
File file = new File("c:\\windows");
// 调用showFiles的方法
showFiles(file);
}
}
1.2 I/O 流
定义:数据在逻辑上的总线上传输时,发生总线位宽大小变化,都将需要一个缓存和流失串行传输的过程,操作这个过程的工具就是流(Stream)
- 是一串连续不断的数据的集合
- 可以一段一段的写入数据
- 只能先读取前面的数据,再读取后面的数据,但读取完的效果是完全一样的
- 流是磁盘或其他存储设备的终点
- 内存->硬盘(输出流Output Stream) 硬盘->内存(输入流Input Stream)
- 分成字节流(所有数据都可以是由字节流组成,8位的通用字节流,字节为基本单位)和字符流(16位的Unicode字符流,以字符(两个字节)为基本单位,适合用于处理字符串和文本,对字符流进行操作的类大部分继承Reader(读取流)和Writer(写入流))
- 一个文件内容是“123是第一第二第三”,则length()为10,file.length()为17字节
- Output Stream字节输出流 Input Stream字节输入流
- 字符操作流: FileWriter,FileReader,BufferedWriter,BufferedReader
1.2.1 FileWriter
文件的写入
//创建FileWriter对象
FileWriter fw = new FileWriter("D:\\aaa\\test.txt");
File file = new File("D:\\aaa\\test.txt");
//调用write方法完成写入操作
fw.write("我真的被创建写入了吗?");
// \r\n换到下一行的行首
fw.write("\r\n对啊");
//要么关闭,要么刷新
// fw.close();
fw.flush();
1.2.2 FileReader
文件的读出
//创建FileReader对象
FileReader fr = new FileReader("D:\\aaa\\test.txt");
File file = new File("D:\\aaa\\test.txt");
// System.out.println("");
char cbuf[] = new char[(int) file.length()];
int n = fr.read(cbuf);
//转成字符串输出
System.out.println(new String(cbuf));
fr.close();
完整例子: 控制台输入你的个人信息,姓名,年龄,性别,购物车等,将这些存储到磁盘文件中,并打印出来
写入
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;
public class TestFff {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
Scanner s = new Scanner(System.in);
String result = "";
//true开启可追加
//new FileWriter( fileName ,append);
FileWriter fw = new FileWriter("D:\\aaa\\info.txt",true);
for (int i = 0; i < 5; i++) {
switch (i) {
case 0:
System.out.println("请输入你的姓名");
break;
case 1:
System.out.println("请输入你的年龄");
break;
case 2:
System.out.println("请输入你的性别");
break;
case 3:
System.out.println("请输入你的购物车清单");
break;
case 4:
System.out.println("请输入你的账户余额");
break;
default:
break;
}
//不需要+=,因为每次写入文件都会覆盖,最后一次
result = s.next();
s.close();
fw.write(result);
fw.flush();
}
System.out.println("个人需求信息文件创建成功");
}
}
1.2.2.1 BufferReader
读出
FileReader fr = new FileReader("D:\\aaa\\test.txt");
BufferedReader br = new BufferedReader(fr);
String str = null;
while ((str = br.readLine()) != null) {
System.out.println(str);
}
fr.close();
br.close();
1.2.3 DataOutputStream
写数据
java.io.OutputStream——java.io.FileOutputStream——java.io,DataOutputStream
数据输出流使应用程序以便捷方式将原始Java数据类型写入输出流,然后应用程序可以使用数据输入流来读取数据
DataOutputStream是一个抽象类,不能直接使用,只能使用实现他的子类,已知直接子类有:
ByteArrayOutputStream、FileOutputStream、FileOutputStream、ObjectOutputStream、OutputStream、PipedOutputStream
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class TestDataOutputStream {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
//需要FileOutputStream
FileOutputStream fos = new FileOutputStream("d:\\test\\px1.txt");
//將fos做為參數使用,創建一個DataOutputStream的對象
DataOutputStream dos = new DataOutputStream(fos);
int no = 1;
String name = "張三";
String address = "福建省";
//調用dos方法
dos.writeInt(no);
dos.writeUTF(name);
dos.writeUTF(address);
//釋放資源
fos.close();
dos.close();
}
}
1.2.4DataInputStream
读数据
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class TestDataInputStream {
public static void main(String[] args) throws IOException {
//創建FileInputStream對象
FileInputStream fis = new FileInputStream("d:\\test\\px1.txt");
//將fis作爲參數,構造一個DataInputStream的對象
DataInputStream dis = new DataInputStream(fis);
//讀寫順序和寫入順序要一致,否則會報EOFException
int no = dis.readInt();
String name = dis.readUTF();
String address = dis.readUTF();
System.out.println("no:"+no+",name:"+name+",address:"+address);
//釋放資源
fis.close();
dis.close();
}
}
如果读写顺序和写入顺序不一致呢?
//读写顺序和写入顺序要一致,否则会报EOFException异常
String name = dis.readUTF();
int no = dis.readInt();
String address = dis.readUTF();
System.out.println("no:"+no+",name:"+name+",address:"+address);
读取顺序和写入的顺序是一致的,否则会报EOFException异常
注意读取的顺序和之前写入的顺序必须严格一致(流是有序的结构),否则不能读取到正确的数据,严重情况下会导致线程阻塞
1.2.4 ObjectOutputStream
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
public class TestObjectOutputStream implements Serializable {
public static void main(String[] args) {
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
fos = new FileOutputStream("D:\\ccp\\apple.txt");
} catch (FileNotFoundException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
try {
oos = new ObjectOutputStream(fos);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Apple a1 = new Apple("RedApple", "100");
Apple a2 = new Apple("BlueApple", "100");
ArrayList alist = new ArrayList();
alist.add(a1);
alist.add(a2);
try {
oos.writeObject(a1);
oos.writeObject(a2);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
if (oos != null) {
oos.close();
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
if (fos != null) {
fos.close();
}
} catch (Exception e2) {
// TODO: handle exception
e2.printStackTrace();
}
}
}
}
1.2.5 ObjectInputStream
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
public class TestObjectInputStream2 implements Serializable {
public static void main(String[] args) throws ClassNotFoundException, IOException {
FileInputStream fis = null;
ObjectInputStream ois = null;
fis = new FileInputStream("D:\\ccp\\apple.txt");
ois = new ObjectInputStream(fis);
Object obj = ois.readObject();
//保证存进去是Apple类型,取出来也是Apple类型
if (obj instanceof Apple) {
//强制转型
Apple a = (Apple)obj;
System.out.println("a.getName:"+a.getName()+"\na.getType:"+a.getType());
//System.out.println(obj);
}
fis.close();
ois.close();
}
}
1.2.5 RandomAccessFile
java.io
Class RandomAccessFile
RandomAccessFile(File file,String mode)
创建一个随机访问文件流从File参数指定的文件中读取,并可选的写入文件
RandomAccessFile(String name,String mode)
创建随机访问文件流,以从中指定名称的文件读取,并可选择写入文件
mode:r\rw\rwd\rws
"rwd"模式可用于减少执行的I / O操作数。 使用"rwd"只需要更新要写入存储的文件内容; 使用"rws"需要更新要写入的文件的内容及其元数据,这通常需要至少一个低级I / O操作。
- 该类的实例支持读取和写入
- 随机访问文件:读取操作
- 如果是要读写字符串,我们一般用getBytes转换字符串格式成字节
- writeUTF()——
- write(byte[] b)——
- writeBytes(String s)——
- 文件指针可以通过读取getFilePointer()方法和设置seek()方法
import java.io.IOException;
import java.io.RandomAccessFile;
public class TestRandomAccessFile {
//主程序入口
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
RafWrite();
RafRead();
}
//写入
public static void RafWrite() throws IOException {
RandomAccessFile raf = new RandomAccessFile("d:\\aaa\\RandomAccessFile.txt", "rw");
raf.writeInt(100);
raf.writeUTF("hello");
raf.writeUTF("ccp");
// 提示
System.out.println("写入成功!");
raf.close();
}
//读出
public static void RafRead() throws IOException {
RandomAccessFile raf = new RandomAccessFile("d:\\aaa\\RandomAccessFile.txt", "rw");
int n = raf.readInt();
String s = raf.readUTF();
String s2 = raf.readUTF();
System.out.println("n:" + n + "\ns:" + s + "\ns2:" + s2);
raf.close();
}
}
案例:读出写出和字节的计算
import java.io.IOException;
import java.io.RandomAccessFile;
public class TestRandomAccessFile {
// 主程序入口
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
RafWrite2();
RafRead1();
}
// 写入——字节序列写入文件 -> "字符"
public static void RafWrite2() throws IOException {
RandomAccessFile raf = new RandomAccessFile("d:\\aaa\\RandomAccessFile.txt", "rw");
long pointer = raf.getFilePointer();
System.out.println("pointer:" + pointer);
// 不管数字多大,Int类型只占4个字节 ->4字节
raf.writeInt(10000000);
pointer = raf.getFilePointer();
System.out.println("pointer:" + pointer);
// 一个XX就占一个字节 ->4字节
raf.writeBytes("Java");
pointer = raf.getFilePointer();
System.out.println("pointer:" + pointer);
// 一个XX就占一个字节 ->2字节
raf.writeBytes("好吃");
pointer = raf.getFilePointer();
System.out.println("pointer:" + pointer);
// 一个XX就占一个字节 ->3字节
raf.writeBytes("格拉条");
pointer = raf.getFilePointer();
System.out.println("pointer:" + pointer);
// 提示
System.out.println("写入成功!");
raf.close();
}
// 写入——字节实现写入 "字符串"
public static void RafWrite1() throws IOException {
RandomAccessFile raf = new RandomAccessFile("d:\\aaa\\RandomAccessFile.txt", "rw");
long pointer = raf.getFilePointer();
System.out.println("pointer:" + pointer);
// 不管数字多大,Int类型只占4个字节 ->4字节
raf.writeInt(10000000);
pointer = raf.getFilePointer();
System.out.println("pointer:" + pointer);
// 一个字母一个"字节"位置 ->4字节
raf.write("Java".getBytes());
pointer = raf.getFilePointer();
System.out.println("pointer:" + pointer);
// 一个中文两个"字节"位置 ->4字节
raf.write("好吃".getBytes());
pointer = raf.getFilePointer();
System.out.println("pointer:" + pointer);
// 一个中文两个"字节"位置 ->6字节
raf.write("格拉条".getBytes());
pointer = raf.getFilePointer();
System.out.println("pointer:" + pointer);
// 提示
System.out.println("写入成功!");
raf.close();
}
// 普通read读出
public static void RafRead() throws IOException {
RandomAccessFile raf = new RandomAccessFile("d:\\aaa\\RandomAccessFile.txt", "r");
int n = raf.readInt();
String s = raf.readUTF();
String s2 = raf.readUTF();
String s3 = raf.readUTF();
System.out.println("n:" + n + "\ns:" + s + "\ns2:" + s2+"\ns3:"+s3);
raf.close();
}
// 读出——raf.read(b) ->参数是字节数组
public static void RafRead1() throws IOException {
RandomAccessFile raf = new RandomAccessFile("d:\\aaa\\RandomAccessFile.txt", "r");
//截取文件中特定位置
raf.seek(4);
//通俗的说,要输出几个
byte b[] = new byte[4];
raf.read(b);
System.out.println(new String(b));
raf.close();
}
//读出字符
public static void RafRead2() throws IOException {
RandomAccessFile raf = new RandomAccessFile("d:\\aaa\\RandomAccessFile.txt", "r");
//截取文件中特定位置
raf.seek(12);
char b[] = new char[6];
for (int i = 0; i < b.length; i++) {
b[i]=raf.readChar();
}
System.out.println(new String(b));
raf.close();
}
}
1.2.6 原始字节流
FileInputStream
FileOutputStream
文件复制:先输入,再输出 (文件输入到内存,内存输出)
**案例:**复制D盘下ccp中的Copy前的.jpg->到同目录下文件夹中
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class TestFileCopy {
public static void main(String[] args) {
try {
CopyFile("D:\\ccp\\Copy前的.jpg","D:\\ccp\\Copy后的.jpg");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void CopyFile(String srcpath,String despath) throws IOException {
// TODO Auto-generated method stub
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(srcpath);
fos = new FileOutputStream(despath);
byte b[] = new byte[6000];
int length = 0;
//这里是read在InputStream类的重写,到达文章末尾会输出-1
while ((length = fis.read(b))!=-1) {
System.out.println(length);
//有多少长度输出多少,如果是write(b)则是每次固定长度,
//就算是最后不够也要输出,所以会导致源文件比之前的更大
//fos.write(b, off, len);
fos.write(b,0,length);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
1.3 Java序列化
意义:解决了只能实现基本类型和String类型的情况
1.3.1 对象序列化
字节流
ObjectOutputStream——writeObject序列化操作
ObjectInputStreanm——readObject反序列化操作
存储自定义对象,读取自定义对象,对象在网络节点上进行传输
java.lang.Object
java.io.OutputStream
java.io.ObjectOutputStream
一个类要想被序列化:
- 必须要实现serializable接口,但不必实现里面的方法(因为它只是一个标识接口,意味着它仅仅是为了说明类的可序列化属性,这个接口中没有包含任何需要子类实现的抽象方法)
- 该类的所有属性必须是可序列化的。如果有一个属性不是可序列化的,则该属性必须注明是短暂的。
案例: 创建Apple类,并使用writeObject序列化操作序列化Apple类,添加到Apple文件中
注意点:注意要实现Serializable接口,否则不能被序列化
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
//注意要实现Serializable接口,否则不能被序列化
public class TestObjectOutputStream implements Serializable {
public static void main(String[] args) {
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
//创建文件输出流以写入由指定的 File对象表示的文件。
fos = new FileOutputStream("D:\\ccp\\apple.txt");
} catch (FileNotFoundException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
try {
oos = new ObjectOutputStream(fos);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
//使用writeObject()
//Object obj = oos.writeObject();
//实例化Apple对象并给值
Apple a1 = new Apple("RedApple", "100");
Apple a2 = new Apple("BlueApple", "100");
//实例化ArrayList对象
ArrayList alist = new ArrayList();
//把Apple对象存在ArrayList集合中
alist.add(a1);
alist.add(a2);
try {
oos.writeObject(a1);
oos.writeObject(a2);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
if (oos != null) {
oos.close();
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
if (fos != null) {
fos.close();
}
} catch (Exception e2) {
// TODO: handle exception
e2.printStackTrace();
}
}
}
}
//注意要实现Serializable接口
class Apple implements Serializable {
private String name;
private String type;
public Apple() {
// TODO Auto-generated constructor stub
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Apple(String name, String type) {
super();
this.name = name;
this.type = type;
} @Override
public String toString() {
return "Apple [name=" + name + ", type=" + type + "]";
}
}
1.3.2 对象反序列化
ObjectInputStream
FileInputStream
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
public class TestObjectInputStream2 implements Serializable {
public static void main(String[] args) throws ClassNotFoundException, IOException {
FileInputStream fis = null;
ObjectInputStream ois = null;
fis = new FileInputStream("D:\\ccp\\apple.txt");
ois = new ObjectInputStream(fis);
Object obj = ois.readObject();
//保证存进去是Apple类型,取出来也是Apple类型
if (obj instanceof Apple) {
//强制转型
Apple a = (Apple)obj;
System.out.println("a.getName:"+a.getName()+"\na.getType:"+a.getType());
//System.out.println(obj);
}
fis.close();
ois.close();
}
}
1.3.3 seriavalversionUID
serialVersionUID(串行化版本统一标识符)
用反序列化过程中验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类,如果两者不同,则会导致InvalidClassException
如果不显式提供serialVersionUID的值,Java会根据以下几个属性自动计算:
- 类的名字
- 属性字段的名字
- 方法的名字
- 已实现的接口
如果一个发生改变,则需要在类里新增一个serialVersionUID,强制类使用原来的UID
private static final long serialVersionUID = -4181171638769266455L;
1.3.4 transient
如果想忽略某些属性,在序列化时不输出,在属性前面加上transient,将不被序列化
private transient String type;
1.4 nio
Java NIO的事件选择器允许一个单独的线程来监视多个输入通道
1.4.1 nio与io的区别
- IO是面向流的,而NIO是面向块(缓冲区)
- IO读取字节时只能从前往后读取,没有缓存其他东西,而 NIO在读取数据时会读取它稍后处理的缓冲区,需要时就可以在缓冲区中前后移动 -> 增加了处理过程中的灵活性
1.4.2 Buffer
NIO中的Buffer用于和NIO通道进行交互
1.4.3 Channel接口 通道
通道也是需要关闭流的
NIO的通道类似流,不同的是:
Channel通道 | Stream流 | |
---|---|---|
数据流向 | 从通道中读取数据,也可以写数据到通道 | 但流读写只能是单向的 |
线程方式 | 异步-同步读写 | 同步 |
读取标准 | 通道中数据总要先读到一个Buffer(或者是从一个Buffer中导入) |
1.4.4 FileChannel
java.lang.Object
java.nio.channels.spi.AbstractInterruptibleChannel
java.nio.channels.FileChannel
方法:
transferTo(long position, long count, WritableByteChannel target)
将该通道文件的字节传输到给定的可写字节通道。
从position开始到count结束,传输到WritableByteChannel target
java.nio.channels的实现类
例子: 实现文件的复制Copy
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
public class TestChannel {
//这边做简单的异常处理,实际上应该加上try-catch
public static void main(String[] args) throws IOException {
//输入字节流
FileInputStream fis = new FileInputStream("D:\\ccp\\Copy前的.jpg");
//输出
FileOutputStream fos = new FileOutputStream("D:\\ccp\\Copy后好几次的.jpg");
//使用channel
FileChannel inchannel = fis.getChannel();
FileChannel outchannel = fos.getChannel();
//inchannel的内容 传输到outchannel的地址
inchannel.transferTo(0, inchannel.size(),outchannel);
inchannel.close();
outchannel.close();
fis.close();
fos.close();
System.out.println("复制成功");
}
}