java- I/O(字节流、字符流、缓存流、数据流、对象流)

I/O

先看个思维导图呗
java- I/O(字节流、字符流、缓存流、数据流、对象流)_第1张图片

  • 文件对象

1.定义:文件和文件夹都是用File代表。
2.创建一个文件对象
使用绝对路径或者相对路径创建File对象。

package file;
import java.io.File;
public class TestFile {
    public static void main(String[] args) {
        // 绝对路径
        File f1 = new File("d:/LOLFolder");
        System.out.println("f1的绝对路径:" + f1.getAbsolutePath());
        // 相对路径,相对于工作目录,如果在eclipse中,就是项目目录
        File f2 = new File("LOL.exe");
        System.out.println("f2的绝对路径:" + f2.getAbsolutePath());
  
        // 把f1作为父目录创建文件对象
        File f3 = new File(f1, "LOL.exe");
  
        System.out.println("f3的绝对路径:" + f3.getAbsolutePath());
    }
}

java- I/O(字节流、字符流、缓存流、数据流、对象流)_第2张图片
3.文件常用方法

package file;
 
import java.io.File;
import java.util.Date;
 
public class TestFile {
 
    public static void main(String[] args) {
 
        File f = new File("d:/LOLFolder/LOL.exe");
        System.out.println("当前文件是:" +f);
        //文件是否存在
        System.out.println("判断是否存在:"+f.exists());
        
        //是否是文件夹
        System.out.println("判断是否是文件夹:"+f.isDirectory());
         
        //是否是文件(非文件夹)
        System.out.println("判断是否是文件:"+f.isFile());
         
        //文件长度
        System.out.println("获取文件的长度:"+f.length());
         
        //文件最后修改时间
        long time = f.lastModified();
        Date d = new Date(time);
        System.out.println("获取文件的最后修改时间:"+d);
        //设置文件修改时间为1970.1.1 08:00:00
        f.setLastModified(0);
         
        //文件重命名
        File f2 =new File("d:/LOLFolder/DOTA.exe");
        f.renameTo(f2);// renameTo方法用于对物理文件名称进行修改,但是并不会修改File对象的name属性。
        System.out.println("把LOL.exe改名成了DOTA.exe");
        
        System.out.println("注意: 需要在D:\\LOLFolder确实存在一个LOL.exe,\r\n才可以看到对应的文件长度、修改时间等信息");
    }
}

java- I/O(字节流、字符流、缓存流、数据流、对象流)_第3张图片

package file;
  
import java.io.File;
import java.io.IOException;
  
public class TestFile {
  
    public static void main(String[] args) throws IOException {
  
        File f = new File("d:/LOLFolder/skin/garen.ski");
  
        // 以字符串数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)
        f.list();
  
        // 以文件数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)
        File[] fs= f.listFiles();
  
        // 以字符串形式返回获取所在文件夹
        f.getParent();
  
        // 以文件形式返回获取所在文件夹
        f.getParentFile();
        // 创建文件夹,如果父文件夹skin不存在,创建就无效
        f.mkdir();
  
        // 创建文件夹,如果父文件夹skin不存在,就会创建父文件夹
        f.mkdirs();
  
        // 创建一个空文件,如果父文件夹skin不存在,就会抛出异常
        f.createNewFile();
        // 所以创建一个空文件之前,通常都会创建父目录
        f.getParentFile().mkdirs();
  
        // 列出所有的盘符c: d: e: 等等
        f.listRoots();
  
        // 刪除文件
        f.delete();
  
        // JVM结束的时候,刪除文件,常用于临时文件的删除
        f.deleteOnExit();
  
    }
}

4.练习

遍历文件夹
java- I/O(字节流、字符流、缓存流、数据流、对象流)_第4张图片

package file;
import java.io.File;

//遍历文件夹 
public class TestFile04 {
	public static void main(String[] args) {
		File f = new File("c:\\Windows");
  
		// 以文件数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)
		File[] fs= f.listFiles();
		File max = null;
		File min = null;
		// 取出一个字节大小大于0的文件,用于作为判断最大或最小初始文件
		for(File FS : fs) {
			if(f.length() != 0) {
				max = FS;
				min = FS;
				break;
			}
		}
		for(int i=0;i<fs.length;i++){
			if(fs[i].length()>0&&fs[i].isDirectory()==false){
				if(fs[i].length()>max.length()){
					max=fs[i];
				}
				if(fs[i].length()<min.length()){
					min=fs[i];
				}
			}
		}
		System.out.println("最大的文件是"+max+",其大小是"+max.length()+"字节");
		System.out.println("最小的文件是"+min+",其大小是"+min.length()+"字节");
	}
}

java- I/O(字节流、字符流、缓存流、数据流、对象流)_第5张图片
遍历子文件夹(同上的练习,要求遍历子文件夹)

package file;
import java.io.File;
public class AtWill {
	public static void main(String[] args) {
		File f = new File("c:\\Windows");
		File[] fs= f.listFiles();
		show(fs);
	}
 
	public static void show(File dir[]) {
		if (dir == null) {
			return;
		} else {
			for (File fi : dir) {
				if (fi.isDirectory()) {
					show(fi.listFiles());
				}
				if (fi.isFile()) {
					System.out.println(fi.getName());
				}
			}
		}
	}
}

注:输出所有文件,问题怎么求最大最小?

package file;

import java.io.File;

public class AtWill {
	static File max = null;
	static File min = null;

	public static void main(String[] args) {
		File f = new File("D:\\LOLFolder");
		File[] fs= f.listFiles();
		// 取出一个字节大小大于0的文件,用于作为判断最大或最小初始文件
		for(File FS : fs) {
			if(f.length() != 0) {
				max = FS;
				min = FS;
				break;
			}
		}
		show(fs);
		System.out.println("最大的文件是"+max+",其大小是"+max.length()+"字节");
		System.out.println("最小的文件是"+min+",其大小是"+min.length()+"字节");
	}

	public static void show(File dir[]) {
		if (dir == null) {
			return;
		} else {
			for (File fi : dir) {
				if (fi.isDirectory()) {
					show(fi.listFiles());
				}
				if (fi.isFile()) {
					System.out.println("文件名:"+fi.getName()+" 大小:"+fi.length());
					if(fi.length()>0&&fi.length()<min.length()){
						min=fi;
					}	
					if(fi.length()>0&&fi.length()>max.length()){
						max=fi;
					}
				}
			}
		}
	}
	
}
  • 什么是流

1.定义:流(Stream)就是一系列的数据。

当不同的介质之间有数据交互的时候,JAVA就使用来实现。
数据源可以是文件,还可以是数据库,网络甚至是其他的程序

比如读取文件的数据到程序中,站在程序的角度来看,就叫做输入流
输入流: InputStream
输出流:OutputStream
java- I/O(字节流、字符流、缓存流、数据流、对象流)_第6张图片
2.文件输入流
如下代码,就建立了一个文件输入流,这个流可以用来把数据从硬盘的文件,读取到JVM(内存)。
目前代码只是建立了流,还没有开始读取。

package stream;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
 
public class TestStream {
 
    public static void main(String[] args) {
        try {
            File f = new File("d:/lol.txt");
            // 创建基于文件的输入流
            FileInputStream fis = new FileInputStream(f);
            // 通过这个输入流,就可以把数据从硬盘,读取到Java的虚拟机中来,也就是读取到内存中
 
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
 
    }
}

3.文件输出流

public class TestStream {
    public static void main(String[] args) {
        try {
            File f = new File("d:/LOLFolder/DOTA.exe")
            FileInputStream fis = new FileInputStream(f);
         
            FileOutputStream fos=new FileOutputStream(f);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

注:文件放c盘可能拒绝访问。

  • 字节流

1.定义:用于以字节的形式读取和写入数据。

2.ASCII码

所有的数据存放在计算机中都是以数字的形式存放的。 所以字母就需要转换为数字才能够存放。
比如A就对应的数字65,a对应的数字97。不同的字母和符号对应不同的数字,就是一张码表。
ASCII是这样的一种码表。 只包含简单的英文字母,符号,数字等等。 不包含中文,德文,俄语等复杂的。

字符	十进制数字	十六进制数字
!			33			21
"			34			22
#			35			23
$			36			24
%			37			25
&			38			26
'			39			27
(			40			28
)			41			29
*			42			2A
+			43			2B
,			44			2C
-			45			2D
.			46			2E
/			47			2F
0			48			30
1			49			31
2			50			32
3			51			33
4			52			34
5			53			35
6			54			36
7			55			37
8			56			38
9			57			39
:			58			3A
;			59			3B
<			60			3C
=			61			3D
>			62			3E
@			64			40
A			65			41
B			66			42
C			67			43
D			68			44
E			69			45
F			70			46
G			71			47
H			72			48
I			73			49
J			74			4A
K			75			4B
L			76			4C
M			77			4D
N			78			4E
O			79			4F
P			80			50
Q			81			51
R			82			52
S			83			53
T			84			54
U			85			55
V			86			56
W			87			57
X			88			58
Y			89			59
Z			90			5A
[			91			5B
\			92			5C
]			93			5D
^			94			5E
_			95			5F
`			96			60
a			97			61
b			98			62
c			99			63
d			100			64
e			101			65
f			102			66
g			103			67
h			104			68
i			105			69
j			106			6A
k			107			6B
l			108			6C
m			109			6D
n			110			6E
o			111			6F
p			112			70
q			113			71
r			114			72
s			115			73
t			116			74
u			117			75
v			118			76
w			119			77
x			120			78
y			121			79
z			122			7A
{			123			7B
|			124			7C
}			125			7D
~			126			7E

3.以字节流的形式读取文件内容(InputStream)
InputStream是字节输入流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。
FileInputStream 是InputStream子类,以FileInputStream 为例进行文件读取。

package stream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;  
public class TestStream {
    public static void main(String[] args) {
        try {
            //准备文件lol.txt其中的内容是AB,对应的ASCII分别是65 66
            File f =new File("d:/lol.txt");
            //创建基于文件的输入流
            FileInputStream fis =new FileInputStream(f);
            //创建字节数组,其长度就是文件的长度
            byte[] all =new byte[(int) f.length()];
            //以字节流的形式读取文件所有内容
            fis.read(all);
            for (byte b : all) {
                //打印出来是65 66
                System.out.println(b);
            }
             
            //每次使用完流,都应该进行关闭
            fis.close();
              
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }     
    }
}

4.以字节流的形式向文件写入数据(OutputStream)
OutputStream是字节输出流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。
FileOutputStream 是OutputStream子类,以FileOutputStream 为例向文件写出数据
注: 如果文件d:/lol2.txt不存在,写出操作会自动创建该文件。
但是如果是文件 d:/xyz/lol2.txt,而目录xyz又不存在,会抛出异常

package stream;
 
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
 
public class TestStream {
    public static void main(String[] args) {
        try {
            // 准备文件lol2.txt其中的内容是空的
            File f = new File("d:/lol2.txt");
            // 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Y
            byte data[] = { 88, 89 };
 
            // 创建基于文件的输出流
            FileOutputStream fos = new FileOutputStream(f);
            // 把数据写入到输出流
            fos.write(data);
            // 关闭输出流
            fos.close();
             
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
 
    }
}

在这里插入图片描述
5.练习
写入数据到文件
以字节流的形式向文件写入数据 中的例子,当lol2.txt不存在的时候,是会自动创建lol2.txt文件的。
但是,如果是写入数据到d:/lol/lol2.txt,而目录xyz又不存在的话,就会抛出异常。
那么怎么自动创建xyz目录?
如果是多层目录 d:/lol/abc/def/lol2.txt 呢?

package stream;
  
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
  
public class TestStream {
  
	public static void main(String[] args) {        
        try {
            File f=new File("d:/lol/abc/bcd/lol2.txt");
            
            // 所以创建一个空文件之前,通常都会创建父目录
            f.getParentFile().mkdirs();
            byte data[] = { 88, 89 };
           // 创建基于文件的输出流,向文件写入数据
            FileOutputStream fos = new FileOutputStream(f);
             // 把数据写入到输出流
            fos.write(data);
            // 关闭输出流
            fos.close();
             
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
         
    }
}

java- I/O(字节流、字符流、缓存流、数据流、对象流)_第7张图片
拆分-合并
java- I/O(字节流、字符流、缓存流、数据流、对象流)_第8张图片

package stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;


public class TestStream {
	public int count;
	//拆分文件
    public static void onetozero(File file) {
        int size = 1024;//1024字符=1k
        if(file.length() == 0) {
            System.out.println("文件大小为0,无法拆分。");
            return;
        }
        byte a[] = new byte[(int) file.length()];
        try {
        	FileInputStream fis = new FileInputStream(file);
            fis.read(a);
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        int sum;//拆分文件总数
        if (a.length % size == 0) {
            sum = (int) (a.length / size);
        }
        else {
            sum = (int) (a.length / size) + 1;//浮点数转变成整数只保留整数部分
        }
        for (int i = 0; i < sum; i++) {
            String name = file.getName() + "_" + i;
            //file.getParent()为父目录
            File zerofile = new File(file.getParent(), name);
            byte b[];
            if (i != sum - 1) {
                b = Arrays.copyOfRange(a, size * i, size * (i + 1));
            }
            else {
                b = Arrays.copyOfRange(a, size * (sum - 1), a.length);
            }
            try {
            	FileOutputStream fos = new FileOutputStream(zerofile);
                fos.write(b);
                System.out.println("拆分的文件:" + name + "文件长度为:" + zerofile.length());
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    //合并文件
    public static void zerotoone(File file) {
    	int size = 1024;//1024字符=1k
        if(file.length() == 0) {
            System.out.println("文件大小为0,无法拆分。");
            return;
        }
        byte aa[] = new byte[(int) file.length()];
        try {
        	FileInputStream fis = new FileInputStream(file);
            fis.read(aa);
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        int sum;//拆分文件总数
        if (aa.length % size == 0) {
            sum = (int) (aa.length / size);
        }
        else {
            sum = (int) (aa.length / size) + 1;//浮点数转变成整数只保留整数部分
        }
        //以上获取文件总数sum--------------------------------
        for (int i = 0; i < sum; i++) {
            String name = file.getName() + "_" + i;
            File zerofile = new File("d:/LOLFolder/", name);
            File onefile = new File("d:/LOLFolder/", "test_new");
            byte a[] = new byte[(int) zerofile.length()];
            try {
            	FileInputStream fis = new FileInputStream(zerofile);
                fis.read(a);
                fis.close();
            }catch (IOException e) {
                e.printStackTrace();
            }
            try {
             //有true不会覆盖已写入的文本,内部使用append
            	FileOutputStream fos = new FileOutputStream(onefile,true);
                fos.write(a);
                fos.close();
            }catch (IOException e) {
                e.printStackTrace();
            }
            zerofile.delete();//合并后删除该文件
            System.out.println(name + "合并入文件:test_new,合并后文件长度为:" + onefile.length());
        }
    }
 
    public static void main(String args[]) {
    	/* File file = new File("./test");*/
    	File file = new File("d:/LOLFolder/test.txt");
        System.out.println("获取文件长度为:" + file.length());
        //onetozero(file);
        zerotoone(file);
    } 
}

java- I/O(字节流、字符流、缓存流、数据流、对象流)_第9张图片
java- I/O(字节流、字符流、缓存流、数据流、对象流)_第10张图片

  • 关闭流的方式

1.关闭流
所有的流,无论是输入流还是输出流,使用完毕之后,都应该关闭。 如果不关闭,会产生对资源占用的浪费。 当量比较大的时候,会影响到业务的正常开展。
2.在try中关闭
在try的作用域里关闭文件输入流,在前面的示例中都是使用这种方式,这样做有一个弊端;
如果文件不存在,或者读取的时候出现问题而抛出异常,那么就不会执行这一行关闭流的代码,存在巨大的资源占用隐患。 不推荐使用

package stream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class TestStream {
    public static void main(String[] args) {
        try {
            File f = new File("d:/lol.txt");
            FileInputStream fis = new FileInputStream(f);
            byte[] all = new byte[(int) f.length()];
            fis.read(all);
            for (byte b : all) {
                System.out.println(b);
            }
            // 在try 里关闭流
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3.在finally中关闭
这是标准的关闭流的方式

  1. 首先把流的引用声明在try的外面,如果声明在try里面,其作用域无法抵达finally.
  2. 在finally关闭之前,要先判断该引用是否为空
  3. 关闭的时候,需要再一次进行try catch处理

这是标准的严谨的关闭流的方式,但是看上去很繁琐,所以写不重要的或者测试代码的时候,都会采用上面的有隐患try的方式,因为不麻烦~

package stream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class TestStream {
    public static void main(String[] args) {
        File f = new File("d:/lol.txt");
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(f);
            byte[] all = new byte[(int) f.length()];
            fis.read(all);
            for (byte b : all) {
                System.out.println(b);
            }
 
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 在finally 里关闭流
            if (null != fis)
                try {
 
                    fis.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
        }
    }
}

注:
当fis==null时,执行fis.close()会发生空指针异常。
只有fis!=null,才能执行fis.close()操作。
4.使用try()的方式
把流定义在try()里,try,catch或者finally结束的时候,会自动关闭
这种编写代码的方式叫做 try-with-resources, 这是从JDK7开始支持的技术

所有的流,都实现了一个接口叫做 AutoCloseable,任何类实现了这个接口,都可以在try()中进行实例化。 并且在try, catch, finally结束的时候自动关闭,回收相关资源。
java- I/O(字节流、字符流、缓存流、数据流、对象流)_第11张图片
对比:
java- I/O(字节流、字符流、缓存流、数据流、对象流)_第12张图片

  • 字符流

1.定义:专门用于字符的形式读取和写入数据。

2.使用字符流读取文件(Reader)
字符输入流FileReader 是Reader子类,以FileReader 为例进行文件读取。

package stream;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class TestStream {
    public static void main(String[] args) {
        // 准备文件lol.txt其中的内容是AB
        File f = new File("d:/lol.txt");
        // 创建基于文件的Reader
        try (FileReader fr = new FileReader(f)) {
            // 创建字符数组,其长度就是文件的长度
           char[] all = new char[(int) f.length()];
            // 以字符流的形式读取文件所有内容
            fr.read(all);
            for (char b : all) {
                // 打印出来是A B
                System.out.println(b);
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

3.使用字符流把字符串写入到文件(Writer)
字符输出流FileWriter 是Writer的子类,以FileWriter 为例把字符串写入到文件。

package stream; 
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class TestStream {
    public static void main(String[] args) {
        // 准备文件lol2.txt
        File f = new File("d:/lol2.txt");
        // 创建基于文件的Writer
        try (FileWriter fr = new FileWriter(f)) {
            // 以字符流的形式把数据写入到文件中
            String data="abcdefg1234567890";
            char[] cs = data.toCharArray();
           fr.write(cs);
  
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

4.练习
加密-解密
java- I/O(字节流、字符流、缓存流、数据流、对象流)_第13张图片

package stream;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;

public class TestStream {
//文件加密
public static void encodeFile(File encodingFile, File encodedFile) {
        
        try {
            FileReader fr = new FileReader(encodingFile);
            char[] in = new char[(int) encodingFile.length()];
            fr.read(in);
         
            char[] out = new char[(int) encodingFile.length()];
            System.out.println("解密前的内容为:");
            for(int i=0; i<in.length; i++) {
                if((in[i] >= '0'&& in[i] < '9') || (in[i] >= 'A'&& in[i] < 'Z') || (in[i] >= 'a'&& in[i] < 'z')) {
                    out[i] = (char) (in[i] +1);
                }
                /*else if(in[i] == '!'||in[i] == '@'||in[i] == '#'||in[i] == '$'||in[i] == '%'||in[i] == '^'||in[i] == '&'||in[i] == '*'
                        ||in[i] == '('||in[i] == ')'||in[i] == '_'||in[i] == '+') out[i] = in[i];*/
                else if(in[i] == '9') out[i] = '0';
                else if(in[i] == 'z') out[i] = 'a';
                else if(in[i] == 'Z')  out[i] = 'A';
                else out[i] = in[i];
                System.out.print(in[i]+ "");
            }
         
            FileWriter fw = new FileWriter(encodedFile);
            fw.write(out);
            fw.close();//必须关闭,不关闭则在缓冲区中,不会写入到txt,不然使用flush方法
            System.out.println();
            System.out.println("加密成功,加密后的内容为:");
            for(int i=0; i<in.length; i++) {
                System.out.print(out[i]+ "");
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }
     
//文件解密     
public static void decodeFile(File decodingFile, File decodedFile) {
         
        try {
        FileReader fr = new FileReader(decodingFile);
        char[] in = new char[(int) decodingFile.length()];
        fr.read(in);
         
        char[] out = new char[(int) decodingFile.length()];
        
        System.out.println("解密后的内容为:");
        for(int i=0; i<in.length; i++) {
            if((in[i] > '0'&& in[i] <= '9') || (in[i] > 'A'&& in[i] <= 'Z') || (in[i] > 'a'&& in[i] <= 'z')) {
                out[i] = (char) (in[i] -1);
            }
            /*else if(in[i] == '!'||in[i] == '@'||in[i] == '#'||in[i] == '$'||in[i] == '%'||in[i] == '^'||in[i] == '&'||in[i] == '*'
                    ||in[i] == '('||in[i] == ')'||in[i] == '_'||in[i] == '+') out[i] = in[i];*/
            else if(in[i] == '0') out[i] = '9';
            else if(in[i] == 'a') out[i] = 'z';
            else if(in[i] == 'A')  out[i] = 'Z';
            else out[i] = in[i];
            //System.out.print(in[i]+ "\t");
             System.out.print(out[i]);
        }
         
        FileWriter fw = new FileWriter(decodedFile);
        fw.write(out);
        fw.close();//必须关闭,不关闭则在缓冲区中,不会写入到txt,不然使用flush方法
        }catch(Exception e){
            e.printStackTrace();
        }
        //System.out.println();
        //System.out.println("解密成功");
       
    }
     
    public static void main(String[] args) {
        // TODO 自动生成的方法存根
        File f = new File("d:/LOLFolder/lol.txt");
        File fen = new File("d:/LOLFolder/lol3.txt");
        File fde = new File("d:/LOLFolder/lol4.txt");
        encodeFile(f,fen);
        System.out.println();
        decodeFile(fen,fde);
    }

}

java- I/O(字节流、字符流、缓存流、数据流、对象流)_第14张图片

  • 中文问题

1.编码概念
计算机存放数据只能存放数字,所有的字符都会被转换为不同的数字。
就像一个棋盘一样,不同的字,处于不同的位置,而不同的位置,有不同的数字编号。
有的棋盘很小,只能放数字和英文
有的大一点,还能放中文
有的“足够”大,能够放下世界人民所使用的所有文字和符号

如图所示,英文字符 A 能够放在所有的棋盘里,而且位置都差不多
中文字符, 中文字符 能够放在后两种棋盘里,并且位置不一样,而且在小的那个棋盘里,就放不下中文
java- I/O(字节流、字符流、缓存流、数据流、对象流)_第15张图片
2.常见编码
ISO-8859-1 ASCII 数字和西欧字母
GBK GB2312 BIG5 中文
UNICODE (统一码,万国码)

ISO-8859-1 包含 ASCII
GB2312 是简体中文,BIG5是繁体中文,GBK同时包含简体和繁体以及日文。
UNICODE 包括了所有的文字,无论中文,英文,藏文,法文,世界所有的文字都包含其中

3.UNICODE和UTF
UNICODE:按照UNICODE的方式来存储数据,就会有很大的浪费。
比如在ISO-8859-1中,a 字符对应的数字是0x61
UNICODE中对应的数字是 0x00000061,倘若一篇文章大部分都是英文字母,那么按照UNICODE的方式进行数据保存就会消耗很多空间

在这种情况下,就出现了UNICODE的各种减肥子编码, 比如UTF-8对数字和字母就使用一个字节,而对汉字就使用3个字节,从而达到了减肥还能保证健康的效果
UTF-8,UTF-16和UTF-32 针对不同类型的数据有不同的减肥效果,一般说来UTF-8是比较常用的方式

4.Java采用的是Unicode
写在.java源代码中的汉字,在执行之后,都会变成JVM中的字符。
而这些中文字符采用的编码方式,都是使用UNICODE. "中"字对应的UNICODE是4E2D,所以在内存中,实际保存的数据就是十六进制的0x4E2D, 也就是十进制的20013。

package stream;
public class TestStream {
    public static void main(String[] args) {
        String str = "中";
    }
}

5.一个汉字使用不同编码方式的表现

以字符 中 为例,查看其在不同编码方式下的值是多少,也即在不同的棋盘上的位置。

package stream;
 
import java.io.UnsupportedEncodingException;
 
public class TestStream {
 
    public static void main(String[] args) {
        String str = "中";
        showCode(str);
    }
 
    private static void showCode(String str) {
        String[] encodes = { "BIG5", "GBK", "GB2312", "UTF-8", "UTF-16", "UTF-32" };
        for (String encode : encodes) {
            showCode(str, encode);
        }
 
    }
 
    private static void showCode(String str, String encode) {
        try {
            System.out.printf("字符: \"%s\" 的在编码方式%s下的十六进制值是%n", str, encode);
            byte[] bs = str.getBytes(encode);
 
            for (byte b : bs) {
                int i = b&0xff;
                System.out.print(Integer.toHexString(i) + "\t");
            }
            System.out.println();
            System.out.println();
        } catch (UnsupportedEncodingException e) {
            System.out.printf("UnsupportedEncodingException: %s编码方式无法解析字符%s\n", encode, str);
        }
    }
}

java- I/O(字节流、字符流、缓存流、数据流、对象流)_第16张图片
6.文件的编码方式-记事本
接下来讲,字符在文件中的保存
字符保存在文件中肯定也是以数字形式保存的,即对应在不同的棋盘上的不同的数字
用记事本打开任意文本文件,并且另存为,就能够在编码这里看到一个下拉。
ANSI 这个不是ASCII的意思,而是采用本地编码的意思。如果你是中文的操作系统,就会使GBK,如果是英文的就会是ISO-8859-1
Unicode UNICODE原生的编码方式
Unicode big endian另一个 UNICODE编码方式
UTF-8 最常见的UTF-8编码方式,数字和字母用一个字节, 汉字用3个字节。
java- I/O(字节流、字符流、缓存流、数据流、对象流)_第17张图片
7.文件的编码方式-eclipse
eclipse也有类似的编码方式,右键任意文本文件,点击最下面的"property"
就可以看到Text file encoding
也有ISO-8859-1,GBK,UTF-8等等选项。
其他的US-ASCII,UTF-16,UTF-16BE,UTF-16LE不常用。
java- I/O(字节流、字符流、缓存流、数据流、对象流)_第18张图片
8.用FileInputStream 字节流正确读取中文
为了能够正确的读取中文内容

  1. 必须了解文本是以哪种编码方式保存字符的
  2. 使用字节流读取了文本后,再使用对应的编码方式去识别这些数字,得到正确的字符
    如本例,一个文件中的内容是字符,编码方式是GBK,那么读出来的数据一定是D6D0。
    再使用GBK编码方式识别D6D0,就能正确的得到字符

注: 在GBK的棋盘上找到的中字后,JVM会自动找到中在UNICODE这个棋盘上对应的数字,并且以UNICODE上的数字保存在内存中。

package stream;   
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException; 
public class TestStream {
    public static void main(String[] args) {
        File f = new File("E:\\project\\j2se\\src\\test.txt");
        try (FileInputStream fis = new FileInputStream(f);) {
            byte[] all = new byte[(int) f.length()];
            fis.read(all);
   
            //文件中读出来的数据是
            System.out.println("文件中读出来的数据是:");
            for (byte b : all)
            {
                int i = b&0x000000ff;  //只取16进制的后两位
              System.out.println(Integer.toHexString(i));
            }
            System.out.println("把这个数字,放在GBK的棋盘上去:");
            String str = new String(all,"GBK");
            System.out.println(str);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
    }
}

java- I/O(字节流、字符流、缓存流、数据流、对象流)_第19张图片
9.用FileReader 字符流正确读取中文
FileReader得到的是字符,所以一定是已经把字节根据某种编码识别成了字符了
而FileReader使用的编码方式是Charset.defaultCharset()的返回值,如果是中文的操作系统,就是GBK
FileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替,像这样:

new InputStreamReader(new FileInputStream(f),Charset.forName(“UTF-8”));

在本例中,用记事本另存为UTF-8格式,然后用UTF-8就能识别对应的中文了。

解释: 为什么中字前面有一个?
如果是使用记事本另存为UTF-8的格式,那么在第一个字节有一个标示符,叫做BOM用来标志这个文件是用UTF-8来编码的。

package stream;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
 
public class TestStream {
 
    public static void main(String[] args) throws UnsupportedEncodingException, FileNotFoundException {
        File f = new File("D:\\lol2.txt");
        System.out.println("默认编码方式:"+Charset.defaultCharset());
        //FileReader得到的是字符,所以一定是已经把字节根据某种编码识别成了字符了
        //而FileReader使用的编码方式是Charset.defaultCharset()的返回值,如果是中文的操作系统,就是GBK
        try (FileReader fr = new FileReader(f)) {
            char[] cs = new char[(int) f.length()];
            fr.read(cs);
            System.out.printf("FileReader会使用默认的编码方式%s,识别出来的字符是:%n",Charset.defaultCharset());
            System.out.println(new String(cs));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //FileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替
        //并且使用new InputStreamReader(new FileInputStream(f),Charset.forName("UTF-8")); 这样的形式
        try (InputStreamReader isr = new InputStreamReader(new FileInputStream(f),Charset.forName("UTF-8"))) {
            char[] cs = new char[(int) f.length()];
            isr.read(cs);
            System.out.printf("InputStreamReader 指定编码方式UTF-8,识别出来的字符是:%n");
            System.out.println(new String(cs));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
         
    }
}

java- I/O(字节流、字符流、缓存流、数据流、对象流)_第20张图片
java- I/O(字节流、字符流、缓存流、数据流、对象流)_第21张图片
10.练习
数字对应的中文:找出 E5 B1 8C 这3个十六进制对应UTF-8编码的汉字。
转化为字节存入数组,找一个文件(创建项目文本)写入,然后读取文件(UTF-8格式)

package stream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.CharBuffer;
 
/*
 *   找出 E5 B1 8C 这3个十六进制对应UTF-8编码的汉字
 * 1.把这三个字节按字节流写入到编码格式为UTF-8的文件中去
 * 2.从文件中按照字符流UTF-8读出这个字符
 * 3.把这个字符用String套上,打印出来
 *  注意:写入文件是字节,读出来是字符,同时注意读出来时的编码设置
 */
public class EncodingDemo1 {
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        byte[] bytes = { (byte) 0xE5, (byte) 0xB1, (byte) 0x8C };
 
        try {
            parseChineseFromHex(bytes);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
 
    private static void parseChineseFromHex(byte[] bytes) throws IOException {
        // TODO Auto-generated method stub
        File file = new File("EncodingDemo1.txt");
        if (!file.exists()) {
            // 如果存在父目录,把上层的目录结构全部补全
            if (null != file.getParentFile())
                file.getParentFile().mkdirs();
            // 创建实体文件
            file.createNewFile();
 
        }
        // 文件在项目的最外层路径下,而不是包目录下
        //System.out.println(file.getAbsolutePath());
        // 写入文件中
        try (FileOutputStream output = new FileOutputStream(file)) {
            output.write(bytes);
        }
 
        // 读出来
        try (InputStreamReader reader = new InputStreamReader(new FileInputStream(file), "UTF-8")) {
            // 只有一个字符
            char[] cbuf = new char[1];
            reader.read(cbuf);
            System.out.println(new String(cbuf));
        }
    }
 
}   

java- I/O(字节流、字符流、缓存流、数据流、对象流)_第22张图片
在这里插入图片描述
移除BOM

如果用记事本根据UTF-8编码保存汉字就会在最前面生成一段标示符,这个标示符用于表示该文件是使用UTF-8编码的。
找出这段标示符对应的十六进制,并且开发一个方法,自动去除这段标示符

package io;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.CharBuffer;
import java.util.Arrays;
 
/*
 * 问题: UTF-8格式保存的文件最前面存在一段标示符BOM,标示它是UTF-8编码的文件
 *      找出这段标示符的十六机制,并开发一个方法把这段自动移出
 *  思路:1.EncodingDemo1.java中的EncodingDemo1.txt的编码修改为UTF-8
 *      2.从EncodingDemo.txt中按照UTF-8格式读出所有字符
 *      3.将字符用十六机制表示出来,观察、结果与“ E5 B1 8C ”的不同之处
 *      4.把观察的结果记录到byte[]数组中
 *      5.把包含BOM的内容byte[]数组去掉上面的观察结果的数组得到移除后的结果
 *      6.把移除后的byte[]数组通过int转换成十六进制和打印成String
 * 注意:字节转换为int才方便打印成十六进制的格式
 * 疑问:若上一个程序结束后直接读取该文件,并不能读到BOM,而必须的手动再修改下该文件的编码格式
 *      实际我也只是点击了另存,并没选择它新的编码格式,它默认帮我选择了UTF-8,默认的不是UTF-8?
 *结果:经百度发现的确windows简体中文操作系统的ANSI编码指向的是GBK编码
 */
public class EncodingDemo2 {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        File file = new File("EncodingDemo2.txt");
        try {
            parseBOMFromUTF8File(file);
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            if (null != file.getParentFile())
                file.getParentFile().mkdirs();
            try {
                file.createNewFile();
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        }
    }
 
    private static void parseBOMFromUTF8File(File file) throws FileNotFoundException {
        // TODO Auto-generated method stub
        try (FileInputStream input = new FileInputStream(file)) {
            byte[] content = new byte[(int) file.length()];
            input.read(content);
            System.out.println(new String(content, "UTF-8"));
            System.out.println("编码修改为UTF-8读取到的十六进制为:");
            for (byte b : content) {
                // 留意int是4个字节,而byte是1个字节
                System.out.print(Integer.toHexString(b & 0x000000ff));
            }
            System.out.println();
            byte[] bom = new byte[3];
            bom[0] = (byte) 0xef;
            bom[1] = (byte) 0xbb;
            bom[2] = (byte) 0xbf;
            byte[] withoutBOM = removeBOM(content,bom);
            System.out.print("去除BOM后的结果为:");
            for (byte b : withoutBOM) {
                // 注意字节转换为int才方便打印成十六进制的格式
                int i = b & 0xff;
                System.out.print(Integer.toHexString(i));
            }
            System.out.println();
            System.out.println("去除BOM后的结果为:" + new String(withoutBOM, "UTF-8"));
            // String的"e5b18c"怎么解读成byte[] bytes = { (byte) 0xE5, (byte) 0xB1, (byte) 0x8C };
//              答案的方式更合适,还是针对byte[]字符进行操作比较方便
             
        } catch (IOException e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }
 
    // 移除BOM标示符
    private static byte[] removeBOM(byte[] content,byte[] bom) {
        // TODO Auto-generated method stub
        return Arrays.copyOfRange(content, bom.length, content.length);
    }
 
}

在这里插入图片描述
java- I/O(字节流、字符流、缓存流、数据流、对象流)_第23张图片

  • 缓存流

1.为什么使用缓存流?
以介质是硬盘为例,字节流和字符流的弊端:
在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。

为了解决以上弊端,采用缓存流。
缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。

缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区达到一定的量,才把这些数据,一起写入到硬盘中去。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了IO操作。

2.使用缓存流读取数据
缓存字符输入流 BufferedReader 可以一次读取一行数据。

package stream;
  
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
  
public class TestStream {
  
    public static void main(String[] args) {
        // 准备文件lol.txt其中的内容是
        // garen kill teemo
        // teemo revive after 1 minutes
        // teemo try to garen, but killed again
        File f = new File("d:/lol.txt");
        // 创建文件字符流
        // 缓存流必须建立在一个存在的流的基础上
        try (
                FileReader fr = new FileReader(f);
                BufferedReader br = new BufferedReader(fr);
            )
        {
            while (true) {
                // 一次读一行
                String line = br.readLine();
                if (null == line)
                    break;
                System.out.println(line);
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
  
    }
}

3.使用缓存流写出数据
BufferedReader 缓存字符输出流, 可以一次写出一行数据

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class TestStream {
		public static void main(String[] args) {
			try {
				BufferedReader br=new BufferedReader(new FileReader("data/17软件1班"));
				BufferedWriter bw=new BufferedWriter(new FileWriter("data/17软件1班副本本"));
				String str="";
				while((str=br.readLine())!=null){
					System.out.println(str);
					bw.write(str+"\n");
				}
				br.close();
				bw.flush();
				bw.close();
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
}

PrintWriter 缓存字符输出流, 可以一次写出一行数据。

package stream;
   
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
   
public class TestStream {
   
    public static void main(String[] args) {
        // 向文件lol2.txt中写入三行语句
        File f = new File("d:/lol2.txt");
          
        try (
                // 创建文件字符流
                FileWriter fw = new FileWriter(f);
                // 缓存流必须建立在一个存在的流的基础上              
                PrintWriter pw = new PrintWriter(fw);              
        ) {
            pw.println("garen kill teemo");
            pw.println("teemo revive after 1 minutes");
            pw.println("teemo try to garen, but killed again");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
   
    }
}

PrintWriter和BufferedReader区别:

  1. PrintWriter的print、println方法可以接受任意类型的参数,而BufferedWriter的write方法只能接受字符、字符数组和字符串;

  2. PrintWriter的println方法自动添加换行,BufferedWriter需要显示调用newLine方法;

  3. PrintWriter的方法不会抛异常,若关心异常,需要调用checkError方法看是否有异常发生;

  4. PrintWriter构造方法可指定参数,实现自动刷新缓存(autoflush);

  5. PrintWriter的构造方法更广。

PrintWriter和BufferedWriter都是继承java.io.Writer,所以很多功能都一样。不过PrintWriter提供println()方法可以写不同平台的换行符,而BufferedWriter可以任意设定缓冲大小。
OutputStream可以直接传给PrintWriter(BufferedWriter不能接收),如:
PrintWriter out = new PrintWriter(new BufferedOutputStream(new FileOutputStream(“foo.out”)));
或者用OutputStreamWriter来将OutputStream转化为Wrtier.这时就可以用BufferedWriter了。

4.flush
有的时候,需要立即把数据写入到硬盘,而不是等缓存满了才写出去。 这时候就需要用到flush。(写入缓冲区,强制写入硬盘)

package stream;   
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class TestStream {
    public static void main(String[] args) {
        //向文件lol2.txt中写入三行语句
        File f =new File("d:/lol2.txt");
        //创建文件字符流
        //缓存流必须建立在一个存在的流的基础上
        try(FileWriter fr = new FileWriter(f);PrintWriter pw = new PrintWriter(fr);) {
            pw.println("garen kill teemo");
            //强制把缓存中的数据写入硬盘,无论缓存是否已满
            pw.flush();           
            pw.println("teemo revive after 1 minutes");
            pw.flush();
            pw.println("teemo try to garen, but killed again");
            pw.flush();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

5.练习-移除注释

设计一个方法,用于移除Java文件中的注释
public void removeComments(File javaFile)
比如,移出以//开头的注释行
File f = new File(“d:/LOLFolder/LOL.exe”);
System.out.println(“当前文件是:” +f);
//文件是否存在
System.out.println(“判断是否存在:”+f.exists());
//是否是文件夹
System.out.println(“判断是否是文件夹:”+f.isDirectory());
注: 如果注释在后面,或者是/**/风格的注释,暂不用处理

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class Main {
    public void removeComments(File javaFile) {
        File newfile=new File(javaFile.getParentFile(),"new"+javaFile.getName());
        System.out.println("选中的文件是:"+javaFile.getName());
        try(FileReader fReader=new FileReader(javaFile);
            BufferedReader bufferedReader=new BufferedReader(fReader);
            FileWriter fileWriter=new FileWriter(newfile);
       		PrintWriter printWriter=new PrintWriter(fileWriter);){
            while(true) {
                String s=bufferedReader.readLine();
                if(s==null) {
                    break;
                }
                else {
               		 //判断该行是否以//开头,不是的话写入
                    if(!s.trim().startsWith("//")) {
                        printWriter.println(s);
                    }
                }
            }
        }
        catch (IOException e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        // TODO 自动生成的方法存根
        File javaFile=new File("D:\\Elipse\\练习-拆分文件\\src\\Main.java");
        Main main=new Main();
        main.removeComments(javaFile);
    }
}
  • 数据流

1.分类
DataInputStream 数据输入流
DataOutputStream 数据输出流

2.直接进行字符串的读写
使用数据流的writeUTF()和readUTF() 可以进行数据的格式化顺序读写
如本例,通过DataOutputStream 向文件顺序写出 布尔值,整数和字符串。 然后再通过DataInputStream 顺序读入这些数据。

注: 要用DataInputStream 读取一个文件,这个文件必须是由DataOutputStream 写出的,否则会出现EOFException,因为DataOutputStream 在写出的时候会做一些特殊标记,只有DataInputStream 才能成功的读取。

package stream;
      
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
      
public class TestStream {
      
    public static void main(String[] args) {
        write();
        read();
    }
 
    private static void read() {
        File f =new File("d:/lol.txt");
        try (
                FileInputStream fis  = new FileInputStream(f);
                DataInputStream dis =new DataInputStream(fis);
        ){
            boolean b= dis.readBoolean();
            int i = dis.readInt();
            String str = dis.readUTF();
             
            System.out.println("读取到布尔值:"+b);
            System.out.println("读取到整数:"+i);
            System.out.println("读取到字符串:"+str);
 
        } catch (IOException e) {
            e.printStackTrace();
        }
         
    }
 
    private static void write() {
        File f =new File("d:/lol.txt");
        try (
                FileOutputStream fos  = new FileOutputStream(f);
                DataOutputStream dos =new DataOutputStream(fos);
        ){
            dos.writeBoolean(true);
            dos.writeInt(300);
            dos.writeUTF("123 this is gareen");
        } catch (IOException e) {
            e.printStackTrace();
        }
         
    }
}

java- I/O(字节流、字符流、缓存流、数据流、对象流)_第24张图片
3.练习-向文件中写入两个数字,然后把这两个数字分别读取出来。
要求
第一种方式: 使用缓存流把两个数字以字符串的形式写到文件里,再用缓存流以字符串的形式读取出来,然后转换为两个数字。
注: 两个数字之间要有分隔符用于区分这两个数字。 比如数字是31和15,如果不使用分隔符,那么就是3115,读取出来就无法识别到底是哪两个数字。 使用分隔符31@15能解决这个问题。

第二种方式: 使用数据流DataOutputStream向文件连续写入两个数字,然后用DataInpuStream连续读取两个数字。

//缓存流
private static void firstMethod() {
        final String filePath = "E://123.txt";
        File file = new File(filePath);
            try {
                FileWriter fw = new FileWriter(file);
                BufferedWriter bw = new BufferedWriter(fw);
                String str = "31@15";
                bw.write(str);
                bw.close();
                fw.close();
                FileReader fr = new FileReader(file);
                BufferedReader br = new BufferedReader(fr);
                String str1 = br.readLine();
                br.close();
                fr.close();
                System.out.println("拆解前:"+str1);
                String[] str2 = str1.split("@");
                System.out.println("拆解后:");
                for(String i : str2) {
                    System.out.println(i);
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
 
    }
//数据流
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
 
public class TestData {
    public static void main(String[] args) {
        write();
        read();
    }
 
    private static void read() {
        File f = new File("d:/test.txt");
        try (FileInputStream fis = new FileInputStream(f); DataInputStream dis = new DataInputStream(fis);) {
 
            int i = dis.readInt();
            int a = dis.readInt();
 
            System.out.println("读取到整数:" + i);
            System.out.println("读取到整数:" + a);
 
        } catch (IOException e) {
            e.printStackTrace();
        }
 
    }
 
    private static void write() {
        File f = new File("d:/test.txt");
        try (FileOutputStream fos = new FileOutputStream(f); DataOutputStream dos = new DataOutputStream(fos);) {
 
            dos.writeInt(300);
            dos.writeInt(200);
 
        } catch (IOException e) {
            e.printStackTrace();
        }
 
    }
}
  • 对象流

1.定义
对象流指的是可以直接把一个对象以流的形式传输给其他的介质,比如硬盘
一个对象以流的形式进行传输,叫做序列化。 该对象所对应的类,必须是实现Serializable接口。

2.序列化一个对象
创建一个Hero对象,设置其名称为garen。
把该对象序列化到一个文件garen.lol。
然后再通过序列化把该文件转换为一个Hero对象
注:把一个对象序列化有一个前提是:这个对象的类,必须实现了Serializable接口

package charactor; 
import java.io.Serializable;
public class Hero implements Serializable {
    //表示这个类当前的版本,如果有了变化,比如新设计了属性,就应该修改这个版本号
    private static final long serialVersionUID = 1L;
    public String name;
    public float hp;
}
package stream;
    
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
  
import charactor.Hero;
    
public class TestStream {
    
    public static void main(String[] args) {
        //创建一个Hero garen
        //要把Hero对象直接保存在文件上,务必让Hero类实现Serializable接口
        Hero h = new Hero();
        h.name = "garen";
        h.hp = 616;
          
        //准备一个文件用于保存该对象
        File f =new File("d:/garen.lol");
 
        try(
            //创建对象输出流
            FileOutputStream fos = new FileOutputStream(f);
            ObjectOutputStream oos =new ObjectOutputStream(fos);
            //创建对象输入流              
            FileInputStream fis = new FileInputStream(f);
            ObjectInputStream ois =new ObjectInputStream(fis);
        ) {
            oos.writeObject(h);
            Hero h2 = (Hero) ois.readObject();
            System.out.println(h2.name);
            System.out.println(h2.hp);
               
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
            
    }
}

java- I/O(字节流、字符流、缓存流、数据流、对象流)_第25张图片
3.分类
ObjectInputStream
ObjectOutputStream

import java.io.Serializable;

public class Student implements Serializable{
 private static final long serialVersionUID=1L;
	private String id;
	private String name;
	private int score;
	public Student(String id, String name, int score) {
		super();
		this.id = id;
		this.name = name;
		this.score = score;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getScore() {
		return score;
	}
	public void setScore(int score) {
		this.score = score;
	}
	public String toString() {
		return  id +" "+ name +" "+ score;
	}
}
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class ObjectOutputTest {
		public static void main(String[] args) {
			try {
				BufferedReader br=new BufferedReader(new FileReader("data/17软件1班"));
				ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("data/17软件1班对象"));
				String str="";
				while((str=br.readLine())!=null){
     String[] strs=str.split("\t");
					Student stu=new Student(strs[0],strs[1],Integer.parseInt(strs[2]));
					//中间过程可以对学号,姓名和分数进行修改
					System.out.println(str);
					oos.writeObject(stu);					
				}
				br.close();
				oos.flush();
				oos.close();
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

}
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ObjectInputTest {
	public static void main(String[] args) {
		try {
			ObjectInputStream ois=new ObjectInputStream(new FileInputStream("data/17软件1班对象"));
			ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("data/17软件1班对象2"));
			Student student;
			/*(Student)ois.readObject()可以理解为一行一个对象,
			然后将对象强制转化为Student类型存储在数组对象student中*/
			while((student=(Student)ois.readObject())!=null){
				student.setScore(student.getScore()+50);
				System.out.println(student);
    oos.writeObject(student);
			}
			ois.close();
			oos.flush();
			oos.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			//e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

}

4.练习-序列化数组

准备一个长度是10,类型是Hero的数组,使用10个Hero对象初始化该数组

然后把该数组序列化到一个文件heros.lol

接着使用ObjectInputStream 读取该文件,并转换为Hero数组,验证该数组中的内容,是否和序列化之前一样

package charactor; 
import java.io.Serializable; 
public class Hero implements Serializable {
    //表示这个类当前的版本,如果有了变化,比如新设计了属性,就应该修改这个版本号
    private static final long serialVersionUID = 1L;
    public String name;
    public float hp;
 
}
import java.io.*;
 
public class ObjectStream {
    public static void main(String[] args) {
        Hero[] h = new Hero[10];
 
        for (int i = 0; i < h.length; i++) {
            h[i] = new Hero();
            h[i].hp = 100 + i;
            h[i].name = "hero" + i;
        }
 
        File f = new File("D:/w/w/garen.lol");
 
        try (FileOutputStream fos = new FileOutputStream(f);
                ObjectOutputStream oos = new ObjectOutputStream(fos);
                FileInputStream fis = new FileInputStream(f);
            ObjectInputStream ois = new ObjectInputStream(fis)) {
            	oos.writeObject(h);
            	Hero[] h2 = (Hero[]) ois.readObject();
            for (Hero i : h2) {
                System.out.println(i.name + ":" + i.hp);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

java- I/O(字节流、字符流、缓存流、数据流、对象流)_第26张图片

  • System.in

自动创建有一个属性的类文件。
通过控制台,获取类名,属性名称,属性类型,根据一个模板文件,自动创建这个类文件,并且为属性提供setter和getter。

public class @class@ {
    public @type@ @property@;
    public @class@() {
    }
    public void set@Uproperty@(@type@  @property@){
        this.@property@ = @property@;
    }
      
    public @type@  get@Uproperty@(){
        return this.@property@;
    }
}
import java.io.*;
import java.lang.reflect.Field;
import java.util.Scanner;
 
import javax.management.modelmbean.ModelMBean;
import javax.swing.text.ChangedCharSetException;
 
/**
 * 步骤 4 : 练习-自动创建类
 * @author 14323
 *
 */
public class SystemIn {
 
    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
        String str = "public class @class@ {\r\n" + 
                "    public @type@ @property@;\r\n" + 
                "    public @class@() {\r\n" + 
                "    }\r\n" + 
                "    public void set@Uproperty@(@type@ @property@){\r\n" + 
                "        this.@property@ = @property@;\r\n" + 
                "    }\r\n" + 
                "      \r\n" + 
                "    public @type@  get@Uproperty@(){\r\n" + 
                "        return this.@property@;\r\n" + 
                "    }\r\n" + 
                "}";
        //创建一个模板文件,并创建文件输出流
        File file = new File("E://C.class");
        FileWriter fw = new FileWriter(file);
        BufferedWriter bw = new BufferedWriter(fw);
        //将模板写入该文件
        bw.write(str);
        bw.close();
        fw.close();
        //调用ScannerByDemo(),获得要替换的数据,并且将各个值依次赋给对应的String
        String[] str1 = ScannerByDemo();
        String ClassName = str1[0];
        String type = str1[1];
        String typeName = str1[2];
        //更改@Uproperty@所对应的数据首字母为大写
        String UtypeName = typeName.substring(0, 1).toUpperCase()+typeName.substring(1);
        //创建文件输入流
        FileReader fr = new FileReader(file);
        BufferedReader br = new BufferedReader(fr);
        
        //实例化一个char类型的数组,用于接收br.read()所读到的数据
        char[] ch = new char[(int) file.length()];
        br.read(ch);
        //将ch转化为string类型后开始替换字符串中相应数据
        String str2 = new String(ch);
		/*	String strs="";
       		String str2="";
		while((strs=br.readLine())!=null){
			str2+=strs+"\n";
		}*/

        String str3 = str2.replaceAll("@class@", ClassName)
                    .replaceAll("@type@", type)
                    .replaceAll("@property@", typeName)
                    .replaceAll("@Uproperty@", UtypeName);
        //新建同名文件把数据写入(覆盖旧文件)
        FileWriter NewFw = new FileWriter(file);
        BufferedWriter NewBw = new BufferedWriter(NewFw);
        NewBw.write(str3);
        NewBw.close();
        NewBw.close();
        System.out.println("类文件创建完成!");
        System.out.println(str3);
         
    }
     
    /**
     * 获得要替换的数据,返回值为String数组
     * @return
     */
    private static String[] ScannerByDemo() {
        Scanner input = new Scanner(System.in);
        System.out.println("请输入类名:");
        String ClassName = input.nextLine();
        System.out.println("请输入属性的类型:");
        String type = input.nextLine();
        System.out.println("请输入属性的名称:");
        String typeName = input.nextLine();
        return new String[]{ClassName,type,typeName};
    }
}

java- I/O(字节流、字符流、缓存流、数据流、对象流)_第27张图片

  • 综合练习

1.复制文件
复制文件是常见的IO操作,设计如下方法,实现复制源文件srcFile到目标文件destFile

public static void copyFile(String srcFile, String destFile){
}

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class copyFile {
     
    public static void main(String[] args) {
     
        copyFile("before.txt","after.txt");
     
         
    }
 
    public static void copyFile(String scrFile, String destFile) {
         
        try(FileReader read = new FileReader(scrFile);BufferedReader bread = new BufferedReader(read);
                FileWriter write = new FileWriter(destFile);PrintWriter pwrite = new PrintWriter(write,true)){
             
            while(true) {
                 
                String string = bread.readLine();
                if(string == null)
                    break;
                                                 
                pwrite.write(string + "\r\n");
                 
            }
             
        }catch(IOException e) {
             
            e.printStackTrace();
        }
    }
}

2.复制文件夹
复制文件夹,实现如下方法,把源文件夹下所有的文件 复制到目标文件夹下(包括子文件夹)

public static void copyFolder(String srcFolder, String destFolder){

}

package stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class TestFile {
	public static void main(String[] args) throws IOException {
        copyFolder("D:\\LOLFolder\\lol\\abc\\bcd","D:\\LOLFolder\\lol");
    }
     
    //复制文件以及文件夹的方法
    public static void copyFolder(String srcFolder,String destFolder) throws IOException {
        //创建文件对象
        File oldfile=new File(srcFolder);
         
        //如果该文件夹为空
        if(oldfile.listFiles()==null) {
            //在目标文件夹下创建一个名字相同的文件对象
            File newfile=new File(destFolder,oldfile.getName());
             
            //在创建文件之前,要先创建父文件夹
            newfile.getParentFile().mkdirs();
             
            //然后创建同名文件夹
            newfile.mkdir();
             
            //然后返回
            return ;
        }
        //如果文件夹不为空,则遍历该文件夹下的所有文件
        else {
            File[] filesInOld=oldfile.listFiles();
            for (File file : filesInOld) {
                //System.out.println(file.getAbsolutePath());
                //判断是否是文件
                if(file.isFile()) {
                    //如果是文件,则在目标文件夹下创建一个同名的文件对象
                    File newf=new File(destFolder,file.getName());
                    //在创建文件之前,要先创建父文件夹
                    newf.getParentFile().mkdirs();
                    //然后新建这个文件
                    newf.createNewFile();
                     
                    //然后将文件中的内容进行复制
                    //注意,这里要传绝对路径
                    copyFile(file.getAbsolutePath(),newf.getAbsolutePath());
                }
                //判断是否是文件夹
                else if(file.isDirectory()) {
                    //如果是文件夹,则进行递归
                    copyFolder(file.getName(),destFolder);
                }
                 
            }
        }
    }
  //复制文件内容的方法
    public static void copyFile(String oldFile,String newFile) throws IOException {
        //由于文件夹中可能有字节码文件,比如图像或者视频等等,所以在这里我们使用字节流进行复制,
        //如果文件中只有文本文件,那我们就可以使用高效的字符缓冲流进行复制
         
        //创建文件对象
        File oldfile=new File(oldFile);
        File newfile=new File(newFile);
         
        //注意,这里的形参必须是D:\\TestLoL的绝对路径,不然的话fis对象会在当前项目目录去寻找要复制的文件
        //创建字节输入流对象
        FileInputStream fis=new FileInputStream(oldfile);
        //创建字节输出流对象
        FileOutputStream fos=new FileOutputStream(newfile);
         
        //读写数据
        byte[] bys=new byte[(int)oldfile.length()];   //定义一个字节数组存储读取到的字节数
        int len;   //保存实际读取到的字节数
        while((len=fis.read(bys))!=-1) {
            fos.write(bys, 0, len);
        }
         
        //释放资源
        fis.close();
        fos.close();
    }
}

3.查找文件内容
public static void search(File folder, String search);

假设你的项目目录是 e:/project,遍历这个目录下所有的java文件(包括子文件夹),找出文件内容包括 Magic的那些文件,并打印出来。

package file;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Scanner;

public class SearchContent {
	public static void main(String[] args) {
		File f = new File("D:\\LOLFolder");
		File[] dir= f.listFiles();
		Scanner scanner=new Scanner(System.in);
		System.out.println("输入你想查找的内容:");
		String search=scanner.nextLine();
		System.out.printf("\n内容包括%s的那些文件:%n",search);
		search(dir,search);
		System.out.println("\n查找结束。");
	}
	public static void search(File dir[], String search){
		if (dir == null) {
			return;
		} else {
			for (File fi : dir) {
				if (fi.isDirectory()) {
					search(fi.listFiles(), search);
				}
				if (fi.isFile()&&fi.getName().endsWith(".java")) {
					try(BufferedReader br = new BufferedReader(new FileReader(fi));){
						while(true){
		                    String line=br.readLine();
		                    if(line==null){
		                        break;
		                    }
		                    if(line.contains(search)){
		                        System.out.printf("文件:%s%n",fi.getAbsolutePath());
		                        break;
		                    }
		                }
					} catch (FileNotFoundException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}       
				}
			}
		}		
	}
}

java- I/O(字节流、字符流、缓存流、数据流、对象流)_第28张图片

  • 流关系图

1.流关系图
这个图把本章节学到的流关系做了个简单整理

  1. 流分为字节流和字符流
  2. 字节流下面常用的又有数据流和对象流
  3. 字符流下面常用的又有缓存流
    java- I/O(字节流、字符流、缓存流、数据流、对象流)_第29张图片

2.其他流
除了上图所接触的流之外,还有很多其他流,如图所示InputStream下面有很多的子类。 这些子类不需要立即掌握,他们大体上用法是差不多的,只是在一些特殊场合下用起来更方便,在工作中用到的时候再进行学习就行了。
java- I/O(字节流、字符流、缓存流、数据流、对象流)_第30张图片

你可能感兴趣的:(java)