Java小白攻略-从class到testng

这篇博客适合对Java有一定基础的通信行业从业者,主要介绍如何在命令行环境中编译、打包和执行Java代码,不依赖IDE。内容包括Java程序的文件结构、类的调用名、包的概念、环境变量CLASSPATH和VM参数classpath的使用,以及jar命令和jar包执行。此外,还提到了javac命令以及TestNG测试框架的执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本人作为学生时代上过几堂课的java业余小白在不懈的努力下还是掌握了一些java coding的基础知识,只是至今我都无法相信我的当代软件开发技术与实践课考试刚刚及格的事实,难道我跟学校真的八字不合~在接下来的讲解中不会使用到eclipse这个目前最流行的开发工具,而是在纯粹的命令行环境下编译打包和执行java代码,虽然平时修改代码我也用eclipse,但是eclipse真的是太好用了反而隐藏了java的众多概念与机制,而不了解这些概念和机制很难说掌握了java的开发技巧,即使能写出性能优良的code~毕竟我并不是一个软件开发工程师,因此更加关注如何修改并执行java code而非如何开发,所以这篇文章的主要适用人群是像我这样的通信人,相信读完之后就可以摆脱小白的身份并应用到工作实践中,并且不要担心本文不涉及eclipse会导致文章实践性不足,在了解以下知识点之后eclipse的使用真的可以无师自通~

1.java程序的文件结构与类的调用名-package

即使不懂java也应该知道java是完全面向对象的编程语言,因此本质上java程序里的文件都是定义java类的类文件(.java),并且文件名就是文件中定义public类的类名(class name),即类文件对应java 类:
<class_name>.java <--> java class

为了实现相同类名存在于程序中而且不影响调用,java引入的包的概念,即在类文件中使用package关键字定义该类隶属的包,没有使用package关键字定义包名的类隶属于默认包(default package),其实java包的实质就是文件夹,包名(package name)就是文件夹的名字,类似于使用定义文件夹的层级结构,java包是用. 来表示层级结构,例如存储在com/xxx/yyy目录下类的包名就是com.xxx.yyy,因此文件夹就对应java包:
<package_name>/ <--> java package

我们知道任何文件系统都有一个根目录作为结构顶级,java程序的“根目录”就是所谓的默认包,该“根目录”可以是系统文件系统任意一个文件夹或者是一个jar包,在该“根目录”下或内存储属于默认包的类文件,对应包层级的文件夹结构以后隶属于相应包的类文件,典型的java程序目录树如下所示:

#Java Program Architecture 
.<Any_dir or Jar>
├── A.class
├── package_A
│    ├── B.class
│    └── package_C
│         └── C.class
└── package_B
     └── D.class

在这种层级结构下程序中每个java类都形成对应其存储路径的调用名(call name),即<package_name>.<class_name>,例如上述class A的调用名就是A,class B的调用名是package_A.B,class C的调用名是package_A.package_C.C,class D的调用名是package_B.D

命令行执行java程序即在java命令后指定执行类的调用名,通过调用名中的包名逐级深入直至定位到类文件,然后java虚拟机加载代码执行,需要注意的是存储在一定层级结构的类文件源码中一定要使用package关键字定义当前层级的包,不然即使调用名能正确的反映类文件的位置,java虚拟机也会报错提示找不到该类。

2.java程序的“根目录”

此外类调用名只是反映了类文件在“根目录”下/内的路径,因此让java命令知晓我们开发的java程序“根目录”就十分重要,只有定位了“根目录”才能逐级找到类代码,java提供了两套方案指定程序“根目录(classpath)”。

a. 环境变量 - CLASSPATH

每次执行程序时Java默认会从该环境变量定义的路径下或者jar包中搜索类,该变量中的“.”代表当前路径,也就是执行Java命令的路径,这样就无需将当前完整路径添加到该变量中,命令执行较为方便。

export CLASSPATH=.:/usr/jre/lib:/usr/lib/tools.jar:/xyz/testng-6.11.1-SNAPSHOT.jar:/xyz/jcommander-1.7.jar

b. Java VM 参数- classpath/cp

java -classpath/cp /path:/path/file.jar:/path/file.zip org.testng.TestNG

example:java -cp /xyz/mob-rmm-R18A.jar:/xyz/testng-6.11.1-SNAPSHOT.jar org.testng.TestNG

the : separated list of directories, JAR archives, and ZIP archives to search for class files,需要注意的是上述VM参数会覆盖环境变量CLASSPATH的设置。

3.Java命令 - VM param & program param

java命令的执行格式如下所示:

java [options] class_call_name [arguments]
java [options] -jar file.jar [arguments]

上述范式中options即为VM param,参数值传递给JAVA虚拟机,设置虚拟机相关属性,除去-cp/classpath <list of path, jar and zip>之外比较重要的的VM参数还有-D<name>=<vaule>,该-D参数设置虚拟机的系统属性和其值(set a system property and its value),如果属性值是包换空格的字符串那必须用双引号包围,使用该参数定义的系统属性值可以使用System类中的getProperties()方法得到<System.getProperty("Name")>。

范式中的arguments就是所谓的program param,参数之间空格隔开,该参数列存储在main方式中定义的args数组中(String[] args)。

虚拟机参数-D和程序参数是最常用的引入外部参数的两种方法,使用以下code加深理解:

//reference from http://blog.youkuaiyun.com/cuidiwhere/article/details/17885675

public class test {

    public static void main(String[] args) {

        System.out.println("program params:");
        if (args == null || args.length == 0) {
            System.out.println("\t" +"no input params");
        } else {
            for (String arg:args) {
                System.out.println("\t" + arg);
            }
        }

        System.out.println("system property params:");
        String sysprop1 = "sysprop1";
        String sysprop2 = "sysprop2";
        System.out.println("\tName:" + sysprop1 + ",Value:" + System.getProperty(sysprop1));
        System.out.println("\tName:" + sysprop2 + ",Value:" + System.getProperty(sysprop2));


        System.out.println("Default VM arguments:");
        System.out.println("java_vendor:" + System.getProperty("java.vendor"));
        System.out.println("java_vendor_url:"
             + System.getProperty("java.vendor.url"));
        System.out.println("java_home:" + System.getProperty("java.home"));
        System.out.println("java_class_version:"
             + System.getProperty("java.class.version"));
        System.out.println("java_class_path:"
            + System.getProperty("java.class.path"));
    }

}

测试结果如下:

xyx@zyx:~/space> java -Dsysprop1=hello -Dsysprop2=world test good day
program params:
        good
        day
system property params:
        Name:sysprop1,Value:hello
        Name:sysprop2,Value:world
Default VM arguments:
java_vendor:Oracle Corporation
java_vendor_url:http://java.oracle.com/
java_home:/usr/java/jdk1.7.0_25/jre
java_class_version:51.0
java_class_path:.:/usr/jre/lib:/usr/lib/tools.jar:/xyz/mob-rmm-R18A.jar:/xyz/jcommander-1.7.jar:/xyz/testng-6.11.1-SNAPSHOT.jar

VM参数和program参数的区别在于位置,class和jar包之前指定的参数均为VM参数,class和jar包之后指定的参数均为program参数,VM参数放置在class和jar包之后会被当做program参数传递到args数组中,而program参数放置到class和jar包之前则被解析为VM参数,一般会导致JAVA报错提示不识别的选项(Unrecognized option: -d)。

jar包是Java定义的打包文件格式,类似于zip包,用于封装Java程序,可以理解为jar包就是上述Java程序“根目录”的文件封装形式,“根目录”下的文件结构原封不动的封装到jar包里,特别是Java原生支持解析读取jar包内的包与类文件,这样就使Java程序的存储、发布变的十分简单。一般jar包生成的过程中会自动建立META-INF/MANIFEST.MF文件,可以使用该文件里参数指定默认执行的类调用名,这样就可以直接通过指定jar包的方式执行程序,该内容在下一节中进行讲解。

4.jar命令和jar包执行

pack:
//将指定路径的文件结构(从根目录开始)打包到指定jar包中,不常用
jar cvf file.jar /path/

//将当前路径下的文件结构打包到指定jar包中,常用
jar cvf file.jar ./
//-C相当于cd到指定的路径下然后执行不带-C参数的jar命令,常用
jar cvf file.jar -C /path/ ./
//使用指定的MANIFEST.MF文件而不是自动生成的,常用
jar cvfm file.jar MANIFEST.MF -C /path/ ./

unpack:
//将jar包内容解压到当前路径
jar xvf file.jar

如果不指定MANIFEST.MF文件,生成jar包的过程会自动建立META-INF文件夹并在其下生成MF文件,jar包内的目录树如下所示:

.<test.jar>
├── main
│   ├── Connect.class
│   ├── Connect.java
│   ├── Enumer.class
│   └── Enumer.java
└── META-INF
    └── MANIFEST.MF

MANIFEST.MF文件的作用比较多,比如之前提过的生成可执行的jar包就要在该文件下使用Main-Class指定默认的类调用名,该类必须是拥有main函数的main class才能作为jar包的程序入口,简单的MF文件如下所示:

exyz@ldfker:~> cat MANIFEST.MF
Manifest-Version: 1.0
Created-By: 1.6.0_20 (Sun Microsystems Inc.)
Main-Class: main.Enumer
Class-Path: /xyz/mob-rmm-R18A.jar

使用jar包执行程序方便简单,但这种方式带来的弊端是无法使用环境变量CLASSPATH和VM参数-classpath/cp引用其他jar包和路径,原因在于上述两种方式指定的classpath(“根目录”)是由AppClassloader加载的,Java命令引入-jar参数后,AppClassloader只解析指定的file.jar内部的class,导致无法引用其他的class。

参考http://www.cnblogs.com/adolfmc/archive/2012/10/07/2713562.html

常用解决方法:

一、MANIFEST.MF文件定义Class-Path

在jar包MANIFEST.MF文件中添加Class-Path: /path /path/file.jar,多个路径或jar包可以采用添加多个Class-Path: 标签的方式也可以一个Class-Path: 标签后多个item用空格隔开。

Class-Path: /xyz/mob-rmm-R18A.jar /xyz/testng-6.11.1-SNAPSHOT.jar

二、Bootstrap Classloader定义类路径

上面介绍了JAVA VM param中的-D参数,要使用bootstrap classloader还需要了解-X参数,该参数形式也是-X<name>,是Java的non-standard options,所谓的非标准参数就是官方会在不通知的情况下修改或废除这些参数。

The -X options are non-standard and subject to change without notice.

使用java -X可以显示当前Java支持的非标准VM参数,我们需要的bootstrap参数有以下三个:

xyz@moiyuier:~> java -X
    -Xbootclasspath:<directories and zip/jar files separated by :>
                      set search path for bootstrap classes and resources //完全的取代系统bootclass,较少使用
    -Xbootclasspath/a:<directories and zip/jar files separated by :>
                      append to end of bootstrap class path //在系统bootclass加载之后载入,常用
    -Xbootclasspath/p:<directories and zip/jar files separated by :>
                      prepend in front of bootstrap class path //在系统bootclass加载之前载入,可能会跟系统bootclass冲突,不常用

所以执行jar包命令如下:

java -Xbootclasspath/a:/path:/path/file.jar -jar file.jar

三、自定义classloader实现加载

参考http://cuixiaodong214.blog.163.com/blog/static/951639820099135859761

5.javac命令

之前各节都只是涉及Java类的执行,从文件形式上来看一个Java类就是后缀为.class的字节码文件,该字节码文件才是Java程序的主体,所以Java跟纯粹的脚本语言区别也体现在这里,Java虚拟机载入的并不是Java源码而是使用后缀名为.java的源码文间编译生成的字节码,实现编译功能的命令就是javac,实质上就是Java的编译器。

Java命令执行类时需要指定类的调用名,执行的前提条件就是类定义的包结构跟所在的文件结构相同,而使用javac编译源码则无需考虑包结构,仅需考虑类需要import的外部类是否通过环境变量CLASSPATH和VM参数-classpath/cp指定。

典型的javac执行命令如下:

javac -cp/classpath /path:/path/file.jar /path/<class_name>.java

如果指定了环境变量CLASSPATH,则无需指定-cp/classpath,上述命令就在源码的同级目录生成同名的class文件,一般手动编译的情况下都是直接cd到源码目录执行javac命令,所以/path就是./,实际中可以直接省略。

6.Junit/TestNG测试框架

不涉及测试框架的细节分析,大体来说能称为框架的软体都是留有调用接口封装好的软件包,Java框架大都以jar包的形式发布,目前TestNG比Junit要流行,所以只演示TestNGK测试的执行,其实框架的执行方式大同小异。

想要使用TestNG进行测试或者开发程序就要下载TestNG的jar包,现在多数的项目都在使用maven作为构建发布工具,所以我竟然没有在TestNG的官网上找到最新版本jar包的下载链接,不得已只能直接clone TestNG的git repo,repo中提供了编译打包的可执行脚本,最终得到了TestNG的jar包,该jar包执行测试时会报错提示not found Jcommander,于是下载Jcommander jar包,遍寻不见,最后还是去maven的中央仓库download到本地,强大的maven~

testng-6.11.1-SNAPSHOT.jar
jcommander-1.7.jar

将上面的两个jar包路径添加到CLASSPATH变量中,就可以执行TestNG主类org.testng.TestNG

xyz@mobrmm:~/space> java org.testng.TestNG
You need to specify at least one testng.xml, one class or one method
Usage: <main class> [options]
 The XML suite files to run
  Options:    -configfailurepolicy              Configuration failure policy (skip or continue)
    -d                                Output directory
    -dataproviderthreadcount          Number of threads to use when running data providers
    -excludegroups                    Comma-separated list of group names to  exclude
    -groups                           Comma-separated list of group names to be run
    -junit                            JUnit mode (default: false)
    -listener                         List of .class files or list of class names implementing ITestListener or ISuiteListener
    -log, -verbose                    Level of verbosity
    -methods                          Comma separated of test methods (default: [])
    -methodselectors                  List of .class files or list of class names implementing IMethodSelector
    -mixed                            Mixed mode - autodetect the type of current test and run it with appropriate runner (default: false)
    -objectfactory                    List of .class files or list of class names implementing ITestRunnerFactory
    -parallel                         Parallel mode (methods, tests or classes)
    -port                             The port
    -reporter                         Extended configuration for custom report listener
    -suitename                        Default name of test suite, if not specified in suite definition file or source code
    -suitethreadpoolsize              Size of the thread pool to use to run suites (default: 1)
    -testclass                        The list of test classes
    -testjar                          A jar file containing the tests
    -testname                         Default name of test, if not specified in suitedefinition file or source code
    -testnames                        The list of test names to run
    -testrunfactory, -testRunFactory  The factory used to create tests
    -threadcount                      Number of threads to use when running tests in parallel
    -usedefaultlisteners              Whether to use the default listeners (default: true)
    -xmlpathinjar                     The full path to the xml file inside the jar file (only valid if -testjar was specified) (default: testng.xml)

执行后会得到main class后可以接的program param列表,一般使用-testclass指定我们定义的测试类,由org.testng.TestNG主类调用执行,更一般的可以将要执行的测试类定义到xml文件中,xml文件直接作为参数传递给TestNG主类。

java org.testng.TestNG -testclass <test_class_call_name>
java org.testng.TestNG /path/<suite>.xml

另外,Junit的主类是org.junit.runner.JUnitCore

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值