在创建了一个结构体之后,结构体可能还具有一些行为,比如Person结构体还有说话、跑步、学习等,此时通过方法才能完成。
Golang
中的方法是作用在指定数据类型上的,是自定义类型。
type A struct{
Num int
}
func (a A) test(){ // A结构体有种方法叫做test()
fmt.Println(a.Num)
}
package main
import "fmt"
type Person struct{
Name string
}
func (p Person) test(){ // 定义方法test()
p.Name = "jack"
fmt.Println("test() name=", p.Name)
}
func main(){
var p Person // 创建结构体实例
p.Name = "xiaoming"
p.test() // 调用方法
fmt.Println("main() p.Name=", p.Name) // 输出是xiaoming
}
test
和结构体Person
进行绑定;只能通过Person类型的变量来进行调用func (receiver type) methodName (参数列表)(返回值列表){
方法体
return 返回值
}
receiver type:type
是结构体,该方法和type结构体进行绑定receiver
:是type
类型的一个变量实例return
语句不是必须的package main
import "fmt"
type Person struct{
Name string
}
func (p Person) speak(){ // 定义方法speak():最直接
fmt.Println(p.Name, "是一个好人!")
}
func (p Person) jisuan(){ // 方法中没有参数
res := 0
for i := 1;i <= 1000;i++{
res += i
}
fmt.Println(p.Name, "计算的结果是=", res)
}
func (p Person) jisuan2(n int){ // 方法中接受一个参数
res := 0
for i := 1;i <= n;i++{
res += i
}
fmt.Println(p.Name, "计算的结果是=", res)
}
func (p Person) getSum(n1, n2 int) int{ // 方法中接受多个参数和返回值
return n1 + n2
}
func main(){
var p Person // 创建结构体实例
p.Name = "Tom"
p.speak() // 调用方法
p.jisuan()
p.jisuan2()
res := p.getSum(10,20) // 调用方法,并用res接受结果
fmt.Println(res)
}
package main
import "fmt"
// 定义结构体
type Person struct{
Name string
}
// 定义一个方法
func (p Person) getSum(n1, n2 int) int{ // 方法中接受多个参数和返回值
return n1 + n2
}
func main(){
var p Person // 创建结构体实例 p
n1 := 10
n2 := 20
res := p.getSum(n1, n2) // 调用方法,并用res接受结果;调用的时候创建独立的栈
fmt.Println(res)
}
package main
import "fmt"
type Circle struct{
radius float64
}
// 声明一个方法area和Circle进行绑定,返回的值面积
func (c Circle) area() float64{
return 3.14 * c.radius * c.radius
}
// 为了提高效率,通常将方法和结构体的指针类型进行绑定
func (c *Circle) area2() float64{
// 标准写法:c是指针,(*c).radius
//return 3.14 * (*c).radius * (*c).radius
fmt.Println("c 是 *Circle 指向的地址", c) // c本身就是指针
c.radius = 10
return 3.14 * c.radius * c.radius
}
func main(){
var c Circle // 声明结构体
c.radius = 4.0 // 半径
res := c.area() // 调用方法
fmt.Println(res)
var c Circle
fmt.Println("main c 结构体变量的地址 = %p", &c) // & 表示的是取地址
c.radius = 5.0
res2 := (&c).area2()
// 编译器底层优化:(&c).area2() 等价于 c.area2()
fmt.Println("面积=", res2)
fmt.Println("c.radius = ", c.radius) // 10
}
int 、float32
等String()
方法,fmt.Println()
默认会调用这个变量的String()
进行输出。package main
import "fmt"
type integer int
func (i integer) print(){
fmt.Println(i)
}
func (i *integer) change(){
*i = *i + 1
}
// 定义结构体
type Student struct{
Name string
Age int
}
// 定义方法
func (stu *Student) String() string{
str := fmt.Sprintf("Name=[%v] Age=[%v]", stu.Name, stu.Age)
return str
}
func main(){
var i integer = 10
i.print() // 10
i.change()
fmt.Println(i) // 11,通过指针的引用传递进行修改
stu := Student{
Name := "tom",
Age := 20,
}
fmt.Println(&stu) // 传入的是地址
}
package main
import "fmt"
type MethodUtils struct{
// 结构体中可以没有任何字段
}
// 方法中没有参数和返回值:打印10 * 8的星号
func (mu MethodUtlis) Print(){
for i:= 1;i <= 10;i++{
for j:=1; j <= 8;j++{
fmt.Print("*")
}
fmt.Println() // 换行作用
}
}
// 方法中带有参数和返回值:求面积
func (mu MethodUtlis) area(lenght, width float64) (float64){
return length * width
}
// 判断奇偶数
func (mu *MehtdoUtlis) JudgeNum(n int){
if n % == 0{
fmt.Println("偶数")
}else{
fmt.Println("奇数")
}
}
// 根据输入的行数和列数,打印符号
func (mu *MethodUtlis) PrintStr(m int, n int, key string){
for i := 1;i<=m;i++{
for j := 1;j<=n;j++{
fmt.Println(key)
}
fmt.Println() // 换行操作
}
}
func main(){
var mu MethodUtlis // 创建结构体实例和调用方法
mu.Print()
res := mu.area(2.5, 8.4)
fmt.Println(res)
mu.JudgeNum(10)
mu.PrintStr(5,7,"*")
}
package main
import "fmt"
// 实现简易计算器
type Calcuator struct{
Num1 float64
Num2 float64
}
// 方式1:写出4种方法单独实现
func (cal *Calcuator) getSum() float64{
// 标注形式:return (*cal).Num1 +(*cal).Num2
return cal.Num1 + cal.Num2
}
// 方式2:用一个方法实现
func (cal * Calcuator) getRes(operator byte) float64{
res := 0.0
switch operator{
case '+':
res = cal.Num1 + cal.Num2
case '-':
res = cal.Num1 - cal.Num2
default:
fmt.Println("运算符有问题")
}
return res
}
func main(){
var cal Calcuator
// 方式1
cal.Num1 = 1.2
cal.Num2 = 2.3
fmt.Printf("sum=%v \n", fmt.Sprintf("%.2f", cal.getSum()))
// 方式2
res := cal.getRes("+") // 调用方法,传入参数
fmt.Println("res",res)
}
package main
import "fmt"
type Person struct{
Name string
}
// 普通函数
func test01(p Person){
fmt.Println(p.Name)
}
func test02(p *Person){
fmt.Println(p.Name)
}
// 方法
func (p Person) test03(){
p.Name = 'jack'
fmt.Println("test03() = ",p.Name) // jack
}
func (p *Person) test04(){
p.Name = "mary"
fmt.Println(p.Name) // mary
}
func main(){
p := Person("tom")
test01(p) // 必须传入值类型,和上面的方法必须保持一致
test02(&p) // 必须传入指针类型
// 比较3和4的区别
p.test03()
fmt.Println("main() p.name=", p.name) // tom
(&p).test03() // 编译器内部处理;形式上传入的地址,仍然是值拷贝
fmt.Println("main() p.name=", p.name) // tom
(&p).test04()
fmt.Println("main() p.Name=", p.Name) // mary
p.test04() // 形式上传入的值类型,仍然是地址拷贝
fmt.Println("main() p.Name=", p.Name) // mary
}
总结:到底是哪种拷贝方式,主要是看方法中传递是值类型还是指针类型。注意观察和方法绑定的是什么类型。
package main
import "fmt"
// 1. 先定义结构体
type Student struct{
name string
gender string
age int
id int
score float64
}
type Box struct{
length float64
width float64
height float64
}
// 2. 根据结构体写出方法
func (s *Student) say() string{
infoStr := fmt.Sprintf("name=[%v] gender=[%v] age=[%v] id=[%v] score=[%v]", s.name, s.gender, s.age, s.id, s.score)
return infoStr
}
func (box *Box) getVolumn() float64{
return box.length * box.width * box.height
}
func main(){
// 3. 创建方法的实例,传入具体信息
var s = Student{
name: "tom",
gender: "male",
age: 18,
id: 100,
score: 90,
}
fmt.Println(s.say())
// 求体积
var box Box // 创建一个实例,同时定义3个属性值
box.length = 1.1
box.width = 2.2
box.height = 2.0
volumn := box.getVolumn() // 调用方法
fmt.Println("体积为=%.2f", volumn)
}
package main
import "fmt"
type Stu struct{
Name string
Age int
}
func main(){
// 要注意顺序
var stu1 = Stu{"小明", 20}
stu2 := Stu{"小明", 20}
// 顺序无关
var stu3 = Stu {
Name: "小明",
Age: 20,
}
stu4 := Stu{
Age: 20,
Name: "小明"
}
fmt.Println(stu1, stu2, stu3, stu4)
// 方式2:返回结构体的指针类型
var stu5 = &Stu{"小明", 20}
stu6 := &Stu{"小明", 20}
var stu7 = &Stu {
Name: "小明",
Age: 20,
}
stu8 := &Stu{
Age: 20,
Name: "小明"
}
fmt.Println(*stu5, *stu6, *stu7, *stu8)
}
当创建的结构体首字母是大写的,外部可以直接调用
但是一般是小写的,那么如何使用呢?此时使用到的是工厂模式。