一、概念
Java中对文件的操作是以流的方式进行的。流是Java内存中的一组有序数据序列。Java将数据从源(文件、内存、键盘、网络)读入到内存中,形成了流,
然后将这些流还可以写到另外的目的地(文件、内存、控制台、网络),之所以称为流,
是因为这个数据序列在不同时刻所操作的是源的不同部分。
二、分类
流的分类,Java的流分类比较丰富,刚接触的人看了后会感觉很晕。流分类的方式很多:
1、按照输入的方向分,输入流和输出流,输入输出的参照对象是Java程序。
2、按照处理数据的单位不同分,字节流和字符流,字节流读取的最小单位是一个字节(1byte=8bit),
而字符流一次可以读取一个字符(1char = 2byte = 16bit)。
3、按照功能的不同分,分节点流和处理流,节点流是直接从一个源读写数据的流(这个流没有经过包装
和修饰),处理流是在对节点流封装的基础上的一种流,FileInputStream是一个接点流,可以直接从文件读取数据,但是BufferedInputStream可以包装 FileInputStream,使得其有缓冲功能。其实除了以上三种分类外,还有一些常常听到的一些分类比如:对象流、缓冲流、压缩流、文件流等等。其实都是节点流和处理流的子分类。当然你也可以创建新的流类型,只要你需要。
三、流分类的关系
不管流的分类是多么的丰富和复杂,其根源来自于四个基本的类。这个四个类的关系如下:字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer
四、字节流和字符流的相互转换
1、从字节流到字符流:InputStreamReader、OutputStreamWriter类可以实现。2、从字符流到字节流:可以从字符流中获取char[]数组,转换为String,然后调用String的API函数getBytes() 获取到byte[],然后就可以通过ByteArrayInputStream、ByteArrayOutputStream来实现到字节流的转换。
File类:文件和目录路径名的抽象表示形式
构造方法:
File(String pathname) 通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。
pathname:"C:/Users/user/Desktop/xxx.txt"
File(File parent, String child) 根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
File(String parent, String child) 根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。
"C:/Users/user/Desktop","xxxx.txt"
File(URI uri) 通过将给定的 file: URI 转换为一个抽象路径名来创建一个新的 File 实例。
"file:///C:/Users/user/Desktop/xxxx.txt"
1.代码实例:在D:盘下创建一个文件hello.txt文件
File(String pathname) 通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。
pathname:"C:/Users/user/Desktop/xxx.txt"
File(File parent, String child) 根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
File(String parent, String child) 根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。
"C:/Users/user/Desktop","xxxx.txt"
File(URI uri) 通过将给定的 file: URI 转换为一个抽象路径名来创建一个新的 File 实例。
"file:///C:/Users/user/Desktop/xxxx.txt"
1.代码实例:在D:盘下创建一个文件hello.txt文件
import java.io.*;
public class hello{
public static void main(String[] args) {
File f=new File("D:\\hello.txt");
try{
f.createNewFile();
}catch (Exception e) {
e.printStackTrace();
}
}
}
这时有人会问windows里面的名称分割符不是用\吗,这里为什么是\\,因为在JAVA中\表示转义字符,要表示\就要\\表示但是在linux里面名称分割符用的是/,为了我们的程序跨平台性更强所以就要使用file提供的两个常量,用来适应各种系统。
separator :与系统有关的默认名称分隔符。window:默认\,linux:默认/。
pathSeparator 与系统有关的路径分隔符。 window:默认;,linux:默认:。
代码实例:用file常量才表示文件分割符
import java.io.*;
public class hello{
public static void main(String[] args) {
File f=new File("D:"+File.separator+"hello.txt");
try{
f.createNewFile();
}catch (Exception e) {
e.printStackTrace();
}
}
}
2.既然可以创建就可以删除,delete方法可以删除指定目录下的一个文件代码实例:删除刚才创建的hello.txt文件
import java.io.*;
class hello{
public static void main(String[] args) {
String fileName="D:"+File.separator+"hello.txt";
File f=new File(fileName);
if(f.exists()){
f.delete();
}else{
System.out.println("文件不存在");
}
}
}
3.创建一个文件夹代码实例:在D盘下创建一个hello的文件夹
import java.io.*;
class hello{
public static void main(String[] args) {
String fileName="D:"+File.separator+"hello";
File f=new File(fileName);
f.mkdir();
}
}
4.遍历出指定目录的全部文件(包括影藏文件)import java.io.*;
class hello{
public static void main(String[] args) {
String fileName="D:"+File.separator;
File f=new File(fileName);
String[] str=f.list();
for (int i = 0; i < str.length; i++) {
System.out.println(str[i]);
}
}
}
list返回的是文件数组,如果想看到文件的具体路径可以使用listFiles返回的是File文件的对象数组。
5.上面只能遍历出指定目录的文件,如果一个目录里面嵌套有其它文件夹,这时就需要循环递归遍历
代码实例:遍历出指定目录下的所有文件及文件夹
import java.io.File;
public class IOTest {
public static void main(String[] args) {
//创建文件对象
File root=new File("C:/");
//实例化对象
IOTest ioTest=new IOTest();
//调用递归方法遍历盘符所有文件目录
ioTest.list(root);
}
//计数器
int count;
/**
* 递归遍历所有目录的方法
* */
public void list(File parent){
//System.out.println(parent.getName());
//首先判断传递进来的是不是一个目录
// if (parent.isDirectory()) {
//计数器自己加一
count++;
//调用listFiles方法返回当前同级目录下的所有目录对象
File file[]=parent.listFiles();
//当目录为空直接返回,不执行遍历
if(file==null)
return ;
//循环遍历文件
for(File f:file){
//打印空格,显示层级关系
for (int i = 0; i < count; i++) {
System.out.print("\t");
}
//输出目录/文件名字
//如果是文件
if (f.isFile()) {
//打印文件名字
System.out.println(f.getName());
}else {
//打印目录名字
System.out.println(f.getName());
//再调用自己方法遍历自己的子目录
list(f);
}
}
//返回到上一级目录
count--;
// }
}
}
字节流
1.读取文件中的内容
代码实例:读取文件中的内容
import java.io.*;
class hello{
public static void main(String[] args) throws IOException {
String fileName="D:"+File.separator+"hello.txt";
File f=new File(fileName);
FileInputStream in=new FileInputStream(f);
byte[] b=new byte[1024];
in.read(b);
in.close();
System.out.println(new String(b));
}
}
那么文件到底有多大呢?设置打了浪费空间,小了又担心不够,这时有同学可能会想直接获取file.length作为数组的长度,那么对于一个未知大小的文件,可能很大而数组的长度又是有限的情况怎么办?代码实例:
import java.io.*;
class hello{
public static void main(String[] args) throws IOException {
String fileName="D:"+File.separator+"hello.txt";
File f=new File(fileName);
InputStream in=new FileInputStream(f);
byte[] b=new byte[1024];
int count =0;
int temp=0;
while((temp=in.read())!=(-1)){
b[count++]=(byte)temp;
}
in.close();
System.out.println(new String(b));
}
}
这里我们定义一个变量来接受读取的长度,当文件读取完时会返回一个-1。我们就可以用这个数来判定读取的条件。
OutputStream
2.向一个文件里面写入内容
代码实例:
import java.io.*;
class hello{
public static void main(String[] args) throws IOException {
String fileName="D:"+File.separator+"hello.txt";
File f=new File(fileName);
OutputStream out =new FileOutputStream(f);
String str="你好";
byte[] b=str.getBytes();
out.write(b);
System.out.println(new String(b));
out.close();
}
}
上面是将一整个字节数组写入进去,当然也可以一个一个字节的单独写入。字符流
FileWriter1.向文件中写入数据
代码实例:
import java.io.*;
class hello{
public static void main(String[] args) throws IOException {
String fileName="D:"+File.separator+"hello.txt";
File f=new File(fileName);
Writer out =new FileWriter(f);
String str="hello";
out.write(str);
out.close();
}
}
字节与字符流区别在于,只是你可以直接输入字符串,而不需要你将字符串转化为字节数组当你如果想问文件中追加内容的时候,可以使用将上面的声明out的哪一行换为:
Writer out =new FileWriter(f,true);(注意:不加true默认的false就回覆盖以前的内容)
当然最好采用循环读取的方式,因为我们有时候不知道文件到底有多大。(方法和之前的字节流一样)
关于字节流和字符流的区别
实际上字节流在操作的时候本身是不会用到缓冲区的,是文件本身的直接操作的,但是字符流在操作的 时候下后是会用到缓冲区的,是通过缓冲区来操作文件的。
读者可以试着将上面的字节流和字符流的程序的最后一行关闭文件的代码注释掉,然后运行程序看看。你就会发现使用字节流的话,文件中已经存在内容,但是使用字符流的时候,文件中还是没有内容的,这个时候就要刷新缓冲区。
缓冲流
BufferedReader:输入流BufferedWriter:输出流
代码实例:将一个文本联盟的内容分100行显示,每行字数随机
public class IoTest {
public static void main(String[] args) {
/*
* 将一个文本分100行显示,每行字数随机
* */
try {
//创建BufferedReader对象
BufferedReader reader=new BufferedReader(new FileReader("../myextends/src/com/yu/ioreader/test.txt"));
//创建BufferedWriter对象
BufferedWriter writer=new BufferedWriter(new FileWriter("C:/Users/Administrator/Desktop/test.txt",false));
//读取一行数据
String string=reader.readLine();
System.out.println(string);
Random random=new Random();
int offset=0;
System.out.println(string.length());
for (int i = 1; i <= 99; i++) {
int len=random.nextInt(string.length()/100+1);
writer.write("第"+i+"行 ");
writer.write(string.substring(offset, offset+len));
writer.newLine();
offset+=len;
}
writer.write("第"+100+"行 ");
writer.write(string.substring(offset));
if (reader!=null&&writer!=null) {
reader.close();
writer.flush();
writer.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
转换流
OutputStreamWriter
将字节输出流转化为字符输出流
代码实例:
将字节输出流转化为字符输出流
代码实例:
import java.io.*;
class hello{
public static void main(String[] args) throws IOException {
String fileName= "d:"+File.separator+"hello.txt";
File file=new File(fileName);
Writer out=new OutputStreamWriter(new FileOutputStream(file));
out.write("hello");
out.close();
}
}
InputStreamReader
将字节输入流变为字符输入流代码实例:
import java.io.*;
class hello{
public static void main(String[] args) throws IOException {
String fileName= "d:"+File.separator+"hello.txt";
File file=new File(fileName);
Reader read=new InputStreamReader(new FileInputStream(file));
char[] b=new char[100];
int len=read.read(b);
System.out.println(new String(b,0,len));
read.close();
}
}
数据流
数据流主要为实现可以存取Java原始数据类型如long,boolean
数据流是字节流
DataInputStream需要和InputStream套接
DataOutputStream需要和OutputStream套接
DataInputStream方法:readBoolean() readInt() read...()...
代码实例:
数据流是字节流
DataInputStream需要和InputStream套接
DataOutputStream需要和OutputStream套接
DataInputStream方法:readBoolean() readInt() read...()...
代码实例:
try {
DataOutputStream outputStream=new DataOutputStream(new FileOutputStream
<span style="white-space:pre"> </span>("../myextends/src/com/yu/iotest/data.txt"));
outputStream.writeInt(10023);
DataInputStream inputStream=new DataInputStream
<span style="white-space:pre"> </span>(new FileInputStream("../myextends/src/com/yu/iotest/data.txt"));
int a=inputStream.readInt();
System.out.println(a);
outputStream.close();
inputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Object流
直接将Object对象写入或读出
transient关键字为不序列化此成员变量
需要序列化的类必须实现Serializable接口
主要方法:writeObject(Object); readObject();
读出为Object类型需要强转数据类型
代码实例:
transient关键字为不序列化此成员变量
需要序列化的类必须实现Serializable接口
主要方法:writeObject(Object); readObject();
读出为Object类型需要强转数据类型
代码实例:
import java.io.*;
public class TestObjectIO {
public static void main(String args[]) throws Exception {
T t = new T();
t.k = 8;
FileOutputStream fos = new FileOutputStream("d:/share/java/io/testobjectio.dat");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(t);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("d:/share/java/io/testobjectio.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
T tReaded = (T)ois.readObject();
System.out.println(tReaded.i + " " + tReaded.j + " " + tReaded.d + " " + tReaded.k);
}
}
class T implements Serializable
{
int i = 10;
int j = 9;
double d = 2.3;
transient int k = 15;
}