美文网首页Java 杂谈
【详细分解】如何把IP地址保存到Int类型变量中

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

作者: 风暴小狼 | 来源:发表于2019-07-04 14:15 被阅读2次

    如何把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类型变量中

        本文链接:https://www.haomeiwen.com/subject/qsnlhctx.html