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

无聊:谁说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");

    }

}

 

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

  • 大小: 7 KB
  • 大小: 5.5 KB
  • 大小: 5.3 KB
分享到:
评论
7 楼 ZangXT 2009-09-13  
凤舞凰扬 写道
   所以在java的早期版本中使用private final来表达,从而提升性能。而在java2后,private默认也就是final的了,不需要在加上final的描述,如果接口方法不需要再加上public abstract的描述一样。如果楼主用SUN也推荐的findbox,你就可以看到相关的警告描述。

static方法也可以内联优化,显然static的无法添加final限定,因为添加了就改变了程序的语义。所以从内联角度无法说明这个问题。可以参考上面原文的修改。

findbox是不是findbug的笔误?
6 楼 jiyanliang 2009-09-11  
狂奔蜗牛 写道
读理科的?我看文也写的不错呀,你经常看鲁迅的作品吧!

你看错了吧,我说的是物理。当时c盛行。况且我也没有贬低他的意思,我只是实话实说罢了。。
5 楼 ZangXT 2009-09-11  
的字节码中又是否会有
凤舞凰扬 写道
   首先要给楼主说明一下情况,Java生成的字节码自然是按照代码本身的定义来进行的(比如private方法没有定义final,也就不会有对应的字节码产生,这丝毫不代表private是final的,如果楼主有兴趣,看看接口方法生产的字节码中又是否会有ACC_PUBLIC何ACC_ABSTRACT这样的信息呢)。其实这个区别是来自于c++中内联函数(inline)的概念,而在java中没有这样的概念,所以在java的早期版本中使用private final来表达,从而提升性能。而在java2后,private默认也就是final的了,不需要在加上final的描述,如果接口方法不需要再加上public abstract的描述一样。如果楼主用SUN也推荐的findbox,你就可以看到相关的警告描述。
 
引用
而且在这个层次上,内存的划分是由jvm规范定义的,包括pc寄存器、虚拟机栈、堆、方法区、常量池、本地方法栈等。好像书里还提到过静态存储区的概念,这个jvm里也是不存在的。
我如果没有理解错的话,这里说的静态存储区应该就是permgen,因为在前面的语句中刚好就漏了它。所以楼主说它不存在,要看你怎么理解这个翻译了。

   至于后面的,晦涩,没看懂,不发表意见

接口的方法是有ACC_PUBLIC和ACC_ABSTRACT标记的。所以这个比较类比不能成立。

如果从“语义”的角度将,认为private的方法有类似final的功能,这个我是赞同的。如果从语言角度这么提,我认为是毫无道理的。从方法绑定的角度看,invokespecial调用private方法时,可以根据引用的类型绑定方法。所谓的“final“语义,似乎就是指这一点了。

在SUN的jvm里,所谓的“静态数据区”应该是对应PermGen的,或者是PermGen的一部分。这跟翻译无关,只是我不喜欢书里的表达而已。

findbox我没看过,谢谢提示。
4 楼 凤舞凰扬 2009-09-10  
   首先要给楼主说明一下情况,Java生成的字节码自然是按照代码本身的定义来进行的(比如private方法没有定义final,也就不会有对应的字节码产生,这丝毫不代表private是final的,如果楼主有兴趣,看看接口方法生产的字节码中又是否会有ACC_PUBLIC何ACC_ABSTRACT这样的信息呢)。其实这个区别是来自于c++中内联函数(inline)的概念,而在java中没有这样的概念,所以在java的早期版本中使用private final来表达,从而提升性能。而在java2后,private默认也就是final的了,不需要在加上final的描述,如果接口方法不需要再加上public abstract的描述一样。如果楼主用SUN也推荐的findbox,你就可以看到相关的警告描述。
 
引用
而且在这个层次上,内存的划分是由jvm规范定义的,包括pc寄存器、虚拟机栈、堆、方法区、常量池、本地方法栈等。好像书里还提到过静态存储区的概念,这个jvm里也是不存在的。
我如果没有理解错的话,这里说的静态存储区应该就是permgen,因为在前面的语句中刚好就漏了它。所以楼主说它不存在,要看你怎么理解这个翻译了。

   至于后面的,晦涩,没看懂,不发表意见
3 楼 狂奔蜗牛 2009-09-09  
读理科的?我看文也写的不错呀,你经常看鲁迅的作品吧!
2 楼 ZangXT 2009-09-05  
jiyanliang 写道
Bruce Eckel本科是读物里的,后来才转过来读软件工程,可以说是半路出家吧

很多读物理然后去搞计算机的,特别猛。Bruce Eckel应该是C++标准委员会的成员。
1 楼 jiyanliang 2009-09-05  
Bruce Eckel本科是读物里的,后来才转过来读软件工程,可以说是半路出家吧

相关推荐

    java八股文.docx "Java八股文"通常指的是一种简单的模板化回答,用于解答关于Java编程语言的基础问题,特别是在面试

    "Java八股文"通常指的是一种简单的模板化回答,用于解答关于Java编程语言的基础问题,特别是在面试中常被问到的问题。这些问题通常覆盖了Java语言的基本概念、特性、语法以及一些常见的面向对象编程原则。以下是一些...

    Java学习笔记---15.面向对象编程10-Java中final关键字,抽象类与接口

    在Java中, 可以使用final关键字修饰类、方法以及成员变量。 (1).final标记的类不能被继承; (2).final标记的方法不能被子类复写; (3).final标记的变量即成为常量,只能被赋值一次. 注意: 如果使用final来...

    Java中的final关键字详解及实例

    Java中的final关键字 1、修饰类的成员变量 这是final的主要用途之一,和C/C++的const,即该成员被修饰为常量,意味着不可修改。   上面的代码对age进行初始化后就不可再次赋值,否则编译时会报类似上图的错误。 ...

    JAVA面试题最全集

    一个“.java”原文件中是否可以包括多个类(不是内部类)? 53.掌握内部类和接口的概念 54.StringTokenizer类的使用 55.数据结构,如何遍历List中的元素? 如果要按照键值保存或者访问数据,使用什么数据结构? ...

    java面试宝典

    42、一个“.java”源文件中是否可以包含多个类(不是内部类)?有什么限制? 12 43、说出一些常用的类,包,接口,请各举5 个。 12 44、Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类?是否可以...

    77.java中的private.zip

    77.java中的private.zip77.java中的private.zip77.java中的private.zip77.java中的private.zip77.java中的private.zip77.java中的private.zip77.java中的private.zip77.java中的private.zip77.java中的private.zip77...

    JAVA入门1.2.3:一个老鸟的JAVA学习心得 PART1(共3个)

    11.3.5 private:仅对本类可见 303 11.3.6 理解4个访问控制符 304 11.3.7 访问控制符可见性汇总 306 11.3.8 访问控制符带来的覆盖问题 306 11.3.9 final:不允许方法被覆盖 310 11.3.10 重温静态方法 311 11.3...

    Thinking in Java 中文第四版+习题答案

    5.2.3 private:不能接触 5.2.4 protected:“友好的一种” 5.3 接口与实现 5.4 类访问 5.5 总结 5.6 练习 第6章 类再生 6.1 合成的语法 6.2 继承的语法 6.2.1 初始化基础类 6.3 合成与继承的结合 6.3.1 确保正确的...

    java 面试题 总结

    内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的...

    Java入门1·2·3:一个老鸟的Java学习心得.PART3(共3个)

    11.3.5 private:仅对本类可见 303 11.3.6 理解4个访问控制符 304 11.3.7 访问控制符可见性汇总 306 11.3.8 访问控制符带来的覆盖问题 306 11.3.9 final:不允许方法被覆盖 310 11.3.10 重温静态方法 311 11.3...

    java 编程入门思考

    5.2.3 private:不能接触 5.2.4 protected:“友好的一种” 5.3 接口与实现 5.4 类访问 5.5 总结 5.6 练习 第6章 类再生 6.1 合成的语法 6.2 继承的语法 6.2.1 初始化基础类 6.3 合成与继承的结合 6.3.1 确保正确的...

    初级Java程序员面试题

    JDK 和 JRE 有什么区别? JDK(Java Development Kit),Java开发工具包 ...private类型的方法默认是final的; java 中的 Math.round(-1.5) 等于多少? Math提供了三个与取整有关的方法:ceil、floor、round

    java private data

    工程中,包PrivateData中的PrivateData.java是程序入口。 如果你要运行,打开后要求输入用户名和密码,只要输入用户名cletor,密码123456即可。 问题描述:本程序使用Access数据库。在添加“分类”时,插入新记录...

    Java编程中常用修饰词使用方法

    Java语言定义了public、protected、private、abstract、static和final这6常用修饰词外还定义了5个不太常用的修饰词,本文简单介绍了这11个Java修饰词。

    面向对象修饰符public private protest super final abstract理解

    public private protest super final abstract this 等用法

    Java 语言基础 —— 非常符合中国人习惯的Java基础教程手册

    在 java 语言中,Java 程序的基本单位是类,也就是说:一个 Java 程序是由多个类组成 的。定义一个类与定义一个数据类型是有区别的。在程序设计语言中,把定义数据类型的能 力作为一种很重要的能力来对待。在面向...

    JAVA_API1.6文档(中文)

    java.sql 提供使用 JavaTM 编程语言访问并处理存储在数据源(通常是一个关系数据库)中的数据的 API。 java.text 提供以与自然语言无关的方式来处理文本、日期、数字和消息的类和接口。 java.text.spi java.text ...

    java 面试常问的问题 如何回答

    1、一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制? 7 2、Java有没有goto? 7 3、说说&和&&的区别。 8 4、在JAVA中如何跳出当前的多重嵌套循环? 8 5、switch语句能否作用在byte上,能否作用在...

    java面试过程当中遇到的一些题目

    7. 一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制? 10 8. 排序都有哪几种方法?请列举。用JAVA实现一个快速排序? 10 9. Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型...

    Java面试攻略面试宝典常见面试题及答案

    Java中是否可以覆盖一个private或者是static的方法? 如果一个类的变量或者方法前面有static修饰,那么表明这个方法或者变量属于这个类,也就是说可以在不创建对象的情况下直接使用 当父类的方法被private修饰时,...

Global site tag (gtag.js) - Google Analytics