我是靠谱客的博主 疯狂电源,最近开发中收集的这篇文章主要介绍Java的package和import机制,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一些人用了一阵子的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 的规范来取套件名称,那理论上不同人所取的套件名称不会相同 ( 请参阅 "命名惯例"的相关文章 ),也就不会发生名称冲突的情况。

  可是问题来了,因为很多套件的名称非常的长,在写程序时,会多打好多字,花费不少时间,比如说在A.B.C文件下有Point和Circle两个类,现在在程序中要调用:

     A.B.C.Point  P1=new A.B.C.Point();

     A.B.C.Circle  C1=new A.B.C.Circle();

实在是不美观又麻烦.于是,Sun 想了一个办法,就是 import. 就是在程序一开头的时候,说明程序中会用到那些类的路径.首先,在档案开头写:

     import A.B.C.Point;

     import A.B.C.Circle;

这两行说明了类的路径,所以当程序中提到Point就是指A.B.C.Point,而Circle就是指A.B.C.Circle,依此类推。于是原来的程序就变成:

     Point  P1=new Point();

     Circle  C1=new Circle();  

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

    import A.B.C.*; 意思就是,等一下程序中提到的没有姓名的类别,全都包含在A.B.C这个目录中。

  注意点:但我们在程序中经常使用System.out这个类,为什么没有import System.out呢,因为java.lang 这个套件实在是太常太常太常用到了,几乎没有程序不用它的,所以不管你有没有写 import java.lang;,编译器都会自动帮你补上,也就是说编译器只要看到没有姓的类别,它就会自动去 java.lang 里面找找看,看这个类别是不是属于这个套件的。所以我们就不用特别去import java.lang 了。

   为甚么我一开始说 import 跟 #include 不同呢?因为 import 的功能到此为止,它不像 #include 一样,会将档案内容载入进来。import 只是请编译器帮你打字,让编译器把没有姓的类别加上姓,并不会把别的文件的程式码写进来。如果你想练习打字,可以不要使用 import,只要在用到类别的时候,用它的全部姓名来称呼它就行了(就像例子一开始那样),跟使用 import 完全没有甚么两样。

 

                                 先介绍Java的Package机制

基本原则:需要将类文件切实安置到其所归属之Package所对应的相对路径下。

例如:以下面程序为例:假设此Hello.java文件在D:Java下
package  A;
public class Hello{
  public static void main(String args[]){   
     System.out.println("Hello World!");
  }
}

D:Java>javac  Hello.java  此程序可以编译通过.接着执行。
D:Java>java  Hello       但是执行时,却提示以下错误!
Exception in thread "main" java.lang.NoClassDefFoundError: hello (wrong name: A/Hello)
        at java.lang.ClassLoader.defineClass0(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:537)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:123)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:251)
        at java.net.URLClassLoader.access$100(URLClassLoader.java:55)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:194)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:187)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:289)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:274)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:235)
        at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:302)

原因是我们把生成的Hello.class规定打包在D:JavaA文件中,必须在A文件中才能去运行。所以应该在D:Java目录下建立一个A目录,然后把把Hello.class放在它下面,执行时,可正常通过!
D:Java>java A.hello   就会输出:Hello world!

 

                              现在介绍Java的import机制

我们在D:Java目录下建立一个JInTian.java文件,其内容如下:
import  A.Hello;
public class JInTian{
   public static void main(String[] args){
       Hello  Hello1=new Hello();
     }
}

D:Java>javac JInTian.java   编译成功!

D:Java>java  JInTian      运行成功!

也就是你在JInTian.class中成功的引用了Hello.class这个类,是通过import A.Hello来实现的,如果你没有这句话,就会提示不能找到Hello这个类。

提示1:如果你在D:Java目录下仍保留一个Hello.java文件的话,执行对主程序的编译命令时仍会报错!你自己可以试试呀!
提示2:如果你删除D:JavaAHello.java文件的话,只留Hello.class文件,执行对主程序的编译命令时是可以通过,此时可以不需要子程序的源代码。
提出一个问题:如果把目录A剪切到其它目录,如D盘根目录下,在A目录如果执行编译和执行命令呢?
很明显,会报以下错误!当然了,前提条件是你没有设置classpath路径,其实只要没把类搜索路径设置到我这个位置就会出错的!你试试吧!



由于大家对package的使用存在太多困惑,我在这里将自己对于package的使用的领悟进行一点总结:

  package中所存放的文件

  所有文件,不过一般分一下就分这三种

  1,java程序源文件,扩展名为.java。

  2,编译好的java类文件,扩展名为.class。

  3,其他文件,其他任何文件,也称为resource

  例如图片文件,xml文件,mp3文件,avi文件,文本文件……

  package是什么

  package好比java用来组织文件的一种虚拟文件系统。package把源代码.java文件,.class文件和其他文件有条理的进行一个组织,以供java来使用。package是将文件组织在一颗类似unix,linux文件系统的树结构里面,它有一个根"/",然后从根开始有目录和文件,目录中也还有文件和目录。

  package怎么实现的呢?

  源代码的要求最严格,而一旦源代码自己声明了在哪个package路径之下,class也就有了自己在哪个package下面的信息,就是那句程序开头的"package xx.xx.xx"。有人问,为什么要有这个信息,直接放目录结构里不就好了么?是啊,直接放目录中确实可以找到.class和.java,但是如果我要输出这个.class是属于哪个package的,该怎么办?所以我们需要在.class里面留一个package的信息。如果我们要区分同样名称为A.class的类怎么办?所以我们需要在.class里面留一个package的信息。

  .java文件是一个独立的编译单元,类似c++里面的cpp文件,但是它不需要.h文件,只要.java就足够了,一个.java文件里面可以包含一个public的类,若干package类(package类特征是没有任何访问控制修饰),还有内隐类的话,则还可以包含若干protected和private的类。每个类,都会在编译的时候生成一个独立的.class文件,所以.java和.class不是一对一,而是一对多的关系,不过.java和public的类是一对一的。所有这些.class,都由这个.java开头的package语句来确定自己在package中的位置。

  package xx.bb.aa;

  说明这个.java编译单元中的所有类都放到xx.bb.aa这个package里面。而对应的,必须把这个.java文件放在xx目录下bb目录下的aa目录里面。如果一个.java文件没有任何package语句,那么这个.java里面的所有类都在package的"/"下面,也称之为default package。可以看出你一般从任何java教科书上写的第一个hello world程序的那个类是在defaultpackage里面的。有了package语句,情况就复杂一点了。这个编译单元.java必须放在package名对应的目录之下。而生成的class文件也要放在对应的目录结构之下才能正常运作。

  例如:

    /* A.java */ 
  package aaa.bbb.ccc; 
  public class A{ 
  B b=new B(); 
  } 
  /* B.java*/ 
  package aaa.bbb.ccc; 
  public class B{}

  编译时候怎么填参数呢?我根据package+文件名的格式来写,

  javac aaa.bbb.ccc.A.java

  漂亮吧?可惜不工作。非要使用合法的路径名才行:

  javac aaa/bbb/ccc/A.java

  但是你发现生成的class丢失了目录结构,直接出现在当前目录下……

  最好的方式是

  javac -d bin aaa/bbb/ccc/A.java

  这样就会在当前目录的bin目录下看到完整的目录结构以及放置妥当的class文件。

  package与classpath不得不说的事

  对于java来讲,所有需要的程序和资源都要以package的形式来组织和读取。

  那么classpath又是什么呢?

  所有放到设定到classpath里面的东西就是package所包纳的资源。classpath的写法如同path,只是里面可以写的一般只有zip文件、jar文件和目录。多个元素之间用当前系统路径分隔符间隔开了,linux上分隔符号是":",windows上是";"。classpath在java里面是被一个叫做classloader的东西所使用的,classloader顾名思义,就是load class用的,但它也可以load其它在package里面的东西。现在的java里面classloader是有阶层关系的,一般我们所常接触到的CLASSPATH环境变量,javac,java的-cp,-classpath参数所给的classpath信息是被appclassloader所使用的。而appclassloader其实是第三层的classloader,最高层的classloader叫做bootstrap classloader,它不是java写的classloader,而是c++写成的,第二层叫做extclassloader,默认包纳是jre/lib/ext里面的classes目录和所有jar文件作为内容。第三层才是我们命令行参数,或者不用命令行参数,用系统环境变量指定classpath的使用者app classloader,这是最基本的java se。如果是java ee,有了服务器,容器,还有更多层的classloader,他们在app classloader的更下面,例如tomcat的某web应用程序的web-inf/lib中的jar,zip和classes目录,是app之下好几层的classloader使用的。

  你可以建立自己的classloader,都在app classloader之下,实际上tomcat本身也是这样建立classloader的。分层的目的是为了安全,试想你加入搞了一个classloader,从网络上读取class,而在里面写上格式化硬盘的代码,人家一读运行,那不就挂了,所以分层之后,首先从最高层读,没有再往下找,找到就不着了。一般java所必须的rt.jar里面的若干class,是在bootstrap classloader启动的时候读入的,而jmf使用的几个jar,是在ext classloader里面读入的。也就是说,读入这些class的时候,我们的appclassloader还在娘胎里呢,所以你在CLASS PATH中指定rt.jar是完全愚蠢多余的。java绝对不会到这里找rt.jar,而bootstrapclassloader如果你不特别要修改,它是常量,不需要你care。

  import干吗用的?

  import只是一种让你偷点懒少打字的方法,绝对不会影响你的classpath,这点你要好好记住,没有非用import不可的理由,用了import也不会起到类似c里面嵌入某文件内容的效果,它只是一种省事的办法。不在classpath中的class,任你再import也无济于事。

  如果你不用import,你用ArrayList这个类,就需要写

  java.util.ArrayList。

  而用了import java.util.ArrayList;的话

  以后代码中写ArrayList就可以了,省事。import可以使用通配符*,*代表某package下所有的class,不包括子目录。

  import java.awt.*

  不等于

  import java.awt.*

  import java.awt.event.*

  如果你要简写java.awt.event下和java.awt下的类,你不能偷懒,两个都要import。


最后

以上就是疯狂电源为你收集整理的Java的package和import机制的全部内容,希望文章能够帮你解决Java的package和import机制所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(35)

评论列表共有 0 条评论

立即
投稿
返回
顶部