IO流
流的流向分类
分为Input输入流和Output输出流
流操作的数据
字符流:操作字符
只能操作普通文本文件
最常见的普通文本文件:.txt、java、.c、.cpp等其他语言的源代码
只要用文本工具(记事本、notepad++、editplus)可以打开的,而且打开后我们人类能看懂的都是文本文件
注意:我们用的.doc、excel、ppt这些不是文本文件
字节流:操作字节
能操作一切文件(文本文件、.mp3、视频、.avi、.rmvb、mp4、doc、excel、ppt)
java中的四大流
字符输入流:共同Reader 比如:FileReader,BufferedReader。功能:读取一个字符,读取一个字符数组。
字符输出流:共同Writer 比如:FileWriter,BufferedWriter。功能:写一个字符,写一个字符数组(一部分),写一个字符串。
字节输入流:共同父类InputSteam 比如:FileInputStream,BufferedInputStream。功能:读取一个字节,读取一个字节数组。
字节输出流:共同父类OutputStream 比如:FileOutputStream,BufferedOutputStream。功能:写一个字节,写一个字节数组(一部分)。
总结2条规律:
只要是输入流,此流的方法名一定叫做read
只要是输出流,此流的方法名一定叫做writer
java中流的命名是十分规范的:
功能+父类的名字 (FileOutputStream)
OutputStream:字节输出流的根类,这是一个抽象类
public void close();
public void flush();
//和写数据有关系的方法
public void write(int b);//写一个字节
public void write(byte[] bs);//写一个字节数组
public void write(byte[] bs, int startIndex, int length);//写一个字节数组的一部分
FileOutputStream:给文件中续写与换行问题
续写
以前用的构造:
public FileOutputStream(String filename);//默认就是不续写
public FileOutputStream(File file);//给定一个File对象,这个File对象代表我们要写入的文件
请用这个构造:
public FileOutputStream(String filename,boolean flag);
public FileOutputStream(File file,boolean flag);
换行
windows:\r\n
Linux:\n
Mac:\r
public static void main(String[] args) throws Exception{
FileOutputStream fos = new FileOutputStream("1.txt",true);
fos.write("\r\nhello".getBytes());
fos.close();
}
InputStream:字节输入流的根类,这是一个抽象类
public int read();//读取一个字节,返回的是码值
public int read(byte[] bs);//读取一个字节数组,返回值表示实际读取到的字节数
读取一个字节
public static void main(String[] args) throws Exception{
/**
* 一、
* 1、创建了文件字节输入流对象
* 2、检测文件是否存在,如果不存在,直接抛出异常
* 3、让fis输入流对象指向该文件
*/
FileInputStream fis = new FileInputStream(new File("1.txt"));
/**
* 二、使用fis读取数据
* 1、读取一个字节
*/
int b = 0;
while((b = fis.read()) != -1){
System.out.print((char)b);
}
fis.close();
}
读取一个字节数组
public static void main(String[] args) throws Exception{
/**
* 一、
* 1、创建了文件字节输入流对象
* 2、检测文件是否存在,如果不存在,直接抛出异常
* 3、让fis输入流对象指向该文件
*/
FileInputStream fis = new FileInputStream(new File("1.txt"));
/**
* 二、使用fis读取数据
* 1、读取一个字节
*/
// int b = 0;
// while((b = fis.read()) != -1){
// System.out.println((char)b);
// }
//2、一次读取一个字节数组
byte[] bs = new byte[4];
int len = 0;
while((len = fis.read(bs)) != -1){
//System.out.println(len);
//String类有一个构造public String(byte[], int startIndex, int len);
System.out.print(new String(bs, 0, len));
}
fis.close();
}
复制文件
以字节方式读取
public static void main(String[] args) throws IOException {
//1、源文件:是读取用的,new FileInputStream("源文件")
FileInputStream fis = new FileInputStream("C:\\Users\\Hasee\\Pictures\\Saved Pictures\\yinyue3.jpg");
FileOutputStream fos = new FileOutputStream("copy.jpg");
//2、目标文件:是写入用的,new OutputStream("目标文件")
//3、一边读取源文件,一边写出到目标文件
long s = System.currentTimeMillis();
int b = 0;
while((b = fis.read()) != -1){
fos.write(b);
}
long e = System.currentTimeMillis();
System.out.println(e-s);
//4、关闭流
fos.close();
fis.close();
}
以字节数组方式读取
public static void main(String[] args) throws IOException {
//1、源文件:是读取用的,new FileInputStream("源文件")
FileInputStream fis = new FileInputStream("C:\\Users\\Hasee\\Pictures\\Saved Pictures\\yinyue3.jpg");
FileOutputStream fos = new FileOutputStream("copy.jpg");
//2、目标文件:是写入用的,new OutputStream("目标文件")
//3、一边读取源文件,一边写出到目标文件
long s = System.currentTimeMillis();
byte[] bs = new byte[1024];
int len = 0;
while((len = fis.read(bs)) != -1){
fos.write(bs,0, len);
}
long e = System.currentTimeMillis();
System.out.println(e-s);
//4、关闭流
fos.close();
fis.close();
}
缓冲输出流
public static void main(String[] args) throws IOException {
/**
* 缓存流:相比没有缓冲区的流,效率更高
* BufferedOutputStream 缓冲输出流
* 构造方法摘要
* public BufferedOutputStream(OutputStream out)
*/
//1、创建BufferedOutputStream对象
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("1.txt"));
//2、写数据
//字节
// bos.write(100);
//字节数组
// byte[] bs = "java".getBytes();
// bos.write(bs);
//字节数组的一部分
bos.write("java".getBytes(), 0, 3);
//3、关闭流
bos.close();
}
缓冲输入流
public static void demo02() throws IOException {
/**
* BufferedInputStream 缓冲输入流
* 构造方法摘要
* public BufferedInputStream(InputStream in)
*/
//1、创建BufferedInputStream对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("1.txt"));
//2、读取数据
// int b = 0;
// while((b = bis.read()) != -1){
// System.out.print((char) b);
// }
byte[] bs = new byte[2];
int len = 0;
while ((len = bis.read(bs)) != -1){
System.out.print(new String(bs, 0, len));
}
//3、关闭流
bis.close();
}
复制单级文件夹
public static void demo03() throws IOException {
/**
* 复制单级文件夹:
* 1、什么叫单级:一个文件夹中只有文件,没有其他文件夹
* 2、复制文件夹,是没有一个专门的流的
* 步骤:
* 数据源:C:\Users\Hasee\Pictures\Saved Pictures\2017-09
* 数据目的:F:\java学习笔记\demo
*/
//1、定义源文件和目标文件
File fileSrc = new File("C:\\Users\\Hasee\\Pictures\\Saved Pictures\\2017-09");
File fileObj = new File("F:\\java学习笔记\\demo");
//2、判断目标文件是否存在
if(!fileObj.exists()){
boolean b = fileObj.mkdir();
if(b){
System.out.println("目标文件夹不存在,成功创建一个!");
}
}else{
System.out.println("目标文件夹已经存在,不需要创建,直接复制即可!");
}
//3、列出源文件下的所有文件对象
File[] files = fileSrc.listFiles();
//4、遍历files数组
for(File file: files){
//a.源文件:file对象
//b.目标文件:file.getName()
File obj = new File(fileObj, file.getName());
System.out.println("源文件" + file);
System.out.println("目标文件" + obj);
//5、复制文件
copyFile(file,obj);
}
System.out.println("文件夹" + fileSrc.getName() + "复制成功!");
}
private static void copyFile(File file1, File file2) throws IOException{
//1、创建两个流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file1));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file2));
//2、一边读取,一边写入
byte[] bs = new byte[1024];
int len = 0;
while((len = bis.read(bs)) != -1){
bos.write(bs, 0, len);
}
//3、关闭流
bos.close();
bis.close();
System.out.println("复制文件" + file1.getName() + "成功!");
}
字节流读取中文出现的问题:
public static void demo04() throws IOException {
//字节流写中文
FileOutputStream fos = new FileOutputStream("ch.txt");
fos.write("中国".getBytes());
fos.close();
//字节流读中文
FileInputStream fis = new FileInputStream("ch.txt");
// int b = fis.read();
// System.out.println((char)b);//一个中文是两个字节,一个一个字节读会有问题
byte[] bs = new byte[2];
int len = 0;
len = fis.read(bs);
System.out.println(len);
System.out.println(new String(bs, 0, len));//一个一个字节数组读取,可能也会出现乱码(中aa国)
}
解决方案:
a.字符流
b.转换流
字符编码表(字符集):
ASCII码表,老美发明的,保存了数组、字母,以及一些符号对应的数字,每一个字符都是一个字节 A-65 a-97 0-48。 0xxx xxxx
GB2312码表,保存常用的汉字(6000-7000个),一个中文占两个字节,而且这两个字节都是负数。1111 1010 1010 1101
GBK码表:保存了基本所有的汉字(20000多个),不管是英文中文还是符号,都是两个字节,第一个是负数,第二个可能是正数也可能是负数。
Unicode,统一码表(万国码表),不管是英文中文还是其他符号,都是两个字节。对于一些只需要一个字节表示的符号,会显得很浪费。
UTF-8码表,基于unicode,一个字节就可以存储的数据,不用两个字节存储,而且这个码表更加标准化,在每一个字节头加入了编码信息(后期到api中查找)。
结论:在GBK码表中一个中文2个字节 在UTF-8中一个中文3个字节。对于我们来说,用到的中文码表:GBK、UTF-8。
ISO-8859-1:拉丁码表,Tomcat。
GB18030:最新的中文码表,目前还没有正式使用。
编码:把具体文字–>对应码值
解码:把码值–>翻译成具体文字
OutputStreamWriter
/**
* OutputStreamWriter:他是一个字符流
* extends Writer
* 方法:写一个字符/字符数组(一部分)/字符串(一部分)
* 描述:OutputStreamWriter是字符通向字节流的桥梁,查码表(编码)
* OutputStreamWriter的构造:
* public OutputStreamWriter(OutputStream out)
*/
public class OutputStreamWriterDemo {
public static void main(String[] args) throws IOException {
//1、创建OutputStreamWriter对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("1.txt"));
//2、osw写数据
osw.write("中国");
//3、关闭流
osw.close();
}
}
InputStreamReader
/**
* InputStreamReader: 字符流
* extends Reader
* 方法:一次读取一个字符,一次读取一个字符数组
* InputStreamReader 是字节流通向字符流的桥梁,查码表
* InputStreamReader的构造:
* public InputStreamReader(InputStream in) //默认查GBK码表
* public InputStreamReader(InputStream in, String charsetName) //查指定码表
*/
public class InputStreamReaderDemo {
public static void main(String[] args) throws IOException {
//1、创建InputStreamReader对象
// InputStreamReader isr = new InputStreamReader(new FileInputStream("1.txt"));
InputStreamReader isr = new InputStreamReader(new FileInputStream("1.txt"),"UTF-8");
//2、读取数据
int ch = isr.read();
System.out.println((char)ch);
ch = isr.read();
System.out.println((char)ch);
//3、关闭
isr.close();
}
}
序列化与反序列化(多用于安卓开发,例如保存游戏信息)
/**
* 序列化流:写对象到文件
* ObjectOutputStream
* 反序列化流:从文件中获取对象
* ObjectInputStream
*
* ObjectOutputStream:对象的字节输出流,写对象
* 构造:
* public ObjectOutputStream(OutputStream out)
* 方法:
* public void writeObject(Object obj)
*
* ObjectInputStream:对象的字节输入流,读对象
* 构造:
* public ObjectInputStream(InputStream in)
* 方法:
* public Object readObject()
*/
public class ObjectStreamDemo {
public static void main(String[] args) throws Exception {
writeDog();
readDog();
}
public static void readDog() throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("1.txt"));
System.out.println(ois.readObject());
ois.close();
}
public static void writeDog() throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.txt"));
Dog dog = new Dog("张三",18);
oos.writeObject(dog);//若Dog类没实现Serilizable,抛出NotSerializableException异常
oos.close();
}
}
java.io.Serializable接口中没有方法,用于标记作用,如果一个类有标记,可以序列化,没有标记就不能序列化
InvalidClassException:无效类异常
当写完一个对象之后,修改该对象所属的类,那么再次读取对象的时候,原来的类失效,会出现无效类异常。jvm通过版本号判断一个类是否被修改。加上
private static final long serialVersionUID = 1L;
可解决。
transient:关键字的作用
用来修饰成员变量,在序列化的时候,如果一个成员变量被transient修饰,那么序列化的时候会忽略该成员变量
打印流:打印数据
PrintWriter:打印字符流
PriterStream:打印字节流
方法都一样,区别在于打印的目的地不一样
PrintWriter:可以打印的目的地:
1、字符串的文件名;2、File对象;3、其他的OutputStream流;4、其他的Writer流。
PriterStream:可以打印的目的地:
1、字符串的文件名;2、File对象;3、其他的OutputStream流。