做淘寶網(wǎng)站的企業(yè)網(wǎng)站排名優(yōu)化方案
- 在
java
中使用final
聲明常量 - 在
kotlin
中使用const val
聲明常量
常量在編譯為字節(jié)碼后會直接把調(diào)用常量的地方直接替換為常量值,示例如下:
public class ConstDemo {public static final String NAME = "Even";private static final int ID = 1001;static final int YEAR = 2024;public final int color = 255;public static int width = 100;public int height = 200;public static void main(String[] args) {System.out.println(NAME);System.out.println(NAME);System.out.println(ID);System.out.println(ID);System.out.println(YEAR);System.out.println(YEAR);ConstDemo demo = new ConstDemo();System.out.println(demo.color);System.out.println(demo.color);final int number = 9;System.out.println(number);System.out.println(number);System.out.println("--------------------------------");final int count;if (width > 100) {count = 1;} else {count = 2;}System.out.println(count);System.out.println(count);System.out.println(width);System.out.println(width);System.out.println(demo.height);System.out.println(demo.height);int weight = 99;System.out.println(weight);System.out.println(weight);}}
編譯后得到class字節(jié)碼,在IntelliJ中可以直接雙擊這個class字節(jié)碼,它是自帶反編譯器,效果如下:
public class ConstDemo {public static final String NAME = "Even";private static final int ID = 1001;static final int YEAR = 2024;public final int color = 255;public static int width = 100;public int height = 200;public ConstDemo() {}public static void main(String[] args) {System.out.println("Even");System.out.println("Even");System.out.println(1001);System.out.println(1001);System.out.println(2024);System.out.println(2024);ConstDemo demo = new ConstDemo();PrintStream var10000 = System.out;Objects.requireNonNull(demo);var10000.println(255);var10000 = System.out;Objects.requireNonNull(demo);var10000.println(255);int number = true;System.out.println(9);System.out.println(9);System.out.println("--------------------------------");byte count;if (width > 100) {count = 1;} else {count = 2;}System.out.println(count);System.out.println(count);System.out.println(width);System.out.println(width);System.out.println(demo.height);System.out.println(demo.height);int weight = 99;System.out.println(weight);System.out.println(weight);}
}
如上代碼,可以發(fā)現(xiàn),只要是final修飾的變量在調(diào)用時直接被常量值替代了,有一個例外,就是在局部變量中聲明的final int count;
,它不是在聲明時直接賦值的,而是經(jīng)過一個if
判斷之后才賦值的,所以需要在運(yùn)行時才能確定它的值是多少,所以在編譯為字節(jié)碼時調(diào)用該變量的地方?jīng)]有被常量值替換,因為此時不知道它的值是多少。
另外也看到了一些有趣的地方,編譯時編譯器會有一些優(yōu)化,比如int number = true;
還能這樣啊?沒搞懂,它的final
被去掉了,count
中地final
修飾符也被去掉了,而且類型變成了byte
類型,編譯器通過if
中的判斷得出值不是1就是2,用byte
足已,所以改成了byte
類型。
基于這個常量的特性,我們可以猜到,通過反射也是無法修改final類型的常量的,示例如下:
public class ConstDemo {public static final int age = 18;public static void main(String[] args) throws Exception {Field field = ConstDemo.class.getField("age");System.out.println("age = " + field.get(null));field.set(null, 30);System.out.println(age);}
}
運(yùn)行結(jié)果如下:
age = 18
Exception in thread "main" java.lang.IllegalAccessException: Can not set static final int field ConstDemo.age to java.lang.Integerat java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:80)at java.base/jdk.internal.reflect.UnsafeQualifiedStaticIntegerFieldAccessorImpl.set(UnsafeQualifiedStaticIntegerFieldAccessorImpl.java:77)at java.base/java.lang.reflect.Field.set(Field.java:799)at ConstDemo.main(ConstDemo.java:9)
從這里也可以看出,為什么常量在編譯為class字節(jié)碼之后,調(diào)用它的地方已經(jīng)被常量值所替換,為什么常量的聲明語句還保留了,因為還是有可能會被用到的,比如我們通過反射讀取該常量的值,這是需要在運(yùn)行時才能完成的,無法在編譯階段就直接使用常量值替代的。
再來看一個Demo:
public class ConstDemo {public final int age = 18;public static final ConstDemo demo = new ConstDemo();public static void main(String[] args) throws Exception {System.out.println(demo);System.out.println(demo);System.out.println(demo.age);System.out.println(demo.age);}
}
編譯為字節(jié)碼后,再反編譯結(jié)果如下:
public class ConstDemo {public final int age = 18;public static final ConstDemo demo = new ConstDemo();public ConstDemo() {}public static void main(String[] args) throws Exception {System.out.println(demo);System.out.println(demo);PrintStream var10000 = System.out;Objects.requireNonNull(demo);var10000.println(18);var10000 = System.out;Objects.requireNonNull(demo);var10000.println(18);}
}
可以看到聲明為非原始類型的final
常量在編譯為字節(jié)碼時無法使用常量值代替,因為它是一個對象,而對象的內(nèi)存地址得在運(yùn)行時才能確定,所以這種不應(yīng)該叫常量的,所以,kotlin在這方面就做的比較好,表示一個變量不可改變用val
,表示一個常量用const val
,分得更加清楚,示例如下:
const val NAME = "Even"class ConstDemo {val width = 100var height = 200companion object {const val ID = 1001@JvmStaticfun main(args: Array<String>) {println(NAME)println(NAME)println(ID)println(ID)val demo = ConstDemo()println(demo.width)println(demo.height)}}}
可以看到,聲明常量的地方只能是頂級屬性或者companion object
中,要查看反編譯,如果直接在IntelliJ中找到class文件然后雙擊會發(fā)現(xiàn)反編譯不了,我們可以這樣查看:工具 > Kotlin > 顯示Kotlin字節(jié)碼 > 反編譯,結(jié)果如下:
public final class ConstDemo {private final int width = 100;private int height = 200;public static final int ID = 1001;public final int getWidth() {return this.width;}public final int getHeight() {return this.height;}public final void setHeight(int var1) {this.height = var1;}public static final void main(@NotNull String[] args) {Intrinsics.checkNotNullParameter(args, "args");String var2 = "Even";System.out.println(var2);var2 = "Even";System.out.println(var2);short var4 = 1001;System.out.println(var4);var4 = 1001;System.out.println(var4);ConstDemo demo = new ConstDemo();int var3 = demo.getWidth();System.out.println(var3);var3 = demo.getHeight();System.out.println(var3);}
}public final class ConstDemoKt {@NotNullpublic static final String NAME = "Even";
}
在Kotlin中,常量只能是8大原始類型,不能是對象類型的,如下代碼在編譯器就報錯了:
聲明常量有什么好處?這里我想到之前寫的一篇文章:https://blog.csdn.net/android_cai_niao/article/details/113571171