大家都认可,对于 final int a = 0
,a 变为 1 是不可接受的。
那为啥 final AtomicInteger a= new AtomicInteger(0)
的 a,由 0 变 1 是没有违背 final 所暗示的语义呢?
Java编译器没有对此报错,是因为(A)在设计时就不觉得要在这里报错,还是(B)要实现这一程度的检查的工作量太大,这里留下final关键词做语义上的暗示算了呢?
1
mineralsalt 2021-12-31 10:51:07 +08:00
final 是限制变量不能被再次赋值, 而修改变量内部数据不属于 final 的管辖范围, 这根本就是两码事, 你要限制数组不能改动 , 可以用别的方法
|
2
aptupdate 2021-12-31 10:53:34 +08:00 via iPhone
我也想过这个问题,但是没想通。
|
3
dqzcwxb 2021-12-31 10:55:42 +08:00
不违反,你改的是字段不是当前这个对象你把这俩分开想就行
|
4
banmuyutian 2021-12-31 10:56:38 +08:00
final 只是限制不能再次赋值,但是对象内的行为是管不到的,如果你不想 List 被修改可以用 ImmutableList
|
5
wolfie 2021-12-31 11:03:07 +08:00
final 项目经理 = 张三。
张三将任务分配给 李四。 张三将任务重新分配给 王五。 |
6
Newyorkcity OP |
7
Cabana 2021-12-31 11:13:03 +08:00
你需要的是 ImmutableList
|
8
misdake 2021-12-31 11:13:58 +08:00
想要内部不可变,java 的话就改用不可变对象,或者对外只暴露不可变接口。反正是可以做到,但可能费一些功夫去写接口。我在一定程度上赞同写一套只读接口,再 extend 出一个读写接口,有点啰嗦,但比较规整。
|
9
otakustay 2021-12-31 11:16:31 +08:00 3
final 和 immutable 是不一样的概念
|
10
seanzxx 2021-12-31 11:28:27 +08:00 9
final 是保证变量的值不变,但什么是变量的值你没清楚
final int a = 3; // a 的值是 3 ,你不能再修改 a 的值 a = 4; // 编译错误: cannot assign a value to final variable a final List b = new ArrayList(); // b 的值是 List 对象的地址(引用),你不能改变 b 的值 b= new LinkedList(); // 编译报错:cannot assign a value to final variable b |
11
Newyorkcity OP @seanzxx 一个数组,在 A 时间点,里面啥元素点也没有,在 B 时间点,有了几个元素在里面。这个数组是否发生过变化?不纠结于数组,一个人,A 时间点身高一米七,B 时间点身高一米八,这个人算变化了吧?
|
12
agagega 2021-12-31 11:32:37 +08:00 via iPhone
因为你这里的 List 不是值语义
|
13
Vegetable 2021-12-31 11:32:55 +08:00 3
final 和 readonly/mutable 其实是不同层面的概念。在扩大解释之前应该慎重,不要想当然。
|
14
aragakiyuii 2021-12-31 11:33:03 +08:00
@Newyorkcity #11 身份证号没变
|
15
yangzzzzzz 2021-12-31 11:38:24 +08:00
张三吃胖了还是张三
|
16
Kaciras 2021-12-31 11:40:43 +08:00
final 修饰的是指针本身,不是所指的对象。
|
17
dying4death 2021-12-31 11:41:43 +08:00
|
18
kop1989 2021-12-31 11:42:25 +08:00
@Newyorkcity #11 但 final ,其实指的是户口本上的这个“人”与生物学上的“人”的绑定关系。虽然你的体态、甚至性别都变了,你的户口本依然是你的户口本。
|
19
mineralsalt 2021-12-31 11:47:25 +08:00
那为啥 final AtomicInteger a= new AtomicInteger(0) 的 a ,由 0 变 1 是没有违背 final 所暗示的语义呢?
你这种描述根本就不对, a 既不是 0 也不是 1, 它是一个对象, 只要这个对象的指针不改变, 那就没有违反 final |
20
wolfie 2021-12-31 11:49:01 +08:00
@Newyorkcity #6
final 就是不存在张三被换掉的可能。 程序角度 final 限制栈修改,实际堆中的对象里面的成员是否被替换 管不着。 class 项目经理 { 实际干活的 = 李四、王五; // 这个指派张三为项目经理的人管不着。加不加 final 由张三决定。 } |
21
seanzxx 2021-12-31 11:50:19 +08:00 2
@Newyorkcity final 和 immutable 是不一样的
final 是指这个变量不能再赋值,也就保证了这个变量的值不会改变 引用变量,他的值是地址,你改变的是地址指向的内存中内容 举个例子,公司的员工管理系统保存了你的家庭地址,但你做的是改变你家的家具。 如果你的家庭地址是 final ,你就不能再搬家了,但你怎么改变你家的布置是没有限制的 |
22
geekfxxk 2021-12-31 11:52:58 +08:00 1
值类型,引用类型没理解好
|
23
yolee599 2021-12-31 11:55:25 +08:00
假设有一个“人”,用来找到这个“人”的关键字叫“身份证号”,现在我们定义“身份证号” 是 final 的,一经赋值就不会变了,无论你身高怎么变,体重怎么变,年龄怎么变,性别怎么变。“身份证号”还是那个“身份证号”。
现在把上面例子的“身份证号”替换为“对象”,是不是就好理解多了。 |
24
Asan 2021-12-31 11:58:10 +08:00
final 不能修改改的是引用,但是引用指向的内存是可以涂涂改改的
|
25
AoEiuV020CN 2021-12-31 12:01:38 +08:00
这种问题学 c 语言的时候讲的比较清楚,因为不搞清楚真的容易炸,
指针不变和内容不变,两码事,基本类型不是指针,不存在指针不变的用法,引用类型指针不变情况内容变没问题, |
26
timethinker 2021-12-31 12:04:53 +08:00 1
一个变量到底存储的是什么?变量类型指明了存储的数据如何使用,对于一个 int 类型的变量来说,存储的就是实际的数字。对于对象来说,存储的就是一个内存地址,它被解释为具体类型在内存中的起始地址加上成员变量类型偏移量。以上这些只是一些表面的解释,编译器或者解释器 /虚拟机会根据这些信息来优化,并最终操作计算机。
所以 final 修饰的是变量值不可改变,而不是这个值再被用来解释翻译,并操作其他内存的数据不可改变。 |
27
dcsuibian 2021-12-31 12:40:38 +08:00
“final AtomicInteger a= new AtomicInteger(0) 的 a ,由 0 变 1”
a 既不是 0 也不是 1 ,而是一个 AtomicInteger 实例,一直都是这个实例,没有变过 如果我写了一个 final CustomClass c=new CustomClass(arg1,arg2,arg3) 那么我的 c 就一直是这个 CustomClass 啊,你把 AtomicInteger 和数字直接连起来了,但其实它们是分开的 |
28
Jooooooooo 2021-12-31 12:45:39 +08:00
final 设计就是如此.
|
29
Shawlaw 2021-12-31 13:11:06 +08:00 via iPhone
对象时,final 的是“指针”,定义如此。
|
30
xiao109 2021-12-31 13:14:20 +08:00
说明你还是没弄清 java 里基础类型和引用类型的区别
|
31
xiao109 2021-12-31 13:16:24 +08:00
简单一点理解就是 final 修饰的变量不能再用=操作符去操作了。
|
32
adeng 2021-12-31 13:17:40 +08:00 via Android
@Newyorkcity 你有问题,你在按照自己的想法去套,seanzxx 说的很清楚了。你说的“一个数组”,这个数组要有名字,定为 arr ,arr 指向 内存地址 addr1 ,你所谓的“A 时间点”,addr1 啥元素也没有;“B 时间点”有几个元素,addr1 没变成别的啊,addr1 内存还是那块内存,没变成 addr2 内存。arr = addr1 ,始终没有变。
|
33
kera0a 2021-12-31 13:18:12 +08:00
楼主肯定明白值类型和引用类型,楼主应该说的是 final 这个概念,应该是只针对指针值不变,还是把指针指向的对象也保护不变。java 中对此的定义是只是保证值不变。
但有些语言例如 swift 的 Array , 使用 let 修饰就会保护 Array 对象不变 |
34
kratzer 2021-12-31 13:21:31 +08:00
final 证件号=你
|
35
kratzer 2021-12-31 13:24:22 +08:00
证件号就不能再指定给她 /他了
你可以变胖,减肥,变形 |
36
Leviathann 2021-12-31 13:36:08 +08:00
swift 是这样的
|
37
yaphets666 2021-12-31 13:41:28 +08:00
list 有个地址对吧,有一个变量 a 存储着这个 list 的地址, 你 final 的是变量 a ,不是 list 。
|
38
cndotaer 2021-12-31 13:45:04 +08:00 1
https://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.4
If a final variable holds a reference to an object, then the state of the object may be changed by operations on the object, but the variable will always refer to the same object. |
39
dinghmcn 2021-12-31 13:52:05 +08:00
cpp 双 const 能保证值和引用都不能改变,java 其实模糊了值和引用才导致这种理解的偏差
|
40
nonoyang 2021-12-31 14:04:37 +08:00
java 归根到底还是值传递,从这个角度看这个 final 没啥问题,说不定未来 jdk20 就支持了呢。
|
41
meeop 2021-12-31 14:49:33 +08:00
你说的对,final 和浅拷贝一样,都只管变量引用本身,不管更深层次的变量持有数据,纯粹从语义上看是效果是不对的
不过,毕竟编程语言是需要和性能妥协的,深层次不可变约束会引入更多性能成本,也许未来的编程语言能支持上 |
42
otakustay 2021-12-31 15:17:26 +08:00
我还以为楼主在纠结 final 的自然语义,结果发现他被程序语义说服了……
|
43
leaves615 2021-12-31 17:04:24 +08:00
final int a = 0; //值赋值
final AtomInteger a = new AtomInteger(0); //引用对象(也就是指针)赋值 final 修饰符是用来标识变量赋值后不能进行重复赋值 final int a 只能赋值一次。 而 final AtomInteger a 也是赋值一次(指针赋值) |
44
sdushn 2021-12-31 17:54:23 +08:00
final 是对 对象地址的限定。
final int a = 0 ,a 变为 1 是不可接受的。并非是值的变化不可接受,而是 a 对象地址不能变,如果 值 0 所在的地址可以变更为 值 1 ,那么 a 的值也可以从 0 变 1 。 |
45
kamal 2021-12-31 18:05:56 +08:00
看了大家的解释,感觉跟 ES 语法的 const 一个意思。
|
46
Greatshu 2021-12-31 18:30:36 +08:00
折腾一下 C 指针就明白了
|
47
GrayXu 2021-12-31 18:36:20 +08:00
@Newyorkcity 不是,到底是谁和你说 java 没有引用的。。。
|
48
moonmagian 2021-12-31 18:55:45 +08:00 via Android
cpp 为了区分这种区别有 logical 和 physical 的 const ,physical const 是指值本身的不变性,而 logical const 指从引用 /指针的视角看指向的对象时的不变性(尽管这个对象可能并不是 physical const )。
java 对引用类型变量的 final 声明更类似 cpp 中的 physical const (指针本身的不变性,T* const ),而不是 logical const (指针指向对象从指针视角看的不变性,const T*),只是 final 对值类型的变量有特殊的逻辑( const T ) |
49
SingeeKing 2021-12-31 19:01:21 +08:00 via iPhone
其实就是设计缺陷吧,例如 Rust 就增加了 mut 关键字来专门标识是否允许修改
|
50
wiix 2021-12-31 20:18:35 +08:00
Collections.unmodifiableList(list)
List.of(list.toArray(new String[]{})) |
51
Dragonphy 2021-12-31 21:53:48 +08:00
这个我记得属于 JVM 的范畴,可以看看相关书籍技能
|
52
bsg1992 2021-12-31 22:06:56 +08:00
完全不一样的 final 修饰的是 内存引用不能改变。 你 add 没有改变内存指向。
|
53
season8 2021-12-31 22:19:32 +08:00
List 集合 -->火车见过吧,就比作火车好了
final List --> D3062 次列车就是这个编号 xxx 的车,不能用别的车 每天坐的人不一样能说 火车变了吗,火车还是那个火车。 其实就是一个概念的问题,你认为 一个 List 就应该是 List + 它的元素 ,但很明显 Java 设计的就是 List 只是容器,你说的这层 “final” 只能你自己去实现。 了解概念区别就行了,再探究下去感觉没啥意义。 |
54
Cbdy 2021-12-31 22:39:22 +08:00 via Android
你说的叫 immutable 吧
贴个链接 https://docs.oracle.com/javase/9/core/creating-immutable-lists-sets-and-maps.htm |
55
totoro52 2022-01-01 14:10:23 +08:00 via iPhone
一个杯子 装了水,你倒了,换成了可乐,你能说这个杯子的物理性质发生了变化吗
|