JAVA IO和NIO详解

在学习io之前我么先来学一下File类,因为io免不了跟文件打交道

File

        // File file1 = new File("C:\\aaa\\a.txt");//绝对路径,文件存在

        //File file2 = new File("demo17.txt");//相对路径(项目根目录),文件不存在

        // File file3 = new File("C:\\aaa","a.txt");//作用跟:file1相同

        //  File file4 = new File("C:\\aaa");
        //  File file5 = new File(file4,"a.txt");//跟file3和file1的效果相同
        
        //public boolean createNewFile():创建文件(如果成功创建返回true,否则(文件已经存在)返回false)
        //public boolean mkdir():创建单级目录
        //public boolean mkdirs():创建多级目录
        //public boolean delete():可以删除文件、可以删除目录(一定要是空目录)
        //判断功能
        // public boolean isDirectory():判断是否是一个目录;
        // public boolean isFile():判断是否是一个文件;
        // public boolean exists():判断文件/目录是否存在;
        // public boolean canRead():判断是否可读;
        // public boolean canWrite():判断是否可写;
        // public boolean isHidden():判断是否隐藏;
        //File类的基本获取功能:
        // public String getAbsolutePath():获取绝对路径
        //public String getPath():获取File封装的路径
        //public String getName():获取文件/目录的名称
        //public long length():获取文件的大小(单位:字节)
        //public long lastModified():最后修改时间(单位:毫秒)
        //File类的高级获取功能:
        //public String[] list():如果当前File表示的是一个目录,则返回此目录下所有的子文件/子目录的名称的String[]数组;
        //public File[] listFiles():如果当前File表示的一个目录,则返回此目录下所有的子文件/子目录的File[]数组;
        //区别:如果仅仅想获取文件名,使用第一种;
        //如果需要对子文件/子目录进行进一步的操作,可以使用第二种;
        //重命名功能
        //public boolean renameTo(File dest):将当前File对象,重命名到dest表示的路径下;
        //注意:
        //1.如果dest和原文件不在同目录下,重命名操作将会相当于"剪切"操作;
        //2.如果dest和原文件在同目录下,相当于"重命名"

io的分类

1.字节流:
		1).输出流:OutputStream(抽象类):
				输出的方法:
				1).write(int n):输出一个字节
				2).write(byte[] b):输出一个字节数组;
				3).write(byte[] b, int off,int len):输出字节数组的一部分;

				|--FileOutputStream(类):
				|--FilterOutputStream(不学):
					|--BufferedOutputStream(缓冲流)
		2).输入流:InputStream(抽象类):
				读取的方法:
				1).int read():读取一个字节
				2).int read(byte[] b):读取一个字节数组;

				|--FileInputStream(类):
				|--FilterInputStream(不学):
					|--BufferedInputStream(缓冲流);
	2.字符流:
		1).输出流:Writer:
				输出的方法:
				1).write(int n):输出一个字符;
				2).write(char[] c):输出一个字符数组;
				3).write(char[] c ,int off,int len):输出字符数组的一部分;
				4).write(String s):输出一个字符串;
				5).write(String s,int off,int len):输出字符串的一部分;
				|--OutputStreamWriter(转换流)
					|--FileWriter(字符流)
				|--BufferedWriter(缓冲流):
					void newLine():输出一个换行符;
		2).输入流:Reader:
				读取的方法:
				1).int read():读取一个字符;
				2).int read(char[] c):读取一个字符数组;

				|--InputStreamReader(转换流);
					|--FileReader(字符流)
				|--BufferedReader(缓冲流):
					String readLine():读取一行数据;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
FileOutputStream

 java.io.FileOutputStream(类):
 * 构造方法:文件可以不存在,会自动创建
 * 		FileOutputStream(String fileName) :创建一个向具有指定名称的文件中写入数据的输出文件流。 
 * 		FileOutputStream(File file) : 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 
 * 		FileOutputStream(String name, boolean append)  创建一个向具有指定 name 的文件中写入数据的输出文件流。 
 * 		FileOutputStream(File file, boolean append)创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 
 * 
 * 输出的方法:
 * 		write(int n):输出一个字节;
 * 		write(byte[] b):输出一个字节数组;
 * 		write(byte[] b,int off,int len):输出一个字节数组的一部分;
 * 成员方法啊:
 * 		close():关闭流

FileInputStream

java.io.FileInputStream(类):
 * 构造方法:文件必须存在,否则运行时异常;
 * 		FileInputStream(String name) 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。 
 * 		FileInputStream(File file) :通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。 
 * 读取的方法:
 * 		int read():读取一个字节
 * 		int read(byte[] byteArray):读取若干个字节,填充参数的byte[]数组;

练习字节流复制文件

  /**
     * 字节流复制文件
     */
    private void fileDemo(String path) {
        try {
            //输入流读取文件信息
            FileInputStream in = new FileInputStream(path);
            //输出流吧文件信息输出到新的path
            FileOutputStream out = new FileOutputStream(path);
            //一次读取一个字节
            //int n = 0;
            //read ==-1 说明没有读取到字节
            //while ((n = in.read()) != -1) {
                //一次输出一个字节
                //out.write(n);
           // }
            //一次读写一个字节数组
            byte[] bytes = new byte[1024];
            int n = 0;
            while ((n=in.read(bytes))!=-1){
                out.write(bytes,0,n);
            }
            in.close();
            out.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

字节缓冲流
无特有方法,用法和上面一样
BufferedInputStream in = new BufferedInputStream(new FileInputStream(path));
编码

编码:将看得懂的,变为看不懂
 * 		String-->getBytes():使用平台默认的字符集
 * 				 getBytes(String charset):指定编码方式
 * 解码:将看不懂的,变为看得懂的:
 * 		String-->构造方法:String(byte[] b):使用平台默认字符集解码
 * 						String(byte[] b,String charset):指定解码方式
 * 
 * 
 * 总之:对于文本文件,写入和读取使用相同的"编码方式",就会避免乱码的情况;

转换流

1).输出流:Writer
 * 					|--OutputStreamWriter(类--转换流):是字符流通向字节流的桥梁
 * 						构造方法:
 * 						OutputStreamWriter(OutputStream s):使用平台默认字符集
 * 						输出的方法(五种):
 * 						1).write(int n):输出一个字符
 * 						2).write(char[] c):输出一个字符数组
 * 						3).write(char[] c ,int off, int len):输出字符数组的一部分;
 * 						4).write(String str):输出一个字符串
 * 						5).write(String str,int off, int len):输出字符串的一部分;
 * 		2).输入流:Reader
 * 					|--InputStreamReader(类--转换流):是字节流通向字符流的桥梁
 * 						构造方法:
 * 						InputStreamReader(InputStream in):使用平台默认字符集
 * 						读取的方法(两个):
 * 						int read():读取一个字符:返回值:读取的"字符"
 * 						int read(char[] c):读取一个字符数组;返回值:读取的"字符数量"
	InputStreamReader in = new InputStreamReader(new FileInputStream(path));

字符流

 * 字符流:
 * 		1.输出流:Writer
 * 				|--OutputStreamWriter(类--转换流):
 * 						|--FileWriter(类--纯字符流):
 * 							构造方法:
 * 							1).FileWriter(File file)根据给定的 File 对象构造一个 FileWriter 对象。 
 * 							2).FileWriter(File file, boolean append) 根据给定的 File 对象构造一个 FileWriter 对象。 
 * 							3).FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象。 
 * 							4).FileWriter(String fileName, boolean append) 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。 
							输出的方法:
							(无特有方法,都是继承:五种)
 * 					
 * 		2.输入流:Reader
 * 				|--InputStreamReader(类--转换流):
 * 						|--FileReader(类--纯字符流):
 * 							构造方法:
 * 							1).FileReader(File file) 在给定从中读取数据的 File 的情况下创建一个新 FileReader。 
 * 							2).FileReader(String fileName) 在给定从中读取数据的文件名的情况下创建一个新 FileReader 
							读取的方法:
							(无特有方法,都是继承:两种)
public void filedemo1(String path) {
        try {
            FileReader fileReader = new FileReader(path);
            FileWriter fileWriter = new FileWriter("");
            //一次读取一个字符
            int n = 0;
            while ((n = fileReader.read()) != -1) {
                fileWriter.write(n);
            }
            //一次读取一个字节数组
            char[] chars = new char[2];
            while ((n = fileReader.read(chars)) != -1) {
                fileWriter.write(chars,0,n);
            }

            fileReader.close();
            fileWriter.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

字符缓冲流

2.字符流:
 * 		1).输出流:Writer(抽象类):
 * 					|--BufferedWriter(字符流--缓冲流):
 * 						构造方法:
 * 						1).BufferedWriter(Writer out):创建一个使用默认大小输出缓冲区的缓冲字符输出流。
 * 						输出方法:
 * 						(无特有方法,都是从父类继承的:五种)
 * 						特有成员方法:
 * 						void newLine():输出一个换行符;
 * 		2).输入流:Reader(抽象类):
 * 					|--BufferedReader(字符流--缓冲流):
 * 						构造方法:
 * 						1).BufferedReader(Reader in) :创建一个使用默认大小输入缓冲区的缓冲字符输入流。
 * 						读取的方法:
 * 						(继承父类:两种)
 * 						String readLine():读取一行数据(不读取换行符)
public void fileDemo3(String path) {
        try {
            BufferedReader bufferedReader = new BufferedReader(new FileReader(path));
            BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(""));
            //一次读一行
            String row = null;
            while ((row = bufferedReader.readLine()) != null) {//不读取换行符
                bufferedWriter.write(row);
                //添加一个换行符
                bufferedWriter.newLine();
            }
            bufferedReader.close();
            bufferedWriter.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

LineNumberReader

/*
 * LineNumberReader类:
 * 
 * 1.跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int) 和 getLineNumber(),
 * 	   它们可分别用于设置和获取当前行号。 默认情况下,行编号从 0 开始。

数据输入输出流

数据输入输出流:
 * 
 * 1.DataInputStream:可以读取Java的"基本数据类型";readXxxx()
 *   DataOutputStream:可以输出Java的"基本数据类型";writeXxxx()
 *   
 *   此类,可以读写Java基本数据类型,是按"写入",同时要按"字节"读取;

字节数组缓冲流

* 字节数组缓冲流:
 * 
 * ByteArrayOutputStream:此类实现了一个输出流,其中的数据被写入一个 byte 数组
 * ByteArrayInputStream:从"byte[]数组缓存区"中读取数据;它可以模仿字节输入流的方式,一次读取一个字节或一个字节数组;
 * 
public static void main(String[] args) throws IOException {
		FileInputStream in = new FileInputStream("demo04.txt");
		//一次读取一个字节数组
		byte[] byteArray = new byte[4];
		int n = 0;
		//先准备一个字节数组缓存区
		ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
		while((n = in.read(byteArray)) != -1){
			//以前我们这里要么向控制台显示读取的内容;
			//要么写入到其它文件中(文件复制)
			//假如,这里暂时什么都不做,可以先将已经读取的字节数组,先缓存起来
			byteOut.write(byteArray , 0 , n);//输出到:内存的byte[]数组缓存区
		}
		in.close();
		
		//一次性的,将缓存区的内容取出
		System.out.println("*****************************");
		byte[] allByteArray = byteOut.toByteArray();
		String str = new String(allByteArray);
		System.out.println(str);
		
		//ByteArrayInputStream
		ByteArrayInputStream byteIn = new ByteArrayInputStream(allByteArray);
		byte[] bArray = new byte[4];
		n = 0;
		while((n = byteIn.read(bArray)) != -1){
			System.out.println("读取的内容:" + new String(bArray,0,n));
		}
		
		
	}

RandomAccessFile


 * RandomAccessFile:
 * RandomAccessFile类不属于流,是Object类的子类。但它融合了InputStream和OutputStream的功能。支持对随机访问文件的读取和写入。
 * 
 * getFilePointer():获取文件指针;
 * seek():设置文件指针

打印流

* 1.字节打印流:PrintStream:
 * 2.字符打印流:PrintWriter:
 * 
 * 打印流的特点:
 * 	只能操作目的地,不能操作数据(只有输出流,没有输入流)
	可以操作任意类型的数据。
	如果启动了自动刷新,能够自动刷新。--PrintWriter
	可以操作文件的流
/*
 * PrintWriter:,如果启用了自动刷新,则只有在调用 println、printf 或 format 的其中一个方法时才可能完成此操作
 */
public class Demo {
	public static void main(String[] args) throws IOException {
		PrintWriter out = new PrintWriter(new FileWriter("demo07.txt"),true);
	//	out.write("Hello");
		out.println("hello");//write() + flush() + newLine()
		out.println("world");
		out.close();
		
	}
}

JAVA NIO

JDK 1.4 的java.nio* 包引入了一种新的java I/O类库,其目的在于提高速度,实际上旧的IO包已经用新的NIO重新实现过,以便充分利用这种速度的提高,因此即使我们不显示的编写NIO,也能从中受益,速度的提高在文件IO和网络IO,都有可能发生,这里我们只研究前者。

速度的提高来源于,它使用的结构方式更加接近操作系统执行IO的方式:通道缓冲器,我们可以把它想象成一个煤矿,通道是包含煤层(数据)的矿藏,而缓冲器则是,派往矿藏的卡车,卡车载满煤矿而归,我们再从卡车上获得煤炭,也就是说我们并没有直接和通道进行交互,我们只是和缓冲器交互,并把缓冲器派送到通道,通道要么从缓冲器获得数据,要么向缓冲器发送数据

唯一和通道交互的缓冲器是ByteBuffer,也就是说可以储存未加工的缓冲器,ByteBuffer是一个非常基础的类,通过告知分配多少储存空间,来创建一个ByteBuffer,并且还有一个方法选集,用以原始的字节形式或基本数据类型,输出和读取数据,但是没有办法输出或读取对象,即使字符串也不行,这种处理虽然很低级,但是正好,因为这是大多数操作系统更有效的操作方式

旧的IO库有三个被修改,可以用来产生通道FileChannel,这三个被修改的类是,FileInputStream,FileOutputStream,RandomAccessFile,注意这些是字节操纵流,和底层的NIO一致,Reader和Writer不能用于产生通道,但是java.nio.channels.Channels,提供了实用方法,可以在通道中产生Reader和Writer。

   public static void main(String[] args) {
        try {
            FileOutputStream outputStream = new FileOutputStream("data.txt");
            FileChannel channel1 = outputStream.getChannel();
            channel1.write(ByteBuffer.wrap("你好".getBytes()));
            channel1.close();

            RandomAccessFile rw = new RandomAccessFile("data.txt", "rw");
            FileChannel channel2 = rw.getChannel();
            channel2.position(channel2.size());//移到文件底部
            channel2.write(ByteBuffer.wrap("哈哈".getBytes()));
            channel2.close();

            FileInputStream fileInputStream = new FileInputStream("data.txt");
            FileChannel channel = fileInputStream.getChannel();
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            channel.read(byteBuffer);
            byteBuffer.flip();

            while (byteBuffer.hasRemaining()) {
                System.out.println((char) byteBuffer.get());
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

对于这里的任何流类,getchannel都会产生一个FileChannel,通道是一种相当基础的东西,你可以向他传送用于读写的ByteBuffer,并且可以锁定文件某些区域用于独占式访问

将字节放于ByteBuffer方法之一put方法,直接对他进行填充,填入一个或多个字节,或者基本数据 类型的值,也可以用wrap方法,将已存在的字节数组包装到ByteBuffer中,一旦如此就不在复制底层数组,而是把它作为产生ByteBuffer的储存器,我们称之为数组支持的ByteBuffer

data.txt 被RandomAccessFile打开,我们可以在文件内移动FileChannel,这里我们把它移动到文件末尾

对于只读访问,我们必须显示的用静态allocate方法来分配ByteBuffer,nio的目标就是快速的移动大量数据,因此ByteBuffer大小就显得尤为重要,这里的1K比我们通常使用的会小一点

一旦调用read来告知FileChannel向ByteBuffer储存数据,就必须调用缓冲器上的filp方法,让他做好让别人读取数据的准备,这是很拙劣的,但是适用于获得最大速度,如果我们使用缓冲器进行进一步的read操作,我们必须调用clear,来为每个read做好准备,例如下边的复制文件

    public static void main(String[] args) {
        try {
            FileChannel in = new FileInputStream("data.txt").getChannel();
            FileChannel out = new FileOutputStream("text.txt").getChannel();
            ByteBuffer byteBuffer1 = ByteBuffer.allocate(1024);

            while (in.read(byteBuffer1) != -1) {
                byteBuffer1.flip();//准备写
                out.write(byteBuffer1);
                byteBuffer1.clear();//准备读
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

每次read之后,数据就会流入缓冲器中,filp则是准备缓冲器,以便信息又write提取,write之后信息还存在缓冲器中,clear对缓冲器进行清空,以便下一个read可以正常储存数据

然而上方的方法并不是理想的形式,transferFrom和transferTo,允许俩个通道直接相连

public static void main(String[] args) {
        try {
            FileChannel in = new FileInputStream("data.txt").getChannel();
            FileChannel out = new FileOutputStream("text.txt").getChannel();
            ByteBuffer byteBuffer1 = ByteBuffer.allocate(1024);
            in.transferTo(0,in.size(),out);
            //或者
           // out.transferFrom(in,0,in.size());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值