1.前言

相信任何使用JAVA语言的开发者,都会在一台新的PC上去装上JDK,JRE,用来可以编译我们所写的.java文件,然后让其生成编译后的.class文件,从而能够争取执行。。。有兴趣可以简单了解一下JDK和JRE的作用。

当我们装上JDK的时候,相信大家还会经历非常重要的一步,就是配置环境变量。并且,毫不避讳的说,初次接触java的初学者,总是在配置环境变量的时候,一头雾水,那我们今天就来以配置环境变量为问题切入点,彻底搞懂他究竟涉及到哪些比较核心的问题-----类加载器。。。

2.问题切入点CLASSPATH

关于如何配置java使用的环境变量,请参考配置环境变量。

上面就是我们再熟悉不过的两个环境变量,,,分别是PATH和CLASSPATH。

2.1 path中配置的两个路径是干嘛的?

首先,我们需要知道这两个路径分别是,,,jdk\bin路径和jre\bin路径。。。想要理解这个,就需要你回上面区看关于JDK和JRE的含义的描述。

我们来看这样的一个操作:DOS命令行编译和执行我们的JAVA代码

  • 第一个红框中是我为了测试,存放一个Hello.java文件的地方,在D盘符下的MyTestCode文件夹下。
  • 第二个红框是我们再熟悉不过的命令行编译(javac)和执行.class文件(java)的操作。

这里就有一个很有意思的问题了,我们的DOS命令让我们进入到了d盘的一个确定的文件夹下,该文件夹下又没有编译和执行java源文件的工具,那么为什么我们可以直接在D:MyTestCode文件夹下,使用javac和java命令的呢???注意,需要明白的是,javac和java是我们正确安装JDK和JRE后,才能使用的命令,是因为在JDK和JRE里面有满足java文件编译和执行的工具。。。 既然如此,我们在DOS命令中想要使用javac和java命令,就必须找到对应的执行工具,而D:MyTestCode文件夹又没有这样的工具。。。

解释了这么多,其实就是为了说明,PATH中的路径就是为了让上述javac和java可以找到对应的编译和执行工具。。。当我们将工具路径注册到Path后,我们可以在任何盘符下,任何位置去使用javac和java命令,当执行这个命令行的时候,windows能够在path中找到对应的工具。。。

2.2 CLASSPATH又是干嘛的?

其实这个CLASSPATH的配置相当简单,但是想要弄清楚这个东西,是一件不容易的是。如果你上网搜索,一定会有一大批答案。但是,我很负责任的告诉你,这些都不是我们想要的。。。

我们来看CLASSPATH的具体配置信息:

.;%JDK_HOME%\lib\dt.jar;%JDK_HOME%\lib\tools.jar;

首先,抛出涉及到的一些问题

  • 或许此时正是你的疑问,CLASSPATH可以不配置啊,我就没有配置
  • CLASSPATH中为什么要添加".;",不添加可不可以?
  • 配置的这两个JAR包路径是干什么的?

上述问题,最终解释为一句话:CLASSPATH中配置的路径是告诉AppClassLoader要去哪里加载.class文件。

这就涉及到了关于类加载器的基本知识,文片不对这些基础知识过多介绍,请参考java中关于类加载器的基础知识。

3. 彻底搞清楚CLASSPATH和AppClassLoader的关系

以下的叙述内容全部按照具体实例来阐述。

我们先来看一段代码:

package com.myClassLoaderTest;import sun.misc.Launcher;import java.net.URL;
import java.net.URLClassLoader;/*** 获取三种JVM类加载器的加载路径** @ Author:  liu xuanjie* @ Date:    2020/8/6*/
public class ClassLoaderPathTest
{/*** 主函数,程序入口*/public static void main(String[] args){//首先是启动类加载器System.out.println("启动类加载器的加载路径: ");URL[] urls = Launcher.getBootstrapClassPath().getURLs();for(URL url : urls){System.out.println(url);}System.out.println("=======================================================");System.out.println("扩展类加载器的路径: ");//然后是扩展类加载器,获取方式是获取系统类加载器(AppClassLoader)的父加载器//(注意这就需要开启双亲委派模型)URLClassLoader extClassLoader = (URLClassLoader)ClassLoader.getSystemClassLoader().getParent();urls = extClassLoader.getURLs();for(URL url : urls){System.out.println(url);}System.out.println("========================================================");//应用程序类加载器System.out.println("应用程序类加载器的路径: ");URLClassLoader appClassLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();urls = appClassLoader.getURLs();for(URL url : urls){System.out.println(url);}}
}

上述这段代码,很简单,就是获取三种类加载器,然后得到其负责的加载路径。

我们在IDEA下运行一下这个代码,看看与我们的猜测是否一致????

 结果似乎跟我们的预期不一致,不是说好AppClassLoader的搜索路径是CLASSPATH中的吗?怎么不一样?

我们先来仔细观察这个结果:

  • 第一个截图,是启动类加载器的搜索路径,很明显,跟我们了解到的一致,是lib目录下的核心类库。多说一句,其实就是我们引入包的时候那些以 java. 开头的类库。
  • 第二个截图,是扩展类加载器的搜索路径,很明显,是lib/ext下的扩展类库,其大部分就是我们引入包的时候那些以 javax. 开头的类库(注意啊,这只是一种笼统的说法)。
  • 第三个截图的结果,可以看到,首先包含了所有第一个截图和第二个截图中出现的类库。。。其次,红框内的那个路径,是当前IDEA下项目文件所生成的.class文件所存放的路径。。。后面的是所有该项目下maven所引入的第三方包。。。

其实,造成AppCLassLoader的加载类库路径与预期不一致的原因很简单,是由于IDEA这个编译器做了一些手脚。

其实根据结果可以猜想到,,,IDEA编译器,,,将AppClassLoader所关注的路径显示(仅仅是显示)出为了以下三部分:

(这里需要注意的是,尽管上述结果是我们调用的方法输出结果,但是这只是Idea给打印出来了,并不是说这里所有显示出来的 路径都是由AppClassLoader负责的路径,为了避免这种不直观的现象,我们抛弃Idea。。。实际上,Idea也只是这样打印出来了这些路径,其中由上层加载器控制的路径肯定还是上层加载器负责,不然就不是双亲委派了,,,所以说Idea这里的打印输出结果有点坑,潜规则,容易直接把人带偏了。。。)

  • 引入JDK的时候的那些基础核心类库和扩展类库。(这肯定不是AppClassLoader负责的)
  • IDEA操作的当前项目文件的.class存放的文件夹路径。
  • 当前项目操作引入的第三方JAR包。

所以,接下来,我们抛弃IDEA编译器,全部在DOS命令下执行:

3.1 第一个测试

我们将上述代码拷贝成一份单独的.java源文件,然后去处文件中的包名,放到一个新的单独路径下。

在命令行中开始我们的测试:(由于我这里编译好了,就直接执行了,自行测试的时候记得编译。)

这一次好像验证了我们的结论,AppClassLoader中加载的是我们CLASSPATH中配置的路径。

CLASSPATH中配置的路径依次是:

  • 先输出的是E:/CommandLineTest
  • 后续依次是一个JDK中的dt.jar和tools.jar的路径
  • 然后又出现了依次E:/CommandLineTest

我们来分析这个结果::::

  • 按照顺序位置猜测,这个CLASSPATH中的”.;“应该是对应了E:/CommandLineTest,那这个"点分号"为什么会对应这个路径呢?是因为dos命令中当前操作的盘符是这个路径?还是.class文件最终存放在这个路径中?后续我们回去验证。
  • 还有就是CLASSPATH的配置中明明只有三个路径,为什么输出结果中有四个路径,最后一个路径还和第一个路径一样?这两个一样的路径之间有关系吗?

3.2 我们来验证3.1中最后的问题

为了验证这一点,我们不进入.class存放的位置,直接执行java命令。

显然,此时我们就需要更改CLASSPATH,让AppClassLoader能够找到.class文件。

(注意啊,由于配置变量发生了改变,需要重新打开命令行,否则执行结果跟刚才一致)

首先,我们分别在C盘目录下和D盘目录下直接执行了java ClassLoaderPathTest的命令,分析结果。

  • 首先,值得高兴的是,我们在CLASSPATH中配置了我们要执行的.class文件的路径,然后我们在DOS中操作的时候,没有进入到这个.class文件所在的路径下,也成功执行了。。。这就直接说明了,CLASSPATH确实是一个AppClassLoader搜索加载类库的路径。。。所以说,当我们配置完CLASSPATH后,我们可以在任何位置去执行CLASSPATH路径中存放的.class文件。
  • 我们也知道了"点分号"对应的路径,跟.class无关,就是DOS执行命令行时,当前所操作的路径。第一次我们直接在C盘根目录中操作,输出的就是C:,第二次我们在D盘根目录中操作,输出的就是D:。并且我们操作的.class文件在E:CommandLineTest中。
  • 根据这一次的实验结果,又产生了一个有趣的问题,本次实验中,没有再多出一个路径了,,,输出的路径就恰好是CLASSPATH中所对应的路径。一个不多,一个不少。。。。很有意思,不慌,我们继续实验。
  • 其实这也验证了3.1中多出来的那一个路径与"点分号"无关。
  • 其实,还表明了,3.1中多出来的那个路径与当前操作的盘符路径也无关。

3.3 我们把CLASSPATH中的"点分号"去掉

很简单,我们把CLASSPATH中的"点分号"去掉

再来执行命令行,(注意啊,由于配置变量发生了改变,需要重新打开命令行,否则执行结果跟刚才一致)

继续分析本次的结果:

  • 首先我们在CLASSPATH中去掉了"点分号",结果也确实如此,"点分号"所对应的当前路径消失了。
  • 而我们本次执行是在.class文件存放目录下执行的,因为我们CLASSPATH中干掉了这个.class所存放的路径,如果不进入到这个E:CommandLineTest下,会导致类找不到(这个结论我们下面会验证到)。
  • 哈哈,本次进入到.class文件存放目录后,这个新增的路径又出现了。。。

3.4 为了继续弄懂关系,我们直接把CLASSPATH干掉

很简单,直接不配置ClassPath,删除掉这个环境变量。看看在不同的操作路径下,会有什么不一样

好了,我们分析两次的执行结果:

  • 首先,第二个结果,表明,CLASSPATH中不存放需要操作的.class文件所在路径的时候,并且如果我们不进入到这个.class文件存放的路径下的时候,AppClassLoader是无法加载的,因为根本找不到。。。
  • 那第一个结果,是我们把CLASSPATH干掉后,然后进入到.class的存放路径下,成功执行了.class文件,并且,执行结果中又新增了这个路径,注意啊,此时的CLASSPATH已经完全没有了。。。
  • 这说明了,CLASSPATH这个系统变量不是必须的,但是如果没有,那就需要手动进入到要执行的.class文件所存放的盘符目录下进行执行。
  • 并且,总和上述三个实验可以得出另一个结论,当我们进入到.class文件存放位置的路径下去执行这个.class文件的时候,AppClassLoader的加载类库路径被默认添加了这个路径,无论我们有没有在CLASSPATH中配置这个路径。。。

3.5 我们验证3.4中的最后一个结论

我们这样做,CLASSPATH中只配置.class文件的存放路径,并且我们进入到这个路径去执行这个.class文件。

结果如图所示:

  • 我们进入到.class文件存放目录去执行的.class文件,为什么这次只打印出来了CLASSPATH中的路径,而没有添加呢?
  • 其实原因很简单,这个路径已经被配置到了CLASSPATH中,也就是如果CLASSPATH中有了这个路径,那么无论如何也都不会再添加一次这个路径了
  • 你可能要问,之前的"点分号"也是当前路径,为什么那个时候还会新增这个.class存放的路径呢???其实这个问题已经解释过了,“点分号”所代表的是DOS命令中当前所操作的盘符,他不是固定的,他跟.class存放的路径毫无关系,只不过是当我们操作路径与.class存放路径一致的时候,产生了巧合而已。。。。
  • 所以说,当我们把.class存放的路径,添加到CLASSPATH的时候,就不会新增了,因为没有意义,即使新增了,也是两个完全一致的路径。

3.6 我们改变.class的位置

为了验证这个问题,我们首先把ClassPath改回去,然后改变ClassLoaderPathTest.class的位置。

我们继续执行命令行;

这个实验没什么大用,只是为了再次证明:

  • "点分号"所代表的路径确实就是当前DOS中所操作的盘符路径
  • 当我们在DOS中进入到要执行的.class文件存放路径后执行对应的.class文件,AppClassLoader的类库加载路径会被默认添加上这个路径。

那么,这就完了吗???看到这里是不是又乱又迷,还感觉没一点用。。。哈哈。。。。

不要慌,更乱更迷更没用的还在后面。。。

4.CLASSPATH中严格的顺序问题

看了上述关于CLASSPATH配置问题的介绍,其实感觉真的一点用没有,哈哈,那是因为我们越来越依赖现成的编译器了,而忽略了我们本应该关心的问题。。。

继续看这样一个问题,,,如下配置CLASSPATH

我们分别在D:\MyTestCode和E:\CommandLineTest中放入同一个.class文件,并且同时将这两个路径配置到CLASSPATH中。

执行命令行如下:

分析结果:

  • 我们直接在外部C盘下去执行了这个.class文件,毫无疑问的可以执行成功
  • 但是,我们配置路径中有两个路径都有这个文件,但是dos命令的结果告诉我们,这个.class文件只被执行了一次,当然了,我们也希望他只执行一次。
  • 那么这是为什么呢???并且,到底执行的是哪一个呢???是第一个路径,还是第二个路径????

4.1 来验证上述问题

验证的操作很简单,我们修改某一个对应的ClassLoaderPathTest的内容,也就是当两个.class同名的时候,并且其存在路径还都被放在了CLASSPATH下,我们来看执行结果。。。

我们修改D:\MyTestCode下的文件

然后编译它,并形成.class文件

执行命令行,展示结果如下:

果不其然,执行的是CLASSPATH中第一个路径下的.class文件:

  • 这样就是说,AppClassLoader在扫描用户类路径的时候,根据CLASSPATH中的配置信息,是存在相对的位置关系的严格顺序的,他会沿着这个顺序去寻找,当在某一个路径下找到.class文件的时候,他就不会再继续往后找了。。。即便后续的路径中仍旧存在着相同名字的.class文件。。。

4.2 从4.1的结果观察中我们有什么启发呢?

  • 首先,我们自己写的用户类的.class文件的存放路径,尽量配置到CLASSPATH中,并且在最前面。
  • 想想我们最初的CLASSPATH的配置,是不是"点分号"在最前面,现在也就理解为什么了,因为,他会首先查找"点分号"路径下的文件,如果找到了,就直接完成了。。。
  • 我们知道,JVM加载类的时候,并不是一股脑的把所有类加载进去,,,他其实是”按需加载“的。。。所有CLASSPATH中的路径配置的前后位置关系,就是AppClassLoader的查找顺序。。。
  • 当多个.class文件同名的时候,且其在CLASSPATH中能够找到加载路径的时候,以最先出现的为执行准则,这仍旧取决于CLASSPATH中的配置顺序。。。

4.3 我们再来进行最终的验证

上面4.1中我们是在C盘根目录下操作的,这次我们改变策略,我们去E:\CommandLineTest下去操作

结果分析:

  • 跟我们预想的一样,即便进入到了E:\CommandLineTest的目录下,还是执行的D盘下的那个.class文件。
  • 并且,可以得出一个新的结论,这个搜索的顺序,是先搜索CLASSPATH中配置的路径,如果没有的话,最后才会到当前DOS命令的当前操作的盘符路径下去搜索。

这就完了吗???不,还没有,哈哈哈哈哈哈哈!!!

5.那dt.jar和tools.jar又是干嘛的嘞??

首先说明,作者精力有限,并没有很深入的研究这个问题,只是提出一个研究方法,和初步的答案。。。。

我们复制这两个jar包到单独的文件,然后去解压(其实jar包就是一种压缩包而已)

5.1 首先是jdk\jre\lib\dt.jar

粗略来看这个dt.jar中主要就是javax.swing下的相关类,是一个拓展包,但是没有放在拓展类加载器的搜索类库路径下,而是需要我们自己手动的配置到CLASSPATH中,由AppCLassLoader来负责加载。。。。其实这也可以验证,就是使用这些类,然后CLASSPATH中不配置这个路径,看看能不能成功,这里就不做实验了。。。

5.2 然后再来看tools.jar

这个里面的类比较多,层次也比较乱,作者也没有深入了解,所以不多做解释,如果有谁研究过的话,可以评论一下,虚心求教。关于这个jar包的作用,大家目前就自行搜索吧,后续深入了解会回来更新这部分内容。。。

这次总算是完了吧,什么???还没有完???

6.另外两个类加载器的路径是怎么配置的呢???

首先来点基础知识,看图:

这个加载类库的路径,其实我们上述的实验中已经验证过了,,,但是仔细想一个问题:

AppClassLoader的路径是通过CLASSPATH来配置的,这很直观,但是我们没有配置启动类加载器和扩展类加载器的路径啊?他们的类库搜索路径是怎么配置的?是什么时候配置的?

我们来看JDK中的源码:

为了理解这个东西,我们先来看这样一个知识:

  • 首先,JVM是”按需加载“的,这个之前已经介绍过了,JVM在启动的时候,不会傻傻的将所有的类和资源都加载进内存
  • 程序在启动的时候,或者说JVM在启动的时候,JVM是会负责先装载所有的ClassLoader的,这一部分,涉及到很底层的JVM指令问题,无需深入,我们只需要知道JVM实例化了sun.misc.Launcher这个类就行,如上图所示。
  • 来看图示为sun.misc.Launcher的构造方法,他加载了扩展类加载器和AppClassLoader,并且注意看图中最后一个标注的地方,他把AppCLassLoader设置为了线程上下文的类加载器,哈哈,是不是长知识了。。。
  • 由于启动类加载器是有C++编写的,不是JAVA语言所写的,所以暂且是需要知道,JVM启动的时候,也会有JVM自己加载这个启动类加载器的。。。

然后我们回到上面的JDK源码,看图中标注的那几个获取系统变量的操作:

 System.out.println(System.getProperty("sun.boot.class.path"));System.out.println(System.getProperty("java.ext.dirs"));System.out.println(System.getProperty("java.class.path"));

很直观的来看,这三个系统变量就对应了三个类加载器的类库加载路径。。。可以自行验证。QAQ。。。、

这里在简单引申一点基础知识:

  • System.getProperty()一定会对应setProrerty的
  • 然后其实内部是一个Hash逻辑的key-value对应关系

所以说,JVM在最开始实例化sun.misc.Launcher类之前,一定配置好了上述三个系统变量,并且前两个变量的路径是由JVM指定的(当然,正如描述中所说的,我们可以通过JVM启动参数去更改)。原因很简单,这两个类加载器负责的是jdk中的类库,不是用户类库,所以其存放位置根据jdk安装位置,是相对就能够得到的(不信的话,你把lib目录换个位置,立马报错)(这句话的意思是说,jdk中的这种文件结构是有其固定的逻辑的,不要所以更改)。。。。而用户类路径具有不确定性,所以需要我们在CLASSPATH中去配置。。。

前面已经介绍过这两个类加载器所负责的类库,这里再解释一下。

  • 启动类加载器,那个类库搜索路径,主要是基础核心类库,就是java.开头的包内类
  • 扩展类加载器,那个类库搜索路径,主要是拓展类库,就是javax.开头的包内类(这只是很粗的一种便于理解的叙述,我们已经知道dt.jar中有javax.swing,并且其被我们放在了CLASSPATH中

这次总该结束了吧,其实本不打算结束的,本来还想继续延伸出关于自定义类加载的内容,但是,由于篇幅已经很长了,所以,关于类加载器的叙述放到单独的一篇文章中去。

本文为原创文章,难免会出现纰漏,甚至错误,,文章中也有一些细节点还没有深入,望大家不吝赐教。。。

根据配置CLASSPATH彻底弄懂AppCLassLoader的加载路径问题相关推荐

  1. 分析BootstrapClassLoader/ExtClassLoader/AppClassLoader的加载路径 及父委托机制

    http://blog.csdn.net/irelandken/article/details/7048817 分析BootstrapClassLoader/ExtClassLoader/AppCla ...

  2. linux nginx 安装stream,Centos7-64bit-编译安装配置Nginx stream四层负载均衡 动态加载

    Centos7-64bit-编译安装配置Nginx stream四层负载均衡 动态加载 2018-08-10 17:12 分享人:老牛 yum install screen -y && ...

  3. Spring-IOC XML 配置多个相同 ID 的 bean 加载分析

    我们现在仍以 xml 中配置 bean 的方式来 使用 Spring ,不考虑注解和扫包 配置相同id 的bean 定义一个 bean 类 TransactionManager /*** @autho ...

  4. 如何配置Ubuntu 16.04 GRUB 2引导加载程序

    正如你所知,GRUB 2 是大多数 Linux 操作系统的默认引导加载程序.GRUB 是 GRand Unified Bootloader 的缩写,它是 Linux 启动时首先要加载的一个程序,此后它 ...

  5. 使用udig配置数据样式(二)——udig加载数据

    1.启动udig 双击udig快捷方式,等几秒后,udig启动成功,如图所示: 2.创建项目 第一次键入该软件,需要新创建一个Project,即工程目录,可以理解为Eclipse的WorkSpace. ...

  6. js模块化编程之彻底弄懂CommonJS和AMD/CMD!

    为什么80%的码农都做不了架构师?>>>    先回答我:为什么模块很重要? 答:因为有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块. 但是,这样做有一个前 ...

  7. js模块化编程之彻底弄懂CommonJS和AMD/CMD

    转载地址:http://www.cnblogs.com/chenguangliang/p/5856701.html 先回答我:为什么模块很重要? 答:因为有了模块,我们就可以更方便地使用别人的代码,想 ...

  8. springboot 读取配置文件_使用 @ConfigurationProperties 在 Spring Boot 中加载配置

    本文地址: 使用 @ConfigurationProperties 在 Spring Boot 中加载配置 使用 Spring Boot 加载配置文件的配置非常便利,我们只需要使用一些注解配置一下就能 ...

  9. Ribbon与Eureka整合分析(四)、客户端配置延迟加载和启用饥饿加载

    客户端配置延迟加载和启用饥饿加载 该博文为Ribbon与Eureka整合分析系列文章中的第四篇,主要介绍客户端所需配置,默认情况下,如何在创建客户端时,才加载配置,以及如何在启动时,加载客户端配置(即 ...

  10. Spark SQL(六)之加载数据的参数配置

    一.配置 忽略损坏的文件.忽略丢失的文件.路径全局过滤器.递归文件查找和修改时间路径过滤器等选项/配置仅在使用基于文件的源(parquet,orc,avro,json,csv,txt)时才有效. 以下 ...

最新文章

  1. 打造生物智能和人工智能“双螺旋”,智源研究院发布“人工智能的认知神经基础”重大研究方向...
  2. PHP爬取企业详情(百度信用)
  3. Windows 容器
  4. 裸板烧写linux内核,嵌入式linux学习(二):烧写裸板进程
  5. vim设置tab宽度为4_vim编辑器VimScript插件开发系列一「定制Vim 3」
  6. 构建插件式的应用程序框架(四)----服务容器
  7. Linux下的Ruby2.7.0下载
  8. bibibi 下载_哔哩哔哩下载电脑版_哔哩哔哩官方版下载[bilibili]-下载之家
  9. 需要在计算机安装msxml版本,win7 Office2010提示安装MSXML版本6.10.1129.0怎么办
  10. 请回答2021,爱彼迎、木鸟民宿、途家民宿年度走心PK
  11. el-table展示枚举值
  12. 风控概要和内容安全,反欺诈(营销风控)
  13. 《菩萨蛮·书江西造口壁》 辛弃疾
  14. 基因家族分析②:linux下blast的安装和使用
  15. html2canvas微信头像跨域,企业微信头像 前端使用canvas处理时跨域
  16. linux 存储映射lun 给_LINUX系统下添加映射存储LUN
  17. 网络安全:常见攻击手段及防御
  18. 查尔斯·巴贝奇——计算机先驱者之父
  19. 有关于3GPP SUL的一些学习
  20. 计算机网络——DV和LS算法笔记

热门文章

  1. 《终结者·洛谷》第3章
  2. 考题篇(6.2) 01 ❀ FortiManager ❀ Fortinet 网络安全专家 NSE 5
  3. gdc服务器硬盘修复,GDC环球数码SX-3000独立媒体模块AHCI设置
  4. cx oracle 提示32位,Windows 32位安装cx_Oracle解决方案
  5. python读取crl吊销列表
  6. 在vue3(vite+ts)中使用shiki实现代码语法高亮渲染
  7. 2022年,我45岁,一息尚存不落征帆,静稳前行未来可期
  8. [数学] 线性微分方程中的“线性性“
  9. 计算机岗位面试专业技术测试,浙江广电计算机岗的笔试题目和面经分享
  10. 验证“哥德巴赫猜想” (20分)