参考:
https://zhuanlan.zhihu.com/p/92906774
Java执行过程如下图:
class Hello{
public static void main(String[] agrs){
System.out.println("hello");
}
}
F:\你的工程所在路径>javac Hello.java
F:\你的工程所在路径> jar -cvf hello.jar Hello.class
已添加清单
正在添加: Hello.class(输入 = 409) (输出 = 280)(压缩了 31%)
F:\你的工程所在路径>java -jar hello.jar
hello.jar中没有主清单属性
解决办法1:
java -classpath hello.jar Hello
解决办法2:添加Main-Class属性
MENIFEST.MF书写规范见:
https://blog.csdn.net/shadow_zed/article/details/80393757
最后一样一定要回车,空一行,不然无法识别最后一行的配置。
Manifest-Version、Main-Class和Class-Path后面跟着一个英文的冒号,冒号后面必须跟着一个空格,然后才是版本号、类和ClassPath。
Class-Path中的各项应使用空格分隔,不是逗号或分号。
Class-Path中如果有很多项,写成一行打包的时候会报错line too long,这时需要把Class-Path分多行写。注意:从第二行开始,必须以两个空格开头,三个以上我没试过,不过不用空格开头和一个空格开头都是不行的,我已经试过了。
Class-Path写完之后最后一定要有一个空行。
jar包内有些配置文件想放在jar包外面,比如文件config.properties:如果这个文件是以路径方式载入的,比如new file("./config/config.properties"),那么将config.properties放在jar包相同目录下的config目录下即可,也就是说“./”路径等价于jar包所在目录;如果这个文件是以ClassPath下的文件这种方式载入的,比如在Spring中载入classpath:config.properties,则在MF文件的配置文件的ClassPath中添加“./”,然后将这个配置文件与jar包放在同一个目录即可,当然也可以在MF文件的配置文件的ClassPath中添加“./config/”,然后把配置文件都放在jar包相同目录下的config目录下。
用压缩软件打开hello.jar,会发现里面多了一个META-INF文件夹,里面有一个MENIFEST.MF的文件,用记事本打开
Manifest-Version: 1.0
Created-By: 1.8.0_121 (Oracle Corporation)
Main-Class: Hello
在第三行的位置写入 Main-Class: Hello (注意冒号后面有一个空格,整个文件最后有一行空行),保存
再次运行
java -jar hello.jar
此时成功在控制台看到 hello ,成功
备注:
.jar分2种,有一种只是当作调用包;另外一种是可以直接执行,类似.exe。
但是一般可直接执行的*.jar文件,用winrar等解压软件打开会发现都有一个META-INF的文件夹,这个文件夹中必须有个MANIFEST.MF文件,这个文件主要是用来描述可执行的*.jar的执行入口文件(通常是含有main()方法的类文件),格式大体如下:
Manifest-Version: 1.0
Created-By: 1.4.2_08 (Sun Microsystems Inc.)
Main-Class: Main
这边Main既是运行类,含有main()方法的一个类文件,名字为Main.class。
方法步骤
(0)创建目录F:\Example_Project
(1)创建edu.hncj.vip目录,用记事本写一个Hello.java的文件
package edu.hncj.vip;
class Hello{
public static void main(String[] agrs){
System.out.println("hello");
}
}
(2)用命令行进入到该目录下,编译这个文件javac -d . Hello.java
F:\Example_Project\edu\hncj\vip>javac -d . Hello.java
javac -d . 中 -d参数:编译时,同时由系统自动生成package目录 .参数:指定Hello.java类的包名 将要生成在哪个目录下,.表示当前目录,可以任意指定
(3)将编译后的Hello.class文件打成jar包
jar -cvf hello.jar edu\hncj\vip\
F:\Example_Project\edu\hncj\vip>jar -cvf hello.jar edu\hncj\vip\
已添加清单
正在添加: edu/hncj/vip/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: edu/hncj/vip/Hello.class(输入 = 422) (输出 = 292)(压缩了 30%)
c表示要创建一个新的jar包,v表示创建的过程中在控制台输出创建过程的一些信息,f表示给生成的jar包命名
(4)运行jar包
java -jar hello.jar
这时会报如下错误 hello.jar中没有主清单属性
用压缩软件打开hello.jar,会发现里面多了一个META-INF文件夹,里面有一个MENIFEST.MF的文件,用记事本打开
添加Main-Class属性
Manifest-Version: 1.0
Created-By: 1.8.0_181 (Oracle Corporation)
Main-Class: edu.hncj.vip.Hello
在第三行的位置写入 Main-Class: Hello (注意冒号后面有一个空格,整个文件最后有一行空行),保存
再次运行 java -jar hello.jar ,此时成功在控制台看到 hello ,成功
(0)创建目录F:\Example_Project\两个类的jar>
(1)用记事本写一个Hello.java和一个Student.java的文件
目的是让Hello调用Student的speak方法
class Hello{
public static void main(String[] agrs){
//System.out.println("hello");
Student.speak();
}
}
class Student{
public static void speak(){
System.out.println("hello,I'm 20级学生 ");
}
}
(2)编译: javac -encoding UTF-8 Hello.java
此时Hello.java和Student.java同时被编译,因为Hello中调用了Student,在编译Hello的过程中发现还需要编译Student
F:\Example_Project\两个类的jar>javac -encoding UTF-8 Hello.java
(3)打jar包
在F:\Example_Project\两个类的jar>下创建META-INF文件夹
F:\Example_Project\两个类的jar>mkdir META-INF
在META-INF下创建MANIFEST.MF
F:\Example_Project\两个类的jar>type nul>META-INF\MANIFEST.MF
这次我们换一种方式直接定义Main-Class。
Manifest-Version: 1.0
Created-By: 1.8.0_121 (Oracle Corporation)
Main-Class: Hello
事先准备好上述的MANIFEST.MF文件,并存放在META-INF文件夹下,此时打jar包的命令如下
F:\Example_Project\两个类的jar> jar -cvfm hello.jar META-INF\MANIFEST.MF Hello.class Student.class
已添加清单
正在添加: Hello.class(输入 = 293) (输出 = 223)(压缩了 23%)
正在添加: Student.class(输入 = 408) (输出 = 308)(压缩了 24%)
jar -cvfm hello.jar META-INF\MANIFEST.MF Hello.class Student.class命令解释如下:
表示用第一个文件当做MENIFEST.MF文件,hello.jar作为名称,将Hello.class和Student.class打成jar包。其中多了一个参数m,表示要定义MANIFEST文件
(4)运行 java -jar hello.jar ,此时成功在控制台看到 hello ,成功
F:\Example_Project\两个类的jar> java -jar hello.jar
hello,I'm 20级学生
(0)创建文件夹
F:\Example_Project\有目录结构的两个类jar
(1)我们将上一个稍稍变化一下,将Student这个类放在edu.hncj.vip包下,Hello放在com包下
修改Hello.java文件
package com;
import edu.hncj.vip.Student;
class Hello{
public static void main(String[] agrs){
//System.out.println("hello");
Student.speak();
}
}
修改Student.java文件,注意需要修改为public公共类
package edu.hncj.vip;
public class Student{
public static void speak(){
System.out.println("hello,I'm 20级学生 ");
}
}
查看当前目录结构
F:\Example_Project\有目录结构的两个类jar>tree . /f /a
卷 工作 的文件夹 PATH 列表
卷序列号为 00000057 5296:88B3
F:\EXAMPLE_PROJECT\有目录结构的两个类JAR
+---com
| Hello.java
|
\---edu
\---hncj
\---vip
Student.java
方法步骤
(1)编译Hello.java
javac -encoding UTF-8 -d . com\Hello.java
编译Hello.java时,自动编译Student.java文件
(2)打jar包,同样准备好MANIFEST文件
在F:\Example_Project\两个类的jar>下创建META-INF文件夹
F:\Example_Project\有目录结构的两个类jar>mkdir META-INF
在META-INF下创建MANIFEST.MF
F:\Example_Project\有目录结构的两个类jar>type nul>META-INF\MANIFEST.MF
这次我们换一种方式直接定义Main-Class。注意后面的空行
Manifest-Version: 1.0
Created-By: 1.8.0_121 (Oracle Corporation)
Main-Class: com.Hello
此处有空行
事先准备好上述的MANIFEST.MF文件,并存放在META-INF文件夹下,此时打jar包的命令如下
jar -cvfm hello.jar META-INF\MANIFEST.MF com\Hello.class edu\hncj\vip
F:\Example_Project\有目录结构的两个类jar>jar -cvfm hello.jar META-INF\MENIFEST.MF com\Hello.class edu\hncj\vip
已添加清单
正在添加: com/Hello.class(输入 = 310) (输出 = 238)(压缩了 23%)
正在添加: edu/hncj/vip/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: edu/hncj/vip/Student.class(输入 = 421) (输出 = 319)(压缩了 24%)
正在添加: edu/hncj/vip/Student.java(输入 = 146) (输出 = 130)(压缩了 10%)
输出信息表示已经把哪些文件打包在hello.jar中
(3)运行 java -jar hello.jar ,此时成功在控制台看到 hello ,成功
我们将场景稍稍变得复杂一点,看看jar包中需要引入其他jar包的场景
1、两个jar包间相互调用——调用jar外的jar输出hello
最终生成的jar包结构
hello.jar
tom.jar
方法步骤
(0)生成student.jar
准备:创建Student.java文件
public class Student{
public static void speak(){
System.out.println("hello,I'm 20级学生 ");
}
}
在Student.java同级目录下打开cmd
执行如下命令编译java文件
javac -encoding UTF-8 Student.java
执行jar -cvf student.jar Student.class 命令生成jar包
F:\Example_Project\两个jar相互调 用>javac -encoding UTF-8 Student.java
F:\Example_Project\两个jar相互调 用>jar -cvf student.jar Student.class
已添加清单
正在添加: Student.class(输入 = 408) (输出 = 307)(压缩了 24%)
(1)编写一个Hello.java并将其编译成Hello.class,注意,由于Hello里面引用了Student类的speak方法,因此在打jar包时应使用-cp参数,将student.jar包引入
Hello.java文件内容:
class Hello{
public static void main(String[] agrs){
//System.out.println("hello");
Student.speak();
}
}
执行 javac命令编译Hello.java
如果直接编译Hello.java会提示如下错误
F:\Example_Project\两个jar相互调用>javac Hello.java
Hello.java:5: 错误: 找不到符号
Student.speak();
^
符号: 变量 Student
位置: 类 Hello
1 个错误
可以在编译Hello.java的同时,把依赖的jar放在classpath下
F:\Example_Project\两个jar相互调 用>javac -cp student.jar Hello.java
这里的 -cp 表示 -classpath,指的是把student.jar加入classpath路径下
(2)将hello.class达成jar包
创建META-INF目录,创建MANIFEST.MF 文件,注意末尾的空行
Manifest-Version: 1.0
Created-By: 1.8.0_181 (Oracle Corporation)
Main-Class: Hello
这里有空行
达成jar包,执行jar -cvfm hello.jar META-INF/MANIFEST.MF hello.class
F:\Example_Project\两个jar相互调 用>jar -cvfm hello.jar META-INF/MANIFEST.MF Hello.class
已添加清单
正在添加: Hello.class(输入 = 293) (输出 = 223)(压缩了 23%)
目前可以顺利的达成jar包,但运行时会出错,如下。
(3)此时运行 java -jar 发现报错 ClassNotFoundException:Student
F:\Example_Project\两个jar相互调 用>java -jar hello.jar
Exception in thread "main" java.lang.NoClassDefFoundError: Student
at Hello.main(Hello.java:5)
Caused by: java.lang.ClassNotFoundException: Student
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 1 more
原因很简单,引入jar包需要在MENIFEST.MF文件中配置一个新属性:Class-Path,路径指向你需要的所有jar包
现在打开hello.jar中MENIFEST.MF文件,添加Class-Path。应该变成
Manifest-Version: 1.0
Created-By: 1.8.0_121 (Oracle Corporation)
Main-Class: Hello
Class-Path: student.jar
(4)好了,修改这个文件,再次运行,发现成功在控制台输出 hello
tips:引入多个jar包,中间用空格隔开
至此,我们可以总结出,命令变化如下
javac -cp xxx.jar 要编译的文件 -d 目标位置
jar -cvfm 命名 MENIFEST文件 要打包的文件1 要打包的文件2
当项目中我们把所需要的第三方jar包也打进了我们自己的jar包中时,如果仍然按照上述操作方式,会报找不到Class异常。原因就是jar引用不到放在自己内部的jar包。
这种情况的具体实现细节比较复杂,可以先参考这篇文章:
http://www.cnblogs.com/adolfmc/archive/2012/10/07/2713562.html
引用方法1:使用AppClassloader来加载
引用方法2:也可以把需要加载的jar都扔到%JRE_HOME%/lib/ext下面,这个目录下的jar包会在Bootstrap Classloader工作完后由Extension Classloader来加载。非常方便,非常省心。
(1)本部分使用AppClassloader加载
在F:\Example_Project\jar包内含有jar包 的目录下创建edu.hncj.vip.Student.java
package edu.hncj.vip;
public class Student{
public static void speak(){
System.out.println("hello,I'm 20级学生 ");
}
}
(2)生成student.jar文件
编译Student.java文件,执行 javac -d . -encoding UTF-8 edu/hncj/vip/Student.java命令
F:\Example_Project\jar包内含有jar包>javac -d . -encoding UTF-8 edu/hncj/vip/Student.java
生成student.jar包,jar -cvf student.jar edu\hncj\vip\
F:\Example_Project\jar包内含有jar包>jar -cvf student.jar edu\hncj\vip\
已添加清单
正在添加: edu/hncj/vip/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: edu/hncj/vip/Student.class(输入 = 421) (输出 = 319)(压缩了 24%)
正在添加: edu/hncj/vip/Student.java(输入 = 146) (输出 = 130)(压缩了 10%)
在 F:\Example_Project\jar包内含有jar包 目录下创建lib目录,把student放置在lib目录下
(3)创建Hello.java,并编译
在 F:\Example_Project\jar包内含有jar包 目录下创建com.Hello.java,Hello中调用了Student
package com;
import edu.hncj.vip.Student;
class Hello{
public static void main(String[] agrs){
//System.out.println("hello");
Student.speak();
}
}
编译Hello.java文件,javac -d . -encoding UTF-8 com/Hello.java
F:\Example_Project\jar包内含有jar包>javac -d . -encoding UTF-8 com/Hello.java
生成hello.jar包,jar -cvf hello.jar com\ lib\
F:\Example_Project\jar包内含有jar包>jar -cvf hello.jar com\ lib\
已添加清单
正在添加: com/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: com/Hello.class(输入 = 310) (输出 = 237)(压缩了 23%)
正在添加: com/Hello.java(输入 = 175) (输出 = 138)(压缩了 21%)
正在添加: lib/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: lib/student.jar(输入 = 1179) (输出 = 790)(压缩了 32%)
(4)运行hello.jar,java -cp hello.jar com.Hello
F:\Example_Project\jar包内含有jar包>java -cp hello.jar com.Hello
Exception in thread "main" java.lang.NoClassDefFoundError: edu/hncj/vip/Student
at com.Hello.main(Hello.java:6)
Caused by: java.lang.ClassNotFoundException: edu.hncj.vip.Student
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 1 more
修改hello.jar中的MANIFEST.MF文件
Manifest-Version: 1.0
Created-By: 1.8.0_181 (Oracle Corporation)
Class-Path: lib/student.jar
再次运行java -cp hello.jar com.Hello即可
(1)在F:\Example_Project\读取jar外的文件 目录中创建Hello.java
Hello.java文件
import java.io.InputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.FileInputStream;
class Hello{
public static void main(String[] args) throws Exception{
Hello hello = new Hello();
InputStream is = new FileInputStream("jarconf.txt");
print(is);
}
/**
* 读取文件,输出里面的内容,通用方法
*/
public static void print(InputStream inputStream) throws Exception {
InputStreamReader reader = new InputStreamReader(inputStream, "utf-8");
BufferedReader br = new BufferedReader(reader);
String s = "";
while ((s = br.readLine()) != null)
System.out.println(s);
inputStream.close();
}
}
(2)同级目录下创建jarconf.txt文本文件,内容如下:
calssname=vip
desc=这是一个外部配置文件
(3)编译并打包java文件 :
编译Hello.java文件
javac -encoding UTF-8 Hello.java
打包hello.jar文件 :
jar -cvf hello.jar Hello.class
修改META-INF\MANIFEST.MF文件添加
Manifest-Version: 1.0
Created-By: 1.8.0_181 (Oracle Corporation)
Main-Class: Hello
执行jar:java -jar hello.jar
完整代码如下:
# 编译Hello.java文件为class文件
F:\Example_Project\读取jar内的文 件>javac -encoding UTF-8 Hello.java
# 生成jar包,
F:\Example_Project\读取jar外的文 件>jar -cvf hello.jar Hello.class
已添加清单
正在添加: Hello.class(输入 = 1008) (输出 = 594)(压缩了 41%)
# 修改hello.jar中的META-INF\MANIFEST.MF
# 执行jar
F:\Example_Project\读取jar外的文 件>java -jar hello.jar
calssname=vip
desc=这是一个外部配置文件
此时的目录结构
F:\Example_Project\读取jar外的文 件>tree . /f /a
卷 工作 的文件夹 PATH 列表
卷序列号为 00000001 5296:88B3
F:\EXAMPLE_PROJECT\读取JAR外的文 件
| Hello.class
| hello.jar
| Hello.java
| jarconf.txt
|
\---META-INF
MANIFEST.MF
(1)在F:\Example_Project\读取jar内的文件中创建Hello.java
Hello.java文件
import java.io.InputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
class Hello{
public static void main(String[] args) throws Exception{
Hello hello = new Hello();
InputStream is = hello.getClass().getResourceAsStream("jarconf.txt");
print(is);
}
/**
* 读取文件,输出里面的内容,通用方法
*/
public static void print(InputStream inputStream) throws Exception {
InputStreamReader reader = new InputStreamReader(inputStream, "utf-8");
BufferedReader br = new BufferedReader(reader);
String s = "";
while ((s = br.readLine()) != null)
System.out.println(s);
inputStream.close();
}
}
同级目录下创建jarconf.txt文本文件,内容如下:
calssname=vip
desc=这是一个内部配置文件
(2)编译并打成jar包
编译java文件 :
javac -encoding UTF-8 Hello.java
打包class文件 :
jar -cvfm hello.jar META-INF\MANIFEST.MF .
执行jar:
java -jar hello.jar
(3)完整过程如下:
# 编译Hello.java文件为class文件
F:\Example_Project\读取jar内的文件>javac -encoding UTF-8 Hello.java
# 生成jar包,同时制定META-INF\MANIFEST.MF
F:\Example_Project\读取jar内的文件>jar -cvfm hello.jar META-INF\MANIFEST.MF .
已添加清单
正在添加: Hello.class(输入 = 1109) (输出 = 637)(压缩了 42%)
正在添加: Hello.java(输入 = 755) (输出 = 385)(压缩了 49%)
正在添加: jarconf.txt(输入 = 50) (输出 = 55)(压缩了 -10%)
正在忽略条目META-INF/
正在忽略条目META-INF/MANIFEST.MF
# 执行hello.jar
F:\Example_Project\读取jar内的文 件>java -jar hello.jar
calssname=vip
desc=这是一个内部配置文件
Maven是一种构建工具(打包项目)、依赖管理工具(资源依赖管理工具:主要用于集成资源)、项目信息聚合工具。
(1)安装过程
最简安装:
选择版本
下载解压
配置MAVEN_HOME环境变量
通过mvn -version确定下版本
这种安装没有修改setting.xml,会导致Maven下载的依赖位于C盘,并且默认从中央仓库下载,可能网速慢一些,这时可修改如下配置:
打开D:\安装目录\maven\conf\settings.xml文件,查找修改如下配置:
<localRepository>d:/apache/maven/maven-repolocalRepository>
<mirrors>
<mirror>
<id>nexus-aliyunid>
<mirrorOf>*,!jeecg,!jeecg-snapshots,!getui-nexusmirrorOf>
<name>Nexus aliyunname>
<url>http://maven.aliyun.com/nexus/content/groups/publicurl>
mirror>
mirrors>
<repositories>
<repository>
<id>codehausSnapshotsid>
<name>Codehaus Snapshotsname>
<releases>
<enabled>falseenabled>
<updatePolicy>alwaysupdatePolicy>
<checksumPolicy>warnchecksumPolicy>
releases>
<snapshots>
<enabled />
<updatePolicy />
<checksumPolicy />
snapshots>
<url>http://snapshots.maven.codehaus.org/maven2url>
<layout>defaultlayout>
repository>
repositories>
我们可以通过在命令行创建Maven工程,也可以通过IDEA创建Maven工程。
打开命令行窗口,跳转到 D:\maven 目录,执行以下 mvn 命令。
mvn archetype:generate -DgroupId=edu.hncj -DartifactId=helloMaven -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
参数说明:
DgroupId: 项目组 ID,通常为组织名或公司网址的反写。
DartifactId: 项目名。
DarchetypeArtifactId: 指定 ArchetypeId,maven-archetype-quickstart 用于快速创建一个简单的 Maven 项目。
DinteractiveMode: 是否使用交互模式。
需要注意的是,每个Maven工程都需要有一个唯一的定位坐标,即上述的GAV(groupId: 项目组 ID,DartifactId: 项目名, Version 唯一发行版本号)
工程结构:约定优于配置
约定优于配置(Convention Over Configuration)是 Maven 最核心的涉及理念之一 ,Maven对项目的目录结构、测试用例命名方式等内容都做了规定,凡是使用 Maven 管理的项目都必须遵守这些规则。
Maven 项目构建过程中,会自动创建默认项目结构,开发人员仅需要在相应目录结构下放置相应的文件即可。
例如,下表显示了项目源代码文件,资源文件和其他配置在 Maven 项目中的默认位置。
文件 目录
Java 源代码 src/main/java
资源文件 src/main/resources
测试源代码 src/test/java
测试资源文件 src/test/resources
打包输出文件 target
编译输出文件 target/classes
1、mvn compile 编译,将Java 源程序编译成 class 字节码文件。
2、mvn test 测试,并生成测试报告
3、mvn clean 将以前编译得到的旧的 class 字节码文件删除
4、mvn pakage 打包,动态 web工程打 war包,Java工程打 jar 包。
5、mvn install 将项目生成 jar 包放在仓库中,以便别的模块调用
参考资源:
https://blog.csdn.net/weixin_38569499/article/details/91456988
https://blog.csdn.net/liuxiao723846/article/details/106578518
常用标签:
modelVersion maven的模型版本,跟随maven定义,一般不能修改。
groupId
项目的组织,一般是顶级域名名称+公司或者组织名称,如alibaba的项目组织为com.alibaba,如果你们公司的域名为www.abc.com,那你们的项目组织最好就以com.abc命名。artifactId
项目的名称,也是项目之间引进依赖的重要标识。像alibaba有个dubbo项目,dubbo项目可能又关联了许多子项目,所以artifactId就会定义有dubbo、dubbo-config这样的工程。version
项目的版本,项目迭代开发,可能经历许多个版本,靠这个定义,默认是打包的组成部分,如dubbo-2.8.4.jar。另外,版本有两个概念,0.0.1-SNAPSHOT这样的是快照版本,0.0.1-RELEASE或者不带SNAPSHOT的就是RELEASE版本。packaging 打包类型,有这几种类型:pom, jar, maven-plugin, ejb, war, ear, rar,
par,默认不填就是jar包,一般常用的是pom、jar、war。properties
配置公共属性,如spring-web,spring-aop你要依赖这两个,它们肯定是同一个版本的如4.5.0,可以把版本号放在属性上统一管理,也方便维护。
dependecies
配置工程的依赖信息builds
用来管理执行maven声明周期的,包括plugins,maven-dependency-plugin,resources
设定GAV坐标:
edu.hncj
VIP01
1.0-SNAPSHOT
在工程下的pom.xml中,新增如下标签:
<dependencies>
<dependency>
<groupId>com.google.zxinggroupId>
<artifactId>coreartifactId>
<version>3.3.1version>
dependency>
<dependency>
<groupId>com.google.zxinggroupId>
<artifactId>javaseartifactId>
<version>3.3.1version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.6.0version>
<configuration>
<source>1.8source>
<target>1.8target>
<encoding>UTF-8encoding>
configuration>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-assembly-pluginartifactId>
<version>3.1.1version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependenciesdescriptorRef>
descriptorRefs>
<encoding>UTF-8encoding>
configuration>
<executions>
<execution>
<id>make-assemblyid>
<phase>packagephase>
<goals>
<goal>singlegoal>
goals>
execution>
executions>
plugin>
plugins>
build>
edu.hncj.VIP01包下,创建QRCodeGenerator类,内容如下:
package edu.hncj.VIP01;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
public class QRCodeGenerator {
public static byte[] createQRCode(int width, int height, String content) throws WriterException, IOException {
// 二维码基本参数设置
Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");// 设置编码字符集utf-8
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);// 设置纠错等级L/M/Q/H,纠错等级越高越不易识别,当前设置等级为最高等级H
hints.put(EncodeHintType.MARGIN, 0);// 可设置范围为0-10,但仅四个变化0 1(2) 3(4 5 6) 7(8 9 10)
// 生成图片类型为QRCode
BarcodeFormat format = BarcodeFormat.QR_CODE;
// 创建位矩阵对象
BitMatrix bitMatrix = new MultiFormatWriter().encode(content, format, width, height, hints);
// 设置位矩阵转图片的参数
// MatrixToImageConfig config = new MatrixToImageConfig(Color.black.getRGB(), Color.white.getRGB());
// 位矩阵对象转流对象
ByteArrayOutputStream os = new ByteArrayOutputStream();
MatrixToImageWriter.writeToStream(bitMatrix, "png", os);
return os.toByteArray();
}
public static void main(String[] args) throws WriterException, IOException {
byte[] b = createQRCode(100, 100, "遇见最好的自己!");
OutputStream os = new FileOutputStream("E:\\bestme.png");
os.write(b);
os.close();
}
}
右键执行,即可运行成功,同时在E盘根目录下,生成bestme.png文件,用微信扫描二维码后,可看到信息 “遇见最好的自己!”
双击maven下的package,会在target生成jar包
进入到G:\ideaproject\VIP01\targe目录下,输入CMD,然后再命令行窗口输入
G:\ideaproject\VIP01\target>java -cp VIP01-1.0-SNAPSHOT-jar-with-dependencies.jar edu.hncj.VIP01.QRCodeGenerator
执行代码,会在E盘根目录下生成bestme.png图片
Git是目前世界上最先进的分布式版本控制系统(没有之一)。
Git有什么特点?简单来说就是:高端大气上档次!由linux之父Linus编写。
Git有
workspace: 工作区
index/Stage: 暂存区
Repository: 本地仓库
Remote: 远程仓库
四个区域,具体作业如下图:
平时我们写的代码都是在工作区,执行add命令后就是提交到了暂存区,再执行commit命令后就把代码提交到了版本库了,最后再执行push命令把本地代码提交到远程版本库。下图也基本是这个意思。
到Git官网下载,网站地址:https://git-scm.com/downloads
下一步安装即可
安装完成,配置用户名和邮箱
$ git config --global user.name "Your Name"
$ git config --global user.email "[email protected]"
查看用户名和邮箱地址
$ git config user.name
$ git config user.email
修改局部用户名和邮箱地址:
$ cd ~/you project
$ git config user.name "username"
$ git config user.email "email"
版本库可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。
$ mkdir learngit
$ cd learngit
$ git init
Initialized empty Git repository in /Users/michael/learngit/.git/
瞬间Git就把仓库建好了,而且告诉你是一个空的仓库(empty Git repository),细心的读者可以发现当前目录下多了一个.git的目录,这个目录是Git来跟踪管理版本库的,没事千万不要手动修改这个目录里面的文件,不然改乱了,就把Git仓库给破坏了。
如果你没有看到.git目录,那是因为这个目录默认是隐藏的,用ls -ah命令就可以看见。
在版本库learngit的目录下,编写一个readme.txt文件,内容如下:
Git is a version control system.
Git is free software.
一定要放到learngit目录下(子目录也行),因为这是一个Git仓库,放到其他地方Git再厉害也找不到这个文件。
和把大象放到冰箱需要3步相比,把一个文件放到Git仓库只需要两步。
第一步,用命令git add告诉Git,把文件添加到仓库:
$ git add readme.txt
执行上面的命令,没有任何显示,这就对了,Unix的哲学是“没有消息就是好消息”,说明添加成功。
第二步,用命令git commit告诉Git,把文件提交到仓库:
$ git commit -m "wrote a readme file"
[master (root-commit) eaadf4e] wrote a readme file
1 file changed, 2 insertions(+)
create mode 100644 readme.txt
修改readme.txt文件,改成如下内容:
Git is a distributed version control system.
Git is free software.
现在,运行git status命令看看结果:
$ git status
On branch master
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: readme.txt
no changes added to commit (use "git add" and/or "git commit -a")
git status命令可以让我们时刻掌握仓库当前的状态,上面的命令输出告诉我们,readme.txt被修改过了,但还没有准备提交的修改。
用git diff这个命令查看具体的修改内容:
$ git diff readme.txt
diff --git a/readme.txt b/readme.txt
index 46d49bf..9247db6 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1,2 @@
-Git is a version control system.
+Git is a distributed version control system.
Git is free software.
git diff顾名思义就是查看difference,显示的格式正是Unix通用的diff格式,可以从上面的命令输出看到,我们在第一行添加了一个distributed单词。
知道了对readme.txt作了什么修改后,再把它提交到仓库就放心多了,提交修改和提交新文件是一样的两步,第一步是git add:
$ git add readme.txt
同样没有任何输出。在执行第二步git commit之前,我们再运行git status看看当前仓库的状态:
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD ..." to unstage)
modified: readme.txt
git status告诉我们,将要被提交的修改包括readme.txt,下一步,就可以放心地提交了:
$ git commit -m "add distributed"
[master e475afc] add distributed
1 file changed, 1 insertion(+), 1 deletion(-)
提交后,我们再用git status命令看看仓库的当前状态:
$ git status
On branch master
nothing to commit, working tree clean
Git告诉我们当前没有需要提交的修改,而且,工作目录是干净(working tree clean)的。
小结
查看历史提交记录:
版本控制系统肯定有某个命令可以告诉我们历史记录,在Git中,我们用git log命令查看:
$ git log
commit 1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master)
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:06:15 2018 +0800
append GPL
commit e475afc93c209a690c39c13a46716e8fa000c366
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:03:36 2018 +0800
add distributed
commit eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 20:59:18 2018 +0800
wrote a readme file
git log命令显示从最近到最远的提交日志,我们可以看到3次提交,最近的一次是append GPL,上一次是add distributed,最早的一次是wrote a readme file。
如果嫌输出信息太多,看得眼花缭乱的,可以试试加上–pretty=oneline参数:
$ git log --pretty=oneline
1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master) append GPL
e475afc93c209a690c39c13a46716e8fa000c366 add distributed
eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0 wrote a readme file
回归历史版本
首先,Git必须知道当前版本是哪个版本,在Git中,用HEAD表示当前版本,也就是最新的提交1094adb…(注意我的提交ID和你的肯定不一样),上一个版本就是HEAD,上上一个版本就是HEAD,当然往上100个版本写100个比较容易数不过来,所以写成HEAD~100。
现在,我们要把当前版本append GPL回退到上一个版本add distributed,就可以使用git reset命令:
$ git reset --hard HEAD^
HEAD is now at e475afc add distributed
–hard参数有啥意义?这个后面再讲,现在你先放心使用。
备注:如果版本回滚后,git log会丢失之后的记录,也就是说无法再查看到当前版本之后的版本了,这时可采用git reflog命令,该命令可以记录每一次操作
Git提供了一个命令git reflog用来记录你的每一次命令:
$ git reflog
e475afc HEAD@{1}: reset: moving to HEAD^
1094adb (HEAD -> master) HEAD@{2}: commit: append GPL
e475afc HEAD@{3}: commit: add distributed
eaadf4e HEAD@{4}: commit (initial): wrote a readme file
从命令的历史执行记录可以看到每一次的变化,这样就可以回滚版本到指定的版本了。
总结下:
命令git checkout
命令git checkout – readme.txt意思就是,把readme.txt文件在工作区的修改全部撤销,这里有两种情况:
一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
一种是readme.txt已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。
总之,就是让这个文件回到最近一次git commit或git add时的状态。
命令git rm
从版本库中删除该文件,那就用命令git rm删掉,并且git commit:
$ git rm test.txt
rm 'test.txt'
$ git commit -m "remove test.txt"
[master d46f35e] remove test.txt
1 file changed, 1 deletion(-)
delete mode 100644 test.txt
现在,文件就从版本库中被删除了。
如果删错了,可以把误删的文件从版本库中恢复到最新版本:
$ git checkout -- test.txt
git checkout其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
添加远程仓库
假设你已经在本地创建了一个Git仓库后,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步,这样,GitHub上的仓库既可以作为备份,又可以让其他人通过该仓库来协作,真是一举多得。
把已有的本地仓库与远程仓库联在本地的learngit仓库下运行命令:
$ git remote add origin git@github.com:michaelliao/learngit.git
请千万注意,把上面的michaelliao替换成你自己的GitHub账户名,否则,你在本地关联的就是我的远程库,关联没有问题,但是你以后推送是推不上去的,因为你的SSH Key公钥不在我的账户列表中。
添加后,远程库的名字就是origin,这是Git默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库。
下一步,就可以把本地库的所有内容推送到远程库上:
$ git push -u origin master
Counting objects: 20, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (15/15), done.
Writing objects: 100% (20/20), 1.64 KiB | 560.00 KiB/s, done.
Total 20 (delta 5), reused 0 (delta 0)
remote: Resolving deltas: 100% (5/5), done.
To github.com:michaelliao/learngit.git
* [new branch] master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
把本地库的内容推送到远程,用git push命令,实际上是把当前分支master推送到远程。
由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
推送成功后,可以立刻在GitHub页面中看到远程库的内容已经和本地一模一样:
从远程库克隆
上次我们讲了先有本地库,后有远程库的时候,如何关联远程库。
现在,假设我们从零开发,那么最好的方式是先创建远程库,然后,从远程库克隆。
现在,远程库已经准备好了,下一步是用命令git clone克隆一个本地库:
$ git clone git@github.com:michaelliao/gitskills.git
Cloning into 'gitskills'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 3
Receiving objects: 100% (3/3), done.
注意把Git库的地址换成你自己的,然后进入gitskills目录看看,已经有README.md文件了:
$ cd gitskills
$ ls
README.md
如果有多个人协作开发,那么每个人各自从远程克隆一份就可以了。
你也许还注意到,GitHub给出的地址不止一个,还可以用https://github.com/michaelliao/gitskills.git这样的地址。实际上,Git支持多种协议,默认的git://使用ssh,但也可以使用https等其他协议。
使用https除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放http端口的公司内部就无法使用ssh协议而只能用https。
小结
要克隆一个仓库,首先必须知道仓库的地址,然后使用git clone命令克隆。
Git支持多种协议,包括https,但ssh协议速度最快。
简易的命令行入门教程:
Git 全局设置:
git config --global user.name "你的用户名"
git config --global user.email "你的邮箱"
创建 git 仓库:
mkdir gittest1
cd gittest1
git init
touch README.md
git add README.md
git commit -m "first commit"
git remote add origin2 git@gitee.com:你的用户名/gittest1.git
git push -u origin master
已有仓库?
cd existing_git_repo
git remote add origin git@gitee.com:你的用户名/gittest1.git
git push -u origin master
本文主要分享了Java打包,Maven的安装与使用,Git的安装与使用,可能内容不够全面,但尽可能在较段的时间内,给读者提供上诉三种技术的基本面貌。
如果有什么问题,欢迎及时留言,看到会及时回复。