首先我们测试final对非静态变量和方法的影响
有final的情况下:
public class FinalTest {
final int a = 1;
final String s = "sss";
final void haha() {
System.out.println("haha");
System.out.println(a);
}
public static void main(String[] args) {
FinalTest finalTest = new FinalTest();
}
}
反编译字节码文件:
public class com.heaboy.mvc.FinalTest {
final int a;
final java.lang.String s;
public com.heaboy.mvc.FinalTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_1
6: putfield #2 // Field a:I
9: aload_0
10: ldc #3 // String sss
12: putfield #4 // Field s:Ljava/lang/String;
15: return
final void haha();
Code:
0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #6 // String haha
5: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
11: iconst_1
12: invokevirtual #9 // Method java/io/PrintStream.println:(I)V
15: return
public static void main(java.lang.String[]);
Code:
0: new #8 // class com/heaboy/mvc/FinalTest
3: dup
4: invokespecial #10 // Method "<init>":()V
7: astore_1
8: return
}
无final的情况下:
public class FinalTest {
int a = 1;
String s = "sss";
void haha() {
System.out.println("haha");
System.out.println(a);
}
public static void main(String[] args) {
FinalTest finalTest = new FinalTest();
}
}
反编译字节码文件:
public class com.heaboy.mvc.FinalTest {
int a;
java.lang.String s;
public com.heaboy.mvc.FinalTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_1
6: putfield #2 // Field a:I
9: aload_0
10: ldc #3 // String sss
12: putfield #4 // Field s:Ljava/lang/String;
15: return
void haha();
Code:
0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #6 // String haha
5: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
11: aload_0
12: getfield #2 // Field a:I
15: invokevirtual #8 // Method java/io/PrintStream.println:(I)V
18: return
public static void main(java.lang.String[]);
Code:
0: new #9 // class com/heaboy/mvc/FinalTest
3: dup
4: invokespecial #10 // Method "<init>":()V
7: astore_1
8: return
}
综上所得,final在两者在haha()方法中有一点点的不同:
-
我们这里输出的是a:
-
没有final修饰的变量a,我们需要aload_0,getfield,即加载变量,获取变量的值,然后输出
- 有final修饰的变量a,我们只需要装载iconst_1或者sipush(这个是大数,比如我们给a刚开始的赋值为10,这就是大数,jvm就不会使用iconst_1,而是选择去用sipush去执行赋值的操作),即直接加载常量,直接输出
- 理论上有final修饰出的常量调用更快一点
-
之后我们测试final对静态变量和方法的影响
有final的情况下:
public class FinalTest {
final static int a = 1;
final static String s = "sss";
final static void haha() {
System.out.println(a);
}
public static void main(String[] args) {
FinalTest finalTest = new FinalTest();
}
}
反编译字节码文件:
public class com.heaboy.mvc.FinalTest {
static final int a;
static final java.lang.String s;
public com.heaboy.mvc.FinalTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
static final void haha();
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: iconst_1
4: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
7: return
public static void main(java.lang.String[]);
Code:
0: new #3 // class com/heaboy/mvc/FinalTest
3: dup
4: invokespecial #5 // Method "<init>":()V
7: astore_1
8: return
}
没有final的情况下:
public class FinalTest {
static int a = 1;
static String s = "sss";
static void haha() {
System.out.println(a);
}
public static void main(String[] args) {
FinalTest finalTest = new FinalTest();
}
}
反编译字节码文件:
public class com.heaboy.mvc.FinalTest {
static int a;
static java.lang.String s;
public com.heaboy.mvc.FinalTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
static void haha();
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: getstatic #3 // Field a:I
6: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
9: return
public static void main(java.lang.String[]);
Code:
0: new #5 // class com/heaboy/mvc/FinalTest
3: dup
4: invokespecial #6 // Method "<init>":()V
7: astore_1
8: return
static {};
Code:
0: iconst_1
1: putstatic #3 // Field a:I
4: ldc #7 // String sss
6: putstatic #8 // Field s:Ljava/lang/String;
9: return
}
综上所得,final修饰的变量有不同,两者在haha()方法中也有一点点的不同:
- final修饰的变量不在静态区当中,而是常量池中
-
我们这里输出的是a:
-
没有final修饰的变量a,我们需要getstatic,即加载静态变量,然后输出
- 有final修饰的变量a,我们只需要装载iconst_1或者sipush即直接加载常量,直接输出
-
父子继承问题
- final修饰的类不能被继承
- final修饰的对象和数组,我们是可以修改里面的内容的,但是不能修改地址
- 我们不能给父类的变量重新赋值,并且不能重写父类的方法
我们测试第一种情况:会报错,不能继承final修饰的类
public class FinalTest extends FinalDadTest {
}
final class FinalDadTest {
}
我们测试第二种情况会输出1111:我们可以得出,final修饰的对象和数组,我们是可以修改里面的内容的,但是不能修改地址
public class FinalTest extends FinalDadTest {
public static void main(String[] args) {
final FinalTest finalTest = new FinalTest();
final int[] arr = finalTest.arr;
arr[0] = 1111;
System.out.println(arr[0]);// 输出1111
}
}
class FinalDadTest {
final int a = 1;
final int[] arr = new int[10];
final void haha() {
System.out.println("sss");
}
final void cece() {
System.out.println("cece");
}
}
我们测试第三种情况:我们会发现我们不能给父类的a重新赋值,并且不能重写父类的方法haha().
public class FinalTest extends FinalDadTest {
public FinalTest() {
super.a = 2;
}
void haha() {
System.out.println(a);
}
public static void main(String[] args) {
}
}
class FinalDadTest {
final int a = 1;
final void haha() {
System.out.println("sss");
}
}