- java内存中存的字符都是Unicode编码的,当读取磁盘中的文件的时候,默认通过GBK解码(记事本的ANSI编码,就是这种默认编码),但是磁盘/网络中有各种各样的的编码方式(比如:UTF-8,UTF-8是网页比较流行的一种格式,但是它无法通过默认的GBK解码,只能通过UTF-8进行解码),因此从java内存到磁盘/网络或者从磁盘/网络到java内存中会有一个转换过程,InputStreamReader和OutputStreamWriter可指定编码格式。
- https://blog.csdn.net/m0_37732829/article/details/80571166
- 将数据存到电脑中,最根本的格式是二进制的格式,但是当存入比如文档中或者别的文件中的时候,会有自己的一个编码格式,将二进制转换成自己的编码格式,当读入到java程序中的时候,java又将他们自己的编码格式变成Unicode编码格式。当从java程序读到别的文件中的时候,又将Unicode编码格式转换成其余的编码格式。
- 做题的时候不需要想太多,比如需要一个String类型的,就用Buffered流,需要读int类型的,就用Data流,需要读结构体类型的,就用结构体类型的流。
- 字符流的时候,write(String类型)一般文件中不会错
- 做题的时候,先想到底是读/写字节/字符进来,最终以什么形式,然后再想各种形式之间的转换问题。
- 想要什么类型,中间需要经过多少转换要想清楚
一、输入流和输出流的四种抽象基本类是InputStream、OutputStream、Reader、Writer
输入流和输出流是相对于java内存而言的
输入流(读到java内存中):InputStream、Reader
输出流(从java内存中读出):OutputStream、Writer
(一)InputStream
直接子类有:FileInputStream, ObjectInputStream
间接子类有:BufferedInputStream(FileInputStream),DataInputStream(FileInputStream)
以字节为单位,通过默认编码从磁盘/网络中读取文件到java内存中进行的操作
1、基本操作
read括号中没有或者是byte[]类型,返回值是int类型
(二)Reader
直接子类有:BufferedReader, InputStreamReader
间接子类有:FileReader(InputStreamReader)
以字符为单位从磁盘/网络中读取文件到java内存中进行的操作
1、基本操作
read括号中没有或者是char[]类型,返回值是int类型
(三)OutputStream
直接子类有:ObjectOutputStream、BufferedOutputStream
间接子类有:DataOutputStream, FileOutputStream
以字节为单位将java内存中的数据写到磁盘/网络中
1、基本操作
write括号中的是int或者byte[]类型,返回值是int类型,但是作用:将指定的字节写入该输出流中
(四)Writer
直接子类有:BufferedWriter, OutputStreamWriter
间接子类有:FileWriter, PrintWriter
以字符为单位将java内存中的数据写到磁盘/网络中
1、基本操作
write括号中的是int或者char[]或者String类型,返回值是void类型
二、文件输入流和输出流的四种基本类是FileInputStream、FileOutputStream、FileReader、FileWriter
输入流和输出流是相对于java内存而言的
输入流(读到java内存中):FileInputStream、FileReader
输出流(从java内存中读出):FileOutputStream、FileWriter
(一)FileInputStream(以字节的形式读入char,输出也要为char)
FileInputStream in=null;//可能会抛出一个已检查异常//别忘记导入包
try
{
in=new FileInputStream("TestFileInputStream.java");
}
catch(FileNotFoundException e)
{
System.out.println("找不到指定文件");
System.exit(-1);//直接退出程序
}
try
{
ong num=0;
while((b=in.read())!=-1)//每次读一个字节给b,-1的意思是已经读到文件结尾了
{
System.out.print((char)b);//读入char,输出也要为char//read的返回值是int类型的
num++;//统计多少个字节
}
System.out.println();
System.out.println("共读取了"+num+"个字节");
}
- read()返回类型是int,结果为-1代表读到结尾了
- 因为读的时候是按照字节来读的,所以输出的时候需要强制转换成char类型的
(二)FileReader(以字符的形式读入char,输出也要为char)
FileReader fr=null;//可能会抛出一个已检查异常//别忘记导入包
int c=0;
try
{
fr=new FileReader("E:\\workplace\\KeJian\\src\\TestFileReader.java");
int ln=0;
while((c=fr.read())!=-1)//没有读到文件结尾
{
System.out.print((char)c);//读入char,输出也要为char
ln++;
}
System.out.println("共读取了"+ln+"个字符");
}
- read()返回类型是int,结果为-1代表读到结尾了
- 因为读的时候是按照字节来读的,所以输出的时候需要强制转换成char类型的
(三)FileOutputStream(以字节的形式写到磁盘中)
- 如果通过FileInputStream读入,那么就需要用FileOutputStream与其相匹配,如果用FileWriter,会导致中文输出不正常,因为按照字节读入,就要按照字节输出,不能按照字符输出,否则会导致乱码。
- 如果通过FileInputStream读入,FileOutputStream与其相匹配输出到文件中的时候,不需要必须为char类型,因为读入和输出的方式(以字节为单位)相同
FileInputStream in=null;//可能会抛出一个已检查异常//别忘记导入包
FileOutputStream out=null;
FileWriter f=null;
try
{
in=new FileInputStream("HelloWorld.java");
out=new FileOutputStream("newHello.java");
while((b=in.read())!=-1)
{
out.write(b);//中文正常//读完之后,写到newHello.java这个文件中去
}
}
(四)FileWriter(以字符的形式写到磁盘)
FileWriter fw=null;//可能会抛出一个已检查异常//别忘记导入包
FileOutputStream fs=null;
try
{
fw=new FileWriter("E:\\workplace\\KeJian\\unicode.txt");//双斜杠是转移符的问题,\\代表\
fs=new FileOutputStream("E:\\workplace\\KeJian\\unicode1.txt");
for(int c=0;c<=50;c++)
{
fw.write(c);//乱码
fs.write(c);//乱码
}
}
- 都是乱码的原因是:需要的是字符/字节,但是传进来的是int不是一个字节/字符,所以会导致乱码
- 可能会想,为什么前面从文件中读取完之后是int类型直接写到文件中却不是乱码呢?因为即使是int类型,也是从文件中按照字节/字符的形式读取,按照字节/字符的形式拷贝的,因此不是乱码,这里是直接把一个int类型的数字放进去,不是字节/字符形式,必然会出现乱码的问题
三、字符处理流
(一)BufferedInputStream(字节流)
括号中需要是InputStream的子类
直接子类有:FileInputStream, ObjectInputStream
间接子类有:BufferedInputStream(FileInputStream),DataInputStream(FileInputStream)
BufferedInputStream bis=null;
try
{
/*FileInputStream fis=new FileInputStream("HelloWorld.java");
bis=new BufferedInputStream(fis);*/
bis=new BufferedInputStream(new FileInputStream("HelloWorld.java"));
int c=0;
for(int i=0;(c=bis.read())!=-1;i++)
{
System.out.print((char)c);
}
}
- 与 FileInputStream相比,没有太大的区别,仅仅是加快了速度
- BufferedInputStream括号中只能new FileInputStream,别的不行
(二)BufferedReader(字符流)
括号中需要是Reader的子类
直接子类有:BufferedReader, InputStreamReader
间接子类有:FileReader(InputStreamReader)
- 作用:缓冲字符,以便提供字符、数组和行的有效读取
- BufferedReader中能new FileReader,new InputStreamReader
- 增加了readLine方法,返回值是String类型,比较好用,一次读取一行,因此,如果想要读取String类型的数据,就用BufferedReader
br=new BufferedReader(new FileReader("test.txt"));//读test.txt中的内容
String s=null;
while((s=br.readLine())!=null)
{
System.out.println(s);
}
(三)BufferedOutputStream(字节流)
括号中需要是OutputStream的子类
直接子类有:ObjectOutputStream
间接子类有: DataOutputStream,FileOutputStream
(四)BufferedWriter(字符流)
括号中只能是Writer的子类
直接子类有:BufferedWriter, OutputStreamWriter
间接子类有: FileWriter, PrintWriter
- 作用:缓冲字符,以便提供字符、数组和行的有效读取
- BufferedWriter中 能new FileWriter 能new OutputStreamWriter
- 增加了newLine方法,比较好用,每次写完一行之后,自动换行,因此,如果想要读完一行之后回车,就用BufferedWriter
四、转换流
(一)InputStreamReader(字符流)
括号中只能是InputStream的子类,可以指定用何种形式编码
- InputStreamReader是将 InputStream中的字节转换成字符
- 为什么要指定编码集合呢?
因为java程序在内存中的字符都是Unicode编码的,但是从文件中读取的内容有自己的编码格式,可能是jbk编码,可能是ISO8859_1编码,还有可能是ASCILL编码,编码不同,所以要根据已有编码转换成Unicode编码,因此,要告诉java原来是什么样的编码格式(但是如果指定编码格式错误的话,会出现乱码问题)。 - 只能new InputStream的子类,如:FileInputStream
FileWriter和FileOutputStream都可以实现在文章结尾继续写东西而不是覆盖掉以前的东西,至于true加到哪里上面的两张图片已经说的很清楚了。
InputStreamReader isr=null;
try
{
isr=new InputStreamReader(new FileInputStream("TestTileInputStream.java"));//通过InputStreamReader读取FileInputStream中读取的TestTileInputStream.java中的字符
long num=0;
while((b=isr.read())!=-1)
{
System.out.print((char)b);
num++;
}
System.out.println("共读取了"+num+"个字符");
}
将键盘输入的内容直接输入到文档中
String s;
InputStreamReader isr=new InputStreamReader(System.in);
/*
* 本来应该套一个InputStream,但是套了System.in
* 所以System.in也是InputStream类型的
* 所以可以传给InputStreamReader
* InputStreamReader套接了控制台键盘输入
* System.in读入的是字节,InputStreamReader转换成了字符
* InputStreamReader只能一个字符一个字符地读,比较麻烦,所以又套接了一个BufferedReader
* */
BufferedReader br=new BufferedReader(isr);//BufferedReader的特点,能一次读取一行
System.out.println("Unix:Type ctrl-d or cril-c to exit."+"\nWindows:Type ctrl-z to exit");
try
{
//用Ctrl+z来结束
while((s=br.readLine())!=null)
{
System.out.println("Read: "+s);//输入一行,打印一行
}
}
(二)OutputStreamWriter(字符流)
括号中只能是OutputStream的子类,可以指定编码格式
- OutputStreamWriter是将 OutputStream中的字节转换成字符
- 为什么要指定编码集合呢?
因为java程序在内存中的字符都是Unicode编码的,到文件中的时候,会有自己的编码格式,可能是jbk编码,可能是ISO8859_1编码,还有可能是ASCILL编码,编码不同,所以要将Unicode编码转换成自己想要的编码格式,因此,要告诉java自己需要什么样的编码格式。 - 只能new OutputStream的子类,如:FileOutputStream
try
{
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("transform1.txt"));//通过FileOutputStream文件输出流,往transform1.txt中写数据
osw.write("计算机学院");
osw.write("SUN Java");
System.out.println(osw.getEncoding());//写入的时候没有指定编码格式,那么就采用中文Window默认编码GBK
osw.close();//FileOutputStream构造方法第二个参数的含义,是否追加
osw=new OutputStreamWriter(new FileOutputStream("transform1.txt",true),"ISO8859_1");
/*这里用FileOutputStream的重载方法,形参加了个true
* 这里本来默认是false,每次写的时候,会覆盖原来文件的内容
* 但是加了true,意思是保留原有文件内容,并且在原有文件的尾部追加新的内容
* 编码格式改成IS08859_1,指的是英文字符,没有中文字符串
* lantin-1
* */
osw.write("山东科技大学");//上面指定的编码格式中,没有文字字符串
osw.write("C#");
System.out.println(osw.getEncoding());
sw.close();
}
- ISO8859_1编码格式中没有文字字符串,所以是错误的。
- osw=new OutputStreamWriter(new FileOutputStream(“transform1.txt”,true),“ISO8859_1”);
/*这里用FileOutputStream的重载方法,形参加了个true
* 这里本来默认是false,每次写的时候,会覆盖原来文件的内容
* 但是加了true,意思是保留原有文件内容,并且在原有文件的尾部追加新的内容
* 编码格式改成IS08859_1,指的是英文字符,没有中文字符串
* lantin-1
* */
五、数据流
(一)DataInputStream(字节流)
括号内只能是InputStream的子类
- 可以直接将字节类型的转换为基本类型数据
(二)DataOutputStream(字节流)
括号内只能是OutputStream的子类
- 可以直接将字节类型的转换为基本类型数据
- 将基本类型转换为字节存起来
六、Print流
(一)PrintStream(字节流)
PrintStream ps=null;
try
{
FileOutputStream fos=new FileOutputStream("redirect.txt");
ps=new PrintStream(fos,true,"utf-8");
/*
* PrintStream中有构造方法,可以有三个参数的构造方法
* 第一个创建一个OutputStream
* 第二个是是不是自动的刷新、缓冲、清空
* 第三个指定输出的字符是什么编码格式
* */
if(ps!=null)
{
System.setOut(ps);//实现输出的重定向,不再向控制台输出,而是向指定文件中输出
}
int line=0;
for(char c=100;c<=200;c++)
{
System.out.println();//此时通过FileOutputStream这个流往redirect.txt这个文件中输出
line=0;
}
}
- System.setOut(ps);可以实现重定向
(二)PrintWriter(字符流)
对象序列化
为什么要进行对象序列化呢?
目的是为了转化成字节序列,便于存盘或者是进行网络传输。存盘和网络传输的是以字节为单位,对象序列化之后,就不会考虑到编码差异等问题了。
1、想要被对象序列化,就必须要实现Serializable或者Externalizable两个接口中的某一个
2、Serializable这个接口中没有任何接口,无需实现任何方法,仅仅继承这个接口就好。
3、使对象序列化的流