IO流
IO流的概念------------------------------
IO就是Input和Output的简写,也就是输入和输出的含义,也可以说读和写
IO流就是指读写数据时像流水一样从一端流到另外一端,因此得名为"流"
基本分类------------------------------
按照读写数据的基本单位不同,分为 字节流 和 字符流
其中字节流主要指以字节为单位进行数据读写的流,可以读写任意类型的文件,因为只要是字节形式的都可以接受
其中字符流主要指以字符(2个字节)为单位进行数据读写的流,只能读写文本文件,因为只接受字符
即有文字,没图片或者音频之类的
汉字一般占两个字节,即用字节流的话,一次只能读出来半个汉字,翻译时,若翻译不明白,就会乱码
即在读取有汉字或者其他特殊的这样的文件时,就用字符流,这样就可以读取完整的汉字或其他的内容,这就是字符流的意义
按照读写数据的方向不同,分为 输入流 和 输出流(站在程序的角度)
其中输入流主要指从文件中读取数据内容输入到程序中,也就是读文件
其中输出流主要指将程序中的数据内容输出到文件中,也就是写文件,所以我才说是站在程序的角度(不是文件的角度)
按照流的角色不同分为节点流和处理流
其中节点流主要指直接和输入输出源对接的流(看是否与文件直接关联)
其中处理流主要指需要建立在节点流的基础之上的流,如缓冲流
体系结构------------------------------
FileWriter类------------------------------
java.io.FileWriter类主要用于将文本内容写入到文本文件
public class FileWriter
extends OutputStreamWriter
public class OutputStreamWriter
extends Writer
public abstract class Writer
extends Object
implements Appendable , Closeable , Flushable
常用的方法------------------------------
FileWriter ( String fileName) ,根据参数指定的文件名构造对象
FileWriter ( String fileName, boolean append) ,以追加的方式根据参数指定的文件名来构造对象
void write ( int c) ,写入单个字符
void write ( char [ ] cbuf, int off, int len) ,将指定字符数组中从偏移量off开始的len个字符写入此文件输出流
void write ( char [ ] cbuf) ,将cbuf. length个字符从指定字符数组写入此文件输出流中
void flush ( ) ,刷新流
void close ( ) ,关闭流对象并释放有关的资源,自带刷新功能
package com. lagou. task17 ;
import java. io. FileWriter ;
import java. io. IOException ;
public class FileWriterTest {
public static void main ( String [ ] args) {
FileWriter fw = null ;
try {
fw = new FileWriter ( "d:/ggg/a.txt" , true ) ;
fw. write ( 'a' ) ;
char [ ] cArr = new char [ ] { 'h' , 'e' , 'l' , 'l' , 'o' } ;
fw. write ( cArr, 1 , 3 ) ;
fw. write ( cArr) ;
fw. flush ( ) ;
System . out. println ( "写入数据成功!" ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
} finally {
if ( null != fw) {
try {
fw. close ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
}
}
}
FileReader类------------------------------
java.io.FileReader类主要用于从文本文件读取文本数据内容
public class FileReader
extends InputStreamReader
public class InputStreamReader
extends Reader
public abstract class Reader
extends Object
implements Readable , Closeable
常用的方法------------------------------
FileReader ( String fileName) ,根据参数指定的文件名构造对象
int read ( ) ,读取单个字符的数据并返回,返回- 1 表示读取到末尾
int read ( char [ ] cbuf, int offset, int length) ,从输入流中将最多len个字符的数据读入一个字符数组中
返回读取到的字符个数,返回- 1 表示读取到末尾,offset是数组的下标,依次往下放
int read ( char [ ] cbuf) ,从此输入流中将最多 cbuf. length 个字符的数据读入字符数组中
返回读取到的字符个数,返回- 1 表示读取到末尾
void close ( ) ,关闭流对象并释放有关的资源
package com. lagou. task17 ;
import java. io. FileReader ;
import java. io. FileWriter ;
import java. io. IOException ;
public class FileCharCopyTest {
public static void main ( String [ ] args) {
FileReader fr = null ;
FileWriter fw = null ;
try {
fr = new FileReader ( "d:/a.txt" ) ;
fw = new FileWriter ( "d:/b.txt" ) ;
System . out. println ( "正在玩命地拷贝..." ) ;
int res = 0 ;
while ( ( res = fr. read ( ) ) != - 1 ) {
fw. write ( res) ;
}
System . out. println ( "拷贝文件成功!" ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
} finally {
if ( null != fw) {
try {
fw. close ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
if ( null != fr) {
try {
fr. close ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
}
}
}
package com. lagou. task17 ;
import java. io. FileReader ;
import java. io. IOException ;
public class FileReaderTest {
public static void main ( String [ ] args) {
FileReader fr = null ;
try {
fr = new FileReader ( "d:/b.txt" ) ;
int res = 0 ;
while ( ( res = fr. read ( ) ) != - 1 ) {
System . out. println ( "读取到的单个字符是:" + ( char ) res + ",对应的编号是:" + res) ;
}
} catch ( IOException e) {
e. printStackTrace ( ) ;
} finally {
if ( null != fr) {
try {
fr. close ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
}
}
}
FileOutputStream类------------------------------
java.io.FileOutputStream类主要用于将图像数据之类的原始字节流写入到输出流中
public class FileOutputStream
extends OutputStream
public abstract class OutputStream
extends Object
implements Closeable , Flushable
常用的方法------------------------------
FileOutputStream ( String name) ,根据参数指定的文件名来构造对象
FileOutputStream ( String name, boolean append) ,以追加的方式根据参数指定的文件名来构造对象
void write ( int b) ,将指定字节写入此文件输出流
void write ( byte [ ] b, int off, int len) ,将指定字节数组中从偏移量off开始的len个字节写入此文件输出流
void write ( byte [ ] b) ,将 b. length 个字节从指定字节数组写入此文件输出流中
void flush ( ) ,刷新此输出流并强制写出任何缓冲的输出字节,注意是缓冲的,一般来说对于缓冲流来说需要他,否则需要封顶才会自动输出,或者操作关闭也行,那么也会操作写出
void close ( ) ,关闭流对象并释放有关的资源
FileInputStream类------------------------------
java.io.FileInputStream类主要用于从输入流中以字节流的方式读取图像数据等
public class FileInputStream
extends InputStream
public abstract class InputStream
extends Object
implements Closeable
常用的方法------------------------------
FileInputStream ( String name) ,根据参数指定的文件路径名来构造对象
int read ( ) ,从输入流中读取单个字节的数据并返回,返回- 1 表示读取到末尾
int read ( byte [ ] b, intoff, int len) ,从此输入流中将最多len个字节的数据读入字节数组中
返回读取到的字节个数(如果在读取过程中到顶端,结束读取,返回读取的字节数,下次就是- 1 了),返回- 1 表示读取到末尾
int read ( byte [ ] b) ,从此输入流中将最多 b. length 个字节的数据读入字节数组中
返回读取到的字节个数,返回- 1 表示读取到末尾
void close ( ) ,关闭流对象并释放有关的资源
int available ( ) ,获取输入流所关联文件的大小
案例题目------------------------------
编程实现两个文件之间的拷贝功能
package com. lagou. task17 ;
import java. io. FileInputStream ;
import java. io. FileOutputStream ;
import java. io. IOException ;
public class FileByteCopyTest {
public static void main ( String [ ] args) {
long g1 = System . currentTimeMillis ( ) ;
FileInputStream fis = null ;
FileOutputStream fos = null ;
try {
fis = new FileInputStream ( "d:/03 IO流的框架图.png" ) ;
fos = new FileOutputStream ( "d:/IO流的框架图.png" ) ;
System . out. println ( "正在玩命地拷贝..." ) ;
byte [ ] bArr = new byte [ 1024 ] ;
int res = 0 ;
while ( ( res = fis. read ( bArr) ) != - 1 ) {
fos. write ( bArr, 0 , res) ;
}
System . out. println ( "拷贝文件成功!" ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
} finally {
if ( null != fos) {
try {
fos. close ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
if ( null != fis) {
try {
fis. close ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
}
long g2 = System . currentTimeMillis ( ) ;
System . out. println ( "使用文件流拷贝视频文件消耗的时间为:" + ( g2- g1) ) ;
}
}
接下来是非常重要的一点:
乱码:
乱码数据是当数据在显示设备(如计算机屏幕、手机屏幕、打印机等)上无法被正确解析或显示时,显示设备可能会用一些特殊的符号或图案来代替未能正确显示的字符,也就是说,乱码是系统自身存在的,并且可能通过某种运行指定到一个乱码
其他乱码:溢出问题,比如-1等等
要理解字节流和字符流的区别,只需要注意byte即可,一般情况下,字节流是将数据直接保存为byte的,并且按照java的系统编码,通常可能是ascii,而字符流则是根据字符的编码来进行
比如字节流写入一个字节,都会将数据变成byte后进行处理,这个时候可能由于编码不同,导致出现乱码数据,但是大多数情况下只是其他乱码,也就是说可能会识别成-1,一般当识别成-1时,就会终止写入,所以会造成文件变小
也有可能会识别成多个字节,那么就会造成拷贝时,可能会使得文件变大
当然,既然是乱码问题,那么字符流自然也是如此,也由于这样,导致反过来必然不会对应原来的,因为他本来就不能回到,因为是不可识别的,所以在这种情况下,就算你使用相同编码编码回去,可能也会操作文件大小的改变
即还是看你编码的方式,即可以说:字符流 = 字节流 + 编码方式(若是字节的默认编码表,读取的有些东西可能识别不了(一般指汉字咯),就会找相似,一般存在隐藏的-1)
这里的编码方式:JVM识别,可以对字符流的编码进行改变,在转换流后面可以指定编码
字节是程序最小单位(虽然有bit存在),虽然存在编码方式,但是这些并不能提供安全,因为某个黑客拿取你的数据,是可以测试出来的,所以需要进行加密,通常情况下我们可以使用自定义编码来操作,即部分操作编码,意思是:假设我有10个数据,前3个数据操作一种编码,后6个数据操作一种编码,由于数据是我们可以加上的,那么可以在服务器中加上数据时进行一个一个的加,那么在读取时可以选择反过来(写入使用字节,将字符变成字节存储,读取时读取字节,将字节变成字符显示,这个变成字节和字符的过程是我们自定义的,即手动将那几个部分进行处理),而黑客并不知道里面是如何反过来操作,只能统一进行处理,那么你要想反过来是基本不可能的,就算一个个的测试也不行,这比弱口令还要困难,所以大多数应用(包括网站或者APP等等)可能是操作自定义的编码的,当然,这还只是一层,可以选择再次进行同样的处理,并且还可以加密信息进行加盐等等,这个时候是非常难以破解,或者几乎不可能破解(之所以是几乎,因为内部人员或者代码是可以的,要不然数据是怎么给你的,源代码的处理不就是一种破解方式吗,只要有,那么就没有不可能破解的情况)
当然,无论是什么编码问题,只需要解码和编码一致即可,一开始的编码格式或者显示(统一编码作为称呼的)由当前写上的数(或者说文本)和当前编码影响的,也更加证明编码一致即可
通过上面的说明,那么我们也就可以知道,像图像什么的其实可以使用字符来处理,只需要编码方式完全参照系统编码,而不是通过对应的编码方式来变成字节,即只需要解码和编码一致即可
BufferedOutputStream类------------------------------
java.io.BufferedOutputStream类主要用于描述缓冲输出流,此时不用为写入的每个字节调用底层系统
public class BufferedOutputStream
extends FilterOutputStream
public class FilterOutputStream
extends OutputStream
public abstract class OutputStream
extends Object
implements Closeable , Flushable
常用的方法------------------------------
BufferedOutputStream ( OutputStream out) ,根据参数指定的引用来构造对象
BufferedOutputStream ( OutputStream out, int size) ,根据参数指定的引用和缓冲区大小来构造对象
void write ( int b) 写入单个字节
void write ( byte [ ] b, int off, int len) ,写入字节数组中的一部分数据
void write ( byte [ ] b) ,写入参数指定的整个字节数组
void flush ( ) ,刷新流
void close ( ) ,关闭流对象并释放有关的资源
BufferedInputStream类------------------------------
java.io.BufferedInputStream类主要用于描述缓冲输入流
public class BufferedInputStream
extends FilterInputStream
public class FilterInputStream
extends InputStream
public abstract class InputStream
extends Object
implements Closeable
常用的方法------------------------------
BufferedInputStream ( InputStream in) ,根据参数指定的引用构造对象
BufferedInputStream ( InputStream in, int size) ,根据参数指定的引用和缓冲区大小构造对象
int read ( ) ,读取单个字节
int read ( byte [ ] b, int off, int len) ,读取len个字节
int read ( byte [ ] b) ,读取b. length个字节
void close ( ) ,关闭流对象并释放有关的资源
package com. lagou. task17 ;
import java. io. * ;
public class BufferedByteCopyTest {
public static void main ( String [ ] args) {
long g1 = System . currentTimeMillis ( ) ;
BufferedInputStream bis = null ;
BufferedOutputStream bos = null ;
try {
bis = new BufferedInputStream ( new FileInputStream ( "d:/03 IO流的框架图.png" ) ) ;
bos = new BufferedOutputStream ( new FileOutputStream ( "d:/IO流的框架结构.png" ) ) ;
System . out. println ( "正在玩命地拷贝..." ) ;
byte [ ] bArr = new byte [ 1024 ] ;
int res = 0 ;
while ( ( res = bis. read ( bArr) ) != - 1 ) {
bos. write ( bArr, 0 , res) ;
}
System . out. println ( "拷贝文件成功!" ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
} finally {
if ( null != bos) {
try {
bos. close ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
if ( null != bis) {
try {
bis. close ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
}
long g2 = System . currentTimeMillis ( ) ;
System . out. println ( "使用缓冲区拷贝视频文件消耗的时间为:" + ( g2- g1) ) ;
}
}
实际上不用缓冲流要比用缓冲流慢,即我们最好用缓冲流
一般对应的FileWriter是操作字符的,所以一般会进行编码,即将字节变成字符,这也是为什么在前面说,字符流=字节流 + 编码方式,所以,这里(代表操作缓冲的)也操作了编码,主要的区别就是,FileWriter是写入缓冲一次编码一次(字节先变字符),而这里是缓冲区满了才会进行(他们是存放字节的哦,因为是字节缓冲流,而不是字符哦,也就是8192的字节数组大小)写入,这就是主要的优势,即速度快的原因(字节处理一般都比较快,因为不需要处理编码),具体可以看这个博客:https://blog.csdn.net/ninewolfyan/article/details/112882614,由于字节流(不是缓冲流)自身不需要编码,所以他本身不会操作缓冲区(机制)
简单来说,他之所以快,就是因为他是改变原有缓冲的规则的
BufferedWriter类------------------------------
java.io.BufferedWriter类主要用于写入单个字符、字符数组以及字符串到输出流中
public class BufferedWriter
extends Writer
public abstract class Writer
extends Object
implements Appendable , Closeable , Flushable
常用的方法------------------------------
BufferedWriter ( Writer out) ,根据参数指定的引用来构造对象
BufferedWriter ( Writer out, int sz) ,根据参数指定的引用和缓冲区大小来构造对象
void write ( int c) ,写入单个字符到输出流中
void write ( char [ ] cbuf, int off, intlen) ,将字符数组cbuf中从下标off开始的len个字符写入输出流中
void write ( char [ ] cbuf) ,将字符串数组cbuf中所有内容写入输出流中
void write ( String s, int off, int len) ,将参数s中下标从off开始的len个字符写入输出流中
void write ( String str) ,将参数指定的字符串内容写入输出流中
void newLine ( ) ,用于写入行分隔符到输出流中,可以到下一行
void flush ( ) ,刷新流
void close ( ) ,关闭流对象并释放有关的资源
BufferedReader类------------------------------
java.io.BufferedReader类用于从输入流中读取单个字符、字符数组以及字符串
public class BufferedReader
extends Reader
public abstract class Reader
extends Object
implements Readable , Closeable
常用的方法------------------------------
BufferedReader ( Reader in) ,根据参数指定的引用来构造对象
BufferedReader ( Reader in, int sz) ,根据参数指定的引用和缓冲区大小来构造对象
int read ( ) ,从输入流读取单个字符,读取到末尾则返回- 1 ,否则返回实际读取到的字符内容
int read ( char [ ] cbuf, intoff, int len) ,从输入流中读取len个字符放入数组cbuf中下标从off开始的位置上
若读取到末尾则返回- 1 ,否则返回实际读取到的字符个数
int read ( char [ ] cbuf) ,从输入流中读满整个数组cbuf
String readLine ( ) ,读取一行字符串并返回,返回null 表示读取到末尾,读取一行字符串,下次一般到下一行
void close ( ) ,关闭流对象并释放有关的资源
package com. lagou. task17 ;
import java. io. * ;
public class BufferedCharCopyTest {
public static void main ( String [ ] args) {
BufferedReader br = null ;
BufferedWriter bw = null ;
try {
br = new BufferedReader ( new FileReader ( "d:/a.txt" ) ) ;
bw = new BufferedWriter ( new FileWriter ( "d:/b.txt" ) ) ;
System . out. println ( "正在玩命地拷贝..." ) ;
String str = null ;
while ( ( str = br. readLine ( ) ) != null ) {
bw. write ( str) ;
bw. newLine ( ) ;
}
System . out. println ( "拷贝文件成功!" ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
} finally {
if ( null != bw) {
try {
bw. close ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
if ( null != br) {
try {
br. close ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
}
}
}
\n和\r的区别,前者\n可以理解为换行,后者,就将回到当前的开头,若在回到开头之前,有其他数,那么就会清除
案例------------------------------
System . out. print ( "啊\n和" ) ;
System . out. print ( "奖金\r哦" ) ;
System . out. print ( "1111111\t" ) ;
System . out. println ( "11111111\t" ) ;
1111111 11111111
System . out. print ( "11111111\t" ) ;
System . out. println ( "11111111\t" ) ;
PrintStream类------------------------------
java.io.PrintStream类主要用于更加方便地打印各种数据内容
public class PrintStream
extends FilterOutputStream
implements Appendable , Closeable
public class FilterOutputStream
extends OutputStream
public abstract class OutputStream
extends Object
implements Closeable , Flushable
常用的方法------------------------------
PrintStream ( OutputStream out) ,根据参数指定的引用来构造对象
void print ( String s) ,用于将参数指定的字符串内容打印出来
void println ( String x) ,用于打印字符串后并终止该行
void flush ( ) ,刷新流
void close ( ) ,用于关闭输出流并释放有关的资源
PrintWriter类------------------------------
java.io.PrintWriter类主要用于将对象的格式化形式打印到文本输出流
public class PrintWriter
extends Writer
public abstract class Writer
extends Object
implements Appendable , Closeable , Flushable
常用的方法------------------------------
PrintWriter ( Writer out) ,根据参数指定的引用来构造对象
void print ( String s) ,将参数指定的字符串内容打印出来
void println ( String x) ,打印字符串后并终止该行
void flush ( ) ,刷新流
void close ( ) ,关闭流对象并释放有关的资源
案例题目------------------------------
不断地提示用户输入要发送的内容,若发送的内容是"bye"则聊天结束,否则将用户输入的内容写入到文件d:/a.txt中
要求使用BufferedReader类来读取键盘的输入
System.in代表键盘输入,前提是一般需要结合(具体原因,可能是jvm启动操作了初始化并且需要结合操作,因为InputStream inputStream = System.in;并不会阻塞),比如这里,还有他自身操作read方法(这里了解即可)
要求使用PrintStream类负责将数据写入文件
package com. lagou. task17 ;
import java. io. * ;
import java. text. SimpleDateFormat ;
import java. util. Date ;
public class PrintStreamChatTest {
public static void main ( String [ ] args) {
BufferedReader br = null ;
PrintStream ps = null ;
try {
br = new BufferedReader ( new InputStreamReader ( System . in) ) ;
ps = new PrintStream ( new FileOutputStream ( "d:/a.txt" , true ) ) ;
boolean flag = true ;
while ( true ) {
System . out. println ( "请" + ( flag? "张三" : "李四" ) + "输入要发送的聊天内容:" ) ;
String str = br. readLine ( ) ;
if ( "bye" . equals ( str) ) {
System . out. println ( "聊天结束!" ) ;
break ;
}
Date d1 = new Date ( ) ;
SimpleDateFormat sdf = new SimpleDateFormat ( "yyyy-MM-dd HH:mm:ss" ) ;
ps. println ( sdf. format ( d1) + ( flag? " 张三说:" : " 李四说:" ) + str) ;
flag = ! flag;
}
ps. println ( ) ;
ps. println ( ) ;
ps. println ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
} finally {
if ( null != ps) {
ps. close ( ) ;
}
if ( null != br) {
try {
br. close ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
}
}
}
OutputStreamWriter类------------------------------
java.io.OutputStreamWriter类主要用于实现从字符流到字节流的转换,对于这样的转换,简单来说,就是将数据必然的通过对应编码过去
public class OutputStreamWriter
extends Writer
public abstract class Writer
extends Object
implements Appendable , Closeable , Flushable
常用的方法------------------------------
OutputStreamWriter ( OutputStream out) ,根据参数指定的引用来构造对象
OutputStreamWriter ( OutputStream out, StringcharsetName ) ,根据参数指定的引用和编码构造对象
void write ( String str) ,将参数指定的字符串写入
void flush ( ) ,刷新流
void close ( ) ,用于关闭输出流并释放有关的资源
InputStreamReader类------------------------------
java.io.InputStreamReader类主要用于实现从字节流到字符流的转换
public class InputStreamReader
extends Reader
public abstract class Reader
extends Object
implements Readable , Closeable
常用的方法------------------------------
InputStreamReader ( InputStream in) ,根据参数指定的引用来构造对象
InputStreamReader ( InputStream in, StringcharsetName ) ,根据参数指定的引用和编码来构造对象
int read ( char [ ] cbuf) ,读取字符数据到参数指定的数组
void close ( ) ,用于关闭输出流并释放有关的资源
字符编码------------------------------
计算机只能识别二进制数据,早期就是电信号
为了方便计算机可以识别各个国家的文字,就需要将各个国家的文字采用数字编号的方式进行描述并建立对应的关系表
该表就叫做编码表
常见的编码表------------------------------
ASCII:美国标准信息交换码, 使用一个字节的低7位二位进制进行表示
ISO8859-1:拉丁码表,欧洲码表,使用一个字节的8位二进制进行表示
GB2312:中国的中文编码表,最多使用两个字节16位二进制为进行表示
GBK:中国的中文编码表升级,融合了更多的中文文字符号,最多使用两个字节16位二进制位表示
Unicode:国际标准码,融合了目前人类使用的所有字符,为每个字符分配唯一的字符码
所有的文字都用两个字节16位二进制位来表示
编码的发展------------------------------
面向传输的众多 UTF(UCS Transfer Format)标准出现了,UTF-8就是每次8个位传输数据,而UTF-16就是每次16个位
这是为传输而设计的编码并使编码无国界,这样就可以显示全世界上所有文化的字符了
Unicode只是定义了一个庞大的、全球通用的字符集,并为每个字符规定了唯一确定的编号
具体存储成什么样的字节流,取决于字符编码方案
推荐的Unicode编码是UTF-8和UTF-16
UTF-8:变长的编码方式,可用1-4个字节来表示一个字符
DataOutputStream类------------------------------
java.io.DataOutputStream类主要用于以适当的方式将基本数据类型写入输出流中
public class DataOutputStream
extends FilterOutputStream
implements DataOutput
public class FilterOutputStream
extends OutputStream
public abstract class OutputStream
extends Object
implements Closeable , Flushable
常用的方法------------------------------
DataOutputStream ( OutputStream out) ,根据参数指定的引用构造对象
OutputStream 类是个抽象类,实参需要传递子类对象
void writeInt ( int v) ,用于将参数指定的整数一次性写入输出流,优先写入高字节
void close ( ) ,用于关闭文件输出流并释放有关的资源
package com. lagou. task17 ;
import java. io. DataOutputStream ;
import java. io. FileOutputStream ;
import java. io. IOException ;
public class DataOutputStreamTest {
public static void main ( String [ ] args) {
DataOutputStream dos = null ;
try {
dos = new DataOutputStream ( new FileOutputStream ( "d:/a.txt" ) ) ;
int num = 66 ;
dos. write ( num) ;
System . out. println ( "写入数据成功!" ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
} finally {
if ( null != dos) {
try {
dos. close ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
}
}
}
DataInputStream类------------------------------
java.io.DataInputStream类主要用于从输入流中读取基本数据类型的数据
public class DataInputStream
extends FilterInputStream
implements DataInput
public class FilterInputStream
extends InputStream
public abstract class InputStream
extends Object
implements Closeable
常用的方法------------------------------
DataInputStream ( InputStreamin ) ,根据参数指定的引用来构造对象
InputStream 类是抽象类,实参需要传递子类对象
int readInt ( ) ,用于从输入流中一次性读取一个整数数据并返回
void close ( ) ,用于关闭文件输出流并释放有关的资源
package com. lagou. task17 ;
import java. io. DataInputStream ;
import java. io. FileInputStream ;
import java. io. IOException ;
public class DataInputStreamTest {
public static void main ( String [ ] args) {
DataInputStream dis = null ;
try {
dis = new DataInputStream ( new FileInputStream ( "d:/a.txt" ) ) ;
int res = dis. read ( ) ;
System . out. println ( "读取到的整数数据是:" + res) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
} finally {
if ( null != dis) {
try {
dis. close ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
}
}
}
上述66会变成B,是因为文件给我们看的不是二进制代码,而是编码表的图案,而图案需要二进制转化为对应编号,即66
所以66 => ‘B’,这个B是给我们看的,即我们在读取时,也是将B对应的二进制,即66的二进制读取出来
然后,在变成对应的内容,即读出66对应的二进制,当然,在打印时,还是会将该二进制变为66,给你看的
然后将该66的整数类型变成字符串,通过字符串,将’‘66’'变成两个’6’一个一个的打出(若有6666,总不能保存6666吧,这也是为什么有些看起来连接的,实际上是操作了多次,虽然这个66不是),而不是找66对应的字符,即打出两个6,即66
即我们看的基本都是图案
ObjectOutputStream类------------------------------
public class ObjectOutputStream
extends OutputStream
implements ObjectOutput , ObjectStreamConstants
public abstract class OutputStream
extends Object
implements Closeable , Flushable
java.io.ObjectOutputStream类主要用于将一个对象的所有内容整体写入到输出流中
只能将支持 java.io.Serializable 接口的对象写入流中
类通过实现 java.io.Serializable 接口以启用其序列化功能
所谓序列化主要指将一个对象需要存储的相关信息有效组织成字节序列的转化过程
如一个图片里面有多个信息,如颜色,长度等,那么就将他们有规则的组织
常用的方法------------------------------
ObjectOutputStream ( OutputStream out) ,根据参数指定的引用来构造对象
void writeObject ( Object obj) ,用于将参数指定的对象整体写入到输出流中
void close ( ) ,用于关闭输出流并释放有关的资源
ObjectInputStream类------------------------------
public class ObjectInputStream
extends InputStream
implements ObjectInput , ObjectStreamConstants
public abstract class InputStream
extends Object
implements Closeable
java.io.ObjectInputStream类主要用于从输入流中一次性将对象整体读取出来
所谓反序列化主要指将有效组织的字节序列恢复为一个对象及相关信息的转化过程
常用的方法------------------------------
ObjectInputStream ( InputStreamin ) ,根据参数指定的引用来构造对象
Object readObject ( ) ,主要用于从输入流中读取一个对象并返回 无法通过返回值来判断是否读取到文件的末尾
void close ( ) ,用于关闭输入流并释放有关的资源
package com. lagou. task17 ;
public class User implements java. io. Serializable {
private static final long serialVersionUID = - 5814716593800822421L ;
private String userName;
private String password;
private transient String phoneNum;
public User ( ) {
}
public User ( String userName, String password, String phoneNum) {
this . userName = userName;
this . password = password;
this . phoneNum = phoneNum;
}
public String getUserName ( ) {
return userName;
}
public void setUserName ( String userName) {
this . userName = userName;
}
public String getPassword ( ) {
return password;
}
public void setPassword ( String password) {
this . password = password;
}
public String getPhoneNum ( ) {
return phoneNum;
}
public void setPhoneNum ( String phoneNum) {
this . phoneNum = phoneNum;
}
@Override
public String toString ( ) {
return "User{" +
"userName='" + userName + '\'' +
", password='" + password + '\'' +
", phoneNum='" + phoneNum + '\'' +
'}' ;
}
}
package com. lagou. task17 ;
import java. io. FileOutputStream ;
import java. io. IOException ;
import java. io. ObjectOutputStream ;
public class ObjectOutputStreamTest {
public static void main ( String [ ] args) {
ObjectOutputStream oos = null ;
try {
oos = new ObjectOutputStream ( new FileOutputStream ( "d:/a.txt" ) ) ;
User user = new User ( "qidian" , "123456" , "13511258688" ) ;
oos. writeObject ( user) ;
System . out. println ( "写入对象成功!" ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
} finally {
if ( null != oos) {
try {
oos. close ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
}
}
}
package com. lagou. task17 ;
import java. io. FileInputStream ;
import java. io. IOException ;
import java. io. ObjectInputStream ;
public class ObjectInputStreamTest {
public static void main ( String [ ] args) {
ObjectInputStream ois = null ;
try {
ois = new ObjectInputStream ( new FileInputStream ( "d:/a.txt" ) ) ;
Object obj = ois. readObject ( ) ;
System . out. println ( "读取到的对象是:" + obj) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
} catch ( ClassNotFoundException e) {
e. printStackTrace ( ) ;
} finally {
if ( null != ois) {
try {
ois. close ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
}
}
}
序列化版本号------------------------------
序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的
在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较
如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常(InvalidCastException)
当然,若反序列化出现的类(包括serialVersionUID)
与当前项目的相同的serialVersionUID对应的类的包名,或者类名不一致(相当于没有对应类),或者其没有实现java.io.Serializable接口(我删除,再操作得到会报错)
那么就算对应的serialVersionUID的值相同,也会报错
若有多个serialVersionUID,那么只要有一个符合了,就不会报错
最后也要注意,操作序列化时,需要实现java.io.Serializable接口,否则报错,若序列化不写,他会默认加上了,所以也不会报错哦(根据类信息加上,具体是什么,可以百度,且默认私有,所以基本得不到,并且也不能直接的使用,因为是类似于Class的保存的,一般明面上的得不到)
注意:只要两个项目能够相互访问,那么序列化和反序列化是可以在不同项目中实现的(无论是否远程),但是前提对应的对象在项目的路径是需要相同的(这里参考79章博客)
transient关键字------------------------------
transient是Java语言的关键字,用来表示一个域不是该对象串行化的一部分
当一个对象被串行化(序列化)的时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的,而没有包括进去的,得到的数据,默认为默认值,即给过默认值进去
经验的分享------------------------------
当希望将多个对象写入文件时,通常建议将多个对象放入一个集合中,因为在读取对象时
不知道读多少(因为没有返回值来返回具体数值),即由于文件里对象太多(有些是乱码的)
那么你判断不出有几个对象,即不知道循环多少次
Object readObject ( ) ,主要用于从输入流中读取一个对象并返回 无法通过返回值来判断是否读取到文件的末尾
然后将集合这个整体看做一个对象写入输出流中,此时只需要调用一次readObject方法就可以将整个集合的数据读取出来
从而避免了通过返回值进行是否达到文件末尾的判断,由于是数组,且对应的是常量,所以他们都是共享一个序列号的,即可以看成一个序列号的判断就行了,虽然他们可能都会判断或者判断一次
RandomAccessFile类------------------------------
public class RandomAccessFile
extends Object
implements DataOutput , DataInput , Closeable
java.io.RandomAccessFile类主要支持对随机访问文件的读写操作
常用的方法------------------------------
RandomAccessFile ( String name, Stringmode ) ,根据参数指定的名称和模式构造对象
r: 以只读方式打开
rw:打开以便读取和写入
rwd: 打开以便读取和写入,同步文件内容的更新
rws: 打开以便读取和写入,同步文件内容和元数据的更新
int read ( ) ,读取单个字节的数据
void seek ( long pos) ,用于设置从此文件的开头开始测量的文件指针偏移量
void write ( int b) ,将参数指定的单个字节写入
void close ( ) ,用于关闭流并释放有关的资源
package com. lagou. task17 ;
import java. io. IOException ;
import java. io. RandomAccessFile ;
public class RandomAccessFileTest {
public static void main ( String [ ] args) {
RandomAccessFile raf = null ;
try {
raf = new RandomAccessFile ( "d:/a.txt" , "rw" ) ;
raf. seek ( 3 ) ;
int res = raf. read ( ) ;
System . out. println ( "读取到的单个字符是:" + ( char ) res) ;
res = raf. read ( ) ;
System . out. println ( "读取到的单个字符是:" + ( char ) res) ;
raf. write ( '2' ) ;
System . out. println ( "写入数据成功!" ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
} finally {
if ( null != raf) {
try {
raf. close ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
}
}
}
对于偏移来说,可以看成下标,如albn
开始下标为0(当成1也可,但需要将1认为是最小的,但最好还是0,因为大多数下标是从零开始的),指向a,然后偏移2,指向b
当写入时,会覆盖所指向的数
也可以用光标来说,如"l",在文件里都有,可以将他的下一个当作要读的数,即开头指向a,然后偏移2,指向b
且光标在"l"和"b"中间,即光标的下一个就是要读取的数,当写入时,会覆盖下一个数
我们最好用第一个解释