javaday26IP归属地在线查询平台

一、项目介绍

  1. 背景
    根据IP得到位置,加标签
    进行大数据分析,比如淘宝推荐等提供优质数据
    www.ip.cn 等 查询IP

  2. 需求
    IP 分析 归属地信息 , 查找在毫秒内完成
    IP地址库,公网都是开放的

IANA : 国际组织,负责公网IP维护分发

  1. 技术栈
    Eclipse ,JavaSE中面向对象、IO流、二分法算法、Base64编码、工具类封装
  2. 目标
    通过开发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. 无脑读取文件
package test;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.Reader;

/**
 * IO读取文件
 * 

 */
public class TestFileIO_01 {
	public static void main(String[] args) {
		try {
			// 1 文件路径
			String ipLibrayPath = "ip_location_relation.txt";
			String encoding = "UTF-8";
			// 2 节点流对接文件
			FileInputStream fis = new FileInputStream(ipLibrayPath);
			// 3 转换为字符流并指定字符编码
			Reader reader = new InputStreamReader(fis, encoding);
			// 4 缓冲流提高效率
			BufferedReader br = new BufferedReader(reader);
			// 5 读取
			String line = null;
			while ((line = br.readLine()) != null) {
				System.out.println(line);
			}
			// 6 关闭
			br.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
}

  1. 文本文件读取工具类
    抽象工具类
    1 通过编码,实现输入与输出
    2 抽象输入与输出,形成方法入参和出参
    3 工具类代码实现并测试
    2.1 工具类编码
package com.tledu.zrz.util;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;

/**
 * 文本文件读取
 * 
 */
public class FileOperatorUtil {
	/**
	 * 读取文件并返回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 工具类测试

package test;

import java.io.IOException;
import java.util.List;

import com.tledu.zrz.util.FileOperatorUtil;


/**
 * 测试读取工具类
 * 

 */
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();
		}
	}
}

  1. 结构化
    结构化 : 当我们知道第一个数据的格式的时候,那么第二个的数据格式就已经确定了,有规律可循,方便操作
    根据非结构化数据,找到对应的规则,并创建对应的实体类进行封装,转换为结构化数据
  2. 抽象实体类并保存数据
    4.1 实体类
package com.tledu.zrz.pojo;

import com.tledu.zrz.util.IPUtil;

/**
 * 结构化的IP地址实体类
 */
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 + "]";
	}
}

4.2 拆分数据为三列

package test;

import java.io.IOException;
import java.util.List;

import com.tledu.zrz.util.FileOperatorUtil;

/**
 * 把每行数据拆分为三列,并保存到实体类对象中
 * 

 */
public class TestNonStructToStruct_01 {
	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();
		}

	}
}


  1. 封装业务类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 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 测试封装的方法

package test;

import java.io.IOException;
import java.util.List;

import com.tledu.zrz.manager.DataProcessManager;
import com.tledu.zrz.pojo.IPAndLocationPojo;

/**
 * 测试
 * 

 */
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();
		}

	}
}

  1. 结构化集合转换为数组,toArray

public class TestListToArray_01 {
public static void main(String[] args) {
List list = new ArrayList();
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();
	}
}

}

  1. 对象数组进行排序
    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 {
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 {
// 衍生字段,用于保存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排序
上面通过两个衍生字段已经让实体类拥有了排序功能,但是还没有进行排序
在这里插入图片描述

7.6 封装排序方法
在这里插入图片描述

7.7 测试
在这里插入图片描述

  1. 二分法查询
    上面已经把结构化数据进行排序,下一步就是二分法查询
    // 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 复杂类型
在这里插入图片描述

  1. IP地址对象二分法
    9.1 编码实现

    public static void test3() throws IOException {
    // 1 文件路径
    String ipLibrayPath = “ip_location_relation.txt”;
    String encoding = “UTF-8”;
    // 保存数据对象
    List 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 测试
在这里插入图片描述

  1. 工具类封装
    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 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 测试
在这里插入图片描述

10.3 优化
在这里插入图片描述

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

静态语句块 : 加载阶段执行,最先执行,且只执行一次
静态变量 : 静态变量的值再整个程序生命周期周有效
11. 入口类
项目代码标准化之后,肯定要准备一个程序入口

在这里插入图片描述在这里插入图片描述在这里插入图片描述
在这里插入图片描述

  1. 打包
    1 普通jar包 : 不能独立运行,一般用于让其他程序引入调用的
    2 可执行jar包 : 一般不会被引入

运行jar包

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
把jar包和地址库放在同一目录下
然后CMD 进入该目录
执行命令 java -jar xxx.jar

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java 中,我们可以使用第三方库来获取 IP归属地信息。其中一个常用的库是 GeoIP2,它基于 MaxMind 的 GeoIP2 数据库。 首先,需要将 GeoIP2 库添加到项目中。可以在 Maven 或 Gradle 构建脚本中添加相应的依赖项。 然后,我们可以使用 GeoIP2 提供的 API 来查询 IP归属地。以下是一个示例代码: ```java import com.maxmind.geoip2.DatabaseReader; import com.maxmind.geoip2.model.CityResponse; import com.maxmind.geoip2.record.Country; import java.io.File; import java.io.IOException; import java.net.InetAddress; public class IPUtils { public static String getIPCountry(String ip) { try { File database = new File("GeoIP2-City.mmdb"); DatabaseReader reader = new DatabaseReader.Builder(database).build(); InetAddress ipAddress = InetAddress.getByName(ip); CityResponse response = reader.city(ipAddress); Country country = response.getCountry(); return country.getName(); } catch (IOException e) { e.printStackTrace(); return null; } } public static void main(String[] args) { String ip = "123.456.789.0"; String country = getIPCountry(ip); System.out.println("IP " + ip + " 的归属地是:" + country); } } ``` 在上述示例中,我们通过 `getIPCountry` 方法传入一个 IP 地址,并在 `main` 方法中调用该方法来获取该 IP归属地信息。具体的归属地信息包括国家、地区、城市等,可以根据需要进行扩展和处理。 需要注意的是,在运行代码之前,我们需要下载并导入 GeoIP2 数据库文件 `GeoIP2-City.mmdb`,该文件包含了 IP 地址和归属地信息的映射关系。可以从 MaxMind 的官网或其他数据源获取该文件。 这样,我们就可以通过 Java 来获取 IP归属地信息了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值