转换流
转换流提供了在字节流和字符流之间的转换
Java API提供了两个转换流:
InputStreamReader和OutputStreamWriter
字节流中的数据都是字符时,转成字符流操作更高效。
InputStreamReader用于将字节流中读取到的字节按指定字符集解码成字符。需要和 InputStream“套接”。
构造方法:
InputStreamReader(InputStream in)
public InputSreamReader(InputStream in,String charsetName)
如: Reader isr = new InputStreamReader(System.in,”ISO5334_1”);
OutputStreamWriter用于将要写入到字节流中的字符按指定字符集编码成字节。 需要和OutputStream“套接”。
构造方法:
public OutputStreamWriter(OutputStream out)
public OutputSreamWriter(OutputStream out,String charsetName)
所有的文件都是有编码格式对于我们来说,TXT和java文件一般来讲有三种编码ISO8859-1,西欧编码,是纯粹英文编码,不适应汉字GBK和UTF-8,这两编码是适用于重要和英文我们一般使用UTF-8编码
import java.io.*;
public class test5 {
//所有的文件都是有编码格式
//对于我们来说,TXT和java文件一般来讲有三种编码
//ISO8859-1,西欧编码,是纯粹英文编码,不适应汉字
//GBK和UTF-8,这两编码是适用于重要和英文
//我们一般使用UTF-8编码
public static void main(String[] args) {
try {
test5.testInputStreamReader("D:\\JavaProjects\\b站草稿\\IO控制流\\src\\t5.txt","UTF-8");
test5.testOutputStreamWriter("D:\\JavaProjects\\b站草稿\\IO控制流\\src\\t6.txt","你好你好","UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 转换字节输入流为字符输入流
* 注意,在转换字符流的时候,设置的字符集编码要与读取的文件的数据的编码一致
* 不然就会出现乱码
* InputStreamReader
*/
public static void testInputStreamReader(String path,String mode) throws Exception {
FileInputStream fs = new FileInputStream(path);
InputStreamReader is = new InputStreamReader(fs,mode);
char[] c = new char[1024];
is.read(c);
System.out.println(c);
is.close();
is.close();
}
/**
* 转换字节输出流为字符输出流
* 注意,在转换字符流的时候,设置的字符集编码要与读取的文件的数据的编码一致
* 不然就会出现乱码
* OutputStreamWriter
*/
public static void testOutputStreamWriter(String path , String s , String mode) throws Exception {
OutputStreamWriter ow = new OutputStreamWriter(new FileOutputStream(path),mode);
ow.write(s);
ow.flush();
ow.close();
}
}
标准输入输出流
System.in和System.out分别代表了系统标准的输入和输出设备
默认输入设备是键盘,输出设备是显示器
System.in的类型是InputStream
System.out的类型是PrintStream,其是OutputStream的子类FilterOutputStream 的子类
通过System类的setIn,setOut方法对默认设备进行改变。
public static void setIn(InputStream in)
public static void setOut(PrintStream out)
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
public class text6 {
public static void main(String[] args) {
try {
text6.testSystemIn();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 标准的输入流
* @throws Exception
*/
public static void testSystemIn() throws IOException {
//创建一个接收键盘输入数据的输入流,参数只要是继承InputStream接口的都行
InputStreamReader ir = new InputStreamReader(System.in);
//放入缓冲流
BufferedReader br = new BufferedReader(ir);
String s = "";//定义一个临时接收数据的字符串
while ((s = br.readLine()) != null){//readLine返回的是字符串
System.out.println(s);
}
br.close();
ir.close();
}
}
运用一
把控制台输入的内容写到指定的TXT文件中,当接收到字符串over,就结束程序的运行
//把控制台输入的内容写到指定的TXT文件中,当接收到字符串over,就结束程序的运行
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.InputStreamReader;
public class text7 {
public static void main(String[] args) {
try {
text7.writeTXT("D:\\JavaProjects\\b站草稿\\IO控制流\\src\\t7.txt");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void writeTXT(String path) throws Exception{
//创建一个接收键盘输入数据的输入流
InputStreamReader inputStreamReader = new InputStreamReader(System.in);
//将输入流放入缓冲流
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
//缓冲流将数据写入指定位置
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(path));
String str = "";
//标准写法
// while((str = bufferedReader.readLine()) != null){
// if(str.equals("over")){
// break;
// }
// //读取的每一行都写到指定的TXT文件
// bufferedWriter.write(str);
// }
while (true){
str = bufferedReader.readLine();
if(str.equals("over")){ break;}
//读取的每一行都写到指定的TXT文件
bufferedWriter.write(str);
}
//错误示范
// str = bufferedReader.readLine();
// while (true){
// if (str.equals("over")){
// bufferedWriter.flush();
// bufferedWriter.close();
// bufferedReader.close();
// inputStreamReader.close();
// break;
// }else {
// bufferedWriter.write(str);
// }
//
// }
bufferedWriter.flush();
bufferedWriter.close();
bufferedReader.close();
inputStreamReader.close();
}
}
运用二
在一个TXT文件中,写一组用户名和密码,通过控制台输入用户名和密码,与TXT文件中的用户名密码做对比,如果一样就在打印登录成功,如果不一致,就打印用户名密码错误
//在一个TXT文件中,写一组用户名和密码,通过控制台输入用户名和密码,
//与TXT文件中的用户名密码做对比,如果一样就在打印登录成功,如果不一致,就打印用户名密码错误
import java.io.*;
public class text9 {
public static void main(String[] args) {
try {
text9.testPassWord();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void testPassWord() throws Exception {
//建立输入流
InputStreamReader ir = new InputStreamReader(System.in);
InputStreamReader ir2 = new InputStreamReader(new FileInputStream("D:\\JavaProjects\\b站草稿\\IO控制流\\src\\t8.txt"),"UTF-8");
//放入缓冲流
BufferedReader br = new BufferedReader(ir);
BufferedReader br2 = new BufferedReader(ir2);
//临时字符串存放数据
String str = "";
String str2 = "";
while ((str = br.readLine())!= null) {
str2 = br2.readLine();
if (str.equals(str2)) {
System.out.println("登录成功");
} else {
System.out.println("用户名密码错误");
}
}
br2.close();br.close();ir2.close();ir.close();
}
}
对象流
ObjectInputStream和OjbectOutputSteam
用于存储和读取对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
序列化(Serialize):用ObjectOutputStream类将一个Java对象写入IO流中
反序列化(Deserialize):用ObjectInputStream类从IO流中恢复该Java对象
ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
对象的序列化
对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的Java对象
序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原
序列化是 RMI(Remote Method Invoke – 远程方法调用)过程的参数和返回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是 JavaEE 平台的基础
如果需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:
Serializable
Externalizable
凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:
private static final long serialVersionUID;
serialVersionUID用来表明类的不同版本间的兼容性
如果类没有显示定义这个静态变量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的源代码作了修改,serialVersionUID 可能发生变化。故建议,显示声明
显示定义serialVersionUID的用途
希望类的不同版本对序列化兼容,因此需确保类的不同版本具有相同的serialVersionUID
不希望类的不同版本对序列化兼容,因此需确保类的不同版本具有不同的serialVersionUID
import java.io.*;
/**
* 序列化与反序列化
* 注意:对象的序列化与反序列化使用的类要严格一致,包名,类名,类机构等等所有都要一致
* @author lby
*
*/
public class text10 {
public static void main(String[] args) {
try {
//text10.testSerialize("D:\\JavaProjects\\b站草稿\\IO控制流\\src\\t9.txt");
text10.testDeserialize();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 对象的序列化
*/
public static void testSerialize(String path) throws Exception {
//序列化完了之后存放在一个地址里 所以这里要newFile
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(path));
Preson p = new Preson();
p.age=10;
p.name="zhangsan";
out.writeObject(p);
out.flush();
out.close();
}
/**
* 对象的反序列化
*/
public static void testDeserialize() throws Exception {
ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:\\\\JavaProjects\\\\b站草稿\\\\IO控制流\\\\src\\\\t9.txt"));
Object obj = in.readObject();
Preson p = (Preson)obj;
System.out.println(p.age + p.name);
in.close();
}
}
import java.io.Serializable;
/**
* 可以序列化与反序列化的对象
* @author lby
*
*/
public class Preson implements Serializable {
/**
* 一个表示序列化版本标识符的静态变量
* 用来表明类的不同版本间的兼容性
*/
public static final long serialVersionUID = 1L;
String name;
int age;
}
RandomAccessFile 类
RandomAccessFile 类支持 “随机访问” 的方式,程序可以直接跳到文件的任意 地方来读、写文件
支持只访问文件的部分内容
可以向已存在的文件后追加内容
RandomAccessFile 对象包含一个记录指针,用以标示当前读写处的位置。 RandomAccessFile 类对象可以自由移动记录指针:
long getFilePointer():获取文件记录指针的当前位置
void seek(long pos):将文件记录指针定位到 pos 位置
构造器
public RandomAccessFile(File file, String mode)
public RandomAccessFile(String name, String mode)
创建 RandomAccessFile 类实例需要指定一个 mode 参数,该参数指定 RandomAccessFile 的访问模式:
r: 以只读方式打开
rw:打开以便读取和写入
rwd:打开以便读取和写入;同步文件内容的更新
rws:打开以便读取和写入;同步文件内容和元数据的更新
r和rw最常用
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.util.RandomAccess;
public class text11 {
public static void main(String[] args) {
try {
text11.testRandomAccessFileRead();
text11.testRandomAccessFileWrite();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 文件的随机读写
* 程序可以直接跳到文件的任意 地方来读、写文件
* @author lby
*
*/
/**
* 随机读文件
*/
public static void testRandomAccessFileRead() throws Exception{
//RandomAccessFile的构造有两个参数,参数1是读写的文件的路径
//参数2是指定 RandomAccessFile 的访问模式
//r: 以只读方式打开
//rw:打开以便读取和写入
//rwd:打开以便读取和写入;同步文件内容的更新
//rws:打开以便读取和写入;同步文件内容和元数据的更新
//最常用是r和rw
RandomAccessFile rf = new RandomAccessFile("D:\\JavaProjects\\b站草稿\\IO控制流\\src\\t11.txt","r");
//rf.seek(0);//设置读取文件内容的起始点
rf.seek(8);//通过设置读取文件内容的起始点,来达到从文件的任意位置读取
byte[] c = new byte[1024];
int len;
StringBuilder sb = new StringBuilder();
while ((len = rf.read(c)) != -1){
sb.append(new String(c,0,len));
}
rf.close();
System.out.println(sb);
}
/**
* 随机写
*/
public static void testRandomAccessFileWrite() throws Exception {
RandomAccessFile ra = new RandomAccessFile("D:\\JavaProjects\\b站草稿\\IO控制流\\src\\t11.txt","rw");
//ra.seek(0);//从头写
ra.seek(ra.length());//末尾追加
ra.write("你好".getBytes());
ra.close();
}
}
总结
流是用来处理数据的。
处理数据时,一定要先明确数据源,与数据目的地
数据源可以是文件,可以是键盘。
数据目的地可以是文件、显示器或者其他设备。
而流只是在帮助数据进行传输,并对传输的数据进行处理,比如过滤处理、转换处理等。
字节流-缓冲流(重点)
输入流InputStream-FileInputStream-BufferedInputStream
输出流OutputStream-FileOutputStream-BufferedOutputStream
字符流-缓冲流(重点)
输入流Reader-FileReader-BufferedReader
输出流Writer-FileWriter-BufferedWriter
转换流
InputSteamReader和OutputStreamWriter
对象流ObjectInputStream和ObjectOutputStream(难点)
序列化
反序列化
随机存取流RandomAccessFile(掌握读取、写入)
扫盲重点:read方法详解
public static void testFileInputStream()throws IOException{
FileInputStream in = new FileInputStream("D:\\JavaProjects\\b站草稿\\IO控制流\\a\\tt1.txt");
byte[] b = new byte[3];//设置一个byte数组接收读取的文件的内容
int len = 0;//设置一个读取数据的长度
while ((len=in.read(b)) != -1){
System.out.println(new String(b,0,len));
}
in.close();//注意。流在使用完毕之后一段要关闭
}
假设我的数据为:abcdefg (长度为7)
首先当read读完之后再读后一位就会返回-1,用这个条件去做while循环,因为read每次只读一个你定义的数组长度大小的数据。
1.read方法里面有一个int read(byte)方法,意思是读出来的数据直接放入byte数组里并且返回一个数据长度,所以这里提前定义了一个byte数组来传进read方法里,in.read(b),read方法会返回有效数据的长度,比如说我的总数据长度是7,那么我定义数组长度为10时new byte[10],read会返回7,这里没什么问题,但如果我定义数组长度为3new byte[3],那么read会返回3次,第一次是3,第二次是3,第三次是1,第四次是-1此时就会结束循环。因为第三次读的时候前面已经读了6位了只剩下一位,所以就读了一位有效长度。另外read三次放入数组的数据是这样的,第一次为abc,第二次为def,但是第三次read方法讲数据放入byte数组时的值为gef,他并不是只有一个g,因为她只读了一个g但是他还是会传入三个数据。
2.那么此时len的作用就体现出来了,在后面的new String(b,0,len)方法里,len保证了每次读取的都是有效值,比如第三次len就是1,那么放入Sting里的数据是0-1位,就是“g”,这样就不会把后面的ef也读进去。
3.另外提一嘴,new String(b,0,len)表示每次读出多少字节就把对应字节转化为字符串
因此我认为这里再加入一个StringBuilder去接收转化出来的字符串会更好,用append方法。这样就会形成一个完成连续的字符串,以后用作比较的时候会更好