java基础-IO流篇

1、什么是IO流?

		I:Input       译:读
		O:Output      译:写
		通过IO流可以完成硬盘文件的读和写。

2、IO流的分类:

  有多重分类方式:
  	    
  	一种是按照流的方向进行分类:
  	    	以内存作为参照物:
  	    	从硬盘往内存中去,叫做输入流(Input),或者叫做读(Reader)。
  	    	从内存中出来,叫做输出流(Output),或者叫做写(Writer)。
	
	另一种方式是按照读取数据方式不同进行分类:
		 有的流是按照字节的方式读取数据,一次读取1个字节(1byte),等同于一次读取8个二进制位。
		 
		 这种流是“万能流”,什么类型的文件都可以读取,包括:音频、视频、文本、图片。。。。等等。

		 有的流是按照字符的方式读取数据,一次读取一个字符,这种流是为了方便读取普通文本文件而存在的。
		 这种流不能读取音频、视频、图片等文件,只能读取纯文本文档,连word文件都无法读取。(word文档不是普通文本文件)

综上所述:流的分类可以分为:
输入流、输出流。
字节流、字符流。

谨记:凡是以Stream结尾的,都是字节流,凡是以Reader/Writer结尾的,都是字符流。

例:InputStreamReader
虽然此流中包含了Stream,但并不是以它结尾,所以该流还是字符流。
此IO流属于:“转换流”。将字节流转换为字符流。

3、java中的IO流都已经写好了

可以暂时不去研究底层的实现原理(反正现在也看不懂源码,不如先学会怎么使用这些流,后期有兴趣再慢慢啃)。
IO流主要掌握:
java中已经提供了哪些流 ?
每个流的特点是什么?
每个流对象上的常用方法有哪些?

java中所有的流都是在 java.io.*; 下。
学习要点:主要研究怎么new流对象、调用流对象哪个方法是读、哪个方法是写、哪个流可以将文件写入到硬盘、哪个流可以将文件读入到内存、如何转换流、如何包装流。

4、javaIO流这里有四大家族:

字节输入/输出流:
	java.io.FileInputStream 
	java.io.FileOutputStream
	
字符输入/输出流:
	java.io.Reader
	java.io.Writer

以上四个流为四大家族“首领”,都是抽象类。(abstract)

所有的流都实现了:
java.io.Closeable 接口,接口内有close() 方法,都是可以关闭的。
比方说流是一个管道,内存和硬盘之间的通道,用完之后一定要记得关闭,否则会耗费很多资源,需要养成好的习惯,用完流一定记得关。

所有的输出流都实现了:
java.io.Flushable接口,接口内有flush()方法,所有的输出流都是可以刷新的。
跟上面管道的例子一样,输出流在最终输出之后,一定要记得刷新,否则有可能导致数据的丢失。

(异常中有一个finally() 方法,可以保证流一定可以关闭,需要注意的是如果有多个流需要关闭,建议分开try…catch否则有可能造成有些流关闭不了。)

小结:close()方法是关闭流,输入和输出流都实现了该方法,都可以调用。
flush()方法是刷新流,输出流才需要刷新,确保数据不会丢失。

5、java.io包下需要掌握的流有:

	文件专属流:
	java.io.FileInputStream
	java.io.FileOutputStream
	java.io.FileReadet
	java.io.FileWriter

	转换流:
	java.io.InputStreamReadet
	java.io.OutputStreamWriter

	缓冲流:
	java.io.BufferedReader
	java.io.BufferdWriter
	java.io.BufferedInputStream
	java.io.BufferedOutputStream

	数据流:
	java.io.DataInputStream
	java.io.DataOutputStream

    标准输出流:
    java.io.PrintWriter
    java.io.PrintStream

	对象专属流:
	java.io.ObjectInputStream
	java.io.ObjectOutputStream

提示:没有学习IO流的朋友不要因为看到了这么多种类而产生抵触心理,其实只要学懂了前两个文件流中的字节输入流和字节输出流,其他的流就不算难了,因为用法都差不多,只不过有个别的地方用法不一样,多练习就好!

6、java.io.File类:

	 文件和目录路径名的抽象表示。
	 可以理解为输入或输出流的路径。
/*
    File常用方法
 */
public class FileTest {
    public static void main(String[] args) {
        //通过将给定的路径名字符串转换为抽象路径名来创建新的File实例。
        File file=new File("F:\\homework\\chapter01 第一章");
        //两种构造方法其实一样。
        String path="F:\\homework\\chapter01 第一章";
        File file1=new File(path);

        System.out.println(file.getName());//获取该文件的文件名
        System.out.println(file.getParent());//获取文件的父路径名
        System.out.println(file.getAbsolutePath());//获取该文件的绝对路径
        System.out.println(file.exists());// 判断路径是否为空
        System.out.println(file.isFile());//判断是否是一个文件
        System.out.println(file.isDirectory());//判断是否是一个目录

        File [] files=file.listFiles();//返回一个File数组

        //返回的是毫秒数,从1970年1月1日0:0:0毫秒开始到现在的时间。
        // 需要创建Date时间对象并创建SimpleDateFormat对象初始化时间格式配合使用
        long time=file.lastModified();//返回该文件最后的修改时间

//      file.createNewFile();//以文件的形式新建
        file.mkdir();//以目录的方式新建指定的路径
        file.mkdirs();//以多重目录的方式新建指定的路径
    }
}

7、对象专属流:

	ObjectOutputStream:序列化
	ObjectInputStream: 反序列化
	

序列化:将内存中的文件,保存到硬盘当中,或理解为:写入。
反序列化:将硬盘中的文件,加载到内存当中,或理解为:读。
可以序列化单个对象,也可以序列化多个对象:
/*
	例:
		序列化单个对象:
*/
	public static void main(String[] args) {
        //创建java对象
        Student s1=new Student(111,"李丽");
        //声明序列化对象
        ObjectOutputStream oos=null;
        try {
            //序列化对象
            oos=new ObjectOutputStream(new FileOutputStream("Students"));
            oos.writeObject(s1);

            //刷新流
            oos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            //关闭流
            if (oos != null) {
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
/*
    反序列化单个对象
 */
public class ObjectFileInputStreamTest011 {
    public static void main(String[] args) {
        ObjectInputStream ofi=null;//声明在这里是为了在下面方便关闭流。
        try {
             //因ObjectInputStream的参数只能是(InputStream)所以可以new出一个FileInputStream作为一个包装流将文件传入。
             ofi=new ObjectInputStream(new FileInputStream("Student1"));
             // Object obj=ofi.readObject();
             //System.out.println(obj); 
            
            //这里的返回类型也可以根据输入流内的文件类型进行强转方便操作
            Student s=(Student)ofi.readObject();
        
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally{
            //判断流不为空就关闭,养成好习惯。
            if (ofi != null) {
                try {
                    ofi.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

序列化多个对象,需要创建一个ArrayList集合,将对象存放到集合当中,然后序列化集合,假如不用集合的话,存储第二个对象会报错。

/*
	例:
	序列化多个对象:
*/
	public static void main(String[] args) {

        List<User> userList=new ArrayList<>();
        userList.add(new User(11,"王红"));
        userList.add(new User(22,"赵敏"));
        userList.add(new User(33,"李丽"));
        //声明序列化对象
        ObjectOutputStream oos=null;
        try {
            oos=new ObjectOutputStream(new FileOutputStream("Users"));
            //将List集合序列化
            oos.writeObject(userList);

        } catch (IOException e) {
            e.printStackTrace();
        }finally{
       	 	//流不为空就关闭
       	    if (oos != null) {
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
/*
    一次序列化多个对象
 */
public class ObjectOutputStreamTest02 {
    public static void main(String[] args) {
        //创建ArrayList集合
        List<User> userList=new ArrayList<>();
        userList.add(new User(11,"王红"));
        userList.add(new User(22,"赵敏"));
        userList.add(new User(33,"李丽"));
        
        //声明序列化对象。声明在这里方便下面关闭流
        ObjectOutputStream oos=null;

        try {
        	//默认保存在项目根路径下
            oos=new ObjectOutputStream(new FileOutputStream("Users"));
            //将List集合序列化
            oos.writeObject(userList);
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            //流不为空就关闭
            if (oos != null) {
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

注意:不管是序列化单个对象还是多个对象,需要序列化的类型必须实现Serializable接口。

Seriaizable接口属标志接口:
这个接口当中什么代码都没有。

标志接口的作用:
起到标识的作用,标志的作用,java虚拟机看到这个类实现了这个接口,可能会对这个类进行特殊待遇。
Serializable这个标志接口是给java虚拟机参考的,java虚拟机看到这个接口之后,会为该类自动生成 一个序列化版本号。

假如不想让对象中的某一属性参与序列化,可以使用transient关键字修饰。
transient关键字表示游离的,不参与序列化。

如何使用IDEA自动生成序列化版本号:

建议将序列化版本号手动写出来,不建议自动生成。
java虚拟机识别一个类的时候,先通过类名,如果类名一致,再通过序列化版本号,如果一样。则证明是同一个类。
在这里插入图片描述
在这里插入图片描述
设置修改好了
设置修改好了之后,当新建了自定义类型想手动给定序列化版本号的话,光标停到类名上,按Alt+回车,第一个选项就是,可以使用电脑自动生成的数值,也可以自定义数值,两者都可以!

为什么要使用序列化版本号?
假如某年写了某些代码,现在需要作出修改,如果没有自定义序列号版本号的话,修改后的代码重新编译之后会自动生成新的序列化版本号,这时java虚拟机就会认为这跟原本的代码不一致,是两个程序,但这就违背了程序员的本意,它其实还是本来的代码本来的程序,但自定义序列版本号后,即使程序作出了修改和重新编译,但序列化版本号还是和原来保持一致,这时java会认为是同一代码,同一个类。

8、IO+Properties集合的联合使用:

设计理念:
以后经常改变的数据,可以单独写到一个文件中,使用程序动态读取。
将来只需要修改这个文件的内容,java代码不需要改动,不需要重新编译,服务器也不需要重启。
就可以拿到动态信息。

类似于以上机制的这种文件被称为配置文件。
并且当配置文件中的内容格式是:
key1=value;
key2=value;
的时候,这类文件被称为“属性配置文件”。
java规范中要求:属性配置文件建议以.properties结尾,但这不是必须的,只是建议使用!
java中Properties是专门存放属性配置文件内容的一个类。

public class IpPropertiesTest01 {
    public static void main(String[] args) {
        //输入流
        FileInputStream fis=null;
        try {
            fis=new FileInputStream("chapter23/userinfo.properties");
            //创建Map集合

            Properties pt=new Properties();
            //调用Properties集合的load()方法,将文件中的数据加载到Map集合中
            //pt.load();形参可以为Reader,也可以为InputStream.
            //lad()方法会将文件中等号“=”的左边作为key的值,右边作为value的值(也可以使用分号 “:”)分隔key和value
            pt.load(fis);

            //通过key获取value的值
            String username=pt.getProperty("username");
            System.out.println(username);
            String password=pt.getProperty("password");
            System.out.println(password);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (InvalidPropertiesFormatException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally{
            //流不等于空,使用后关闭
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

配置文件示例:

总结:IO流章节很重要,建议各个流都多练习几次,各个常用方法可以背不会,但在IO流使用的过程中(需要某个方法时)通过 “.” 的形式,看到某个方法时要大概知道是干什么用的。
建议,记方法的中文翻译,这样比较好记。
当需要使用某个方法时:
一、在IDEA中直接使用 " . " 来寻找
二、可以查API帮助文档
三、可以借助“有道”等翻译软件,直接输入汉字看翻译,看翻译可以记起大概怎么写。
另外:在写代码时,用到某些方法不要只输入前几个字母靠IDEA的提示直接回车选择方法,除非你能够将方法名全部背下,否则建议手动把方法名写全,帮助和加深记忆。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值