├── .gitignore ├── README.md ├── 其他 ├── grpc-case │ ├── go.mod │ ├── hello-client │ │ ├── main.go │ │ └── proto │ │ │ ├── hello.pb.go │ │ │ ├── hello.proto │ │ │ └── hello_grpc.pb.go │ └── hello-server │ │ ├── main.go │ │ └── proto │ │ ├── hello.pb.go │ │ ├── hello.proto │ │ └── hello_grpc.pb.go └── kafka-case │ ├── consumer │ └── main.go │ ├── go.mod │ └── producer │ └── main.go ├── 基础 ├── map │ └── map.go ├── 二维数组 │ └── 二维数组.go ├── 切片 │ ├── 切片入门.go │ └── 斐波拉契数列.go ├── 匿名函数 │ └── 匿名函数入门.go ├── 单元测试 │ ├── 单元测试 │ │ ├── hello.go │ │ └── hello_test.go │ └── 单元测试介绍.md ├── 多态 │ └── 多态.go ├── 接口 │ ├── GO自带排序接口.go │ ├── 接口入门.go │ ├── 接口得继承.go │ ├── 接口的细节1.go │ ├── 接口的细节2.go │ ├── 接口的细节3.go │ └── 空接口的使用.go ├── 接口和继承 │ └── 接口和继承的关系入门.go ├── 数组 │ └── 数组.go ├── 方法 │ └── GO的方法.go ├── 管道 │ ├── channel入门.go │ ├── select读取管道.go │ ├── 协程和管道综合使用.go │ ├── 空接口类型channel.go │ └── 管道的关闭和遍历.go ├── 类型断言 │ ├── 类型断言入门.go │ ├── 类型断言实践.go │ └── 类型断言应用.go ├── 结构体 │ ├── 结构体.go │ └── 结构体序列化.go ├── 继承 │ └── GO的继承.go ├── 递归 │ └── 递归.go ├── 错误处理 │ └── 函数错误异常处理.go ├── 闭包 │ └── 闭包.go └── 随机数 │ └── 随机数.go └── 进阶 ├── _context ├── introduce.go ├── one.go ├── three.go └── two.go ├── _goroutine ├── 介绍.go ├── 入门.go ├── 异常处理.go ├── 控制协程数量.go ├── 统计协程执行时间.go ├── 设置CPU数量.go └── 资源竞争.go ├── _waitGroup ├── introduce.go ├── one.go └── two.go ├── 反射 ├── 反射介绍.go ├── 反射入门.go ├── 反射的实例3.go ├── 反射的实践1.go └── 反射的实践2.go ├── 文件操作 ├── 文件操作基础.go ├── 文件的写入.go ├── 文件的创建和写入.go ├── 文件的拷贝.go ├── 文件的读取.go ├── 文件的读取和写入.go ├── 文件目录是否存在.go └── 统计文件字符个数.go ├── 泛型 ├── base.go ├── four.go ├── one.go ├── three.go └── two.go └── 网络编程 ├── client.go ├── server.go └── 网络编程介绍.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.sum -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-base 2 | go的各种case 3 | -------------------------------------------------------------------------------- /其他/grpc-case/go.mod: -------------------------------------------------------------------------------- 1 | module grpc-case 2 | 3 | go 1.19 4 | 5 | require ( 6 | google.golang.org/grpc v1.55.0 7 | google.golang.org/protobuf v1.30.0 8 | ) 9 | 10 | require ( 11 | github.com/golang/protobuf v1.5.3 // indirect 12 | golang.org/x/net v0.8.0 // indirect 13 | golang.org/x/sys v0.6.0 // indirect 14 | golang.org/x/text v0.8.0 // indirect 15 | google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /其他/grpc-case/hello-client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "google.golang.org/grpc" 7 | "google.golang.org/grpc/credentials/insecure" 8 | "grpc-case/hello-server/proto" 9 | "log" 10 | ) 11 | 12 | type ClientTokenAuth struct{} 13 | 14 | func (c ClientTokenAuth) GetRequestMetadata(ctx context.Context, url ...string) (map[string]string, error) { 15 | return map[string]string{ 16 | "appid": "123456", 17 | "appkey": "golang", 18 | }, nil 19 | } 20 | func (c ClientTokenAuth) RequireTransportSecurity() bool { 21 | return false 22 | } 23 | 24 | func main() { 25 | // 连接服务 26 | var opts []grpc.DialOption 27 | opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) 28 | opts = append(opts, grpc.WithPerRPCCredentials(new(ClientTokenAuth))) 29 | conn, err := grpc.Dial("127.0.0.1:8888", opts...) 30 | if err != nil { 31 | log.Fatal("failed conn grpc server:", err) 32 | } 33 | defer func() { 34 | err = conn.Close() 35 | if err != nil { 36 | log.Fatal("failed conn close:", err) 37 | } 38 | }() 39 | 40 | // 创建客户端 41 | client := service.NewSayHelloClient(conn) 42 | 43 | // 调用服务端api 44 | res, _ := client.SayHello(context.TODO(), &service.HelloRequest{Name: "张三"}) 45 | 46 | fmt.Println(res) 47 | } 48 | -------------------------------------------------------------------------------- /其他/grpc-case/hello-client/proto/hello.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.30.0 4 | // protoc v4.23.1 5 | // source: hello.proto 6 | 7 | package service 8 | 9 | import ( 10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 12 | reflect "reflect" 13 | sync "sync" 14 | ) 15 | 16 | const ( 17 | // Verify that this generated code is sufficiently up-to-date. 18 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 19 | // Verify that runtime/protoimpl is sufficiently up-to-date. 20 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 21 | ) 22 | 23 | type HelloRequest struct { 24 | state protoimpl.MessageState 25 | sizeCache protoimpl.SizeCache 26 | unknownFields protoimpl.UnknownFields 27 | 28 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 29 | } 30 | 31 | func (x *HelloRequest) Reset() { 32 | *x = HelloRequest{} 33 | if protoimpl.UnsafeEnabled { 34 | mi := &file_hello_proto_msgTypes[0] 35 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 36 | ms.StoreMessageInfo(mi) 37 | } 38 | } 39 | 40 | func (x *HelloRequest) String() string { 41 | return protoimpl.X.MessageStringOf(x) 42 | } 43 | 44 | func (*HelloRequest) ProtoMessage() {} 45 | 46 | func (x *HelloRequest) ProtoReflect() protoreflect.Message { 47 | mi := &file_hello_proto_msgTypes[0] 48 | if protoimpl.UnsafeEnabled && x != nil { 49 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 50 | if ms.LoadMessageInfo() == nil { 51 | ms.StoreMessageInfo(mi) 52 | } 53 | return ms 54 | } 55 | return mi.MessageOf(x) 56 | } 57 | 58 | // Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead. 59 | func (*HelloRequest) Descriptor() ([]byte, []int) { 60 | return file_hello_proto_rawDescGZIP(), []int{0} 61 | } 62 | 63 | func (x *HelloRequest) GetName() string { 64 | if x != nil { 65 | return x.Name 66 | } 67 | return "" 68 | } 69 | 70 | type HelloResponse struct { 71 | state protoimpl.MessageState 72 | sizeCache protoimpl.SizeCache 73 | unknownFields protoimpl.UnknownFields 74 | 75 | Res string `protobuf:"bytes,1,opt,name=res,proto3" json:"res,omitempty"` 76 | } 77 | 78 | func (x *HelloResponse) Reset() { 79 | *x = HelloResponse{} 80 | if protoimpl.UnsafeEnabled { 81 | mi := &file_hello_proto_msgTypes[1] 82 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 83 | ms.StoreMessageInfo(mi) 84 | } 85 | } 86 | 87 | func (x *HelloResponse) String() string { 88 | return protoimpl.X.MessageStringOf(x) 89 | } 90 | 91 | func (*HelloResponse) ProtoMessage() {} 92 | 93 | func (x *HelloResponse) ProtoReflect() protoreflect.Message { 94 | mi := &file_hello_proto_msgTypes[1] 95 | if protoimpl.UnsafeEnabled && x != nil { 96 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 97 | if ms.LoadMessageInfo() == nil { 98 | ms.StoreMessageInfo(mi) 99 | } 100 | return ms 101 | } 102 | return mi.MessageOf(x) 103 | } 104 | 105 | // Deprecated: Use HelloResponse.ProtoReflect.Descriptor instead. 106 | func (*HelloResponse) Descriptor() ([]byte, []int) { 107 | return file_hello_proto_rawDescGZIP(), []int{1} 108 | } 109 | 110 | func (x *HelloResponse) GetRes() string { 111 | if x != nil { 112 | return x.Res 113 | } 114 | return "" 115 | } 116 | 117 | var File_hello_proto protoreflect.FileDescriptor 118 | 119 | var file_hello_proto_rawDesc = []byte{ 120 | 0x0a, 0x0b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x22, 0x0a, 121 | 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 122 | 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 123 | 0x65, 0x22, 0x21, 0x0a, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 124 | 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 125 | 0x03, 0x72, 0x65, 0x73, 0x32, 0x37, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 126 | 0x12, 0x2b, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x0d, 0x2e, 0x48, 127 | 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x48, 0x65, 128 | 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x0b, 0x5a, 129 | 0x09, 0x2e, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 130 | 0x6f, 0x33, 131 | } 132 | 133 | var ( 134 | file_hello_proto_rawDescOnce sync.Once 135 | file_hello_proto_rawDescData = file_hello_proto_rawDesc 136 | ) 137 | 138 | func file_hello_proto_rawDescGZIP() []byte { 139 | file_hello_proto_rawDescOnce.Do(func() { 140 | file_hello_proto_rawDescData = protoimpl.X.CompressGZIP(file_hello_proto_rawDescData) 141 | }) 142 | return file_hello_proto_rawDescData 143 | } 144 | 145 | var file_hello_proto_msgTypes = make([]protoimpl.MessageInfo, 2) 146 | var file_hello_proto_goTypes = []interface{}{ 147 | (*HelloRequest)(nil), // 0: HelloRequest 148 | (*HelloResponse)(nil), // 1: HelloResponse 149 | } 150 | var file_hello_proto_depIdxs = []int32{ 151 | 0, // 0: SayHello.SayHello:input_type -> HelloRequest 152 | 1, // 1: SayHello.SayHello:output_type -> HelloResponse 153 | 1, // [1:2] is the sub-list for method output_type 154 | 0, // [0:1] is the sub-list for method input_type 155 | 0, // [0:0] is the sub-list for extension type_name 156 | 0, // [0:0] is the sub-list for extension extendee 157 | 0, // [0:0] is the sub-list for field type_name 158 | } 159 | 160 | func init() { file_hello_proto_init() } 161 | func file_hello_proto_init() { 162 | if File_hello_proto != nil { 163 | return 164 | } 165 | if !protoimpl.UnsafeEnabled { 166 | file_hello_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 167 | switch v := v.(*HelloRequest); i { 168 | case 0: 169 | return &v.state 170 | case 1: 171 | return &v.sizeCache 172 | case 2: 173 | return &v.unknownFields 174 | default: 175 | return nil 176 | } 177 | } 178 | file_hello_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 179 | switch v := v.(*HelloResponse); i { 180 | case 0: 181 | return &v.state 182 | case 1: 183 | return &v.sizeCache 184 | case 2: 185 | return &v.unknownFields 186 | default: 187 | return nil 188 | } 189 | } 190 | } 191 | type x struct{} 192 | out := protoimpl.TypeBuilder{ 193 | File: protoimpl.DescBuilder{ 194 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 195 | RawDescriptor: file_hello_proto_rawDesc, 196 | NumEnums: 0, 197 | NumMessages: 2, 198 | NumExtensions: 0, 199 | NumServices: 1, 200 | }, 201 | GoTypes: file_hello_proto_goTypes, 202 | DependencyIndexes: file_hello_proto_depIdxs, 203 | MessageInfos: file_hello_proto_msgTypes, 204 | }.Build() 205 | File_hello_proto = out.File 206 | file_hello_proto_rawDesc = nil 207 | file_hello_proto_goTypes = nil 208 | file_hello_proto_depIdxs = nil 209 | } 210 | -------------------------------------------------------------------------------- /其他/grpc-case/hello-client/proto/hello.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = ".;service"; 4 | 5 | service SayHello { 6 | rpc SayHello(HelloRequest) returns (HelloResponse){ } 7 | } 8 | message HelloRequest{ 9 | string name = 1; 10 | } 11 | message HelloResponse{ 12 | string res = 1; 13 | } -------------------------------------------------------------------------------- /其他/grpc-case/hello-client/proto/hello_grpc.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT. 2 | // versions: 3 | // - protoc-gen-go-grpc v1.3.0 4 | // - protoc v4.23.1 5 | // source: hello.proto 6 | 7 | package service 8 | 9 | import ( 10 | context "context" 11 | grpc "google.golang.org/grpc" 12 | codes "google.golang.org/grpc/codes" 13 | status "google.golang.org/grpc/status" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file 17 | // is compatible with the grpc package it is being compiled against. 18 | // Requires gRPC-Go v1.32.0 or later. 19 | const _ = grpc.SupportPackageIsVersion7 20 | 21 | const ( 22 | SayHello_SayHello_FullMethodName = "/SayHello/SayHello" 23 | ) 24 | 25 | // SayHelloClient is the client API for SayHello service. 26 | // 27 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. 28 | type SayHelloClient interface { 29 | SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) 30 | } 31 | 32 | type sayHelloClient struct { 33 | cc grpc.ClientConnInterface 34 | } 35 | 36 | func NewSayHelloClient(cc grpc.ClientConnInterface) SayHelloClient { 37 | return &sayHelloClient{cc} 38 | } 39 | 40 | func (c *sayHelloClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) { 41 | out := new(HelloResponse) 42 | err := c.cc.Invoke(ctx, SayHello_SayHello_FullMethodName, in, out, opts...) 43 | if err != nil { 44 | return nil, err 45 | } 46 | return out, nil 47 | } 48 | 49 | // SayHelloServer is the server API for SayHello service. 50 | // All implementations must embed UnimplementedSayHelloServer 51 | // for forward compatibility 52 | type SayHelloServer interface { 53 | SayHello(context.Context, *HelloRequest) (*HelloResponse, error) 54 | mustEmbedUnimplementedSayHelloServer() 55 | } 56 | 57 | // UnimplementedSayHelloServer must be embedded to have forward compatible implementations. 58 | type UnimplementedSayHelloServer struct { 59 | } 60 | 61 | func (UnimplementedSayHelloServer) SayHello(context.Context, *HelloRequest) (*HelloResponse, error) { 62 | return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented") 63 | } 64 | func (UnimplementedSayHelloServer) mustEmbedUnimplementedSayHelloServer() {} 65 | 66 | // UnsafeSayHelloServer may be embedded to opt out of forward compatibility for this service. 67 | // Use of this interface is not recommended, as added methods to SayHelloServer will 68 | // result in compilation errors. 69 | type UnsafeSayHelloServer interface { 70 | mustEmbedUnimplementedSayHelloServer() 71 | } 72 | 73 | func RegisterSayHelloServer(s grpc.ServiceRegistrar, srv SayHelloServer) { 74 | s.RegisterService(&SayHello_ServiceDesc, srv) 75 | } 76 | 77 | func _SayHello_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 78 | in := new(HelloRequest) 79 | if err := dec(in); err != nil { 80 | return nil, err 81 | } 82 | if interceptor == nil { 83 | return srv.(SayHelloServer).SayHello(ctx, in) 84 | } 85 | info := &grpc.UnaryServerInfo{ 86 | Server: srv, 87 | FullMethod: SayHello_SayHello_FullMethodName, 88 | } 89 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 90 | return srv.(SayHelloServer).SayHello(ctx, req.(*HelloRequest)) 91 | } 92 | return interceptor(ctx, in, info, handler) 93 | } 94 | 95 | // SayHello_ServiceDesc is the grpc.ServiceDesc for SayHello service. 96 | // It's only intended for direct use with grpc.RegisterService, 97 | // and not to be introspected or modified (even as a copy) 98 | var SayHello_ServiceDesc = grpc.ServiceDesc{ 99 | ServiceName: "SayHello", 100 | HandlerType: (*SayHelloServer)(nil), 101 | Methods: []grpc.MethodDesc{ 102 | { 103 | MethodName: "SayHello", 104 | Handler: _SayHello_SayHello_Handler, 105 | }, 106 | }, 107 | Streams: []grpc.StreamDesc{}, 108 | Metadata: "hello.proto", 109 | } 110 | -------------------------------------------------------------------------------- /其他/grpc-case/hello-server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "google.golang.org/grpc" 8 | "google.golang.org/grpc/metadata" 9 | "grpc-case/hello-server/proto" 10 | "log" 11 | "net" 12 | ) 13 | 14 | type server struct { 15 | service.UnimplementedSayHelloServer 16 | } 17 | 18 | func (s *server) SayHello(ctx context.Context, req *service.HelloRequest) (*service.HelloResponse, error) { 19 | // 校验客户端 20 | md, ok := metadata.FromIncomingContext(ctx) 21 | if !ok { 22 | return nil, errors.New("客户端未传token!") 23 | } 24 | var appid, appkey string 25 | if v, ok := md["appid"]; ok { 26 | appid = v[0] 27 | } 28 | if v, ok := md["appkey"]; ok { 29 | appkey = v[0] 30 | } 31 | if appid != "123456" || appkey != "golang" { 32 | return nil, errors.New("token不正确!") 33 | } 34 | // 业务逻辑 35 | fmt.Println("client:", req.Name) 36 | return &service.HelloResponse{Res: "hello," + req.Name}, nil 37 | } 38 | 39 | func main() { 40 | // 创建grpc服务 41 | grpcServer := grpc.NewServer() 42 | // 注册我们自定义的服务到grpc服务 43 | service.RegisterSayHelloServer(grpcServer, &server{}) 44 | 45 | // 监听端口 46 | listener, err := net.Listen("tcp", ":8888") 47 | if err != nil { 48 | log.Fatal("failed listen:", err) 49 | } 50 | 51 | // 启动grpc服务 52 | err = grpcServer.Serve(listener) 53 | if err != nil { 54 | log.Fatal("failed start grpc server:", err) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /其他/grpc-case/hello-server/proto/hello.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.30.0 4 | // protoc v4.23.1 5 | // source: hello.proto 6 | 7 | package service 8 | 9 | import ( 10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 12 | reflect "reflect" 13 | sync "sync" 14 | ) 15 | 16 | const ( 17 | // Verify that this generated code is sufficiently up-to-date. 18 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 19 | // Verify that runtime/protoimpl is sufficiently up-to-date. 20 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 21 | ) 22 | 23 | type HelloRequest struct { 24 | state protoimpl.MessageState 25 | sizeCache protoimpl.SizeCache 26 | unknownFields protoimpl.UnknownFields 27 | 28 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 29 | } 30 | 31 | func (x *HelloRequest) Reset() { 32 | *x = HelloRequest{} 33 | if protoimpl.UnsafeEnabled { 34 | mi := &file_hello_proto_msgTypes[0] 35 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 36 | ms.StoreMessageInfo(mi) 37 | } 38 | } 39 | 40 | func (x *HelloRequest) String() string { 41 | return protoimpl.X.MessageStringOf(x) 42 | } 43 | 44 | func (*HelloRequest) ProtoMessage() {} 45 | 46 | func (x *HelloRequest) ProtoReflect() protoreflect.Message { 47 | mi := &file_hello_proto_msgTypes[0] 48 | if protoimpl.UnsafeEnabled && x != nil { 49 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 50 | if ms.LoadMessageInfo() == nil { 51 | ms.StoreMessageInfo(mi) 52 | } 53 | return ms 54 | } 55 | return mi.MessageOf(x) 56 | } 57 | 58 | // Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead. 59 | func (*HelloRequest) Descriptor() ([]byte, []int) { 60 | return file_hello_proto_rawDescGZIP(), []int{0} 61 | } 62 | 63 | func (x *HelloRequest) GetName() string { 64 | if x != nil { 65 | return x.Name 66 | } 67 | return "" 68 | } 69 | 70 | type HelloResponse struct { 71 | state protoimpl.MessageState 72 | sizeCache protoimpl.SizeCache 73 | unknownFields protoimpl.UnknownFields 74 | 75 | Res string `protobuf:"bytes,1,opt,name=res,proto3" json:"res,omitempty"` 76 | } 77 | 78 | func (x *HelloResponse) Reset() { 79 | *x = HelloResponse{} 80 | if protoimpl.UnsafeEnabled { 81 | mi := &file_hello_proto_msgTypes[1] 82 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 83 | ms.StoreMessageInfo(mi) 84 | } 85 | } 86 | 87 | func (x *HelloResponse) String() string { 88 | return protoimpl.X.MessageStringOf(x) 89 | } 90 | 91 | func (*HelloResponse) ProtoMessage() {} 92 | 93 | func (x *HelloResponse) ProtoReflect() protoreflect.Message { 94 | mi := &file_hello_proto_msgTypes[1] 95 | if protoimpl.UnsafeEnabled && x != nil { 96 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 97 | if ms.LoadMessageInfo() == nil { 98 | ms.StoreMessageInfo(mi) 99 | } 100 | return ms 101 | } 102 | return mi.MessageOf(x) 103 | } 104 | 105 | // Deprecated: Use HelloResponse.ProtoReflect.Descriptor instead. 106 | func (*HelloResponse) Descriptor() ([]byte, []int) { 107 | return file_hello_proto_rawDescGZIP(), []int{1} 108 | } 109 | 110 | func (x *HelloResponse) GetRes() string { 111 | if x != nil { 112 | return x.Res 113 | } 114 | return "" 115 | } 116 | 117 | var File_hello_proto protoreflect.FileDescriptor 118 | 119 | var file_hello_proto_rawDesc = []byte{ 120 | 0x0a, 0x0b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x22, 0x0a, 121 | 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 122 | 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 123 | 0x65, 0x22, 0x21, 0x0a, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 124 | 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 125 | 0x03, 0x72, 0x65, 0x73, 0x32, 0x37, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 126 | 0x12, 0x2b, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x0d, 0x2e, 0x48, 127 | 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x48, 0x65, 128 | 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x0b, 0x5a, 129 | 0x09, 0x2e, 0x3b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 130 | 0x6f, 0x33, 131 | } 132 | 133 | var ( 134 | file_hello_proto_rawDescOnce sync.Once 135 | file_hello_proto_rawDescData = file_hello_proto_rawDesc 136 | ) 137 | 138 | func file_hello_proto_rawDescGZIP() []byte { 139 | file_hello_proto_rawDescOnce.Do(func() { 140 | file_hello_proto_rawDescData = protoimpl.X.CompressGZIP(file_hello_proto_rawDescData) 141 | }) 142 | return file_hello_proto_rawDescData 143 | } 144 | 145 | var file_hello_proto_msgTypes = make([]protoimpl.MessageInfo, 2) 146 | var file_hello_proto_goTypes = []interface{}{ 147 | (*HelloRequest)(nil), // 0: HelloRequest 148 | (*HelloResponse)(nil), // 1: HelloResponse 149 | } 150 | var file_hello_proto_depIdxs = []int32{ 151 | 0, // 0: SayHello.SayHello:input_type -> HelloRequest 152 | 1, // 1: SayHello.SayHello:output_type -> HelloResponse 153 | 1, // [1:2] is the sub-list for method output_type 154 | 0, // [0:1] is the sub-list for method input_type 155 | 0, // [0:0] is the sub-list for extension type_name 156 | 0, // [0:0] is the sub-list for extension extendee 157 | 0, // [0:0] is the sub-list for field type_name 158 | } 159 | 160 | func init() { file_hello_proto_init() } 161 | func file_hello_proto_init() { 162 | if File_hello_proto != nil { 163 | return 164 | } 165 | if !protoimpl.UnsafeEnabled { 166 | file_hello_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 167 | switch v := v.(*HelloRequest); i { 168 | case 0: 169 | return &v.state 170 | case 1: 171 | return &v.sizeCache 172 | case 2: 173 | return &v.unknownFields 174 | default: 175 | return nil 176 | } 177 | } 178 | file_hello_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 179 | switch v := v.(*HelloResponse); i { 180 | case 0: 181 | return &v.state 182 | case 1: 183 | return &v.sizeCache 184 | case 2: 185 | return &v.unknownFields 186 | default: 187 | return nil 188 | } 189 | } 190 | } 191 | type x struct{} 192 | out := protoimpl.TypeBuilder{ 193 | File: protoimpl.DescBuilder{ 194 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 195 | RawDescriptor: file_hello_proto_rawDesc, 196 | NumEnums: 0, 197 | NumMessages: 2, 198 | NumExtensions: 0, 199 | NumServices: 1, 200 | }, 201 | GoTypes: file_hello_proto_goTypes, 202 | DependencyIndexes: file_hello_proto_depIdxs, 203 | MessageInfos: file_hello_proto_msgTypes, 204 | }.Build() 205 | File_hello_proto = out.File 206 | file_hello_proto_rawDesc = nil 207 | file_hello_proto_goTypes = nil 208 | file_hello_proto_depIdxs = nil 209 | } 210 | -------------------------------------------------------------------------------- /其他/grpc-case/hello-server/proto/hello.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = ".;service"; 4 | 5 | service SayHello { 6 | rpc SayHello(HelloRequest) returns (HelloResponse){ } 7 | } 8 | message HelloRequest{ 9 | string name = 1; 10 | } 11 | message HelloResponse{ 12 | string res = 1; 13 | } -------------------------------------------------------------------------------- /其他/grpc-case/hello-server/proto/hello_grpc.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT. 2 | // versions: 3 | // - protoc-gen-go-grpc v1.3.0 4 | // - protoc v4.23.1 5 | // source: hello.proto 6 | 7 | package service 8 | 9 | import ( 10 | context "context" 11 | grpc "google.golang.org/grpc" 12 | codes "google.golang.org/grpc/codes" 13 | status "google.golang.org/grpc/status" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file 17 | // is compatible with the grpc package it is being compiled against. 18 | // Requires gRPC-Go v1.32.0 or later. 19 | const _ = grpc.SupportPackageIsVersion7 20 | 21 | const ( 22 | SayHello_SayHello_FullMethodName = "/SayHello/SayHello" 23 | ) 24 | 25 | // SayHelloClient is the client API for SayHello service. 26 | // 27 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. 28 | type SayHelloClient interface { 29 | SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) 30 | } 31 | 32 | type sayHelloClient struct { 33 | cc grpc.ClientConnInterface 34 | } 35 | 36 | func NewSayHelloClient(cc grpc.ClientConnInterface) SayHelloClient { 37 | return &sayHelloClient{cc} 38 | } 39 | 40 | func (c *sayHelloClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) { 41 | out := new(HelloResponse) 42 | err := c.cc.Invoke(ctx, SayHello_SayHello_FullMethodName, in, out, opts...) 43 | if err != nil { 44 | return nil, err 45 | } 46 | return out, nil 47 | } 48 | 49 | // SayHelloServer is the server API for SayHello service. 50 | // All implementations must embed UnimplementedSayHelloServer 51 | // for forward compatibility 52 | type SayHelloServer interface { 53 | SayHello(context.Context, *HelloRequest) (*HelloResponse, error) 54 | mustEmbedUnimplementedSayHelloServer() 55 | } 56 | 57 | // UnimplementedSayHelloServer must be embedded to have forward compatible implementations. 58 | type UnimplementedSayHelloServer struct { 59 | } 60 | 61 | func (UnimplementedSayHelloServer) SayHello(context.Context, *HelloRequest) (*HelloResponse, error) { 62 | return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented") 63 | } 64 | func (UnimplementedSayHelloServer) mustEmbedUnimplementedSayHelloServer() {} 65 | 66 | // UnsafeSayHelloServer may be embedded to opt out of forward compatibility for this service. 67 | // Use of this interface is not recommended, as added methods to SayHelloServer will 68 | // result in compilation errors. 69 | type UnsafeSayHelloServer interface { 70 | mustEmbedUnimplementedSayHelloServer() 71 | } 72 | 73 | func RegisterSayHelloServer(s grpc.ServiceRegistrar, srv SayHelloServer) { 74 | s.RegisterService(&SayHello_ServiceDesc, srv) 75 | } 76 | 77 | func _SayHello_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 78 | in := new(HelloRequest) 79 | if err := dec(in); err != nil { 80 | return nil, err 81 | } 82 | if interceptor == nil { 83 | return srv.(SayHelloServer).SayHello(ctx, in) 84 | } 85 | info := &grpc.UnaryServerInfo{ 86 | Server: srv, 87 | FullMethod: SayHello_SayHello_FullMethodName, 88 | } 89 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 90 | return srv.(SayHelloServer).SayHello(ctx, req.(*HelloRequest)) 91 | } 92 | return interceptor(ctx, in, info, handler) 93 | } 94 | 95 | // SayHello_ServiceDesc is the grpc.ServiceDesc for SayHello service. 96 | // It's only intended for direct use with grpc.RegisterService, 97 | // and not to be introspected or modified (even as a copy) 98 | var SayHello_ServiceDesc = grpc.ServiceDesc{ 99 | ServiceName: "SayHello", 100 | HandlerType: (*SayHelloServer)(nil), 101 | Methods: []grpc.MethodDesc{ 102 | { 103 | MethodName: "SayHello", 104 | Handler: _SayHello_SayHello_Handler, 105 | }, 106 | }, 107 | Streams: []grpc.StreamDesc{}, 108 | Metadata: "hello.proto", 109 | } 110 | -------------------------------------------------------------------------------- /其他/kafka-case/consumer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/segmentio/kafka-go" 7 | "log" 8 | ) 9 | 10 | func main() { 11 | topic := "vcz" 12 | 13 | r := kafka.NewReader(kafka.ReaderConfig{ 14 | Brokers: []string{"localhost:9092"}, 15 | GroupID: "test01", 16 | Topic: topic, 17 | }) 18 | defer func() { 19 | if err := r.Close(); err != nil { 20 | log.Fatal("failed to close reader:", err) 21 | } 22 | }() 23 | 24 | for { 25 | m, err := r.ReadMessage(context.Background()) 26 | if err != nil { 27 | break 28 | } 29 | fmt.Printf("message at topic:%v partition:%v offset:%v %s = %s\n", m.Topic, m.Partition, m.Offset, string(m.Key), string(m.Value)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /其他/kafka-case/go.mod: -------------------------------------------------------------------------------- 1 | module kafka 2 | 3 | go 1.19 4 | 5 | require github.com/segmentio/kafka-go v0.4.40 6 | 7 | require ( 8 | github.com/klauspost/compress v1.15.9 // indirect 9 | github.com/pierrec/lz4/v4 v4.1.15 // indirect 10 | ) 11 | -------------------------------------------------------------------------------- /其他/kafka-case/producer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "github.com/segmentio/kafka-go" 6 | "log" 7 | ) 8 | 9 | func main() { 10 | topic := "vcz" 11 | 12 | w := &kafka.Writer{ 13 | Addr: kafka.TCP("localhost:9092"), 14 | Topic: topic, 15 | Balancer: &kafka.LeastBytes{}, 16 | } 17 | defer func() { 18 | if err := w.Close(); err != nil { 19 | log.Fatal("failed to close writer:", err) 20 | } 21 | }() 22 | 23 | err := w.WriteMessages(context.Background(), 24 | kafka.Message{ 25 | Key: []byte("Key-A"), 26 | Value: []byte("One!"), 27 | }, 28 | kafka.Message{ 29 | Key: []byte("Key-B"), 30 | Value: []byte("Two!"), 31 | }, 32 | kafka.Message{ 33 | Key: []byte("Key-C"), 34 | Value: []byte("Three!"), 35 | }, 36 | ) 37 | if err != nil { 38 | log.Fatal("failed to write messages:", err) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /基础/map/map.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | func main(){ 9 | case_1() 10 | case_2() 11 | case_3() 12 | case_4() 13 | case_5() 14 | case_6() 15 | case_7() 16 | case_8() 17 | } 18 | 19 | /** 20 | map的初始化 21 | map:键值对 无序 22 | */ 23 | func case_1(){ 24 | //一:make创建一个map,再给其赋值 25 | var map_a map[int]string = make(map[int]string) //也可以写成 map_a := make(map[int]string) 26 | map_a[7] = "aaa" 27 | map_a[8] = "bbb" 28 | map_a[9] = "ccc" 29 | //二:初始化 赋值 一体化 30 | var map_b map[string]string = map[string]string{"七":"aaa","八":"bbb","九":"ccc"} //也可以写成 map_b := map[int]string{7:"aaa",8:"bbb",9:"ccc"} 31 | fmt.Print("map_a:",map_a,len(map_a),"\n","map_b:",map_b,len(map_b)) 32 | } 33 | 34 | 35 | /** 36 | map元素查找 37 | */ 38 | func case_2(){ 39 | myMap := make(map[int]string) 40 | myMap[7] = "aaa" 41 | myMap[8] = "bbb" 42 | myMap[9] = "ccc" 43 | value,result := myMap[5] //第一个返回值value接收值,第二个返回值result接收查询结果 44 | if result { 45 | fmt.Println("result:",result,"value:",value) //查找到result为true,value为查找到的值 46 | }else{ 47 | fmt.Println("result:",result,"value:",value) //未查找到result为false,value为空 48 | } 49 | } 50 | 51 | 52 | /** 53 | map元素删除 54 | */ 55 | func case_3(){ 56 | myMap := make(map[int]string) 57 | myMap[7] = "aaa" 58 | myMap[8] = "bbb" 59 | myMap[9] = "ccc" 60 | fmt.Println(myMap) 61 | delete(myMap,8) //第一个参数为map对象,第二个参数要删除的map对象元素的键 62 | fmt.Println(myMap) 63 | delete(myMap,1) //没有该键就不做任何操作,也不报错 64 | fmt.Println(myMap) 65 | } 66 | 67 | 68 | /** 69 | map元素遍历 70 | go中map的键值都是无规律,所以不能用for循环遍历 71 | map只能用for range语句遍历 72 | 注意:go中map是无序的!!! 73 | */ 74 | func case_4(){ 75 | myMap := make(map[int]string) 76 | myMap[7] = "aaa" 77 | myMap[8] = "bbb" 78 | myMap[9] = "ccc" 79 | for key,value := range myMap{ 80 | fmt.Println("key:",key,"value:",value) //因为map无序,所以遍历打印的顺序不能每次都相同 81 | } 82 | } 83 | 84 | 85 | /** 86 | map的嵌套 87 | */ 88 | func case_5(){ 89 | var myMap map[int]map[string]int = make(map[int]map[string]int) //初始化一个map,键是[int],值是map[string]int 90 | myMap[5] = make(map[string]int) //每个键的值map也要初始化,初始化后才能添加元素 91 | myMap[5]["我"] = 22 //添加元素时将myMap[1]看作一个map变量名,["我"]为键,22为值 92 | myMap[5]["吾"] = 33 93 | myMap[8] = make(map[string]int) //新的键的值map同样也要初始化才能添加变量 94 | myMap[8]["他"] = 22 95 | myMap[8]["她"] = 33 96 | fmt.Println(myMap) //整个双层map的嵌套打印 97 | fmt.Println(myMap[5]) //第一层键返回的是第二层的map整体 98 | fmt.Println(myMap[5]["吾"]) //访问时注意层次即可 99 | } 100 | 101 | 102 | /** 103 | 嵌套的map遍历 104 | map只能用for range语句遍历 105 | */ 106 | func case_6(){ 107 | var myMap map[int]map[string]int = make(map[int]map[string]int) 108 | myMap[5] = make(map[string]int) 109 | myMap[5]["我"] = 22 110 | myMap[5]["吾"] = 33 111 | myMap[8] = make(map[string]int) 112 | myMap[8]["他"] = 22 113 | myMap[8]["她"] = 33 114 | for key,value := range myMap{ //先遍历myMap的键值对 115 | fmt.Println("myMap:{ key:",key," value:",value,"}") 116 | for key,value := range value{ //再用获取到的myMap的值遍历myMap嵌套的map的键值对 117 | fmt.Println("myMap的嵌套:{ key:",key," value:",value,"}") 118 | } 119 | } 120 | } 121 | 122 | 123 | /** 124 | map的排序 125 | 先遍历map的key并存入一个创建好的slice中,再用sort.Ints(slice)函数对slice的元素进行排序 126 | 排序好后的slice的元素即为排序后的map的key,遍历slice的元素并通过map的key获取map的value即完成对map的排序 127 | */ 128 | func case_7(){ 129 | myMap := map[int]string{9:"九",8:"八",5:"五",22:"二十二",12:"十二"} 130 | var slice []int 131 | fmt.Println("排序前:") 132 | for key,value:= range myMap { 133 | fmt.Println("myMap:{ key:",key,"value:",value,"}") 134 | slice = append(slice,key) //遍历map并存入slice中 135 | } 136 | sort.Ints(slice) //该函数对切片的元素进行排序返回排序好的slice 137 | fmt.Println("排序后:") 138 | for _ , value := range slice{ //遍历排序好的slice 139 | fmt.Println("myMap:{ key:",value,"value:",myMap[value],"}") //slice的value是排序好的map的key,再通过map的key获取map的value,遍历slice就可以得到排好序的map 140 | } 141 | } 142 | 143 | 144 | /** 145 | map切片 146 | */ 147 | func case_8(){ 148 | var slice []map[string]string = make([]map[string]string,2,3) //创建一个map[string]string数据类型的切片 149 | //slice第一个元素 150 | if slice[0] == nil { //先判断元素是否为nil,为nil就创建map元素 151 | slice[0] = make(map[string]string) //创建切片的map[string]string元素,先创建才能赋值 152 | } 153 | slice[0]["第一个map元素的第一个key"] = "第一个map元素的第一个value" //给第一个slice元素赋值,这里slice[0]相当于map的变量名 154 | slice[0]["第一个map元素的第二个key"] = "第一个map元素的第二个value" 155 | //slice第二个元素 156 | if slice[1] == nil { 157 | slice[1] = make(map[string]string) 158 | } 159 | slice[1]["第二个map元素的第一个key"] = "第二个map元素的第一个value" 160 | slice[1]["第二个map元素的第二个key"] = "第二个map元素的第二个value" 161 | fmt.Println("slice:",slice,len(slice),cap(slice)) 162 | for key , value := range slice { 163 | fmt.Println(key,value) 164 | } 165 | } -------------------------------------------------------------------------------- /基础/二维数组/二维数组.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main(){ 6 | case_1() 7 | case_2() 8 | case_3() 9 | } 10 | 11 | /** 12 | 二维数组的创建方式 13 | */ 14 | func case_1() { 15 | //一:先创建再赋值,为赋值的为默认值0 16 | var tdArray_1 [3][3]int 17 | tdArray_1[0][0] = 0 18 | tdArray_1[0][1] = 1 19 | tdArray_1[0][2] = 2 20 | tdArray_1[1][0] = 3 21 | tdArray_1[2][1] = 4 22 | //二:创建并赋值 23 | tdArray_2 := [3][3]int{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}} 24 | fmt.Printf("tdArray_1:%v 数据类型:%T\n", tdArray_1, tdArray_1) 25 | fmt.Printf("tdArray_2:%v 数据类型:%T", tdArray_2, tdArray_2) 26 | } 27 | 28 | /** 29 | 二维数组双for循环嵌套遍历 30 | */ 31 | func case_2() { 32 | tdArray := [3][3]int{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}} 33 | for i := 0; i < len(tdArray); i++ { 34 | for j := 0; j < len(tdArray[i]); j++ { 35 | fmt.Println(tdArray[i][j]) 36 | } 37 | } 38 | } 39 | 40 | /** 41 | 二维数组for range遍历 42 | */ 43 | func case_3() { 44 | tdArray := [3][3]int{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}} 45 | for indexOne, valueOne := range tdArray { 46 | fmt.Println("arrayOne:", indexOne, valueOne) //indexOne为一维数组下标,valueOne是一个数组,即二维数组 47 | for indexTwo, valueTwo := range tdArray[indexOne] { //遍历一维数组的值得到二维数组的下标和值 48 | fmt.Println("arrayTwo:", indexTwo, valueTwo) //indexTwo为二维数组的下标,valueTwo是一个整数,也就是二维数组的值 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /基础/切片/切片入门.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | case_1() 7 | case_2() 8 | case_3() 9 | case_4() 10 | case_5() 11 | case_6() 12 | } 13 | 14 | /** 15 | 切片的初始化 16 | */ 17 | func case_1() { 18 | //一:定义一个切片直接截取一个已经创建好的数组 19 | array := [3]int{7, 8, 9} //创建一个数组 20 | var slice_a []int = array[0:3] //创建切片并引用(截取)创建好的数组 也可以写成:slice_a := array[:] []int可以不写 21 | //二:make关键字创建切片 22 | var slice_b []int = make([]int, 3, 5) //[]int切片数据类型,3是切片长度,5是切片容量,容量必须大于或等于长度 []int可以不写 23 | slice_b[0] = 7 24 | slice_b[2] = 8 25 | slice_b[1] = 9 26 | //三:定义一个切片,直接指定具体元素 27 | var slice_c []int = []int{7, 8, 9} 28 | fmt.Println("slice_a:", slice_a, len(slice_a), cap(slice_a)) //len()长度,cap()容量 29 | fmt.Println("slice_b:", slice_b, len(slice_b), cap(slice_b)) 30 | fmt.Println("slice_c:", slice_c, len(slice_c), cap(slice_c)) 31 | } 32 | 33 | /** 34 | 切片截取一个新的切片 35 | */ 36 | func case_2() { 37 | slice := []int{2, 5, 1, 4} 38 | newSlice := slice[1:] 39 | fmt.Println("slice:", slice, len(slice), cap(slice)) 40 | fmt.Println("newSlice:", newSlice, len(newSlice), cap(newSlice)) 41 | } 42 | 43 | /** 44 | 字符串切片 45 | */ 46 | func case_3() { 47 | var slice []string = []string{"aaa", "bbb", "ccc", "ddd"} 48 | fmt.Println("slice:", slice, len(slice), cap(slice)) 49 | } 50 | 51 | /** 52 | 切片元素追加 53 | */ 54 | func case_4() { 55 | slice1 := make([]int, 2, 4) 56 | slice1 = []int{1, 2, 3, 4, 5} 57 | slice2 := append(slice1, 6, 7, 8) //也可以用slice1接收,表示在slice1基础上增加6,7,8元素成为新的slice1 58 | slice3 := append(slice1[0:2], slice1[3:]...) 59 | fmt.Print("slice2:", slice2, len(slice2), cap(slice2)) //追加后容量自己扩容,长度为原切片长度加上追加的元素数量 60 | fmt.Print("\nslice3:", slice3, len(slice3), cap(slice3)) 61 | } 62 | 63 | /** 64 | 切片复制 65 | */ 66 | func case_5() { 67 | slice := []string{"aaa", "bbb", "ccc", "ddd"} 68 | var tarSlice []string = make([]string, 3, 5) 69 | copy(tarSlice, slice) //长度不够,只复制能够存储的元素,剩下的省略不复制,不会报错 70 | fmt.Print("slice:", tarSlice, len(tarSlice), cap(tarSlice)) 71 | } 72 | 73 | /** 74 | 字符串转切片,切片截取字符串,切片转字符串 75 | */ 76 | func case_6() { 77 | var str = "hello golang!" 78 | slice := []rune(str[1:]) //字符用[]byte也可以 全部转换:slice := []rune(str) 79 | slice[5] = 'A' //更改转换后的切片的元素 80 | str = string(slice) //再将切片转为字符串打印检查元素是否改变 81 | fmt.Print("str:", str) 82 | } 83 | -------------------------------------------------------------------------------- /基础/切片/斐波拉契数列.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | /** 6 | 斐波拉契数列 7 | 给出要求的斐波拉契数列的个数,控制台打印对应个数的斐波拉契数列 8 | 斐波那契数列:1 1 2 3 5 8....构成了一个序列,这个数列的特点:前面相邻两项之和,构成了后一项。 9 | */ 10 | func main(){ 11 | var number int 12 | start:fmt.Println("输入个数:") 13 | fmt.Scan(&number) 14 | if fibonacci(number) == nil { 15 | fmt.Println("输入有误!(大于等于2)") 16 | goto start 17 | }else{ 18 | fmt.Print("result:",fibonacci(number)) 19 | } 20 | } 21 | func fibonacci(number int)([]int){ 22 | if number < 2 { 23 | return nil //如果小于2就返回空 24 | } 25 | slice := make([]int, number, number) 26 | slice[0] = 1 27 | slice[1] = 1 28 | for i := 2 ; i < number; i++{ 29 | slice[i] = slice[i-1] + slice[i-2] 30 | } 31 | return slice 32 | } -------------------------------------------------------------------------------- /基础/匿名函数/匿名函数入门.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main(){ 6 | case_1() 7 | case_2() 8 | case_3() 9 | } 10 | 11 | /** 12 | 直接在末尾调用 13 | */ 14 | func case_1() { 15 | var className int = func(number int) int { //定义一个变量给其赋值一个函数 即创建了一个匿名函数 16 | number = 100 17 | return number 18 | }(20) //在匿名函数后直接传入参数调用 19 | result := className //className也可以赋值给其他变量 20 | fmt.Println(result) 21 | } 22 | 23 | /** 24 | 调用赋值给的变量为函数名 25 | */ 26 | func case_2() { 27 | className := func(number_1 int) (int, int) { 28 | number_1 = 100 29 | number_2 := 101 30 | return number_1, number_2 31 | } 32 | result_1, result_2 := className(99) //匿名函数有两个返回值则需要两个变量来接收 33 | fmt.Println(result_1, result_2) 34 | } 35 | 36 | /** 37 | 定义全局变量 函数名为定义的全局变量 38 | */ 39 | var ( 40 | className = func(number_1 int, number_2 int) int { //定义一个全局变量 41 | return number_1 + number_2 42 | } 43 | ) 44 | 45 | func case_3() { 46 | result := className(10, 20) //全局变量名为函数名 47 | fmt.Println(result) 48 | } 49 | -------------------------------------------------------------------------------- /基础/单元测试/单元测试/hello.go: -------------------------------------------------------------------------------- 1 | package hello 2 | 3 | func SayHello(name string) string { 4 | return "hello," + name 5 | } 6 | -------------------------------------------------------------------------------- /基础/单元测试/单元测试/hello_test.go: -------------------------------------------------------------------------------- 1 | package hello 2 | 3 | import "testing" 4 | 5 | func TestSayHello(t *testing.T) { 6 | res := SayHello("alex") 7 | t.Logf("SayHello函数结果: %s", res) 8 | } 9 | -------------------------------------------------------------------------------- /基础/单元测试/单元测试介绍.md: -------------------------------------------------------------------------------- 1 | 使用 go自带的轻量级的测试框架testing 和 "go test"命令 来实现单元测试和性能测试 2 | 3 | 作用: 4 | ``` 5 | 1.确保每个函数可运行并且结果正确 6 | 2.确保代码性能 7 | 3.及时发现代码逻辑错误或程序设计上的问题,便于问题定位解决 8 | ``` 9 | 10 | 用法: 11 | ``` 12 | 创建一个名称以_test.go结尾的文件,该文件包含TestXxx()函数,TestXxx()的参数类型必须是(t *testing.T) 13 | ``` 14 | 15 | 16 | 细节: 17 | ``` 18 | 1.TestXxx()中Xxx可以是任何字符串,但首字母不能是小写的a-z 19 | 2.一个测试文件可以有多个测试函数 20 | 3.t.Logf()方法输出相应日志 21 | ``` 22 | 23 | 输出结果: 24 | ``` 25 | PASS测试用例运行成功 FAIL测试用例运行失败 26 | ``` 27 | 28 | 命令: 29 | 1.测试单个文件 30 | ``` 31 | go test -v 测试文件_test.go 文件.go 32 | ``` 33 | 2.测试单个方法 (GO111MODULE=off 才可用) 34 | ``` 35 | go test -v -test.run TestXxx 36 | ``` -------------------------------------------------------------------------------- /基础/多态/多态.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | /** 6 | 多态:变量(实例)具有多种形态。 7 | 在GO中多态特征是通过接口实现的:统一的接口调用不同的实现,这时接口变量就呈现不同的形态,即就是多态。 8 | 接口体现多态的两种形式:多态参数 多态数组 9 | */ 10 | 11 | //创建一个接口 12 | type Usb interface { 13 | Start() //Usb设备开始工作 14 | Stop() //Usb设备停止工作 15 | } 16 | //创建Usb手机设备 17 | type Phone struct { 18 | name string 19 | } 20 | func (phone Phone)Start(){ 21 | fmt.Println("手机开始工作。。。") 22 | } 23 | func (phone Phone)Stop(){ 24 | fmt.Println("手机停止工作。。。") 25 | } 26 | //创建Usb相机设备 27 | type Camera struct { 28 | name string 29 | } 30 | func (camera Camera)Start(){ 31 | fmt.Println("相机开始工作。。。") 32 | } 33 | func (camera Camera)Stop(){ 34 | fmt.Println("相机停止工作。。。") 35 | } 36 | func main(){ 37 | //创建一个接口类型的数组,里面可以存放Phone和Camera两种实现了Usb接口的结构体变量,体现出多态数组 38 | var usbArr [3]Usb 39 | usbArr[0] = Phone{"PhoneOne"} 40 | usbArr[1] = Phone{"PhoneTwo"} 41 | usbArr[2] = Camera{"CameraOne"} 42 | fmt.Println(usbArr) 43 | } 44 | -------------------------------------------------------------------------------- /基础/接口/GO自带排序接口.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "sort" 7 | ) 8 | 9 | //创建一个结构体 10 | type Student struct { 11 | name string 12 | age int 13 | } 14 | //创建一个Student结构体类型的切片 15 | type Students []Student 16 | //让该切片实现下面的三种方法,即就是让Students切片实现了sort包中的Sort接口 17 | func (students Students) Len() int{ 18 | return len(students) //返回Students切片的长度 19 | } 20 | func (students Students) Less(i int , j int) bool { 21 | return students[i].age > students[j].age //返回Students切片的排序规则:ij是从大到小,==不排序 22 | } 23 | func (students Students) Swap(i int , j int) { 24 | temp := students[i] //交换切片中下标为i和j的元素位置 25 | students[i] = students[j] 26 | students[j] = temp 27 | //以上三行代码可以简化为:students[i],students[j] = students[j],students[i] 体现了go语言的简洁性 28 | } 29 | func main(){ 30 | var students Students //实例化切片 31 | //给切片的各个元素赋值 32 | for i := 0 ; i < 6 ; i++ { 33 | student := Student{ 34 | name : fmt.Sprintf("学生%d",rand.Intn(100)), 35 | age : rand.Intn(100), 36 | } 37 | students = append(students,student) //将赋值好的元素添加到切片中 38 | } 39 | fmt.Println("排序前:") 40 | for _ , v := range students{ //排序前遍历 41 | fmt.Println(v) 42 | } 43 | //使用go自带包里的sort包中的Sort方法进行排序。 44 | sort.Sort(students) //参数是一个接口变量,前面Students切片实现了该接口,students又是Students数据类型的,所以可以传入进行排序。 45 | fmt.Println("排序后:") 46 | for _ , v := range students{ //排序后遍历 47 | fmt.Println(v) 48 | } 49 | } -------------------------------------------------------------------------------- /基础/接口/接口入门.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | /** 6 | 接口:接口是一组仅包含方法名、参数、返回值的未具体实现的方法的集合。 7 | 1.一个自定义类型只有实现了某个接口,才能将自身的实例赋值给该接口 8 | 2.接口中所有方法都没有方法体,即都是没有实现的方法 9 | 3.在go中一个自定义类型需要将某个接口定义的所有方法都实现,才可以说该自定义类型实现了这个接口 10 | 4.不仅仅是结构体,只要是自定义数据类型,都可以实现接口 11 | 5.GO中的接口不需要显性实现,只要一个变量实现了这个接口中的所有方法,那么就可以说该变量实现了这个接口。 12 | 6.一个自定义类型可以实现多个接口 13 | 7.go中的接口不能有任何变量,只有未实现的方法 14 | 8.一个接口可以继承其他多个接口(继承的接口中不能有相同的方法),如果A接口继承了B和C接口,那么要实现A接口,也必须要将B和C接口的方法全部实现,缺一不可。 15 | 9.接口默认是一个指针类型(引用类型),如果没有对接口初始化就使用,就会输出nil 16 | 10.一个空的接口没有定义任何方法,可以说所有的类型都实现了该接口。 17 | */ 18 | 19 | //创建一个接口 20 | type Usb interface { 21 | //创建接口的方法(这个接口能做些什么事) 22 | Start() //Usb设备开始工作 23 | Stop() //Usb设备停止工作 24 | } 25 | 26 | 27 | //创建一个可以传入Usb接口的对象 28 | type Computer struct { 29 | 30 | } 31 | /** 32 | Computer有一个Working方法拥有Usb接口,其他Usb设备可以通过Working拥有的这个Usb接口接入Computer 33 | */ 34 | func (computer Computer)Working(usb Usb) { //接口默认是引用变量,这里传入的usb对象是地址 35 | //Working方法负责在有Usb设备接入时通过Usb接口调用Usb设备的Start()和Stop()方法 36 | //因为Working实现了Usb接口,并且Usb接口拥有Start()和Stop()这两个方法,所以可以通过Usb接口调起Usb设备的Start()和Stop()方法 37 | usb.Start() 38 | usb.Stop() 39 | } 40 | 41 | 42 | /** 43 | 创建两个Usb设备 44 | Usb设备有Start()和Stop()两个方法,Usb接口厂家和Usb设备厂家都遵守统一的制作标准,目的就是方便Usb接口和Usb设备的灵活对接 45 | 如果一个Usb设备没有Start()和Stop()两个任何一个方法,都不能通过Usb接口接入。(缺少方法的Usb设备在传入该设备对象时编译不通过) 46 | 这样一来无论什么Usb设备出厂前都有了Start()和Stop()方法,Usb接口就不用管什么Usb设备,只负责执行该Usb设备Start()和Stop()方法即可 47 | */ 48 | //创建Usb手机设备 49 | type Phone struct { 50 | 51 | } 52 | func (phone Phone)Start(){ 53 | fmt.Println("手机开始工作。。。") 54 | } 55 | func (phone Phone)Stop(){ 56 | fmt.Println("手机停止工作。。。") 57 | } 58 | //创建Usb相机设备 59 | type Camera struct { 60 | 61 | } 62 | func (camera Camera)Start(){ 63 | fmt.Println("相机开始工作。。。") 64 | } 65 | func (camera Camera)Stop(){ 66 | fmt.Println("相机停止工作。。。") 67 | } 68 | 69 | 70 | /** 71 | 实例化两个Usb设备和Computer并让他们互动 72 | 给Usb设备和电脑通电,并将Usb设备通过接口接入电脑 73 | */ 74 | func main(){ 75 | computer := Computer{} //给电脑通电 76 | phone := Phone{} //给手机通电 77 | camera := Camera{} //给相机通电 78 | computer.Working(phone) //手机接入电脑 79 | computer.Working(camera) //相机接入电脑 80 | } -------------------------------------------------------------------------------- /基础/接口/接口得继承.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | 4 | /** 5 | 一个接口可以继承其他多个接口(继承的接口中不能有相同的方法),如果A接口继承了B和C接口,那么要实现A接口,也必须要将B和C接口的方法全部实现,缺一不可。 6 | */ 7 | type A interface { 8 | aa() 9 | bb() 10 | } 11 | type B interface { 12 | //aa() //继承的接口中不能有相同的方法,B中的aa()和A中的aa()相同 13 | cc() 14 | } 15 | type C interface { 16 | A 17 | B //继承的接口中不能有相同的方法,如果有会冲突,编译不能通过 18 | } 19 | -------------------------------------------------------------------------------- /基础/接口/接口的细节1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main(){ 6 | case_1() 7 | case_2() 8 | } 9 | 10 | type interface_1 interface { 11 | show() 12 | } 13 | type interface_2 struct { 14 | name string 15 | } 16 | func (i2 interface_2)show(){ 17 | fmt.Println("i2.show():",i2.name) 18 | } 19 | func case_1(){ 20 | var i1 interface_1 21 | i2 := interface_2{"hello golang"} 22 | i1 = i2 //一个自定义类型只有实现了某个接口,才能将自身的实例赋值给该接口再被调用 23 | i1.show() 24 | } 25 | 26 | 27 | type interface_3 interface { 28 | show() 29 | } 30 | type interface_4 int //不仅仅是结构体,只要是自定义数据类型,都可以实现接口 31 | func (i4 interface_4)show(){ 32 | fmt.Println("i4.show():i4 =",i4) 33 | } 34 | func case_2(){ 35 | var i3 interface_3 36 | var i4 interface_4 = 20 37 | i3 = i4 //一个自定义类型只有实现了某个接口,才能将自身的实例赋值给该接口再被调用 38 | i3.show() 39 | } -------------------------------------------------------------------------------- /基础/接口/接口的细节2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type AInterface interface { 6 | sing() 7 | } 8 | type BInterface interface { 9 | jump() 10 | } 11 | type Kun struct { 12 | name string 13 | } 14 | func (kun Kun) sing(){ 15 | fmt.Println(kun.name,"在唱!") 16 | } 17 | func (kun Kun) jump(){ 18 | fmt.Println(kun.name,"在跳!") 19 | } 20 | func main(){ 21 | kun := Kun{"坤坤"} 22 | var a AInterface 23 | var b BInterface 24 | a = kun //一个自定义类型可以实现多个接口 25 | b = kun //一个自定义类型可以实现多个接口 26 | a.sing() 27 | b.jump() 28 | } -------------------------------------------------------------------------------- /基础/接口/接口的细节3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | //创建一个接口 6 | type interface_5 interface { 7 | show() 8 | } 9 | //创建一个结构体并实现interface_5接口 10 | type struct_1 struct { 11 | name string 12 | } 13 | //创建一个绑定struct_1结构体指针实现interface_5接口的方法 14 | func (struct_1 *struct_1) show(){ 15 | fmt.Println("struct_1.show():",struct_1.name) 16 | } 17 | func main(){ 18 | var i5 interface_5 19 | s1 := struct_1{"hello golang!"} 20 | i5 = &s1 //当实现接口的结构体方法绑定的是结构体指针时,在给接口赋值结构体时要记得取地址 21 | i5.show() 22 | } 23 | -------------------------------------------------------------------------------- /基础/接口/空接口的使用.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | //创建一个空接口 6 | type interface_6 interface { 7 | 8 | } 9 | //随便创建一个变量 10 | type struct_2 struct { 11 | name string 12 | } 13 | func main() { 14 | var i6 interface_6 //一个空的接口没有定义任何方法,可以说所有的类型都实现了该接口。 15 | var s2 struct_2 16 | i6 = s2 17 | fmt.Println(i6) 18 | 19 | //接口也可以作为实例化变量时的数据类型 20 | var i7 interface{} //interface{}是一个空接口数据类型,{}里可以添加接口要定义的方法 21 | //结构体也可以作为实例化变量时的数据类型,{}里可以添加结构体的字段(这样创建的结构体只可以添加一个字段) 22 | var s3 struct{} 23 | i7 = s3 24 | fmt.Println(i7) 25 | } 26 | -------------------------------------------------------------------------------- /基础/接口和继承/接口和继承的关系入门.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | /** 6 | 接口和继承的关系:从设计层面来说,继承是一种模板设计,接口是一种行为的规范,接口是不破坏继承关系并对继承的一种补充。 7 | 继承:解决代码的复用性和可维护性 8 | 接口:设计好某种行为规范,让其他对象实现这种行为规范 9 | 1.接口比继承更灵活:继承是is-a的关系,接口是like-a的关系。 10 | 2.接口在一定程度上实现了代码的解耦。 11 | */ 12 | 13 | /** 14 | 有一个猴子名叫悟空,不仅继承了猴子会爬树的基因,还学会了鸟儿会飞,鱼儿会游泳的本领 15 | */ 16 | //创建一个猴子 17 | type Monkey struct{ 18 | name string 19 | } 20 | //猴子有爬树的本领 21 | func (monkey Monkey) climbTree(){ 22 | fmt.Println(monkey.name,"会爬树") 23 | } 24 | //创建一个鸟儿 25 | type bird interface{ 26 | fly() //鸟儿有会飞的本领 27 | } 28 | //创建一个鸟儿 29 | type fish interface{ 30 | swimming() //鱼儿有会游泳的本领 31 | } 32 | //创建一个悟空,继承猴子 33 | type wuKong struct{ 34 | Monkey 35 | } 36 | //让悟空学会鸟儿会飞的本领 37 | func (wuKong wuKong) fly() { 38 | fmt.Println(wuKong.name,"会飞") 39 | } 40 | //让悟空学会鱼儿会游泳的本领 41 | func (wuKong wuKong) swimming() { 42 | fmt.Println(wuKong.name,"会游泳") 43 | } 44 | //让猴子学会鸟儿和鱼儿的本领 45 | func main(){ 46 | //实例化悟空 47 | wuKong := wuKong { 48 | Monkey{ 49 | name : "悟空", 50 | }, 51 | } 52 | //悟空继承猴子会爬树的基因 53 | wuKong.climbTree() 54 | //让鸟儿教悟空飞 55 | var bird bird 56 | bird = wuKong 57 | bird.fly() 58 | //让鱼儿教悟空爬树 59 | var fish fish 60 | fish = wuKong 61 | fish.swimming() 62 | } -------------------------------------------------------------------------------- /基础/数组/数组.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main(){ 8 | case_1() 9 | case_2() 10 | case_3() 11 | case_4() 12 | } 13 | 14 | //数组的初始化 15 | func case_1(){ 16 | //一 17 | var array_a [3]int 18 | array_a[0] = 7 19 | array_a[1] = 8 20 | array_a[2] = 9 21 | //二 22 | var array_b [3]int = [3]int{2: 9,1:8,0:7} 23 | //三 24 | var array_c = [3]int{2: 9,0:7,8} 25 | //四 26 | array_d := [3]int{0: 7,8} 27 | //五 28 | var array_e = [...]int{7,2:8,9} 29 | //六 30 | var array_f = [...]int{1: 7,3:9} 31 | fmt.Println("array_a:", array_a,"\narray_b:", array_b,"\narray_c:", array_c,"\narray_d:", array_d,"\narray_e:", array_e,"\narray_f:", array_f) 32 | } 33 | 34 | 35 | /** 36 | 数组的遍历(for循环遍历) 37 | */ 38 | func case_2(){ 39 | var array [26]byte //byte数据类型的数组 40 | n := 0 41 | for i := 65 ; i < 91 ; i++ { 42 | array[n] = byte(i) //i是int数据类型的,array是byte数组,i赋值给数组元素要强转 43 | fmt.Printf("%c", array[n]) 44 | n++ 45 | } 46 | } 47 | 48 | 49 | /** 50 | 数组的遍历(for range遍历) 51 | */ 52 | func case_3(){ 53 | var array [4]float64 54 | array[0] = 10.2 55 | array[1] = 8.8 56 | array[2] = 11.0 57 | array[3] = 15.0 58 | sum := 0.0 59 | for index,value := range array { 60 | sum += array[index] 61 | fmt.Println(index,value) 62 | } 63 | average := sum / float64(len(array)) //sum为float64数据类型,len(array)是数组长度是int数据类型,go中不同数据类型运算必须强转 64 | fmt.Printf("平均数:%.2f\n总和:%.2f",average,sum) //%.2f:精确到小数点后两位 65 | } 66 | 67 | 68 | /** 69 | 数组的数据类型和数组的len也有关 70 | len为2的数组和len为3的数组,不是同一数据类型 71 | 参数是 len为3的数组 的函数,调用时传入参数 len为2的数组 会报错,即使数组元素的数据类型一致 72 | */ 73 | func case_4(){ 74 | array := [3]int{1,2,3} 75 | case_4_1(array) 76 | } 77 | func case_4_1(array [3]int){ 78 | fmt.Print(array) 79 | } -------------------------------------------------------------------------------- /基础/方法/GO的方法.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main(){ 6 | case_1() 7 | case_2() 8 | case_3() 9 | case_4() 10 | } 11 | 12 | /** 13 | go中的方法是和结构体绑定的,绑定了结构体的函数就叫方法(不仅仅是结构体,也可以是其他自定义数据类型),方法名首字母大写可以在其他包内访问 14 | 注意:绑定了结构体的方法,只能通过该结构体数据类型声明的变量来调用,不能直接调用或用其他数据类型声明的变量调用 15 | */ 16 | type Method struct{ 17 | Name string 18 | Number int 19 | } 20 | func (method Method) case_1_1(number int) bool { // case_1_1()是绑定了Method数据类型的方法,就是说case_1_1()该方法作用于Method类型 method参数结构体名可以自定义,数据类型必须是已创建的结构体 21 | method.Name = "method" 22 | method.Number = 104 23 | number = 2 24 | fmt.Printf("method的地址%p method.Name的地址%p\n",&method,&method.Name) 25 | fmt.Println("case_1_1():",method.Name,method.Number,number) 26 | return true 27 | } 28 | func case_1(){ 29 | var method Method //声明一个Method数据类型的变量(实例),这里定义的method和case_1_1()中的method不是同一个,调用时会将method值拷贝传给case_1_1(),虽是互相绑定但只是规定谁可以调用 30 | method.Name = "method~" //给Method生命的变量method的字段赋值 31 | method.Number = 105 32 | number := 1 33 | result := method.case_1_1(number) //调用与其数据类型绑定的方法,调用时会将method之前的赋值(值类型数据)的变量值拷贝过去给case_1_1()方法 34 | fmt.Printf("method的地址%p method.Name的地址%p\n",&method,&method.Name) //这里method的地址和case_1_1()中的method地址不同,所以调用case_1_1()方法时也会将method进行值拷贝传给case_1_1()方法 35 | fmt.Println("case_1():",method.Name,method.Number,number,result) //go中函数调用使用的是值拷贝(值类型数据),所以case_1_1()的赋值不会影响case_1() 36 | } 37 | 38 | 39 | /** 40 | 结构体是值类型,在方法调用中遵守值拷贝传递方式;如果希望在方法中修改结构体变量的值,可以通过绑定结构体指针的方式 41 | */ 42 | type Circle struct { 43 | radius float32 44 | } 45 | //为了提高效率,我们通常将方法和结构体的指针绑定 46 | func (circle *Circle) case_2_1() float32 { 47 | (*circle).radius = 6.0 //指针绑定 这里修改的是case_2()中声明的变量的本身 48 | fmt.Printf("circle的值%p circle.radius的地址%p\n",circle,&circle.radius) //结构体地址 字段地址和case_2()里的都相同 49 | //因为circle是指针,所以要使用(*circle)方式访问 50 | return 3.14 * (*circle).radius * (*circle).radius // (*circle)等价于circle,go的底层做了优化,变量是指针时,方法内会自动访问他指向的值,也就是说自动为其变量加上* 51 | } 52 | func case_2(){ 53 | var circle Circle 54 | circle.radius = 5.0 55 | result := circle.case_2_1() //因为是指针绑定,上面的变量传递时不是值拷贝而是变量地址 56 | fmt.Printf("circle的地址%p circle.radius的地址%p\n",&circle,&circle.radius) //这里的地址和case_2_1()相同 57 | fmt.Println("case_2():",circle.radius,result) //在case_2_1()修改的变量的值这里也被修改了 58 | } 59 | 60 | 61 | /** 62 | 方法不仅仅是绑定结构体,也可以是其他自定义数据类型 63 | */ 64 | type integer int 65 | func (number *integer) case_3_1(){ 66 | (*number) += 1 67 | } 68 | func case_3(){ 69 | var number integer 70 | number = 10 71 | number.case_3_1() 72 | fmt.Println("number:",number) 73 | } 74 | 75 | 76 | /** 77 | 如果一个类型实现了String()方法,那么fmt.Println默认会调用这个变量的String()进行输出 78 | */ 79 | type Log struct { 80 | name string 81 | flag int 82 | } 83 | func (log *Log) String() string { 84 | //fmt.Sprintf():用传入的格式化规则符将传入的变量格式化(终端中不会有显示),有一个返回值:返回格式化后的字符串 85 | str := fmt.Sprintf("自定义方法String():\nlog.name:%v\nlog.flag:%v",log.name,log.flag) 86 | return str 87 | } 88 | func case_4(){ 89 | log := Log { 90 | name : "hello golang!" , 91 | flag : 5 , 92 | } 93 | fmt.Println(&log) //因为Log数据类型的log实现了String()方法,所以fmt.Println是调用自定义的方法 自定义方法是指针数据类型,所以要传入地址 94 | } -------------------------------------------------------------------------------- /基础/管道/channel入门.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | /** 6 | 管道(channel):的本质就是一个数据结构 - 单向队列。 7 | 8 | 特点: 9 | 1.先进先出(FIFO:first in first out) 10 | 2.线程安全,多goroutine访问时,不会发生资源竞争问题,无需加锁,就是说channel本身就是线程安全的。 11 | 12 | 注意事项: 13 | 1.channel是指针类型,必须初始化才能完成数据写入,即必须make后才能使用。 14 | 2.channel是有数据类型的,一个string数据类型的channel只能存放string类型数据。 15 | 3.make时不设置容量或者设置为false就是无缓冲channel,设置大于0的容量就是有缓冲channel。 16 | 4.channel关闭后不能再放数据,再放会报错。 17 | 18 | 在没有使用协程的情况下: 19 | 1.当channel内放满时再放就会报错,长度不能超过容量;当channel内的数据全部取出时(长度等于零)再取就会报错。 20 | 2.当channel内放满时再放就会报错,但将管道内数据取出后,仍可以继续放入;取出几个数据便可以再放入几个数据,直至再次放满。 21 | 3.使用for range对未关闭的管道进行遍历会报错。 22 | 23 | channel声明: 24 | var ch chan 数据类型 25 | 管道可以声明为只读或只写(管道在默认情况下是双向的) 26 | var ch chan<- 数据类型 (只写管道)(管道只能放入数据,不能取出) 27 | var ch <-chan 数据类型 (只读管道)(管道只能取出数据,不能放入) 28 | ch = make(chan 数据类型 , 容量) // 管道必须make后才能使用 29 | */ 30 | 31 | func main() { 32 | case_1() 33 | case_2() 34 | } 35 | 36 | /** 37 | channel声明和数据存取 38 | */ 39 | func case_1() { 40 | //管道声明和实例化 41 | var intChan chan int = make(chan int, 3) //intChan必须make后才能使用,chan int是intChan的数据类型,3是intChan的容量 42 | //向管道内放入数据 43 | intChan <- 10 //将10放入管道 44 | intChan <- 12 //将12放入管道 45 | fmt.Printf("intChan的长度:%v intChan的容量:%v\n", len(intChan), cap(intChan)) //查看管道的长度和容量 46 | //从管道内取出数据 47 | var num1 int 48 | num1 = <-intChan 49 | num2 := <-intChan 50 | fmt.Printf("num1=%v num2=%v\n", num1, num2) 51 | fmt.Printf("intChan的长度:%v intChan的容量:%v\n", len(intChan), cap(intChan)) //查看管道的长度和容量 52 | } 53 | 54 | func case_2() { 55 | //管道声明和实例化 56 | var intChan chan int = make(chan int, 2) 57 | //向管道内放入数据 58 | intChan <- 25 //将25放入管道 59 | intChan <- 15 //将15放入管道 60 | fmt.Printf("intChan的长度:%v intChan的容量:%v\n", len(intChan), cap(intChan)) //查看管道的长度和容量 61 | //当管道放满时,再放就会报错。 62 | <-intChan //将intChan管道内的数据取出来不要了,给管道清理个位置来放入需要使用的数据 63 | fmt.Printf("intChan的长度:%v intChan的容量:%v\n", len(intChan), cap(intChan)) //查看管道的长度和容量 64 | intChan <- 18 //但将管道内数据取出后,仍可以继续放入,取出几个数据便可以再放入几个数据,直至再次放满。 65 | fmt.Printf("intChan的长度:%v intChan的容量:%v\n", len(intChan), cap(intChan)) //查看管道的长度和容量 66 | } 67 | -------------------------------------------------------------------------------- /基础/管道/select读取管道.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | case_8() 7 | } 8 | 9 | /** 10 | 传统方法在遍历管道时,如果管道没有关闭就会报错。 11 | select方法可以在管道未关闭的情况下遍历管道。 12 | */ 13 | func case_8() { 14 | intChan := make(chan int, 5) 15 | intChan <- 20 16 | intChan <- 30 17 | intChan <- 40 18 | A: 19 | for { // 使用for循环一直执行select 20 | select { 21 | // 如果intChan没有关闭,当这里读取不到数据就会进入下一个case不会报错 22 | case v := <-intChan: 23 | fmt.Printf("读取到:%v\n", v) 24 | // 加default的select是没有阻塞的,如果没有满足的case就执行default语句;不会阻塞等待满足case条件 25 | // 如果不加default则select有阻塞,如果没有满足的case就阻塞等待,只有当满足的case管道读写操作时才会结束阻塞 26 | default: 27 | fmt.Printf("读取不到数据,循环结束!!!") 28 | break A 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /基础/管道/协程和管道综合使用.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main(){ 9 | case_6() 10 | case_7() 11 | } 12 | 13 | /** 14 | 开启一个writeData协程,向intChan管道写入50个int类型数据 15 | 开启一个readData协程,从intChan管道里读取writeData写入的50个int类型数据 16 | 主线程等待writeData协程和readData协程都执行完毕再退出 17 | */ 18 | //向intChan写入50个数据 19 | func writeData(intChan chan int){ 20 | for i := 1 ; i <= 50 ; i++ { 21 | intChan <- i 22 | fmt.Printf("放入数据:%v\n",i) 23 | } 24 | close(intChan)//数据写完就关闭intChan,防止后面数据取完再取会报错,关闭intChan不影响intChan里的数据取出 25 | } 26 | //取出intChan里的50个数据,取完向exitChan放入一个数据 27 | func readData(intChan chan int , exitChan chan bool){ 28 | for v := range intChan{ 29 | fmt.Printf("取出数据:%v\n",v) 30 | } 31 | exitChan <- true //intChan里的数据取完,向exitChan放入一个数据,通知主线程退出 32 | close(exitChan)//数据写完就关闭exitChan,防止后面数据取完再取会报错,关闭exitChan不影响exitChan里的数据取出 33 | } 34 | func case_6(){ 35 | intChan := make(chan int,50) //intChan存取50个int类型数据 36 | exitChan := make(chan bool,1) //exitChan存储是否退出程序的bool类型数据 37 | go writeData(intChan) //开启writeData协程放入数据 38 | go readData(intChan,exitChan) //开启writeData协程取出数据 39 | //遍历exitChan 40 | for v := range exitChan { 41 | //如果exitChan里没有数据,程序会一直阻塞直至读取到数据 42 | if v { 43 | fmt.Printf("主线程退出:%v",v) 44 | break//读取到数据并且为true时退出for循环 45 | } 46 | } 47 | //for循环退出 主线程结束 程序结束退出 48 | } 49 | 50 | 51 | /** 52 | 使用管道加协程,统计1-1000的整数中,有哪些素数。 53 | */ 54 | //创建1-800的整数 55 | func writeNum(intChan chan int){ 56 | for i := 2 ; i < 1000 ; i++{ //1不是素数,所以从整数2开始存入intChan 57 | intChan <- i //将整数i放入intChan管道 58 | } 59 | close(intChan)//数据写完就关闭intChan,防止后面数据取完再取会报错,关闭intChan不影响intChan里的数据取出 60 | } 61 | //从intChan管道中统计素数,如果是素数就将其放入resChan管道 62 | func findPrime(intChan chan int,resChan chan int,exitChan chan bool){ 63 | var flag bool //num是否为素数的标识 64 | for { 65 | flag = true //先假设num是素数 66 | num , ok := <- intChan //从intChan取出整数num 67 | if !ok { //管道中数据取完或管道中取不出数据 68 | exitChan <- true //通知主线程执行完毕 69 | break //跳出for循环,协程结束退出 70 | } 71 | for i := 2 ; i < num ; i++ { //判断num是不是素数 72 | if num % i == 0 { 73 | flag = false //num不是素数 74 | break //不是素数直接跳出“判断num是不是素数”的for循环,去判断下一个 75 | } 76 | } 77 | if flag { 78 | resChan <- num//flag为真,说明num是素数,就将该num放入resChan管道中 79 | } 80 | } 81 | } 82 | func case_7(){ 83 | start := time.Now().UnixNano() 84 | intChan := make(chan int,500) //存放1-800的整数 85 | resChan := make(chan int,500) //存放1-800里的统计的素数结果 86 | exitChan := make(chan bool,8) //存放协程是否执行完毕 87 | 88 | go writeNum(intChan) //开启创建整数协程 89 | 90 | for i := 0 ; i < 8 ; i++ { 91 | go findPrime(intChan,resChan,exitChan) //开启八个从intChan管道中统计素数的协程 92 | } 93 | 94 | go func(){ //开启一个匿名函数协程,统计“统计素数”的协程执行情况 95 | count := 0 96 | for { 97 | if count == 8 { 98 | break //如果开启的所有协程都执行完毕就跳出for range遍历 99 | } 100 | <- exitChan //取出的数不需要,直接丢弃 101 | count++ //增加“执行完毕”的协程数量 102 | } 103 | close(resChan) //统计结束,数据写完就关闭resChan,防止后面数据取完再取会报错,关闭resChan不影响resChan里的数据取出 104 | close(exitChan) //统计结束,关闭exitChan协程执行情况管道 105 | }() 106 | 107 | //遍历resChan统计素数结果管道,并统计一共有多少个素数 108 | count := 0 109 | fmt.Print("素数有:{") 110 | for v := range resChan { 111 | count++ 112 | fmt.Printf("%v ",v) 113 | } 114 | fmt.Printf("}共%v个。",count) 115 | 116 | end := time.Now().UnixNano() 117 | fmt.Printf("\n耗时=%v纳秒", end - start) 118 | } -------------------------------------------------------------------------------- /基础/管道/空接口类型channel.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main(){ 6 | case_3() 7 | } 8 | 9 | /** 10 | 如果要在channel内放入不同类型的数据,可以将channel的类型设置为interface{}类型(即空接口类型) 11 | 当空接口类型的channel放入结构体类型数据,取出时要类型断言,否则编译不通过。 12 | */ 13 | type People struct{ 14 | Name string 15 | Age int 16 | } 17 | func case_3(){ 18 | //声明channel并放入数据 19 | interfaceChan := make(chan interface{},4) 20 | interfaceChan <- People{"golang",20} //放入一个People类型结构体 21 | interfaceChan <- 15 //放入一个int类型数据 22 | interfaceChan <- "你好,GO!" //放入一个string类型数据 23 | chanMap := make(map[int]string,2) //声明一个map 24 | chanMap[5] = "hello" //给map赋值 25 | chanMap[9] = "golang" 26 | interfaceChan <- chanMap //放入一个map类型数据 27 | 28 | //channel是FIFO的存放原则,所以第一个取出的就是People类型结构体 29 | //取出People结构体数据 30 | people := <- interfaceChan //此时people不是结构体数据类型,而是interface{}类型 31 | fmt.Printf("people:%v\n",people)//直接输出是可以的 32 | //但是访问people.Name时就会编译不通过,因为此时的people不是结构体类型,而是interface{}类型,编译器找不到people的Name字段 33 | peopleTem := people.(People) //类型断言people是不是People结构体类型,用peopleTem接收 34 | fmt.Printf("peopleTem.Name:%v\n",peopleTem.Name) //类型断言people是People结构体类型后就可以访问它的Name字段 35 | 36 | numTem := <- interfaceChan //取出第二个int数据 37 | strTem := <- interfaceChan //取出第三个string数据 38 | mapTem := <- interfaceChan //取出第四个map数据 39 | fmt.Printf("numTem=%v strTem=%v mapTem=%v\n",numTem,strTem,mapTem) 40 | //同样map的具体元素同样也需要类型断言后才可访问 41 | mapTemTem := mapTem.(map[int]string) //mapTem不是map[int]string类型,而是空接口类型,需要类型断言为map[int]string类型 42 | fmt.Printf("mapTemTem[5]=%v mapTemTem[6]=%v\n",mapTemTem[5],mapTemTem[9]) //此时mapTemTem是map[int]string类型,可以访问他的元素 43 | } -------------------------------------------------------------------------------- /基础/管道/管道的关闭和遍历.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | case_4() 7 | case_5() 8 | } 9 | 10 | /** 11 | 管道的关闭 12 | 使用close(管道名)方法对管道进行关闭,管道关闭后只能取出数据不能放入数据 13 | */ 14 | func case_4(){ 15 | intChan := make(chan int,3) 16 | intChan <- 20 17 | intChan <- 30 18 | close(intChan)//关闭intChan管道 19 | numTem := <- intChan //管道关闭后还是可以取出数据的 20 | fmt.Printf("numTem=%v",numTem) 21 | } 22 | 23 | /** 24 | 管道的遍历 25 | */ 26 | func case_5(){ 27 | intChan := make(chan int,50) 28 | //给管道放入50个int数据 29 | for i := 1 ; i <= 50 ; i++ { 30 | intChan <- i 31 | } 32 | //管道遍历前要先关闭管道 不然当管道内数据取完后会报错 33 | close(intChan) //不关闭管道for range将管道内数据取完后还会接着取,因为数据已取完,再取就会报错。 34 | //使用for range对管道进行遍历 35 | for v := range intChan { 36 | fmt.Printf("v=%v ",v) 37 | } 38 | } -------------------------------------------------------------------------------- /基础/类型断言/类型断言入门.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main(){ 6 | case_1() 7 | case_2() 8 | case_3() 9 | } 10 | 11 | //创建一个空接口A 12 | type A interface{} 13 | //创建一个结构体B 14 | type B struct { 15 | name string 16 | } 17 | func case_1(){ 18 | var a A 19 | var b B = B{"结构体b"} 20 | a = b //一个空的接口没有定义任何方法,可以说所有的类型都实现了该接口,b实现了a所以b可以赋值给a,空接口可以接收任意类型 21 | var c B //再定义一个B数据类型的变量c 22 | //c = a.(B)就是类型断言,逻辑是:判断接口变量a是否指向B数据类型的变量,如果是就将接口变量a转成B数据类型变量并赋值给c,否则报错 23 | c = a.(B) //c必须是接口a指向的变量b的数据类型B,即变量c必须是()里的数据类型,接口a必须指向()里数据类型的变量 24 | fmt.Println(c) 25 | } 26 | 27 | 28 | func case_2(){ 29 | var a interface{} //空接口 30 | var b int = 12 31 | a = b //空接口可以接收任意类型 32 | c := a.(int) //c必须是接口a指向的变量b的数据类型int,即变量c必须是()里的数据类型,接口a必须指向()里数据类型的变量 33 | fmt.Printf("c的值:%v 数据类型:%T",c,c) 34 | } 35 | 36 | 37 | /** 38 | 带检测的类型断言 39 | 给断言语句加个判断,无论结果如何,保障后续代码继续执行 40 | */ 41 | func case_3(){ 42 | var a interface{} //空接口 43 | var b float64 = 5.8 44 | a = b //空接口可以接收任意类型 45 | //c断言后的值 result断言结果:成功为true,失败为false 46 | if c,result := a.(float64) ; result { 47 | fmt.Printf("断言成功:c的值:%v 数据类型:%T",c,c) 48 | } else { 49 | fmt.Printf("断言失败:c的值:%v 数据类型:%T",c,c) 50 | } 51 | fmt.Println("\n程序继续执行。。。") 52 | } -------------------------------------------------------------------------------- /基础/类型断言/类型断言实践.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | //创建一个接口 6 | type Usb interface { 7 | Start() //Usb设备开始工作 8 | Stop() //Usb设备停止工作 9 | } 10 | 11 | //创建一个可以传入Usb接口的对象 12 | type Computer struct { 13 | 14 | } 15 | /** 16 | Computer有一个Working方法拥有Usb接口,其他Usb设备可以通过Working拥有的这个Usb接口接入Computer 17 | */ 18 | func (computer Computer)Working(usb Usb) { //接口默认是引用变量,这里传入的usb对象是地址 19 | usb.Start() 20 | if phone , result := usb.(Phone) ; result { //类型断言判断传入的变量参数是不是Phone类型的,是就调用它的Call方法 21 | phone.Call() 22 | } 23 | usb.Stop() 24 | } 25 | //创建Usb手机设备 26 | type Phone struct { 27 | name string 28 | } 29 | func (phone Phone)Start(){ 30 | fmt.Println("手机开始工作。。。") 31 | } 32 | func (phone Phone)Call(){ 33 | fmt.Println("手机在打电话。。。") 34 | } 35 | func (phone Phone)Stop(){ 36 | fmt.Println("手机停止工作。。。") 37 | } 38 | //创建Usb相机设备 39 | type Camera struct { 40 | name string 41 | } 42 | func (camera Camera)Start(){ 43 | fmt.Println("相机开始工作。。。") 44 | } 45 | func (camera Camera)Stop(){ 46 | fmt.Println("相机停止工作。。。") 47 | } 48 | func main(){ 49 | var usbArr [3]Usb 50 | usbArr[0] = Phone{"phoneOne"} 51 | usbArr[1] = Camera{"cameraOne"} 52 | usbArr[2] = Phone{"phoneTwo"} 53 | var computer Computer 54 | for _ , v := range usbArr { 55 | computer.Working(v) 56 | } 57 | } -------------------------------------------------------------------------------- /基础/类型断言/类型断言应用.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Student struct{ 6 | name string 7 | } 8 | func typeJudge(variable ...interface{}){ 9 | for index , value := range variable { 10 | switch value.(type){ 11 | case int,int8,int32,int64 : 12 | fmt.Printf("第%v个变量%v是\"整数\"类型\n",index+1,value) 13 | case string : 14 | fmt.Printf("第%v个变量%v是\"string\"类型\n",index+1,value) 15 | case float32: 16 | fmt.Printf("第%v个变量%v是\"float32\"类型\n",index+1,value) 17 | case float64: 18 | fmt.Printf("第%v个变量%v是\"float64\"类型\n",index+1,value) 19 | case bool : 20 | fmt.Printf("第%v个变量%v是\"布尔\"类型\n",index+1,value) 21 | case Student : 22 | fmt.Printf("第%v个变量%v是\"Student\"类型\n",index+1,value) 23 | case *Student: 24 | fmt.Printf("第%v个变量%v是\"*Student\"类型\n",index+1,value) 25 | default: 26 | fmt.Printf("第%v个变量%v类型无法确定!!!\n",index+1,value) 27 | } 28 | } 29 | } 30 | func main(){ 31 | var a int8 = 15 32 | b := "hello golang!" 33 | var c = 65.5 34 | var d bool 35 | e := 502 36 | var f float32 = 12.8 37 | g := Student{"学生g"} 38 | h := &Student{"学生h"} 39 | typeJudge(a,b,c,d,e,f,g,h) 40 | } -------------------------------------------------------------------------------- /基础/结构体/结构体.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main(){ 6 | case_1() 7 | case_2() 8 | case_3() 9 | case_4() 10 | } 11 | 12 | /** 13 | 结构体的几种创建和赋值方式 14 | 使用和赋值结构体前先要创建结构体 15 | */ 16 | type Cat struct { //声明结构体格式:type 结构体名 struct {结构体字段} 17 | name string 18 | age int 19 | color string 20 | } 21 | 22 | func case_1() { 23 | var cat_1 Cat 24 | cat_1.name = "golang" 25 | cat_2 := Cat{ 26 | name: "cat_2", 27 | age: 15, 28 | color: "read", 29 | } 30 | cat_3 := Cat{"go", 18, "blue"} 31 | var cat_4 *Cat = new(Cat) 32 | (*cat_4).name = "cat_4" 33 | (*cat_4).age = 4 34 | var cat_5 *Cat = &Cat{ 35 | name: "cat_5", 36 | age: 5, 37 | color: "red", 38 | } 39 | //结构体也可以作为实例化变量时的数据类型,{}里可以添加结构体的字段(这样创建的结构体只可以添加一个字段) 40 | var dog struct{ name string } 41 | dog.name = "dog" 42 | fmt.Print(cat_1, cat_2, cat_3, *cat_4, *cat_5, dog) 43 | } 44 | 45 | /** 46 | 结构体各种数据类型字段的赋值 47 | */ 48 | type Person struct { 49 | name string 50 | age int 51 | scores [3]int 52 | slice []int 53 | map1 map[string]string 54 | } 55 | 56 | func case_2() { 57 | var person Person 58 | person.name = "person" 59 | person.age = 12 60 | person.scores[0] = 174 61 | person.scores[1] = 52 62 | person.slice = make([]int, 2) 63 | person.slice[1] = 100 64 | person.map1 = make(map[string]string) 65 | person.map1["一"] = "111" 66 | person.map1["二"] = "222" 67 | fmt.Print(person) 68 | } 69 | 70 | /** 71 | 结构体在内存中的存储顺序是连续的 72 | */ 73 | type Point struct { 74 | x, y int 75 | } 76 | type Rect struct { 77 | up, down Point //Rect成员的数据类型是 Point结构体 数据类型 78 | } 79 | 80 | func case_3() { 81 | line := Rect{Point{1, 2}, Point{5, 6}} 82 | //按创建和赋值的顺序打印他们的内存地址,可以看到他们的地址在内存中是连续的(计算机内存采用的是16进制) 83 | fmt.Printf("line.up.x的地址:%p\nline.up.y的地址:%p\nline.down.x的地址:%p\nline.down.y的地址:%p\n", &line.up.x, &line.up.y, &line.down.x, &line.down.y) 84 | fmt.Printf("line的数据类型:%T\nline.up的数据类型:%T\nline.up.x的数据类型:%T\n", line, line.up, line.up.x) 85 | } 86 | 87 | /** 88 | 结构体相互转换:两个结构体的字段完全相同的话,这两个结构体就可以相互转换(字段名称 字段数据类型 字段的数量都必须相同) 89 | */ 90 | type A struct { 91 | name string 92 | age int 93 | } 94 | type B struct { 95 | name string 96 | age int 97 | } 98 | type C A //自定义一个数据类型:名称是C,C的数据类型是A 99 | func case_4() { 100 | a := A{ 101 | name: "a", 102 | age: 1, 103 | } 104 | b := B{"b", 2} 105 | var c C 106 | fmt.Println(a, b, c) 107 | a = A(b) //A和B的字段的数量、名称、数据类型都完全相同,可以转换 108 | c = C(b) //A和B的字段的数量、名称、数据类型都完全相同,又因为A和C也完全相同,所以C和B完全形同,可以转换 109 | fmt.Print(a, b, c) 110 | } 111 | -------------------------------------------------------------------------------- /基础/结构体/结构体序列化.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | /** 9 | 结构体的每个字段上可以写个tar,该tar可以通过反射机制获取,常见的应用场景有序列化和反序列化 10 | */ 11 | type People struct { 12 | Name string `json:"name"` //json:"name"就是给Name字段加了个`json:"name"`的tar,tar必须要写在``内 13 | Age int `json:"age"` //当结构体字段加了tar时,json解析时就会解析为tar里定义的字段名 14 | Number string `json:"number"` //tar的格式:`json:"XXX"` 15 | } 16 | func main(){ 17 | people := People{"golang",21,"a1b2c3d4"} 18 | //将people序列化为json格式 19 | //json.Marshal()函数里使用了反射 20 | peopleStr , err := json.Marshal(people) 21 | if err != nil { 22 | fmt.Println("出现错误:",err) 23 | }else{ 24 | fmt.Println(string(peopleStr)) 25 | } 26 | } -------------------------------------------------------------------------------- /基础/继承/GO的继承.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | /** 6 | 继承:go中继承是一个结构体嵌入匿名结构体的方法实现 7 | 可以让某个类型的对象获得另一个类型的对象的属性的方法 8 | 它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。 9 | 10 | 多重继承:一个结构体嵌套了多个(两个或两个以上)匿名结构体 11 | 为了保证代码的简洁性,建议尽量不要使用多重继承 12 | */ 13 | 14 | func main(){ 15 | case_1() 16 | case_2() 17 | case_3() 18 | case_4() 19 | case_5() 20 | case_6() 21 | case_7() 22 | } 23 | 24 | /** 25 | 先定义一个有共同属性(相同字段、方法)的结构体 26 | */ 27 | type Student struct { 28 | name string 29 | score int 30 | } 31 | //绑定共同属性的方法是共同方法,即继承该结构体的对象都需要实现的方法 32 | func (student *Student) ShowInfo(){ 33 | fmt.Printf("姓名:%v 成绩:%v",(*student).name,(*student).score) 34 | } 35 | func (student *Student) setScore(score int){ 36 | (*student).score = score 37 | } 38 | 39 | 40 | //要继承Student的结构体 41 | type pupil struct { 42 | Student //pupil嵌入匿名结构体Student,此时pupil具有Student的所有字段和方法 43 | } 44 | //特有方法或字段(Student中没有的)要自己实现,并绑定自己 45 | func (pupil *pupil)testing(){ 46 | fmt.Println("pupil testing...") 47 | } 48 | /** 49 | 当结构体和匿名字段有相同字段或方法时,编译器会采用就近访问原则 50 | 如想访问匿名结构体字段,可以通过匿名结构体名来区分 51 | */ 52 | func case_1(){ 53 | pupil := &pupil{} 54 | (*pupil).Student.name = "golang" 55 | (*pupil).testing() 56 | (*pupil).Student.setScore(60) 57 | (*pupil).Student.ShowInfo() 58 | } 59 | 60 | 61 | //要继承Student的结构体 62 | type graduate struct { 63 | Student //graduate嵌入匿名结构体Student,此时graduate具有Student的所有字段和方法 64 | end bool //非公有字段要自己添加 65 | } 66 | //特有方法或字段(Student中没有的)要自己实现,并绑定自己 67 | func (graduate *graduate)testing(){ 68 | fmt.Println("graduate testing...") 69 | } 70 | //graduate也绑定了和Student共有结构体绑定的ShowInfo()方法,方法名相同 71 | func (graduate *graduate) ShowInfo(){ 72 | fmt.Printf("姓名:%v 成绩:%v end:%v",(*graduate).name,(*graduate).score,(*graduate).end) 73 | } 74 | /** 75 | 如果结构体和匿名结构体无相同字段或方法,就可以简化访问 76 | 编译器先会找声明的对象对应结构体的字段和绑定的方法,没有的话就会去嵌入的匿名结构体里找字段或与匿名结构体绑定的方法,直到找不到该字段或方法 77 | 即无需通过匿名结构体名,可以直接访问匿名结构体里的方法和字段 78 | 但是如果有和匿名结构体相同的字段时,就需要通过匿名结构体名来区分,因为编译器是就近访问原则,不区分就无法访问匿名结构体里的字段和方法 79 | */ 80 | func case_2(){ 81 | graduate := &graduate{} 82 | (*graduate).name = "hello golang" //结构体和匿名结构体无相同字段,可以简化访问 83 | (*graduate).end = true 84 | (*graduate).testing() 85 | (*graduate).setScore(80) //结构体和匿名结构体无相同方法,可以简化访问 86 | (*graduate).ShowInfo() //结构体和匿名结构体有相同方法,采用就近访问原则,先访问graduate里的该访问,没有才会去嵌入的匿名结构体里找 87 | } 88 | 89 | 90 | /** 91 | 结构体嵌入两个或多个结构体 92 | 在两个匿名结构体都有相同的字段和方法(同时结构体本身没有该相同的字段或方法) 93 | 在访问时,如果不指明哪个结构体,即不通过匿名结构体名访问该字段或方法,直接简化访问,编译器就会报错 94 | */ 95 | type case_3_1 struct { 96 | name string 97 | age int 98 | id string 99 | } 100 | type case_3_2 struct { 101 | name string 102 | score int 103 | id string 104 | } 105 | //case_91_3嵌入case_91_1和case_91_2两个匿名结构体,这里就使用了多重继承 106 | type case_3_3 struct { 107 | case_3_1 108 | case_3_2 109 | id string 110 | } 111 | func case_3(){ 112 | case_3_3 := case_3_3{} 113 | case_3_3.case_3_1.name = "hello" //必须通过嵌入匿名结构体名来访问,直接简化访问会报错 114 | case_3_3.case_3_1.name = "golang" 115 | case_3_3.age = 20 //嵌入的两个或多个结构体不相同的字段,可以直接简化访问 116 | case_3_3.id = "hello golang!" //嵌入的两个或多个结构体有相同字段,同时自身结构体也有,会采用就近访问原则只访问自身该相同字段,嵌入的结构体不会打架,所以可以直接简化访问,不会报错 117 | } 118 | 119 | 120 | /** 121 | 如果一个结构体嵌入了有名结构体,那么这种模式就称为组合 122 | 访问嵌入的结构体字段或方法时,就必须通过该结构体名访问,不能直接简化访问,会报错 123 | */ 124 | type case_4_1 struct { 125 | name string 126 | } 127 | type case_4_2 struct { 128 | a case_4_1 //嵌入了有名结构体 a是名 case_92_1是数据类型 129 | } 130 | func case_4() { 131 | case_4_2 := case_4_2{} 132 | case_4_2.a.name = "golang" //嵌入有名结构体时访问其字段必须通过 133 | } 134 | 135 | 136 | /** 137 | 嵌入匿名结构体后,可以在常见变量时,直接给匿名结构体字段赋值 138 | */ 139 | type case_5_1 struct { 140 | name string 141 | age int 142 | } 143 | type case_5_2 struct { 144 | id string 145 | score float64 146 | } 147 | type case_5_3 struct { 148 | case_5_1 149 | case_5_2 150 | } 151 | //和普通结构体创建时赋值一样,注意好嵌套的顺序和层级关系即可 152 | func case_5(){ 153 | c1 := case_5_3{case_5_1{"hello golang",16}, case_5_2{"2021041301",88.5}} 154 | c2 := case_5_3{ 155 | case_5_1{ 156 | age : 18, //指定字段时就无需按照结构体里字段的顺序进行赋值了 157 | name : "hello go", 158 | }, 159 | case_5_2{ 160 | id : "2021041302", 161 | score : 90.7, 162 | }, 163 | } 164 | fmt.Println("c1:",c1,"c2:",c2) 165 | } 166 | 167 | 168 | /** 169 | 嵌入匿名结构体的指针,效率更高 170 | */ 171 | type case_6_1 struct { 172 | name string 173 | age int 174 | } 175 | type case_6_2 struct { 176 | id string 177 | score float64 178 | } 179 | type case_6_3 struct { 180 | *case_6_1 181 | *case_6_2 182 | } 183 | //在创建时传入case_93_1和case_93_2的地址即可 184 | func case_6(){ 185 | c1 := case_6_3{&case_6_1{"hello",20},&case_6_2{"2021041303",60.5}} 186 | c2 := case_6_3{ 187 | &case_6_1{ 188 | age : 19, //指定字段时就无需按照结构体里字段的顺序进行赋值了 189 | name : "golang", 190 | }, 191 | &case_6_2{ 192 | id : "2021041304", 193 | score : 70.5, 194 | }, 195 | } 196 | fmt.Println("c1:",c1,"c2:",c2) 197 | //c1和c2存储的都是case_6_1和case_6_2两个嵌入匿名结构体的指针,取值要使用指针变量,并且嵌入的匿名结构体指针取值要一个一个取 198 | fmt.Println("c1:",*c1.case_6_1,*c1.case_6_2,"c2:",*c2.case_6_1,*c2.case_6_2) 199 | } 200 | 201 | 202 | /** 203 | 结构体嵌套的匿名结构体是一个数据类型 204 | 那么不仅是结构体数据类型,其他数据类型也可以匿名嵌入结构体,访问时和嵌入的匿名结构体访问方式相同 205 | */ 206 | type case_7_1 struct { 207 | name string 208 | age int 209 | } 210 | type case_7_2 struct { 211 | case_7_1 212 | int //这里的int和case_7_1是一样的作用,也是个case_7_2里嵌套的匿名数据类型,但int里没有字段,只有一个变量名作为case_7_2的字段,int数据类型,int只能有一个 213 | number int //如果有第二个int字段就必须给其指定名字,否则两个int会报错 214 | } 215 | func case_7(){ 216 | c1 := case_7_2{} 217 | c1.name = "hello" 218 | c1.age = 18 219 | c1.int = 108 220 | c1.number = 56 221 | fmt.Printf("c1:%v\nc1数据类型:%T\nc1.int数据类型:%T",c1,c1,c1.int) 222 | } -------------------------------------------------------------------------------- /基础/递归/递归.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main(){ 6 | case_1(10) 7 | case_2(20) 8 | case_3(30) 9 | case_4(40) 10 | case_5(50) 11 | } 12 | 13 | /** 14 | 递归:输入一个数如果大于2就递归并打印 15 | */ 16 | func case_1(number int) { 17 | if number > 2 { 18 | number-- 19 | case_1(number) 20 | } 21 | fmt.Println(number) 22 | } 23 | 24 | /** 25 | 递归:输入一个数如果大于2就递归,小于等于2就打印 26 | */ 27 | func case_2(number int) { 28 | if number > 2 { 29 | number-- 30 | case_2(number) 31 | } else { 32 | fmt.Println(number) 33 | } 34 | } 35 | 36 | /** 37 | 方程:已知f(1)=3;f(n)=2*f(n-1)+1,求f(n)的值 38 | */ 39 | func case_3(n int) int { 40 | if n == 1 { 41 | return 3 //当条件中n==1时,返回条件中f(1)=3给出的结果3 42 | } else { 43 | return 2*case_3(n-1) + 1 //n未知就返回条件开始递归 44 | } 45 | } 46 | 47 | /** 48 | 斐波那契数列 49 | 输入斐波那契数列中数的位置,就返回斐波那契数列中该位置对应的数,数列中第一位和第二位的值已知 50 | 斐波那契数列:1 1 2 3 5 8....构成了一个序列,这个数列的特点:前面相邻两项之和,构成了后一项。 51 | */ 52 | func case_4(position int) int { 53 | if position == 1 || position == 2 { //返回已知值 54 | return 1 55 | } else { 56 | return case_4(position-2) + case_4(position-1) //未知就返回条件开始递归 57 | } 58 | } 59 | 60 | /* 61 | 有一堆桃子,猴子第一天吃了其中一半,并再多了一个! 62 | 以后每天猴子吃其中一半再多吃一个,当吃到第十天(还没吃),发现只有1个桃子了 63 | 问:第x(1 <= x <= 10)天还有多少个桃子 64 | */ 65 | func case_5(number int) int { 66 | if number > 10 || number < 1 { 67 | return 0 //返回0表示输入错误,不能超过10天也不能小于1天 68 | } 69 | if number == 10 { 70 | return 1 //返回已知值 71 | } else { 72 | return (case_5(number+1) + 1) * 2 //未知就返回条件开始递归 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /基础/错误处理/函数错误异常处理.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | func main(){ 9 | case_1() 10 | case_2() 11 | } 12 | 13 | /** 14 | 函数自定义异常信息返回值 15 | */ 16 | func case_1(){ 17 | errResult := case_1_1("hello golang!") //errResult接收函数执行错误信息,如果为nil则未发生错误 18 | if errResult != nil { //错误信息返回值不为空 19 | panic(errResult) //panic():函数抛出一个异常,括号里写自定义异常内容,string数据类型 20 | } 21 | } 22 | //该函数只有执行发生异常时才会有返回值,返回值为异常原因 没有异常返回值为nil 23 | func case_1_1(name string) error { 24 | if name == "hello golang"{ 25 | return nil //函数正常执行未发生异常返回nil 26 | }else{ 27 | return errors.New("发生错误!!!") //errors.New():自定义异常信息返回值,括号里写自定义内容,string数据类型 28 | } 29 | } 30 | 31 | 32 | /** 33 | Go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理。 34 | */ 35 | func case_2(){ 36 | defer func(){ 37 | if err := recover() ; err != nil { //在defer中通过recover捕获这个异常,然后正常处理。 38 | fmt.Println("发生错误!!!\n错误原因:", err) 39 | } 40 | }() 41 | panic("抛出panic异常") //抛出一个panic的异常 42 | } -------------------------------------------------------------------------------- /基础/闭包/闭包.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | /** 9 | 闭包:闭包就是一个函数和与其相关的引用环境组合的一个整体 10 | */ 11 | func main(){ 12 | case_1() 13 | case_2() 14 | } 15 | 16 | func case_1() { 17 | //这里f用来接收case_1_1()函数返回值 因为返回值类型是一个匿名函数 所以当前f是一个匿名函数 func (x int) int 18 | f := case_1_1() //f接收一个闭包 case_1_1()返回一个闭包 19 | fmt.Println(f(1)) //f(1) 相当于调用匿名函数func (x int) int 传入参数1 调用闭包f传入参数1 20 | fmt.Println(f(2)) 21 | fmt.Println(f(3)) 22 | } 23 | //累加器 24 | // func (x int) int是case_79_1()函数的返回值类型 因为返回值类型func(x int)int没有函数名,所以是一个匿名函数 25 | func case_1_1() func (int) int { //函数返回值是一个匿名函数 26 | /*******************************以下内容形成一个闭包***********************************/ 27 | var n int = 10 28 | return func (number int) int { 29 | n = n + number 30 | return n //匿名函数返回一个int类型数据 31 | } 32 | //这里可以这么理解:闭包是类 返回值匿名函数是操作 n是字段 匿名函数和n构成一个闭包 33 | //n只在调用函数时初始化一次 当我们反复调用闭包时 n是不断累加的 f(1)执行过后n就更新为11 f(2)执行过后n就更新为13(11+2) 34 | //要弄明白闭包 就要分析出匿名函数引用到了哪些变量 因为匿名函数和它引用到的变量构成一个闭包 35 | //n只在调用函数时初始化一次 调用闭包时就不会再初始化了 (变量不会再初始化 上次操作执行后n的值最后是多少 下次调用闭包时n初始值就是多少) 36 | /******************************以上内容形成一个闭包**************************************/ 37 | } 38 | 39 | 40 | /** 41 | 编写一个函数传入指定文件后缀名 返回一个闭包 42 | 调用闭包 传入一个文件名 如果该文件的后缀名是 以函数传入的指定文件后缀名 结尾就返回原文件名 43 | 如果不是就给追加 函数传入的指定文件后缀名 再返回追加后的文件名 44 | */ 45 | func case_2() { 46 | s := makeSuffix(".jpg") //给函数传入指定文件后缀名.jpg 函数返回一个闭包s 47 | fmt.Println(s("aaa.jpg")) //调用闭包 给它传入aaa.jpg 48 | fmt.Println(s("bbb")) 49 | fmt.Println(s("ccc.avi")) 50 | } 51 | //makeSuffix(suffix string)函数返回值类型为 func (string) string 52 | func makeSuffix(suffix string) func (string) string { 53 | //返回一个匿名函数 54 | return func (name string) string { 55 | endName := "nil" 56 | if strings.HasSuffix(name,suffix) { //strings.HasSuffix(name,suffix):判断name是否以suffix结尾 是返回true 不是返回false 57 | endName = name 58 | }else{ 59 | endName = name + suffix //给name追加suffix 60 | } 61 | return endName 62 | } 63 | } 64 | //因为返回的匿名函数func (name string) string引用到了suffix 所以返回的匿名函数和函数makeSuffix(suffix string)的suffix构成一个闭包 65 | //使用传统方法也可以实现此功能 但是传统方法每次都需要传入后缀名.jpg 而闭包因为可以保留上次引用的值 所以我们传入一次就可以重复使用 -------------------------------------------------------------------------------- /基础/随机数/随机数.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func main(){ 10 | case_1() 11 | case_2() 12 | } 13 | 14 | func case_1(){ 15 | //go中生成随机数时先要给math的rand中Seed一个当前系统时间 16 | //系统时间秒和毫秒都可以,毫秒随机生成的更快一些 17 | rand.Seed(time.Now().Unix()) 18 | result := rand.Intn(100)//获取100以内的随机数,不包括0和括号里的数(0到该数之内) 19 | fmt.Println(result) 20 | } 21 | 22 | func case_2(){ 23 | var count int 24 | for{ 25 | rand.Seed(time.Now().UnixNano()) 26 | num := rand.Intn(100)-1 27 | fmt.Println(num) 28 | count++ 29 | if num == 0 { 30 | break 31 | } 32 | } 33 | fmt.Printf("共%d次生成随机数5",count) 34 | } -------------------------------------------------------------------------------- /进阶/_context/introduce.go: -------------------------------------------------------------------------------- 1 | package main 2 | -------------------------------------------------------------------------------- /进阶/_context/one.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | ctx, cancel := context.WithCancel(context.Background()) 11 | 12 | go Worker(ctx, "node-1") 13 | go Worker(ctx, "node-2") 14 | go Worker(ctx, "node-3") 15 | 16 | time.Sleep(time.Second * 5) 17 | 18 | fmt.Println("stop worker") 19 | cancel() 20 | 21 | time.Sleep(time.Second * 3) 22 | } 23 | 24 | func Worker(ctx context.Context, name string) { 25 | for { 26 | select { 27 | case <-ctx.Done(): 28 | fmt.Println(name + " done!") 29 | return 30 | default: 31 | fmt.Println(name + " ing...") 32 | time.Sleep(time.Second) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /进阶/_context/three.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "sync" 7 | ) 8 | 9 | func main() { 10 | wg := sync.WaitGroup{} 11 | wg.Add(2) 12 | ctx := context.WithValue(context.Background(), "one", "go语言") 13 | go g1(ctx, &wg) 14 | wg.Wait() 15 | } 16 | 17 | func g1(ctx context.Context, wg *sync.WaitGroup) { 18 | fmt.Println("g1:", ctx.Value("one")) 19 | go g2(context.WithValue(ctx, "two", "hello word!"), wg) 20 | wg.Done() 21 | } 22 | 23 | func g2(ctx context.Context, wg *sync.WaitGroup) { 24 | fmt.Println("g2:", ctx.Value("one"), ctx.Value("two")) 25 | wg.Done() 26 | } 27 | -------------------------------------------------------------------------------- /进阶/_context/two.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | ctx, _ := context.WithTimeout(context.Background(), time.Second*3) 11 | worker(ctx) 12 | } 13 | 14 | func worker(ctx context.Context) { 15 | select { 16 | case <-ctx.Done(): 17 | fmt.Println("超时了。。。") 18 | case <-job(): 19 | fmt.Println("任务完成。。。") 20 | } 21 | } 22 | 23 | func job() <-chan struct{} { 24 | ch := make(chan struct{}) 25 | go func() { 26 | time.Sleep(time.Second * 3) 27 | ch <- struct{}{} 28 | }() 29 | return ch 30 | } 31 | -------------------------------------------------------------------------------- /进阶/_goroutine/介绍.go: -------------------------------------------------------------------------------- 1 | package main 2 | /** 3 | 进程和线程: 4 | 进程就是程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位。 5 | 线程是进程的一个执行实例,是程序执行的最小单元。 6 | 一个进程可以创建和销毁多个线程,同一个进程中的多个线程可以并发执行。 7 | 一个程序至少有一个进程,一个进程至少有一个线程。 8 | 9 | 并发和并行: 10 | 并发:多个线程在同一个CPU上运行,在某个时间点上只有一个线程在运行,这就是并发。 11 | 并行:多个线程在多个CPU上运行,在某个时间点上有多个(CPU个数)线程在运行,这就是并行。 12 | 理解:并发是一个人同时吃三个馒头,而并行是三个人同时吃三个馒头。 13 | 14 | GO主线程(可以称为线程、也可以理解为进程)和GO协程: 15 | GO主线程:一个GO主线程上,可以起多个协程(轻轻松松起上万个协程),协程是轻量级的线程。 16 | GO协程特点:1.有独立的栈空间 2.共享程序堆空间 3.调度由用户控制 4.协程是轻量级的线程 17 | 区别: 18 | 1.如果主线程退出了,主线程起的协程即使没有执行完毕,也会退出;当协程执行完任务后就会结束,即使主线程还在运行。 19 | 2.主线程是一个物理线程,直接作用在CPU上。是重量级的,非常消耗资源。 20 | 3.协程是从线程开启的,是轻量级的线程,是逻辑态,对资源消耗很少。 21 | 4.GO的协程的机制,使GO主线程可以轻松开启上万个协程,运行非常稳定,这就是突显GO在并发上的优势。 22 | */ 23 | -------------------------------------------------------------------------------- /进阶/_goroutine/入门.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | /** 9 | 主线程每隔一秒输出一句“Hello World!” 10 | 在主线程中开启一个协程,协程每隔一秒输出一句“你好,世界!”。 11 | */ 12 | func hello_cn(){ 13 | for i := 1 ; i <= 10 ; i++{ 14 | fmt.Println("你好,世界!----",i) 15 | time.Sleep(time.Second) 16 | } 17 | } 18 | func hello_en(){ 19 | for i := 1 ; i <= 10 ; i++{ 20 | fmt.Println("Hello World!----",i) 21 | time.Sleep(time.Second) 22 | } 23 | } 24 | func main(){ 25 | go hello_cn() 26 | hello_en() 27 | } -------------------------------------------------------------------------------- /进阶/_goroutine/异常处理.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | /** 9 | 当开启协程出现panic时,如果程序员没有捕获这个panic,整个程序就会崩溃 10 | 如果主线程起的一个协程发生panic,那么主线程和主线程起的所有协程都将崩溃 11 | 使用recover()捕获协程的panic,可以防止程序崩溃;当主线程起的一个协程发生panic时,可以保证主线程和主线程起的所有协程继续执行。 12 | */ 13 | func main(){ 14 | //看warn()协程发生的异常是否会影响到hello()协程的执行 15 | go hello() 16 | go warn() 17 | time.Sleep(time.Second * 3) //主线程休眠3秒等待协程执行 18 | } 19 | //打印10句hello golang! 20 | func hello(){ 21 | for i := 0 ; i < 10 ; i++ { 22 | fmt.Println("hello golang!") 23 | } 24 | } 25 | //使用defer + recover来捕获函数的异常 26 | func warn(){ 27 | //defer后的语句待函数执行完毕再执行 28 | defer func(){ 29 | //捕获当前函数发生的异常 30 | if err := recover() ; err != nil { 31 | fmt.Printf("warn()发生异常:%v\n",err) 32 | } 33 | }() 34 | panic("warn()抛出一个恐慌!!!") //抛出一个异常 35 | } 36 | -------------------------------------------------------------------------------- /进阶/_goroutine/控制协程数量.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | // 每次发放五个协程,所有协程执行完,再发放五个。 10 | func main() { 11 | ch1 := make(chan struct{}, 5) 12 | ch2 := make(chan struct{}, 5) 13 | 14 | go sendGoroutine(ch1, ch2) 15 | 16 | for { 17 | go job(ch2) 18 | <-ch1 19 | } 20 | } 21 | 22 | func job(ch2 chan struct{}) { 23 | fmt.Println(rand.Intn(100)) 24 | ch2 <- struct{}{} 25 | } 26 | 27 | func sendGoroutine(ch1, ch2 chan struct{}) { 28 | n := 0 29 | for range ch2 { 30 | if n%5 == 0 { 31 | fmt.Println("开始发放。。。") 32 | for i := 0; i < 5; i++ { 33 | ch1 <- struct{}{} 34 | } 35 | time.Sleep(time.Second * 2) 36 | } 37 | n++ 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /进阶/_goroutine/统计协程执行时间.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | type myWaitGroup struct { 11 | sync.WaitGroup 12 | t int64 13 | } 14 | 15 | func newMyWaitGroup() *myWaitGroup { 16 | return &myWaitGroup{} 17 | } 18 | 19 | func (w *myWaitGroup) Done(start time.Time) { 20 | defer w.WaitGroup.Done() 21 | defer func() { 22 | w.t += int64(time.Since(start).Seconds()) 23 | }() 24 | } 25 | 26 | func myJob(wg *myWaitGroup, start time.Time) { 27 | t := time.Second * time.Duration(rand.Intn(4)) 28 | defer wg.Done(start) 29 | time.Sleep(t) 30 | fmt.Println("运行了", t, "秒") 31 | } 32 | 33 | func main() { 34 | start := time.Now() 35 | wg := newMyWaitGroup() 36 | for i := 0; i < 5; i++ { 37 | wg.Add(1) 38 | go myJob(wg, start) 39 | } 40 | wg.WaitGroup.Wait() 41 | println("一共运行了", wg.t, "秒") 42 | } 43 | -------------------------------------------------------------------------------- /进阶/_goroutine/设置CPU数量.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | ) 7 | 8 | func main() { 9 | cpuNum := runtime.NumCPU() //获取当前设备CPU核数目 10 | fmt.Printf("cpuNum = %v\n数据类型:%T\n", cpuNum, cpuNum) 11 | 12 | runtime.GOMAXPROCS(4) //设置当前程序使用多少个CPU核数目 13 | fmt.Println("设置成功。") 14 | } 15 | -------------------------------------------------------------------------------- /进阶/_goroutine/资源竞争.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | /** 10 | 使用goroutine完成 11 | 计算 1-10 各个数的阶乘,并把各个数的阶乘放入到map中。 12 | 13 | 通过 lock sync.Mutex 这个全局变量“锁”解决协程资源竞争问题 14 | sync.Mutex是GO自带包sync里的结构体Mutex,它有两个方法:加锁Lock() 和 解锁Unlock() 15 | */ 16 | var ( 17 | myMap = make(map[int]int,10) 18 | lock sync.Mutex //lock是一个全局变量的锁,数据类型是:sync.Mutex。 作用:全局互斥锁 19 | ) 20 | //计算传入参数n的阶乘,并存入myMap中 21 | func calculation(n int){ 22 | res := 1 23 | for i := 1 ; i <= n ; i++ { 24 | res *= n 25 | } 26 | lock.Lock() //加锁,防止多个协程使用myMap时出现协程资源竞争问题 27 | myMap[n] = res 28 | lock.Unlock()//解锁,使用完myMap就解锁 29 | } 30 | //开启多个协程求 1-10 的阶乘 31 | func main(){ 32 | for i := 1 ; i <= 10 ; i++ { 33 | go calculation(i) 34 | } 35 | time.Sleep(time.Second * 3) //休息3秒,不然主线程过早退出,没执行完的协程也会跟着退出,myMap里的数据会不完整。 36 | lock.Lock() //加锁,主线程不知道myMap是否使用完毕,有可能会出现资源竞争问题,所以遍历myMap也要加锁 37 | for index , value := range myMap { 38 | fmt.Printf("myMap[%v] = %v\n",index,value) 39 | } 40 | lock.Unlock()//解锁,使用完myMap就解锁 41 | } -------------------------------------------------------------------------------- /进阶/_waitGroup/introduce.go: -------------------------------------------------------------------------------- 1 | package main 2 | -------------------------------------------------------------------------------- /进阶/_waitGroup/one.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | // 两个协程,要求实现交替打印的效果,从1开始打印到20 9 | func main() { 10 | wg := sync.WaitGroup{} 11 | wg.Add(2) 12 | 13 | ch1 := make(chan bool, 0) 14 | ch2 := make(chan bool, 0) 15 | 16 | go func() { 17 | defer wg.Done() 18 | for i := 1; i < 20; i += 2 { 19 | fmt.Println("第1个协程打印:", i) 20 | ch2 <- true 21 | <-ch1 22 | } 23 | }() 24 | 25 | go func() { 26 | defer wg.Done() 27 | for i := 2; i < 21; i += 2 { 28 | <-ch2 29 | fmt.Println("第2个协程打印:", i) 30 | ch1 <- true 31 | } 32 | }() 33 | 34 | wg.Wait() 35 | fmt.Println("打印完成。。。") 36 | } 37 | -------------------------------------------------------------------------------- /进阶/_waitGroup/two.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | type Counter struct { 10 | mu sync.Mutex 11 | count uint64 12 | } 13 | 14 | func (c *Counter) Increase() { 15 | c.mu.Lock() 16 | defer c.mu.Unlock() 17 | c.count++ 18 | fmt.Println("当前value=", c.count) 19 | } 20 | 21 | func Worker(c *Counter, wg *sync.WaitGroup) { 22 | defer wg.Done() // 执行后WaitGroup的计数值-1 23 | time.Sleep(time.Second) 24 | c.Increase() 25 | } 26 | 27 | func main() { 28 | var counter Counter 29 | var wg sync.WaitGroup 30 | 31 | wg.Add(10) // 设置WaitGroup的起始值 32 | 33 | for i := 0; i < 10; i++ { 34 | go Worker(&counter, &wg) 35 | } 36 | 37 | // wg.Wait():goroutine直阻塞 直到WaitGroup的计数值变为0 才执行后面的代码 38 | wg.Wait() 39 | 40 | fmt.Println("所有工作完成,count最终结果:", counter.count) 41 | } 42 | -------------------------------------------------------------------------------- /进阶/反射/反射介绍.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /** 4 | 反射介绍: 5 | 1.反射在运行时可以动态获取变量的各种信息,比如变量的类型(type),类别(kind)。 6 | 2.如果是结构体变量,还可以获取结构体本身的信息(包括结构体字段、方法)。 7 | 3.通过反射,可以修改变量的值,可以调用其绑定的方法。 8 | 4.使用反射,需要import "reflect" 9 | 10 | 11 | 反射应用场景: 12 | 1.不知道接口调用哪个函数,根据传入参数在运行时确定调用的具体接口。 13 | 2.对结构体序列化时,如果结构体有指定Tag,也会使用到反射生成对应的字符串。 14 | 15 | 16 | 反射的函数和概念: 17 | 1.reflect.TypeOf(变量名),返回reflect.Type类型。 18 | 2.reflect.ValueOf(变量名),返回reflect.Value类型;通过reflect.Value可以获取变量的更多信息。 19 | 3.变量、interface{}和reflect.Value是可以相互转换的。 20 | 举例: 21 | var stu Student stu这是一个Student结构体类型的变量 22 | stu在作为参数传给函数时,函数用interface{}数据类型来接收 23 | stu转reflect.Type: rVal := reflect.ValueOf(stu) 24 | reflect.Type转interface{}: iVal := rVal.interface() 25 | interface{}转原来变量,使用类型断言: v := iVal.(Student) 26 | 27 | 28 | 反射注意事项和细节: 29 | 1.reflect.Type().Kind()和reflect.Value().Kind()都可以获取变量的类别,返回的是一个常量。 30 | 2.Type是类型,Kind是类别;两者可能相同也可能不同。 31 | var num int = 10 num的Type是int,Kind也是int 32 | var stu Student stu的Type是main.Student,Kind是struct 33 | 3.使用反射获取变量的值,要类型匹配;比如修改int类型的变量就应该使用reflect.Value(变量).Int() 34 | 4.通过反射修改变量的值,应通过变量的指针完成修改,这样才能改变传入变量的值;使用reflect.Value().Elem()获取传入指针变量的值。 35 | */ 36 | -------------------------------------------------------------------------------- /进阶/反射/反射入门.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | func main() { 9 | case_1() 10 | case_2() 11 | } 12 | 13 | /** 14 | 基本数据类型反射操作 15 | */ 16 | func case_1() { 17 | var num int64 = 58 18 | case_1_1(num) 19 | } 20 | func case_1_1(model interface{}) { 21 | //获取model的reflect.Type 22 | rType := reflect.TypeOf(model) 23 | //获取model的reflect.Value 24 | rVal := reflect.ValueOf(model) 25 | //获取变量的Kind 26 | tKind := rType.Kind() 27 | rKind := rVal.Kind() 28 | fmt.Printf("tKind=%v(%T) rKind=%v(%T)\n", tKind, tKind, rKind, rKind) 29 | //获取model的具体值 30 | modelValue := rVal.Int() 31 | //将model转为传入函数之前的变量num:先将rVal转为interface{}类型,再通过类型断言转为变量num 32 | iVal := rVal.Interface() //将model获取的的reflect.Value转为interface{}类型 33 | num := iVal.(int64) 34 | fmt.Printf("rType=%v(%T)\nrVal=%v(%T)\nmodelValue=%v(%T)\niVal=%v(%T)\nnum=%v(%T)\n", rType, rType, rVal, rVal, modelValue, modelValue, iVal, iVal, num, num) 35 | } 36 | 37 | /** 38 | 结构体反射操作 39 | */ 40 | type Student struct { 41 | Name string 42 | Age int 43 | } 44 | 45 | func case_2() { 46 | stu := Student{ 47 | Name: "hello golang!", 48 | Age: 18, 49 | } 50 | case_2_1(stu) 51 | } 52 | func case_2_1(model interface{}) { 53 | //获取model的reflect.Type 54 | rType := reflect.TypeOf(model) 55 | //获取model的reflect.Value 56 | rVal := reflect.ValueOf(model) 57 | fmt.Printf("rType=%v(%T) rVal=%v(%T)\n", rType, rType, rVal, rVal) 58 | //获取变量的Kind 59 | tKind := rType.Kind() 60 | rKind := rVal.Kind() 61 | fmt.Printf("tKind=%v(%T) rKind=%v(%T)\n", tKind, tKind, rKind, rKind) 62 | //获取model的具体值 63 | modelValue := rVal.String() 64 | //将model转为传入函数之前的变量num:先将rVal转为interface{}类型,再通过类型断言转为变量num 65 | iVal := rVal.Interface() //将model获取的的reflect.Value转为interface{}类型 66 | str := iVal.(Student) //类型断言 67 | strName := str.Name //str经过类型断言后才能访问它的字段Name 68 | fmt.Printf("modelValue=%v(%T)\niVal=%v(%T)\nnum=%v(%T)\nstrName=%v(%T)\n", modelValue, modelValue, iVal, iVal, str, str, strName, strName) 69 | } 70 | -------------------------------------------------------------------------------- /进阶/反射/反射的实例3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | /** 9 | 使用反射创建并操作结构体: 10 | model是User结构体的指针 rType是通过reflect.TypeOf(model)获取的model指针的Type 11 | 此时获取的rType是指针变量的Type 要想获取具体值就要通过rType.Elem()获取指向的值 12 | 获取到具体值后通过reflect.New(rTypeValue)获取到User结构体的Value,返回的rValue也是个指针类型 13 | 再将指针类型的rValue通过rValue.Elem()获取到指向的值rValueValue 14 | 通过*model的Value.Elem()即rValueValue就可以对User结构体进行赋值 15 | */ 16 | 17 | type User struct { 18 | Admin string 19 | Password string 20 | } 21 | 22 | func main() { 23 | var ( 24 | model *User //User指针类型的变量model 25 | rType reflect.Type 26 | rValue reflect.Value 27 | ) 28 | 29 | rType = reflect.TypeOf(model) //获取类型为*User的model变量的Type 30 | fmt.Printf("rType的类别是:%v\n", rType.Kind()) 31 | rTypeValue := rType.Elem() //获取rType指向的值 32 | fmt.Printf("rType所指向:%v 类型是:%v\n", rTypeValue, rTypeValue.Kind()) 33 | 34 | //返回一个Value类型值,它的类别是指针; 它持有一个指向类型为User结构体的新指针 35 | rValue = reflect.New(rTypeValue) //此时rValue指向一个User类型的空结构体 36 | fmt.Printf("rValu的类别是:%v\n", rValue.Kind()) 37 | rValueValue := rValue.Elem() //获取rValue指向的值,此时rValueValue是一个空的User结构体变量 38 | fmt.Printf("rValu所指向:%v 类型是:%v\n", rValueValue, rValueValue.Kind()) 39 | 40 | //将model转为传入函数之前的变量*User:先将rValue转为interface{}类型,再通过类型断言转为变量*User 41 | //类型断言 rValue.Interface() 是否为 *User 类型 断言成功就可以对model进行和User结构体一样的操作 42 | model = rValue.Interface().(*User) //此时的model是一个指针类型,指向User类型 43 | fmt.Printf("model的类型是:%v 指向:%v\n", model, *model) 44 | 45 | //给获取的rValue指向的值 User 结构体变量赋值 46 | //因为是结构体,所以赋值要分别给它的字段进行赋值 47 | rValueValue.FieldByName("Admin").SetString("golang") 48 | rValueValue.FieldByName("Password").SetString("hello World!") 49 | 50 | //理解:rValueValue就是结构体指针model的Value,利用反射可以通过结构体指针model的Value.Elem()对model所指向的结构体进行赋值 51 | //最后打印*model就可以这个赋值后的User结构体 52 | fmt.Printf("rValueValue = %v rValueValue.Admin = %v\n", *model, (*model).Admin) //打印(*model)检查是否创建并给其字段赋值成功 53 | } 54 | -------------------------------------------------------------------------------- /进阶/反射/反射的实践1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | /** 9 | 使用反射遍历结构体的字段,调用结构体的方法,并获取结构体标签的值。 10 | */ 11 | 12 | //创建一个结构体 13 | type People struct { 14 | Name string `js:"people_name"` 15 | Age int `js:"people_int"` 16 | Address string `js:"people_address"` 17 | } 18 | 19 | //修改结构体各字段的值 20 | func (people People) Set(name string, age int, address string) People { 21 | people.Name = name 22 | people.Age = age 23 | people.Address = address 24 | return people 25 | } 26 | 27 | //打印结构体 28 | func (people People) Show() { 29 | fmt.Printf("people:%v\n", people) 30 | } 31 | 32 | func peopleStruct(people interface{}) { 33 | //获取结构体的Type 34 | rTyp := reflect.TypeOf(people) 35 | //获取结构体的Value 36 | rVal := reflect.ValueOf(people) 37 | 38 | //获取结构体的Kind 39 | vKin := rVal.Kind() 40 | //判断传入的变量是不是结构体,不是就退出。 41 | if vKin != reflect.Struct { 42 | fmt.Println("该变量不是结构体!") 43 | return 44 | } 45 | 46 | //获取结构体的字段数量 47 | numFieldStruct := rVal.NumField() 48 | fmt.Printf("该结构体变量的字段有 %v 个\n", numFieldStruct) 49 | 50 | //遍历结构体的所有字段和字段的标签 51 | for i := 0; i < numFieldStruct; i++ { 52 | //获取结构体第i个字段并打印 53 | field := rVal.Field(i) //返回结构体第i个字段的封装的Value 54 | fmt.Printf("该结构体变量第 %v 个字段值:%v\n", i+1, field) 55 | //获取结构体第i个字段的标签并打印 没有就不打印 56 | tagField := rTyp.Field(i).Tag.Get("js") //这里必须用rTyp.Field(i)获取tag,因为rTyp.Field(i)才会返回结构体的第i个字段 57 | if tagField != "" { 58 | fmt.Printf("该结构体变量第 %v 个字段的标签:%v\n", i+1, tagField) 59 | } 60 | } 61 | 62 | //获取结构体的方法数量并打印(只能获取首字母大写的方法) 63 | numMethodStruct := rVal.NumMethod() 64 | fmt.Printf("该结构体变量的方法有 %v 个\n", numMethodStruct) 65 | //调用它的第二个方法 方法排名顺序是按照方法名在ASCII码表中的顺序进行顺序 66 | rVal.Method(1).Call(nil) 67 | //调用它的Set方法,传入参数是一个切片,先创建一个切片 68 | //创建一个切片 69 | var slice []reflect.Value 70 | slice = append(slice, reflect.ValueOf("hello golang!")) 71 | slice = append(slice, reflect.ValueOf(22)) 72 | slice = append(slice, reflect.ValueOf("US")) 73 | peopleSet := rVal.MethodByName("Set").Call(slice) //调用它的Set方法,传入参数是一个切片 74 | fmt.Printf("peopleSet:%v\n", peopleSet[0]) 75 | } 76 | 77 | func main() { 78 | people := People{ 79 | Name: "golang", 80 | Age: 21, 81 | Address: "Google", 82 | } 83 | peopleStruct(people) 84 | } 85 | -------------------------------------------------------------------------------- /进阶/反射/反射的实践2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | /** 9 | 使用反射遍历结构体的字段,并修改字段。 10 | 使用reflect.Value().Elem()函数来获取指针变量所指的值 11 | */ 12 | 13 | //创建一个结构体 14 | type Monster struct { 15 | Name string `js:"monster_name"` 16 | Age int `js:"monster_int"` 17 | Address string `js:"monster_address"` 18 | } 19 | 20 | func monsterStruct(monster interface{}) { 21 | //获取结构体指针的Type 22 | rTyp := reflect.TypeOf(monster) 23 | //获取结构体指针的Value 24 | rVal := reflect.ValueOf(monster) 25 | 26 | //获取结构体指针的Kind 27 | vKin := rVal.Kind() 28 | //如果传入的变量不是结构体指针类型,或传入的变量指向的不是结构体类型,就return函数。 29 | if vKin != reflect.Ptr || rVal.Elem().Kind() != reflect.Struct { 30 | fmt.Println("该变量不是结构体指针类型!") 31 | return 32 | } 33 | 34 | //获取该结构体指针变量所指的结构体的字段数 35 | numFieldStruct := rVal.Elem().NumField() 36 | fmt.Printf("该结构体指针变量所指的结构体的字段有 %v 个\n", numFieldStruct) 37 | 38 | //遍历该结构体指针变量所指的结构体的所有字段和字段的标签 39 | for i := 0; i < numFieldStruct; i++ { 40 | //获取第i个字段并打印 41 | field := rVal.Elem().Field(i) //返回第i个字段的封装的Value 42 | fieldType := field.Kind() //获取该字段的类别 43 | fmt.Printf("该结构体变量第 %v 个字段:%v (%v)\n", i+1, field, fieldType) 44 | //获取第i个字段的标签并打印 没有就不打印 45 | tagField := rTyp.Elem().Field(i).Tag.Get("js") //这里必须用rTyp.Field(i)获取tag,因为rTyp.Field(i)才会返回结构体的第i个字段 46 | if tagField != "" { 47 | fmt.Printf("该结构体变量第 %v 个字段的标签:%v\n", i+1, tagField) 48 | } 49 | } 50 | 51 | //修改该结构体指针变量所指的结构体的第一个字段 52 | //因为第一个字段数据类型为string,所以使用SetString()函数;如果是int类型,则使用SetInt()进行修改。 53 | rVal.Elem().Field(0).SetString("hello golang!") 54 | //修改该结构体指针变量所指的结构体的"Age"字段 55 | rVal.Elem().FieldByName("Age").SetInt(18) 56 | } 57 | 58 | func main() { 59 | monster := Monster{ 60 | Name: "golang", 61 | Age: 21, 62 | Address: "Google", 63 | } 64 | fmt.Println("修改前的monster = ", monster) //打印修改前的结构体 65 | monsterStruct(&monster) //传入monster地址 66 | fmt.Println("修改后的monster = ", monster) //打印修改后的结构体,检查是否修改成功 67 | } 68 | -------------------------------------------------------------------------------- /进阶/文件操作/文件操作基础.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | func main(){ 9 | case_1() 10 | } 11 | 12 | /** 13 | 文件在程序中以流的形式进行操作 14 | 流:文件在数据源(文件)和程序(内存)之间经历的路径 15 | 输入流(读取)(input):数据从数据源到程序的路径 16 | 输出流(写入)(output):数据从程序到数据源的路径 17 | */ 18 | 19 | /** 20 | 用GO自带的os包下的Open方法打开一个文件 21 | file接收文件指针(文件对象) 22 | err接收打开文件失败信息,打开成功err为nil 23 | */ 24 | func case_1(){ 25 | //file有三种叫法:1.file对象 2.file指针 3.file文件句柄 26 | file , openErr := os.Open("A:/test.txt") 27 | if openErr != nil { 28 | fmt.Println("打开文件错误:",openErr) 29 | return 30 | } 31 | //打开成功就输出 查看文件 32 | fmt.Println("test:",file) //因为file是一个指针类型,所以输出的是一个地址(文件地址) 33 | 34 | //关闭文件Close() 35 | CloseErr := file.Close() 36 | if CloseErr != nil { 37 | fmt.Println("关闭文件错误:",CloseErr) 38 | } 39 | } -------------------------------------------------------------------------------- /进阶/文件操作/文件的写入.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func main(){ 10 | case_5() 11 | case_6() 12 | } 13 | 14 | /** 15 | 打开一个存在的文件,覆盖原内容替换为10句:“你好,GO!” 16 | */ 17 | func case_5(){ 18 | //打开已存在文件 19 | filePath := "A:/hello.txt" 20 | //file接收文件指针,err接收发生错误时的信息,未发生错误err为nil 21 | //os.O_WRONLY文件只写模式 os.O_TRUNC在打开文件同时清空文件内容 这两个属性均来自于GO自带包os 22 | //第二个参数为文件打开模式,这个参数传入什么很重要,直接关系到文件写入结果 23 | file , err := os.OpenFile(filePath,os.O_WRONLY | os.O_TRUNC,0) 24 | if err != nil { 25 | fmt.Printf("OpenFile err=%v",err) 26 | return 27 | } 28 | fmt.Println("*****文件已打开!*****") 29 | 30 | defer file.Close() //函数执行完毕时要及时的关闭文件句柄,否则会有内存泄漏 31 | defer fmt.Println("*****文件已关闭!*****") 32 | 33 | //写入文件 34 | writer := bufio.NewWriter(file) //用GO自带包bufio里的NewWriter()方法对文件进行写入操作,返回一个file文件写入流指针 35 | //循环执行10次该操作 36 | for i := 0 ; i < 10 ; i++ { 37 | writer.WriteString("你好,GO!\n") //file文件写入流指针里有一个WriteString()方法对file文件进行写入 38 | } 39 | //因为writer是带缓存的,在调用WriteString()方法时是先写入到缓存中 40 | //写入操作执行完要执行Flush()方法才会将writer缓存的内容真正写入到file文件中 41 | //注意:写入操作执行完不执行Flush()方法文件不会file更新 42 | writer.Flush() 43 | fmt.Println("文件写入成功!!!") 44 | } 45 | 46 | 47 | /** 48 | 打开一个存在的文件,在原内容的基础上追加5局“hello golang!” 49 | */ 50 | func case_6(){ 51 | //打开已存在文件 52 | filePath := "A:/hello.txt" 53 | //os.O_WRONLY文件只写模式 os.O_APPEND在打开的文件原内容基础上追加 这两个属性均来自于GO自带包os 54 | //第二个参数为文件打开模式,这个参数传入什么很重要,直接关系到文件写入结果 55 | file , err := os.OpenFile(filePath,os.O_WRONLY | os.O_APPEND,0) 56 | if err != nil { 57 | fmt.Printf("OpenFile err=%v",err) 58 | return 59 | } 60 | fmt.Println("*****文件已打开!*****") 61 | 62 | defer file.Close() //函数执行完毕时要及时的关闭文件句柄,否则会有内存泄漏 63 | defer fmt.Println("*****文件已关闭!*****") 64 | 65 | //追加写入文件 66 | writer := bufio.NewWriter(file) //用GO自带包bufio里的NewWriter()方法对文件进行写入操作,返回一个file文件写入流指针 67 | //循环执行10次该操作 68 | for i := 0 ; i < 5 ; i++ { 69 | writer.WriteString("hello golang!\n") //file文件写入流指针里有一个WriteString()方法对file文件进行写入 70 | } 71 | //因为writer是带缓存的,在调用WriteString()方法时是先写入到缓存中 72 | //写入操作执行完要执行Flush()方法才会将writer缓存的内容真正写入到file文件中 73 | //注意:写入操作执行完不执行Flush()方法文件不会file更新 74 | writer.Flush() 75 | fmt.Println("文件追加写入成功!!!") 76 | } -------------------------------------------------------------------------------- /进阶/文件操作/文件的创建和写入.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func main(){ 10 | case_4() 11 | } 12 | 13 | /** 14 | 创建一个文件并写入5句hello golang! 15 | */ 16 | func case_4(){ 17 | //打开或创建文件 18 | filePath := "A:/hello.txt" 19 | //file接收文件指针,err接收发生错误时的信息,未发生错误err为nil 20 | //os.O_WRONLY文件只写模式 os.O_CREATE文件不存在就创建 这两个属性均来自于GO自带包os 21 | //第二个参数为文件打开模式,这个参数传入什么很重要,直接关系到文件写入结果 22 | file , err := os.OpenFile(filePath,os.O_WRONLY | os.O_CREATE,0) 23 | if err != nil { 24 | fmt.Printf("OpenFile err=%v",err) 25 | return 26 | } 27 | fmt.Println("*****文件已打开!*****") 28 | 29 | defer file.Close() //函数执行完毕时要及时的关闭文件句柄,否则会有内存泄漏 30 | defer fmt.Println("*****文件已关闭!*****") 31 | 32 | //写入文件 33 | writer := bufio.NewWriter(file) //用GO自带包bufio里的NewWriter()方法对文件进行写入操作,返回一个file文件写入流指针 34 | //循环执行5次该操作 35 | for i := 0 ; i < 5 ; i++ { 36 | writer.WriteString("hello golang!\n") //file文件写入流指针里有一个WriteString()方法对file文件进行写入 37 | } 38 | //因为writer是带缓存的,在调用WriteString()方法时是先写入到缓存中 39 | //写入操作执行完要执行Flush()方法才会将writer缓存的内容真正写入到file文件中 40 | //注意:写入操作执行完不执行Flush()方法文件不会file更新 41 | writer.Flush() 42 | fmt.Println("文件写入成功!!!") 43 | } -------------------------------------------------------------------------------- /进阶/文件操作/文件的拷贝.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "os" 9 | ) 10 | 11 | func main(){ 12 | case_9() 13 | case_10() 14 | } 15 | 16 | /** 17 | 文本文件的拷贝 18 | */ 19 | func case_9(){ 20 | 21 | readFilePath := "A:/hello.txt" //要复制的文件路径 22 | writeFilePath := "C:/hello.txt" //目标文件路径 23 | 24 | //先读取要拷贝的文件 25 | //file的Open和Close已经被封装到ioutil.ReadFile函数内,所以我们不用对file进行打开和关闭操作,直接读取即可。 26 | data , readErr:= ioutil.ReadFile(readFilePath) 27 | if readErr != nil { 28 | fmt.Printf("文件读取错误:%v",readErr) 29 | return 30 | } 31 | 32 | //将读取到的内容写入到目标文件即完成了文件的拷贝 33 | //file的Open和Close已经被封装到ioutil.WriteFile()函数内,所以我们不用对file进行打开和关闭操作,直接读取即可。 34 | writeErr := ioutil.WriteFile(writeFilePath,data,0) 35 | if writeErr != nil { 36 | fmt.Printf("文件写入失败:%v",writeErr) 37 | return 38 | } 39 | 40 | fmt.Println("文件拷贝成功!!!") 41 | } 42 | 43 | 44 | /** 45 | 非文本文件的拷贝(视频、图片、音乐) 46 | 此方法适用于所有视频、图片、音乐文件的拷贝,因为他们都是二进制文件 47 | */ 48 | func case_10(){ 49 | srcFilePath := "A:/mv.mp4" 50 | dstFilePath := "C:/视频.mp4" 51 | written , err := CopyFile(srcFilePath,dstFilePath) 52 | if err != nil || written == 0 { 53 | fmt.Printf("文件拷贝失败 err=%v",err) 54 | }else{ 55 | fmt.Printf("总拷贝大小:%v 字节\n文件拷贝成功!!!",written) 56 | fmt.Print("\n***********拷贝文件结束***********") 57 | } 58 | } 59 | func CopyFile(srcFilePath string , dstFilePath string) (written int64 , err error) { 60 | fmt.Println("***********拷贝文件开始***********") 61 | 62 | //打开源文件获取文件读取流指针 63 | srcFileInfo , openErr := os.Open(srcFilePath) 64 | if openErr != nil { 65 | fmt.Printf("源文件打开失败 openErr=%v",openErr) 66 | return 67 | } 68 | fmt.Println("源文件已打开。。。") 69 | defer srcFileInfo.Close() //函数执行完要及时关闭源文件 70 | defer fmt.Println("源文件已关闭。。。") 71 | reader := bufio.NewReader(srcFileInfo) //文件读取流指针 72 | 73 | //打开目标文件获取文件写入流指针 74 | datFileInfo , openFileErr := os.OpenFile(dstFilePath , os.O_WRONLY | os.O_CREATE,0 ) 75 | if openFileErr != nil { 76 | fmt.Printf("目标文件打开失败 openFileErr=%v",openErr) 77 | } 78 | fmt.Println("目标文件已打开。。。") 79 | defer srcFileInfo.Close() //函数执行完要及时关闭目标文件 80 | defer fmt.Println("目标文件已关闭。。。") 81 | writer := bufio.NewWriter(datFileInfo)//文件写入流指针 82 | 83 | //用GO自带包io的Copy()方法进行文件的拷贝,将获取到的 读取流指针 和 写入流指针 传给他即可 84 | return io.Copy(writer,reader) 85 | } -------------------------------------------------------------------------------- /进阶/文件操作/文件的读取.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "os" 9 | ) 10 | 11 | func main(){ 12 | case_2() 13 | case_3() 14 | } 15 | 16 | /** 17 | 带缓冲区的文件读取 18 | 缓冲区的作用是:在读取文件时不是一次性将全部文件读取到内存,而是读取一部分处理一部分,这样就可以处理一些大的文件。 19 | 缓冲区默认大小为4096 20 | */ 21 | func case_2(){ 22 | //file有三种叫法:1.file对象 2.file指针 3.file文件句柄 23 | file , openErr := os.Open("A:/test.txt") 24 | if openErr != nil { 25 | fmt.Println("打开文件错误:",openErr) 26 | return 27 | } 28 | fmt.Println("*****文件已打开!*****") 29 | 30 | //defer语句后面的代码会在函数执行完毕时最后执行 31 | defer file.Close() //函数执行完毕时要及时的关闭文件句柄,否则会有内存泄漏 32 | defer fmt.Print("*****文件已关闭!*****") 33 | 34 | //开始读取文件 35 | //reader的默认缓冲区大小为4096 36 | reader := bufio.NewReader(file)//用GO自带包bufio里面的NewReader()方法读取文件,返回一个读取流指针 37 | //此时reader读取流已经和file文件绑定,通过reader对file进行读取操作 38 | for { 39 | //reader读取流指针里有一个ReadString()方法可以读取file文件的内容 40 | //str为读取到的内容,读取发生错误的信息会返回给err,读取成功时err为nil 41 | str , err := reader.ReadString('\n') //读到一个换行就结束 42 | fmt.Print(str) 43 | if err != nil { 44 | fmt.Print("\n") 45 | if err == io.EOF { 46 | fmt.Printf("文件读取到末尾:%v\n",err) 47 | }else{ 48 | fmt.Printf("文件读取发生错误:%v\n",err) 49 | } 50 | break 51 | } 52 | } 53 | } 54 | 55 | 56 | /** 57 | 一次性读取文件:一次将整个文件读取到内存中(不带缓冲区) 58 | 使用GO自带包ioutil里的ReadFile()函数进行一次性读取文件 59 | 此方法适用于文件不大的情况,文件太大效率会很低!!! 60 | */ 61 | func case_3(){ 62 | //content接收读取到的文件内容,读取发生错误的信息会返回给err,读取成功时err为nil 63 | file := "A:/test.txt" 64 | //file的Open和Close已经被封装到ioutil.ReadFile函数内,所以我们不用对file进行打开和关闭操作,直接读取即可。 65 | content , err := ioutil.ReadFile(file) 66 | if err != nil { 67 | fmt.Printf("文件读取发生错误:%v\n",err) 68 | return 69 | } 70 | //读取的内容是一个byte切片,切片元素为file内容的十进制表示(UTF-8表) 71 | //查看file原内容要将十进制表示转换为对应字符表示(UTF-8表) 72 | fmt.Println(content) //十进制切片表示 73 | fmt.Printf(string(content)) //字符表示 74 | } -------------------------------------------------------------------------------- /进阶/文件操作/文件的读取和写入.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "os" 8 | ) 9 | 10 | func main(){ 11 | case_7() 12 | } 13 | 14 | /** 15 | 先读取已存在文件的内容并打印在控制台,再给文件追加内容。 16 | */ 17 | func case_7(){ 18 | //打开已存在文件 19 | filePath := "A:/hello.txt" 20 | //os.O_RDWR文件读取并写入模式 os.O_APPEND在打开的文件原内容基础上追加 这两个属性均来自于GO自带包os 21 | //第二个参数为文件打开模式,这个参数传入什么很重要,直接关系到文件写入结果 22 | file , openErr := os.OpenFile(filePath,os.O_RDWR | os.O_APPEND,0) 23 | if openErr != nil { 24 | fmt.Printf("OpenFile err=%v",openErr) 25 | return 26 | } 27 | fmt.Println("*****文件已打开!*****") 28 | defer file.Close() //函数执行完毕时要及时的关闭文件句柄,否则会有内存泄漏 29 | defer fmt.Println("*****文件已关闭!*****") 30 | 31 | //读取文件内容并打印 32 | reader := bufio.NewReader(file)//获取读取流指针 33 | for { 34 | str , readerErr := reader.ReadString('\n')//读到一个换行就结束 35 | fmt.Print(str) 36 | if readerErr != nil { 37 | if readerErr == io.EOF { 38 | fmt.Printf("文件读取到末尾:%v\n",readerErr) 39 | }else{ 40 | fmt.Printf("文件读取发生错误:%v\n",readerErr) 41 | } 42 | break 43 | } 44 | } 45 | 46 | 47 | //追加写入文件 48 | writer := bufio.NewWriter(file) //用GO自带包bufio里的NewWriter()方法对文件进行写入操作,返回一个file文件写入流指针 49 | //循环执行3次该操作 50 | for i := 0 ; i < 3 ; i++ { 51 | writer.WriteString("ABCD\n") //file文件写入流指针里有一个WriteString()方法对file文件进行写入 52 | } 53 | //因为writer是带缓存的,在调用WriteString()方法时是先写入到缓存中 54 | //写入操作执行完要执行Flush()方法才会将writer缓存的内容真正写入到file文件中 55 | //注意:写入操作执行完不执行Flush()方法文件不会file更新 56 | writer.Flush() 57 | fmt.Println("文件追加写入成功!!!") 58 | } 59 | -------------------------------------------------------------------------------- /进阶/文件操作/文件目录是否存在.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | func main(){ 9 | case_8() 10 | } 11 | 12 | /** 13 | 判断文件或目录是否存在 14 | */ 15 | func case_8(){ 16 | file := "A:/test.txt" 17 | fileInfo , err := os.Stat(file) 18 | if os.IsNotExist(err) { 19 | //用GO自带包os里的函数IsNotExist()判断err为true的话说明文件不存在 20 | //文件不存在的情况下:fileInfo为nil err不为nil os.IsNotExist(err)为true 21 | fmt.Printf("文件:%v 不存在:%v",fileInfo,err) 22 | return 23 | } 24 | if err == nil { 25 | //如果err为nil说明文件存在 26 | //文件存在的情况下:fileInfo不为nil err为nil os.IsNotExist(err)为false 27 | fmt.Printf("文件:%v 存在:%v",fileInfo,err) 28 | return 29 | } 30 | //以上无法判断就告诉用户无法确定文件是否存在 31 | //文件不确定是否存在的情况下:fileInfo为nil err不为nil os.IsNotExist(err)为false 32 | fmt.Printf("文件:%v 不确定是否存在:%v",fileInfo,err) 33 | } -------------------------------------------------------------------------------- /进阶/文件操作/统计文件字符个数.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "os" 8 | ) 9 | 10 | func main(){ 11 | case_11() 12 | } 13 | 14 | //字符个数统计结构体 15 | type count struct { 16 | charCount int //字母个数 17 | numCount int //数字个数 18 | spaceCount int //空格个数 19 | otherCount int //其他个数 20 | } 21 | 22 | func case_11(){ 23 | var count count //创建结构体 24 | filePath := "A:/test.txt" 25 | file , openErr := os.Open(filePath) 26 | if openErr != nil { 27 | fmt.Printf("文件打开失败 openErr=%v",openErr) 28 | } 29 | defer file.Close() //及时关闭文件 30 | 31 | reader := bufio.NewReader(file) //获取读取流指针 32 | 33 | for { 34 | str , readErr := reader.ReadString('\n') 35 | if readErr != nil { 36 | if readErr == io.EOF { 37 | fmt.Printf("文件读取到末尾:%v\n",readErr) 38 | }else{ 39 | fmt.Printf("文件读取发生错误:%v\n",readErr) 40 | } 41 | break 42 | } 43 | for _ , v := range str { 44 | fmt.Print(v," ") 45 | switch { 46 | case v >= 'a' && v <= 'z': 47 | fallthrough 48 | case v >= 'A' && v <= 'Z': 49 | count.charCount++ 50 | case v >= '0' && v <= '9' : 51 | count.numCount++ 52 | case v == ' ' || v == '\t': 53 | count.spaceCount++ 54 | default : count.otherCount++ 55 | } 56 | } 57 | } 58 | //统计结果不兼容中文,如出现中文则会忽略 59 | fmt.Println("统计结束,统计结果如下:") 60 | fmt.Printf("字母个数:%v\n数字个数:%v\n空格个数:%v\n其他字符个数:%v",count.charCount,count.numCount,count.spaceCount,count.otherCount) 61 | } 62 | -------------------------------------------------------------------------------- /进阶/泛型/base.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // 泛型:相同逻辑的代码处理不同类型数据的能力 4 | -------------------------------------------------------------------------------- /进阶/泛型/four.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // 自定义泛型 6 | type MyFanXing interface { 7 | int | int8 | ~int64 | float64 | string 8 | } 9 | 10 | // F的约束是MyFanXing MyFanXing是自定义泛型 11 | func GetMaxNum[F MyFanXing](a, b F) F { 12 | if a > b { 13 | return a 14 | } 15 | return b 16 | } 17 | 18 | func main() { 19 | var n1 int = 188 20 | var n2 int = 12 21 | fmt.Println(GetMaxNum(n1, n2)) 22 | 23 | // 使用自定义类型时需在自定义泛型中找到其基础类型 并在前面加“~” 24 | // 下面myInt类型的基础类型是int64 所以要在MyFanXing中的int64前面加“~” 这样传n3,n4才不会报错 25 | type myInt int64 26 | var n3 myInt = 15 27 | var n4 myInt = 111 28 | fmt.Println(GetMaxNum(n3, n4)) 29 | } 30 | -------------------------------------------------------------------------------- /进阶/泛型/one.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // 声明泛型变量 6 | 7 | func main() { 8 | // 声明一个F类型切片 9 | // F约束int、string类型 10 | type Slice[F int | string] []F 11 | var a Slice[int] = []int{11, 12, 13} 12 | var b Slice[string] = []string{"11", "22", "33"} 13 | fmt.Printf("a=%v 类型:%T\nb=%v 类型:%T\n", a, a, b, b) 14 | 15 | // 声明一个key为F1类型value为F2类型的map 16 | // F1约束int、string类型 F2约束float64、string类型 17 | type MyMap[F1 int | string, F2 float64 | string] map[F1]F2 18 | var c MyMap[int, float64] = map[int]float64{10: 10.06, 11: 12.3} 19 | fmt.Printf("c=%v 类型:%T\n", c, c) 20 | 21 | // 声明一个ID字段为F类型的结构体 22 | type MyStruct[F int | string] struct { 23 | ID F 24 | Name string 25 | } 26 | d := MyStruct[string]{ 27 | ID: "001", 28 | Name: "golang", 29 | } 30 | fmt.Printf("d=%v 类型:%T\n", d, d) 31 | } 32 | -------------------------------------------------------------------------------- /进阶/泛型/three.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // 泛型方法 6 | type MySlice[F string | float64] []F 7 | 8 | func (m MySlice[F]) Add() F { 9 | var sum F 10 | for _, v := range m { 11 | sum += v 12 | } 13 | return sum 14 | } 15 | 16 | func main() { 17 | var aa MySlice[string] = []string{"aaa", "bbb", "ccc"} 18 | fmt.Println(aa.Add()) 19 | var bb MySlice[float64] = []float64{11.1, 11.2, 11.3} 20 | fmt.Printf("%.2f\n", bb.Add()) 21 | } 22 | -------------------------------------------------------------------------------- /进阶/泛型/two.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // 约束类型F的类型可以为int、string、float64 6 | func PrintfMyVar[F int | string | float64](va []F) { 7 | // 此时形参va可接收以上约束的三种类型 8 | fmt.Println(va) 9 | } 10 | 11 | func main() { 12 | str := []string{"aa", "bb", "cc"} 13 | num := []int{33, 44, 55} 14 | fl := []float64{33.1, 33.2, 33.4} 15 | PrintfMyVar(str) 16 | PrintfMyVar(num) 17 | PrintfMyVar(fl) 18 | } 19 | -------------------------------------------------------------------------------- /进阶/网络编程/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "net" 7 | "os" 8 | "strings" 9 | ) 10 | 11 | /* 12 | * 13 | 客户端 14 | */ 15 | func main() { 16 | //连接服务端 17 | clientConn, clientConnErr := net.Dial("tcp", "192.168.31.95:8888") 18 | if clientConnErr != nil { 19 | fmt.Printf("clientConnErr=%v", clientConnErr) 20 | return 21 | } 22 | fmt.Printf("连接成功:%v\n", clientConn) 23 | 24 | //函数退出后关闭clientConn 25 | defer func() { 26 | clientCloseErr := clientConn.Close() //关闭clientConn 27 | if clientCloseErr != nil { 28 | fmt.Printf("clientCloseErr=%v\n", clientCloseErr) 29 | return 30 | } 31 | fmt.Printf("clientConn已关闭。。。") 32 | }() 33 | 34 | //客户端发送消息 35 | //获取终端输入的内容的指针 36 | reader := bufio.NewReader(os.Stdin) //os.Stdin读取终端输入的内容 os.Stdin本质上是一个文件,所以会使用bufio.NewReader() 37 | 38 | //循环向服务器发送消息 39 | for { 40 | //从指针里读取 客户端发送消息的文件 内容 41 | con, conErr := reader.ReadString('\n') //读取到'\n'字符结束 42 | if conErr != nil { 43 | fmt.Printf("conErr=%v", conErr) 44 | return 45 | } 46 | 47 | conEnd := strings.Trim(con, " \n\r") //去掉con字符串里空格换行并返回给conEnd 48 | 49 | //如果客户端输入“exit”就退出程序 50 | if conEnd == "exit" { 51 | fmt.Println("程序已退出。。。") 52 | return 53 | } 54 | 55 | //将读取到的内容发送给服务端 56 | clientWriteN, clientWriteNErr := clientConn.Write([]byte(conEnd)) //将string类型的con转为socket信息管道规定的[]byte类型 57 | if clientWriteNErr != nil { 58 | fmt.Printf("nErr=%v", clientWriteNErr) 59 | return 60 | } 61 | fmt.Printf("已发送[%v字节]\n", clientWriteN) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /进阶/网络编程/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | ) 7 | 8 | /* 9 | * 10 | 服务器端 11 | */ 12 | func main() { 13 | fmt.Println("服务器开始监听。。。") 14 | 15 | listen, listenErr := net.Listen("tcp", "0.0.0.0:8888") 16 | if listenErr != nil { 17 | fmt.Printf("listenErr=%v\n", listenErr) 18 | return 19 | } 20 | 21 | //函数退出后关闭listen 22 | defer func() { 23 | CloseErr := listen.Close() //关闭listen 24 | if CloseErr != nil { 25 | fmt.Printf("listen CloseErr=%v\n", CloseErr) 26 | return 27 | } 28 | fmt.Println("listen已关闭。。。") 29 | }() 30 | 31 | for { 32 | serverConn, serverConnErr := listen.Accept() 33 | if serverConnErr != nil { 34 | fmt.Printf("connErr=%v\n", serverConnErr) 35 | } else { 36 | ip := serverConn.RemoteAddr().String() 37 | fmt.Printf("服务器有新的连接:%v IP地址:%v\n", serverConn, ip) 38 | go process(serverConn, ip) 39 | } 40 | } 41 | 42 | } 43 | 44 | func process(serverConn net.Conn, ip string) { 45 | //函数退出后关闭serverConn 46 | defer func() { 47 | CloseErr := serverConn.Close() //关闭serverConn 48 | if CloseErr != nil { 49 | fmt.Printf("serverConn CloseErr=%v\n", CloseErr) 50 | return 51 | } 52 | fmt.Printf("{%v}serverConn已关闭。。。", ip) 53 | }() 54 | //循环读取消息 55 | for { 56 | buf := make([]byte, 1024) //创建一个切片接收读取的消息 57 | serverReadN, serverReadNErr := serverConn.Read(buf) //返回接收的数据大小 和 错误信息 58 | if serverReadNErr != nil { 59 | fmt.Printf("%v客户端断开连接 serverReadErr=%v\n", ip, serverReadNErr) 60 | return 61 | } 62 | //buf存储接收到的内容,buf是byte切片类型所以要强转为string类型 63 | //接收的数据大小是serverReadN,所以打印buf[0-serverReadN]的内容 64 | //buf默认大小是1024,如果不设置打印buf[0-serverReadN]的内容,就会将buf的[serverReadN-1024]多余内容打印出来 65 | fmt.Printf("客户端{%v}:%v\n", ip, string(buf[:serverReadN])) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /进阶/网络编程/网络编程介绍.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /** 4 | 网络编程主要有两种: 5 | 1.TCP socket编程:是网络编程的主流,底层是TCP/IP协议。 比如QQ聊天 6 | 2.b/s结构的http编程:我们使用浏览器访问服务器时,使用的就是http协议,底层是tcp socket实现的。比如京东商城(go web开发) 7 | 8 | IP: 9 | 1.每个internet设备都有IP地址,它包括网络号和主机号,ip地址有ipv4(32位)和ipv6(128位),使用ipconfig来查看 10 | 2."tracert 地址"指令可以查看访问 这个地址 的过程。(经过的公网路由器以及数据收发时间) 11 | 12 | 端口: 13 | 1.一个IP有65535个端口(1-65535) 14 | 2.只要是做服务的程序,都必须监听一个端口,该端口就是其他程序和该服务程序通讯的通道。 15 | 3.一旦一个端口被某个程序监听(占用),那么这个端口就不能再被其他程序监听。 16 | 4.0号端口是保留端口,1-1024是固定端口,又叫有名端口,即被某些程序固定使用,一般程序员不使用 17 | 5.1-1024端口分类:21:ftp使用 22:SSH远程登录协议 23:telnte使用 25:smtp服务使用 80:iis使用 7:echo服务 18 | 6.1025-65535是动态端口,一般程序员可以使用。 19 | 端口注意事项: 20 | 1.计算机,尤其是服务器的端口尽量少开。 21 | 2.一个端口只能被一个程序监听。 22 | 3.netstat-an 查看本机有哪些端口在监听。 23 | 4.netstat-anb 查看监听端口的pid,再结合任务管理器关闭不安全的端口。 24 | */ 25 | --------------------------------------------------------------------------------