------- android培训、java培训、期待与您交流! ----------
IO流
一、概述IO(Input Output)流
IO流用来处理设备之间的数据传输。Java对数据的操作是通过流的方式,Java用于操作流的对象都在IO包中。
流按操作数据分为两种:字节流与字符流。
流按流向分为:输入流,输出流。
流常用基本类
字节流的抽象基类:
InputStream, OutputStream
字符流的抽象基类:
Reader, Writer
注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。
如:InputStream的子类FileInputStream.
如:Reader的子类FileReader.
既然IO流失用于操作数据的,那么数据的最常见体现形式是:文件。
示例代码:import java.io.*;
class FileWriterDemo
{
public static void main(String[] args) throws IOException
{
//创建一个FileWriter对象。该对象一被初始化就必须要明确被操作的文件。
//而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。
//其实该步就是在明确数据要存放的目的地。
FileWriter fw=new FileWriter("demo.txt"); //这只是创建。
//调用write方法,将字符串写入到流中。
fw.write("abcde");
//刷新流对象中的缓冲中的数据。
//将数据刷到目的地中。
//fw.flush();
//关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据。
//将数据刷到目的地中。
//和flush区别:flush刷新后,流可以继续使用。close刷新后,会将流关闭。
fw.close();
}
}
二、IO流(BufferedWriter)
字符流的缓冲区
缓冲区的出现是为了提高流的操作效率而出现的。所以在创建缓冲区之前,必须要先有流对象。
对应类
BufferedWriter
BufferedReader
缓冲区要结合流才可以使用。在流的基础上对流的功能进行了增强。
该缓冲区中提供了一个跨平台的换行符。newLine();
示例代码:import java.io.*;
class BufferedWriterDemo
{
public static void main(String[] args) throws IOException
{
//创建一个字符写入流对象。
FileWriter fw=new FileWriter("buf.txt");
//为了提高字符写入流效率,加入了缓冲技术。
//只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
BufferedWriter bufw=new BufferedWriter(fw);
for (int x=1; x<5; x++)
{
bufw.write("abcd"+x);
bufw.newLine(); //换行
bufw.flush();
}
//记住:只要用到缓冲区,就要记得刷新。
//bufw.flush();
//其实关闭缓冲区,就是在关闭缓冲区中的流对象。
bufw.close();
}
}
三、IO流(BufferedReader)
字符读取流缓冲区
该缓冲区提供了一个一次读一行的方法 readLine(),方便于对文本数据的获取。当返回null时,表示读到文件末尾。
readLine方法返回的时候只返回回车符之前的数据内容,并不返回回车符。
readLine方法的原理:
无论是读一行,或者读取多个字符。其实最终都是在硬盘上一个一个读取。所以最终使用的还是read方法一次读一个的方法。
示例代码:
import java.io.*;
class BufferedReaderDemo
{
public static void main(String[] args) throws IOException
{
//创建一个读取流对象和文件相关联。
FileReader fr=new FileReader("buf.txt");
//为了提高效率,加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。
BufferedReader bufr=new BufferedReader(fr);
//String s1=bufr.readLine();
//System.out.println("s1:"+s1);
String line=null;
while ((line=bufr.readLine())!=null)
{
System.out.println(line);
}
bufr.close();
}
}
四、IO流(装饰设计模式)
装饰设计模式:
当想要对已有的对象进行功能增强时,可以定义类。将已有对象传入,基于已有对象的功能,并提供加强功能。那么自定义的该类就称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象。并基于被装饰的对象的功能,提供更强的功能。
示例代码:
class Person
{
public void chifan()
{
System.out.println("吃饭!");
}
}
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();
}
}
五、IO流(装饰和继承的区别)
MyReader //专门用于读取数据的类。
|--MyTextReader
|--MyBufferTextReader
|--MyMediaReader
|--MyBufferMediaReader
|--MyDataReader
|--MyBufferDataReader
class MyBufferReader
{
MyBufferReader(MyTextReader text)
{}
MyBufferReader(MyMediaReader media)
{}
}
上面这个类扩展性很差。
找到其参数的共同类型,通过多态的形式,可以提高扩展性。
class MyBufferReader extends MyReader
{
private MyReader r;
MyBufferReader(MyReader r)
{}
}
MyReader //专门用于读取数据的类。
|--MyTextReader
|--MyMediaReader
|--MyDataReader
|--MyBufferReader
装饰模式比继承要灵活,避免了继承体系臃肿。而且降低了类与类之间的关系。
装饰类因为增强已有对象,具备的功能和已有的对象是相同的,只不过提供了更强功能。所以装饰类和被装饰类通常是都属于一个体系中的。
读取键盘录入。
System.out:对应的是标准输出设备,控制台。
System.in:对应的是标准输入设备,键盘。
六、读取转换流和写入转换流
字符流:
FileReader
FileWriter
BufferedReader
BufferedWriter
字节流:
FileInputStream
FileOutputStream
BufferedInputStream
BufferedOutputStream
通过刚才的键盘录入一行数据并打印其大写,发现其实就是读一行数据的原理。也就是readLine方法。
能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?
readLine方法时字符流BufferedReader类中的方法。
而键盘录入的read方法是字节流InputStream的方法。
那么能不能将字节流转成字符流再使用字符流缓冲区的readLine方法呢?
示例代码:
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 bufr=new BufferedReader(isr);
*/
//键盘录入的最常见写法。上面的三句话转成一句话。(背)
BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
/*
String line=null;
while ((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
System.out.println(line.toUpperCase());
}
bufr.close();
*/
//===================================================
/*
OutputStream out=System.out;
OutputStreamWriter osw=new OutputStreamWriter(out);
BufferedWriter bufw=new BufferedWriter(osw);
*/
//上面的三句话转成一句话。
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.newLine();
bufw.flush();
}
bufr.close();
}
}
七、IO流(流操作规律)
1,
源:键盘录入。
目的:控制台。
2,需求:想把键盘录入的数据存储到一个文件中。
源:键盘。
目的:文件。
3,需求:想要将一个文件的数据打印在控制台上。
源:文件。
目的:控制台。
流操作的基本规律:
最痛苦的就是流对象有很多,不知道该用哪一个?
通过三个明确来完成。
1,明确源和目的。
源:输入流。 InputStream Reader
目的:输出流。 OutputStream Writer
2,操作的数据是否是纯文本。
是:字符流。
不是:字节流。
3,当体系明确后,再明确要使用哪个具体的对象。
通过设备来进行区分:
源设备:内存,硬盘,键盘。
目的设备:内存,硬盘,控制台。
1,将一个文本文件中数据存储到另一个文件中。复制文件。
源:因为是源,所以使用读取流。InputStream Reader.
是不是操作文本文件。
是!这时就可以选择Reader。这样体系就明确了。
接下来明确要使用该体系中的哪个对象。
明确设备:硬盘。上一个文件。
Reader体系中可以操作文件的对象是 FileReader.
是否需要提高效率:是!加入Reader体系中的缓冲区 BufferedReader.
FileReader fr=new FileReader("a.txt");
BufferedReader bufr=new BufferedReader(fr);
目的:OutputStream Writer
是否是纯文本。
是!Writer
设备:硬盘,一个文件。
Writer体系中可以操作文件的对象 FileWriter。
是否需要提高效率:是!加入Writer体系中的缓冲区 BufferedWriter.
FileWriter fw=new FileWriter("b.txt");
BufferedWriter bufw=new BufferedWriter(fw);
练习:将一个图片文件中的数据存储到另一个文件中。复制文件。要按照以上格式自己完成三个明确。
---------------------------------------------------------
2,需求:将键盘录入的数据保存到一个文件中。
这个需求中有源和目的都存在。
那么分别分析
源:InputStream Reader
是不是纯文本?是!Reader
设备:键盘。对应的对象是System.in。
不是选择Reader吗?System.in对应的不是字节流吗?
为了操作键盘的文本数据方便。转成字符流按照字符串操作是最方便的。所以既然明确了Reader,那么就将System.in转换成Reader。用到了Reader体系中的转换流,InputStreamReader.
InputStreamReader isr=new InputStreamReader(System.in);
需要提高效率吗?需要!BufferedReader
BufferedReader bufr=new BufferedReader(isr);
目的:OutputStream Writer
是否是纯文本?是!Writer。
设备:硬盘。一个文件。使用FileWriter.
FileWriter fw=new FileWriter("c.txt");
需要提高效率吗?需要。
BufferedWriter bufw=new BufferedWriter(fw);
*****************************************************
扩展一下,想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中。
目的:OutputStream Writer
是否是纯文本?是!Writer。
设备:硬盘。一个文件。使用FileWriter.
但是FileWriter是使用的默认编码表。GBK
但是存储时,需要加入指定编码表utf-8。而指定的编码表只有转换流可以指定。所以要使用的对象是OutputStreamWriter.
而该转换流对象要接收一个字节输出流。而且还可以操作文件的字节输出流。FileOutputStream.
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");
需要高效吗?需要。
BufferedWriter bufw=new BufferedWriter(osw);
所以,记住:转换流什么时候使用?字符和字节之间的桥梁,通常涉及到字符编码转换时,需要用到转换流。
练习:将一个文本数据打印在控制台上。要按照以上格式自己完成三个明确。
示例代码:
import java.io.*;
class TransStreamDemo2
{
public static void main(String[] args) throws IOException
{
/*
//键盘录入的最常见写法。(背)
//BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
//BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(System.out));
//2,需求:想把键盘录入的数据存储到一个文件中。
BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("out.txt")));
//3,需求:想要将一个文件的数据打印在控制台上。
BufferedReader bufr=new BufferedReader(new InputStreamReader(new FileInputStream("CopyPic.java")));
BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(System.out));
*/
//扩展一下,想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中。
BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("d2.txt"),"UTF-8"));
String line=null;
while ((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
bufr.close();
}
}
八、File概述
File类
用来将文件或者文件夹封装成对象。方便对文件与文件夹的属性信息进行操作。File对象可以作为参数传递给流的构造函数。
了解File类中的常用方法。
File类常见方法:
1,创建。
boolean createNewFile();在指定位置创建文件,如果该文件已经存在,则不创建,返回false。和输出流不一样,输出流对象一建立就创建文件,而且文件已经存在,会覆盖。
boolean mkdir(); 创建文件夹。
boolean mkdirs(); 创建多级文件夹。
2,删除。
boolean delete();删除失败返回false。如果文件正在被使用,则删除不了返回false。
void deleteOnExit();在程序退出时删除指定文件。
3,判断。
boolean exists(); 文件是否存在。
boolean isFile(); 是否是文件。
boolean isDirectory(); 是否是文件夹目录。
boolean isHidden(); 是否是隐藏文件。
boolean isAbsolute(); 是否是绝对路径。
4,获取信息。
String getName();
String getPath(); //获取相对路径。
String getParent();
String getAbsolutePath(); //获取绝对路径。
long lastModified(); 返回此抽象路径名表示的文件最后一次被修改的时间。
long length();
boolean renameTo(File dest); 重新命名此抽象路径名表示的文件。
示例代码:import java.io.*;
class FileDemo
{
public static void main(String[] args) throws IOException
{
//consMethod();//创建File对象
//method_1(); //删除
//method_2(); //创建文件夹
//method_3(); //判断
//method_4(); //获取信息
method_5(); //重新命名
}
//renameTo(File dest); 重新命名
public static void method_5()
{
File f1=new File("c:\\Test.java");
File f2=new File("c:\\haha.java");
sop("rename:"+f1.renameTo(f2));
}
//4,获取信息。
public static void method_4()
{
File f=new File("c:\\abc\\file.txt");
sop("path:"+f.getPath());
sop("abspath:"+f.getAbsolutePath());
sop("parent:"+f.getParent()); //该方法返回的是绝对路径中的父目录。如果获取的是相对路径,返回null。
//如果相对路径中有上一层目录,那么该目录就是返回结果。
}
//3,判断。
public static void method_3()throws IOException
{
File f=new File("D:\\java0403\\day20\\file.txt");
//f.createNewFile(); //创建文件。
f.mkdir(); //创建文件夹。
//记住:在判断文件对象是否是文件或者目录时,必须要先判断该文件对象封装的内容是否存在。
//通过exists判断。
sop("dir:"+f.isDirectory());
sop("file:"+f.isFile());
sop(f.isAbsolute());//判断是否是绝对路径。
}
public static void method_2()
{
File f=new File("file.txt");
//sop("execute:"+f.canExecute());
//sop("exists:"+f.exists());
//创建文件夹
File dir=new File("abc\\kkk\\a\\b\\c");
//sop("mkdir:"+dir.mkdir());//mkdir只能创建一级目录。
sop("mkdirs:"+dir.mkdirs());
}
//2,删除。
public static void method_1()throws IOException
{
File f=new File("file.txt");
//sop("create:"+f.createNewFile()); //在指定位置创建文件。
sop("delete:"+f.delete()); //删除
}
//创建File对象
public static void consMethod()
{
//将a.txt封装成file对象。可以将已有的和未出现的文件或者文件夹封装成对象。
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");
sop("f1:"+f1);
sop("f2:"+f2);
sop("f3:"+f3);
//实现跨平台的分隔符。"c:\\abc\\zzz\\a.txt"
File f4=new File("c:"+File.separator+"abc"+File.separator+"zzz"+File.separator+"a.txt");
}
//打印
public static void sop(Object obj)
{
System.out.println(obj);
}
}
九、Properties简述
Properties是Hashtable的子类。
也就是说它具备map集合的特点。而且它里面存储的键值对都是字符串。是集合中和IO技术相结合的集合容器。
该对象的特点:可以用于键值对形式的配置文件。那么在加载数据时,需要数据有固定格式:键=值。
示例代码:import java.io.*;
import java.util.*;
class PropertiesDemo
{
public static void main(String[] args) throws IOException
{
//setAndGet();
//method_1();
loadDemo();
}
public static void loadDemo()throws IOException
{
Properties prop=new Properties();
FileInputStream fis=new FileInputStream("info.txt");
//将流中的数据加载进集合。
prop.load(fis);
prop.setProperty("wangwu","39");
FileOutputStream fos=new FileOutputStream("info.txt");
prop.store(fos,"haha");
//System.out.println(prop);
prop.list(System.out);
fos.close();
fis.close();
}
//演示:如何将流中的数据存储到集合中。
//想要将info.txt中的键值数据存到集合中进行操作。
/*
思路:
1,用一个流和info.txt文件关联。
2,读取一行数据,将该行数据用"="进行切割。
3,等号左边作为键,右边作为值。存入到Properties集合中即可。
*/
public static void method_1()throws IOException
{
BufferedReader bufr=new BufferedReader(new FileReader("info.txt"));
String line=null;
Properties prop=new Properties();
while ((line=bufr.readLine())!=null)
{
String[] arr=line.split("=");
//System.out.println(arr[0]+"......"+arr[1]);
prop.setProperty(arr[0],arr[1]);
}
bufr.close();
System.out.println(prop);
}
//设置和获取元素。
public static void setAndGet()
{
Properties prop=new Properties();
prop.setProperty("zhangsan","30");
prop.setProperty("lisi","39");
//System.out.println(prop); //结果:{zhangsan=30, lisi=39}
String value=prop.getProperty("lisi");
//System.out.println(value);
prop.setProperty("lisi",89+"");
Set<String> names=prop.stringPropertyNames();
for(String s : names)
{
System.out.println(s+":"+prop.getProperty(s));
}
}
}
十、PrintWriter
IO包中的其他类:
打印流
PrintWriter与PrintStream
可以直接操作输入流和文件。
序列流
SequenceInputStream
对多个流进行合并。
操作对象
ObjectInputStream与ObjectOutputStream
被操作的对象需要实现Serializable(标记接口);
打印流:该流提供了打印方法,可以将各种数据类型的数据都原样打印。
字节打印流:
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 bufr=new BufferedReader(new InputStreamReader(System.in));
//PrintWriter out=new PrintWriter(System.out);
//PrintWriter out=new PrintWriter(System.out,true);
PrintWriter out=new PrintWriter(new FileWriter("a.txt"),true);
String line=null;
while ((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
out.println(line.toUpperCase());
//out.write(line.toUpperCase());
//out.flush();
}
out.close();
bufr.close();
}
}
十一、对象的序列化
操作对象
ObjectInputStream与ObjectOutputStream
被操作的对象需要实现Serializable(标记接口)。
示例代码: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("lisi0",399,"kr"));
oos.close();
}
}
class Person implements Serializable
{
public static final long serialVersionUID = 42L;
private String name;
/*
要是不想被序列化的话,可以加上transient。
age被transient修饰之后,就不能被序列化了。
保证其值在堆内存中存在,而不在文本文件中存在。
*/
transient int age; //加上transient之后,不能被序列化。
static String country="cn"; //静态是不能被序列化的。
Person(String name,int age,String country)
{
this.name=name;
this.age=age;
this.country=country;
}
public String toString()
{
return name+":"+age+":"+country;
}
}
十二、IO包中的其他类
IO包中的其他类
RandomAccessFile
随机访问文件,自身具备读写的方法。通过SkipBytes(int x),seek(int x)来达到随机访问。
管道流
PipedInputStream和PipedOutputStream
输入输出可以直接进行连接,通过结合线程使用。
RandomAccessFile:
该类不算是IO体系中的子类,而是直接继承自Object。但是,它是IO包中成员。因为它具备读和写功能。内部封装了一个数组,而且通过指针对数组的元素进行操作。 可以通过getFilePointer获取指针位置。同时可以通过seek改变指针的位置。其实完成读写的原理就是内部封装了字节输入流和输出流。
通过构造函数可以看出,该类只能操作文件。而且操作文件还有模式:只读r,,读写rw等。("r","rw","rws","rwd")
如果模式为只读 r,不会创建文件。会去读取一个已存在文件。如果该文件不存在,则会出现异常。
如果模式为 rw,操作的文件不存在,会自动创建。如果存在则不会覆盖。
操作基本数据类型
DataInputStream与DataOutputStream
可以用于操作基本数据类型的数据的流对象。
操作字节数组
ByteArrayInputStream与ByteArrayOutputStream
操作字符数组
CharArrayReader与CharArrayWriter
操作字符串
StringReader与StringWriter
用于操作字节数组的流对象。
ByteArrayInputStream:在构造的时候,需要接收数据源,而且数据源是一个字节数组。
ByteArrayOutputStream:在构造的时候,不用定义数据目的。因为该对象中已经内部封装了可变长度的字节数组。这就是数据目的地。
因为这两个流对象都操作的是数组,并没有使用系统资源。所以,不用进行close关闭。
在流操作规律讲解时:
源设备:
键盘 System.in,硬盘 FileStream,内存 ArrayStream。
目的设备:
控制台 System.out,硬盘 FileStream,内存 ArrayStream。
这就是用流的读写思想来操作数组。
示例代码:import java.io.*;
class ByteArrayStream
{
public static void main(String[] args) throws IOException
{
//数据源
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")); //需要异常处理
}
}
十三、字符编码
字符编码
字符流的出现为了方便操作字符。更重要的是加入了编码转换。通过子类转换流来完成。
InputStreamReader
OutputStreamWriter
在两个对象进行构造的时候可以加入字符集。
编码表的由来
计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字。就将各个国家的文字用数字来表示,并一一对应,形成一张表。这就是编码表。
编码:字符串变成字节数组。
解码:字节数组变成字符串。
String --> byte[]: str.getBytes(charsetName);
byte[] --> String: new String(byte[],charsetName);
示例代码:import java.util.*;
class EncodeDemo
{
public static void main(String[] args) throws Exception
{
String s="你好";
byte[] b1=s.getBytes("GBK");
System.out.println(Arrays.toString(b1));
//String s1=new String(b1,"ISO8859-1");
String s1=new String(b1,"UTF-8");
System.out.println("s1="+s1);
//对s1进行ISO8859-1编码
//byte[] b2=s1.getBytes("ISO8859-1");
byte[] b2=s1.getBytes("UTF-8");
System.out.println(Arrays.toString(b2));
String s2=new String(b2,"GBK");
System.out.println("s2="+s2);
}
}