IO流的初步了解以及基本使用

1.IO的介绍

IO,即in和out,也就是输入和输出,指应用程序和外部设备之间的数据传递,常见的外部设备包括文件、管道、网络连接。

Java 中是通过流处理IO 的,那么什么是流?

流(Stream),是一个抽象的概念,是指一连串的数据(字符或字节),是以先进先出的方式发送信息的通道。

当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。这时候你就可以想象数据好像在这其中“流”动一样。

一般来说关于流的特性有下面几点:

  • 1 先进先出:最先写入输出流的数据最先被输入流读取到。
  • 2 顺序存取:可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据。(RandomAccessFile除外)
  • 3 只读或只写:每个流只能是输入流或输出流的一种,不能同时具备两个功能,输入流只能进行读操作,对输出流只能进行写操作。在一个数据传输通道中,如果既要写入数据,又要读取数据,则要分别提供两个流。
    在这里插入图片描述

在这里插入图片描述

1.1 实例操作
1. FileInputStream、FileOutputStream(字节流)

字节流的方式效率较低,不建议使用

public class IOTest {
	public static void main(String[] args) throws IOException {
		File file = new File("D:/test.txt");

		write(file);
		System.out.println(read(file));
	}

	public static void write(File file) throws IOException {
		OutputStream os = new FileOutputStream(file, true);

		// 要写入的字符串
		String string = "松下问童子,言师采药去。只在此山中,云深不知处。";
		// 写入文件
		os.write(string.getBytes());
		// 关闭流
		os.close();
	}

	public static String read(File file) throws IOException {
		InputStream in = new FileInputStream(file);

		// 一次性取多少个字节
		byte[] bytes = new byte[1024];
		// 用来接收读取的字节数组
		StringBuilder sb = new StringBuilder();
		// 读取到的字节数组长度,为-1时表示没有数据
		int length = 0;
		// 循环取数据
		while ((length = in.read(bytes)) != -1) {
			// 将读取的内容转换成字符串
			sb.append(new String(bytes, 0, length));
		}
		// 关闭流
		in.close();

		return sb.toString();
	}
}

2、BufferedInputStream、BufferedOutputStream(缓冲字节流)

缓冲字节流是为高效率而设计的,真正的读写操作还是靠FileOutputStream和FileInputStream,所以其构造方法入参是这两个类的对象也就不奇怪了。

public class IOTest {

	public static void write(File file) throws IOException {
		// 缓冲字节流,提高了效率
		BufferedOutputStream bis = new BufferedOutputStream(new FileOutputStream(file, true));

		// 要写入的字符串
		String string = "松下问童子,言师采药去。只在此山中,云深不知处。";
		// 写入文件
		bis.write(string.getBytes());
		// 关闭流
		bis.close();
	}

	public static String read(File file) throws IOException {
		BufferedInputStream fis = new BufferedInputStream(new FileInputStream(file));

		// 一次性取多少个字节
		byte[] bytes = new byte[1024];
		// 用来接收读取的字节数组
		StringBuilder sb = new StringBuilder();
		// 读取到的字节数组长度,为-1时表示没有数据
		int length = 0;
		// 循环取数据
		while ((length = fis.read(bytes)) != -1) {
			// 将读取的内容转换成字符串
			sb.append(new String(bytes, 0, length));
		}
		// 关闭流
		fis.close();

		return sb.toString();
	}
}

3 InputStreamReader、OutputStreamWriter(字符流)

字符流适用于文本文件的读写,OutputStreamWriter类其实也是借助FileOutputStream类实现的,故其构造方法是FileOutputStream的对象

public class InputStreamReaderTest {
    @Test
    public void test1() throws IOException {
        //参数2 指明了字符集 具体使用哪个字符集 取决于文件保存时使用的字符集
        InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("11.1.txt"));

        char[] chars = new char[20];
        int len;
        while ((len = inputStreamReader.read(chars))!= -1){
            String s = new String(chars,0,len);
            System.out.println(s);
        }
        inputStreamReader.close();
    }

    /*
    综合使用 InputStreamReader和OutputStreamWriter
     */
    @Test
    public void test2() throws IOException {
        InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("11.1.txt"),"utf-8");
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream("11.1_gbk.txt"), "gbk");
        char[] chars = new char[20];
        int len;
        while ((len = inputStreamReader.read(chars))!=-1){
            outputStreamWriter.write(chars,0,len);
        }
        outputStreamWriter.close();
        inputStreamReader.close();

    }

运行结果 :
在这里插入图片描述

4、字符流便捷类

Java提供了FileWriter和FileReader简化字符流的读写,new FileWriter等同于new OutputStreamWriter(new FileOutputStream(file, true))
注:FileWriter和FileReader在效率上不如BufferedReader和BufferedWriter

    @Test
    将day09下的hello.txt文件内容读入程序中 并输出到控制台

    Reader说明点:
    1.read()的理解:返回读入的一个字符。如果达到文件的末尾,返回-1
    2.异常的处理:为了保证流资源一定可以执行关闭操作 需要使用try catch finally 来处理
    3.读入的文件一定要存在 否则就会报异常
    
    Writer说明:
    1.输出操作,对应的FiLe可以不存在的。并不会报异常
    2.FiLe对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件File对应的硬盘中的文件如果存在:
      如果流使用的构造器是:FileWriter(file,false) / FiLeWriter(file):  覆盖原有文件
      如果流使用的构造器是:FileWriter(file,true):true 是在原有文件上添加
    public void testFiLeReaderFileWriter(){
        FileInputStream fr = null;
        FileOutputStream fw = null;
        try {
            //1.创建File类的对象,指明读入和写出的文件
//            File file = new File("hello.txt");
//            File file1 = new File("hello2.txt");

            File file = new File("preview.jpg");
            File file1 = new File("preview1.jpg");

            //2.创建输入流和输出流的对象
            fr = new FileInputStream(file);
            fw = new FileOutputStream(file1);
            //3.数据的读入和写出操作
            byte [] bytes = new byte[5];
            int len;//记录每次读入到chars数组中的字符个数
            while ((len = fr.read(bytes))!=-1){
                //每次写出len个字符
                fw.write(bytes,0,len);
            }
            System.out.println("复制成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.关闭流资源
            if (fw != null)
            try {
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (fr != null)
            try {
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
5、BufferedReader、BufferedWriter(字符缓冲流)

可以用 long start = System.currentTimeMillis(); 方法来查看到Buffered缓冲流的效率明显的提高

public class BufferedTest {
    /*
    实现非文本文件的复制
     */
    @Test
    public void test1(){
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        BufferedInputStream bufferedInputStream = null;
        BufferedOutputStream bufferedOutputStream = null;
        try {
            //1.造文件
            File file = new File("preview.jpg");
            File file1 = new File("preview2.jpg");
            //2.造流
            //2.1造节点流
            fileInputStream = new FileInputStream(file);
            fileOutputStream = new FileOutputStream(file1);
            //2.2造缓冲流
            bufferedInputStream = new BufferedInputStream(fileInputStream);
            bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
            //3.复制的细节 读取 写入
            byte [] bytes = new byte[1024];
            int len;
            while ((len = bufferedInputStream.read(bytes))!= -1){
                bufferedOutputStream.write(bytes,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.资源关闭
            //要求:先关闭外层的流,再关闭内层的流
            if (bufferedOutputStream != null) {
                try {
                    bufferedOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bufferedInputStream != null)
            try {
                bufferedInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
//        //关闭外层流的同时,内层流也会自动关闭 所以可以省略关闭内层流
//        fileOutputStream.close();
//        fileInputStream.close();

        }
    }

2 IO流方法

2.1 字节流方法

字节输入流InputStream主要方法:

  • read():从此输入流中读取一个数据字节。

  • read(byte[] b) :从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。

  • read(byte[] b, int off, int len) :从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。

  • close():==关闭此输入流并释放与该流关联的所有系统资源。
    字节输出流OutputStream主要方法:

  • write(byte[] b) :将 b.length 个字节从指定 byte 数组写入此文件输出流中。

  • write(byte[] b, int off, int len) :将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。

  • write(int b) :将指定字节写入此文件输出流。

  • close() :关闭此输入流并释放与该流关联的所有系统资源。

2.2 字符流方法

字符输入流Reader主要方法:

  • read():读取单个字符。

  • read(char[] cbuf) :将字符读入数组。

  • read(char[] cbuf, int off, int len) : 将字符读入数组的某一部分。

  • read(CharBuffer target) :试图将字符读入指定的字符缓冲区。

  • flush() :刷新该流的缓冲。

  • close() :关闭此流,但要先刷新它。
    字符输出流Writer主要方法:

  • write(char[] cbuf) :写入字符数组。

  • write(char[] cbuf, int off, int len) :写入字符数组的某一部分。

  • write(int c) :写入单个字符。

  • write(String str) :写入字符串。

  • write(String str, int off, int len) :写入字符串的某一部分。

  • flush():刷新该流的缓冲。

  • close() :关闭此流,但要先刷新它。
    另外,字符缓冲流还有两个独特的方法:

  • BufferedWriter类newLine() :写入一个行分隔符。这个方法会自动适配所在系统的行分隔符。

  • BufferedReader类readLine() :读取一个文本行。

3 FileUtils工具类的使用

我们可以通过导入相关的jar包来使用FileUtiles工具类 这样的话直接用其方法就行了
简单的介绍:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
 
/**
 *文件操作工具类
 *
 * @author xiu
 * @version 2017年7月31日 下午8:17:13 
 */
public class FileUtil {
	
	private FileUtil() {
	}
 
	/**
	 * 列出指定文件夹的内容 
	 * 
	 * @param dir 指定文件夹的路径
	 * @return 指定文件夹的内容
	 */
	public static String[] ls(String dir) {
		File file = new File(dir);
		if (file.exists()) {
			return file.list();
		}
		return new String[0];
	}
	
    /**
     * 在指定的位置创建指定的文件
     *
     * @param filePath 完整的文件路径
     * @param mkdir 是否创建相关的文件夹
     * @throws Exception
     */
    public static void mkFile(String filePath, boolean mkdir) throws Exception {
        File file = new File(filePath);
        file.getParentFile().mkdirs();
        file.createNewFile();
        file = null;
    }
    
    /**
     * 在指定的位置创建文件夹
     *
     * @param dirPath 文件夹路径
     * @return 若创建成功,则返回True;反之,则返回False
     */
    public static boolean mkDir(String dirPath) {
        return new File(dirPath).mkdirs();
    }
    
	/**
	 * 写入文件(完全自定义文件格式和写入内容)
	 * 
	 * @param filename 文件名(可包含路径)
	 * @param data 要写入的数据
	 * @param flag 是否追加(true追加,false不追加)
	 * @return boolean
	 */
	public static boolean write(String filename, String data, boolean flag) {
		int i = filename.lastIndexOf('/');
		File file = new File(filename.substring(0, i));
		if (!file.exists()) {
			file.mkdirs();
		}
		try {
			FileWriter fw = new FileWriter(filename, flag);
			fw.write(data);
			fw.flush();
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
 
	/**
	 * 删除某个文件或目录 2012-3-13
	 * 
	 * @param filename
	 *            文件名或目录
	 * @return 0:删除成功,-1:指定文件夹不存在,-2:指定文件不存在
	 */
	public static int delete(String filename) {
		File file = new File(filename);
		if (file.isDirectory()) {
			String[] dir = ls(filename);
			for (int i = 0; i < dir.length; i++) {
				new File(filename + "/" + dir[i]).delete();
				if (file.isDirectory()) {
					delete(filename + "/" + dir[i]);
				}
			}
			if (file.delete()) {
				return 0;
			} else {
				return -1;
			}
		} else if (file.isFile()) {
			if (file.delete()) {
				return 0;
			} else {
				return -2;
			}
		} else {
			return -1;
		}
	}
	
    /**
     * 移动指定的文件(夹)到目标文件(夹)
     * @param source 源文件(夹)
     * @param target 目标文件(夹)
     * @param isFolder 若为文件夹,则为True;反之为False
     * @return
     * @throws Exception
     */
    public static boolean move(String source, String target, boolean isFolder)
            throws Exception {
        copy(source, target, isFolder);
        if (isFolder) {
            return delDir(source, true);
        } else {
            return delFile(source);
        }
    }
 
    /**
     * 删除指定的文件
     *
     * @param filePath 文件路径
     *
     * @return 若删除成功,则返回True;反之,则返回False
     *
     */
    public static boolean delFile(String filePath) {
        return new File(filePath).delete();
    }
 
    /**
     * 删除指定的文件夹
     *
     * @param dirPath 文件夹路径
     * @param delFile 文件夹中是否包含文件
     * @return 若删除成功,则返回True;反之,则返回False
     *
     */
    public static boolean delDir(String dirPath, boolean delFile) {
        if (delFile) {
            File file = new File(dirPath);
            if (file.isFile()) {
                return file.delete();
            } else if (file.isDirectory()) {
                if (file.listFiles().length == 0) {
                    return file.delete();
                } else {
                    int zfiles = file.listFiles().length;
                    File[] delfile = file.listFiles();
                    for (int i = 0; i < zfiles; i++) {
                        if (delfile[i].isDirectory()) {
                            delDir(delfile[i].getAbsolutePath(), true);
                        }
                        delfile[i].delete();
                    }
                    return file.delete();
                }
            } else {
                return false;
            }
        } else {
            return new File(dirPath).delete();
        }
    }
    /**
     * 复制文件/文件夹 若要进行文件夹复制,请勿将目标文件夹置于源文件夹中
     * @param source 源文件(夹)
     * @param target 目标文件(夹)
     * @param isFolder 若进行文件夹复制,则为True;反之为False
     * @throws Exception
     */
    public static void copy(String source, String target, boolean isFolder)
            throws Exception {
        if (isFolder) {
            (new File(target)).mkdirs();
            File a = new File(source);
            String[] file = a.list();
            File temp = null;
            for (int i = 0; i < file.length; i++) {
                if (source.endsWith(File.separator)) {
                    temp = new File(source + file[i]);
                } else {
                    temp = new File(source + File.separator + file[i]);
                }
                if (temp.isFile()) {
                    FileInputStream input = new FileInputStream(temp);
                    FileOutputStream output = new FileOutputStream(target + "/" + (temp.getName()).toString());
                    byte[] b = new byte[1024];
                    int len;
                    while ((len = input.read(b)) != -1) {
                        output.write(b, 0, len);
                    }
                    output.flush();
                    output.close();
                    input.close();
                }
                if (temp.isDirectory()) {
                    copy(source + "/" + file[i], target + "/" + file[i], true);
                }
            }
        } else {
            int byteread = 0;
            File oldfile = new File(source);
            if (oldfile.exists()) {
                InputStream inStream = new FileInputStream(source);
                File file = new File(target);
                file.getParentFile().mkdirs();
                file.createNewFile();
                FileOutputStream fs = new FileOutputStream(file);
                byte[] buffer = new byte[1024];
                while ((byteread = inStream.read(buffer)) != -1) {
                    fs.write(buffer, 0, byteread);
                }
                inStream.close();
                fs.close();
            }
        }
    }
    	/**
	 * 读取文件类容
	 * 
	 * @param filename 文件名(可包含路径)
	 * @return 文件类容
	 */
	public static String read(String filename) {
		try {
			String text = null;
			StringBuilder sb = new StringBuilder();
			BufferedReader input = new BufferedReader(new FileReader(filename));
			while ((text = input.readLine()) != null) {
				sb.append(text);
				sb.append("\n");
			}
			input.close();
			return sb.toString();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return "";
	}
		/**
	 * 获取包下面所有类
	 * @param pack  文件夹路径
	 * @return
	 */
	public static Set<Class<?>> getClasses(String pack) {
 
		// 第一个class类的集合
		Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
		// 是否循环迭代
		boolean recursive = true;
		// 获取包的名字 并进行替换
		String packageName = pack;
		String packageDirName = packageName.replace('.', '/');
		// 定义一个枚举的集合 并进行循环来处理这个目录下的things
		Enumeration<URL> dirs;
		try {
			dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
			// 循环迭代下去
			while (dirs.hasMoreElements()) {
				// 获取下一个元素
				URL url = dirs.nextElement();
				// 得到协议的名称
				String protocol = url.getProtocol();
				// 如果是以文件的形式保存在服务器上
				if ("file".equals(protocol)) {
					//System.err.println("file类型的扫描");
					// 获取包的物理路径
					String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
					// 以文件的方式扫描整个包下的文件 并添加到集合中
					findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
				} else if ("jar".equals(protocol)) {
					// 如果是jar包文件
					// 定义一个JarFile
					//System.err.println("jar类型的扫描");
					JarFile jar;
					try {
						// 获取jar
						jar = ((JarURLConnection) url.openConnection()).getJarFile();
						// 从此jar包 得到一个枚举类
						Enumeration<JarEntry> entries = jar.entries();
						// 同样的进行循环迭代
						while (entries.hasMoreElements()) {
							// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
							JarEntry entry = entries.nextElement();
							String name = entry.getName();
							// 如果是以/开头的
							if (name.charAt(0) == '/') {
								// 获取后面的字符串
								name = name.substring(1);
							}
							// 如果前半部分和定义的包名相同
							if (name.startsWith(packageDirName)) {
								int idx = name.lastIndexOf('/');
								// 如果以"/"结尾 是一个包
								if (idx != -1) {
									// 获取包名 把"/"替换成"."
									packageName = name.substring(0, idx).replace('/', '.');
								}
								// 如果可以迭代下去 并且是一个包
								if ((idx != -1) || recursive) {
									// 如果是一个.class文件 而且不是目录
									if (name.endsWith(".class") && !entry.isDirectory()) {
										// 去掉后面的".class" 获取真正的类名
									String className = name.substring(packageName.length() + 1, name.length() - 6);
										try {
											// 添加到classes
											classes.add(Class.forName(packageName + '.' + className));
										} catch (ClassNotFoundException e) {
											// log
											// .error("添加用户自定义视图类错误
											// 找不到此类的.class文件");
											e.printStackTrace();
										}
									}
								}
							}
						}
					} catch (IOException e) {
						// log.error("在扫描用户定义视图时从jar包获取文件出错");
						e.printStackTrace();
					}
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
 
		return classes;
	}
 
	public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive,
			Set<Class<?>> classes) {
		// 获取此包的目录 建立一个File
		File dir = new File(packagePath);
		// 如果不存在或者 也不是目录就直接返回
		if (!dir.exists() || !dir.isDirectory()) {
			// log.warn("用户定义包名 " + packageName + " 下没有任何文件");
			return;
		}
		// 如果存在 就获取包下的所有文件 包括目录
		File[] dirfiles = dir.listFiles(new FileFilter() {
			// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
			public boolean accept(File file) {
				return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
			}
		});
		// 循环所有文件
		for (File file : dirfiles) {
			// 如果是目录 则继续扫描
			if (file.isDirectory()) {
				findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,
						classes);
			} else {
				// 如果是java类文件 去掉后面的.class 只留下类名
				String className = file.getName().substring(0, file.getName().length() - 6);
				try {
					// 添加到集合中去
					// classes.add(Class.forName(packageName + '.' +
					// className));
					// 经过回复同学的提醒,这里用forName有一些不好,会触发static方法,没有使用classLoader的load干净
					classes.add(
							Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
				} catch (ClassNotFoundException e) {
					// log.error("添加用户自定义视图类错误 找不到此类的.class文件");
					e.printStackTrace();
				}
			}
		}
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值