目录放在这里太长了,附目录链接大家可以自由选择查看--------Java学习目录
对于程序语言的设计者来说,创建一个好的输入/输出(I/O)系统是一项非常艰难的任务
添加了NIO类
,添加进来是为了改善性能和功能.因此,在充分理解Java I/O系统以便正确运行之前,我们需要学习相当数量的类
.如果缺乏历史的眼光,很快我们就会对什么使用该使用哪些类,以及什么使用不该使用它们而感到迷惑
java.io包
中,可以从其中读入一个字节序列的对象称为输入流
,而可以向其中写入一个字节序列的对象称为输出流
.这些字节序列的来源地和目的地可以是文件(绝大多数),也可以是网络连接,内存块等.错误的想法
是:我要把a.txt文件内容取出来,然后再输入到b.txt里面,那岂不是从a.txt中拿数据应该是输出流,往b.txt文件里面放数据是输入流吗?这样想就错了.把程序当成判断输入输出的标准
,从a.txt取出数据到程序这个中转站中,这是对程序的输入,从程序汇总对数据进行处理后放入b.txt是输出流,所以千万不要搞混了流的方向
来划分的,此外根据功能的不同也可以分为节点流和处理流(个人感觉了解一下就行,不算非常重要)四大抽象类
来决定的抽象类InputStream和OutputStream
是构成面向字节
的输入/输出类层次结构的基础,其他所有字节输入输出流类都继承自这两个基类抽象类Reader和Writer
是构成面向字符
的输入/输出类层次结构的基础,其他所有字符输入输出流类都继承自这两个基类read()
的基本方法,用于读取单个字节或者字节数组;继承自OutputStream和Writer的类都含有write()
的基本方法,用于写出单个字节或字节数组
.用来处理Unicode字符的单独的类层次结构
.这些类拥有的读入和写出操作都是基于两字节
的Char值,而不是基于byte值的.其实并不在于频繁使用它们,而是因为别的类可以使用它们,以便提供更加有用的接口
.因此,我们很少使用单一的类来创建流对象,而是通过叠加多个对象来提供所期望的功能(这就是装饰器设计模式)
,相信这一点对于使用过I/O流的小伙伴就不陌生了,创建流对象的构造方法的参数还是一个流对象等等,这样的嵌套太常见了,初学者小白也不必慌,后面都会一一介绍.这也是JavaI/O流让人迷惑的一个原因:创建单一的结果流,却需要创建多个对象
直观的感受
一下四大抽象类的继承结构,我手动画了尽可能详细继承结构,虽然看起来很多,别慌,我们常用的还是有限的,分享给大家(如果看不清的话,建议右键--查看图像
,因为截图可能显得比较小)FileInputStream和FileOutputStream
可以提供对于磁盘文件的输入流和输出流,我们只需要向投早期提供文件名(相对路径)或文件的完整路径名(绝对路径)从用户工作目录开始
,可以通过调用System.getproperty("user.dir")
获得具体位置转义字符
,因此要确保在Windows风格的路径名中使用\\
(比如:c:\test\test.txt),因为大部分Windows文件处理的系统调用都会将斜杠解释成文件分隔符.但是最好获取一下自己使用的平台上的文件分割符,可以使用下面这种方式打印输出查看一下同InputStream和OutputStream一样
,只支持字节级别
的读写.只能读入字节和字节数组.增强
提高了效率
如果是纯文本,推荐使用字符流,提高读取效率和写入效率可以使用,其他情况都可以使用字节流来完成
// 将InputStream转为Reader
public class InputStreamReader extends Reader {
private final StreamDecoder sd;
// 使用默认的字符编码集创建一个InputStreamReader
public InputStreamReader(InputStream in) {
super(in);
try {
sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
} catch (UnsupportedEncodingException e) {
// The default encoding should always be available
throw new Error(e);
}
}
// 使用指定的字符编码集(名称)和InputStream创建一个InputStreamReader
public InputStreamReader(InputStream in, String charsetName)
throws UnsupportedEncodingException
{
super(in);
if (charsetName == null)
throw new NullPointerException("charsetName");
sd = StreamDecoder.forInputStreamReader(in, this, charsetName);
}
// 使用指定的字符编码类和InputStream创建一个InputStreamReader
public InputStreamReader(InputStream in, Charset cs) {
super(in);
if (cs == null)
throw new NullPointerException("charset");
sd = StreamDecoder.forInputStreamReader(in, this, cs);
}
// 使用指定的解码器dec和InputStream创建
public InputStreamReader(InputStream in, CharsetDecoder dec) {
super(in);
if (dec == null)
throw new NullPointerException("charset decoder");
sd = StreamDecoder.forInputStreamReader(in, this, dec);
}
// 返回使用的编码类型
public String getEncoding() {
return sd.getEncoding();
}
//读取一个字符
public int read() throws IOException {
return sd.read();
}
//将数据写入一个字符数组,cbuf中写入开始位置为offset,写入长度lenght
public int read(char cbuf[], int offset, int length) throws IOException {
return sd.read(cbuf, offset, length);
}
// 返回该流是否可以被读取
public boolean ready() throws IOException {
return sd.ready();
}
//关闭流
public void close() throws IOException {
sd.close();
}
}
// 将OutputStream转为Writer
public class OutputStreamWriter extends Writer {
private final StreamEncoder se;
// 使用指定名称的编码集和OutputStream创建
public OutputStreamWriter(OutputStream out, String charsetName)
throws UnsupportedEncodingException
{
super(out);
if (charsetName == null)
throw new NullPointerException("charsetName");
se = StreamEncoder.forOutputStreamWriter(out, this, charsetName);
}
// 使用默认编码集和OutputStream创建
public OutputStreamWriter(OutputStream out) {
super(out);
try {
se = StreamEncoder.forOutputStreamWriter(out, this, (String)null);
} catch (UnsupportedEncodingException e) {
throw new Error(e);
}
}
// 使用指定的Charset类型和OutputStream创建
public OutputStreamWriter(OutputStream out, Charset cs) {
super(out);
if (cs == null)
throw new NullPointerException("charset");
se = StreamEncoder.forOutputStreamWriter(out, this, cs);
}
// 使用CharsetEncoder类型的编码器和OutputStream创建
public OutputStreamWriter(OutputStream out, CharsetEncoder enc) {
super(out);
if (enc == null)
throw new NullPointerException("charset encoder");
se = StreamEncoder.forOutputStreamWriter(out, this, enc);
}
// 获取当前的编码类型
public String getEncoding() {
return se.getEncoding();
}
// 刷新缓冲区,将缓冲区中数据输出
void flushBuffer() throws IOException {
se.flushBuffer();
}
// 输出一个字符
public void write(int c) throws IOException {
se.write(c);
}
// 将cbuf字符数组从off位置开始,写出len长度的字符
public void write(char cbuf[], int off, int len) throws IOException {
se.write(cbuf, off, len);
}
//刷新输出流
public void flush() throws IOException {
se.flush();
}
//关闭流
public void close() throws IOException {
se.close();
}
}
将基本数据类型格式化成字符串输出
。对象类型将先调用toString(),然后输出该方法返回的字符串都不抛出IOException
System.out.println
,源码上就是调用的PrintStream的println方法public static void main(String[] args) {
try{
//1. 创建一个字节输出流
FileOutputStream fos = new FileOutputStream("e:\\test.txt");
//2. 构建缓冲流提高效率
BufferedOutputStream bs = new BufferedOutputStream(fos);
//3.构建字节打印流
PrintStream ps = new PrintStream(bs);
//4. 进行输出
ps.println("学习打印流");
ps.println("学习Java");
ps.println(2020);
ps.close();
}catch (Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
//1. 构建一个字符输出流
Writer os = new FileWriter("e:\\test.txt");
//2. 构建缓冲流,提高效率
BufferedWriter bos = new BufferedWriter(os);
//3. 构建字符打印流
PrintWriter ps = new PrintWriter(bos);
// 数据输出
ps.println(false);//写入boolean型
ps.println("好好学习,天天向上");//写入字符串
ps.println(3);//写入int类型
//关闭流
ps.close();
} catch (Exception e) {
e.printStackTrace();
}
}
针对字节流
,二进制文件二进制
形式保存的,但是使用数据流重新读该文件是可以重新拿到数据的字节序列
是平台无关的)Serializable接口中没有任何的方法
,实现该接口的类不需要实现额外的方法。如果对象的属性是对象,属性对应类也必须实现Serializable接口
ObjectOutputStream对象
内.这时,只需要调用writeObject()
就可以将其序列化,并将其发送给OutputStream必须向下转型
才能进一步使用public class TestSerializable {
public static void main(String[] args) throws Exception {
//写入对象
// 1. 创建一个文件输出流
FileOutputStream fos = new FileOutputStream("e:\\test.txt");
// 2. 这里就直接将fos放入对象流里面了,省略了包装为缓冲流之类的做法
ObjectOutputStream oos = new ObjectOutputStream(fos);
// 3.写对象
oos.writeObject(new Person(1,"root"));
//4.关闭流
if (oos!=null)
oos.close();
// 读取对象
//1. 创建一个文件输入流
FileInputStream fis = new FileInputStream("e:\\test.txt");
//2.创建对象输入流
ObjectInputStream ois = new ObjectInputStream(fis);
//3.读对象 默认得到的Object类型,需要做强制类型转换
Person person = (Person)ois.readObject();
//4. 关闭流
if (ois!=null)
ois.close();
System.out.println(person.toString());
}
}
class Person implements Serializable{
private int id;
private String name;
public Person() {
}
public Person(int id, String name) {
this.id = id;
this.name = name;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
该对象是序列化的
,而且如果该对象中保存的其他对象的引用的话,那么对应的类也必须是可序列化的
也必须是可序列化的。序列化版本不兼容
的问题,一句话描述一下:假设有一个Person类,有id和name属性,首先对其进行了序列化,在反序列化之前,给Person类添加了一个address属性,然后进行反序列化,这样程序就会报错需要注意一点:依旧是类内部有其他对象引用,比如Person类中有一个Student类对象,那么该类对象也得去生成序列化号