《Go语言实战》Go 类型:基本类型、引用类型、结构类型、自定义类型
Go 语言是一种静态类型的编程语言,所以在编译器进行编译的时候,就要知道每个值的类型,这样编译器就知道要为这个值分配多少内存,并且知道这段分配的内存表示什么。
提前知道值的类型的好处有很多,比如编译器可以合理的使用这些值,可以进一步优化代码,提高执行的效率,减少 bug 等等。
基本类型
基本类型是 Go 语言自带的类型,比如 数值、浮点、字符串、布尔、数组 及 错误 类型,他们本质上是原始类型,也就是不可改变的,所以对他们进行操作,一般都会返回一个新创建的值,所以把这些值传递给函数时,其实传递的是一个值的副本。
funcmain() {
name := "张三"
fmt.Println(modify(name))
fmt.Println(name)
}
funcmodify(sstring)string{
s = s + s
return s
}
张三张三
张三
以上是一个操作字符串的例子,通过打印的结果,可以看到,本来 name 的值并没有被改变,也就是说,我们传递的时一个副本,并且返回一个新创建的字符串。
基本类型因为是拷贝的值,并且在对他进行操作的时候,生成的也是新创建的值,所以这些类型在多线程里是安全的,我们不用担心一个线程的修改影响了另外一个线程的数据。
引用类型
引用类型和原始的基本类型恰恰相反,它的修改可以影响到任何引用到它的变量。在 Go 语言中,引用类型有 切片(slice)、字典(map)、接口(interface)、函数(func) 以及 通道(chan) 。
引用类型之所以可以引用,是因为我们创建引用类型的变量,其实是一个标头值,标头值里包含一个指针,指向底层的数据结构,当我们在函数中传递引用类型时,其实传递的是这个标头值的副本,它所指向的底层结构并没有被复制传递,这也是引用类型传递高效的原因。
本质上,我们可以理解函数的传递都是值传递,只不过引用类型传递的是一个指向底层数据的指针,所以我们在操作的时候,可以修改共享的底层数据的值,进而影响到所有引用到这个共享底层数据的变量。
funcmain() {
ages := map[string]int{"张三": 20}
fmt.Println(ages)
modify(ages)
fmt.Println(ages)
}
funcmodify(mmap[string]int) {
m["张三"] = 10
}
这是一个很明显的修改引用类型的例子,函数 modify 的修改,会影响到原来变量 ages 的值。
结构类型
结构类型是用来描述一组值的,比如一个人有身高、体重、名字和年龄等,本质上是一种聚合型的数据类型。
type person struct {
age int
name string
}
要定义一个结构体的类型,通过 type 关键字和类型 struct 进行声明,以上我们就定义了一个结构体类型 person ,它有 age , name 这两个字段数据。
结构体类型定义好之后,就可以进行使用了,我们可以用过 var 关键字声明一个结构体类型的变量。
var p person
这种声明的方式,会对结构体 person 里的数据类型默认初始化,也就是使用它们类型的零值,如果要创建一个结构体变量并初始化其为零值时,这种 var 方式最常用。
如果我们需要指定非零值,就可以使用我们字面量方式了。
jim := person{10, "Jim"}
示例这种我们就为其指定了值,注意这个值的顺序很重要,必须和结构体里声明字段的顺序一致,当然我们也可以不按顺序,但是这时候我们必须为字段指定值。
jim := person{name: "Jim", age: 10}
使用冒号 : 分开字段名和字段值即可,这样我们就不用严格的按照定义的顺序了。
除了基本的原始类型外,结构体内的值也可以是引用类型,或者自己定义的其他类型。具体选择类型,要根据实际情况,比如是否允许修改值本身,如果允许的话,可以选择引用类型,如果不允许的话,则需要使用基本类型。
函数传参是值传递,所以对于结构体来说也不例外,结构体传递的是其本身以及里面的值的拷贝。
funcmain() {
jim := person{10, "Jim"}
fmt.Println(jim)
modify(jim)
fmt.Println(jim)
}
funcmodify(p person) {
p.age = p.age + 10
}
type person struct {
age int
name string
}
以上示例的输出是一样的,所以我们可以验证传递的是值的副本。如果上面的例子我们要修改 age 的值可以通过传递结构体的指针,我们稍微改动下例子
funcmain() {
jim := person{10, "Jim"}
fmt.Println(jim)
modify(&jim)
fmt.Println(jim)
}
funcmodify(p *person) {
p.age = p.age + 10
}
type person struct {
age int
name string
}
这个例子的输出是
{10 Jim}
{20 Jim}
非常明显的, age 的值已经被改变。如果结构体里有引用类型的值,比如 map ,那么我们即使传递的是结构体的值副本,如果修改这个 map 的话,原结构的对应的 map 值也会被修改,这里不再写例子,大家可以验证下。
自定义类型
Go 语言支持我们自定义类型,比如刚刚上面的结构体类型,就是我们自定义的类型,这也是比较常用的自定义类型的方法。
另外一个自定义类型的方法是基于一个已有的类型,就是基于一个现有的类型创造新的类型,这种也是使用 type 关键字。
type Duration int64
我们在使用 time 这个包的时候,对于类型 time.Duration 应该非常熟悉,它其实就是基于 int64 这个基本类型创建的新类型,来表示时间的间隔。
但是这里我们注意,虽然 Duration 是基于 int64 创建,觉得他们其实一样,比如都可以使用数字赋值。
type Duration int64 var i Duration = 100
var j int64 = 100
但是本质上,他们并不是同一种类型,所以对于Go这种强类型语言,他们是不能相互赋值的。
type Duration int64 var dur Duration
dur = int64(100)
fmt.Println(dur)
上面的例子,在编译的时候,会报类型转换的异常错误。
cannot use int64(100) (type int64) as type Duration in assignment
Go 的编译器不会像 Java 的那样,帮我们做隐式的类型转换。
有时候,大家会迷茫,已经有了 int64 这些类型了,可以表示,还要基于他们创建新的类型做什么?其实这就是 Go 灵活的地方,我们可以使用自定义的类型做很多事情,比如添加方法,比如可以更明确的表示业务的含义等等。
《Go语言实战》Go 类型:基本类型、引用类型、结构类型、自定义类型的更多相关文章
- 阶段1 语言基础+高级_1-3-Java语言高级_04-集合_08 Map集合_7_HashMap存储自定义类型键值
自定义类型做key值.必须要重写hashCode和equals方法 创建pserson类 有name个age两个成员变量.重写toString方法 key有重复,会被新的value值替换掉. key值 ...
- 阶段1 语言基础+高级_1-3-Java语言高级_04-集合_06 Set集合_5_HashSet存储自定义类型元素
想存储的元素不重复,就必须重写hashCode和equals这两个方法 新建一个Person类.添加姓名和年龄这两个成员变量..get和set,有参和无参构造. 重点是重写了toString的方法 自 ...
- MyBatis-xml配置SQL文件中,传入List数组、基本类型String、int……、与自定义类型的方法
//基本类型 @Override public String queryItemNumber(String packId) throws Exception { // TODO Auto-genera ...
- JS类型判定方法(不包括自定义类型)
//判定数据类型 function isType(obj, type) { return toString.call(obj).indexOf('[object ' + type) == 0; } / ...
- javascript面向对象--自定义类型
Javascript是基于原型实现面向对象的,因此并没有类和接口,它的对象也与其他基于类的语言中的对象有所不同.在Javascript中,每个对象都是基于一个引用类型创建的,这个引用类型可以是原生类型 ...
- struts2自定义类型转换器
首先,何为struts2的类型转换器? 类型转换器的作用是将请求中的字符串或字符串数组参数与action中的对象进行相互转换. 一.大部分时候,使用struts2提供的类型转换器以及OGNL类型转换机 ...
- map以自定义类型当Key
关于map的定义: template < class Key, class T, class Compare = less<Key>, class Allocator = alloc ...
- Golang之方法(自定义类型,struct)
方法的使用,请看本天师的代码 //Golang的方法定义 //Golang中的方法是作用在特定类型的变量上,因此自定义类型,都可以有方法,不仅仅是struct //定义:func (recevier ...
- JavaScript--我发现,原来你是这样的JS(再说引用类型,基本包装类型与个体内置对象)
一.介绍 本篇是续上一篇的,引用类型的后篇,本篇主要是说基本包装类型和个体内置对象.如果你能收获一些知识,那我很高兴,很满足,哈哈哈,希望大家能愉快看完.如果你想学好一门技术,要不忘初心,方得始终. ...
随机推荐
- CF989C A Mist of Florescence (构造)
CF989C A Mist of Florescence solution: 作为一道构造题,这题确实十分符合构造的一些通性----(我们需要找到一些规律,然后无脑循环).个人认为这题规律很巧妙也很典 ...
- python 获取二进制文件
import requests response = requests.get('https://www.baidu.com/aladdin/img/tools/ip.png')with open(' ...
- Number of Islands I & II
Given a boolean 2D matrix, find the number of islands. Notice 0 is represented as the sea, 1 is repr ...
- django Rest Framework---缓存通过drf-extensions扩展来实现
什么情况下使用缓存 1.不经常更新的数据 2.用户经常访问的一些页面,比如商品列表页.商品详情页等 3.用户经常修改的一些操作:购物车.订单中心等 关于DRF缓存扩展可以参考文档:http://chi ...
- Java中static关键字概述
例如一个学生类中,我们需要统计下学生类中学生对象的数量,此时数量要定义为静态变量: 示例代码: package com.java1995; public class Student { int id= ...
- 为你的 Hadoop 集群选择合适的硬件
随着Apache Hadoop的起步,云客户的增多面临的首要问题就是如何为他们新的的Hadoop集群选择合适的硬件. 尽管Hadoop被设计为运行在行业标准的硬件上,提出一个理想的集群配置不想提供硬件 ...
- selenium 操作文本框(textarea输入)
selenium 对文本框的输入操作一般有两种形式,传统的是直接通过定位元素通过sendKeys()方法直接在文本框中输入信息.但有时候我们可以通过id 的方式将其进行定位,但却不能通过sendKey ...
- Java第三阶段学习(十四、JSP动态页面、EL表达式、JSTL标签库)
一.JSP技术 1.jsp脚本和注释 jap脚本: 1)<%java代码%> ----- 内部的java代码翻译到service方法的内部,比如写在doget.dopost 内的代码 2) ...
- 使用libvirt管理KVM(一)
一. 安装和配置libvirt,源码下载http://www.qemu-project.org/download/#source. 二. 从包和源码包进行安装libvirt. 1. 在ubuntu系统 ...
- ubuntu下安装python3及常用爬虫库命令
爬虫常用库安装: