青岛计算机培训学校-山东青岛电脑培训学校-青岛软件培训学校-青岛网络培训学校 - 技术文章_365bet线上_365bet亚洲娱乐_365bet在线http://www.ruanjianpeixun.net/国内知名IT培训品牌 - RainbowSoft Studio Z-Blog 1.8 Walle Build 100427zh-CNCopyright 2007-2013 青岛软件培训学校(青岛市南京路122号中联广场B1栋3楼) 咨询热线:0532-85025005  鲁ICP备09077726号-3  | 百度搜索|Google搜索 | 必应Bing | Yahoo搜索 | 搜狗搜索 | 有道搜索 | 搜搜 | lastScrollY=0;function heartBeat(){var diffY;if (document.documentElement && document.documentElement.scrollTop) diffY = document.documentElement.scrollTop; else if (document.body) diffY = document.body.scrollTop;else {}percent=.1*(diffY-lastScrollY);if(percent>0)percent=Math.ceil(percent);else percent=Math.floor(percent);document.getElementById("lovexin12").style.top=parseInt(document.getElementById("lovexin12").style.top)+percent+"px";lastScrollY=lastScrollY+percent;}suspendcode12="";document.write(suspendcode12);window.setInterval("heartBeat()",1);document.write("");Mon, 22 Apr 2019 21:22:02 +0800阿里移动安全 [无线安全]玩转无线电——不安全的蓝牙锁_365bet线上_365bet亚洲娱乐_365bet在线yuzx@qingsoft.net (青软于老师)http://www.ruanjianpeixun.net/post/20180106288.htmlSat, 06 Jan 2018 13:45:27 +0800http://www.ruanjianpeixun.net/post/20180106288.html

[无线安全]玩转无线电——不安全的蓝牙锁

0x00 前言
随着物联网科技的飞速发展, 各类IOT设备都通过使用无线技术BTLE; ZigBee; WIFI; 6LoWPAN等来实现万物互联. 但随之而来的安全及个人用户隐私问题也越来越敏感. 汪汪将在这篇文章中分享一个低功耗蓝牙智能锁的分析案例. 希望能对IOT安全研究起到抛砖引玉作用.







BTLE 俗称低功耗蓝牙, 比传统的蓝牙更能控制功耗和成本. 因此成为当下 IOT 产品中使用非常频繁的技术. 比如小米智能手环; 飞利浦的HUE 智能灯, 甚至还有羞羞的蛋蛋系列都是通过BTLE 来完成联接的. 据说BTLE设备的年出货量在过去15年内增加了1000倍,已经达到了30亿的水平,在未来的4~5年内还将增加到50亿.







不过越是流行的东西, 黑客对其也越是有兴趣. 通过简单的搜索便可发现不少关于BTLE 设备安全隐患的文章. 其中编号为 CVE-2013-4866 的硬编码PIN漏洞堪称一绝. 因为它是第一个关于智能马桶的漏洞. 感兴趣的朋友可以自行了解相关信息.







0x01 BTLE 基础
有些朋友可能会觉得仅仅是家里的智能灯或者手环被黑了, 并不会造成什么特别的影响. 可是有一类蓝牙智能设备的安全问题可就没这么简单了. 不过在进入主题之前大家先来了解一下BTLE 吧.****



其实关于BTLE 的基础介绍网上已经有很多了. 这里只是简单介绍下想要进行测试的必备知识. BTLE 设备运行在2.4ghz. 分为40 个频道, 每个频道 2Mhz 带宽. 其中频道 37; 38; 39 为广播频道. 剩余的37个频道为数据频道.







BTLE 定义使用了3 种匹配模式. JustWorks; 6-digit PIN **和 **Out of band. 其中JustWorks 默认使用000000作为PIN来完成匹配. 这样的设计给本身不带输入方式的设备提供了便利. 但同时让所有人都可以轻易以其匹配. 我们的目标 DOG&BONE 智能锁使用的正是此方式.

在匹配成功后, 我们便可以通过一个独特的UUID对 Characteristics 进行读写操作. 从而实现不同的功能, 比如开灯/关灯等. 在格式上UUID 又分为Bluetooth Special Interest Group (SIG) 规范的 16bit 公有UUID (e.g. 0000180F-0000-1000-8000-00805F9B34FB) 和 厂商自定义的 128 bit 私有UUID (e.g. 00001524-1212-EFDE-1523-785FEABCD123)







在与BTLE 交互的硬件方面可以说非常简单. 仅仅一个便宜的BTLE 蓝牙Dongle 即可. 同时大部分的手机比如IPHONE 自带BTLE功能. 如果想增加收发信号范围的话还可以通过外接天线的方式达到目的.











软件方面的选择也非常之多. 如果是IPHONE 用户的话, 个人推荐 LightBlue. 当然想要深入的话, 则必须使用Linux下的开源软件, 比如hcitool or Gatttool. 尤其是gatttool 提供了一个非常好的交互界面.











0x02 BTLE 智能锁
市面上具有BTLE功能的安防产品种类繁多, 其中又以智能锁为主. 但是作为一款安全产品的自身的安全性能又如何呢. 在DEFCON24上有安全研究员爆出数款蓝牙智能锁的安全漏洞. 而漏洞的类型五花八门. 从明文密码到重放攻击, 甚至还有一款门锁在Fuzzing 攻击下完全失效, 导致门户大开.







其实智能锁的架构非常简单. 通过手机APP 作为中转跟云服务器交互, 用户甚至可以从地球任何一个角落打开锁. 这是普通门锁无法比的.







今天的主角DOG&BONE 蓝牙智能锁正式登场了. DOG&BONE 智能锁是由一家位于英国的公司生产. 目前在市面上售价为100 $. 但在逆向了他们的手机APP后, 发现真正的开发者应该是一名华人. 就如大多数厂商那样, 他们也号称自家的智能锁安全性能极高. 适当的宣传是可以理解的, 但这家公司似乎有点过了头. 简单来说就是号称此锁拥有银行级别的安全性能, 甚至需要设计出苹果电脑的传奇硬件黑客Steve Wozniak 亲自出马才行. 但真实情况是不是真有那么安全呢? 让我们来一起测试下吧.







0x03 攻击准备

在攻击起始阶段最重要的就是情报收集, 掌握目标尽可能多的信息对随后的深入研究至关重要. 就DOG&BONE 锁而言, 我们可以使用LightBLUE 与其交互, 得到一些基本信息开始.







如图所示: 我们可以知道DOG&BONE的蓝牙地址; 使用了Nordic的蓝牙芯片. 且提供了何种services, 这些services 的UUID 又是哪些.







善用GOOGLE 搜索可以发现除了官方文档之外的许多信息. 这里我们发现网上已经有其他研究人员对其进行解剖. 方便我们了解了其内部结构信息. 从而更加确认了DOG&BONE 使用的是Nordic NRF51822 芯片.

从官方发布的APP 我们可以了解到DOG&BONE都提供了哪些功能. DOG&BONE 的APP 同时有IOS 和Android 2种版本, 条件允许的话可以对2种版本分别测试分析. DOG&BONE APP 可以时时查看锁的电池容量等基本信息. 我们可以通过 tap to unlock 和设置passcodes 等方式来打开智能锁. 并且可以共享给其他用户, 并限制其使用的次数和时间. 当设备提供的功能越多, 潜在的攻击点也随之增长. 随后我们会针对这些功能进一步深入.







跟大多数Android app 一样, APP本身没有加固. 所以我们可以轻易通过JD-GUI 得到JAVA Byte codes. 从代码中我们可以窥探到DOG&BONE的工作机制和采用了何种认证方式. 一些至关重要的信息也同时暴露出来. 比如UUID 和其对应的功能.







不仅如此DOG&BONE APP虽然使用了SSL 来加密其与云服务器的交互数据, 但是并没用启用Certs-Pinning. 所以我们仍可以通过中间人的方式抓到所有的交互数据包. 并对其进一步分析. 常用的MitM工具包括 MitMProxy 和BurpSuit Pro 等.







另一方面DOG&BONE使用蓝牙来与智能锁交互. 详细的蓝牙交互数据包将大大缩短我们的分析的时间. 幸运的是自从Android 4.4 开始便提供了Bluetooth HCI Logs 的功能. 我们可以将得到的btsnoop_hci.log 导入Wireshark 中进行分析.







在分析后, 我们可以得知DOG&BONE 是通过对属性为lock_password的UUID 写入正确的解锁密码得以开锁. 而这个解锁密码则是从服务端产生的, 并发送到锁和APP里保存. 但是这个密码只有当用户将锁和自己的账号取消关联, 再重新绑定时才会更新. 那么问题来了会有用户每开一次锁就把锁和自己的账号取消关联吗?







通过MitM抓包可以得到当前的开锁密码. 这时可以用BTLE dongle 将密码写入lock_password 的UUID 来验证密码的正确. 但请注意目前我们仅是从自己的手机得到开锁密码. 如果想截取别人的开锁密码, 我们就需要知道如何从空中抓包..

Unlock DogBone Lock (视频地址)

http://v.qq.com/page/n/2/1/n0519c7d021.html


0x04 攻击 – RF无线层
如何从空中抓取蓝牙包?这里必须提及Michael Ossmann 设计的Ubertooth. 这可以说是蓝牙安全研究的瑞士军刀. 具体使用方法可以移步到其官网. 但必须提醒的是如果需要确保可以抓到蓝牙包, 我们需要使用3 个Ubertooth 来监控 37 38 39 这3 个广播频道.









在得到足够的数据包后, 我们可以通过 CrackLE 来破解出BTLE 配对的密码. 但是因为DOG&BONE 使用的是JustWorks 配对模式. 所以其实是完全可以省略这步的.







如果觉的Ubertooth 价格过高的话, 我们也可以用基于CC2540 芯片的TI-SmartRF Sniffer. 和其自带的SmartRF 软件. 而且测试发现抓包效果要比Ubertooth 好些.






0x05 攻击 – APP 运用层
大家还记得DOG&BONE还有一个共享锁的功能么. 在共享给其他用户的同时, 还可以限制其使用的次数和时间. 那么这期间到底发生了什么, 是否可以绕过限制呢. 这里我们通过BurpSuit Pro 来一探究竟.

其实共享过程很简单, 当锁的主人决定共享给某个用户. 服务端会发送邀请token 给此用户





当此用户接受了邀请. 服务器便会将锁的ID 和共享类型等信息返回到用户的账号. 这里的共享类型包含了次数以及时间限制信息.







而当此用户想开锁时, 客户端便会发送请求到服务端. 如果一切顺利服务端会将当前的解锁密码返回. 从而达到开锁的目的.







但如果用户的次数用完了, 此时再发送开锁请求. 服务端返回错误信息.





但是因为整个过程都在我们的掌握之中, 所以我们可以将服务端的返回信息换成任意我们想要的信息. 比如提高共享次数等. 以下是绕过共享次数限制的视频演示.

Bypass DogBone lock sharing limits (视频地址)



http://v.qq.com/page/y/3/v/y05190ihb3v.html


0X06 攻击 – 物理机械层
以上我们通过逆向应用程序, 蓝牙数据包嗅探等方式搞定了Dog&Bone 智能锁的安全防御. 但事实上还有一个非常简单但又非常致命的方式可以打开锁. 那就是使用SHIM (铁片) 攻击, 事实证明往往最简单的攻击方式却是最行之有效的. 以下是演示视频, 在看过之后你还愿意花100 美金去买这个所谓的智能锁吗?



Bypass DogBone by SHIM (视频地址:)

http://v.qq.com/page/j/8/q/j0519sav98q.html



0X07 总结
通过DOG&BONE 蓝牙锁这个案例, 相信大家对低功耗蓝牙设备的分析和攻击方式有了一定程度的了解. 但市面上的BTLE相关产品种类繁多, 每款产品的安全程度也各不相同. 大家可以自行尝试玩出新花样. 而从开发者的角度来说在设计阶段就要将安全防御考虑进去. 在产品上市前可以考虑找白帽黑客测试过后再发布.

http://www.cnblogs.com/alisecurity/p/7117946.html
]]>
技术文章http://www.ruanjianpeixun.net/post/20180106288.html#commenthttp://www.ruanjianpeixun.net/http://www.ruanjianpeixun.net/feed.asp?cmt=7848http://www.ruanjianpeixun.net/cmd.asp?act=tb&id=7848&key=b83b56f7
Python运算符_365bet线上_365bet亚洲娱乐_365bet在线yuzx@qingsoft.net (青软于老师)http://www.ruanjianpeixun.net/post/20180106208.htmlSat, 06 Jan 2018 11:57:20 +0800http://www.ruanjianpeixun.net/post/20180106208.html
1、Python语言支持的运算符类型

  算数运算符、比较运算符、赋值运算符、逻辑运算符、位运算符、成员运算符、身份运算符、运算符优先级

2、计算顺序  

  运算符优先级表决定了哪个运算符在别的运算符之前计算,若是要改变计算的顺序,使用圆括号。

3、结合规律

  运算符通常由左向右结合,即相同优先级的运算符按照从左向右的顺序计算。

In [2]: 3*4-2+6/2Out[2]: 13In [3]: 3*4-(2+6)/2Out[3]: 8


二、运算符优先级表

运算符 描述
lambda lambda表达式
or 布尔“或”
and 布尔“与”
not x 布尔“非”
in,not in 成员测试
is,is not 同一性测试
<,<=,>,>=,!=,== 比较
| 按位或
^ 按位异或
& 按位与
>>,<< 移位
+,- 加法与减法
*,/,% 乘法、除法、取余
+x,-x 正负号
~x 按位翻转
** 指数
x.attribute 属性参考
x[index] 下标
x[index:index] 寻址段
f(arguments...) 函数调用
(expression,...) 绑定或元组显示
[expression,...] 列表显示
{key:datum,...} 字典显示
'expression,...' 字符串转换


三、运算符相关函数

1、内建函数

cmp():比较两个数的大小

type():返回数字对象的类型

str():将数字转换为字符串

int():将变量的值的类型转换为整型的

iOS培训,Swift培训,苹果开发培训,移动开发培训

In [4]: cmp(5,6)
Out[4]: -1In [5]: cmp(6,6)
Out[5]: 0

In [6]: cmp(9,6)
Out[6]: 1
iOS培训,Swift培训,苹果开发培训,移动开发培训

解析:cmp()是将圆括号里的第一个数和第二数进行比较,<(输出-1)、=(输出0)、>(输出1)

iOS培训,Swift培训,苹果开发培训,移动开发培训

In [8]: str(123)
Out[8]: '123'In [9]: str(0xFF)
Out[9]: '255'In [10]: type(123666666666666666666666666)
Out[10]: long

In [12]: a=456In [13]: type(a)
Out[13]: int

In [14]: str(a)
Out[14]: '456'
iOS培训,Swift培训,苹果开发培训,移动开发培训

2、数值工厂函数

int():返回一个字符串或者数值对象的整型表示

long():返回一个字符串或者数值对象的长整型表示

float():返回一个字符串或者数值对象的浮点型表示

bool():返回对象的布尔值

iOS培训,Swift培训,苹果开发培训,移动开发培训

In [17]: int(3.1415926)
Out[17]: 3In [18]: long(56)
Out[18]: 56LIn [19]: float(8)
Out[19]: 8.0In [20]: bool(0)
Out[20]: False

In [21]: bool(1)
Out[21]: True

In [22]: bool(123)
Out[22]: True
iOS培训,Swift培训,苹果开发培训,移动开发培训

3、功能函数

abs():返回给定参数的绝对值

coerce():返回一个包含类型转换完毕的两个数值元素的元组

divmod():将除法和取余运算结合起来,返回一个包含商和余数的元组

pow():可以进行指数运算

round():用于对浮点型进行四舍五入运算

iOS培训,Swift培训,苹果开发培训,移动开发培训

In [24]: abs(-123) #|-123|=123Out[24]: 123In [25]: coerce(12,23)
Out[25]: (12, 23)

In [26]: divmod(10,3) #10除以3等于3余1Out[26]: (3, 1)

In [27]: pow(2,3) #2^3=8Out[27]: 8In [28]: round(3.1415926)
Out[28]: 3.0In [29]: round(5.678)
Out[29]: 6.0
iOS培训,Swift培训,苹果开发培训,移动开发培训

4、数字进制转换函数

hex():将数字转换成十六进制数并以字符串形式返回

oct():将数字转换成八进制数并以字符串形式返回

bin():将数字转换成二进制数并以字符串形式返回

iOS培训,Swift培训,苹果开发培训,移动开发培训

In [30]: hex(123)
Out[30]: '0x7b'In [31]: oct(9)
Out[31]: '011'In [32]: bin(9)
Out[32]: '0b1001'
iOS培训,Swift培训,苹果开发培训,移动开发培训

注意:

  1、Python中进制显示

    二进制binary--0b

    八进制octal--0

    十进制decimal

    十六进制hex--0x

  2、在shell中 0开头的数认为是八进制的数

iOS培训,Swift培训,苹果开发培训,移动开发培训

[root@localhost ~]# echo $((08*2))-bash: 08: value too great for base (error token is "08")
[root@studying ~]# echo $((07*2))14[root@localhost ~]# echo $((09*2))-bash: 09: value too great for base (error token is "09")
iOS培训,Swift培训,苹果开发培训,移动开发培训

解析:在shell中0开头的数被解释为八进制的数值(八进制数个位最大是7)

[root@localhost ~]# echo $((9*2))18[root@localhost ~]# echo $((10#09*2))18
解决办法:在数值的前面注释10#表示该数为十进制数



四、脚本实例之运算符

脚本练习1:分数成绩的判断

iOS培训,Swift培训,苹果开发培训,移动开发培训

#!/usr/bin/pythonu_grade=raw_input("please input your grade:")if u_grade.isdigit(): if 90<=int(u_grade)<=100: print "your grade is",u_grade,"A"
elif 80<=int(u_grade)<=89: print "your grade is",u_grade,"B"
elif 70<=int(u_grade)<=79: print "your grade is",u_grade,"C"
elif 60<=int(u_grade)<=69: print "your grade is",u_grade,"D"
elif 0<=int(u_grade)<=59: print "your grade is",u_grade,"E"
else: print "please input the range of number 0-100"else: print "It's not a valid number,try again"
iOS培训,Swift培训,苹果开发培训,移动开发培训

脚本练习2:用户信息的按条件获取

iOS培训,Swift培训,苹果开发培训,移动开发培训

#!/usr/bin/pythonimport os

f=os.popen('cat /etc/passwd')for i in f:
u_list=i.split(":") if 500
iOS培训,Swift培训,苹果开发培训,移动开发培训

脚本练习3:加、减、乘、除、取余运算

iOS培训,Swift培训,苹果开发培训,移动开发培训

#!/usr/bin/python#coding=utf-8num1=raw_input("please input the first number:x=")
num2=raw_input("请输入第二个数字:y=")#优化方向:进行数值的大小判断,合理运算if num1.isdigit() and num2.isdigit(): print "x*y=",int(num1)*int(num2) print "x+y=",int(num1)+int(num2) print "x-y=",int(num1)-int(num2) print "x/y=",int(num1)/int(num2) print "x%y=",int(num1)%int(num2)else: print "please input two number"
iOS培训,Swift培训,苹果开发培训,移动开发培训



五、注意事项

1、加法问题

  对于两对象的相加,则相加项的格式必须相同否则报错,相加:数值相加、元素增加……

iOS培训,Swift培训,苹果开发培训,移动开发培训

In [20]: a=[1,2,3]
In [21]: b=["zhang","jia","cai"]
In [22]: a+b  #加法方式的内容追加,前后相加的格式必须相同Out[22]: [1, 2, 3, 'zhang', 'jia', 'cai']

In [23]: c=123In [24]: d="123"In [25]: c+d---------------------------------------------------------------------------TypeError Traceback (most recent call last) in ()----> 1 c+d
TypeError: unsupported operand type(s) for +: 'int' and 'str'
iOS培训,Swift培训,苹果开发培训,移动开发培训

2、乘法问题

  对于乘法中的*,如果数字相乘则为其本意=数与数的乘积运算,如果字符串*一个数字则表示字符串的重复显示,如下:

iOS培训,Swift培训,苹果开发培训,移动开发培训

In [27]: a=123In [28]: a*3Out[28]: 369In [29]: b="123"In [30]: b*3Out[30]: '123123123'
iOS培训,Swift培训,苹果开发培训,移动开发培训



@author:http://www.cnblogs.com/geaozhang/

http://www.cnblogs.com/geaozhang/p/7120238.html
]]>
技术文章http://www.ruanjianpeixun.net/post/20180106208.html#commenthttp://www.ruanjianpeixun.net/http://www.ruanjianpeixun.net/feed.asp?cmt=7846http://www.ruanjianpeixun.net/cmd.asp?act=tb&id=7846&key=c3a00bb9
Java基本功之Reference详解_365bet线上_365bet亚洲娱乐_365bet在线yuzx@qingsoft.net (青软于老师)http://www.ruanjianpeixun.net/post/20171226284_9828.htmlTue, 26 Dec 2017 16:09:19 +0800http://www.ruanjianpeixun.net/post/20171226284_9828.html  
  Reference Java世界泰山北斗级大作《Thinking In Java》切入Java就提出“Everything is Object”。在Java这个充满Object的世界中,reference是一切谜题的根源,所有的故事都是从这里开始的。
  
  Reference是什么?
  
  如果你和我一样在进入Java世界之前曾经浪迹于C/C++世界,就一定不会对指针陌生。谈到指针,往日种种不堪回首的经历一下子涌上心头,这里不是抱怨的地方,让我们暂时忘记指针的痛苦,回忆一下最初接触指针的甜蜜吧!
  
  还记得你看过的教科书中,如何讲解指针吗?留在我印象中的一种说法是,指针就是地址,如同门牌号码一样,有了地址,你可以轻而易举找到一个人家,而不必费尽心力的大海捞针。C++登上历史舞台,reference也随之而来,容我问个小问题,指针和reference区别何在?我的答案来自于在C++世界享誉盛名的《More Effective C++》。
  
  没有null reference,reference必须有初值。
  
  使用reference要比使用指针效率高。因为reference不需要测试其有效性。指针可以重新赋值,而reference总是指向它最初获得的对象。
  
  设计选择:
  
  当你指向你需要指向的某个东西,而且绝不会改指向其它东西,或是当你实作一个运算符而其语法需要无法有指针达成,你就应该选择reference。其它任何时候,请采用指针。
  
  这和Java有什么关系?
  
  初学Java,鉴于reference的名称,我毫不犹豫的将它和C++中的reference等同起来。不过,我错了。在Java中,reference 可以随心所欲的赋值置空,对比一下上面列出的差异,就不难发现,Java的reference如果要与C/C++对应,它不过是一个穿着 reference外衣的指针而已。于是,所有关于C中关于指针的理解方式,可以照搬到Java中,简而言之,reference就是一个地址。我们可以把它想象成一个把手,抓住它,就抓住了我们想要操纵的数据。如同掌握C的关键在于掌握指针,探索Java的钥匙就是reference。
  
  一段小程序
  
  我知道,太多的文字总是令人犯困,那就来段代码吧!
  
  public class ReferenceTricks
  {
  public static void main(String[] args)
  {
  ReferenceTricks r = new ReferenceTricks();
  // reset integer
  r.i = 0;
  System.out.println
  ("Before changeInteger:" + r.i);
  changeInteger(r);
  System.out.println
  ("After changeInteger:" + r.i);
  
  // just for format
  System.out.println();
  
  // reset integer
  r.i = 0;
  System.out.println
  ("Before changeReference:" + r.i);
  changeReference(r);
  System.out.println
  ("After changeReference:" + r.i);
  }
  
  private static void
  changeReference(ReferenceTricks r)
  {
  r = new ReferenceTricks();
  r.i = 5;
  System.out.println
  ("In changeReference: " + r.i);
  }
  
  private static void
  changeInteger(ReferenceTricks r)
  {
  r.i = 5;
  System.out.println
  ("In changeInteger:" + r.i);
  }
  
  public int i;
  }
  
  我知道,把一个字段设成public是一种不好的编码习惯,这里只是为了说明问题。如果你有兴趣自己运行一下这个程序。你已经运行过了吗?结果如何?是否如你预期?下面是我在自己的机器上运行的结果:
  
  Before changeInteger:0
  In changeInteger:5
  After changeInteger:5
  
  Before changeReference:0
  In changeReference: 5
  After changeReference:0
  
  这里,我们关注的是两个change,changeReference和changeInteger。从输出的内容中,我们可以看出,两个方法在调用前和调用中完全一样,差异出现在调用后的结果。
  
  糊涂的讲解
  
  先让我们来分析一下changeInteger的行为。
  
  前面说过了,Java中的reference就是一个地址,它指向了一个内存空间,这个空间存放着一个对象的相关信息。这里我们暂时不去关心这个内存具体如何排布,只要知道,通过地址,我们可以找到r这个对象的i字段,然后我们给它赋成5。
  
  既然这个字段的内容得到了修改,从函数中返回之后,它自然就是改动后的结果了,所以调用之后,r对象的i字段依然是5。下图展示了changeInteger调用前后内存变化。
  
  让我们把目光转向changeReference。从代码上,我们可以看出,同changeInteger之间的差别仅仅在于多了这么一句:
  
  r = new ReferenceTricks();
  
  这条语句的作用是分配一块新的内存,然后将r指向它。执行完这条语句,r就不再是原来的r,但它依然是一个ReferenceTricks的对象,所以我们依然可以对这个r的i字段赋值。到此为止,一切都是那么自然。
  
  顺着这个思路继续下去的话,执行完changeReference,输出的r的i字段,那么应该是应该是新内存中的i,所以应该是5。至于那块被我们抛弃的内存,Java的GC功能自然会替我们善后的。
  
  事与愿违。
  
  实际的结果我们已经看到了,输出的是0。肯定哪个地方错了,究竟是哪个地方呢?
  
  参数传递的秘密
  
  知道方法参数如何传递吗?
  
  记得刚开始学编程那会儿,老师教导,所谓参数,有形式参数和实际参数之分,参数列表中写的那些东西都叫形式参数,在实际调用的时候,它们会被实际参数所替代。
  
  编译程序不可能知道每次调用的实际参数都是什么,于是写编译器的高手就出个办法,让实际参数按照一定顺序放到一个大家都可以找得到的地方,以此作为方法调用的一种约定。所谓“没有规矩,不成方圆”,有了这个规矩,大家协作起来就容易多了。这个公共数据区,现在编译器的选择通常是“栈”,而所谓的顺序就是形式参数声明的顺序。
  
  显然,程序运行的过程中,作为实际参数的变量可能遍布于内存的各个位置,而并不一定要老老实实的呆在栈里。为了守“规矩”,程序只好将变量复制一份到栈中,也就是通常所说的将参数压入栈中。
  
  我刚才说什么来着?将变量复制一份到栈中,没错,“复制”!
  
  这就是所谓的值传递。
  
  C语言的旷世经典《The C Programming Language》开篇的第一章中,谈到实际参数时说,“在C中,所有函数的实际参数都是传‘值’的”。
  
  马上会有人站出来,“错了,还有传地址,比如以指针传递就是传地址”。不错,传指针就是传地址。在把指针视为地址的时候,是否考虑过这样一个问题,它也是一个变量。前面的讨论中说过了,参数传递必须要把参数压入栈中,作为地址的指针也不例外。所以,必须把这个指针也复制一份。函数中对于指针操作实际上是对于这个指针副本的操作。
  
  Java的reference等于C的指针。所以,在Java的方法调用中,reference也要复制一份压入堆栈。在方法中对reference的操作就是对这个reference副本的操作。
  
  谜底揭晓
  
  好,让我们回到最初的问题上。在changeReference中对于reference的赋值实际上是对这个reference的副本进行赋值,而对于reference的本尊没有产生丝毫的影响。
  
  回到调用点,本尊醒来,它并不知道自己睡去的这段时间内发生过什么,所以只好当作什么都没发生过一般。就这样,副本消失了,在方法中对它的修改也就烟消云散了。
  
  也许你会问出这样的问题,“听了你的解释,我反而对changeInteger感到迷惑了,既然是对于副本的操作,为什么changeInteger可以运作正常?”这是很有趣的现象。
  
  好,那我就用前面的说法解释一下changeInteger的运作。所谓复制,其结果必然是副本完全等同于本尊。reference复制的结果必然是两个reference指向同一块内存空间。
  
  虽然在方法中对于副本的操作并不会影响到本尊,但对内存空间的修改确实实实在在的,回到调用点,虽然本尊依然不知道曾经发生过的一切,但它按照原来的方式访问内存的时候,取到的确是经过方法修改之后的内容。于是方法可以把自己的影响扩展到方法之外。
  
  多说几句
  
  这个问题起源于我对C/C++中同样问题的思考。同C/C++相比,在changeReference中对reference赋值可能并不会造成什么很严重的后果,而在C/C++中,这么做却会造成臭名昭着的“内存泄漏”,根本的原因在于Java拥有了可爱的GC功能。即便这样,我仍不推荐使用这种的手法,毕竟GC已经很忙了,我们怎么好意思再麻烦人家。
  
  在C/C++中,这个问题还可以继续引申。既然在函数中对于指针直接赋值行不通,那么如何在函数中修改指针呢?答案很简单,指针的指针,也就是把原来的指针看作一个普通的数据,把一个指向它的指针传到函数中就可以了。
  
  同样的问题到了Java中就没有那么美妙的解决方案了,因为Java中可没有reference的reference这样的语法。可能的变通就是将reference进行封装成类。至于值不值,公道自在人心。


http://www.iteye.com/topic/12961


在 jdk 1.2 及其以后,引入了强引用、软引用、弱引用、虚引用这四个概念。网上很多关于这四个概念的解释,但大多是概念性的泛泛而谈,今天我结合着代码分析了一下,首先我们先来看定义与大概解释(引用类型在包 java.lang.ref 里)。
  1、强引用(StrongReference)
    强引用不会被GC回收,并且在java.lang.ref里也没有实际的对应类型。举个例子来说:
    Object obj = new Object();
    这里的obj引用便是一个强引用,不会被GC回收。
  2、软引用(SoftReference)
    软引用在JVM报告内存不足的时候才会被GC回收,否则不会回收,正是由于这种特性软引用在caching和pooling中用处广泛。软引用的用法:
1     Object obj = new Object();
2     WeakReference softRef = new WeakReference(obj);
3     // 使用 softRef.get() 获取软引用所引用的对象
4     Object objg = softRef.get();
  3、弱引用(WeakReference)
    当GC一但发现了弱引用对象,将会释放WeakReference所引用的对象。弱引用使用方法与软引用类似,但回收策略不同。
  4、虚引用(PhantomReference)
    当GC一但发现了虚引用对象,将会将PhantomReference对象插入ReferenceQueue队列,而此时PhantomReference所指向的对象并没有被GC回收,而是要等到ReferenceQueue被你真正的处理后才会被回收。虚引用的用法:
1 Object obj = new Object();
2 ReferenceQueue refQueue = new ReferenceQueue();
3 PhantomReference phanRef = new PhantomReference(obj, refQueue);
4 // 调用phanRef.get()不管在什么情况下会一直返回null
5 Object objg = phanRef.get();
6 // 如果obj被置为null,当GC发现了虚引用,GC会将phanRef插入进我们之前创建时传入的refQueue队列
7 // 注意,此时phanRef所引用的obj对象,并没有被GC回收,在我们显式地调用refQueue.poll返回phanRef之后
8 // 当GC第二次发现虚引用,而此时JVM将phanRef插入到refQueue会插入失败,此时GC才会对obj进行回收
9 Reference phanRefP = refQueue.poll();
看了简单的定义之后,我们结合着代码来测试一下,强引用就不用说了,软引用的描述也很清楚,关键是 “弱引用” 与 “虚引用”。
弱引用:
01 public static void main(String[] args) throws InterruptedException {
02 Object obj = new Object();
03 ReferenceQueue refQueue = new ReferenceQueue();
04 WeakReference weakRef = new WeakReference(obj, refQueue);
05 System.out.println(weakRef.get());
06 System.out.println(refQueue.poll());
07 obj = null;
08 System.gc();
09 System.out.println(weakRef.get());
10 System.out.println(refQueue.poll());
11 }
由于System.gc()是告诉JVM这是一个执行GC的好时机,但具体执不执行由JVM决定,因此当JVM决定执行GC,得到的结果便是(事实上这段代码一般都会执行GC):
  java.lang.Object@de6ced
  null
  null
  java.lang.ref.WeakReference@1fb8ee3
从执行结果得知,通过调用weakRef.get()我们得到了obj对象,由于没有执行GC,因此refQueue.poll()返回的null,当我们把obj = null;此时没有引用指向堆中的obj对象了,这里JVM执行了一次GC,我们通过weakRef.get()发现返回了null,而refQueue.poll()返回了WeakReference对象,因此JVM在对obj进行了回收之后,才将weakRef插入到refQueue队列中。
虚引用:
01 public static void main(String[] args) throws InterruptedException {
02 Object obj = new Object();
03 ReferenceQueue refQueue = new ReferenceQueue();
04 PhantomReference phanRef = new PhantomReference(obj, refQueue);
05 System.out.println(phanRef.get());
06 System.out.println(refQueue.poll());
07 obj = null;
08 System.gc();
09 System.out.println(phanRef.get());
10 System.out.println(refQueue.poll());
11 }
同样,当JVM执行了GC,得到的结果便是:
  null
  null
  null
  java.lang.ref.PhantomReference@1fb8ee3
从执行结果得知,我们先前说的没有错,phanRef.get()不管在什么情况下,都会返回null,而当JVM执行GC发现虚引用之后,JVM并没有回收obj,而是将PhantomReference对象插入到对应的虚引用队列refQueue中,当调用refQueue.poll()返回PhantomReference对象时,poll方法会先把PhantomReference的持有队列queue(ReferenceQueue)置为NULL,NULL对象继承自ReferenceQueue,将enqueue(Reference paramReference)方法覆盖为return false,而此时obj再次被GC发现时,JVM再将PhantomReference插入到NULL队列中便会插入失败返回false,此时GC便会回收obj。事实上通过这段代码我们也的却看不出来obj是否被回收,但通过 PhantomReference 的javadoc注释中有一句是这样写的:
Once the garbage collector decides that an object obj is phantom-reachable, it is being enqueued on the corresponding queue, but its referent is not cleared. That is, the reference queue of the phantom reference must explicitly be processed by some application code.
翻译一下(这句话很简单,我相信很多人应该也看得懂):
一旦GC决定一个“obj”是虚可达的,它(指PhantomReference)将会被入队到对应的队列,但是它的指代并没有被清除。也就是说,虚引用的引用队列一定要明确地被一些应用程序代码所处理。
弱引用与虚引用的用处
  软引用很明显可以用来制作caching和pooling,而弱引用与虚引用呢?其实用处也很大,首先我们来看看弱引用,举个例子:
1 class Registry {
2 private Set registeredObjects = new HashSet();
3
4 public void register(Object object) {
5 registeredObjects.add( object );
6 }
7 }
所有我添加进 registeredObjects 中的object永远不会被GC回收,因为这里有个强引用保存在registeredObjects里,另一方面如果我把代码改为如下:
1 class Registry {
2 private Set registeredObjects = new HashSet();
3
4 public void register(Object object) {
5 registeredObjects.add( new WeakReference(object) );
6 }
7 }
现在如果GC想要回收registeredObjects中的object,便能够实现了,同样在使用HashMap如果想实现如上的效果,一种更好的实现是使用WeakHashMap。
而虚引用呢?我们先来看看javadoc的部分说明:
Phantom references are useful for implementing cleanup operations that are necessary before an object gets garbage-collected. They are sometimes more flexible than the finalize() method.
翻译一下:
虚引用在实现一个对象被回收之前必须做清理操作是很有用的。有时候,他们比finalize()方法更灵活。
很明显的,虚引用可以用来做对象被回收之前的清理工作。

转自:http://blog.sina.com.cn/s/blog_667ac0360102e9f3.html]]>
技术文章http://www.ruanjianpeixun.net/post/20171226284_9828.html#commenthttp://www.ruanjianpeixun.net/http://www.ruanjianpeixun.net/feed.asp?cmt=7432http://www.ruanjianpeixun.net/cmd.asp?act=tb&id=7432&key=0109c50b
Kotlin语言和Java语言100%互通_365bet线上_365bet亚洲娱乐_365bet在线yuzx@qingsoft.net (青软于老师)http://www.ruanjianpeixun.net/post/20171222916.htmlFri, 22 Dec 2017 08:55:57 +0800http://www.ruanjianpeixun.net/post/20171222916.html年初,甲骨文再次对谷歌所谓的安卓侵权使用Java提起诉讼,要求后者赔偿高达90亿美元。随后便传出谷歌因此计划将主力语言切换到苹果主导的Swift。
具体来说,Kotlin语言正式成为安卓开发的一级编程语言。
资料显示,Kotlin由JetBrains公司开发,于2010年首次推出,次年开源。它与Java 100%互通,并具备诸多Java尚不支持的新特性,下一版的Android Studio(3.0)将提供支持。
photoshop培训,电脑培训,电脑维修培训,移动软件开发培训,网站设计培训,网站建设培训

很多网友可能不禁会想,是不是使用这个语言就会脱离了Java虚拟机机制?是不是这样就是原生代码了?是不是用了这个就可以让安卓应用又快又好了?答案是,并不是的。Kotlin语言目前还是一个基于JVM(Java Virtual Machine)的语言,意味着和Java一样,也是运行在Java的虚拟机Runtime中的。
那Kotlin既然和Java一样都是基于JVM的语言,那支持安卓开发有什么意义呢?其实Kotlin是一门非常现代化的编程语言,比起来Java也有很多的优点。
首先,Kotlin可以使用Java所有的Library,两种代码可以在同一个项目中共存,甚至可以做到双向的一键转换。
其次,在Java编程中,开发者经常会遇到的一个问题就是null pointer exception,而使用Kotlin却完全不会遇到这类的问题。
很多时候开发者用Java编程,有很多Class都是大量的无用代码,有时候仅仅是为了储存一些数据就要新建一个Class和相应的get与set。在Kotlin中,类似的代码仅用一行就可以完成,大大减小了无用代码的数量。
Kotlin还是很年轻的编程语言,在今后发展的道路上还有很长的路要走。原生应用的支持也在官方的发展图中。
Kotlin最大的优势是和Java的互操作性。
其他JVM语言都实现了自己语言的标准库。而Kotlin则使用Java的标准库,只是对其进行了扩充。例如其他语言中的List可能是自己写的一个容器,而Kotlin里面的List则可能就是java.util.List类。
这样和Java交互的时候,就不需要进行类型转换了,能更加流畅的和Java交互,学习成本也更低。]]>
技术文章http://www.ruanjianpeixun.net/post/20171222916.html#commenthttp://www.ruanjianpeixun.net/http://www.ruanjianpeixun.net/feed.asp?cmt=7320http://www.ruanjianpeixun.net/cmd.asp?act=tb&id=7320&key=58556eca
Java程序员必须熟知的十项技术_365bet线上_365bet亚洲娱乐_365bet在线chengrw@qingsoft.net (青软程老师)http://www.ruanjianpeixun.net/post/20170625335.htmlSun, 25 Jun 2017 13:37:42 +0800http://www.ruanjianpeixun.net/post/20170625335.html 1、语法

Java程序员必须比较熟悉语法,在写代码的时候IDE的编辑器对某一行报错应该能够根据报错信息知道是什么样的语法错误并且知道任何修正。

2、命令

必须熟悉JDK带的一些常用命令及其常用选项,命令至少需要熟悉:appletviewer、 HtmlConverter、jar、 java、 javac、javadoc、javap、javaw、native2ascii、serialver,如果这些命令你没有全部使用过,那么你对java 实际上还很不了解。

3、工具

必须至少熟练使用一种IDE的开发工具,例如Eclipse、Netbeans、JBuilder、Jdeveloper、IDEA、JCreator或者Workshop,包括进行工程管理、常用选项的设置、插件的安装配置以及进行调试。

4、API

Java的核心API是非常庞大的,但是有一些内容笔者认为是Java程序员必须熟悉的,否则不可能熟练的运用Java,包括:

  • java.lang包下的80%以上的类的功能的灵活运用。

  • java.util包下的80%以上的类的灵活运用,特别是集合类体系、规则 表达式、zip、以及时间、随机数、属性、资源和Timer.

  • java.io包下的60%以上的类的使用,理解IO体系的基于管道模型的设计思路以及常用IO类的特性和使用场合。

  • java.math包下的100%的内容。

  • java.net包下的60%以上的内容,对各个类的功能比较熟悉。

  • java.text包下的60%以上的内容,特别是各种格式化类。

  • 熟练运用JDBC. 8)、java.security包下40%以上的内容,如果对于安全没有接触的话根本就不可能掌握java.

  • AWT的基本内容,包括各种组件事件、监听器、布局管理器、常用组件、打印。

  • Swing的基本内容,和AWT的要求类似。

  • XML处理,熟悉SAX、DOM以及JDOM的优缺点并且能够使用其中的一种完成XML的解析及内容处理。

5、测试

必须熟悉使用junit编写测试用例完成代码的自动测试。

6、管理

Java程序员必须熟悉使用ant完成工程管理的常用任务,例如工程编译、生成javadoc、生成jar、版本控制、自动测试。

7、排错

应该可以根据异常信息比较快速的定位问题的原因和大致位置。

8、思想

必须掌握OOP的主要要求,这样使用Java开发的系统才能是真正的Java系统。

9、规范

编写的代码必须符合流行的编码规范,例如类名首字母大写,成员和方法名首字母小写,方法名的第一个单词一般是动词,包名全部小写等,这样程序的可读性才比较好。

10、博学

Java程序员除了精通Java意外,还要掌握J2EE 、Oracle 、WebLogic、Jboss、Spring、Struts、Hibernate 等流行技术,掌握软件架构设计思想、搜索 引擎优化、缓存系统设计、网站负载均衡、系统性能调优等实用技术。

]]>
技术文章http://www.ruanjianpeixun.net/post/20170625335.html#commenthttp://www.ruanjianpeixun.net/http://www.ruanjianpeixun.net/feed.asp?cmt=1213http://www.ruanjianpeixun.net/cmd.asp?act=tb&id=1213&key=ff51c267
一个俄罗斯方块程序(非案例版)_365bet线上_365bet亚洲娱乐_365bet在线chengrw@qingsoft.net (青软程老师)http://www.ruanjianpeixun.net/post/20170625107.htmlSun, 25 Jun 2017 13:29:43 +0800http://www.ruanjianpeixun.net/post/20170625107.html 我们学面向对象的时候曾经做过俄罗斯方块这个案例,当时没有做完,我也不知道有《JAVA经典项目集锦》这个教材(做飞机大战的时候老师才告诉我们有这个书)。我当时想把它完成,可是自己又水平不够,不会JFrame,不会操纵图片,不会定时器,不会监听键盘,感觉难以下手。所以我在网上找了一个别人做的俄罗斯方块程序,把它小幅修改了一下,分享给大家。

即使你对我们已经做过的俄罗斯方块项目已经非常熟了,也可以看看这个程序。这个程序比起来《JAVA经典项目集锦》上面完整版的俄罗斯方块项目的优点在于精简,它就是为了实现俄罗斯方块运行时的效果而写的程序,能少写则少写,最终不到300行完成这个程序。而我们的项目为了教学,刻意使用了很多的类,使得最终程序很长(我没有敲一遍,估计不算注释要有600行)。这个程序节省篇幅的地方主要就在于它只用了一个三维数组,36行代码就把7种形状,4种旋转状态全部包括进去了,而我们的案例中采用了1个类(包括8个内部类),总共200行代码才做到同样的效果,而且,对这些类的操作也比对一个数组操作要更加麻烦,又在操作这些形状上拉开200行代码的差距。因此,虽然面向对象有很多好处,但是滥用面向对象也会招致很多不必要的麻烦,比如编写代码篇幅剧增,使得编程花费时间增加。

此程序除了没有用图片,直接用黑块代替cell的图片,实际游戏效果几乎和案例的一样,它也实现了计分和让速度等级随着游戏的进行递增。大家可以阅读一下,和我们做的案例对比一下,反思如果你自己编写一个程序,如何能尽量缩短程序的篇幅,兼顾写代码的效率和代码性能。

 

package Tetris; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import javax.swing.*; import javax.swing.Timer; public class Tetris extends JFrame{         public Tetris(){                 Tetrisblok a=new Tetrisblok();                 addKeyListener(a);                 add(a);         }         public static void main(String[] args){                 Tetris frame=new Tetris();                 JMenuBar menu=new JMenuBar();                 frame.setJMenuBar(menu);                 JMenu game=new JMenu("游戏");                 JMenuItem newgame=game.add("新游戏");                 JMenuItem pause=game.add("暂停");                 JMenuItem goon=game.add("继续");                 JMenuItem exit=game.add("退出");                 JMenu help=new JMenu("帮助");                 JMenuItem about=game.add("关于");                 menu.add(game);                 menu.add(help);                 frame.setLocationRelativeTo(null);                 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);                 frame.setSize(220,275);                 frame.setTitle("Tetris内测版");                 frame.setVisible(true);                 frame.setResizable(false);                         } } class        Tetrisblok extends JPanel implements KeyListener{         private int level=1;         private int sum=0;         private int blockType;         private int score=0;         private int turnState;         private int x;         private int y;         private int i=0;         int j=0;         int flag=0;         int[][]map=new int[13][23];         private final int shapes[][][]=new int[][][]{         //i                         {        {0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0},                                 {0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0},                                 {0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0},                                 {0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0}        },         //s                         {        {0,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0},                                 {1,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0},                                 {0,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0},                                 {1,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0}        },         //z                         {        {1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0},                                 {0,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0},                                 {1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0},                                 {0,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0}        },         //j                         {        {0,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0},                                 {1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0},                                 {1,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0},                                 {1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0}        },         //o                         {        {1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0},                                 {1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0},                                 {1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0},                                 {1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0}        },         //l                         {        {1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0},                                 {1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0},                                 {1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0},                                 {0,0,1,0,1,1,1,0,0,0,0,0,0,0,0,0}        },         //t                         {        {0,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0},                                 {0,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0},                                 {1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0},                                 {0,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0}        }         };         public void newblock(){                 sum++;                 if(sum%20==0){                         level++;                 }                 blockType=(int)(Math.random()*1000)%7;                 turnState=(int)(Math.random()*1000)%4;                 x=4;                 y=0;                 if(gameover(x,y)==1){                         newmap();                         drawwall();                         score=0;                         level=1;                         JOptionPane.showMessageDialog(null, "GAME OVER");                 }         }         public void drawwall(){                 for(i=0;i<12;i++){                         map[i][21]=2;                 }                 for(j=0;j<22;j++){                         map[11][j]=2;                         map[0][j]=2;                 }         }         public void newmap(){                 for(i=0;i<12;i++){                         for(j=0;j<22;j++){                                 map[i][j]=0;                         }                 }         }         Tetrisblok(){                 newblock();                 newmap();                 drawwall();                 Timer timer=new Timer(700/level,new TimerListener());                 timer.start();         }         public void turn(){                 int tempturnState=turnState;                 turnState=(turnState+1)%4;                 if(blow(x,y,blockType,turnState)==1){                 }                 if(blow(x,y,blockType,turnState)==0){                         turnState=tempturnState;                 }                 repaint();         }         public void left(){                 if(blow(x-1,y,blockType,turnState)==1){                         x=x-1;                 }                 ;                 repaint();         }         public void right(){                 if(blow(x+1,y,blockType,turnState)==1){                         x=x+1;                 }                 ;                 repaint();         }         public void down(){                 if(blow(x,y+1,blockType,turnState)==1){                         y=y+1;                         delline();                 }                 if(blow(x,y+1,blockType,turnState)==0){                         add(x,y,blockType,turnState);                         newblock();                         delline();                 }                 ;                 repaint();         }         public int blow(int x, int y, int blocktype,int turnState){                 for(int a=0;a<4;a++){                         for(int b=0;b<4;b++){                                 if(((shapes[blockType][turnState][a*4+b]==1)&&(map[x+b+1][y+a]==1))||                                                 ((shapes[blockType][turnState][a*4+b]==1)&&(map[x+b+1][y+a]==2))){                                         return 0;                                 }                         }                 }                 return 1;         }         public void delline(){                 int c=0;                 int d1=0;                 for(int b=0;b<22;b++){                         for(int a=0;a<12;a++){                                 if(map[a][b]==1){                                         c=c+1;                                         if(c==10){                                                 d1+=10;                                                 score+=d1;                                                 for(int d=b;d>0;d--){                                                         for(int e=0;e<11;e++){                                                                 map[e][d]=map[e][d-1];                                                         }                                                 }                                         }                                 }                         }                         c=0;                 }         }         public int gameover(int x,int y){                 if(blow(x,y,blockType,turnState)==0){                         return 1;                 }                 return 0;         }         public void add(int x,int y,int blockType,int turnState){                 int j=0;                 for(int a=0;a<4;a++){                         for(int b=0;b<4;b++){                                 if(map[x+b+1][y+a]==0){                                         map[x+b+1][y+a]=shapes[blockType][turnState][j];                                 }                                 ;                                 j++;                         }                 }         }         public void paintComponent(Graphics g){                 super.paintComponent(g);                 for(j=0;j<16;j++){                         if(shapes[blockType][turnState][j]==1){                                 g.fillRect((j%4+x+1)*10,(j/4+y)*10,10,10);                         }                 }                 for(j=0;j<22;j++){                         for(i=0;i<12;i++){                                 if(map[i][j]==1){                                         g.fillRect(i*10, j*10, 10, 10);                                 }                                 if(map[i][j]==2){                                         g.drawRect(i*10, j*10, 10, 10);                                 }                         }                 }                 g.drawString("score=" +score,125,10);                 g.drawString("level=" +level,125,30);                 g.drawString("抵制不良游戏,",125,50);                 g.drawString("拒绝盗版游戏。",125,70);                 g.drawString("注意自我保护,",125,90);                 g.drawString("谨防受骗上当。",125,110);                 g.drawString("适度游戏益脑,",125,130);                 g.drawString("沉迷游戏伤身。",125,150);                 g.drawString("合理安排时间,",125,170);                 g.drawString("享受健康生活。",125,190);         }         public void keyPressed(KeyEvent e){                 switch(e.getKeyCode()){                 case KeyEvent.VK_DOWN:                         down();                         break;                 case KeyEvent.VK_UP:                         turn();                         break;                 case KeyEvent.VK_RIGHT:                         right();                         break;                 case KeyEvent.VK_LEFT:                         left();                         break;                 }         }         public void keyReleased(KeyEvent e){         }         public void keyTyped(KeyEvent e){         }         class TimerListener implements ActionListener{                 public void actionPerformed(ActionEvent e){                         repaint();                         if(blow(x,y+1,blockType,turnState)==1){                                 y=y+1;                                 delline();                         }                         ;                         if(blow(x,y+1,blockType,turnState)==0){                                 if(flag==1){                                         add(x,y,blockType,turnState);                                         delline();                                         newblock();                                         flag=0;                                 }                                 flag=1;                         }                         ;                 }         } }
]]>
技术文章http://www.ruanjianpeixun.net/post/20170625107.html#commenthttp://www.ruanjianpeixun.net/http://www.ruanjianpeixun.net/feed.asp?cmt=1211http://www.ruanjianpeixun.net/cmd.asp?act=tb&id=1211&key=ec02e412
讲解2种查找素数的方法_365bet线上_365bet亚洲娱乐_365bet在线chengrw@qingsoft.net (青软程老师)http://www.ruanjianpeixun.net/post/20170622105.htmlThu, 22 Jun 2017 10:13:21 +0800http://www.ruanjianpeixun.net/post/20170622105.html 

  • 365bet线上题目大概的意思是: 查找2~N范围内所有的素数


    题意简单,粗暴。

    首先需要考虑,一个数字需要怎么判断它是否是素数?
    普及一下素数的概念:除了 1 和 它本身外没有能被整除的数,我们称为素数
    以数字 7为例:   int su = 7;  给 su 贴一个标签 假设是素数,boolean flag = true;
    然后我们根据素数的概念可以写出一下循环:
    for(int i = 2; i < su; i++){
        if(su%i==0)  {//有可以被整除的数字,说明su不是素除这时候我们就需要做两件事情
            //1. 需要把su的标记改成false说明它不是素数
            flag = false;
            //2. 然后跳出循环,因为有一个可以被整除就说明它不是素数了,继续判断下去没有意义。
             break;
        }
    }
    当学会判断某个数字是否为素数后,查找2~N范围内的素数就容易很多,因为我们直到for就是重复干相同一件事情的。所以外层嵌套一个for就可以了。
    for(int su = 2; su < N; su++){
        for(int i = 2; i < su; i++){
            if(su%i==0)  {//有可以被整除的数字,说明su不是素除这时候我们就需要做两件事情
                //1. 需要把su的标记改成false说明它不是素数
                flag = false;
                //2. 然后跳出循环,因为有一个可以被整除就说明它不是素数了,继续判断下去没有意义。
                 break;
            }
        }

       if(flag){
          //如果flag = true 则说明su为素数,直接输出就好了。
          System.out.println(su);
       }
    }
    小提示:判断一个数字不许要判断到它本身减1,实践证明 判断到它的平方跟即可。
    所以判断素数的条件可以写为:
    for(int i = 2; i <= Math.sqrt(su); i++)
    改写后的方法效率会快很多,但是还不能满足我们的要求。(假设寻找一千万以内的素数,没有优化的方法需要很久,优化后的方法大概需要13秒)
    但是楼主非要没有人性,想查找1亿以内的素数!!!
    以上方法虽然可以,但是效率一般。下面介绍一种更好的查找素数方法:
    我们知道如果2是素数,那么2的倍数就不是素数;3是素数,3的倍数就不是素数。
    基于这种想法我们就有了这第二种方法:
    先定义一个boolean类型的数组,用下标表示自然数0,1,2,3....N
    用数组里的内容来标记它是否为素数,是素数就true 不是则为false;初始时假设全是素数,当碰到第一个素数(2)的时候把它的倍数标记为false
    boolean[] prime = new boolean[N];
    //把数字全标记为true表示假设全为素数。
    for(int i = 2; i < prime.length;i++){
        prime = true;
    }

    /也可以用数组自带的fill方法,填充要简单的多。fill(要填充的数组,要填充的内容)

    接下来就是实现查找素数:

    for(int i = 2; i < prime.length; i++){

    if(prime){ //如果为真,则需要标记它的倍数为false

    for(int j = 2*i; j < prime.length; j+=2){ // 大家可以想想 j *= 2 为什么是错的。

    prime[j]=false;

    }

    }

    }

    这样就完成了素数的查找,输出的时候。只需判断boolean数组就可以了,当数组内容为true则输出它的下标。

    基于这种思想,可以做一个和素数有关的题: 将一个偶数分解称质因数相乘的形式 ,例如 : 90 = 2 * 3 * 3 *5

    利用编程解决问题,会让人体会到成功的快感。 不说了,敲代码去了。

]]>
技术文章http://www.ruanjianpeixun.net/post/20170622105.html#commenthttp://www.ruanjianpeixun.net/http://www.ruanjianpeixun.net/feed.asp?cmt=1210http://www.ruanjianpeixun.net/cmd.asp?act=tb&id=1210&key=c1378e4b
如何使按秒输出当前时间的程序不跳秒_365bet线上_365bet亚洲娱乐_365bet在线chengrw@qingsoft.net (青软程老师)http://www.ruanjianpeixun.net/post/201706222.6.htmlThu, 22 Jun 2017 10:10:23 +0800http://www.ruanjianpeixun.net/post/201706222.6.html 

  • 学习线程的时候,做了一个按秒显示当前时间的小程序。这个程序不难,
    但提到这个小程序有缺陷,就是你仔细盯着输出结果看,有时候会发现显示
    的时间突然跳了一秒,比如从19:56:42 跳到19:56:44。
    我对这个现象产生的原因很感兴趣,于是研究了一下它是如何产生的以及研究了一下解决办法。
    原程序如下:

    import java.text.SimpleDateFormat;
    import java.util.Calendar;
    import java.util.Date;

    public class ThreadDemo8 {

    /**
    * @param args
    */
    public static void main(String[] args) {
    /*
    * 实现电子表功能
    * 每秒钟输出依次当前系统时间
    * HH:mm:ss
    */
    Thread t=new Thread(){
    public void run(){
    Calendar calendar;
    Date date;
    String str;
    SimpleDateFormat sdf=new SimpleDateFormat("HH:mm:ss");
    for(;;){
    calendar=Calendar.getInstance();
    calendar.add(calendar.HOUR_OF_DAY, 16);
    calendar.add(calendar.MINUTE, 4);
    date=calendar.getTime();
    str=sdf.format(date);
    System.out.println(str+" ");//输出当前时间
    try{
    Thread.sleep(1000);
    }catch(InterruptedException e){
    }
    }
    }
    };
    t.start();
    }
    }

    为了使时间显示的更精确,我将用于输出当前时间的 System.out.println(str+" ");
    这句改为了 System.out.println(str+" "+System.currentTimeMillis()%1000);
    输出结果如下:
    20:09:43 998
    20:09:44 998
    20:09:45 998
    20:09:46 999
    20:09:47 999
    20:09:48 999
    20:09:49 999
    20:09:50 999
    20:09:52 0
    20:09:53 0
    20:09:54 0
    20:09:55 0
    20:09:56 1
    20:09:57 1
    20:09:58 1
    20:09:59 1
    20:10:00 2
    20:10:01 2
    20:10:02 2

    可以发现,在20:09:50到20:09:52发生了跳跃,把20:09:51隔过去了。还能发现大约每数4.4次,
    毫秒数会+1,也就是每4.4*1000=4400次即73分钟会跳一次秒。这是不是很像每天比实际地球自转
    时间多算约4分钟,所以每4年要多1天来抵消这个误差?这个计时程序的误差来自
    Thread.sleep(1000); 这里没有把计算机运行一次for循环所用的时间考虑进去。所以,它应
    该sleep更少的时间才对,大约少sleep 1/4.4毫秒。
    于是,我就像用增加润年的方法把
    Thread.sleep(1000); 改为:
    Thread.sleep(1000-i%5/4+i%40/39); //每5毫秒减少1毫秒,每40毫秒增加1毫秒
    它的输出结果变成这样了
    20:25:36 393
    20:25:37 393
    20:25:38 394
    20:25:39 394
    20:25:40 394
    20:25:41 393
    20:25:42 393
    20:25:43 394
    20:25:44 394
    20:25:45 394
    20:25:46 393
    20:25:47 394
    20:25:48 394
    误差小了很多,但是还是不够精确。就像我们现实的时间虽有润年调整,还是无法和地球自转时间对照,
    因此前几年有2天都出现了如19:59:60这样时间,增加1秒以对地球的计时做校正。
    但是,在这个程序中我们可以抛弃这种永远无法真正校准时间的方法。我想起了原来我学过的一门课程
    《自动控制原理》,里面有个概念叫做负反馈,根据结果来校正下一次的结果可以做到真正的精准。
    用在这里具体就是:输出的时间超了1毫秒就在下次减1毫秒,超2毫秒就减2毫秒。反正它能自动调整为
    准确时间
    于是有了如下方法:
    Thread.sleep(1000); 改为:
    Thread.sleep(1000-System.currentTimeMillis()%1000);
    输出结果如下:
    20:32:32 0
    20:32:33 0
    20:32:34 0
    20:32:35 0
    20:32:36 0
    20:32:37 0
    20:32:38 0
    可以发现,这次的输出时间都是和系统几乎同步的!做到了真正的精准。最终版本的程序如下:


    import java.text.SimpleDateFormat;
    import java.util.Calendar;
    import java.util.Date;

    public class ThreadDemo8 {

    /**
    * @param args
    */
    public static void main(String[] args) {
    /*
    * 实现电子表功能
    * 每秒钟输出依次当前系统时间
    * HH:mm:ss
    */
    Thread t=new Thread(){
    public void run(){
    Calendar calendar;
    Date date;
    String str;
    SimpleDateFormat sdf=new SimpleDateFormat("HH:mm:ss");
    for(int i=0;;i++){
    calendar=Calendar.getInstance();
    calendar.add(calendar.HOUR_OF_DAY, 16);
    calendar.add(calendar.MINUTE, 4);
    date=calendar.getTime();
    str=sdf.format(date);
    if(i>0){
    System.out.println(str+" "+System.currentTimeMillis()%1000);
    }
    try{
    Thread.sleep(1000-System.currentTimeMillis()%1000);
    }catch(InterruptedException e){
    }
    }
    }
    };
    t.start();
    }

    }
]]>
技术文章http://www.ruanjianpeixun.net/post/201706222.6.html#commenthttp://www.ruanjianpeixun.net/http://www.ruanjianpeixun.net/feed.asp?cmt=1209http://www.ruanjianpeixun.net/cmd.asp?act=tb&id=1209&key=ed0ba017
如何选择Java的分支?_365bet线上_365bet亚洲娱乐_365bet在线chengrw@qingsoft.net (青软程老师)http://www.ruanjianpeixun.net/post/20170622482.htmlThu, 22 Jun 2017 10:08:34 +0800http://www.ruanjianpeixun.net/post/20170622482.html 

  • 自诞生之日起,Java 语言就处于不断的发展中。目前,其主要分为以下 3 个分支。

    Java EE:Java Enterprise Edition 的缩写,主要用于企业级网站开发。由于其卓越

    的性能,被世界 500 强企业广泛使用。

    Java SE:Java Standard Edition 的缩写,主要用于桌面应用开发,常见软件包括

    Eclipse、NetBeans 等。同时,它也是学习其他分支的基础。

    Java ME:Java Micro Edition 的缩写,主要用于手机游戏的开发。由于其可以在

    PC 机上模拟手机开发,节约了购买专业设备的资金,因此备受推崇。

    对于 Java 初学者来说,应该从 Java SE 入手。目前,Java SE 的版本是 6.0 版。读者可

    以在 Oracle 的官方网站下载。

    java专家点评

    Java 语言本身内容就很复杂,再加上其开源的特性,吸引着全世界的程序员为其开发

    各种框架、小工具、游戏引擎等。作为一个新人应该认真学好基础知识,须知“万丈高

    楼平地起”。 

]]>
技术文章http://www.ruanjianpeixun.net/post/20170622482.html#commenthttp://www.ruanjianpeixun.net/http://www.ruanjianpeixun.net/feed.asp?cmt=1208http://www.ruanjianpeixun.net/cmd.asp?act=tb&id=1208&key=2b69f0c1
java中final 参数可以修改吗?_365bet线上_365bet亚洲娱乐_365bet在线chengrw@qingsoft.net (青软程老师)http://www.ruanjianpeixun.net/post/20170622530.htmlThu, 22 Jun 2017 10:07:07 +0800http://www.ruanjianpeixun.net/post/20170622530.html 

  • 参数被修饰成 final,意味着该参数不能在方法体中被修改,所以一旦修改了方法体中的 final 参数,程序将无法通过编译。例如:

     

    package com.mingrisoft;public class Something {public int addOne(final int x) {return ++x; //改变 final 参数的值,出错}}
    说明:

     

    上面代码使用关键字 final 修饰了 addOne()方法的参数 x,而在方法体中对参数 x 的值

    进行了改变,所以程序将无法通过编译,当鼠标移动到 return 语句的 x 处时,将显示提示

    文本,如图 3.2 所示。

    final 参数可以修改吗?

    图 3.2 改变方法的 final 参数,程序出错

]]>
技术文章http://www.ruanjianpeixun.net/post/20170622530.html#commenthttp://www.ruanjianpeixun.net/http://www.ruanjianpeixun.net/feed.asp?cmt=1207http://www.ruanjianpeixun.net/cmd.asp?act=tb&id=1207&key=f92d6302