1、什么是流?流是一个抽象的概念,代表一串数据集合,当java程序需要从数据源读取数据时,就开启了一个到数据源的流,同样,当数据需要输出数据到达目的地时,也需要开启一个流。流是用来处理数据的通道。流有字节流,字符流;输入流,输出流。
2、InputStream和OutputStream是以字节为单位的输入输出抽象流类(输入是指从字节到流(可读),输出是指从流到字节(可写))。
3、Reader和Writer是以字符为单位的输入、输出抽象流类(输入是指从字节到流(可读),输出是指从流到字节(可写))
4、FileInputStream和FileOutputStream是以字节为操作单位的文件输入流和文件输出流。FileReader和FileWriter是以字符为操作单位的文件输入流和文件输出流。一般来讲,以字节为单位的适合用来对图片、声音、视频等文件进行操作。而已字符为单位的适合对文本文件进行操作。
使用I/O流类来操作对象时一般步骤如下:
(1)创建连接到指定数据源的I/O流对象
(2)利用I/O流类提供的方法进行数据的读取和写入,整个过程中都需要操作java.io.IOException异常。另外,如果是向输入流写入数据,还需要在写入操作完成后调用flush()方法强制写出所有缓冲的数据。
(3)操作完毕后,一定要调用close()方法关闭I/O流对象,后创建的先关闭。
package stream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class FileRWTest {
public static void main(String[] args) {
try {
/**
* 使用以字节为单位的可以有效减少乱码产生
*/
FileReader fileReader=new FileReader("test1.txt");
FileWriter fileWriter=new FileWriter("test_new.txt");
char charArray[]=new char[20];
int length=0;
while((length=fileReader.read(charArray))!=-1){
fileWriter.write(charArray,0,length);
}
fileWriter.flush();
System.out.println("复制完成");
fileReader.close();
fileWriter.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package stream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import commonly_class.string;
public class FileStreamTest {
/**
* US-ASCII Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the Unicode character set
ISO-8859-1 ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1
UTF-8 Eight-bit UCS Transformation Format
UTF-16BE Sixteen-bit UCS Transformation Format, big-endian byte order
UTF-16LE Sixteen-bit UCS Transformation Format, little-endian byte order
UTF-16 Sixteen-bit UCS Transformation Format, byte order identified by an optional byte-order mark
* @param args
*/
public static void main(String[] args) {
try {
FileInputStream fileInputStream=new FileInputStream("tset.mp4");
FileOutputStream fileOutputStream=new FileOutputStream("test_new.mp4");
byte byteArray[]=new byte[21];//使用字节流读取数据,容易出现乱码
int length=0;
while ((length=fileInputStream.read(byteArray))!=-1) {//在文件结尾处返回-1
fileOutputStream.write(byteArray,0,length);//用length控制复制范围,防止在最后一次多复制
//String string=new String(byteArray,0,length);
//System.out.println(string);
}
fileOutputStream.flush();//刷新数据流
System.out.println("复制完成");
fileInputStream.close();//关闭
fileOutputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
5、缓冲流:为了提高读写速度,java提供了缓冲功能的流类,在使用这些带缓冲功能的流类时,它会创建一个内部缓存冲区数组。在读取字节或字符时,会先从数据源取到数据填充到缓冲区,然后在返回。在写入字节和字符时,会先以写入的数据填充到该内部缓冲区,然后依次性将目标写入数据源中。简而言之就是在数据源(磁盘)和流之间建立一个区域,用于一次性取较多数据并缓存起来,流再从该区域取数据,这样就减少访问数据源次数,提高效率。
缓冲流也分为两类,针对字节的缓冲输入和输出流:BufferedInputStream和BufferedOutputStream;针对字符的缓冲输入流和输出流:BufferedReader和BufferedWriter
package stream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class BufferedIOTest {
public static void main(String[] args) {
/**
* 使用缓冲区处理大型文件可以提高很大的效率
* 具体什么时候能达到效率最高,
* 需要反复调试BufferedOutputStream(fileOutputStream,50000);和new byte[3000];的数值匹配
*/
try {
FileInputStream fileInputStream=new FileInputStream("tset.mp4");
FileOutputStream fileOutputStream=new FileOutputStream("test_new.mp4");
BufferedInputStream bufferedInputStream=new BufferedInputStream(fileInputStream,50000);
BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(fileOutputStream,50000);
byte byteArray[]=new byte[3000];//使用字节流读取数据,容易出现乱码
int length=0;
while ((length=bufferedInputStream.read(byteArray))!=-1) {
bufferedOutputStream.write(byteArray, 0, length);
}
System.out.println("复制成功");
bufferedOutputStream.flush();//刷新缓冲区
bufferedInputStream.close();//依次关闭流
bufferedOutputStream.close();
fileInputStream.close();
fileOutputStream.close();
}
catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
6、转换流用于字节流和字符流之间的转换。InputStreamReader用于将字节流读取的字节按指定字符集解码成字符,他需要与InputStream套接。OutputStreamWriter用于将写入到字节流的字符按照指定字符集编码成字节,它需要与OutputStream套接。
package stream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class ByteToChar {
public static void main(String[] args) {
System.out.println("请输入信息(退出输入e)");
//将在标准的输入流的字节流转化为字符流,再包装成缓冲流
BufferedReader bufferedReader=new BufferedReader(
new InputStreamReader(System.in));
String string=null;
try {
while ((string=bufferedReader.readLine())!=null) {
if (string.equalsIgnoreCase("e")) {//输入e退出
System.out.println("安全退出!");
break;
}
System.out.println("---->:"+string.toUpperCase());//将字符串转化为大写
System.out.println("请继续输入信息");
}
bufferedReader.close();//关闭缓冲流,会自动关闭它包装的底层的字节流和字符流
} catch (IOException e) {
e.printStackTrace();
}
}
}
7、数据流是用来专门把Double、Int等基本数据类型写入文本或从文本中读取数据。数据流主要有两类,DataInputStream和DataOutputStream,分别用来读取和写入基本数据类型的数据。,例如他们分别对应readInt()方法和writeBoolean()分别代表读取一个int类型数据和写入一个Boolean类型数据。
package stream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
public class TestDataStream {
public static void main(String[] args) {
ByteArrayOutputStream baos=new ByteArrayOutputStream();
DataOutputStream dos=new DataOutputStream(baos);//数据流
try {
dos.writeInt(1);
dos.writeBoolean(true);
dos.writeChars("中国china\t");
dos.writeUTF("中国china");
ByteArrayInputStream bais=new ByteArrayInputStream(baos.toByteArray());
System.out.println(bais.available());//长度
DataInputStream dis=new DataInputStream(bais);
System.out.println(dis.readInt());//读取一个32为整数
System.out.println(dis.readBoolean());
char temp[]=new char[200];
int length=0;
char c=0;
while ((c=dis.readChar())!='\t'){
temp[length]=c;
length++;
}
String string=new String(temp,0,length);
System.out.println(string);
System.out.println(dis.readUTF());//读取一个由UTF格式字符组成的字符串
dos.close();
dis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
8、打印流,PrintStream和PrintWriter流都属于打印流,他们提供了一系列的print和println方法,可以实现将基本数据类型的数据格式转化为字符串输出。我们常用的Systeam.out.println语句中的Systeam.out就是PrintStream类的一个实例
package stream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import javax.crypto.NullCipher;
/**
* 把标准的输出改为文件的输出
* @author kepu
*
*/
public class PrintStreamTest {
public static void main(String[] args) {
FileOutputStream fos=null;
try {
fos=new FileOutputStream("test1.txt");
//创建打印输出流,设置为自动刷新模式
PrintStream ps=new PrintStream(fos, true);
if(ps!=null){
//把标准输出流改为文件
System.setOut(ps);
}
for (int i = 0; i <255; i++) {//输入ASCII字符
System.out.print((char)i);
if (i%50==0) {
System.out.println();
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
finally{
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
9、对象流。JDk中提供了ObjectOutputStream和ObjectInputStream类是用于存储和读取基本数据类=类型数据或对象的流,他的强大之处在于可以把java中的对象写到数据源中,也能把对象中还原回来。用ObjectOutputStream类保存基本数据类型或对象的机制叫做序列化;用ObjectInputStream类读取基本数据类型或对象的机制叫做反序列化。
需要注意的是ObjectOutputStream和ObjectInputStream不能序列化static或transient修饰的成员变量。另外,能被序列化的对象所对应的类必须实现java.io.Serializable这个标示性接口。
package stream;
public class Student implements java.io.Serializable {
/**
* 凡是实现 Serializable接口的类都有一个表示序列化版本的标识符
* erialVersionUID用来表示类的不同版本间的兼容性
* erialVersionUID可以显示的表示出来,也可以采用系统默认的值,
* 对代码进行修改再重新编译时,erialVersionUID的值可能会改变
* 显示的定义erialVersionUID的值主要有以下两种用途:
* 在某些场合,希望类的不同版本对序列化兼容,因此需要确保不同版本具有相同的erialVersionUID值
* 在某些场合,希望类的不同版本对序列化不兼容,因此需要确保不同版本具有不同的erialVersionUID值
*/
private static final long erialVersionUID = 858606830804257830L;
private int id;
private String name;
private transient int age;//不需要序列化的属性
public Student(int id,String name ,int age) {
this.age=age;
this.id=id;
this.name=name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String toString() {
return "id: "+id+"name: "+name+"age: "+age;
}
}
package stream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class ObjectIOStreamTest {
public static void main(String[] args) {
ObjectOutputStream oos=null;
ObjectInputStream ois=null;
try {
//创建连接到指定文件的对象流实例
oos=new ObjectOutputStream(new FileOutputStream("test1.txt"));
ois=new ObjectInputStream(new FileInputStream("test1.txt"));
oos.writeObject(new Student(22, "张三", 18));
//把Student对象序列化到文件中
oos.flush();//刷新输出流
System.out.println("序列化成功");
Student student=(Student) ois.readObject();//读取对象
System.out.println(student);
System.out.println("反序列化成功");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
finally{
try {
oos.close();
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
10、ZIP文件流,由于网络宽带速度有限,所以数据文件的压缩有利于数据在Internet上快速传输,同时也节省空间。java实现了I/O数据流与网络数据流的单一接口,因此数据的压缩、网络传输和解压文件的实现比较容易。java支持GIZP和ZIP两种格式。以ZIP为例来讲,主要有java.util.zip包中ZipEntry、ZipInputStream、ZipoutputStream三个类实现ZIP数据压缩方法的实现。
ZipEntry代表ZIP文件条目,要压缩的文件都要转化为一个个条目
ZipInputStream实现了ZIP压缩文件的读输入流,支持压缩和非压缩entry
ZipOutputStrem实现了ZIP压缩文件的输出流,支持压缩和非压缩entry
package stream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
public class ZipOutDemo {
public static void main(String[] args) {
/**
* 对单一文件压缩
*/
ZipOutputStream zos=null;
FileInputStream fis=null;
BufferedInputStream bis=null;
BufferedOutputStream bos=null;
ZipEntry ze=null;//条目
String tarstr = "tset.mp4";//源文件
String destrtr = "zzz.zip";//目标文件
try {
//创建文件输入流对象
fis = new FileInputStream(tarstr);
//创建Zip输出流
zos=new ZipOutputStream(new FileOutputStream(destrtr));
//创建条目
ze=new ZipEntry(tarstr);
//写入条目
zos.putNextEntry(ze);
//创建输出输入缓冲流
bos=new BufferedOutputStream(zos,10000);
bis=new BufferedInputStream(fis,10000);
//byte c=0;
byte b[]=new byte[500];
int c=0;
while ((c= bis.read(b))!=-1) {
bos.write(b,0,c);
}
bos.flush();//刷新zip输出流
zos.closeEntry();//关闭当前zip条目
System.out.println("压缩完成");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally{
try {
fis.close();//关闭各种流
zos.close();
bis.close();
bos.close();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
/**
* 对单一文件解压
*/
ZipInputStream zis=null;
FileOutputStream fos=null;
ZipEntry ze2=null;
String tarstr2 = "zzz.zip";//源文件
String destsr2 = "zzz.mp4";//目标文件
try {
//创建文件输出流对象
fos=new FileOutputStream(destsr2);
//创建zip输入流
zis=new ZipInputStream(new FileInputStream(tarstr2));
//写入条目
ze2=zis.getNextEntry();
//创建输出输入缓冲流
bos=new BufferedOutputStream(fos,10000);
bis=new BufferedInputStream(zis,10000);
byte b[]=new byte[500];
int c=0;
while ((c= bis.read(b))!=-1) {//边读边写
fos.write(b,0,c);
}
zis.closeEntry();//关闭当前zip条目
System.out.println("解压完成");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally{
try {
bis.close();//关闭各种流
bos.close();
fos.close();
zis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package stream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import try_catch.ThrowsDemo;
/**
* zip压缩文件夹与解压文件夹
* @author kepu
*
*/
public class ZipTest {
public static void main(String[] args) {
zipFile("g:\\www", "g:\\abc.zip");
System.out.println("压缩完成");
upZipFile("g:\\abc.zip","g:\\www_new");
System.out.println("解压完成");
}
//zip压缩功能。压缩baseDir(文件目录下)的所有文件,包含子目录
public static void zipFile(String baseDir,String fileName) {
List<File> fileList =getSubFiles(new File(baseDir));
ZipOutputStream zos =null;//zip输出流
ZipEntry ze=null;//条目
byte buf[]=new byte[1000];//缓冲区
try {
zos = new ZipOutputStream(new FileOutputStream(fileName));
for(File f: fileList){
//条目的名只能使用相对于基目录的相对路径
ze = new ZipEntry(getAbsFileName(baseDir, f));
//文件当做条目使用
ze.setSize(f.length());
ze.setTime(f.lastModified());
zos.putNextEntry(ze);//开始一个新文件写入
InputStream is = new BufferedInputStream(new FileInputStream(f));
//创建连接到指定文件的输入流
int readLen=-1;
while((readLen=is.read(buf))!=-1){//从文件中读取数据
zos.write(buf, 0, readLen);//往ZIp输出流中写
}
zos.closeEntry();//关闭当前条目
is.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
zos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//给定根目录,返回另一个文件名的相对路径,使用ZIP文件的路径
public static String getAbsFileName(String baseDir,File file)throws IOException{
String reult=file.getName();//记住文件名
File base=new File(baseDir);
File temp=file;
while (true) {
temp=temp.getParentFile();
if((temp==null)||(temp.equals(base))){
break;
}else {
reult=temp.getName()+"/"+reult;
}
}
reult=base.getName()+"/"+reult;
return reult;
}
//递归获取指定目录下的所有子孙文件、目录列表
private static List<File> getSubFiles(File baseDir) {
List<File> list =new ArrayList<File>();
File tmp[]=baseDir.listFiles();
for (int i = 0; i < tmp.length; i++) {
if (tmp[i].isFile()) {
list.add(tmp[i]);
}
if (tmp[i].isDirectory()) {
list.addAll(getSubFiles(tmp[i]));//递归
}
}
return list;
}
/**
* 解压缩功能. 将zipName文件解压到destDir目录下.
*/
@SuppressWarnings("unchecked")
public static void upZipFile(String zipName, String destDir) {
ZipFile zfile = null;
ZipEntry ze = null;
byte[] buf = new byte[8192];
try{
zfile = new ZipFile(zipName);
Enumeration zList = zfile.entries();
while (zList.hasMoreElements()) { //遍历zip中的条目
ze = (ZipEntry) zList.nextElement();
if (ze.isDirectory()) { //如果条目是目录
File f = new File(destDir + "/" + ze.getName());
f.mkdir();
}else{
OutputStream os = new BufferedOutputStream(
new FileOutputStream(
getRealFileName(destDir, ze.getName())));
InputStream is = new BufferedInputStream(zfile.getInputStream(ze));
int readLen = -1;
while ((readLen = is.read(buf)) != -1) {
os.write(buf, 0, readLen);
}
is.close();
os.close();
}
}
}catch(IOException e){
e.printStackTrace();
}finally{
try {
zfile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 给定根目录,返回一个相对路径所对应的实际文件名.
* @param baseDir 指定根目录
* @param absFileName 相对路径名,来自于ZipEntry中的name
* @return java.io.File 实际的文件
*/
public static File getRealFileName(String baseDir, String absFileName) {
String[] dirs = absFileName.split("/");
File dest = new File(baseDir);
if (dirs.length > 1) {
for (int i = 0; i < dirs.length - 1; i++) {
dest = new File(dest, dirs[i]);
}
if (!dest.exists()){
dest.mkdirs();
}
dest = new File(dest, dirs[dirs.length - 1]);
return dest;
}
return dest;
}
}
11、随机存储流类,RandomAccessFile是一个特殊的流类,它可以在文件的任何地方读取或写入数据。打开一个随机存储文件后,要么对他进行只读操作,要么同时进行读写操作。可以通过他的构造方法的第二个参数进行设置,r为只读。rw为读写,还要rws,rwd。
随机存取文件的类似于存储文件系统中的一个大型的byte数组,它提供了一个指向该数组的光标或索引,成为文件指针,该文件指针用来标识将要进行的读写操作的下一个字节位置,getFilePointer方法可以返回文件指针的当前位置。使用seek方法可以将文件指针移动到该文件内部的任意字节位置处。
package stream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class RandomAccessFileTest {
public static void main(String[] args) {
File file = new File("g:"+File.separator+"test.txt");
try {
RandomAccessFile raf=new RandomAccessFile(file, "rw");
//同时读写方式,文件如果不存在,会创建文件
String name = null;
int age=0;
name="dds sh";//字符串长度为8
age=22;//数字长度为4
raf.writeBytes(name);//写字符串
raf.writeInt(age);//写整型
name="dddd ";//字符串长度为8
age=10;//数字长度4
raf.writeUTF(name);//写字符串
raf.writeInt(age);//写整型
name="lilidqqqq";//字符串长度8
age=19;//数字长度4
raf.writeUTF(name);//写字符串
raf.writeInt(age);//写整型
System.out.println("写入完成");
byte b[]=new byte[8];
raf.seek(0);
//raf.skipBytes(12);
for (int i = 0; i <b.length; i++) {
b[i]=raf.readByte();
}
name=new String(b);
age=raf.readInt();
System.out.println("第一个学员信息为: name:+"+name+ " age:"+age);
raf.seek(12);
//raf.skipBytes(12);
name=raf.readUTF();
age=raf.readInt();
System.out.println("第二个学员信息为: name:+"+name+ " age:"+age);
raf.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
另外,在Apache提供的各类库中有更为丰富的类和接口,使用起来也很方便。