各个国家为自己国家的字符取的一套编号规则,计算机底层只能存储二进制,二进制可以转成十进制,十进制可以进行整数编号,所以计算机底层可以存储编号规则
File类只能操作文件对象本身,并不能操作文件的内容(对文件内容进行读/写)。如果需要读写数据的操作,就需要使用I/O流
public int read(); //每次读取一个字节返回,读取完毕会返回-1
package FileInputStreamDemo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInDemo {
public static void main(String[] args) throws IOException {
//创建File对象
File f1=new File("src/FileInputStreamDemo/text.txt");
//创建文件输入流对象
FileInputStream in = new FileInputStream(f1);
//读取一个字节
int code=in.read();
//不断循环输出,直到数据末尾返回-1
while(code!=-1) {
char c=(char)code;
System.out.println(c);
code = in.read();
}
}
}
但这种读取方式并不有效,在中文出现后无法避免出现乱码(因为会截断中文字节),并且这种方式效率较差,不建议采用
package FileInputStreamDemo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInDemo {
public static void main(String[] args) throws IOException {
FileInputStream in = new FileInputStream("src/FileInputStreamDemo/text.txt");
//按照字节数组读取
byte[] buffer=new byte[1024];
//返回值是读取的字节数
int len=in.read(buffer);
System.out.println("读取了"+len+"个字节");
String rs=new String(buffer);
System.out.println(rs);
}
}
这种方式仍然无法避免中文输出乱码的情况
在定义一个字节数组用于缓存数据后,不断从文件中读取数据到字节数组中,假如下一次读取仍然利用这个字节数组,但读取的字节数小于第一次字节数组被占用长度,则后续部分的字节不被覆盖
例如第一次读取5字节abcde,第二次读取2字节fg。则读取完后字节数组的组成是fgcde,只有前两位被覆盖,后三位并没有改变
String rs=new String(buffer,0,len); //限制输出范围
public class FileInDemo {
public static void main(String[] args) throws IOException {
FileInputStream in = new FileInputStream("src/FileInputStreamDemo/text.txt");
byte[] buffer=in.readAllBytes();
String rs=new String(buffer);
System.out.println(rs);
}
}
package FileInputStreamDemo;
import java.io.*;
public class FileOutDemo {
public static void main(String[] args) throws IOException {
//创建字节输出流管道与目标文件对象连通
FileOutputStream out=new FileOutputStream("src/FileInputStreamDemo/text.txt");
//写一个字节出去,这种方式不能写入中文字符,因为一个中文字符3个字节,超出限制
out.write(97); //注意这里写的是写入字符的字符编码,不是要写入的字符
out.write('b');
//写一个字节数组出去
byte[] bytes=new byte[]{'a','b','c',100,101,102};
out.write(bytes);
byte[] bytes1="Less is more!--工程学名言".getBytes("GBK"); //可以指定编码格式,也可以直接默认采用系统当前编码格式
out.write(bytes1);
//写字节数组的一部分出去
out.write(bytes1,0,13);
//换行
out.write("\r\n".getBytes()); //这里利用\r\n来换行的原因是为了保证兼容性更好,Windows可以直接用\n换行
out.flush(); //立即刷新数据到文件中去,刷新后管道out还是可以正常调用
//以下的关闭操作在每一次进行读写操作后都必须运行
out.close(); //结束运行。结束后管道out不能继续使用,关闭操作包含刷新操作
}
}
IO流管道默认是覆盖管道,每次启动新的Stream管道之前,都会清空文件对象之前的内容,注意,这里是启动新管道之前,不是调用管道执行方法时
如果想要追加数据,而不是覆盖,只需要在创建管道时,设置管道第二个参数为true即可(第二个参数表示是否为追加数据管道)
字节是计算机中文件存储的最基本单位,所以字节流适合做一切文件的复制。
复制是把源文件的全部字节一个不漏的全部转移到目标文件,只要保证前后的格式一样,绝对不会出现错误
package FileInputStreamDemo;
import java.io.*;
public class CopyDemo {
public static void main(String[] args) throws IOException {
//创建字节输入流管道与源文件接通
FileInputStream in = new FileInputStream("src/FileInputStreamDemo/text.txt");
//创建字节输出流管道与目标文件连通
FileOutputStream out=new FileOutputStream("src/FileInputStreamDemo/output.txt");
//创建字节数组用来作为中间传播媒介,并从字节输入流中读取数据
byte[] medium=in.readAllBytes();
//向字节输出流中写入数据
out.write(medium);
//关闭所有管道资源
in.close();
out.close();
}
}
以内存为基准,把磁盘文件的数据以字符的形式读入到内存
package FileInputStreamDemo;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderDemo {
public static void main(String[] args) throws IOException {
FileReader fd = new FileReader("src/FileInputStreamDemo/input.txt");
//按照字符逐个读取
int c=fd.read();
while(c!=-1){
System.out.printf("%c",(char)c);
c=fd.read();
}
FileReader fd2 = new FileReader("src/FileInputStreamDemo/input.txt");
//读取字符数组
char[] chars=new char[1024];
int len=fd2.read(chars);
System.out.printf("\n\n共读取%d个字符!\n\n",len);
String str=new String(chars);
System.out.println(str);
}
}
可见字符流按照字符数组循环读取数组。可以解决中文读取乱码的问题,并且性能较好
缓冲流可以提高字节流和字符流的读写数据的性能
用于提高相对应的文件字节输入流读写数据的性能
可以把低级的字节输入流(FileInputStream)包装成一个高级的缓冲字节输入流(BufferedInputStream)管道,从而提高字节输入流读数据的性能
public BufferedInputStream(InputStream in);
缓冲字节输入流管道自带一个8KB的缓冲池,每次可以直接借用操作系统的功能最多提取8KB的数据到缓冲池中去,以后我们直接从缓冲池读取数据,所以性能较好
用于提高相对应的文件字节输出流读写数据的性能
可以把低级的字节输出流(FileInputStream)包装成一个高级的缓冲字节输出流(BufferedInputStream)管道,从而提高字节输入流读数据的性能
public BufferedOutputStream(OutputStream in);
与之同理
与之同理
我们在日常操作中经常需要把对象作为一种数据保存在文件中,典型的如涉及登录的cookies等。
这个过程中将对象作为数据保存到文件中的过程称为序列化,将文件中的数据重写读取出来并转换为对象的过程称为反序列化
序列化与反序列化使用到了相较于Reader,Writer更高级的对象输入输出流
package FileInputStreamDemo;
import java.io.*;
public class SerizalizeDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//对象序列化
//创建准备序列化的对象
User user = new User("Leslie", 18, 95);
//创建文件输出字节流,指向用于储存对象的文件
OutputStream out = new FileOutputStream("src/FileInputStreamDemo/text.txt");
//将文件输出字节流包装成高级的对象字节输出流
ObjectOutputStream out_object=new ObjectOutputStream(out);
//将对象写入文件
out_object.writeObject(user);
//关闭资源
out_object.close();
out.close();
System.out.println("对象序列化成功!");
//对象反序列化
//创建空对象用于接收输入的对象
User user_new=new User();
//创建文件输入字节流指向要读取的文件
FileInputStream in=new FileInputStream("src/FileInputStreamDemo/text.txt");
//将文件输入字节流包装成高级的能够用于对象反序列化的对象字节输入流
ObjectInputStream in_object=new ObjectInputStream(in);
//读入对象并强转为指定类型
user_new= (User) in_object.readObject();
System.out.println("反序列化成功!");
System.out.printf("姓名:%s 年龄:%d 分数:%d\n",user_new.getName(),user_new.getAge(),user_new.getScore());
}
}
在实际存储过程中还涉及到密码这种危险变量的存储问题,一般情况下,如果我们并不想将这种可能存在隐患的数据一并存储到文件中,我们可以在定义对象类时,在不想存储的成员变量前用transient变量修饰,由此修饰符修饰的变量,在对象被存储进文件中时,不会一并被存储(不参与序列化)
加入一个对象数据可能经过多次迭代更新,这时我们就可以在定义时为这个类添加版本序列号
private static final long serialVersionUID=1L;
版本序列号用长整型定义,结尾的L可加可省略,在定义了版本序列号后,序列化与反序列化要求前后的序列号必须一致,即版本1定义的变量存储在文件中,只能用版本同样为1的变量来接收,如果前后的版本号不一致,在反序列化时就会报错
Exception in thread "main" java.io.InvalidClassException: FileInputStreamDemo.User; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
public class PrintStreamDemo {
public static void main(String[] args) throws FileNotFoundException {
//打印流可以接低级流管道,或者直接接文件路径等等
PrintStream ps=new PrintStream("src/FileInputStreamDemo/input.txt");
ps.println(955); //注意这里写入的就是数字97,不是字符集的编码,因为打印流写什么就打印什么
ps.println("Leslie"); //这里直接打印字符串
ps.println(99.90); //打印浮点数
ps.println(false); //打印布尔值
//写字节出去
ps.write(97); //这里的97表示字符集的编码,也就是a
ps.write(100);
ps.close();
}
}
注意,在向缓冲流中传入低级流时不能传入打印流,因为打印流本身功能相较于缓冲流更强大,打印流本身以及包含了缓冲流的内容,是比缓冲流更高级的流
public class PrintStreamDemo {
public static void main(String[] args) throws FileNotFoundException {
PrintStream ps=new PrintStream("src/FileInputStreamDemo/input.txt");
System.setOut(ps); //重定向功能,让系统的输出流流向打印流,所以以下输出均不会在控制台显示
System.out.println("Leslie");
System.out.println("John");
ps.close();
}
}
本质是一个Map集合,即键值对集合。核心用途在于当作属性文件(后缀是.properties结尾的文件,里面的内容都说是键值对,在大型框架中十分常见)。可以把键值对的数据存入到一个属性文件中去
public class PropertiesDemo {
public static void main(String[] args) throws IOException {
//将数据写入属性集文件
//创建属性集对象
Properties p=new Properties();
//存入键值对数据
p.setProperty("admin","123456");
p.setProperty("root","123456");
//将数据存入属性文件中
OutputStream os=new FileOutputStream("src/FileInputStreamDemo/new.properties");
p.store(os,""); //参数二表示--保存心得,对保存数据进行解释说明
os.close();
//从属性集文件中读出属性集对象
//利用字节输入流加载属性文件中的数据到属性集对象p1中去
Properties p1=new Properties();
InputStream is=new FileInputStream("src/FileInputStreamDemo/new.properties");
p1.load(is);
System.out.println(p1);
}
}