NanYin的博客

记录生活点滴


  • Home

  • About

  • Tags

  • Categories

  • Archives

  • Search

Linux常用命令整理

Posted on 2019-01-29
Words count in article: 1.4k | Reading time ≈ 5

linux中必须掌握的基本命令

在开始前,我推荐安装 trdr(too long dont read)工具,用来简化man命令,来实现查看命令含义和用法的作用。

一 、系统工作命令

  1. echo

    echo命令一般用于shell中打印变量或者字符串的作用,主要用来显示和提醒。

    • 输出一段信息 如:echo "hello world" 其中"不是必须的
    • 输出一段带环境变量的信息 如:echo "My Path is $PATH"
    • 输出并不带换行符号 如:echo -n "Hello World"
    • 输出一段激活转依字符 如: echo -e "Column 1 \t Column 2"
  2. date

    date 命令主要用来设置和打印系统时间。

    • 输出默认时区格式的事件 date +"%c"
    • 输出当前事件按照UTC和iso格式输出 date -u +"%Y-%m-%dT%H:%M:%SZ"
    • 输出事件戳 date +"%s"
  3. reboot 命令主要用来重启系统

    • 重启系统 reboot
    • 强制重启 reboot -f
  4. poweroff 命令用户关机

  5. wget 命令 用于从web中下载文件,支持 http,https,ftp

    • 获取URL中的内容到foo中 wget https://example.com/foo
    • 获取URL中的内容到bar中 wget -O bar https://example.com/foo
    • 继续未完成的下载 wget -c https://example.com
    • 后台下载 wget -q https://example.com
  6. ps命令主要用于查看正在运行的进程的信息

    • 打印全部正在执行的进程 ps aux
    • 打印全部正在执行的进程并且伴有完整的命令串 ps auxww
    • 通过 grep 管道过滤 ps aux | grep "tomcat"
    • 得到进程的父进程pid :ps -o ppid= -p pid
  7. top命令 主要用户动态的实时的显示正在运行的程序

    • top -i 不显示空闲或者是僵尸进程
    • top -u $user 显示指定用户的进程
    • top -p PID 只显示指定进程PID
    • top -p $(pgrep -d ',' process_name) 通过进程名来查询
  8. pidof 命令主要用于查找指定进程的id号

    • pidof -s 进返回一个进程号
    • pidof -c 仅显示具有相同root目录下的进程

二、系统状态

  1. ifconfig 命令主要用于网络接口的查看和配置
  • Ifconfig eth0 查看etho0接口网络设置
  • ifconfig -a 查看所有接口的详细信息
  • ifconfig eth0 down 关闭接口eth0
  • ifconfig eth0 ipaddr 设置端口ip地址
  1. uname 打印出现当前机器和系统的相关信息
  • uname -n 打印当前的hostname
  • uname -a 打印当前可用的系统的信息,包括硬件软件。
  1. who 打印哪些用户登录再这台机器上
  • who 显示username等比较全的信息
  • who am i 打印出当前用户
  1. last 用于查看当前用户最近的登录信息

三、文件和目录

  1. pwd 用于打印当前目录

  2. cd 用于切换目录

  3. ls 用于罗列目录下的文件或者文件夹

  • ls -l按行罗列文件或文件夹
  • ls -a 显示所有文件,包括隐藏的文件。
  • ls -lh 按照大小排泄排列
  1. touch 命令主要用于改变文件的访问时间和修改时间。
  • touch filename 以当前时间创建文件
  • touch -t yyyy-mm-dd filename 以某一时间来改变文件时间
  1. mkdir 命令主要用于创建文件
  • mkdir dir 创建文件夹
  • mkdir -p /path/dir 创建指定路径下的文件
  1. cp 命令主要用于文件的复制
  • cp path/to/file.ext path/to/copy.ext 复制文件到另一个地址
  • cp path/to/file.ext path/to/target_parent_directory 复制文件到指定地址,并且保持文件名不变
  • cp -r path/to/directory path/to/copy 递归的复制整个文件夹及其中的内容到指定目录
  • cp -i *.txt path/to/target_directory 以交互的模式复制文件到指定文件夹中
  1. mv 命令主要用于移动文件
  • mv source target 移动文件
  • mv -f 强制移动,覆盖现有文件时不要提示
  1. rm命令主要用于删除文件
  • rm path/to/file path/to/another/file 删除特定文件夹中的文件
  • rm -r path/to/directory 递归删除文件夹及文件夹中的文件
  • rm -rf 递归删除,并且不要提示信息
  1. dd 命令主要用于转化和复制文件,制作usb镜像
  • Clone a drive to another drive with 4MB block and ignore error dd if=/dev/source_drive of=/dev/dest_drive bs=4m conv=noerror
  1. file命令主要用于确定文件类型
  • file filename 输出文件类型
  • file -s filename 用于特殊的文件或者设备文件。

四、文本编辑命令

  1. cat 打印和串联文件
  • cat file 打印文件内容到标准输出
  • cat file1 file2 > targetFile 将多个文件内容串联到目标文件中
  • cat -n file 打印出行数到标准输出
  1. more 用于互动的阅读文件内容,可以上下滚动
  • more file 打开文件,<space>用于移动到下一页,/用于搜索文件内容, q用于退出当前界面
  1. head 输出文件开始的部分
  • head -n count_of_line filename 查看文件的前n行
  • head -c number_of_byte filename 查看文件的前n个字节
  1. tail 输出文件最后的部分
  • tail -n num file打印出文件最后num行
  • tail -n +num file 打印出文件从num开始的几行
  • tail -f file 保持阅读状态直到 Ctrl + c
  1. wc命令用于统计文件的单词,字节,行数
  • wc -l filename 统计文件的行数
  • wc -w filename 统计文件的字数
  • wc -c filename 统计文件的字节数
  1. 显示文件的状态
  • stat filename 显示文件的属性比如大小,权限等。
  • stat -x filename格式化显示文件的详细属性信息。

Linux常用命令整理

Posted on 2019-01-29 | In Linux
Words count in article: 1.4k | Reading time ≈ 5

linux中必须掌握的基本命令

在开始前,我推荐安装 trdr(too long dont read)工具,用来简化man命令,来实现查看命令含义和用法的作用。

一 、系统工作命令

  1. echo

    echo命令一般用于shell中打印变量或者字符串的作用,主要用来显示和提醒。

    • 输出一段信息 如:echo "hello world" 其中"不是必须的
    • 输出一段带环境变量的信息 如:echo "My Path is $PATH"
    • 输出并不带换行符号 如:echo -n "Hello World"
    • 输出一段激活转依字符 如: echo -e "Column 1 \t Column 2"
  2. date

    date 命令主要用来设置和打印系统时间。

    • 输出默认时区格式的事件 date +"%c"
    • 输出当前事件按照UTC和iso格式输出 date -u +"%Y-%m-%dT%H:%M:%SZ"
    • 输出事件戳 date +"%s"
  3. reboot 命令主要用来重启系统

    • 重启系统 reboot
    • 强制重启 reboot -f
  4. poweroff 命令用户关机

  5. wget 命令 用于从web中下载文件,支持 http,https,ftp

    • 获取URL中的内容到foo中 wget https://example.com/foo
    • 获取URL中的内容到bar中 wget -O bar https://example.com/foo
    • 继续未完成的下载 wget -c https://example.com
    • 后台下载 wget -q https://example.com
  6. ps命令主要用于查看正在运行的进程的信息

    • 打印全部正在执行的进程 ps aux
    • 打印全部正在执行的进程并且伴有完整的命令串 ps auxww
    • 通过 grep 管道过滤 ps aux | grep "tomcat"
    • 得到进程的父进程pid :ps -o ppid= -p pid
  7. top命令 主要用户动态的实时的显示正在运行的程序

    • top -i 不显示空闲或者是僵尸进程
    • top -u $user 显示指定用户的进程
    • top -p PID 只显示指定进程PID
    • top -p $(pgrep -d ',' process_name) 通过进程名来查询
  8. pidof 命令主要用于查找指定进程的id号

    • pidof -s 进返回一个进程号
    • pidof -c 仅显示具有相同root目录下的进程

二、系统状态

  1. ifconfig 命令主要用于网络接口的查看和配置
  • Ifconfig eth0 查看etho0接口网络设置
  • ifconfig -a 查看所有接口的详细信息
  • ifconfig eth0 down 关闭接口eth0
  • ifconfig eth0 ipaddr 设置端口ip地址
  1. uname 打印出现当前机器和系统的相关信息
  • uname -n 打印当前的hostname
  • uname -a 打印当前可用的系统的信息,包括硬件软件。
  1. who 打印哪些用户登录再这台机器上
  • who 显示username等比较全的信息
  • who am i 打印出当前用户
  1. last 用于查看当前用户最近的登录信息

三、文件和目录

  1. pwd 用于打印当前目录

  2. cd 用于切换目录

  3. ls 用于罗列目录下的文件或者文件夹

  • ls -l按行罗列文件或文件夹
  • ls -a 显示所有文件,包括隐藏的文件。
  • ls -lh 按照大小排泄排列
  1. touch 命令主要用于改变文件的访问时间和修改时间。
  • touch filename 以当前时间创建文件
  • touch -t yyyy-mm-dd filename 以某一时间来改变文件时间
  1. mkdir 命令主要用于创建文件
  • mkdir dir 创建文件夹
  • mkdir -p /path/dir 创建指定路径下的文件
  1. cp 命令主要用于文件的复制
  • cp path/to/file.ext path/to/copy.ext 复制文件到另一个地址
  • cp path/to/file.ext path/to/target_parent_directory 复制文件到指定地址,并且保持文件名不变
  • cp -r path/to/directory path/to/copy 递归的复制整个文件夹及其中的内容到指定目录
  • cp -i *.txt path/to/target_directory 以交互的模式复制文件到指定文件夹中
  1. mv 命令主要用于移动文件
  • mv source target 移动文件
  • mv -f 强制移动,覆盖现有文件时不要提示
  1. rm命令主要用于删除文件
  • rm path/to/file path/to/another/file 删除特定文件夹中的文件
  • rm -r path/to/directory 递归删除文件夹及文件夹中的文件
  • rm -rf 递归删除,并且不要提示信息
  1. dd 命令主要用于转化和复制文件,制作usb镜像
  • Clone a drive to another drive with 4MB block and ignore error dd if=/dev/source_drive of=/dev/dest_drive bs=4m conv=noerror
  1. file命令主要用于确定文件类型
  • file filename 输出文件类型
  • file -s filename 用于特殊的文件或者设备文件。

四、文本编辑命令

  1. cat 打印和串联文件
  • cat file 打印文件内容到标准输出
  • cat file1 file2 > targetFile 将多个文件内容串联到目标文件中
  • cat -n file 打印出行数到标准输出
  1. more 用于互动的阅读文件内容,可以上下滚动
  • more file 打开文件,<space>用于移动到下一页,/用于搜索文件内容, q用于退出当前界面
  1. head 输出文件开始的部分
  • head -n count_of_line filename 查看文件的前n行
  • head -c number_of_byte filename 查看文件的前n个字节
  1. tail 输出文件最后的部分
  • tail -n num file打印出文件最后num行
  • tail -n +num file 打印出文件从num开始的几行
  • tail -f file 保持阅读状态直到 Ctrl + c
  1. wc命令用于统计文件的单词,字节,行数
  • wc -l filename 统计文件的行数
  • wc -w filename 统计文件的字数
  • wc -c filename 统计文件的字节数
  1. 显示文件的状态
  • stat filename 显示文件的属性比如大小,权限等。
  • stat -x filename格式化显示文件的详细属性信息。

Ubuntu18.04 配置全过程

Posted on 2019-01-04
Words count in article: 2.2k | Reading time ≈ 10

Ubuntu18.04 配置全过程

因为最近在弄win10双系统,又因为PopOs在装机的时候设置grub比较麻烦,所以换成最新的Ubuntu稳定版来做成win/ubunut双系统。

安装问题

在装机的需要使用grub做启动引导。所以设置保留BIOS启动区域而不是挂载/boot。亲身经历,UEFI启动模式下,Ubuntu的引导挂载应该是biosgrub,否则出错。导致无法家在grub。

界面配置

安装gnome-tweak-tool

1
sudo apt install gnome-tweak-tool

如果需要更改shell则需要安装:

1
sudo apt install gnome-shell-extensions

这样就能轻松愉快的使用gnome-tewak-tool了。

主题方案

主界面

因为前一阵使用Pop!_OS发现它默认的主题非常好看,所以下载Popos默认的主题,然后在gnome-tweak-tool中设置好就ok了,这里是PopOs的gtk主题地址。但是发现PopOs默认的输入法输入框是默认米黄色的,打字的时候不太显眼,所以在基础上添加了如下代码。添加方法:

  1. 根据说明下载Pop-theme
1
2
3
sudo add-apt-repository ppa:system76/pop
sudo apt update
sudo apt install pop-theme
  1. 定位到 /usr/share/themes/Pop/gnome-shell/ 的gnome-shell.css和Pop.css 将IBus的界面代码替换成如下代码
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
.candidate-popup-content {
padding: 8px;
spacing: 0;
}

.candidate-index {
padding: 0 4px 0 0;
color: rgba(255, 255, 255, 0.5);
}

.candidate-box:selected .candidate-index {
color: rgba(255, 255, 255, 0.5);
}

.candidate-box {
transition-duration: 0ms;
min-height: 28px;
padding: 0 8px;
border-radius: 8px;
}

.candidate-box:hover {
background-color: rgba(255, 255, 255, 0.06);
color: rgba(255, 255, 255, 0.9);
}

.candidate-box:active {
background-color: rgba(255, 255, 255, 0.1);
color: rgba(255, 255, 255, 0.9);
}

.candidate-box:selected {
background-color: #0046DB;
color: rgba(255, 255, 255, 0.85);
}

.candidate-page-button-box {
height: 28px;
}

.vertical .candidate-page-button-box {
padding-top: 0;
}

.horizontal .candidate-page-button-box {
padding-left: 0;
}

.candidate-page-button {
min-width: 28px;
min-height: 28px;
padding: 0;
}

.candidate-page-button-previous {
border-radius: 8px;
border-right-width: 0;
}

.candidate-page-button-next {
border-radius: 8px;
}

.candidate-page-button-icon {
icon-size: 1.23077em;
}

最后成品:

输入法

  1. 应用程序窗口用的是 macOS High Sierra 主题,仿照MAC的窗口。比较漂亮。macOS High Sierra 主题,下载下来后解压后放到 /usr/share/themes 下,之后就能在gnome-tweak-tool中看到了,设置完成后可以看到如下效果。

主题

gdm主题

gdm主题选用类似MAC登陆的主题,界面美观,而且还有毛玻璃效果。High Ubunterra下载后解压之后,直接命令行执行./install.sh。之后直接右键设置后,直接将锁屏界面直接设置好。其实主要目的的就是将 /usr/share/gnome-shell/theme 中的ubuntu.css替换掉。

grub主题

grub的主题比较少,推荐的就是Breeze GRUB2 theme 是一款比较漂亮美观的主题。具体成果界面可以点击链接地址查看。

普通主题需要作如下顺序的操作,但是 Breeze Grub2 theme 有脚本,直接执行解压后的脚本程序,就ok了。

  1. 创建grub的主题文件夹
1
sudo mkdir /boot/grub/themes
  1. 将主题包移动到这个目录下
1
sudo cp 主题包名 /boot/grub/themes/
  1. 修改主题的配置文件
1
sudo vim /etc/grub.d/00_header
  1. 在配置文件中添加
1
2
GRUB_THEME="/boot/grub/themes/主题包名/theme.txt"
GRUB_GFXMODE="1920x1080x32"
  1. 最后执行 update-grub 命令

gnome拓展推荐

我没有什么特殊的拓展安装,在Ubuntu系统下,一般安装如下拓展。

  1. User themes 可以使用用户目录下的主题

  2. removeable Drive Menu 当插入移除设备的时候在上方有状态图标提醒。

  3. Caffeine 可以关闭屏保和自动挂起,很好用。

  4. Dash To Dock 如果你不喜欢ubuntu的侧边dock可以尝试这个dock,比较美观的dock,可以设置在侧边或者在底下。

  5. openWeather 可以在顶部栏中查看天气,点击可以查看最近天气,需要手动添加地址。

  6. ClipBord Indicator 是可以记录粘贴板的内容,有时可以避免重复复制的操作。

推荐的有这几个,但是gnome-shell拓展可谓非常的多,并且大多数都非常好用,可以去 gnome extensions 官网 看一看,看有没有更适合自己的。

配置Vim和终端

配置vim

我vim使用的是vundle插件管理器,首先要安装vundle.

首先要下载git

1
sudo apt-get install git

然后安装vundle

1
git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vi

将如下配置替换到.vimrc 中,首先要进行备份哦!!!

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
set nocompatible " be iMproved, required
filetype off " required
set number
let mapleader=","
set rtp+=~/.vim/bundle/Vundle.vim
call vundle#begin()

Plugin 'VundleVim/Vundle.vim'
Plugin 'scrooloose/nerdtree'
Plugin 'kien/ctrlp.vim'
Plugin 'junegunn/fzf'
Plugin 'Shougo/neocomplete.vim'
Plugin 'vim-airline/vim-airline'
Plugin 'vim-airline/vim-airline-themes'
Plugin 'terryma/vim-smooth-scroll'
Plugin 'terryma/vim-expand-region'
Plugin 'tpope/vim-commentary'
Plugin 'tpope/vim-surround'
Plugin 'godlygeek/tabular'
Plugin 'plasticboy/vim-markdown'
Plugin 'tpope/vim-fugitive'
Plugin 'airblade/vim-gitgutter'
Plugin 'justinmk/vim-sneak'
Plugin 'powerline/fonts'
Plugin 'scrooloose/nerdcommenter'
Plugin 'suan/vim-instant-markdown'
Plugin 'mzlogin/vim-markdown-toc'
call vundle#end() " required
filetype plugin indent on
" let g:airline_powerline_fonts = 1
map <C-n> :NERDTreeToggle<CR>
" smooth-scroll
function SmoothScroll(up)
if a:up
let scrollaction="^Y"
else
let scrollaction="^E"
endif
exec "normal " . scrollaction
redraw
let counter=1
while counter<&scroll
let counter+=1
sleep 10m
redraw
exec "normal " . scrollaction
endwhile
endfunction
noremap <silent> <c-u> :call smooth_scroll#up(&scroll, 15, 2)<CR>
noremap <silent> <c-d> :call smooth_scroll#down(&scroll, 15, 2)<CR>
noremap <silent> <c-b> :call smooth_scroll#up(&scroll*2, 10, 4)<CR>
noremap <silent> <c-f> :call smooth_scroll#down(&scroll*2, 10, 4)<CR>
set mouse=
map <ScrollWheelUp> <C-Y>
map <ScrollWheelDown> <C-E>
"高亮错误
" let g:syntastic_enable_highlighting=1
" 快速选中
" let g:expand_region_use_select_mode = 1
" map w <Plug>(expand_region_expand)
" map W <Plug>(expand_region_shrink)
" markdown setting
let g:vim_markdown_toc_autofit = 1
let g:vim_markdown_autowrite = 1
let g:vim_markdown_folding_disabled = 1
set updatetime=100
let g:gitgutter_grep=''
let g:gitgutter_terminal_reports_focus=0

最后在vim中使用:PluginInstall 安装插件。具体的插件的作用以后会专门写一篇来介绍。也可自行百度。

配置终端

我使用的是zsh,首先安装zsh

1
sudo apt-get install zsh

然后配置将zsh为默认

1
sudo chsh -s /bin/zsh

最后使用著名的oh-my-zsh 来装点B吧。

1
sh -c "$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"

配置git环境和github连接

安装好git后,配置git

1
2
git config --global user.name "你的github用户名"
git config --global user.email "你的github邮箱地址"

生成密钥,一路enter就可以

1
ssh-keygen -t rsa -C "你自己的github对应的邮箱地址"

将文件中生成的密钥复制到github上的配置中去

1
vim ~/.ssh/id_rsa.pub

检查是否配置成功

1
ssh -T git@github.com

配置Java开发环境

JDK

  1. 在官网下载jdk源码,并在解压前创建 /usr/local/java 文件夹
1
mkdir /usr/local/java
  1. 将文件解压到刚创建的文件夹中,也可以解压到当前文件夹下,然后移动到刚才创建的文件夹下。
1
2
3
tar -xzvf jdk1.8.0_191.tar.gz 

mv jdk1.8.0_191 /usr/local/java/
  1. 使用vim编辑/etc/profile 文件,在结尾处添加如下内容
1
2
3
export JAVA_HOME=/usr/local/java/jdk1.8.0_191 
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
  1. 执行 source /etc/profile 刷新配置文件,然后使用 java -version来查看是否成功。

java

Maven

  1. 在官网 下载源码包,同样解压前创建 /usr/local/maven文件夹

  2. 将文件解压到/usr/local/maven中。

  3. 使用vim编辑 /etc/profile文件,在末尾添加如下内容

1
2
export MAVEN_HOME=/usr/local/maven/apache-maven-3.6.0
export PATH=$MAVEN_HOME/bin:$PATH
  1. 同样使用source /etc/profile 命令刷新配置文件,使用mvn -v 命令查看是否安装成功。

  2. 配置阿里maven镜像

编辑 /usr/local/maven/apache-maven-3.6.0/conf/setting.xml,在<mirrors></mirrors>标签内添加如下内容

1
2
3
4
5
6
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
  <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>

maven

Mysql安装和数据库管理软件推荐

  1. 进入官网下载ubuntu的deb包,直接使用命令安装
1
sudo dpkg -i mysql-apt-config_0.8.11-1_all.deb
  1. 刷新源列表
1
sudo apt-get update
  1. 安装mysql
1
sudo apt-get install mysql-server mysql-client

其中会让你输入密码,输入就行了。

Ubuntu18.04 配置全过程

Posted on 2019-01-04 | In Linux
Words count in article: 2.2k | Reading time ≈ 10

Ubuntu18.04 配置全过程

因为最近在弄win10双系统,又因为PopOs在装机的时候设置grub比较麻烦,所以换成最新的Ubuntu稳定版来做成win/ubunut双系统。

安装问题

在装机的需要使用grub做启动引导。所以设置保留BIOS启动区域而不是挂载/boot。亲身经历,UEFI启动模式下,Ubuntu的引导挂载应该是biosgrub,否则出错。导致无法家在grub。

界面配置

安装gnome-tweak-tool

1
sudo apt install gnome-tweak-tool

如果需要更改shell则需要安装:

1
sudo apt install gnome-shell-extensions

这样就能轻松愉快的使用gnome-tewak-tool了。

主题方案

主界面

因为前一阵使用Pop!_OS发现它默认的主题非常好看,所以下载Popos默认的主题,然后在gnome-tweak-tool中设置好就ok了,这里是PopOs的gtk主题地址。但是发现PopOs默认的输入法输入框是默认米黄色的,打字的时候不太显眼,所以在基础上添加了如下代码。添加方法:

  1. 根据说明下载Pop-theme
1
2
3
sudo add-apt-repository ppa:system76/pop
sudo apt update
sudo apt install pop-theme
  1. 定位到 /usr/share/themes/Pop/gnome-shell/ 的gnome-shell.css和Pop.css 将IBus的界面代码替换成如下代码
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
.candidate-popup-content {
padding: 8px;
spacing: 0;
}

.candidate-index {
padding: 0 4px 0 0;
color: rgba(255, 255, 255, 0.5);
}

.candidate-box:selected .candidate-index {
color: rgba(255, 255, 255, 0.5);
}

.candidate-box {
transition-duration: 0ms;
min-height: 28px;
padding: 0 8px;
border-radius: 8px;
}

.candidate-box:hover {
background-color: rgba(255, 255, 255, 0.06);
color: rgba(255, 255, 255, 0.9);
}

.candidate-box:active {
background-color: rgba(255, 255, 255, 0.1);
color: rgba(255, 255, 255, 0.9);
}

.candidate-box:selected {
background-color: #0046DB;
color: rgba(255, 255, 255, 0.85);
}

.candidate-page-button-box {
height: 28px;
}

.vertical .candidate-page-button-box {
padding-top: 0;
}

.horizontal .candidate-page-button-box {
padding-left: 0;
}

.candidate-page-button {
min-width: 28px;
min-height: 28px;
padding: 0;
}

.candidate-page-button-previous {
border-radius: 8px;
border-right-width: 0;
}

.candidate-page-button-next {
border-radius: 8px;
}

.candidate-page-button-icon {
icon-size: 1.23077em;
}

最后成品:

输入法

  1. 应用程序窗口用的是 macOS High Sierra 主题,仿照MAC的窗口。比较漂亮。macOS High Sierra 主题,下载下来后解压后放到 /usr/share/themes 下,之后就能在gnome-tweak-tool中看到了,设置完成后可以看到如下效果。

主题

gdm主题

gdm主题选用类似MAC登陆的主题,界面美观,而且还有毛玻璃效果。High Ubunterra下载后解压之后,直接命令行执行./install.sh。之后直接右键设置后,直接将锁屏界面直接设置好。其实主要目的的就是将 /usr/share/gnome-shell/theme 中的ubuntu.css替换掉。

grub主题

grub的主题比较少,推荐的就是Breeze GRUB2 theme 是一款比较漂亮美观的主题。具体成果界面可以点击链接地址查看。

普通主题需要作如下顺序的操作,但是 Breeze Grub2 theme 有脚本,直接执行解压后的脚本程序,就ok了。

  1. 创建grub的主题文件夹
1
sudo mkdir /boot/grub/themes
  1. 将主题包移动到这个目录下
1
sudo cp 主题包名 /boot/grub/themes/
  1. 修改主题的配置文件
1
sudo vim /etc/grub.d/00_header
  1. 在配置文件中添加
1
2
GRUB_THEME="/boot/grub/themes/主题包名/theme.txt"
GRUB_GFXMODE="1920x1080x32"
  1. 最后执行 update-grub 命令

gnome拓展推荐

我没有什么特殊的拓展安装,在Ubuntu系统下,一般安装如下拓展。

  1. User themes 可以使用用户目录下的主题

  2. removeable Drive Menu 当插入移除设备的时候在上方有状态图标提醒。

  3. Caffeine 可以关闭屏保和自动挂起,很好用。

  4. Dash To Dock 如果你不喜欢ubuntu的侧边dock可以尝试这个dock,比较美观的dock,可以设置在侧边或者在底下。

  5. openWeather 可以在顶部栏中查看天气,点击可以查看最近天气,需要手动添加地址。

  6. ClipBord Indicator 是可以记录粘贴板的内容,有时可以避免重复复制的操作。

推荐的有这几个,但是gnome-shell拓展可谓非常的多,并且大多数都非常好用,可以去 gnome extensions 官网 看一看,看有没有更适合自己的。

配置Vim和终端

配置vim

我vim使用的是vundle插件管理器,首先要安装vundle.

首先要下载git

1
sudo apt-get install git

然后安装vundle

1
git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vi

将如下配置替换到.vimrc 中,首先要进行备份哦!!!

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
set nocompatible " be iMproved, required
filetype off " required
set number
let mapleader=","
set rtp+=~/.vim/bundle/Vundle.vim
call vundle#begin()

Plugin 'VundleVim/Vundle.vim'
Plugin 'scrooloose/nerdtree'
Plugin 'kien/ctrlp.vim'
Plugin 'junegunn/fzf'
Plugin 'Shougo/neocomplete.vim'
Plugin 'vim-airline/vim-airline'
Plugin 'vim-airline/vim-airline-themes'
Plugin 'terryma/vim-smooth-scroll'
Plugin 'terryma/vim-expand-region'
Plugin 'tpope/vim-commentary'
Plugin 'tpope/vim-surround'
Plugin 'godlygeek/tabular'
Plugin 'plasticboy/vim-markdown'
Plugin 'tpope/vim-fugitive'
Plugin 'airblade/vim-gitgutter'
Plugin 'justinmk/vim-sneak'
Plugin 'powerline/fonts'
Plugin 'scrooloose/nerdcommenter'
Plugin 'suan/vim-instant-markdown'
Plugin 'mzlogin/vim-markdown-toc'
call vundle#end() " required
filetype plugin indent on
" let g:airline_powerline_fonts = 1
map <C-n> :NERDTreeToggle<CR>
" smooth-scroll
function SmoothScroll(up)
if a:up
let scrollaction="^Y"
else
let scrollaction="^E"
endif
exec "normal " . scrollaction
redraw
let counter=1
while counter<&scroll
let counter+=1
sleep 10m
redraw
exec "normal " . scrollaction
endwhile
endfunction
noremap <silent> <c-u> :call smooth_scroll#up(&scroll, 15, 2)<CR>
noremap <silent> <c-d> :call smooth_scroll#down(&scroll, 15, 2)<CR>
noremap <silent> <c-b> :call smooth_scroll#up(&scroll*2, 10, 4)<CR>
noremap <silent> <c-f> :call smooth_scroll#down(&scroll*2, 10, 4)<CR>
set mouse=
map <ScrollWheelUp> <C-Y>
map <ScrollWheelDown> <C-E>
"高亮错误
" let g:syntastic_enable_highlighting=1
" 快速选中
" let g:expand_region_use_select_mode = 1
" map w <Plug>(expand_region_expand)
" map W <Plug>(expand_region_shrink)
" markdown setting
let g:vim_markdown_toc_autofit = 1
let g:vim_markdown_autowrite = 1
let g:vim_markdown_folding_disabled = 1
set updatetime=100
let g:gitgutter_grep=''
let g:gitgutter_terminal_reports_focus=0

最后在vim中使用:PluginInstall 安装插件。具体的插件的作用以后会专门写一篇来介绍。也可自行百度。

配置终端

我使用的是zsh,首先安装zsh

1
sudo apt-get install zsh

然后配置将zsh为默认

1
sudo chsh -s /bin/zsh

最后使用著名的oh-my-zsh 来装点B吧。

1
sh -c "$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"

配置git环境和github连接

安装好git后,配置git

1
2
git config --global user.name "你的github用户名"
git config --global user.email "你的github邮箱地址"

生成密钥,一路enter就可以

1
ssh-keygen -t rsa -C "你自己的github对应的邮箱地址"

将文件中生成的密钥复制到github上的配置中去

1
vim ~/.ssh/id_rsa.pub

检查是否配置成功

1
ssh -T git@github.com

配置Java开发环境

JDK

  1. 在官网下载jdk源码,并在解压前创建 /usr/local/java 文件夹
1
mkdir /usr/local/java
  1. 将文件解压到刚创建的文件夹中,也可以解压到当前文件夹下,然后移动到刚才创建的文件夹下。
1
2
3
tar -xzvf jdk1.8.0_191.tar.gz 

mv jdk1.8.0_191 /usr/local/java/
  1. 使用vim编辑/etc/profile 文件,在结尾处添加如下内容
1
2
3
export JAVA_HOME=/usr/local/java/jdk1.8.0_191 
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
  1. 执行 source /etc/profile 刷新配置文件,然后使用 java -version来查看是否成功。

java

Maven

  1. 在官网 下载源码包,同样解压前创建 /usr/local/maven文件夹

  2. 将文件解压到/usr/local/maven中。

  3. 使用vim编辑 /etc/profile文件,在末尾添加如下内容

1
2
export MAVEN_HOME=/usr/local/maven/apache-maven-3.6.0
export PATH=$MAVEN_HOME/bin:$PATH
  1. 同样使用source /etc/profile 命令刷新配置文件,使用mvn -v 命令查看是否安装成功。

  2. 配置阿里maven镜像

编辑 /usr/local/maven/apache-maven-3.6.0/conf/setting.xml,在<mirrors></mirrors>标签内添加如下内容

1
2
3
4
5
6
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
  <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>

maven

Mysql安装和数据库管理软件推荐

  1. 进入官网下载ubuntu的deb包,直接使用命令安装
1
sudo dpkg -i mysql-apt-config_0.8.11-1_all.deb
  1. 刷新源列表
1
sudo apt-get update
  1. 安装mysql
1
sudo apt-get install mysql-server mysql-client

其中会让你输入密码,输入就行了。

SpringBoot过程问题

Posted on 2019-01-04 | In Spring
Words count in article: 1.5k | Reading time ≈ 6

SpringBoot过程问题

spring注解相关

@RestController

​ @RestController 是spring 4.0来引入的简化的RESTful WEB应用的创建,是一个结合@Constroller和@ResponseBody的注解,不用每个类都使用ResponseBody注解来达到处理请求的目的。

​ 控制器使用@RestController注释进行注释,因此不需要@ResponseBody。

@RequestMapping

@RequestMapping的注解中的参数:

name : 在4.0后引入的,开始我错把name当value用,结果出现 [java.lang.IllegalStateException: Ambiguous mapping found. Cannot map 'appController' bean method] 类似这种错误。其实name就是一个名字而已,没有什么特殊含义,但是官方文档中指出 在类型级别和方法级别支持!在两个级别上使用时,组合名称通过串联以“#”作为分隔符派生 。使用上,官方说明是主要可以通过使用Spring jsp tag包里面的mvcUrl,来生成jsp到controller的链接。

1
2
3
4
5
6
7
@RequestMapping("/people")
class PersonController {

@RequestMapping("/{id}")
public HttpEntity getPerson(@PathVariable String id) { ... }

}
1
2
3
<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>

<a href="${s:mvcUrl('PC#getPerson').arg(0,"123").build()}">Get Person</a>

value:用来表示主要映射,是常用的

1
2
3
4
5
@RequestMapping(value = "/user",method = RequestMethod.GET,produces = "application/json")
public @ResponseBody
List<User> findUsers() {
return userService.findUsers();
}

path: 指定访问的uri地址

1
2
3
4
5
@RequestMapping(path = "/user",method = RequestMethod.POST,name = "this is user`s post methods")
public String insertUser(){
userService.saveUser();
return null;
}

method:映射的HTTP的访问方法,有GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE

consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;

produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;

params: 指定request中必须包含某些参数值时,才让该方法处理。

headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求。

Jpa常用注解

@Entity

标注在类外侧,标注着这个类是一个实体类。

@Table

​ 标注在类外侧,标注实体的主表,其中有 catalog,indexes, name, schema,分别注明为:指定database的目录,索引,名称,和表的结构描述

​ 注意:schema定义了表、每个表的字段,还有表和字段之间的关系。

@Id

表示是实体类的主键

@GeneratedValue

​ 提供主键的值得生成策略规范。GeneratedValue注释可以与Id注释一起应用于实体或映射超类的主键属性或字段,派生主键不支持该注解。提供两个可选值:1. generator :要在SequenceGenerator或TableGenerator注解中指定的主键生成器的名称。2. strategy 生成策略,有四个可选值AUTO,INDENTITY,SEQUENCE 和 TABLE ,默认为AUTO

@Column

指定持久属性或字段的映射列。如果未指定Column注释,则应用默认值。

updatable:是否包含在框架自动生成的update语句中。

其他可以通过例子一看便知:

1
2
@Column(name="description", nullable=false, length=512)
public String getDescription() { return description; }

@Transient

@Transient表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性.
如果一个属性并非数据库表的字段映射,就务必将其标示为@Transient,否则,ORM框架默认其注解为@Basic.

@ManyToOne

表示一个多对一的映射,该注解标注的属性通常是数据库表的外键

1
2
3
4
5
6
7
//订单Order和用户User是一个ManyToOne的关系
//在Order类中定义
@ManyToOne()
@JoinColumn(name=”USER”)
public User getUser() {
return user;
}

@JoinColumn

​ 指定用于连接实体关联或元素集合的列。如果JoinColumn注解本身是默认的,则假定使用单个连接列并应用默认值。其默认的名称为实体User的名称+下划线+实体User的主键名称。也就是USER_ID.

@OneToMany

@OneToMany描述一个一对多的关联,该属性应该为集体类型,在数据库中并没有实际字段.例如:实体User和Order是OneToMany的关系,则实体User被删除时,其关联的实体Order也应该被全部删除

1
2
3
4
@OneTyMany(cascade=ALL)
public List getOrders() {
return orders;
}

@OneToOne

描述一对一的关系

如User表和person表,一个是基本信息一个是详细信息

1
2
3
4
@OneToOne(fetch=FetchType.LAZY) // Fetch是抓取策略,如果不写,自动的策略是LAZY
public Person getPerson() {
return person;
}

@ManyToMany

@ManyToMany 描述一个多对多的关联.多对多关联上是两个一对多关联,但是在ManyToMany描述中,中间表是由ORM框架自动处理

@Embedded

@Embedded将几个字段组合成一个类,并作为整个Entity的一个属性.

1
2
3
4
5
6
7
8
9
@Embeddable
public class Address {city,street,zip}
@Entity
public class User {
@Embedded
public Address getAddress() {

}
}
注解 说明
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值,同理@Max(value)
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值,同理@DecimalMax(value)
@Size(max=, min=) 被注释的元素的大小必须在指定的范围内
@Past 被注释的元素必须是一个过去的日期,同理@Future
@Pattern(regex=,flag=) 符合正则
Hibernate Validator 附加的 constraint
@NotBlank(message =) 验证字符串非null,且长度必须大于0,message表示提示信息
@Email 邮箱验证
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内

spring boot 配置问题

spring2.0后静态资源访问不到的问题

需要手动写配置类进行静态资源配置

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 添加静态资源文件,外部可以直接访问地址
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
registry.addResourceHandler("/templates/**").addResourceLocations("classpath:/templates/");
}
}

配置SpringBoot Jpa默认mysql生成表引擎为InnoDB

在配置文件appliction.properties中加入

1
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect

Ubuntu系的IBUS输入法上方panel的输入法图标消失问题

Posted on 2018-12-28 | In Linux
Words count in article: 86 | Reading time ≈ 1

Ubuntu系的IBUS输入法上方panel的输入法图标消失问题

最近迷上了Pop!_Os,基于Ubuntu的发行版,非常简洁,开箱即用。和ubuntu相同,ibus的输入法图标有时消失,在这里总结以下,方便后面再出现问题的时候查阅。

打开终端输入:

1
ibus-daemon -drx

解决!!

关于SpringMvc实现restFul时DELETE和PUT出现错误

Posted on 2018-11-08 | In Java
Words count in article: 217 | Reading time ≈ 1

关于RESTful

我理解的restful是一种风格,每一个url表示一个资源,一个资源不同的操作享用同一个接口,但是传输的状态是不同的,比如http中的POST,DELETE,PUT,GET。

再springMVC中实现RESTful

  1. web.xml中配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<filter>
<filter-name>HttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
  1. springmvc control层使用相应状态码来标识。
1
@RequestMapping(value = "/navbarCategory/{id}", method = RequestMethod.DELETE)

String、StringBuffer、StringBuilder的区别和关系

Posted on 2018-11-08 | In Java
Words count in article: 675 | Reading time ≈ 2

String、StringBuffer、StringBuilder的区别和关系

String声明后是一个常量,而Stringbuffer和StringBuilder是数据结构是变量,所以比String在连接字符时效率高。

并且StringBuffer和StringBuilder的区别是StringBuffer是线程安全的(synchronized) ,而StringBuilder是非线程安全的,所以速度大致排行为:StringBuilder>StringBuffer>String

以上只是基于印象或者说是死记硬背的东西,没有看源码是否真的是这样的。下面就来看看String,StringBuilder,StringBuffer的源码来真正的记住。

String

String中的concat方法实现了字符串的拼接。

实例变量

String类中有两个实例变量,分别为value和hash

1
2
3
4
5
/** The value is used for character storage. */
private final char value[];

/** Cache the hash code for the string */
private int hash; // Default to 0

构造方法

String的构造方法很多,但是都比较简单,下面来说比较常用的几种。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 当新建一个String对象的时候,会默认付一个空的字符序列,请注意,由于字符串是不可变的,因此不必使用此构造函数
public String() {
this.value = "".value;
}
// 这个构造方法,会默认新建一个值为original的字符串。同上,如果字符串不可变的,就没必要这样声明。
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
// 使用copyof方法用来复制一个新的字符数组,后续修改字符数组,不会影响到新建的字符串
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}

concat方法

为什么说String的效率低,就体现在concat拼接字符串的时候每次都要返回一个新的String对象。

1
2
3
4
5
6
7
8
9
10
11
 public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
// 返回一个新建的String对象
return new String(buf, true);
}

StringBuilder

StringBuilder继承了AbstractStringBuilder,其中的实例变量和String中的基本相同,

构造方法

1
2
3
4
5
6
7
public StringBuilder(int capacity) {
super(capacity);
}
// 调用父类的构造方法
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}

append方法

可以看到,并没有新建对象,只是在改变对象中的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public StringBuilder append(String str) {
super.append(str);
return this;
}
// append调用的还是父类的append方法
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
// 把value扩容后再把str加进去
count += len;
return this;
}

StringBuffer

和StringBuilder一样,也是继承了AbstractStringBuilder类,但是看一个append这个方法的源码就能够知道有什么区别了

append方法

添加了synchronized关键字,使得变为线程安全的,但是代价就是效率不如StringBuilder高。

1
2
3
4
5
6
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}

消息中间件,RPC框架,数据库中间件的概念认知

Posted on 2018-10-29 | In Java
Words count in article: 663 | Reading time ≈ 2

消息中间件,RPC框架,数据库中间件的概念认知

消息中间件

什么是消息中间件

对中间件的理解大概就是具体业务和底层逻辑解耦的组件。相当于前端业务和底层逻辑的中间桥梁。

消息中间件是在分布式系统中发送和接受消息的基础服务设施。

能够做什么?

  • 业务解耦:交易系统不需要知道短信通知服务的存在,只需要发布消息
  • 削峰填谷:比如上游系统的吞吐能力高于下游系统,在流量洪峰时可能会冲垮下游系统,消息中间件可以在峰值时堆积消息,而在峰值过去后下游系统慢慢消费消息解决流量洪峰的问题
  • 事件驱动:系统与系统之间可以通过消息传递的形式驱动业务,以流式的模型处理

RPC框架

什么是RPC框架

远程过程调用协议RPC(Remote Procedure Call Protocol) 两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,
由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。

能够做什么?

  • 建立客户端和服务器之间的TCP链接,不同的RPC协议,有不同的连接寻址方式。
  • 在发起远程调用时,由于网络协议是二进制的,所以在传输前要进行对传输实体进行转换二进制的过程,也是就序列化过程(Serialize),通过寻址和二进制的数据传输到服务器端
  • 在接受到数据后,需要进行反序列化过程解析二进制数据。

其实就是解决在分布式下,横向拓展的前提下,不同服务器相互调用方法的问题。

数据库中间件

什么是数据库中间件

同样在分布式的架构中,数据库也放到不同的服务器上,需要通过中间的路由进行调用。这个路由实际上就是数据库中间件。

能够做什么?

  • 实现读写分离,但是每个服务器上都需要有一份完整的数据库
  • 实现分库分表,通过消息中间件进行查询分派到某一个特定的服务器上,通过这个服务器查询出内容返回到消息中间件,再通过中间件返回到客户端。

Junit的使用和源码分析

Posted on 2018-10-28
Words count in article: 2.3k | Reading time ≈ 11

Junit的使用和源码分析

Junit是一个编写可重复测试的Java测试框架,代码编写非常有技巧性,值得反复阅读。

跟着官方文档学习Junit

官方文档往往是学习最好的资料。

简单测试例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class FirstTest {

public int add(int a, int b) {
return a + b;
}

@Test
public void testAdd(){
FirstTest firstTest = new FirstTest();
int result = firstTest.add(1,2);
// assertEquals(4,result);
assertEquals(3,result);
}

<!--
- 如果判断不相等的时候,后台报错信息
- java.lang.AssertionError:
- Expected :4
- Actual :3
- <Click to see difference>
-->

}

这只是简单的例子,实际上的单元测试要比这个复杂的多,在实际应用上单元测试十分有必要,编写后台代码时能够尽快检验代码的正确性。

Assertions 断言

Junit提供了所有基本数据类型,Object类和数组的断言,参数是 预期值后面是实际值,可选项,第一个参数可以是断言失败时输出的内容,与其他断言稍有不同的是,AssertThat的参数是 失败输出的内容,实际值和一个Matcher
Object。

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
public class AssertTests {
@Test
public void testAssertArrayEquals() {
byte[] expected = "trial".getBytes();
byte[] actual = "trial".getBytes();
assertArrayEquals("failure - byte arrays not same", expected, actual);
}

@Test
public void testAssertEquals() {
assertEquals("failure - strings are not equal", "text", "text");
}

@Test
public void testAssertFalse() {
assertFalse("failure - should be false", false);
}

@Test
public void testAssertNotNull() {
assertNotNull("should not be null", new Object());
}

@Test
public void testAssertNotSame() {
assertNotSame("should not be same Object", new Object(), new Object());
}

@Test
public void testAssertNull() {
assertNull("should be null", null);
}

@Test
public void testAssertSame() {
Integer aNumber = Integer.valueOf(768);
assertSame("should be same", aNumber, aNumber);
}

// JUnit Matchers assertThat
@Test
public void testAssertThatBothContainsString() {
assertThat("albumen", both(containsString("a")).and(containsString("b")));
}

@Test
public void testAssertThatHasItems() {
assertThat(Arrays.asList("one", "two", "three"), hasItems("one", "three"));
}

@Test
public void testAssertThatEveryItemContainsString() {
assertThat(Arrays.asList(new String[] { "fun", "ban", "net" }), everyItem(containsString("n")));
}

// Core Hamcrest Matchers with assertThat
@Test
public void testAssertThatHamcrestCoreMatchers() {
assertThat("good", allOf(equalTo("good"), startsWith("good")));
assertThat("good", not(allOf(equalTo("bad"), equalTo("good"))));
assertThat("good", anyOf(equalTo("bad"), equalTo("good")));
assertThat(7, not(CombinableMatcher.<Integer> either(equalTo(3)).or(equalTo(4))));
assertThat(new Object(), not(sameInstance(new Object())));
}

@Test
public void testAssertTrue() {
assertTrue("failure - should be true", true);
}
}

异常测试

下面分为两种方式来完成对异常的测试

期待的异常

如何检测程序是否如期的抛出异常,junit可以使用注解的参数来实现。

1
2
3
4
@Test(expected = IndexOutOfBoundsException.class)
public void testException(){
new ArrayList<Integer>().get(0);
}

深入的异常

上述方法对于简单的情况很有用,但它有其局限性。例如,您无法在异常中测试消息的值,也无法在抛出异常后测试域对象的状态

  • try/catch 语句
1
2
3
4
5
6
7
8
9
@Test
public void testTryCatch(){
try {
new ArrayList<Integer>().get(0);
fail("失败信息");
}catch (IndexOutOfBoundsException indexOutOfBoundsExecption){
assertThat(indexOutOfBoundsExecption.getMessage(), is("Index: 0, Size: 0"));
}
}
  • rule 规则
1
2
3
4
5
6
7
8
9
10
@Rule
public ExpectedException thrown = ExpectedException.none();

@Test
public void testExpectException(){
List<Object> list = new ArrayList<Object>();
thrown.expect(IndexOutOfBoundsException.class);
thrown.expectMessage("Index: 0, Size: 0");
list.get(0); // execution will never get past this line
}

Matchers and assertThat [ 匹配器和assertThat ]

新加入了assertThat断言机制 assertThat([value], [matcher statement]);

1
2
3
4
assertThat(x, is(3));
assertThat(x, is(not(4)));
assertThat(responseString, either(containsString("color")).or(containsString("colour")));
assertThat(myList, hasItem("3"));

assertThat 更具有可读性和可输入性,并且有组合性,就像 is(not(4)) 任何Machers都可以组合起来使用

以前的assertEquals等也是可以用的,assertThat 在使用Matchers的时候需要使用 import static org.hamcrest.CoreMatchers.*;来引用。里面的方法非常多。。

junit源码跟读

使用junit流程

使用继承自TestCase类

下面通过运行junit的自带的test,源程序为:

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
/**
* Some simple tests.
*/
public class SimpleTest extends TestCase {
protected int fValue1;
protected int fValue2;

@Override
protected void setUp() {
fValue1 = 2;
fValue2 = 3;
}

public static Test suite() {

/*
* the type safe way
*
TestSuite suite= new TestSuite();
suite.addTest(
new SimpleTest("add") {
protected void runTest() { testAdd(); }
}
);

suite.addTest(
new SimpleTest("testDivideByZero") {
protected void runTest() { testDivideByZero(); }
}
);
return suite;
*/

/*
* the dynamic way
*/
return new TestSuite(SimpleTest.class);
}

public void testAdd() {
double result = fValue1 + fValue2;
// forced failure result == 5
assertTrue(result == 6);
}

public int unused;

public void testDivideByZero() {
int zero = 0;
int result = 8 / zero;
unused = result; // avoid warning for not using result
}

public void testEquals() {
assertEquals(12, 12);
assertEquals(12L, 12L);
assertEquals(new Long(12), new Long(12));

assertEquals("Size", 12, 13);
assertEquals("Capacity", 12.0, 11.99, 0.0);
}

public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
}

来看main方法,使用 junit.textui.TestRunner.run(suite()); 使用TestRunner运行test。首先先来看suite方法,有两种方法

  1. 静态的,需要手动在testSuite中添加test。
  2. 动态的,静态需要实现TestCase的runTest方法。而动态的只需要返回 TestSuite(SimpleTest.class);,下面来看这个TestSuite类

testSuite实际上就是运行test的集合,使用vector来存储test,其中这里使用到的TestSuite构造方法是:

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
public TestSuite(final Class<?> theClass) {
addTestsFromTestCase(theClass);
}
private void addTestsFromTestCase(final Class<?> theClass) {
fName = theClass.getName();
try {
getTestConstructor(theClass); // Avoid generating multiple error messages
} catch (NoSuchMethodException e) {
addTest(warning("Class " + theClass.getName() + " has no public constructor TestCase(String name) or TestCase()"));
return;

}
// 这个类是否是public的 如果不是 发出warning ,并且fail(message)
if (!Modifier.isPublic(theClass.getModifiers())) {
addTest(warning("Class " + theClass.getName() + " is not public"));
return;
}

Class<?> superClass = theClass;
List<String> names = new ArrayList<String>();
// 这句话说明的是如果这个类是superClass的超类,或者接口就返回true,否则返回false
while (Test.class.isAssignableFrom(superClass)) {
// 如果是true 有顺序的返回声明的方法
for (Method each : MethodSorter.getDeclaredMethods(superClass)) {
addTestMethod(each, names, theClass);
}
// 获得superclass类,递归的查找test方法
superClass = superClass.getSuperclass();
}
if (fTests.size() == 0) {
addTest(warning("No tests found in " + theClass.getName()));
}
}

最后提到的 fTests.size() == 0 这里会发生warning 然后fail。这个fTests 已经再前面声明
了,声明方法是:private Vector<Test> fTests = new Vector<Test>(10);

在对每个声明的方法循环的时候,使用到 addTestMethod 方法,来对每个方法进行处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void addTestMethod(Method m, List<String> names, Class<?> theClass) {
// 获得方法名如果list数组中已经包含这个方法名,就直接退出
String name = m.getName();
if (names.contains(name)) {
return;
}
if (!isPublicTestMethod(m)) {
if (isTestMethod(m)) {
addTest(warning("Test method isn't public: " + m.getName() + "(" + theClass.getCanonicalName() + ")"));
}
return;
}
// 把这个方法名加入到names List中
names.add(name);
addTest(createTest(theClass, name));
}
//再来看外层的addTest方法 把拥有的test方法放到fTests中。
public void addTest(Test test) {
fTests.add(test);
}

使用createTest来针对test方法创建一个Test类

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
static public Test createTest(Class<?> theClass, String name) {
Constructor<?> constructor;
try {
constructor = getTestConstructor(theClass);
} catch (NoSuchMethodException e) {
return warning("Class " + theClass.getName() + " has no public constructor TestCase(String name) or TestCase()");
}
Object test;
// 通过反射方式获得方法的test实例
try {
if (constructor.getParameterTypes().length == 0) {
test = constructor.newInstance(new Object[0]);
if (test instanceof TestCase) {
((TestCase) test).setName(name);//如果继承TestCase
}
} else {
test = constructor.newInstance(new Object[]{name});
}
} catch (InstantiationException e) {
return (warning("Cannot instantiate test case: " + name + " (" + Throwables.getStacktrace(e) + ")"));
} catch (InvocationTargetException e) {
return (warning("Exception in constructor: " + name + " (" + Throwables.getStacktrace(e.getTargetException()) + ")"));
} catch (IllegalAccessException e) {
return (warning("Cannot access test case: " + name + " (" + Throwables.getStacktrace(e) + ")"));
}
return (Test) test;
}

经过以上的步骤获得了这个类中及其父类中的所有方法的Test。

使用testRunner.run运行test

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
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
// 使用TestRunner的run的静态方法 返回一个TestResult用来返回结果。
static public TestResult run(Test test) {
TestRunner runner = new TestRunner();
return runner.doRun(test);
}
// 调用runner的doRun方法
public TestResult doRun(Test test) {
return doRun(test, false);
}
public TestResult doRun(Test suite, boolean wait) {
// 用来返回结果的TestResult
TestResult result = createTestResult();
// 注册一个TestListener
result.addListener(fPrinter);
long startTime = System.currentTimeMillis();
// test.run方法
suite.run(result);
long endTime = System.currentTimeMillis();
long runTime = endTime - startTime;
fPrinter.print(result, runTime);
pause(wait);
return result;
}

运行Test的核心方法 返回TestResult返回结果。

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
/**
* Creates the TestResult to be used for the test run.
*/
protected TestResult createTestResult() {
return new TestResult();
}

public synchronized void addListener(TestListener listener) {
// protected List<TestListener> fListeners;
fListeners.add(listener);
}
// suite.run() fTests中的每一个test
public void run(TestResult result) {
for (Test each : fTests) {
if (result.shouldStop()) {
break;
}
runTest(each, result);
}
}
/**
* Runs the test case and collects the results in TestResult.
* 调用testResult的run方法
*/
public void run(TestResult result) {
result.run(this);
}
// 运行test
protected void run(final TestCase test) {
startTest(test);
Protectable p = new Protectable() {
public void protect() throws Throwable {
test.runBare();//执行runBare方法执行test用例
}
};
runProtected(test, p);

endTest(test);
}
public void startTest(Test test) {
final int count = test.countTestCases();
synchronized (this) {
fRunTests += count;
}
for (TestListener each : cloneListeners()) {
each.startTest(test);
}
}

在resultPriter中使用继承TestListener中的startTest方法。

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
public void startTest(Test test) {
getWriter().print(".");//运行一个则加一个点
if (fColumn++ >= 40) {
getWriter().println();
fColumn = 0;
}
}

// 运行一个空的测试序列
public void runBare() throws Throwable {
Throwable exception = null;
// 设置装置。比如打开网络连接等,在执行测试之前调用方法。
setUp();
try {
// 看下面的runTest方法
runTest();//执行test
} catch (Throwable running) {
exception = running;
} finally {
try {
tearDown();
} catch (Throwable tearingDown) {
if (exception == null) exception = tearingDown;
}
}
if (exception != null) throw exception;
}

protected void runTest() throws Throwable {
assertNotNull("TestCase.fName cannot be null", fName); // Some VMs crash when calling getMethod(null,null);
Method runMethod = null;
try {
runMethod = getClass().getMethod(fName, (Class[]) null);
} catch (NoSuchMethodException e) {
fail("Method \"" + fName + "\" not found");
}
// .......... runMethod.invoke(this); 执行方法
try {
runMethod.invoke(this);
} catch (InvocationTargetException e) {
// 如果方法执行错误,会触发这个异常 会连续被上层捕捉到
e.fillInStackTrace();
throw e.getTargetException();
} catch (IllegalAccessException e) {
e.fillInStackTrace();
throw e;
}
}

如果测试失败,则最终将被runProtected中的try/catch捕捉到后输出错误信息。

1
2
3
4
5
6
7
8
9
10
11
12
public void runProtected(final Test test, Protectable p) {
try {
p.protect();
} catch (AssertionFailedError e) {
// 验证失败,添加一条失败信息
addFailure(test, e);
} catch (ThreadDeath e) { // don't catch ThreadDeath by accident
throw e;
} catch (Throwable e) {
addError(test, e);
}
}

经过以上的步骤执行完一个test。

<i class="fa fa-angle-left"></i>1…789…12<i class="fa fa-angle-right"></i>
NanYin

NanYin

Was mich nicht umbringt, macht mich starker.

111 posts
16 categories
21 tags
RSS
GitHub E-Mail
近期文章
  • ThreadLocal
  • Java的四种引用类型
  • Markdown语法入门
  • IDEA设置代码注释模板和JavaDoc文档生成
  • 基于Java的WebService实践
0%
© 2023 NanYin
|
本站访客数:
|
博客全站共140.1k字
|