【Java学习笔记】——IO基础知识

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012198209/article/details/81639306

    最近准备学习NIO与Netty相关的知识,但是尴尬地发现自己已经把IO的基本知识忘得差不多了,于是拿出以前的Java基础的书重新学习了一遍,记此博客作为笔记。

一、访问文件和目录

    File类可以使用文件路径字符串来创建File实例,该路径既可以是绝对路径,也可以是相对路径。在默认情况下系统会按照用户的工作路径来作为相对路径,这个路径由系统属性“user.dir”指定,通常也就是运行Java虚拟机所在的路径。

    File类中的一些对于文件操作的方法如下:

(一)访问文件名或路径

1)String getName()  返回File对象所表示的文件名或文件路径

2)String getPath()    返回File对象所对应的相对路径名。

3)File getAbsoluteFile() 返回File对象的绝对路径文件

4)String getAbsolutePath() 返回File对象所对应的绝对路径名

5)String getParent() 返回File对象所对应目录的父目录

6) boolean renameTo(File dest) 重命名File对象的文件或目录

(二)文件检测

1boolean exists()   判断File对象的文件或目录是否存在

2)bool canWrite()     判断File对象是否可写

3)boolean canRead()判断File对象是否可读

4)boolean isDirectory() 判断File对象是否是目录

5)boolean isFile() 判断File对象是否是文件

6)boolean  isAbsolute() 判断File对象是否采用绝对路径

(三)文件信息

1)long length()   File对象对应文件的长度

2)long lastNodified()   File对象最后修改的时间

(四)文件操作

1)boolean createNewFile()   检查文件是否存在,当文件不存在时创建一个新的文件

2) boolean delete()   删除File对象所对应的文件或目录 

(五)目录操作

1)boolean mkdir()   创建一个File对象所对应的路径

2)String[] list()   列出File对象所有的子文件名和路径名

3)File[] listFile()   列出File对象的所有子文件或路径

4)static File[] listRoots()   列出系统所有的根路径

    以下是其中几个方法的实践    

public class FileTest {
	public static void main(String args[]) throws Exception{
		File file = new File(".");//.表示当前目录,相当于创建目录
		System.out.println(file.getName());
		System.out.println(file.getParent());
		System.out.println(file.getAbsoluteFile());
		System.out.println(file.getAbsoluteFile().getParentFile());
		
		File tmpFile = File.createTempFile("ocean", ".txt", file); //创建临时文件
		tmpFile.deleteOnExit();
		
		File newFile = new File(System.currentTimeMillis()+"~"); //创建文件
		System.out.println("newFile是否存在:"+newFile.exists());
		newFile.createNewFile();  //用此抽象文件的路径创建文件
		newFile.mkdir();  //创建目录,经过这两步,文件被存储在硬盘上
		String[] fileList = file.list();
		System.out.println("=====当前路径下所有文件=====");
		for(String fileName : fileList) {
			System.out.println(fileName);
		}
		File[] roots = File.listRoots();
		System.out.println("=====系统所有跟路径=====");
		for(File root : roots) {
			System.out.println(roots);
		}
	}
}

(六)  文件过滤器

    File类的list()方法中可以接收一个FilenameFilter参数,通过这个参数可以列出符合条件的文件。FilenameFilter接口里包含了一个accept(File dir, String name)方法,该方法将会对指定的File的所有文件以及子目录进行迭代,如果返回true,则list()方法会列出该子目录或者文件。

public class FileNameFilter {
	public static void main(String args[]) {
		File file = new File(".");
		String[] nameList = file.list((dir, name)->name.endsWith(".java") || 
				new File(name).isDirectory()); //输出所有java文件以及目录
		for(String name : nameList) {
			System.out.println(name);
		}
	}
}

输出结果:

二、Java的IO流

1、基本概念

    Java的IO流是实现输入输出的基础,可以方便的实现输入输出操作。在Java中不同的输入输出源(键盘、文件、网络连接等)都抽象地表述为流。就好像水管中的水流一样,只能顺序读取,想要读取后面的数据,就必须等前面的数据流出以后。通过流的方式,我们可以使用相同的方式来访问来自不同源头的输入/输出。

    按照流的分类,可以分为输入流和输出流。

    输入流:只能从中读取数据,不能写入数据到其中;

    输出流:只能向其中写入数据,不能从中读取数据。

    之前学习输入输出流的时候,有一个问题困扰了我很久,那就是分不清到底是输入流负责写数据还是输出流负责写数据,反之也是。因为仅仅从字面上来看,正反好像都有道理。这里要注意,输入和输出这两个概念中的“入”和“出”,针对的是当前在执行的程序,所以说输入流指的是输入到当前程序,当然是负责读取数据;输出流指的是从当前程序输出,当然也就是负责写数据的。

    Java的输入流主要由InputStream和Reader两个类作为基类,而输出流由OutpuStream和Writer两个类作为基类。这四个类都是抽象类,无法创建实例。

2、字节流和字符流

    字节流与字符流主要的区别是他们的的处理方式。字节流是最基本的,采用ASCII编码,所有的InputStream和OutputStream的子类都是,主要用在处理二进制数据,它是按字节来处理的。也就是说输入输出流中传输的是一个个8位字节码。但实际中很多的数据是文本,于是就有了了字符流的概念,采用Unicode编码。它是按虚拟机的encode来处理,也就是要进行字符集的转化。字符流中传输的是16位的字符,Reader和Writer用来处理字符流。

3、节点流和处理流

    可以从一个特定的IO设备(磁盘、网络)中执行读取和写入操作的流叫做节点流,节点流也被称为低级流,也就是对基本的数据流进行操作。当使用节点流进行输入输出时,程序会直接连接到实际的数据源,和实际的节点连接。

    处理流则像它的名字一样,是要对一个存在的流进行一些封装的处理,使得程序可以用相同的代码来访问不同来源的流数据。处理流其实也是一种典型的装饰器模式,可以消除不同节点流之间的差异,提供更方便的方法实现输出输入。

4、使用输入/输出流

    输入输出流都提供了隐式的指针来记录当前IO的位置,每次读取或者写入数据的时候,指针会自动移动,记录位置。

    在处理流中体现了Java IO的设计灵活性,体现在以下方面:

    1)性能提高:以提高缓冲的方式提高效率;

    2)操作便捷:处理流提供了一系列方法来一次性输入/输出批量的内容。

    InputStream和Reader是所有输入流的基类,其中包含一些方法,可以作为所有输入流的通用模版。

    InputStream中包含这三个方法:

    read() :读取单个字节,返回读取的信息;

    read(byte b[]) :读取相当于b数组大小的一串字节,并且存入b数组中;

    read(byte b[], int off, int len) :读取最多len个字节的一串字节,将其存入到b数组中偏移量为off的位置。

    Reader中包含三个read方法:

    read()、read(char buf[])、read(char buf[], int off, int len) 具体用法和上面的三个相似,在此不赘述。

public class FileInputStreamTest {
	public static void main(String args[]) throws IOException{
		//默认路径为工程路径。   ./开头为从相对路径开始
		FileInputStream in = new FileInputStream("./src/IO/FileTest.java");
		byte[] bs = new byte[1024];
		while(in.read(bs)>0) {
			System.out.println(new String(bs));
		}
		in.close();
	}
}

    此程序打印FileTest.java文件中的代码,使用read(byte[] b)方法读取。

    同样的,OutputStream和Writer中的三个方法分别为:write(int c)、write(byte[]/char[] buf)、write(byte[]/char[] buf, int len, int off),分别表示向指定的输出流中写入c(可以代表字节或者字符),写入数组中的数据以及写入数组中的从off位置开始的长度为len的数据。

    另外,由于Writer可以用字符串来代替字符数组,即以String为参数。于是还有以下两个方法:

    write(String str)  将str字符串输出到输出流中。

    write(String str, int off, int len)  将str字符串中的从off开始的长度为len的子串输出到输出流中。

5、输入输出流体系

    下图是java.io包中的基本输入输出流体系。

分类 字节输入流 字节输出流 字符输入流 字符输出流
抽象基类 InputStream OutputStream Reader Writer
访问文件 FileInputStream FileOutputStream FileReader FileWriter
访问数组 BiteArrayInputStream BiteArrayOutputStream CharArrayReader CharArrayWriter
访问管道 PipedInputStream PipedOutputStream PipedReader PipedWriter
访问字符串     StringReader StringWriter
缓冲流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter
转换流     InputStreamReader OutputStreamWriter
对象流 ObjectInputStream ObjectOutputStream    
抽象基类 FilterInputStream FilterOutputStream FilterReader FilterWriter
打印流   PrintStream   PrintWriter
推回输入流 PushbackInputStream   PushbackReader  
特殊流 DataInputStream DataOutputStream    

    下面我们来简单研究一下处理流的基本用法。

public class PrintStreamTest {
	public static void main(String args[]) throws IOException{
		try(
			FileOutputStream out = new FileOutputStream("output.txt");
			PrintStream ps = new PrintStream(out);)
		{
			ps.println("输出流文件");
			ps.print(new PrintStreamTest());
		}catch (IOException e) {
			e.printStackTrace();
		}
	}
}

    上面的程序现创建了一个FileOutputStream对象,然后使用一个PrintStream实例包装了这个输出流对象,然后我们就可以用这个PrintStream进行各种各样的操作,而不用再一个个调用write方法对数据进行写入了。我们经常使用的System.out就是PrintStream类型的。

6、转换流

    输入输出流体系中还提供了两个转换流,这两个转换流用于将字节流转换为字符流。其中InputStreamReader将字节输入流转换成字符输入流,OutputStreamWriter将字节输出流转换成字符流。

    下面程序将System.in转换成InputStreamReader,再将其包装成BufferedReader,利用BufferedReader的readLine()方法可以一次读取一行内容,因为BufferedReader具有缓冲的功能,它以换行符为标志,如果没有读到换行符则打印程序阻塞;直到读到换行符,程序输出一行内容。

public static void main(String args[]) throws IOException{
		try(
			InputStreamReader ir = new InputStreamReader(System.in);
			BufferedReader br = new BufferedReader(ir);)
		{
			String str = new String();
			while((str = br.readLine()) != null) {
				if(str.equals("exit")) {
					System.exit(1);
				}
				System.out.println(str);
			}
		}catch (IOException e) {
			e.printStackTrace();
		}
	}

    下面程序通过Java命令启动另一个进程,并将本程序的内容输出到另一个程序中,另一个程序读取数据并将其打印到一个txt文件 中。(注意:java 命令只能执行编译好的 .class文件,要先用javac命令将其编译成 .class文件,并且java命令默认是在当前的工作目录下执行的,在这里是项目的根目录)

public class WriteToProcess {
	public static void main(String args[]) throws IOException{
		Process p = Runtime.getRuntime().exec("java ReadStandard");
		try(
			//这里是用的是本程序的输出流作为p的输入流
			PrintStream ps = new PrintStream(p.getOutputStream()))
		{
			ps.println("你好");
			ps.println("Hello World ~~~");
		}
	}
}


public class ReadStandard {
	//可以接收标准输入并将数据写入文件
	public static void main(String args[]) {
		try (
			Scanner sc = new Scanner(System.in);
			PrintStream ps = new PrintStream(new FileOutputStream("out.txt")))
		{
			//使用回车作为分隔符
			sc.useDelimiter("\n");
			while(sc.hasNext()) {
				ps.println("获取的输入:" + sc.next());
			}
		}catch (IOException e) {
			e.printStackTrace();
		}
	}
}

7、RandomAccessFile

    RandomAccessFile是Java输入/输出流体系中功能最丰富的文件内容访问类,既可以读取文件内容,也可以向文件输出数据。与普通的输入/输出流不同的是,RandomAccessFile支持跳到文件任意位置读写数据,RandomAccessFile对象包含一个记录指针,用以标识当前读写处的位置,当程序创建一个新的RandomAccessFile对象时,该对象的文件记录指针对于文件头(也就是0处),当读写n个字节后,文件记录指针将会向后移动n个字节。除此之外,RandomAccessFile可以自由移动该记录指针

    RandomAccessFile包含两个方法来操作文件记录指针:

  • long getFilePointer():返回文件记录指针的当前位置
  • void seek(long pos):将文件记录指针定位到pos位置

    RandomAccessFile类在创建对象时,除了指定文件本身,还需要指定一个mode参数,该参数指定RandomAccessFile的访问模式,该参数有如下四个值:

  • r:以只读方式打开指定文件。如果试图对该RandomAccessFile指定的文件执行写入方法则会抛出IOException
  • rw:以读取、写入方式打开指定文件。如果该文件不存在,则尝试创建文件
  • rws:以读取、写入方式打开指定文件。相对于rw模式,还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备,默认情形下(rw模式下),是使用buffer的,只有cache满的或者使用RandomAccessFile.close()关闭流的时候儿才真正的写到文件
  • rwd:与rws类似,只是仅对文件的内容同步更新到磁盘,而不修改文件的元数据

    具体用法可以参考博客:https://www.cnblogs.com/baoliyan/p/6225842.html

猜你喜欢

转载自blog.csdn.net/u012198209/article/details/81639306