Java从入门到精通十一(javaIO流)
IO引入
字节流和字符流(一些说明)
在java中io流分为字节流和字符流。字节流和字符流分别对应相应的读取和写入操作。整体的功能就是实现对输入输出的操作。
I/O就是input和output的缩写。而java之所以将此称之为流就是将其抽象化,来表示输入输出的功能。封装为对应的类,实现具体的功能,然后全部都存在io包当中。
用流来表示输入输出是也是非常形象的。可以想象数据传输信道中数据像流水一样进行传输。
字节流就是传输单位按为字节进行传输,字符流就是传输按照字符为单位进行传输
字节流没有用到缓冲区并不是没有用到内存
字节流与字符流的区别是什么呢?
从传输上面讲,字节流是字节在文件测层次上进行操作的,并没有用到缓冲区,字符流会用到缓冲区,然后通过缓冲区操作文件。我们是从传输上面讲,但是不要认为这个缓存区就是缓存,缓存是cache,缓冲区是buffer。这是两个不同的概念。
buffer也就是我们的缓冲区是内存空间的一部分,内存空间是预留了一定的存储空间的,用来缓冲输入或者输出。这就是我们 的缓冲区。当然缓冲区根据输入设备或者是输出设备分文输入缓冲区和输出缓冲区。
但是缓存(cache)就是我们为了内存为了弥补速度上与cpu的差异而划分出来的。当然磁盘上也有缓存,cpu也有。但是我们这里只区分内存上的缓冲区与缓存。这是完全不一样的两个概念。
字节流本身没有用到缓冲区,但是也可以加入缓冲流来加快读取效率。那就是字节缓冲流了
缓冲区是为了缓冲,缓存是为了加快存取速度。
缓冲区不等于内存,没有用到缓冲区不是没有用到内存。如果认为没有用到内存,那计算机就白学了。
在处理或者说是传输数据上面,字节流基本是可以处理任何类型的数据类型,但是字符流只能处理的是字符或者是字符串。字节流是不能直接处理Unicode字符的,字符流是可以进行处理的。在涉及到一些编码的问题上,比如文件中存在汉字,我们可以去用字符流去处理。
为什么输入流是读数据,而输出流是写数据?
不知道你是否有这样的疑问。
为什么输入流是读取数据,而输出流却是写数据。、
因为输入输出都是相对于内存来说的。input是将数据从磁盘读取到内存当中,而输出就是将数据从内存输出道磁盘。
我们程序在运行地时候也会从磁盘中被调入内存中,程序和其运行时数据都是会在内存中驻留,然后在真正执行的时候,cpu会从中读取到相应的数据指令,执行相应的指令。
字节流说明
字节输入流读数据的方法
public abstract class InputStreamextends InputStream
字节输入流InputStream是一个抽象类,直接继承于Object类。所以我们具体在应用功能的时候,最好还是去用到实现它的一些基本的类。
比较主要的读取方法在参数设定上会和字节输出流的写入方法相似。
字节输入流是用来进行读取数据的,不过读取数据的方式可以有两种,基本的是这样。
package io_demo;
//字节流读取数据
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class IoDrmo_2 {
public static void main(String args[]) throws IOException {
FileInputStream fis = new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
//int read() 从该输入流读取一个字节的数据
// int by = fis.read();
// System.out.println(by);
// System.out.println((char)by);
// fis.close();
int by = fis.read();//读取一个字节
while(by!=-1)
{
System.out.print((char)by);
by = fis.read();
}
//优化一下读取
while((by = fis.read())!=-1)
{
System.out.print((char)by);
}
fis.close();
}
}
当然我们也可以按照字节数组去读取,我们可以让一次读取多个字节。这样读取的话,就快一些。
package io_demo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class IoDemo_4 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
byte[] bytes = new byte[1024];
// System.out.println(len);
int len;
//采用循环
while((len= fis.read(bytes))!=-1)
{
System.out.print(new String(bytes,0,len));
}
fis.close();
}
}
一定要记得在操作完毕之后,关闭资源。
字节缓冲输入流
提供的主要的构造方法
从构造方法可知。我们使用它的时候需要传入一个字节缓冲输入流对象。
FileInputStream fis = new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
BufferedInputStream bis = new BufferedInputStream(fis);
同样我们可以用这样的对象去进行读取操作。
字节输出流写数据的方法
public abstract class OutputStreamextends OutputStream
字节输出流就是进行写入数据,我们可以进行向文件中写入数据。基本的抽象类还是需要去实现类中实现它的基本功能。
可以这样去实现,输出流的时候可以这样传入参数。
package java_practice;
import java.io.*;
public class FileDemo__ {
public static void main(String args[]) throws IOException {
File file = new File("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
FileOutputStream fos = new FileOutputStream(file);
fos.write(97);
fos.close();
}
}
将这个文件对象传入,当然你也可以向文件输入流一样直接传入文件路径。但是需要注意的是,如果文件不存在的话 输入流是会报错的。因为输入流是读取文件的,被读取的文件不存在,那就会报错。输出流会自动创建文件,输入流不会自动创建文件。
写入数据的时候,比如我们写入一个97,
就会转换为字符a。这就涉及到对应的编码。
对照ASCII码表,我们可以看到十进制97对应的字符为a。这个write()方法会把对应的十进制数字97按照ASCII码进行转换。
0到97的输入,会看到ASCII表进行转换,其余的编码默认会查询系统编码GBK。关于编码的问题,我们在后面说。
当然write()方法也可以接收一个数组类型。一个字节数组。去对应的源码里面一看便知。
当然还可以有不同的参数列表
当然你如果不是很想去查看源码,也可以在javaapi里面找到对应的方法说明。
与此相关的方法,都可以查阅文档得到。
byte[] by = {97,98,99,100};
//取范围写入
fos.write(by,0,by.length);
fos.write(by);
当然你不能直接给write()中直接传入字符或者是字符串,我们的直接传入类型中是直接接收的是int类型,或者是可以传入一个byte数组。
既然是字节流,我们可以尝试将字符串或者字符转换为对应的byte类型。用到的一个方法是getBytes()。查看一下源码,看看是否转换为了数组类型,你可以验证一下。
public byte[] getBytes() {
return StringCoding.encode(value, 0, value.length);
}
我们也可以直接用代码进行操作。
fos.write("hello".getBytes());
字节缓冲输出流
public class BufferedOutputStream
extends FilterOutputStream
提供的主要的两个构造方法
BufferedOutputStream(OutputStream out)
//创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
BufferedOutputStream(OutputStream out, int size)
//创建一个新的缓冲输出流,以便以指定的缓冲区大小将数据写入指定的底层输出流。
提供的一些具体的方法
void flush()
//主要是进行刷新用的
void write(byte[] b, int off, int len)
//从指定的字节数组写入 len个字节,从偏移 off开始到缓冲的输出流。
void write(int b)
//将指定的字节写入缓冲的输出流。
**从构造方法可知,在使用这个缓冲输出流的时候,我们可以传一个输出流的对象进去。**
```javascript
FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
BufferedOutputStream bos = new BufferedOutputStream(fos);
当然也可以直接这样进行传
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\\java_doc\\src\\io_demo\\demo_pratice"));
注意这样的做法叫做匿名对象,而不是匿名内部类
然后后面的写入操作基本还是一样的。可以按照字节进行写入,也可以写入字节数组。但是加入缓冲区是一定会比较快的,
用输入输出实现数据的复制
可以进行文件到文件的复制
package io_demo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
//用输入输出流进行文件的复制
public class IoDemo_3 {
public static void main(String args[]) throws IOException {
//字节输入流对象
FileInputStream fis = new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
//创建字节输出流对象
FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo\\demo_01");
int by;
while((by = fis.read())!=-1)
{
fos.write(by);
}
fos.close();
fis.close();
}
}
package io_demo;
import java.io.*;
public class IoDemo_06 {
public static void main(String args[]) throws IOException {
FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
BufferedOutputStream bos = new BufferedOutputStream(fos);
FileInputStream fis = new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
BufferedInputStream bis = new BufferedInputStream(fis);
int bu;
while((bu=bis.read())!=-1)
{
System.out.println((char)bu);
}
//一次读取一个字节数组的数据
byte[] bytes = new byte[1024];
int len;
while((len = bis.read(bytes))!=-1)
{
bos.write(bytes,0,len);
//System.out.println(new String(bytes,0,len));
}
bis.close();
fis.close();
}
}
当然是也是可以实现对视频数据地复制的。选一个视频的话。然后比较一下种复制的效率。
提供一段代码,方法仅供参考,可以自己进行优化。
package io_demo;
import java.io.*;
public class IoDemo_07 {
public static void main(String args[]) throws IOException {
//四种方式实现复制视频
// 1:基本字节流一次读取一个字节
// 2:基本字节流一次读取一个字节数组
// 3:字节缓存流一次读取一个字节
// 4:字节缓存流一次读取一个字节数组
//记录开始时间
long startTime = System.currentTimeMillis();
//复制视频
method();
method_2();
method3();
method4();
//纪录结束时间
long endTime = System.currentTimeMillis();
System.out.println("共耗时:" + (endTime - startTime) + "毫秒秒");
}
private static void method() throws IOException {
//读取字节
FileInputStream fis = new FileInputStream("D:\\KuGou\\Tank - 三国恋.mkv");
FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo\\1.mv");
int by;
while ((by = fis.read()) != -1) {
fos.write(by);
}
fos.close();
fis.close();
}
private static void method_2() throws IOException {
//一次读取一个字节数组
FileInputStream fis = new FileInputStream("D:\\KuGou\\Tank - 三国恋.mkv");
FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo\\1.mv");
byte[] bytes = new byte[1024];
int len;
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
fos.close();
fis.close();
}
private static void method3() throws IOException {
FileInputStream fis = new FileInputStream("D:\\KuGou\\Tank - 三国恋.mkv");
FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo");
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
int by;
while ((by = bis.read()) != -1) {
bos.write(by);
}
bos.close();
bis.close();
fis.close();
fos.close();
}
private static void method4() throws IOException {
FileInputStream fis = new FileInputStream("D:\\KuGou\\Tank - 三国恋.mkv");
FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo");
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
byte[] bytes = new byte[1024];
int len;
while((len = bis.read(bytes))!=-1)
{
bos.write(bytes,0,len);
}
bos.close();
bis.close();
fis.close();
fos.close();
}
}
事实其实字节缓冲流加上一次读取一个字节数组的话,就非常快了。
复制单级文件夹(文件夹中只含有文件,不包含其它文文件夹)
使用字节流来进行复制
这样的单级文件夹得话,复制的时候主要还是需要对文件夹下面的文件进行一个遍历。
package io_demo;
import java.io.*;
public class Only_doc {
public static void main(String args[]) throws IOException {
//创建数据源
File srcFolder = new File("D:\\c_doc");
String srcFolderName = srcFolder.getName();
//创建目的地
File destFolder = new File("E:\\java_doc\\src\\io_demo", srcFolderName);
if(!destFolder.exists())
{
destFolder.mkdir();
}
File[] listFiles = srcFolder.listFiles();
for(File srcFile:listFiles)
{
String srcFileName = srcFile.getName();
File destFile = new File(destFolder, srcFileName);
copyFile(srcFile,destFile);
}
}
private static void copyFile(File srcFile, File destFile) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
byte[] bytes = new byte[1024];
int len;
while((len=bis.read(bytes))!=-1)
{
bos.write(bytes,0,len);
}
bos.close();
bis.close();
}
}
需要注意的是,复制到源文件的指定父级路径一定要存在。如果不存在的话,会报错。子级别路径可以自己设定。
复制多级文件夹(文件夹中包含文件夹)
package io_demo;
import java.io.*;
public class Mult_doc
{
//复制多级文件夹
public static void main(String args[]) throws IOException {
File srcfile = new File("D:\\BaiduNetdiskWorkspace");
File destFile = new File("E:\\java_doc\\src\\io_demo");
copyFolder(srcfile,destFile);
}
//复制文件夹
private static void copyFolder(File srcfile, File destFile) throws IOException {
//判断数据源是否是目录
if(srcfile.isDirectory())
{
//在目的地下创建和数据源File名称一样的目录
String srcFileName = srcfile.getName();
File new_Folder = new File(destFile,srcFileName);
if(!new_Folder.exists())
{
new_Folder.mkdir();
}
File[] filearry = srcfile.listFiles();
for(File file:filearry)
{
copyFolder(file,new_Folder);
}
}else {
File file = new File(destFile,srcfile.getName());
copyFile(srcfile,file);
}
}
private static void copyFile(File srcFile,File destFile) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
byte[] bytes = new byte[1024];
int len;
while((len= bis.read(bytes))!=-1)
{
bos.write(bytes,0,len);
}
}
}
这里面有一个基本的应用点就是采用了递归调用,基于判断是否遍历出来的是文件还是文件夹来采用是否进行继续遍历。
一个编码与解码的过程
这是一段简单的程序
package io_demo;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class IoDemo_09 {
public static void main(String[] args) throws UnsupportedEncodingException {
//进行编码解码的演示
String s = "中国";
//使用默认的字符集将String编码为一系列字节,将结果存储到新的数组当中
//编码
byte[] bytes = s.getBytes();//使用默认的字符utf-8编码
System.out.println(Arrays.toString(bytes));
byte[] bytes1 = s.getBytes("UTF-8");
System.out.println(Arrays.toString(bytes1));
//解码过程
String ss = new String(bytes);
System.out.println(ss);
//平台的默认编码和平台默认的解码是一样的,所以可以正确输出
}
}
好,首先getBytes()。
然后我们跟进它的encode方法
可以看到它这里是指定了编码的。
然后String里面的解码时怎么回事呢?继续跟进。
然后要知道如何区跟进,我们跟进this。
从单词字面意思也可以看出这是一个解码的过程,但是我们可以继续进行跟进。
这里时一段解码的过程。所有的调用都可以进行溯源。
字符流说明
字符输入流读数据的方法
Reader是一个抽象类
我们主要演示的实现类就是InputStreamReader
当然这个缓冲流我们在后面举例
InputStreamReader的主要构造方法有这么几个
主要的方法
从提供的构造方法的·参数我们可以了解到这个实现类基本的底层还是有字节流的实现。或者可以跟进源码。
我们可以认为这个字符输入流实现类基本实现的就是字节流加上编码的效果。
现在我们可以去看看它的读取数据的方法。(到源码中)
我们可以这样用字符流进行简单读取
package io_demo;
import java.io.*;
public class IODemo_10 {
public static void main(String args[]) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice"));
int ch ;
while((ch=isr.read())!=-1)
{
System.out.println((char)ch);
}
isr.close();
}
}
当然还是可以进行一次读取一个字符数组的方法
package io_demo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
public class IoDemo_12 {
public static void main(String args[]) throws IOException {
//字符流读数据
InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice"));
// int ch;
// while((ch=isr.read())!=-1)
// {
// System.out.println((char)ch);
char[] chs = new char[1024];
int len;
while ((len = isr.read(chs)) != -1) {
System.out.println(new String(chs,0,len));
}
isr.close();
}
}
字符缓冲输入流
我们照样可以使用缓冲流
这个缓冲流也继承了Reader类,基本上也提供了父类有的read()等方法,另外还有readline()方法。我们在后面的例子中使用。
package io_demo;
import java.io.*;
public class DemoPratice01 {
public static void main(String args[]) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_01"));
BufferedReader br= new BufferedReader(isr);
int ch;
while((ch=br.read())!=-1)
{
System.out.print((char)ch);
}
char[] chars = new char[1024];
int len;
while ((len = br.read(chars))!=-1)
{
System.out.println(new String(chars,0,len));
}
}
}
使用字符缓输入流可以实现一行一行读取的操作。
BufferedReader br = new BufferedReader(new FileReader("bw.newLine();"));
String line;
while((line = br.readLine())!=null)
{
System.out.println(line);
}
字符输出流写数据的方法
还是首先选一个实现类开始一些说明
主要的构造方法
常用的方法
很明显这样的设计和输入流还是十分对称的。
下面还是简单的去使用它。
package io_demo;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
public class IoDemo__ {
public static void main(String args[]) throws IOException {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\\java_doc\\src\\io_demo\\demo_pratice1"));
osw.write("hello");
char[] chars = new char[]{'a','b','c'};
osw.write(chars,0,chars.length);
}
}
字符缓冲输出流
同样可以使用缓冲流
public class BufferedWriter extends Writer {
private Writer out;
private char cb[];
private int nChars, nextChar;
private static int defaultCharBufferSize = 8192;
缓冲流使用到的一些方法
主要的构造方法
一些普通方法
BufferedWriter bw = new BufferedWriter(osw);
同样还有进行读入行的方法
使用的时候这样将字符输出流对象进行传入就可以
BufferedWriter bw = new BufferedWriter(osw);
用输入输出实现数据的复制
package io_demo;
import java.io.*;
public class IoDemo13 {
//字符流进行复制文件
public static void main(String args[]) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java_doc\\src\\io_demo\\IoDemo_12.java"));
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\\java_doc\\src\\io_demo\\Demo_Copy.java"));
int ch;
// while((ch = isr.read())!=-1)
// {
// osw.write(ch);
// }
//一次读取一个字符数组
// char[] chars = new char[1024];
// int len;
// while((len= isr.read(chars))!=-1)
// {
// osw.write(chars,0,len);
// }
osw.close();
isr.close();
}
}
但是其实我们还可以使用这个两个类下的子类,更方便的进行操作。
package io_demo;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class IoDemo_14 {
public static void main(String args[]) throws IOException {
//使用下面的子类(可以简化书写长度)
FileReader fr = new FileReader("E:\\java_doc\\src\\io_demo\\demo_pratice");
FileWriter fw = new FileWriter("E:\\java_doc\\src\\io_demo\\demo_01");
int ch;
while((ch = fr.read())!=-1)
{
fw.write(ch);
}
char[] chars = new char[1024];
int len;
while((len= fr.read(chars))!=-1)
{
fw.write(chars,0,len);
}
fw.close();
fr.close();
}
}
同样该子类也可以结合字符缓冲流
package io_demo;
import java.io.*;
public class IoDemo15 {
public static void main(String args[]) throws IOException {
//字符缓冲流
FileWriter fw = new FileWriter("E:\\java_doc\\src\\io_demo\\demo_pratice");
BufferedWriter bw = new BufferedWriter(fw);
bw.write("Hello");
bw.close();
BufferedReader br = new BufferedReader(new FileReader("E:\\java_doc\\src\\io_demo\\demo_01"));
// 一次读一个字符数据
int ch;
while((ch=br.read())!=-1)
{
System.out.println((char)ch);
}
//一次读取一个字符数组
char[] chars = new char[1024];
int len;
while((len=br.read(chars))!=-1)
{
System.out.println(new String(chars,0,len));
}
}
}
做一个随机点名器
假设文件中有这些名字,然后我们从中读取信息,并随机输出。这样就时一个随机点名器了。
package io_demo;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
//从文件中读取数据,做一个随机点名器
public class IoDemo18 {
public static void main(String args[]) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("E:\\java_doc\\src\\io_demo\\demo_pratice"));
//创建ArrayList对象
ArrayList<String> array = new ArrayList<String>();
String line;
while((line= br.readLine())!=null)
{
array.add(line);
}
br.close();
Random r = new Random();
int i = r.nextInt(array.size());
String name = array.get(i);
System.out.println("幸运者是"+name);
}
}
集合到文件(将对象信息拼接输入到文件)
首先我们创建一个学术类
package io_demo;
public class Student {
private String sid;
private String name;
private int age;
private String address;
public Student()
{
}
public Student(String sid, String name, int age, String address) {
this.sid = sid;
this.name = name;
this.age = age;
this.address = address;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
测试代码
package io_demo;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
public class IoDemo19 {
//集合到文件,改进版
public static void main(String args[]) throws IOException {
//创建ArrayList集合
ArrayList<Student> array = new ArrayList<>();
Student s1 = new Student("001", "jgdabc", 19, "山西");
Student s2 = new Student("002", "兰舟千帆", 20, "山西");
Student s3 = new Student("003", "潇潇", 18, "湖北");
Student s4 = new Student("004", "小雨", 21, "河北");
Student s5 = new Student("005", "叶灵芸", 22, "浙江");
array.add(s1);
array.add(s2);
array.add(s3);
array.add(s4);
array.add(s5);
//创建字符流输出对象
BufferedWriter bw = new BufferedWriter(new FileWriter("E:\\\\java_doc\\\\src\\\\io_demo\\\\Student.txt"));
for (Student s : array) {
StringBuilder sb = new StringBuilder();
sb.append(s.getSid()).append(",").append(s.getName())
.append(",").append(s.getAge()).append(",").append(s.getAddress());
bw.write(sb.toString());
bw.newLine();
bw.flush();
}
//释放资源
bw.close();
}
}
文件到集合(将文件中的人物属性通过对象存放于集合中,然后进行遍历)
还是基本的学术类
package io_demo;
public class Student {
private String sid;
private String name;
private int age;
private String address;
public Student()
{
}
public Student(String sid, String name, int age, String address) {
this.sid = sid;
this.name = name;
this.age = age;
this.address = address;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
测试类
package io_demo;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
//将文本文件当中的数据督导集合当中
public class IoDemo_20 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("E:\\java_doc\\src\\io_demo\\Student.txt"));
ArrayList<Student> array = new ArrayList<>();
//调用字符缓冲输入流对象的方法读取数据
String line;
while ((line = br.readLine()) != null) {
String[] strArray = line.split(",");
Student s = new Student();
//将字符串数组的每一个元素取出来对应的复制给学生对象的成员变量值
s.setSid(strArray[0]);
s.setName(strArray[1]);
s.setAge(Integer.parseInt(strArray[2]));
s.setAddress(strArray[3]);
array.add(s);
}
br.close();
for (Student s : array) {
System.out.println(s.getSid() + "," + s.getName() + "," + s.getAge() + "," + s.getAddress());
}
}
}
将学生成绩排序,并将数据写入文件中(排序采用多级条件)
我们可以先创建一个学生类
package io_demo;
public class Student01 {
private String name;
private int chinese;
private int math;
private int english;
public Student01() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getChinese() {
return chinese;
}
public void setChinese(int chinese) {
this.chinese = chinese;
}
public int getMath() {
return math;
}
public void setMath(int math) {
this.math = math;
}
public int getEnglish() {
return english;
}
public void setEnglish(int english) {
this.english = english;
}
public Student01(String name, int chinese, int math, int english) {
this.name = name;
this.chinese = chinese;
this.math = math;
this.english = english;
}
public int getSum() {
return this.chinese + this.english + this.math;
}
}
创建学生类的唯一作用就是来获取对象,存放相关的属性。
然后我们再创建一个测主类
package io_demo;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Comparator;
import java.util.Scanner;
import java.util.TreeSet;
public class Student01_Demo {
public static void main(String[] args) throws IOException {
TreeSet<Student01> ts = new TreeSet<>(new Comparator<Student01>() {
@Override
public int compare(Student01 s1, Student01 s2) {
int num = s2.getSum() - s1.getSum();
int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num;
int num3 = num2 == 0 ? s1.getMath() - s2.getMath() : num2;
int num4 = num3 == 0 ? s1.getName().compareTo(s2.getName()) : num3;
return num4;
//return 0;
}
});
for (int i = 0; i < 5; i++) {
Scanner sc = new Scanner(System.in);
System.out.println("请录入第" + (i + 1) + "个学生信息");
System.out.println("姓名:");
String name = sc.nextLine();
System.out.println("语文成绩:");
int chinese = sc.nextInt();
System.out.println("数学成绩:");
int math = sc.nextInt();
System.out.println("英语成绩:");
int english = sc.nextInt();
Student01 s = new Student01();
s.setName(name);
s.setChinese(chinese);
s.setMath(math);
s.setEnglish(english);
ts.add(s);
}
BufferedWriter bw = new BufferedWriter(new FileWriter("E:\\java_doc\\src\\io_demo\\Student01.txt"));
for (Student01 s : ts) {
StringBuilder sb = new StringBuilder();
sb.append(s.getName()).append(",").append(s.getChinese())
.append(",").append(s.getMath()).append(",").append(s.getEnglish()).append(",").append(s.getSum());
bw.write(sb.toString());
bw.newLine();
bw.flush();
}
bw.close();
}
}
标准输入输出流
无论是在api中查看,还是在Ststem中查看,我们都可以了解到System有标准输入和输出流的定义,并且声明为static,于是肯定是可以直接进行按照类名调用的。
api对System类有以下说明
作为了System类的两个成员变量。这两个流的主要是进行键盘和内存的交互。
标准输入流
这个抽象类唯一的构造方法就是InputStream()这个无参构造方法。
简单了解下
package io_demo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Scanner;
public class IoDemo_21 {
public static void main(String args[]) throws IOException {
//字节流
InputStream in = System.in;
int by;
while((by=in.read())!=-1)
{
System.out.print((char)by);
}
//使用字符流
InputStreamReader isr = new InputStreamReader(in);
//使用字符流实现一次读取一次数据(使用字符缓冲输入流)
BufferedReader br = new BufferedReader(isr);
System.out.println("请输入一个字符串:");
String line = br.readLine();
System.out.println("你输入的字符串是"+line);
System.out.println("请输入一个整数:");
int i = Integer.parseInt(br.readLine());
System.out.println("你输入的整数是:"+i);
//java提供了一个类实现键盘录入
Scanner sc = new Scanner(System.in);//既可以接收整数也可以接收字符串
}
}
平常我们都会使用Scanner这个下面的方法去进行键盘输入数据,但是弄清楚它的本质也是一件非常有意义的事情。
它的底层还是使用了字节输入流。
这样就与流的知识衔接起来了。
标准输出流
所具有的一些构造方法
这样我们得到的信息是,我么可以在参数中按照布尔值确定是否进行自动刷新,另外还可以进行指定编码。
这个和标准输入流在System类中定义的格式是一样的。底层话,其实同样也是字节流。简单看下怎么使用。
//标准输出流
PrintStream out = System.out;
out.print("hello");//输出在控制台
但是你其实也可以向文件中进行输出,也就是将数据输入到文件中。
//字节打印流
PrintStream ps = new PrintStream("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
ps.write(97);//使用父类方法
ps.print(97);//使用自己特有方法
ps.close();
//字符打印流
PrintWriter pW = new PrintWriter("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
pW.write("hello");
pW.flush();
pW.println("Hello");
pW.flush();
PrintWriter pw = new PrintWriter(new FileWriter("E:\\java_doc\\src\\io_demo\\jgdabc.txt"),true);//会自动刷新
pw.println("Hello");
pw.println("world"
对象序列化流于反序列化流
什么是序列化与反序列化
java序列化,就是指吧java对象转换为字节序列的过程。而反序列自然就是将字节对象恢复为java对象。
这样做的意义在哪呢?对象进行序列化,会转换为字节流,这样在网络上传输,或者是进行保存为本地文件都是非常方便的。反序列很明显就是进行对象的重构。
其实你可以和通信联系在一起。网络上的文本,图片,视频,音频都是通过二进制进行传输的,我们的java所创建的对象在传输的时候也应该进行序列化,转换为字节流,然后通过网络,io传入,当我们的对象序列传输完成后,对方进行反序列化,就可以读取到数据内容。
对象序列化流( ObjectOutputStream)
api对这个类有很多的说明,主要说明
看一下简单使用
首先我们需要一个对象类
package io_demo;
import java.io.Serializable;
public class Student implements Serializable {
private String sid;
private String name;
int age;
private String address;
public Student()
{
}
public Student(String sid, String name, int age, String address) {
this.sid = sid;
this.name = name;
this.age = age;
this.address = address;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
然后测试主类
package io_demo;
import java.io.*;
public class IoDemo_23 {
//对象序列化流
// 构造方法
// ObjectOutputStream(OutputStream out)
public static void main(String args[]) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\java_doc\\src\\io_demo\\jgdabc.txt"));
Student s = new Student("001","刘备",28,"汉中");
oos.writeObject(s);//将对象序列化()
oos.close();
}
}
为什么Student需要实现Serializable,api有说明
为什么没有具体实现?
因为这个接口本身没有具体实现,我们不要要实现什么,这个只是作为一个标识而已。
对象反序列化流(.ObjectInputStream)
说明如下
首先还是原来的Student对象类
package io_demo;
import java.io.Serializable;
public class Student implements Serializable {
private String sid;
private String name;
int age;
private String address;
public Student()
{
}
public Student(String sid, String name, int age, String address) {
this.sid = sid;
this.name = name;
this.age = age;
this.address = address;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
对的,你虽然是从文件中读取,但是如果你不使用构建的对象的话。是绝对无法成功进行反序列化的。
主要的测试代码
package io_demo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
//反序列化ObiectreadObject() 进行读取来反序列化
public class IoDemo_24 {
public static void main(String args[]) throws IOException, ClassNotFoundException {
//实现对象反序列化流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\\java_doc\\src\\io_demo\\jgdabc.txt"));
Object o = ois.readObject();
Student s = (Student)o;
System.out.println(s.getName()+","+s.getAge());
ois.close();
}
}
这样感觉有时候很像加密与解密的样子。
如果在反序列话的时候原来对象类有修改?
我现在就修改一个属性。我让age属性权限有所变化,变为私有属性
**结果反序列化就报错了,为什么?你看报错 **
再清晰一点就是
serialVersionUID是什么?(为什么修改对象类会反序列化失败?)
serialVersionUID是序列化的时候会生成的一个的版本标识,。当我们对对象进行修改后,那么这个序列化的版本标识的值就会与之前的不一样了。这样会导致字节流的版本标识与本地的版本标识不一样,不一样就会反序列化失败。
解决修改对象类修改反序列化失败的问题
我们就自己定义一个常量。让这个版本标识符不再变化
注意,一定是在对象类中,不是在测试类。
不想让某些属性被序列化?
这样就可以
集合(Properties)
介绍这个集合的原因就是这个集合可以和流结合使用。
这个集合的具体说明就不在本文详细说明了,具体的在之前的集合文中再补充吧。简单说明一下和其用法。
这个集合首先是Map下的子类,所以也是双列集合。但是不支持泛型。用代码来看看具体使用。简单演示。
package io_demo;
import java.util.Properties;
import java.util.Set;
public class IoDemo_25 {
public static void main(String[] args) {
//创建集合对象
Properties pro = new Properties();
//存储元素
pro.put(001,"jgdabc");
pro.put(002,"兰舟千帆");
pro.put(002,"lym");
//遍历集合
Set<Object> KeySet = pro.keySet();
for(Object key:KeySet)
{
Object value = pro.get(key);
System.out.print(key+","+value);
}
pro.setProperty("000","jgdabc");
pro.getProperty("000");
//获取键值
Set<String> names = pro.stringPropertyNames();//获取到键集合
//于是可以这样遍历
for(String key:names)
{
String value = pro.getProperty(key);
System.out.println(key+value);
}
}
}
它的put()方法键是Object类型的,setProperty()方法的键必须是String类型。这个需要注意。
集合到文件,文件到集合
package io_demo;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
public class IoDemo_26 {
public static void main(String[] args) throws IOException {
//把集合中的数据保存到文件
myStore();
//文件数据加载到集合
myLoad();
}
private static void myLoad() throws IOException {
Properties prop = new Properties();
FileReader fr = new FileReader("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
prop.load(fr);
fr.close();
System.out.print(prop);
}
private static void myStore() throws IOException {
Properties prop = new Properties();
prop.setProperty("000","jack");
prop.setProperty("001","jgdabc");
prop.setProperty("002","john");
FileWriter fw = new FileWriter("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
prop.store(fw,null);//null代表不描述信息
}
}
做一个猜数字游戏
文件中给一个count=0,然后用这个集合读取到数据(需要转换为int类型,然后根据值得限制,限制玩的次数。当然没每次运行完毕都会count+1,运行三次就不能玩了。这个和一直在交互是不一样的。)
首先一个猜数字游戏类
package java_practice;
import java.util.Scanner;
public class GuessNumber {
public static void start()
{
Scanner input = new Scanner(System.in);
int number = (int) (Math.random() * 100)+1; // 产生随机数
int guess;// 用户猜的数字
int count = 0;// 猜测次数
System.out.println("我心里有一个0到100之间的整数,你猜是什么?");
// 用户猜测随机数
do {
guess = input.nextInt();
if (number < guess) {
System.out.println("大了点,再猜!");
count++;
} else if (number > guess) {
System.out.println("小了点,再猜!");
count++;
} else {
count++;
break;
}
} while (true);
System.out.println("这个数字是"+number);
System.out.println("您猜的次数是"+count);
// 根据猜测次数给出评价
if (count == 1) {
System.out.println("你太聪明了!");
} else if (count >= 2 && count <= 5) {
System.out.println("不错,再接再厉!");
} else {
System.out.println("要努力啊!");
}
}
}
然后一个测试主类
package io_demo;
import java_practice.GuessNumber;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.SQLOutput;
import java.util.Properties;
public class IoDemo_27 {
public static void main(String args[]) throws IOException {
GuessNumber guessNumber = new GuessNumber();
Properties prop = new Properties();
FileReader fr = new FileReader("E:\\java_doc\\src\\io_demo\\count.txt");
prop.load(fr);
fr.close();
String count = prop.getProperty("count");
int number = Integer.parseInt(count);
if(number>=3)
{
System.out.println("游戏试玩结束,想玩请充值");
}else
{
guessNumber.start();
prop.setProperty("count",String.valueOf(number));
number++;
FileWriter fw = new FileWriter("E:\\java_doc\\src\\io_demo\\count.txt");
prop.store(fw,null);
fw.close();
}
}
}
注意一定要自己建立一个文件,里面存一个标记值。这样功能就可以实现了。
IO异常处理
可以根据自己的需要选择将数据输入到文件中还是将信息输出到控制台。
抛出异常
package io_demo;
import java.io.*;
public class IODemo_10 {
public static void main(String args[]) throws IOException {
FileOutputStream fos = new FileOutputStream("E:\\java_doc\\src\\io_demo\\demo_pratice");
OutputStreamWriter osw = new OutputStreamWriter(fos);
InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java_doc\\src\\io_demo\\demo_pratice"));
//
osw.write("中国");
osw.close();
int ch ;
while((ch=isr.read())!=-1)
{
System.out.println((char)ch);
}
isr.close();
}
}
标准捕获
package io_demo;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class IoDemoException {
public static void main(String args[])
{
FileReader fr=null;
FileWriter fw=null;
try {
fr= new FileReader("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
fw= new FileWriter("E:\\java_doc\\src\\io_demo\\demo_pratice");
char[] chs = new char[1024];
int len;
while((len = fr.read())!=-1)
{
fw.write(chs,0,len);
}
}catch (IOException e)
{
e.printStackTrace();
}finally {
if(fw!=null)
try {
fw.close();
}catch (IOException e)
{
e.printStackTrace();
}
if(fr!=null){
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
但是这样的处理还是比较麻烦的
捕获处理改进版(jdk7特性)
package io_demo;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
//jdk7的改进
public class IoDemoException01 {
public static void main(String args[])
{
try ( FileReader fr = new FileReader("E:\\java_doc\\src\\io_demo\\jgdabc.txt");
FileWriter fw = new FileWriter("E:\\java_doc\\src\\io_demo\\Student.txt");) {
char[] chars = new char[1024];
int len;
while ((len = fr.read()) != -1) {
fw.write(chars, 0, len);
}
}catch (IOException e)
{
e.printStackTrace();
}
}
;
}
//自动释放资源
这样的处理还是会自动释放资源,不需要人为关闭。
就先介绍这么多。之后有问题会继续更改补充。
我的主页