编码问题
String s ="东南大学";
//字符串转换成字节数组
byte[] bytes = s.getBytes();//使用项目默认编码utf-8
byte[] bytes1 = s.getBytes("gbk");//指定编码
for (byteb:bytes){//把字节以16进制方式显示
System.out.println(Integer.toHexString(b&0xff));
}
//字节数组转换为字符串
String s_utf8=newString(bytes);
String s_gbk =new String(bytes1,"gbk");
//对应编码解码不正确会乱码
System.out.println(s_utf8);
System.out.println(s_gbk);
gbk编码中文占2字节,英文1字节
utf-8中文占3字节,英文1字节
字节序列变成字符串,需要用对应的编码方式,否则会乱码。
File类
File类只用表示文件或目录的信息(名称、路径、大小等),不能用于内容的访问。
import java.io.File;
File file = new File("./testfiles/"); if (!file.exists()){//判断文件是否存在 file.createNewFile();//创建文件 } // file.isDirectory();//判断是否是目录 // file.isFile();//判断是否是文件 // file.mkdir();//只建一级目录 // file.mkdirs();//递归建立多级目录 // file.delete();//删除文件或目录 for(String s:file.list()){//列出目录下文件 System.out.println(s); } for(File f :file.listFiles()){ //常用的File对象的API System.out.println(f);//file.toString()的内容 System.out.println(f.getAbsolutePath()); System.out.println(f.getName()); System.out.println(f.getParent()); System.out.println(f.getParentFile().getAbsolutePath()); }
RandomAccessFile类
RandomAccessFile java提供的对文件内容的访问,既可以读文件,也可以写文件。
RandomAccessFile支持随机访问文件,可以访问文件的任意位置。
File demo = new File("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');//只写了一个字节 System.out.println(raf.getFilePointer());//1 raf.write('B'); 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());//6 //可以直接写一个int raf.writeInt(i); System.out.println(raf.getFilePointer());//10 String s = "中"; byte[] gbk = s.getBytes("gbk"); raf.write(gbk); System.out.println(raf.length());//12 //读文件,必须把指针移到头部 raf.seek(0); //一次性读取,把文件中的内容都读到字节数组中 byte[] buf = new byte[(int)raf.length()]; raf.read(buf); //输出[65, 66, 127, -1, -1, -1, 127, -1, -1, -1, -42, -48](十进制) System.out.println(Arrays.toString(buf)); for (byte b : buf) { //输出41 42 7f ff ff ff 7f ff ff ff d6 d0(十六进制) System.out.print(Integer.toHexString(b & 0xff)+" "); } raf.close();
//读取 RandomAccessFile raf = new RandomAccessFile("demo/raf.dat", "r"); raf.seek(2); int i = 0; int b = raf.read();//读取到一个字节 System.out.println(raf.getFilePointer()); i = i | (b << 24 ); b = raf.read(); i = i | ( b << 16); b = raf.read(); i = i | (b << 8 ); b = raf.read(); i = i | b; System.out.println(Integer.toHexString(i)); raf.seek(2); i = raf.readInt(); System.out.println(Integer.toHexString(i)); raf.close();
IO流
继承Inputstream或Reader的类都有read()方法,用于读取单个字节或者字节数组
继承Outputstream或Writer的类都有writer()方法,用于写单个字节或者字节数组
以上基本方法通常不会用到。
字节流
Inputstream抽象了应用程序读取数据的方式
Outputstream抽象了应用程序写出数据的方式
数据源包括:字节数组,String对象,文件,“管道”其他种类的流组成的序列,Internet连接等。
InputStream
类 | 功能 | 构造器参数 |
ByteArrayInputStream | 将内存的缓冲区当做InputStream使用 | 缓冲区 |
StringBufferInputStream | String转换InputStream | 字符串(StringBuffer) |
FileInputStream | 从文件读取信息 | 字符串,表文件名 |
PipedInputStream | 用于写入PipedOutputStream | PipedOutputStream |
SequenceInputStream | 多个InputStream对象转换成单一InputStream | 两个InputStream对象或容纳InputStream对象的容器Enumeration |
FilterInputStream | 抽象类,作为装饰器的接口 | BufferedInputStream DataInputStream PushbakInputStream |
DataInputStream | 与DataOutputStream搭配,从流读取基本数据类型(int,char,long) | InputStream |
BufferedInputStream | 减少写操作 | InputStream,可以指定缓冲区大小 |
PushbackInputStream | 能弹出一个字节的缓冲区,可以将读到的最后一个字符回退 | InputStream |
OutputStream
类 | 功能 | 构造器参数 |
ByteArrayOutputStream | 在内存中创建缓冲区 | 缓冲区初始化尺寸 |
FileOutputStream | 用于将信息写至文件 | 字符串,表文件名 |
PipedOutputStream | 用于PipedInputStream输出 | PipedInputStream |
FilterOutputStream | 抽象类,作为装饰器 | DataOutputStream PrintStream BufferedOutputStream |
DataOutputStream | 与DataInputStream搭配,从流读取基本数据类型(int,char,long) | OutputStream |
PrintStream | 用于产生格式化输出 DataOutputStream处理数据存储 PrintStream处理显示 | OutputStream |
BufferedOutputStream | 减少写操作 | OutputStream,可以指定缓冲区大小 |
InputStream和OutputStream
try{ byte bWrite[] = {11, 21, 3, 40, 5}; OutputStream os = new FileOutputStream("test.txt"); for (int x = 0; x < bWrite.length; x++) { os.write(bWrite[x]); // writes the bytes } os.close(); InputStream is = new FileInputStream("test.txt"); int size = is.available(); for (int i = 0; i < size; i++) { System.out.print((char) is.read() + " "); } is.close(); } catch (IOException e) { System.out.print("Exception"); }
ByteArrayOutputStream和ByteArrayInputStream
byte[] bytes={1,2,3,127,-127}; byte[] bytes1=new byte[10]; ByteArrayInputStream bais=new ByteArrayInputStream(bytes); for (int i=0;i<5;i++){ System.out.println(bais.read()); } bais.read(bytes1);//读取到字节数组中 for(int i=0;i<10;i++) System.out.println(bytes1[i]); ByteArrayOutputStream baos=new ByteArrayOutputStream(5); baos.write(3); baos.write(4); bytes1=baos.toByteArray(); for (int i = 0; i < bytes1.length; i++) System.out.println(bytes1[i]);
FileInputStream和FileOutputStream
这样效率低,仅仅作为学习
File file = new File("C:\\1.pdf"); FileInputStream fin = new FileInputStream("C:\\1.pdf"); FileOutputStream fout = new FileOutputStream("C:\\222.pdf"); for(int i=0;i<file.length();i++) { fout.write(fin.read()); } FileOutputStream out = new FileOutputStream("testfiles/fos.dat"); out.write('A');//写出了'A'的低八位 out.write('B');//写出了'B'的低八位 int a = 0Xabcd1234;//write只能写八位,那么写一个int需要些4次每次8位 out.write(a >>> 24); out.write(a >>> 16); out.write(a >>> 8); out.write(a); byte[] gbk = "你好".getBytes("utf8"); out.write(gbk); out.close(); //此时fos.dat文件中十六进制表示为 //41 42 ab cd 12 34 e4 bd a0 e5 a5 bd
DataInputStream和DataOutputStream
String file = "testfiles/dos.dat"; DataOutputStream dos = new DataOutputStream( new FileOutputStream(file)); dos.writeInt(10); dos.writeInt(-10); dos.writeLong(10l); dos.writeDouble(10.5); //采用utf-8编码写出 dos.writeUTF("你好"); dos.close(); DataInputStream dis = new DataInputStream( new FileInputStream(file)); int i = dis.readInt(); System.out.println(i); i = dis.readInt(); System.out.println(i); long l = dis.readLong(); System.out.println(l); double d = dis.readDouble(); System.out.println(d); String s = dis.readUTF(); System.out.println(s); dis.close();
BufferedInputStream和BufferedOutputStream
BufferedInputStream bis = new BufferedInputStream( new FileInputStream("testfiles/text1.txt")); BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream("testfiles/text2.txt")); byte[] buffer = new byte[1024]; int c ; while((c = bis.read(buffer,0,buffer.length))!=-1){ bos.write(buffer,0,c); bos.flush(); } bis.close(); bos.close();
字符流
InputStreamReader和OutputStreamWriter
InputStreamReader完成byte流解析为char流,按照编码解析。
OutputStreamWriter完成char流到byte流,按照编码处理。
InputStreamReader isr = new InputStreamReader( new FileInputStream("test1.txt"),"utf-8");//使用文件编码读 OutputStreamWriter osw = new OutputStreamWriter( new FileOutputStream("test2.txt"),"utf-8");//写入文件的编码,可以为其他编码,如gbk
FileInputStream in = new FileInputStream("testfiles/text1.txt"); InputStreamReader isr = new InputStreamReader(in,"utf-8");//默认项目的编码,操作的时候,要写文件本身的编码格式 FileOutputStream out = new FileOutputStream("testfiles/text2.txt"); OutputStreamWriter osw = new OutputStreamWriter(out,"utf-8"); /*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); System.out.print(s); osw.write(buffer,0,c); osw.flush(); } isr.close(); osw.close();
BufferedReader和BufferedWriter
BufferedReader 一次读一行readLine
BufferedWriter 一次写一行write();+ newline();//换行
//对文件进行读写操作 BufferedReader br = new BufferedReader( new InputStreamReader( new FileInputStream("testfiles/file1"),"utf8")); BufferedWriter bw = new BufferedWriter( new OutputStreamWriter( new FileOutputStream("testfiles/file2"))); PrintWriter pw = new PrintWriter("testfiles/file3"); //可以自动flush,第一个参数则为输出流 //PrintWriter pw1 = new PrintWriter(outputStream,boolean autoFlush); 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();
FileReader和FileWriter
FileReader fr = new FileReader("testfiles/text1.txt"); FileWriter fw = new FileWriter("testfiles/text2.txt"); //FileWriter fw = new FileWriter("testfiles/text2.txt",true); char[] buffer = new char[1024]; int c ; while((c = fr.read(buffer,0,buffer.length))!=-1){ fw.write(buffer,0,c); fw.flush(); } fr.close(); fw.close();
应用(文件拷贝)
栗子参考慕课网(https://www.imooc.com/learn/123)
IOUtil.java
public class IOUtil { /** * 读取指定文件内容,按照16进制输出到控制台 * 并且每输出10个byte换行 * @param fileName * 单字节读取不适合大文件,大文件效率很低 */ 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(); } } in.close(); } /** * 批量读取,对大文件而言效率高,也是我们最常用的读文件的方式 * @param fileName * @throws IOException */ 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++){ System.out.print(Integer.toHexString(buf[i] & 0xff)+" "); 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(); } /** * 文件拷贝,字节批量读取 * @param srcFile * @param destFile * @throws IOException */ 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(); } /** * 进行文件的拷贝,利用带缓冲的字节流 * @param srcFile * @param destFile * @throws IOException */ 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(); } /** * 单字节,不带缓冲进行文件拷贝 * @param srcFile * @param destFile * @throws IOException */ public static void copyFileByByte(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); int c ; while((c = in.read())!=-1){ out.write(c); out.flush(); } in.close(); out.close(); } }
测试
long start = System.currentTimeMillis(); IOUtil.copyFileByByte(new File("C:\\Users\\SUN\\Desktop\\1.pdf"), new File("C:\\Users\\SUN\\Desktop\\2.pdf")); //两万多毫秒 long end = System.currentTimeMillis(); System.out.println(end - start ); start = System.currentTimeMillis(); IOUtil.copyFileByBuffer(new File("C:\\Users\\SUN\\Desktop\\1.pdf"), new File("C:\\Users\\SUN\\Desktop\\3.pdf"));//一万多毫秒*/ end = System.currentTimeMillis(); System.out.println(end - start ); start = System.currentTimeMillis(); IOUtil.copyFile(new File("C:\\Users\\SUN\\Desktop\\1.pdf"), new File("C:\\Users\\SUN\\Desktop\\3.pdf"));//7毫秒 end = System.currentTimeMillis(); System.out.println(end - start );
对象的序列化、反序列化
对象序列化,将object转换为byte序列,反正为反序列化
序列化流ObjectOutputStream——writeObject()
反序列化流ObjectInputStream——readObject()
需要实现序列化接口(Serializable)
注意transient修饰的元素不会进行jvm默认的序列化,可以自己手动进行序列化,但手动序列化和反序列化顺序要一致。
如public transientint age;//在序列化时不会写入age值。
对象在反序列化时不需要调用构造方法。
Student.java
public class Student implements Serializable { private String stuno; private String stuname; //该元素不会进行jvm默认的序列化,也可以自己完成这个元素的序列化 private transient int stuage; private transient int stuscore; public Student(String stuno, String stuname, int stuage, int stuscore) { super(); this.stuno = stuno; this.stuname = stuname; this.stuage = stuage; this.stuscore = stuscore; } @Override public String toString() { return "Student [stuno=" + stuno + ", stuname=" + stuname + ", stuage=" + stuage + ", stuscore=" + stuscore + "]"; } private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { s.defaultWriteObject();//把jvm能默认序列化的元素进行序列化操作 s.writeInt(stuage);//自己完成stuage的序列化 s.writeInt(stuscore); } private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject();//把jvm能默认反序列化的元素进行反序列化操作 this.stuscore = s.readInt(); this.stuage = s.readInt();//自己完成stuage的反序列化操作 } }
ObjectSeriaDemo.java
public class ObjectSeriaDemo1 { public static void main(String[] args) throws Exception{ String file = "demo/obj.dat"; //1.对象的序列化 ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream(file)); Student stu = new Student("10002", "豆子", 18,100); oos.writeObject(stu); oos.flush(); System.out.println(stu); oos.close(); ObjectInputStream ois = new ObjectInputStream( new FileInputStream(file)); Student stu1 = (Student)ois.readObject(); System.out.println(stu1); ois.close(); } }
这里输出为:
Student [stuno=10002, stuname=豆子, stuage=18, stuscore=100] Student [stuno=10002, stuname=豆子, stuage=100, stuscore=18]
因为手动序列化顺序不一致,所以相反,要注意。
常见的序列化协议:COM、CORBA、XML&SOAP、JSON、Thrift、Protobuf、Avro。
l 保护性地编写readObject方法。readObject方法实际上相当于另一个公有的构造器,如同其他构造器一样,它也要求注意检查参数的有效性,并且在必要的时候对参数进行保护性拷贝。