基本配置

Hexo安装

安装nodejs

安装Hexo

1
npm install hexo-cli -g

修改菜单

在菜单中添加链接。编辑 主题配置文件 , 取消categories前面的注释#,添加 categories 到 menu 中,如下:

1
2
3
4
5
6
7
8
9
menu:
home: / || home
#about: /about/ || user
tags: /tags/ || tags
categories: /categories/ || th
archives: /archives/ || archive
#schedule: /schedule/ || calendar
#sitemap: /sitemap.xml || sitemap
#commonweal: /404/ || heartbeat

然后,开启本地服务器,显示效果如下:

添加标签页面

创建页面

1
hexo new page tags

打开菜单

1
2
3
4
5
6
7
8
9
menu:
home: / || home
#about: /about/ || user
tags: /tags/ || tags
categories: /categories/ || th
archives: /archives/ || archive
#schedule: /schedule/ || calendar
#sitemap: /sitemap.xml || sitemap
#commonweal: /404/ || heartbeat

设置代码高亮主题

NexT 使用 Tomorrow Theme 作为代码高亮,共有5款主题供你选择。 NexT 默认使用的是 白色的 normal 主题,可选的值有 normal,night, night blue, night bright, night eighties:

主题配置文件中,更改 highlight_theme 字段,将其值设定成你所喜爱的高亮主题,例如:

1
highlight_theme: night

这样,代码框部分就显示如下了。这个根据个人喜好配置。

设置头像

打开主题配置文件,查找avatar,设置头像的路径(url):

1
2
3
4
5
6
7
8
9
10
11
12
# Sidebar Avatar
avatar:
# In theme directory (source/images): /images/avatar.gif
# In site directory (source/uploads): /uploads/avatar.gif
# You can also use other linking images.
url: /images/avatar.jpg
# If true, the avatar would be dispalyed in circle.
rounded: true
# The value of opacity should be choose from 0 to 1 to set the opacity of the avatar.
opacity: 1
# If true, the avatar would be rotated with the cursor.
rotated: false

配置解释:

  • url: /images/avatar.jpg,这个表示头像的路径在博客站点目录下source目录下的images目录下。这就是这样的形式\blog7\source\images
  • rounded: true表示图像显示的方式,false则显示方形,true则显示圆形
  • rotated: false表示当鼠标碰到头像的时候,是否旋转头像.
  • opacity: 1这个是不透明度值,可以设置从0到1的任意小数,如设置为0.5.

设置图标

在主题配置文件中,查找favicon:,然后设置对应的图片。图片路径还是在博客站点目录下source目录下的images目录下。这就是这样的形式\blog7\source\images

1
2
3
4
5
6
7
favicon:
small: /images/favicon-16x16-next.png
medium: /images/favicon-32x32-next.png
apple_touch_icon: /images/apple-touch-icon-next.png
safari_pinned_tab: /images/logo.svg
#android_manifest: /images/manifest.json
#ms_browserconfig: /images/browserconfig.xml

还有就是图片的名字不要乱改成其他名字,使用配置文件中默认的即可.这样在后续重新搭建一个hexo博客的时候,就可以吧images目录直接复制过去使用.就不用再浪费时间重新配置一次。

图标显示N的问题

上面的设置后就可以正常显示图标了,如果图标还没显示出来,按下Ctrl+F5直接从服务器加载开开.如果还不行的话,到主题的图标文件夹:\blog7\themes\next\source\images中删除,或者替换同名的图片即可,也就是:

1
2
3
4
favicon-16x16-next.png
favicon-32x32-next.png
apple-touch-icon-next.png
logo.svg

还有就是替换loading.gif可以实现自定义的图片加载效果。

开启打赏

在主题配置文件中,查找reward:然后设置对应的图片即可。图片要先放到博客站点目录下source目录下的images目录下。这就是这样的形式\blog7\source\images

1
2
3
4
5
6
reward:
enable: true
#comment: 欢迎打赏.
wechatpay: /images/wechatpay.jpg
alipay: /images/alipay.jpg
#bitcoin: /images/bitcoin.jpg

修改内容区域显示宽度

http://theme-next.iissnan.com/faqs.html#custom-content-width
如何更改内容区域的宽度?
NexT 对于内容的宽度的设定如下:

  • 700px,当屏幕宽度 < 1600px
  • 900px,当屏幕宽度 >= 1600px
  • 移动设备下,宽度自适应
    如果你需要修改内容的宽度,同样需要编辑样式文件。 编辑主题的 source/css/_variables/custom.styl 文件,新增变量:
    1
    2
    3
    4
    // 修改成你期望的宽度
    $content-desktop = 700px
    // 当视窗超过 1600px 后的宽度
    $content-desktop-large = 900px

    修改代码块字体

    默认的字体太大了,会溢出代码块,为了避免反复拖拽滚动条,我选择把字体改小。
    http://theme-next.iissnan.com/faqs.html#custom-font
    编辑主题下的 source/css/_variables/custom.styl 文件,新增下面变量变量:
    1
    2
    // 代码字体的大小
    $code-font-size = 12px
    我这里调整代码时为了在移动端,可以多看点,免得我反复拖动.

    插件

    插件库

    https://hexo.io/plugins/

    本地搜索

    hexo-generator-search

    安装

    1
    npm install hexo-generator-search --save

    修改站点配置文件

    添加:
    1
    2
    3
    4
    search:
    path: search.xml
    field: post
    content: true

    修改Next主题配置文件

    找到local_search:,把enable改为true即可。
    1
    2
    local_search:
    enable: true

    github远程仓库推送

    插件github:hexo-deployer-git

安装

1
npm install hexo-deployer-git --save

修改站点配置文件

1
2
3
4
5
6
7
# Deployment
## Docs: https://hexo.io/docs/deployment.html
deploy:
- type: git
repo:
github: git@github.com:XXXXXXXXX/XXXXXXXXX.github.io.git # Github pages地址
branch: master

持久化链接

插件位置:hexo-abbrlink

安装

1
npm install hexo-addlink --save

修改站点配置文件

站点配置文件中查找permalink:,把permalink: :year/:month/:day/:title/修改为:

1
permalink: blog/:abbrlink/ #blog可以修改为其他信息,blog

站点配置文件中设置算法:

1
2
3
abbrlink:
alg: crc32 # 算法:crc16(default) and crc32
rep: hex # 进制:dec(default) and hex

文章置顶

卸载hexo-generator-index插件

hexo站点目录下,输入下面命令卸载插件

1
npm uninstall hexo-generator-index --save

安装hexo-generator-index-pin-top插件

hexo站点目录下,输入下面命令安装插件

1
npm install hexo-generator-index-pin-top --save

添加front-matter配置项

在需要置顶的文章的Front-matter中加上top: true即可实现文章置顶功能。

问题

端口被占用 则修改端口

临时改动

如果你想修改端口,可以通过如下命令来指定端口:

1
hexo s -p 4007

这个是临时改动而已,当你下一次使用hexo s,来启动的时候还是在默认端口4000.

永久改动

永久改动的话,有的人推荐修改hexo-server的源文件,但是我不建议这么做,这里可以通过批处理文件实现,免去每次手动写出hexo s -p 4007,首先创建一个名字为启动博客.bat的批处理文件,然后写上如下几条命令:

1
2
3
4
5
6
7
8
::关闭回显
@echo off
::切换到博客所在的盘符
E:
::进入站点目录
cd E:\Blog\blog7
::在4007端口运行程序
hexo s -p 4007

然后把启动博客.bat放到桌面中,下次点击一下就可以在4007端口下启动博客了。这样做的好处就是可以,启动多个网站.
这里有一张图片

自定义样式

修改的文件位置:blog7\themes\next\source\css\_custom\custom.styl.

修改代码段样式

打开blog7\themes\next\source\css\_custom\custom.styl这个文件,添加如下的样式代码:

1
2
3
4
5
/* 自定义代码块样式 */
code {
color: #c7254e;
background-color: #f9f2f4;
}

这个样式是我从CSDN上的代码段样式中复制下来的,个人觉得比较好看.

参考资料

hexo修改默认端口

区别

  • BufferedInputStream读数组方法,尽量读满整个数组,然后再返回,所以可能会多次读取,才返回。
  • FileInputStream的读数组方法只会读取一次,读到多少就返回多少。读取一次,就返回一次。不管数组有没有读满。

问题描述

Linux下,如果想要删除目录及其子目录下某种类型文件,比如说所有的txt文件,则可以使用下面的命令:

命令

1
find . -name "*.txt" -type f -print -exec rm -rf {} \;  

执行结果:

1
2
3
4
5
$ find . -name "*.txt" -type f -print -exec rm -rf {} \;
./a.txt
./b.txt
./Java/c.txt
./其他/d.txt

可以看到,当前目录下的a.txt,b.txt,子目录./Java/下的c.txt,子目录./其他/下的d.txt都被删除掉了。

参数说明

. : 表示在当前目录下

1
-name "*.txt"  

表示查找所有后缀为txt的文件

1
-type f  

表示文件类型为一般正规文件

1
-print

表示将查询结果打印到屏幕上

1
-exec command  

command为其他命令,-exec后可再接其他的命令来处理查找到的结果,上式中,{}表示”由find命令查找到的结果“,如上所示,find所查找到的结果放置到{}位置,-exec一直到”;“是关键字,表示find额外命令的开始(-exec结束(\;,这中间的就是find命令的额外命令,上式中就是 rm -rf

参考资料

原文:https://www.cnblogs.com/wyb628/p/7374372.html

常用处理流(关闭处理流使用关闭里面的节点流)

  • 缓冲流:BufferedInputStrean BufferedOutputStream BufferedReader BufferedWriter—增加缓冲功能,避免频繁读写硬盘。
  • 转换流:InputStreamReader OutputStreamReader实现字节流和字符流之间的转换。
  • 数据流 DataInputStream DataOutputStream 等-提供将基础数据类型写入到文件中,或者读取出来.

流的关闭顺序

  • 一般情况下是:先打开的后关闭,后打开的先关闭
  • 另一种情况:看依赖关系,如果流a依赖流b,应该先关闭流a,再关闭流b。例如,处理流a依赖节点流b,应该先关闭处理流a,再关闭节点流b
  • 可以只关闭处理流,不用关闭节点流。处理流关闭的时候,会调用其处理的节点流的关闭方法。

注意:

  • 如果将节点流关闭以后再关闭处理流,会抛出IO异常。
  • 如果关闭了处理流,在关闭与之相关的节点流,也可能出现IO异常。(hadoop编程文件流操作中遇到了。)

总结:通常在IO的继承树中,继承自Filter开头的流或者转换流(InputStreamReader,OutputStreamReader)都是处理流,其他的都是节点流。

过滤器使用规则

过滤器可以和其他过滤器、原始流和阅读器/书写器链接使用,但要遵守以下规则:

  • 过滤器流可以置于原始流或其他过滤器流之上;
  • 阅读器/书写器可置于原始流、过滤器流或其他阅读器/书写器之上;
  • 过滤器流不能放在阅读器/书写器之后。

正则表达式匹配IP地址

正则表达式,又称规则表达式。接下来通过本文给大家介绍正则表达式匹配IP的表达式,非常不错,具有参考借鉴价值,需要的的朋友参考下吧

这里给大家详细讲解一下一个匹配IP地址的正则表达式,
有关正则方面的知识,会在详细的讲解中提到。
在讲解之前,我先给大家介绍一下,ip地址的生成规则。
IP地址,是由32位数字二进制转为四个十进制的字符串组成。
怎么转化?下面讲解:

二进制:11111111111111111111111111111111
分为四部分:11111111.11111111.11111111.11111111
转化:2^7+2^6+2^5+2^4+2^3+2^2+2^1+2^0=255
转为十进制范围:0~255.0~255.0~255.0~255
这就是IP地址的范围。
根据这个生成IP的规则和范围,我们可以用正则表达式来匹配出IP地址,但怎么匹配呢?各人有各人的方法,这里我讲解一下我的思路。

根据IP地址的字符串规律,我把匹配IP地址的表达式分为两部分来考虑。
第一部分:匹配3个0~255.(注意后面的一个点)
第二部分:匹配最后的数字0~255
也就是说,先匹配出 0~255.(注意后面的一个点) 这个字符串,然后重复匹配3次,然后再匹配最后的数字部分0~255。这就是我匹配IP地址的思路。

首先,我要提一下,正则是没有办法做数字运算的,所以,我们无法用数字运算的方式筛选出IP的数字范围。既然没法用数字运算的方式筛选出IP的数字范围,那么我们应该用什么其他方式来筛选这个数字范围呢?我的思路是分组讨论,然后再把这些分组合并起来组成IP的数字范围。

匹配一个合法的数字

三位数的情况:

  • 假设百位是2,那么根据IP数字的范围规则,这里又要分为两种情况,为什么?你想想,最大数字是255,当十位数为5时,个位数最大只能为5是吧?(250-255).而当十位数为0到4时,个位数可以是任意数字对吧?(200-249)
    所以,这里的两种情况分别为:
    • 2[0-4][0-9]
    • 25[0-5]
  • 假设百位数是1,那么这个数字的范围为100-199,对应正则表达式就为1[0-9][0-9]。这个应该不难理解,就不解释。

两位数的情况

分析完了三位数的情况,接下来就是两数的情况了,假如是两位数,那么十位数的前面第一个数不能为零是吧?也就是范围为10-99(10,11...,19,20,...,99),对应的正则表达式为[1-9][0-9]

一位数的情况

剩下的就是个位数的情况了,个位数的情况,大家应该很容易得出结论,就是:[0-9]

四种情况分析下来,我们得出了IP数字的范围分组为:

  • 25[0-5]
  • 2[0-4][0-9]
  • 1[0-9][0-9]
  • [1-9][0-9]
  • [0-9]

匹配一个合法的数字

所以匹配IPv4中一个和法的数组的正则表达式为(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])

优化:

  • 可以把两位数的正则[1-9][0-9]和一位数的正则[0-9]合并,写为[1-9]?[0-9].
  • 用简写\d替代[0-9]

所以优化后的一个合法的数字的正则表达式为:(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)

匹配一个合法的数字和点号三次 ##:

需要注意的是,点号要转义为\.,所以得到的正则表达式为:
((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}

最终的表达式:

三组数字和点号之后,后面还有一组数字。这样的话综合起来就是:
((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)

优化:

上面我们使用了括号(...)来限定量词和多选的作用范围。但是这样也会在匹配的时候进行捕获,捕获的话会造成不必要的开销,这里使用(?:...)来限定,这样省去捕获分组的开销。
(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)

总结

匹配IP地址中的一个数

支持0开头,捕获分组:

1
(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)

不支持0开头,不捕获分组:

1
(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)

不支持0开头,匹配整个IP地址

1
(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)


匹配整个IP地址,并捕获每个数字:

1
(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)

问题描述

MarkDown语法并不支持表格单元格合并,不过可以插入html表格标签来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<table>
<tr>
<th>序号
</td>
<th>OSI的体系结构
</td>
<th>TCP/IP的体系结构
</td>
</tr>
<tr>
<td>7</td>
<td>应用层</td>
<td rowspan="3">应用层 (各种应用层协议如 TELNET, FTP, SMTP 等)</td>
</tr>
<tr>
<td>6</td>
<td>表示层</td>
</tr>
<tr>
<td>5</td>
<td>会话层</td>
</tr>
<tr>
<td>4</td>
<td>传输层</td>
<td>传输层</td>
</tr>
<tr>
<td>3</td>
<td>网络层</td>
<td>互连网络层</td>
</tr>
<tr>
<td>2</td>
<td>数据链路层</td>
<td rowspan="2">网络接入层</td>
</tr>
<tr>
<td>1</td>
<td>物理层</td>
</tr>
</table>

效果

序号 OSI的体系结构 TCP/IP的体系结构
7 应用层 应用层 (各种应用层协议如 TELNET, FTP, SMTP 等)
6 表示层
5 会话层
4 传输层 传输层
3 网络层 互连网络层
2 数据链路层 网络接入层
1 物理层
是不是看起很奇怪,表格跟上面的文字隔了这么多行这是`
`标签之间有回车符造成的,删除这些回车符,这样就显示正常了,不过这样的坏处就是看起来很凌乱不好修改。 当然要是自己一行一行的删的话那得删到什么时候,最好使用正则表达式进行删除。我这里用的editplus这个编辑器。 把上面的代码粘贴到文件找那个,然后打开替换,使用正则表达式`\n\s+`进行替换即可。 ![](https://i.imgur.com/SeKfPU3.png) 点击Find按钮可以看到匹配的结果。 ![](https://i.imgur.com/A9GkZ8h.png) 可以看到已经匹配到换行符合多余的空格了,点击ReplaceAll按钮全部替换即可。 替换后的效果为: ![](https://i.imgur.com/bGCze8K.png) 替换后的标签为:
1
2
<table><tr><th>序号</th><th>OSI的体系结构</th> <th>TCP/IP的体系结构</th></tr><tr><td>7</td><td>应用层</td><td rowspan="3">应用层  (各种应用层协议如 TELNET, FTP, SMTP 等)</td></tr><tr><td>6</td><td>表示层</td> </tr><tr><td>5</td><td>会话层</td> </tr><tr><td>4</td> <td>传输层</td>
<td>传输层</td></tr><tr><td>3</td><td>网络层</td><td>互连网络层</td></tr><tr><td>2</td><td>数据链路层</td><td rowspan="2">网络接入层</td> </tr><tr><td>1</td> <td>物理层</td></tr></table>
显示效果:
序号OSI的体系结构 TCP/IP的体系结构
7应用层应用层 (各种应用层协议如 TELNET, FTP, SMTP 等)
6表示层
5会话层
4 传输层 传输层
3网络层互连网络层
2数据链路层网络接入层
1 物理层

继承关系

1
2
3
4
java.lang.Object
|----> java.io.OutputStream
|----> java.io.FilterOutputStream
|----> java.io.PrintStream

public class PrintStream extends FilterOutputStream implements Appendable, Closeable
PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。它还提供其他两项功能。与其他输出流不同,PrintStream 永远不会抛出 IOException;而是,异常情况仅设置可通过 checkError 方法测试的内部标志。另外,为了自动刷新,可以创建一个 PrintStream;这意味着可在写入 byte 数组之后自动调用 flush 方法,可调用其中一个 println 方法,或写入一个换行符或字节 (‘\n‘)。

PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。

构造方法

方法 描述
PrintStream(File file) 创建具有指定文件且不带自动行刷新的新打印流。
PrintStream(File file, String csn) 创建具有指定文件名称和字符集且不带自动行刷新的新打印流。
PrintStream(OutputStream out) 创建新的打印流。
PrintStream(OutputStream out, boolean autoFlush) 创建新的打印流。
PrintStream(OutputStream out, boolean autoFlush, String encoding) 创建新的打印流。
PrintStream(String fileName) 创建具有指定文件名称且不带自动行刷新的新打印流。
PrintStream(String fileName, String csn) 创建具有指定文件名称和字符集且不带自动行刷新的新打印流。

成员方法

write(…)方法

方法 描述
void write(byte[] buf, int off, int len) 将 len 字节从指定的初始偏移量为 off 的 byte 数组写入此流。
void write(int b) 将指定的字节写入此流。

write(int b)源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void write(int b) {
try {
synchronized (this) {
ensureOpen();
out.write(b);
//如果这个字符是换行符,而且开启了自动刷新的话
if ((b == '\n') && autoFlush)
out.flush();//调用flush()方法
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;//设置标志
}
}

write(int b)详细描述

将指定的字节写入此流,如果这个字节是换行符而且开启了自动刷新的话,就会调用flush()方法
注意,字节是按给定的方式写入的。 要根据平台的默认字符编码编写一个字符,请使用print(char)或println(char)方法。
参数: b 将要写入的字节
覆盖:类 FilterOutputStream 中的 write

关于自动刷新

所以如果开启了自动刷新,write(int b)方法在写入回车符的时候会调用flush()方法

write(byte buf[], int off, int len)源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void write(byte buf[], int off, int len) {
try {
synchronized (this) {
ensureOpen();
out.write(buf, off, len);
if (autoFlush)
out.flush();
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;
}
}

write(byte buf[], int off, int len)详细描述

将 len 字节从指定的初始偏移量为 off 的 byte 数组写入此流。如果启用自动刷新,则调用 flush 方法。
注意,字节将按给定的方式写入;
要根据平台的默认字符编码来写入,请使用print(char)println(char)方法。
参数: buf 字节数组
参数: off 相对于字节数组开始位置的偏移量
参数: len 需要写入的字节数

1
2
3
public void print(char c) {
write(String.valueOf(c));
}

打印一个字符,根据平台默认字符编码把这个字符转换为一个或者多个字节,然后这些字节完全以write(int)方法的方式进行写入。

1
2
3
public void print(int i) {
write(String.valueOf(i));
}

打印整数。按照平台的默认字节编码将 String.valueOf(i) 生成的字符串转换为字节,并完全以 write(int) 方法的方式写入这些字节。

参数:
i - 要打印的 int

1
2
3
public void print(long l) {
write(String.valueOf(l));
}

打印long整数,按照平台默认字节编码将String.valueOf(l)生成的字符串转换为字节,并完全以write(int)方法的方式写入这些字节

1
2
3
public void print(float f) {
write(String.valueOf(f));
}

其他print(…)方法源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void print(double d) {
write(String.valueOf(d));
}
public void print(char s[]) {
write(s);
}
public void print(String s) {
if (s == null) {
s = "null";
}
write(s);
}
public void print(Object obj) {
write(String.valueOf(obj));
}

println(…)方法

方法 描述
void println() 通过写入行分隔符字符串终止当前行。
void println(boolean x) 打印 boolean 值,然后终止行。
void println(char x) 打印字符,然后终止该行。
void println(char[] x) 打印字符数组,然后终止该行。
void println(double x) 打印 double,然后终止该行。
void println(float x) 打印 float,然后终止该行。
void println(int x) 打印整数,然后终止该行。
void println(long x) 打印 long,然后终止该行。
void println(Object x) 打印 Object,然后终止该行。
void println(String x) 打印 String,然后终止该行。

println(…)方法源码

1
2
3
4
5
/* Methods that do terminate lines */

public void println() {
newLine();
}

通过写入行分隔符字符串终止当前行。行分隔符字符串由系统属性 line.separator 定义,不一定是单个换行符 (‘\n‘)。

1
2
3
4
5
6
public void println(boolean x) {
synchronized (this) {
print(x);
newLine();
}
}

打印 boolean 值,然后终止行。此方法的行为就像先调用 print(boolean) 然后调用 println() 一样。

参数:
x - 要打印的 boolean

1
2
3
4
5
6
public void println(char x) {
synchronized (this) {
print(x);
newLine();
}
}

打印字符,然后终止该行。此方法的行为就像先调用 print(char) 然后调用 println() 一样。

参数:
x - 要打印的 char。

1
2
3
4
5
6
public void println(int x) {
synchronized (this) {
print(x);
newLine();
}
}

打印整数,然后终止该行。此方法的行为就像先调用 print(int) 然后调用 println() 一样。

参数:
x - 要打印的 int。

1
2
3
4
5
6
public void println(long x) {
synchronized (this) {
print(x);
newLine();
}
}

打印 long,然后终止该行。此方法的行为就像先调用 print(long) 然后调用 println() 一样。

参数:
x - 要打印的 long。

1
2
3
4
5
6
7
public void println(float x) {
synchronized (this) {
print(x);
newLine();
}
}

打印 float,然后终止该行。此方法的行为就像先调用 print(float) 然后调用 println() 一样。
参数:
x - 要打印的 float。

1
2
3
4
5
6
public void println(double x) {
synchronized (this) {
print(x);
newLine();
}
}

打印 double,然后终止该行。此方法的行为就像先调用 print(double) 然后调用 println() 一样。

参数:
x - 要打印的 double。

1
2
3
4
5
6
public void println(char x[]) {
synchronized (this) {
print(x);
newLine();
}
}

打印字符数组,然后终止该行。此方法的行为就像先调用 print(char[]) 然后调用 println() 一样。
参数:
x - 要打印的字符数组。

1
2
3
4
5
6
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}

打印 String,然后终止该行。此方法的行为就像先调用 print(String) 然后调用 println() 一样。
参数:
x - 要打印的 String。

1
2
3
4
5
6
7
public void println(Object x) {
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}

打印 Object,然后终止该行。此方法首先调用 String.valueOf(x) 获取打印对象的字符串值,然后的行为如同先调用 print(String) 再调用 println() 一样。
参数:
x - 要打印的 Object。

format(…)方法

方法 描述
PrintStream format(Locale l, String format, Object... args) 使用指定格式字符串和参数将格式化字符串写入此输出流中。
PrintStream format(String format, Object... args) 使用指定格式字符串和参数将格式化字符串写入此输出流中。
PrintStream printf(Locale l, String format, Object... args) 使用指定格式字符串和参数将格式化的字符串写入此输出流的便捷方法。
PrintStream printf(String format, Object... args) 使用指定格式字符串和参数将格式化的字符串写入此输出流的便捷方法。

format(…)方法源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public PrintStream format(String format, Object ... args) {
try {
synchronized (this) {
ensureOpen();
if ((formatter == null)
|| (formatter.locale() != Locale.getDefault()))
formatter = new Formatter((Appendable) this);
formatter.format(Locale.getDefault(), format, args);
}
} catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
} catch (IOException x) {
trouble = true;
}
return this;
}

使用指定格式字符串和参数将格式化字符串写入此输出流中。
始终使用的语言环境是由 Locale.getDefault() 返回的语言环境,不管以前在此对象上调用了其他什么样的格式化方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public PrintStream format(Locale l, String format, Object ... args) {
try {
synchronized (this) {
ensureOpen();
if ((formatter == null)
|| (formatter.locale() != l))
formatter = new Formatter(this, l);
formatter.format(l, format, args);
}
} catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
} catch (IOException x) {
trouble = true;
}
return this;
}

使用指定格式字符串和参数将格式化字符串写入此输出流中。
参数:

  • l 格式化过程中应用的 locale。如果 l 为 null,则不应用本地化。
  • format 在格式字符串的语法中描述的格式字符串
  • args 格式字符串中的格式说明符引用的参数。如果参数多于格式说明符,则忽略额外的参数。参数的数量是可变的,并且可以为零。参数的最大数量受到 Java Virtual Machine Specification 定义的 Ja
  • va 数组的最大维数的限制。针对 null 参数的行为依赖于 conversion。
    返回: 此输出流
    抛出:
  • IllegalFormatException 如果格式字符串包含非法语法、与给定参数不兼容的格式说明符、对给定格式字符串而言不够充足的参数或其他非法条件。有关所有可能的格式错误的规范,请参阅 formatter 类规范的详细信息部分。
  • NullPointerException 如果 format 为 null
    从以下版本开始:

1.5

append(…)方法

方法 描述
PrintStream append(char c) 将指定字符添加到此输出流。
PrintStream append(CharSequence csq) 将指定字符序列添加到此输出流。
PrintStream append(CharSequence csq, int start, int end) 将指定字符序列的子序列添加到此输出流。
1
2
3
4
5
6
public PrintStream append(CharSequence csq, int start, int end) {
CharSequence cs = (csq == null ? "null" : csq);
write(cs.subSequence(start, end).toString());
return this;
}

将指定字符序列添加到此输出流。
此方法调用 out.append(csq) 的行为与调用下列方法完全相同:

1
out.print(csq.toString()) 

可能不添加整个序列,也可能添加,具体取决于字符序列 csq 的 toString 规范。例如,调用一个字符缓冲区的 toString 方法将返回一个子序列,其内容取决于缓冲区的位置和限制。
指定者:接口 Appendable 中的 append
参数:csq - 要添加的字符序列。如果 csq 为 null,则向此输出流添加四个字符 “null”。
返回:此输出流
从以下版本开始: 1.5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Appends the specified character to this output stream.
*
* <p> An invocation of this method of the form <tt>out.append(c)</tt>
* behaves in exactly the same way as the invocation
*
* <pre>
* out.print(c) </pre>
*
* @param c
* The 16-bit character to append
*
* @return This output stream
*
* @since 1.5
*/
public PrintStream append(char c) {
print(c);
return this;
}

错误相关方法

方法 描述
boolean checkError() 刷新流并检查其错误状态。
protected void clearError() 清除此流的内部错误状态。
void close() 关闭流。
void flush() 刷新该流的缓冲。
protected void setError() 将该流的错误状态设置为 true。

继承关系

1
2
3
java.lang.Object
|----> java.io.Writer
|----> java.io.PrintWriter

public class PrintWriter extends Writer向文本输出流打印对象的格式化表示形式。此类实现在 PrintStream 中的所有 print 方法。它不包含用于写入原始字节的方法,对于这些字节,程序应该使用未编码的字节流进行写入。

PrintStream 类不同,如果启用了自动刷新,则只有在调用 printlnprintfformat 的其中一个方法时才可能完成此操作,而不是每当正好输出换行符时才完成。这些方法使用平台自有的行分隔符概念,而不是换行符。

此类中的方法不会抛出 I/O 异常,尽管其某些构造方法可能抛出异常。客户端可能会查询调用 checkError() 是否出现错误。

构造函数

方法 描述
PrintWriter(File file) 打印到文件中,不自动刷新
PrintWriter(File file, String csn) 打印到文件中,指定字符集,自动刷新
PrintWriter(OutputStream out) 打印到OutputStream中,不自动刷新
PrintWriter(OutputStream out, boolean autoFlush) 打印到输出流中,autoFlush为true就自动刷新
PrintWriter(String fileName) 打印到文件名为fileName的文件中
PrintWriter(String fileName, String csn) 打印到文件名为fileName的文件中,指定字符编码
PrintWriter(Writer out) 打印到字符输出流中
PrintWriter(Writer out, boolean autoFlush) 打印到字符输出流中,autoFlush为true时自动刷新

成员方法

这个方法有点多,分开写比较好。

可以自动刷新的方法

注意了,只有下表的方法可以自动刷新,其他方法是不会自动刷新的。
自动刷新的情况:

  • 调用println()方法
  • 调用printf()方法
  • 调用format()方法
方法 描述
PrintWriter format(Locale l, String format, Object... args) 使用指定格式字符串和参数将一个格式化字符串写入此 writer 中。
PrintWriter format(String format, Object... args) 使用指定格式字符串和参数将一个格式化字符串写入此 writer 中。
PrintWriter printf(Locale l, String format, Object... args) 使用指定格式字符串和参数将格式化的字符串写入此 writer 的便捷方法。
PrintWriter printf(String format, Object... args) 使用指定格式字符串和参数将格式化的字符串写入此 writer 的便捷方法。
void println() 通过写入行分隔符字符串终止当前行。
void println(boolean x) 打印 boolean 值,然后终止该行。
void println(char x) 打印字符,然后终止该行。
void println(char[] x) 打印字符数组,然后终止该行。
void println(double x) 打印双精度浮点数,然后终止该行。
void println(float x) 打印浮点数,然后终止该行。
void println(int x) 打印整数,然后终止该行。
void println(long x) 打印 long 整数,然后终止该行。
void println(Object x) 打印 Object,然后终止该行。
void println(String x) 打印 String,然后终止该行。

追加方法

方法 描述
PrintWriter append(char c) 将指定字符添加到此 writer。
PrintWriter append(CharSequence csq) 将指定的字符序列添加到此 writer。
PrintWriter append(CharSequence csq, int start, int end) 将指定字符序列的子序列添加到此 writer。
方法 描述
void print(boolean b) 打印 boolean 值。
void print(char c) 打印字符。
void print(char[] s) 打印字符数组。
void print(double d) 打印 double 精度浮点数。
void print(float f) 打印一个浮点数。
void print(int i) 打印整数。
void print(long l) 打印 long 整数。
void print(Object obj) 打印对象。
void print(String s) 打印字符串。

write(…)方法

方法 描述
void write(char[] buf) 写入字符数组。
void write(char[] buf, int off, int len) 写入字符数组的某一部分。
void write(int c) 写入单个字符。
void write(String s) 写入字符串。
void write(String s, int off, int len) 写入字符串的某一部分。

错误相关方法

方法 描述
boolean checkError() 如果流没有关闭,则刷新流且检查其错误状态。
protected void clearError() 清除此流的错误状态。
protected void setError() 指示已发生错误。

刷新方法

方法 描述
void close() 关闭该流并释放与之关联的所有系统资源。
void flush() 刷新该流的缓冲。

实例

不自动刷新的例子

来看一个不自动刷新的例子

1
2
3
4
5
public static void main(String[] args) throws IOException
{
BufferedWriter writer=new BufferedWriter(new FileWriter("xiaoming.txt"));
writer.write("小明");
}

这样会不会在xiaoming.txt文件中写入“小明”这个字符串呢?答案是不会,因为,现在写的这个小明保存在BufferedWriter的内部字符数组中,并没有写到本地,想写到本地的话需要调用flush()方法刷新内部字符数组,把放在内部字符数组中的“小明”这个字符串写到本地中去。BufferedWriter内部缓存放满了,或者放不下了,会自动把内部缓存刷到本地中去。或者调用close()方法的时候也会刷新一下内部缓存。
现在小明两个字符没有充满内部缓存,BufferedWriter不会自动刷新内部缓存。而我们没有调用flush()方法,也没有关闭BufferdWriter。所以文件不会写入数据,main()方法结束后数据丢失。

PrintWriter自动刷新实例

使用PrintWriter并使用自动刷新刷新:

1
2
3
4
5
6
7
8
9
public static void main(String[] args) throws IOException
{
//自动刷新到文件xiaoming.txt中
PrintWriter writer=new PrintWriter(new FileWriter("xiaoming.txt"),true);
//使用下面三个方法会自动刷新
writer.println("小明");
writer.printf("%-10s#\n","小明");
writer.format("%-10d#", 12345);
}

运行结果,xiaoming.txt文件中的内容:

1
2
3
小明
小明
12345

PrintWriter不会自动刷新的方法

PrintWriter的其他不会自动刷新的方法:

  • write()方法
  • print()方法
  • append()方法
1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) throws IOException
{
PrintWriter writer=new PrintWriter(new FileWriter("xiaoming.txt"),true);
writer.println("小明");
writer.printf("%-10s#\n","小明");
writer.format("%-10d#\n", 12345);
writer.write("我是不会写到文件中去的");
writer.print("我也不会自动写到文件中去的");
writer.append("我也一样不会写到文件中去的");
}

运行后,xiaoming.txt中的内容如下:

1
2
3
小明
小明
12345

可以看到这三个方法是不会自动刷新的,需要显示调用flush()方法进行写入,或者在写文件最后调用close()写入文件。这里调用close()方法在最后关闭文件,close()方法会把缓存的字符刷新到文件中去。

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) throws IOException
{
PrintWriter writer=new PrintWriter(new FileWriter("xiaoming.txt"),true);
writer.println("小明");
writer.printf("%-10s#\n","小明");
writer.format("%-10d#\n", 12345);
writer.write("我是不会写到文件中去的\n");
writer.print("我也不会自动写到文件中去的\n");
writer.append("我也一样不会写到文件中去的\n");
writer.close();
}

运行上面代码后,xiaoming.txt文件中的内容如下

1
2
3
4
5
6
7
小明
小明
12345
我是不会写到文件中去的
我也不会自动写到文件中去的
我也一样不会写到文件中去的

疑惑的地方write()方法和print(...)方法append()方法的区别?

write()方法和print(...)方法的区别

  • 共同点:两者都不刷新页面,只在原来的页面写数据.最终都是重写了抽象类Writer里面的write方法.
  • 不同点:print方法可以将各种类型的数据转换成字符串的形式输出。重载的write方法只能输出字符、字符数组、字符串等与字符相关的数据。

这些疑惑看下面的源码就知道了。

看源码

字段

1
2
3
4
5
6
7
protected Writer out;//字符输出流
private final boolean autoFlush;//自动刷新
private boolean trouble = false;
private Formatter formatter;
private PrintStream psOut = null;
private final String lineSeparator;//换行符

构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public PrintWriter (Writer out) {
//调用另一个:PrintWriter(Writer out, boolean autoFlush)
this(out, false);
}
public PrintWriter(Writer out, boolean autoFlush) {
super(out);
//设置字符输出流
this.out = out;
//设置自动刷新标志
this.autoFlush = autoFlush;
//设置换行符
lineSeparator = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("line.separator"));
}
public PrintWriter(OutputStream out) {
//调用另一个构造方法:PrintWriter(OutputStream out, boolean autoFlush)
this(out, false);
}
public PrintWriter(OutputStream out, boolean autoFlush) {
//把字节流封装成字符流:BufferedWriter后,
//调用:PrintWriter(OutputStream out, boolean autoFlush)
this(new BufferedWriter(new OutputStreamWriter(out)), autoFlush);

// save print stream for error propagation
if (out instanceof java.io.PrintStream) {
psOut = (PrintStream) out;
}
}
public PrintWriter(String fileName) throws FileNotFoundException {
//包装后调用:PrintWriter(OutputStream out, boolean autoFlush)
this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName))),
false);
}
//搞不懂为什么这里要颠倒一下参数的顺序?
private PrintWriter(Charset charset, File file)
throws FileNotFoundException
{
//包装后调用:PrintWriter(OutputStream out, boolean autoFlush)
this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charset)),
false);
}
public PrintWriter(String fileName, String csn)
throws FileNotFoundException, UnsupportedEncodingException
{
//调用:PrintWriter(Charset charset, File file)
this(toCharset(csn), new File(fileName));
}
public PrintWriter(File file) throws FileNotFoundException {
//调用:PrintWriter(OutputStream out, boolean autoFlush)
this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))),
false);
}
public PrintWriter(File file, String csn)
throws FileNotFoundException, UnsupportedEncodingException
{
//调用:PrintWriter(Charset charset, File file)
this(toCharset(csn), file);
}

看源码就知道了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public void print(boolean b) {
write(b ? "true" : "false");
}
public void print(char c) {
write(c);
}
public void print(int i) {
write(String.valueOf(i));
}
public void print(long l) {
write(String.valueOf(l));
}
public void print(float f) {
write(String.valueOf(f));
}
public void print(double d) {
write(String.valueOf(d));
}
public void print(char s[]) {
write(s);
}
public void print(String s) {
if (s == null) {
s = "null";
}
write(s);
}
public void print(Object obj) {
write(String.valueOf(obj));
}

write(…)方法源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
protected Writer out;
public void write(int c) {
try {
synchronized (lock) {
ensureOpen();
//调用低层Writer的write(int)方法
out.write(c);
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;
}
}
public void write(char buf[], int off, int len) {
try {
synchronized (lock) {
ensureOpen();
//调用低层writer的写字符数组方法
out.write(buf, off, len);
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;
}
}
public void write(char buf[]) {
//调用自己的write(char buf[], int off, int len)
write(buf, 0, buf.length);
}
public void write(String s, int off, int len) {
try {
synchronized (lock) {
ensureOpen();
//调用低层得写字符串方法
out.write(s, off, len);
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;
}
}
public void write(String s) {
//调用自己的写字符串方法
write(s, 0, s.length());
}

可以看到PrintWriter的writer(…)方法最终调用低层流的writer(..)来实现的。

append(…)方法源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public PrintWriter append(CharSequence csq) {
if (csq == null)
write("null");
else
write(csq.toString());
return this;
}
public PrintWriter append(CharSequence csq, int start, int end) {
CharSequence cs = (csq == null ? "null" : csq);
write(cs.subSequence(start, end).toString());
return this;
}
public PrintWriter append(char c) {
write(c);
return this;
}

我们可以看到append方法最终也是调用PrintWriterwrite()方法来实现的,write()方法和append方法不同的地方在于返回值吧,append返回值为PrintWriter,而write()返回值为void
print()方法和append()方法相同的地方是它们都通过write()方法实现。

println(…)方法源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
private void newLine() {
try {
synchronized (lock) {
ensureOpen();
out.write(lineSeparator);
if (autoFlush)
out.flush();
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;
}
}
public void println() {
newLine();
}
public void println(boolean x) {
synchronized (lock) {
print(x);
println();
}
}
public void println(char x) {
synchronized (lock) {
print(x);
println();
}
}
public void println(int x) {
synchronized (lock) {
print(x);
println();
}
}
public void println(long x) {
synchronized (lock) {
print(x);
println();
}
}
public void println(float x) {
synchronized (lock) {
print(x);
println();
}
}
public void println(double x) {
synchronized (lock) {
print(x);
println();
}
}
public void println(char x[]) {
synchronized (lock) {
print(x);
println();
}
}
public void println(String x) {
synchronized (lock) {
print(x);
println();
}
}
public void println(Object x) {
String s = String.valueOf(x);
synchronized (lock) {
print(s);
println();
}
}

我们看到println(…)最终调用print(…)和println()方法事项,print(…)方法前面已经说了。
而println()方法,调用newLine()这私有的方法来进行换行和刷新操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void newLine() {
try {
synchronized (lock) {
ensureOpen();
//写换行符
out.write(lineSeparator);
if (autoFlush)
out.flush();//刷新缓存
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;
}
}

因为println()方法提供和换行和自动刷新功能,所以其他println(…)调用了该方法也就能实现自动刷新的功能了。

format()方法源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//格式化类:java.util.Formatter
private Formatter formatter;
......
public PrintWriter format(String format, Object ... args) {
try {
synchronized (lock) {
ensureOpen();
if ((formatter == null)
|| (formatter.locale() != Locale.getDefault()))
formatter = new Formatter(this);
formatter.format(Locale.getDefault(), format, args);
if (autoFlush)
out.flush();
}
} catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
} catch (IOException x) {
trouble = true;
}
return this;
}
public PrintWriter format(Locale l, String format, Object ... args) {
try {
synchronized (lock) {
ensureOpen();
if ((formatter == null) || (formatter.locale() != l))
formatter = new Formatter(this, l);
formatter.format(l, format, args);
if (autoFlush)
out.flush();
}
} catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
} catch (IOException x) {
trouble = true;
}
return this;
}

这里的format()方法其实就java.util.Formatter类的format()方法实现的。而且format()方法调用了out.flush()来实现自动刷新。

printf()方法源码

1
2
3
4
5
6
public PrintWriter printf(String format, Object ... args) {
return format(format, args);
}
public PrintWriter printf(Locale l, String format, Object ... args) {
return format(l, format, args);
}

可以看到这里的printf()方法最终是调用了format(``.``.)方法来实现的。因为format()方法中调用了out.flush()来自动刷新,所以printf()方法也就跟着实现了自动刷新功能。
这些自动刷新功能说的很玄乎,其实不过就是下面两句话:

1
2
3
4
......
if (autoFlush)
out.flush();//刷新缓存
......

其他方法如write()方法,print()方法,append()方法没有上面这句话所以就没了自动刷新功能。

就这样吧

Java IO流 接口

Closeable接口

public interface Closeable
Closeable 是可以关闭的数据源或目标。调用 close 方法可释放对象保存的资源(如打开文件)。
Closeable接口

void close() throws IOException关闭此流,并释放与此流关联的所有系统资源。如果已经关闭该流,则调用此方法无效。
抛出:
&emsp;&emsp;&emsp;&emsp;IOException - 如果发生 I/O 错误

Flushable接口

public interface Flushable

Flushable 是可刷新数据的目标地。调用 flush 方法将所有已缓冲输出写入底层流。

方法详细信息
void flush() throws IOException通过将所有已缓冲输出写入底层流来刷新此流。

抛出:
&emsp;&emsp;&emsp;&emsp; IOException - 如果发生 I/O 错误

Appendable接口

public interface Appendable能够被添加 char 序列和值的对象。

如果某个类的实例打算接收取自 Formatter 的格式化输出,那么该类必须实现 Appendable 接口。

要添加的字符应该是有效的 Unicode 字符,正如 Unicode Character Representation 中描述的那样。注意,增补字符可能由多个 16 位 char 值组成。

Appendable 对于多线程访问而言没必要是安全的。线程安全由扩展和实现此接口的类负责。

由于此接口可能由具有不同的错误处理风格的现有类实现,所以无法保证错误不会传播给调用者。

方法 描述
Appendable append(char c) 向此 Appendable 添加指定字符。
Appendable append(CharSequence csq) 向此 Appendable 添加指定的字符序列。
Appendable append(CharSequence csq, int start, int end) 向此 Appendable 添加指定字符序列的子序列。