2.1.1 一個(gè)基本的甘特圖基本的甘特圖由「標(biāo)題」、「日期格式約定」、「分組及任務(wù)」三部分組成。實(shí)例 1:一個(gè)基本的甘特圖。?```mermaidgantt title 簡單的甘特圖 dateFormat YYYY-MM-DD section 分區(qū)1 任務(wù)1-1 :a1, 2014-01-01, 30d 任務(wù)1-2 :after a1 , 20d section 分區(qū)2 任務(wù)2-1 :2014-01-12 , 12d 任務(wù)2-2 : 24d?```渲染效果如下:2.1.2 規(guī)定日期格式通過設(shè)置 dateFormat 屬性,可以指定甘特圖定義日期時(shí)的解析方式。日期的格式支持以下情形:表達(dá)式取值示例描述 YYYY20144 位數(shù)年 YY142 位數(shù)年 Q1…4 季度數(shù) M MM1…12 月份數(shù) MMM MMMMJanuary…Dec 月份名稱 D DD1…31 月中天數(shù) Do1st…31st 月中第幾天 DDD DDDD1…365 年中天數(shù) X1410715640.579Unix 時(shí)間戳(秒)x1410715640579Unix 時(shí)間戳(毫秒)H HH0…2324 小時(shí)制小時(shí)數(shù) h hh1…1212 小時(shí)制小時(shí)數(shù) a Aam pm 上午、下午 m mm0…59 分鐘數(shù) s ss0…59 秒鐘數(shù) S0…9 十分之一秒 SS0…99 百分之一秒 SSS0…999 千分之一秒 Z ZZ+12:00 時(shí)區(qū)2.1.3 任務(wù)的定義Mermaid 甘特圖中,每個(gè)任務(wù)隸屬于一個(gè)分組,一個(gè)分組內(nèi)可以定義多個(gè)任務(wù),一張甘特圖表中可以包含多個(gè)分組。甘特圖中的任務(wù)定義格式形如:<任務(wù)名> : [crit], [active], [任務(wù)ID], [前置任務(wù)], <周期>,其中「任務(wù)名」和「周期」兩項(xiàng)是必要項(xiàng)。實(shí)例 2:在甘特圖中定義任務(wù)。 gantt 蘋果 :a, 2017-07-20, 1w 香蕉 :crit, b, 2017-07-23, 1d 櫻桃 :active, c, after b a, 1d渲染效果如下:2.1.4 定義對(duì)象的生命周期甘特圖上的對(duì)象都是以時(shí)間為基礎(chǔ)而存在的,對(duì)于時(shí)間,我們可能有非常多的定義需求,比如精度上的「年」、「月」、「日」、「時(shí)」等,又比如「24H」或者 「12H」這樣寫法上的不同等。Mermaid 為時(shí)間提供了豐富的語法支持。完整的定義語法如下:%a - 周維度名稱(簡寫形式)。%A - 周維度名稱(完整形式)。%b - 月維度名稱(簡寫形式)。%B - 月維度名稱(完整形式).%c - 日期時(shí)間,相當(dāng)于"%a %b %e %H:%M:%S %Y"。%d - 月中日期固定寬度寫法,取值范圍 [01,31].%e - 月中日期變動(dòng)寬度寫法,取值范圍 [ 1,31];等同于 %_d.%H - 小時(shí)數(shù)(24小時(shí)制)取值范圍 [00,23]。%I - 小時(shí)數(shù)(12小時(shí)制)取值范圍 [01,12]。%j - 年中日期固定寬度寫法,取值范圍 [001,366]。%m - 年中月份固定寬度寫法,取值范圍 [01,12]。%M - 分鐘數(shù)固定寬度寫法,取值范圍 [00,59]。%L - 毫秒數(shù)固定寬度寫法,取值范圍 [000, 999]。%p - 上午 \ 下午。%S - 秒數(shù)固定寬度寫法,取值范圍 [00,61]。%U - 年中周數(shù)的固定寬度寫法,以周日為每周第一天,取值范圍 [00,53]。%w - 周中日期寫法,取值范圍 [0(周日),6]。%W - 年中周數(shù)的固定寬度寫法,以周一為每周第一天,取值范圍 [00,53]。%x - 日期,等同于 "%m/%d/%Y"。%X - 時(shí)間,等同于 "%H:%M:%S"。%y - 年,僅后兩位,取值范圍 [00,99]。%Y - 年,完整四位。%Z - 時(shí)區(qū),例如:"-0700"。%% - 用于輸出百分號(hào) "%" 。
manage.py 是一個(gè)命令行工具,用于與 Django 進(jìn)行不同方式的交互腳本,通過 python manage.py help 命令,我們可以看到 manage.py 文件支持的所有操作:(django-manual) [root@server first_django_app]# python manage.py helpType 'manage.py help <subcommand>' for help on a specific subcommand.Available subcommands:[auth] changepassword createsuperuser[contenttypes] remove_stale_contenttypes[django] check compilemessages createcachetable dbshell diffsettings dumpdata flush inspectdb loaddata makemessages makemigrations migrate sendtestemail shell showmigrations sqlflush sqlmigrate sqlsequencereset squashmigrations startapp startproject test testserver[sessions] clearsessions[staticfiles] collectstatic findstatic runserver比較常用的命令有:createsuperuser:創(chuàng)建超級(jí)用戶,用來登錄 Django 自帶的管理系統(tǒng);shell:進(jìn)入shell 模式可以直接對(duì) Django 中的模型進(jìn)行增刪改查等測(cè)試,這個(gè)后續(xù)介紹 Django 的 ORM 模型中使用;makemigrations/migrate:用來進(jìn)行數(shù)據(jù)庫遷移工作的,比如我們修改了數(shù)據(jù)庫的表的字段,然后需要保存修改,直接使用makemigrations 生成遷移文件,然后到了新環(huán)境使用 migrate 命令根據(jù)遷移文件生成相應(yīng)的數(shù)據(jù)表;showmigrations:查看遷移表中的信息;(django-manual) [root@server first_django_app]# python manage.py showmigrationsadmin [ ] 0001_initial [ ] 0002_logentry_remove_auto_add [ ] 0003_logentry_add_action_flag_choicesauth [ ] 0001_initial [ ] 0002_alter_permission_name_max_length [ ] 0003_alter_user_email_max_length [ ] 0004_alter_user_username_opts [ ] 0005_alter_user_last_login_null [ ] 0006_require_contenttypes_0002 [ ] 0007_alter_validators_add_error_messages [ ] 0008_alter_user_username_max_length [ ] 0009_alter_user_last_name_max_length [ ] 0010_alter_group_name_max_length [ ] 0011_update_proxy_permissionscontenttypes [ ] 0001_initial [ ] 0002_remove_content_type_namesessions [ ] 0001_initialstartproject/startapp:創(chuàng)建項(xiàng)目和應(yīng)用。這里我們發(fā)現(xiàn)它的作用和前面使用 django-admin startproject/startapp 作用是一樣的;runserver:在調(diào)試時(shí)會(huì)常常使用。例如下面的操作,啟動(dòng) Django 內(nèi)置的 Web 服務(wù)器,監(jiān)聽8000端口,等待 HTTP 請(qǐng)求:(django-manual) [root@server first_django_app]# python manage.py runserver 0.0.0.0:8000Watching for file changes with StatReloaderPerforming system checks...System check identified no issues (0 silenced).You have 17 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.Run 'python manage.py migrate' to apply them.March 14, 2020 - 13:47:04Django version 2.2.11, using settings 'first_django_app.settings'Starting development server at http://0.0.0.0:8000/Quit the server with CONTROL-C.
Python 自 1989 年誕生以來,經(jīng)歷了 30 年的發(fā)展,已經(jīng)成為流行的編程語言之一。TIOBE 編程語言排行榜根據(jù)互聯(lián)網(wǎng)上程序員、教程和第三方廠商的數(shù)量,并使用搜索引擎統(tǒng)計(jì)出編程語言的排名數(shù)據(jù),反映了某個(gè)編程語言的熱門程度。下圖為 2019 年 12 月份的 TIOBE 編程語言排行榜。TIOBE 編程語言排行榜表明 Python 語言的熱門程度已經(jīng)成為僅次于 Java 語言 和 C 語言。2019 年 12 月的 TIOBE 編程語言的排行榜2017 年 10 月,教育部考試中心發(fā)布《關(guān)于全國計(jì)算機(jī)等級(jí)考試體系調(diào)整的通知》,新增 Python 語言程序設(shè)計(jì)科目。2018 年 9 月,舉行了首考。Python 編程語言作為一種國家標(biāo)準(zhǔn)進(jìn)入了中國的考試認(rèn)證體系,這是一個(gè)里程碑性質(zhì)的事件,意味著 Python 語言在國內(nèi)教育領(lǐng)域得到官方的正式認(rèn)同,在部分高校,Python 程序設(shè)計(jì)已經(jīng)成為一門必修課程。無論是計(jì)算機(jī)專業(yè)的院系還是非計(jì)算機(jī)專業(yè)的院系,開設(shè) Python 程序設(shè)計(jì)課程,已經(jīng)成為一種趨勢(shì)。Python 已經(jīng)成為全國計(jì)算機(jī)等級(jí)考試二級(jí)科目Python 在國內(nèi)工業(yè)界得到了廣泛的應(yīng)用和認(rèn)可,并且Python 的人才易于培養(yǎng)和招聘,國內(nèi)的互聯(lián)網(wǎng)的領(lǐng)頭企業(yè)提供了大量的和 Python 的工作崗位,下圖為騰訊發(fā)布的 Python 的工作崗位。騰訊招聘的 Python 工作崗位
ES5 中 JavaScript 中沒有所謂的集合概念,但是在其他一些語言如:C++、Java、python 等很早就有集合的概念了。ES6 對(duì)集合的數(shù)據(jù)結(jié)構(gòu)進(jìn)行了補(bǔ)充,引入了 Set 數(shù)據(jù)結(jié)構(gòu)。Set 對(duì)象允許你存儲(chǔ)任何類型的值,且存儲(chǔ)的值是唯一的,存儲(chǔ)的值可以是原始類型或者是引用類型。并且 Set 對(duì)象提供了讀、寫、查找和遍歷等方法,用以更加靈活地操作數(shù)據(jù)。Set 是一個(gè)構(gòu)造函數(shù),在使用時(shí)必須要使用 new 來創(chuàng)建一個(gè)實(shí)例,創(chuàng)建的實(shí)例基本上可以當(dāng)作數(shù)組來使用。構(gòu)造方法如下:var set = new Set([iterable]);在 new Set() 時(shí),可以接收一個(gè)默認(rèn)值作為參數(shù),這個(gè)參數(shù)需要滿足 可迭代 的要求。下面我們看下方法的使用實(shí)例:// 創(chuàng)建一個(gè)空的 Set 實(shí)例var set = new Set() // Set(0) {}// 添加數(shù)據(jù)set.add('es6') // Set(1) {"es6"}// 還可以鏈?zhǔn)教砑訑?shù)據(jù)set.add('imooc').add('7') // Set(3) {"es6", "imooc", "7"}// 接受一個(gè)可遍歷的對(duì)象,作為默認(rèn)值,大部分情況是數(shù)組var set = new Set([1, 2, 3])console.log(set) // Set(3) {1, 2, 3}上面的實(shí)例就是使用 Set 操作數(shù)據(jù)的過程,和數(shù)字不同的是對(duì)于數(shù)據(jù)的增刪改查 Set 都有對(duì)應(yīng)的方法,而數(shù)字則通過索引的方式來實(shí)現(xiàn)的,這樣在一定情況下會(huì)出現(xiàn)不可控的因素。后面的章節(jié)我們會(huì)對(duì) Set 做詳細(xì)的學(xué)習(xí)。
我們都知道 JVM 中的泛型一般是通過類型擦除實(shí)現(xiàn)的,也就是說泛型類實(shí)例的類型實(shí)參在編譯時(shí)被擦除,在運(yùn)行時(shí)是不會(huì)被保留的?;谶@樣實(shí)現(xiàn)的做法是有歷史原因的,最大的原因之一是為了兼容JDK1.5之前的版本,當(dāng)然泛型類型擦除也是有好處的,在運(yùn)行時(shí)丟棄了一些類型實(shí)參的信息,對(duì)于內(nèi)存占用也會(huì)減少很多。正因?yàn)榉盒皖愋筒脸蛟跇I(yè)界 Java 的泛型又稱偽泛型。因?yàn)榫幾g后所有泛型的類型實(shí)參類型都會(huì)被替換為 Object 類型或者泛型類型形參指定上界約束類的類型。例如:List<Float>、List<String>、List<Student>在 JVM 運(yùn)行時(shí)Float、String、Student都被替換成Object類型,如果是泛型定義是List<T extends Student>那么運(yùn)行時(shí)T被替換成Student類型,具體可以通過反射Erasure類可看出。雖然 Kotlin 沒有和 Java 一樣需要兼容舊版本的歷史原因,但是由于 Kotlin 編譯器編譯后出來的 class 也是要運(yùn)行在和 Java 相同的 JVM 上的,JVM的泛型一般都是通過泛型擦除,所以 Kotlin 始終還是邁不過泛型擦除的坎。但是 Kotlin 是一門有追求的語言,不想再被 C# 那樣噴 Java 說什么泛型集合連自己的類型實(shí)參都不知道,所以 Kotlin 借助 inline 內(nèi)聯(lián)函數(shù)玩了個(gè)小魔法。
@PreAuthorize 最主要的作用是決定一個(gè)方法能否被執(zhí)行。例如:@PreAuthorize("hasRole('USER')")public void create(Contact contact);這段代碼的含義是,只有包含了 USER 角色的用戶,才被允許調(diào)用 create 方法。對(duì)于權(quán)限,還有另一種更為具體的寫法:@PreAuthorize("hasPermission(#contact, 'admin')")public void deletePermission(Contact contact, Sid recipient, Permission permission);這里我們指定了調(diào)用方法中的參數(shù),用來判斷當(dāng)前用戶是否對(duì)將被刪除的 Contact 對(duì)象具有 admin 角色。通過這種寫法,我們可以將目標(biāo)對(duì)象中的任意方法作為表達(dá)式的變量參數(shù)。Spring Security 要在表達(dá)式中訪問方法參數(shù)有幾種方式:Spring Security 可以使用 @P 注解,為變量設(shè)置別名。例如:import org.springframework.security.access.method.P;...@PreAuthorize("#c.name == authentication.name")public void doSomething(@P("c") Contact contact);使用 Spring 的 @Param 注解,為變量設(shè)置別名。例如:import org.springframework.data.repository.query.Param;...@PreAuthorize("#n == authentication.name")Contact findContactByName(@Param("n") String name);如果使用的是 JDK 8 + Spring 4+ ,不需要額外的注解,程序會(huì)自動(dòng)發(fā)現(xiàn)參數(shù)名稱。Spring Security 的表達(dá)式注解支持對(duì)象的屬性。例如,當(dāng)我們指定方法入?yún)⒅械膶傩悦Q和我們身份信息中的名稱一致時(shí)允許訪問:@PreAuthorize("#contact.name == authentication.name")public void doSomething(Contact contact);這里我們用到了內(nèi)置參數(shù) authentication ,該對(duì)象保存在 Spring Security 的安全上下文中。我們也可以直接訪問到該對(duì)象的身份信息屬性。@PostAuthorize 使用場景相對(duì)較少,它針對(duì)方法的返回進(jìn)行權(quán)限過濾。
我們知道,在 RabbitMQ 中,消息的產(chǎn)生是源自生產(chǎn)者,對(duì)應(yīng)的,消費(fèi)消息是依靠消費(fèi)者,而在生產(chǎn)者生產(chǎn)消息到消費(fèi)者最終消費(fèi)消息的過程中,消息發(fā)送模式扮演著重要的角色。如果需要將消息發(fā)送模式結(jié)合生產(chǎn)者與消費(fèi)者進(jìn)行理解的話,那么,我們可以這樣來理解:在生產(chǎn)者生產(chǎn)出一條消息后,需要經(jīng)過 RabbitMQ 的通道來發(fā)送給消費(fèi)者,消費(fèi)者接收到消息,并最終對(duì)消息進(jìn)行消費(fèi),這其中的通道,指的就是 RabbitMQ 的消息發(fā)送模式。接下來讓我們具體來看一下,RabbitMQ 是如何把生產(chǎn)者生產(chǎn)的消息傳輸給消費(fèi)者消費(fèi)的。約定:由于在本節(jié)中所使用的消息發(fā)送模式原理圖均來自官網(wǎng),所以圖中每個(gè)元素都代表什么意思,我在這里一并說過,后面不再贅述:天藍(lán)色橢圓 + 其中的字母 P : 代表生產(chǎn)者。藍(lán)色的橢圓 + 其中的字母 C + 數(shù)字下表 : 代表消費(fèi)者。深藍(lán)色橢圓 + 其中的字母 X : 代表交換機(jī)。交換機(jī)上方的 type : 代表交換機(jī)的類型。橙色小矩形塊所組成的大矩形塊 : 代表具體的一個(gè)隊(duì)列。圖中的箭頭,不帶字母標(biāo)識(shí)的 : 代表消息的流向。圖中箭頭上的字母標(biāo)識(shí) : 代表特定模式下的 key 值。
3.3.1 封裝封裝是將數(shù)據(jù)和代碼捆綁到一起,對(duì)象的某些屬性和方法可以是私有的,不能被外界訪問,以此實(shí)現(xiàn)對(duì)數(shù)據(jù)和代碼不同級(jí)別的訪問權(quán)限。防止了程序相互依賴性而帶來的變動(dòng)影響,有效實(shí)現(xiàn)了兩個(gè)目標(biāo):對(duì)數(shù)據(jù)和行為的包裝和信息隱藏。在下面的程序中,類 Span 描述了間距,間距有 3 個(gè)屬性:開始位置結(jié)束位置長度,長度等于開始位置到結(jié)束位置之間的距離使用封裝通過方法訪問這些屬性,而不是直接訪問這些屬性,代碼如下:class Span: def __init__(self, start, end): self._start = start self._end = end def get_start(self): return self._start def get_end(self): return self._end def get_length(self): return self._end - self._startspan = Span(10, 100)print('start = %d' % span.get_start())print('end = %d' % span.get_end())print('length = %d' % span.get_length())在第 2 行,定義了構(gòu)造函數(shù),使用 start 和 end 構(gòu)造間距在第 3 行,_start 是私有屬性,描述了間距的開始位置,通過參數(shù) start 設(shè)置在第 4 行,_end 是私有屬性,,描述了間距的結(jié)束位置,通過參數(shù) end 設(shè)置在第 6 行,定義了成員函數(shù) get_start,外界通過 get_start 獲取開始位置在第 9 行,定義了成員函數(shù) get_end,外界通過 get_end 獲取結(jié)束位置在第 12 行,定義了成員函數(shù) get_length,外界通過 get_length 獲取間距的長度程序運(yùn)行輸出如下:start = 10end = 100length = 903.3.2 繼承繼承是一種層次模型,這種層次模型能夠被重用。層次結(jié)構(gòu)的上層具有通用性,但是下層結(jié)構(gòu)則具有特殊性。在繼承的過程中,子類則可以從父類繼承一些方法和屬性。子類除了可以繼承以外同時(shí)還能夠進(jìn)行修改或者添加。下面的例子描述父類 Person 和子類 Teacher 之間的繼承關(guān)系,子類 Teacher 從父類 Person 繼承了一些方法和屬性,父類 Person 代碼如下:class Person: def __init__(self, name, age): self.name = name self.age = age def introduce(self): print('My name is', self.name) print('My age is', self.age)在第 2 行,在構(gòu)造函數(shù) __init__ 中,設(shè)置屬性 name 和 age在第 6 行,在成員函數(shù) introduce 中,輸出屬性 name 和 age子類 Teacher 代碼如下:class Teacher(Person): def __init__(self, name, age, salary): Person.__init__(self, name, age) self.salary = salary def showSalary(self): print('My salary is', self.salary)teacher = Teacher('tom', 30, 5000)teacher.introduce() teacher.showSalary()在第 2 行,在構(gòu)造函數(shù) __init__ 中,設(shè)置屬性 name、age 和 salary在第 3 行,調(diào)用父類的 __init__ 方法,設(shè)置屬性 name 和 age在第 4 行,設(shè)置子類 Tearch 特有的屬性 salary在第 6 行,在成員函數(shù) showSalary 中,輸出屬性 salary在第 9 行,實(shí)例化一個(gè)對(duì)象 teacher在第 10 行,調(diào)用 teacher 的 introduce 方法,該方法是子類繼承的方法在第 11 行,調(diào)用 teacher 的 showSalary 方法,該方法是子類特有的方法程序運(yùn)行輸出如下:My name is tomMy age is 30My salary is 50003.3.3 多態(tài)在面向?qū)ο笳Z言中,接口的多種不同的實(shí)現(xiàn)方式即為多態(tài)。多態(tài)機(jī)制使具有不同內(nèi)部結(jié)構(gòu)的對(duì)象可以共享相同的外部接口,它是面向?qū)ο蟪绦蛟O(shè)計(jì)(OOP)的一個(gè)重要特征。下面通過一個(gè)具體的例子演示多態(tài)的意義:有多種類型的形體,例如:正方形、三角形等。但是,不論形體的類型是什么,每一種形體都有周長的概念。顯然,計(jì)算每種形體的周長的公式是不一樣的。面對(duì)抽象的形體時(shí),希望能夠獲取形體的周長。首先,定義父類 Shape,代碼如下:class Shape: def get_circle(self): pass在第 1 行,定義了父類 Shape,子類 Square 和 Triangle 繼承于 Shape在第 2 行,定義了成員方法 get_circle,get_circle 計(jì)算形體的周長在第 3 行,這里是一個(gè)空方法,僅僅定義了接口,在子類中定義具體的實(shí)現(xiàn)定義子類 Square,代碼如下:class Square(Shape): def __init__(self, side): self.side = side def get_circle(self): return self.side * 4在第 1 行,定義了子類 Square,繼承于父類 Shape在第 2 行,定義構(gòu)造函數(shù) __init__,參數(shù) side 表示正方形的邊長在第 5 行,定義成員方法 get_circle,計(jì)算正方形的周長定義子類 Triangle,代碼如下:class Triangle(Shape): def __init__(self, a, b, c): self.a = a self.b = b self.c = c def get_circle(self): return self.a + self.b + self.c在第 1 行,定義了子類 Triangle,繼承于父類 Shape在第 2 行,定義構(gòu)造函數(shù) __init__,參數(shù) a、b、c 表示三角形的邊長在第 7 行,定義成員方法 get_circle,計(jì)算三角形的周長在父類 Shape 中定義了接口 get_circle ,子類 Square 和子類 Triangle 分別實(shí)現(xiàn)了接口 get_circle。只要對(duì)象的類型是 Shape,不論具體的類型是 Square 還是 Triangle,都可以調(diào)用接口 get_circle,代碼如下:square = Square(10)triangle = Triangle(3, 4, 5)shapes = [square, triangle]for shape in shapes: print(shape.get_circle())在第 1 行,構(gòu)造一個(gè)正方形 square,邊長是 10在第 2 行,構(gòu)造一個(gè)三角形 triangle,邊長是 3、4、5在第 3 行,構(gòu)造一個(gè)列表 shapes,將 square 和 triangle 加入到列表 shapes 中在第 4 行,遍歷列表 shapes在第 5 行,調(diào)用 shape 的 get_circle 方法,獲取形體的周長程序運(yùn)行輸出如下:4012
強(qiáng)參數(shù)查詢使用 SQL 查詢時(shí),可以指定查詢條件,這個(gè)地球人都知道。HQL 中同樣能使用條件查詢:from Student s where s.stuId> 2在 HQL 中,如果查詢條件中的數(shù)據(jù)需要通過參數(shù)傳遞,則會(huì)有兩種方案:匿名方案,已經(jīng)司空見慣,對(duì)不對(duì);from Student s where s.stuId> ?命名參數(shù)方案。from Student s where s.stuId> :id參數(shù)名前面一定要有一個(gè)冒號(hào) :id。完整實(shí)例獻(xiàn)上:String hql="from Student s where s.stuId> :id";Query query=session.createQuery(hql);query.setInteger("id", 2);List<Student> stus= query.list();for (Student student : stus) { ystem.out.println(student);}return null;可自行查看控制臺(tái)上的輸出結(jié)果。強(qiáng)命名參數(shù)和 ? 占位符作用是一樣的,但是,強(qiáng)命名參數(shù)可減少指定實(shí)參時(shí)的出錯(cuò)率。分頁查詢分頁查詢是很實(shí)用的查詢機(jī)制。使用原生 SQL 分頁查詢時(shí),需要自己構(gòu)建查詢 SQL 語句,不同的數(shù)據(jù)庫中的分頁查詢語句編寫也有差異性。Hibernate 通過其提供的分頁查詢功能很好地避開了這些問題。分頁查詢之前,先搞清楚幾個(gè)與查詢有關(guān)的參數(shù):pageSize: 每一頁大小;pageNum: 頁碼。假如數(shù)據(jù)庫中有 20 行數(shù)據(jù),分頁查詢時(shí)指定 pageSize 為 5,則每 5 條數(shù)據(jù)為一個(gè)邏輯頁,總共有 4 頁。如果要查詢第 3 頁數(shù)據(jù),即 pageNum=3。則需要跳過去的記錄數(shù)為:(pageNum-1)*pageSize=(3-1)*5=10 ,也就是從第 11 條數(shù)據(jù)開始查詢?,F(xiàn)在直接上實(shí)例代碼:String hql = "from Student s order by stuId" ;Query query = session.createQuery(hql);int pageNum=3;int pageSize=5;int passNum=(pageNum-1)*pageSize;query.setFirstResult(passNum);query.setMaxResults(pageSize);List<Student> stus = query.list();for (Student student : stus) { System.out.println(student.getStuName()); }return null;HIbernate 會(huì)從第 11 條記錄開始,查詢出 5 條記錄。針對(duì)不同的數(shù)據(jù)庫系統(tǒng),Hibernate 會(huì)給出最佳的 SQL 分頁方案。聯(lián)合查詢程序中所需要的數(shù)據(jù)可不一定在同一張表中,往往都是在多張表中。原生 SQL 通過多表連接或子查詢方式解決這個(gè)問題。使用 HQL 一樣能表達(dá)出多表連接的意圖??赡苣銜?huì)問:前面的一對(duì)一、一對(duì)多、多對(duì)多映射關(guān)聯(lián)關(guān)系后,不就已經(jīng)能夠查詢出多張表中的數(shù)據(jù)嗎。如下面表數(shù)據(jù):在學(xué)生類中采用立即查詢策略:@ManyToOne(targetEntity = ClassRoom.class, cascade = CascadeType.REMOVE,fetch=FetchType.EAGER)@JoinColumn(name = "classRoomId")public ClassRoom getClassRoom() { return classRoom;}查詢所有學(xué)生:String hql = "from Student s";Query query = session.createQuery(hql);List<Student> stus = query.list();System.out.println("-----------------------------");for (Student student : stus) {System.out.println("學(xué)生姓名:"+student.getStuName());System.out.println("班級(jí)名稱: "+student.getClassRoom().getClassRoomName()); }return null;不要懷疑,結(jié)果一定是會(huì)出現(xiàn)的。但是,可以看到控制臺(tái)輸出了很多 SQL 語句。那是因?yàn)?,Hibernate 會(huì)先查詢出所有學(xué)生,然后根據(jù)班級(jí) ID 再進(jìn)入班級(jí)表進(jìn)行查詢,這就是 Hibernate 查詢過程的 1+N 問題。可改成下面的關(guān)聯(lián)查詢方式:String hql = "select s.stuName,c.classRoomName from Student s,ClassRoom c where s.classRoom=c";Query query = session.createQuery(hql);List<Object[]> stus = query.list();System.out.println("-----------------------------");for (Object[] student : stus) { System.out.println("學(xué)生姓名:"+student[0]); System.out.println("班級(jí)名稱: "+student[1]); }return null;控制臺(tái)輸入結(jié)果:Hibernate: select student0_.stuName as col_0_0_, classroom1_.classRoomName as col_1_0_ from Student student0_ cross join ClassRoom classroom1_ where student0_.classRoomId=classroom1_.classRoomIdHibernate 僅構(gòu)建了一條 SQL 語句,直接查詢出來了所有數(shù)據(jù),看得出來,其性能要大于 1+N 方案。HQL 比想象中要簡單,比你預(yù)期的功能要強(qiáng)大。有了它,再也不怕查詢不到我們需要的數(shù)據(jù)。
無論是在 Java 或者 C++ 中都有函數(shù)重載一說,函數(shù)重載目的為了針對(duì)不同功能業(yè)務(wù)需求,然后暴露不同參數(shù)的接口,包括參數(shù)列表個(gè)數(shù),參數(shù)類型,參數(shù)順序。也就是說幾乎每個(gè)不同需求都得一個(gè)函數(shù)來對(duì)應(yīng),隨著以后的擴(kuò)展,這個(gè)類中的相同名字函數(shù)會(huì)堆成山,而且每個(gè)函數(shù)之間又存在層級(jí)調(diào)用,函數(shù)與函數(shù)之間的參數(shù)列表差別有時(shí)候也是細(xì)微的,所以在調(diào)用方也會(huì)感覺很疑惑。舉個(gè)例子(Android圖片加載框架我們都習(xí)慣于再次封裝一次,以便調(diào)用方便)fun ImageView.loadUrl(url: String) {//ImageView.loadUrl這個(gè)屬于擴(kuò)展函數(shù),后期會(huì)介紹,暫時(shí)可以先忽略 loadUrl(Glide.with(context), url)}fun ImageView.loadUrl(requestManager: RequestManager, url: String) { loadUrl(requestManager, url, false)}fun ImageView.loadUrl(requestManager: RequestManager, url: String, isCrossFade: Boolean) { ImageLoader.newTask(requestManager).view(this).url(url).crossFade(isCrossFade).start()}fun ImageView.loadUrl(urls: List<String>) { loadUrl(Glide.with(context), urls)}fun ImageView.loadUrl(requestManager: RequestManager, urls: List<String>) { loadUrl(requestManager, urls, false)}fun ImageView.loadUrl(requestManager: RequestManager, urls: List<String>, isCrossFade: Boolean) { ImageLoader.newTask(requestManager).view(this).url(urls).crossFade(isCrossFade).start()}fun ImageView.loadRoundUrl(url: String) { loadRoundUrl(Glide.with(context), url)}fun ImageView.loadRoundUrl(requestManager: RequestManager, url: String) { loadRoundUrl(requestManager, url, false)}fun ImageView.loadRoundUrl(requestManager: RequestManager, url: String, isCrossFade: Boolean) { ImageLoader.newTask(requestManager).view(this).url(url).crossFade(isCrossFade).round().start()}fun ImageView.loadRoundUrl(urls: List<String>) { loadRoundUrl(Glide.with(context), urls)}fun ImageView.loadRoundUrl(requestManager: RequestManager, urls: List<String>) { loadRoundUrl(requestManager, urls, false)}fun ImageView.loadRoundUrl(requestManager: RequestManager, urls: List<String>, isCrossFade: Boolean) { ImageLoader.newTask(requestManager).view(this).url(urls).crossFade(isCrossFade).round().start()}//調(diào)用的地方activity.home_iv_top_banner.loadUrl(bannerUrl)activity.home_iv_top_portrait.loadUrl(portraitUrls)activity.home_iv_top_avatar.loadRoundUrl(avatarUrl)activity.home_iv_top_avatar.loadRoundUrl(avatarUrls)
在上面的小節(jié)中講解了常見的異常類型,Python 中全部的標(biāo)準(zhǔn)的異常類型如下:異常名稱描述SystemExit解釋器請(qǐng)求退出KeyboardInterrupt用戶中斷執(zhí)行(通常是輸入^C)Exception常規(guī)錯(cuò)誤的基類StopIteration迭代器沒有更多的值GeneratorExit生成器(generator)發(fā)生異常來通知退出StandardError所有的內(nèi)建標(biāo)準(zhǔn)異常的基類ArithmeticError所有數(shù)值計(jì)算錯(cuò)誤的基類FloatingPointError浮點(diǎn)計(jì)算錯(cuò)誤OverflowError數(shù)值運(yùn)算超出最大限制ZeroDivisionError除(或取模)零AssertionError斷言語句失敗AttributeError對(duì)象沒有這個(gè)屬性EOFError沒有內(nèi)建輸入,到達(dá)EOFEnvironmentError操作系統(tǒng)錯(cuò)誤的基類IOError輸入/輸出操作失敗OSError操作系統(tǒng)錯(cuò)誤WindowsError系統(tǒng)調(diào)用失敗ImportError導(dǎo)入模塊/對(duì)象失敗LookupError無效數(shù)據(jù)查詢的基類IndexError序列中沒有此索引(index)KeyError映射中沒有這個(gè)鍵MemoryError內(nèi)存溢出錯(cuò)誤(對(duì)于PythonNameError未聲明/初始化對(duì)象UnboundLocalError訪問未初始化的本地變量ReferenceError弱引用(WeakRuntimeError一般的運(yùn)行時(shí)錯(cuò)誤NotImplementedError尚未實(shí)現(xiàn)的方法SyntaxErrorPythonIndentationError縮進(jìn)錯(cuò)誤TabErrorTabSystemError一般的解釋器系統(tǒng)錯(cuò)誤TypeError對(duì)類型無效的操作ValueError傳入無效的參數(shù)UnicodeErrorUnicodeUnicodeDecodeErrorUnicodeUnicodeEncodeErrorUnicodeUnicodeTranslateErrorUnicodeDeprecationWarning關(guān)于被棄用的特征的警告FutureWarning關(guān)于構(gòu)造將來語義會(huì)有改變的警告OverflowWarning舊的關(guān)于自動(dòng)提升為長整型(long)的警告PendingDeprecationWarning關(guān)于特性將會(huì)被廢棄的警告RuntimeWarning可疑的運(yùn)行時(shí)行為(runtimeSyntaxWarning可疑的語法的警告UserWarning用戶代碼生成的警告
TCP 是面向字節(jié)流的傳輸協(xié)議。所謂字節(jié)流是指 TCP 并不理解它所傳輸?shù)臄?shù)據(jù)的含義,在它眼里一切都是字節(jié),1 字節(jié)是 8 比特。比如,TCP 客戶端向服務(wù)器發(fā)送“Hello Server,I’m client。How are you?”,TCP 客戶端發(fā)送的是具有一定含義的數(shù)據(jù),但是對(duì)于 TCP 協(xié)議棧來說,傳輸?shù)氖且淮止?jié)流,具體如何解釋這段數(shù)據(jù)需要 TCP 服務(wù)器的應(yīng)用程序來完成,這就涉及到“應(yīng)用層協(xié)議設(shè)計(jì)”的問題。在 TCP/IP 協(xié)議棧的四層協(xié)議模型中,操作系統(tǒng)內(nèi)核協(xié)議棧實(shí)現(xiàn)了鏈路層、網(wǎng)絡(luò)層、傳輸層,將應(yīng)用層留給了應(yīng)用程序來實(shí)現(xiàn)。在編程實(shí)踐中,通常有文本協(xié)議和二進(jìn)制協(xié)議兩種類型,前者通常通過一個(gè)分隔符區(qū)分消息語義,而后者通常是需要通過一個(gè) length 字段指定消息體的大小。比如著名的 HTTP 協(xié)議就是文本協(xié)議,通過 “\r\n” 來區(qū)分 HTTP Header 的每一行。而 RTMP 協(xié)議是一個(gè)二進(jìn)制協(xié)議,通過 length 字段來指定消息體的大小。解析 TCP 字節(jié)流的語義通常叫做消息解析,如果按照傳統(tǒng) C 語言函數(shù)的方式來實(shí)現(xiàn),還是比較麻煩的,有很多細(xì)節(jié)需要處理。好在 Java 為我們提供了很多工具類,給我們的工作帶來了極大地便利。
ObjectOutputStream類下的void writeObject(Object obj)方法用于將一個(gè)對(duì)象寫入對(duì)象輸出流,也就是序列化;ObjectInputStream類下的Object readObject()方法用于讀取一個(gè)對(duì)象到輸入流,也就是反序列化。實(shí)例代碼如下:import java.io.*;public class SerializeDemo1 { static class Cat implements Serializable { private static final long serialVersionUID = 1L; private String nickname; private Integer age; public Cat() {} public Cat(String nickname, Integer age) { this.nickname = nickname; this.age = age; } @Override public String toString() { return "Cat{" + "nickname='" + nickname + '\'' + ", age=" + age + '}'; } } /** * 序列化方法 * @param filepath 文件路徑 * @param cat 要序列化的對(duì)象 * @throws IOException */ private static void serialize(String filepath, Cat cat) throws IOException { // 實(shí)例化file對(duì)象 File file = new File(filepath); // 實(shí)例化文件輸出流 FileOutputStream fileOutputStream = new FileOutputStream(file); // 實(shí)例化對(duì)象輸出流 ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); // 保存cat對(duì)象 objectOutputStream.writeObject(cat); // 關(guān)閉流 fileOutputStream.close(); objectOutputStream.close(); } /** * 反序列化方法 * @param filepath 文件路徑 * @throws IOException * @throws ClassNotFoundException */ private static void deserialize(String filepath) throws IOException, ClassNotFoundException { // 實(shí)例化file對(duì)象 File file = new File(filepath); // 實(shí)例化文件輸入流 FileInputStream fileInputStream = new FileInputStream(file); // 實(shí)例化對(duì)象輸入流 ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); Object o = objectInputStream.readObject(); System.out.println(o); } public static void main(String[] args) throws IOException, ClassNotFoundException { String filename = "C:\\Users\\Colorful\\Desktop\\imooc\\Hello.txt"; Cat cat = new Cat("豬皮", 1); serialize(filename, cat); deserialize(filename); }}運(yùn)行結(jié)果:Cat{nickname='豬皮', age=1}上述代碼中,我們定義了一個(gè)Cat類,它實(shí)現(xiàn)了Serializable接口,類內(nèi)部有一個(gè)private static final long serialVersionUID = 1L;,關(guān)于這兩點(diǎn),我們下面緊接著就會(huì)介紹。除了Cat類的定義,我們還分別封裝了序列化與反序列化的方法,并在主方法中調(diào)用了這兩個(gè)方法,實(shí)現(xiàn)了cat對(duì)象的序列化和反序列化操作。在調(diào)用序列化方法后,你會(huì)發(fā)現(xiàn)磁盤中的Hello.txt文件中被cat對(duì)象寫入了序列化后的數(shù)據(jù):
Zookeeper 與 Redis 分布式鎖比較除了 Zookeeper 可以實(shí)現(xiàn)分布式鎖之外,我們還可以使用高性能緩存技術(shù) Redis 來實(shí)現(xiàn),我們來比較一下它們的優(yōu)缺點(diǎn)。分布式鎖優(yōu)點(diǎn)缺點(diǎn)Zookeeper1. 功能已經(jīng)封裝,實(shí)現(xiàn)簡單 2. 有等待鎖的隊(duì)列,提升了搶鎖的效率添加和刪除節(jié)點(diǎn)性能較低RedisSet 和 Del 指令的性能高1. 實(shí)現(xiàn)較復(fù)雜,需要考慮超時(shí)、原子性、誤刪的情況 2. 沒有等鎖隊(duì)列,只能通過客戶端自旋來等鎖,效率低下Zookeeper 與 Eureka 的比較Eureka 是 Spring Cloud 微服務(wù)架構(gòu)的分布式注冊(cè)中心。在進(jìn)行比較之前,我們來了解一下 CAP 定理。什么是 CAP 定理呢?C : Consistent ,一致性,需要保證數(shù)據(jù)的一致性。A : Availability ,可用性,需要保證服務(wù)的可用性。P : Partition tolerance ,分區(qū)容錯(cuò)性,服務(wù)對(duì)網(wǎng)絡(luò)分區(qū)故障的容錯(cuò)性。在 CAP 這個(gè)定理中,任何分布式系統(tǒng)都只能保證其中兩條。那么 Zookeeper 和 Eureka 又是保證的哪兩條呢?分布式注冊(cè)中心優(yōu)點(diǎn)缺點(diǎn)Zookeeper (保證 CP)新的服務(wù)注冊(cè)時(shí)會(huì)同步到其他節(jié)點(diǎn),保證了節(jié)點(diǎn)數(shù)據(jù)的一致性為了保證一致性,在 Leader 選舉階段服務(wù)不能注冊(cè),失去了可用性Eureka (保證 AP)Eureka 的各個(gè)節(jié)點(diǎn)是平等的,只要有一個(gè)節(jié)點(diǎn)存在,就可以提供服務(wù)新服務(wù)注冊(cè)時(shí),不會(huì)把數(shù)據(jù)同步到其他節(jié)點(diǎn)上,失去了數(shù)據(jù)一致性
Centos操作系統(tǒng)中進(jìn)程的狀態(tài)有五種:運(yùn)行:正在運(yùn)行或在運(yùn)行隊(duì)列中等待;中斷:休眠中,受阻,在等待某個(gè)條件的形成或接受到信號(hào);不可中斷:收到信號(hào)不喚醒和不可運(yùn)行, 進(jìn)程必須等待直到有中斷發(fā)生;僵死:進(jìn)程已終止, 但進(jìn)程描述符存在, 直到父進(jìn)程調(diào)用 wait4() 系統(tǒng)調(diào)用后釋放;停止:進(jìn)程收到SIGSTOP,SIGSTP,SIGTIN,SIGTOU 信號(hào)后停止運(yùn)行運(yùn)行。ps 命令可以用來查看進(jìn)程相關(guān)信息,下面列舉一些 ps 命令的參數(shù):ps 命令參數(shù)名稱功能與作用描述-a顯示現(xiàn)行終端機(jī)下的所有程序,包括其他用戶的程序。-A顯示所有程序。-c列出程序時(shí),顯示每個(gè)程序真正的指令名稱,而不包含路徑,參數(shù)或常駐服務(wù)的標(biāo)示。-e此參數(shù)的效果和指定 A 參數(shù)相同。 例如: ps -ee列出程序時(shí),顯示每個(gè)程序所使用的環(huán)境變量。f用ASCII字符顯示樹狀結(jié)構(gòu),表達(dá)程序間的相互關(guān)系。-H顯示樹狀結(jié)構(gòu),表示程序間的相互關(guān)系。-N顯示所有的程序,除了執(zhí)行ps指令終端機(jī)下的程序之外。-s采用程序信號(hào)的格式顯示程序狀況。-S列出程序時(shí),包括已中斷的子程序資料。-u以用戶為主的格式來顯示程序狀況。-x顯示所有程序,不以終端機(jī)來區(qū)分。Tips:使用上述一個(gè)或者多個(gè)參數(shù)可以查看到指定的進(jìn)程信息。
那么 JVM 作為一部獨(dú)立運(yùn)行的機(jī)器,它的字節(jié)序又是如何呢?通過 Java 程序測(cè)試字節(jié)序的思路和 C 程序的一致,代碼片段如下: public static void checkEndian() { int x = 0xAABBCCDD; ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES); buffer.putInt(x); byte[] lbytes = buffer.array(); for (byte b : lbytes){ System.out.printf("%X\n", b); } }關(guān)于 JAVA 程序需要說明的是 JAVA 中沒有指針的概念,所以不能通過取地址的方式直接打印內(nèi)存的值。需要借助 JAVA 的 ByteBuffer,將 int 型數(shù)值存儲(chǔ)到 ByteBuffer 中,然后將 ByteBuffer 轉(zhuǎn)換成字節(jié)數(shù)組,通過打印數(shù)組的方式來達(dá)到我們的目的。引用 ByteBuffer 需要通過語句 import java.nio.ByteBuffer; 導(dǎo)入ByteBuffer 類。JAVA 測(cè)試結(jié)果:AABBCCDD從輸出結(jié)果可以看出 ByteBuffer 默認(rèn)是以大端序來存儲(chǔ)整數(shù)的,因?yàn)?Java 虛擬機(jī)本身采用的就是大端序,ByteBuffer 也要和整個(gè)系統(tǒng)保持一致。當(dāng)然,ByteBuffer 也提供了 ByteBuffer order() 和 ByteBuffer order(ByteOrder bo) 方法,用來獲取和設(shè)置 ByteBuffer 的字節(jié)序。另外,像一些多字節(jié) Buffer,如 IntBuffer、LongBuffer,它們的字節(jié)序規(guī)則如下:如果多字節(jié) Buffer 是通過數(shù)組(Array)創(chuàng)建的,那么它的字節(jié)序和底層系統(tǒng)的字節(jié)序一致。如果多字節(jié) Buffer 是通過 ByteBuffer 創(chuàng)建的,那么它的字節(jié)序和 ByteBuffer 的字節(jié)序一致。測(cè)試程序如下: public static void checkByteBuffer(){ ByteBuffer byteBuffer = ByteBuffer.allocate(Long.BYTES); long [] longNumber = new long[]{ 0xAA,0xBB,0xCC,0xDD }; LongBuffer lbAsArray = LongBuffer.wrap(longNumber); System.out.println("The byte order for LongBuffer wrap array: " + lbAsArray.order()); LongBuffer lbAsByteBuffer = byteBuffer.asLongBuffer(); System.out.println("The byte order for LongBuffer from ByteBuffer: " + lbAsByteBuffer.order()); }執(zhí)行結(jié)果:The byte order for LongBuffer wrap array: LITTLE_ENDIANThe byte order for LongBuffer from ByteBuffer: BIG_ENDIAN如果在上面的 checkByteBuffer 方法中,首先將對(duì)象 byteBuffer 的字節(jié)序設(shè)置為 ByteOrder.LITTLE_ENDIAN(通過 ByteBuffer 的 order 方法設(shè)置),然后再創(chuàng)建 lbAsByteBuffer 對(duì)象,那么 lbAsByteBuffer 的字節(jié)序該是什么呢?
add () 函數(shù)根據(jù)索引值,對(duì)相同索引的數(shù)據(jù)進(jìn)行加法運(yùn)算,注意字符串的加法是拼接操作。# 創(chuàng)建 Series 對(duì)象new_series = pd.Series(["11","22",33,"44"], index=['編程語言','推出時(shí)間','價(jià)格','last'])print(new_series)# --- 輸出結(jié)果 ---編程語言 11推出時(shí)間 22價(jià)格 33last 44dtype: object# data 為從 Excel 解析出的 DataFrame 對(duì)象# 通過 add 函數(shù)進(jìn)行加運(yùn)算new_result=data.add(new_series)print(new_result)# --- 輸出結(jié)果 --- last 主要?jiǎng)?chuàng)始人 價(jià)格 推出時(shí)間 編程語言0 NaN NaN 78.6 1995年22 java111 NaN NaN 100 1991年22 python112 NaN NaN 66.9 1972年22 C113 NaN NaN 92.5 1995年22 js114 NaN NaN 102.9 2012年22 php115 NaN NaN 108 1983年22 C++11輸出解析:new_series 對(duì)象中的 “l(fā)ast” 索引和 data 對(duì)象中 “主要?jiǎng)?chuàng)始人” 索引,他們之間不存在對(duì)應(yīng)關(guān)系,而其他的索引是一一對(duì)應(yīng)的,因此輸出結(jié)果其他的列均做了加法運(yùn)算 (字符串是拼接,數(shù)值是相加),而這兩列則單獨(dú)加入到結(jié)果集中,并且用 NaN 進(jìn)行填充。如果我們的 Series 對(duì)象中的 “價(jià)格” 是字符型,在和 DataFrame 對(duì)象運(yùn)算過程中就會(huì)報(bào)類型不匹配的錯(cuò)誤。# 創(chuàng)建 Series 對(duì)象new_series = pd.Series(["11","22","33","44"], index=['編程語言','推出時(shí)間','價(jià)格','last'])print(new_series)# --- 輸出結(jié)果 ---編程語言 11推出時(shí)間 22價(jià)格 33last 44dtype: object # data 為從 Excel 解析出的 DataFrame 對(duì)象# 通過 add 函數(shù)進(jìn)行加運(yùn)算new_result=data.add(new_series)print(new_result)# --- 輸出結(jié)果 ---……TypeError: unsupported operand type(s) for +: 'float' and 'str'輸出解析:因?yàn)槲覀兊牟僮魇窃?data 數(shù)據(jù)集中加上 new_series 數(shù)據(jù)集,在 Pandas 中 str 類型加上任何類型相當(dāng)于是字符串的拼接操作,而 int/float 類型值加上字符串,Pandas 不支持該算術(shù)運(yùn)算,因此會(huì)報(bào)錯(cuò)。
LinkedList 是一個(gè)以雙向鏈表實(shí)現(xiàn)的List。和ArrayList一樣,也按照索引位置排序,但它的元素是雙向連接的,因此順序訪問的效率非常高,而隨機(jī)訪問的效率比較低。3.4.1 構(gòu)造方法LinkedList():構(gòu)造一個(gè)空列表;LinkedList(Collection<? extends E> c):構(gòu)造一個(gè)包含指定集合元素的列表,其順序由集合的迭代器返回。3.4.2 常用成員方法void add(E e):將指定的元素追加到此列表的末尾;void add(int index, E element):將指定的元素插入此列表中的指定位置;void addFirst(E e):將指定的元素插入此列表的開頭;vod addLast(E e):將指定的元素添加到此列表的結(jié)尾;E remove(int index):刪除此列表中指定位置的元素;boolean remove(Object o):如果存在指定元素,則從該列表中刪除第一次出現(xiàn)的該元素;void clear():從此列表中刪除所有元素;E set(int index, E element):用指定的元素替換此列表中指定位置的元素;E get(int index):返回此列表中指定位置的元素;E getFirst():返回此列表的第一個(gè)元素;E getLast():返回此列表的最后一個(gè)元素;boolean contains(Object o):如果此列表包含指定的元素,則返回 true,否則返回 false;int size():返回該列表中元素的數(shù)量;Object[] toArray():以正確的順序(從第一個(gè)元素到最后一個(gè)元素)返回一個(gè)包含此列表中所有元素的數(shù)組。更多成員方法請(qǐng)翻閱官方文檔,對(duì)于成員方法的使用,與ArrayList大同小異,這里不再贅述。
在通常情況下,定義函數(shù)時(shí),函數(shù)的參數(shù)個(gè)數(shù)是預(yù)先確定的。例如,編寫計(jì)算兩個(gè)數(shù)相加的函數(shù) add(a, b),代碼如下:def add(a, b): return a + bsum = add(1, 2) 在第 1 行,定義了函數(shù) add,函數(shù)有兩個(gè)參數(shù),第 1 個(gè)參數(shù)是 a,第 2 個(gè)參數(shù)是 b在第 2 行,計(jì)算參數(shù) a 和 參數(shù) b 的累加和,通過 return 語句將計(jì)算結(jié)果返回給調(diào)用者在第 4 行,通過 add(1, 2) 調(diào)用函數(shù) add將整數(shù) 1 傳遞給第 1 個(gè)參數(shù) a將整數(shù) 2 傳遞給第 2 個(gè)參數(shù) b傳入的兩個(gè)整數(shù)按照位置順序依次賦給函數(shù)的參數(shù) a 和 b,參數(shù) a 和參數(shù) b 被稱為位置參數(shù)。在 Python 中,調(diào)用函數(shù)時(shí),根據(jù)函數(shù)定義的參數(shù)位置來傳遞參數(shù),要求傳遞的參數(shù)與函數(shù)定義的參數(shù)兩者一一對(duì)應(yīng),如果 “傳遞的參數(shù)個(gè)數(shù)” 不等于 “函數(shù)定義的參數(shù)個(gè)數(shù)”,運(yùn)行時(shí)會(huì)報(bào)錯(cuò),例如:def add(a, b): return a + bsum = add(1, 2, 3)在第 1 行,定義了函數(shù) add,函數(shù)有 2 個(gè)參數(shù)在第 4 行,通過 add(1, 2, 3) 調(diào)用函數(shù) add,傳遞了 3 個(gè)參數(shù)因?yàn)?“傳遞的參數(shù)個(gè)數(shù)” 不等于 “函數(shù)定義的參數(shù)個(gè)數(shù)”,運(yùn)行時(shí)報(bào)錯(cuò)如下:C:\> python add.pyTraceback (most recent call last): File "error.py", line 4, in <module> sum = add(1, 2, 3)TypeError: add() takes 2 positional arguments but 3 were given在第 5 行,運(yùn)行時(shí)提示:函數(shù) add 有 2 個(gè)位置參數(shù),但是調(diào)用時(shí)傳遞了 3 個(gè)位置參數(shù)。
面試官提問: MySQL 中事務(wù)的特性是什么?題目解析:ACID 是衡量事務(wù)的 4 個(gè)維度,分別的定義是:(1)原子性(Atomic,簡寫 A):原子性要求事務(wù)是一個(gè)不可分割的執(zhí)行單位,如果一個(gè)事務(wù)包含多條 SQL 語句,要么所有的 SQL 都執(zhí)行成功,要么所有的 SQL 都執(zhí)行失敗,不存在兩者之間的中間狀態(tài)。如果事務(wù)中的任意一條 SQL 執(zhí)行失敗,那么已執(zhí)行成功的也需要回滾。MySQL 中 InnoDB 引擎利用 undo log 實(shí)現(xiàn)原子性,undo log 記錄了所有已執(zhí)行的 SQL 記錄,如果事務(wù)執(zhí)行失敗調(diào)用了 rollback 語句,那么使用 undo log 的記錄回滾已執(zhí)行的 SQL。(2)持久性(Durability,簡寫 D):持久性要求事務(wù)一旦提交(commit),對(duì)數(shù)據(jù)庫的改變就應(yīng)該是永久的,其他的操作不會(huì)對(duì)已提交的事務(wù)有影響。InnoDB 引擎中使用 redo log 實(shí)現(xiàn)持久性,如果 MySQl 服務(wù)器宕機(jī),那么在重啟時(shí)可以讀取 redo log 中的記錄恢復(fù)數(shù)據(jù)庫。(3)隔離性(Isolation,簡寫 I):要求一個(gè)事務(wù)的執(zhí)行不受到其他并發(fā)執(zhí)行事務(wù)的影響。(4)一致性(Consistency,簡寫 C):事務(wù)將數(shù)據(jù)庫從一種狀態(tài)轉(zhuǎn)換到另一種狀態(tài),但是兩種狀態(tài)從數(shù)據(jù)上是一致的。例如用戶下單扣庫存讓庫存減少了一個(gè)單位,那么在訂單中就會(huì)增加一個(gè)單位的商品,庫存和訂單中的商品數(shù)量和是不會(huì)改變的。
操作系統(tǒng)協(xié)議棧支持的 Socket 選項(xiàng)參數(shù)有很多,匯總起來如下圖所示:從圖中可以看出,Socket 選項(xiàng)按照級(jí)別進(jìn)行分類,級(jí)別有很多種,但是總結(jié)起來分兩類:通用 Socket 級(jí)別的選項(xiàng)。枚舉值為 SOL_SOCKET。協(xié)議相關(guān)的選項(xiàng)。協(xié)議棧為我們提供了控制所有協(xié)議的選項(xiàng),比如 IP、IPv6、TCP、UDP、ICMP 等。枚舉值的格式為 IPPROTO_XXX,XXX 代表協(xié)議。每一種選項(xiàng)級(jí)別下面包含了很多選項(xiàng)參數(shù)。比如,通用 Socket 選項(xiàng)的級(jí)別枚舉值是 SOL_SOCKET,其下面包含 SO_RCVBUF 和 SO_SNDBUF 選項(xiàng)參數(shù);IP 協(xié)議選項(xiàng)的級(jí)別的枚舉值是 IPPROTO_IP,其下面包含 IP_TTL、IP_TOS 等選項(xiàng)參數(shù)。在 Linux 系統(tǒng)下,所有的選項(xiàng)參數(shù)都可以在幫助手冊(cè)里面查找,具體方法如下:通用 Socket 級(jí)別選項(xiàng)參數(shù)man 7 socketIP 協(xié)議級(jí)別選項(xiàng)參數(shù):man 7 ipIPv6 協(xié)議級(jí)別選項(xiàng)參數(shù):man 7 ipv6TCP 協(xié)議級(jí)別選項(xiàng)參數(shù):man 7 tcpUDP 協(xié)議級(jí)別選項(xiàng)參數(shù):man 7 udpSocket 選項(xiàng)參數(shù)最終是如何設(shè)置到協(xié)議棧的呢?協(xié)議棧提供了 getsockopt() 和 setsockopt() 兩個(gè) C 語言函數(shù),分別用于獲取和設(shè)置選項(xiàng)參數(shù)。調(diào)用兩個(gè)函數(shù)所需要包含的頭文件,以及他們的聲明如下:#include <sys/types.h> #include <sys/socket.h>int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);如果你對(duì)系統(tǒng)本身的 Socket 選項(xiàng)感興趣,可以通過 man 查找相關(guān)幫助。本節(jié)重點(diǎn)介紹通用 Socket 選項(xiàng)。
在上面的表格中, 你會(huì)發(fā)現(xiàn)大量出現(xiàn)Cgroup 中的進(jìn)程這個(gè)描述,原因在于我們必須先掛載子系統(tǒng),將進(jìn)程納入 Cgroup 組規(guī)則中,然后 Cgroup 機(jī)制才能控制這個(gè)進(jìn)程。我們馬上上手嘗試一下,先安裝stresssudo dnf install https://download-ib01.fedoraproject.org/pub/epel/7/x86_64/Packages/s/stress-1.0.4-16.el7.x86_64.rpmTips:我們使用stress 的軟件進(jìn)行壓力負(fù)載測(cè)試,stress 會(huì)根據(jù)設(shè)定,執(zhí)行一系列消耗系統(tǒng)資源的操作,使得系統(tǒng)在一定的負(fù)載下運(yùn)行。啟動(dòng)一個(gè)壓力負(fù)載測(cè)試的進(jìn)程:# 產(chǎn)生 1個(gè)進(jìn)程,每個(gè)進(jìn)程都反復(fù)不停地計(jì)算隨機(jī)數(shù)的平方根stress -c 1 > /dev/null &使用top 命令查看資源占用情況圖中兩個(gè)紅框從左到右分別代表壓力測(cè)試的進(jìn)程號(hào)是 1816,CPU 占用百分比 99%,說明壓力測(cè)試進(jìn)程正常生效了。切換到 root 用戶:sudo su - root進(jìn)入 /sys/fs/cgroup/cpu 這個(gè)目錄:cd /sys/fs/cgroup/cpu創(chuàng)建一個(gè) Cgroup 組,即在當(dāng)前目錄下創(chuàng)建一個(gè)子目錄:mkdir testcpu進(jìn)入 testcpu 目錄:cd testcpu查看 cpu.cfs_period_us 代表時(shí)間周期總長度:cat cpu.cfs_period_us # 100000將 cpu.cfs_quota_us 設(shè)為當(dāng)前 cgroup 在設(shè)置的周期長度內(nèi)所能使用的 CPU 時(shí)間 10000,即總周期100000的 10%:echo 10000 > cpu.cfs_quota_us 將壓力測(cè)試的進(jìn)程添加到 Cgroup 組的規(guī)則中,1816 是stress進(jìn)程 id:echo 1816 > taskstop 查看資源占用,發(fā)現(xiàn) stress 進(jìn)程的 CPU 占用率被壓到了 10%,說明 Cgroups 對(duì)于 CPU 的控制起了效果。
WEB 程序整體上是 B / S 結(jié)構(gòu),本質(zhì)上是基于底層網(wǎng)絡(luò)的程序,是 C / S 結(jié)構(gòu)體系的升級(jí)。回憶一下網(wǎng)絡(luò)編程的幾個(gè)基本要素:服務(wù)器端建立監(jiān)聽端口;客戶端向監(jiān)聽端口發(fā)起網(wǎng)絡(luò)連接;連接成功后建立起雙向的網(wǎng)絡(luò)流傳輸通道;彼此之間通過網(wǎng)絡(luò)流通道進(jìn)行數(shù)據(jù)交流。程序的本質(zhì)就是解決數(shù)據(jù)從哪里來、如何處理數(shù)據(jù)以及數(shù)據(jù)要去哪里的問題?;诰W(wǎng)絡(luò)的程序也是如此,其數(shù)據(jù)的來來往往是通過 IO 流實(shí)現(xiàn)的。WEB 程序也是網(wǎng)絡(luò)程序,數(shù)據(jù)的來去也是通過 IO 流實(shí)現(xiàn)??赡苣銜?huì)說,使用原生 Servlet 規(guī)范開發(fā) WEB 項(xiàng)目時(shí),你從沒有接觸或使用過這樣的流、哪樣的流。那是因?yàn)闊o論你使用原生 Servlet API 還是使用 Spring MVC 開發(fā) WEB ,兩者已經(jīng)幫你隱藏了其中的細(xì)節(jié)。HttpServletRequest 其本質(zhì)就是封裝網(wǎng)絡(luò)輸入流的操作。如果你不信,你可以查看 HttpServletRequest 的相關(guān)方法,會(huì)看到:InputStream inStream=request.getInputStream();觸類旁通,此時(shí)你應(yīng)該能理解到 HttpServletResponse 封裝了網(wǎng)絡(luò)輸出流的相關(guān)操作。OutputStream outputStream= response.getOutputStream();那么,在 Spring MVC 中我們能不能在控制器的方法中直接引用底層的 IO 流對(duì)象?當(dāng)然可以!TIps: Spring MVC 既可以把原生的 Servlet API 對(duì)象注入到控制器的方法中,也可以把更底層的 IO 流對(duì)象注入到控制器方法中。
是否還記得上一篇文章開頭的那個(gè)例子和那幅漫畫圖:對(duì)于協(xié)變的理解,例子代碼如下:fun main(args: Array<String>) { val stringList: List<String> = listOf("a", "b", "c", "d") val intList: List<Int> = listOf(1, 2, 3, 4) printList(stringList)//向函數(shù)傳遞一個(gè)List<String>函數(shù)實(shí)參,也就是這里L(fēng)ist<String>是可以替換List<Any> printList(intList)//向函數(shù)傳遞一個(gè)List<Int>函數(shù)實(shí)參,也就是這里L(fēng)ist<Int>是可以替換List<Any>}fun printList(list: List<Any>) {//注意:List是協(xié)變的,這里函數(shù)形參類型是List<Any>,函數(shù)內(nèi)部是不知道外部傳入是List<Int>還是List<String>,全部當(dāng)做List<Any>處理 list.forEach { println(it) }}理解:對(duì)于 printList 函數(shù)而言,它需要的是 List<Any> 類型是個(gè)相對(duì)具體類型更加泛化的類型,且在函數(shù)內(nèi)部的操作不會(huì)涉及到修改寫操作,然后在外部傳入一個(gè)更為具體的子類型肯定是滿足要求的泛化類型最基本需求。所以外部傳入更為具體子類型 List<String>、List<Int> 的兼容性更好。對(duì)于逆變的理解,例子代碼如下:class A<in T>{ fun doAction(t: T){ ... }}fun main(args: Array<String>) { val intA = A<Int>() val anyA = A<Any>() doSomething(intA)//不合法, doSomething(anyA)//合法}fun doSomething(a: A<Number>){//在doSomething外部不能傳入比A<Number>更為具體的類型,因?yàn)樵诤瘮?shù)內(nèi)部涉及寫操作. ....}理解:對(duì)于 doSomething,它需要的 A<Number> 是個(gè)相對(duì)泛化類型更加具體的類型,由于泛型類 A 逆變的,函數(shù)內(nèi)部的操作放開寫操作權(quán)限,試著想下在 doSomething 函數(shù)外部不能傳入比他更為具體的比較器對(duì)象了,因?yàn)橹灰斜?A<Number> 更為具體的,就會(huì)出問題,利用反證法來理解下,假如傳入 A<Int> 類型是合法的,那么在內(nèi)部函數(shù)還是當(dāng)做 A<Number>, 在函數(shù)內(nèi)部寫操作時(shí)候很有可能把它往里面寫入一個(gè) Float 類型的數(shù)據(jù),因?yàn)橥?Number 類型寫入 Float 類型是很合法的,但是外部實(shí)際上傳入的是 A<Int>,往 A<Int> 寫 Float 類型不出問題才怪呢,所以原假設(shè)不成立。所以逆變放開了寫權(quán)限,那么對(duì)于外部傳入的類型要求就更加嚴(yán)格了。引出另一個(gè)問題,為什么逆變寫操作是安全的呢? 細(xì)想也是很簡單的,對(duì)于逆變泛型類型作為函數(shù)形參的類型,那么在函數(shù)外部的傳入實(shí)參類型就一定要比函數(shù)形參的類型更泛化不能更具體,所以在函數(shù)內(nèi)部操作的最具體的類型也就是函數(shù)形參類型,所以肯定可以大膽寫操作啊。就比如 A<Number> 類型形參類型,在 doSomething 函數(shù)中明確知道外部不能比它更為具體,所以在函數(shù)內(nèi)部大膽在 A<Number> 基礎(chǔ)上寫操作是可以的。對(duì)于不變的理解,例子代碼如下:fun main(args: Array<String>) { val stringList: MutableList<String> = mutableListOf("a", "b", "c", "d") val intList: MutableList<Int> = mutableListOf(1, 2, 3, 4) printList(stringList)//這里實(shí)際上是編譯不通過的 printList(intList)//這里實(shí)際上是編譯不通過的}fun printList(list: MutableList<Any>) { list.add(3.0f)//開始引入危險(xiǎn)操作dangerous! dangerous! dangerous! list.forEach { println(it) }}理解:不變實(shí)際上就更好理解了,因?yàn)椴淮嬖谧宇愋突P(guān)系,沒有所謂的子類型 A 的值在任何地方任何時(shí)候可以替換超類型 B 的值的規(guī)則,所以上述例子編譯不過,對(duì)于 printList 函數(shù)而言必須接收的類型是 MutableList<Any>,因?yàn)橐坏﹤魅牒退灰粯拥木唧w類型就會(huì)存在危險(xiǎn)操作,出現(xiàn)不安全的問題。
互聯(lián)網(wǎng)行業(yè)因?yàn)閺V為人知的高薪以及相對(duì)于傳統(tǒng)工科行業(yè)更多的發(fā)展機(jī)會(huì),最近幾年涌入了越來越多的非計(jì)算機(jī)專業(yè)畢業(yè)的從業(yè)人員,校招 / 社招面試的時(shí)候,候選人往往也會(huì)被分為兩種:科班和非科班,互聯(lián)網(wǎng)科班一般特指大學(xué)就讀計(jì)算機(jī)科學(xué)與技術(shù)或者軟件專業(yè),非科班則包含其他各大傳統(tǒng)工科甚至是文科專業(yè)。某些大廠在招聘后端開發(fā)工程師時(shí)會(huì)嚴(yán)格要求科班背景,因?yàn)閷?duì)于非科班的同學(xué),一般都能勝任計(jì)算機(jī)網(wǎng)絡(luò)應(yīng)用層以上的工作(例如編寫一個(gè)低并發(fā)的后臺(tái)管理系統(tǒng)),但是對(duì)于計(jì)算機(jī)底層的知識(shí)往往是一片盲區(qū)。當(dāng)對(duì)計(jì)算機(jī)網(wǎng)絡(luò)了解甚少的非科班同學(xué)遇到線上問題時(shí),或者網(wǎng)絡(luò)通信相關(guān)的運(yùn)維故障,往往會(huì)束手無策。所以了解計(jì)算機(jī)底層如何運(yùn)作是非常有必要的,我們這里談到的計(jì)算機(jī)底層知識(shí),包括但不限于:計(jì)算機(jī)組成:CPU 運(yùn)行的原理,內(nèi)存、硬盤等各種硬件如何協(xié)調(diào)合作;操作系統(tǒng):支撐后端框架的系統(tǒng),具體做了哪些操作;編譯原理:對(duì)于 Java 、C++ 這類高級(jí)語言,如何經(jīng)過編譯,轉(zhuǎn)換為匯編語言以及二進(jìn)制文件;計(jì)算機(jī)網(wǎng)絡(luò):計(jì)算機(jī)與計(jì)算機(jī)之間如何進(jìn)行通信。從本小節(jié)開始,我們會(huì)開始學(xué)習(xí)計(jì)算機(jī)網(wǎng)絡(luò)相關(guān)的面試題目,并且在熟悉題目的同時(shí),掌握計(jì)算機(jī)網(wǎng)絡(luò)的基本知識(shí)框架。
實(shí)現(xiàn)腳本#!/bin/bash# Description: count file scripts# Auth: kaliarch# Email: kaliarch@163.com# function: count file# Date: 2020-03-29 14:00# Version: 1.0[ $(id -u) -gt 0 ] && exit 1# cpu使用超過百分之多少進(jìn)行限制PEC_CPU=80# 限制進(jìn)程使用百分之多少,如果程序?yàn)槎嗑€程,單個(gè)cpu限制為85,如果為多核心,就需要按照比例寫,例如cpu為2c,像限制多線程占比80%,就寫170LIMIT_CPU=85# 日志LOG_DIR=/var/log/cpulimit/# 超過閥值進(jìn)程pidPIDARG=$(ps -aux |awk -v CPU=${PEC_CPU} '{if($3 > CPU) print $2}')# 安裝cpulimit 函數(shù)install_cpulimit() { [ ! -d /tmp ] && mkdir /tmp || cd /tmp wget -c https://github.com/opsengine/cpulimit/archive/v0.2.tar.gz tar -zxf v0.2.tar.gz cd cpulimit-0.2 && make [ $? -eq 0 ] && cp src/cpulimit /usr/bin/}# 執(zhí)行cpulimitdo_cpulimit() {[ ! -d ${LOG_DIR} ] && mkdir -p ${LOG_DIR}for i in ${PIDARG};do CPULIMITCMD=$(which cpulimit) MSG=$(ps -aux |awk -v pid=$i '{if($2 == pid) print $0}') echo ${MSG} [ ! -d /tmp ] && mkdir /tmp || cd /tmp nohup ${CPULIMITCMD} -p $i -l ${LIMIT_CPU} & echo "$(date) -- ${MSG}" >> ${LOG_DIR}$(date +%F).logdone}# 主函數(shù)main() { hash cpulimit if [ $? -eq 0 ];then # 調(diào)用函數(shù) do_cpulimit else install_cpulimit && do_cpulimit fi}main測(cè)試需編寫 CPU 壓力測(cè)試 python 腳本,后期可以放入計(jì)劃任務(wù)來監(jiān)控并限制 CPU 使用率。#!/bin/env pythonimport mathimport randoma=10000b=10000c=10000sum=0for i in range(0,a): for j in range(0,b): randomfloat=random.uniform(1,10) randompow=random.uniform(1,10) sum+=math.pow(randomfloat, randompow)print "sum is %s" % sum當(dāng)我們執(zhí)行 python 的 CPU 壓力測(cè)試腳本后,發(fā)現(xiàn)單核服務(wù) CPU 跑到了 100%。之后運(yùn)行我們的 CPU 限制腳本,可以看到 CPU 被成功限制。在此案例中,我們著重來看 Shell 函數(shù),在實(shí)例中我們編寫了安裝與執(zhí)行 CPU 限制兩個(gè)函數(shù),最后編寫主函數(shù),在主函數(shù)中調(diào)用其他函數(shù),配合定時(shí)任務(wù)就能達(dá)到限制某進(jìn)程的 CPU。
ArrayList 可以理解為動(dòng)態(tài)數(shù)組,它的容量可以動(dòng)態(tài)增長。當(dāng)添加元素時(shí),如果發(fā)現(xiàn)容量已滿,會(huì)自動(dòng)擴(kuò)容為原始大小的 1.5 倍。3.2.1 構(gòu)造方法ArrayList():構(gòu)造一個(gè)初始容量為 10 的空列表;ArrayList(int initialCapacity):構(gòu)造一個(gè)指定容量的空列表;ArrayList(Collection<? extends E> c):構(gòu)造一個(gè)包含指定集合元素的列表,其順序由集合的迭代器返回。在代碼中,我們可以這樣實(shí)例化ArrayList對(duì)象:// 無參構(gòu)造實(shí)例化,初始容量為10List arrayList1 = new ArrayList();// 實(shí)例化一個(gè)初始容量為20的空列表List arrayList2 = new ArrayList(20);// 實(shí)例化一個(gè)集合元素為 arrayList2 的列表(由于 arrayList2 為空列表,因此其實(shí)例化的對(duì)象也為空列表)List arrayList3 = new ArrayList(arrayList2);3.2.2 常用成員方法void add(E e):將指定的元素追加到此列表的末尾;void add(int index, E element):將指定的元素插入此列表中的指定位置;E remove(int index):刪除此列表中指定位置的元素;boolean remove(Object o):如果存在指定元素,則從該列表中刪除第一次出現(xiàn)的該元素;void clear():從此列表中刪除所有元素;E set(int index, E element):用指定的元素替換此列表中指定位置的元素;E get(int index):返回此列表中指定位置的元素;boolean contains(Object o):如果此列表包含指定的元素,則返回 true,否則返回 false;int size():返回該列表中元素的數(shù)量;Object[] toArray():以正確的順序(從第一個(gè)元素到最后一個(gè)元素)返回一個(gè)包含此列表中所有元素的數(shù)組。更多成員方法請(qǐng)翻閱官方文檔,下面我們將結(jié)合實(shí)例來介紹以上成員方法的使用。
1. 定義變量 def在 Groovy 中,我們除了指定類型定義變量外,還可以使用def關(guān)鍵字來定義變量,變量是由數(shù)字、字母、下劃線組成。但是它不能以數(shù)字開頭。我們看下下面一段代碼://定義一個(gè)整型變量def i = 5;println(i.class)//定義一個(gè)字符串def _name = "Groovy語言基礎(chǔ)"println(_name.class)//無效變量(變量不能以數(shù)字開頭)//def 5imooc = 0;以上輸出結(jié)果為:class java.lang.Integerclass java.lang.String通過上面的代碼可以發(fā)現(xiàn),其實(shí)它和 Java 是差不多的,不過 Java 中我們定義變量必須指明它的類型。而 Groovy 中我們可以使用def,系統(tǒng)會(huì)自動(dòng)幫我們轉(zhuǎn)換。2. 引入包 import這點(diǎn)跟我們?cè)?Java 中的是一樣的,用的是import。下面我們看個(gè)簡單例子[引入MarkupBuilder類,創(chuàng)建 xml]:import groovy.xml.MarkupBuilder def newXml = new MarkupBuilder() 這里我們引入了 MarkupBuilder 類。我們?cè)賮砜匆粋€(gè)例子:def _name = "Groovy語言基礎(chǔ)"println(_name.class)細(xì)心的同學(xué)會(huì)發(fā)現(xiàn),我們這里定義 String 時(shí),并沒有引入 String 對(duì)應(yīng)的包。但是我們寫 Java 時(shí)需要引入 java.lang.String。這就是我們下面說的一個(gè)小知識(shí)點(diǎn)。Tips:在開發(fā) Groovy 時(shí),以下一些庫,我們不需要顯式的去引入它們,系統(tǒng)已經(jīng)包含了這些庫。import java.lang.*import java.util.*import java.io.*import java.net.*import groovy.lang.*import groovy.util.*import java.math.BigIntegerimport java.math.BigDecimal3. 注釋和分號(hào)在 Groovy 語言中它的注釋及分號(hào)和 Java 語言完全一樣。以分號(hào)為語句間的分隔??梢杂卸嘈凶⑨尯蛦涡凶⑨?。//這是單行注釋/** 這是 * 多行 * 注釋**/ def i = 0; println('Hello Mooc'); 4. 關(guān)鍵字在 Java 和 C 語言中我們都知道,定義變量的時(shí)候不能是關(guān)鍵字,那么我們來看下 Groovy 有哪些關(guān)鍵字呢?asassertbreakcaseenumextendscatchclassconstcontinuefalseFinallydefdefaultdoelseforgotoimportininstanceofinterfaceifimplementsnewpullpackagereturnthrowstraitsuperswitchthisthrowtruetrywhile
GNU/Linux 將可以兼容并能夠執(zhí)行 UNIX 標(biāo)準(zhǔn)的程序,但是不會(huì)和 UNIX 系統(tǒng)完全一模一樣,最大的不同是 GNU 計(jì)劃擁有支持長文件名、版本號(hào)、一個(gè)健壯穩(wěn)定文件系統(tǒng),在某些情況下還有自動(dòng)文件名補(bǔ)全、與使用終端無關(guān)的顯示支持、可能最后還要有一個(gè)基于 x-windows 的視窗系統(tǒng),以使好幾個(gè)系統(tǒng)程序和普通的 UNIX 應(yīng)用程序能共享同一屏幕。C 語言將成為系統(tǒng)的程序語言。并且會(huì)想辦法支持 UUCP,MIT Chaosnet,及 Internet 的通訊交流協(xié)議。 GNU 最初的目標(biāo)是在有虛擬內(nèi)存的 68000/16000 等系列機(jī)器上開發(fā),因?yàn)檫@樣的環(huán)境是最容易開發(fā)程序來實(shí)現(xiàn) GNU 的運(yùn)行。剩下來讓 GNU 能在其它較小的機(jī)器上運(yùn)行的工作,將會(huì)留給那些希望能在這些機(jī)器上使用的人。GNU 不是公共的,毫無約束的軟件(public domain)。GNU 計(jì)劃允許每一個(gè)人修改及傳播 GNU,但是絕不允許傳播者對(duì)他傳播的程序再加進(jìn)其他的限制。也就是說,不允許將修改后的程序據(jù)為己有。GNU 計(jì)劃希望能確保 GNU 所有的版本都能保持自由,所以 GNU 的核心精神是自由與分享,這和真正的 Hacker 精神有異曲同工之妙,雖不能說 GNU 精神就是 Hacker 精神,不過很難把兩者清楚的分開,實(shí)際上很多 Hacker 都為 GNU 做出了巨大的貢獻(xiàn)。對(duì)知識(shí)的渴求是支持他們探索的唯一動(dòng)力。當(dāng)然這里的 Hacker 黑客不是那些破壞計(jì)算機(jī)系統(tǒng)牟取私利而臭名昭著的駭客。
在程序中,我們需要實(shí)現(xiàn)兩個(gè)主要的功能,一個(gè)是將字符串中的每個(gè)字符都訪問一次。另外一個(gè)就是將遍歷過程中獲取的大寫字符轉(zhuǎn)換為小寫字符。#include <stdio.h>#include <string.h>#include <ctype.h>int main(){ char a[100] = "Welcome to our WIKI! It is a interest place.", temp; printf("Before convert: %s\n", a); for (int i = 0; i < strlen(a); i++) { temp = tolower(a[i]); a[i] = temp; } printf("After convert: %s\n", a); return 0;}運(yùn)行結(jié)果:Before convert: Welcome to our WIKI! It is a interest place.After convert: welcome to our wiki! it is a interest place.程序首先通過一個(gè)循環(huán)語句,遍歷訪問字符串的每一個(gè)元素。在這里,我們用了 C 語言標(biāo)準(zhǔn)庫中的字符串函數(shù) strlen 來獲取字符串的長度。不過這里請(qǐng)注意的是,這里獲取的長度不是字符數(shù)組的長度,而是里面包含的字符的長度。這樣就不用循環(huán) 100 次,因?yàn)槔锩娲鎯?chǔ)的字符內(nèi)容很顯然不到 100 個(gè),同時(shí),這個(gè)字符數(shù)組中在字符后面會(huì)緊接著一個(gè)字符串的空串符號(hào),也就是 \0 這個(gè)字符。但是這個(gè)字符不會(huì)出現(xiàn)在我們的循環(huán)中,因?yàn)橥ㄟ^ strlen 這個(gè)函數(shù)獲取的字符串的長度中是不包含這個(gè)符號(hào)的。在循環(huán)語句的內(nèi)部,我們只要簡單的執(zhí)行將每個(gè)元素執(zhí)行一次大寫轉(zhuǎn)換為小寫的函數(shù)操作就可以了。這個(gè)函數(shù)是 ctype.h 函數(shù)庫中的函數(shù),只會(huì)將大寫字母轉(zhuǎn)換為小寫字母,不會(huì)變換小寫字母以及符號(hào)。這里我們需要利用一個(gè)臨時(shí)的字符變量來存儲(chǔ)變化后的變量,然后再賦值給當(dāng)前的字符數(shù)組。