当前位置: 首页 > 知识库问答 >
问题:

在Java,可以

齐迪
2023-03-14

在此代码中:

if (value >= x && value <= y) {

当<代码>值时

具体来说,我正在考虑如何

但是当<代码>

因此,强制表达式不包含惰性成分肯定会更具确定性,并且不容易预测失败。正确

注意事项:

  • 显然,如果代码看起来像这样,我的问题的答案将是“否”:if(value

更新只是为了解释我为什么感兴趣:马丁·汤普森在他的机械同情博客上写了一些系统,在他来并做了一个关于Aeron的演讲之后,我一直在关注这些系统。其中一个关键信息是我们的硬件中包含了所有这些神奇的东西,而我们软件开发人员不幸未能利用它。别担心,我不会去

当然,@StephenC提出了一个奇妙的观点,即将代码弯曲成奇怪的形状会使JIT不那么容易发现常见的优化 - 如果不是现在,那么将来。上面提到的“非常著名的问题”是特殊的,因为它将预测复杂性推到了远远超出实际优化的范围。

我非常清楚,在大多数(或几乎所有)情况下,< code >

更新2:(解决问题过于宽泛的建议。我不想对这个问题做大的改动,因为它可能会影响下面一些高质量的答案!)也许需要一个野外的例子;这是从番石榴LongMath类(非常感谢@ maaartinus发现这一点):

public static boolean isPowerOfTwo(long x) {
    return x > 0 & (x & (x - 1)) == 0;
}

请先查看


共有3个答案

佟和平
2023-03-14

我将从另一个角度来看待这个问题。

考虑这两个代码片段,

  if (value >= x && value <= y) {

  if (value >= x & value <= y) {

如果我们假设 xy 具有基元类型,则这两个(部分)语句将为所有可能的输入值提供相同的结果。(如果涉及包装器类型,则它们不完全等效,因为 y 的隐式测试可能会在

如果即时编译器做得很好,它的优化器将能够推断出这两个语句做同样的事情:

>

  • 如果一个比另一个快,那么它应该能够使用更快的版本......在 JIT 编译的代码中。

    如果不是,那么在源代码级别使用哪个版本并不重要。

    由于即时编译器在编译之前收集路径统计信息,因此它可能拥有有关程序员(!)的执行特性的更多信息。

    如果当前一代JIT编译器(在任何给定的平台上)没有很好地优化来处理这个问题,那么下一代可能会做得很好......取决于经验证据是否表明这是一个值得优化的模式。

    事实上,如果您以一种为此进行优化的方式编写Java代码,那么通过选择更“晦涩”的代码版本,您可能会抑制当前或未来JIT编译器的优化能力。

    简而言之,我认为你不应该在源代码级别做这种微优化。而且如果你接受这个论点1,并遵循它的逻辑结论,哪个版本更快的问题是… moot2

    1 -我不认为这是一个证据。

    2-除非你是真正编写JIT编译器Java小社区之一…

    “非常著名的问题”在两个方面很有趣:

    >

  • 一方面,这是一个例子,其中产生影响所需的优化远远超出了即时编译器的能力。

    另一方面,对数组进行排序不一定是正确的事情......只是因为排序的数组可以更快地处理。对阵列进行排序的成本很可能(远)大于节省的成本。

  • 祖麻雀
    2023-03-14
    匿名用户

    对于这类问题,你应该运行一个微基准测试。我用JMH进行了这个测试。

    基准的执行情况如下

    // boolean logical AND
    bh.consume(value >= x & y <= value);
    

    // conditional AND
    bh.consume(value >= x && y <= value);
    

    // bitwise OR, as suggested by Joop Eggen
    bh.consume(((value - x) | (y - value)) >= 0)
    

    其中值为< code>value,x和y显示基准名称。

    吞吐量基准测试的结果(五次预热和十次测量迭代)是:

    Benchmark                                 Mode  Cnt    Score    Error   Units
    Benchmark.isBooleanANDBelowRange          thrpt   10  386.086 ▒ 17.383  ops/us
    Benchmark.isBooleanANDInRange             thrpt   10  387.240 ▒  7.657  ops/us
    Benchmark.isBooleanANDOverRange           thrpt   10  381.847 ▒ 15.295  ops/us
    Benchmark.isBitwiseORBelowRange           thrpt   10  384.877 ▒ 11.766  ops/us
    Benchmark.isBitwiseORInRange              thrpt   10  380.743 ▒ 15.042  ops/us
    Benchmark.isBitwiseOROverRange            thrpt   10  383.524 ▒ 16.911  ops/us
    Benchmark.isConditionalANDBelowRange      thrpt   10  385.190 ▒ 19.600  ops/us
    Benchmark.isConditionalANDInRange         thrpt   10  384.094 ▒ 15.417  ops/us
    Benchmark.isConditionalANDOverRange       thrpt   10  380.913 ▒  5.537  ops/us
    

    对于评估本身,结果并没有太大的不同。只要在那段代码上没有发现性能影响,我就不会尝试优化它。根据代码中的位置,热点编译器可能会决定进行一些优化。上述基准可能没有涵盖。

    一些参考资料:

    布尔逻辑 AND - 如果两个操作数值都为真,则结果值为 true;否则,结果是
    条件和 - 就像

    商高谊
    2023-03-14
    匿名用户

    好的,所以你想知道它在较低级别的行为…那么让我们看看字节码!

    编辑:在最后添加了为AMD64生成的汇编代码。看看一些有趣的笔记。
    编辑2(重新:OP的“更新2”):为番石榴的是PowerOfTwo方法添加了asm代码。

    我写了这两个快速方法:

    public boolean AndSC(int x, int value, int y) {
        return value >= x && value <= y;
    }
    
    public boolean AndNonSC(int x, int value, int y) {
        return value >= x & value <= y;
    }
    

    如您所见,除了AND运算符的类型之外,它们完全相同。

    这是生成的字节码:

      public AndSC(III)Z
       L0
        LINENUMBER 8 L0
        ILOAD 2
        ILOAD 1
        IF_ICMPLT L1
        ILOAD 2
        ILOAD 3
        IF_ICMPGT L1
       L2
        LINENUMBER 9 L2
        ICONST_1
        IRETURN
       L1
        LINENUMBER 11 L1
       FRAME SAME
        ICONST_0
        IRETURN
       L3
        LOCALVARIABLE this Ltest/lsoto/AndTest; L0 L3 0
        LOCALVARIABLE x I L0 L3 1
        LOCALVARIABLE value I L0 L3 2
        LOCALVARIABLE y I L0 L3 3
        MAXSTACK = 2
        MAXLOCALS = 4
    
      // access flags 0x1
      public AndNonSC(III)Z
       L0
        LINENUMBER 15 L0
        ILOAD 2
        ILOAD 1
        IF_ICMPLT L1
        ICONST_1
        GOTO L2
       L1
       FRAME SAME
        ICONST_0
       L2
       FRAME SAME1 I
        ILOAD 2
        ILOAD 3
        IF_ICMPGT L3
        ICONST_1
        GOTO L4
       L3
       FRAME SAME1 I
        ICONST_0
       L4
       FRAME FULL [test/lsoto/AndTest I I I] [I I]
        IAND
        IFEQ L5
       L6
        LINENUMBER 16 L6
        ICONST_1
        IRETURN
       L5
        LINENUMBER 18 L5
       FRAME SAME
        ICONST_0
        IRETURN
       L7
        LOCALVARIABLE this Ltest/lsoto/AndTest; L0 L7 0
        LOCALVARIABLE x I L0 L7 1
        LOCALVARIABLE value I L0 L7 2
        LOCALVARIABLE y I L0 L7 3
        MAXSTACK = 3
        MAXLOCALS = 4
    

    AndSC

      < li >它将< code>value和< code>x加载到堆栈上,如果< code>value较低,则跳转到L1。否则它继续运行下一行。 < li >它将< code>value和< code>y加载到堆栈上,如果< code>value较大,也跳转到L1。否则它继续运行下一行。 < li >如果两次跳转都没有成功,则返回< code > true 。 < li >然后是标记为L1的行,这是一个< code >返回false。

    < code>AndNonSC (

      < li >它将< code>value和< code>x加载到堆栈上,如果< code>value较低,则跳转到L1。因为现在它需要保存结果,以便与AND的另一部分进行比较,所以它必须执行“save true”或“save false”,它不能用同一条指令同时执行这两个操作。 < li >它将< code>value和< code>y加载到堆栈上,如果< code>value较大,则跳转到L1。它再次需要保存< code>true或< code>false,这是两个不同的行,取决于比较结果。 < li >现在两个比较都完成了,代码实际上执行AND操作——如果两个都为真,它跳转(第三次)返回true;否则继续执行下一行,返回false。

    虽然我对Java字节码不是很有经验,而且我可能忽略了一些东西,但在我看来,

    正如其他人所建议的那样,重写代码,用算术运算代替比较,可能是实现<代码>的一种方法

    正如注释中所提到的,相同的Java字节码可以在不同的系统中产生不同的机器码,所以尽管Java字节码可能会提示我们哪个版本的性能更好,但获得编译器生成的实际ASM是真正找出答案的唯一方法。< br >我打印了这两种方法的AMD64 ASM说明;下面是相关的线(剥离的入口点等。).

    注:除非另有说明,所有方法都是用java 1.8.0_91编译的。

    具有默认选项的方法和SC

      # {method} {0x0000000016da0810} 'AndSC' '(III)Z' in 'AndTest'
      ...
      0x0000000002923e3e: cmp    %r8d,%r9d
      0x0000000002923e41: movabs $0x16da0a08,%rax   ;   {metadata(method data for {method} {0x0000000016da0810} 'AndSC' '(III)Z' in 'AndTest')}
      0x0000000002923e4b: movabs $0x108,%rsi
      0x0000000002923e55: jl     0x0000000002923e65
      0x0000000002923e5b: movabs $0x118,%rsi
      0x0000000002923e65: mov    (%rax,%rsi,1),%rbx
      0x0000000002923e69: lea    0x1(%rbx),%rbx
      0x0000000002923e6d: mov    %rbx,(%rax,%rsi,1)
      0x0000000002923e71: jl     0x0000000002923eb0  ;*if_icmplt
                                                    ; - AndTest::AndSC@2 (line 22)
    
      0x0000000002923e77: cmp    %edi,%r9d
      0x0000000002923e7a: movabs $0x16da0a08,%rax   ;   {metadata(method data for {method} {0x0000000016da0810} 'AndSC' '(III)Z' in 'AndTest')}
      0x0000000002923e84: movabs $0x128,%rsi
      0x0000000002923e8e: jg     0x0000000002923e9e
      0x0000000002923e94: movabs $0x138,%rsi
      0x0000000002923e9e: mov    (%rax,%rsi,1),%rdi
      0x0000000002923ea2: lea    0x1(%rdi),%rdi
      0x0000000002923ea6: mov    %rdi,(%rax,%rsi,1)
      0x0000000002923eaa: jle    0x0000000002923ec1  ;*if_icmpgt
                                                    ; - AndTest::AndSC@7 (line 22)
    
      0x0000000002923eb0: mov    $0x0,%eax
      0x0000000002923eb5: add    $0x30,%rsp
      0x0000000002923eb9: pop    %rbp
      0x0000000002923eba: test   %eax,-0x1c73dc0(%rip)        # 0x0000000000cb0100
                                                    ;   {poll_return}
      0x0000000002923ec0: retq                      ;*ireturn
                                                    ; - AndTest::AndSC@13 (line 25)
    
      0x0000000002923ec1: mov    $0x1,%eax
      0x0000000002923ec6: add    $0x30,%rsp
      0x0000000002923eca: pop    %rbp
      0x0000000002923ecb: test   %eax,-0x1c73dd1(%rip)        # 0x0000000000cb0100
                                                    ;   {poll_return}
      0x0000000002923ed1: retq   
    

    带有-XX的方法AndSC:PrintAssemblyOptions=intel选项

      # {method} {0x00000000170a0810} 'AndSC' '(III)Z' in 'AndTest'
      ...
      0x0000000002c26e2c: cmp    r9d,r8d
      0x0000000002c26e2f: jl     0x0000000002c26e36  ;*if_icmplt
      0x0000000002c26e31: cmp    r9d,edi
      0x0000000002c26e34: jle    0x0000000002c26e44  ;*iconst_0
      0x0000000002c26e36: xor    eax,eax            ;*synchronization entry
      0x0000000002c26e38: add    rsp,0x10
      0x0000000002c26e3c: pop    rbp
      0x0000000002c26e3d: test   DWORD PTR [rip+0xffffffffffce91bd],eax        # 0x0000000002910000
      0x0000000002c26e43: ret    
      0x0000000002c26e44: mov    eax,0x1
      0x0000000002c26e49: jmp    0x0000000002c26e38
    

    带有默认选项的方法AndNonSC

      # {method} {0x0000000016da0908} 'AndNonSC' '(III)Z' in 'AndTest'
      ...
      0x0000000002923a78: cmp    %r8d,%r9d
      0x0000000002923a7b: mov    $0x0,%eax
      0x0000000002923a80: jl     0x0000000002923a8b
      0x0000000002923a86: mov    $0x1,%eax
      0x0000000002923a8b: cmp    %edi,%r9d
      0x0000000002923a8e: mov    $0x0,%esi
      0x0000000002923a93: jg     0x0000000002923a9e
      0x0000000002923a99: mov    $0x1,%esi
      0x0000000002923a9e: and    %rsi,%rax
      0x0000000002923aa1: cmp    $0x0,%eax
      0x0000000002923aa4: je     0x0000000002923abb  ;*ifeq
                                                    ; - AndTest::AndNonSC@21 (line 29)
    
      0x0000000002923aaa: mov    $0x1,%eax
      0x0000000002923aaf: add    $0x30,%rsp
      0x0000000002923ab3: pop    %rbp
      0x0000000002923ab4: test   %eax,-0x1c739ba(%rip)        # 0x0000000000cb0100
                                                    ;   {poll_return}
      0x0000000002923aba: retq                      ;*ireturn
                                                    ; - AndTest::AndNonSC@25 (line 30)
    
      0x0000000002923abb: mov    $0x0,%eax
      0x0000000002923ac0: add    $0x30,%rsp
      0x0000000002923ac4: pop    %rbp
      0x0000000002923ac5: test   %eax,-0x1c739cb(%rip)        # 0x0000000000cb0100
                                                    ;   {poll_return}
      0x0000000002923acb: retq   
    

    方法和NonSC-XX:打印组件选项=英特尔选项

      # {method} {0x00000000170a0908} 'AndNonSC' '(III)Z' in 'AndTest'
      ...
      0x0000000002c270b5: cmp    r9d,r8d
      0x0000000002c270b8: jl     0x0000000002c270df  ;*if_icmplt
      0x0000000002c270ba: mov    r8d,0x1            ;*iload_2
      0x0000000002c270c0: cmp    r9d,edi
      0x0000000002c270c3: cmovg  r11d,r10d
      0x0000000002c270c7: and    r8d,r11d
      0x0000000002c270ca: test   r8d,r8d
      0x0000000002c270cd: setne  al
      0x0000000002c270d0: movzx  eax,al
      0x0000000002c270d3: add    rsp,0x10
      0x0000000002c270d7: pop    rbp
      0x0000000002c270d8: test   DWORD PTR [rip+0xffffffffffce8f22],eax        # 0x0000000002910000
      0x0000000002c270de: ret    
      0x0000000002c270df: xor    r8d,r8d
      0x0000000002c270e2: jmp    0x0000000002c270c0
    
    • 首先,生成的ASM代码因我们是否选择默认AT而异
    • AndSC的ASM代码较短,只有2个条件跳转(不包括末尾的非条件jmp.)。实际上,它只是两个CMP、两个JL/E和一个XOR/MOV,具体取决于结果
    • AndNonSC的ASM代码现在比AndSC长!然而,它只有一个条件跳转(用于第一次比较),使用寄存器直接比较第一个结果和第二个结果,而不再跳转
    • 在 AMD64 机器语言级别,

    正如我在一些评论中所说的,这在不同的系统之间会有很大的不同,所以如果我们谈论分支预测优化,唯一真实的答案是:它取决于你的JVM实现、你的编译器、你的CPU和你的输入数据。

    在这里,Guava的开发人员提出了一种简洁的方法来计算给定数字是否为2的幂:

    public static boolean isPowerOfTwo(long x) {
        return x > 0 & (x & (x - 1)) == 0;
    }
    

    报价操作:

    这是使用<代码>吗

    为了找出答案,我在测试类中添加了两个类似的方法:

    public boolean isPowerOfTwoAND(long x) {
        return x > 0 & (x & (x - 1)) == 0;
    }
    
    public boolean isPowerOfTwoANDAND(long x) {
        return x > 0 && (x & (x - 1)) == 0;
    }
    

    英特尔的番石榴版本的 ASM 代码

      # {method} {0x0000000017580af0} 'isPowerOfTwoAND' '(J)Z' in 'AndTest'
      # this:     rdx:rdx   = 'AndTest'
      # parm0:    r8:r8     = long
      ...
      0x0000000003103bbe: movabs rax,0x0
      0x0000000003103bc8: cmp    rax,r8
      0x0000000003103bcb: movabs rax,0x175811f0     ;   {metadata(method data for {method} {0x0000000017580af0} 'isPowerOfTwoAND' '(J)Z' in 'AndTest')}
      0x0000000003103bd5: movabs rsi,0x108
      0x0000000003103bdf: jge    0x0000000003103bef
      0x0000000003103be5: movabs rsi,0x118
      0x0000000003103bef: mov    rdi,QWORD PTR [rax+rsi*1]
      0x0000000003103bf3: lea    rdi,[rdi+0x1]
      0x0000000003103bf7: mov    QWORD PTR [rax+rsi*1],rdi
      0x0000000003103bfb: jge    0x0000000003103c1b  ;*lcmp
      0x0000000003103c01: movabs rax,0x175811f0     ;   {metadata(method data for {method} {0x0000000017580af0} 'isPowerOfTwoAND' '(J)Z' in 'AndTest')}
      0x0000000003103c0b: inc    DWORD PTR [rax+0x128]
      0x0000000003103c11: mov    eax,0x1
      0x0000000003103c16: jmp    0x0000000003103c20  ;*goto
      0x0000000003103c1b: mov    eax,0x0            ;*lload_1
      0x0000000003103c20: mov    rsi,r8
      0x0000000003103c23: movabs r10,0x1
      0x0000000003103c2d: sub    rsi,r10
      0x0000000003103c30: and    rsi,r8
      0x0000000003103c33: movabs rdi,0x0
      0x0000000003103c3d: cmp    rsi,rdi
      0x0000000003103c40: movabs rsi,0x175811f0     ;   {metadata(method data for {method} {0x0000000017580af0} 'isPowerOfTwoAND' '(J)Z' in 'AndTest')}
      0x0000000003103c4a: movabs rdi,0x140
      0x0000000003103c54: jne    0x0000000003103c64
      0x0000000003103c5a: movabs rdi,0x150
      0x0000000003103c64: mov    rbx,QWORD PTR [rsi+rdi*1]
      0x0000000003103c68: lea    rbx,[rbx+0x1]
      0x0000000003103c6c: mov    QWORD PTR [rsi+rdi*1],rbx
      0x0000000003103c70: jne    0x0000000003103c90  ;*lcmp
      0x0000000003103c76: movabs rsi,0x175811f0     ;   {metadata(method data for {method} {0x0000000017580af0} 'isPowerOfTwoAND' '(J)Z' in 'AndTest')}
      0x0000000003103c80: inc    DWORD PTR [rsi+0x160]
      0x0000000003103c86: mov    esi,0x1
      0x0000000003103c8b: jmp    0x0000000003103c95  ;*goto
      0x0000000003103c90: mov    esi,0x0            ;*iand
      0x0000000003103c95: and    rsi,rax
      0x0000000003103c98: and    esi,0x1
      0x0000000003103c9b: mov    rax,rsi
      0x0000000003103c9e: add    rsp,0x50
      0x0000000003103ca2: pop    rbp
      0x0000000003103ca3: test   DWORD PTR [rip+0xfffffffffe44c457],eax        # 0x0000000001550100
      0x0000000003103ca9: ret    
    

    英特尔的asm代码

      # {method} {0x0000000017580bd0} 'isPowerOfTwoANDAND' '(J)Z' in 'AndTest'
      # this:     rdx:rdx   = 'AndTest'
      # parm0:    r8:r8     = long
      ...
      0x0000000003103438: movabs rax,0x0
      0x0000000003103442: cmp    rax,r8
      0x0000000003103445: jge    0x0000000003103471  ;*lcmp
      0x000000000310344b: mov    rax,r8
      0x000000000310344e: movabs r10,0x1
      0x0000000003103458: sub    rax,r10
      0x000000000310345b: and    rax,r8
      0x000000000310345e: movabs rsi,0x0
      0x0000000003103468: cmp    rax,rsi
      0x000000000310346b: je     0x000000000310347b  ;*lcmp
      0x0000000003103471: mov    eax,0x0
      0x0000000003103476: jmp    0x0000000003103480  ;*ireturn
      0x000000000310347b: mov    eax,0x1            ;*goto
      0x0000000003103480: and    eax,0x1
      0x0000000003103483: add    rsp,0x40
      0x0000000003103487: pop    rbp
      0x0000000003103488: test   DWORD PTR [rip+0xfffffffffe44cc72],eax        # 0x0000000001550100
      0x000000000310348e: ret    
    

    在这个特定的示例中,JIT编译器为生成的汇编代码少得多

    所以一切都指向番石榴的<代码>

    ……还是?

    如前所述,我用Java 8运行上面的例子:

    C:\....>java -version
    java version "1.8.0_91"
    Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
    Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)
    

    但是如果换成Java 7呢?

    C:\....>c:\jdk1.7.0_79\bin\java -version
    java version "1.7.0_79"
    Java(TM) SE Runtime Environment (build 1.7.0_79-b15)
    Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode)
    C:\....>c:\jdk1.7.0_79\bin\java -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*AndTest.isPowerOfTwoAND -XX:PrintAssemblyOptions=intel AndTestMain
      .....
      0x0000000002512bac: xor    r10d,r10d
      0x0000000002512baf: mov    r11d,0x1
      0x0000000002512bb5: test   r8,r8
      0x0000000002512bb8: jle    0x0000000002512bde  ;*ifle
      0x0000000002512bba: mov    eax,0x1            ;*lload_1
      0x0000000002512bbf: mov    r9,r8
      0x0000000002512bc2: dec    r9
      0x0000000002512bc5: and    r9,r8
      0x0000000002512bc8: test   r9,r9
      0x0000000002512bcb: cmovne r11d,r10d
      0x0000000002512bcf: and    eax,r11d           ;*iand
      0x0000000002512bd2: add    rsp,0x10
      0x0000000002512bd6: pop    rbp
      0x0000000002512bd7: test   DWORD PTR [rip+0xffffffffffc0d423],eax        # 0x0000000002120000
      0x0000000002512bdd: ret    
      0x0000000002512bde: xor    eax,eax
      0x0000000002512be0: jmp    0x0000000002512bbf
      .....
    

    惊喜!为 生成的程序集代码

    所以回到OP的最新问题:

    这是使用<代码>吗

    而IMHO的答案是一样的,即使是为了这个(非常!)具体场景:取决于你的JVM实现,你的编译器,你的CPU和你的输入数据。

     类似资料:
    • 问题内容: 在有效Java中,Bloch建议使所有字段最终成为使对象不变的对象。 是否有必要这样做?不只是不提供访问器方法就使其不可变。 例如 上述类是不可变的,即使我不申报的吗?我想念什么吗? 问题答案: 除了@Bozho的要点之外,将字段声明为意味着可以安全地访问它而无需任何同步。 相反,如果该字段不是,则存在另一个风险,即如果另一个线程在没有适当同步的情况下访问该字段,则另一个线程将看到该字

    • 在Java中可以吗? 例如: 我的目标是编写一个返回的函数。所述存储在节点的高度中,如果节点不存在,它将为空,我需要检查一下。 我这样做是为了做家庭作业,但这一特定部分并不是家庭作业的一部分,它只是帮助我完成我正在做的事情。 谢谢你的评论,但似乎很少有人真正读过代码下的内容,我在问我还能如何实现这个目标;很容易发现它不起作用。

    • Windows缩放弄乱了我的JavaFX应用程序上的UI。在java中有什么方法可以让windows知道这个特定的应用程序不应该缩放吗?谢谢

    • 问题内容: 我希望对所有网址应用一种具体的过滤器,但一种具体的除外(即除外)。 有可能这样做吗? 样例代码: 问题答案: 标准Servlet API不支持此功能。你可能想要为此使用改写URL过滤器(例如Tuckey的过滤器)(与Apache HTTPD的过滤器非常相似),或者侦听的方法中添加一个检查。 如有必要,可以将要忽略的路径指定为init-param过滤器的,以便web.xml无论如何都可以

    • 问题内容: 是否可以在Gradle中声明一个可在Java中使用的变量?基本上,我想在build.gradle中声明一些var,然后在构建时(显然)获取它。就像C / C ++中的预处理器宏一样… 一个声明的例子就是这样: 有没有办法做这样的事情? 问题答案: 生成Java常量 你可以使用 产生Android资源 你可以使用或以通常的方式访问它们

    • 问题内容: 是否可以在Java枚举中使用,还是需要使用?在我的测试中,它始终有效,但是我不确定是否可以保证。特别是,在枚举上没有方法,因此我不知道是否有可能获得一个枚举,该枚举将返回不同于的值。 例如,这样可以吗: 还是我需要这样写: 问题答案: 仅需2美分:这是Sun发布的Enum.java的代码,并且是JDK的一部分: