黑马程序员__IO流

来源:互联网 发布:v百科网络流行语 编辑:程序博客网 时间:2024/04/29 22:50

--------- android培训java培训、期待与您交流! ------------

IO(Input Output)流

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

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

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

流按操作数据分为两种:字节流与字符流

流按流向分为:输入流,输出流

IO流常用基类

字节流的抽象基类InputStream,OutputStream

字符流的抽象基类Reader,Writer

注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀

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

//传递一个true参数,代表不覆盖已有的文件,并在已有文件的末尾处进行数据续写

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

复制的原理:其实就是将C盘下的文件数据存储到D盘的一个文件中

步骤:

1.在D盘创建一个文件,用于存储C盘文件中的数据

2.定义读取流和C盘文件关联

3.通过不断的读写完成数据存储

4.关闭资源

public static void cpoy()throws IOException

{

        FileWriter fw = new FileWriter("xxx_copy.txt");//创建目的地

        FileReader fr = new FileReader("xxx.txt");//与已有文件关联

        char[] chs = new char[1024];

        int num = 0;

        while((num=fr.read(chs))!=-1)

        {

            fw.write(new String(chs,0,num));

        }

        fw.close();

        fr.close();

}

BufferedWriter缓冲区的出现是为了提高流的操作效率而出现的,所以在创建缓冲区之前,必须要先有流对象

该缓冲区中提供了一个跨平台的换行符newLine()

其实关闭缓冲区,就是在关闭缓冲区中的流对象

readLine()方法返回的时候只返回回车符之前的数据内容,并不返回回车符

装饰设计模式:

当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能,那么自定义的该类称为装饰类

装饰类通常会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能提供更强的功能

装饰模式比继承要灵活,避免了继承体系臃肿,而且降低了类与类之间的关系

装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰类通常是都属于一个体系中的

FileOutputStream的write方法在不指定缓冲区时不用刷新字节流直接写进目的,但要close

int num = fis.available();//读取了多少个字节

键盘录入的常见写法

BufferedReader bufr =

                new BufferedReader(new InputStreamReader(System.in));

流操作的基本规律:通过三个明确来完成

1.     明确源和目的

        源:输入流InputStream Reader

        目的:输出流OutputStream  Writer

2.     操作的数据是否是纯文本

        是:字符流

        不是:字节流

3.     当体系明确后,再明确要使用哪个具体的对象

        通过设备来进行区分:

        源设备:内存,硬盘,键盘

        目的设备:内存,硬盘,控制台

在存储时,需要加入指定编码表utf-8,而指定的编码表只有转换流可以指定,所以要使用的对象是OutputStrWriter而该转换流对象要接收一个字节输出流,而且还可以操作文件的字节输出流FileOutputStream

OutputStreamWriter osw =

new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");

所以,记住,转换流什么时候使用,字符和字节之间的桥梁,通常,涉及到字符编码转换时,需要用到转换流

System.setIn(new FileInputStream(“xxx.java”);//改变输入源

System.setOut(new PrintStream(“zz.txt”));//改变输出目的

获取系统信息到文件中(带换行的)

Properties prop = System.getProperties();

prop.list(new PrintStream("C:\\pro.txt"));

File类

用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作,File对象可以作为参数传递给流的构造函数

separator与系统有关的默认名称分隔符

//将a.txt封装成file对象,可以将已有的和未出现的文件或者文件夹封装成对象

File f1 = new File("a.txt");

File f2 = new File("c:\\abc","b.txt");//前面是目录,后面是文件名

File d = new File("c:\\abc");

File f3 = new File(d,"c.txt");//和f2是一样的

File f = new File("file.txt");

File类常见方法

1.创建

        boolean createNewFile():在指定位置创建文件,如果该文件已经存在

        则不创建,返回false,和输出流不一样,输出流对象一建立创建文件,

        而且文件如果已经存在,会覆盖

        boolean mkdir():创建文件夹

        boolean mkdirs():创建多级文件夹

 

2.删除

        boolean delete();删除失败返回false如果文件正在被使用,则删除不了返回false

        void deleteOnExit();在程序退出时删除指定文件

3.判断

        boolean canExecute();判断文件是否可执行,像xxx.txt如果存在,结果为true

        boolean exists():文件是否存在

        isFile()

        isDirectory()

        isHidden()

        isAbsolute()是否是绝对

4.获取信息

        getName()

        getPath()

        getParent()

        getAbsolutePath()

        long lastModified()

        long length()文件大小

        f1.renameTo(f2);将f1对象重命名为f2对象和剪切相似

记住:在判断文件对象是否是文件或者目录时,必须要先判断文件封装的对象是否存在

通过exists判断

File f = new File("c:\\myclass");

String[] names = f.list();

//调用list方法的file对象必须是封装了一个目录,该目录还必须存在

File[] files = File.listRoots();

//获取所有盘符

//用匿名内部类过虑文件

File[] files = dir.listFiles(new FilenameFilter()

{

        public boolean accept(File dir,String name)

        {

            return name.endsWith(".jpg");

        }

});

递归要注意:

1.  限定条件

2.  要注意递归的次数,尽量避免内在溢出

删除一个带内容的目录:

删除原理:在Windows中,删除目录从里面往外删除的,既然是从里往外删除,就需要用到递归

import java.io.*;

class RemoveDir

{

        public static void main(String[] args)

        {

            removeDir(new File("d:\\testdir"));

        }

        //删除文件

        public static void removeDir(File dir)

        {

            File[] files = dir.listFiles();

            for(int x = 0;x<files.length;x++)

            {

                if(files[x].isDirectory())

                    removeDir(files[x]);

                else

                    System.out.println(files[x].toString()+ ".."+files[x].delete());

            }

            System.out.println(dir.toString() + ".目录." + dir.delete());

        }

}

Propertieshashtable的子类,也就是说它具备map集合的特点,而且它里面存储的键值对都是字符串,是集合和IO技术相结合的集合容器

该对象的特点:可以用于键值对形式的配置文件,那么在加载数据时,需要数据有固定格式:
=

        Properties prop = new Properties();

        prop.setProperty("zhangsan","30");//设置键值对

        prop.setProperty("lisi","39");

//      System.out.println(prop);

        String value = prop.getProperty("lisi");//根据键获取值

//      System.out.println(value);     

        prop.setProperty("lisi","29");//修改键的值     

        Set<String> names = prop.stringPropertyNames();//stringPropertyNames()返回类型为Set     

        for(String s : names){         

            System.out.println(s+"..."+prop.getProperty(s));

        }

打印流:

该流提供了打印方法,可以将各种数据类型的数据都原样打印

字节打印流:PrintStream

构造可以接收的参数类型

1.file对象。File

2.字符串路径。String

3.字节输出流。OutputStream

字符打印流:PrintWriter

构造可以接收的参数类型

1.file对象。File

2.字符串路径。String

3.字节输出流。OutputStream

4.字符输出流。Writer

PrintWriter out = new PrintWriter(new FileWriter("a.txt"),true);

AutoFlush只是对流而言

合并流:

import java.io.*;

import java.util.*;

class SequenceDemo

{

        public static void main(String[] args) throws Exception

        {

            Vector<FileInputStream> v = new Vector<FileInputStream>();

            v.add(new FileInputStream("c:\\1.txt"));

            v.add(new FileInputStream("C:\\2.txt"));

            v.add(new FileInputStream("c:\\3.txt"));

            Enumeration<FileInputStream> en = v.elements();

            FileOutputStream fos = new FileOutputStream("c:\\4.txt");

            SequenceInputStream sis = new SequenceInputStream(en);

            //合并流

            int num = 0;

            byte[] buf = new byte[1024];

            while((num=sis.read(buf))!=-1)

            {

                fos.write(buf,0,num);

            }

            fos.close();

            fis.close();

        }

}

切割与合并文件

import java.util.*;

import java.io.*;

class SplitFile

{

        public static void main(String[] args) throws Exception

        {

            splitFile();

            merge();

        }

        public static void merge()throws Exception

        {

            ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();

            for(int i=1;i<=6;i++)

            {

                al.add(new FileInputStream("c:\\myclass\\"+i+".part"));

            }

            final Iterator<FileInputStream> it = al.iterator();

            Enumeration<FileInputStream> en = new Enumeration<FileInputStream>()

            {

                public boolean hasMoreElements()

                {

                    return it.hasNext();

                }

                public FileInputStream nextElement()

                {

                    return it.next();

                }

            };

            SequenceInputStream sis = new SequenceInputStream(en);

            FileOutputStream fos = new FileOutputStream("c:\\myclass\\m.mp3");

            byte[] buf = new byte[1024];

            int len = 0;

            while((len=sis.read(buf))!=-1)

            {

                fos.write(buf,0,len);

            }

            fos.close();

            sis.close();

 

        }

        public static void splitFile()throws Exception

        {

            FileInputStream fis = new FileInputStream("c:\\m2m.mp3");

            byte[]buf = new byte[1024*1024];

            int len = 0;

            int count = 1;

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

            {

                FileOutputStream fos =

                    new FileOutputStream("c:\\myclass\\"+(count++)+".part");

                fos.write(buf,0,len);

                fos.close();

            }

            fis.close();

        }

}

对象的序列化:目的:将一个具体的对象进行持久化,写入到硬盘上。

注意:静态数据不能被序列化,因为静态数据不在堆内存中,是存储在静态方法区中。

如何将非静态的数据不进行序列化?用transient关键字修饰此变量即可。

Serializable:用于启动对象的序列化功能,可以强制让指定类具备序列化功能,该接口中没有成员,这是一个标记接口。这个标记接口用于给序列化类提供UID。这个uid是依据类中的成员的数字签名进行运行获取的。如果不需要自动获取一个uid,可以在类中,手动指定一个名称为serialVersionUID id号。依据编译器的不同,或者对信息的高度敏感性。最好每一个序列化的类都进行手动显示的UID的指定。

 

import java.io.*;

class ObjectStreamDemo {

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

              writeObj();

              readObj();

       }

       public static void readObj()throws Exception{

              ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));

              Object obj = ois.readObject();//读取一个对象。

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

       }

       public static void writeObj()throws IOException{

              ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));

              oos.writeObject(new Person("lisi",25)); //写入一个对象。

              oos.close();

       }

}

class Person implements Serializable{

       private static final long serialVersionUID = 42L;

       private transient String name;//transient修饰后name将不会进行序列化

       public int age;

       Person(String name,int age){

              this.name = name;

              this.age = age;

       }

       public String toString(){

              return name+"::"+age;

       }

}

管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个线程从PipedInputStream对象读取,并由其他线程将其写入到相应的PipedOutputStream。不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。

管道流有两种创建方式:

1.       通过构造函数连接输入输出流PipedInputStream(PipedOutputStream src)创建PipedInputStream,使其连接到管道输出流src

2.       创建无参对象 PipedInputStream()通过connect(PipedOutputStream src)使此管道输入流连接到管道输出流src

RandomAccessFile该类不是IO体系中子类,而是直接继承自Object

但是它是IO包中成员,因为它具备读和写功能,内部封装了一个数组,而且通过指针对数组的元素进行操作,可以通过getFilePointer获取指针位置,同时可以通过seek改变指针的位置,其实完成读写的原理就是内部封装了字节输入流和输出流

通过构造函数可以看出,该类只能操作文件,而且操作文件还有模式,只读r读写rw

如果模式为只读r不会创建文件,会去读取一个已存在文件,如果该文件不存在,则会出现异常

如果模式为rw,操作的文件不存在,会自动创建,如果存在则不会覆盖

RandomAccessFile数据要分段,而且要有规律

DataInputStreamDataOutputStream可以用于操作基本数据类型的数据的流对象

注意:怎么写入的数据,就要按相应的顺序读取

操作字节数据

ByteArrayInputStream,在构造的时候,需要接收数据源,而且数据源是一个字节数组

ByteArrayOutputStream在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组,这就是数据目的地

因为这两个流对象都操作的是数组,并没有使用系统资源,所以不用进行close关闭

关闭ByteArrayInputStreamByteArrayOutputStream无效。它们中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。只有一个方法writeTo(OutputStream out)会产生IOException

源设备

       键盘System.in 硬盘FileStream 内存ArrayStream

目的设备

       控制台System.out 硬盘FileStream 内存ArrayStream

操作字符数组

CharArrayReaderCharArrayWriter

操作字符串

StringReaderStringWriter

转换流的字符编码

public static void readText()throws IOException

{

       InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"),"GBK");

       char[] buf = new char[10];

       int len = isr.read(buf);

       String str = new String(buf,0,len);

       System.out.println(str);

}

public static void writeText()throws IOException

{

       OutputStreamWriter osw =

new OutputStreamWriter(new FileOutputStream("gbk.txt"),"GBK");

       osw.write("你好");

       osw.close();

}

编码:字符串变成字节数组

解码:字节数组变成字符串

String-->byte[]:str.getBytes(charsetName);

byte[] -->String:new String(byte[],charsetName)

import java.util.*;

class EncodeDemo

{

       public static void main(String[] args) throws Exception

       {

              String s = "你好";

              byte[] b1 = s.getBytes("utf-8");

              System.out.println(Arrays.toString(b1));

              String str = new String(b1,"UTF-8");

              System.out.println("str="+str);

       }

}

如果解错了怎么办?

编码用码表1编成byte1,解码用码表2解成str1

再返回去就行了将str1用码表2编码编成byte2,再用码表1解码

但如果是GBK编码,却用UTF-8解码,再用这种方式就不行了,因为它们都识别中文

 

--------- android培训java培训、期待与您交流! ------------

原创粉丝点击