面向对象编程
-
OOP思想 :拟人化思想。物以类聚. 类:抽象的模板,定义一个类(属性、方法),实例化出来一个对象-具体的。
-
OOP三大特性:封装、继承、多态
-
Go语言不是面向对象的编程语言,有一些机制可以实现面向对象编程的思维
-
封装:结构体封装 + 方法实现 , 模拟一个类
-
继承:匿名字段 、提升字段 (属性、方法)
-
多态:一个事务可以拥有多种形态(接口)
-
空接口 any
接口嵌套
package main import ( "fmt" ) type AA interface { test1() } type BB interface { test2() } // 接口嵌套 CC : test1()/test2()/test3() // 如果要实现接口CC,那么需要实现这个三个方法。那这个对象就有3个接口可以转型。 type CC interface { AA // 导入AA接口中的方法 BB test3() } // 编写一个结构体实现接口CC type Dog7 struct { } func (dog Dog7) test1() { fmt.Println("test1") } func (dog Dog7) test2() { fmt.Println("test2") } func (dog Dog7) test3() { fmt.Println("test3") } func main() { // dog 拥有4种形态: Dog7 、CC 、 BB 、 AA var dog Dog7 = Dog7{} dog.test1() dog.test2() dog.test3() // var a AA = dog a.test1() //a.test2() // 向上转型之后只能调用它自己对应的方法 var b BB = dog b.test2() var c CC = dog c.test1() c.test2() c.test3() }
接口断言
检查一个接口类型的变量是不是符合你的预期值。
比如:我们预期使用 test3() . 如果传递的是 a 、b 对象,我们就需要判断这个对象是否是我们预期的对象。 = 断言。
被断言的对象必须是接口类型,否则会报错
如果一个对象存在多种形态: 接口判断 登录 20个实现类 微信登录
-
i.(T) 判断是否是预期接口
-
swtich i.(type) 判断是否是预期的case结果
package main import "fmt" // 通过switch来判断 switch i.(T) type I interface{} // 如果断言的类型同时实现了switch 多个case匹配,默认使用第一个case func testAsserts(i interface{}) { // switch i.(type) 接口断言 switch i.(type) { case string: fmt.Println("变量为string类型") case int: fmt.Println("变量为int类型") case I: fmt.Println("变量为I类型") case nil: fmt.Println("变量为nil类型") case interface{}: fmt.Println("变量为interface{}类型") // ..... default: fmt.Println("未知类型") } } func main() { testAsserts("string") testAsserts(1) var i I // 默认值为 nil var i2 I = 1 // 只有赋值了之后,才是对应的类型 testAsserts(i) testAsserts(i2) }
type 别名
-
type xxx TTT 自定义类型
-
type xxx = TTT 起别名
package main import "fmt" // var 变量 type 类型(结构体、接口、别名...) // type的别名用法,全局变量中 // 这是定义了一个新类型 MyInt,是int转换过来的,和int一样,但是不能通int发生操作,类型不同 // MyInt int // 创建了一种新类型! type MyInt int func main() { var a MyInt = 20 // MyInt var b int = 10 // int // invalid operation: a + b (mismatched types MyInt and int) //fmt.Println(a + b) // 类型转换: T(v) fmt.Println(int(a) + b) // 30 fmt.Printf("%T\n", a) // main.MyInt fmt.Printf("%T\n", b) // int // 给int起一个小名,但是它还得是int type any var c diyint = 30 fmt.Printf("%T\n", c) // int } /* type关键字的理解: 1、type 定义一个类型 - type User struct 定义结构体类型 - type User interface 定义接口类型 - type Diy (int、string、....) 自定义类型,全新的类型 2、type 起别名 - type xxx = 类型 ,将某个类型赋值给 xxx,相当于这个类型的别名 - 别名只能在写代码的时候使用,增加代码的可阅读性。 - 真实在项目的编译过程中,它还是原来的类型。 */
异常和错误
错误与异常
错误:指的是程序中预期会发生的结果,预料之中
打开一个文件:文件正在被占用,可知的。
异常:不该出现问题的地方出现了问题,预料之外
调用一个对象,发现这个对象是个空指针对象,发生错误。
错误是业务的一部分,而异常不是。
go语言开发过程中遇到最多的代码,就是error。
需要将所有的错误情况都考虑到,并写到你的代码中。
错误 error
鼓励工程师在代码中显式的检查错误,而非忽略错误。
package main import ( "fmt" "os" ) // 错误是开发中必须要思考的问题 // - 某些系统错误 ,文件被占用,网络有延迟 // - 人为错误:核心就是一些不正常的用户会怎么来给你传递参数,sql注入 func main() { //打开一个文件 os 系统包,所有可以用鼠标和键盘能执行的事件,都可以用程序实现 // func Open(name string) (*File, error) file, err := os.Open("aaa.txt") // 在开发中,我们需要思考这个错误的类型 PathError // 1、文件不存在 err // 2、文件被占用 err // 3、文件被损耗 err // 调用方法后,出现错误,需要解决 if err != nil { fmt.Println(err) return } fmt.Println(file.Name()) } // 在实际工程项目中, // 我们希望通过程序的错误信息快速定位问题,但是又不喜欢错误处理代码写的冗余而又啰嗦。 // Go语言没有提供像Java. c#语言中的try...catch异常处理方式, // 而是通过函数返回值逐层往上抛, 如果没有人处理这个错误,程序就终止 panic // 这种设计,鼓励工程师在代码中显式的检查错误,而非忽略错误。 // 好处就是避免漏掉本应处理的错误。但是带来一个弊端,让代码繁琐。 // Go中的错误也是一种类型。错误用内置的error类型表示。就像其他类型的,如int, float64。 // 错误值可以存储在变量中,从函数中返回,传递参数 等等。
自己定义一个错误
package main import ( "errors" "fmt" ) // 自己定义一个错误 // 1、errors.New("xxxxx") // 2、fmt.Errorf() // 都会返回 error 对象, 本身也是一个类型 func main() { age_err := setAge(-1) if age_err != nil { fmt.Println(age_err) } fmt.Printf("%T\n", age_err) // *errors.errorString // 方式二 errInfo1 := fmt.Errorf("我是一个错误信息:%d\n", 500) //errInfo2 := fmt.Errorf("我是一个错误信息:%d\n", 404) if errInfo1 != nil { // 处理这个错误 fmt.Println(errInfo1) } } // 设置年龄的函数,一定需要处理一些非正常用户的请求 // 返回值为 error 类型 // 作为一个开发需要不断思考的事情,代码的健壮性和安全性 func setAge(age int) error { if age < 0 { // 给出一个默认值 age = 3 // 抛出一个错误 errors 包 return errors.New("年龄不合法") } // 程序正常的结果,给这个用户赋值 fmt.Println("年龄设置成功:age=", age) return nil }
自定义一个错误
package main import ( "fmt" ) type KuangShenError struct { msg string code int } // Error() string func (e *KuangShenError) Error() string { // fmt.Sprintf() 返回string return fmt.Sprintf("错误信息:%s,错误代码:%d\n", e.msg, e.code) } // 处理error的逻辑 func (e KuangShenError) print() bool { fmt.Println("hello,world") return true } // 使用错误测试 func test(i int) (int, error) { // 需要编写的错误 if i != 0 { // 更多的时候我们会使用自定义类型的错误 return i, &KuangShenError{msg: "非预期数据", code: 500} } // 正常结果 return i, nil } func main() { i, err := test(1) if err != nil { fmt.Println(err) ks_err, ok := err.(*KuangShenError) if ok { if ks_err.print() { // 处理这个错误中的子错误的逻辑 } fmt.Println(ks_err.msg) fmt.Println(ks_err.code) } } fmt.Println(i) }
购物
下单:
1、查询商品信息 (FindGoodsError: msg、code | fun-商品不在了 ,fun-商品失效 ,fun-网络错误 )
2、查询库存 (FindxxxError: msg、code | fun-库存为0 ,fun-库存>0)
3、查询物流是否可以到达 (xxxError: msg、code | fun ,fun)
4、查询价格生成订单 (xxxError: msg、code | fun ,fun)
5、支付成功与否 (xxxError: msg、code | fun ,fun)
6、成功就产生一个订单,推送到商家和物流 (xxxError: msg、code | fun ,fun)
作业
自定义三个错误
1、简单的错误类型;只包含一些属性 、msg code
思路: 创建一个错误类型结构体 实现error接口 编写一个测试函数,返回err和正常值 func test(xxx ) (xx,err){} 在程序中调用这个错误,接收这个错误的结果 断言这个错误,解决错误
2、定义一个复杂的错误类型,包含属性和方法,msg code info 、检查这个错误是否符合预期、系统错误、认为错误
3、定义个统一返回结果:R (属性:msg、code,data) 方法:R.ok() R.error() R.info() (100%项目中都会有这个)
type R struct{
}