一、 项目介绍
1. 背景
根据IP得到位置,加标签
进行大数据分析,比如淘宝推荐等提供优质数据
www.ip.cn 等 查询IP
2. 需求
IP 分析 归属地信息 , 查找在毫秒内完成
IP地址库,公网都是开放的
IANA : 国际组织,负责公网IP维护分发
3. 技术栈
Eclipse ,JavaSE中面向对象、IO流、二分法算法、Base64编码、工具类封装
4. 目标
通过开发IP地址归属地查询平台,我们需要对JavaSE综合技术有所提升,增强实战能力。学习完该项目我们应该具备如下能力:
1 面向对象程序设计
2 工具类封装与使用写法
3 文件IO流
4 字符串处理
5 二分法查找
6 IP地址的不同形式的使用
二、 主要思路
1 程序中读取内容
2 解析IP字符串,进行结构化处理
3 封装工具类
4 接口API
入参 : IP
出参 : 归属地
三、 主要思路
应用开发类项目
C/S结构,需要有特定的客户端,比如QQ,微信,eclipse
Web开发类项目
B/S结构为主.通过网页形式访问的在线系统,比如各类官网,各类管理系统等
中小型项目研发标准流程
1 需求概述-需求描述:说清楚你为什么做这个项目
根据IP获取归属地
2 需求分析 :
根据需求概述,用技术角度考虑一下,是否可行
三方面 : 1 输入 , 2 输出 , 3 必备物料(地址库)
输入 : 给定一个任意的合法IP地址
输出 : 返回IP地址对应的地址库
3 开发步骤
1 读取IP地址库
2 解析地址库的信息,进行结构化处理
3 将对象保存到list中
4 进行二分法查找,提高效率
5 对外提供访问的接口
6 测试
4 细节开发与风险控制
5 BUG修复,调优,标准化
6 正式上线
7 项目总结-项目复盘
四、 代码开发
1. 无脑读取文件
2. 文本文件读取工具类
抽象工具类
1 通过编码,实现输入与输出
2 抽象输入与输出,形成方法入参和出参
3 工具类代码实现并测试
2.1 工具类编码
/**
* 读取文件并返回list集合
*
* @param filePath
* 文件路径
* @param encoding
* 字符编码
* @return
* @throws IOException
*/
public static List<String> getLineList(String filePath, String encoding)
throws IOException {
// 2 节点流对接文件
FileInputStream fis = new FileInputStream(filePath);
// 3 转换为字符流并指定字符编码
Reader reader = new InputStreamReader(fis, encoding);
// 4 缓冲流提高效率
BufferedReader br = new BufferedReader(reader);
// 5 读取
String line = null;
// 保存读取的数据
List<String> lineList = new ArrayList<String>();
while ((line = br.readLine()) != null) {
// 添加到集合中
lineList.add(line);
}
// 6 关闭
br.close();
return lineList;
}
2.2 工具类测试
/**
* 测试读取工具类
*
* @author 天亮教育-帅气多汁你泽哥
* @Date 2022年2月11日 上午10:50:25
*/
public class TestFileIO_02 {
public static void main(String[] args) {
// 1 文件路径
String ipLibrayPath = "ip_location_relation.txt";
String encoding = "UTF-8";
try {
List<String> lineList = FileOperatorUtil.getLineList(ipLibrayPath,
encoding);
for (String string : lineList) {
System.out.println(string);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3. 结构化
结构化 : 当我们知道第一个数据的格式的时候,那么第二个的数据格式就已经确定了,有规律可循,方便操作
根据非结构化数据,找到对应的规则,并创建对应的实体类进行封装,转换为结构化数据
4. 抽象实体类并保存数据
4.1 实体类
/**
* 结构化的IP地址实体类
*
* @author 天亮教育-帅气多汁你泽哥
* @Date 2022年2月11日 上午11:23:44
*/
public class IPAndLocationPojo implements Comparable<IPAndLocationPojo> {
// 衍生字段,用于保存IP对应的long值
private long startIPLong;
private long endIPLong;
/**
* 起始IP
*/
private String startIP;
/**
* 结束IP
*/
private String endIP;
/**
* 归属地
*/
private String location;
@Override
public int compareTo(IPAndLocationPojo o) {
long status = this.startIPLong - o.startIPLong;
// 不能强制转换 , 如果两个值相差 2147483647的话,转换为int之后 得到负数
// return (int) (this.startIPLong - o.startIPLong);
return status > 0 ? 1 : 0;
}
public String getStartIP() {
return startIP;
}
4.2 拆分数据为三列
public static void main(String[] args) {
// 1 文件路径
String ipLibrayPath = "ip_location_relation.txt";
String encoding = "UTF-8";
try {
List<String> lineList = FileOperatorUtil.getLineList(ipLibrayPath,
encoding);
for (String string : lineList) {
// System.out.println(string);
String[] columnArray = string.split(" ");
for (String col : columnArray) {
System.out.println(col);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
5. 封装业务类DataProcessManager
5.1 封装数据集合
// 1 文件路径
String ipLibrayPath = "ip_location_relation.txt";
String encoding = "UTF-8";
// 保存数据对象
List<IPAndLocationPojo> ipAndLocationPojos = new ArrayList<IPAndLocationPojo>();
try {
List<String> lineList = FileOperatorUtil.getLineList(ipLibrayPath,
encoding);
for (String string : lineList) {
// 判断是否是空行
if (string==null || string.trim().equals("")) {
continue;
}
// 分割为数组
String[] columnArray = string.split(" ");
// 获取起始IP
String startIP = columnArray[0];
// 获取结束IP
String endIP = columnArray[1];
// 获取归属地
String location = columnArray[2];
// 封装到对象中
IPAndLocationPojo ipAndLocationPojo = new IPAndLocationPojo(startIP, endIP, location);
// 添加到集合中
ipAndLocationPojos.add(ipAndLocationPojo);
}
// 遍历测试
for (IPAndLocationPojo ip : ipAndLocationPojos) {
System.out.println(ip);
}
} catch (IOException e) {
e.printStackTrace();
}
5.2 封装为管理类
public class DataProcessManager {
/**
* 结构化数据集合
*
* @param filePath
* @param encoding
* @return
* @throws IOException
*/
public static List<IPAndLocationPojo> getPojoList(String filePath,
String encoding) throws IOException {
// 保存数据对象
List<IPAndLocationPojo> ipAndLocationPojos = new ArrayList<IPAndLocationPojo>();
List<String> lineList = FileOperatorUtil
.getLineList(filePath, encoding);
for (String string : lineList) {
// 判断是否是空行
if (string == null || string.trim().equals("")) {
continue;
}
// 分割为数组
String[] columnArray = string.split(" ");
// 获取起始IP
String startIP = columnArray[0];
// 获取结束IP
String endIP = columnArray[1];
// 获取归属地
String location = columnArray[2];
// 封装到对象中
IPAndLocationPojo ipAndLocationPojo = new IPAndLocationPojo(
startIP, endIP, location);
// 添加到集合中
ipAndLocationPojos.add(ipAndLocationPojo);
}
return ipAndLocationPojos;
}
}
5.3 测试封装的方法
public class TestNonStructToStruct_03 {
public static void main(String[] args) {
// 1 文件路径
String ipLibrayPath = "ip_location_relation.txt";
String encoding = "UTF-8";
// 保存数据对象
try {
List<IPAndLocationPojo> ipAndLocationPojos = DataProcessManager
.getPojoList(ipLibrayPath, encoding);
for (IPAndLocationPojo ipAndLocationPojo : ipAndLocationPojos) {
System.out.println(ipAndLocationPojo);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
6. 结构化集合转换为数组,toArray
public class TestListToArray_01 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
String[] strs = new String[list.size()];
list.toArray(strs);
for (String string : strs) {
System.out.println(string);
}
// 结构化数据集合转数组
// 1 文件路径
String ipLibrayPath = "ip_location_relation.txt";
String encoding = "UTF-8";
// 保存数据对象
try {
List<IPAndLocationPojo> ipAndLocationPojos = DataProcessManager
.getPojoList(ipLibrayPath, encoding);
IPAndLocationPojo[] ipAndLocationPojoArray = new IPAndLocationPojo[ipAndLocationPojos
.size()];
ipAndLocationPojos.toArray(ipAndLocationPojoArray);
for (IPAndLocationPojo ipAndLocationPojo : ipAndLocationPojoArray) {
System.out.println(ipAndLocationPojo);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
7. 对象数组进行排序
7.1 实现排序
1 自定义实现
冒泡排序
选择排序
2 工具类
Collections
Arrays
一般不需要自己写,使用工具即可
7.2 排序注意事项
被排序的对象必须具备可比性
1 实现Comparable接口
2 实现 Comparator接口
public class TestArraySort_01 {
public static void main(String[] args) {
// 基本类型
int[] intArray = { 10, 6, 8, 9, 3, 15 };
Arrays.sort(intArray);
for (int i : intArray) {
System.out.println(i);
}
// 引用类型 按照年龄升序
User[] users = { new User(18, "张三1"), new User(19, "张三2"),
new User(15, "张三3"), new User(17, "张三4") };
Arrays.sort(users);
for (User user : users) {
System.out.println(user);
}
}
}
class User implements Comparable<User> {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User(int age, String name) {
super();
this.age = age;
this.name = name;
}
public User() {
super();
}
@Override
public String toString() {
return "User [age=" + age + ", name=" + name + "]";
}
@Override
public int compareTo(User o) {
// 返回大于0 往后放
// 返回小于0 往前放
// 返回0 相等
return this.age - o.age;
}
}
7.3 业务问题
上面已经通过测试,解决了技术问题,下面就是业务问题
我们比较肯定是IP,我们的IP地址没有办法使用字符串进行比较,因为字符串比较的ASCII码进行比较的
比如 1.1.6.2 和 1.1.125.1
看上去 肯定是 125大于 6 但是按ASCII码进行比较的话 是 6 大于 125
所以 我们需要把IP进行转换
public class IPUtil {
public static void main(String[] args) {
String ip = "126.56.78.59";
long ipLong = ipToLong(ip);
System.out.println(ipLong);
System.out.println(longToIP(ipLong));
}
/**
* 将127.0.0.1形式的IP地址转换成十进制整数,这里没有进行任何错误处理
* 通过左移位操作(<<)给每一段的数字加权,第一段的权为2的24次方,第二段的权为2的16次方,第三段的权为2的8次方,最后一段的权为1
*/
public static long ipToLong(String ipaddress) {
long[] ip = new long[4];
// 先找到IP地址字符串中.的位置
int position1 = ipaddress.indexOf(".");
int position2 = ipaddress.indexOf(".", position1 + 1);
int position3 = ipaddress.indexOf(".", position2 + 1);
// 将每个.之间的字符串转换成整型
ip[0] = Long.parseLong(ipaddress.substring(0, position1));
ip[1] = Long.parseLong(ipaddress.substring(position1 + 1, position2));
ip[2] = Long.parseLong(ipaddress.substring(position2 + 1, position3));
ip[3] = Long.parseLong(ipaddress.substring(position3 + 1));
return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3];
}
/**
* 将十进制整数形式转换成127.0.0.1形式的ip地址 将整数值进行右移位操作(>>>),右移24位,右移时高位补0,得到的数字即为第一段IP。
* 通过与操作符(&)将整数值的高8位设为0,再右移16位,得到的数字即为第二段IP。
* 通过与操作符吧整数值的高16位设为0,再右移8位,得到的数字即为第三段IP。 通过与操作符吧整数值的高24位设为0,得到的数字即为第四段IP。
*/
public static String longToIP(long ipaddress) {
StringBuffer sb = new StringBuffer("");
// 直接右移24位
sb.append(String.valueOf((ipaddress >>> 24)));
sb.append(".");
// 将高8位置0,然后右移16位
sb.append(String.valueOf((ipaddress & 0x00FFFFFF) >>> 16));
sb.append(".");
// 将高16位置0,然后右移8位
sb.append(String.valueOf((ipaddress & 0x0000FFFF) >>> 8));
sb.append(".");
// 将高24位置0
sb.append(String.valueOf((ipaddress & 0x000000FF)));
return sb.toString();
}
}
7.4 实体类中衍生两个字段
上面可以把IP地址转换为long类型,方便进行排序比较
那么我们有起始IP和结束IP , 另外转换之后的长整型的值,也是需要保存起来的,和对应的IP字段需要一一对应
所以需要在对应的IP实体类中,再添加两个成员变量,分别保存转换之后的long值
通过构造方法对long类型赋值
完整的实体类
public class IPAndLocationPojo implements Comparable<IPAndLocationPojo> {
// 衍生字段,用于保存IP对应的long值
private long startIPLong;
private long endIPLong;
/**
* 起始IP
*/
private String startIP;
/**
* 结束IP
*/
private String endIP;
/**
* 归属地
*/
private String location;
@Override
public int compareTo(IPAndLocationPojo o) {
long status = this.startIPLong - o.startIPLong;
// 不能强制转换 , 如果两个值相差 2147483647的话,转换为int之后 得到负数
// return (int) (this.startIPLong - o.startIPLong);
return status > 0 ? 1 : 0;
}
public String getStartIP() {
return startIP;
}
public long getStartIPLong() {
return startIPLong;
}
public void setStartIPLong(long startIPLong) {
this.startIPLong = startIPLong;
}
public long getEndIPLong() {
return endIPLong;
}
public void setEndIPLong(long endIPLong) {
this.endIPLong = endIPLong;
}
public void setStartIP(String startIP) {
this.startIP = startIP;
}
public String getEndIP() {
return endIP;
}
public void setEndIP(String endIP) {
this.endIP = endIP;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public IPAndLocationPojo(String startIP, String endIP, String location) {
super();
this.startIP = startIP;
this.endIP = endIP;
this.location = location;
// 对长整型赋值
this.startIPLong = IPUtil.ipToLong(startIP);
this.endIPLong = IPUtil.ipToLong(endIP);
}
public IPAndLocationPojo() {
super();
}
@Override
public String toString() {
return "IPAndLocationPojo [startIP=" + startIP + ", endIP=" + endIP
+ ", location=" + location + "]";
}
}
7.5 测试pojo排序
上面通过两个衍生字段已经让实体类拥有了排序功能,但是还没有进行排序
public class TestArraySort_02 {
public static void main(String[] args) {
// 1 文件路径
String ipLibrayPath = "ip_location_relation.txt";
String encoding = "UTF-8";
// 保存数据对象
try {
List<IPAndLocationPojo> ipAndLocationPojos = DataProcessManager
.getPojoList(ipLibrayPath, encoding);
IPAndLocationPojo[] ipAndLocationPojoArray = new IPAndLocationPojo[ipAndLocationPojos
.size()];
ipAndLocationPojos.toArray(ipAndLocationPojoArray);
// 排序
Arrays.sort(ipAndLocationPojoArray);
for (IPAndLocationPojo ipAndLocationPojo : ipAndLocationPojoArray) {
System.out.println(ipAndLocationPojo);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
7.6 封装排序方法
public static IPAndLocationPojo[] convertListToArraySort(
List<IPAndLocationPojo> ipAndLocationPojos) {
// 创建数组
IPAndLocationPojo[] ipAndLocationPojoArray = new IPAndLocationPojo[ipAndLocationPojos
.size()];
// 转换为数组
ipAndLocationPojos.toArray(ipAndLocationPojoArray);
// 排序
Arrays.sort(ipAndLocationPojoArray);
return ipAndLocationPojoArray;
}
7.7 测试
public class TestArraySort_03 {
public static void main(String[] args) {
// 1 文件路径
String ipLibrayPath = "ip_location_relation.txt";
String encoding = "UTF-8";
// 保存数据对象
try {
// 获取结构化数据
List<IPAndLocationPojo> ipAndLocationPojos = DataProcessManager
.getPojoList(ipLibrayPath, encoding);
// 转换数组并排序
IPAndLocationPojo[] ipAndLocationPojoArray = DataProcessManager
.convertListToArraySort(ipAndLocationPojos);
// 遍历输出
for (IPAndLocationPojo ipAndLocationPojo : ipAndLocationPojoArray) {
System.out.println(ipAndLocationPojo);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
8. 二分法查询
上面已经把结构化数据进行排序,下一步就是二分法查询
1 确定起始和结束位置及中间位置
2 如果目标数据小于中间数据,起始位置不变,结束位置为 中间位置-1 , 重新生成中间位置
3 如果目标数据大于中间数据,结束位置不变,起始位置为 中间位置+1 , 重新生成中间位置
4 如果目标数据等于中间数据, 终止,中间位置则是对应的下标
5 如果起始位置 大于 结束位置 说明不存在
8.1 基本类型
public class TestBinaraySearch_01 {
public static void main(String[] args) {
test1();
}
public static void test1() {
// 1 确定起始和结束位置及中间位置
// 2 如果目标数据小于中间数据,起始位置不变,结束位置为 中间位置-1 , 重新生成中间位置
// 3 如果目标数据大于中间数据,结束位置不变,起始位置为 中间位置+1 , 重新生成中间位置
// 4 如果目标数据等于中间数据, 终止,中间位置则是对应的下标
// 5 如果起始位置 大于 结束位置 说明不存在
int[] arr = { 1, 2, 3, 4, 7, 8, 9, 11, 15, 16, 18, 21, 26, 33, 55 };
int target = 19;
int startIndex = 0;
int endIndex = arr.length - 1;
int m = (startIndex + endIndex) / 2;
while (startIndex <= endIndex) {
if (target == arr[m]) {
System.out.println(target + " 在 " + m + " 位上");
return;
}
if (target < arr[m]) {
endIndex = m - 1;
} else {
startIndex = m + 1;
}
m = (startIndex + endIndex) / 2;
}
System.out.println(target+" 不存在");
}
}
8.2 复杂类型
9. IP地址对象二分法
9.1 编码实现
public static void test3() throws IOException {
// 1 文件路径
String ipLibrayPath = "ip_location_relation.txt";
String encoding = "UTF-8";
// 保存数据对象
List<IPAndLocationPojo> ipAndLocationPojos = DataProcessManager
.getPojoList(ipLibrayPath, encoding);
// 转数组并排序
IPAndLocationPojo[] ipAndLocationPojoArray = DataProcessManager
.convertListToArraySort(ipAndLocationPojos);
// 目标数据是IP
String targetIP = "1226.44.58.127";
// 把IP转换为long
long targetIPLong = IPUtil.ipToLong(targetIP);
int startIndex = 0;
int endIndex = ipAndLocationPojoArray.length - 1;
int m = (startIndex + endIndex) / 2;
/**
* 如果 小于 起始IP 找前面
*
* 如果 大于 起始IP 找后面
*
* 如果 大于等于起始IP且 小于等于 结束IP 则说明找到了
*/
while (startIndex <= endIndex) {
if (targetIPLong >= ipAndLocationPojoArray[m].getStartIPLong() && targetIPLong <= ipAndLocationPojoArray[m].getEndIPLong()) {
System.out.println(targetIP + " 在 " + ipAndLocationPojoArray[m].getLocation());
return;
}
if (targetIPLong < ipAndLocationPojoArray[m].getStartIPLong()) {
endIndex = m - 1;
} else {
startIndex = m + 1;
}
m = (startIndex + endIndex) / 2;
}
System.out.println(targetIP + " 不存在");
}
9.2 封装
public class DataProcessManager {
/**
* 二分法查找,入参是IP和数组,出参是对应的索引,找不到返回-1
*
* @param ipAndLocationPojoArray
* @param targetIP
* @return
* @throws IOException
*/
public static int binaraySeach(IPAndLocationPojo[] ipAndLocationPojoArray,
String targetIP) throws IOException {
// 把IP转换为long
long targetIPLong = IPUtil.ipToLong(targetIP);
int startIndex = 0;
int endIndex = ipAndLocationPojoArray.length - 1;
int m = (startIndex + endIndex) / 2;
/**
* 如果 小于 起始IP 找前面
*
* 如果 大于 起始IP 找后面
*
* 如果 大于等于起始IP且 小于等于 结束IP 则说明找到了
*/
while (startIndex <= endIndex) {
if (targetIPLong >= ipAndLocationPojoArray[m].getStartIPLong()
&& targetIPLong <= ipAndLocationPojoArray[m].getEndIPLong()) {
return m;
}
if (targetIPLong < ipAndLocationPojoArray[m].getStartIPLong()) {
endIndex = m - 1;
} else {
startIndex = m + 1;
}
m = (startIndex + endIndex) / 2;
}
return -1;
}
9.3 测试
public class TestBinaraySearch_02 {
public static void main(String[] args) throws IOException {
// 1 文件路径
String ipLibrayPath = "ip_location_relation.txt";
String encoding = "UTF-8";
// 保存数据对象
List<IPAndLocationPojo> ipAndLocationPojos = DataProcessManager
.getPojoList(ipLibrayPath, encoding);
// 转数组并排序
IPAndLocationPojo[] ipAndLocationPojoArray = DataProcessManager
.convertListToArraySort(ipAndLocationPojos);
// 目标数据是IP
String targetIP = "216.44.58.127";
// 二分法查找
int index = DataProcessManager.binaraySeach(ipAndLocationPojoArray, targetIP);
System.out.println(ipAndLocationPojoArray[index]);
}
}
10. 工具类封装
10.1 编码
上面已经把二分法完成,已经可以实现功能了,测试代码就相当于客户端
但是客户端现在需要知道的数据还有点多,比如IP地址库文件名,比如字符编码等
这些都是客户无感的,客户只关心 IP和归属地,我把IP给你,你把归属地给我,就行了
所以此时 我们需要对外提供一个方法,只有入参和出参
public class DataProcessManager {
/**
* 对外提供的接口,入参是IP,出参是归属地
*
* @param ip
* @return
*/
public static String getLocation(String ip) {
// 1 文件路径
String ipLibrayPath = "ip_location_relation.txt";
String encoding = "UTF-8";
// 保存数据对象
List<IPAndLocationPojo> ipAndLocationPojos = null;
IPAndLocationPojo[] ipAndLocationPojoArray = null;
try {
// 获取数据
ipAndLocationPojos = DataProcessManager.getPojoList(ipLibrayPath,
encoding);
// 转数组并排序
ipAndLocationPojoArray = DataProcessManager
.convertListToArraySort(ipAndLocationPojos);
} catch (IOException e) {
e.printStackTrace();
}
// 二分法查找
int index = DataProcessManager.binaraySeach(ipAndLocationPojoArray, ip);
// 判断是否找到
if (index == -1) {
return null;
} else {
return ipAndLocationPojoArray[index].getLocation();
}
}
10.2 测试
public class TestBinaraySearch_03 {
public static void main(String[] args) throws IOException {
String ip = "210.2.1.215";
long startTime = System.currentTimeMillis();
String location = DataProcessManager.getLocation(ip);
long endTime = System.currentTimeMillis();
System.out.println("耗时 : " + (endTime - startTime) + " " + location);
startTime = System.currentTimeMillis();
location = DataProcessManager.getLocation(ip);
endTime = System.currentTimeMillis();
System.out.println("耗时 : " + (endTime - startTime) + " " + location);
startTime = System.currentTimeMillis();
location = DataProcessManager.getLocation(ip);
endTime = System.currentTimeMillis();
System.out.println("耗时 : " + (endTime - startTime) + " " + location);
}
}
10.3 优化
现在getLocation方法,每次调用都会去读取IP地址库,并且转换为结构化数据,然后进行排序
这样效率很低,我们只需要让以上初始化工作在程序生命周期中只执行一次就可以
静态语句块 : 加载阶段执行,最先执行,且只执行一次
静态变量 : 静态变量的值再整个程序生命周期周有效
11. 入口类
项目代码标准化之后,肯定要准备一个程序入口
public class DataProcessManager {
private static IPAndLocationPojo[] ipAndLocationPojoArray = null;
static {
// 1 文件路径
String ipLibrayPath = "ip_location_relation.txt";
String encoding = "UTF-8";
// 保存数据对象
List<IPAndLocationPojo> ipAndLocationPojos = null;
try {
// 获取数据
ipAndLocationPojos = DataProcessManager.getPojoList(ipLibrayPath,
encoding);
// 转数组并排序
ipAndLocationPojoArray = DataProcessManager
.convertListToArraySort(ipAndLocationPojos);
} catch (IOException e) {
e.printStackTrace();
}
}
public class DataProcessManager {
private static IPAndLocationPojo[] ipAndLocationPojoArray = null;
static {
// 1 文件路径
String ipLibrayPath = "ip_location_relation.txt";
String encoding = "UTF-8";
// 保存数据对象
List<IPAndLocationPojo> ipAndLocationPojos = null;
try {
// 获取数据
ipAndLocationPojos = DataProcessManager.getPojoList(ipLibrayPath,
encoding);
// 转数组并排序
ipAndLocationPojoArray = DataProcessManager
.convertListToArraySort(ipAndLocationPojos);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 对外提供的接口,入参是IP,出参是归属地
*
* @param ip
* @return
*/
public static String getLocation(String ip) {
// 二分法查找
int index = DataProcessManager.binaraySeach(ipAndLocationPojoArray, ip);
// 判断是否找到
if (index == -1) {
return null;
} else {
return ipAndLocationPojoArray[index].getLocation();
}
}
12. 打包
1 普通jar包 : 不能独立运行,一般用于让其他程序引入调用的
2 可执行jar包 : 一般不会被引入
运行jar包
把jar包和地址库放在同一目录下
然后CMD 进入该目录
执行命令 java -jar xxx.jar