整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

Java测试题1

Java测试题1

ava测试题_1


一、选择题

1、 编译Java源程序文件产生的字节码文件的扩展名为( B )。

A、java B、class C、html D、exe


2、 main方法是Java Application程序执行的入口点,关于main方法的方法头以下哪项是合法的( B )?

A、public static void main()

B、public static void main(String args[])

C、public static int main(String [] arg)

D、public void main(String arg[])


3、 为AB类的一个无形式参数无返回值的方法method书写方法头,使得使用类名AB作为前缀就可以调用它,该方法头的形式为( A )。

A、static void method( ) B、public void method( )

C、final void method( ) D、abstract void method( )


4、 下列哪个是合法的Java标识符?( B\C )

A、Tree&Glasses B、FirstJavaApplet

C、_$theLastOne D、273.5


5、 Java Application源程序的主类是指包含有( A )方法的类。

A、main方法 B、toString方法

C、init方法 D、actionPerfromed方法


6、 若a的值为3时,下列程序段被执行后,c的值是多少?( C )

int c=1;

if ( a>0 )

if ( a>3 )

c=2;

else

c=3;

else

c=4;

A、1 B、2 C、3 D、4


7、 对于构造方法,下列叙述正确的是( ACD )。

A、构造方法是类的一种特殊方法,它的方法名必须与类名相同。

B、构造方法的返回类型只能是void型。

C、构造方法的主要作用是完成对类的对象的初始化工作。

D、一般在创建新对象时,系统会自动调用构造方法。


8、 下面的是关于类及其修饰符的一些描述,正确的是:( A C )。

A、abstract类只能用来派生子类,不能用来创建abstract类的对象。

B、final类不但可以用来派生子类,也可以用来创建final类的对象。

C、abstract不能与final同时修饰一个类。

D、abstract方法必须在abstract类中声明,但abstract类定义中可以没有abstract方法。


9、 若需要定义一个类,应使用哪种修饰符?(ACD )

A、static B、package C、private D、public


10、若在某一个类定义中定义有如下的方法:abstract void performDial( );该方法属于( D )。

A、本地方法 B、最终方法 C、解态方法 D、抽象方法


11、设有下面两个类的定义:

class Person { class Student extends Person {

long id; // 身份证号 int score; // 入学总分

String name; // 姓名 int getScore(){

}

2006 年开始转战 Linux 系统后,经过几年的实践,我的软件设置在过去十年内出人意料的固定。再过十年回顾一下,看看发生了什么,也许会非常有趣。

? 来源:linux.cn ? 作者:Dennis Felsing ? 译者:chenmu-kk ?

(本文字数:12075,阅读时长大约:15 分钟)

从 2006 年开始转战 Linux 系统后,经过几年的实践,我的软件设置在过去十年内出人意料的固定。再过十年回顾一下,看看发生了什么,也许会非常有趣。在写这篇推文时,我迅速回顾了正在运行的内容:

htop overview


动机

我的软件介绍排序不分先后:

  • 程序应该运行在本地系统中以便我可以控制它,这其中并不包括云解决方案。
  • 程序应在终端中运行,以便于在任何地方连贯地使用它们,包括性能稍差的电脑或手机。
  • 通过使用终端软件,可以实现自动聚焦键盘。只有在一些有意义的地方,我会更喜欢使用鼠标,因为在打字期间一直伸手去拿鼠标感觉像在浪费时间。有时候过了一个小时我才注意到甚至还没有插鼠标。
  • 最好使用快速高效的软件,我不喜欢听到风扇的声音和感到房间在变热。我还可以继续长久地运行旧硬件,已经使用了 10 年的 Thinkpad x200s 还能很好地支持我所使用的软件。
  • 组合。我不想手动执行每个步骤,而是在需要时自动执行更多操作,这时自然是支持 shell。

操作系统

十二年前移除 Windows 系统后,我在 Linux 系统上经历了一个艰难的开始,当时我手上只有 Gentoo Linux 系统的安装光盘和一本打印的说明书,要用它们来实现一个可运行的 Linux 系统。虽然花费了几天的时间去编译和修整,但最终还是觉得自己受益颇多。

自此我再也没有转回 Windows 系统,但在持续的编译压力导致风扇失灵后,我将我的电脑系统切换到 Arch Linux 。之后我将其他的电脑和私人服务器也切换到了 Arch Linux。作为一个滚动发布发行版,你可以随时升级软件包,但 Arch Linux News 已经详细报道了其中最主要的漏洞。

不过,令人烦恼的是一旦你更新了旧的内核模组,Arch Linux 就会移除旧版的相关信息。我经常注意到一旦我试着插入一个 USB 闪存盘,内核就无法加载相关组件。相反,每次内核升级后都应该进行重启。有一些 方法 可以解决这个问题,但我还没有实际地使用它们。

其他程序也会出现类似的情况,通常 Firefox 、 cron 或者 Samba 在升级后都需要重启,但恼人的是,它们没有警告你存在这种情况。我在工作中使用的 SUSE 很好地提醒了这种情况。

对于 DDNet 产品服务器,相较于 Arch Linux ,我更倾向于 Debian 系统,这样在每次升级时出现故障的几率更低。我的防火墙和路由器使用了 OpenBSD ,它拥有干净系统、文档和强大的 pf 防火墙 ,而我现在不需要一个单独的路由器。

窗口管理器

从我开始使用 Gentoo 后,我很快注意到 KDE 的编译时间非常长,这让我没办法继续使用它。我四处寻找更简单的解决方案,最初使用了 Openbox 和 Fluxbox 。某次,为了能更多进行纯键盘操作,我开始尝试转入平铺窗口管理器,并在研究其初始版本的时候学习了 dwm 和 awesome 。

最终,由于 xmonad 的灵活性、可扩展性以及使用纯 Haskell (一种出色的函数编程语言)编写和配置,最终选择了它。一个例子是,我在家中运行一个 40" 4K 的屏幕,但经常会将它分为四个虚拟屏幕,每个虚拟屏幕显示一个工作区,每个工作区自动排列在我的窗口上。当然, xmonad 有一个对应的 模块 。

dzen 和 conky 对我来说是一个非常简单的状态栏。我的整体 conky 配置看起来是这样的:

out_to_console yes
update_interval 1
total_run_times 0

TEXT
${downspeed eth0} ${upspeed eth0} | $cpu% ${loadavg 1} ${loadavg 2} ${loadavg 3} $mem/$memmax | ${time %F %T}

输入命令直接通过管道输入 dzen2:

conky | dzen2 -fn '-xos4-terminus-medium-r-normal-*-12-*-*-*-*-*-*-*' -bg '#000000' -fg '#ffffff' -p -e '' -x 1000 -w 920 -xs 1 -ta r

对我而言,一项重要功能是在完成工作后使终端发出蜂鸣声。只需要简单地在 zsh 中的 PR_TITLEBAR 变量中添加一个 \a 字符就可以做到,只要工作完成就可以发出蜂鸣声。当然,我使用了命令:

echo "blacklist pcspkr" > /etc/modprobe.d/nobeep.conf

将 pcspkr 内核模块列入黑名单来禁用实际的蜂鸣声。相反 urxvt 的 URxvt.urgentOnBell: true 设置会将声音变为尖锐。之后 xmonad 有一个 urgency 钩子来捕捉这类信号,并且我可以使用组合键自动聚焦到当前的发出紧急信号的窗口。在 dzen 中我可以看到一个漂亮且明亮的 #ff0000 紧急窗口。

在我笔记本上所得到的最终成品是:

Laptop screenshot


我听说前几年 i3 变得非常流行,但它要求更多的手动窗口对齐而不是自动对齐。

我意识到也有像 tmux 那样的终端多路复用器,但我仍想要一些图形化应用程序,因此最终我没有有效地使用它们。

终端连续性

为了使终端保持活跃状态,我使用了 dtach ,它只是模拟屏幕分离功能。为了使计算机上的每个终端都可连接和断开,我编写了一个小的封装脚本。 这意味着,即使必须重新启动 X 服务器,我也可以使所有终端都运行良好,包括本地和远程终端。

Shell & 编程

对于 shell,我使用 zsh 而不是 bash ,因为它有众多的功能。

作为终端模拟,我发现 urxvt 足够轻巧,支持 Unicode 编码和 256 色,具有出色的性能。另一个重要的功能是可以分别运行 urxvt 客户端和守护进程。因此,即使大量终端也几乎不占用任何内存(回滚缓冲区除外)。

对我而言,只有一种字体看起来绝对干净和完美: Terminus 。 由于它是位图字体,因此所有内容都是完美像素,渲染速度极快且 CPU 使用率低。为了能使用 CTRL-WIN-[1-7] 在每个终端按需切换字体,我的 ~/.Xdefaults 包含:

URxvt.font: -xos4-terminus-medium-r-normal-*-14-*-*-*-*-*-*-*
dzen2.font: -xos4-terminus-medium-r-normal-*-14-*-*-*-*-*-*-*

URxvt.keysym.C-M-1: command:\033]50;-xos4-terminus-medium-r-normal-*-12-*-*-*-*-*-*-*\007
URxvt.keysym.C-M-2: command:\033]50;-xos4-terminus-medium-r-normal-*-14-*-*-*-*-*-*-*\007
URxvt.keysym.C-M-3: command:\033]50;-xos4-terminus-medium-r-normal-*-18-*-*-*-*-*-*-*\007
URxvt.keysym.C-M-4: command:\033]50;-xos4-terminus-medium-r-normal-*-22-*-*-*-*-*-*-*\007
URxvt.keysym.C-M-5: command:\033]50;-xos4-terminus-medium-r-normal-*-24-*-*-*-*-*-*-*\007
URxvt.keysym.C-M-6: command:\033]50;-xos4-terminus-medium-r-normal-*-28-*-*-*-*-*-*-*\007
URxvt.keysym.C-M-7: command:\033]50;-xos4-terminus-medium-r-normal-*-32-*-*-*-*-*-*-*\007

URxvt.keysym.C-M-n: command:\033]10;#ffffff\007\033]11;#000000\007\033]12;#ffffff\007\033]706;#00ffff\007\033]707;#ffff00\007
URxvt.keysym.C-M-b: command:\033]10;#000000\007\033]11;#ffffff\007\033]12;#000000\007\033]706;#0000ff\007\033]707;#ff0000\007

对于编程和书写,我使用 Vim 语法高亮显示和 ctags 进行索引,以及一些带有 grep 、sed 和其他用于搜索和操作的常用终端窗口。这可能不像 IDE 那样舒适,但可以实现更多的自动化。

Vim 的一个问题是你已经习惯了它的键映射,因此希望在任何地方都使用它们。

在 shell 功能不够强大时, Python 和 Nim 作为脚本语言也不错。

系统监控

htop (查看当前站点的后台运行,是托管服务器的实时视图)非常适合快速了解软件的当前运行状态。 lm_sensors 可以监控硬件温度、风扇和电压。 powertop 是一款由 Intel 发布的优秀省电小工具。 ncdu 可以交互式分析磁盘使用情况。

nmap 、 iptraf-ng、 tcpdump 和 Wireshark 都是分析网络问题的基本工具。

当然还有很多更优秀的工具。

邮件 & 同步

在我的家庭服务器上,我为自己所有的邮箱账号运行了 fetchmail 守护进程。fetchmail 只是检索收到的邮件并调用 procmail :

#!/bin/sh
for i in /home/deen/.fetchmail/*; do
 FETCHMAILHOME=$i /usr/bin/fetchmail -m 'procmail -d %T' -d 60
done

配置非常简单,然后等待服务器通知我们有新的邮件:

poll imap.1und1.de protocol imap timeout 120 user "dennis@felsin9.de" password "XXX" folders INBOX keep ssl idle

我的 .procmailrc 配置包含一些备份全部邮件的规则,并将邮件整理在对应的目录下面。例如,基于邮件列表名或者邮件标题:

MAILDIR=/home/deen/shared/Maildir
LOGFILE=$HOME/.procmaillog
LOGABSTRACT=no
VERBOSE=off
FORMAIL=/usr/bin/formail
NL="
"

:0wc
* ! ? test -d /media/mailarchive/`date +%Y`
| mkdir -p /media/mailarchive/`date +%Y`

# Make backups of all mail received in format YYYY/YYYY-MM
:0c
/media/mailarchive/`date +%Y`/`date +%Y-%m`

:0
* ^From: .*(.*@.*.kit.edu|.*@.*.uka.de|.*@.*.uni-karlsruhe.de)
$MAILDIR/.uni/

:0
* ^list-Id:.*lists.kit.edu
$MAILDIR/.uni-ml/

[...]

我使用 msmtp 来发送邮件,它也很好配置:

account default
host smtp.1und1.de
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
auth on
from dennis@felsin9.de
user dennis@felsin9.de
password XXX

[...]

但是到目前为止,邮件还在服务器上。 我的文档全部存储在一个目录中,我使用 Unison 在所有计算机之间进行同步。Unison 可以视为双向交互式 rsync ,我的邮件是这个文件目录下的一部分,因此它们最终存储在我的电脑上。

这也意味着,尽管邮件会立即到达我的邮箱,但我只是按需拿取,而不是邮件一到达时就立即收到通知。

从此我使用 mutt 阅读邮件,使用侧边栏显示我的邮件目录。 /etc/mailcap 文件对于显示非纯文本邮件( HTML, Word 或者 PDF)不可或缺:

text/html;w3m -I %{charset} -T text/html; copiousoutput
application/msword; antiword %s; copiousoutput
application/pdf; pdftotext -layout /dev/stdin -; copiousoutput

新闻 & 通讯

Newsboat 是一个非常棒的终端 RSS/Atom 阅读器。我在一个有约 150 个提要的 tach 会话服务器上运行它。也可以在本地选择提要,例如:

ignore-article "https://forum.ddnet.tw/feed.php" "title=~ \"Map Testing ?\" or title=~ \"Old maps ?\" or title=~ \"Map Bugs ?\" or title=~ \"Archive ?\" or title=~ \"Waiting for mapper ?\" or title=~ \"Other mods ?\" or title=~ \"Fixes ?\""

我以同样的方式使用 Irssi 进行 IRC 通讯。

日历

remind 是一个可以从命令行获取的日历。通过编辑 rem 文件可以设置新的提醒:

# One time events
REM 2019-01-20 +90 Flight to China %b

# Recurring Holidays
REM 1 May +90 Holiday "Tag der Arbeit" %b
REM [trigger(easterdate(year(today()))-2)] +90 Holiday "Karfreitag" %b

# Time Change
REM Nov Sunday 1 --7 +90 Time Change (03:00 -> 02:00) %b
REM Apr Sunday 1 --7 +90 Time Change (02:00 -> 03:00) %b

# Birthdays
FSET birthday(x) "'s " + ord(year(trigdate())-x) + " birthday is %b"
REM 16 Apr +90 MSG Andreas[birthday(1994)]

# Sun
SET $LatDeg 49
SET $LatMin 19
SET $LatSec 49
SET $LongDeg -8
SET $LongMin -40
SET $LongSec -24

MSG Sun from [sunrise(trigdate())] to [sunset(trigdate())]
[...]

遗憾的是,目前 remind 中还没有中国农历的提醒功能,因此中国的节日不易计算。

我给提醒设置了两个名字:

rem -m -b1 -q -g

按时间顺序查看待办事项清单

rem -m -b1 -q -cuc12 -w$(($(tput cols)+1)) | sed -e "s/\f//g" | less

显示适应终端宽度的日历:

remcal


字典

rdictcc 是鲜为人知的字典工具,它可以使用 dict.cc 很棒的词典并将他们转存在本地数据库中:

$ rdictcc rasch====================[ A=> B ]====================rasch:
 - apace
 - brisk [speedy]
 - cursory
 - in a timely manner
 - quick
 - quickly
 - rapid
 - rapidly
 - sharpish [Br.] [coll.]
 - speedily
 - speedy
 - swift
 - swiftly
rasch [gehen]:
 - smartly [quickly]
Rasch {n} [Zittergras-Segge]:
 - Alpine grass [Carex brizoides]
 - quaking grass sedge [Carex brizoides]
Rasch {m} [regional] [Putzrasch]:
 - scouring pad====================[ B=> A ]====================Rasch model:
 - Rasch-Modell {n}

记录和阅读

我有一个简单记录任务的备忘录,在 Vim 会话中基本上一直处于打开状态。我也使用备忘录作为工作中“已完成”工作的记录,这样就可以检查自己每天完成了哪些任务。

对于写文件、信件和演示文稿,我会使用 LaTeX 进行高级排版。德式的简单信件可以这样设置,例如:

\documentclass[paper=a4, fromalign=right]{scrlttr2}
\usepackage{german}
\usepackage{eurosym}
\usepackage[utf8]{inputenc}
\setlength{\parskip}{6pt}
\setlength{\parindent}{0pt}

\setkomavar{fromname}{Dennis Felsing}
\setkomavar{fromaddress}{Meine Str. 1\\69181 Leimen}
\setkomavar{subject}{Titel}

\setkomavar*{enclseparator}{Anlagen}

\makeatletter
\@setplength{refvpos}{89mm}
\makeatother

\begin{document}
\begin{letter} {Herr Soundso\\Deine Str. 2\\69121 Heidelberg}
\opening{Sehr geehrter Herr Soundso,}

Sie haben bei mir seit dem Bla Bla Bla.

Ich fordere Sie hiermit zu Bla Bla Bla auf.

\closing{Mit freundlichen Grü?en}

\end{letter}
\end{document}

在 我的私人网站 上可以找到更多的示例文档和演示文稿。

Zathura 读取 PDF 文件速度很快,支持 Vim 类控件,还支持两种不同的 PDF 后端: Poppler 和 MuPDF。另一方面,在偶尔遇到 Zathura 无法打开的文件时, Evince 则显得更全能一些。

图片编辑

简便的选择是, GIMP 和 Inkscape 分别用于照片编辑和交互式向量图形。

有时 Imagemagick 已经足够好了,它可以从命令行直接使用,从而自动编辑图片。同样 Graphviz 和 TikZ 可以用来绘制曲线图和其他图表。

Web 浏览器

对于 Web 浏览器,我一直在使用 Firefox 。相较于 Chrome,它的可扩展性更好,资源使用率更低。

不幸的是,在 Firefox 完全改用 Chrome 风格的扩展之后, Pentadactyl 扩展的开发就停止了,所以我的浏览器中再也没有令人满意的 Vim 类控件了。

媒体播放器

通过设置 vo=gpu 以及 hwdec=vaapi,具有硬件解码功能的 mpv 在播放期间 CPU 的占用率保持在 5%。相较于默认的 PulseAudio,mpv 中的 audio-channels=2 似乎可以使我的立体扬声器/耳机获得更清晰的降级混频。一个很棒的小功能是用 Shift-Q 退出,而不是只用 Q 来保存回放位置。当你与母语是其他语言的人一起看视频时,你可以使用 --secondary-sid=同时显示两种字幕,主字幕位于底部,次字幕位于屏幕顶部。

我的无线鼠标可以简单地通过一个小的配置文件( ~/.config/mpv/input.conf )实现远程控制 mpv :

MOUSE_BTN5 run "mixer" "pcm" "-2"
MOUSE_BTN6 run "mixer" "pcm" "+2"
MOUSE_BTN1 cycle sub-visibility
MOUSE_BTN7 add chapter -1
MOUSE_BTN8 add chapter 1

youtube-dl 非常适合观看在线托管的视频,使用 -f bestvideo+bestaudio/best --all-subs --embed-subs 命令可获得最高质量的视频。

作为音乐播放器, MOC 不再活跃开发,但它仍是一个简易的播放器,可以播放各种可能的格式,包括最不常用的 Chiptune 格式。在 AUR 中有一个 补丁 增加了 PulseAudio 支持。即使在 CPU 时钟频率降到 800 MHz, MOC 也只使用了单核 CPU 的 1-2% 。

moc


我的音乐收藏夹保存在我的家庭服务器上,因此我可以从任何地方访问它。它使用 SSHFS 挂载并自动安装在 /etc/fstab/ 目录下:

root@server:/media/media /mnt/media fuse.sshfs noauto,x-systemd.automount,idmap=user,IdentityFile=/root/.ssh/id_rsa,allow_other,reconnect 0 0

跨平台构建

除了 Linux 本身,它对于构建任何主流操作系统的软件包都很优秀! 一开始,我使用 QEMU 与旧版 Debian、 Windows 以及 Mac OS X VM 一起构建这些平台。

现在我在旧版 Debian 发行版上转而使用 chroot (以获得最大的 Linux 兼容性),在 Windows 上使用 MinGW 进行交叉编译,在 Mac OS X 上则使用 OSXCross 。

用于 构建 DDNet 的脚本以及 更新库构建的说明 的脚本都基于这个。

备份

通常,我们都会忘记备份。即使这是最后一节,它也不应该成为事后诸葛。

十年前我写了 rrb (反向 rsync 备份)重新封装了 rsync ,因此我只需要将备份服务器的 root SSH 权限授予正在备份的计算机。令人惊讶地是,尽管我一直在使用 rrb ,但它在过去十年里没有任何改变。

备份文件直接存储在文件系统中。使用硬链接实现增量备份(--link-dest)。一个简单的 配置 定义了备份保存时间,默认为:

KEEP_RULES=( \
   7  7 \ # One backup a day for the last 7 days
  31  8 \ # 8 more backups for the last month
 365 11 \ # 11 more backups for the last year
1825  4 \ # 4 more backups for the last 5 years
)

因为我的一些计算机没有静态 IP / DNS 但仍想使用 rrb 备份,那我会使用反向安全隧道(作为 systemd 服务):

[Unit]
Description=Reverse SSH Tunnel
After=network.target

[Service]
ExecStart=/usr/bin/ssh -N -R 27276:localhost:22 -o "ExitOnForwardFailure yes" server
KillMode=process
Restart=always

[Install]
WantedBy=multi-user.target

现在,隧道运行备份时,服务器可以通过 ssh -p 27276 localhost 命令或者使用 .ssh/config 到达服务器端。

Host cr-remote
 HostName localhost
 Port 27276

在谈及 SSH 技巧时,有时由于某些中断的路由会很难访问到服务器。在那种情况下你可以借道其他服务器的 SSH 连接,以获得更好的路由。在这种情况下,你可能通过美国连接访问到我的中国服务器,而来自德国的不可靠连接可能需要几个周:

Host chn.ddnet.tw
 ProxyCommand ssh -q usa.ddnet.tw nc -q0 chn.ddnet.tw 22
 Port 22

结语

感谢阅读我工具的收藏。这其中我也许遗漏了许多日常中自然成习惯的步骤。让我们来看看我的软件设置在下一年里能多稳定吧。如果你有任何问题,随时联系我 dennis@felsin9.de 。

在 Hacker News 下评论吧。


via: hookrace.net

作者: Dennis Felsing 选题: lujun9972 译者: chenmu-kk 校对: wxy

本文由 LCTT 原创编译, Linux中国 荣誉推出

点击“了解更多”可访问文内链接

、前言

为什么会产生这个需求呢?

我们公司作为乙方,老是被客户追着要一份API文档,当我们把一个 Swagger 文档地址丢给客户的时候。客户还是很不满意,嫌不够正式!!死活坚持要一份 word 文档 。然后领导给了个接口模板,就把这个活交给我了……我去,近10个微服务,几百个接口,这不得要了我的命啊(最后整理出来将近200页的 word 文档)。最后,还是领导有办法:要不我们把Swagger的 json文件转成word文档吧!

一直坚持一句话。作为使用者,人要迁就机器;作为开发者,要机器迁就人。

二、思路

领导提供了一个接口模板,类似下面这样,其实就是一个word的table页。想到 html 可以转 word ,那么问题就变成了 :

  • 解析JSON 文件
  • 把JSON文件的内容填充进html 的Table中
  • 由html直接转成word

几百个接口,一气呵成!如下,还有一个简单的示例,就是请求参数 和 返回值 。怎么处理呢?在程序中写了 HTTP 的请求,封装了需要的参数去执行了一个请求,得到相应的返回值!

三、实现

1、封装对象

按照面向对象的思想,一个接口Table就是一个对象,可变的请求参数和返回参数也封装成一个对象……

Table

public class Table {
 /**
 * 大标题
 */
 private String title;
 /**
 * 小标题
 */
 private String tag;
 /**
 * url
 */
 private String url;
 /**
 * 响应参数格式
 */
 private String responseForm;
 /**
 * 请求方式
 */
 private String requestType;
 /**
 * 请求体
 */
 private List<Request> requestList;
 /**
 * 返回体
 */
 private List<Response> responseList;
 /**
 * 请求参数
 */
 private String requestParam;
 /**
 * 返回值
 */
 private String responseParam;
 public String getTitle() {
 return title;
 }
 public void setTitle(String title) {
 this.title=title;
 }
 public String getTag() {
 return tag;
 }
 public void setTag(String tag) {
 this.tag=tag;
 }
 public String getUrl() {
 return url;
 }
 public void setUrl(String url) {
 this.url=url;
 }
 public String getResponseForm() {
 return responseForm;
 }
 public void setResponseForm(String responseForm) {
 this.responseForm=responseForm;
 }
 public String getRequestType() {
 return requestType;
 }
 public void setRequestType(String requestType) {
 this.requestType=requestType;
 }
 public List<Request> getRequestList() {
 return requestList;
 }
 public void setRequestList(List<Request> requestList) {
 this.requestList=requestList;
 }
 public List<Response> getResponseList() {
 return responseList;
 }
 public void setResponseList(List<Response> responseList) {
 this.responseList=responseList;
 }
 public String getRequestParam() {
 return requestParam;
 }
 public void setRequestParam(String requestParam) {
 this.requestParam=requestParam;
 }
 public String getResponseParam() {
 return responseParam;
 }
 public void setResponseParam(String responseParam) {
 this.responseParam=responseParam;
 }
}


Request

public class Request {
 /**
 * 请求参数
 */
 private String description;
 /**
 * 参数名
 */
 private String name;
 /**
 * 数据类型
 */
 private String type;
 /**
 * 参数类型
 */
 private String paramType;
 /**
 * 是否必填
 */
 private Boolean require;
 /**
 * 说明
 */
 private String remark;
 public String getDescription() {
 return description;
 }
 public void setDescription(String description) {
 this.description=description;
 }
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name=name;
 }
 public String getType() {
 return type;
 }
 public void setType(String type) {
 this.type=type;
 }
 public Boolean getRequire() {
 return require;
 }
 public void setRequire(Boolean require) {
 this.require=require;
 }
 public String getRemark() {
 return remark;
 }
 public void setRemark(String remark) {
 this.remark=remark;
 }
 public String getParamType() {
 return paramType;
 }
 public void setParamType(String paramType) {
 this.paramType=paramType;
 }
}


Response

public class Response {
 /**
 * 返回参数
 */
 private String description;
 /**
 * 参数名
 */
 private String name;
 /**
 * 说明
 */
 private String remark;
 public Response(String description, String name, String remark) {
 this.description=description;
 this.name=name;
 this.remark=remark;
 }
 public String getDescription() {
 return description;
 }
 public void setDescription(String description) {
 this.description=description;
 }
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name=name;
 }
 public String getRemark() {
 return remark;
 }
 public void setRemark(String remark) {
 this.remark=remark;
 }
}

2、解析 json

先来看看Swagger json文件的格式吧!需要注意的是这个 json 文件默认的 host 是没有加 http:// 前缀的,需要我们手动加上,因为程序的HTTP请求不像浏览器一样会自动补上 http:// 的前缀 ……

解析JSON真是一件枯燥的工作,大家可以按照自己想要生成模板的样子修改这边的代码……需要提的是,这里有一点让我纠结了好久。怎么伪造接口的请求参数发送HTTP请求以避免不会抛异常呢?最后还是参考了Swagger的方式,即:如果是 String 类型的参数,就把这个参数置为"string";如果是 Integer 类型的参数,就把这个参数置为 0 ;如果是Double 类型的参数,就置为 0.0 ;如果是其他没办法预见的类型,就全部置为 null;

解析 JSON 用的是Spring推荐的 jackson ,这部分感觉没什么好说的,直接上代码吧!

@Service
public class TableServiceImpl implements TableService {
 @Override
 public List<Table> tableList() {
 List<Table> list=new LinkedList();
 try {
 ClassLoader classLoader=TableService.class.getClassLoader();
 URL resource=classLoader.getResource("data.json");
 Map map=new ObjectMapper().readValue(resource, Map.class);
 //得到host,用于模拟http请求
 String host=String.valueOf(map.get("host"));
 //解析paths
 LinkedHashMap<String, LinkedHashMap> paths=(LinkedHashMap) map.get("paths");
 if (paths !=null) {
 Iterator<Map.Entry<String, LinkedHashMap>> iterator=paths.entrySet().iterator();
 while (iterator.hasNext()) {
 Table table=new Table();
 List<Request> requestList=new LinkedList<Request>();
 String requestType="";
 Map.Entry<String, LinkedHashMap> next=iterator.next();
 String url=next.getKey();//得到url
 LinkedHashMap<String, LinkedHashMap> value=next.getValue();
 //得到请求方式,输出结果类似为 get/post/delete/put 这样
 Set<String> requestTypes=value.keySet();
 for (String str : requestTypes) {
 requestType +=str + "/";
 }
 Iterator<Map.Entry<String, LinkedHashMap>> it2=value.entrySet().iterator();
 //解析请求
 Map.Entry<String, LinkedHashMap> get=it2.next();//得到get
 LinkedHashMap getValue=get.getValue();
 String title=(String) ((List) getValue.get("tags")).get(0);//得到大标题
 String tag=String.valueOf(getValue.get("summary"));
 //请求体
 ArrayList parameters=(ArrayList) getValue.get("parameters");
 if (parameters !=null && parameters.size() > 0) {
 for (int i=0; i < parameters.size(); i++) {
 Request request=new Request();
 LinkedHashMap<String, Object> param=(LinkedHashMap) parameters.get(i);
 request.setDescription(String.valueOf(param.get("description")));
 request.setName(String.valueOf(param.get("name")));
 request.setType(String.valueOf(param.get("type")));
 request.setParamType(String.valueOf(param.get("in")));
 request.setRequire((Boolean) param.get("required"));
 requestList.add(request);
 }
 }
 //返回体,比较固定
 List<Response> responseList=listResponse();
 //模拟一次HTTP请求,封装请求体和返回体,如果是Restful的文档可以再补充
 if (requestType.contains("post")) {
 Map<String, String> stringStringMap=toPostBody(requestList);
 table.setRequestParam(stringStringMap.toString());
 String post=NetUtil.post(host + url, stringStringMap);
 table.setResponseParam(post);
 } else if (requestType.contains("get")) {
 String s=toGetHeader(requestList);
 table.setResponseParam(s);
 String getStr=NetUtil.get(host + url + s);
 table.setResponseParam(getStr);
 }
 //封装Table
 table.setTitle(title);
 table.setUrl(url);
 table.setTag(tag);
 table.setResponseForm("application/json");
 table.setRequestType(StringUtils.removeEnd(requestType, "/"));
 table.setRequestList(requestList);
 table.setResponseList(responseList);
 list.add(table);
 }
 }
 return list;
 } catch (IOException e) {
 e.printStackTrace();
 }
 return null;
 }
 //封装返回信息,可能需求不一样,可以自定义
 private List<Response> listResponse() {
 List<Response> responseList=new LinkedList<Response>();
 responseList.add(new Response("受影响的行数", "counts", null));
 responseList.add(new Response("结果说明信息", "msg", null));
 responseList.add(new Response("是否成功", "success", null));
 responseList.add(new Response("返回对象", "data", null));
 responseList.add(new Response("错误代码", "errCode", null));
 return responseList;
 }
 //封装post请求体
 private Map<String, String> toPostBody(List<Request> list) {
 Map<String, String> map=new HashMap<>(16);
 if (list !=null && list.size() > 0) {
 for (Request request : list) {
 String name=request.getName();
 String type=request.getType();
 switch (type) {
 case "string":
 map.put(name, "string");
 break;
 case "integer":
 map.put(name, "0");
 break;
 case "double":
 map.put(name, "0.0");
 break;
 default:
 map.put(name, "null");
 break;
 }
 }
 }
 return map;
 }
 //封装get请求头
 private String toGetHeader(List<Request> list) {
 StringBuffer stringBuffer=new StringBuffer();
 if (list !=null && list.size() > 0) {
 for (Request request : list) {
 String name=request.getName();
 String type=request.getType();
 switch (type) {
 case "string":
 stringBuffer.append(name+"&=string");
 break;
 case "integer":
 stringBuffer.append(name+"&=0");
 break;
 case "double":
 stringBuffer.append(name+"&=0.0");
 break;
 default:
 stringBuffer.append(name+"&=null");
 break;
 }
 }
 }
 String s=stringBuffer.toString();
 if ("".equalsIgnoreCase(s)){
 return "";
 }
 return "?" + StringUtils.removeStart(s, "&");
 }
}

3、html 模板

我们需要一个和 Word Table 模板一样的HTML 页面,然后利用JSP的 foreach 遍历后台得到的 List<Table>集合,一气呵成,生成所有接口……

<%-- text/html:正常的html显示 application/msword:html页面直接转word--%><%@ page contentType="application/msword" pageEncoding="UTF-8" language="java" %><%--<%@page contentType="text/html" pageEncoding="UTF-8" language="java" %>--%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
 <head>
 <title>tool</title>
 <style type="text/css">
 .bg {
 background-color: rgb(84, 127, 177);
 }
 tr {
 height: 20px;
 font-size: 12px;
 }
 .specialHeight {
 height: 40px;
 }
 </style>
 </head>
 <body>
 <div style="width:800px; margin: 0 auto">
 <c:forEach items="${table}" var="t">
 <h4>${t.title}</h4> <%--这个是类的说明--%>
 <h5>${t.tag}</h5> <%--这个是每个请求的说明,方便生成文档后进行整理--%>
 <table border="1" cellspacing="0" cellpadding="0" width="100%">
 <tr class="bg">
 <td colspan="6"><c:out value="${t.tag}"/></td>
 </tr>
 <tr>
 <td>URL</td>
 <td colspan="5">${t.url}</td>
 </tr>
 <tr>
 <td>请求方式</td>
 <td colspan="5">${t.requestType}</td>
 </tr>
 <tr>
 <td>返回值类型</td>
 <td colspan="5">${t.responseForm}</td>
 </tr>
 <tr class="bg" align="center">
 <td>请求参数</td>
 <td>参数名</td>
 <td>数据类型</td>
 <td>参数类型</td>
 <td>是否必填</td>
 <td>说明</td>
 </tr>
 <c:forEach items="${t.requestList}" var="req">
 <tr align="center">
 <td>${req.description}</td>
 <td>${req.name}</td>
 <td>${req.type}</td>
 <td>${req.paramType}</td>
 <td>
 <c:choose>
 <c:when test="${req.require==true}">Y</c:when>
 <c:otherwise>N</c:otherwise>
 </c:choose>
 </td>
 <td>${remark}</td>
 </tr>
 </c:forEach>
 <tr class="bg" align="center">
 <td>返回参数</td>
 <td>参数名</td>
 <td colspan="4">说明</td>
 </tr>
 <c:forEach items="${t.responseList}" var="res">
 <tr align="center">
 <td>${res.description}</td>
 <td>${res.name}</td>
 <td colspan="4">${res.remark}</td>
 </tr>
 </c:forEach>
 <tr class="bg">
 <td colspan="6">示例</td>
 </tr>
 <tr class="specialHeight">
 <td class="bg">请求参数</td>
 <td colspan="5">${t.requestParam}</td>
 </tr>
 <tr class="specialHeight">
 <td class="bg">返回值</td>
 <td colspan="5">${t.responseParam}</td>
 </tr>
 </table>
 <br>
 </c:forEach>
 </div>
 </body>
</html>

4、效果

把代码运行起来后,访问JSP页面,不会像平常一样看到 HTML 页面,而是直接下载生成一个 文件,按照SpringMVC请求方法命名(这个项目中是getWord文件)。把这个文件的后缀名改成 .doc 就能看到效果了!差不多是如下效果:

当然,剩下的工作,就要我们手动去整理维护了。比如:把属于同一个类的请求分类整理到一起;把HTTP请求错误的返回值删除(还无法适配所有的HTTP请求情况);整理维护效果如下:

四、使用

如果直接采用我的API文档模板的话,只需要将 resources 目录下的 data.json 文件的内容替换成自己的Swagger Json 文件内容就好。但是,考虑到我们模板中的返回参数是我们公司一个自定义的对象,所以可能这里还需要大家根据自己的要求稍作修改,主要 修改TableServiceImpl 类下的 listResponse() 方法。

需要说明的是,这个项目还没有很好的支持所有的HTTP请求,比如 restful 服务将请求参数放在请求路径中的;比如参数是放在header中的;以及一系列可能没有考虑到的bug……

另外,我觉得 TableServiceImpl 还有很大可以改善的地方,代码略显冗余。之后慢慢维护吧!当然,很欢迎大家一起来开发…哈哈

五、结语

一直觉得,IT最迷人的地方就是开源和分享,大家互不相识,即使没有利益可图,却能为同一个项目,相同的目标 贡献自己的时间和精力。想想就不可思议。写这个博文的目地更多是分享自己的创意和想法,说实话,代码可能写的有点烂。还请大家不要嫌弃,不吝指教!

六、更新说明

之前看《Spring In Action》的时候,发现了 RestTemplate 这个东西, 作为取代 HttpClients 的请求方式。当时就在想,要是放在这个项目中不是恰到好处?

更新说明如下:

1、引入了Spring的RestTemplate取代 HttpClients 以支持更多的Restful请求。

2、命名规范以及增加异常处理,对于无法处理的HTTP请求返回空字符串。

3、修改之前导入data.josn的方式,变成restTemplate.getForObject("SwaggerJson的url地址",Map.class);的动态获取方式。

现在的使用方式也更加简单:

1、修改resources目录下resources.properties文件的 swaggerUrl 为Swagger Json资源的url地址。

2、服务启动后:访问 http://host(主机):port(端口)/getWord,etc:http://localhost:8080/getWord

3、将生成的getWord文件,增加后缀名 getWord.doc 。

GitHub 地址

https://github.com/JMCuixy/swagger2word


练手项目源码、新技术介绍、原理性知识、应用框架知识点讲解、常见笔试面试题分析,搜索【Java知音】