在终端上创建Java项目及编译和运行

一:实践一次这样的操作有助于理解Tomcat/Eclipse的启动原理,包括classpath的设置,option的配置等等;

二:通过Bash终端创建一个简单的Java项目(单项目单Module,如果是IDEA的多Module可以创建一个Proj,然后内部再创建Module,这时候Module就类似这里的Proj,但是编译和运行的pwd应该为Proj而非Module)

1.cd到工作空间,个人是:cd /home/silentdoer/TerminalProjs/JavaProjs;然后执行mkdir FirstTerminalProj创建第一个终端项目;

2.cd到FirstTerminalProj里,通过mkdir -p src/me/silentdoer/terminalprojtest classes同时创建src和classes目录,并创建src的包路径,然后通过touch src/me/silentdoer/terminalprojtest/Entrance.java创建一个java文件(后缀其实可以任意或不要);

3.vim ./me/silentdoer/terminalprojtest/Entrance.java;开始写简单代码;

4.第一步先写package me.silentdoer.terminalprojtest;之后便是写Entrance类和main方法;

5.把Entrance.java写好后保存退出,此时pwd在FirstTerminalProj目录里;

6.用javac src/me/silentdoer/terminalprojtest/Entrance.java -d classes命令将该文件编译到对应的目标目录,javac会自动在目标目录内创建与该类包路径对应的目录路径然后生成 类名.class文件;

7.用java -cp classes me.silentdoer.terminalprojtest.Entrance(这里可接参数列表)来运行me.silentdoer.terminalprojtest.Entrance类;注意这里是运行这个类,如果没有指定-cp则jvm会以CLASSPATH的为准去搜索这个类然后执行;

8.运行jar包的方法为java -jar xx.jar(接参数列表),注意这里是需要有.jar后缀的,因为jar包实际上还可以是zip包,它不像.class一样是固定的;

总结:

javac里的-classpath或-cp是用来指定java源文件中(不一定要.java结尾,就像shell脚本不一定要.sh一样,编译后自动是.class)引用的jar包或其它module的classes目录(即Module依赖另一个Module),否则javac发现引用的东西不存在会编译失败(这也是为什么看框架源码要将编译方式由javac换成eclipse的原因);

1.可通过-d xx/classes指定编译输出目录;注意javac可以编译任何路径下的源文件而不需要此文件在对应的目录结构下(已经经过测试确实是这样),但是运行.class则需要在对应的目录结构下(即包路径和目录路径一致,这是因为运行某个类实际上不是去运行而是告诉jvm我要运行哪个类,然后jvm去classpath里搜索)

2.可以javac ./src/me/silentdoer/terminaltestproj/Entrance.java -d ./classes来编译,最后自动生成完整的与包对应的目录和.class文件在classes中;

3.如果要一次性编译目录下所有的文件可以用:查了下也测试了似乎只能编译某个包下的所有文件用如me/silentdoer/test01/*.java实现;(最终想了下还是用shell脚本实现吧,就是传递两个参数,src路径和classes路径,然后shell遍历src下所有的目录通过cd $dir和cd ..实现转接到不同目录然后通过javac ./*.java -d classes来实现编译所有的.java文件[这时候后缀的好处体现出来了,虽然并不要求必须是.java],编译方式类似:javac src/me/silentdoer/terminalprojtest/*.java -d classes),这里不需要用到-sourcepath,目前不知道这个有什么用。

1)java运行字节码文件则要求提供-cp为classes目录,用java -cp ./classes me.silentdoer.terminalprojtest.Entrance即可运行;注意这里之所以不需要.class是因为javac是为了查找要编译的文件跟后缀无关,而所有的编译好的都叫XX.class,因此字节码文件后缀唯一而源文件后缀任意,所以运行时不需要附带.class;

2)如果运行的项目需要用到其它jar包或引用了其它module,则应该是java -cp ./classes:xx/other-module-classes:xx/XX.jar。。。这样子引用其它module和jar包;

3)java命令还可以指定JVM的其它参数,如java -cp ./classes-server -d64 -Dmypro=33 me.silentdoer.terminalprojtest.Entrance等等,这里-server表示jvm以server模式执行,-d64表示以64位数据模式执行[需要安装的是64位jre],-Dmypro=33表示在系统参数里添加key-value为mypro=33;

4)经常用来优化jvm和配置全局变量的参数:

1.-server,如tomcat启动时会指定;

2.-d64,只要安装的是64位的最好指定;

3.Dmypro=33,如tomcat启动时会指定DCATALINA_HOME=xxx等等

4.-Xms设定此jvm实例的初始的堆大小(内存)也是最小大小,使用方式为-Xms256m,即这个jvm在启动后就会从操作系统那申请256兆的内存;(单位包括k即kb,m即兆,g为G,应该没有byte单位的)

5.-Xmx设定此jvm实例最多可以从操作系统那里申请多少的内存,使用方式为-Xmx2048m,则当随着对应java app需要的内存越多此jvm实例会继续像操作系统申请内存,但不超过2048m;如果java app的需求超过了这个值就会报outofmemory异常;

6.-Xss设定jvm为对应java app每个启动线程分配的大小,jdk1.5以后是1M

这里java.lang.Runtime类中的 freeMemory(), totalMemory(), maxMemory()的输出和上面的配置有莫大关系,maxMemory获得此jvm实例设置的-Xmx大小,而totalMemory则是获得当前从操作系统中请求到的内存大小,freeMemory则是此jvm实例请求了多少内存但是没用上的;

如果没有配置-Xms则jvm实例是一点一点的从操作系统里申请内存的,即基本上用到多少申请多少,因而一定程序上影响jvm的性能,但是freeMemory会很小;而如果一开始就设置-Xms为一个较大的值,则对应java app一开始的freeMemory是会比较大的

,但是随着业务的进行freeMemory会波动,而且要用到内存可以立刻使用而不用等操作系统去分配;(输出的totalMemory会比jvm请求分配的略小,因为jvm自己也需要占用内存,这也是java程序比系统原生程序要慢的原因之一)

重要:这些参数都要在xxx.Entrance之前指定而不能放在后面,即java -cp ./ demo.Test -server -d64 -Xms1024m是错误的,要放在demo.Test的前面,至少我这里1.8测试是这样的;

以上的配置要符合条件,比如你只有4G内存却设置-Xms20480m显然是有问题的,也不能设置满,毕竟操作系统还要运行其它进程而且也未必给jvm实例这么多内存;

上一篇:eclipse中的javac命令与java命令


下一篇:穿越之旅之--android中如何执行java命令