JavaSE基本知识补充 -IO流
目录
1.IO概述
1.1流的分类
2.字符流
2.2案例-输入
文件的追加
换行
Writer的五种写入的方法
2.3案例-输出
Reader的读取方式
2.4复制
2.5 BufferedWriter
2.6 BufferedReader
3.字节流
1.输出OutputStream
输出单个字符
输出字节数组
追加
2.输入InputStream
输入单个符
输入字节数组
输入字节到数组的某一部分
3.复制
复制图像
4.复制-BufferedInputStream和BufferedOutputStream
总结:
4.相互转化
5.properties
实际应用场景:
6.序列化流
7.random
1.IO概述
IO(Input/Output):输入和输出,指的是某个设备或环境进行数据的输入或者输出。例如:键盘的输入,再比如显示器就是输出设备,输出图像。
对于java来说输入输出问题,java将它抽象成流对象来解决。
以游戏程序为中心读取文件就是输入,写入文件是输出。
所有的文件的传递,都变成流。
从外部读取文件叫做输入。reader
把游戏记录保存到外部设备(目标文件)叫做输出。writer
1.1流的分类
IO流在java中从输入输出角度分类:
1.输入流
2.输出流
IO流在java中从数据的角度来分类:
1.字符流
文本,我们能读的懂的都可以认为是字符流。比如:文章,java文件等等
2.字节流
二进制的数据,这种数据一般用文本打开我们读不懂。比如,图片文件,mp3文件,等等。能看懂图片的样式,但是不懂怎么形成的图片
2.字符流
文本,我们能读的懂的都可以认为是字符流。比如:文章,java文件等等
字符流
输出流
输入流
字符流的类的命名的规则:
如果是输出流就以Writer结尾
如果是输入流就以Reader结尾
2.2案例-输入
使用字符流向一个文件输入helloworld。
分析:
步骤:
1.创建文件
2.创建输出流对象
3.把流指向指定的文件
4.释放资源
//创建一个文件
File file = new File("test.txt");
Writer writer = null;
try {
//IO流是需要关闭的,如果不这样设计就会不能关闭资源
writer = new FileWriter(file);
writer.write("HelloWorld");
} catch (IOException e) {
e.printStackTrace();
}finally {
//判断writer不是空防止空指针异常
if(writer != null) {
try {
//在关闭前会做flush的事情
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
在IO流里一定会用try-catch
finally一定会执行,无论有没有异常
如果有异常,捕获异常。
流,就要关闭流。
文件的追加
Writer writer = null;
try {
//IO流是需要关闭的,如果不这样设计就会不能关闭资源
//writer = new FileWriter("test.txt", true);
writer = new FileWriter("test.txt", true);
writer.write("zhangsan");
} catch (IOException e) {
e.printStackTrace();
}finally {
//判断writer不是空防止空指针异常
if(writer != null) {
try {
//在关闭前会做flush的事情
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
追加,不然会覆盖。
换行
输出换行
把文本写入文件中\n代表换行
问题是不同的环境下换行的方式也不一样
Windows: \r\n
Linux:\n
Mac:\r
//创建一个文件
File file = new File("test.txt");
Writer writer = null;
try {
//IO流是需要关闭的,如果不这样设计就会不能关闭资源
writer = new FileWriter(file);
for (int i = 0; i < 100; i++) {
writer.write("HelloWorld\r\n");
//每次写入10个helloworld的时候做一次flush
if(i % 10 == 0){
writer.flush();
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//判断writer不是空防止空指针异常
if(writer != null) {
try {
//在关闭前会做flush的事情
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
flush:流可以理解为一个管道,如果一直开着,最后关闭管道里还会有数据。每十个做一次把数据推出去。刷新缓冲
不是追加,覆盖
Writer的五种写入的方法
1.写入字符数组
char[] c = {'a','b','p','b','p'};
writer.write(c);
2.写入字符数组的某一部分
char[] c = {'a','b','p','b','p'};
// writer.write(c);
//把数组中的一部分写入文件
writer.write(c, 2, 2);
从下标为2开始,长度为2
3.写入单个字符
writer.write(97);
4.写入字符串
writer.write("nihao");
5.写入字符串的某一部分
writer.write("helloworld", 2, 2);
2.3案例-输出
分析:
步骤:
1.创建文件
2.创建输出流对象
3.把流指向指定的文件
4.释放资源
File file = new File("c.txt");
Reader reader = null;
try {
//创建出入流对象FileReader
reader = new FileReader(file);
//读取数据, 读取的字符被转换成了ascii
int c = -1;
//单个字符的读取性能低
while((c = reader.read()) != -1){
System.out.print((char)c);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
if(reader != null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
如果读取到的是-1后面就没有字符了
Reader的读取方式
1.将字符读入数组
File file = new File("c.txt");
Reader reader = null;
try {
//创建出入流对象FileReader
reader = new FileReader(file);
//定义一个数组 字符 char
char[] cs = new char[5];
//向字符数组中填数据
int len = reader.read(cs);
System.out.println("读取的长度:"+len+" 读取的内容:"+ Arrays.toString(cs));
//本次读取的字符的长度,如果要是文件中没有数据了,读取到的是-1
len = reader.read(cs);
System.out.println("第二次读取的长度:"+len+" 读取的内容:"+ Arrays.toString(cs));
len = reader.read(cs);
System.out.println("第三次读取的长度:"+len+" 读取的内容:"+ Arrays.toString(cs));
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
if(reader != null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.将字符读入数组的某一部分
File file = new File("c.txt");
Reader reader = null;
try {
//创建出入流对象FileReader
reader = new FileReader(file);
//定义一个数组
char[] cs = new char[5];
int read = reader.read(cs, 2, 3);
System.out.println(Arrays.toString(cs));
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
if(reader != null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.读取单个字符
reader = new FileReader(file);
System.out.println((char)reader.read());
reader = new FileReader(file);
for (int i = 0; i <5 ; i++) {
System.out.println((char)reader.read());
}
4.读入字符串
reader = new FileReader(file);
StringBuilder s=new StringBuilder();
int c = -1;
//单个字符的读取性能低
while((c = reader.read()) != -1){
s.append((char)c);
}
System.out.println(s);
读到-1表示没有了。
2.4复制
1.创建输入输出流对象
2.创建要拷贝的文件对象和要到达的文件对象
3.输入流读取文件
4.输出流写入
5.关闭输入输出流
//1.创建需要的输出输入流对象
Reader reader=null;
Writer writer=null;
//2.创建要拷贝的文件file对象 以及要到达的新文件
File file=new File("test3.txt");
File file1=new File("test5.txt");
//3.利用字符输入流读取文件test3.txt
try {
//读取文件test3.txt
reader = new FileReader(file);
//创建字符输出流对象
writer=new FileWriter(file1);
//创建一个字符数组 减少读取的io次数
char [] cs = new char[1024];
//定义边界值
int len = -1;
//利用循环 找到边界值 读取文件
while((len = reader.read(cs)) != -1){
//此时读到的内容要写进新的文件通过字符输出流
//把输入流读取到的数据写入字符输出流
writer.write(cs, 0, len);
}
//为了解决传递压迫性能问题 使用flush操作
writer.flush();
//改成大异常 更方便
} catch (Exception e) {
e.printStackTrace();
}finally{
//最后把两个流关闭
try {
if(writer != null){
writer.close();
}
if(reader != null){
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
2.5 BufferedWriter
.newLine()一行一行输入的
BufferedWriter (Java 2 Platform SE 6)
可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。
该类提供了 newLine() 方法,它使用平台自己的行分隔符概念,
//创建高效缓冲字符输出流
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter("IOTest.java"));
//写一行数据
writer.write("helloworld");
//换行
writer.newLine();
writer.write("helloworld");
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
//资源关闭
if(writer != null){
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.6 BufferedReader
BufferedReader reader = null;
try {
//创建一个高效缓存字符输入流对象
reader = new BufferedReader(new FileReader("CopyDemo1.java"));
String line = null;
while((line = reader.readLine()) != null){
//打印一行
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//关闭外层的对象的时候,内存的资源会自动的背关闭
if(reader != null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Java。io下的outputStream
3.字节流
二进制的数据,这种数据一般用文本打开我们读不懂。比如,图片文件,mp3文件,等等。
能看懂图片的样式,但是不懂怎么形成的图片
1.输出OutputStream
输出单个字符
//创建字节输出流
OutputStream out = null;
try {
out = new FileOutputStream(new File("a.txt"));
//字节流不需要flush
out.write(98);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(out != null){
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
输出字节数组
//创建一个字节的数组
byte[] bs = {97, 99, 103, 111};
out = new FileOutputStream(new File("a.txt"));
out.write(bs);
追加
//创建一个字节的数组
byte[] bs = {97, 99, 103, 111};
//out = new FileOutputStream(new File("a.txt"));
out = new FileOutputStream("a.txt", true);
out.write(bs, 1, 2);
2.输入InputStream
创建输入流对象
创建输入流文件对象
关闭
输入单个符
//创建字节输入流的对象
InputStream in = null;
try {
in = new FileInputStream("a.txt");
int r = in.read();
System.out.println((char)r);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(in != null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
输入字节数组
//创建字符输入流的对象
InputStream in = null;
try {
byte[] bs = new byte[6];
in = new FileInputStream("a.txt");
int len = in.read(bs);
System.out.println("读取了字节数:"+len+" 读取的内容:"+ new String(bs, 0, len ));
len = in.read(bs);
System.out.println("读取了字节数:"+len+" 读取的内容:"+ new String(bs, 0, len ));
len = in.read(bs);
System.out.println("读取了字节数:"+len);
//创建一个字节的数组
byte[] bs = new byte[1024];
//创建字符输入流的对象,如果有中文有可能出现乱码
in = new FileInputStream("a.txt");
//定义读取到的长度的标识
int len = -1;
while((len = in.read(bs)) != -1){
String s = new String(bs, 0 , len);
System.out.print(s);
}
输入字节到数组的某一部分
byte[] bs = new byte[6];
in = new FileInputStream("a.txt");
int len = in.read(bs, 1, 2);
System.out.println("读取了字节数:"+len+" 读取到的内容:"+ Arrays.toString(bs));
从下标为1开始,长度为2
3.复制
创建输入输出流对象
创建被复制文件的输入流对象
创建目标文件的输出流对象
定义一个字节数组,输入流读取写入数组,输出流读取数组中的内容写入字节输出流对象
复制图像
InputStream in = null;
OutputStream out = null;
try {
//创建字节输入流
in = new FileInputStream("plane.jpg");
//创建字节输出流
out = new FileOutputStream("plane01.jpg");
//定义一个字节数组,输入流读取写入数组,输出流读取数组中的内容写入字节输出流对象
byte[] bs = new byte[1024];
int len = -1;
while((len = in.read(bs)) != -1){
//把读取的数据写入字节输出流
out.write(bs, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(out != null){
out.close();
}
if(in != null){
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
4.复制-BufferedInputStream和BufferedOutputStream
BufferedInputStream (Java 2 Platform SE 6)
创建 BufferedInputStream 时,会创建一个内部缓冲区数组。在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。
之前的是一个字节一个字节读,带缓冲之后的流,可以一次读很多字节。
//定义一个高效缓存字节流
BufferedInputStream in = null;
BufferedOutputStream out = null;
try {
//创建一个高效缓存字节流对象
in = new BufferedInputStream(new FileInputStream("plane.jpg"));
out = new BufferedOutputStream(new FileOutputStream("plane01.jpg"));
//定义一个字节数组
byte[] bs = new byte[1024];
//定义一个标志
int len = -1;
while((len = in.read(bs)) != -1){
out.write(bs, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(out != null){
out.close();
}
if(in != null){
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
总结:
- 从输入输出角度,分为
输入流,输出流
- 从数据角度,分为
- 字符流:Writer和Reader
高级的
BufferedReader和BufferedWriter
2. 字节流:inputStream和outputStream
高级的就是BufferedinputStream和BufferedOutputStream
复制,就是先输入,再输出
4.相互转化
字符流可以操作字节流的所有操作
编码格式不一样,会造成翻译过来的字节数组不一样。
OutputStreamWriter是一个单独的类:字符流通向字节流的桥梁
与之相对的是InputStreamReader: 字节流通向字符流的桥梁
//创建字符流转换字节流的桥梁的对象
OutputStreamWriter ow = null;
try {
ow = new OutputStreamWriter(new FileOutputStream("b.txt"));
ow.write("中");
ow.flush();
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(ow != null){
ow.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
String s = new String("中"); byte[] gbks = s.getBytes("GBK"); System.out.println(Arrays.toString(gbks));
5.properties
在JDK里properties是对象
有一种文件格式.properties,里面是key-value格式。
通过IO流读取这个文件,得到key和value。一般可以用于连接数据库。
Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。
特点:
1.继承于Hashtable,是线程安全的键值对存储结构
2. Properties 可保存在流中或从流中加载
3. 只能保存字符串的键值对。
//创建一个Properties对象
Properties prop = new Properties();
System.out.println(prop);
InputStream in = null;
try {
//in = new FileReader("src/names.properties");
//通过类的类对象 加载的时候读取 propreties文件拿到键值对数据 并使用Properties的类的API存取数据的过程
in = PropTest6.class.getClassLoader().getResourceAsStream("names.properties");
System.out.println(in);
//从字节流中来加载数据到属性对象中
prop.load(in);
System.out.println(prop);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(in != null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
类名.class发射获取类信息。
.getClassLoader()加载的时候
getResourceAsStream资源变成流的形式。
创建properties对象,这个对象加载这个流得到键值对。
//创建一个Properties对象
Properties prop = new Properties();
//存储键值对
prop.setProperty("001", "liangge");
prop.setProperty("002", "liangliang");
prop.setProperty("003", "daliang");
System.out.println(prop);
String name1 = prop.getProperty("001");
String name2 = prop.getProperty("002");
String name3 = prop.getProperty("003");
System.out.println(name1);
System.out.println(name2);
System.out.println(name3);
实际应用场景:
JDBC:
创建了一个连接数据库的文件。
工具类:
创建properties对象,在加载这个流的时候得到键值对。
通过key找到对应的value值,把value值赋给静态对象。
static代码块:类一加载的时候就调用
一加载一调用一读取,就可以连接到数据库。
之后如果再想更改数据,直接在配置文件里更改
微服务器:游戏,原来更新需要停服好几天。现在是不停服更新。
以后项目部署了,想换一个库,不需要停掉项目,可以在外部映射一个文件,外部这个文件改,里面那个文件跟着改。
6.序列化流
序列化流:
对象输出流:(对象序列化)
ObjectOutputStream 将 Java 对象写入 OutputStream。
对象输入流:(对象反序列化)
ObjectInputStream 对以前使用 ObjectOutputStream 写入的对象进行反序列化。
将对象序列化:
实现序列化接口
以后在写实体类对象时可以加上。
生活例子:
两套一模一样的积木,每一个零件都有编码,如果打散了,还能再找回。
我们一般会提供一个serialVersionUID
某个类序列化之后,如果类发生了 ,那么依然可以反序列化。
如果要是对多个对象来做序列化,一定要放在集合中。
一旦实现了Serializable接口,就能够实现序列化和反序列化接口。
当我们的业务中需要序列化多个文件时,用ArrayList存储对象,并序列化这个ArrayList就可以了。
因为ArrayList本身是就是实现了Serializable接口,所以ArrayList是可以参与序列化的。
7.random
Random (Java 2 Platform SE 6)
Random(long seed) 使用单个 long 种子创建一个新的随机数生成器。
种子不变,生成的这一组数据不变。
next(int bits) 生成下一个伪随机数。
nextInt() 返回下一个伪随机数,它是此随机数生成器的序列中均匀分布的 int 值。
nextInt(int n) 返回一个伪随机数,它是取自此随机数生成器序列的、在 0(包括)和指定值(不包括)之间均匀分布的 int 值。