黑马程序员——IO

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

IO(Input Output)流:

· IO流用来处理设备之间的数据传输

· java对数据的操作是通过流的方式

· 流按操作数据分为2种:字节流和字符流

· 流按流向分为:输入流和输出流

IO流常用基类:

· 字节流的抽象基类:InputStream,OutputStream

· 字符流的抽象基类:Reader,Writer

· 注:由这4个类派生出来的子类名称都是以父类名作为子类后缀名。如InputStream的子类FileInputStream;Reader的子类FileReader

IO程序的书写:

· 导入IO包中的类

· 进行IO异常处理

· 在finally中对流进行关闭

字符流——创建文件:

· 创建一个FileWriter对象,该对象一被初始化就必须要明确被操作的文件。而且该文件会被创建到指定目录下:

FileWriter writer = new FileWriter("demo.txt");//如果该目录下已有同名文件,将被覆盖

FileWriter writer = new FileWriter("demo.txt",true);//这种方法不覆盖已经存在文件的内容,并且从文件的末尾处续写内容

· 调用流对象的写入方法,将数据写入流:writer.write("hello world");

· 刷新该流的缓冲:writer.flush();

· 关闭流资源,并将流中的缓冲数据刷新到目的地中:writer.close();

· 完整代码:

FileWriter writer = null;//建立引用,全局使用
try {
	writer = new FileWriter("demo.txt");
	writer.write("hello word");
} catch (IOException e) {
	System.out.println(e.toString());
} finally {
	try {
		if(writer!=null)
			writer.close();
	} catch (Exception e2) {
		System.out.println(e2.toString());
	}
}

字符流——读取文件:

· 建立一个流对象,将已存在的一个文件加载进流。并保证此文件是存在的:FileReader reader = new FileReader("demo.txt");

· 读取方式1:int read():read()方法会读一个字符并自动往下读,当读到结束标识时会返回-1

int ch = 0;
while((ch=reader.read())!=-1) {
	System.out.print((char)ch);
}
· 读取方式2:int read(char[] buf):将字符读进数组:
FileReader reader = new FileReader("demo.txt");
char[] chs = new char[1024];//一般定义为1024的整数倍
int num = 0;//每次reader.read(char[] chs)的返回值都是读到的字符数
while((num=reader.read(chs))!=-1) {
	System.out.print(new String(chs,0,num));//通过String的构造方法,创建这个字符串
}
· 完整代码:
Reader reader = null;
try {
	reader = new FileReader("demo.txt");
	char[] chs = new char[1024];
	int num = 0;
	while((num = reader.read(chs))!=-1) {
		System.out.print(new String(chs,0,num));
	}
} catch (IOException e) {
	System.out.println(e.toString());
} finally {
	try {
		if(reader!=null)
			reader.close();
	} catch (Exception e2) {
		System.out.println(e2.toString());
	}
}

 注意:

· 在定义文件路径时,可以用"\\"或者"/"

· 在创建一个文件时,如果目录下有同名文件将被覆盖

· 在读取一个文件时,必须保证该文件已经存在,否则会出异常

字符流缓冲区:

· 缓冲区的出现是为了提高流的操作效率而出现的,所以在创建缓冲区之前,必须要先有流对象

· 对应类:BufferedReader和BufferedReader。

· 缓冲区要结合流才可以使用

· 在流的基础上对流的功能进行了增强

· BufferedWriter提供了一个特有的方法,newLine(),里面提供了换行符

· BufferedReader提供了String readLine()的方法,当返回null时,代表读到行末,不包含换行符

装饰设计模式:

· 对原有的类进行了功能的改变和增强

· 装饰设计模式的基本格式:装饰类一般都是通过构造函数接收被装饰的类,再提供比被装饰类更强的功能

· 它与继承有什么不同?

答:装饰模式比继承要更加灵活,避免了继承体系的臃肿,而且降低了类与类之间的关系。装饰类因为增强已有对象,具备的功能和已有类是相同的,只不过提供了更强功能

。所以装饰类和被装饰类通常是属于一个体系的。

· 了解BufferedReader的原理:

答:BufferedReader的构造函数可以接收一个Reader的子类,内部定义了一个数组,调用Reader的read()方法,一个个的读取数据并存放到数组中。当读到行末时,把这个字符数组转换成字符串打印出来。

字节流:

· 基本操作与字符流相同 InputStream和OutputStream;因为是对字节最小单位进行操作,所以不可以使用flush()方法。

· 不仅可以操作字符,还可以操作其它媒体文件

· 例:copy一个jpg文件:用FileInputStream读取jpg文件,再通过FileOutputStream写出

import java.io.*;
class  CopyPic
{
	public static void main(String[] args) 
	{
		FileOutputStream fos = null;
		FileInputStream fis = null;
		try
		{
			fos = new FileOutputStream("c:\\2.bmp");
			fis = new FileInputStream("c:\\1.bmp");

			byte[] buf = new byte[1024];

			int len = 0;

			while((len=fis.read(buf))!=-1)
			{
				fos.write(buf,0,len);
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("复制文件失败");
		}
		finally
		{
			try
			{
				if(fis!=null)
					fis.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("读取关闭失败");
			}
			try
			{
				if(fos!=null)
					fos.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("写入关闭失败");
			}
		}
	}
}

字节流缓冲区:

· 同样是提高了字节流的读写效率:BufferedInputStream和BufferedOutputStream

· 模拟一个BufferedIInputStream,BufferedInputStream是把数据一个个的存到缓冲区里面,再从缓冲区里取的

import java.io.*;

class MyBufferedInputStream
{
	private InputStream in;

	private byte[] buf = new byte[1024*4];
		
	private int pos = 0,count = 0;
	
	MyBufferedInputStream(InputStream in)
	{
		this.in = in;
	}

	//一次读一个字节,从缓冲区(字节数组)获取。
	public int myRead()throws IOException
	{
		//通过in对象读取硬盘上数据,并存储buf中。
		if(count==0)
		{
			count = in.read(buf);
			if(count<0)
				return -1;
			pos = 0;
			byte b = buf[pos];

			count--;
			pos++;
			return b&255;
		}
		else if(count>0)
		{
			byte b = buf[pos];

			count--;
			pos++;
			return b&0xff;
		}
		return -1;

	}
	public void myClose()throws IOException
	{
		in.close();
	}
}

转换流:

· InputStreamReader和OutputStreamWriter

· 转换流的由来

1. 字符流与字节流之间的桥梁

2. 方便了字符流与字节流之间的操作

· 转换流的应用:字节流中的数据都是字符时,转换成字符流操作更高效,而且构造方法中可以指定编码表

代码示例:标准输入输出

//键盘录入
BufferedReader bufr = 
			new BufferedReader(new InputStreamReader(System.in));
//控制台输出
BufferedWriter bufw = 
			new BufferedWriter(new OutputStreamWriter(System.out));
		
String line = null;
while((line=bufr.readLine())!=null) {
	if("over".equals(line))
		break;
	bufw.write(line.toUpperCase());
	bufw.flush();
	bufw.newLine();
}
		
bufr.close();
bufw.close();

标准输入输出流:

· System类中的字段:in;out

· 它们各代表了系统标准的输入和输出设备

· 默认输出设备是键盘,输出设备是控制台

· System.in的类型是InputStream

· System.out的类型是PrintStream。是OutputStream的子类,FilterOutputStream的子类

· System中提供了setIn和setOut的方法,改变标准的输入输出流

流的基本应用小节:

· 流是用来处理数据的

· 处理数据时,一定要先明确数据来源,与数据目的地(数据汇)

· 数据源可以是文件,也可以是键盘

· 数据目的地可以是文件、显示器或者其他设备

· 而流只是在帮助数据进行传输,并对传输的数据进行处理,比如过滤处理、转换处理等

File类:

· 用来对文件或者文件夹封装成对象

· 方便对文件与文件夹的属性信息进行操作

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

· File类中的常用方法

//1.创建:
boolean createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。和输出流不一样,输出流对象一初始化就会创建文件,如果文件存在则会覆盖
boolean mkdir():创建目录
boolean mkdirs():可以创建多级目录
//2.删除:
boolean delete():删除,删除失败返回false
void deleteOnExit():退出时删除
//3.判断:
boolean exists():文件是否存在
boolean isFile():是否是文件
boolean isDirectory():是否是目录
boolean isHidden():是否隐藏
boolean isAbsolutely():是否是绝对路径
//4.获取信息
String getName():返回文件名字
String getPath():
String getAbsolutePath():返回绝对路径
File getAbsoluteFile():返回绝对路径并封装成对象
String getParent():如果获取是相对路径,则会返回null。如果相对路径中有上一层目录,那么该目录就是返回结果
long lastModified():返回最后一次修改时间
boolean renameTo():相当于剪切并可以重命名
String[] list():返回该目录下所有的文件和文件夹的名称
File[] listFiles():list里面还可以传递一个文件或者文件名过滤器

递归:

· 函数自己调用自己

· 注意:

1. 递归时一定要明确结束条件

2. 要注意递归次数,尽量避免内存溢出

· 应用场景:当某一个功能要重复使用时

· 例:列出一个文件夹下所有的子文件夹以及文件

public static void showDir(File dir) {
	if(!dir.exists() || !dir.isDirectory()) 
		throw new RuntimeException("目录不存在或者传入路径不是目录");
	System.out.println(dir);
	File[] dirs = dir.listFiles();
	for(File file : dirs) {
		if(file.isDirectory())
			showDir(file);//当file还是一个文件夹时,调用自己的方法继续递归
		System.out.println(file);
	}
}
Properties:

· Properties是hashtable的子类。也就是说它具备集合的特点,而且它里面存储的键值对都是字符串

· 是集合和IO技术相结合的集合容器

· 该对象的特点,可以用于创建键值对形式的配置文件

//Properties中常见方法:
String getProperty(String key):获取属性信息
void list(PrintStream out):将属性列表输出到指定的输出流
void list(PrintWriter out)
void load(Reader reader):从输入流中读取属性列表(键值对)
void load(InputStream inStream)
setProperty(String key, String value):设置属性
store(OutputStream out, String comments):设置属性,并加载到输出流中

IO包中的其它类:

· RandomAccessFile

该类不算是IO体系中子类,而是直接继承自Object

但是它是IO包中成员,因为它具备读和写的功能。内部封装了一个数组,而且通过指针对数组的元素进行操作。可以通过getFilePointer获取指针位置。同时可以通过seek改变指针的位置。还可以通过skipBytes(int n)方法跳过n个字节。

其实完成读写的原理就是内部封装了字节输入流和输出流。

该类只能操作文件,并且操作文件还有固定的mode

moder只读时,new对象只会去查找已经存在的文件;当moderw时,如果文件不存在则会自动创建一个文件。

这个类通过设置指针,可以从文件的指定位置的开始读写和修改数据。

 

· 管道流:

PipedInputStreamPipedOutputStream

输入输出可以直接进行连接,通过结合线程使用。不建议2个对象使用同一个线程,因为这样可能死锁。

通过PipedInputStream内的void connect(PipedOutputStreaem src)连接。

 

· 打印流:PrintWriterPrintStream

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

构造函数可以接收的对象:

1file对象 File

2,字符串路径 String

3,字节输出流 OutputStream

4,字符输出流 InputStream

参数中,也可以传入boolean autoFlush值,如果为trueprintln等方法自动刷新

 

· 序列流:SequenceInputStream

将多个读取流合并成为一个读取流。反过来思考,切割一个文件就是把文件输出到不同的流中。

 

· 操作对象:ObjectInputStreamObjectOutputStream

直接操作对象的流,被操作的对象需要实现Serializable接口。该接口会根据对象的成员生成一个UID。也可以自己在对象中定义一个这个序列号ANY-ACCESS-MODIFIER static final long serialVessionUID = 42L

并且,对象中的被static/transient修饰的成员,都不可以被序列化。

这个流就是对对象进行持久化本地存储。

 

· 操作基本数据类型:DataInputStream DataOutputStream

可以用于操作基本数据类型的流对象。

你可能感兴趣的:(java,黑马程序员)