V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
monster1priest
V2EX  ›  Java

Java 库为什么要这么写?

  •  
  •   monster1priest · 41 天前 via iPhone · 6030 次点击
    这是一个创建于 41 天前的主题,其中的信息可能已经有所发展或是发生改变。
    我一般都是
    int a = getNumber(); if(a >0)…….
    但是我看到源码写法很多是
    int a; if((a=getNumber()) >0)…….
    为什么会这么写?是有什么好处吗
    50 条回复    2021-10-27 20:03:38 +08:00
    bololobo
        1
    bololobo  
       41 天前
    能显得比较厉害。别人问我为什么要这样写,我就会说 你看源码也这样写
    monster1priest
        2
    monster1priest  
    OP
       41 天前 via iPhone
    @bololobo hashmap 就是这么写的。人家应该不是为了炫耀吧
    BBCCBB
        3
    BBCCBB  
       41 天前
    一般是
    int a;
    if (xxx && (a=getNumber() > 0)) 这样吧,
    不能只有一个条件也 f((a=getNumber()) >0)/
    cmdOptionKana
        4
    cmdOptionKana  
       41 天前   ❤️ 6
    有 C 语言背景可能会习惯这样写
    rannnn
        5
    rannnn  
       41 天前
    这么写一般是在 while 里吧
    zardmyLove
        6
    zardmyLove  
       41 天前
    我的想法是显得紧凑
    zjsxwc
        7
    zjsxwc  
       41 天前 via Android   ❤️ 3
    因为这个变量 a 只是单纯想在 if 条件中使用?
    而 if 的 condition 里不能 declare 变量 a 类型,不得不挪到外面。

    https://stackoverflow.com/questions/16148580/assign-variable-value-inside-if-statement
    zjsxwc
        8
    zjsxwc  
       41 天前 via Android
    感觉,莫名好笑😄
    546L5LiK6ZOt
        9
    546L5LiK6ZOt  
       41 天前   ❤️ 4
    我记得很多类库还有一个常见的写法,就是在一个方法里,把类的成员变量赋值给局部变量,方法里都使用这个局部变量,而不直接用成员变量。例如:
    class A {
    int a;

    void func() {
    int a = this.a;
    // ……
    }
    }

    后来看到一种解释,说访问方法里成员变量不利于运行时优化(至少局部变量肯定是线程安全的)。


    我记得 jdk 的类库里还经常看到这么种情况,明明这两个循环可以写在一起,合并成一个循环,但是却特地分开写。一种解释说,小的循环容易让 cpu 命中 cache ,执行更快。。。

    总之,类库总有些特殊的写法,跟写业务代码不一样
    mritd
        10
    mritd  
       41 天前 via iPhone
    @546L5LiK6ZOt #9 这种好像是为了满足那些 get set 规范
    Jooooooooo
        11
    Jooooooooo  
       41 天前
    这是习惯问题

    两种写法还能用性能差异那是 jvm 有毛病
    chendy
        12
    chendy  
       40 天前
    没啥好处,甚至不太好读
    是源码还是反编译?反编译的话经常看到一些奇怪的写法,但是都是编译器优化导致的
    rpish
        13
    rpish  
       40 天前
    @546L5LiK6ZOt
    @zjsxwc
    感谢两位老哥,涨姿势了👍
    yidinghe
        14
    yidinghe  
       40 天前 via Android
    不看上下文的话,每次都这么写是脱裤子放屁,但确实有合理使用的场景,一般都是在 IO 流读取时出现。
    Cbdy
        15
    Cbdy  
       40 天前
    IO 时会这样写,C 语言延续下来的传统,我记得 Unix 高程称赞过这种写法
    Leviathann
        16
    Leviathann  
       40 天前 via iPhone
    这就是 c 味
    反正我挺不喜欢的
    micean
        17
    micean  
       40 天前 via Android
    @zjsxwc 那不需要声明 a 变量
    aguesuka
        18
    aguesuka  
       40 天前
    就是 4 楼的说法, 请相信这种简单的优化, 编译器比人更聪明.
    bk201
        19
    bk201  
       40 天前
    语意应该浅显易懂,我觉得源码这处不行
    cubecube
        20
    cubecube  
       40 天前
    @aguesuka 然而有时候编译器并不会更聪明。
    javac 近些年几乎保持不变。很多 jdk 的代码的确有部分优化,至于这种方式是否有神秘性能加成,回头可以分析下。
    wolfie
        21
    wolfie  
       40 天前
    简洁啊。

    举例不对,这个例子没有任何必要。
    cpstar
        22
    cpstar  
       40 天前
    从编译出的 bytecode 上,这两个没差别,也无所谓性能调优。
    都是先 invoke 再 ifgt 。

    如果说性能调优,那就是 9#说的问题,局部变量,不需要每次都 invoke——当然编译器也可能自动识别并且加上一个局部变量压栈
    yinzhili
        23
    yinzhili  
       40 天前
    一些人原先习惯了其它的编程语言,写 Java 代码的时候就会带上之前的习惯,比如:喜欢用下划线开头的变量名
    ipwx
        24
    ipwx  
       40 天前
    if 这么写就是魔怔了。。。

    但是如果换成 while ,这么写就爽了。比如:

    int bytesRead;
    while ((bytesRead = read(fd)) > 0) {
    ...
    }

    对比如果不这么写:

    while (true) {
    int bytesRead = read(fd);
    if (bytesRead <= 0) {
    break;
    }
    ...
    }

    显然前者更简洁。
    skinny
        25
    skinny  
       40 天前
    可能没什么特殊用意,可能就是写这部分代码的人有一些在 C/C++的不良习惯而已,这写法不是特殊需要在 C/C++也不是推荐写法啊。后面更新维护的人也不会在没有明显问题时去动它,于是就保留了下来。JVM 优化不会这么挫的。说起库源代码违反最佳写法官方推荐写法的多了去了,比如 Python ,就很多问题,但是没人愿意去更新源代码。
    clf
        26
    clf  
       40 天前
    有没有可能第二种的 a 作用域只在 if 的括号里,所以这样写。
    b0644170fc
        27
    b0644170fc  
       40 天前
    我觉得 4 楼说的有理.
    cubecube
        28
    cubecube  
       40 天前   ❤️ 1
    @aguesuka 的确没有神秘加成, 可能就是程序员的个人习惯了

    JMHPerfCondition.conditionInvokeFirst avgt 3 13.930 ± 3.733 ns/op
    JMHPerfCondition.conditionInvokeLater avgt 3 13.742 ± 1.632 ns/op

    ```java
    @Benchmark
    public int conditionInvokeFirst() {
    int a = SpecialCaseTest.getNumber();
    if (a > 0) {
    return 1;
    } else {
    return 0;
    }

    }

    @Benchmark
    public int conditionInvokeLater() {
    int a;
    if ((a = SpecialCaseTest.getNumber()) > 0) {
    return 1;
    } else {
    return 0;
    }
    }
    ```
    MineDog
        29
    MineDog  
       40 天前
    @546L5LiK6ZOt #9 还有是访问成员变量需要的指令比局部变量多,在保证语义一致的情况下,指令更少,性能更好一点
    tobepro
        30
    tobepro  
       40 天前
    记得以前学嵌入式的时候,听韦东山老师说过,有部分大型开源项目的看着感觉很高大上的代码,其实没什么卵用,单纯就是写代码的人想炫技
    streamrx
        31
    streamrx  
       40 天前 via iPhone
    1
    yuchting
        32
    yuchting  
       40 天前
    其实更吊的写法应该是 if((var a = getNumber()) > 0) ...
    可惜没语言支持。
    oldshensheep
        33
    oldshensheep  
       40 天前
    @yuchting
    python 可以
    xiang0818
        34
    xiang0818  
       40 天前
    因为是上了年代的程序员写的~
    ColinZeb
        35
    ColinZeb  
       40 天前
    @yuchting c# 可以 ,还可以顺便判断类型或者是否为空。

    if( obj is int a )//判断是否 int 类型 如果是 int 赋值给 int a

    if(obj is {})//判断是否非空
    ipwx
        36
    ipwx  
       40 天前
    @yuchting python 3.9


    if (a := getNumber()) > 0:
    ....
    newmlp
        37
    newmlp  
       40 天前   ❤️ 1
    代码行数少,有限的空间内可以看到更多的逻辑,没其他原因
    zhgg0
        38
    zhgg0  
       40 天前
    @newmlp 就是 37 楼说的原因,纯粹就是因为代码行数少。仔细翻下 HashMap 的源码就能验证,我刚验证过。
    在 HashMap 源码里面,就拿楼主写的代码来作比方 int a = getNumber(); if(a >0);
    1 、如果 a 变量在别的地方早就定义过,那就会被写成 if((a=getNumber()) >0);
    2 、如果 a 变量在别的地方没定义过,需要定义,那就会被写成 int a = getNumber(); if(a >0);

    1 的情况放 if 里能节省一行,所以放 if 里了; 2 的情况不管是否放 if 里都不能节省一行,所以没放 if 里;仔细翻下源码就发现了。
    penguinWWY
        39
    penguinWWY  
       40 天前
    @cubecube 优化是后面 Hotspot 干的事情,javac 几乎不做优化
    geligaoli
        40
    geligaoli  
       40 天前
    @546L5LiK6ZOt 多线程中,有时这么写是为了避免锁,局部变量之后的操作,不用担心其他线程的影响。
    littlewing
        41
    littlewing  
       40 天前
    羡慕 Java ,能看懂 标准库的代码,最新学 C++,STL 不是人看的
    rrfeng
        42
    rrfeng  
       40 天前 via Android
    @yuchting
    你们最讨厌的 go 也支持
    iceheart
        43
    iceheart  
       40 天前 via Android
    C 代码转过来的吧,直接用了,省事
    eason1874
        44
    eason1874  
       40 天前
    @yuchting #32 PHP 很多就这么写的,不用提前声明变量 $a ,直接在条件判断里赋值运算,某些场景能省不少 if 嵌套

    if (isOk() && ($a = getNumber()) > 0 && $a < 10) echo $a;
    someonedeng
        45
    someonedeng  
       40 天前
    c 语言留下的习惯,个人感觉不好看
    fuchaofather
        46
    fuchaofather  
       39 天前
    c 味儿
    GiftedJarvis
        47
    GiftedJarvis  
       39 天前
    我也有这疑问, 不光 HashMap, 还有一堆 BlockQueue, AQS 里都这么写
    yolee599
        48
    yolee599  
       39 天前
    @skinny #25 对于 C 语言,这样写不是不良习惯,反而是好习惯,因为有的编译器不支持以下语法:

    for (int i = 0; i < 10; i++)
    {
    ...
    }

    要改成下面的写法才能编译通过:

    int i;
    for (i = 0; i < 10; i++)
    {
    ...
    }

    为了兼容所有的编译器所以使用第二种写法,还有的编译器定义变量的时候不能在函数中间定义,必须在函数最前面定义。
    yolee599
        49
    yolee599  
       39 天前
    @yolee599 #48 第一种写法是 C99 的语法,很多编译器是不支持 C99 语法的。有时候编译器支持 C99 也不会特意去开启 C99 ,都是为了兼容😂
    skinny
        50
    skinny  
       39 天前
    @yolee599 你没明白我的意思
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1177 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 18:05 · PVG 02:05 · LAX 10:05 · JFK 13:05
    ♥ Do have faith in what you're doing.