协议定义了一个蓝图,规定了用来实现某一特定工作或者功能所必需的方法和属性。类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议定义的方法和功能。任意能够满足协议要求的类型被称为遵循(conform)这个协议。

协议定义,协议中的属性需求,协议中的方法需求

//土豪修别墅,看看如何使用协议
@objc protocol Villa{//土豪朋友必须知道有多少层,实例属性var floors: Int {get}//土豪朋友必须知道要什么风格,实例属性var style: String {get}//别墅必须有门可以打开,实例方法func openGate() -> String//别墅必须要有空调func openAirCondition() -> Bool//别墅有个狗窝更好optional func hasDogHouse() -> Bool
}//土豪朋友以前也是码农,虽然改了别墅,但也必须要有一个工作室写程序
protocol Workshop:class{//必须要有桌子var desk: String {get set}//必须要有一本编程书籍var book: String {get}//可以编写程序func program(code: String) -> Bool//可以调试func debug()
}//码农离不开两样东西,电脑和游戏,必须要有游戏屋
protocol GameRoom{var games: String {get}
}//最终土豪朋友拿着这份组合协议的要求去招标
protocol CombineRequirement: Villa,Workshop{//这里不能够使用class,类属性static var name: String {get}//在协议中声明类方法,仍然要使用static关键字。static func finish() -> Bool}//实现了别墅建设要求的组合协议
class 别墅: CombineRequirement{@objc let floors = 4@objc var style:String{return "全欧式建筑"}@objc func openGate() -> String {return "全自动门,自由打开!"}@objc func openAirCondition() -> Bool {return true}var desk: String{get{return "码农专办公室"}set{print("新的值为:\(newValue)")}}var book = "编程玩转 Swift"func program(code: String) -> Bool {print("土豪哥写的Swift程序:\(code)")return true}func debug() {print("调试通过哦!")}class var name: String{return "别墅"}class func finish() -> Bool {return true}
}

上述代码分析:

1:协议定义的几种格式:

  普通格式: protocol 协议名称 {协议体} 如:protocol GameRoom{协议体}继承格式:protocol 协议名称:协议1,协议2,...{协议体}如:protocol CombineRequirement: Villa,Workshop{协议体}类特定协议格式1:@objc protocol 协议名称 {协议体} 如:@objc protocol Villa{协议体}类特定协议格式2:protocol 协议名称: class {协议体} 如:protocol Workshop:class{协议体}注意点:要使类遵循某个协议,需要在类型名称后加上协议名称,中间以冒号:分隔,作为类型定义的一部分。遵循多个协议时,各协议之间用逗号,分隔。class SomeClass: FirstProtocol, AnotherProtocol {// 类的内容}如果类在遵循协议的同时拥有父类,应该将父类名放在协议名之前,以逗号分隔。class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {// 类的内容} 

2:协议也是一种类型,就像String,Enum一样,所以我们定义协议名时,首字母应大写,遵守命名规范.

3:关于协议中的属性需求,如下:

 1)协议中描述属性需求时,必须使用var.2)遵守类型实现协议属性需求时,不能够改变属性的名字和类型,比如Villa中的style,在其遵守类型中不能改为其他名字或者类型,否则会被认为没有遵守该需求.3)协议中的需求可以描述为实例属性,也可以是类型属性.而不指定是存储型属性(stored property)还是计算型属性(calculate property),在协议中定义类属性(type property)时,总是使用static关键字作为前缀。当协议的遵循者是类时,可以使用class或static关键字来声明类属性,但是在协议的定义中,仍然要使用static关键字。4)属性后面的{get}表示,协议遵守类型在实现该属性需求时,既可以把该属性定义为变量var,也可以是常量let.5)属性后面的{get set}表示,协议遵守类型在实现该属性需求时,只能够是把该属性定义为变量var,而不能够为let.

4:关于协议中的方法需求,如下:

 1)既可以描述为实例方法,也可以描述为类型方法.像普通的方法一样放在协议的定义中,但是不需要大括号和方法体。可以在协议中定义具有可变参数的方法,和普通方法的定义方式相同。但是在协议的方法定义中,不支持参数默认值。2)和属性要求一样,其遵守类型在实现方法需求时不能够改变协议方法的名称,返回值和参数必须完全匹配.3)协议可以继承并且可以多继承,如:protocol CombineRequirement: Villa,Workshop{},CombineRequirement协议同时遵守了两个协议.4)标准格式的协议可以被类,结构体和枚举三种类型遵守,但我们也可以通过使用"类特定协议格式1"和"类特定协议格式2"强制这个协议只能是被类类型遵守,如: Villa和 Workshop协议只能是被类类型遵守,如果使用值类型会提示和错误.5)需要注意一个细节:如果一个协议 A 继承自多个协议(B,C,D 等),如果被继承的多个协议里面有一个是类特定使用的协议,则最终A协议及时没有要求只能够被类遵守,其结果也是类特定的协议.如上述代码:CombineRequirement,因为Villa是类特定协议,所以最终CombineRequirement也是类特定协议.6)并不是所有的协议方法都需要在遵守类型中实现,如:optional func hasDogHouse() -> Bool,使用optional修饰,是可选的,不是强制性的实现.7)在协议中定义类方法的时候,总是使用static关键字作为前缀。当协议的遵循者是类的时候,虽然你可以在类的实现中使用class或者static来实现类方法,但是在协议中声明类方法,仍然要使用static关键字。

协议中的突变方法

有时需要在方法中改变它的实例。例如,值类型(结构体,枚举)的实例方法中,将mutating关键字作为函数的前缀,写在func之前,表示可以在该方法中修改它所属的实例及其实例属性的值.

protocol Togglabel{mutating func toggle()
}//定义枚举开关类型,来实现Togglabel协议
enum Switcher:Togglabel{case Off,Onmutating func toggle() {switch self{case .Off:self = .Oncase .On:self = .Off}}
}//定义结构体按钮类型,来实现Togglabel的协议
struct Button:Togglabel{var status = "Normal"mutating func toggle() {if status == "Normal"{self = Button(status: "Pressed")}else{self = Button(status: "Normal")}}
}//定义控件类,来实现Togglabel协议
class UIControl:Togglabel{//此时这个协议的突变方法,在类里面就是普通方法,因为类是引用类型,不支持突变方法.func toggle() {print("Toggle me")}
}func testTwo(){var switcher:Switcher = .Offswitcher.toggle()/*注意的地方:1:协议中的突变方法需求的定义格式,mutating func 方法名(),如:mutating func toggle()2:突变方法只能够用于值类型,不能够用于引用类型.对于定义了突变方法需求的协议也可以被类类型实现,但是此时突变方法一普通方法对待,因为类类型是引用类型,不能够使用突变方法,所以实现要去掉前面的mutating 标示符.如: func toggle() { print("Toggle me") }3:突变方法的目的是程序运行时改变自己,所以凡是要使用突变方法的类型在使用时,都要使用变量类型(var),而不能够是常量类型(let).ru : var switcher:Switcher = .Off*/
}
所以我们在写给别人用的协议时需要多考虑是否使mutating用来修饰方法,比如定义为 mutating func myMethod()。Swift的mutating关键字修饰方法是为了能在该方法中修改struct或是enum的变量,所以如果我们没在协议方法里写 mutating 的话,别人如果用struct或是enum来实现这个协 议的话,就不能在方法里改变自己的变量了。

协议中的构造器

//凡是侠客,都应该有一把武器
protocol KnightErrant{init(weapon: String)//注意:在协议中使用可失败构造器要求init?(hasWeapon: Bool)
}//武林低值都是侠客,所以应该遵守侠客的协议
class Disiple: KnightErrant{//注意:协议中的构造器方法在遵守类中被实现为指定构造器required init(weapon: String) {}//注意:在协议遵守类中实现协议中的可失败构造器的要求,使用init?方式required init?(hasWeapon: Bool) {}//增加便捷构造器convenience init(){self.init(weapon: "青蛇剑")}
}//武当对自己的弟子也有要求,需要佩带剑
class WuDang{init(weapon: String){}
}//武当弟子,既要遵守武当的门规也要符合侠客的协议
class WuDangDisiple:WuDang,KnightErrant{//发现协议中的构造器和父类的构造器相同,所以要使用required和overriderequired  override init(weapon: String) {super.init(weapon: weapon)}//在协议遵守类中实现协议的可失败构造器的要求,使用init!方式.required init!(hasWeapon: Bool) {super.init(weapon: "武当")if hasWeapon == false{return nil}}
}//隐居大侠也是侠客类,所以同样遵守侠客的协议
class Hermit: KnightErrant{init(name: String){}required init?(hasWeapon: Bool) {if hasWeapon == false{return nil}}//协议中的构造器方法在遵守类中被实现为便捷构造器required convenience init(weapon: String){self.init(name: "令狐冲")}
}//武林盟主更应该是侠客,所以遵守KnightErrant协议
final class 武林盟主: KnightErrant{//注意:协议中的构造器方法在遵守类中被实现,但省略了required修饰符,因为该类是final类.所以可以省略.此处init(weapon: String)是构造器的要求.init!(weapon: String) {}init?(hasWeapon: Bool) {}
}

上面的代码演示了构造器的使用场景:

1:协议中的构造器可以在遵守类中实现为指定构造器或者便捷构造器.

2:协议遵守类在实现构造器需求时,构造器的定义要求使用required修饰符.

3:如果遵守类使用了final修饰,说明该类不会再被继承,所以此时实现的构造器方法可以省略required修饰符.

4:如果遵守类既遵守某个协议又继承某个类,发现父类中的指定构造器和协议中要求的构造器相同,这时在遵守类中的required和override都要写.

5:关于协议中的可失败构造器的使用,如我们定义了一个可失败构造器init?(hasWeapon: Bool) {},为了满足可失败构造器要求,在协议的实现类中,我们就可以使用init?的方式,也可以使用init!的方式实现.同时也要注意:对于非可失败构造器init(weapon: String),我们在遵守类中的实现也有2种方式,分别是init和init!方式.如: required init(weapon: String) {}和init?(hasWeapon: Bool) {} .

这里为什么一会要required一会不要呢?因为Swift中的所有的语法和修饰符的使用都是为了语法逻辑更加清晰,当一个类不被final修饰,说明该类可以被继承,被重写,甚至可能会被继承多次,所以需要写required,其目的是想告诉后面的继承者们,你们必须重写这个构造器,这个构造器是必须的,但是如果被final修饰了,说明该类不会被继承,那写不写required就没什么价值了,所以可以省略.

协议与委托

委托是一种设计模式,它允许类或者结构体将一些需要它们负责的功能交由(委托)其他类型的实例来完成.在这种设计模式中,主要有以下几个角色:
     公共接口:负责封装起需要被委托的功能.
     代理者:被委托通过它来调用委托的功能.
     委托者:将它们负责的功能委托给其他的类或者结构体,及被委托者.

//异形者的委托协议
@objc protocol SkinChangeDelegate{var skinType:String {get}func spy()optional func Investigate()optional func attack()
}//鹰,可以在空中侦察
class EagleSkin:SkinChangeDelegate{@objc var skinType: String = "鹰"@objc func spy() {print("\(skinType)熬翔于天际,侦察敌情!")}
}//狼,可以在夜间侦察
class WolfSkin:SkinChangeDelegate{@objc var skinType: String = "狼"@objc func spy() {print("\(skinType)穿行于黑夜,侦察敌情!")}
}//曼斯的野人军团
class WildArmy{var warrior: Int = 1000var giant: Int = 25var mammoth: Int = 10//异性者,凡是异性者都实现了这个委托协议var animalDelegate: SkinChangeDelegate?//进行侦察func spyByAnimal(){animalDelegate?.spy()}func attack(){print("感谢异形者控制\(animalDelegate!.skinType)侦察信息,进行攻击!")}
}func testOne(){//创建曼斯的野蛮军团let armyOfMance = WildArmy()//目前有两个动物可以被异形者控制,分别是狼和鹰let animals:[SkinChangeDelegate] = [EagleSkin(),WolfSkin()]for animal in animals{//进入动物体内,通过委托协议来调用动物的方法.armyOfMance.animalDelegate = animal//通过异性者侦察armyOfMance.spyByAnimal()//攻击armyOfMance.attack()}/*鹰熬翔于天际,侦察敌情!感谢异形者控制鹰侦察信息,进行攻击!狼穿行于黑夜,侦察敌情!感谢异性者控制狼侦察信息,进行攻击!注意:1:协议中定义了可委托给异性者的公共接口,并且实现了两个委托者,分别是鹰和狼,它们都遵守了异形者的协议,因此它们可把技能委托给代理者var animalDelegate: SkinChangeDelegate?,及异性者.2:@objc的用法:1)标志着当前的Swift类可以导入 OC 中使用2)标志当前的协议只能够用于类类型.3)允许在协议中使用可选的方法和属性.*/
}

协议与扩展

//在保安的思维里好人一定开好车,所以定义了好人的协议就是必须要有一辆好车
protocol GoodMan{func haveGoodCar()
}//贼也可以开好车,虽然没有遵守好人的协议,但是满足保安对于好人开好车的要求.
class Thief{var work = "贼"func haveGoodCar(){print("开好车的人不一定是好人,也可能是贼!")}
}//普通的民工,是好人,但没有好车
class Civilian{var work = "挤奶工"
}//1:扩展遵守内容,因为事先实现了协议内容,所以这里不再实现.出门时,保安看到贼开的好车,认为他们是好人,所以扩展给Thief好人的协议
extension Thief:GoodMan{}//2:扩展遵守协议,实现协议内容.
extension Civilian:GoodMan{func haveGoodCar() {print("好人不一定开好车!")}
}func testThree(){var 刘德华 = Thief()刘德华.haveGoodCar()var 傻根 = Civilian()傻根.haveGoodCar()/*开好车的人不一定是好人,也可能是贼!好人不一定开好车!上述使用了<天下无贼>的例子说明了通过扩展使被扩展类型遵守协议的类中情况.1:对于已经存在的类,结构体或者枚举,可以扩展它们,使它们遵守新的协议.如民工类Civilian,刚开始并没有遵守GoodMan协议,我们可以在扩展的时候,使它遵守GoodMan协议.2:如果一个类,结构体或者枚举,即便已经实现某个协议要求的方法和属性,但是Swift不会认为这个类型已经遵守这个协议.就像Thief类已经实现了方法haveGoodCar,满足了GoodMan协议,但是Thief不会自动遵守这个协议.所以我们可以通过扩展,让Thief类遵守协议,如extension Thief:GoodMan{},这里注意:不需要重新实现协议,只需要把扩展的内容留空即可.*/
}

可选协议

//定义一份保险合同的协议
@objc protocol CarInsurance{ //在@objc修饰后,其类型要和 OC 的类型想匹配,所以其中的属性不能够是可选,方法不能够返回可选类型,只是为了要实现Swift和 OC的互用和兼容!//强制缴纳交通险var compulsoryInsurance: Int {get}//第三方责任险,可以不买,根据需求自行购买optional var thirdPartyInsurance: Int {get}//被盗险,可以不买,根据需求自行购买optional var theftAgainstInsurance: Int {get}//用于复杂的保险金额统计,如果只有一种保险,那就没有必要实现optional func countInsuranceCost() -> Int
}//保险公司给出的保险单
class CarInsuranceReport{//车种类型var carBrand:String//车牌var carId:String//保险种类数据var insuranceData: CarInsurance?init(carBrand: String, carId: String){self.carBrand = carBrandself.carId = carId}//汇报购买保险的金额和种类func reportInsuranceInformation(){//如果购买了强制交通险if let amount = insuranceData?.compulsoryInsurance{print("\(carBrand)\(carId)买了如下保险:")print("强制交通险:\(amount)")} else{print("\(carBrand)\(carId)没有保险记录:")return}//如果购买了第三方责任险if let amount = insuranceData?.thirdPartyInsurance{print("第三方责任险:\(amount)")}//如果购买了被盗险if let amount = insuranceData?.theftAgainstInsurance{print("第三方被盗险:\(amount)")}//如果购买了复杂的保险金额统计if let amount = insuranceData?.countInsuranceCost?(){print("总计:\(amount)")} else{print("总价不要算了,这个屌丝只买了强制险!")}}
}//保险公司为屌丝准备了一份合同
class DiaoSiContract: CarInsurance{@objc let compulsoryInsurance = 960
}//保险公司为土豪准备了一份合同
class TuHaoContract: CarInsurance{@objc let compulsoryInsurance = 960@objc let thirdPartyInsurance:Int@objc let theftAgainstInsurance:Int//土豪买了所有的保险init(thirdPartyInsurance: Int,theftAgainstInsurance: Int){self.thirdPartyInsurance = thirdPartyInsuranceself.theftAgainstInsurance = theftAgainstInsurance}@objc func countInsuranceCost() -> Int {var amount = thirdPartyInsurance + theftAgainstInsuranceif amount > 5000{return Int(Double(amount) * 0.9) + compulsoryInsurance //有打折}else{return compulsoryInsurance}}}func testFour(){//迈巴赫霸气的牌子var report = CarInsuranceReport(carBrand: "迈巴赫", carId: " A111111")//买了车,没钱买保险,只能够暂时不买!report.reportInsuranceInformation()//被警察叔叔逮着,强制买了屌丝专用保险report.insuranceData = DiaoSiContract()report.reportInsuranceInformation()//一不小心中了彩票,买了土豪专用保险,9折优惠!report.insuranceData = TuHaoContract(thirdPartyInsurance: 3500, theftAgainstInsurance: 2500)report.reportInsuranceInformation()/*迈巴赫 A111111没有保险记录:迈巴赫 A111111买了如下保险:强制交通险:960总价不要算了,这个屌丝只买了强制险!迈巴赫 A111111买了如下保险:强制交通险:960第三方责任险:3500第三方责任险:2500总计:6360分析:1:可选协议的声明必须使用@objc修饰,修饰符有以下两个用途:一是用于把 Swift类型导出到 OC 代码时用.二允许协议指定为可选.如:@objc protocol CarInsurance{}2:对于协议中的可选属性和方法,需要使用optional修饰.3:可选协议的遵守类可以只实现协议中的非可选需求.4:在使用时,注意可选链:insuranceData?.countInsuranceCost?().第一个?表示insuranceData可能存在也可能不存在,第二个?表示,及时insuranceData存在了,但是countInsuranceCost方法可能没有实现,不存在.如DiaoSiContract就没有实现.*/}

协议的组合和转换

@objc protocol Money{func haveMoney() ->Bool
}//定义帅的协议
@objc protocol GoodLook{func haveGoodLook() ->Bool
}//既帅又有钱,人生赢家,符合帅和有钱的协议
class LifeWinner: Money,GoodLook{var name = ""@objc func haveGoodLook() -> Bool {print("帅的一逼!")return true}@objc func haveMoney() -> Bool {print("富的流油!")return true}
}//有钱但是不帅,比如李刚
class LiGang:Money{@objc func haveMoney() -> Bool {print("我是李刚,哥有钱!")return true}
}struct MatchMakingAgent{//对于婚姻中介,只要有征婚的需求,不管条件,都会放到获选人数据库里var candidatsList:[Money] = [LifeWinner(),LiGang()];var name = "百合网"//为客户服务func service(customer: String){//有客户提出需求时,需要在候选人库里找for candidate in candidatsList{if candidate is GoodLook{print("遍历获选人数据,发现这个人确实很帅!")}//如果是人生赢家,优先推荐if candidate as? LifeWinner != nil{print("遍历候选人数据库,这个候选人看起来是人生赢家,莫非是郭富城,客户会满意的!")//给客户看看这个男人怎么样!findBoyFriendForCustomer(customer, candidate: candidate as! LifeWinner)break}}}//为客户找一个男朋友,女人们都想找一个男人既帅又有钱,所以后面的参数是一个协议组合func findBoyFriendForCustomer(customer:String,candidate:protocol<Money,GoodLook>){if candidate.haveGoodLook() && candidate.haveMoney(){print("\(customer)觉得这个人帅又有钱,人生赢家,\(customer)很开心!")}}
}func testFive(){var matchMakingAgent = MatchMakingAgent()matchMakingAgent.service("凤姐")/*遍历获选人数据,发现这个人确实很帅!遍历候选人数据库,这个候选人看起来是人生赢家,莫非是郭富城,客户会满意的!帅的一逼!富的流油!凤姐觉得这个人帅又有钱,人生赢家,凤姐很开心!分析:1:定义协议组合的格式为:protocol<协议1,协议2,...>.如上述代码:func findBoyFriendForCustomer(customer:String,candidate:protocol<Money,GoodLook>),其意思是凡是传入方法的参数candidate必须同时满足Money,GoodLook协议.2:之前说过,协议本身也是一种类型,所以可以被存储,被转换.如: var candidatsList:[Money] = [LifeWinner(),LiGang()];这里定义了一个Money协议的数组,里面存放着该协议的遵守类型.协议可以像其他普通类型一样使用,使用场景:作为函数、方法或构造器中的参数类型或返回值类型作为常量、变量或属性的类型作为数组、字典或其他容器中的元素类型注意协议是一种类型,因此协议类型的名称应与其他类型(Int,Double,String)的写法相同,使用大写字母开头的驼峰式写法,例如(FullyNamed和RandomNumberGenerator)3:使用is判断一个类型是否遵守了某个协议.4:使用as!和as?来实现协议的转换.其区别在前面文章提到过,as!属于强制转换,如果转换失败会报运行时错误,而as?则会返回一个可选类型,如果转换失败,其返回值为 nil,转换成功,则表示转换后的值.*/
}

Swift 协议(Protocols)相关推荐

  1. Swift协议(Protocols)

    协议主要为一个特定的任务和功能定义一个方法.属性和其他要求,你也可以理解协议就是一种要遵循的规范. 学过设计模式的,都知道工厂模式,如果你不知道可以查阅我的博文<23设计模式之工厂方法(Fact ...

  2. Swift学习笔记-协议(Protocols)

    1.0 翻译:geek5nan 校对:dabing1022 2.0 翻译:futantan 校对:小铁匠Linus 定稿:shanksyang 本页包含内容: 协议的语法(Protocol Synta ...

  3. Swift ——协议

    Swift --协议 1. 协议与继承 2. 协议的基本语法 3. 协议原理探究 4. 写时复制 1. 协议与继承 为两个类创建一个 debug 函数来打印当前类的基本信息.从继承的⻆度来说,我们可能 ...

  4. 6.Swift协议|扩展|访问权限|异常调试|类型转换|运算函数|ARC|类类型初试化器|值类型初始化器...

    1. 协议(Protocol):与OC之间唯一不同的是Swift中的协议不管是属性还时方法全部是必须实现的 /** protocol*/ protocol FullNamed { /** 计算属性申明 ...

  5. Swift协议与扩展

    转载:http://www.jianshu.com/p/e70bd6645d88 前言 熟悉Objective-C语言的同学们肯定对协议都不陌生,在Swift中苹果将protocol这种语法发扬的更加 ...

  6. Swift 协议protocol

    /* 定义协议时候,协议后面最好跟上class delegate的属性最好用weak,用于防止循环引用 */ protocol BuyTicketDelegate : class{ func buyT ...

  7. swift_028(Swift 的协议)

    //***********swift学习之28--协议--*************************** /* 协议规定了用来实现某一特定功能所必需的方法和属性. 任意能够满足协议要求的类型被 ...

  8. Swift之深入解析协议Protocol的底层原理

    一.Swift 协议 ① 概念 协议规定了用来实现某一特定功能所必需的方法和属性. 任意能够满足协议要求的类型被称为遵循(conform)这个协议. 类,结构体或枚举类型都可以遵循协议,并提供具体实现 ...

  9. Swift 面向协议编程的那些事

    一直想写一些 Swift 的东西,却不知道从何写起.因为想写的东西太多,然后所有的东西都混杂在一起,导致什么都写不出来.翻了翻以前在组内分享的一些东西,想想把这些内容整理下,写进博客吧.我对计划要写的 ...

最新文章

  1. 在RHEL 5中Yum应用大全
  2. 请使用frameset/frameset完成以下功能
  3. opencv reduce函数
  4. codesmith学习总结
  5. openstack 热迁移(Live Migration)和冷迁移(Cold Migration)
  6. 牛津学霸的20条读博建议
  7. 不是我不想动脑筋,给我一个支点看看——看《编程之美》,玩数独,大有乐趣...
  8. MySQL 驱动的下载方法
  9. 阿里面试官:分别说说微信和淘宝扫码登录背后的实现原理?
  10. 微信小程序“淘淘猜成语”开发教程(该成语接龙已上线,功能齐全)
  11. 南大俞扬:环境模型学习——让强化学习走出游戏
  12. linux 16进制编辑器 知乎,狂揽2500星,开源十六进制编辑器登顶GitHub热榜
  13. java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to com.mpg.ehr.humanaffairs.salaryb
  14. 测试工程师进阶之测试用例发散思维(一)
  15. volatile原理:happen before
  16. 适合程序员的笔记本电脑
  17. zzuli 2126 tmk买礼物 思维题
  18. java反射机制原理,为什么需要反射,反射的作用
  19. 网络安全和黑客技能:15本必读书籍推荐
  20. 初识node.js(1)

热门文章

  1. R语言建立决策树模型(movie数据集)
  2. 如何在App里面运行Windows 95?
  3. Arduino UNO + Proteus串口通讯仿真实验
  4. 年度报告,如何写出“年”味
  5. 迟来的 2020 年度总结
  6. CodeForces 377 A. Maze
  7. jshell如何导入外部包
  8. Oracle中的wm_concat函数用法
  9. 苹果为什么要禁止 JSPatch 等热更新技术?
  10. 【职场真相】:怎么向领导申请升职加薪?