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

Mybatis源码分析之参数映射及处理ParameterHandler

$
0
0

ParameterHandler是用来设置参数规则的,当StatementHandler调用prepare方法之后,接下来就是调用它来进行设置参数。

ParameterHandler接口:

public interface ParameterHandler {

  Object getParameterObject();

  void setParameters(PreparedStatement ps)
      throws SQLException;

}
getParameterObject是用来获取参数的,setParameters(PreparedStatement ps)是用来设置参数的,相当于对sql中所有的参数都执行ps.setXXX(value);

ParameterHandler的默认实现类是DefaultParameterHandler,其实现了接口中定义的两个方法。

getParameterObject是获取参数,这个参数值就是你传递进来的值,可能是个实体、map或单个基本类型数据。

@Override
  public Object getParameterObject() {
    return parameterObject;
  }
设置参数,其实就是你在sql语句中配置的java对象和jdbc类型对应的关系#{id,jdbcType=INTEGER},id默认类型是javaType=class java.lang.Integer。
//设置参数
  @Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
	//获取所有参数,ParameterMapping是java类型和jdbc类型的对应关系
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if (parameterMapping.getMode() != ParameterMode.OUT) {
		  //参数值
          Object value;
		  //获取参数名称
          String propertyName = parameterMapping.getProperty();
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
			//获取参数值
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
			 //如果是单个值则直接赋值
            value = parameterObject;
          } else {
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
         //获取参数值对应的jdbc类型
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
	   //设置参数值和jdbc类型的对应关系
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          } catch (SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }
这样就设置了一个参数值对应的jdbcType了

完整的DefaultParameterHandler源码如下:

/**
 * @author Clinton Begin
 * @author Eduardo Macarron
 */
public class DefaultParameterHandler implements ParameterHandler {

  private final TypeHandlerRegistry typeHandlerRegistry;

  private final MappedStatement mappedStatement;
  //所有的参数值
  private final Object parameterObject;
  private BoundSql boundSql;
  private Configuration configuration;

  public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    this.mappedStatement = mappedStatement;
    this.configuration = mappedStatement.getConfiguration();
    this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
    this.parameterObject = parameterObject;
    this.boundSql = boundSql;
  }

  @Override
  public Object getParameterObject() {
    return parameterObject;
  }
  //设置参数
  @Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
	//获取所有参数,ParameterMapping是java类型和jdbc类型的对应关系
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if (parameterMapping.getMode() != ParameterMode.OUT) {
		  //参数值
          Object value;
		  //获取参数名称
          String propertyName = parameterMapping.getProperty();
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
			//获取参数值
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
			 //如果是单个值则直接赋值
            value = parameterObject;
          } else {
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
		  //获取参数值对应的jdbc类型
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
			//设置参数值和jdbc类型的对应关系
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          } catch (SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }

}


作者:qq924862077 发表于2016/12/15 19:14:38 原文链接
阅读:64 评论:0 查看评论

Mybatis源码学习之TypeHandler

$
0
0
ORM框架最重要功能是将面向对象方法中的对象和关系型数据库中的表关联了起来,在关联过程中就必然涉及到对象中的数据类型和数据库中的表字段类型的转换,Mybatis中的org.apache.ibatis.type包主要就是实现这个功能。TypeHandler的功能就是给参数设置指定的jdbc类型和返回对应的java类型的数据。
接口、抽象类和实现类的结构体系:

接口TypeHandler中定义如下方法:
主要有两部分:
(1)setParameter是设置参数的类型
(2)getResult是根据值的jdbc类型返回对应的java类型
public interface TypeHandler<T> {
  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
  T getResult(ResultSet rs, String columnName) throws SQLException;
  T getResult(ResultSet rs, int columnIndex) throws SQLException;
  T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
在抽象类BaseTypeHandler中又增加了一些相应的模板方法
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
  protected Configuration configuration;
  public void setConfiguration(Configuration c) {
    this.configuration = c;
  }
  @Override
  public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
    if (parameter == null) {
      if (jdbcType == null) {
        throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
      }
      try {
        ps.setNull(i, jdbcType.TYPE_CODE);
      } catch (SQLException e) {
        throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
                "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
                "Cause: " + e, e);
      }
    } else {
      try {
        setNonNullParameter(ps, i, parameter, jdbcType);
      } catch (Exception e) {
        throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
                "Try setting a different JdbcType for this parameter or a different configuration property. " +
                "Cause: " + e, e);
      }
    }
  }
  @Override
  public T getResult(ResultSet rs, String columnName) throws SQLException {
    T result;
    try {
      result = getNullableResult(rs, columnName);
    } catch (Exception e) {
      throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set.  Cause: " + e, e);
    }
    if (rs.wasNull()) {
      return null;
    } else {
      return result;
    }
  }
  @Override
  public T getResult(ResultSet rs, int columnIndex) throws SQLException {
    T result;
    try {
      result = getNullableResult(rs, columnIndex);
    } catch (Exception e) {
      throw new ResultMapException("Error attempting to get column #" + columnIndex+ " from result set.  Cause: " + e, e);
    }
    if (rs.wasNull()) {
      return null;
    } else {
      return result;
    }
  }
  @Override
  public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
    T result;
    try {
      result = getNullableResult(cs, columnIndex);
    } catch (Exception e) {
      throw new ResultMapException("Error attempting to get column #" + columnIndex+ " from callable statement.  Cause: " + e, e);
    }
    if (cs.wasNull()) {
      return null;
    } else {
      return result;
    }
  }
  public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
  public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;
  public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;
  public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;

}
最终完整的实现还是在实现类中,接下来我们介绍众多实现类中的一个IntegerTypeHandler
1、setNonNullParameter实现的功能就是接口中定义的setParameter方法要提供的功能,其实也是比较简单的IntegerTypeHandler中实现的就是调用PreparedStatement的setInt方法来给相应的参数设置int值,其他类型类似。
@Override
  public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType)
      throws SQLException {
    ps.setInt(i, parameter);
  }
2、getNullableResult实现的就是接口中定义的getResult方法提供的功能,主要就是将返回值作为Integer返回。
  @Override
  public Integer getNullableResult(ResultSet rs, int columnIndex)
      throws SQLException {
    return rs.getInt(columnIndex);
  }

IntegerTypeHandler完整的源码如下:
public class IntegerTypeHandler extends BaseTypeHandler<Integer> {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType)
      throws SQLException {
    ps.setInt(i, parameter);
  }

  @Override
  public Integer getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    return rs.getInt(columnName);
  }

  @Override
  public Integer getNullableResult(ResultSet rs, int columnIndex)
      throws SQLException {
    return rs.getInt(columnIndex);
  }

  @Override
  public Integer getNullableResult(CallableStatement cs, int columnIndex)
      throws SQLException {
    return cs.getInt(columnIndex);
  }
}

其他类型对应的TypeHandler与IntegerTypeHandler类似。


作者:qq924862077 发表于2016/12/15 19:37:14 原文链接
阅读:52 评论:0 查看评论

[UML]用例图

$
0
0

用例图是由软件需求分析到最终实现的第一步,它描述人们希望如何使用一个系统。用例图是从用户的角度出发描述对软件产品的需求,分析产品所需的功能和动态行为。因此对于整个软件而言,用例图的正确直接影响到用户对最终产品的满意度。

UML中的用例图描述了用例;参与者;参与者、用例之间的关系,泛化关系、包含关系、扩展关系等。

参与者概念

参与者(Actor)是系统外部的一个实体(可以是任何的事物或人),它以某种方式参与了用例的执行过程。参与者通过向系统输入或请求系统输入某些事件来出发系统的执行。参与者有他们参与用例时所担当的角色来表示。
每个参与者可以参与一个或多个用例。通过交换信息与用例发生交互(因此也与用例所在的系统或类发生了交互),而参与者的内部实现与用例是不相关的,可以用一组定义其状态的属性充分描述参与者。
参与者不一定是人,也可以是一个外部系统,外部系统可以是软件系统也可以是硬件系统。
确定参与者:
(1)谁或为什么使用该系统
(2)交互中,他们扮演什么角色;
(3)谁安装系统;
(4)谁启动或关闭系统;
(5)谁维护系统
(6)与该系统交互的是什么系统;
(7)谁从系统获取信息;
(8)谁提供信息给系统
(9)有什么事发生在固定时间
在建模参与者过程中,记住以下要点:
(1)参与者对于系统而言总是外部的,因此它们在你的控制之外。
(2)参与者直接同系统交互,这可以帮助定义系统边界。
(3)参与者表示人和事物与系统发生交互式所扮演的角色,而不是特定的人或特定的事物。
(4)一个人或事物在与系统发生交互时,可以同时或不同时扮演多个角色。例如,某研究生担任某教授的助教,从职业的角度看,他扮演了两个角色——学生和助教。
(5)每一个参与者需要有一个具有业务一样的名字。
(6)每个参与者必须又简短的描述,从业务角度描述参与者是什么。
(7)像类一样,参与者可以具有分栏,表示参与者属性和它可接受的事件。一般情况下,这种分栏使用的并不多,很少显示在用例图中。
参与者间的关系:
在用例图中,使用了泛化关系来描述多个参与者之间的公共行为。如果系统中存在几个参与者,他们即扮演自身的角色,同时也扮演更具一般化的角色,呢么就用泛化关系来描述他们。

用例

用例的概念:
用例是对系统的用户需求(主要是功能需求)的描述,用例表达了系统的功能和所提供的服务。

参与者与用例之间的关系

1.关联关系(Association)
参与者与用例之间通常用关联关系来描述。
参与者与用例之间的关联关系通常使用带箭头的实线(Rational Rose)或不带箭头的实线(EA)来表示 .
例:
这里写图片描述
2.泛化关系(Generalization)
一个用例可以被特别列举为一个或多个子用例,这被称为用例泛化。用例间的泛化关系和类间的泛化关系类似,就是说在用例泛化中,子用例表示父用例的特殊形式。子用例从父用例处继承行为和属性,还可以添加行为或覆盖、改变已集成的行为。当系统中具有一个或多个用例是一般用例的特化时,就使用用例泛化。
用例间的泛化关系通常使用带空心箭头的实现表示,箭头的方向由子用例指向父用例。
例:
这里写图片描述
3.包含关系(Include)
包含指的是其中一个用例(基础用例)的行为包含了另一个用例(包含用例)的行为。基础用例可以看到包含用例,并依赖于包含用例的执行结果。但是二者不能询问对方的属性。
通常包含关系表示为虚线箭头加《Include》字样,箭头指向被包含的用例。(UML1.1中Include为uses)
例:
这里写图片描述
4.扩展关系(Extend)
一个用例也可以被定义为基础用例的增量扩展,这称作扩展关系。扩展关系是把新行为插入到已有用例的方法。基础用例提供了一组扩展点(Extension Points),在这些扩展点中可以太你就爱新的行为,而扩展用例提供了一组插入片段,这些片段能够被插入到基础用例的扩展点中。
扩展关系表示为虚线箭头加《extend》字样,箭头指向被扩展的用例(基础用例)
例:
这里写图片描述

由于我的机房收费系统跟标准版的不太一样,所以我还是按照自己的系统来画用例图。我的机房是去掉了操作员,直接是管理员和学生或教师登录,另外学生(教师)账户可以自主查询各种记录和充值(只能查看自己的),第一次画,有什么不对或者更好的意见欢迎大家指出。
机房收费系统用例图:
这里写图片描述

作者:vop444 发表于2016/12/15 19:42:24 原文链接
阅读:44 评论:0 查看评论

个人记录-LeetCode 49. Group Anagrams

$
0
0

问题:
Given an array of strings, group anagrams together.

For example, given: [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”],
Return:

[
  ["ate", "eat","tea"],
  ["nat","tan"],
  ["bat"]
]

代码示例:
1、对每个字符串重排序,然后进行比较。

public class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        List<List<String>> rst = new ArrayList<>();

        if (strs == null || strs.length < 1) {
            return rst;
        }

        Map<String, List<String>> tmp = new HashMap<>();

        for (String str : strs) {
            //重排序得到key值
            String key = transfer(str);

            if (tmp.keySet().contains(key)) {
                tmp.get(key).add(str);
            } else {
                List<String> list = new ArrayList<>();
                list.add(str);
                tmp.put(key, list);
            }
        }

        rst.addAll(tmp.values());

        return rst;
    }

    private String transfer(String str) {
        char[] tmp = str.toCharArray();
        //排序构造key值,比较耗时
        Arrays.sort(tmp);
        return new String(tmp);
    }
}

2、本质与1相似,只是采用更高效的方式构造key值。

public class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        List<List<String>> rst = new ArrayList<>();

        if (strs == null || strs.length < 1) {
            return rst;
        }

        Map<Long, List<String>> tmp = new HashMap<>();

        for (String str : strs) {
            Long key = transfer(str);

            if (tmp.keySet().contains(key)) {
                tmp.get(key).add(str);
            } else {
                List<String> list = new ArrayList<>();
                list.add(str);
                tmp.put(key, list);
            }
        }

        rst.addAll(tmp.values());

        return rst;
    }

    //每个字符对应一个质数
    private static int[] prime = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
            31, 41, 43, 47, 53, 59, 61, 67, 71, 73, 
            79, 83, 89, 97, 101, 103};

    private static Long transfer(String str) {
        char[] tmp = str.toCharArray();

        Long key = Long.valueOf(1);
        for (char c : tmp) {
            //key值等于各个字符对应质数的积
            //由于质数无法被拆解,因此仅当两个字符串的构成完全一致时,key才相等
            //唯一的弊端是,当String的长度过长时,long类型可能都不够用
            //这里只是提供一个可行的思路,实在不行可以BigInteger
            key *= prime[c-'a'];
        }

        return key;
    }
}
作者:Gaugamela 发表于2016/12/15 19:59:11 原文链接
阅读:69 评论:0 查看评论

带你快速玩转canvas(4)实战——写个折线图

$
0
0

需求:

1. 自适应父Dom的宽高,但设置canvas元素的最小宽高,小于最小宽高则设置父Dom带滚动条。
2.  窗口大小变化时,重新绘制折线图,以适应新的大小,保证折线图一直以最好效果展现;
3. x轴的坐标尺度为时间,单位是月份,数据类型是数组,数组元素是字符串,格式类似"2016/12"这种,为了方便,假设x轴固定有12个刻度,初始刻度为x=0的位置;
4. y轴尺度为数字,要求跟随数据的最大值而自动变化,以保证良好的可见性,并且需要绘制参考线;
5. 简单来说,参考excel表格自动生成的折线图而绘制。

分析需求:

1. canvas元素要自适配父Dom,因此要设法获得父Dom的大小,然后调整自身的大小;
2. canvas元素的宽高的设置,需要写在html元素中,而不能通过css设置(否则可能出问题),因此应该通过js显式的写出来(例如通过canvas.width这样的方法);
3. 折线图,首先确定xy坐标轴绘制在画布上的范围(比canvas画布小);
4. 然后绘制xy坐标轴;
5. 再确定表示数据的折线的范围,注意,数据折线的范围应该比xy坐标轴的范围要小,且左下角和xy坐标轴的原点重合,如此方能有更好的体验(否则若比如数据折线的最右边和坐标轴的最右边重合,是会容易带来误解的);
6. x轴的时间刻度为12个刻度,且初始刻度为x=0的位置,因此每个刻度的宽度 = 数据折线的总宽度/11;
7. 假如x轴的时间刻度为可变数量,也不难,每个刻度的宽度 = 数据折线总宽度(可确认) / (x轴刻度总数量 - 1) 即可;

8. 比较麻烦的y轴的刻度确认。
9. 首先要确认y轴刻度的最大值,遍历所有数据,取其最大值dataMaxY。
10. 然后将dataMaxY向上取值获得用于计算刻度的最大值MaxY,逻辑是这样的:
    假如第一个数字是8或者9,,那么取比开头数字大1的数字。例如2开头就是3,6开头就是7,然后后面用0补足位数(以确保和原来的最大数值是同一个量级的)
    假如第一个数字是1开头,例如13,那么取比其前两位大的偶数,例如13的话取14,14的话取16,18或19则取20;
11. 根据MaxY来确定y轴的刻度,存储刻度的为数组,数组的元素个数就是刻度的数量。
12. 当MaxY小于10时,固定刻度为2/格,最大10
13. 当MaxY为1开头时,刻度为2/格,最大刻度比MaxY要大;
14. 当MaxY为2~5开头时,刻度为5/格,最大刻度比MaxY大;
15. 当MaxY为6~9开头时,刻度为10/格,最大刻度比MaxY大;
16. 根据刻度数组,以及数据折线图的绘制区域,绘制参考线;
17. 至此y轴刻度和参考线完;

18. 根据刻度数组的最后一个元素(刻度的最大值),以及数据的值,外加数据折线图区域的坐标,可以绘制出折线图;
19. 绘制折线图时可以顺便写下x轴的刻度(x坐标和数据折线图当前数据坐标相同,y坐标固定);
20. 有必要的话,添加输入验证,如果输入错误,则在绘制区域显示错误文字。
21. 添加各种自定义设置,用于设置文字的样式、颜色,宽度大小等;

分拆需求为函数:

1. 获得并设置canvas标签的最小宽高,然后返回canvas中,绘图区域坐标(指x、y坐标轴的绘图坐标);
2. 绘制x、y坐标轴(不包含刻度);
3. 获得y轴最大尺度;
4. 确定y轴刻度数组;
5. 根据y轴刻度数组,绘制参考线和刻度的数字;
6. 绘制数据折线图和x坐标刻度;
7. 绘图函数,需要重新绘制图时,通过本方法来调用以上方法(本接口暴露出来可被外界调用);
8. 输入检查函数,用于提示错误;
9. 刷新函数,用于在窗口大小变化时主动调用绘图函数重新绘图。
10. 【测试用】自动生成数据的函数;

源代码如下:

<html>
<head>
    <meta charset="UTF-8">
    <title>用Canvas绘制折线图</title>
    <style>
        #test {
            border: 1px solid black;
            height: 100%;
            width: 100%;
            box-sizing: border-box;
        }
    </style>
</head>
<body>
<div id="test"></div>
<script>
    var dom = document.getElementById("test");    //数据源,这里是模拟生成的
    //数据生成函数
    var caseInfo = (function () {
        var caseInfo = [];
        //最大值,实际使用中不要用这一条,这个只是用来生成数据的
        var max = Math.pow(10, parseInt(Math.random() * 5));

        for (let i = 0; i < 12; i++) {
            caseInfo.push(parseInt(max * Math.random()));
        }
        console.log(caseInfo);
        return caseInfo;
    })();
    //    var caseInfo = [0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0];

    var dateText = ["2014/2", "2014/3", "2014/4", "2014/5", "2014/6", "2014/7", "2014/8", "2014/9", "2014/10", "2014/11", "2014/12", "2015/1"];

    var draw = new drawCanvas(dom, caseInfo, dateText);

    //  绘图函数的类,传入的参数依次为,canvas标签应该被放置的父Dom,数据,时间
    //  1、父dom:支持自适应,最小宽高(此时会设置父dom的overflow为auto)
    //  2、数据:标准的为12个数据,类型为number,不足12个会自动用0填充满(填充在数组的开始部分);
    //  3、时间要求格式为:年份/月份,例如2016/12,类型为字符串,非该格式的会被识别为错误并报错(如需修改请自行更改相关判断部分);
    //  4、y轴坐标的刻度会根据数据的最大值而自动变化;
    //  5、x轴坐标的刻度自动设为12格
    function drawCanvas(Dom, caseInfoArray, dateTextArray) {
        //  设置
        var color = {
            xyAxisLine: "#000", //x、y坐标轴的颜色
            xScale: "#000",      //x轴刻度文字的颜色
            yScale: "#000",    //y轴刻度文字的颜色
            referenceLine: "#bbb",  //参考线带颜色
            dataLine: "#f6793c",     //数据线条的颜色
            errorMessage: "#000"    //错误提示的颜色
        };
        var font = {
            yScale: "Microsoft YaHei 12px",  //y轴刻度文字
            xScale: "Microsoft YaHei 12px"    //x轴刻度文字
        };
        var dataLineWidth = 3;   //数据线条的宽度
        var error = {
            errorCaseInfo: "错误的数据",
            errorCaseTpye: "数据类型不是数字,无法绘图",
            errorDate: "错误的时间输入"
        }
        //  设置完

        //获取基础数据
        var canvas = document.createElement("canvas");
        Dom.appendChild(canvas);
        var ctx = canvas.getContext("2d");
        var caseInfo = caseInfoArray;
        var dateText = dateTextArray;

        //获得并设置canvas标签的最小宽高,然后返回canvas中,绘图区域坐标
        var setWidthWithHeight = function (Dom, canvas) {
            //在dojo中,用aspect.after改造
            //    window.onresize,每次触发事件时重置一次,并且绘制一次
            //获得画布区域的宽度和高度,并重置
            if (Dom.clientWidth < 700) {
                canvas.width = 700;
                Dom.style.overflowX = "auto";
            } else {
                canvas.width = Dom.clientWidth;
            }
            if (Dom.clientHeight < 250) {
                canvas.height = 250;
                Dom.style.overflowY = "auto";
            } else {
                canvas.height = Dom.clientHeight;
            }

            //坐标轴区域
            //注意,实际画折线图区域还要比这个略小一点
            return {
                x: 60 - 0.5,    //坐标轴在canvas上的left坐标
                y: 40 - 0.5,    //坐标轴在canvas上的top坐标
                maxX: canvas.width - 60.5,   //坐标轴在canvas上的right坐标
                maxY: canvas.height - 40.5   //坐标轴在canvas上的bottom坐标
            };
        }

        //  绘制x、y坐标轴(不包含刻度)
        var drawAxis = function (ctx, axis) {
            ctx.beginPath();
            ctx.lineWidth = 1;
            ctx.strokeStyle = color.xyAxisLine;
            ctx.moveTo(axis.x, axis.maxY);
            ctx.lineTo(axis.x, axis.y);
            ctx.lineTo(axis.x - 5, axis.y + 5);
            ctx.moveTo(axis.x, axis.y);
            ctx.lineTo(axis.x + 5, axis.y + 5);
            ctx.stroke();

            //  再画X轴
            ctx.beginPath();
            ctx.lineWidth = 1;
            ctx.strokeStyle = color.xyAxisLine;
            ctx.moveTo(axis.x, axis.maxY);
            ctx.lineTo(axis.maxX, axis.maxY);
            ctx.lineTo(axis.maxX - 5, axis.maxY + 5);
            ctx.moveTo(axis.maxX, axis.maxY);
            ctx.lineTo(axis.maxX - 5, axis.maxY - 5);
            ctx.stroke();

            // 写y轴原点的数字(注意,虽然是坐标原点,但这个是y轴的)
            ctx.font = font.yScale;
            ctx.textAlign = "right";
            ctx.fillStyle = color.referenceLine;
            // 设置字体内容,以及在画布上的位置
            ctx.fillText("0", axis.x - 5, axis.maxY);
        }

        // 获得Y轴的最大尺度
        var getMAXrectY = function (caseInfo) {
            var theMaxCaseInfo = 0;
            //用于获取最大值
            caseInfo.forEach(function (item) {
                if (item > theMaxCaseInfo) {
                    theMaxCaseInfo = item;
                }
            });

            //返回计算出的最大数字
            return (function (str) {
                var number = null;
                //用于计量坐标轴y轴的最大数字
                if (str[0] == 1) {
                    if (str[0] + str[1] >= 18) {
                        number = '20';
                    } else {
                        if (Number(str[1]) % 2) {
                            number = str[0] + String(Number(str[1]) + 1);
                        } else {
                            number = str[0] + String(Number(str[1]) + 2);
                        }
                    }
                    for (let i = 2; i < str.length; i++) {
                        number += '0';
                    }
                } else {
                    number = String(Number(str[0]) + 1);
                    for (let i = 1; i < str.length; i++) {
                        number += '0';
                    }
                }
                return number;
            })(String(theMaxCaseInfo));
        }

        //划线和确定单元格的逻辑在这里,逻辑确定好后是将单元格放在rectYArray这个数组中
        var getDrawYLineLaw = function (MAXrectY) {
            var rectYArray = [];
            //当最大案件数小于等于10时,以2为一格
            if (MAXrectY <= 10) {
                console.log(MAXrectY);
                rectYArray.push(2, 4, 6, 8, 10);
            } else {
                var str = String(MAXrectY);
                var zeroNumber = MAXrectY.length - 2;

                //  用于填充的0的数量,原因是判断时只判断前一位或两位
                var fillZero = String(Math.pow(10, zeroNumber)).replace('1', '');

                //  然后先判断首位,如果是1,则最大是之前获取到的最大数值,以2/格为单位
                //  如果是2~5,则以5/格为单位
                //  如果是6~9,则以10/格为单位
                if (Number(str[0]) === 1) {
                    for (var i = 0; i < Number(str[0] + str[1]); i = i + 2) {
                        rectYArray.push(i + 2 + fillZero);
                    }
                } else if (Number(str[0]) >= 2 && Number(str[0]) < 6) {
                    for (var i = 0; i < Number(str[0] + str[1]); i = i + 5) {
                        rectYArray.push(i + 5 + fillZero);
                    }
                } else if (Number(str[0]) >= 6 && Number(str[0]) < 10) {
                    for (var i = 0; i < Number(str[0] + str[1]); i = i + 10) {
                        rectYArray.push(i + 10 + fillZero);
                    }
                }
            }
            console.log(rectYArray);
            return rectYArray;
        }

        //画y轴参考线和坐标数字
        var DrawYLine = function (ctx, axis, YLineLaw) {
            //  在得到单元格后,开始绘图,绘出y轴上每个单元格的直线
            //  Y轴参考线的x坐标是从0到axis.maxX - 10

            var yMaxPoint = axis.y + 20;    //最上面的y轴坐标
            var xMaxPoint = axis.maxX - 10; //最右边的x轴坐标
            ctx.strokeStyle = color.referenceLine;
            for (let i = 0; i < YLineLaw.length; i++) {
                ctx.beginPath();
                //  当前绘制线条的y坐标
                let yLine = (YLineLaw[i] - YLineLaw[0] ) / YLineLaw[YLineLaw.length - 1] * (axis.maxY - yMaxPoint) + yMaxPoint;
                ctx.moveTo(axis.x, yLine);
                ctx.lineTo(xMaxPoint, yLine);
                ctx.stroke();
                //绘完线条写文字
                ctx.font = font.yScale;
                ctx.textAlign = "right";
                ctx.fillStyle = color.yScale;
                // 设置字体内容,以及在画布上的位置
                ctx.fillText(YLineLaw[YLineLaw.length - i - 1], axis.x - 5, yLine + 5);
            }
        }

        //绘制数据
        var DrawData = function (ctx, axis, caseInfo, YLineMax, dateText) {
            //  折线绘图区域的x轴从x=0开始绘图,绘制的最右边是axis.maxX-20(参考线是-10)
            //  y轴是从y=0开始绘制,绘制的最顶部是最顶部参考线的位置(axis.y+20)
            //  参数依次为:绘图对象ctx,坐标轴区域坐标axis,绘图用的数据caseInfo,Y轴最大值YLineMax,x轴横坐标文字dateText
            var rect = {
                left: axis.x,           //折线绘图区域的left
                top: axis.y + 20,       //折线绘图区域的top
                height: axis.maxY - axis.y - 20,      //折线绘图区域的bottom
                width: axis.maxX - 20 - axis.x   //折线绘图区域的right
            };
            //绘制数据的折线
            ctx.beginPath();
            ctx.strokeStyle = color.dataLine;
            ctx.lineWidth = dataLineWidth;
            var firstPoint = {
                x: rect.left + 0.5, //之所以+0.5,是因为rect.x来源于axis.x表示划线,因此少了0.5px宽,这里要弥补上
                y: rect.top + (1 - caseInfo[0] / YLineMax) * rect.height + 0.5
            }
//            console.log(firstPoint);
            ctx.moveTo(firstPoint.x, firstPoint.y);
            for (let i = 0; i < caseInfo.length; i++) {
                var point = {
                    x: rect.left + i / 11 * rect.width + 0.5,
                    y: rect.top + (1 - caseInfo[i] / YLineMax) * rect.height
                };
                ctx.lineTo(point.x, point.y);
                //写x轴坐标文字
                ctx.font = font.xScale;
                ctx.textAlign = "center";
                ctx.fillStyle = color.xScale;
                ctx.fillText(dateText[i], point.x, rect.top + rect.height + 15);
            }
            ctx.stroke();
        }

        //错误检查
        var inputError = function () {
            //不是数组
            if (!(caseInfo instanceof Array)) {
                return error.errorCaseInfo;
            }
            //  数组数目不足12,用0填充靠前的部分
            //  大于12,移除前面的部分
            if (caseInfo.length < 12) {
                while (caseInfo.length < 12) {
                    caseInfo.unshift(0);
                }
            } else if (caseInfo.length > 12) {
                while (caseInfo.length > 12) {
                    caseInfo.shift(0);
                }
            }

            //判断数组每个元素的类型是否是number或者能否转换为number
            var checkElementType = caseInfo.every(function (item) {
                //如果强制转换后为NaN,那么
                if (typeof item !== "number") {
                    return false;
                } else {
                    return true;
                }
            })
            if (!checkElementType) {
                return error.errorCaseTpye;
            }

            //  月份应该是字符串,如2016/2
            //  如果被/分割拆分后数组长度不是2,或者拆分后元素0的长度不是4,或者拆分后元素1的长度不是1或2
            //  或者parseInt转换后为NaN
            var checkDateText = dateText.every(function (item) {
                var date = item.split("/");
                if (date.length !== 2 || date[0].length !== 4 || date[1].length < 1 || date[1].length > 2 ||
                        isNaN(parseInt(date[0])) || isNaN(parseInt(date[1]))) {
                    return false;
                } else {
                    return true;
                }
            })
            if (!checkDateText) {
                return error.errorDate
            }
            return false;
        }

        //绘图函数,绘制时调用本函数
        this.toDraw = function () {
            //  设置canvas的Dom的宽高
            var axis = setWidthWithHeight(Dom, canvas);
            //  绘制x、y坐标轴(不包含刻度)
            drawAxis(ctx, axis);
            //如果检测返回false

            //  如果没问题,则返回false,否则值可以隐式转换为true
            var errorMessage = inputError();
            if (errorMessage) {
                ctx.font = "Bold 20px Arial";
                ctx.textAlign = "center";
                ctx.fillStyle = color.errorMessage;
                ctx.fillText(errorMessage, (axis.x + axis.maxX) / 2, (axis.y + axis.maxY) / 2);
                return;
            }
            //  获得Y轴的最大尺度
            var MAXrectY = getMAXrectY(caseInfo);
            //  获得y轴划参考线规则
            var YLineLaw = getDrawYLineLaw(MAXrectY);
            //  绘制Y轴参考线
            DrawYLine(ctx, axis, YLineLaw);
            //  绘制数据
            DrawData(ctx, axis, caseInfo, YLineLaw[YLineLaw.length - 1], dateText);
        };

        //启动本实例时绘图一次
        this.toDraw();
        var self = this;
        //浏览器窗口大小变化时,绘图一次
        //潜在缺点:会覆盖其他的这个方法,建议用jquery的$(window).resize来替代
        window.onresize = function () {
            self.toDraw();
        };
    }

</script>
</body>
</html>
作者:qq20004604 发表于2016/12/15 20:30:23 原文链接
阅读:60 评论:0 查看评论

个人记录-LeetCode 50. Pow(x, n)

$
0
0

问题:
Implement pow(x, n).

代码示例:
1、注意n小于0的情况;
2、合理地递归

public class Solution {
    public double myPow(double x, int n) {
        double rst;
        if (n == 0) {
            rst = 1;
        } else if (n > 0) {
            rst = getPositiveResult(x, n);
        } else {
            rst = 1 / getPositiveResult(x, -n);
        }

        return rst;
    }

    private double getPositiveResult (double x, int n) {
        if (n == 0) {
            return 1;
        }

        return (n%2 == 0) ? getPositiveResult(x*x, n/2) : x*getPositiveResult(x*x, n/2);
    }
}
作者:Gaugamela 发表于2016/12/15 20:47:08 原文链接
阅读:38 评论:0 查看评论

Java入门到精通——调错篇之Spring2.5利用aspect实现AOP时报错: error at ::0 can't find referenced pointcut XXX

$
0
0
一、问题描述及原因。

       利用Aspect注解实现AOP的时候出现了error at ::0 can't find referenced pointcut XXX。一看我以为注解写错了,结果通过查询相关资料是因为Spring2.5与中的aspectjweaver.jar 和aspectjrt.jar这两个jar包与JDK1.7不匹配。

[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'u' defined in file [D:\提高班资料\3.spring\尚学堂马士兵_Spring_00_项目源码\src\Spring_1500_AOP_Annotation\bin\com\bjsxt\dao\impl\UserDAOImpl.class]: Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: error at ::0 can't find referenced pointcut myMethod  
  2.     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:480)  
  3.     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)  
  4.     at java.security.AccessController.doPrivileged(Native Method)  
  5.     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)  
  6.     at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)  
  7.     at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)  
  8.     at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)  
  9.     at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)  
  10.     at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)  
  11.     at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:429)  
  12.     at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:728)  
  13.     at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:380)  
  14.     at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)  
  15.     at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)  
  16.     at com.bjsxt.service.UserServiceTest.testAdd(UserServiceTest.java:14)  
  17.     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)  
  18.     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)  
  19.     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)  
  20.     at java.lang.reflect.Method.invoke(Method.java:606)  
  21.     at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)  
  22.     at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)  
  23.     at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)  
  24.     at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)  
  25.     at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)  
  26.     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)  
  27.     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)  
  28.     at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)  
  29.     at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)  
  30.     at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)  
  31.     at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)  
  32.     at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)  
  33.     at org.junit.runners.ParentRunner.run(ParentRunner.java:309)  
  34.     at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)  
  35.     at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)  
  36.     at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)  
  37.     at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)  
  38.     at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)  
  39.     at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)  
  40. Caused by: java.lang.IllegalArgumentException: error at ::0 can't find referenced pointcut myMethod  
  41.     at org.aspectj.weaver.tools.PointcutParser.parsePointcutExpression(PointcutParser.java:317)  
  42.     at org.springframework.aop.aspectj.AspectJExpressionPointcut.buildPointcutExpression(AspectJExpressionPointcut.java:206)  
  43.     at org.springframework.aop.aspectj.AspectJExpressionPointcut.checkReadyToMatch(AspectJExpressionPointcut.java:193)  
  44.     at org.springframework.aop.aspectj.AspectJExpressionPointcut.getClassFilter(AspectJExpressionPointcut.java:174)  
  45.     at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:195)  
  46.     at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:250)  
  47.     at org.springframework.aop.support.AopUtils.findAdvisorsThatCanApply(AopUtils.java:284)  
  48.     at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply(AbstractAdvisorAutoProxyCreator.java:113)  
  49.     at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findEligibleAdvisors(AbstractAdvisorAutoProxyCreator.java:85)  
  50.     at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean(AbstractAdvisorAutoProxyCreator.java:66)  
  51.     at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:362)  
  52.     at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:325)  
  53.     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:361)  
  54.     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1344)  
  55.     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:473)  
  56.     ... 37 more  


二、解决办法。

      将aspectjweaver.jar 和aspectjrt.jar这两个jar包的版本升级一下用aspectjweaver-1.7.0 和aspectjrt-1.7.2这两个版本的jar包就可以运行成功.

<<jar包下载>>

三、总结。

       在我们开发中我们得注意jdk与各个jar包版本是否匹配有些时候虽然JDK升级了但是我们所要用的jar并没有适配到更高版本的jdk这时候我们就要降级JDK或者用其他已经适配了最新JDK的jar包。




作者:erlian1992 发表于2016/12/15 20:52:23 原文链接
阅读:43 评论:1 查看评论

kafka文档(6)----Introduction-基本介绍

$
0
0

Kafka™ is a distributed streaming platform. What exactly does that mean?


kafka是分布式流式平台,到底是什么意思呢?


We think of a streaming platform as having three key capabilities:

  1. It lets you publish and subscribe to streams of records. In this respect it is similar to a message queue or enterprise messaging system.
  2. It lets you store streams of records in a fault-tolerant way.
  3. It lets you process streams of records as they occur.


一般认为流式平台有三个关键能力:

     1、可以发布以及订阅数据流。这和消息队列或者企业消息系统比较相似

     2、以容错的方式存储数据流

     3、可以及时处理数据流

What is Kafka good for?

It gets used for two broad classes of application:

  1. Building real-time streaming data pipelines that reliably get data between systems or applications
  2. Building real-time streaming applications that transform or react to the streams of data


那么kafka优点何在?

       1.在应用和系统之间建立实时流式数据管道,传输可靠数据。

       2.建立实时流式应用,用来转换数据流或者对数据流做出回应。

To understand how Kafka does these things, let's dive in and explore Kafka's capabilities from the bottom up.

First a few concepts:

  • Kafka is run as a cluster on one or more servers.
  • The Kafka cluster stores streams of records in categories called topics.
  • Each record consists of a key, a value, and a timestamp.


想要理解kafka这些优点,需要从基础开始探索kafka的能力。

首先来看一些概念:

    -kafka以集群方式运行在一个或多个servers上

    -kafka集群以topic分类存储数据流

    -每条记录包含一个key,一个value,以及一个时间戳

Kafka has four core APIs:

  • The Producer API allows an application to publish a stream records to one or more Kafka topics.
  • The Consumer API allows an application to subscribe to one or more topics and process the stream of records produced to them.
  • The Streams API allows an application to act as a stream processor, consuming an input stream from one or more topics and producing an output stream to one or more output topics, effectively transforming the input streams to output streams.
  • The Connector API allows building and running reusable producers or consumers that connect Kafka topics to existing applications or data systems. For example, a connector to a relational database might capture every change to a table.
kafka包括以下四类核心APIs:

    -Producer API:支持应用将数据发布到一个或者多个topics上

    -Consumer API:允许应用订阅一个或者多个topics

    -Streams API:支持应用作为一个流式处理器,从一个或者多个topics中获取数据,然后将输出流发送到一个或者多个topics

    -Connector API:可以建立并运行可重用的生产者或者消费者,用来连接kafka topics与应用或者数据系统。例如,针对关系型数据库的连接器可以捕捉数据表的任何变化


In Kafka the communication between the clients and the servers is done with a simple, high-performance, language agnostic TCP protocol. This protocol is versioned and maintains backwards compatibility with older version. We provide a Java client for Kafka, but clients are available in many languages.

kafka中,clients与servers之间通过简单的、高性能的、与编程语言无关的TCP协议。此协议具有版本号,同时是向后兼容老版本的。官方提供kafka的java版本客户端,同时clients也有其他语言版本的

Topics and Logs

Let's first dive into the core abstraction Kafka provides for a stream of records—the topic.

A topic is a category or feed name to which records are published. Topics in Kafka are always multi-subscriber; that is, a topic can have zero, one, or many consumers that subscribe to the data written to it.


首先来深入了解一下kafka有关数据流的核心抽象-topic。

topic是类别或者信息流名字,用来发布数据的。kafka中的topics的订阅者可能有多个模式;即,topic可以有0个、一个、或者多个consumers。

For each topic, the Kafka cluster maintains a partitioned log that looks like this:

对于每个topic,kafka集群维护一个分区log,如下所示:


Each partition is an ordered, immutable sequence of records that is continually appended to—a structured commit log. The records in the partitions are each assigned a sequential id number called the offset that uniquely identifies each record within the partition.

The Kafka cluster retains all published records—whether or not they have been consumed—using a configurable retention period. For example, if the retention policy is set to two days, then for the two days after a record is published, it is available for consumption, after which it will be discarded to free up space. Kafka's performance is effectively constant with respect to data size so storing data for a long time is not a problem.


每个partition都是有序,不可更改的纪录序列,可以持续的向后追加新纪录--一个结构化的提交日志。partitions中的每条记录都会分配一个独一无二的id数字-offset,用来唯一的标识partition中的每条记录。

kafka集群保存所有发布记录-无论是否消耗过-可以配置保存时长。例如,如果删除策略设置为两天,则发布后两天之内是可以消费的,两天之后则会删除并释放空间。kafka可以高效的保存大量的数据。


In fact, the only metadata retained on a per-consumer basis is the offset or position of that consumer in the log. This offset is controlled by the consumer: normally a consumer will advance its offset linearly as it reads records, but, in fact, since the position is controlled by the consumer it can consume records in any order it likes. For example a consumer can reset to an older offset to reprocess data from the past or skip ahead to the most recent record and start consuming from "now".

This combination of features means that Kafka consumers are very cheap—they can come and go without much impact on the cluster or on other consumers. For example, you can use our command line tools to "tail" the contents of any topic without changing what is consumed by any existing consumers.

The partitions in the log serve several purposes. First, they allow the log to scale beyond a size that will fit on a single server. Each individual partition must fit on the servers that host it, but a topic may have many partitions so it can handle an arbitrary amount of data. Second they act as the unit of parallelism—more on that in a bit.


实际上,每个consumer需要保存的元数据信息就是当前消耗的offset。offset需要由consumer控制,consumer一般是随着读取记录线性增大自己的offset,但是,实际上,正是由于consumer自己控制offset,所以consumer可以选择从任意位置读取日志。例如,consumer可以重读已经处理过的数据并重新处理,或者可以跳过某些数据并从现在最新的数据开始读取。

这些特征意味着:kafka consumers资源耗费比较少-它们可以来回读取日志,而不会给集群或者其他consumers造成很大的影响。例如,可以使用官方提供的命令行工具去获取任何topics的最新日志,这不需要对现存的consumers做任何操作。

partitions的存在有多个目的。首先,可以使每个topic的日志空间超出单个server的最大空间。但是,每个独立的partition的最大空间就是它宿主机的最大空间,但是每个topic都可以有多个partitions,这样就可以存储巨量的数据。其次,partitions作为并行处理的单元-对并行处理能力的增加是非常显著的。

Distribution

The partitions of the log are distributed over the servers in the Kafka cluster with each server handling data and requests for a share of the partitions. Each partition is replicated across a configurable number of servers for fault tolerance.

Each partition has one server which acts as the "leader" and zero or more servers which act as "followers". The leader handles all read and write requests for the partition while the followers passively replicate the leader. If the leader fails, one of the followers will automatically become the new leader. Each server acts as a leader for some of its partitions and a follower for others so load is well balanced within the cluster.


分布式

日志的partitions分布在kafka集群中不同servers上,这样每个server就可以分别处理一部分partitions的请求。每个partitions备份数目是可配置的,用来容错。

每个partition都有一个leader以及0个或者多个followers,leaders处理所有读写请求,followers只对leaders进行备份操作。一旦leaders不可用,某个followers就会自动成为新leader。每个server都会作为某些partitions的leader,同时又作为某些partitions的followers,主要目的是为了集群的负载均衡。

Producers

Producers publish data to the topics of their choice. The producer is responsible for choosing which record to assign to which partition within the topic. This can be done in a round-robin fashion simply to balance load or it can be done according to some semantic partition function (say based on some key in the record). More on the use of partitioning in a second!


生产者发布数据到topics。生产者负责将数据发布到topic下的哪些partitions上。可以使用轮询方式进行负载均衡,或者可以通过某些语义分区函数(基于数据中的某些关键字)进行负载均衡。多数使用第二种方式进行数据分发。

Consumers

Consumers label themselves with a consumer group name, and each record published to a topic is delivered to one consumer instance within each subscribing consumer group. Consumer instances can be in separate processes or on separate machines.

If all the consumer instances have the same consumer group, then the records will effectively be load balanced over the consumer instances.

If all the consumer instances have different consumer groups, then each record will be broadcast to all the consumer processes.


消费者

consumer使用consumer group名字标识它们自己,topic中任何一条记录都只能由每个consumer group中某个consumer实例消费。同一个consumer group的consumer实例可以在不同的进程中,也可以在不同的机器上。

如果所有consumer实例都在同一个consumer group,记录可以在consumer实例之间达到负载均衡。

如果所有consumer实例都在不同的consumer groups,则每条记录都可以由所有consumer实例所消费。


A two server Kafka cluster hosting four partitions (P0-P3) with two consumer groups. Consumer group A has two consumer instances and group B has four.

More commonly, however, we have found that topics have a small number of consumer groups, one for each "logical subscriber". Each group is composed of many consumer instances for scalability and fault tolerance. This is nothing more than publish-subscribe semantics where the subscriber is a cluster of consumers instead of a single process.

The way consumption is implemented in Kafka is by dividing up the partitions in the log over the consumer instances so that each instance is the exclusive consumer of a "fair share" of partitions at any point in time. This process of maintaining membership in the group is handled by the Kafka protocol dynamically. If new instances join the group they will take over some partitions from other members of the group; if an instance dies, its partitions will be distributed to the remaining instances.

Kafka only provides a total order over records within a partition, not between different partitions in a topic. Per-partition ordering combined with the ability to partition data by key is sufficient for most applications. However, if you require a total order over records this can be achieved with a topic that has only one partition, though this will mean only one consumer process per consumer group.


包含两台server的kafka集群存储了四个partitions(p0-p3),由两个consumer groups消费。consumer group A有两个consumer 实例,consumer group B有四个。

更普遍的情况是,topics的consumer groups数目比较少,每个consumers都是topics逻辑上的订阅者。每个group都由很多consumer实例组成,目的是为了扩展性和容错。相比发布-订阅语义模式,kafka这种模式中,订阅者是consumers的集群而不是一个单独的进程。

kafka中消费方式是:多个consumers消费多个partitions,目的是为了使每个实例在任何时刻都可以“相对公平”的独享某个partition。组中成员关系是根据kafka协议动态维护的。如果新实例加入到消费组,这些新实例将会接管组中原有成员的某些partitions;如果某个实例不可用了,它的partitions会分发给其他可用实例上。

kafka只能保证partition内部的数据是有序的,不能保证同一个topics内不同partitions之间数据的顺序。每个partition内部有序以及可以通过key分发数据到不同的partition,对于大多数应用来说足够了。然而,如果想要所有数据都是有序的,可以设置topic只有一个partition,同时也就意味着每个consumer group只有一个consumer实例。


Guarantees

At a high-level Kafka gives the following guarantees:

  • Messages sent by a producer to a particular topic partition will be appended in the order they are sent. That is, if a record M1 is sent by the same producer as a record M2, and M1 is sent first, then M1 will have a lower offset than M2 and appear earlier in the log.
  • A consumer instance sees records in the order they are stored in the log.
  • For a topic with replication factor N, we will tolerate up to N-1 server failures without losing any records committed to the log.

More details on these guarantees are given in the design section of the documentation.


保证

high-level的kafka提供以下保证:

  -消息在topic-partition上的存储顺序是按照他们发送的顺序来的。即,M1和M2由相同producer发送,其中先发送M1,则M1在log中offset要早于M2

  -consumer实例获取消息的顺序和消息在log中顺序相同

  -某个topic的备份数目为N,则容错能力为可以承受N-1个server出现不可用而不会丢失任何已经提交到日志的数据

更多的细节需要查看文档的设计说明部分


Kafka as a Messaging System

How does Kafka's notion of streams compare to a traditional enterprise messaging system?

Messaging traditionally has two models: queuing and publish-subscribe. In a queue, a pool of consumers may read from a server and each record goes to one of them; in publish-subscribe the record is broadcast to all consumers. Each of these two models has a strength and a weakness. The strength of queuing is that it allows you to divide up the processing of data over multiple consumer instances, which lets you scale your processing. Unfortunately, queues aren't multi-subscriber—once one process reads the data it's gone. Publish-subscribe allows you broadcast data to multiple processes, but has no way of scaling processing since every message goes to every subscriber.

The consumer group concept in Kafka generalizes these two concepts. As with a queue the consumer group allows you to divide up processing over a collection of processes (the members of the consumer group). As with publish-subscribe, Kafka allows you to broadcast messages to multiple consumer groups.


Kafka作为一个消息系统

kafka和传统企业消息系统相比有哪些独特之处?

传统消息系统有两个模型:队列模式发布-订阅模式。队列模式中,消费组从server中读取数据,每条数据只能由某个消费者读取。发布-订阅模式中,每条数据都是广播到所有consumers。这两种模型都有各自的优缺点。队列模式的优势在于可以将数据分发到不同的consumer实例,这可以扩展处理性能。不好的是,队列模式不能有多个订阅者--一旦某个进程读过某条消息,这条消息就没了。发布-订阅模式允许广播数据到多个进程,但是正是忧郁每条数据都会广播到每个订阅者,所以没有办法扩展处理能力。

kafka的consumer group概念引申出两个概念。作为队列时,consumer group支持多个进程的集合同时分担数据处理。作为发布-订阅模式时,kafka支持发布消息到多个consumer groups。


The advantage of Kafka's model is that every topic has both these properties—it can scale processing and is also multi-subscriber—there is no need to choose one or the other.

Kafka has stronger ordering guarantees than a traditional messaging system, too.

A traditional queue retains records in-order on the server, and if multiple consumers consume from the queue then the server hands out records in the order they are stored. However, although the server hands out records in order, the records are delivered asynchronously to consumers, so they may arrive out of order on different consumers. This effectively means the ordering of the records is lost in the presence of parallel consumption. Messaging systems often work around this by having a notion of "exclusive consumer" that allows only one process to consume from a queue, but of course this means that there is no parallelism in processing.


kafka模式的优点在于每个topic都有这两个特征-既可以扩展处理能力又可以支持多个订阅者---不需要选择这个却不得不舍弃那个。

kafka要比传统消息系统有更强的次序保证。

传统队列在server按照次序存储数据,同时如果多个consumers从队列中消费数据,那么server会按照存储的顺序发出数据。然而,尽管server按照顺序发出数据,数据以异步的方式发送到consumers,则数据到达不同consumers的次序可能是无序的。这就意味着消息的次序在并行消费中丢失了。消息系统通常采用consumer独占队列的方式来保证次序性,但是这也就意味失去了并行处理能力。


Kafka does it better. By having a notion of parallelism—the partition—within the topics, Kafka is able to provide both ordering guarantees and load balancing over a pool of consumer processes. This is achieved by assigning the partitions in the topic to the consumers in the consumer group so that each partition is consumed by exactly one consumer in the group. By doing this we ensure that the consumer is the only reader of that partition and consumes the data in order. Since there are many partitions this still balances the load over many consumer instances. Note however that there cannot be more consumer instances in a consumer group than partitions.


kafka这方面做得比较好。通过并行处理机制-partition-在topic内部,kafka可以提供次序保证以及consumer 进程组之间负载均衡。这是通过分配topic的每个partition给consumer group中某个consumer,这样可以保证一个partition只会被一个consumer消费。这样就保证了consumer是partition唯一的消费者,即可以获得有序的消息。由于有多个partitions存在,也就需要多个consumer实例来保证负载均衡。注意,同一个consumer group中consumers实例的个数不能多于partitions的数目


Kafka as a Storage System

Any message queue that allows publishing messages decoupled from consuming them is effectively acting as a storage system for the in-flight messages. What is different about Kafka is that it is a very good storage system.

Data written to Kafka is written to disk and replicated for fault-tolerance. Kafka allows producers to wait on acknowledgement so that a write isn't considered complete until it is fully replicated and guaranteed to persist even if the server written to fails.

The disk structures Kafka uses scale well—Kafka will perform the same whether you have 50 KB or 50 TB of persistent data on the server.

As a result of taking storage seriously and allowing the clients to control their read position, you can think of Kafka as a kind of special purpose distributed filesystem dedicated to high-performance, low-latency commit log storage, replication, and propagation.


Kafka作为一个存储系统

任何消息队列如果允许发布消息和消费消息分离的话,则对于不断产生的消息来说可以充当一个存储系统。kafka的不同之处就是它也是一个非常好的存储系统。

写入kafka的数据最终会落到磁盘上,同时备份以供容错。kafka允许producers等待server的写入确认消息,以便确认数据写入是否完成,直到数据备份完成,提高容错能力。

kafka使用的磁盘结构扩展性也很好-server上无论是50kb还是50tb的持久话数据,kafka性能都是相同的。

kafka存储能力的优越性以及允许客户端控制它们自己的读取位置,可以将kafka作为一个特定目的的分布式文件系统:高性能、低延迟提交日志的存储系统、可备份、扩展能力突出。


Kafka for Stream Processing

It isn't enough to just read, write, and store streams of data, the purpose is to enable real-time processing of streams.

In Kafka a stream processor is anything that takes continual streams of data from input topics, performs some processing on this input, and produces continual streams of data to output topics.

For example, a retail application might take in input streams of sales and shipments, and output a stream of reorders and price adjustments computed off this data.


kafka作为流式处理

只是读写以及存储数据流是不够的,kafka还有一个功能是支持实时流式处理。

在kafka中,流式处理器可以从输入topics获取持续的数据流,对这些输入数据执行某些操作,同时生产持续的数据流到输出topics。

例如,一个零售应用可能获取销售和运输的输入流,然后输出由这些数据计算得出的重排序以及价格的输出流。


It is possible to do simple processing directly using the producer and consumer APIs. However for more complex transformations Kafka provides a fully integrated Streams API. This allows building applications that do non-trivial processing that compute aggregations off of streams or join streams together.

This facility helps solve the hard problems this type of application faces: handling out-of-order data, reprocessing input as code changes, performing stateful computations, etc.

The streams API builds on the core primitives Kafka provides: it uses the producer and consumer APIs for input, uses Kafka for stateful storage, and uses the same group mechanism for fault tolerance among the stream processor instances.


使用producer和consumer APIs做简单的处理是可以的。然而对于更复杂的转换来说,kafka也提供了完整工具集合-Streams API。这将支持创建以下应用:即做一些非琐碎的处理,计算数据聚合度或者聚合数据。

这些能力将有助于解决一些应用遇到的这些复杂问题:处理无序数据,当代码改变时重新处理数据,执行有状态的计算。

数据流API构建了kafka所能提供的核心基础能力:它使用producer以及consumer APIs处理输入数据,使用kafka作为有状态的存储,使用相同的组机制做容错。


Putting the Pieces Together

This combination of messaging, storage, and stream processing may seem unusual but it is essential to Kafka's role as a streaming platform.

A distributed file system like HDFS allows storing static files for batch processing. Effectively a system like this allows storing and processing historical data from the past.

A traditional enterprise messaging system allows processing future messages that will arrive after you subscribe. Applications built in this way process future data as it arrives.

Kafka combines both of these capabilities, and the combination is critical both for Kafka usage as a platform for streaming applications as well as for streaming data pipelines.


消息传递、存储以及流式处理这些功能一块来看的话时非同寻常的,同时kafka作为流式平台这个角色是最基本的。

类似HDFS的分布式文件系统允许存储静态文件,以支持批量处理。像这样的系统一般可以有效的支持存储盒处理历史的数据。

传统企业消息系统支持处理在你订阅之后到达的数据。内置这种方式的应用可以处理以后到达的数据。

kafka包括以上这些能力,同时这些能力的综合对于kafka作为流式处理平台以及流式处理管道来说非常关键。


By combining storage and low-latency subscriptions, streaming applications can treat both past and future data the same way. That is a single application can process historical, stored data but rather than ending when it reaches the last record it can keep processing as future data arrives. This is a generalized notion of stream processing that subsumes batch processing as well as message-driven applications.

Likewise for streaming data pipelines the combination of subscription to real-time events make it possible to use Kafka for very low-latency pipelines; but the ability to store data reliably make it possible to use it for critical data where the delivery of data must be guaranteed or for integration with offline systems that load data only periodically or may go down for extended periods of time for maintenance. The stream processing facilities make it possible to transform data as it arrives.

For more information on the guarantees, apis, and capabilities Kafka provides see the rest of the documentation.


通过整合存储以及低延迟提交,流式处理应用可以以相同的方式处理过去的和将来的数据。一个单独应用可以处理历史的、存储的数据而不是当处理完数据之后就结束,当再有数据到来时,它依然可以处理。流式处理的通用概念包括批处理和消息驱动的应用。

就像流式数据管道,实时事件提交使得将kafka作为低延迟管道称为可能。但是存储数据可靠性的能力也提高以下面方式使用kafka的可能性:即必须保证数据的可靠性或者离线系统周期性加载数据或者维护期间的宕机。流式处理能力可以尽快的转发到达的数据。

更多有关保证、apis、以及kafka提供的能力请看以下documentation

































































作者:beitiandijun 发表于2016/12/15 20:58:20 原文链接
阅读:23 评论:0 查看评论

个人记录-LeetCode 51. N-Queens

$
0
0

问题:
The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.

Given an integer n, return all distinct solutions to the n-queens puzzle.
Each solution contains a distinct board configuration of the n-queens’ placement, where ‘Q’ and ‘.’ both indicate a queen and an empty space respectively.

For example,
There exist two distinct solutions to the 4-queens puzzle:

[
 [".Q..",  // Solution 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // Solution 2
  "Q...",
  "...Q",
  ".Q.."]
]

代码示例:
暴力递归即可

public class Solution {
    public List<List<String>> solveNQueens(int n) {
        //保存结果
        List<List<String>> rst = new ArrayList<>();

        //n == 2时,无解
        if (n > 2) {
            //bitMap保存当前位置是否放置了皇后
            //true表示放置
            boolean[][] bitMap = new boolean[n][n];

            //暴力递归
            getResult(rst, bitMap, n, 0);
        } else if (n == 1){
            //n == 1时,单一解
            List<String> tmp = new ArrayList<>();
            tmp.add("Q");

            rst.add(tmp);
        }
        return rst;
    }

    //now用于表示当前行
    //我们一行一行的递归
    private void getResult(List<List<String>> rst, boolean[][] bitMap, int n, int now) {
        if (now == n) {
            //保存结果
            storeResult(rst, bitMap, n);
            return;
        }

        for (int j = 0; j < n; ++j) {
            //flag置为true时,表示该位置不能放置
            boolean flag = false;

            //只需要判断当前行之上,相同列是否放置了Queue
            for (int i = 0; i < now; ++i) {
                if (bitMap[i][j]) {
                    flag = true;
                    break;
                }
            }

            //判断当前位置之上,左对角线是否有Queue
            int curRow = now - 1;
            int curCol = j - 1;
            while (curRow >= 0 && curCol >= 0) {
                if (bitMap[curRow][curCol]) {
                    flag = true;
                    break;
                }
                --curRow;
                --curCol;
            }

            //判断当前位置之上,右对角线是否有Queue
            curRow = now - 1;
            curCol = j + 1;
            while (curRow >= 0 && curCol < n) {
                if (bitMap[curRow][curCol]) {
                    flag = true;
                    break;
                }
                --curRow;
                ++curCol;
            }

            //不能放置,测试下一个位置
            if (flag) {
                continue;
            }

            //能放置,修改bitMap
            bitMap[now][j] = true;
            //放置下一行
            getResult(rst, bitMap, n, now + 1);

            //复位,测试下一个位置
            bitMap[now][j] = false;
        }
    }

    //将bitMap转换为实际结果
    private void storeResult(List<List<String>> rst, boolean[][] bitMap, int n) {
        List<String> l = new ArrayList<>();
        for (int i = 0; i < n; ++i) {
            StringBuilder sb = new StringBuilder();
            for (int j = 0; j < n; ++j) {
                char tmp = bitMap[i][j] ? 'Q' : '.';
                sb.append(tmp);
            }
            l.add(sb.toString());
        }
        rst.add(l);
    }
}
作者:Gaugamela 发表于2016/12/15 21:37:53 原文链接
阅读:21 评论:0 查看评论

迭代子模式

$
0
0

概述

概念:在阎宏博士的《JAVA与模式》中关于迭代子模式的定义是这样的:迭代子模式又叫游标(Cursor)模式,是对象的行为模式。迭代子模式可以顺序地访问一个聚集中的元素而不必暴露聚集的内部表象(internal representation)。

迭代子模式的意图及组成

迭代子模式有两种实现方式,分别是白箱聚集与外禀迭代子和黑箱聚集于内禀迭代子。

白箱聚集与外禀迭代子

如果一个聚集的接口提供了可以用来修改聚集元素的方法,这个接口就是所谓的宽接口。
如果聚集对象为所有对象提供同一个接口,也就是宽接口的话,当然会满足迭代子模式对迭代子对象的要求。但是,这样会破坏对聚集对象的封装。这种提供宽接口的聚集叫做白箱聚集。聚集对象向外界提供同样的宽接口,如下图所示:
这里写图片描述
由于聚集自己实现迭代逻辑,并向外部提供适当的接口,使得迭代子可以从外部控制聚集元素的迭代过程。这样一来迭代子所控制的仅仅是一个游标而已,这种迭代子叫做游标迭代子(Cursor Iterator)。由于迭代子是在聚集结构之外的,因此这样的迭代子又叫做外禀迭代子(Extrinsic Iterator)。
一个白箱聚集向外界提供访问自己内部元素的接口(称作遍历方法或者Traversing Method),从而使外禀迭代子可以通过聚集的遍历方法实现迭代功能。
  因为迭代的逻辑是由聚集对象本身提供的,所以这样的外禀迭代子角色往往仅仅保持迭代的游标位置。
  一个典型的由白箱聚集与外禀迭代子组成的系统如下图所示:
这里写图片描述

迭代子模式组成

迭代子模式的组成主要有以下几个角色:
抽象迭代子(Iterator)角色:此抽象角色定义出遍历元素所需的接口。
具体迭代子(ConcreteIterator)角色:此角色实现了Iterator接口,并保持迭代过程中的游标位置。
聚集(Aggregate)角色:此抽象角色给出创建迭代子(Iterator)对象的接口。
具体聚集(ConcreteAggregate)角色:实现了创建迭代子(Iterator)对象的接口,返回一个合适的具体迭代子实例。
客户端(Client)角色:持有对聚集及其迭代子对象的引用,调用迭代子对象的迭代接口,也有可能通过迭代子操作聚集元素的增加和删除。

代码实例

抽象聚集角色类
这个角色规定出所有的具体聚集必须实现的接口。迭代子模式要求聚集对象必须有一个工厂方法,也就是createIterator()方法,以向外界提供迭代子对象的实例。

public abstract class Aggregate {  
    /** 
     * 工厂方法,创建相应迭代子对象的接口 
     */  
    public abstract Iterator createIterator();  
} 

具体聚集角色类
实现了抽象聚集角色类所要求的接口,也就是createIterator()方法。此外,还有方法getElement()向外界提供聚集元素,而方法size()向外界提供聚集的大小等。

public class ConcreteAggregate extends Aggregate {  

    private Object[] objArray = null;  
    /** 
     * 构造方法,传入聚合对象的具体内容 
     */  
    public ConcreteAggregate(Object[] objArray){  
        this.objArray = objArray;  
    }  

    @Override  
    public Iterator createIterator() {  

        return new ConcreteIterator(this);  
    }  
    /** 
     * 取值方法:向外界提供聚集元素 
     */  
    public Object getElement(int index){  

        if(index < objArray.length){  
            return objArray[index];  
        }else{  
            return null;  
        }  
    }  
    /** 
     * 取值方法:向外界提供聚集的大小 
     */  
    public int size(){  
        return objArray.length;  
    }  
}  

抽象迭代子角色类

public interface Iterator {  
    /** 
     * 迭代方法:移动到第一个元素 
     */  
    public void first();  
    /** 
     * 迭代方法:移动到下一个元素 
     */  
    public void next();  
    /** 
     * 迭代方法:是否为最后一个元素 
     */  
    public boolean isDone();  
    /** 
     * 迭代方法:返还当前元素 
     */  
    public Object currentItem();  
}  

具体迭代子角色类

public class ConcreteIterator implements Iterator {  
    //持有被迭代的具体的聚合对象  
    private ConcreteAggregate agg;  
    //内部索引,记录当前迭代到的索引位置  
    private int index = 0;  
    //记录当前聚集对象的大小  
    private int size = 0;  

    public ConcreteIterator(ConcreteAggregate agg){  
        this.agg = agg;  
        this.size = agg.size();  
        index = 0;  
    }  
    /** 
     * 迭代方法:返还当前元素 
     */  
    @Override  
    public Object currentItem() {  
        return agg.getElement(index);  
    }  
    /** 
     * 迭代方法:移动到第一个元素 
     */  
    @Override  
    public void first() {  

        index = 0;  
    }  
    /** 
     * 迭代方法:是否为最后一个元素 
     */  
    @Override  
    public boolean isDone() {  
        return (index >= size);  
    }  
    /** 
     * 迭代方法:移动到下一个元素 
     */  
    @Override  
    public void next() {  

        if(index < size)  
        {  
            index ++;  
        }  
    }  

}  

客户端测试类

public class Client {  
    public void operation(){  
        Object[] objArray = {"One","Two","Three","Four","Five","Six"};  
        //创建聚合对象  
        Aggregate agg = new ConcreteAggregate(objArray);  
        //循环输出聚合对象中的值  
        Iterator it = agg.createIterator();  
        while(!it.isDone()){  
            System.out.println(it.currentItem());  
            it.next();  
        }  
    }  
    public static void main(String[] args) {  

        Client client = new Client();  
        client.operation();  
    }  

} 

黑箱聚集与内禀迭代子

如果一个聚集的接口没有提供修改聚集元素的方法,这样的接口就是所谓的窄接口。
  聚集对象为迭代子对象提供一个宽接口,而为其他对象提供一个窄接口。换言之,聚集对象的内部结构应当对迭代子对象适当公开,以便迭代子对象能够对聚集对象有足够的了解,从而可以进行迭代操作。但是,聚集对象应当避免向其他的对象提供这些方法,因为其他对象应当经过迭代子对象进行这些工作,而不是直接操控聚集对象。
这里写图片描述
在JAVA语言中,实现双重接口的办法就是将迭代子类设计成聚集类的内部成员类。这样迭代子对象将可以像聚集对象的内部成员一样访问聚集对象的内部结构。下面给出一个示意性的实现,说明这种双重接口的结构时怎么样产生的,以及使用了双重接口结构之后迭代子模式的实现方案。这种同时保证聚集对象的封装和迭代子功能的实现的方案叫做黑箱实现方案。
  由于迭代子是聚集的内部类,迭代子可以自由访问聚集的元素,所以迭代子可以自行实现迭代功能并控制对聚集元素的迭代逻辑。由于迭代子是在聚集的结构之内定义的,因此这样的迭代子又叫做内禀迭代子(Intrinsic Iterator)。
  为了说明黑箱方案的细节,这里给出一个示意性的黑箱实现。在这个实现里,聚集类ConcreteAggregate含有一个内部成员类ConcreteIterator,也就是实现了抽象迭代子接口的具体迭代子类,同时聚集并不向外界提供访问自己内部元素的方法。

代码实例

抽象聚集角色类

public abstract class Aggregate {
    /**
     * 工厂方法,创建相应迭代子对象的接口
     */
    public abstract Iterator createIterator();
}

抽象迭代子角色类

public interface Iterator {
    /**
     * 迭代方法:移动到第一个元素
     */
    public void first();
    /**
     * 迭代方法:移动到下一个元素
     */
    public void next();
    /**
     * 迭代方法:是否为最后一个元素
     */
    public boolean isDone();
    /**
     * 迭代方法:返还当前元素
     */
    public Object currentItem();
}

具体聚集角色类,实现了抽象聚集角色所要求的接口。

public class ConcreteAggregate extends Aggregate {

    private Object[] objArray = null;
    /**
     * 构造方法,传入聚合对象的具体内容
     */
    public ConcreteAggregate(Object[] objArray){
        this.objArray = objArray;
    }

    @Override
    public Iterator createIterator() {

        return new ConcreteIterator();
    }
    /**
     * 内部成员类,具体迭代子类
     */
    private class ConcreteIterator implements Iterator
    {
        //内部索引,记录当前迭代到的索引位置
        private int index = 0;
        //记录当前聚集对象的大小
        private int size = 0;
        /**
         * 构造函数
         */
        public ConcreteIterator(){

            this.size = objArray.length;
            index = 0;
        }
        /**
         * 迭代方法:返还当前元素
         */
        @Override
        public Object currentItem() {
            return objArray[index];
        }
        /**
         * 迭代方法:移动到第一个元素
         */
        @Override
        public void first() {

            index = 0;
        }
        /**
         * 迭代方法:是否为最后一个元素
         */
        @Override
        public boolean isDone() {
            return (index >= size);
        }
        /**
         * 迭代方法:移动到下一个元素
         */
        @Override
        public void next() {

            if(index < size)
            {
                index ++;
            }
        }
    }
}

客户端测试类

public class Client {

    public void operation(){
        Object[] objArray = {"One","Two","Three","Four","Five","Six"};
        //创建聚合对象
        Aggregate agg = new ConcreteAggregate(objArray);
        //循环输出聚合对象中的值
        Iterator it = agg.createIterator();
        while(!it.isDone()){
            System.out.println(it.currentItem());
            it.next();
        }
    }
    public static void main(String[] args) {

        Client client = new Client();
        client.operation();
    }

}

使用迭代子模式的优点

 (1)迭代子模式简化了聚集的接口。迭代子具备了一个遍历接口,这样聚集的接口就不必具备遍历接口。
  (2)每一个聚集对象都可以有一个或多个迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。因此,一个聚集对象可以同时有几个迭代在进行之中。
  (3)由于遍历算法被封装在迭代子角色里面,因此迭代的算法可以独立于聚集角色变化。

作者:xiangzhihong8 发表于2016/12/15 21:52:02 原文链接
阅读:3 评论:0 查看评论

Eclipse构建Maven的SpringMVC项目

$
0
0

首先Eclipse需要安装Maven的插件,地址:http://m2eclipse.sonatype.org/sites/m2e。

        用MyEclipse安装Maven插件,建出的Maven项目有些问题。一是,发布tomcat的时候resources总是不会被发布到tomcat下;二是,把WEB-INF下的classes改到target下的classes,但是不知道为什么MyEclipse要么仍然在WEB-INF下生成class。要么真不在WEB-INF生成classes了但是发布tomcat的时候,class文件一个都不会给你发布过去,超级郁闷。但是使用Eclipse构建Maven项目后,使用MyEclipse打开就没问题了。

        用maven的好处:我感觉最主要就是自动下载jar包和它所依赖的包,这样可以保证了多人开发时jar版本不同的问题。再就是文件结构清晰,java文件,资源文件,测试文件都分的很清楚。

        将介绍两种方法:一,直接建立Maven项目方法;二、建立Dynamic Web project转成Maven项目方法。

一、直接建立Maven项目方法

1、建立Maven项目

        接下来使用Eclipse的maven构建一个web项目,以构建SpringMVC项目为例:

1.1 选择建立Maven Project

选择File -> New -> Other,在New窗口中选择 Maven -> Maven Project。点击newxt。


 

 

1.2 选择项目路径

Use default Workspace location默认工作空间。


 

 

1.3 选择项目类型

在Artifact Id中选择maven-archetype-webapp


 

 

1.4 输入Group ID和 Artifact ID,以及Package

Group ID一般写大项目名称。Artifact ID是子项目名称。

例如Spring的web包,Group ID:org.springframework,artifactId:spring-web。

Package是默认给你建一个包,不写也可以。


 

 

1.5 刚建立好后的文件结构如下图


 

如果这里显示的内容多,一般是Filters设置的问题。或perspective为JavaEE模式,改成Java模式就可以了。

 

 

2、配置Maven项目

接下来就需要更改好多配置了。

 

2.1 添加Source文件夹

接下来需要添加src/main/java,src/test/java ,src/test/resources三个文件夹。右键项目根目录点击New -> Source Folder,

建出这三个文件夹。注意不是建普通的Folder,而是Source Folder。



 

 

2.2 更改class路径

右键项目,Java Build Path -> Source

下面应该有4个文件夹。src/main/java,src/main/resources,src/test/java ,src/test/resources。

 

双击每个文件夹的Output folder,选择路径。

src/main/java,src/main/resources,选择target/classes;

src/test/java ,src/test/resources, 选择target/test-classes;

 

选上Allow output folders for source folders.

 

 

在此处还要更改:

更改文件夹显示的顺序:点击Order and Export。

更改JDK版本:在Libraries双击JRE System Library,要1.6版本。


 

2.3 把项目变成Dynamic Web项目

 

2.3.1 右键项目,选择Project Facets,点击Convert to faceted from


 

2.3.2 配置Project Facets

 

更改Dynamic Web Module的Version为2.5。(3.0为Java7的)。

如果提示错误,可能需要在Java Compiler设置Compiler compliance level 为1.6。或者需要在此窗口的Java的Version改成1.6。


 

 

2.3.3 配置 Modify Faceted Project

点击Further configuration available…,弹出Modify Faceted Project窗口

此处是设置web.xml文件的路径,我们输入src/main/webapp。

Generate web.xml deployment descriptor自动生成web.xml文件,可选可不选。

 

 

2.4 设置部署程序集(Web Deployment Assembly)

 

上面步骤设置完成后,点击OK,Properties窗口会关闭,在右键项目打开此窗口。在左侧列表中会出现一个Deployment Assembly,点击进去后,如下图:


 

 

此处列表是,部署项目时,文件发布的路径。

1,我们删除test的两项,因为test是测试使用,并不需要部署。

2,设置将Maven的jar包发布到lib下。

Add -> Java Build Path Entries -> Maven Dependencies -> Finish

设置完成效果图


 

 

 

 

 

3、向maven项目中添加jar包

maven可以管理项目依赖的jar包,通过groupID、artifactId以及版本号可以唯一确定一个jar包。这样可以防止老式Web项目中WEB-INF/lib下jar包不一致的问题。并且maven还会自动下载添加进的jar包所依赖的jar包。

 

3.1 在pom.xml中添加所需要的jar包

使用Maven POM editor打开项目中的pom.xml文件,选择Dependencies,在Dependencies栏目点击Add进行,首先弹出一个搜索按钮,例如输入spring-web,就会自动搜索关于spring-web相关的jar包,我们选择3.0.5版本的spring。将spring包全部添加进来。需要添加的其他jar包有:junit、jstl。或者点击pom.xml直接编辑pom.xml文件。这样可以直接copy过来dependencies内容。




3.2设置jar包的scope

 

当添加进入一个jar包后,有一些属性需要设置,最重要的就是scope,它有以下几种取值:

1.         compile,缺省值,适用于所有阶段,会随着项目一起发布。

2.         provided,类似compile,期望JDK、容器或使用者会提供这个依赖。如servlet.jar。

3.         runtime,只在运行时使用,如JDBC驱动,适用运行和测试阶段。

4.         test,只在测试时使用,用于编译和运行测试代码。不会随项目发布。

5.         system,类似provided,需要显式提供包含依赖的jar,Maven不会在 Repository中查找它。

 

 

通常SpringMVC项目所需要配置scope的jar包如下图:


 

有的时候发现servlet-api还是被打包到lib下面了,此时肯定会报错。就需要把maven插件中的WTP也安装一下。

Eclipse在线安装路径:http://m2eclipse.sonatype.org/sites/m2e-extras。选择for Eclipse WTP。


 

 

 

4、构建SpringMVC框架

 

4.1 编辑web.xml文件

 

需要添加log4j,字符过滤,Spring 的dispatcher等。

webx.xml代码如下:

 

Xml代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app xmlns="http://java.sun.com/xml/ns/javaee"   
  3.          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.          xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   
  5.                              http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"   
  6.          version="2.5" >  
  7.       
  8.     <!-- 区分项目名称,防止默认重名 -->  
  9.     <context-param>  
  10.         <param-name>webAppRootKey</param-name>  
  11.         <param-value>maven.example.root</param-value>  
  12.     </context-param>  
  13.   
  14.     <!-- Spring的log4j监听器 -->  
  15.     <listener>  
  16.         <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>  
  17.     </listener>  
  18.   
  19.     <!-- 字符集 过滤器  -->  
  20.     <filter>  
  21.         <filter-name>CharacterEncodingFilter</filter-name>  
  22.         <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  
  23.         <init-param>  
  24.             <param-name>encoding</param-name>  
  25.             <param-value>UTF-8</param-value>  
  26.         </init-param>  
  27.         <init-param>  
  28.             <param-name>forceEncoding</param-name>  
  29.             <param-value>true</param-value>  
  30.         </init-param>  
  31.     </filter>  
  32.     <filter-mapping>  
  33.         <filter-name>CharacterEncodingFilter</filter-name>  
  34.         <url-pattern>/*</url-pattern>  
  35.     </filter-mapping>  
  36.   
  37.     <!-- Spring view分发器 -->  
  38.     <servlet>  
  39.         <servlet-name>dispatcher</servlet-name>  
  40.         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  41.         <init-param>  
  42.             <param-name>contextConfigLocation</param-name>  
  43.             <param-value>/WEB-INF/dispatcher-servlet.xml</param-value>  
  44.         </init-param>  
  45.         <load-on-startup>1</load-on-startup>  
  46.     </servlet>  
  47.     <servlet-mapping>  
  48.         <servlet-name>dispatcher</servlet-name>  
  49.         <url-pattern>*.do</url-pattern>  
  50.     </servlet-mapping>  
  51.   
  52. </web-app>  
 

 

4.2 编写Spring配置文件dispatcher-servlet.xml

 

如要添加MVC驱动、注解检测、视图解析等。dispatcher-servlet.xml代码如下:

 

Xml代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"   
  3.        xmlns:aop="http://www.springframework.org/schema/aop"   
  4.        xmlns:context="http://www.springframework.org/schema/context"  
  5.        xmlns:mvc="http://www.springframework.org/schema/mvc"   
  6.        xmlns:tx="http://www.springframework.org/schema/tx"   
  7.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  8.        xsi:schemaLocation="http://www.springframework.org/schema/aop   
  9.         http://www.springframework.org/schema/aop/spring-aop-3.0.xsd   
  10.         http://www.springframework.org/schema/beans   
  11.         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd   
  12.         http://www.springframework.org/schema/context   
  13.         http://www.springframework.org/schema/context/spring-context-3.0.xsd   
  14.         http://www.springframework.org/schema/mvc   
  15.         http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd   
  16.         http://www.springframework.org/schema/tx   
  17.         http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">  
  18.   
  19.     <mvc:annotation-driven />  
  20.     <context:component-scan base-package="liming.maven.example" />  
  21.   
  22.     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
  23.         <property name="prefix" value="/WEB-INF/views/" />  
  24.         <property name="suffix" value=".jsp" />  
  25.     </bean>  
  26.   
  27. </beans>  
 

 

 

 

4.3 编写一个Controller层测试类

 

编写一个SpringMVC的Controller层测试类。此类只有一个方法做地址映射,并向页面传递一个数据。代码如下:

 

Java代码  收藏代码
  1. package liming.maven.example.view;  
  2.   
  3. import org.springframework.stereotype.Controller;  
  4. import org.springframework.ui.Model;  
  5. import org.springframework.web.bind.annotation.RequestMapping;  
  6.   
  7. @Controller  
  8. public class GeneralController {  
  9.   
  10.     @RequestMapping(value="index.do")  
  11.     public void index_jsp(Model model){  
  12.         model.addAttribute("liming""黎明你好");  
  13.         System.out.println("index.jsp");  
  14.     }  
  15. }  
 

 

 

 

4.4 编写index.jsp页面

首先在src/main/webapp/WEB-INF下建文件夹views。此处和dispatcher-servlet.xml配置文件中的prefix属性路径要一样。

在views下建index.jsp文件

我们使用jstl获取Controlleradd的数据。

Jsp页面代码如下:

 

Html代码  收藏代码
  1. <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>  
  2. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>  
  3. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">  
  4. <html>  
  5.     <head>  
  6.         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  
  7.         <title>Insert title here</title>  
  8.     </head>  
  9.       
  10.     <body>  
  11.         <c:out value="${liming}"></c:out>  
  12.     </body>  
  13. </html>  
 

 

 

 

5,发布到tomcat

这个就没啥说的了。

Eclipse下Tomcat常用设置:http://limingnihao.iteye.com/admin/blogs/825394

 

 

6,测试

访问地址:http://localhost:8080/liming.maven.example/index.do

访问的结果来个截图:


作者:baidu_21578557 发表于2016/12/16 14:51:06 原文链接
阅读:66 评论:0 查看评论

变位词程序的实现

$
0
0

这篇文章是 读厚《编程珠玑》系列博客 的第 2 篇,主要的内容是《编程珠玑》第二章最后提出的变位词程序的实现。

问题简述

问题来源于《编程珠玑》第二章中最后提出的变位词程序的实现。其中的变位词的概念,在第二章开篇的 C 问题中得到了阐释。

C. 给定一个英语词典,找出其中所有变位词的集合。例如,『pots』,『stop』,『tops』互为变位词,因为每一个单词都可以通过改变其他单词中字母的顺序来得到。

我们所实现的变位词程序需要做的就是:将字典读入,然后找出所有的变位词集合

实现思路

本文中的思路就按照文章中介绍的进行实现:

将程序分为三个部分:

  • sign:读入字典,对单词进行『sign』(计算标识符)。计算标识符的过程就是对单词中的字母进行排序。例如:上述三个单词『pots』,『stop』,『tops』的标识符是相同的,都是『opst』
  • sort:将所有具有相同标识符的单词放到一起(这里我们直接使用系统的 sort 程序)
  • squash:将具有相同标识符的单词放到一行打印出来

最后,通过管道将三个程序连接起来:

sign < words.txt | sort | squash > gramlist.txt

最终gramlist.txt中就是我们需要的结果。

代码实现

sign 程序

//
//  sign.c
//  somecode
//
//  Created by 罗远航 on 16/12/2016.
//  Copyright © 2016 罗远航. All rights reserved.
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define WORDMAX 100

int charcomp(const void *x, const void *y){
    return *(char *)x - *(char *)y;
}

int main(){
    char word[WORDMAX], sig[WORDMAX];
    while(scanf("%s",word) != EOF){
        strcpy(sig, word);
        qsort(sig, strlen(sig), sizeof(char), charcomp);
        printf("%s %s\n",sig, word);
    }
}

squash 程序

//
//  squash.c
//  somecode
//
//  Created by 罗远航 on 16/12/2016.
//  Copyright © 2016 罗远航. All rights reserved.
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define WORDMAX 100

int main()
{
    char word[WORDMAX], sig[WORDMAX], oldsig[WORDMAX];
    int linenum = 0;
    strcpy(oldsig, "");
    while (scanf("%s %s", sig, word) != EOF)
    {
        if (strcmp(oldsig, sig) != 0 && linenum > 0)
            printf("\n");
        strcpy(oldsig, sig);
        linenum++;
        printf("%s ", word);
    }
    printf("\n");
    return 0;
}

运行结果

运行程序之后,我找到了几个比较长的变位词:

algorithm's logarithm's 
anatomicopathological pathologicoanatomical 
paradisaically paradisiacally 
……

注:单词表我是在这里下载到的,大概有35万英文单词。


本文的版权归作者 罗远航 所有,采用 Attribution-NonCommercial 3.0 License。任何人可以进行转载、分享,但不可在未经允许的情况下用于商业用途;转载请注明出处。感谢配合!

作者:luoyhang003 发表于2016/12/16 14:59:48 原文链接
阅读:94 评论:0 查看评论

pwnable 笔记 Rookiss - simple login - 50 pt

$
0
0

题目分析

附件下载下来看大小有差不过有1MB,应该是静态链接的程序,送到IDA Pro中查看,果然,函数多的不能自已,主函数的结构也很复杂:
func
还好没有strip,main函数200+行,勉强能分析下来
main
程序的流程如下:

  1. 输入Authenticate,Base64Decode后送至auth()

  2. auth()生成其MD5值并与f87cd601aa7fedca99018a8be88eda34

  3. 若相等则调用correct(),给shell

这里有一个输入长度的判断,如下图:
len

decode后的字符串长度要小于12,这里不妨生成一个长度12的字符串输入进去试试:

ipython

overflow

发现溢出,下面用GDB调试,定位溢出点:
GDB

可以发现0x43434343(CCCC)覆盖了main()EBP,当执行到leave指令时出错了

leave相当于以下两条汇编指令:

指令 含义
MOV ESP, EBP EBP的值赋给ESP,即将栈指针移动至栈底部
POP EBP 弹出保存在栈顶的前一个函数的EBP,即恢复栈桢

这里由于出现溢出,main函数保存在栈中的EBPCCCC覆盖

MOV ESP, EBP指令会把ESP的值赋为0x43434343,而这个地址是不合法的,显然不能作为栈的地址,因此会报错

这个溢出的利用方法简单,把main函数的EBP覆写为一个可以访问的我们可以控制的值即可

解题思路

构造字符串,将main()函数EBP覆写为我们输入的字符串的起点:

input: junk code RET EBP of main
0811EB40: 0xdeadbeef system(“/bin/sh”) addr of input

system("/bin/sh")地址如下:
system

解题过程

  • 控制EBP

    溢出可导致EBP被覆写

  • 控制ESP

    MOV ESP, EBP 使ESP也可控

  • 控制EIP

    POP EBP 将input前4字节弹出栈
    RET 相当于 POP EIP
    input的中间4字节会覆写EIP

解题脚本

#!/usr/bin/python
from pwn import *

# context.log_level = 'debug'
# p = process('./login')
p = remote('pwnable.kr',9003)

congz = 0x08049278
input = 0x0811EB40
payload = (p32(0xdeadbeef) + p32(congz) + p32(input)).encode('base64')

p.recvuntil('Authenticate : ')
# gdb.attach(p,'b *0x804930b') # breakpoint at leave
p.sendline(payload)

p.interactive()

More

OK

正如Flag所言,漏洞的利用其实就是一个逐步渗透的过程。

作者:SmalOSnail 发表于2016/12/16 16:05:24 原文链接
阅读:57 评论:0 查看评论

PAT乙级-1003. 我要通过!

$
0
0

PAT乙级-1003. 我要通过!

答案正确”是自动判题系统给出的最令人欢喜的回复。本题属于PAT的“答案正确”大派送 —— 只要读入的字符串满足下列条件,系统就输出“答案正确”,否则输出“答案错误”。

得到“答案正确”的条件是:

1. 字符串中必须仅有P, A, T这三种字符,不可以包含其它字符;
2. 任意形如 xPATx 的字符串都可以获得“答案正确”,其中 x 或者是空字符串,或者是仅由字母 A 组成的字符串;
3. 如果 aPbTc 是正确的,那么 aPbATca 也是正确的,其中 a, b, c 均或者是空字符串,或者是仅由字母 A 组成的字符串。

现在就请你为PAT写一个自动裁判程序,判定哪些字符串是可以获得“答案正确”的。

输入格式: 每个测试输入包含1个测试用例。第1行给出一个自然数n (<10),是需要检测的字符串个数。接下来每个字符串占一行,字符串长度不超过100,且不包含空格。

输出格式:每个字符串的检测结果占一行,如果该字符串可以获得“答案正确”,则输出YES,否则输出NO。

输入样例:
8
PAT
PAAT
AAPATAA
AAPAATAAAA
xPATx
PT
Whatever
APAAATAA
输出样例:
YES
YES
YES
YES
NO
NO
NO
NO
/*分析:
1、第一个条件:必须只含有P、A、T
2、第二个条件:xPATx,PAT前后的A必须相等
3、第三个条件:aPbTc 是正确的,那么 aPbATca 也是正确的

递推一下:假设A之前有X1个A,PT之间有X2个A,T之后X3个A
(1)aPATc是对的条件是a=c,那么就是aPATa(X2=1,X3=X1)
(2)aPATa是对的,那么aPAATaa是对的(X2=2,X3=X1+X1=2*X1)
(3)aPAATaa是对的,那么aPAAATaaa也是对的(X3,X3=X1+X1+X1=3*X1)
...(X2=n,X3=n*X1)
最终得到规律:X3=X1*X2
*/
#include<iostream>
#include<string>
using namespace std;

bool isValid(string s)
{
  int count1 = 0, count2 = 0, count3 = 0;
  int i = 0;
  while (i < s.size() && s[i] == 'A')
  {
    i++;
    count1++;
  }  
  if (s[i] == 'P')
  {
    i++;
    while (i < s.size() && s[i] == 'A')
    {
      i++;
      count2++;
    }
    if (count2 == 0)
      return false;
  }
  else
    return false;
  if (s[i] == 'T')
  {
    i++;
    while (i < s.size() && s[i] == 'A')
    {
      i++;
      count3++;
    }
  }
  else
    return false;
  return count3 == (count1*count2);
}

int main()
{
  int n;
  cin >> n;
  while (n--){
    string str;
    cin>>str;
    if (isValid(str))
      cout << "YES" << endl;
    else
      cout << "NO" << endl;
  }
}


作者:u011391629 发表于2016/12/16 16:35:55 原文链接
阅读:97 评论:0 查看评论

回复热爱计算机的数控生

$
0
0

【来信】
  贺老师你好,我是一名在校大专生,幸运的读了您写的逆袭大学,让我对未来产生莫大的憧憬,可现实却是我身在一个自己丝毫不敢兴趣的专业(数控) 曾经我是一名网瘾青年,对此丝毫不在意,做着自己游戏打的好说不定就能去打职业什么的美梦。直到现在我清醒过来了,我热爱计算机,我热爱他的一切,他的每个01都是那么的迷人,但是此时的我却陷入一片迷茫。一年半的日子,我接下去该怎么走,我如果在学校这个不喜欢的数控专业继续混日子的话然后自学计算机的话我感觉好难,时间完全掌控不了,辅导员和我也是互不理解,但是我要休学自学计算机的话我就是零基础了,这我是想过的,但是我感觉这个压力不知从何谈起的难,或许是自己怕自己学习方法不当,亦或者怕自己在接触这个计算机庞然大物的时候如同井底之蛙一样漏出一些自己根本都不知道的知识,换句话说,我到底是怎么打知识基础比较好呀,需要再去看一个计算机培训学校学习吗?但是网上那么多的小广告,我真的不知道该去哪找一个学习氛围才不会上当受骗,那么多的职业培训机构网上查到的骗局,漏动让我心寒?然后紧接着呢,就是我基础知识学会了,该往哪方面走,怎么走,其实我有一个梦想,那就是在所有的电脑计算机领域都有我的容身之处,可能老师您不相信,但是我就是这样一个喜欢追求全面发展的学生,我对知识的渴望已经让我发狂,但是苦于不得法,总之我就是希望我得到它(计算机),我想要她的一切,它是那么的迷人?抱歉了,老师。我有点失态,但是我所言句句属实发自肺腑,希望老师拯救一个有梦想的孩子,给我一条通往西天的阳光大道!不仅是为了我自己,而且是为了我的父母,一天一月一年一辈子,学无止境,这一次我拼了,希望老师可以看到我,支持我!谢谢。

【回复】
  你好。我认真地看过了你写的信,感受到了一位迷途知返的青年,对未来美好生活和事业由衷的向往。同时,我也惭愧,我恐怕不能给你”通往西天的阳光大道”。唐僧师徒获取真经之道,也不是谁指出来的,而是他们走出来的,不是大道,而是一段至少不轻松的道。
  按我一贯的原则,首先建议你想一下,是否能充分利用好现有的专业。说实话,数控和计算机,离得还不是很远,我看了一下数控专业一般的培养方案,开的课程中,有程序设计、微机原理之类的课程,也是很实用的。喜欢计算机,那就用计算机,将那些生产线上的设备玩转了,用你配置的运行方案,用你编制的程序,生产出美轮美奂的产品,就我的感觉,比0、1更具体,更有吸引力。
  这样的建议,还有一层考虑,你既然有这一段的求学经历,将毕业证拿下来了,这也是很重要的。
  若你铁心要和数控说ByeBye的话,你需要再深入地考虑,你希望学了计算机后,想做哪方面的事。且不论你现在的急迫,就时间宽裕的学生,找一个主要方向去专注也是必须的。追求全面发展,更强调专业能力和一般能力的协调,而自己的专业能力,尤其是面向很宽的计算机,需要收一收。你希望自己能从事软件开发、游戏开发、网站、移动计算、网络工程,还是希望构建计算机系统解决具体问题(这更接近数控专业了),需要你先回答,然后再设计什么方式,走什么途径。
  把这些想清楚了,我们再讨论下一步如何做。如果现在空想,会想出比你来信中更多的话题,反而会使人更乱。唐僧从长安出发,他自己是不知道路过五行山会收一个孙悟空的,我们且行且看。
  你的来信,让我看看到年轻的激情,但在这件事上,需要更多理性。你考虑一下,把进一步的想法整理好,可以再给我来信,我愿意陪你,发现你该走的路。
  你给我写Email可能更方便。我的邮箱:sxhelijian@163.com。

作者:sxhelijian 发表于2016/12/16 18:59:51 原文链接
阅读:67 评论:1 查看评论

个人记录-LeetCode 52. N-Queens II

$
0
0

问题:
Follow up for N-Queens problem.

Now, instead outputting board configurations, return the total number of distinct solutions.

代码示例:
LeetCode 51. N-Queens一样, 暴力递归即可。

1、与 N-Queens思路一致,故不再做注释

public class Solution {
    public int totalNQueens(int n) {
        int rst = 0;

        if (n > 2) {
            boolean[][] bitMap = new boolean[n][n];

            int[] tmp = new int[1];
            tmp[0] = 0;

            getResult(tmp, bitMap, n, 0);

            rst = tmp[0];
        } else if (n == 1){
            rst = 1;
        }

        return rst;
    }

    private void getResult(int[] tmp, boolean[][] bitMap, int n, int now) {
        if (now == n) {
            ++tmp[0];
            return;
        }

        for (int j = 0; j < n; ++j) {
            boolean flag = false;

            for (int i = 0; i < now; ++i) {
                if (bitMap[i][j]) {
                    flag = true;
                    break;
                }
            }

            int curRow = now - 1;
            int curCol = j - 1;
            while (curRow >= 0 && curCol >= 0) {
                if (bitMap[curRow][curCol]) {
                    flag = true;
                    break;
                }
                --curRow;
                --curCol;
            }

            curRow = now - 1;
            curCol = j + 1;
            while (curRow >= 0 && curCol < n) {
                if (bitMap[curRow][curCol]) {
                    flag = true;
                    break;
                }
                --curRow;
                ++curCol;
            }

            if (flag) {
                continue;
            }

            bitMap[now][j] = true;
            getResult(tmp, bitMap, n, now + 1);
            bitMap[now][j] = false;
        }
    }
}

2、优化方案

public class Solution {
    public int totalNQueens(int n) {
        int rst = 0;

        if (n > 2) {
            //colBitMap表示某列是否放置了Queen
            boolean[] colBitMap = new boolean[n];

            //表示某条右斜线是否放置了Queen
            //例如:(0,0),(1,1)等是一条右斜线
            //(0,1), (1,2)等是条右斜线
            //记第一条右斜线为(n-1,0),最后一条为(0,n-1)
            //因此在同一条斜线上的坐标满足j-i+n-1相等
            boolean[] rightSlashMap = new boolean[2*n-1];

            //表示某条左斜线是否放置了Queen
            //例如(0,1),(1,0)等是一条左斜线
            //(0,2),(1,1)等是一条左斜线
            //记第一条右斜线为(0,0),最后一条为(n-1,n-1)
            //因此在同一条斜线上的坐标满足i+j相等
            boolean[] leftSlashMap = new boolean[2*n-1];

            int[] tmp = new int[1];
            tmp[0] = 0;

            getResult(tmp, colBitMap, rightSlashMap, leftSlashMap, n, 0);

            rst = tmp[0];
        } else if (n == 1){
            rst = 1;
        }

        return rst;
    }

    private void getResult(int[] tmp, boolean[] colBitMap, boolean[] rightSlashMap, boolean[] leftSlashMap,
            int n, int now) {
        if (now == n) {
            ++tmp[0];
            return;
        }

        for (int j = 0; j < n; ++j) {
            //分别定义bitMap的好处,就是不用通过for循环来判断是否能放置Queen,加快了速度
            if (colBitMap[j]) {
                continue;
            }

            if (rightSlashMap[j-now+n-1]) {
                continue;
            }

            if (leftSlashMap[j+now]) {
                continue;
            }

            colBitMap[j] = true;
            rightSlashMap[j-now+n-1] = true;
            leftSlashMap[j+now] = true;

            //其它迭代方式一致
            getResult(tmp, colBitMap, rightSlashMap, leftSlashMap, n, now + 1);

            colBitMap[j] = false;
            rightSlashMap[j-now+n-1] = false;
            leftSlashMap[j+now] = false;
        }
    }
}
作者:Gaugamela 发表于2016/12/16 19:09:11 原文链接
阅读:26 评论:0 查看评论

poj1573——Robot Motion(模拟)

$
0
0

Description

这里写图片描述

A robot has been programmed to follow the instructions in its path. Instructions for the next direction the robot is to move are laid down in a grid. The possible instructions are

N north (up the page)
S south (down the page)
E east (to the right on the page)
W west (to the left on the page)

For example, suppose the robot starts on the north (top) side of Grid 1 and starts south (down). The path the robot follows is shown. The robot goes through 10 instructions in the grid before leaving the grid.

Compare what happens in Grid 2: the robot goes through 3 instructions only once, and then starts a loop through 8 instructions, and never exits.

You are to write a program that determines how long it takes a robot to get out of the grid or how the robot loops around.
Input

There will be one or more grids for robots to navigate. The data for each is in the following form. On the first line are three integers separated by blanks: the number of rows in the grid, the number of columns in the grid, and the number of the column in which the robot enters from the north. The possible entry columns are numbered starting with one at the left. Then come the rows of the direction instructions. Each grid will have at least one and at most 10 rows and columns of instructions. The lines of instructions contain only the characters N, S, E, or W with no blanks. The end of input is indicated by a row containing 0 0 0.
Output

For each grid in the input there is one line of output. Either the robot follows a certain number of instructions and exits the grid on any one the four sides or else the robot follows the instructions on a certain number of locations once, and then the instructions on some number of locations repeatedly. The sample input below corresponds to the two grids above and illustrates the two forms of output. The word “step” is always immediately followed by “(s)” whether or not the number before it is 1.
Sample Input

3 6 5
NEESWE
WWWESS
SNWWWW
4 5 1
SESWE
EESNW
NWEEN
EWSEN
0 0 0
Sample Output

10 step(s) to exit
3 step(s) before a loop of 8 step(s)

题意就是按照图中给的方位上下左右移动,如果能出去就输出走了多少步,如果陷入循环,就输出在循环前走了多少步和循环了多少步。
按照题意模拟就行,先是边走边标记,记步数,如果走出去了,直接输出步数就行。如果没有,则在循环的这个点,按照标记走,边走边抹除标记,并计步数,直到没有标记为止。

#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <cstdio>
#include <set>
#include <cmath>
#include <map>
#include <algorithm>
#include <stack>
#define INF 0x3f3f3f3f
#define MAXN 100010
#define Mod 10001
using namespace std;
char grid[20][20];
int vis[20][20];
int main()
{
    int r,c,e;
    while(cin>>r>>c>>e)
    {
        if(r==0&&c==0&&e==0)
            break;
        memset(vis,0,sizeof(vis));
        for(int i=1; i<=r; ++i)
        {
            vis[i][0]=1;
            vis[i][c+1]=1;
        }
        for(int i=1; i<=c; ++i)
        {
            vis[0][i]=1;
            vis[r+1][i]=1;
        }
        for(int i=1; i<=r; ++i)
            for(int j=1; j<=c; ++j)
                cin>>grid[i][j];
        int x=1,y=e,sum1=0;
        while(!vis[x][y])
        {
            if(!vis[x][y]&&grid[x][y]=='N')
            {
                vis[x][y]=1;
                x--;
                sum1++;
            }
            else if(!vis[x][y]&&grid[x][y]=='S')
            {
                vis[x][y]=1;
                x++;
                sum1++;
            }
            else if(!vis[x][y]&&grid[x][y]=='W')
            {
                vis[x][y]=1;
                y--;
                sum1++;
            }
            else if(!vis[x][y]&&grid[x][y]=='E')
            {
                vis[x][y]=1;
                y++;
                sum1++;
            }
        }
        if(x<1||x>r||y<1||y>c)
            printf("%d step(s) to exit\n",sum1);
        else
        {
            int sum2=0;
            while(vis[x][y])
            {
                if(vis[x][y]&&grid[x][y]=='N')
                {
                    vis[x][y]=0;
                    x--;
                    sum2++;
                }
                else if(vis[x][y]&&grid[x][y]=='S')
                {
                    vis[x][y]=0;
                    x++;
                    sum2++;
                }
                else if(vis[x][y]&&grid[x][y]=='W')
                {
                    vis[x][y]=0;
                    y--;
                    sum2++;
                }
                else if(vis[x][y]&&grid[x][y]=='E')
                {
                    vis[x][y]=0;
                    y++;
                    sum2++;
                }
            }
            printf("%d step(s) before a loop of %d step(s)\n",sum1-sum2,sum2);
        }
    }
    return 0;
}
作者:blue_skyrim 发表于2016/12/16 20:16:44 原文链接
阅读:30 评论:0 查看评论

java访问控制修饰符

$
0
0

Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java支持4种不同的访问权限。

默认的,也称为 default,在同一包内可见,不使用任何修饰符。

私有的,以 private 修饰符指定,在同一类内可见。

共有的,以 public 修饰符指定,对所有类可见。

受保护的,以 protected 修饰符指定,对同一包内的类和所有子类可见。

我们可以可以通过以下表来说明访问权限:

访问控制
修饰符 当前类 同一包内 子孙类 其他包
public Y Y Y Y
protected Y Y Y N
default Y Y N N
private Y N N N

默认访问修饰符-不使用任何关键字

使用默认访问修饰符声明的变量和方法,对同一个包内的类是可见的。接口里的变量都隐式声明为public static final,而接口里的方法默认情况下访问权限为public。

如下例所示,变量和方法的声明可以不使用任何修饰符。

实例

String version = "1.5.1";
boolean processOrder() {
return true;
}

私有访问修饰符-private

私有访问修饰符是最严格的访问级别,所以被声明为 private 的方法、变量和构造方法只能被所属类访问,并且类和接口不能声明为 private

声明为私有访问类型的变量只能通过类中公共的 getter 方法被外部类访问。

Private 访问修饰符的使用主要用来隐藏类的实现细节和保护类的数据。

下面的类使用了私有访问修饰符:

public class Logger {
private String format;
public String getFormat() {
return this.format;
} public void setFormat(String format) {
this.format = format;
}
}

实例中,Logger 类中的 format 变量为私有变量,所以其他类不能直接得到和设置该变量的值。为了使其他类能够操作该变量,定义了两个 public 方法:getFormat() (返回 format的值)和 setFormat(String)(设置 format 的值)

公有访问修饰符-public

被声明为 public 的类、方法、构造方法和接口能够被任何其他类访问。

如果几个相互访问的 public 类分布在不同的包中,则需要导入相应 public 类所在的包。由于类的继承性,类所有的公有方法和变量都能被其子类继承。

以下函数使用了公有访问控制:

public static void main(String[] arguments) {
// ...
}

Java 程序的 main() 方法必须设置成公有的,否则,Java 解释器将不能运行该类。

受保护的访问修饰符-protected

被声明为 protected 的变量、方法和构造器能被同一个包中的任何其他类访问,也能够被不同包中的子类访问。

Protected 访问修饰符不能修饰类和接口,方法和成员变量能够声明为 protected,但是接口的成员变量和成员方法不能声明为 protected。

子类能访问 Protected 修饰符声明的方法和变量,这样就能保护不相关的类使用这些方法和变量。

下面的父类使用了 protected 访问修饰符,子类重载了父类的 openSpeaker() 方法。

class AudioPlayer {
protected boolean openSpeaker(Speaker sp) {
// 实现细节
}
}
class StreamingAudioPlayer {
boolean openSpeaker(Speaker sp) {
// 实现细节
}
}

如果把 openSpeaker() 方法声明为 private,那么除了 AudioPlayer 之外的类将不能访问该方法。如果把 openSpeaker() 声明为 public,那么所有的类都能够访问该方法。如果我们只想让该方法对其所在类的子类可见,则将该方法声明为 protected。

访问控制和继承

请注意以下方法继承的规则:

  • 父类中声明为 public 的方法在子类中也必须为 public。

  • 父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private。

  • 父类中声明为 private 的方法,不能够被继承。

  • 技术分享:www.kaige123.com

作者:l4432321 发表于2016/12/16 20:24:52 原文链接
阅读:50 评论:0 查看评论

关于ListView中包含EditText数据复用引起异常的解决方案

$
0
0

概述

前几天测试提了一个bug,在ListView中添加留言信息,导致错乱的问题。实际上就是ListView需要添加一个EditText,复用导致错乱的问题,这个问题以前也遇到过。诸如,ListView嵌套EditText、CheckBox等焦点问题都会出现复用的错乱,其根源就是ViewHolder的复用问题。

这里写图片描述
说说上面的问题吧,保存item中EditText中的数据,导致数据复用的时候都给设置了值。我们在最外层存了一个Map

Map<Integer, String> edItem;

监听每个Item的输入(OnTextChangedListener),并在afterTextChanged()将值保存到Map中去。

 holder.editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {

                if ((edItem.size() == inputContainer.size())) {    
                    // 添加一项控件
                    edItem.put(edindex, "edindex");
                }

                return false;
            }
        });

但是这里出现了一个问题,由于复用,导致,每一个Item都被赋值了,所以我们要解决这个问题得从源头阻断给EditText赋值,也就是在OnTextChange方法里面,我们判断一下,如果用户操作的是当前的Item,我们就给Map赋值,否则不赋值,或者赋值为空值。
所以这个时候我们要对EditText的触摸事件做监听:

editText.setOnTouchListener(new View.OnTouchListener() {

            public boolean onTouch(View view, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    index = position;
                }
                return false;
            }
        });

然后我们在TextWatcher的onTextChanged判断一下:

public void onTextChanged(CharSequence text, int start, int before, int count) {
                //如果该edittext有默认内容,还要在if那里进行过滤
 if (index>=0 && text.length() > 0 &&index== position) {
      mData.get(index).put("input", text.toString());
                }
            }

这样就解决了复用的问题,完整代码:

public class ListViewTestAdapter extends BaseAdapter {

    private List<Map<String, String>> mData;
    private LayoutInflater mInflater;
    private Context mContext;

    private int index = -1;

    public ListViewTestAdapter(Context context, List<Map<String, String>> data) {
        this.mContext = context;
        this.mData = data;
        this.mInflater = LayoutInflater.from(context);
    }

    @Override
    public int getCount() {
        return mData.size();
    }

    @Override
    public Object getItem(int position) {
        return mData.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        ViewHolder holder = new ViewHolder();
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.main_function_listitem3, null);
            holder.title = (TextView) convertView.findViewById(R.id.txtTitle);
            holder.editText = (EditText) convertView.findViewById(R.id.input);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }


        EditText editText = holder.editText;


        editText.setOnTouchListener(new View.OnTouchListener() {

            public boolean onTouch(View view, MotionEvent event) {

                if (event.getAction() == MotionEvent.ACTION_UP) {
                    index = position;
                }
                return false;
            }
        });


        editText.addTextChangedListener(new TextWatcher() {

            public void afterTextChanged(Editable editable) {
            }

            public void beforeTextChanged(CharSequence text, int start, int count, int after) {
            }

            public void onTextChanged(CharSequence text, int start, int before, int count) {
                //如果该edittext有默认内容,还要在if那里进行过滤
                if (index>=0 && text.length() > 0 && index == position ) {
                    mData.get(index).put("input", text.toString());
                }
            }

        });
        holder.title.setText(mData.get(position).get("title"));
        holder.editText.setText(mData.get(position).get("input"));

        editText.clearFocus();

        if (index != -1 && index == position) {

            editText.requestFocus();
        }

        final ViewHolder finalHolder = holder;
        convertView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(mContext, finalHolder.title.getText(), Toast.LENGTH_SHORT).show();
            }
        });
        return convertView;
    }

    public final class ViewHolder {
        public TextView title;
        public EditText editText;
    }
}
作者:xiangzhihong8 发表于2016/12/16 21:13:02 原文链接
阅读:14 评论:0 查看评论

如何编写入门级redis客户端

$
0
0

概述

Redis是开源的、基于内存的数据结构存储系统,可用作数据库、缓存以及消息代理方面。Redis支持许多种数据结构,并内置了丰富的诸如冗余、脚本、事务、持久化等功能,深受业界喜爱,被各种业务系统广泛使用。为了方便使用,Redis官网推荐了针对各种编程语言的多种客户端,支持java、c#、python、c++等主流编程语言。那么大家会问,既然Redis客户端已经这么丰富了,为什么还要尝试自己编写客户端?我的看法是,知己知彼,自己尝试制作Redis客户端,不仅可以加深对Redis的了解,而且可以通晓Redis客户端的原理,为今后的更好地使用、乃至定制改造Redis作好充分准备。

知识准备

要想亲自开发Redis客户端,需要以下知识:
1、网络编程基础
2、熟悉Redis协议
3、了解Redis的基本操作
另外文中的例子将会采用java编写,因此最好有基本的java编程知识。

面向对象

Redis Protocal

Redis协议被称为:RESP (REdis Serialization Protocol),客户端通过TCP协议连接到客户端的6379端口(默认端口)。
RESP协议是在Redis1.2中引入的,不过现在已经是Redis2.0中的标准协议了。所以你应该再Redis客户端中实现这个协议。

RESP描述

RESP其实是一个序列化协议,支持简单字符串、错误、整数、整块字符串和数组。数据类型依赖头文字,分别表示如下:
简单字符串的头文字是“+”
错误的头文字是“-”
整数的头文字是“:”
整块字符串的头文字是“$”
数组的头文字是“*”

RESP在请求-响应模型中的用法

-客户端向Redis服务器发送命令,命令的格式是仅以RESP整块字符串构成的数组。。
-服务器端根据命令的结果,选择适宜的一种RESP类型返回

简单字符串
简单字符串是以半角加号开头,后跟随着不含回车换行的字符串,然后以回车换行结尾。
举例如下:+OK\r\n
简单字符串是非二进制安全的,如果需要二进制安全,可使用“整块字符串”。

错误
错误和简单字符串类似,但头文字换成半角减号了。后面跟随的文字,可以视为错误消息内容。
举例如下:

-ERR unknown command 'foobar'
-WRONGTYPE Operation against a key holding the wrong kind of value

整数
整数与简单字符串类似,头文字为半角冒号。
举例如下:

:0\r\n
:1000\r\n

整块字符串
整块字符串可以用来标示二进制安全的、最大512MB长度的字符串。它以$符号开头,后跟随实际字符串长度,以回车换行结尾,后跟随实际字符串,再最终以回车换行结尾。
举例如下:

$6\r\nfoobar\r\n
空字符串表现形式如下:$0\r\n\r\n
nil表现形式如下:$-1\r\n\r\n

数组
数组以半角星号开头,后接数组中元素个数,然后以回车换行结尾,然后后接各个元素。
举例如下:

空数组:*0\r\n
包含两个整块字符串的数组:*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n
包含三个整数的数组:*3\r\n:1\r\n:2\r\n:3\r\n

Redis客户端代码实现

要实现和Redis服务端通信,首先需要与Redis服务端建立TCP通信连接,然后使用上述的RESP协议,将想要执行的Redis命令发送至服务端,并等待服务端响应,然后接收到响应结果,展示给用户。

以下代码实现了一个简单的获取info的操作。

public class App 
{
    public static void main( String[] args )
    {
        //定义redis服务端默认端口
        int port = 6379;

        Socket socket = null;
        BufferedReader in = null;
        PrintWriter out = null;

        try {
            //创建tcp连接
            socket = new Socket("localhost", port);
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new PrintWriter(socket.getOutputStream(), true);

            //传送info命令
            //客户端向Redis服务器发送命令,以RESP整块字符串数组的形式
            out.println("*1\r\n$4\r\ninfo\r\n");
            System.out.println("Redis command wat sent successfully.");

            //接收服务器的回复
            CharBuffer response = CharBuffer.allocate(1024);
            int readedLen = in.read(response);
            String responseBody = response.flip().toString();

            //输出服务器的回复
            System.out.println(responseBody);

        }
        catch(Exception e) {
            e.printStackTrace();
        }
        finally {
            //最后关闭相关的流
            if (out != null){
                out.close();
                out = null;
            }

            if (in != null) {
                try {
                    in.close();
                }
                catch(IOException e){
                    e.printStackTrace();
                }

                in = null;
            }

            if (socket != null) {
                try {
                    socket.close();
                }
                catch(IOException e){
                    e.printStackTrace();
                }

                socket = null;
            }
        }
    }
}

运行后,系统将会在命令行界面输出info的执行结果。

作者:xiangzhihong8 发表于2016/12/16 21:31:14 原文链接
阅读:37 评论:0 查看评论
Viewing all 35570 articles
Browse latest View live


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