简介:Java I/O
I :输入 程序读取文件
O:输出 程序往外写文件
技能目标:
一、理解Java中的流
二、会使用字节流读写文件
三、会使用字符流读写文件
四、掌握对象的序列化和反序列化
五、了解反射原理
—————————————————————————————————————
File类
File对象即可表示文件,也可表示目录,在程序中一个File对象可以代表一个文件或目录。利用它可用来对文件或目录进行基本操作。它可以查出与文件相关的信息,如名称、最后修改日期、文件大小等。
File类的常用方法:
1.boolean exists():判断文件或目录是否存在
2.boolean isFile():判断是否是文件
3.boolean isDirectory():判断是否是目录
4.String getPath():返回此对象表示的文件的相对路径
5.String getAbsolutePath():返回此对象表示的文件的绝对路径
6.String getName():返回此对象表示的文件或目录的名称
7.boolean delete():删除此对象指定的文件或目录
8.boolean createNewFile():创建名称的空文件,不创建文件夹
9.long length():返回文件的长度,单位为字节,如果文件不存在,则返回OL
站在程序的角度上
—————————————————————————————————————
认识Java的流
前面讲述了如何利用java.io包的File类对文件或目录的属性进行操作,但File类不能访问文件的内容,即不能从文件中读取数据或往文件里写数据。
读文件是指把文件中的数据读取到内存中。反之,写文件是把内存中的数据写到文件中。那么通过什么读写文件呢?答案就是流。
流:流是指一连串流动的字符,是以先进先出的方式发送和接受数据的通道。
流分为输入流和输出流。输入/输出流是相对于计算机内存来说的,如果数据输入到内存,则称为输入流,如果从内存中输出则称为输出流。Java的输出流主要由OutputStream和Write作为基类,而输入流则主要有InputStream和Reader作为基类。
构造流对象时往往会和数据源(如文件)联系起来。数据源分为源数据源和目标数据源。输入流关联的是源数据源,输出流则关联的是目标数据源,如图下所示:
—————————————————————————————————————
字节流
InputStream(抽象类):字节输入流 (读)
OutputStream(抽象类):字节输出流(写)
—————————————————————————————————————
InputStream类常用方法:
1.int read():从输入流一个字节一个字节的读,返回的是该字节的整数表示形式,如果到了输入流的末尾,返回-1
2.int read(byte[] b):从输入流读取若干个字节,把这些字节保存到数组b中,返回的是读取到的字节数,如果到了输入流的末尾,返回-1
3.int read(byte[] b, int off, int len):从输入流读取若干个字节,把这些字节保存到数组b中,off指的是字节数组中开始保存数据的起始下标,len是指读取的字节数目,返回的是读取到的字节数,如果到了输入流的末尾,返回-1
4.void close():关闭输入流
5.int available():可以从输入流中读取的字节数目
子类FileInputStream常用的构造方法:
FileInputStream(File file)
FileInputStream(String name)
使用FileInputStream读取文本文件:
实现步骤:引入相关的类
构造文件输入流FileInputStream对象
读取文本文件的数据
关闭文件流对象
采用int read():
public class InputStreamDemo {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("d:/denglong/hello.txt");
System.out.println("可以读到的字节数:"+fis.available());
int data = 0;
while((data=fis.read())!=-1){
System.out.print((char)data);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if (fis!=null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
采用int read(byte[] b)
public class InputStreamDemo2 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("d:/denglong/hello.txt");
byte[] b = new byte[1024];
int num = 0;
while((num=fis.read(b))!=-1){
for (int i = 0; i < num; i++) {
System.out.print((char)b[i]);
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
—————————————————————————————————————
OutputStream类常用方法:
1.void write(int c)
2.void write(byte[] buf)
3.void write(byte[] b,int off,int len)
4.void close()
5.void flush():强制将缓冲区清空
子类FileOutputStream常用的构造方法
FileOutputStream(File file):写内容会把文件原有内容覆盖掉
FileOutputStream(String name):写内容会把文件原有内容覆盖掉
FileOutputStream(String name,boolean append):可以指定一个布尔类型的变量,如果指定为true,可以在后面追加文件内容
public class OutputStreamDemo {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream("d:/denglong/hello.txt", true);
String str = "好好学习,天天向上";
byte[] b = str.getBytes();
fos.write(b,0,b.length);
System.out.println("成功!");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
—————————————————————————————————————
字符流
Reader 字符输入流(读) 抽象类
Writer 字符输出流(写) 抽象类
Reader类:
Reader类常用方法:
1.int read()
2.int read(char[] c)
3.read(char[] c,int off,int len)
4.void close()
子类InputStreamReader常用构造方法:
InputStreamReader(InputStream in)
InputStreamReader(InputStream in,String charsetName)
FileReader类是InputStreamReader的子类
FileReader常用构造方法:
FileReader(File file)
FileReader(String name)
常见问题:中文乱码
原因:文件编码格式和程序环境的编码格式不一致
解决方案:字符流去读的时候,指定字符流的编码格式
FileReader 无法指定编码格式,会按照文件系统默认编码格式读
(Syetem.out.println(System.getProperty(“file.encoding”));//查看系统默认编码格式)
所以使用InputStreamReader
缓冲流
BufferedReader类:BufferedReader类是Reader类的子类,BufferedReader带有缓冲区,按行读取内容的readLine()方法
FileReader:
InputStreamReader
InputStreamReader可以指定字符编码格式
BufferedReader 缓冲流
—————————————————————————————————————
Writer类
Writer类常用方法:
1.write(String str)
2.write(String str, int off, int len)
3.void colse()
4.void flush()
子类:OutputStreamWriter常用的构造方法:
OutputStreamWriter(OutputStream out)
OutputStreamWriter(OutputStream out,String charsetName)
FileWriter类是OutputStreamWriter的子类
FileWriter常用构造方法:以下两种构造方法都可以重载,制定一个boolean类型的参数,用来指定是用来追加还是覆盖这个文件内容
FileWriter(File file)
FileWriter(String name)
缓冲流
BufferedWriter类: BufferedWriter类是Writer类的子类,BufferWriter带有缓冲区
常用构造方法:
BufferedWriter(Writer out)
FileWriter
OutputStreamWriter
BufferedWriter
public class BufferedWriterDemo {
public static void main(String[] args) {
FileOutputStream fos = null;
OutputStreamWriter ops = null;
BufferedWriter bw = null;
try {
fos = new FileOutputStream("d:/denglong/hello.txt",true);
ops = new OutputStreamWriter(fos,"utf-8");
bw = new BufferedWriter(ops);
String line = "嘿嘿";
bw.write(line);//换行输出
bw.newLine();
bw.write("嘻嘻");
System.out.println("输出成功");
ops.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
bw.close();
ops.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class PetDemo {
public static void main(String[] args) {
FileReader fr = null;
BufferedReader br = null;
File file = null;
FileWriter fw = null;
BufferedWriter bw = null;
try {
//读取pet.template信息
fr = new FileReader("d:/hello/pet.template");
br = new BufferedReader(fr);
StringBuffer sb = new StringBuffer();
String line = null;
while((line = br.readLine())!=null){
sb.append(line);
}
System.out.println("替换前:"+sb);
//用String的replace方法进行替换
String newStr = sb.toString().replace("{name}", "欧欧").replace("{type}", "狗狗").replace("{master}", "李伟");
//将替换后的新内容写入新文件
fw = new FileWriter("d:/denglong/pet.txt");
bw = new BufferedWriter(fw);
bw.write(newStr);
System.out.println("替换后:"+newStr);
bw.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
bw.close();
fw.close();
br.close();
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
—————————————————————————————————————
读写二进制文件
读写二进制文件常用的类有DataInputStream和DataOutputStream。
DataInputStream类:FileInputStream的子类,与FileInputStream类结合使用读取二进制文件
DataOutputStream类:FileOutputStream的子类,与FileOutputStream类结合使用写二进制文件
例:复制图片
public class TestDemo {
public static void main(String[] args) {
FileInputStream fis = null;
DataInputStream dis = null;
FileOutputStream fos = null;
DataOutputStream dos = null;
try {
//读
fis = new FileInputStream("d:/hello/1.jpg");
dis = new DataInputStream(fis);
//写
fos = new FileOutputStream("d:/denglong/2.jpg");
dos = new DataOutputStream(fos);
int temp;
while((temp = dis.read())!=-1){
dos.write(temp);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
dos.close();
fos.close();
dis.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
—————————————————————————————————————
重定向标准I/O
在Java中还有两个非常重要的流,即System.in和System.out,它们是Java提供的两个标准输入/输出流,主要用于向键盘接收数据以及向屏幕输出数据。
System.in常见方法:
int read(),此方法从键盘接收一个字节的数据,返回值是该字符的ASCII码。
int read(byte[] buf),此方法从键盘接收多个字节的数据,保存至buf中,返回值是接收字节数据的个数,非ASCII码。
System.out常见方法:
print(),向屏幕输出数据,不换行,参数可以是Java的任意数据类型。
println(),向屏幕输出数据,换行,参数可以是Java的任意数据类型。
详细查资料。
—————————————————————————————————————
序列化和反序列化
什么是序列化
序列化就是将对象的状态存储到特定存储介质中的过程,也就是将对象状态转换为可保持或可传输的格式的过程。在序列化过程中,会将对象的公有成员、私有成员包括类名,转换为字节流,然后再把字节流写入数据流,存储到存储介质中,这里说的存储介质通常指的是文件。
使用序列化的意义在于将Java对象序列化后,可以将其转换为字节序列,这些字节序列可以被保存在磁盘上,也可以借助网络进行传输,同时序列化后的对象保存的是二进制状态,这样实现了平台无关性。即可以将在Windows操作系统中实现序列化的一个对象,传输到UNIX操作系统的机器上,再通过反序列化后得到相同的对象,而无需担心数据因平台问题显示异常。
序列化:序列化是将对象的状态写入到特定的流中的过程。
反序列化:反序列化则是从特定的流中获取数据重新构建对象的过程。
—————————————————————————————————————
实现序列化
ObjectInputStream:反序列化
ObjectOutputStream:序列化
序列化:Java中只有实现了java.io.Serializable接口的类的对象才能被序列化,Serializable表示可串行的,可序列化的,所以,对象序列化有时也被称为串行化。JDK类库中有些类,如String类、包装类和Date类等都实现了Serializable接口。
对象序列化的步骤很简单,可以概括成如下两大步:
(1):创建一个对象输出流(ObjectOutputStream),它可以包装一个其他类型的输出流,如文件输出流FileOutputStream。
(2):通过对象输出流的writeObject()方法写对象,也就是输出可序列化对象。
—————————————————————————————————————
反序列化:顾名思义就是与序列化相反,序列化是将对象的状态信息保存到存储介质中,反序列化则是从特定存储介质中读取数据并重新构建成对象的过程。通过反序列化,可以将存储在文件上的对象信息读取出来,然后重新构建为对象。这样就不需要再讲文件上的信息一一读取、分析在组织为对象。
反序列化的步骤大致概括为以下两步:
(1):创建一个对象输入流(ObjectInputStream),它可以包装一个其他类型的输入流,如文件输入流FileInputStream。
(2):通过对象输入流的readObject()方法读取对象,该方法返回一个Object类型的对象,如果程序知道该Java对象的类型,则可以将该对象强制转换成其真是的类型。
序列化和反序列化:
public class Test {
public static void main(String[] args) {
Student stu1 = new Student("小红",18,"女","123456");
FileOutputStream fos = null;
ObjectOutputStream oos = null;
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
//序列化
fos = new FileOutputStream("d:/denglong/denglong.txt");
oos = new ObjectOutputStream(fos);
oos.writeObject(stu1);
//反序列化
fis = new FileInputStream("d:/denglong/denglong.txt");
ois = new ObjectInputStream(fis);
Student stu = (Student)ois.readObject();
System.out.println(stu.getName()+"-"+stu.getAge()+"-"+stu.getGender()+"-"+stu.getPassword());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally{
try {
ois.close();
fis.close();
oos.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
注意:
①:如果向文件中使用序列化机制写入多个对象,那么反序列化恢复对象时,必须按照写入的顺序读取。
②:如果一个可序列化的类,有多个父类(包括直接或间接父类),则这些父类要么是可序列化的,要么有无参的构造器;否则会抛出异常。
屏蔽敏感字段transient:
通常,对象中的所有属性都会被序列化,但是对于一些比较敏感的信息,如用户的密码,一旦序列化后,人们完全可以通过读取文件或拦截网络传输数据的方式获得这些信息。因此,出于对安全的考虑,某些属性应限制被序列化。解决这个办法是使用transient来修饰。
来源:https://blog.csdn.net/weixin_43857032/article/details/86479200