文章目录
File
File文件和目录路径名的抽象表示,用来封装文件和目录的路径(这个文件或者目录不一定存在)
-
File的构造函数:
File:抽象的描述了磁盘路径(创建一个具体的对象指定路径 但是该路径的文件或者文件夹不一定在磁盘真的存在!!!)
一个File对象就是一个具体的文件或者目录(目的:指定文件或文件夹保存的路径)
如何得到一个具体的File对象?? 通过构造方法
File(String pathname):通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。 即给一个路径
File(String parent, String child):根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。
File(File parent, String child):根据 parent 对象抽象路径名和 child 路径名字符串创建一个新 File 实例。(就是给parent后面加一个路径例如D://filetest文件夹下的read.txt表示是:
File file=new File(“D://filetest”);
File newfile=new File(file,“read.txt”);) -
使用File在磁盘中,创建文件,及文件夹的对象方法:
boolean createNewFile() :创建文件 返回值表示是否创建成功
①当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件,返回true
②如果路径不存在,就不能创建文件,返回false
boolean mkdir() :创建此抽象路径名指定的目录(文件夹) 注意:如果父路径不存在,则不会创建目录
boolean mkdirs() :如果父路径不存在,会自动先创建路径所需的目录,即会先创建父路径内容再创建目录(就是用来创建多级目录)
boolean isDirectory() :判断是否是一个目录
boolean isFile() :判断是否是一个文件
boolean exists() :判断此抽象路径名表示的文件或目录是否存在
介绍绝对路径和相对路径
这里的代码同时给大家实践了创建文件
public static void main(String[] args) throws IOException{
//创建File对象,声名路径。可以是绝对路径也可以是相对路径
File file1 =new File("D://filetest//atest.txt");//绝对路径
File file2 =new File("src//btest.txt");//相对路径
//File方法创建文件,当没有此文件则创建,有则覆盖。
file2.createNewFile();
System.out.println(file2);//File重写了toStrng方法,调用getPath
//file2.getPath()方法输出相对路径
System.out.println(file2.getPath());
//ile2.getAbsolutePath()方法输出绝对路径
System.out.println(file2.getAbsolutePath());
}
输出如下
- 绝对路径:大家都看到也就不多说,就是把他的路径从磁盘精确写出来。
- 相对路径:相对路径使用“/”字符作为目录的分隔字符,而绝对路径可以使用“\”或“/”字符作为目录的分隔字符。在 相对路径里常使用“…/”来表示上一级目录。如果有多个上一级目录,可以使用多个“…/”。
我们这里绝对路径是什么呢?
按照jdk Doc上的说法”绝对路径名是完整的路径名,不需要任何其他信息就可以定位自身表示的文件。相反,相对路径名必须使用来自其他路
径名的信息进行解释。默认情况下,java.io 包中的类总是根据当前用户目录来分析相对路径名。此目录由系统属性 user.dir 指定,通常是
Java 虚拟机的调用目录.”
相对路径顾名思义,相对于某个路径,那么究竟相对于什么路径我们必须弄明白.按照上面jdk文档上讲的这个路径是”当前用户目录”也就是”
java虚拟机的调用目录”.更明白的说这个路径其实是我们在哪里调用jvm的路径。
File file2 =new File("btest.txt");//相对路径
//File方法创建文件,当没有此文件则创建,有则覆盖。
// file1.createNewFile();
file2.createNewFile();
System.out.println(file2.getAbsolutePath());
这里我们能看到java运行时的相对目录
这里我们绝对路径和相对路径讲解告一段落,顺带也看了创建文件方法。
- 创建目录/删除目录
当目录里面有文件时无法直接删除目录
public static void main(String[] args) throws IOException{
//创建filetest目录
File file1=new File("D://filetest");
System.out.println(file1.mkdir());
//在filetest目录下创建atest.txt
File file2 =new File("D://filetest//atest.txt");
System.out.println(file2.createNewFile());
//删除filetest目录,注意这里filetest里面有文件所以删除不会成功
System.out.println(file1.delete());
//删除文件atest.txt
System.out.println(file2.delete());
//删除filetest目录成功
System.out.println(file1.delete());
}
- 案例:递归遍历目录
public class IoDemo {
public static void main(String[] args) throws IOException{
File srcfile =new File("D://filevisit");
visitfile(srcfile);
}
public static void visitfile(File file){
File[] files = file.listFiles();
if (files!=null){
for (File nfile: files) {
if (nfile.isDirectory()){//判断是否是目录,是递归调用函数
visitfile(nfile);
}
if (nfile.isFile()){//判断是否是文件,是输出文件的绝对地址
System.out.println("文件"+nfile.getAbsolutePath());
}
}
}
}
}
IO流
IO(Input Output)
IO流用来处理设备之间的数据传输
- 流的分类:
流按数据分为两种:字节流/字符流
流按流向分为两类:输入流/输出流
什么时候使用字节流,什么时候使用字符流?
如果用Windows自带记事本打开文件,如果是你能读栋文字(例如小说txt)就用字符流,如果不能读懂用字节流(图片)。如果不知道就用字节流。 - IO流的常用基类
字节流的抽象基类:InputStream/OutputStream (这两个是所有字节输入/输出流的超类)
字符流的抽象基类:Reader(读输入)/Writer (所有字符流的超类)
注:这四个类派生出来的子类名称都是以其父类的名称作为后缀,子类功能作为前缀。
字节流
- 构造函数
这里我们看到两种类型的构造函数: - 不同点
- public FileOutputStream(File file):这种构造函数参数是File封装的文件或者目录。创建文件输出流以写入由指定的File对象表示的文件
- public FileOutputStream(String name):这种构造函数参数不是File封装的文件,参数是文件的路径。创建文件输出流以指定的名称写入文件。
- 相同点
- 两个种构造如果文件存在但是是一个目录而不是常规文件,不存在但不能创建,或者由于任何其他原因无法打开,那么抛出一个FileNotFoundException 。(如果是常规文件就就创建(不管是否存在直接覆盖,除非第二个参数true这种可以在原存在文件中最后操作),如果是目录不管是否存在都不创建,如果进行操作时无法找到则抛异常FileNotFoundException )
输入输出流构造函数意思差不多。
FileOutputStream(字节流写数据)
public static void main(String[] args) throws IOException{
//创建字节输出流对象
FileOutputStream outputStream=new FileOutputStream("D://filetest//outtest.txt");
//给文件写数据,97因为是字节流所以a
outputStream.write(97);
//所有的流使用完后都要关闭,释放资源
outputStream.close();
}
FileOutputStream outputStream=new FileOutputStream(“D://filetest//outtest.txt”);
注意在这里做了三件事:
1:调用系统功能创建文件。outtest.txt在这里创建,如果有相同的文件则会覆盖,和前面File创建文件不一样(那里如果有就创建失败)。
从源码可以看出
2:创建字节输出流对象
3:让字节输出流对象指向文件
实现文件的追加写入,我们前面知道每次创建对象那如果有相同的文件,就会覆盖,这里不会覆盖直接运行多次,可写入之前已经被的文件,我们可以用构造函数,在文件末尾之家写入。
FileOutputStream outputStream=new FileOutputStream(“D://filetest//outtest.txt”,true);
当我们想实现换行,因为不同的操作系统换行符不同,Windows:"\r\n",linux"\n",mac"\r"
for (int i = 0; i ❤️; i++) {
outputStream.write(“hello”.getBytes());
outputStream.write("\r\n".getBytes());
}
FileInputStream(字节流读数据)
public static void main(String[] args) throws IOException{
FileInputStream inputStream=new FileInputStream("D://filetest//outtest.txt");
int by=inputStream.read();
while (by!=-1){
System.out.println((char)by);
by=inputStream.read();
}
}
- 复制文本文件实现
public static void main(String[] args) throws IOException{
FileInputStream inputStream1=new FileInputStream("D://filetest//read.txt");
FileOutputStream outputStream=new FileOutputStream("D://filetest//write.txt",true);
FileInputStream inputStream2=new FileInputStream("D://filetest//write.txt");
int by1;
while ((by1=inputStream1.read())!=-1){
outputStream.write(by1);
}
int by2;
while ((by2=inputStream2.read())!=-1){
System.out.println((char)by2);
}
inputStream1.close();
inputStream2.close();
outputStream.close();
}
public static void main(String[] args) throws IOException{
FileInputStream inputStream=new FileInputStream("D://filetest//read.txt");
byte []bys=new byte[4];
int len;
while ((len = inputStream.read(bys)) != -1){
System.out.println(new String(bys,0,len));
}
}
len=fis.read(arr),len不一定是上面字节数组 bys[4]的长度5,是当前读到的字节长度,如果最后只有三个字节那么len=3,最后没有字节len=-1。
字节缓冲流
这里要注意的是,前面我们一直说流用完后一定要关闭。在前面字节流如果不关闭,字节还是能写入,但是在字节缓冲流如果不关闭,那么字节将无法写入。
因为字节流在读取文件时每次读取一个字节都会去操作磁盘,这样如果数据量大的话,就会很频繁的操作磁盘。效率低下。
但如果使用了缓冲流,JAVA程序就会一次性从磁盘拉取大量数据先放到JAVA内存中。然后在优先从这个内存读取。如果读完了。在重新从磁盘在拉取大量数据。这样就大大的减少了操作磁盘的频率,从内存读取数据的效率要远高于从磁盘读取的效率。
这样如果不关闭那么,数据都在缓冲区,没有写入磁盘。关闭之前会有一个刷新操作,将缓冲区中的数据写入磁盘中。
-
构造函数
-
public BufferedOutputStream**(OutputStream** out)
-
public BufferedOutputStream(OutputStream out, int size)
创建一个新的缓冲输出流,以便以指定的缓冲区大小将数据写入指定的底层输出流。
缓冲流参数类型字节缓冲流是OutputStream所以里面传的是字节流对象,我们可以使用FileOutputStream对象传入,后面的参数out - 底层输出流。,size - 缓冲区大小。指定是否新创建或者是在原文件后面写入,是在FileOutputStream后面的参数指出的true。因为是缓冲流所以缓冲输入流和普通缓冲流多一个方法可以一次读一行的方法, -
案例练习
public static void main(String[] args) throws IOException{
BufferedInputStream bfin=new BufferedInputStream(new FileInputStream("D://filetest//read.txt"));
BufferedOutputStream bfout=new BufferedOutputStream(new FileOutputStream("D://filetest//write.txt",true));
byte []bys=new byte[5];
int len;
while ((len=bfin.read(bys))!=-1){
bfout.write(bys,0,len);
}
bfin.close();//注意缓冲流一定要关闭,不关闭字节无法写入,因为在缓冲区为保存
bfout.close();
}
字符流
字符流=字节流+编码表
这里我们说字符流=字节流+编码表,说明字符流底层还是字节流,所以字符输入流参数传字节流。
例如在文件中存储汉字如果用字节流输出到控制台无法看懂的,因为一次输出一个字节且没有编码表。UTF-8一个汉字是3个字节。
字符流的抽象基类:Reader/Writer
InputStreamreader/OutputStreamWriter
InputStreamReader是从字节流到字符流的桥(就是将字节流转换为字符流):它读取字节,并使用指定的charset将其解码为字符 。 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。
每个调用InputStreamReader的read()方法之一可能会导致从底层字节输入流读取一个或多个字节。 为了使字节有效地转换为字符,可以从底层流读取比满足当前读取操作所需的更多字节。
-
构造函数
public InputStreamReader(InputStream in)
public InputStreamReader(InputStream in,String charsetName)
这里我们看到字符流的构造函数参数是字节流类型,后面的参数是指定编码表,不指定默认字符集.。 -
案例练习
public static void main(String[] args) throws IOException{
InputStreamReader isr=new InputStreamReader(new FileInputStream("D://filetest//read.txt"));
OutputStreamWriter osr=new OutputStreamWriter(new FileOutputStream("D://filetest//write.txt"));
int ch;
while ((ch=isr.read())!=-1){
osr.write(ch);
}
isr.close();
osr.close();
}
之后一定要关闭,不然无法写入。
FileWriter/FileReader
- 构造函数
public FileWriter(File file)
public FileWriter(File file,boolean append)
这里我们看到上面的字符流参数是字节流,那就的创建字符流之前创建字节流。这里方便了操作直接参数就是File操作封装了的文件夹。这里性质和字节输入流相似,如果是常规文件就就创建(不管是否存在直接覆盖,除非第二个参数true这种可以在原存在文件中最后操作),如果是目录不管是否存在都不创建,如果进行操作时无法找到则抛异常
*练习
public static void main(String[] args) throws IOException{
FileReader fr=new FileReader("D://filetest//read.txt");
FileWriter fw=new FileWriter("D://filetest//writer.txt");
int ch;
while ((ch=fr.read())!=-1){
fw.write(ch);
}
fr.close();
fw.close();
}
字符缓冲流
将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入。
可以指定缓冲区大小,或者可以接受默认大小。 默认值足够大,可用于大多数用途。
提供了一个newLine()方法,它使用平台自己的系统属性line.separator定义的行分隔符概念(所有系统都能识别的换行)。 并非所有平台都使用换行符(’\ n’)来终止行。 因此,调用此方法来终止每个输出行,因此优选直接写入换行符。
- 构造方法
public BufferedWriter(Writer out)
public BufferedWriter(Writer out,int sz)
我们看到参数类型是Writer,为了方便我们可以放Filewrite类型。
newline行分隔符是根据不同系统都可以
readline只读内容不读换行符字符缓冲流特有方法
- 案例练习:将集合数据写入到文件中
public static void main(String[] args) throws IOException{
BufferedWriter bw=new BufferedWriter(new FileWriter("D://filetest//write.txt",true));
ArrayList<String> al=new ArrayList();
al.add("我是");
al.add("wang");
al.add("nihaao");
for (String a: al) {
bw.write(a);
bw.newLine();
bw.flush();
}
bw.close();
}
String line;
while ((line=br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.flush();
}
字符缓冲流标准读写方式,因为readline方法没有不识别换行符,所以用newline,最好没此flush。
- 练习案例:点名器
public static void main(String[] args) throws IOException{
BufferedReader br=new BufferedReader(new FileReader("D://filetest//read.txt"));
ArrayList<String> name=new ArrayList<>();
String brre;
while ((brre=br.readLine())!=null){
name.add(brre);
}
br.close();
Random r=new Random();
int random=r.nextInt(name.size());
System.out.println("请这位同学签到"+name.get(random));
}
- 案例练习从集合到文件(改进版)
public class Student {
String ID;
String name;
int age;
String address;
public Student(String ID, String name, int age, String address) {
this.ID = ID;
this.name = name;
this.age = age;
this.address = address;
}
}
public class IoDemo {
public static void main(String[] args) throws IOException{
ArrayList<Student> arr=new ArrayList<Student>();
Student stu1=new Student("170601","李明",12,"西安");
Student stu2=new Student("170602","王明",18,"北京");
Student stu3=new Student("170603","张明",19,"上海");
arr.add(stu1);
arr.add(stu2);
arr.add(stu3);
BufferedWriter bw=new BufferedWriter(new FileWriter("D://filetest//studentw.txt",true));
for (Student s: arr) {
StringBuilder sb=new StringBuilder();
sb.append(s.ID).append(s.name).append(s.address).append(s.age);
String all=sb.toString();
bw.write(all);
bw.newLine();
bw.flush();
}
bw.close();
}
}
- 案例练习:从文件到集合
public class Student {
String ID;
String name;
public Student(String ID, String name) {
this.ID = ID;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"ID='" + ID + '\'' +
", name='" + name + '\'' +
'}';
}
}
public class IoDemo {
public static void main(String[] args) throws IOException{
BufferedReader br=new BufferedReader(new FileReader("D://filetest//read.txt"));
ArrayList<Student> arr=new ArrayList<>();
String line;
while ((line=br.readLine())!=null){
String []str=line.split(" ");
Student stu=new Student(str[0],str[1]);
arr.add(stu);
}
br.close();
System.out.println(arr.toString());
}
}
这个里面Student用到toString方法,IoDemp里面用到Strng.split()方法,他是通过指定参数里分割成字符串数组。
- 案例复制单机文件夹
应为文件夹内有图片有文本只能用字节流。
public class IoDemo {
public static void main(String[] args) throws IOException{
File srcfile=new File("D://filetest1");//数据源文件夹
File aimfile=new File("D://filetest2");//目的文件夹
String aimdirname=aimfile.getAbsolutePath();//目的文件夹路径
File []srcfilex=srcfile.listFiles();//得到目的文件夹内文件对象
for (File fx:srcfilex) {//目的文件路径=目的文件夹路径+源文件名
String fxname=fx.getName();//得到数据源文件夹文件名称
File aimfilex=new File(aimdirname,fxname);//创建目的文件夹内文件File对象
copyFile(fx,aimfilex);
}
}
public static void copyFile(File f1,File f2x)throws IOException{
BufferedInputStream bis=new BufferedInputStream(new FileInputStream(f1));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(f2x));
byte[]by=new byte[1024];
int len;
while ((len=bis.read(by))!=-1){
bos.write(by,0,len);
}
bis.close();
bos.close();
}
}
- 案例练习:复制多级文件夹
import java.io.*;
public class IoDemo {
public static void main(String[] args) throws IOException {
File srcfile = new File("D://filetest1");//源文件夹
File aimdirfile=new File("D://java-code");//目的文件夹
copydir(srcfile,aimdirfile);
}
/**
* 复制文件
* @param srcpath
* @param aimpath
*/
public static void copyFile(File srcpath,File aimpath)throws IOException{
BufferedInputStream bis=new BufferedInputStream(new FileInputStream(srcpath));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(aimpath));
byte[]bys=new byte[1024];
int len;
while((len=bis.read(bys))!=-1){
bos.write(bys);
}
bis.close();
bos.close();
}
/**
* 判断是否dir目录存在(存在返回true/不存在创建目录返回false)
* @param dir
* @return
*/
public static boolean havedir(File dir){
if (dir.exists()){
return true;
}
dir.mkdir();
return false;
}
/**
* 复制目录
* @param srcdir
* @param aimdir
*/
public static void copydir(File srcdir,File aimdir)throws IOException{
if (srcdir.isDirectory()){//判断是否是目录
File aimdirfile=new File(aimdir,srcdir.getName());
if (!aimdirfile.exists()){
aimdirfile.mkdir();
}
File []filesrclist= srcdir.listFiles();
for (File tmpfile:filesrclist) {
copydir(tmpfile,aimdirfile);
}
}else {
File aimfile=new File(aimdir,srcdir.getName());
copyFile(srcdir,aimfile);
}
}
}
File用来封装路径
标准输入/输出流
标准输入/输出流方法都在System类下因为是static修饰所以可以直接用类名调用。System被final修饰所以不可以被继承。
- 标准输入流
- Scanner 原理举例
//public static final InputStream in得到标准字符输入流
InputStream ips=System.in;
int i;
while ((i=ips.read())!=-1){
System.out.println((char)i);
}
上面的代码用标准输入流可以输出字母,数字,因为UTF-8占一个字节,这里是字节流一次读一个字节。如果是汉字就不行,汉字是三个字节。
InputStream ips=System.in;
//使用字符流就可以输出汉字->所以把ips转换为字符流
InputStreamReader isr=new InputStreamReader(ips);
char a;
int s;
while ((s=isr.read())!=-1){
System.out.println((char) s);;
}
使用字符流就可以输出汉字->所以把ips转换为字符流
InputStream ips=System.in;
InputStreamReader isr=new InputStreamReader(ips);
BufferedReader br=new BufferedReader(isr);
String str=br.readLine();
System.out.println(str);
当我们不想一次一个字符,想一次一行,那就使用缓冲字符输入流(字符缓冲流中才有readline方法)。
到了这里我们看到能自己从键盘得到一行String,但是当我们想要得到Int类型怎么办?有了下面的代码
BufferedReader br=new BufferedReader(isr);
int num=Integer.parseInt(br.readLine());
System.out.println(num);
我们只需要将String转为Int就行
好了这里最后了,我们当想从键盘得到数据这么多行太麻烦了怎么办,于是java把上各种封装了有了Scanner类
Scanner in=new Scanner(System.in);
int i=in.nextInt();
-
标准输出流
public static final PrintStream out“标准”输出流。 此流已经打开并准备好接受输出数据。 通常,此流对应于显示输出或由主机环境或用户指定的另一个输出目标。
对于简单的独立Java应用程序,编写一行输出数据的典型方法是: System.out.println(data) -
*打印流
总结
分析需要使用字节流流的过程
- 第一步:我们得到封装了来源路径。例如File file=new File(“D://filetest”);
- 第二步:我们要使用流呀,这时用字符输入/输出或者字节输入/输出,参数时第一步封装的路径。
例如FileInputStream isr=new FileInputStream(file);
或者这里可以直接省去第一步FileOutputStream file=new FileOutputStream(“D://filetest1//write.txt”); - 第三步:我们想要提升流的效率,这是我们有了缓冲各种流(这个可以一次读一行)。
例如:BufferedInputStream br=new BufferedInputStream(isr);
第一版:
File file = new File("D://filetest1//write1.txt");
FileInputStream file1 = new FileInputStream(file);
BufferedInputStream file2 = new BufferedInputStream(file1);
缩减版:
BufferedInputStream file2 = new BufferedInputStream(new FileInputStream("D://filetest1//write1.txt"));
读写的标准方法
byte []bys=new byte[5];
int len;
while ((len=bfin.read(bys))!=-1){
bfout.write(bys,0,len);
}
分析需要使用字符流的过程
字符流是基于字节流的
普通方法:
InputStreamReader isr=new InputStreamReader(new FileInputStream("D://filetest//read.txt"));
FileWriter fw=new FileWriter("D://filetest//writer.txt");//上面类的子类,方便
缓冲办法:
BufferedWriter bw=new BufferedWriter(new FileWriter("D://filetest//write.txt",true));
读写流程:
String line;
while ((line=br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.flush();
}
不管是字节缓冲流还是字符缓冲流都有刷新flush方法,要刷新。
异常处理
当我们使用上面改进try…catch就会系统自动释放资源close,当然会刷新。