
输入流与输出流的层次结构

Reader 和 Writer 的层次结构
创建一个 FileWriter 对象,该文件会在指定目录下创建. 如果同名则覆盖, 除非构造方法第二个参数append 为 true; 由此可得默认为false.
/**
*
* @param str
* String to be written
*
* @throws IOException
*/
public static void createFile(String str) throws IOException{
//在D盘下创建abc.txt. 如果同名则覆盖,除非构造方法第二个参数append为true;
File file = new File("D:" + File.separator + "abc.txt");
FileWriter fw = new FileWriter(file, false);
fw.write(str);
fw.flush(); //关闭流对象,之前会flash一次缓冲中的数据.
fw.close(); //与flush的区别: flush刷新后流可以继续使用,close却将流关闭,不可再写入
} IO异常的标准处理方式一(以FileWriter为例)
String fileName = "D:" + File.separatorChar + "abc.txt";
FileWriter fw = null;
try {
fw = new FileWriter(fileName);
fw.write("balabala...");
fw.flush();
} catch (IOException e) {
//检查异常转为非检查异常
throw new RuntimeException("产生IO异常");
} finally {
if (fw != null)
try {
fw.close();
} catch (IOException e) {
throw new RuntimeException("流关闭异常");
}
}IO异常的标准处理方式二(SE 7 try-with-resources方式)
String fileName = "D:" + File.separatorChar + "abc.txt";
try (FileWriter fw = new FileWriter(fileName)) {
fw.write("balabala...");
fw.flush();
} catch (IOException e) {
//检查异常转为非检查异常
throw new RuntimeException("产生IO异常");
}使用FileReader读取文本文件方式一
int ch = 0;
while((ch=fr.read())!=-1){
//relevant operation
}使用FileReader读取文本文件方式二(较方法一好,推荐使用)
int len= 0;
char[] buf = new char[1024];
while((len=fr.read(buf))!=-1){
//relevant operation
}拷贝文件(建议使用方式二) //relevant operation --> fileWriter.write(buf,0,len);
BufferedWriter
BufferedReader 读取数据
String line = null;
while((line=bfr.readLine())!=null){
//relevant operation
}使用BufferedWriter, BufferedReader拷贝文件关键代码
String line = null;
while((line=bfr.readLine())!=null){
bfr.write(line);
bfr.newLine();
bfr.flush();
}牵扯到装饰设计模式
LineNumberReader BufferedReader()的子类,只是多了标号而已. 通过setLineNumber设置初始行号, 和输出可以getLineNumber获取每行的行号
可以进行二进制形式进行图片, 音乐等文件的读写.
//拷贝一个图片
public static void copyFile() throws IOException{
InputStream fis = new FileInputStream("D:\\source.jpg" );
OutputStream fos = new FileOutputStream("D:\\dst.jpg" );
int len = 0;
byte[] buf = new byte[1024];
while((len=fis.read(buf))!=-1){
fos.write(buf, 0, len);
}
fis.close(); 拷贝一首歌(使用字节流的Buffered缓冲区)
InputStream inputStream = new BufferedInputStream(
new FileInputStream("D:" + File.separator + "刘涛 - 说不出口.mp3"));
OutputStream outputStream = new BufferedOutputStream(
new FileOutputStream("D:" + File.separator + "刘某 - 就不要说.mp3")); System.out: 对应的是标准输入设备,控制台 System.in: 对应的是标准输出设备,键盘
练习: 通过键盘录入,当输入一行数据后将改行数据进行打印,如果录入的数据是over,那么停止录入.
//方法一: 传统思考
//要点记住回车的处理方式: ASCII码 13 '\r', 10 '\n'
public static void method1() throws IOException{
final InputStream in = System.in;
int character ;
final StringBuilder stringBuilder = new StringBuilder();
while(true){
character = in.read();
switch (character) {
case '\r':
break;
case '\n':
final String result = stringBuilder.toString();
if("over".equals(result)){
return;
}
//StringBuilder的清空方式
stringBuilder.delete(0, stringBuilder.length());
break;
default:
stringBuilder.append((char)character);
break;
}
}
}
//方法二: InputStreamReader转换流,将字节流转成字符流的桥梁,然后经缓冲包装提高效率
public static void method2() throws IOException{
final InputStream in = System.in;
final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));
String line = null;
while ((line = bufferedReader.readLine()) != null) {
if("over".equals(line)){
break;
}
}
}
//方法三: Scanner [5.0]
public static void method3(String[] args) throws IOException {
final InputStream in = System.in;
String line = null;
final Scanner scanner = new Scanner(in);
while(scanner.hasNextLine()){
line = scanner.nextLine();
if("over".equals(line)){
break;
}
}
}和InputStreamReader类似,是字符流通向字节流的桥梁.只是包装System.out在缓冲后.
OutputStream os = System.out;
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(os);
outputStreamWriter.write("cba");
outputStreamWriter.flush();
outputStreamWriter.close();System的setIn()方法 重新分配“标准”输入流。否则标准输入流一般都是键盘InputStream. System的setOut()方法 重新分配“标准”输出流。否则标准输入流一般都是键盘PrintStream. 可以利用这两个已关联的流进行相关操作
该流提供了打印方法,可以将各种类型的数据原样打印.
PrintStream PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。它还提供其他两项功能。与其他输出流不同,PrintStream 永远不会抛出 IOException;而是,异常情况仅设置可通过 checkError 方法测试的内部标志。另外,为了自动刷新,可以创建一个 PrintStream;这意味着可在写入 byte 数组之后自动调用 flush 方法,可调用其中一个 println 方法,或写入一个换行符或字节 ('\n')。
PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。
PrintWriter
// 设置自动刷新
PrintWriter pw = new PrintWriter(System.out, true);
String line = null;
BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in));
while ((line = bfr.readLine()) != null) {
// 这样写比用BufferedWriter更简洁.PrintWriter更适合打印各种数据.
pw.println(line);
}序列流SequenceInputStream(**表示其他输入流的逻辑串联,没有对应的输出流)
练习: 文件的分割与合并
//切割只用字节流,而不是字符流
public static void split() throws IOException{
FileInputStream fis = new FileInputStream("d:\\123.pdf" );
FileOutputStream fos = null;
int len = 0;
//1M=1024KB=1024*1024字节 存储
byte[] buf = new byte[1024*1024];
int i=0;
while((len=fis.read(buf))!=-1){
fos = new FileOutputStream("d:\\" +(++i)+ ".part");
fos.write(buf, 0, len);
fos.flush();
fos.close();;
}
fis.close();
}
//文件合并
public static void meger() throws IOException{
Vector<FileInputStream> v = new Vector<FileInputStream> ();
for(int i=1;i<4;i++)
v.add( new FileInputStream("d:\\" + i+".part"));
SequenceInputStream sis = new SequenceInputStream(v.elements());
FileOutputStream fos = new FileOutputStream("d:\\kk.pdf" );
int len = 0;
byte[] buf = new byte[1024];
while((len=sis.read(buf))!=-1){
fos.write(buf, 0, len);
}
sis.close();
fos.close();
}PipedInputStream,接收InputStream对象 用于与另一输出管道相连, 读取写入到输出管道中的数据,用于程序中线程的通信
PipedOutputStream, 可以将管道输出流连接到管道输入流来创建通信管道。管道输出流是管道的发送端。通常,数据由某个线程写入 PipedOutputStream 对象,并由其他线程从连接的 PipedInputStream 读取。
public class DemoPipedStream {
public static void main(String[] args) throws IOException {
//建立管道读入流
PipedInputStream pis = new PipedInputStream();
//建立管道输出流并与读入流关联,也可以写成connect
PipedOutputStream pos = new PipedOutputStream(pis);
new Thread(new Write(pos)).start();
new Thread(new Read(pis)).start();
}
}
class Read implements Runnable{
private PipedInputStream pis;
public Read(PipedInputStream pis){
this.pis = pis;
}
@Override
public void run(){
byte[] buf = new byte[200];
try{
System.out.println("---读取流开始获取信息");
int len = pis.read(buf);
System.out.println("--"+new String(buf,0,len));
System.out.println("---读取流读取流信息完毕");
}catch(IOException e){
throw new RuntimeException("流读取异常");
}
finally{
if(pis!=null)
try{pis.close();}
catch(IOException e){
throw new RuntimeException("流关闭异常");
}
}
}
}
class Write implements Runnable{
private PipedOutputStream pos;
public Write(PipedOutputStream pos){
this.pos = pos;
}
public void run(){
try {
System.out.println("写入流开始写入流信息,持续3S");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
pos.write("Hello PipedStram".getBytes());
System.out.println("写入流开始写入完毕");
} catch (IOException e) {
throw new RuntimeException("流写入异常");
}
finally{
if(pos!=null)
try{pos.close();}
catch(IOException e){
throw new RuntimeException("流关闭异常");
}
}
}
}可以在文件中的任何位置查找或写入数据。磁盘文件都是随机访问的,但是从网络而来的数据流却不是。你可以打开一个随机访问文件,只用于读入或者同 时用于读写. 构造器的第二个参数来指定这个选项。
//调整对象中指针
seek(long pos)
//尝试跳过输入的 n 个字节以丢弃跳过的字节。
skipBytes()以二进制格式读写基本Java类型 DataInputStream与DataOutputstream 操作字节数组 ByteArrayInputStream与ByteArrayOutputStream 操作字符数组 CharArrayReader与CharArrayWriter 操作字符串 StringReader与StringWriter
总结: 以二进制格式写出数据,需要使用 DataOutputStream。 以文本格式写出数据,需要使用 PrintWriter。
PrintWriter printWriter = new PrintWriter("setting.dat", "UTF-8");在过去,国际化字符集已经得到了处理,但是处理得很不系统,散布在 Java 类库的各处。在 Java SE 1.4 中引入的 java.nio 包用 Charset 类统一了对字符集的转换(注意 s 是小写的)。
//获取Charset实例
Charset.defaultCharset(); //默认字符集
Charset charset = Charset.forName("UTF-8");//静态获取指定字符集
//Returns a set containing this charset's aliases.
for(String alias: charset.aliases()){
System.out.println(alias);
}
//编码Java字符串
ByteBuffer bytBuffer = charset.encode("自古中秋月最明, 凉风届候夜弥清");
byte[] bytes = bytBuffer.array();
//要想解码字节序列,需要有字节缓冲区
CharBuffer cbuf = charset.decode(bytBuffer);
System.out.println(cbuf.toString()); //"你好"-->"??"是GBK变utf-8 ; -->"浣犲ソ"是utf-8变GBK 编一次解一次即可.
总体可以认为是“先开后关”原则,原因是 IO 流的打开顺序是固定且层层依托的。 然后是closeable关闭的优化
如果new FileOutputStream, 然后上级目录不存在会抛出FileNotFoundException异常, 所以需要先行创建上层文件夹。