尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

java第六部分 stream流到io流

java第六部分 stream流到io流 一、Stream流三种方法1.1 获取Stream流的方法1作用获取stream对象。2具体方法各种类型的数据获取Stream流的方式不同如下1.2 中间方法1作用由Stream对象调用并且返回处理后的Stream对象。2具体方法a. filter用于数据过滤Predicate是个函数式接口我们用实现类重写test方法并传入对象即可调用流的每个元素会依次被test方法处理test返回true则保留false则丢弃。可进一步简化为lambda表达式。b. limit和skip方法其实就是用于从流中获取某几个元素c. concat与distinct合并两个流去除相同元素1.3 终结方法1特点一个Stream流只能有一个终结方法。是流水线上的最后一个操作。2具体方法比如下面三种a. forEach方法演示依然是实现函数式接口并重写抽象方法并传入对象。流中的每个元素会交给accept进行处理。可进一步简化为lambda表达式。b. collect方法演示要把流收入到什么集合中就在collect中填对应的方法即可。下面是把流收集为map这个有些特殊由于流中的元素是一个一个的没有键值之分所以需要多一步操作来获取键和值对于过滤逻辑这里就不要关注了咱们只关注collect部分。toMap其实是要传入两个Function接口的实现类对象。lambda表达式还原为匿名内部类为因此就是流中的每个元素都会被这两个函数处理第一个函数的返回值作为键第二个作为值存入map。二、异常2.1 异常的体系结构1ErrorError表示 JVM 或系统级别的严重错误通常程序无法通过代码解决一般不进行处理。2运行时异常范围RuntimeException及其子类。特点如果某个方法可能出现运行时异常编译阶段时不会检查是否有这种异常的处理方法。因此有可能发生这类异常的部分我们可以不写代码进行处理。3编译时异常范围除RuntimeException体系的所有异常。特点如果某个方法可能出现编译时异常编译时就会检查该异常有没有处理方法的异常。因此当某个方法可能出现编译时异常时我们必须写好处理方法否则会编译报错。例子如下parse有可能出现编译时异常写好应对方法使用throws处理。2.2 运行时异常的产生过程以运行时异常数组越界异常为例第一步异常触发由于数组没有索引4因此就触发了运行时异常然后把异常信息封装到对应的异常类对象中。第二步异常传递由于getelement方法没有异常的处理方法把异常对象沿着方法调用关系向上传递。也就是给到main方法。第三步JVM处理由于main方法并没有异常处理main方法把异常抛给JVMJVM把异常信息打印到屏幕上然后使程序终止如果说调用了很多层方法异常会一层层的向上传递。2.3 抛出异常throw1异常抛出过程先创建异常对象然后使用throw抛出。因此异常的抛出格式就是如下所示throw new 异常类名(参数);2抛出异常时机比如当我们检查传入参数的合法性时如果不合法我们就可以抛出异常。2.4 异常的处理方法1方法一声明异常throwsthrows用于在方法声明中指出该方法可能抛出的异常类型并将处理这些异常的责任交给调用该方法的方法。对于上述例子的解释由于函数内部throw的那部分可能会发生编译时异常必须在代码中写好处理规则我们选择使用throws的方式进行处理因此就要使用throws方法把异常声明好意思就是方法内部不处理这些异常了调用者去处理吧。throws指明当前函数可能向上抛出FileNotFoundException异常。2方法二通过try-catch处理使用格式例子如下由于read方法有可能出现编译时异常所以我们可以用try-catch进行处理。我们把可能出现异常的代码放在try里catch的里其实就是形参要捕获的异常类型 形参。在catch中获取到异常对象后可以使用以下方法来打印异常的信息处理多个种类的异常如果我们要处理多个种类的异常可以采用如下方法每类异常采用不同的处理方法。这种异常处理方式要求多个catch中的异常不能相同并且若catch中的多个异常之间有子父类异常的关系那么子类异常要求在上面的catch处理父类异常在下面的catch处理。3finally块在catch后面还可以使用finally格式如下finally的语法try...catch....finallyfinally中的代码通常无论是否发生异常都会执行除非在try或者catch中调用退出JVM的相关方法常用于释放资源。例子如下2.5 自定义异常没学后续有用的话看笔记再补三、File类3.1 概念File对象只是表示一个文件路径不代表这个文件真的存在。3.2 构造方法三种后两种构造方法等价于 new File(D:\\aaa\\bbb.txt);3.3 常用方法getPath()返回你创建 File 对象时写的路径getAbsolutePath()返回完整的绝对路径。public String getName()返回由此File表示的文件或目录的名称。public long length()返回由此File表示的文件的长度。注意绝对路径与相对路径绝对路径从盘符开始的路径这是一个完整的路径。相对路径相对于项目目录的路径这是一个便捷的路径开发中经常使用。这里的当前目录指的是untitle4这级目录而不是java文件所在的目录3.4 其他常用方法delete方法如果此File表示目录则目录必须为空才能删除。需要创建多级目录时用mkdirs四、IO流4.1 IO的分类根据数据的流向分为输入流和输出流。根据数据的类型分为字节流和字符流。close方法当完成流的操作时必须调用此方法释放系统资源。4.2 字节流4.2.1 字节输出流4.2.1.1 顶级父类 OutputStreamjava.io.OutputStream抽象类是表示字节输出流的所有类的父类。4.2.1.2 FileOutputStream类OutputStream子类之一1作用用于将数据写出到文件。2构造方法四种上述两种构造方法当传入的文件不存在时会自动创建好。如果文件本就有数据会自动清空。public FileOutputStream(File file, boolean append)创建文件输出流以写入由指定的 File 对象表示的文件。public FileOutputStream(String name, boolean append)创建文件输出流以指定的名称写入文件。这两个构造方法boolean 值true表示追加数据false表示清空原有数据。因此想在原有文件中追加信息可以用这两种构造方法。3写出数据 write 方法FileOutputStream 的所有write()重载最终写入文件的都是字节数据输出流本身不关心这些字节代表什么。读取时如何解释这些字节取决于读取方式和编码规则。write(int b)他会把 int 的低 8 位作为一个字节写入文件。write(byte[] b)这里的 getBytes() 会使用当前系统/JVM的默认字符集 将“黑马程序员”编码成对应的字节数组。write(byte[] b, int off, int len)每次写出从 off 索引开始len 个字节。例子中每个字符被对应到一个字节。换行的方式其实要换行时写入这个就可以了。不同系统要写入的不一样。4.2.2 字节输入流在java中存储字符规则如下Java 在表示字符数据时使用 UTF-16 编码单元。可以理解为字符先根据 Unicode 得到码点再按 UTF-16 规则表示最后存入char或String中。4.2.2.1 顶级父类java.io.InputStream抽象类是表示字节输入流的所有类的父类可以读取字节信息到内存中。常用方法4.2.2.2 FileInputStream类inputstream子类之一1两种构造方法当传入的路径不存在时会抛出FileNotFoundException2read方法read方法每次可以读取一个字节的数据提升为int类型读取到文件末尾返回-1例子如下FileInputStream.read()读取一个字节后会将这个字节按照无符号方式提升为int返回即前面补24个0。如果强转c为har取低16位。read(byte[] b)read方法返回读取到的字节数当一个字节也没读到时返回-1更推荐这个方法例子如下由于文件中只有5个字节前两次读两个字节装满字节数组此时第三此读取时就是只读到了一个字节返回值为1此时数组索引为1的地方依然放着上次独到的字节第四次没读到返回-1以后使用read方法就用这个固定格式就好字节数组b的0到len-1是读出的字节4.2.3 图片复制练习其实就是把一个文件字节都读出来放进另一个文件流的关闭原则先开后关后开先关。4.3 常见字符集4.3.1 ascii字符集由来美国人给自己的文字进行编号特征128个字符存储方式把数字转为2进制再补0由于范围再0-1277位二进制就能表示不足一个字节因此在ascii中使用一个字节来存储数据4.3.2 gbk1GBK兼容ASCIIASCII字符在GBK中最终存储的二进制也完全相同。2gbk中汉字按两个字节来存储3区分汉字和英文的方式汉字的编码第一位是1而英文是04查GBK码表直接获得的是要存储的字节byte这些字节转换成二进制后就是实际存储的数据4.3.3 unicode万国码规定了文字到数值的映射1编码方案utf-8作用将unicode中的码点转化为二进制英文和数字占一个字节汉字占三个字节编码规则如下ASCII中存在的字符数字、英文、符号存储结果与ASCII一致。汉字就是先把数值转为二进制数值然后填空到上述格式中4.3.4 使用java程序进行编码与解码编码其实就是按照指定的规则把字符转换为二进制再一个字节一个字节的存进字节数组打印字节数组时会按照补码来解释解码其实就是再把字节数组里的二进制们给翻译回字符4.4 字符流有中文时用这个4.4.1 字符输入流4.4.1.1 顶级父类java.io.Reader抽象类是表示用于读取字符流的所有类的超类父类。4.4.1.2 FileReader类Reader的实现类之一FileReader默认使用JVM的默认字符集进行解码通常与操作系统默认编码一致例如简体中文Windows通常为GBK。1构造方法两种2read方法两种read()每次可以读取一个字符的数据提升为int类型读取到文件末尾返回-1。读取数据过程以我为例假设文件采用 UTF-8 编码并且FileReader也按 UTF-8 解码字符“我”在文件中对应三个字节E6 88 91。解码后得到 Unicode 码点U6211它在 UTF-16 中由一个代码单元0x6211表示所以read()返回int 25105强转为char后得到“我”。read(char[] cbuf)每次读取b的长度个字符到数组中返回读取到的有效字符个数读取到末尾时返回-1。其实不严谨这句话4.4.2 字符输出流4.4.2.1 顶级父类Writerjava.io.Writer抽象类是表示用于写出字符流的所有类的超类。4.4.2.2 FileWriter类1构造方法两种2write方法write(int)write(97)→ 取低 16 位得到 char 0x0061即 a→ 按 FileWriter 使用的字符集编码→ 写出字节FileWriter写入后如果不关闭流也不调用flush()数据可能仍然停留在缓冲区中不能保证已经写入文件FileOutputStream没有Java层面的写缓冲区write后通常会直接交给操作系统处理。write(char[] cbuf)和write(char[] cbuf, int off, int len)注意方法形参len指的是字符数图中所说的字节有问题。例子如下文件中存储的其实就是对应的utf-8编码。写出字符串write(String str)和write(String str, int off, int len)换行文本编辑器读取文件时识别到这组特殊字节就会把后续内容换到下一行显示。3关闭和刷新flush()的作用把缓冲区的信息写到文件流对象依然能使用。close()的作用把缓冲区的信息写到文件流对象不能使用。即便是flush()方法写出了数据操作的最后还是要调用close()方法释放系统资源。4.4.3 字符流的底层原理4.4.3.1 字符输入流在创建字符输入流对象时还会创建一个长度为8192的字节数组缓冲区字节流没有缓冲区在read时首先会看缓冲区中有没有数据没有的话就去文件读取尽可能装满缓冲区然后再从缓冲区读数据有的话直接在缓冲区读如果缓冲区没数据文件也没数据则证明读完了返回-14.4.3.2 字符输出流创建字符输出流对象时依然会创建长度为8192字节的缓冲区字节流没有缓冲区直接写到文件而字符流有缓冲区先写到缓冲区缓冲区数据写入文件的时机装满了或flush或close4.5 IO 流的异常处理方式演进直接套模板就行4.5.1 jdk7之前4.5.2 jdk7新增执行完try代码块try后面里的资源会自动关闭4.5.3 jdk9新增用到再学4.6 缓冲流4.6.1 概述4.6.2 字节缓冲流缓冲流中真正干活的还是基本流比如写文件和读文件只不过效率更高缓冲流为基本流新增了大小为8192大小的字节数组的缓冲区使用缓冲流拷贝文件示例释放资源时不用管基本流直接关缓冲流他的close方法中本就会关闭基本流拷贝时的底层原理基本流会从文件读取数据把数据放到缓冲区尽可能读满调用read和write时其实就是把数据从第一个缓冲区放到第二个缓冲区第二个缓冲区满后基本流会自动写出数据到文件。由于这两个缓冲区都是在内存的数据传输很快也就提高了效率4.6.3 字符缓冲流依然是为基本流提供了8192大小的字符数组的缓冲区但是效果不明显因为字符流本就自带缓冲区2特有方法readline每次读取一行也就是遇到\n\r时停止newline方法其实就是针对操作系统写入该操作系统对应的换行符依然是只用关闭缓冲流即可4.7 转换流二者搭配可以实现字符集的转换4.7.1 InputStreamReader类reader的子类1两种构造方法这里的gbk指的是解码的方式不指定则采用默认方式例子如下注意这里的read方法不再是fileinputstream那种读一个字节再补0FileInputStream负责读原始字节InputStreamReader根据编码规则凑够表示一个字符所需的字节后再解码成字符。文件中的字节↓按 GBK / UTF-8 解码↓得到字符“你”↓Java 用 UTF-16 编码单元表示它↓数值 0x4F6020320↓read() 用 int 返回4.7.2 OutputStreamWriter类writer的子类两种构造方法Java 中的 String / char↓OutputStreamWriter↓ 按指定字符集编码字节数据↓FileOutputStream↓文件4.8 序列化4.8.1 ObjectOutputStream类1作用2构造方法3序列化的前提该类必须实现java.io.Serializable接口Serializable是一个标记接口如果一个对象整体要被序列化但其中某个实例属性不想被序列化就可以用transient修饰这个属性。当一个类中有不能被序列化的属性时比如另一个自定义类address可以让他去实现serializable或者用transient来修饰该属性具体演示如下fileoutputstream与objectoutputstream的配合方式Employee 对象↓ObjectOutputStream对象序列化成字节数据↓FileOutputStream把字节写入 employee.txt注意transient表示该属性不参与序列化因此该值不会被保存反序列化时age的值为0int的默认值而不是当前的20序列化主要保存的是对象的状态也就是成员变量的数据。方法代码属于类本身反序列化时 JVM 还是根据已经存在的Employee.class来知道它有哪些方法。4.8.2 ObjectInputStream类1构造方法2反序列化 readObjectemployee.txt↓FileInputStream读取文件中的字节↓ObjectInputStream按照 Java 序列化格式解析这些字节恢复出 Java 对象这里的readObject返回值类型为Object所以要自己强转3细节(a) 对于 serialVersionUID即使不在类中自己定义java也会默认计算出一个对象序列化时会记录当前的serialVersionUID。反序列化时会检查当前的serialVersionUID是不是和当时记录的一样如果不一样则报错由此可以实现下面操作在类中新添属性后只要serialVersionUID不变依然可以进行反序列化新添加的属性值为默认值(b) 出现下面三种情况时反序列化会报错该类的序列版本号与从流中读取的类描述符的版本号不匹配该类包含未知数据类型对于Serializable类它的第一个没有实现Serializable的父类需要有一个可访问的无参构造方法。(c)JVM 反序列化时必须能够找到这个对象对应类的 class 文件。原因如下4.9 打印流我们平时使用的print和println就是PrintStream类的PrintStream ps new PrintStream(ps.txt);ps.println(97);上述代码其实就是向文件写97System.out本就是个printstream对象只不过这个PrintStream默认的输出位置是控制台。System.out.println(hello);因此这是向控制台写信息System.setOut()的作用就是重新设置System.out所引用的PrintStream对象。4.10 压缩流与解压缩流没学有用的话后面补
返回列表