Java学习笔记-IO

IO

IO流概述

可以将数据传输操作,看作一种数据的流动,按照流动的方向分为输入(Input)和输出(Output)
java中的IO操作主要指的是java.io包下的一些常用类的使用,通过这些常用类对数据进行读取(输入Input)和写出(输出Output)

IO流的分类

按照流的方向来分,可以分为:输入流和输出流
按照流动的数据类型来分,可以分为:字节流和字符流
计算机中的任何数据(文本、图片、视频、音乐等等)都是以二进制的形式存储的
在数据传输时也都是以二进制的形式存储的
后续的任何流,在传输时底层都是二进制
流在写完以后一定要关闭

字节流:一切皆字节

输入流:InputStream

常用方法:
    int read (byte[] b): 从输入流中读取一些字节数并将它们存储到缓冲区数组 b 。  
    int read (byte[] b, int off, int len): 从输入流 len最多 len字节的数据读入一个字节数组。  
    abstract int read():从输入流中读取下一个数据字节。  
    byte[] readAllBytes(): 从输入流中读取所有剩余字节。  
    int readNBytes (byte[] b, int off, int len): 从输入流中读取请求的字节数到给定的字节数组中。  
    byte[] readNBytes (int len): 从输入流中读取指定的字节数。  
    void mark (int readlimit) 标记此输入流中的当前位置。  
    void reset(): 将此流重新定位到上次在此输入流上调用 mark方法时的位置。 
    long transferTo (OutputStream out) 从该输入流中读取所有字节,并按读取顺序将字节写入给定的输出流。      void close(): 关闭此输入流并释放与该流关联的所有系统资源。  

常用子类:FileInputStream(实体类)文件输入流

构造方法:
    FileInputStream(File file): 通过打开与实际文件的连接来创建 FileInputStream ,该文件由文件系统中的 File对象 file命名。  
    FileInputStream (String name): 通过打开与实际文件的连接来创建 FileInputStream ,该文件由文件系统中的路径名 name命名。  
常用方法:
    int available(): 返回可以从此输入流中读取(或跳过)的剩余字节数的估计值,而不会被下一次调用此输入流的方法阻塞。  
    void close(): 关闭此文件输入流并释放与该流关联的所有系统资源。  
    FileChannel getChannel(): 返回与此文件输入流关联的唯一FileChannel对象。  
    FileDescriptor getFD(): 返回 FileDescriptor对象,该对象表示与此 FileInputStream正在使用的文件系统中的实际文件的连接。  
    int read(): 从此输入流中读取一个字节的数据。  
    int read (byte[] b): 从此输入流 b.length最多 b.length字节的数据读 b.length字节数组。  
    int read (byte[] b, int off, int len): 从此输入流 len最多 len字节的数据读入一个字节数组。  
    long skip (long n): 跳过并从输入流中丢弃 n字节的数据。  
​
注意:通过while (inputStream.read() != -1)这种方式来循环输出文件内容是不可行的,隔一个输出一个
​
     通过一组字节数组来获取文件内容,文件内容是进行覆盖操作,abcdefg,用一个长度为三的字节数组输出,
     输出结果为 abc def gef 最后一个是因为只有一个元素g了,覆盖了上一数组 def 就变成了 gef
     解决方案:
        byte[] bytes = new byte[3];
        int len = fis.read(bytes);
        sout(new String(bytes,0,len));
        使用重复输出时,数据读完后len == -1
        
常用一组获得的方法

输出流:OutputStream(抽象类)

常用方法:
    void close():关闭此输出流并释放与此流关系的所有系统资源
    void flush():刷新此输出流并强制写出任何缓冲的输出字节
    static OutputStream nullOutputStream():返回一个新的OutputStream,它丢弃所有字节
    void write(byte[] b):将b.length字节从指定的字节数组写入此输出流
    void write(byte[] b,int off ,int len):将从偏移量off开始的指定字节数组汇总的len字节写入此输出流
    abstract void write(int b):将指定的字节写入此输出流

常用子类:FileOutputStream(实体类)文件输出流

构造方法:
                FileOutputStream (File file): 创建文件输出流以写入由指定的 File对象表示的文件。  
                FileOutputStream (File file, boolean append): 创建文件输出流以写入由指定的 File对象表示的文件。  
                FileOutputStream (String name): 创建文件输出流以写入具有指定名称的文件。  
                FileOutputStream (String name, boolean append): 创建文件输出流以写入具有指定名称的文件。 
                append参数表示是否是续写 新建对象时参数写的是true\false
            常用方法:
                void close():关闭此文件输出流并释放与此流关联的所有系统资源。  
                void write (byte[] b):将指定字节数组中的 b.length字节写入此文件输出流。  
                void write (byte[] b, int off, int len):将从偏移量 off开始的指定字节数组中的 len字节写入此文件输出流。    off是开始的下标。len是写出的长度
                void write (int b):将指定的字节写入此文件输出流。
                
注意:流的所有操作都要在关闭(close())之前执行,在关闭后执行会报异常,而且关闭要尽可能早的关闭
     使用字节流输出一串字符串时,要先将字符串转换成byte,xx.getBytes();

字符流:

以字符为单位,解决了字节流字符乱码的问题

输入流:Reader

构造方法:
    protected  Reader(): 创建一个新的字符流阅读器,其关键部分将在阅读器本身上同步。  
    protected  Reader (Object lock): 创建一个新的字符流阅读器,其关键部分将在给定对象上同步。  
常用方法:
    abstract void close(): 关闭流并释放与其关联的所有系统资源。  
    void mark (int readAheadLimit): 标记流中的当前位置。  
    boolean markSupported(): 判断此流是否支持mark()操作。  
    static Reader nullReader(): 返回不读取任何字符的新 Reader 。  
    int read(): 读一个字符。  
    int read (char[] cbuf): 将字符读入数组。  
    abstract int read (char[] cbuf, int off, int len): 将字符读入数组的一部分。  
    int read (CharBuffer target): 尝试将字符读入指定的字符缓冲区。  
    boolean ready(): 判断此流是否可以读取。  
    void reset(): 重置流。  
    long skip (long n): 跳过字符。  
    long transferTo (Writer out): 读取此阅读器中的所有字符,并按照读取的顺序将字符写入给定的编写器。  
​

输出流:Writer

构造方法:
	protected  Writer(): 创建一个新的字符流编写器,其关键部分将在编写器本身上同步。  
	protected  Writer (Object lock): 创建一个新的字符流编写器,其关键部分将在给定对象上同步。  
常用方法:
	Writer append (char c): 将指定的字符追加到此writer。  
	Writer append (CharSequence csq): 将指定的字符序列追加到此writer。  
	Writer append (CharSequence csq, int start, int end): 将指定字符序列的子序列追加到此writer。 
	abstract void close(): 关闭流,先冲洗它。  
	abstract void flush(): 刷新流。  
	static Writer nullWriter(): 返回一个新的 Writer ,它丢弃所有字符。  
	void write (char[] cbuf): 写一个字符数组。  
	abstract void write (char[] cbuf, int off, int len): 写一个字符数组的一部分。  
	void write (int c): 写一个字符。  
	void write (String str): 写一个字符串。  
	void write (String str, int off, int len): 写一个字符串的一部分。  
	
append()方法返回的是调用append()方法的对象,所以append()方法可以套娃,xx.append().append()....;

转换流

将字节流 转换(装饰)为 字符流:使用了装饰者设计模式

InputStreamReader

构造方法:
	InputStreamReader(InputStream(要转换的字节输入流))

OutputStreamWriter

构造方法:
	OutputStreamWriter(OutputStream(要转换的字节输出流))

###

Print与BufferedReader

一种更好的字节流转换成字符流的方法

print

PrintStream:字节流
PrintWriter:字符流  记得最后要刷新流(flush) 或者 关闭(close)	
	操作方法:
		new PrintStream("地址")
		new PrintWriter("地址")
		
		FileOutputStream fos = new FileOutputStream("地址")
		new PrintStream(fos)
		
		FileWriter fw = new FileWriter("地址")
		new PrintWriter(fw)

BufferReader

缓存读取流,将字符输入流,转换为带有缓存 可以一次读取一行的缓存字符读取流
操作方法:
	FileReader fr = new FileReader("地址")
	new BufferReader(fr);

收集异常日志

try{
    String s =null;
    s.toString();
}catch(Exception e){
    e.printStackTrace();
}

try{
    String s =null;
    s.toString();
}catch(Exception e){
    PrintWriter pw = new PrintWriter("地址");
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
    pw.println(sdf);
    e.printStackTrace(pw);
    pw.close();
}

Properties

是使用键值对方式进行存储的
构造方法:
	Properties(): 创建一个没有默认值的空属性列表。
    
常用方法:
	String getProperty (String key): 在此属性列表中搜索具有指定键的属性。  
	String getProperty (String key, String defaultValue): 在此属性列表中搜索具有指定键的属性。  
	void list (PrintStream out): 将此属性列表打印到指定的输出流。  
	void list (PrintWriter out): 将此属性列表打印到指定的输出流。  
	void load (InputStream inStream): 从输入字节流中读取属性列表(键和元素对)。  
	void load (Reader reader): 以简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。  
	void loadFromXML (InputStream in): 将指定输入流上的XML文档表示的所有属性加载到此属性表中。  
	Object setProperty (String key, String value): 调用 Hashtable方法 put 。
    
    void store (OutputStream out, String comments): 将此 Properties表中的此属性列表(键和元素对)以适合使用 load(InputStream)方法加载到 Properties表的格式写入输出流。  
    void store (Writer writer, String comments): 将此 Properties表中的此属性列表(键和元素对)以适合使用 load(Reader)方法的格式写入输出字符流。  
    void storeToXML (OutputStream os, String comment): 发出表示此表中包含的所有属性的XML文档。  
    void storeToXML (OutputStream os, String comment, String encoding): 使用指定的编码发出表示此表中包含的所有属性的XML文档。  
    void storeToXML (OutputStream os, String comment, Charset charset): 使用指定的编码发出表示此表中包含的所有属性的XML文档。  
    
    comment:输出到文件时的备注
    
    Set<String> stringPropertyNames(): 从此属性列表返回一组不可修改的键,其中键及其对应的值是字符串,如果尚未从主属性列表中找到相同名称的键,则包括默认属性列表中的不同键。  

序列化与反序列化

能用,但不建议用,因为java官方说近几个版本会删除
使用序列化时,类和属性都必须实现序列化接口
存进去的一个ArrayList取出来也得是要个集合,不能

对象的序列化和反序列化实现

public class Student implements Serializable {
    private String stuNum;
    private String stuName;
    private List<String> teacherList;
}

定义序列化和反序列化工具类
public class MySerializeUtil {
	//序列化
    public static void mySerialize(Object obj , String fileName) throws IOException {
        FileOutputStream ops = new FileOutputStream(fileName);
        ObjectOutputStream oos = new ObjectOutputStream(ops);
        oos.writeObject(obj);
        oos.close();
    }
	//反序列化
    public static Object myDeserialize (String fileName) throws IOException, ClassNotFoundException {
        FileInputStream fis = new FileInputStream(fileName);
        ObjectInputStream ois = new ObjectInputStream(fis);
        Object object = ois.readObject();
        return object;
    }

}

main方法测试
public static void main( String[] args ) throws IOException {
        ArrayList<String> teacher = new ArrayList<>();
        teacher.add("bdyjy");
        teacher.add("ssyy");
        Student student = new Student("20181271","szh",teacher);
        System.out.println("原始对象:" + student);
        String fileName = "stu.txt";

        try {
            MySerializeUtil.mySerialize(student,fileName);
            System.out.println("对象序列化完成");
            Object obj = MySerializeUtil.myDeserialize(fileName);
            System.out.println("对象反序列化完成");
            if (obj instanceof Student){
                Student NewStu = (Student) obj;
                System.out.println("反序列化后的对象:" + NewStu);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

执行结果:
    原始对象:Student{stuNum='20181271', stuName='szh', teacherList=[bdyjy, ssyy]}
	对象序列化完成
	对象反序列化完成
	反序列化后的对象:Student{stuNum='20181271', stuName='szh', teacherList=[bdyjy, ssyy]}

同时 stu.txt 文件里面也有内容写入

部分属性的序列化

1.使用transient修饰符 : 使用该修饰符修饰的属性将不会被序列化
2.使用static修饰符 :static修饰符修饰的属性也不会参与序列化和反序列化。修改实体类,将实体类中不想序列化的属性添加static修饰词。
3.默认方法writeObject和readObject。
        private void writeObject(ObjectOutputStream objOut) throws IOException {
    		 System.out.println("writeObject-----------");
			objOut.writeObject(stuNum); 
			objOut.writeObject(stuName); 
		}
		private void readObject(ObjectInputStream objIn)throws IOException,ClassNotFoundException 		  { 
			System.out.println("readObject-----------"); 
			stuNum= (String) objIn.readObject(); 
			stuName= (String) objIn.readObject(); 
		}
	我们在添加的方法中只对stuNum和stuName属性做了序列化和反序列化的操作,因此只有这个两个属性可以被序列化和反序列化。
    注意:添加的两个方法必须是private void,否则不生效。

Externalizable实现Java序列化

刚刚我们说实现部分属性序列化的方式有多种,最后一种来啦!就是通过实现Eexternalizable接口。
Externalizable继承自Serializable,使用Externalizable接口需要实现readExternal方法和
writeExternal方法来实现序列化和反序列化。

需要实现序列化和反序列化的类实现Externalizable接口,并重写里面的两个方法
@Override 
	public void writeExternal(ObjectOutput out) throws IOException {
    	out.writeObject(stuNum);
    	out.writeObject(stuName);
    	//out.writeObject(teacherList);
	}
@Override 
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { 
        stuNum= (String) in.readObject();
        stuName= (String) in.readObject();
        //teacherList= (List<String>) in.readObject();
    }

Externalizable接口继承了Serializable接口,所以实现Externalizable接口也能实现序列化和反序列化。
Externalizable接口中定义了writeExternal和readExternal两个抽象方法,这两个方法其实对应
Serializable接口的writeObject和readObject方法。可以这样理解:Externalizable接口被设计出来的
目的就是为了抽象出writeObject和readObject这两个方法,但是目前这个接口使用的并不多。

序列化

Java序列化是指把Java对象转换为字节序列的过程,Java反序列化是指把字节序列恢复成Java对象的过程。通过序列化和反序列化实现网络传输、本地存储的目的。

Serializable实现Java序列化

要实现Java对象的序列化,只要将类实现标识接口--Serializable接口即可,不需要我们重写任何方法就可以实现序列化

Serializable VS Externalizable

区别SerializableExternalizable
实现复杂度实现简单,Java对其有内建支持实现复杂,由开发人员自己完成
执行效率所有对象由Java统一保存,性能较低开发人员决定哪个对象保存,可能造成速度提升
保存信息保存时占用空间大部分存储,可能造成空间减少
使用频率偏低

try-with-resources

JDK1.7之前  : 太麻烦,为了获取一个字符,写了一大段代码
FileReader fr = null;
try{
    fr = new FileReader("地址");
    int c = fr.read();
    sout((char)c);
}catch(IOExceprion e){
    e.printStackTrace();
}finally{
    try{
        fr.close();
    }catch(IOException e){
        e.printStackTrace();
    }
}

JDK1.7时 : 耦合性太强,假如fr在前面就已经被定义,或者在后面还需调用的话,无法使用此方法
try(FileReader fr = new FileReader("地址")){
    int c = fr.read();
    sout((char)c);
}catch(IOException e){
    e.printStackTrace();
}

JDK9进行优化  可自动关闭流。也不妨碍前后调用。需要关闭多个流,各个流之间用;隔开
FileReader fr = new FileREader("地址");
FileWriter fw = new FileWriter("地址");
try(fr;fw){
   int c = fr.read();
   sout((char)c);
}catch(IOException e){
   e.printStackTrace();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值