java基础-文件与IO
以下为本人的学习笔记
IO是一个庞大的体系,信息传递的通道靠IO,例如:文件读写、上传下载、网络通信等都要用到。
1.File类的基本概念
File类:表示文件和目录路径名的抽象表示形式
File类可以实现文件的创建、删除、重命名、得到路径、创建时间等等。是唯一与文件本身有关的操作类
File类的操作方法
Method | 说明 |
---|---|
public static final String separator | 表示路径分隔符“ \ ” |
public File(String pathname) | 构造File类实例,要传入路径 |
public boolean createNewFile() | 创建新文件 |
public boolean delete() | 删除文件 |
public boolean isDirectory() | 判断给定的路径是否是文件夹 |
public boolean isFile() | 判断给定的路径是否是文件 |
public String[] list() | 列出文件夹中的文件 |
public File[] listFiles() | 列出文件夹中的所有文件 |
boolean mkdir() | 创建新的文件夹 |
public boolean renameTo(File dest) | 为文件重命名 |
public long length() | 返回文件大小 |
String getPath() | 路径名字符串 |
/**
File类的使用
*/
public static void main(String[] args){
//File类表示一个文件或目录
//“c:\\test\\vince.txt"
//"c:/test/vince.txt"
File f1 = new File("C:/test/vince.txt");
if(!f1.exists()){
//判断f1指向的文件是否存在
try{
f1.createNewFile();//创建文件
System.out.println("文件创建成功")
}catch(IOException e){
e.printStackTrace();
}
}
//f1.isFile()是否为文件
System.out.println("是否为文件夹:"+f1.isDirectory());
File f2 = new File("c:\\test\\my");
boolean b = f2.delete();//delete()只能删除空文件夹
System.out.println(b);
String[] names = f2.list();//列出当前目录下的所有文件名
System.out.println(Arrays.toString(names);
File[] fs = f2.listFiles();//列出当前目录下的所有文件,以file对象返回
for(File f : fs){
System.out.println("length="+f.length());
System.out.println("name="+f.name());
System.out.println("相对路径path="+f.path());
System.out.println("绝对路径getAbsolutePath="+f.getAbsolutePath());
System.out.println("是否为隐藏文件="+f.isHidden());
System.out.println("是否可读文件="+f.canRead());
System.out.println("文件最后修改的时间="+f.lastModified());
System.out.println("-------------“);
}
File f3 = new File("temp.txt");
System.out.println(f3.getPath());
System.out.println(f3.getAbsolutePath());
File f4 = new File("c:\\test\\dabin");
f4.mkdirs();
//重命名与移动文件
//f4.renameTo(new File("C:\\test\\dabin1"))
f4.renameTo(new File("c:\\dabin1"));
File f5 = new File("c:\\test\\my");
// File[] files = f5.listFiles(new FileFilter(){
// public boolean accept(File pathname){
// return pathname.getName().endsWith(".txt");
// }
// }) ;
File[] files = f5.listFiles((pathname)->pathname.getName().endsWith(".txt"));
System.out.println("-------");
for(File f: files){
System.out.println(f.getName());
}
}
示例
/**
在指定的目录中查找文件
*/
public class FileDeom2{
public static void main(String[] args){
findFile(new File("c:\\Users\\vince\\Downloads"),".jpg");
}
//查找文件的方法
private static void findFile(File target,String ext){
if(target==null)return;
//如果文件是目录
if(target.isDirectory()){
File[] files = target.listFiles();
if(files!=null){
for(File f : files){
findFile(f,ext);//递归调用,一层层找,层数太深,会溢出
}
}
}else{
//此处表示File是一个文件
String name = target.getName().toLowerCase();
if(name.endsWith(ext)){
System.out.println(target.getAbsolutePath());
}
}
}
}
2.字节流
2.1 IO流概述
IO流:输入输出流(Input/Output)
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流
流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
2.2 IO流的分类
根据处理数据类型的不同分为:字符流和字节流
根据数据流向不同分为:输入流和输出流
字节是数据传输的基本单位,文件内容以字节为单位存储
读文件叫输入流,从文件中把数据读到程序使用输入流
写文件叫输出流,从程序中把数据写到文件使用输出流
2.3 字节输出流
OutputStream类定义
-
public abstract class **OutputStream**extends Object implements Closeable,Flushable
此抽象类是表示输出字节流的所有类的超类。输出流接受输出字节并将这些字节发送到InputStream类某个接收器,要向文件中输出,使用FileOutputStream类
2.4 字节输入流
InputStream定义:
-
public abstract class **InputStream**extends Object implements Closeable
此抽象类是表示字节输入流的所有类的超类
FileInputStream从文件系统中的某个文件中获得输入字节
/**
字节输出输入流
输出流:超类OutputStream,对文件的输出流使用子类FileOutputStream
输入流:超类InputStream,对文件的输入流使用子类FileInputStream
输入输出字节流操作原理,每次只会操作一个字节,(从文件中读取或写入)
字节操作流,默认每次执行写入操作会直接把数据写入文件,读也一样。
*/
public class ByteStreamDemo{
private static void in\(){
//1、确定目标文件
File file = new File("c:\\test\\vince.txt");
//2.构建一个文件输入流对象
try{
InputStream in = new FileInputStream(file);
//3.声明StringBuilder
StringBUilder buf = new StringBuilder();
//4.声明数组和长度
byte[] bytes = new byte[1024];//1024个字节
int len = -1 ;//表示每次读取的字节长度,-1表不存在
//5.把数据读入到数组中并返回读取的字节数,当不等-1时,表示读取到数据,等于-1表示文件已经读完
while(len = in.read(bytes)!= -1 ){
//6.根据读取到的字节数组,再转换为字符串内容,添加到StringBilder中
buf.append(new String(bytes,0,len));
//当new byte[1]时,写入中文字符会出错,有乱码,因为一个中文字符为两个字节,而一个字节构成不了一个字符
//当new byte[10]时,输出:疑是银河落九天银河落,读第二个数组时后来的字符覆盖,数组后部分的字符没被覆盖
}
//7.打印内容
System.out.println(buf);
//8.关闭输入流
in.close();
}catch (FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}
private static void out(){
//1、确定目标文件
File file = new File("c:\\test\\vince.txt");
//2.构建一个文件输出流对象
try{
OutputStream out = new FileOutputStream(file);//每一次执行会覆盖
//OutputStream out = new FileOutputStream(file,true);//append为true表示追加内容
//3.输出的内容是
String info = "疑是银河落九天\r\n";//在windows里'\r\n'表示换行
//String line = System.getProperty("line.separator");//获取换行符
//4.把内容写入到文件
out.write(info.getBytes())//不能传字符,要转换成字节
//5.关闭流
out.close();
System.out.println("write success...");
}catch (FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}
}
public static void main(String[] args){
out();
in();
}
}
字节流有乱码问题,字符流可以解决乱码问题
3.字符流
Writer
写入字符流的抽象类。子类必须实现的方法仅有writer(char[],int,int)、flush 和close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率或其他功能。
与OutputStream一样,对文件的操作使用:FileWriter类完成。
Reader
用于读取字符流的抽象类。子类必须实现的方法仅有read(char[],int,int)、flush 和close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率或其他功能。
使用FileReader类进行实例化操作。
/**
字符流:
字符输出流:Writer,对文件的操作使用子类:FileWriter
字符输入流:Reader,对文件的操作使用子类:FileReader
每次操作的单位是一个字符
特点:
文件字符操作流会自带缓存(缓存:临时写到内存,关闭时一次性写入到文件里),默认大小为1024字节,
在缓存满后,或手动刷新缓存(刷新缓存:把缓存清空,再把内容写到文件里),或关闭流时会把数据写入文件
如何选择使用字节流还是字符流:
一般操作非文本文件(图片,视频等等)时,使用字节流,操作文本文件时,建议使用字符流
*/
public class CharStreamDemo{
private static void in(){
File file = new File("c:\\test\\vince.txt");
try{
Reader in = new FileReader(file);
char[] cs = new char[1];
int len = -1;
StringBuilder buf = new StringBuilder();
while((len = in.read(cs))!=-1){
buf.append(new String(cs,0,len));
}
in.close();
System.out.println(buf);
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
private static void out(){
File file = new File("c:\\test\\vince.txt");
Writer out = new FileWriter(file);
try{
Writer out = new FileWriter(file,true);
out.write("村花到我家");
out.close();
}catch (IOException e){
e.printStackTrace();
}
}
public static void main(String[] args){
out();
}
}
案例:指定一个盘符下的文件,把该文件复制到指定的文件下。
/**
文件的复制:
从一个输入流中读取数据,然后通过输出流写入目标位置
一边读一边写
*/
public class CopyFileDemo{
private static void copy(String src,String target){
File srcFile = new File(src);
File targetFile = new File(target);
InputStream in = null;
OUtputStream out = null;
try{
in = new FileInputStream(srcFile);
out = new FileOutputstream(targetFlie);
byte[] bytes = new byte[1024];
int len = -1;
while((len = in.read(bytes))!= -1){
out.write(bytes,0,len);
}
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}finally{
try{
if(in!=null)in.close();
if(out!=null)out.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
public static void main(String[] args){
System.out.println("start copy...");
copy("c:\\mm.jpg","c:\\test\\mm.jpg");
System.out.println("copy success...");
}
}
4.字节字符转换流
转换流,可以将一个字节流转换为字符流,也可以将一个字符流转换为字节流
OutputStreamWriter:可以将输出的字符流转换为字节流的输出形式,即OutputStream→Writer InputStreamReader:将输入的字节流转换为字符流输入形式,即InputStream→Reader
字符流的本质也是通过字节实现,当我们需要在字节与字符流之间转换使用时可以方便的使用这两个类
/**
转换流
OutputStreamWriter:可以将输出的字符流转换为字节流的输出形式
InputStreamReader:将输入的字节流转换为字符流输入形式
*/
public class ChangeStreamDeom{
private static void writer(OutputStream out){
//字节流封装为字符流
Writer writer = new OutputStreamWriter(out,Charset.defaultCharset());//字符编码
try{
writer.write("疑是银河落九天");
writer.close();
}catch (IOException e){
e.printStackTrace();
}
}
private static void read(InputStream in){
Reader reader = new InputStreamReader(in,Charset.defaultCharset());//字符编码
char[] cs = new char[1024];
int len =-1;
try{
while((len = reader.read(cs))!=-1){
System.out.println(new String(sc,0,len));
}
reader.close();
}catch (IOException e){
e.printStackTrace();
}
}
public static void main(String[] args) throws FileNotFoundException{
InputStream in = new FileInputStream("c:\\test\\vince.txt");
read(in);
OutputStream out = new FileOutputStream("c:\\test\\vince.txt");
write(out);
}
}
字节,字符,流向:
5.缓冲流
对文件或其他目标频繁的读写操作,效率低,性能差,Buffered:缓冲的
使用缓冲流的好处是,能够更高效的读写信息,原理是将数据先缓冲起来,然后一起写入或者读取出来
BufferedInputStream:为另一个输入流添加一些功能,在创建BufferedInputStream时,会创建一个内部缓冲区数组,用于缓冲数据
BufferedOutputStream:通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统
BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取
BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入
字节缓冲流是为了提高字节读写的效率,因此属于在字节输入输出流的基本上通过缓冲流来做增强
字符缓冲流也是对字符流的增强,实现更高效的读取和写入操作
/**
缓存的目的:
解决在写入文件操作时,频繁的操作文件所带来的的性能降低的问题
BufferedOutputStream 内部默认的缓存大小是8KB,每次写入时存储到缓存中的byte数组里,
当数组存满时,会把数组中的数据写入文件,并且缓存下标归零
字符流
1.加入字符缓冲流,增强读取功能(readLine)
2.更高效的读取数据
FileReader:内部使用InputStreamReader(sun.nio.cs.StreamDecode),解码过程,byte->char,默认缓存大小是8K
BufferedReader:默认缓存大小是8K,但可以手动指定缓存大小,把数据直接读取到缓存中,减少每次转换过程,效率更高
BufferedWriter同上
*/
public class BufferStreamDemo{
private static void byteReader(){
File file = new File("c:\\test\\vince.txt");
try{
InputStream in = new FileInputStream(file);
//构造一个字节缓冲流
BufferedInputStream bis = new BufferedInputStream(in);
byte[] bytes = new byte[1024]:
int len = -1 ;
while((len =bis.read(bytes) )!=-1){
System.out.println(new String(bytes,0,len));
}
bis.close();
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
private static void byteWriter(){
File file = new File("c:\\test\\vince.txt");
try{
OutputStream out = new FileOutputStream(file);
//构造一个字节缓冲流
BufferedOutputStream bos = new BufferedOutputStream(out);
String info = "疑是银河落九天";
bos.write(info.getBytes());
bos.close();//自动将out关闭
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
private static void charReader(){
File file = new File("c:\\test\\vince.txt");
try{
Reader reader = new FileReader(file);
//为字符流提供缓冲,以达到高效读取的目的
BufferedReader br = new BufferedReader(reader);
char[] cs = new char[1024];
int len = -1;
while((len = br.read(cs))!=-1){
System.out.println(new String(cs,0,len);
}
br.close();
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
private static void charWriter(){
File file = new File("c:\\test\\vince.txt");
try{
Writer writer = new FileWriter(file);
//加缓存
BufferedWriter bw = new BufferedWriter(writer);
bw.write("疑是银河落九天");
bw.flush();
bw.close();
}catch (FileNotFoundException e){
e.printStackTrace();
}
catch (FileNotFoundException e){
e.printStackTrace();
}
}
public static void main(String[] args){
byteWriter();
btteReader();
}
}
6.打印流
打印
/**
打印流:很方便的进行输出
字节打印流
在字节输出时,可以增强输出功能
字符打印流
*/
public class PrintStreamDeom{
private static void charPrint(){
File file = new File("c:\\test\\vince.txt");
try{
Writer out = new FileWriter(file);
//加缓存
BufferedWriter bos = new BufferedWriter(out);
//增强打印功能
PrintWriter pw = new PrintWriter(bos);
pw.println("疑是银河落九天");
pw.close()
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
private static void bytePrint(){
File file = new File("c:\\test\\vince.txt");
try{
OutputStream out = new FileOutputStream(file);
//加缓存
BufferedOutputStream bos = new BufferedOutputStream(out);
//增强打印功能,这两层相当于游戏里套了两层buff
PrintStream ps = new PrintStream(bos);
ps.println("疑是银河落九天");
ps.close()
}catch (FileNotFoundException e){
e.printStackTrace();
}
}
public static void main(String[] args){
bytePrint();
charPrint();
}
}
7.对象流
对象流的两个类:
ObjectOutputStream将java对象的基本数据类型和图形写入OutputStream
ObjectInputStream对以前使用ObjectOutputStream写入的基本数据和对象进行反序列化
序列化一组对象:
在序列化操作中,同时序列化多个对象时,反序列化也必须按顺序操作,如果想要序列化一组对象该如何操作呢?
序列化一组对象可采用:对象数组的形式,因为对象数组可以想Object进行转型操作
transient关键字:
如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。
对象序列化就是把对象写入到输出流中,用来存储或传输;
反序列化就是从输入流中读取对象
对象序列化到文件,即把对象的属性转换成字节写入到文件
将属性读出来,重新创建对象,字节还原成对象,这就是反序列化
public class ObjectStreamDeom{
/**
反序列化的过程
从文件中把对象的内容读出来,还原成对象
*/
private static void readObject(){
File file = new File("c:\\test\\dog.obj")
try{
InputStream in = new FileInputSream(file);
ObjectInputStream ois = new ObjectInputStream(in);
Dog dog = (Dog)ois.readObject();
//读一组对象时,用对象数组接收
// Dog[] dog = (Dog[])ois.readObject();
oos.close();
System.out.println(dog);
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}catch (ClassNotFoundException e){
e.printStackTrace();
}
}
/**
对象序列化
把对象写入文件:实际写入的是类名、属性名、属性类型、属性的值等
*/
private static void writeObject(){
Dog dog = new Dog("dahuang",2,"公");
File file = new File("c:\\test\\dog.obj")
try{
OutputStream out = new FileOutputSream(file);
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(dog);
oos.close();
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
public static void main(String[] args){
writeObject();
readObject();
}
/**
序列化一组对象
*/
private static void writeObjects(){
Dog dog = new Dog("dahuang",2,"公");
Dog dog2 = new Dog("erhuang",3,"公");
Dog[] dogs = {dog,dog2};
File file = new File("c:\\test\\dog.obj")
try{
OutputStream out = new FileOutputSream(file);
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(dogs);
oos.close();
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
}
/**
如果一个类创建的对象,需要被序列化,那么该类必须实现Serializable
Serializable是一个标记接口,没有任何定义,为了告诉JVM该类对象可以被序列化
什么时候对象需要被序列化?
1.把对象保存到文件中(存储到物理介质)
2.对象需要在网络上传输时
如果对象没有实现Serializable接口,会报错:java.io.NotSerializableException
*/
public class Dog implement Serializable{
private static final long serialversionID;//序列化编号
private String name;
private int age;
private String sex;
private transient int id;//transient在序列化中被忽略
...
}
8.字节数组流
ByteArrayInputStream,(用在内存),
包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪read方法要提供的下一个字节。关闭ByteArrayInputStream无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何IOException
ByteArrayOutputStream
此类实现了一个输出流,其中的数据被写入到一个byte数组。缓冲区会随着数据的不断写入而自动增长。可使用toByteArray()和toString()获取数据。关闭ByteArrayOutputStream无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何IOException
要单个字节处理字符串跟文件无关可选用字节数组流
/**
字节数组流
基于内存操作,内部维护着一个字节数组,我们可以利用流的读取机制(一个一个读取)来处理字符串
无需关闭
*/
public class ByteArrayStreamDeom{
//
private static void byteArray(){
String s = "12345dsfs(83*^&^^%&^%)";
//跟文件无关
ByteArrayInputStream bais = new ByteArrayInputStream(s.getBytes());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int curr = -1;//每次读取的字节
while((curr =bais.read())!=-1){//read()每次读一个字节
if((curr>=65 && curr<=90)|| (curr>=97&&curr<=122){//提取字母
baos.write(curr);
}
}
//此时无需关闭,原因,字节数组流是基于内存的操作流
System.out.println(baos.toString());
}
public static void main(String[] args){
byteArray();
}
}
9.数据流
DataInputStream
数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本java数据类型。应用程序可以使用数据输出流写入稍后有数据输入流读取的数据。DataInputStream对于多线程访问不一定是安全的。线程安全是可选的,它由此类方法的使用者负责。
DateOutputStream
数据输出流允许应用程序以适当方式将基本java数据类型写入输出流中。然后,应用程序可以使用数据输入流将数据读入。
专门用来读写各种数据的,比如(int,char,long等),一定要注意DataOutputStream与DataInputStream配合使用,而且二者读写的顺序要一样
/**
数据流
与机器无关的操作Java的基本数据类型
*/
public class DataStreamDeom{
private static void write(){
File file = new File("c:\\test\\vince.txt");
try{
OutputStream out = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(out);
DataOutputStream dos = new DataOutputStream(bos);
dos.writeInt(10);//写入4个字节
dos.writeByte(1);//写入1个字节
dos.writeUTF("中");
dos.close();
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
private static void read(){
File file = new File("c:\\test\\vince.txt");
try{
InputStream in = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(in);
DataInputStream dis = new DataInputStream(bis);
//按写入的顺写读取
int num = dis.readInt();
byte b = dis.readByte();
String s = dis.readUTF();
System.out.println(num+","+b+","+s);
dis.close();
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
public static void main(String[] args){
}
}
案例:实现文件分割合并(把大文件分割成若干个小文件,视频的话,加载完小文件,就可以播放视频小文件)
/**
*/
public class FIleDivisionMergeDeom{
/**
文件的分割
targetFile要分割的目标文件
cutSize 每个文件分割的大小
*/
private static void division(File targetFile,long cutSize){
if(targetFile==null)return;
//计算总分割的文件数
int num = targetFile.length()%cutSize==0?(int)(targetFile.length()/cutSize):(int)(targetFile.length()/cutSize+1);
try{
//构造一个文件输入流
BufferedInputStream in = new BufferedInputStream(new FileInputStream(targeFile));
BufferedOutputStream out = null;
byte[] bytes = null;//每次要读取的字节数
int len = -1;
int count = 0;//每一个文件要读取的次数
//循环次数为生成文件的个数
for(int i = 0;i<num;i++){
out = new BufferedOutputStream(new FileOutputStream(new File("c:\\test\\"+(i+1) +"-temp-"+targetFile.getName())));
//
if(cutSize<=1024){
bytes = new byte[(int)cutSize];
count = 1;
}else{
bytes = new byte[1024];
cont = (int)cutSize/1024;
}
while(count>0 && (len=in.read(bytes))!=-1){
out.write(bytes,0,len);
out.flush();
count--;
}
//计算每个文件大小除于1024的余数,决定是否要再读取一次
if(cutSize%1024!=0){
bytes = new byte[(int)cutSize%1024];
len = in.read(bytes);
out.write(bytes,0,len);
out.close();
}
}
in.close();
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
/**
文件合并
*/
private static void merge(Enumeration<InputStream> es){
//构造一个合并流
SequenceInputStream sis = SequenceInputStream(es);
try{
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("c:\\test\\xxx.avi"))
byte[] bytes = new byte[1024];
int len = -1 ;
while((len=sis.read(bytes))!=-1){
bos.write(bytes,0,len);
bos.flush();
}
bos.close();
sis.close();
System.out.println("合并完成...");
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
public static void main(String[] args){
// File file = new File("c:\\xxx.avi");
// division(file,1024*1024*20);
try{
InputStream in1 = new FileInputStream(new File("c:\\test\\1-temp-xxxx.avi"));
InputStream in2 = new FileInputStream(new File("c:\\test\\2-temp-xxxx.avi"));
InputStream in3 = new FileInputStream(new File("c:\\test\\3-temp-xxxx.avi"));
InputStream in4 = new FileInputStream(new File("c:\\test\\4-temp-xxxx.avi"));
InputStream in5 = new FileInputStream(new File("c:\\test\\5-temp-xxxx.avi"))
//集合工具类,内部实现使用了数组
Vector<InputStream> v = new Vector<InputStream>();
v.add(in1);
v.add(in2);
v.add(in3);
v.add(in4);
v.add(in5);
Enumeration<InputStream> es = v.element();
merge(es);
}catch (FileNotFoundException e){
e.printStackTrace();
}
}
}
10.合并流、字符串流、管道流
10.1合并流
SequenceInputStream表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,以此类推,直到到达包含的最后一个输入流的文件末尾为止。
10.2 字符串流
StringReader其源为一个字符串的字符流
StringWriter一个字符流,可以用其回收在字符串缓冲区的输出来构造字符串。关闭StringWriter无效。此类中的方法在关闭该流后仍可被调用,而不会产生任何IOException
/**
字符串流:以一个字符串为数据源,来构造一个字符流
作用:在WEB开发中,我们经常要从服务器上获取数据,数据的返回格式通常是一个字符串(XML,JSON),我们需要把这个字符串构造成一个字符流,然后再用第三方的数据解析器来解析数据。
StringWriter,写入字符串的流
*/
public class StringDeom{
private static void stringReader(){
//计算多少个单词
String info = "good good study day day up";
StringReader sr = new StringReader(info);//info放到StringReader,可以以流的方式处理
//流标记器
StreamTokenizer st = new StreamTokenizer(sr);
int count = 0;
while(st.ttype != StreamTokenizer.TT_EOF){//ttype类型,TT表结尾
try{
if(st.nextToken() == StreamTokenizer.TT_WORD){//nextToken下一个标记
count++;
}
}catch (IOException e){
e.printStackTrace();
}
}
sr.close();
System.out.println("count="+count);
}
public static void main(String[] args){
}
}
10.3管道流
管道输入流应该连接到管道输出流:管道输入流提供要写入管道输出流的所有数据字节。通常,数据有某个线程从PipedInputStream对象读取,并由其他线程将其写入到相应的PipedOutputStream。不建议对这两个对象尝试使用单个查询,因为这样可能死锁线程。管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开。如果想连接管道输出流提供商数据字节的线程不再存在,则认为该管道已损坏。
/**
管道流测试;一个线程写入,一个线程读取
作用:用于线程之间的数据通讯
*/
access:通道
RandomAcceessFile是IO包的类(类似工具类),从Object直接继承而来。只可以对文件进行操作,可以对文件进行读取和写入。当模式W为r时,当文件不存在时会报错,当模式为rw时,当文件不存在时,会自己创建文件,当文件已经存在时不会对原有文件进行覆盖。
RandomAcceessFile有强大的文件读写功能,其内部是大型byte[],可以通过seek(),getFilePointer()等方法操作的指针,方便对数据进行写入与读写。还可以对基本数据类型进行直接的读和写操作。
RandomAcceessFile的绝大多数功能,已经被JDK1.4的nio的“内存映射文件(memory-mapped files)”给取代了,你该考虑一下是不是用“内存映射文件”来代替RandomAcceessFile了。
/**
*/
public class RandomAccessFileDeom{
public static void main(String[] args){
try{
//读取文件
RandomAccessFile r = new RandomAccessFile("c:\\test\\home.jpg","r");//"r"模式读
//写入文件
RandomAccessFile w = new RandomAccessFile("c:\\test\\home.jpg","rw");//"rw"模式读写
byte[] bytes = new byte[1024];
int len = -1;
while((len=r.read(bytes))!=-1){
w.write(bytes,o,len);
}
w.close();
r.close();
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
System.out.println("copy seccess...");
}
}
12.Properties文件操作
Properties(java.util.Properties)(工具类),主要用于读取java 的配置文件,各种语言都有自己所支持的配置文件,配置文件中很多变量是经常改变的,这样做也是为了方便用户,让用户能够脱离程序本身去修改相关的变量设置。
它提供了几个主要的方法:
method | 说明 |
---|---|
getProperty(String key) | 用指定的键在此属性列表中搜索属性。也就是通过参数key,得到key所对应的value |
load(InputStream inStream) | 从输入流中读取属性列表(键和元素对)。通过对指定的文件(比如说上面的test.properties文件)进行装载来获取该文件中的所有键-值对。以供getProperty(String key)来搜索 |
setProperty(String key,String value) | 调用Hashtable的方法put。它通过调用基类的put方法来设置键-值对 |
store(OutputStream out,String value) | 以适合使用load方法加载到Properties表中的格式,将此Properties表中的属性列表(键和元素对)写入输出流。与load方法相反,该方法将键-值对写入到指定的文件中去 |
clear() | 清除所有装载的键-值对。该方法在基类中提供 |
/**
Properties:
Properties可以用来做配置文件
javaweb javaee 开发中通常会用到
便于修改,便于维护
ResouceBundle只读
Properties 可读可写
*/
public class PropertiesDeom{
public static String version="";
public static String username="";
public static String password="";
//静态代码块,只会执行一次,加载类就执行static块
static{
readConfig();
}
/**
读取properties配置文件
*/
private static void readConfig(){
//1.创建Properties对象
Properties p = new Properties();
try{
//2.构建配置文件的输入流对象
InputStream inStream = new FileInputStream("config.properties");
//3.在properties加载输入流的文件
p.load(inStream);//加载文件,将数据读取到Properties类里
//4.从properties中获取数据
version = p.getProperties("app.version");
username = p.getProperties("db.username");
password = p.getProperties("db.password");
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
/**
对属性文件的写操作
*/
private static void writeConfig(String version,String username,String password){
//1.创建Properties对象
Properties p = new Properties();
//2.添加键值对数据
p.put("app.version",version);
p.put("db.username",username);
p.put("db.password",password);
try{
//通过当前线程的类加载器对象,来加载指定包下的配置文件
//InputStream out = Thread.currentThread().getContextClasssLoader().getResourceAsStream("config.properties");
//3.构建一个配置文件的输出流对象
OutputStream out = new FileOutputStrea("config.properties");//注意配置文件的路径
//4.写文件
p.store(out,"update config")
//5.关闭输出流
out.close();
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
public static void main(String[] args){
//readConfig();
writeConfig("2","vince","123344");
System.out.println(PropertiesDeom.version);
System.out.println(PropertiesDeom.username);
System.out.println(PropertiesDeom.password);
}
}
config.properties文件:
app.version=1
db.username=admin
db.password=123
13.文件压缩与解压缩
Java中实现zip的压缩与解压缩
ZipOutputStream
实现文件的压缩
一个文件条目对应一个ZipEntry对象
method | 说明 |
---|---|
ZipOutputStream(OutputStream out) | 创建新的ZIP输出流 |
void putNextEntry(ZipEntry e) | 开始写入新的ZIP文件条目并将流定位到条目数据的开始处 |
ZipEntry(String name) | 使用指定名称(名字唯一)创建新的ZIP条目 |
ZIpInputStream
实现文件的解压
method | 说明 |
---|---|
ZipInputStream(InputStream in) | 创建新的ZIP输入流 |
ZipEntry getNextEntry() | 读取下一个ZIP文件条目并将该条目数据的开始处 |
压缩文件的本质是把存储文件的二进制,用一定的规则表示来减少原文件的二进制长度,从而减少文件的大小,而这个规则不同的压缩软件规则不同
/**
压缩与解压缩
*/
public class Deom{
/**
压缩
*/
private static void compression(String zipFileName,File targetFile){
System.out.println("压缩中...");
try{
//1.创建Zip输出流,要生成的压缩文件
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFileNname));
//2.套入缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(out);
//3.zip方法压缩
zip(out,targetFile,targetFile.getName(),bos);
//4.关闭流
bos.close();
out.close();
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
System.out.println("压缩完成...");
}
//zip
private static void zip(ZipOutputStream zOut,File targetFile,String name,BufferedOutputStream bos) throws IOException
{
//1.判断,如果是目录
if(targetFile.isDirectory()){
//2.列出目录下的File类型的名字
File[] files = targteFile.listFiles();
//3.判断文件是否为空
if(files.length==0){//空文件夹
//处理空目录,压缩
zOut.putNextEntry(new ZipEntry(name+"/"));
}
//处理目录下的目录...
for(File f: files){
//递归处理
zip(zOut,f,name+"/"+f.getName(),bos);
}
}else{
//4.压缩文件
zOut.putNextEntry(new ZipEntry(name));
//5.构建目标文件的输入流对象
InputStream in = new FileInputStream(targetFile);
//6.套入缓冲输入流
BufferedInputStream bis =new BufferedInputStream(in);
byte[] bytes = new byte[1024];
int len = -1 ;
//7.读取流数据
while((len=bis.read(btyes))!=-1){
bos.write(bytes,0,len);
}//8.关闭流
bis.close();
}
}
/**
解压
*/
private static void decompression(String targetFileName,String parent)
try{
//1.构造解压的输入流
ZipInputStream zIn = new ZipInputStream(new FileInputStream(targetFileName));
ZipEntry entry = null;
File file = ;
//2.判断有值且是文件
while((entry = zIn.getNextEntry())!= null && !entry.isDirectory()){
file = new File(parent,entry.getName());//父目录,子目录
if(!file.exists()){
//创建此文件的上级目录
new File(file.getParent()).mkdirs()
}
OutputStream out = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(out);
byte[] bytes= new byte[1024];
int len = -1 ;
while((len = zIn.read(bytes))!=-1){
bos.write(bytes,0,len);
}
bos.close();
System.out.println(file.getAbsultePath()+"解压成功");
}
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
public static void main(String[] args){
//compression("c:\\test.zip",new File("c:\\test"));
decompression("c:\\test.zip","c:\\Download\\");
}
}
IO流常用类结构:
14.装饰者模式
解决因继承而类爆炸
15.常见字符编码
在程序中如果没有处理好字符的编码,就有可能出现乱码问题,
在计算机世界里,任何的文字都是以指定的编码方式存在的。
常见编码有:ISO8859-1、GBK/GB2312、unicode、UTF
iso8859-1:编码属于单字节编码,最多只能表示0-255的字符范围,主要在英文上应用
GBK/GB2312:中文的国际编码,专门用来表示汉子,是双字节编码
unicode:java中就是使用此编码方式,也是最标准的一种编码,是使用16进制表示的编码。但此编码不兼容iso8859-1编码。
UTF:由于unicode不支持iso8859-1编码,而且容易占用更多的空间,而且对于英文字母也需要使用两个字节编码,这样使用unicode不便于传输和存储,因此产生了utf编码,utf编码兼容iso8859-1编码,也可以用来表示所有语言字符,不过utf是不定长编码,每个字符的长度从1-6个字节不等,一般在中文网页中使用此编码,因为这样可以节省空间
造成乱码的根本原因:
-
程序使用的编码与本机的编码不统一
-
在网络中,客户端与服务端编码不统一(WEB开发中出现的乱码情况)
/**
*/
public class CodeDeom{
public static void main(String[] args){
//通常产生乱码的情况是,两个不兼容的编码相互转换
String info = "疑是银河落九天";
try{
//将gb2312转换成iso8859-1
String newInfo = new String(info.getBytes("gb2312"),"iso8859-1");
System.out.println(newInfo);//乱码???????
String newInfo2 = new String(newInfo.getBytes("iso8859-1"),"gb2312");
System.out.println(newInfo2);
}catch (UnsupportedEncodingException e){
e.printStackTrace();
}
}
}
16.New IO
16.1为什么要使用NIO?
NIO是JDK1.4加入的新包,NIO的创建目的是为了让Java程序员可以实现高速I/O而无需编写自定义的本机代码。NIO将最耗时的I/O操作(即填充和提取缓冲区)转移回操作系统,因而可以极大地提高速度。
16.2流与块的比较
原来的I/O库(在java.io.*中)与NIO最重要的区别是数据打包和传输的方式,原来的I/O以流的方式处理数据,而NIO以块的方式处理数据
面向流的I/O系统一次一个字节地处理数据。一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。不利的一面是,面向流的I/O通常相当慢
一个面向块的I/O系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块。按块处理比按(流式的)字节处理数据要快得多。但是面向块的I/O缺少一些面向流的I/O所具有的优雅性和简单性
16.3缓冲区
在NIO库中,所有数据都是用缓冲区处理的。 在读取数据时,他是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区中的。任何时候访问NIO中的数据,您都是将它放到缓冲区中。
缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。但是一个缓冲区不仅仅是一个数组。缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程
16.4缓冲区类型
最常用的缓冲区类型是ByteBuffer。一个ByteBuffer可以在其底层字节数组上进行get/set操作(即字节的获取和设置)。ByteBuffer不是NIO中唯一的缓冲区类型。事实上,对于每一个基本java类型都有一种缓冲区类型
类型 |
---|
ByteBuffer |
CharBuffer |
ShortBuffer |
IntBuffer |
LongBuffer |
FloatBuffer |
DoubleBuffer |
public class NIODeom{
public static void main(String[] args){
//创建一个字节缓冲区,申请内存空间为8字节,分配缓冲区
ByteBuffer buf = ByteBuffer.allocate(8);
System.out.println("position=",buf.position()); //位置
System.out.println("limit=",buf.limit()); //默认limmit=capacity
System.out.println("capacity=",buf.capacity());
//向缓冲区写入数据
buf.put((byte)10);
buf.put((byte)20);
buf.put((byte)30);
buf.put((byte)40);
buf.put((byte)50);
System.out.println("position=",buf.position()); //位置
System.out.println("limit=",buf.limit());
System.out.println("capacity=",buf.capacity());
//缓冲区反转(截取),数组可能没有满,要限定元素所在范围
buf.flip();//limit=position,,position=0,
System.out.println("----------------------");
System.out.println("position=",buf.position()); //位置
System.out.println("limit=",buf.limit());
System.out.println("capacity=",buf.capacity());
//hasRemaining,告知在当前位置和限制直接是否有元素
if(buf.hasRemaining()){
//remaining,返回当前位置与限制数之间的元素数,limit-position
for(int i=0;i<buf.remaining();i++){
byte b = buf.get(i);
System.out.println(b);
}
}
}
}
16.5通道:Channel
Channel是一个对象,可以通过它读取和写入数据。拿NIO与原来的I/O做个比较,通道就像是流。
正如前面提到的,所有数据都通过Buffer对象来处理。你永远不会将字节直接写入通道中,相反,你是将数据写入包含一个或者多个字节的缓冲区。同样,你不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节
NIO可以提高数据的读取效率的根本原因是利用了操作系统(如Linux)底层的IO多路复用机制实现
/**
IO操作的性能比较
1、内存映射最快
2、NIO读写文件
3、使用了缓冲的IO流(加了Buffered的)
4、无缓冲的IO流
*/
public class CopyFileDeom{
private static void randomAccessFileCopy() throws Exception{
RandomAccessFile in = new RandomAccessFile("c:\\xx\\lyh.txt",r);
RandomAccessFile out = new RandomAccessFile("c:\\xx\\lyh.txt",rw);
FileChannel fcIn = in.getChannel();
FileChannel fcOut = out.getChannel();
long size = fcIn.size();//输入流的字节大小
//获取输入流的缓冲区,以映射方式
MappedByteBuffer inBuf = fcIn.map(MapMode.READ_ONLY,0,size);//输入流的映射,映射到缓冲区
//获取输出流的缓冲区
MappedByteBuffer outBuf = fcOut.map(MapMode.READ_WRITE,0,size);
for(int i= 0;i<size;i++){
outBuf.put(inBUf.get());//装到缓冲区
}
//关闭(关闭通道时会写入数据块)
fcIn.close();
fcOut.close());
in.close();
out.close();
System.out.println("copy seccess...");
}
/**
通过文件通道实现文件的复制
*/
private static void copyFile throws Exception(){
//1.创建一个输入文件的通道
FileChannel fcIn = new FileInputStream("c:\\xx\\lyh.txt").getChannel;
//创建一个输出文件的通道
FoleChannel fcOut = new FileOutputStream("c:\\xx\\lyh1.txt").getChannel();
//2.创建字节数组缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);
while(fcIn.read(buf)!=-1){//4.将输入流通道fcIn的数据存到buf缓冲区里
buf.flip();
fcOut.write(buf);//再从缓冲区里写入到输出流通道
buf.clear();//缓冲区清空
}
//5.关闭通道
fcIn.close();
fcOut.close();
System.out.println("copy seccess...");
}
public static void main(String[] args){
try{
coptFile();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
JDK1.7引入了新的IO操作类,java.nio.file包下,Java NIO Path接口和files类(工具类)
16.6 Path接口
1、Path表示的是一个目录名序列,其后还可以跟着一个文件名,路径中第一个部件是根部件时就是绝对路径,例如 / 或C:\,而允许访问的根部件取决于文件系统;
2、以根部件开始的路径是绝对路径,否则就是相对路径;
3、静态的(工具类)Paths.get方法接收一个或多个字符串,字符串之间自动使用默认文件系统的路径分割符连接起来(Unix是 / ,Windows是 \ ),这就解决了跨平台的问题,接着解析连接起来的结果,如果不是合法路径就抛出InvalidPathException异常,否则就返回一个Path对象;
16.7 Files工具类
1.读写文件
method | 说明 |
---|---|
Static path write(Path path,byte[] bytes,OpenOption... options) | 写入文件 |
Static byte[] readAllBytes(Path path) | 读入文件中的所有字节 |
2.复制、剪切、删除
method | 说明 |
---|---|
Static path copy(Path source,Path target,CopyOption... options) | |
Static path move(Path source,Path target,CopyOption... options) | |
Static void delete(Path path) | 如果path不存在文件将抛出异常,此时调用下面的比较好 |
Static boolean deleteIfExists(Path path) |
补充:
OpenOption的options |
---|
APPEND 如果文件打开 访问,则字节将被写入文件的末尾而不是开头。 |
CREATE 创建一个新文件(如果不存在)。 |
CREATE_NEW 创建一个新的文件,如果该文件已经存在失败。 |
DELETE_ON_CLOSE 关闭时删除。 |
DSYNC 要求将文件内容的每次更新都与底层存储设备同步写入。 |
READ 打开阅读权限。 |
SPARSE 稀疏文件 |
SYNC 要求将文件内容或元数据的每次更新都同步写入底层存储设备。 |
TRUNCATE_EXISTING 如果文件已经存在,并且打开 访问,则其长度将截断为0。 |
WRITE 打开以进行写入。 |
CopyOption的 options |
---|
ATOMIC_MOVE 将文件作为原子文件系统操作移动。 |
COPY_ATTRIBUTES 将属性复制到新文件。 |
REPLACE_EXISTING 替换现有文件(如果存在)。 |
3.创建文件和目录
method | 说明 |
---|---|
Files.createDirectory(path); | 创建新目录,除了最后一个部件,其他必须是已存在的 |
Files.createDirectories(path); | 创建路径中的中间目录,能创建不存在的中间部件 |
Files.createFile(path); | 创建一个空文件,检查文件存在,如果已存在则抛出异常而检查文件存在时原子性的,因此在此过程中无法执行文件创建操作 |
Path new Path = Files.createTempFile(dir,preflx,suffix); | 添加前/ 后缀创建临时文件 |
Path new Path = Files.createDirectory(dir,prefix); | 添加前创建临时目录 |
/**
JDK1.7新的文件操作类
Path接口
Paths类
Files类
*/
public class PathFilesDeom{
public static void main(String[] args){
File file = new File("c:\\test\\lyh.txt");
//path
Path p1 = Paths.get("c:\\test","lyh.txt");//所有文件用path路径解决
System.out.println(p1);//输出路径①:c:\test\lyh.txt
Path p2 = file.toPath();
System.out.println(p2);//输出路径②:c:\test\lyh.txt
Path p3 = FileSystems.getDefault().getPath("c:\\test","lyh.txt")//输出路径③:c:\test\lyh.txt
//Files工具类
Path p4 = Paths.get("c:\\test\\lyh.txt");
String info = "疑是银河落九天";
try{
Files.write(p4,info.getBytes("gb2312"),StandardOpenOption.APPEND);
}catch (IOException e){
e.printStackTrace();
}
//读取文件
try{
byte[] bytes Files.readAllBytes(p4);
System.out.println(new String(bytes));
}catch (IOException e){
e.printStackTrace();
}
//复制文件
try{
Files.copy(p3,Paths.get("c:\\test\\vince.txt"),StandardCopyOption.REPLACE_EXISTING);
}catch (IOException e){
e.printStackTrace();
}
//移动文件
try{
Files.move(p3,Paths.get("c:\\test\\vince.txt"),StandardCopyOption.REPLACE_EXISTING);
}catch (IOException e){
e.printStackTrace();
}
//删除文件
try{
Files.delete(p3);//Static boolean deleteIfExists(Path path)
}catch (IOException e){
e.printStackTrace();
}
//创建新目录,除了最后一个部件,其他必须是已存在的
try{
Files.createDirectory(Paths.get("c:\\vince.txt");
}catch (IOException e){
e.printStackTrace();
}
//创建文件
try{
Files.createFile(Paths.get("c:\\test\\vin.txt");
}catch (IOException e){
e.printStackTrace();
}
//添加前/ 后缀创建临时文件或临时目录
Path new Path = Files.createTempFile(dir,preflx,suffix);
Path new Path = Files.createDirectory(dir,prefix);
}
}
参考资料:
JDK1.8帮助文档
■免责申明
⒈ 本站是纯粹个人学习网站,与朋友交流共赏,不存在任何商业目的。
⒉ 本站利用了部分网络资源,版权归原作者及网站所有,如果您对本站所载文章及作品版权的归属存有异议,请立即通知我们,我们将在第一时间予以删除,同时向你表示歉意!