黑马程序员-io流
---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------
概念:
java中的io流是用来处理设备间的数据传输的,而java操作数据的方式是通过流的方式,之所以被称为io流,是因为流按流向来分有 (Input)输入流和(Output)输出流。而按操作的数据类型可分为字符流和字节流,java中的流对象都在io包中。
将内存中的数据写入到外设中称为输出,将外设中的数据读取到内存称为输入。
附:java中字符流的由来,其实就是用字节流操作文字数据后,先查码表,把相应的字节数据转换为文字后,再对文字进行操作。
这句话就是说java中字符流内部有默认的编码表,该编码表为系统平台默认编码表。
字符流:
1.FileReader(输入流)
import java.io.FileReader;
import java.io.IOException;
public class FileReaderDemo {
/**FileReader字符输入流
* @param args
*/
public static void main(String[] args) {
//如果操作的数据是纯字符文件,就用字符流。
//注意字符流有默认的码表和缓冲区
FileReader fr=null;
try {
fr=new FileReader("f:\\1.txt");//如果文件不存在会抛出异常。
//1.第一种读取方式,单个字节读取。
// int ch=0;//一次读取一个字节,返回该字节对应的ascii码值。
// while((ch=fr.read())!=-1){
// System.out.println((char)ch);
// }
//2.第二种读取方式,数组读取。
char[] bufr=new char[1024];
int len=0;//把读到的数据存在数组缓冲中,返回读到的字符的个数。
while((len=fr.read(bufr))!=-1){
System.out.println(new String(bufr,0,len));//操作从0到len的数据,只有这部分才是有效的读取数据。
}
} catch (IOException e) {
// TODO: handle exception
}finally{
if(fr!=null){
try {
fr.close();
} catch (Exception e2) {
throw new RuntimeException("关闭流失败");
}
fr=null;
}
}
}
}
2.FileWriter(输出流)
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterDemo {
/**字符输出流FileWriter
* @param args
*/
public static void main(String[] args) {
//正常的流的操作流程。操作完流后要关闭流资源。
FileWriter fw=null;
try {
fw=new FileWriter("f:\\1.txt");//注意如果指定的路径不存在,则会抛出异常,不会创建流。如果存在就会覆盖。
fw.write("123");
//fw.write(1);//注意该方法只写出该字节的最低八位。
} catch (Exception e) {
// 出现异常后的处理代码
}finally{
if(fw!=null){
try {
fw.close();
} catch (IOException e) {
throw new RuntimeException("关闭流失败");
}
fw=null;//保证流资源被释放。
}
}
}
}
3.BufferedReader(缓冲输入流)
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReaderDemo {
/**缓冲输入流
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
FileReader fr=new FileReader("f:\\1.txt");
BufferedReader br=new BufferedReader(fr);
String line=null;
while((line=br.readLine())!=null){//缓冲输入流提供了readLine一次读取一行方法
System.out.println(line);
}
br.close();//底层就是关的被缓冲的流 fr
}
}
import java.io.IOException;
import java.io.Reader;
import java.util.Collection;
import java.util.Collections;
//模拟BufferReader,实现自己的缓冲读取流(自己的字符输入流装饰类)。
//在java中如果发现有些对象的方法不满足需求,这时我们可以对其进行增强,而增强的方式有三种:
//一是通过继承,直接覆盖要增强的方法;二是通过装饰设计模式;三是利用动态代理。
//实现装饰类五部曲:
//1.实现和继承与被装饰对象相同的接口和父类。
//2.定义变量记住被装饰对象。
//3.定义构造函数接受被装饰对象。
//4.覆盖要装饰的方法。
//5.不想被装饰的方法直接调用被装饰对象的方法。
public class MyBufferReader extends Reader {
//接受要缓冲的流对象。
private Reader fr;
//指定一个缓冲区,存放从源中读取的数据。
private char[] buf=new char[1024];
//指定角标,用于操纵缓冲区中的元素。
private int pos;
//指定计数器,用于记录缓冲区中元素的个数。
private int count;
public MyBufferReader(Reader fr){
this.fr=fr;
}
public int myRead() throws IOException{
if(count==0){
count=fr.read(buf);
//每次重新从源中获取数据后都要将操纵的角标置零。
pos=0;
}
if(count<0){
return -1;
}
char ch=buf[pos++];
count--;
return ch;
}
public String myReadLine() throws IOException{
StringBuilder sb=new StringBuilder();
int ch=0;
while((ch=myRead())!=-1){
if((char)ch=='\r')
continue;
if((char)ch=='\n')
return sb.toString();
sb.append((char)ch);
}
if(sb.length()!=0){
return sb.toString();
}
return null;
}
@Override
public void close() throws IOException {
fr.close();
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return fr.read(cbuf, off, len);
}
}
4.BufferedWriter(缓冲输出流)
public class BufferedWriterDemo {
/**缓冲输出流
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
FileWriter fr=new FileWriter("f:\\1.txt");
BufferedWriter br=new BufferedWriter(fr);//内部使用默认的缓冲区大小。也可以自己指定。
br.write("123");
br.newLine();//特有的方法,写入换行符。其实就是用了System.getProperty("line.separator")
br.close();//底层就是关的被缓冲的流 fr,所以不用再关fr
}
}
字节流:
1.FileInputStream(输入流)
public class FileInputStreamDemo {
/**缓冲输入流
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//字节流读取方式也有两种形式,一个字节读取或缓冲读取。
FileInputStream fis=new FileInputStream("f:\\1.txt");
byte[] bufr=new byte[1024];//与字节流不同,缓冲用字节数组。
int len=0;
while((len=fis.read(bufr))!=-1){
System.out.println(new String(bufr,0,len));
}
}
}
2.FileOutputStream(输出流)
public class FileOutputStreamDemo {
/**缓冲输出流
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//字节输出流和字符流不同,它没有缓冲,不用刷新,但要关闭资源。
FileOutputStream fos=new FileOutputStream("f:\\1.txt");
fos.write("123".getBytes());
fos.close();
}
}
转换流:
1.InputStreamReader(字节通向字符)
public class InputStreamReaderDemo {
/**转换流
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//字节通向字符转换流,可以指定码表。
InputStreamReader isr=new InputStreamReader(new FileInputStream("f:\\student.txt"),"GBK");
char[] bufr=new char[1024];
int len;
while((len=isr.read(bufr))!=-1){
System.out.println(new String(bufr,0,len));
}
isr.close();
}
}
2.OutputStreamWriter(字符通向字节)
public class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
//字符通向字节转换流,可以指定码表。
OutputStreamWriter osr=new OutputStreamWriter(new FileOutputStream("f:\\1.txt"),"GBK");
osr.write("123");//不需要刷新,因为是字节流。
osr.close();
}}
File对象:
在用流操作数据时经常用到的File对象,该对象可以将文件和目录封装起来,方便操作。
public class FileDemo2 {
/**创建文件清单文件
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
List<String> list=new LinkedList<String>();
File dir=new File("e:\\java");//将制定目录或文件封装
FilenameFilter filter=new FilenameFilter() {//匿名内部类 ,过滤器
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".java");
}
};
getFiles(dir,filter,list);
File file=new File("f:\\list.txt");
BufferedWriter fos=new BufferedWriter(new FileWriter(file));
for(String s : list){
fos.write(s);
fos.newLine();
fos.flush();
}
fos.close();
}
//多级目录遍历,这里用的是递归。
//注意:1.递归时注意递归的次数,否则容易堆栈溢出。
//2.递归时应注意递归条件。
public static void getFiles(File dir, FilenameFilter filter,
List<String> list) {
File[] files=dir.listFiles();
for(File file : files){
if(file.isDirectory()){
getFiles(file, filter, list);
}else{
if(filter.accept(file, file.getName())){
list.add(file.getAbsolutePath());
}
}
}
}
}
public class FileDemo {
/**
* @param args
*/
public static void main(String[] args) {
getMaxFreeSpaceDir();
//File dir=new File("f:\\AgileSoft");
//listAll(dir,0);
// File dir=new File("f:\\1");
// deleteDeepDir(dir);
}
//删除多级目录的文件夹
public static void deleteDeepDir(File dir) {
File[] files=dir.listFiles();
for(File file : files){
if(file.isDirectory()){
deleteDeepDir(file);
}else {
System.out.println(file+"::"+file.delete());
}
}
System.out.println(dir+"::"+dir.delete());//当把一个文件夹下的文件都删除后,就把文件夹删除
}
public static void listAll(File dir,int level) {
System.out.println(getLevel(level)+dir.getName());
level++;
File[] files=dir.listFiles();//返回file对象数组。
if (files != null && files.length != 0) {
for (File file : files) {
if (file.isDirectory())
listAll(file.getAbsoluteFile(), level);
else
System.out.println(getLevel(level) + file.getName());
}
}
}
//获取目录层级
private static String getLevel(int level) {
StringBuilder sb=new StringBuilder();
sb.append("|--");
for(int i=0;i<level;i++){
sb.insert(0, " ");
}
return sb.toString();
}
// 获取本机最大空闲空间盘符
public static void getMaxFreeSpaceDir() {
File[] files=File.listRoots();
String dirroot="";
long size=0;
for(File file:files){
System.out.println(file.getPath()+":"+(file.getFreeSpace()>>>30)+"GB");
if(file.getFreeSpace()>size){
size=file.getFreeSpace();
dirroot=file.getPath();
}
}
System.out.println("The max freespace directory is "+dirroot);
}
}
其他功能流:
1.SequenceInputStream(合并流)
public class SequenceInDemo {
/**
* @param args
* 演示合并流SequenceInputStream
* @throws IOException
*/
public static void main(String[] args) throws IOException {
Vector<InputStream> vector=new Vector<InputStream>();
vector.add(new FileInputStream("1.txt"));
vector.add(new FileInputStream("2.txt"));
vector.add(new FileInputStream("3.txt"));
Enumeration<InputStream> en=vector.elements();
//通过集合工具类获得枚举
// ArrayList<InputStream> list=new ArrayList<InputStream>();
// for(int i=0;i<3;i++){
// list.add(new FileInputStream(i+".txt"));
// }
// final Iterator<InputStream> it=list.iterator();
// Enumeration<InputStream> en=new Enumeration<InputStream>() {
//
// @Override
// public boolean hasMoreElements() {
// return it.hasNext();//由于迭代器和枚举的功能是重复的,所以直接调用迭代器的方法即可。
// }
//
// @Override
// public InputStream nextElement() {
// return it.next();
// }
// };
//Enumeration<InputStream> en=Collections.enumeration(list);
//合并流接受的是Enumeration
SequenceInputStream sis=new SequenceInputStream(en);
FileOutputStream fis=new FileOutputStream("4.txt");
byte[] bufr=new byte[1024];
int len=0;
//合并流在读取数据时,会把合并流中最后一个流的结尾作为结束标记。
//也就是说当合并流中的第一个流读到结尾时会继续判断合并流中还又没有下一个流。
while((len=sis.read(bufr))!=-1){
fis.write(bufr, 0, len);
}
sis.close();
fis.close();
}
}
2. ObjectOutputStream和ObjectInputStream(对象操作流)
//序列化和反序列化对象
//一个对象想要被序列化,那么创建该对象的类必须实现Serializable接口。
public class ObjectStreamDemo {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
//writeObject();
readObject();
}
//反序列化对象
//在反序列化时,如果创建该对象的类的serialVersionUID改变,而此时并未重新序列化,那么在反序列化原来的对象时将会抛出InvalidClassException异常。
private static void readObject() throws FileNotFoundException, IOException, ClassNotFoundException {
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("f:\\obj.object"));
Student student=(Student) ois.readObject();//这句话会用到obj.object文件和该对象的字节码文件,如果找不到其字节码文件,会抛出ClassNotFoundException
System.out.println(student.getName()+":"+student.getAge());
}
//序列化对象
//为什么要序列化,因为有些对象的创建比较复杂,所以我们不需要再每次都去创建这个对象,而只需将它序列化到硬盘。
private static void writeObject() throws IOException, FileNotFoundException {
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("f:\\obj.object"));
oos.writeObject(new Student("小李子",20));//将一个对象序列化到硬盘。
oos.close();
}
}
//被序列化的对象的生成类
public class Student implements Serializable {
/**
* Serializable接口为标记接口,里面没有任何方法,实现了该接口就是标记该类可以序列化。
* 一个类实现Serializable接口后,不显示指定serialVersionUID时,编译器会默认给该类添加一个serialVersionUID
* 正因为编译器版本的不同,所以导致同一个类在不同编译器下默认生成的serialVersionUID会不同,因此强烈建议显示指定serialVersionUID。
* serialVersionUID的产生,也正是Serializable接口的作用。
*
* 一个类被序列化时只将该类中的非瞬态和非静态的字段序列化。
* 被标识为transient的字段为瞬时的,表示该字段不会被序列化,当一个字段不是共享数据(static)时又不想被序列化,就将该字段标识为transient。
*/
private static final long serialVersionUID = 231231313131L;
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
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;
}
}
3.RandomAccessFile(不属于io体系)
//RandomAccessFile不属于io体系。该对象一般用于多线程同时往一个文件写入数据。通过seek方法操作指针完成。
//RandomAccessFile特点:
//1.该对象既能读,又能写。
//2.该对象内部维护了一个byte数组,并可以通过指针操作数组中的元素。
//3.可以同个getFilePointer方法获取指针位置,seek方法设置指针位置。
//4.该对象内部封装了字节输入流和输出流。
//5.该对象的源和目的只能是文件,通过构造函数看出。
public class RandomAccessDemo {
public static void main(String[] args) throws IOException {
writeFile();
randomWrite();
readFile();
}
//RandomAccessFile随机读取数据
private static void readFile() throws FileNotFoundException, IOException {
RandomAccessFile raf=new RandomAccessFile("f:\\rand.txt", "r");
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+":"+age);
}
//体现RandomAccessFile的随机写入
private static void randomWrite() throws IOException {
RandomAccessFile raf=new RandomAccessFile("f:\\rand.txt", "rw");
raf.seek(2*8);//设置指针位置,随机写入,如果不设置,则从数组零角标开始写入,所以存在覆盖。
raf.write("小星".getBytes());
raf.writeInt(102);
raf.close();
}
//用RandomAccessFile往文件里写入数据
private static void writeFile() throws FileNotFoundException, IOException {
//如果文件不存在则创建,存在则不创建。
RandomAccessFile raf=new RandomAccessFile("f:\\rand.txt", "rw");
raf.write("张三".getBytes());
raf.writeInt(89);
raf.write("小强".getBytes());
raf.writeInt(99);
raf.close();
}
}
4.PipedInputStream和PipedOutputStream(管道流,一般用于多线程)
//管道流,用于多线程,因为在单线程情况下容易死锁(read方法的阻塞性导致的)。
public class PipedStreamDemo {
//
public static void main(String[] args) throws IOException {
PipedInputStream input=new PipedInputStream();
//PipedInputStream input=new PipedInputStream(new PipedOutputStream());
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;
public Input(PipedInputStream in) {
this.in=in;
}
@Override
public void run() {
try {
byte[] buf=new byte[1024];
int len=in.read(buf);
System.out.println(new String(buf,0,len));
in.close();
} catch (Exception e) {
}
}
}
class Output implements Runnable{
private PipedOutputStream out;
public Output(PipedOutputStream out) {
this.out=out;
}
public void run(){
try {
TimeUnit.SECONDS.sleep(5);//休眠时间为指定格式的时间
out.write("管道流。。。。。。。".getBytes());
out.close();
} catch (Exception e) {
// TODO: handle exception
}
}
}
5.ByteArrayInputStream和ByteArrayOutputStream(操作内存的流对象,该流不用关闭)
public class ByteArrayStreamDemo {
public static void main(String[] args) {
// ByteArrayInputStream和ByteArrayOutputStream 底层不涉及系统资源,所以不需要关闭。其实该流就是在用流的思想操作数组。
//注意,由于该流操作的是内存,所以在操作时应该注意数据不要太大。
ByteArrayInputStream bais=new ByteArrayInputStream("abc".getBytes());
ByteArrayOutputStream baos=new ByteArrayOutputStream();
int ch=0;
while((ch=bais.read())!=-1){
baos.write(ch);
}
System.err.println(baos.toString());
}
}
总结:
由于io体系中,对象较多,所以在使用流对象时应该明确源和目的,以及相关的功能,确保选择相应的流对象,这些流对象都有一个共有的特点:它们的后缀标明了它们所属流体系,前缀标明了它们的功能。这样在选择流对象时就能更好的分析了。
---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------