首页 文章 精选 留言 我的

精选列表

搜索[知识资产沉淀],共10002篇文章
优秀的个人博客,低调大师

资产交易所系统架构设计

本文介绍一个实际开发并上线的加密货币交易所项目的逻辑架构设计,其中包括撮合服务、做市服务、用户服务、市场数据服务、钱包服务、报表服务等核心组件,并采用了基于AKKA集群的微服务架构。 加密交易所的逻辑架构图如下所示: 如果希望快速掌握区块链应用的开发,推荐汇智网的区块链应用开发系列教程,内容涵盖比特币、以太坊、eos、Hyperledger fabric和tendermint等多种区块链,以及java、c#、go、nodejs、python、php、dart等多种开发语言。 1、订单服务 订单服务(Order Service)包含了负责匹配委托单并生成市场数据的撮合引擎(matching engine)。在加密货币交易所中,委托单按交易对进行撮合匹配,因此一个ETH/BTC的买单永远不会匹配到一个ETH/USDT的卖单。通常认为每一个交易对的订单匹配是依次进行的,例如,撮合进程必须是单线程的(每个交易对一个线程)。不过实际上还是有办法让这个流程中的某些环节实现并行化以便提高系统的吞吐量。订单处理过程包括以下步骤: 订单验证。这包括确认交易对处于可交易状态、校验订单的价格与数量等符合最低要求等等。 资金检查。目的是确保用户的账户中有足额资金用于下单。 风险检查,以避免异常的交易行为 确定该委托单的挂单(Maker)费率和吃单(Taker)费率,所依据的规则可能设计交易对、订单数量、用户等级等等。 创建用于撮合处理的订单对象,冻结下单用户账户中可能会用到的最大数量的资金。 在交易委托账本中匹配该新订单,匹配的结果可能是:无匹配、部分成交或全部成交。当 部分成交或全部成交时,可能在交易委托账本中存在一个或多个匹配的订单。 更新并持久化成交订单用户的账户余额,更新交易委托账本。 持久化由订单撮合成功而触发的其他数据,例如:订单执行记录、资金交易日志等等。 生成市场数据,并将更新的数据发布到前台服务器(Front Server) 假设从第1步到第5步需要5ms,第6步到第7步需要2ms,第8步需要2ms,第9步需要2ms。如果系统几乎同时接收到9个委托订单,使用单线程来执行上述全部步骤需要 9 x (5 + 2 + 2 + 2) = 99ms。然而,在上面的步骤中,其实只有第6步和第7步是真正需要单线程的。如果我们为第1步到第5步使用3个Actor,第6步到第7步使用1个actor以确保这两个步骤单线程执行,第8步和第9步使用1个actor,那么处理流程就变成了这样: 现在全部9个订单的总处理时间就缩短到了25ms,相对于前面的全串行处理所需的99ms,这是不小的改善。 订单服务(Order Service)是一个使用Akka和Spring框架开发的独立应用。它作为一个节点加入Akka集群,然后集群中的前台服务器(Front Server)节点就可以发现它。当启动订单服务实例时,我们可以在命令行参数中指定需要处理哪些交易对。我们可以使用单一的订单服务节点来处理所有的交易对,也可以使用多个订单服务节点分别处理不同的交易对集合。这意味着订单服务支持按交易对进行分片(sharding)。由于Akka系统actor的位置透明特性,前台服务节点并不需要关心订单服务是运行在一个节点还是多个节点上,这使得撮合引擎具备了水平扩展(Scale Out)的能力。 如果在集群中针对一个交易对(Trading Pair)存在多个订单服务节点,前台服务节点将始终访问最近加入集群的节点。我们可以启动一个订单服务节点A来处理BTC/USDT交易对,然后启动第二个订单服务节点B也来处理BTC/USDT交易对。前台服务器会始终将消息发送给最后加入集群的订单服务节点,因此集群中所有的前台服务节点都会发送BTC/USDT订单消息给节点B。如果之后的某个时间,节点B从集群中被移除,那么所有的前台服务节点将会得到Akka集群的通知,之后就会将BTC/USDT订单消息发送给节点A了。 我们使用这个用于前台服务节点和订单服务节点之间的简单的服务发现及路由协议,来实现订单服务的热备份(Hot Standby)高可用性。这个简单协议使得订单服务的水平扩展和升级非常容易。例如,当有一个新的订单服务需要部署时,我们可以先让老版本节点继续运行,然后启动新版本的节点。一旦新版本上线,前台服务节点将自动使用最新加入的节点而非之前的老版本节点,接下来我们只需要关掉老版本节点就可以了。 为了尽可能缩短订单匹配的时间,订单服务需要在内存中缓存(Cache)整个交易委托账本。当系统中存在多个处理相同交易对的订单服务节点时,就会在不同的节点上缓存交易委托账本的完全一致的多个拷贝。我们设计了一个简单但有效的方法来校验并同步多个节点上的内存委托账本。 订单服务还负责生成以下市场数据: K线数据,间隔分别为:1分钟、5分钟、15分钟、30分钟、1小时、2小时、4小时、6小时、12小时、 1天、1周、1月 所有交易对的市场深度(market depth)数据 所有交易对的最新出价 最近成交的交易对 订单服务将这些市场数据存入Redis服务中,并通过Redis的复制机制同步到前台集群中的redis服务器上,然后由前台服务器载入并推送给订阅该数据的用户。 2、做市服务 做市(Market making)对于加密货币交易所 —— 尤其是对于一个新创办的加密交易所 —— 的成功至关重要。如果交易所没有足够的交易人,那么买卖价差(bid-ask spread)通常要大于那些具有较好流动性的加密货币交易所。做市商(Market maker)为交易所增加了流动性,缩小了买卖价差,同时也为交易委托账本增加了深度,这些因素同时也会更加吸引交易人的加入。 做市商可以来自交易所的外部,他们使用交易所提供的API向交易委托账本中挂买单(Bid)和卖单(Ask)。对于一个刚创立的加密交易所而言,其本身也需要为某些交易对提供流动性,从而承担了做市商的角色。 一个基本的做市策略包括同时挂买单和卖单,这样当两方的订单都被市场吃掉后就挣到了买卖价差。由于加密货币的价格剧烈不稳定性,当市场价格向一个方向持续运动时,使用这种策略的做市商有可能损失惨重。例如,做市商挂买单以300 USDT的价格买1 ETH,同时挂另一个卖单以301 USDT的价格卖1 ETH。如果这两个订单都成交了,那么做市商就赚了1 USDT。如果卖单成交,价格继续上涨至310USDT,那么做市商的买单就没有机会在短期内成交,这导致做市商面临一个潜在的9 USDT的亏损。 做市模块使用订单服务以及外部交易所的交易委托账本、做市商的有效资金等作为输入,生成一系列不同价位的卖单和卖单。当任何输入发生变化,输出的做市委托单也会相应地变化,从而引起交易委托账本的相应变化。出于性能方面的考虑,订单服务内置了做市支持能力。订单服务将区别对待来自普通用户的委托单以及来自做市模块的委托单 —— 这些做市委托单只需要很少的资源,因此更加具有动态性。它们可以作为一个整体加入交易委托账本,或者进行一些调整,乃至从账本中整体移除,而不需要像普通用户的委托单那样必须逐一对待。同时,这些做市委托单也可以与用户单成交,如果任何用户的委托能够匹配这些做市单,那么接下来的执行和普通委托单是一致的。 除了向交易委托账本中添加订单,做市模块还能够向外部交易所下单,并且监视外部交易所中的订单执行状态,以实现自动化的对冲(Hedging)。 做市模块是使用Akka和Spring框架开发的一个独立应用。它连接到集群中的订单服务节点,看起来像订单服务的扩展模块 —— 订单服务的运行不需要做市模块。 3、用户服务 用户服务是一个用Akka和spring框架开发的独立应用,它为前台服务器提供用户相关的服务,例如用户注册、用户身份验证、KYC提交、密码管理、API密钥管理、充值地址管理等等。 当用户服务实例启动时,它会加入Akka集群,前台服务节点将会自动发现用户服务。我们可以在集群中启动多个用户服务节点。在这种情况下,前台服务器以轮询调度方式(round robin)将消息发送给所有的存活用户服务节点,从而保证了用户服务模块的可扩展性及高可用性。 用户服务可以直接访问核心数据库以及redis集群。它使用钱包服务节点提供的服务来创建新的区块链地址。 4、钱包服务 与逻辑架构图中的其他模块不同,钱包服务不是一个单一的独立应用,而是由多个应用组成。这主要是基于安全方面的考虑。作为一个整理,钱包服务: 为用户账户生成新的区块链地址以用于接收充值 管理交易所持有的热钱包地址私钥 创建提币交易,签名并提交到区块链网络 连接到多个区块链网络,搜索处理充值交易,跟踪提币交易的执行状态 在某些情况下,归集散落在众多用户地址中的加密货币到交易所的中心热/冷钱包。 校验交易所数据库中的资金数目,确保与区块链上的数据一致。 上述这些功能,有些是由用户服务模块通过REST接口调用,有些则需要接入区块链网络,而另一些则不需要接入区块链网络或其他模块,甚至不需要网络接入。我们将钱包服务基于其连接性与安全需求等拆分为多个应用。 5、Maria DB及ETL 在核心站点中包含一个Maria DB Galera集群。集群中的主(Master)数据库实例由应用直接使用,同时在集群中创建了两级从(Slave)数据库: 一个从集群中直接复制的数据库是复制中转库(replication staging database)。它作为边缘站点中的主库提供服务。当从核心数据库集群中复制时,它会过滤掉前台服务和web服务不使用的数据表,这可以避免泄漏敏感数据,同时减少了需要复制到边缘站点的数据量。 另一个直接从集群中复制的数据库是报表库实例。在这个MariaDB从库实例上创建有两个库,一个用于从核心集群中复制,另一个服务于管理服务器的报表功能。我们使用多维数据模型,并且在报表数据库中实现分析报表的生成。新的数据每个几秒钟就从复制库中抽取、转换并载入,以便保证管理服务器的数据时效。 出于安全及报表目的,每个30分钟或1个小时,系统制作一个所有用户状态的快照,包括账户余额、订单、交易执行等等。快照是活的,因为所有的历史快照都可以直接从报表数据库中查询并加以利用。当制作快照时,ETL流程将临时停止复制过程,以确保复制库处于静止状态。 6、REdis 核心站点中的Redis用于保存订单服务生成的市场数据,并通过复制机制推送到前端站点。我们之前在Part 2中已经详细介绍了redis的复制机制。 7、管理服务器和ETL 管理服务器是一个J2EE web应用,使用spring框架和spring mvc开发。Tomcat作为管理服务模块的应用服务器。Nginx置于Tomcat之前以启用HTTPS以及内容缓存。 管理服务器为交易所内部员工提供后台管理系统。采用基于角色的权限分配机制,用户可以分配不同的角色,而不同的角色则可以访问系统中的不同功能。管理服务器可以对每个用户的每个操作都生成审计日志。 我们使用ELK栈作为准实时分析子系统的基础。Logstash负责从各种来源(例如前台服务器和web服务器)采集日志并存入Elasticsearch数据库。验证过身份的交易所员工可以使用Kibana来查看预定义的报表,或者在ElasticSearch上执行任意的查询。我们也在管理服务器上构建了一些查询Elasticsearch数据的报表,这样大部分员工就不用同时访问管理服务器和Kibana了。 原文链接:加密货币交易所微服务架构设计 —— 汇智网

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

数据库知识:SQLServer变量相关知识介绍

1、概述 SQLServer变量对应内存中的一个存储空间。它和常量不同,变量的值可以在执行过程中改变。 2、分类 SQLServer变量根据作用范围不同主要分为局部变量和全局变量。 2.1.局部变量 局部变量是用户在程序中定义的变量,它仅在定义的程序范围内有效。局部变量可以用来保存从表中读取的数据,也可以作为临时变量保存计算的中间结果。 注意:局部变量名称的第一个字符必须为一个@。 示例: --定义两个局部变量 DECLARE@mint,@nint; --赋值 SET@m=9; SET@n=1; --查询 SELECT@m+@n; 2.2 全局变量 全局变量通常以@@开头。这些变量一般都是SQL Server中的系统函数,它们的语法遵循函数的规则。用户可以在程序中使用这些函数测试系统特性和SQL命令的执行情况。常用的全局变量如下: @@ERROR--最后一个SQL错误的错误号 @@IDENTITY--最后一次插入的标识值 @@LANGUAGE--当前使用的语言的名称 @@MAX_CONNECTIONS--可以创建的同时连接的最大数目 @@ROWCOUNT--受上一个SQL语句影响的行数 @@SERVERNAME--本地服务器的信息 @@TRANSCOUNT--当前连接打开的事物数 @@VERSION--查看SQLServer的版本信息 3.声明变量 3.1、简单声明 变量只有在声明后才能使用,可以使用DECLARE语句来声明变量。在声明变量时可以指定变量的数据类型和长度。 DECLARE@namenvarchar(30); 3.2 声明多个变量 声明多个局部变量,需要在定义的局部变量后使跟上一个逗号,然后指定下一个局部变量名称和数据类型。 DECLARE@Namenvarchar(30),@Ageint; 3.3 变量作用域 在使用变量时,需要注意变量的作用域。变量具有局部作用域,只在定义它们的批处理或过程中可见。作用域范围从声明变量的地方开始到声明变量的批处理或存储过程的结尾。 USEDB; GO DECLARE@Ageint; SET@Age=30; GO--结束符 select@Age--报错 4.变量赋值 在声明变量后,变量值被默认设置为NULL。可以使用SET或SELECT语句。SET是为变量赋值的首选方法。 变量也可以通过选择列表中当前所引用的值赋值。如果在选择列表中引用变量,则它应当被赋以标量值或者SELECT语句应仅返回一行。 示例: DECLARE@Ageint; --SELECT语句赋值 SELECT@Age=agefromt_userORDERBYagedesc;--返回最后一行的值 select@Age;

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

Django 搭建CMDB系统完整[12](软件资产、厂商)

search_software.html {% extends 'base.html' %} {% block title %} <script type="text/javascript" src="/static/scripts/jquery/jquery-1.7.1.js"></script> <link href="/static/style/authority/basic_layout.css" rel="stylesheet" type="text/css"> <link href="/static/style/authority/common_style.css" rel="stylesheet" type="text/css"> <script type="text/javascript" src="/static/scripts/authority/commonAll.js"></script> <script type="text/javascript" src="/static/scripts/fancybox/jquery.fancybox-1.3.4.js"></script> <script type="text/javascript" src="/static/scripts/fancybox/jquery.fancybox-1.3.4.pack.js"></script> <link rel="stylesheet" type="text/css" href="/static/style/authority/jquery.fancybox-1.3.4.css" media="screen"></link> <script type="text/javascript" src="/static/scripts/artDialog/artDialog.js?skin=default"></script> <div id="container"> <div class="ui_content"> <div class="ui_text_indent"> <div id="box_border"> <div id="box_top">搜索</div> 软件名字<input type="text" id="mname" name="mname" value="{{ mname }}" class="ui_input_txt02" /> </div> <div id="box_top">新增/编辑</div> <form id="submitForm" name="submitForm" method="post" action="add_software.html"> <input type="hidden" id="softwareid" name="softwareid"/> 软件名字<input type="text" id="softwarename" name="softwarename" class="ui_input_txt02" /> 授权数<input type="text" id="softwarelicense_num" name="softwarelicense_num" class="ui_input_txt02" /> 版本号<input type="text" id="softwareversion" name="softwareversion" class="ui_input_txt02" /> </div> <div id="box_bottom"> <div class="pagination"> <span class="current"> {% if softwarelist.has_previous %} <a href="javascript:void(0)" onclick="search_software({{ softwarelist.previous_page_number }});">上一页</a> {% endif %} <span class="current"> Page {{ softwarelist.number }} of {{ softwarelist.paginator.num_pages }}. </span> {% if softwarelist.has_next %} <a href="javascript:void(0)" onclick="search_software({{ softwarelist.next_page_number }});">下一页</a> {% endif %} </span> </div> <input type="button" value="查询" class="ui_input_btn01" onclick="search_software(1);"/> <input type="submit" value="新增" class="ui_input_btn01" /> <input type="button" value="编辑" class="ui_input_btn01" onclick="edit_software({{ softwarelist.number }});" /> <input type="button" value="删除" class="ui_input_btn01" onclick="batdel_software();" /> <input type="button" value="导出EXCEL" class="ui_input_btn01" onclick="excel_software();" /> </div> </form> </div> </div> {% endblock %} {% block content %} <div class="ui_content"> <div class="ui_tb"> <table class="table" cellspacing="0" cellpadding="0" width="100%" align="center" border="0"> <tr> <th width="30"><input type="checkbox" id="id" name="id" /> </th> <th>软件名字</th> <th>授权数</th> <th>版本号</th> <th>操作</th> </tr> {% for mr in softwarelist.object_list %} <tr> <td><input type="checkbox" name="idcheck" value={{ mr.id }} class="acb" /></td> <td>{{ mr.name }}</td> <td>{{ mr.license_num }}</td> <td>{{ mr.version }}</td> <td> <a href="javascript:void(0)" onclick="edit_mr('{{ mr.id }}','{{ mr.name }}','{{ mr.license_num }}','{{ mr.version }}')" class="edit">编辑</a> <a href="javascript:void(0)" onclick="del_software({{ mr.id }},{{ softwarelist.number }});">删除</a> </td> </tr> {% endfor %} </table> </div> </div> </div> <script type="text/javascript"> function search_software(page){ var text1 = document.getElementById("mname").value; var myurl="search_software.html"+"?"+"mname="+text1+"&&page="+page; window.location.assign(encodeURI(myurl)) } function edit_mr(id,name,license_num,version){ document.getElementById("softwareid").value=id; document.getElementById("softwarename").value=name; document.getElementById("softwarelicense_num").value=license_num; document.getElementById("softwareversion").value=version; } function edit_software(page){ var text1 = document.getElementById("mname").value; id=document.getElementById("softwareid").value; name=document.getElementById("softwarename").value; license_num=document.getElementById("softwarelicense_num").value; version=document.getElementById("softwareversion").value; var myurl="edit_software.html"+"?"+"id="+id+"&&name="+name+"&&license_num="+license_num+"&&version="+version+"&&mname="+text1+"&&page="+page; window.location.assign(encodeURI(myurl)) } function del_software(id,page){ if(window.confirm('确定要删除该记录吗?')){ var text1= document.getElementById("mname").value; var myurl="del_software.html"+"?"+"id="+id+"&&mname="+text1+"&&page="+page; window.location.assign(encodeURI(myurl)) return true; }else{ //alert("取消"); return false; } } function batdel_software(){ if(window.confirm('确定要删除记录吗?')){ obj = document.getElementsByName("idcheck"); var text1= document.getElementById("mname").value; check_val = []; for(k in obj){ if(obj[k].checked) check_val.push(obj[k].value); } var myurl="batdel_software.html"+"?"+"ids="+check_val+"&&mname="+text1; window.location.assign(encodeURI(myurl)) return true; }else{ return false; } } function excel_software(){ var text1 = document.getElementById("mname").value; var myurl="excel_software.html"+"?"+"mname="+text1; window.location.assign(encodeURI(myurl)) } </script> {% endblock %} search_manufactory.html {% extends 'base.html' %} {% block title %} <script type="text/javascript" src="/static/scripts/jquery/jquery-1.7.1.js"></script> <link href="/static/style/authority/basic_layout.css" rel="stylesheet" type="text/css"> <link href="/static/style/authority/common_style.css" rel="stylesheet" type="text/css"> <script type="text/javascript" src="/static/scripts/authority/commonAll.js"></script> <script type="text/javascript" src="/static/scripts/fancybox/jquery.fancybox-1.3.4.js"></script> <script type="text/javascript" src="/static/scripts/fancybox/jquery.fancybox-1.3.4.pack.js"></script> <link rel="stylesheet" type="text/css" href="/static/style/authority/jquery.fancybox-1.3.4.css" media="screen"></link> <script type="text/javascript" src="/static/scripts/artDialog/artDialog.js?skin=default"></script> <div id="container"> <div class="ui_content"> <div class="ui_text_indent"> <div id="box_border"> <div id="box_top">搜索</div> 厂商名字<input type="text" id="mname" name="mname" value="{{ mname }}" class="ui_input_txt02" /> </div> <div id="box_top">新增/编辑</div> <form id="submitForm" name="submitForm" method="post" action="add_manufactory.html"> <input type="hidden" id="manufactoryid" name="manufactoryid"/> 厂商名字<input type="text" id="manufactorymanufactory" name="manufactorymanufactory" class="ui_input_txt02" /> 厂商电话<input type="text" id="manufactorysupport_num" name="manufactorysupport_num" class="ui_input_txt02" /> 备注<input type="text" id="manufactorymemo" name="manufactorymemo" class="ui_input_txt02" /> </div> <div id="box_bottom"> <div class="pagination"> <span class="current"> {% if manufactorylist.has_previous %} <a href="javascript:void(0)" onclick="search_manufactory({{ manufactorylist.previous_page_number }});">上一页</a> {% endif %} <span class="current"> Page {{ manufactorylist.number }} of {{ manufactorylist.paginator.num_pages }}. </span> {% if manufactorylist.has_next %} <a href="javascript:void(0)" onclick="search_manufactory({{ manufactorylist.next_page_number }});">下一页</a> {% endif %} </span> </div> <input type="button" value="查询" class="ui_input_btn01" onclick="search_manufactory(1);"/> <input type="submit" value="新增" class="ui_input_btn01" /> <input type="button" value="编辑" class="ui_input_btn01" onclick="edit_manufactory({{ manufactorylist.number }});" /> <input type="button" value="删除" class="ui_input_btn01" onclick="batdel_manufactory();" /> <input type="button" value="导出EXCEL" class="ui_input_btn01" onclick="excel_manufactory();" /> </div> </form> </div> </div> {% endblock %} {% block content %} <div class="ui_content"> <div class="ui_tb"> <table class="table" cellspacing="0" cellpadding="0" width="100%" align="center" border="0"> <tr> <th width="30"><input type="checkbox" id="id" name="id" /> </th> <th>厂商名字</th> <th>厂商电话</th> <th>备注</th> <th>操作</th> </tr> {% for mr in manufactorylist.object_list %} <tr> <td><input type="checkbox" name="idcheck" value={{ mr.id }} class="acb" /></td> <td>{{ mr.manufactory }}</td> <td>{{ mr.support_num }}</td> <td>{{ mr.memo }}</td> <td> <a href="javascript:void(0)" onclick="edit_mr('{{ mr.id }}','{{ mr.manufactory }}','{{ mr.support_num }}','{{ mr.memo }}')" class="edit">编辑</a> <a href="javascript:void(0)" onclick="del_manufactory({{ mr.id }},{{ manufactorylist.number }});">删除</a> </td> </tr> {% endfor %} </table> </div> </div> </div> <script type="text/javascript"> function search_manufactory(page){ var text1 = document.getElementById("mname").value; var myurl="search_manufactory.html"+"?"+"mname="+text1+"&&page="+page; window.location.assign(encodeURI(myurl)) } function edit_mr(id,name,license_num,version){ document.getElementById("manufactoryid").value=id; document.getElementById("manufactorymanufactory").value=name; document.getElementById("manufactorysupport_num").value=license_num; document.getElementById("manufactorymemo").value=version; } function edit_manufactory(page){ var text1 = document.getElementById("mname").value; id=document.getElementById("manufactoryid").value; name=document.getElementById("manufactorymanufactory").value; license_num=document.getElementById("manufactorysupport_num").value; version=document.getElementById("manufactorymemo").value; var myurl="edit_manufactory.html"+"?"+"id="+id+"&&manufactory="+name+"&&support_num="+license_num+"&&memo="+version+"&&mname="+text1+"&&page="+page; window.location.assign(encodeURI(myurl)) } function del_manufactory(id,page){ if(window.confirm('确定要删除该记录吗?')){ var text1= document.getElementById("mname").value; var myurl="del_manufactory.html"+"?"+"id="+id+"&&mname="+text1+"&&page="+page; window.location.assign(encodeURI(myurl)) return true; }else{ //alert("取消"); return false; } } function batdel_manufactory(){ if(window.confirm('确定要删除记录吗?')){ obj = document.getElementsByName("idcheck"); var text1= document.getElementById("mname").value; check_val = []; for(k in obj){ if(obj[k].checked) check_val.push(obj[k].value); } var myurl="batdel_manufactory.html"+"?"+"ids="+check_val+"&&mname="+text1; window.location.assign(encodeURI(myurl)) return true; }else{ return false; } } function excel_manufactory(){ var text1 = document.getElementById("mname").value; var myurl="excel_manufactory.html"+"?"+"mname="+text1; window.location.assign(encodeURI(myurl)) } </script> {% endblock %} cmdbapp/softwareviews.py -- coding: utf-8 -- from future import unicode_literals from django.shortcuts import render,render_to_response from django.core.paginator import Paginator,InvalidPage,EmptyPage from cmdbapp.models import * from django.http import HttpResponse from django.http import HttpResponseRedirect from xlwt import * import StringIO def search_software(request): error = False each_page = 5 mname=request.GET.get('mname','0') if mname=='0' or mname.strip()=='': record_list = Software.objects.all() mname='' paginator = Paginator(record_list,each_page) try: page = int(request.GET.get('page', '1')) except ValueError: page = 1 try: contacts = paginator.page(page) except (EmptyPage, InvalidPage): contacts = paginator.page(paginator.num_pages) return render_to_response('search_software.html',{'softwarelist':contacts,"mname":mname}) else: record_list = Software.objects.filter(name=mname) paginator = Paginator(record_list,each_page) try: page = int(request.GET.get('page', '1')) except ValueError: page = 1 try: contacts = paginator.page(page) except (EmptyPage, InvalidPage): contacts = paginator.page(paginator.num_pages) return render_to_response('search_software.html',{'softwarelist':contacts,"mname":mname}) def add_software(request): n = request.POST.get('softwarename') v = request.POST.get('softwareversion') l = request.POST.get('softwarelicense_num') Software.objects.create(name=n,license_num=l,version=v) return HttpResponseRedirect('search_software.html') def edit_software(request): id=request.GET.get('id') iid=int(id) name=request.GET.get('name') l=request.GET.get('license_num') v=request.GET.get('version') Software.objects.filter(id=iid).update(name=name,license_num=l,version=v) page=request.GET.get('page') mname=request.GET.get('mname') return HttpResponseRedirect('search_software.html?mname='+mname+"&&page="+page) def del_software(request): id=request.GET.get('id') iid=int(id) Software.objects.filter(id=iid).delete() page=request.GET.get('page') mname=request.GET.get('mname') return HttpResponseRedirect('search_software.html?mname='+mname+"&&page="+page) def batdel_software(request): ids=request.GET.get('ids') b=ids.split(',') arr = map(int,b) for aaa in arr: Software.objects.filter(id=aaa).delete() mname=request.GET.get('mname') return HttpResponseRedirect('search_software.html?mname='+mname) def excel_software(request): mname=request.GET.get('mname','0') if mname=='0' or mname.strip()=='': list_obj = Software.objects.all() mname='' else: list_obj = Software.objects.filter(name=mname) if list_obj: # 创建工作薄 ws = Workbook(encoding='utf-8') w = ws.add_sheet(u"软件清单") w.write(0, 0, "id") w.write(0, 1, u"软件名字") w.write(0, 2, u"授权数") w.write(0, 3, u"版本号") # 写入数据 excel_row = 1 for obj in list_obj: data_id = obj.id data_name = obj.name data_license_num = obj.license_num data_version = obj.version w.write(excel_row, 0, data_id) w.write(excel_row, 1, data_name) w.write(excel_row, 2, data_license_num) w.write(excel_row, 3, data_version) excel_row += 1 sio = StringIO.StringIO() ws.save(sio) sio.seek(0) response = HttpResponse(sio.getvalue(), content_type='application/vnd.ms-excel') response['Content-Disposition'] = 'attachment; filename=机房清单-'+mname+'.xls' response.write(sio.getvalue()) return response cmdbapp/manufactoryviews.py -- coding: utf-8 -- from future import unicode_literals from django.shortcuts import render,render_to_response from django.core.paginator import Paginator,InvalidPage,EmptyPage from cmdbapp.models import * from django.http import HttpResponse from django.http import HttpResponseRedirect from xlwt import * import StringIO def search_manufactory(request): error = False each_page = 5 mname=request.GET.get('mname','0') if mname=='0' or mname.strip()=='': record_list = Manufactory.objects.all() mname='' paginator = Paginator(record_list,each_page) try: page = int(request.GET.get('page', '1')) except ValueError: page = 1 try: contacts = paginator.page(page) except (EmptyPage, InvalidPage): contacts = paginator.page(paginator.num_pages) return render_to_response('search_manufactory.html',{'manufactorylist':contacts,"mname":mname}) else: record_list = Manufactory.objects.filter(name=mname) paginator = Paginator(record_list,each_page) try: page = int(request.GET.get('page', '1')) except ValueError: page = 1 try: contacts = paginator.page(page) except (EmptyPage, InvalidPage): contacts = paginator.page(paginator.num_pages) return render_to_response('search_manufactory.html',{'manufactorylist':contacts,"mname":mname}) def add_manufactory(request): n = request.POST.get('manufactorymanufactory') v = request.POST.get('manufactorysupport_num') l = request.POST.get('manufactorymemo') Manufactory.objects.create(manufactory=n,support_num=v,memo=l) return HttpResponseRedirect('search_manufactory.html') def edit_manufactory(request): id=request.GET.get('id') iid=int(id) name=request.GET.get('manufactory') l=request.GET.get('support_num') v=request.GET.get('memo') Manufactory.objects.filter(id=iid).update(manufactory=name,support_num=l,memo=v) page=request.GET.get('page') mname=request.GET.get('mname') return HttpResponseRedirect('search_manufactory.html?mname='+mname+"&&page="+page) def del_manufactory(request): id=request.GET.get('id') iid=int(id) Manufactory.objects.filter(id=iid).delete() page=request.GET.get('page') mname=request.GET.get('mname') return HttpResponseRedirect('search_manufactory.html?mname='+mname+"&&page="+page) def batdel_manufactory(request): ids=request.GET.get('ids') b=ids.split(',') arr = map(int,b) for aaa in arr: Manufactory.objects.filter(id=aaa).delete() mname=request.GET.get('mname') return HttpResponseRedirect('search_manufactory.html?mname='+mname) def excel_manufactory(request): mname=request.GET.get('mname','0') if mname=='0' or mname.strip()=='': list_obj = Manufactory.objects.all() mname='' else: list_obj = Manufactory.objects.filter(name=mname) if list_obj: # 创建工作薄 ws = Workbook(encoding='utf-8') w = ws.add_sheet(u"厂商清单") w.write(0, 0, "id") w.write(0, 1, u"厂商名字") w.write(0, 2, u"电话") w.write(0, 3, u"备注") # 写入数据 excel_row = 1 for obj in list_obj: data_id = obj.id data_name = obj.manufactory data_license_num = obj.support_num data_version = obj.memo w.write(excel_row, 0, data_id) w.write(excel_row, 1, data_name) w.write(excel_row, 2, data_license_num) w.write(excel_row, 3, data_version) excel_row += 1 sio = StringIO.StringIO() ws.save(sio) sio.seek(0) response = HttpResponse(sio.getvalue(), content_type='application/vnd.ms-excel') response['Content-Disposition'] = 'attachment; filename=机房清单-'+mname+'.xls' response.write(sio.getvalue()) return response cmdb/urls.py """cmdb URL Configuration The urlpatterns list routes URLs to views. For more information please see:https://docs.djangoproject.com/en/1.11/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: url(r'^', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ from django.conf.urls import url from django.contrib import admin from cmdbapp import machinaroomviews,serverviews,networkdeviceviews,securitydeviceviews,softwareviews,storagedeviceviews,manufactoryviews from django.contrib.auth.views import * from django.contrib.staticfiles.urls import staticfiles_urlpatterns from django.contrib import staticfiles from django.views.static import serve urlpatterns = [ url(r'^static/(?P<path>.*)',machinaroomviews.main_page,name='main_page'), url(r'^base',machinaroomviews.base,name='base'), url(r'^search_machinaroom.html',machinaroomviews.add_machinaroom,name='add_machinaroom'), url(r'^edit_machinaroom.html',machinaroomviews.edit_machinaroom,name='edit_machinaroom'), url(r'^del_machinaroom.html',machinaroomviews.del_machinaroom,name='del_machinaroom'), url(r'^batdel_machinaroom.html',machinaroomviews.batdel_machinaroom,name='batdel_machinaroom'), url(r'^excel_machinaroom.html',machinaroomviews.excel_machinaroom,name='excel_machinaroom'), url(r'^login/',serverviews.search_server,name='search_server'), url(r'^add_server.html',serverviews.edit_server,name="edit_server"), url(r'^del_server.html',serverviews.del_server,name='del_server'), url(r'^batdel_server.html',serverviews.batdel_server,name='batdel_server'), url(r'^excel_server.html',serverviews.excel_server,name='excel_server'), url(r'^search_networkdevice.html',networkdeviceviews.add_networkdevice,name="add_networkdevice"), url(r'^edit_networkdevice.html',securitydeviceviews.search_securitydevice,name='search_securitydevice'), url(r'^add_securitydevice.html',securitydeviceviews.edit_securitydevice,name="edit_securitydevice"), url(r'^del_securitydevice.html',securitydeviceviews.del_securitydevice,name='del_securitydevice'), url(r'^batdel_securitydevice.html',securitydeviceviews.batdel_securitydevice,name='batdel_securitydevice'), url(r'^excel_securitydevice.html',securitydeviceviews.excel_securitydevice,name='excel_securitydevice'), url(r'^search_storagedevice.html',storagedeviceviews.add_storagedevice,name="add_storagedevice"), url(r'^edit_storagedevice.html',softwareviews.search_software,name='search_software'), url(r'^add_software.html',manufactoryviews.search_manufactory,name='search_manufactory'), url(r'^add_manufactory.html$',manufactoryviews.add_manufactory,name='add_manufactory'), url(r'^edit_manufactory.html',manufactoryviews.edit_manufactory,name='edit_manufactory'), url(r'^del_manufactory.html',manufactoryviews.del_manufactory,name='del_manufactory'), url(r'^batdel_manufactory.html',manufactoryviews.batdel_manufactory,name='batdel_manufactory'), url(r'^excel_manufactory.html',manufactoryviews.excel_manufactory,name='excel_manufactory'), ]

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

Linux初级知识

Linux内核创始人 : 芬兰人李纳斯.托瓦兹Linux是一套免费使用、自由传播的类unix操作系统特点是多用户、多任务、支持多线程和多CPU的系统Linux的发行版:比较知名的有Ubuntu、Redhat、Centos、FedoraLinux应用领域:通常服务器使用LAMP(Linux+Apache+Mysql+Php) LNMP(Linux+Nginx+Mysql+Php) Linux的启动过程,可以分为5个阶段1.内核的引导2.运行Int3.系统初始化4.建立终端5.用户登录系统 操作系统——》/boot--》init进程——》运行级别——》/etc/init.d--》用户登录

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

Java知识整理

Java面向对象的特征有哪些方面? 抽象 忽略与主题无关的其他信息 继承 extends的意思是扩展,即子类是父类的扩展。扩展的意义在于不需要重新造轮子,这也是继承的意义。 子类继承父类就可以得到父类的全部属性和方法,除了私有的属性和方法以及父类的构造方法。 Java 只允许单继承,目的是不想使得继承链过于复杂而导致系统难以维护。如果想”多继承”,可以使用接口代为实现。 如果没有指定父类,那么它的直接父类是java.lang.Object 继承会导致重写,子类有时会需要重写父类的方法,自身的行为替换父类的行为。方法的重写是实现多态的必要条件。重写需要符合三个条件 方法名、形参列表相同。返回值类型和异常类型,子类需要小于父类。访问权限,子类需要大于父类。 封装 封装的意思是想让你看到的就让你看到,不想让你看见的就隐藏起来。优点是高内聚低耦合。 高内聚:封装细节,保证安全。 低耦合:简化使用,便于扩展。 Java使用访问修饰符实现封装 private:私有的,只能在同一个类中使用 default:默认的,可以在同一个包中使用 protected:受保护的,可以由子类使用 public:公开的,可以被所有的类访问 一般使用peivate修饰属性,但提供get/set方法供外界访问。 多态 同一个方法调用,不同的对象可能会有不同的行为。例如同样是十一假期,有人会出去旅游,有人会宅在家里。 多态是方法的多态不是属性的多态。 多态的必要条件,继承、方法重写、父类引用指向子类对象。 java.util.comparator 接口是干什么? 如果我们需要控制某个类的次序并且这个类本身不支持排序,那么就可以建立一个类比较器来进行排序。 类比较器的只需要实现 java.util.Comparator 接口并实现对应的方法就可以了~ String类为什么使用final修饰 ? 如果String类可以被修改的话,那么就很难做到修改A中的String值的同时而不修改B中的String值。原因是String的两个引用有可能会来自同一块内存地址。 final修饰符是线程安全的,在创建后就不能修改,因此不会产生线程安全问题,所以可以在多线程环境下使用。 在现实生活中,如密码,如手机号码,如文件名称之类都是以String类型的方式存储,如果String不使用final修饰,那么就有可能账号和密码会被人不知不觉地修改了。Interger等包装类型也是同样的道理。而用于精确计算的BigDecimel类则不同,它主要是为了解决二进制不能很好的表示浮点数而设计的。 String类还是用了Static修饰符修饰,目的是方便调用。如果不使用Static,那么就不能在静态方法下直接调用String的方法或值,而必须使用new进行实例化。 String常量池的存储位置 在JDK1.6中 String存储在jvm的PermGen中,并且限制大小。同时该区域还用于存储类信息。 在JDK1.7中 String存储在heap 中, 大小无限制。 序列化是什么和什么时候使用序列化? 序列化在Java中一般指对象的序列化。具体含义是将对象转换为字节序列即二进制的过程。在这个过程中不仅仅保留当前对象的数据,而且会以递归地形式保存引用的每个对象的数据。利用对象序列化可以实现对对象的”深复制”。即复制对象本身以及引用的对象本身。序列化一个对象就可以得到整个对象序列。而反序列化则是将字节序列恢复为对象的过程。 pojo implements Serializable 序列化通常有两种用途,都是将对象写入字节流中。 当需要把对象的字节序列永久的保存到硬盘上时,一般以文件的形式。字节序列需要在网络上传输时。P:选择一种高效的序列化和反序列化方式可以提升整个架构的性能。 Java虚拟机内存中的堆区(heap),栈区(stack),和静态区(static/method)。 堆区 存储真正的对象,每个对象都包含一个与之对应的class信息,这个class信息的作用是得到操作指令。 特点:只有一个堆区,随着JVM的启动而创建,所有线程共享区域,因此不存放基本类型和对象引用,只存放对象本身。如果堆内存剩余的空间不足以创建对象,JVM会抛出OutOfMemoryError的错误。P:堆一般由程序员分配。 栈区 存放基本数据类型和引用数据类型。P:Strtus中的压栈出栈操作 特点:每个线程包含一个栈区,并且栈中的数据都是私有的。栈中的数据由编译器自动分配释放。当一段代码定义一个变量时,JVM就为该变量在栈中开辟空间,当该变量退出作用域时,JVM就释放该空间,这个空间就随时可以被其他变量使用。P:栈分为三个部分。基本类型变量区、执行环境上下文、操作指令区。 静态区 又名方法区,是所有线程共享的区域,存放所有的类信息(类名,类方法信息,类字段信息)和static修饰的变量。 特点:整个程序中永远唯一的元素。全局变量和静态变量都放一起。不同的是,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和静态变量在相邻的另一块区域。 HashMap和HashTable的区别? HashMap是HashTable的轻量级的非线程安全的实现。它们都完成了Map接口。采用的算法hash/rehash都大概一样,底层也都是采用数据加链表结构实现,所以性能上不会有很大差异。除了HashMap和HashTable,Hash集合中还有一个HashSet,它里面存储的并不是key-value结构,仅仅是存储不重复的元素,相当于简化版的HashMap,内部采用HashMap实现,所有的value都是同一个Object,只是包含HashMap中的key而已。 主要的区别在于HashMap允许空键值,而HashTable不允许。 由于HashMap是非线程安全,所以效率上会高于HashTable。 HashMap将HashTable中的contains()改成了containsValue()和containsKey()。原因大概是便于阅读吧~ HashMap直接继承Map,而Hashtable继承Dictionary。 HashMap在多线程的环境下需要为方法提供外同步(Collections.synchronizedMap),而HashTable不需要。 int类型和Integer类型有什么区别? Java提供了两种不同的类型,数据类型和引用类型,int是Java的数据类型,Integer是Java的引用类型。不仅是int,Java为每个基本类型都准备了对应的引用类型。它们的行为和语义都不同。 大小和速度的问题 存储数据结构问题 缺省值不同的问题 String、StringBuffer和StringBuilder的区别? String是作为常量在Java中被定义的,而StringBuffer是变量,意味着它可以修改对象内容。如果需要经常对一个字符串进行修改那么就可以考虑使用StringBuffer,因为它不会生成新的对象。 JDK1.5新增了一个可变的字符序列StringBuilder,它与StringBuilder有兼容但不同步的API,是StringBuilder的一个简单替换,用于字符串缓冲区被单个线程使用的时候,Java建议优先使用StringBuilder,因为在大多数实现中,它比StringBuffer速度要快,并且使用方法基本相同。 什么是运行时异常和非运行时异常? 非运行时异常指的是编译时异常或者说受检异常,Java编译器强制要求这类异常在代码编写的时候就处理,或抛出或捕获。运行时异常指的是虚拟机正常运行中可能会遇到的异常。 说说你常见的异常 *Java.lang.NullPointerException 程序遇上了空指针,简单地说就是调用了未经初始化的对象或者不存在的对象,这个错误经常出现在创建图片(路径),使用数组(初始化)中。 java.lang.ClassNotFoundException 类不存在,注意检查类的名称和路径是否正确 java.lang.ArrayIndexOutOfBoundsException 数组越界访问 java.lang.IllegalArgumentException 方法参数错误 java.lang.IllegalStateException org.springframework.web.util.NestedServletException 写一个Singleton单例模式吧 单例模式的特点 单例模式中的类只能有一个实例单例类必须自己创建自己的实例单例类必须给其他对象提供实例 在计算机系统中,线程池、端口、缓存、日志、对话框、打印机、显卡的驱动程序常常被设计成单例模式,这主要是为了避免发生诸如两台计算机同时打印时打印机该如何工作的问题,还有就是端口冲突的问题。再例如听歌,一般音乐软件同一时间只能播放一首歌。,虽然你有两个耳朵,但是这不代表你开心同时听两首歌~ 单例模式有两种表现形式 // 第一种形式 Class Singleton { Private Singleton(){} Private static Singleton instance = new Singleton(); // 提供一个供外界访问本类的静态API Public static Singleton getInstance(){} } // 第二种形式 Class Singleton { Private static Singleton instance = null; // 创建静态访问器,为了防止两个线程同时进行对象的创建,加上同步锁 Public static synchronized Singleton getInstance(){ If (instance == null) { Instance = new Singleton(); } Return instance; } } J2EE常用的设计模式,并重点说一下工厂模式。 Factory(工厂模式)Factory Method(工厂方法模式)Prototype(原始模型模式)Singleton(单例模式)Adapter(适配器模式)Decorator(装饰模式)Proxy(代理模式)Observer(观察者模式) 工厂模式是最常用的模式之一,根据工厂模式实现的类可以根据提供的数据生成一组类中的某一个类的实例,通过这一组类有一个公共的抽象父类并且实现了相同的方法,但是这些方法针对不同的数据进行了不同的操作。首先需要定义一个基类,该类的子类通过不同的方法实现了基类中的方法,然后需要定义一个工厂类,工厂类可以根据条件生成不同的子类实例。当得到子类实例侯,开发人员可以调用基类的方法而不必考虑到底返回哪一个子类的实例。 写一个工厂模式 工厂模式是Java中最常用的模式,原理是利用Java反射机制和多态的特性。目的是让程序更灵活,可以使项目并行开发,其中的粘合剂就是接口和配置文件。 Interface InterfaceTest{ Public void getName(); } // 有了接口就可以根据接口 进行并行开发 // 程序员A Class Test1 implements InterfaceTest{ Public void getName(){ System.out.println(“test1”); } } // 程序员B Class Test2 implements InterfaceTest{ Public void getName(){ System.out.println(“test2”); } } // 工厂类 生产接口对象 // 在调用时得到的是接口对象,一个接口变量指向实现该接口的类对象 // 通过键获取值,而不是类的全路径 Class Factory{ Private static Properties pro = new Properties(); Static { try { // 加载配置文件 Pro.load(new FileInputStream(“xxx.xxx”)); } catch (Exception e) { e.printStackTrace(); } } private static Factory factory = new Factory(); private Factory(){}; Public static Factory getFactory() { return factory; } Public InterfaceTest getInterface() { InterfaceTest interfaceTest = null; try { // 根据key,获取value value为类的全路径 String classInfo = pro.getProperty(“name”); // 利用反射生成class对象 Class c = Class.forName(classInfo); Object obj = c.newInstance(); interfaceTest = (interfaceTest )obj; }catch (Exception e) { e.printStackTrace(); } return interfaceTest ; } } // 调用方法 class FactoryTest { public static void main(String[] args) { Factory factory = Factory.getFactory(); // 通过创建的实例调用获得接口对象的方法获取接口对象 InterfaceTest inter = factory.getInterface(); // 调用接口定义的方法 Inter.getName(); } } 说出ArrayList、Vector、LinkedList的存储性能和特性。 ArrayList和Vector都使用数组的方式存储数据。当数组元素大于实际存储的数据时以便增加和插入元素,它们都允许直接按序号索引元素,但是插入数据要涉及到数组元素移动等内存操作,所以索引数据块而插入数据慢,Vector由于使用了synchronized方法保证线程安全,因此性能上较ArrayList差。 LinkedList使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,索引速度慢。因为插入数据时只需要记录本项的前后项即可,插入速度较快。 Collection和Collections Collection是集合类的上级接口,继承于它的接口主要是Set和List。 Collections是针对集合类的一个帮助类,提供了一系列静态API,实现了对集合的搜索、排序、线程安全化等操作。 final、finally、finalized之间的区别? final用于声明属性、方法和类,分别表示该属性不可变、该方法不可覆盖、该类不可继承。 定义变量时加上final,代表一旦被初始化后就不可改变 将方法声明为final,则说明你已经知道这个方法提供的功能已经满足你要求不需要进行扩展,并且也不允许任何从此类继承的类来覆写这个方法。只能通过继承这个方法来直接使用。 当final用于类的时候,代表此类在继承树中是一个叶子类,如果你认为此类涉及已经很完美而不需要进行修改和扩展的时候就大胆使用它吧。 finally是异常处理结构语句的一部分,表示该代码块中的代码总是执行。如果是断电或者强行中断程序,则该块中的代码才不会被执行。 finalize是Object类中的一个方法,在垃圾回收器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾回收时其他资源的回收,例如关闭文件。 说出你知道的线程同步方法 wait()使一个线程处于等待状态,并释放所持有对象的lock. sleep()使一个正在运行的线程处于睡眠状态,是静态方法,调用此方法要捕捉InterruptedException异常。 notify()唤醒一个处于等待状态的线程。注意是在调用此方法时,并不能确切知道唤醒的是那个等待状态的线程,也不是按优先级,而是由JVM确定唤醒的线程。 allnotity()唤醒所有处于等待状态的线程,注意不是给所有唤醒线程一个对象的锁,而是让它们竞争。 OverLoad 和 Override的区别 方法的重载和重写是Java多态的表现。前者是父子类之间,后者是同一个类之中。如果说子类中定义某方法与父类中的方法名称和参数都相同的话,我们就说该方法被重写了。如果在同一个类中定义了多个同名的方法,它们有不同的参数个数和参数类型,则称之为方法的重载。 interface和abstract 两者是Java面向对象的重要实现。声明方法的存在而不去实现它的类叫做抽象类,它的子类必须全部实现它父类的所有方法,否则它也是一个抽象类。抽象类不能有抽象构造函数和抽象静态方法。也不能被实例化,并且它只能被public、protected修饰符修饰。 接口是抽象类的变体。在接口中所有方法都是抽象的,并且它只可以定义static final的成员变量。Instanceof运算符可以用来决定某对象的类是否实现了接口。 什么情况下使用同步或者异步编程 如果数据将在线程间共享,正在写的数据以后或者已经可能被另一个线程读到,那么这些数据就是共享数据,就必须使用同步存取。 当应用程序在对象上调用了一个需要花费很长时间来执行的方法,例如下载操作,这个时候不希望程序等待方法的返回,你在下载音乐的同时也可以听音乐,这个时候就需要使用异步编程。类似这种场景下异步途径往往更具有效率。 GC是什么?为什么需要GC? 内存处理是程序员最容易出错的地方,忘记或者错误的内存回收会导致堆栈溢出系统崩溃等严重错误。Java并没有提供显示的释放已分配内存的操作方法,而是提供GC功能,用于自动检测对象是否超出作用域从而达到自动回收内存的目的。这个功能被称之为垃圾回收机制。 垃圾回收的优缺点?和回收机制 优点是程序员不再需要考虑内存管理,同时垃圾回收可以有效地防止内存泄露,有效地使用可用的内存。 缺点则是垃圾回收器作为一个单独的低级别线程运行,在不可预知的情况下对内存已经死亡或者长时间没有使用的对象进行清除和回收,程序员不能监测到异常情况,也不能实时调用垃圾回收器对对象进行垃圾回收。程序员可以手动调用System.gc(),但java语言规范并不保证GC一定会运行。 垃圾回收的机制有:分代复制垃圾回收、标记垃圾回收、增量垃圾回收。 启动一个线程使用start()还是run()? start()使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行,但并不意味着线程就会立即执行。此时线程处于就绪状态。 run()线程体,包含要执行线程的内容,当线程得到CPU的时间片,就会开始执行相应线程的此方法。 使用start()开辟线程,使用run()执行线程。 为什么要有多线程? 线程的出现是为了更好的利用CPU,提高程序运行效率。 说一说你开发过程中常见到的runtime exception ClassCastException IndexOutOfBoundsException NullPointerException SystemException 线程池的实现原理 使用线程池对线程进行统一分配,调优,监控可以降低资源消耗,提高相应速度,提高线程的可管理性。 J2EE服务器会在启动时建立若干线程池连接,并一直维持不少于此数目的池连接,当客户端程序需要连接时,池连接程序会返回一个未使用的池连接并标记为忙。如果当前没有空闲连接,池驱动程序就新建一定数量的连接,新建连接的数量由配置参数决定,当使用的池连接调用完成后,池驱动程序将池连接标记为空闲,其他调用就可以使用这个连接。 forward和redirect forward转发是服务器请求资源,服务器直接访问目标地址的URL,将URL的响应内容读取过来,然后将这些内容再发送给浏览器,浏览器因此并不知道服务器发送的内容是从哪里来的,所以它的地址栏还是原来的地址。 redirect重定向是服务端根据业务逻辑,发送一个验证码,告知浏览器去请求某个地址,一般来说,浏览器会用刚才请求的参数重新请求,所以session、request参数都可以获取到。 Set集合中的元素不能重复,那么怎样区分重复与否呢?使用==还是equals()?它们有何区别? Set里的元素不能重复,那么可以使用iterator()方法区分是否重复。 equals()是判断两个Set是否相等。属于深度比较,为的是当两个分离的对象的内容和类型相匹配的话返回ture。而”==”是比较两个对象的引用是否指向同一个内存地址,也就是判断对象是否分离。 hashCode()通常被设计用于提高性能。它和equals()的区别就在于当两个对象相等(equals()),那么它们就一定拥有同样的哈希值,但如果两个对象的哈希值相等,则不一定代表这两个对象就一定相等。 Java中异常机制的简单原理和应用 当Java程序违反了java的语义规则时,虚拟机就会将发生的错误表示为一个异常。违反语义包括了两种情况。 Java类库内置的语义检查。如果数组下标越界则会引发IndexOutBoundsException;访问null对象时会引发NullPointerException。另一种是程序员扩展的语义检查,自定义异常并自由选择在何时使用throw关键字引发异常。所有自定义的异常都必须继承java.lang.Thowable。 XML文档定义有几种形式?它们有何本质区别?解析XML文档有几种方式? 两种形式:dtd和schema dtd(data type definition) 数据类型定义,用于描述XML文档的文档结构,是早期的XML文档定义形式。 schema 本身基于XML编写,在类型和语法的限定能力上比dtd强,处理也方便,因此正逐渐代替dtd成为新的模式定义语言。 本质区别:schema本身是xml,可以被xml解析器解析。(这也是从DTD上发展schema的根本原因) 解析方式:DOM、SAX、STAX等 DOM 处理大型文件时性能下降厉害,这个问题是由DOM的树结构造成的,这种结构占用的内存较多,而且DOM必须在解析文件之前把整个文档都装进内存,便于对XML的随机访问。 SAX 事件驱动型的XML解析方式。它按顺序读取XML文件,而不需要一次全部装载整个文件。当遇到像文件开头,文档结束或者标签开头与标签结束时会触发一个事件,用户通过在其回调事件中写入处理代码来处理XML文件,适合对XML的顺序访问。 STAX Streaming API for XML(StAX) Servlet Public void init(ServletConfig config); Public ServletConfig getServletConfig(); Public String getServletInfo(); Public void service(ServletRequest request, ServletResponse response); Public void destroy(); - init()方法在servlet生命周期中仅执行一次,在服务器装载servlet时执行。缺省的init()通常是符合要求的,不过也可以根据需要override,比如管理服务器端资源,一次性装入Gif图像,初始化数据库连接等。由于缺省的init()中设置了servlet的初始化参数,并使用了ServletConfig对象参数来启动配置,所以覆盖init()时,需要调用super.init()以确保仍然执行这些任务。 - service()是servlet的核心方法,在调用service()之前,应确保已完成了init()。对于HttpServlert,每当用户请求一个HttpServlet对象,该对象的service()就会被调用,HttpServlet缺省的service()中的服务功能就是调用与HTTP请求的方法对应的do功能,所以对于HttpServlet,一般都重写doPost()或者doGet()。 - destroy()在servlet的生命周期中也仅执行一次,在服务器停止并卸载servlet时执行,将servlet作为服务器进程的一部分进行关闭。缺省的destroy()是符合要求的,但也可以override。比如在卸载servlet时将统计的数字保存在文件中,比如关闭数据库连接。 - getServletConfig()返回一个servletConfig对象。该对象用来返回初始化参数和servletContext。servletContext接口提供有关servlet的环境信息。 - getServletInfo()提供有关servlet的信息,比如作者、版本、版权。 #throw和throws >throw语句用来明确地抛出一个异常 >throws用来表明一个成员函数可能抛出的各种异常 #排序有哪几种方法?请列举并口述用java实现快速排序。 >排序的方法有 * 插入排序(直接插入排序,希尔排序) * 交换排序(冒泡排序,快速排序) * 选择排序(直接选择排序,堆排序) * 归并排序 * 分配排序(箱排序,基数排序) >快速排序的伪代码 从a[0:n-1]中选择一个元素作为middle,该元素为支点。 将余下的元素分割为left和right两段。使left中元素都小于等于支点,而right中的元素都大于等于支点。 递归地使用快速排序方法对left进行排序,然后是right 所得出的结果为left+middle+right; #MVC的各个部分都有那些技术实现?怎么实现的? - Model 代表着业务逻辑,通过JavaBean实现 - View 是应用的表现层,用于用户交互,使用JSP页面技术实现 - Controller 应用程序处理过程控制,一般是Servlet >优点:开发效率高;程序灵活性和扩展性好;代码重用度高;便于人员分工。 >缺点:代码复杂度增加;代码数量增加;不适合开发小型程序。 #java中有几种方法可以实现一个线程?用什么关键字修饰同步方法?Stop()和suspend()为何不推荐使用? >有两种方法,分别是继承Thread类和实现Runnable接口。 >使用关键字synchronized修饰同步方法 >反对使用stop()是因为不安全。它会接触由线程获取的所有锁定,而且如果对象处于一种不连贯的状态,那么其他线程能在那种状态下检查和修改他们,结果很难检查出真正的问题所在.suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获取的锁定,此时,其他线程都不能访问锁定的资源,除非被”挂起”的线程恢复运行。对任何线程来说,如果它们都想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便使用wait()命令其进入等待状态,若标志指出线程应当恢复,则使用一个notify()重新启动线程。 #java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类? >字节流、字符流。字节流继承InputStream OutputStream 字符流继承于InputStreamReader OutputStreamWriter >在java.io包中还有许多其他的流,主要是为了提高性能和使用方便 #内部类可以引用他包含类的成员吗?有什么限制? >一个内部类对象可以访问创建它的外部类对象的内容。 >内部类如果不是static,那么它可以访问创建它的外部类对象的所有属性。如果是static,即为nested class,那么它只可以访问创建它的外部类对象的所有static属性。 >一般类只有public或者package等访问修饰符。而内部类可以实现static、protected、private等访问修饰符。 >当从内部类继承的时候内部类是不会被覆盖的,它们是完全独立的实体,每个都在自己的命名空间内,如果从内部类中明确地继承,就可以覆盖原来内部类的方法。 #进程和线程的区别 + 进程 是相对于操作系统而言的。例如你一边听歌一边玩游戏,我们就会说此时系统内有两个进程在运行,专业的话说叫做多个程序几乎在同一时间执行多个任务。 + 线程 相对某一程序而言。例如你一边听歌一边看歌曲排行榜,还可以下载歌曲,这三件事情互相不会干扰,就可以说这一音乐程序至少有三个线程在运行,专业的表述为一个程序在同一时间内执行多个任务。 #你能说出TCP/IP的七层协议吗? >应用层,、表示层、会话层、传输层、网络层、数据链路层、物理层。 #在connection类中提供了3个控制事务的方法,说说它们。 - setAutoCommit(boolean bln) 保持数据的完整性。一个系统的更新操作可能要涉及多张表,需要多个SQL语句进行操作,循环连续的进行插入操作,如果在开始时设置了”conn.setAutoCommit(false);”,然后再进行”conn.commit()”。这样即使插入的时候报错,修改的内容也不会提交到数据库,而如果没有手动进行setAutoCommit,那么出错的时候就会造成,前几条数据插入成功而后几条数据插入失败的情况,这就是脏数据。 - commit() 提交事务,用于把事务所做的修改都保存到数据库中,它把上一个commit或者rollback命令之后的全部事务都保存到数据库中。 - rollback() 撤销事务,在事务运行的过程中发生了某种故障,事务不能继续执行,系统将事务中对数据库的所有已经完成的操作全部撤销,回滚到事务开始的状态,这里的操作特指数据库更新操作,并且回滚后,事务进入提交状态,因为回滚是回滚到事务开始时的状态。 #EL表达式的隐含对象 * applicationScope 应用程序范围内的scoped变量组成的集合 * cookie 所有cookie组成的集合 * header HTTP请求头部,字符串 * headerValues HTTP请求头部,字符串集合 * pageContext 当前页面的javax.servlet.jsp.PageContext对象 * initParam 全部应用程序参数名组成的集合 * pageScope 页面范围内所有对象的集合 * param 所有请求参数字符串组成的集合 * paramValues 所有作为字符串集合的请求参数 * requestScope 所有请求范围的对象的集合 * sessionScope 所有会话范围的对象集合 #简述Statement和PreparedStatement的区别 >无论多少次地使用同一SQL命令PreparedStatement都只对它解析和编译一次,当使用Statement对象时,每次执行一个SQL命令的时候都会对它解析和编译。因此使用PreparedStatement要比使用Statement速度要快。 #java中包的用途 * 允许将类文件组织起来,便于查找合适的类 * 包可以包含其他的,形成有层次的包空间 * 包有助于避免命名冲突 #请简述一下什么是流? >流是指一连串流动的字符,以先进先出的方式发送和接收数据的通道。流分为输入流和输出流,相对于内存而言,数据输入到内存就是输入流,反之就是输出流。 #java.io.reader和java.io.inputStream的区别? >两者共同组成了java输入类。Reader用于读入16位字符,即Unicode编码的字符;而InputStream用于读入ASCLL字符和二进制数据。 #请说出ArrayList和Vector的区别? - 同步性 数组序列是线程不安全也不同步的,而矢量队列则完全相反。 - 数据增长 需要增长时,数组序列增长原来的一半,而矢量队列增长一倍。 #super关键字是干什么的?为什么使用它?它的访问范围? >super用于解决如何直接访问或初始化从父类继承来的成员。 >在写子类无参数构造方法时,不用显式调用父类无参数构造方法,系统会自动提供,但在写子类带参数的构造方法时,应该在第一句写super(参数)来初始化父类成员变量 >访问范围包括父类属性、一般方法和构造方法 #Cookie和Session的区别与联系? - Session的概念 Session 是存放在服务器端的,类似于Session结构来存放用户数据。当浏览器第一次发送请求时,服务器自动生成一个Session和Session ID用来标识这个Session,并将通过相应发送到浏览器。当浏览器第二次发送请求,会将前一次服务器响应中的Session ID放在请求中一并发送到服务器上,服务器从请求中提取Session ID并和保存的所有Session ID进行对比,找到这个用户对应的Session。一般浏览器提供了两种方式来保存Session,还有一种是程序员使用HTML隐藏域的方式自定义实现。 使用Cookie实现保存,这是最常见的方法,例如”记住我的登录状态”这一功能就是通过这种方式实现的。服务器通过设置Cookie的方式将Session ID发送到浏览器。如果我们不设置过期时间,那么Cookie将不会保存在硬盘上,会随着浏览器的关闭而消失,Session ID也就不复存在了。如果我们设置这个时间为若干天后,那么这个Cookie会保存在客户端硬盘中,即使浏览器关闭,这个值仍然存在,下次访问相应的网站时同样会发送到服务器上去。 使用URL附加信息的方式,这也是有时候我们会在JSP网站上看到”xxx.jsp?JSESSIONID=x”的原因。这种方式和使用Cookie的方式中不设置过期时间是一样的。 第三种方式是通过在页面表单里增加隐藏域的方式,这种方式实际上跟URL附加信息的方式一样,只不过前者使用GET方式发送数据,后者使用POST方式发送数据,但是明显后者比前者麻烦。 - 会话技术 会话指的是用户登录网站后的一系列操作,例如浏览商品->添加到购物车->提交订单->付款。会话跟踪时WEB程序中常用的技术,用户跟踪用户的整个会话,常用的会话跟踪技术就是Session和Cookie。Session在服务端记录信息并确认用户身份,Cookie在客户端记录信息并确认用户身份。 - 区别 Cookie存储在浏览器客户端,Session存储在服务端,简单说,当你登录一个网站的时候,如果web服务器端使用的是session,那么所有的数据都保存在服务器上面,客户端每次请求服务器的时候会发送当前会话的session id,服务器根据当前session id判断相应用户数据标志,以确定用户是否登录或者具有某种权限。由于数据时存储在服务器上面,所以无法伪造,当时如果你能获取某个登录用户的session id,用特殊的浏览器伪造该用户的请求也是能够成功的。session id 是服务器和客户端链接时随机分配的,一般来说不会重复,但如果存在大量并发的请求,也不是没有重复的可能性。 - 联系 Cookie是Session的一种,但Cookie不会占用服务器资源,是存储在客户端内存或者一个cookie的文本文件中;而Session则会占用服务器资源,从这点上看,应该尽量使用Cookie而不是Session。 如果用户禁用cookie,那么可以采用url重写技术来进行页面处理,调用session中大量有用的方法从session中获取数据后置入页面。 - 应用场景 Cookie的典型应用时下次直接登录和在线商城的购物车的设计,当然这其中也会有一些安全和性能的问题存在。 由于Cookie的不安全性,所以一般会在Session上保存重要的信息。 #JSP的九大内置对象及其作用 >内置对象指的是可以不加声明和创建就可以在JSP脚本中使用的成员变量. >产生的时机:一个JSP页面对应一个Servlet类,每个Servlet有三个方法。init()初始化JSP;destory()销毁JSP;service()对用户请求产生相应的方法。Request和response是service()的形参,application、page、out、pageContext、session这些对象都是在service()中生成的实例。 * javax.servlet.httpServletRequest 客户端的请求信息被封装在request对象中,主要用于接受通过HTTP协议传送到服务器的数据,包括头信息,系统信息,请求方式以及参数等,作用域为一次请求。 * javax.servlet.httpServletResponse 代表对客户端的相应,主要是将JSP容器处理过的对象传回客户端,response对象只在JSP页面有效。 * Session Session是由服务器自动创建的与用户请求相关的对象,服务器为每个用户都生成一个session对象,用户保存用户的信息,跟踪用户的操作状态。它内部使用Map类来保存数据,即key/value,value可以是复杂的对象类型,而不仅仅局限于字符串类型。 * application Application对象可以将信息保存到服务器直到服务器关闭为止,可以认为是系统中的全局变量,是ServletContext类的实例。 * out 此对象用于在web浏览器内输出信息,并且管理应用服务器上的输出缓冲区,是JspWriter的实例。 * pageContext 此对象提供了对JSP页面内所有对象以及命名空间的访问,它的本类名也叫做pageContext。 * config 获取服务器的配置信息 * page 它是java.lang.Object的实例,代表着JSP本身,本质上包含当前Servlet接口引用的变量,可以看做Java编程中的this * exception 显示异常信息,只有在包含isErrorPage=”true”的页面上才可以使用。JAVA程序可以使用try/catch处理异常信息,但在JSP页面中出现异常就会生成exception对象,并把该对象传送到在page指令中设定的错误页面中,程序员在错误页面中处理exception对象即可。跟JAVA一样,JSP中exception对象也是由系统提供的继承机构,它实际上是java.lang.Throwable的对象。 #说一说你常用的Linux命令 * ls 显示文件或目录 -l 列出文件详细信息(list) -a 列出当前目录下所有的文件及目录,包括隐藏文件(all) * mkdir 创建目录 - p 创建目录,如果没有父目录则创建父目录(parent) * cd 切换目录 * touch 创建空文件 * echo 创建带有内容的文件 * cat 查看文件内容 * cp 拷贝 * mv 移动或者重命名 * rm 删除文件 -r 递归删除,删除文件时一并删除子目录及文件 -f 强制删除 * find 在文件系统中搜索某文件 * wc 统计文本中行数、字数、字符数 * grep 在文本文件中查找某个字符串 * rmdir 删除空目录 * tree 显示目录为树形结构,需要安装tree包 * pwd 显示当前目录 * in 创建链接文件 * more、less 分页显示文本文件内容 * head、tail 显示文件头、尾内容 * Ctrl+alt+F1 命令行全屏模式 ------系统管理命令------ * stat 显示指定文件的详细信息,比ls更详细 * who 显示在线登录用户 * whoami 显示当前操作用户 * hostname 显示主机名 * uname 显示系统信息 * top 动态显示当前耗费资源最多的进程信息 * ps 显示瞬时进程的运行状态,ps aux:以BSD格式显示进程;ps ef:以标准格式显示进程 * kill 杀死进程,一般使用ps或top命令查看进程的id,然后使用kill命令杀死对应进程。 * du 查看目录大小 -h 带有单位地显示目录信息 * df 查看磁盘大小 -h 带单位显示磁盘信息 * ifconfig 查看网络情况 * ping 测试网络连通 * netstat 显示网络状态信息 * man 帮助命令,使用-加上其他命令来查看对应参数的使用 * clear 清屏 * alias 对命令重命名 * sudo 允许系统管理员让普通用户执行一些或者root命令的工具 -b 在后台执行指令 -h 显示帮助 -H 将HOME环境变量设置为新身份的HOME环境变量 -k 结束密码的有效期,使用sudo的时候先输入密码,默认为5分钟有效期限 -l 列出目前用户可执行与无法执行的指令 -p 改变询问密码的提示符号 -s<shell> 执行指定的shell -u<用户> 以指定的用户作为新的身份,若无此参数,则默认使用root为新的身份 -v 延长密码有效期限5分钟 -V 显示版本信息 ------打包压缩相关命令------ * tar 打包压缩 -c 归档文件 -x 压缩文件 -v 显示压缩或解压缩过程(view) -f 使用档名 -z gzip压缩文件 -j bzip2压缩文件 例如 tar -cvf /home/java.tar home/java 只打包不压缩 tar -zcvf /home/java.tar home/java 打包并使用gzip压缩 ------打包压缩相关命令------ * shutdown -r 关机重启 -h 关机不重启 now 立即关机 * halt 关机 * reboot 重启 ------VIM相关命令------ Vim中有三种模式,命令模式、插入模式和编辑模式,使用ESC或i或:来切换模式。命令模式下 :q 退出 :q! 强制退出 :wq 保存并退出 :set number 显示行号 :set nonumber 隐藏行号 /java 在文档中查找java这个字符串,按n跳下一个,shift+n上一个 yyp 复制光标所在行,并粘贴 ------文件权限管理------ R 读 数值表示为4 W 写 数值表示为2 X 可执行 数值表示为1 更改权限命令语法 sudo chmod[u=所属用户/g=所属组/o=其他用户/a所有用户][+=增加权限/-=减少权限][r/w/x] 目录/文件名 #Java运行时区域 程序计数器:记录当前指令所在单元地址,也就是程序执行.class文件某行。由于存储的只是一个行数number,所以区域较小。在多线程环境中,每个线程拥有一个程序计数器,分别记录着各自程序的执行程序所在行数。 JVM栈:存放执行的方法,方法中的局部变量和对象的引用(对象的实例存放在堆中)。由于拥有着先进后出的特性,所以可以看做一个桶。对桶中数据的操作成为压栈和弹栈。 本地方法栈:存放执行的本地方法,即使用native关键字修饰的方法。当程序执行到本地方法时,程序计数器不会记录,而是使用undefined替代(undefined和null的主要区别在于null表示此处不应该有值,而undefined表示此处有值但是还没有定义) Java堆:存放类的实例和数组。分为新生代(young generation)和老年代(old generation),这样划分主要是为了方便垃圾回收器的工作,垃圾回收器会频繁地扫描新生代中的对象并进行垃圾回收,没有进行垃圾回收的对象就被移到了老年代中。 P:为了更加方便垃圾回收器的工作,JVM在新生代中又进行划分(eden和survicor)~~ 方法区:又被称为非堆,存放类的基本信息以及常量(final)和静态(static)变量,这些数据被定义为永久代,垃圾回收器不会回收这些数据。 P1:此方法区跟java中的方法没有任何关系~ P2:JDK文档中说方法区是JAVA堆逻辑上的一部分。 P3:在JDK8中取消了永久代而是使用了metespace替代 运行时常量池:存放字面量(例如int number = 10 中的10就是一个字面量)和符号引用(例如类与引用名的全限定名,List<Dept> deptList = new ArrayList<Dept>();中的deptList实际是一个java.lang.List) P1:常量池的出现是为了避免频繁的创建和销毁对象而影响到系统性能,其实现了对于对象的共享。例如字符串常量池就是在编译期间就把所有字符串文字放到一个常量池中。 P2:字符串常量池在JDK7时被移到了Java堆中(String name = “老王”中name作为引用存放到字符串常量池中,”老王”作为实例存放到堆中)。 P3:程序计数器、JVM栈和本地方法栈是线程私有的,Java堆、方法区和运行时常量池是线程共享的,因此一个多线程程序出现BUG,那么一定是线程共享中的区域出现了问题。 P4:Java规范中指出,除了程序计数器之外其他区域都有可能会发生OutOfMemoryError(内存溢出)异常。除了程序计数器和JVM栈其他区域都有可能会发生StackOverflowError(栈溢出)异常。使用JConsole(java自带)可以监控当前程序的内存和系统资源使用情况。 #常用的SQL语句 # stuscore 表中数据结构 stuid name subject score 1 张三 数学 91 1 张三 英语 89 1 张三 语文 87 2 李四 语文 89 2 李四 英语 28 2 李四 数学 99 3 王五 语文 66 3 王五 数学 24 3 王五 英语 32 4 朱六 数学 88 >计算每个人的总成绩并排名,要求显示字段:名字和总成绩 思路:根据名字分组 group by 根据成绩排名 order by SELECT name,SUM(score) FROM stuscore GROUP BY name ORDER BY SUM(score) >计算每个人的总成绩并排名,要求显示字段:名字、学号和总成绩 思路:DISTINCT 去重关键字 使用子查询先查询学号和总成绩 SELECT DISTINCT t1.name, subject,MAX(score) FROM stuscore t1, (SELECT stuid,SUM(score) AS allscore FROM stuscore GROUP BY stuid) t2 WHERE t1.stuid=t2.stuid ORDER BY t2.allscore DESC >计算每个人单科的最高成绩,要求显示字段:学号、姓名、课程、最高成绩 思路:有两种写法 #写法一 SELECT stuid,name,subject,MAX(score) FROM stuscore GROUP BY stuid #写法二 SELECT t1.stuid,t1.name,t1.subject,t1.score FROM stuscore t1, (SELECT stuid,MAX(score) AS maxscore FROM stuscore GROUP BY stuid) t2 WHERE t1.stuid=t2.stuid AND t1.score=t2.maxscore >计算每个人的平均成绩,要求显示字段:学号、成绩、平均成绩 思路:使用AVG()函数求出平均成绩,然后根据name分组 SELECT stuid,name,AVG(score) FROM stuscore GROUP BY name >列出各门课程成绩最好的学生,要求显示字段:学号、姓名、科目、成绩 #写法一,这种写法会出现问题 SELECT stuid,name,subject,MAX(score) FROM stuscore GROUP BY subject #写法二 SELECT s1.stuid,s1.name,s1.subject,s2.max_score FROM stuscore s1, (SELECT subject,MAX(score) AS max_score FROM stuscore GROUP BY subject) s2 WHERE s1.subject=s2.subject AND s1.score=s2.max_score >列出成绩最好的两位学生,要求显示字段:学号、姓名、成绩 思路:使用DESC将查询结果降序,然后使用limit截取列数 SELECT stuid,name,SUM(score) AS sumscore FROM stuscore GROUP BY name ORDER BY sumscore DESC LIMIT 2 #写法二,在SQL Server中的top关键字 SELECT top 2 * FROM stuscore ORDER BY sumscore DESC >列出数学成绩最好的学生 SELECT name,subject,score FROM stuscore WHERE subject=’数学’ ORDER BY score DESC >求出某位学生的数学成绩排名 思路:使用rownum临时变量输出mysql的排序后的行号 SELECT s.rowNo FROM (SELECT (@rowNum:=@rowNum+1) AS rowNo,name,subject,score FROM stuscore, (SELECT (@rowNum :=0)) b WHERE subject=’数学’ ORDER BY stuscore.’score’ DESC) s WHERE name=’张三’ >统计学科成绩优、良、及格的个数 SELECT subject, (SELECT COUNT(*) FROM stuscore WHERE score>80 AND score <=100) AS 优, (SELECT COUNT(*) FROM stusocre WHERE score>60 AND score <80) AS 良, (SELECT COUNT(*) FROM stuscore WHERE score<60) AS 不及格 FROM stuscore GROUP BY subject >使用SQL语句输出以下格式的数据 # 学号 姓名 语文 数学 英语 总分 平均分 SELECT stuid AS 学号,name AS 姓名, SUM(CASE WHEN subject=’语文’ THEN score ELSE 0 END) AS 语文, SUM(CASE WHEN subject=’数学’ THEN score ELSE 0 END) AS 数学, SUM(CASE WHEN subject=’英语’ THEN score ELSE 0 END) AS 英语, SUM(score) AS 总分,(SUM(score)/COUNT(*)) AS 平均分 FROM stuscore GROUP BY stuid,name >使用一条SQL语句查询出每门课都大于80的学生姓名 思路:使用HAVING()筛选成组后的各种数据,真实表中没有此数据,这些数据是通过一些函数生存。需要注意的是where子句在聚合前先筛选记录,作用于group by 和 having子句之前 SELECT name FROM stusocre GROUP BY name HAVING MIN(score)>=80 AND COUNT(subject)>=3 >修改表结构,学历 #添加列信息 Alter table stuscore add 学历 varchar(6); #删除列信息 Alter table stuscore drop column 学历; >修改表数据 朱六 #将数学成绩改为90 UPDATE stuscore SET score=90 WHERE name LIKE ‘朱六’; #删除表数据似乎不能使用like进行delete DELETE FROM stuscore WHERE stuid=4; >连接查询 # 当用户查看的数据来自于多张表的时候,连接查询会将多张表按照某个指定的条件进行记录的拼接。最终的结果是记录数可能会变化,但字段数一定会增加。 SELECT s1.*,s2.* FROM student AS s1 left join stuscore AS s2 ON s1.student_id = s2.stuscore_id #能介绍一下redis吗?在项目中你是怎么使用它的? >redis是最常用的非关系型数据库(Not-Only SQL)中的键值存储数据库(key-value),除了键值存储数据库之外还有文档型数据库(MongoDB)、图形(Graph)数据库(InfoGrid)、列存储数据库(HBase)。 对数据库高并发读写(High performance)的需求;对海量数据的高效率存储访问(Huge Storage)的需求;对数据库的高可扩展性(High Scalability)和高可用性( High Availability)的需求,带动了NOSQL的发展。 >redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,类似于memcached(比之出色的地方在redis的value的最大限制是1GB,而memcached只有1MB),它通过异步的方式将整个数据库加载到内存中然后进行操作,因为是纯内存操作,所以性能很好。不仅仅是出色的性能,它还通过提供多种键值数据类型来适应不同场景下的存储需求。 - 字符串类型 redis没有采用C语言对字符串的处理方式,而是自定义了一个数据结构SDS(imple dynamic string),简单动态字符串。打开redis源码包,在src下的sds.h文件下查看sds的源码如下: struct sdshdr { // 字符串长度 unsigned int len; // buf数组中未使用的字节数量 unsigned int free; // 用于保存字符串 char buf[]; }; >C语言对字符串的存储是使用字符数组,遇到'\0'字符则认为字符串结束,而redis的字符串可以存储任何类型的数据,因为任何类型数据都可以表示成二进制,sds结构中的char buf[]就是存储了二进制数据。并且redis的字符串是二进制安全的,什么是二进制安全?简单理解就是存入什么数据取出的还是什么数据。redis中的sds不像c语言处理字符串那样遇到'\0'字符则认证字符串结束,它不会对存储进去的二进制数据进行处理,存入什么数据取出还是什么数据。 >应用:自增主键,例如电商项目中的订单号和商品编号都是采用String的递增数字特性生成,使用INCR(此命令用于将key中存储的数字值增1)命令完成。 定义商品编号key:items:id 192.168.101.3:7003> INCR items:id (integer) 2 192.168.101.3:7003> INCR items:id (integer) 3 散列类型 hash 如果有一个对象User以JSON序列化(String)的形式存储在redis,它的存储过程是User->JSON->Redis。该User对象有id、name、age等属性。如果在业务中只需要修改name属性,其他属性不做修改时该怎么做?如果采用传统的存储过程时必然会导致资源的的浪费,使用hash可以解决此问题。 hash会怎么做?它会提供字段与字段值的映射,并且规定字段值只能是字符串不能是散列或者集合类型。使用HSET(此命令不区分插入和更新操作,当执行插入操作时HSET命令返回1,当执行更新操作时返回0)命令对字段值做修改: HSET user name laowang 应用:商品信息,包括商品id、商品名称、商品描述、商品库存、商品好评 商品信息在redis中为item:1001 获取命令 HGET items:1001 id "3" HGETALL items:1001 1) "id" 2) "3" 3) "name" 4) "apple" 5) "price" 6) "999.9" 列表类型 -arrayList && linkedList ArrayList使用数组方式存储数据,所以根据索引查询数据速度快,而新增或者删除元素时需要设计到位移操作,所以比较慢。LinkedList使用双向链接(AB互相指向)方式存储数据,每个元素都记录前后元素的指针,所以插入、删除数据时只是更改前后元素的指针指向即可,速度非常快,然后通过下标查询元素时需要从头开始索引,所以比较慢,但是如果查询前几个元素或后几个元素速度比较快。 -List 列表类型可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素,或者获得列表的某一个片段。列表类型内部是使用双向链表(double linked list)实现的,所以向列表两端添加元素的时间复杂度为0(1)(算法中的时间复杂度,它和空间复杂度被合称为算法的复杂度,是衡量算法重要指标),获取越接近两端的元素速度就越快。这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是极快的。 应用,商品评论列表 思路:在redis中创建商品评论列表,当用户发布商品评论时,将评论信息转成json存储到list中,用户在页面查询评论列表,从redis中取出json数据展示到页面。 定义商品评论列表key: 商品编号为1001的商品评论key:items: comment:1001 LPUSH items:comment:1001 '{"id":1,"name":"色情买家举报了!!!","date":20180510}' 集合类型 -set 集合的含义是每个元素的顺序不同,且没有顺序。集合类型的常用操作是向集合中加入或删除元素、判断某个元素是否存在等,由于集合类型的Redis内部是使用值为空的散列表实现,所有这些操作的时间复杂度都为0(1)。 Redis还提供了多个集合之间的交集、并集、差集的运算。 有序集合类型 - 在集合类型的基础上有序集合类型为集合中的每个元素都关联一个分数,这使得我们不仅可以完成插入、删除和判断元素是否存在在集合中,还能够获得分数最高或最低的前N个元素、获取指定分数范围内的元素等与分数有关的操作。 在某些方面有序集合和列表类型有些相似。 1、二者都是有序的。 2、二者都可以获得某一范围的元素。 但是,二者有着很大区别: 1、列表类型是通过链表实现的,获取靠近两端的数据速度极快,而当元素增多后,访问中间数据的速度会变慢。 2、有序集合类型使用散列表实现,所有即使读取位于中间部分的数据也很快。 3、列表中不能简单的调整某个元素的位置,但是有序集合可以(通过更改分数实现) 4、有序集合要比列表类型更耗内存。 应用:商品销售排行榜 根据商品销售量对商品进行排行显示,定义sorted set集合,商品销售量为元素的分数。 定义商品销售排行榜key:items:sellsort 写入商品销售量: 商品编号1001的销量是9,商品编号1002的销量是10 ZADD items:sellsort 9 1001 10 1002 商品编号1001的销量加1 ZINCRBY items:sellsort 1 1001 商品销量前10名: ZRANGE items:sellsort 0 9 withscores redis的主要缺点在于数据库容量受到物理内存的限制不能用作海量数据的高性能读写(意思就是要考虑那些低内存的用户电脑),因此redis最合适的使用场景是那些较小数据量的高性能操作和运算上,例如: 缓存(数据查询、短连接、新闻内容、商品内容等等)(最多使用) 分布式集群架构中的session分离。 聊天室的在线好友列表。 任务队列(秒杀、抢购、12306等等) 应用排行榜。 网站访问统计。 数据过期处理(可以精确到毫秒) redis不仅仅只能用命令的形式操作,主流的后端语言都有对应的客户端支持,java官方推荐使用jedis和redisson。jedis托管在github上,你可以通过https://github.com/xetorthio/jedis访问和下载jedis客户端。 Redis的高性能是由于其将所有数据都存储在了内存中,为了使Redis在重启之后仍能保证数据不丢失,需要将数据从内存中同步到硬盘中,这一过程就是持久化。Redis支持两种方式的持久化,一种是RDB方式,一种是AOF方式。可以单独使用其中一种或将二者结合使用。 RDB方式的持久化是通过快照(snapshotting)完成的,当符合一定条件时Redis会自动将内存中的数据进行快照并持久化到硬盘。RDB是Redis默认采用的持久化方式。Redis启动后会读取RDB快照文件,将数据从硬盘载入到内存。根据数据量大小与结构和服务器性能不同,这个时间也不同。通常将记录一千万个字符串类型键、大小为1GB的快照文件载入到内存中需要花费20~30秒钟。 P:通过RDB方式实现持久化,一旦Redis异常退出,就会丢失最后一次快照以后更改的所有数据。这就需要开发者根据具体的应用场合,通过组合设置自动快照条件的方式来将可能发生的数据损失控制在能够接受的范围。如果数据很重要以至于无法承受任何损失,则可以考虑使用AOF方式进行持久化。 默认情况下Redis没有开启AOF(append only file)方式的持久化,可以通过appendonly参数开启:appendonly yes。开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof,可以通过appendfilename参数修改:appendfilename appendonly.aof 为了保证redis不会因为重启等原因而导致数据丢失,redis提供了持久化的解决方案,redis服务重新启动后会将硬盘中的数据恢复到内存中,但是此过程中如果redis服务器的硬盘损坏的话可能就会导致数据丢失。为了更好的保证数据的完整性,避免这种单点故障,可以使用redis提供了主从复制机制。主redis(master)中的数据有两个副本(replication)即从redis1(slave)和从redis2(slave),即使一台redis服务器宕机其它两台redis服务也可以继续提供服务 redis中常见的性能问题和解决方案 master写内存快照(snapshotting),sava命令调度rdbSava函数会阻塞主线程的工作,当快照比较大时对性能影响非常大,会间断性暂停服务,所以master最好不要写内存快照。 master AOF持久化,如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响master重启的恢复速度。master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化,如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。 master调用BGREWRITEAOF(redis中的一个命令,作用是异步执行一个AOF文件重写操作。重写会创建一个当前AOF文件的体积优化版本,即使BGREWRITEAOF执行失败,也不会有任何数据丢失,旧的AOF文件在BGREWRITEAOF操作成功之前不会被修改。从redis2.4开始,AOF重写由redis自行触发,BGREWRITEAOF仅用于手动触发重写操作。)重写AOP文件,在AOP重写的时候会大量的占用CPU和内存资源,导致服务load过高,出现短暂暂停现象。 redis主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网下。 MySQL中有2000W数据,redis中只存了20W数据,那么如何保证reids中的数据都是热点数据。 redis内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略(回收策略),redis提供了6种数据淘汰策略。 volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰。volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰。volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任何选择数据淘汰。allkeys-lru:从数据集(server.db(i).dict)中挑选最近最少使用的数据淘汰。allkeys-random:从数据集(server.db(i).dict)中任何选择数据淘汰。no-enviction(驱逐):禁止驱逐数据。 什么是spring框架,它有哪些模块,使用它有什么好处。 Spring框架是一个为Java应用程序的开发提供了综合、广泛的基础性支持的java平台,也是一个实现IOC和AOP的容器框架,设计理念就是简化开发。 spring框架集成了许多模块,常用的有spring ioc、spring aop,spring mvc、spring boot、spring security。 使用spring的好处 降低开发组件之间的耦合度,实现软件各层的解耦。 spring提供DI spring通过AOP技术完成了对事务的管理,使得开发人员不再需要手工控制事务,也不需要处理复杂的事务传播。同时提供的AOP技术可以很容易实现比如权限拦截、运行期监控、日志管理等功能 在Hibernate中管理事务 public void save(){ Session session = sessionFactory.getCurrentSession(); Session.beginTransaction(); Info info = new Info(“百度”); Info.setContent(“一家良心企业”); session.save(); session.getTransaction().commit(); } 在JDBC中管理事务 Connection conn = null; try{ conn.setAutoCommit(false); Statement stmt = conn.createStatement(); stmt.executeUpdate(update person where name=”老王”) conn.commit(); } catch(Exception e){ } final {conn.close()} 在spring中管理事务 @Transactional public void save(){ sessionFactory.getCurrentSession.svae(info); } 提供了很多辅助类,比如JdbcTemplate、HibernateTemplate,用于加快应用的开发 spring容器提供了单例支持(spring配置文件中定义的bean默认为单例模式),开发人员不需要编写代码来实现单例。 spring对于主流的框架,比如MyBatis、Hibernate提供了集成支持。 相对于其他集成框架,比如说EJB,spring IoC更加轻量级,使其在有限的内存和CPU资源下可以进行应用程序的开发和发布 请你谈一谈Spring的IOC和AOP? IOC是一种设计模式的实现,有两层实现。 对象的创建都不再由程序员控制,而是交由Spring控制,称之为控制反转。 对象的依赖关系由Spring的配置文件描述,并且这种关系只有在使用的时候才会建立,称之为依赖注入。 spring中的org.springfremework.beans包和org.springfreamework.context包构成了spring框架的IoC容器的基础,org.springframework.beans.factory.BeanFactory是spring IoC容器的具体实现,用于包装和管理各种bean,因此该接口是spring IoC的核心接口。ApplicationContext接口对BeanFactory进行了扩展,提供了message resource用于国际化的机制、事务传播和应用层的特别配置,比如针对WEB应用的WebApplicationContext。 AOP是一种编程思想,叫做面向切面,是OOP的延续和补充。P:这种在运行时,动态地将代码切入到类的指定方法,指定位置上的编程思想就是面向切面的编程。 在Spring中的具体实现是将系统中的非核心业务基于代理机制做提取,进行单独处理。比如事务,比如日志,比如安全授权等。 spring框架中大量使用了设计模式,请列举几个 代理模式 Spring AOP就是采用动态代理实现的,spring中有两种动态代理方式,jdk代理和cglib代理。 单例模式 Spring配置文件中的bean默认为单例 模版方法 Spring提供了许多模板类用于解决代码的重复问题,例如RestTemplate、JmsTemplate、JpaTemplate 前端控制器 Spring 提供给了DispatcherServlet来对请求进行分发 视图帮助 Spring提供了一系列的JSP标签,高效宏来辅助视图 工厂模式 使用BeanFactory创建对象实例

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

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

用户登录
用户注册