首页 文章 精选 留言 我的

精选列表

搜索[提高],共10005篇文章
优秀的个人博客,低调大师

量子纠缠纯化效率提高6000多倍远超国际水平

当前,以量子信息科学为代表的量子科技不断进步,形成新的科学前沿,激发科技创新,孕育对人类社会产生巨大影响的颠覆性技术。 世界各国都对其高度关注,各大科技公司、研究院也纷纷布局。 我国作为科技强国,高度重视量子信息科技的发展,在量子信息科技领域突破了一系列重要科学问题和关键核心技术,产出了一批具有重要国际影响力的成果。 来自中国科大的消息显示,近日,中国科大郭光灿院士团队李传锋、柳必恒研究组与南京邮电大学盛宇波等人合作,利用高品质的超纠缠源,首次实现了11公里的远距离量子纠缠纯化,纯化效率比此前国际最好水平提升了6000多倍,在量子通信和量子网络的研究中取得了重要进展。 据悉,在量子通信传输中,如何实现远距离量子通信一直以来都是国际研究的热点。对于这一问题,目前主要有两种解决方案: 其一是在几乎真空,量子信号损耗极小的外太空,利用卫星扩展量子通信距离。例如,我国于2016年成功发射了国际首颗量子科学实验卫星“墨子号”,成功验证了这一方案的可行性。 其二则是在光纤网络中使用量子中继器,将一段长距离光纤信道分割成多段距离比较短的信道,使得量子信号不再随距离的增加而指数衰减,从而扩展量子通信距离。 谈及量子中继,它是在噪声信道中实现长距离量子通信的重要途径,具有重要科学研究和应用价值,国际上关于量子中继器研究的竞争非常激烈。传统量子中继器需要基于纠缠交换、纠缠纯化、量子存储三个必不可少的技术。 其中,量子纠缠纯化是量子中继中的关键操作,利用量子纠缠纯化操作可以从两份纠缠度较低的纠缠态中提炼出一份纠缠度较高的纠缠态。此前的纠缠纯化协议都是利用两对低纠缠度的光子对实现,而研究组与合作者提出仅需一对超纠缠光子对的纠缠纯化方案。 那么,如何提纯高品质的量子纠缠态? 研究团队通过制备出偏振和路径分别处于纠缠态的超纠缠光子对,并在11公里长的多芯光纤里进行纠缠分发,然后进行量子纠缠纯化操作。实验结果表明,分发后的偏振纠缠和路径纠缠初始保真度均为约0.665时,纯化得到的纠缠态的保真度可以提升到0.774,而初始保真度均为约0.771时,纯化后的保真度则可提升到0.887。 除此之外,研究团队首次将纠缠纯化用于量子密钥分发,使有效密钥率从0提升至0.371。 中国科大取得的这一成果可以说是迈出了纠缠纯化从实验室平台到远距离的关键一步,同时大幅提升了纠缠纯化效率,为将来实现高效率的量子中继提供了有力的技术保障。

优秀的个人博客,低调大师

Dubbo如何通过SPI提高框架的可扩展性?

介绍 最近看了一下Dubbo的源码,国人写的框架和国外的果然是两种不同的风格,Dubbo的源码还是比较清晰容易懂的。Spring框架一个Bean的初始化过程就能绕死在源码中. Dubbo的架构是基于分层来设计的,每层执行固定的功能,上层依赖下层,下层的改变对上层不可见,每层都是可以被替换的组件 Service和Config为API接口层,让Dubbo使用者方便的发布和引用服务 其他各层均为SPI层,意味着每层都是组件化的,可以被替换 例如,注册中心可以用Redis,Zookeeper。传输协议可以用dubbo,rmi,hessian等。网络通信可以用mina,netty。序列化可以用fastjson,hessian2,java原生的方式等 SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能 那么Dubbo的SPI是怎么实现的呢?先来了解一下Java SPI Java SPI Java SPI是通过策略模式实现的,一个接口提供多个实现类,而使用哪个实现类不在程序中确定,而是配置文件配置的,具体步骤如下 定义接口及其对应的实现类 在META-INF/services目录下创建以接口全路径命名的文件 文件内容为实现类的全路径名 在代码中通过java.util.ServiceLoader#load加载具体的实现类 写个Demo演示一下 publicinterfaceCar{voidgetBrand();} publicclassBenzCarimplementsCar{@OverridepublicvoidgetBrand(){System.out.println("benz");}} publicclassBMWCarimplementsCar{@OverridepublicvoidgetBrand(){System.out.println("bmw");}} org.apache.dubbo.Car的内容如下 org.apache.dubbo.BenzCarorg.apache.dubbo.BMWCar 测试类 publicclassJavaSpiDemo{publicstaticvoidmain(String[]args){ServiceLoader<Car>carServiceLoader=ServiceLoader.load(Car.class);//benz//bmwcarServiceLoader.forEach(Car::getBrand);}} Dubbo SPI 用Dubbo SPI将上面的例子改造一下 @SPIpublicinterfaceCar{voidgetBrand();} publicclassBenzCarimplementsCar{@OverridepublicvoidgetBrand(){System.out.println("benz");}} publicclassBMWCarimplementsCar{@OverridepublicvoidgetBrand(){System.out.println("bmw");}} org.apache.dubbo.quickstart.Car的内容如下 benz=org.apache.dubbo.quickstart.BenzCarbmw=org.apache.dubbo.quickstart.BMWCar 测试类 publicclassDubboSpiDemo{publicstaticvoidmain(String[]args){ExtensionLoader<Car>extensionLoader=ExtensionLoader.getExtensionLoader(Car.class);Carcar=extensionLoader.getExtension("benz");car.getBrand();}} @Documented@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE})public@interfaceSPI{Stringvalue()default"";} @SPI标记接口是一个Dubbo SPI接口,即是一个扩展点,value属性可以指定默认实现 Dubbo 并未使用 Java 原生的 SPI 机制,而是对其进行了增强,使其能够更好的满足需求。Dubbo SPI的优点如下 JDK标准的SPI会一次性实例化扩展点的所有实现。而Dubbo SPI能实现按需加载 Dubbo SPI增加了对扩展点Ioc和Aop的支持 Dubbo SPI的实现步骤如下 定义接口及其对应的实现类,接口上加@SPI注解,表明这是一个扩展类 在META-INF/services目录下创建以接口全路径命名的文件 文件内容为实现类的全路径名 在代码中通过ExtensionLoader加载具体的实现类 Dubbo SPI 扩展点的特性 自动包装 扩展类的构造函数是一个扩展点,则认为这个类是一个Wrapper类,即AOP 用例子演示一下 @SPIpublicinterfaceCar{voidgetBrand();} publicclassBenzCarimplementsCar{@OverridepublicvoidgetBrand(){System.out.println("benz");}} publicclassCarWrapperimplementsCar{privateCarcar;publicCarWrapper(Carcar){this.car=car;}@OverridepublicvoidgetBrand(){System.out.println("start");car.getBrand();System.out.println("end");}} org.apache.dubbo.aop.Car内容如下(resources\META-INF\services目录下) benz=org.apache.dubbo.aop.BenzCarorg.apache.dubbo.aop.CarWrapper 测试类 publicclassDubboSpiAopDemo{publicstaticvoidmain(String[]args){ExtensionLoader<Car>extensionLoader=ExtensionLoader.getExtensionLoader(Car.class);Carcar=extensionLoader.getExtension("benz");//start//benz//endcar.getBrand();}} BenzCar是一个扩展类,CarWrapper是一个包装类,当获取BenzCar的时候实际获取的是被CarWrapper包装后的对象,类似代理模式 自动加载 如果一个扩展类是另一个扩展类的成员变量,并且拥有set方法,框架会自动注入这个扩展点的实例,即IOC。先定义2个扩展点 org.apache.dubbo.ioc.Car(resources\META-INF\services目录下) benz=org.apache.dubbo.ioc.BenzCar org.apache.dubbo.ioc.Wheel(resources\META-INF\services目录下) benz=org.apache.dubbo.ioc.BenzWheel @SPIpublicinterfaceWheel{voidgetBrandByUrl();} publicclassBenzWheelimplementsWheel{@OverridepublicvoidgetBrandByUrl(){System.out.println("benzWheel");}} @SPIpublicinterfaceCar{voidgetBrandByUrl();} publicclassBenzCarimplementsCar{privateWheelwheel;publicvoidsetWheel(Wheelwheel){this.wheel=wheel;}@OverridepublicvoidgetBrandByUrl(){System.out.println("benzCar");wheel.getBrandByUrl();}} 测试demo publicclassDubboSpiIocDemo{publicstaticvoidmain(String[]args){ExtensionLoader<Car>extensionLoader=ExtensionLoader.getExtensionLoader(Car.class);Carcar=extensionLoader.getExtension("benz");car.getBrandByUrl();}} 我跑这个代码的时候直接报异常,看了一下官网才发现dubbo是可以注入接口的实现的,但不像spring那么智能,dubbo必须用URL(类似总线)来指定扩展类对应的实现类.。这就不得不提到@Adaptive注解了 自适应 使用@Adaptive注解,动态的通过URL中的参数来确定要使用哪个具体的实现类 @Documented@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE,ElementType.METHOD})public@interfaceAdaptive{String[]value()default{};} @SPIpublicinterfaceWheel{@Adaptive("wheel")voidgetBrandByUrl(URLurl);} publicclassBenzWheelimplementsWheel{@OverridepublicvoidgetBrandByUrl(URLurl){System.out.println("benzWheel");}} @SPIpublicinterfaceCar{voidgetBrandByUrl(URLurl);} publicclassBenzCarimplementsCar{//这个里面存的是代理对象privateWheelwheel;publicvoidsetWheel(Wheelwheel){this.wheel=wheel;}@OverridepublicvoidgetBrandByUrl(URLurl){System.out.println("benzCar");//代理类根据URL找到实现类,然后再调用实现类wheel.getBrandByUrl(url);}} publicclassDubboSpiIocDemo{publicstaticvoidmain(String[]args){ExtensionLoader<Car>extensionLoader=ExtensionLoader.getExtensionLoader(Car.class);Carcar=extensionLoader.getExtension("benz");Map<String,String>map=newHashMap<>();//指定wheel的实现类为benzmap.put("wheel","benz");URLurl=newURL("","",1,map);//benzCar//benzWheelcar.getBrandByUrl(url);}} 可以看到BenzCar对象成功注入了BenzWheel。BenzCar中其实注入的是BenzWheel的代码对象,这个代理对象会根据@Adaptive("wheel")获取到wheel,然后从url中找到key为wheel的值,这个值即为实现类对应的key。 上面的注释提到BenzCar里面注入的Wheel其实是一个代理对象(框架帮我们生成),在代理对象中根据url找到相应的实现类,然后调用实现类。 因为代理对象是框架在运行过程中帮我们生成的,没有文件可以查看,所以用Arthas来查看一下生成的代理类 curl-Ohttps://alibaba.github.io/arthas/arthas-boot.jarjava-jararthas-boot.jar#根据前面的序号选择进入的进程,然后执行下面的命令jadorg.apache.dubbo.adaptive.Wheel$Adaptive 生成的Wheel packageorg.apache.dubbo.adaptive;importorg.apache.dubbo.adaptive.Wheel;importorg.apache.dubbo.common.URL;importorg.apache.dubbo.common.extension.ExtensionLoader;publicclassWheel$AdaptiveimplementsWheel{publicvoidgetBrandByUrl(URLuRL){if(uRL==null){thrownewIllegalArgumentException("url==null");}URLuRL2=uRL;Stringstring=uRL2.getParameter("wheel");if(string==null){thrownewIllegalStateException(newStringBuffer().append("Failedtogetextension(org.apache.dubbo.adaptive.Wheel)namefromurl(").append(uRL2.toString()).append(")usekeys([wheel])").toString());}Wheelwheel=(Wheel)ExtensionLoader.getExtensionLoader(Wheel.class).getExtension(string);wheel.getBrandByUrl(uRL);}} @Adaptive可以标记在类上或者方法上 标记在类上:将该实现类直接作为默认实现,不再自动生成代码标记在方法上:通过参数动态获得实现类,比如上面的例子 用源码演示一下用在类上的@Adaptiv,Dubbo为自适应扩展点生成代码,如我们上面的WheelAdaptive类如下所示¨G30G∗∗@Adaptive可以标记在类上或者方法上∗∗标记在类上:将该实现类直接作为默认实现,不再自动生成代码标记在方法上:通过参数动态获得实现类,比如上面的例子用源码演示一下用在类上的@Adaptiv,Dubbo为自适应扩展点生成代码,如我们上面的WheelAdaptive,但生成的代码还需要编译才能生成class文件。我们可以用JavassistCompiler(默认的)或者JdkCompiler来编译(需要配置),这个小小的功能就用到了@Adaptive 如果想用JdkCompiler需要做如下配置 <dubbo:applicationcompiler="jdk"/> Compiler类图如下 @SPI("javassist")publicinterfaceCompiler{Class<?>compile(Stringcode,ClassLoaderclassLoader);} Compiler用@SPI指定了默认实现类为javassist 源码中获取Compiler调用了如下方法 org.apache.dubbo.common.compiler.Compilercompiler=ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); getAdaptiveExtension()会获取自适应扩展类,那么这个自适应扩展类是谁呢? 是AdaptiveCompiler,因为类上有@Adaptive注解 @AdaptivepublicclassAdaptiveCompilerimplementsCompiler{privatestaticvolatileStringDEFAULT_COMPILER;publicstaticvoidsetDefaultCompiler(Stringcompiler){DEFAULT_COMPILER=compiler;}/***获取对应的Compiler,并调用compile做编译*用户设置了compiler,就用设置了的,不然就用默认的*/@OverridepublicClass<?>compile(Stringcode,ClassLoaderclassLoader){Compilercompiler;ExtensionLoader<Compiler>loader=ExtensionLoader.getExtensionLoader(Compiler.class);Stringname=DEFAULT_COMPILER;//copyreferenceif(name!=null&&name.length()>0){//用用户设置的compiler=loader.getExtension(name);}else{//用默认的compiler=loader.getDefaultExtension();}returncompiler.compile(code,classLoader);}} 从compile方法可以看到,如果用户设置了编译方式,则用用户设置的,如果没有设置则用默认的,即JavassistCompiler 自动激活 使用@Activate注解,可以标记对应的扩展点默认被激活使用 @Documented@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE,ElementType.METHOD})public@interfaceActivate{//所属组,例如消费端,服务端String[]group()default{};//URL中包含属性名为value的键值对,过滤器才处于激活状态String[]value()default{};//指定执行顺序,before指定的过滤器在该过滤器之前执行@DeprecatedString[]before()default{};//指定执行顺序,after指定的过滤器在该过滤器之后执行@DeprecatedString[]after()default{};//指定执行顺序,值越小,越先执行intorder()default0;} 可以通过指定group或者value,在不同条件下获取自动激活的扩展点。before,after,order是用来排序的,感觉一个order参数就可以搞定排序的功能,所以官方把before,after标记为@Deprecated Dubbo Filter就是基于这个来实现的。Dubbo Filter是Dubbo可扩展性的一个体现,可以在调用过程中对请求进行进行增强 我写个demo演示一下这个自动激活是怎么工作的 @SPIpublicinterfaceMyFilter{voidfilter();} consumer组能激活这个filter @Activate(group={"consumer"})publicclassMyConsumerFilterimplementsMyFilter{@Overridepublicvoidfilter(){}} provider组能激活这个filter @Activate(group={"provider"})publicclassMyProviderFilterimplementsMyFilter{@Overridepublicvoidfilter(){}} consumer组和provide组都能激活这个filter @Activate(group={"consumer","provider"})publicclassMyLogFilterimplementsMyFilter{@Overridepublicvoidfilter(){}} consumer组和provide组都能激活这个filter,同时url中指定key的value为cache @Activate(group={"consumer","provider"},value="cache")publicclassMyCacheFilterimplementsMyFilter{@Overridepublicvoidfilter(){}} 测试类如下getActivateExtension有3个参数,依次为url, key, group publicclassActivateDemo{publicstaticvoidmain(String[]args){ExtensionLoader<MyFilter>extensionLoader=ExtensionLoader.getExtensionLoader(MyFilter.class);//url中没有参数URLurl=URL.valueOf("test://localhost");List<MyFilter>allFilterList=extensionLoader.getActivateExtension(url,"",null);/***org.apache.dubbo.activate.MyConsumerFilter@53e25b76*org.apache.dubbo.activate.MyProviderFilter@73a8dfcc*org.apache.dubbo.activate.MyLogFilter@ea30797**不指定组则所有的Filter都被激活*/allFilterList.forEach(item->System.out.println(item));System.out.println();List<MyFilter>consumerFilterList=extensionLoader.getActivateExtension(url,"","consumer");/***org.apache.dubbo.activate.MyConsumerFilter@53e25b76*org.apache.dubbo.activate.MyLogFilter@ea30797**指定consumer组,则只有consumer组的Filter被激活*/consumerFilterList.forEach(item->System.out.println(item));System.out.println();//url中有参数myfilterurl=URL.valueOf("test://localhost?myfilter=cache");List<MyFilter>customerFilter=extensionLoader.getActivateExtension(url,"myfilter","consumer");/***org.apache.dubbo.activate.MyConsumerFilter@53e25b76*org.apache.dubbo.activate.MyLogFilter@ea30797*org.apache.dubbo.activate.MyCacheFilter@aec6354**指定key在consumer组的基础上,MyCacheFilter被激活*/customerFilter.forEach(item->System.out.println(item));System.out.println();}} 总结一下就是,getActivateExtension不指定组就是激活所有的Filter,指定组则激活指定组的Filter。指定key则从Url中根据key取到对应的value,假设为cache,然后把@Activate注解中value=cache的Filter激活 即group用来筛选,value用来追加,Dubbo Filter就是靠这个属性激活不同的Filter的 ExtensionLoader的工作原理 ExtensionLoader是整个Dubbo SPI的主要实现类,有如下三个重要方法,搞懂这3个方法基本上就搞懂Dubbo SPI了。 加载扩展类的三种方法如下 getExtension(),获取普通扩展类 getAdaptiveExtension(),获取自适应扩展类 getActivateExtension(),获取自动激活的扩展类 getExtension()上面的例子中已经有了。自适应的特性上面已经演示过了,当获取Wheel的实现类是框架会调用getAdaptiveExtension()方法。 代码就不放了,这3个方法的执行过程还是比较简单的,如果你有看不懂的,可以看我给源码加的注释。 https://github.com/erlieStar/dubbo-analysis 理解了Dubbo SPI你应该就把Dubbo搞懂一半了,剩下就是一些服务导出,服务引入,服务调用的过程了 欢迎关注 有帮助?点赞!转发! 本文分享自微信公众号 - Java识堂(erlieStar)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

JUC包中的分而治之策略-为提高性能而生

一、前言 本次分享我们来共同探讨JUC包中一些有意思的类,包含AtomicLong & LongAdder,ThreadLocalRandom原理。 二、AtomicLong & LongAdder 2.1 AtomicLong 类 AtomicLong是JUC包提供的原子性操作类,其内部通过CAS保证了对计数的原子性更新操作。 大家可以翻看源码发现内部是通过UnSafe(rt.jar)这个类的CAs操作来保证对内部的计数器变量 long value进行原子性更新的,比如JDK8中: public final long incrementAndGet() { return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L; } 其中unsafe

优秀的个人博客,低调大师

如何提高阿里云上应用的可用性(一)

如今,开发并上线一款应用十分方便。因为云计算提供了从最基础的计算资源如服务器网络、数据库服务、中间件PaaS平台到各种应用支撑的云管理服务,同时开源社区的迅猛发展也提供了从数据库、缓存到应用全生命流程中各种必须的组件,所以越来越多的应用开发者可以把精力放在业务创新上。然而另一方面,应用所依赖的大量的IaaS、PaaS、Cloud API和开源组件无疑增加了应用的复杂度,如何保障云上应用的可用性,成了用户越来越关注的话题。 保障云上应用的可用性的第一步,是需要知道你的应用长什么样子,用了哪些云服务和开源组件,运行的环境是怎样的,服务之间的依赖如何,必须把它可视化出来,并且持续的跟踪变化。 阿里云最新发布的应用高可用服务,针对云原生应用架构复杂,变化快的特点,推出了架构感知的功能模块,可以非常方便的跟踪展现你在云端的应用架构,接下来我们一

优秀的个人博客,低调大师

写出高质量jq代码常记,提高代码性能

论jQuery和javascript性能的文章并不罕见。然而,本文我计划总结一些速度方面的技巧和我本人的一些建议,来提升你的jQuery和javascript代码。好的代码会带来速度的提升。快速渲染和响应意味着更好的用户体验。 首先,在脑子里牢牢记住jQuery就是javascript。这意味着我们应该采取相同的编码惯例,风格指南和最佳实践。 当你准备使用jQuery,我强烈建议你遵循下面这些指南: 一、DOM操作 1.缓存变量 DOM遍历是昂贵的,所以尽量将会重用的元素缓存。 // 糟糕 // 建议 2.繁重的操作中分离元素 如果你打算对DOM元素做大量操作(连续设置多个属性或css样式),建议首先分离元素然后在添加。 // 糟糕 // 建议 3.使用子查询缓存的父元素 正如前面所提到的,DOM遍历是一项昂贵的操作。典型做法是缓存父元素并在选择子元素时重用这些缓存元素。 // 糟糕 // 建议 (高效) 二、选择器 1.避免通用选择符 将通用选择符放到后代选择符中,性能非常糟糕。 // 糟糕 // 建议 2.避免隐式通用选择符 通用选择符有时是隐式的,不容易发现。 // 糟糕 // 建议 3.优化选择符 例如,Id选择符应该是唯一的,所以没有必要添加额外的选择符。 // 糟糕 // 建议 4.避免多个ID选择符 在此强调,ID 选择符应该是唯一的,不需要添加额外的选择符,更不需要多个后代ID选择符。 // 糟糕 // 建议 三、变量 1.避免全局变量 jQuery与javascript一样,一般来说,最好确保你的变量在函数作用域内。 // 糟糕 // 建议 2.使用匈牙利命名法 在变量前加$前缀,便于识别出jQuery对象。 // 糟糕 // 建议 - 在jQuery对象前加$前缀 3.使用 Var 链(单 Var 模式) 将多条var语句合并为一条语句,我建议将未赋值的变量放到后面。 四、函数调用 1.请使用’On’ 在新版jQuery中,更短的 on(“click”) 用来取代类似 click() 这样的函数。在之前的版本中 on() 就是 bind()。自从jQuery 1.7版本后,on() 附加事件处理程序的首选方法。然而,出于一致性考虑,你可以简单的全部使用 on()方法。 // 糟糕 // 建议 2.精简javascript 一般来说,最好尽可能合并函数。 // 糟糕 // 建议 3.链式操作 jQuery实现方法的链式操作是非常容易的。下面利用这一点。 // 糟糕 // 建议 五、技巧 1.维持代码的可读性 伴随着精简代码和使用链式的同时,可能带来代码的难以阅读。添加缩紧和换行能起到很好的效果。 // 糟糕 // 建议 2.选择短路求值 短路求值是一个从左到右求值的表达式,用 &&(逻辑与)或 || (逻辑或)操作符。 // 糟糕 // 建议 3.选择捷径 精简代码的其中一种方式是利用编码捷径。 // 糟糕 // 建议 4.熟记技巧 你可能对使用jQuery中的方法缺少经验,一定要查看的文档,可能会有一个更好或更快的方法来使用它。 // 糟糕 // 建议 (高效) 5.坚持最新版本 新版本通常更好:更轻量级,更高效。显然,你需要考虑你要支持的代码的兼容性。例如,2.0版本不支持ie 6/7/8。摒弃弃用方法关注每个新版本的废弃方法是非常重要的并尽量避免使用这些方法。

优秀的个人博客,低调大师

【Java入门提高篇】Day17 Java异常处理(下)

今天继续讲解java中的异常处理机制,主要介绍Exception家族的主要成员,自定义异常,以及异常处理的正确姿势。 Exception家族 一图胜千言,先来看一张图。 Exception这是一个父类,它有两个儿子,IOException和RuntimeException,每个儿子都很能生,所以它有着一堆的孙子,但其实,Exception家族还有一个大家伙,那就是Throwable,这是一个接口,看名字就知道意思,就是“可被抛出”嘛,它还有一个同父异母的哥哥,那就是Error,这家伙可厉害了,Error类一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢出等。catch语句里,不仅可以catch住Exception,还能catch住Error(什么?你真的打算catchError??程独秀同学,你先坐下。)一般情况下,是不能捕获Error的,对于这类错误,Java编译器不去检查他们。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和预防,遇到这样的错误,建议让程序终止。除非你有把握能正确处理,否则程独秀同学还是坐下吧(滑稽)。 UncheckedException和CheckedException 你也许会一脸懵逼,???,这是啥?异常也是分派别的,Unchecked Exception表示“未检查异常“,CheckedException自然就是”已检查异常“,派生于Error或者RuntimeException的异常称为unchecked异常,所有其他的异常成为checked异常。那问题来了,为啥要区分这两种异常? 我们可以再看看上面那个图,可以看出,RuntimeException和Error都是由程序内部引发的错误,比如上一篇里所说的空指针和算术异常。而CheckedException则大都是由外部因素导致的,如文件无法找到异常,这是虚拟机无法掌控的情况,当出现异常,虚拟机也只能一脸懵逼,不知道该如何是好,所以当有可能发生时,就必须要使用try..catch去捕获它,而对于Unchecked Exception时,大部分是由于代码引发的,所以只要代码写的足够完善,是不会抛出这样的异常的,所以也不强制要求捕获。 所以原因其实很简单,编译器将检查你是否为所有的已检查异常提供了异常处理机制,比如说我们使用Class.forName()来查找给定的字符串的class对象的时候,如果没有为这个方法提供异常处理,编译是无法通过的。已检查异常的意义就在于让你知道,这地方是有可能抛异常的,你要注意了,赶紧捕获了。 自定义异常 那么如何自定义一个异常呢?其实很简单,只需要继承Exception类就好了。看下面的栗子: public class MyException extends Exception { public MyException() { super(); } public MyException(String message) { super(message); } public MyException(String message, Throwable cause) { super(message, cause); } public MyException(Throwable cause) { super(cause); } protected MyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } } MyException继承了Exception类,重写了构造函数,并没有加自己的逻辑,只是调用了父类的方法。你看,自定义一个异常其实很简单吧。看到这你也许又疑惑了,这尼玛好像就是给Exception换了个名字,有啥用??? 别急,别急,你忘了吗,Exception不仅是可以捕获的,还是可以主动抛出的,所以当遇到某些特定的情况时,我们就可以主动抛出异常,然后在调用时去捕获它,获取异常信息,如果直接用Exception的话,那么捕获的时候,会把所有的异常,该捕获不该捕获的都一起捕获了,那么就没法区分哪些是我们主动抛出来的异常了,这样就无法对那些异常进行特殊处理了。 异常处理的正确姿势 接下来要简单介绍一个实际使用中常用的异常处理方法——异常链化处理。 在一些大型的,模块化的软件开发中,一旦一个地方发生异常,则如骨牌效应一样,将导致出现一连串的异常。假设B模块需要调用A模块的方法,如果A模块发生异常,则B也将不能完成而发生异常,但是B在抛出异常时,会将A的异常信息掩盖掉,这将使得异常的根源信息丢失。而使用异常的链化可以将多个模块的异常串联起来,使得异常信息不会丢失。 异常链化就是用一个异常对象为参数构造新的异常对象。新的异对象将包含先前异常的信息。这项技术主要是异常类的一个带Throwable参数的函数来实现的。这个当做参数的异常,我们叫他根源异常(cause)。如果你细心一点的话,会发现上面的栗子里也有一个叫做cause的东西,没错,说的其实就是它,在new一个新的异常时,将之前的异常信息传入构造函数即可。下面再用一个简单的栗子进行说明: public class Test { public static void main(String[] args) { System.out.println("请输入2个加数"); int result; try { result = add(); System.out.println("结果:"+result); } catch (Exception e){ e.printStackTrace(); } } /** * 执行加法计算 */ private static int add() throws Exception { int result; try { List<Integer> nums =getInputNumbers(); result = nums.get(0) + nums.get(1); }catch(InputMismatchException immExp){ //链化:以一个异常对象为参数构造新的异常对象。 throw new Exception("计算失败",immExp); } return result; } /** * 获取输入的整数 */ private static List<Integer> getInputNumbers() { List<Integer> nums = new ArrayList<>(); Scanner scan = new Scanner(System.in); try { int num1 = scan.nextInt(); int num2 = scan.nextInt(); nums.add(new Integer(num1)); nums.add(new Integer(num2)); }catch(InputMismatchException immExp){ throw immExp; }finally { scan.close(); } return nums; } } 输出如下: 请输入2个加数 d d java.lang.Exception: 计算失败 at com.frank.chapter17.Test.add(Test.java:35) at com.frank.chapter17.Test.main(Test.java:18) Caused by: java.util.InputMismatchException at java.util.Scanner.throwFor(Scanner.java:864) at java.util.Scanner.next(Scanner.java:1485) at java.util.Scanner.nextInt(Scanner.java:2117) at java.util.Scanner.nextInt(Scanner.java:2076) at com.frank.chapter17.Test.getInputNumbers(Test.java:47) at com.frank.chapter17.Test.add(Test.java:31) ... 1 more 可以看到,当输入的不是整数时,发生了异常,在getInputNumbers方法里没有处理这个异常,而是将它继续抛出,在add方法里捕获了异常之后,以该异常为构造参数,重新抛出了一个异常,从打印输出的信息可以看到,不仅仅有第二次抛出的异常信息,第一次的输出信息不匹配异常的详细信息也包含在了里面,衔接在Causedby之后,形成了一条异常链,这样可以方便我们更快的排查问题所在。 至此,异常就讲解完毕了,希望能给大家带来一些启发和思考,如果觉得还算ok的话,记得动动小手点推荐,让更多人可以看到,也欢迎关注我的博客,会持续更新的。如果有什么讲的不好的地方。。。emmmmmm,你倒是来打我呀(逃) 真正重要的东西,用眼睛是看不见的。

优秀的个人博客,低调大师

【Java入门提高篇】Day16 Java异常处理(上)

当当当当当当,各位看官,好久不见,甚是想念。 今天我们来聊聊Java里的一个小妖精,那就是异常。 什么是异常?什么是异常处理? 异常嘛,顾名思义就是不正常,(逃),是Java程序运行时,发生的预料之外的事情,它阻止了程序按照程序员的预期正常执行。 异常处理,应该说异常处理机制,就是专门用来制服这个小妖精的法宝。Java中的异常处理机制能让程序在异常发生时,按照代码的预先设定的异常处理逻辑,针对性地处理异常,让程序尽最大可能恢复正常并继续执行,且保持代码的清晰。 简而言之,Java异常处理就是能让我们主动迎击可能到来的异常,并将它们以圆润的方式处理掉。 还是先来看个小栗子,看看java里的异常长什么样。 public class Test { public static void main(String args[]){ int i = 0 / 0; System.out.println("i = " + i); } } 别慌别慌,不要看到红色提示就内心崩溃只想关掉IDE,来,抓紧我的手,带你看清“异常”这个磨人的小妖精的真面目(滑稽)。 代码里将0作为了分母,因此程序会发生算术异常,抛出一个异常后,如果没有任何处理,默认会终止程序,所以后面的打印内容并没有输出。在异常内容里,有说明异常类型为:java.lang.ArithmeticException,也就是算术异常,后面跟着的是异常原因: / by zero,也就是说异常出现的原因是将0作为了分母,而且后面还有堆栈信息,指出了异常抛出的位置是在com.frank.chapter16.main.Test.main这个包下,Test类的第11行(这个行数如果跟你想的不一样,不要在意,因为我的代码开始之前还有一些不可描述的说明信息),因为只有一次方法调用,所以没有很长的堆栈信息,看起来也很简洁明了。 所以你看,其实异常也没那么可怕吧,不仅给了异常原因,还告诉了你这个bug是出在第几行,所以好好利用它,可以帮助你写出更难以发现的bug,呸,说错了,可以帮助你更容易找到bug(手动滑稽)。 如果不希望抛出异常后程序就结束,而是希望它继续运行呢?那么就捕获它。 如何使用异常处理 我们来把上面那个栗子改改: public class Test { public static void main(String args[]){ try{ int i = 0 / 0; }catch (Exception e){ System.out.println("好像发生异常了,但是我不管,我还要继续运行"); } System.out.println("运行完毕!"); } } 输出如下: 好像发生异常了,但是我不管,我还要继续运行 运行完毕! 好的,很强势,现在即使抛出了异常,程序也继续运行了。异常就像是一头野兽,但你一旦捕获它,驯服它,就可以为你所用,为所欲为了。 try...catch...是常用的异常处理搭配,如果在try语句块中发生了异常,如果刚好这个异常被捕获到了,那么会直接跳到catch语句块中,执行catch语句中的代码,像上面的栗子里,因为对Exception类进行了捕获处理,所以当它的子类异常java.lang.ArithmeticException被抛出来的时候,也能捕获它。关于Exception类的结构层次关系,后面再做详细介绍。 还有另外一种搭配方式,那就是try...catch...finally,finally语句块比catch要强势的多,前面说了catch语句块必须要捕获到了特定的Exception才会执行里面的代码,如果catch的是ArithmeticException但是抛出的却是空指针异常,那就不会被捕获了,异常也就逃之夭夭了。这个时候,finally的优势就展示出来了,不管抛出什么样的异常,也不管是否抛出了异常,finally中的代码都会被执行。所以一般的用法是在finally语句块里释放掉那些需要被释放的资源,如socket连接,关闭io流,关闭数据库连接等等。也就是说一般在finally中收拾try中抛出的烂摊子,心疼一秒finally,果然能者多劳啊。 当然,try...finally这样的搭配也是ok的,需要注意的是,当try语句中发生了异常之后,在发生异常处之后的代码将不会再执行,而是跳到相应的catchu或者finally中去。 public class Test { public static void main(String args[]){ try{ int i = 0 / 0; }catch (NullPointerException e) { System.out.println("这里捕获空指针异常"); }catch (ArithmeticException e){ System.out.println("这里捕获算术异常"); }finally { System.out.println("这里是finally"); } System.out.println("运行完毕!"); } } 输出如下: 这里捕获算术异常 这里是finally 运行完毕! 在上面的代码中,catch语句块是可以同时使用多个的,第一个catch语句块捕获的是空指针异常,但由于抛出的是算术异常,所以没有捕获住,但被第二个catch捕获到了,所以第二个catch语句块中的代码执行了。异常匹配是按照从上到下的顺序进行匹配的,最后才执行finally中的代码块。关于try...catch...finally,还有一个很有趣的return问题,如果三个语句块里都有return,最终返回结果会是怎样呢?这里做了详细的说明,http://www.cnblogs.com/mfrank/p/7895660.html 有兴趣的话可以看一看。 绝大多数情况下,finally中的代码都是会被执行的,只有一种情况下,finally中的代码不会被执行,那就是在try语句块中结束掉了虚拟机(如:使用 System.exit(0); )。 关于异常,还有一个关键字需要介绍,那就是throw,使用throw可以主动抛出一个异常。看到这你也许会一脸懵逼,主动抛出???嫌异常不够多,凑热闹不嫌事大??别急别急,中间一定有什么误会,把刀放下,有话好好说。 throw关键字确实是用来抛出异常的,你可以这样使用: public class Test { public static void main(String args[]){ try{ throw new NullPointerException("听说你很闲,给你抛个异常。"); }catch (NullPointerException e) { System.out.println("这里捕获空指针异常,提示内容:" + e.getMessage()); e.printStackTrace(); } } } 输出如下: 这里捕获空指针异常,提示内容:听说你很闲,给你抛个异常。 java.lang.NullPointerException: 听说你很闲,给你抛个异常。 at com.frank.chapter16.main.Test.main(Test.java:11) 用throw关键字可以抛出任意类型的异常,当然,你想的话,还有抛Error,至于什么是Error,已经跟Exception的关系,将在下一篇里进行讲解。暂时不用深究。 在throw异常的时候,可以加上抛出异常的原因,这样可以更方便定位问题所在,当然,一般来说不会像栗子中这样使用的,这里只是为了简单起见。 到此为止,异常的上半篇已经讲解完毕,在这一篇里,说明了什么是异常,什么是异常处理,以及如何使用异常处理机制。相信大家对这个小妖精有了初步的认识,下一篇中,将会讲解Exception家族都有哪些成员,如何使用自定义异常,已经异常处理的实际使用中的正确姿势。欢迎大家继续关注,之后计划每周两篇以上的更新,如果有讲解遗漏或者不好的地方,欢迎大家及时指出,共同进步! 真正重要的东西,用眼睛是看不见的。

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。