請不要再說 Java 中 final 方法比非 final 性能更好了

来源:http://www.cnblogs.com/wl2014/archive/2017/12/12/8029679.html
-Advertisement-
Play Games

無繼承 有 static 修飾 static final static 非 final 結果 這裡使用了 OpenJDK 的 JMH 基準測試工具來測試的,結果如下: 總結:你說final的性能比非final有沒有提升呢?可以說有,但幾乎可以忽略不計。如果單純地追求性能,而將所有的方法修改為 fin ...


無繼承

有 static 修飾

static final

// 生成隨機數字和字母,
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

// 生成隨機數字和字母,
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 基準測試工具來測試的,結果如下:

# 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

它們在位元組碼上的差別:

[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"

可以看到除了方法名和方法修飾符不同之外,其他的沒有什麼區別了。

在調用者上面的位元組碼差別

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 的修飾。

結果

# 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

分析

位元組碼級別的差別

[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 修飾符差別外,其餘的是一樣的。

在調用者上面的位元組碼差別

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 修飾

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 修飾

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 有否對多態中的影響

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

然後在基準測試中:

@Benchmark
public void benchmark() {
    new StringKit().getStringRandom(32);
}
@Benchmark
public void benchmarkFinal() {
    new StringKitFinal().getStringRandomFinal(32);
}

測試結果

非 final 結果

# 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 結果

# 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)

總對比

# 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

它們位元組碼的區別

[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 的。

歡迎加入學習交流群569772982,大家一起學習交流。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 如果上一篇我轉發的關於ubuntu的博文,你看完覺得還沒準備好,那麼,本篇從最基礎的開始,安裝虛擬機到正常使用ubuntu 虛擬機 1.什麼是虛擬機 虛擬機(Virtual Machine)指通過軟體模擬的具有完整硬體系統功能的、運行在一個完全隔離環境中的完整電腦系統。 通俗的說,我們平常看得見摸 ...
  • 題目描述 如題,已知一個數列,你需要進行下麵兩種操作: 1.將某一個數加上x 2.求出某區間每一個數的和 輸入輸出格式 輸入格式: 第一行包含兩個整數N、M,分別表示該數列數字的個數和操作的總個數。 第二行包含N個用空格分隔的整數,其中第i個數字表示數列第i項的初始值。 接下來M行每行包含3個整數, ...
  • 最近從百度雲盤上下載了一批視頻,然而這些視頻的文件名都帶有廣告。有潔癖的我看著感覺難受,於是想修改過來。但是一個個的修改文件名又嫌麻煩,聯想到業餘時看過的python,於是用python寫了幾行代碼輕鬆地批量修改了文件名稱。 代碼如下: python學習起來著實有趣,聽說python明年在某個省份要 ...
  • 類型檢查 創建類的實例時,該實例的類型為類本身: 要測試實例是否屬於某個類,可以使用type()內置函數: 當然,python中不建議如此檢查,更好的辦法是使用內置類型檢查函數isinstance(obj, cls): 同樣的,內置函數issubclass(cls1, cls2)可以用做子類的檢查: ...
  • 背景: 以前學的Java進行開發,多用到Mybatis,Hiberante等ORM框架,最近需要上手一個C#的項目,由於不是特別難,也不想再去學習C#的ORM框架,所以就想著用反射簡單的實現一下ORM框架的內容,簡單的增刪改查,沒有用到多表之間的聯繫。 反射: Java和C#中的反射大體相同,主要是 ...
  • 由於自己的公司的項目需要調用視頻地址 1:當為鏈接時:直接在播放器用資料庫查找的地址 2:當為外部鏈接時:直接用window.location.href('資料庫查找的地址') 3:當為H5鏈接時:使用<ifram src="資料庫查找的地址">播放 4:當為其餘網站鏈接時,要去第三方網站讀取jso ...
  • 瞭解會話的產生過程、會話的特性、簡單的Cookie、在URL中內嵌會話ID以及這樣做帶來什麼後果 ...
  • spring boot 使用 starter 解決了很多配置問題, 但是, 他是怎麼來解決這些問題的呢? 這裡通過一個簡單的例子, 來看一下, starter是怎麼來設置預設配置的. 一. 建 starter 項目 自定義的starter, 項目命名規範是 : 自定義名-spring-boot-st ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...