IO流的定义:
数据以流的形式传输。
对于程序而言,从磁盘文件读到程序、内存叫输入流,由程序、内存写到磁盘文件叫输出流。
(对于输出的介质来说,叫输出流;对于输入的介质来说,叫输入流。)
区别于集合里的流Stream,那是函数表达式实现数据的处理。
反斜杠\代表的是转义:
使用可以输出不能打印的字符\,\r代表覆盖前面输出的字符,\n代表换行,\t是制表符,\r\n代表换行。
创建文件夹对象,可以传文件路径也可以传对象。
newFile(pathname);
new File(parent,child);
还有各种api
读取磁盘文件夹里的图片
是文件夹就递归调用,不是就获取名字退出。
public class findPngTest {
@Test
public void findAllPng(){
String path = "D:\\image"; //把此路径下的所有图片都打印出来
File file = new File(path); //创建一个文件的对象
//file有很多的api
//.list(new FilenameFilter)有这么一个接口可以把图片过滤出来
File[] list = file.listFiles(new PngFilter());
if (list != null){
for (File image : list){ //循环
if (image.isDirectory()){ //是文件夹就递归调用
findAllPng();
}else {
System.out.println(image.getName()); //不是就打印图片的名字
return; //退出循环
}
}
}
}
public static class PngFilter implements FilenameFilter {
@Override
public boolean accept(File dir, String name) {
return name.contains("jpg") || name.contains("png");
}
}
}
为了安全性,当我们的程序、用户空间要读取数据时,会向内核申请,让内核空间、操作系统去设备上读取数据,读取完以后,用户空间去内核空间复制一份,用户空间再复制一份到另一个内核空间中,由另一个内核空间、操作系统对另一个设备输出数据。
BIO:阻塞的IO、程序必须等着内核读完数据才能进行下一步操作。
NIO:not blocking io,程序向内核申请完读数据以后,就可以接着执行代码,让一个线程不停的自旋访问内核是否已经读完数据。
节点流:直接读文件写文件。
InputStream .read(bytes),OutputStream .write(bytes)。
处理流:处理读流和写流。读BufferReader .readLine(),写objectOutputStream .writeObject(user)。
字节流:以字节为单位读取数据。
字符流:以字符为单位读取数据。
程序要去磁盘上读文件,对于程序而言就是输入流。
输入流inputStream只有一个核心方法是read()。
读到-1表示读完了。
read()返回的是int关于读几个数据的值,一个一个读的。
read(bytes):意思是一个一个byte读,读完以后把数据储存到bytes中,直接new String打印bytes就可以了。new String是因为byte没有重写toString(),直接打印bytes得到的结果是内存地址。
//文件保存的是记事本,word文档是不行的
public class StreamTest {
//字节流读文件
@Test
public void inputFile() throws IOException {
File file = new File("D:\\image\\a.txt");//左单击文件 获取全路径的方式
InputStream inputStream = new FileInputStream(file);
int read;
while ((read = inputStream.read()) != -1){ //-1表示已经读完了
System.out.println(read);
}
// byte[] bytes = new byte[10];
// while ((read = inputStream.read(bytes)) != -1){
// System.out.println(new String(bytes));//没有重写toString()
// }
}
程序需要将文件写到磁盘上,对于程序而言就是输出流。
输出流outputStream只有一个核心方法就是write()。
new FileOutputStream(“path name”,append:true/false);
append:true表示写的数据是要append,false表示写的数据要覆盖之前的数据。
//字节流写文件
@Test
public void outputFile() throws IOException {
byte[] bytes = {97,98,99,100};
OutputStream outputStream = new FileOutputStream("D:\\image\\a.txt",true);//append
for (int i = 0; i < bytes.length; i++) { //一个一个写
outputStream.write(bytes[i]);
}
}
读文件,输出文件。
new FileOutputStream(“path name”);这里的pathname的最后接的文件名要和输出的文件名相同。
不能直接在方法名后面抛异常,代码trycatch,IO流的线程占用内存资源,要使用finally关闭,关闭时也要if判断输入输出流可能没有创建也要trycatch。
可以把输入输出流定义在try里,这样 inputStream 和outputStream 就会继承AutoClosable()接口由JVM自动关闭掉io流的线程。
//字节流一边读一边写文件
@Test
public void copyFile1() throws IOException {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
String path = "D:\\image\\InputPackage\\mk.mp4";
inputStream = new FileInputStream(path);//从哪读
outputStream = new FileOutputStream("D:\\image\\OutputPackage\\mk.mp4");//读到哪,文件名字要相同
int readNumber;//一次读几个
byte[] bytes = new byte[1024 * 1024];//缓存传输的单位
//开始读
while ((readNumber = inputStream.read(bytes)) != -1){ //-1表示读完了,退出
outputStream.write(bytes);//写过去
}
}catch (IOException e){
e.printStackTrace();
}finally { //关闭线程不占用资源
if (inputStream != null){ //开启了才关闭
try {
inputStream.close();
}catch (IOException e){
e.printStackTrace();
}
}
if (outputStream != null){
try {
outputStream.close();
} catch (IOException e){
e.printStackTrace();
}
}
}
}
@Test
public void copyFile(){
//定义在try里这样jvm可以在执行完读写操作以后关闭线程
try(InputStream inputStream = new FileInputStream("D:\\image\\InputPackage\\mk.mp4");
OutputStream outputStream = new FileOutputStream("D:\\image\\OutputPackage\\mk.mp4")) {
byte[] bytes = new byte[1024 * 1024];
int read;
while ((read = inputStream.read(bytes)) != -1){
outputStream.write(bytes);
}
}catch (IOException e){
e.printStackTrace();
}
}
字符流读写文件
Reader、Writer,new FileReader、new FileWriter,读写出来的文件名要相同,写时是append还是覆盖要考虑好,读到-1退出循环、一边读一边写,写完要调用flush(),文件内容才会显示出来,读完写完要关闭读写流。
@Test
public void ReadWriteStream() throws IOException {
Reader reader = new FileReader("D:\\image\\a.txt");
Writer writer = new FileWriter("D:\\image\\OutputPackage\\a.txt");
int read;
while ((read = reader.read()) != -1){
writer.write(read);
}
writer.flush();
reader.close();
writer.close();
}
处理流读文件
处理文件BufferReader .readLine()。
//处理流一行行读文件
@Test
public void ReadProcessStream() throws IOException {
Reader reader = new FileReader("D:\\image\\a.txt");//读哪
BufferedReader bufferedReader = new BufferedReader(reader);//处理流
String content;
while ((content = bufferedReader.readLine()) != null){ //不为空
System.out.println(content); //打印
}
//关闭
reader.close();
bufferedReader.close();
}
处理流写文件
//处理流写文件
@Test
public static void main(String args[]) throws IOException {
Writer writer = new FileWriter("D:\\image\\OutputPackage\\a.txt");
BufferedWriter bufferedWriter = new BufferedWriter(writer);
while (true){
Scanner scanner = new Scanner(System.in);
String next = scanner.next();
bufferedWriter.write(next);
bufferedWriter.flush();
}
}
Serializable
序列化:程序中的对象可以被写到磁盘文件中。
反序列化:磁盘文件中的数据可以恢复成对象。
被作为流传输的对象要执行Serializable接口,否则报异常NotSerializableException。
序列化的版本号serialversionUID:不写不能修改。
private static final long serial VersionUID=1L;
当一个JAVA类序列化时,jvm会给class随机生成一个版本号,一旦修改了代码或者改变jdk再序列化就会失败,因为修改代码或者改变jdk会生成一个新的版本号,jvm觉得此时被序列化的不再是原先的类,就不让序列化,并报异常InvalidClassException。
而在现实的项目开发中写了代码,有可能之后需要优化和升级代码,可以自定义一个版本号,不用系统随机生成的版本号,这样改了代码以后仍然可以序列化和反序列化。
给别的类不同的类添加相同的版本号可以一样的运行序列化和反序列化。
如何自动生成序列化的版本号?
Setting- inspections- serialization issues-Serializable class without serialVersion UID
transient:加上这个关键字的属性都不可以被序列化或反序列化。
被序列化和反序列化的类的引用类型成员变量必须也要实现Serializable 接口,否则类无法被序列化和反序列化。
浅拷贝:
新对象的引用指向是旧对象的堆内存的空间,那么旧对象的引用类型属性的值改变,新对象的也会改变。
被拷贝的类实现了Clonable接口,重写了父类clone()以后,在拷贝对象时调用clone(),可以实现浅拷贝,新对象的属性的值不会因为旧对象的属性的值改变而改变。但新对象的引用类型成员变量仍然是指向旧对象在堆中的引用类型成员变量。
实现深拷贝:
保存读的对象的字节数组ByteArrayOutputStream。
可以写对象的处理流objectOutputStrea把对象写到数组里。
ByteArrayOutputStream有一个方法toByteArray()可以把对象保存写到数组里。
读对象的处理流,把对象读出来,返回一个object类型强转成user类,打印对象,关闭流。
用带泛型的工具类封装深拷贝:传入一个对象,返回一个对象。
//序列化反序列化实现深拷贝
public class DeepCopyUtils {
public static <T> T deepCopy(T t) throws IOException, ClassNotFoundException {
//先写
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();//数组
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);//处理对象的流
objectOutputStream.writeObject(t);//把对象写到数组
//取出写的对象
byte[] bytes = byteArrayOutputStream.toByteArray();
//再读出来
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
User user = (User)objectInputStream.readObject();
return (T) user;
}
}