目录
IO流的分类方式
第一种方式:以内存为参照物,按照流的方向分类
从硬盘到内存的,叫做输入,或读(read)
从内存到硬盘的,叫做输出,或写(write)
第一种方式:按照读取数据的方式不同进行分类
一次读取一个字节(8bit)的方式,叫做字节流,这种流是万能的,可以读取任何形式的文件,包括文字,图片,视频等
一次读取一个字符的方式,叫做字符流,这种流只能读取txt文档。
流的四大类
1、java.io.InputStream 字节输入流
2、java.io.OutputStream 字节输出流
3、java.io.Reader 字符输入流
4、java.io.Writer 字符输出流
这四大类都是抽象类,其他的流都直接或最终继承这四大类中的一个
在java中,只要类名以Stram结尾的都是字节流,以Reader/Writer结尾的都是字符流。
所有的流都实现了 java.io.Closeable接口,都有close方法,用于关闭流。流是内存和硬盘之间的一个通道,使用完后不关闭会持续消耗内存。 所以用完流后一定要关闭
所有的流都实现了 java.io.Flushable接口,都有flush方法,用于刷新流,可以将管道中剩余的数据强制输出(清空管道) 所以每次输出完以后要刷新一下。
常用流IO流(均是java.io包下的)
文件专属:
FileInputStream;(掌握)
FileOutputStream(掌握)
FIleReader
FileWriter
转换流:(将字节流转换成字符流)
InputStreamReader
OutputStreamWriter
缓冲流:
BufferedReader
BufferedWriter
BufferedInputStream
BufferedOutputStream
数据流:
DataInputStream
DataOutputStream
标准输出流
PrintWriter
PrintStream(掌握)
对象专属流:
ObjectInputStream(掌握)
ObjectOutputStream(掌握)
文件专属
FileInputStream详解
FileOutputStream详解
1、构造方法
两种:
FileOutputStream fos=new FileOutputStream(路径);
FileOutputStream fos=new FileOutputStream(路径,true);
第一种每次写的时候会清空文件原有内容,第二种不清空,在原内容后面追加着写。
当路径上的文件不存在,会自动创建该文件。
如下列代码:
FileOutputStream fos=null;
try {
fos=new FileOutputStream("C:\\Users\\Administrator\\Desktop\\a\\myfile",true);
fos.write('a');
fos.flush();
} catch (Exception e) {
e.printStackTrace();
}finally {
if(fos!=null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
在new FileOutputStream(“路径”)中,如果加上参数true,即new FileOutputStream(“路径”,true)则每次写都会在文本后面追加内容,否则会清空文本重新开始写。
2、write方法
有三种形式:
1、void write(int b);写入一个ASCII码为b的字符
2、void write(byte b[]);写入数组b中的内容
3、write(byte b[], int off, int len);写入数组b中下标为off到len的内容
文件复制
使用FileInputStream,FileOutputStream通过读和写两种方式完成文件复制。用一个while循环,每次将第一个文件的内容读到一个byte数组中,然后将byte数组的内容写到第二个文件中。(这两个流是字节流,可以复制所有类型的文件。)
代码如下:
public static void main(String[] args) {
FileInputStream fis=null;
FileOutputStream fos=null;
try {
fis=new FileInputStream("C:\\Users\\Administrator\\Desktop\\a\\myfile");
fos=new FileOutputStream("C:\\Users\\Administrator\\Desktop\\a\\myfile2");
byte[] b=new byte[1024*1024];//每次拷贝1MB
int readCount=0;
while((readCount=fis.read(b))!=-1) {
fos.write(b, 0, readCount);
}
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch(IOException e) {
e.printStackTrace();
}finally {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意:在finally字句中,fis和fos的关闭要分开来进行异常处理,如果放在一起,第一个出错了,则第二个无法被处理到,可能会导致第二个无法被关闭。
FileReader与FIleWrite
这两个流与FIleInputStream和FileOutStream相似,只是用到的数组类型变为char,每次读取的内容是字节,而且字节流只能读文本文件。
缓冲流:BufferedReader与BufferedWriter
带有缓冲区的字符输入流,使用这个流,不需要自定义byte或char数组,自带缓冲。如下列代码:
FileReader reader;
BufferedReader br=null;
try {
reader = new FileReader("b");
br=new BufferedReader(reader);
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}finally {
try {
if(br!=null)
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
BufferedReader的构造方法需要传入一个reader类型的参数,而reader是抽象类,不能直接创建对象,所以使用其子类FileReader进行创建。其中:FIleReader是用于传入参数的流,叫做节点流;外部负责包装的BufferedReader流叫做包装流或处理流;
注意:在关闭流时,只需关闭包装流,因为在BufferedReader的底层代码中,close方法关闭的对象是传入来的对象,所以最终关闭的还是节点流
转换流:InputStreamReader和OutputStreamWriter
转换流可以把字节流和字符流相互转换,如下列代码
public static void main(String[] args) {
FileInputStream fis=null;
InputStreamReader reader=null;
BufferedReader br=null;
try {
fis=new FileInputStream("b");
reader=new InputStreamReader(fis);
br=new BufferedReader(reader);
String line=br.readLine();
String line2=br.readLine();
String line3=br.readLine();
System.out.println(line+line2+line3);
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
其中,fis是reader的节点流,reader是br的节点流。由于fis是字节流,而BufferedReader需要传入字符流,所以先用转换流把fis转换成字符流,再传入br中。
OutputStreamWriter同理。
数据流: DataInputStream和DataOutputStream
DataOutputStream:这个流将数据连同类型写入文件,这个文件不是普通文本文档,记事本打不开。
DataInputStream:这个流将文件的数据按基本数据类型读出。
标准输出流:PrintStream和PrintWriter
PrintStream:标准字节输出流,默认输出到控制台 ,标准输出流不需要手动关闭
PrintStream ps=System.out
out.println("1");
以上代码相当与输出语句System.out.println(“1”);在控制台输出1
标准输出流的方向可以改变:
try {
//创建标准输出流,指向方向是文件b
PrintStream ps=new PrintStream("b");
//改变控制台的输出方向,输出方向是ps流,即文件b中
System.setOut(ps);
//输出
System.out.println("hello ");
System.out.println("world");
//执行完后上面两句输出到文件b中,而不是控制台
} catch (FileNotFoundException e) {
e.printStackTrace();
}
PrintWriter同理。
File类
File类和IO流四大类没有关系,所以File类不能完成文件的读写。
FIle对象是文件和目录路径名的抽象表示形式。
File类构造方法:
File(String pathname);传入一个字符串作为文件的路径,
File(String Parent, String child);传入一个父路径和子路径
File(File parent, String child);传入一个父路径和子路径,父路径以File类型传入。
File类常用方法:
1、String getName();返回文件名
2、String getPath();以String类型返回路径。
3、getAbsolutePath();以String类型返回绝对路径
4、boolean isDirectory();判断路径是否为一个目录(文件夹)
5、boolean isFile();判断路径是否为一个文件夹。
6、long lastModified();获取最后一次编辑时间,返回1970年到该时间的毫秒数,可以把该值转换成时间
7、File[] listFiles();获取目录下的所有子文件或文件夹。
对象的序列化和反序列化:ObjectOutputStream和
对象序列化是将对象从内存放到硬盘中,但切段电源后不会消失。对象序列化是将对象分割成若干小节进行存储。
对象反序列化是将对象从硬盘中取出,拼接成原来的对象读到内存中。
注意:参与序列化和反序列化的对象对应的类必须实现Serializable接口
下列代码实现序列化:
public class 序列化 {
public static void main(String[] args) throws IOException {
Student s=new Student(1111,"zhangsan");
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("students"));
oos.writeObject(s);
oos.flush();
oos.close();
}
}
class Student implements Serializable {
private int no;
private String name;
public Student(int no,String name) {
this.no=no;
this.name=name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
对象反序列化(要在Student类中实现toString方法):
public class 序列化 {
public static void main(String[] args) throws Exception {
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("a"));
Object obj=ois.readObject();
System.out.println(obj);
ois.close();
}
}
如果是多个对象,则ois.readObject()返回一个Object的list集合。
transient关键字:将Student类中的name属性改为private transient String name;则该属性不参与序列化,存储的值为null,上述代码执行的结果如下:
序列化版本号
当一个类实现了Serializable接口,系统会自动提供一个序列化版本号。
优点:
当出现了同名类时,两个类的序列化版本号不同,JVM可以通过序列化版本号进行区分。
缺点: 但一个类实现了Serializable接口,后来改动了代码重新编译,则重新生出序列化版本号。该类未改动前实例化并存储在硬盘中的对象在改动代码后将无法进行反序列化(序列化版本号不同)。因此,对于需要改动代码又要将对象序列化和反序列化的类,要在该类中固定一个序列化版本号:如下列代码:
private static final long serialVersionUID=1231456654;
在类中加入这句代码后,版本号将固定不变,即使改了代码,之前存储的对象依然可以进行反序列化。
IO与properties的联合使用
Properties是一个Map集合,key和value都是String类型
可以将userinfo文件中的数据加载到Properties中
userinfo文件如下:
public static void main(String[] args) {
try {
FileReader reader=new FileReader("userinfo");
Properties pro=new Properties();//穿件一个Properties集合。
pro.load(reader);//load方法将文件中的数据加载到Map集合中
//其中,等号左边是key,右边是value
String userN=pro.getProperty("username");
System.out.println(userN);
String passW=pro.getProperty("password");
System.out.println(passW);
} catch (Exception e) {
e.printStackTrace();
}
}
输出结果:
作用: 一般用于输出配置文件,这样修改配置时,只需要修改文件的信息,不需要修改代码。一般配置文件的扩展名命名为.properties。配置文件中也可以把等号改为冒号。
获取文件的绝对路径
在使用IO流或FIle类操作文件时,需要获取文件路径。在IDE中使用相对路径只能是java工程在IDE的指定文件夹时使用,如果java文件移植到其他环境中,路径对出问题
使用以下方式可以保证路径的准确性:
String path=Thread.currentThread().
getContextClassLoader().getResource("reflect//db.properties").getPath();
其中,getResource中的括号是从src路径下开始寻找文件的,reflect是src路径下的一个包。
注意:这种方式,路径中不得有中文
此时获取到的path是绝对路径,输出结果为:
获取的路径可以以流的方式返回,如下列代码
InputStream is=Thread.currentThread().getContextClassLoader().
getResourceAsStream("reflect//db.properties");