1、File类
File类的基本概念
File类:表示文件和目录路径名的抽象表示方法
File类可以实现文件的创建、删除、重命名、得到路径、创建时间等,是唯一与文件本身有关的操作类
File类的操作方法
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;
/**
* File类的使用
* @description
*/
public class FileDemo {
public static void main(String[] args) {
//File类表示一个文件或目录
// "C:\\test\\vince.txt"
//"C:/test/vince.txt"
File f1 = new File("c:"+File.separator+"test"+File.separator+"vince.txt");
if(!f1.exists()){ //判断文件是否存在
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(); //删除空文件夹
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.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("HH:mm:ss");
System.out.println("文件最后修改的时间="+df.format(date));
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\\dabin1");
f4.mkdirs();
//重命名与移动文件
//重命名f4.renameTo(new File("c:\\test\\dabin1"));;
//移动文件
f4.renameTo(new File("c:\\test1"));
//返回所有“.txt”格式的文件
File f5 = new File("c:\\test\\my");
// File[] files = f5.listFiles(new FileFilter() {
// @Override
// 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());
}
}
}
import java.io.File;
/**
* 在指定的目录中查找文件
* @description
*/
public class FileDemo2 {
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();
//System.out.println(name);
if(name.endsWith(ext)){
System.out.println(target.getAbsolutePath());
}
}
}
}
2、字节流
1、IO流概述
IO流:输入输出流(Input/Output) 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。
即数据在两设备间的传输称为流
流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
IO流的分类:
根据处理数据类型的不同分为:字符流和字节流
根据数据流向不同分为:输入流和输出流
2、字节输出流
OutputStream类定义
public abstract class OutputStream extends Object implements Closeable,Flushable
此抽象类是表示输出字节流的所有类的超类。输出流接受输出字节并将这些字节发送到InputStream 类某个接收器要向文件中输出,使用FileOutputStream类
3、字节输入流
InputStream类定义:
public abstract class InputStream extends Object implements Closeable
此抽象类是表示字节输入流的所有类的超类。 FileInputStream 从文件系统中的某个文件中获得输入字节
package com.vince;
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;
/**
* 字节输出输入流
* 输出流:超类 OutputStream,对文件的输出流使用子类FileOutputStream
* 输入流:超类InputStream,对文件的输入流使用子类FileInputStream
*
* 输入输出字节流操作原理,每次只会操作一个字节,(从文件中读取或写入 )
* 字节操作流,默认每次执行写入操作会直接把数据写入文件
* @author vince
* @description
*/
public class ByteStreamDemo {
private static void in(){
//0、确定目标文件
File file = new File("d:\\test\\test.txt");
//1、构建 一个文件输入流对象
try {
InputStream in = new FileInputStream(file);
byte[] bytes = new byte[10];
StringBuilder buf = new StringBuilder();
//读取过程
int len = -1;//表示每次读取的字节长度
//把数据读入到数组中,并返回读取的字节数,当不等-1时,表示读取到数据,等于-1表示文件已经读完
while((len = in.read(bytes))!=-1){
//根据读取到的字节数组,再转换为字符串内容,添加到StringBilder中
buf.append(new String(bytes,0,len));
}
//打印内容:
System.out.println(buf);
//关闭输入流
in.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void out(){
//0、确定目标文件
File file = new File("d:\\test\\test.txt");
//1、构建一个文件输出流对象
try {
OutputStream out = new FileOutputStream(file,true); //append 为true表示追加内容,为false表示覆盖内容
//2、输出的内容
String info = "小桥流水人家\r\n";// \r\n表示换行
//String line = System.getProperty("line.separator");//获取换行符
//3、把内容写入到文件
out.write(info.getBytes()); //以字节数组的形式写入
//4、关闭流
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();
}
}
4、文件的复制
一边读、一边写
package com.vince;
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
*/
public class CopyFileDemo {photo
public static void main(String[] args) {
System.out.println("start copy...");
copy("d:\\photo.jpg","d:\\test\\photo.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[] 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();
}
}
}
}
3、字符流
字节流与字符流的区别 在所有的流操作里。
字节永远是最基础的。任何基于字节的操作都是正确的。无论你是文本文件还是二进制的文件。
如果确认流里面只有可打印的字符,包括英文的和各种国家的文字,也包括中文,那么可以考虑用字符流。 由于编码不同,多字节的字符可能占用多个字节。比如GBK的汉字就占用2个字节,而UTF-8的汉字就占用3个字节。
所以,字符流是根据指定的编码,将1个或多个字节转化为java里面的unicode的字符,然后进行操作。字符操作一般使用Writer,Reader等, 字节操作一般都是InputStream, OutputStream 以及各种包装类,比如BufferedInputStream和BufferedOutputStream等。
总结:
如果你确认你要处理的流是可打印的字符,那么使用字符流会看上去简单一点。如果不确认,那么用字节流总是不会错的。
Writer
写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和close()。但是,多数子类将重写此 处定义的一些方法,以提供更高的效率和/或其他功能。 与OutputStream一样,对文件的操作使用:FileWriter类完成。
Reader
用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int)和close()。但是,多数子类将重写 此处定义的一些方法,以提供更高的效率和/或其他功能。 使用FileReader类进行实例化操作。
import java.io.BufferedWriter;
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;
/**
* 字符流:
* 字符输出流:Writer,对文件的操作使用子类:FileWriter
* 字符输入流:Reader,对文件的操作使用子类:FileReader
* 每次操作的单位是一个字符
* 文件字符操作流会自带缓存,默认大小为1024字节,在缓存满后,或手动刷新缓存,或关闭流时会把数据写入文件
*
* 如何选择使用字节流还是字符流:
* 一般操作非文本文件时,使用字节流,操作文本文件时,建议使用字符流
*
* 字符流的内部实现还是字节流
* @description
*/
public class CharStreamDemo {
private static void in(){
File file = new File("d:\\test\\test.txt");
try {
Reader in = new FileReader(file);
char[] cs = new char[3];
StringBuilder buf = new StringBuilder();
int len = -1;
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("d:\\test\\test.txt");
try {
Writer out = new FileWriter(file,true);
out.write(",枯藤老树昏鸦");
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
out();
in();
}
}
4、字节字符转换流
转换流,可以将一个字节流转换为字符流,也可以将一个字符流转换为字节流。
OutputStreamWriter:可以将输出的字符流转换为字节流的输出形式
InputStreamReader:将输入的字节流转换为字符流输入形式
package com.vince;
import java.io.*;
import java.nio.charset.Charset;
/**
* 转换流
* OutputStreamWriter:可以将输出的字符流转换为字节流的输出形式
InputStreamReader:将输入的字节流转换为字符流输入形式
* @author vince
* @description
*/
public class ChangeStreamDemo {
private static void write(OutputStream out){
//Charset.defaultCharset()使用默认字符集
Writer writer = new OutputStreamWriter(out,Charset.defaultCharset());
try {
writer.write("古道西风瘦马\r\n");
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(cs,0,len));
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws FileNotFoundException {
InputStream in = new FileInputStream("d:\\test\\test.txt");
read(in);
OutputStream out = new FileOutputStream("d:\\test\\test.txt");
write(out);
}
}
5、缓冲流
首先要明确一个概念: 对文件或其它目标频繁的读写操作,效率低,性能差。
使用缓冲流的好处是,能够更高效的读写信息
原理是将数据先缓冲起来,然后一起写入或者读取出来。
BufferedInputStream: 为另一个输入流添加一些功能,在创建BufferedInputStream 时,会创建一个内部缓冲区数组,用于缓冲数据。
BufferedOutputStream:通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。
BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
/**
* 缓存的目的:
* 解决在写入文件操作时,频繁的操作文件所带来的性能降低的问题,
* BufferedOutputStream 内部默认的缓存大小是8KB,每次写入时存储到缓存中的byte数组中,当数组存满时,会把数组中的数据写入文件,
* 并且缓存下标归零
*
* 字符流
* 1、加入字符缓存流,增强读取功能(readLine)
* 2、更高效的读取数据
* FileReader: 内部使用InputStreamReader( sun.nio.cs.StreamDecoder),解码过程,byte->char,默认缓存大小是8K
* BufferedReader:默认缓存大小是8K,但可以手动指定缓存大小,把数据进接读取到缓存中,减少每次转换过程,效率更高
* BufferedWriter 同上
*
* @description
*/
public class BufferStreamDemo {
private static void charWriter(){
File file = new File("d://test//test.txt");
try {
Writer writer = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(writer);
bw.write("古道西风瘦马");
bw.flush(); //刷新缓存
bw.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void charReader(){
File file = new File("d://test//test.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 byteReader2(){
File file = new File("d://test//test.txt");
try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
byte[] bytes = new byte[1024];
int len = -1;
while((len=bis.read(bytes))!=-1){
System.out.println(new String(bytes,0,len));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void byteReader(){
File file = new File("d://test//test.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("d://test//test.txt");
try {
OutputStream out = new FileOutputStream(file,true);
//构造一个字节缓冲流
BufferedOutputStream bos = new BufferedOutputStream(out);
//
String info = "小桥流水人家";
bos.write(info.getBytes());
bos.close();
//out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
byteWriter();
byteReader();
}
}
6、打印流
打印流的主要功能是用于输出,在整个IO包中打印流分为两种类型:
字节打印流:PrintStream
字符打印流:PrintWriter
打印流可以很方便的进行输出
package com.vince;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Writer;
/**
* 打印流:很方便的进行输出
*
* 字节打印流
* 在字节输出时,可以增强输出功能
* 字符打印流
*
* @author vince
* @description
*/
public class PrintStreamDemo {
private static void charPrint(){
File file = new File("d:\\test\\test.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("d:\\test\\test.txt");
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();
}
}
public static void main(String[] args) {
bytePrint();
charPrint();
}
}
7、对象流
对象流的两个类:
ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream
ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
序列化一组对象:
在序列化操作中,同时序列化多个对象时,反序列化也必须按顺序操作,如果想要序列化一组对象该如何操作呢? 序列化一组对象可采用:对象数组的形式,因为对象数组可以向Object进行转型操作。
transient关键字:
如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。
ObjectStreamDemo
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.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
public class ObjectStreamDemo {
/**
* 对象序列化
* 把对象写入文件:实际写入的是类名、属性名、属性类型、属性的值等
*/
private static void writeObjects(){
Dog dog = new Dog("wangwang",2,"母"); //以组的形式将对象写入,在读取的时候,也要将对象的类型转为相应的组
Dog dog2 = new Dog("2哈",3,"公");
Dog[] dogs = {dog,dog2};
File file = new File("d:\\test\\dog.obj");
try {
OutputStream out = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(dogs);
oos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 反序列化的过程
* 从文件中把对象的内容读取出来,还原成对象
*/
private static void readObject(){
File file = new File("d:\\test\\dog.obj");
try {
InputStream in = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(in);
Dog dog = (Dog)ois.readObject();
ois.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("wangwang",2,"母");
File file = new File("d:\\test\\dog.obj");
try {
OutputStream out = new FileOutputStream(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();
}
}
Dog
import java.io.Serializable;
//如果一个类创建的对象,需要被序列化,那么该类必须实现Serializable接口,
//Serializable是一个标记接口,没有任何定义,为了告诉JVM该类对象可以被序列化
//什么时候对象需要被序列化呢?
//1、把对象保存到文件中(存储到物理介质)
//2、对象需要在网络上传输
//如果对象没有实现Serializable接口,会报错误:java.io.NotSerializableException
public class Dog implements Serializable{
private static final long serialVersionUID = 1L; //序列化编号,确定是不是一个对象
private String name;
private int age;
private String sex;
private transient int id; //在序列化中被忽略
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Dog(String name, int age, String sex) {
super();
this.name = name;
this.age = age;
this.sex = sex;
}
public Dog() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "Dog [name=" + name + ", age=" + age + ", sex=" + sex + "]";
}
}
8、字节数组流
ByteArrayInputStream
包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪 read 方法要提供的下一个字节。 关闭 ByteArrayInputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。
ByteArrayOutputStream
此类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 获取数据。 关闭ByteArrayOutputStream 无效。此类中的方法在关闭此流后仍可 被调用,而不会产生任何 IOException。
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
/**
* 字节数组流:
* 基于内存操作,内部维护着一个字节数组,我们可以利用流的读取机制来处理字符串
* 无需关闭
* @description
*/
public class ByteArrayStreamDemo {
private static void byteArray(){
String s = "12345676dfghjhg(*$$%^&SDFGHJ";
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()); //dfghjhgSDFGHJ
}
public static void main(String[] args) {
byteArray();
}
}
9、数据流
DataInputStream:
数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。应用程序可以使用数据输出 流写入稍后由数据输入流读取的数据。DataInputStream对于多线程访问不一定是安全的。 线程安全是可选的,它由此类方法的使用者负责。
DataOutputStream:
数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。然后,应用程序可以使用数据输入流将数据读入。
案例: 实现文件分割合并。
与底层无关,还原很方便
package com.vince;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
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;
/**
* 数据流
* 与机器无关的操作JAVA的基本数据类型
* @description
*/
public class DataStreamDemo {
private static void read(){
File file = new File("d:\\test\\test.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 s = dis.readUTF();
System.out.println(num+","+b+","+s);
dis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void write(){
File file = new File("d:\\test\\test.dat");
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();
}
}
public static void main(String[] args) {
write();
read();
}
}
文件分割与合并
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
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.SequenceInputStream;
import java.util.Enumeration;
import java.util.Vector;
/**
* 文件分割合并示例
*
* @description
*/
public class FileDivisionMergeDemo {
/**
* 文件的分割 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:\\test\\" + (i + 1) + "-temp-" + targetFile.getName())));
if (cutSize <= 1024) {
bytes = new byte[(int) cutSize];
count = 1;
} else {
bytes = new byte[1024];
count = (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.flush();
out.close();
}
}
in.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 文件合并
*/
private static void merge(Enumeration<InputStream> es) { //枚举类型
//构造一个合并流
SequenceInputStream sis = new SequenceInputStream(es);
try {
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("c:\\test\\第01章_Java开发入门_01_计算机基本概念与DOS命令.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:\\第01章_Java开发入门_01_计算机基本概念与DOS命令.avi");
// division(file, 1024 * 1024 * 20);
try {
InputStream in1 = new FileInputStream(new File("c:\\test\\1-temp-第01章_Java开发入门_01_计算机基本概念与DOS命令.avi"));
InputStream in2 = new FileInputStream(new File("c:\\test\\2-temp-第01章_Java开发入门_01_计算机基本概念与DOS命令.avi"));
InputStream in3 = new FileInputStream(new File("c:\\test\\3-temp-第01章_Java开发入门_01_计算机基本概念与DOS命令.avi"));
InputStream in4 = new FileInputStream(new File("c:\\test\\4-temp-第01章_Java开发入门_01_计算机基本概念与DOS命令.avi"));
InputStream in5 = new FileInputStream(new File("c:\\test\\5-temp-第01章_Java开发入门_01_计算机基本概念与DOS命令.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.elements();
merge(es);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
10、合并流、字符串流、管道流
一、合并流:
SequenceInputStream 表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取, 直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。
二、字符串流
1、StringReader 其源为一个字符串的字符流。
2、StringWriter 一个字符流,可以用其回收在字符串缓冲区中的输出来构造字符串。 关闭 StringWriter 无效。此类中的方法在关闭该流后仍可被调用,而不会产生任何 IOException。
package com.vince;
import java.io.IOException;
import java.io.StreamTokenizer;
import java.io.StringReader;
/**
* 字符串流:以一个字符串为数据源,来构造一个字符流
* 作用:在WEB开发中,我们经常要从服务器上获取数据,数据的返回格式通过是一个字符串(XML,JSON),我们需要把这个字符串构造成一个字符流
* 然后再用第三方的数据解析器来解析数据。
* StringWriter
* @description
*/
public class StringStreamDemo {
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);
}
public static void main(String[] args) {
stringReader();
}
}
三、管道流
管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个线程从 PipedInputStream 对象读取,并由其他线程将其写入到相应的 PipedOutputStream。
不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读 操作和写操作分离开。如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏。
package com.vince;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
/**
* 管道流测试:一个线程写入,一个线程读取
* 作用,用于线程之间的数据通讯
* @description
*/
public class PipedStreamDemo {
public static void main(String[] args) {
PipedInputStream pin = new PipedInputStream();
PipedOutputStream pout = new PipedOutputStream();
try {
pin.connect(pout); //两个管道进行链接
} catch (IOException e) {
e.printStackTrace();
} //输入流与输出流连接
ReadThread readTh = new ReadThread(pin);
WriteThread writeTh = new WriteThread(pout);
new Thread(readTh).start();
new Thread(writeTh).start();
//读到:你好...
}
}
//读取数据的线程
class ReadThread implements Runnable {
private PipedInputStream pin; //输入管道
ReadThread(PipedInputStream pin){
this.pin = pin;
}
public void run(){
try {
byte[] buf = new byte[1024];
int len = pin.read(buf); // read阻塞
String s = new String(buf, 0, len);
System.out.println("读到:"+s);
pin.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//写入数据的线程
class WriteThread implements Runnable {
private PipedOutputStream pout; //输出管道
WriteThread(PipedOutputStream pout) {
this.pout = pout;
}
public void run() {
try {
pout.write("你好...".getBytes()); // 管道输出流
pout.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
11、RandomAccessFile
RandomAccessFile是IO包的类,从Object直接继承而来。 只可以对文件进行操作,可以对文件进行读取和写入。
当模式为r是,当文件不存在时会报异常,
当模式为rw时,当文件不存在时,会自己动创建文件,当文件已经存在时 不会对原有文件进行覆盖。
RandomAccessFile有强大的文件读写功能,其内部是大型 byte[],可以通过seek(),getFilePointer()等方法操作的指针,方便对数据进行写入与读取。还可以对基本数据类型进行直接的读和写操作。
RandomAccessFile的绝大多数功能,已经被JDK 1.4的nio的“内存映射文件(memory-mapped files)”给取代了,考虑一下是不是用“内存映射文件”来代替RandomAccessFile了。
package com.vince;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* RandromAccessFile
* @author vince
* @description
*/
public class RandromAccessFileDemo {
public static void main(String[] args) {
try {
//读取文件
RandomAccessFile r = new RandomAccessFile("d:\\3D0.jpg","r");
//写入文件
RandomAccessFile w= new RandomAccessFile("d:\\test\\3D0.jpg","rw");
byte[] bytes = new byte[1024];
int len = -1;
while((len=r.read(bytes))!=-1){
w.write(bytes,0,len);
}
w.close();
r.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("copy success.");
}
}
12、Properties文件操作
Properties(Java.util.Properties)主要用于读取Java的配置文件,各种语言都有自己所支持的配置文件,配置文件中很多变量是经常改变的,这样做也是为了方便用户,让用户能够脱离程序本身去修改相关的变量设置。
它提供了几个主要的方法:
- 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 comments),以适合使用 load 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。与 load 方法相反,该方法将键 - 值对写入到指定的 文件中去。
- clear (),清除所有装载的 键 - 值对。该方法在基类中提供。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Properties;
/**
* Properties:
* properties可以用来做配置文件
* javaweb javaee 开发中通常会用到
*
* ResouceBundle 只读
* Properties 可读可写
*
* @description
*/
public class PropertiesDemo {
public static String version = "";
public static String username = "";
public static String password = "";
//静态代码块,只会执行一次
static{
//readConfig();
}
/**
* 对属性文件的写操作
* @param version
* @param username
* @param 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();
}
}
/**
* 读取properties配置文件
*/
private static void readConfig(){
Properties p = new Properties();
try {
//通过当前线程的类加载器对象,来加载指定包下的配置文件
InputStream inStream = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("com/res/config.properties");
// 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) {
//writeConfig("2","vince","654321");
readConfig();
System.out.println(PropertiesDemo.version);
System.out.println(PropertiesDemo.username);
System.out.println(PropertiesDemo.password);
}
}
13、文件压缩与解压缩
java中实现zip的压缩与解压缩 ZipOutputStream 实现文件的压缩
ZipOutputStream(OutputStream out) 创建新的 ZIP 输出流。
void putNextEntry(ZipEntry e) 开始写入新的 ZIP 文件条目并将流定位到条目数据的开始处。
ZipEntry(String name) //test/mm.jpg /test/a.txt 使用指定名称创建新的 ZIP 条目。
ZipInputStream 实现文件的解压 ZipInputStream(InputStream in) 创建新的 ZIP 输入流。
ZipEntry getNextEntry() 读取下一个 ZIP 文件条目并将流定位到该条目数据的开始处。
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
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;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
/**
* 压缩与解压缩
* @description
*/
public class CompressionAndDecompressionDemo {
/**
* 压缩
*/
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("压缩完成");
}
//zip
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+"/")); //处理空目录
}
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();
}
}
/**
* 解压
*/
private static void decompression(String targetFileName,String parent){
try {
//构造解压的输入流
ZipInputStream zIn = new ZipInputStream(new FileInputStream(targetFileName));
ZipEntry entry = null;
File file = null;
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.getAbsolutePath()+" 解压成功");
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
//compression("d:\\test.zip",new File("d:\\test"));
decompression("d:\\test.zip","d:\\QMDownload\\");
}
}
14、装饰者模式
意图:
动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。该模式以对客 户端透明的方式扩展对象的功能。
适用环境
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。 处理那些可以撤消的职责。 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类
类图:
Component(被装饰对象基类) 定义对象的接口,可以给这些对象动态增加职责;
ConcreteComponent(具体被装饰对象) 定义具体的对象,Decorator可以给它增加额外的职责;
Decorator(装饰者抽象类) 维护指向Component实例的引用,定义与Component一致的接口;
ConcreteDecorator(具体装饰者) 具体的装饰对象,给内部持有的具体被装饰对象增加具体的职责;
涉及角色
- 抽象组件:定义一个抽象接口,来规范准备附加功能的类。
- 具体组件:将要被附加功能的类,实现抽象构件角色接口。
- 抽象装饰者:持有对具体构件角色的引用并定义与抽象构件角色一致的接口。
- 具体装饰:实现抽象装饰者角色,负责为具体构件添加额外功能。
代码实现
- Drink.java 被装饰者对象的接口
- SoyaBeanMilk.java 具体的被装饰者对象
- EggDecorator.java 具体装饰者对象
- SugarDecorator.java 具体装饰者对象
- BlackBeanDecorator.java 具体装饰者对象
- Decorator.java 装饰者基类
- Test.java 测试
装饰者模式小结:
OO原则:动态地将责任附加到对象上。想要扩展功能,装饰者提供有别于继承的另一种选择。
要点:
- 继承属于扩展形式之一,但不见得是达到弹性设计的最佳方案。
- 在我们的设计中,应该允许行为可以被扩展,而不须修改现有的代码。
- 组合和委托可用于在运行时动态地加上新的行为。
- 除了继承,装饰者模式也可以让我们扩展行为。
- 装饰者模式意味着一群装饰者类,这些类用来包装具体组件。
- 装饰者类反映出被装饰的组件类型(实际上,他们具有相同的类型, 都经过接口或继承实现)。
- 装饰者可以在被装饰者的行为前面与/或后面加上自己的行为, 甚至将被装饰者的行为整个取代掉,而达到特定的目的。
- 可以有无数个装饰者包装一个组件。
- 装饰者一般对组建的客户是透明的, 除非客户程序依赖于组件的具体类型。
1、Drink
//被装饰者的接口
public interface Drink {
float cost();//计算价格
String description();//描述
}
2、SoyaBeanMilk
/**
* 具体的被装饰者类
* 豆浆
* @description
*/
public class SoyaBeanMilk implements Drink{
@Override
public float cost() {
return 10f;
}
@Override
public String description() {
return "纯豆浆";
}
}
3、Decorator
/**
* 装饰者的基类
* @description
*/
public abstract class Decorator implements Drink{
private Drink drink;//要装饰的对象
public Decorator(Drink drink){
this.drink = drink;
}
@Override
public float cost() {
return drink.cost();
}
@Override
public String description() {
return drink.description();
}
}
4、SugerDecorator
/**
* 具体的装饰者类
* @description
*/
public class SugarDecorator extends Decorator {
public SugarDecorator(Drink drink) {
super(drink);
}
@Override
public float cost() {
return super.cost()+1.0f;
}
@Override
public String description() {
return super.description()+"+糖";
}
}
5、BlackBeanDrcorator
/**
* 具体的装饰者类
* @description
*/
public class BlackBeanDecorator extends Decorator {
public BlackBeanDecorator(Drink drink) {
super(drink);
}
@Override
public float cost() {
return super.cost()+2.0f;
}
@Override
public String description() {
return super.description()+"+黑豆";
}
}
6、EggDecorator
/**
* 具体的装饰者类
* @description
*/
public class EggDecorator extends Decorator {
public EggDecorator(Drink drink) {
super(drink);
}
@Override
public float cost() {
return super.cost()+3.0f;
}
@Override
public String description() {
return super.description()+"+鸡蛋";
}
}
7、Test
package com.vince.decorator;
public class Test {
public static void main(String[] args) {
// OutputStream out = new FileOutputStream("xxx");
// BufferedOutputStream bos = new BufferedOutputStream(out);
// PrintStream ps = new PrintStream(bos);
// ps.print(..);
Drink drink = new SoyaBeanMilk();
SugarDecorator sugar = new SugarDecorator(drink);
EggDecorator egg = new EggDecorator(sugar);
BlackBeanDecorator blackBean = new BlackBeanDecorator(egg);
System.out.println("你点的豆浆是:"+blackBean.description());
System.out.println("一共花了"+blackBean.cost()+"元");
}
}
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开发中出现的乱码情况)
import java.io.UnsupportedEncodingException;
public class CodeDemo {
public static void main(String[] args) {
//通常产生乱码的情况是,两个不兼容的编码相互转换
String info = "小河流水哗啦啦"; //GB2312
try { //将 编码x 转成 编码y
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
为什么要使用 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。一个ByteBuffer可以在其底层字节数组上进行 get/set 操作(即字节的获取和设置)。ByteBuffer不是 NIO 中唯一的缓冲区类型。事实上,对于每一种基本 Java 类型都有一种缓冲区类型: ByteBuffer CharBuffer ShortBuffer IntBuffer LongBuffer FloatBuffer DoubleBuffer
缓冲区内部细节:
状态变量 可以用三个值指定缓冲区在任意时刻的状态: position limit capacity
NIODemo
import java.nio.ByteBuffer;
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());
System.out.println("---------");
//向缓冲区中写入数据
buf.put((byte)10);
buf.put((byte)20);
buf.put((byte)30);
buf.put((byte)40);
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
//缓冲区反转:截取相关数据
buf.flip();
System.out.println("-----------");
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
//告知在当前位置和限制之间是否有元素。
if(buf.hasRemaining()){
//返回当前位置与限制之间的元素数。
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 对象来处理。您永远不会将字节直接写入通道中, 相反,您是将数据写入包含一个或者多个字节的缓冲区。同样,您不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。
JDK1.7引入了新的IO操作类, java.nio.file包下,Java NIO Path接口和Files类
CopyDemo
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
/**
* 比较IO操作的性能比较:
* 1、内存映射最快
* 2、NIO读写文件
* 3、使用了缓存的IO流
* 4、无缓存的IO流
*
* @description
*/
public class CopyFileDemo {
private static void randomAccessFileCopy() throws Exception{
RandomAccessFile in = new RandomAccessFile(":d\\3D0.jpg","r");
RandomAccessFile out = new RandomAccessFile("d:\\test\\3D0.jpg","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");
}
/**
* 通过文件通道实现文件的复制
* @throws Exception
*/
private static void copyFile() throws Exception{
//创建一个输入文件的通道
FileChannel fcIn = new FileInputStream("d:\\3D0.jpg").getChannel();
//创建一个输出文件的通道
FileChannel fcOut = new FileOutputStream("d:\\test\\3D0.jpg").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.");
}
public static void main(String[] args) {
try {
// copyFile();
randomAccessFileCopy();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Path接口
- Path表示的是一个目录名序列,其后还可以跟着一个文件名,路径中第一个部件是根部件时就是 绝对路径,例如 / 或 C:\ ,而允许访问的根部件取决于文件系统;
- 以根部件开始的路径是绝对路径,否则就是相对路径;
- 静态的Paths.get方法接受一个或多个字符串,字符串之间自动使用默认文件系统的路径分隔符连 接起来(Unix是 /,Windows是 \ ),这就解决了跨平台的问题,接着解析连接起来的结果,如果不是合法路径就抛出InvalidPathException异常,否则就返回一个Path对象;
Files工具类
1、读写文件
- static path write(Path path, byte[] bytes, OpenOption… options) 写入文件
- static byte[] readAllBytes(Path path) 读取文件中的所有字节。
2、复制、剪切、删除
- 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)
3、创建文件和目录
//创建新目录,除了最后一个部件,其他必须是已存在的
Files.createDirectory(path); //创建路径中的中间目录,能创建不存在的中间部件
Files.createDirectories(path); //创建一个空文件,检查文件存在,如果已存在则抛出异常而检查文件存在是原子性的,
//因此在此过程中无法执行文件创建操作 Files.createFile(path);
//添加前/后缀创建临时文件或临时目录 Path newPath = Files.createTempFile(dir, prefix, suffix);
Path newPath = Files.createTempDirectory(dir, prefix);
PathFilesDemo
package com.nio;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
/**
* JDK1.7新的文件操作类 Path接口 Paths类 Files类
*
* @author vince
* @description
*/
public class PathFilesDemo {
public static void main(String[] args) {
File file = new File("d:\\test\\3D0.jpg");
// path
Path p1 = Paths.get("d:\\test", "3D0.jpg");
System.out.println(p1);
Path p2 = file.toPath();
System.out.println(p2);
Path p3 = FileSystems.getDefault().getPath("c:\\test", "3D0.jpg");
// Files工具类
Path p4 = Paths.get("d:\\test\\test.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) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 复制文件
// try {
// Files.copy(p3, Paths.get("d:\\3D0.jpg"),StandardCopyOption.REPLACE_EXISTING);
// } catch (IOException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// 移动文件
// try {
// Files.move(p3,Paths.get("c:\\3D0.jpg"),StandardCopyOption.REPLACE_EXISTING);
// } catch (IOException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
//删除文件
// try {
// Files.delete(p3);//static boolean deleteIfExists(Path path)
// } catch (IOException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
//创建新目录,除了最后一个部件,其他必须是已存在的
// try {
// Files.createDirectory(Paths.get("d:\\BB"));
// //Files.createDirectories(path); 创建多级不存在的目录
// } catch (IOException e) {
// e.printStackTrace();
// }
//创建文件
try {
Files.createFile(Paths.get("d:\\BB.txt"));
} catch (IOException e) {
e.printStackTrace();
}
//添加前/后缀创建临时文件或临时目录
// Path newPath = Files.createTempFile(dir, prefix, suffix);
// Path newPath = Files.createTempDirectory(dir, prefix);
}
}