关于java的io的,共有下面几块内容。
1. 文件的编码
我们常用的编码的方式有GBK,utf-8,utf-16be(java默认编码)等。
在java中,可以将一个字符串或者文本转换成字节序列。用到的方法是:
String.getBytes()
,参数可以是任一的编码格式。
其中要熟记下面几种编码在java文件中的区别:
- gbk编码:中文占用两个字节,英文占用一个字节。
- utf-8编码:中文占用3个字节,英文占用1个字节。
- utf-16be:中文占用两个字节,英文占用两个字节。
- 注意:当字节序列是某种编码是,这个时候想把字节序列变成这种字符串,也需要用这种编码,否则会出现乱码。
- 注意:文本文件就是字节序列可以是任一编码的字节序列如果在中文机器上直接创建文本文件,那么该文件只认识ANSI编码。
下面的方法可以把一个字节转成16进制显示:
System.out.print(Integer.toHexString(b & 0Xff) + " ");
2. java.io.File类
File类只用于表示文件(目录)的信息(名称,大小),不能用于文件内容的访问。
可以用File.separators设置分隔符,适用于任何系统。
对于目录常用的方法有:
//java.io.File用于表示文件(目录)
// File类只用于表示文件(目录)的信息(名称,大小),
// 不能用于文件内容的访问
import java.io.*;
public class FileDemo {
public static void main(String[] args) throws IOException {
// FileAPI();
lisrDirectiry(new File("E:\\video"));
}
public static void FileAPI() {
// 了解构造函数的几种情况,查找帮助
File file1 = new File("E:\\javaio");
//可以用File.separators设置分隔符,适用于任何系统
// 判断目录存不存在
// System.out.println(file1.exists());
// if(!file1.exists()) {
// file1.mkdir(); //新建目录
// }
// else {
// file1.delete(); //删除目录
// }
// 判断是否是一个目录:如果是目录(true),不是目录或者目录不存在(false)
System.out.println(file1.isDirectory());
//是否是一个文件
System.out.println(file1.isFile());
// File file2 = new File("E:\\javaio\\孙文.txt");
//另一种构造函数的方法
File file2 = new File("E:\\javaio", "suuwen.txt");
if(!file2.exists()) {
try{
file2.createNewFile(); //会有io异常,要加入异常控制
}
catch(IOException e) {
e.printStackTrace();
}
}
else {
file2.delete();
}
// 其他常用的API
// 直接打印文件的目录
System.out.println(file1);
// 与上面一样
System.out.println(file1.getAbsolutePath());
// 打印文件(目录)的当前名称
System.out.println(file1.getName());
//父目录
System.out.println(file1.getParent());
System.out.println(file1.getParentFile());
}
// 涉及File的一些常用操作,比如过滤,遍历等
//列出指定目录下(包括其子目录)的所有文件
public static void lisrDirectiry(File dir) throws IOException {
if(!dir.exists()) {
throw new IllegalArgumentException("目录:" + dir + "不存在");
}
if (!dir.isDirectory()) {
throw new IllegalArgumentException(dir + "不是目录");
}
//返回一个字符串数组,包含子目录和文件
//不包含子目录下的目录和文件.
// String[] fileNames = dir.list();
// for(String str : fileNames) {
// System.out.println(dir + "\\" + str);
// }
// 如果要遍历子目录下的内容,就需要构造File对象做递归操作。
//此方法返回直接子目录(文件)的抽象
//遍历所有的文件和目录。
File[] files = dir.listFiles();
if(files != null && files.length > 0) {
for(File file : files) {
if(file.isDirectory()) {
lisrDirectiry(file);
}
else {
System.out.println(file);
}
}
}
// for( File file : files) {
// System.out.println(file);
// }
}
}
3. RandomAccessFile类
RandomAccessFile java提供的对文件内容的访问,既可以读文件,也可以写文件。
RandomAccessFile支持随机访问文件,可以访问文件的任意位置。
(1)java文件模型:在硬盘的文件是byte byte byte存储的,是数据的集合
(2)打开方式:
有两种模式“rw”(读写) “r”(只读)
RandomAccessFile = new RandomAccessFile(file,”rw”);
内部包含文件指针,打开文件时指针在开头 pointer = 0
(3)写方法:
raf.write(int):只能写一个字节(后8位),同时指针指向下一个位置再次写入。
(4)读方法:
int b = raf.read():读一个字节
(5)文件读写一定要关闭。
import java.io.*;
import java.util.*;
public class RafDemo {
public static void main(String[] args) throws IOException {
File file = new File("D:\\yuu.txt");
if(!file.exists()) {
file.createNewFile();
}
RandomAccessFile raf = new RandomAccessFile(file, "rw");
//指针的位置
// 可实现多线程文件下载
System.out.println(raf.getFilePointer());
// java中的char类型是使用UTF16来编码的,
// 也就是说用16位(也就是两字节)来表示一个char
raf.write('A'); //只写了一个字节(低8位)。
System.out.println(raf.getFilePointer());
raf.write('B');
System.out.println(raf.getFilePointer());
int i = 0x7fffffff;
//用write方法每次只能写一个字节,如果要把i写进去就得写4次。
raf.write(i >>> 24); //高8位,无符号左移
raf.write(i >>> 16);
raf.write(i >>> 8);
raf.write(i);
System.out.println(raf.getFilePointer());
//可以直接写一个int
raf.writeInt(i);
String s = "中";
byte[] gbk = s.getBytes("gbk");
raf.write(gbk);
System.out.println(raf.length());
//读文件,必须把指针移到头部
raf.seek(0);
//一次性读取,把文件中的内容都不到字节数组中
byte[] buf = new byte[(int)raf.length()];
raf.read(buf);
System.out.println(Arrays.toString(buf));
String s1 = new String(buf,"gbk");
System.out.println(s1);
for(byte b : buf) {
System.out.print(Integer.toHexString(b & 0xff) + " ");
}
System.out.println();
//一定要关闭
raf.close();
}
}
4. 字节流
IO流:可以分为输入流,输出流
字节流和字符流
1. (1)字节流:InputStream:抽象了应用程序读取数据的方式
OutputStream:抽象了应用程序写出数据的方式
(2):EOF = End,读到-1就读到结尾。
(3):输入流:基本方法
int b = in.read();读取一个字节,无符号,填充到int低8位,读到-1(EOF)结束。
in.read(byte[] buf):读取数据填充到字节数组buf
in.read(byte[] buf, int start, int size)
(4): 输出流基本方法:
out.write(int b):写出一个byte到流,b的低8位。
out.write(byte[] buf):将buf字节数组都写入流
out.write(byte[] buf, int start, int size)
(5):FileInnputStream—》具体实现了在文件上读取数据
(6): FileOutputStream–> 具体实现了向文件中写出byte数据的方法。
(7):DataOutputStream/DataInputStream
对“流”功能的扩展,可以更加方便的读取int, long,字符等类型数据
DataOutputStream/DataInputStream
writeInt()/writeDouble()/writeUTF()
(8):BufferedInputStream & BufferedOutputStream
这两个流类为IO提供了带缓冲区的操作,一般打开文件进行写入或者读取操作时,都会加上缓冲
,这种流模式提高了IO的性能,从应用程序中把输入放入文件。
BufferedOutputStream–》write方法更方便,相当于一瓢一瓢水放入桶(缓冲区)中,在倒入另一缸中
import java.io.*;
public class IOUtil {
public static void main(String[] args) throws IOException{
// printHex("D:\\yulu.txt");
// printHexByteByteArray("D:\\yulu.txt");
// outputToFile("D:\\yulu1.txt");
copyFileByBuffer(new File("D:\\yulu.txt"), new File("D:\\yulu3.txt"));
}
/*
读取文件指定内容,按照16进制输出到控制台
并且每输出10gebyte换行
单字节读取,效率很低
*/
public static void printHex(String fileName) throws IOException {
//把文件作为字节流进行读操作
FileInputStream in = new FileInputStream(fileName);
int b;
int i = 1;
while((b = in.read()) != -1) {
if( b < 0xf) {
//单位数前面补0
System.out.print("0");
}
System.out.print(Integer.toHexString(b) + " ");
if(i++ % 10 == 0) {
System.out.println();
}
}
System.out.println();
in.close();
}
//对打文件批量读取,效率高
public static void printHexByteByteArray(String fileName) throws IOException {
FileInputStream in = new FileInputStream(fileName);
byte[] buf = new byte[20 * 1024];
//从in中批量读取字节,放入到buf这个字节数组中,
// 从0开始放,做到放到buf.length个,返回的是读到的字节个数
int bytes = in.read(buf, 0,buf.length);
// 一次性读完,说明字节数组足够大
int j = 1;
//第一种方式
for(int i = 0; i < bytes; i++) {
if(buf[i] <= 0xf) {
System.out.println("0");
}
System.out.print(Integer.toHexString(buf[i]) + " ");
if(j++ % 10 == 0) {
System.out.println();
}
}
System.out.println();
in.close();
}
public static void outputToFile(String fileName) throws IOException {
// 如果该文件不存在,则直接创建;如果存在,删除后创建。
//注意:若是构造函数的参数有true的话,则是在文件后追加。
FileOutputStream out = new FileOutputStream(fileName);
// 写出a字符的低8位(每次只能一个字节)
out.write('A');
out.write('B');
//如果是一个int型数据,则要写4次,每次8位。
int a = 11;
out.write( a >>> 24);
out.write( a >>> 16);
out.write( a >>> 8);
out.write( a);
// 可以写入一个字符数组
byte[] gbk = "中国".getBytes("gbk");
out.write(gbk);
out.close();
IOUtil.printHex(fileName);
}
//实现一个复制文件的操作
public static void copyFile(File srcFile, File destFile) throws IOException {
if(!srcFile.exists()) {
throw new IllegalArgumentException("文件" + srcFile + "不存在");
}
if(!srcFile.isFile()) {
throw new IllegalArgumentException( srcFile + "不是文件");
}
//读写文件的类
FileInputStream in = new FileInputStream(srcFile);
FileOutputStream out = new FileOutputStream(destFile);
//进行批量读写
byte[] buf = new byte[8 * 1024];
int b;
while( (b = in.read(buf, 0, buf.length)) != -1 ) {
out.write(buf, 0, b);
out.flush();//最好加上
}
in.close();
out.close();
}
public static void copyFileByBuffer(File srcFile, File destFile) throws IOException {
if(!srcFile.exists()) {
throw new IllegalArgumentException("文件" + srcFile + "不存在");
}
if(!srcFile.isFile()) {
throw new IllegalArgumentException( srcFile + "不是文件");
}
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();
}
}
5. 字符流
字符流
(1):编码问题
(2):文本和文本文件
java的文本(char)是16位无符号整数,是字符的unicode编码(双字节编码)
文本文件是文本(char)序列按照某种编码方案(utf-8, utf-16be,gbk)序列化为byte的存储结果。
(3):字符流(Reader, Writer)
字符的处理:一次处理一个字符
字符的底层任然是基本的字节序列。
字符流的基本实现:
InputStreamReader:完成byte流解析到char流,按照编码解析。
OutputStreamWrite 提供char流到byte流,按照编码处理
FileReader/FileWriter
//字符流的过滤器
BufferedReader --》readLine,一次读一行
BufferedWriter/PrintWriter --->写一行
import java.io.*;
public class FrAndFwDemo {
public static void main(String[] args) throws IOException {
// IsrAndOswDemo("D:\\yulu.txt");
// FrAndFw();
RrAndBwOrPwDemo();
}
public static void IsrAndOswDemo(String filename) throws IOException {
FileInputStream in = new FileInputStream(filename);
//默认项目的编码。操作是要写文件本身的编码
InputStreamReader isr = new InputStreamReader(in, "gbk");
// int c;
// while( (c = isr.read()) != -1 ) {
// System.out.print( (char)c );
// }
// System.out.println();
char[] buffer = new char[8 * 1024];
int c;
//批量读取,放入到buffer这个字符数组,最
// 多放buffer.length,返回的是读到字符的个数
while((c = isr.read(buffer, 0, buffer.length)) != -1) {
// System.out.println(c);
String s = new String(buffer, 0, c);
System.out.println(s);
}
// System.out.println(c);
}
public static void FrAndFw() throws IOException {
FileReader fr = new FileReader("D:\\yulu.txt");
FileWriter fw = new FileWriter("D:\\yulu4.txt",true); //有true是追加
char[] buffer = new char[2056];
int c;
while( (c = fr.read(buffer, 0, buffer.length)) != -1 ) {
fw.write(buffer, 0, c);
fw.flush();
}
fr.close();
fw.close();
}
public static void RrAndBwOrPwDemo() throws IOException{
BufferedReader br = new BufferedReader(
new InputStreamReader(
new FileInputStream("D:yulu.txt")));
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter (
new FileOutputStream("D:\\yulu5.txt")));
String line;
while((line = br.readLine()) != null) {
// 不能识别换行,一次读一行;
System.out.println(line);
bw.write(line);
//单独写出换行操作
bw.newLine();
bw.flush();
}
br.close();
bw.close();
}
}
6. 序列化与反序列化
对象的序列化,反序列化
1)对象序列化,就是将Object转换成byte序列,反之叫对象的反序列化
2)序列化流(ObjectOutputStream),是过滤流—-writeObject
反序列化流(ObjectInputStream)—readObject
3)序列化接口(Serializable)
对象必须实现序列化接口 ,才能进行序列化,否则将出现异常
这个接口,没有任何方法,只是一个标准
4) transient关键字
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException
分析ArrayList源码中序列化和反序列化的问题
import java.io.*;
class Student implements Serializable{
private String stuno;
private String stuname;
//该元素不会进行jvm默认的序列化,也可以自己完成这个元素的序列化
private transient int stuage;
public Student(String stuno, String stuname, int stuage) {
super();
this.stuno = stuno;
this.stuname = stuname;
this.stuage = stuage;
}
public String getStuno() {
return stuno;
}
public void setStuno(String stuno) {
this.stuno = stuno;
}
public String getStuname() {
return stuname;
}
public void setStuname(String stuname) {
this.stuname = stuname;
}
public int getStuage() {
return stuage;
}
public void setStuage(int stuage) {
this.stuage = stuage;
}
@Override
public String toString() {
return "Student [stuno=" + stuno + ", stuname=" + stuname + ", stuage="
+ stuage + "]";
}
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
s.defaultWriteObject();//把jvm能默认序列化的元素进行序列化操作
s.writeInt(stuage);//自己完成stuage的序列化
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException{
s.defaultReadObject();//把jvm能默认反序列化的元素进行反序列化操作
this.stuage = s.readInt();//自己完成stuage的反序列化操作
}
}
public class ObjectSeriaDemo1 {
public static void main(String[] args) throws Exception{
String file = "D:\\xuliehua.txt";
//1.对象的序列化
/*
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(file));
Student stu = new Student("10001", "张三", 20);
oos.writeObject(stu);
oos.flush();
oos.close();
*/
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(file));
Student stu = (Student)ois.readObject();
System.out.println(stu);
ois.close();
}
}
关于java的io主要涉及以上内容。我们基本必须了解本件的编码,以及不同编码的之间的区别,占用字节的大小。接着了解了File类,可以实现文件或者目录的操作,比如获取文件目录路径,遍历目录等,但不涉及文件内容的读取。
接着了解到一个随机的文件读取类,RandomAccessFile(”rw”,”r”),可以实现文件的读写。
重要的当属:io分为输入流和输出流,总是一一对应的。对不用的流分为字节流和字符流。字节流可以处理包括文本,图像,音视频在内的文件(这些文件都是字节序列),类InputStream和OutputStream是字节流类的抽象类。具体实现的类包括(FileInputStream和FileOutputStream).
而字符流操作的是文本文件,一次处理一个字符。java的文本是(char)是16为无符号整数,是字符的unicode编码。字符的底层任然是基本的字节序列。FileReader/FileWriter。
字符流的基本实现
InputStreamReader 完成byte流解析为char流,按照编码解析
OutputStreamWriter 提供char流到byte流,按照编码处理
最后是序列化的知识:
对象序列化,就是将Object转换成byte序列,反之叫对象的反序列化 。以及transient关键字。