#
从很小都认识苹果机了,我不记得我是否在小学的时候学过Basic,只记得大学实验室里的苹果机我的确是摸过(得益于我是教师子弟,有“特殊待遇”),也看到计算机系的学生们编写的游戏。初中,有了自己的游戏机。玩过魂斗罗,坦克。当时觉得很不过瘾,心想以后能自己编写游戏就好了,于是立志以后做个程序员。
高考不顺利,只考上了普通学校电力专业。这还是幸亏当时学校的罗老师(那四年,她一直都在帮助我)看到我以前的成绩还不错,决定要下我,否则,我就往下落的更厉害了。电力专业几乎没有计算机课程。等到学校关于自动化的时候,开始接触了汇编,和自学了C.当时很羡慕学计算机的那个女老乡,姓杨,呵呵,因为羡慕,还被别人误认为我喜欢她,其实完全不是,她根本对计算机没有兴趣,毕业后也去了当公务员,可惜啊,早知道如此,她何必要高出几分,占据我喜欢的专业呢,我甚至为此感到暗自不爽。 不过大学还是学到了一些计算机皮毛知识,C程序写的很好,记得写了一个模仿TT的打字程序。汇编也不错,写个文件病毒,源代码10K,编译链接后3K多,很大,AV95能识别出来,我想大概是我写的太烂,别的杀毒程序,象KV300,都不认为这是个病毒。不管怎么样,我没有拿这个干啥坏事情。这始终是不光彩的事情。
该毕业了,家乡的供电局没能进去。我怨我老妈没有帮我跑关系,其实我跟我老妈都不愿意我去,我老妈是不想让我回铜仁,我自己也不想做电力职工(虽然在我们那是一等的暴有钱的工作),我还是喜欢去做个程序员,为此也退掉了别的电力相关的工作。但是,我始终不到该如何入门。毕业了,门卫老头开始赶我们出去,我工作无着落,同学们都已经回到家乡开始上班了,我还在跟老头打游击。他进我退,他退我上床休息,有次晚上洗澡,被他发现,吓得我光着屁股从三楼跑到5楼,再跑回三楼。呵呵,那时候整个宿舍楼都空了,所以也不算丢脸了。
好运终于坚持到了,网上碰到一网友,后来我叫他秦哥,他说他需要一个人帮他做个网站。我便毛遂自荐了一下,其实,那时候我不懂做网站,不懂ASP,不过我相信我的能力,果然,一段适应时间后,我成了他得力的帮手,我也开始正式进入程序员这个行业了。相比现在的很多学生,我觉得他们比我幸运多了,在大学的时候都已经学习到很多知识,甚至是已经有一定的实践了。刚毕业就能踏入这行,还能有地方住,要知道我不光要跟老头打游击,有时候还睡在电脑城广场的板凳上,早上起来看的第一眼便是保安和他身边对我俯视眈眈的狼狗。
搞懂了ASP和网站后,开始考虑学更多的东西,这时候我已经放弃了我编写游戏程序的梦想了,因为我跟本不知道如何去追逐这个梦想。我也放弃了我比较擅长的单片机开发(现在应该叫嵌入式)。我转向了Java.俗话说,女怕嫁错狼,男怕入错行。8年前的这个时候,我算是马马虎虎开始我的JavaEE道路吧,这儿有点体会就是一定要坚持自己的理想,而这个理想,应该能养活你的,能让你有兴趣做的事情。
初学Java,有些迷惑,当时微软有个VJ++,我先买了一本介绍这样的书看,结构后来发现它主要是桌面程序的,而且,跟我知道的JSP不太一样。当时也没有想到可以找人问或者论坛上发给帖子问。幸好后来明智的转到了JSP,挺简单,跟ASP差不多,tb概念都能通用(毕竟处理的问题都一样嘛),比起现在的孩子来说,我当时学的东西太少了,不用学习hibernate,spring,j2ee,也不用学习ant,Junit什么的,呵呵,关键还是当时书太少,见识少,也没有这么多新玩意。好处就是我能深入JSP技术,为以后理解这些Web框架打下了很好的基础。不象现在的孩子,还搞不懂JSP,就去弄MVC,搞的本末倒置了。
J2EE技术得到提高得益于后来到了北京,去了ZZ公司,现在看来,好不夸张的说,从这个公司出来的程序员,都有一定创新能力和解决问题能力。一到这公司,就做了一个算是大的项目,几十个人,还包括国防科技大学的数十个博士,当时用到了很多J2EE技术,象EJB,JMS都用到了,当时不懂这些,费了很多力气去学,还好项目本身就是个很好的学习材料。通过专研项目代码学到了不少东西,远比看书强多了。现在的很多培训方式都是通过做独立完成项目来学习技术,这是很有道理的。当时那个项目做了一年,期间我对自己要求蛮高的,总会多学点东西,比如学了EJB 无状态会话Bean,虽然项目没有用到有状态Bean,但还是花时间去搞明白。这个项目期间,头一次知道了还有英文资料这么一说,象什么Weblogic使用说明,Java文档都,我都会强迫自己去看,有时候打印下来,躺在小床,打开台灯看,那感觉真是美阿。
对于J2ME编程开发平台,在其他平台风生水起的时候,J2ME编程开发平台似乎很沉寂。本文将简单介绍一下几个J2ME编程开发平台的重要概念。
内存
我们一直在强调,移动信息设备的内存非常小,使用起来应该加倍的珍惜,但是我们却很少知道这些内存是如何分类的,下面将做详细的介绍。事实上MIDP设备的内存分为三种,
1.ProgrammeMemory、Heap、persistentStorage
ProgrammeMemory是移动信息设备分配给MIDletsuite的空间,因为MIDletsuite是以jar文件进行发布的,所以这个文件的大小可以认为是ProgrammeMemory的大小。一些厂商对ProgrammeMemory的最大值是有限制的,例如我的Nokia6108的最大值是64k,超过的话将不能进行安装。减小MIDletsuite的大小非常重要,一个便捷的方法就是使用混淆器对应用程序进行混淆,这样可以减小jar文件的大小。在以后的文章中我会谈到如何使用Proguard.
Heap是应用程序在运行过程中存放所创建的对象的存储空间,tb本地变量和成员变量也是放在Heap上的,MIDP设备中提供的Heap空间大概在几十k到几百K.
PersistentStorage的空间是用来实现MIDP应用程序的本地数据持久性存储的,在RecordManagementSystem从入门到精通中我做了详细的介绍这里就不再多说了。
2.ConnectedLimitedDeviceConfiguration
CLDC包括一个Java虚拟机和一系列的基础类,J2ME的专家组经过对移动信息设备进行硬件抽象后得到他们的特点,然后设计并实现了在移动信息设备上运行的java虚拟机,通常我们把它叫做KVM.在CLDC1.0还同时提供了由java.io、java.lang、javax.microediton.io、java.util组成的基础类。在CLDC1.1里面添加了java.lang.ref.
3.MobileInfomationDeviceProfile
MIDP是运行在CLDC基础之上的,在MIDP中定义了应用程序的生命周期、用户图形界面、数据管理系统等子集,从而构建起了J2ME平台。通常,J2ME平台由一个CLDC和一个或者多个Profile构成。
Jquery是继prototype之后又一个优秀的Javascrīpt框架。它是轻量级的js库,除了兼容CSS3外,还兼容各种浏览器+)。jQuery使用户能更方便地处理HTML documents、events、实现动画效果,并且方便地为网站提供AJAX交互。
HTML5 中新定义的 HTML 元素,可以用来在 HTML 页面中通过 JavaScriptb 绘制图形、制作动画。现在要推荐的 jCanvas 就是一个 jQuery 的绘图插件,它封装了一些绘制图形的方法,只需编写几行代码即可生成图形。
以下是JCanvas 绘制窗口并对其监听的程序代码分享
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
class JCanvas extends JComponent
{
public JCanvas()
{
setDoubleBuffered(true);
}
public void paintComponent(Graphics g)
{
Dimension size = getSize();
g.setColor(getBackground());
g.fillRect(0,0,size.width,size.height);
}
}
class TestJCanvas
{
public static void main(String s[] )
{
MyWindowListener l = new MyWindowListener();
JCanvas c = new JCanvas();
c.setBackground(Color.yellow);
JFrame f = new JFrame("Test JCanvas...");
f.addWindowListener(l);
f.getContentPane().add(c,BorderLayout.CENTER);
f.pack();
f.setSize(500,400);
f.show();
}
}
class MyWindowListener extends WindowAdapter
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
}
虽然jQuery上手简单,相比于其他库学习起来较为简单,但是要全面掌握,却不轻松。因为它涉及到网页开发的方方面面,提供的方法和内部变化有上千种之多。初学者常常感到,入门很方便,提高很困难。本文的目标是将jQuery选择器做一个系统的梳理,试图理清jQuery的设计思想,找出学习的脉络,使读者从入门到精通。
jQuery是什么
简单的说,jQuery是一个JavaScript框架,它的宗旨是:写更少的代码,做更多的事情。对于Web开发人员而言,jQuery是一个功能强大的JavaScript库,能更加快速开发相关应用,例如AJAX交互,JavaScript动画效果等。对于Web设计师而言,jQuery封装了Javascript源码细节,实现了与HTML标签的有效分离,便于设计师更加专注于Web页面设计效果。基于此,网页的用户体验大大增强,包括网页的交互性,视觉效果等等。
jQuery的核心设计思想是:选择某个网页元素,然后对其进行某种操作。那么如何选择、定位某个网页元素呢?对于JavaScript开发人员而言,通常的一种手段是document.getElementById()。而在jQuery语法中,使用的是美元符号“$”,等价的选择表达式写法为:
var someElement = $("#myId");
jQuery之所以称之为“jQuery”,主要就是因为它强大的选择器,也就是Javascript Query的意思。下面,我们具体介绍jQuery选择器相关的设计思想:
一、jQuery基本选择器
前面提到,选择器是jQuery的特色。jQuery的基本选择器主要分为tb以下五种类型:
1. $(“#myId”) // 选择ID为myId的网页元素
2. $(“标签名”) // 例如$(“div”)获取的就是HTML文档中的所有的div元素的jQuery对象集合
3. $(“.myClass”) // 获取的是HTML文档中所有的class为“myClass”的元素集合
4. $(“*”) // 这个获取的是HTML文档中的所有的元素
5. $(“selector1,selector2,selector3…selectorN “) // 这种选择器叫做组选择器。例如:$(“span,#two”)
// 选取所有的span标签元素和id=two的元素。
二、jQuery层次选择器
无论何时,通过jQuery选择器获取的jQuery对象任何时候都是一组元素。jQuery的层次选择器主要分为以下两种类型:
1. $(“ancestor descendant”):选取parent元素后所有的child元素。ancestor的中文意思是“祖先”,descendant的中文意思是“后代”。例如:
$(“body div”) 选取body元素下所有的div元素。
$(“div#test div”) 选取id为“test”的div所包含的所有的div子元素
2. $(“parent > child”):选取parent元素后所有的第一个child元素。例如:
$(“body > div”) 选取body元素下所有的第一级div元素。
$(“div#test > div”) 选取id为“test”的div所包含的所有的第一级div子元素
三、jQuery过滤选择器
jQuery最基本过滤选择器包括:
1. :first // 选取第一个元素。$(“div:first”)选取所有div元素中的第一个div元素
2. :last // 选取最后一个元素。$(“div:last”)选取所有div元素中的最后一个div元素
3. :even // 选取索引是偶数的所有元素。$(“input:even”)选取索引是偶数的input元素。
4. :odd // 选取索引是奇数的所有元素。$(“input:odd”)选取索引是奇数的input元素。
5. :eq(index) // 选取索引等于index的元素。$(“input:eq(1)”)选取索引等于1的input元素。
6. :gt(index) // 选取索引大于index的元素。$(“input:gt(1)”)选取索引大于1的input元素。
7. :lt(index) // 选取索引小于index的元素。$(“input:lt(3)”)选取索引小于3的input元素。
jQuery内容过滤选择器,可以轻松地对DOM文档中的文本内容进行筛选,从而准确地选取我们所需要的元素。
1. :contains(text) // 选取含有文本内容为“text”元素。$(“div:contains(‘你’)”)选取含有文本“你”的div元素。
2. :empty // 选取不包含子元素和文本的空元素。$(“div:empty”)选取不包含子元素(包括文本元素)的div空元素。
3. :parent // 选取含有子元素或者文本的元素。$(“div:parent”)选取拥有子元素(包括文本元素)的div元素。
可以看见,jQuery内容过滤选择器的过滤规则主要体现在它所包含的子元素或文本内容上。
jQuery可见性过滤选择器的用法如下:
1. :hidden // 选取所有不可见的元素。$(“:hidden”)选取网页中所有不可见的元素。
2. :visible // 选取所有可见元素。$(“div:visible”)选取所有可见的div元素。
jQuery属性过滤选择器的过滤规则是通过元素的属性来获取相应的元素。
1. [attribute] // 选择拥有此属性的元素。$(“div[id]“)选取拥有属性id的元素。
2. [attribute=value] // 选取属性值为value的元素。$(“div[name=test]“)选取属性name的值为“test”的div元素。
3. [attribute!value] // 选取属性值不等于value的元素。
4.[attribute^=value] // 选取属性的值以value开始的元素。
5. [attribute$=value] // 选取属性的值以value为结束的元素。
6. [attribute*=value] // 选取属性的值含有value的元素。
7. [selector1] [selector2] [selectorN] //复合属性选择器。$(“div[id][name*=test]“)选取拥有属性id,且属性name的值含有“test”的div元素
jQuery子元素过滤选择器的过滤规则相对于其它的选择器稍微有些复杂。
1. :nth-child(index/even/odd/equation) // 选取每个父元素下的第index个子元素或者奇偶元素。
2. :first-child // 选取每个父元素的第一个子元素
3. :last-child // 选取每个父元素的最后一个子元素
jQuery表单对象属性过滤选择器主要是对所选择的表单元素进行过滤,例如选择不可用的表单元素、被选中的下拉框、多选框等等。
1. :enabled // 选取所有可用的表单元素。$(“#form1 :enabled”)选取id为“form1”的表单内的所有可用元素。
2. :disabled // 选取所有不可用的表单元素。
3. :checked // 选取所有被选中的元素。$(“input:checked”)选取所有被选中的元素。
4. :selected // 选取所有被选中的选项元素。$(“select :selected”)选取所有被选中的选项元素(option)。
jQuery中引入了表单选择器,让我们能极其方便地获取到一个表单中的某个或某类型的元素。
在前一节实现异步调用的基础上 , 现在我们来看一下一个完善的 Java 异步消息处理机制 .
[ 写在本节之前 ]
在所有这些地方 , 我始终没有提到设计模式这个词 , 而事实上 , 多线程编程几乎每一步都在应该设计模式 . 你只要能恰如其份地应用它 , 为什么要在意你用了某某名称的模式呢 ?
一个说书人它可以把武功招数说得天花乱坠 , 引得一班听书客掌声如雷 , 但他只是说书的 . 真正的武林高手也许并不知道自己的招式在说书人口中叫什么 , 这不重要 , 重要的是他能在最恰当的时机把他不知名的招式发挥到极致 !
你了解再多的设计模式 , 或你写出了此类的著作 , 并不重要 , 重要的是你能应用它设计出性能卓越的系统 .
本节的这个例子 , 如果你真正的理解了 , 不要怀疑自己 , 你已经是 Java 高手的行列了 . 如果你抛开本节的内容 , 五天后能自己独立地把它再实现一次 , 那你完全可以不用再看我写的文章系列了 , 至少是目前 , 我再也没有更高级的内容要介绍了 .
上节的 Java 异步调用为了简化调用关系 , 很多角色被合并到一个类中实现 , 为了帮助大家改快地抓住核心的流程 . 那么一个真正的异步消息处理器 , 当然不是这样的简单 .
一. 它要能适应不同类型的请求 :
本节用 makeString 来说明要求有返回值的请求 . 用 displayString 来说明不需要返回值的请求 .
二. 要能同时并发处理多个请求 , 并能按一定机制调度 :
本节将用一个队列来存放请求 , 所以只能按 FIFO 机制调度 , 你可以改用 LinkedList, 就可以简单实现一个优先级 ( 优先级高的 addFirst, 低的 addLast).
三. 有能力将调用的边界从线程扩展到机器间 (RMI)
四. 分离过度耦合 , 如分离调用句柄 ( 取货凭证 ) 和真实数据的实现 . 分离调用和执行的过程 , 可以尽快地将调返回 .
现在看具体的实现 :
public interface Axman {
Result resultTest(int count,char c);
void noResultTest(String str);
}
这个接口有两个方法要实现 , 就是有返回值的调用 resultTest 和不需要返回值的调用
noResultTest, 我们把这个接口用一个代理类来实现 , 目的是将方法调用转化为对象 , 这样就可以将多个请求 ( 多个方法调 ) 放到一个容器中缓存起来 , 然后统一处理 , 因为 Java 不支持方法指针 , 所以把方法调用转换为对象 , 然后在这个对象上统一执行它们的方法 , 不仅可以做到异步处理 , 而且可以将代表方法调用的请求对象序列化后通过网络传递到另一个机器上执行 (RMI). 这也是 Java 回调机制最有力的实现 .
一个简单的例子 .
如果 1: 做 A
如果 2: 做 B
如果 3: 做 C
如果有 1000 个情况 , 你不至于用 1000 个 case 吧 ? 以后再增加呢 ?
所以如果 C/C++ 程序员 , 会这样实现 : (c 和 c++ 定义结构不同 )
type define struct MyStruct{
int mark;
(*fn) ();
} MyList;
然后你可以声明这个结构数据 :
{1,A,
2,B
3,C
}
做一个循环 :
for(i=0;i<length;i++) {
if( 数据组 [i].mark == 传入的值 ) ( 数据组 [i].*fn)();
}
简单说 c/c++ 中将要被调用的涵数可以被保存起来 , 然后去访问 , 调用 , 而 Java 中 , 我们无法将一个方法保存 , 除了直接调用 , 所以将要调用的方法用子类来实现 , 然后把这些子类实例保存起来 , 然后在这些子类的实现上调用方法 :
interface My{
void test();
}
class A implements My{
public void test(){
System.out.println(“A”):
}
}
class B implements My{
public void test(){
System.out.println(“B”):
}
}
class C implements My{
public void test(){
System.out.println(“C”):
}
}
class MyStruct {
int mark;
My m;
public MyStruct(int mark,My m){this.mark = amrk;this.m = m}
}
数组 :
{ new MyStruct(1,new A()),new MyStruct(2,new B()),new MyStruct(3,new C())}
for(xxxxxxxxx) if( 参数 == 数组 [i].mark) 数组 [i].m.test();
这样把要调用的方法转换为对象的保程不仅仅是可以对要调用的方法进行调度 , 而且可以把对象序列化后在另一台机器上执行 , 这样就把调用边界从线程扩展到了机器 .
回到我们的例子 :
class Proxy implements Axman{
private final Scheduler scheduler;
private final Servant servant;
public Proxy(Scheduler scheduler,Servant servant){
this.scheduler = scheduler;
this.servant = servant;
}
public Result resultTest(int count,char c){
FutureResult futrue = new FutureResult();
this.scheduler.invoke(new ResultRequest(servant,futrue,count,c));
return futrue;
}
public void noResultTest(String str){
this.scheduler.invoke(new NoResultRequest(this.servant,str));
}
}
其中 scheduler 是管理对调用的调度 , servant 是真正的对方法的执行 :
Servant 就是去真实地实现方法 :
class Servant implements Axman{
public Result resultTest(int count,char c){
char[] buf = new char[count];
for(int i = 0;i < count;i++){
buf[i] = c;
try{
Thread.sleep(100);
}catch(Throwable t){}
}
return new RealResult(new String(buf));
}
public void noResultTest(String str){
try{
System.out.println("displayString :" + str);
Thread.sleep(10);
}catch(Throwable t){}
}
}
在 scheduler 将方法的调用 (invkoe) 和执行 (execute) 进行了分离 , 调用就是开始 ” 注册 ” 方法到要执行的容器中 , 这样就可以立即返回出来 . 真正执行多久就是 execute 的事了 , 就象一个人点燃爆竹的引信就跑了 , 至于那个爆竹什么时候爆炸就不是他能控制的了 .
public class Scheduler extends Thread {
private final ActivationQueue queue;
public Scheduler(ActivationQueue queue){
this.queue = queue;
}
public void invoke(MethodRequest request){
this.queue.putRequest(request);
}
public void run(){
while(true){
// 如果队列中有请求线程 , 测开始执行请求
MethodRequest request = this.queue.takeRequest();
request.execute();
}
}
}
在 schetbduler 中只用一个队列来保存代表方法和请求对象 , 实行简单的 FIFO 调用 , 你要实更复杂的调度就要在这里重新实现 :
class ActivationQueue{
private static final int MAX_METHOD_REQUEST = 100;
private final MethodRequest[] requestQueue;
private int tail;
private int head;
private int count;
public ActivationQueue(){
this.requestQueue = new MethodRequest[MAX_METHOD_REQUEST];
this.head = this.count = this.tail = 0;
}
public synchronized void putRequest(MethodRequest request){
while(this.count >= this.requestQueue.length){
try {
this.wait();
}
catch (Throwable t) {}
}
this.requestQueue[this.tail] = request;
tail = (tail + 1)%this.requestQueue.length;
count ++ ;
this.notifyAll();
}
public synchronized MethodRequest takeRequest(){
while(this.count <= 0){
try {
this.wait();
}
catch (Throwable t) {}
}
MethodRequest request = this.requestQueue[this.head];
this.head = (this.head + 1) % this.requestQueue.length;
count --;
this.notifyAll();
return request;
}
}
为了将方法调用转化为对象 , 我们通过实现 MethodRequestb 对象的 execute 方法来方法具体方法转换成具体对象 :
abstract class MethodRequest{
protected final Servant servant;
}
本文的目的并不是介绍使用的什么技术,而是重点阐述其实现原理。
一、 异步和同步
讲通俗点,异步就是不需要等当前执行的动作完成,就可以继续执行后面的动作。
通常一个程序执行的顺序是:从上到下,依次执行。后面的动作必须等前面动作执行完成以后方可执行。这就是和异步相对的一个概念——同步。
案例:
A、张三打电话给李四,让李四帮忙写份材料。
B、李四接到电话的时候,手上有自己的工作要处理,但他答应张三,忙完手上的工作后马上帮张三写好材料,并传真给张三。
C、通完电话后,张三外出办事。
说明:
张三给李四通完电话后,就出去办事了,他并不需要等李四把材料写好才外出。那么张三让李四写材料的消息就属于异步消息。
相反,如果张三必须等李四把材料写好才能外出办事的话,那么这个消息就属于同步消息了。
二、 异步的实现
传统的程序执行代码都是从上到下,一条一条执行的。
但生活中有很多情况并不是这样,以上的案例中,如果李四需要几个小时以后才能帮张三写好材料的话,那张三就必须等几个小时,这样张三可能会崩溃或者抓狂。
这种一条龙似的处理,显示不太合理。
可以使用以下办法来处理这种问题:
张三找王五去给李四打电话,等李四写好材料后,由王五转交给张三。这样张三就可以外出办其他的事情了。
问题得到了合理的解决,之前张三一条线的工作,由张三和王五两条线来完成了,两边同时进行,彼此不耽误。
三、 计算机语言的实现
办法有了,如何用程序来模拟实现呢?
A、以前由一个线程来处理的工作,可以通过新增一个线程来达到异步的目的。这也就是JAVA中的多线程技术。
B、最后李四写好的材料必须交给张三,以做他用。这就是回调。
回调你可以这样来理解:
A发送消息给B,B处理好A要求的事情后,将结果返回给A,A再对B返回的结果来做进一步的处理。
四、 Java代码的实现
A、 回调的实现
Java代码
tb
public interface CallBack {
public void execute(Object... objects );
}
public interface CallBack { public void execute(Object... objects ); }
Java是面向对象的语言,因此回调函数就变成了回调接口。
B、 消息的发送者
Javatb代码
public class Local implements CallBack,Runnable{
private Remote remote;
private String message;
public Local(Remote remote, String message) {
super();
this.remote = remote;
this.message = message;
}
public void sendMessage()
{
System.out.println(Thread.currentThread().getName());
Thread thread = new Thread(this);
thread.start();
System.out.println("Message has been sent by Local~!");
}
public void execute(Object... objects