iLeichun

当前位置:首页

DIV+CSS与table布局的比较

分类:网站建设  来源:网络  时间:2011-2-15 22:26:10
CSS是英语Cascading Style Sheets(层叠样式表单)的缩写,它是一种用来表现 HTML 或 XML 等文件式样的计算机语言。
DIV元素是用来为HTML文档内大块(block-level)的内容提供结构和背景的元素。DIV的起始标签和结束标签之间的所有内容都是用来构成这个块的,其中所包含元素的特性由DIV标签的属性来控制,或者是通过使用样式表格式化这个块来进行控制。
DIV
CSS单元的位置和层次-div标签
我们都知道,在网页上利用HTML定位文字和图像是一件“令人心痛”的事情。我们必须使用表格标签和隐式GIF图像,即使这样也不能保证定位的精确,因为浏览器和操作平台的不同会使显示的结果发生变化。
而CSS能使你看到希望的曙光。利用今天我们即将学到的CSS属性,你可以精确地设定要素的位置,还能将定位的要素叠放在彼此之上,还有...还有......你自己慢慢看吧!
Cascading Style Sheets(CSS)是 DHTML 的基础。CSS 用来设定你网页上的元素是如何展示的。Cascading Style Sheets Positioning(CSS-P)是 CSS 的一个扩展,它可用来控制任何东西在网页上或是说在窗口中的位置。请你记住这两个名词:CSS 和 CSS-P。下面四个英文网址提供了详细的关于 CSS 和 CSS-P 的文件和解释。
● 1.使用 DIV 标签 (div)
当我们使用 CSS-P 的时候, 我们主要把它用在 DIV(division)tag 上。当你把文字,图像,或其他的放在 DIV 中,它可称作为“DIV block”,或“DIV element”或“CSS-layer”,或干脆叫“layer”。而中文我们把它称作“层次”。所以当你以后看到这些名词的时候,你就知道它们是指一段在 DIV 中的 HTML。
使用 DIV 的方法跟使用其他 tag 的方法一样:
<DIV>This is a DIV tag .</DIV>
如果单独使用 DIV 而不加任何 CSS-P, 那么它在网页中的效果和使用 <P></P> 是一样的。
但当我们把 CSS-P 用到 DIV 中去以后,我么就可以严格设定它的位置。首先我们需要给这个可以被 CSS-P 控制的 DIV 一个 ID 或说是它的名字。比如说我们给下面这个 DIV 的名字是 truck。给名字的目的是我们以后可用 JavaScript 来控制它, 比如说移动它或改变它的一些性质等等。
<DIV ID="truck">
This is a truck
</DIV>
给层次取什么名字是随意的,名字可以是任何英文字母和数字,但第一个必须是字母。有两种把 CSS-P 应用到 DIV 的方法。
Inline CSS:Inline 是最常用的方法。
<DIV ID="truck" STYLE="styles go here">
This is a truck .
</DIV>
External STYLE tag:使用 External 方法的结果是一样的。我们会在以后的课程里再详细解释这种方法。现在我们主要讨论Inline 方法。请注意在 External 方法里,在 STYLE 里的 ID 和 DIV 里的关系。
<STYLE TYPE="text/css">
<!-- #truck {styles go here} -->
</STYLE>
<DIV ID="truck">This is a truck .</DIV>
Cross-Browser CSS 性质:
我们这个课程的主要目的是让你写出的网页在 NS4 和 IE4 上都能工作,所以我们主要讨论那些对俩者都通用的性质。下面这些性质符合由 W3C 给出的标准。
position 决定 DIV tag 是如何放置的。“relative”意思是DIV的位置是相对于其他 tag 的,而“absolute”是说 DIV tag 的位置是相对于它所在的窗口。
left 相对于窗口左边的位置
top 相对于窗口上边的位置
width DIV tag 的宽度。所有在 DIV 里的文字或html都在里面。
height DIV tag 的高度。这个性质很少用除非你想 Clip 层次。
clip 给出 layer 的 clipping(可看的见的)部分。Clip 可使得 DIV 显示为一个可以定义的很准确的方块。你可以用以下的四个值来给出这个方块的在 DIV 位置和大小。
clip:rect(top,right,bottom,left);
visibility 隐蔽或展现DIV 根据它的值“visible”,“hidden”,“inherit”。
z-index DIV tag 的立体位置。值越大 DIV 的位置越高。
background-color DIV 背景的颜色。
layer-background-color Netscape 的 DIV 背景颜色。
background-image DIV 的背景图像。
layer-background-image Netscape 的 DIV 的背景图像。
● 2.绝对定位和相对定位 (position)
绝对定位:
定位属性将是网虫们打开幸福之门的钥匙:
H4 { position: absolute; left: 100px; top: 43px }
这项CSS规则让浏览器将<H4>的起始位置精确地定在距离浏览器左边100象素,距离其顶部43象素的位置。注意这里唯一设置了的是左边和顶部,也就说,文字将从左到右,从上到下载入浏览窗口。
左边和顶部属性很直观,左边(left)设定要素距浏览器窗口左边的距离,顶部(top)设定距离浏览器窗口顶部的距离。设定这些距离时,你可以使用所学过的各种度单位或比例值。使用比例值时,比例值的是相对于母体要素的尺寸。
你可以定位什么呢?任何东西!段落、单词、GIF和JPEG图像、QUICKTIME电影等等。
相对定位:
绝对定位使你能精确地定位要素在页面的独立位置,而不考虑页面其它要素的定位设置。相对定位指你所定位的要素的位置相对于在文件中所分配的位置。例:
I { position: relative; left: 40px; top: 10px }
相对定位的关键在于定位了的要素的位置是相对于它通常应在的位置进行定位。相对定位单元出现在普通的静态定位单元的行间,定位时没有把自己和静态定位单元完全分开。如果你停止使用相对定位,则文字的显示位置将恢复正常。使用相对定位时要小心,否则容易将页面弄得非常乱。
除了相对定位和绝对定位,你还可以使用static(静止)参数值。Static 是 position 特性的缺省值。它的使用方法同普通HTML中的定位方法,不能附加特殊的定位设置。也就是说,除了边距特性,或通过使用 float 特性来浮动单元可影响单元的定位外,其它均不可以。
● 3.定位单元的控制 (width、height、visiblility)
除了控制定位单元的左上角位置,你还可以控制单元的宽度和高度,及单元在页面的可视性。
宽度:定位了的要素在页面上显示时仍然会从左到右一直显示。利用宽度属性就可以设定字符向右流动的限制,即设定要素的宽度。
DIV { position: absolute; left: 200px; top: 40px; width: 150px }
浏览器接到这项规则时,它将文字按照规则规定的效果显示,还将段落的最大水平尺寸限制在150象素。
宽度属性只适用于绝对定位的要素。你可以使用我们学过的任何一种长度单位,或使用比例值设定宽度,比例值指相对于母体要素的比例。IE 4中,这项属性还可用于图像。你可以通过宽度设置人为地拉宽或压缩图像。
高度:理论上讲,高度应该和宽度的设置类似,只不过是在垂直方向上:
DIV { position: absolute; left: 200px; top: 40px; height: 150px }
这里我用了“理论上讲”,因为有些浏览器不支持高度属性。
可视性:利用CSS,你可以隐藏要素,使其在页面上看不见。这条属性对于定位了的和未定位的要素都适用。
H4 { visibility: hidden }
选项:
visible 使要素可以被看见
hidden 使要素被隐藏
inherit 指它将继承母体要素的可视性设置。
值 inherit 为缺省值。这使单元继承父单元的可见性。所以,如果某一段是隐藏的,则它包含的任何行间单元也都被隐藏。这一继承性可被明确指定的可见性取代。例如,段内的 EM 单元被指定为可见,这时如果该段被隐藏,则段内的所有其它内容都将消失,而唯有 EM 单元中的文本是可见的。
当一个要素被隐藏后,它仍然要占据浏览器窗口中的原有空间。所以,如果你将文字包围在一幅被隐藏的图像周围,那么,其显示效果将是文字包围着一块空白区域。此新闻
这条属性在编写语言和使用动态HTML时很有用,比如你可以使某段落或图像只在鼠标滑过时才显示。
● 4.单元层次 (z-index)
特性 z-index 用于堆叠屏幕上的单元。缺省情况下,单元堆叠的顺序为它们出现在HTML标记的顺序——也就是说,后出现单元堆叠在早出现单元的上面。Z-index 特性实际上定义同属(sibling)单元的堆叠顺序以及单元相对父单元的堆叠。按照规范草案,具有正 z-index 值的单元群都堆叠在父单元之上,它们自己的堆叠顺序则按其取值的大小来决定(值大的单元在上层)。同样,具有负 z-index 值的单元群都堆叠在父单元之下,它们自己的堆叠顺序也按取值的大小来定(值大的单元在上层,例如值为 -1 的单元在值为 -2 的单元的上面)。
该参数值使用纯整数。z-index用于绝对定位或相对定位了的要素。你也可以给图像设定z-index。(对于Communicator,最好将 <IMG>标签包在<SPAN>或 <DIV>标签内,然后将z-index应用到<SPAN>或 <DIV>。)
● 5.剪辑绝对定位单元 (clip)
绝对定位单元可以被剪辑——也即剪辑显示给用户的区域,只显示单元的一部分而把其余部分作透明处理。对于传统的基于文本和图像的网络页面,这并不是一个很有用的特征。但若要求多媒体页面,这是很有用的。如 Netscape Communivator 4 和 Internet Explorer 4 都支持多媒体页面,它们通过文档的脚本接口动态地调整单元周围的剪辑区,从而实线文本“划入”和图像渐显等显示特征。
在CSS中,剪辑通过 clip 特性来控制,这一特性只能用于绝对定位单元,其缺省值为 auto,按单元的外边缘来剪辑单元(实际上等于没有剪辑)。另外,可通过如下表达式设置剪辑框:
clip : rect(top,right.bottom,left) ;
其中 top、right、bottom 和 left 分别是矩形剪辑框的上边、右边、下边和左边相对于被剪辑单元左上角的位置。Top、right、bottom 和 left 的值可以为任意绝对或相对长度值(但不能为百分比值),或者是关键字 auto。取值为 auto 意味着剪辑区域的各边放好以后,被剪辑单元中的任何内容都不会超出这个区域。
● 6.控制单元溢出 (overflow)
固定绝对或相对定位单元的 width 和 height,很可能会因为指定的区域不能满足单元实际内容的需要,而造成单元内容溢出。这时可使用 overflow 来指定浏览器如何处理溢出:值 none(缺省值)允许浏览器显示溢出的内容,因此单元可溢出指定的区域。而值 clip 要求浏览器在单元底部和右边剪辑单元内容,这样,超出指定区域的单元内容将不显示。值 scroll 也同样要求浏览器在单元底部和右边剪辑单元内容(同 clip),不过,浏览器应该(如果可能的话)为单元提供滚动条以使用户能通过滚动来浏览被剪辑的内容。

和webpage打交道起页面布局就一直是我关注的内容,从早期table构架页面到DIV再到DIV+Table,可以说我们的需求一直在变,但是目的一直没有改变。为什么这么说,很明显从简单到复杂,再从复杂到简单;从简单运用到复杂运用;一切都是围绕需求性来做的。很多开发设计人员在从事页面布局开发的时候都要考虑到几点:布局是否合理,结构是否紧凑,是否有充分的扩展性,可读性是否强。而合理使用Table和DIV来构架我们的web是我们探讨的一个重要问题。对此要从几个不同的方位来看待:
一. 定位
首先,严格意义来说,table和div都是可用合理的布局方法,你不能否认table的价值,或者div只有优点没有缺点。可以说web架构即可以使用table也可以div。那么关键就是你对你的web需求的定位。
我们需要考虑到web页面给我们的site会带来多大的影响。如我们的site针对的海量的访问,海量的数据,(当然cache问题这里不讨论)那么在构架上通常会减少table使用量,尤其是大量的循环的时候,当然table也是有用的。对于复杂的site来说,div+css有时候很难准确定义出我们所表达的内容这时候table就是很好的选择。这也是开发前要考虑的问题,在要达到开发目的的同时保证所需要的成本。同样用div+css来实现一个结构复杂的page时往往不如用一个table就能简单得搞定。
二. 特性
table和div有其各自得特征。这也意味着他们价值取向有不同,对于开发设计的人员来说很重要的。
table可以很容易建立起结构化的界面,通过table自身的参数定义,我们能把页面布局很快定义成我们所需要的效果。当然css的配合就可以相对减少。缺点就是扩展性和可读性相对较差,扩展性差表现在维护和修正上面,一个复杂的table布局的site,她的海量页面在随着需求变化下,开发人员将一筹莫展,大量的修改需求会把web的界面开发工作彻底粉碎。可读性差,这个也是相对来说,来看个例子:我们用同一效果table和div来显示一个 page
----table----
<table width="300" border="0" cellspacing="0" cellpadding="0">
<tr>
<td rowspan="3" bgcolor="#FF0000"> </td>
<td> </td>
<td rowspan="3" bgcolor="#0000FF"> </td>
</tr>
<tr>
<td> </td>
</tr>
<tr>
<td> </td>
</tr>
</table>
----table----
----div 1----
<div style="width:100px;background-color:red;"></div>
<div style="width:100px;background-color:white;">
<div></div>
<div></div>
<div></div>
</div>
<div style="width:100px;background-color:blue;"></div>
----div 1----
----div 2----
<div style="display:inline-table; width:300px;">
<div style="float:left; width:200px; clear:left">
<div style="display:inline-table;">
<div style="float:left;clear:left;width:100px; background-color:red;"></div>
<div style="float:right; clear:right; width:100px;">
<div></div>
<div></div>
<div></div>
</div>
</div>
</div>
<div style="float:right; width:100px; clear:right; background-color:blue;"></div>
</div>
----div 2----
...
table在表现上更加“严谨”,有局限性。在表现复杂的结构时会非常难懂。往往我们的website的程序内容又十分庞大,对开发人员来说要把代码马上从头脑中有清晰的轮廓十分不易。
div呢,在html语法中我们知道div的含有和作用,如果用它来实现布局页面的话,几乎完全要靠css来支撑,可以说div不能单独使用,尤其是针对性强的web,给用户视觉上的效果要求十分严格,div的使用要配合专业的css参数来实现。从前面的例子可以看出div布局更加灵活,能简单也能够复杂。相同的显示效果在css和div的配合上可以产生不同的搭配方式。扩展性强是她的优点,开发设计人员只要对相应的css做调整就能让布局焕然一新,这点是table远远不及的。但在对结构相对复杂的局部,往往div+css开发难度高,一个简单效果div和css要写半天,这点table就好很多了,用dw之类的所见即所得的软件下我们可以轻易做出用div+css写半天才能做出的东西。

c3p0数据源默认配置

分类:Java  来源:网络  时间:2011-1-28 9:05:36

Initializing c3p0 pool...
com.mchange.v2.c3p0.ComboPooledDataSource [
 acquireIncrement -> 3,
 acquireRetryAttempts -> 30,
 acquireRetryDelay -> 1000,
 autoCommitOnClose -> false,
  automaticTestTable -> null,
 breakAfterAcquireFailure -> false,
 checkoutTimeout -> 0,
 connectionCustomizerClassName -> null,
 connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester,
 dataSourceName -> 1hge5wi8d1ai640lmkau5c|1991de1,
 debugUnreturnedConnectionStackTraces -> false,
 description -> null,
 driverClass -> com.mysql.jdbc.Driver,
 factoryClassLocation -> null,
 forceIgnoreUnresolvedTransactions -> false,
 identityToken -> 1hge5wi8d1ai640lmkau5c|1991de1,
 idleConnectionTestPeriod -> 0,
 initialPoolSize -> 3,
 jdbcUrl -> jdbc:mysql://localhost:3306/test,
 maxAdministrativeTaskTime -> 0,
 maxConnectionAge -> 0,
 maxIdleTime -> 0,
 maxIdleTimeExcessConnections -> 0,
 maxPoolSize -> 15,
 maxStatements -> 0,
 maxStatementsPerConnection -> 0,
 minPoolSize -> 3,
 numHelperThreads -> 3,
 numThreadsAwaitingCheckoutDefaultUser -> 0,
 preferredTestQuery -> null,
 properties -> {user=******, password=******},
 propertyCycle -> 0,
 testConnectionOnCheckin -> false,
 testConnectionOnCheckout -> false,
 unreturnedConnectionTimeout -> 0,
 usesTraditionalReflectiveProxies -> false
]

Hibernate hql时间比较

分类:Hibernate  来源:网络  时间:2011-1-27 23:48:53

 

/**
     * 获取现在时间
     *
     * @return 返回时间类型 yyyy-MM-dd HH:mm:ss
     */ 
     public static java.sql.Timestamp getNowSqlDate() {
     Date currentTime = new Date();
     SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
     String dateString = formatter.format(currentTime);
     java.sql.Timestamp currentTime_2 = java.sql.Timestamp.valueOf(dateString);
     return currentTime_2;
     }

返回24时制当前时间yyyy-MM-dd HH:mm:ss 如果yyyy-MM-dd hh:mm:ss则返回12时制的当前时间

hql代码

Map filterMap1=new HashMap();         
filterMap1.put("kcjyid", kcjy.getId().toString());
filterMap1.put("nowtime", DateUtil.getNowSqlDate());

Collection<Object> teachksaplist=(Collection<Object>) serviceFactory.getAdminService().list1("from Ksap as b where b.kcjyId=:kcjyid and b.ksend <:nowtime order by b.ksend ", page, 1, filterMap1);

ksend 为Ksap 实体中的属性类型为Date
 

PHP生成图片验证码

分类:PHP  来源:网络  时间:2011-1-25 0:09:45

<?php
session_start();
//生成验证码图片
Header("Content-type: image/PNG");
$im = imagecreate(44,18);
$back = ImageColorAllocate($im, 245,245,245);
imagefill($im,0,0,$back); //背景
srand((double)microtime()*1000000);
//生成4位数字
for($i=0;$i<4;$i++){
$font = ImageColorAllocate($im, rand(100,255),rand(0,100),rand(100,255));
$authnum=rand(1,9);
$vcodes.=$authnum;
imagestring($im, 5, 2+$i*10, 1, $authnum, $font);
}
for($i=0;$i<100;$i++) //加入干扰象素
{
$randcolor = ImageColorallocate($im,rand(0,255),rand(0,255),rand(0,255));
imagesetpixel($im, rand()%70 , rand()%30 , $randcolor);
}
ImagePNG($im);
ImageDestroy($im);
$_SESSION[¹VCODE¹] = $vcodes;
?>

Java线程sleep的用法入门

分类:个人日志  来源:网络  时间:2011-1-24 23:58:51

public class SleepTest extends Thread{
 double total;
 public void run(){
  for(int i=0;i<=10000000;i++){
   total+=i;
  }
  System.out.println("Finished!");
  Thread.sleep(10);
 }

 public static void main(String[] args) {
  // TODO Auto-generated method stub
  SleepTest mt=new SleepTest();
  mt.start();
  try{
   mt.join(10);
  }catch(InterruptedException e){
   e.getMessage();
  }
  System.out.println("Total="+mt.total);
 }

}
 

Oracle游标

分类:Oracle  来源:网络  时间:2011-1-24 23:43:49

查询 
    SELECT语句用于从数据库中查询数据,当在PL/SQL中使用SELECT语句时,要与INTO子句一起使用,查询的返回值被赋予INTO子句中的变量,变量的声明是在DELCARE中。SELECT             INTO语法如下: 
     SELECT [DISTICT|ALL]{*|column[,column,...]} 
     INTO (variable[,variable,...] |record) 
     FROM {table|(sub-query)}[alias] 
     WHERE............ 
    PL/SQLSELECT语句只返回一行数据。如果超过一行数据,那么就要使用显式游标(对游标的讨论我们将在后面进行),INTO子句中要有与SELECT子句中相同列数量的变量。INTO子句中也可以是记录变量。 

%TYPE属性 
     在PL/SQL中可以将变量和常量声明为内建或用户定义的数据类型,以引用一个列名,同时继承他的数据类型和大小。这种动态赋值方法是非常有用的,比如变量引用的列的数据类型和大小改变了,如果使用了%TYPE,那么用户就不必修改代码,否则就必须修改代码。 

例: 
  v_empno SCOTT.EMP.EMPNO%TYPE; 
  v_salary EMP.SALARY%TYPE; 
   不但列名可以使用%TYPE,而且变量、游标、记录,或声明的常量都可以使用%TYPE。这对于定义相同数据类型的变量非常有用。 
    DELCARE 
    V_A NUMBER(5):=10; 
    V_B V_A%TYPE:=15; 
    V_C V_A%TYPE; 
    BEGIN 
      DBMS_OUTPUT.PUT_LINE 
      (¹V_A=¹||V_A||¹V_B=¹||V_B||¹V_C=¹||V_C); 
    END 
    
    SQL>;/ 
    V_A=10 V_B=15 V_C= 
     PL/SQL procedure successfully completed. 
     SQL>; 
     
其他DML语句 
    其它操作数据的DML语句是:INSERTUPDATEDELETELOCK TABLE,这些语句在PL/SQL中的语法与在SQL中的语法相同。我们在前面已经讨论过DML语句的使用这里就不再重复了。在DML语句中可以使用任何在DECLARE部分声明的变量,如果是嵌套块,那么要注意变量的作用范围。 

例: 
  CREATE OR REPLACE PROCEDURE FIRE_EMPLOYEE (pempno in number) 
   AS 
    v_ename EMP.ENAME%TYPE; 
    BEGIN 
     SELECT ename INTO v_ename 
      FROM emp 
      WHERE empno=p_empno; 
      INSERT INTO FORMER_EMP(EMPNO,ENAME) 
      VALUES (p_empno,v_ename); 
      DELETE FROM emp 
      WHERE empno=p_empno; 
      UPDATE former_emp 
      SET date_deleted=SYSDATE 
      WHERE empno=p_empno; 
      
    EXCEPTION 
       WHEN NO_DATA_FOUND THEN 
       DBMS_OUTPUT.PUT_LINE(¹Employee Number Not Found!¹); 
    END 

DML语句的结果 
    当执行一条DML语句后,DML语句的结果保存在四个游标属性中,这些属性用于控制程序流程或者了解程序的状态。当运行DML语句时,PL/SQL打开一个内建游标并处理结果,游标是维护查询结果的内存中的一个区域,游标在运行DML语句时打开,完成后关闭。隐式游标只使用SQL%FOUND,SQL%NOTFOUND,SQL%ROWCOUNT三个属性.SQL%FOUND,SQL%NOTFOUND是布尔值,SQL%ROWCOUNT是整数值。 

SQL%FOUNDSQL%NOTFOUND 
    在执行任何DML语句前SQL%FOUNDSQL%NOTFOUND的值都是NULL,在执行DML语句后,SQL%FOUND的属性值将是: 

. TRUE :INSERT 
. TRUE :DELETEUPDATE,至少有一行被DELETEUPDATE. 
. TRUE :SELECT INTO至少返回一行 
SQL%FOUNDTRUE,SQL%NOTFOUNDFALSE。 

SQL%ROWCOUNT 
   在执行任何DML语句之前,SQL%ROWCOUNT的值都是NULL,对于SELECT             INTO语句,如果执行成功,SQL%ROWCOUNT的值为1,如果没有成功,SQL%ROWCOUNT的值为0,同时产生一个异常NO_DATA_FOUND. 

SQL%ISOPEN 
  SQL%ISOPEN是一个布尔值,如果游标打开,则为TRUE, 如果游标关闭,则为FALSE.对于隐式游标而言SQL%ISOPEN总是FALSE,这是因为隐式游标在DML语句执行时打开,结束时就立即关闭。 

事务控制语句 
    事务是一个工作的逻辑单元可以包括一个或多个DML语句,事物控制帮助用户保证数据的一致性。如果事务控制逻辑单元中的任何一个DML语句失败,那么整个事务都将回滚,在PL/SQL中用户可以明确地使用COMMITROLLBACKSAVEPOINT以及SET TRANSACTION语句。 
     COMMIT语句终止事务,永久保存数据库的变化,同时释放所有LOCK,ROLLBACK终止现行事务释放所有LOCK,但不保存数据库的任何变化,SAVEPOINT用于设置中间点,当事务调用过多的数据库操作时,中间点是非常有用的,SET TRANSACTION用于设置事务属性,比如read-write和隔离级等。 

显式游标 
    当查询返回结果超过一行时,就需要一个显式游标,此时用户不能使用select into语句。PL/SQL管理隐式游标,当查询开始时隐式游标打开,查询结束时隐式游标自动关闭。显式游标在PL/SQL块的声明部分声明,在执行部分或异常处理部分打开,取数据,关闭。 

使用游标 
    这里要做一个声明,我们所说的游标通常是指显式游标,因此从现在起没有特别指明的情况,我们所说的游标都是指显式游标。要在程序中使用游标,必须首先声明游标。 

声明游标 
语法: 
    CURSOR cursor_name IS select_statement; 

PL/SQL中游标名是一个未声明变量,不能给游标名赋值或用于表达式中。 

例: 
    DELCARE 
    CURSOR C_EMP IS SELECT empno,ename,salary 
    FROM emp 
    WHERE salary>;2000 
    ORDER BY ename; 
    ........ 
    BEGIN 
    在游标定义中SELECT语句中不一定非要表可以是视图,也可以从多个表或视图中选择的列,甚至可以使用*来选择所有的列 。 
    
打开游标 
使用游标中的值之前应该首先打开游标,打开游标初始化查询处理。打开游标的语法是: 
    OPEN cursor_name 
       cursor_name是在声明部分定义的游标名。 
    
例: 
     OPEN C_EMP; 
        
关闭游标 
语法: 
     CLOSE cursor_name 
    
例: 
     CLOSE C_EMP; 

从游标提取数据 
    从游标得到一行数据使用FETCH命令。每一次提取数据后,游标都指向结果集的下一行。语法如下: 
     FETCH cursor_name INTO variable[,variable,...] 
     对于SELECT定义的游标的每一列,FETCH变量列表都应该有一个变量与之相对应,变量的类型也要相同。 

例: 
   SET SERVERIUTPUT ON 
   DECLARE 
     v_ename EMP.ENAME%TYPE; 
     v_salary EMP.SALARY%TYPE; 
     CURSOR c_emp IS SELECT ename,salary FROM emp; 
     BEGIN 
       OPEN c_emp; 
          FETCH c_emp INTO v_ename,v_salary; 
            DBMS_OUTPUT.PUT_LINE(¹Salary of Employee¹|| v_ename ||¹is¹|| v_salary); 
          FETCH c_emp INTO v_ename,v_salary; 
            DBMS_OUTPUT.PUT_LINE(¹Salary of Employee¹|| v_ename ||¹is¹|| v_salary); 
          FETCH c_emp INTO v_ename,v_salary; 
            DBMS_OUTPUT.PUT_LINE(¹Salary of Employee¹|| v_ename ||¹is¹|| v_salary); 
       CLOSE c_emp; 
     END 
     
    这段代码无疑是非常麻烦的,如果有多行返回结果,可以使用循环并用游标属性为结束循环的条件,以这种方式提取数据,程序的可读性和简洁性都大为提高,下面我们使用循环重新写上面的程序: 
SET SERVERIUTPUT ON 
DECLARE 
v_ename EMP.ENAME%TYPE; 
v_salary EMP.SALARY%TYPE; 
CURSOR c_emp IS SELECT ename,salary FROM emp; 
BEGIN 
  OPEN c_emp; 
    LOOP 
      FETCH c_emp INTO v_ename,v_salary; 
      EXIT WHEN c_emp%NOTFOUND; 
      DBMS_OUTPUT.PUT_LINE(¹Salary of Employee¹|| v_ename ||¹is¹|| v_salary); 
END 

记录变量 
    定义一个记录变量使用TYPE命令和%ROWTYPE,关于%ROWsTYPE的更多信息请参阅相关资料。 
    记录变量用于从游标中提取数据行,当游标选择很多列的时候,那么使用记录比为每列声明一个变量要方便得多。 
    当在表上使用%ROWTYPE并将从游标中取出的值放入记录中时,如果要选择表中所有列,那么在SELECT子句中使用*比将所有列名列出来要安全得多。 

例: 
SET SERVERIUTPUT ON 
DECLARE 
R_emp EMP%ROWTYPE; 
CURSOR c_emp IS SELECT * FROM emp; 
BEGIN 
  OPEN c_emp; 
   LOOP 
     FETCH c_emp INTO r_emp; 
     EXIT WHEN c_emp%NOTFOUND; 
     DBMS_OUT.PUT.PUT_LINE(¹Salary of Employee¹||r_emp.ename||¹is¹|| r_emp.salary); 
   END LOOP; 
  CLOSE c_emp; 
END; 

%ROWTYPE也可以用游标名来定义,这样的话就必须要首先声明游标: 

SET SERVERIUTPUT ON 
DECLARE 
CURSOR c_emp IS SELECT ename,salary FROM emp; 
R_emp c_emp%ROWTYPE; 
BEGIN 
 OPEN c_emp; 
  LOOP 
    FETCH c_emp INTO r_emp; 
    EXIT WHEN c_emp%NOTFOUND; 
    DBMS_OUT.PUT.PUT_LINE(¹Salary of Employee¹||r_emp.ename||¹is¹|| r_emp.salary); 
  END LOOP; 
 CLOSE c_emp; 
END; 

带参数的游标 
    与存储过程和函数相似,可以将参数传递给游标并在查询中使用。这对于处理在某种条件下打开游标的情况非常有用。它的语法如下: 

CURSOR cursor_name[(parameter[,parameter],...)] IS select_statement; 

定义参数的语法如下: 
   Parameter_name [IN] data_type[{:=|DEFAULT} value] 

  与存储过程不同的是,游标只能接受传递的值,而不能返回值。参数只定义数据类型,没有大小。 
   另外可以给参数设定一个缺省值,当没有参数值传递给游标时,就使用缺省值。游标中定义的参数只是一个占位符,在别处引用该参数不一定可靠。 

在打开游标时给参数赋值,语法如下: 

OPEN cursor_name[value[,value]....]; 
参数值可以是文字或变量。 

例: 
DECALRE 
CURSOR c_dept IS SELECT * FROM dept ORDER BY deptno; 
CURSOR c_emp (p_dept VARACHAR2) IS 
  SELECT ename,salary 
  FROM emp 
  WHERE deptno=p_dept 
  ORDER BY ename 
r_dept DEPT%ROWTYPE; 
v_ename EMP.ENAME%TYPE; 
v_salary EMP.SALARY%TYPE; 
v_tot_salary EMP.SALARY%TYPE; 
BEGIN 
  OPEN c_dept; 
     LOOP 
        FETCH c_dept INTO r_dept; 
        EXIT WHEN c_dept%NOTFOUND; 
        DBMS_OUTPUT.PUT_LINE(¹Department:¹|| r_dept.deptno||¹-¹||r_dept.dname); 
        v_tot_salary:=0; 
        OPEN c_emp(r_dept.deptno); 
            LOOP 
               FETCH c_emp INTO v_ename,v_salary; 
               EXIT WHEN c_emp%NOTFOUND; 
               DBMS_OUTPUT.PUT_LINE(¹Name:¹|| v_ename||¹ salary:¹||v_salary); 
               v_tot_salary:=v_tot_salary+v_salary; 
            END LOOP; 
        CLOSE c_emp; 
        DBMS_OUTPUT.PUT_LINE(¹Toltal Salary for dept:¹|| v_tot_salary); 
     END LOOP; 
  CLOSE c_dept; 
END; 

游标FOR循环 
在大多数时候我们在设计程序的时候都遵循下面的步骤: 
1、打开游标 
2、开始循环 
3、从游标中取值 
4、检查那一行被返回 
5、处理 
6、关闭循环 
7、关闭游标 
    可以简单的把这一类代码称为游标用于循环。但还有一种循环与这种类型不相同,这就是FOR循环,用于FOR循环的游标按照正常的声明方式声明,它的优点在于不需要显式的打开、关闭、取数据,测试数据的存在、定义存放数据的变量等等。游标FOR循环的语法如下: 

FOR record_name IN 
 (corsor_name[(parameter[,parameter]...)] 
 | (query_difinition) 
LOOP 
  statements 
END LOOP; 

下面我们用for循环重写上面的例子: 
DECALRE 
CURSOR c_dept IS SELECT deptno,dname FROM dept ORDER BY deptno; 
CURSOR c_emp (p_dept VARACHAR2) IS 
 SELECT ename,salary 
 FROM emp 
 WHERE deptno=p_dept 
 ORDER BY ename 
v_tot_salary EMP.SALARY%TYPE; 
BEGIN 
   FOR r_dept IN c_dept LOOP 
     DBMS_OUTPUT.PUT_LINE(¹Department:¹|| r_dept.deptno||¹-¹||r_dept.dname); 
     v_tot_salary:=0; 
     FOR r_emp IN c_emp(r_dept.deptno) LOOP 
    DBMS_OUTPUT.PUT_LINE(¹Name:¹ || v_ename || ¹salary:¹ || v_salary);  
    v_tot_salary:=v_tot_salary+v_salary; 
     END LOOP; 
     DBMS_OUTPUT.PUT_LINE(¹Toltal Salary for dept:¹|| v_tot_salary); 
  END LOOP; 
END; 

在游标FOR循环中使用查询 
    在游标FOR循环中可以定义查询,由于没有显式声明所以游标没有名字,记录名通过游标查询来定义。 
DECALRE 
 v_tot_salary EMP.SALARY%TYPE; 
BEGIN 
  FOR r_dept IN (SELECT deptno,dname FROM dept ORDER BY deptno) LOOP 
     DBMS_OUTPUT.PUT_LINE(¹Department:¹|| r_dept.deptno||¹-¹||r_dept.dname); 
     v_tot_salary:=0; 
     FOR r_emp IN (SELECT ename,salary 
               FROM emp 
               WHERE deptno=p_dept 
               ORDER BY ename) LOOP 
       DBMS_OUTPUT.PUT_LINE(¹Name:¹|| v_ename||¹ salary:¹||v_salary); 
       v_tot_salary:=v_tot_salary+v_salary; 
     END LOOP; 
  DBMS_OUTPUT.PUT_LINE(¹Toltal Salary for dept:¹|| v_tot_salary); 
  END LOOP; 
END; 

游标中的子查询 
    语法如下: 
    
CURSOR C1 IS SELECT * FROM emp 
  WHERE deptno NOT IN (SELECT deptno 
   FROM dept 
   WHERE dname!=¹ACCOUNTING¹); 
可以看出与SQL中的子查询没有什么区别。 

游标中的更新和删除 
    在PL/SQL中依然可以使用UPDATEDELETE语句更新或删除数据行。显式游标只有在需要获得多行数据的情况下使用。PL/SQL提供了仅仅使用游标就可以执行删除或更新记录的方法。 
UPDATEDELETE语句中的WHERE CURRENT OF子串专门处理要执行UPDATEDELETE操作的表中取出的最近的数据。要使用这个方法,在声明游标时必须使用FOR UPDATE子串,当对话使用FOR UPDATE子串打开一个游标时,所有返回集中的数据行都将处于行级(ROW-LEVEL)独占式锁定,其他对象只能查询这些数据行,不能进行UPDATEDELETESELECT...FOR            UPDATE操作。 

语法: 
    FOR UPDATE [OF [schema.]table.column[,[schema.]table.column].. 
    [nowait] 
    
    在多表查询中,使用OF子句来锁定特定的表,如果忽略了OF子句,那么所有表中选择的数据行都将被锁定。如果这些数据行已经被其他会话锁定,那么正常情况下ORACLE将等待,直到数据行解锁。 

UPDATEDELETE中使用WHERE CURRENT OF子串的语法如下: 

WHERE{CURRENT OF cursor_name|search_condition} 

例: 
DELCARE 
CURSOR c1 IS SELECT empno,salary 
 FROM emp 
 WHERE comm IS NULL 
 FOR UPDATE OF comm; 
v_comm NUMBER(10,2); 
BEGIN 
   FOR r1 IN c1 LOOP 
     IF r1.salary<500 THEN 
       v_comm:=r1.salary*0.25; 
     ELSEIF r1.salary<1000 THEN 
       v_comm:=r1.salary*0.20; 
     ELSEIF r1.salary<3000 THEN 
       v_comm:=r1.salary*0.15; 
     ELSE 
         v_comm:=r1.salary*0.12; 
     END IF; 
   UPDATE emp; 
   SET comm=v_comm 
   WHERE CURRENT OF c1l; 
   END LOOP; 
END

从游标提取数据
   从游标得到一行数据使用FETCH命令。每一次提取数据后,游标都指向结果集的下一行。语法如下:
   FETCH cursor_name INTO variable[,variable,...]
   对于SELECT定义的游标的每一列,FETCH变量列表都应该有一个变量与之相对应,变量的类型也要相同。

 

例:
   SET SERVERIUTPUT ON
   DECLARE
   v_ename EMP.ENAME%TYPE;
   v_salary EMP.SALARY%TYPE;
   CURSOR c_emp IS SELECT ename,salary FROM emp;
   BEGIN
     OPEN c_emp;
       FETCH c_emp INTO v_ename,v_salary;
         DBMS_OUTPUT.PUT_LINE(¹Salary of Employee¹|| v_ename ||¹is¹|| v_salary);
       FETCH c_emp INTO v_ename,v_salary;
         DBMS_OUTPUT.PUT_LINE(¹Salary of Employee¹|| v_ename ||¹is¹|| v_salary);
       FETCH c_emp INTO v_ename,v_salary;
         DBMS_OUTPUT.PUT_LINE(¹Salary of Employee¹|| v_ename ||¹is¹|| v_salary);
     CLOSE c_emp;
   END
    
   这段代码无疑是非常麻烦的,如果有多行返回结果,可以使用循环并用游标属性为结束循环的条件,以这种方式提取数据,程序的可读性和简洁性都大为提高,下面我们使用循环重新写上面的程序:
 SET SERVERIUTPUT ON
 DECLARE
 v_ename EMP.ENAME%TYPE;
 v_salary EMP.SALARY%TYPE;
 CURSOR c_emp IS SELECT ename,salary FROM emp;
 BEGIN
 OPEN c_emp;
   LOOP
     FETCH c_emp INTO v_ename,v_salary;
     EXIT WHEN c_emp%NOTFOUND;
     DBMS_OUTPUT.PUT_LINE(¹Salary of Employee¹|| v_ename ||¹is¹|| v_salary);
 END

 记录变量
   定义一个记录变量使用TYPE命令和%ROWTYPE,关于%ROWsTYPE的更多信息请参阅相关资料。
   记录变量用于从游标中提取数据行,当游标选择很多列的时候,那么使用记录比为每列声明一个变量要方便得多。
   当在表上使用%ROWTYPE并将从游标中取出的值放入记录中时,如果要选择表中所有列,那么在SELECT子句中使用*比将所有列名列出来要得多。

 

例:
 SET SERVERIUTPUT ON
 DECLARE
 R_emp EMP%ROWTYPE;
 CURSOR c_emp IS SELECT * FROM emp;
 BEGIN
 OPEN c_emp;
   LOOP
   FETCH c_emp INTO r_emp;
   EXIT WHEN c_emp%NOTFOUND;
   DBMS_OUT.PUT.PUT_LINE(¹Salary of Employee¹||r_emp.ename||¹is¹|| r_emp.salary);
   END LOOP;
 CLOSE c_emp;
 END;

 

%ROWTYPE也可以用游标名来定义,这样的话就必须要首先声明游标:

 

SET SERVERIUTPUT ON
 DECLARE
 CURSOR c_emp IS SELECT ename,salary FROM emp;
 R_emp c_emp%ROWTYPE;
 BEGIN
 OPEN c_emp;
 LOOP
   FETCH c_emp INTO r_emp;
   EXIT WHEN c_emp%NOTFOUND;
   DBMS_OUT.PUT.PUT_LINE(¹Salary of Employee¹||r_emp.ename||¹is¹|| r_emp.salary);
 END LOOP;
 CLOSE c_emp;
 END;

 带参数的游标
   与存储过程和函数相似,可以将参数传递给游标并在查询中使用。这对于处理在某种条件下打开游标的情况非常有用。它的语法如下:

 

CURSOR cursor_name[(parameter[,parameter],...)] IS select_statement;

 

定义参数的语法如下:
   Parameter_name [IN] data_type[{:=|DEFAULT} value]

 

  与存储过程不同的是,游标只能接受传递的值,而不能返回值。参数只定义数据类型,没有大小。
   另外可以给参数设定一个缺省值,当没有参数值传递给游标时,就使用缺省值。游标中定义的参数只是一个占位符,在别处引用该参数不一定可靠。

 

在打开游标时给参数赋值,语法如下:

 

OPEN cursor_name[value[,value]....];
 参数值可以是文字或变量。

 

例:
 DECALRE
 CURSOR c_dept IS SELECT * FROM dept ORDER BY deptno;
 CURSOR c_emp (p_dept VARACHAR2) IS
 SELECT ename,salary
 FROM emp
 WHERE deptno=p_dept
 ORDER BY ename
 r_dept DEPT%ROWTYPE;
 v_ename EMP.ENAME%TYPE;
 v_salary EMP.SALARY%TYPE;
 v_tot_salary EMP.SALARY%TYPE;
 BEGIN
 OPEN c_dept;
   LOOP
     FETCH c_dept INTO r_dept;
     EXIT WHEN c_dept%NOTFOUND;
     DBMS_OUTPUT.PUT_LINE(¹Department:¹|| r_dept.deptno||¹-¹||r_dept.dname);
     v_tot_salary:=0;
     OPEN c_emp(r_dept.deptno);
         LOOP
           FETCH c_emp INTO v_ename,v_salary;
           EXIT WHEN c_emp%NOTFOUND;
           DBMS_OUTPUT.PUT_LINE(¹Name:¹|| v_ename||¹ salary:¹||v_salary);
           v_tot_salary:=v_tot_salary+v_salary;
         END LOOP;
     CLOSE c_emp;
     DBMS_OUTPUT.PUT_LINE(¹Toltal Salary for dept:¹|| v_tot_salary);
   END LOOP;
 CLOSE c_dept;
 END;

 游标FOR循环
 在大多数时候我们在设计程序的时候都遵循下面的步骤:
 1、打开游标
 2、开始循环
 3、从游标中取值
 那一行被返回
 5、处理
 6、关闭循环
 7、关闭游标
   可以简单的把这一类代码称为游标用于循环。但还有一种循环与这种类型不相同,这就是FOR循环,用于FOR循环的游标按照正常的声明方式声明,它的优点在于不需要显式的打开、关闭、取数据,测试数据的存在、定义存放数据的变量等等。游标FOR循环的语法如下:

 

FOR record_name IN
 (corsor_name[(parameter[,parameter]...)]
 | (query_difinition)
 LOOP
 statements
 END LOOP;

 

下面我们用for循环重写上面的例子:
 DECALRE
 CURSOR c_dept IS SELECT deptno,dname FROM dept ORDER BY deptno;
 CURSOR c_emp (p_dept VARACHAR2) IS
 SELECT ename,salary
 FROM emp
 WHERE deptno=p_dept
 ORDER BY ename
 v_tot_salary EMP.SALARY%TYPE;
 BEGIN
   FOR r_dept IN c_dept LOOP
   DBMS_OUTPUT.PUT_LINE(¹Department:¹|| r_dept.deptno||¹-¹||r_dept.dname);
   v_tot_salary:=0;
   FOR r_emp IN c_emp(r_dept.deptno) LOOP
   DBMS_OUTPUT.PUT_LINE(¹Name:¹ || v_ename || ¹salary:¹ || v_salary);  
   v_tot_salary:=v_tot_salary+v_salary;
   END LOOP;
   DBMS_OUTPUT.PUT_LINE(¹Toltal Salary for dept:¹|| v_tot_salary);
 END LOOP;
 END;

在游标FOR循环中使用查询
  在游标FOR循环中可以定义查询,由于没有显式声明所以游标没有名字,记录名通过游标查询来定义。
 DECALRE
 v_tot_salary EMP.SALARY%TYPE;
 BEGIN
 FOR r_dept IN (SELECT deptno,dname FROM dept ORDER BY deptno) LOOP
   DBMS_OUTPUT.PUT_LINE(¹Department:¹|| r_dept.deptno||¹-¹||r_dept.dname);
   v_tot_salary:=0;
   FOR r_emp IN (SELECT ename,salary
            FROM emp
            WHERE deptno=p_dept
            ORDER BY ename) LOOP
     DBMS_OUTPUT.PUT_LINE(¹Name:¹|| v_ename||¹ salary:¹||v_salary);
     v_tot_salary:=v_tot_salary+v_salary;
   END LOOP;
 DBMS_OUTPUT.PUT_LINE(¹Toltal Salary for dept:¹|| v_tot_salary);
 END LOOP;
 END;

 

游标中的子查询
   语法如下:
  
 CURSOR C1 IS SELECT * FROM emp
 WHERE deptno NOT IN (SELECT deptno
   FROM dept
   WHERE dname!=¹ACCOUNTING¹);
 可以看出与SQL中的子查询没有什么区别。

Java多线程入门简介

分类:Java  来源:网络  时间:2011-1-23 11:41:23

  一、理解多线程

  多线程是这样一种机制,它允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立。

  线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单。

  多个线程的执行是并发的,也就是在逻辑上“同时”,而不管是否是物理上的“同时”。如果系统只有一个CPU,那么真正的“同时”是不可能的,但是由于CPU的速度非常快,用户感觉不到其中的区别,因此我们也不用关心它,只需要设想各个线程是同时执行即可。

  多线程和传统的单线程在程序设计上最大的区别在于,由于各个线程的控制流彼此独立,使得各个线程之间的代码是乱序执行的,由此带来的线程调度,同步等问题,将在以后探讨。

  二、在Java中实现多线程

  我们不妨设想,为了创建一个新的线程,我们需要做些什么?很显然,我们必须指明这个线程所要执行的代码,而这就是在Java中实现多线程我们所需要做的一切!

  真是神奇!Java是如何做到这一点的?通过类!作为一个完全面向对象的语言,Java提供了类java.lang.Thread来方便多线程编程,这个类提供了大量的方法来方便我们控制自己的各个线程,我们以后的讨论都将围绕这个类进行。

  那么如何提供给 Java 我们要线程执行的代码呢?让我们来看一看 Thread 类。Thread 类最重要的方法是run(),它为Thread类的方法start()所调用,提供我们的线程所要执行的代码。为了指定我们自己的代码,只需要覆盖它!

  方法一:继承 Thread 类,覆盖方法 run(),我们在创建的 Thread 类的子类中重写 run() ,加入线程所要执行的代码即可。下面是一个例子:

  public class MyThread extends Thread
  {
   int count= 1, number;
   public MyThread(int num)
   {
    number = num;
    System.out.println
    ("创建线程 " + number);
   }
   public void run() {
    while(true) {
     System.out.println
      ("线程 " + number + ":计数 " + count);
     if(++count== 6) return;
    }
  }
  public static void main(String args[])
  {
   for(int i = 0;i 〈 5; i++) new MyThread(i+1).start();
  }
 }
  
  这种方法简单明了,符合大家的习惯,但是,它也有一个很大的缺点,那就是如果我们的类已经从一个类继承(如小程序必须继承自 Applet 类),则无法再继承 Thread 类,这时如果我们又不想建立一个新的类,应该怎么办呢?

  我们不妨来探索一种新的方法:我们不创建Thread类的子类,而是直接使用它,那么我们只能将我们的方法作为参数传递给 Thread 类的实例,有点类似回调函数。但是 Java 没有指针,我们只能传递一个包含这个方法的类的实例。

  那么如何限制这个类必须包含这一方法呢?当然是使用接口!(虽然抽象类也可满足,但是需要继承,而我们之所以要采用这种新方法,不就是为了避免继承带来的限制吗?)

  Java 提供了接口 java.lang.Runnable 来支持这种方法。

  方法二:实现 Runnable 接口

  Runnable接口只有一个方法run(),我们声明自己的类实现Runnable接口并提供这一方法,将我们的线程代码写入其中,就完成了这一部分的任务。但是Runnable接口并没有任何对线程的支持,我们还必须创建Thread类的实例,这一点通过Thread类的构造函数 public Thread(Runnable target);来实现。下面是一个例子:

  public class MyThread implements Runnable
  {
   int count= 1, number;
   public MyThread(int num)
   {
    number = num;
    System.out.println("创建线程 " + number);
   }
   public void run()
   {
    while(true)
    {
     System.out.println
     ("线程 " + number + ":计数 " + count);
     if(++count== 6) return;
    }
   }
   public static void main(String args[])
   {
    for(int i = 0; i 〈 5;i++) new Thread(new MyThread(i+1)).start();
   }
  }
  
  严格地说,创建Thread子类的实例也是可行的,但是必须注意的是,该子类必须没有覆盖 Thread 类的 run 方法,否则该线程执行的将是子类的 run 方法,而不是我们用以实现Runnable 接口的类的 run 方法,对此大家不妨试验一下。

  使用 Runnable 接口来实现多线程使得我们能够在一个类中包容所有的代码,有利于封装,它的缺点在于,我们只能使用一套代码,若想创建多个线程并使各个线程执行不同的代码,则仍必须额外创建类,如果这样的话,在大多数情况下也许还不如直接用多个类分别继承 Thread 来得紧凑。

  综上所述,两种方法各有千秋,大家可以灵活运用。

  下面让我们一起来研究一下多线程使用中的一些问题。

  三、线程的四种状态

  1. 新状态:线程已被创建但尚未执行(start() 尚未被调用)。

  2. 可执行状态:线程可以执行,虽然不一定正在执行。CPU 时间随时可能被分配给该线程,从而使得它执行。

  3. 死亡状态:正常情况下 run() 返回使得线程死亡。调用 stop()或 destroy() 亦有同样效果,但是不被推荐,前者会产生异常,后者是强制终止,不会释放锁。

  4. 阻塞状态:线程不会被分配 CPU 时间,无法执行。

  四、线程的优先级

  线程的优先级代表该线程的重要程度,当有多个线程同时处于可执行状态并等待获得 CPU 时间时,线程调度系统根据各个线程的优先级来决定给谁分配 CPU 时间,优先级高的线程有更大的机会获得 CPU 时间,优先级低的线程也不是没有机会,只是机会要小一些罢了。

  你可以调用 Thread 类的方法 getPriority() 和 setPriority()来存取线程的优先级,线程的优先级界于1(MIN_PRIORITY)和10(MAX_PRIORITY)之间,缺省是5(NORM_PRIORITY)。

 

 

  五、线程的同步

  由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。

  由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是 synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块。

  1. synchronized 方法:通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。如:

  public synchronized void accessVal(int newVal);
  
   synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。

  这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。

  在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问。

  synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run() 声明为 synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可以通过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问题,但是 Java 为我们提供了更好的解决办法,那就是 synchronized 块。

  2. synchronized 块:通过 synchronized关键字来声明synchronized 块。语法如下:

  synchronized(syncObject)
  {
   //允许访问控制的代码
  }
  
  synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。

  六、线程的阻塞

  为了解决对共享存储区的访问冲突,Java 引入了同步机制,现在让我们来考察多个线程对共享资源的访问,显然同步机制已经不够了,因为在任意时刻所要求的资源不一定已经准备好了被访问,反过来,同一时刻准备好了的资源也可能不止一个。为了解决这种情况下的访问控制问题,Java 引入了对阻塞机制的支持。

  阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪),学过操作系统的同学对它一定已经很熟悉了。Java 提供了大量方法来支持阻塞,下面让我们逐一分析。

  1. sleep() 方法:sleep() 允许指定以毫秒为单位的一段时间作为参数,它使得线程在指定的时间内进入阻塞状态,不能得到CPU 时间,指定的时间一过,线程重新进入可执行状态。典型地,sleep() 被用在等待某个资源就绪的情形:测试发现条件不满足后,让线程阻塞一段时间后重新测试,直到条件满足为止。

  2. suspend() 和 resume() 方法:两个方法配套使用,suspend()使得线程进入阻塞状态,并且不会自动恢复,必须其对应的resume() 被调用,才能使得线程重新进入可执行状态。典型地,suspend() 和 resume() 被用在等待另一个线程产生的结果的情形:测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果后,调用 resume() 使其恢复。

  3. yield() 方法:yield() 使得线程放弃当前分得的 CPU 时间,但是不使线程阻塞,即线程仍处于可执行状态,随时可能再次分得 CPU 时间。调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程。

  4. wait() 和 notify() 方法:两个方法配套使用,wait() 使得线程进入阻塞状态,它有两种形式,一种允许指定以毫秒为单位的一段时间作为参数,另一种没有参数,前者当对应的 notify() 被调用或者超出指定时间时线程重新进入可执行状态,后者则必须对应的 notify() 被调用。

  初看起来它们与 suspend() 和 resume() 方法对没有什么分别,但是事实上它们是截然不同的。区别的核心在于,前面叙述的所有方法,阻塞时都不会释放占用的锁(如果占用了的话),而这一对方法则相反。

  上述的核心区别导致了一系列的细节上的区别。

  首先,前面叙述的所有方法都隶属于 Thread 类,但是这一对却直接隶属于 Object 类,也就是说,所有对象都拥有这一对方法。初看起来这十分不可思议,但是实际上却是很自然的,因为这一对方法阻塞时要释放占用的锁,而锁是任何对象都具有的,调用任意对象的 wait() 方法导致线程阻塞,并且该对象上的锁被释放。

  而调用 任意对象的notify()方法则导致因调用该对象的 wait() 方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。

  其次,前面叙述的所有方法都可在任何位置调用,但是这一对方法却必须在 synchronized 方法或块中调用,理由也很简单,只有在synchronized 方法或块中当前线程才占有锁,才有锁可以释放。

  同样的道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调用必须放置在这样的 synchronized 方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件,则程序虽然仍能编译,但在运行时会出现 IllegalMonitorStateException 异常。

  wait() 和 notify() 方法的上述特性决定了它们经常和synchronized 方法或块一起使用,将它们和操作系统的进程间通信机制作一个比较就会发现它们的相似性:synchronized方法或块提供了类似于操作系统原语的功能,它们的执行不会受到多线程机制的干扰,而这一对方法则相当于 block 和wakeup 原语(这一对方法均声明为 synchronized)。

  它们的结合使得我们可以实现操作系统上一系列精妙的进程间通信的算法(如信号量算法),并用于解决各种复杂的线程间通信问题。关于 wait() 和 notify() 方法最后再说明两点:

  第一:调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。

  第二:除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用 notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。

  谈到阻塞,就不能不谈一谈死锁,略一分析就能发现,suspend() 方法和不指定超时期限的 wait() 方法的调用都可能产生死锁。遗憾的是,Java 并不在语言级别上支持死锁的避免,我们在编程中必须小心地避免死锁。

  以上我们对 Java 中实现线程阻塞的各种方法作了一番分析,我们重点分析了 wait() 和 notify()方法,因为它们的功能最强大,使用也最灵活,但是这也导致了它们的效率较低,较容易出错。实际使用中我们应该灵活使用各种方法,以便更好地达到我们的目的。

  七、守护线程

  守护线程是一类特殊的线程,它和普通线程的区别在于它并不是应用程序的核心部分,当一个应用程序的所有非守护线程终止运行时,即使仍然有守护线程在运行,应用程序也将终止,反之,只要有一个非守护线程在运行,应用程序就不会终止。守护线程一般被用于在后台为其它线程提供服务。

  可以通过调用方法 isDaemon() 来判断一个线程是否是守护线程,也可以调用方法 setDaemon() 来将一个线程设为守护线程。

  八、线程组

  线程组是一个 Java 特有的概念,在 Java 中,线程组是类ThreadGroup 的对象,每个线程都隶属于唯一一个线程组,这个线程组在线程创建时指定并在线程的整个生命期内都不能更改。

  你可以通过调用包含 ThreadGroup 类型参数的 Thread 类构造函数来指定线程属的线程组,若没有指定,则线程缺省地隶属于名为 system 的系统线程组。

  在 Java 中,除了预建的系统线程组外,所有线程组都必须显式创建。在 Java 中,除系统线程组外的每个线程组又隶属于另一个线程组,你可以在创建线程组时指定其所隶属的线程组,若没有指定,则缺省地隶属于系统线程组。这样,所有线程组组成了一棵以系统线程组为根的树。

  Java 允许我们对一个线程组中的所有线程同时进行操作,比如我们可以通过调用线程组的相应方法来设置其中所有线程的优先级,也可以启动或阻塞其中的所有线程。

  Java 的线程组机制的另一个重要作用是线程安全。线程组机制允许我们通过分组来区分有不同安全特性的线程,对不同组的线程进行不同的处理,还可以通过线程组的分层结构来支持不对等安全措施的采用。

  Java 的 ThreadGroup 类提供了大量的方法来方便我们对线程组树中的每一个线程组以及线程组中的每一个线程进行操作。

  九、总结

  在本文中,我们讲述了 Java 多线程编程的方方面面,包括创建线程,以及对多个线程进行调度、管理。我们深刻认识到了多线程编程的复杂性,以及线程切换开销带来的多线程程序的低效性,这也促使我们认真地思考一个问题:我们是否需要多线程?何时需要多线程?

  多线程的核心在于多个代码块并发执行,本质特点在于各代码块之间的代码是乱序执行的。我们的程序是否需要多线程,就是要看这是否也是它的内在特点。

  假如我们的程序根本不要求多个代码块并发执行,那自然不需要使用多线程;假如我们的程序虽然要求多个代码块并发执行,但是却不要求乱序,则我们完全可以用一个循环来简单高效地实现,也不需要使用多线程;只有当它完全符合多线程的特点时,多线程机制对线程间通信和线程管理的强大支持才能有用武之地,这时使用多线程才是值得的。 

JDK和Tomcat环境变量的配置

分类:个人日志  来源:网络  时间:2011-1-20 23:53:45

先右键“我的电脑”->“属性”->“高级”->“环境变量”->“系统变量”

看看有没有path变量,没有的话新建一个path变量,有的话直接按以下方式配置:

 

Jdk环境变量变量配置:

JAVA_HOMEC:Program FilesJavajdk1.5.0_06

CLASSPATH.;%JAVA_HOME%libdt.jar;%JAVA_HOME%lib ools.jar;

在系统变量Path的值的前面加入以下内容:%JAVA_HOME%in;(这里的分号不能省略)

 

 

 

 

Tomcat环境变量配置:

 

 

 

CATALINA_HOMED:jakarta-tomcat-5.0.30

 

CATALINA_BASEc: omcat 

TOMCAT_HOME: C:Tomcat 

PHP文件上传类

分类:个人日志  来源:网络  时间:2011-1-20 23:27:50

<?php

/**
 *
 * @name 文件管理类
 * @author lession
 *
 */

class File{
 //上传文件大小,以字节记
 var $fileSize;
 
 //上传文件类型,对于IE,识别jpg文件的类型必须是pjpeg,对于 FireFox,必须是jpeg。
 var $fileType;
 
 //上传文件名称
 var $fileName;
 
 //上传文件临时存储位置
 var $fileTemp;
 
 //由文件上传引发的错误代码
 var $fileError;

 /**
  * @name 构造函数
  */
 function File(){
  $this->fileName=$_FILES["file"]["name"];
  $this->fileType=$_FILES["file"]["type"];
  $this->fileSize=$_FILES["file"]["size"];
  $this->fileTemp=$_FILES["file"]["tmp_name"];
  $this->fileError=$_FILES["file"]["error"];
 }

 /**
  * @name 文件上传,只能是图片
  * @param $headFileName 上传文件待命名名称前缀,如20100331101126_21136.gif中的20100331101126
  * @param $fileSize 上传文件大小限制,以kb计
  * @param $dir 上传文件存放目录
  * @return $newFileName
  */
 function uploadFile($headFileName,$fileSize,$dir){
  $fileType1="image/gif";
  $fileType2="image/jpeg";
  $fileType3="image/pjpeg";
  $fileType4="image/x-png";
  //没有选择文件
  if ($this->fileName==NULL){
   //表单中没有选择文件
   if ($this->fileError==4){
    echo "<script>alert(¹Choose a picture,please!¹);</script>";
    return ;
   }
   //刚点击链接
   else{
    //echo "I¹m dead2!";
    return ;
   }
  }
  //已选择文件
  else {
   switch ($this->fileError){
    //文件大小超过了php.ini里面允许的大小
    case 1:
     echo "<script>alert(¹The size of picture must be less than 1M!¹);</script>";
     return ;
     break;
    case 3:
     echo "<script>alert(¹Error!¹);</script>";
     return ;
     break;
    default:
     if(!($this->fileType== $fileType1||$this->fileType==$fileType2||$this->fileType==$fileType3||$this->fileType==$fileType4)){
      echo "<script>alert(¹Only for *.jpg,*.jpeg,*.gif or *.png!¹);</script>";
      return ;
     }
     if(($this->fileSize/1024>$fileSize)){
      echo "<script>alert(¹The size of picture must be less than 1M!¹);</script>";
      return ;
     }else{
           $newFileName=$this->fileType==$fileType1?$headFileName."_".rand(1000,99999).".gif":($this->fileType==$fileType4?$headFileName."_".rand(1000,99999).".png":$headFileName."_".rand(1000,99999).".jpg");
         /*echo "文件名: ".$this->fileName."<br />";
         echo "文件类型: ".$this->fileType."<br />";
         echo "文件大小: ".($this->fileSize/1024)." Kb<br />";
         echo "存储临时目录: ".$this->fileTemp."<br />";*/
         if (file_exists($newFileName)){
              echo "<script>alert(¹Picture upload fail!¹);</script> ";
              return ;
           }else{
            //echo $newFileName."<br>";
            move_uploaded_file($this->fileTemp,$dir.$this->fileName);
           rename($dir.$this->fileName,$dir.$newFileName);
            //echo "存储目录: ".$dir.$newFileName;
            //echo "<script>alert(¹照片上传成功!¹);</script>";
            return $newFileName;
           }
     }
   }//switch结束
  }
 }//function uploadFile($fileSize,$dir)结束
 
 /**
  * @name 大文件上传,不能是图片
  * @param $headFileName 上传文件待命名名称前缀,如20100331101126_21136.gif中的20100331101126
  * @param $fileSize 上传文件大小限制,以kb计
  * @param $dir 上传文件存放目录
  * @return $newFileName
  */
 function uploadBigFile($headFileName,$fileSize,$dir){
  //没有选择文件
  if ($this->fileName==NULL){
   //表单中没有选择文件
   if ($this->fileError==4){
    echo "<script>alert(¹Choose a file,please!¹);</script>";
    return ;
   }
   //刚点击链接
   else{
    //echo "I¹m dead2!";
    return ;
   }
  }
  //已选择文件
  else {
   switch ($this->fileError) {
    //文件大小超过了php.ini里面允许的大小
    case 1:
     echo "<script>alert(¹The size of picture must be less than ".($fileSize/1024)."M!¹);</script>";
     return ;
     break;
    case 3:
     echo "<script>alert(¹Error!¹);</script>";
     return ;
     break;
    default:
     if(($this->fileSize/1024>$fileSize)){ 
      echo "<script>alert(¹The size of picture must be less than ".($fileSize/1024)."M!¹);</script>";
      return ;
     }else{
           $newFileName=$headFileName."_".rand(1000,99999).".".mb_substr($this->fileName,strrpos($this->fileName,".")+1,3,"utf8");
         /*echo "文件名: ".$this->fileName."<br />¹;
         echo "文件类型: ".$this->fileType."<br />";
         echo "文件大小: ".($this->fileSize/1024)." Kb<br />";
         echo "存储临时目录: ".$this->fileTemp."<br />";*/
         if (file_exists($newFileName)){
              echo "<script>alert(¹file onload fail!¹);</script> ";
              return ;
           }else{
            //echo $newFileName."<br>";
            move_uploaded_file($this->fileTemp,$dir.$this->fileName);
           rename($dir.$this->fileName,$dir.$newFileName);
            //echo "存储目录: ".$dir.$newFileName;
            //echo "<script>alert(¹照片上传成功!¹);</script>";
            return $newFileName;
           }
     }
   }//switch结束
  }
 }//function uploadBigFile($fileSize,$dir)结束

 /**
  * @name 文件修改
  * @param $headFileName 上传文件待命名名称前缀,如20100331101126_21136.gif中的20100331101126
  * @param $dir
  * @param $fileName
  * @param $fileSize
  */
 function updateFile($headFileName,$dir,$fileName,$fileSize) {
  //先删除原文件
  $this->deleteFile($dir,$fileName);
  //再新增文件
  $newFileName=$this->uploadFile($headFileName,$fileSize,$dir);
  return $newFileName;
 }

 /**
  * @name 文件删除
  * @param $fileName 要删除的文件名
  */
 function deleteFile($dir,$fileName) {
  if (file_exists($dir.$fileName))
   unlink($dir.$fileName);
  else
   echo "No such file!";
 }
 
}

?>

PHP数据库操作类

分类:个人日志  来源:网络  时间:2011-1-20 23:25:11

<?php

/**
 *
 * @name 数据库管理类
 * @author lession
 *
 */

class Database{

 var $host=¹localhost¹; //数据库地址
 var $database=¹mydb¹;   //数据库名称
 var $user=¹root¹;   //用户名
 var $password=¹mysql¹;   //密码

 /**
  * @name 构造函数
  */
  function Database(){

 }

 

 /**
  * @name 1.连接数据库
  * @return $conn
  */
  function connect(){
  $conn=mysql_connect($this->host,$this->user,$this->password) or die("<script>alert(¹数据库连接出错!¹);</script>");
  return $conn;
 }

 

 /**
  * @name 选择数据库
  * @param $conn
  * @return $result
  */
  function selectDb($conn){
  if($conn)
    $result=mysql_select_db($this->database) or die("<script>alert(¹数据库选择出错!¹);</script>");
  return $result;
 }

 

 /**
  * @name 数据添加
  * @param $table
  * @param $field
  * @param $value
  * @param $url
  * @return $result
  */
  function add($table,$field,$value,$url){
  $msg1="添加成功!";
  $msg2="添加失败!";
  $conn=$this->connect();
  $this->selectDb($conn);
  mysql_query("set names ¹utf8¹");

  $field=implode(",",$field);
  $value=implode(",",$value);
  $sql="insert into $table($field) values($value)";

  $result = mysql_query($sql) or die("<script>alert(¹新增数据出错!¹);</script>");
  $this->result($result,$msg1,$msg2,$url);
  $this->close($conn);
  return $result;
 }

 

 /**
  * @name 数据查询
  * @param $sql
  * @return $result
  */
  function select($sql){
  $conn=$this->connect();
  $this->selectDb($conn);
  mysql_query("set names ¹utf8¹");
//  echo $sql;
  $result=mysql_query($sql) or die("<script>alert(¹查询数据出错!¹);</script>");
  $this->close($conn);
  return $result;
 }

 

 /**
  * @name 数据修改1(显示修改结果)
  * @param $table
  * @param $field
  * @param $value
  * @param $id
  * @param $url
  * @return $result
  */
  function update($table,$condition,$id,$url){
  $msg1="修改成功!";
  $msg2="修改失败!";
  $conn=$this->connect();
  $this->selectDb($conn);
  mysql_query("set names ¹utf8¹");

  $condition=implode(",",$condition);
  $sql="update $table set $condition where id=$id";

  $result = mysql_query($sql) or die("<script>alert(¹修改数据出错!¹);</script>");
  $this->result($result,$msg1,$msg2,$url);
  $this->close($conn);
  return $result;
 }

 

 /**
  * @name 数据修改2(不显示修改结果)
  * @param $table
  * @param $field
  * @param $value
  * @param $id
  * @param $url
  */
  function update2($table,$condition,$id){
  $conn=$this->connect();
  $this->selectDb($conn);
  mysql_query("set names ¹utf8¹");

  $condition=implode(",",$condition);
  $sql="update $table set $condition where id=$id";

  $result = mysql_query($sql) or die("<script>alert(¹修改数据出错!¹);</script>");
  $this->close($conn);
  return $result;
 }

 

 /**
  * @name 数据删除
  * @param $table
  * @param $id
  * @param $url
  * @return $result
  */
  function del($table,$id,$url){
  $msg1="删除成功!";
  $msg2="删除失败!";
  $conn=$this->connect();
  $this->selectDb($conn);
  mysql_query("set names ¹utf8¹");

  $sql="delete from $table where id=$id";

  $result = mysql_query($sql) or die("<script>alert(¹删除数据出错!¹);</script>");
  $this->result($result,$msg1,$msg2,$url);
  $this->close($conn);
  return $result;
 }

 

 /**
  * @name 获取记录总数
  * @param $result
  * @return $count
  */
  function rows($result){
  $count=mysql_num_rows($result);
  return $count;
 }

 

 /**
  * @name 获取数据记录
  * @param $result
  * @return $fetch
  */
  function fetch($result){
  $fetch=mysql_fetch_array($result);
  return $fetch;
 }

 

 /**
  * @name 显示数据库操作结果
  * @param $result
  * @param $msg1
  * @param $msg2
  * @param $url
  */
  function result($result,$msg1,$msg2,$url){
  if($result>0)
     echo "<script>window.alert(¹$msg1¹);window.location.href=¹$url¹;</script>";
  else
     echo "<script>window.alert(¹$msg2¹);window.history.back();</script>";
 }

 

 /**
  * @name 关闭数据库连接
  * @param $conn
  */
  function close($conn){
  mysql_close($conn) or die("<script>alert(¹关闭数据库连接出错!¹);</script>");
 }

}

?>

  • 60
  • |<
  • <<
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • >>
  • >|