Java IO

Posted cpluspluser

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java IO相关的知识,希望对你有一定的参考价值。

一、简述

Java提供了完整的IO操作,IO分为输入流和输出流,Java又分为了字节和字符两大类。字符专门用来处理字符型数据,非常方便;字节也可处理文本数据,但是更多地是用来处理非文本的数据。


二、基于"字节"的IO

1、InputStream和OutputStream

基于字节的IO中的输入和输出分别为InputStream类和OutputStream类,这两个类都是抽象类。


2、FileInputStream和FileOutputStream

FileInputStream用于从文件中读取信息,FileOutputStream用于往文件中写入信息。

可以这样从文件中读取数据:

FileInputStream fis = new FileInputStream("buf.txt");
int length = 0;
byte[] buf = new byte[1024];
while((length = fis.read(buf)) != -1) {
	String str = new String(buf, 0, length);
	System.out.println(str);
}

fis.close()

用FileOutputStream读取数据:

FileOutputStream fos = new FileOutputStream("buf.txt");
byte[] out = "12342134436435\\n".getBytes();
fos.write(out);
out = "hsdkafhlsadfhsadf".getBytes();
fos.write(out);

fos.close();

3、BufferedInptuStream和BufferedOutputStream

BufferedInputStream和BufferedOutputStream为输入输出加入了缓冲区,这样读取起来更加高效。

自己也可以实现同样功能的类,如可以这样设计:

import java.io.*;

public class MyBufferedInputStream {
	private InputStream in;
	private byte[] buf = new byte[1024 * 4];
	private int pos = 0, count = 0;
	
	MyBufferedInputStream(InputStream in)
	{
		this.in = in;
	}
	
	//一次读一个字节,从缓冲区(字节数组)获取
	public int myRead() throws IOException
	{
		//通过in对象读取硬盘上数据,并存储buf中
		if(count == 0)
		{
			count = in.read(buf);
			if(count < 0)
				return -1;
			pos = 0;
			byte b = buf[pos];
			
			count--;
			pos++;
			return b&0xff;  //b may be read 11111111B in the memory,decimal is -1,that will be make an error.
		}
		else if (count > 0)
		{
			byte b = buf[pos];

			count--;
			pos++;
			return b&255;
		}
		return -1;
	}
	
	public void myClose() throws IOException
	{
		in.close();
	}
}

import java.io.*;

public class BufferedStreamDemo {
	public static void main(String[] args) throws IOException{
		BufferedOutputStreamDemo();
		BufferedInputStreamDemo();
	}
	
	public static void BufferedInputStreamDemo() throws IOException {
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("buf.txt"));
		int buf;
		while((buf = bis.read()) != -1) {
			System.out.print((char)buf);
		}
		
		bis.close();
	}
	
	public static void BufferedOutputStreamDemo() throws IOException{
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("buf.txt"));
		byte[] buf = "buffered output stream content.\\n".getBytes();
		bos.write(buf);
		buf = "second row.\\n".getBytes();
		bos.write(buf);
		bos.flush();  //刷新缓冲区
		
		bos.close();
	}
}

4、DataInputStream和DataOutputStream

DataInputStream和DataOutputStream是专门用于操作基本数据类型的流对象。可以以指定类型输入输出数据。

public class DataStreamDemo {
	public static void main(String[] args) throws IOException {
		writeData();
		readData();
		
		writeUTFDemo();
		readUTFDemo();
	}
	
	public static void readData() throws IOException
	{
		DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
		
		int num = dis.readInt();
		double d = dis.readDouble();
		boolean bl = dis.readBoolean();
		
		System.out.println(num + " " + d + " " + bl);
		
		dis.close();
	}
	
	public static void readUTFDemo() throws IOException
	{
		DataInputStream dis = new DataInputStream(new FileInputStream("utfdata.txt"));
		
		String s = dis.readUTF();
		
		System.out.println(s);
		dis.close();
	}
	
	public static void writeData() throws IOException
	{
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
		dos.writeInt(243);
		dos.writeDouble(2.34234);
		dos.writeBoolean(true);
		
		dos.close();
	}
	
	public static void writeUTFDemo() throws IOException
	{
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdata.txt"));
		
		dos.writeUTF("你好");
		
		dos.close();
	}
}

输出为:

243 2.34234 true
你好

5、PipedInputStream和PipedOutputStream

管道流也用于传输数据,输入管道流与输出管道流连接,输入管道流提供要写入管道输出流的所有数据字节。

不建议使用单线程,因为会发生死锁问题。

class Read implements Runnable
{
	private PipedInputStream in;
	Read(PipedInputStream in)
	{
		this.in = in;
	}
	public void run()
	{
		try {
			byte[] buf = new byte[1024];
			int len = in.read(buf);
			String s = new String(buf, 0, len);
			System.out.println(s);
			in.close();
		}catch(IOException e)
		{
			throw new RuntimeException("管道读取流失败");
		}
	}
}

class Write implements Runnable
{
	private PipedOutputStream out;
	Write(PipedOutputStream out)
	{
		this.out = out;
	}
	public void run()
	{
		try {
			out.write("piped arrived".getBytes());
		}catch(IOException e)
		{
			throw new RuntimeException("管道输出流失败");
		}
	}
}

public class PipedStreamDemo {
	public static void main(String[] args) throws IOException {
		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();
	}
}

6、ByteArrayInputStream和ByteArrayOutputStream

这两个流对象专门用来操作字节。

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

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

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

源设备可以有:

  • 键盘:System.in
  • 硬盘:FileStream
  • 内存:ArrayStream

目的设备可以有:

  • 控制台:System.out
  • 硬盘:FileStream
  • 内存:ArrayStream
//数据源
ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFD".getBytes());

//数据目的
ByteArrayOutputStream bos = new ByteArrayOutputStream();

int by = 0;
while((by = bis.read()) != -1)
{
	bos.write(by);
}

System.out.println(bos.size());
System.out.println(bos.toString());

输出为:

7
ABCDEFD

7、SequenceInputStream

SequenceInputStream流对象可以将两个或多个InputStream转换为单一InputStream。

Vector<FileInputStream> v = new Vector<FileInputStream>();
		
v.add(new FileInputStream("1.txt"));
v.add(new FileInputStream("2.txt"));
v.add(new FileInputStream("3.txt"));

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

SequenceInputStream sis = new SequenceInputStream(en);

FileOutputStream fos = new FileOutputStream("123.txt");

byte[] buf = new byte[1024];
int len = 0;
while((len = sis.read(buf)) != -1)
{
	fos.write(buf, 0, len);
	
}

fos.close();
sis.close();

8、LineNumberInputStream

此类可以跟踪流中的行号,其实就是传入一个计数变量,每读取一行打印一次罢了,这个流已经过时了,可以看LineNumberReader类。


9、StringBufferInputStream

此类也过时了,可以看StringReader类。


10、PrintStream

PrintStream是打印流,此类提供了打印方法,可以将各种数据类型都原样打印。

构造函数可以接收的参数类型:

  1. file对象。File
  2. 字符串路径。String
  3. 字节输出流。OutputStream
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("buf.txt"));
		
PrintStream ps = new PrintStream(System.out, true);
int buf = 0;
while((buf = bis.read()) != -1) {
	ps.print((char)buf);
}

bis.close();

三、基于"字符"的IO

1、Reader和Writer

Reader和Writer分别对应InputStream和OutputStream的字符形式,用来表示字符IO的输入和输出流,这两个类也是抽象类。


2、InputStreamReader和OutputStreamWriter

InputStreamReader是读取转换流,OutputStream是输出转换流,这两个流搭建起了字节IO和字符IO的桥梁。

public class TranslateStream {
	public static void main(String[] args) throws IOException{
		InputStream in = System.in;
		
		InputStreamReader isr = new InputStreamReader(in);  //byte stream to char stream
		
		BufferedReader bufr = new BufferedReader(isr);  //use buffer to improve speed
		String line = null;
		while((line = bufr.readLine()) != null)
		{
			if("over".equals(line))  //exit condition
				break;
			System.out.println(line.toUpperCase());
		}
		
		bufr.close();
	}
}

3、FileReader和FileWriter

此二类分别对应FileInputStream和FileWriter类。

//创建一个文件读取流对象,和指定名称的文件相关联
//要保证该文件是已经存在的,若不存在,抛异常FileNotFoundException
FileReader fr = new FileReader("demo.txt");

int ch;
while((ch = fr.read()) != -1) {
	System.out.print((char)ch);	
}

char[] text = new char[20];
int n = fr.read(text);
System.out.println("n:"+ n + new String(text));

fr.close();
//有同名文件将被覆盖,没有则创建
//即明确数据要存放的目的地
FileWriter fw = null;
try {
	fw = new FileWriter("demo.txt");
	fw.write("abcde");    //向流中写数据
	//fw.flush();         //刷新缓冲区
	fw.write("jhahaha");  //向流中写数据	
}catch(IOException e) {
	e.printStackTrace();
}finally {
	try {
		if(fw != null) {
			//关闭流资源,关闭资源之前会刷新一次缓冲区
			fw.close();	
		}
	}catch(IOException e) {
		e.printStackTrace();
	}
}

4、BufferedReader和BufferedWriter

见名便知此二类是字符型的缓冲区流,因为是字符流,因此可以直接用String接收。

FileReader fr = new FileReader("buf.txt");
BufferedReader bufr = new BufferedReader(fr);
String str;
while((str = bufr.readLine()) != null) {
	System.out.println(str);
}
bufr.close();
//创建一个字符写入流对象
FileWriter fw = new FileWriter("buf.txt", true);

//为了提高字符写入操作,加入了缓冲技术
//只要将需要被提高效率的流对象作为参数传递给缓冲区
BufferedWriter bufw = new BufferedWriter(fw);

for(int i = 0; i < 5; i++) {
	bufw.write("abdedsd" + i);
	bufw.newLine();  //换行符,可跨平台
	bufw.flush();    //刷新缓冲区
}


//只要用到缓冲区,就要记得刷新
//bufw.flush();

//其实关闭缓冲区,就是在关闭缓冲区中的流对象,故不用写fw.close了
bufw.close();

5、PipedReader和PipedWriter

对应字节型的管道流对象。


6、CharArrayReader和CharArrayWriter

用于操作字符数组。


7、LineNumberReader

FileReader fr = new FileReader("buf_copy.txt");
		
LineNumberReader lnr = new LineNumberReader(fr);
String line = null;
lnr.setLineNumber(5);  //设置行号
while((line = lnr.readLine()) != null)
{
	System.out.println(lnr.getLineNumber() + ":" + line);
}

lnr.close();

8、PrintWriter

字符打印流构造函数可以接收的参数类型有:

  1. file对象。File
  2. 字符串路径。String
  3. 字节输出流。OutputStream
  4. 字符输出流。Writer
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

//PrintWriter out = new PrintWriter(System.out,true);
PrintWriter out = new PrintWriter(new FileWriter("printwriter.txt"), true);

String line = null;

while((line = bufr.readLine()) != null)
{
	if("over".equals(line))
		break;
	out.println(line.toUpperCase());
	//out.flush();
}

out.close();
bufr.close();

9、StringBuffer和StringBuilder

StringBuffer类是线程安全的可变字符序列,是一个类似String的字符串缓冲区,但不能修改。

StringBuffer主要操作append()和insert方法,可以重载来接收任意类型的数据。每个方法才能有效地将给定的数据转换成字符串,然后将字符串的字符追加或插入到字符串缓冲区中。

StringBuilder类是StringReader的简单替换,若是字符串被单个线程使用,则建议使用StringBuilder。


四、基于"磁盘"的IO

1、File

IO流可以对流进行操作却不可直接操作文件,File类在IO包中唯一代表磁盘文件本身,该类定义了一些和平台无关的方法来操作文件。

File file = new File("F://Project//java//Fileiostream//test.txt");
if(file.exists()) {
	String name = file.getName();
	String parent = file.getParent();
	long length = file.length();
	boolean bool = file.canWrite();
	System.out.println("file name:" + name);
	System.out.println("file parent:" + parent);
	System.out.println("file size:" + length);
	System.out.println("是否为可写文件?" + bool);
}

File dir = new File("F://Project//java//");
if(dir.isDirectory()) {
	File[] files = dir.listFiles();
	for(int i = 0; i < files.length; i++) {
		File f = files[i];
		System.out.println("第" + (i + 1) + "个文件的名称是:" + f.getAbsolutePath());
	}
}

2、RandomAccessFile

该类不算是IO体系的子类,而是直接继承自Object。但是它可操作IO包中成员,因其具备读和写功能。

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

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

通过构造函数可看出其只能操作文件且操作文件还有模式:r,rw...

如果模式为r,不会创建文件,会去读一个已存在的文件,如果该文件不存在,则会报异常。

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

而且该对象的构造函数要操作的文件不存在,会自动创建,若存在不会覆盖。

因为该类支持随机读写访问文件,所以可以通过seek来实现多线程文件下载。

public static void readFile() throws IOException
{
	RandomAccessFile raf = new RandomAccessFile("ran.txt", "r");
	
	//调整对象中指针
	//raf.seek(7);
	
	//跳过指定的字节数
	raf.skipBytes(7);
	
	byte[] buf = new byte[4];
	raf.read(buf);
	
	String name = new String(buf);
	
	int age = raf.readInt();
	
	System.out.println("name:" + name);
	System.out.println("age:" + age);
	
	raf.close();
}

public static void writeFile() throws IOException
{
	RandomAccessFile raf = new RandomAccessFile("ran.txt", "rw");
	
	raf.write("neo".getBytes());
	raf.writeInt(97);  //write只写低8位,会丢失,故用其提供的带Int版本的.
	raf.write("mike".getBytes());
	raf.writeInt(108);  //write只写低8位,会丢失,故用其提供的带Int版本的.
	
	raf.close();
}

public static void writeFile_2() throws IOException
{
	RandomAccessFile raf = new RandomAccessFile("ran.txt", "rw");
	raf.seek(8*0);
	raf.write("周期".getBytes());
	raf.writeInt(37);
	
	raf.close();
}

五、练习

1、复制图片

思路:

  1. 用字节读取流对象和图片关联
  2. 用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。
  3. 通过循环读写,完成数据的存储
  4. 关闭资源

实现:

public class CopyPic {
	public static void main(String[] args) {
		FileOutputStream fos = null;
		FileInputStream fis = null;
		try 
		{
			fos = new FileOutputStream("cpp_copy.png");
			fis = new FileInputStream("C++.png");
			
			byte[] buf = new byte[1024];
			
			int len = 0;
			
			while((len = fis.read(buf)) != -1)
			{
				fos.write(buf, 0, len);
			}
			
			System.out.println("复制成功!");
		}
		catch(IOException e)
		{
			throw new RuntimeException("复制文件失败.");
		}
		finally
		{
			try 
			{
				if (fos != null)
				{
					fos.close();
				}
			}catch(IOException e)
			{
				e.printStackTrace();
			}

			try
			{
				if (fis != null)
				{
					fis.close();
				}
			}catch(IOException e)
			{
				e.printStackTrace();
			}
		}
	}
}


2、成绩录入

有五个学生,每个学生有3门课的成绩
从键盘录入以上数据
输入的格式:如:zhangsan,30,40,60计算出总成绩
并把学生的信息和计算出的部分数高低存放在磁盘文件"stud.txt"中

  1. 描述一个学生对象
  2. 定义一个可操作学生对象的工具类

思路:

  1. 通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象.
  2. 因为学生很多,所以用集合存储.因为以要排序,因而可以使用TreeSet.
  3. 将集合的信息写入到一个文件中.

实现:

class Student implements Comparable<Student>
{
	private String name;
	private int math, chinese, english;
	private int sum;
	
	Student(String name, int ma, int ch, int en)
	{
		this.name = name;
		this.math = ma;
		this.chinese = ch;
		this.english = en;
		this.sum = ma + ch + en;
	}
	
	public String getName()
	{
		return this.name;
	}
	
	public int getSum()
	{
		return sum;
	}
	
	public int hashCode()
	{
		return name.hashCode() + sum * 78;
	}
	
	public boolean equals(Object obj)
	{
		if(!(obj instanceof Student))
			throw new ClassCastException("类型不匹配");
		
		Student s = (Student)obj;
		
		return this.name.equals(s.name) && this.sum==s.sum;
	}
	
	public String toString()
	{
		return "student[" + name + " math:" + math + " chinese:" + chinese + " english:" + english + "]";
	}
	
	@Override
	public int compareTo(Student s) {
		int num = new Integer(this.sum).compareTo(new Integer(s.sum));
		if(num == 0)
			return this.name.compareTo(s.name);
		return num;
	}
}

class StudentInfoTool
{
	public static Set<Student> getStudents() throws IOException
	{
		return getStudents(null);
	}
	
	public static Set<Student> getStudents(Comparator<Student> cmp) throws IOException
	{
		BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
		
		String line = null;
		
		Set<Student> stus = null;
		if(cmp == null)
			stus = new TreeSet<Student>();
		else
			stus = new TreeSet<Student>(cmp);
		
		while((line = bufr.readLine()) != null)
		{
			if("over".equals(line))
				break;
			
			String[] info = line.split(",");
			
			Student stu = new Student(info[0], Integer.parseInt(info[1]), 
					Integer.parseInt(info[2]),
					Integer.parseInt(info[3]));
			stus.add(stu);
		}
		
		bufr.close();
		
		return stus;
	}
	
	public static void write2File(Set<Student> stus) throws IOException
	{
		BufferedWriter bufw = new BufferedWriter(new FileWriter("stuinfo.txt"));
		
		for(Student stu : stus)
		{
			bufw.write(stu.toString() + "\\t");
			bufw.write(stu.getSum() + "");
			bufw.newLine();
			bufw.flush();
		}
		
		bufw.close();
	}
}

public class Practice {
	public static void main(String[] args) throws IOException {
		Comparator<Student> cmp = Collections.reverseOrder();
		
		Set<Student> stus = StudentInfoTool.getStudents(cmp);
		
		StudentInfoTool.write2File(stus);
	}
}

以上是关于Java IO的主要内容,如果未能解决你的问题,请参考以下文章

java.io.ByteArrayInputStream

java大对象存取的简单实现的代码

csharp C#代码片段 - 使类成为Singleton模式。 (C#4.0+)https://heiswayi.github.io/2016/simple-singleton-pattern-us

Android android.view.InflateException Binary XML 文件第 16 行:膨胀类片段时出错

golang代码片段(摘抄)

java代码在片段活动中不起作用