一、 项目介绍

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. 无脑读取文件

java 实现搜索 java实现搜索设备ip功能_IP

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值

java 实现搜索 java实现搜索设备ip功能_eclipse_02

通过构造方法对long类型赋值

java 实现搜索 java实现搜索设备ip功能_tcp/ip_03

完整的实体类

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 复杂类型

java 实现搜索 java实现搜索设备ip功能_tcp/ip_04

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 优化

java 实现搜索 java实现搜索设备ip功能_java 实现搜索_05

现在getLocation方法,每次调用都会去读取IP地址库,并且转换为结构化数据,然后进行排序
这样效率很低,我们只需要让以上初始化工作在程序生命周期中只执行一次就可以

静态语句块 : 加载阶段执行,最先执行,且只执行一次
静态变量 : 静态变量的值再整个程序生命周期周有效

java 实现搜索 java实现搜索设备ip功能_java_06

11. 入口类

项目代码标准化之后,肯定要准备一个程序入口

java 实现搜索 java实现搜索设备ip功能_tcp/ip_07

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包 : 一般不会被引入

java 实现搜索 java实现搜索设备ip功能_tcp/ip_08


java 实现搜索 java实现搜索设备ip功能_eclipse_09


java 实现搜索 java实现搜索设备ip功能_IP_10


java 实现搜索 java实现搜索设备ip功能_java 实现搜索_11


java 实现搜索 java实现搜索设备ip功能_java_12


运行jar包

java 实现搜索 java实现搜索设备ip功能_eclipse_13


把jar包和地址库放在同一目录下

然后CMD 进入该目录

执行命令 java -jar xxx.jar