Skip to content

Commit e79355f

Browse files
committed
java
1 parent 801c9d9 commit e79355f

21 files changed

Lines changed: 566 additions & 35 deletions

src/com/yale/test/java/classpath/ClassPathTest.java

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,89 @@
11
package com.yale.test.java.classpath;
2+
/*
3+
* 如果我们需要在src目录下执行javac命令:
4+
* javac -d ../bin ming/Person.java hong/Person.java mr/jun/Arrays.java
5+
* https://www.liaoxuefeng.com/wiki/1252599548343744/1260466914339296
6+
*
7+
* 在Java中,我们经常听到classpath这个东西。网上有很多关于“如何设置classpath”的文章,但大部分设置都不靠谱。
28
9+
到底什么是classpath?
10+
11+
classpath是JVM用到的一个环境变量,它用来指示JVM如何搜索class。
12+
13+
因为Java是编译型语言,源码文件是.java,而编译后的.class文件才是真正可以被JVM执行的字节码。因此,JVM需要知道,如果要加载一个abc.xyz.Hello的类,应该去哪搜索对应的Hello.class文件。
14+
15+
所以,classpath就是一组目录的集合,它设置的搜索路径与操作系统相关。例如,在Windows系统上,用;分隔,带空格的目录用""括起来,可能长这样:
16+
17+
C:\work\project1\bin;C:\shared;"D:\My Documents\project1\bin"
18+
19+
在Linux系统上,用:分隔,可能长这样:
20+
21+
/usr/shared:/usr/local/bin:/home/liaoxuefeng/bin
22+
23+
现在我们假设classpath是.;C:\work\project1\bin;C:\shared,当JVM在加载abc.xyz.Hello这个类时,会依次查找:
24+
25+
<当前目录>\abc\xyz\Hello.class
26+
27+
C:\work\project1\bin\abc\xyz\Hello.class
28+
29+
C:\shared\abc\xyz\Hello.class
30+
31+
注意到.代表当前目录。如果JVM在某个路径下找到了对应的class文件,就不再往后继续搜索。如果所有路径下都没有找到,就报错。
32+
33+
classpath的设定方法有两种:
34+
35+
在系统环境变量中设置classpath环境变量,不推荐;
36+
37+
在启动JVM时设置classpath变量,推荐。
38+
39+
我们强烈不推荐在系统环境变量中设置classpath,那样会污染整个系统环境。在启动JVM时设置classpath才是推荐的做法。实际上就是给java命令传入-classpath或-cp参数:
40+
41+
java -classpath .;C:\work\project1\bin;C:\shared abc.xyz.Hello
42+
43+
或者使用-cp的简写:
44+
45+
java -cp .;C:\work\project1\bin;C:\shared abc.xyz.Hello
46+
47+
没有设置系统环境变量,也没有传入-cp参数,那么JVM默认的classpath为.,即当前目录:
48+
49+
java abc.xyz.Hello
50+
51+
上述命令告诉JVM只在当前目录搜索Hello.class。
52+
在IDE中运行Java程序,IDE自动传入的-cp参数是当前工程的bin目录和引入的jar包。
53+
通常,我们在自己编写的class中,会引用Java核心库的class,例如,String、ArrayList等。这些class应该上哪去找?
54+
有很多“如何设置classpath”的文章会告诉你把JVM自带的rt.jar放入classpath,但事实上,根本不需要告诉JVM如何去Java核心库查找class,JVM怎么可能笨到连自己的核心库在哪都不知道?
55+
不要把任何Java核心库添加到classpath中!JVM根本不依赖classpath加载核心库!
56+
更好的做法是,不要设置classpath!默认的当前目录.对于绝大多数情况都够用了。
57+
jar包
58+
如果有很多.class文件,散落在各层目录中,肯定不便于管理。如果能把目录打一个包,变成一个文件,就方便多了。
59+
jar包就是用来干这个事的,它可以把package组织的目录层级,以及各个目录下的所有文件(包括.class文件和其他文件)都打成一个jar文件,这样一来,无论是备份,还是发给客户,就简单多了。
60+
jar包实际上就是一个zip格式的压缩文件,而jar包相当于目录。如果我们要执行一个jar包的class,就可以把jar包放到classpath中:
61+
java -cp ./hello.jar abc.xyz.Hello
62+
这样JVM会自动在hello.jar文件里去搜索某个类。
63+
那么问题来了:如何创建jar包?
64+
因为jar包就是zip包,所以,直接在资源管理器中,找到正确的目录,点击右键,在弹出的快捷菜单中选择“发送到”,“压缩(zipped)文件夹”,就制作了一个zip文件。然后,把后缀从.zip改为.jar,一个jar包就创建成功。
65+
假设编译输出的目录结构是这样:
66+
见jar.png
67+
package_sample
68+
└─ bin
69+
├─ hong
70+
│ └─ Person.class
71+
│ ming
72+
│ └─ Person.class
73+
└─ mr
74+
└─ jun
75+
└─ Arrays.class
76+
这里需要特别注意的是,jar包里的第一层目录,不能是bin,而应该是hong、ming、mr。如果在Windows的资源管理器中看,应该长这样:
77+
说明打包打得有问题,JVM仍然无法从jar包中查找正确的class,原因是hong.Person必须按hong/Person.class存放,而不是bin/hong/Person.class。
78+
jar包还可以包含一个特殊的/META-INF/MANIFEST.MF文件,MANIFEST.MF是纯文本,可以指定Main-Class和其它信息。JVM会自动读取这个MANIFEST.MF文件,如果存在Main-Class,我们就不必在命令行指定启动的类名,而是用更方便的命令:
79+
java -jar hello.jar
80+
jar包还可以包含其它jar包,这个时候,就需要在MANIFEST.MF文件里配置classpath了。
81+
在大型项目中,不可能手动编写MANIFEST.MF文件,再手动创建zip包。Java社区提供了大量的开源构建工具,例如Maven,可以非常方便地创建jar包。
82+
打jar包的命令 jar -cvf test.jar javalist.txt
83+
javalist.txt文件放你要要打成jar包的class文件
84+
还有打War包的命令:
85+
jar包和war包的介绍和区别 - 简书 这个写到java文档里面去
86+
*/
387
public class ClassPathTest {
488
public static void main(String[] args) {
589
/**
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package com.yale.test.java.classpath;
2+
3+
/*
4+
* https://www.liaoxuefeng.com/wiki/1252599548343744/1281795926523938
5+
* 从Java 9开始,JDK又引入了模块(Module)。
6+
什么是模块?这要从Java 9之前的版本说起。
7+
我们知道,.class文件是JVM看到的最小可执行文件,而一个大型程序需要编写很多Class,并生成一堆.class文件,很不便于管理,所以,jar文件就是class文件的容器。
8+
在Java 9之前,一个大型Java程序会生成自己的jar文件,同时引用依赖的第三方jar文件,而JVM自带的Java标准库,实际上也是以jar文件形式存放的,这个文件叫rt.jar,一共有60多M。
9+
如果是自己开发的程序,除了一个自己的app.jar以外,还需要一堆第三方的jar包,运行一个Java程序,一般来说,命令行写这样:
10+
11+
java -cp app.jar:a.jar:b.jar:c.jar com.liaoxuefeng.sample.Main
12+
注意:JVM自带的标准库rt.jar不要写到classpath中,写了反而会干扰JVM的正常运行。
13+
14+
如果漏写了某个运行时需要用到的jar,那么在运行期极有可能抛出ClassNotFoundException。
15+
16+
所以,jar只是用于存放class的容器,它并不关心class之间的依赖。
17+
18+
从Java 9开始引入的模块,主要是为了解决“依赖”这个问题。如果a.jar必须依赖另一个b.jar才能运行,那我们应该给a.jar加点说明啥的,让程序在编译和运行的时候能自动定位到b.jar,这种自带“依赖关系”的class容器就是模块。
19+
20+
为了表明Java模块化的决心,从Java 9开始,原有的Java标准库已经由一个单一巨大的rt.jar分拆成了几十个模块,这些模块以.jmod扩展名标识,可以在$JAVA_HOME/jmods目录下找到它们:
21+
22+
java.base.jmod
23+
java.compiler.jmod
24+
java.datatransfer.jmod
25+
java.desktop.jmod
26+
...
27+
28+
这些.jmod文件每一个都是一个模块,模块名就是文件名。例如:模块java.base对应的文件就是java.base.jmod。模块之间的依赖关系已经被写入到模块内的module-info.class文件了。所有的模块都直接或间接地依赖java.base模块,只有java.base模块不依赖任何模块,它可以被看作是“根模块”,好比所有的类都是从Object直接或间接继承而来。
29+
30+
把一堆class封装为jar仅仅是一个打包的过程,而把一堆class封装为模块则不但需要打包,还需要写入依赖关系,并且还可以包含二进制代码(通常是JNI扩展)。此外,模块支持多版本,即在同一个模块中可以为不同的JVM提供不同的版本。
31+
编写模块
32+
33+
那么,我们应该如何编写模块呢?还是以具体的例子来说。首先,创建模块和原有的创建Java项目是完全一样的,以oop-module工程为例,它的目录结构如下:
34+
35+
oop-module
36+
├── bin
37+
├── build.sh
38+
└── src
39+
├── com
40+
│ └── itranswarp
41+
│ └── sample
42+
│ ├── Greeting.java
43+
│ └── Main.java
44+
└── module-info.java
45+
46+
其中,bin目录存放编译后的class文件,src目录存放源码,按包名的目录结构存放,仅仅在src目录下多了一个module-info.java这个文件,这就是模块的描述文件。在这个模块中,它长这样:
47+
48+
module hello.world {
49+
requires java.base; // 可不写,任何模块都会自动引入java.base
50+
requires java.xml;
51+
}
52+
53+
其中,module是关键字,后面的hello.world是模块的名称,它的命名规范与包一致。花括号的requires xxx;表示这个模块需要引用的其他模块名。除了java.base可以被自动引入外,这里我们引入了一个java.xml的模块。
54+
55+
当我们使用模块声明了依赖关系后,才能使用引入的模块。例如,Main.java代码如下:
56+
57+
package com.itranswarp.sample;
58+
59+
// 必须引入java.xml模块后才能使用其中的类:
60+
import javax.xml.XMLConstants;
61+
62+
public class Main {
63+
public static void main(String[] args) {
64+
Greeting g = new Greeting();
65+
System.out.println(g.hello(XMLConstants.XML_NS_PREFIX));
66+
}
67+
}
68+
69+
如果把requires java.xml;从module-info.java中去掉,编译将报错。可见,模块的重要作用就是声明依赖关系。
70+
71+
下面,我们用JDK提供的命令行工具来编译并创建模块。
72+
73+
首先,我们把工作目录切换到oop-module,在当前目录下编译所有的.java文件,并存放到bin目录下,命令如下:
74+
75+
$ javac -d bin src/module-info.java src/com/itranswarp/sample/*.java
76+
77+
如果编译成功,现在项目结构如下:
78+
79+
oop-module
80+
├── bin
81+
│ ├── com
82+
│ │ └── itranswarp
83+
│ │ └── sample
84+
│ │ ├── Greeting.class
85+
│ │ └── Main.class
86+
│ └── module-info.class
87+
└── src
88+
├── com
89+
│ └── itranswarp
90+
│ └── sample
91+
│ ├── Greeting.java
92+
│ └── Main.java
93+
└── module-info.java
94+
95+
注意到src目录下的module-info.java被编译到bin目录下的module-info.class。
96+
97+
下一步,我们需要把bin目录下的所有class文件先打包成jar,在打包的时候,注意传入--main-class参数,让这个jar包能自己定位main方法所在的类:
98+
99+
$ jar --create --file hello.jar --main-class com.itranswarp.sample.Main -C bin .
100+
101+
现在我们就在当前目录下得到了hello.jar这个jar包,它和普通jar包并无区别,可以直接使用命令java -jar hello.jar来运行它。但是我们的目标是创建模块,所以,继续使用JDK自带的jmod命令把一个jar包转换成模块:
102+
103+
$ jmod create --class-path hello.jar hello.jmod
104+
105+
于是,在当前目录下我们又得到了hello.jmod这个模块文件,这就是最后打包出来的传说中的模块!
106+
运行模块
107+
108+
要运行一个jar,我们使用java -jar xxx.jar命令。要运行一个模块,我们只需要指定模块名。试试:
109+
110+
$ java --module-path hello.jmod --module hello.world
111+
112+
结果是一个错误:
113+
114+
Error occurred during initialization of boot layer
115+
java.lang.module.FindException: JMOD format not supported at execution time: hello.jmod
116+
117+
原因是.jmod不能被放入--module-path中。换成.jar就没问题了:
118+
119+
$ java --module-path hello.jar --module hello.world
120+
Hello, xml!
121+
122+
那我们辛辛苦苦创建的hello.jmod有什么用?答案是我们可以用它来打包JRE。
123+
打包JRE
124+
125+
前面讲了,为了支持模块化,Java 9首先带头把自己的一个巨大无比的rt.jar拆成了几十个.jmod模块,原因就是,运行Java程序的时候,实际上我们用到的JDK模块,并没有那么多。不需要的模块,完全可以删除。
126+
127+
过去发布一个Java应用程序,要运行它,必须下载一个完整的JRE,再运行jar包。而完整的JRE块头很大,有100多M。怎么给JRE瘦身呢?
128+
129+
现在,JRE自身的标准库已经分拆成了模块,只需要带上程序用到的模块,其他的模块就可以被裁剪掉。怎么裁剪JRE呢?并不是说把系统安装的JRE给删掉部分模块,而是“复制”一份JRE,但只带上用到的模块。为此,JDK提供了jlink命令来干这件事。命令如下:
130+
131+
$ jlink --module-path hello.jmod --add-modules java.base,java.xml,hello.world --output jre/
132+
133+
我们在--module-path参数指定了我们自己的模块hello.jmod,然后,在--add-modules参数中指定了我们用到的3个模块java.base、java.xml和hello.world,用,分隔。最后,在--output参数指定输出目录。
134+
135+
现在,在当前目录下,我们可以找到jre目录,这是一个完整的并且带有我们自己hello.jmod模块的JRE。试试直接运行这个JRE:
136+
137+
$ jre/bin/java --module hello.world
138+
Hello, xml!
139+
140+
要分发我们自己的Java应用程序,只需要把这个jre目录打个包给对方发过去,对方直接运行上述命令即可,既不用下载安装JDK,也不用知道如何配置我们自己的模块,极大地方便了分发和部署。
141+
访问权限
142+
143+
前面我们讲过,Java的class访问权限分为public、protected、private和默认的包访问权限。引入模块后,这些访问权限的规则就要稍微做些调整。
144+
145+
确切地说,class的这些访问权限只在一个模块内有效,模块和模块之间,例如,a模块要访问b模块的某个class,必要条件是b模块明确地导出了可以访问的包。
146+
147+
举个例子:我们编写的模块hello.world用到了模块java.xml的一个类javax.xml.XMLConstants,我们之所以能直接使用这个类,是因为模块java.xml的module-info.java中声明了若干导出:
148+
149+
module java.xml {
150+
exports java.xml;
151+
exports javax.xml.catalog;
152+
exports javax.xml.datatype;
153+
...
154+
}
155+
156+
只有它声明的导出的包,外部代码才被允许访问。换句话说,如果外部代码想要访问我们的hello.world模块中的com.itranswarp.sample.Greeting类,我们必须将其导出:
157+
158+
module hello.world {
159+
exports com.itranswarp.sample;
160+
161+
requires java.base;
162+
requires java.xml;
163+
}
164+
165+
因此,模块进一步隔离了代码的访问权限。
166+
*/
167+
public class ModuleJava {
168+
169+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
policy/billing/billingCustomerUpdate.jsp
2+
WEB-INF/classes/com/isoftstone/pcis/webservice/interfaces/PriceGB/NewPriceServiceClient.class
3+
WEB-INF/classes/com/isoftstone/pcis/policy/app/payseemoney/service/EleAppPolicyService.class
4+
WEB-INF/classes/com/isoftstone/pcis/policy/app/quickapp/action/QuickAppBaseBizAction.class
5+
WEB-INF/classes/com/isoftstone/pcis/policy/prod/base/action/PolicyAppBizAction.class
6+
WEB-INF/classes/com/isoftstone/pcis/policy/prod/vhl/service/PolicyAppVhlService.class
7+
WEB-INF/classes/com/isoftstone/pcis/scoket/newbusiness/servlet/EleAppStatusQueryOuterService.class
8+
policy/universal/quickapp/js/customer_quick.js
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
@echo off
2+
setlocal enabledelayedexpansion
3+
echo --------------------------------导出jar包批处理工具--------------------------------
4+
echo 说明1:请在当前bat文件目录下放置jar_list.txt清单
5+
echo 说明2:根据jar_list.txt将会生成export_file.txt(自动追加相关匿名内部类)
6+
echo 说明3:最终会以export_file.txt为标准执行导出
7+
echo 说明4:成功为[绿色]背景,失败为[红色]背景
8+
echo 即将开始,请输入您的项目名(如policy,nonvhl,finarp,reinsure,isc,vch,claim7,commbase)↓
9+
set /p inputName=
10+
echo !inputName!
11+
set exportJarName=%~n0
12+
set char1=$&set char2=*
13+
set Dyy=%date:~0,4%& set DMM=%date:~5,2%& set Ddd=%date:~8,2%
14+
type nul>export_file.txt
15+
set export_file=%~dp0export_file.txt
16+
echo 重置export_file.txt文件内容
17+
echo 追加export_file.txt文件内容开始
18+
for /f %%a in (jar_list.txt) do (
19+
Rem echo %%~nxa
20+
call :count countResult %%~nxa
21+
set /a countInt=!countResult!*-1
22+
echo %%~a>>%export_file%
23+
set name=%%~na
24+
if %%~xa == ^.class (
25+
call :length lengthResult %%a !countInt!
26+
pushd %cd%
27+
cd %%~da%%~pa
28+
set pattern=!name!%char1%%char2%%%~xa
29+
Rem echo !pattern!
30+
for %%c in (!pattern!) do (
31+
echo 发现遗漏文件!lengthResult!%%c,已追加到export_file.txt
32+
echo !lengthResult!%%c>>%export_file%
33+
)
34+
popd
35+
) else (
36+
echo ''>nul
37+
)
38+
)
39+
echo 追加export_file.txt文件内容结束
40+
echo 将以export_file.txt为标准执行jar包导出
41+
jar -cvf !inputName!%Dyy%%DMM%%Ddd%01.jar @%export_file%
42+
if %errorlevel% equ 0 (
43+
echo 导出最终jar包:!inputName!%Dyy%%DMM%%Ddd%01.jar 成功
44+
color 2f
45+
) else (
46+
echo 导出最终jar包:!inputName!%Dyy%%DMM%%Ddd%01.jar 错误
47+
color 4f
48+
)
49+
50+
goto exit
51+
52+
:count
53+
set /a num=0
54+
set countArg2=%2
55+
:innercount
56+
if not "%countArg2%"=="" (
57+
set /a num+=1
58+
set countArg2=%countArg2:~1%
59+
goto innercount
60+
)
61+
set %1=%num%
62+
goto :eof
63+
64+
:length
65+
set lengthArg2=%2
66+
set lengthArg3=%3
67+
set o=!lengthArg2:~0,%lengthArg3%!
68+
Rem echo %2%3!o!
69+
set %1=!o!
70+
goto :eof
71+
72+
:exit
73+
pause
121 KB
Loading
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
policy/billing/billingCustomerUpdate.jsp
2+
WEB-INF/classes/com/isoftstone/pcis/webservice/interfaces/PriceGB/NewPriceServiceClient.class
3+
WEB-INF/classes/com/isoftstone/pcis/policy/app/payseemoney/service/EleAppPolicyService.class
4+
WEB-INF/classes/com/isoftstone/pcis/policy/app/quickapp/action/QuickAppBaseBizAction.class
5+
WEB-INF/classes/com/isoftstone/pcis/policy/prod/base/action/PolicyAppBizAction.class
6+
WEB-INF/classes/com/isoftstone/pcis/policy/prod/vhl/service/PolicyAppVhlService.class
7+
WEB-INF/classes/com/isoftstone/pcis/scoket/newbusiness/servlet/EleAppStatusQueryOuterService.class
8+
policy/universal/quickapp/js/customer_quick.js
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.yale.test.java.demo;
2+
3+
//一个.java文件只能包含一个public类,但可以包含多个非public类。如果有public类,文件名必须和public类的名字相同。
4+
public class InnerClass {
5+
public static void main(String[] args) {
6+
7+
}
8+
9+
private static void test() {
10+
//定义在一个class内部的class称为嵌套类(nested class),Java支持好几种嵌套类。
11+
System.out.println("由于Java支持嵌套类,如果一个类内部还定义了嵌套类,那么,嵌套类拥有访问private的权限:");
12+
}
13+
14+
static class Inner {
15+
public void hi() {
16+
InnerClass.test();//静态内部类,可以访问外部类的private属性和方法
17+
}
18+
}
19+
}

0 commit comments

Comments
 (0)