JAVA中如何判断对象与类的关系之-instanceOf, Class.isInstance, Class.isAssignableFrom

前言 在开发中,常常会碰到检查JAVA对象是不是指定类型的情况,在JAVA中提供了以下几种方法实现此类需求: instanceOf Class.isInstance() Class.isAssignbleFrom() 今天对此三种实现进行具体学习,以加深理解并更好的使用 准备工作 先创建一个名为 Shape 的接口,一个实现了 Shape 接口的类 Triangle , 再定义一个继承 Triangle 类的类 IsoscelesTriangle public interface Shape { } public class Triangle implements Shape { } public class IsoscelesTriangle extends Triangle { } 以上三个类的类图关系如下 instanceOf instanceOf 是JAVA中的一个关键字,用于判断 实例对象是否是指定类型的子类型 ,常用于类型转换(cast)之前进行判断,操作的元素是一个对象和一个类型 Shape shape = new Triangle(); Triangle triangle = new Triangle(); IsoscelesTriangle isoscelesTriangle = new IsoscelesTriangle(); Shape nonspecificShape = null; assertTrue(shape instanceof Shape); assertTrue(triangle instanceof Shape); assertTrue(isoscelesTriangle instanceof Shape); assertFalse(nonspecificShape instanceof Shape); assertTrue(shape instanceof Triangle); assertTrue(triangle instanceof Triangle); assertTrue(isoscelesTriangle instanceof Triangle); assertFalse(nonspecificShape instanceof Triangle); assertFalse(shape instanceof IsoscelesTriangle); assertFalse(triangle instanceof IsoscelesTriangle); assertTrue(isoscelesTriangle instanceof IsoscelesTriangle); assertFalse(nonspecificShape instanceof IsoscelesTriangle); 上面例子测试 instanceOf 左侧对象是否是右侧类型的实例类型 ...

2022-12-12 · 2 min · 249 words · tomyli

Springwebflux源码学习-服务启动流程

学习目标 理解WebFlux核心组件HttpHandler、WebHandler的创建过程 理解DispatcherHandler如何映射请求url及参数的过程 理解Netty服务的主要启动节点 此次学习的Spring boot版本为2.6.6,对应的Spring webflux版本为5.3.18 入口 Springboot工程入口方法为 SpringApplication.run() ,从此方法开始进行分析。 public static void main(String[] args) { SpringApplication.run(Main.class, args); } 决定启动的web应用类型 整个服务的web应用类型是在构造 SpringApplication 类时进行确定的,具体代码如下: this.webApplicationType = WebApplicationType.deduceFromClasspath(); 判断应用类型的逻辑 org.springframework.web.reactive.DispatcherHandler存在 org.springframework.web.servlet.DispatcherServlet不存在 org.glassfish.jersey.servlet.ServletContainer不存在 即确认webApplicationType为 Reactive if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { return WebApplicationType.REACTIVE; } 创建具体的ApplicationContext 根据webApplicationType(Reactive)确定创建的ApplicationContext为AnnotationConfigReactiveWebServerApplicationContext,此Context支持 @Component 和 JSR-330规范中 @Inject 形式的依赖注解 SpringAplication.createApplicationContext() switch (webApplicationType) { case SERVLET: return new AnnotationConfigServletWebServerApplicationContext(); case REACTIVE: return new AnnotationConfigReactiveWebServerApplicationContext(); default: return new AnnotationConfigApplicationContext(); } 最主要的ApplicationContext.refresh()方法 refresh的Refresh阶段 刷新操作是在 AbstractApplicationContext 类的refresh方法中进行,在调用invokeBeanFactoryPostProcessors方法时会有对controller注解的扫描与处理 ...

2022-05-24 · 2 min · 351 words · tomyli

记一次SpringWebflux框架下堆外OOM排查经历

症状 生产环境业务页面打不开,看错误日志是 OutOfDirectMemoryError (OOM)了,详情报错信息如下: 2022-03-21 06:00:00.541 [,] [tafprx-asyrecv_9] ERROR r.c.p.Operators - [error,314] - Operator called default onErrorDropped java.lang.OutOfMemoryError: Direct buffer memory at java.base/java.nio.Bits.reserveMemory(Bits.java:175) at java.base/java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:118) at java.base/java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:317) at io.netty.buffer.PoolArena$DirectArena.allocateDirect(PoolArena.java:632) at io.netty.buffer.PoolArena$DirectArena.newChunk(PoolArena.java:607) at io.netty.buffer.PoolArena.allocateNormal(PoolArena.java:202) at io.netty.buffer.PoolArena.tcacheAllocateSmall(PoolArena.java:172) at io.netty.buffer.PoolArena.allocate(PoolArena.java:134) at io.netty.buffer.PoolArena.allocate(PoolArena.java:126) at io.netty.buffer.PooledByteBufAllocator.newDirectBuffer(PooledByteBufAllocator.java:395) at io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:187) at io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:178) at io.netty.buffer.AbstractByteBufAllocator.buffer(AbstractByteBufAllocator.java:115) at org.springframework.core.io.buffer.NettyDataBufferFactory.allocateBuffer(NettyDataBufferFactory.java:71) at org.springframework.core.io.buffer.NettyDataBufferFactory.allocateBuffer(NettyDataBufferFactory.java:39) at org.springframework.http.codec.json.AbstractJackson2Encoder.encodeValue(AbstractJackson2Encoder.java:236) at org.springframework.http.codec.json.AbstractJackson2Encoder.lambda$encode$0(AbstractJackson2Encoder.java:150) at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:113) at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1815) at reactor.core.publisher.MonoCompletionStage.lambda$subscribe$0(MonoCompletionStage.java:82) at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:859) at java.base/java.util.concurrent.CompletableFuture.uniWhenCompleteStage(CompletableFuture.java:883) at java.base/java.util.concurrent.CompletableFuture.whenComplete(CompletableFuture.java:2251) at java.base/java.util.concurrent.CompletableFuture.whenComplete(CompletableFuture.java:143) at reactor.core.publisher.MonoCompletionStage.subscribe(MonoCompletionStage.java:57) at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:157) at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1815) at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:249) at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:199) at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:199) at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.complete(MonoIgnoreThen.java:284) at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onNext(MonoIgnoreThen.java:187) at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1815) at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:151) 根据报错的堆栈,找到 AbstractJackson2Encoder:236 行,调试发现框架使用的buffer在最后都进行了释放,看着没问题。 ...

2022-03-31 · 3 min · 615 words · tomyli

让好代码生生不息-更好的使用String

开发中常用的String操作 开发中经常用到的操作有: 格式化/日志格式化 字符串判断 字符串处理,常见的如处理字符串的第一个/最后一个字符 String.format String.format()是一个格式化的方法,使用%为前缀来表示要格式化的内容占位,一般的使用方法为: String.format("test result is %s", "success"); 但是当要格式化的占位符与参数个数不同时会发生什么呢? 占位符个数与参数个数相同 如上代码,可以正常进行输出: test result is success 占位符个数少于参数个数 String.format("test result is %s", "success", "other"); 可以正常打印,输出结果为: test result is success 占位符个数多于参数个数 String.format("test result is %s %s %s", "success", "other"); 抛出了异常java.util.MissingFormatArgumentException: Format specifier ‘%s’ 问题总结 为什么占位符数少于参数个数可以正常执行,而多于参数个数却报了异常?看JAVADOC发现,如果占位符个数少于参数个数,则多余的参数被忽略,如果多于参数个数,则在进行解析时会判断待处理占位符位置与参数个数,主要代码逻辑如下: if (args != null && lasto > args.length - 1) throw new MissingFormatArgumentException(fs.toString()); 所以在使用String.format方法 切记 要参数个数多于待解析占位符的个数,这种问题常因为Copy操作后,处理了部分参数而忘记对应处理占位符 替换方案 目前看到很多的老代码打印日志时大都使用String.format(),可以改成slf4j的{} 还可以使用Apache common utils中的StringUtils.joinWith()或者Google guava的Joiner连接器 Apache commons utils-StringUtils 在StringUtils中有一个isNumeric方法,用来判断字符串是不是数字,那下面的代码会返回什么呢? ...

2021-08-23 · 2 min · 221 words · tomyli

让好代码生生不息-更好的对待NULL

什么样的代码是好代码 易读的代码,行云流水的代码 JAVA中的NULL处理 众所周知,在Java代码中总是少不了一堆的判null逻辑,在代码bug中,NullPointerException出镜率也是非常高的,尤其是现在的主流分布式架构,一个web接口要调用后端的N个RPC服务来实现功能,基于防御编程的思想,判null就更重要了 在处理NULL时怎么做更好 IDE 既然无法避免null,IDE就给程序员增加了可以快速判断空的功能,比如在IDEA中,可以使用Postfix功能,在对象上o使用.nn来快速输入if(o != null) {} JAVA基础类库 近期的JDK版本也针对null判断增加了一些实用的方法 Objects类 JAVA8在Objects工具提供了isNull()和nonNull()方法,JAVA9又增加了requireNonNullElse()和requireNonNullElseGet()来给待处理对象增加设置默认值的方法 知名三方库 Apache common utils中提供了非常多的判空工具类,如针对String操作的StringUtils类和针对集合操作的CollectionUtils、MapUtils、ListUtils、SetUtils,这几个类都有一些典型的方法来进行判空处理,通用的方法如下: StringUtils isEmpty()/isNotEmpty() 判断String是否为空/不空 isBlank()/isNoneBlank() 判断String是否为空/不空,这类方法还会判断实际值是否为空串 defaultString() 此方法允许在String为null时为返回空串 defaultIfBlank()/defaultIfEmpty() 此方法允许在String为空串或null时设置一个默认值 集合类Utils isEmpty()/isNotEmpty() 判断集合是否为空/不空 emptyIfNull() 这是一个很有用的方法,当传入对象为空时则返回设置的默认对象,可以很好的根治if处理null的问题,下面两段代码演示一下 正常情况下有if判断 List<Object> values = rpcClient.getFromRpcList(); if(CollectionUtils.isNotEmpty(values)) { return values .forEach(System.out::println); } 使用emptyIfNull方法 return CollectionUtils.emptyIfNull(rpcClient.getFromRpcList()) .forEach(System.out::println); 这样保证了代码编写的连贯性,爽! 此方法在ListUtis、MapUtils、SetUtils中也有提供 defaultIfNull() 此方法允许在集合为null时设置一个默认值,也是可以链式操作 优秀的Coder 在编写通用方法时,对于有null返回的情况可以使用返回默认值来代替null 不返回空,以空对象来代替 比如返回一个new Object() ...

2021-07-17 · 1 min · 60 words · tomyli

JAVA8问题处理回顾-持续更新

金额累加问题 使用Reduce进行累加操作,参考这里 List<BigDecimal> bdList = new ArrayList<>(); //populate list BigDecimal result = bdList.stream().reduce(BigDecimal.ZERO, BigDecimal::add); 查找集合的最后一个元素 JAVA8的Stream只提供了FindFirst功能,没有FindLast功能,可以使用Reduce来实现查找最后一个元素的效果 import java.util.*; import java.util.stream.*; public class GetLastEleFromCollection { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("aaa"); list.add("idddd"); list.add("bbbb"); String lastElement = list.stream() .reduce((first, second) -> second) .orElse(null); System.out.println(lastElement); } } bbbb 比较两个时间是否是同一天 import java.time.*; import java.time.temporal.*; public class CompareDay { public static void main(String[] args) { final LocalDateTime now = LocalDateTime.now(); final LocalDateTime localDateTime = LocalDateTime.of(2019, 9, 29, 0, 0, 0); System.out.println(localDateTime.truncatedTo(ChronoUnit.DAYS).isEqual(now.truncatedTo(ChronoUnit.DAYS))); } } true 实现与Python的zip函数功能 import java.util.*; import java.util.function.*; public class Zip { public static void main(String[] args) { List<List<Integer>> result = handle(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6), (i, j) -> Arrays.asList(i, j)); System.out.println(result); } public static <T, U, R> List<R> handle(List<T> list, List<U> list1, BiFunction<T, U, R> biFunction) { List<R> result = new ArrayList<>(); for(int i = 0; i < list.size(); i ++) { result.add(biFunction.apply(list.get(i), list1.get(i))); } return result; } } [[1 (\, 4)] (\, [2 (\, 5)]) (\, [3 (\, 6)])] 多个Predicate链 import java.util.*; import java.util.stream.*; import java.util.function.*; public class MorePredicate { public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6); final Predicate<Integer> predicate = i -> i % 2 == 0; final Predicate<Integer> predicate1 = predicate.and(integer -> integer > 5); List<Integer> result = list.stream() .filter(predicate1) .collect(Collectors.toList()); System.out.println(result); } } [6]

2019-08-24 · 2 min · 220 words · tomyli

设计模式学习之代理模式

说明 代理模式是设计模式中使用频率非常的高的模式,像日常生活中的房产中介、外卖平台都属于代理。代理模式就是在访问真正的业务之时不会直接与真正的业务进行调用,而是与一个与真正业务对象有着相同功能声明的代理来进行处理,但是最终的处理还是由真正的业务类来进行(决定),比如房产中介,他们会帮助找房者根据条件来对可选房屋进行筛选,但是最终订与不订的权利还是由找房者来决定。常见的代理有保护代理、虚拟代理、远程代理。 代理模式 我们常用的后台系统一般会根据用户权限的不同对用户展示不同的业务菜单,拥有高权限的人会展示相对多的菜单进行操作,在这个过程中还要记录用户的操作日志,用户点了什么,执行了什么操作都需要进行记录,达到有据可查。在这个业务场景下,就可以使用代理模式进行实现。代理对象会持有一个真正业务对象的实例,在代理对象中还引用日志服务与权限服务,所有的请求都经过这个代理类来进行,这样就可以根据操作的用户的状态来进行相应的处理了。具体的代码实现如下: 定义一个查询接口,在这里定义一个查询用户收入的方法,如下: package cn.imcompany.proxy; /** * Created by tomyli on 2018/6/28. * Github: https://github.com/peng051410 */ public interface Search { void queryIncome(String username); } 再定义一个实现该接口的具体业务类: package cn.imcompany.proxy; /** * Created by tomyli on 2018/6/28. * Github: https://github.com/peng051410 */ public class SearchService implements Search { @Override public void queryIncome(String username) { System.out.println(username + "开始查询"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(username + "查询完成"); } } 在上面代码中就实现了查询收入的方法,简单的打印了两行日志。 ...

2018-07-16 · 1 min · 189 words · tomyli

设计模式学习之委托模式

说明 委托模式不属于23种设计模式中,但是它在Spring框架中使用的很频繁,Spring的作用在Java的开发是非常大,所以需要学习一下委托模式。委托模式是指两个对象都有相同的方法与功能,比如A、B两个对象都有相同的方法,但是有调用A的method方法,A不会自己来处理而是直接调用了B的同名方法,由两个对象参与同一个请求,接受请求的对象将委托给另一个对象来处理。 委托模式 在一般的公司中,都会分为老板、项目经理、开发人员结构,老板负责制定决策,分配给项目经理后,他不会自己来完成,而是把需要的功能进行分解,交给擅长的开发人员进行实现。虽然项目经理也可以完成,但是是他把这些事情委托给了多个开发人员来做,加快了完成任务的速度。就以这个需求来说,使用委托模式来进行实现: 定义抽象的人类接口: package cn.imcompany.delegate; /** * Created by tomyli on 2018/6/27. * Github: https://github.com/peng051410 */ public interface Member { void showName(); void showSkill(); void doWork(); } 上面代码中定义了抽象的人类接口,显示这个人的名字,拥有的技能,工作的状态情况,接下来实现需求中的每个人。 定义老板(BOSS): package cn.imcompany.delegate; /** * Created by tomyli on 2018/6/27. * Github: https://github.com/peng051410 */ public class Boss implements Member { @Override public void showName() { System.out.println("i am boss"); } @Override public void showSkill() { System.out.println("send commend"); } @Override public void doWork() { } } 定义项目经理和员工: ...

2018-07-07 · 2 min · 282 words · tomyli

设计模式学习之观察者

说明 观察者模式在日常开发中使用频率也是非常高的,它建立了对象与对象之间的关系,一个对象的行为改变会通知到另一个对象,被通知的对象根据通知做出相应的处理动作。在观察者中分为目标对象(被观察者)、观察者。观察者模式是一种对象行为模式。 观察者模式 现在很多人都购买股票,在市面上有很多的股票软件供广大股民使用。股票软件一般会在用户关注的股票有变动时发送消息来通知股民。在这里,某一支股票就是目标对象,广大股民与股票软件就是观察者,他们根据股票的行情变化来做出相应买入或者卖出操作。这一需求可以使用观察者模式进行实现。代码如下: 先来定义一个抽象的购买股票的股民们,他们有喜有悲的心情: package cn.imcompany.observer.stock; /** * Created by tomyli on 2018/6/23. * Github: https://github.com/peng051410 */ public interface Buyer { void setName(String name); String getName(); void happy(); void sad(); } 再来定义具体的股民行为实现类: package cn.imcompany.observer.stock; /** * Created by tomyli on 2018/6/23. * Github: https://github.com/peng051410 */ public class ConcrectBuyer implements Buyer { private String name; @Override public void setName(String name) { this.name = name; } @Override public String getName() { return name; } @Override public void happy() { System.out.println(this.getName() + ":happy"); } @Override public void sad() { System.out.println(this.getName() + ":sad"); } } 具体的股民实现了喜与悲的方法,这样观察者已经准备就绪。下面来定义股票软件: ...

2018-07-04 · 2 min · 244 words · tomyli

设计模式学习之装饰器

说明 装饰器设计模式是使用比较频繁的设计模式,它在不改变原物体的情况对原物体实现了功能的增加,原物体还是一个可单独使用的个体。在JAVA的类库中应该会马上想到IO类,各种的InputStrem,OutputStrem。 装饰器模式 在用户登录的中过程,我们需要对用户输入的密码进行加密操作,在设计时会提供好多种加密的算法,例如简单的BASE64加密,复杂的如MD5、DSA加密。直接把用户的密码存入数据库是真的在耍流氓。在使用这些算法时,我们想灵活的使用,比如使用完简单加密还可以再进行一下MD5的加密。针对这样的需求,可以使用装饰器模式进行实现。大体思路如下: 加密接口,定义加密方法。 在装饰器中,最原始的被装饰者是目标实现,它们一般会实现加密定义的具体方法。 装饰者会针对目标对象进行装饰,所以它要同样实现加密接口的方法,一般情况下这个装饰器会定义为抽象的形式,在这个装饰器父类中维护着对目标对象的引用。 具体装饰器继承抽象装饰器来实现对目标对象的真正装饰操作。 以上的情况在代码上体现为如下形式: 加密接口 package cn.imcompany.decorator.encrypt; /** * Created by tomyli on 2018/6/25. * Github: https://github.com/peng051410 */ public interface Encryptor { void encrypt(); } 具体加密类,实现加密处理接口 package cn.imcompany.decorator.encrypt; /** * Created by tomyli on 2018/6/25. * Github: https://github.com/peng051410 */ public class ConcreteEncryptor implements Encryptor { @Override public void encrypt() { System.out.println("base encrypt!"); } } 抽象加密装饰器 package cn.imcompany.decorator.encrypt; /** * Created by tomyli on 2018/6/25. * Github: https://github.com/peng051410 */ public abstract class EncryptDecorator implements Encryptor { private Encryptor encrypt; public EncryptDecorator(Encryptor encrypt) { this.encrypt = encrypt; } @Override public void encrypt() { encrypt.encrypt(); } } 上面的抽象装饰器实现的加密接口,在这个类中维护加密接口的实体,加密方法直接调用其维护实体的加密方法。 ...

2018-07-02 · 2 min · 236 words · tomyli