无聊:谁说private方法自动是final的?兼评《Java编程思想》中的几个问题

          首先声明,本文无任何实际价值,只是讨论一些无聊的说法。

    有一个著名的帖子《Java关键字finalstatic使用总结》,里面有这样一句话:

注意:父类的private成员方法是不能被子类方法覆盖的,因此private类型的方法默认是final类型的。

    这句话的确值得注意,搞不明白一个表示可见性的关键字private和一个表示禁止覆盖的关键字final怎么扯上关系了。

    我说这句话是在java语义的角度看是错误的。有人说《Java编程思想》里这么说的,应该可信。如果你认为是错误的,如何证明?

    其实从语义角度很容易说明它们是不相关的了,证明也不难。我们知道,在class文件中,方法在常量池中有专门的项进行描述。

        method_info {

 

 

 

         u2 access_flags;

         u2 name_index;

         u2 descriptor_index;

         u2 attributes_count;

         attribute_info attributes[attributes_count];

    }

    其中access_flags描述方法的访问标志和属性,具体信息有:

Flag Name

Value

Interpretation

ACC_PUBLIC

0x0001

Declared public; may be accessed from outside its package.

ACC_PRIVATE

0x0002

Declared private; accessible only within the defining class.

ACC_PROTECTED

0x0004

Declared protected; may be accessed within subclasses.

ACC_STATIC

0x0008

Declared static.

ACC_FINAL

0x0010

Declared final; may not be overridden.

ACC_SYNCHRONIZED

0x0020

Declared synchronized; invocation is wrapped in a monitor lock.

ACC_NATIVE

0x0100

Declared native; implemented in a language other than Java.

ACC_ABSTRACT

0x0400

Declared abstract; no implementation is provided.

ACC_STRICT

0x0800

Declared strictfp; floating-point mode is FP-strict

 

    如果说“private类型的方法默认是final类型的”是正确的,那么自然,如果一个方法是private的,它的access_flags中应该存在

ACC_PRIVATEACC_FINAL,如果不存在ACC_FINAL则自然证明了该说法的错误。

   怎么看呢?找工具吧。

   http://www.ej-technologies.com/products/jclasslib/overview.html

   jclasslib是不错的工具。

   我们写一个简单的测试类:

public class Main {

    private void test(){}

    private final void test2(){}

}

编译,使用jclasslib打开Main.class文件。容易辨别。 

test():

test2():

 

    如果语言角度有“自动是……”这个语义的话,应该会自己添加相应的描述信息的。比如,我们说一个接口中的方法自动是public abstract 的,即使你不写,编译器也会自动添加的。比如下面例子:

public interface Main { void test(); }

 

 test()方法的描述符是:

 

    补充说明:在回复信息中,有一种说法是“private自动是final的”是为了内联优化。我们知道,final的方法可以内联(inline)优化,private的和static的也可以进行同样的优化。

这里要注意逻辑关系,private和static的可以进行内联优化,并不能说它们就是final的。其实final不过是语言级别的一个概念而已,它是内联的充分不必要条件。如果说,可以内联优化就是final的话,那么static的呢?可惜从语言角度我们无法说明private和final无关。只能从侧面说明一下:

class Base {
    public static final void test() {
    }
}

class Derived extends Base {
    public static void test() {
    }
}

    上面代码编译错误,因为final的存在。

    这起码能够说明,不要从内联角度来说“private方法自动为final的”。

 

 

    我感觉《Thinking In Java》上很多地方是值得推敲的。

    比如《Everything is an Object》一节,作者将存储区分为寄存器、栈、堆、常量区和非内存存储等5个部分,

当然,这是没有问题的。但是放到一个讲java的地方就有些奇怪了,毕竟java里提到的堆、栈一般来讲指的是面向

jvm的逻辑意义上的堆和栈,而且在这个层次上,内存的划分是由jvm规范定义的,包括pc寄存器、虚拟机栈、堆、

方法区、常量池、本地方法栈等。好像书里还提到过静态存储区的概念,这个jvm里也是不存在的。

   书里还指出“构造方法是static的”:

   Even though it doesn’t explicitly use the static keyword, the constructor is actually a

static method. So the first time an object of type Dog is created, or the first time a

static method or static field of class Dog is accessed, the Java interpreter must

locate Dog.class, which it does by searching through the classpath.

  

   实在搞不清楚作者为什么要说constructor是静态的,如果是的话,构造方法里还能用this吗

        此外还有一段争论“pass by value”了,作者在注释里有一段说明,我总感觉他有狡辩的意思。不好直接说作者的说法是错误的,不过感觉没有必要老在一些说法上标新立异。

       关于创建派生类对象,书里有这样的说法:When you create an object of the derived class, it contains within it a subobject of the base class.

This subobject is the same as if you had created an object of the base class by itself. It’s just that from the outside, the subobject of the base

class is wrapped within the derived-class object.

       好像别的资料里都没有“subobject”这样的说法。作者的这种想法估计是从C++来的。但我想java的设计者们并没有这么考虑。

首先,我们看在父类的构造函数中调用被子类覆盖的方法时的表现。在java中,我们不提什么“父类subobject”之类的说法,

创建的就是一个完整的对象,那就是派生类的对象,很自然的,在父类构造方法中调用的覆盖方法会是当前对象对应的重写过的方法,

因此此时就出现了多态现象。而不会根据“父类subobject”的类型去找方法。但是C++中确实是根据“父类subobject”的类型去找

方法的,因此不会出现多态现象。其次,在java中,实际的对象都需要包含一些标志信息,比如垃圾回收标志、线程锁标志等。

显然所谓的“父类subobject”是不会有这种信息的,从这个角度看,也没有必要非要制造“父类subobject”这样一个概念。

文字比较拗口,示例代码如下

public class Test {

    public static void main(String[] args) {

        new Derived();

    }

}

class Super {

    public Super() {

        test();

    }

    public void test() {

        System.out.println("Super");

    }

}

 

class Derived extends Super {

    public Derived() {

    }

    public void test() {

        System.out.println("Derived");

    }

}

 

    当然,学语言没有必要务求表达精确。这是个“度”的问题了。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值