一、File类
-
file类:表示文件和目录路径名的抽象表示形式。
file类可以实现文件的创建、删除、重命名、得到路径、创建时间等等,是唯一与文件本身有关的操作类。
-
file常见的方法
package com.haitaiinc.file.io;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
/**
* @Description
* File 包括文件和文件夹
* @author qr.huang
* @date 2019年10月11日
*/
public class FileDemo {
public static void main(String[] args) {
//Flie表示一个文件或目录
//C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning (\表示转义字符,所以需要两个反斜杠)
//或者可以用File.separator表示斜杠
File f1 = new File("C:/Users/雨后黄昏sky/OneDrive/桌面/Learning/OftenClass.md");
//导包操作:ctrl + shift + o
File f2 = new File("C:"+File.separator+"Users"+File.separator+"雨后黄昏sky"+File.separator+"OneDrive"+File.separator+"桌面"+File.separator+"Learning");
if(!f1.exists()) {//判断文件是否存在
try {
f1.createNewFile(); //如果不存在我们创建一个文件
System.out.println("文件创建成功!");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//f1.isFile() :是否为文件
System.out.println("是否为文件夹:"+f1.isDirectory());
//delete可以删除单个文件,但是不能删除有文件的文件夹
File f3 = new File("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning");
boolean b = f3.delete();
System.out.println(b);
//.list()列出当前目录中的所有文件名
String [] name =f3.list();
System.out.println(Arrays.toString(name));
//列出所有的文件,以file对象返回
File[] fs = f3.listFiles();
for (File f : fs) {
System.out.println("文件长度="+f.length());
System.out.println("文件名="+f.getName());
System.out.println("相对路径="+f.getPath());
System.out.println("绝对路径="+f.getAbsolutePath());//存在计算机的具体地址
System.out.println("是否为隐藏文件="+f.isHidden());
System.out.println("是否可读"+f.canRead());
//转化日期格式
Date date = new Date(f.lastModified());
DateFormat df = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
System.out.println("最后修改时间="+df.format(date));
System.out.println("---------------------------");
}
//系统默认以工程当前目录名作为首选路径,test.txt相当于自己文件夹下的文件
File f4 = new File("test.txt");
System.out.println("相对路径="+f4.getPath());
System.out.println("绝对路径="+f4.getAbsolutePath());
//创建一个文件夹
File f5 = new File("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\dabin");
f5.mkdirs();//比mdkir功能更加丰富,可以连续创建一连串没有的目录,比如Learning这个目录不存在也是可以创建的
//修改文件名,也可以移动到别的文件夹下(重命名和移动文件)
f5.renameTo(new File("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\binbin"));
File f6 = new File("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning");
/*File[] files = f6.listFiles(new FileFilter() {
//输出文件拓展名为.docx的文件
@Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(".docx");
}
});*/
File[] files = f6.listFiles((pathname)->pathname.getName().endsWith(".docx"));//使用Lambda表达式的写法
for(File f : files) {
System.out.println(f.getName());
}
}
}
1、查找指定目录下的指定文件
package com.haitaiinc.file.io;
import java.io.File;
import java.util.Arrays;
/**
* @Description
* 在指定的目录中查找文件
* @author qr.huang
* @date 2019年10月11日
*/
public class FileDemo1 {
public static void main(String[] args) {
findFile(new File("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning"),".md");
}
//查找文件的方法,target文明路径,ext拓展名
private static void findFile(File target,String ext) {
if(target==null)return;
if(target.isDirectory()) {//判断传过来的路径是否为目录
File[] files = target.listFiles();
//如果查询c盘可能不能访问系统文件
if(files!=null) {
for(File f:files) {
findFile(f, ext);//进行递归调用,一层一层进去
}
}
}else{
//此处表示File是一个文件
String name = target.getName().toLowerCase();//把字符串都转为小写
if(name.endsWith(ext))
//判断name的结尾是不是传过来的拓展名
{
System.out.println(target.getAbsolutePath());//输出路径
System.out.println(name);//输出文件名
System.out.println("--------------------------------");
}
}
}
}
二、字节
-
io流概述
io流:输入输出流(Input / Output)
-
流是一组有顺序的,有起点和重点的字节集合,是对数据传输的总称或抽象,即数据在两设备间的传输称为流。
-
流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
-
Io流的分类:
根据处理数据类型的不同分为:字节流和字符类。
根据数据流向不分为:输入流和输出流。(针对的程序,从程序把数据写入文件,叫输出流;从程序中把数据从文件读出来,交输入流。)
判断是输入流还是输出流。是以程序为中心。可以理解为,程序输出数据和程序输入数据。
-
-
字节输出流
OutputStream类定义
public abstract class OutputStream extends Object implements Closeable,Flushable
-
此抽象类是表示输出字节流的所有类的超类。输出流接受输出字节并将这些字节发送到inputStream类某个接收器。
-
要向文件输出,使用FileOutputStream类
-
-
字节输入流
InputStream类定义
public abstract class InputStream extends Object implements Closeable
- 次抽象类是表示字节输入流的所有超类
- FileInputStream从文件系统中的某个文件中获得输入字节。
-
输入输出字节流的操作原理,每次只会操作一个字节(从文件读取或写入)
package com.haitaiinc.file.io;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* @Description
* 字节输出输入流
* 输出流:超类OutputStream ,对文件的输出流使用子类FileOutputStream
* 输入流: 超类InputStream,对文件的输入使用子类FileInputStream
* 字节操作流,默认每次执行写入操作,会直接把数据写入文件。
* @author qr.huang
* @date 2019年10月12日
*/
public class ByteStreamDemo {
public static void main(String[] args) {
out();
in();
}
private static void in() {
//0、确定目标文件
File file = new File("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\OftenClass.md");
//1、构建一个文件输入流对象
try {
InputStream in = new FileInputStream(file);
//2、定义一次读取多少个字节
byte[] bytes = new byte[1024];//如果每次读取的字节太少,如一个字节,(一个汉字两个字节),会出现乱码
StringBuilder buf = new StringBuilder();
int len = -1;//每次实际读取的字节数
//3、读取文件
try {
//把数据读取到数组中,并返回字节数,当不等-1,表示读到数据,若返回数据,表示已经读完
while((len =in.read(bytes))!=-1){
//根据读取到的字节数组,再转换为字符串内容,添加到StringBuilder中
buf.append(new String(bytes,0,len));//从0-len长度读取,否者会溢出
}
//打印内容
System.out.println(buf);
//4、关闭输入流
in.close();
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
private static void out() {
//0、确定目标文件
File file = new File("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\OftenClass.md");
//1、构建一个文件输出流对象
try {
OutputStream out = new FileOutputStream(file,true);//append为True表示追加内容
//2、输出的内容
String info = "小乔六岁哗啦啦\r\n";//\r\n表示换行
String line = System.getProperty("line.separaor");//表示获取换行符,也可以吧line拼到输入的字段中
//3、把内容写入文件
try {
out.write(info.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
//4、关闭流
try {
out.close();
System.out.println("write success");
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
三、字符流
-
Writer
-
写入字符流的抽象类。子类必须实现的方法仅有write(char[],int,int)、flush()和close().但是,多数子类将重此处定义的一些方法,以提供跟高的效率或其它功能。
-
与OutputStream一样,对文件的操作使用FlieWriter类完成。
-
-
Reader
- 用于读取字符流的抽象类。子类必须实现的方只有read(char[],int,int)和close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率或其它功能。
- 使用FileReader类进行实例化操作。
package com.haitaiinc.file.io;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
/**
* @Description
* 字符流
* 字符输入流:Writer,对文件的操作使用子类FileWriter
* 字符输出流:Reader,对文件的操作使用子类FileReader
* 每次操作的单位是一个字符,
* 文件字符操作流会自带缓存,默认大小为1024字节,在缓存满后或手动刷新或关闭流时会把数据写入文件。
* 一般使用非文本文件使用字节流,一般使用文本文件使用字符流
* 字符流的内部实现还是字节流
* @author qr.huang
* @date 2019年10月13日
*/
public class CharStreamDemo {
public static void main(String[] args) {
//out();
in();
}
private static void in() {
File file = new File("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\OftenClass.md");
try {
Reader in = new FileReader(file);
char [] cs = new char[1];
int len = -1;
StringBuilder buf = new StringBuilder();
try {
while((len=in.read(cs))!=-1) {
buf.append(new String(cs,0,len));
}
in.close();
System.out.println(buf);
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
private static void out() {
File file = new File("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\OftenClass.md");
try {
Writer out = new FileWriter(file,true);
out.write(",村花到我家");
System.out.println("write success!");
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
-
字节流和字符类的区别
-
在所有流操作里,字节流永远是最基础的。任何基于字节的操作都是正确的。
-
无论是使用文本文件还是二进制文件,如果确认流里面只有可打印的字符,包括英文和各种国家的文字,也包括中文,那么可以考虑用字符流。
-
由于编码的不同,多字节的字符可能占用多个字节。比如GBK的汉字就占用两个字节,而UTE-8的汉字就占3个字节。所以,字符流是根据指定的编码,将一个或多个字节转化为java里面的unicode的字符,然后进行操作。
-
字符操作一般用Writer,Reader等,字节操作一般都是InputStream,OutputStream以及各种包装类,比如BufferedInputStream和BufferOutputStream等
-
总结:如果确认要处理的流逝可以打印的字符,那么使用字符流会看上去简单一点。如果不确认,那么用字节流总是不会有错的。
-
1、指定一个盘符下的文件,把该文件复制到指定目录下
package com.haitaiinc.file.io;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* @Description
* 文件的复制原理
* 从一个输入流中读取输出数据,然后通过输出流下入目标位置
* 一边读一边写
* @author qr.huang
* @date 2019年10月13日
*/
public class CopyFileDemo {
public static void main(String[] args) {
System.out.println("copy star");
copy("C:\\Users\\雨后黄昏sky\\OneDrive\\图片\\Oracle\\查询表结构.jpg","C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\查询表结构.jpg");
System.out.println("copy success");
}
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(targetFile);
byte[] bt = new byte[1024*1024];
int len = -1;
try {
while((len=in.read(bt))!=-1) {
out.write(bt,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally{
try {
if(in!=null) in.close();
if(out!=null) out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
四、字节字符转换流
-
转换流,可以将一个字节流转换为字符流,也可以将一个字符流转换为字节流。
-
OutputStreamWriter:可以将输出的字符流转换为字节流的输出形式。
-
InputStreamReader:可以将输入的字节流转换为字符输入形式。
-
/**
*
*/
package com.haitaiinc.file.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.Charset;
/**
* @Description
* InputStreamReader:可以将输入的字节流转换为字符输入形式。
* @author qr.huang
* @date 2019年10月13日
*/
public class ChangeStream {
public static void main(String[] args) throws IOException {
OutputStream out = new FileOutputStream("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\OftenClass.md");
write(out);
InputStream in = new FileInputStream("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\OftenClass.md");
read(in);
}
private static void read(InputStream in) {
Reader reader = new InputStreamReader(in, Charset.forName("UTF-8"));//规定转为UTF-8可能会出现乱码
char[] cs = new char[1];
int len = -1;
try {
while((len=reader.read(cs))!=-1) {
System.out.print(new String(cs,0,len));
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void write(OutputStream out) throws IOException {
Writer writer = new OutputStreamWriter(out,Charset.defaultCharset());
writer.write("欢迎到我家玩耍!\r\n");
writer.close();
}
}
五、缓冲流
-
概念
对文件或其他目标的频繁读写操作,效率低,性能差。
使用缓冲流的好处是,能够高效的读写信息,原理是将数据先缓冲起来,然后一起写入或读取。
-
BufferedInputStream:为另一个输入流添加一些功能,在创建BufferedInputStream时,会创建一个内部缓冲去数组,用于缓冲数据。
-
BufferedOutputStream:通过设置这种输入流,应用程序就可以将哥哥字节写入底层输出流中,而不必针对每次字节写入调用底层系统
-
BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而数显字符、数组和行的高效读取。
-
BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
1、加入字符缓冲流能,能增强读取功能(ReadLine)
2、更高效读取数据
3、FileReader:内部使用InputStreamReader(sun,nio.cs.StreamDecoder),解码过程,byte->char,默认大小是8k;
而使用BufferedReader:默认缓存大小也是8k,但是可以手动指定缓存大小,把数据直接读取到缓存中,减少每次转换的过程,效率更高。
private static void charWriter() {
File file = new File("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\OftenClass.md");
try {
Writer writer = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(writer);
bw.write("小乔流水哗啦啦,我到你家");
bw.flush();//刷新
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void charReader() throws IOException {
File file = new File("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\OftenClass.md");
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();
}
}
六、打印流
- 打印流主要功能是用于输出,在整个IO包中打印流分为两种类型:
- 字节打印流:PrintStream
- 字符打印流:PrintWriter
- 目的是:方便输出
private static void bytePrint() {
File file = new File("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\OftenClass.md");
try {
OutputStream out = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(out);//加入缓冲流的一层
PrintStream ps = new PrintStream(bos);//加入打印流的一层
//在字节输出时候,增强打印功能
ps.println("小河流水天天哗啦啦");
ps.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
private static void charPrint() {
//只针对文件
File file = new File("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\OftenClass.md");
try {
Writer out = new FileWriter(file);
//只针对Writer这种类型,加强它的属性
BufferedWriter bos = new BufferedWriter(out);//加入缓冲流的一层
PrintWriter ps = new PrintWriter(bos);//加入打印流的一层
//在字节输出时候,增强打印功能
ps.println("小河流水天天哗啦啦");
ps.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
七、对象流
-
对象流的两个类
- objectOutputStream将java对象的基本数据类型和图形写入OutputStream
- objectInputStream对以前使用ObjectOutputStream写入的基本数据可对象进行反序列化
-
序列化一组对象(把对象中的属性值写入到文件中)
- 在序列化操作中,同时序列化多个对象时,反序列化也必须按顺序操作,如果想要序列化一组对象该如何操作呢?
- 序列化一组对象可采用对象数组的形式,因为对象数组可以向Object进行转型操作
1、序列化:把对象写入文件,实际写入的是类名,属性名,属性类型,属性的值等
2、反序列化:把文件中的对象内容读取出来,还原成对象
- translent关键字
- 如果是用translent声明一个实例变量,当对象存储时,它的值不需要维持。
public class ObjectStreamDemo {
public static void main(String[] args) {
writeObject();
readObject();
}
private static void writeObject() {
Dog dog = new Dog("wangwang",4,"母");
Dog dog1 = new Dog("hsq",4,"公");
Dog[] dogs = {dog,dog1};
File file = new File("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\dog.obj");
try {
OutputStream out = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(dogs);
oos.close();
} catch (Exception e) {
}
}
private static void readObject() {
File file = new File("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\dog.obj");
try {
InputStream in = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(in);
Dog[] dog = (Dog[])ois.readObject();
ois.close();
/*for (int i = 0; i < dog.length; i++) {
System.out.println(dog[i]);
}*/
for (Dog x : dog) {
System.out.println(x);
}
} catch (Exception e) {
}
}
}
八、字节数组流
字符数组流:是基于内存操作,内部维护着一个字节数组,我么可以利用流的读取机制来处理字符串
无需关闭操作。
因为他们是在基于内存总操作的,所以关闭无效。
-
ByteArrayInputStream
包含一个内部缓冲区,该缓冲区包含从流中读取的 字节,内部计数器跟踪read方法要提供下一个字节。关闭ByteArrayinputStream无效。此类中的方法在关闭流后仍可被调用,而不会产生任何的IOException
-
ByteArrayOutputStream
此类驶向了一个输出流,其中输出数据被写成一个byte数组。缓冲区会随着数据的不断写入自动增长。可以使用toByteArray()和toString()获取数据。关闭ByteArrayOutputStream无效。此类中的方法在关闭流后仍可以被调用,而不会产生任何IOException
public class ByteArrayStreamDemo {
private static void byteArray() {
String s = "shhsddw234hui138w#$sD5t$#25Ewe";
ByteArrayInputStream bais = new ByteArrayInputStream(s.getBytes());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int curr = -1;//每次读取的字节数
while((curr=bais.read())!=-1) {
if((curr>=65&&curr<=90)||(curr>=97 && curr<=122)) {
baos.write(curr);
}
}
//此时无需关闭,原因是字节数组流是基于内存的操作流
System.out.println("晒选出来的字符串:"+baos.toString());
System.out.println("一共有多少个字母:"+baos.toString().length());
}
public static void main(String[] args) {
byteArray();
}
九、数据流
-
DataInputStream
数据输入流允许应用程序以与机器无关的方式从底层输入流中读取基本java数据类型,应用程序可以使用数据输出流写入,稍后有数据输入流读取的数据。
DataInputStream对于多线程访问不一定是安全的,线程安全是可选的,他又此类方法的使用者负责
-
DataOutputStream
数据输出流允许应用成簇以适当的方式将基本的java数据类型写入输出流中,然后应用成簇可以使用数据输入流将数据读入。
-
案例:实现文件分割合并
public class DataStreamDemo {
private static void write() {
File file = new File("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\vince.dat");
try {
OutputStream out = new FileOutputStream(file);
//缓冲流
BufferedOutputStream bos = new BufferedOutputStream(out);
//数据流
DataOutputStream dos = new DataOutputStream(bos);
dos.writeInt(10);//写入4个字节
dos.writeByte(1);//写入一个字节
dos.writeUTF("中");
dos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void read() {
File file = new File("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\vince.dat");
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 str = dis.readUTF();
dis.close();
System.out.println(num+","+b+","+str);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
write();
read();
}
}
十、文件的分割和合并
- 文件的分割
/**
*
* @Description 文件的分割
* @author qr.huang
* @date 2019年11月8日
* @throws 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(targetFile));
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:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\binbin\\" + (i + 1)
+ "-temp" + targetFile.getName())));
if (cutSize <= 1024) {
bytes = new byte[(int) cutSize];
count = 1;
} else {
bytes = new byte[1024];
count = (int) cutSize / 1024; //这里的count不包括最后一次,如果有余数我们后面再处理
}
try {
while (count > 0 && ((len = in.read(bytes)) != -1)) {// 次数大于0(要放前面),而且文件要大于零
out.write(bytes, 0, len);// 从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.flush();
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
try {
in.close();// in要在循环结束后关闭
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
-
文件合并(通过合并流实现)
-
SequenceInputStream
表示其他输入流的逻辑串联,它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。
也就是说要排好顺序
-
/**
*
* @Description合并文件 @author qr.huang @date 2019年11月8日 @throws
*/
private static void merge( Enumeration<InputStream> es ) {
//构造一个合并流
SequenceInputStream sis = new SequenceInputStream(es);
try {
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\binbin\\第一章软件测试教程:1.oracle数据库基础.mp4"));
byte[] bytes = new byte[1024];
int len = -1;
try {
while((len = sis.read(bytes))!=-1){
bos.write(bytes, 0, len);
bos.flush();
}
bos.close();
sis.close();
System.out.println("合并完成");
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
十一、字符串流和管道流
-
StringReader
其源为一个字符串的字符流
-
StringWriter
一个字符流,可以用其回收在字符串缓冲区中的输出来构造字符串,关闭StringWriter无效。此类中的方法在关闭留后仍可以被调用。不会产生任何的IOException
public class StringStreamDeam {
//计算单词个数
private static void stringReader() {
String info = "good good study day day up";
StringReader sr = new StringReader(info);
//流的标记器
StreamTokenizer st = new StreamTokenizer(sr);
int count = 0 ;
while(st.ttype != StreamTokenizer.TT_EOF) {//只要类型不等于结尾,我们就继续循环
try {
if(st.nextToken()==StreamTokenizer.TT_WORD) {//如果是一个单词,则++
count++;
}
} catch (IOException e) {
e.printStackTrace();
}
}
sr.close();
System.out.println("count="+count);
}
-
管道流
管道流输入应该连接管道输出流:管道输入流提供写入管道输出流的所有数据字节。通常,数据从某个线程从PipedInputStream对象读取,并由其它线程将其写入到相应的PipedInputStream。
不建议对这两个对象尝试使用单个线程,因为这样可能会死锁线程。
管道输入流包含一个缓冲区,可以在缓冲区限定的范围内将读操作和写操作分开。如果向链接管道输出流提供数据字节的线程不再存在,则认为改管道已坏。
十二、RandomAccessFile
-
RandomAccessFile 是IO类,从Object直接继承而来,只可以对文件进行操作,可以对文件进行读取和写入
- 当模式是r时,文件不存在时会报异常。
- 当模式是rw时,文件不存在时,会自动创建文件,若文件已经存在,不会对原文件进行覆盖。
-
RandomAccessFile有强大的文件读写功能,其内部是大型byte[],可以通过seek().getFilePointer()等方法操作的指针,方便对数据进行写入与读取。还可以对基本数据类型进行直接的读和写操作。
-
RandomAccessFile的绝大多数功能,已经被JDK1.4的NIO的“内存映射文件(memory-mapped files)”给取代了,可以考虑一下是不是用“内存映射文件”来取代RandomAccessFile了。
public class RandomAccessFileDemo {
public static void main(String[] args) {
try {
//读文件
RandomAccessFile r = new RandomAccessFile("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\OftenClass.md","r");
//写文件
RandomAccessFile w = new RandomAccessFile("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\binbin\\OftenClass.md","rw");
byte[] bytes = new byte[1024];
int len = -1;
try {
while((len=r.read(bytes))!=-1) {
w.write(bytes,0,len);
}
w.close();
r.close();
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
System.out.println("copy success");
}
}
十三、Properties文件的操作
- Properties(Java.util.Priperties)。主要用来读取java的配置文件,各种语言都有自己所支持的配置文件,配置文件中有很多变量经常改变,这样也是为了方便用户,让用户能脱离程序本身去修改相应的变量配置文件。
public class PropertiesDemo {
public static String version = "";
public static String username = "";
public static String password = "";
private static void readConfig() {
Properties p = new Properties();
try {
InputStream inStream = new FileInputStream("Config.properties");
p.load(inStream);//加载文件
//从properties中获取数据
version = p.getProperty("app.version");
username = p.getProperty("db.username");
password = p.getProperty("db.password");
inStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//读取代码
public static void main(String[] args) {
readConfig();
System.out.println(PropertiesDemo.version);
System.out.println(PropertiesDemo.username);
System.out.println(PropertiesDemo.password);
}
}
- 写入操作
private static void writeConfig(String version, String username ,String password) {
Properties p = new Properties();
p.put("app.version", version);
p.put("db.username", username);
p.put("db.password", password);
//写文件
try {
OutputStream out = new FileOutputStream("Config.properties");
p.store(out, "update config");
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
-
一些常用的方法
-
getProperty(String key)
用指定的键在此属性列表中搜索属性。也就是通过参数key,得到key所对应的value值。
-
load(InputStream inStream)
从输入流中读取属性列表(键值对),通过对指定的文件(比如,上面的Config.properies文件)进行装载来获取改文件的所有键值对,以供getProperty(String key)来搜索
-
setProperty(String key,String value)
调用Hashtable的方法put,他通过调用基类的put方法来设置键值对
-
store(OutputStream out ,String comments)
以适合使用load方法来加载到properties表中的格式,将此properties表中的属性列表(键值对)写入输入流,与load方法相反,该方法将键值对写入到指定的文件中去。
-
clear()清楚所有装载的键值对,该方法在基类中提供
-
十四、文件的压缩和解压缩
-
ZipOutputStream 实现文件的压缩
-
ZipOutputStream(OutputStream out)
穿件新的zip输出流
-
void putNextEntry(ZipEntry)
快开始写入学年度Zip文件条目,并将流定位到条目数据的开始处
-
ZipEntry(String name)
使用指定名称创建新的Zip条目
-
/**
*
* @Description 压缩
* @author qr.huang
* @date 2019年11月8日
* @throws
*/
private static void compression(String zipFileName, File targetFile) {
System.out.println("文件压缩中,,,。。。");
try {
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFileName));
BufferedOutputStream bos = new BufferedOutputStream(out);
zip(out,targetFile,targetFile.getName(),bos);//做递归
bos.close();
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("压缩完成");
}
private static void zip(ZipOutputStream zOut , File targetFile ,String name,BufferedOutputStream bos) throws IOException {
//如果是目录
if(targetFile.isDirectory()) {
File[] files = targetFile.listFiles();
//空文件夹
if(files.length==0) {
zOut.putNextEntry(new ZipEntry(name+"/"));//出路空目录
}else {
for(File f:files) {
//递归处理
zip(zOut,f,name+"/"+f.getName(),bos);
}
}
}else {
zOut.putNextEntry(new ZipEntry(name));
InputStream in = new FileInputStream(targetFile);
BufferedInputStream bis = new BufferedInputStream(in);
byte[] bytes = new byte[1024];
int len = -1;
while((len=bis.read(bytes))!=-1) {
bos.write(bytes,0,len);
}
bis.close();
}
}
-
ZipInputStream 实现文件的解压
-
ZipInputStream(InputStream in)
创建新的Zip输入流
-
ZipEntry getNextEntry()
读取下一个Zip文件条目并将流定位到该条目数据的开始处
-
/**
*
* @Description 解压
* @author qr.huang
* @date 2019年11月8日
* @throws
*/
private static void decompression(String targetFileName,String parent) {
try {
//构建解压输入流
ZipInputStream zIn = new ZipInputStream(new FileInputStream(targetFileName));
ZipEntry entry = null;
File file = null;
//不等于空,并且是文件的时候才需要解压,(!entry.isDirectory())即表示是文件
while((entry = zIn.getNextEntry())!=null && !entry.isDirectory()) {
file = new File(parent,entry.getName());
if(!file.exists()) {
//如果文件不存在,就创建此文件的上级目录
new File(file.getParent()).mkdir();//创建此文件的上级目录
OutputStream out = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(out);
byte[] bytes = new byte[1024];
int len = -1;
while((len=zIn.read())!=-1) {
bos.write(bytes,0,len);
}
bos.close();
System.out.println(file.getAbsolutePath()+"解压成功");
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
十五、装饰者修饰模式
-
意图
动态地给一个对象添加一些额外的职责,就附加功能来说,Decorator模式相比生成子类更为灵活,该模式以对客户端透明的方式进行拓展对象的功能。
-
适应环境
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
处理那些撤销的职责。
当不能采用生成子类的方式进行扩充时,一共情况是,可能有大量的独立拓展,为支持每一种组合将产生大流量的子类,使得子类数目呈爆炸性增长,另一种情况可能是因为类定义被隐藏或类定义不能用于生成子类。
-
类图
-
Component:被装饰对象基类
定义对象的接口,可以给这些对象动态增加职责。
-
ConcreateComponet:具体被装饰对象
定义具体的对象,Decorator可以给它增加额外的职责
-
Decorator:装饰者抽象类
维护指向Componet实例的引用,定义与Componet一致的接口。
-
ConcreateDecorator:具体装饰者
具体的装饰对象,给内部特有的具体被装饰对象增加具体的职责。
-
-
涉及的角色
- 抽象组件:定义一个抽象接口,来规范准备附加功能的类。
- 具体组件:将要被附加功能的类,实现抽象构建角色接口。
- 抽象装饰者:持有对具体构建角色的引用并定义与抽象构建角色一致的接口。
- 具体装饰:实现抽象装饰者角色,复制为具体构件添加额外功能。
-
豆浆管理系统的实现
Drink.java 被装饰者对象的接口 SoyaBeanMilk.java 具体的被装饰者对象 EggDecorator.java 具体装饰者对象 SugarDecorator.java 具体装饰者对象 BlackBeanDecorator.java 具体装饰者对象 Decorator.java 装饰者基类 Test.java 测试 -
装饰者模式小结
oo原则:动态地将责任附加到对象上,想要拓展功能,装饰者提供有别于继承的另一种选择。
-
要点:
- 继承属于扩展的方式之一,但是不见得是达到拓展的最佳方案
- 在我们的设计中,应该允许行为可以被扩展,而不需修改现有的代码
- 组合和委托可用于运行时动态地加上新的行为
- 除了继承,装饰者也可以让我们扩展行为
- 装饰者模式意味着一群装饰则,这些类用来包装具体组件
- 装饰种类反应出装饰的组件类型(实际上,他们具有相同的类型,都经过接口或继承实现)
- 装饰者可以在被装饰者的行为前面、或后面加上自己的行为,甚至将被装饰着的行为整个取代掉,而达到特定的目的
- 你可以有无数个装饰者包装一个组件
- 装饰者一般对组建的客户是透明的,除非客户程序依赖于组建的具体类型。
十六、常见的字符编码
-
常见的有:ISO8859-1、GBK/GB2312、unicode、UTF-8
-
ISO8859-1:编码属于单字节,最多能表示0-255的字符范围,主要用在英文上。
-
GBK/GB2312:中文的国际编码,专门用来表示汉字,是双字节编码。
-
unicode:java就是用次编码,也是最标准的一种编码,是使用16进表示的,但是此编码不兼容ISO8859-1编码。(是固定长度的)
-
UTF:由于nuicode不支持ISO8859-1编码,而且容易占用更多的空间,而且对于英文字母也需要使用两个字节编码,这样用unicode不便于传输和存储,因此产生了utf编码。
utf编码兼容了ISO8859-1编码,也可以用来表示所有语言字符,不过是不定长编码,每一个字符的长发从1-6个字节不等,一般在中文网页中使用此编码,这样可以节省空间。
-
造成乱码的根本原因:
- 程序使用到的编码和本机的编码不统一
- 在网络中,客户端和服务端的编码不统一。
public class CodeDome {
//通常产生乱码的情况是,两个不兼容的 编码相互转换。
public static void main(String[] args) {
String info = "小桥流水哗啦啦啦";//程序的默认编码是gb2312
try {
String newInfo = new String (info.getBytes("gb2312"),"iso8859-1");
System.out.println("转换成iso8859-1后:"+newInfo);
String newInfo2 = new String(newInfo.getBytes("iso8859-1"),"gb2312");
System.out.println("转换成gb2312编码后:"+newInfo2);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
十七、NEW IO
-
为什么要使用NIO?
NIO是JDK1.4加入的新包,NIO的创建目的是为了让java程序员可以实现高速I/O而无需自定义本机的代码。
NIO将最耗时的I/O操作,(即填充和提取缓冲区)转移会操作系统,因而极大地提高速度。
-
流与块的比较。
原来的I/O库(在java.io.*中)于NIO最重要的区别是数据打包和传输的方式,原来的I/O以流的方式来处理数据,而NIO是以块的方式处理数据
面向刘的I/O系统,一次一个字节地处理数据,一个输入流产生一个字节的数据,一个输出流消耗一个自己诶数据,不利的一面是面向I/O通常非常慢
一个面向块的 I/O系统是以块的形式来处理数据。每一个操作都在一步中产生,或者消费一个数据块,按照快处理数据比按照流的方式(字节)处理数据要快得多,但是面向块的I/O缺少一些面向流I/O所具有的有雅性和简单性。
-
缓冲区
在NIO库中,所有数据都是用缓冲区处理,读写数据都是在缓冲区中进行。任何访问NIO的数据都是放在他的缓冲区中。
缓冲区实际上就是一个数据,通常是一个字节数组,但是也可以使用其他类型的数组,但是一个缓冲区不仅仅是一个数组,缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读、写进程。
-
缓冲区类型。
最常见的缓冲区类型是ByteBuffer,一个Byteuffer可以在其底层字节数组上进行get/set操作,即字节的获取和设置,ByteBuffer不是NIO中唯一的缓冲区类型,事实上,对于每一种基本java类型都有一种缓冲区类型(ByteBuffer 、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer)
-
缓冲区状态变量
position | limit | capacity |
---|---|---|
位置 | 限制 | 最大容量,一般不变 |
public class NioDemo {
public static void main(String[] args) {
//创建内存空间,创建一个字节缓冲区,申请空间为8个字节。
ByteBuffer buf = ByteBuffer.allocate(8);
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
//缓冲区中写入数据
buf.put((byte)10);
buf.put((byte)20);
buf.put((byte)40);
buf.put((byte)80);
System.out.println("------------");
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
//缓冲区的反转:也就是截取的意思
buf.flip();// limit=position,position=0;
//也就是position,和limit中才有数据
System.out.println("-------------");
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
//告知当前位置和限制之间是否有元素
if(buf.hasRemaining()) {
//返回当前位置(position)和限制(limit)之间的元素数
for(int i=0;i<buf.remaining();i++) {
byte b = buf.get(i);
System.out.println(b);
}
System.out.println("-------------");
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
//结果不变说明取数据对缓冲区没有影响
}
}
}
十八、通道:Channel
-
Channel是一个对象。他通过读取和写入数据,拿NIO和原来的I/O作比较,通道就像流。
正如上面提到的,所有数据投通过Buffer对象来处理。你永远不会讲字节直接写入通道中,相反,你要是将数据写入包含一个或多个字节的缓冲区。同样你不会直接从同道中直接读取数据,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。
/**
* @Description 通过文件通道的方式实现复制
* @author qr.huang
* @date 2019年11月13日
*/
public class CopyFileDemo {
/**
*
* @Description 通道
* @author qr.huang
* @date 2019年11月13日
* @throws Exception
* @throws
*/
private static void copyFile() throws Exception {
//创建一个输入的文件通道
FileChannel fcIn = new FileInputStream("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\OftenClass.md").getChannel();
//创建一个输出的文件通道
FileChannel fcOut = new FileOutputStream("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\dabin\\OftemClass").getChannel();
ByteBuffer buf = ByteBuffer.allocate(1024);
while(fcIn.read(buf)!=-1) {
buf.flip();
fcOut.write(buf);
buf.clear();
}
fcIn.close();
fcOut.close();
System.out.println("copy success!");
}
/**
*
* @Description 映射
* @author qr.huang
* @date 2019年11月13日
* @throws Exception
* @throws
*/
private static void randomAccessFileCopy() throws Exception {
RandomAccessFile in = new RandomAccessFile("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\OftenClass.md","r");
RandomAccessFile out = new RandomAccessFile("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\dabin\\OftenClass.md","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 success...");
}
public static void main(String[] args) throws Exception {
// try {
// copyFile();
// } catch (Exception e) {
// e.printStackTrace();
// }
randomAccessFileCopy();
}
}
- 比较IO操作的性能
- 内存映射最快
- NIO读写第二
- 使用缓存的IO流第三
- 最后是无缓存的IO流
十九、Path接口和Files工具类
JDK引入的新的IO操作,java.nio.file包下,java NIO Path和Files类
-
Path接口
-
Path表示的是一个目录名序列,气候还可以跟一个文件名,路径中第一个部件是跟部件时是绝对路径。
例如:/或C:\,
而允许访问的跟部件取决于文件系统。
-
以跟部件开始的路径是绝对路径,否则为相对路径
-
静态的Paths.get方法接受一个或多个字符串,字符串之间自动使用默认文件系统的路径分割符链接起来(Unix是/,Windows是\)这就解决了跨平台的问题,接着解析链接起来的结果,如果不是合法路径就抛出InvalidPathException异常,否则返回一个Path对象。
-
public class PathFilesDemo {
public static void main(String[] args) throws UnsupportedEncodingException, IOException {
File file = new File("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\OftenClass.md");
//path:获取路径的方法
Path p1 = Paths.get("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning","OftenClass.md");
System.out.println("获得路径的第一种方法:\n P1="+p1);
Path p2 = file.toPath();
System.out.println("获得路径的第二种方法:\n P2="+p2);
Path p3 = FileSystems.getDefault().getPath("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning","OftenClass.md");
System.out.println("获得路径的第三种方法:\n P3="+p3);
//Files工具类
//写文件
Path p4 = Paths.get("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\OftenClass.md");
String info = "村花到我家,一起...\n";
Files.write(p4, info.getBytes("utf-8"), StandardOpenOption.APPEND);
//读文件
byte[] bytes = Files.readAllBytes(p4);
System.out.println(new String(bytes));
//复制文件
//Files.copy(p3, Paths.get("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\dabin\\OftenClass.md"),StandardCopyOption.REPLACE_EXISTING);
//移动文件
//Files.move(p3, Paths.get("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\dabin\\OftenClass.md"),StandardCopyOption.REPLACE_EXISTING);
//删除文件
//Files.delete(p3);
//Files.deleteIfExists(p3);//这个删除方法即便找不到文件也不会抛异常
//创建目录
Files.createDirectory(Paths.get("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\test"));
//创建多级目录
//Files.createDirectories("");
//创建文件
Files.createFile(Paths.get("C:\\Users\\雨后黄昏sky\\OneDrive\\桌面\\Learning\\text.txt"));
}
}