(轉貼)數據庫連接(內連接,外連接,交叉連接)
數據庫連接分為:內連接,外連接(左、右連接,全連接),交叉連接文章地址 : http://www.zxbc.cn/html/20080527/51189.html
交叉連接
將兩個表所有行組合,連接后的行數為兩個表行數的乘積(笛卡爾積)
語法,借用上面的例子應該是
行數應該為12行 :
posted @ 2011-11-30 17:24 AK47 閱讀(490) | 評論 (0) | 編輯 收藏
posted @ 2011-11-30 17:24 AK47 閱讀(490) | 評論 (0) | 編輯 收藏
posted @ 2011-11-23 14:33 AK47 閱讀(10126) | 評論 (4) | 編輯 收藏
posted @ 2011-11-16 15:39 AK47 閱讀(441) | 評論 (0) | 編輯 收藏
posted @ 2011-11-11 17:21 AK47 閱讀(373) | 評論 (0) | 編輯 收藏
posted @ 2011-11-11 16:19 AK47 閱讀(818) | 評論 (0) | 編輯 收藏
posted @ 2011-11-09 17:22 AK47 閱讀(1776) | 評論 (0) | 編輯 收藏
posted @ 2011-11-09 15:44 AK47 閱讀(375) | 評論 (0) | 編輯 收藏
posted @ 2011-11-08 18:35 AK47 閱讀(1443) | 評論 (0) | 編輯 收藏
posted @ 2011-11-08 15:10 AK47 閱讀(1636) | 評論 (0) | 編輯 收藏
posted @ 2011-11-01 16:56 AK47 閱讀(832) | 評論 (0) | 編輯 收藏
一、 jqGrid的加載。
1.引用相關頭文件
引入CSS:
<link href="Scripts/jquery-ui-1.8.1.custom.css" rel="stylesheet" type="text/css" />
<link href="Scripts/ui.jqgrid.css" rel="stylesheet" type="text/css" />
引入JS:
<script src="Scripts/jquery-1.5.1.js" type="text/javascript"></script>
<script src="Scripts/jquery-ui.min.js" type="text/javascript"></script>
<script src="Scripts/grid.locale-en.js" type="text/javascript"></script>
<script src="Scripts/jquery.jqGrid.min.js" type="text/javascript"></script>
因為jqGrid3.6及以后的版本集成了jQuery UI,所以,此處需要導入UI相關js和css。另外grid.locale-en.js這個語言文件必須在jquery.jqGrid.min.js之前加載,否則會出問題。
2.將jqgrid加入頁面中
根據jqGrid的文檔,要想生成一個jqGrid,最直接的方法就是:
$("#list").jqGrid(options);
其中list是頁面上的一個table:<table id="list"></table>
下面是一個簡單的例子:
<script type="text/javascript">
$(document).ready(function () {
jQuery("#list").jqGrid({
url: 'Handler.ashx',
datatype: "json",
mtype: 'GET',
colNames: ['SalesReasonID', 'Name', 'ReasonType', 'ModifiedDate'],
colModel: [
{ name: 'SalesReasonID', index: 'SalesReasonID', width: 40, align: "left", editable: true },
{ name: 'Name', index: 'Name', width: 100, align: "center" },
{ name: 'ReasonType', index: 'ReasonType', width: 100, align: "center" },
{ name: 'ModifiedDate', index: 'ModifiedDate', width: 150, align: "center", search: false }
],
rowList: [10, 20, 30],
sortname: 'SalesReasonID',
viewrecords: true,
sortorder: "desc",
jsonReader: {
root: "griddata",
total: "totalpages",
page: "currpage",
records: "totalrecords",
repeatitems: false
},
pager: jQuery('#pager'),
rowNum: 5,
altclass: 'altRowsColour',
//width: 'auto',
width: '500',
height: 'auto',
caption: "DemoGrid"
}).navGrid('#pager', { add: true, edit: true, del: true,search:false,refresh:false }); ;
})
二、 jqgrid的重要選項
具體的options參考,可以訪問jqGrid文檔關于option的章節(http://www.trirand.com/jqgridwiki/doku.php?id=wiki:options)。其中有幾個是比較常用的,重點介紹一下:
2.1 prmNames選項
prmNames是jqGrid的一個重要選項,用于設置jqGrid將要向Server傳遞的參數名稱。其默認值為:
prmNames : {
page:"page", // 表示請求頁碼的參數名稱
rows:"rows", // 表示請求行數的參數名稱
sort: "sidx", // 表示用于排序的列名的參數名稱
order: "sord", // 表示采用的排序方式的參數名稱
search:"_search", // 表示是否是搜索請求的參數名稱
nd:"nd", // 表示已經發送請求的次數的參數名稱
id:"id", // 表示當在編輯數據模塊中發送數據時,使用的id的名稱
oper:"oper", // operation參數名稱
editoper:"edit", // 當在edit模式中提交數據時,操作的名稱
addoper:"add", // 當在add模式中提交數據時,操作的名稱
deloper:"del", // 當在delete模式中提交數據時,操作的名稱
subgridid:"id", // 當點擊以載入數據到子表時,傳遞的數據名稱
npage: null,
totalrows:"totalrows" // 表示需從Server得到總共多少行數據的參數名稱,參見jqGrid選項中的rowTotal
}
2.2 jsonReader選項
jsonReader是jqGrid的一個重要選項,用于設置如何解析從Server端發回來的json數據,如果Server返回的是xml數據,則對應的使用xmlReader來解析。jsonReader的默認值為:
jsonReader : {
root: "rows", // json中代表實際模型數據的入口
page: "page", // json中代表當前頁碼的數據
total: "total", // json中代表頁碼總數的數據
records: "records", // json中代表數據行總數的數據
repeatitems: true, // 如果設為false,則jqGrid在解析json時,會根據name來搜索對應的數據元素(即可以json中元素可以不按順序);而所使用的name是來自于colModel中的name設定。
cell: "cell",
id: "id",
userdata: "userdata",
subgrid: {
root:"rows",
repeatitems: true,
cell:"cell"
}
}
假如有下面一個json字符串:
{"totalpages":"3","currpage":"1","totalrecords":"11","griddata": [{"SalesReasonID":"1","Name":"Price","ReasonType":"Other","ModifiedDate":"1998 年6月1日"},{"SalesReasonID":"2","Name":"On Promotion","ReasonType":"Promotion","ModifiedDate":"1998年6月1日"}, {"SalesReasonID":"3","Name":"Magazine Advertisement","ReasonType":"Marketing","ModifiedDate":"1998年6月1日"}, {"SalesReasonID":"4","Name":"Television Advertisement","ReasonType":"Marketing","ModifiedDate":"1998年6月1日"}, {"SalesReasonID":"5","Name":"Manufacturer","ReasonType":"Other","ModifiedDate":"1998 年6月1日"}]}
其對應的jsonReader為:jsonReader: {
root: "griddata",
total: "totalpages",
page: "currpage",
records: "totalrecords",
repeatitems: false
}
注:cell、id在repeatitems為true時可以用到,即每一個記錄是由一對id和cell組合而成,即可以適用另一種json結構。援引文檔中的例子:
repeatitems為true時:
jQuery("#gridid").jqGrid({
...
jsonReader : {
root:"invdata",
page: "currpage",
total: "totalpages",
records: "totalrecords"
},
...
});
json結構為:
{
"totalpages": "xxx",
"currpage": "yyy",
"totalrecords": "zzz",
"invdata" : [
{"id" :"1", "cell" :["cell11", "cell12", "cell13"]}, // cell中不需要各列的name,只要值就OK了,但是需要保持對應
{"id" :"2", "cell" :["cell21", "cell22", "cell23"]},
...
]
}
repeatitems為false時:
jQuery("#gridid").jqGrid({
...
jsonReader : {
root:"invdata",
page: "currpage",
total: "totalpages",
records: "totalrecords",
repeatitems: false,
id: "0"
},
...
});
json結構為:
{
"totalpages" : "xxx",
"currpage" : "yyy",
"totalrecords" : "zzz",
"invdata" : [
{"invid" : "1","invdate":"cell11", "amount" :"cell12", "tax" :"cell13", "total" :"1234", "note" :"somenote"}, // 數據中需要各列的name,但是可以不按列的順序
{"invid" : "2","invdate":"cell21", "amount" :"cell22", "tax" :"cell23", "total" :"2345", "note" :"some note"},
...
]
}
2.3 colModel的重要選項
colModel也有許多非常重要的選項,在使用搜索、排序等方面都會用到。這里先只說說最基本的。
三、 注意事項
1. 動態改變Add Form或者Edit Form中的select的內容,如:改變下圖中的Comparator下拉中的內容。
$("#list_d").navGrid('#pager_d',{add:true,edit:true,del:true,search:false,refresh:false},
{
checkOnSubmit:false, closeAfterEdit: true,recreateForm:true,
beforeInitData:function(formid){
initComparator();
},
beforeShowForm: function(formid){
$("#list_d").jqGrid('setColProp', 'Name', { editrules:{required:false},});
$('#tr_Name', formid).hide();
}
},//edit
{},//add
{}//del
)
beforeInitData, beforeShowForm在每次點擊編輯的時候都會執行。initComparator的作用是通過ajax獲取數據,然后利 用$("#list_d").jqGrid('setColProp', 'Comparator', { editoptions: { value: valueString} });來設置Comparator下拉中的內容。其中valueString的格式如下’ equal to: equal to; not equal to: not equal to’。鍵值之間用冒號隔開,2項之間用分號隔開。注意:把recreateForm設為true,否則'setColProp'只在第一次調用時有效。
2. var rowNum = parseInt($(this).getGridParam("records"), 10); 得到數據條數。
3. jQuery("#list_d").clearGridData();清空數據。
4. jQuery("#list").getCell(ids,"Key");獲取第ids行的key列。
5. $("#list").jqGrid('setSelection', "1");選中第一行。放在loadComplete:中在gird加載完成的時候自動選中第一行。 loadComplete:function(data){$("#list").jqGrid('setSelection', "1");
}
6. 對于像1中的可編輯的字段,可以設定rule,參見http://www.trirand.com/jqgridwiki/doku.php?id=wiki:common_rules#editrules
7. 修改Option,以URL為例
jQuery("#list_d").jqGrid('setGridParam',{url:"xxx.aspx",page:1}).trigger('reloadGrid');
復雜的表格可以參考jquery grid demo網站 :
posted @ 2011-11-01 14:23 AK47 閱讀(2321) | 評論 (0) | 編輯 收藏
Spring 2.5 中除了提供 @Component 注釋外,還定義了幾個擁有特殊語義的注釋,它們分別是:@Repository、@Service 和 @Controller。在目前的 Spring 版本中,這 3 個注釋和 @Component 是等效的,但是從注釋類的命名上,很容易看出這 3 個注釋分別和持久層、業務層和控制層(Web 層)相對應。雖然目前這 3 個注釋和 @Component 相比沒有什么新意,但 Spring 將在以后的版本中為它們添加特殊的功能。所以,如果 Web 應用程序采用了經典的三層分層結構的話,最好在持久層、業務層和控制層分別采用 @Repository、@Service 和 @Controller 對分層中的類進行注釋,而用 @Component 對那些比較中立的類進行注釋。
在 一個稍大的項目中,通常會有上百個組件,如果這些組件采用xml的bean定義來配置,顯然會增加配置文件的體積,查找以及維護起來也不太方便。 Spring2.5為我們引入了組件自動掃描機制,他可以在類路徑底下尋找標注了 @Component,@Service,@Controller,@Repository注解的類,并把這些類納入進spring容器中管理。它的作用 和在xml文件中使用bean節點配置組件時一樣的。要使用自動掃描機制,我們需要打開以下配置信息:
Java代碼
1. <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"
2. >
3.
4. <context:component-scan base-package=”com.eric.spring”>
5. </beans>
/*其中base-package為需要掃描的包(含所有子包)
@Service用于標注業務層組件,
@Controller用于標注控制層組件(如struts中的action),
@Repository用于標注數據訪問組件,即DAO組件,
@Component泛指組件,當組件不好歸類的時候,我們可以使用這個注解進行標注。
*/
6. @Service public class VentorServiceImpl implements iVentorService {
7. } @Repository public class VentorDaoImpl implements iVentorDao {
8. }
/*getBean的默認名稱是類名(頭字母小 寫),如果想自定義,可以@Service(“aaaaa”)這樣來指定,這種bean默認是單例的,如果想改變,可以使用 @Service(“beanName”) @Scope(“prototype”)來改變。可以使用以下方式指定初始化方法和銷毀方法(方法名任意): @PostConstruct public void init() {
*/
9. }
10. @PreDestroy public void destory() {
11. }
注入方式:
把 DAO實現類注入到service實現類中,把service的接口(注意不要是service的實現類)注入到action中,注入時不要new 這個注入的類,因為spring會自動注入,如果手動再new的話會出現錯誤,然后屬性加上@Autowired后不需要getter()和 setter()方法,Spring也會自動注入。至于更具體的內容,等對注入的方式更加熟練后會做個完整的例子上來。
注解:
在 spring的配置文件里面只需要加上<context:annotation-config/> 和<context:component-scan base-package="需要實現注入的類所在包"/>,可以使用base-package="*"表示全部的類。
<context:component-scan base-package=”com.eric.spring”>
其中base-package為需要掃描的包(含所有子包)
在接口前面標上@Autowired和@Qualifier注釋使得接口可以被容器注入,當接口存在兩個實現類的時候必須指定其中一個來注入,使用實現類首字母小寫的字符串來注入,如:
否則可以省略,只寫@Autowired 。
@Service服務層組件,用于標注業務層組件,表示定義一個bean,自動根據bean的類名實例化一個首寫字母為小寫的bean,例如Chinese實例化為chinese,如果需要自己改名字則:@Service("你自己改的bean名")。
@Controller用于標注控制層組件(如struts中的action)
@Repository持久層組件,用于標注數據訪問組件,即DAO組件
@Component泛指組件,當組件不好歸類的時候,我們可以使用這個注解進行標注。
@Service
public class VentorServiceImpl implements iVentorService {
}
@Repository
public class VentorDaoImpl implements iVentorDao {
}
getBean 的默認名稱是類名(頭字母小寫),如果想自定義,可以@Service(“aaaaa”) 這樣來指定,這種
bean默認是單例的,如果想改變,可以使用@Service(“beanName”) @Scope(“prototype”)來改變。
可以使用以下方式指定初始化方法和銷毀方法(方法名任意):
@PostConstruct
public void init() {
}
@PreDestroy
public void destory() {
}
posted @ 2011-10-10 16:46 AK47 閱讀(49714) | 評論 (3) | 編輯 收藏
注釋配置相對于 XML 配置具有很多的優勢:
因此在很多情況下,注釋配置比 XML 配置更受歡迎,注釋配置有進一步流行的趨勢。Spring 2.5 的一大增強就是引入了很多注釋類,現在您已經可以使用注釋配置完成大部分 XML 配置的功能。在這篇文章里,我們將向您講述使用注釋進行 Bean 定義和依賴注入的內容。
posted @ 2011-10-10 15:49 AK47 閱讀(330) | 評論 (0) | 編輯 收藏
數據庫的設計范式是數據庫設計所需要滿足的規范,滿足這些規范的數據庫是簡潔的、結構明晰的,同時,不會發生插入(insert)、刪除(delete)和更新(update)操作異常。反之則是亂七八糟,不僅給數據庫的編程人員制造麻煩,而且面目可憎,可能存儲了大量不需要的冗余信息。
設計范式是不是很難懂呢?非也,大學教材上給我們一堆數學公式我們當然看不懂,也記不住。所以我們很多人就根本不按照范式來設計數據庫。
實質上,設計范式用很形象、很簡潔的話語就能說清楚,道明白。本文將對范式進行通俗地說明,并以筆者曾經設計的一個簡單論壇的數據庫為例來講解怎樣將這些范式應用于實際工程。
范式說明
第一范式(1NF):數據庫表中的字段都是單一屬性的,不可再分。這個單一屬性由基本類型構成,包括整型、實數、字符型、邏輯型、日期型等。
例如,如下的數據庫表是符合第一范式:
字段1 | 字段2 | 字段3 | 字段4 |
? | ? | ? | ? |
字段1 | 字段2 | 字段3 | 字段4 | |
? | ? | 字段3.1 | 字段3.2 | ? |
很顯然,在當前的任何關系數據庫管理系統(DBMS)中,傻瓜也不可能做出不符合第一范式的數據庫,因為這些DBMS不允許你把數據庫表的一列再分成二列或多列。因此,你想在現有的DBMS中設計出不符合第一范式的數據庫都是不可能的。
第二范式(2NF):數據庫表中不存在非關鍵字段對任一候選關鍵字段的部分函數依賴(部分函數依賴指的是存在組合關鍵字中的某些字段決定非關鍵字段的情況),也即所有非關鍵字段都完全依賴于任意一組候選關鍵字。
假定選課關系表為SelectCourse(學號, 姓名, 年齡, 課程名稱, 成績, 學分),關鍵字為組合關鍵字(學號, 課程名稱),因為存在如下決定關系:
(學號, 課程名稱) → (姓名, 年齡, 成績, 學分)
這個數據庫表不滿足第二范式,因為存在如下決定關系:
(課程名稱) → (學分)
(學號) → (姓名, 年齡)
即存在組合關鍵字中的字段決定非關鍵字的情況。
由于不符合2NF,這個選課關系表會存在如下問題:
(1) 數據冗余:
同一門課程由n個學生選修,"學分"就重復n-1次;同一個學生選修了m門課程,姓名和年齡就重復了m-1次。
(2) 更新異常:
若調整了某門課程的學分,數據表中所有行的"學分"值都要更新,否則會出現同一門課程學分不同的情況。
(3) 插入異常:
假設要開設一門新的課程,暫時還沒有人選修。這樣,由于還沒有"學號"關鍵字,課程名稱和學分也無法記錄入數據庫。
(4) 刪除異常:
假設一批學生已經完成課程的選修,這些選修記錄就應該從數據庫表中刪除。但是,與此同時,課程名稱和學分信息也被刪除了。很顯然,這也會導致插入異常。
把選課關系表SelectCourse改為如下三個表:
學生:Student(學號, 姓名, 年齡);
課程:Course(課程名稱, 學分);
選課關系:SelectCourse(學號, 課程名稱, 成績)。
這樣的數據庫表是符合第二范式的,消除了數據冗余、更新異常、插入異常和刪除異常。
另外,所有單關鍵字的數據庫表都符合第二范式,因為不可能存在組合關鍵字。
第三范式(3NF):在第二范式的基礎上,數據表中如果不存在非關鍵字段對任一候選關鍵字段的傳遞函數依賴則符合第三范式。所謂傳遞函數依賴,指的是如果存在"A → B → C"的決定關系,則C傳遞函數依賴于A。因此,滿足第三范式的數據庫表應該不存在如下依賴關系:
關鍵字段 → 非關鍵字段x → 非關鍵字段y
假定學生關系表為Student(學號, 姓名, 年齡, 所在學院, 學院地點, 學院電話),關鍵字為單一關鍵字"學號",因為存在如下決定關系:
(學號) → (姓名, 年齡, 所在學院, 學院地點, 學院電話)
這個數據庫是符合2NF的,但是不符合3NF,因為存在如下決定關系:
(學號) → (所在學院) → (學院地點, 學院電話)
即存在非關鍵字段"學院地點"、"學院電話"對關鍵字段"學號"的傳遞函數依賴。
它也會存在數據冗余、更新異常、插入異常和刪除異常的情況,讀者可自行分析得知。
把學生關系表分為如下兩個表:
學生:(學號, 姓名, 年齡, 所在學院);
學院:(學院, 地點, 電話)。
這樣的數據庫表是符合第三范式的,消除了數據冗余、更新異常、插入異常和刪除異常。
鮑依斯-科得范式(BCNF):在第三范式的基礎上,數據庫表中如果不存在任何字段對任一候選關鍵字段的傳遞函數依賴則符合第三范式。
假設倉庫管理關系表為StorehouseManage(倉庫ID, 存儲物品ID, 管理員ID, 數量),且有一個管理員只在一個倉庫工作;一個倉庫可以存儲多種物品。這個數據庫表中存在如下決定關系:
(倉庫ID, 存儲物品ID) →(管理員ID, 數量)
(管理員ID, 存儲物品ID) → (倉庫ID, 數量)
所以,(倉庫ID, 存儲物品ID)和(管理員ID, 存儲物品ID)都是StorehouseManage的候選關鍵字,表中的唯一非關鍵字段為數量,它是符合第三范式的。但是,由于存在如下決定關系:
(倉庫ID) → (管理員ID)
(管理員ID) → (倉庫ID)
即存在關鍵字段決定關鍵字段的情況,所以其不符合 BCNF范式。它會出現如下異常情況:
(1) 刪除異常:
當倉庫被清空后,所有"存儲物品ID"和"數量"信息被刪除的同時,"倉庫ID"和"管理員ID"信息也被刪除了。
(2) 插入異常:
當倉庫沒有存儲任何物品時,無法給倉庫分配管理員。
(3) 更新異常:
如果倉庫換了管理員,則表中所有行的管理員ID都要修改。
把倉庫管理關系表分解為二個關系表:
倉庫管理:StorehouseManage(倉庫ID, 管理員ID);
倉庫:Storehouse(倉庫ID, 存儲物品ID, 數量)。
這樣的數據庫表是符合BCNF范式的,消除了刪除異常、插入異常和更新異常。
原帖地址: http://www.cublog.cn/u/23975/showart.php?id=391210
posted @ 2011-02-21 14:45 AK47 閱讀(321) | 評論 (0) | 編輯 收藏
與數據庫中的記錄沒有任何關系,即沒有與其相關聯的數據庫記錄.
與session沒有任何關系.即沒有通過session對象的實例對其進行任何持久化的操作
Persistent:
每個persistent狀態的實體對象都與一個session對象的實例相關聯
處于 Persistent狀態的實體對象是與數據庫中的記錄相關聯的.
Hibernate會依據persistent狀態的實體對象的屬性變化而改變數據庫中相對應的記錄.
Detached:
游離態是由持久態實體對象轉變而來的.
游離態實體不再與session對象相關聯.
游離態實體對象與數據庫中的記錄沒有直接聯系,對其所做的任何修改將不會影響到到數據庫中的數據.
游離態實體對象在數據庫有相對應的數據記錄,如果沒有被其他事務刪除.
posted @ 2011-02-14 14:26 AK47 閱讀(333) | 評論 (0) | 編輯 收藏
Java 提供了兩個類專門用于進行高精度運算BigInteger 和 BigDecimal ,盡管它們可大致劃分到與封裝器相同的類別里,但兩者都沒有對應的主類型;這兩個類都有自己的一系列方法,類似于我們針對主類型執行的操作,也就是說能用 int 或float 做的事情,用BigInteger和BigDecimal 一樣可以做,只是必須換用方法調用,而不是使用運算符。此外由于牽涉更多,所以運算速度會慢一點總之我們犧牲了速度,但換來了精度。
高精度浮點數BigDecimal
一些非整數值(如幾美元和幾美分這樣的小數)需要很精確。浮點數不是精確值,所以使用它們會導致舍入誤差。因此,使用浮點數來試圖表示象貨幣量這樣的精確數量不是一個好的想法。使用浮點數來進行美元和美分計算會得到災難性的后果。浮點數最好用來表示象測量值這類數值,這類值從一開始就不怎么精確。
從 JDK 1.3 起,Java 開發人員就有了另一種數值表示法來表示非整數:BigDecimal。BigDecimal 是標準的類,在編譯器中不需要特殊支持,它可以表示任意精度的小數,并對它們進行計算。在內部,可以用任意精度任何范圍的值和一個換算因子來表示 BigDecimal,換算因子表示左移小數點多少位,從而得到所期望范圍內的值。因此,用 BigDecimal 表示的數的形式為 unscaledValue*10-scale。
用于加、減、乘和除的方法給 BigDecimal 值提供了算術運算。由于 BigDecimal 對象是不可變的,這些方法中的每一個都會產生新的 BigDecimal 對象。因此,因為創建對象的開銷,BigDecimal 不適合于大量的數學計算,但設計它的目的是用來精確地表示小數。如果您正在尋找一種能精確表示如貨幣量這樣的數值,則 BigDecimal 可以很好地勝任該任務。
如浮點類型一樣,BigDecimal 也有一些令人奇怪的行為。尤其在使用 equals() 方法來檢測數值之間是否相等時要小心。equals() 方法認為,兩個表示同一個數但換算值不同(例如,100.00 和 100.000)的 BigDecimal 值是不相等的。然而,compareTo() 方法會認為這兩個數是相等的,所以在從數值上比較兩個 BigDecimal 值時,應該使用 compareTo() 而不是 equals()。
另外還有一些情形,任意精度的小數運算仍不能表示精確結果。例如,1 除以 9 會產生無限循環的小數 .111111...。出于這個原因,在進行除法運算時,BigDecimal 可以讓您顯式地控制舍入。movePointLeft() 方法支持 10 的冪次方的精確除法。
對于 BigDecimal,有幾個可用的構造函數。其中一個構造函數以雙精度浮點數作為輸入,另一個以整數和換算因子作為輸入,還有一個以小數的 String 表示作為輸入。要小心使用 BigDecimal(double) 構造函數, 因為如果不了解它,會在計算過程中產生舍入誤差。請使用基于整數或 String 的構造函數。
如果使用 BigDecimal(double) 構造函數不恰當,在傳遞給 JDBC setBigDecimal() 方法時,會造成似乎很奇怪的 JDBC 驅動程序中的異常。例如,考慮以下 JDBC 代碼,該代碼希望將數字 0.01 存儲到小數字段:
PreparedStatement ps =connection.prepareStatement("INSERT INTO Foo SET name=?, value=?");
ps.setString(1, "penny");
ps.setBigDecimal(2, new BigDecimal(0.01));
ps.executeUpdate();
在執行這段似乎無害的代碼時會拋出一些令人迷惑不解的異常(這取決于具體的 JDBC 驅動程序),因為 0.01 的雙精度近似值會導致大的換算值,這可能會使 JDBC 驅動程序或數據庫感到迷惑。JDBC 驅動程序會產生異常,但可能不會說明代碼實際上錯在哪里,除非意識到二進制浮點數的局限性。相反,使用 BigDecimal("0.01") 或 BigDecimal(1, 2) 構造 BigDecimal 來避免這類問題, 因為這兩種方法都可以精確地表示小數。
高精度整數BigInteger
BigInteger支持任意精度的整數,也就是說我們可精確表示任意大小的整數值;同時在運算過程中不會丟失任何信息;
在BigInteger類中有所有的基本算術運算方法,如加、減、乘、除,以及可能會用到的位運算如或、異或、非、左移、右移等。下面是一些方法的例子:當然,如果要有更多的使用方法,可以查閱java api 。
posted @ 2010-12-10 14:16 AK47 閱讀(1029) | 評論 (0) | 編輯 收藏
原帖地址: http://blog.csdn.net/fenglibing/archive/2007/08/23/1756773.aspx
其實java 本身也提供了api ,java.math.BigInteger;import java.math.BigDecimal; 也可以實現。
這里只是給出簡單的例子。
posted @ 2010-12-10 14:06 AK47 閱讀(927) | 評論 (0) | 編輯 收藏
posted @ 2010-12-10 11:22 AK47 閱讀(627) | 評論 (0) | 編輯 收藏
posted @ 2010-11-05 14:09 AK47 閱讀(317) | 評論 (0) | 編輯 收藏
posted @ 2010-11-02 14:59 AK47 閱讀(611) | 評論 (0) | 編輯 收藏
1)裝載:查找并裝載類型的二進制數據
2)連接:執行驗證,準備,和解析(可選)
a) 驗證:確保導入類型正確
b) 準備:為類變量分配內存,并將其初始化為默認值
c) 解析:把類型中的符號引用轉換成直接引用
3)初始化:把類變量初始化為默認初值
隨著Java虛擬機裝載了一個類,并執行了一些它選擇進行的驗證之后,類就可以進入準備階
段了。在準備階段,Java虛擬機為類變量分配內存,設置默認初始值:但在到達初始化階段之前,
類變量都沒有被初始化為真正的初始值。(在準備階段是不會執行Java代碼的。)在準備階段,虛
擬機把給類變量新分配的內存根據類型設置為默認值。
為了準備讓一個類或者接口被"首次主動"使用,最后一個步驟就是初始化,也就是為類變量
賦予正確的初始值。這里的”正確”初始值指的是程序員希望這個類變量所具備的起始值。正
確的初始值是和在準備階段賦予的默認初始值對比而言的。前面說過,根據類型的不同,類變
量已經被賦予了默認初始值。而正確的初始值是根據程序員制定的主觀計劃面生成的。
在Java代碼中,一個正確的初始值是通過類變量初始化語句或者靜態初始化語句給出的。
1)一個類變量初始化語句是變量聲明后面的等號和表達式:
2)靜態初始化語句是一個以static開頭的程序塊
example :
public class Example1 {
// 類變量初始化語句
static int value = (int) (Math.random()*6.0);
// 靜態初始化語句
static{
System.out.println("this is example");
}
}
所有的類變量初始化語句和類型的靜態初始化器都被Java編譯器收集在—起,放到——個特殊
的方法中。對于類來說,這個方法被稱作類初始化方法;對于接口來說,它被稱為接口初始化
方法。在類和接口的Javaclass文件中,這個方法被稱為”<clinit>”。通常的Java程序方法是無法
調用這個<clinit>方法的。這種方法只能被Java虛擬機調用
clinit>()方法
前面說過,Java編譯器把類變量初始化語句和靜態初始化浯句的代碼都放到class文件的
<clinit>()方法中,順序就按照它們在類或者接門聲明中出現的順序。
example:
public class Example1 {
static int width;
static int height = (int) (Math.random()*6.0);
static{
width = (int) (Math.random()*3.0);
}
}
java 編譯器生成下面<clinit>方法:
0 invokestatic java.lang.Math.random
3 ldc2_w 6.0 (double)
6 dmul
7 d2i
8 putstatic Example1.height
11 invokestatic java.lang.Math.random
14 ldc2_w 3.0 (double) 17 dmul
18 d2i
19 putstatic Example1.width
22 return
clinit 方法首先執行唯一的類變量初始化語句初始化heght,然后在靜態初始化語句中
初始化width(雖然它聲明在height之前,但那僅僅是聲明了類變量而不是類變量初始化語句).
除接口以外,初始化一個類之前必須保證其直接超類已被初始化,并且該初始化過程是由 Jvm 保證線程安全的。
另外,并非所有的類都會擁有一個 <clinit>() 方法。
1)如果類沒有聲明任何類變量,也沒有靜態初始化語句,那么它不會有<clinit>()方法。
2)如果聲明了類變量但是沒有使用類變量初始化語句或者靜態初始化語句初始它們,那么類不會有<clinit>()方法。
example:
public class example{
static int val;
}
3)如果類僅包含靜態 final 變量的類變量初始化語句,并且類變量初始化語句是編譯時常量表達式,類不會有<clinit>()方法。
example:
public class Example {
static final String str ="abc";
static final int value = 100;
}
這種情況java編譯器把 str 和 value 被看做是常量,jvm會直接使用該類的常量池或者在字節碼中直接存放常量值。該類不會被加載。
如果接口不包含在編譯時解析成常量的字段初始化語句,接口中就包含一個<clinit>()方法。
example:
interface Example{
int i =5;
int hoursOfSleep = (int) (Math.random()*3.0);
}
字段hoursOfSleep會被放在<clinit>()方法中(比較詭異???它被看作類變量了),而字段i被看作是編譯時常量特殊處理(JAVA語法規定,接口中的變量默認自動隱含是public static final)。
java 編譯器生成下面<clinit>方法:
0 invokestatic java.lang.Math.random
3 ldc2_w 3.0 (double)
6 dmul
7 d2i
8 putstatic Example.hoursOfSleep
11 return
主動使用和被動使用
在前面講過,Java虛擬機在首次主動使用類型時初始化它們。只有6種活動被認為是主動使
用:
1)創建類的新實例,
2)調用類中聲明的靜態方法,
3)操作類或者接口中聲明的非常量靜態字段,
4)調用JavaAPI中特定的反射方法
5)初始化一個類的子類;
6)以及指定一個類作為Java虛擬機啟動時的初始化類。
使用一個非常量的靜態字段只有當類或者接口的確聲明了這個字段時才是主動使用、比如,
類中聲明的字段可能會被子類引用;接口中聲明的字段可能會被子接口或者實現了這個接口的
類引用。對于子類、子接口和實現接口的類來說.這就是被動使用(使用它們并不會觸發
它們的初始化)。下面的例子說明了這個原理:
class NewParement{
static int hoursOfSleep = (int) (Math.random()*3.0);
static{
System.out.println("new parement is initialized.");
}
}
class NewbornBaby extends NewParement{
static int hoursOfCry = (int) (Math.random()*2.0);
static{
System.out.println("new bornBaby is initialized.");
}
}
public class Example1 {
public static void main(String[] args){
int hours = NewbornBaby.hoursOfSleep;
System.out.println(hours);
}
static{
System.out.println("example1 is initialized.");
}
}
運行結果:
example1 is initialized.
new parement is initialized.
0
NewbornBaby 沒有被初始化,也沒有被加載。
對象的生命周期
當java虛擬機創建一個新的類實例時不管明確的還是隱含的,首先要在堆中為保存對象的實例變量分配內存,包含所有在對象類中和它超類中
聲明的變量(包括隱藏的實例變量)都要分配內存。其次賦默認初值,最后賦予正確的初始值。
java編譯器為每個類都至少生成一個實例初始化方法 "<init>()"與構造方法相對應。
如果構造方法調用同一個類中的另一個構造方法(構造方法重載),它對應的init<>():
1)一個同類init<>()調用。
2)對應構造方法體代碼的調用。
如果構造方法不是通過this()調用開始,且對象不是Object 它對應的init<>():
1)一個超類init<>()調用。
2)任意實例變量初始化代碼調用。
3)對應構造方法體代碼的調用。
如果上述對象是Object,則去掉第一條。如果構造方法明確使用super()首先調用對應超類init<>()其余不變。
下面的例子詳細說明了實例變量初始化(摘自Java Language Specification)
class Point{
int x,y;
Point(){x=1;y=1;}
}
class ColoredPoint extends Point{
int color = OxFF00FF;
}
class Test{
public static void main(String[] args){
ColoredPoint cp = new ColoredPoint();
System.out.println(cp.color);
}
}
首先,為新的ColoredPoint實例分配內存空間,以存儲實例變量x,y和color;然后將這些變量初始化成默認值
在這個例子中都是0。
接下來調用無參數的ColoredPoint(),由于ColorPoint沒有聲明構造方法,java編譯器會自動提供如下的構造方
法:ColoredPoint(){super();}。
該構造方法然后調用無參數的Point(),而Point()沒有顯示的超類,編譯器會提供一個對其無參數的構造方法的
隱式調用:Point(){super();x=1;y=1}。
因此將會調用到Object();Object類沒有超類,至此遞歸調用會終止。接下來會調用Object任何實例初始化語句
及任何實例變量初始化語句。
接著執行Object()由于Object類中未聲明這樣的構造方法。因此編譯器會提供默認的構造方法object(){}。
但是執行該構造方法不會產生任何影響,然后返回。
接下來執行Point類實例變量初始化語句。當這個過程發生時,x,y的聲明沒有提供任何初始化表達式,因此這個
步驟未采取任何動作(x,y 仍為0);
接下來執行Point構造方法體,將x,y賦值為1。
接下來會執行類ColoredPoint的實例變量初始化語句。把color賦值0xFF00FF,最后執行ColoredPoint構造方法體
余下的部分(super()調用之后的部分),碰巧沒有任何語句,因此不需要進一步的動作,初始化完成。
與C++不同的是,在創建新的類實例期間,java編程語言不會為方法分派來指定變更的規則。如果調用的方法在被
初始化對象的子類中重寫,那么就是用重寫的方法。甚至新對象被完全初始化前也是如此。編譯和運行下面的例子
class Super{
Super(){printThree();}
void printThree{System.out.println("Three");}
}
class Test extends Super{
int three = (int)Math.PI; // That is 3
public static void main(String args[]){
Test t = new Test();
t.printThree();
}
void printThree(){System.out.println(three);}
}
輸出:
0
3
這表明Super類中的printThree()沒有被執行。而是調用的Test中的printThree()。
posted @ 2010-07-14 16:18 AK47 閱讀(895) | 評論 (0) | 編輯 收藏
Java虛擬機體系結構
方法區
在Java虛擬機中,被裝載類型的信息存儲在一個邏輯上被稱為方法區的內存中。
當虛擬機裝載某個類型時,它使用類裝載器定位相應的class文件,-->讀入這個class文件(一個線性的二進制流)->將它傳入虛擬機-->
虛擬機提取類型信息,并將信息存入方法區,類型中的類(靜態)變量也存儲在方法區.
方法區特點:
1)所有線程共享方法區。它是線程安全的。
2)方法區大小不是固定的。虛擬機根據需要自行調整。
3)方法區可以被垃圾回收。
對于每個被裝載的類型,虛擬機會在方法區中存儲以下信息。
1)類型的基本信息;
a)類型的全限定名
b)類型的直接超類全限定名(除非這個類型是java.lang.Objet,它沒超類)。
c)類型是類類型還是接口類型(就是說是一個類還是一個接口)。
d)類型的訪問修飾符(public ,abstract或final的某個子類)
e)任何直接超接口的全限定名的有序列表。
2)該類型的常量池
虛擬機必須為每個被裝載的類型維護一個常量池。常量池就是該類型所用常量的一個有序集合,
包括直接常量(string,integer,floating point常量)和對其他類型、字段和方法的符號引用。
池中的數據項就像數組一樣是通過索引訪問的。因為常量池存儲了相應類型所用到的所有類型、
字段和方法的符號引用,所以它在Java程序的動態連接中起著核心的作用。
3)字段信息
類型中聲明的每一個字段,方法區中必須保存下面的信息,字段在類或接口中聲明的順序也必須保存。
字段名,字段類型,字段修飾符(public private protected static final 等)
4)方法信息
類型中聲明的每一個方法,方法區中必須保存下面的信息,方法在類或接口中聲明的順序也必須保存。
方法名,返回值類型,參數數量和類型(按聲明順序),方法修飾符(public private protected static final 等)
如果方法不是抽象的或本地的還必須保存:方法字節碼,操作數棧和該方法在棧針中局部變量的大小,異常表。
5)除了常量以外的所有類(靜態)變量
這里主要說下編譯時常量:就是那些用final聲明以及編譯時已知的值初始化的類變量(例如:static final int val =5)
每個編譯時常量的類型都會復制它所有常量到它自己的常量池中或者它的字節碼流中(通常情況下編譯時直接替換字節碼)。
6)一個到類classLoader的引用
指向ClassLoader類的引用 每個類型被裝載的時候,虛擬機必須跟蹤它是由啟動類裝載器
還是由用戶自定義類裝載器裝載的。如果是用戶自定義類裝載器裝載的,那么虛擬機必須在類
型信息中存儲對該裝載器的引用:這是作為方法表中的類型數據的一部分保存的。
虛擬機會在動態連按期間使用這個信息。當某個類型引用另一個類型的時候,虛擬機會請求裝載
發起引用類型的類裝載器來裝載被引用的類型。這個動態連接的過程,對于虛擬機分離命名空間
的方式也是至關重要的。為了能夠正確地執行動態連接以及維護多個命名空間,虛擬機需要在方
法表中得知每個類都是由哪個類裝載器裝載的。
7)一個到Class類的引用
指向Class類的引用 對于每一個被裝載的類型(不管是類還是接口),虛擬機都會相應地為
它創建一個java.lang.Class類的實例(Class實例放在內存中的堆區),而且虛擬機還必須以某種方式把這個實例的引用存儲在方法區
為了盡可能提高訪問效率,設計者必須仔細設計存儲在方法區中的類型信息的數據結構,因此,
除了以上時論的原始類型信息,實現中還可能包括其他數據結構以加快訪問原始數據的速度,比如方法表。
虛擬機對每個裝載的非抽象類,都生成一個方法表,把它作為類信息的一部分保存在方法區。方法表是一個數組,
它的元素是所有它的實例可能被調用的實例方法的直接引用,包括那些從超類繼承過來的實例方法:(對于抽象類和接口,方法表沒有什么幫
助,因為程序決不會生成它們的實例。)運行時可以通過方法表快速搜尋在對象中調用的實例方法。
方法區使用的例子
class Lava{
private int speed = 5;
void flow(){
}
}
public class Volcano {
public static void main(String args[]){
Lava lava = new Lava();
lava.flow();
}
}
1)虛擬機在方法區查找Volcano這個名字,未果,載入volcano.class文件,并提取相應信息
存入方法區。
2)虛擬機開始執行Volcano類中main()方法的字節碼的時候,盡管Lava類還沒被裝載,
但是和大多數(也許所有)虛擬機實現一樣,它不會等到把程序中用到的所有類都裝載后才開
始運行程序。恰好相反,它只在需要時才裝載相應的類。
3)main()的第一條指令告知虛擬機為列在常量池第一項的類分配足夠的內存。所以虛擬機
使用指向Volcano常量池的指針找到第一項,發現它是一個對Lava類的符號引用,然后它就檢查
方法區,看Lava類是否已經被裝載了。
4)當虛擬機發現還沒有裝載過名為"Lava"的類時,它就開始查找并裝載文件“Lava.class”,
并把從讀入的二進制數據中提取的類型信息放在方法區中。
5)虛擬機以一個直接指向方法區Lava類數據的指針來替換常量池第—項(就是那個
字符串“Lava”)——以后就可以用這個指針來快速地訪問Lava類了。這個替換過程稱為常量池
解析,即把常量池中的符號引用替換為直接引用:這是通過在方法區中搜索被引用的元素實現
的,在這期間可能又需要裝載其他類。在這里,我們替換掉符號引用的“直接引用”是一個本
地指針。
6)虛擬機準備為一個新的Lava對象分配內存。此時,它又需要方法區中的信息。還記
得剛剛放到Volcano類常量池第——項的指針嗎?現在虛擬機用它來訪問Lava類型信息(此前剛放
到方法區中的),找出其中記錄的這樣一個信息:一個Lava對象需要分配多少堆空間。
7)虛擬機確定一個Lava對象大小后,就在堆上分配空間,并把這個對象實例變量speed初始化為默認初始值0
8)當把新生成的Lava對象的引用壓到棧中,main()方法的第一條指令也完成了,指令通過這個引用
調用Java代碼(該代碼把speed變量初始化為正確初始值5).另外用這個引用調用Lava對象引用的flow()方法。
堆
每個java虛擬機實例都有一個方法區以及一個堆,一個java程序獨占一個java虛擬機實例,而每個java程序都有自己的堆空間,它們不會彼此干擾,但同一個java程序的多個線程共享一個堆空間。這種情況下要考慮多線程訪問同步問題。
Java棧
一個新線程被創建時,都會得到自己的PC寄存器和一個java棧,虛擬機為每個線程開辟內存區。這些內存區是私有的,任何線程不能訪問其他線程的PC寄存器和java棧。java棧總是存儲該線程中java方法的調用狀態。包括它的局部變量,被調用時傳進來的參數,它的返回值,以及運算的中間結果等。java棧是由許多棧幀或者說幀組成,一個棧幀包含一個java方法的調用狀態,當線程調用java方法時,虛擬機壓入一個新的棧幀到該線程的java棧中。當方法返回時,這個棧幀被從java棧中彈出并拋棄。
.本地方法棧
任何本地方法接口都會使用某種本地方法餞。當線程調用Java方法時,虛擬機會創建一個新的棧幀井壓人Java棧。
然而當它調用的是本地方法時,虛擬機會保持Java棧不變,不再在線程的Java棧中壓人新的幀,虛擬機只是簡單地動態連接
并直接調用指定的本地方法。可以把這看做是虛擬機利用本地方法來動態擴展自己。
posted @ 2010-07-06 13:47 AK47 閱讀(377) | 評論 (0) | 編輯 收藏
posted @ 2010-06-10 14:17 AK47 閱讀(452) | 評論 (0) | 編輯 收藏
posted @ 2010-03-05 12:10 AK47 閱讀(261) | 評論 (0) | 編輯 收藏