------ ASP.Net+Android+IO开发.Net培训期待与您交流! ------
总结内容来源于黑马毕老师的java基础教学视频
IO流
IO流,用来处理设备间的数据传输,Java中对数据的操作是通过流的方式,用于操作流的对象都在IO包中.
流按流向可分为输入流和输入流;
流按操作的数据可分为字节流和字符流;
字节流操作的是字节,字符流操作的是字符。
先有字节流,为了方便人们识别和操作,将字节编码通过编码表生成字符,由此在字节流的基础上产生了字符流。
Java的IO流中主要由四个基类:
字符流的基类:Writer,Reader;
字节流的基类:InputStream,OutputStream;
字符输出流 Writer
找到一个专门用于操作文件的Writer子类对象FileWriter
(前缀是该流对象的功能,后缀是父类名)
- import java.io.*;
- class FileWriterDemo
- {
- public static void main(String[] args) throws IOException
- {
- //创建一个FileWriter对象,该类对象一被初始化就必须要明确被操作的文件
- //而且该文件会被创建到指定目录下,如果该目录下已有同名文件,将被覆盖.
- //其实该步就是在明确数据要存放的目的地
- FileWriter fw = new FileWriter("demo.txt");
- //调用write方法,将字符串写到流中
- fw.write("dwadwadwa");
- //刷新流对象中的缓冲中的数据
- //刷新到目的地中.
- //fw.flush();
- //关闭流资源,关闭前会刷新一次内部的缓冲数据
- //将数据刷到目的地中
- //和flush 的区别:flush刷新,流可以继续使用,close刷新后,流关闭.
- fw.close();
- }
- }
IO异常的处理方式.
- import java.io.*;
- class FileWriterDemo2
- {
- public static void main(String[] args)
- {
- //建立并初始化流
- FileWriter fw = null;
- try
- {
- fw = new FileWriter("demo.txt");
- fw.write("abcdefg");
- }
- catch (IOException e)
- {
- System.out.println(e.toString());
- }
- finally
- {
- try
- {
- //防止出现null异常,先判断fw是否为null
- if(fw!=null)
- fw.close();
- }
- catch (IOException e)
- {
- System.out.println(e.toString());
- }
- }
- }
- }
演示对已有文件的数据续写.
- import java.io.*;
- class FileWriterDemo3
- {
- public static void main(String[] args)
- {
- FileWriter fw = null;
- try
- {
- //传递一个true参数,代表不覆盖已有文件.并在已有文件的末尾处进行数据的续写
- fw = new FileWriter("demo.txt",true);
- //windows中换行是\r\n
- fw.write("haha\r\nxiexie");
- }
- catch (IOException e)
- {
- System.out.println(e.toString());
- }
- finally
- {
- try
- {
- if(fw!=null)
- fw.close();
- }
- catch (IOException e)
- {
- System.out.println(e.toString());
- }
- }
- }
- }
字符输入流 Reader
找到一个专门用于读取的字符输入流FileReader
示例代码
- /*
- 文件的读取
- 第一种方式:通过每次取出一个字符的方式读取
- read()返回的字符的10进制数字.
- */
- import java.io.*;
- class FileReaderDemo
- {
- public static void main(String[] args)
- {
- FileReader fr = null;
- try
- {
- //创建一个文件读取流对象,和指定名称的文件相关联.
- //要保证该文件是已经存在的,如果不存在,会发生FIleNotFoundException异常
- fr = new FileReader("demo.txt");
- int i = 0;
- while((i=fr.read())!=-1)
- {
- System.out.println("i="+(char)i);
- }
- }
- catch (IOException e)
- {
- System.out.println(e.toString());
- }
- finally
- {
- try
- {
- if(fr!=null)
- fr.close();
- }
- catch (IOException e)
- {
- System.out.println(e.toString());
- }
- }
- }
- }
- /*
- 第二种方式:通过字符数组进行读取.
- */
- import java.io.*;
- class FileReaderDemo2
- {
- public static void main(String[] args)
- {
- FileReader fr = null;
- try
- {
- fr = new FileReader("demo.txt");
- //定义一个字符数组,用于存储读取到的字符
- //该read(char[])返回的是读到的字符个数
- char[] ch = new char[1024];
- int num = 0;
- while ((num=fr.read(ch))!=-1)
- {
- System.out.println(new String(ch,0,num));
- }
- }
- catch (IOException e)
- {
- System.out.println(e.toString());
- }
- finally
- {
- try
- {
- if(fr!=null)
- fr.close();
- }
- catch (IOException e)
- {
- System.out.println(e.toString());
- }
- }
- }
- }
- //文件的读取练习
- import java.io.*;
- class FileReaderTest
- {
- public static void main(String[] args)
- {
- FileReader fr = null;
- try
- {
- fr = new FileReader("DateDemo.java");
- char[] ch = new char[1024];
- int num = 0;
- while ((num=fr.read(ch))!=-1)
- {
- System.out.println(String.valueOf(ch));
- }
- }
- catch (IOException e)
- {
- System.out.println(e.toString());
- }
- finally
- {
- try
- {
- if(fr!=null)
- fr.close();
- }
- catch (IOException e)
- {
- System.out.println(e.toString());
- }
- }
- }
- }
字符流(拷贝文本文件练习)用到FileWriter和FileReader
- /*
- 将C盘一个文本文件复制到D盘
- 复制原理:
- 其实就是讲C盘下的文件数据存储到D盘的一个文件中
- 步骤:
- 1.在D盘创建一个文件,用于存储C盘文件中的数据.
- 2.定义读取流和C盘文件关联.
- 3.通过不断的读写完成数据存储.
- 4.关闭资源.
- */
- import java.io.*;
- class CopyTest
- {
- public static void main(String[] args) throws IOException
- {
- copy_1();
- }
- public static void copy_1()throws IOException
- {
- FileWriter fw = null;
- FileReader fr = null;
- try
- {
- fw = new FileWriter("d:\\demo.txt");
- char[] copy = new char[1024];
- fr = new FileReader("c:\\demo.txt");
- int len = 0;
- while ((len=fr.read(copy))!=-1)
- {
- fw.write(copy,0,len);
- }
- }
- catch (IOException e)
- {
- throw new RuntimeException("读写失败");
- }
- finally
- {
- if(fr!=null)
- try
- {
- fr.close();
- }
- catch (IOException e)
- {
- System.out.println(e.toString());
- }
- if(fw!=null)
- try
- {
- fw.close();
- }
- catch (IOException e)
- {
- System.out.println(e.toString());
- }
- }
- }
- }
BufferdWriter和BufferedReader字符流缓冲区
字符流缓冲区的出现是为了提高流的操作效率而出现的.
所以在创建缓冲区之前,必须要先有流对象
该缓冲区中提供了一个跨平台的换行符
newLine():
原理:底层调用read()方法,把数据存到临时数组中,
读到回车符的时候,就把数组中的数据全部返回.
- import java.io.*;
- class BufferedWriterDemo
- {
- public static void main(String[] args) throws IOException
- {
- //创建一个字符写入流对象
- FileWriter fw = new FileWriter("buf.txt");
- //为了提高字符写入流效率,加入缓冲技术.
- //只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可
- BufferedWriter bw = new BufferedWriter(fw);
- for (int x=0; x<5; x++)
- {
- bw.write("abcde");
- //记住,只要用到缓冲区,就要记得刷新
- bw.newLine();
- bw.flush();
- }
- //其实关闭缓冲区,就是再关闭缓冲区流对象.
- bw.close();
- }
- }
该缓冲区提供了一个一次读取一行的方法readLine(),返回一个字符串,方便于对文本数据的获取.
当返回null时,表示读到文件末尾.
- import java.io.*;
- class BufferedReaderDemo
- {
- public static void main(String[] args) throws IOException
- {
- //创建一个读取流对象和文件相关联
- FileReader fr = new FileReader("buf.txt");
- //为了提高效率,加入缓冲技术.将字符读取流对象作为参数传递给缓冲对象.
- BufferedReader br = new BufferedReader(fr);
- String line = null;
- while ((line = br.readLine())!=null)
- {
- System.out.println("line="+line);
- }
- }
- }
字符流(通过缓冲区复制.java文件)
- /*
- 通过缓冲区复制一个.java文件.
- */
- import java.io.*;
- class CopyTextByBuf
- {
- public static void main(String[] args)
- {
- FileWriter fw = null;
- FileReader fr = null;
- BufferedWriter bw = null;
- BufferedReader br = null;
- try
- {
- fw = new FileWriter("CopyByBuf.txt");
- fr = new FileReader("BufferedWriterDemo.java");
- bw = new BufferedWriter(fw);
- br = new BufferedReader(fr);
- String line = null;
- while ((line = br.readLine())!=null)
- {
- bw.write(line);
- bw.newLine();
- bw.flush();
- }
- }
- catch (IOException e)
- {
- throw new RuntimeException("读写失败");
- }
- finally
- {
- if(bw!=null)
- try
- {
- bw.close();
- }
- catch (IOException e)
- {
- throw new RuntimeException("无此对象");
- }
- if(br!=null)
- try
- {
- br.close();
- }
- catch (IOException e)
- {
- throw new RuntimeException("无此对象");
- }
- }
- }
- }
字符流(readLine方法的原理和写法)
- /*
- 明白了BufferedReader类中特有方法readLine的原理后
- 可以自定义一个类中包含一个功能和readLine一致的方法
- 来模拟一下BufferedReader
- */
- import java.io.*;
- class MyBufferedDemo1
- {
- public static void main(String[] args)
- {
- FileReader fr = null;
- MyBufferedReader mbr =null;
- try
- {
- fr = new FileReader("CopyByBuf.txt");
- mbr = new MyBufferedReader(fr);
- String s = null;
- while ((s = mbr.myReadLine())!=null)
- {
- System.out.println(s);
- }
- }
- catch (IOException e)
- {
- throw new RuntimeException(e);
- }
- finally
- {
- if(mbr!=null)
- try
- {
- mbr.myClose();
- }
- catch (IOException e)
- {
- throw new RuntimeException(e);
- }
- }
- }
- }
- class MyBufferedReader
- {
- private FileReader r;
- MyBufferedReader(FileReader r)
- {
- this.r = r;
- }
- public String myReadLine() throws IOException
- {
- //定义一个临时容器.原BufferedReader封装的字符数组.
- //为了演示方便.定义一个StringBuilder容器.因为最终还是要将数组变成字符串.
- StringBuilder sb = new StringBuilder();
- int ch = 0;
- while ((ch = r.read())!=-1)
- {
- if(ch=='\r')
- continue;
- if(ch=='\n')
- return sb.toString();
- sb.append((char)ch);
- }
- //上面的循环语句,只能返回有换行符的每行
- //这个判断语句在最后一行没有换行符的时候,仍然能返回最后一行
- if(sb.length()!=0)
- return sb.toString();
- return null;
- }
- public void myClose() throws IOException
- {
- r.close();
- }
- }
装饰设计模式
- /*
- 装饰设计模式:
- 当想要对已有的对象进行功能增强时,
- 可以定义一个类,将已有对象传入,基于已有对象的功能,
- 并提供此功能加强功能,那么自定义的类称为装饰类.
- 装饰类通常会通过构造方法接收被装饰的对象.
- 并基于被装饰的对象的功能,提供更强的功能.
- */
- class Person
- {
- public void chifan()
- {
- System.out.println("吃饭");
- }
- }
- //SuperPerson就是Person的装饰类,它增强了Person的吃饭方法
- class SuperPerson
- {
- private Person p;
- SuperPerson(Person p)
- {
- this.p = p;
- }
- public void superChifan()
- {
- System.out.println("开胃酒");
- p.chifan();
- System.out.println("甜点");
- System.out.println("来一根");
- }
- }
- class PersonDemo
- {
- public static void main(String[] args)
- {
- Person p = new Person();
- SuperPerson sp = new SuperPerson(p);
- sp.superChifan();
- }
- }
继承和装饰的区别和特点
继承和装饰的区别和特点
MyReader//专门用于读取数据的类
|--MyTextReader
|--MyBufferedTextReader
|--MyMediaReader
|--MyBufferedMediaReader
|--MyDataReader
|--MyBufferedDataReader
class MyBufferedReader
{
MyBufferedReader(MyBufferedTextReader text)
{}
MyBufferedReader(MyBufferedMediaReader media)
{}
MyBufferedReader(MyDataReader data)
{}
}
上面的类扩展性很差,找到其参数的共同类型.通过多态的形式,可以提高其扩展性
class MyBufferedRead extends MyReader
{
MyBufferedRreader(MyReader r)
{}
}
MyReader//专门用于读取数据的类
|--MyTextReader
|--MyMediaReader
|--MyDataReader
|--MyBufferedReader
装饰模式比继承要灵活,避免了继承体系臃肿.
而且降级了类于类之间的关系.
装饰类因为增强已有对象,具备的功能和已有的是相同,只不过提供了更强功能.
所以装饰类和被装饰类通常都是一个体系中的.
自定义一个Reader的装饰类
- /*
- 自定义一个继承Reader的装饰类,并复写了Reader的抽象方法.
- */
- import java.io.*;
- class MyBufferedReader extends Reader
- {
- private Reader r;
- MyBufferedReader(Reader r)
- {
- this.r = r;
- }
- //增强了Reader的read()方法
- public String readLine() throws IOException
- {
- //定义一个临时容器.原BufferedReader封装的字符数组.
- //为了演示方便.定义一个StringBuilder容器.因为最终还是要将数组变成字符串.
- StringBuilder sb = new StringBuilder();
- int ch = 0;
- while ((ch = r.read())!=-1)
- {
- if(ch=='\r')
- continue;
- if(ch=='\n')
- return sb.toString();
- sb.append((char)ch);
- }
- //上面的循环语句,只能返回有换行符的每行
- //这个判断语句在最后一行没有换行符的时候,仍然能返回最后一行
- if(sb.length()!=0)
- return sb.toString();
- return null;
- }
- //复写了Reader的read(char[],int,int)方法
- public int read(char[] ch,int a,int b) throws IOException
- {
- return r.read(ch,a,b);
- }
- //复写了Reader的close()方法
- public void close() throws IOException
- {
- r.close();
- }
- }
LineNumberReader也是一个包装类,它在BufferedReader的基础上增加了一个记录行号的功能,而记录行号是在readLine方法中操作的,所以它继承了BufferedReader并复写了readLine方法,同时增加了getLineNumber和setLineNumber方法。
LineNumberReader读取行号的原理
- /*
- 解析LineNumberReader的原理,通过自定义一个功能相同的类来体现.
- */
- import java.io.*;
- class MyLineNumberBuffered extends BufferedReader
- {
- private Reader r;
- private int count;
- //调用父类构造函数,提高代码复用
- MyLineNumberBuffered(Reader r)
- {
- super(r);
- }
- //调用父类readLine方法,提高代码复用性
- public String readLine() throws IOException
- {
- //每次调用readLine时count就自增一次
- count++;
- return super.readLine();
- }
- public int getLineNumber()
- {
- return count;
- }
- public int setLineNumber(int count)
- {
- return this.count = count;
- }
- }
- class MyLineNumberBufferedDemo
- {
- public static void main(String[] args) throws IOException
- {
- FileReader fr = new FileReader("CopyByBuf.txt");
- MyLineNumberBuffered mlnb = new MyLineNumberBuffered(fr);
- String s = null;
- mlnb.setLineNumber(100);
- while ((s = mlnb.readLine())!=null)
- {
- System.out.println(mlnb.getLineNumber()+"::"+s);
- }
- }
- }
字节流
字节流的两个基类是InputStream和OutputStream,相应的缓冲区是BufferedInputStream和BufferedOutputStream。
它的操作与字符流类似,可以参与字符流的定义、读取、写入、处理异常的格式。
只不过是处理的数据不同,因为对于非字符的数据,比如图片、视频、音频文件(例如mp3)等,这些文件只能用字节流对之进行操作。
字节流的写操作,不用刷新动作,也能写入目的地,这是为什么?
字符流底层用也的字节流,因为涉及编码,比如写一个汉字,它有两个字节,你不能读一个字节就写入,而要等到两个字节都读取完后,才写入,
所以必须要有flush机制。而字节流,不涉及编码,可以读一个就写一个而不出现问题,所以不用刷新也能写入。
字节流特有的读取方式:available()方法可以知道文件的所有字节数,以此可以定义一个刚
刚好的字节数组,只用读取一次,然后写入来完成文件复制。但注意:如果字节数过大,会造成内存溢出。
字节流读写示例:
- /*
- 字节流
- 读 InputStream 写 OutputStream
- 需求:想要操作图片数据.这时就要用到字节流.
- */
- import java.io.*;
- class FileStream
- {
- public static void main(String[] args) throws IOException
- {
- writeFile();
- readFile_1();
- System.out.println();
- readFile_2();
- System.out.println();
- readFile_3();
- }
- public static void writeFile() throws IOException
- {
- FileOutputStream fos = new FileOutputStream("fos.txt");
- fos.write("abcde".getBytes());
- fos.close();
- }
- public static void readFile_1() throws IOException
- {
- FileInputStream fis = new FileInputStream("fos.txt");
- int by = 0;
- while ((by=fis.read())!=-1)
- {
- System.out.print((char)by);
- }
- fis.close();
- }
- public static void readFile_2() throws IOException
- {
- FileInputStream fis = new FileInputStream("fos.txt");
- byte[] bys = new byte[1024];
- int by = 0;
- while ((by=fis.read(bys))!=-1)
- {
- System.out.print(new String(bys,0,by));
- }
- fis.close();
- }
- public static void readFile_3() throws IOException
- {
- FileInputStream fis = new FileInputStream("fos.txt");
- byte[] bys = new byte[fis.available()];//定义一个刚刚好的数组,
- fis.read(bys); //不建议用这种.
- System.out.println(new String(bys));
- fis.close();
- }
- }
复制一个图片
- /*
- 复制一个图片
- 思路:
- 1.用字节读取流对象和图片关联.
- 2.用字节写入流创建一个图片文件.用于存储获取到的图片数据.
- 3.通过循环读写,完成数据的存储.
- 4.关闭资源
- */
- import java.io.*;
- class CopyPic
- {
- public static void main(String[] args)
- {
- FileInputStream fis = null;
- FileOutputStream fos = null;
- try
- {
- fis = new FileInputStream("c:\\1.jpg");
- fos = new FileOutputStream("c:\\2.jpg");
- byte[] bys = new byte[1024];
- int num = 0;
- while ((num = fis.read(bys))!=-1)
- {
- fos.write(bys);
- }
- }
- catch (IOException e)
- {
- System.out.println("复制图片失败");
- }
- finally
- {
- try
- {
- if(fis!=null)
- fis.close();
- }
- catch (IOException e)
- {
- System.out.println("读取关闭失败");
- }
- try
- {
- if(fos!=null)
- fos.close();
- }
- catch (IOException e)
- {
- System.out.println("写入关闭失败");
- }
- }
- }
- }
利用字节流缓冲区复制MP3
- import java.io.*;
- class CopyMp3
- {
- public static void main(String[] args)
- {
- FileInputStream fis = null;
- FileOutputStream fos = null;
- BufferedInputStream bis = null;
- BufferedOutputStream bos = null;
- try
- {
- fis = new FileInputStream("c:\\1.mp3");
- fos = new FileOutputStream("c:\\2.MP3");
- bis = new BufferedInputStream(fis);
- bos = new BufferedOutputStream(fos);
- int num =0;
- while ((num=bis.read())!=-1)
- {
- bos.write(num);
- }
- }
- catch (IOException e)
- {
- System.out.println("复制MP3失败");
- }
- finally
- {
- try
- {
- if(bis!=null)
- bis.close();
- }
- catch (IOException e)
- {
- System.out.println("读取关闭失败");
- }
- try
- {
- if(bos!=null)
- bos.close();
- }
- catch (IOException e)
- {
- System.out.println("写入关闭失败");
- }
- }
- }
- }
自定义字节流缓冲区
- import java.io.*;
- class MyBufferedInputStream
- {
- //定义计数器count,角标pos
- private int count = 0,pos = 0;
- byte[] bys = new byte[1024*1024];
- private InputStream in;
- MyBufferedInputStream(InputStream in)
- {
- this.in = in;
- }
- public int myRead() throws IOException
- {
- //计数器是否为0
- if(count==0)
- {
- count = in.read(bys);//把字节流读取的数据存到数组中
- if(count<0)//字节流读取到数据的结尾处就返回-1
- return -1;
- pos = 0;//初始化角标pos为0
- byte b = bys[pos];
- count--;//计数器count自减一次
- pos++;//角标pos自增
- return b&255;
- }
- else if(count>0)
- {
- int b = bys[pos];
- count--;
- pos++;
- return b&255;//byte型的b的二进制有可能是1111-1111,十进制就是-1,
- } //就会直接中止read,所以要&255取后8位并把前面补上0
- else
- return -1;
- }
- public void myClose() throws IOException
- {
- in.close();
- }
- }
- class MyBufferedInputStreamDemo
- {
- public static void main(String[] args) throws IOException
- {
- long start = System.currentTimeMillis();
- FileOutputStream ou = new FileOutputStream("c:\\3.mp3");
- FileInputStream in = new FileInputStream("c:\\1.mp3");
- MyBufferedInputStream mbis = new MyBufferedInputStream(in);
- int num = 0;
- while ((num = mbis.myRead())!=-1)
- {
- ou.write(num);
- }
- mbis.myClose();
- long end = System.currentTimeMillis();
- System.out.println((end-start)+"毫秒");
- }
- }
读取键盘录入。
System.out:对应的是标准输出设备,控制台。
System.in:对应的是标准的输入设备,键盘。
通过键盘录入数据。
当录入一行数据后,就将改行数据打印。
如果录入的数据是over,那么停止录入。
- /*
- 读取键盘录入.
- System.out:对应标准输出设备,控制台
- System.in:对应标准输入设别:键盘.
- */
- import java.io.*;
- class ReadIn
- {
- public static void main(String[] args) throws IOException
- {
- InputStream in = System.in;
- StringBuilder sb = new StringBuilder();
- while (true)
- {
- int ch = in.read();
- if(ch=='\r')
- continue;
- if(ch=='\n')//如果是回车符
- {
- String s = sb.toString();//就把sb转成字符串
- if("over".equals(s))//判断是否是over
- break;//如果是,就停止循环
- System.out.println(s.toUpperCase());//如果不是,就输出大写
- sb.delete(0,sb.length());//输出后,把sb清空,下次继续存入
- }
- else
- sb.append((char)ch);
- }
- }
- }
通过刚才的键盘录入一行数据并打印其大写,发现其实就是读一行数据的原理.
也就是readLine方法
能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?
readLine方法是字符流BufferedReader类中的方法.
而键盘录入的read方法是字节流InputStream的方法.
那么能不能将字节流转成字符流在使用字符缓冲区的readLine方法呢?
在API中找到Reader的子类
|--InputStreamReader:读取转换流
|--OutputStreamWriter:写入转换流
- import java.io.*;
- class TransStreamDemo
- {
- public static void main(String[] args) throws IOException
- {
- //获取键盘录入对象
- // InputStream in = System.in;
- //将字节流对象转成字符流对象,使用转换流InputStreamReader
- // InputStreamReader isr = new InputStreamReader(in);
- //为了提高效率,将字符串进行缓冲区技术高效操作.使用BufferedReader
- // BufferedReader br = new BufferedReader(isr);
- //最常用的写法,背下来
- BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
- // OutputStream out = System.out;
- // OutputStreamWriter osw = new OutputStreamWriter(out);
- // BufferedWriter bw = new BufferedWriter(osw);
- BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
- String s = null;
- while ((s = br.readLine())!=null)
- {
- if(s.equals("over"))
- break;
- bw.write(s.toUpperCase());
- bw.newLine();
- bw.flush();
- }
- }
- }
流操作的规律
流操作的基本规律:
最痛苦的就是流对象有很多,不知道该用那一个.
通过3个明确来完成.
1.明确源和目的.
源:输入流.InputStream Reader
目的:输出流.OutputStream Writer
2.操作的数据是否是纯文本.
是:字符流
不是:字节流
3.当体系明确后,明确要使用哪个具体的对象.
通过设备来进行区分:
源设备有:内存,硬盘,键盘
目的设别:内存,硬盘,控制台
------------------------------------------------------------------------------
范例 1.将一个文本文件中数据存储到另一个文件中.复制文件
源:因为是源,所以使用读取流.InputStream Reader
是不是操作文本文件?
是!这时就可以选择Reader
这样体系就明确了
接下来要明确要使用该体系中的那个对象.
明确设别:硬盘上的文件
Reader体系中可以操作文件的对象是FileReader
是否需要提高效率? 是! 加入Reader体系中的缓冲区BufferedReader.
FileReader fr = new FileReader("一个文件");
BufferedReader br = new BufferedReader(fr);
目的:OutputStream Writer
是不是纯文本?
是!Writer
设别:硬盘,一个文件
Writer体系中可以操作文件的对象FileWriter
是否需要提高效率? 是! 加入Writer体系中的缓冲区BufferedWriter.
FileWriter fw = new FileWriter("另一个文件");
BufferedWriter bw = new BufferedWriter(fw);
------------------------------------------------------------------------------
练习:将一个图片文件中数据存储到另一个文件中,复制文件,按照上面格式完成三个明确.
源:InputStream Reader
是不是操作文本文件?
不是!选择InputStream
设备:硬盘上的文件
InputStream体系中可以操作文件的是FileInputStream
是否需要提高效率? 是! 加入InputStream体系中的BufferedInputStream.
FileInputStream fis = new FileInputStream("一个图片");
BufferedInputStream bis = new BufferedInputStream(fis);
目的:是不是纯文本?
不是!OutputStream
设备:硬盘,图片
OutputStream体系中可以操作文件的是FileOutputStream
是否需要提高效率? 是! 加入OutputStream体系中的BufferedOutputStream.
FileOutputStream fos = new FileOutputStream("另一个图片");
BufferedOutputStream bos = new BufferedOutputStream(fos);
-------------------------------------------------------------------------------
范例 2.将键盘录入的数据保存到一个文件中.
源:是不是纯文本?
是! Reader
设备:键盘,对应的对象是System.in
不是选择Reader嘛? System.in对应的不是字节流InputStream嘛?
为了操作键盘的文本数据方便,转成字符流按照字符串操作是最方便的.
所以既然明确了Reader,那么就将System.in转成Reader.
用Reader体系中的读取转换流,InputStreamReader
是否需要提高效率? 是! 用到Reader体系中的BufferedReader.
InputStream in = System.in;
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
目的:是不是纯文本?
是! Writer
设备:硬盘,txt文件.使用FileWriter
是否需要提高效率? 是! 用到Writer体系中的BufferedWriter
FileWriter fw = new FileWriter("xxx.txt");
BufferedWriter bw = new BufferedWriter(fw);
****************************************************************************
扩展:
扩展一下.想要把录入的数据按照指定的编码表(utf-8),将数据存储到文件中.
目的:是不是纯文本?
是! Writer
设备:硬盘,txt文件.使用FileWriter
但是FileWriter是使用默认的编码表
但是存储时,需要加入指定的编码表,而指定的表码表只有转换流可以指定
所以要使用的对象是写入转换流OutputStreamWriter
而该转换流对象要接受一个可以操作文件的字节输出流,FileOutputStream
FileOutputStream fos = new FileOutputStream("d.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
需要高效嘛? 需要!
BufferedWriter bw = new BufferedWriter(osw);
所以,记住! 转换流什么时候使用.字符和字节之间的桥梁,
通常涉及到字符编码转换时,需要用到转换流.
- import java.io.*;
- class TransStreamDemo2
- {
- public static void main(String[] args) throws IOException
- {
- BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
- FileOutputStream fos = new FileOutputStream("d.txt");
- OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
- BufferedWriter bw = new BufferedWriter(osw);
- String s = null;
- while ((s = br.readLine())!=null)
- {
- if(s.equals("over"))
- break;
- bw.write(s.toUpperCase());
- bw.newLine();
- bw.flush();
- }
- }
- }
- /*
- 练习:将一个文本数据打印在控制台上,按照三个明确来完成
- 源:InputStream Reader
- 是否为纯文本? 是! Reader
- 设备:硬盘,文本文件.使用FileReader
- 需要提高效率嘛? 需要!
- Reader体系中的BufferedReader
- FileReader fr = new FileReader("d.xxt");
- BufferedReader br = new BufferedReader(fr);
- 目的:OutputStream Writer
- 是否为纯文本? 是! Writer
- 设备:控制台,对应的对象是System.out,
- 用到Writer体系中的写入转换流OutputStreamWriter
- 需要高效嘛? 需要!
- Writer体系中的BufferedWriter
- OutputStreamWriter osw = new OutputStreamWriter(System.in);
- BufferWriter bw = new BufferedWriter(osw);
- */
- import java.io.*;
- class TransStreamTest
- {
- public static void main(String[] args) throws IOException
- {
- System.setOut(new PrintStream("dd.txt"));//重新设置目的out,也可以重新设置源,写法相同
- FileReader fr = new FileReader("d.txt");
- BufferedReader br = new BufferedReader(fr);
- OutputStreamWriter osw = new OutputStreamWriter(System.out);
- BufferedWriter bw = new BufferedWriter(osw);
- String s = null;
- while ((s=br.readLine())!=null)
- {
- bw.write(s);
- bw.newLine();
- bw.flush();
- }
- }
- }
前提是必须有调用System.in和System.out
System.setIn(InputStream in);改变标准输入
System.setOut(PrintStream ps);改变标准输出
异常信息序列化
- import java.io.*;
- import java.util.*;
- import java.text.*;
- class ExceptionInfo
- {
- public static void main(String[] args)
- {
- try
- {
- int[] arr = new int[2];
- System.out.println(arr[3]);
- }
- catch (Exception e)
- {
- try
- {
- Date d = new Date();
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- String s = sdf.format(d);
- PrintStream ps = new PrintStream("exception.log");
- ps.println(s);
- System.setOut(ps);
- }
- catch (IOException ie)
- {
- throw new RuntimeException("创建日志文件失败");
- }
- e.printStackTrace(System.out);
- }
- }
- }
工具log4j,有很多对象可以方便创建日志信息的建立.
File类
文件是一类事物,它有自己的名字、属性、大小、位置、后缀名等属性信息,那么根据面向对象的思想,就可以把它封装描述为一个类,
这个类就java.io包中的File类。File类是文件和目录路径名的抽象表示形式,它可以方便的对文件与文件夹的属性信息进行操作,也可以作为参数传递给流对象。
它弥补了了流的不足,比如流只能操作数据,而不能操作文件的属性与文件夹。
File类常见方法:
1.创建
boolean createNewFile();在指定位置创建文件,如该文件已存在,不创建,返回false.
和输出流不同,不管文件是否存在,都创建,如过有,就覆盖.
boolean mkdir();创建一级目录
boolean mkdirs();创建多级目录
2.删除
boolean delete();删除失败返回false
void deleteOnExit();在程序退出时删除指定文件.
3.判断
boolean canExecute();
boolean canRead();
boolean canWrite();
boolean exists();判断文件是否存在.
boolean isFile();判断是够是文件
boolean isDirectory();判断是否是目录
boolean isHidden();是否是隐藏文件
boolean isAbssolute():是否是绝对路径
4.获取信息
String getName();
String getPath();获取封装的路径,封装的什么就获取什么
String getParent();获取绝对路径中的父目录,如果获取的是相对路径,返回null
|--如果相对路径有上一层目录,那么该目录就是返回结果.
String getAbsolutePath();获取绝对路径
long lastModifiel();获取最后一次修改时间
long length();获取文件大小
- class FileDemo
- {
- public static void main(String[] args) throws IOException
- {
- method_4();
- }
- public static void method_4() throws IOException
- {
- File f1 = new File("c:\\Test.java");
- File f2 = new File("c:\\haha.java");
- System.out.println("rename:"+f1.renameTo(f2));//类似剪切,并修改文件名
- }
- public static void method_3() throws IOException
- {
- File f = new File("FileDemo.java");
- System.out.println("path:"+f.length());
- System.out.println("path:"+f.getAbsolutePath());
- }
- public static void method_2() throws IOException
- {
- File f = new File("file.txt");
- f.mkdir();
- //在判断文件对象是否是文件或者目录时,必须先判断该文件是否存在.
- //通过exists()判断.
- System.out.println("dir:"+f.isDirectory());
- System.out.println("dir:"+f.isFile());
- }
- public static void method_1() throws IOException
- {
- File f = new File("file.txt");
- System.out.println("create:"+f.createNewFile());//创建
- System.out.println("delete:"+f.delete());//删除
- System.out.println("execute:"+f.canExecute());//判断是否能执行
- System.out.println("exist:"+f.exists());//判断文件是否存在
- }
- //创建File对象
- public static void consMethod()
- {
- //将a.txt封装成file对象.可以将已有的和未出现的文件或者文件夹封装成对象.
- File f1 = new File("c:\\abc\\a.txt");
- //File.separator跨平台的分隔符类似newLine()
- File f2 = new File("c:"+File.separator+"abc","b.txt");
- File d = new File("c:\\abc");
- File f3 = new File(d,"c.txt");
- System.out.println("f1:"+f1);
- System.out.println("f2:"+f2);
- System.out.println("f3:"+f3);
- }
- }
File类中的高级方法
File[] listRoots();返回有效的盘符的对象
String[] list();返回当前目录下所有文件的名称
String[] list(filter);传递一个过滤器,按照过滤器返回文件名
File[] listFiles();返回当目录下所有文件的对象
- class FileDemo2
- {
- public static void main(String[] args)
- {
- listFileDemo();
- }
- public static void listFileDemo()
- {
- File dir = new File("d:\\learn\\day19");
- //获取当前目录下所有文件的对象.
- File[] files = dir.listFiles();
- for (File f : files )
- {
- System.out.println(f.getName()+"::"+f.length());
- }
- }
- public static void listDemo()
- {
- File f = new File("D:\\learn\\day19");
- //调用list方法的file对象封装的必须是一个目录,并且存在.
- String[] names = f.list(new MyFilenameFileter());//传进一个过滤器
- for(String s : names)
- {
- System.out.println(s);
- }
- }
- public static void listRootsDemo()
- {
- //列出电脑中有效的盘符
- File[] files = File.listRoots();
- for(File f : files)
- {
- System.out.println(f);
- }
- }
- }
- //定义一个过滤器
- class MyFilenameFileter implements FilenameFilter
- {
- public boolean accept(File dir,String name)
- {
- return name.endsWith(".class");
- }
- }
递归--列出目录下所有内容
- /*
- 列出指定目录下文件或者文件夹,包含子目录中的内容.
- 也就是列出指定目录下所有内容.
- 因为目录中还有目录,只要使用同一个列出目录功能的函数完成即可.
- 在列出过程中出现的还是目录的话,还可以再次调用本功能.
- 也就是函数自身调用自身.
- 这种表现形式,或者变成手法,称为递归.
- 递归要注意的:
- 1.一定要限定条件
- 2.要注意递归的次数,尽量避免内存溢出.
- */
- import java.io.*;
- class FileDemo3
- {
- public static void main(String[] args)
- {
- File f = new File("d:\\learn");
- //showDir(f);
- //toBin(6);
- System.out.println(getSum(10));
- }
- public static int getSum(int n)
- {
- if (n==1)
- return 1;
- else
- return n+getSum(n-1);
- }
- public static void toBin(int n)
- {
- if (n>0)
- {
- toBin(n/2);
- System.out.print(n%2);
- }
- }
- public static void showDir(File dir)
- {
- System.out.println(dir);
- File[] files = dir.listFiles();
- for(int x= 0; x<files.length; x++)
- {
- if(files[x].isDirectory())
- showDir(files[x]);
- else
- System.out.println(files[x]);
- }
- }
- }
列出目录下所有内容--带层级
- import java.io.*;
- class FileDemo4
- {
- public static void main(String[] args)
- {
- File f = new File("d:\\learn");
- showDir(f,0);
- }
- //定义一个层级方法
- public static String getLevel(int level)
- {
- StringBuilder sb = new StringBuilder();
- for (int x = 1; x<level; x++)
- {
- sb.append(" ");
- }
- return sb.toString();
- }
- public static void showDir(File dir,int level)
- {
- System.out.println(getLevel(level)+dir);
- level++;
- File[] files = dir.listFiles();
- for(int x= 0; x<files.length; x++)
- {
- if(files[x].isDirectory())
- showDir(files[x],level);
- else
- System.out.println(getLevel(level)+files[x]);
- }
- }
- }
删除带内容的目录
- import java.io.*;
- /*
- 删除原理:
- 在windows中,删除目录是从里面往外删除的.
- 既然是从里面往外删除,就需要用到递归.
- */
- class RemoveDir
- {
- public static void main(String[] args)
- {
- File dir = new File("d:\\新建文件夹");
- removeDir(dir);
- }
- public static void removeDir(File dir)
- {
- File [] files = dir.listFiles();
- for (int x=0; x<files.length; x++ )
- {
- if(files[x].isDirectory())
- removeDir(files[x]);
- else
- System.out.println(files[x]+"--file--"+files[x].delete());
- }
- System.out.println(dir+"==dir=="+dir.delete());
- }
- }
创建java文件列表
- /*
- 练习
- 将一个指定目录下的java文件的绝对路径,存储到一个文本文件中
- 建立一个java文件列表文件.
- 思路:
- 1.对指定的目录进行递归.
- 2.获取递归过程所有的java文件路径
- 3.将这些路径存储到集合中.
- 4.将集合中的数据写入到一个文件中
- */
- import java.io.*;
- import java.util.*;
- class JavaFileList
- {
- public static void main(String[] args) throws IOException
- {
- File dir = new File("d:\\learn");
- List<File> list = new ArrayList<File>();
- fileToList(dir,list);
- File file = new File(dir,"javalist.txt");
- writeTOFile(list,file);
- }
- public static void fileToList(File dir,List<File> list)
- {
- File[] files = dir.listFiles();
- for (File f : files )
- {
- if (f.isDirectory())
- fileToList(f,list);
- else
- {
- if(f.getName().endsWith(".java"));
- list.add(f);
- }
- }
- }
- public static void writeTOFile(List<File> list,File file)throws IOException
- {
- BufferedWriter bw = new BufferedWriter(new FileWriter(file));
- for (File f : list )
- {
- bw.write(f.getAbsolutePath());
- bw.newLine();
- bw.flush();
- }
- }
- }
Properties类
Properties是hashtable的子类.
也就是说它具备map集合的特点.而且它里面存储的键值对都是字符串.
是集合中和IO技术相集合的集合容器.
该对象的特点:可以用于键值对形式的配置文件
那么在加载数据时,需要数据有固定格式:键=值;
- import java.io.*;
- import java.util.*;
- import java.util.*;
- class PropertiesDemo
- {
- public static void main(String[] args) throws IOException
- {
- //setAndget();
- //method_1();
- loadDemo();
- }
- //Properties中提供了load方法来完成method_1
- public static void loadDemo() throws IOException
- {
- Properties prop = new Properties();
- FileInputStream fis = new FileInputStream("c:\\info.txt");
- prop.load(fis);
- prop.setProperty("zhangsan","88");
- FileOutputStream fos = new FileOutputStream("c:\\info.txt");
- prop.store(fos,"haha");
- prop.list(System.out);
- fis.close();
- fos.close();
- }
- //演示,如何将流中的数据存储到集合中.
- //想要将info.txt中键值数据存到集合中进行操作.
- /*
- 1.用一个流和info.txt文件关联.
- 2.读取一个数据,将该数据用"="进行切割
- 3.等号左面作为键,右边作为值.存入到Properties集合中即可
- */
- public static void method_1() throws IOException
- {
- BufferedReader br = new BufferedReader(new FileReader("c:\\info.txt"));
- Properties prop = new Properties();
- String line = null;
- while ((line=br.readLine())!=null)
- {
- String[] arr = line.split("=");
- prop.setProperty(arr[0],arr[1]);
- }
- br.close();
- System.out.println(prop);
- }
- //设置和获取元素.
- public static void setAndget()
- {
- Properties prop = new Properties();
- prop.setProperty("zhangsan","39");
- prop.setProperty("lisi","47");
- System.out.println(prop);
- System.out.println(prop.getProperty("lisi"));
- prop.setProperty("lisi",90+"");
- Set<String> names = prop.stringPropertyNames();
- for (String s : names)
- {
- System.out.println(s+":"+prop.getProperty(s));
- }
- }
- }
Properties练习--限定程序执行次数
- import java.io.*;
- import java.util.*;
- /*
- 用于记录应用程序运行次数.
- 如果使用次数已到,那么给出注册提示.
- 很容易想到的是:计数器.
- 开始该计数器定义在程序中,随着程序的运行而存在的内存中,并进行字增
- 可是随着该应用程序的推出,该计数器也在内存中消失了.
- 下一次在启动该程序,又重新开始从0计数
- 这样就不是我们想要的.
- 程序即使结束,该计数器的值也存在.
- 下次程序启动会先家在该计数器的值并加1后在重新存储起来.
- 所以要建立一个配置文件,用于记录该软件的使用次数.
- 该配置文件使用键值对的形式.
- 这样便于阅读数据,并操作数据.
- 键值对数据是map集合
- 数据是以文件形式存储,用到IO技术.
- 那么map-->Properties + IO
- 配置文件可以实现应用程序数据的共享.
- 1.创建有给读取流关联配置文件
- */
- class RunCount
- {
- public static void main(String[] args) throws IOException
- {
- File file = new File("c:\\property.ini");
- if(!file.exists())
- file.createNewFile();
- Properties prop = new Properties();
- FileInputStream fr = new FileInputStream(file);
- prop.load(fr);
- String value = prop.getProperty("count");
- if(value==null)
- prop.put("count","1");
- else
- {
- System.out.println(value);
- int count = Integer.parseInt(value);
- if(count>=5)
- {
- System.out.println("您的使用次数已到");
- return;
- }
- count++;
- prop.setProperty("count",count+"");
- }
- FileOutputStream fw = new FileOutputStream(file);//输出流会覆盖原文件
- prop.store(fw,"test");
- fr.close();
- fw.close();
- }
- }
打印流PrintWriter和PrintStream
打印流:
该流提供了打印方法,可以将各种数据类型的数据都原样打印.
字节打印流
PrintStream
构造函数可以接收的参数类型.
1.File对象.File
2.字符串路径.String
3.字节输出流.OutputStream
字符打印流
PrintWriter(常用)
构造函数可以接收的参数类型
1.File对象.File
2.字符串路径.String
3.字节输出流.OutputStream
4.字符输出流.Writer
- import java.io.*;
- class PrintStreamDemo
- {
- public static void main(String[] args) throws IOException
- {
- BufferedReader br =
- new BufferedReader(new InputStreamReader(System.in));
- PrintWriter pw =
- new PrintWriter(new FileWriter("c:\\demo.txt"),true);//可实现自动刷新
- String line = null;
- while ((line=br.readLine())!=null)
- {
- if(line.equals("over"))
- break;
- pw.println(line.toUpperCase());
- }
- br.close();
- pw.close();
- }
- }
序列合并流
SequenceInputStream是能对多个流进行合并成一个读取流,它在构造时需要传入Enumeration,而这个只用Vector中有,所以这个多个读取流要加入Vector集合中。注意:它只是对读取流进行合并。
它使用步骤:
1.创建Vector<InputStream>
2.将要合并的InputStream加入Vector
3.通过Vector获取Enumeration
4.创建SequenceInputStream,将Enumeration作为参数传入。
- import java.io.*;
- import java.util.*;
- class SequenceDemo
- {
- public static void main(String[] args) throws IOException
- {
- Vector<FileInputStream> v = new Vector<FileInputStream>();
- v.add(new FileInputStream("c:\\1.txt"));
- v.add(new FileInputStream("c:\\2.txt"));
- v.add(new FileInputStream("c:\\3.txt"));
- Enumeration<FileInputStream> e = v.elements();
- SequenceInputStream sis = new SequenceInputStream(e);
- FileOutputStream fos = new FileOutputStream("c:\\4.txt");
- byte[] bys = new byte[1024];
- int len = 0;
- while ((len = sis.read(bys))!=-1)
- {
- fos.write(bys,0,len);
- }
- fos.close();
- sis.close();
- }
- }
文件的切割和合并
- import java.io.*;
- import java.util.*;
- class SplitFile
- {
- public static void main(String[] args) throws IOException
- {
- //splitFile();
- merge();
- }
- //合并
- public static void merge() throws IOException
- {
- ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
- al.add(new FileInputStream("c:\\2.part"));
- al.add(new FileInputStream("c:\\3.part"));
- al.add(new FileInputStream("c:\\4.part"));
- final Iterator<FileInputStream> it = al.iterator();
- 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:\\0.jpg");
- byte[] bys = new byte[1024];
- int len = 0;
- while ((len=sis.read(bys))!=-1)
- {
- fos.write(bys,0,len);
- }
- sis.close();
- fos.close();
- }
- //切割
- public static void splitFile() throws IOException
- {
- FileInputStream fis = new FileInputStream("c:\\1.jpg");
- FileOutputStream fos = null;
- byte[] bys = new byte[1024*50];
- int len = 0;
- int count = 2;
- while ((len=fis.read(bys))!=-1)
- {
- fos = new FileOutputStream("c:\\"+(count++)+".part");
- fos.write(bys,0,len);
- fos.close();
- }
- fis.close();
- }
- }
对象序列化:
被序列化的类必须实现Serializable接口,来获得ID.
也可以自己设置ID,例如:static final long serialVersionUID = 42L
static静态成员是不能被序列化的.
transient修饰的成员也是不能被序列化的
- import java.io.*;
- class ObjectStreamDemo
- {
- public static void main(String[] args) throws Exception
- {
- //writeObj();
- readObj();
- }
- public static void readObj() throws Exception
- {
- ObjectInputStream ois =
- new ObjectInputStream(new FileInputStream("obj.txt"));
- Person p = (Person)ois.readObject();
- System.out.println(p);
- ois.close();
- }
- public static void writeObj() throws IOException
- {
- ObjectOutputStream oos =
- new ObjectOutputStream(new FileOutputStream("obj.txt"));
- oos.writeObject(new Person("lisi",97));
- oos.close();
- }
- }
- class Person implements Serializable
- {
- //给类设置ID,
- public static final long serialVersionUID = 42L;
- String name;
- int age;
- Person(String name,int age)
- {
- this.name = name;
- this.age = age;
- }
- public String toString()
- {
- return name+":"+age;
- }
- }
管道流:输入输出可以直接连接,结合线程使用.
|--管道输入流:PipedInputStream
|--管道输出流:PipedOutpuStream
- import java.io.*;
- class Read implements Runnable
- {
- private PipedInputStream in;
- Read(PipedInputStream in)
- {
- this.in=in;
- }
- public void run()
- {
- try
- {
- byte[] bys = new byte[1024];
- System.out.println("读取前..没有数据..进入阻塞状态");
- int len = in.read(bys);
- System.out.println("读取到数据..阻塞状态结束");
- String s = new String(bys,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("PipedOutputStream is coming".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);
- new Thread(new Read(in)).start();
- new Thread(new Write(out)).start();
- }
- }
随机读写RandomAccessFile
该类不算是IO体系中的子类.
而是直接继承自Object.
但是它是IO包的成员,因为它具备读和写功能.
内部封装了一个byte数组,而且通过指针对数组的元素进行操作.
可以通过getFilePointer获取指针位置.
同时可以通过seek改变指针的位置
其实完成读写的原理就是内部封装了字节输入流和输出流.
通过构造函数可以看出,该类只能操作文件.
而且操作的文件还需要指定权限.
如果模式为只读 r,则不会创建文件,回去读取一个已存在的文件,如果该文件不存在,会出现异常
如果模式为 rw,操作的文件不存在,会自动创建,如果存在则不会覆盖
- class RandomAccessFileDemo
- {
- public static void main(String[] args) throws IOException
- {
- //writeFile();
- //readFile();
- writeFile_2();
- }
- public static void readFile() throws IOException
- {
- RandomAccessFile raf = new RandomAccessFile("ran.txt","r");
- //用seek()方法把指针调到8位置上
- //raf.seek(8);
- //跳过指定的字节数,skipBytes()只能向前,不能后退
- raf.skipBytes(8);
- byte[] bys = new byte[4];
- raf.read(bys);
- String name = new String(bys);
- //方法readInt()取一个32位的整数
- int age = raf.readInt();
- System.out.println("name="+name);
- System.out.println("age="+age);
- }
- public static void writeFile_2()throws IOException
- {
- RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
- raf.seek(16);//利用seek方法,可以实现指定位置数据的添加和修改
- raf.write("周七".getBytes());
- raf.writeInt(103);
- }
- 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();
- }
- }
操作[基本数据类型]的流对象
可以用于操作基本数据类型的数据的流对象,按照输入输出分为DataInputStream和DataOutputStream。这两个类提供了对8种基本数据类型的写入和读取的方法。
此外,它们有对应的wirtUTF-8(String str)和readUTF-8()方法,来支持按照UTF-8修改版编码(与UTF-8稍有不同)来写入和读取字符串。
- <pre name="code" class="java">import java.io.*;
- class DataStreamDemo
- {
- public static void main(String[] args) throws IOException
- {
- //writeData();
- //readData();
- //writeUtf();
- readUtf();
- }
- public static void readUtf() throws IOException
- {
- DataInputStream dis =
- new DataInputStream(new FileInputStream("utfdat.txt"));
- System.out.println(dis.readUTF());
- dis.close();
- }
- public static void writeUtf() throws IOException
- {
- DataOutputStream dos =
- new DataOutputStream(new FileOutputStream("utfdat.txt"));
- dos.writeUTF("你好");
- dos.close();
- }
- public static void readData() throws IOException
- {
- DataInputStream dis =
- new DataInputStream(new FileInputStream("data.txt"));
- System.out.println("b="+dis.readInt());
- boolean b = dis.readBoolean();
- double d = dis.readDouble();
- System.out.println("b="+b+"\r\n"+"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();
- }
- }
- </pre>
- <pre></pre>
- <p></p>
- <pre></pre>
- <div>
- <p>操<span style="font-family:宋体">作字节数组</span><span style="font-family:宋体">的流对象</span></p>
- <p>按输入输出分为两个类</p>
- <p>ByteArrayInputStream<span style="font-family:宋体">:在构造的时候,需要接受数据源,而且这个数据源是一个字符数组。</span></p>
- <p>ByteArrayOutputStream<span style="font-family:宋体">:在构造的时候,不需要定义目的地,因为该对象内部已经封装了可变长度的字节数组,它就是目的地;它对外提供了</span><span style="font-family:Times New Roman">toString()</span><span style="font-family:宋体">方法,可以把内部封装的字节数组按照默认的字符集或指定的字符集以字符串的形式返回。</span></p>
- <p>注意:</p>
- <p>1.<span style="font-family:宋体">因为这两个流对象都操作的是数组,并没有使用系统资源,所以,不用进行</span><span style="font-family:Times New Roman">close</span><span style="font-family:宋体">关闭,即使你关闭了,它的其他方法还可以使用,而不会抛出</span><span style="font-family:Times New Roman">IOException</span><span style="font-family:宋体">。</span></p>
- <p>2.<span style="font-family:宋体">使用这对对象操作时,它的源和目的都是内存。</span></p>
- <p>用途:这两个对象是在用流的思想来操作数组,当我们需要把一个文件中的数据加入内存中的数组时,就可以考虑用这个两个对象。此外,它还有<span style="font-family:Times New Roman">writeTo</span><span style="font-family:宋体">(</span><span style="font-family:Times New Roman">OutputStream os</span><span style="font-family:宋体">)可以把</span><span style="font-family:Times New Roman">ByteArrayOutputStream</span><span style="font-family:宋体">对象内部定义的缓冲区内容,一次性写入</span><span style="font-family:Times New Roman">os</span><span style="font-family:宋体">中。</span></p>
- <p>操作字符数组、字符串的流对象类型与之相似,可以参与它们的使用方法。</p>
- <p></p>
- <pre name="code" class="java">/*
- 用于操作字节数组的流对象,
- ByteArrayInputStream:在构造的时候,需要接收数据源,而且数据源是一个字节数组.
- ByteArrayOutputStream:在构造的时候,不用定义目的,因为该对象中已经
- 封装了可变长度的字节数组.
- (CharArrayReader CharArrayWriter)功能和用法基本相同
- (StringReader StringWiter)功能和用法基本相同
- 因为这两个流对象操作的都是数组,并没有使用系统资源.
- 所以,不用进行close关闭资源.
- 在流操作规律讲解时:
- 源设备:
- 键盘 System.in, 硬盘 FileInputStream, 内存 ArrayInputStream
- 目的设备
- 控制台 System.out, 硬盘 FileOutputStream, 内存 ArrayOutputStream
- 用流的读写思想操作数组.
- */
- import java.io.*;
- class ByteArrayStream
- {
- public static void main(String[] args) throws IOException
- {
- //数据源.
- ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEF".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"));//只有writeTO()方法需要抛出异常
- }
- }
- dos.writeBoolean(true);
- dos.writeDouble(9887.543);
- dos.close();
- }
- }</pre><br>
- <p></p>
- <div>
- <p>编码问题 <br>
- </p>
- <p>字符流的出现是为了方便操作字符数据,其方法操作的原因是因为内部加入了编码表。<span style="font-family:Times New Roman">Java</span><span style="font-family:宋体">中能够实现字节根据指定编码表转成字符的,有四个类:</span><span style="font-family:Times New Roman">InputStreamReader</span><span style="font-family:宋体">和</span><span style="font-family:Times New Roman">OutputStreamWriter</span><span style="font-family:宋体">,</span><span style="font-family:Times New Roman">PrintStream</span><span style="font-family:宋体">和</span><span style="font-family:Times New Roman">PrintWriter</span><span style="font-family:宋体">。它们都能够加构造时,指定编码表;但后两个是打印流,只能用于打印,使用有局限,所以相对而言,还是前两个转换流使用多一些。</span></p>
- <p>编码表的由来</p>
- <p>计算机只能识别二进制数据,早期是电信号。为了应用计算机方便,让它可以识别各个国家的文字,就将各个国家的文字用数字来表示,并将文字与二进制数字一一对应,形成了一张表,这个表就是编码表。</p>
- <p>常见的编码表</p>
- <p>地域码表</p>
- <p>1.ASCII<span style="font-family:宋体">:美国码表,息交换码,用一个字节的</span><span style="font-family:Times New Roman">7</span><span style="font-family:宋体">位表示。</span></p>
- <p>2.ISO8859-1<span style="font-family:宋体">:欧洲码表,拉丁码表,用一个字节的</span><span style="font-family:Times New Roman">8</span><span style="font-family:宋体">位表示,最高位</span><span style="font-family:Times New Roman">1</span></p>
- <p>3.GB2312<span style="font-family:宋体">:中国中文编码表,它用两个字节表示,为兼容</span><span style="font-family:Times New Roman">ASCII</span><span style="font-family:宋体">,它的两个字节的高位都是</span><span style="font-family:Times New Roman">1</span><span style="font-family:宋体">,也即是两个负数;但与</span><span style="font-family:Times New Roman">ISO8859-1</span><span style="font-family:宋体">冲突。大概有六七千个字。</span></p>
- <p>4.GBK<span style="font-family:宋体">:中国的中文编码表的升级版,扩容到</span><span style="font-family:Times New Roman">2</span><span style="font-family:宋体">万多字。</span></p>
- <p>通用码表:</p>
- <p>1.Unicode<span style="font-family:宋体">:国际标准码,融合多种语言文字。所有的文字都用两个字节表示,</span><span style="font-family:Times New Roman">Java</span><span style="font-family:宋体">默认使用的就是</span><span style="font-family:Times New Roman">Unicode</span><span style="font-family:宋体">。</span></p>
- <p>2.UTF-8<span style="font-family:宋体">:</span><span style="font-family:Times New Roman">UnicodeTransform Format -8</span><span style="font-family:宋体">。</span><span style="font-family:Times New Roman">Unicode</span><span style="font-family:宋体">码把用一个字节能装下的文字,也用两个字节表示,有些浪费空间,对之进行优化的结果就是</span><span style="font-family:Times New Roman">UTF-8</span><span style="font-family:宋体">。</span></p>
- <p>UTF-8<span style="font-family:宋体">编码表,一个文字最用一个字节表示,最多用</span><span style="font-family:Times New Roman">3</span><span style="font-family:宋体">个字节表示,并且每个字节开始都有标识头,所以很容易于其他编码表区分出来。</span></p>
- </div>
- <pre name="code" class="java">//转换流的字符编码使用
- import java.io.*;
- class EncodeStream
- {
- public static void main(String[] args) throws IOException
- {
- writeText();
- readText();
- }
- public static void readText() throws IOException
- {
- InputStreamReader isr =
- new InputStreamReader(new FileInputStream("gbk.txt"),"UTF-8");
- char[] buf = new char[10];
- int len = isr.read(buf);
- String s = new String(buf,0,len);
- System.out.println(s);
- isr.close();
- }
- public static void writeText() throws IOException
- {
- OutputStreamWriter osw =
- new OutputStreamWriter(new FileOutputStream("gbk.txt","UTF-8"));
- osw.write("你好");
- osw.close();
- }
- }</pre><br>
- <div>
- <p>编码问题的产生与解决</p>
- <p>从上边的那些编码表可以看出,<span style="font-family:Times New Roman">GBK</span><span style="font-family:宋体">和</span><span style="font-family:Times New Roman">Unicode</span><span style="font-family:宋体">都能识别中文,那么当一台电脑使用</span><span style="font-family:Times New Roman">GBK</span><span style="font-family:宋体">,而另一台电脑使用</span><span style="font-family:Times New Roman">Unicode</span><span style="font-family:宋体">时,虽然在各自的电脑上都能识别中文,但他们其中一方向另一方发送中文文字时,另一方却不能识别,出现了乱码。这是因为</span><span style="font-family:Times New Roman">GBK</span><span style="font-family:宋体">和</span><span style="font-family:Times New Roman">Unicode</span><span style="font-family:宋体">虽然都能识别中文,但对同一个中文文字,他们在两个编码表对应的编码值不同。这时,在解读别人传来的中文数据时,就需要指定解析中文使用的编码表了。</span></p>
- <p>而转换流就能指定编码表,它的应用可以分为:</p>
- <p>1. <span style="font-family:宋体">可以将字符以指定的编码格式存储。</span></p>
- <p>2. <span style="font-family:宋体">可以对文本数据以指定的编码格式进行解读。</span></p>
- <p>它们指定编码表的动作是由构造函数完成的。</p>
- <p>编码:字符串变成字节数组,<span style="font-family:Times New Roman">String to byte[]</span><span style="font-family:宋体">,使用</span><span style="font-family:Times New Roman">str.getBytes(charsetName)</span><span style="font-family:宋体">;</span></p>
- <p>解码:字节数组变成字符串,<span style="font-family:Times New Roman">byte[] to String</span><span style="font-family:宋体">,使用</span><span style="font-family:Times New Roman">new String(byte[] b, charsetName);</span></p>
- <p>编码编错:是指你对一个文字进行编码时,使用了不识别该文字的编码表,比如你编码一个汉字,却使用了<span style="font-family:Times New Roman">ISO8859-1</span><span style="font-family:宋体">这个拉丁码表,</span><span style="font-family:Times New Roman">ISO8859-1</span><span style="font-family:宋体">根本就不识别汉字。编码编错时,你用任何方式对编码后的数据进行处理,都不可能再拿到这个汉字了。</span></p>
- <p>解码解错:是指你对一个文字进行编码事,使用了正确的码表,编码正确,但在解码时使用了错误的码表,那么你还有可能拿到这个文字。这分为两种情况:</p>
- <p>第一种情况:你使用的是<span style="font-family:Times New Roman">GBK</span><span style="font-family:宋体">编码,解码时用的是</span><span style="font-family:Times New Roman">ISO8859-1</span><span style="font-family:宋体">,因为</span><span style="font-family:Times New Roman">GBK</span><span style="font-family:宋体">编译一个汉字,使用两个字节,</span><span style="font-family:Times New Roman">ISO8859-1</span><span style="font-family:宋体">解码时是一个字节一个字节读取,虽然解码出现了乱码,但是这个汉字的二进制数据没有变化,那么你可以通过再次编译获取其原来的二进制数据,然后再次使用</span><span style="font-family:Times New Roman">GBK</span><span style="font-family:宋体">编码,解码成功。</span></p>
- <p>第二种情况:你使用的是<span style="font-family:Times New Roman">GBK</span><span style="font-family:宋体">编码,解码时用的却是</span><span style="font-family:Times New Roman">UTF-8</span><span style="font-family:宋体">,因为这两个码表都识别汉字,那么你再次使用</span><span style="font-family:Times New Roman">UTF-8</span><span style="font-family:宋体">编码时,就有可能把一个汉字的</span><span style="font-family:Times New Roman">2</span><span style="font-family:宋体">个字节,变成</span><span style="font-family:Times New Roman">3</span><span style="font-family:宋体">个,这时再用</span><span style="font-family:Times New Roman">GBK</span><span style="font-family:宋体">解码时,得到的仍然是乱码,解码仍然失败。</span></p>
- </div>
- <pre name="code" class="java">/*
- 编码:字符串变字节数组;
- 解码:字节数组变字符串;
- String --> byte[]: str.getBytes(码表);
- byte[] --> String: new String(byte[],码表);
- 注意:GBK和UTF-8是不能编码解码的.
- */
- import java.util.*;
- class EncodeDemo
- {
- public static void main(String[] args) throws Exception
- {
- String s = "你好";
- byte[] bys = s.getBytes("GBK");
- System.out.println(Arrays.toString(bys));
- String s1 = new String(bys,"iso8859-1");
- System.out.println(s1);
- byte[] buf = s1.getBytes("iso8859-1");
- System.out.println(Arrays.toString(buf));
- String s2 = new String(buf,"GBK");
- System.out.println(s2);
- }
- }</pre><br>
- <p></p>
- <p>“联通”的编码问题</p>
- <p>问题描述:打开记事本仅写入“联通”两个汉字,关闭后,再次打开会出现乱码。</p>
- <p></p>
- <pre name="code" class="java">class EncodeDemo2
- {
- public static void main(String[] args) throws Exception
- {
- String s = "联通";
- byte [] by = s.getBytes("GBK");
- for(byte b:by)
- {
- System.out.println(Integer.toBinaryString(b&255));
- /*
- * “联通”编码问题的原因:
- //boBinaryString(int)它接受的是int,byte类型的b参与运算时会类型提升为int,我们需要的是提升后的低8位,所以&255
- 对联通的结果进行GBK编码时,其二进制码为:
- 11000001
- 10101010
- 11001101
- 10101000
- 编码的结果符合UTF-8的格式,所以再次打开记事本时,它会把它按照UTF-8的格式进行解码,结果就是两个乱码。
- */
- }
- }
- }</pre><br>
- IO流练习
- <p></p>
- <p></p>
- <pre name="code" class="java">/*
- 有五个学生,每个学生有3门课的成绩,
- 从键盘输入以上数据(包括姓名,三门课成绩)
- 输入的格式:如:zhang,30,40,60,并计算出总成绩.
- 并把学生的信息按照计算出的总分高低顺序存放在文件"stud.txt"中
- 1.描述学生对象.
- 2.定义一个操作学生对象的工具类.
- 思想:
- 1.通过键盘录入一行数据,并将该行中的信息取出封装成学生对象.
- 2.因为学生有很多,那么需要存储,使用到集合,因为要对学生总分进行排序.
- 所以可以使用TreeSet.
- 3.将集合的信息写入到一个文件中.
- */
- import java.io.*;
- import java.util.*;
- class Student implements Comparable<Student>
- {
- private String name;
- private int ma,ch,en
- private int sum;
- Student(String name,int ma,int ch,int en)
- {
- this.name = name;
- this.ma = ma;
- this.ch = ch;
- this.en = en;
- sum = ma+ch+en;
- }
- public int getSum()
- {
- return sum;
- }
- public boolean equals(Student s)
- {
- if(!(s instanceof Student))
- throw new ClassCastException("类型不匹配");
- return this.name.equals(s.name) && this.sum == s.sum;
- }
- public int hashCode()
- {
- return this.name.hashCode()+sum*5;
- }
- public int compareTo(Student s)
- {
- int num = new Integer(this.sum).compareTo(new Integer(s.sum));
- if(num==0)
- return this.name.compareTo(s.name);
- return num;
- }
- public String toString()
- {
- return "Student["+name+","+ma+","+ch+","+en+"]";
- }
- }
- class StudentTools
- {
- public static TreeSet<Student> readInfo() throws IOException
- {
- TreeSet<Student> ts = new TreeSet<Student>();
- BufferedReader br =
- new BufferedReader(new InputStreamReader(System.in));
- String line = null;
- while ((line=br.readLine())!=null)
- {
- if("over".equals(line))
- break;
- String[] str = line.split(",");
- Student s = new Student(str[0],Integer.parseInt(str[1]),
- Integer.parseInt(str[2]),
- Integer.parseInt(str[3]));
- ts.add(s);
- }
- br.close();
- return ts;
- }
- public static void writeInfo(TreeSet<Student> ts) throws IOException
- {
- BufferedWriter bw = new BufferedWriter(new FileWriter("stu.txt"));
- for (Student s : ts )
- {
- bw.write("学生信息:"+s.toString());
- bw.write("总分:"+s.getSum());
- bw.newLine();
- bw.flush();
- }
- bw.close();
- }
- }
- class StudentInfoTest
- {
- public static void main(String[] args) throws IOException
- {
- TreeSet<Student> ts = StudentTools.readInfo();
- StudentTools.writeInfo(ts);
- }
- }</pre><br>
- <br>
- <p></p>
- <br>
- </div>
- <p></p>
- <pre></pre>