提交 c73f8598 authored 作者: 邓砥奕's avatar 邓砥奕

更新笔记

上级 28421349
...@@ -6,15 +6,15 @@ Java8之前Lambda和匿名内部类引用外部局部变量必须声明为final ...@@ -6,15 +6,15 @@ Java8之前Lambda和匿名内部类引用外部局部变量必须声明为final
#### 二、为什么必须为final或effectively final #### 二、为什么必须为final或effectively final
1.由于Lambda和匿名内部类无法直接访问外部局部变量,因此只能通过它们的构造方法将需要引用的局部变量传入进去。通过构造方法传递的只是局部变量的一个拷贝,因此当外部或者内部类修改这个变量时,对方都不知道该变量的修改。为了保证变量的安全,所以在Java中直接不允许修改引用的局部变量,编译时该变量必须为final或者effectively final类型 1.引入的局部变量是拷贝,不能改变原本的值。由于Lambda和匿名内部类有可能在另外一个线程执行无法访问外部局部变量,因此jvm通过它们的构造方法将需要引用的局部变量传入进去。通过构造方法传递的只是局部变量的一个拷贝,因此当外部或者内部类修改这个变量时,对方都不知道该变量的修改。既然改变不了该变量的值,为了保证变量的安全,所以在Java中索性直接不允许修改引用的局部变量,编译时强制该变量必须为final或者effectively final类型。相当于直接告诉使用者:这个局部变量在表达式内部不能改动,在外部也不要改动了
2.局部变量和内部类对象的生命周期不同。当调用该方法时,该方法中的局部变量在栈中被创建,可能该方法调用结束并且释放了栈中的变量,但是内部类却还没有执行完,此时就找不到该局部变量了。为了保证变量的安全访问,内部类会自动通过构造函数的参数创建一个final类型的拷贝值。由于是拷贝值,因此一但一方修改了局部变量的值会导致另外一方对该变量的更改不可见,从而出现问题。 2.局部变量和内部类对象的生命周期不同。当调用该方法时,该方法中的局部变量在栈中被创建,是线程私有的。可能该方法调用结束并且释放了栈中的变量,但是内部类却还没有执行完,此时就找不到该局部变量了。为了保证变量的安全访问,内部类会自动通过构造函数的参数创建一个final类型的拷贝值。由于是拷贝值,因此一但一方修改了局部变量的值会导致另外一方对该变量的更改不可见,从而出现问题。
3.并发的安全性问题。当多个线程并发执行Lambda或匿名内部类中的代码时,不能保证外部成员变量的原子性,从而可能导致变量值的安全性问题。当引用自由变量时,考虑并发的安全性,也必须为final或effectively final。 3.并发的安全性问题。当多个线程并发执行Lambda或匿名内部类中的代码时,不能保证外部成员变量的原子性,从而可能导致变量值的安全性问题。当引用自由变量时,考虑并发的安全性,也必须为final或effectively final。
#### 三、修改变量的一些方法 #### 三、有哪些变量可变
1.定义为内部类局部变量 1.定义为内部类局部变量
2.定义为外部类的成员变量:成员变量存储在堆上,可以正常访问。 2.定义为外部类的成员变量:成员变量存储在堆上,可以正常访问。
...@@ -22,3 +22,13 @@ Java8之前Lambda和匿名内部类引用外部局部变量必须声明为final ...@@ -22,3 +22,13 @@ Java8之前Lambda和匿名内部类引用外部局部变量必须声明为final
4.定义为数组、list:数组和list的地址不会发生改变,可以修改数组和list中的值,但是并发时不能保证安全性。 4.定义为数组、list:数组和list的地址不会发生改变,可以修改数组和list中的值,但是并发时不能保证安全性。
5.定义为静态的变量
#### 四、参数传递
在研究本问题时发现了java参数传递的另外一个有趣的问题。在java中参数传递的究竟是参数的值还是地址呢?
1.当传递的参数为基本类型及其封装类和String时,传递的是值的拷贝,无论如何修改这个拷贝值,原值是不会改变的。基本类型的封装类和String会new一个新的拷贝对象,修改的是拷贝的对象。
2.当传递的是引用类型,包括数组、对象以及接口时,传递的是对象在内存中的地址的拷贝。因此修改这些对象的值会影响原值。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论