一、Java常用小知识之将long型转换成字节表示

本文介绍了一种利用二进制表示签到状态的方法,通过左移操作和二进制位运算来记录用户的签到历史,包括签到状态更新算法及时间间隔计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

将long型转换成字节表示

/**
     * 将 long 类型数据转成二进制的字符串
     */
    private static String toFullBinaryString(long num) {
        //规定输出的long型最多有42位(00 00000000 00000000 00000000 00000000 00000000)
        final int size = 42;
        char[] chs = new char[size];
        for (int i = 0; i < size; i++) {
            /**
             * 目的:获取第i位的二进制数
             * 解析:
             *     ((num >> i) & 1):
             *     因为1的二进制的特殊性(0000 0001),在进行“&”运算时,只有末位会被保留下来(比如,末位为“1”,运算
             * 后为1;末位为0,运算后为0),其他位置全部被置为0。
             *     这样当需要将一个整型转换成二进制表示时,需要取出每个位置的二进制数值(1或者0),那么就可以右移该位置
             * 数,与“1”进行“&”运算。
             *     比如:取出3(0000 0011)的第2位(也就是1),则右移1位--》得到“0000 0001”,然后与1(“0000 0001”)
             * 进行“&”运算,得到“1”。以此类推即可得到每个位置为二进制数
             *     “((num >> i) & 1) + '0'”:
             *     字符‘0’的“ASCLL”码对应的数值为48,比如"(char) (49)"那么计算字符的方法就是:49-48 = 1,所以对应的
             * 字符就是'1',所以“((num >> i) & 1) + '0'”这段代码的意思就是,取出“num”二进制时对应的每个位置数值(只能
             * 为“0”或“1”,不过此时还是二进制表示法:0 0000 0000 ; 1 0000 0001)。之后,与字符'0'对应的数值“48”进行
             * 加运算,结果只能为(48 或者 49),再经过(char)强转后(“char”强转就是根据传入的数值,与“48”进行比较,
             * 比如“49”,多1,那么对应的字符就是“1”),就变成了字符,“0”或者“1”,就取出了特定位置的二进制数值
             *
             */
            chs[size - 1 - i] = (char) (((num >> i) & 1) + '0');
        }
        return new String(chs);
    }

应用

签到历史:
“签到历史”要实现的效果:
* 签到历史为整数。将整数转化为2进制数,1表示签到,0表示未签到
* 例如:
* 签到历史为3(11)时,则表示用户从第一次签到导现在为止连续签到两天。
* 签到历史为5(101)时,则表示第二天未签到
* 签到历史为25(11001)时,则表示第二天和第三天未签到

实现代码

public class SignInHistoryTool {
    /**
     * 计算签到历史记录
     * 签到历史
     * 签到历史为整数。将整数转化为2进制数,1表示签到,0表示未签到
     * 例如:
     * 签到历史为3(11)时,则表示用户从第一次签到导现在为止连续签到两天。
     * 签到历史为5(101)时,则表示第二天未签到
     * 签到历史为25(11001)时,则表示第二天和第三天未签到
     *
     * oldHistory << moveAmount 向左位移未签到天数
     * @param oldHistory 上次签到的数值
     * @param moveAmount 中间未签到的天数
     * @return
     */

    public static int historyDays(int oldHistory, int moveAmount) {
        /*
         * 重置签到历史记录(积分,连续打卡记录不清除)
         * 条件:每个月的月初重置一次
         */
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
        if (dayOfMonth == 1){
            oldHistory = 0;
            moveAmount = 1;
        }
        //(比如:0000 0011 2天)
        /*
         * 若“moveAmonut = 1”:属于正常的隔天签到
         * 若“moveAmonut = 2”:属于隔1天签到
         */
        long moveResult = oldHistory << moveAmount;
        /*
         *  将本次签到放到最后一位
         */
        moveResult +=1;
        /*
         * 将 long 类型数据转成二进制的字符串
         * 参数一:将 long 类型数据转成二进制的字符串
         * 参数二:该字符串的进制(是2进制,还是8进制之类)
         */
        return Integer.parseInt(toFullBinaryString(moveResult), 2);
    }

    /**
     * 将 long 类型数据转成二进制的字符串
     */
    private static String toFullBinaryString(long num) {
        //规定long型最多有42位(00000000 00000000 00000000 00000000 00000000 00000000)
        final int size = 42;
        char[] chs = new char[size];
        for (int i = 0; i < size; i++) {
            /**
             * 目的:获取第i位的二进制数
             * 解析:
             *     ((num >> i) & 1):
             *     因为1的二进制的特殊性(0000 0001),在进行“&”运算时,只有末位会被保留下来(比如,末位为“1”,运算
             * 后为1;末位为0,运算后为0),其他位置全部被置为0。
             *     这样当需要将一个整型转换成二进制表示时,需要取出每个位置的二进制数值(1或者0),那么就可以右移该位置
             * 数,与“1”进行“&”运算。
             *     比如:取出3(0000 0011)的第2位(也就是1),则右移1位--》得到“0000 0001”,然后与1(“0000 0001”)
             * 进行“&”运算,得到“1”。以此类推即可得到每个位置为二进制数
             *     “((num >> i) & 1) + '0'”:
             *     字符‘0’的“ASCLL”码对应的数值为48,比如"(char) (49)"那么计算字符的方法就是:49-48 = 1,所以对应的
             * 字符就是'1',所以“((num >> i) & 1) + '0'”这段代码的意思就是,取出“num”二进制时对应的每个位置数值(只能
             * 为“0”或“1”,不过此时还是二进制表示法:0 0000 0000 ; 1 0000 0001)。之后,与字符'0'对应的数值“48”进行
             * 加运算,结果只能为(48 或者 49),再经过(char)强转后(“char”强转就是根据传入的数值,与“48”进行比较,
             * 比如“49”,多1,那么对应的字符就是“1”),就变成了字符,“0”或者“1”,就取出了特定位置的二进制数值
             *
             */
            chs[size - 1 - i] = (char) (((num >> i) & 1) + '0');
        }
        translate(chs);
        return new String(chs);
    }

    /**
     * 将得到的二进制数组倒置
     * 例:
     *     第1天、第2天签到:0000 0011
     *     第3/4天未签到,第五天签到,则先左移2位,再末位加1:0001 1001
     *     将得到的二进制数组倒置:00010011(即变成第1、2天签到,第3、4天未签到)
     * @param chs 二进制数组
     */
    private static void translate(char[] chs) {
        int reverseIndex = 0;
        for (int i = 0; i < chs.length; i++){
            if (!String.valueOf(chs[i]).equals("0")){
                reverseIndex = i;
                break;
            }
        }
        if (reverseIndex != 0){
            swap(chs, reverseIndex, chs.length-1);
        }
    }

    private static void swap(char[] chs, int startIndex, int endIndex) {
        if (chs == null || chs.length==0 || startIndex >= chs.length || endIndex >= chs.length){
            return;
        }
        if (startIndex < 0){
            startIndex = 0;
        }
        if (startIndex >= endIndex){
            return;
        }
        int middleIndex = (endIndex - startIndex) / 2;
        if ((endIndex - startIndex) % 2 == 1){
            middleIndex+=1;
        }
        for (int i = 0; i < middleIndex; i++){
            char temp = chs[startIndex+i];
            chs[startIndex+i] = chs[chs.length-1-i];
            chs[chs.length-1-i] = temp;
        }
    }

    /**
     * 计算距离上一次签到,相隔天数
     * @return
     * @throws ParseException 
     */
    public static int countSignDays(Date nowDate,Date lastModifyDate) throws ParseException{
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String lastDateStr = df.format(lastModifyDate);
        Date lastDate = df.parse(lastDateStr);
        // 这样得到的差值是微秒级别
        long diff = nowDate.getTime() - lastDate.getTime();
        int days = (int) (diff / (1000 * 60 * 60 * 24));
        if (days == 0){
            //与当天24点比较,有没有过24点,过了,不足1天,按1天算
            days = compare(nowDate, lastModifyDate);
        }
        return days;
    }

    /**
     * 若当前签到时间过了前一天的24点,也算隔1天
     * @param nowDate 当前签到时间
     * @param lastModifyDate 上次签到时间
     * @return 若当前签到时间过了前一天(也就是上次签到)的24点,也算隔1天,可以签到;否则不可以签到
     */
    private static int compare(Date nowDate, Date lastModifyDate) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(lastModifyDate);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 9999);
        return nowDate.getTime() > calendar.getTimeInMillis() ? 1 : 0;
    }
}
### 将大文件换为字节流的Java实现 在处理大文件时,为了避免内存溢出问题,通常会采用分块读取的方式来将文件逐步加载到内存中并将其化为字节流。以下是基于 `FileInputStream` 和缓冲区机制的个高效实现方案。 #### 实现代码示例 以下是个完整的 Java 示例代码,用于将大文件逐块读取并存储为字节数组: ```java import java.io.*; public class LargeFileToByteStream { public static void main(String[] args) { File file = new File("path/to/largefile.txt"); try { byte[] fileBytes = readFileAsByteArray(file); System.out.println("文件已成功换为字节流,大小:" + fileBytes.length + " 字节"); } catch (IOException e) { e.printStackTrace(); } } /** * 方法功能:将大文件按块读取并返回其字节数组表示形式。 * * @param file 输入的大文件对象 * @return 文件对应的字节数组 * @throws IOException 如果发生I/O错误,则抛出异常 */ public static byte[] readFileAsByteArray(File file) throws IOException { // 获取文件长度作为字节数组容量 long fileSize = file.length(); if (fileSize > Integer.MAX_VALUE) { throw new IOException("文件过大,无法完全加载至内存!"); } FileInputStream fis = null; ByteArrayOutputStream bos = null; try { fis = new FileInputStream(file); bos = new ByteArrayOutputStream((int) fileSize); byte[] buffer = new byte[8192]; // 定义缓存区大小(8KB) int bytesRead; while ((bytesRead = fis.read(buffer)) != -1) { // 循环读取直到结束 bos.write(buffer, 0, bytesRead); // 将每次读取的数据写入输出流 } return bos.toByteArray(); // 返回最终的字节数组 } finally { if (fis != null) { fis.close(); // 关闭输入流 } if (bos != null) { bos.close(); // 关闭输出流 } } } } ``` 此代码通过定义个固定大小的缓冲区(如 8 KB),循环读取文件内容并将每块数据追加到 `ByteArrayOutputStream` 中,最后生成整个文件的字节数组[^4]。 --- ### 注意事项 对于非常大的文件,即使使用上述方法也可能面临内存不足的风险。在这种情况下,推荐直接操作文件而不将其全部加载到内存中。例如,在网络传输场景下可以直接利用 `InputStream` 或其他流式 API 进行实时处理[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值