Java中IO是以流为基础进行输入输出的,所有数据被串行化写入输出流,或者从输入流读入。
File类
File类是java.io包中很重要的一个类;
File类的对象可以表示文件,还可以表示目录,在程序中一个File类对象可以代表一个文件或目录;一个Flie类的对象可以表示一个具体的文件或目录
File对象可以对文件或目录的属性进行操作,如:文件名、最后修改日期、文件大小等;
File对象无法操作文件的具体数据,即不能直接对文件进行读/写操作
File构造方法
public class FileDemo2 {
public static void main(String[] args) {
File file=new File("D:\\浏览器\\Demo.txt");
File file1=new File("Demo2.txt");
File file2=new File("D:\\JAVA");
}
}
这里便是File的构造方法的三种用法。
第一行 File file=new File("D:\\浏览器\\Demo.txt"); 指定的是相应的文件路径以及文件名。这里的路径指的是绝对路径 即完整的路径。
第二行 File file1=new File("Demo2.txt"); 指在当前目录下创建一个与Demo2.txt文件名相关的文件对象。
第三行File file2=new File("D:\\JAVA"); 指明详细的路径和目录名。
File类常用方法
public static void main(String[] args) {
File file=new File("D:\\浏览器\\Demo.txt");
File file1=new File("Demo2.txt");
File file2=new File("D:\\JAVA");
System.out.println(file1.getAbsolutePath());//绝对路径 像我们不知道file1的位置的话就可以使用此方法查到绝对路径
System.out.println(file.getParent());//父级
System.out.println(file.lastModified());//文件最后修改时间
System.out.println(file.length());//获取文件内容长度(以字节为单位)
System.out.println(file.canRead());//判断文件是否可读
System.out.println(file.canWrite());//判断文件是否可写
System.out.println(file.exists());//判断文件是否存储
System.out.println(file.isFile());//判断表示的是否是文件
System.out.println(file.isDirectory());//判断表示的是否是目录
System.out.println(file.delete()); //删除文件 删除成功返回ture,反之返回false
File f=new File("D:\\Demo");
f.mkdir(); //创建单级目录
File f1=new File("D:\\Demo1\\Demo2");
f1.mkdirs(); //创建多级目录
}
public static void main(String[] args) {
File file=new File("D:\\JAVA");
String[] s=file.list();
for(String name: s)
System.out.println(name);
}
list()方法是 获得某个目录中所有的子级目录/文件 返回文件名(String)
我们再使用增强for循环将得到的文件名遍历出来。
File file1=new File("D:\\JAVA");
File []s1=file1.listFiles();
for(File f:s1){
System.out.println(f);
}
listFile()方法是获得某个目录中所有的子级目录/文件 返回文件对象(File)
当然你也可以使用delete()方法区删除文件,不过这可正儿八经的删除的是你的文件,建议留心点。
输入及输出的概念
输入输出(I/O) 把电脑硬盘上的数据读到程序中,称为输入,即input,进行数据的read 操作从程序往外部设备写数据,称为输出,即output,进行数据的write操作。
输入流与输出流
流即是读写操作的类。
流按着数据的传输方向分为:
输入流:往程序中读叫输入流。
输出流:从程序中往外写叫输出流。
类按照操作不同,单位不同分为:
字节流:每次读取数据时,是以字节为单位,一次读一个字节(可以读取任何文件)
字节输入流 FileInputStream
字节输出流 FileOutputStream
字符流:每次读取数据时,是以字符为单位,一次第一个字符(只适合文本文件)
字符输入流 FileReader
字符输出流 FileWriter
InputStream的基本方法:
读取一个字节并以整数的形式返回(0~255),如果返回-1已到输入流的末尾。
int read() throws IOException
读取一系列字节并存储到一个数组buffer, 返回实际读取的字节数,如果读取前已到输入流的末尾返回-1
int read(byte[] buffer) throws IOException
关闭流释放内存资源
void close() throws IOException
OutputStream的基本方法
向输出流中写入一个字节数据,该字节数据为参数b的低8位
void write(int b) throws IOException
将一个字节类型的数组中的从指定位置(off)开始的 len个字节写入到输出流
void write(byte[] b, int off, int len) throws IOException
关闭流释放内存资源
void close() throws IOException
public static void main(String[] args) throws IOException {
FileInputStream in=new FileInputStream("D:\\game1\\Demo.txt");
FileOutputStream out=new FileOutputStream("D:\\game1\\Demo2.txt");
int b=0;
while ((b=in.read())!=-1){
out.write(b);
}
in.close();
out.close();
}
这里提几点
1. 要读取的文件必须要存在,而输出的文件会自己创建。
2. 因为当返回为read 返回-1时就代表读取完毕了。所以我们while循环中条件是当读取不为-1时。
int的b值是充当一个承载事物的对象。
3.完事记得close关闭流释放资源。
下面是一个规范的格式写输出输出。因为我们在写代码中会碰到异常,需要try catch语句环绕处理异常。平常演示图个方便所以就直接抛出,规范写法应该如下。
public static void main(String[] args) throws IOException {
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("E:/demo1.txt");
out = new FileOutputStream("E:/demo2.txt");
// in.read(); 一次读一个字节,返回字节编码(int), 文件内容读完后返回-1.
int b = 0;
while((b = in.read())!=-1){
out.write(b);
//write(int b); 一次向目标文件写出一个字节
}
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println(e.getMessage());//日志记录
}finally {
if(in!=null){
in.close();//关闭流通道 释放文件
}
if(out!=null){
out.close();
}
}
}
下面介绍另一种读取方案,因为read读取是一个字节一个字节慢慢读取的,所以我们定义一个数组,使数组被读取装满后再去输出,这样降低了传输次数加大了传输效率。
public static void main(String[] args) throws IOException {
long l=new Date().getTime();
FileInputStream in=new FileInputStream("D:\\game1\\Demo.txt");
FileOutputStream out=new FileOutputStream("D:\\game1\\Demo3.txt");
int b=0;
while ((b=in.read())!=-1){
out.write(b);
}
long l2=Calendar.getInstance().getTimeInMillis();
System.out.println(l2-l);
in.close();
out.close();
System.out.println("----------------------------------------------");
long l3=new Date().getTime();
FileInputStream in2=new FileInputStream("D:\\game1\\Demo.txt");
FileOutputStream out2=new FileOutputStream("D:\\game1\\Demo4.txt");
byte[] bytes=new byte[100];
int size=0;
while ((size=in2.read(bytes))!=-1){
out2.write(bytes,0,size);
}
long l4=Calendar.getInstance().getTimeInMillis();
System.out.println(l4-l3);
in2.close();
out2.close();
}
分界线上方是普通传输方式,分界线下方是使用数组传输的方式。
我们专门使用了Date类来获取时间差,来计算两种方式的工作效率。
最后可以看到,使用数组传输效率明显大于普通传输方式。
字符流
字符流的使用方法与字节流大同小异,不过处理原理上不一样。字节流是按照一个字节一个字节读写的。而字符流失按照一个字符一个字符读写的。为什么说字符流适合处理纯文本文件,因为一个文字占有三字节,而一个字母只占有一字节。若使用字节流如果传输过程没有写好,可能会将文本的字节没有写完就结束,这样会使读写出错。
FileReader reader=new FileReader("D:\\game1\\中文.txt");
FileWriter writer=new FileWriter("D:\\game1\\中文Demo.txt");
char [] b=new char[10];
int size=0;
while ((size=reader.read(b))!=-1){
writer.write(b,0,size);
}
reader.close();
writer.close();
节点流和处理流
根据封装类型不同流又分为:
节点流:
如果流封装的是某种特定的数据源,如文件、字符串、字符串数组等,则称为节点流。亦即上面提到的字节流与字符流。
处理流:
如果流封装的是其它流对象,称为处理流。 处理流提供了缓冲功能,提高读写效率,同时增加了一些新的方法。
缓冲字节输出流 BufferedOutputStream
缓冲字节输入流 BufferedInputStream
缓冲字符输入流 BufferedReader
缓冲字符输出流 BufferedWriter
处理流
缓冲处理流内置有一个缓冲数组,更有效率的完成读写任务。自己可以定义一个数组大小,在read时,如果自定义的数组不大于底层固定数组时,会把读到的数据缓冲到数组中去,将数组读满之后写出去。write时也并不是每一次都往磁盘中去写,等待内部缓存数组写满时,再向磁盘输出。
FileReader reader=new FileReader("D:\\game1\\中文.txt");
FileWriter writer=new FileWriter("D:\\game1\\中文Demo22.txt",true);
BufferedReader bufferedReader=new BufferedReader(reader);
BufferedWriter bufferedWriter=new BufferedWriter(writer);
String line="";
int n=0;
while ((line=bufferedReader.readLine())!=null){
bufferedWriter.write(line);
bufferedWriter.newLine();
}
bufferedWriter.flush();
bufferedReader.close();
bufferedWriter.close();
简单来说处理流就是充当了上面我们自己定义的数组的角色。
以上代码还有几点要讲
1.在FileWriter writer=new FileWriter("D:\\game1\\中文Demo22.txt",true);中,文件路径的后面跟了一个ture。这里的作用是写入数据时,保留之前的,向后面追加内容。
2.我们可以看到这串代码是一行一行字符读取的。使用的便是readLine()方法。
3.newLine()就相当于换行
4.最后别忘了flush()刷新缓冲区,以及close()输入输出流。
Print流
Print 打印流:
只做输出没有输入 打印流分为字节打印流和字符打印流
PrintWriter:
字符打印流 print方法可以打印各种类型数据
在javaweb项目中,服务器端向客户端响应数据以打印流的方式响应.
public static void main(String[] args) throws FileNotFoundException {
PrintWriter pw=new PrintWriter("D:\\game1\\Demo.html");
pw.print("<h1>你好客户端</h1>");
pw.print("<h1>你好客户端</h1>");
pw.print("<h1>你好客户端</h1>");
pw.print("<h1>你好客户端</h1>");
pw.close();
}
对象输入输出流--对象序列化
对象存储在内存中,程序终止运行,内存数据清空,
有时需要保存对象中的数据,需要用流对象,将对象数据输出到文件中,做到对象数据的持久化(长久保存)
因此便有了 ObjectInputStream ObjectOutputStream 都属于处理流
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileOutputStream fout= new FileOutputStream("D:/game1/obj.txt");
ObjectOutputStream out = new ObjectOutputStream(fout);
Date date = new Date();
String s = "abc";
out.writeObject(date);
out.writeObject(s);
out.flush();
out.close();
//反序列化 会创建对象
FileInputStream in = new FileInputStream("D:/game1/obj.txt");
ObjectInputStream oin = new ObjectInputStream(in);
Date date2 = (Date)oin.readObject();
String s2 = (String)oin.readObject();
System.out.println(date2);
System.out.println(s2);
}
1. 对象的输出流将指定的对象写入到文件的过程,就是将对象序列化的过程.
2. 对象 的输入流将指定序列化好的文件读出来的过程,就是对象反序列化的过程。
3. 对象的输出流将对象写入到文件中称之为对象的序列化,所以被序列化对象的类必须要实现 Serializable接口。 Serializable接口中没有任何方法。当一个类声明实现Serializable接口后, 表明该类可被序列化。在类中可以生成一个编号
private static final long serialVersionUID = -5974713180104013488L;
4. 随机生成 唯一的 serialVersionUID 用来表明实现序列化类的不同版本间的兼容性。某个类在与之对应的对象已经序列化出去后做了修改,该对象依然可以被正确反序列化.
5. 如果不显示生成序列号,那么将会隐式生产,但是隐式生成后,类一旦发生改变,序列号也会随之改变.
FileOutputStream fileOutputStream=new FileOutputStream("D:/game1/CarDemo.txt");
ObjectOutputStream objectOutputStream=new ObjectOutputStream(fileOutputStream);
Car car=new Car(999,"玛莎拉蒂");
objectOutputStream.writeObject(car);
objectOutputStream.flush();
objectOutputStream.close();
FileInputStream fileInputStream=new FileInputStream("D:/game1/CarDemo.txt");
ObjectInputStream objectInputStream=new ObjectInputStream(fileInputStream);
Car car1=(Car)objectInputStream.readObject();
System.out.println(car1);
public class Car implements Serializable {
private static final long serialVersionUID = -9055250986688943401L;
private int num;
private String name;
public Car(int num, String name) {
this.num = num;
this.name = name;
}
@Override
public String toString() {
return "Car{" +
"num=" + num +
", name='" + name + '\'' +
'}';
}
}
如果需要将某个类的对象进行序列化,那么此类必须实现Serializable接口。
会为类生成一个版本号,如果不显得定义版本号,会自定默认生成,会有一个问题,每次当类改变了,版本号会重新生成
图中的private static final long serialVersionUID = -9055250986688943401L;正是序列化版本号 显示的定义之后,类信息发生改变不会重新生成。
另外如果某个类属性我们不想存入序列化对象当中,可以使用transient修饰的属性在序列式,不会保存到文件中。