黑马程序员-javaIO总结 续——其他常用与IO流有关的对象

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------


本文整理自JAVA IO详解,包括讲解IO中各种流及其用法



名称对应的对象
文件类File
属性类Properties
打印流PrintStream、PrintWriter
管道流PipedInputStream
序列流SequenceInputStream
对象序列化流ObjectInputStream、ObjectOutputStream
随机存取文件类RandomAccessFile
基本数据流DataInputStream、DataOutputStream
字节数组流ByteArrayInputStream、ByteArrayOutputStream
字符数组流CharArrayReader、CharArrayWriter
字符串流StringReader、StringWriter

一、File

该类的出现是对文件系统中的文件及文件夹进行对象的封装。可以通过对象的思想来操作文件以及文件夹,方便对文件及文件夹的属性信息进行操作,File对象可以作为参数传递给流的构造函数。

1、构造函数:

File(String fileName):将一个字符串路径(相对或者绝对)封装成File对象,该路径是可存在的,也可以是不存在的。

File(String parent,String child);

File(File parent,String child);

File f1=new File("a.txt");
		
File f2=new File("c:\\abc","b.txt");
		
File d=new File("c:\\abc");
File f3=new File(d,"c.txt");

2、特别的字段:

separator:跨平台的目录分隔符。例如,

File file=new File("c:"+File.separator+"a.txt");

3、常见方法:

    1)创建:

    boolean createNewFile()throws IOException:创建文件,如果被创建的文件已经存在,则不创建。注意,与输出流创建文件时的情况不同。

    boolean mkdir():创建文件夹。

    boolean mkdirs():创建多级文件夹。

File f=new File("a.txt");
f.createNewFile();
		
File d=new File("c:\\abc");
d.mkdir();

    2)删除:

    boolean delete():可用于删除文件或者文件夹。注意,对于文件夹只能删除不带内容的空文件夹。对于带有内容的文件夹不可以直接删除,必须要从里往外删除。

    void deleteOnExit():删除动作交给系统完成。无论是否发生异常,系统在退出时执行删除动作。

File f1=new File("a.txt");
f1.deleteOnExit();
		
File f2=new File("b.txt");
System.out.println("create:"+f2.createNewFile());//true
System.out.println("delete:"+f2.delete());       //true

    3)判断:

    boolean canExecute();

    boolean canWrite();

    boolean canRead();


    boolean exists():判断文件或文件夹是否存在。

    boolean isFile():判断File对象中封装的是否是文件。

    boolean isDirectory():判断File对象中封装的是否是文件夹。

    boolean isHidden():判断文件或文件夹是否隐藏。在获取硬盘文件或文件夹时,对于系统目录中的文件,java是无法访问的,所以在遍历时,可以避免遍历隐藏文件。

File f=new File("a.txt");
f.createNewFile();

System.out.println("exists:"+f.exists());//true
System.out.println("isFile:"+f.isFile());//true
System.out.println("isDirectory:"+f.isDirectory());//false

   4)获取:

    getName():获取文件或文件夹的名称。

    getPath():File对象中封装的路径是什么,获取的就是什么。

    getAbsolutePath():无论File对象中封装的路径是什么,获取的都是绝对路径。

    getParent():获取File对象封装的文件或文件夹的父目录。注意,如果封装的是相对路径,那么返回null。

    long length():获取文件大小。

File f=new File("a.txt");
f.createNewFile();

System.out.println("getName:"+f.getName());//a.txt
System.out.println("getPath:"+f.getPath());//a.txt
System.out.println("getAbsolutePath:"+f.getAbsolutePath());//D:\JAVA\a.txt
System.out.println("getParent:"+f.getParent());//null

    static File[] listRoots():获取该系统中有效的盘符。

    String[] list():获取指定目录下当前所有的文件及文件夹的名称。

    String[] list(FilenameFilter filter):获取被指定过滤器过滤后的文件及文件夹的名称。

    File[] listFiles():获取指定目录下的文件及文件夹的对象。

File f=new File("a.txt");
		f.createNewFile();

		//文件列表1
		File dir1=new File("c:\\");
		String[] names=dir1.list();
		//有效盘符列表
		File[] files=File.listRoots();
		//文件列表2
		File dir2=new File("c:\\");
		File[] files=dir2.listFiles();
		//有过滤规则的文件列表
		File dir3=new File("c:\\");
		String[] names=dir3.list(new FilenameFilter()
		{
			public boolean accept(File dir,String name)
			{
				return name.endsWith("*.bmp");
			}
		});

    5)重命名

    renameTo(File f),例如,

File f1=new File("c:\\a.txt");
File f2=new File("c:\\b.txt");
f1.renameTo(f2);  //将c盘下的a.txt文件改名为b.txt文件

4、递归

其实就是在使用一个功能过程中,又对该功能有需求,就出现了函数调用自身的情况,而这就是递归。

注意:

1)一定要限定条件,否则内存溢出。

2)使用递归时,调用次数不要过多,否则也会出现内存溢出。


例1:利用递归列出指定目录下的所有文件及文件夹中的文件(子文件)

import java.io.*;
class Test 
{
	public static void main(String[] args) throws IOException
	{
		File dir=new File("e:\\Youku Files");
		listDir(dir,0);
	}
	public static void listDir(File dir,int level)
	{
		System.out.println(getLevel(level)+dir.getName());
		level++;
		File[] files=dir.listFiles();
		for (File file:files)
		{
			if(file.isFile())//如果是文件,打印出来
				System.out.println(getLevel(level)+file.getName());
			else             //如果是文件夹,递归调用listDir()
				listDir(file,level);
		}
	}
	//使列出的目录具有层次
	public static String getLevel(int level)
	{
		StringBuilder sb=new StringBuilder();
		sb.append("|--");
		for (int x=0;x<level ;x++ )
		{
			sb.insert(0," ");
		}
		return sb.toString();

	}
}

输出结果:

|--Youku Files
 |--download
  |--temp
 |--transcode
 |--youkudisk
  |--cache_0.dat
  |--cache_1.dat
  |--cache_2.dat
  |--cache_3.dat

例2:利用递归删除一个带内容的目录

import java.io.*;
class Test 
{
	public static void main(String[] args) throws IOException
	{
		File dir=new File("e:\\Youku Files");
		removeDir(dir);
	}
	public static void removeDir(File dir)
	{
		File[] files=dir.listFiles();
		for (int x=0;x<files.length ;x++ )
		{
			if(files[x].isFile())
				files[x].delete();
			else
				removeDir(files[x]);
		}
		dir.delete();
	}
}

二、Properties

Map

    |--Hashtable

        |--Properties

Properties是Hashtable的子类,也就是说它具备Map集合的特点,而且它里面存储的键值对都是字符串(因此,Properties不需要泛型)。Properties是与IO技术相结合的集合容器,该对象的特点是可以用于键值对形式的配置文件

常见方法:

1、存入键值对:setProperty(String key,String value);

2、获取指定键对应的值:String getProperty(String key);

3、获取集合中所有键元素:

Enumeration propertyNames();

JDK1.6提供新方法:Set<String> stringPropertyNames();

4、列出该集合中的所有键值对,可以通过参数打印流指定列出的目的地:

list(PrintStream); list(PrintWriter),例如,

list(System.out):将集合中的键值对打印到控制台;list(new PrintStream("prop.txt")):将集合中的键值对存储到prop.txt文件中。

5、将流中的规则数据加载进集合,并称为键值对:

load(InputStream in);JDK1.6提供新方法:load(Reader r);

注意:流中的数据要求是“键=值”的规则数据。

6、将集合中的数据存储在指定的目的地:

store(OutputStream os,String comment);JDK1.6提供新方法:store(Writer w,String comment)。使用该方法存储时,会带着当时存储的时间。

import java.util.*;
class TestDemo
{
	public static void main(String[] args)
	{
		Properties p=new Properties();
		p.setProperty("zhangsan","30");
		p.setProperty("lisi","22");
		System.out.println("lisi="+p.getProperty("lisi"));//lisi=22
		Set<String> names=p.stringPropertyNames();
		for(String str:names)
		{
			System.out.println(str+"="+p.getProperty(str));//zhangsan=30
			                                               //lisi=22
		}
	}
}


例3:记录一个程序运行的次数,当超过指定次数时,该程序不能继续运行

import java.util.*;
import java.io.*;
class TestDemo
{
	public static void main(String[] args)throws IOException
	{
		Properties prop=new Properties();
		File file=new File("count.ini");
		if(!file.exists())
			file.createNewFile();
		FileInputStream fis=new FileInputStream(file);
		prop.load(fis);
		int count=0;
		String value=prop.getProperty("Times");
		if(value!=null)
		{
			count=Integer.parseInt(value);
			if(count>=5)
			{
				System.out.println("抱歉,使用次数已到,您不能继续使用!");
				return;
			}
		}
		count++;
		prop.setProperty("Times",count+"");
		FileOutputStream fos=new FileOutputStream(file);
		prop.store(fos,"");
		fis.close();
		fos.close();
		
	}
}


三、打印流

该流提供了打印方法,可以将各种数据类型的数据都原样打印

1、PrintStream:是一个字节打印流,System.out对应的类型就是printStream。它的构造函数可以接收三种数据类型的值。

1)字符串路径;2)File对象;3)OutputStream。


2、PrintWriter:是一个字符打印流。它的构造函数可以接收四种类型的值。

1)字符串路径;2)File对象;3)OutputStream;4)Writer。

其中,对于1)和2)类型的数据,还可以指定编码表;对于3)和4)类型的数据,可以指定自动刷新。注意:该自动刷新值为true时,只有三个方法可用:println、print、format。

import java.util.*;
import java.io.*;
class TestDemo
{
	public static void main(String[] args)throws IOException
	{
		BufferedReader bufr=
			new BufferedReader(new InputStreamReader(System.in));
		PrintWriter pw=new PrintWriter(new FileWriter("c:\\1.txt"),true);
		
		String line=null;
		while ((line=bufr.readLine())!=null)
		{
			if("over".equals(line))
				break;
			pw.println(line.toUpperCase());
			//pw.flush();
		}
		pw.close();
		bufr.close();
	}
}


如果PrintWriter既想要有自动刷新,又可指定编码表,还要提高效率:
PrintWriter pw=
	new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(1.txt),"utf-8")),true);

四、序列流

SequenceInputStream,也称为合并流。

特点:可以将多个读取流合并成一个流,这样操作起来很方便。

原理:将每个读取流对象存储到一个集合中,最后一个流对象结尾作为整个流的结尾。

构造函数:

1)SequenceInputStream(InputStream in1,InputStream in2):将两个读取流合并成一个流。

2)SequenceInputStream(Enumeration<? extends InputStream> en):将枚举中的多个流合并成一个流。

作用:用于多个数据的合并。

注意:因为Enumeration是Vector中特有的取出方式。而Vector被ArrayList取代,所以使用ArrayList集合效率更高些。那么,如何获取Enumeration呢?请见下例,

例4:文件的分割与合并

import java.util.*;
import java.io.*;
class TestDemo
{
	public static void main(String[] args)throws IOException
	{
		split();
		merge();
		
	}
	//合并文件
	public static void merge()throws IOException
	{
		ArrayList<FileInputStream> al=new ArrayList<FileInputStream>();
		for (int x=1;x<=51 ;x++ )
		{
			al.add(new FileInputStream("c:\\splitfiles\\"+x+".part"));
		}
		final Iterator<FileInputStream> it=al.iterator();
		//以匿名内部类的形式,用Iterator的方法取得Enumeration
		Enumeration<FileInputStream> en=new Enumeration<FileInputStream>()
		{
			public boolean hasMoreElements()
			{
				return it.hasNext();
			}
			public FileInputStream nextElement()
			{
				return it.next();
			}
		};
		//多个流变成了一个流
		SequenceInputStream sis=new SequenceInputStream(en);
		FileOutputStream fos=new FileOutputStream("c:\\splitfiles\\0.JPG");
		byte[] buf=new byte[1024];
		int len=0;
		while ((len=sis.read(buf))!=-1)
		{
			fos.write(buf,0,len);
		}
		fos.close();
		sis.close();		
	}
	//分割文件
	public static void split()throws IOException
	{
		FileInputStream fis=new FileInputStream("c:\\1.JPG");
		FileOutputStream fos=null;
		byte[] buf=new byte[1024];
		int len=0,count=1;
		while ((len=fis.read(buf))!=-1)
		{
			//每次形成一个1Kb的part文件
			fos=new FileOutputStream("c:\\splitfiles\\"+(count++)+".part");
			fos.write(buf,0,len);
			fos.close();
		}
		fis.close();
	}
}


五、对象的序列化

ObjectInputStream\ObjcetOutputStream

可以通过这两个流对象直接操作已有对象,并将对象进行本地持久化存储。存储后的对象可以进行网络传输。

特有方法:

ObjectInputStream:

    Object readObject():该方法抛出ClassNotFoundException。

ObjectOutputStream:

    void writeObject(Object):被写入的对象必须实现Serializable接口,否则会抛出NotSerializableException。

public static void writeObj()throws IOException
	{
		ObjectOutputStream oos=
			new ObjectOutputStream(new FileOutputStream("obj.txt"));
		oos.writeObject(new Person("lisi",39));
		oos.close();
	}
	public static void readObj()throws IOException
	{
		ObjectInputStream ois=
			new ObjectInputStream(new FileInputStream("obj.txt"));
		Person p=(Person)ois.readObject();
		System.out.println(p);
		ois.close();
	}

Serializable:该接口其实就是一个没有方法的标记接口。

用于给类指定一个UID。该UID是通过类中的可序列化成员的数字签名运算出来的一个long型值。只要这些成员没有变化,那么该值每次运算都一样。

该值用于判断被序列化的对象和类文件是否兼容。如果被序列化的对象需要被不同的类版本所兼容,可以在类中自定义UID。

定义方式:static final long serialVersionUID=42L;

注意:静态成员变量,不会被序列化;非静态成员若不想被序列化,可用transient关键字修饰。


六、管道流

PipedInputStream\PipedOutputStream

特点:读取管道流和写入管道流可以进行连接。

通常这两个流在使用时,需要加入多线程技术,也就是让读写同时运行。注意:对于read方法,该方法是阻塞式的,也就是没有数据的情况下该方法会一直等待。

例5:多线程下的管道流实例

import java.io.*;
class Test 
{
	public static void main(String[] args) throws IOException
	{
		PipedInputStream pis=new PipedInputStream();
		PipedOutputStream pos=new PipedOutputStream();
		pis.connect(pos);//读取管道流与写入管道流连接

		Read r=new Read(pis);
		Write w=new Write(pos);
		new Thread(r).start();
		new Thread(w).start();
	}
	
}
class Read implements Runnable
{
	private PipedInputStream pis;
	Read(PipedInputStream pis)
	{
		this.pis=pis;
	}
	public void run()
	{
		try
		{
			byte[] buf=new byte[1024];
			int len=pis.read(buf);
			String str=new String(buf,0,len);
			System.out.println("pis:"+str);
			pis.close();

		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
	}
}
class Write implements Runnable
{
	private PipedOutputStream pos;
	Write(PipedOutputStream pos)
	{
		this.pos=pos;
	}
	public void run()
	{
		try
		{
			pos.write("pos is coming!".getBytes());
			pos.close();
		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
		
	}
}

输出结果:

pis:pos is coming!

七、RandomAccessFile

该对象不是流体系中的一员。该对象中封装了字节流,同时还封装了一个缓冲区(字节数组),通过内部的指针来操作数组中的数据。

特点:

1)该对象只能操作文件,所以构造函数接收两种类型的参数:a.字符串路径;b.File对象。

2)该对象既可以对文件进行读取,也可以写入。在进行对象实例化时,必须要指定该对象的操作模式,r\rw等。

r:只读模式,不会创建文件,会去读取一个已存在的文件。如果该文件不存在,则发生异常。

rw:若操作的文件不存在,会自动创建;若存在则不会覆盖。


常用方法:

skipBytes(int n):跳过指定的字节数。

seek(long pos):指定指针的位置。

getFilePointer():获取指针位置。


该对象中有可以直接操作基本数据类型的方法,如,

readInt() \ readByte() \ writeInt() \ writeByte()等。


该对象既可以读数据,也可以写数据,而且想读哪里就读哪里,想往哪里写就往哪里写。如果写入位置已有数据,会发生数据覆盖,即可以对数据进行修改。

在使用该对象时,建议数据都是规则的,或者是分段的。

可以用于多线程的下载,也就是通过多线程往一个文件中同时存储。

例6:RandomAccessFile读写实例

import java.io.*;
class Test 
{
	public static void main(String[] args) throws IOException
	{
		writeFile();
		readFile();
	}
	public static void readFile()throws IOException
	{
		RandomAccessFile raf=new RandomAccessFile("c:\\1.txt","r");//只读模式
		//调整指针位置
		//raf.seek(8*1);
		//跳过指定的字节数
		raf.skipBytes(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);
		raf.close();		
	}
	public static void writeFile()throws IOException
	{
		RandomAccessFile raf=new RandomAccessFile("c:\\1.txt","rw");
		raf.write("李四".getBytes());
		raf.writeInt(33);
		raf.write("王五".getBytes());
		raf.writeInt(22);
		raf.close();
	}
}

输出结果:

name=王五
age=22

八、操作基本数据类型的流对象

DataInputStream: DataInputStream(InputStream is);

操作基本数据类型的方法:

int readInt():一次读取四个字节,并将其转成int值。

boolean readBoolean():一次读取一个字节。

short readShort():一次读取两个字节。

long readLong():一次读取八个字节。

String readUTF():按照utf-8修改版读取字符。注意:它只能读writeUTF()写入的字符数据。


DataOutputStream:DataOutputStream(OutputStream os);

操作基本数据类型的方法:

writeInt(int i):一次写入四个字节。注意,与write(int i)不同,write(int i)只将该整数的最低一个8位写入,剩余三个8位丢弃。

writeBoolean(boolean b );

writeShort(short s);

writeLong(long l);

writeUTF(String str):按照utf-8修改版将字符数据进行存储,只能通过readUTF()读取。


通常只要操作基本数据类型的数据,就需要这两个流。通常成对使用。

例7:操作基本数据类型流的读写实例

import java.io.*;
class Test 
{
	public static void main(String[] args) throws IOException
	{
		writeData();
		readData();
	}
	public static void writeData()throws IOException
	{
		DataOutputStream dos=new DataOutputStream(new FileOutputStream("c:\\1.txt"));
		dos.writeInt(123);
		dos.writeBoolean(true);
		dos.writeDouble(11.22);
		dos.close();
	}
	public static void readData()throws IOException
	{
		DataInputStream dis=new DataInputStream(new FileInputStream("c:\\1.txt"));
		int i=dis.readInt();
		boolean b=dis.readBoolean();
		double d=dis.readDouble();
		System.out.println("int num="+i);
		System.out.println("boolean b="+b);
		System.out.println("double d="+d);
	}
}

输出结果:

int num=123
boolean b=true
double d=11.22

九、操作数组的流对象

1、操作字节数组

ByteArrayInputStream

ByteArrayOutputStream:

toByteArray();

toString();

writeTo(OutputStream os);


2、操作字符数组

CharArrayReader\CharArrayWriter


对于这些流,源是内存,目的也是内存。

这些流并未调用系统资源,使用的就是内存中的数组,所以这些流在使用的时候不需要close()。

操作数组的读取流在构造的时候,必须要明确一个数据源,所以要传入相对应的数组;操作数组的写入流在构造时可以使用空参数,因为它内置了一个可变长度数组作为缓冲区。

这些流的出现其实就是通过流的读写思想来操作数组。

import java.io.*;
class Test 
{
	public static void main(String[] args) throws IOException
	{
		//数据源,必须传入一个字节数组
		ByteArrayInputStream bis=new ByteArrayInputStream("ABC".getBytes());
		//数据目的,不必定义数据目的,因为已经内置了可变长度的字节数组
		ByteArrayOutputStream bos=new ByteArrayOutputStream();
		int by=0;
		while ((by=bis.read())!=-1)
		{
			bos.write(by);
		}
		System.out.println(bos.size());    //3
		System.out.println(bos.toString());//ABC
		bos.writeTo(new FileOutputStream("c:\\1.txt"));//1.txt文件中写入了ABC
	}
}


---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------详细请查看:www.itheima.com

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值