finalrecovery汉化版 疑惑

问题对人有帮助,内容完整,我也想知道答案
问题没有实际价值,缺少关键内容,没有改进余地
中,对于 DCL提供了一种通过 final 关键字来实现的方式,源码如下:
public class FinalWrapper&T& {
public final T
public FinalWrapper(T value) {
this.value =
public class Foo {
private FinalWrapper&Helper& helperW
public Helper getHelper() {
FinalWrapper&Helper& wrapper = helperW
if (wrapper == null) {
synchronized(this) {
if (helperWrapper == null) {
helperWrapper = new FinalWrapper&Helper&(new Helper());
wrapper = helperW
return wrapper.
同时,文章中有如下说明:The local variable wrapper is required for correctness.
请问一下,为什么这个 局部变量 wrapper 是必须的?我个人认为此变量是可有可无的,还请有识者指点一下。
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
这个问题我最近也看了,请教一下二位,你们最终的结论是:The local variable wrapper is required for correctness. 是完全不需要的对吗?
该答案已被忽略,原因:不符合答题规范:内容不是答案,可用评论、投票替代
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
第一次看到双重锁定检查还可以用final来实现,我试着回答一下这个问题,说一下自己的理解。
首先说一下基于volatile实现的双重锁定检查:
public class SafeDoubleCheckedLocking {
private volatile static I
public static Instance getInstance() {
if (instance == null) {
synchronized (SafeDoubleCheckedLocking.class) {
if (instance == null)
instance = new Instance();//instance为volatile,现在没问题了
如果instance不声明为volatile类型的,上面代码中的双重锁定检查在多线程环境下是会有问题的。主要问题在于多线程环境下,在一个线程中instance有可能在没有完全初始化之前(只初始化了一部分),就被其他线程使用了。 比如,instance刚被分配了内存空间,还没完成new Instance()的全部操作,其他线程在第一次对instance进行是否为空的判断的时候,结果是false(由于instance指向的是个内存地址,所以分配了内存空间之后,instance这个变量已经不为空),这个时候这个线程就会直接返回,然而instance变量指向的内存空间还没完成new Instance()的全部操作。 这样一来,一个没有被完全初始化的instance就会被使用。
上面我说的这种状况看起来不可能发生,毕竟从代码来看,这根本不可能啊。
初始化instance变量的伪代码如下所示:
memory = allocate();
//1:分配对象的内存空间
ctorInstance(memory);
//2:初始化对象
instance =
//3:设置instance指向刚分配的内存地址
之所以会发生上面我说的这种状况,是因为在一些编译器上存在指令排序,初始化过程可能被重排成这样:
memory = allocate();
//1:分配对象的内存空间
instance =
//3:设置instance指向刚分配的内存地址
//注意,此时对象还没有被初始化!
ctorInstance(memory);
//2:初始化对象
而volatile存在的意义就在于禁止这种重排!要想详细了解一下指令重排,可以参考一下
再来说一下final域的重排规则:
写final的重排规则:
JMM禁止编译器把final域的写重排序到构造函数之外。
编译器会在final域的写之后,构造函数return之前,插入一个StoreStore屏障。这个屏障禁止处理器把final域的写重排序到构造函数之外。也就是说:写final域的重排序规则可以确保:在对象引用为任意线程可见之前,对象的final域已经被正确初始化过了。
读final的重排规则:
在一个线程中,初次读对象引用与初次读该对象包含的final域,JMM禁止处理器重排序这两个操作(注意,这个规则仅仅针对处理器)。编译器会在读final域操作的前面插入一个LoadLoad屏障。也就是说:读final域的重排序规则可以确保:在读一个对象的final域之前,一定会先读包含这个final域的对象的引用。
如果final域是引用类型,那么增加如下约束:
在构造函数内对一个final引用的对象的成员域的写入,与随后在构造函数外把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。(个人觉得基本意思也就是确保在构造函数外把这个被构造对象的引用赋值给一个引用变量之前,final域已经完全初始化并且赋值给了当前构造对象的成员域,至于初始化和赋值这两个操作则不确保先后顺序。)
具体解释可以参考
再回到这个问题本身:
public class Foo {
private FinalWrapper&Helper& helperW
public Helper getHelper() {
FinalWrapper&Helper& wrapper = helperW
if (wrapper == null) {
synchronized(this) {
if (helperWrapper == null) {
helperWrapper = new FinalWrapper&Helper&(new Helper());
wrapper = helperW//此处增加了对helperWrapper的引用,写final域的重排序规则可以确保:在对象引用为任意线程可见之前,对象的final域已经被正确初始化过了。
//所以此处增加一次对helperWrapper的引用,可以确保FinalWrapper中的value已经成功初始化。
//如果此处没有赋值给本地变量,final域的写操作保证不能生效,那么getHelper()方法并不能保证,被return的value已经被正确初始化过。
return wrapper.
//读final域的重排序规则可以确保:在读一个对象的final域之前,一定会先读包含这个final域的对象的引用。所以此处的读取操作确保读value之前,会读取wrapper这个引用变量指向的对象,
//而写操作又可以确保在对象可见之前,final域已经正确初始化了。所以此处肯定会读取到正确的value。
//如果读final域不做这个保证,那么对value的读取操作有可能会被重排到对wrapper赋值之前,有可能读取到不正确的value值。
主要问题在于这些关键字对指令重排的保证,个人的一个理解是这样的,欢迎讨论。
分享到微博?
你好!看起来你挺喜欢这个内容,但是你还没有注册帐号。 当你创建了帐号,我们能准确地追踪你关注的问题,在有新答案或内容的时候收到网页和邮件通知。还能直接向作者咨询更多细节。如果上面的内容有帮助,记得点赞 (????)? 表示感谢。
明天提醒我
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)
我要该,理由是:2587人阅读
java基础(7)
&关于final关键字,总是那么些疑惑,今天就总结一下。
一.final的概念:在Java中, 可以使用final关键字修饰类、方法以及成员变量。
1.final标记的类不能被继承;
在设计类时候,如果这个类不需要有子类,类的实现细节不允许改变,并且确信这个类不会载被扩展,那么就设计为final类。
2.final标记的方法不能被子类复写;
如果一个类不允许其子类覆盖某个方法,则可以把这个方法声明为final方法。
使用final方法的原因有二:
第一、把方法锁定,防止任何继承类修改它的意义和实现。
第二、高效。编译器在遇到调用final方法时候会转入内嵌机制,大大提高执行效率。
3.final标记的变量即成为常量,只能被赋值一次.
二、final的用法
1、final 对于常量来说,意味着值不能改变,例如 final int i=100。这个i的值永远都是100。
但是对于变量来说又不一样,只是标识这个引用不可被改变,例如 final File f=new File(&c:\\test.txt&);那么这个f一定是不能被改变的,如果f本身有方法修改其中的成员变量,例如是否可读,是允许修改的。有个[/size][size=medium][size=large][/size]形象的比喻:一个女子定义了一个final的老公,这个老公的职业和收入都是允许改变的,只是这个女人不会换老公而已。
2、关于空白final
final修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。&
 另外,final变量定义的时候,可以先声明,而不给初值,这中变量也称为final空白,无论什么情况,编译器都确保空白final在使用之前必须被初始化。但是,final空白在final关键字final的使用上提供了更大的灵活性,为此,一个类中的final数据成员就可以实现依对象而有所不同,却有保持其恒定不变的特征。
public class FinalTest {
final int q=3;
FinalTest(){
FinalTest(int i){
p=i;//可以赋值,相当于直接定义p
q=i;//不能为一个final变量赋值
3、final内存分配
刚提到了内嵌机制,现在详细展开。
要知道调用一个函数除了函数本身的执行时间之外,还需要额外的时间去寻找这个函数(类内部有一个函数签名和函数地址的映射表)。所以减少函数调用次数就等于降低了性能消耗。&
final修饰的函数会被编译器优化,优化的结果是减少了函数调用的次数。如何实现的,举个例子给你看:
public class Test{
final void func(){System.out.println(&g&);};
public void main(String[] args){
for(int j=0;j&1000;j++)&&
经过编译器优化之后,这个类变成了相当于这样写:
public class Test{
final void func(){System.out.println(&g&);};
public void main(String[] args){
for(int j=0;j&1000;j++)&
{System.out.println(&g&);}
看出来区别了吧?编译器直接将func的函数体内嵌到了调用函数的地方,这样的结果是节省了1000次函数调用,当然编译器处理成字节码,只是我们可以想象成这样,看个明白。
不过,当函数体太长的话,用final可能适得其反,因为经过编译器内嵌之后代码长度大大增加,于是就增加了jvm解释字节码的时间。
在使用final修饰方法的时候,编译器会将被final修饰过的方法插入到调用者代码处,提高运行速度和效率,但被final修饰的方法体不能过大,编译器可能会放弃内联,但究竟多大的方法会放弃,我还没有做测试来计算过。
本文是通过两个疑问来继续阐述的:
1、使用final修饰方法会提高速度和效率吗;
见下面的测试代码,我会执行五次:
public&class&Test &&
{ && &&&&public&static&void&getJava() &&
&&&&{ && &&&&&&&&String&str1&=&&Java&&; &&
&&&&&&&&String&str2&=&&final&&; &&
&&&&&&&&for&(int&i&=&0;&i&&&<span style="color:#c0;&i&#43;&#43;)
&& &&&&&&&&{ && &&&&&&&&&&&&str1&&#43;=&str2; && &&&&&&&&} && &&&&} && &&&&public&static&final&void&getJava_Final() &&
&&&&{ && &&&&&&&&String&str1&=&&Java&&; &&
&&&&&&&&String&str2&=&&final&&; &&
&&&&&&&&for&(int&i&=&0;&i&&&<span style="color:#c0;&i&#43;&#43;)
&& &&&&&&&&{ && &&&&&&&&&&&&str1&&#43;=&str2; && &&&&&&&&} && &&&&} && &&&&public&static&void&main(String[]&args) &&
&&&&{ && &&&&&&&&long&start&=&System.currentTimeMillis(); &&
&&&&&&&&getJava(); && &&&&&&&&System.out.println(&调用不带final修饰的方法执行时间为:&&&#43;&(System.currentTimeMillis()&-&start)&&#43;&&毫秒时间&); &&
&&&&&&&&start&=&System.currentTimeMillis(); && &&&&&&&&String&str1&=&&Java&&; &&
&&&&&&&&String&str2&=&&final&&; &&
&&&&&&&&for&(int&i&=&0;&i&&&<span style="color:#c0;&i&#43;&#43;)
&& &&&&&&&&{ && &&&&&&&&&&&&str1&&#43;=&str2; && &&&&&&&&} && &&&&&&&&System.out.println(&正常的执行时间为:&&&#43;&(System.currentTimeMillis()&-&start)&&#43;&&毫秒时间&); &&
&&&&&&&&start&=&System.currentTimeMillis(); && &&&&&&&&getJava_Final(); && &&&&&&&&System.out.println(&调用final修饰的方法执行时间为:&&&#43;&(System.currentTimeMillis()&-&start)&&#43;&&毫秒时间&); &&
&&&&} && }&&
public&class&Test&{&public&static&void&getJava()&{&String&str1&=&&Java&&;&String&str2&=&&final&&;&for&(int&i&=&0;&i&&&10000;&i&#43;&#43;)&{&str1&&#43;=&str2;&}&}&public&static&final&void&getJava_Final()&{&String&str1&=&&Java&&;&String&str2&=&&final&&;&for&(int&i&=&0;&i&&&10000;&i&#43;&#43;)&{&str1&&#43;=&str2;&}&}&public&static&void&main(String[]&args)&{&long&start&=&System.currentTimeMillis();&getJava();&System.out.println(&调用不带final修饰的方法执行时间为:&&&#43;&(System.currentTimeMillis()&-&start)&&#43;&&毫秒时间&);&start&=&System.currentTimeMillis();&String&str1&=&&Java&&;&String&str2&=&&final&&;&for&(int&i&=&0;&i&&&10000;&i&#43;&#43;)&{&str1&&#43;=&str2;&}&System.out.println(&正常的执行时间为:&&&#43;&(System.currentTimeMillis()&-&start)&&#43;&&毫秒时间&);&start&=&System.currentTimeMillis();&getJava_Final();&System.out.println(&调用final修饰的方法执行时间为:&&&#43;&(System.currentTimeMillis()&-&start)&&#43;&&毫秒时间&);&}&}&&
public class Test { public static void getJava() { String str1 = &Java &; String str2 = &final &; for (int i = 0; i & 10000; i++) { str1 += str2; } } public static final void getJava_Final() { String str1 = &Java &; String str2 = &final &; for (int i = 0; i & 10000; i++) { str1 += str2; } } public static void main(String[] args) { long start = System.currentTimeMillis(); getJava(); System.out.println(&调用不带final修饰的方法执行时间为:& + (System.currentTimeMillis() - start) + &毫秒时间&); start = System.currentTimeMillis(); String str1 = &Java &; String str2 = &final &; for (int i = 0; i & 10000; i++) { str1 += str2; } System.out.println(&正常的执行时间为:& + (System.currentTimeMillis() - start) + &毫秒时间&); start = System.currentTimeMillis(); getJava_Final(); System.out.println(&调用final修饰的方法执行时间为:& + (System.currentTimeMillis() - start) + &毫秒时间&); } }
调用不带final修饰的方法执行时间为:1732毫秒时间
正常的执行时间为:1498毫秒时间
调用final修饰的方法执行时间为:1593毫秒时间
调用不带final修饰的方法执行时间为:1217毫秒时间
正常的执行时间为:1031毫秒时间
调用final修饰的方法执行时间为:1124毫秒时间
调用不带final修饰的方法执行时间为:1154毫秒时间
正常的执行时间为:1140毫秒时间
调用final修饰的方法执行时间为:1202毫秒时间
调用不带final修饰的方法执行时间为:1139毫秒时间
正常的执行时间为:999毫秒时间
调用final修饰的方法执行时间为:1092毫秒时间
调用不带final修饰的方法执行时间为:1186毫秒时间
正常的执行时间为:1030毫秒时间
调用final修饰的方法执行时间为:1109毫秒时间
&&& 由以上运行结果不难看出,执行最快的是“正常的执行”即代码直接编写,而使用final修饰的方法,不像有些书上或者文章上所说的那样,速度与效率与“正常的执行”无异,而是位于第二位,最差的是调用不加final修饰的方法。
观点:加了比不加好一点。
1、使用final修饰变量会让变量的&#20540;不能被改变吗;
public&class&Final &&
{ && &&&&public&static&void&main(String[]&args) &&
&&&&{ && &&&&&&&&Color.color[3]&=&&white&; &&
&&&&&&&&for&(String&color&:&Color.color) &&
&&&&&&&&&&&&System.out.print(color&#43;&&&); &&
&&&&} && } && && class&Color && { && &&&&public&static&final&String[]&color&=&{&&red&,&&blue&,&&yellow&,&&black&&};
public&class&Final&{&public&static&void&main(String[]&args)&{&Color.color[3]&=&&white&;&for&(String&color&:&Color.color)&System.out.print(color&#43;&&&);&}&}&class&Color&{&public&static&final&String[]&color&=&{&&red&,&&blue&,&&yellow&,&&black&&};&}&&
public class Final { public static void main(String[] args) { Color.color[3] = &white&; for (String color : Color.color) System.out.print(color+& &); } } class Color { public static final String[] color = { &red&, &blue&, &yellow&, &black& }; }
执行结果:
red blue yellow white
看!,黑色变成了白色。
&&& 在使用findbugs插件时,就会提示public static String[] color = { &red&, &blue&, &yellow&, &black& };这行代码不安全,但加上final修饰,这行代码仍然是不安全的,因为final没有做到保证变量的&#20540;不会被修改!原因是:final关键字只能保证变量本身不能被赋与新&#20540;,而不能保证变量的内部结构不被修改。例如在main方法有如下代码Color.color = new String[]{&&};就会报错了。那可能有的同学就会问了,加上final关键字不能保证数组不会被外部修改,那有什么方法能够保证呢?答案就是降低访问级别,把数组设为private。这样的话,就解决了数组在外部被修改的不安全性,但也产生了另一个问题,那就是这个数组要被外部使用的。
解决这个问题见代码:
import&java.util.AbstractL &&
import&java.util.L && && public&class&Final &&
{ && &&&&public&static&void&main(String[]&args) &&
&&&&{ && &&&&&&&&for&(String&color&:&Color.color) &&
&&&&&&&&&&&&System.out.print(color&&#43;&&&&); &&
&&&&&&&&Color.color.set(3,&&white&); &&
&&&&} && } && && class&Color && { && &&&&private&static&String[]&_color&=&{&&red&,&&blue&,&&yellow&,&&black&&};
&& &&&&public&static&List&String&&color&=&new&AbstractList&String&() &&
&&&&{ && &&&&&&&&@Override&&
&&&&&&&&public&String&get(int&index) &&
&&&&&&&&{ && &&&&&&&&&&&&return&_color[index]; &&
&&&&&&&&} && &&&&&&&&@Override&&
&&&&&&&&public&String&set(int&index,&String&value) &&
&&&&&&&&{ && &&&&&&&&&&&&throw&new&RuntimeException(&为了代码安全,不能修改数组&); &&
&&&&&&&&} && &&&&&&&&@Override&&
&&&&&&&&public&int&size() &&
&&&&&&&&{ && &&&&&&&&&&&&return&_color. &&
&&&&&&&&} && &&&&};&&
import&java.util.AbstractL&import&java.util.L&public&class&Final&{&public&static&void&main(String[]&args)&{&for&(String&color&:&Color.color)&System.out.print(color&&#43;&&&&);&Color.color.set(3,&&white&);&}&}&class&Color&{&private&static&String[]&_color&=&{&&red&,&&blue&,&&yellow&,&&black&&};&public&static&List&String&&color&=&new&AbstractList&String&()&{&@Override&public&String&get(int&index)&{&return&_color[index];&}&@Override&public&String&set(int&index,&String&value)&{&throw&new&RuntimeException(&为了代码安全,不能修改数组&);&}&@Override&public&int&size()&{&return&_color.&}&};&&
import java.util.AbstractL import java.util.L public class Final { public static void main(String[] args) { for (String color : Color.color) System.out.print(color + & &); Color.color.set(3, &white&); } } class Color { private static String[] _color = { &red&, &blue&, &yellow&, &black& }; public static List&String& color = new AbstractList&String&() { @Override public String get(int index) { return _color[index]; } @Override public String set(int index, String value) { throw new RuntimeException(&为了代码安全,不能修改数组&); } @Override public int size() { return _color. } };
}这样就OK了,既保证了代码安全,又能让数组中的元素被访问了。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:349673次
积分:3963
积分:3963
排名:第7907名
原创:40篇
转载:269篇
评论:38条
(4)(2)(27)(1)(28)(247)Posts - 15,
Articles - 0,
Comments - 9
软件工程基础
23:28 by 大金箔, ... 阅读,
1.对软件工程M1/M2做一个总结
  在M1阶段,我在C705组。M1阶段我与黄漠源同学结对,一起完成提取关键词算法的优化。最初我们一起测试提取关键词算法功能的实现效果,随后我主要负责从网络上搜寻并整理提取关键词算法,黄漠源同学负责测试算法的BUG。随后我接受整合算法的工作,测试已有的整合算法,并尝试作出改进。
  在M2阶段,我在sixsix组。M2阶段我主要负责管理博客和TFS。我对TFS的功能有了许多认识,主要负责整理大家的工作情况,同时发布博客,说明大家的任务分配以及出现的种种情况。熟悉了TFS的使用。
      M2阶段中,我们团队的爬虫在中期过后的一段时间内出现了不少问题,我也参加了对爬虫改进的工作。在网上收集了有关反爬虫机制的信息,思考了一些应对反爬虫机智的方法。
2.对以前阅读产生问题的总结。
  在以前的阅读作业中产生的问题:
    关于产品规模与快速软件开发的疑惑:/jinbo123/p/4027618.html
    在M1/M2阶段中,时间总是比较紧张,平时有其他课业要求,同时还要完成软件工程的任务。时间的紧张导致了我们必须采取快速软件开发的策略。我们不得不牺牲一些本来决定的任务。比如在M2阶段中,我们因为没有考虑到各种外卖网站的反爬虫问题,导致网页爬取一直失败,过了一段时间才意识到是因为反爬虫机制。但是我们并没有有效的应对策略,最终只能放弃对具有反爬虫机制网站的爬取。在M1阶段中,我们也遇到了类似的问题。C705小队的代码源自于学长,而实际上学长的代码存在着众多的问题,尤其是学长留下的信息有着一定的欺骗性,有的功能实现非常差,在实际操作时产生了非常不好的效果,大大加大了我们的工作量。
    我的总结是,在快速软件开发中,我们本身要做的就是一些功能基础,竞争力一般的软件。重点不在于开发效果,而在于能够开发出来。因此,牺牲规模加快速度显然是必要的。
    关于最佳的项目一定是建立在最佳的软件工程基础上的:/jinbo123/p/4027712.html
    疑惑依然存在,或许是因为我读书读得少。平时或许应该多读读书。
    需求蔓延相关疑问:/jinbo123/p/4027786.html
    还是不知道需求蔓延是什么。。因M1阶段我们小队的应用用户量非常少,M2阶段我们的餐站用户量也很少。
    两个产品的分布程度和需求可靠性的关系的相关疑惑:/jinbo123/p/4027751.html
    关于分布程度与需求可靠性分布图的理解。这个图我大概是看懂了。因为不同应用本身需求的可靠性是不同的。比方说卡牌游戏,可靠性需求较少,但是分布广度很广。所以说,分布广度某种程度上影响了需求的可靠性,但并不能决定。
3.&新问题:
    软件工程基础是我们的课程名,但是用户量对软件开发也起到了很大的影响。
    对于M1/M2阶段,我们都有一个问题,就是用户量问题一直都没有得到解决。
    接下来问题就出现了。用户量不足的情况下,反馈的问题自然比较少。如何增加普通软件的用户量呢?
    我们在课堂上,通过有限的时间制作出来的应用自然比较平常,很难吸引用户,况且现在应用多如牛毛。用户量问题实在难以解决。
4.6个阶段学习到的知识点
需求:需求分析很重要,尤其是基础需求必须分析到位。
设计:设计要考虑界面以及用户使用效果。
实现:实现要迅速,同时要标准化实现,方便修改。
测试:测试时,要对全部代码进行测试。适合的可以在编写时测试单个模块的功能。
发布:发布时多平台发布,发布的平台多,用户量才会多。
维护:维护时尽量考虑全局,不要在解决旧问题的同时产生新问题。

我要回帖

更多关于 final cut pro x 的文章

 

随机推荐