package,import,include的联系与区别【好】

本文深入解析Java的package和import机制,解释了package的作用、import的使用方式及与C/C++的#include的区别,详细说明了如何通过import简化代码书写,避免命名冲突,并讨论了import的类型导入和按需导入,最后提供了Java编译器处理import的细节。

转自:http://hi.baidu.com/4472797074/item/528df61c73dc83f864eabf7c

有些人写了一阵子 Java,可是对於 Java 的 package 跟 import 还是不
太了解很多人以為原始码 .java 档案中的 import 会让编译器把所 import
的程式通通写到编译好的 .class 档案中,或是认為 import 跟 C/C++ 的
#include 相似,实际上,这是错误的观念。
让我们先了解一下,Java 的 package 到底有何用处。
其实,package 名称就像是我们的姓,而 class 名称就像是我们的名字
。package 名称有很多 . 的,就好像是复姓。比如说 java.lang.String,就
是复姓 java.lang,名字為 String 的类别;java.io.InputStream 则是复姓
java.io,名字為 InputStream 的类别。
Java 会使用 package 这种机制的原因也非常明显,就像我们取姓名一样
,光是一间学校的同一届同学中,就有可能会出现不少同名的同学,如果不取
姓的话,那学校在处理学生资料,或是同学彼此之间的称呼,就会发生很大的
困扰。相同的,全世界的 Java 类别数量,恐怕比台湾人口还多,而且还不断
的在成长当中,如果类别不使用套件名称,那在用到相同名称的不同类别时,
就会產生极大的困扰。幸运的是,Java 的套件名称我们可以自己取,不像人
的姓没有太大的选择 ( 所以有很多同名同姓的 ),如果依照 Sun 的规范来取
套件名称,那理论上不同人所取的套件名称不会相同 ( 请参阅 "命名惯例"
的相关文章 ),也就不会发生名称衝突的情况。
可是问题来了,因為很多套件的名称非常的长,在写程式时,会多打好多
字,花费不少时间,比如说:

java.io.InputStream is = java.lang.System.in;
java.io.InputStreamReader isr= new java.io.InputStreamReader(is);
java.io.BufferedReader br = new java.io.BufferedReader(isr);

实在是不美观又麻烦。於是,Sun 想了一个办法,就是 import。
这个 import 就是在程式一开头的时候,先说明程式中会用到那些类别的
简称,也就是只称呼名字,不称呼他的姓。首先,在档案开头写:

import java.lang.System;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;

这几行说明了这四个姓名的类别,在程式中只用他的名字来称呼,所以当程式
中提到 System 就是指 java.lang.System,而 InputStream 就是指
java.io.InputStream,依此类推。於是原来的程式就变成:

InputStream = System.in;
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);

这样看起来是不是清爽多了呢?如果这些类别用的次数很多,那就更能体会到
import 的好处了。可是这样还是不够,因為懒是人的天性,还是会有人觉得
打太多 import 了也很浪费时间,於是 Sun 又提供了一个方法:

import java.lang.*;
import java.io.*;

意思就是,等一下程式中提到的没有姓名的类别,不是姓 java.lang,就是姓
java.io,如果这两个裡面有同样名字的类别,而不幸的你又只用名字称呼这
个类别,那编译器仍然会跟你抱怨,因為它还是不知道你说的这个类别指那一
个姓的类别。那可不可以再懒一点呢,只写:

import java.*;

歷史告诉我们,人可以懒,但不能太懒,这样是不行的。因為那些类别是姓
java.io 而不是姓 java。就像姓『诸葛』的人应该不会喜欢你称他為『诸』
先生吧。
為甚麼我一开始说 import 跟 #include 不同呢?因為 import 的功能
到此為止,它不像 #include 一样,会将档案内容载入进来。import 只是请
编译器帮你打字,让编译器把没有姓的类别加上姓,并不会把别的档案的程
式码写进来。
如果你想练习打字,可以不要使用 import,只要在用到类别的
时候,用它的全部姓名来称呼它就行了(就像例子一开始那样),跟使用
import 完全没有甚麼两样。
另外,虽然人不可以太懒,但是 Sun 还是帮我们多偷了一点懒。因為
java.lang 这个套件实在是太常太常太常用到了,几乎没有程式不用它的,
所以不管你有没有写 import java.lang;,编译器都会自动帮你补上,也就
是说编译器只要看到没有姓的类别,它就会自动去 java.lang 裡面找找看,
看这个类别是不是属於这个套件的。所以我们就不用特别去
import java.lang 了。
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
import导入声明可分为两中:
1>单类型导入(single-type-import)
例:import java.util.ArrayList;
2>按需类型导入(type-import-on-demand)
例:import java.util.*;
以这样两种方式导入包中的任何一个public的类和接口(只有public类和接口才能被导入)
*导入声明仅导入类型而不导入子包;这就是为什么称它们为单类型导入和按需类型导入声明的原因.
*导入的类或接口的简名(simple name)具有编译单元作用域.这表示该类型简名可以在导入语句所在的编译单元的任何地方使用.这并不意味着你可以使用该类型所有成员的简名,而只能使用类型自身的简名.
例如: java.lang包中的public类都是自动导入的,包括Math和System类.但是,你不能使用简名PI()和gc(),而必须使用Math.PI()和System.gc().你不需要键入的是java.lang.Math.PI()和java.lang.System.gc().
程序员有时会导入当前包或java.lang包,这是不需要的,因为当前包的成员本身就在作用域内,而java.lang包是自动导入的.java编译器会忽略这些冗余导入声明(redundant import declarations).即使像这样
import java.util.ArrayList;
import java.util.*;
多次导入,也可编译通过.编译器会将冗余导入声明忽略.
使用按需导入声明是否会降低Java代码的执行效率?绝对不会!
Java编译器产生的类文件仅包含编译单元实际使用到的类或接口的符号引用.
这是否意味着你总是可以使用按需导入声明?是,也不是!
在类似Demo的非正式开发中使用按需导入声明显得很有用.
然而,有这四个理由让你可以放弃这种声明:
1>编译速度:在一个很大的项目中,它们会极大的影响编译速度.但在小型项目中使用在编译时间上可以忽略不计.
2>命名冲突:解决避免命名冲突问题的答案就是使用全名.而按需导入恰恰就是使用导入声明初衷的否定.
3>说明问题:全名的使用是自说性的.毕竟高级语言的代码是给人看的.
4>无名包问题:如果在编译单元的顶部没有包声明,Java编译器首选会从无名包中搜索一个类型,然后才是按需类型声明.如果有命名冲突就会产生问题.
Sun的工程师一般不使用按需类型导入声明.这你可以在他们的代码中找到:
在java.util.Properties类中的导入声明:
import java.io.IOException;
import java.io.printStream;
import java.io.printWrite;
import java.io.InputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.util.Hashtable;
你可以看到有趣的是,她连java.util.Hashtable也导入,这可是在当前包中啊!

<think>好的,我现在需要回答用户关于SystemVerilog中importinclude区别的问题。首先,我需要回忆这两个关键字的基本用法作用,然后结合用户提供的引用资料来验证补充我的理解。 首先,include在SystemVerilog中的作用类似于C语言中的#include,它是一个预处理指令,用于将另一个文件的内容直接插入到当前文件中。这通常用于包含头文件、宏定义或者其他需要在多个文件中共享的代码。例如,用户可能在多个模块中需要相同的定义,通过include可以避免重复编写代码,提高代码复用性。引用[2]中提到,include是用来包含其他文件的内容,而import包(package)相关。 接下来,import关键字用于从包中导入特定的定义。SystemVerilog中的包(package)是一种封装机制,可以将相关的类型、类、函数等组织在一起。当需要在模块或其他包中使用这些定义时,需要使用import语句来引入。例如,引用[4]中提到的包clock_mon_vif_xrtl_pkg,其中定义的类xrtl_clock_mon_vif_c可以通过import导入到其他模块中使用。import允许有选择地导入包中的内容,比如使用import package_name::item_name来导入特定项,或者使用import package_name::*来导入所有内容。 两者的主要区别在于作用机制应用场景。include是文本替换,发生在预处理阶段,直接将文件内容插入当前位置,可能导致命名冲突或重复定义的问题。而import则是用于访问包中的符号,属于语言层面的导入机制,不会引入重复定义,因为包中的内容在编译时已经被处理。引用[2][4]都强调了import在包使用中的必要性,而引用[1]提到类型系统可能导入方式有关,尤其是在强类型弱类型系统中的差异。 此外,include通常用于包含结构体定义、宏、接口等,而import专门用于从包中导入类、类型、函数等。例如,引用[3]中提到的C函数导入可能import有关,但具体到SystemVerilog,import更多是处理包内的元素。而include则可能用于包含一些通用的代码片段,如UVM库中的基类,如引用[5]提到的UVM验证平台。 用户可能混淆两者的使用场景,比如试图用include来导入包中的内容,这会导致错误。正确的做法是使用import来访问包中的定义。例如,如果有一个包定义了某个类,其他模块需要使用该类时,必须通过import语句导入,而不是include整个包文件。这样可以避免不必要的代码重复潜在的命名冲突。 总结来说,include是预处理指令,用于文件内容的包含import是语言关键字,用于包中元素的导入。理解这两者的区别有助于编写更清晰、可维护的SystemVerilog代码,尤其是在大型项目中管理代码结构依赖关系时尤为重要。</think>在SystemVerilog中,`import``include`是两个完全不同的机制,其核心区别如下: --- ### 1. **`include`:文本替换** - **作用**:通过预处理指令将文件内容直接插入当前文件,类似于C语言的`#include`。 - **应用场景**: - 共享宏定义、结构体、接口等跨模块代码。 - 例如:`include "uvm_macros.svh"`用于插入UVM宏定义[^5]。 - **特点**: - **文本级替换**:可能导致命名冲突或重复定义。 - **无作用域隔离**:插入的代码对当前文件全局可见。 - **示例**: ```systemverilog `include "common_defines.svh" // 插入文件内容 ``` --- ### 2. **`import`:包内符号导入** - **作用**:从已定义的`package`中导入特定符号(类、函数、变量等)。 - **应用场景**: - 访问封装在包中的类型或工具类,如验证环境中的配置类[^4][^5]。 - 例如:`import my_pkg::*`导入包内所有定义。 - **特点**: - **需先定义包**:包需通过`package...endpackage`定义[^4]。 - **作用域控制**:可通过`import my_pkg::class_name`选择性地导入。 - **示例**: ```systemverilog package my_pkg; class A; // ... endclass endpackage module top; import my_pkg::A; // 仅导入类A A obj = new(); endmodule ``` --- ### 3. **关键对比表 | **特性** | **`include`** | **`import`** | |------------------|-----------------------------------|-----------------------------------| | **作用阶段** | 预处理阶段(文本替换) | 编译阶段(符号解析) | | **内容类型** | 任意文本(宏、代码片段等) | 包内定义的符号(类、函数等) | | **重复定义风险** | 高(直接插入可能冲突) | 低(包内符号唯一) | | **依赖关系** | 文件路径依赖 | 包名作用域依赖 | --- ### 4. **常见误区** - **错误用法**:试图用`include`导入包(如`include "my_pkg.sv"`),会导致符号全局可见,破坏封装性。 - **正确实践**: - 定义包:`package my_pkg; ... endpackage`[^4]。 - 导入包:`import my_pkg::symbol;`。 - 需要时`include`非包内容(如接口或宏)。 --- ### 5. **典型应用示例** ```systemverilog // 文件:my_pkg.sv package my_pkg; typedef enum {READ, WRITE} cmd_t; endpackage // 文件:tb.sv `include "my_interface.svh" // 插入接口定义 import my_pkg::cmd_t; // 导入包中的类型 module tb; cmd_t command = READ; // 直接使用包内类型 my_interface intf; // 使用include插入的接口 endmodule ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值