一、流
1、概念:一组有顺序、有起点和终点的字节集合,是对数据传输的总称或抽象。
2、本质:数据传输
根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作,一般用于文件操作、socket等。
3、分类
(1)根据操作单元分:字符流和字节流
①字节流【InputStream、OutputStream】:操作8bit字节,将数据解释成原始的二进制数,读写均为字节数据,因为不需要编码和解码的,比文本i/o效率高,
可移植,java中可以按照最小字节单位读取的流,即每次读写一个字节,字节流直接连接到输入源。
②字符流【Reader、Writer】:操作16位字符,将原始数据解释成字符的序列,输入和输出需要进行编码和解码,因为数据编码的不同,而有了对字符进行高效操作的流对象,本质是基于字节流读取时,去查指定的码表。
字符流和字节流的区别:
①读写单位不同
字节流以字节(8bit)为单位;字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
②处理对象不同
字节流能处理所有类型的数据;字符流只能处理字符类型的数据。
③读入读出字节位不同
字节流一次读入或读出是8位二进制;字符流一次读入或读出是16位二进制。
注:设备上的数据无论是图片、视频、文字,都以二进制存储的。二进制最终都以一个8位为数据单元进行体现,计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。
结论:只要是处理纯文本数据,优先考虑使用字符流, 除此之外都使用字节流。
(2)根据数据流向分:输入流和输出流
①输入流【InputStream、Reader】:存储介质→程序,读取数据(从磁盘、硬盘、键盘、socket等)到程序中。
②输出流【OutputStream、Writer】:程序→目标源,将程序中的数据输出(到磁盘、硬盘、显示器、socket等)。
(3)根据流的角色分:节点流和处理流
①节点流(节点:文件、内存缓冲区如Byte数组,Char数组,StringBuffer对象等):也叫做原始流,直接和IO设备相连,主要指从具体介质上读取或写入数据的类。
②处理流:主要指FilterInputStream/FilterOutStream和FilterReader/FilterWriter的子类,过滤流使用节点流作为输入或者输出,对其包装的节点流进行特定的处理。直接使用节点流读写不方便,为了更快读写文件,才有了处理流。
4、编码表
①ASCII表(一个字符中的7位就可以表示):对应的字节都是正数 0-XXXXXXX;
②ISO-8859-1:拉丁码表latin,用一个字节中的8位表示,可表示整数和负数1-XXXXXXX;
③GBK:目前最常用的中文码表,用2个字节表示,约2万的中文和符号,2个字节的第一个字节开图是1,第二个字节的开头是0;
④Unicode:国际标准码表,用2个字节存储utf-8,基于Unicode,一个字节就可以存储数据,不要用两个字节存储,而且这个码表更加标准化,在每一个字节头加入了编码信息(API中可查找)。
二、常用IO流类
(一)InputStream——字节输入抽象类(各字节输入类的基类)
(1)节点流
FileInputStream:文件字节输入流(操作对象为File文件对象)
ByteArrayInputStream:字节数组输入流(操作对象为字节数组)
(2)处理流
FilterInputStream:过滤字节输入流(简单的实现了InputSteam类,一般用它的子类BufferedInputSteam、DataInputStream等)
BufferedInputStream:字节输入缓冲流
DataInputStream:基本数据类型输入处理流
ObjectInputStream:引用数据类型输入处理流(对象反序列化)
(二)OutputStream——字节输出抽象类(各字节输出类的基类)
(1)字节流
FileOutputStream:文件字节输出流(操作对象为File文件对象)
ByteArrayOutputStream:字节数组输出流(操作对象为字节数组)
(2)处理流
FilterOutputStream:过滤字节输出流(简单的实现了OutputStream类,一般用它的子类BufferedOutputSteam、DataOutputStream等)
BufferedOutputStream:字节输出缓冲流
DataOutputStream:基本数据类型输出处理流
ObjectOutputStream:引用数据类型输出处理流(对象序列化)
(三)Reader——字符输入抽象类(各字符输入类的基类)
//常用方法
public int read() throws IOException {}//读取单个字符,返回作为整数读取的字符,如果已到达流的末尾返回-1
public int read(char[] cbuf) throws IOException {}//将字符读入数组,返回读取的字符数
public abstract int read(char[] cbuf, int off, int len) throws IOException {}//读取 len 个字符的数据,并从数组cbuf的off位置开始写入到这个数组中
public abstract void close() throws IOException {}//关闭该流并释放与之关联的所有资源
public long skip(long n) throws IOException {}//跳过n个字符。
public int available() //还可以有多少能读到的字节数
(1)节点流
FileReader:文件字符输入流(操作对象为File文件对象)
CharArrayReader:字符数组输入流(操作对象为字符数组)
(2)处理流
BufferedReader:字符输入缓冲流
InputStreamReader:字符输入格式处理流(可以设置字符编码等)
(四)Writer——字符输出抽象类(各字符输出类的基类)
public void write(int c) throws IOException {} //写入单个字符
public void write(char[] cbuf) throws IOException {} //写入字符数组
public abstract void write(char[] cbuf, int off, int len) throws IOException {} //写入字符数组的某一部分
public void write(String str) throws IOException {} //写入字符串
public void write(String str, int off, int len) throws IOException {}//写字符串的某一部分
public abstract void close() throws IOException {} //关闭此流,但要先刷新它
public abstract void flush() throws IOException {} //刷新该流的缓冲,将缓冲的数据全写到目的地
(1)节点流
FileWriter:文件字符输出流(操作对象为File文件对象)
CharArrayWriter:字符数组输出流(操作对象为字符数组)
(2)处理流
BufferedWriter:字符输出缓冲流
OutputStreamWriter:字符输出格式处理流(可以设置字符编码等)
以FileReader、FileWriter为例:
//FileReader构造方法:
FileReader(File file) //在给定从中读取数据的 File 的情况下创建一个新 FileReader
FileReader(String fileName)// 在给定从中读取数据的文件名的情况下创建一个新 FileReader
//FileReader常用方法:
int read() //读取单个字符
int read(char[] cbuf) //将字符读入数组
int read(char[] cbuf,int off, int len)//将字符读入数组的某一部分
void close()//关闭该流并释放与之关联的所有资源。
//FileWriter构造方法:
FileWriter(File file)//根据给定的 File 对象构造一个 FileWriter 对象。
FileWriter(File file,boolean append)//根据给定的 File 对象构造一个 FileWriter 对象,写入的内容追加在上次写入内容后面。
FileWriter(String fileName)//根据给定的文件名构造一个 FileWriter 对象。
FileWriter(String fileName,boolean append)//根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。(追加)
//FileWriter常用方法:
void write(int c)//写入单个字符
void write(char[] cbuf,int off,int len)//写入字符数组的一部分
void writer(String str,int off,int len)//写入字符串的某一部分
void flush()//刷新该流的缓冲
void close()//关闭此流,但要先刷新它
三、关于序列化和反序列化
(1)序列化:是一种对象持久化的手段,是将对象转化为字节流的过程。
(2)反序列化:将字节流装换为对象的过程。
(3)序列化需要使用ObjectOutputStream,反序列化使用ObjectInputStream。
(4)要实现对象的序列化和反序列化需要类实现java.io.Serializable
(5)Transient关键字:作用是控制变量的序列化。序列化过程中在变量前加该关键字,可以阻止该变量参与序列化;反序列化过程中,Transient修饰变量的值被设置为初始值(例如int类型初始值是0, String对象类型初始值是null)。
(6)虚拟机是否允许反序列化,不仅取决于类路径的代码是否一致,还取决于两个类的序列化ID是否一致(private static final long serialVersionUID)
(7)如果是类的父类需要实现序列化和反序列化,则父类也需要实现Serializable接口。
(8)序列化和反序列化应用场景:在网络中进行数据传输时,对于敏感的信息不在网络中传输,需要使用对象序列化和反序列化中的关键字Transient。
(9)serialVersionUID(串行化版本统一标识符):用来表示类中不同版本的兼容性。有两种生成方式:①默认是1L;②根据类名、类路径、接口名、属性、方式生成的64位的哈希字段。
UID的默认值依赖于JAVA编译器,对于同一个类,不同的编译器UID可能不同。
显性定义UID的作用:①确保不同版本有不同的UID;②在类中新增或者修改属性不设置UID会抛出异常。
(10)writeObject、readObject
序列化过程中,如果被序列化的类中定义了writeObject和readObject方法,虚拟机会试图调用来类中的writeObject和readObject方法进行自定义序列化和反序列化。如果没有这个方法则默认调用ObjectOutputStream中的defaultWriteObject操作。反序列化过程则调用ObjectInputStream中的defaultWriteObject操作。
(11)ArrayList中存储数据的elementData属性添加Transient,为什么ArrayList还能进行序列化和反序列化?
ArrayList->writeObject->writeObject0
(12)在对象的序列化过程中,接口Serializable中并没有任何需要实现的方法,
抛出NotSerializableExecption异常是?
答:序列化和反序列化的实例必须是String、Array、Enum、Serializable的实例。
四、关于随机访问文件类RandomAccessFile
(1)构造函数
public RandomAccessFile(String name, String mode)
public RandomAccessFile(File file, String mode)
(2)模式
r:可读模式,该模式只可读取,不能写入,否则抛异常
rw:读写模式,该模式可读可写
rws:读写模式,将更新的内容和元数据更新到存储设备
rwd:读写模式,将更新的内容更新到存储设备
(3)常用方法
length() //返回此文件的长度。
getFilePointer() //返回此文件中的当前偏移量。
skipBytes(int) //尝试跳过输入的 n 个字节以丢弃跳过的字节。
seek(long) //设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。
read(byte[] b, int off, int len) //将最多 len 个数据字节从此文件读入 byte 数组。
write(byte[] b, int off, int len) //将 len 个字节从指定 byte 数组写入到此文件,并从偏移量 off 处开始。
writeUTF(String) //使用 modified UTF-8 编码以与机器无关的方式将一个字符串写入该文件。
writeBytes(String s) //按字节序列将该字符串写入该文件。
writeChars(String s) //按字符序列将一个字符串写入该文件。
writeBoolean(boolean v) //按单字节值将 boolean 写入该文件。
writeByte(int v) // 按单字节值将 byte 写入该文件。
writeChar(int v) //按双字节值将 char 写入该文件,先写高字节。
writeInt(int v) // 按四个字节将 int 写入该文件,先写高字节。
练习:使用RandomAccessFile像文件中指定位置插入数据。
import java.io.IOException;
import java.io.RandomAccessFile;
public class InsertBufferDemo {
public static void addContent(String srcFile, int index, String append) {
RandomAccessFile randomAccessFile = null;
try {
randomAccessFile = new RandomAccessFile(srcFile, "rw");
randomAccessFile.seek(index);
StringBuffer sb = new StringBuffer();
byte[] b = new byte[100];
int len;
while ((len = randomAccessFile.read(b)) != -1) {
sb.append(new String(b, 0, len));
}
randomAccessFile.seek(index);
randomAccessFile.write(append.getBytes());
randomAccessFile.write(sb.toString().getBytes());
} catch (IOException e) {
e.printStackTrace();
}
try {
randomAccessFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
InsertBufferDemo ic = new InsertBufferDemo();
ic.addContent("C:\\Users\\Administrator\\Desktop\\新建文本文档.txt", 3,"A");
}
}
五、File类
用来操作文件,存在java.io目录下
1、构造函数
相对路径:不带盘符。cd . 表示当前目录。cd . . 表示当前目录的父目录。
绝对路径:带有盘符。例:C:\java\test\test1.txt
File(String pathname) 直接传入路径
File(String parent,String child) 传入父路径和子路径
File(File parent,String child) 传入file实例
File(URI uri) 远程链接
2、常用方法
boolean exists() //判断路径是否存在
String getAbsolutePath() //获取当前文件的绝对路径
boolean isFile() //判断实例是否是文件
boolean isDirectory() //判断实例是否是目录
boolean mkdir() //新创建目录
boolean createNewFile() throws IOException //创建新文件(在文件夹不存在的情况下文件创建不成功)
boolean delete() //删除文件,立即执行删除操作
void deleteOnExit() //删除文件,在程序退出时才删除
String getName() //获取名字
String[] list() //获取目录下的所有文件
//获取指定的内容(注:实现FilenameFilter接口)
FilenameFilter filter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.startsWith("Java");
}
};
//获取文件目录,返回的是file类型的数组
File[] files = file.listFiles();
File[] files1 = file.listFiles(filter);
六、练习(运用IO流、HashMap、PriorityQueue等对文档中数据进行读取并统计出重复次数最多的五组数据)
import java.io.*;
import java.util.*;
public class IOTest {
public static void main(String[] args) {
File f = new File("C:\\Users\\Administrator\\Desktop\\上课\\JavaSE\\笔记\\readerFile.txt");
//读取文件
FileRead(f);
//获取重复次数最多的五个数据
getLastFive(f);
}
public static ArrayList FileRead(File f) {
String path = f.getPath();
ArrayList arrayList = new ArrayList();
try {
FileInputStream fileInputStream = new FileInputStream(path);
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
BufferedReader bufferedReader= new BufferedReader(inputStreamReader);
String s = bufferedReader.readLine();
//将每行读取的数据存入s
while(s!=null){
String[] split = s.split(",");
for(int i=0;i<split.length;i++){
arrayList.add(Integer.valueOf(split[i]));
}
s=bufferedReader.readLine();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return arrayList;
}
public static void getLastFive(File f) {
ArrayList arrayList=FileRead(f);
HashMap<Integer, Integer> hashMap = new HashMap<Integer, Integer>();
Iterator<Integer> iterator = arrayList.iterator();
while (iterator.hasNext()) {
Integer value = iterator.next();
if (hashMap.containsKey(value)) {
hashMap.put(value, hashMap.get(value) + 1);
} else {
hashMap.put(value, 1);
}
}
Comparator<Map.Entry<Integer, Integer>> comparator = new Comparator<Map.Entry<Integer, Integer>>() {
@Override
public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2) {
return o1.getValue() - o2.getValue();
}
};
PriorityQueue<Map.Entry<Integer, Integer>> priorityQueue = new
PriorityQueue<Map.Entry<Integer, Integer>>(5, comparator);
Iterator<Map.Entry<Integer, Integer>> iterator1 = hashMap.entrySet().iterator();
while (iterator1.hasNext()) {
Map.Entry<Integer, Integer> entry = iterator1.next();
if (priorityQueue.size() < 5) {
priorityQueue.add(entry);
} else {
Map.Entry<Integer, Integer> entry1 = priorityQueue.peek();
if (entry.getValue() > entry1.getValue()) {
//当前value值大于队列顶的元素
priorityQueue.poll();
priorityQueue.add(entry);
}
}
}
Iterator<Map.Entry<Integer, Integer>> iterator2 = priorityQueue.iterator();
while (iterator2.hasNext()) {
Map.Entry<Integer, Integer> entry = iterator2.next();
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
}