请不要再说Java中final方法比非final性能更好了

Java中的final变量 final修饰变量 final修饰方法 final修饰类 final和String的关系 在开发的过程中,经常会使用String类来处理字符串相关的数据,但今天去看String源代码时,发现这个类是用final修饰的,于是,决定去探究inal关键字。final关键字可以修改类,方法和属性,但这个关键字的作用是什么?文章可以带你了解。... 阅读详情

无继承

有 static 修饰

static final

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 生成随机数字和字母,
    public static final String getStringRandomFinal(int length) {
        String val = "";
        Random random = new Random();
        // 参数length,表示生成几位随机数
        for (int i = 0; i < length; i++) {
            String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";
            // 输出字母还是数字
            if ("char".equalsIgnoreCase(charOrNum)) {
                // 输出是大写字母还是小写字母
                // int temp = random.nextInt(2) % 2 == 0 ? 65 : 97;
                val += (char) (random.nextInt(26) + 97);
            } else if ("num".equalsIgnoreCase(charOrNum)) {
                val += String.valueOf(random.nextInt(10));
            }
        }
        return val;
    }

static 非 final

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 生成随机数字和字母,
    public static String getStringRandom(int length) {
        String val = "";
        Random random = new Random();
        // 参数length,表示生成几位随机数
        for (int i = 0; i < length; i++) {
            String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";
            // 输出字母还是数字
            if ("char".equalsIgnoreCase(charOrNum)) {
                // 输出是大写字母还是小写字母
                // int temp = random.nextInt(2) % 2 == 0 ? 65 : 97;
                val += (char) (random.nextInt(26) + 97);
            } else if ("num".equalsIgnoreCase(charOrNum)) {
                val += String.valueOf(random.nextInt(10));
            }
        }
        return val;
    }

结果

这里使用了 OpenJDK 的 JMH 基准测试工具来测试的,结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# JMH 1.4.1 (released 903 days ago, please consider updating!)
# VM invoker: /srv/jdk1.8.0_92/jre/bin/java
# VM options: <none>
# Warmup: 20 iterations, 1 s each
# Measurement: 20 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.agoncal.sample.jmh.Main.benchmark
中间忽略了预热及测试过程,这里只显示结果
Result: 206924.113 ±(99.9%) 7746.446 ops/s [Average]
  Statistics: (min, avg, max) = (132107.466, 206924.113, 267265.397), stdev = 32798.937
  Confidence interval (99.9%): [199177.667, 214670.559]
# JMH 1.4.1 (released 903 days ago, please consider updating!)
# VM invoker: /srv/jdk1.8.0_92/jre/bin/java
# VM options: <none>
# Warmup: 20 iterations, 1 s each
# Measurement: 20 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.agoncal.sample.jmh.Main.benchmarkFinal
中间忽略了预热及测试过程,这里只显示结果
Result: 210111.568 ±(99.9%) 8486.176 ops/s [Average]
  Statistics: (min, avg, max) = (133813.368, 210111.568, 267525.228), stdev = 35931.001
  Confidence interval (99.9%): [201625.392, 218597.744]
# Run complete. Total time: 00:13:54
Benchmark                       Mode  Samples       Score      Error  Units
o.a.s.j.Main.benchmark         thrpt      200  206924.113 ± 7746.446  ops/s
o.a.s.j.Main.benchmarkFinal    thrpt      200  210111.568 ± 8486.176  ops/s

总结:你说final的性能比非final有没有提升呢?可以说有,但几乎可以忽略不计。如果单纯地追求性能,而将所有的方法修改为 final 的话,我认为这样子是不可取的。而且这性能的差别,远远也没有网上有些人说的提升 50% 这么恐怖(有可能他们使用的是10年前的JVM来测试的吧^_^,比如 《35+ 个 Java 代码性能优化总结》这篇文章。雷总:不服?咱们来跑个分!)

分析

字节码级别的差别

StringKit.java StringKitFinal.java

它们在字节码上的差别:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
[18:52:08] emacsist:target $ diff /tmp/stringkit.log /tmp/stringkit-final.log
1,5c1,5
< Classfile /Users/emacsist/Documents/idea/logging/target/classes/org/agoncal/sample/jmh/StringKit.class
<   Last modified 2017-6-15; size 1098 bytes
<   MD5 checksum fe1ccdde26107e4037afc54c780f2c95
<   Compiled from "StringKit.java"
< public class org.agoncal.sample.jmh.StringKit
---
> Classfile /Users/emacsist/Documents/idea/logging/target/classes/org/agoncal/sample/jmh/StringKitFinal.class
>   Last modified 2017-6-15; size 1118 bytes
>   MD5 checksum 410f8bf0eb723b794e4754c6eb8b9829
>   Compiled from "StringKitFinal.java"
> public class org.agoncal.sample.jmh.StringKitFinal
24c24
<   #15 = Class              #52            // org/agoncal/sample/jmh/StringKit
---
>   #15 = Class              #52            // org/agoncal/sample/jmh/StringKitFinal
32,33c32,33
<   #23 = Utf8               Lorg/agoncal/sample/jmh/StringKit;
<   #24 = Utf8               getStringRandom
---
>   #23 = Utf8               Lorg/agoncal/sample/jmh/StringKitFinal;
>   #24 = Utf8               getStringRandomFinal
47c47
<   #38 = Utf8               StringKit.java
---
>   #38 = Utf8               StringKitFinal.java
61c61
<   #52 = Utf8               org/agoncal/sample/jmh/StringKit
---
>   #52 = Utf8               org/agoncal/sample/jmh/StringKitFinal
75c75
<   public org.agoncal.sample.jmh.StringKit();
---
>   public org.agoncal.sample.jmh.StringKitFinal();
87c87
<             0       5     0  this   Lorg/agoncal/sample/jmh/StringKit;
---
>             0       5     0  this   Lorg/agoncal/sample/jmh/StringKitFinal;
89c89
<   public static java.lang.String getStringRandom(int);
---
>   public static final java.lang.String getStringRandomFinal(int);
91c91
<     flags: ACC_PUBLIC, ACC_STATIC
---
>     flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
187c187
< SourceFile: "StringKit.java"
---
> SourceFile: "StringKitFinal.java"

可以看到除了方法名和方法修饰符不同之外,其他的没有什么区别了。

在调用者上面的字节码差别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
  public void benchmark();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: bipush        32
         2: invokestatic  #2                  // Method org/agoncal/sample/jmh/StringKit.getStringRandom:(I)Ljava/lang/String;
         5: pop
         6: return
      LineNumberTable:
        line 21: 0
        line 22: 6
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       7     0  this   Lorg/agoncal/sample/jmh/Main;
    RuntimeVisibleAnnotations:
      0: #26()
  public void benchmarkFinal();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: bipush        32
         2: invokestatic  #3                  // Method org/agoncal/sample/jmh/StringKitFinal.getStringRandomFinal:(I)Ljava/lang/String;
         5: pop
         6: return
      LineNumberTable:
        line 26: 0
        line 27: 6
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       7     0  this   Lorg/agoncal/sample/jmh/Main;
    RuntimeVisibleAnnotations:
      0: #26()

可以看到,它们在调用者上面的字节码也没有什么区别,只是方法名不一样之外。

对于 JVM 来说,它是只认字节码的,既然字节码除了方法名和修饰符一样,其他都一样,那就可以大概推测它们的性能几乎可以忽略不计了。因为调用 static final 和 static 非 final 的JVM指令是一样。

无 static 修饰

方法体是一样的,只是将它们删除了 static 的修饰。

结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# JMH version: 1.19
# VM version: JDK 1.8.0_92, VM 25.92-b14
# VM invoker: /srv/jdk1.8.0_92/jre/bin/java
# VM options: <none>
# Warmup: 20 iterations, 1 s each
# Measurement: 20 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.agoncal.sample.jmh.Main.benchmark
中间忽略了预热及测试过程,这里只显示结果
Result "org.agoncal.sample.jmh.Main.benchmark":
  201306.770 ±(99.9%) 8184.423 ops/s [Average]
  (min, avg, max) = (131889.934, 201306.770, 259928.172), stdev = 34653.361
  CI (99.9%): [193122.347, 209491.193] (assumes normal distribution)
# JMH version: 1.19
# VM version: JDK 1.8.0_92, VM 25.92-b14
# VM invoker: /srv/jdk1.8.0_92/jre/bin/java
# VM options: <none>
# Warmup: 20 iterations, 1 s each
# Measurement: 20 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.agoncal.sample.jmh.Main.benchmarkFinal
中间忽略了预热及测试过程,这里只显示结果
Result "org.agoncal.sample.jmh.Main.benchmarkFinal":
  196871.022 ±(99.9%) 8595.719 ops/s [Average]
  (min, avg, max) = (131182.268, 196871.022, 265522.769), stdev = 36394.814
  CI (99.9%): [188275.302, 205466.741] (assumes normal distribution)
# Run complete. Total time: 00:13:35
Benchmark             Mode  Cnt       Score      Error  Units
Main.benchmark       thrpt  200  201306.770 ± 8184.423  ops/s
Main.benchmarkFinal  thrpt  200  196871.022 ± 8595.719  ops/s

分析

字节码级别的差别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
[19:20:17] emacsist:target $ diff /tmp/stringkit.log /tmp/stringkit-final.log
1,5c1,5
< Classfile /Users/emacsist/Documents/idea/logging/target/classes/org/agoncal/sample/jmh/StringKit.class
<   Last modified 2017-6-15; size 1110 bytes
<   MD5 checksum f61144e86f7c17dc5d5f2b2d35fac36d
<   Compiled from "StringKit.java"
< public class org.agoncal.sample.jmh.StringKit
---
> Classfile /Users/emacsist/Documents/idea/logging/target/classes/org/agoncal/sample/jmh/StringKitFinal.class
>   Last modified 2017-6-15; size 1130 bytes
>   MD5 checksum 15ce17ee17fdb5f4721f0921977b1e69
>   Compiled from "StringKitFinal.java"
> public class org.agoncal.sample.jmh.StringKitFinal
24c24
<   #15 = Class              #52            // org/agoncal/sample/jmh/StringKit
---
>   #15 = Class              #52            // org/agoncal/sample/jmh/StringKitFinal
32,33c32,33
<   #23 = Utf8               Lorg/agoncal/sample/jmh/StringKit;
<   #24 = Utf8               getStringRandom
---
>   #23 = Utf8               Lorg/agoncal/sample/jmh/StringKitFinal;
>   #24 = Utf8               getStringRandomFinal
47c47
<   #38 = Utf8               StringKit.java
---
>   #38 = Utf8               StringKitFinal.java
61c61
<   #52 = Utf8               org/agoncal/sample/jmh/StringKit
---
>   #52 = Utf8               org/agoncal/sample/jmh/StringKitFinal
75c75
<   public org.agoncal.sample.jmh.StringKit();
---
>   public org.agoncal.sample.jmh.StringKitFinal();
87c87
<             0       5     0  this   Lorg/agoncal/sample/jmh/StringKit;
---
>             0       5     0  this   Lorg/agoncal/sample/jmh/StringKitFinal;
89c89
<   public java.lang.String getStringRandom(int);
---
>   public final java.lang.String getStringRandomFinal(int);
91c91
<     flags: ACC_PUBLIC
---
>     flags: ACC_PUBLIC, ACC_FINAL
169c169
<             0     125     0  this   Lorg/agoncal/sample/jmh/StringKit;
---
>             0     125     0  this   Lorg/agoncal/sample/jmh/StringKitFinal;
188c188
< SourceFile: "StringKit.java"
---
> SourceFile: "StringKitFinal.java"

可以看到,字节码上除了名字和 final 修饰符差别外,其余的是一样的。

在调用者上面的字节码差别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
  public void benchmark();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: new           #2                  // class org/agoncal/sample/jmh/StringKit
         3: dup
         4: invokespecial #3                  // Method org/agoncal/sample/jmh/StringKit."<init>":()V
         7: bipush        32
         9: invokevirtual #4                  // Method org/agoncal/sample/jmh/StringKit.getStringRandom:(I)Ljava/lang/String;
        12: pop
        13: return
      LineNumberTable:
        line 21: 0
        line 22: 13
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      14     0  this   Lorg/agoncal/sample/jmh/Main;
    RuntimeVisibleAnnotations:
      0: #30()
  public void benchmarkFinal();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: new           #5                  // class org/agoncal/sample/jmh/StringKitFinal
         3: dup
         4: invokespecial #6                  // Method org/agoncal/sample/jmh/StringKitFinal."<init>":()V
         7: bipush        32
         9: invokevirtual #7                  // Method org/agoncal/sample/jmh/StringKitFinal.getStringRandomFinal:(I)Ljava/lang/String;
        12: pop
        13: return
      LineNumberTable:
        line 26: 0
        line 27: 13
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      14     0  this   Lorg/agoncal/sample/jmh/Main;
    RuntimeVisibleAnnotations:
      0: #30()

可以看到,它们除了名字不同之外,其他的JVM指令都是一样的。

总结

对于是否有 final 修饰的方法,对性能的影响可以忽略不计。因为它们生成的字节码除了 flags 标志位是否有 final 修饰不同之外,其他所有的JVM指令,都是一样的(对于方法本身,以及调用者本身的字节码都一样)。对于JVM来说,它执行的就是字节码,如果字节码都一样的话,那对于JVM来说,它就是同一样东西的了。

有继承

无 final 修饰

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package org.agoncal.sample.jmh;
import java.util.Random;
/**
 * Created by emacsist on 2017/6/15.
 */
public abstract class StringKitAbs {
    // 生成随机数字和字母,
    public String getStringRandom(int length) {
        String val = "";
        Random random = new Random();
        // 参数length,表示生成几位随机数
        for (int i = 0; i < length; i++) {
            String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";
            // 输出字母还是数字
            if ("char".equalsIgnoreCase(charOrNum)) {
                // 输出是大写字母还是小写字母
                // int temp = random.nextInt(2) % 2 == 0 ? 65 : 97;
                val += (char) (random.nextInt(26) + 97);
            } else if ("num".equalsIgnoreCase(charOrNum)) {
                val += String.valueOf(random.nextInt(10));
            }
        }
        return val;
    }
}

有 final 修饰

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package org.agoncal.sample.jmh;
import java.util.Random;
/**
 * Created by emacsist on 2017/6/15.
 */
public abstract class StringKitAbsFinal {
    // 生成随机数字和字母,
    public final String getStringRandomFinal(int length) {
        String val = "";
        Random random = new Random();
        // 参数length,表示生成几位随机数
        for (int i = 0; i < length; i++) {
            String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";
            // 输出字母还是数字
            if ("char".equalsIgnoreCase(charOrNum)) {
                // 输出是大写字母还是小写字母
                // int temp = random.nextInt(2) % 2 == 0 ? 65 : 97;
                val += (char) (random.nextInt(26) + 97);
            } else if ("num".equalsIgnoreCase(charOrNum)) {
                val += String.valueOf(random.nextInt(10));
            }
        }
        return val;
    }
}

测试代码

写一个类来继承上面的抽象类,以此来测试在继承中 final 有否对多态中的影响

1
2
3
4
5
6
package org.agoncal.sample.jmh;
/**
 * Created by emacsist on 2017/6/15.
 */
public class StringKitFinal extends StringKitAbsFinal {
}
1
2
3
4
5
6
package org.agoncal.sample.jmh;
/**
 * Created by emacsist on 2017/6/15.
 */
public class StringKit extends StringKitAbs {
}

然后在基准测试中:

1
2
3
4
5
6
7
8
    @Benchmark
    public void benchmark() {
        new StringKit().getStringRandom(32);
    }
    @Benchmark
    public void benchmarkFinal() {
        new StringKitFinal().getStringRandomFinal(32);
    }

测试结果

非 final 结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# JMH version: 1.19
# VM version: JDK 1.8.0_92, VM 25.92-b14
# VM invoker: /srv/jdk1.8.0_92/jre/bin/java
# VM options: <none>
# Warmup: 20 iterations, 1 s each
# Measurement: 20 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.agoncal.sample.jmh.Main.benchmark
中间忽略了预热及测试过程
Result "org.agoncal.sample.jmh.Main.benchmark":
  213462.677 ±(99.9%) 8670.164 ops/s [Average]
  (min, avg, max) = (135751.428, 213462.677, 264182.887), stdev = 36710.017
  CI (99.9%): [204792.513, 222132.841] (assumes normal distribution)

有 final 结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# JMH version: 1.19
# VM version: JDK 1.8.0_92, VM 25.92-b14
# VM invoker: /srv/jdk1.8.0_92/jre/bin/java
# VM options: <none>
# Warmup: 20 iterations, 1 s each
# Measurement: 20 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.agoncal.sample.jmh.Main.benchmarkFinal
中间忽略了预热及测试过程
Result "org.agoncal.sample.jmh.Main.benchmarkFinal":
  213684.585 ±(99.9%) 8571.512 ops/s [Average]
  (min, avg, max) = (133472.162, 213684.585, 267742.236), stdev = 36292.318
  CI (99.9%): [205113.073, 222256.097] (assumes normal distribution)

总对比

1
2
3
4
# Run complete. Total time: 00:13:35
Benchmark             Mode  Cnt       Score      Error  Units
Main.benchmark       thrpt  200  213462.677 ± 8670.164  ops/s
Main.benchmarkFinal  thrpt  200  213684.585 ± 8571.512  ops/s

它们字节码的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
[12:12:19] emacsist:classes $ diff /tmp/StringKit.log /tmp/StringKitFinal.log
1,5c1,5
< Classfile /Users/emacsist/Documents/idea/logging/target/classes/org/agoncal/sample/jmh/StringKit.class
<   Last modified 2017-6-16; size 317 bytes
<   MD5 checksum 7f9b024adc7f39345215e3e8490cafe4
<   Compiled from "StringKit.java"
< public class org.agoncal.sample.jmh.StringKit extends org.agoncal.sample.jmh.StringKitAbs
---
> Classfile /Users/emacsist/Documents/idea/logging/target/classes/org/agoncal/sample/jmh/StringKitFinal.class
>   Last modified 2017-6-16; size 337 bytes
>   MD5 checksum f54eadc79a90675d97e95f766ef88a87
>   Compiled from "StringKitFinal.java"
> public class org.agoncal.sample.jmh.StringKitFinal extends org.agoncal.sample.jmh.StringKitAbsFinal
10,12c10,12
<    #1 = Methodref          #3.#13         // org/agoncal/sample/jmh/StringKitAbs."<init>":()V
<    #2 = Class              #14            // org/agoncal/sample/jmh/StringKit
<    #3 = Class              #15            // org/agoncal/sample/jmh/StringKitAbs
---
>    #1 = Methodref          #3.#13         // org/agoncal/sample/jmh/StringKitAbsFinal."<init>":()V
>    #2 = Class              #14            // org/agoncal/sample/jmh/StringKitFinal
>    #3 = Class              #15            // org/agoncal/sample/jmh/StringKitAbsFinal
19c19
<   #10 = Utf8               Lorg/agoncal/sample/jmh/StringKit;
---
>   #10 = Utf8               Lorg/agoncal/sample/jmh/StringKitFinal;
21c21
<   #12 = Utf8               StringKit.java
---
>   #12 = Utf8               StringKitFinal.java
23,24c23,24
<   #14 = Utf8               org/agoncal/sample/jmh/StringKit
<   #15 = Utf8               org/agoncal/sample/jmh/StringKitAbs
---
>   #14 = Utf8               org/agoncal/sample/jmh/StringKitFinal
>   #15 = Utf8               org/agoncal/sample/jmh/StringKitAbsFinal
26c26
<   public org.agoncal.sample.jmh.StringKit();
---
>   public org.agoncal.sample.jmh.StringKitFinal();
32c32
<          1: invokespecial #1                  // Method org/agoncal/sample/jmh/StringKitAbs."<init>":()V
---
>          1: invokespecial #1                  // Method org/agoncal/sample/jmh/StringKitAbsFinal."<init>":()V
38c38
<             0       5     0  this   Lorg/agoncal/sample/jmh/StringKit;
---
>             0       5     0  this   Lorg/agoncal/sample/jmh/StringKitFinal;
40c40
< SourceFile: "StringKit.java"
---
> SourceFile: "StringKitFinal.java"

可以看到,除了它们的方法签名和方法名字不同之外其他的都是一样的,包括JVM调用指令也完全是一样的。

总结

可以看到它们几乎是一样的。

总结

基于上面的基准测试结论,我认为滥用或刻意为了所谓的提升性能,而去为每一个方法尽可能添加 final 的关键字是不可取的。使用 final ,更多的应该是根据Java对 final 的语义来定义,而不是只想着为了提升性能(而且这影响可以忽略不计)而刻意用 final.

使用 final 的情况:

final 变量: 表示只读(只初始化一次,但可多次读取) final 方法:表示子类不可以重写。(网上认为 final 比非 final 快,就是认为它是在编译的时候已经静态绑定了,不需要在运行时再动态绑定。这个可能以前的JVM上是正确的,但在现代的JVM上,这个可以认为没什么影响,至少我在基准测试里是这样子) final 类: 它们不能被继承,而且final类的方法,默认也是 final 的。

智能车实战——从ADC采集到PWM控制,让电磁车精准循迹 本文详细介绍了电磁循迹智能车的实现原理与实战技巧,涵盖从ADC信号采集、PWM控制到PID算法整定的全流程。通过STM32代码示例解析电感信号处理、差比和偏差计算及电机驱动等关键技术,帮助开发者快速掌握智能车精准循迹的核心方法,提升电磁车竞赛项目的开发效率。 阅读详情

相关推荐

嵌入式:简单的service框架

在嵌入式中,我们获取一个资源、设置一个变量、更新数据的话最直接的方式就是直接调用一个函数。这样的写法也可以满足基本的功能,前提是这个系统是只要一个任务,只有一个while(1)如图1.1。图1.1当有两个或者两个以上的任务的时候,同时调用一个函数(设置一个状态值、操作同一个资源、操作指针等行为)的流程如图1.2。图1.2当两个任务同时操作一个资源的时候,就会有重入的问题,会导致想不到的异常问题(数据不对、指针异常等)。

stupid的博客 987

java final 使用讲解(讲解string)

一、java final基本概念: 1、主要用于修饰类、属性和方法: 被final修饰的类不可以被继承 被final修饰的方法不可以被重写 被final修饰的变量不可以被改变,被final修饰不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的 2、final finally finalize区别: (1)final:可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表 示该变量是一个常量不能被重新赋值。 (2)finally:一般作用在try-ca

qq_33834493的博客 1911

十一、机器学习进阶知识:径向基神经网络(RBFNN)的实现(Python,附源码及数据集)

本文对径向基神经网络(RBFNN)的理论基础及建模步骤进行介绍,之后使用Python实现基于RBFNN的数据预测,通俗易懂,适合新手学习,附源码及实验数据集。

weixin_42051846的博客 1万+

Java中的final关键字

一.final关键字的基本用法 在Java中,final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)。下面就从这三个方面来了解一下final关键字的基本用法。 1.修饰类 当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方...

qq_42499188的博客 270

STM32CubeMX、keil、simulink联合开发MCU教程

一、整体思路 利用Matlab的simulink搭建模型生成C代码,通过stm32cubemx生成工程,最后在KEIL或者IAR等工具里面编译生成代码下载至MCU中执行。 本文以流水灯建立模型和简单使用定时器为例进行演示。 二、开发环境 1、安装Matlab Matlab版本建议在2013B以上,本文使用Matlab 2016a。 2、安装STM32硬件支持包 下载地址(https://www.stmcu.org.cn/document/detail/index/id-216053) 安装完成后重启MAT

tzlaa的博客 7742

深入解析poi-tl自定义表格渲染插件中的多级子循环实现

本文深入解析了poi-tl自定义表格渲染插件中的多级子循环实现,详细介绍了其在处理复杂业务数据时的核心价值、实现原理及实战技巧。通过多级子循环机制,开发者可以高效地将层级数据结构渲染到Word表格中,显著提升代码简洁性和维护效率。文章还分享了性能优化和异常处理经验,帮助开发者更好地应用该插件。

weixin_33734785的博客 488

Java的final关键字与String的内部比较方法

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34414196的博客 366

时序异常检测汇总

时序异常检测

Mark_Australia的博客 1万+

final的作用以及String为什么不可变

1. final修饰类 final类不能被继承,因此final类的成员方法没有机会被覆盖,默认都是final的。在设计类时候,如果这个类不需要有子类,类的实现细节不允许改变,并且确信这个类不会载被扩展,那么就设计为final类。 2. final修饰方法 如果一个类不允许其子类覆盖某个方法(即不允许被子类重写),则可以把这个方法声明为final方法。 使用final方法的原因有二: 把方法锁定,防止任何继承类修改它的意义和实现。 高效。编译器在遇到调用final方法时候会转入内嵌机制,大大提高执行效率。 3

qq_54494937的博客 654

医疗大模型实战——从零构建MedicalGPT全流程解析

本文详细解析了从零构建医疗大模型MedicalGPT的全流程实战。基于Qwen-7B模型与约195万条中文医疗数据,完整复现了增量预训练、监督微调与偏好对齐三大核心环节,并分享了如何在有限硬件资源下实现高效训练与部署,为开发者提供了可复现的医疗AI项目实战指南。

weixin_29193259的博客 556

华为OD机试真题2025双机位A卷 Python&JavaScript 实现【采购订单】

在一个采购系统中,采购申请(PR)需要经过审批后才能生成采购订单(PO)。每个PR包含商品的单价(假设相同商品的单价一定是一样的)及数量信息。系统要求对商品进行分类处理:单价高于100元的商品需要单独处理,单价低于或等于100元的相同商品可以合并到同一采购订单PO中。针对单价低于100的小额订单,如果量大可以打折购买。

MISAYAONE的博客 1463

请不要再说 Java 中 final 方法比非 final 性能更好了

无继承有 static 修饰static finalstatic 非 final结果这里使用了 OpenJDK 的 JMH 基准测试工具来测试的,结果如下:总结:你说fi...

哲洛不闹的专栏 216

Graphviz安装及使用:决策树可视化

Python大数据分析与机器学习学习笔记 Graphviz安装及使用:决策树可视化

算法工程师 1万+

the art of java 源代码_请不要再说Java中final方法比非final性能更好了

无继承有 static 修饰static finalstatic 非 final结果这里使用了 OpenJDK 的 JMH 基准测试工具来测试的,结果如下:总结:你说final的性能比非final有没有提升呢?可以说有,但几乎可以忽略不计。如果单纯地追求性能,而将所有的方法修改为 final 的话,我认为这样子是不可取的。而且这性能的差别,远远也没有网上有些人说的提升 50% 这么恐怖(有可能他们...

weixin_39797381的博客 128

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Java最新请不要再说Java中final方法比非final性能更好了,Java面试题合集

对于是否有 final 修饰的方法,对性能的影响可以忽略不计。因为它们生成的字节码除了 flags 标志位是否有 final 修饰不同之外,其他所有的JVM指令,都是一样的(对于方法本身,以及调用者本身的字节码都一样)。对于JVM来说,它执行的就是字节码,如果字节码都一样的话,那对于JVM来说,它就是同一样东西的了。有继承===无 final 修饰12345678910111213141516171819202122232425/***/

2301_76348206的博客 155

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Java中final方法比非final性能更好?

无继承 有 static 修饰 static final   // 生成随机数字和字母,    public static final String getStringRandomFinal(int length) {        String val = "";        Random random = new Random();  ...

KHOST的博客 702

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

java final 关键字

java的final关键字详解

tuoniaoxs的博客 3万+

java 中final修饰的变量_java中final修饰符的使用方法

本文为大家分享了java中final修饰符的使用,供大家参考,具体内容如下1.final修饰符的用法:final可以修饰变量,被final修饰的变量被赋初始值之后,不能对它重新赋值。final可以修饰方法,被final修饰的方法不能被重写。final可以修饰类,被final修饰的类不能够被继承。上面的这些“语法口诀”对真正掌握final修饰符的用法依然是不够的。2.final修饰的变量:被fina...

weixin_33259400的博客 2116

Java 中 final 关键字详解

在 Java 中,final 关键字可以修饰的东西比较多,很容易混淆,在这里记录一下。主要从功能上说明一下,不做过多的代码演示。 final 关键字用途 1. final 变量 凡是对成员变量或者本地变量(在方法中的或者代码块中的变量称为本地变量)声明为final的都叫作final变量。final变量经常和static关键字一起使用,作为常量。用final关键字修饰的变量,只能进行一次赋值操作,并且在生存期内不可以改变它的值。 2. final方法参数 如果 final 关键字修饰方法参数时,方法中是不能改

qhh0205 3136

java — 关于final的总结

关于java中final关键字的总结 文章目录一. 引言二. final修饰变量:相当于常量三. final修饰方法四. 简单说说final和其他关键字的使用 一. 引言 final是java中一个非常重要的关键字,final关键字在java中可以用于类、方法、变量,java中的String类、System类、StringBuffer类都不可被继承,是final的典型例子。下面就来总结我所学过的final的用法。 二. final修饰变量:相当于常量 1. 成员变量: 简述: 就是在类里面定义的变量,成员

Jwasx的博客 404

JAVA中final修饰参数

1、 final修饰在变量上,说明这个变量的值不能修改 final修饰在方法上,说明这个方法在继承后不能覆盖. final修饰在类上,说明这个类为终态,不能被继承了. 2、final类型修饰的参数分为两种类型:基本类型 与 引用类型 参考:http://www.cnblogs.com/lan0725/archive/2013/05/24/3097573.html (1) 在调用......

苍煜 5281

Java中的final详解以及用途实战

浅析Java中的final关键字   谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字。另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法。下面是本文的目录大纲:   一.final关键字的基本用法   二.深入理解final关键字   若有不正之处,请多多谅解并欢迎指正。   请尊重作者劳动

litong09282039的博客 2843

java中final是啥意思_(转)深入理解Java中的final关键字

Java中的final关键字非常重要,它可以应用于类、方法以及变量。这篇文章中我将带你看看什么是final关键字?将变量,方法和类声明为final代表了什么?使用final的好处是什么?最后也有一些使用final关键字的实例。final经常和static一起使用来声明常量,你也会看到final是如何改善应用性能的。final关键字的含义?final在Java中是一个保留的关键字,可以声明成员变量、...

weixin_35866180的博客 2101

记录一下Java中final的作用

记录一下Java中final的作用   final关键字可以修饰类、成员变量、方法、以及方法中的局部变量。 1、final修饰类   可以使用final将类声明为final类。final类不能被继承,即不能有子类。例如: final class A{ ... }   A就是一个final类,将不允许任何类声明成A的子类。有时候是出于一些安全性的考虑,将一些类修饰为final类。例如,Java在java.lang包中提供的String类对于编译器和解释器的正常运行有着很重要的作用,Java不允许用户程序扩

爱吃雪糕的小布丁的博客 9202
上一篇: JAVA多线程之volatile 与 synchronized 的比较
下一篇: Java 反射:框架设计的灵魂
Java奥利给
博客等级 码龄6年 150粉丝 154原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值