当前位置: 首页 > 后端技术 > Java

这些Java特性虽然鲜为人知,但非常棒

时间:2023-04-02 01:21:45 Java

在本文中,您将了解一些您以前可能没有听说过的有用的Java特性。这是我最近在阅读有关Java的文章时发现并整理的私有特性列表。我不会关注语言,而是关注API。您喜欢Java并想了解它的最新特性吗?如果是,你可以阅读我关于Java8之后的新特性的文章。接下来,在这篇文章中,你将了解八个鲜为人知但非常有用的特性。让我们开始吧!1.延迟队列我们都知道Java中有很多种集合。那么你听说过DelayQueue吗?它是一种特殊类型的Java集合,允许我们根据元素的延迟对元素进行排序。坦率地说,这是一门非常有趣的课。尽管DelayQueue类是Java集合的成员,但它位于java.util.concurrent包中。它实现了BlockingQueue接口。只有当一个元素的时间到期时,它才能从队列中移除。要使用这个集合,首先我们的类需要实现Delayed接口的getDelay方法。当然不一定非得是类,也可以是JavaRecord。publicrecordDelayedEvent(longstartTime,Stringmsg)implementsDelayed{publiclonggetDelay(TimeUnitunit){longdiff=startTime-System.currentTimeMillis();返回unit.convert(diff,TimeUnit.MILLISECONDS);}publicintcompareTo(Delayedo){return(int)(this.startTime-((DelayedEvent)o).startTime);}}假设我们要让元素延迟10秒,那么只需要将DelayedEvent类上的时间设置为当前时间加上10秒即可。finalDelayQueuedelayQueue=newDelayQueue<>();finallongtimeFirst=System.currentTimeMillis()+10000;delayQueue.offer(newDelayedEvent(timeFirst,"1"));log.info("Done");log.info(delayQueue.take().msg());对于上面的代码,我们可以看到什么输出?如下。2.时间格式支持显示一天中的时间好吧,我承认这个Java特性对你们大多数人来说用处不大,但我对这个特性情有独钟……Java8对时间处理做了很多改进应用程序接口。从这个版本的Java开始,在大多数情况下,我们不需要任何额外的库来处理时间,比如JodaTime。你可能想不到,从Java16开始,我们甚至可以使用标准格式化程序来表示一天中的时间,即“早上”或“下午”。这是一种名为B的新格式模式。Strings=DateTimeFormatter.ofPattern("B").format(LocalDateTime.now());System.out.println(s);下面是我运行的结果。当然,您的结果可能会随时间而变化。OK,等一下……现在,你可能会问为什么这种格式叫B。事实上,这并不是这种格式最直观的名称。但也许下表会消除我们所有的疑虑。它是DateTimeFormatter可以处理的模式字符和符号的片段。我想,B是第一个免费的字母。当然,我可能是错的。3.StampedLock在我看来,JavaConcurrent是最有趣的Java包之一。同时,它也是一个不为开发者所熟知的包,尤其是在开发者主要使用web框架的情况下。我们当中有多少人曾经在Java中使用过锁?锁是一种比同步块更灵活的线程同步机制。从Java8开始,我们可以使用一种名为StampedLock的新锁。StampedLock是ReadWriteLock的替代品。它允许对读取操作进行乐观锁定。此外,它的性能优于ReentrantReadWriteLock。假设我们有两个线程。第一个线程更新余额,而第二个线程读取余额的当前值。为了更新余额,我们当然需要先读取它的当前值。这里我们需要某种同步机制,假设第一个线程同时运行多次。第二个线程说明了如何对读取操作使用乐观锁定。StampedLocklock=newStampedLock();Balanceb=newBalance(10000);Runnablew=()->{longstamp=lock.writeLock();b.setAmount(b.getAmount()+1000);System.out.println("写入:"+b.getAmount());lock.unlockWrite(stamp);};Runnabler=()->{longstamp=lock.tryOptimisticRead();如果(!lock.validate(stamp)){stamp=lock.readLock();try{System.out.println("读取:"+b.getAmount());}最后{lock.unlockRead(stamp);}}else{打印lnSystem.out。“乐观读取失败”);}};现在,我们同时运行两个线程50次。它应该按预期工作,最终余额为60000。ExecutorService执行器=Executors.newFixedThreadPool(10);for(inti=0;i<50;i++){executor.submit(w);executor.submit(r);}4.并发累加器在Java的Concurrent包中,有趣的不只是锁,还有一个有趣的地方是并发累加器。我们也有并发加法器,但它们的功能非常相似。LongAccumulator(我们还有DoubleAccumulator)将使用提供给它的函数更新一个值。在很多场景下,它允许我们实现无锁算法。当多个线程正在更新一个公共值时,它通常比AtomicLong更合适。让我们看看它是如何工作的。要创建它,我们需要在构造函数中设置两个参数。第一个参数是用于计算累加结果的函数。通常,我们会使用sum方法。第二个参数表示累加器的初始值。现在,让我们创建一个初始值为10000的LongAccumulator,并从多个线程调用accumulate()方法。最后的结果是什么?如果您还记得的话,我们正在做与上一节完全相同的事情,但这次没有任何锁。LongAccumulatorbalance=newLongAccumulator(Long::sum,10000L);Runnablew=()->balance.accumulate(1000L);ExecutorServiceexecutor=Executors.newFixedThreadPool(50);for(inti=0;i<50;i++){executor.submit(w);}executor.shutdown();if(executor.awaitTermination(1000L,TimeUnit.MILLISECONDS))System.out.println("Balance:"+balance.get());断言余额。得到()==60000L;5.十六进制格式这个功能没什么大不了的。有时我们需要在十六进制的字符串、字节或字符之间进行转换。从Java17开始,我们可以使用HexFormat类来实现这一点。只需创建一个HexFormat实例,然后您就可以将输入的字节数组等格式化为十六进制字符串。您还可以将输入的十六进制字符串解析为字节数组,如下所示。HexFormatformat=HexFormat.of();byte[]input=newbyte[]{127,0,-50,105};Stringhex=format.formatHex(input);System.out.println(hex);byte[]output=format.parseHex(hex);assertArrays.compare(input,output)==0;6.对数组进行二分查找假设我们要向排序后的数组中插入一个新元素。如果元素已存在于数组中,Arrays.binarySearch()返回搜索键的索引,否则,它返回一个插入点,我们可以使用它来计算新键的索引:-(insertionpoint)-1。此外,在Java中,binarySearch方法是在有序数组中查找元素的最简单、最有效的方法。让我们考虑以下示例。我们有一个包含四个按升序排列的元素的输入数组。我们要将数字3插入到这个数组中,下面的代码显示了如何计算插入点的索引。int[]t=newint[]{1,2,4,5};intx=Arrays.binarySearch(t,3);断言~x==2;7.BitSet如果我们需要做一些位数组应该怎么办?你打算使用boolean[]来实现它吗?实际上,有一种更有效和内存效率更高的方法可以做到这一点。这是BitSet类。BitSet类允许我们存储和操作位数组。与boolean[]相比,它消耗的内存少8倍。我们可以对数组进行逻辑运算,比如:and,or,xor。假设我们有两个位数组,我们想对它们执行异或运算。为此,我们需要创建两个BitSet实例并在实例中插入样本元素,如下所示。最后,在其中一个BitSet实例上调用xor方法,并将第二个BitSet实例作为参数。BitSetbs1=newBitSet();bs1.set(0);bs1.set(2);bs1.set(4);System.out.println("bs1:"+bs1);BitSetbs2=newBitSet();bs2.set(1);bs2.set(2);bs2.set(3);System.out.println("bs2:"+bs2);bs2.xor(bs1);System.out.println("异或:“+bs2);下面是上面代码的运行结果:8.Phaser最后,我们介绍本文最后一个有趣的Java特性。与其他一些示例一样,它也是名为Phaser的JavaConcurrent包的一个元素。它与广为人知的CountDownLatch非常相似。但是,它提供了一些附加功能。它允许我们设置在继续执行之前需要等待的动态线程数。在Phaser中,一定数量的线程需要在屏障上等待,然后才能继续执行下一步。多亏了这一点,我们可以协调多个阶段的执行。在下面的示例中,我们设置了50个线程的障碍,在进入下一阶段执行之前需要达到该障碍。然后,我们创建一个调用Phaser实例上的arriveAndAwaitAdvance()方法的线程。它会阻塞线程,直到所有50个线程都到达屏障。然后,它进入阶段1,该阶段也再次调用arriveAndAwaitAdvance()方法。Phaser移相器=newPhaser(50);Runnabler=()->{System.out.println("phase-0");phaser.arriveAndAwaitAdvance();System.out.println("第一阶段");移相器.arriveAndAwaitAdvance();System.out.println("第二阶段");phaser.arriveAndDeregister();};ExecutorServiceexecutor=Executors.newFixedThreadPool(50);for(inti=0;i<50;i++){executor.submit(r);}下面是执行上面代码的结果: