Java:IO

 

IO里面主要学习五个类(File、OutputStream、InputStream、Reader、Writer)和一个接口(Serializable)。

一、File类

File类是唯一一个与文件本身操作(创建、删除、取得信息等)有关的程序类。

1、File类的基本操作:

①:实例化File类对象(两种构造方法):

        a:public File(String pathname):根据绝对路径来产生File类对象。

        b:public File(String parent, String child):相对路径进行创建。

        这里解释一下,什么是相对路径。比如说:天安门广场在中国北京市的北京市东城区东长安街,而我在天安门的五星红旗的旗台下。那么我告诉别人我的位置就有两种方式:绝对路径:中国北京市的北京市东城区东长安街天安门广场旗台下;相对路径:我先告诉别人天安门广场的位置,然后再告诉别人我自己相对于天安门广场的位置。

②:创建新文件:public boolean createNewFile() throws IOException

③:判断文件是否存在:public boolean exists()

④:删除文件:public boolean delete()

2、File类的目录操作:

①:取得当前路径的上一级目录:public String getParent()

②:取得当前路径的上一级目录的file类对象:public File getParentFile()

③:创建目录(无论有多少级父目录,都会创建):public boolean mkdirs()

3、取得文件信息:

①:判断当前路径是否是文件:public boolean isFile()

②:判断当前路径是否是目录:public boolean isDirectory()

③:取得当前文件大小(单位字节):public long length()

④:取得当前文件最后修改日期:public long lastModified()

下来,我们对上面提到的所有方法综合起来进行应用,看代码:

public class Test{
	public static void main(String[] args) throws Exception {
		File file = new File("C:"+File.separator+"Users"+File.separator+"Administrator"+File.separator+
				"Desktop"+File.separator+"java"+File.separator+"test"+File.separator+"testIO"
				+File.separator+"123.txt");
		if(!file.exists()) {
			file.getParentFile().mkdirs();
			file.createNewFile();
			System.out.println("改路径是否是文件:"+file.isFile());
			System.out.println("该文件大小:"+file.length());
			System.out.println("该文件上次修改时间:"+new Date(file.lastModified()));
		}else {
			file.delete();
		}
	}
}

程序运行结果:

改路径是否是文件:true
该文件大小:0
该文件上次修改时间:Fri Aug 10 16:32:31 CST 2018

可以看到,运行之后,在定义路径创建了一个文件“123.txt”。这里稍微解释一下:File.separator,separator是File类的一个静态属性,用它来得到当前环境下路径的分隔符。

二、字节流与字符流

File类不支持对文件的处理,如果要处理文件内容,必须通过流的操作来完成。“流”分为输入流和输出流,在java.io包中,流分为字节流与字符流。

下面用两张图来形象的说说字节流与字符流:

字节流与字符流操作的本质区别只有一个:字节流是原生的操作,而字符流是需要经过处理的操作。

不管是字节流还是字符流,其基本的操作流程几乎是一样(都需要下面四个步骤):

a:根据文件路径创建File类对象。

b:根据字节流或字符流的子类实例化父类对象(因为字节流和字符流输入输出流都是抽象类,所以要用其子类实力化父类对象,向上转型来完成)。

c:进行数据的读取或写入操作。

d:关闭流。

下来,我们依次来看看字节流与字符流。

1、字节输出流

这里用到的类是OutputStream类:public abstract class OutputStream implements Closeable, Flushable

OutputStream类实现了Closeable和Flushable两个接口的两个方法:

①:public void close() throws IOException:关闭流的方法。

②:void flush() throws IOException:刷新缓冲区的方法。

在OutputStream类中还定义了其它的方法:

①:public abstract void write(int b) throws IOException:输入单个字节

②:public void write(byte b[]) throws IOException:将给定的字节数组的内容全部输出

③:public void write(byte b[], int off, int len) throws IOException:将所给字节数组的内容部分输出

因为OutputStream类是一个抽象类,所以需要用它的子类(FileOutputStream)为其实例化,我们只需要关心子类的构造方法,通过FileOutputStream类来进行文件的操作:

①:public FileOutputStream(File file) throws FileNotFoundException

②:public FileOutputStream(File file, boolean append)

这两个构造方法有什么区别呢?第一个构造方法在对应路径的文件中进行输出的时候,默认覆盖掉文件内的所有内容;而第二个方法除了接收路径参数外,还有第二个属性,它是boolean类型的:传递“true”在文件原有内容上进行追加输出,而传递“false”时,跟第一种构造方法相同,对文件原有内容进行覆盖。

下面,我们用字节输出流来对目标文件进行输出,看下面代码:

public class Test{
	public static void main(String[] args) throws Exception {
		File file = new File("C:"+File.separator+"Users"+File.separator+"Administrator"+File.separator+
				"Desktop"+File.separator+"java"+File.separator+"test"+File.separator+"testIO"
				+File.separator+"123.txt");
		OutputStream out = new FileOutputStream(file);
		String str = "我爱你中国!";
		out.write(str.getBytes());
		out.close();
	}
}

程序运行结果:

***注意:在进行文件输出的时候,所有的文件将自动帮助用户创建,不再需要createNewFile()进行手工创建。

2、字节输入流

这里用到的类是InputStream类:public abstract class InputStream implements Closeable

与字节输出流相同,InputStream也是一个抽象类,所以,想要进行实例化,只能通过子类(FileInputStream)进行实例化。

InputStream类中提供了read()方法,下面对read()方法的返回值进行说明:

①:返回字节数组的大小

②:返回读取数据的大小

③:返回-1,表示读取完成

下面,我们就对刚刚写到“123.txt”文件中的内容进行读取,看下面代码:

public class Test{
	public static void main(String[] args) throws Exception {
		File file = new File("C:"+File.separator+"Users"+File.separator+"Administrator"+File.separator+
				"Desktop"+File.separator+"java"+File.separator+"test"+File.separator+"testIO"
				+File.separator+"123.txt");
		InputStream in = new FileInputStream(file);
		byte[] data = new byte[1024];
		int len = in.read(data);
		String str = new String(data, 0, len);
		System.out.println(str);
		in.close();
	}
}

程序运行结果:

我爱你中国!

3、字符输出流

这里用到的是Writer类,Writer类的结构和方法的使用与OutputStream非常类似,只是Writer类提供了直接写入String的方法而已。它也需要子类(FileWriter)为其实例化。

刚才说到,Writer类提供了直接写入String的方法:

public void write(String str) throws IOException

下面使用Writer实现对文件输出:

public class Test{
	public static void main(String[] args) throws Exception {
		File file = new File("C:"+File.separator+"Users"+File.separator+"Administrator"+File.separator+
				"Desktop"+File.separator+"java"+File.separator+"test"+File.separator+"testIO"
				+File.separator+"123.txt");
		Writer out = new FileWriter(file,true);
		out.write("我爱你IT!");
		out.close();
	}
}

程序运行结果:

4、字符输入流

这里用到的是Reader类,Reader类的结构和方法的使用与InputStream非常类似,它也需要子类(FileReader)为其实例化。

直接看下面的代码:

public class Test{
	public static void main(String[] args) throws Exception {
		File file = new File("C:"+File.separator+"Users"+File.separator+"Administrator"+File.separator+
				"Desktop"+File.separator+"java"+File.separator+"test"+File.separator+"testIO"
				+File.separator+"123.txt");
		Reader in = new FileReader(file);
		char[] arr = new char[1024];
		int len = in.read(arr);
		String str = new String(arr, 0, len);
		System.out.println(str);
		in.close();
	}
}

程序运行结果:

我爱你中国!
我爱你IT!

5、总结:

字节流与字符流的代码形式上差别不大,但是要是用的话首选字节流操作。因为所有的字符流操作,无论是读入还是输出,数据都先保存在缓存当中。如果字符流不关闭,数据就有可能保存在缓存中,没有输出到目标源。这种情况下,必须强制刷新,才能够得到完整数据。

字节流可以处理一切的文件,而字符流处理文本文件。

三、内存操作流

前面的操作都是针对于文件进行IO处理。除了文件之外,IO操作也可以发生在内存之中,这种流称为内存操作流。文件流的操作里面一定会产生一个文件数据(不管最后这个文件是否被保存)。

如果现在需要进行IO处理,但是又不希望产生文件。这种情况下就可以使用内存作为操作终端。

内存操作流也分为两类:

字节内存流:ByteArrayInputStream  ByteArrayOutputStream

字符内存流:CharArrayReader    CharArrayWriter   

下面我们先用内存流来实现一下大小写转换的的操作:

public class Test{
	public static void main(String[] args) throws Exception {
		String msg = "hello world";
		InputStream input = new ByteArrayInputStream(msg.getBytes());
		OutputStream output = new ByteArrayOutputStream();
		int temp = 0;
		while((temp = input.read()) != -1) {
			output.write(Character.toUpperCase(temp));
		}
		System.out.println(output);
		input.close();
		output.close();
	}
}

这个时候发生了IO操作,但是没有文件产生。

内存操作流的核心就是:将所有OutputStream输出的内容全部保存在了程序里面。

四、打印流:

打印流解决的就是OutputStream设计的缺陷,属于OutputStream功能的增强版。如果操作的不是二进制数据,只是想通过程序向终端目标输出信息的话,OutputStream不是很方便,它的缺点有两个:

①:所有信息必须转换成字节数组。

②:如果要输出的是int、double等类型就不方便了。

1、自己设计一个打印流:

class Print{
	private OutputStream output;
	public Print(OutputStream output) {
		this.output = output;
	}
	public void print(String str) {
		try {
			this.output.write(str.getBytes());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public void println(String str) {
		this.print(str + "\r\n");
	}
	public void print(int data) {
		this.print(String.valueOf(data));
	}
	public void println(int data) {
		this.println(String.valueOf(data));
	}
	public void print(Double data) {
		this.print(String.valueOf(data));
	}
	public void println(Double data) {
		this.println(String.valueOf(data));
	}
}
public class Test{
	public static void main(String[] args) throws Exception {
		Print print = new Print(new FileOutputStream(new File("C:"+File.separator+"Users"+File.separator+"Administrator"+File.separator+
				"Desktop"+File.separator+"java"+File.separator+"test"+File.separator+"testIO"
				+File.separator+"123.txt")));
		print.print("姓名:");
		print.println("张三");
		print.print("年龄:");
		print.println(18);
		print.print("职业:");
		print.println("程序员");
		print.print("工资:");
		print.println(3000.25);
	}
}

其实打印流的本质就是对OutputStream的功能进行类封装。Java有自己的打印流处理类。

2、系统常见的打印流

PrintStream:字节打印流。

PrintWriter:字符打印流。

打印流的具体使用与上面我们自己实现的打印流用法相同,在这里不再说明。

五、System类对IO的支持

在System类中定义了三个常量:

①:public final static InputStream in:标准输入(显示器)

②:public final static PrintStream out:标准输出(键盘)

③:public final static PrintStream err:错误输出

1、系统输出

由于System.out是PrintStream的实例化对象,PrintStream又是OutputStream的子类,所以可以用System.out直接为OutputStream实例化,这时,OutputStream输出的位置将变为标准输出(显示器)。看下面代码:

public class Test{
	public static void main(String[] args) throws IOException  {
		OutputStream outputStream = System.out;
		outputStream.write("hello world".getBytes());
	}
}

2、系统输入

System.in对应类型是InputStream,而这种输入流指的是由用户通过键盘进行输入。java本身并没有直接的用户输入处理,如果要实现这种操作,必须使用IO来进行完成。

那么,到底如何使用IO进行完成呢?看下面代码:

public class Test{
	public static void main(String[] args) throws IOException  {
		InputStream in = System.in;
		byte[] data = new byte[1024];
		System.out.println("请输入信息...");
		int temp = in.read(data);
		System.out.println(new String(data,0,temp));
	}
}

程序运行结果:

请输入信息...
123        //输入数据
123        //系统输出数据

六、两种输入流

1、BufferedReader类:缓冲的输入流,而且是一个字符流的操作对象。在此类中有如下方法(读取一行数据):

public String readLine() throws IOException:这个方法可以读取一行数据,以回车为换行符。看下面代码:

public class Test{
	public static void main(String[] args) throws IOException  {
		BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));
		System.out.println("请输入信息:");
		String str = buf.readLine();
		System.out.println(str);
	}
}

程序输出结果:

请输入信息:
hello world   //键盘输入信息
hello world   //标准输出信息

2、Scanner类

Scanner类可以说是对BufferedReader类进行优化的类,它是一个专门进行输入流处理的程序类,同时也可以结合正则表达式进行各项处理,这个类中有以下几个方法:

①:判断是否有指定类型的数据:public boolean hasNextXxx()

②:取得指定类型的数据:public 数据类型 nextXxx()

③:定义分隔符:public Scanner useDelimiter(Pattern pattern)

④:构造方法:private Scanner(InputStream  source)

使用Scanner实现数据输入,看下面代码:

public class Test{
	public static void main(String[] args)  {
		Scanner scanner = new Scanner(System.in);
		System.out.println("请输入数据:");
		if(scanner.hasNext()) {
			System.out.println(scanner.next());
		}
	}
}

程序运行结果:

请输入数据:
123abc
123abc

3、总结

从代码简洁性来说(除了二进制文件的拷贝处理外),打印流使用PrintStream,信息输入流用Scanner。

七、序列化

对象序列化是指:将内存中保存的对象变为二进制数据流的形式进行传输,或者是将其保存在文本当中。所有需要被序列化的类必须实现Serializable接口,这个接口只是一个标识,内部没有定义任何方法。JVM识别到you'有此标识时,自动序列化。

1、实现对象序列化:

class Person implements Serializable{
	private String name;
	private int age;
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
}
public class Test{
	public static final File FILE = new File("C:"+File.separator+"Users"+File.separator+"Administrator"+File.separator+
			"Desktop"+File.separator+"java"+File.separator+"test"+File.separator+"testIO"
			+File.separator+"123.txt");
	public static void main(String[] args) throws Exception  {
		ser(new Person("张三", 18));	
	}
	public static void ser(Object obj) throws FileNotFoundException, IOException {
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FILE));
		oos.writeObject(obj);
		oos.close();
	}
}

程序运行结果:

 sr 鏃ュ父缁冧範.Person9縉?弎} I ageL namet Ljava/lang/String;xp   t 寮犱笁

2、实现对象反序列化

在上面代码的Test类加下面方法实现反序列化:

	public static void dser() throws Exception{
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream(FILE));
		System.out.println(ois.readObject());
		ois.close();
	}

程序运行结果:

Person [name=张三, age=18]

3、transient关键字

使用Serializable接口序列化时,会将类中所有属性进行序列化保存,如果希望希望某些属性不被保存,就可以加上transient关键字。

对上面代码的“age”属性加上transient关键字后,重新进行序列化和反序列化:

class Person implements Serializable{
	private String name;
	private transient int age;
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
}
public class Test{
	public static final File FILE = new File("C:"+File.separator+"Users"+File.separator+"Administrator"+File.separator+
			"Desktop"+File.separator+"java"+File.separator+"test"+File.separator+"testIO"
			+File.separator+"123.txt");
	public static void main(String[] args) throws Exception  {
		dser();
	}
	public static void ser(Object obj) throws FileNotFoundException, IOException {
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FILE));
		oos.writeObject(obj);
		oos.close();
	}
	public static void dser() throws Exception{
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream(FILE));
		System.out.println(ois.readObject());
		ois.close();
	}
}

程序输出结果:

Person [name=张三, age=0]

完!

 

猜你喜欢

转载自blog.csdn.net/weixin_41890097/article/details/81564595