结语
小编也是很有感触,如果一直都是在中小公司,没有接触过大型的互联网架构设计的话,只靠自己看书去提升可能一辈子都很难达到高级架构师的技术和认知高度。向厉害的人去学习是最有效减少时间摸索、精力浪费的方式。
我们选择的这个行业就一直要持续的学习,又很吃青春饭。
虽然大家可能经常见到说程序员年薪几十万,但这样的人毕竟不是大部份,要么是有名校光环,要么是在阿里华为这样的大企业。年龄一大,更有可能被裁。
送给每一位想学习Java小伙伴,用来提升自己。
本文到这里就结束了,喜欢的朋友可以帮忙点赞和评论一下,感谢支持!
FileInputStream 常用方法如下:
| Modifier and Type | 方法 | 描述 |
| — | — | — |
| void
| close()
| 关闭此文件输入流并释放与流相关联的任何系统资源。 |
| int
| read()
| 从该输入流读取一个字节的数据。 |
| int
| read(byte[] b)
| 从该输入流读取最多 b.length
个字节的数据到一个字节数组。 |
| int
| read(byte[] b, int off, int len)
| 从该输入流读取最多 len
个字节的数据到字节数组。 |
package Stream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamDemo01 {
public static void main(String[] args) throws IOException {
//创建输入流对象
FileInputStream fis = new FileInputStream("IO\\fos.txt");
/*
文件内容:
Hello
World
Java
*/
//第一种读取数据的方法(一次只读一个字节数据)
int by;
while ((by = fis.read()) != -1){
System.out.print((char)by);
}
//第二张读取数据的方法
byte[] bys = new byte[1024];
int len;
while((len = fis.read(bys)) != -1){
//String构造方法
//String(byte[] bytes, int offset, int length) 通过使用平台的默认字符集解码指定的字节子阵列来构造新的 String 。
System.out.print(new String(bys, 0, len));
}
fis.close();
}
}
通过字节流读取文件数据是一个重复的操作,因此我们可以使用一个循环进行操作,这个循环的关键就在于控制循环的条件,在read方法中,如果没有更多的数据,因为文件的结尾已经到达, 那么则会返回-1 ;否则从该输入流读取最多bys.length个字节的数据作为字节数组。
用一个len变量接受返回的数值,如果长度不为-1那么则打印出数据,以此来作为循环的约束条件。
字节缓冲流主要有两个:BufferedOutputStream 和 BufferedInputStream
- BufferedOutputStream : 该类实现缓冲输出流。 通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用;
- BufferedInputStream :创建BufferedInputStream 将会创建一个内部缓冲数组,当从流读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流重新填充,一次很多个字节。
使用字节缓冲流对象的时候,会创建一个缓冲数值,通过缓冲数组进行读和写,从而减少IO次数,进而提高读写的效率。讲白了IO就像送快递,字节流就是快递小哥一次只带一个快递,但是缓冲流就是快递小哥用车来运输快递,一次运输多个快递,效率大大提高。
(一)BufferedOutputStream
构造方法如下:
| Constructor | 描述 |
| — | — |
| BufferedOutputStream(OutputStream out)
| 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。 |
| BufferedOutputStream(OutputStream out, int size)
| 创建一个新的缓冲输出流,以便以指定的缓冲区大小将数据写入指定的底层输出流。 |
从 BufferedOutputStream 构造方法可以知道,传递过去的参数是 OutputStream类型的,因此要使用缓冲输出流之前先要创建一个字节输出流FileOutputStream对象。
常用的方法如下:
| Modifier and Type | 方法 | 描述 |
| — | — | — |
| void
| flush()
| 刷新缓冲输出流。 |
| void
| write(byte[] b, int off, int len)
| 从偏移量 off
开始的指定字节数组写入 len
字节到缓冲输出流。 |
| void
| write(int b)
| 将指定的字节写入缓冲的输出流。 |
这其中的write方法和FileOutputStream中的write方法用法大体上一样。
而flush()方法则是实现了一个刷新功能,因为输入的数据是存在一个缓冲区里面,若想吧缓冲区的数据存放到文件中,那么就需要用到flush方法,它可以刷新缓冲输入流,实现此功能。
package Stream;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class BufferedOutputStreamDemo01 {
public static void main(String[] args) throws IOException {
//创建字节输出缓冲流对象
BufferedOutputStream bof = new BufferedOutputStream(new FileOutputStream("IO\\bof.txt"));
//写入数据
bof.write("Hello\r\n".getBytes());
bof.write("World\r\n".getBytes());
//刷新缓冲流
bof.flush();
//释放资源
bof.close();
}
}
改程序的运行结果是将Hello和World分别写进文件的两行,这时候就会感觉奇怪了,不是说要不说明是否追加写入的话默认不追加写入吗?那为什么这里还能写进两个字符串?
这是文件关闭(执行close方法)后再打开文件写入数据,原来数据保存,那才叫追加写入。但是这里从头到尾文件只打开一次,所以不是追加写入,只是在打开一个文件的情况下,进行写入数据。
(二)BufferedInputStream
构造方法如下:
| Constructor | 描述 |
| — | — |
| BufferedInputStream(InputStream in)
| 创建一个 BufferedInputStream
并保存其参数,输入流 in
供以后使用。 |
| BufferedInputStream(InputStream in, int size)
| 创建具有指定缓冲区大小的 BufferedInputStream
,并保存其参数,输入流 in
供以后使用。 |
同样的,BufferedInputStream 的构造方法传递的参数类型是 InputStream 类型的数据,因此在创建这个BufferedInputStream 对象时,需要先构造一个 FileInputStream 对象,然后作为BufferedInputStream 对象构造方法的参数。
常用方法如下:
| Modifier and Type | 方法 | 描述 |
| — | — | — |
| void
| close()
| 关闭此输入流并释放与流相关联的任何系统资源。 |
| int
| read()
| 见 read
法 InputStream
的一般合同。 |
| int
| read(byte[] b, int off, int len)
| 从给定的偏移开始,将字节输入流中的字节读入指定的字节数组。 |
这些常用的方法的使用方法,与FileInputStream类中的没啥区别,用法大致相同
package Stream;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class BufferedInputStreamDemo01 {
public static void main(String[] args) throws IOException {
//创建字节缓冲输入流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("IO\\bof.txt"));
//一次读取一个字节数据
int by;
while ((by = bis.read()) != -1){
System.out.print((char) by);
}
//一次读取一个字节数组
byte[] bys = new byte[1024];
int len;
while ((len = bis.read(bys)) != -1) {
System.out.println(new String(bys, 0, len));
}
bis.close();
}
}
为什么会出现字符流呢?
是因为如果读取文本数据时,如果文本数据中有中文,而中文是由多个字节构成的,那么字节流通过读取一个一个字节输出的方式显然是不行,这样输出的结果会是一串乱码。
此时字符流的优势就展现出来了,而字符流 = 字节流 + 编码表;
一个汉字在不同的编码的存储是不一样的
- 如果是GBK编码,占用2个字节
- 如果是UTF-8编码,占用3个字节
由前面我们可以知道,String类中的getBytes方法可以使用平台的默认字符集将该 String编码为一系列字节,将结果存储到新的字节数组中**,其中IDEA中默认的编码是UTF-8,**因此用它来解码一个汉字,则会得到三个字节。
String类中的getBytes方法是一个重载的方法,具体如下:
| Modifier and Type | 方法 | 描述 |
| — | — | — |
| byte[]
| getBytes()
| 使用平台的默认字符集将该 String
编码为一系列字节,将结果存储到新的字节数组中。 |
| byte[]
| getBytes(String charsetName)
| 使用命名的字符集将该 String
编码为一系列字节,将结果存储到新的字节数组中。 |
因此我们可以使用第二个重载了的getBytes方法进行指定编码解码汉字
package Stream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
public class getBytesDemo {
public static void main(String[] args) throws UnsupportedEncodingException {
String s = "中国";
byte[] bys01 = s.getBytes();
byte[] bys02 = s.getBytes("GBK");
System.out.println("UTF-8解码结果:" + Arrays.toString(bys01));
System.out.println("GBK解码结果:" + Arrays.toString(bys02));
/*
运行结果:
UTF-8解码结果:[-28, -72, -83, -27, -101, -67]
GBK解码结果:[-42, -48, -71, -6]
*/
}
}
但是我们使用字节流也可以复制文本文件,文本文件也会有中文,但是却没问题,这是为什么呢?
原因是最终底层操作会自动进行字节拼接成中文,但是是如何识别是中文的呢?
其实,汉字在存储的时候,无论选择哪种编码存储,其第一个字节都是负数,因此只要是字节是负数就说明它是汉字。
(一)编码表
字符流涉及到编码方式,因此需要了解到编码表
基础知识
-
计算机中存储的信息都是用二进制表示的;
-
按照某种规则,将字符存储到计算机中,称为编码;
-
将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码;
字符集
字符集是一个系统支持的所有字符的集合,包括国家文字,图形符号,数字等;
常见的字符集有:ASCII字符集、GBXXXX字符集、Unicode字符集
各个国家为了符合自己国家语言,然后设计出了一套属于自己国家的字符集,但是其他国家并没有它这套字符集,那么其他国家解码岂不是都是乱码???
其实并不然Unicode字符集的出现解决了这个现象,其中最为常用的就是UTF-8编码,互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码,它使用一至四个字节为每个字符编码。
必须强调的是采用哪种规则编码,就必须采用对应规则解码,否则会出现乱码!!!!
(二)OutputStreamWriter
Writer:字节输出流的抽象类
而 OutputStreamWriter 是继承 Writer 的子类
其构造方法如下:
| Constructor | 描述 |
| — | — |
| OutputStreamWriter(OutputStream out)
| 创建一个使用默认字符编码的OutputStreamWriter。 |
| OutputStreamWriter(OutputStream out, String charsetName)
| 创建一个使用命名字符集的OutputStreamWriter。 |
常用方法如下:
| Modifier and Type | 方法 | 描述 |
| — | :-: | — |
| void
| close()
| 关闭流,先刷新。 |
| void
| flush()
| 刷新流。 |
| void
| write(char[] cbuf, int off, int len)
| 写入字符数组的一部分。 |
| void
| write(int c)
| 写一个字符 |
| void
| write(String str, int off, int len)
| 写一个字符串的一部分。 |
这些方法的使用方法大体上与字节流差不多,值得注意的是write方法传递的参数可以直接传递字符数组,不用先解码成byte数组再传递过去。
package Stream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
public class OutputStreamWriterDemo01 {
public static void main(String[] args) throws IOException {
//创建对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("IO\\osw.txt"));
//1.写入字符
osw.write('a');
//2.写入字符串
osw.write("\r\n中国\r\n");
osw.flush();
//3.字符数组
char[] ch = {'a', 'b', 'c'};
osw.write(ch);
osw.write(ch, 0, 1);
osw.flush();
//4.写入一部分
osw.write("HelloWorld", 0, 5);
osw.flush();
osw.close();
}
}
需要注意的是:OutputStreamWriter的中的close有点特别,它是在关闭之前会先刷新,也就是说在执行释放资源的之前,其会先主席那个flush方法,达到先把数据存储在文本信息的结果再释放资源。
(三)InputStreamReader
Reader:字节输入流的抽象类
InputStreamReader 是继承了 Reader 的子类
其构造方法如下:
| Constructor | 描述 |
| — | — |
| InputStreamReader(InputStream in)
| 创建一个使用默认字符集的InputStreamReader。 |
| InputStreamReader(InputStream in, String charsetName)
| 创建一个使用命名字符集的InputStreamReader。 |
常用方法如下:
| Modifier and Type | 方法 | 描述 |
| — | — | — |
| void
| close()
| 关闭流并释放与之相关联的任何系统资源。 |
| int
| read()
| 读一个字符 |
| int
| read(char[] cbuf, int offset, int length)
| 将字符读入数组的一部分。 |
package Stream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
public class InputStreamReaderDemo01 {
public static void main(String[] args) throws IOException {
//创建对象
InputStreamReader isr = new InputStreamReader(new FileInputStream("IO\\osw.txt"));
//读一个字符数据数据
int ch;
while ((ch = isr.read()) != -1){
System.out.print((char) ch);
}
//一次读一个字符数组数据
char[] chs = new char[1024];
int len;
while ((len = isr.read(chs)) != -1){
System.out.println(new String(chs));
}
}
}
InputStreamReader的常用方法的使用大多与字节流类似
(四)字符流中的编码解码问题
在IDEA中默认的编码解码都是使用UTF-8编码来实现的
但是如果我们构建字符输出流对象 OutputStreamWriter 的时候指定使用其他字符集编码,比如指定使用GBK编码,这是后我们打开已经写入数据的txt文件中,我们会发现出现了乱码情况,这是为什么呢???
这是因为txt默认使用UTF-8编码进行解码,但是编码是使用GBK进行编码的,因此肯定会出现乱码情况。
这时候我们读取数据显示在屏幕上的时候,如果我们创建字符输入流对象 InputStreamReader 的时候,没有指定使用GBK编码进行解码,那么它会默认使用UTF-8编码进行解码,进而出现乱码的情况,若我们需要显示出正常的内容而不是乱码,我们需要执行GBK编码进行解析。
总而言之,我们规定了使用哪种规则进行编码,就要使用哪种规则进行解析,否则会乱码!!!!
(五)FileWrite 与 FileReader
在处理现实问题中,比如复制一个java文件,如果其中不涉及编码问题,即不需要指定某种编码规则进行编码和解码,那么为了简便,我们可以使用 OutputStreamWriter 的子类 FileWrite 、 InputStreamReader 的子类 FileReader ;
但是如果一旦涉及到编码问题,还得使用 OutputStreamWriter 和 InputStreamReader 进行处理文件!!!
FileWrite 的构造方法如下:
| Constructor | 描述 |
| — | — |
| FileReader(File file)
| 创建一个新的 FileReader
,给出 File
读取。 |
| FileReader(String fileName)
| 创建一个新的 FileReader
,给定要读取的文件的名称。 |
由于 FileWrite 继承了 OutputStreamWriter ,所以 FileWrite 的常用方法的类型与用法与OutputStreamWriter一致。
FileReader 的构造方法如下:
| Constructor | 描述 |
| — | — |
| FileWriter(File file)
| 给一个File对象构造一个FileWriter对象。 |
| FileWriter(File file, boolean append)
| 给一个File对象构造一个FileWriter对象。 |
| FileWriter(String fileName)
| 构造一个给定文件名的FileWriter对象。 |
| FileWriter(String fileName, boolean append)
| 构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据。 |
同样,由于 FileReader 继承了 InputStreamReader ,所以 FileReader 的常用方法的类型与用法与InputStreamReader 一致。
字符缓冲流主要是 BufferedWriter 与 BufferedReader, 两者分别继承两个抽象类 Writer 与 Reader ;
BufferedWriter :将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小,默认足够打,可用于大多数用途。
BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以接受默认大小,默认足够打,可用于大多数用途。
(一)BufferedWriter
BufferedWriter 字符缓冲输出流的构造方法如下:
| Constructor | 描述 |
| — | — |
| BufferedWriter(Writer out)
| 创建使用默认大小的输出缓冲区的缓冲字符输出流。 |
| BufferedWriter(Writer out, int sz)
| 创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区。 |
从构造方法可知道,需要传递一个 Writer 类型的作为参数过去, 因此在使用字符缓冲输出流的时候,要先创建一个FileWriter对象。
BufferedWriter 字符缓冲输出流的常用方法如下:
| Modifier and Type | 方法 | 描述 |
| — | — | — |
| void
| close()
| 关闭流,先刷新。 |
| void
| flush()
| 刷新流。 |
| void
| newLine()
| 写一行行分隔符。 |
最后
无论是哪家公司,都很重视基础,大厂更加重视技术的深度和广度,面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。
针对以上面试技术点,我在这里也做一些分享,希望能更好的帮助到大家。
tor | 描述 |
| — | — |
| BufferedWriter(Writer out)
| 创建使用默认大小的输出缓冲区的缓冲字符输出流。 |
| BufferedWriter(Writer out, int sz)
| 创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区。 |
从构造方法可知道,需要传递一个 Writer 类型的作为参数过去, 因此在使用字符缓冲输出流的时候,要先创建一个FileWriter对象。
BufferedWriter 字符缓冲输出流的常用方法如下:
| Modifier and Type | 方法 | 描述 |
| — | — | — |
| void
| close()
| 关闭流,先刷新。 |
| void
| flush()
| 刷新流。 |
| void
| newLine()
| 写一行行分隔符。 |
最后
无论是哪家公司,都很重视基础,大厂更加重视技术的深度和广度,面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。
针对以上面试技术点,我在这里也做一些分享,希望能更好的帮助到大家。
[外链图片转存中…(img-dIwUn5Ob-1714838711105)]
[外链图片转存中…(img-hL7WqBtC-1714838711105)]
[外链图片转存中…(img-DkId2XAU-1714838711105)]