AOP与OOP

(最初发布在JavaResearch.org,现在整理好放到自己的地方.)

作者:Narayanan A.R. June 15, 2005
翻译:ZhangV 2005-06-28
原文出自:DevX.com
原文链接:http://www.devx.com/Java/Article/28422/0/page/1

首先你要明确的一点,AOP和OOP是两种不同的认识事物的角度,并不是说有了AOP就不要用OOP.AOP所关注的是传统OOP不能优雅解决的问 题.(程序员通常都是完美主义者,当解决某个问题不优雅的时候,那就意味着不完美.)下面将就一个简单的例子来说明他们到底如何的不同.

作为一个使用OOP多年的人来说,当我听说AOP可以解决一些OOP一直都不能优雅地解决的问题时,我觉得应该去探个究竟了.对两种技术的比较最能给我们实际应用提供见解.这里我设计了一个例子:一个OOP应用,其中某些方面适合使用AOP.

本文展示了一个简单的例子.一开始介绍了问题域,然后分别给出传统OOP的解决方法与结合了AOP解决方法.后者使用了 JDK5.0,JUnit,和 AspectWerkz.最后我会说明如何编写代码.读完本文后,我希望你能知道AOP到底是什么,用来解决什么样的问题.

问题域描述
一个软件公司雇佣一个程序员,指定给他一个业务部门并要求他随时向经理报告.当团队成员完成他们的目标时,经理会给他们相应的奖金.公司所需要的方案必须能够增加一个新的雇员并给当前的员工增加奖金.为了方便,我们用CSV文件存储数据.


图1 解决方案模型

类Manager(经理)继承自类Employee,包含一个额外的属性,Managing Project.一个部门可能包含很多员工.多个部门构成了公司.暂不考虑公司这样的一个类,因为它在问题域之外.

解决方案设计
以下流程图描述了解决方案设计.


图2 对象之间的交互(增加一个新的员工,指派给他一个部门和经理)

出于简单的考虑,本文只关注必需的细节.你也可以深入代码得到你想要的其他信息.
下载
EmployeeServiceTestCase, 一个JUnit测试用例,模拟一个最终用户,创建新员工记录,指派部门和经理.它获取所有可用的部门和经理数据并显示在图形界面上.为了实例化域对象 BusinessUnit和Manager,获得的记录将传递给工厂类.之后,通过给EmployeeService传递一个引用来创建一个 Employee对象.这个服务类使用EmployeeFactory创建对象,并把这个对象传给EmployeeRepository 来进行持久化操作.

应用程序中需要面向哪些”Aspect”
到目前为止,对模型和设计的讨论还限于一个较抽象的层面.现在,我转向这个应用的其他方面 – 这对理解AOP的价值至关重要.

操作所需的资源

<pre lang=”java”>

public static Set findAllBusinessUnits() throws RepositoryException {
Set businessUnits = new HashSet();
try {
FileReader businessUnitFile = null;
BufferedReader bufferedBusinessUnitFile = null;
try {
businessUnitFile = new FileReader(FILE_NAME);
bufferedBusinessUnitFile = new BufferedReader(businessUnitFile);
String businessUnitRecord;
while((businessUnitRecord = bufferedBusinessUnitFile.readLine()) != null) {
BusinessUnit businessUnit = BusinessUnitFactory.createBusinessUnit(businessUnitRecord);
businessUnits.add(businessUnit);
}
} finally {
if(bufferedBusinessUnitFile != null) {
bufferedBusinessUnitFile.close();
}
if(businessUnitFile != null) {
businessUnitFile.close();
}
}
} catch(IOException ioe) {
String message = “IOError. Unable to find Business Unit records”;
logger.log(SEVERE, message, ioe);
throw new RepositoryException(message, ioe);
}

logger.log(INFO, “Manager Records returned:” + businessUnits.size());
return businessUnits;
}
</pre>

上面的代码通过FileReader和BufferedReader来读取CSV文件中的业务数据.
应用程序重复地从资源文件中取得数据然后在操作完成后释放. 我们会发现:去掉程序的这两个”Aspect”将提高代码的可读性并达到一个更好的设计,因为去掉这些”多余”的东西,剩下的代码才是这个方法真正的精 髓.这个方法的作用是读取业务单位数据.所以不应该也不需要去知道”如何获取和释放资源以及这个过程中出现的异常”这个”切面”.同样地,使用AOP处理 异常也变得不同.(后面将详细介绍)

持久层
传统的OOP使用持久化类(repository classes)来持久应用程序的持久层.即:

<coolcode lang=”java” linenum=”off”>
public class EmployeeRepository {

public static void createEmployee(Employee employee) throws RepositoryException {
//使用print writer把数据放入csv文件
}

public static String findEmployeeRecordById(String id) throws RepositoryException {
//使用file reader来获得指定id的员工数据
}

public static Employee findEmployeeById(String id) throws RepositoryException {
//使用该方法获取员工数据,Employee对象由工厂类创建
}

public static void updateEmployee(Employee employee) {
//更新员工数据
}
}
</coolcode>

类EmployeeService 使用一个EmployeeRepository给应用中相关雇员提供服务,在一个企业应用中,从域模型 (domain model)中去掉持久层代码是一种设计上的改进.模型设计者和程序员就可以关注各自的业务逻辑和持久层处理.后面你将会看到如何通过 AOP来达到这样的效果.

日志
删除用于调试的日志代码将会极大地改进代码的可读性.考虑下面的代码片断:

1.
2. public Employee createEmployee(String name,
3. String contactNumber,
4. BusinessUnit businessUnit,
5. Manager manager)
6. throws EmployeeServiceException {
7. String id = createNewEmployeeId();
8. Employee employee =
9. EmployeeFactory.createEmployee(id, name, contactNumber, businessUnit, manager);
10. try {
11. EmployeeRepository.createEmployee(employee);
12. } catch(RepositoryException re) {
13. String message = “Created employee successfully:” + employee;
14. logger.log(SEVERE, message);
15. throw new EmployeeServiceException(message, re);
16. }
17. logger.log(INFO, “Created employee successfully:” + employee);
18. return employee;
19. }

上面的代码里包含了一个致命错误和一个成功信息.输出日志这一”Aspect”同样可以移到业务模型外独立实现.

异常处理
异常处理的例子我这里不再赘述,但这节已经通过上面的代码讨论了潜在的问题.当你调用EmployeeRepository 对象的 createEmployee 方法时,你可能会得到一个RepositoryException异常.传统的解决方法是,在这个类中处理.另一种方法是,当 RepositoryException 异常被抛出时createEmployee 方法返回null,catch块中的其他逻辑可以在类外处理这一错误.
错误处理在不同的情况中也会不同.但是,通过AOP可以区分开每种情况.

图3

图3中描述了AOP方法的设计以及在一个更抽象的层次上类间的交互.你可以通过对比图1和图3来更好地理解AOP.
程序的目的是通过BusinessUnit对象读取CSV文件中的记录然后填入类BusinessUnitService 中的map.使用AOP来填充这个map有点类似后门(backdoor)方法 — 控制被委派给BusinessUnit 来读取存储介质中的记录.

AOP就是定义一些切入点(pointcut)和处理方法(advice).一个”切入点”是源代码中一个执行点.前面的例子定义了一个”切入点” — 类 BusinessUnitService中的findBusinessUnits方法.一个”处理方法”顾名思义就是当执行到某个”切入点”时的一块代 码.类BusinessUnitPersistentAspect 包括advice方法findAllBusinessUnits,该方法从存储介质中载入数据,然后使用工厂类创建BusinessUnit 对象.然后这个对象被加入map,map对象的引用通过 BusinessUnitService 对象获得.”切入点”和”处理方法”组成了所谓的”aspect”.

为了读取存储介质中的数据,OOP方法通过一个DAO类来做.而AOP中,你只要定义一个”切入点”和相应的”处理方法”来读取数据.AOP框架会以advice的形式注入代码,既可以在执行期也可以在编译期.

总而言之,当类BusinessUnitService 中的findAllBusinessUnits 方法被调用时,AOP框架会在”切入点”处注入处理方法,通过BusinessUnit 对象预先读取数据来填充map对象.这样,持久层方面的代码就可以移到业务代码之外了.

新方法里的”Aspect”

本节讨论如何用AOP为应用程序的各个”切面”建模

操作资源

类BusinessUnitPersistenceAspect 的持久方法使用了一个buffered reader.你甚至可以定义”切面”的”切面”,但为了简单,这里只关注类的查找方法.

1.
2. @Aspect(“perJVM”)
3. public class BufferedFileReaderAspect {
4.
5. @Expression(“execution(* org.javatechnocrats.aop.withaop.aspects.BusinessUnitPersistenceAspect.find*(..))”)
6. Pointcut businessUnitPersistenceAspect;
7.
8. // 其他”切入点”定义
9.
10. @Expression(“businessUnitPersistenceAspect ||
11. employeePersistenceAspect ||
12. managerPersistenceAspect”)
13. Pointcut allPersistencePointcuts;
14.
15. private Map<Class, String> fileNames;
16.
17. public BufferedFileReaderAspect() {
18. System.out.println(“BufferedFileReaderAspect created”);
19. fileNames = new HashMap<Class, String>();
20. fillFileNames();
21. }
22.
23. @Before(“allPersistencePointcuts”)
24. public void assignReader(JoinPoint joinPoint) throws Throwable {
25. System.out.println(“assignReader advice called”);
26. Object callee = joinPoint.getCallee();
27. IBufferedFileReaderConsumable bufReaderConsumable = (IBufferedFileReaderConsumable)callee;
28. Class persistenceClass = callee.getClass();
29. String fileName = fileNames.get(persistenceClass);
30. FileReader fileReader = new FileReader(fileName);
31. BufferedReader bufferedReader = new BufferedReader(fileReader);
32. bufReaderConsumable.setBufferedReader(bufferedReader);
33. }
34.
35. @AfterFinally(“allPersistencePointcuts”)
36. public void releaseReader(JoinPoint joinPoint) throws Throwable {
37. //释放buffered reader等资源
38. }
39. //其他方法
40. }

上面的代码试图为每一个方法创建”切入点”– 所有以find开头的方法.无论何时这些方法被调用,assignReader方法都会被提前执行.这里它获取被调用的类实例然后设置新建的BufferedReader.

同样地,在releaseReader 方法里,代码会预先关闭buffered reader集合.本节只解释@before和@
AfterFinally 这两个”切入点”.(以J2SE 5.0的标记定义).另外,你也可以在方面定义的xml文件中声明他们.你可以查看例程源代码中的aop.xml文件.

下载

持久化

前面提到,OOP方法使用BusinessUnit 来为应用的持久层填充Map.在下面的高亮代码中(@before一行,以及while循环代码 – 译者注),当BusinessUnitService 中的方法findAllBusinessUnits 被调用时”处理方法 “findAllBusinessUnits 也将被调用.

1.
2. @Aspect(“perJVM”)
3. public class BusinessUnitPersistenceAspect implements IBufferedFileReaderConsumable {
4.
5. private BufferedReader buffFileReader;
6.
7. @Before(“execution(Collection org.javatechnocrats.aop.withaop.BusinessUnitService.findAllBusinessUnits())”)
8. public void findAllBusinessUnits(JoinPoint joinPoint) throws Throwable {
9. System.out.println(“findAllBusinessUnits advice called”);
10. Map<String, BusinessUnit> businessUnits =
11. ((BusinessUnitService)joinPoint.getThis()).getBusinessUnits();
12. String businessUnitRecord;
13. while((businessUnitRecord = buffFileReader.readLine()) != null) {
14. BusinessUnit businessUnit = BusinessUnitFactory.createBusinessUnit(businessUnitRecord);
15. businessUnits.put(businessUnit.getId(), businessUnit);
16. }
17. }
18.
19. public void setBufferedReader(BufferedReader buffFileReader) {
20. System.out.println(“BusinessUnitPersistenceAspect.setBufferedReader called”);
21. this.buffFileReader = buffFileReader;
22. }
23.
24. public BufferedReader getBufferedReader() {
25. System.out.println(“BusinessUnitPersistenceAspect.getBufferedReader called”);
26. return this.buffFileReader;
27. }
28. }

“处理方法”从数据存储中读取记录,使用工厂类创建一个BusinessUnit实例.然后这个实例被加入到Map.该Map掌管程序的所有持久化”aspect”.

日志
本文中的例子没有包含一个完整的日志AOP解决方案.但是,它为java.lang.Object类的toString方法定义了一个”切入点”来获取类 的调试信息.因此,域中的类不需要实现toString方法.通常情况下你可能需要为每一个类都要实现toString方法.

1.
2. @Aspect(“perJVM”)
3. public class LoggingAspect {
4.
5. @Around(“execution(String org.javatechnocrats.aop.withaop..*.toString())”)
6. public Object toStringAdvice(JoinPoint joinPoint) throws Throwable {
7. System.out.println(“toStringAdvice called”);
8. String toString = (String)joinPoint.proceed();
9. Object target = joinPoint.getThis();
10. Field fields[] = target.getClass().getDeclaredFields();
11. List members = new ArrayList(fields.length + 1);
12. members.add(toString);
13. for(Field field : fields) {
14. field.setAccessible(true);
15. Object member = field.get(target);
16. members.add(field.getName() + “=” + member);
17. }
18. return members.toString();
19. }

你也可以用这个样例代码完成错误处理”切面”.

深入源代码

为了理解样例需求的OOP设计,请参看源代码并思考以下几个问题:
下载

* 首先分析oldway包中EmployeeServiceTestCase 类中的代码
* 查看testEmployeeCredit 方法
* 搞懂业务类Employee和BusinessUnit
* 学习 service,repository和factory这些概念.这些是业务驱动设计的主要概念.
* 更深入地理解oldway包中的service,repository和factory类

而AOP地理解则应该是:
* 分析newway包中EmployeeServiceTestCase 类
* 查看service,repository和factory类,基本和前一种差不多.只是你要让”处理方法”截获程序的执行流程.
* 研究aspect类学习”切入点”的定义

要执行程序,你需要做的工作:
* 下载AspectWerkz 2.0 http://aspectwerkz.codehaus.org/
* 设置以下的环境变量:
set JAVA_HOME=c:Program FilesJavajdk1.5.0
set ASPECTWERKZ_HOME=C:aw_2_0_2
set PATH=%PATH%;%ASPECTWERKZ_HOME%bin
set CLASSPATH=
C:aw_2_0_2libaspectwerkz-2.0.RC2.jar;C:aw_2_0_2libaspectwerkz-jdk5-2.0.RC2.jar; classes;C: junit3.8.1resourceslibjunit.jar
* 解压缩源代码和其他文件
* 编译Java文件,但不要编译测试用例否则你调试时会遇到一个错误.
* 进行离线调试.假设你把文件解压缩到c:aop ,类文件解压到c:aopclasses,在c:aop目录下执行以下命令:
% ASPECTWERKZ_HOME%binaspectwerkz -offline etc/aop.xml -cp classes classes
* AOP框架会修改类来注入必要的字节码
* 编译测试用例,使用JUnit运行它.

后记
当你完成了上面的这些工作,你应该有以下的领悟:
* 程序中的交叉关联
* 关于AOP中”切面”的含义
* 如何用AOP来把程序业务层中的交叉关联分离出来,使用”切入点”和”处理方法”
* OOP和AOP时在程序控制流上的不同

从本文你应该也得到一种看待实际开发的新视角.你应该有信心使用AOP来改进项目中的设计,建模,提高代码的重用性.至少,你可以开始使用AOP来处理日志,错误和持久化.

个人觉得,AOP的学习曲线相对较陡,尤其在理解定义”切入点”的句法时.理想的情况是,使用OOP来设计业务模型,使用AOP把业务模型中的交叉关联移出,从而使代码简洁并提高可读性.

AOP的一个缺点是会使调试变得困难,因为不同于OOP,程序流变的复杂了,交互是由编译期或执行期决定.我准备将来做一些自动化工具来解决这个问题.

有点迷失

突然有点迷失了,一不小心步入外包这行,感觉自己距离梦想似乎越来越远.也许是一直都不清楚自己”到底”适合做什么吧. 这个问题直到这刻仍然不能给自己一个满意的答案.

一直以来觉得软件这一行应该是google,ms那样 –  实现一个想法(不一定要多伟大),做出人人都喜欢的东西 . 可能是这段时间的工作性质的关系,整天的作一些毫无生趣的事情.辞职的想法每半分钟从脑中闪过一次.但是当清醒一些的时候,就像现在,我又会问自己:ok,如果辞职了,你打算干什么?不要告诉你要去读书吧,我知道当你看了研究生的那些华而不实的课程设计以后,你就不会想去浪费一年或者两年的时间了.

那我到底可以做什么?到底可以怎样去改变自己的现状. –  难道我真的还怕改变吗?

Nokia E61 E61i键盘使用方法

Nokia E61 E61i键盘使用方法

符号说明:
键盘默认输入法为拼音。
↗:左下角的蓝色箭头,按一次后可输入1个符号或1个数字,之后恢复拼音键盘;快速按2次后,键盘变为数字或符号输入方式;
↑:Windows的Shift健,按1次可输入1个小写英文字母,之后恢复拼音键盘;
Ctrl+↑:切换输入法(和Windows的一样),输入法依次为“拼音”,“笔画”,“英文”,“拼音”;
Ctrl+空格健:转换输入法(和Windows的一样)中文,英文;
↗+Ctrl:打开或关闭蓝牙;
↗+Car:打开或关闭红外;
在拼音和英文输入方式下,键盘输入没有错误。
在中文输入方式下,符号可通过按Car键后选择符号,在英文输入方式下,Car不可用。
在英文输入方式下,按1、2、3次↑(Shift)可切换英文的输入状态为“Abc”,“abc”,“ABC”。
在中文或英文输入方式下,按2次↗都可换为符号输入方式,除Q(!),R(1),T(2),Y(3),U(*),F(4),G(5),H(6),J (#),V(7),B(8),N(9),M(0),正确外,其它的都有错误,实为:W(”),E(&),I(-),O(+),P(=),A(¥),S ($),D(ε),K(_),L(、),Z(<),X(>),C()
@:在空格键的左边(”,)。
键盘锁(开锁):左上角键+↗

(个别仅E61/E70等QWERTY全功能键盘机型)拼音打字的时候,按右下角的Chr键,也就是亚太版的中键,可以区分待选字符的四个声调,按一下是 第一声,按两下是第二声…….按ctrl+space可实现中英文切换;按↑+Chr可调出编辑语言窗口,选择不同的语言感谢其他机油提供新的信 息!!中文选择字区翻页:在输入后出现的当前中文字对话框内如果没有需要的字,可以使用空格键进行翻页直到选到需要的字。候选字对话框内字的选择:按一下 左下角的箭头(笔形键),再按相应的数字区的数字即可选择。中文输入状态时,按住Chr不放再按字母键可以输入小写英文字母;按一下↑再放开,然后按字母 键也可输入小写英您尚未登陆/注册查看回复返回论坛续上:文字母;按住↑的同时再按字母键,则是输入大写英文字母。从[多媒体资料]浏览图片时,可使用以 下数字键代替菜单,快速操作:1:逆时针旋转3:顺时针旋转5:放大0:缩小2,4,6,8:上左右下移动图片*:在全屏幕和普通视图间切换。用 realone观看视频时,按2可实现全屏切换用自带播放器播放音乐时,5——暂停或播放,4——前一首/(长按快退),6——后一首/(长按快进),8 ——停止,按挂机键直接转入后台播放按↑+回车可做标记,在批量删除短信时很方便;按Ctrl键+上下摇动摇杆可标记相应的文件,再次重复操作则取消标 记。在应用程序中选取文字。按住shift键,并以游戏杆向左或向右卷动以选取文字。您尚未登陆/注册查看回复返回论坛续上:t+游戏杆复制。Ctrl+ C剪下。Ctrl+X贴上。Ctrl+V复原。Ctrl+Z斜体。Ctrl+I粗体。Ctrl+B进入文件夹或者选择程序时可用回车键代替摇杆的OK键 (部分不行)在短信输入完成后,可按拨号键直接发送短信待机状态下:↗+Car为打开红外的快捷方式(↗为键盘左下角的兰色箭头):按住↗再按Car键可 打开红外,红外在没有找到对应红外口后会自动关闭。↗+Ctrl为打开蓝牙或者关闭蓝牙的快捷方式。按住↗再按Ctrl键可打开蓝牙,再次重复操作则为关 闭蓝牙。

[在待机状态下]

↗+Car为打开红外的快捷方式(↗为键盘左下角的兰色箭头):
按住↗再按 Car 键可打开红外,红外在没有找到对应红外口后会自动关闭。

↗+Ctrl 为打开蓝牙或者关闭蓝牙的快捷方式:
按住↗再按 Ctrl 键可打开蓝牙,再次重复操作则为关闭蓝牙。

左功能键+↗为键盘锁:
按下左功能键再按↗可以锁闭手机键盘,重复操作则为打开手机键盘。

左功能键+* 为键盘锁:
按下左功能键再按 * 可以锁闭手机键盘,重复操作则为打开手机键盘。

[在文字编辑中]

↗键的基本使用(↗为键盘左下角的兰色箭头):

按下一次可以输入一个数字或者符号,输入完成后则恢复拼音键盘。
快速按下二次则变为可输入多个数字或者符号,再次按下↗则恢复复拼音键盘。

↑为键盘最下面一行键盘的两个向上方向的键盘:

按下一次可输入一个小写英文字母,输入完成后则恢复拼音键盘。
按住↑再按其他键可以输入多个大写英文字母,输入完成后松开↑则恢复拼音键盘。
快速按两下↑并不松开可以输入多个小写英文字母,输入完成后松开↑则恢复拼音键盘。

注:↑键与其他symbian机型上的编辑键功能相同,也就是我们常说的笔形键。
注:←键与其他symbian机型上的C键功能相同,就是删除键。

Ctrl键+↑为切换输入法:

按住输Ctrl键并按下↑一次或者多次法排列顺序:拼音-笔画-英文。

Ctrl键+空格键:

按住输 Ctrl 键并按下空格键为转换输入法,在中文输入和英文输入之间转换。

中文输入状态下按下Car:

在中文输入状态下按下Car可出现标点符号的选择对话框。

↗+数字键中文选字:

在输入拼音或者笔画完成出现的中文字对话框时,选择第一个字可以直接用回车键选择,需要选择第二字至第六个字可按住↗+字左上角的对应数字来选择需要的字,当然也可以使用5向操纵杆选择需要的字。

注:回车键即为Car键正上方的键。

中文选择字区翻页:

在输入后出现的当前中文字对话框内如果没有需要的字,可以使用空格键进行翻页直到选到需要的字。

待机画面:
▲按住#键→可切换"标准"与"标准模式"(如要切换其它模式请轻按一下电源键)
▲按住P或M键→打开Web浏览
▲按住红色键→结束UMTS/GPRS连结(帮你节约联机费用)
▲按住菜单建→打开类似Windows XP的”工作管理员”窗口,可以迅速切换程序(此功能适用所有画面,不仅限于待机)*

■菜单画面:(不同韧体可能有异)
请注意到菜单排列恰好可以对应到蓝色数字键共12个(横的算起来有3列,纵的有4行)
▲左下方蓝色功能键+对应的蓝色数字键→选取并进入该程序(可以有效减少游戏杆使用)
■讯息画面:
▲Ctrl + C 复制
▲Ctrl + X 剪下
▲Ctrl + V 贴上
以上三项,若为中文韧体,可用”按住Chr键→带出剪贴画面”取代
▲Chr + 任意文字键→输入特殊字符(若是在注音模式下,会输入注音符号,不过无法和键盘上的符号一致,真是奇怪)
▲Ctrl + 游戏杆向下→下一页 ▲Ctrl + 游戏杆向上→上一页
▲Shift + 游戏杆左(右)→选取字符
▲Shift + Ctrl + 游戏杆左(右)→选取整组文字(word)
▲Ctrl + 游戏杆左(右)→移动光标至前后文字(word)
▲绿色通话键→立即寄出讯息
▲红色结束键→立即退出讯息编辑
▲Shift + 游戏杆向上(下)→欲选取多个讯息或邮件,功能类似windows xp中的档案总管

■行事历画面:
▲蓝色功能键+ * →切换月、周、日、待办事项检视画面
▲蓝色功能键+ # →切换回今日
■浏览器画面:
▲蓝色功能键+0 → 回到首页 ▲蓝色功能键+1 → 显示书签
▲蓝色功能键+2 → 显示文字搜寻对话框
▲蓝色功能键+3 → 回到前一网页检视
▲蓝色功能键+5 → 跳出网页选取窗口
▲蓝色功能键+8 → 切换显示全网页缩图
▲蓝色功能键+9 → 显示本网页url对话框
▲蓝色功能键+* → Zoom In
▲蓝色功能键+# →Zoom Out
■文件编辑画面:(文字编辑器)
▲Ctrl + B →黑体字
▲Ctrl + I →斜体字
▲Ctrl + U →底线
▲Ctrl + 游戏杆向下→跳到文件结尾
▲Ctrl + 游戏杆向上→跳到文件开头

■多媒体数据画面
▲1 →逆时针旋转
▲3 →顺时针旋转
▲* →切换窗口与全屏幕检视
▲2, 4, 6, 8 →平移,和游戏杆功能相同

■音乐播放机画面:
▲4 →跳至前一首歌曲
▲Hold 4 →快速回转歌曲(非常好用)
▲6 →跳至下一首歌曲诺基亚
▲Hold 6 →快速播放歌曲
▲5 →播放/暂停
▲8 →结束播放

■Real Player画面:
▲2 →切换全屏幕画面
▲ 游戏杆向上→快速播放影片
▲ 游戏杆向下→快速回转影片
■工程码(待机画面直接输入,不必按enter
▲*#0000# →显示韧体版本
▲*#06# →显示IMEI号码
▲*#2820# →显示蓝牙地址
▲*#62209526# →显示WLAN adapter的MAC地址
▲*#92702689# →显示通话定时器
▲*#7780# →soft resets并且reset手机设定
▲*#7370# →hard resets
若你指的是回复E61手机的初始值的话
1. 键入*#7370#
2. Default Password 是 “12345”
ps : Password 失败不要超过 3 次,否则手机会被lock

大屿山一日游

本来打算昨天去的 - 重阳节要登高的嘛.可是一出门看到到处是人.有点人群恐惧.于是改成今天.大约花了2个小时的时间才到了东涌,去看大佛的人并不多,以外国人和内地人为主. 虽然缆车因为上次事故而停用,空荡荡的大楼反成东涌一景.(香港人的神经似呼太过脆弱了,人天天死,何况都没死人). 尽管如此直达宝莲寺的23路大巴还是很值得一坐的,窄窄的山道和不时的陡坡让人知道什么是真正的过山车.大约45分钟后,车到了昂坪宝莲寺.

说死在的,没觉得有什么特别的地方,大佛是挺大的,很不甘心地绕着他走了两圈,终归还是有点失望的离开了. – 不过山顶上的风景还不错,可以看到附近光秃秃的山头和山下的居民,还有不远处正在测试的缆车……大佛脚下就是宝莲寺,不值一提.
还是不甘心,所以没有原路返回,而是坐上去梅窝的大巴,梅窝曾经是大屿山的水陆交通要枢,游荡了大约40分钟,就上的船回中环.

下次如果再来而又有时间的话,从昂坪沿凤凰径走到梅窝应该是个不错的选择.看了一下地图大概需要1个半小时到2个小时的样子.

路线:马铁 第一城 ->大围换东铁 ->旺角换荃湾线 ->荔景换东涌线 -> 东涌 23路 ->宝莲寺大佛 2路 ->梅窝 新渡轮->中环码头 天星小轮 ->红勘 东铁-> 大围 马铁 ->第一城
基本上一天的大多数时间都是花在了路上.

和谐个鸟

最近一段时间准备去香港,发现出入境管理局的人真是蠢啊, ….
一开始准备办Business Visa, 由于没有上海户口, 所以需要居住证, 正赶上居住证正在续办.这种情况下,出入的人告诉我开什么证明都不好使,只有等.
可是偏偏客户那边比较急, 我就打家(户口所在地)那边出入的电话, 得到的答案是:由于我所在的公司是上海的公司,我也不能在那边办.
然后没办法,那就办Personal的吧,问了一下上海出入,人家告诉我personal的要到户口所在地办, 就算有居住证都不好使.
我在打家出入的电话, 得到的答案是,我必须本人去办. 崩溃了, 回家比tm去香港都远呢. 至于么????? 有必要吗? 我要通行证看来是鸟用都没有.
设计这种制度的人真是跟傻子一样. — 当然客观的来说,我的情况实在是太特殊了一点.谁让咱是没有上海户口呢?似乎我要是有上海户口什么事情都可以解决了.
最后看一下我的可选方案:
1.搞个户口来 — 难度系数google, 必须是海龟或者海带或者海白菜,俺土鳖没戏
2.飞回家办personal的 —难度系数0, 来回机票的钱,过去露个脸,等3天.然后可以办半个月的,如果下次再去还要再办
3.偷渡 — 难度系数10,先到深圳, 香港也是偷渡么?不知道
以前觉得户口没什么…”老子好好干就行了,管他户口呢”  — 傻b了吧
现在我真是稀罕这东西,真好啊,真好, 你说中国人整天就把心思放到tm户口上能搞出个p来!
我就是质疑这种sb制度, 头脑正常的人都去哪里了?
唉,香港,号称咱自己的地方,比去印度都难……

敲几个字到notepad

好久没有这么奢侈的休息了,懒觉,坐在电脑前发呆,杀杀毒,查查爵士现在多少钱,清理一下桌面,陪lp看看电视剧,敲几个字到notepad

“一个人能不能成功,运气占至少50%,也就是外界不可抗因素,而像我这样并不聪明的人,可能运气需要的更多”, 一直都觉得自己可以凭实力赢得成功, 看来还是不成熟的表现.当然现在也不能说已经成熟了. — 一个人永远都不能说自己成熟了.
不是说只靠运气就行.没有成功并不意味着没努力或者太差劲,有可能只是运气欠佳.但也要看如何定义成功,每个人对成功的定义都不一样,有的人觉得有钱,有的人觉得要有名,有的人要追求有意义的生活….说我现在要追求有意义的生活简直就是cd
有时候在想自己给自己定了那么多苛刻的目标是为什么,仅仅因为看到那些”成功”的人总是”规划好自己的事业,然后努力向一个有一个目标迈进”,引用从志峰那里听到的一句话,”那就是一个美丽的扯”.那之后呢?有没有想过
简单一点,做自己觉得有意义的事情,不要被别人影响,不要期望影响别人.休息,休息

得了,不臭p了,去买酒,给下周做准备!