![e4703c6222e73e4fd8befc9640914a4e.png](https://i-blog.csdnimg.cn/blog_migrate/d3b0b2ad0c8a5c51157948e8616989cf.jpeg)
1 http://JAVA.IO字节流
![c2e86df64ebb8e1ecb33cfb05053611d.png](https://i-blog.csdnimg.cn/blog_migrate/e0f88ba38754e611a1e32f130a0367f5.jpeg)
- LineNumberInputStream和StringBufferInputStream官方建议不再使用,推荐使用LineNumberReader和StringReader代替
- ByteArrayInputStream和ByteArrayOutputStream
- 字节数组处理流,在内存中建立一个缓冲区作为流使用,从缓存区读取数据比从存储介质(如磁盘)的速率快
![f80982fb8c8c5413d05d3c15cab598c4.png](https://i-blog.csdnimg.cn/blog_migrate/f7e8f1c29c3f8c85cd306e7091a6b904.jpeg)
- FileInputStream和FileOutputStream
- 访问文件,把文件作为InputStream,实现对文件的读写操作
- ObjectInputStream和ObjectOutputStream
- 对象流,构造函数需要传入一个流,实现对JAVA对象的读写功能;可用于序列化,而对象需要实现Serializable接口
![d346f953281717eef4edcb17ee07be41.png](https://i-blog.csdnimg.cn/blog_migrate/75804d79f935cb2602807550f10bf8a0.jpeg)
- PipedInputStream和PipedOutputStream
- 管道流,适用在两个线程中传输数据,一个线程通过管道输出流发送数据,另一个线程通过管道输入流读取数据,实现两个线程间的数据通信
![d703d19144cf12091a4208379c0988de.png](https://i-blog.csdnimg.cn/blog_migrate/fae76b368aa092454a86fb28389bc23e.jpeg)
- SequenceInputStream
- 把多个InputStream合并为一个InputStream,允许应用程序把几个输入流连续地合并起来
![004218e6825889aef13bcdf7e3e477f1.png](https://i-blog.csdnimg.cn/blog_migrate/eed17df6ea6d69d023aab24d5f5211ca.jpeg)
FilterInputStream和FilterOutputStream 使用了装饰者模式来增加流的额外功能,子类构造参数需要一个InputStream/OutputStream
![4530a3088236f604b6a41cf0bbb440f4.png](https://i-blog.csdnimg.cn/blog_migrate/b59ff6581e07a779494683cf99655032.jpeg)
- DataInputStream和DataOutputStream (Filter流的子类)
- 为其他流附加处理各种基本类型数据的能力,如byte、int、String
- BufferedInputStream和BufferedOutputStream (Filter流的子类)
- 为其他流增加缓冲功能
- PushBackInputStream (FilterInputStream子类)
- 推回输入流,可以把读取进来的某些数据重新回退到输入流的缓冲区之中
- PrintStream (FilterOutputStream子类)
- 打印流,功能类似System.out.print
2 http://JAVA.IO字符流
![b28c4d3378791d81287da7944b2e0783.png](https://i-blog.csdnimg.cn/blog_migrate/ae8c39bd051656465249a3949633327e.jpeg)
- 从字节流和字符流的导向图来,它们之间是相互对应的,比如CharArrayReader和ByteArrayInputStream
- 字节流和字符流的转化:InputStreamReader可以将InputStream转为Reader,OutputStreamReader可以将OutputStream转为Writer
![0e9f4361f2728ce65254a8cb35ecf1d7.png](https://i-blog.csdnimg.cn/blog_migrate/b27d4df7c5f38155d418973c0da8c33e.jpeg)
- 区别:字节流读取单位是字节,字符流读取单位是字符;一个字符由字节组成,如变字长编码UTF-8是由1~4个字节表示
3 乱码问题和字符流
- 字符以不同的编码表示,它的字节长度(字长)是不一样的。如“程”的utf-8编码格式,由[-25][-88][-117]组成。而ISO_8859_1编码则是单个字节[63]
- 平时工作对资源的操作都是面向字节流的,然而数据资源根据不同的字节编码转为字节时,它们的内容是不一样,容易造成乱码问题
- 两种出现乱码场景
- encode和decode使用的字符编码不一致:资源使用UTF-8编码,而在代码里却使用GBK解码打开
- 使用字节流读取字节数不符合字符规定字长:字符是由字节组成的,比如“程”的utf-8格式是三个字节;如果在InputStream里以每两个字节读取流,再转为String(java默认编码是utf-8),此时会出现乱码(半个中文,你猜是什么)
![140fdc7e4f08c6bca271936f0603da83.png](https://i-blog.csdnimg.cn/blog_migrate/2d18b5c8c44d271fb6d1a12554636451.jpeg)
- 乱码场景1,知道资源的字符编码,就可以使用对应的字符编码来解码解决
- 乱码场景2,可以一次性读取所有字节,再一次性编码处理。但是对于大文件流,这是不现实的,因此有了字符流的出现
- 字节流使用InputStreamReader、OutputStreamReader转化为字符流,其中可以指定字符编码,再以字符为单位来处理,可解决乱码
![f7f472539bac94489dc6ad31fb179c58.png](https://i-blog.csdnimg.cn/blog_migrate/a8f3d9a1d4473f54a96a5a6cc48ab245.jpeg)
4 字符集和字符编码的概念区分
- 字符集和字符编码的关系,字符集是规范,字符编码是规范的具体实现;字符集规定了符号和二进制代码值的唯一对应关系,但是没有指定具体的存储方式;
- unicode、ASCII、GB2312、GBK都是字符集;其中ASCII、GB2312、GBK既是字符集也是字符编码;注意不混淆这两者区别;而unicode的具体实现有UTF-8,UTF-16,UTF-32
- 最早出现的ASCII码是使用一个字节(8bit)来规定字符和二进制映射关系,标准ASCII编码规定了128个字符,在英文的世界,是够用的。但是中文,日文等其他文字符号怎么映射呢?因此其他更大的字符集出现了
- unicode(统一字符集),早期时它使用2个byte表示1个字符,整个字符集可以容纳65536个字符。然而仍然不够用,于是扩展到4个byte表示一个字符,现支持范围是U+010000~U+10FFFF
- unicode是两个字节的说法是错误的;UTF-8是变字长的,需要用1~4个字节存储;UTF-16一般是两个字节(U+0000~U+FFFF范围),如果遇到两个字节存不下,则用4个字节;而UTF-32是固定四个字节
- unicode表示的字符,会用“U+”开头,后面跟着十六进制的数字,如“字”的编码就是U+5B57
- UTF-8 编码和unicode字符集
![3bd6d58c08d94ab4a24a25dbce03e0d7.png](https://i-blog.csdnimg.cn/blog_migrate/4ab8db888b45c5165b7512cee368ae60.png)
- 程序是分内码和外码,java的默认编码是UTF-8,其实指的是外码;内码倾向于使用定长码,和内存对齐一个原理,便于处理。外码倾向于使用变长码,变长码将常用字符编为短编码,罕见字符编为长编码,节省存储空间与传输带宽
- JDK8的字符串,是使用char[]来存储字符的,char是两个字节大小,其中使用的是UTF-16编码(内码)。而unicode规定的中文字符在U+0000~U+FFFF内,因此使用char(UTF-16编码)存储中文是不会出现乱码的
- JDK9后,字符串则使用byte[]数组来存储,因为有一些字符一个char已经存不了,如emoji表情字符,使用字节存储字符串更容易拓展
- JDK9,如果字符串的内容都是ISO-8859-1/Latin-1字符(1个字符1字节),则使用ISO-8859-1/Latin-1编码存储字符串,否则使用UTF-16编码存储数组(2或4个字节)
![c483cb67e944050a6fa8583c64c9a9e2.png](https://i-blog.csdnimg.cn/blog_migrate/3f2fb3cdab0178c140674655995fd62e.jpeg)
- “程序”的UTF-16编码竟是输出6个字节,多出了两个字节,这是什么情况?再试试一个字符的输出
![42de795a5b4c413b294e8f324d47ce99.png](https://i-blog.csdnimg.cn/blog_migrate/126f09b67b3a3c08bba7724472b50f0a.jpeg)
- 可以看出UTF-16编码的字节是多了[-2][-1]两个字节,十六进制是0xFEFF。而它用来标识编码顺序是Big endian还是Little endian。以字符'中'为例,它的unicode十六进制是4E2D,存储时4E在前,2D在后,就是Big endian;2D在前,4E在后,就是Little endian。FEFF表示存储采用Big endian,FFFE表示使用Little endian
- 为什么UTF-8没有字节序的问题呢?个人看法,因为UTF-8是变长的,由第一个字节的头部的0、110、1110、11110判断是否需后续几个字节组成字符,使用Big endian易读取处理,反过来不好处理,因此强制用Big endian
- 其实感觉UTF-16可以强制规定用Big endian;但这其中历史问题。。。
5 URI概念的简单介绍
- 既然有了http://java.io来操作资源流;但是对于网络的资源,该怎么打开,怎么定位呢?答URI-URL
- URI全称是
Uniform Resource Identifier
统一资源标识符 - 通俗说,就是一个类似身份证号码的字符串,只不过它是用来标识资源(如:邮件地址,主机名,文件等)
- URI 具有特定的规则:
[scheme]:[scheme-specific-part][#fragment]
- 进一步细入划分可表示为
[scheme]:[//authority][/path][?query][#fragment]
,其中模式特定部分为authority和path、query;而authority可以看做域名,如www.baidu.com
- 终极细分则是
[scheme]:[//host:port][/path][?query][#fragment]
,和日常见到的地址链接一毛一样了
- 进一步细入划分可表示为
- 模式特定部分(scheme-specific-part)的形式取决于模式,而URI的常用模式如下
- ftp:FTP服务器
- file:本地磁盘上的文件
- http:使用超文本传输协议
- mailto:电子邮件的地址
- telnet:基于Telnet的服务的连接
- Java中还大量使用了一些非标准的定制模式,如rmi、jar、jndi、doc、jdbc等
- 在java中URI抽象为java.net.URI类,下面列举几种常用构造方法
![7a29ac42cd86ba720e462e1935731137.png](https://i-blog.csdnimg.cn/blog_migrate/dfb05f49a8d2e94872d069b2bea20fc1.jpeg)
- JAVA.URI的常用操作方法
![c7a078e4f517e82b9889a780649168f4.png](https://i-blog.csdnimg.cn/blog_migrate/d90e6ce71698374a3d293bb40394aed0.jpeg)
6 URL概念及与URL的区别
- URL全称是
Uniform Resource Location
,统一资源定位符 - URL就是URI的子集,它除了标识资源,还提供找到资源的路径;在Java类库中,URI类不包含任何访问资源的方法,它唯一的作用就是解析,而URL类可以打开一个到达资源的流
- 同属URI子集的URN(统一资源名称),只标识资源名称,却不指定如何定位资源;如:
mailto:clswcl@gmail.com
就是一种URN,知道这是个邮箱,却不知道该怎么查找定位 - 通俗就是,URN告诉你有一个地方叫广州,但没有说怎么去,你可以搭动车,也可以搭飞机;URL会告诉你坐飞机去广州,而另一URL则说搭动车去
- URL的一般语法规则
![225c4d0de9b8247c5f723a85a5c53479.png](https://i-blog.csdnimg.cn/blog_migrate/b464f26992decc9a7ed79f586a2945aa.jpeg)
- URL的构造方法、获取方法
![7e2b8ea288a4fe06f6779b18d2580fb4.png](https://i-blog.csdnimg.cn/blog_migrate/7a3ce5cce7daea35a08c55cc70901801.jpeg)
- 通过URL获取资源数据的操作函数
![790be5ae510b113bf4f18baa3cf9cc51.png](https://i-blog.csdnimg.cn/blog_migrate/0ee166448162073ed7e60655f1a5de65.jpeg)
7 Spring.Resource与Spring资源获取方式
- 讲到资源,就得提下Spring获取资源方式,常用的有两种
- 通过Resource接口的子类获取资源
- 通过ResourceLoader接口的子类获取资源
- Spring.Resource 资源操作函数一览
![67fda55bff513ce684597a578391fd98.png](https://i-blog.csdnimg.cn/blog_migrate/69b69142d9dd37bfdcb701c6928f59c0.jpeg)
介绍下Resource相关子类的使用
- 1 FileSystemResource:通过文件系统获取资源
![bd5b44aa639a21ec79cdef97ffc909a2.png](https://i-blog.csdnimg.cn/blog_migrate/b64dd13f2e43ba84148e4a2c1239ee5f.jpeg)
- 2 ByteArrayResource:获取byte数组表示的资源
- 基于ByteArrayInputStream和字节数组实现,应用场景类似ByteArrayInputStream,缓存byte[]资源
- 3 ClassPathResource:获取类路径下的资源
![26cf272e40149bf0f2a7084631058e1d.png](https://i-blog.csdnimg.cn/blog_migrate/e81bac615845aae7038ac15852bb3b35.jpeg)
- 4 InputStreamResource:接收一个InputStream对象,获取输入流封装的资源
- 5 ServletContextResourse:加载ServletContext环境下(相对于Web应用根目录的)路径资源,获取的资源
- 6 UrlResource:通过URL访问http资源和FTP资源等
8 ResourceLoader 获取资源
![aaeb03c0515f2b1567566fda85bae654.png](https://i-blog.csdnimg.cn/blog_migrate/538a96bb61092cd7aa76dbfc5c437d79.jpeg)
- ResourceLoader是为了屏蔽了Resource的具体实现,统一资源的获取方式。你即能从ResourceLoader加载ClassPathResource,也能加载FileSystemResource等
![ee9b577747e92804111696b819320a97.png](https://i-blog.csdnimg.cn/blog_migrate/964c056ca560f159c31df307ef2ff75b.jpeg)
- ResourceLoader接口默认对classpath路径下面的资源进行加载
![7d794da4be03d490df409986341e5326.png](https://i-blog.csdnimg.cn/blog_migrate/d09d0fbdbb5da3d715390c755bf4d633.jpeg)
- ResourcePatternResolver默认会加载所有路径下面的文件,获得ClassPathResource;classpath:只会在class类路径下查找;而classpath*:会扫描所有JAR包及class类路径下出现的文件
![f3cd9f5217af26e218d3c1344c6fd91a.png](https://i-blog.csdnimg.cn/blog_migrate/b3e9190f03b6a05d9fe188d4f0321238.jpeg)
- ResourceLoader方法getResource的locationPattern可设置资源模式前缀来获取非ClassPathResource资源,locationPattern支持Ant风格
![0b1540144dd7eec49a82c9b1173c00ea.png](https://i-blog.csdnimg.cn/blog_migrate/3f40a734f2be0a4f91ba6f4591bfb43d.png)
9 JAVA.Properties了解一下
- Properties是java自带的配置处理类;Properties加载资源的两种方式
![5d834578e2bdc52cbea916f880c9d943.png](https://i-blog.csdnimg.cn/blog_migrate/bd4b87f7890577767a6bf4b02a161412.jpeg)
- Properties读取配置示例代码
![5281e727a07aa5e5ed704118e1d943f0.png](https://i-blog.csdnimg.cn/blog_migrate/c1469bb5e3bf56866fbc251c826973e6.jpeg)
10 yml配置资源的读取
- 普通java项目如果需要读取yml可引入jackson-dataformat-yaml,而springboot默认配置支持yml的读取
![e1f4a60a17a311b3b81d6e55e6ec5154.png](https://i-blog.csdnimg.cn/blog_migrate/6e54ae0c58f773c5eb52c30deee7d617.jpeg)
- 基于jackson-dataformat-yaml对yml配置资源的读取
![d2759fa2d02cd31a43c970f4aec2b6ff.png](https://i-blog.csdnimg.cn/blog_migrate/13556c58863d854e51d94500c7ef11e3.jpeg)
11 优雅地关闭资源,try-with-resource语法和lombok@Cleanup
- 资源的打开就需要对应的关闭,但我们常会忘记关闭资源,或在多处代码关闭资源感到杂乱,有没有简洁的关闭方法呢?
- 自动关闭资源类需实现AutoCloseable接口和配和try-with-resource语法糖使用
![e4f479a8a31e539f95e4d1f758eaf615.png](https://i-blog.csdnimg.cn/blog_migrate/75c2fd8d06cbb91fccca35ed4113ac49.jpeg)
- lombok注解@Cleanup,对象生命周期结束时会调用
public void close()
;对象需实现AutoCloseable接口
import lombok.Cleanup; @Cleanup // @Cleanup的使用 YSOAPConnection soapConnection=YSOAPConnection.open(soapConnectionFactory) 复制代码12 资源不关闭,会导致什么最坏的结果
- JDK的原生资源类不关闭,它也不会永远存在。JVM会借助finalize自动关闭它,例如FileInputStream
![b617a05382b09f55df871abc3e7638b6.png](https://i-blog.csdnimg.cn/blog_migrate/d083e66e5c74a89a9cccebe7b8a780c8.jpeg)
- 在JDK9后,用Cleaner机制代替了finalize机制;Cleaner机制自动回收的对象同样需要实现AutoCloseable接口;Cleaner是基于PhantomReference实现的;对实现细节感兴趣的同学,可自行查阅下相关文档
- 但是使用JDK的提供的资源关闭机制的,那么资源的关闭比手动关闭时要延后很长时间的。据测试,使用try-with-resources关闭资源,并让垃圾回收器回收它的时间在12纳秒。而使用finalizer机制,时间增加到550纳秒
- 不及时关闭资源,就会占用资源,影响其他线程的执行;比如linux的文件资源,linux进程默认能打开的最大文件数是1024(有的是2048,此数值是可配置的);如果一个线程持有十几个文件资源,还要等550纳秒用finalizer机制释放资源,同进程的其他线程都等到花谢了
欢迎指正文中错误
作者:cscw
链接: https:// juejin.im/post/68562667 75022174222
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。