java基础之编译运行使用文本编辑器开发的JAVA程序
事件起因
昨天跑通了一个JAVA程序,是基于SpringBoot项目的,本地运行完全没问题,但到了服务器上启动的时候总是报错,提示IdWorker类中的getDataCenterId方法有空指针。IdWorker类一般是基于雪花算法生成ID。仔细查看代码,实际上是获取网卡信息失败了,导致的空指针。 在网上找资料的时候,也有macOS遇到过类似的问题
单元测试
为了对代码进行测试,定位具体代码问题所在,于是想到简单的方法,用文本文档写个程序,进行简单的单元测试,验证代码问题出在哪里。
于是用记事本编写了一个JAVA代码
package com.test;
public class Test {
public static void main(String[] args) throws Exception{
System.out.println("hello");
}
}
保存为Test.java
通过javac 编译
javac Test.java
执行成功,生成了Test.class
再执行java 指令
java Test
此时,发生了错误
找不到或无法加载主类
于是乎上网看文章,发现需要配置JAVA相关的环境变量,于是按下面的参数配置环境变量操作
JAVA_HOME JAVA程序所在主路径,我这里是 E:\jdk,根据你的情况来调整。 CLASSPATH .;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar; PATH 包含 %JAVA_HOME%\bin
再重新打开命令行,执行JAVA程序
为了确保环境变量生效,可以通过echo 查看环境变量的值,如果跟设置的一样,那就是已生效了。比如
echo %CLASSPATH%
当我配置完成后,环境变量都已生效了。
再次执行java Test 还是报错
尝试执行java com.test.Test 也报错
找不到或无法加载主类
此时才发现,由于给定了com.test包,而程序并没有在该路径下,因此需要去掉包名。
修改代码如下:
public class Test {
public static void main(String[] args) throws Exception{
System.out.println("hello");
}
}
此时重新编译
javac Test.java
再执行java命令
java Test
执行成功!
再把获取mac地址的代码放进来
import java.net.InetAddress;
import java.net.NetworkInterface;
public class Test {
public static void main(String[] args) throws Exception{
InetAddress ip = InetAddress.getLocalHost();
System.out.println("ip:"+ip);
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
if (network == null) {
System.out.println("network:null");
} else {
byte[] mac = network.getHardwareAddress();
System.out.println("mac:"+mac.toString());
}
}
}
再执行编译
javac Test.java
再执行java命令
java Test
本地得到结果
E:\mavenres\com\java2nb\novel-admin\4.3.0>java Test
ip:8RCF9JB/192.168.0.141
mac:[B@d934260d
服务器得到结果
[root@hcss-ecs-5eee novel]# java Test
ip:hcss-ecs-5eee/127.0.0.1
Exception in thread "main" java.lang.NullPointerException
at Test.main(Test.java:15)
最终确认问题出在 getHardwareAddress 这个方法,在服务器上这个方法返回的是空,出现空指针异常。
解决办法
为了解决这个问题,新建了一个IdWorkerUtil类,来替换原来的IdWorker类,所有使用该类的地方,都使用IdWorkerUtil类来代替
在IdWorkerUtil类中,重写getDataCenterId方法,我这里为了省事,让系统先跑起来,只有在能够获取到mac地址的时候才调用getHardwareAddress这个方法,当获取不到时,就不去计算了。
实际上可以使用遍历多个地址的方式,取一个有效值,再去进行运算。后续有时间再去考虑一下怎么优化。
IdWorkerUtil类代码:
package com.java2nb.novel.core.utils;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
public enum IdWorkerUtil {
INSTANCE;
private static final long epoch = 1288834974657L;
private static final long workerIdBits = 5L;
private static final long datacenterIdBits = 5L;
private static final long maxWorkerId = 31L;
private static final long maxDatacenterId = 31L;
private static final long sequenceBits = 12L;
private static final long workerIdShift = 12L;
private static final long datacenterIdShift = 17L;
private static final long timestampLeftShift = 22L;
private static final long sequenceMask = 4095L;
private static long lastTimestamp = -1L;
private long sequence = 0L;
private final long workerId;
private final long datacenterId = this.getDatacenterId();
private IdWorkerUtil() {
this.workerId = this.getMaxWorkerId(this.datacenterId);
}
public synchronized long nextId() {
long timestamp = this.timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
} else {
if (lastTimestamp == timestamp) {
this.sequence = this.sequence + 1L & 4095L;
if (this.sequence == 0L) {
timestamp = this.tilNextMillis(lastTimestamp);
}
} else {
this.sequence = 0L;
}
lastTimestamp = timestamp;
return timestamp - 1288834974657L << 22 | this.datacenterId << 17 | this.workerId << 12 | this.sequence;
}
}
private long tilNextMillis(long lastTimestamp) {
long timestamp;
for(timestamp = this.timeGen(); timestamp <= lastTimestamp; timestamp = this.timeGen()) {
;
}
return timestamp;
}
private long timeGen() {
return System.currentTimeMillis();
}
private long getMaxWorkerId(long datacenterId) {
StringBuilder mPid = new StringBuilder();
mPid.append(datacenterId);
String name = ManagementFactory.getRuntimeMXBean().getName();
if (!name.isEmpty()) {
mPid.append(name.split("@")[0]);
}
return (long)(mPid.toString().hashCode() & '\uffff') % 32L;
}
private long getDatacenterId() {
long id = 0L;
try {
InetAddress ip = InetAddress.getLocalHost();
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
if (!network.isUp()||network.isLoopback()) {
id = 1L;
} else {
byte[] mac = network.getHardwareAddress();
id = (255L & (long)mac[mac.length - 1] | 65280L & (long)mac[mac.length - 2] << 8) >> 6;
id %= 32L;
}
} catch (Exception ex) {
ex.printStackTrace();
}
return id;
}
}