黑马程序员——7.2.IO(File、Properties对象、打印流、流的分并与割合、对象的序列化、管道流)

时间:2023-02-18 15:06:17

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

File类

File类用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作。

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

创建File对象

public static void consMethod()
{
//可以将已有的和未出现的文件或者文件夹封装成对象。

//创建File对象
File f1 = new File("a.txt");

//创建包含父路径的File对象
File f2 = new File("c:\\abc","b.txt");

//把抽象路径封装成File对象
File d = new File("c:\\abc");

//把抽象路径File对象传递给构造函数创建新的File对象
File f3 = new File(d,"c.txt");

//考虑到不同系统的兼容性问题,可以使用File.separator代替目录分隔符
File f4 = new File("c:"+File.separator+"abc"+File.separator+"zzz"+File.separator+"a.txt");
}

File.separator是与系统有关的默认名称分隔符。在 UNIX 系统上,此字段的值为 ‘/’;在 Microsoft Windows 系统上,它为 ‘\’。

File对象的常用方法:
一、获取

import java.io.File;
import java.text.DateFormat;
import java.util.Date;

public class FileMethodDemo{
public static void main(String[] args){
getDemo();
}

public static void getDemo(){
//创建File对象
File file1 = new File("a.txt" );

//创建带路径的File对象
File file2 = new File("d:\\demo\\a.txt" );

//获取File对象的名称
String name = file2.getName();

//获取File对象的绝对路径
String absPath = file2.getAbsolutePath();

//获取File对象的抽象路径(创建对象的封装的路径)
String path2 = file2.getPath();

//获取文件的字节数大小
long len = file2.length();

//获取文件的最后修改时间
long time = file2.lastModified();

//获取文件的父目录(创建对象时封装的父路径)
//如果没有指定父目录,则返回 null
String parent1 = file1.getParent();
String parent2 = file2.getParent();

//用自定义日期格式初始化最后修改文件的时间
Date date = new Date(time);
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.LONG);
String str_time = df.format(date);
}
}

二、创建和删除

import java.io.File;
import java.io.IOException;

public class FileMethodDemo{
public static void main(String[] args) throws IOException {
createAndDeleteDemo();
}

public static void createAndDeleteDemo() throws IOException {
//创建File对象
File file = new File("file.txt" );

//使用File对象创建新文件
//如果文件不存在,则创建,如果文件存在,则不创建
boolean b1 = file.createNewFile();
System.out.println( "b1 = " + b1);

//使用delete方法删除File对象对应的文件或文件夹
//使用delete方法删除文件夹的时候,如果文件夹中有文件,会删除失败
boolean b2 = file.delete();
System.out.println( "b2 = " + b2);

//创建封装了路径的File对象
File dir = new File("abc" );

//创建封装了多级路径的File对象
File dir = new File("abc\\ab\\cc" );

//使用mkdir可以创建文件夹
boolean b3 = dir.mkdir();
System.out.println( "b3 = " + b3);

//使用mkdirs可以创建多级文件夹
boolean b3 = dir.mkdirs();
System.out.println( "b4 = " + b4);

//最里层目录被干掉,dir代表的是最里层的目录
boolean b5 = dir.delete();
}
}

三、判断

import java.io.File;
import java.io.IOException;

public class FileMethodDemo{
public static void main(String[] args) throws IOException {
isDemo();
}

public static void isDemo() throws IOException {
File f = new File("aaa.txt" );

//判断File对象对应的文件或文件夹存不存在
boolean b = f.exists();

System.out.println( "b = " + b);

if(!f.exists()){
f.createNewFile();
}

//最好先判断是否存在
if(f.exists()){

//判断File对象对应的是不是文件
System.out.println(f.isFile());

//判断File对象对应的是不是文件夹
System.out.println(f.isDirectory());
}

f = new File("aa\\bb" );

f.mkdirs();
if(f.exists()){
System.out.println(f.isFile());
System.out.println(f.isDirectory());
}
}
}

四、重命名

import java.io.File;
import java.io.IOException;

public class FileMethodDemo{
public static void main(String[] args) throws IOException {
renameToDemo();
}

public static void renameToDemo() throws IOException {

//f1已存在
File f1 = new File("d:\\code\\day21\\0.mp3" );

//f2不存在
File f2 = new File("d:\\code\\day21\\1.mp3" );

//把f1文件重命名成f2
boolean b = f1.renameTo(f2);

System.out.println( "b = " + b);
}
}

五、系统根目录和容量获取

import java.io.File;
import java.io.IOException;

public class FileMethodDemo{
public static void main(String[] args) throws IOException {
listRootsDemo();
}

public static void listRootsDemo() throws IOException {

//获取系统根目录下的file对象数组
File[] files = File.listRoots();

//打印file对象数组
for(File file : files){
System.out.println(file);
}

//把D盘封装成File对象
File file = new File("d:\\" );

//获取D盘的剩余容量字节数大小
System.out.println( "getFreeSpace:" + file.getFreeSpace());

//获取D盘的全部容量字节数大小
System.out.println( "getTotalSpace:" + file.getTotalSpace());

//获取D盘已使用容量字节数大小
System.out.println( "getUsableSpace:" + file.getUsableSpace());
}
}

六、获取目录下的文件及文件夹

import java.io.File;
import java.io.FilenameFilter;

public class FileListDemo{
public static void main(String[] args){
listDemo();
}

public static void listDemo(){

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

//获取目录下所有对象名称,返回字符串数组(包含隐藏文件,目录下没有内容时返回的数组长度为0)
//File对象中封装的必须是目录,否则会发生异常(访问系统级目录也会发生异常)
String[] names = file.list();

//打印名称
for(String name : names){
System.out.println(name);

//使用自定义的规则获取需要的列表
String[] names = dir.list(new FilterByJava());
}
}

//自定义获取目录列表的规则,并实现FilenameFilter接口
class FilterByJava implements FilenameFilter{

//实现接口中的accept方法,定义返回规则
public boolean accept(File dir,String name){

//符合规则返回真,否则返回假
//这里文件名是".java"结尾的返回真
return name.endsWith(".java" );
}
}

七、其他方法

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

//判断该文件或文件夹是否隐藏(文件必须存在)
file.isHidden();

Properties对象:

hashtable的子类,即具有Map的特性,可以存储键值对,还可以从流中加载跟输出键值对信息

集合中的键和值都是字符串类型

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

import java.util.*;

public class PropertiesDemo{
public static void main(String[] args){
propertiesDemo();
}

public static void propertiesDemo(){
//创建一个Properties集合
Properties prop = new Properties();

//存储元素
prop.setProperty( "zhangsan","10" );
prop.setProperty( "lisi","20" );
prop.setProperty( "wangwu","30" );
prop.setProperty( "zhaoliu","40" );

//修改元素
prop.setProperty( "wangwu","26" );

//取出所有元素
//获取Properties的键集合
Set<String> names = prop.stringPropertyNames();

for(String name : names){

//通过Properties的键获得值
String value = prop.getProperty(name);

System.out.println(name + ":" + value);
}
}
}

/*
记录应用程序运行次数。
如果使用次数已到,那么给出注册提示,并不要再运行程序
即应该有计数器,并有文件保存计数器信息
*/

import java.io.*;
import java.util.*;

public class PropertiesTest{
public static void main(String[] args) throws IOException{
getAppCount();
}

public static void getAppCount() throws IOException {

//创建Properties对象
Properties prop = new Properties();

//创建记录次数的文件count.ini并与File对象关联起来
File file = new File("count.ini");

//文件不存在则创建
if(!file.exists())
file.createNewFile();

//把文件输入字节流与File对象关联起来
FileInputStream fis = new FileInputStream(file);

//从输入流中读取键值对到Properties对象中
prop.load(fis);

//创建记录次数的变量count
int count = 0;

//从Properties对象中读取记录的次数(此时为字符串)
String value = prop.getProperty("time");

//如果文件中存在该键值
if(value!=null)
{
//把该值转换成Int类型并赋给count
count = Integer.parseInt(value);

//如果超过指定次数则打印提示信息,
if(count>=5)
{
System.out.println("您好,使用次数已到,拿钱!");
return ;
}
}
else{
//让运行次数加1
count++;

//运行该程序
appRun();
}

//在Proterties对象中存储新的次数键值对
prop.setProperty("time",count+"");

//把文件输出字节流与File对象关联起来
FileOutputStream fos = new FileOutputStream(file);

//把Properties对象的键值对写入到输出流中
prop.store(fos,"");

//输出流关闭时会先把数据刷新到文件中
fos.close();

fis.close();
}

public static void appRun(){
System.out.println("软件来了!");
}
}

打印流:

可以直接操作输入流和文件。

打印流为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。
与其他输出流不同,打印流永远不会抛出IOException,它可以捕获自己的异常。
打印流打印的所有字符都使用平台的默认字符编码转换为字节。提供了打印方法可以对多种数据类型值进行打印,并保持数据的表示形式

PrintStrean是写入一串8bit的数据的。
PrintWriter是写入一串16bit的数据的。
PrintStream主要操作byte流,而PrintWriter用来操作字符流。读取文本文件时一般用后者。

字节打印流:

PrintStream
构造函数可以接收的参数类型:

  1. file对象。File
  2. 字符串路径。String
  3. 字节输出流。OutputStream
    字符打印流:

PrintWriter
构造函数可以接收的参数类型:

  1. file对象。File
  2. 字符串路径。String
  3. 字节输出流。OutputStream
  4. 字符输出流,Writer。

/*
键盘写入数据到out.txt文件中
*/

import java.io.*;

class PrintWriterDemo
{
public static void main(String[] args) throws IOException
{
//标准键盘输入
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));

//把打印字符输出流与文件关联起来,构造函数的第二个参数设置true表示自动刷新
PrintWriter out = new PrintWriter(new FileWriter("a.txt"),true);

String line = null;
//循环读取键盘输入的一行数据
while((line=bufr.readLine())!=null)
{
//为over就结束循环
if("over".equals(line))
break;

//否则把输入的数据转成大写
out.println(line.toUpperCase());

//不用刷新因为上面的true设置了自动刷新
//out.flush();
}

out.close();
bufr.close();
}
}

合并流

SequenceInputStream:对多个流进行合并。

/*
需求:将1.txt、2.txt、3、txt文件中的数据合并到一个文件中。
*/

import java.io.*;
import java.util.*;

public class SequenceInputStreamDemo{
public static void main(String[] args) throws Exception {

//创建ArrayList集合用来存储要合并的输入流
ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();

//把要合并的输入流添加到ArrayList集合中
for(int x = 1; x <= 3; x++){
al.add( new FileInputStream(x + ".txt" ));
}

//获取ArrayList集合的迭代器
final Iterator<FileInputStream> it = al.iterator();

//获取ArrayList集合上的枚举
Enumeration<FileInputStream> en = Collections.enumeration(al);

/*
//Collections工具类的enumeration方法核心代码:
Enumeration<FileInputStream> en = new Enumeration<FileInputStream>(){
public boolean hasMoreElements(){
return it.hasMoreElements();
}

public FileInputStream nextElement(){
return it.next();
}
};*/


//创建合并流并通过集合上的枚举初始化该流
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();
}
}

文件的分割

import java.io.*;
import java.util.*;

public class SplitFileDemo{

private static final int SIZE = 1024*1024;

public static void main(String[] args) throws IOException{
File file = new File("0.mp3" );
splitFile(file);
}

public static void splitFile(File file) throws IOException {

//用输入流关联源文件
FileInputStream fis = new FileInputStream(file);

//定义一个1M的缓冲区
byte[] buf = new byte[SIZE];

//创建输出流
FileOutputStream fos = null;

int len = 0;
int count = 1;

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

Properties prop = new Properties();

//创建保存键值对的文件
File dir = new File("c:\\partFiles" );
if(!dir.exists())
dir.mkdirs();

//将输入流循环写入到缓冲区中,直到数据为空
while((len = fis.read(buf)) != -1){

//每读一次就把缓冲区中的数据写入到一个part文件中
fos = new FileOutputStream(new File(dir,(count++) + ".part"));
fos.write(buf,0,len);
fos.close();
}

//将被切割文件的信息保存到prop集合中
prop.setProperty( "partcount",count + "" );
prop.setProperty( "filename",file.getName());

fos = new FileOutputStream(new File(dir,count + ".properties" ));

//将prop集合中的数据存储到文件中
prop.store(fos, "save file info");

fis.close();
fos.close();
}
}

对象的序列化:

序列化堆内存中的对象
Serializable接口:要序列化对象必须要实现Serializable接口,接口中没有方法,会赋予对象一个UID号(long型的值),只是一个标记接口。

import java.io.*;

class ObjectStreamDemo
{
public static void main(String[] args) throws Exception
{
//writeObj();
readObj();
}

public static void readObj()throws Exception
{

//创建对象输入流对象并关联obj.txt文件
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));

//从该流中读取对象并转换成Person对象
Person p = (Person)ois.readObject();

System.out.println(p);
ois.close();
}
public static void writeObj()throws IOException
{
//创建对象输出流对象并关联obj.txt文件
ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("obj.txt"));

//把Person对象写出到流中
oos.writeObject(new Person("lisi0",399,"kr"));
oos.close();
}
}

静态不能被序列化,因为静态成员在方法区中
加上修饰符transient的成员也无法被序列化
自定义UID:任意修饰符+static final long serialVersionUID = 42L;

管道流:

PipedInputStream、PipedOutputStream
直接连接输入输出流,一般使用多线程,单线程容易造成死锁

import java.io.*;
class Read implements Runnable
{
private PipedInputStream in;
Read(PipedInputStream in)
{
this.in = in;
}
public void run()
{
try
{
byte[] buf = new byte[1024];
System.out.println("读取前。。没有数据阻塞");

//等待输出流的数据输入,这是阻塞式方法,获取数据会存储到buf中
int len = in.read(buf);

System.out.println("读到数据。。阻塞结束");
String s= new String(buf,0,len);
System.out.println(s);
in.close();
}
catch (IOException e)
{
throw new RuntimeException("管道读取流失败");
}
}
}
class Write implements Runnable
{
private PipedOutputStream out;
Write(PipedOutputStream out)
{
this.out = out;
}
public void run()
{
try
{
System.out.println("开始写入数据,等待6秒后。");
Thread.sleep(6000);

//往输出流写入的数据会被输入流读取
out.write("piped lai la".getBytes());
out.close();
}
catch (Exception e)
{
throw new RuntimeException("管道输出流失败");
}
}
}
class PipedStreamDemo
{
public static void main(String[] args) throws IOException
{

//创建输入流跟输出流对象
PipedInputStream in = new PipedInputStream();
PipedOutputStream out = new PipedOutputStream();

//把输入流跟输出流连接起来
in.connect(out);

Read r = new Read(in);
Write w = new Write(out);
new Thread(r).start();
new Thread(w).start();
}
}

RandomAccessFile:

具备读和写功能,内部封装了数组,通过指针操作
内部封装了输入及输出流,只能操作文件
如果模式为只读r。不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常。
如果模式rw。操作的文件不存在,会自动创建。如果存则不会覆盖。

class RandomAccessFileDemo 
{
public static void main(String[] args) throws IOException
{
//writeFile_2();
//readFile();
//System.out.println(Integer.toBinaryString(258));
}
public static void readFile()throws IOException
{
//创建对象关联文件,模式为只读
RandomAccessFile raf = new RandomAccessFile("ran.txt","r");

//调整对象中指针到第8个二进制位(从0开始),即从第二个字节开始读起。
//raf.seek(8*1);

//跳过指定的字节数
raf.skipBytes(8);
byte[] buf = new byte[4];

//读取数据到buf中,一次读buf.length个字节
raf.read(buf);
String name = new String(buf);

//直接读32位整数,即读4个字节
int age = raf.readInt();
System.out.println("name="+name);
System.out.println("age="+age);
raf.close();
}
public static void writeFile_2()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
raf.seek(8*0);

//写入byte数据
raf.write("周期".getBytes());

//写入4个字节的int数据
raf.writeInt(103);
raf.close();
}
public static void writeFile()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");

raf.write("李四".getBytes());
raf.writeInt(97);
raf.write("王五".getBytes());
raf.writeInt(99);
raf.close();
}
}

DataStream基本数据类型流:

用于操作基本数据类型

import java.io.*;
class DataStreamDemo
{
public static void main(String[] args) throws IOException
{
//writeData();
//readData();
//writeUTFDemo();
//readUTFDemo();
}
public static void readUTFDemo()throws IOException
{
DataInputStream dis = new DataInputStream(new FileInputStream("utfdate.txt"));

//使用UTF的编码方式读取一个字符。
String s = dis.readUTF();
System.out.println(s);
dis.close();
}

public static void writeUTFDemo()throws IOException
{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdate.txt"));

//写入一个UTF-8编码的字符串
dos.writeUTF("你好");
dos.close();
}
public static void readData()throws IOException
{
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));

//可以直接读取基本数据类型
int num = dis.readInt();
boolean b = dis.readBoolean();
double d = dis.readDouble();
System.out.println("num="+num);
System.out.println("b="+b);
System.out.println("d="+d);
dis.close();
}
public static void writeData()throws IOException
{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));

//可以直接写入基本数据类型
dos.writeInt(234);
dos.writeBoolean(true);
dos.writeDouble(9887.543);
dos.close();
}

ByteArrayInputStream:

用于操作字节数组的流对象
ByteArrayInputStream :在构造的时候,需要接收数据源,而且数据源是一个字节数组。
ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。
这就是数据目的地。
因为这两个流对象都操作的数组,并没有使用系统资源。
所以,不用进行close关闭。
用流的读写思想来操作数据。

import java.io.*;
class ByteArrayStream
{

public static void main(String[] args)
{
//数据源。直接连接字节数组
ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFD".getBytes());

//数据目的,内部封装了可变长度的字节数组
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int by = 0;
while((by=bis.read())!=-1)
{
bos.write(by);
}

//输出流中封装了数组
System.out.println(bos.size());
System.out.println(bos.toString());
// bos.writeTo(new FileOutputStream("a.txt"));