golang入门

Go学习

环境:

操作系统:mac

安装

  1. 官网地址下载mac版本的安装包,双击按照提示安装就行了
  2. 打开terminal,输入go version,验证是否安装成功
  3. 输入cd /usr/local/go查看go的安装目录

语法

变量声明方式

package main

import(
    "fmt"
)

//声明全局变量,方法一、方法二、方法三是可以的
var gA int = 100
var gB = 200

//方法四只能在方法体内声明
// gC := 300 这里会报错

func main()  {
    //方法一:声明一个变量,默认值0
    var a int 
    fmt.Println("a = ", a)
    fmt.Printf("type of a = %T\n", a)

    //方法二:声明一个变量,初始化一个值
    var b int =100
    fmt.Println("b = ", b)
    fmt.Printf("type of b = %T\n", b)

    var bb string = "bbb"
    fmt.Printf("bb = %s, type of bb = %T\n", bb, bb)

    //方法三:在初始化时候,可以省去数据类型,通过值匹配当前变量的数据类型
    var c =100.0
    fmt.Println("c = ", c)
    fmt.Printf("type of c = %T\n", c)

    var cc = "ccc"
    fmt.Printf("cc = %s, type of cc = %T\n", cc, cc)

    //方法四:常用方法,省去var关键字,直接自动匹配
    d := 3.14
    fmt.Println("d = ", d)
    fmt.Printf("type of d = %T\n", d)

    var dd = "ddd"
    fmt.Printf("dd = %s, type of dd = %T\n", dd, dd)

    fmt.Println("--全局变量--")
    fmt.Println("gA = ", gA, "gB = ",gB)

    fmt.Println("--多变量声明--")
    //多变量声明
    var xx, yy int =100, 200
    fmt.Println("xx = ", xx, "yy = ",yy)

    //多行变量声明
    var (
        zz int = 300
        uu bool = false
    )
    fmt.Println("zz = ", zz, "uu = ",uu)

}

const和iota声明枚举

可以在const()中添加一个关键字iota,每行的iota都会累加1,第一行的iota默认为0

package main

import(
    "fmt"
)


//const定义枚举类型
const (
    //可以在const()中添加一个关键字iota,每行的iota都会累加1,第一行的iota默认为0
    BEIJING = iota*10  //iota =0 ,BEIJING = 0*10
    SHANGHAI		//iota =1, SHANGHAI = 1*10
    SHENZHEN		//iota=2, SHENZHEN = 2*10
)

const (
    a, b = iota+1, iota+2	//iota=0, a=0+1=1, b=0+2=2
    c, d					//iota=1, c=1+1=2, d=1+2=3
    e, f					//iota=2, e=2+1=3, f=2+2=4

    g, h = iota*2, iota*3	//iota=3, g=3*2=6, h=3*3=9
    i, k 					//iota=4, i=4*2=8, k=4*3=12
)


func main(){
    //const定义常量,只读
    const lengths int = 10
    // lengths = 100 //常量不允许修改
    fmt.Println("length = ", lengths)
    
    fmt.Println("BEIJING = ", BEIJING)
    fmt.Println("SHANGHAI = ", SHANGHAI)
    fmt.Println("SHENZHEN = ", SHENZHEN)

    fmt.Println("----------------")
    fmt.Println("a=",a," b=",b)
    fmt.Println("c=",c," d=",d)
    fmt.Println("e=",e," f=",f)
    fmt.Println("g=",g," h=",h)
    fmt.Println("i=",i," k=",k)

    //iota只能和const()一起使用,iota只有在const进行累加效果
    // var a int = iota 报错
}

函数

多返回值

go中函数能够返回多个返回值

package main

import(
    "fmt"
)

func main(){
    c := foo("1", 2)
    fmt.Println("c = ", c)

    m, n := foo2("123", 11)
    fmt.Println("m =",m," n=",n)

    r1, r2 := foo3("1",1)
    fmt.Println("r1 = ", r1, " r2=", r2)

    r3, r4 := foo4("1",1)
    fmt.Println("r3 = ", r3, " r4=", r4)
}


//定义一个函数入参是string类型的a,int类型的b
//返回值是int类型
func foo(a string, b int) int {
    fmt.Println("a = ", a)
    fmt.Println("b = ", b)
    c := 100
    return c
}
//定义一个函数入参是string类型的a,int类型的b
//返多个返回值,匿名的
func foo2(a string, b int) (int, int) {
    fmt.Println("a = ", a)
    fmt.Println("b = ", b)
    return 123,456
}

//定义一个函数入参是string类型的a,int类型的b
//返多个返回值,有形参名
func foo3(a string, b int) (r1 int, r2 string) {
    fmt.Println("a = ", a)
    fmt.Println("b = ", b)

    //给有名称的返回值变量赋值,作用域在整个函数{}包裹的区域
    r1 = 100
    r2 = "200"

    return r1,r2
}

//定义一个函数入参是string类型的a,int类型的b
//返多个相同类型返回值,有形参名
func foo4(a string, b int) (r3 , r4 int) {
    fmt.Println("a = ", a)
    fmt.Println("b = ", b)

    //给有名称的返回值变量赋值,作用域在整个函数{}包裹的区域
    r3 = 1000
    r4 = 2000

    return r3,r4
}

init函数和import导包

如图:

go-init函数

匿名导包和别名导包

import (
    // f "fmt" //别名导包
    // . "fmt" //将fmt包中的全部方法导入到当前包的作用域中,不需要在fmt.Println这样调用,直接Println调用 ----不推荐
    _ "fmt" //匿名导包,无法使用当前包的方法,但是会执行包的init方法
)

指针

定义:指针是一种数据类型,用于表示数据的内存地址

注意:

声明指针类型只需要在类型前加一个*,不管什么指针类型(*int,*string,*float),初始值都是nil

取变量name的首地址,也就是指针的值,需要用&name表示

package main

import (
    "fmt"
)

func main(){
    var a int = 1
    var b int  = 20
    //值传递
    swapValue(a, b)
    fmt.Println("swap value: a = ", a, " ,b = ",b)
    //指针传递, &a代表a值的地址, &b代表b值的地址
    swapPointer(&a, &b)
    fmt.Println("swap pointer: a = ", a, " ,b = ",b)

    //一级指针
    var x float64 = 1.1
    var y * float64  = &x
    //申明一个二级指针
    var pp **float64 
    pp = &y
    // &x = y
    fmt.Println("&x = ", &x, "y = ", y)
    //**pp和x值一样
    fmt.Println("pp = ", **pp)
}

func swapValue(a int, b int){
    var tmp int
    tmp = a
    b = a
    a = tmp
}

//a *int a代表一个int类型的指针
//b *int b代表一个int类型的指针
func swapPointer(a *int, b *int){
    var tmp int
    //*a 代表取出a指针指向的值
    tmp = *a 
    //*b 代表取出b执政指向的值
    *a = *b
    *b = tmp
}

defer

defer用于资源的释放,会在函数返回之前进行调用,如果有多个defer表达式,调用顺序类似于栈,越后面的defer表达式越先被调用。

package main

import ("fmt")

func main(){
    defer_test()
}

//defer函数作用是在函数返回值前调用
//调用顺序类似栈,后入先出,defer 函数3->defer 函数2->defer 函数1
func defer_test(){
    fmt.Println("defer_test")
    defer fmt.Println("defer 函数1")
    defer fmt.Println("defer 函数2")
    defer fmt.Println("defer 函数3")
}

注意:

defer是在return之前被执行的,需要注意的是return XXX这个语句不是原子语句,所以会执行XXX在执行defer有点反直觉。详细代码参考如下:

package main

import ("fmt")

func main(){
    returnAndDefer()
}

func defer_function() int{
    fmt.Println("defer func")
    return 0;
}

func return_function() int{
    fmt.Println("return func")
    return 0;
}

//这里打印的结果是:
//return func
//defer func
func returnAndDefer() int{
    defer defer_function()
    return return_function()
}

数组和动态数组

静态数组

传递值默认是值传递,即用一个拷贝值传入,拷贝的变化不会引起原数组变化。如果想要引用传递,需要借助数组指针来实现。

package main

import "fmt"

func main()  {
    //固定长度的数组,初始值是类型的初始值,int是0,string是''
    var intarr [10]int
    fmt.Printf("intarr type is %T\n", intarr)
    //初始化的固定数组
    intarr2 := [10]int{1,2,3,4}
    fmt.Printf("intarr2 type is %T\n", intarr2)
    
    //fori方式遍历数组
    //for i:=0; i<10; i++
    for i:=0; i<len(intarr); i++ {
        fmt.Println(intarr[i])
    }
    //range方式遍历数组
    for index, value := range intarr2{
        fmt.Println("index = ", index, "value = ",value)
    }
    
    fmt.Println("---普通数组---")
    printArray2(intarr2)
    for index, value := range intarr2{
        fmt.Println("index = ", index, "value = ",value)
    }

    fmt.Println("---指针数组---")
    printArray(&intarr2)
    for index, value := range intarr2{
        fmt.Println("index = ", index, "value = ",value)
    }

}

//传递一个数组指针,可以同步改变数组内外的值
func printArray(myArr *[10]int){
    for index,value := range myArr{
        fmt.Println("index = ", index, "value = ",value)
    }
    myArr[0]=111
}

func printArray2(myArr [10]int){
    for index,value := range myArr{
        fmt.Println("index = ", index, "value = ",value)
    }
    myArr[0]=111
}

动态数组(slice 切片)

默认就是引用传递

package main

import "fmt"

func main() {
    //[]中没有值就是一个动态数组,也就是slice-切片
    myArr := []int{1,2,3,4}
    fmt.Printf("myArr type is %T\n", myArr)
    
    prinArray(myArr)

    for _,value := range myArr{
        fmt.Println("value  = ", value)
    }
    
}

func prinArray(a []int){
    //引用传递
    //_表示匿名的变量,可以无需使用也不会报错
    for _, value := range a{
        fmt.Println("value = ", value)
    }
    //这里值变化,原来传入的数组值变化,这就是引用传递的用处
    a[0] = 100
}

切片

声明切片的4种方式

package main

import "fmt"

func main() {
    //1.声明slice1是一个切片,并初始化,默认值是1,2,3, len是3
    slice1 := []int{1, 2, 3}
    //打印结果是:len = 3, slice = [1 2 3]
    fmt.Printf("len = %d, slice = %v \n", len(slice1), slice1)

    //2.声明sclice2是一个切片,但是没有给slice2分配空间
    var sclice2 []int
    //给slice2初始化空间
    sclice2 = make([]int, 3)
    sclice2[0] = 100
    fmt.Printf("len = %d, slice = %v \n", len(sclice2), sclice2)

    //3.声明slice3是一个切片,同时给其分配空间,3个空间,初始化值是0
    var slice3 []int = make([]int, 3)
    fmt.Printf("len = %d, slice = %v \n", len(slice3), slice3)

    //4.通过:=推导出slice4是一个切片
    slice4 := make([]int, 3)
    fmt.Printf("len = %d, slice = %v \n", len(slice4), slice4)

    //判断一个slice是否为0
    var slice6 []int
    if slice6 == nil {
        fmt.Println("slice 是一个空切片")
    } else {
        fmt.Println("slice 不是空")
    }
}

切片的追加

切片扩容机制:

向一个容量满的slice追加一个元素,会动态开辟一个原来cap容量的空间

package main

import "fmt"

func main() {
    //定义一个整数类型切片,长度是3,容量是5
    var numbers = make([]int, 3, 5)
    //打印结果:len = 3, cap = 5, slice = [0 0 0]
    fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)

    //追加一个元素
    numbers = append(numbers, 1)
    //打印结果:len = 4, cap = 5, slice = [0 0 0 1]
    fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)

    //追加一个元素
    numbers = append(numbers, 1)
    //打印结果:len = 5, cap = 5, slice = [0 0 0 1 1]
    fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)

    //切片扩容机制:向一个容量满的slice追加一个元素,会动态开辟一个原来cap容量的空间
    numbers = append(numbers, 1)
    //打印结果:len = 6, cap = 10, slice = [0 0 0 1 1 1]
    fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)	


    //不设置cap容量,cap默认和len相同
    var numbers2 = make([]int, 3)
    fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers2), cap(numbers2), numbers2)	
}

切片截取

package main

import "fmt"

func main() {
    //定义一个整数类型切片,长度是3,容量是5
    numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    fmt.Println("numbers == ", numbers)

    //打印子切片索引从1(包含)到4(不包含4	)
    fmt.Println("numbers[1:4] == ", numbers[1:4])

    //默认下限为0
    fmt.Println("numbers[:3] == ", numbers[:3])

    //默认上限为len(numbers)
    fmt.Println("numbers[0:] == ", numbers[0:])

    //这里number1是numbers的一个切片,但是底层还是指向同一个数组
    //所以修改number1的值,也会同步修改numbers的值
    number1 := numbers[0:5]
    number1[0] = 100
    fmt.Println(numbers)
    fmt.Println(number1)

    //为了修改一个切片,不影响另外一个,可以使用copy进行一个深拷贝
    number2 := make([]int, 4)
    copy(number2, numbers)
    number2[0] = 101
    fmt.Println(numbers)
    fmt.Println(number2)

}

map

map的3种定义方式

package main

import "fmt"

func main() {
    //第一种声明方式
    //声明myMap是一个map类型,key是string类型,value是string类型
    var myMap map[string]string
    if myMap == nil {
        fmt.Println("myMap是一个空map")
    }

    //在使用map前,需要先使用make给map分配空间
    myMap = make(map[string]string, 10)

    myMap["one"] = "Java"
    myMap["two"] = "C++"
    myMap["tree"] = "Go"

    fmt.Println(myMap)

    //第二种声明方式
    myMap2 := make(map[int]string)
    myMap2[0] = "Java"
    myMap2[1] = "C++"
    myMap2[2] = "Go"
    fmt.Println(myMap2)

    //第三种声明方式
    myMap3 := map[string]string{
        "one":  "c#",
        "two":  "kotlin",
        "tree": "c",
    }
    fmt.Println(myMap3)
}

map的使用

package main

import "fmt"

func printMap(myMap map[string]string) {
    // 遍历
    for key, value := range myMap {
        fmt.Printf("key = %v, value = %v\n", key, value)
    }
}

func changeMap(myMap map[string]string) {
    //myMap是一个引用传递,在内部修改之后,会影响外面map
    myMap["England"] = "London"
}

func main() {
    cityMap := make(map[string]string)

    //添加key-value
    cityMap["China"] = "beijing"
    cityMap["Japan"] = "Tokyo"
    cityMap["USA"] = "NewYork"
    printMap(cityMap)

    //删除
    delete(cityMap, "China")

    //修改
    cityMap["USA"] = "DC"
    changeMap(cityMap)

    fmt.Println("---")
    printMap(cityMap)
}

结构体

基本概念

package main

import "fmt"

// 申明一个新的数据类型,myint是int的一个别名
type myint int

// 定义一个结构体
type Book struct {
    title  string
    author string
}

func printBook(book Book) {
    //传递book的一个副本
    book.author = "123"
}

func changeBooke2(book *Book) {
    //指针传递
    book.author = "123"
}

func main() {
    var a myint = 10
    fmt.Println(a)

    //使用一个结构体
    var book1 Book
    book1.title = "golang"
    book1.author = "ada"
    fmt.Printf("%v\n", book1)

    //值传递
    printBook(book1)
    fmt.Printf("%v\n", book1)

    //指针传递,参数是book1的地址
    changeBooke2(&book1)
    fmt.Printf("%v\n", book1)
}

封装

类名、属性名、方法名如果首字母大写表示对外(其他包)可以访问,否则只能在本包访问

package main

import "fmt"

// 定义一个结构体
//
// 如果类名首字母大写,表示其他包也能访问
type Hero struct {
    //如果属性名大写,表示该属性对外能够访问
    Name  string
    Ad    int
    Level int
}

/*
// (this Hero) -> 表示当前方法绑定到Hero结构
func (this Hero) getName()string{
    return this.Name
}

func (this Hero) SetName(name string) {
    //当前this是调用该方法的对象的一个拷贝
    this.Name = name
}

func (this Hero) show(){
    fmt.Println("Name = ", this.Name, ",Ad = ",this.Ad, ",Level = ", this.Level)
}
*/

// (this Hero) -> 表示当前方法绑定到Hero结构
func (this *Hero) getName() string {
    return this.Name
}

// 如果方法首字母大写,表示方法对外能够访问
func (this *Hero) SetName(name string) {
    this.Name = name
}

func (this *Hero) show() {
    fmt.Println("Name = ", this.Name, ",Ad = ", this.Ad, ",Level = ", this.Level)
}

func main() {
    //创建一个Hero对象
    hero := Hero{Name: "zhangshan", Ad: 11, Level: 12}
    hero.show()

    hero.SetName("zzzz")
    hero.show()
}
继承

子类继承父类,只需要将父类的类型名写入到子类结构体种,如下:

type SuperMan struct {
    Human //SuperMan继承了Human类
    level int
}
package main

import "fmt"

// 父类
type Human struct {
    name string
    sex  string
}

func (this *Human) Eat() {
    fmt.Println("Human Eat()...")
}

func (this *Human) Walk() {
    fmt.Println("Human Walk()...")
}

// =============
// 子类
type SuperMan struct {
    Human //SuperMan继承了Human类
    level int
}

// 重写父类(Human)方法
func (this *SuperMan) Eat() {
    fmt.Println("SuperMan Eat()...")
}

// 子类自己的新方法
func (this *SuperMan) Fly() {
    fmt.Println("SuperMan Fly()...")
}

func main() {
    //创建一个Human对象
    human := Human{name: "ll", sex: "男"}
    human.Eat()
    human.Walk()

    fmt.Println("=====")
    //创建一个子类,方式一
    // s := SuperMan{ Human{name: "ll", sex: "男"}, 2}
    //方式二
    var s SuperMan
    s.Human = human
    s.level = 1
    //调用父类方法
    s.Walk()
    //调用子类重写的方法
    s.Eat()
    //调用子类自己写的方法
    s.Fly()
}
多态

父类的变量(指针)指向(调用)子类的具体数据变量

package main

import "fmt"

// 本质是一个指针
type Animal interface {
    Sleep()
}

// 定一个实现Animal接口的类
type Cat struct {
}

func (this *Cat) Sleep() {
    fmt.Println("cat is sleep")
}

// 定义第二个实现Animal接口的类
type Dog struct {
}

func (this *Dog) Sleep() {
    fmt.Println("dog is sleep")
}

// 这就是多态现象
func main() {
    var animal Animal

    animal = &Cat{}
    //cat is sleep
    animal.Sleep()

    animal = &Dog{}
    //cat is sleep
    animal.Sleep()

}

接口

interface {}空接口,我们常用的数据结构:int、string、float等都实现了空接口,所以interface{}就能引用任意的数据类型,也称之为万能类型

package main

import "fmt"

// arg 使用interface{},表示是一个万能类型,可以传入任何值
func myFunc(arg interface{}) {
    fmt.Println("my func is called")
    fmt.Println(arg)

    //arg如何区分底层引用的数据类型?
    //
    //go给interface{}提供了“类型断言”的机制
    value, ok := arg.(string)
    if !ok {
        fmt.Println("arg is not srirng type")
    } else {
        fmt.Println("arg is srirng type, value = ", value)
        fmt.Printf("value type is %T\n", value)
    }
}

type zz struct {
    a string
}

func main() {

    myFunc(123)   //123
    myFunc("adv") //adv
    myFunc(3.13)  //3.13

    z := zz{a: "测试"}
    myFunc(z) //{测试}
}

反射

变量结构

一个变量内置结构被称为pair,pair包含type和value。type中又分为两类static type(int、string…)和concrete type(interface所指向的具体的数据类型,系统能够看的见的数据类型),并且一个type要么是static,要么是concrete,不会同时存在

go变量pair结构

package main

import "fmt"

type Reader interface {
    ReadBook()
}

type Writer interface {
    WriteBook()
}

// Book结构体,即实现Reader接口,又实现了Write接口
type Book struct {
}

func (this *Book) ReadBook() {
    fmt.Println("read a book")
}

func (this *Book) WriteBook() {
    fmt.Println("write a book")
}

func main() {
    var a string
    //a内部有个pair<statictype:string, value:"abc">
    a = "abc"

    //定义一个万能类型,pair<type:, value:>
    var allType interface{}
    //这是interfalce指向的数据结构就是a,因此pair<type:string, value:"abc">
    allType = a

    value, _ := allType.(string)
    fmt.Println(value)

    //-------
    fmt.Println("-------")
    //b:pair<type:Book, value:book{}地址>
    b := &Book{}

    //r:pair<type:, value:>
    var r Reader
    //r:pair<type:Book, value:book{}地址>
    r = b
    //这时候r.ReadBook相当于调用Book的ReadBook方法
    r.ReadBook()

    //r:pair<type:, value:>
    var w Writer
    //w:pair<type:Book, value:book{}地址>
    w = r.(Writer) //此处为什么断言会成功?因为r,w具体的type是一致的
    w.WriteBook()
}
reflect包

反射可以根据数据推测出你的数据类型,比较重要的两个方法

  • func Value(i interface{}) Value{…}:输入任意数据类型,返回当前数据类型的value值,接口为空返回0
  • func TypeOf(i interface{}) Type{…}:输入任意数据类型,返回当前数据类型的Type,接口为空返回nil
package main

import (
    "fmt"
    "reflect"
)

func relectNum(arg interface{}) {
    //使用反射查看变量的类型和值
    fmt.Printf("type = %v, value = %v \n", reflect.TypeOf(arg), reflect.ValueOf(arg))
}

type User struct {
    Id   int
    Name string
    Age  int
}

func (this User) Call() {
    fmt.Println("user is called...")
    fmt.Printf("%v\n", this)
}

func FindFileAndMethod(input interface{}) {
    //获取input的type
    inputType := reflect.TypeOf(input)
    fmt.Println("inputType type is ", inputType.Name())
    //获取input的value
    inputValue := reflect.ValueOf(input)
    fmt.Println("inputType value is ", inputValue)

    //通过type获取里面的字段
    //1.获取interface的reflect.Type,通过Type得到NumFeild(属性数量),进行遍历
    //2.得到每个field,数据类型
    //3.通过field又个有一个Interface()方法得到对应的value
    for i := 0; i < inputType.NumField(); i++ {
        field := inputType.Field(i)
        value := inputValue.Field(i).Interface()

        fmt.Printf("%s:%v = %v \n", field.Name, field.Type, value)
    }

    //通过type获取里面的方法(只能是可以访问的即大写字母开头的),调用
    for i := 0; i < inputType.NumMethod(); i++ {
        m := inputType.Method(i)
        fmt.Printf("%s:%v\n", m.Name, m.Type)
    }
}

func main() {
    //基本类型的反射
    var num float64 = 1.234
    relectNum(num)

    //------
    //通过user对象解析User类信息
    fmt.Println("----")
    user := User{1, "zhu", 12}
    FindFileAndMethod(user)
}

结构体标签

package main

import (
    "fmt"
    "reflect"
)

// 主要用于字段说明
type resume struct {
    Name string `info:"name" doc:"名称"`
    Sex  string `info:"sex"`
}

func findTag(str interface{}) {
    //获取结构体全部元素
    t := reflect.TypeOf(str).Elem()

    for i := 0; i < t.NumField(); i++ {
        //解析标签
        tagInfo := t.Field(i).Tag.Get("info")
        tagDoc := t.Field(i).Tag.Get("doc")
        fmt.Println("info: ", tagInfo, "doc: ", tagDoc)
    }
}

func main() {
    var re resume
    findTag(&re)
}
结构体标签在json中的应用
package main

import (
    "encoding/json"
    "fmt"
)

type Movie struct {
    Title  string   `json:title`
    Year   int      `json:year`
    Price  float32  `json:price`
    Actors []string `json:actors`
}

func main() {
    movie := Movie{"戏剧之王", 2000, 10, []string{"周星驰", "张柏芝"}}
    //将结构体转成json格式字符串 结构体---》json
    jsonStr, err := json.Marshal(movie)
    if err != nil {
        fmt.Println("json marshal err")
        return
    }
    fmt.Printf("jsonString  = %s\n", jsonStr)

    //将json字符串转成结构体 json---》结构体
    newMovie := Movie{}
    err = json.Unmarshal(jsonStr, &newMovie)
    if err != nil {
        fmt.Printf("json unmarshal err")
        return
    }
    fmt.Printf("newMovie = %v\n", newMovie)

}