几乎所有的应用程序都需要数据,而数据怎么来的,不可能都是人工录入,所以我们要借助数据存储设备,最常见的存储设备有硬盘、网络,而IO就是指应用程序对这些数据存储设备的输入和输出。
而在计算机中,所以的数据都被转化成二进制数进行存储,而这些数据在操作的过程中是作为一个文件来体现,所以,在IO的前提要先提一个File类,这是IO的基础。
一、java.io.File
在Java中,对存储设备中的文件和目录进行了封装,用一个File类来代表存储设备中的文件和目录。所以操作一个File对象就等于操作存储设备的文件或目录。
一个File对象可以是一个文件,也可以是一个目录,创建File对象后,如果是目录,可以显示目录清单,新建或删除目录,如果是文件可以查询文件的属性和路径星系。
1、File类的构建
File(String pathname)
pathname可以使绝对路径,也可以是相对路径。相对路径,就是相对于Java系统属性的user.dri中的路径。也就是当前字节码运行的目录。
File(String parent,String filename)
parent是父目录,filename是不含路径的文件名
File(File parent,String filename)
跟上面方法一样,不过parent由一个File对象提供
public class FileTest {
public static void main(String[] args){
//创建当前目录
File dir1=new File(".");
//创建上级目录
File dir2=new File("..");
//当前目录创建haha文件
File test1=new File(dir1,"haha");
File test2=new File(dir2,"haha.txt");
System.out.println(dir1);
System.out.println(dir2.exists());
System.out.println(test1.exists());
System.out.println(test2.exists());
}
File类也给我们提供了很多方法来进行操作,其中很多访问和判断的方法,还有少部分创建和删除的方法。
public static void printProperties(File file){
//getName()方法,获取文件或目录名
System.out.println("name"+file.getName());
//getPath()方法,获取文件路径
System.out.println("name"+file.getPath());
//getAbsolutePath()方法,获取绝对路径
System.out.println("name"+file.getAbsolutePath());
//isDirectory()是否目录
System.out.println("name"+file.isDirectory());
//isFile()是否文件
System.out.println("name"+file.isFile());
}
File类里面提供了很多的方法,但是我们不用死记硬背,把绝对路径和相对路径,文件和目录的区别这几个概念搞清楚了,File类就基本掌握了,不记得可以查阅api文档。
虽然File给我们提供了很多的方法,但File不能操作文件里面的二进制数据,所以,我们就需要IO技术。
二、Java IO技术
java中的流就是数据的传输通道,程序需要数据,可以通过读取流从键盘,存储设备读取,当数据需要存储,可以通过输出流存储到设备中。
流根据传输单位来分可以分为字节流和字符流。
字节流可以操作任何数据,而字符流可以更方便的操作字符数据。
1、InputStream\OutputSteam
从上图可以看出,InputStream是所有字节输入流的超类。而字节输入流分不同的操作对象而分成不同的字节流。其中有一个很特殊FilterInputStream是专门接受流对象,从而增加一些功能。
同理,OutputStream是所有字节输出流的超类。而字节输出流分不同的操作对象而分成不同的字节流。FilterOutputStream是专门接受流对象,从而增加一些功能。
因为他们都是抽象类,所以,要创建对象就要根据需求用他们的子类来创建对象。
1、操作文件
有一个流的子类专门用于操作文件的,就是FileInputStream和FileOutputStream,到底他们怎么用下面有个例子。
public static void main(String[] args)throws Exception{
File text=new File(".","text.txt.txt");
File a1=new File(".","a.txt");
a1.createNewFile();
InputStream bin=new FileInputStream(text);
OutputStream bos=new FileOutputStream(a1);
int a2=0;
a2=bin.read();
bos.write(a2);
bos.flush();
bin.close();
bos.close();
System.out.println();
}
有几点需要注意,构建流需要try{}catch来构建,因为流容易发生不同的异常,还有输入流的文件必须要存在,否则报异常,输出流每次写完都要用flush()来刷新。
2、缓冲流
缓冲流并不操作数据,它只是一个增强功能的类,也称作过滤流。所以它操作的对象是其他数据流对象。
public static void main(String[] args)throws Exception{
File text=new File(".","text.txt.txt");
File a1=new File(".","a.txt");
a1.createNewFile();
BufferedInputStream bin=new BufferedInputStream(new FileInputStream(text));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(a1));
int a2=0;
a2=bin.read();
bos.write(a2);
bos.flush();
bin.close();
bos.close();
System.out.println();
}
}
其实这个例子缓冲流就是增强了上面文件流的处理效率,它是一个增强行的中转站。
3、转换流
InputStreamReader和 OutputStreamWriter
它们是字节流转换成字符流的通道。
这里就先理解一下字符流。下面有张图。
字符流和字节流基本是一样的,不过字符流里面有一种参照的码表,会自动对应码表上的数字把字节数字转换成字符。
而更专业的就有转换流,它是直接告诉你我是把字节转换成字符,而且你可以直接指定码表。
他们对应的类就是InputStreamReader和 OutputStreamWriter。
public class IoTest {
public static void main(String[] args){
BufferedReader bfr = null;
BufferedWriter bfw = null;
try{
//System.in就是键盘录入
bfr = new BufferedReader(new InputStreamReader(System.in));
//输出控制台
bfw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = null;
while((line=bfr.readLine())!=null){
if("over".equals(line))
break;
bfw.write(line.toUpperCase());
bfw.newLine();
bfw.flush();
}
}catch (IOException e){
throw new RuntimeException("读写失败。");
}
finally{
try{
if(bfr!=null)
bfr.close();
}catch (IOException e){
throw new RuntimeException("读取流关闭失败。");
}
try{
if(bfw!=null)
bfw.close();
}catch (IOException e){
throw new RuntimeException("写入流关闭失败。");
}
}
}
}
字符流涉及到编码和解码,编码解码要用同一张表才能正确显示数据。
其实流的应用并不复杂,明白了源、目的、还有操作的数据类型,需要的方式,就会潜意识的选择不同的流来处理。
关于流还有一个比较强大的处理文件的类RandomAccessFile,这个类它可以读也可以写,而且可以根据不同的数据类型来读写,它里面有一个大型的Byte数组,
而且有一个指针,可以指定从哪里读起,也可以指定在哪个位置写,非常好用。下面是毕老师让我们模拟一个多线程下载的作业。
public class ClassTest {
public static void main(String[] args)throws Exception {
//这是源文件
File f=new File("e:"+File.separator+"gjqt2_movie02.mp4");
//这是复制的路径
File k=new File("d:"+File.separator+"copy3.mp4");
k.createNewFile();
//每个线程用50M来分割
long spilt=1024*1024*50;
//求出线程数量
int threadNum=(int)(f.length()/spilt);
System.out.println(threadNum+1);
//通过循环建立线程
for(int x=0;x<=threadNum;x++)
new Thread(new ThreadDownload(x,f,k)).start();
}
}
class ThreadDownload implements Runnable{
int account;
int num=1;
File fread=null;
File fwrite=null;
RandomAccessFile fr=null;
RandomAccessFile fw=null;
ThreadDownload(int account,File fread,File fwrite)
{
this.account=account;
this.fread=fread;
this.fwrite=fwrite;
}
public void run(){
try{
RandomAccessFile fr=new RandomAccessFile(fread,"r");
RandomAccessFile fw=new RandomAccessFile(fwrite,"rw");
fr.seek(1024*1024*50*account);
fw.seek(1024*1024*50*account);
byte[] buf=new byte[1024*1024];
int length=0;
while(num<=50&&(length=fr.read(buf))!=-1)
{
num++;
fw.write(buf,0,length);
buf=new byte[1024*1024];
}
}
catch(Exception e)
{
throw new RuntimeException("下载失败");
}
finally
{
try{
if(fr!=null)
fr.close();
}
catch(Exception e)
{
throw new RuntimeException("读取关闭失败");
}
try{
if(fw!=null)
fw.close();
}
catch(Exception e)
{
throw new RuntimeException("写入关闭失败");
}
}
}
}