您的位置:首页 > 编程语言 > Java开发

Java I/O输入输出流

2015-08-30 16:02 501 查看

编码问题

String s = "Java 教程";
byte[] byte1 = s.getBytes();   //使用默认编码。
byte[] byte2 = s.getBytes("utf-8"); //显示指定编码格式为 utf-8
//把字节(转换成int)以16进制方式显示
// GBK编码 中文占2个字节,英文占1个字节
// UTF-8 编码,中文占用3个字节,英文占用1个字节
// Java 是双字节编码 UTF-16be
for( byte b : byte1) {
System.out.print(Integer.toHexString(b & 0xff) + "  ");
}


当你的字节序列是某种编码时,在把字节序列变成字符串时,也需要使用该种编码,否则会乱码。

String str = new String(b,"utf-16be");


文本文件就是字节序列,可以是任意编码的字节序列。

如果在中文机器上直接创建的文本文件,那么文本文件只认识ansi编码。

复制的还是按照原来的编码格式

File 类常用API

Java中,
java.io.FIle
类用于表示文件(目录),只用于表示文件(目录)的信息(名称、大小),不能用于文件访问。

File file = new File("E:\\java\\javaTest"); // 一个目录
File file2 = new File("E://java//test1.java"); // 一个文件
File file3 = new File("E://java","test2.java") //一个文件

if(!file.exists()) {
file.mkdir(); //创建一个目录
file.createNewFile(); //创建一个文件
} else {
file.delete();
}
if(file.isDirectory()) { }

if(file2.isFile()) { }
//常用的File对象API
file.getAbsolutePath();
file.getName();
file.getParent();


File常用操作:遍历,过滤

//列出指定目录下的目录和文件
String[] filenames = dir.list(); //列出当前目录下的子目录和文件,返回字符串数组。不包含子目录下的内容

// 需要遍历目录下的内容时,就要构造File对象做递归操作。
//File提供了直接返回对象的方法。
File[] files = dir.listFiles(); //返回直接子目录(文件)的对象
if(files != null && files.length > 0) {
for (file : files) {
if(file.isDirectory()) {
//递归
}
else {
//直接返回
}
}
}


RandomAccessFile的使用

RandomAcceseeFile 是Java 提供的对文件内容的访问类。

既可以读取文件,也可以写文件。并且可以随机访问文件,可以访问文件的任意位置

java 文件模型

在硬盘上的文件是按 字节 存储的,是数据的集合;

打开文件

两种模式: “rw”“r”。

RandomAccessFile raf = new RandomAccessFile(file, “rw”);

文件指针: 打开文件时指针在开头 pointer = 0;

写方法

raf.write(int) ; //只写一个字节(后8位),同时指针指向下一个字节的位置,准备再次写入

读方法

int b = raf.read(); //只读一个字节

读写文件后一定要关闭文件,否则可能出现不可预测的错误。

File file  = new File("001.dat") {
if(!file.exist()) {
file.createNewFile();
}
RandomAccessFile raf = new RandomAccessFile(file,"rw");
int i = 0x7fffffff;
//用write方法没戏只写一个字节。int4个字节,需要写4次
raf.write(i >>> 24); 高8位
raf.write(i >>> 16);
raf.write(i >>> 8);

//或是用 writeInt 直接写一个int
raf.writeInt(i);

//write 可以直接写一个字节数组
String s = "中";
byte[] gbk = s.getBytes("gbk");
raf.write(gbk); //写入一个字节数组

//读文件,必须把指针移到头部
raf.seek(0);
//一次性读取文件中的所有内容到字节数组中
byte[] buf = new Byte[(int)raf.length()];
raf.read(buf);


IO流(输入流,输出流)

字节流的使用

InputStream、OutputStream

InputStream 抽象了应用程序读取数据的方式

OutputStream 抽象了应用程序写出数据的方式

EOF == end of file 读到-1表示读到文件结尾

输入流基本方法:(注意,键盘是输入流。 从键盘读入数据写到文件中)

int b = in.read(); // 读取一个字节无符号填充int低8位。 -1 是 EOF


`int read(byte[] buf); //读取数据填充到字节数组buf

int read(byte[] buf, int start, int size); //读取数据到字节数组buf,从buf 的start位置开始存放size长度的数据


输出流基本方法

out.write(int b); // 写出一个byte到流中,b的低8位


out.write(byte[] buf); //将buf字节数组写入到流


out.write(byte[] buf, int start, int size); //字节数组buf从start位置开始写size长度的字节到流


FileInputStream 继承InputStream,具体实现了文件上读取数据。

FileInputStream in = new FileInputStream("file");


int b = in.read();


Byte[] buf = new byte[20*1024];


int bytes = in.read(buf,0,buf.length);//从in中批量读取字节,放入到buf这个字节数组中,从第0个位置开始放,最多放buf.length个字节。返回的是读到的字节的个数


FileOutputStream 继承了OutputStream,具体实现了向文件中写出byte数据的方法

//如果文件不存在,直接创建。如果存在,删除后创建


FileOutputStream out = new FileOutputStream(file);


//true 表示 append,在文件后面追加.


//如果文件不存在,直接创建。如果文件存在,直接在文件末尾写入


FileOutputStream out = new FileOutputStream(file,true);


DataInputStream 和 DateOutputStream 数据输入输出流

是对“流”功能的扩展,可以更加方便的读取int,long,字符等类型数据

writeInt()/ writeDouble()/ writeChar()


//有OutputStream嵌套生成


FileOutputStream fout = new FileOutputStream("E:\\001.txt");


DataOutputStream dos = new DataOutputStream(fout);


dos.writeInt(10);


dos.writeUTF("中国");// 采用utf-8编码写出,每个汉字3个字节


dos.writeChars("中国"); //采用utf-16be 编码,每个汉字2个字节


BufferedInputStream & BufferedOutputStream 带缓冲区的字节流操作。

一般打开文件进行写入或读取操作时,都会加上缓冲,这种流模式提高了IO的性能。

FileOutoutStream ---> write()方法:类似一滴一滴的把水“转移”过去
DataOutputStream ---> writeXxx方法:较方便,类似一瓢一瓢的把水‘转移’过去
BufferedOutputStream ---> write() 方法: 更方便,类似把水先一瓢一瓢放到桶中,在从桶中导入水缸中。
FileOutoutStream ---> write(buf,start,size)方法: 速度最快


BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
int c;
while((c = bis.read()) != -1) {
bos.write(c);
bos.flush(); //一定要刷新缓冲区,将数据写入文件中
}
bis.close();
bos.close();


字符流的使用

认识文本和文本文件

文本: Java 文本(char)是16位无符号整数,是字符的unicode编码(双字节编码)

文件: 是按照byte存储的数据序列

文本文件: 是文本(char)序列按照某种编码方案(utf-8,utf-16be, gbk) 序列化为byte的存储结构

字符流输入输出流(Reader, Writer) 操作文本文件

字符的处理: 一次处理一个字符

字符的底层仍然是基本的字节序列

字符流的基本实现

InputStreamReader
: 完成byte流解析为char流,按照编码解析

OuputStreamWriter
: 提供char流发到byte流,按照编码解析

FileInputStream in = new FileInputStram("001.txt");
InputStreamReader isr = new InputStreamReader(in,"gbk"); //默认按照gbk编码
int c = isr.read();
char[] buffer = new char[8 * 1024];
c = isr.read(buffer, 0, buffer.length); //批量读取字符,放入buffer字符数组,从0位置开始放置,最多放置length个,返回读取到的字符个数
String s = new String(buffer,0,c); //把字符存储到字符串中


另外一种实现: FileReader&FileWriter

注意: 没有编码参数

需要设置编码时,就用InputStreamReader&OutputStreamWriter

FileReader fr = new FileReader("001.txt");
FileWriter fw = new FileWriter("002.txt");
//FileWriter fw = new FileWriter("002.txt","true");//文件后面追加
while((int c = fr.read(buffer,0,buffer,length) != -1) {
fw.write(buffer,0,c);
fw.flush();
}
fr.close();
fw.close();


字符流的过滤器 BufferedReader

BufferedReader 除了基本的读取外,可以一次读取一行

BufferedWriter/ PrintWriter 可以一次写一行

BufferedReader br = new BufferedReader(
new InputStreamReader(
new FileInputStream("001.txt"[,"utf-8"])));
//或是用FileReader()
//BufferedReader br = new BufferedWriter(FileReader("001.txt"));

BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(
new FileInputStream("002.txt"[,"utf-8"]));
String line;
//一次读取一行,不能识别换行,返回是字符串
while(line = br.readLine() != null) {
bw.write(line);
//单独写出换行操作
bw.newLine(); //换行操作
bw.flush();
}
br.close();
bw.close();

//  可以直接用PrintWriter 代替 BufferedWriter
// PrintWriter pw = new PrintWriter("003.txt");
// PrintWriter pw1 = new PrintWriter(outputStream, boolean autoFlush); //自动刷新
// pw.print(line); //输出一行,没有换行
// pw.println(line); // 输出一行,有换行


对象的序列化和反序列化

对象的序列化&反序列化

对象的序列化,就是将Object 转换成byte序列

对象的反序列化,就是将byte序列转换成Object

序列化流:ObjectOutputStream, 是过滤流 -> writeObject 方法

反序列化流:ObjectInputStream, -> readObject方法

序列化接口 (Serializable)

对象必须实现序列化接口,才能进行序列化,否则会出现异常;

序列化接口没有任何实现方法,只是一个标准。

ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("001.txt"));
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("002.txt"));
Student stu = new Student("1001","张三"); // Student 类要实现Serializable 接口,否则出现异常
oos.writeObject(stu);
Student stu2 = (Student)ois.readObject();
oos.flush();
oos.close();
ois.close();


关键字: transient

transient 修饰的元素不会进行JVM默认的序列化

但是可以自己定义给元素的序列化。





例如在ArrayLis中,底层的数组是不一定会被填满的。

所以ArrayList 自己定义数组元素的序列化和反序列化,这样可以舍去空元素,提高性能。

序列化中子类和父类构造函数的调用

父类实现了序列化接口,其子类都能进行序列化。

定义的3个foo类



序列化时递归调用父类构造函数



反序列化操作1



反序列化操作1 结果: 只打印出了对象,没有打印出构造函数调用结果



另外3个类



序列化时会打印3个构造函数。

反序列化时会打印父类的构造函数:

反序列化操作 bar 类



说明 对子类对象进行反序列化操作时,如果其父类没有实现序列化接口,那么其父类的构造函数会被调用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: