天道酬勤,学无止境

万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)

万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)

前言

在上一篇的文章获取不错的浏览量后,继续加更的念头一直徘徊在心中,本来是想花段时间深入学习tomcat的,可是tomcat的源码中就有至关重要的NIO,于是得先整一下NIO,但是NIO的基础是BIO,于是这篇文章就先写IO流吧。

学习NIO(非阻塞IO),千万不能被IO阻塞住哇!

IO流在java中其实是很重要的一块知识点,难度还好,但是容易被忽视,因为工作中真正写IO的代码少之又少。

IO的难点在于

IO流api很多,各种基础的流,包装的流嵌套使用很难记忆
基本每个方法都要抛出非运行时异常
导致很多开发学过io流一段时间后,写不出一段正确的io流代码。

本文将从理论+代码的方式由浅入深的带大家学习IO流,通过图解的方式来记忆常用的IO流。

文末有IO总结的思维导图,很多博文采用的都是上来一张图,我觉得对于阅读者来说很容易陷进去,所以建议理清各个流后再去看思维导图。

万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)
File
在正式的介绍IO流之前,我觉得应该介绍一下File类,该类主要是对文件和目录的抽象表示,因为学习io流第一反应就是文件,该类提供了对文件的创建、删除、查找等操作。主要有以下特点

  1. java的世界万物皆对象,文件和目录就可抽象为File对象
  2. 对于File而言,封装的并不是真正的文件,封装的仅仅是一个路径名,磁盘文件本身可以存在,也可以不存在
  3. 文件的内容不能用File读取,而是通过流来读取,File对象可以作为流的来源地和目的地
    File类的常用构造方法

万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)
如下示例,表示这几种文件和目录的代码

万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)


// pathname
File liuBei = new File("D:/三国/刘备.jpg");
// String parent, String child
File guanYu = new File("D:/三国", "关羽.jpg");
// 目录
File sanGuo = new File("D:/三国");
// File parent, String child
File zhangFei = new File(sanGuo, "张飞.txt");
// 可以声明不存在的文件
File zhuGeLiang = new File(sanGuo, "诸葛亮.txt");

绝对路径和相对路径
绝对路径:从盘符开始的路径,表示一个完整的路径。(经常使用) 相对路径:相对于当前项目目录的路径


File f = new File("D:/bbb.java");
// D:\bbb.java
System.out.println(f.getAbsolutePath());
File f2 = new File("bbb.java");
// F:\code\ad\bbb.java
System.out.println(f2.getAbsolutePath());

路径分隔符和换行符

  1. 路径分隔符

  2. windows的路径分隔符: \
    linux的路径分隔符: /
    java有常量separator表示路径分隔符

public static final String separator = "" + separatorChar;

换行符

  1. windows的换行符: \r\n
  2. linux的换行符 \n
    File的常用方法
    创建、删除

万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)
上述方法比较简单,其中需要注意的是

  • 创建多级目录时,mkdir创建失败,返回false,mkdirs创建成功,返回true(推荐使用mkdirs)
  • 删除目录时,目录内不为空时,删除失败,返回false, 即只能删除文件或者空目录

File shuiHu = new File("D:/四大名著/水浒传");
// 返回false 创建失败
boolean mkdir = shuiHu.mkdir();
// 返回true 创建失败
boolean mkdirs = shuiHu.mkdirs();

File four = new File("D:/四大名著");
// 返回false 删除目录时必须目录为空才能删除成功
boolean delete = four.delete();

File shuiHu = new File("D:/四大名著/水浒传");
// true 正确删除了水浒传目录
boolean delete1 = shuiHu.delete();

File liuBei = new File("D:/三国/刘备.jpg");
// 返回true 正确删除了刘备.jpg文件
boolean delete2 = liuBei.delete();

文件检测
万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)
注意的是

  • 文件或目录不存在时, isDirectory() 或 isFile() 返回false
  • 可读、可写、可执行是对操作系统给文件赋予的权限
    File xiYou = new File("D:/西游记");
    // 文件或目录不存在时 返回false
    System.out.println(xiYou.isDirectory());
    文件获取

万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)
注意

  • length() 返回的是文件的字节数,目录的 长度是0
  • getPath()在用绝对路径表示的文件时相同,用相对路径表示的文件时不同
  • listFiles和list方法的调用,必须是实际存在的目录,否则返回null
  • listFiles和list 可以传入FilenameFilter的实现类,用于按照文件名称过过滤文件
File shuiHu = new File("D:/水浒传");
// 0
System.out.println(shuiHu.length());
File liuBei = new File("D:/三国/刘备.jpg");
// 24591
System.out.println(liuBei.length());

File f = new File("D:/bbb.java");
 // D:\bbb.java
System.out.println(f.getPath());

File f2 = new File("bbb.java");
// bbb.java
System.out.println(f2.getPath());

File sanGuo2 = new File("D:/三国2");
// 该目录不存在,返回null
String[] list = sanGuo2.list();

过滤文件的接口


@FunctionalInterface
public interface FilenameFilter {
   // 参数为目录和指定过滤名称
    boolean accept(File dir, String name);
}

扩展(由读者自己实现)

读取目录下所有的文件以及目录,包括子目录下所有的文件及目录

IO流
上一章节学习了使用File类创建、查找、删除文件,但是无法读取、传输文件中的内容。

IO流主要是读取、传输、写入数据内容的。

I: input,O:output

这里的主体说的都是程序(即内存),从外部设备中读取数据到程序中 即为输入流,从程序中写出到外部程序中即为输出流

万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)
IO的分类

  • 本地IO和网络IO

本地IO主要是操作本地文件,例如在windows上复制粘贴操作文件,都可以使用java的io来操作

网络IO主要是通过网络发送数据,或者通过网络上传、下载文件,我们每天上网无时无刻不在体验着IO的传输

  • 按流向分,输入流和输出流

  • 按数据类型分:字节流和字符流

  • 按功能分:节点流和处理流

  • 程序直接操作目标设备的类称为节点流
  • 对节点流进行装饰,功能、性能进行增强,称为处理流
    IO流主要的入口是数据源,下面列举常见的源设备和目的设备

源设备

  1. 硬盘(文件)
  2. 内存(字节数组、字符串等)
  3. 网络(Socket)
  4. 键盘(System.in)
    目的设备

  5. 硬盘(文件)
  6. 内存(字节数组、字符串等)
  7. 网络(Socket)
  8. 控制台(System.out)
    本文先探讨本地IO的字节流和字符流,先列举字节流和字符流的公共方法

万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)
注意:

  • 程序中打开的IO资源不属于内存资源,垃圾回收机制无法回收该资源,需要显式的关闭文件资源
  • 下面的代码示例中就不显示的调用close方法,也不会处理IOException,只是为了代码的简洁,方便阅读
    字节流
    一切皆为字节

一切文件数据(文本、图片、视频等)在存储时,都是以二进制的形式保存,都可以通过使用字节流传输。

InputStream是字节输入流的顶层抽象

// Closeable有close()方法
public abstract class InputStream implements Closeable {}

核心方法如下

万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)
OutputStream是字节输出流的顶层抽象

// Flushable里面有flush()方法
public abstract class OutputStream implements Closeable, Flushable {}

核心方法如下

万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)
文件节点流
InputStream有很多的实现类,先介绍下文件节点流,即目标设备是文件,输入流和输出流对应的是

FileInputStream和FileOutputStream

FileInputStream主要从磁盘文件中读取数据,常用构造方法如下


public FileInputStream(File file) throws FileNotFoundException{}
public FileInputStream(String name) throws FileNotFoundException{};

当传入的文件不存在时,运行时会抛出FileNotFoundException异常

1.read()方法读取


File file = new File("D:/三国/诸葛亮.txt");
FileInputStream fileInputStream = new FileInputStream(file);

// 核心代码
int b;
while ((b = fileInputStream.read()) != -1 ){
    System.out.print((char) b);
}

// 输出结果
abcde

2.read(byte[])读取

File file = new File("D:/三国/诸葛亮.txt");
FileInputStream fileInputStream = new FileInputStream(file);

// 核心代码
byte[] data = new byte[2];
while (fileInputStream.read(data) != -1) {
    System.out.println(new String(data));
}

// 输出结果
ab
cd
ed

上述代码由于最后一次读取时,只读取一个字节 e ,数组中还是上次的数据cd,只替换了e,所以最后输出了ed

下面是使用FileInputStream读取的正确姿势


File file = new File("D:/三国/诸葛亮.txt");
FileInputStream fileInputStream = new FileInputStream(file);

// 核心代码
byte[] data = new byte[2];
int len;
while ((len = fileInputStream.read(data)) != -1) {
    // len 为每次读取的有效的字节个数
    System.out.println(new String(data, 0, len));
}

// 输出结果
ab
cd
e

注意:使用数组读取,每次读取多个字节,减少了系统间的IO操作次数,从而提高了效率,建议使用

源码解析


public int read() throws IOException {
    return read0();
}
private native int read0() throws IOException;

public int read(byte b[]) throws IOException {
   return readBytes(b, 0, b.length);
}
private native int readBytes(byte b[], int off, int len) throws IOException;

上面列了read()和read(byte[])的源码,可见都是调用native的方法,涉及底层的系统调用。

  • 如果用read()读取文件,每读取一个字节就要访问一次硬盘,这种效率较低。

  • 如果用read(byte[])读取文件,一次读取多个字节,当文件很大时,也会频繁访问硬盘。如果一次读取超多字节,效率也不会高。

FileOutputStream主要是向磁盘文件中写出数据,常用构造方法如下

万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)
注意:

  • 上述构造方法执行后,如果file不存在,会自动创建该文件

  • 如果file存在,append没有传或者传了false,会清空文件的数据

  • 如果file存在,append传了true,不会清空文件的数据
File file = new File("D:/三国/赵云.txt");
FileOutputStream fos = new FileOutputStream(file);
FileOutputStream fos1 = new FileOutputStream("D:/三国/司马懿.txt");
// 上述代码中执行完后,赵云.txt和司马懿.txt都会自动创建出来

向文件写数据


FileOutputStream fos = new FileOutputStream("D:/三国/司马懿.txt");
fos.write(96);
fos.write(97);
fos.write(98);
// 文件内容为 abc

FileOutputStream fos = new FileOutputStream("D:/三国/赵云.txt");
fos.write("三国赵云".getBytes());
// 文件内容为 三国赵云

上述代码每执行一次,文件里的内容就会被覆盖,有时候这不是我们想要的场景,我们一般是想追加文件

FileOutputStream fos = new FileOutputStream("D:/三国/赵云.txt", true);
fos.write("有万夫不当之勇".getBytes());
fos.close();
// 文件内容为 三国赵云有万夫不当之勇

应用场景

开发中涉及文件的上传、下载、传输都是用的这个节点流,会结合装饰后的处理流一起使用,在缓冲流部分有介绍。

扩展(由读者自己实现)

利用文件节点流实现文件的复制

内存节点流
ByteArrayInputStream是从内存的字节数组中读取数据


public ByteArrayInputStream(byte buf[]) {}

注意:不需要close数据源和抛出IOException,因为不涉及底层的系统调用

ByteArrayOutputStream是向内存字节数组中写数据,内部维护了一个数组

public ByteArrayOutputStream() {
   // 内部维护了一个可变的字节数组
   // protected byte buf[];
    this(32);
}

内存节点流代码示例


ByteArrayInputStream bis = new ByteArrayInputStream("data".getBytes());
ByteArrayOutputStream bos = new ByteArrayOutputStream();

int len = 0;
while ((len = bis.read()) != -1){
    bos.write(len);
}
// 输出data
System.out.println(new String(bos.toByteArray()));


应用场景

  1. 内存操作流一般在一些生成临时信息时会被使用,如果临时信息保存着文件中,代码执行完还要删除文件比较麻烦
  2. 结合对象流,可以实现对象和字节数组的互转
    字符流
    字符流封装了更加适合操作文本字符的方法

Reader用于读取文本字符

public abstract class Reader implements Readable, Closeable {}

核心方法

万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)
Writer用于写出文本字符


public abstract class Writer implements Appendable, Closeable, Flushable {}

核心方法

万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)
文件节点流
字符流操作纯文本字符的文件是最合适的,主要有FileReader和FileWriter

FileReader主要是向磁盘文件中写出数据,常用构造方法如下

public FileReader(String fileName) throws FileNotFoundException{}
public FileReader(File file) throws FileNotFoundException {}

注意:当读取的文件不存在时,会抛出FileNotFoundException,这点和FileInputStream一致

  1. read()循环读取文件

FileReader fileReader = new FileReader("D:/三国/赵云.txt");
int b;
while ((b = fileReader.read()) != -1) {
    System.out.println((char) b);
}

2.read(char[]) 读取文件

FileReader fileReader = new FileReader("D:/三国/赵云.txt");
int len;
char[] data = new char[2];
while ((len = fileReader.read(data)) != -1) {
    System.out.println(new String(data, 0, len));
}
// 两个字符两个字符依次读取

FileWriter构造方法如下,和FileOutStream构造方法类似,和FileOutputStream类似。

public FileWriter(String fileName) throws IOException {}
public FileWriter(String fileName, boolean append) throws IOException {}
public FileWriter(File file) throws IOException{}
public FileWriter(File file, boolean append) throws IOException {}

常用的写数据进文件的方法

FileWriter fileWriter = new FileWriter("D:/三国/孙权.txt");
fileWriter.write(97); 
fileWriter.write('b'); 
fileWriter.write('C'); 
fileWriter.write("权"); 
fileWriter.append("力");

注意:

* 如果不执行close()或者flush()方法,数据只是保存到缓冲区,不会保存到文件。这点和与FileOutputStream不同,原因见 字节流和字符流的共同点章节
应用场景

纯文本文件的io操作,配合处理流一起实现。

内存节点流
字符流也有对应的内存节点流,常用的有StringWriter和CharArrayWriter

StringWriter是向内部的StringBuffer对象写数据。

// 定义
public class StringWriter extends Writer {

    private StringBuffer buf;

    public StringWriter() {
        buf = new StringBuffer();
        lock = buf;
    }
}

// 应用
StringWriter sw = new StringWriter();
sw.write("hello");

StringBuffer buffer = sw.getBuffer();
// 输出hello
System.out.println(buffer.toString());

CharArrayWriter是向内部的char数组写数据

// 定义
public class CharArrayWriter extends Writer {
    protected char buf[];
}

// 应用 
CharArrayWriter caw = new CharArrayWriter();
caw.write("hello");
char[] chars = caw.toCharArray();
for (char c : chars) {
 // 输出了h e l l o
 System.out.println(c);
}

四种常用节点流的使用总结

万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)
字节流和字符流的共同点
注意到,OutputStream、Reader、Writer都实现了Flushable接口,Flushable接口有flush()方法

flush():强制刷新缓冲区的数据到目的地,刷新后流对象还可以继续使用

close(): 强制刷新缓冲区后关闭资源,关闭后流对象不可以继续使用

缓冲区:可以理解为内存区域,程序频繁操作资源(如文件)时,性能较低,因为读写内存较快,利用内存缓冲一部分数据,不要频繁的访问系统资源,是提高效率的一种方式

具体的流只要内部有维护了缓冲区,必须要close()或者flush(),不然不会真正的输出到文件中

处理流
上面的章节介绍了字节流和字符流的常用节点流,但是真正开发中都是使用更为强大的处理流

处理流是对节点流在功能上、性能上的增强

字节流的处理流的基类是FilterInputStream和FilterOutputStream

缓冲流(重点)
前面说了节点流,都是直接使用操作系统底层方法读取硬盘中的数据,缓冲流是处理流的一种实现,增强了节点流的性能,为了提高效率,缓冲流类在初始化对象的时候,内部有一个缓冲数组,一次性从底层流中读取数据到数组中,程序中执行read()或者read(byte[])的时候,就直接从内存数组中读取数据。

分类

字节缓冲流:BufferedInputStream , BufferedOutputStream

字符缓冲流:BufferedReader , BufferedWriter

字节缓冲流
可见构造方法传入的是节点流,是对节点流的装饰


// 内部默认8192 =8*1024 即8M的缓冲区
public BufferedInputStream(InputStream in) {
   // 8192    
   // 内部维护了下面这样的字节数组
    // protected volatile byte buf[];
    this(in, DEFAULT_BUFFER_SIZE);
}
public BufferedOutputStream(OutputStream out) {
        this(out, 8192);
}

这里使用复制一部1G的电影来感受缓冲流的强大

使用基本的流读取数据(一次传输一个字节)
long start = System.currentTimeMillis();
FileInputStream fis = new FileInputStream("D:/三国/视频.mp4");
FileOutputStream fos = new FileOutputStream("D:/三国/拷贝.mp4");

int data;
while ((data = fis.read()) != -1) {
    fos.write(data);
}
log.info("拷贝电影耗时:{}ms", System.currentTimeMillis() - start);
// 五分钟还没拷好,关闭程序了...

使用基本的流读取数据(一次传输一个8M的字节数组)


long start = System.currentTimeMillis();
FileInputStream fis = new FileInputStream("D:/三国/视频.mp4");
FileOutputStream fos = new FileOutputStream("D:/三国/拷贝.mp4");

int len;
byte[] data = new byte[1024 * 1024 * 1024];
while ((len = fis.read(data)) != -1) {
    fos.write(data, 0, len);
}
log.info("拷贝电影耗时:{}ms", System.currentTimeMillis() - start);
// 拷贝电影耗时:4651ms

使用缓冲流读取数据(一次传输一个字节)

long start = System.currentTimeMillis();
BufferedInputStream fis = new BufferedInputStream(new FileInputStream("D:/三国/视频.mp4"));
BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream("D:/三国/拷贝.mp4"));

int data;
while ((data = fis.read()) != -1) {
    fos.write(data);
}

log.info("拷贝电影耗时:{}ms", System.currentTimeMillis() - start);
// 拷贝电影耗时:39033ms

使用缓冲流读取数据(一次传输一个8M的字节数组)(最常使用)


long start = System.currentTimeMillis();
BufferedInputStream fis = new BufferedInputStream(new FileInputStream("D:/三国/视频.mp4"));
BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream("D:/三国/拷贝.mp4"));

int len;
byte[] data = new byte[8 * 1024];
while ((len = fis.read(data)) != -1) {
    fos.write(data, 0, len);
}

log.info("拷贝电影耗时:{}ms", System.currentTimeMillis() - start);
// 拷贝电影耗时:1946ms
由上述四个例子可以得出结论,缓冲流读取数据比普通流读取数据快很多!

注意:采用边读边写的方式,一次传输几兆的数据效率比较高,如果采用先把文件的数据都读入内存,在进行写出,这样读写的次数是较小,但是占用太大的内存空间,一次读太大的数据也严重影响效率!

字符缓冲流
对字符节点流的装饰,下面是字符缓冲流的构造方法

public BufferedReader(Reader in) {
 // private static int defaultCharBufferSize = 8192;
 // 内部维护了一个字符数组
    // private char cb[];
    this(in, defaultCharBufferSize);
}

public BufferedWriter(Writer out) {
        this(out, defaultCharBufferSize);
}

字符缓冲流的特有方法

万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)


// 创建流对象
BufferedReader br = new BufferedReader(new FileReader("D:/三国/赵云.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("D:/三国/赵子龙.txt"));
String line = null;
while ((line = br.readLine())!=null) {
  System.out.println(line);
  bw.write(line);
  bw.newLine();
}
// 结果
我乃常山赵子龙
于万军从中,取上将首级

缓冲流的正确姿势
缓冲流是IO流中最重要的知识点,下面通过代码实现正确用IO流的姿势

BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
    bis = new BufferedInputStream(new FileInputStream(new File("D:/三国/视频.mp4")));
    bos = new BufferedOutputStream(new FileOutputStream(new File("D:/三国/拷贝.mp4")));
    int len;
    // 一次传输8M的文件,实际测试这里传输的大小并不影响传输的速度
    byte[] data = new byte[8 * 1024];
    while ((len = bis.read(data)) != -1) {
        bos.write(data, 0, len);
    }
} catch (IOException e) {
    log.error("error", e);
} finally {
   // finally块中关闭流,确保资源一定被关闭
    if (bis != null) {
        try {
            bis.close();
        } catch (IOException e) {
            log.error("error", e);
        }
    }
    if (bos != null) {
        try {
            bos.close();
        } catch (IOException e) {
            log.error("error", e);
        }
    }
}

转换流
字符编码与字符集
字符编码

计算机存储的数据都是二进制的,而我们在电脑上看到的数字、英文、汉字等都是二进制转换的结果

  • 将字符转换成二进制,为编码

  • 将二进制转换为字符,为解码

字符编码 就是 自然语言和二进制的对应规则

字符集

就是一个编码表,常见的字符集有ASCII字符集、GBK字符集、Unicode字符集等,具体各个编码的介绍在这里就不介绍了。

图片
IDEA中
IDEA中,使用 FileReader 读取项目中的文本文件。IDEA可以设置为GBK 编码,当读取Windows系统中创建的默认的UTF8文本文件时,就会出现乱码 。

如下例

万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)
idea的字符集设置 默认是UTF-8,这里修改为GBK

万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)
运行的代码及结果


FileReader fileReader = new FileReader("D:/sanguo/utf8.txt");
int read;
while ((read = fileReader.read()) != -1) {
    System.out.print((char)read);
}
// 浣犲ソ

InputStreamReader
Reader的子类,读取字节,并使用指定的字符集将其解码为字符。字符集可以自己指定,也可以使用平台的默认字符集。

构造方法如下


// 使用平台默认字符集
public InputStreamReader(InputStream in) {}
// 指定字符集
public InputStreamReader(InputStream in, String charsetName)
        throws UnsupportedEncodingException{}

读取文件的“你好",文件默认的字符集是UTF8

// 创建流对象,默认UTF8编码
InputStreamReader isr = new InputStreamReader(new FileInputStream("D:/三国/utf8.txt"));
// 创建流对象,指定GBK编码
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("D:/三国/utf8.txt"), "GBK");

int read;
while ((read = isr.read()) != -1) {
    System.out.println((char) read);
}

while ((read = isr2.read()) != -1) {
    System.out.println((char) read);
}

// 输出结果
你好
浣犲ソ

OutputStreamWriter
Writer的子类,使用指定的字符集将字符编码为字节。字符集可以自己指定,也可以使用平台的默认字符集。

构造方法如下

// 使用平台默认字符集
public OutputStreamWriter(OutputStream out) {}
// 使用平台默认字符集
public OutputStreamWriter(OutputStream out, String charsetName)
throws UnsupportedEncodingException{}

如下面的代码,将你好写入文件。写入后两个文件的字符集不一样,文件大小也不同

// 创建流对象,默认UTF8编码
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:/三国/黄忠.txt"));
osw.write("你好"); // 保存为6个字节

// 创建流对象,指定GBK编码
OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream("D:/三国/马超.txt"),"GBK");
osw2.write("你好");// 保存为4个字节

万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)
对象流
序列化
jdk提供了对象序列化的方式,该序列化机制将对象转为二进制流,二进制流主要包括对象的数据、对象的类型、对象的属性。可以将java对象转为二进制流写入文件中。文件会持久保存了对象的信息。

同理,从文件中读出对象的信息为反序列化的过程

对象想序列化,满足的条件:

  1. 该类必须实现 java.io.Serializable 接口, Serializable 是一个标记接口(没有任何抽象方法),不实现此接口的类将不会使任何状态序列化或反序列化,会抛出 NotSerializableException 。
  2. 该类的所有属性必须是可序列化的,如果有一个属性不需要可序列化的,则该属性使用transient 关键字修饰
    ObjectOutputStream
    该类实现将对象序列化后写出到外部设备,如硬盘文件

public ObjectOutputStream(OutputStream out) throws IOException{}
常用方法

万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)
如下代码,将User对象写入文件中

public class User implements Serializable {
    private static final long serialVersionUID = 8289102797441171947L;

    private String name;
    private Integer age;
}
// 下面是将对象输出到文件的核心代码
User user = new User("马超",20);
// 创建序列化流对象
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("D:/三国/马超.txt"));
// 写出对象
out.writeObject(user);

注意:

1.实现了Serializable的实体一定要加一个serialVersionUID变量,这也是习惯问题,idea可以设置一下。
2.serialVersionUID生成后不要改变,避免反序列化失败,改变后会抛出InvalidClassException异常
万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)
生成的文件内容如下

万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)
ObjectInputStream
该类将ObjectOutputStream写出的对象反序列化成java对象

public ObjectInputStream(InputStream in) throws IOException 

常用方法
万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)


ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:/三国/马超.txt"));
// 强转为user
User user = (User) in.readObject();
System.out.println(user);
// 输出内容
User(name=马超, age=20)

对象和字节数组的转换
利用对象流和字节数组流结合 ,可以实现java对象和byte[]之间的互转

// 将对象转为byte[]
public static <T> byte[] t1(T t) {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(t);
    return bos.toByteArray();
}
// 将byte[]转为对象
public static <T> T t2(byte[] data) throws IOException, ClassNotFoundException {
    ByteArrayInputStream bos = new ByteArrayInputStream(data);
    ObjectInputStream oos = new ObjectInputStream(bos);
    return (T) oos.readObject();
}

管道流(了解)
管道流主要用于两个线程间的通信,即一个线程通过管道流给另一个线程发数据

注意:线程的通信一般使用wait()/notify(),使用流也可以达到通信的效果,并且可以传递数据

使用的类是如下

  • PipedInputStream和PipedOutStream
  • PipedReader和PipedWriter
    这里使用字节流为例

class Sender implements Runnable {
private PipedOutputStream pos;
private String msg;

public Sender(String msg) {
    this.pos = new PipedOutputStream();
    this.msg = msg;
}

@Override
public void run() {
   pos.write(msg.getBytes());
}

public PipedOutputStream getPos() {
    return pos;
}

}

class Receiver implements Runnable {
private PipedInputStream pis;

public Receiver() {
    this.pis = new PipedInputStream();
}

@Override
public void run() {
     byte[] data = new byte[1024];
        int len;
        while ((len = pis.read(data)) != -1) {
            System.out.println(new String(data, 0, len));
        }
}

}

Sender sender = new Sender("hello");
Receiver receiver = new Receiver();
receiver.getPis().connect(sender.getPos());
new Thread(sender).start();
new Thread(receiver).start();
// 控制台输出 hello


**输入与输出流(了解)**
System.in和System.out代表了系统标准的输入、输出设备

默认输入设备是键盘,默认输出设备是控制台

可以使用System类的setIn,setOut方法对默认设备进行改变

我们开发中经常使用的输出到控制台上的内容的方法。

System.out.println("a");
System.out.print("b");
class System{
public final static InputStream in = null;
public final static PrintStream out = null;
}
public PrintStream(String fileName) throws FileNotFoundException{}


**数据流(了解)**
主要方便读取Java基本类型以及String的数据,有DataInputStream 和 DataOutputStream两个实现类

DataOutputStream dos = new DataOutputStream(new FileOutputStream("D:/三国/周瑜.txt"));
dos.writeUTF("周瑜");
dos.writeBoolean(false);
dos.writeLong(1234567890L);

DataInputStream dis = new DataInputStream(new FileInputStream("D:/三国/周瑜.txt"));
String s = dis.readUTF();
System.out.println(s);
boolean b = dis.readBoolean();
System.out.println(b);
// 输出
周瑜
false



**IO流总结**
以上各个章节详细介绍了各个流,可见流的种类比较多,记忆确实增加了困难。但是可以通过思维导图的方式整理出来,方便记忆。

字节流的导图

![](https://s4.51cto.com/images/blog/202105/05/7be91e46c2a4598dc18992d50385f9f1.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
字符流的导图

![](https://s4.51cto.com/images/blog/202105/05/1161ed20623045327ecdfa42ccef53ab.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
按照功能划分

![](https://s4.51cto.com/images/blog/202105/05/189c51e97776b8f9985df610e2554321.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
输入、输出对应关系

![](https://s4.51cto.com/images/blog/202105/05/9ea492325909a2dee0763c834bdf0521.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
**结语**
短期的加更计划

1.NIO
2.tomcat系列源码解析
文章篇幅较长,给看到这里的小伙伴点个大大的赞!由于作者水平有限,文章中难免会有错误之处,欢迎小伙伴们反馈指正。

如果觉得文章对你有帮助,麻烦 点赞、评论、转发、在看 、关注 走起

你的支持是我加更最大的动力!!!

另
琐碎时间想看一些技术文章,可以去公众号菜单栏翻一翻我分类好的内容,应该对部分童鞋有帮助。同时看的过程中发现问题欢迎留言指出,不胜感谢~。另外,有想多了解哪些方面内容的可以留言(什么时候,哪篇文章下留言都行),附菜单栏截图(PS:很多人不知道公众号菜单栏是什么)
![](https://s4.51cto.com/images/blog/202105/05/99a2296945e08ff078233d26f265d9e7.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
标签

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • 程序员进入大厂至少需要学习哪些知识,一张思维导图帮你搞定
    程序员进入大厂至少需要学习哪些知识,一张思维导图帮你搞定 来源:https://blog.csdn.net/m0_55379894/article/details/115287352
  • 看完不会的来打我!我离职后面试收割小米等大厂offer,灵魂拷问
    缘起 随着Android开发行业逐渐饱和,对Android开发者的面试要求也越来越高,是否掌握底层源码,是面试官衡量一名Android开发者的重要依据。有没有读过源码也可以很大程度上判断你这个人的学习能力和思维方式。无论你开发经验几年,面试被问到源码问题答不出来,不仅会掉身价、砍薪资尚且不谈,甚至连面试都过不了! 网上各类源码解析的文章博客五花八门、良莠不齐。杂乱、要么内容质量太浅,零散、碎片化,总看着看着就衔接不上了。 所以,博主利用闲暇时间,花了近三个月将Android开发中最常用、面试被问频次最高的18类源码整合成了一套系统知识笔记PDF,共计487页,18个章节!相信看完这份文档,你将会对Android开发中的各类核心源码有着更深入、更系统的理解。 由于内容较多,避免影响到大家的阅读体验,在此只截图展示目录部分,487详细完整版的《Android 开发相关源码精编解析》电子书文档领取方式:**点赞+关注,然后私信关键词 【666】**即可加我的个人微信私发给你(无偿)。也欢迎大家找我探讨Android技术问题 这里总结一下,Android入门的时候,需要有一本入门书,好好学习书中的内容,同时花一年时间把Android官方文档中的training和guide看一遍,同时通过写博客和记笔记的方式来做总结,建议让自己的每篇博客都有价值些。通过一年时间的学习
  • 看完不会的来打我!大厂经典高频面试题体系化集合,offer拿到手软
    前言 最近有些朋友提问,Android QQ空间 换肤实现原理是什么?于是,我决定在这里做一下回答。对这个方面感兴趣的朋友也可以来看下。 手q的换肤机制主要是通过拦截系统resource中的sPreloadedDrawables静态缓存变量,把这个缓存变量替换成自定义皮肤资源的变量实现的。 下面说一个简单的实现,如果有什么遗漏的地方,欢迎大家在评论区进行补充。 1、自我介绍。自我介绍其实是个比较关键的过程,这基本上决定了后续环节将会问哪些问题以及初步给你的评分定位(作为无数候选人的面试官,这一点主观上个人也是如此认为)。自我介绍的要点是重点讲述一两个最近自己主要参与或者负责的项目,着重说明自己承担的角色,所负责角色具体所做的事情,思路一定要清晰。 2、完了之后,基本上面试官会对着其擅长和当前尚未解决的或者心存疑虑的问题进行提问或者说相互沟通、交流。因为面试的是架构师,所以纯粹的技术性问题(比如aop,bean生命周期,jdk基本的并发,常见缓存方式等等)已经没有问了,直接就是针对项目里面的具体架构、业务问题进行深入的讨论。 比如整体架构他会问道为什么我要用F5以及LVS而不是其他的。问到的包括对于性能,如何进行排查,通用的答复(比如说我说先看前台还是后台,前台findbugs、chrome f12,后台先看哪台服务器负载高,top、vmstat、sar等等
  • 看完不会的来打我!一个三非渣本的Android校招秋招之路,最全的BAT大厂面试题整理
    大家是不是和我有一样的感觉,不知道什么时候开始,全世界都在问:Android开发凉了吗?就我个人而言,我觉得没有,从我干Android开发开始,就从来没有觉得Android开发在走下坡路。 市场需求 讲道理,Android开发的市场需求还是很大的,因为从岗位招聘情况来看,Android开发岗位的需求量并不比其他岗位少。如果说Android开发凉了,那有些行业应该已经“凉凉三生三世思念成河”了。那是什么原因导致了焦虑呢,我想了想应该是前几年移动开发行业过度火爆,门槛太低导致大家都蜂拥而入,所以现在供应些许不足。Android开发没有凉,只不过确实没之前那么“热”了。 笔记内容概要 一、架构师必备Java基础 1、深入理解Java泛型 2、注解深入浅出 3、并发编程 4、数据传输与序列化 5、Java虚拟机原理 6、高效IO 二、设计思想解读开源框架 1、热修复设计 2、插件化框架设计 3、组件化框架设计 4、图片加载框架 5、网络访问框架设计 6、RXJava响应式编程框架设计 三、360°全方位性能调优 1、设计思想与代码质量优化 2、程序性能优化 启动速度与执行效率优化布局检测与优化内存优化耗电优化网络传输与数据储存优化APK大小优化 3、开发效率优化 分布式版本控制系统Git自动化构建系统Gradle 4、项目实战
  • 小白都能看得懂的教程 看完这篇还不会生成随机验证码图片,你来打我!!!
    小白都能看得懂的教程 一文教你实现生成随机图像验证码 大家好,我叫亓官劼(qí guān jié ),三本计算机在读,目前在积极准备21计算机考研中,同时也在学习后端开发,准备工作。不敢孤注一掷,因为要留条后路;不求两全其美在,因为那需要运气+机遇;只求学有所得,慢慢成长。CSDN中记录学习的点滴历程,时光荏苒,未来可期,加油~ 博主博客文章内容导航(实时更新) 更多优质文章推荐: 收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!一本教你如何在前端实现富文本编辑器小白都能看得懂的教程 一本教你如何在前端实现markdown编辑器一文教会你Bootstrap,让你也可以快速建站一文教你如何白嫖JetBrains全家桶(IDEA/PtChram/CLion)免费正版小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 1)小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 2)小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 3)小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 4)小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 5)小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 6
  • 看完不会的来打我!吃透这份Android高级工程师面试497题解析,面试真题解析
    前言 选了开发这一行,就意味着想混得好就要持续学习,你的技术和薪资、位置直接挂钩,进步对于程序员的重要性就不赘述了,接下来作为过来人,为广大同行分享一些学习干货,希望可以帮到大家 Android相关 Android部分我就不分几大块了。直接列举,但是列举到的每一项都是面试经常会问到并且会延伸问的,所以需要深入的去研究。 1.Android事件分发机制,请详细说下整个流程 2.Android view绘制机制和加载过程,请详细说下整个流程 3.Activty的加载过程 请详细介绍下 4.Activity的启动模式: 5.Activity缓存方法: 6.Service的生命周期,两种启动方法,有什么区别: 7.怎么保证service不被杀死8.广播的两种注册方法,有什么区别。 9.Intent可以传递哪些数据类型10.Json有什么优劣势 11.动画有哪几类,各有什么特点: 12.Handler、Loop消息队列模型,各部分的作用。 13. 怎样退出终止App:自己设置一个Activity的栈,然后一个个finish()。 14. Android IPC:Binder原理 15.android的优化 16.一个singleton如何实现线程的同步问题 17.android重要术语解释 18.理解Window和WindowManager 19.Bitmap的处理: 20.综合技术: 21
  • android常用框架!万字长文轻松彻底入门Flutter,使用指南
    前言 说不焦虑其实是假的,因为无论是现在还是最近几年,很早就有人察觉Android开发的野蛮生长时代已经过去。过去的优势是市场需要,这个技术少有人有,所以在抢占市场的时候,基本上满足需要就已经可以了。但是现在,各式各样的APP层出不穷,APP的质量成为新的竞争标准。这也意味着不管是系统设计还是性能优化等方面,市场都给Android开发者提出了更高的要求。那么,Android是不是真的不吃香了呢?其实不是的,高级及以上还有很大的需求缺口,非常稀缺人才。但是初级开发者真的已经不吃香了。丧气的说一句,初级学者在市场中的竞争优势其实是很少的。 我们一样也可以从Android开发的需求岗位来了解目前的市场情况。 常规电话面试 1 JAVA基础思想:设计模式与面向对象 2 安卓View绘制流程 3 常规的组件问题 4 事件分发机制 5 多线程和安全问题 6 安卓性能优化和兼容问题: 性能优化回答 具体面试 1 线程池原理 2 线程安全有多少种实现方式 3 图片加载框架原理 4 Http 协议原理 5 Okhttp 原理 6 各种内存优化 7 垃圾回收机制原理 8 谈谈对同步请求和异步请求的理解 9 怎么保证同步和异步 10 Intent servise ,底层原理实现 11 Handler 为什么能够进行跨进城通讯 12 Handler 为啥不能在子线程声明,声明后,报运行时异常异常 13
  • 字符编码看不懂你来打我!(ASCII,Unicode,Utf-8,GB2312…)
    这篇文章估计有些年头了,但作者确实将字符编码的发展历程介绍的很到位,偶然看到,突然觉得“相逢恨晚”,故转载——,也当做自己的收藏吧。 字符编码的问题看似很小,经常被技术人员忽视,但是很容易导致一些莫名其妙的问题。这里总结了一下字符编码的一些普及性的知识,希望对大家有所帮助。 1. 还是得从ASCII码说起 说到字符编码,不得不说ASCII码的简史。计算机一开始发明的时候是用来解决数字计算的问题,后来人们发现,计算机还可以做更多的事,例如文本处理。但由于计算机只识“数”,因此人们必须告诉计算机哪个数字来代表哪个特定字符,例如65代表字母‘A’,66代表字母‘B’,以此类推。但是计算机之间字符-数字的对应关系必须得一致,否则就会造成同一段数字在不同计算机上显示出来的字符不一样。因此美国国家标准协会ANSI制定了一个标准,规定了常用字符的集合以及每个字符对应的编号,这就是ASCII字符集(Character Set),也称ASCII码。 当时的计算机普遍使用8比特字节作为最小的存储和处理单元,加之当时用到的字符也很少,26个大小写英文字母还有数字再加上其他常用符号,也不到100个,因此使用7个比特位就可以高效的存储和处理ASCII码,剩下最高位1比特被用作一些通讯系统的奇偶校验。 注意,字节代表系统能够处理的最小单位,不一定是8比特。只是现代计算机的事实标准就是用8比特来代表一个字节
  • 看完不会的来打我!深入理解Flutter动画原理,架构师必备技能
    前言 这里整理的是一些与技术没有直接关系的面试题,但是能够考察你的综合水平,所以不要以为不是技术问题,就不看,往往有时候就是这样一些细节的题目被忽视,而错过了一次次面试机会。 想要成为一名优秀的Android开发,你需要一份完备的知识体系,在这里,让我们一起成长为自己所想的那样。 说到高级工程师,大家的第一印象自然就是工作经验丰富,技术够牛逼。 注意这里说的是工作经验,并不是指工作时间。有的人踏踏实实做项目,业余时间想着多学点新技术,坚持读书,认真写博客总结,多实践,加上自己本身悟性又不差的,2、3 年的时间都可以有。别人 3、5 年的工作经验,成长为公司的高级工程师水到渠成。而有的人混日子,做的项目得过且过,业余时间玩游戏,追剧,也许你在公司工作了 10 年看起来兢兢业业,然而你对自己没有更高的要求,安于现状,不求进步,这类人即使有 10 年的工作时间,却只能止步于此。 初级工程师拿到需求会直接开始做,然后做着做着发现有问题了,要么技术实现不了,要么逻辑有问题。 而高级工程师拿到需求会考虑很多,技术的可行性?对现有业务有没有帮助?对现有技术架构的影响?扩展性如何?等等…之后才会再进行设计编码阶段。 而现在随着跨平台开发,混合式开发,前端开发之类的热门,Android开发者需要学习和掌握的技术也在不断的增加。 通过和一些行业里的朋友交流讨论,以及参考现在大厂面试的要求
  • android面试简历!万字长文总结Android多进程,面试必问
    开头 互联网时代的到来,让我们获取知识变得更加简单,理论上讲只要你想学,便会有不尽的知识等你,只要方法得当,够努力,任何人都可以都有可能成为大牛。 自己在努力的基础上,还学习了一些高效的学习方法,让我在学习的过程中更加高效,更迅速的掌握,以下是我学习Android的一些套路。 Android Jetpack组件的作用是什么? Navigation:一个用于管理Fragment切换的工具类,可视化、可绑定控件、支持动画等是其优点。 Data Binding:不用说,都知道,加速MVVM的创建。 Lifecycle:他是我们能够处理Activity和Fragment的生命周期的重要原因,在AndroidX的Fragment和Activity已经对Lifecycle提供了默认支持。 ViewModel:当做MVVM的ViewModel层,并具有声明周期意识的处理和UI相关的数据。 LiveData:同RxJava的作用一样,对数据进行监听,优点就是无需处理生命周期、无内存泄漏等。 Room:强大的ORM数据库框架。 Paging:易于使用的数据分页库,支持RecyclerView。WorkManager:灵活、简单、延迟和保证执行的后台任务处理库。 你可能选择Android Jetpack的原因 以下可能是你会选择Android Jetpack的原因: 一起使用更方便:因为Android
  • flutter技术栈!万字长文轻松彻底入门Flutter,送大厂面经一份!
    前言 马上快到金三银四都春招阶段了,在这本就是跳槽、找工作的年后黄金时间,大多数求职者都早早做好年后求职的准备,其中不乏有年前早早辞了工作准备年后跳槽的有经验的职场老人们,也有一批即将毕业的应届毕业生的职场新人们。 但是受此次“新冠肺炎”疫情影响之后,“金三银四”逐渐演变成千军万马过独木桥,一边是摩拳擦掌有经验的职场老人们,而另一边则是即将毕业跃跃欲试的新鲜血液,只会让求职人才们越积越多,面对这样岗位少求职者多的情况下,竞争力可想而知,再加上企业的招聘计划调整,侧面也是加剧了求职的难度。 一、java面试题 熟练掌握java是很关键的,大公司不仅仅要求你会使用几个api,更多的是要你熟悉源码实现原理,甚至要你知道有哪些不足,怎么改进,还有一些java有关的一些算法,设计模式等等。 (一) java基础面试知识点 java中==和equals和hashCode的区别 int、char、long各占多少字节数 int与integer的区别 探探对java多态的理解 String、StringBuffer、StringBuilder区别 什么是内部类?内部类的作用 抽象类和接口区别 抽象类的意义 抽象类与接口的应用场景 抽象类是否可以没有方法和属性? 接口的意义 泛型中extends和super的区别 父类的静态方法能否被子类重写 进程和线程的区别 final,finally
  • 聊聊什么是缓存雪崩和缓存穿透
    缓存雪崩 假如一个系统,它在高峰期有每秒7000个请求,这时我们使用缓存抗住了这么高的请求。但如果在某个时间点缓存大量失效,或者缓存服务器挂掉了,那么这些请求就会直接作用在普通数据库中(如MySQL)。这么高的请求量,MySQL肯定抗不住,进而挂掉,数据库一挂,也会导致系统挂掉。 我们总结缓存雪崩触发的条件: “ 高并发情况下 缓存服务器挂了 大量缓存集中失效 ” 导致的后果就是:系统崩溃。 解决思路,对数据库增加限流排队访问,假设我们的数据库最多能抗住每秒2000的请求,那当我们请求数据库时,每秒的请求就需要控制在2000内,其余的请求就需要排队。同时我们要保证缓存服务器的高可用,就需要使用集群。另外我们也需要随机设置缓存key的失效时间,防止key在同一时间失效。 雪崩解决 关于限流操作,可以使用Spring Cloud相关的限流插件,这里我们使用Semaphore信号量模拟下限流操作。 在这里我们限定同时最多可以有5个请求访问,当我们有9个请求进来时,只有5个请求获取到可以访问的令牌,其余的4个请求在外面等待。当前面的访问结束时,会释放令牌,后面的4个请求就会去抢占这些令牌,这样反复执行。 这里我们就起到了限流的操作。 缓存穿透 假设存在一个key永远不会在缓存中存在,当黑客通过这个key去攻击系统,比如每秒发起了7000次攻击,那么无论如何都不会走缓存
  • 博客文章内容导航(实时更新)
    博客文章内容导航(实时更新)   大家好,我是亓官劼(qí guān jié),在博客中分享数据结构与算法、Python全栈开发、Java后端开发、前端、OJ题解及各类报错信息解决方案等经验。一起加油,用知识改变命运,未来可期。   本文为博客内各个文章的导航,基本上是会实时更新的,方便大家查找博主的博客文章。 Flask专栏 收藏!最详细的Python全栈开发指南 看完这篇你还不会Python全栈开发 你来打我!!!Flask基本学习(一)小白都能看得懂的教程 看完这篇还不会生成随机验证码图片,你来打我!!!一文教会你Bootstrap,让你也可以快速建站小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 1)小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 2)小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 3)小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 4)小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 5)小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 6)小白都能看懂的实战教程 手把手教你Python Web全栈开发 (DAY 7)小白都能看得懂的教程 一本教你如何在前端实现富文本编辑器小白都能看得懂的教程
  • Android经典面试:耗时两个礼拜,8000字安卓面试长文,全套教学资料
    程序员、网络工程师、数据库管理员这类人构成了 IT 共和国的主体,这个阶层是十九世纪的产业大军在二十一世纪的再现,只不过劳作的部分由肢体变成大脑,繁重程度却有增无减。在渺如烟海的程序代码和迷宫般的网络软硬件中,他们如二百多年前的码头搬运工般背起重负,如妓女般彻夜赶工。信息技术的发展一日千里,除了部分爬到管理层的幸运儿,其他人的知识和技能很快过时,新的 IT 专业毕业生如饥饿的白蚁般成群涌来,老的人(其实不老,大多三十出头)被挤到一边,被代替和抛弃,但新来者没有丝毫得意,这也是他们中大多数人不算遥远的前景…… 这个阶层被称做技术无产阶级。 你们感觉写的真实吗?其实,回过头去想想,随着时代的发展,从互联网到移动互联网再到所谓的人工智能智能时代,大数据,云计算,世界可能确实也越来越需要程序员,程序员也越来越多,然后再想想我们自身的工作,每天重复的复制,粘贴,确实像码头的搬运工,每天干着苦力,而这个苦是脑子的苦,而不是身体的苦。 而我们Android程序员面临的悲哀不光是中年危机、而是信息技术的更新迭代。突然发现行业已经缩减Android开发岗位了,我们快失业了! 从初中级到高级,移动端程序员的进阶宝典 想要成为一名优秀的Android开发,你需要一份完备的 知识体系,在这里,让我们一起成长为自己所想的那样。 下面我们就以 Android 开发为例,从硬技能和软技能两方面
  • 万字长文总结Android多进程,面试心得体会
    前言 跳槽容易,但想拿大厂的offer可不那么容易。很多小伙伴一直叨叨要跳槽,大大小小的公司面试了很多,但却很难拿到一个满意的offer,要么package太低,要么就是面试被虐。经过多次面试失利之后,方能明白什么叫基础不牢,地动山摇。面试官随便针对一个知识点深入考察一下,就回答不出来,这样还怎么能通过面试? 那么大厂到底面试到底需要哪些技术功底呢?这个就多了,不同公司的技术要求也不一样,但是相同的点在于,大公司对于技术的要求都不会很表面,必然会在一定广度的基础上要求有一定的深度。 独立开发过几个产品,说一下自己的见解 基本上有完整经历过一个产品的开发过程都应该清楚UI层是应用开发中最常变的一部分,app开发者绝大部分的时间都花在UI微调上,而业务逻辑基本上需求定下来不会变动太多,除非是产品迭代一些功能性的追加,不然就是不停的堆UI、堆UI,干过两年以上的开发者基本的开发能力已经有了,业务能力也差不多,这时候会遇到瓶颈,基本上就是找不到更加深入的方向 这个时候就可以考虑一下自己参与过的产品所使用到的技术是自己深究过的 打个比方,开发app的时候,一般会用到Http框架,这样的框架你是否有真正去实践过,或者有研究过它的实现思路和设计思想,我们一般都会拿过来用,但为什么要这样设计就是我们需要去了解的,这可以提高你的架构的设计能力和编码的水平 除了框架
  • 万字长文总结Android多进程,面试心得体会
    前言 跳槽容易,但想拿大厂的offer可不那么容易。很多小伙伴一直叨叨要跳槽,大大小小的公司面试了很多,但却很难拿到一个满意的offer,要么package太低,要么就是面试被虐。经过多次面试失利之后,方能明白什么叫基础不牢,地动山摇。面试官随便针对一个知识点深入考察一下,就回答不出来,这样还怎么能通过面试? 那么大厂到底面试到底需要哪些技术功底呢?这个就多了,不同公司的技术要求也不一样,但是相同的点在于,大公司对于技术的要求都不会很表面,必然会在一定广度的基础上要求有一定的深度。 独立开发过几个产品,说一下自己的见解 基本上有完整经历过一个产品的开发过程都应该清楚UI层是应用开发中最常变的一部分,app开发者绝大部分的时间都花在UI微调上,而业务逻辑基本上需求定下来不会变动太多,除非是产品迭代一些功能性的追加,不然就是不停的堆UI、堆UI,干过两年以上的开发者基本的开发能力已经有了,业务能力也差不多,这时候会遇到瓶颈,基本上就是找不到更加深入的方向 这个时候就可以考虑一下自己参与过的产品所使用到的技术是自己深究过的 打个比方,开发app的时候,一般会用到Http框架,这样的框架你是否有真正去实践过,或者有研究过它的实现思路和设计思想,我们一般都会拿过来用,但为什么要这样设计就是我们需要去了解的,这可以提高你的架构的设计能力和编码的水平 除了框架
  • Android高级工程师面试实战,万字长文总结Android多进程,实战解析
    开头 眼看着金九银十就快来了,各大厂也开始了新一轮的招聘计划,尤其是腾讯前一段时间爆出了一个大消息: 将正式启动2021届秋季招聘,加大对数字经济和产业互联网人才的挖掘培养。 在本次招聘中,特别面向2021年应届毕业生开放5000个岗位,也是腾讯有史以来最大规模的校招,总招聘量预计较去年提升42%。 这的确是一个千载难逢的好机会,想圆“大厂梦”的21届毕业生注意了,好好把握住这个机会呀。 为了帮助我们Android领域的新人,我在牛客,CSDN上泡了一段时间。操千曲而后晓声,观千剑而后识器。我看过很多人分享的关于腾讯的面经后,发现大家对于性能优化这个方面存在一些知识漏洞。 于是,我花了几天整理了一份关于性能优化的资料,下面这些关于性能优化的知识点大家不能不看呀,了解了的就当复习一遍,看看自己是不是记住了,不了解的就赶紧下载后当手机屏保吧。 1、Java se基础 1).Java基本数据类型与表达式,分支循环。 2).String和StringBuffer的使用、正则表达式。 3).面向对象的抽象,封装,继承,多态,类与对象,对象初始化和回收;构造函数、this关键字、方法和方法的参数传递过程、static关键字、内部类。 4).对象实例化过程、方法的覆盖、final关键字、抽象类、接口、继承的优点和缺点剖析;对象的多态性:子类和父类之间的转换、抽象类和接口在多态中的应用
  • 看完不会的来打我!想给金三银四找工作的程序员几点建议,社招面试心得
    前言 今年是转折的一年,很多学android开发的小伙伴失业了,虽找到了一份工作,但高不成低不就,下半年金九银十有想法更换一份工作,很多需要大厂面试经验和大厂面试真题的小伙伴,想提前准备刷下题。接下来分享一份我的字节跳动、阿里巴巴、百度、小米等大厂面试经验和总结。(文末附真题解析大全) 一线企业的app都是多线程和多进程的,而Android进程间通信机制就是Binder,原生的线程间通信则是Handler,Binder和Handler是了解安卓运行机制必须要掌握的一个知识点,更是一线企业面试必问的知识点! 以下几道就是大厂关于和Binder常见的面试真题: Binder有什么优势?(字节跳动)Binder是如何做到一次拷贝的?(腾讯)MMAP的原理讲解;(腾讯)为什么Intent不能传递大数据?(阿里)描述AIDL生成的java类细节;(字节跳动)四大组件底层的通信机制;(字节跳动)为什么Intent不能传递大数据?(阿里)Binder机制是如何跨进程的?Binder机制原理 为了让大家更好的掌握Framework与Binder的原理,今天分享一份 Android Framework 高频面试题总结 和 BATJ 大牛笔记Android Framework 内核解析,看完之后不信你还不掌握Framework和Binder。 Android Framework 高频面试题解析 1
  • 看完不会的来打我!你会的还只有初级安卓工程师的技术吗?含泪整理面经
    不是安卓不行了,是你跟不上了 我的很多读者都在反馈说,现在一个岗位可以收到的简历数,是前几年的几倍。我们必须承认,僧多粥少就是 Android 行业的现状,别说初中级工程师,就是高级工程师也是一抓一大把。企业招人的眼光也越来越高,如果你没点“真东西”,是真的挺难的。 前几天和一位 Android 前辈聊天,他说面试中很多工程师,对特别基础的问题都停留在“使用过”或者“听说过”。没有深入到技术细节里,在竞争激烈的市场中就只能被淘汰。 下面这些,是他提到的几个问题,你能快速回答上么? SoftReference(软引用)在内存不足时会被虚拟机回收,那它会不会导致 OOM?Android 的 Touch 事件中 CANCEL 事件是如何产生的?哪些场景下会发生 CANCEL 事件?Handler 中的 Looper 无限循环,为什么没有阻塞UI主线程?你在简历中写“精通多线程”,那么:线程中自己的“工作内存”指的是什么? 为什么阿里开发规范中不允许使用 Executors 创建线程池? 你一定想说“面试造火箭,工作拧螺丝”。实际上,你的答案反映了你对技术理解的深度,以及解决问题的能力。 听起来很套路,但企业需要的是能对自己提交代码负责、对使用每一个工具负责的「高级/资深 Android 开发」。 我认识很多优秀的 Android 工程师,他们丝毫不焦虑
  • Android开发必须掌握!万字长文总结Android多进程,看看这篇文章吧!
    很多程序员都有这样的觉悟;找工作学历是敲门砖,没有211,985起步的学历,想进一家大公司不太可能。 举个例子好了; 如果你是大厂面试官,参与面试的有10个刚刚毕业没有工作经验的普通学校应届生,还有10个刚刚毕业的985应届生,而你们只打算招5个人实习。 你是看学历,还是看能力? 我想正常面试官都会挑选学历好的。因为在工作之前,学历在很大程度上就已经代表了你的能力 Java相关 无论什么级别的Android从业者,Java作为Android开发基础语言。不管是工作还是面试中,Java都是必考题。如果不懂Java的话,薪酬会非常吃亏(美团尤为重视Java基础) 详细介绍了Java泛型、注解、并发编程、数据传输与序列化、高效IO、容器集合、反射与类加载以及JVM重点知识线程、内存模型、JVM运行时内存、垃圾回收与算法、Java中四种引用类型、GC 分代收集算法 VS 分区收集算法、GC 垃圾收集器、JAVA IO/NIO 、JVM 类加载机制的各大知识点。 详细知识点太多,文案过长可见《Android核心知识体系》 JVM基本概念: JVM 是可运行 Java 代码的假想计算机 ,包括一套字节码指令集、一组寄存器、一个栈、 一个垃圾回收,堆 和 一个存储方法域。JVM 是运行在操作系统之上的,它与硬件没有直接 的交互。 Android框架体系架构 详细介绍了高级UI