I : Input
O: Output
通过IO可以完成硬盘文件的读和写。
有多种分类方式:
1)一种方式是按照流的方向进行分类:
以内存作为参照物,
往内存中去,叫做输入。或者叫做读(Read)。
从内存中出来,叫做输出。或者叫做写(Write)。
2)另一种方式是按照读取数据方式不同进行分类:
有的流是按照字节的方式读取数据,一次读取1个字节byte等同于一次读取8个二进制位。
这种流是万能的,什么类型的文件都可以读取。包括文本文件、图片、声音文件、视频文件等等…
假设文件file1.txt,采用字符流的话是这样读的:
a中国bc张三fe
第一次读:1个字节,正好读到’a’
第二次读:1个字节,正好读到’中’字符的一半
第三次都:1个字节,正好读到’中’字符的另外一半
有的流是按照字符的方式读取数据,一次读取一个字符,这种流是为了方便读取普通文本文件存在的。这种流不能读取:图片、声音、视频等文件,只能读取纯文本文件,连word文件都无法读取。
假设文件file1.txt,采用字符流的话是这样读的:
a中国bc张三fe
第一次读: 'a’字符('a’字符在windows系统中占用1个字节)
第二次读: '中’字符('中’字符在windows系统中占用2个字节)
综上所述: 流的分类
输入流、输出流(按照流的方向进行分类)
字节流、字符流(按照读取的方式进行分类)
java中的IO流都已经写好了,我们程序员不需要关心,我们最主要还是掌握,在java中已经提供了哪些流,每个流的特点是什么,每个流对象上的常用方法有哪些?
java中所有的流都是在: java.io.*下。
java中主要还是研究:
怎么new流对象。
调用流对象的哪个方法是读,哪个方法是写。
java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流
java.io.Reader 字符输入流
java.io.Writer 字符输出流
注意: 在java中只要"类名"以Stream结尾的都是字节流。以"Reader/Writer"结尾的都是字符流。
文件专属:
java.io.FileInputStream
java.io.FileOutputStream
java.io.FileReader
java.io.FileWriter
转换流:(将字节流转换成字符流)
java.io.InputStreamReader
java.io.OutputStreamWriter
缓冲流专属:
java.io.BufferedReader
java.io.BufferedWriter
java.io.BufferedInputStream
java.io.BufferedOutputStream
数据流专属:
java.io.DateInputStream
java.io.DateOutputStream
标准输出流:
java.io.printWriter
java.io.printStream
对象专属流:
java.io.ObjectInputStream
java.io.ObjectOutputStream
package com.jmpower.javase.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
java.io.FileInputStream:
1.文件字节输入流,万能的,任何类型的文件都可以采用这个流来读。
2.字节的方式,完成输入的操作,完成读的操作。(硬盘-->内存)
*/
public class FileInputStreamTest01 {
public static void main(String[] args) {
FileInputStream fis=null;
try {
//创建字节输入流对象
//文件路径:C:\Users\Jm\Desktop\Java\doSome.txt
//文件内容为:abcedf
//FileInputStream fis=new FileInputStream("C:\\Users\\Jm\\Desktop\\Java\\doSome.txt");
//都采用了: 绝对路径
//将"\\"写成"/"也是可以的
fis=new FileInputStream("C:/Users/Jm/Desktop/Java/doSome.txt");
int readDate=fis.read();//这个方法的返回值是读到字节本身
System.out.println(readDate);// 97
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//在finally语句块中确保流一定要关闭
if (fis != null) {
//关闭流的前提是:流不是空。流是null的时候没必要关闭。
try {
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
package com.jmpower.javase.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
int read(byte[] b)
一次最多读取b.length个字节。
减少硬盘和内存之间的交互,提高程序的执行效率。
往byte[]数组当中读。
*/
public class FileInputStreamTest02 {
public static void main(String[] args) {
FileInputStream fis=null;
try {
//相对路径的话呢?相对路径一定是从当前所在的位置作为起点开始找!
//IDEA默认的当前路径是工程Object的根就是IDEA的默认当前路径。
//文件内容:abcdef
fis=new FileInputStream("text02.txt");
byte[] bytes=new byte[4];//准备一个4个长度的byte数组,一次最多读取4个字节。
//这个方法的返回值是,读取到的字节数量。(不是字节本身)
//int readCount=fis.read(bytes);
//System.out.println(readCount);// 第一次读到4个字节
//将字节数组全部转换成字符串
//System.out.println(new String(bytes));
//不应该全部转换,应该读取了多少个字节,转换多少个。
//System.out.println(new String(bytes,0,readCount));// abcd
//最终版本,读取文件
int readCount=0;
while((readCount = fis.read(bytes))!=-1)
{
System.out.print(new String(bytes,0,readCount));
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
1)int available(): 返回流当中剩余的没有读取到的字节的数量
package com.jmpower.javase.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest03 {
public static void main(String[] args) {
FileInputStream fis=null;
try {
fis=new FileInputStream("test03");
System.out.println("总字节数量:"+fis.available());// 41
//读一个字节
//int readDate=fis.read();
//还剩下的可读字节为40
//System.out.println("还剩下的可读字节的数量:"+fis.available());// 40
//这个方法有什么用?
byte[] bytes=new byte[fis.available()];
//不需要循环了!
//直接读一次就行了。
int readCount=fis.read(bytes);// 6
System.out.println(new String(bytes)); // abcdef
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
2)long stip(long n): 跳过几个字节不读
package com.jmpower.javase.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest04 {
public static void main(String[] args) {
FileInputStream fis=null;
try {
fis=new FileInputStream("text02.txt");
//a b c d e f
//97 98 99 100 101 102
System.out.println(fis.read());// 97
fis.skip(3);//跳过三个字节
System.out.println(fis.read());// 101
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
package com.jmpower.javase.io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamTest01 {
public static void main(String[] args) {
FileOutputStream fos=null;
try {
//file01文件不存在会自动新建!
//这种方式慎用,这种方式会先将文件清空,然后重新写入。
//fos=new FileOutputStream("file01");
//以追加的方式在文件末尾写入。不会清空原文件的内容。
fos=new FileOutputStream("text02.txt",true);
//开始写
byte[] bytes={97,98,99,100};
//将byte数组全部写出!
fos.write(bytes);// abcd
//将bute数组的一部分写出!
fos.write(bytes,0,2);// ab
//字符串
String s="我是中国人!";
//将字符串转换成数组
byte[] bs=s.getBytes();
//写
fos.write(bs);
//写完之后一定要刷新
fos.flush();
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
package com.jmpower.javase.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
使用FileInputStream + FileOutputStream完成文件的拷贝。
拷贝的过程一定是一边读,一边写。
使用以上的字节流拷贝文件的时候,文件类型随意,万能的。什么样的文件都可以拷贝。
*/
public class Copy01 {
public static void main(String[] args) {
FileInputStream fis=null;
FileOutputStream fos=null;
try {
//创建一个输入流对象
fis=new FileInputStream("C:\\Users\\Jm\\Pictures\\自建\\十三届蓝桥杯省一.jpg");
//创建一个输出流对象
fos=new FileOutputStream("C:\\Users\\Jm\\Desktop\\Java\\十三届蓝桥杯省一.jpg");
//最核心的: 一边读,一边写
byte[] bytes=new byte[1024*1024];//1MB(一次最多拷贝1MB)
int readCount=0;
while((readCount=fis.read(bytes))!=-1)
{
fos.write(bytes,0,readCount);
}
//刷新,输出流最后要刷新
fos.flush();
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//分开try,不要一起try
//一起try的时候,其中一个出现异常,可能会影响到另一个流的关闭
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
package com.jmpower.javase.io;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/*
FileReader:
读取文本内容时,比较方便,快捷。
一次读取一个字符。
*/
public class FileReaderTest {
public static void main(String[] args) {
FileReader reader=null;
try {
//创建文件字符输入流
reader=new FileReader("text02.txt");
//开始读
char[] chars=new char[4];//一次读取4个字符(1个字符2个字节)
int readCount=0;
while((readCount=reader.read(chars))!=-1)
{
System.out.print(new String(chars,0,readCount));
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
package com.jmpower.javase.io;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Copy02 {
public static void main(String[] args) {
FileReader in=null;
FileWriter out=null;
try {
//读
in=new FileReader("src/com/jmpower/javase/io/FileInputStreamTest02.java");
//写
out=new FileWriter("reader");
//一边读一边写
char[] chars=new char[1024*512];//1MB
int readCount=0;
while((readCount=in.read(chars))!=-1)
{
out.write(new String(chars,0,readCount));
}
//刷新
out.flush();
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//关闭流
if (in != null) {
try {
in.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
package com.jmpower.javase.io;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/*
BufferedReader:
带有缓冲区的字符输入流。
使用这个流的时候不需要自定义char数组,或者说不需要自定义byte数组。自带缓冲。
*/
public class BufferedReaderTest01 {
public static void main(String[] args) {
FileReader reader=null;
BufferedReader br=null;
try {
reader=new FileReader("src/com/jmpower/javase/io/Copy02.java");
//当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做: 节点流。
//外部负责包装的这个流,叫做: 包装流,还有一个名字叫做: 处理流。
//像当前这个程序来说: FileReader叫做一个节点流。BufferedReader就是包装流/处理流。
br=new BufferedReader(reader);
/*//第一行
String firstLine=br.readLine();
System.out.println(firstLine);
//第二行
String secondLine=br.readLine();
System.out.println(secondLine);*/
//br.readLine()方法读取一个文本行,但不带换行符。
String line=null;
while((line=br.readLine())!=null)
{
System.out.println(line);
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//关闭流
//对于包装流来说,只需要关闭最外层流就行,里面的节点流会自动关闭。(可以看源代码)
if (br != null) {
try {
br.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
package com.jmpower.javase.io;
import java.io.*;
public class BufferedReaderTest02 {
public static void main(String[] args) {
BufferedReader br=null;
try {
//字节流
FileInputStream in=new FileInputStream("src/com/jmpower/javase/io/Copy02.java");
//通过转换流转换(InputStreamReader将字节流转换成字符流)
//in是节点流。reader是包装流。
InputStreamReader reader=new InputStreamReader(in);
//这个构造方法只能传一个字符流。不能传字节流。
//reader是节点流。br是包装流。
br=new BufferedReader(reader);
String line=null;
while((line=br.readLine())!=null)
{
System.out.println(line);
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//关闭最外层
try {
br.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
package com.jmpower.javase.io;
import java.io.*;
/*
BufferedWriter: 带有缓冲的字符输出流
OutputStreamWriter: 转换流
*/
public class BufferedWriterTest01 {
public static void main(String[] args) {
BufferedWriter out=null;
try {
//带有缓冲的字符输出流
//out=new BufferedWriter(new FileWriter("bo"));
out=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("doSome"/*,true*/)));
//开始写
out.write("123");
out.write("\n");
out.write("456");
//刷新(输出流记得刷新)
out.flush();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
1)写
package com.jmpower.javase.io;
import java.io.*;
/*
java.io.DataOutputStream:数据专属的流
这个流可以将数据连通数据的类型一并写入文件。
注意: 这个文件不是普通文本文档。(这个文件使用记事本打不开)
*/
public class DataOutputStreamTest01 {
public static void main(String[] args) {
DataOutputStream dos=null;
try {
//创建数据专属的字节输出流
dos=new DataOutputStream(new FileOutputStream("data"));
//写数据
byte b=100;
short s=400;
int i=200;
long l=300;
float f=3.0f;
double d=3.14;
boolean sex=false;
char c='0';
//写
dos.writeByte(b);
dos.writeShort(s);
dos.writeInt(i);
dos.writeLong(l);
dos.writeFloat(f);
dos.writeDouble(d);
dos.writeBoolean(sex);
dos.writeChar(c);
//刷新
dos.flush();
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (dos != null) {
try {
dos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
2)读
package com.jmpower.javase.io;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
java.io.DataInputStream: 数据字节输入流
DataOutputStream写的文件,只能使用DataInputStream去读。并且读的时候你需要提前知道写入的顺序。
读的顺序需要和谐的顺序一致。才可以正常读出数据。
*/
public class DataInputStreamTest01 {
public static void main(String[] args) {
DataInputStream dis=null;
try {
dis=new DataInputStream(new FileInputStream("data"));
//开始读
byte b=dis.readByte();
short s=dis.readShort();
int i=dis.readInt();
long l=dis.readLong();
float f= dis.readFloat();
double d= dis.readDouble();
boolean sex= dis.readBoolean();
char c= dis.readChar();
System.out.println(b);
System.out.println(s);
System.out.println(i);
System.out.println(l);
System.out.println(f);
System.out.println(d);
System.out.println(sex);
System.out.println(c);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (dis != null) {
try {
dis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
package com.jmpower.javase.io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
/*
java.io.PrintStream: 标准的字节输出流。默认输出到控制台。
*/
public class PrintStreamTest {
public static void main(String[] args) {
//联合起来写
System.out.println("hello world!");
//分开写
java.io.PrintStream ps=System.out;
ps.println("hello zhangsan");
ps.println("hello lisi");
ps.println("hello wangwu");
//标准输出流不需要手动colse()关闭。
//可以改变标准输出流的输出方向吗?可以
try {
//标准输出流不再指向控制台,指向“log”文件
PrintStream printStream=new PrintStream(new FileOutputStream("log"));
//修改输出方向,将输出方向修改到"log"文件
System.setOut(printStream);
//再输出,输入到了"log"文件中
System.out.println("hello lisi");
System.out.println("hello zhangsan");
System.out.println("hello wangwu");
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
}
1)
boolean exists() 测试此抽象路径名表示的文件或目录是否存在。
boolean createNewFile() 当且仅当具有该名称的文件尚不存在时,原子地创建一个由该抽象路径名命名的新的空文件。
boolean mkdir() 创建由此抽象路径名命名的目录。
boolean mkdirs() 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录。
String getParent() 返回此抽象路径名的父 null的路径名字符串,如果此路径名未命名为父目录,则返回null。
File getParentFile() 返回此抽象路径名的父,或抽象路径名 null如果此路径名没有指定父目录。
String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串。
String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串。
package com.jmpower.javase.io;
import java.io.File;
import java.io.IOException;
/*
File
1.File类和四大家族没有关系,所以File类不能完成文件的读和写
2.File对象代表什么?
文件和目录路径名的抽象表示方式。
C:\Users\Jm\Desktop\Java 这是一个File对象
C:\Users\Jm\Desktop\Java\学习笔记(Java) 这也是一个File对象
一个File对象有可能对应的目录,也可能是文件
File只是一个路径名的抽象表达形式
3.需要掌握File类中常用的方法
boolean exists() 测试此抽象路径名表示的文件或目录是否存在。
boolean createNewFile() 当且仅当具有该名称的文件尚不存在时,原子地创建一个由该抽象路径名命名的新的空文件。
boolean mkdir() 创建由此抽象路径名命名的目录。
boolean mkdirs() 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录。
String getParent() 返回此抽象路径名的父 null的路径名字符串,如果此路径名未命名为父目录,则返回null。
File getParentFile() 返回此抽象路径名的父,或抽象路径名 null如果此路径名没有指定父目录。
String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串。
String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串。
*/
public class FileTest01 {
public static void main(String[] args) {
//创建一个File对象
File f1=new File("D:\\file");
//判断是否存在
System.out.println(f1.exists()); // false
//如果D:\file不存在,则以文件的形式创建出来
/*if(!f1.exists())
{
try {
//以文件的形式创建出来
f1.createNewFile();
} catch (IOException e) {
throw new RuntimeException(e);
}
}*/
//如果D:\\file不存在,则以目录的形式创建出来
/*if(!f1.exists())
{
//以目录的形式创建出来
f1.mkdir();
}*/
//可以创建多重目录吗?
/*File f2=new File("D:\\a\\b\\c\\d");
if(!f2.exists())
{
f2.mkdirs();
}*/
File f3=new File("C:\\Users\\Jm\\Desktop\\Java\\学习笔记(Java)");
//获取文件的父路径
String fatherPath=f3.getParent();
System.out.println(fatherPath);// C:\Users\Jm\Desktop\Java
File parentFile=f3.getParentFile();
System.out.println("获得绝对路径: "+parentFile.getAbsolutePath());
File f4=new File("doSome");
System.out.println("绝对路径: "+f4.getAbsolutePath());
}
}
2)
String getName() 返回由此抽象路径名表示的文件或目录的名称。
boolean isDirectory() 测试此抽象路径名表示的文件是否为目录。
boolean isFile() 测试此抽象路径名表示的文件是否为普通文件。
long lastModified() 返回此抽象路径名表示的文件上次修改的时间。
long length() 返回由此抽象路径名表示的文件的长度。
package com.jmpower.javase.io;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
File常用方法:
String getName() 返回由此抽象路径名表示的文件或目录的名称。
boolean isDirectory() 测试此抽象路径名表示的文件是否为目录。
boolean isFile() 测试此抽象路径名表示的文件是否为普通文件。
long lastModified() 返回此抽象路径名表示的文件上次修改的时间。
long length() 返回由此抽象路径名表示的文件的长度。
*/
public class FileTest02 {
public static void main(String[] args) {
File f1=new File("C:\\Users\\Jm\\Desktop\\Java\\学习笔记(Java)\\day01.txt");
//获取文件名
System.out.println("文件名: "+f1.getName());
//判断是否是一个目录
System.out.println(f1.isDirectory());// false
//判断是否是一个文件
System.out.println(f1.isFile());// true
//判断文件最后一次修改时时间
long haoMiao=f1.lastModified();
Date time=new Date(haoMiao);
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String strTime=sdf.format(time);
System.out.println(strTime);
//获取文件的大小
System.out.println(f1.length());// 2155字节
}
}
3)
File[] listFiles() 返回一个抽象路径名数组,表示由该抽象路径名表示的目录中的文件。
package com.jmpower.javase.io;
import java.io.File;
/*
File中的lastFiles方法。
File[] listFiles() 返回一个抽象路径名数组,表示由该抽象路径名表示的目录中的文件。
*/
public class FileTest03 {
public static void main(String[] args) {
File f1=new File("C:\\Users\\Jm\\Desktop\\Java\\学习笔记(Java)");
File[] files=f1.listFiles();
//foreach
for(File file:files)
{
//获取绝对路径
//System.out.println(file.getAbsolutePath());
//获取名字
System.out.println(file.getName());
}
}
}
package com.jmpower.javase.io;
import java.io.*;
//程序BUG:拷贝源目录下的第一个文件无法拷贝
public class CopyAll {
public static void main(String[] args) {
//拷贝源
File srcFile=new File("D:\\666\\c语言");
//拷贝目标
File destFile=new File("C:\\Users\\Jm\\Desktop\\Java");
//调用方法拷贝
copyDir(srcFile,destFile);
}
/**
* 拷贝目录
* @param srcFile 拷贝源
* @param destFile 拷贝目标
*/
private static void copyDir(File srcFile,File destFile){
if(srcFile.isFile())
{
//srcFile如果是一个文件的话,递归结束
//是文件需要拷贝
//....一边读一边写
FileInputStream in=null;
FileOutputStream out=null;
try {
//读这个文件
//D:\666\c语言\算法模板\map函数.txt
in=new FileInputStream(srcFile);
//写到这个文件中
//C:\Users\Jm\Desktop\Java\666\c语言\算法模板\map函数.txt
String path=(destFile.getAbsolutePath().endsWith("\\")?destFile.getAbsolutePath():destFile.getAbsolutePath()+"\\")+srcFile.getAbsolutePath().substring(3);
out=new FileOutputStream(path);
//一边读一边写
byte[] bytes=new byte[1024*1024];//1MB
int readCount=0;
while((readCount=in.read(bytes))!=-1)
{
out.write(bytes,0,readCount);
}
//刷新
out.flush();
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (in != null) {
try {
in.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
return ;
}
//获取源下面的子目录
File[] files=srcFile.listFiles();
for(File file:files)
{
if(file.isDirectory())
{
//新建对应目录
//System.out.println(file.getAbsolutePath());
//D:\666\c语言\算法模板 源目录
//C:\Users\Jm\Desktop\Java\666\c语言\算法模板 目标目录
String srcDir=file.getAbsolutePath();
String destDir=(destFile.getAbsolutePath().endsWith("\\")?destFile.getAbsolutePath():destFile.getAbsolutePath()+"\\")+srcDir.substring(3);
File newFile=new File(destDir);
if(!newFile.exists())
{
newFile.mkdirs();
}
}
else {
//保证拷贝目录下第一个是文件时,不会出现FileNotFound的异常
File newFile=new File((destFile.getAbsolutePath().endsWith("\\")?destFile.getAbsolutePath():destFile.getAbsolutePath()+"\\")+srcFile.getAbsolutePath().substring(3));
if(!newFile.exists())
{
newFile.mkdirs();
}
}
//递归调用
copyDir(file,destFile);
}
}
}
序列化: Serialize java对象存储到文件中。将java对象的状态保存下来的过程。
反序列化:DeSerialize 将硬盘上的数据重新恢复到内存当中,恢复成java对象。
1)序列化的实现:
package com.jmpower.javase.io;
import com.jmpower.javase.bean.Student;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
/*
1.参与序列化和反序列化的对象,必须实现Serializable接口。
2.注意: 通过源码发现,Serializable接只是一个标志接口:
public interface Serializable{
}
这个接口当中什么代码都没有。
他只是起到一个标识的作用,标志的作用,java虚拟机看到这个类实现了这个接口,可能会对这个类进行特殊待遇。
3.序列化版本号有什么用呢?
java.io.InvalidClassException:
com.jmpower.javase.bean.Student;
local class incompatible:
stream classdesc serialVersionUID = -6680413217747085692, (10年前)
local class serialVersionUID = -1967551671371379446(10年后)
java语言是采用什么机制来区分类的?
第一,首先通过类名进行比对,如果类名不一样,肯定不是同一个类。
第二,如果类名一样,再根据序列化版本号进行区分。
自动生成序列化版本号的好处:
java虚拟机会自动区分不同人相同的实现了Serializable接口的类,都有默认的序列化版本号,
他们的序列化版本号不一样。所以区分开了。
自动生成序列化版本号的坏处:
这种自动生成的序列化版本号,一旦代码确定之后,不能进行后续的修改,
因为只要修改,必然会重新编译,此时会生成全新的序列化版本号,这个时候
java虚拟机会认为这是一个全新的类。(不利于以后的修改)
*/
public class ObjectOutputStreamTest01 {
public static void main(String[] args) {
//创建java对象
Student s=new Student(1111,"zhansan");
ObjectOutputStream oos=null;
try {
//序列化
oos=new ObjectOutputStream(new FileOutputStream("students"));
//序列化对象
oos.writeObject(s);
//刷新
oos.flush();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
2)反序列化的实现
package com.jmpower.javase.io;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
/*
反序列化
*/
public class ObjectInputStreamTest01 {
public static void main(String[] args) {
ObjectInputStream ois=null;
try {
ois=new ObjectInputStream(new FileInputStream("students"));
//开始反序列化,读
Object obj=ois.readObject();
//反序列化回来是一个学生对象,所以会调用学生对象的toString方法。
System.out.println(obj);
ois.close();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} finally {
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
3)Student类
package com.jmpower.javase.bean;
import java.io.Serializable;
public class Student implements Serializable {
private int age;
private int no;
private String name;
public Student(){}
public Student(int no, String name) {
this.no = no;
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
1)序列化
package com.jmpower.javase.io;
import com.jmpower.javase.bean.User;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
/*
一次序列化多个对象呢?
可以,可以将对象放到集合当中,序列化对象。
提示:
参与序列化的ArrayList集合以及集合中的元素User都需要实现java.io.Serializable接口。
*/
public class ObjectOutputStreamTest02 {
public static void main(String[] args) {
List<User> list=new ArrayList<>();
list.add(new User(1111,"zhangsan"));
list.add(new User(2222,"lisi"));
list.add(new User(3333,"wangwu"));
list.add(new User(4444,"zhaoliu"));
ObjectOutputStream oos=null;
try {
oos=new ObjectOutputStream(new FileOutputStream("users"));
//序列化一个集合,这个集合对象中放了很多其他对象。
oos.writeObject(list);
//刷新
oos.flush();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
2)反序列化
package com.jmpower.javase.io;
import com.jmpower.javase.bean.User;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List;
public class ObjectInputStreamTest02 {
public static void main(String[] args) throws FileNotFoundException {
ObjectInputStream ois=null;
try {
//反序列化
ois=new ObjectInputStream(new FileInputStream("users"));
List<User> list= (List<User>) ois.readObject();
for(User user:list)
{
System.out.println(user);
}
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} finally {
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
变量被transient关键词修饰,不会参与序列化。
package com.jmpower.javase.bean;
import java.io.Serializable;
public class User implements Serializable {
//java虚拟机看到Serializable接口之后,会自动生成一个序列化版本号
//这里没有手动写出来,java虚拟机会默认提供这个序列化版本号。
//建议将序列化版本号手动的写出来。不建议自动生成。
private static final long serialVersionUID=1L;//java虚拟机识别一个类的时候,先看类名,类名一样再看序列化版本号。
private int age;
//若不手动写序列化版本号,过了很久,Student这个类源代码改动了。
//源代码改动之后,需要重新编译,编译之后生成了全新的字节码文件。
//并且class文件再次运行的时候,java虚拟机生成的序列化版本号也会发生相应的改变。
private int no;
//transient关键字表示游离的,不参与序列化。
private transient String name;//name不参与序列化操作!
}
序列化版本号有什么用呢?
java.io.InvalidClassException:
com.jmpower.javase.bean.Student;
local class incompatible:
stream classdesc serialVersionUID = -6680413217747085692,
(10年前)
local class serialVersionUID = -1967551671371379446
(10年后)
java语言是采用什么机制来区分类的?
第一,首先通过类名进行比对,如果类名不一样,肯定不是同一个类。
第二,如果类名一样,再根据序列化版本号进行区分。
自动生成序列化版本号的好处:
java虚拟机会自动区分不同人相同的实现了Serializable接口的类,都有默认的序列化版本号,
他们的序列化版本号不一样。所以区分开了。
自动生成序列化版本号的坏处:
这种自动生成的序列化版本号,一旦代码确定之后,不能进行后续的修改,
因为只要修改,必然会重新编译,此时会生成全新的序列化版本号,这个时候
java虚拟机会认为这是一个全新的类。(不利于以后的修改)
所以我们应该手动生成序列化版本号,以后修改源代码的时候,可以不用重新编译,直接运行,不会使得java虚拟机产生新的类,便于程序的修改。
package com.jmpower.javase.bean;
import java.io.Serializable;
public class User implements Serializable {
//java虚拟机看到Serializable接口之后,会自动生成一个序列化版本号
//这里没有手动写出来,java虚拟机会默认提供这个序列化版本号。
//建议将序列化版本号手动的写出来。不建议自动生成。
private static final long serialVersionUID=1L;//java虚拟机识别一个类的时候,先看类名,类名一样再看序列化版本号。
private int age;
//若不手动写序列化版本号,过了很久,Student这个类源代码改动了。
//源代码改动之后,需要重新编译,编译之后生成了全新的字节码文件。
//并且class文件再次运行的时候,java虚拟机生成的序列化版本号也会发生相应的改变。
private int no;
//transient关键字表示游离的,不参与序列化。
private transient String name;//name不参与序列化操作!
}
package com.jmpower.javase.io;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/*
IO+Properties的联合应用:
非常好的一个设计理念:
以后经常改变的数据,可以单独写到一个文件中,使用程序动态读取。
将来只需要修改这个文件的内容,java代码不需要改动,不需要重新编译,
服务器也不需要重启。就可以拿到动态的信息。
类似于以上机制的这种文件被称为配置文件。
并且当配置文件中的内容格式是:
key1=value
key2=value
的时候,我们把这种配置文件叫做属性配置文件。
java规范中要求: 属性配置文件建议以.properties结尾,但这不是必须的。
这种以.properties结尾的文件在java中称为: 属性配属文件。
其中Properties是专门存放属性配置文件内容的一个类。
*/
public class IopropertiesTest01 {
public static void main(String[] args) {
/*
Properties是一个Map集合,key和value都是String类型。
想将doSome文件中的数据加载得到Properties的对象当中。
*/
FileReader reader=null;
try {
//创建输入流对象
reader=new FileReader("doSome1.properties");
//创建一个Map集合
Properties pro=new Properties();
//调用Properties对象的load方法将文件中的数据加载到Map集合中。
pro.load(reader);//文件中的数据顺着管道加载到Map集合中,其中=左边做key,右边做value
//通过key来的调用value
System.out.println(pro.getProperty("age"));
//遍历
Set<Map.Entry<Object, Object>> s=pro.entrySet();
for(Map.Entry<Object,Object> o:s)
{
System.out.println(o.getKey()+"="+o.getValue());
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
属性配置文件的使用:
#在属性配置文件中井号是注释
#建议key和value之间使用=的方式,不建议使用 age:40
#=左边是key,=右边是value
#最好不要有空格
username=zhangsan
age=40