首页 > java程序运行时是如何找到库函数并运行的?

java程序运行时是如何找到库函数并运行的?

题主是一名C/C++程序员,刚开始学习java。

疑惑如下:

  1. java没有头文件,当调用第三方包(无源码)的方法,编译器如何保证程序员使用了正确的原型呢?

  2. 定义包时,为了保证包名唯一,使用package a.b.c语法,包的类需要在文件系统的a/b/c目录下。但当其他java程序使用该包时,这个路径信息怎么在运行查找这个包时体现?

  3. 某c程序编译时依赖库liba.so,那么运行时系统必须提供库liba.so,但是java程序编译时使用abc.jar,但是运行时可以提供bcd.jar,只要bcd.jar里具有该java程序用到的类就行?(我自己瞎猜的,请问这个说法对不对?)

最后,java有没有类似于《链接、装载与库》这样的书籍,或者请大家推荐一些可以了解原理的书籍,能够解答这些困惑,中英文皆可。

感谢。


非常感谢大家的解答,试着将各位的回答总结整理如下:

  1. jar包里的class文件中含有文件原型。c/c++的libxxx.so/libxx.a中只有符号,没有原型,原型由头文件中提供;java的class文件中即含有原型信息。编译器只要解析class文件即可知道程序员是否使用了正确的原型。(@fredric_201 与 @心不在焉 )

  2. jar包即zip包,里面存在目录结构,该结构与包名结构完全一致(标准jar包,非android jar包)。(@心不在焉 与 @beanlam)

  3. 说法正确。java程序依赖的实际是class,jar包只是一组class的zip包,其命名无关紧要,因此可以任意修改。如果非要和c/C++进行对比,libxx.so类似于.class,而非jar包。(@心不在焉 与 @beanlam)

笔者在ubuntu机器上使用zipinfo查看openjdk自带的jar包,如下:

prife@droi: /usr/lib/jvm/java-7-openjdk-amd64/jre/lib
$ zipinfo rt.jar
...
-rw----     2.0 fat    24298 bl defN 15-Jul-24 08:17 java/lang/String.class
-rw----     2.0 fat     1734 bl defN 15-Jul-24 08:17 java/lang/Object.class

可以看到rt.jar包里具有跟包名完全一致的目录结构。

最后感谢大家推荐的书籍:

《深入理解java虚拟机》
《Java Virtual Machine Specification》

PS. 笔者之所以对第二点看到困惑,因为作为Android程序员,发现安卓的jar包里是只有dex文件,没有包名的目录结构。

再次感谢大家的解答。


我个人是这样理解的:
1、c/c++编译出的库和java生成jar包这两件事情是不等价的,jar包本质上只是一个压缩包;如果一定要类比,c/c++编译的过程应该相当于java生成class文件的过程,也就是代码转换成机器(或虚拟机)可以理解并执行的文件;
2、JAVA虚拟机会把目标class文件加载到内存里,其中类的信息应该放到“方法区”,被实例化的时候,引用在栈、实例在堆(和c/c++的malloc或new类似)。
3、每一个类是通过 类加载器 + 包名 + 类名 三部分信息在JVM中唯一标识;
4、比较好的书有《深入理解java虚拟机》、《osgi原理与最佳实践》;


1,Java的bytecode里面有方法的原型信息,编译时有class文件即可
2,Java运行时从classpath里面寻找jar文件,再在jar文件里面找包名对应的a/b/c.class
3,是这样的


请参考《Java Virtual Machine Specification》第五章:Chapter 5. Loading, Linking, and Initializing


看一本关于讲解jvm的书,具体的细节,看看类加载,《深入jvm虚拟机》。
1、:全类名定义一个类,每个类被加载一次。
2、:你是想问怎么找到吧,我觉得和全类名比较类似。
最后个我没懂你的意思


1,如果A.java依赖了xxx.jar包,在编译时(例如javac命令),需要在classpath把xxx.jar包含进去,编译器在编译A.java的时候,会去找xxx.jar包,看看是否有A所依赖的类,所依赖的类是否有对应的方法,等等。不知道这样是否是你所说的保证使用了正确的原型

2,Java要求包名必须有对应的文件系统结构,比如com.x.y这个包,就必须有相应的com/x/y/目录与它对应。这个路径信息在两方面有体现,一个是编译的时候,一个是运行的时候。
编译和运行都要指定classpath,可以把这个classpath类比成操作系统的环境变量path,比如我们把javac这个命令加到了path环境变量下,需要在把javac所在的目录加到path上。
classpath也一样,无论在运行还是编译的时候,如果你要引用一个类,比如说是com.a.b.String,那么首先你得有对应的目录结构com/a/b/。你可以把com文件夹随便放在硬盘中的某个目录下,比如说放在D:/test下,那么你需要在classpath里加上D:/test,这样,当要引用到com.a.b.String时,编译器或者Runtime会去classpath指定的目录里面去找,怎么找呢,根据包名,然后找对应的目录结构。
目录结构就在这里体现出它的作用。

3, 不用在意jar包的命名,因为jar包实际上是一个zip格式的文件,jar包也会被加到classpath下,Java虚拟机真正care的是jar包里面的目录结构,以此来确定包名和类的位置等。只要Jar包有对应的类就行,你可以对jar包任意改名。

4, Java跟C/C++不一样的就是Java是跑在Java虚拟机上的,所以了解一下底层的机制的话,十有八九都会往虚拟机那方面去看。C/C++的链接,装载,对应到Java里就是类加载器(ClassLoader)的原理。这方面的资料可以看《深入理解Java虚拟机》(周志明),或者Oracle官方的Java虚拟机规范(jvms.pdf),到官网找找就有。
实际上我觉得楼主的问题更多的是Java这门语言本身的规范,这个可以参考一些教程书,或者oracle官方的Java语言规范(jls.pdf),一样在Oracle官网找。

【热门文章】
【热门文章】