`
ZangXT
  • 浏览: 118310 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

无聊:为啥可以使用空引用调用static方法或者访问static field

JVM 
阅读更多

先看代码:

public class Test {

public static void testStatic(){

    System.out.println("testStatic()");

}

public void testNonStatic(){

    System.out.println("testNonStatic()");

}

public static void main(String[] args) {

    Test.testStatic();

    Test test = null;

    test.testStatic();

    test = new Test();

    test.testStatic();

    test.testNonStatic();

}

}

结果:

testStatic()

testStatic()

testStatic()

testNonStatic()

       原因其实很简单,调用静态方法或者访问静态数据时根本不需要实例,如例子中test.testStatic(); ,这里的test是否为空根本无所谓,因为它存在的目的就是向编译器传达一个信号,"我要调用的是Test类的testStatic()方法,别给我搞错了啊!"。虽然java是面向对象的语言,但到底层执行依然是过程式的,这是计算机的本质决定的。因此,实例方法的调用不过是换了个形式而已,就是把对象引用本身作为第一个参数,比如test.testNonStatic();其实可以看作Test::testNonStatic(test);这里传入的参数test就是非静态方法中可以拿来用的this了。

       换个形式可能更容易理解:

public void Test::testNonStatic(final Test this){……}

      非静态方法里就可以使用this这个形参来访问数据了。调用非静态方法的时候会检查该实例是否为null。

最后,看一下main方法反编译代码:

public static void main(java.lang.String[]);

Code:

0:    invokestatic    #6; //Method testStatic:()V

3:    aconst_null

4:    astore_1

5:    aload_1

6:    pop

7:    invokestatic    #6; //Method testStatic:()V

10:    new    #7; //class Test

13:    dup

14:    invokespecial    #8; //Method "":()V

17:    astore_1

18:    aload_1

19:    pop

20:    invokestatic    #6; //Method testStatic:()V

23:    aload_1

24:    invokevirtual    #9; //Method testNonStatic:()V

27:    return

      这里可以发现一个问题,当用类直接调用方法时,只需要1条指令:

invokestatic    #6; //Method testStatic:()V

      而用对象引用来调用却需要3条指令,而且前两条完全是无用功:

5:    aload_1

6:    pop  //干嘛,耍我呢?

7:    invokestatic    #6; //Method testStatic:()V

      刚把引用test放到栈顶就立马弹出去了,瞎折腾了一下。剩下的才是真正起作用的调用指令。很明显,test是否为null根本不用理会了。

      这里发现一个问题,使用类直接调用静态方法不但是一个好的编码习惯,还可能有性能上的优势,毕竟使用对象引用来调用时白白浪费了两条指令。之所以说是"可能",因为jvm编译器完全有可能把这种无用指令优化掉。

      还是非静态方法的调用规矩啊,

23:    aload_1

24:    invokevirtual    #9; //Method testNonStatic:()V

      将test作为唯一的参数,调用testNonStatic方法。invokevirtual会检查该参数,为null时抛异常。

分享到:
评论
2 楼 hunter4java 2009-11-04  
精辟。。
我等望其项背啊。。

哎,汇编我大2的时候全部逃课去了,害我现在不会汇编。我要加油了。
1 楼 patrickyao1988 2009-09-12  
每次来看博主的文章都有收获啊~~~顶下

相关推荐

    反射,动态加载指定类调用类中的方法

    反射机制使得我们能够在运行时动态地获取类的信息,并且能够创建对象、调用方法、访问字段,甚至改变类的行为。在Java中,`java.lang.reflect`包提供了对反射的支持。 在“反射,动态加载指定类调用类中的方法”这...

    C#调用Delphi dll库文件

    3. **定义本地方法**:由于C#不支持直接传递方法作为参数,我们需要定义一个本地方法,该方法接受结构体参数并调用Delphi的存储过程: ```csharp private static void CallDelphiProcedure([In] TMyRecord record) ...

    Java开发中static.this.super.final用法

    它可以用于调用父类的构造函数(`super()`),或者访问父类的字段和方法(`super.field`或`super.method()`)。在子类中,如果重写了父类的方法,可以通过`super`调用父类的原版方法。在示例中没有使用`super`,但它...

    静态成员间的调用实例(基础)

    在上述示例中,静态方法`StaticMethod`调用了实例方法`InstanceMethod`是不可能的,因为静态方法不依赖于实例,无法直接访问实例的成员。如果需要在静态方法中使用实例成员,必须先创建实例。 总结来说,C#中的静态...

    C#net反射实现访问类中的私有变量或者方法

    例如,可以使用`MethodInfo`类的`Invoke`方法来调用方法。 ### 四、总结 通过上述分析,我们可以看出反射在C#中的强大功能。它可以让我们在运行时动态地获取并操作类的成员,这对于构建高度灵活和可扩展的应用程序...

    Java 反射创建get set方法及反射方法的调用

    本文介绍了如何使用Java反射机制来创建get和set方法,并通过反射调用这些方法来访问对象的属性。这种方式虽然灵活,但在实际开发中应当谨慎使用,因为它可能会降低代码的性能和可维护性。了解反射机制的基本原理对于...

    android jni使用static变量

    JNI为Java应用程序提供了一个接口,可以调用本地方法(即非Java代码),同时也允许本地方法调用Java代码。在Android环境中,JNI通常用于提高性能、利用硬件特性或者调用已有的C/C++库。 在Java层定义一个静态变量:...

    Java中static、this、super、final用法

    1. **访问父类字段**:如果子类和父类有同名字段,`super.field` 可以访问父类的字段。 2. **调用父类方法**:`super.method()` 用来调用父类的非覆盖方法,特别是覆盖了父类方法的情况下。 3. **构造器链**:子类...

    C#调用BarTender条码并打印

    3. **启动BarTender并执行打印**:创建完`ProcessStartInfo`后,我们可以使用`Process`类的`Start`方法来启动BarTender并执行指定的操作: ```csharp Process barTenderProcess = new Process(); barTenderProcess....

    findbug 常见异常处理

    描述:调用的方法中不会抛出异常,但是调用方法的时候尝试使用try catch 捕获异常; 处理方式:确认此方法的调用会不会导致异常的发生,如果不会抛出异常请去 掉try catch,确认方法调用会不会抛出异常关键是对方法...

    day11-面向对象-static&继承

    例如,`super.someMethod()`调用父类的`someMethod`方法,`super.someField`访问父类的`someField`字段。 4. final关键字与继承 `final`关键字可以应用于类、方法和变量,表示不可改变或不可继承。如果一个类被...

    Matlab COM之MWMCR::EvaluateFunction error问题解决

    - 另外,还可以尝试在Matlab端编写一个包装函数,该函数接收结构体作为输入,并将其转换为`sim`函数可以接受的形式。 #### 示例代码 假设C#端有如下代码片段: ```csharp using MathWorks.MATLAB.NET.Arrays; ...

    Java反射之Constructor、Method、Field使用及说明

    这使得开发者可以在运行时动态地创建对象、调用方法或访问字段。RTTI主要通过两种方式实现: 1. **传统RTTI**:假设在编译阶段已经知道了所有类型信息,如通过`instanceof`关键字判断对象类型。 2. **反射机制**:...

    1C#中调用API函数方法[定义].pdf

    在C#代码中,你需要在引用`System.Runtime.InteropServices`命名空间后,使用`DllImport`特性来声明一个静态的extern方法。例如,调用`CreateFile`函数: ```csharp [DllImport("coredll.dll", EntryPoint = ...

    Android 5.0jni调用

    在JNI中,可以通过`FindClass`, `GetStaticFieldID`和`GetStatic*Field`(或`SetStatic*Field`)方法来获取和修改Java类的静态字段。 6. **线程安全**:在Android 5.0中,JNI函数默认运行在主线程之外,开发者需要...

    JAVA反射机制的入门代码

    这个特性使得Java具有了高度的灵活性和动态性,尤其是在处理元数据、创建对象、调用私有方法以及访问未导出的类等方面显得尤为有用。以下是对"JAVA反射机制的入门代码"的详细解释。 首先,我们要理解反射的基本概念...

    使用java反射机制实现java的深拷贝

    此外,为了提高性能,实际应用中可以考虑使用序列化或克隆方法,或者针对特定类实现专门的拷贝构造函数或`copy()`方法。 总结来说,Java反射机制为我们提供了动态访问和操作类的能力,可以用来实现对象的深拷贝。...

    用反射的方式获取父类中的所有属性和方法

    有了父类的`Class`对象后,我们可以通过`getFields()`方法获取所有的public字段,或者使用`getDeclaredFields()`获取包括私有在内的所有字段: ```java Field[] fields = superClass.getDeclaredFields(); for ...

    C#调用C/C++ Dll中函数实例代码

    本文将详细介绍如何在C#中使用P/Invoke调用C/C++ DLL中的函数,并处理输入、输出字符串参数和结构类型参数。 首先,我们需要理解P/Invoke的概念。P/Invoke是.NET框架提供的一种机制,用于使托管代码能够访问非托管...

    9 对象的实例化内存布局与访问定位.md,学习代码

    这部分信息可以帮助JVM执行方法调用、垃圾回收等操作。 2. **实例数据**:这是对象真正存储字段值的地方,包括类中的基本类型和引用类型。基本类型如int、char等占用固定大小的内存,而引用类型则指向其他对象的...

Global site tag (gtag.js) - Google Analytics