Quantcast
Channel: CSDN博客推荐文章
Viewing all 35570 articles
Browse latest View live

Spring-AOP @AspectJ进阶之绑定代理对象

$
0
0

概述

使用this()或target()可绑定被代理对象实例,在通过类实例名绑定对象时,还依然具有原来连接点匹配的功能,只不过类名是通过增强方法中同名入参的类型间接决定罢了。

这里我们通过this()来了解对象绑定的用法:


实例

代码已托管到Github—> https://github.com/yangshangwei/SpringMaster

这里写图片描述

业务类

package com.xgj.aop.spring.advisor.aspectJAdvance.bindProxyObj;

import org.springframework.stereotype.Component;

/**
 * 
 * 
 * @ClassName: BussinessLogicService
 * 
 * @Description: @Component标注的bean
 * 
 * @author: Mr.Yang
 * 
 * @date: 2017年9月12日 下午12:11:28
 */

@Component
public class BussinessLogicService {

    public void doLogic() {
        System.out.println("BussinessLogicService doLogic executed ");
    }
}

切面

package com.xgj.aop.spring.advisor.aspectJAdvance.bindProxyObj;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/**
 * 
 * 
 * @ClassName: BindProxyObjAspect
 * 
 * @Description: 绑定代理对象
 *               使用this()或target()可绑定被代理对象实例,在通过类实例名绑定对象时,还依然具有原来连接点匹配的功能,
 *               只不过类名是通过增强方法中同名入参的类型间接决定罢了
 * 
 * @author: Mr.Yang
 * 
 * @date: 2017年9月12日 下午12:04:44
 */

@Aspect
public class BindProxyObjAspect {
    // (1)处通过②处查找出waiter对应的类型为BussinessLogicService,因而切点表达式
    // 为this(bussinessLogicService),当增强方法织入目标连接点时,增强方法通过bussinessLogicService
    // 入参可以引用到代理对象的实例。
    @Before("this(bussinessLogicService)")
    public void bindProxyObj(BussinessLogicService bussinessLogicService) { // (2)
        System.out.println("----bindProxyObj()----");
        System.out.println(bussinessLogicService.getClass().getName());
        System.out.println("----bindProxyObj()----");
    }

}

①处的切点表达式首先按类变量名查找②处增强方法的入参列表,进而获取类变量名对应的类为com.xgj.aop.spring.advisor.aspectJAdvance.bindProxyObj.BussinessLogicService,这样就知道了切点的定义为this(com.xgj.aop.spring.advisor.aspectJAdvance.bindProxyObj.BussinessLogicService),即所有代理对象为BussinessLogicService类的所有方法匹配该切点。②处的增强方法通过bussinessLogicService入参绑定目标对象。
可见BussinessLogicService的所有方法匹配①处的切点


配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">

<!-- (1)声明Context命名空间以及Schema文件   (2)扫描类包以及应用注解定义的bean -->
<context:component-scan base-package="com.xgj.aop.spring.advisor.aspectJAdvance.bindProxyObj"/>

<!-- 基于@AspectJ切面的驱动器 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>

<!-- 使用了@AspectJ注解的切面类 -->
<bean class="com.xgj.aop.spring.advisor.aspectJAdvance.bindProxyObj.BindProxyObjAspect"/>

</beans>

测试类

package com.xgj.aop.spring.advisor.aspectJAdvance.bindProxyObj;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BindProxyObjAspectTest {

    @Test
    public void test() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:com/xgj/aop/spring/advisor/aspectJAdvance/bindProxyObj/conf-bindProxyObj.xml");

        BussinessLogicService bussinessLogicService = ctx.getBean(
                "bussinessLogicService", BussinessLogicService.class);

        bussinessLogicService.doLogic();
    }
}

运行结果

2017-09-12 13:54:41,463  INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@292898f5: startup date [Tue Sep 12 13:54:41 BOT 2017]; root of context hierarchy
2017-09-12 13:54:41,557  INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/aop/spring/advisor/aspectJAdvance/bindProxyObj/conf-bindProxyObj.xml]
----bindProxyObj()----
com.xgj.aop.spring.advisor.aspectJAdvance.bindProxyObj.BussinessLogicService$$EnhancerBySpringCGLIB$$472f5f0d
----bindProxyObj()----
BussinessLogicService doLogic executed 

按相似的方法使用target()进行绑定。

作者:yangshangwei 发表于2017/9/13 1:55:53 原文链接
阅读:24 评论:0 查看评论

Spring-AOP @AspectJ进阶之绑定类注解对象

$
0
0

概述

@within()和@target()函数可以将目标类的注解对象绑定到增强方法中。


我们通过@within()演示注解绑定的操作

实例

代码已托管到Github—> https://github.com/yangshangwei/SpringMaster


这里写图片描述


注解(使用的是自定义注解,也可以使用框架提供的注解)

package com.xgj.aop.spring.advisor.aspectJAdvance.bindTypeAnnoObj;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//声明注解的保留期限
@Retention(RetentionPolicy.RUNTIME)
// 声明可以使用该注解的目标类型
@Target(ElementType.TYPE)
// 可以被javadoc此类的工具文档化
@Documented
public @interface Monitor { // 定义注解
    // 声明注解成员
    boolean value() default false;
}

业务类

package com.xgj.aop.spring.advisor.aspectJAdvance.bindTypeAnnoObj;

import org.springframework.stereotype.Component;

/**
 * 
 * 
 * @ClassName: Bussiness
 * 
 * @Description: bean使用@Component注解,
 * 
 *               同时标注了@@Monitor注解,所有Bussiness Bean匹配切点, 其@Monitor注解对象将绑定到增强方法中
 * 
 * @author: Mr.Yang
 * 
 * @date: 2017年9月12日 下午4:32:23
 */

@Component
@Monitor
public class Bussiness {

    public void dealBussinessOne() {
        System.out.println("dealBussinessOne executed");
    }

    public void dealBussinessTwo() {
        System.out.println("dealBussinessTwo executed");
    }
}

切面

package com.xgj.aop.spring.advisor.aspectJAdvance.bindTypeAnnoObj;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/**
 * 
 * 
 * @ClassName: BindTypeAnnoObjectAspect
 * 
 * @Description: @Aspect标注的切面
 * 
 *               (1)通过(2)处查找出m对应Monitor类型的注解, 因而真实的切点表达式为@within
 *               (Monitor),当增强方法织入目标 连接点时,增强方法通过m入参可以引用到连接点处的注解对象。
 * 
 * @author: Mr.Yang
 * 
 * @date: 2017年9月12日 下午4:27:55
 */

@Aspect
public class BindTypeAnnoObjectAspect {
    // (1)
    @Before("@within(m)")
    public void bindTypeAnno(Monitor m) { // (2)
        System.out.println("----bindTypeAnnoObject()----");
        System.out.println(m.getClass().getName());
        System.out.println("----bindTypeAnnoObject()----");
    }
}

(1)通过(2)处查找出m对应Monitor类型的注解, 因而真实的切点表达式为@within(Monitor),当增强方法织入目标 连接点时,增强方法通过m入参可以引用到连接点处的注解对象。


配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">

<!-- (1)声明Context命名空间以及Schema文件   (2)扫描类包以及应用注解定义的bean -->
<context:component-scan base-package="com.xgj.aop.spring.advisor.aspectJAdvance.bindTypeAnnoObj"/>

<!-- 基于@AspectJ切面的驱动器 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>

<!-- 使用了@AspectJ注解的切面类 -->
<bean class="com.xgj.aop.spring.advisor.aspectJAdvance.bindTypeAnnoObj.BindTypeAnnoObjectAspect"/>

</beans>

测试类

package com.xgj.aop.spring.advisor.aspectJAdvance.bindTypeAnnoObj;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BindTypeAnnoObjectAspectTest {
    @Test
    public void test() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:com/xgj/aop/spring/advisor/aspectJAdvance/bindTypeAnnoObj/conf-bindTypeAnnoObj.xml");

        Bussiness bussiness = ctx.getBean("bussiness", Bussiness.class);

        bussiness.dealBussinessOne();
        bussiness.dealBussinessTwo();
    }
}

输出结果

2017-09-12 16:58:15,464  INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@292898f5: startup date [Tue Sep 12 16:58:15 BOT 2017]; root of context hierarchy
2017-09-12 16:58:15,684  INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/aop/spring/advisor/aspectJAdvance/bindTypeAnnoObj/conf-bindTypeAnnoObj.xml]
----bindTypeAnnoObject()----
com.sun.proxy.$Proxy6
----bindTypeAnnoObject()----
dealBussinessOne executed
----bindTypeAnnoObject()----
com.sun.proxy.$Proxy6
----bindTypeAnnoObject()----
dealBussinessTwo executed

从输出信息中,com.sun.proxy.$Proxy6,即使用CGLib代理NaiveWaiter时,其类的注解Monitorable对象也被代理了.

作者:yangshangwei 发表于2017/9/13 5:00:21 原文链接
阅读:20 评论:0 查看评论

Spring-AOP @AspectJ进阶之绑定连接点方法的返回值

$
0
0

概述

后置增强中,可以通过returning绑定连接点方法的返回值


实例

代码已托管到Github—> https://github.com/yangshangwei/SpringMaster

这里写图片描述


业务类

package com.xgj.aop.spring.advisor.aspectJAdvance.bingReturnValue;

import org.springframework.stereotype.Component;

@Component
public class BussinessRet {

    public String dealBussiness() {
        System.out.println("dealBussiness executed");
        return "SUCCESS";
    }
}

切面

package com.xgj.aop.spring.advisor.aspectJAdvance.bingReturnValue;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;

/**
 * 
 * 
 * @ClassName: BindReturnValueAspect
 * 
 * @Description: @Aspect标注的切面,
 * 
 *               在后置增强中,可以通过returning绑定连接点方法的返回值
 * 
 *               (1)处和(2)处的名字必须相同,此外(2)处retMsg的类型必须和连接点方法的返回值类型匹配
 * 
 * @author: Mr.Yang
 * 
 * @date: 2017年9月12日 下午5:47:23
 */

@Aspect
public class BindReturnValueAspect {
    // (1)
    @AfterReturning(value = "target(com.xgj.aop.spring.advisor.aspectJAdvance.bingReturnValue.BussinessRet)", returning = "retMsg")
    public void crossCuttingCode(String retMsg) {// (2)
        System.out.println("----bindReturnValue----");
        System.out.println("retMsg:" + retMsg);
        System.out.println("----bindReturnValue----");
    }
}

(1)处和(2)处的名字必须相同,此外(2)处retMsg的类型必须和连接点方法的返回值类型匹配


配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">

<!-- (1)声明Context命名空间以及Schema文件   (2)扫描类包以及应用注解定义的bean -->
<context:component-scan base-package="com.xgj.aop.spring.advisor.aspectJAdvance.bingReturnValue"/>

<!-- 基于@AspectJ切面的驱动器 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>

<!-- 使用了@AspectJ注解的切面类 -->
<bean class="com.xgj.aop.spring.advisor.aspectJAdvance.bingReturnValue.BindReturnValueAspect"/>

</beans>

测试类

package com.xgj.aop.spring.advisor.aspectJAdvance.bingReturnValue;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BindReturnValueAspectTest {
    @Test
    public void test() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:com/xgj/aop/spring/advisor/aspectJAdvance/bingReturnValue/conf-bindReturnValue.xml");

        BussinessRet bussinessRet = ctx.getBean("bussinessRet",
                BussinessRet.class);
        bussinessRet.dealBussiness();
    }
}

输出结果

2017-09-12 17:53:10,730  INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3695de1a: startup date [Tue Sep 12 17:53:10 BOT 2017]; root of context hierarchy
2017-09-12 17:53:10,818  INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/aop/spring/advisor/aspectJAdvance/bingReturnValue/conf-bindReturnValue.xml]
dealBussiness executed
----bindReturnValue----
retMsg:SUCCESS
----bindReturnValue----
作者:yangshangwei 发表于2017/9/13 5:59:54 原文链接
阅读:21 评论:0 查看评论

Spring-AOP @AspectJ进阶之绑定抛出的异常

$
0
0

概述

和通过切点函数绑定连接点信息不同,连接点抛出的异常必须使用AfterThrowing注解的throwing成员进行绑定


实例

代码已托管到Github—> https://github.com/yangshangwei/SpringMaster

这里写图片描述

业务类

package com.xgj.aop.spring.advisor.aspectJAdvance.bindException;

import org.springframework.stereotype.Component;

@Component
public class BussinessException {

    public void dealBussiness(String bussinessName) {
        System.out.println("dealBussiness executed");
        // just a demo code ,in fact it's not cautious
        if (bussinessName != null && "bug".equals(bussinessName))
            throw new IllegalArgumentException("iae Exception");
        else
            throw new RuntimeException("re Exception");
    }
}

切面

package com.xgj.aop.spring.advisor.aspectJAdvance.bindException;

import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;

/**
 * 
 * 
 * @ClassName: BindReturnValueAspect
 * 
 * @Description: @Aspect标注的切面,
 *               和通过切点函数绑定连接点信息不同,连接点抛出的异常必须使用AfterThrowing注解的throwing成员进行绑定
 * 
 *               (1)处throwing指定的异常名和(2)处入参的异常名相同,这个异常增强只在连接点抛出的异常instanceof
 *               IllegalArgumentException才匹配,增强方法通过iae参数可以访问抛出的异常对象。
 * 
 * @author: Mr.Yang
 * 
 * @date: 2017年9月12日 下午5:47:23
 */

@Aspect
public class BindExceptionAspect {
    // (1)
    @AfterThrowing(value = "target(com.xgj.aop.spring.advisor.aspectJAdvance.bindException.BussinessException)", throwing = "iae")
    public void crossCuttingCode(IllegalArgumentException iae) {// (2)
        System.out.println("----bindException()----");
        System.out.println("exception:" + iae.getMessage());
        System.out.println("----bindException()----");
    }
}

(1)处throwing指定的异常名和(2)处入参的异常名相同,这个异常增强只在连接点抛出的异常instanceof IllegalArgumentException才匹配,增强方法通过iae参数可以访问抛出的异常对象。


配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">

<!-- (1)声明Context命名空间以及Schema文件   (2)扫描类包以及应用注解定义的bean -->
<context:component-scan base-package="com.xgj.aop.spring.advisor.aspectJAdvance.bindException"/>

<!-- 基于@AspectJ切面的驱动器 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>

<!-- 使用了@AspectJ注解的切面类 -->
<bean class="com.xgj.aop.spring.advisor.aspectJAdvance.bindException.BindExceptionAspect"/>

</beans>

单元测试

package com.xgj.aop.spring.advisor.aspectJAdvance.bindException;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BindExceptionAspectTest {
    @Test
    public void test() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:com/xgj/aop/spring/advisor/aspectJAdvance/bindException/conf-bindException.xml");

        ctx.getBean("bussinessException", BussinessException.class)
                .dealBussiness("bug");
    }
}

输出结果

2017-09-12 20:26:25,344  INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3695de1a: startup date [Tue Sep 12 20:26:25 BOT 2017]; root of context hierarchy
2017-09-12 20:26:25,458  INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/aop/spring/advisor/aspectJAdvance/bindException/conf-bindException.xml]
dealBussiness executed
----bindException()----
exception:iae Exception
----bindException()----

可见当sdealBussiness(“bug”)抛出异常后,异常增强起效,处理完成后,再向外抛出IllegalArgumentException。如果将①处的代码调整为dealBussiness(“bug2”)后,再运行代码,将只看到异常输出的信息,异常增强没有任何动作,这是因为RuntimeException 不按类型匹配于 IllegalArgumentException,切点不匹配。

这里写图片描述


总结

通过切点复合运算,你可以定义出各种复杂的切点,使切点表达式的能力进一步提升。

你可以直接使用切点复合运算符对切点函数进行运算,也可以通过切点名引用其它命名切点。

当对同一个连接点织入多个增强时,你必须考虑让切面类实现Ordered接口,此外还必须合理计划同一个切面类中增强方法的声明顺序,因为这些信息都会影响到增强的织入顺序。

在@AspectJ的切点表达式中,大多数的切点函数都可以绑定连接点方法的入参,以便增强方法访问连接点信息。

此外,你也可以简单地将增强方法的第一个入参定义为JoinPoint访问连接点的上下文。

作者:yangshangwei 发表于2017/9/13 8:35:19 原文链接
阅读:19 评论:0 查看评论

安卓实现扫一扫识别数字

$
0
0

公司业务需求,需要做手机号码的识别。所以有了此篇文章,现在就将实现过程分享给大家。

1.准备工作

  • 首先实现识别数字等字符,我们要知道需要采用OCR (Optical Character Recognition,光学字符识别)来实现。而tesseract是非常不错的开源OCR工具,但是要在Android中直接使用可能要费点功夫。不过不用担心,tess-two拯救了我们。

  • 其次是扫一扫识别,那么很快联想到的就是常见的二维码扫描这类的项目。通过扫一扫实时拿到图像,来做识别。

  • 接下来在Github上找到了QrCodeScanner项目,作者通过一定的优化,使得识别的效率有所提升。那么我们用它来扫描数字,也会有效率上的提升。

2.实现细节

1.首先是tess-two的用法。

app下的build.gradle的配置如下

android {

    defaultConfig {
       ....

        ndk {
            abiFilters 'armeabi' //自行选择添加
        }
    }

}


dependencies {
    compile 'com.rmtheis:tess-two:8.0.0'
}

识别方法:

public String detectText(Bitmap bitmap) {

        TessBaseAPI tessBaseAPI = new TessBaseAPI();
        String path = ""; //训练数据路径

        tessBaseAPI.setDebug(true);
        tessBaseAPI.init(path, "eng"); //eng为识别语言
        tessBaseAPI.setVariable(TessBaseAPI.VAR_CHAR_WHITELIST, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); // 识别白名单
        tessBaseAPI.setVariable(TessBaseAPI.VAR_CHAR_BLACKLIST, "!@#$%^&*()_+=-[]}{;:'\"\\|~`,./<>?"); // 识别黑名单
        tessBaseAPI.setPageSegMode(TessBaseAPI.PageSegMode.PSM_AUTO_OSD);//设置识别模式

        tessBaseAPI.setImage(bitmap); //设置需要识别图片的bitmap
        String inspection = tessBaseAPI.getHOCRText(0);
        tessBaseAPI.end();
        return inspection ;
    }

训练数据可以在tessdata下载,里面包含各种语言。当然你自己也可以训练它,有兴趣的可以学习一下相关内容。

2.从tess-two的用法可以知道,我们最终需要的是识别图片的Bitmap。在扫码项目中我们找到在DecodeHandler类的decode方法中,我们会得到一个PlanarYUVLuminanceSource类的实例。在使用HybridBinarizer算法解析数据源,最终采用MultiFormatReader解析图像出结果。代码大致如下:

    Result rawResult = null;
    MultiFormatReader mMultiFormatReade = new MultiFormatReader();
    try {
        PlanarYUVLuminanceSource source =
                    new PlanarYUVLuminanceSource(```, false);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
        rawResult = mMultiFormatReader.decode(bitmap, mHints);
    } catch (ReaderException ignored) {

    } finally {
        mMultiFormatReader.reset();
    }

看完后懵逼了,没有Bitmap。经过一番查找,找到了在旧版的zxing中PlanarYUVLuminanceSource类有renderCroppedGreyscaleBitmap方法,不知为何去除了。。。

3.之后修改了一些相机的参数信息,适配了部分设备的预览效果。基本的页面修改了一下。这里就不赘述了。

走一波,如下效果:

可以发现除了数字以外,它将中文识别为了字母。其实问题首先是我们使用了英文的训练数据,同时白名单设置了a~z的字母。当然你也不能将字母设置为黑名单,那样只会让识别不出的字符识别为乱七八糟的数字。

这里我给出的建议是利用正则去筛选,这样你可以识别你想要的各种格式数据。我这里只是做了手机号的简单识别,大家可以举一反三去处理。

    public static String getTelNum(String sParam){
        if(TextUtils.isEmpty(sParam)){
            return "";
        }

        Pattern pattern = Pattern.compile("(1|861)(3|5|7|8)\\d{9}$*");
        Matcher matcher = pattern.matcher(sParam);
        StringBuilder bf = new StringBuilder();
        while (matcher.find()) {
            bf.append(matcher.group()).append(",");
        }
        int len = bf.length();
        if (len > 0) {
            bf.deleteCharAt(len - 1);
        }
        return bf.toString();
    }

修改后如下:(同时支持多个号码)

当然本项目也保留了扫码功能(可在DecodeHandler中自己添加条码格式):

细心的同学可以从图中看到扫描框的大小都不一样,这里我是改成了可以手动调节大小的扫描框。毕竟扫码模式下,框大一点还是比较好识别(将二维码放入框中有时就费时间)。扫数字这些文字时,框小一点会好识别。具体可以下载自行体验。

最后我将代码已经上传至Github:Tesseract-OCR-Scanner大家多点赞点星,感谢!!

3.参考

作者:qq_17766199 发表于2017/9/13 10:59:09 原文链接
阅读:0 评论:0 查看评论

Spring Cloud中负载均衡器概览

$
0
0

在上篇文章中(RestTemplate的逆袭之路,从发送请求到负载均衡)我们完整的分析了RestTemplate的工作过程,在分析的过程中,我们遇到过一个ILoadBalancer接口,这个接口中有一个chooseServer方法是我们选择服务实例的方法,这个也是整个负载均衡中最最核心的部分,那么它到底是采用了什么样的策略从服务提供者列表中选出了一个服务供服务消费者去调用的?这是我们今天要讨论的问题,本文我主要是想基于互联网上公开的资料,来对Spring Cloud中提供的负载均衡器做一个简明扼要的介绍。


本文是Spring Cloud系列的第八篇文章,了解前七篇文章内容有助于更好的理解本文:

1.使用Spring Cloud搭建服务注册中心
2.使用Spring Cloud搭建高可用服务注册中心
3.Spring Cloud中服务的发现与消费
4.Eureka中的核心概念
5.什么是客户端负载均衡
6.Spring RestTemplate中几种常见的请求方式
7.RestTemplate的逆袭之路,从发送请求到负载均衡


负载均衡器

首先我们来看一张上篇文章中的旧图:

这里写图片描述

这是ILoadBalancer接口的一张类关系图,我们就从这张图里看起吧。

AbstractLoadBalancer

AbstractLoadBalancer类的定义如下:

public abstract class AbstractLoadBalancer implements ILoadBalancer {

    public enum ServerGroup{
        ALL,
        STATUS_UP,
        STATUS_NOT_UP        
    }
    public Server chooseServer() {
        return chooseServer(null);
    }
    public abstract List<Server> getServerList(ServerGroup serverGroup);
    public abstract LoadBalancerStats getLoadBalancerStats();    
}

关于这个类我说以下几点:
1. AbstractLoadBalancer实现了ILoadBalancer接口,但它是一个抽象类,它里边定义了一个关于服务实例的分组枚举类,包含了三种类型的服务:ALL表示所有服务,STATUS_UP表示正常运行的服务,STATUS_NOT_UP表示下线的服务。
2. chooseServer方法毫无疑问是用来选取一个服务实例,但是要怎么选这里并没有说,我们以后在它的实现类里边寻找选取策略。
3. getServerList方法用来获取某一个分组中所有的的服务实例。
4. getLoadBalancerStats方法用来获取LoadBalancerStats对象,LoadBalancerStats对象中保存了每一个服务的所有细节信息。

BaseLoadBalancer

BaseLoadBalancer是AbstractLoadBalancer的一个实现类,源码比较长我就不贴出来的,我们在这里主要来说说BaseLoadBalancer提供了哪些功能:

1. 首先这个类中有两个List集合中放的Server对象,一个List集合用来保存所有的服务实例,还有一个List集合用来保存当前有效的服务实例。
2. BaseLoadBalancer中定义了一个IPingStrategy,用来描述服务检查策略,IPingStrategy默认实现采用了SerialPingStrategy实现,SerialPingStrategy中的pingServers方法就是遍历所有的服务实例,一个一个发送请求,查看这些服务实例是否还有效,如果网络环境不好的话,这种检查策略效率会很低,如果我们想自定义检查策略的话,可以重写SerialPingStrategy的pingServers方法。
3. 在BaseLoadBalancer的chooseServer方法中(负载均衡的核心方法),我们发现最终调用了IRule中的choose方法来找到一个具体的服务实例,IRule是一个接口,在BaseLoadBalancer它的默认实现是RoundRobinRule类,RoundRobinRule类中采用了最常用的线性负载均衡规则,也就是所有有效的服务端轮流调用。
4. 在BaseLoadBalancer的构造方法中会启动一个PingTask,这个PingTask用来检查Server是否有效,PingTask的默认执行时间间隔为10秒。
5. markServerDown方法用来标记一个服务是否有效,标记方式为调用Server对象的setAlive方法设置isAliveFlag属性为false。
6. getReachableServers方法用来获取所有有效的服务实例列表。
7. getAllServers方法用来获取所有服务的实例列表。
8. addServers方法表示向负载均衡器中添加一个新的服务实例列表。

BaseLoadBalancer的功能大概就这么多。

DynamicServerListLoadBalancer

DynamicServerListLoadBalancer是BaseLoadBalancer的一个子类,在DynamicServerListLoadBalancer中对基础负载均衡器的功能做了进一步的扩展,我们来看看。

1. 首先DynamicServerListLoadBalancer类一开始就声明了一个变量serverListImpl,serverListImpl变量的类型是一个ServerList<T extends Server>,这里的泛型得是Server的子类,ServerList是一个接口,里边定义了两个方法:一个getInitialListOfServers用来获取初始化的服务实例清单;另一个getUpdatedListOfServers用于获取更新的服务实例清单。
2. ServerList接口有很多实现类,DynamicServerListLoadBalancer默认使用了DomainExtractingServerList类作为ServerList的实现,但是在DomainExtractingServerList的构造方法中又传入了DiscoveryEnabledNIWSServerList对象,查看源码发现最终两个清单的获取方式是由DiscoveryEnabledNIWSServerList类来提供的。
3. DomainExtractingServerList类中的obtainServersViaDiscovery方法是用来发现服务实例并获取的,obtainServersViaDiscovery方法的主要逻辑是这样:首先依靠EurekaClient从服务注册中心获取到具体的服务实例InstanceInfo列表,然后对这个列表进行遍历,将状态为UP的实例转换成DiscoveryEnabledServer对象并放到一个集合中,最后将这个集合返回。
4. DynamicServerListLoadBalancer中还定义了一个ServerListUpdater.UpdateAction类型的服务更新器,Spring Cloud提供了两种服务更新策略:一种是PollingServerListUpdater,表示定时更新;另一种是EurekaNotificationServerListUpdater表示由Eureka的事件监听来驱动服务列表的更新操作,默认的实现策略是第一种,即定时更新,定时的方式很简单,创建Runnable,调用DynamicServerListLoadBalancer中updateAction对象的doUpdate方法,Runnable延迟启动时间为1秒,重复周期为30秒。
5. 在更新服务清单的时候,调用了我们在第一点提到的getUpdatedListOfServers方法,拿到实例清单之后,又调用了一个过滤器中的方法进行过滤。过滤器的类型有好几种,默认是DefaultNIWSServerListFilter,这是一个继承自ZoneAffinityServerListFilter的过滤器,具有区域感知功能。即它会对服务提供者所处的Zone和服务消费者所处的Zone进行比较,过滤掉哪些不是同一个区域的实例。

综上,DynamicServerListLoadBalancer主要是实现了服务实例清单在运行期间的动态更新能力,同时提供了对服务实例清单的过滤功能。

ZoneAwareLoadBalancer

ZoneAwareLoadBalancer是DynamicServerListLoadBalancer的子类,ZoneAwareLoadBalancer的出现主要是为了弥补DynamicServerListLoadBalancer的不足。由于DynamicServerListLoadBalancer中并没有重写chooseServer方法,所以DynamicServerListLoadBalancer中负责均衡的策略依然是我们在BaseLoadBalancer中分析出来的线性轮询策略,这种策略不具备区域感知功能,这样当需要跨区域调用时,可能会产生高延迟。ZoneAwareLoadBalancer重写了setServerListForZones方法,该方法在其父类中的功能主要是根据区域Zone分组的实例列表,为负载均衡器中的LoadBalancerStats对象创建ZoneStats并存入集合中,ZoneStats是一个用来存储每个Zone的状态和统计信息。重写之后的setServerListForZones方法主要做了两件事:一件是调用getLoadBalancer方法来创建负载均衡器,同时创建服务选择策略;另一件是对Zone区域中的实例清单进行检查,如果对应的Zone下已经没有实例了,则将Zone区域的实例列表清空,防止节点选择时出现异常。

OK,以上就是我们对负载均衡器的一个简单介绍,下一篇文章我们将继续介绍负载均衡策略

更多JavaEE资料请关注公众号:

这里写图片描述

以上。。

作者:u012702547 发表于2017/9/13 8:34:20 原文链接
阅读:150 评论:0 查看评论

Angular最新教程-第三节在谷歌浏览器中调试Angular

$
0
0

这节课,我们将要了解的是如何使用VS Code安装Chrome扩展对Angular进行调试。

步骤一:确保环境

  • 确保将谷歌浏览器安装在其默认位置。
    这里写图片描述
  • 确保在VS Code中安装了插件:Debugger for Chrome
    这里写图片描述
  • 使用npm在全局范围内安装了angular/cli ,并且版本大于1.3
    (我们第一节课中就讲过这个了,你也可以在确认一下。Windows用户快捷键win+r ,输入 ng -v)
    这里写图片描述
  • 使用angular/cli创建新的项目
    ng new oniplan-ng
    第一节课中我们已经创建过了。所以我们只要cd到项目目录,然后执行code . 就可以了。
    不喜欢使用命名行的,可以先运行VS Code,然后左上角文件-打开文件夹 选中项目文件夹。
    这里写图片描述

步骤二:配置 launch.json 文件

点击调制-设置-Chrome,如图:
这里写图片描述
使用以下配置替换掉launch.json文件的内容

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "ng serve",
      "type": "chrome",
      "request": "launch",
      "url": "http://localhost:4200/#",
      "webRoot": "${workspaceRoot}"
    },
    {
      "name": "ng test",
      "type": "chrome",
      "request": "launch",
      "url": "http://localhost:9876/debug.html",
      "webRoot": "${workspaceRoot}"
    },
    {
      "name": "ng e2e",
      "type": "node",
      "request": "launch",
      "program": "${workspaceRoot}/node_modules/protractor/bin/protractor",
      "protocol": "inspector",
      "args": ["${workspaceRoot}/protractor.conf.js"]
    }      
  ]
}

步骤三:开始调试

  • 在src/app/app.component.ts中设置断点。
    这里写图片描述

  • 在根文件夹上打开终端并使用angular/cli开启本地服务
    在状态栏中选择查看-集成终端(也可以使用快捷键ctrl+` 数字1左边那个键,不是引号)
    这里写图片描述
    执行npm start
    这里写图片描述

  • 然后进入调试界面,点击绿色按钮,启动调试。
    这里写图片描述
    会自动打开谷歌浏览器窗口,然后再按F5刷新一下,就能进入断点了。
    这里写图片描述
    调试的时候的快捷键F10啥的都可以使用。

步骤四:调试单元测试

  • 在单元测试src/app/app.component.spec.ts中设置断点
    这里写图片描述
    如果是新项目的话,设置断点的时候,可能会提示,已自动忽略断点,什么源文件映射啥的,可以不理会。下一步执行就会自动生成源文件。

  • 在终端中执行 npm run test
    刚才已经运行着npm start了,可以使用快捷键ctrl+c停掉当前的服务,再执行npm run test
    这里写图片描述

  • 然后进入调试界面,选择ng test ,点击绿色按钮,启动单元测试调试。
    这里写图片描述

步骤五:调试End-to-end测试

接下来我们简要的说明一下端对端测试的调试。
- 首先先停掉终端里的服务ctrl+c,然后执行 ng server。
- 在调试里面选择ng e2e,点击绿色按钮。
这里写图片描述
- 刷新一下,查看控制台
这里写图片描述

英文原文链接:https://github.com/Microsoft/vscode-recipes/tree/master/Angular-CLI

这节课的内容就到这里结束了。
感谢您的阅读。
我是莽夫,希望你开心。
如果你觉得本文对你有帮助,请扫描文末二维码,支持博主原创。
希望大家关注我的个人公众号ionic_
这里写图片描述

作者:onil_chen 发表于2017/9/13 11:56:52 原文链接
阅读:28 评论:0 查看评论

os_sem.c

$
0
0

  定位到uCOS-II/Source/os_sem.c,该文件是信号量的相关操作函数。

  信号量适用于资源保护的场合,它和互斥型信号量Mutex一样,用于保护着某个共享资源,二者的差别是:Mutex是二值的(0/1),其初始值为1,某任务要操作共享资源,需要获取信号量,获取后信号量计数器为0,那么下一个任务来获取该Mutex将获取不到;而信号量Semaphore的初始值(信号量计数器)可以为大于1的数,假设为3,,那么任务A要使用该资源时,获取信号量后信号量值为2,同理,被任务B获取后信号量值为1,任务C获取后信号量值为0,那么再下一个任务来获取时就将获取不到信号量了。

1. 非阻塞的获取一个信号量函数OSSemAccept()

  OSSemAccept()用于检测信号量是否可用,若资源不可用,调用此函数不会使得所在任务被挂起。

#if OS_SEM_ACCEPT_EN > 0u               //定义OSSemAccept()函数使能宏
INT16U  OSSemAccept (OS_EVENT *pevent)  //pevent指向需要保护的共享资源的信号量
{
    INT16U     cnt;
#if OS_CRITICAL_METHOD == 3u            //信号量中的值
    OS_CPU_SR  cpu_sr = 0u;
#endif

#if OS_ARG_CHK_EN > 0u                  //参数检测使能宏
    if (pevent == (OS_EVENT *)0) { 
        return (0u);
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { 
        return (0u);
    }
    OS_ENTER_CRITICAL();
    cnt = pevent->OSEventCnt;           //取出信号量的值
    if (cnt > 0u) {                     //大于0表示信号量还可以使用
        pevent->OSEventCnt--;           //自减表示使用了该信号量
    }
    OS_EXIT_CRITICAL();
    return (cnt);                       //返回值cnt大于0表示获取信号量成功,反之获取失败
}
#endif

2. 创建信号量函数OSSemCreate()

  OSSemCreate()用于创建并初始化一个信号量。

OS_EVENT  *OSSemCreate (INT16U cnt)     //cnt为信号量的初始值
{
    OS_EVENT  *pevent;
#if OS_CRITICAL_METHOD == 3u
    OS_CPU_SR  cpu_sr = 0u;
#endif

#ifdef OS_SAFETY_CRITICAL_IEC61508      //系统安全相关,不管
    if (OSSafetyCriticalStartFlag == OS_TRUE) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return ((OS_EVENT *)0);
    }
#endif

    if (OSIntNesting > 0u) { 
        return ((OS_EVENT *)0);
    }
    OS_ENTER_CRITICAL();
    pevent = OSEventFreeList;               //pevent指向空闲事件链表
    if (OSEventFreeList != (OS_EVENT *)0) { //如果有空余事件管理块 
        //空闲事件控制链表指向下一个空闲事件控制块  
        OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
    }
    OS_EXIT_CRITICAL();
    if (pevent != (OS_EVENT *)0) {  //解引用前先判断指针是否有效
        //初始化该event(信号量)的相关参数
        pevent->OSEventType    = OS_EVENT_TYPE_SEM;
        pevent->OSEventCnt     = cnt;
        pevent->OSEventPtr     = (void *)0; //OSEventPtr初始值为空,等到某任务获取该event后,OSEventPtr指向该任务的TCB
#if OS_EVENT_NAME_EN > 0u
        pevent->OSEventName    = (INT8U *)(void *)"?";
#endif
        OS_EventWaitListInit(pevent);       //定义在os_core.c中,实现清空该event的等待列表OSEventTbl和等待组OSEventGrp
    }
    return (pevent);
}

3. 删除信号量函数OSSemDel()

  OSSemDel()函数用于删除一个信号量,因为可能存在其他多个任务在等待这个信号量,所以注意,删除之前对这些等待任务执行相关操作。

#if OS_SEM_DEL_EN > 0u                  //允许定义OSSemDel()函数
OS_EVENT  *OSSemDel (OS_EVENT  *pevent, //指向待删除的信号量
                     INT8U      opt,    //删除选项:OS_DEL_NO_PEND/OS_DEL_ALWAYS
                     INT8U     *perr)   //输出型参数,用于存放出错信息
{
    BOOLEAN    tasks_waiting;           //Bool型变量,用于表示是否有任务在等待该事件
    OS_EVENT  *pevent_return;
#if OS_CRITICAL_METHOD == 3u
    OS_CPU_SR  cpu_sr = 0u;
#endif

#ifdef OS_SAFETY_CRITICAL
    if (perr == (INT8U *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return ((OS_EVENT *)0);
    }
#endif

#if OS_ARG_CHK_EN > 0u
    if (pevent == (OS_EVENT *)0) {
        *perr = OS_ERR_PEVENT_NULL;
        return (pevent);
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {
        *perr = OS_ERR_EVENT_TYPE;
        return (pevent);
    }
    if (OSIntNesting > 0u) {
        *perr = OS_ERR_DEL_ISR; 
        return (pevent);
    }
    OS_ENTER_CRITICAL();

    //OSEventGrp不等于0表示当前有任务在等待该事件
    if (pevent->OSEventGrp != 0u) {
        tasks_waiting = OS_TRUE;    //Yes
    } else {
        tasks_waiting = OS_FALSE;   //No
    }

    switch (opt) {
        //opt等于OS_DEL_NO_PEND表示若有任务在等待就不删除
        //opt等于OS_DEL_ALWAYS表示不管有无任务等待都删除该event
        case OS_DEL_NO_PEND:
             if (tasks_waiting == OS_FALSE) {   //无任务等待
#if OS_EVENT_NAME_EN > 0u
                 pevent->OSEventName    = (INT8U *)(void *)"?";
#endif
                 pevent->OSEventType    = OS_EVENT_TYPE_UNUSED;  //设置该event为未使用状态
                 pevent->OSEventPtr     = OSEventFreeList;       //信号量对应的指针设置为空闲块链接表  
                 pevent->OSEventCnt     = 0u;                    //信号量初始值为0
                 OSEventFreeList        = pevent;                //空线块链接表等于当前事件指针  
                 OS_EXIT_CRITICAL();
                 *perr                  = OS_ERR_NONE;
                 pevent_return          = (OS_EVENT *)0;         //返回NULL
             } else {                   //有任务在等待
                 OS_EXIT_CRITICAL();
                 *perr                  = OS_ERR_TASK_WAITING;   //返回错误(有一个或一个以上的任务在等待信号量) 
                 pevent_return          = pevent;
             }
             break;

        case OS_DEL_ALWAYS:
             //OSEventGrp不为0表示有任务在等待该信号量,那么挨个清除这个在等待的任务的等待标记,
             //即将它们设置Rdy状态
             while (pevent->OSEventGrp != 0u) {
                 (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_ABORT);
             }
#if OS_EVENT_NAME_EN > 0u
             pevent->OSEventName    = (INT8U *)(void *)"?";
#endif
             pevent->OSEventType    = OS_EVENT_TYPE_UNUSED;
             pevent->OSEventPtr     = OSEventFreeList;
             pevent->OSEventCnt     = 0u;
             OSEventFreeList        = pevent;
             OS_EXIT_CRITICAL();

             //若系统正在运行则进行调度
             if (tasks_waiting == OS_TRUE) {
                 OS_Sched(); 
             }
             *perr                  = OS_ERR_NONE;
             pevent_return          = (OS_EVENT *)0; 
             break;

        default:  //opt输入有误
             OS_EXIT_CRITICAL();
             *perr                  = OS_ERR_INVALID_OPT;
             pevent_return          = pevent;
             break;
    }
    return (pevent_return);
}
#endif

4. 阻塞的获取一个信号量函数OSSemPend ()

  任务在试图取得共享资源的使用权时,通过此函数去获取保护该资源的信号量,若成功获取信号量(获取前的信号量计数器大于0)则返回,反之陷入等待状态,直到有其他任务释放了该信号量,或者超时,或者该信号量被其他任务删除而被唤醒。

void  OSSemPend (OS_EVENT  *pevent,     //指向信号量指针
                 INT32U     timeout,    //该函数在获取不到信号时的阻塞超时时间,单位为systick
                 INT8U     *perr)       //输入型参数,表示出错原因
{
#if OS_CRITICAL_METHOD == 3u 
    OS_CPU_SR  cpu_sr = 0u;
#endif

#ifdef OS_SAFETY_CRITICAL
    if (perr == (INT8U *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return;
    }
#endif

#if OS_ARG_CHK_EN > 0u
    if (pevent == (OS_EVENT *)0) {
        *perr = OS_ERR_PEVENT_NULL;
        return;
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {
        *perr = OS_ERR_EVENT_TYPE;
        return;
    }
    if (OSIntNesting > 0u) {
        *perr = OS_ERR_PEND_ISR;
        return;
    }
    if (OSLockNesting > 0u) { 
        *perr = OS_ERR_PEND_LOCKED;
        return;
    }
    OS_ENTER_CRITICAL();
    if (pevent->OSEventCnt > 0u) { //信号量计数器>0表示信号量还可以使用
        pevent->OSEventCnt--;       //自减
        OS_EXIT_CRITICAL();
        *perr = OS_ERR_NONE;
        return;                     //获取成功,返回
    }

    //执行到这里说明信号量已经被其他任务取完,要陷入等待信号量的阻塞状态
    OSTCBCur->OSTCBStat     |= OS_STAT_SEM;     //将任务状态置为等待信号量状态
    OSTCBCur->OSTCBStatPend  = OS_STAT_PEND_OK; //挂起
    OSTCBCur->OSTCBDly       = timeout;         //设置等待超时时间   
    OS_EventTaskWait(pevent);                   //定义在os_core.c中,调用此函数使得当前任务陷入等待:
                                                //将当前任务添加到pevent指向的event等待链表中
    OS_EXIT_CRITICAL();
    OS_Sched();   //调度
    OS_ENTER_CRITICAL();

    //能执行到这里说明当前任务从挂起状态中醒来了
    switch (OSTCBCur->OSTCBStatPend) {      //正确等待目标信号量的到来
        case OS_STAT_PEND_OK:
             *perr = OS_ERR_NONE;       //正常返回
             break;

        case OS_STAT_PEND_ABORT:            //别的任务已经将目标信号量删除而唤醒本任务
             *perr = OS_ERR_PEND_ABORT;  
             break;

        case OS_STAT_PEND_TO:           //等待超时
        default:
             OS_EventTaskRemove(OSTCBCur, pevent);  //删除当前任务在该信号量中等待队列中等待标记
             *perr = OS_ERR_TIMEOUT;                  /* Indicate that we didn't get event within TO   */
             break;
    }

    //设置相关状态量
    OSTCBCur->OSTCBStat          =  OS_STAT_RDY;   
    OSTCBCur->OSTCBStatPend      =  OS_STAT_PEND_OK;
    OSTCBCur->OSTCBEventPtr      = (OS_EVENT  *)0;
#if (OS_EVENT_MULTI_EN > 0u)    //OS_EVENT_MULTI_EN表示可以等待多个事件
    OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0;
#endif
    OS_EXIT_CRITICAL();
}

5. 以删除信号量的方式唤醒等待任务的函数OSSemPendAbort()

  正常情况下调用OSSempost()函数(该函数分析在下面)来释放一个信号量以通知其它在正在等待该信号量的任务得以继续执行,但是在非正常情况下,可以调用此函数实现废除信号量,同时同样让所有等待该信号的任务被唤醒后继续执行。

#if OS_SEM_PEND_ABORT_EN > 0u
INT8U  OSSemPendAbort (OS_EVENT  *pevent,   //指向信号量指针
                       INT8U      opt,      //操作选项
                       INT8U     *perr)
{
    INT8U      nbr_tasks;
#if OS_CRITICAL_METHOD == 3u
    OS_CPU_SR  cpu_sr = 0u;
#endif

#ifdef OS_SAFETY_CRITICAL
    if (perr == (INT8U *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return (0u);
    }
#endif

#if OS_ARG_CHK_EN > 0u
    if (pevent == (OS_EVENT *)0) {
        *perr = OS_ERR_PEVENT_NULL;
        return (0u);
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {
        *perr = OS_ERR_EVENT_TYPE;
        return (0u);
    }
    OS_ENTER_CRITICAL();
    if (pevent->OSEventGrp != 0u) { //OSEventGrp不等于0说明有任务在等待该信号量
        nbr_tasks = 0u;
        switch (opt) {
            case OS_PEND_OPT_BROADCAST: //BROADCAST意为广播,即将所有等待惹怒都设置为Rdy状态使其不再等待
                 while (pevent->OSEventGrp != 0u) {
                     (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_ABORT);
                     nbr_tasks++;
                 }
                 break;

            case OS_PEND_OPT_NONE:  //只唤醒一个在等待本信号量的任务,即在等待任务群中优先级最高的一个(HTP)
            default:
                  //OS_EventTaskRdy()定义在os_core.c中,用户唤醒pevent中的等待任务列表中优先级最高的任务
                 (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_ABORT);
                 nbr_tasks++;
                 break;
        }
        OS_EXIT_CRITICAL();
        OS_Sched();             //重新调度
        *perr = OS_ERR_PEND_ABORT;
        return (nbr_tasks);
    }

    //没任务在等待该信号量,啥都不做
    OS_EXIT_CRITICAL();
    *perr = OS_ERR_NONE;
    return (0u);
}
#endif

6. 释放信号量函数OSSemPost()

  OSSemPost()函数用于释放信号量,唤醒在等待该信号量的任务队列中优先级最高的任务。

INT8U  OSSemPost (OS_EVENT *pevent)
{
#if OS_CRITICAL_METHOD == 3u
    OS_CPU_SR  cpu_sr = 0u;
#endif

#if OS_ARG_CHK_EN > 0u
    if (pevent == (OS_EVENT *)0) {
        return (OS_ERR_PEVENT_NULL);
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {
        return (OS_ERR_EVENT_TYPE);
    }
    OS_ENTER_CRITICAL();
    if (pevent->OSEventGrp != 0u) { //有任务在等待

         //唤醒优先级最高的等待任务
        (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_OK);
        OS_EXIT_CRITICAL();
        OS_Sched();                 //重新调度
        return (OS_ERR_NONE);
    }

    //执行到这里,说明并没有任务在等待,那么释放信号量的操作很简单,因为获取信号量是自减一操作,那么释放则自加一
    if (pevent->OSEventCnt < 65535u) {
        pevent->OSEventCnt++; 
        OS_EXIT_CRITICAL();
        return (OS_ERR_NONE);
    }
    OS_EXIT_CRITICAL();                               /* Semaphore value has reached its maximum       */
    return (OS_ERR_SEM_OVF);
}

提两个问题:
(1) 为什么OSSemPost()函数唤醒的是任务等待队列中优先级最高的一个而OSSemPendAbort的操作选项中有唤醒全部?
因为正常情况下释放一个信号量只会被一个任务获取到,该任务自然是优先级最高的一个,那么假设唤醒了全部等待的任务也没用。而OSSemPendAbort()有广播唤醒任务的选项是因为信号量已经被Abort了,所以要告诉所有等待任务不要继续等待了。

(2) 为什么当有任务在等待该信号量的时候,释放信号量操作cnt不需要自加一?
因为其他任务在获取信号量时若获取不到时只是陷入等待状态而没有执行cnt自减一操作。

7. 获取一个信号量的信息函数OSSemQuery()

  OSSemQuery()函数用于获取信号量的信息,存储于OS_SEM_DATA结构体中。

#if OS_SEM_QUERY_EN > 0u
INT8U  OSSemQuery (OS_EVENT     *pevent,
                   OS_SEM_DATA  *p_sem_data)    //输入型参数,用于存放信号量信息
{
    INT8U       i;
    OS_PRIO    *psrc;
    OS_PRIO    *pdest;
#if OS_CRITICAL_METHOD == 3u
    OS_CPU_SR   cpu_sr = 0u;
#endif

#if OS_ARG_CHK_EN > 0u
    if (pevent == (OS_EVENT *)0) {
        return (OS_ERR_PEVENT_NULL);
    }
    if (p_sem_data == (OS_SEM_DATA *)0) {
        return (OS_ERR_PDATA_NULL);
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {
        return (OS_ERR_EVENT_TYPE);
    }
    OS_ENTER_CRITICAL();
    p_sem_data->OSEventGrp = pevent->OSEventGrp; 

    //数组不能直接赋值,需要对所有成员逐一赋值
    psrc                   = &pevent->OSEventTbl[0];
    pdest                  = &p_sem_data->OSEventTbl[0];
    for (i = 0u; i < OS_EVENT_TBL_SIZE; i++) {
        *pdest++ = *psrc++;
    }
    p_sem_data->OSCnt = pevent->OSEventCnt;                /* Get semaphore count                      */
    OS_EXIT_CRITICAL();
    return (OS_ERR_NONE);
}
#endif 

8. 设置信号量的计数值函数OSSemSet()

  信号量的计数值在创建信号量时候OSSemCreate()的初始化操作中已经确定,在程序运行期间还可以通过OSSemSet()函数改变其计数值。

#if OS_SEM_SET_EN > 0u
void  OSSemSet (OS_EVENT  *pevent,
                INT16U     cnt,     //
                INT8U     *perr)
{
#if OS_CRITICAL_METHOD == 3u 
    OS_CPU_SR  cpu_sr = 0u;
#endif

#ifdef OS_SAFETY_CRITICAL
    if (perr == (INT8U *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return;
    }
#endif

#if OS_ARG_CHK_EN > 0u
    if (pevent == (OS_EVENT *)0) { 
        *perr = OS_ERR_PEVENT_NULL;
        return;
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {
        *perr = OS_ERR_EVENT_TYPE;
        return;
    }

    OS_ENTER_CRITICAL();
    *perr = OS_ERR_NONE;
    if (pevent->OSEventCnt > 0u) {  //信号量还没被获取完,直接修改信号量计数值
        pevent->OSEventCnt = cnt; 
    } else {    //信号量已经被获取完
        if (pevent->OSEventGrp == 0u) { //没有任务在等待,直接修改
            pevent->OSEventCnt = cnt;
        } else {    //信号量被获取万里且当前还有任务在等待获取,这时候不可直接设置计数值,否则会使得正在等待信号量的任务
                    //被唤醒,扰乱程序执行逻辑
            *perr              = OS_ERR_TASK_WAITING;
        }
    }
    OS_EXIT_CRITICAL();
}
#endif
作者:qq_29344757 发表于2017/9/13 12:38:19 原文链接
阅读:22 评论:0 查看评论

【Solidity】5.表达式和控制结构 - 深入理解Solidity

$
0
0

表达式和控制结构

输入参数和输出参数

与Javascript一样,函数可以将参数作为输入; 与Javascript和C不同,它们也可以返回任意数量的参数作为输出。

输入参数

输入参数的声明方式与变量相同。 作为例外,未使用的参数可以省略变量名称。 例如,假设我们希望我们的合约接受一种具有两个整数的外部调用,我们会写下如下:

pragma solidity ^0.4.0;

contract Simple {
    function taker(uint _a, uint _b) {
        // do something with _a and _b.
    }
}

输出参数

输出参数可以在返回关键字之后以相同的语法声明。 例如,假设我们希望返回两个结果:两个给定整数的总和和乘积,那么我们将写:

pragma solidity ^0.4.0;

contract Simple {
    function arithmetics(uint _a, uint _b) returns (uint o_sum, uint o_product) {
        o_sum = _a + _b;
        o_product = _a * _b;
    }
}

可以省略输出参数的名称。 也可以使用return语句指定输出值。 返回语句还能够返回多个值,请参阅返回多个值。 返回参数初始化为零; 如果没有明确设置,它们将保持为零。

输入参数和输出参数可以用作函数体中的表达式。 在那里,他们也可以在任务的左边使用。

控制结构

来自JavaScript的大多数控件结构都可以使用Solidity,除了switchgoto。 所以有: if, else, while, do, for, break, continue, return, ?:,具有C或JavaScript中已知的通常语义。

圆括号不能被省略为条件,但卷边可以在单个语句体上省略。

请注意,没有类型转换从非布尔类型到布尔类型,因为在C和JavaScript中,所以if (1) { ... }无效Solidity。

返回多个值

当一个函数有多个输出参数时, return (v0, v1, ..., vn) can return multiple values. The number of components must be the same as the number of output parameters.可以返回多个值。 组件的数量必须与输出参数的数量相同。

函数调用

内部函数调用

当前合约的功能可以直接调用(“internally”),也可以递归地调用,如这个无意义的例子所示:

pragma solidity ^0.4.0;

contract C {
    function g(uint a) returns (uint ret) { return f(); }
    function f() returns (uint ret) { return g(7) + f(); }
}

这些函数调用被转换为EVM内部的简单跳转。 这具有当前存储器不被清除的效果,即将存储器引用传递到内部称为功能是非常有效的。 只能在内部调用相同合同的功能。

外部函数调用

表达式this.g(8);c.g(2); (其中c是合约实例)也是有效的函数调用,但这一次,函数将被称为“外部”,通过消息调用,而不是直接通过跳转。 请注意,这是函数调用不能在构造函数中使用,因为实际的合同尚未创建。

其他合同的职能必须被外部调用。 对于外部调用,所有函数参数都必须复制到内存中。

当调用其他合同的功能时,可以使用特殊选项.value().gas()指定与呼叫和气体一起发送的数量:

pragma solidity ^0.4.0;

contract InfoFeed {
    function info() payable returns (uint ret) { return 42; }
}

contract Consumer {
    InfoFeed feed;
    function setFeed(address addr) { feed = InfoFeed(addr); }
    function callFeed() { feed.info.value(10).gas(800)(); }
}

必须用于infopayable,否则,.value() 选项将不可用。

请注意,InfoFeed(addr)表达式执行显式类型转换,表示“我们知道给定地址的合同类型为InfoFeed”,并且不执行构造函数。 显式类型转换必须非常谨慎地处理。 不要调用,你不知道它的类型合同上的功能。

我们也可以直接使用 function setFeed(InfoFeed _feed) { feed = _feed; }。注意feed.info.value(10).gas(800)只有(本地)设置通过函数调用发送的gas的值和数量,只有末端的括号执行实际调用。

函数调用导致异常,如果所谓的合同没有(在这个意义上,该帐户不包含代码)存在,或者如果被叫合同本身抛出一个异常或熄灭气体。

与另一个合约的任何交互都会产生潜在的危险,特别是如果合约的源代码未提前知道。 目前的合约对被叫合约进行了控制,可能会对任何事情产生影响。 即使被叫合约从已知的母合约中继承,继承合约只需要具有正确的接口。 然而,合约的执行可以是完全任意的,从而构成危险。 另外,如果在第一次呼叫返回之前调用了您的系统的其他合约,甚至重新进入呼叫合约,您应该做好准备。 这意味着被叫合同可以通过其功能改变呼叫合同的状态变量。 编写你的功能,例如,调用外部函数发生在您的合同中状态变量的任何更改后,您的合同不容易受到重入漏洞的攻击。

命名调用和匿名功能参数

函数调用参数也可以通过名称,以任何顺序给出,如果它们被包含在{}中,可以在下面的例子中看到。 参数列表必须与名称和函数声明中的参数列表重合,但可以按任意顺序排列。

pragma solidity ^0.4.0;

contract C {
    function f(uint key, uint value) {
        // ...
    }

    function g() {
        // named arguments
        f({value: 2, key: 3});
    }
}

省略函数参数名

可以省略未使用参数的名称(特别是返回参数)。 这些名字仍然存在于堆栈中,但是它们是无法访问的。

pragma solidity ^0.4.0;

contract C {
    // 省略参数名称
    function func(uint k, uint) returns(uint) {
        return k;
    }
}

创建新合约

合同可以使用关键字new创建新合同。 正在创建的合同的完整代码必须提前知道,因此递归创建依赖是不可能的。

pragma solidity ^0.4.0;

contract D {
    uint x;
    function D(uint a) payable {
        x = a;
    }
}

contract C {
    D d = new D(4); // 将作为C构造函数的一部分执行

    function createD(uint arg) {
        D newD = new D(arg);
    }

    function createAndEndowD(uint arg, uint amount) {
        // 创建的时候发送ether
        D newD = (new D).value(amount)(arg);
    }
}

如示例所示,可以使用.value()选项将Ether转发到创建,但不可能限制气体量。 如果创建失败(由于堆栈不足,余额不足或其他问题),则抛出异常。

表达式的评估顺序

没有指定表达式的评估顺序(更正式地,表达式树中的一个节点的子节点被评估的顺序未被指定,但是当然在节点本身之前进行评估)。 只保证按照顺序执行语句,完成布尔表达式的短路。 有关详细信息,请参阅运算符优先顺序

分配

解析分配和返回多个值

内部的Solidity允许元组类型,即在编译时大小不变的潜在不同类型的对象列表。 这些元组可以用来同时返回多个值,并且同时将它们分配给多个变量(或一般的值):

pragma solidity ^0.4.0;

contract C {
    uint[] data;

    function f() returns (uint, bool, uint) {
        return (7, true, 2);
    }

    function g() {
        // 声明和分配变量。 明确指定类型是不可能的。
        var (x, b, y) = f();
        // 分配给一个预先存在的变量。
        (x, y) = (2, 7);
        // 互换值的常用技巧对于非价值存储类型不起作用。
        (x, y) = (y, x);
        // 组件可以省略(也可以用于变量声明)。
        // 如果元组以空组件结束,其余的值将被丢弃。
        (data.length,) = f(); // 设置长度为 7
        // 在左边也可以做同样的事情。
        (,data[3]) = f(); // Sets data[3] to 2
        // 组件只能在作业的左侧排除,但有一个例外:
        (x,) = (1,);
        // (1,) 是指定1元组元的唯一方法,因为(1)等于1。
    }
}

并发症数组和结构

赋值的语义对于非数值类型(如数组和结构体)来说有点复杂。 分配给状态变量总是创建一个独立的副本。 另一方面,分配给局部变量仅为基本类型创建独立的副本,即适合32个字节的静态类型。 如果结构体或数组(包括字节和字符串)从状态变量分配给局部变量,则局部变量保存对原始状态变量的引用。 对本地变量的第二个赋值不会修改状态,只会更改引用。 对局部变量的成员(或元素)的分配会改变状态。

范围界定和声明

声明的变量将具有初始默认值,其字节表示全为零。 变量的“默认值”是任何类型的典型“零状态”。 例如,bool的默认值为false。 uint或int类型的默认值为0.对于静态大小的数组和bytes1到bytes32,每个单独的元素将被初始化为与其类型对应的默认值。 最后,对于动态大小的数组,字节和字符串,默认值为空数组或字符串。

在函数中任何地方声明的变量将在整个函数的范围内,无论它在哪里被声明。 这是因为Solidity从JavaScript继承其范围规则。 这与许多语言形成对比,在这些语言中,只有范围被限定到变量才被声明,直到语义块结束。 因此,以下代码是非法的,并导致编译器抛出错误,标识符已声明:

// 这不会编译

pragma solidity ^0.4.0;

contract ScopingErrors {
    function scoping() {
        uint i = 0;

        while (i++ < 1) {
            uint same1 = 0;
        }

        while (i++ < 2) {
            uint same1 = 0;// same1的非法,第二个声明
        }
    }

    function minimalScoping() {
        {
            uint same2 = 0;
        }

        {
            uint same2 = 0;// same2的非法,第二个声明
        }
    }

    function forLoopScoping() {
        for (uint same3 = 0; same3 < 1; same3++) {
        }

        for (uint same3 = 0; same3 < 1; same3++) {// same3的非法,第二个声明
        }
    }
}

除此之外,如果一个变量被声明,它将在函数的开头被初始化为其默认值。 因此,以下代码是合法的,尽管写得不好:

function foo() returns (uint) {
    // baz被隐式初始化为0
    uint bar = 5;
    if (true) {
        bar += baz;
    } else {
        uint baz = 10;// 永不执行
    }
    return bar;// 返回 5
}

错误处理: Assert, Require, Revert and Exceptions

Solidity使用状态恢复异常来处理错误。 这种异常将撤消在当前调用(及其所有子调用)状态的所有变化,也标志的错误给调用者。 方便函数assert和require可以用于检查条件,如果条件不满足则抛出异常。 assert函数只能用于测试内部错误,并检查不变量。 应该使用require函数来确保满足输入或合同状态变量的有效条件,或者验证从外部合同的调用返回值。 如果正确使用,分析工具可以评估您的合同,以识别将达到失败断言的条件和函数调用。 正常运行的代码不应该达到失败的断言声明; 如果发生这种情况,您的合同中会出现一个您应该修复的错误。

还有另外两种方法可以触发异常:revert函数可用于标记错误并恢复当前的调用。 将来可能还可以包括有关恢复调用中的错误的详细信息。 throw关键字也可以用作revert()的替代方法。

从0.4.13版本,throw关键字已被弃用,将来会被淘汰。

当子调用中发生异常时,它们会自动“冒泡”(即异常被重新引导)。 此规则的异常是发送和低级函数调用,委托调用和调用代码 - 在异常情况下返回false而不是“冒泡”。

作为EVM设计的一部分,如果调用帐户不存在,低级呼叫,委托呼叫和呼叫代码将返回成功。 如果需要,必须在调用前检查是否存在。

捕捉异常还不可能。

在下面的示例中,您可以看到如何使用需求来轻松检查输入条件,以及断言如何用于内部错误检查:

pragma solidity ^0.4.0;

contract Sharer {
    function sendHalf(address addr) payable returns (uint balance) {
        require(msg.value % 2 == 0); // 只允许偶数
        uint balanceBeforeTransfer = this.balance;
        addr.transfer(msg.value / 2);
        // 由于转移抛出异常失败,不能在这里回调,我们应该没有办法仍然有一半的钱。
        assert(this.balance == balanceBeforeTransfer - msg.value / 2);
        return this.balance;
    }
}

在以下情况下会生成assert样式异常:

1.如果您以太大或负数索引访问数组(比如x[i]i >= x.length or i < 0)
2.如果您以太大或负数索引访问固定长度的bytesN。
3.如果您划分或模数为零(例如5/0或23%0)。
4.如果通过负移动量。
5.如果转换过大或负进枚举类型的值。
6.如果调用内部函数类型的零初始化变量。
7.如果您使用一个评估为false的参数调用assert。

在以下情况下会生成require-style异常:

1.调用throw
2.调用require并且条件为false
3.如果您通过消息调用调用函数,但是它没有正确完成(即,用尽了气体,没有匹配的功能,或者引发异常本身),除非使用低级别的操作callsenddelegatecallcallcode。 低级别的操作不会抛出异常,而是通过返回false来指示失败。
4.如果您使用new关键字,但合同创建未正常完成创建合同(见上文的“无法正常完成”的定义)。
5.如果您执行一个定向不包含代码的合同的外部函数调用。
6.如果您的合约通过无功能修改器(包括构造函数和后备功能)通过公共函数接收Ether。
7.如果你的合约通过一个公共的getter函数接收Ether。
8.如果.transfer()失败。

在内部,Solidity对require-style异常执行一个还原操作(0xfd指令),并执行一个无效操作(指令0xfe)来抛出一个assert-style异常。 在这两种情况下,这将导致EVM恢复对状态所做的所有更改。 恢复原因是没有安全的方式来继续执行,因为没有发生预期的效果。 因为我们要保留交易的原子性,所以最安全的做法是恢复所有的变化,并使整个事务(或至少调用)无效。 请注意,断言风格的异常消耗调用中可用的所有gas,而需求风格的异常将不会消耗从大都会版本开始的任何gas。

作者:diandianxiyu 发表于2017/9/13 12:54:11 原文链接
阅读:0 评论:0 查看评论

挑战程序竞赛系列(62):4.6平面上的分治法(2)

$
0
0

挑战程序竞赛系列(62):4.6平面上的分治法(2)

传送门:Codeforces 97B: Superset


题意:(参考hankcs)
http://www.hankcs.com/program/algorithm/codeforces-97b-superset.html

点集:给定n个点,请添加一些点,使任意两点满足①在同一条水平线或竖直线上②或构成一个矩形框住其他点。

直接看hankcs的解释吧,通俗易懂。

指出一点:新的点一定由这些坐标的横纵坐标生成,所以求出投影即能满足条件①,条件②在求解①的过程中自然满足。

注意Java中Set的使用方法,需要重写hashCode 和 equals方法,因为hashCode可能相同,所以判断两个对象是否相等还需要加入eqauls方法。

代码如下:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;

public class Main{

    String INPUT = "./data/judge/201709/C097B.txt";

    public static void main(String[] args) throws IOException {
        new Main().run();
    }

    static final int MAX_N = 10000 + 16;

    class Pair implements Comparable<Pair>{
        int x;
        int y;
        Pair(int x, int y){
            this.x = x;
            this.y = y;
        }

        @Override
        public String toString() {
            return x + " " + y;
        }

        @Override
        public int compareTo(Pair o) {
            return this.x - o.x;
        }

        @Override
        public int hashCode() {
            return x * 17 + y * 13;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) return true;
            if (obj == null || getClass() != obj.getClass()) return false; 

            Pair p = (Pair) obj;
            return x == p.x && y == p.y;
        }
    }

    Set<Pair> ans = new HashSet<>();
    void solve(Pair[] p, int l, int r) {
        if (r - l <= 1) return;
        int m = (r + l) / 2;
        int x = p[m].x;
        for (int i = l; i < r; ++i) {
            ans.add(new Pair(x, p[i].y));
        }
        solve(p, l, m);
        solve(p, m, r);
    }

    void read() {
        int N = ni();
        Pair[] p = new Pair[N];
        for (int i = 0; i < N; ++i) {
            p[i] = new Pair(ni(), ni());
        }
        Arrays.sort(p);
        for (Pair ps : p) ans.add(ps);
        solve(p, 0, N);
        out.println(ans.size());
        for (Pair pp : ans) {
            out.println(pp);
        }
    }

    FastScanner in;
    PrintWriter out;

    void run() throws IOException {
        boolean oj;
        try {
            oj = ! System.getProperty("user.dir").equals("F:\\java_workspace\\leetcode");
        } catch (Exception e) {
            oj = System.getProperty("ONLINE_JUDGE") != null;
        }

        InputStream is = oj ? System.in : new FileInputStream(new File(INPUT));
        in = new FastScanner(is);
        out = new PrintWriter(System.out);
        long s = System.currentTimeMillis();
        read();
        out.flush();
        if (!oj){
            System.out.println("[" + (System.currentTimeMillis() - s) + "ms]");
        }
    }

    public boolean more(){
        return in.hasNext();
    }

    public int ni(){
        return in.nextInt();
    }

    public long nl(){
        return in.nextLong();
    }

    public double nd(){
        return in.nextDouble();
    }

    public String ns(){
        return in.nextString();
    }

    public char nc(){
        return in.nextChar();
    }

    class FastScanner {
        BufferedReader br;
        StringTokenizer st;
        boolean hasNext;

        public FastScanner(InputStream is) throws IOException {
            br = new BufferedReader(new InputStreamReader(is));
            hasNext = true;
        }

        public String nextToken() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (Exception e) {
                    hasNext = false;
                    return "##";
                }
            }
            return st.nextToken();
        }

        String next = null;
        public boolean hasNext(){
            next = nextToken();
            return hasNext;
        }

        public int nextInt() {
            if (next == null){
                hasNext();
            }
            String more = next;
            next = null;
            return Integer.parseInt(more);
        }

        public long nextLong() {
            if (next == null){
                hasNext();
            }
            String more = next;
            next = null;
            return Long.parseLong(more);
        }

        public double nextDouble() {
            if (next == null){
                hasNext();
            }
            String more = next;
            next = null;
            return Double.parseDouble(more);
        }

        public String nextString(){
            if (next == null){
                hasNext();
            }
            String more = next;
            next = null;
            return more;
        }

        public char nextChar(){
            if (next == null){
                hasNext();
            }
            String more = next;
            next = null;
            return more.charAt(0);
        }
    }

    static class ArrayUtils {

        public static void fill(int[][] f, int value) {
            for (int i = 0; i < f.length; ++i) {
                Arrays.fill(f[i], value);
            }
        }

        public static void fill(int[][][] f, int value) {
            for (int i = 0; i < f.length; ++i) {
                fill(f[i], value);
            }
        }

        public static void fill(int[][][][] f, int value) {
            for (int i = 0; i < f.length; ++i) {
                fill(f[i], value);
            }
        }
    }
}

alt text

作者:u014688145 发表于2017/9/13 13:03:57 原文链接
阅读:0 评论:0 查看评论

git常用命令

$
0
0
查看、添加、提交、删除、找回,重置修改文件
git help <command> # 显示command的help
git show # 显示某次提交的内容 git show $id
git co -- <file> # 抛弃工作区修改
git co . # 抛弃工作区修改
git add <file> # 将工作文件修改提交到本地暂存区
git add . # 将所有修改过的工作文件提交暂存区
git rm <file> # 从版本库中删除文件
git rm <file> --cached # 从版本库中删除文件,但不删除文件
git reset <file> # 从暂存区恢复到工作文件
git reset -- . # 从暂存区恢复到工作文件
git reset --hard # 恢复最近一次提交过的状态,即放弃上次提交后的所有本次修改
git ci <file> git ci . git ci -a # 将git add, git rm和git ci等操作都合并在一起做                                    git ci -am "some comments"
git ci --amend # 修改最后一次提交记录
git revert <$id> # 恢复某次提交的状态,恢复动作本身也创建次提交对象
git revert HEAD # 恢复最后一次提交的状态
查看文件diff
git diff <file> # 比较当前文件和暂存区文件差异 git diff
git diff <id2> # 比较两次提交之间的差异
git diff <branch1>..<branch2> # 在两个分支之间比较
git diff --staged # 比较暂存区和版本库差异
git diff --cached # 比较暂存区和版本库差异
git diff --stat # 仅仅比较统计信息
查看提交记录
git log git log <file> # 查看该文件每次提交记录
git log -p <file> # 查看每次详细修改内容的diff
git log -p -2 # 查看最近两次详细修改内容的diff
git log --stat #查看提交统计信息
tig
Mac上可以使用tig代替diff和log,brew install tig
Git 本地分支管理
查看、切换、创建和删除分支
git br -r # 查看远程分支
git br <new_branch> # 创建新的分支
git br -v # 查看各个分支最后提交信息
git br --merged # 查看已经被合并到当前分支的分支
git br --no-merged # 查看尚未被合并到当前分支的分支
git co <branch> # 切换到某个分支
git co -b <new_branch> # 创建新的分支,并且切换过去
git co -b <new_branch> <branch> # 基于branch创建新的new_branch
git co $id # 把某次历史提交记录checkout出来,但无分支信息,切换到其他分支会自动删除
git co $id -b <new_branch> # 把某次历史提交记录checkout出来,创建成一个分支
git br -d <branch> # 删除某个分支
git br -D <branch> # 强制删除某个分支 (未被合并的分支被删除的时候需要强制)
 分支合并和rebase
git merge <branch> # 将branch分支合并到当前分支
git merge origin/master --no-ff # 不要Fast-Foward合并,这样可以生成merge提交
git rebase master <branch> # 将master rebase到branch,相当于: git co <branch> && git rebase master && git co master && git merge <branch>
 Git补丁管理(方便在多台机器上开发同步时用)
git diff > ../sync.patch # 生成补丁
git apply ../sync.patch # 打补丁
git apply --check ../sync.patch #测试补丁能否成功
 Git暂存管理
git stash # 暂存
git stash list # 列所有stash
git stash apply # 恢复暂存的内容
git stash drop # 删除暂存区
Git远程分支管理
git pull # 抓取远程仓库所有分支更新并合并到本地
git pull --no-ff # 抓取远程仓库所有分支更新并合并到本地,不要快进合并
git fetch origin # 抓取远程仓库更新
git merge origin/master # 将远程主分支合并到本地当前分支
git co --track origin/branch # 跟踪某个远程分支创建相应的本地分支
git co -b <local_branch> origin/<remote_branch> # 基于远程分支创建本地分支,功能同上
git push # push所有分支
git push origin master # 将本地主分支推到远程主分支
git push -u origin master # 将本地主分支推到远程(如无远程主分支则创建,用于初始化远程仓库)
git push origin <local_branch> # 创建远程分支, origin是远程仓库名
git push origin <local_branch>:<remote_branch> # 创建远程分支
git push origin :<remote_branch> #先删除本地分支(git br -d <branch>),然后再push删除远程分支
Git远程仓库管理
git remote -v # 查看远程服务器地址和仓库名称
git remote show origin # 查看远程服务器仓库状态
git remote add origin git@ github:robbin/robbin_site.git # 添加远程仓库地址
git remote set-url origin git@ github.com:robbin/robbin_site.git # 设置远程仓库地址(用于修改远程仓库地址) git remote rm <repository> # 删除远程仓库
创建远程仓库
git clone --bare robbin_site robbin_site.git # 用带版本的项目创建纯版本仓库
scp -r my_project.git git@ git.csdn.net:~ # 将纯仓库上传到服务器上
mkdir robbin_site.git && cd robbin_site.git && git --bare init # 在服务器创建纯仓库
git remote add origin git@ github.com:robbin/robbin_site.git # 设置远程仓库地址
git push -u origin master # 客户端首次提交
git push -u origin develop # 首次将本地develop分支提交到远程develop分支,并且track
git remote set-head origin master # 设置远程仓库的HEAD指向master分支
也可以命令设置跟踪远程库和本地库
git branch --set-upstream master origin/master
git branch --set-upstream develop origin/develop
作者:peixiaopao 发表于2017/9/13 9:08:07 原文链接
阅读:32 评论:0 查看评论

LeetCode 105. Construct Binary Tree from Preorder and Inorder Traversal

$
0
0

Construct Binary Tree from Preorder and Inorder Traversal


题目描述:

Given preorder and inorder traversal of a tree, construct the binary tree.

Note:
You may assume that duplicates do not exist in the tree.


题目大意:

给定满二叉树中序遍历和先序遍历,建立二叉树。
可以根据先序遍历找出根节点,在中序遍历中找出该节点元素,该节点元素的左边都是左子树的节点,右边都是右子树的节点。可以通过递归建树。


题目代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        return build(preorder, inorder, 0, preorder.size()-1, 0, inorder.size()-1);
    }
    
    TreeNode* build(vector<int>& preorder, vector<int>& inorder, int ps, int pe, int is, int ie){
        if(ps > pe || is > ie)
            return nullptr;
        TreeNode* root = new TreeNode(preorder[ps]);
        int i = 0;
        while(inorder[i] != root->val) i++;
        root->left  = build(preorder, inorder, ps+1, ps+i-is, is, i-1);
        root->right = build(preorder, inorder, ps+i-is+1, pe,i+1, ie);
        return root;
    }
    
};


作者:qq_34594236 发表于2017/9/13 14:33:17 原文链接
阅读:9 评论:0 查看评论

LeetCode 106. Construct Binary Tree from Inorder and Postorder Traversal

$
0
0

Construct Binary Tree from Inorder and Postorder Traversal


题目描述:

Given inorder and postorder traversal of a tree, construct the binary tree.

Note:
You may assume that duplicates do not exist in the tree.


题目大意:

给出一个二叉树的中序遍历和后续遍历,建立二叉树。
根据后续遍历找出根节点,在中序遍历中找出该节点的位置,所有左边的节点属于左子树,所有右边的节点属于右子树,可通过递归建树。


题目代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        return build(inorder, postorder, 0, inorder.size()-1, 0, postorder.size()-1);
    }
    TreeNode* build(vector<int>& inorder, vector<int>& postorder, int is, int ie, int ps, int pe){
        if(is > ie || ps > pe)
            return nullptr;
        
        TreeNode* root = new TreeNode(postorder[pe]);
        
        int i = 0;
        while(inorder[i] != root->val) i++;
        
        root->right  = build(inorder, postorder, i+1, ie, ps+i-is, pe-1);
        root->left = build(inorder, postorder, is, i-1, ps, ps+i-is-1);
        
        return root;
    }
};


作者:qq_34594236 发表于2017/9/13 14:39:58 原文链接
阅读:10 评论:0 查看评论

LeetCode 153. Find Minimum in Rotated Sorted Array

$
0
0

Find Minimum in Rotated Sorted Array


题目描述:

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).

Find the minimum element.

You may assume no duplicate exists in the array.


题目大意:

找出数组中的最小值。


题目代码:

class Solution {
public:
    int findMin(vector<int>& nums) {
        int ans = INT_MAX;
        for(int i = 0; i < nums.size(); i++){
            ans = min(nums[i], ans);
        }
        return ans;
    }
};


作者:qq_34594236 发表于2017/9/13 14:44:14 原文链接
阅读:9 评论:0 查看评论

剑指offer——38.二叉树的深度

$
0
0

题目描述

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

代码

思路:递归求左子树和右子树深度,然后比较,最终返回最大值加1

function TreeDepth(pRoot)
{
    // write code here
    if(pRoot==null) return 0;
    var left=TreeDepth(pRoot.left);
    var right=TreeDepth(pRoot.right);
    return (left>right)?left+1:right+1;
}
作者:owen1190 发表于2017/9/13 14:49:19 原文链接
阅读:3 评论:0 查看评论

LeetCode 162. Find Peak Element

$
0
0

Find Peak Element


题目描述:

A peak element is an element that is greater than its neighbors.

Given an input array where num[i] ≠ num[i+1], find a peak element and return its index.

The array may contain multiple peaks, in that case return the index to any one of the peaks is fine.

You may imagine that num[-1] = num[n] = -∞.

For example, in array [1, 2, 3, 1], 3 is a peak element and your function should return the index number 2.


题目大意:

找出元素b,假设b元素前面的元素是a,后面的元素是c。则元素b满则b>ab>c。则返回元素b的下标。如果存在多个,则返回任意一个即可。我们把第一个元素的前面看作负无穷,最后一个元素的后一个也看作负无穷。


题目代码:

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        if(nums.size() < 2) return 0;
        for(int i = 1; i < nums.size(); i++){
            if((i!= nums.size()-1 && nums[i] > nums[i-1] && nums[i] > nums[i+1]) || (i==nums.size()-1 && nums[i] > nums[i-1])){
                return i;
            }
        }
        return 0;
    }
};


作者:qq_34594236 发表于2017/9/13 14:50:02 原文链接
阅读:9 评论:0 查看评论

剑指offer——39.平衡二叉树

$
0
0

题目描述

输入一棵二叉树,判断该二叉树是否是平衡二叉树。

代码

思路:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。遍历左子树和右子树的深度,然后比较二者差值

function IsBalanced_Solution(pRoot)
{
    // write code here
    if(pRoot==null) return true;
    var left=TreeDepth(pRoot.left);
    var right=TreeDepth(pRoot.right);
    if((left-right)>1||(left-right)<-1){
        return false;
    }
    return IsBalanced_Solution(pRoot.left)&&IsBalanced_Solution(pRoot.right)    
}
function TreeDepth(root){
    if(root==null) return 0;
    var left=TreeDepth(root.left);
    var right=TreeDepth(root.right);
    return (left>right)?left+1:right+1;
}
作者:owen1190 发表于2017/9/13 14:50:40 原文链接
阅读:8 评论:0 查看评论

IntelliJ IDEA 实现Spring项目的热部署

$
0
0

IntelliJ IDEA 实现Spring项目的热部署

1.Jrebel简介

    通过jrebel插件,我们可以实现项目在开发工具中的热部署,以减少8%-18%的部署等待时间。

    尽管JVM已经支持了热部署,但是他是局限性的,当你更改了方法的参数列表,新增一个方法,或者新增一个类。JVM内置的热部署将失去作用,你必须重新部署项目。
    而借助jrebel,你可以新增一个方法,修改参数列表,甚至你新增一个接口,新增一个Spring的bean都可以不用重新部署项目。去掉这些可恶的等待时间。

// 更多简介可以去jrebel官网查看,此处只是大致说明热部署的用途。
// https://zeroturnaround.com/software/jrebel/

或者点击此链接去往jrebel官方网站

2.在IDEA中安装Jrebel插件

1.按下ctrl+alt+s快捷键,打开设置面板
2.点击plugins
3.点击browse repositories
4.在搜索中输入jrebel for intellij
5.然后点击安装,如下图。
6.安装完成后重启IDEA即可

这里写图片描述

3.激活插件

Jrebel是一款收费的插件,初次使用有14天的试用期

// so 如果你有钱,你可以购买该插件
但是,如果你不想花钱,那

...
...
也是有办法的。
你可以搞一张通行证,也就是license code。
如果你能拿到license server的链接也是可以的。

本文提供一个免费通道。
你可以用你的facebook帐号授权给Jrebel,然后,你会获得一个license code。
推特帐号也是可以的。

激活步骤

去到Jrebel官网
https://my.jrebel.com/

这里写图片描述

然后在右侧通过connect wl facebook登录。
然后是填写各种信息,授权什么的。
完了之后你会进入到如下图所示的页面。
这里写图片描述

点击红框所示的activate,你就得到了license code,复制到你的项目,点击激活即可。

需要vpn翻墙的,请加入博客左侧qq群找群主。

4.配置插件

项目结构的配置

这里写图片描述

这两个选择你都可以设置成一样的。
但是第二个选项,建议最好设置成只更新资源,而不更新classes文件。
不然在你编码的时候切出IDEA,IDEA会自动执行项目编译。会弹出消息框,比较恼人。
但是如果你不自动更新classes文件,那你就需要手动更新,需要按下ctrl+f9,此处仍然能够保证你不需要重新部署项目。
所以,看个人需要了。

项目配置

在你的项目里面,enable Jrebel,启动插件。

这里写图片描述

5.运行插件

你可以通过小红框处的启动按钮以运行或者debug模式启动你的项目。
然后你可以在控制台输出中看到Jrebel输出的信息。 
至此,Jrebel成功安装并运行。

这里写图片描述

作者:qq_15071263 发表于2017/9/13 14:51:38 原文链接
阅读:11 评论:0 查看评论

剑指offer——40.数组中只出现一次的数字

$
0
0

题目描述

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

代码

思路:将数组同去重后的数组进行比较,求出数组中元素出现的次数

Array.prototype.uniq = function () {
    var res = [];
    var json = {};

    for (var i = 0; i < this.length; i++) {
        if (!json[this[i]]) {
            res.push(this[i]);
            json[this[i]] = 1;
        }
    }

    return res;
}

function FindNumsAppearOnce(array)
{
    // write code here
    // return list, 比如[a,b],其中ab是出现一次的两个数字
    var a = array;
    var b = a.uniq();
    var arr = [];
    var k = 0;

    for (var i = 0; i < b.length; i++) {
        for (var j = 0; j < a.length; j++) {
            if (a[j] == b[i]) {
                k++;
            }
        }
        if (k === 1) {
            arr.push(b[i]);
        }
        k = 0; // 重新赋值0,继续下一次循环
    }

    return arr;
}
作者:owen1190 发表于2017/9/13 14:52:47 原文链接
阅读:0 评论:0 查看评论

剑指offer——41.和为S的连续正数序列

$
0
0

题目描述

小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!

输出描述:
输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序

代码

思路:设定两个指针,如果和大于sum,左指针向后移位,如果小于,右指针向后移位。
如果两个指针碰在一起,则跳出,
左指针一直小于sum的一半,

function FindContinuousSequence(sum)
{
    // write code here
    if(sum<2) return[];
    var result=[];
    var a=1,b=2,s=3;
    while(a<=Math.floor(sum/2)){
        if(s<sum){
            b++;
            s+=b;
        }else if(s>sum){
            s-=a
            a++;            
        }else{
            var temp=[];
            for(var i=a;i<=b;i++){
                temp.push(i)
            }
            result.push(temp)
            if(a+1<b){
                s-=a;
                a++
            }else{
                break;
            }
        }        
    }
    return result;
}
作者:owen1190 发表于2017/9/13 14:53:37 原文链接
阅读:0 评论:0 查看评论
Viewing all 35570 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>