javaIO流

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了javaIO流相关的知识,希望对你有一定的参考价值。

IO流:

IO流是用来处理设备之间的数据传输的。

java对数据的操作是通过流的方式。

java用于操作流的对象都在IO包中。

流按操作的数据分为两种:字符流和字节流。

  字符流的由来:字符流其实就是字节流读取文字字节数据后,不直接操作而是先查指定的编码表,  获取对应的文字。再对这个文字进行操作。

          简单说,就是字符流=字节流+编码表。


字节流的抽象基类(就是顶层父类):InputStream,OutputStream 

字符流的抽象基类:Reader,Writer

这些体系的子类都以父类名作为后缀,而子类名的前缀就是该对象的功能。


就从熟悉的文字开始学习,也就是字符流:

//需求:将一些文字存入到硬盘中。(记住:如果要操作文字,优先考虑字符流,而且要将文字存到硬盘上,要用输出流——writer)

//      硬盘的数据基本体现是文件。所以就找到了FileWriter.

package day20;

import java.io.FileWriter;

import java.io.IOException;


public class FileWriterDemo {


public static void main(String[] args) throws IOException {

/*创建一个可以往文件中写入字符数据的字符输出流对象。

* 既然是往一个文件中写入文字数据,那么在创建对象时就必须明确该文件(用于存储数据的目的地)

* 如果文件不存在就会自动创建。如果存在就会被覆盖。

* */

FileWriter fw=new FileWriter("demo.txt");

/*

* 调用Writer对象中的write(String)方法写入数据。

* 这样做后数据就写入到了临时存储缓冲区中。

* */

fw.write("abcde");

/*

* 要将数据存储到目的地,还需要进行刷新操作。

* */

fw.flush();

/*

* 关闭流,关闭资源。在关闭前会调用flush刷新缓冲区的数据到达目的地。

* */

fw.close();

//fw.write("haha");   //这句话就会抛出异常,流都关闭了,不能再往进写了。

 

/* flush与close区别:

* flush可以刷新多次,close只能一次

* flush相当于文件写入新数据保存后,文件没关闭,刷新一下,就会看到新的内容,然后还可以继续写,可以反复这样做,重复多次。

* close会自动调用flush就相当于你关闭文件时,系统会提示你文件未保存是否要保存一样,点了保存,但是如果要查看,就必须再次打开文件,这就相当于重新打开了一个流。所以说close只能刷新一次。

* */

/* 两个小问题:

* 1.换行:

* 如果要在文件中第一行写abcd  第二行写haha  怎么操作呢?

*        方法一:windows系统的换行\r\n

*  \n是不行的,记事本是windows操作软件,不认识这个。

*  fw.write("abcde\r\nhaha");

*  方法二:unix系统的换行。先要声明LINE_SEPARATOR

*  private static final String LINE_SEPARATOR = System.getProperty("line.separator");

*        fw.write("abcde"+LINE_SEPARATOR+"haha");

* 2.续写:

* 如果写好了数据之后,又想要添加新的内容,还不想覆盖原来的,怎么操作?

*  看下API里FileWriter的构造函数,可以知道在创建对象时,加上true就行了。

* FileWriter fw=new FileWriter("demo.txt",true);

* */  

}

}



IO异常的处理方式:

package day20;

import java.io.FileWriter;

import java.io.IOException;

/*

 * IO异常处理:

 * 1.在try外面创建对象,里面初始化。

 * 2.close放在finally中,而且记得要判断对象是否为空。

 *  如果创建的文件地址不存在,就不需要close。

 * */


public class IOExceptionDemo {


public static void main(String[] args) {

FileWriter fw=null;

try {

fw=new FileWriter("demo.txt");

fw.write("abcde");

}catch (Exception e) {

System.out.println(e.toString());

}finally{

if(fw!=null)

try {

fw.close(); //fw对象在try代码块中创建的,所以这样会异常。

 

} catch (IOException e) {

throw new RuntimeException("关闭失败");

   }        

}

}


}



//需求:读取一个文本文件,将读取到的字符打印。和上面Writer类似,找到了FileReader。

package day20;

import java.io.FileNotFoundException;

import java.io.FileReader;

import java.io.IOException;


public class FileReaderDemo {


public static void main(String[] args) throws IOException   {

/*

* 创建读取字符数据的流对象。

* 在创建读取流对象时,必须明确被读取的文件,一定要确定该文件是存在的。

* 用一个读取流关联一个已存在的文件。

* */

FileReader fr=new FileReader("demo.txt");

//用read方法读取文件中的内容。返回的是int

int ch=0;

while((ch=fr.read())!=-1){

System.out.println((char)ch);

}

/*

* int ch=fr.read();

* System.out.println(ch);

* int ch1=fr.read();

* System.out.println(ch1);

* fr.close();                 //如果文件读完了,就返回-1.不过这样写太麻烦了。

* */

/* 使用read(char[])读取文本文件数据。

* char[] buf=new char[3];

* int len=0;

* while((len=fr.read(buf))!=-1){

* System.out.println(new String(buf,0,len));

* }

* */

}

}



了解几个类:


System类:

System类中的方法都是静态的。

常用方法:

currentTimeMillis();获取当前时间的毫秒值。两个一减就能算出程序运行的时间。

getProperty();  确定当前的系统属性。

properties集合是map的子集,其中存储的都是String类型的键和值。键是固定的,不同机器属性不同,所以键对应的值就不同。

                        最好使用它自己的存储和取出的方法来完成元素的操作。比如换行操作:通常我们知道的是unix系统中的\n,windows系统就是\r\n,其他系统可能还不一样,java最牛的就是跨平台性,怎么解决呢?

     其实系统属性中就有换行的操作,所以只需要调用getProperty()方法中的line.separator键,就可获取他的值,就是换行操作。

     例如:System.out.println("hello"+System.getProperty("line.separator")+"world");

             这句代码在任何系统都可以获取该系统的回车符,从而实现换行操作。很常用,通常会把他定义成常量来使用。

     private static final String LINE_SEPARATOR = System.getProperty("line.separator");

             System.out.println("hello"+LINE_SEPARATOR+"world");

setProperty("key","value");  给系统设置一些属性信息,这些信息是全局的,其他程序都可以使用。




Runtime类:

没有构造方法摘要,这说明该类不可以创建对象。

又发现还有非静态的方法,说明该类应该提供静态的返回该类对象的方法。

而且发现静态方法只有一个,说明runtime类使用了单例设计模式。

package day20;

import java.io.IOException;

public class RunTimeDemo {

        public static void main(String[] args) throws IOException {

Runtime r=Runtime.getRuntime();  //返回与当前 Java 应用程序相关的运行时对象。

//execute:执行    xxx.exe

r.exec("notepad.exe");      //开启执行文件——记事本。

//先在当前文件目录找notepad,没找到,然后去path找,找到了。

//如果执行qq就异常了,因为qq不在path目录下,找不到文件。

Process p=r.exec("notepad.exe");

p.destroy();              //结束子进程。只能结束r.exec()开启的。

}

}



Math类:

math提供了操作数学运算的方法。都是静态的。

常用方法:

abs();          返回绝对值

ceil(); 返回大于参数的最小整数

floor(); 返回小于参数的最大整数

round();     返回四舍五入的整数。

pow(a,b); 返回a的b次方

random(); 产生一个大于0小于1的double类型的值。利用这个可以产生随机数。

比如要产生1-10的随机数:Math.ceil(Math.random()*10);



Date类:

after(date)         测试此日期是否在指定日期之后。

bofore(date)     测试此日期是否在指定日期之前。

compareTo(date)     比较两个日期是否相等。

package day20;

import java.text.DateFormat;

import java.text.SimpleDateFormat;

import java.util.Date;

public class DateDemo {


public static void main(String[] args) {

long time=System.currentTimeMillis();

System.out.println(time);

Date date=new Date();      //将当前日期时间封装成Date对象

System.out.println(date);  //输出结果就是当前时间Sun May 21 17:14:18 CST 2017

Date date2=new Date(time); //将time代表的毫秒值封装成Date对象

System.out.println(date2); //输出的是time对应的日期时间。

/*日期对象和毫秒值之间的转换

* 毫秒值转换成日期:

* 因为可以通过Date对象的方法对该日期的各个字段(年月日)进行操作。

* 1.通过date对象的构造方法new Date(timeMillis)。比如上面例子

* 2.也可以通过setTime设置。

* 日期对象转换成毫秒:

* 因为可以通过具体的数值进行运算。

* 1.通过getTime方法

*/

//对日期对象进行格式化

Date date3=new Date();

DateFormat dateformat=DateFormat.getDateInstance();  //获取日期格式对象,具备着默认的风格。

//DateFormat dateformat=DateFormat.getDateInstance(DateFormat.FULL);  //这是另一种格式,上面的是默认格式,还有其他的格式。

dateformat=DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.LONG);

String str_date=dateformat.format(date3);

System.out.println(str_date);

//自定义格式

dateformat=new SimpleDateFormat("yyyy-MM-dd");

String str_date1=dateformat.format(date3);

System.out.println(str_date1);

/*

* 将日期对象转成日期格式的字符串

* 使用的是DateFormat类中的format方法。比如上面的例子。

* 将日期格式的字符串转成日期对象

* 使用的是DateFormat类中的parse方法。

* */

                

                /*

 * 该程序运行结果为:

 * 1496796013615

 * Wed Jun 07 08:40:13 CST 2017

 * Wed Jun 07 08:40:13 CST 2017

                 * 2017年6月7日 上午08时40分13秒

                 * 2017-06-07

 * */

}

}



package day20;


import java.text.DateFormat;

import java.text.ParseException;

import java.text.SimpleDateFormat;

import java.util.Date;


public class DateTest {


/*

* 练习:"2017-5-17"到"2017-5-20"中间有多少天?

* 思路:

* 两个日期相减就行了,咋减?

* 必须要有两个可以进行减法运算的数才行。就可以用毫秒值。

* 如何获取毫秒值?通过date对象。

* 如何获取date对象?可以将字符串转成date对象。

*  

*  1.将日期格式的字符串转成date对象。

*  2.将date对象转成毫秒值。

*  3.相减,变成天数。

*/

public static void main(String[] args) throws ParseException {

String str_Date1="2017--5--17";

String str_Date2="2017--5--20";

test(str_Date1,str_Date2);

}


public static void test(String str_Date1,String str_Date2) throws ParseException {

//1.将日期格式的字符串转成date对象。   定义日期格式对象

//DateFormat dateFormat=DateFormat.getDateInstance();    //这个是默认格式的日期,下面是自定义格式

DateFormat dateFormat=new SimpleDateFormat("yyyy--MM--dd");


Date date1=dateFormat.parse(str_Date1);

System.out.println(date1);

Date date2=dateFormat.parse(str_Date2);

System.out.println(date2);

//2.将date对象转成毫秒值。

long time1=date1.getTime();

long time2=date2.getTime();

//3.相减

long time=Math.abs(time1-time2);

System.out.println(time/1000/60/60/24);

                

                /*

 * 该程序运行结果为:

 * Wed May 17 00:00:00 CST 2017

 * Sat May 20 00:00:00 CST 2017

 * 3

 * */

}

}




Calendar类:

常用方法:

set();   获取指定日期

add();   日期的偏移

get(); 获取当前日期

package day20;

import java.util.Calendar;

public class CalendarDemo {

public static void main(String[] args) {

Calendar c=Calendar.getInstance();

c.set(2017, 5, 20);   

c.add(Calendar.YEAR,-1);

showDate(c);          

}


private static void showDate(Calendar c) {

int year=c.get(Calendar.YEAR);

int month=c.get(Calendar.MONTH)+1;    //注意月份是0-11表示的,0表示1月以此类推。

int day=c.get(Calendar.DAY_OF_MONTH);

int week=c.get(Calendar.DAY_OF_WEEK);  //周日是一周的第一天,所以这里要处理一下

System.out.println(year+"年"+month+"月"+day+"日"+" "+getWeek(week));

}


public static String getWeek(int i) {

String[] weeks={"","星期日","星期一","星期二","星期三","星期四","星期五","星期六"};

return weeks[i];

}

}



例:

将C盘的一个文件复制到D盘


package day20;

import java.io.FileReader;

import java.io.FileWriter;

import java.io.IOException;


public class CopyTextTest {

private static final int BUFFER_SIZE = 1024*3;


/*

* 需求:将C盘的一个文件复制到D盘

* 分析:读取C盘文件中的数据,并写入到d盘当中。(这就是复制的原理)

*/

public static void main(String[] args) throws IOException {

method_2();        //这个比较常用,效率高,读一大片写一次,循环次数少。

}



public static void method_1() throws IOException {

//1.读取一个已有的文本文件,使用字符读取流和文件相关联。

FileReader fr=new FileReader("demo.txt");

//2.创建一个目的,用于存储读到的数据。

FileWriter fw=new FileWriter("CopyText_1.txt");

//3.频繁的读写操作。

int ch=0;

while((ch=fr.read())!=-1){

fw.write(ch);

}

//4.关闭流资源。

fw.close();

fr.close();

}

public static void method_2() {

FileReader fr=null;

FileWriter fw=null;

try {

fr=new FileReader("demo.txt");

fw=new FileWriter("CopyText_2.txt");

//创建一个临时容器,用于缓存读取到的字符

char[] buf=new char[BUFFER_SIZE];

//定义一个变量记录往数组里装的字符的个数

int len=0;

while((len=fr.read(buf))!=-1){

fw.write(buf, 0,len);

}

} catch (IOException e) {

//System.out.println("读写失败");

throw new RuntimeException();

}

finally{

if(fr!=null)

try {

fr.close();

} catch (IOException e1) {

e1.printStackTrace();

}

if(fw!=null)

try {

fw.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}


}




字符流的缓冲区:

缓冲区的出现提高了对数据的读写效率。上面例子中BUFFER_SIZE就是自定义的缓冲区。

对应类:BufferWriter   特有方法:newline();

        BufferReader   特有方法:readline();

缓冲区要结合流才可以使用。

在流的基础上对流的功能就行了增强。

package day20;

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.FileReader;

import java.io.FileWriter;

import java.io.IOException;

import java.io.Reader;


public class BufferedDemo {


public static void main(String[] args) throws IOException {

bufferedWriter();

bufferedReader();

}



public static void bufferedWriter() throws IOException {

FileWriter fw=new FileWriter("demo.txt");

//为了提高写入效率,使用了字符流缓冲区。

//创建一个字符写入流的缓冲区对象,并和指定要被缓冲的流对象相关联。

BufferedWriter bufw=new BufferedWriter(fw);

//使用缓冲区的写入方法将数据写入到缓冲区中。

bufw.write("abcdef");

bufw.newLine();

bufw.write("wqnmlgb");

//使用缓冲区的刷新方法将数据刷新到目的地中。

bufw.flush();

//关闭缓冲区,其实关闭的就是被缓冲的流对象。

bufw.close();

}

public static void bufferedReader() throws IOException {

FileReader fr=new FileReader("demo.txt");

BufferedReader bufr=new BufferedReader(fr);

String line=null;

while((line=bufr.readLine())!=null){

System.out.println(line);

}

bufr.close();

}

}





例:模拟一个BufferedReader.

package day20;

import java.io.FileReader;

import java.io.IOException;


public class MyBufferReader {


/**

* 自定义读取缓存区,其实就是模拟一个BufferedReader.

* 分析:

* 缓冲区中无非就是封装了一个数组,并对外提供了更多的方法对数组进行访问。

* 其实这些方法最终操作的都是数组的角标。

* 缓冲的原理:

* 其实就是从源中获取一批数据装进缓冲区中,再从缓冲区中不断地取出一个一个数据。

* 此次取完后,再从源中继续取一批数据装进缓冲区,当源中的数据取完后,用-1作为结束标记。

*/

private FileReader r;

//定义一个数组作为缓冲区

private char[] buf=new char[1024];

//定义一个指针用于操作这个数组中的元素。当操作到最后一个元素后,指针归零。

private int pos=0;

//定义一个计数器用于记录缓冲区中数据个数。当该数据减为0,就从源中继续获取数据到缓冲区。

private int count=0;

MyBufferReader(FileReader r){

this.r=r;

}

public int myRead() throws IOException{

//1.从源中获取一批数据到缓冲区中。需要先做判断,只有计数器为0时,才需要从源中获取数据

if(count==0){

count=r.read(buf);

pos=0; //每次获取数据到缓冲区后,角标归零。

}

if(count<0)

return -1;

char ch=buf[pos++];

count--;

return ch;

/*    这样的代码是初稿,思路很清晰,上面是修改之后的。

if(count==0){

count=r.read(buf);

if(count<0)

return -1;

pos=0;                 //每次获取数据到缓冲区后,角标归零。

char ch=buf[pos];

pos++;

count--;

return ch;

}else if(count>0){

char ch=buf[pos];

pos++;

count--;

return ch;

}*/

}

public String myReadLine() throws IOException{

StringBuilder sb=new StringBuilder();

int ch=0;

while((ch=myRead())!=-1){

if(ch==‘\r‘)

continue;

if(ch==‘\n‘)

return sb.toString();

//将从缓冲区读到的字符,存储到缓存行数据的缓冲区中。

sb.append((char)ch);

}

if(sb.length()!=0){           //若没有这个if代码块,demo.txt的最后一行如果没有回车符,那么MyRead就会丢失最后一行。

return sb.toString();

}

return null;

}

public void myClose() throws IOException{

r.close();

}

}

package day20;

import java.io.FileReader;

import java.io.IOException;


public class MyBufferReaderDemo {


public static void main(String[] args) throws IOException {

FileReader fr=new FileReader("demo.txt");

MyBufferReader bufr=new MyBufferReader(fr);

String line=null;

while((line=bufr.myReadLine())!=null){

System.out.println(line);

}

bufr.myClose();

}

}




缓冲区中的read方法对FileReader中的Read进行了功能的加强,效率的提高。这就是装饰设计模式。

装饰设计模式:

对一组对象的功能进行增强时,就可以使用该模式就行问题的解决。

装饰和继承都具有一样的特点:进行功能的扩展。那么他们有何区别呢?

  

   假如有一个继承体系:

   Writer

|--TextWriter:用于操作文本

|--MediaWriter:用于操作媒体。

   

   想要对操作的动作进行效率的提高,按照面向对象的思想,可以通过继承对具体对象进行功能的扩展。

   效率提高需要加入缓冲技术。

   Writer

|--TextWriter:用于操作文本

    |--BufferTextWriter:加入了缓冲技术的操作文本的对象

|--MediaWriter:用于操作媒体。

    |--BufferMediaWriter:加入了缓冲技术的操作媒体的对象

   到这里就哦了,但是这样做好像并不理想。

   如果这个体系就行功能扩展,又多了流对象。那么这个流要提高效率,是不是也要产生子类呢?对,这时就会发现只为提高功能,进行的继承,导致继承体系越来越臃肿。不够灵活。

   

   重新思考这个问题?

   既然加入的都是同一组技术——缓冲。前一种是让缓冲和具体的对象相结合。

   可不可以将缓冲进行单独的封装,哪个对象需要缓冲就将那个对象和缓冲关联。

   class BufferWriter extends Writer{

    Buffer(Writer w){}

   }

   这时这个体系就变成了这样:

   Writer

|--TextWriter:用于操作文本

|--MediaWriter:用于操作媒体

    |--BufferWriter:用于提高效率


到这里就可以发现,装饰比继承更加灵活。特点:装饰类和被装饰类都必须所属同一个接口或者父类。



装饰设计模式的一个简单的演示

package day20;

public class PersonDemo {


public static void main(String[] args) {

Person p=new Person();

NewPerson p1=new NewPerson(p);

p1.chifan();

NewPerson2 p2=new NewPerson2();

p2.chifan();

/*

* 那么问题来了,继承也可以解决这种问题,他们有何区别呢?

*/

}

}

class Person{

void chifan(){

System.out.println("吃饭");

}

}


//这个类的出现是为了增强Person而出现的。

class NewPerson{

private Person p;

NewPerson(Person p){

this.p=p;

}

 

public void chifan(){

System.out.println("开胃酒");

p.chifan();

System.out.println("饭后甜点");

}

}

class NewPerson2 extends Person{

public void chifan(){

System.out.println("开胃酒");

super.chifan();

System.out.println("饭后甜点");

}

}


LineNumberReader            了解即可,可以读行号。

package day20;

import java.io.FileReader;

import java.io.IOException;

import java.io.LineNumberReader;


public class LineNumberReaderDemo {


/**

* @param args

* @throws IOException 

*/

public static void main(String[] args) throws IOException {

FileReader fr=new FileReader("demo.txt");

LineNumberReader lnr=new LineNumberReader(fr);

String line=null;

lnr.setLineNumber(100);              //默认行号是从1开始的,这个方法可以自己设置从几开始。

while((line=lnr.readLine())!=null){

System.out.println(lnr.getLineNumber()+":"+line);

}

lnr.close();

}

}








到这里,字符流的基本对象学习完了,接下来学习字节流。

字节流

字节流的基本操作与字符流类相同。

但它不仅可以操作字符,还可以操作其他媒体文件。比如Copy一个jpg文件。

package day22;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;


public class ByteStreamDemo {


/**

* @param args

* @throws IOException 

*/

public static void main(String[] args) throws IOException {

demo_write();

demo_read();

}


public static void demo_read() throws IOException {

/*

available()方法就是返回文件的字节数,用这个可以创建一个可文件长度刚好一样的为数组,读文件就简单多了

但是这个慎用,如果获取一个2G多的电影,累死你。

FileInputStream fis=new FileInputStream("demo2.txt");

byte[] buf=new byte[fis.available()]; 

fis.read(buf);

System.out.println(new String(buf));

*/

//还是这个最常用。

FileInputStream fis=new FileInputStream("demo2.txt");

byte[] buf=new byte[1024];

int len=0;

while((len=fis.read(buf))!=-1){

System.out.println(new String(buf,0,len));

}

fis.close();             

/*

//1.创建一个读取流对象,和指定文件关联。

FileInputStream fis=new FileInputStream("demo2.txt");

//2.读数据。一次读取一个字节。读中文就不行,一个汉字两个字节。

int ch=0;

while((ch=fis.read())!=-1){

System.out.println((char)ch);

}

fis.close();

*/

}


public static void demo_write() throws IOException {

//1.创建字节输出流对象。用于操作文件。

FileOutputStream fos=new FileOutputStream("demo2.txt");

//2.写数据。直接写入到了目的地中。

fos.write("abcdefg".getBytes());

fos.close();       //不需要刷新了,但是关闭还是要的。

}


}




练习:复制一个mp3文件。

package day22;

import java.io.BufferedInputStream;

import java.io.BufferedOutputStream;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;


public class CopyMp3Test {


/**

* 需求:复制一个mp3文件。其实和复制txt文件是一样的,就是换了一个对象。 

*/

public static void main(String[] args) throws IOException {

copy_1();

copy_2();

}


public static void copy_2() throws IOException {

FileInputStream fis=new FileInputStream("LoginScreenLoop.mp3");

BufferedInputStream bufis=new BufferedInputStream(fis);

FileOutputStream fos=new FileOutputStream("CopyMp3.mp3");

BufferedOutputStream bufos=new BufferedOutputStream(fos);

int ch=0;

while((ch=bufis.read())!=-1){

bufos.write(ch);

}

bufos.close();

bufis.close();

}


public static void copy_1() throws IOException {

FileInputStream fis=new FileInputStream("LoginScreenLoop.mp3");

FileOutputStream fos=new FileOutputStream("CopyMp3.mp3");

byte[] buf=new byte[1024];

int len=0;

while((len=fis.read(buf))!=-1){

fos.write(buf,0,len);

}

fos.close();

fis.close();

}


}
















本文出自 “12946849” 博客,请务必保留此出处http://12956849.blog.51cto.com/12946849/1932971

以上是关于javaIO流的主要内容,如果未能解决你的问题,请参考以下文章

JavaIO

04.JavaIO流问题

JAVAIO (文件流与字符数组流)

JavaIO流-对象流

JavaIO流学习总结-字符流总结

JavaIO学习:打印流