天天看点

java输出流_Java输入输出流

IO流

IO流用来处理设备之间的数据传输,传输是通过流的方式

Java用于操作流的对象都在java.io包中

流就是指一连串流动的字符,以先进先出的方式发送信息的通道。

流的方式

按照流的方向划分

输入流:将外部数据源的数据转换成流,程序通过读取流中的数据,完成对数据源读取的访问

输出流:将流中的数据转换到对应的数据源中,程序通过向流中写入数据,完成对数据源写入

按照类型划分

字节流:以字节为单位(8位),可以访问所有文件

字符流:以字符为单位(16位Unicode),只能访问文本文件

输出流

比如我们经常使用的 System.out.println("cocci"); 就是将字符串输出到控制台中,或者说输出到屏幕中,这里的屏幕就是输出设备,由程序将cocci通过流输出到目的地。

java输出流_Java输入输出流

输出设备除了屏幕,还有打印机、文件等。

输入流

比如使用键盘接收数据 Scanner sc = new Scanner(System.in); 这里的System.in就是输入流,程序从数据源这里指键盘去读取数据通过流输入到程序当中。

java输出流_Java输入输出流

输入设备除了键盘,还有扫描仪、文件等。

File类

文件和目录路径名的抽象表示形式,表示磁盘上的文件或目录

文件:可认为是相关记录或放在一起的数据的集合

构造方法

java输出流_Java输入输出流

示例:以下三种方式等价,各有不同的使用场景

//Windows中路径分隔可使用/或者\\

File file1 = new File("d:\\java\\a.txt");

File file2 = new File("d:\\java","a.txt");

File file3 = new File(new File("d:\\"),"java\\a.txt");

常用方法

java输出流_Java输入输出流

注意事项

delete()方法在删除目录时如果其内有内容则无法删除,需要先清空目录下的内容,再删除目录本身

isDirectory()和isFile()方法在判断是否为文件或者目录时,如果文件或目录不存在则返回false

createNewFile()方法在创建文件时,如果文件所在的目录不存在则创建失败并抛出异常

String[] list()和 File[] listFiles() 都是返回对象包含的所有的文件和目录,返回类型不同

文件与目录的相关操作

public static void main(String[] args) {

File file=new File("d:\\test\\java");

if(file.exists()){

System.out.println("目录存在,删除目录");

file.delete(); //只会删除一级目录,即java

}else{

boolean b=file.mkdirs(); //创建多级目录

System.out.println("创建文件目录结果:" + b);

}

File file2=new File("d:\\test\\a.txt");

if(file2.exists()){

System.out.println("文件存在,删除文件");

file2.delete();

}else{

try {

file2.createNewFile(); //创建文件

} catch (IOException e) {

e.printStackTrace();

}

System.out.println("创建文件");

}

//重命名,把a.txt重命名为b.txt

//file2.renameTo(new File("d:\\test\\b.txt"));

//把b.txt剪切到java目录下并重命名为c.txt

file2.renameTo(new File("d:\\test\\java\\c.txt"));

}

获取File对象的所有子文件和目录

public class TestFile {

public static void main(String[] args) {

//获取磁盘的所有根目录

File[] files =File.listRoots();

for(int i=0;i

System.out.println(files[i].getPath());

//输出的是 C:\ D:\

}

TestFile tf = new TestFile();

//打印Silly目录下的所有子目录和文件

File file=new File("d:\\Silly");

tf.printFile(file,"");

}

public void printFile(File file,String str){

System.out.println(str + file.getName());

if(file.isDirectory()){

File[] files=file.listFiles();

for(int i=0;i

//递归调用

printFile(files[i], str+"\t");

}

}

}

}

字节流

字节输入流

InputStream,此抽象类是表示字节输入流的所有类的超类,其主要子类如下

java输出流_Java输入输出流

字节输出流

OutputStream,此抽象类是字节输出流的所有类的超类,其主要子类如下

java输出流_Java输入输出流

过滤器输入输出流

FileInputStream

从文件系统中的某个文件中获得输入字节

用于读取诸如图像数据之类的原始字节流

构造方法

java输出流_Java输入输出流

主要方法

read方法不带参数的返回值为读取的单个字节值,带参数的返回值表示读取的字节长度。如果返回值为-1,则表示已经达到文件末尾!

java输出流_Java输入输出流

FileOutputStream

构造方法

java输出流_Java输入输出流

主要方法

java输出流_Java输入输出流

演示Demo

public class IOTest {

public static void main(String[] args) {

OutputStream fos = null;

InputStream fis = null;

try {

fos=new FileOutputStream("d:\\silly.txt");

String str="最好的我们隔了一整个青春";

byte[] words=str.getBytes(); //把字符串编码成字节序列

//写入操作

fos.write(words, 0, words.length);

System.out.println("写入成功!");

fis = new FileInputStream("d:\\silly.txt");

StringBuilder sb = new StringBuilder();

byte[] buf = new byte[1024]; //字节数组缓存数据

int n = 0; //记录读取的字节长度

//循环读取数据

while((n = fis.read(buf)) != -1){

//这里使用三个参数的构造方法,因为最后一次读取的长度可能达不到buf数组的长度

//所以根据实际读取的长度n去构造对象更合理

sb.append(new String(buf, 0, n));

buf = new byte[1024]; //重新初始化,避免数据重复

}

System.out.println(sb.toString());

} catch (IOException e) {

e.printStackTrace();

}finally{

try {

//释放资源

if(fos != null) fos.close();

if(fis != null) fis.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

文件复制

public static void main(String[] args) {

//将d盘的图片复制到桌面

InputStream is = null;

OutputStream os = null;

try {

//实例化输入输出流对象

is = new FileInputStream("d:\\girl.jpg");

os = new FileOutputStream("C:\\Users\\ruoxiyuan\\Desktop\\mm.jpg");

//定义一个2048字节的缓存

byte[] buffer=new byte[2048];

int len= 0; //读取的字节长度

while((len = is.read(buffer)) != -1){

//最后一次读取的长度len不一定能达到buffer数组的长度,也就是空间有可能富余

//如果直接使用write(buffer)方法可能导致写入更多的字节,新文件会稍大

os.write(buffer, 0, len);

}

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}finally{

try {

//释放资源

if(is != null) is.close();

if(os != null) os.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

字符流

字符输入流

Reader,此抽象类是表示字符输入流的所有类的超类,其主要子类如下

java输出流_Java输入输出流

字符输出流

Writer,此抽象类是表示字符输出流的所有类的超类,其主要子类如下

java输出流_Java输入输出流

字节字符转换流

InputStreamReader:是字节流通向字符流的桥梁,它使用指定的 charset 读取字节并将其解码为字符。

OutputStreamWriter:是字符流通向字节流的桥梁,它使用指定的 charset 将写入其中的字符编码成字节。

其使用的字符集可以由构造方法指定,或者使用平台默认的字符集。

FileReader

用来读取字符文件的便捷类,使用默认字符集进行编码,InputStreamReader的子类

构造方法

java输出流_Java输入输出流

常用方法

java输出流_Java输入输出流

FileWriter

用来写入字符文件的便捷类,使用默认字符集进行编码,OutputStreamWriter的子类

构造方法

如果使用new FileWriter(file),已有文件内容此时会被清空,设置第二个参数为true表示追加内容

java输出流_Java输入输出流

常用方法

java输出流_Java输入输出流

演示Demo

public class IOTest {

public static void main(String[] args) {

try {

FileWriter writer = new FileWriter("d:\\silly.txt");

String str = "成功的人只会去做他们该做的事情,只会面对他们的困难,不会有抱怨,"

+ "更不会有羡慕,因为他们心中有目标,理想,动力以及那颗沉淀的心";

writer.write(str);

writer.flush(); //刷新缓冲区

FileReader reader = new FileReader("d:\\silly.txt");

char[] buffer = new char[1024];//定义缓冲区

int len = 0;

while((len = reader.read(buffer)) != -1){

String res = new String(buffer, 0, len);

System.out.println(res);

}

writer.close();

reader.close();

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

}

缓冲流

使用缓冲流可以提高读写速度,注意缓冲流在调用write方法写入数据时,是写入的缓冲区,如果缓冲区满了自动执行写操作,缓冲区不满则需要执行flush方法强制写入到输出设备,调用close方法也会强制写入

字节缓冲流

缓冲输入流BufferedInputStream,缓冲输出BufferedOutputStream

只是对字节流进行了包装,使用方式和字节流基本一致

构造方法:

public BufferedInputStream(InputStream in)

public BufferedOutputStream(OutputStream out)

字符缓冲流

缓冲输入流BufferedReader

构造方法:public BufferedReader(Reader in)

特殊方法:String readLine() 读取一个文本行

缓冲输出流BufferedWriter

构造方法:public BufferedWriter(Writer out)

特殊方法:void newLine() 写入一个行分隔符

演示Demo

public class IOTest {

public static void main(String[] args) {

try {

FileWriter writer = new FileWriter("d:\\silly.txt");

BufferedWriter bw = new BufferedWriter(writer);

bw.write("书山有路勤为径");

bw.newLine(); //写入换行符(根据平台写入对应的换行符)

bw.write("学海无涯苦作舟");

bw.newLine();

//windows下使用\r\n也能换行

bw.write("世间安得两全法" + "\r\n");

bw.write("不负如来不负卿");

bw.flush(); //必须刷新缓冲区

String record = null; //存储文件的内容

int count = 0; //记录行数

FileReader reader = new FileReader("d:\\silly.txt");

BufferedReader br = new BufferedReader(reader);

//每次读取一整行数据,返回值为空时说明读取到文件末尾

while((record = br.readLine()) != null){

count++;

System.out.println("当前行数"+count+":"+record);

}

bw.close();

br.close();

writer.close();

reader.close();

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

}

对象序列化与反序列化

序列化:把Java对象转换为字节序列的过程

反序列化:把字节序列恢复为Java对象的过程

只有实现了Serializable接口的类的对象才能被序列化

序列化用途

把对象的字节序列永久保存在硬盘上,通常放在一个文件中

在网络上传送对象的字节序列

相关类

对象输入流类:ObjectInPutStream

对象输出流类:ObjectOutPutStream

序列化步骤

创建一个对象输出流:ObjectOutPutStream out = new ObjectOutPutStream(OutPutStream out);

调用方法对对象进行序列化 out.writeObject(Object obj); 把对象序列化,得到字节序列写入流中

刷新缓冲并关闭流 out.flush(); out.close();

反序列化步骤

创建一个对象输入流:ObjectInPutStream in = new ObjectInPutStream(InPutStream in);

调用方法 in.readObject(); 读取字符序列,把参数反序列化成对象,返回该对象

关闭流 in.close();

演示Demo

定义一个学生类实现Serializable接口

public class Student implements Serializable{

private String name;

private int age;

public Student(String name, int age) {

super();

this.name = name;

this.age = age;

}

@Override

public String toString() {

return "Student [name=" + name + ", age=" + age + "]";

}

}

实现对象序列化与反序列化

public class IOTest {

public void save(Student stu){

OutputStream os=null;

try {

os = new FileOutputStream("d:\\stu.dat");

ObjectOutputStream oos = new ObjectOutputStream(os);

oos.writeObject(stu);

//写入其他类型数据

oos.writeBoolean(true);

oos.flush();

oos.close();

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}finally{

if(os!=null){

try {

os.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

public Student read(){

InputStream is=null;

Student stu=null;

try {

is = new FileInputStream("d:\\stu.dat");

ObjectInputStream ois = new ObjectInputStream(is);

stu = (Student)ois.readObject();

//注意读取顺序要和写入顺序保持一致

System.out.println(ois.readBoolean());

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

} catch (ClassNotFoundException e) {

e.printStackTrace();

}finally{

if(is!=null){

try {

is.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

return stu;

}

public static void main(String[] args) {

Student stu = new Student("小小", 18);

IOTest test = new IOTest();

test.save(stu);

Student res = test.read();

System.out.println(res);

//true

//Student [name=小小, age=18]

}

}

Serializable接口

实现该接口会按默认方式进行序列化和反序列化

默认方式序列化

这种序列化方式仅对非transient的实例变量进行序列化

不会序列化对象的transient的实例变量,也不会序列化静态变量

默认方式反序列化

如果内存中对象所属的类还没有被加载,那么会先加载并初始化这个类,如果classpath中不存在该类文件,那么抛出ClassNotFoundException;

在反序列化时不会调用类的任何构造方法

如果希望控制类的序列化方式添加如下方法

private void writeObject(ObjectOutputStream out) throws IOException

private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException

可序列化类添加该方法后进行可序列化操作会执行该方法,否则按默认方式执行。在以上方法中可以先调用默认的defaultWriteObject()和read方法

序列化说明

对于包含敏感信息的对象,可以先加密后再序列化,反序列化时需要解密

默认序列化方式会序列化整个对象图,需要递归遍历对象图,如果对象图复杂,递归遍历需要消耗大量空间和时间,图内部数据结构为双向列表

实际应用中,如果对某些成员变量改为transient类型,可以节省空间和时间,提高可序列化的性能