本地倉庫可以簡單理解成一個(gè)目錄,這個(gè)目錄里面的所有文件都被 Git 管理,Git 記錄了每個(gè)文件的修改、刪除,因此我們可以對歷史版本進(jìn)行查看或者還原。那么,如何創(chuàng)建一個(gè)版本庫呢?(說明:以下內(nèi)容作為 “本地倉庫” 概念的輔助理解,具體命令操作后續(xù)會(huì)更多接觸,先不用糾結(jié)) a. 選擇一個(gè)合適的地方,創(chuàng)建一個(gè)空目錄。 b. 在目錄下執(zhí)行:git init 命令即可初始化一個(gè)本地倉庫。 c. 之后當(dāng)前目錄會(huì)出現(xiàn)一個(gè)隱藏的.git 的目錄,這個(gè)目錄是 Git 來跟蹤管理版本庫的,不要手動(dòng)修改這個(gè)目錄里面的文件,否則會(huì)把 Git 倉庫給破壞了?! . 本地倉庫初始化完成后,我們就可以在本地倉庫進(jìn)行文件的管理。
使用以上的函數(shù)編寫一個(gè)復(fù)制文件的程序 copy.py:import osdef copy(sourcePath, targetPath): sourceFd = os.open(sourcePath, os.O_RDONLY) targetFd = os.open(targetPath, os.O_WRONLY | os.O_CREAT) while (True): binary = os.read(sourceFd, 512) if len(binary) == 0: return os.write(targetFd, binary)copy('test.txt', 'text.bak') 在第 1 行,引入 os 模塊在第 3 行,編寫函數(shù) copysourcePath 是源文件路徑,targetPath 是目標(biāo)文件路徑在第 4 行,打開源文件os.O_RDONLY 表示以只讀方式打開在第 5 行,打開目標(biāo)文件os.O_WRONLY 表示以只寫方式打開os.O_CREAT 表示創(chuàng)建一個(gè)新文件在第 9 行,如果讀取的 binary 的長度為 0,則表示讀取到文件末尾在第 13 行,將文件 test.txt 復(fù)制到文件 test.bak運(yùn)行程序,輸出如下:C:\> python copy.pyC:\> dir2001/10/01 10:40 <DIR> .2001/10/01 10:40 <DIR> ..2001/10/01 10:40 333 copy.py2001/10/01 09:48 19 test.txt2001/10/01 10:40 19 text.bak在第 1 行,運(yùn)行程序 copy.py在第 2 行,使用 dir 命令顯示當(dāng)前目錄結(jié)果表明,在當(dāng)前目錄下新生成一個(gè)文件 test.bak
基礎(chǔ)概念:我們在上一小節(jié)中,已經(jīng)對消息適配器有了一個(gè)基礎(chǔ)的認(rèn)識(shí),去并且在介紹消息適配器的同時(shí),也引入了消息轉(zhuǎn)換器,就是代碼中的 MessageConverter 類。在經(jīng)過上節(jié)消息適配器代碼的實(shí)操之后,相信大家對消息轉(zhuǎn)換器的感念已經(jīng)不是那么陌生了,想必在各位同學(xué)的心里,已經(jīng)對消息轉(zhuǎn)換器有一個(gè)初步的認(rèn)知了。同樣地,在介紹什么是消息轉(zhuǎn)換器時(shí),還是和消息適配器那樣,將消息和轉(zhuǎn)換器兩個(gè)概念拆開來介紹,但是,消息這一名詞已經(jīng)在消息適配器小節(jié)中做了介紹,這里不再贅述,我們直接從轉(zhuǎn)換器開始。我們先拋開計(jì)算機(jī)這一專業(yè)領(lǐng)域來說,在我們的日常生活中,轉(zhuǎn)換器的例子隨處可見,這里我們還拿在上節(jié)中提到的筆記本電腦的例子繼續(xù)介紹。我們都知道,隨著筆記本電腦行業(yè)的飛速發(fā)展,筆記本的功能也是越來越強(qiáng)大,那么,筆記本電腦提供給我的接入端口也在發(fā)生著強(qiáng)大的變化,傳統(tǒng)的 USB 端口在最新的筆記本電腦中甚至已經(jīng)看不見了,越來越多的則是 Type-C 或 Light 形式的接入端口。試想一下,如果我們需要使用移動(dòng)硬盤來連接電腦,進(jìn)行工作數(shù)據(jù)的處理,移動(dòng)硬盤只有一個(gè)普通的 USB 端口,但是我們的筆記本電腦只有一個(gè) Type-C 的接入端口,那么此時(shí)我們又應(yīng)該如何將移動(dòng)硬盤來連接電腦呢。這種情況,我們就需要一條轉(zhuǎn)接線來發(fā)揮作用了。就上述例子而言,轉(zhuǎn)接線提供了一種在移動(dòng)硬盤和筆記本電腦之間,由于接入端口的不同,移動(dòng)硬盤和筆記本電腦可以進(jìn)行接入的橋梁。轉(zhuǎn)接線的一端連接電腦的 Type-C 端口,另一端則通過 USB 端口的形式連接移動(dòng)硬盤,以達(dá)到不同的端口之間移動(dòng)硬盤可以繼續(xù)連接筆記本電腦的目的。而這一轉(zhuǎn)接線在移動(dòng)硬盤和筆記本電腦之間就發(fā)揮了轉(zhuǎn)換器的作用。說白了,轉(zhuǎn)換器其實(shí)就是為了在兩種不同的物品之間,通過一定的媒介,使這兩種不同的物品可以繼續(xù)進(jìn)行互聯(lián)互通的一種技術(shù)手段,而這種技術(shù)手段可以是技術(shù)實(shí)物(比如轉(zhuǎn)換線),也可以是一種可以在空氣中進(jìn)行傳播的介質(zhì)(Wifi投屏)。消息轉(zhuǎn)換器亦是如此,在 Spring-AMQP 中,消息適配器指的就是,將我們接收的消息進(jìn)行不同格式的轉(zhuǎn)換,從而可以解決數(shù)據(jù)在不同業(yè)務(wù)場景下所需要的格式的不同的問題,即 RabbitMQ 中的消息轉(zhuǎn)換器就是將 RabbitMQ 中的消息進(jìn)行不同格式的轉(zhuǎn)換。在介紹完消息轉(zhuǎn)換器的基礎(chǔ)概念之后,下面讓我們來看一下如何對消息轉(zhuǎn)換器進(jìn)行簡單的配置吧。
在我們開始的我們的可視化的之旅之前,需要簡單的介紹一些數(shù)據(jù)分析工具,我們的數(shù)據(jù)可視化的任務(wù)也是建立在數(shù)據(jù)分析的基礎(chǔ)之上。Python 的主要數(shù)據(jù)分析工具如下所示:Numpy:這個(gè)是數(shù)據(jù)計(jì)算的工具,主要用來進(jìn)行矩陣的運(yùn)算,矢量運(yùn)算等等。Scipy:科學(xué)計(jì)算函數(shù)庫,主要用在學(xué)術(shù)領(lǐng)域,主要包含線性代數(shù)模塊,信號(hào)與圖像處理模塊,統(tǒng)計(jì)學(xué)模塊等等。Sympy:數(shù)學(xué)符號(hào)計(jì)算庫Pandas:包含了 numpy 的各種功能,并提供了更加強(qiáng)大的函數(shù),以及更加豐富的數(shù)據(jù)模型。Pandas的主要數(shù)據(jù)結(jié)構(gòu)為 Series 和 DataFrame。Series 可以當(dāng)作是一般的數(shù)組,區(qū)別就是Series數(shù)組有索引的性質(zhì),這個(gè)和普通的數(shù)組十分不同。我們可以通過series.index來獲取index的值。DataFrame 可以把它想像成數(shù)據(jù)的表格的概念,它是把一個(gè)或者多個(gè)Series按照邏輯合并后的二維數(shù)據(jù)結(jié)構(gòu)。接下來讓我們開始我們數(shù)據(jù)可視化之旅吧。首選,我們來畫一張最基本的直方圖。from matplotlib import pyplot //引入matplotlib庫進(jìn)行繪制圖形import numpy as np //引入numpy,來生成隨機(jī)數(shù)x = np.arage(12)y = np.random.rand(12)labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec]pyplot.bar(x,y, color='red', tick_label=labels) //繪制條形圖pyplot.title('first chart')plot.show()上面這幅是條形圖,我們可以看到 x 軸是月份,總共分為 12 個(gè)月,縱軸則是每個(gè)月的數(shù)值在 0-1 之間的值。x = np.random.randn(800)pyplot.hist(x,150) //繪制直方圖pyplot.title('second chart')pyplot.show()上面這幅是直方圖,默認(rèn)的為藍(lán)色,數(shù)據(jù)在我們?nèi)‰S機(jī)數(shù)之后,基本上是呈現(xiàn)一個(gè)對稱分布的情況。from mpl_toolkits.mplot3d import Axes3D //引入三維圖形包pic = pyplot.figure()ax = Axes3D(pic)x = np.arange(-1, 3, 0.3) //x軸取值范圍y = np.arange(-1, 3, 0.3 //y軸取值范圍a, b = np.meshgrid(x,y) //繪制二維圖形c = a**2 + b **2ax.plot_surface(a,b,c, cmap= pyplot.get_cmap('rainbow')) //繪制三維圖ax.set_zlim(-1, 10)pyplot.title('last chart')pyplot.show()除了簡單的二維圖形,同樣,matplotlib 也可以很輕松的繪制三維圖形,上面的代碼就是我們繪制三位圖形的簡單版本,效果如下所示:
ol 標(biāo)簽代表整個(gè)列表,里面只能 li 標(biāo)簽,li 標(biāo)簽代表列表的每一項(xiàng);ol li 標(biāo)簽的程序方式默認(rèn)為豎向排列,如想橫向排列,需要借助 CSS ;ol li 標(biāo)簽列表項(xiàng)前默認(rèn)以阿拉伯?dāng)?shù)字 1、2、3…排列;每一個(gè)列表項(xiàng)有順序;li 標(biāo)簽里可以再嵌套 ol 標(biāo)簽,如 li 標(biāo)簽嵌套了 ol 標(biāo)簽,內(nèi)層列表的默認(rèn)排列方式不會(huì)改變,具體如下圖所示:<ol> <li> <ol> <li>紅蘋果</li> <li>青蘋果</li> </ol> </li> <li>香蕉</li> <li>橘子</li> </ol>這樣代表外層的列表的第一個(gè)選項(xiàng)又是一個(gè)列表。如果想改變 LI 列表選項(xiàng)的排列方式,可以設(shè)置 ol 的 type 屬性, type 屬性的可選值為 1、A、a、I、i,默認(rèn)為1。如果設(shè)置 ol type 屬性為 A,則列表排列方式為 A、B、C…的英文大寫字母排列,如下圖: <ol type="A"> <li>蘋果</li> <li>香蕉</li> <li>橘子</li> <li>西瓜</li> <li>葡萄</li> </ol>如果如果設(shè)置 ol type 屬性為 a,則列表排列方式為 a、b、c…的英文大寫字母排列,如下圖:<ol type="a"> <li>蘋果</li> <li>香蕉</li> <li>橘子</li> <li>西瓜</li> <li>葡萄</li> </ol>如果如果設(shè)置 ol type 屬性為 i,則列表排列方式為 i、ii、iii、iv…的小寫羅馬數(shù)字排列,如下圖:<ol type="i"> <li>蘋果</li> <li>香蕉</li> <li>橘子</li> <li>西瓜</li> <li>葡萄</li></ol>如果如果設(shè)置 ol type 屬性為 I,則列表排列方式為 I、II、III、IV…的大寫羅馬數(shù)字排列,如下圖:<ol type="I"> <li>蘋果</li> <li>香蕉</li> <li>橘子</li> <li>西瓜</li> <li>葡萄</li></ol>
在早期的 JavaScript 中是沒類的概念的,如果想要實(shí)現(xiàn)類的功能需要通過構(gòu)造函數(shù)來創(chuàng)建,使用 prototype 來實(shí)現(xiàn)類的繼承。對于一些高級語言如 C++、Java、python 等,都是有類的概念的,而且在這些語言中類是非常重要的。而 JavaScript 由于歷史原因在設(shè)計(jì)最初就沒有想要引入類的概念,隨著 JavaScript 越來越多地應(yīng)用到大型項(xiàng)目中,JavaScript 的短板就顯現(xiàn)了。雖然,可以使用原型等方式來解決,但是還是存在各種各樣的問題。要學(xué)習(xí) ES6 中的 class 首先要了解 ES5 中的構(gòu)造函數(shù),主要了解在構(gòu)造函數(shù)中是如何實(shí)現(xiàn)類和繼承的,了解了這些知識(shí)帶你有助于后面我們更深入的理解 ES6 中的 class。
命令參數(shù)名稱功能與作用描述Ctrl + C顯示光標(biāo)在文件中的位置信息Ctrl + G顯示 nano 編輯器的幫助窗口Ctrl + J調(diào)整當(dāng)前文本段落Ctrl + K剪切文本行,并將其保存在剪切緩沖區(qū)Ctrl + O將當(dāng)前文本編輯緩沖區(qū)的內(nèi)容寫入文件Ctrl + R將文件讀入當(dāng)前文本編輯緩沖區(qū)Ctrl + T啟動(dòng)可用的拼寫檢查器Ctrl + U將剪切緩沖區(qū)中的內(nèi)容放入當(dāng)前行Ctrl + V翻動(dòng)到文本編輯緩沖區(qū)中的下一頁內(nèi)容Ctrl + W在文本編輯緩沖區(qū)中搜索單詞或短語Ctrl + X關(guān)閉當(dāng)前文本編輯緩沖區(qū),退出 nano 編輯器Ctrl + Y翻動(dòng)到文本編輯緩沖區(qū)中的上一頁內(nèi)容Tips:可以在 nano 編輯器界面使用 Ctrl + G 顯示更多的命令。
下面列舉了一些 du 命令參數(shù)的作用:du 命令參數(shù)名稱功能與作用描述-a表示 --all,列出所有的文件和目錄容量大小而不僅僅列出目錄容量大小,默認(rèn)情況只是統(tǒng)計(jì)目錄的容量大小。-B表示 --block-size=SIZE,指定單位大小。-b表示 --bytes,以字節(jié)為單位列出文件和目錄的容量大小。-c表示 --total,除了列出文件和目錄的容量大小外,列出總的容量大小。-h表示 --human-readable,以人們易讀的方式 (KB、MB、GB) 顯示容量大小。-k以 KB 為單位。-m以 MB 為單位。-s表示 --separate-dirs,僅列出總量,而不列出每個(gè)目錄和文件的大小-S表示 --summarize,和-s參數(shù)類似,但是統(tǒng)計(jì)時(shí)不包含子目錄的容量大小。
寫入文本常用方法,見下表。屬性描述 write(r, c, label, style) 將指定單元格寫入工作表 write_merge(r1, r2, c1, c2, label, style) 將指定合并單元格寫入工作表具體方法使用,如下所示:ws = wb.add_sheet("sheet1")ws.write(3,0,"張三")如何確定要填入的單元格位置呢?工作表中的行和列索引均為從 0 開始,即行索引為 1,表示第 2 行,列索引為 2,表示第三列。起始位置從工作表中左上角位置。通過上述代碼,可以得知在創(chuàng)建的 sheet 1 下,第 4 行第 1 列的位置寫入了 "張三"。ws = wb.add_sheet("sheet1")ws.write_merge(0, 1, 0, 5, "班級學(xué)員名單", titlestyle)通過上述代碼,可以得知在創(chuàng)建的 sheet1 下,第 1 行,第 1-5 列做了單元格合并,里面填寫標(biāo)題內(nèi)容,且應(yīng)用 titlestyle 樣式(關(guān)于樣式如何定義在下面小節(jié)中詳細(xì)說明)。
Spring Boot 也支持自定義日志配置,可以直接采用指定日志系統(tǒng)的配置文件,如 logback 、 log4j 。以 logback 為例,可以直接在 application.properties 文件中指定 logback 配置文件。實(shí)例:# 指定logback配置文件,位于resources目錄下logging.config=classpath:logback-spring.xmlTips:使用 logback 日志系統(tǒng)后,日志級別與日志文件等信息都可以使用 logback-spring.xml 文件設(shè)置,不再需要從 properties 文件中設(shè)置了。在生產(chǎn)環(huán)境,我們希望指定日志保存的位置,另外日志不能無限制一直保存,一般情況下保存最近 30 天左右的日志即可。這些都可以在 logback-spring.xml 文件中指定,此處給出一個(gè)完整實(shí)例供大家參考。實(shí)例:<?xml version="1.0" encoding="UTF-8"?><!-- logback 配置 --><configuration> <!-- 輸出到控制臺(tái) --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化輸出:%d表示日期;%thread表示線程名;%-5level:左對齊并固定顯示5個(gè)字符;%msg:日志消息;%n:換行符; --> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> </appender> <!-- 輸出到文件 --> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在打印的日志文件 --> <File>C:/logs/spring-boot-log.log</File> <encoder> <!--格式化輸出:%d表示日期;%thread表示線程名;%-5level:左對齊并固定顯示5個(gè)字符;%msg:日志消息;%n:換行符; --> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n </pattern> </encoder> <!-- 日志文件的滾動(dòng)策略 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 日志歸檔 --> <fileNamePattern>C:/logs/spring-boot-log-%d{yyyy-MM-dd}.log </fileNamePattern> <!-- 保留30天日志 --> <maxHistory>30</maxHistory> </rollingPolicy> </appender> <!-- 指定日志輸出的級別 --> <root level="INFO"> <appender-ref ref="STDOUT" /> <appender-ref ref="FILE" /> </root></configuration>logback 日志系統(tǒng)的功能比較全面,網(wǎng)上可以查詢到的資料也非常多,大家可以自行查閱以做進(jìn)一步的了解。
結(jié)構(gòu)體內(nèi)嵌結(jié)構(gòu)體是用的最多的一種內(nèi)嵌方式。它有點(diǎn)類似于面向?qū)ο蛘Z言中的繼承,但是面向?qū)ο蟮脑O(shè)計(jì)原則中最好不要有多繼承。甚至于 java 這個(gè)面向?qū)ο笳Z言的老大哥,直接就是不允許多繼承的。所以 Go 語言中的內(nèi)嵌結(jié)構(gòu)體更好理解為組合。代碼示例:package mainimport "fmt"type Callable struct{}func (c Callable) Call() { fmt.Println("can call")}type Photographic struct{}func (p Photographic) Photograph() { fmt.Println("can take photos")}type Moblie struct { Callable Photographic}type Pad struct { Photographic}func main() { var m Moblie fmt.Println("Moblie Function:") m.Call() m.Photograph() var p Pad fmt.Println("Pad Function:") p.Photograph()}第 5~15 行:定義了兩個(gè)功能結(jié)構(gòu)體:打電話和拍照;第 17~20 行:用打電話和拍照兩個(gè)功能結(jié)構(gòu)體,組合成為手機(jī)結(jié)構(gòu)體;第 22~24 行:用拍照功能結(jié)構(gòu)體,組合成為平板電腦結(jié)構(gòu)體;第 26~34 行:初始化兩個(gè)結(jié)構(gòu)體,并打印它們的功能。執(zhí)行結(jié)果:
如果要替換字符串中的文本,請使用gsub方法:實(shí)例:# 讓我們用“cats”代替“dogs”一詞string = "We have many dogs"string.gsub("dogs", "cats")# ---- 輸出結(jié)果 ----"We have many cats"如果要?jiǎng)h除字符串,請使用空字符串作為第二個(gè)參數(shù)。實(shí)例:string = "abccc"string.gsub("c", "")# ---- 輸出結(jié)果 ----"ab"注意事項(xiàng):gsub方法返回一個(gè)新字符串;如果要將更改應(yīng)用于原始字符串,可以使用gsub!方法。gsub方法還可以將正則表達(dá)式作為參數(shù),因此您可以根據(jù)模式替換而不是確切的單詞。實(shí)例:string = "We have 3 cats"string.gsub(/\d+/, "5")# ---- 輸出結(jié)果 ----"We have 5 cats"解釋:這會(huì)將字符串中的所有數(shù)字(\d+)替換為數(shù)字5。我們也可以和塊(block)一同使用title = "the lord of the rings"title.gsub(/\w+/) { |word| word.capitalize }# ---- 輸出結(jié)果 ----"The Lord Of The Rings"Tips : 那么gsub vs sub有什么區(qū)別呢?sub和gsub使用方法一樣,但是sub只會(huì)替換第一個(gè)匹配項(xiàng),gsub會(huì)替換所有項(xiàng)。
數(shù)組的聲明與變量的聲明十分類似,只不過需要在變量名的后面添加一對方括號(hào)。如同下面,我們聲明了一個(gè)可以包含 10 個(gè)整數(shù)的數(shù)組一樣。int intArray[10];在這種形式下,你必須指明指明數(shù)組的大小,也就是方括號(hào)中的數(shù)值?;蛘吣阍诔跏蓟臅r(shí)候使用大括號(hào)直接賦值,這樣就不用直接指明數(shù)組的大小。int intArray[]={1,2,3,4,5};雖然沒有指明數(shù)組的大小,但是其大小就是初始化的元素的數(shù)量。 C 語言中的數(shù)組一旦聲明,其大小是不可以變化的。上面的方式可能是在之前教課書中看到的。在 C99 標(biāo)準(zhǔn)中,引入了一種新的方式來聲明數(shù)組。就是使用變量。之前你可以使用常量來聲明。但是不可以使用變量。這次。規(guī)則發(fā)生了變化。你可以用變量來聲明。int a=10, intArray[a];
刪除方式有兩種:第一種解除方案HibernateTemplate<Student> hibernateTemplate = new HibernateTemplate<Student>(); hibernateTemplate.template(new Notify<Student>() { @Override public Student action(Session session) { // 查詢學(xué)生 Student student =(Student)session.get(Student.class, new Integer(1)); // 查詢Java課程 Course javaCourse = (Course) session.get(Course.class, new Integer(1)); // 查詢C課程 Course ccourse = (Course) session.get(Course.class, new Integer(2)); // 解除關(guān)系 student.getCourses().remove(javaCourse); student.getCourses().remove(ccourse); return null; } });查詢到 ”Hibernate“ 同學(xué)的信息,注意,此時(shí) student 對象處于持久化狀態(tài),意味著 student 對象在程序世界的行為可以同步到數(shù)據(jù)庫中。查詢 ”Hibernate“ 同學(xué)選修的 Java 和 C 兩門課程,此時(shí)保存這兩個(gè)課程信息的對象也處于持久化狀態(tài)。使用如下代碼解除學(xué)生和課程之間的關(guān)系:student.getCourses().remove(javaCourse);student.getCourses().remove(ccourse);因?yàn)閷W(xué)生對象、課程對象都處于持久化狀態(tài)。它們在程序世界中的一言一行都會(huì)同步到數(shù)據(jù)庫中。既然在程序世界解除了彼此之間的關(guān)系,在數(shù)據(jù)庫中,中間表中的關(guān)系描述數(shù)據(jù)也會(huì)自動(dòng)刪除。從控制臺(tái)上所顯示出來的 SQL 語句其實(shí)也知道刪除已經(jīng)成功,這個(gè)就不貼出來了。進(jìn)入 MySql 驗(yàn)證一下:中間表中已經(jīng)不存在和 ”Hibernate“ 同學(xué)相關(guān)的課程信息。但是,此時(shí)你可能會(huì)有一個(gè)想法,剛剛是以學(xué)生對象為主動(dòng)方,向課程對象提出了分手,那么,能不能以課程方為主動(dòng)方提出分手呢?試一下便知,測試之前,先恢復(fù)原來的內(nèi)容:執(zhí)行下面的實(shí)例代碼:HibernateTemplate<Student> hibernateTemplate = new HibernateTemplate<Student>(); hibernateTemplate.template(new Notify<Student>() { @Override public Student action(Session session) { // Hiberante學(xué)生 Student student =(Student)session.get(Student.class, new Integer(1)); // 查詢Java Course javaCourse = (Course) session.get(Course.class, new Integer(1)); // 查詢C Course ccourse = (Course) session.get(Course.class, new Integer(2)); // 解除關(guān)系,以課程對象為主動(dòng)方 javaCourse.getStudents().remove(student); ccourse.getStudents().remove(student); return null; } });可能會(huì)讓你失望,這次操作對數(shù)據(jù)庫沒有任何影響??梢?,分手只能是由學(xué)生對象提出來。雖然在前面課程中,咱們配置了學(xué)生類和課程類的雙向多對多關(guān)聯(lián)映射,但是,兩者之間只能有一個(gè)主動(dòng)方,這里要了解,所謂主動(dòng)方,就是關(guān)系的維系者。關(guān)系的建立和解除只能由主動(dòng)方提供。是不是有點(diǎn)像霸道總裁的狗血故事。第二種解除方案Ok!一起繼續(xù)了解第 2 種方案。不管是哪種方案,切記,只能是由主動(dòng)方提出分手。行事之前,一定要先檢查數(shù)據(jù)是否存在。HibernateTemplate<Student> hibernateTemplate = new HibernateTemplate<Student>(); hibernateTemplate.template(new Notify<Student>() { @Override public Student action(Session session) { // Hibernate學(xué)生 Student student =(Student)session.get(Student.class, new Integer(1)); //解除關(guān)系 student.getCourses().removeAll(student.getCourses()); return null; } });和第一種方案相比較,不再查詢課程信息,由學(xué)生對象單方面一次性解除關(guān)系。student.getCourses().removeAll(student.getCourses()); return null;執(zhí)行結(jié)果沒有什么意外,程序世界中關(guān)系的解除操作同步到了數(shù)據(jù)庫中。一起看看控制臺(tái)上輸出的信息:Hibernate: select student0_.stuId as stuId1_1_0_, student0_.stuName as stuName2_1_0_, student0_.stuPassword as stuPassw3_1_0_, student0_.stuPic as stuPic4_1_0_, student0_.stuSex as stuSex5_1_0_ from Student student0_ where student0_.stuId=?Hibernate: select courses0_.stuId as stuId1_1_1_, courses0_.courseId as courseId2_2_1_, course1_.courseId as courseId1_0_0_, course1_.courseDesc as courseDe2_0_0_, course1_.courseName as courseNa3_0_0_ from score courses0_ inner join Course course1_ on courses0_.courseId=course1_.courseId where courses0_.stuId=?Hibernate: delete from score where stuId=?大家可以看到,Hibernate 接收到解除操作后,立即由中間表連接到課程表,把相關(guān)課程信息從中間表中抺出。一切進(jìn)行得簡單而有序:記住,持久化對象的行為可以同步到數(shù)據(jù)庫中去;多對多雙向關(guān)聯(lián)映射中,有主動(dòng)方和被動(dòng)方一說。
面試官提問: Linux 系統(tǒng)中的 fork 函數(shù)是什么,有什么用途?題目解析:首先從定義上看,fork 函數(shù)的作用是在一個(gè)進(jìn)程的基礎(chǔ)上創(chuàng)建新的進(jìn)程,原有的進(jìn)程被定義為父進(jìn)程,新的進(jìn)程被定義為子進(jìn)程。在 C 語言中調(diào)用 fork() 函數(shù)即實(shí)現(xiàn) fork 功能,示例:#include<unistd.h> //包含fork函數(shù)的頭文件pid_t fork(void); //fork的返回類型為pid_t,我們可以看成int類型認(rèn)識(shí)一個(gè)函數(shù),需要從函數(shù)的定義入手,了解函數(shù)做了什么事情,入?yún)⑹鞘裁?,出參是什么。我們?C 語言實(shí)現(xiàn)的一個(gè)典型的 fork 的程序入手,示例:#include <unistd.h>#include <stdio.h> int main (){ pid_t fpid; int count = 0; fpid = fork(); if (fpid == 0) { //返回值是0,說明是子進(jìn)程 printf("i am the child process, process id is %d\n", getpid()); count++; } else if(fpid > 0) { //返回值>0,說明是父進(jìn)程 printf("i am the parent process, process id is %d\n", getpid()); count++; } else { //返回值<0,說明fork發(fā)生異常 printf("fork encounter exception, process id is %d\n", getpid()); } //打印計(jì)數(shù)器 printf("after fork, counting result : %d\n", count); return 0;}在 MacOS 系統(tǒng)上編譯運(yùn)行案例示例代碼,運(yùn)行結(jié)果如下圖。test.c 編譯執(zhí)行結(jié)果如果是不了解函數(shù)原理的前提下,僅僅從代碼層面分析,在調(diào)用 fork() 函數(shù)之后,代碼會(huì)進(jìn)入 if-else 判斷邏輯,在控制臺(tái)輸出一條語句,然后在控制臺(tái)打印計(jì)數(shù)器的數(shù)值。但是從真正執(zhí)行的結(jié)果來看,這兩個(gè)打印動(dòng)作都分別執(zhí)行了兩次,并不符合我們的預(yù)期。fork 之后的代碼邏輯被執(zhí)行了兩次,而且兩次進(jìn)入的不同的分支,所以重點(diǎn)在于 fork 函數(shù)到底有啥作用。按照定義、入?yún)⒑统鰠⑷阶叩目蚣?,首先是分析函?shù)的定義,調(diào)用 fork 函數(shù)之后發(fā)生了什么事情:(1)分配內(nèi)存:分配新的內(nèi)存空間給子進(jìn)程;(2)拷貝數(shù)據(jù):拷貝父進(jìn)程的數(shù)據(jù)結(jié)構(gòu)給子進(jìn)程;(3)加入列表:將新生成的子進(jìn)程添加到操作系統(tǒng)的進(jìn)程列表;(4)返回結(jié)果:fork 函數(shù)調(diào)度并且返回。然后是分析 fork 函數(shù)的入?yún)?,fork 函數(shù)入?yún)⑹?void,也就是自動(dòng)同步進(jìn)程的上下文,不需要手動(dòng)聲明。最后是分析 fork 函數(shù)的出參,fork 函數(shù)和程序員日常接觸的函數(shù)不同,我們在 C 或者 Java 中定義的函數(shù)只會(huì)有一個(gè)返回值,fork 函數(shù)則是調(diào)用一次,返回二次。調(diào)用方(例如上述案例的 main 函數(shù))根據(jù)返回值的不同判斷處于父進(jìn)程還是子進(jìn)程。(1)返回值 < 0:調(diào)用失敗,一般是因?yàn)椴僮飨到y(tǒng)中的進(jìn)程個(gè)數(shù)達(dá)到上限或者內(nèi)存不足以分配給新的進(jìn)程;(2)返回值 = 0:調(diào)用成功,并且處于子進(jìn)程;(3)返回值 > 0:調(diào)用成功,并且處于父進(jìn)程?,F(xiàn)在就不難理解,從調(diào)用 fork() 函數(shù),代碼實(shí)際上是被父子進(jìn)程分別執(zhí)行了一次,父進(jìn)程的進(jìn)程 id 是 52331,子進(jìn)程的進(jìn)程 id 是 52332。在掌握原理之后,我們繼續(xù)探究 fork 函數(shù)的應(yīng)用場景。fork 函數(shù)的本質(zhì)在原有的進(jìn)程基礎(chǔ)上創(chuàng)建一個(gè)新的進(jìn)程,所以在網(wǎng)絡(luò)通信中使用較多,例如在客戶端發(fā)送一個(gè) HTTP 請求打到服務(wù)器時(shí),服務(wù)器進(jìn)程 fork 出一個(gè)子進(jìn)程用于處理單個(gè)請求,父進(jìn)程則繼續(xù)等待其他的請求。
網(wǎng)絡(luò)爬蟲是一段具有特殊含義的代碼,其功能是模擬用戶在瀏覽器上的操作,發(fā)送 HTTP 請求,接收數(shù)據(jù),然后解析并保存數(shù)據(jù),方便其他應(yīng)用程序使用和分析。這個(gè)過程中間包含了許多自動(dòng)化的操作,若使用得當(dāng),可以產(chǎn)生大量的經(jīng)濟(jì)價(jià)值以及幫助我們減少繁雜的工作?;ヂ?lián)網(wǎng)上每天都會(huì)有無數(shù)的爬蟲在網(wǎng)絡(luò)上游走,獲取相應(yīng)網(wǎng)站的數(shù)據(jù)。這些爬蟲和人一樣,有好有壞,有正義的,也有邪惡的。比如百度 Spider 等搜索引擎爬蟲,為我們提供了信息檢索的最新數(shù)據(jù),我們能通過搜索關(guān)鍵字找到相應(yīng)的網(wǎng)站,正是得益于百度 Spider 每天孜孜不倦的工作。搬運(yùn)相應(yīng)網(wǎng)站的地址和更新相應(yīng)的信息數(shù)據(jù),這也是必要的爬蟲,許多網(wǎng)站也樂于被百度爬蟲抓取最新數(shù)據(jù)。但是也存在許多惡意爬蟲,長時(shí)間、大規(guī)模的請求特定網(wǎng)站數(shù)據(jù),給網(wǎng)站服務(wù)器造成了巨大的壓力,影響正常用戶請求,這也是許多網(wǎng)站討厭爬蟲并積極設(shè)置反爬蟲策略的原因。對于個(gè)人開發(fā)者而言,學(xué)好爬蟲技術(shù),對于個(gè)人成長方面有著極大的好處:鍛煉個(gè)人技能,及時(shí)體驗(yàn)技術(shù)帶來的好處:使用簡單的幾行 Python 代碼就能獲取網(wǎng)站數(shù)據(jù),這樣的學(xué)習(xí)曲線遠(yuǎn)勝于使用 C/C++ 進(jìn)行爬蟲開發(fā)。這也是很多人選擇 Python 開發(fā)爬蟲的原因;在工作和生活上有時(shí)候能帶來極大的好處:比如收集數(shù)據(jù),完成畢業(yè)論文;比如開-發(fā)一款 12306 搶票助手,解決回家搶票困難的問題;又或者抓取股票交易數(shù)據(jù),幫助我們分析股票走勢等等。事實(shí)上,已經(jīng)有很多人做了這些工作并在 Github 上進(jìn)行了開源。事實(shí)上,爬蟲的應(yīng)用還有很多,就不在此逐一說明了。接下來我們介紹 Python 的爬蟲框架以及使用爬蟲框架進(jìn)行開發(fā)的好處。
在 Kotlin 中我們使用 as 或者 as? 來進(jìn)行類型轉(zhuǎn)換,注意在使用 as 轉(zhuǎn)換時(shí),仍然可以使用一般的泛型類型。只有該泛型類的基礎(chǔ)類型是正確的即使是類型實(shí)參錯(cuò)誤也能正常編譯通過,但是會(huì)拋出一個(gè)警告。一起來看個(gè)例子:package com.mikyou.kotlin.genericfun main(args: Array<String>) { printNumberList(listOf(1, 2, 3, 4, 5))//傳入List<Int>類型的數(shù)據(jù)}fun printNumberList(collection: Collection<*>) { val numberList = collection as List<Int>//強(qiáng)轉(zhuǎn)成List<Int> println(numberList)}運(yùn)行輸出:package com.mikyou.kotlin.genericfun main(args: Array<String>) { printNumberList(listOf("a", "b", "c", "d"))//傳入List<String>類型的數(shù)據(jù)}fun printNumberList(collection: Collection<*>) { val numberList = collection as List<Int> //這里強(qiáng)轉(zhuǎn)成List<Int>,并不會(huì)報(bào)錯(cuò),輸出正常, //但是需要注意不能默認(rèn)把類型實(shí)參當(dāng)做Int來操作,因?yàn)椴脸裏o法確定當(dāng)前類型實(shí)參,否則有可能出現(xiàn)運(yùn)行時(shí)異常 println(numberList)}運(yùn)行輸出:如果我們把調(diào)用的地方改成 setOf(1,2,3,4,5):fun main(args: Array<String>) { printNumberList(setOf(1, 2, 3, 4, 5))}fun printNumberList(collection: Collection<*>) { val numberList = collection as List<Int> println(numberList)}運(yùn)行輸出:分析:仔細(xì)想下,得到這樣的結(jié)果也很正常,我們知道泛型的類型實(shí)參雖然在編譯期被擦除,泛型類的基礎(chǔ)類型不受其影響。雖然不知道 List 集合存儲(chǔ)的具體元素類型,但是肯定能知道這是個(gè) List 類型集合不是Set 類型的集合,所以后者肯定會(huì)拋異常。至于前者因?yàn)樵谶\(yùn)行時(shí)無法確定類型實(shí)參,但是可以確定基礎(chǔ)類型。所以只要基礎(chǔ)類型匹配,而類型實(shí)參無法確定有可能匹配有可能不匹配,Kotlin 編譯采用拋出一個(gè)警告的處理。注意:不建議這樣的寫法是因?yàn)槿菀状嬖诎踩[患,由于編譯器只給了個(gè)警告,并沒有卡死后路。一旦后面默認(rèn)把它當(dāng)做強(qiáng)轉(zhuǎn)的類型實(shí)參來操作,而調(diào)用方傳入的是基礎(chǔ)類型匹配而類型實(shí)參不匹配就會(huì)出問題。package com.mikyou.kotlin.genericfun main(args: Array<String>) { printNumberList(listOf("a", "b", "c", "d"))}fun printNumberList(collection: Collection<*>) { val numberList = collection as List<Int> println(numberList.sum())}運(yùn)行輸出:
Python 交互模式是 Python 向用戶提供的命令行界面,在 Windows 命令行中輸入 python,即可進(jìn)入 Python 交互模式,如下圖所示: 進(jìn)入 Python 交互模式 在上圖中,Windows 命令行的提示符是 “C:>”,在 Windows 命令行下,用戶輸入的是 Windows 的命令,例如 dir、python;Python 交互模式的提示符是 “>>>”,在 Python 交互模式下,用戶輸入的是 Python 的命令,例如 print。啟動(dòng) Python 時(shí)會(huì)出現(xiàn)如下常見錯(cuò)誤,在命令行窗口中輸入命令 Python,如果出現(xiàn)提示 “python 不是內(nèi)部或外部命令”,請參考本文最后的小節(jié)“常見錯(cuò)誤”。注意:Python 的版本分為兩個(gè)系列: Python 2 和 Python 3,兩者語法不兼容,目前 Python 3成為主流。在 Linux 命令行中請輸入 python3 而不是 python,命令 python3 啟動(dòng)的是 Python 3,命令 python 啟動(dòng)的是 Python 2。
從宏觀的角度來看,操作系統(tǒng)就是我們?nèi)粘J褂玫?Windows、MacOS、Linux 這類的系統(tǒng),但是這種直觀的用戶交互界面只是操作系統(tǒng)的一小部分功能,操作系統(tǒng)如何決定系統(tǒng)的資源調(diào)度、如何處理內(nèi)存的分配以及如何管理網(wǎng)絡(luò)和文件系統(tǒng),這些都是隱藏在用戶界面之下的內(nèi)容。從課程設(shè)計(jì)的角度來看,操作系統(tǒng)(Operating System)是計(jì)算機(jī)專業(yè)的核心專業(yè)課程,所以可以用來衡量候選人的計(jì)算機(jī)基本功。對于后端程序員,如果是使用 Java 語言,Java 中的多線程會(huì)涉及到進(jìn)程和線程的關(guān)系,這是操作系統(tǒng)中的概念。如果使用 C++ 語言,那么無法避免內(nèi)存分配和管理,這也是操作系統(tǒng)中的基礎(chǔ)概念,因此操作系統(tǒng)是校招面試中的核心之一。
我們知道人類是通過語言進(jìn)行溝通的。比如,帥哥 A 說:“慕課網(wǎng)的課程都很棒!”。而美女 B 是 imooc 迷,隨聲附和道:“嗯,確實(shí)!”。然而美女 C 完全不了解 imooc, 她可能默不作聲,或者反問:“慕課網(wǎng)是什么?”。這意味著,人們的溝通是基于一定的前提假設(shè)的,是基于一些大家都能理解的、約定俗成的規(guī)定的。我們把這些約定或者規(guī)則叫做協(xié)議(Protocol)。兩臺(tái)計(jì)算機(jī)之間的通信也是模擬人類交流的,通信之前也需要有一些約定,也就是說要提前設(shè)計(jì)好協(xié)議。計(jì)算機(jī)網(wǎng)絡(luò)協(xié)議的規(guī)模非常龐大,數(shù)據(jù)包的收發(fā)過程也非常復(fù)雜。為了使計(jì)算機(jī)網(wǎng)絡(luò)容易理解、傳播和實(shí)現(xiàn),科學(xué)家們對網(wǎng)絡(luò)進(jìn)行了分層設(shè)計(jì),并對其進(jìn)行了標(biāo)準(zhǔn)化,最終形成了經(jīng)典的 ISO/OSI 參考模型和 TCP/IP 參考模型。
該函數(shù)用于檢測數(shù)據(jù)的重復(fù)值,返回值是一個(gè)布爾序列,如果某個(gè)值存在重復(fù),則返回的為 True。該函數(shù)有兩個(gè)參數(shù),一個(gè)是參數(shù) subset ,用于指定檢測的某個(gè)列,另一個(gè)是 keep 指定如何控制如何檢測重復(fù)值,有三個(gè)值可選擇:first:表示將第一次出現(xiàn)重復(fù)的值視為唯一的,后面重復(fù)的值標(biāo)記為 True ,默認(rèn)是這種方式;last:表示將最后一次出現(xiàn)重復(fù)的值視為唯一的,前面的重復(fù)值標(biāo)記為 True ;False:表示將所有的重復(fù)項(xiàng)都標(biāo)記為 True ;下面我們通過實(shí)際代碼操作來演示重復(fù)數(shù)據(jù)的檢測操作:# 導(dǎo)入pandas包import pandas as pddata_path="C:/Users/13965/Documents/myFuture/IMOOC/pandasCourse-progress/data_source/第12小節(jié)/execl數(shù)據(jù)demo.xlsx"# 解析數(shù)據(jù)data = pd.read_excel(data_path)print(data)# --- 輸出結(jié)果 --- 編程語言 推出時(shí)間 價(jià)格 主要?jiǎng)?chuàng)始人0 java 1983年 45.6 James Gosling1 python 1991年 67.0 Bjarne Stroustrup2 python 1972年 45.6 Dennis MacAlistair Ritchie3 js 1983年 45.6 js4 James Gosling 2012年 45.6 Rasmus Lerdorf5 C++ java 75.0 Bjarne Stroustrup# duplicated() 重復(fù)數(shù)據(jù)的判斷new_data= data.duplicated()print(new_data)# --- 輸出結(jié)果 ---0 False1 False2 False3 False4 False5 Falsedtype: bool# 結(jié)果解析:這里的 duplicated() 函數(shù)我們什么參數(shù)也沒設(shè)置,所有默認(rèn)會(huì)以正行值作為判斷,也就是判斷是否有重復(fù)行的數(shù)據(jù)內(nèi)容。# duplicated() 設(shè)置 subsetnew_data= data.duplicated(subset="價(jià)格")print(new_data)# --- 輸出結(jié)果 ---0 False1 False2 True3 True4 True5 Falsedtype: bool# 結(jié)果解析:這里程序設(shè)置 subset="價(jià)格",則只對標(biāo)簽為"價(jià)格"的列進(jìn)行重復(fù)數(shù)據(jù)的檢測,可以看到結(jié)果中重復(fù)數(shù)據(jù)值除了第一個(gè)重復(fù)值被視為唯一的,后面出現(xiàn)的重復(fù)值檢測結(jié)果均被設(shè)置為了 True,因?yàn)?keep 參數(shù)如果不設(shè)置,默認(rèn)是 first 方式檢測。# duplicated() 設(shè)置 keep 為 lastnew_data= data.duplicated(subset="價(jià)格",keep="last")print(new_data)# --- 輸出結(jié)果 ---0 True1 False2 True3 True4 False5 Falsedtype: bool# 結(jié)果解析:這里指定 keep="last" ,表示最后一次出現(xiàn)的重復(fù)值視為唯一的,之前出現(xiàn)的重復(fù)值被檢查為重復(fù),結(jié)果為 True 。# duplicated() 設(shè)置 keep 為 Falsenew_data= data.duplicated(subset="價(jià)格",keep=False)print(new_data)# --- 輸出結(jié)果 ---0 True1 False2 True3 True4 True5 Falsedtype: bool# 結(jié)果解析:這里通過設(shè)置 keep=False ,將檢測出的所有重復(fù)值均表示為 True 。
安裝包下載完成之后雙擊安裝包,然后跟隨圖形界面不斷地點(diǎn)擊下一步就可以了。安裝成功后,進(jìn)入 cmd 窗口,輸入git version驗(yàn)證是否安裝成功:驗(yàn)證 Git 是否安裝成功如果出現(xiàn)下圖類似的提示,可能是在安裝的時(shí)候環(huán)境變量自動(dòng)配置失敗,這時(shí)候就需要我們自己手動(dòng)配置一下環(huán)境變量:安裝 Git 可能會(huì)出現(xiàn)的錯(cuò)誤右擊 此電腦->屬性->高級系統(tǒng)設(shè)置->環(huán)境變量。選中 Path,然后點(diǎn)擊下方的編輯,將 Git 的安裝目錄下的 cmd 寫入環(huán)境變量中,Git 會(huì)默認(rèn)安裝在這個(gè)路徑下面:C:\Program Files\Git\cmd。將 Git 配置到環(huán)境變量中環(huán)境變量設(shè)置好了之后,重新打開一個(gè) cmd,再次嘗試 git version 這個(gè)命令,查看安裝是否成功。重新輸入 git version 命令打印版本信息視頻演示安裝過程:44
編程范式是計(jì)算機(jī)編程的基本風(fēng)格或典范模式。如果說每個(gè)編程者都在創(chuàng)造虛擬世界,那么編程范式就是程序員置身其中采用的世界觀和方法論。常見的編程范式包括:面向過程編程面向?qū)ο缶幊叹幊谭缎吞峁┝顺绦騿T對程序執(zhí)行的看法:在面向過程編程中,程序員認(rèn)為程序是一系列相互調(diào)用的過程或者函數(shù);在面向?qū)ο缶幊讨?,程序員認(rèn)為程序是一系列相互作用的對象;而在函數(shù)式編程中一個(gè)程序會(huì)被看作是一個(gè)無狀態(tài)的函數(shù)計(jì)算的序列。不同的編程語言也會(huì)提倡不同的編程范式,一些語言是專門為某個(gè)特定的編程范式設(shè)計(jì)的。例如,C 支持面向過程編程,Java 支持面向?qū)ο缶幊?。Python 編程語言支持多種編程范式,應(yīng)該在不同的應(yīng)用場景下,選擇合適的編程范式。
Zabbix是一個(gè)企業(yè)級的高度集成開源監(jiān)控軟件,提供分布式監(jiān)控解決方案,可以用來監(jiān)控設(shè)備、服務(wù)等的可用性和性能,由國外團(tuán)隊(duì)進(jìn)行維護(hù)以及持續(xù)更新源碼,可以自由下載,是一個(gè)真正的源代碼開放產(chǎn)品。Zabbix的通用架構(gòu)是C/S架構(gòu),通過B/S在web端進(jìn)行展示和配置,分布式架構(gòu)為Client/Proxy/Server,Zabbix-Server將采集到的數(shù)據(jù)持久地存儲(chǔ)到數(shù)據(jù)庫中。Zabbix數(shù)據(jù)的采集不僅可以使用Agent方式,也可以使用SNMP、SSH、Telent、IPMI等多種協(xié)議。Zabbix的主要特點(diǎn):安裝配置簡單;免費(fèi)開源,試錯(cuò)成本低;支持多語言;自動(dòng)發(fā)現(xiàn)服務(wù)器;分布式監(jiān)控;集中式web管理;郵件、微信等通知功能。Zabbix 的新版本 5.0,可以支持 proxy/server 端通過 odbc 連接 MySQL 進(jìn)行采集監(jiān)控?cái)?shù)據(jù),所有的配置在web端都可以完成,而且有現(xiàn)成的完善的監(jiān)控模板,使用起來非常簡單高效。
能不能把一張圖片保存到數(shù)據(jù)庫?答案是明確的。真實(shí)應(yīng)用場景中不會(huì)這么做。數(shù)據(jù)庫中只會(huì)保存圖片路徑,具體的圖片文件會(huì)存儲(chǔ)在文件服務(wù)器中。Hibernate 支持的大對象有:Clob:文本大對象;Blob:二進(jìn)制數(shù)據(jù)大對象?,F(xiàn)在為每一個(gè)學(xué)生保存?zhèn)€人圖片:student 類中添加 stuPic 屬性(注意類型): private Blob stuPic;編寫測試實(shí)例: @Test public void testInsertPic() { //會(huì)話對象 Session session = sessionFactory.openSession(); // 事務(wù)對象 Transaction transaction = null; try { // 打開事務(wù) transaction = session.beginTransaction(); //添加新學(xué)生 Student stu=new Student("MK", "男"); InputStream is=new FileInputStream("pic.png"); Blob stuPic=Hibernate.getLobCreator(session).createBlob(is, is.available()); stu.setStuPic(stuPic); session.merge(stu); transaction.commit(); } catch (Exception e) { transaction.rollback(); } finally { session.close(); } }如果要保存文本大對象,則使用如下代碼:Clob c=Hibernate.getLobCreator(session).createClob("我是中國人……");執(zhí)行結(jié)果,不出意外,數(shù)據(jù)保存成功。不要試著把很多圖片直接保存到數(shù)據(jù)庫中,圖片的存儲(chǔ)與查詢會(huì)比較慢,會(huì)嚴(yán)重拖累數(shù)據(jù)庫性能。另外數(shù)據(jù)庫的體積也會(huì)變得臃腫不堪,現(xiàn)在可是一個(gè)以瘦為美的世界!如何讀取數(shù)據(jù)庫中保存的圖片?相信你一定能找到答案。
由于 Android Studio 是通過 ADB 來連接真機(jī)進(jìn)行調(diào)試和測試的,所以我們必須安裝 ADB 程序。前面章節(jié)我們已經(jīng)學(xué)習(xí)過安裝 Android SDK,Android SDK Tools 中已經(jīng)包含 ADB,我們只需要完成環(huán)境變量的配置即可。macOS:無需其他配置。Ubuntu Linux在 ~/.bashrc 文件中添加如下內(nèi)容:export SDK_HOME=/work/androidenv/android-sdk-linuxexport SDK_PLATFORM_TOOLS=$SDK_HOME/platform-toolsexport SDK_TOOLS=$SDK_HOME/toolsexport PATH=$SDK_PLATFORM_TOOLS:$SDK_TOOLS:$PATHTips:SDK_HOME 是指 Android SDK 安裝目錄如果 ADB 在使用中提示如下錯(cuò)誤:error: insufficient permissions for device: udev requires plugdev group membership原因是當(dāng)前 Linux 用戶未在 plugdev 用戶組中,解決方法是把當(dāng)前用戶添加到 plugdev 組中。sudo usermod -aG plugdev $LOGNAMEWindows按如下步驟添加環(huán)境變量:首先右擊計(jì)算機(jī) > 屬性 > 高級系統(tǒng)設(shè)置 > 環(huán)境變量,然后點(diǎn)擊環(huán)境變量 > 系統(tǒng)變量 > Path > 編輯,添加 ADB 程序所在目錄。Tips:上圖中 ADB 所在目錄為 C:\Android。
編寫程序 calc.py,對命令行輸入的表達(dá)式進(jìn)行計(jì)算,例如:C:\> python calc.py 1 + 12C:\> python calc.py 2 - 12在第 1 行,計(jì)算 1 + 1 的結(jié)果在第 3 行,計(jì)算 2 - 1 的結(jié)果注意:數(shù)字和運(yùn)算符之間必須要有空格正確的輸入:python calc.py 1 + 1錯(cuò)誤的輸入:python calc.py 1+1程序 calc.py 的代碼如下:import sysif len(sys.argv) != 4: print('Usage: python calc.py operand [+|-] operand') sys.exit(0)left = sys.argv[1]operator = sys.argv[2]right = sys.argv[3]if operator == '+': print(int(left) + int(right))if operator == '-': print(int(left) - int(right))在第 3 行,檢查命令行參數(shù)個(gè)數(shù),正常情況下是 4 個(gè)參數(shù)參數(shù) 0 是 ‘calc.py’參數(shù) 1 是左邊的操作數(shù)參數(shù) 2 是操作符參數(shù) 3 是右邊的操作數(shù)在第 7 行到第 9 行,獲取左邊的操作數(shù)、操作符、右邊的操作數(shù)在第 11 行,求和注意,命令行參數(shù)是字符串,需要使用函數(shù) int(string) 轉(zhuǎn)換為整數(shù)在第 14 行,相減注意,命令行參數(shù)是字符串,需要使用函數(shù) int(string) 轉(zhuǎn)換為整數(shù)
在傳統(tǒng)的軟件開發(fā)中,框架是一種提供了可重用的公共結(jié)構(gòu)的技術(shù),為構(gòu)建新的應(yīng)用程序提供了極大的便利??蚣苁且环N非常成熟的技術(shù),在多個(gè)領(lǐng)域得到了成功的應(yīng)用,例如:在桌面應(yīng)用程序開發(fā)的領(lǐng)域,微軟公司的 Visual Studio C++ 為應(yīng)用程序生成框架,基于 VC++ 應(yīng)用程序框架可以大大的提升桌面程序的開發(fā)效率。隨著 Web 開發(fā)項(xiàng)目的復(fù)雜度的日益提升,軟件開發(fā)中的框架技術(shù)被引入到 Web 開發(fā)領(lǐng)域。Web 開發(fā)框架是用于進(jìn)行 web 開發(fā)的一套軟件架構(gòu),Web 框架為 Web 應(yīng)用程序提供了基礎(chǔ)的功能。開發(fā)人員在 Web 框架的基礎(chǔ)上實(shí)現(xiàn)自己的業(yè)務(wù)邏輯,基于 Web 框架開發(fā)應(yīng)用,開發(fā)人員只需要專注應(yīng)用的業(yè)務(wù)邏輯,非業(yè)務(wù)邏輯的基礎(chǔ)功能則由框架提供,從而提升開發(fā)效率。
要想構(gòu)建 Gradle Wrapper 就必須本地先配置了 Gradle 環(huán)境變量,具體的配置方法可以參考《構(gòu)建自己的 Gradle 工程》中關(guān)于 Gradle 環(huán)境變量的配置。Gradle 的命令中已經(jīng)內(nèi)置了 wrapper 命令,其實(shí)就是執(zhí)行 warpper 任務(wù)。該任務(wù)就是生成我們前面說的 gradle 文件夾及它的子目錄 wrapper 文件夾。在根目錄下我們先刪掉 gradle 文件夾,然后在項(xiàng)目根目錄執(zhí)行g(shù)radle wrapper命令。C:\Users\LeiQi PC\Documents\MyApplication>gradle wrapperDeprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.Use '--warning-mode all' to show the individual deprecation warnings.See https://docs.gradle.org/6.0.1/userguide/command_line_interface.html#sec:command_line_warningsBUILD SUCCESSFUL in 1s1 actionable task: 1 executed我們會(huì)看到會(huì)重新生成 Gradle 文件夾,目錄結(jié)構(gòu)如我們上節(jié)中的一樣如下。這兩個(gè)文件的含義如下:gradle-wrapper.jar: 既然是 jar 包,那么它肯定是包含了 Gradle 運(yùn)行時(shí)的邏輯代碼;gradle-wrapper.properties: 這個(gè)文件主要負(fù)責(zé)配置 Gradle wrapper 運(yùn)行時(shí)的屬性文件,聲明具體使用哪個(gè)版本的 Gradle。
首先,我們都知道最基礎(chǔ)的分層協(xié)議是計(jì)算機(jī)網(wǎng)絡(luò) OSI(Open System Interconnection)體系。OSI 模型如上圖(a)所示,網(wǎng)絡(luò)結(jié)構(gòu)被拆分為 7 層,自頂向下分別是應(yīng)用層、表示層、會(huì)話層、傳輸層、網(wǎng)絡(luò)層、數(shù)據(jù)鏈路層以及物理層。但是 OSI 模型是一種概念模型,雖然理論比較完整,并不實(shí)用。TCP/IP 體系如上圖(c)所示,包含了應(yīng)用層、傳輸層、網(wǎng)絡(luò)層以及網(wǎng)絡(luò)接口層,不過我們一般關(guān)注上面三層的內(nèi)容,最下層及網(wǎng)絡(luò)接口層沒有實(shí)質(zhì)性的協(xié)議。TCP/IP 體系分層就是我們實(shí)際應(yīng)用中的網(wǎng)絡(luò)協(xié)議。作為 OSI 七層協(xié)議和 TCP/IP 四層協(xié)議的折中,還有一種是五層協(xié)議的體系,往往是面試中考察的重點(diǎn)。五層協(xié)議,如上圖中(b)所示,自頂向下分為應(yīng)用層、傳輸層、網(wǎng)絡(luò)層、數(shù)據(jù)鏈路層以及物理層,下面我們將詳細(xì)闡述每層的作用以及代表協(xié)議。