String s = "慕课ABC"; byte[] bytes1 = s.getBytes();//转换成字节序列用的是项目默认的编码 ,编码GBK for(byte b : bytes1) { //把字节(转换成)int以16进制的方式显示, 只显示后8位 System.out.println(Integer.toHexString(b & 0xff) + " "); } //gbk编码中文占用两个字节,英文占用1个字节 //utf-8编码中文占用3个字节,英文占用1个字节 byte[] bytes2 = s.getBytes("utf-8"); for(byte b : bytes2) { System.out.print(Integer.toHexString(b & 0xff) + " "); }
//java是双字节编码, utf-16be编码 ,它中文占用两个字节,英文也是占用两个字节
当你的字节序列是某种编码时,这时候想把字节序列变成字符串,也需要用这种编码方式,否则会出现乱码。
String str2 = new String(bytes4, "utf-16be");
文本文件就是字节序列,可以是任意编码的字节序列。如果我们在中文机器上直接创建(如果复制过去则可以识别)文本文件,该文本文件只认识ANSI编码。
File类
java.io.File类用于表示文件(目录)
File类只用于表示文件(目录)的信息(名称,大小)等,不能用于文件内容的访问
File file = new File("D:\\javaio"); //System.out.println(file.exists());//文件是否存在 if(!file.exists())//是否存在 file.mkdir();//创建目录 //file.mkdirs多级目录 else file.delete(); System.out.println(file.isDirectory());//是否是目录,是目录返回true System.out.println(file.isFile());//是否是文件 //File file2 = new File("D:\\javaio\\日记1.txt"); File file2 = new File("D:\\javaio", "日记1.txt"); if( !file2.exists()) try{ file2.createNewFile();//创建文件 }catch(IOException e){ e.printStackTrace(); } else file2.delete();//文件删除掉 //常用的file对象的API System.out.println(file);//file.toStrint()的内容 ,目录 System.out.println(file.getAbsolutePath()); System.out.println(file.getName());//文件或目录的名字 System.out.println(file.getParent());//父目录的路径 //列出指定目录下(包括其子目录)的所有文件 public static void listDirectory(File dir) throws IOException { if(!dir.exists())//不存在 { throw new IllegalArgumentException("目录"+dir+"不存在"); } if(!dir.isDirectory())//不是目录 { throw new IllegalArgumentException(dir+"不是目录"); } String[] filenames = dir.list(); //list()方法用于列出当前目录下的子目录和文件名称,字符串数组,包含子目录的名称,不包含子目录下的内容 for(String string : filenames) { System.out.println(string); } //如果要遍历子目录下的内容,就需要构造成file对象做递归操作,File提供了直接返回file对象的API File[] files = dir.listFiles();//返回的是直接子目录(文件)的抽象,不会做递归 if(files != null && files.length > 0) { for( File file : files) { if(file.isDirectory()) { listDirectory(file);//递归操作 } else System.//输出 } } }
java提供的对文件内容的访问,既可以读文件,也可以写文件,支持随机访问文件,可以访问文件的任意位置。
(1)java文件模型: 在硬盘上的文件是byte byte byte存储的,是数据的集合
(2)打开文件有两种模式 "rw"读写 "r"只读
RandomAccessFile raf = new RandomAccessFile(file, "rw");
文件指针,打开文件时,pointer = 0;
(3)写文件
raf.write(int) 只写一个字节(只能写int的后8位),同时指针指向下一个位置,准备再次写入
(4)读方法
int b = raf.read() 读一个字节
(5)文件读写完成后一定要关闭 (可能会有意想不到的错误)
文件读写过程:
File demo = new File("D:\\demo"); if(!demo.exists()) demo.mkdir(); File file = new File(demo, "raf.dat"); if(!file.exists()) file.createNewFile(); RandomAccessFile raf = new RandomAccessFile(file, "rw"); System.out.println(raf.getFilePointer());//获得指针位置,此时值为0 raf.write('A');//写A的后8位写进去,只写了一个字节 System.out.println(raf.getFilePointer());//获得指针位置,此时值位1 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());//获得指针位置,此时为5 //可以直接写一个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));//输出[65, 127, -1, -1, -1, 127, -1, -1, -1, -42, -48] 65是’A‘ String s1 = new String(buf,"gbk"); System.out.println(s1); for(byte b : buf) { System.out.println(Integer.toHexString(b & 0xff) + " "); } raf.close();//关闭文件
字节流,字符流
1.字节流
(1) InputStream OutputStream 抽象类
InputStream 抽象了应用程序读取数据的方式
OutputStream 抽象了应用程序写出数据的方式
(2)EOF = End 读到-1就读到结尾
(3)输入流 键盘输入是输入流
基本方法:
int b = in.read();//读取一个字节无符号填充到int的低8位,其他位补0
in.read(byte[] buf)读取数据填充到字节数组buf
in.read(byte[] buf,int start, int size)读取数据到字节数组buf中,从start位置开始放,最多放多少个
(4)输出流
out.write(int b)写出一个byte到流,写的是b的低8位
out.write(byte[] buf)将字节数组buf都写入到流
out.write(byte[], int start, int size)
(5)子类 FileInputStream -->具体实现了在文件上读取数据
/读取文件内容,按照16进制输出到控制台,并且每输出10个byte换行, 一个一个字节读 public static void printHex(String fileName) throws IOException { FileInputStream in = new FileInputStream(fileName);//把文件作为字节流进行读操作 int b; int i = 1; while((b = in.read()) != -1)//读到一个字节 { System.out.print(Integer.toHexString(b) + " "); if( i ++ % 10 == 0) { System.out.println(); } } in.close(); } //批量读字节 public static void printHexByByteArray(String fileName) throws IOException { FileInputStream in = new FileInputStream(fileName);//把文件作为字节流进行读操作 byte[] buf = new byte[8*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.print("0"); System.out.print(Integer.toHexString(buf[i]) + " "); if( j ++ % 10 == 0) { System.out.println(); } } */ //数组不够大怎么办,解决方法 int bytes = 0; int j = 1; while((bytes = in.read(buf, 0, buf.length)) != -1) { for(int i = 0; i < bytes; i ++) { System.out.print(Integer.toHexString(buf[i] & 0xff)+ " "); if(j++ % 10 == 0) System.out.println(); } } in.close(); }
批量字节读取,对大文件效率高,也是我们最常用的读文件的方式
(6)FileOutputStream实现了向文件中写出byte数据的方法
//如果该文件不存在,则直接创建,如果存在,删除后创建 FileOutputStream out = new FileOutputStream("demo/out.dat"); //如果要追加,则参数为"demo/out.data', true true代表是否追加 out.write('A');//写出了'A'的低8位 out.write('B');//写出了'B'的低8位 int a = 10;//write只能写8位,写int需要写四次 out.write(a >>> 24); out.write(a >>>16); out.write(a >>> 8); out.write(a); byte[] gbk = "中国".getBytes("gbk"); out.write(gbk); out.close();
/将一个文件拷贝到另一个文件 最快的 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(); }
对"流“功能的扩展,可以更加方面的读取int long 字符等类型数据
DataOutStream 来说 writeInt()/ writeDouble / writeUTF()
<span style="white-space:pre"> </span>String file = "demo/dos.dat"; DataOutputStream dos = new DataOutputStream( new FileOutputStream(file)); dos.writeInt(10); dos.writeInt(-10); dos.writeLong(100); dos.writeDouble(10.5); //采用utf-8编码写出 dos.writeUTF("中国"); //以utf-16be编码写出 dos.writeChars("中国"); dos.close(); DataInputStream dis = new DataInputStream(new FileInputStream(file)); int i = dis.readInt();
(8) BufferedInputStream BufferedOutputStream
这两个流类为IO提供了带缓冲区的操作,一般打开文件进行写入或者读取操作时,都会加上缓冲,
这种流模式提高了IO的性能。
//从应用程序中把数据放入文件,相当于把一缸水倒入到另一个缸中
FileOutputStream -->write()相当于一滴一滴得把水”转移“过去
DataOutputStream -->writeXxx()方法会方便一些,相当于一瓢一瓢的转移过去
BufferedOutputStream --->writeXxx()方法更方便,相当于一瓢一瓢水先放入桶中,再从桶中倒入到另一个缸中,性能提高
public static void copyFileByBuffer(File srcFile, File destFile) throws IOException { //利用带缓冲的字节流进行文件的拷贝 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(); }
2)认识文本和文本文件
java的文本(char)是16位无符号整数,是字符的unicode编码(双字节编码)
文件是 byte byte byte.... 的数据序列
文本文件是文本(char)序列按照某种编码方案(utf-8 utf-16be,gbk)序列化为byte的存储结果
3)字符流(Reader Writer) 操作的是文本文件
字符的处理,一次处理一个字符
字符的底层仍然是基本的字节序列。
字符流的基本实现
InputStreamReader 完成byte流解析为char流,按照编码解析
OutputStreamReader 提供char流到byte流,按照编码处理
FileInputStream in = new FileInputStream("d:\\sr.txt"); InputStreamReader isr = new InputStreamReader(in);//默认项目的编码,操作时要写文件的编码 FileOutputStream out = new FileOutputStream("d:\\sx.txt"); OutputStreamWriter osw = new OutputStreamWriter(out); /*int c; while((c = isr.read())!= -1){ System.out.print((char)c); } */ char[] buffer = new char[8 * 1024]; int c; //批量读取,放入buffer这个字符数组,从第0个开始放,最多放buffer.length个 //返回的是读到的字符的个数 while((c = isr.read(buffer, 0,buffer.length))!= -1){ String s = new String(buffer, 0, c); osw.write(buffer, 0, c); System.out.print(s); } osw.close(); isr.close();
FileReader/ FileWriter
FileReader fr = new FileReader("D:\\sr.txt"); FileWriter fw = new FileWriter("D:\\sx.txt");//可以添加一个参数,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();
字符流的过滤器
BufferedReader -->一次读一行
BufferedWriter / PrintWriter --->写一行
//对文件进行读写操作 BufferedReader br = new BufferedReader( new InputStreamReader(new FileInputStream("D:\\sr.txt"))); /*BufferedWriter bw = new BufferedWriter( new OutputStreamWriter(new FileOutputStream("D:\\sx.txt"))); */ PrintWriter pw = new PrintWriter("D:\\sx.txt"); String line; while((line = br.readLine()) != null){ System.out.println(line);//一次读一行,并不能识别换行 //bw.write(line); //单独写出换行操作 //bw.newLine(); //bw.flush(); pw.println(line); pw.flush(); } br.close(); //bw.close(); pw.close();
2)序列化流(ObjectOutStream),是过滤流 ---writeObject方法
反序列化流(ObjectInputStream),---readObject方法
3)序列化接口(Serializable)
对象必须实现序列化接口,才能进行序列化,否则将出现异常
这个接口,没有任何方法,只是一个标准
public class Student implements Serializable { private String stuno; private String stuname; private int stuage; public Student(){} 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 [stuage=" + stuage + ", stuname=" + stuname + ", stuno=" + stuno + "]"; } } public static void main(String[] args) throws IOException, ClassNotFoundException{ String file = "D:\\1.dat"; //1.对象的序列化 /*ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file)); Student stu = new Student("1002", "sr", 12); 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(); }
private transient int stuage;//该元素不会进行jvm默认的序列化, 有些情况下有些变量不需要放在网络上传输
不代表不能序列化,也可以自己完成这个元素的序列化
4)
import java.io.Serializable; import java.util.jar.JarException; public class Student implements Serializable { private String stuno; private String stuname; private transient int stuage;//该元素不会进行jvm默认的序列化 public Student(){} 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 [stuage=" + stuage + ", stuname=" + stuname + ", stuno=" + stuno + "]"; } private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ s.defaultWriteObject();//把jvm能默认序列化的元素进行序列化操作 s.write(stuage);//自己完成stuage的序列化 } private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException{ s.defaultReadObject();//把jvm能默认序列化的元素进行反序列操作 this.stuage = s.readInt();//自己完成stuage的反序列化 } }
5)序列化中 子类和父类构造函数的调用
一个类实现了序列化接口,其子类都可以进行序列化。
对子类对象进行反序列操作时,如果其父类没有实现序列化接口,那么其父类的构造函数会被调用