java IO流总结

IO流

IO流的整体内容:
1.java.io.File类的使用
2.IO原理及流的分类
3.文件流: FileInputStream / FileOutputStream / FileReader / FileWriter
4.缓冲流:BufferedInputStream / BufferedOutputStream / BufferedReader / BufferedWriter
5.转换流: InputStreamReader / OutputStreamWriter
6.对象流 ----涉及序列化、反序列化: ObjectInputStream / ObjectOutputStream

File类

java.io.File类:文件和目录路径名的抽象表示形式,与平台无关
File 能新建、删除、重命名文件和目录,但 File 不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流。

File对象可以作为参数传递给流的构造函数
File类的常见构造方法:
public File(String pathname)
以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。

public File(String parent,String child)
以parent为父路径,child为子路径创建File对象。

File file1 = new File("d:\\io\\helloworld.txt");
File file2 = new File("d:\\io\\io1");

File的静态属性String separator存储了当前系统的路径分隔符。
在UNIX中,此字段为‘/’,在Windows中,为‘\’
String separator = File.separator;
System.out.println(separator);//输出""

File类的常用方法

	File f1 = new File("d:\\hello.txt"); // 绝对路径
	File f2 = new File("abc.txt"); // 相对路径
	File f3 = new File("d:/aa/cc/dd"); // 绝对路径
	File f4 = new File("d:/io/io1"); // 绝对路径
	File f5 = new File("d:/");
	@Test
	public void test6(){
		File f = new File("d:/abc.txt");
		File ff = new File("d:/abc/bcd");
		try {
			f.createNewFile();  // 创建文件
			ff.mkdirs();   //创建目录 ,如果父集的目录不存在的话同时创建
			//ff.mkdir();			//mkdir不会创建父级目录
			f.delete();  //文件删除
			ff.delete();  //目录的删除 
		} catch (IOException e) {
			e.printStackTrace();
		}
	}


@Test
	public void test5(){
		/*mkDir()
		mkDirs()
		list()
		listFiles()*/
		System.out.println(f3.mkdir());
		System.out.println(f4.mkdirs());
		String[] list = f5.list();  //文件或目录的名字
		System.out.println(list[0]);
		File[] lf = f5.listFiles();//listFiles() 返回一个抽象路径名数组,表示由该抽象路径名表示的目录中的文件
		System.out.println(lf[0].getName());
	}


@Test
	public void test4() throws Exception{
		/*createNewFile()
		delete()*/
		File f1 = new File("d:\\hello.txt"); 
		boolean bool = f1.createNewFile();//createNewFile() 当且仅当具有该名称的文件尚不存在时,原子地创建一个由该抽象路径名命名的新的空文件。
		System.out.println(bool);
		boolean del = f1.delete();
		System.out.println(del);   //该删除不会进入回收站
	}


@Test
	public void test3(){
		/*lastModified()
		length()*/
		System.out.println(new Date(f1.lastModified()));  //返回此抽象路径名表示的文件上次修改的时间。long类型的时间Sat Mar 23 11:02:01 CST 2019
		System.out.println(f1.length());//返回文件的字节的长度
		//System.out.println(System.currentTimeMillis());  //当前
	}


	@Test 
	public void test2(){
		// 创建File对象
		File f1 = new File("d:\\aaa.txt"); // 绝对路径
		File f2 = new File("abc.txt"); // 相对路径
		File f3 = new File("d:/aa/cc"); // 绝对路径
		/*
		 * exists() //判断文件或目录 是否存在 
		 * canWrite() 
		 * canRead() 
		 * isFile() 
		 * isDirectory()
		 */
		System.out.println(f1.exists()?"aaa.txt存在":"不存在");
		System.out.println(f1.canWrite());//是否可写
		System.out.println(f1.canRead());
		System.out.println(f1.isFile()?"是文件":"目录");
		System.out.println(f3.isDirectory()?"目录":"是文件");

	}


@Test
	public void test1() {
		// 创建File对象
		File f1 = new File("d:\\hello.txt"); // 绝对路径
		File f2 = new File("abc.txt"); // 相对路径
		File f3 = new File("d:/aa/cc"); // 绝对路径
		// 属性
		System.out.println(File.pathSeparator); // ;
		System.out.println(File.separator); // 重 \
		System.out.println("---------------------------------------------------");
		// 操作
		/*
		 * getName() getPath() getAbsoluteFile() getAbsolutePath() getParent()
		 * renameTo(File newName)
		 */ // 改名 原文件必须存在,目标文件不能存在(同一个目录中)
		System.out.println(f1.getName());
		System.out.println(f1.getPath());
		System.out.println(f1.getAbsoluteFile());
		System.out.println(f1.getAbsolutePath());
		System.out.println(f1.getParent());
		System.out.println(f1.renameTo(new File("d:/aaa.txt")) ? "修改成功" : "修改失败");
		System.out.println("---------------------------------------------------");
	}
 结果如下:
 ;
\
---------------------------------------------------
hello.txt
d:\hello.txt
d:\hello.txt
d:\hello.txt
d:\
修改成功
---------------------------------------------------

IO流 体系

输入流
InputStream 和 Reader 是所有输入流的基类。
InputStream(典型实现:FileInputStream)
int read()
int read(byte[] b)
int read(byte[] b, int off, int len)

Reader(典型实现:FileReader)
int read()
int read(char [] c)
int read(char [] c, int off, int len)

程序中打开的文件 IO 资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件 IO 资源。

输出流
OutputStream :
void write(int b/int c);
void write(byte[] b/char[] cbuf);
void write(byte[] b/char[] buff, int off, int len);
void flush();
void close(); 需要先刷新,再关闭此流

Writer
因为字符流直接以字符作为操作单位,所以 Writer 可以用字符串来替换字符数组,即以 String 对象作为参数
void write(String str);
void write(String str, int off, int len);

注意:
1.从硬盘中读入一个文件,要求此文件一定得存在。若不存在,报FileNotFoundException的异常
2.从程序中输出一个文件到硬盘,此文件可以不存在。若不存在,就创建一个实现输出。若存在,则将已存在的文件覆盖
3.真正开发时,就使用缓冲流来代替节点流
4.主要最后要关闭相应的流。先关闭输出流,再关闭输入流。将此操作放入finally
缓冲流(注意这是 重点 开发中经常使用)

//用BufferedInputStream和BufferedOutputStream实现文件的复制
@Test
	public void test2(){
		long start = System.currentTimeMillis();
		String src="d:/111.jpg";  //源文件
		String dest="d:/222.jpg";  //目标文件
		copyFile(src,dest);
		long end = System.currentTimeMillis();
		System.out.println("复制所用的时间:"+(end-start));  //4
	}
	public void copyFile(String src,String dest){
		BufferedInputStream bis = null;
		BufferedOutputStream bos = null;
		try {
			FileInputStream fis = new FileInputStream(src);
			FileOutputStream fos = new FileOutputStream(dest);
			bis = new BufferedInputStream(fis);
			bos = new BufferedOutputStream(fos);
			//创建 一个字节数组 
			byte[] b = new byte[1024];//这里一般定义1024
			int len;
			while((len=bis.read(b))!=-1){//字节数组读
				bos.write(b, 0, len);//字节数组写
				bos.flush();//释放缓存
			}
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (bos != null)//这里需要判断是否为空
				try {
					bos.close();//后打开的文件流先关
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			if (bis != null)
				try {
					bis.close();//先打开的流后关
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		}
	}

用BufferedReader 和BufferedWriter完成复制工作
@Test
	public void test3(){
		BufferedReader br = null;
		BufferedWriter bw = null;
		try {
			FileReader fr = new FileReader("hello.txt");
			FileWriter fw = new FileWriter("abcd2.txt");
			br = new BufferedReader(fr);
			bw = new BufferedWriter(fw);
			//创建 一个字符数组 
			char[] c = new char[1024];
            			//效率低
                			/*int len;
                			while((len=br.read(c))!=-1){
                				bw.write(c, 0, len);
                			}*/
			//效率高
			String str;
			while((str=br.readLine())!=null){//整行读返回String
				bw.write(str);  //写一行  第二行
				bw.newLine();   //换行
				bw.flush();  //刷新到文件
			}
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (bw != null)
				try {
					bw.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			if (br != null)
				try {
					br.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		}
	}

转换流用于将字节流中读取到的字节按指定字符集解码成字符。
InputStreamReader
构造方法
public InputStreamReader(InputStream in)
public InputSreamReader(InputStream in,String charsetName)//InputStreamReader(System.in,”ISO5334_1”);可以规定字符编码格式

OutputStreamWriter 用于将要写入到字节流中的字符按指定字符集编码成字节。需要和OutputStream“套接”。
构造方法
public OutputStreamWriter(OutputStream out)
public OutputSreamWriter(OutputStream out,String charsetName)

常用的写法:和FileInputStream  FileOutputStream连用
public void testMyInput() throws Exception{
    FileInputStream fis = new FileInputStream("dbcp.txt");
    FileOutputStream fos = new FileOutputStream("dbcp5.txt");

    InputStreamReader isr = new InputStreamReader(fis,"GBK");
    OutputStreamWriter osw = new OutputStreamWriter(fos,"GBK");

    BufferedReader br = new BufferedReader(isr);
    BufferedWriter bw = new BufferedWriter(osw);

    String str = null;
    while((str = br.readLine()) != null){
        bw.write(str);
        bw.newLine();
        bw.flush();
}   
 bw.close(); 
 br.close();}

这里涉及到编码的格式:
编码表的由来
计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字。就将各个国家的文字用数字来表示,并一一对应,形成一张表。这就是编码表。
常见的编码表
ASCII:美国标准信息交换码。用一个字节的7位可以表示。
ISO8859-1:拉丁码表。欧洲码表用一个字节的8位表示。
GB2312:中国的中文编码表。
GBK:中国的中文编码表升级,融合了更多的中文文字符号。
Unicode:国际标准码,融合了多种文字。所有文字都用两个字节来表示,Java语言使用的就是unicode
UTF-8:最多用三个字节来表示一个字符。
标准输入输出流
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)

应用标准的输入流封装自己的键盘输入程序
public class MyScanner {
	//用户可以输入字符串
	public String nextString(){
		InputStreamReader isr = new InputStreamReader(System.in);//System.in是InputStream
		BufferedReader br =new BufferedReader(isr);
		String str="";
		try {
			str = br.readLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return str;
	}
	//输入整形数据
	public int nextInt(){
		return Integer.parseInt(nextString());
	}
	//boolean
	public boolean nextBoolean(){
		return Boolean.parseBoolean(nextString());
	}
}
//测试
public class TestScanner {
	public static void main(String[] args) {
		MyScanner input = new MyScanner();
		System.out.println("请输入姓名:");
		String name = input.nextString();
		System.out.println(name);
	}
}

想起点知识贴在这里
System.out.println(Boolean.parseBoolean(“false”));//false
System.out.println(Boolean.parseBoolean(“true”));//true
System.out.println(Boolean.parseBoolean(“qqq”));//false
System.out.println(Boolean.parseBoolean(“trueqqq”));//false

从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续进行输入操作,直至当输入“e”或者“exit”时,退出程序。
System.out.println("请输入信息(退出输入e或exit):");
//把"标准"输入流(键盘输入)这个字节流包装成字符流,再包装成缓冲流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));//记住这种用法,将标准的额输入流转换成BufferedReader
String s = null;
try {
        while ((s = br.readLine()) != null) {  //读取用户输入的一行数据 --> 阻塞程序
                if (s.equalsIgnoreCase("e") || s.equalsIgnoreCase("exit")) {
                	 System.out.println("安全退出!!");
        		break;
                }
        	//将读取到的整行字符串转成大写输出
            System.out.println("-->:"+s.toUpperCase());
            System.out.println("继续输入信息");
        }	
} catch (IOException e) {
    	e.printStackTrace();
    	} finally { 
        	try {
            	if (br != null) {
            	br.close();  //关闭过滤流时,会自动关闭它包装的底层节点流
                    }
    	} catch (IOException e) {
            	e.printStackTrace();
             }	
}

打印流
在整个IO包中,打印流是输出信息最方便的类。
PrintStream(字节打印流)和PrintWriter(字符打印流)提供了一系列重载的print和println方法,用于多种数据类型的输出
PrintStream和PrintWriter的输出不会抛出异常
PrintStream和PrintWriter有自动flush功能
System.out返回的是PrintStream的实例
PrintStream 打印字节输出;流
PrintStream可以接受文件和其他字节输出流,所以打印流是对普通字节输出流的增强,其中定义了很多的重载的print()和println(),方便输出各种类型的数据。
PrintStream:是一个字节打印流,System.out对应的类型就是PrintStream。
它的构造函数可以接收三种数据类型的值。
1,字符串路径。
PrintStream(String filename)//使用指定的文件名创建新的打印流,就是讲指定的内容打印到这个指定文件中

2,File对象。
PrintStream(File file);//使用指定的file 对象创建新的打印流,就是讲指定的内容打印到这个指定文件中
3,OutputStream。
public PrintStream(OutputStream out)//创建一个新的打印流。 此流不会自动刷新。

public PrintStream(OutputStream out,boolean autoFlush)//创建打印流,自动刷新

注意: 打印流的三种方法
void print(数据类型 变量)
println(数据类型 变量)
printf(String format, Object… args)

可以自定数据格式
print 和println方法的区别在于,一个换行一个不换行
print 方法和write方法的却别在于,print提供自动刷新.
普通的write方法需要调用flush或者close方法才可以看到数据.
JDK1.5之后Java对PrintStream进行了扩展,增加了格式化输出方式,可以使用printf()重载方法直接格式化输出。但是在格式化输出的时候需要指定输出的数据类型格 式。%d, %s, %f, %lf, %c
PrintStream在实际中主要有下面两个用处
1、把任意类型的数据自动转换为字符串输出,相当的方便。
2、方便收集异常日志。
现在说一下一直使用了那么久的System.out到底是什么,api文档直接查看可以知道System.out就是System内部维护的一PrintStream的对象,默认情况这个标准输出流,把内容输出到控制台,你完全可以使用System.setOut方法重新制定标准输出的位置,只需要传入一个相应的PrintStream参数即可。
//重置标准输出流的位置
System.setOut(printStream);
收集异常
//收集异常的日志信息 默认情况下异常信息打印到控制台,产品部署到服务器上。而控制台上只能打印部分日志信息,会丢失很多。
下边有几个例子

使用PrintStream流复制文件
import java.io.*;
class IODemo
{
	public static void main(String[] args) 
	{
		try
		{
		FileReader fr=new FileReader("a.txt");
		BufferedReader br=new BufferedReader(fr);
		FileWriter fw=new FileWriter("33.txt");
		PrintWriter pw=new PrintWriter(fw,true);//设置打印流并设置自动刷新

		String s=br.readLine();
			while(null!=s)
			{
				//PrintWriter的println方法 相当于
				//BufferedWriter 的write() + newLine()+flush()
				pw.println(s);//这里完成了输出换行和刷新
				s=br.readLine();
			}
			br.close();
			pw.close();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		
	}
}
重定向System.out的标准输出的方向
class Test {
 public static void main(String[] args) {
		try {
				//System.out.println("java");//这一句相当于下边的两句,因为System.out就是一个PrintStream的对象,相当于一个对象调用println()
				//PrintStream ps = System.out;
				//ps.println("java");
				//对流进行转向,使得后面的标准输出输出到文件日志中,这种方法通常被用来记录日志。
                            //这样设置之后就代表以后的System.out的输出流指向了PrintStream对象的指定目录,即"F:/log"
				System.setOut(new PrintStream(new FileOutputStream("F:/log")));//每次的输出会覆盖上一次的内容
                           // System.setOut(new PrintStream(new FileOutputStream("F:/log",true)));//这样就具有了追加的功能,
				System.out.println("log"+System.currentTimeMillis());//将括号内的内容输出到了F:/log,而且加上了换行和自动的刷新
				method1();//方法中的打印也要打印到F:/log
				System.out.println("log"+System.currentTimeMillis());将括号内的内容输出到了F:/log,而且加上了换行和自动的刷新
		} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
}
public static void method1() {
	System.out.println("execute method1");
	}
}

这就是
常用的写日志文件的方法,先将System.out的标准输出流重新定向到指定的PrintStream对象指定的目录
之后调用System.out.println()直接打印内容就可以输出到文件中了
但是这时候有一个问题.就是每次运行之后上次的文件就会被覆盖,怎么办呢?
OutputStream的构造器中裤增加追加的功能,按照下面这样写就可以实现每次追加的功能了
System.setOut(new PrintStream(new FileOutputStream(“F:/log”,true)))

关于PrintStream的自动刷新功能
下边PrintStream的 Write(char[] char)方法源码
private void write(char buf[]) {
        try {
            synchronized (this) {
                ensureOpen();
                textOut.write(buf);
                textOut.flushBuffer();
                charOut.flushBuffer();
                if (autoFlush) {
                    for (int i = 0; i < buf.length; i++)
                        if (buf[i] == '\n')
                            out.flush();//如果设置自动刷新且检测到换行符号的时候才会自动刷新
                }
            }
        }
        catch (InterruptedIOException x) {
            Thread.currentThread().interrupt();
        }
        catch (IOException x) {
            trouble = true;
        }
    }
    
  // println(boolean x)源码  println都调用了newLine所以println都会自动刷新
      public void println(boolean x) {//
        synchronized (this) {
            print(x);
            newLine();
        }
    }
    //newLine()源码
     private void newLine() {
        try {
            synchronized (this) {
                ensureOpen();
                textOut.newLine();
                textOut.flushBuffer();
                charOut.flushBuffer();
                if (autoFlush)
                    out.flush();//只要设置好了自动刷新就会自动刷新
            }
        }
        catch (InterruptedIOException x) {
            Thread.currentThread().interrupt();
        }
        catch (IOException x) {
            trouble = true;
        }
    }
    //print 的源码
     public void print(int i) {
        write(String.valueOf(i));//调用了Write方法
    }

总结如下:PrintStream构造器中的自动刷新是这个意思:当设置了自动刷新之后调用Println和newLine方法的时候是会自动进行刷新的,
但是调用print和Write的方法的时候必须接受一个"\n"换行符号且设置自动刷新为true之后才能进行刷新

关于PrintWriter自动刷新的功能的详解
今天简单的谈谈java语言中IO流中的打印流的使用中的自动刷新。希望能给大家打来帮助,特别针对初学者。
首先,大家都知道这样的打印流的特性及优点吧:
(1) 打印流都是输出流
(2)打印流是节点流
(3)打印流具有字符与字节的转换功能
(4)打印流可以自动刷新(是真的自动吗?)
现在我们就讨论打印流到底是不是真的自动刷新:(注:以PrintWriter为例)
首先我们先看看打印流的底层部分代码

首先截取的是PrintWriter类中的用来判断是否可以自动刷新的私有属性,看到这个应该可以得到在构造器时会使用这个属性
private final boolean autoFlush;
好现在展示构造器的代码


构造器一: 
	public PrintWriter (Writer out) {
        this(out, false);
    	}
	
构造器二:
public PrintWriter(Writer out,boolean autoFlush) {
        super(out);
        this.out = out;
        this.autoFlush = autoFlush;
        lineSeparator = java.security.AccessController.doPrivileged(
        new sun.security.action.GetPropertyAction("line.separator"));
    }
以上是PrintWriter的两个构造器。 由此可以看出如果调用构造器一的时候自动刷新的属性等于false 说明该构造器不带有自动刷新的功能。
调用构造器二时需要指定他是否自动刷新,true 自动刷新,false 不自动刷新。

看到PrintWriter类的截取的部分代码是否明白点了呢。
但是重要的来了:是谁到底使用了这个autoFlush属性呢,只要使用构造器二并且指定为true所有的输出打印方法就可以自动刷新了吗?

其实并并不然。在构造器二中有这样的注释:

 /**
     * Creates a new PrintWriter.
     *
     * @param  out        A character-output stream
     * @param  autoFlush  A boolean; if true, the <tt>println</tt>,
     *                    <tt>printf</tt>, or <tt>format</tt> methods will
     *                    flush the output buffer
     */
所以说只有println,printf,format才会执行自动刷新。其实我认为它还缺少一个方法没说 :newLine();----->println()方法可以实现自动刷新其实是调用了newLine()方法;
口说无凭源码为证:

先说println()和newLine()public void println(String x) {
        synchronized (lock) {
            print(x);
            println();
        }
   }
public void println() {
        newLine();
    }
 private void newLine() {
        try {
            synchronized (lock) {
                ensureOpen();
                out.write(lineSeparator);
                if (autoFlush)
                    out.flush();
            }
        }
        catch (InterruptedIOException x) {
            Thread.currentThread().interrupt();
        }
        catch (IOException x) {
            trouble = true;
        }
    }
    //print源码
      public void print(String s) {
        if (s == null) {
            s = "null";
        }
        write(s);//调用了write方法
    }
    //write源码中压根没有提到自动刷新的事,也就是说自动刷新和write还有那个print没关系
     public void write(String s) {
        write(s, 0, s.length());
    }
    public void write(String s, int off, int len) {
        try {
            synchronized (lock) {
                ensureOpen();
                out.write(s, off, len);
            }
        }
        catch (InterruptedIOException x) {
            Thread.currentThread().interrupt();
        }
        catch (IOException x) {
            trouble = true;
        }
    }

所以说println()是因为调用newLine()才实现刷新功能的
对于printf,format和println与newLine关系相同。 format中有刷新功能,而printf的刷新是调用了format方法实现的。

PrintStream 同理可知,就不累述了。

总结:所以说PrintWriter的自动刷新功能是在使用 public PrintWriter(Writer out,boolean autoFlush)构造器,autoFlush为true,并且在使用println newLine printf format 才会实现。

总结:所以说PrintWriter的自动刷新功能是在使用 public PrintWriter(Writer out,boolean autoFlush)构造器,autoFlush为true,并且在使用println newLine printf format 才会实现。其他的write和print是没有自动刷新的功能的
PrintWriter
是一个字符打印流。构造函数可以接收四种类型的值。
1,字符串路径。
public PrintWriter(String fileName)throws FileNotFoundException
使用指定的文件名创建一个新的PrintWriter,而不需要自动执行行刷新。

2,File对象。
public PrintWriter(File file)throws FileNotFoundException
使用指定的文件创建一个新的PrintWriter,而不需要自动的线路刷新。

public PrintWriter(File file, String csn)throws FileNotFoundException,UnsupportedEncodingException
使用指定的文件和字符集创建一个新的PrintWriter,而不需要自动进行线条刷新。

对于1,2类型的数据,还可以指定编码表。也就是字符集。
3,OutputStream
public PrintWriter(OutputStream out)
从现有的OutputStream创建一个新的PrintWriter,而不需要自动线路刷新

public PrintWriter(OutputStream out,boolean autoFlush)
从现有的OutputStream创建一个新的PrintWriter。

4,Writer
public PrintWriter(Writer out)
创建一个新的PrintWriter,没有自动线冲洗。

public PrintWriter(Writer out, boolean autoFlush)
创建一个新的PrintWriter。

对于3,4类型的数据,可以指定自动刷新。
注意:该自动刷新值为true时,只有三个方法可以用:println,printf,format.
如果想要既有自动刷新,又可执行编码。如何完成流对象的包装?
PrintWrter pw =new PrintWriter(new OutputSteamWriter(new FileOutputStream(“a.txt”),“utf-8”),true);

如果想要提高效率。还要使用打印方法。
PrintWrter pw =newPrintWriter(new BufferdWriter(new OutputSteamWriter(newFileOutputStream(“a.txt”),“utf-8”)),true);

下边有个案例使用了printWriter

public class javaTest {
   
    public static void main(String[] args) throws IOException  {
        /*
         * 创建输出流,将信息写入指定的文件中
         */
        OutputStream os=new FileOutputStream("C:\\Users\\qinghuang\\Desktop\\io1.txt");//这样写没有追加的功能,每次覆盖
     //   OutputStream os=new FileOutputStream("C:\\Users\\qinghuang\\Desktop\\io1.txt",true);//这样就能每次想文件中追加信息了
        PrintWriter pw=new PrintWriter(os);
        pw.write("小帅哥");
        pw.append(" 你真帅~");
        pw.println("我稀罕你");
        pw.write("我爱你");
        //输出流需要在读取之前关闭保存
        pw.close();//这里其实相等于进行了刷新
        os.close();      
        
        /*
         * 创建输入流,将信息读到控制台
         */
        InputStream is=new FileInputStream("C:\\Users\\qinghuang\\Desktop\\io1.txt");
        InputStreamReader isr=new InputStreamReader(is);
        BufferedReader br=new BufferedReader(isr);
        
        String info=null;
        info=br.readLine();
        while(info!=null){
            System.out.println(info);
            info=br.readLine();
        }               
        br.close();
        isr.close();
        is.close();          
    }
}

运行结果
运行结果:
 1 小帅哥 你真帅~我稀罕你 2 我爱你 
io2.txt文档中为:
小帅哥 你真帅~我稀罕你
我爱你
public static void main(String[] args) throws IOException  {

		        /*
		         * 创建输出流,将信息写入指定的文件中
		         */
		        OutputStream os=new FileOutputStream("d:io5.txt",true);
		        PrintWriter pw=new PrintWriter(os,true);//设置了自动刷新,
		        pw.println("我稀罕你");//这个会进行自动的刷新
		        pw.write("小帅哥");//这个不会进行自动刷新
		        pw.append(" 你真帅~");//这个不会进行自动刷新
		        pw.write("我爱你");//这个不会进行自动刷新
		        //输出流需要在读取之前关闭保存
		     //   pw.close();
		     //   os.close();      
		        
		        /*
		         * 创建输入流,将信息读到控制台
		         */
		        InputStream is=new FileInputStream("io5.txt");
		        InputStreamReader isr=new InputStreamReader(is);
		        BufferedReader br=new BufferedReader(isr);
		        
		        String info=null;
		        info=br.readLine();
		        while(info!=null){
		            System.out.println(info);
		            info=br.readLine();
		        }               
		        br.close();
		        isr.close();
		        is.close();          
		    }
		}
运行结果每一次只能输出"我稀罕你"因为只有println进行了刷新缓存

对象流
什么是对象流
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

序列化的步骤
若某个类实现了 Serializable 接口,该类的对象就是可序列化的:
创建一个 ObjectOutputStream
调用 ObjectOutputStream 对象的 writeObject(对象) 方法输出可序列化对象。注意写出一次,操作flush()
注意:如果某个类的属性不是基本数据类型或 String 类型,而是另一个引用类型,那么这个引用类型必须是可序列化的,否则拥有该类型的 Field 的类也不能序列化
反序列化的步骤
创建一个 ObjectInputStream
调用 readObject() 方法读取流中的对象
序列化和反序列化的实例

public class TestObjectStream {
	//反序列化
	@Test
	public void test2(){
		ObjectInputStream ois=null;
		try {
			FileInputStream fis = new FileInputStream(new File("d:/aa.abc"));
			ois = new ObjectInputStream(fis);
			//如果不是同一个对象 
			/*Object obj = ois.readObject();
			if(obj instanceof Person){
				Person p=(Person)obj;
				System.out.println(p);
			}*/
			
			//反序列化
			Person p=(Person)ois.readObject();
			Person p2=(Person)ois.readObject();
			System.out.println(p);
			System.out.println(p2);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}finally {
			if(ois!=null)
				try {
					ois.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		}	
	}
	//把对象存到文件
	@Test
	public void test1(){
		//创建对象 (接下来要存储对象中的信息)
		Person p=new Person("张三",23,new Dog("小花"));
		Person p2=new Person("李四",22,new Dog("小明"));
		//创建序列化的对象 
		ObjectOutputStream oos =null;
		try {
			//保存文件名可以是txt,doc..,但不建议建议是一个别人打不开的类型文件
			FileOutputStream fis = new FileOutputStream("d:/aa.abc");
			 oos =new ObjectOutputStream(fis);
			oos.writeObject(p);	
			oos.writeObject(p2);
			oos.flush();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if(oos!=null)
				    oos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
//人类
class Person implements Serializable{
	private static final long serialVersionUID = 1231421341324123L;
	String name;
	int age;
	Dog dog;//这里有一个类对象
	public Person() {
		super();
	}
	public Person(String name, int age,Dog dog) {
		super();
		this.name = name;
		this.age = age;
		this.dog=dog;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + ", dog=" + dog + "]";
	}	
}
//该类也要实现Serializable接口
class Dog implements Serializable{
	private static final long serialVersionUID = -1491479613572200499L;
	String nickname;
	public Dog() {
		super();
	}
	public Dog(String nickname) {
		super();
		this.nickname = nickname;
	}
	@Override
	public String toString() {
		return "Dog [nickname=" + nickname + "]";
	}
	
	
}

6.序列化的过程中有一个问题,就是当多次将不同对象序列化之后(追加),反序列化的时候有问题,具体情况之后搞清楚
RandomAccessFile 类
RandomAccessFile 类支持 “随机访问” 的方式,程序可以直接跳到文件的任意地方来读、写文件
支持只访问文件的部分内容
可以向已存在的文件后追加内容
支持从文件的开头读取、写入。若输出的文件不存在,直接创建。若存在,则是对原有文件内容的覆盖。
2.RandomAccessFile 对象包含一个记录指针,用以标示当前读写处的位置。RandomAccessFile 类对象可以自由移动记录指针:
long getFilePointer():获取文件记录指针的当前位置
void seek(long pos):将文件记录指针定位到 pos 位置
3.构造器
public RandomAccessFile(File file, String mode)
public RandomAccessFile(String name, String mode)
创建 RandomAccessFile 类实例需要指定一个 mode 参数,该参数指定 RandomAccessFile 的访问模式:
r: 以只读方式打开
rw:打开以便读取和写入
rwd:打开以便读取和写入;同步文件内容的更新
rws:打开以便读取和写入;同步文件内容和元数据的更新

3.用RandomAccessFile类实现文件的复制 内容插入

public class TestRandomAccessFile {
    //需求:在"d:/aa.txt"文件中有多行内容,在第三行abc的后边加入"xyz"
	/**源文件中的内容是这样的
	 * 	abcdefghijklmnopqrstuvwxyz
		abcdefghijklmnopqrstuvwxyz
		abcdefghijklmnopqrstuvwxyz
		abcdefghijklmnopqrstuvwxyz
		abcdefghijklmnopqrstuvwxyz
	 */
	@Test
	public void test5(){
		RandomAccessFile raf=null;
		try {
			raf=new RandomAccessFile("d:/aa.txt","rw");
			int len;
			int num=1;//记录行数
			long pos=0;//
                   //将光标移动到准备插入的位置
			while((len=raf.read())!=-1){
				if(len==10){//换行符的ASCII值是10,读到这里证明读到了换行符号,此时指针应该在下一行的首位
					num++;
				}
				if(num==3){
					pos = raf.getFilePointer()+3;//记录要插入的的位置的指针数字
					System.out.println(pos);//此时的位置是59,也就是每行的后边还有一个\r\n各占用1个字节位置也就是一行有28个字节
					raf.seek(pos);
					break;
				}
			}
                   //读取光标之后的所有的数据,注意换行符的问题
			StringBuilder str=new StringBuilder();
			String s="";
			while((s=raf.readLine())!=null){//readLine返回的值中是不包含换行符的,所以后边追加的过程中要加入换行符
				str.append(s+"\r\n");//这里注意了,在TXT中只有"\r\n"才是标准的换行符,
			}	
			raf.seek(pos);//读完之后将光标放回插入位置
			raf.write("xyz".getBytes());//将xyz写入
			raf.write(str.toString().getBytes());//将剩下的全部写入
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			try {
				raf.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

总结如下:
① 利用read(byte[] b)来读取第三个位置之后的所有内容,将byte数组转换成字符串,之后追加到StringBuilder中,待插入”xyz”之后再把StringBuilder中的内容写入,
整个过程不用添加换行符,因为read(byte[] b)能把换行符一起读到数组中,这样StringBuilder中的内容已经包含换行符号了,插入之后自然带有换行
② 这种是利用readLine()来读,发现这种情况完成之后TXT文件中没有换行符,
这是因为readLine()在读取的时候是不会返回换行符号的,所以要在StringBuilder追加的时候加入换行符号,
而且TXT中的换行符号是”\r\n”,如果只加其中的一个是不能换行的
//插入数据 (多行)
//源文件中有多行的数据,打算在此文件中插入一个字符串
//光标换行,多行的情况用readLine的读取方式解决

 /**源文件中的内容是这样的,打算第一行c后边插入xyz
	 * 	abcdefghijklmnopqrstuvwxyz
		abcdefghijklmnopqrstuvwxyz
		abcdefghijklmnopqrstuvwxyz
		abcdefghijklmnopqrstuvwxyz
		abcdefghijklmnopqrstuvwxyz
	 */
		@Test
		public void test4(){
			RandomAccessFile raf=null;
			try {
				raf=new RandomAccessFile("d:/aa.txt","rw");
				raf.seek(3);//位置在c后边
				
				StringBuilder str=new StringBuilder();
				String s="";
				while((s=raf.readLine())!=null){//readLine返回的值中是不包含换行符的,所以后边追加的过程中要加入换行符
					str.append(s+"\r\n");//这里注意了,在TXT中只有"\r\n"才是标准的换行符,
				}	
				raf.seek(3);
				raf.write("xyz".getBytes());
				raf.write(str.toString().getBytes());
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}finally{
				try {
					raf.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}			
		}
  	//插入数据 (多行)
 //源文件中有多行的数据,打算在此文件中插入一个字符串
 /**源文件中的内容是这样的,打算第一行c后边插入xyz
	 * 	abcdefghijklmnopqrstuvwxyz
		abcdefghijklmnopqrstuvwxyz
		abcdefghijklmnopqrstuvwxyz
		abcdefghijklmnopqrstuvwxyz
		abcdefghijklmnopqrstuvwxyz
	 */
	@Test
	public void test3(){
		RandomAccessFile raf=null;
		try {
			raf = new RandomAccessFile("d:/hello.txt","rw");
			//指定位置  第三个位置
			raf.seek(3);//将光标插入到想要插入字符串的位置
			//String tmp = raf.readLine();  //读取从光标开始到这行结束  defg
			StringBuilder str = new StringBuilder();
			byte[] b = new byte[10];
			int len;
			while((len=raf.read(b))!=-1){//用字节数组读取的时候换行符也存入了字节数组,所以这种读法不用手动添加换行符
				String s = new String(b,0,len);
				str.append(s);//将光标之后的字符串全部已追加的形式存到str中
			}
			raf.seek(3);//将光标插入到想要插入字符串的位置
			raf.write("xyz".getBytes());  //abcxyz     将想要插入的内容写到里边,这是光标在abcxyz后边
			raf.write(str.toString().getBytes());  //将之后的内容再重新写入
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			if(raf!=null)
				try {
					raf.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}
	
	//插入数据   
	//abcdefg   第三个位置插入"xyz"  结果:abcxyzdefg 
	@Test
	public void test2(){
		RandomAccessFile raf=null;
		try {
			raf = new RandomAccessFile("d:/hello.txt","rw");
			//指定位置  第三个位置
			raf.seek(3);//设置指针在3位置也就是c后边
			String tmp = raf.readLine();  //读取从光标开始到这行结束  tmp=defg  这时候光标在最后
			raf.seek(3);//还将光标放回c后边
			raf.write("xyz".getBytes());  //abcxyz
			raf.write(tmp.getBytes());    //abcxyzdefg
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			if(raf!=null)
				try {
					raf.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
		
	}
	//文件复制
	@Test
	public void test1(){
		RandomAccessFile raf1=null;
		RandomAccessFile raf2=null;
		try {
			raf1 = new RandomAccessFile("hello.txt","r");//创建对象,只读的模式
			raf2 = new RandomAccessFile("cccc.txt","rw");//创建对象读写模式
			byte[] b = new byte[10];
			int len;
			while((len=raf1.read(b))!=-1){
				raf2.write(b, 0, len);
			}
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			if(raf2!=null)
				try {
					raf2.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			if(raf1!=null)
				try {
					raf1.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
		
	 }
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值