黑马程序员————IO流(2)


------Java培训Android培训iOS培训.Net培训、期待与您交流!-------


一、File类

 

用来将文件和文件夹封装成对象

方便对文件和文件夹的属性进行操作

File对象可以作为参数传递给流的构造函数

 

流对象:

FileReader

FileWriter

FileOutputStre

FileInputStream

 

常用的方法:

1.构造方法和字段

//1.可以将一个已存在或者不存在的文件或者目录封装成对象

File f1 = new File("f:\\a.txt");


//2.这个和1比较可以改变目录和目录下的其他文件,不局限于f盘和f盘下的a.txt

File f2 = new File("f:\\","a.txt");


//3.和2比较可以对f进行file对象操作,原来只能按照字符串进行操作

File f = new File("f:\\");

File f3 = new File(f,"a.txt");


//4.字段摘要

/*File f4 = new File("f:\\abc\\a.txt");在UNIX下不可以,

UNIX只认同“/”所以可以用file.separator*/

File f4 = new File("f:"+File.separator+"abc"+File.separator+"a.txt");

 

2.常用的方法

(1)获取

File file = new File("gbk_3.txt");

//获取名称

String name = file.getName();


//获取文件的路径

String path = file.getAbsolutePath();//绝对路径

String path1 = file.getPath();//相对路径


//文件大小

long len = file.length();


//获取文件的修改时间

long time = file.lastModified();

 

补充:获取有效的时间(*)

long time = file.lastModified();

Date date = new Date(time);

DateFormat dateFormat =  DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.LONG);

String str_time = dateFormat.format(date);

 

(2)创建和删除

 

文件的创建和删除

File file = new File("file.txt");

// 1.文件的创建,和输出流不一样,如果文件存在则不创建,反之则创建

file.createNewFile();


//2.文件的删除

file.delete();

 

文件夹的创建和删除

//1.单目录文件夹

File dir1 = new File("nba");

dir1.mkdir();//创建

dir1.delete();//删除

 

//2.多级目录文件夹

File dir = new File("abc\\v\\c\\d\\v\\h\\j\\k\\l");//将“l”封装成了对象

dir.mkdirs();

dir.delete();//删除的是“l”文件夹

 

注意:windows删除是从内往外删除,如果目录中有内容目录则不会被删除

 

(3)判断

File file = new File("a.txt");

//判断文件是否存在

file.exists();


//判断是否是文件

file.isFile();


//判断是否是目录

file.isDirectory();

 

注意:最好先判断文件是否存在,如果文件不存在,判断的结果都是false 

 

(4)重命名

File file = new File("f:\\0.jpg");

File file2 = new File("f:\\3.jpg");

File file3 = new File("d:\\aa.jpg");


//重命名

file.renameTo(file2);


//剪切

file.renameTo(file3);

 

(5)获取系统的根目录及容量的获取

File file = new File("d:\\");

//可用空间,迅雷看看下载就选择最大空闲的盘符

System.out.println(file.getFreeSpace());


//总容量

System.out.println(file.getTotalSpace());


//虚拟机可用空间(不常用)

System.out.println(file.getUsableSpace());

 

(6)获取目录内容

/*

 * 获取当前目录下文件以及文件夹名称,包含隐藏的文件,调用list方法

 * 的file对象中封装的必须是目录,否则会发生空指针异常,如果访问的

 * 系统级目录也会发生空指针异常,如果目录存在但是没有内容,会返回

 * 一个数组,但是长度为0。

*/

File file = new File("f:\\");

String[] names = file.list();

for(String name:names){

System.out.println(name);

}

 

(7)过滤器


过滤器1的定义:

public class FileByJava implements FilenameFilter {
@Override
public boolean accept(File dir, String name) {
//遍历数组,进行运算,只要返回的是true,就存储文件
return name.endsWith(".java");
}
}
过滤f盘下的".java"文件:
File file = new File("f:\\");
String[] names = file.list(new FileByJava());
for(String name:names){
System.out.println(name);
}

过滤器2的定义:(过滤隐藏文件)

public class FilterByHidden implements FileFilter {
@Override
public boolean accept(File pathname) {
return !pathname.isHidden();
}
}

过滤器3的定义:(根据后缀名过滤)

public class SuffixFilter implements FilenameFilter {
private String suffix;
private SuffixFilter(String suffix) {
super();
this.suffix = suffix;
}
@Override
public boolean accept(File dir, String name) {
return name.endsWith(suffix);
}
}

例子:

1.深度遍历文件夹 ,并且有层级显示

import java.io.File;
public class TestDemo6 {
/**
 * @param args
 */
public static void main(String[] args) {
File dir = new File("f:\\demodir");
listAll(dir,0);
}
private static void listAll(File dir,int level) {
System.out.println(getSpace(level)+dir.getName());
level++;
//获取指定目录下的当前的所有文件夹或者文件对象
File[] files = dir.listFiles();
for(int x = 0; x<files.length; x++){
if(files[x].isDirectory())
listAll(files[x],level);
else 	System.out.println(getSpace(level)+files[x].getAbsolutePath());
}
}
private static String getSpace(int level) {
StringBuilder sb = new StringBuilder();
for(int x=0; x<level; x++){
sb.append("    ");
}
return sb.toString();
}
}

2.递归

定义:函数自身或者间接的调用了自身

注意:(1)递归一定要明确条件,否则容易栈溢出,(2)注意递归的次数,防止栈溢出

 

求二进制

private static void toBin(int num) {
if(num>0)
toBin(num/2);
System.out.print(num%2);
}

求和

private static int getSum(int num) {
if(num==1)
return 1;
return num+getSum(num-1);
}

3.删除目录

 

需要深度遍历目录,必须从里面往外删除

import java.io.File;
public class TestDemo8 {
/**
 * @param args
 */
public static void main(String[] args) {
File dir = new File("f:\\demodir");
removeDir(dir);
}
private static void removeDir(File dir) {
File[] files = dir.listFiles();
for(File file : files){
if(file.isDirectory())
removeDir(file);
else
System.out.println(file+" "+file.delete());
}
System.out.println(dir+" "+dir.delete());
}
}

二、Properties

 

特点:

1.该集合中的键和值嗾使字符串类型

2.该集合中的数据可以保存到流中,或者从流中获取

 

通常该集合用于操作以键值对形式存在的配置文件

 

1.Properties集合的存和取

Properties prop = new Properties();
//存储元素,用到setProperty方法
prop.setProperty("zhangsan", "31");
prop.setProperty("lisi", "28");
prop.setProperty("xiaoqiang", "15");
//修改元素,键不变值改变
prop.setProperty("zhangsan", "39");
//取元素,用到setProperty方法
Set<String> names = prop.stringPropertyNames();
for(String name : names){
String value = prop.getProperty(name);
System.out.println(name+":"+value);
}

2.list方法

Properties prop = new Properties();
prop.setProperty("zhangsan", "31");
prop.list(System.out);

3.store方法

Properties prop = new Properties();
prop.setProperty("zhangsan", "31");
prop.setProperty("lisi", "28");
prop.setProperty("xiaoqiang", "15");
//想要持久化存储信息需要关联流
FileOutputStream fos = new FileOutputStream("info.txt");
prop.store(fos, "info");
fos.close();

4.修改配置信息

 

load方法:

//集合中的数据来源于一个文件,注意必须要保证该文件中的数据时键值对,需要用到读取流
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("info.txt");
//使用load方法
prop.load(fis);
prop.list(System.out);
fis.close();

修改配置文件

/*
 * 1.读取这个文件,并将这个文件中的键值数据存储到集合中
 * 2.在通过集合对数据进行修改
 * 3.在通过流将修改后的数据存储到文件中
 */
//读取文件
File file = new File("info.txt");
if(!file.exists())
file.createNewFile();
FileReader fr = new FileReader("info.txt");
//创建集合存储数据
Properties prop = new Properties();
//将流中的数据存储到集合中
prop.load(fr);
//修改信息
prop.setProperty("zhangsan", "100");
//将修该后的数据存储
FileWriter fw = new FileWriter(file);
prop.store(fw, "info1");
fr.close();
fw.close();


注意:

不要紧跟着输入流创建后创建输出流,将输出流在修改配置文件后再创建,因为原来的数据会被覆盖。 

 

练习1:获取一个应用程序运行次数,如果超过5次,给出使用次数已到请注册的提示,并不要运行程序

 

思路:

1.应该有计数器

 

每次程序的启动都需要计数一次,并且是在原有的次数上进行计数

 

2.计数器是一个变量

 

程序启动时进行计数,计数器必须存在于内存并进行运算,而我们需要多次启动同一个应用程序,使用同一个计数器。而这就需要计数器的生命周期变长,从内存存储到硬盘文件中。

 

3.如何使用这个计数器呢?

 

首先,程序启动时,应该先读取这个用于记录计数器信息的配置文件,获取上一次计数器次数,并进行使用次数的判断。其次,对该次数进行自增,并自增后的次数重新存储到配置文件中。

 

4.文件中的信息该如何存储并体现呢?

 

直接存储数值可以,但不明确该数据的含义,所以起名字就显得比较重要。这就有了名字和值得对应,所以可以使用键值对,可用映射关系map集合搞定,又需要读取硬盘上的数据,所以map+io=properties

//将配置文件封装成file对象
File confile = new File("count.properties");
if(!confile.exists())
confile.createNewFile();
FileInputStream fis = new FileInputStream(confile);
Properties prop = new Properties();
prop.load(fis);
//从集合中通过键获取次数
String value = prop.getProperty("time");
int count = 0;
if(value!=null){
count=Integer.parseInt(value);
if(count>=5)
throw new RuntimeException("使用次数已到,请注册,购买正版!!");
}
count++;
//将改变后的次数重新存储到集合中
prop.setProperty("time", count+"");
FileOutputStream fos = new FileOutputStream(confile);
prop.store(fos, "");
fos.close();
fis.close();
}

练习2:建立指定扩展名的文件清单列表


思路:

1.必须进行深度遍历

2.要在遍历的过程中进行过滤,将符合条件的内容都存储到容器中

3.对容器中的这些内容进行遍历,并将绝对路径写入到文件中

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class TestDemo9 {
/**
 * @param args
 */
public static void main(String[] args) {
File dir = new File("F:\\Workspaces\\MyEclipse 10\\Review\\src");
FilenameFilter filter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".java");
}
};
List<File> list = new ArrayList<File>();
getFile(dir, filter, list);
File destFile = new File(dir,"test.txt");
write2File(list, destFile);
}
public static void getFile(File dir ,FilenameFilter filter,List<File> list){
File[] files = dir.listFiles();
for(File file : files){
if(file.isDirectory()){
getFile(file, filter, list);
}
else{
if(filter.accept(dir, file.getName())){
list.add(file);
}
}
}
}
public static void write2File(List<File> list,File destFile){
BufferedWriter bufw = null;
try {
bufw = new BufferedWriter(new FileWriter(destFile));
for(File file :list){
bufw.write(file.getAbsolutePath());
bufw.newLine();
bufw.flush();
}
} catch (IOException e) {
throw new RuntimeException("写入失败");
}finally{
if(bufw!=null)
try {
bufw.close();
} catch (IOException e) {
throw new RuntimeException("关闭失败");
}
}
}
}

三、IO包中的其他类

 

1.打印流

 

PrintStream


(1)提供了打印的方法可以对多种数据类型值进行打印,并保持数据的表示形式

(2)它不抛IoException


构造函数,接受三种不同的类型的值

(1)字符串路径

(2)File对象

(3)字节输出流

PrintStream out = new PrintStream("print.txt");
//out.write(97);结果是a,只写最低八位
out.print(97);//将97先变成字符,保持原样的数据打印到目的地
out.close();

PrintWriter


构造函数参数:

(1)字符串路径 

(2)File对象

(3)字节输出流

(4)字符输出流

 

例1

BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(System.out);
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line))
break;
out.println(line.toUpperCase());
out.flush();
}
out.close();
bufr.close();

例2

PrintWriter out = new PrintWriter(System.out,true);
替换上面的
PrintWriter out = new PrintWriter(System.out);
//可以不用刷新,即可以不用写out.flush();

例3

PrintWriter out = new PrintWriter(new FileWriter("out.txt"),true);
替换上面的
PrintWriter out = new PrintWriter(System.out,true);
//可以实现自动刷新

2.序列流-SequenceInputstream

 

对多个流进行合并

 

例子1:将1.txt,2.txt,3.txt文件中的数据合并成一个文件中

ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
for(int x=1; x<=3; x++){
al.add(new FileInputStream(x+".txt"));
}
Enumeration<FileInputStream> en = Collections.enumeration(al);
SequenceInputStream sis = new SequenceInputStream(en);
FileOutputStream fos = new FileOutputStream("4.txt");
byte[] buf = new byte[1024];
int len=0;
while((len=sis.read(buf))!=-1){
fos.write(buf,0,len);
}
fos.close();
sis.close();
}

例子2:文件的切割

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class TestDemo10 {
/**
 * @param args
 * @throws IOException 
 */
public static void main(String[] args) throws IOException {
File file = new File("f:\\1.mp3");
spiltFile(file);
}
private static void spiltFile(File file) throws IOException {
FileInputStream fis = new FileInputStream(file);
byte[] buf = new byte[1024*1024];
FileOutputStream fos = null;
int len = 0;
int count = 1;
File dir = new File("f:\\properties");
if(!dir.exists())
dir.mkdir();
while((len=fis.read(buf))!=-1){
fos=new FileOutputStream(new File(dir,(count++)+".part"));
fos.write(buf,0,len);
}
fis.close();
fos.close();
}
}

例子3文件的切割加配置文件

 

切割文件是,必须记录住被切割文件的名称,以及切割出来的碎片文件的个数,以便于合并。这个信息为了描述,使用键值对的方式,用到了properties对象

package io;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
public class TestDemo10 {
/**
 * @param args
 * @throws IOException 
 */
public static void main(String[] args) throws IOException {
File file = new File("f:\\1.flv");
spiltFile(file);
}
private static void spiltFile(File file) throws IOException {
FileInputStream fis = new FileInputStream(file);
byte[] buf = new byte[1024*1024];
FileOutputStream fos = null;
int len = 0;
int count = 1;
Properties prop = new Properties();
File dir = new File("f:\\properties");
if(!dir.exists())
dir.mkdir();
while((len=fis.read(buf))!=-1){
fos=new FileOutputStream(new File(dir,(count++)+".part"));
fos.write(buf,0,len);
fos.close();
}
prop.setProperty("partCount", count+"");
prop.setProperty("fileName", file.getName());
fos=new FileOutputStream(new File(dir, (count)+".properties"));
prop.store(fos, "info");
fis.close();
fos.close();
}
}
 

例子4 文件的合并加配置文件

package io;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Properties;
import filter.SuffixFilter;
public class TestDemo11 {
/**
 * @param args
 * @throws IOException 
 */
public static void main(String[] args) throws IOException {
File dir = new File("f:\\properties");
//因为该目录不知道有“x.properties”x是多少,但是只有一个扩展名为properties文件所以用到过滤器
File[] files = dir.listFiles(new SuffixFilter(".properties"));
if(files.length!=1){
throw new RuntimeException(dir+",该目录下没有properties文件或者不唯一");
}
//记录配置文件对象
File confile = files[0];
//获取该文件中的信息
Properties prop = new Properties();
FileInputStream fis = new FileInputStream(confile);
prop.load(fis);
String fileName = prop.getProperty("fileName");
int count = Integer.parseInt(prop.getProperty("partCount"));
//获取该目录下的所有碎片文件
File[] partFiles = dir.listFiles(new SuffixFilter(".part"));
if(partFiles.length!=(count-1)){
throw new RuntimeException("碎片文件不符文要求,个数不对,应该为"+count+"个");
}
ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
for(int x=1; x<partFiles.length; x++){
al.add(new FileInputStream(partFiles[x]));
}
Enumeration<FileInputStream> en = Collections.enumeration(al);
SequenceInputStream sis = new SequenceInputStream(en);
FileOutputStream fos = new FileOutputStream(fileName);
byte[] buf = new byte[1024];
int len = 0;
while((len=sis.read(buf))!=-1){
fos.write(buf,0,len);
}
fos.close();
sis.close();
}
}


 

3.对象的序列化-ObjectInputStrea和ObjectOutputStream

 

对象的序列化:将数据作为对象放入硬盘进行持久化存储

//一般存储对象的文件的后缀名用object
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.object"));
//对象的序列化,被序列化的对象必须实现Serializable接口
oos.writeObject(new Person("小强",25));
oos.close();

对象的反序列化:对象被序列化存储后,读取序列化的对象


ObjectInputStream对以前ObjectOutputStream写入的基本数据和对象进行反序列化

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.object"));
//因为知道文件对象是Person、,所以可以强转
Person p = (Person) ois.readObject();
System.out.println(p.getName()+":"+p.getAge());

Serializable接口

 

如果读取序列化对象文件,而反序列化过程中对象变了,则会放生异常。

 

解释:例如Person实现了Serializable接口,此时该Person具备了ID号,而序列化过程,序列化和这个来相关联,而反序列化过程中Person类改变了,所以ID号变了,因此放生了异常。

 

总的来说:Serializable就是给序列化的类加上一个ID号,用于判断类和对象是否是同一个版本

 

怎么给可序列化类声明一个ID号呢?

 

public class Person implements Serializable{

private static final long serialVersionUID =125845641651L;

后面代码略

 

序列化这个知识点在以后的web开发中会用到。

 

transient关键字


比如:有些数据,不是公共数据,对象特有的,不能静态

 

例如:不想将name这个非静态的数据序列化。

private transient String name;

private static int age;


结果反序列化的结果是:

null:0

 

4.RandomAccessFile-随机访问文件


不是IO体系的子类


特点:

1.该对象既能读又能写

2.该对象内部维护了一个byte数据,并通过指针操作数组中的元素

3.可以通过getFilePointer方法获取指针的位置,和通过get方法设置指针的位置

4.其实该对象就是讲字节输入流和输出进行了封装

5.该对象的源和吗目的只能是文件

 

1.写数据 

//如果文件不存在,则创建;如果存在则不会创建
RandomAccessFile raf = new RandomAccessFile("ranacc.txt", "rw");
raf.write("张三".getBytes());
//raf.write(97);只写最低8位
raf.writeInt(97);//按4个字节写,先写高位
raf.write("小王".getBytes());
raf.writeInt(25);
raf.close();

2.读取或随机读取

ranacc.txt文件内容

读取:

RandomAccessFile raf = new RandomAccessFile("ranacc.txt", "r");
byte[] buf = new byte[4];
raf.read(buf);
String name = new String(buf);
//因为年龄是整数,用readInt更方便
int age = raf.readInt();
System.out.println(name);
System.out.println(age);
raf.close();

随机读取:

RandomAccessFile raf = new RandomAccessFile("ranacc.txt", "r");
//通过seek来设置指针的位置
raf.seek(1*8);//随机的读取,只要指定指针的位置即可
byte[] buf = new byte[4];
raf.read(buf);
String name = new String(buf);
int age = raf.readInt();
System.out.println("name="+name);
System.out.println("age="+age);
//指针的位置
System.out.println("pos:"+raf.getFilePointer());
raf.close();

3.随机写

紧接着rannac.txt后面写入

RandomAccessFile raf = new RandomAccessFile("ranacc.txt", "rw");
raf.seek(raf.length());
raf.write("哈哈".getBytes());
raf.close();

用处:多线程写入用到RandomAccessFile。

 

 

5.管道流-PipedInputStream和PipedOutputStream

 

不能单线程使用,不然会死锁

 

例子:

package io;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
public class PipedDemo {
/**
 * @param args
 * @throws IOException 
 */
public static void main(String[] args) throws IOException {
PipedInputStream input = new PipedInputStream();
PipedOutputStream output = new PipedOutputStream();
input.connect(output);
new Thread(new Input(input)).start();
new Thread(new Output(output)).start();
}
}
class Input implements Runnable{
private PipedInputStream in;
Input(PipedInputStream in) {
super();
this.in = in;
}
@Override
public void run() {
try {
byte[] buf = new byte[1024];
int len = in.read(buf);
String s = new String(buf,0,len);
System.out.println(s);
in.close();
} catch (IOException e) {
e.printStackTrace();
};
}
}
class Output implements Runnable{
private PipedOutputStream out;
 Output(PipedOutputStream out) {
super();
this.out = out;
}
@Override
public void run() {
try {
Thread.sleep(5000);
out.write("s=hi,管道来了".getBytes());
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

6.操作基本数据类型:DataOutputStream 和DataInputStream 

 

写数据

DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeUTF("你好");
dos.close();

读数据

DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
String str = dis.readUTF();
System.out.println(str);
dis.close();

7.操作字节数组

 

不需要关闭流,此类中的close方法在关闭此流后仍可被调用。该对象一创建必须要有源,源是字节数组。

ByteArrayInputStream bis = new ByteArrayInputStream("abcd".getBytes());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int ch=0;
while((ch=bis.read())!=-1){
bos.write(ch);
}
System.out.println(bos.toString());

8.操作字符数组


CharArrayReader;

CharArrayWriter

类似上,源是字符数组。


9.操作字符串


StringReader;

StringWriter

类似上,源是字符串。


------Java培训Android培训iOS培训.Net培训、期待与您交流!-------


 

智能推荐

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
© 2014-2019 ITdaan.com 粤ICP备14056181号  

赞助商广告