java.io包
核心组成:一个类(File)、一个接口(Serializable)、四个抽象类(InputStream/OutputStream、Reader/Writer)
InputStream
常用方法:
OutputStream
常用方法:
**Reader **
**Writer **
File类:表示文件和目录路径名的抽象表示形式。
File类可以实现文件的创建、删除、重命名、得到路径、创建时间等等,是唯一与文件本身有关的操作类。
操作方法:
变量和类型 | 方法 | 描述 |
---|---|---|
boolean |
canExecute() |
测试应用程序是否可以执行此抽象路径名表示的文件。 |
boolean |
canRead() |
测试应用程序是否可以读取此抽象路径名表示的文件。 |
boolean |
canWrite() |
测试应用程序是否可以修改此抽象路径名表示的文件。 |
int |
compareTo(File pathname) |
按字典顺序比较两个抽象路径名。 |
boolean |
createNewFile() |
当且仅当具有此名称的文件尚不存在时,以原子方式创建由此抽象路径名命名的新空文件。 |
static File |
createTempFile(String prefix, String suffix) |
在默认临时文件目录中创建一个空文件,使用给定的前缀和后缀生成其名称。 |
static File |
createTempFile(String prefix, String suffix, File directory) |
在指定目录中创建一个新的空文件,使用给定的前缀和后缀字符串生成其名称。 |
boolean |
delete() |
删除此抽象路径名表示的文件或目录。 |
void |
deleteOnExit() |
请求在虚拟机终止时删除此抽象路径名表示的文件或目录。 |
boolean |
equals(Object obj) |
测试此抽象路径名与给定对象的相等性。 |
boolean |
exists() |
测试此抽象路径名表示的文件或目录是否存在。 |
File |
getAbsoluteFile() |
返回此抽象路径名的绝对形式。 |
String |
getAbsolutePath() |
返回此抽象路径名的绝对路径名字符串。 |
File |
getCanonicalFile() |
返回此抽象路径名的规范形式。 |
String |
getCanonicalPath() |
返回此抽象路径名的规范路径名字符串。 |
long |
getFreeSpace() |
通过此抽象路径名返回分区 named中未分配的字节数。 |
String |
getName() |
返回此抽象路径名表示的文件或目录的名称。 |
String |
getParent() |
返回此抽象路径名父项的路径名字符串,如果此路径名未指定父目录,则返回 null 。 |
File |
getParentFile() |
返回此抽象路径名父项的抽象路径名,如果此路径名未指定父目录,则返回 null 。 |
String |
getPath() |
将此抽象路径名转换为路径名字符串。 |
long |
getTotalSpace() |
通过此抽象路径名返回分区 named的大小。 |
long |
getUsableSpace() |
通过此抽象路径名返回分区 named上此虚拟机可用的字节数。 |
int |
hashCode() |
计算此抽象路径名的哈希码。 |
boolean |
isAbsolute() |
测试此抽象路径名是否为绝对路径。 |
boolean |
isDirectory() |
测试此抽象路径名表示的文件是否为目录。 |
boolean |
isFile() |
测试此抽象路径名表示的文件是否为普通文件。 |
boolean |
isHidden() |
测试此抽象路径名指定的文件是否为隐藏文件。 |
long |
lastModified() |
返回上次修改此抽象路径名表示的文件的时间。 |
long |
length() |
返回此抽象路径名表示的文件的长度。 |
String[] |
list() |
返回一个字符串数组,用于命名此抽象路径名表示的目录中的文件和目录。 |
String[] |
list(FilenameFilter filter) |
返回一个字符串数组,用于命名由此抽象路径名表示的目录中的文件和目录,以满足指定的过滤器。 |
File[] |
listFiles() |
返回一个抽象路径名数组,表示此抽象路径名表示的目录中的文件。 |
File[] |
listFiles(FileFilter filter) |
返回一个抽象路径名数组,表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。 |
File[] |
listFiles(FilenameFilter filter) |
返回一个抽象路径名数组,表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。 |
static File[] |
listRoots() |
列出可用的文件系统根目录。 |
boolean |
mkdir() |
创建此抽象路径名指定的目录。 |
boolean |
mkdirs() |
创建此抽象路径名指定的目录,包括任何必需但不存在的父目录。 |
boolean |
renameTo(File dest) |
重命名此抽象路径名表示的文件。 |
boolean |
setExecutable(boolean executable) |
一种方便的方法,用于设置此抽象路径名的所有者执行权限。 |
boolean |
setExecutable(boolean executable, boolean ownerOnly) |
设置此抽象路径名的所有者或每个人的执行权限。 |
boolean |
setLastModified(long time) |
设置此抽象路径名指定的文件或目录的上次修改时间。 |
boolean |
setReadable(boolean readable) |
一种方便的方法,用于设置此抽象路径名的所有者读取权限。 |
package com.huang.TestIO.Demo01;
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类的使用
*
*/
public class Demo01 {
public static void main(String[] args) {
//File类表示一个文件或目录
//"H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt"
//"H:/JAVA/2.Java常用类、集合、IO/IO/word1.txt"
/*"H:"+File.separator+"JAVA"+File.separator+"2.Java常用类、集合、IO"
+ File.separator+"IO"+File.separator+"word2.txt"*/
File f1 = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
File f2 = new File("H:/JAVA/2.Java常用类、集合、IO/IO/my");
File f3 = new File("H:"+File.separator+"JAVA"+File.separator+"2.Java常用类、集合、IO"
+ File.separator+"IO"+File.separator+"word2.txt");
if(!f1.exists()){//判断文件是否存在,异常抛出 -->IOException
try {
f1.createNewFile();
System.out.println("文件创建成功!");
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("是否为文件夹:"+f1.isDirectory());
System.out.println("是否为文件:"+f1.isFile());
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());
Date date = new Date(f.lastModified());
DateFormat df = new SimpleDateFormat("YYYY/MM HH:mm:ss");
System.out.println("文件最后修改的时间 = "+df.format(date));
System.out.println("文件最后修改的时间 = "+f.lastModified());
System.out.println("------------------");
}
File f4 = new File("test.txt");
System.out.println(f4.getPath());//相对路径
System.out.println(f4.getAbsolutePath());//绝对路径
//创建文件夹
File f5 = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my1");
f5.mkdir();//单级目录
//f5.mkdirs();//若创建的目录,多级目录不存在时使用
//重命名与移动文件
//f5.renameTo(new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my2"));
//f5.renameTo(new File("H:\\JAVA\\2.Java常用类、集合、IO\\my1"));
System.out.println("-----------------");
//过滤文件
File f6 = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my");
//匿名函数形式
/* File[] files = f6.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(".txt");
}
});*/
//Lambda表达式形式
File[] files = f6.listFiles((pathname)->pathname.getName().endsWith(".txt"));
for(File f:files){
System.out.println(f.getName());
}
}
}
package com.huang.TestIO.Demo01;
import java.io.File;
/**
* 指定目录中查找文件
*/
public class Demo02 {
public static void main(String[] args) {
findFile(new File("H:\\JAVA\\"),".txt");
}
//查找文件方法
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());
}
}
}
}
IO流:输入输出流(Input/Output)
流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直接的进行数据操作。
IO流分类:
字节是数据传输的基本单位,文件内容也是以字节为单位存储的;
从文件中把数据读到程序使用:输入流,从程序把数据写到文件使用:输出流。
InputStream&OutputStream
字节输出流:OutputStream抽象类是表示输出字节流的所有类的超类。输出流接受输出字节并将这些字节发送到InputStream类某个接收器要向文件中输出,使用FileOutputStream类
字节输入流:InputStream抽象类是表示输入字节流的所有类的超类。FileInputStream从文件系统中的某个文件中获得输入字节。
package com.huang.TestIO.Demo02;
import java.io.*;
/**
* 字节输出输入流
* 输出流:超类OutputStream,对文件的输出流使用子类FileOutputStream
* 输入流:超类InputStream,对文件输入流使用子类FileInputStream
*/
public class Demo01 {
public static void main(String[] args) {
out();
in();
}
private static void out(){
//0、确定目标文件
File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
//1、构建一个文件输出流对象
try {
OutputStream out = new FileOutputStream(file,true);//append 为true 表示追加内容
//2、输出的内容
// String line = System.getProperty("line.separator");//获取换行符
// String str = "温柔的故乡"+line;
String str = "温柔的故乡\r\n";//\r\n表示换行
//3、把内容写入到文件
out.write(str.getBytes());
//4、关闭流
out.close();
System.out.println("write success!");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void in(){
//0、确定目标文件
File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
//1、构建一个文件输入流对象
try {
InputStream in = new FileInputStream(file);
byte[] bytes = new byte[1024];
StringBuilder buf = new StringBuilder();
int len = -1;//表示每次读取的字节长度
//把数据读入到数组当中,并返回读取的字节数,当不等于-1时,表示读取到数据;等于-1,表示文件已经读取完毕。
while((len = in.read(bytes)) != -1){
buf.append(new String(bytes));//根据读取到的字节数组,转换为字符串内容,添加到StringBilder中
}
//打印内容
System.out.println(buf);
//关闭输入流
in.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
输入输出字节流操作原理:每次只会操作一个字节(从文件中读取或写入)
用于解决多次提取字节,而取到一半字节译不出来的问题。
Writer
写入字符流的抽象类。子类必须实现的方法仅有write(char[],int,int)、f lush()和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。
与OutputStream一样,对文件的操作使用:FileWriter类完成。
Reader
用于读取字符流的抽象类。子类必须实现的方法只有read(char[],int,int)和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。
使用FileReader类进行实例化操作。
文件字符操作流会自带缓存,默认大小为1024字节,在缓存满后、或手动刷新缓存、或关闭流时会把数据写入文件;
字节操作流,默认每次执行写入操作会直接把数据写入文件。
字节流与字符流的区别:(字符流的内部实现还是字节流)
如何选择字节流/字符流:
一般操作非文本文件时,使用字节流;操作文本文件时,建议使用字符流。
package com.huang.TestIO.Demo03;
import java.io.*;
/**
* 字符流
* 字符输出流:Writer,对文件的操作使用类:FileWriter
* 字符输入流:Reader,对文件的操作使用类:FileReader
* 每次操作的单位时一个字符
*/
public class Demo01 {
public static void main(String[] args) {
// out();
in();
}
private static void out(){
File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
try {
Writer out = new FileWriter(file,true);
out.write("良辰美景奈何天");
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void in(){
File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.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();
}
}
}
实例:文件的复制
package com.huang.TestIO.Demo03;
import java.io.*;
/**
* 文件复制
* 从一个输入流中读取数据,然后通过输出流写入目标文件
* 一边读一边写
*/
public class Demo02 {
public static void main(String[] args) {
System.out.println("start cpoy...");
copy("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt","H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my\\word.txt");
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 {
if(in!=null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(out!=null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
转换流,可以将一个字节流转换为字符流,也可以将一个字符流转换为字节流。
package com.huang.TestIO.Demo04;
import java.io.*;
import java.nio.charset.Charset;
/**
* 转换流
* - OutputStreamWriter:可以将输出的字符流转换为字节流的输出形式
* - InputStreamReader:将输入的字节流转换为字符流输入形式
*/
public class demo01 {
public static void main(String[] args) throws IOException {
// InputStream in = new FileInputStream("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
// read(in);
OutputStream out = new FileOutputStream("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt",true);
write(out);
}
public static void read(InputStream in) throws IOException {
Reader reader = new InputStreamReader(in, Charset.defaultCharset());
char[] cs = new char[1024];
int len = -1;
while((len=reader.read(cs)) != -1){
System.out.println(new String(cs,0,len));
}
reader.close();
}
public static void write(OutputStream out) throws IOException {
Writer writer = new OutputStreamWriter(out,Charset.defaultCharset());
writer.write("赏心悦目谁家院\r\n");
writer.close();
}
}
对文件或其它目标频繁的读写操作,效率低,性能差。
使用缓冲流的好处:能够更高效的读写信息]原理是将数据先缓冲起来,然后一起写入或者读取出来。
package com.huang.TestIO.Demo05;
import java.io.*;
/**
* 缓存流
* 缓存目的:解决在写入文件操作时,频繁操作文件所带来的性能降低的问题
* BufferedOutputStream 内部缓存大小时8KB,每次写入时存储到缓存中的byte数组中,
* 当数组存满时,会把数组中的数据写入到文件中,并且缓存下标归零。
*/
public class Demo01 {
public static void main(String[] args) throws IOException {
// byteWriter();
byteReader2();
}
private static void byteWriter() throws IOException {
File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
OutputStream out = new FileOutputStream(file,true);
//构造一个字节缓冲流
BufferedOutputStream bos = new BufferedOutputStream(out);
String info = "无可奈何花落去,似曾相识燕归来。\r\n";
bos.write(info.getBytes());
bos.close();
// out.close();
}
private static void byteReader() throws IOException {
File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
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();
}
private static void byteReader2(){
File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
InputStream in = null;
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));
}
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.huang.TestIO.Demo05;
import java.io.*;
/**
* 字符缓存流
*- BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
*- BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
*
* 字符流
* 1、加入字符缓存流,增强读取功能(readLine)
* 2、更高效的读取数据
* FileReader:内部使用InputStreamReader(sun.nio.cs.StreamDecoder),解码过程,byte-->char,默认缓存大小时8K
* BufferedReader:默认缓存大小时8K,但是可以手动指定缓存大小,把数据直接读取到缓存中,减少每次转换过程,效率更高
* BufferedWriter 同上
*/
public class Demo02 {
public static void main(String[] args) {
}
private static void charReader() throws IOException {
File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
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();//同时关闭reader
}
private static void charWriter() throws IOException {
File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
Writer writer = new FileWriter(file);
//为字符流提供缓冲,已达高效读取的目的
BufferedWriter bw = new BufferedWriter(writer);
bw.write("天生我才必有用!");
bw.flush();//刷新缓存
bw.close();//同时关闭reader
}
}
打印流的主要功能是用于输出,在整个10包中打印流分为两种类型:
打印流可以很方便的进行输出
package com.huang.TestIO.Demo06;
import java.io.*;
/**
* 打印流
* - 字节打印流:PrintStream
* 在字节输出时,可以增强输出功能
* - 字符打印流:PrintWriter
*/
public class Demo01 {
public static void main(String[] args) throws IOException {
// bytePrint();
charPrint();
}
private static void bytePrint() throws FileNotFoundException {
File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
OutputStream out = new FileOutputStream(file);
//加缓存
BufferedOutputStream bos = new BufferedOutputStream(out);
//增强打印功能
PrintStream ps = new PrintStream(bos);
ps.println("天若有情天亦老");
ps.close();
}
private static void charPrint() throws IOException {
File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
Writer out = new FileWriter(file);
//加缓存
BufferedWriter bos = new BufferedWriter(out);
//增强打印功能
PrintWriter pw = new PrintWriter(bos);
pw.println("天若有情天亦老");
pw.close();
}
}
对象流的两个类:
序列化一组对象:
在序列化操作中,同时序列化多个对象时,反序列化也必须按顺序操作,如果想要序列化一组对象该如何操作呢?
序列化一组对象可采用:对象数组的形式,因为对象数组可以向Object进行转型操作。
transient关健:
如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。
package com.huang.TestIO.Demo07.Demo01;
import java.io.Serializable;
//java.io.NotSerializableException,没有序列化
//如果一个类创建对象,需要被序列化,那么该类必须实现Serializable接口,
//Serializable 是一个标记接口,没有任何定义,为了告诉JVM该类对象可以被序列化
//什么时候对象需要被序列化呢?
//1、把对象保存到文件中(存储到物理介质)
//2、对象需要在网络上传输
//如果对象没有实现Serializable接口,会报错java.io.NotSerializableException
//java.io.NotSerializableException,没有序列化
public class Dog implements Serializable {
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) {
this.name = name;
this.age = age;
this.sex = sex;
}
public Dog() {
super();
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
package com.huang.TestIO.Demo07.Demo01;
/**
* 对象流
* - ObjectOutputStream 将 Java对象的基本数据类型和图形写入OutputStream
* - ObjectlnputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
*
*
*/
import java.io.*;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
writerObjects();
readObject();
}
/**
*反序列化的过程
* 从文件中把对象的内容读取出来,还原成对象
*/
private static void readObject() throws IOException, ClassNotFoundException {
File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\dog.txt");
InputStream in = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(in);
Dog[] dog = (Dog[])ois.readObject();
ois.close();
System.out.println(dog[0]);
System.out.println(dog[1]);
}
/**
* 对象序列化
* 把对象写入文件:实际写入的是类名、属性名、属性类型、属性的值等
*/
private static void writerObject() throws IOException {
Dog dog = new Dog("豆豆",1,"母");
File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\dog.txt");
OutputStream out = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(dog);
oos.close();
}
/**
* 对象序列化
* 把对象写入文件:实际写入的是类名、属性名、属性类型、属性的值等
*/
private static void writerObjects() throws IOException {
Dog dog = new Dog("豆豆",1,"母");
Dog dog1 = new Dog("二哈",2,"公");
Dog[] dogs = {dog,dog1};
File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\dog.txt");
OutputStream out = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(dogs);
oos.close();
}
}
ByteArraylnputStream
包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪read方法要提供的下一个字节。
关闭ByteArraylnputStream无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何IOException。
ByteArrayOutputStream
此类实现了一个输出流,其中的数据被写入一个byte数组。缓冲区会随着数据的不断写入而自动增长。
可使用toByteArray()和 toString()获取数据。
关ByteArrayOutputStream无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何IOException
ByteArraylnputStream 可以将字节数组转化为输入流;
ByteArrayOutputStream可以捕获内存缓冲区的数据,转换成字节数组。
package com.huang.TestIO.Demo08;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
/**
* 字节数组流:
*基于内存操作,内部维护着宇哥字节数组,可以利用流的读取机制来处理字符串
* 无需关闭
*/
public class Demo01 {
public static void main(String[] args) {
byteArray();
}
private static void byteArray(){
String str = "234csdfwt5efHDr4&^&*(90c";
ByteArrayInputStream bais = new ByteArrayInputStream(str.getBytes());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int current = -1;//每次读取字节
while((current = bais.read()) != -1){
if((current>=65 && current<=90) ||(current>=97 && current<=122)){
baos.write(current);
}
}
//此时无需关闭,原因:字节数组流是基于内存的操作流
System.out.println(baos.toString());
}
}
DatalnputStream:
DataOutputStream:
package com.huang.TestIO.Demo09;
import java.io.*;
/**
* 数据流
* 与机器无关的操作Java的基本数据类型
*/
public class Demo01 {
public static void main(String[] args) throws IOException {
// Write();
Read();
}
private static void Write() throws IOException {
File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.dat");
OutputStream out = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(out);
DataOutputStream dos = new DataOutputStream(bos);
dos.writeInt(10);//写入10个字节
dos.writeByte(1);
dos.writeUTF("中");
dos.close();
}
private static void Read() throws IOException {
File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.dat");
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();
}
}
package com.huang.TestIO.Demo09;
import java.io.*;
import java.util.Enumeration;
import java.util.Vector;
/**
* 文件分割与合并
*/
public class Demo02 {
public static void main(String[] args) throws IOException {
// File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my\\图片.png");
// disvision(file,1024*200);
InputStream in1 = new FileInputStream(new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my\\1-temp-图片.png"));
InputStream in2 = new FileInputStream(new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my\\2-temp-图片.png"));
InputStream in3 = new FileInputStream(new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my\\3-temp-图片.png"));
InputStream in4 = new FileInputStream(new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my\\4-temp-图片.png"));
InputStream in5 = new FileInputStream(new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my\\5-temp-图片.png"));
//集合工具类,内部实现使用了数组
Vector<InputStream> v = new Vector<>();
v.add(in1);
v.add(in2);
v.add(in3);
v.add(in4);
v.add(in5);
Enumeration<InputStream> es = v.elements();
merge(es);
}
/**
* 文件分割
* targetFile:要分割的目标文件
* cutSize:每个文件大小
*/
private static void disvision(File targetFile, long cutSize) throws IOException {
if(targetFile == null)return;
//计算总分割的文件数
int num = targetFile.length()%cutSize==0?
(int)(targetFile.length()/cutSize):(int)(targetFile.length()/cutSize + 1);
//构造一个文件输入流
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("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my\\"+(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--;
}
//计算每个文件大小除以 1024Byte = 1KB 的余数,决定是否在读取一次
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();
}
/**
* 文件合并
*/
private static void merge(Enumeration<InputStream> es) throws IOException {
//构造一个合并流
SequenceInputStream sis = new SequenceInputStream(es);
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my\\-合并-图片.png")
);
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("合并完成!");
}
}
合并流
**SequencelnputStream **表示其他输入流的逻辑串联。
它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。
字符串流
管道流
管道输入流应该连接到管道输出流,管道输入流提供要写入管道输出流的所有数据字节。
通常,数据由某个线程从PipedlnputStream对象读取,并由其他线程将其写入到相应的PipedOutputStream。
一个线程写入,一个线程读取,作用:用于线程之间的数据通讯!
不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开。如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏。
其中StringReader和StringWriter未来在操作XML或JSON时可能用到!
RandomAccessFile功能虽然强大,但在读写效率上的缺陷问题,可以使用nio的内存映射文件来替代!
package com.huang.TestIO.Demo11;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* RandomAccessFile类
*/
public class Demo01 {
public static void main(String[] args) {
try {
//读取文件
RandomAccessFile r = new RandomAccessFile("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\dog.txt","r");
//写入文件
RandomAccessFile w = new RandomAccessFile("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my\\dog.txt","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) {
e.printStackTrace();
}
System.out.println("copy success!");
}
}
Properties(Java.util.Properties),主要用于读取Java的配置文件,各种语言都有自己所支持的配置文件,配置文件中很多变量是经常改变的,这样做也是为了方便用户,让用户能够脱离程序本身去修改相关的变量设置。(在JavaWeb和Java EE中经常用到)
它提供了几个主要的方法:
软件发布流程:编写源代码–>打包项目–>发布–>服务器
package com.huang.TestIO.Demo12;
import java.io.*;
import java.util.Properties;
/**
* Properties文件操作
* properties可以用来做配置文件
* javaweb javaee开发中通常会用到
*
* ResourcceBundle只读
* Properties可读,可写
*/
public class Demo01 {
public static String version = "";
public static String username = "";
public static String password = "";
//静态代码块,只会执行一次
static {
// readConfig();//读取一次结束
}
/**
* 读取properties配置文件
*/
private static void readConfig(){
Properties p = new Properties();
try {
//线程-类加载器-资源,通过当前线程的类加载器,来加载指定包下的配置文件
InputStream inStream = Thread.currentThread().getContextClassLoader().
getResourceAsStream("com/res/config.properties");
// InputStream inStream = new FileInputStream("H:\\JAVA\\1.Java零基础\\JavaCode\\Java基础语法" +
// "\\src\\com\\huang\\TestIO\\Demo12\\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();
}
}
/**
*对属性文件的写操作
*/
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("H:\\JAVA\\1.Java零基础\\JavaCode\\Java基础语法" +
"\\src\\com\\huang\\TestIO\\Demo12\\config.properties");
p.store(out,"update confige");
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
//writeConfig("2","admin","987654");
readConfig();
System.out.println(Demo01.version);
System.out.println(Demo01.username);
System.out.println(Demo01.password);
}
}
java中实现zip的压缩与解压缩
装饰者模式的应用场景是,不改变原有类结构基础上,在新增或者限制或者改造功能时候
意图:
动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。该模式以对客户端透明的方式扩展对象的功能。
适用环境:
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
处理那些可以撤消的职责。
当不能采用生成子类的方法进行扩充时。一种情况是,**可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。**另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
类图:
涉及角色:
抽象组件:定义一个抽象接口,来规范准备附加功能的类。
具体组件:将要被附加功能的类,实现抽象构件角色接口。
抽象装饰者:持有对具体构件角色的引用并定义与抽象构件角色一致的接口。
具体装饰:实现抽象装饰者角色,负责为具体构件添加额外功能。
代码实现:
Drink.java 被装饰者对象的接口
SoyaBeanMilk.java 具体的被装饰者对象
EggDecorator.java 具体装饰者对象
SugarDecorator.java 具体装饰者对象
BlackBeanDecorator.java 具体装饰者对象
Decorator.java 装饰者基类
Test.java 测试
OutputStream out = new FileOutputStream("xxx");
BufferedOutputStream bos = new BufferedOutputStream(out);
PrintStream ps = new PrintStream(bos);
ps.print(..);
装饰者模式小结:
OO原则:动态地将责任附加到对象上。想要扩展功能,装饰者提供有别于继承的另一种选择。
要点:
1、继承属于扩展形式之一,但不见得是达到弹性设计的最佳方案。
2、在我们的设计中,应该允许行为可以被扩展,而不须修改现有的代码。
3、组合和委托可用于在运行时动态地加上新的行为。
4、除了继承,装饰者模式也可以让我们扩展行为。
5、装饰者模式意味着一群装饰者类,这些类用来包装具体组件。
6、装饰者类反映出被装饰的组件类型(实际上,他们具有相同的类型,都经过接口或继承实现)。
7、装饰者可以在被装饰者的行为前面与/或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的。
8、你可以有无数个装饰者包装一个组件。
9、装饰者一般对组建的客户是透明的,除非客户程序依赖于组件的具体类型。
了解编码问题可以帮助我们在开发中避免乱码,尤其在学习后续的web开发中
在计算机世界里,任何的文字都是以指定的编码方式存在的。
常见编码有: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个字节不等,一般在中文网页中使用此编码,因为这样可以节省空间。
造成乱码的根本原因:
package com.huang.TestIO.Demo15;
import java.io.UnsupportedEncodingException;
/**
* 字符编码
*/
public class CodeDemo {
public static void main(String[] args) {
//通常产生乱码的情况是,两个不兼容的编码相互转换
String info = "西北望,射天狼。";//GB2312
try {
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();
}
}
}
NIO模式是新的主流IO操作模式,最重要的提升就是在高效读取数据上,未来学习的Netty网络通信框架就是基于NIO实现
NIO可以提高数据的读取效率的根本原因是利用了操作系统(如Linux)底层的10多路复用机制实现
为什么要使用NIO?
NIO是JDK1.4加入的新包,NIO的创建目的是为了让Java程序员可以实现高速I/O而无需编写自定义的本机代码。
NIO将最耗时的I/O操作(即填充和提取缓冲区)转移回操作系统,因而可以极大地提高速度。
流与块的比较
缓冲区
在NIO库中,断有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区中的。任何时候访问NIO中的数据,您都是将它放到缓冲区中。
缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。但是一个缓冲区不仅仅是一个数组。缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读写进程。
缓冲区类型
最常用的缓冲区类型是ByteBuffer。一个ByteBuffer可以在其底层字节数组上进行get/set操作(即字节的获取和设置)。ByteBuffer不是NIO中唯一的缓冲区类型。事实上,对于每一种基本Java类型都有一种缓冲区类型:
ByteBuffer
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
缓冲区内部细节:
状态变量:(可以用三个值指定缓冲区在任意时刻的状态)
position limit capacity
package com.huang.TestIO.Demo16;
import java.nio.ByteBuffer;
/**
* New IO
*/
public class Demo01 {
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());
System.out.println("----------------------");
//缓冲区反转
buf.flip();//flip()-->limit=position;position=0;
System.out.println("position=" + buf.position());
System.out.println("limit=" + buf.limit());
System.out.println("capacity=" + buf.capacity());
System.out.println("----------------------");
//告知在当前位置和限制之间是否有元素
if(buf.hasRemaining()){
//返回当前位置与限制之间的元素数
for(int i=0; i<buf.remaining();i++){
byte b = buf.get(i);
System.out.println(b);
}
}
//取值不会对状态变量有影响
System.out.println("position=" + buf.position());
System.out.println("limit=" + buf.limit());
System.out.println("capacity=" + buf.capacity());
System.out.println("----------------------");
}
}
**通道:**Channel
Channel 是一个对象,可以通过审读取和写入数据。拿NIO与原来的I/O做个比较,通道就像是流。
程序–>缓冲区–>通道–>文件
正如前面提到的,所有数据都通过Buffer对象来处理。
永远不会将字节直接写入通道中,相反,是将数据写入包含一个或者多个字节的缓冲区。
同样,不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。
使用通道读写文件示例
package com.huang.TestIO.Demo16;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
/**
* 使用通道复制文件
* FileChannel类
* 比较IO操作的性能比较
* 1、内存映射最快
* 2、NIO读写文件
* 3、使用了缓存的IO流
* 4、无缓存的IO流
*/
public class Demo02 {
public static void main(String[] args) throws IOException {
copyFile();
randomAccessFileCopy();
}
private static void copyFile() throws IOException {
//创建一个输入文件的通道
FileChannel fcIn = new FileInputStream
("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\picture.png").getChannel();
//创建一个输出文件的通道
FileChannel fcOut = new FileOutputStream("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my1\\picture.png").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!");
}
//使用内存映射实现文件复制
private static void randomAccessFileCopy() throws IOException {
RandomAccessFile in = new RandomAccessFile("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\picture.png","r");
RandomAccessFile out = new RandomAccessFile("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my1\\picture1.png","rw");
FileChannel fcIn = in.getChannel();
FileChannel fcOut = out.getChannel();
long size = fcIn.size();//输入流的大小
//输入流的缓冲区
MappedByteBuffer inBuff = fcIn.map(FileChannel.MapMode.READ_ONLY,0,size);
//输出流的缓冲区
MappedByteBuffer outBuff = fcOut.map(FileChannel.MapMode.READ_WRITE,0,size);
for(int i=0;i<size;i++){
outBuff.put(inBuff.get());
}
//关闭(关闭通道时会写入数据块)
fcIn.close();
fcOut.close();
in.close();
out.close();
System.out.println("copy succsee!");
}
}
JDK1.7引人了新的IO操作类,java.nio.file包下,Java NIO Path接口、Paths类和Files类
Path接口
Files工具类
读写文件
static path write(Path path,byte[] bytes,OpenOption… options)写入文件
static byte[] readAllBytes(Path path)读取文件中的所有字节。
复制、剪切、删除
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 deletelfExists(Path path)
创建文件和目录
//创建新目录,除了最后一个部件,其他必须是已存在的
Files.createDirectory(path);
//创建路径中的中间目录,能创建不存在的中间部件
Files.createDirectories(path);
//创建一个空文件,检查文件存在,如果已存在则抛出异常而检查文件存在是原子性的,
//因此在此过程中无法执行文件创建操作
Files.create File(path);
//添加前/后缀创建临时文件或临时目录
Path newPath = Files.createTempFile(dir,prefix,suffix);
Path newPath = Files.createTempDirectory(dir,prefix);
资料整理于:千峰Java
视频连接:点击进入