0%

系统趋于稳定,修改的bug通过git来管理,通过git的提交日志及提交文件变更识别潜在的风险。
工具SourceTree,当然eclipse也可以胜任(体验差一点)
这里写图片描述
要达到识别代码变更需具备基本的代码阅读能力,包括:java、js、jsp、html、css
未完待续

这篇文章的主要目的是记录使用python来编写公司发版系统的想法、过程。

一、构思

主要特征

该系统拥有web界面
代码发布功能:可以在web中实现各个webent单独发布、整体发布
git历史查看功能:查看git提交历史、查看每次提交修改文件。所以需要定时自动更新日志。
两次发布版本差异对比功能

实践能力

一个python的web框架
python调度操作git、maven、执行脚本
python写入和读取数据库
python/git版本对比

二、计划

1、寻找一款python的web框架。定为django。
2、学习并实践该框架
3、设计系统的MVC
4、各个功能编码实现和测试

三、执行

django的安装

安装的方法丰富多彩,很简单。参考github上django/django
或者参考官方网站的安装办法。

django的学习

主要参考官网的翻译资料:http://python.usyiyi.cn/translate/django_182/index.html
这里从文章发出到现在(2017/1/8),持续有40多天,期间断断续续的看,断断续续的边学边练习里面的例子。插播了很多其他事情,尤其是一些无聊的手机游戏,双休要跟老婆见面,时间蛮少了,坚持下来确实有点苦难,还好基础看完了。继续加油!下面硬着头皮继续下一个阶段的学习实践。

阅读全文 »

本人是测试人员,包括我在内,团队中有2名测试,整个研发不含测试大概10名(后台前端)。除了负责测试工作,还兼任负责发布公司测试环境及生产环境的代码。

代码环境及架构

公司所属行业为微信电商,使用java流行的MVC框架(这个我还不是很懂,不知道用了Spring哪些东西),这个了解过,只是并不深入,可以参考我的另一篇入门摘抄的java web基础。言归正传:
后台编程语言:java、UI框架kendo ui
web容器:tomcat
微信端:js、html、vue、css、jsp等
代码管理:git
构建工具:maven
部署操作系统:CentOS7
数据库:MySQL
其他数据库或工具:Redis、Memche、RPC、activemq、阿里云sso
桌面开发工具:eclipse

代码发布的主要流程

Created with Raphaël 2.1.0 研发编码/修改 发版到测试环境 测试确认 发版到生产环境 yes no

流程中两次发版目前都是执行我手动写的脚本。

Created with Raphaël 2.1.0 发版需求 git pull代码 maven编译构建 发布代码到tomcat 重启tomcat 开始测试

当然git由于有分支管理,发布的代码需要根据实际情况进行切换。这里git的分支管理就不再赘述。

由于手工发版实属重复劳动,后来约定定时测试环境每日定时发版2次,一次是中午12点10分,一次是晚上7点30.使用Linux的crontab。解放后的双手就是生产力。

1
2
3
crontab -e #编辑任务,进入文本编辑窗口,输入以下内容。两个脚本分别是拉取代/构建maven项目和发版代码用
10 12 * * 1-5 bash ~/mvn.sh && bash ~/updateSites.sh
30 19 * * 1-5 bash ~/mvn.sh && bash ~/updateSites.sh

PS:此处有坑一个,mvn.sh和updateSites.sh中存在git和mvn这两个非系统自带命令,使用crontab定时运行会缺乏环境变量,导致不能执行。解决办法:在脚本中添加:source /etc/profile。

生产环境的代码严格执行测试环境的代码为基准,所以发布的代码直接从tomcat的webent下面打包,唯一的不同是执行打包的时候会讲配置文件替换为生产。配置文件在代码库中做了统一的管理。
Created with Raphaël 2.1.0 发起发版任务 配置文件check 配置文件替换 代码打包 sftp方式拉取代码包 备份源代码发布代码包 发布代码包 上线回归测试

阅读全文 »

if you had used “apt-get update”….
the new update contains some bugs for nvidia drivers….
To slove:
1.get the nvidia software

1
sudo dpkg -l |grep nvidia
1
2
3
rc  nvidia-304                               304.132-0ubuntu0.16.04.2                      amd64        NVIDIA legacy binary driver - version 304.132
rc nvidia-opencl-icd-304 304.132-0ubuntu0.16.04.2 amd64 NVIDIA OpenCL ICD
rc nvidia-settings 361.42-0ubuntu1 amd64 Tool for configuring the NVIDIA graphics driver

2.remove nvidia all

1
sudo apt-get remove nvidia-opencl-icd-304 nvidia-304 nvidia-settings

3.restart your computer

1
init 6

前言

受《分布式自动化版本发布系统》启发,决定对我们公司现有的shell脚本发版进行升级。业余时间研究,主要达到以下目的:

前二个做完,可以了解python的一些基本语法数据类型,把更新记录写入到数据库便于日常查看。
第三个,续用当前测试环境发布代码的shell脚本。

python3.5 and mysql5.7

mysql5.7 install

参考:官网安装方法
首先:下载MySQL APT Repository。参考链接的方法,设置MySQL APT Repository。

1
2
3
sudo dpkg -i mysql-apt-config_0.8.0-1_all.deb
sudo apt-get update
sudo apt-get install mysql-server

第一个命令需要选择自己将要安装的mysql版本。
启动mysql
安装mysql workbench。

Connector/Python install

参考:官方网站

基本的python数据库操作

以下操作完成了对git log记录数据库。不足之处在于,没有找到pygit2的方法去追加git 日志,先跳过去了。
大部分代码参考:pygit2官网document

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import time,datetime
import config as config
from pygit2 import Repository
from pygit2 import GIT_SORT_NONE,GIT_SORT_TOPOLOGICAL, GIT_SORT_REVERSE,GIT_SORT_TIME
<!-- more -->
import mysql.connector

cnx = mysql.connector.connect(**config)
#存储日志的列表[[logA],[logB]...[logN]]
argslog=[]
cursor = cnx.cursor()
repo = Repository('homeOfyourGit/.git')
for commit in repo.walk(repo.head.target,GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE):
#logA..N
logtmp=[]
logtmp.append(commit.tree.id.hex)
logtmp.append(commit.message)
logtmp.append(datetime.datetime.utcfromtimestamp(commit.author.time))
logtmp.append(commit.author.name)
print(logtmp)
argslog.append(logtmp)

add_log="INSERT INTO gitlog.gitlog (nvalue,comments,date,Author) VALUES (%s,%s,%s,%s)"
cursor.executemany(add_log, argslog)
cursor.close()
cnx.commit()
cnx.close()

更新数据涉及到一个增量更新git的log日志问题,pygit2中没有找到类似的api。所以考虑有时间作为新旧数据判断依据。
这里面的难点(对于小白的我来说),是如何比较时间。开始直接比较:
/#下面 datetimelog 是tuple类型的
dataNewtime= datetimelog
/#commit.author.time是long型自然是无法比较的,发现这个是使用了commit.author.time - dataNewtime,编译器提示发现类型不正确。

正确代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import time,datetime

import config as config
from pygit2 import Repository
from pygit2 import GIT_SORT_NONE,GIT_SORT_TOPOLOGICAL, GIT_SORT_REVERSE,GIT_SORT_TIME
import mysql.connector

cnx = mysql.connector.connect(user='root',password='????',host='127.0.0.1',database='gitlog')
argslog=[]
cursor = cnx.cursor()
cursor.execute("SELECT UNIX_TIMESTAMP(date) FROM gitlog.gitlog order by gitlog.date desc limit 1")
for (datetimelog) in cursor:
#既然是数组,取数组第一个值
dataNewtime= datetimelog[0]

repo = Repository('/home/chaosbom/git/ArhasMK/.git')

for commit in repo.walk(repo.head.target,GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE):
#时间比数据库里的新则更新进去
if(commit.author.time > dataNewtime):
logtmp=[]
logtmp.append(commit.tree.id.hex)
logtmp.append(commit.message)
logtmp.append(datetime.datetime.utcfromtimestamp(commit.author.time))
logtmp.append(commit.author.name)
print(logtmp)
argslog.append(logtmp)
log=(commit.tree.id.hex,commit.message,datetime.datetime.utcfromtimestamp(commit.author.time).strftime("%Y-%m-%d %H:%M:%S"),commit.author.name)
add_log="INSERT INTO gitlog.gitlog (nvalue,comments,date,Author) VALUES (%s,%s,%s,%s)"
cursor.executemany(add_log, argslog)
cursor.close()
cnx.commit()
cnx.close()

测试后,新增记录被更新到数据库,当然记录很简单,解决的时候百度了很多次。

python调用shell

在百度中看了数篇关于python调用shell的入门文章。推荐使用Python subprocess模块。
Python subprocess模块学习总结

python调用shell命令易如反掌,现在需要考虑的是如何使python调用的脚本联动起来。

题外话

python在ubuntu下的编译器

本次实验使用了:

python连接mysql使用config

未解决,使用的时候类型不匹配错误。

计划和实际

写这篇文章的时候是希望使用python来自动处理目前公司的发版流程,基于实际考虑,我编写的shell脚本已经足够应付发版工作,再深究下去并不是非常必要,目前发版的不足回头总结再弥补也来得及。那么问题来了,用python去实现的必要性?首先明确一点,我使用python的目的是学习和研究。其次,目前对于web系统的了解程度还不够深入,没有真正的学习过一个web框架。所以还是要做一个发布的系统出来。这个内容留到下一篇文章再继续了。

特殊变量

变 量 含义 $0 当前脚本的文件名 $n 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是$1,第二个参数是$2。 $/# 传递给脚本或函数的参数个数。 $/* 传递给脚本或函数的所有参数。 $@ 传递给脚本或函数的所有参数。被双引号(” “)包含时,与 $/* 稍有不同,下面将会讲到。 $? 上个命令的退出状态,或函数的返回值。 $$ 当前Shell进程ID。对于 Shell 脚本,就是这些脚本所在的进程ID。

转义字符

转义字符 含义 \ 反斜杠 \a 警报,响铃 \b 退格(删除键) \f 换页(FF),将当前位置移到下页开头 \n 换行 \r 回车 \t 水平制表符(tab键) \v 垂直制表符

变量替换

变量替换可以根据变量的状态(是否为空、是否定义等)来改变它的值。
转义字符 含义 ${var} 变量本来的值 ${var:-word} 如果变量 var 为空或已被删除(unset),那么返回 word,但不改变 var 的值。 ${var:=word} 如果变量 var 为空或已被删除(unset),那么返回 word,并将 var 的值设置为 word。 ${var:?message} 如果变量 var 为空或已被删除(unset),那么将消息 message 送到标准错误输出,可以用来检测变量 var 是否可以被正常赋值。若此替换出现在Shell脚本中,那么脚本将停止运行。 ${var:+word} 如果变量 var 被定义,那么返回 word,但不改变 var 的值。

Shell 传递参数

在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推……
参数处理 说明 $/# 传递到脚本的参数个数 $/* 以一个单字符串显示所有向脚本传递的参数。如”$/*”用「”」括起来的情况、以”1、$2…n”的形式输出所有参数。跟0,表示执行脚本自己的名称。 $$ 脚本运行的当前进程ID号 $! 后台运行的最后一个进程的ID号 $@ 与∗相同,但是使用时加引号,并在引号中返回每个参数。如”@”用「”」括起来的情况、以”1”“2” … “$n” 的形式输出所有参数。 $- 显示Shell使用的当前选项,与set命令功能相同。 $? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

$/* 与 $@ 区别:
相同点:都是引用所有参数。
不同点:只有在双引号中体现出来。假设在脚本运行时写了三个参数 1、2、3,,则 ” /* ” 等价于 “1 2 3”(传递了一个参数),而 “@” 等价于 “1” “2” “3”(传递了三个参数)。

shell数组

数组中可以存放多个值。Bash Shell 只支持一维数组(不支持多维数组),初始化时不需要定义数组大小。与大部分编程语言类似,数组元素的下标由0开始。

shell运算符

文件测试运算符

文件测试运算符用于检测 Unix 文件的各种属性。

属性检测描述如下:
操作符 说明 举例 -d file 检测文件是否是目录,如果是,则返回 true。 [ -d $file ] 返回 false。 -f file 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 [ -f $file ] 返回 true。 -k file 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 [ -k $file ] 返回 false。 -p file 检测文件是否是有名管道,如果是,则返回 true。 [ -p $file ] 返回 false。 -u file 检测文件是否设置了 SUID 位,如果是,则返回 true。 [ -u $file ] 返回 false。 -g file 检测文件是否设置了 SGID 位,如果是,则返回 true。 [ -g $file ] 返回 false。 -r file 检测文件是否可读,如果是,则返回 true。 [ -r $file ] 返回 true。 -w file 检测文件是否可写,如果是,则返回 true。 [ -w $file ] 返回 true。 -x file 检测文件是否可执行,如果是,则返回 true。 [ -x $file ] 返回 true。 -s file 检测文件是否为空(文件大小是否大于0),不为空返回 true。 [ -s $file ] 返回 true。 -e file 检测文件(包括目录)是否存在,如果是,则返回 true。 [ -e $file ] 返回 true。

PS:SUID 是 Set User ID, SGID 是 Set Group ID的意思。设置了suid的程序文件,在用户执行该程序时,用户的权限是该程序文件属主的权限。例如程序文件的属主是root,那么执行该程序的用户就将暂时获得root账户的权限。sgid与suid类似,只是执行程序时获得的是文件属组的权限。

阅读全文 »

一开始懂一点SWING界面皮毛,打算使用SWING绘制出了计算器的界面,花了大概2个下班业余时间,一点一点开始摸索,知道SWING界面布局的基本原理。然后开始按照教材中讲解的过程去实现,结果发现界面根本不是那么做出来的。在网上找到了java的源代码,开始研究阅读和抄写编译。墨迹一周有如下收获。
1、JFrame是整个界面的底层,所有行为都给予它;
2、任何界面元素都需要个容器(Jpanel类)放置,每个容器需要定义一个基本的布局设置(Layout类)。布局中可以放置各种容器及元素。
3、元素过多,编程绘制界面效率比手绘要快。
4、任何类都功能单一为原则。编程中极容易按流程把功能给混淆导致设计的冗余。
5、Java的监听有所理解:为界面元素添加监听器,扩展java监听类并实现其方法,该方法将监听器捕获的动作事件作为传入参数,然后取得动作事件后执行方法内的业务处理逻辑。

参考:http://blog.csdn.net/frank_softworks/article/details/1629615
源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
package myPractise.Creasy2;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;

import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils.Text;

public class Calculator2 extends JFrame implements ActionListener
{

/** 计算器上的键的显示名字 */
private final String[] KEYS = { "7", "8", "9", "/", "sqrt", "4", "5", "6", "*", "%", "1", "2", "3", "-", "1/x", "0",
"+/-", ".", "+", "=" };
/** 计算器上的功能键的显示名字 */
private final String[] COMMAND = { "Backspace", "CE", "C" };
/** 计算器左边的M的显示名字 */
private final String[] M = { " ", "MC", "MR", "MS", "M+" };
/** 计算器上键的按钮 */
<!-- more -->
private JButton keys[] = new JButton[KEYS.length];
/** 计算器上的功能键的按钮 */
private JButton commands[] = new JButton[COMMAND.length];
/** 计算器左边的M的按钮 */
private JButton m[] = new JButton[M.length];
/** 计算结果文本框 */
private JTextField resultText = new JTextField("0");

// 标志用户按的是否是整个表达式的第一个数字,或者是运算符后的第一个数字
private boolean firstDigit = true;
// 计算的中间结果。
private double resultNum = 0.0;
// 当前运算的运算符
private String operator = "=";
// 操作是否合法
private boolean operateValidFlag = true;

/**
* 构造函数
*/
public Calculator2() {
super();
// 初始化计算器
init();
// 设置计算器的背景颜色
this.setBackground(Color.LIGHT_GRAY);
this.setTitle("计算器");
// 在屏幕(500, 300)坐标处显示计算器
this.setLocation(500, 300);
// 不许修改计算器的大小
this.setResizable(false);
// 使计算器中各组件大小合适
this.pack();
}

/**
* 初始化计算器
*/
public void init()
{
// 文本的字体和大小
resultText.setFont(new Font("宋体", Font.PLAIN, 15));
// 文本框中的内容采用右对齐方式
resultText.setHorizontalAlignment(JTextField.RIGHT);
// 不允许修改结果文本框
resultText.setEditable(false);
// 设置文本框背景颜色为白色
resultText.setBackground(Color.WHITE);
// 初始化计算器上键的按钮,将键放在一个画板内
JPanel calckeysPanel = new JPanel();
// 用网格布局器,4行,5列的网格,网格之间的水平方向间隔为3个象素,垂直方向间隔为3个象素
calckeysPanel.setLayout(new GridLayout(4, 5, 3, 3));
for (int i = 0; i < KEYS.length; i++)
{
keys[i] = new JButton(KEYS[i]);
calckeysPanel.add(keys[i]);
keys[i].setForeground(Color.blue);
}
// 运算符键用红色标示,其他键用蓝色表示
keys[3].setForeground(Color.red);
keys[8].setForeground(Color.red);
keys[13].setForeground(Color.red);
keys[18].setForeground(Color.red);
keys[19].setForeground(Color.red);

// 初始化功能键,都用红色标示。将功能键放在一个画板内
JPanel commandsPanel = new JPanel();
// 用网格布局器,1行,3列的网格,网格之间的水平方向间隔为3个象素,垂直方向间隔为3个象素
commandsPanel.setLayout(new GridLayout(1, 3, 3, 3));
for (int i = 0; i < COMMAND.length; i++)
{
commands[i] = new JButton(COMMAND[i]);
commandsPanel.add(commands[i]);
commands[i].setForeground(Color.red);
}

// 初始化M键,用红色标示,将M键放在一个画板内
JPanel calmsPanel = new JPanel();
// 用网格布局管理器,5行,1列的网格,网格之间的水平方向间隔为3个象素,垂直方向间隔为3个象素
calmsPanel.setLayout(new GridLayout(5, 1, 3, 3));
for (int i = 0; i < M.length; i++)
{
m[i] = new JButton(M[i]);
calmsPanel.add(m[i]);
m[i].setForeground(Color.red);
}

// 下面进行计算器的整体布局,将calckeys和command画板放在计算器的中部,
// 将文本框放在北部,将calms画板放在计算器的西部。

// 新建一个大的画板,将上面建立的command和calckeys画板放在该画板内
JPanel panel1 = new JPanel();
// 画板采用边界布局管理器,画板里组件之间的水平和垂直方向上间隔都为3象素
panel1.setLayout(new BorderLayout(3, 3));
panel1.add("North", commandsPanel);
panel1.add("Center", calckeysPanel);

// 建立一个画板放文本框
JPanel top = new JPanel();
top.setLayout(new BorderLayout());
top.add("Center", resultText);

// 整体布局,getcontentpane方法是JRame的静态方法
getContentPane().setLayout(new BorderLayout(3, 5));
getContentPane().add("North", top);
getContentPane().add("Center", panel1);
getContentPane().add("West", calmsPanel);
// 为各按钮添加事件侦听器
// 都使用同一个事件侦听器,即本对象。本类的声明中有implements ActionListener
for (int i = 0; i < KEYS.length; i++)
{
keys[i].addActionListener(this);
}
for (int i = 0; i < COMMAND.length; i++)
{
commands[i].addActionListener(this);
}
for (int i = 0; i < M.length; i++)
{
m[i].addActionListener(this);
}
}

/*
* 处理键盘事件 覆盖ActionListenser的方法,接受被监听对象的活动
*/
@Override
public void actionPerformed(ActionEvent e)
{
String label = e.getActionCommand();
if (label.equals(COMMAND[0]))
{
// 用户按了"Backspace"键方法
BackspaceWay(label);
}
else if (label.equals(COMMAND[1]))
{
// 用户按了"CE"键
resultText.setText("0");
}
else if (label.equals(COMMAND[2]))
{
// 用户按了"C"键
resultText.setText("0");
operator = "=";
operateValidFlag = true;
}
else if ("0123456789.".indexOf(label) >= 0)
{
// 用户按了数字键或者小数点键
NumWay(label);

}
else
{
// 用户按了运算符键
CalculatorWay(label);
}

}

public void BackspaceWay(String label)
{
String text = resultText.getText();
if (text.length() > 0)
{
text = text.substring(0, text.length() - 1);
if (text.length() == 0)
{
resultText.setText("0");
operator = "=";
operateValidFlag = true;
}
else
{
resultText.setText(text);
}
}
}

public void NumWay(String label)
{

if (firstDigit)
{
resultText.setText(label);
}else if (!label.equals("."))
{
resultText.setText(resultText.getText() + label);
}else if(label.equals(".")&&resultText.getText().indexOf(".")<0) {
resultText.setText(resultText.getText() + ".");
}
firstDigit = false;
}

public void CalculatorWay(String label)
{

if (operator.equals("/")) {
// 除法运算
// 如果当前结果文本框中的值等于0
if (getNumberFromText() == 0.0) {
// 操作不合法
operateValidFlag = false;
resultText.setText("除数不能为零");
} else {
resultNum /= getNumberFromText();
}
} else if (operator.equals("1/x")) {
// 倒数运算
if (resultNum == 0.0) {
// 操作不合法
operateValidFlag = false;
resultText.setText("零没有倒数");
} else {
resultNum = 1 / resultNum;
}
} else if (operator.equals("+")) {
// 加法运算
resultNum += getNumberFromText();
} else if (operator.equals("-")) {
// 减法运算
resultNum -= getNumberFromText();
} else if (operator.equals("*")) {
// 乘法运算
resultNum *= getNumberFromText();
} else if (operator.equals("sqrt")) {
// 平方根运算
resultNum = Math.sqrt(resultNum);
} else if (operator.equals("%")) {
// 百分号运算,除以100
resultNum = resultNum / 100;
} else if (operator.equals("+/-")) {
// 正数负数运算
resultNum = resultNum * (-1);
} else if (operator.equals("=")) {
// 赋值运算
resultNum = getNumberFromText();
}
if (operateValidFlag) {
// 双精度浮点数的运算
long t1;
double t2;
t1 = (long) resultNum;
t2 = resultNum - t1;
if (t2 == 0) {
resultText.setText(String.valueOf(t1));
} else {
resultText.setText(String.valueOf(resultNum));
}
}
// 运算符等于用户按的按钮
operator = label;
firstDigit = true;
operateValidFlag = true;
}

public double getNumberFromText(){
double result = 0;
try {
result = Double.valueOf(resultText.getText()).doubleValue();
} catch (NumberFormatException e) {
}
return result;
}
public static void main(String[] args)
{
Calculator2 calculator2=new Calculator2();
calculator2.setVisible(true);

}

}

目录

主要经验

游戏描述

参考的是《疯狂Java实战演义》——杨恩雄(文字版)中第一章内容。

Java源代码

GobangGame.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
package practise.fiveChess;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class GobangGame {
private int WIN_COUNT = 5;
private int posX, posY;
Chessboard chessboard = new Chessboard();

public int getPosX() {
return posX;
}

public void setPosX(int posX) {
this.posX = posX;
}

public int getPosY() {
return posY;
}

public void setPosY(int posY) {
this.posY = posY;
<!-- more -->
}

public static void main(String[] args) {
//游戏初始化
boolean isOver = false;
System.out.println("游戏开始!");
GobangGame gobangGame = new GobangGame();
Chessboard chessboard = new Chessboard();
chessboard.initBorad();
chessboard.printBoard();
//执行游戏交互
do {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String inputStr = null;
// 判断输入是否合法
while ((inputStr = br.readLine()) != null && gobangGame.isValid(inputStr) && isOver == false) {
int posX = gobangGame.getPosX();
int posY = gobangGame.getPosY();
String colorDark = Chessman.BLACK.getChessman();
String colorWhite = Chessman.WHITE.getChessman();
// 放棋子,如果失败则重放
if (!chessboard.setBoardByMan(posX, posY, colorDark)
|| !chessboard.setBoardByComputer(colorWhite)) {
continue;
}
// 打印棋盘
chessboard.printBoard();
// 判断是否有赢家
if (gobangGame.isWin(chessboard, posX, posY, colorDark)
|| gobangGame.isWin(chessboard, posX, posY, colorWhite)) {
if (gobangGame.isWin(chessboard, posX, posY, colorDark) == true) {
System.out.println("比赛结束!" + colorDark + "获得胜利");
System.out.println("是否继续游戏?y/n");
// 用户确认是否继续游戏,继续则初始化棋盘,否则退出程序
if (br.readLine().contentEquals("y")) {
chessboard.initBorad();
System.out.println("游戏开始!");
chessboard.printBoard();
continue;
} else {
isOver = true;
break;
}
}
if (gobangGame.isWin(chessboard, posX, posY, colorWhite) == true) {
System.out.println("比赛结束!" + colorWhite + "获得胜利");
System.out.println("是否继续游戏?y/n");
// 用户确认是否继续游戏,继续则初始化棋盘,否则退出程序
if (br.readLine().contentEquals("y")) {
chessboard.initBorad();
System.out.println("游戏开始!");
chessboard.printBoard();
continue;
} else {
isOver = true;
break;
}
}
}
}

} catch (IOException e) {
e.printStackTrace();
}
} while (isOver == false);
System.out.println("Game Over!");
}

// 输入合法性检测
public boolean isValid(String str) {
String[] posStrArr = str.split(",");
try {
posX = Integer.parseInt(posStrArr[0]);
posY = Integer.parseInt(posStrArr[1]);
if (posX > chessboard.BOARD_SIZE || posY > chessboard.BOARD_SIZE || posX < 0 || posY < 0) {
System.out.println("输入不合法,请输入合法的坐标范围:0--" + (chessboard.BOARD_SIZE - 1));
return false;
}
} catch (NumberFormatException e) {
//chessboard.printBoard();
System.out.println("输入不合法,请重新按\"x,y\"的形式输入");
return false;
}
return true;
}
//是否继续游戏方法
public boolean isReplay(String enterStr) {
if (enterStr == "y" || enterStr == "Y") {
return true;
} else {
return false;
}
}

//以下方法基于输入棋子坐标,按不同方向(基于棋子坐标),东西|南北|东北-西南|西北-东南。
//东西
public boolean conetX(Chessboard chessboard, int posX, int posY, String chessColor) {
String[][] board;
board = chessboard.getBord();
int count = 1;
try {
int tmpY = posY - 1;
// 输入点不是起点所在列则判断
// 输入点左侧
while (posY >= tmpY && tmpY > 0) {
if (board[posX][tmpY] != chessColor) {
break;
} else {
count++;
tmpY--;
}
}
// 输入点右侧
tmpY = posY + 1;
while (posY <= tmpY && tmpY < chessboard.BOARD_SIZE) {
if (board[posX][tmpY] != chessColor) {
break;
} else {
count++;
tmpY++;
}
}
if (count >= WIN_COUNT) {
return true;
} else {
return false;
}
} catch (Exception e) {
System.out.println("异常错误:" + e.getMessage());
return false;
}
}

/**
* 南北统计
*/
public boolean conetY(Chessboard chessboard, int posX, int posY, String chessColor) {
String[][] board;
board = chessboard.getBord();
int count = 1;
try {
int tmpX = posX - 1;
// 输入点上方,如果输入的是原点则不计
while (tmpX >= 0) {
if (board[tmpX][posY] != chessColor) {
break;
} else {
count++;
tmpX--;
}
}
// 输入点下方
tmpX = posX + 1;
while (posX < tmpX && tmpX < chessboard.BOARD_SIZE) {
if (board[tmpX][posY] != chessColor) {
break;
} else {
count++;
tmpX++;
}
}
// 累加后是否符合要求
if (count >= WIN_COUNT) {
return true;
} else {
return false;
}
} catch (Exception e) {
System.out.println("异常错误:" + e.getMessage());
return false;
}

}

/**
* 东北\西南斜线方向
*/
public boolean conetEN(Chessboard chessboard, int posX, int posY, String chessColor) {
String[][] board;
board = chessboard.getBord();
int count = 1;

try {
int tmpX1 = posX - 1;
int tmpY1 = posY - 1;

// 西北线
while (tmpX1 < posX && tmpX1 >= 0 && tmpY1 < posY && tmpY1 >= 0) {
if (board[tmpX1][tmpY1] != chessColor) {
break;
} else {
count++;
tmpY1--;
tmpX1--;
}
}
// 东南线
int tmpX2 = posX + 1;
int tmpY2 = posY + 1;
while (tmpX2 > posX && tmpX2 < chessboard.BOARD_SIZE && tmpY2 > posY && tmpY2 < chessboard.BOARD_SIZE) {
if (board[tmpX2][tmpY2] != chessColor) {
break;
} else {
count++;
tmpY2++;
tmpX2++;
}
}
if (count >= WIN_COUNT) {
return true;
} else {
return false;
}

} catch (Exception e) {
System.out.println("异常错误:" + e.getMessage());
return false;
}
}

/**
* 东北西南斜线方向
*/
public boolean conetES(Chessboard chessboard, int posX, int posY, String chessColor) {
String[][] board;
board = chessboard.getBord();
int count = 1;
count = 1;
try {
int tmpX1 = posX - 1;
int tmpY1 = posY + 1;
// 东北线
while (tmpX1 < posX && tmpX1 >= 0 && tmpY1 >= posY && tmpY1 < chessboard.BOARD_SIZE) {
if (board[tmpX1][tmpY1] != chessColor) {
break;
} else {
count++;
tmpY1++;
tmpX1--;
}
}
// 西南线
int tmpX2 = posX + 1;
int tmpY2 = posY - 1;
while (tmpX2 < chessboard.BOARD_SIZE && tmpX2 > posX && tmpY2 < posY && tmpY2 >= 0) {
if (board[tmpX2][tmpY2] != chessColor) {
break;
} else {
count++;
tmpY2--;
tmpX2++;
}
}
if (count >= WIN_COUNT) {
return true;
} else {
return false;
}

} catch (Exception e) {
System.out.println("异常错误:" + e.getMessage());
return false;
}
}

// 判断所输入打棋子是否能够赢得比赛
public boolean isWin(Chessboard chessboard, int posX, int posY, String chessColor) {
boolean isWinx = this.conetX(chessboard, posX, posY, chessColor);
boolean isWiny = this.conetY(chessboard, posX, posY, chessColor);
boolean isWinEN = this.conetEN(chessboard, posX, posY, chessColor);
boolean isWinES = this.conetES(chessboard, posX, posY, chessColor);
if (isWinx || isWiny || isWinEN || isWinES) {
return true;
} else {
return false;
}
}
}

Chessman.java

1
2
3
4
5
6
7
8
9
10
public enum Chessman {
BLACK("●"), WHITE("○");
private String chessman;
private Chessman(String chessman) {
this.chessman = chessman;
}
public String getChessman() {
return this.chessman;
}
}

Chessboard.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package practise.fiveChess;

public class Chessboard {

static int BOARD_SIZE=22;
String[][] board=new String[BOARD_SIZE][BOARD_SIZE];
//初始化棋盘
public void initBorad(){
for (Integer i = 0; i < BOARD_SIZE; i++) {
for (Integer j = 0; j < BOARD_SIZE; j++) {
board[i][j]="╂";
}
}
}
//打印棋盘
public void printBoard(){
for (int i = 0; i < BOARD_SIZE; i++) {
//System.out.print(i);
for (int j = 0; j < BOARD_SIZE; j++) {
System.out.print(board[i][j]);
}
System.out.println();
}
}

//人执黑棋下棋落子
public boolean setBoardByMan(int posX,int posY,String color){
if(board[posX][posY]!="╂"){
System.out.println("输入位置已有棋子,请重新输入");
return false;
}else{
board[posX][posY]="●";
return true;
}
}
//电脑执白棋
public boolean setBoardByComputer(String color){
int posX,posY;
posX=(int)((Math.random())*BOARD_SIZE);
posY=(int)((Math.random())*BOARD_SIZE);
if(board[posX][posY]!="╂"){
setBoardByComputer(color);
return false;
}else{
board[posX][posY]="○";
return true;
}

}
//返回棋盘,二维数组
public String[][] getBord(){
return board;
}

}

参考来源:http://www.xuebuyuan.com/2153333.htm

1.Action/Service/DAO简介:

Action是管理业务(Service)调度和管理跳转的。

Service是管理具体的功能的。

Action只负责管理,而Service负责实施。

DAO只完成增删改查,虽然可以1-n,n-n,1-1关联,模糊、动态、子查询都可以。但是无论多么复杂的查询,dao只是封装增删改查。
至于增删查改如何去实现一个功能,dao是不管的。

总结这三者,通过例子来解释:

Action像是服务员,顾客点什么菜,菜上给几号桌,都是ta的职责;

Service是厨师,action送来的菜单上的菜全是ta做的;

Dao是厨房的小工,和原材料打交道的事情全是ta管。

相互关系是,小工(dao)的工作是要满足厨师(service)的要求,厨师要满足服务员(action)转达的客户(页面用户)的要求,服务员自然就是为客户服务喽。

现在最基本的分层方式,结合了SSH架构。Model层就是对应的数据库表的实体类。Dao层是使用了hibernate连接数据库、操作数据库(增删改查)。
Service层:引用对应的Dao数据库操作。Action层:引用对应的Service层,在这里结合Struts的配置文件,跳转到指定的页面,当然也能接受页面传递的请求数据,也可以做些计算处理。

以上的Hibernate,Struts,都需要注入到spring的配置文件中,Spring把这些联系起来,成为一个整体。

2.三大框架Struts/Hibernate/Spring

简单地说:

Struts——控制用的;

Hibernate——操作数据库的;

Spring——解耦用的。

阅读全文 »

java类的成员变量有俩种:一种是被static关键字修饰的变量,叫类变量或者静态变量;另一种没有static修饰,为实例变量。
在语法定义上的区别:静态变量前要加static关键字,而实例变量前则不加。
在程序运行时的区别:实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。总之,实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。
例如,对于下面的程序,无论创建多少个实例对象,永远都只分配了一个staticVar变量,并且每创建一个实例对象,这个staticVar 就会加1;但是,每创建一个实例对象,就会分配一个instanceVar,即可能分配多个instanceVar,并且每个instanceVar的值都只自加了1次。
类的静态变量在内存中只有一个,java虚拟机在加载类的过程中为静态变量分配内存,静态变量位于方法区,被类的所有实例共享。静态变量可以直接通过类名进行访问,其生命周期取决于类的生命周期。


而实例变量取决于类的实例。每创建一个实例,java虚拟机就会为实例变量分配一次内存,实例变量位于堆区中,其生命周期取决于实例的生命周期。

结果为:
1
0 (成员变量具有缺省值 而局部变量则没有)
把代码改为:

结果则为
0
1
0

引用网址:http://www.233.com/Java/zhuanye/20110716/17284517.html