<?xml version="1.0" encoding="UTF-8" ?>



<rss version="2.0" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">

<channel>
<title><![CDATA[蛐蛐JAVA博客交流区]]> </title>
<description>
<![CDATA[JAVA 精彩闪过]]>
</description>
<link>http://longkuangxiao.blog.bokee.net/</link>
<language>zh-cn</language>
<creator>longkuangxiao</creator>
<pubDate>Wed, 22 Feb 2006 18:02:42 CST </pubDate>
<generatorAgent rdf:resource="http://www.bokee.net"/>
<ttl>5</ttl>

<item>
<title>JavaScript就这么回事1：基础知识</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/76651.html</link>
<description>
<![CDATA[<span class="tpc_content" id="text0">有些时候你精通一门语言，但是会发现你其实整天在和其它语言打交道，也许你以为这些微不足道，不至于影响你的开发进度，但恰恰是这些你不重视的东西会浪费你很多时间，我一直以为我早在几年前就已经精通JavaScript了，直到目前，我才越来越觉得JavaScript 远比我想象的复杂和强大，我开始崇拜它，就像崇拜所有OOP语言一样～<br />趁着节日的空隙，把有关JavaScript的方法和技巧整理下，让每个在为JavaScript而烦恼的人明白，JavaScript就这么回事！并希望JavaScript还可以成为你的朋友，让你豁然开朗，在项目中更好的应用～<br />适合阅读范围：对JavaScript一无所知～离精通只差一步之遥的人<br />基础知识：HTML<br /><br /><br /><br />1 创建脚本块<br />1: &lt;script language=&rdquo;JavaScript&rdquo;&gt;<br />2: JavaScript code goes here<br />3: &lt;/script&gt;<br /><br /><br />2 隐藏脚本代码<br />1: &lt;script language=&rdquo;JavaScript&rdquo;&gt;<br />2: &lt;!--<br />3: document.write(&ldquo;Hello&rdquo;);<br />4: // --&gt;<br />5: &lt;/script&gt;<br /><br />在不支持JavaScript的浏览器中将不执行相关代码<br /><br />3 浏览器不支持的时候显示<br />1: &lt;noscript&gt;<br />2: Hello to the non-JavaScript browser.<br />3: &lt;/noscript&gt;<br /><br /><br />4 链接外部脚本文件<br />1: &lt;script language=&rdquo;JavaScript&rdquo; src=&rdquo;filename.js&rdquo;&gt;&lt;/script&gt;<br /><br /><br />5 注释脚本<br />1: // This is a comment<br />2: document.write(&ldquo;Hello&rdquo;); // This is a comment<br />3: /*<br />4: All of this<br />5: is a comment<br />6: */<br /><br /><br />6 输出到浏览器<br />1: document.write(&ldquo;&lt;strong&gt;Hello&lt;/strong&gt;&rdquo;);<br /><br /><br />7 定义变量<br />1: var myVariable = &ldquo;some value&rdquo;;<br /><br /><br />8 字符串相加<br />1: var myString = &ldquo;String1&rdquo; + &ldquo;String2&rdquo;;<br /><br /><br />9 字符串搜索<br />1: &lt;script language=&rdquo;JavaScript&rdquo;&gt;<br />2: &lt;!--<br />3: var myVariable = &ldquo;Hello there&rdquo;;<br />4: var therePlace = myVariable.search(&ldquo;there&rdquo;);<br />5: document.write(therePlace);<br />6: // --&gt;<br />7: &lt;/script&gt;<br /><br /><br />10 字符串替换<br />1: thisVar.replace(&ldquo;Monday&rdquo;,&rdquo;Friday&rdquo;);<br /><br /><br />11 格式化字串<br />1: &lt;script language=&rdquo;JavaScript&rdquo;&gt;<br />2: &lt;!--<br />3: var myVariable = &ldquo;Hello there&rdquo;;<br />4: document.write(myVariable.big() + &ldquo;&lt;br&gt;&rdquo;);<br />5: document.write(myVariable.blink() + &ldquo;&lt;br&gt;&rdquo;);<br />6: document.write(myVariable.bold() + &ldquo;&lt;br&gt;&rdquo;);<br />7: document.write(myVariable.fixed() + &ldquo;&lt;br&gt;&rdquo;);<br />8: document.write(myVariable.fontcolor(&ldquo;red&rdquo;) + &ldquo;&lt;br&gt;&rdquo;);<br />9: document.write(myVariable.fontsize(&ldquo;18pt&rdquo;) + &ldquo;&lt;br&gt;&rdquo;);<br />10: document.write(myVariable.italics() + &ldquo;&lt;br&gt;&rdquo;);<br />11: document.write(myVariable.small() + &ldquo;&lt;br&gt;&rdquo;);<br />12: document.write(myVariable.strike() + &ldquo;&lt;br&gt;&rdquo;);<br />13: document.write(myVariable.sub() + &ldquo;&lt;br&gt;&rdquo;);<br />14: document.write(myVariable.sup() + &ldquo;&lt;br&gt;&rdquo;);<br />15: document.write(myVariable.toLowerCase() + &ldquo;&lt;br&gt;&rdquo;);<br />16: document.write(myVariable.toUpperCase() + &ldquo;&lt;br&gt;&rdquo;);<br />17:<br />18: var firstString = &ldquo;My String&rdquo;;<br />19: var finalString = firstString.bold().toLowerCase().fontcolor(&ldquo;red&rdquo;);<br />20: // --&gt;<br />21: &lt;/script&gt;<br /><br /><br />12 创建数组<br />1: &lt;script language=&rdquo;JavaScript&rdquo;&gt;<br />2: &lt;!--<br />3: var myArray = new Array(5);<br />4: myArray[0] = &ldquo;First Entry&rdquo;;<br />5: myArray[1] = &ldquo;Second Entry&rdquo;;<br />6: myArray[2] = &ldquo;Third Entry&rdquo;;<br />7: myArray[3] = &ldquo;Fourth Entry&rdquo;;<br />8: myArray[4] = &ldquo;Fifth Entry&rdquo;;<br />9: var anotherArray = new Array(&ldquo;First Entry&rdquo;,&rdquo;Second Entry&rdquo;,&rdquo;Third Entry&rdquo;,&rdquo;Fourth Entry&rdquo;,&rdquo;Fifth Entry&rdquo;);<br />10: // --&gt;<br />11: &lt;/script&gt;<br /><br /><br />13 数组排序<br />1: &lt;script language=&rdquo;JavaScript&rdquo;&gt;<br />2: &lt;!--<br />3: var myArray = new Array(5);<br />4: myArray[0] = &ldquo;z&rdquo;;<br />5: myArray[1] = &ldquo;c&rdquo;;<br />6: myArray[2] = &ldquo;d&rdquo;;<br />7: myArray[3] = &ldquo;a&rdquo;;<br />8: myArray[4] = &ldquo;q&rdquo;;<br />9: document.write(myArray.sort());<br />10: // --&gt;<br />11: &lt;/script&gt;<br /><br /><br />14 分割字符串<br />1: &lt;script language=&rdquo;JavaScript&rdquo;&gt;<br />2: &lt;!--<br />3: var myVariable = &ldquo;a,b,c,d&rdquo;;<br />4: var stringArray = myVariable.split(&ldquo;,&rdquo;);<br />5: document.write(stringArray[0]);<br />6: document.write(stringArray[1]);<br />7: document.write(stringArray[2]);<br />8: document.write(stringArray[3]);<br />9: // --&gt;<br />10: &lt;/script&gt;<br /><br /><br />15 弹出警告信息<br />1: &lt;script language=&rdquo;JavaScript&rdquo;&gt;<br />2: &lt;!--<br />3: window.alert(&ldquo;Hello&rdquo;);<br />4: // --&gt;<br />5: &lt;/script&gt;<br /><br /><br />16 弹出确认框<br />1: &lt;script language=&rdquo;JavaScript&rdquo;&gt;<br />2: &lt;!--<br />3: var result = window.confirm(&ldquo;Click OK to continue&rdquo;);<br />4: // --&gt;<br />5: &lt;/script&gt;<br /><br /><br />17 定义函数<br />1: &lt;script language=&rdquo;JavaScript&rdquo;&gt;<br />2: &lt;!--<br />3: function multiple(number1,number2) {<br />4: var result = number1 * number2;<br />5: return result;<br />6: }<br />7: // --&gt;<br />8: &lt;/script&gt;<br /><br /><br />18 调用JS函数<br />1: &lt;a href=&rdquo;#&rdquo; onClick=&rdquo;functionName()&rdquo;&gt;Link text&lt;/a&gt;<br />2: &lt;a href=&rdquo;javascript:functionName()&rdquo;&gt;Link text&lt;/a&gt;<br /><br /><br />19 在页面加载完成后执行函数<br />1: &lt;body onLoad=&rdquo;functionName();&rdquo;&gt;<br />2: Body of the page<br />3: &lt;/body&gt;<br /><br /><br />20 条件判断<br />1: &lt;script&gt;<br />2: &lt;!--<br />3: var userChoice = window.confirm(&ldquo;Choose OK or Cancel&rdquo;);<br />4: var result = (userChoice == true) ? &ldquo;OK&rdquo; : &ldquo;Cancel&rdquo;;<br />5: document.write(result);<br />6: // --&gt;<br />7: &lt;/script&gt;<br /><br /><br />21 指定次数循环<br />1: &lt;script&gt;<br />2: &lt;!--<br />3: var myArray = new Array(3);<br />4: myArray[0] = &ldquo;Item 0&rdquo;;<br />5: myArray[1] = &ldquo;Item 1&rdquo;;<br />6: myArray[2] = &ldquo;Item 2&rdquo;;<br />7: for (i = 0; i &lt; myArray.length; i++) {<br />8: document.write(myArray<em> + &ldquo;&lt;br&gt;&rdquo;);<br />9: }<br />10: // --&gt;<br />11: &lt;/script&gt;<br /><br /><br />22 设定将来执行<br />1: &lt;script&gt;<br />2: &lt;!--<br />3: function hello() {<br />4: window.alert(&ldquo;Hello&rdquo;);<br />5: }<br />6: window.setTimeout(&ldquo;hello()&rdquo;,5000);<br />7: // --&gt;<br />8: &lt;/script&gt;<br /><br /><br />23 定时执行函数<br />1: &lt;script&gt;<br />2: &lt;!--<br />3: function hello() {<br />4: window.alert(&ldquo;Hello&rdquo;);<br />5: window.setTimeout(&ldquo;hello()&rdquo;,5000);<br />6: }<br />7: window.setTimeout(&ldquo;hello()&rdquo;,5000);<br />8: // --&gt;<br />9: &lt;/script&gt;<br /><br /><br />24 取消定时执行<br />1: &lt;script&gt;<br />2: &lt;!--<br />3: function hello() {<br />4: window.alert(&ldquo;Hello&rdquo;);<br />5: }<br />6: var myTimeout = window.setTimeout(&ldquo;hello()&rdquo;,5000);<br />7: window.clearTimeout(myTimeout);<br />8: // --&gt;<br />9: &lt;/script&gt;<br /><br /><br />25 在页面卸载时候执行函数<br />1: &lt;body onUnload=&rdquo;functionName();&rdquo;&gt;<br />2: Body of the page<br />3: &lt;/body&gt; </em></span><br />]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/76651.html</guid>
<subject>JavaScript 相关文章</subject>
<author>longkuangxiao</author>
<category>JavaScript 相关文章</category>
<pubDate>Fri, 24 Mar 2006 09:20:04 CST </pubDate>
</item>

<item>
<title>解析Java的多线程机制</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/76545.html</link>
<description>
<![CDATA[一、进程与应用程序的区别 <br />　　 <br />　　进程（Process）是最初定义在Unix等多用户、多任务操作系统环境下用于表示应用程序在内存环境中基本执行单元的概念。以Unix操作系统为例，进程是Unix操作系统环境中的基本成分、是系统资源分配的基本单位。Unix操作系统中完成的几乎所有用户管理和资源分配等工作都是通过操作系统对应用程序进程的控制来实现的。 <br />　　 <br />　　C、C++、Java等语言编写的源程序经相应的编译器编译成可执行文件后，提交给计算机处理器运行。这时，处在可执行状态中的应用程序称为进程。从用户角度来看，进程是应用程序的一个执行过程。从操作系统核心角度来看，进程代表的是操作系统分配的内存、CPU时间片等资源的基本单位，是为正在运行的程序提供的运行环境。进程与应用程序的区别在于应用程序作为一个静态文件存储在计算机系统的硬盘等存储空间中，而进程则是处于动态条件下由操作系统维护的系统资源管理实体。多任务环境下应用程序进程的主要特点包括： <br />　　 <br />　　●进程在执行过程中有内存单元的初始入口点，并且进程存活过程中始终拥有独立的内存地址空间； <br />　　 <br />　　●进程的生存期状态包括创建、就绪、运行、阻塞和死亡等类型； <br />　　 <br />　　●从应用程序进程在执行过程中向CPU发出的运行指令形式不同，可以将进程的状态分为用户态和核心态。处于用户态下的进程执行的是应用程序指令、处于核心态下的应用程序进程执行的是操作系统指令。 <br />　　 <br />　　在Unix操作系统启动过程中，系统自动创建swapper、init等系统进程，用于管理内存资源以及对用户进程进行调度等。在Unix环境下无论是由操作系统创建的进程还要由应用程序执行创建的进程，均拥有唯一的进程标识（PID）。 <br /><br />二、进程与Java线程的区别 <br />　　 <br />　　 <br />　　应用程序在执行过程中存在一个内存空间的初始入口点地址、一个程序执行过程中的代码执行序列以及用于标识进程结束的内存出口点地址，在进程执行过程中的每一时间点均有唯一的处理器指令与内存单元地址相对应。 <br />　　 <br />　　Java语言中定义的线程（Thread）同样包括一个内存入口点地址、一个出口点地址以及能够顺序执行的代码序列。但是进程与线程的重要区别在于线程不能够单独执行，它必须运行在处于活动状态的应用程序进程中，因此可以定义线程是程序内部的具有并发性的顺序代码流。 <br />　　 <br />　　Unix操作系统和Microsoft Windows操作系统支持多用户、多进程的并发执行，而Java语言支持应用程序进程内部的多个执行线程的并发执行。多线程的意义在于一个应用程序的多个逻辑单元可以并发地执行。但是多线程并不意味着多个用户进程在执行，操作系统也不把每个线程作为独立的进程来分配独立的系统资源。进程可以创建其子进程，子进程与父进程拥有不同的可执行代码和数据内存空间。而在用于代表应用程序的进程中多个线程共享数据内存空间，但保持每个线程拥有独立的执行堆栈和程序执行上下文（Context）。 <br />　　 <br />　　基于上述区别，线程也可以称为轻型进程 (Light Weight Process，LWP)。不同线程间允许任务协作和数据交换，使得在计算机系统资源消耗等方面非常廉价。 <br />　　 <br />　　线程需要操作系统的支持，不是所有类型的计算机都支持多线程应用程序。Java程序设计语言将线程支持与语言运行环境结合在一起，提供了多任务并发执行的能力。这就好比一个人在处理家务的过程中，将衣服放到洗衣机中自动洗涤后将大米放在电饭锅里，然后开始做菜。等菜做好了，饭熟了同时衣服也洗好了。 <br />　　 <br />　　需要注意的是：在应用程序中使用多线程不会增加 CPU 的数据处理能力。只有在多CPU 的计算机或者在网络计算体系结构下，将Java程序划分为多个并发执行线程后，同时启动多个线程运行，使不同的线程运行在基于不同处理器的Java虚拟机中，才能提高应用程序的执行效率。<br /><br />另外，如果应用程序必须等待网络连接或数据库连接等数据吞吐速度相对较慢的资源时，多线程应用程序是非常有利的。基于Internet的应用程序有必要是多线程类型的，例如，当开发要支持大量客户机的服务器端应用程序时，可以将应用程序创建成多线程形式来响应客户端的连接请求，使每个连接用户独占一个客户端连接线程。这样，用户感觉服务器只为连接用户自己服务，从而缩短了服务器的客户端响应时间。 <br />　　 <br />　　 <br />三、Java语言的多线程程序设计方法 <br />　　 <br />　　 <br />　　利用Java语言实现多线程应用程序的方法很简单。根据多线程应用程序继承或实现对象的不同可以采用两种方式：一种是应用程序的并发运行对象直接继承Java的线程类Thread；另外一种方式是定义并发执行对象实现Runnable接口。 <br />　　 <br />　　继承Thread类的多线程程序设计方法 <br />　　 <br />　　Thread 类是JDK中定义的用于控制线程对象的类，在该类中封装了用于进行线程控制的方法。见下面的示例代码： <br />　　 <br />　　[code]//Consumer.java <br />　　import java.util.*; <br />　　class Consumer extends Thread <br />　　{ <br />　　 int nTime; <br />　　 String strConsumer; <br />　　 public Consumer(int nTime, String strConsumer) <br />　　 { <br />　　 this.nTime = nTime; <br />　　 this.strConsumer = strConsumer; <br />　　 } <br />　　 public void run() <br />　　 { <br />　　while(true) <br />　　{ <br />　　 try <br />　　{ <br />　　 System.out.println(&quot;Consumer name:&quot;+strConsumer+&quot;\n&quot;); <br />　　 Thread.sleep(nTime); <br />　　 } <br />　　catch(Exception e) <br />　　{ <br />　　 e.printStackTrace(); <br />　　 } <br />　　} <br />　　 } <br />　　static public void main(String args[]) <br />　　{ <br />　　 Consumer aConsumer = new Consumer (1000, &quot;aConsumer&quot;); <br />　　 aConsumer.start(); <br />　　 Consumer bConsumer = new Consumer (2000, &quot;bConsumer&quot;); <br />　　 bConsumer.start(); <br />　　 Consumer cConsumer = new Consumer (3000, &quot;cConsumer &quot;); <br />　　 cConsumer.start(); <br />　　} <br />　　} [/code]<br />　　 <br />　　 <br />　　 <br />　　 <br />　　从上面的程序代码可以看出：多线程执行地下Consumer继承Java语言中的线程类Thread并且在main方法中创建了三个Consumer对象的实例。当调用对象实例的start方法时，自动调用Consumer类中定义的run方法启动对象线程运行。线程运行的结果是每间隔nTime时间打印出对象实例中的字符串成员变量strConsumer的内容。 <br />　　 <br />　　可以总结出继承Thread类的多线程程序设计方法是使应用程序类继承Thread类并且在该类的run方法中实现并发性处理过程。 <br />　　 <br />　　实现Runnable接口的多线程程序设计方法 <br />　　 <br />　　Java语言中提供的另外一种实现多线程应用程序的方法是多线程对象实现Runnable接口并且在该类中定义用于启动线程的run方法。这种定义方式的好处在于多线程应用对象可以继承其它对象而不是必须继承Thread类，从而能够增加类定义的逻辑性。 <br />　　 <br />　　实现Runnable接口的多线程应用程序框架代码如下所示： <br />　　 <br />　　//Consumer.java <br />　　import java.util.*; <br />　　class Consumer implements Runnable <br />　　{ <br />　　 &hellip; &hellip; <br />　　public Consumer(int nTime, String strConsumer){&hellip; &hellip;} <br />　　public void run(){&hellip; &hellip;} <br />　　static public void main(String args[]) <br />　　{ <br />　　Thread aConsumer = new Thread(new Consumer(1000, &quot;aConsumer&quot;)); <br />　　aConsumer.start(); <br />　　//其它对象实例的运行线程 <br />　　 //&hellip; &hellip; <br />　　 } <br />　　} <br />　　 <br />　　从上述代码可以看出：该类实现了Runnable接口并且在该类中定义了run方法。这种多线程应用程序的实现方式与继承Thread类的多线程应用程序的重要区别在于启动多线程对象的方法设计方法不同。在上述代码中，通过创建Thread对象实例并且将应用对象作为创建Thread类实例的参数。 <br /><br />四、线程间的同步 <br />　　 <br />　　Java应用程序的多个线程共享同一进程的数据资源，多个用户线程在并发运行过程中可能同时访问具有敏感性的内容。在Java中定义了线程同步的概念，实现对共享资源的一致性维护。下面以笔者最近开发的移动通信计费系统中线程间同步控制方法，说明Java语言中多线程同步方式的实现过程。 <br />　　 <br />　　在没有多线程同步控制策略条件下的客户账户类定义框架代码如下所示： <br />　　 <br />　　public class RegisterAccount <br />　　{ <br />　　float fBalance; <br />　　//客户缴费方法 <br />　　public void deposit(float fFees){ fBalance += fFees; } <br />　　//通话计费方法 <br />　　public void withdraw(float fFees){ fBalance -= fFees; } <br />　　&hellip; &hellip; <br />　　} <br /><br />　　 <br />　　 <br />　　 <br />　　 <br />　　读者也许会认为：上述程序代码完全能够满足计费系统实际的需要。确实，在单线程环境下该程序确实是可靠的。但是，多进程并发运行的情况是怎样的呢？假设发生这种情况：客户在客户服务中心进行缴费的同时正在利用移动通信设备仅此通话，客户通话结束时计费系统启动计费进程，而同时服务中心的工作人员也提交缴费进程运行。读者可以看到如果发生这种情况，对客户账户的处理是不严肃的。 <br />　　 <br />　　如何解决这种问题呢？很简单，在RegisterAccount类方法定义中加上用于标识同步方法的关键字synchronized。这样，在同步方法执行过程中该方法涉及的共享资源（在上述代码中为fBalance成员变量）将被加上共享锁，以确保在方法运行期间只有该方法能够对共享资源进行访问，直到该方法的线程运行结束打开共享锁，其它线程才能够访问这些共享资源。在共享锁没有打开的时候其它访问共享资源的线程处于阻塞状态。 <br />　　 <br />　　进行线程同步策略控制后的RegisterAccount类定义如下面代码所示： <br />　　 <br />　　public class RegisterAccount <br />　　{ <br />　　float fBalance; <br />　　public synchronized void deposit(float fFees){ fBalance += fFees; } <br />　　public synchronized void withdraw(float fFees){ fBalance -= fFees; } <br />　　&hellip; &hellip; <br />　　} <br /><br />　　 <br />　　从经过线程同步机制定义后的代码形式可以看出：在对共享资源进行访问的方法访问属性关键字（public）后附加同步定义关键字synchronized，使得同步方法在对共享资源访问的时候，为这些敏感资源附加共享锁来控制方法执行期间的资源独占性，实现了应用系统数据资源的一致性管理和维护。 <br /><br /><br />五、 Java线程的管理 <br />　　 <br />　　 <br />　　线程的状态控制 <br />　　 <br />　　在这里需要明确的是：无论采用继承Thread类还是实现Runnable接口来实现应用程序的多线程能力，都需要在该类中定义用于完成实际功能的run方法，这个run方法称为线程体（Thread Body）。按照线程体在计算机系统内存中的状态不同，可以将线程分为创建、就绪、运行、睡眠、挂起和死亡等类型。这些线程状态类型下线程的特征为： <br />　　 <br />　　创建状态：当利用new关键字创建线程对象实例后，它仅仅作为一个对象实例存在，JVM没有为其分配CPU时间片等线程运行资源； <br />　　 <br />　　就绪状态：在处于创建状态的线程中调用start方法将线程的状态转换为就绪状态。这时，线程已经得到除CPU时间之外的其它系统资源，只等JVM的线程调度器按照线程的优先级对该线程进行调度，从而使该线程拥有能够获得CPU时间片的机会。 <br />　　 <br />　　睡眠状态：在线程运行过程中可以调用sleep方法并在方法参数中指定线程的睡眠时间将线程状态转换为睡眠状态。这时，该线程在不释放占用资源的情况下停止运行指定的睡眠时间。时间到达后，线程重新由JVM线程调度器进行调度和管理。 <br />　　 <br />　　挂起状态：可以通过调用suspend方法将线程的状态转换为挂起状态。这时，线程将释放占用的所有资源，由JVM调度转入临时存储空间，直至应用程序调用resume方法恢复线程运行。 <br />　　 <br />　　死亡状态：当线程体运行结束或者调用线程对象的stop方法后线程将终止运行，由JVM收回线程占用的资源。 <br />　　 <br />　　在Java线程类中分别定义了相应的方法，用于在应用程序中对线程状态进行控制和管理。 <br />　　 <br />　　线程的调度 <br />　　 <br />　　线程调用的意义在于JVM应对运行的多个线程进行系统级的协调，以避免多个线程争用有限资源而导致应用系统死机或者崩溃。 <br />　　 <br />　　为了线程对于操作系统和用户的重要性区分开，Java定义了线程的优先级策略。Java将线程的优先级分为10个等级，分别用1-10之间的数字表示。数字越大表明线程的级别越高。相应地，在Thread类中定义了表示线程最低、最高和普通优先级的成员变量MIN_PRIORITY、MAX_PRIORITY和NORMAL_PRIORITY，代表的优先级等级分别为1、10和5。当一个线程对象被创建时，其默认的线程优先级是5。 <br />　　 <br />　　为了控制线程的运行策略，Java定义了线程调度器来监控系统中处于就绪状态的所有线程。线程调度器按照线程的优先级决定那个线程投入处理器运行。在多个线程处于就绪状态的条件下，具有高优先级的线程会在低优先级线程之前得到执行。线程调度器同样采用&quot;抢占式&quot;策略来调度线程执行，即当前线程执行过程中有较高优先级的线程进入就绪状态，则高优先级的线程立即被调度执行。具有相同优先级的所有线程采用轮转的方式来共同分配CPU时间片。 <br />　　 <br />　　在应用程序中设置线程优先级的方法很简单，在创建线程对象之后可以调用线程对象的setPriority方法改变该线程的运行优先级，同样可以调用getPriority方法获取当前线程的优先级。 <br />　　 <br />　　在Java中比较特殊的线程是被称为守护（Daemon）线程的低级别线程。这个线程具有最低的优先级，用于为系统中的其它对象和线程提供服务。将一个用户线程设置为守护线程的方式是在线程对象创建之前调用线程对象的setDaemon方法。典型的守护线程例子是JVM中的系统资源自动回收线程，它始终在低级别的状态中运行，用于实时监控和管理系统中的可回收资源。 <br />　　 <br />　　线程分组管理 <br />　　 <br />　　Java定义了在多线程运行系统中的线程组（ThreadGroup）对象，用于实现按照特定功能对线程进行集中式分组管理。用户创建的每个线程均属于某线程组，这个线程组可以在线程创建时指定，也可以不指定线程组以使该线程处于默认的线程组之中。但是，一旦线程加入某线程组，该线程就一直存在于该线程组中直至线程死亡，不能在中途改变线程所属的线程组。 <br />　　 <br />　　当Java的Application应用程序运行时，JVM创建名称为main的线程组。除非单独指定，在该应用程序中创建的线程均属于main线程组。在main线程组中可以创建其它名称的线程组并将其它线程加入到该线程组中，依此类推，构成线程和线程组之间的树型管理和继承关系。 <br />　　 <br />　　与线程类似，可以针对线程组对象进行线程组的调度、状态管理以及优先级设置等。在对线程组进行管理过程中，加入到某线程组中的所有线程均被看作统一的对象。 <br /><br />六、小结：<br />本文针对Java平台中线程的性质和应用程序的多线程策略进行了分析和讲解。 <br />　　 <br />　　与其它操作系统环境不同，Java运行环境中的线程类似于多用户、多任务操作系统环境下的进程，但在进程和线程的运行及创建方式等方面，进程与Java线程具有明显区别。 <br />　　 <br />　　Unix操作系统环境下，应用程序可以利用fork函数创建子进程，但子进程与该应用程序进程拥有独立的地址空间、系统资源和代码执行单元，并且进程的调度是由操作系统来完成的，使得在应用进程之间进行通信和线程协调相对复杂。而Java应用程序中的多线程则是共享同一应用系统资源的多个并行代码执行体，线程之间的通信和协调方法相对简单。 <br />　　 <br />　　可以说：Java语言对应用程序多线程能力的支持增强了Java作为网络程序设计语言的优势，为实现分布式应用系统中多客户端的并发访问以及提高服务器的响应效率奠定坚实基础。<br />]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/76545.html</guid>
<subject>J2SE 相关文章</subject>
<author>longkuangxiao</author>
<category>J2SE 相关文章</category>
<pubDate>Fri, 24 Mar 2006 00:15:14 CST </pubDate>
</item>

<item>
<title>Java 线程/内存模型的缺陷和增强</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/76544.html</link>
<description>
<![CDATA[<p>本文是由JR主持写作的《J2SE进阶》一书的部分章节整理而成，《J2SE进阶》正在写作、完善阶段。您阅读后，有任何建议、批评，请和我联系，或在这儿留言。《J2SE进阶》写作项目组感谢您阅读本文。</p>
<p><br />Java在语言层次上实现了对线程的支持。它提供了Thread/Runnable/ThreadGroup等一系列封装的类和接口，让程序员可以高效的开发Java多线程应用。为了实现同步，Java提供了synchronize关键字以及object的wait()/notify()机制，可是在简单易用的背后，应藏着更为复杂的玄机，很多问题就是由此而起。<br /><br /><br />一、Java内存模型<br /><br />在了解Java的同步秘密之前，先来看看JMM(Java Memory Model)。<br />Java被设计为跨平台的语言，在内存管理上，显然也要有一个统一的模型。而且Java语言最大的特点就是废除了指针，把程序员从痛苦中解脱出来，不用再考虑内存使用和管理方面的问题。<br />可惜世事总不尽如人意，虽然JMM设计上方便了程序员，但是它增加了虚拟机的复杂程度，而且还导致某些编程技巧在Java语言中失效。<br /><br />JMM主要是为了规定了线程和内存之间的一些关系。对Java程序员来说只需负责用synchronized同步关键字，其它诸如与线程/内存之间进行数据交换/同步等繁琐工作均由虚拟机负责完成。如图1所示：根据JMM的设计，系统存在一个主内存(Main Memory)，Java中所有变量都储存在主存中，对于所有线程都是共享的。每条线程都有自己的工作内存(Working Memory)，工作内存中保存的是主存中某些变量的拷贝，线程对所有变量的操作都是在工作内存中进行，线程之间无法相互直接访问，变量传递均需要通过主存完成。<br /><img alt="" src="http://www.csdn.net/Develop/ArticleImages/24/24319/CSDN_Dev_Image_2004-2-122227460.gif" /><br />图1 Java内存模型示例图<br /><br />线程若要对某变量进行操作，必须经过一系列步骤：首先从主存复制/刷新数据到工作内存，然后执行代码，进行引用/赋值操作，最后把变量内容写回Main Memory。Java语言规范(JLS)中对线程和主存互操作定义了6个行为，分别为load，save，read，write，assign和use，这些操作行为具有原子性，且相互依赖，有明确的调用先后顺序。具体的描述请参见JLS第17章。<br /><br />我们在前面的章节介绍了synchronized的作用，现在，从JMM的角度来重新审视synchronized关键字。<br />假设某条线程执行一个synchronized代码段，其间对某变量进行操作，JVM会依次执行如下动作：<br />(1) 获取同步对象monitor (lock)<br />(2) 从主存复制变量到当前工作内存 (read and load)<br />(3) 执行代码，改变共享变量值 (use and assign)<br />(4) 用工作内存数据刷新主存相关内容 (store and write)<br />(5) 释放同步对象锁 (unlock)<br />可见，synchronized的另外一个作用是保证主存内容和线程的工作内存中的数据的一致性。如果没有使用synchronized关键字，JVM不保证第2步和第4步会严格按照上述次序立即执行。因为根据JLS中的规定，线程的工作内存和主存之间的数据交换是松耦合的，什么时候需要刷新工作内存或者更新主内存内容，可以由具体的虚拟机实现自行决定。如果多个线程同时执行一段未经synchronized保护的代码段，很有可能某条线程已经改动了变量的值，但是其他线程却无法看到这个改动，依然在旧的变量值上进行运算，最终导致不可预料的运算结果。<br /><br /><br />二、DCL失效<br /><br />这一节我们要讨论的是一个让Java丢脸的话题：DCL失效。在开始讨论之前，先介绍一下LazyLoad，这种技巧很常用，就是指一个类包含某个成员变量，在类初始化的时候并不立即为该变量初始化一个实例，而是等到真正要使用到该变量的时候才初始化之。<br />例如下面的代码：<br />代码1<br /><br />class Foo { private Resource res = null; public Resource getResource() { if (res == null) res = new Resource(); return res; }}<br /><br />由于LazyLoad可以有效的减少系统资源消耗，提高程序整体的性能，所以被广泛的使用，连Java的缺省类加载器也采用这种方法来加载Java类。<br />在单线程环境下，一切都相安无事，但如果把上面的代码放到多线程环境下运行，那么就可能会出现问题。假设有2条线程，同时执行到了if(res == null)，那么很有可能res被初始化2次，为了避免这样的Race Condition，得用synchronized关键字把上面的方法同步起来。代码如下：<br />代码2<br /><br />Class Foo { Private Resource res = null; Public synchronized Resource getResource() { If (res == null) res = new Resource(); return res; }}<br /><br />现在Race Condition解决了，一切都很好。<br /><br />N天过后，好学的你偶然看了一本Refactoring的魔书，深深为之打动，准备自己尝试这重构一些以前写过的程序，于是找到了上面这段代码。你已经不再是以前的Java菜鸟，深知synchronized过的方法在速度上要比未同步的方法慢上100倍，同时你也发现，只有第一次调用该方法的时候才需要同步，而一旦res初始化完成，同步完全没必要。所以你很快就把代码重构成了下面的样子：<br />代码3<br /><br />Class Foo {Private Resource res = null; Public Resource getResource() { If (res == null){ synchronized(this){ if(res == null){ res = new Resource();}} } return res; }}<br /><br />这种看起来很完美的优化技巧就是Double-Checked Locking。但是很遗憾，根据Java的语言规范，上面的代码是不可靠的。<br /><br />造成DCL失效的原因之一是编译器的优化会调整代码的次序。只要是在单个线程情况下执行结果是正确的，就可以认为编译器这样的&ldquo;自作主张的调整代码次序&rdquo;的行为是合法的。JLS在某些方面的规定比较自由，就是为了让JVM有更多余地进行代码优化以提高执行效率。而现在的CPU大多使用超流水线技术来加快代码执行速度，针对这样的CPU，编译器采取的代码优化的方法之一就是在调整某些代码的次序，尽可能保证在程序执行的时候不要让CPU的指令流水线断流，从而提高程序的执行速度。正是这样的代码调整会导致DCL的失效。为了进一步证明这个问题，引用一下《DCL Broken Declaration》文章中的例子：<br />设一行Java代码：<br /><br />Objects[i].reference = new Object();<br /><br />经过Symantec JIT编译器编译过以后，最终会变成如下汇编码在机器中执行：<br /><br />0206106A mov eax,0F97E78h0206106F call 01F6B210 ;为Object申请内存空间 ; 返回值放在eax中02061074 mov dword ptr [ebp],eax ; EBP 中是objects[i].reference的地址 ; 将返回的空间地址放入其中 ; 此时Object尚未初始化02061077 mov ecx,dword ptr [eax] ; dereference eax所指向的内容 ; 获得新创建对象的起始地址02061079 mov dword ptr [ecx],100h ; 下面4行是内联的构造函数0206107F mov dword ptr [ecx+4],200h 02061086 mov dword ptr [ecx+8],400h0206108D mov dword ptr [ecx+0Ch],0F84030h<br /><br />可见，Object构造函数尚未调用，但是已经能够通过objects[i].reference获得Object对象实例的引用。<br />如果把代码放到多线程环境下运行，某线程在执行到该行代码的时候JVM或者操作系统进行了一次线程切换，其他线程显然会发现msg对象已经不为空，导致Lazy load的判断语句if(objects[i].reference == null)不成立。线程认为对象已经建立成功，随之可能会使用对象的成员变量或者调用该对象实例的方法，最终导致不可预测的错误。<br /><br />原因之二是在共享内存的SMP机上，每个CPU有自己的Cache和寄存器，共享同一个系统内存。所以CPU可能会动态调整指令的执行次序，以更好的进行并行运算并且把运算结果与主内存同步。这样的代码次序调整也可能导致DCL失效。回想一下前面对Java内存模型的介绍，我们这里可以把Main Memory看作系统的物理内存，把Thread Working Memory认为是CPU内部的Cache和寄存器，没有synchronized的保护，Cache和寄存器的内容就不会及时和主内存的内容同步，从而导致一条线程无法看到另一条线程对一些变量的改动。<br />结合代码3来举例说明，假设Resource类的实现如下：<br /><br />Class Resource{ Object obj;}<br /><br />即Resource类有一个obj成员变量引用了Object的一个实例。假设2条线程在运行，其状态用如下简化图表示：<br /><img alt="" src="http://www.csdn.net/Develop/ArticleImages/24/24319/CSDN_Dev_Image_2004-2-122227462.gif" /><br />图2 <br />现在Thread-1构造了Resource实例，初始化过程中改动了obj的一些内容。退出同步代码段后，因为采取了同步机制，Thread-1所做的改动都会反映到主存中。接下来Thread-2获得了新的Resource实例变量res，由于没有使用synchronized保护所以Thread-2不会进行刷新工作内存的操作。假如之前Thread-2的工作内存中已经有了obj实例的一份拷贝，那么Thread-2在对obj执行use操作的时候就不会去执行load操作,这样一来就无法看到Thread-1对obj的改变，这显然会导致错误的运算结果。此外，Thread-1在退出同步代码段的时刻对ref和obj执行的写入主存的操作次序也是不确定的，所以即使Thread-2对obj执行了load操作，也有可能只读到obj的初试状态的数据。(注：这里的load/use均指JMM定义的操作)<br /><br />有很多人不死心，试图想出了很多精妙的办法来解决这个问题，但最终都失败了。事实上，无论是目前的JMM还是已经作为JSR提交的JMM模型的增强，DCL都不能正常使用。在William Pugh的论文《Fixing the Java Memory Model》中详细的探讨了JMM的一些硬伤，更尝试给出一个新的内存模型，有兴趣深入研究的读者可以参见文后的参考资料。<br /><br />如果你设计的对象在程序中只有一个实例，即singleton的，有一种可行的解决办法来实现其LazyLoad：就是利用类加载器的LazyLoad特性。代码如下：<br /><br />Class ResSingleton {public static Resource res = new Resource();}<br /><br />这里ResSingleton只有一个静态成员变量。当第一次使用ResSingleton.res的时候，JVM才会初始化一个Resource实例，并且JVM会保证初始化的结果及时写入主存，能让其他线程看到，这样就成功的实现了LazyLoad。<br />除了这个办法以外，还可以使用ThreadLocal来实现DCL的方法，但是由于ThreadLocal的实现效率比较低，所以这种解决办法会有较大的性能损失，有兴趣的读者可以参考文后的参考资料。<br /><br />最后要说明的是，对于DCL是否有效，个人认为更多的是一种带有学究气的推断和讨论。而从纯理论的角度来看，存取任何可能共享的变量（对象引用）都需要同步保护，否则都有可能出错，但是处处用synchronized又会增加死锁的发生几率，苦命的程序员怎么来解决这个矛盾呢？事实上，在很多Java开源项目（比如Ofbiz/Jive等）的代码中都能找到使用DCL的证据，我在具体的实践中也没有碰到过因DCL而发生的程序异常。个人的偏好是：不妨先大胆使用DCL，等出现问题再用synchronized逐步排除之。也许有人偏于保守，认为稳定压倒一切，那就不妨先用synchronized同步起来，我想这是一个见仁见智的问题，而且得针对具体的项目具体分析后才能决定。还有一个办法就是写一个测试案例来测试一下系统是否存在DCL现象，附带的光盘中提供了这样一个例子，感兴趣的读者可以自行编译测试。不管结果怎样，这样的讨论有助于我们更好的认识JMM，养成用多线程的思路去分析问题的习惯，提高我们的程序设计能力。<br /><br /><br />三、Java线程同步增强包<br /><br />相信你已经了解了Java用于同步的3板斧：synchronized/wait/notify，它们的确简单而有效。但是在某些情况下，我们需要更加复杂的同步工具。有些简单的同步工具类，诸如ThreadBarrier，Semaphore，ReadWriteLock等，可以自己编程实现。现在要介绍的是牛人Doug Lea的Concurrent包。这个包专门为实现Java高级并行程序所开发，可以满足我们绝大部分的要求。更令人兴奋的是，这个包公开源代码，可自由下载。且在JDK1.5中该包将作为SDK一部分提供给Java开发人员。<br /><br />Concurrent Package提供了一系列基本的操作接口，包括sync，channel，executor,barrier,callable等。这里将对前三种接口及其部分派生类进行简单的介绍。<br /><br />sync接口：专门负责同步操作，用于替代Java提供的synchronized关键字，以实现更加灵活的代码同步。其类关系图如下：<br /><img alt="" src="http://www.csdn.net/Develop/ArticleImages/24/24319/CSDN_Dev_Image_2004-2-122227464.jpg" /><br />图3 Concurrent包Sync接口类关系图<br />Semaphore：和前面介绍的代码类似，可用于pool类实现资源管理限制。提供了acquire()方法允许在设定时间内尝试锁定信号量，若超时则返回false。<br /><br />Mutex：和Java的synchronized类似，与之不同的是，synchronized的同步段只能限制在一个方法内，而Mutex对象可以作为参数在方法间传递，所以可以把同步代码范围扩大到跨方法甚至跨对象。<br /><br />NullSync：一个比较奇怪的东西，其方法的内部实现都是空的，可能是作者认为如果你在实际中发现某段代码根本可以不用同步，但是又不想过多改动这段代码，那么就可以用NullSync来替代原来的Sync实例。此外，由于NullSync的方法都是synchronized，所以还是保留了&ldquo;内存壁垒&rdquo;的特性。<br /><br />ObservableSync：把sync和observer模式结合起来，当sync的方法被调用时，把消息通知给订阅者，可用于同步性能调试。<br /><br />TimeoutSync：可以认为是一个adaptor，其构造函数如下：<br />public TimeoutSync(Sync sync, long timeout){&hellip;}<br />具体上锁的代码靠构造函数传入的sync实例来完成，其自身只负责监测上锁操作是否超时，可与SyncSet合用。<br /><br />Channel接口：代表一种具备同步控制能力的容器，你可以从中存放/读取对象。不同于JDK中的Collection接口，可以把Channel看作是连接对象构造者(Producer)和对象使用者(Consumer)之间的一根管道。如图所示：<br /><img alt="" src="http://www.csdn.net/Develop/ArticleImages/24/24319/CSDN_Dev_Image_2004-2-122227466.jpg" /><br />图4 Concurrent包Channel接口示意图<br /><br />通过和Sync接口配合，Channel提供了阻塞式的对象存取方法（put/take）以及可设置阻塞等待时间的offer/poll方法。实现Channel接口的类有LinkedQueue，BoundedLinkedQueue，BoundedBuffer，BoundedPriorityQueue，SynchronousChannel，Slot等。<br /><img alt="" src="http://www.csdn.net/Develop/ArticleImages/24/24319/CSDN_Dev_Image_2004-2-122227468.jpg" /><br />图5 Concurrent包Channel接口部分类关系图<br /><br />使用Channel我们可以很容易的编写具备消息队列功能的代码，示例如下：<br />代码4<br /><br />Package org.javaresearch.j2seimproved.thread;Import EDU.oswego.cs.dl.util.concurrent.*;public class TestChannel { final Channel msgQ = new LinkedQueue(); //log信息队列 public static void main(String[] args) { TestChannel tc = new TestChannel(); For(int i = 0;i &lt; 10;i ++){ Try{ tc.serve(); Thread.sleep(1000); }catch(InterruptedException ie){ } } } public void serve() throws InterruptedException { String status = doService();//把doService()返回状态放入Channel，后台logger线程自动读取之 msgQ.put(status); } private String doService() { // Do service here return &quot;service completed OK! &quot;; } public TestChannel() { // start background thread Runnable logger = new Runnable() { public void run() { try { for (; ; ) System.out.println(&quot;Logger: &quot; + msgQ.take()); } catch (InterruptedException ie) {} } }; new Thread(logger).start(); }}<br /><br />Excutor/ThreadFactory接口: 把相关的线程创建/回收/维护/调度等工作封装起来，而让调用者只专心于具体任务的编码工作（即实现Runnable接口），不必显式创建Thread类实例就能异步执行任务。<br />使用Executor还有一个好处，就是实现线程的&ldquo;轻量级&rdquo;使用。前面章节曾提到，即使我们实现了Runnable接口，要真正的创建线程，还是得通过new Thread()来完成，在这种情况下，Runnable对象(任务)和Thread对象(线程)是1对1的关系。如果任务多而简单，完全可以给每条线程配备一个任务队列，让Runnable对象(任务)和Executor对象变成n:1的关系。使用了Executor，我们可以把上面两种线程策略都封装到具体的Executor实现中，方便代码的实现和维护。<br />具体的实现有: PooledExecutor，ThreadedExecutor，QueuedExecutor，FJTaskRunnerGroup等<br />类关系图如下：<br /><img alt="" src="http://www.csdn.net/Develop/ArticleImages/24/24319/CSDN_Dev_Image_2004-2-1222274610.jpg" /><br />图6 Concurrent包Executor/ThreadFactory接口部分类关系图<br />下面给出一段代码，使用PooledExecutor实现一个简单的多线程服务器<br />代码5<br /><br />package org.javaresearch.j2seimproved.thread;import java.net.*;import EDU.oswego.cs.dl.util.concurrent.*;public class TestExecutor { public static void main(String[] args) { PooledExecutor pool = new PooledExecutor(new BoundedBuffer(10), 20); pool.createThreads(4); try { ServerSocket socket = new ServerSocket(9999); for (; ; ) { final Socket connection = socket.accept(); pool.execute(new Runnable() { public void run() { new Handler().process(connection); } }); } } catch (Exception e) {} // die } static class Handler { void process(Socket s){ } }}<br /></p>]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/76544.html</guid>
<subject>J2SE 相关文章</subject>
<author>longkuangxiao</author>
<category>J2SE 相关文章</category>
<pubDate>Fri, 24 Mar 2006 00:09:32 CST </pubDate>
</item>

<item>
<title>实战J2EE—开发购物网站（四）</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/76542.html</link>
<description>
<![CDATA[GO ON 继续进阶！！(本贴个人认为对初学者很有帮助，请大家认真看。因时间仓促，如有错误请指正)<br /><br />SQL*PLUS基础<br />在上一贴中，我们掌握了些基本的oracle操作，如创建、授权用户，创建数据库等。在OEM(Oracle Enterprise Manager)可视化的窗口环境中，虽然我们也可以很方便地做这些事，但是事实上，用SQL语言书写在开发上更有效率！！oracle提供的SQL*Plus就是个不错的工具，如果大家喜欢窗口的开发环境，用SQLPlus Worksheet也行！下面说点基本的西西！<br /><br />SQL(Structure Query Language)语言是结构化查询语言，是数据库的核心语言，是面向集合的描述性非过程化语言。<br />SQL语言共分为四大类：数据查询语言DQL,数据操纵语言DML，数据定义语言DDL,数据库控制语言DCL。<br /><br />1.数据查询语言DQL的基本结构是由select子句，from子句，where子句组成的查询块：<br />select &lt;字段名表&gt; from &lt;表或视图名&gt; where &lt;查询条件&gt;<br /><br />2.数据操纵语言DML完成在数据库中确定、修改、添加、删除某一数据值的任务(以下是部分常用DML语句):<br />insert 增加数据行到表<br />delete 从表中删除数据行<br />Update 更改表中数据<br /><br />3.数据定义语言DDL完成定义数据库的结构，包括数据库本身、数据表、目录、视图等数据库元素(以下是部分常用DDL语句)<br />create table 创建表<br />create index 创建索引<br />create view 创建视图<br />alter table 增加表列，重定义表列，更改存储分配<br />drop table 删除表<br />drop index 删除索引<br /><br />4.数据库控制语言DCL用来授予或回收访问数据库的某种特权，并控制数据库操纵事务发生的时间及效果，对数据库实行监视等。如：<br />grant 将权限或角色授予用户或其它角色<br />revoke 回收用户权限<br />roll 回滚，是当某个对话更改了数据库中的数据后，由于某种原因用户不想提交此更改时，oracle所采取的保护操作。这是一个把信息恢复到用户使update、insert、delete前最后提交的状态。<br />commit 提交。在完成数据库的插入，删除和修改操作时，只有当事务提交到数据库才算完成，有提交前只有操作数据库的本人才能看到，别人只有在最后提交完成才可以看到。<br /><br />接下来，我们在SQL*Plus中实战一下，为我们下面将要做的打好基础。<br />用system登陆到SQL*Plus后，我们做如下操作(这次没有截图，有详细的说明)<br />SQL&gt;create user maxuan identified by max; #创建口令为max的用户maxuan<br />SQL&gt;grant connect,resource to maxuan; #为用户maxuan授权<br />SQL&gt;conn maxuan/max; #以用户maxuan进行连接<br />L&gt;create table test(a number); #建立一个名为test的表，只有字段名为A的一列，数据类型为数字<br />SQL&gt;insert into test values(1); #插入一条记录<br />SQL&gt;select * from test; #查询记录，此时A列的第一行为1<br />SQL&gt;update test set a=2; #更改记录，此时A列的第一行已改为2<br />SQL&gt;commit; #提交<br />SQL&gt;delete from test; #删除test表中所有的记录，此时test表中没有记录<br />SQL&gt;roll; #回滚到提交前，此时再查询test表，A列第一行值又回复到2<br /><br />oracle的数据类型<br /><br />在数据库中创建数据表的时候，我们需要定义表中所有字段的类型，数据类型大致分为：character,numberic,date,lob和raw等，这些是最基本的数据类型。当然在oracle中也允许自定义数据类型！<br /><br />在oracle中提供的character数据类型:<br />char(<size></size>):固定长度字符串，最大长度为2000字节，如果不指定长充，缺省为1个字节长。<br />varchar2(<size></size>):可变长度的字符串，最大长度为4000字节，具体定义时指明最大长度，这咱类型可以放数字、字母以及ASCII码字符集(或者EBCDIC等数据库系统接受的字符集标准)中的所有符号。如果数据长度没有达到最大值，oracle会根据数据大小自动调节字段长度。是最长用的数据类型。<br />nchar(<size></size>):根据字符集而定的固定长度字符串，最大长度2000字节。<br />nvarchar2(<size></size>):根据字符集而定的可变长度字符串，最大长度4000字节。<br />long:可变长字符列，最大长度限制为2GB，用于不需要作字符串搜索的长串数据。此类型是一个遗留下来的而且将来不会被支持的数据类型，逐渐被BLOB，CLOB，NCLOB等大的数据类型所取代。<br />numberic数据类型用来存储负的和正的整数，分数和浮点型数据，在oracle中提供的numberic数据类型：<br />number(<m></m>,<n></n>):可变长的数值列，允许0、正值及负值，m是所有的有效数字的位数，n是小数点以后的位数。<br />在oracle中提供的date数据类型:<br />date:缺省格式是dd-mon-yy(日-月-年)<br />在oracle中提供的lob数据类型:<br />blob、clob、nclob：三种大型对象(lob)，用来保存较大的图形文件或带格式的文本文件，如word文档，以及音频、视频等非文本文件，最大长充是4GB。晕些数据存储在数据库内部保存。<br />bfile:在数据库外部保存的大型二进制对象文件，最大长度是4GB，这种外部的LOB类型，通过数据库记录变化情况，但是数据的具体保存是在数据库外部进行的。<br />在oracle中提供的raw数据类型:<br />raw(<size></size>):可变长二进制数据，具体定义字段时必须指明最大长度，这种格式用来保存较小的图形文件或带格式的文本文件，它也是一种较老的数据类型，将被lob数据类型所取代。<br />long raw:可变长二进制数据，最大长度是2GB，可以用来保存较大的图形或带格式的文本文件，以及音频、视频等非文本文件，这也是一种较老的数据类型，将被lob数据类型所取代。<br />其它的数据类型：<br />rowid:这是oracle数据表中的一个伪例，它是数据表中每行数据内在的唯一标识<br />integer:整数类型<br /><br />创建购物网站后台数据库<br /><br />现在我们回到用J2EE体系开发购物网站的主题，开始实战建购物网站的后台数据库。<br />为了实现购物网站的基本的功能，我们需要建立四个表：商品列表(products)、商品类型表(item)、订单列表(orders)和管理员列表(admin)。表结构如下所示：<br /><br />item表结构（商品类型表)<br />字段名称 数据类型 允许空 主键/外键 备注 <br />type_id INTEGER(自动编号) 否 主键 商品类别ID标记<br />type varchar2(30) 否 商品类别名称<br /><br />product表结构(商品列表)<br />字段名称 数据类型 允许空 主键/外键 备注<br />product_id INTEGER(自动编号) 否 主键 商品ID标记<br />title varchar2(30) 否 商品名称<br />type_id INTEGER 否 外键 商品类别标记<br />info varchar2(80) 是 商品简介<br />price number(16,2) 否 商品价格<br /><br />orders表结构(订单列表)<br />字段名称 数据类型 允许空 主键/外键 备注<br />order_id INTEGER(自动编号) 否 主键 订单ID标记<br />name varchar2(20) 否 顾客姓名<br />address varchar2(100) 是 发货地址<br />tel number(16) 是 联系电话<br />email varchar2(30) 否 联系email<br />btime date 是 订购日期<br />product_id INTEGER 否 外键 商品标记<br />uword varchar2(100) 是 顾客留言<br /><br />admin表结构(管理员列表)<br />字段名称 数据类型 允许空 主键/外键 备注<br />admin_id INTEGER(自动编号) 否 主键 管理员ID标记<br />adminname varchar2(20) 否 管理员名称<br />password varchar2(20) 否 管理员密码<br /><br />设计完表结构后，我们就要开始创建了。<br />创建表我想已经不是什么难事了，那么我们要注意的是product、item、orders这三个表之间的关联，还有自动编号。<br /><br />下面是完整的SQL语句，在后面我会给出详细的说明，你可以在SQL*Plus里对照着输入，也可以将它存为SQL脚本文件，在SQL*Plus或SQLPlus Worksheet里执行。当然也可以把代码直接拷贝到SQL*Plus里执行！<br /><br />rem ///BY MAXUAN 开始///<br />create table item(<br />type_id integer not null,<br />type varchar2(30),<br />constraint item_pk primary key(type_id)<br />);<br /><br />create table product(<br />product_id integer not null,<br />title varchar2(30) not null, <br />type_id integer not null,<br />info varchar2(80),<br />price number(16,2) not null,<br />constraint product_pk primary key (product_id),<br />constraint product_fk foreign key(type_id) references item(type_id)<br />);<br /><br />create table orders(<br />order_id integer not null,<br />name varchar2(20) not null,<br />address varchar2(100),<br />tel number(16),<br />email varchar2(30) not null,<br />btime date,<br />product_id integer not null,<br />uword varchar2(100),<br />constraint orders_pk primary key(order_id),<br />constraint orders_fk foreign key(product_id) references product(product_id)<br />);<br /><br />create table admin(<br />admin_id integer not null,<br />adminname varchar2(20) not null,<br />password varchar2(20) not null,<br />constraint admin_pk primary key(admin_id)<br />);<br /><br />create sequence type_id increment by 1 start with 1;<br />create sequence product_id increment by 1 start with 1;<br />create sequence order_id increment by 1 start with 1;<br />create sequence admin_id increment by 1 start with 1;<br /><br />rem ///BY MAXUAN 结束///<br /><br />说明一：建立表之间的关联<br />　　product、item、orders三个表通过公共域，通常称为键域(Key Field)进行关联，存在两种类型的键：主键(Primary key)和外部键(Foreign key)。主键使表中的数据行保持唯一，在表product中，product_id为主键，表orders中也包含有product_id,此时的product_id就是外部键。一个表的外部键从其它表中获取信息。看看上面的SQL语句，应该会了吧！<br /><br />说明二：关于自动编号<br />　　在access中有自动编号的数据类型，MSSQL和MYSQL也都有自动增长的数据类型，插入记录时不用操作此字段，会自动获得数据值，而oracle没有自动增长的数据类型，我们需要建立一个自动增长的序列号，插入记录时要把序列号的下一个值赋于此字段，可以预见的是，有此功能，我们可以把数据从ACCESS、MSSQL或MYSQL迁移到oracle了！<br />create sequence type_id increment by 1 start with 1;<br />这句中，type_id为序列号的名称，每次增长为1，起始序号为1。<br /><br />　　好了，咱们的数据库已经建好了，而且从中也了解到一些基本的相关知识，关于本人的用J2EE开发购物网站之二oracle篇到此结束，如有什么疑问请留言!!]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/76542.html</guid>
<subject>J2EE 相关文章</subject>
<author>longkuangxiao</author>
<category>J2EE 相关文章</category>
<pubDate>Fri, 24 Mar 2006 00:05:22 CST </pubDate>
</item>

<item>
<title>实战J2EE—开发购物网站（三）</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/76541.html</link>
<description>
<![CDATA[<p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td valign="top" width="86%">二：创建数据库<br /><br />小试牛刀oracle 8i(顺便我们练练手)(为了方便，我就将oracle 8i简写8i了)<br />8i和9i确实有些不一样，不过在界面上区别不大，还是以我的oracle 8i为例为大家讲解一下吧！既然是实战，首先我们为自己新建一个开发用户，并创建一个表！！<br />在oracle里创建新用户和表的方法比较多，只要你有管理员的权限（废话！），首先用最简单的，在8i的程序组中启动DBA Studio程序项,在出现的数据库链接信息中输入管理用户名和口令即可，当然，在oracle中，你可以用用户名斜杠口令的方法登陆，连接身份选SYSDBA。<br /><br /><br /><br />(1-1)创建用户：<br />在登陆后，点安全，在第一项，你就会看到用户，用右键你可以创建新用户，或者给用户授于角色，不同的角色拥有不同的权限！oracle8i预定义的角色，我在下面会讲。看看下边我抓的图吧，在一般信息后面的角色，你就可以赋予connect,resource权限。<br /><br /><br /><br /><br /><br />(1-2)创建数据库：<br />咱们先断开system的数据库连接，可以在文件中选择，也可以用右键。然后用我们创建的用户来登陆，并且在方案中找到表，然后就可以直接创建，由于是图形可视化，而且又是全中文的，我就不缀述了！！大家可以多试试！<br /><br /><br /><br />(2)用SQLPLUS工具创建用户和数据库：<br />在oracle中可以使用sqlplus这个工具来书写SQL语句，对于SQL高手来说，这是发挥才能的好地方！！<br />咱们来试试吧，首先一样要先登陆，为了创建新用户，咱们仍以system来登陆。在主机字符串中填入数据标识(oradb)<br /><br /><br /><br />下用我用一张截图说明所有的操作，这里做一下说明：<br />在登陆进去后，出现&ldquo;SQL&gt;&rdquo;我们就可以在它后面书写SQL语句，grant connect,resouce to maxuan identified by max,这一句是创建用户maxuan,口令为max,并赋予connect,resource权限，在出现授权成功后，我们用conn maxuan/max这句来以maxuan连接数据库，这时的角色已经不是DBA了！接下来的是些基本的SQL建表语句，不用多说了，在sqlplus中，每条语句结束用分号标识，在完成后，我们可以用quit或exit命令断开<br /><br /><br /><br />ps:这里我说几个8i的预定义角色！<br /><br />1.CONNECT角色: 授于最终用户的典型最基本的权利<br />ALTER SESSION --修改会话<br />CREATE CLUSTER --建立聚簇<br />CREATE DATABASE LINK --建立数据库链接<br />CREATE SEQUENCE --建立序列<br />CREATE SESSION --建立会话<br />CREATE SYNONYM --建立同义词<br />CREATE VIEW --建立视图<br /><br />2.RESOURCE角色: 是授予开发人员的<br />CREATE CLUSTER --建立聚簇<br />CREATE PROCEDURE --建立过程<br />CREATE SEQUENCE --建立序列<br />CREATE TABLE --建表<br />CREATE TRIGGER --建立触发器<br />CREATE TYPE --建立类型<br /><br />3.DBA角色：拥有系统所有系统级权限<br /><br />4.IMP_FULL_DATABASE角色、EXP_FULL_DATABASE角色：<br />BACKUP ANY TABLE --备份任何表<br />EXECUTE ANY PROCEDURE --执行任何操作<br />SELECT ANY TABLE --查询任何表<br /><br />5.DELETE_CATALOG_ROLE角色：<br />授予用户这个角色，用户就可以从表sys.aud$中删除记录，<br />sys.aud$表中记录着审计后的记录，使用这个角色可以简化审计踪迹管理。<br /><br />6.SELECT_CATALOG_ROLE角色、EXECUTE_CATALOG_ROLE角色：<br />SELECT_CATALOG_ROLE角色具有从数据字典查询的权利，<br />EXECUTE_CATALOG_ROLE角色具有从数据字典中执行部分过程和函数的权利。<br /></td>
        </tr>
    </tbody>
</table>
</p>]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/76541.html</guid>
<subject>J2EE 相关文章</subject>
<author>longkuangxiao</author>
<category>J2EE 相关文章</category>
<pubDate>Fri, 24 Mar 2006 00:03:33 CST </pubDate>
</item>

<item>
<title>实战J2EE—开发购物网站（二）</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/76540.html</link>
<description>
<![CDATA[第三步，安装配置jBuilder 7<br /><br />这里我安装的是weblogic版<br />安装jBuilder不要把它装到带有空格的目录里如Prograih Files,或是中文目录中，不然编译时会出错的。安装很简单，不必多言，这里着重讲一下配置<br /><br />小技巧一：大家觉得jBuilder运行时也许很慢，因为jbuilder默认使用32MB内存，如果你的内存够大的话，可以加大一点，具体办法如下，<br /><br />在jBuilder安装目录BIN下找到jbuilder.config文件，编辑它，改变Vmparams 后的参数，我的配置为 <br />vmparam -Xms64M<br />vmparam -Xmx64M<br />启动jBuilder，在Help下的About Jbuilder下可以看到Java heap已经增加了，运行也快了许多，起码以后可以编译较大的文件。<br /><br />小技巧二：运行jBuilder时，发现编写代码很不方便，所选的代码与实际所见的总是差一个，这是因为字体的原因，解决方法如下，在Tools的Editor Options下的Display里，将Editor font里的Font Family改为新宋体即可。<br /><br /><br /><br />配置weblogic服务<br /><br />用jBuilder可以整合weblogic，这样可以把在jBuilder下开发的EJB或打包成WAR的网站直接发布到weblogic！<br /><br />首先在tools下选择Configure Servers<br /><br />在下面的user Home下，你可以看到&ldquo;webLogic Application Server 6.X-7.0&rdquo;项，点击它，在右边会出现Server Settings ，将Enable server打上勾，在下面的General下选择home directory和Working directory:本人的为&quot;D:/bea/wlserver6.1&quot;<br /><br /><br /><br />然后点Custom项<br />选择JDK installation directory:本人的为&ldquo;D:/bea/jdk131&rdquo;<br />BEA home directory:本人的安装目录为&ldquo;D:/bea&rdquo;，接着输入密码和设定的Domain name和Server name这些都是你安装weblogic时设定的，如果目录选择正确，在下面的version下会自动出现你的weblogci的版本号。点击OK即完成！<br /><br /><br /><br />下面，我们做一个试验，看看weblogic是不是和jbuilder整合了！<br />首先，我们新建一个工程，选Application,在name里填写你的工程名,接着按完成即可。<br /><br /><br /><br /><br />在新建工程项目时顺便看一看Server是不是用的Weblogic<br /><br /><br /><br />然后再选新建，在出现的Object Gallery下选Web,因为我们是要发布WEB应用，所以选取Web Application，为它命个名，这将是网站包的名字，指定它的工作目录后选择Generate war,点OK即可<br /><br /><br /><br />接下来，我们要新建一个JSP文件，一样点新建，在WEB下选择JAVAServer Page,按它的默认点下一步即可，它会自动创建一个JAVABEAN。<br /><br />首先，我们要把自动生成的这个BEAN编译为class，选中它，点右键，在出现的菜单选Rebuild编译<br /><br /><br /><br />然后编译你的Web Application，它会自动生成WAR包,点这个包发布即可<br /><br /><br /><br />发布网站<br /><br /><br /><br />接下来打开浏览器输入 http://127.0.0.1:7001/tds/index.jsp 即可看到该页面。<br /><br /><br /><br />PS:WAR为整个web appliction压缩包，讲简单明白点就是我们所做的整个网站都可以打成一个包，然后用weblogic来发布，当然weblogic也支持用目录发式来发布的！<br />]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/76540.html</guid>
<subject>J2EE 相关文章</subject>
<author>longkuangxiao</author>
<category>J2EE 相关文章</category>
<pubDate>Fri, 24 Mar 2006 00:02:01 CST </pubDate>
</item>

<item>
<title>实战J2EE—开发购物网站（一）</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/76539.html</link>
<description>
<![CDATA[前言<br /><br />本文通过实战全程编写一个购物网站来讲解如何使用J2EE来建立企业级的网络应用！本文针对的是像我这样的绝对初学者，如果您是高手就莫要见笑了！<br /><br />一：搭建开发平台<br /><br />本文从实战出发，所以关于一些概念性的问题就不多讲了，这些东东可以从google上搜到，首先准备下面的家伙吧：<br /><br />1.oracle<br />2.weblogic<br />3.jbuilder<br /><br />其它：struts-console-2.2（用作struts视图开发）PowerBulider（方便数据库操作）Dreamweaver(美化前台页面)<br />以上就是我在公司里开发的所用的东东了！开发平台为win2000。<br /><br />以下以我家里的配置为例详细介绍搭建J2EE的开发平台。（家里的软件都是从网上当的）<br /><br />家用机配置：P4赛扬1.7G,256MB 20GHDD 操作系统：windows2000 SP3<br />数据库：oracle 8i<br />J2EE应用平台:weblogic 6.1 sp2<br />EJB,JSP,jAVABEAN开发：jbuilder 7 weblogic版<br /><br />第一步，安装配置oracle数据库<br /><br />安装oracle最新版的9i什么事都没有，但如果你在P4的机器上装8i可能就会出现问题，点了安装之后没有反应。（P4赛扬也存在这个问题）没法，我的oracle就是从网上当的8i（比9i苗条多了），如果您是P4的机器又要装8i的话，那么先按如下方法做（这是我从网上搜到的方法）<br /><br />（1）创建一临时目录，并将Oracle8i的安装源程序拷贝到此目录。 <br />找到目录 <br />stage\Components\oracle.swd.jre\1.1.7.30\1\DataFiles\Expanded\jre\win32\bin下的 symcjit.dll 的文件，并改名为 symcjit.old。注意OEM目录下还有一个symcjit.dll文件要改名。<br />（2）搜索到oraparam.ini文件，打开它，改变行RE_MEMORY_OPTIONS=-mx48m的参数为 JRE_MEMORY_OPTIONS=-nojit -ms16m -mx32m <br />（3）其它的参数保持不变。 <br />（4）执行本地硬盘install\win32\setup.exe目录下的Setup.exe文件。此时调用的是修改过的参数文件oraparam.ini。 <br />（5）安装时选择自定义模式，并且不创建数据库。 <br />（7）Oracle8i安装完成后，安装目录中的下列每一个文件都有需要作相应修改。(如果不修改点任何应用都没有反应)<br /><br />assistants\dbca\dbassist.cl <br />assistants\dbma\dbmig.cl <br />assistants\ifa\ifa.cl <br />bin\elogin.cl <br />bin\owm.cl <br />ldap\oidamdin\oidadmin.cl <br />network\tools\netasst.cl <br />network\tools\netca.cl <br />owm\install\instelogin.cl <br />owm\install\instowm.cl <br /><br />用编辑工具依次打开上述文件，增加参数 -nojit，以dbassist.cl文件为例示意如下： <br /><br />Command=(&quot;C:\Program Files\Oracle\jre\1.1.7\BIN\JREW&quot; -nojit -classpath ....) <br /><br />用相同的方法修改所列出的每一个.cl 文件内容。<br />修改完后运行database administrator下的Database Configuration Assistant就可以创建数据库了，这可是一个漫长的过程，在创建数据库时要创建全局标识，搞个好记的就行，以后好配置数据库连接。创建完后，默认的DBA用户名为system,密码为manager。<br />在以后，我会陆续讲一些关于oracle的初学经验，帮助大家快速入门！ <br /><br />第二步，安装配置weblogic 6.1SP2,并连接oracle<br /><br />win平台的weblogic基本上都可以直接安装，但要注意不要把它装到带有空格的目录里如Program Files,或是中文目录中，默认为c:\bea，按默认点下一步，就可以完成安装，这个基本没有意外！（最简单的安装操作）,最后要记住你设的管理密码!<br />我把管理员名和服务名改了。<br /><br /><br /><br />为了调试方便，不将weblogic设为自动服务启动<br /><br /><br /><br />设定您的管理密码，进入控制台，启动服务都需要<br /><br /><br /><br />为了方便，我将目录安到了D盘，并将domain改为maxuan,server改为max,接下来我们将配置JDBC连接oracle数据库<br /><br />首先打开D:\bea\wlserver6.1\config\maxuan(maxuan为我改后的名字，默认的为mydomain)，用文本编辑器如：记事本或editplus打开startWebLogic.cmd，找到&ldquo;set PATH=&rdquo;这一行，加入&ldquo;.\bin\oci817_8&rdquo;，如本人的为&ldquo;set PATH=.\bin;.\bin\oci817_8;%PATH%&rdquo;<br /><br />为了使启动weblogic不需要每次都输入密码，在&ldquo;set WLS_PW=&rdquo;输入安装时所设定的密码即可。<br /><br />双击startWebLogic.cmd或在程序中直接运行Start Default Server启动weblogic服务（注意不要将这个窗口关了哟）<br />启动weblogic<br /><br /><br /><br />然后在程序中运行Start Default Console，进入控制台操作,在出现的对话框中输入管理用户名system，密码为你设的密码即可进入！<br /><br /><br /><br />点开左边的Services，你可以看到JDBC<br /><br /><br /><br />点开它,点击在下面的Connection Pools，右边会出现配置项目，点击Configure a new JDBC Connection Pool出现配置项，在Configuration下的General标签下配置<br /><br />name:这里我填写的是&ldquo;mytest&rdquo;；<br />URL填写为&ldquo;jdbc：weblogic：oracle&rdquo;；<br />Driver Classname:填写为&ldquo;weblogic.jdbc.oci.Driver&rdquo;；<br />Properties(key=value)：填写为：<br />user=system<br />password=manager<br />server=oradb<br />这里的oradb即是所安装的oracle所创建的数据库的全局标识。user和password我用的是默认的DBA，你可以用自己创建的用户名和密码。点击Apply按钮完成。<br /><br /><br /><br />点击Targets下的Servers标签，把Available下的Tgets-Server移到Chosen下，然后点Apply按钮。<br /><br /><br /><br />接着配置下面的Data Sources，在右边用&ldquo;Configure a new JDBC Data Source&rdquo;创建新的Data Sources，这里的Name和Pool Name均填上面的设定&ldquo;mytest&rdquo;，JNDI Name填写&ldquo;oradb&rdquo;,同上面一样，点击Targets下的Servers标签，把Available下的Targets-Server移到Chosen下，然后点Apply按钮。<br /><br /><br /><br />这时点开Start Default Server窗口应当看到已经启动jDriver/Oracle了！<br />PS：用weblogic已经有段时间了，而且也参加过weblogic J2ee 的培训，以后将会把相关经验放到论坛上！<br />重启服务吧！<br />]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/76539.html</guid>
<subject>J2EE 相关文章</subject>
<author>longkuangxiao</author>
<category>J2EE 相关文章</category>
<pubDate>Fri, 24 Mar 2006 00:00:00 CST </pubDate>
</item>

<item>
<title>JAVA 对象持久化技术 Hibernate</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/76511.html</link>
<description>
<![CDATA[<p>&nbsp;Hibernate是什么?</p>
<p>&nbsp;</p>
<p>1)它是连接JAVA应用程序和关系数据库的中间件.</p>
<p>2)它对JDBC API进行了封装,负责JAVA对象的持久化.</p>
<p>3)在分层的软件架构中它位于持久化层,封装了所有数据访问细节,使业务逻辑层可以专注于实现业务逻辑.</p>
<p>4)它是一种ORM(对象关系映射)影射工具,能建立面向对象的域模型和关系模型之间的映射.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在如今的企业应用开发环境中,面向对象的开发方法已成为主流.对象只能存在于内存中,而内存不能永久保存数据.如果要永远保存对象的状态,需要进行对象的持久化,既把对象存储到专门的数据存储中.目前,关系数据库仍然是使用最广泛的数据存储库.关系数据库中存放的是关系数据,它是非面向对象的.内存中的对象之间存在关联和继承的关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系.因此,把对象持久化到关系数据库中,需要进行对象-关系的影射(Object/Relation Mapping,简ORM),这是一项烦琐的工作,值得我们庆幸的是Hibernate解决了这一问题.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 目前,越来越多的JAVA开发人员把Hibernate作为企业应用和关系数据库之间的中间件,以节省和对象持久化有关的30%的JDBC变成工作量.2005年,Hibernate作为优秀的类库和组件,荣获了第15届Jolt奖.Hibernate之所以能够流行,归功于它的以下优势:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>1)它是开放源代码的,允许开发人员在需要的时候研究源代码,改写源代码,制定客户化功能.</p>
<p>2)具有详细的参考文档.</p>
<p>3)对JDBC仅做了轻量级封装,必要的话,用户可绕过Hibernate,直接访问JDBC API.</p>
<p>4)具有可扩展性.</p>
<p>5)使用方便,容易上手.</p>
<p>6)Hibernate既适用于独立的JAVA程序,也适用于JAVA Web应用,而且还可以在J2EE架构中取代CMP(Container-managered Persistence,由容器管理持久化),Hibernate能集成到会话EJB和基于BMP的实体EJB中,BMP(Bean-managered Persistence)是指由实体EJB本身管理持久化.</p>
<p>7)Hibernate可以和多种Web服务器,应用服务器良好的集成,并且支持几乎所以流行的数据库服务器.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 给大家简单的介绍了Hibernate.希望高手们能够多发表一些关于技术方面的文章供大家学习.也希望JAVA爱好者们在这些精彩文章的基础上相互研究,讨论.我们的目标是共同进步.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 前进...........................................</p>]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/76511.html</guid>
<subject>J2EE 相关文章</subject>
<author>longkuangxiao</author>
<category>J2EE 相关文章</category>
<pubDate>Thu, 23 Mar 2006 21:49:36 CST </pubDate>
</item>

<item>
<title>Java初学者都必须理解的六大问题</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/56716.html</link>
<description>
<![CDATA[对于这个系列里的问题，每个学Java的人都应该搞懂。当然，如果只是学Java玩玩就无所谓了。如果你认为自己已经超越初学者了，却不很懂这些问题，请将你自己重归初学者行列。 <br /><br />　　问题一：我声明了什么！ <br /><br /><br />　　String s = &quot;Hello world!&quot;; <br /><br />　　许多人都做过这样的事情，但是，我们到底声明了什么？回答通常是：一个String，内容是&ldquo;Hello world!&rdquo;。这样模糊的回答通常是概念不清的根源。如果要准确的回答，一半的人大概会回答错误。 <br /><br />　　这个语句声明的是一个指向对象的引用，名为&ldquo;s&rdquo;，可以指向类型为String的任何对象，目前指向&quot;Hello world!&quot;这个String类型的对象。这就是真正发生的事情。我们并没有声明一个String对象，我们只是声明了一个只能指向String对象的引用变量。所以，如果在刚才那句语句后面，如果再运行一句： <br /><br /><br />　　String string = s; <br /><br />　　我们是声明了另外一个只能指向String对象的引用，名为string，并没有第二个对象产生，string还是指向原来那个对象，也就是，和s指向同一个对象。 <br /><br />　　 问题二：&quot;==&quot;和equals方法究竟有什么区别？ <br /><br />　　==操作符专门用来比较变量的值是否相等。比较好理解的一点是： <br /><br /><br />　　int a=10; <br />　　int b=10; <br /><br />　　则a==b将是true。 <br /><br />　　但不好理解的地方是： <br /><br /><br />　　String a=new String(&quot;foo&quot;); <br />　　String b=new String(&quot;foo&quot;); <br /><br />　　则a==b将返回false。 <br /><br />　　根据前一帖说过，对象变量其实是一个引用，它们的值是指向对象所在的内存地址，而不是对象本身。a和b都使用了new操作符，意味着将在内存中产生两个内容为&quot;foo&quot;的字符串，既然是&ldquo;两个&rdquo;，它们自然位于不同的内存地址。a和b的值其实是两个不同的内存地址的值，所以使用&quot;==&quot;操作符，结果会是false。诚然，a和b所指的对象，它们的内容都是&quot;foo&quot;，应该是&ldquo;相等&rdquo;，但是==操作符并不涉及到对象内容的比较。 <br /><br />　　对象内容的比较，正是equals方法做的事。 <br /><br />　　看一下Object对象的equals方法是如何实现的： <br /><br /><br />　　boolean equals(Object o){ <br /><br />　　return this==o; <br /><br />　　} <br /><br />　　Object对象默认使用了==操作符。所以如果你自创的类没有覆盖equals方法，那你的类使用equals和使用==会得到同样的结果。同样也可以看出，Object的equals方法没有达到equals方法应该达到的目标：比较两个对象内容是否相等。因为答案应该由类的创建者决定，所以Object把这个任务留给了类的创建者。 <br /><br />　　看一下一个极端的类： <br /><br /><br />　　Class Monster{ <br />　　private String content; <br />　　... <br />　　boolean equals(Object another){ return true;} <br /><br />　　} <br /><br />　　我覆盖了equals方法。这个实现会导致无论Monster实例内容如何，它们之间的比较永远返回true。 <br /><br />　　所以当你是用equals方法判断对象的内容是否相等，请不要想当然。因为可能你认为相等，而这个类的作者不这样认为，而类的equals方法的实现是由他掌握的。如果你需要使用equals方法，或者使用任何基于散列码的集合（HashSet,HashMap,HashTable），请察看一下java doc以确认这个类的equals逻辑是如何实现的。 <br />　　 <br />　　问题三：String到底变了没有？ <br /><br />　　没有。因为String被设计成不可变(immutable)类，所以它的所有对象都是不可变对象。请看下列代码： <br /><br /><br />　　String s = &quot;Hello&quot;; <br />　　s = s + &quot; world!&quot;; <br /><br />　　s所指向的对象是否改变了呢？从本系列第一篇的结论很容易导出这个结论。我们来看看发生了什么事情。在这段代码中，s原先指向一个String对象，内容是&quot;Hello&quot;，然后我们对s进行了+操作，那么s所指向的那个对象是否发生了改变呢？答案是没有。这时，s不指向原来那个对象了，而指向了另一个String对象，内容为&quot;Hello world!&quot;，原来那个对象还存在于内存之中，只是s这个引用变量不再指向它了。 <br /><br />　　通过上面的说明，我们很容易导出另一个结论，如果经常对字符串进行各种各样的修改，或者说，不可预见的修改，那么使用String来代表字符串的话会引起很大的内存开销。因为String对象建立之后不能再改变，所以对于每一个不同的字符串，都需要一个String对象来表示。这时，应该考虑使用StringBuffer类，它允许修改，而不是每个不同的字符串都要生成一个新的对象。并且，这两种类的对象转换十分容易。 <br /><br />　　同时，我们还可以知道，如果要使用内容相同的字符串，不必每次都new一个String。例如我们要在构造器中对一个名叫s的String引用变量进行初始化，把它设置为初始值，应当这样做： <br /><br /><br />　　public class Demo { <br />　　private String s; <br />　　... <br />　　public Demo { <br />　　s = &quot;Initial Value&quot;; <br />　　} <br />　　... <br />　　} <br /><br />　　而非 <br /><br /><br />　　s = new String(&quot;Initial Value&quot;); <br /><br />　　后者每次都会调用构造器，生成新对象，性能低下且内存开销大，并且没有意义，因为String对象不可改变，所以对于内容相同的字符串，只要一个String对象来表示就可以了。也就说，多次调用上面的构造器创建多个对象，他们的String类型属性s都指向同一个对象。 <br />　　上面的结论还基于这样一个事实：对于字符串常量，如果内容相同，Java认为它们代表同一个String对象。而用关键字new调用构造器，总是会创建一个新的对象，无论内容是否相同。 <br /><br />　　至于为什么要把String类设计成不可变类，是它的用途决定的。其实不只String，很多Java标准类库中的类都是不可变的。在开发一个系统的时候，我们有时候也需要设计不可变类，来传递一组相关的值，这也是面向对象思想的体现。不可变类有一些优点，比如因为它的对象是只读的，所以多线程并发访问也不会有任何问题。当然也有一些缺点，比如每个不同的状态都要一个对象来代表，可能会造成性能上的问题。所以Java标准类库还提供了一个可变版本，即StringBuffer。 <br />]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/56716.html</guid>
<subject>J2SE 相关文章</subject>
<author>longkuangxiao</author>
<category>J2SE 相关文章</category>
<pubDate>Thu, 23 Feb 2006 08:25:12 CST </pubDate>
</item>

</channel>
</rss>
