【详细分解】如何把IP地址保存到Int类型变量中

如何把IP地址保存到Int型变量中,之前看过类似的文章,但是属于走马观花,没有对细节进行理解。这两天抽空重新复习了下二进制和Java运算符,事后把这个问题当作对自己的考核来对待吧。

想搞明白为什么要存到Int类型的变量中,首先要理解什么是Int和熟悉运算符的使用。

什么是Int

Int是java的原始数据类型,JDK定义Int有4个字节共32位。因为int类型存在正数和负数,32位中有一位是来标识正负数的,所以int类型的变量最大和最小值是:0111....11和1111...11(二进制),转化为十进制也就是-2147483648和2147483648

Java运算符

位与运算符(&):两个数都转为二进制,然后从高位开始比较,如果两个数都为1则为1,否则为0。

位或运算符( |):两个数都转为二进制,然后从高位开始比较,两个数只要有一个为1则为1,否则就为0。

左移运算符(<<):value << num,num 是要向左左移动的位数,丢弃最高位,0补最低位。

右移运算符(>>):value << num,num 是要向右 移动的位数,符号位不变,左边补上符号位(正数0负数1)。

无符号右移运算符(>>>):无符号右移规则和右移运算是一样的,只不过忽略了符号位扩展,0补最高位。

正数的位移没有涉及到符号,而且正数的原码、反码、补码都是一样的,所以相对简单,但是对于负整数的位移,往往容易混淆。例如对整数-3进行<< >> >>>运算做说明:
移位运算符案列
实现原理

ipv4的地址可分解为4段,每段范围0-255;int类型的变量同样也有4个字节,每个字节的上限也是255(11111111)且每个字节有8位,结合这两个特性,可以把IP的地址的每一段分别对应到int的每一个字节当中,因为要存储到一个int变量中,所以要在存储时要依次位移8位,这样的话一个IP便可保存在一个int型变量中。

原理搞明白后,编码的话就容易了许多。

    public static void main(String[] args)
    {
        String ip = "172.185.255.233";

        //step1: 分解IP字符串,并对应写对字节数组
        byte[] ip1 = ipToBytes(ip);

        //step2: 对字节数组里的每个字节进行左移位处理,分别对应到整型变量的4个字节
        int ip2 = bytesToInt(ip1);
        System.out.println("整型ip ----> " + ip2);
        
        //对整型变量进行右位移处理,恢复IP字符串
        String ip3 = intToIp(ip2);
        System.out.println("字符串ip---->"+ip3);

    }

    /**
     * 第一步,把IP地址分解为一个btye数组
     * 
     * @param ipAddr
     * @return int
     */
    public static byte[] ipToBytes(String ipAddr)
    {
        //初始化字节数组,定义长度为4
        byte[] ret = new byte[4];
        try
        {
            //使用关键字"." 分割字符串数组
            String[] ipArr = ipAddr.split("\\.");
            
            //将字符串数组依次写入字节数组
            ret[0] = (byte) (Integer.parseInt(ipArr[0]));
            ret[1] = (byte) (Integer.parseInt(ipArr[1]));
            ret[2] = (byte) (Integer.parseInt(ipArr[2]));
            ret[3] = (byte) (Integer.parseInt(ipArr[3]));
            return ret;
        } catch (Exception e)
        {
            throw new IllegalArgumentException("invalid IP : "+ipAddr);
        }
    }

    /**
     * 根据位运算把 byte[] -> int
     * 
     * 原理:将每个字节强制转化为8位二进制码,然后依次左移8位,对应到Int变量的4个字节中
     * 
     * @param bytes
     * @return int
     */
    public static int bytesToInt(byte[] bytes)
    {
        int addr = 0;               //初始化Int变量addr=0
        addr |= (bytes[0] & 0xFF);  //强制转化为8位二进制码,比如原码是101,强转后00000101
        addr = addr << 8;           //左移8位,得到00000101 00000000,给下个字节的拼接创造环境(预留8位0,方便用|进行拼接)
        addr |=(bytes[1] & 0xFF);   //强制转化为8位二进制码,比如原码是10101,强转后00010101,和00000101 00000000进行或运算后得到00000101 00010101
        addr = addr << 8;           //左移8位,得到00000101 00010101 00000000
        addr |= (bytes[2] & 0xFF);  //强制转化为8位二进制码,比如原码是111,强转后00000111,和00000101 00010101 00000000进行或运算后得到00000101 00010101 00000111 
        addr = addr << 8;           //左移8位,得到00000101 00010101 00000111 00000000
        addr |= ((bytes[3]) & 0xFF);//强制转化为8位二进制码,比如原码是1,强转后00000001,和00000101 00010101 00000111 00000000进行或运算后得到00000101 00010101 00000111 00000001
        return addr;                //拼接结束,返回int变量

//      优化之后的写法,原理相同,不过是先移位后直接强转的同时指定位数
//      int addr = bytes[3] & 0xFF;
//      addr |= ((bytes[2] << 8) & 0xFF00);
//      addr |= ((bytes[1] << 16) & 0xFF0000);
//      addr |= ((bytes[0] << 24) & 0xFF000000);
//      return addr;
        
    }
    

    /** 
     * 把int->string地址 
     * @param ipInt 
     * @return String 
     */  
    public static String intToIp(int ipInt) {  
        return new StringBuilder()
                .append(((ipInt >> 24) & 0xFF)).append('.')   //右移3个字节(24位),得到IP地址的第一段也就是int变量的第一个字节(从左边算起)
                .append((ipInt >> 16) & 0xFF).append('.')     //右移2字节(16位),得到int变量的第一和第二个字节(从左边算起),经过&0xFF处理得到后8位也就是byte[1]
                .append((ipInt >> 8) & 0xFF).append('.')      //同理如上
                .append((ipInt & 0xFF))                       //同理如上
                .toString();  

//        第二种,先强转二进制,再进行移位处理
//        return new StringBuilder()
//                .append(((ipInt & 0xFF000000) >> 24) & 0xFF).append('.')   //右移3个字节(24位),得到IP地址的第一段也就是byte[0],为了防止符号位是1也就是负数,最后再一次& 0xFF
//                .append((ipInt & 0xFF0000) >> 16).append('.')
//                .append((ipInt & 0xFF00) >> 8).append('.')
//                .append((ipInt & 0xFF))  
//                .toString();  
    } 
总结

二进制,是计算技术中广泛采用的一种数制,虽然平时用的不多,但是熟练掌握后,有助于加强我们对机器语言的理解和提升我们的编码水平,特别是面对资源紧张(运存)的场景时,有助于我们分析和优化问题。

你可能感兴趣的:(【详细分解】如何把IP地址保存到Int类型变量中)