你好,我是 shengjk1,多年大厂经验,努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注!你会有如下收益:
- 了解大厂经验
- 拥有和大厂相匹配的技术等
希望看什么,评论或者私信告诉我!
一、 前言
这一篇是上一篇 关于 Python 的 import,你了解多少? 的姊妹篇。在上一篇中,我们详细论述了 Python import 关键字。本篇,将会详细比一下,两个特别有代表性的语言 Java 和 Python 中的 import 关键字同和异
二、不同点
两种语言的 import 的不同点有很多,比如:
-
语法:Java和Python中的
import
语句的语法不同。在Java中,import
语句通常以import
关键字开始,后面跟着要导入的类或包的全限定名。而在Python中,import
语句通常以import
关键字开始,后面跟着要导入的模块的名称。 -
编译时 vs 运行时:在Java中,
import
语句在编译时处理,编译器会解析导入路径、查找类文件并加载类。而在Python中,import
语句在运行时处理,解释器会查找模块文件并执行其中的代码。 -
名称冲突解决:在Java中,如果导入的类或包的名称与当前命名空间中的其他定义冲突,必须使用完整的类名或使用
import
语句的全限定名来区分。而在Python中,可以使用as
关键字为导入的模块或对象指定别名,以避免名称冲突。 -
静态绑定 vs 动态绑定:在Java中,类的导入是在编译时进行的,编译器会根据导入的类的类型进行静态绑定。而在Python中,模块的导入是在运行时进行的,解释器会根据导入的模块的名称进行动态绑定。
其他的很容易理解,这里我们重点对 第 2 和 3 条进行详细描述
2.1 import module or class
2.1.1 Python import module
在Python中,当一个模块被导入时,模块中的代码会被执行。然而,模块中的代码只会在第一次导入时执行一次。之后,如果再次导入同一个模块,Python解释器会直接使用已经加载的模块,而不会再次执行模块中的代码。
比如:创建test1.py
python
print("我是 test1,我被执行了")
def say_hello():
print("hello world test1")
再创建 test2.py
python
def say_hello():
from test import test1
test1.say_hello()
if __name__ == '__main__':
for i in range(10):
say_hello()
执行 test2.py 输出
text
我是 test1,我被执行了
hello world test1
hello world test1
hello world test1
hello world test1
hello world test1
hello world test1
hello world test1
hello world test1
hello world test1
hello world test1
2.1.2 Java import class
在Java中,当一个类被导入时,该类的代码不会被立即执行。类的执行是在实际使用该类的时候发生的。
当导入一个类时,Java编译器会在编译阶段检查类的语法和类型,并生成或获取对应的字节码文件。这个字节码文件包含了类的定义和方法的字节码指令,但不会立即执行。
在程序运行时,当使用该类创建对象、调用方法或访问静态成员时,Java虚拟机(JVM)会加载该类的字节码文件,并根据需要执行类的代码。
需要注意的是,类的加载和初始化是按需进行的,即当第一次使用某个类时才会发生。一旦类被加载和初始化,JVM会缓存该类的Class对象,以便后续的使用。这种机制可以提高类的加载和执行的性能。
例如,假设我们有一个名为MyClass
的类,并将其导入到另一个类中:
java
import com.example.MyClass;
public class AnotherClass {
public static void main(String[] args) {
System.out.println("Before creating an instance of MyClass");
MyClass obj = new MyClass(); // 创建MyClass的实例
System.out.println("After creating an instance of MyClass");
obj.someMethod(); // 调用MyClass的方法
}
}
在上述代码中,当AnotherClass
被执行时,会先打印Before creating an instance of MyClass
,然后创建MyClass
的实例,并打印After creating an instance of MyClass
。这时,MyClass
的构造函数会被调用。
如果MyClass
中有其他静态成员或静态初始化块,它们也会在类被加载时执行一次。
总结起来,当一个Java类被导入时,类的代码不会立即执行。类的加载和初始化是在实际使用该类的时候按需发生的。类的加载和执行是由Java虚拟机(JVM)在程序运行时控制的。
2.2 import package
2.2.1 Python import package
在Python中,当导入一个包(package)时,包本身不会被执行。只有在使用该包中的模块或对象时,才会执行相应的代码。
当导入一个包时,Python解释器会查找并执行该包下的__init__.py
文件 。__init__.py
文件可以包含一些初始化代码,用于设置包的环境、导入子模块或执行其他需要在导入时执行的操作。但这些代码只会在首次导入包时执行一次。 例如,假设我们有一个名为my_package
的包,它的结构如下:
markdown
my_package/
__init__.py
module1.py
module2.py
在__init__.py
文件中,我们可以放置一些初始化代码:
python
print("Executing initialization code in my_package")
# 导入子模块
from . import module1
from . import module2
然后,我们可以在另一个脚本中导入my_package
包:
python
import my_package
print("Import completed")
运行上述脚本,输出如下:
css
Executing initialization code in my_package
Import completed
2.2.2 Java import package
在Java中,当一个包(package)被导入后,并不会立即加载和执行其中的类。类的加载和执行是在实际使用该类时进行的。
具体来说,当使用一个类时(例如创建该类的实例、调用该类的静态方法、访问该类的静态字段等),Java虚拟机(JVM)会根据需要进行类的加载和初始化操作。类的加载是指将类的字节码加载到内存中,并创建对应的Class对象。类的初始化是指执行类的静态初始化器和静态变量的赋值操作。
类的加载和初始化是按需进行的,即当第一次使用某个类时才会发生。一旦类被加载和初始化,JVM会缓存该类的Class对象,以便后续的使用。
需要注意的是,Java的类加载是按需延迟的,即在类被首次使用之前,它的加载和初始化都不会发生。这种延迟加载的机制可以提高性能,避免不必要的类加载和初始化操作。
3. import 时各自的解释器/编译器在做什么( 只是一个大概的过程,并不是严格正确,主要是为了方便理解 )
3.1 Python
在Python中,当解释器遇到import
语句时,它执行以下几个步骤:
-
解析导入路径 :解释器会解析
import
语句中指定的模块的路径。这个路径可以是相对路径或绝对路径,并且可以包含多个层级和包名。 -
查找模块 :解释器会根据解析的路径查找对应的模块文件(以
.py
或.pyc
形式存在)。它会按照一定的搜索顺序查找模块,包括当前目录、Python标准库目录和自定义模块路径等。 -
编译模块 :一旦找到模块文件,解释器会对模块进行编译,将其转换为字节码形式。对于首次导入的模块,解释器会生成对应的
.pyc
文件,以便提高后续导入的性能。 -
创建命名空间:解释器会为导入的模块创建一个独立的命名空间,用于存储模块中定义的变量、函数和类等。这样可以避免命名冲突,并允许代码通过模块名来引用其中的对象。
-
执行模块代码:解释器会执行模块中的代码,按照顺序执行模块中的语句和定义的函数、类等。这样,模块中的变量和函数会被加载到命名空间中,可以在代码中直接访问和使用。
-
返回模块对象:当模块中的代码执行完毕后,解释器会返回一个代表该模块的模块对象。这个模块对象可以被赋值给变量,以便在代码中使用模块中的功能。
需要注意的是,Python解释器在首次导入模块时,会执行模块中的代码并创建对应的命名空间。但在后续的导入过程中,解释器会直接使用已经编译和加载的模块对象,而不会再次执行模块中的代码。
总结起来,Python解释器在遇到import
语句时会解析导入路径,查找并编译模块,创建命名空间,并执行模块中的代码。这样,模块中的变量、函数和类等会被加载到命名空间中,可以在代码中直接访问和使用。
3.2 Java
在Java中,import
语句的作用是引入其他类或包,以便在代码中使用这些类或包中定义的功能。当Java解释器遇到import
语句时,它会执行以下几个步骤:
- 解析import语句: Java解释器会解析
import
语句,获取导入的类或包的名称。例如,import java.util.ArrayList;
将导入java.util
包中的ArrayList
类。 - 查找类或包: 解释器会查找导入的类或包,以确定其位置。它首先搜索Java类路径(包括标准库和自定义库),然后在指定位置查找。如果找到了类或包,解释器将继续执行下一步。否则,将会抛出
ClassNotFoundException
或NoClassDefFoundError
等异常。 - 加载类或包: 如果找到了类或包,解释器将加载它们,同时也会创建相应的命名空间。加载过程包括加载字节码文件、验证字节码的正确性以及将字节码转换为内存中的可执行代码。如果导入的是一个包,解释器将加载包中的所有类。
- 建立符号引用: 解释器建立与导入的类或包的符号引用,以便在代码中引用它们。这允许你使用导入的类或包的名称而不必使用完全限定的类名。
- 解析符号引用: 当你在代码中使用导入的类或包时,解释器会解析符号引用,以确定它们的具体含义。例如,如果你在代码中使用
ArrayList
,解释器会解析它为导入的java.util.ArrayList
类。
需要注意的是,import
语句本身并不会导致类的加载或执行,它只是告诉解释器需要访问某个类或包。类的加载和初始化是在实际使用该类时按需发生的,如创建对象、调用方法等。
一旦类被加载和初始化,JVM会缓存该类的Class对象,以便后续的使用。这样,在多次使用同一个类时,解释器不需要重新加载和初始化,而是直接使用缓存的Class对象。
三、相同点
- 功能:无论是 Java 还是 Python ,
import
语句的主要功能都是引入其他类或模块,以便在代码中使用它们的功能。 - 命名空间:
import
语句将被导入的类或模块的定义引入到当前命名空间中,以便在代码中直接访问它们。 - 都是 关键字
四、总结
Python和Java作为流行的编程语言,都提供了import机制来引入外部代码模块/类。尽管在具体语法、导入时机、执行细节等方面有一定区别,但二者的import语句都具有引入外部代码、扩展功能、统一命名空间的作用,可以增强代码的模块化和可重用性。掌握import的用法对于合理组织和复用代码至关重要。