Java 基础IO

理解文件

  • 文件是相关记录或者放在一起的数据的集合

  • 你在windows操作中,经常在硬盘上创建的各种.txt, .doc, .exe, .java, .lib, .mp3等等,都可以称之为文件

  • 文件简单的可以理解成,在外设硬盘上面保存数据的一种方式

  • 文件一共可以由两部分构成:属性(文件大小,文件名,文件类型等)+内容(就是文件里面放的是什 么)

  • JavaIO ,学什么呢?就学对文件的属性和内容进行操作,而实际写入或者读取的过程,我们称之为IO

File文件操作类

在 java.io 包之中,用 File 类来对文件进行操作(创建、删除、取得信息等)

file类的使用-准备

方法解释
public File(String pathname)创建指定路径文件对象
public File(String parent, String child)同上,但可指明父路径和子路径

为了方便表述,我们先做两方面准备:1. 创建 FileDemo.java , 方便测试, 2. 指定并创建文件测试 路径为 E:\java_code\file

package com.bittech;
public class FileDemo {    
	public static void main(String[] args){        
		//注意后的\\,拼接,不是必须的,仅仅为了让同学们理解路径的构成 文件路径+文件名        
		String path = "E:\\java_code\\file\\";         
		String name = "demo.txt";        
		String pathname = path + name;    
	} 
}

File类常用方法-基本文件操作

方法说明
public boolean exists()测试指定路径中文件或者目录是否 存在
public boolean isDirectory()判定一个文件是目录
public boolean isFile()判定是否是文件
public boolean delete()删除文件
public boolean createNewFile() throwsIOException创建一个新文件

实际项目部署环境可能与开发环境不同。那么这个时候路径分隔符是一个很重要的问题。 windows 下使用的是 \ ,而 Unix/Linux 系统下使用的是 / 。所以在使用路径分隔符时都会 采用File类的一个常量 public static final String separator 来描述。
在Java中要进行文件的处理操作是要通过本地操作系统支持的,在这之中如果操作的是同名 文件,就可能出现延迟的问题。(开发之中尽可能避免文件重名问题)

File类常用方法-目录操作

方法解释
public boolean mkdir()创建一个空目录
public boolean mkdirs()创建目录(无论有多少级父目录,都会创建)
public String getParent()取得父路径
public File getParentFile()取得父File对象

File类常用方法-文件属性操作

方法解释
public long length()取得文件大小(字节)
public long lastModified()最后一次修改日期

相对路径/绝对路径

绝对路径
是指目录下的绝对位置,直接到达目标位置,通常是从盘符开始的路径。完整的描述文件位置的路径就 是绝对路径 。如: E:\javacode\Java8\Test.java 。通常:http://www.sun.com/index.htm也 代表了一个URL绝对路径。
当前目录
这个参数还可以使用一些常用的路径表示方法,例如 ”.” 或 ”./” 代表当前目录,这个目录也就是 jvm 启动 路径 .所以如下代码能得到当前 .java 文件的当前目录的完整路径:(注意:这个目录也是当前 idea 中的 JVM 的启动路径,我们可以修改这个路径:在 Run->edit Configurations->Working directory 可 以看到)
相对路径
相对与某个基准目录的路径 。使用相对路径可以为我们带来非常多的便利 。如当前路径为 E:\javacode ,要描述上述路径( E:\javacode\Java8\Test.java ),只需输 入: Java8\Test.java 。此时的路径是相对 E:\javacode 来说的。

File类常用方法-其他操作

方法解释
public File[] listFiles()列出一个目录指定的全部组


在 Java中所有数据都是使用流读写的。流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。
即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象 为各种类,方便更直观的进行数据操作。

  1. 按照流向分:输入流;输出流
  2. 按照处理数据的单位分:字节流(8位的字节);字符流(16位的字节)

什么是输入输出流
输入就是将数据从各种输入设备(包括文件、键盘等)中读取到内存中。
输出则正好相反,是将数据写入到各种输出设备(比如文件、显示器、磁盘等)。
例如键盘就是一个标准的输入设备,而显示器就是一个标准的输出设备,但是文件既可以作为输入设 备,又可以作为输出设备

什么是字节流,字符流
File类不支持文件内容处理,如果要处理文件内容,必须要通过流的操作模式来完成。 在java.io包中,流分为两种:字节流与字符流
字节流:数据流中最小的数据单元是字节 。
InputStream、OutputStream 字符流:数据流中最小的数据单元是字符, Java中的字符是Unicode编码,一个字符占用两个字节 。Reader、Writer

字节流

FileInputStream 和 FileOutputStream

public  class FileInputStream extends InputStream {}

FileInputStream 从文件系统中的某个文件中获得输入字节。 FileInputStream 用于读取诸如图像数据之类的原始字节流.

方法解释
FileInputStream(Filefile)通过打开与实际文件的连接创建一个 FileInputStream ,该文 件由文件系统中的 File 对象 file 命名
FileInputStream(Stringname)通过打开与实际文件的连接来创建一个 FileInputStream ,该 文件由文件系统中的路径名 name 命名。
public class FileOutputStream extends OutputStream 

文件输出流是用于将数据写入到输出流 File 或一个 FileDescriptor 。 文件是否可用或可能被 创建取决于底层平台。
特别是某些平台允许一次只能打开一个文件来写入一个 FileOutputStream (或其他文件写入对 象)。 在这种情况下,如果所涉及的文件已经打开,则此类中的构造函数将失败。

方法解释
FileOutputStream(File file)创建文件输出流以写入由指定的 File 对象表示的文件。
FileOutputStream(Stringname)创建文件输出流以指定的名称写入文件

字节缓冲流 BufferedInputStream 和 BufferedOutputStream

为什么需要有缓冲流?
答:当我们用read()读取文件时,每读一个字节,访问一次硬盘,效率很低 。文件过大时,操作 起来也不是很方便。因此我们需要用到buffer缓存流,当创建buffer对象时,会创建一个缓冲区 数组。当我们读一个文件时,先从硬盘中读到缓冲区,然后直接从缓冲区输出即可,效率会更高。

public class BufferedInputStream extends FilterInputStream 

BufferedInputStream 为另一个输入流添加了功能,即缓冲输入和支持 mark 和 reset 方法的功 能。 当创建 BufferedInputStream 时,将创建一个内部缓冲区数组。 当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次有多个字 节。 mark 操作会记住输入流中的一点,并且 reset 操作会导致从最近的 mark 操作之后读取的所 有字节在从包含的输入流中取出新的字节之前重新读取。

方法解释
BufferedInputStream(InputStreamin)创建一个 BufferedInputStream 并保存其参数,输 入流 in ,供以后使用。
BufferedInputStream(InputStreamin,int size)创建 BufferedInputStream 具有指定缓冲区大小,保存其参数,输入流in,供以后使用
public class BufferedOutputStream  extends FilterOutputStream

该类实现缓冲输出流。 通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为 写入的每个字节导致底层系统的调用。

方法解释
BufferedOutputStream(OutputStreamout)创建一个新的缓冲输出流,以将数据写入指定 的底层输出流。
BufferedOutputStream(OutputStreamout, int size)创建一个新的缓冲输出流,以便以指定的缓冲 区大小将数据写入指定的底层输出流

字符流

字符流 FileReader 和 FileWriter

public class FileReader extends InputStreamReader

如果要从文件中读取内容,可以直接使用 FileReader 子类。 FileReader 是用于读取字符流。 要读取原始字节流,请考虑使用 FileInputStream .

方法解释
FileReader(File file)创建一个新的 FileReader ,给出 File 读取。
FileReader(String fileName)创建一个新的 FileReader ,给定要读取的文件的名称。
方法解释
FileWriter(File file)给一个File对象构造一个FileWriter对象。
FileWriter(String fileName)构造一个给定文件名的FileWriter对象。
public class FileWriter extends OutputStreamWriter

如果是向文件中写入内容,应该使用 FileWriter 子类
FileWriter 是用于写入字符流。要编写原始字节流,请考虑使用 FileOutputStream.

方法解释
FileWriter(File file)给一个File对象构造一个FileWriter对象。
FileWriter(String fileName)构造一个给定文件名的FileWriter对象。

字符缓冲流 BufferedReader 和 BufferedWriter

为了提高字符流读写的效率,引入了缓冲机制,进行字符批量的读写,提高了单个字符读写的效率。 BufferedReader 用于加快读取字符的速度, BufferedWriter 用于加快写入的速度。
BufferedReader 和 BufferedWriter 类各拥有 8192个 字符的缓冲区。当 BufferedReader在 读取文 本文件时,会先尽量从文件中读入字符数据并放满缓冲区,而之后若使用read()方法,会先从缓冲区中 进行读取。如果缓冲区数据不足,才会再从文件中读取,使用 BufferedWriter 时,写入的数据并不会 先输出到目的地,而是先存储至缓冲区中。如果缓冲区中的数据满了,才会一次对目的地进行写出。

public class BufferedReader extends Reader

从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取

方法解释
BufferedReader(Reader in)创建使用默认大小的输入缓冲区的缓冲字符输入 流。
BufferedReader(Reader in, int sz)创建使用指定大小的输入缓冲区的缓冲字符输入 流
public class BufferedWriter  extends Writer

将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入

方法解释
BufferedWriter(Writer out)创建使用默认大小的输出缓冲区的缓冲字符输出流。
BufferedWriter(Writer out, int sz)创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区。

字节流对比字符流
1、字节流操作的基本单元是字节;字符流操作的基本单元为Unicode码元。
2、字节流在操作的时候本身不会用到缓冲区的,是与文件本身直接操作的;而字符流在操作的时候使 用到缓冲区的。
3、所有文件的存储都是字节(byte)的存储,在磁盘上保留的是字节。
4、在使用字节流操作中,即使没有关闭资源(close方法),也能输出;而字符流不使用close方法的 话,不会输出任何内容。

字符字节转换流
有时候我们需要进行字节流与字符流二者之间的转换,因为这是两种不同的流,所以,在进行转换的时 候我们需要用到 OutputStreamWriter 和 InputStreamReader 。
InputStreamReader 是Reader的子类,将输入的字节流转换成字符流。

public class InputStreamReader extends Reader

InputStreamReader 是从字节流到字符流的桥:它读取字节,并使用指定的 charset 将其解码为 字符 。
它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集

方法解释
InputStreamReader(InputStream in)创建一个使用默认字符集的 InputStreamReader。
InputStreamReader(InputStream in,Charset cs)创建一个使用给定字符集的 InputStreamReader

OutputStreamWriter 是Writer的子类,将输出的字符流转换成字节流。

public class OutputStreamWriter  extends Writer

OutputStreamWriter 是字符的桥梁流以字节流:向其写入的字符编码成使用指定的字节 charset 。 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集.

方法解释
OutputStreamWriter(OutputStream out)创建一个使用默认字符编码的 OutputStreamWriter。
OutputStreamWriter(OutputStream out,Charset cs)创建一个使用给定字符集的 OutputStreamWriter

小结
:在 Java中所有数据都是使用流读写的。流是一组有顺序的,有起点和终点的字节集合,是对数据传 输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象 为各种类,方便更直观的进行数据操作。
I/O流的分类
1.按照流向分:输入流;输出流 2.按照处理数据的单位分:字节流(8位的字节);字符流(16位的字节)
3.按照流的功能分: 节点流(低级流):可以从一个特定的IO设备上读/写数据的流。(了解) 处理流(高级流/过滤流):是对一个已经存在的流的连接和封装,通过所封装的流的功能调用实现 数据读/写操作。通常处理流的构造器上都会带有一个其他流的参数。(了解)
流的作用:为数据源和目的地建立一个输送通道。

序列化/反序列化

什么是序列化和反序列化
序列化:把对象转换为字节序列的过程称为对象的序列化。
反序列化:把字节序列恢复为对象的过程称为对象的反序列化。
有时候我们想把一些信息持久化保存起来,那么序列化的意思就是把内存里面的这些对象给变成一连串 的字节描述的过程。 常见的就是变成文件

什么时候需要序列化
记住以下几点:
1、把内存中的对象状态保存到一个文件中或者数据库中时候;
2、 用套接字在网络上传送对象的时候;

实现序列化的方式
一定要牢记实现序列化本身是跟语言无关的

0、Java对象序列化
1、JSON序列化
2、XML
3、Protostuff
4、Hession(它基于HTTP协议传输,使用Hessian二进制序列化,对于数据包比较大的情况比较 友好。)
5、Dubbo Serialization(阿里dubbo序列化)
6、FST(高性能、序列化速度大概是JDK的4-10倍,大小是JDK大小的1/3左右)
7、自定义协议进行序列化

如何实现序列化(Java对象序列化)
如下代码,我们自己实现一个Person类,在类当中定义成员变量 name , age , sex , stuId , count ,如果要讲Person进行序列化需要实现Serializable接口即可 。

import java.io.Serializable 
class Person implements Serializable{    
 	//private static final long serialVersionUID = 1L;    
 	private String name;    
 	private int age;    
 	private String sex;    
 	//transient修饰的变量,不能被序列化    
 	transient private int stuId;    
 	//static修饰的变量,不能被序列化    
 	private static int count = 99;
    public String getName() {        
    	return name;    
    }
    public int getAge() {        
   	return age;    
   }
    public String getSex() {        
    	return sex;    
    }
    public int getStuId() {        
    	return stuId;    
    }
    public void setName(String name) {        
    	this.name = name;    
    }
    public void setAge(int age) {        
    	this.age = age;    
    }
    public void setSex(String sex) {        
    	this.sex = sex;    
    }
    public void setStuId(int stuId) {        
    	this.stuId = stuId;    
    } 
     @Override    
     public String toString() {        
     	return "Person{" +                
     	"name='" + name + '\'' +                
     	", age=" + age +              
       	", sex='" + sex + '\'' +                
       	", stuId=" + stuId +                
       	", count=" + count +                
       	'}';    
       } 
}
public class TestDemo3 {    
public static void main(String[] args) throws Exception {        
	serializePerson();        
	Person person = deserializePerson();        
	System.out.println(person.toString());    
}    
/**     * 序列化     */    
private static void serializePerson() throws IOException {        
	Person person = new Person();        
	person.setName("bit");        
	person.setAge(10);        
	person.setSex("男");        
	person.setStuId(100);        
	// ObjectOutputStream 对象输出流,将 person 对象存储到E盘的        
	// person.txt 文件中,完成对 person 对象的序列化操作        
	ObjectOutputStream oos = new ObjectOutputStream                (new FileOutputStream(new File("e:/person.txt")));        
	oos.writeObject(person);        
	System.out.println("person 对象序列化成功!");        
	oos.close();    
}
    
    /**     * 反序列化     */    
    private static Person deserializePerson() throws Exception {        
    	ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("e:/person.txt")));        
    	Person person = (Person) ois.readObject();        
    	System.out.println("person 对象反序列化成功!");        
    	return person;    
    }
} 

ObjectOutputStream 代表对象输出流: 它的 writeObject(Object obj) 方法可对参数指定的 obj 对象进行序列化,把得到的字节序列写到一个目标输出流中。 ObjectInputStream 代表对象输入流: 它的 readObject() 方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返 回。

 person 对象序列化成功!
 person 对象反序列化成功! 
 Person{name='bit', age=10, sex='男', stuId=0, count=99}

1,他实现了对象的序列化和反序列化。
2,transient 修饰的属性,是不会被序列化的 。我们的 stuId 本应是100,可现在为0.注意:内置类型 为对应0值。引用类型为null; 3、静态变量 好像也被序列化了,但是其实并没有,

小结
1、一个类如果想被序列化,那么需要实现一个Serializable接口。 2、类中的静态变量的值是不会被进行序列化的,transient 修饰的属性,是不会被序列化的,内置类型 为对应0值。引用类型为null;
3、在实现这个Serializable 接口的时候,一定要给这个 serialVersionUID 赋值,最好设置为1L,这 个L最好大写来区分,不然小写看起来像是1,不同的 serialVersionUID 的值,会影响到反序列化 .

字符编码

常见的编码有哪些

  1. GBK、GB2312:表示的是国标编码,GBK包含简体中文和繁体中文,而GB2312只包含简体 中文。也就是说,这两种编码都是描述中文的编码。
  2. UNICODE编码:java提供的16进制编码,可以描述世界上任意的文字信息,但是有个问 题,如果现在所有的字母也都使用16进制编码,那么这个编码太庞大了,会造成网络传输的 负担。
  3. ISO8859-1:国际通用编码,但是所有的编码都需要进行转换。
  4. UTF编码:相当于结合了UNICODE、ISO8859-1,也就是说需要使用到16进制文字使用 UNICODE,而如果只是字母就使用ISO8859-1,而常用的就是UTF-8编码形式。

在以后的开发之中使用的编码只有一个:UTF-8编码 .

乱码产生的本质:编码和解码不统一产生的问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值