第一:首先说一下字符,字节等概念,不然很容易乱
一个字节是八个比特位(bit),一个英文占1个字节,一个汉字在utf-8里占3个字节
字节是byte,字符是char
字符不管编码是怎么样,一个英文是一个字符,一个汉字也是一个字符
注意,不要觉得字符比字节大,因为这两个不是一个概念
第二:总结一下下面内容。
流相当于外卖小哥,可以从某个文件对象或者文件地址拿进拿出东西,也就是对应着输入流和输出流。
这种真正拿东西的称为节点流,增强拿东西速度的或者其他方面优化的,称为处理流,处理流相当于把节点流放进去升级一下,所以传入的对象是流对象,操作起来就有别的方法。
节点流和处理流是一个方面的分类,另一个方面的分类称为字符流和字节流(也可以说节点流和处理流分别又分为字符流和字节流)
字节流是指传递的单位是字节,也就是一个bety,也可以传bety数组,总之单位是字节,这种的优势是能够传输图像视频这种二进制文件不会有损耗,但是传输不快,并且由于编码方式不同,传输汉字等会乱码
字符流按字符为单位传输,适合传输文本文件,速度快而且不会乱码,但是不能传送图像。
流用完了要关闭,不然不仅浪费空间,还有可能造成数据没传输成功的后果。
文件
1.文件相关概念
什么是文件:文件就是储存数据的地方
文件流:文件在程序中是以流的形式来操作的
输入流:把文件的数据读入到内存中
输出流:吧内存中的程序写到文件中
2.常用的文件操作
文件一般指的是File类型的对象,如果对某个文件操作,首先要为他创建一个对象,在用这个对象的方法操作。
1.创建文件对象相关构造器和方法
new File(String pathname); //根据路径构造一个File对象
new File(File parent, String child); // 根据父目录文件+子路径构建
new File(String parent,String chile); //根据父目录+子路径构建
创建新文件方法:
creatNewFile
提问:为什么new了新文件,还要再创建文件的方法才能创建
因为new了,只是在java程序中的一个对象而已,只有调了创建方法,才会在硬盘生成
举例:
在e盘下,创建文件new.txt
File file = new File("e:\\new.txt");
try {
file.createNewFile();
System.out.println("文件创建成功");
} catch (IOException e) {
throw new RuntimeException(e);
}
2.获取文件信息
获取名字:getName
文件的绝对路径:getAbsolutePath
文件的父级目录:getParent
文件大小(字节):lenth(不同编码不一样,utf-8是英文一个字节,汉字3个字节)
文件是否存在:existes
是不是一个文件:isFile
是不是一个目录:isDirectory
3.目录的操作
目录就是文件夹,操作和文件一样
删除操作:file.delete
创建目录:file.mkdirs() //加了s是多级的
4.类图分析:
File实现了两个接口,Serializable说明可以串行化,Comparable说明可以比较
Io流原理及流的分类
java的io流原理:
1.io是input和output的缩写,是非常实用的技术,用于处理数据传输,如读写文件,网络通信
2.java程序中,对于数据的输入输出操作以流(stream)的方式进行
3.io下提供了各种流的类和接口,用于获取不同的种类和数据,并通过方法输入或输出各种数据
4.不只是文件和内存中,也可能是与网络,与数据库之间的传输
流的分类
1.按照操作数据的单位:字节流(8bit),字符流(对应几个字节与编码有关)<字符更快,因为一次传的多,但是如果传图片影像,用字节流,可以无损传播,如果传输文本,可以用字符流>
2.按照流向:输入流,输出流
3.按照流的角色不同:节点流,处理流/包装流
什么是流?
流是传输的一个东西,是输入输出的一种路线的感觉,实际上,他没那么抽象
他是一个类!根据上边不同的分类,有很多类流的类,这些类负责对文件等进行操作,就像一个快递员,我把任务交给了快递员,他就有一系列方法,比如,read方法,读取一个字节,等等
节点流和处理流
节点流:可以从一个特定的数据源(比如某个文件)读写数据,如FileReader、FileWriter
处理流(包装流):是在连接已存在的流之上,为程序提供更强大的读写功能,比如:
BufferedReader,缓冲流,更快
(举例在输入流里有)
Object…流,叫对象处理流,解释如下:
1.将一个数据保存到文件中,但是不止是保存值,而是值和数据类型都保存,比如100这个数,不知要保存100,还要表明时int 还是String还是别的。
2.序列化:都保存,就叫序列化
3.反序列化:恢复的时候,数据类型和值都恢复就叫反序列化
4.需要让某个对象支持序列化机制,前提时实现Serializable(标记接口,没有方法,推荐)或者Externalizable这两个接口之一。
5. 序列化后,保存的文件格式不是存到文本,而是按照他的格式来保存
上代码:
序列化过程:
String path1 = "e:\\object.txt";
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path1));
oos.writeInt(100); //自动打包变成Intager类,可以序列化
oos.writeBoolean(true);
oos.writeChar('a');
oos.writeUTF("我是大王"); //UTF的意思是字符串
oos.writeObject(new Dog()); //这里Dog在定义的时候要去实现Serializable接口,才能序列化
oos.close(); //关外层流
System.out.println("序列化完成");
反序列化(恢复)一定要和输入的顺序一致,不然要抛异常
String path1 = "e:\\object.txt";
ObjectInputStream ooi = new ObjectInputStream(new FileInputStream(path1));
System.out.println(ooi.readInt());
System.out.println(ooi.readBoolean());
System.out.println(ooi.readChar());
System.out.println(ooi.readUTF());
System.out.println(ooi.readObject()); //这里要用这个对象,要引用对应的类进来然后向下转型,另外,每次修改了东西,想读的话要重新序列化
ooi.close();
对象处理流的注意事项:
1.读写顺序一致
2.要求序列化或者反序列化的对象, 需要实现Serializable接口
3.序列化的类中加SerialVersionUID可以提高版本兼容性
4.序列化对象时,默认将里面所有属性都序列化,但除了static或transient修饰的
5.序列化对象里面的对象也要实现Serializable接口
输入流✨重点
————————————————————————————
字节流:
1.字节输入流InputStream(读,由外到内叫做输入)(抽象类,以下为其实现子类)。
(1)FileInputStream文件字节输入流
(2)BufferedInputSteam缓冲字节输入流
(3)ObjectInputStream对象字节输入流
2.FileInputStream文件输入流:
(1)构造器:使用文件对象,路径(字符串),文件描述符,三者其一构造即可
(2)方法:read()读取一个字节的数据,返回int,文件末尾返回-1,说明读完了
read(byte[] b)返回的是数组中有字符的长度,也就是这一次读了多少个字符,然后真正的数据保存到b这个字节数组里,我们再打印的时候就用new String(b,0,每次的长度)这样来生成字符串再打印。(好处是可以一次传多个)
(3)使用:先新建一个流对象
FileInputStream fileInputStream = new FileInputStream(path);
然后读数据,注意:
①因为每次都一个字节,所以要循环
②读取的数据每次返回的int,所以要转成char
③因为流创建了就会在那里,所以每次用完了要关闭,不然浪费,所以在finally里要点close。但是因为try作用域的问题,所以我们要把字节流的定义放到外面,这样扩大了作用域finally才能关闭。
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(path);
while((readDate = fileInputStream.read()) != -1){
System.out.println((char)readDate);
}
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
try {
fileInputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
字节数组:
int readDate;
byte[] b = new byte[8];
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(path);
while((readDate = fileInputStream.read(b)) != -1){
System.out.println(new String(b,0,readDate));
}
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
try {
fileInputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
————————————————————————————
字符流:
FileReader文件字符输入流
构造器:可以文件或路径构造
方法:
read,每次读取一个字符,返回该字符(类型为int,需要自己再转成char),如果到达末尾则返回-1
read(char[])读多个字符到char数组,返回读取的字符数目
然后我门用new String(char[])或者(char[],off,len)读取一部分
代码:
//第一种
FileReader fileReader = null;
int a ;
try {
fileReader = new FileReader("e:\\new2.txt");
while((a =fileReader.read()) != -1) {
System.out.println((char)a);
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
try {
fileReader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
//第二钟,一次读一个字符数组,长度为4
FileReader fileReader = null;
int a ;
char[] c = new char[4];
try {
fileReader = new FileReader("e:\\new2.txt");
while((a = fileReader.read(c)) != -1) {
System.out.println((new String(c,0,a)));
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
try {
fileReader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
——————————————————————————————
BufferedReader(缓冲,让读取的更高效)
1.后边是Reader说明是字符的
2.构造器要传的不是地址或者文件对象,而是流对象,意思是他是包装流(处理流),把原来就有的流升级,如下:
BufferedReader bufferedReader = new BufferedReader(new FileReader(path));
使用:
1.可以一次读一行,readLine(),空的时候就是读完了
String line;
while((line = bufferedReader.readLine()) != null){
System.out.println(line);
}
2.也需要关闭,但是只用关闭buffered的就行,底层会自动环关闭节点流
——————————————————————————————
输出流✨重点
1.FileOutputStream文件字节输出流
如果该文件不存在,则创建该文件再写入
与输入流方法类似,只是读变成了写,此处只写不同点与使用演示。
方法:
写一个字节:fileOutputStream.write(“a”); //注意输入的应该是int,但是字符类型可以自动转成int
写入字符串:fileOutputStream.write(str.getBytes()); //这里需要一个字节数组,字符串提供了一个方法就是把字符串转成字节数组
写入字符串的某一部分fileOutputStream.write(str.getBytes(),int off,int len);//截取off开始的长度为len的数组写入
注意:如果新建字符流的形式是
fileOutputStream = new FileOutputStream(path);
那么每次都会覆盖掉原来的,想要追加,创建字符流要在后边加一个true(也就是append这个位是真,append可以理解为追加)
fileOutputStream = new FileOutputStream(path,true);
写文件演示
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(path,true);
fileOutputStream.write("aasaas".getBytes());
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
try {
fileOutputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
————————————————————————————
字符流:
FileWriter文件字符输入流
构造器:可以文件或路径构造,加了true就是追加模式,不加就是覆盖
方法:
可以写入单个字符,也可以写入char数组,或者指定数组的范围,或者字符串,或者字符串的范围,如果想把String转成char数组,就用String的toCharArray这个方法
注意,使用后一定呀close或者flush,不然写不进去,只是保存在内存里,只有关了或者刷新才真正进入文件
——————————————————————————————
文件拷贝(文件输入输出流应用)
思路:创建输入流,把文件的内容输入到程序,再创输出流,把读到的内容输出到某一位置,文件一般比较大,所以应该读一部分就写一部分。
我的疑惑:怎么做到读一部分写一部分?我觉得应该读到一个数据结构里,然后打断读操作,再把数据结构里的东西写到某个文件。但是我不知道怎么打断
其实:读要一个while循环,只要再循环判断那里读到数据结构,然后再循环内部写就好了赛,代码如下:
(这里注意,write不能直接传字节数组,要加上范围传,不然传图片什么的会出错)
int readDate;
byte[] b = new byte[8];
FileOutputStream fileOutputStream = null;
FileInputStream fileInputStream = null;
try {
fileOutputStream = new FileOutputStream(path2,true);
fileInputStream = new FileInputStream(path);
while((readDate = fileInputStream.read(b)) != -1) {
fileOutputStream.write(b,0,readDate);
}
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
try {
fileOutputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
————————————————————————————
标准输入输出
System.in System.out
in 和 out 时System里的属性,类型分别为
public final static InputStream in = null; //运行类型是buffered 的
public final static PrintStream out = null;
标准输入是键盘,也就是System.in这个流连接的是键盘和内存
标准输出是显示器,也就是System.out这个流连接的是内存和显示器
转换流
可以把字节流转成字符流,为什么要转?
因为不同的编码方式,字符读的不一样
可以先用字节读,指定编码方式,然后再转成对应编码方式的字符
(InputStreamReader其实就是FileReader类似的,不止是转换工具,而是真正的流)
演示:使用InputStreamReader解决中文乱码问题。将字节流FileInputStream转成字符流InputStreamReader,指定编码
也就是先用字节读,用gbk编码,转成InputStreamReader类型,再转入处理流buffer,提高效率
打印流
打印流只有输出流,没有输入流
PrintStream(字节) 和 PrintWriter(字符)
构造器可以定义文件或者地址,不止是可以输出在显示器上
Properties类
用来处理xxx.properties配置文件
要从配置文件里读数据,按照传统方式,就是BufferedReader读了,再按照等号分割(String 的split方法,返回一个字符串数组)
但是比较麻烦,我们要想只得到某个数据,就要一直判断
引入了Properties
专门用于读写配置文件的集合类,格式为
键=值
注意键值对不需要空格,值也不需要引号
常见方法:
1.load:加载配置文件的键值对到Properties对象
2.list:将数据显示到指定设备
3.getProperty(ket):根据键获取值
4.setProoerty(key,value)设置键值对到Properties对象
5.store:将Properties中的键值对储存到配置文件,再idea中,保存信息到配置文件,如果含有中文,会储存对应的unicode码(字节输出)
使用:
Properties properties = new Properties(); //新建一个对象
properties.load(new FileReader("src\\peizhi.properties")); //把对应文件加载到对象里,首先要加载到流里,再把流加载到对象
//properties.list(System.out); //把数据显示到标准输出流,就是显示器(这里需要的参数是打印流,我只是用了标准的)
System.out.println(properties.get("ip")); //通过键值获取对应信息,返回的是字符串
修改(修改完了,要store,才能存进去):
properties.setProperty("老李","sasa");
properties.store(new FileOutputStream("src\\peizhi.properties"),null); //null这个位置代表注释