IO流
1、File
- java.io.File表示文件(目录),操作本地的文件和目录
- File对象只能操作文件的基本信息(名称、大小等)。不能对文件的位置进行访问。
public static void main(String[] args) {
//在D:\java\ideaProjects\core\src\streams\已经创建了一个FileTest.txt文件
/*
1.构造方法
*/
//public File(String pathname)
File file1 = new File("D:\\java\\ideaProjects\\core\\src\\streams\\FileTest.txt");
System.out.println(file1.exists());//true
//public File(String parent,String child)
String parent = "D:\\java\\ideaProjects";
File file2 = new File(parent,"core\\src\\streams\\FileTest.txt");
System.out.println(file2.exists());//true
//public File(File parent,String child)
File file3 = new File("D:\\java\\ideaProjects");
File file4 = new File(file3,"core\\src\\streams\\FileTest.txt");
System.out.println(file3.exists());//true
/*
相对路径:从当前位置(module子工程)开始
绝对路径:从盘符开始
*/
//绝对路径
File file5 = new File("D:\\java\\ideaProjects\\core\\src\\streams\\FileTest.txt");
//相对路径
File file6 = new File("core\\src\\streams\\FileTest.txt");
System.out.println(file5.exists());//true
System.out.println(file6.exists());//true
/**
* 表示符:.系统默认目录
* .. 上级目录
* 分隔符:正斜杠 /(建议使用适合所有系统)
* 反斜杠\\(只能windows使用)
*/
File file7 = new File(".");
System.out.println(file7.getAbsolutePath());//D:\java\ideaProjects\.
File file8 = new File("../Test.txt");//Test.txt文件时在java目录下的
System.out.println(file8.exists());//true
}
常用方法
public static void main(String[] args) throws IOException {
File file = new File("core/src/streams");//相对路径
File file2 = new File(file, "FileTest1.txt");
//1.createNewFile() 当文件不存在时创建一个新文件
file2.createNewFile();
//2.canRead() 文件是否可读
System.out.println(file2.canRead());//true
//3.canWrite() 文件是否可写
System.out.println(file2.canWrite());//true
//4.exists() 文件是否存在
System.out.println(file2.exists());//true
//5.createTempFile(String prefix, String suffix, File directory)在指定的目录中创建一个新的空文件
// 参数 前缀 后缀 目录(没有目录则是在默认目录)
//File fileTemp = File.createTempFile("FileTest2", ".txt",file);
//6.getAbsolutePath())//获取绝对路径 字符串形式
System.out.println(file2.getAbsolutePath());//D:\java\ideaProjects\core\src\streams\FileTest1.txt
//7.getName()获取抽象路径(最后一级)目录或文件的名字
System.out.println(file2.getName());//FileTest1.txt
//8.getParent() 获取抽象路径的父目录没有返回null
System.out.println(file2.getParent());//core\src\streams
//9.isAbsolute() 是否是绝对路径
System.out.println(file2.isAbsolute());//false
//10.isDirectory() 是否是目录
System.out.println(file.isDirectory());//true
//11.length() 测试文件的长度
System.out.println(file2.length());//按照存储的字符个数
//12.mkdirs() 创建一个多级目录
File file3 = new File(file,"a/b/c");
file3.mkdirs();
//13.delete() 删除目录或文件(一次只能删除一级)
file3.delete();
//14.setReadable() 设置文件的读取权限
file2.setReadable(true,false);
System.out.println(file2.canRead());//true
System.out.println(file3.canWrite());//false
//15.lastModified() 文件的最后修改时间
System.out.println(new Date(file2.lastModified()));
//16.String[] list() 以字符数组形式返抽象路径下的所有文件
String[] list = file.list();
System.out.println(Arrays.toString(list));
//17.File[] listFiles() 以文件数组形势返回抽象路径名中所有路径
File[] files = file.listFiles();
System.out.println(Arrays.toString(files));
}
2、IO流概述
- IO:指输入输出
- input:输入,从文件到程序
- Output:输出,从程序到文件
- 流的分类:
- 字节流和字符流
- 字节流:传输的最小单位是字节
- 字符流:传输的最小单位是字符
- 节点流(低级流)和处理流(高级流或过滤流)
- 节点流:可以向一个特定的地方读写数据FileIO
- 处理流:封装了节点流,通过封装的流的功能调用实现数据读写,如BufferedIO。
- 字节流和字符流
- 流的连接:一个流对象经过其他流的多次包装,成为流的链接。
3、字节流
- 字节流有两个抽象类:InputStream和OutputStream
FIS和FOS
- FIS:FileInputStream,文件输入流
- FOS:FileOutputStream,文件输出流
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/*
单元测试:他不是jdk下的包他需要自己导入JUnit包
@Before 执行单元测试前都会执行
@After 执行单元测试后都会执行
@Test 是独立的单元可以直接执行像main()一样
绝对路径:从盘符开始
相对路径:当前位置开始
main从module开始
单元测试是从Classpath(类路径开始) 单元测试的默认路径为src
可以标记一个普通文件为类路径位:Mark Directory as
*/
public class TestFileStream {
File file;
FileInputStream fis;//input 文件-->程序
FileOutputStream fos;//output 程序-->文件
//初始化对象
@Before
public void newFile() throws Exception{
file = new File("src/streams/testfile/testInput.txt");
}
/**
* 1.写:write(),写入一个字节
* @throws Exception
*/
@Test
public void testWrite() throws Exception{
String str = "Hello World!";
fos = new FileOutputStream(file,true);//append为true在末尾追加,如果没有则是在最前面覆盖
for (int i = 0; i < str.length(); i++) {
fos.write((byte)(str.charAt(i)));
}
}
/**
* 2.读:read(),读取一个字节 返回的字符的编码整数
* @throws Exception 抛出异常
*/
@Test
public void testRead() throws Exception{
fis = new FileInputStream(file);
int i = -1;
while ((i = fis.read()) != -1){//读取到文件末尾
System.out.print((char)i);
}
}
/**
* 4.读:read(byte[] b) 将字节读取到字节数组中 返回的整数是读取到的字节个数
* @throws Exception
*/
@Test
public void testReadByte() throws Exception{
fis = new FileInputStream(file);
byte[] bus = new byte[1024];//一般1024为1k
fis.read(bus);
System.out.println(new String(bus));
}
/**
* 4.写:write(byte[] b) 以字节数组形势写入
* @throws Exception
*/
@Test
public void testWriteByte() throws Exception{
fos = new FileOutputStream(file);
String str = "I love java";
fos.write(str.getBytes());
}
/**
*复制文件将testInput.txt文件复制到testInput2.txt中
* @throws Exception
*/
@Test
public void testFileCopy() throws Exception{
fis = new FileInputStream(file);
fos = new FileOutputStream("src/streams/testfile/testInput2.txt");
byte[] bus = new byte[1024];
int i = -1;
while((i = fis.read(bus)) != -1){
//System.out.println(new String(bus));
//如果直接方法bus则会最后读取到的没有1k则就会多复制进去
fos.write(bus,0,i);//没有该文件会自动创建一个
}
}
//关闭io流
@After
public void colseFile() throws Exception{
if(fis != null)fis.close();
if(fos != null)fos.close();
}
}
BIS和BOS
-
缓冲区的优势:减少了读写次数
-
flush():清空缓冲,将缓冲区中的数据全部写出
-
缓冲流高级流是以FileIO这种基础流作为载体使用
-
BIS:BufferedInputStream 缓冲输入流
-
BOS:BufferedOutputStream 缓冲输出流
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.*;
public class TestBufferStream {
File file;
FileInputStream fis;//低级输入流
FileOutputStream fos;//低级输出流
BufferedInputStream bis;//高级输入流,高级输入流是一低级输入流为基础
BufferedOutputStream bos;//高级输出流,高级输入流是一低级输出流为基础
@Before
public void newFile(){
file = new File("src\\streams\\testfile\\testInput.txt");
}
/**
* 读,Buffere高级流是以低级流File为载体,
* 读取出来的数据先放着缓存区中然后等缓存区满或者强制刷新才能写入,这样大大的提供了读写速度
* 强制刷新:flush();colse();
* @throws Exception
*/
@Test
public void testBufferRead() throws Exception{
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
byte[] bus = new byte[1024];
int i = -1;
while( (i = bis.read(bus) )!= -1){
System.out.print(new String(bus));
}
}
/**
* 写
* @throws Exception
*/
@Test
public void testBufferWrite() throws Exception{
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
String str = "Hello World!";
bos.write(str.getBytes());
bos.flush();//立即写入
}
/**
* 复制文件将testInput.txt文件复制到testInput2.txt中
* @throws Exception
*/
@Test
public void TestBufferCopy() throws Exception{
String str = "src/streams/testfile/testInput2.txt";
fis = new FileInputStream(file);
fos = new FileOutputStream(str);
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
int i = -1;
byte[] bus = new byte[1024];
while((i = bis.read(bus)) != -1){
bos.write(bus,0,i);
}
bos.flush();
}
//关闭io流
@After
public void closeFile() throws Exception{
if(fis != null){ fis.close(); }
if(fos != null){ fos.close(); }
if(bis != null){ bis.close(); }
if(bos != null){ bos.close(); }
}
}
注:使用缓冲流的时候要注意刷新缓冲区,因为数据从缓冲区写入文件只有两种情况,缓冲区满或者强制清空缓冲区(flush()或close()),否则数据就在缓冲区中并没有写入文件中。
4、字符流
- 字符流有两个抽象类Reader和Writer
- 字符流的底层是字节流,每次处理一个字符(char)
- 常见字节集:
- UTF-8:是针对Unicode的一种可变长度的字符编码
- GBK:汉字编码字符集
ISR和OSW
-
ISR:inputStreamReader,字符输入流
-
OSW:OutputStreamWriter,字符输出流
以Reader和Writer结尾的对象都是字符流
public class TestISRandOSW {
File file;
FileInputStream fis;
FileOutputStream fos;
BufferedInputStream bis;
BufferedOutputStream bos;
InputStreamReader isr;
OutputStreamWriter osw;
@Before
public void filePath(){
file = new File("src/streams/testfile/reader.txt");
}
//写
@Test
public void testWriter() throws Exception {
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
osw = new OutputStreamWriter(fos);
osw.write("大家好!");
osw.flush();
}
//读
@Test
public void testReader() throws Exception{
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
isr = new InputStreamReader(bis);
char[] chars = new char[1024];
isr.read(chars);
System.out.println(new String(chars));
}
//把reader.txt复制到reader_copy.txt
@Test
public void testCopy() throws Exception{
//创建输入字符流
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
isr = new InputStreamReader(bis);
//创建输出字符流
fos = new FileOutputStream("src/streams/testfile/reader_copy.txt");
bos = new BufferedOutputStream(fos);
osw = new OutputStreamWriter(bos);
//复制
int i;
char[] chs = new char[1024];
while((i = isr.read(chs)) != -1){
osw.write(chs,0,i);
}
osw.flush();//不强制刷新缓冲区,读取到的字符就还在缓冲区中,并没有存放到文件中去。
}
@After
public void closeFile() throws Exception{
if(fis != null){fis.close();}
if(fos != null){fos.close();}
if(bis != null){bis.close();}
if(bos != null){bos.close();}
if(isr != null){isr.close();}
if(osw != null){osw.close();}
}
}
PW和BR
- PW:PrintWriter:输入打印流,搭建了自动刷新缓冲区
- BR:BufferedReader:缓冲字符输入流
public class TestPWandBR {
File file;
PrintWriter pw;
BufferedReader br;
//创建文件
@Before
public void filePath(){
file = new File("src/streams/testfile/pw.txt");
}
//写
@Test
public void testPW() throws Exception{
/*
PrintWriter如果autoFlush参数为true就会自动刷新缓冲区,就不用了手动刷新缓冲区了
注:但是自动刷新缓冲区是遇见'\r','\n',就会自动刷新缓冲区;
故建议还是在文件读取完之后手动添加刷新缓冲区;
如果为 true,<tt>println<tt>、<tt>printf<tt> 或 <tt>format<tt> 方法将刷新输出缓冲区
*/
pw = new PrintWriter(new BufferedOutputStream(new FileOutputStream(file)),true);
pw.print("大家好!");//在文件末尾追加
pw.println("你好!");//在末尾添加后面会多追加一个换行
pw.print("123");
pw.flush();
}
//读
@Test
public void testBR() throws Exception{
br = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
String line = null;
//readLine() 读取一行字符
while((line = br.readLine()) != null){
System.out.println(line);
}
//char[] chs = new char[1024];
//br.read(chs);
//System.out.println(new String(chs));
}
//关闭流
@After
public void colseFile() throws Exception{
if(pw != null){pw.close();}
if(br != null){br.close();}
}
}
5、对象流
- 序列化:对象转化为字节序列
- 反序列化:字节序列转化为对象
OOS和OIS
- 对象序列化注意事项:
- 必须实现Serializable接口,该接口没有方法的实现
- 建议添加对象序列号(serialVersionUID),避免版本差别和属性被修改后反序列化错误
- transient关键字:被修饰的对象,对象序列化时忽略属性值,从而对对象序列化的字节序列达到“廋身”的目的。
/**
* Studet实体类
*/
public class Student implements Serializable {
private static final long serialVersionUID = -3822482643965664L;
private String name1;
//被transient修饰的属性,序列化时不考虑它,起到一个序列化廋身的效果
private transient int age;
public Student(String name, int age) {
this.name1 = name;
this.age = age;
}
public Student() {
}
public String getName() {
return name1;
}
public void setName(String name) {
this.name1 = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name1 + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name1, student.name1);
}
@Override
public int hashCode() {
return Objects.hash(name1, age);
}
}
对象的序列化与反序列化
public class TestOISandOOS {
File file;
FileInputStream fis;
FileOutputStream fos;
BufferedInputStream bis;
BufferedOutputStream bos;
ObjectInputStream ois;
ObjectOutputStream oos;
@Before
public void filePath(){
file = new File("src/streams/testfile/osi.txt");
}
/**
* 序列化:对象-->文件
* 需要让对象类实现 Serializable 接口
* 否则就会报错NotSerializableException 不可序列化异常
* @throws Exception
*/
@Test
public void testOOS() throws Exception{
fos = new FileOutputStream(file);
//bos = new BufferedOutputStream(fos);//对象序列化时不能使用缓冲输出流作为载体
oos = new ObjectOutputStream(fos);//创建对象序列化对象以fos为载体
//Student是一个实体类自己创建的里面有name和age属性
Student student = new Student("张三", 20);
oos.writeObject(student);
}
/**
* 反序列化:文件-->对象
* serialVersionUID(序列化号):功能解决不兼容问题
* 当对象已经序列化在硬盘中,反序列化是对象属性发生了改变,就会报出如下错误:
* java.io.InvalidClassException:
* streams.Student; local class incompatible:
* stream classdesc serialVersionUID = -1712708104449596143,
* local class serialVersionUID = -3822482643965664
* 可以通过在序列化的类中修饰一个序列化号(serialVersionUID)
*/
@Test
public void testOIS() throws Exception{
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
ois = new ObjectInputStream(bis);//创建一个对象翻序列化对象以fis作为载体
byte[] bus = new byte[1024];
int i = -1;
Object o = ois.readObject();
System.out.println(o);
}
@After
public void colseFile() throws Exception{
if(fis != null){
fis.close();
}
if(fos != null){
fos.close();
}
if(bis != null){
bis.close();
}
if(bos != null){
bos.close();
}
if(ois != null){
ois.close();
}
if(oos != null){
oos.close();
}
}
}
案例测试:
/*
输入一个字符串统计字符个数并放入文件中
*/
public class TestCase {
public static void main(String[] args) throws Exception{
TestCase tc = new TestCase();
//1.输入字符串
Scanner scanner = new Scanner(System.in);
System.out.println("请输入字符串:");
String str = scanner.next();
//2.统计字符个数
Map<Character,Integer> map = tc.countChar(str);
//System.out.println(map.toString());
//3.存入文件中
File file = new File("core/src/streams/testfile/count_char.txt");
tc.putFile(str,map.toString(),file);
System.out.println("存入成功!");
}
//统计字符个数
public Map<Character,Integer> countChar(String string){
Map<Character, Integer> countMap = new HashMap<>();
//字符串处理
char[] chars = string.toCharArray();
for (int i = 0; i < chars.length; i++) {
//存入map,有+1,没有赋初值1
countMap.put(chars[i],countMap.containsKey(chars[i]) ? (countMap.get(chars[i]) + 1):1);
}
return countMap;
}
//存入文件中
public boolean putFile(String string, String countChar, File file) throws Exception{
FileOutputStream fos = new FileOutputStream(file,true);
BufferedOutputStream bos = new BufferedOutputStream(fos);
bos.write(string.getBytes());
bos.write(countChar.getBytes());
bos.write('\n');
if(bos != null){
bos.close();
return true;
}
return false;
}
}