Preface
Today, I'll analyze why static methods can't call non-static methods, but can instead.This article will analyze the underlying cause from the perspective of byte codes.
Principle analysis
This concludes that static methods belong to classes, and there is no default implied this reference in the method's parameter list. This also indicates that calling static methods does not require a reference to the current object, but calling non-static methods requires a reference to the current object to invoke, so this is the main reason that non-static methods cannot be invoked. Non-static methods belong to instance objects.The method's parameter list has a default implicit this reference, and this is not required to call a static method, so it can be called.
Sample Code
package com.github.xfc.staticmethod; /** * Why static methods cannot call non-static methods * * @author xf.chen * @date 2021/9/2 07:31 * @since 1.0.0 */ public class StaticMethodTest { /** * Non-static method */ public void normalMethod(String data) { // Non-static method calls static method staticMethod(data); } /** * Static method */ public static void staticMethod(String data) { System.out.println("static:" + data); } public static void main(String[] args) { // Static Method Call StaticMethodTest.staticMethod("hello"); // instantiation final StaticMethodTest palindrome = new StaticMethodTest(); // Non-static method call palindrome.normalMethod("xf"); // Invoke non-static method calls through instance objects palindrome.staticMethod("chen"); } }
The above code is very simple, mainly defining a static method and a non-static method, and adding a main method to test the effect.
The results of the above code execution are as follows
Byte Code
Last modified 2021-9-2; size 1074 bytes MD5 checksum df4162d6ec0e9c271672f25e14c6b50e Compiled from "StaticMethodTest.java" public class com.github.xfc.staticmethod.StaticMethodTest minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #16.#36 // java/lang/Object."<init>":()V #2 = Methodref #11.#37 // com/github/xfc/staticmethod/StaticMethodTest.staticMethod:(Ljava/lang/String;)V #3 = Fieldref #38.#39 // java/lang/System.out:Ljava/io/PrintStream; #4 = Class #40 // java/lang/StringBuilder #5 = Methodref #4.#36 // java/lang/StringBuilder."<init>":()V #6 = String #41 // static: #7 = Methodref #4.#42 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #8 = Methodref #4.#43 // java/lang/StringBuilder.toString:()Ljava/lang/String; #9 = Methodref #44.#45 // java/io/PrintStream.println:(Ljava/lang/String;)V #10 = String #46 // hello #11 = Class #47 // com/github/xfc/staticmethod/StaticMethodTest #12 = Methodref #11.#36 // com/github/xfc/staticmethod/StaticMethodTest."<init>":()V #13 = String #48 // xf #14 = Methodref #11.#49 // com/github/xfc/staticmethod/StaticMethodTest.normalMethod:(Ljava/lang/String;)V #15 = String #50 // chen #16 = Class #51 // java/lang/Object #17 = Utf8 <init> #18 = Utf8 ()V #19 = Utf8 Code #20 = Utf8 LineNumberTable #21 = Utf8 LocalVariableTable #22 = Utf8 this #23 = Utf8 Lcom/github/xfc/staticmethod/StaticMethodTest; #24 = Utf8 normalMethod #25 = Utf8 (Ljava/lang/String;)V #26 = Utf8 data #27 = Utf8 Ljava/lang/String; #28 = Utf8 staticMethod #29 = Utf8 main #30 = Utf8 ([Ljava/lang/String;)V #31 = Utf8 args #32 = Utf8 [Ljava/lang/String; #33 = Utf8 palindrome #34 = Utf8 SourceFile #35 = Utf8 StaticMethodTest.java #36 = NameAndType #17:#18 // "<init>":()V #37 = NameAndType #28:#25 // staticMethod:(Ljava/lang/String;)V #38 = Class #52 // java/lang/System #39 = NameAndType #53:#54 // out:Ljava/io/PrintStream; #40 = Utf8 java/lang/StringBuilder #41 = Utf8 static: #42 = NameAndType #55:#56 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #43 = NameAndType #57:#58 // toString:()Ljava/lang/String; #44 = Class #59 // java/io/PrintStream #45 = NameAndType #60:#25 // println:(Ljava/lang/String;)V #46 = Utf8 hello #47 = Utf8 com/github/xfc/staticmethod/StaticMethodTest #48 = Utf8 xf #49 = NameAndType #24:#25 // normalMethod:(Ljava/lang/String;)V #50 = Utf8 chen #51 = Utf8 java/lang/Object #52 = Utf8 java/lang/System #53 = Utf8 out #54 = Utf8 Ljava/io/PrintStream; #55 = Utf8 append #56 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder; #57 = Utf8 toString #58 = Utf8 ()Ljava/lang/String; #59 = Utf8 java/io/PrintStream #60 = Utf8 println { public com.github.xfc.staticmethod.StaticMethodTest(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 31: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/github/xfc/staticmethod/StaticMethodTest; public void normalMethod(java.lang.String); descriptor: (Ljava/lang/String;)V flags: ACC_PUBLIC Code: stack=1, locals=2, args_size=2 0: aload_1 1: invokestatic #2 // Method staticMethod:(Ljava/lang/String;)V 4: return LineNumberTable: line 38: 0 line 39: 4 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/github/xfc/staticmethod/StaticMethodTest; 0 5 1 data Ljava/lang/String; public static void staticMethod(java.lang.String); descriptor: (Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=1, args_size=1 0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 3: new #4 // class java/lang/StringBuilder 6: dup 7: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V 10: ldc #6 // String static: 12: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 15: aload_0 16: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 22: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 25: return LineNumberTable: line 46: 0 line 47: 25 LocalVariableTable: Start Length Slot Name Signature 0 26 0 data Ljava/lang/String; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=2, args_size=1 0: ldc #10 // String hello 2: invokestatic #2 // Method staticMethod:(Ljava/lang/String;)V 5: new #11 // class com/github/xfc/staticmethod/StaticMethodTest 8: dup 9: invokespecial #12 // Method "<init>":()V 12: astore_1 13: aload_1 14: ldc #13 // String xf 16: invokevirtual #14 // Method normalMethod:(Ljava/lang/String;)V 19: aload_1 20: pop 21: ldc #15 // String chen 23: invokestatic #2 // Method staticMethod:(Ljava/lang/String;)V 26: return LineNumberTable: line 52: 0 line 54: 5 line 56: 13 line 58: 19 line 59: 26 LocalVariableTable: Start Length Slot Name Signature 0 27 0 args [Ljava/lang/String; 13 14 1 palindrome Lcom/github/xfc/staticmethod/StaticMethodTest; }
There are a lot of byte codes. Let's split up the analysis one by one.A more readable jclasslib tool is also recommended here.Essentially consistent with the byte code file content described above.
Method Overview
Important to this java code are the following four methods, a default parameterless construction method, and three methods implemented within the source code.
Nonstatic method normalMethod byte code
public void normalMethod(java.lang.String); descriptor: (Ljava/lang/String;)V flags: ACC_PUBLIC Code: stack=1, locals=2, args_size=2 0: aload_1 // The #2 query constant pool is as follows #2 com/github/xfc/staticmethod/StaticMethodTest.staticMethod: essentially calling static methods directly from a class 1: invokestatic #2 // Method staticMethod:(Ljava/lang/String;)V 4: return LineNumberTable: line 38: 0 line 39: 4 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/github/xfc/staticmethod/StaticMethodTest; 0 5 1 data Ljava/lang/String;
As with the above byte code content, our method actually only has one parameter, which is as follows: NoralMethod (String data), but the above byte code hints that locals=2 represents two local variables.So let's look at the local variable table LocalVariableTable
LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/github/xfc/staticmethod/StaticMethodTest; 0 5 1 data Ljava/lang/String;
As shown in the code above, the 0th variable is the this reference.The first is the data field inside the code, so the 0 instruction is aload_1, which means pushing the second reference type local variable to the top of the stack.That's because the tenth is this`reference.From the above, it is shown that non-static methods contain implicit object references.
Static Method staticMethodc Byte Code
public static void staticMethod(java.lang.String); descriptor: (Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=1, args_size=1 0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 3: new #4 // class java/lang/StringBuilder 6: dup 7: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V 10: ldc #6 // String static: 12: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 15: aload_0 16: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 22: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 25: return LineNumberTable: line 46: 0 line 47: 25 LocalVariableTable: Start Length Slot Name Signature 0 26 0 data Ljava/lang/String;
The reference code uses both print statements and string splicing, so there are more byte code commands, and here we focus on locals=1 and LocalVariableTable.The local variable table is as follows
LocalVariableTable: Start Length Slot Name Signature 0 26 0 data Ljava/lang/String;
As you can see from the local variable table above, there is only one data variable and there is no this reference, so the above non-static method cannot be called.You now have a basic understanding of why static methods cannot call non-static methods.
Why can static methods be invoked through instance objects in the next extended analysis?
To put it straight: If you invoke a static method through an instance object, it will be optimized for static method calls during the compilation phase
Code Review
Also through byte code content analysis, look directly at the main method byte code key commands
As shown in the figure above, static methods are invoked directly, so it can be said that the JVM is optimized to invoke static methods directly through classes.The following is decompiled content
conclusion
- Static methods belong to classes, and there is no default implicit this reference in the method's parameter list. This also indicates that calling a static method does not require a reference to the current object, but calling a non-static method requires a reference to the current object to invoke, so this is the main reason that a non-static method cannot be invoked.
- A non-static method is an instance object, and its parameter list has a default implicit this reference, while calling a static method does not require this, so it can be called.
- If you force a static method to be called through an object, the compiler optimizes to call the static method directly.
Personal thinking: In the development process, it is taken for granted that many questions are not considered and validated.So I occasionally encounter problems I don't know where to start.In the future, we should think more and practice more.
Add an instruction document:https://docs.oracle.com/javase/specs/jvms/se16/html/jvms-6.html#jvms-6.5.aload_n