dos窗口下java自定义跨文件类引用的解决办法

1.背景叙述
  • Recurson.java所在的文件目录:

在这里插入图片描述

  • Recursion.java中引用了我自定义的另一个类Array

在这里插入图片描述

  • 我的Array.java所在目录:

在这里插入图片描述

  • 目录结构图也就是这样的:

在这里插入图片描述

2.编译跨文件引用类
  • 此时的Recursion.javaArray.java都没有任何packageimport语句:

在这里插入图片描述

  • Array.java自然是能够正确编译形成在D:\桌面\JAVA\1_hsp_java\javacode\chapter06_Array内的Array.class文件

在这里插入图片描述

  • 可是当我们开始运行Recursion.java的时候就不行了,因为它引用了Array.class,但是,JVM不知道从哪里找到这个类,就会报错找不到类:

在这里插入图片描述

  • 我自然想到把这个Array.class导入Recursion.java
    1. 我知道Array.class位于D:\桌面\JAVA\1_hsp_java\javacode\chapter06_Array目录下,我的第一想法是,该从哪里开始导入?

      import D.桌面.JAVA.1_hsp_java.javacode.chapter06_Array.Array;
      

      这个显然是不正确的,这就把windows和java混为一谈了。

    2. 我的第二想法是,为什么平时常用的import java.util.Scanner;就有用?这个java.util.Scanner目录结构究竟在哪里?JVM又是怎么找到它们的?

    3. 反着思考,先探究JVM是怎么找到这些类的?
      我们在dos窗口下来执行,为了帮助JVM找到位于windows系统某些目录下的类,windows在系统环境变量中提供一个classpath,通过该环境变量,我们可以帮助JVM找到我们自定义的类。

    4. 因此,我就把Array.class所在的目录加入环境变量classpath中,再来试一下能否编译成功:
      在这里插入图片描述在这里插入图片描述

至此, 跨文件类引用算是初步解决,把要引用的类所在的文件目录加入classpath中,别的类在引用该类时,JVM就可以加载到这些类,甚至都不用packageimport语句。

3.改进类加载方式
  • 上述直接把类添加入classpath的方式虽然简单粗暴,却只适合少量类和简单的目录结构,一旦有很多类要引用,各个类的目录层级结构又非常复杂,那么手动添加classpath就会变成非常痛苦枯燥的工作。

  • 此时,我才恍然packageimport的思想是多么地睿智,如果把很多类所在的一个公共的根目录加入classpath中,就只需要加入一条,JVM就可以找到很多类所在的包了,在这个包内发生的一些错综复杂的引用通过importpackage和构建。
    在这里插入图片描述

  • 在这种情形中,我先只把D:\桌面\JAVA\1_hsp_java\javacode加入classpath中,使得JVM可以找到这个大包(不需要打包成jar)

  • 在这个大包内,Recursion.java位于chapter07_Object文件夹下,那么在源程序中,就应该添加上package chapter07_Object;这句话,表示生成的类将会在放在这个文件夹内,到时候JVM在查看D:\桌面\JAVA\1_hsp_java\javacode时就能知道chapter07_Object这个文件夹里面包含了一个Recursion.class文件

  • 同样的,Array.java位于chapter06_Array文件夹中,在该源程序内也应该加上package chapter06_Array;以便别的类可以import到。

  • 把上述package都写好,重新编译Array.java,并且在Recursion.javaimport Chapter06_Array.Array;这样便可以成功了。
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
注意:如果源程序内写有package,那么在运行时,必须加上此包前缀,否则无法运行成功。

4.体会
  • 从头梳理一遍,我发现,我之前很多次尝试都失败,根源在于我混淆了windows目录结构JVM类加载目录结构,在我的windows系统下,我所见到的目录结构都是windows的目录结构,但是JVM并不是根据这个目录来加载类的。通过classpath,使得JVM类加载结构和windows目录结构有了一个交点,但JVM在此这个交点后就完全遵循package和import所构建起的“隐形的类加载结构”,有package信息的.class文件就被隐形地放入了此package下,只有通过package.class才能访问到。

  • 要证明上述说法非常简单,上面的两个源代码Array.javaRecursion.java中我都写入了package语句,这表明我把编译生成的Array.classRecursion.class都扔入了隐形的类加载结构下,其中Recurson.class位于此结构的chapter07_Object文件夹下,此结构的切入点就是classpath 内的 D:\桌面\JAVA\1_hsp_java\javacode。因为环境变量在整个windows系统都是可见的,因此无论在任何地方打开dos命令窗口,都可以通过java chapter07_Object.Recursion命令来执行该class文件,这就表明两种目录是基于一个交点的不同的结构。

  • 下面是我在C盘根目录下运行Recursion.class文件,是可以成功的。
    在这里插入图片描述

  • 反过来思考,如果一个class文件包含了package信息,那么只有通过该package路径找到该class文件这一种途径才可以执行这个class文件,把这个class文件迁移到别的地方就必须也设一个该package文件夹,并且把新的上层目录也加入classpath才可以。

  • 就像我把刚才的Resursion.class文件移动到C盘后,无论如何也运行不了,因为迁移了class文件,对于JVM类加载结构而言就相当于删除了class文件,毕竟JVM是无法理解windows系统目录的,因此必须在C盘内建一个同样的目录结构,并且把C盘根目录加入classpath,这样就可以成功了,此时相当于两种目录多了一个交点:

在这里插入图片描述

在这里插入图片描述

5.联想
  • 上文也提到过,为什么平时常用的import java.util.Scanner;就有用?这个java.util.Scanner目录结构究竟在哪里?JVM又是怎么找到它们的?
  • 我去classpath中找,并没有找到包含这些类的目录文件
  • 然后我了解到,JVM启动的时候就会自动加载一些jar包,一般是系统java的核心jar包,通过以下程序,可以了解到你的JVM一开始加载了哪些jar包:
/**
 * 看看JVM一开始加载了那些类,为什么classpath里面不用指定这些类
 */
import java.lang.*;
import java.net.URL;
public class WhyRtJarInclude{
	public static void main(String[] args) {
		URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();

		for (int i = 0; i < urls.length; i++) {

		System.out.println(urls[i].toExternalForm());

		}

	}
}
//以下是输出结果:
file:/E:/java/jdk1.8u271/jre/lib/resources.jar
file:/E:/java/jdk1.8u271/jre/lib/rt.jar
file:/E:/java/jdk1.8u271/jre/lib/sunrsasign.jar
file:/E:/java/jdk1.8u271/jre/lib/jsse.jar
file:/E:/java/jdk1.8u271/jre/lib/jce.jar
file:/E:/java/jdk1.8u271/jre/lib/charsets.jar
file:/E:/java/jdk1.8u271/jre/lib/jfr.jar
file:/E:/java/jdk1.8u271/jre/classes
  • 我们可以看到,JVM开启时就加载了很多jre内的核心类库,我们重点关注一下rt.jar,顺着file:/E:/java/jdk1.8u271/jre/lib/rt.jar可以发现一个rt.jar包,解压出现以下文件夹:

在这里插入图片描述

在这里插入图片描述

6.最后

妈耶,终于搞明白了,兄弟,你悟了吗?7千多字,码字不易,有帮助的话,点个赞吧,谢谢啦^ V ^

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值