关于JDK中的ByteBuffer与Netty中的ByteBuf的对比

本文对比了JDK中ByteBuffer的局限性,并深入介绍了Netty的ByteBuf如何改进这些局限,实现了更高效的读写分离及动态容量调整。通过具体代码示例展示了其内部工作机制。

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

JDK中ByteBuffer的缺点

  1. 只是用一个标志位position来进行读写标记,读写操作要使用flip方法进行切换,不太友好。
  2. 因为ByteBuffer中的实际存储数据的数组是使用final修饰符修饰的,所以不可以 在原来buffer的基础上动态扩容或者缩小。如果需要扩容,需要另外新建一个ByteBuffer,并将旧的ByteBuffer里面的数组复制到已经扩容的ByteBuffer.
     final byte[] hb;

Netty中的ByteBuf则完全对JDK中的ByteBuffer的缺点进行了改进

  1. 使用readerIndex和writerIndex分别维护读操作和写操作,实现读写索引分离,更加直观。
    byte[] array;
  2. ByteBuf使用的底层数据维护数组没有使用final关键字,所以存在直接在原来ByteBuf进行扩容的可能,而这件事,Betty已经为我们完成,封装在它的write系列方法当中。但是它也存在着一个上限,这个上限就是Integer.MAX_VALUE.
        public ByteBuf writeByte(int value) {
            ensureWritable0(1);//用于验证可写字节长度是否大于将要写入的字节长度。
            _setByte(writerIndex++, value);//写入一个字节并且索引加一的操作。
            return this;
        }
        final void ensureWritable0(int minWritableBytes) {
            ensureAccessible();//验证ByteBuf中的引用计数是否为零,为真则抛出异常,违背了Netty对于垃圾回收的约定
            if (minWritableBytes <= writableBytes()) {//验证可写字节长度是否大于将要写入的字节长度。
                return;//如果是大于,则什么都不做,直接返回
            }
            //如果可写字节长度小于将要写入的最小字节长度,则需要进行扩容
            if (minWritableBytes > maxCapacity - writerIndex) {//判断如果将要到达最大的上限,则抛出异常
                throw new IndexOutOfBoundsException(String.format(
                        "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",
                        writerIndex, minWritableBytes, maxCapacity, this));
            }
    
            // Normalize the current capacity to the power of 2.//如果可写字节长度小于将要写入的最小字节长度,并且没有达到最大上限,
            int newCapacity = alloc().calculateNewCapacity(writerIndex + minWritableBytes, maxCapacity);//计算扩容后capacity
            //扩容的当前capactiy的两倍。
            // Adjust to the new capacity.
            capacity(newCapacity);//重新设置capactiy.并进行数组的重新拷贝
        }

        public ByteBuf capacity(int newCapacity) {
            checkNewCapacity(newCapacity);//验证新生成的capacity是否大于最大整数,是则抛出异常
    
            int oldCapacity = array.length;
            byte[] oldArray = array;
            if (newCapacity > oldCapacity) {
                byte[] newArray = allocateArray(newCapacity);//新建一个新的数组,数组长度为新创建capacity
                System.arraycopy(oldArray, 0, newArray, 0, oldArray.length);//进行旧数组中的数组拷贝到新数组中
                setArray(newArray);//将当前buffer中的array指向新数组
                freeArray(oldArray);//释放掉旧数组
            } else if (newCapacity < oldCapacity) {
                byte[] newArray = allocateArray(newCapacity);
                int readerIndex = readerIndex();
                if (readerIndex < newCapacity) {
                    int writerIndex = writerIndex();
                    if (writerIndex > newCapacity) {
                        writerIndex(writerIndex = newCapacity);
                    }
                    System.arraycopy(oldArray, readerIndex, newArray, readerIndex, writerIndex - readerIndex);
                } else {
                    setIndex(newCapacity, newCapacity);
                }
                setArray(newArray);
                freeArray(oldArray);
            }
            return this;
        }



### Java 实现根据经纬度计算日出和日落时间 以下是基于太阳位置算法的一个简单实现,用于通过给定的经纬度以及日期来计算当天的日出和日落时间。此方法依赖于天文计算中的标准公式[^1]。 ```java import java.time.LocalDate; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; public class SunriseSunsetCalculator { private static final double PI = Math.PI; private static final double DEG_TO_RAD = PI / 180.0; public static void main(String[] args) { double latitude = 40.7128; // 替换为实际纬度 double longitude = -74.0060; // 替换为实际经度 LocalDate date = LocalDate.now(); // 当前日期 ZonedDateTime sunriseTime = calculateSunrise(date, latitude, longitude); ZonedDateTime sunsetTime = calculateSunset(date, latitude, longitude); System.out.println("Sunrise Time: " + sunriseTime.format(DateTimeFormatter.ISO_LOCAL_TIME)); System.out.println("Sunset Time: " + sunsetTime.format(DateTimeFormatter.ISO_LOCAL_TIME)); } private static ZonedDateTime calculateSunrise(LocalDate date, double latitude, double longitude) { return computeSunEventTime(date, latitude, longitude, true); } private static ZonedDateTime calculateSunset(LocalDate date, double latitude, double longitude) { return computeSunEventTime(date, latitude, longitude, false); } private static ZonedDateTime computeSunEventTime(LocalDate date, double latitude, double longitude, boolean isRiseTime) { int dayOfYear = date.getDayOfYear(); ZoneId zoneId = ZoneId.systemDefault(); double lngHour = longitude / 15.0; double t = dayOfYear + ((6 - lngHour) / 24); // 日序数调整 double meanAnomaly = (0.9856 * t) - 3.289; double sunLongitude = meanAnomalyToSunLongitude(meanAnomaly); double rightAscension = sunLongitudeToRightAscension(sunLongitude); double sinDec = Math.sin(sunLongitude * DEG_TO_RAD) * Math.sin(23.44 * DEG_TO_RAD); double cosH = (Math.cos(-0.83 * DEG_TO_RAD) - (sinDec * Math.sin(latitude * DEG_TO_RAD))) / (Math.cos(sinDec) * Math.cos(latitude * DEG_TO_RAD)); double h = Math.acos(cosH) / DEG_TO_RAD; h = isRiseTime ? 360 - h : h; double localMeanNoon = (720 - (4 * longitude) - (lngHour * 60)) + (h * 4); double utcTime = localMeanNoon / 60; return ZonedDateTime.of(date.getYear(), date.getMonthValue(), date.getDayOfMonth(), (int) utcTime, (int) ((utcTime % 1) * 60), 0, 0, zoneId); } private static double meanAnomalyToSunLongitude(double m) { double l = m + (1.916 * Math.sin(m * DEG_TO_RAD)) + (0.020 * Math.sin(2 * m * DEG_TO_RAD)) + 282.634; while (l >= 360) l -= 360; while (l < 0) l += 360; return l; } private static double sunLongitudeToRightAscension(double l) { double ra = Math.atan(0.91764 * Math.tan(l * DEG_TO_RAD)) / DEG_TO_RAD; if (ra < 0) ra += 360; return ra; } } ``` #### 关键说明 - **输入参数**:该程序接受三个主要参数——`latitude`(纬度)、`longitude`(经度)和 `date`(日期)。这些数据决定了具体的地理位置目标日期。 - **核心逻辑**:利用天文学上的均差修正法,结合地球自转角度变化规律,推导出特定地的日出/日落时刻[^2]。 - **时区处理**:最终返回的时间会自动转换成运行环境所在的本地时区。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值