Go语言基础之18--接口编程
一、接口介绍和定义
1.1 接口定义了一个对象的行为规范
A. 只定义规范,不实现
B. 具体的对象需要实现规范的细节
葵花宝典:
接口就是一层封装,1个例子,封装一个返还浏览器内容的接口。为什么不直接面向对象呢。你封装成一个接口的话,不论是返回文件或者图片或者html都可以通过接口进行返回,不用接口的话,你需要为每一种返回类型写函数。
1.2 Go中接口定义
A. type 接口名字 interface
B. 接口里面是一组方法签名的集合(后面的调用参数和返回值都要和接口中的方法一模一样)
C.接口是引用类型(指针);注意:函数传递接口类型参数时,一定不要加*,因为接口本身就是引用类型,如果加*就报错了
type Animal interface {
Talk()
Eat() int
Run()
}
1.3 Go中接口的实现
A. 在Go语言中,一个类只要实现了接口要求的所有函数,我们就说这个类实现了该接口
B. 接口类型的变量可以保存实现该接口的任何具体类型的实例。
实例:
package main import (
"fmt"
) type Animal interface { //定义了动物的规范(接口定义的一组方法)
Eat()
Talk()
Run()
} type Dog struct { //狗如果能够满足了动物的规范(接口方法),那其就是动物
name string
} func (d *Dog) Eat() { //目前狗还不是动物,因为其只实现了Eat,还需要实现Talk和Run,其才算是动物
fmt.Printf("%s is eating\n", d.name)
} func (d *Dog) Talk() {
fmt.Printf("%s is talking\n", d.name)
} func (d *Dog) Run() {
fmt.Printf("%s is runing\n", d.name)
} func (d *Pig) Eat() {
fmt.Printf("%s is eating\n", d.name)
} func (d *Pig) Talk() {
fmt.Printf("%s is talking\n", d.name)
} func (d *Pig) Run() {
fmt.Printf("%s is runing\n", d.name)
} func main() {
var dog = &Dog{
name: "旺财",
} var a Animal
fmt.Printf("a:%v dog:%v\n", a, dog) //接口底层就是指针,指向的就是一个空对象,如果字节调用就会panic a = dog //dog满足了接口所有方法,所以我们可以直接将其复制给Animal,对应的理论就是接口类型的变量可以保存实现该接口的任何具体类型的实例。
a.Eat()
a.Run()
a.Talk() var pig = &Pig {
name:"佩奇",
} a = pig
a.Eat()
a.Run()
a.Talk()
}
执行结果:
上面这个例子中,Pig和Dog实现了Animal的所有方法,所以Cat和Dog都是动物
小结一下:
Go中的接口只要一个对象实现了接口类型中的所有方法,那么这个对象就实现了这个接口,当然如果一个对象实现了多个interface类型的方法,那么这个对象就实现了多个接口
1.4 接口实例
A. 一个公司需要计算所有职员的薪水
B. 每个职员的薪水计算方式不同
如何解答呢?
我们先单纯通过结构体进行解答:
结构体示例:
package main import (
"fmt"
) type Developer struct { //开发
Name string
Base int
} func (d *Developer) Calc() int {
return d.Base
} type PM struct { //pm
Name string
Base int
Option int
} func (p *PM) Calc() int {
return p.Base + p.Option
} type YY struct { //运营
Name string
Base float32
Option float32
Rate float32 //0.6 ~ 3
} func (p *YY) Calc() float32 {
return p.Base + p.Option*p.Rate
} type EmployeeMgr struct { //最终汇总
devList []*Developer //用切片存
pmList []*PM
yyList []*YY
} func (e *EmployeeMgr) Calc() float32 { //进行计算
var sum float32
for _, v := range e.devList { //计算程序员
sum += float32(v.Calc())
} for _, v := range e.pmList { //计算pm
sum += float32(v.Calc())
} for _, v := range e.yyList { //计算运营
sum += float32(v.Calc())
} return sum
} func (e *EmployeeMgr) AddDev(d *Developer) { //人从哪来,要添加人,添加到列表的函数
e.devList = append(e.devList, d)
} func (e *EmployeeMgr) AddPM(d *PM) {
e.pmList = append(e.pmList, d)
} func (e *EmployeeMgr) AddYY(d *YY) {
e.yyList = append(e.yyList, d)
} func main() {
var e = &EmployeeMgr{} dev := &Developer{ //添加具体人
Name: "develop",
Base: ,
}
e.AddDev(dev) pm := &PM{
Name: "pm",
Base: ,
Option: ,
}
e.AddPM(pm) yy := &YY{
Name: "yy",
Base: ,
Option: ,
Rate: 1.2,
}
e.AddYY(yy) sum := e.Calc() //计算所有人
fmt.Printf("sum:%f\n", sum)
}
执行结果:
解释:
单纯用结构体实现有一个很大的弊端就是如果我们要添加一个职位的话,非常不方便,要改动地方太多,比如:增加职位结构体(类)、结构体方法、计算方式添加、增加进总列表,十分的不灵活,下面我们来看看通过接口实现怎么样
接口实例:
package main import (
"fmt"
) type Employee interface { //定义1个雇员的接口,其规定的方法是calc(计算工资)
Calc() float32 //接下来的各种职位要想使用这个接口,就需要有该接口规定的方法,并且类型也要一致
} type Developer struct {
Name string
Base float32
} func (d *Developer) Calc() float32 {
return d.Base
} type PM struct {
Name string
Base float32
Option float32
} func (p *PM) Calc() float32 {
return p.Base + p.Option
} type YY struct {
Name string
Base float32
Option float32
Rate float32 //0.6 ~ 3
} func (p *YY) Calc() float32 {
return p.Base + p.Option*p.Rate
} type EmployeeMgr struct {
employeeList []Employee //员工管理列表不需要在区分职位了,只需要1个职员列表即可(因为无论是dev还是pm、yy都实现了employee的接口)
} func (e *EmployeeMgr) Calc() float32 { //计算也是都统一一个了
var sum float32
for _, v := range e.employeeList {
sum += v.Calc()
} return sum
} func (e *EmployeeMgr) AddEmpoyee(d Employee) { //追加也是都只有1个了,并且这里参数也不需要加*,因为interface自身就是引用类型。
e.employeeList = append(e.employeeList, d)
} func main() {
var e = &EmployeeMgr{} dev := &Developer{
Name: "develop",
Base: ,
}
e.AddEmpoyee(dev) pm := &PM{
Name: "pm",
Base: ,
Option: ,
}
e.AddEmpoyee(pm) yy := &YY{
Name: "yy",
Base: ,
Option: ,
Rate: 1.2,
}
e.AddEmpoyee(yy) sum := e.Calc()
fmt.Printf("sum:%f\n", sum)
}
执行结果:
解释:
可以发现用接口来实现,非常方便,新增加一个职位,只需要增加一个职位的类和方法以及该职位职员相关信息即可,十分方便了。
1.5 接口类型变量
A. var a Animal
B. 那么a能够存储所有实现Animal接口的对象实例
实例参考Dog和Pig的实例。
二、空接口
2.1 空接口
A. 空接口没有定义任何方法
B. 所以任何类型都实现了空接口,所以空接口就可以用来存储任何类型的实例。
有一个思考,空接口这么牛逼,可以存储任何类型,那我岂不是可以随意用空接口了?
答:这样不好,首先go语言本身是一个强类型语言,规定传什么类型参数就传什么类型,强类型语言通过规定了,能够提高性能,这也是其的一大特点,而如果不规定,用途就模糊不清了,那样维护性差,可操作性低,就丧失了go的优势了。
interface { }
实例如下:
package main import (
"fmt"
) func main() {
var a interface{} //定义1个空接口
var b int a = b //a是空接口,所以可以存储任何类型
fmt.Printf("a=%v a:%T\n", a, a)
var c float32 a = c
fmt.Printf("a=%v a:%T\n", a, a)
}
执行结果:
实例2:
package main import (
"fmt"
) func describe(i interface{}) {
fmt.Printf("Type = %T, value = %v\n", i, i)
} func main() {
s := "hello world"
describe(s) //空接口可以存string
i :=
describe(i) //空接口可以存int
strt := struct {
name string
}{
name: "Naveen R",
}
describe(strt) //空接口可以存struct,可以证明其可以存储任意类型
}
执行结果:
三、类型断言
3.1 如何获取接口类型里面存储的具体的值呢?
实例1:
package main import (
"fmt"
) type Animal interface {
Eat()
} type Dog struct {
name string
} func (d *Dog) Eat() {
fmt.Printf("%s is eating\n", d.name)
} type Pig struct {
name string
} func (d *Pig) Eat() {
fmt.Printf("%s is eating\n", d.name)
} //类型断言
func Describe(a Animal) { //注意Animal(接口)本身就是引用类型,这里不能加*,加上会报错
dog := a.(*Dog) //将animal指定为dog(但是animal中不一定只有dog)
dog.Eat()
} func main() {
var dog = &Dog{
name: "旺财",
} var a Animal
a = dog
fmt.Printf("I am dog\n")
Describe(a) //dog调用类型断言的describe函数 var pig = &Pig{
name: "佩奇",
} a = pig
fmt.Printf("I am pig\n")
Describe(a) //pig也调用类型断言的describe函数
}
执行结果:
解释:
可以发现直接报错了,这是因为我们强行将animal定为dog,但是animal中还有pig,所以pig在调用describe函数时,就冲突了,就报错了。这是一个很大的坑,如何解决呢?
解决办法:
引入 ok判断机制!
实例如下:
package main import (
"fmt"
) type Animal interface {
Eat()
} type Dog struct {
name string
} func (d *Dog) Eat() {
fmt.Printf("%s is eating\n", d.name)
} type Pig struct {
name string
} func (d *Pig) Eat() {
fmt.Printf("%s is eating\n", d.name)
} //类型断言
func Describe(a Animal) { //注意Animal(接口)本身就是引用类型,这里不能加*,加上会报错
dog, ok := a.(*Dog) //引入ok判断机制
if !ok {
fmt.Printf("convert to dog failed\n")
return
}
fmt.Printf("describe suncc\n")
dog.Eat()
fmt.Printf("describe suncc----------\n")
} func main() {
var dog = &Dog{
name: "旺财",
} var a Animal
a = dog
fmt.Printf("I am dog\n")
Describe(a) var pig = &Pig{
name: "佩奇",
} a = pig
fmt.Printf("I am pig\n")
Describe(a)
}
执行结果:
解释:
我们可以发现引入ok判断机制后,将animal定为dog,但是animal中还有pig,当确定dog时就会执行成功,而是pig则就会失败。
实例2:
package main import (
"fmt"
) func assert(i interface{}) {
s := i.(int) //将空接口传入的类型定为int
fmt.Println(s)
} func main() {
var s interface{} = "harden"
assert(s)
}
执行结果:
解决上述报错:
引入ok机制:
package main import (
"fmt"
) func assert(i interface{}) {
v, ok := i.(int) //将空接口传入的类型定为int
fmt.Println(v, ok)
} func main() {
var s interface{} =
assert(s)
var i interface{} = "harden"
assert(i)
}
执行结果:
3.2 type switch
方法1:问题:需要转2次
实例1:
package main import (
"fmt"
) type Animal interface {
Eat()
} type Dog struct {
name string
} func (d *Dog) Eat() {
fmt.Printf("%s is eating\n", d.name)
} type Pig struct {
name string
} func (d *Pig) Eat() {
fmt.Printf("%s is eating\n", d.name)
} //types switch
func DescribeSwitch(a Animal) {
fmt.Printf("DescribeSwitch(a) begin\n")
switch a.(type) { //格式是固定的,type是一个关键字 //强制转第一次type关键字
case *Dog: //强制转换成dog(强制转第二次)
dog := a.(*Dog)
dog.Eat()
case *Pig: //强制转成pig(强制转第二次)
pig := a.(*Pig)
pig.Eat()
}
fmt.Printf("DescribeSwitch(a) end\n")
} func main() {
var dog = &Dog{
name: "旺财",
} var a Animal
a = dog
fmt.Printf("I am dog\n")
DescribeSwitch(a) var pig = &Pig{
name: "佩奇",
} a = pig
fmt.Printf("I am pig\n")
DescribeSwitch(a)
}
执行结果:
实例2:
package main import (
"fmt"
) func findType(i interface{}) {
switch i.(type) {
case string:
fmt.Printf("I am a string and my value is %s\n", i.(string))
case int:
fmt.Printf("I am a int and my value is %d\n", i.(int))
default:
fmt.Printf("Unknown type\n")
}
} func main() {
findType("hello")
findType()
findType(88.98)
}
执行结果:
方法2:(推荐) 解决需要转2次问题
实例1:
package main import (
"fmt"
) type Animal interface {
Eat()
} type Dog struct {
name string
} func (d *Dog) Eat() {
fmt.Printf("%s is eating\n", d.name)
} type Pig struct {
name string
} func (d *Pig) Eat() {
fmt.Printf("%s is eating\n", d.name)
} //types switch
func DescribeSwitch(a Animal) {
fmt.Printf("DescribeSwitch(a) begin\n")
switch v := a.(type) { //格式是固定的,type是一个关键字
case *Dog:
v.Eat() //v就是断言之后的具体类型
case *Pig:
v.Eat()
}
fmt.Printf("DescribeSwitch(a) end\n")
} func main() {
var dog = &Dog{
name: "旺财",
} var a Animal
a = dog
fmt.Printf("I am dog\n")
DescribeSwitch(a) var pig = &Pig{
name: "佩奇",
} a = pig
fmt.Printf("I am pig\n")
DescribeSwitch(a)
}
执行结果:
实例2:
package main import (
"fmt"
) func findType(i interface{}) {
switch v := i.(type) {
case string:
fmt.Printf("I am a string and my value is %s\n", v)
case int:
fmt.Printf("I am a int and my value is %d\n", v)
default:
fmt.Printf("Unknown type\n")
}
} func main() {
findType("hello")
findType()
findType(88.98)
}
执行结果:
四、指针接收和值接收
葵花宝典:
指针类型实现的接口,值类型是赋值不了的。但是值类型实现的接口,指针类型依然可以赋值
例子如下:
比如说Dog是通过指针类型实现的接口:
当我们进行值类型赋值就会报错:
我们可以通过传入地址来解决:
但如果Dog是值类型实现的接口:
我们的指针类型对其进行赋值依然没问题:
这是因为go在传入时帮我们将指针类型转换为了值类型。
五、多接口
5.1 多接口
同一个类型可以实现多个接口。
比如说狗是一个动物,它可以实现动物的接口,狗同时还是一个哺乳动物,他也可以实现哺乳动物的接口。重要的是:只要把多个接口的方法都实现了,那么其就可以实现多个接口。
package main import (
"fmt"
) //定义2个接口
type Animal interface {
Eat()
} type BuRuAnimal interface {
ChiNai()
} type Dog struct {
name string
} //Dog实现了上述2个接口的方法,所以其也实现了上述2个接口
func (d *Dog) Eat() {
fmt.Printf("%s is eating\n", d.name)
} func (d *Dog) ChiNai() {
fmt.Printf("%s is ChiNai\n", d.name)
} func main() {
var dog = &Dog{
name: "旺财",
} var a Animal
fmt.Printf("a:%v dog:%v\n", a, dog)
a = dog
a.Eat() var b BuRuAnimal
b = dog
b.ChiNai()
}
执行结果:
5.2 接口嵌套
package main import (
"fmt"
) //定义2个接口
type Animal interface {
Eat()
} type BuRuAnimal interface {
ChiNai()
} //接口嵌套
type AdvanceAnimal interface { //要想实现AdvanceAnimal接口,那么就需要满足嵌套的接口的所有方法
Animal
BuRuAnimal
} type Dog struct {
name string
} func (d *Dog) Eat() {
fmt.Printf("%s is eating\n", d.name)
} func (d *Dog) ChiNai() {
fmt.Printf("%s is ChiNai\n", d.name)
} func main() {
var dog = &Dog{
name: "旺财",
} var a AdvanceAnimal
fmt.Printf("a:%v dog:%v\n", a, dog)
a = dog
a.Eat()
a.ChiNai()
}
执行结果:
六、接口实例讲解
6.1 io包中的writer接口
底层writer接口的规定的方法:
实例:
package main import (
"fmt"
"os"
) type Test struct {
data string
} //这里实现1个wirter接口的方法
func (t *Test) Write(p []byte) (n int, err error) {
t.data = string(p)
return len(p), nil
} func main() {
file, _ := os.Create("c:/tmp/c.txt")
fmt.Fprintf(os.Stdout, "hello world\n") //输出到终端 (这里FPrintf函数要传入一个writer类型接口,把具体实例os.Stdout传给writer接口)
fmt.Fprintf(file, "hello world\n")
/* 因为writer接口的存在,我们可以省去下面的步骤:
fmt.FPtrintfConsole()
fmt.FPtrintfFile()
fmt.FPtrintfNet()
*/
var t *Test = &Test{}
fmt.Fprintf(t, "this is a test inteface:%s", "?akdfkdfjdkfk\n") //存入到data中了 fmt.Printf("t.data:%s\n", t.data)
}
执行结果:
6.2 fmt包中的Stringer接口
底层stringer接口
实例:
package main import (
"encoding/json"
"fmt"
) type Student struct {
Name string
Age int
} func (s *Student) String() string {
data, _ := json.Marshal(s)
return string(data)
} func main() {
var a = &Student{
Name: "hell",
Age: ,
}
fmt.Printf("a = %v\n", a) //fmt包调Print相关函数时,看传进去的变量是否实现了stringer接口
}
执行结果:
解释:
Printf函数会判断传入的变量a是否实现了stringer接口,stringer接口会调其中的string方法去格式化输出字符串
6.3 error包中的error接口
底层error接口:
type error interface {
Error() string
}
实例1:
package main import (
"fmt"
"time"
) type MyError struct {
When time.Time
What string
} func (e MyError) Error() string {
str := fmt.Sprintf("at %v, %s", e.When, e.What)
fmt.Printf("1:%T\n", str)
return str
}
func run() error {
fmt.Println("")
str := MyError{time.Now(), "it didn't work"}
fmt.Printf("2:%T\n", str)
fmt.Println(MyError{time.Now(), "it didn't work"})
return str
}
func main() {
if err := run(); err != nil {
fmt.Printf("3:%T\n", err)
fmt.Println(err)
}
}
执行结果:
解释:
因为error接口只有1个Error方法,所以我们就可以实现自己的错误类型,此处我们就是定义了myerror结构体,但是我们实现了error方法,在函数中返回时返回error,就把我们自己的错误类型反回给了error接口了
实例2:
package main import (
"fmt"
) type ErrNegativeSqrt float64 func (e ErrNegativeSqrt) Error() string { return fmt.Sprintf("cannot Sqrt negative number:%v", float64(e))
} func Sqrt(x float64) (float64, error) {
if x < {
return , ErrNegativeSqrt(x) //如果小于0,则返回我们自定义的错误
} return x, nil
} func main() {
fmt.Println(Sqrt())
_, err := Sqrt(-)
if err != nil {
switch err.(type) { //类型断言,获取错误码
case ErrNegativeSqrt:
fmt.Printf("ErrNegativeSqrt\n")
default: }
}
}
执行结果:
6.4 Reader接口
实例:
package main import (
"fmt"
_ "strings"
"time"
) type MyReader struct{} // TODO: Add a Read([]byte) (int, error) method to MyReader.
func (r MyReader) Read(b []byte) (int, error) {
b[] = 'A'
return , nil
}
func main() {
var myre MyReader
b := make([]byte, )
//for {
//r := strings.NewReader(b)
myre.Read(b)
fmt.Printf("%c\n", b[])
time.Sleep( * time.Second)
myre.Read(b)
fmt.Println(b[])
//}
}
执行结果:
解释:
定义1个Myreader结构体,下面实现reader方法,实例实现了reader方法,按照定义的read方法去操作。
6.5 Image接口
image接口底层:
type Image interface {
ColorModel() color.Model
Bounds() Rectangle
At(x, y int) color.Color
}
实例:
package main import (
"image"
"image/color" "golang.org/x/tour/pic"
//"fmt"
) type Image struct {
weight int
height int
} func (c Image) ColorModel() color.Model {
return color.RGBAModel
}
func (b *Image) Bounds() image.Rectangle {
return image.Rect(, , b.weight, b.height)
}
func (a *Image) At(x, y int) color.Color {
//fmt.Println(x, y)
return color.RGBA{uint8(x), uint8(y), , }
}
func main() {
m := &Image{, }
//m.At(225, 0)
pic.ShowImage(m) //m.At(x, y)的参数由pic传入,传入了所有情况
}
解释:
定义了image的结构体,下面实现了底层image接口的方法,所以我们就可以传入实例进行操作。
Go语言基础之18--接口编程的更多相关文章
- Python语言基础07-面向对象编程基础
本文收录在Python从入门到精通系列文章系列 1. 了解面对对象编程 活在当下的程序员应该都听过"面向对象编程"一词,也经常有人问能不能用一句话解释下什么是"面向对象编 ...
- PL/SQL语言基础
PL/SQL语言基础 进行PL/SQL编程前,要打开输出set serveroutput on 1.创建一个匿名PL/SQL块,将下列字符输出到屏幕:"My PL/SQL Block Wor ...
- Go语言基础之接口(面向对象编程下)
1 接口 1.1 接口介绍 接口(interface)是Go语言中核心部分,Go语言提供面向接口编程,那么接口是什么? 现实生活中,有许多接口的例子,比如说电子设备上的充电接口,这个充电接口能干什么, ...
- Go语言面组合式向对象编程基础总结
转自:http://blog.csdn.net/yue7603835/article/details/44282823 Go语言的面向对象编程简单而干净,通过非侵入式接口模型,否定了C/C++ Jav ...
- GO学习-(19) Go语言基础之网络编程
Go语言基础之网络编程 现在我们几乎每天都在使用互联网,我们前面已经学习了如何编写Go语言程序,但是如何才能让我们的程序通过网络互相通信呢?本章我们就一起来学习下Go语言中的网络编程. 关于网络编程其 ...
- GO学习-(14) Go语言基础之接口
Go语言基础之接口 接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节. 接口 接口类型 在Go语言中接口(interface)是一种类型,一种抽象的类 ...
- Go语言基础之接口
Go语言基础之接口 接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节. 接口 接口介绍 在Go语言中接口(interface)是一种类型,一种抽象的类 ...
- C语言与MATLAB接口 编程与实例 李传军编着
罗列一下以前自己学习C语言与MATLAB混编的笔记,顺便复习一遍. <C语言与MATLAB接口 编程与实例 李传军编着>(未看完,目前看到P106) 目录P4-8 ************ ...
- 2018.3.5 Java语言基础与面向对象编程实践
Java语言基础与面向对象编程实践 第一章 初识Java 1.Java特点 http://www.manew.com/blog-166576-20164.html Java语言面向对象的 Java语言 ...
随机推荐
- 定位程序问题出现的原因工具-jstack
jstack还可以生成线程快照 如何使用jstack: 1.打开命令行,输入jstack 在任务管理器中就可看到对应进程id 2.在命令行中输入 jstack -l 进程id 这样就可得到进程中所有的 ...
- css知多少(9)——float下篇(转)
css知多少(9)——float下篇 float内容比较多,咱们分上.下两篇来介绍,上篇已经写完,这是下篇.建议大家先把上篇看了,再来看下文,精彩内容不要掠过啊. 1. 清除float <上 ...
- Codeforces #345div1 C Table Compression (650C) 并查集
题意:给你一个n*m的矩阵,需要在不改变每一行和每一列的大小关系的情况下压缩一个矩阵,压缩后的矩阵所有数的总和尽量的小. 思路:我们有这样的初步设想:对于在一行或一列的数x,y,若x<y,则建立 ...
- POJ 1151 扫描线 线段树
题意:给定平面直角坐标系中的N个矩形,求它们的面积并. 题解:建立一个四元组(x,y1,y2,k).(假设y1<y2)用来储存每一条线,将每一条线按x坐标排序.记录所有的y坐标以后排序离散化.离 ...
- ROS探索总结(二)——ROS总体框架
个人分类: ROS 所属专栏: ROS探索总结 一. 总体结构 根据ROS系统代码的维护者和分布来标示,主要有两大部分: (1)main:核心部分,主要由Willow G ...
- final 子类禁止重写
<?php //子类中编写和父类中完全一样的函数,是对父类中的函数进行重写 class BaseClass{ public function test() { echo "BaseCl ...
- auth 权限控制
一. 权限介绍所谓权限控制,大部分是在管理后台上使用.比如超级管理员登录,会得到所有操作的控制权:认证专员,只能给会员做认证操作:审核专员,只能操作内容的审核.删除.加精等操作,以此类推.那么 Thi ...
- c语言学习笔记 const变量
在c语言的编程过程中经常会遇到有常数参加运算的运算,比如这种. int a=100*b; 这个100我们叫常数或者叫常量,但是程序中我们不推荐这种直接写常数的方法,有两个缺点. 第一是程序可读性差. ...
- BMP是可以保存alpha通道的。
一直以来都不太熟悉BMP格式,今天保存图片的时候发现bmp是可以保存透明通道的,突然想起来以前下载的一些游戏素材贴图里面就有一些bmp格式的贴图.记录一下.
- super-smack
我有个办法,不过不是用LR,是用super-smack,如果只对数据库进行抗压力测试,应该管用.Super-smack 现在是1.3版,源码下载地址如下:http://vegan.net/tony/s ...