java final 修饰参数_值传递还是址传递,Java中使用final作为参数的修饰的感想

背景

最近在项目里经常看到有的小伙伴喜欢在参数里面加上final关键字,平常也没怎么注意,偶然几天有空仔细看了一下,觉得十分有意思。所以记录下来,一方面是给自己多一些加深回忆的素材,另一方面也是希望能给与我有相同疑惑的同学有个参考。当然这个本身可能是比较基础的问题,但是多换个角度看也蛮有意思的

final关键字在Java中的应用场景其实也还是蛮多的,其中最出名的地方,应该就是继承的时候不允许继承吧。然后还有很多的场景就是final static来定义一些常量吧。当然,还有一个比较有意思的场景,就是今天我们的主角,在传参时使用final关键字。

比如这样:

public void doThings(final Object inputParam)

这里不由得联想到了当年刚学C++中,一个很有意思又很容易混淆的概念,参数时按址传递还是按值传递的呢?当然在Java中因为取消了 * 和 & 这些容易混淆的关键字,但是如果在传递参数时不注意的话,还是很容易造成一些不容易发现的错误的。那么就想从使用final关键词上来在看看Java有趣的传参机制。

为什么在传参中加上final

首先,我觉得还是给出一个大概的结论吧:

int、char这些基础类型来说,可以认为就是按值传递的(在函数中对其的修改不会影响到参数本身来源的值得改变)。当然String这个类型会比较特殊,在于虽然本身是一个基础类型,但是它同时又是一个对象,有一些列Object中继承的方法。(这里和String在内存中具体处理有关系,因为我们实际操作的是String其实是一个对象,而真实的字串值是存在于静态区,并非是String本身,这一点和装箱类型的Integer之类的有点相似。具体有兴趣的同学可以参考一下Java中内存结构,如内存模型的一个图 谜之音:凑不要脸的! 我:是啊,我就是,怎么了(捂脸))

对于对象来说,按照按值传递来处理,基本上也是没有问题的,而且能够简化问题本身。

但是(终于等到这个词了,据说是中国人最讨厌的两个字之一(捂脸)),在Java中其实也是可以直接改变参数对象的值的,只要你加上final关键字之后编译器不报错的话,而且这很好的弥补了有时我们返回值只有一个时的尴尬。

那么,这是为啥呢,我们就来看看final关键字吧~其实就final本身来说,还是比较好理解的,简单的可以认为final就代表着不能改变的意思。可以发现,不管是final在修饰变量,或者修饰方法,或者修饰类的时候,其实都最最终意味着不可改变的意思。

乍一听,感觉有点玄乎,不可改变为啥修饰了参数之后反而使得参数值本身可以改变了呢?其实这里和Java本身的机制有关,因为传递的是参数并非参数本身,而是参数本身的一个引用的复制。这也就是很多文章将这里的传递和赋值号联系起来的原因。具体可以参考这些文章,写的挺好的:

所以参考下图,我们可以发现,其实参数值的变化,是因为我们在内部可以通过引用来修改到参数值本身,但是其本质仍然是一个引用,而final定义的不可改变,是引用的不可改变,也就是引用本身是不能改变的(即不能指向其它对象!)

3419270ba367

参数传递

所以引用的不可改变就表示了我们始终只能修改最初开始所指向的值本身,而不会因为修改成中间可能指向了其它值得引用本身。这里可能有点难以理解,我们可以通过以下代码来加强理解一下:

public class AnPoJo {

public int a = 1;

@Override

public String toString() {

return String.valueOf(a);

}

}

public class Application {

public static void main(String[] args) {

AnPoJo pojo = new AnPoJo();

modify1(pojo);

System.out.println("modify1: "+ pojo);

modify2(pojo);

System.out.println("modify2: " + pojo);

}

private static AnPoJo modify1(AnPoJo pojo){

pojo = new AnPoJo();

pojo.a = 2;

return pojo;

}

private static AnPoJo modify2(AnPoJo pojo){

pojo.a = 2;

return pojo;

}

}

打印结果如下:

modify1: 1

modify2: 2

由此可见,其实只要引用本身不变化,是可以成功达到想要修改参数值的效果的。因此这里如果使用final关键字来标记想要直接改变参数值的方法是一个非常好的编程习惯,杜绝了疏忽可能导致的错误。例如可以这样来修改方法modify1,通过编译器就能很容易的检测到错误点:

private static AnPoJo modify1(final AnPoJo pojo){

pojo = new AnPoJo(); // compile error

pojo.a = 2;

return pojo;

}

结语

其实有很多非常好的编程技巧,看似平淡无奇,但是仔细思考起来还是蛮有意思的一件事,能从中体会到优秀代码中对语言本身的理解和把握。除了本例之外,还有诸如其它一些例子,如: if(true==isValid())

当然由于知识结构所限,也有一个学习的过程,如果有所错误或者遗漏,欢迎大家指正补充~

并在此给出一个简单的结论:

如果不清楚到底是传值还是传址,Java中就全部当做传值处理就ok(除了部分特殊场景外,也不会有什么不足)

如果一定要改变参数值本身,一定要加final关键字!

enjoy~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值