黑马程序员-IO流

---------------------- ASP.Net+Android+IOS开发</a>、 .Net培训、期待与您交流! ----------------------


IO流

在java.io包下主要包括输入、输出两种IO流,每种输入、输出流又分为字节流和字符流两大类
字节流以字节为单位来处理输入、输出操作
字符流以字符为单位来处理输入、输出操作


File类

File类用来将文件或者文件夹封装成对象,方便对文件或文件夹的属性信息进行操作,创建、删除、重命名等操作
File不能访问文件内容本身,需要使用输入、输出流
File类可以使用文件路径字符串来创建File实例,可以是绝对或相对路径

Field:
static String separator    //表示系统相关的分隔符

Constructor:
File(String pathname)    //pathname为路径+文件名或者文件名
File(String parent, String child)    //parent为路径,child为文件名

Method:

访问文件名相关的方法:
String getName()    //最后一级的名称,无论目录或文件
String getPath()    //返回全名,无论目录或文件
File getAbsoluteFile()
String getAbsolutePath()    //返回绝对路径
String getParent()    //返回上一级,无论目录或文件
boolean renameTo(File newName)

文件检测相关方法:
boolean exists()    //文件或目录是否存在
boolean canWrite()
boolean canRead()
boolean isFile()    //是否是文件
boolean isDirectory()    //是否是目录
boolean isAbsolute()    //是否是绝对路径

获取常规文件信息:
long lastModified()
long length()

文件操作相关方法:
boolean createNewFile()    //如果已存在,不会覆盖,返回false
boolean delete()    //不走回收站
static File createTempFile(String prefix, String suffix)    //在默认临时目录创建空文件
void deleteOnExit()    //注册一个删除钩子,Java虚拟机退出时删除,保证程序出现异常,文件仍会被删除

目录操作相关方法:
boolean mkdir()    //创建目录,但不可以创建多级目录
boolean mkdirs()    //可以创建多级目录
String[] list()    //列出File对象的所有子文件和路径名,返回String数组
String[] list(FilenameFilter filter)    //
File[] listFiles()    //列出File对象的所有子文件和路径,返回File数组
File[] listFiles(FileFilter filter)    //过滤出File对象的子文件和路径,返回File数组
static File[] listRoots()    //列出系统所有的根路径

Windows的路径分隔符使用反斜线(\),而Java程序中的反斜线标识转义字符,所以如果需要在Windows中使用反斜线,应该用两条反斜线(\\),或者直接使用斜线,Java程序支持将斜线当成平台无关的路径分隔符

文件过滤器
在File的list()方法中可以接收一个实现FilenameFilter接口实例的参数
FilenameFilter接口里有且只有一个accept(File dir, String name)方法,该方法会迭代file目录下的文件及文件夹,满足条件返回true时,会存入数组中

public static void main(String[] args)
{
    File file = new File("r:/");

    String[] filenames = file.list(new FilenameFilter(){
        @Override
        public boolean accept(File dir, String name)
        {
            return name.endsWith(".java");
        }
    });

    for(String filename : filenames)
    {
        System.out.println(filename);
    }
}


字节流和字符流

字节流和字符流的区别非常简单,用法几乎一样,区别在于所操作的数据单元不同:
字节流操作的最小数据单元是8位的字节
字符流操作的最小数据单元是16位(GBK编码两个字节表示一个字符)的字符

字节流基类:InputStream和OutputStream
字符流基类:Reader和Writer
字符流都有编码,没有指定时使用的是系统默认编码


编码集

ASCII:(American Standard Code for Information Interchange,美国信息互换标准代码)
    拉丁字母--二进制码:一个字母占一个字节中的7位

ISO8859-1:欧洲码表

GB2312:共收入汉字6763个和非汉字图形字符682个
    简体中文--二进制码:一个字符占两个字节

GBK:GB2312的扩展(K),共收录汉字21003个、符号883个,并提供1894个造字码位,简、繁体字融于一库
    简体、繁体中文--二进制码:一个字符占两个字节

Big5:台湾、香港与澳门地区使用的繁体中文字符集,由台湾五大厂商共同制定
    繁体中文--二进制码

Unicode:
Unicode是编码规范,目前实际实现的Unicode编码主要有三种:UTF-8、UCS-2、UTF-16,三种Unicode字符集之间可以按照规范进行转换

最初的Unicode编码是固定长度的,2个字节表示一个字符,但2^16=65535个字符显然远远不够。Unicode4.0定义了一组附加字符编码,使用4个字节表示一个字符

UTF-8(Unicode Transformation Format)是一种8位的Unicode字符集,编码长度可变,兼容ASCII字符集,每个汉字使用3个字节表示

UCS-2(Universal Character Set)是固定长度为16位的Unicode字符集,UCS-2只支持Unicode3.0,所以不支持附加字符

UTF-16是16位的Unicode字符集,兼容UCS-2和附加字符

JVM里的char、String的默认编码是Unicode,即在JVM里出现的所有char、String均是Unicode编码

import java.io.*;
/**
阿
Unicode: 963F  1001 0110  0011 1111
GBK:     B0A2  1011 0000  1010 0010

demo.txt文件中内容:
阿
*/

class CharsetTest
{
    public static void main(String[] args) throws Exception
    {
        unicodeRead();
        gbkRead();
    }

    public static void unicodeRead() throws Exception
    {
        System.out.println("--------Unicode-------------");
        //读取到JVM中的字符(字符串)均会使用Unicode编码
        InputStreamReader isr = new InputStreamReader(new FileInputStream("demo.txt"), "gbk");

        int num = 0;
        while((num = isr.read()) != -1)
        {
            System.out.println(num);
            System.out.println(Integer.toBinaryString(num));
        }
    }

    public static void gbkRead() throws Exception
    {
        System.out.println("--------GBK-----------------");
        //直接读取字节数据,没有编码转换过程
        FileInputStream fis = new FileInputStream("demo.txt");

        int num = 0;
        while((num = fis.read()) != -1)
        {
            System.out.println(num);
            System.out.println(Integer.toBinaryString(num));
        }
    }
}


InputStream与Reader

InputStream和Reader是所有输入流的基类,都是抽象类,本身并不能创建实例来执行输入
但它们将成为所有输入流的模版,所以它们的方法是所有输入流都可以使用的方法

InputStream里包含如下三个方法:
int read()    //读取1个字节的数据。前面补24个0,返回32位的int值,当到达流末尾时返回-1
int read(byte[] b)    //读取最大b.length个字节的数据,并存储在字节数组b中,返回实际读取的字节数,到达流末尾返回-1
int read(byte[] b, int off, int len)    //读取最多len长度的字节,放入b中,并从off位置开始存放,返回实际读取的字节数,到达流末尾返回-1

Reader里包含如下三个方法:
int read()    //读取1个字符。前面补16个0,返回32位的int值,当到达流末尾时返回-1
int read(char[] cbuf)
int read(char[] cbuf, int off, int len)


OutputStream与Writer

OutputStream包含的方法:
void write(int c)    //输出一个字节的数据(c的最低8位)
void write(byte[] buf)    //将字节数组输出到输出流中(几乎用不到)
void write(byte[] buf, int off, int len)    //从off开始到len的长度,输出到输出流中(最常用)
void flush()    //只有写数据才需要刷新流,字节流可以不用刷新

Writer包含的方法:
void write(int c)    //输出一个字符(c的低16位)
void write(char[] buf)
void write(char[] buf, int off, int len)

void write(String str)
void write(String str, int off, int len)

void flush()    //只有写数据才需要刷新流


FileInputStream与FileOutputStream

Constructor-FileOutputStream
FileOutputStream(File file, boolean append)    //append为true时,从文件末尾追加内容

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
使用字节流复制音乐文件
*/

class FileOutputStreamTest
{
    public static void main(String[] args) throws IOException
    {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try
        {
            fis = new FileInputStream("r:/0.mp3");
            fos = new FileOutputStream("r:/1.mp3");
            //创建1M大小的容器,用于存放读取的字节数据
            byte[] bbuf = new byte[1024*1024];
            //实际读取的字节数
            int hasRead = 0;
            //循环读取
            while((hasRead = fis.read(bbuf)) != -1)
            {
                //写入容器中实际读取到的数据
                fos.write(bbuf, 0, hasRead);
            }
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if(fis != null)
            {
                fis.close();
            }
            if(fos != null)
            {
                fos.close();
            }
        }
    }
}


FileReader与FileWriter

FileReader是InputStreamReader的子类,FileWriter是OutputStreamWriter的子类。即是转换流的子类

FileReader与FileWriter均使用系统默认的编码集,不可以指定编码集(转换流可以指定编码集)

Constructor:FileWriter
FileWriter (String filename, boolean append)    //append如果为true,在创建FileWriter对象时不会清空原有文件内容,从而实现了在文件末尾处续写内容

import java.io.FileWriter;
import java.io.IOException;
/**
FileWriter的使用
*/

class FileWriterTest
{
    public static void main(String[] args) throws IOException
    {
        //创建一个FileWriter对象,该对象初始化就要明确被操作的文件,如果文件已存在则覆盖
        FileWriter fw = new FileWriter("r:/demo.txt");
        //写入到内存中,并没有写入到文件
        fw.write("abced");
        //刷新内存中的数据到文件
        fw.flush();
        //关闭FileWriter流
        fw.close();
    }
}


字符缓冲流:BufferedReader与BufferedWriter

BufferedReader是Reader的子类
BufferedWriter是Writer的子类

BufferedReader中注意如下方法:
String readLine()    //读取一行数据,当遇到换行(\n)或回车(\r)或(\r\n)时,则认为所读取的数据为一行,这些行结束标记决定读取到哪里为止。如果读取的是一个文件,文件末尾的标记也可以作为行结束标记

BufferedWriter中注意如下方法:
其中所有的write方法,都必须flush()刷新后才会输出
void newLine()    //跨平台的换行

注意:
如果BufferedWriter流输出的数据将由readLine()读取,则需要结束标记和刷新才可以被读取到
bw.write("写出数据");
bw.newLine();
bw.flush();

BufferedReader和BufferedWriter只能包装字符流,如果要包装字节流,需要先通过转换流转换

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
/**
从bufferedreader.txt一行行的读取并写入到bufferedwriter.txt中
*/

class BufferedTest
{
    public static void main(String[] args) throws Exception
    {
        BufferedReader br = new BufferedReader(new FileReader("r:/bufferedreader.txt"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("r:/bufferedwriter.txt"));

        String buf = null;
        while((buf = br.readLine()) != null)    //readLine返回该行内容的字符串,不包含任何行终止符
        {
            bw.write(buf);
            bw.newLine();
            bw.flush();
        }

        br.close();
        bw.close();        
    }
}


打印流:PrintStream与PrintWriter

打印流可以将各种数据类型的数据原样打印

字节打印流(PrintStream)在OutputStream体系下,是FilterOutputStream的子类
字符打印流(PrintWriter)在Writer体系下,是Writer的子类

Constructor:

PrintStream构造函数可以接收的参数类型(即目的地):
1,File对象。File    //如new PrintStream(new File("r:/a.txt"));
2,字符串路径。String    //如new PrintStream("r:/a.txt");
3,字节输出流。OutputStream    //如new PrintStream(new OutputStream("r:/a.txt"), true);    //true为自动刷新

PrintWriter构造函数可以接收的参数类型(即目的地):
1,File对象。File    //同上
2,字符串路径。String    //同上
3,字节输出流。OutputStream    //同上
4,字符输出流。Writer    //同上

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
/**
输入什么就打印出什么
*/

class PrintTest
{        
    public static void main(String[] args) throws Exception
    {
        //标准键盘输入流被BufferedReader包装
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        //打印到System.out(标准输出设备)
        PrintWriter pw = new PrintWriter(System.out, true);

        String str = null;
        while((str = br.readLine()) != null)
        {
            if("over".equals(str))
                break;
            pw.println(str);
        }

        br.close();
        pw.close();
    }
}


转换流:InputStreamReader与OutputStreamWriter

InputStreamReader是Reader子类,OutputStreamWriter是Writer子类

转换流对字节输入流进行包装,同时可以被缓存流包装以提高效率

InputStreamReader:将字节输入流转换成指定字符集(不指定则为默认字符集)的字符输入流
OutputStreamWriter:将字符输出流转换成指定字符集的(不指定则为默认字符集)字节输出流

Java中System.in代表标准输入设备(键盘),键盘输入内容都是文本内容,所以我们可以使用InputStreamReader将其转化为字符输入流
普通Reader读取输入内容时依然不方便,将Reader再次包装成BufferedReader,BufferedReader的readLine方法可以一次读取一行内容

import java.io.*;
/**
转换流
*/
 
class InputStreamReaderTest
{
    public static void main(String[] args) throws Exception
    {
        //System.in返回的是InputStream的子类实例
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
 
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
 
        String buf = null;
 
        while((buf = br.readLine()) != null)
        {
            if("exit".equals(buf))
            {
                System.exit(1);
            }
 
            bw.write(buf);
            bw.newLine();
            bw.flush();
        }
 
        br.close();
    }
}


序列流:SequenceInputStream

把多个输入流合并成一个输入流

Constructor:
SequenceInputStream(Enumeration<? extends InputStream> e)
SequenceInputStream(InputStream s1, InputStream s2)

import java.io.*;
import java.util.*;
/**
SequenceInputStream
*/
 
class SequenceInputStreamTest
{
    public static void main(String[] args) throws Exception
    {
        Vector<InputStream> v = new Vector<InputStream>();
 
        v.add(new FileInputStream("r:/1.txt"));
        v.add(new FileInputStream("r:/2.txt"));
        v.add(new FileInputStream("r:/3.txt"));
 
        Enumeration<InputStream> en = v.elements();
 
        SequenceInputStream sis = new SequenceInputStream(en);
 
        FileOutputStream fos = new FileOutputStream("r:/4.txt");
 
        byte[] buf = new byte[1024];
 
        int len = 0;
 
        while((len = sis.read(buf)) != -1)
        {
            fos.write(buf, 0, len);
        }
 
        sis.close();
        fos.close();
    }
}


对象流:ObjectInputStream与ObjectOutputStream

如果需要将某个对象保存到磁盘或者通过网络传输,这个类应该实现Serializable接口或者Externalizable接口
使用Serializable来显示序列化很简单,只需实现该接口,无需实现任何方法,一旦某个类实现了Serializable接口,该类的对象就是可序列化的

Serializable这样的接口称为标记接口

import java.io.*;
/**
对象序列化
*/
 
class ObjectSerializable
{
    public static void main(String[] args) throws Exception
    {
        writeObj();    //把一个对象写入文件
        readObj();    //从文件中读取对象
    }
 
    public static void writeObj() throws Exception
    {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("r:/obj.txt"));
        oos.writeObject(new Person("lisi", 22));
 
        oos.close();
    }
 
    public static void readObj() throws Exception
    {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("r:/obj.txt"));
        Person p = (Person)ois.readObject();
        System.out.println(p);
 
        ois.close();
    }
}
 
class Person implements Serializable
{
    //如果不指定UID,改变类中的成员,UID会产生变化,就无法读取以前保存的对象。指定UID,就没有问题
    public static final long serialVersionUID = 42L;
 
    private String name;
    private int age;
 
    Person(String name, int age)
    {
        this.name = name;
        this.age = age;
    }
 
    public String toString()
    {
        return name+"::"+age;
    }
}


管道流:PipedInputStream与PipedOutputStream

输出流和输入流建立连接后,输出流的数据直接由输入流接受,通常结合线程使用

建立连接的两种方式:
1)构造方法
2)in.connect(out)

import java.io.*;
/**
管道流
*/
 
class PipedStreamTest
{
    public static void main(String[] args) throws Exception
    {
        PipedInputStream in = new PipedInputStream();
        PipedOutputStream out = new PipedOutputStream();
 
        in.connect(out);    //管道连通
 
        Read r = new Read(in);
        Write w = new Write(out);
 
        new Thread(r).start();    //读取数据
        new Thread(w).start();    //写出数据
    }
}
 
class Read implements Runnable
{
    private PipedInputStream in;
 
    Read(PipedInputStream in)
    {
        this.in = in;
    }
 
    public void run()
    {
        try
        {
            byte[] buf = new byte[1024];
 
            int len = 0;
 
            while((len = in.read(buf)) != -1)
            {
                System.out.println(new String(buf, 0, len));
            }
 
            in.close();
        }
        catch(Exception e)
        {
        }
    }
}
 
class Write implements Runnable
{
    private PipedOutputStream out;
 
    Write(PipedOutputStream out)
    {
        this.out = out;
    }
 
    public void run()
    {
        try
        {
            out.write("hello hello".getBytes());
 
            out.close();
        }
        catch(Exception e)
        {
        }
    }
}


任意访问文件:RandomAccessFile

该类不是IO体系中子类,而是直接继承Object,但它是IO包中成员,因为它具备读和写的功能

内部封装了一个大型byte数组,而且通过指针对数组的元素进行操作
可以通过long getFilePointer()获取指针位置
同时可以通过void seek(long pos)改变指针的位置

其实完成读写的原理就是内部封装了字节输入流和字节输出流

import java.io.RandomAccessFile;
/**
RandomAccessFile
*/

class RandomAccessFileDemo
{
    public static void main(String[] args) throws Exception
    {
        writeFile();
        readFile();
        writeFile2();
    }

    public static void writeFile() throws Exception
    {
        //读写模式
        RandomAccessFile raf = new RandomAccessFile("ran.txt", "rw");

        raf.write("张三".getBytes());
        raf.writeInt(97);
    }

    public static void readFile() throws Exception
    {
        //只读模式
        RandomAccessFile raf = new RandomAccessFile("ran.txt", "r");

        //设置指针位置
        raf.seek(8);

        byte[] buf = new byte[4];
        raf.read(buf);

        String name = new String(buf);

        int age = raf.readInt();

        System.out.println(name);
        System.out.println(age);
    }

    public static void writeFile2() throws Exception
    {
        //读写模式
        RandomAccessFile raf = new RandomAccessFile("ran.txt", "rw");

        //设置指针位置
        raf.seek(8*3);

        raf.write("周期".getBytes());

        raf.write(54);

        raf.close();
    }
}


字节数组流:ByteArrayInputStream和ByteArrayOutputStream

ByteArrayInputStream的源是数组,从数组中读取数据
ByteArrayOutputStream的目的是内部封装的可变长度数组
这两个流都是对数组进行操作,所有数据都存储在内存中,没有任何底层资源访问(例如创建文件),所以不需要关闭流,也不会产生任何IOException

Method: ByteArrayOutputStream
byte[] toByteArray()    //返回存储的字节数组(有效数据)

import java.io.*;
/**
把文件存入数组
 
源:文件
目的:数组
*/
 
class ByteArrayTest
{
    public static void main(String[] args) throws Exception
    {
        byte[] context = new ByteArrayTest().getBytes("r:/demo.txt");
        System.out.println(new String(context));
    }
 
    //接收一个文件路径,返回字节数组
    public byte[] getBytes(String src) throws Exception
    {
        InputStream is = new FileInputStream(src);
 
        ByteArrayOutputStream ba = new ByteArrayOutputStream();
 
        byte[] buf = new byte[1024];
        int len = 0;
 
        while((len = is.read(buf)) != -1)
        {
            ba.write(buf, 0, len);
        }
 
        return ba.toByteArray();
    }
}



---------------------- ASP.Net+Android+IOS开发</a>、 .Net培训、期待与您交流! ----------------------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值