Java与Go:指针

在计算机内存中,每个变量都有一个唯一的地址,指针就是用来保存这个地址的变量。通过指针,我们可以间接地访问和修改存储在该地址处的数据。今天我们来聊一聊Java和Go指针,预告一下,我们需要借助C语言做一些小小的比较。

Java

在Java中,不存在直接的指针概念,而是通过引用来访问对象。Java中的对象是通过引用来操作的,这些引用本质上是对对象的引用,而不是指向内存地址的指针。Java的引用是一种高级抽象,它隐藏了底层内存管理的细节,开发者不需要关心对象的内存分配和释放。Java中的引用可以被认为是一种安全的指针,它提供了更高的抽象级别,可以减少内存管理错误的发生。其实大家第一门编程语言应该就是大一学习的C语言编程吧。当时学习指针的时候很懵,尤其是各种指针运算。后来学Java的对象以及引用再回来看就很好理解。

CStudent *student = (Student*)malloc(sizeof(Student));JavaStudent student = new Student();

他们的本质是一样的,都是指向堆空间的某一块地址。之后再回去写C语言,就越写越顺,每次创建变量之前都问自己一句,在栈里还是堆里? 然后决定要不要用指针。

话说回来,sun.misc.Unsafe类提供了一种机制来进行一些底层的、不安全的操作,包括直接操作内存和执行不受限制的任意指针算术运算等。

sun.misc.Unsafe类并不是 Java 标准 API 的一部分,因此它并不受到 Java 平台的正式支持,并且可能在未来的 Java 版本中被移除或更改。它主要被用来实现 Java 核心类库和一些 Java 虚拟机的实现。

通过sun.misc.Unsafe类,你可以直接进行一些底层的内存操作,比如:

  • 分配内存
  • 释放内存
  • 修改内存中的值
  • 进行指针算术运算等
    使用sun.misc.Unsafe类需要谨慎,因为它涉及到底层的内存操作,可能会导致不稳定性和不可预测的结果。此外,由于它不是 Java 标准 API 的一部分,因此在不同的 Java 实现中,它的行为可能会有所不同。

在 Java 9 中,sun.misc.Unsafe类的一些方法被标记为不安全,并且在一些场景下会抛出 java.lang.UnsupportedOperationException 异常,这是为了增强 Java 应用程序的安全性。

总的来说,除非你对 Java 的内存模型和底层运行机制非常了解,并且对使用 sun.misc.Unsafe 类的风险有所认识,并且确实需要进行底层的内存操作,否则不建议使用 sun.misc.Unsafe 类。Java部分就到此结束,毕竟不是重点。

Go 中的指针

Go语言支持指针,但是和C语言中的指针还是有些不同。在Go中,指针是一种数据类型,它指向了一个内存地址,允许你直接访问内存中的数据。与C语言不同的是,Go语言的指针是类型安全的,不允许进行指针运算,从而减少了一些常见的指针错误。

在Go中,你可以通过使用操作符来声明指针变量,使用&操作符来获取变量的地址,使用操作符来获取指针指向的值。

依稀记得当时学习C语言的时候老师引出的指针的第一个例子:将两个int数传入一个方法,希望这两个int互换数值。这里我用Go来写:

package mainimport "fmt"// 定义一个交换函数
func swap(x, y int) {var i int = xx = yy = i
}func main() {// 定义两个整数变量a, b := 5, 10fmt.Println("Before swapping:")fmt.Println("a =", a)fmt.Println("b =", b)// 调用交换函数,并接收返回值swap(a, b)fmt.Println("After swapping:")fmt.Println("a =", a)fmt.Println("b =", b)
}

结果显而易见,我们失败了。然后老师开始长篇大论的解释,然后我就睡着了。其实现在回来再想想很简单,x和y的变量在main的栈帧里面,调用swap方法无非就是创建新的栈帧,并把xy的数值复制一边传递出去。抓捕周树人和我鲁迅有什么关系?

那正确写法就是搞一个指向栈内存的指针。

package mainimport "fmt"// 定义一个交换函数
func swap(x, y *int) {temp := *x*x = *y*y = temp
}func main() {// 定义两个整数变量a, b := 5, 10fmt.Println("Before swapping:")fmt.Println("a =", a)fmt.Println("b =", b)// 调用交换函数swap(&a, &b)fmt.Println("After swapping:")fmt.Println("a =", a)fmt.Println("b =", b)
}

接下来我们再看看数组和指针碰到一起会发生什么

指针和数组

指针指向数组中的某一个元素
var arr [5]int
var ptr *int
ptr = &arr[0] // 将指针指向数组的第一个元素
指针指向数组整体
// 声明一个数组
var arr [5]int
// 声明一个指向数组的指针
var ptr *[5]int
// 将指针指向数组
ptr = &arr

这里要注意类型匹配,我们之前说过长度也是类型的一部分,[5]int和[7]int是不一样的。[5]int要配合*[5]int

眼神要好
var ptrArr *[3]int //指向一个数组
var ptrArr [3]*int //每个元素指向一个数
传递数组

之前我们说过,在方法间传递数组其实是复制整个数组传递过去,相当于寻觅另外一个地方盖一座一模一样的房子。如果我们传递指向数组的指针,函数将能够修改原始数组的值。

func modifyArray(arr *[5]int) {// 修改数组的值(*arr)[0] = 100
}func main() {var arr [5]intmodifyArray(&arr)fmt.Println(arr) // 输出 [100 0 0 0 0]
}

指针和切片

之前我们已经学过,切片其实就是对数组进行包装,内部通过指针对数组进行操作。那么当切片和指针一起使用时,可以实现更灵活和高效的数据操作,比如动态地管理内存和访问数组的部分元素(二级指针嘛,玩C的都懂)。以下是一些示例,演示了切片和指针的结合使用:

package mainimport "fmt"func main() {// 创建一个切片slice := []int{1, 2, 3, 4, 5}// 创建一个指向切片的指针var ptr *[]intptr = &slice// 修改切片的值(*ptr)[0] = 100(*ptr)[1] = 200// 打印修改后的切片fmt.Println(*ptr) // 输出: [100 200 3 4 5]
}

个人觉得切片指针反而比数组用起来更轻松。map也一样道理:

package mainimport "fmt"func modifyMap(m *map[string]int) {// 向 map 中添加新的键值对(*m)["d"] = 4(*m)["e"] = 5// 修改已有键的值(*m)["a"] = 100
}func main() {// 创建一个 mapmyMap := map[string]int{"a": 1,"b": 2,"c": 3,}// 创建指向 map 的指针ptr := &myMap// 修改 map 内容modifyMap(ptr)// 打印修改后的 mapfmt.Println(*ptr)
}

指针和结构体

在 Go 语言中,指向结构体的指针是一种非常常见的用法基本和C语言一样。结构体是一种用户自定义的复合数据类型,它可以包含多个不同类型的字段,而指向结构体的指针则允许我们直接访问结构体的字段,并且可以在函数之间共享结构体的实例,而不需要进行复制。以下是指向结构体的指针如何使用的详细说明:

创建结构体指针
// 定义结构体
type Person struct {Name stringAge  int
}func main() {// 创建结构体的指针var p *Personp = &Person{"John", 30}// 或者使用 new() 函数创建结构体的指针p = new(Person)p.Name = "Alice"p.Age = 25
}

可以理解这里的new就相当于(*Person)malloc(sizeof(Person));

操作结构体字段
访问结构体字段:
fmt.Println((*p).Name)  // 打印结构体字段 Name
fmt.Println(p.Age)       // 也可以直接使用 p.Age 访问
修改结构体字段:
p.Name = "Bob"  // 直接赋值修改结构体字段
p.Age = 40
传递结构体指针给函数:
func modifyPerson(p *Person) {if p != nil {p.Age = 50}
}modifyPerson(p) // 调用函数修改结构体的字段值

总的来说,指向结构体的指针在 Go 中是非常常见的用法,它允许我们在函数间共享结构体的实例,并且可以在需要时直接访问和修改结构体的字段。使用指向结构体的指针可以避免结构体的复制,提高程序的性能和效率。

总结

Java中的指针是被隐藏的,程序员无法直接操作内存地址,而Go中的指针是一等公民,允许直接操作内存地址。本文仅仅介绍了指针操作的冰山一角,之后我们会继续介绍指针在面向对象编程中的应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/758732.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【堆】Top-K问题

标题:C语言库函数scanf()解读 水墨不写bug (图片来源于网络) 正文开始: Top-K问题是一类问题的统称: 即根据对象的某一属性,找出这个属性最突出的K个对象,并且通常对象…

22. UE5 RPG使用MMC根据等级设置血量和蓝量(下)

上一篇,我们实现了玩家角色和敌人的等级的获取,使用MMC的提前工作已经准备完成,那么,这一篇讲一下,如何使用MMC,通过角色等级和体力值设置角色的最大血量。 MMC 全称 Mod Magnitude Calculation&#xff0c…

基于springboot的同城宠物帮养照看平台

技术:springbootmysqlvue 一、背景 如今社会上各行各业,都喜欢用自己行业的专属软件工作,互联网发展到这个时候,人们已经发现离不开了互联网。新技术的产生,往往能解决一些老技术的弊端问题。因为传统同城上门喂遛宠物…

Java学习六—面向对象

一、关于面向对象 1.1简介 Java 是一种面向对象编程语言,其核心思想是面向对象编程(Object-Oriented Programming,OOP)。 面向对象编程是一种程序设计范式,它将数据与操作数据的方法(函数)捆…

软件工程-第三版王立福-第1章 绪论

本书结合IEEE最新发布的软件工程体系SWEBOK,和IEEE/ACM软件工程学科小组公布的软件工程教育知识体系SEEK,北大本科生指定教材。注重基础知识的系统性,选材的先进性及知识的应用。2009年出版 软件开发本质的认识,两大技术问题&…

JUnit5的条件测试、嵌套测试、重复测试

条件测试 JUnit5支持条件注解,根据布尔值判断是否执行测试。 自定义条件 EnabledIf和DisabledIf注解用来设置自定义条件,示例: Test EnabledIf("customCondition") void enabled() { // ... } Test DisabledIf("cust…

Java并发基石ReentrantLock:深入解读其原理与实现

码到三十五 : 个人主页 心中有诗画,指尖舞代码,目光览世界,步履越千山,人间尽值得 ! 在Java的并发编程库中,ReentrantLock是一种非常重要的同步工具,它提供了一种比内置synchronized关键字更加…

科研学习|研究方法——实验法

1.实验方法的渊源 今天我们说物理学、生物学是实验的科学,应该不会有人再持异议了,然而连物理学这样的学科在历史上也并非一开始就是实验科学。在2000多年以前的亚里士多德时代,众人都认为物理学是非实验性质的,物理学成为实验科学…

netty基础_12.用 Netty 自己实现简单的RPC

用 Netty 自己实现简单的RPC RPC 基本介绍我们的RPC 调用流程图己实现 Dubbo RPC(基于 Netty)需求说明设计说明代码封装的RPCNettyServerNettyServerHandlerNettyClientHandlerNettyClient 接口服务端(provider)HelloServiceImplServerBootstrap 客户端(…

第四百一十四回

文章目录 1. 概念介绍2. 思路与方法2.1 实现思路2.2 实现方法 3. 示例代码4. 内容总结 我们在上一章回中介绍了"自定义标题栏"相关的内容,本章回中将介绍自定义Action菜单.闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 我们在这里提到的…

【呼市经开区建设服务项目水、电能耗监测 数采案例】

实施方案 针对能耗采集中的水、电能源数据采集,因客观因素条件,数据采集方面存在较大难度。大多数国网电表485接口由于封签限制,不能实施采集,不让拆机接线,采集实施存在困难。水量能耗采集,存在类似问题&a…

腾讯云GPU服务器深度计算怎么收费?1小时、一个月和一年报价

腾讯云GPU服务器怎么收费?GPU服务器1小时多少钱?一个月收费价格表和一年费用标准,腾讯云百科txybk.com分享腾讯云GPU服务器GPU计算型GN10Xp、GPU服务器GN7、GPU渲染型 GN7vw等GPU实例费用价格,以及NVIDIA Tesla T4 GPU卡和V100详细…

Jmeter Ultimate Thread Group 和 Stepping Thread Group

线程组:使用复杂场景的性能测试 有时候我们做性能测试时,只依靠自带的线程组,显示满足不了性能测试中比较复杂的场景,下面这两种线程组可以帮助你很好的完成复杂的场景 第一种:Stepping Thread Group 在取样器错误后…

Socket类

2.2 Socket类 Socket 类:该类实现客户端套接字,套接字指的是两台设备之间通讯的端点。 构造方法 public Socket(String host, int port) :创建套接字对象并将其连接到指定主机上的指定端口号。如果指定的host是null ,则相当于指定地址为回送…

Appium —— 移动应用自动化测试开源工具!

Appium介绍 Appium是一个用于自动化移动应用程序的开源工具,它支持iOS和Android平台。通过Appium,开发人员可以使用各种编程语言(如Java、Python、Ruby等)编写测试脚本,以自动化测试移动应用程序的功能和用户界面。Ap…

基于springboot+vue的小区团购管理

博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战,欢迎高校老师\讲师\同行交流合作 ​主要内容:毕业设计(Javaweb项目|小程序|Pyt…

如何在Windows系统使用VS Code制作游戏网页并实现无公网IP远程访问

文章目录 前言1. 编写MENJA小游戏2. 安装cpolar内网穿透3. 配置MENJA小游戏公网访问地址4. 实现公网访问MENJA小游戏5. 固定MENJA小游戏公网地址 前言 本篇教程,我们将通过VS Code实现远程开发MENJA小游戏,并通过cpolar内网穿透发布到公网,分…

《操作系统实践-基于Linux应用与内核编程》第10章--实验 Qt聊天程序

前言: 内容参考《操作系统实践-基于Linux应用与内核编程》一书的示例代码和教材内容,所做的读书笔记。本文记录再这里按照书中示例做一遍代码编程实践加深对操作系统的理解。 引用: 《操作系统实践-基于Linux应用与内核编程》 作者:房胜、李旭健、黄…

微信小程序调试、断点调试

1、wxml 查看对应的页面组件 2、console面板可以用来打印信息 3、sources 用来断点调试 4、network面板用来调试接口 5、storage面板 可以查看每个key对应的value内容,这些数据在用户使用小程序时被持久化保存在本地。

【mac M3】idea删除不用或者失效的jdk

【mac M3】idea删除不用或者失效的jdk 不用(重复)或者失效的jdk如下: 重复或者已失效的JDK版本出现在下拉列表中不仅影响美观,也影响效率,删除jdk的步骤如下: 步骤1.点击File 步骤2.选择Project Structure…