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
我的軟件介紹排序不分先后:
十二年前移除 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,我使用 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 瀏覽器,我一直在使用 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 ,那么問題就變成了 :
幾百個接口,一氣呵成!如下,還有一個簡單的示例,就是請求參數 和 返回值 。怎么處理呢?在程序中寫了 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 。
https://github.com/JMCuixy/swagger2word
練手項目源碼、新技術介紹、原理性知識、應用框架知識點講解、常見筆試面試題分析,搜索【Java知音】
*請認真填寫需求信息,我們會在24小時內與您取得聯系。