2007年11月9日 星期五

Head First Java 讀後整理(7) - Numbers Matter

1、當 method 的行為皆與 instance variable 無關時,僅對 argument 有執行動作,就設定為「static」吧
static method 的使用並不需要有實際的 object 存在,如果 method 僅對 argument 有效用,設定為 static 反而可以省下 heap 寶貴的空間,例如:Math class 中的 abs() method (取絕對值)

2、要限制 non-abstract class 被初始化,可將所有 constructor 的存取權限設定為 private

3、static method 中不能使用 non-static variable
因為 static method 是在無關特定 class 的 instance 下執行,不應該參考到任何 instance variable


4、static method 中不能呼叫其他 non-static method

5、static variable 的值,對所有的 instance 來說都是相同的,也是共用的

6、static variable 會在該 class 的任何 object 建構出之前,以及 static method 執行之前就初始化完成

7、若沒有給 static variable 初始值,它就會自動被設定成預設值
long、int、shot => 0
double、float => 0.0
boolean => false
object reference => null

8、被標示為「final」的 static variable 代表它一旦被初始化後就不會更動,一般會稱為 Constant(常數),且命名方式都是大寫並以底線分隔

9、static final variable 必須初始化,否則 compiler 會顯示錯誤訊息

10、「final」不只可用在 static variable 上
final 的 variable 代表不能改變它的值
final 的 method 代表不能被 override
final 的 class 代表不能被 extend

11、若 class 已被標示為 final,其 method 就不需要標示為 final (因為那是多餘的)

12、Java 5.0 版開始加入「autoboxing」功能,能將 primitive 自動轉換為包裝過的 object
//Java 5.0 之前,沒有 Autoboxing 的功能
public void doNumsOldWay() {
ArrayList listOfNumbers = new ArrayList();
//必須以 object 的形式加入
listOfNumbers.add(new Integer(3));
//要針對 return type 進行轉換
Integer one = (Integer)listOfNumbers.get(0);
//最後還得透過 Integer class 所提供的 method 取得原值
int intOne = one.intValue();
}

//In Java 5.0,已經提供 Autoboxing 的功能
public voif doNumsNewWay() {
//建構 Integer 型別的 ArrayList
ArrayList<Integer> listOfNumbers = new ArrayList<Integer>();
//直接加!
listOfNumbers.add(3);
//compiler 會自動解開 Integer 的包裝,因此可以直接指派給 int
int num = listOfNumbers.get(0);
}

13、使用 Autoboxing 還是必須注意 object 與 primitive 之間轉換時的差異
public class TestBox {
Integer i; // object reference => 預設值為 null
int j; // primitive => 預設值為 null

public static void main(String[] args) {
TestBox t = new TestBox();
t.go();
}

public void go() {
j = i; // i 只是 object reference,預設值為 null
//以下兩行會發生錯誤,因為 i 與 j 都是 null
System.out.println(j);
System.out.println(i);

//若是換成下列寫法
i = j; // j 是 primitive,預設值為 0
//以下兩行都會正常處理而顯示 0
System.out.println(j);
System.out.println(i);
}
}

14、數字的格式化語法 => %[argument number][flags][width][.precision]type
public class NumberFormat {
public static void main(String args[]) {
System.out.println(String.format("%d", 42)); //42
System.out.println(String.format("%.3f", 42.000)); //42.000
System.out.println(String.format("%,.2f", 45674.9876)); //45,678.99
System.out.println(String.format("%,d", 10000000)); //10,000,000
System.out.println(String.format("%x", 42)); //2a
System.out.println(String.format("%c", 42)); //*

//variable argument list
//The rank is 234,567 out of 123,456.12
System.out.println(String.format("The rank is %,d out of %,.2f", 234567, 123456.12345));
}
}

15、日期的格式化
import java.util.*;
public class DateFormat {
public static void main(String args[]) {
Date today = new Date();

//完整的日期與時間:%tc
System.out.println(String.format("%tc", today)); //星期五 十一月 09 11:51:37 CST 2007
//只有時間:%tr
System.out.println(String.format("%tr", today)); //11:51:37 上午
//週、月、日:%tA %tB %td
System.out.println(String.format("%tA %tB %td", today, today, today)); //星期五 十一月 09
//同上,但不用重複給 argument
System.out.println(String.format("%tA %<tB %<td", today, today, today)); //星期五 十一月 09
}
}

16、處理日期,可使用強大的 Calendar
由於 Calendar 是 abstract class,因此無法將 Calendar 實體化,因此要改用下面方式取得 Calendar 物件:

Calendar cal = Calendar.getInstance();

透過使用 getInstance() 這個 static method,就可以取得具體 subclass 的 instance(Calendar 的 polymorphism 變化版本),然而根據 polymorphism 的機制規定,此 instance 會提供 Calendar 該提供的所有 method。





2007年11月8日 星期四

Head First Java 讀後整理(6) - Life and Death of an Object

1、「heap」為 object 的生存空間;「stack」則是 method invoke 與 local variable 的生存空間

2、Instance Variable:宣告在 class 中而非 method 中,代表每個獨立 object 的 field,存活與所屬物件中 (heap)

3、Local Variable:與 method 的參數都是被宣告在 method 中,為暫時性的,且生命週期只限於 method 被放置於 stack 的期間 (stack)

4、當呼叫 method 時,此 method 會被包含至 stack frame 後再堆到 call stack 上

5、非 primitive 的變數只是保存對 object 的參考,因此只會放在 stack 上 (所參考的 object 永遠存於 heap 中)

6、不論是 instance or local variable,objecy 本身永遠都會存在於 heap 中

7、constructor 的名稱必須與 class 名稱相同,而且沒有 return type

8、compiler 僅有在完全沒有設定 constructor 的情況下,才會預設一個不帶 argument 的 constructor

9、若 class 有一個以上的 constructor,則 argument 一定要長的不一樣 => overload

10、constructor 的存取權限可以是 public、private(class 以外不能存取)、或是不指定

11、建構新 object 時,所有繼承下來的 constructor 都會執行 (越上層的越先執行)
class Animal {
public Animal() {
System.out.println("Making a Animal");
}
}
class Hippo extends Animal {
public Hippo() {
System.out.println("Making a Hippo");
}
}

public class test {
public static void main(String args[]) {
Hippo h = new Hippo();

//輸出結果:
//Making a Animal
//Making a Hippo
}
}

12、compiler 對每個 overload 版本的 constructor 加上「super()」,而且一定是沒有 argument 的版本

13、即使要手動加入 super(),也要擺在 constructor 的第一行,否則 compiler 會顯示錯誤訊息

14、object 的 superclass 必須先完備,subclass 的部分才能建構

15、若要在 constructor 中呼叫另一個 constructor,可呼叫 this()

16、this() 僅能用在 constructor 中,且必須放在第一行

17、每個 constructor 可以選擇呼叫 super() 或是 this(),但不能同時呼叫

18、只要 stack frame 還存於 stack 上,local variable 就算存活,亦即會存活到 method 執行完畢為止

19、當最後一個 reference 消失後,object 就變成可回收的,此時就會被 GC 從 heap 中移除

20、若想要使用已經設定為 null 的 reference 來呼叫特定方法,會產生 NullPointerException

Head First Java 讀後整理(5) - Serious Polymorphism

1、有些 class 不應該被初始化,可透過在 class 前面使用「abstract」關鍵字達成
Animal ani = new Animal();  //這樣是一件很奇怪的事情

2、除了 class 之外,也可以將 method 標示為 abstract
public abstract void eat(); //沒有實作! 直接以分號結束

3、abstract class 代表此 class 必須要被 extend 過,而 abstract method 代表此 method 一定要被 override 過

4、abstract class 可以帶有 abstract method 或是 non-abstract method

5、若 class 中帶有 abstract method,則此 class 必須也是 abastract

6、有時就是沒辦法為所有 subclass 寫出有意義的共通程式碼,但還是可透過 abstract 定義出共通的協定 => 最終目的就是「polymorphism」

7、abstract method 沒有內容,它只是為了標釋出 polymorphism 的部分而存在

8、而繼承 abstract class 的 concrete class,必須要實作出所有的 abstract method (其實還是可以透過 abstract 將實作再度轉移給下一層 concrete class)
9、Java 中所有的 class 都是由 Object class 所 extend 出來的,因此使用 polymorphism 機制時可適時使用 Object 作為 argument 或 return type

10、承上,ArrayList 可以處理任何 class 的秘密在於他所用的 argument 與 return type 為 Object

11、Object 除了 polymorphism 外,還提供 Java 在 run-time 期間對任何 object 都有需要的 method 提供實作程式碼

10、任何從 ArrayList<Object> 取出的東西都會被 compiler 認為是 Object 型態的參考 (因此僅能使用 Object class 中所定義的 method)
ArrayList<Object> myDogArrayList = new ArrayList<Object>();
Dog aDog = new Dog();
myDogArrayList.add(aDog);
Dog d = myDogArrayList.get(0); //compiler 會提示錯誤,因為從 ArrayList 取出的為 Object 而非 Dog

11、若確定 Object 原來的 type,還是可以轉換為原來的 type
Object o = a1.get(0);
Dog d = (Dog)o; //轉換為原來 type
d.bark(); //此時就可以使用 Dog class 才有的 method

12、interface 本身帶有 public 與 abstract 的意義,因此這兩個關鍵字可以省略

13、實作某 interface 的 class 必須實作出它所有的 method,因為這些 method 都是 public && abstract

14、若以 interface 取代 concrete subclass 或 abstract superclass 作為 argument 或 return type,就可以傳入任何實作該 interface 的東西

15、class 僅能繼承一個 class,但卻能實作多個 interface

2007年11月7日 星期三

Head First Java 讀後整理(4) - Better Living in Objectville

1、若 subclass 打算引用 superclass 的 method 再加上額外的行為,可使用「super」關鍵字

2、繼承 => (1)避免重複的程式碼 (2)定義出共通的協議

3、使用 polymorphism,reference 與 object 可以是不同的型別 (參考型別可以是實際物件型別的 superclass)
Animal[] animals = new Animal[3];
animals[0] = new Dog(); //後面可放置任何 Animal 的 subclass 物件
animals[1] = new Cat();
animals[2] = new Wolf();

4、不僅可用在新生 object 上,polymorphism 也可以用在 argument 與 return type 上
class Vet {
public void giveShot(Animal a) {
a.makeNoise();
}
}

class PetOwner {
public void start() {
Vet v = new Vet();
v.giveShot(new Dog()); //執行 Dog 的 makeNoise()
v.giveShot(new Cat()); //執行 Cat 的 makeNoise()
}
}

5、使用 polymorphism,就可以開發出引進新型態 subclass 時也不需修改的程式

6、若要讓 class 不被繼承,可使用「final」關鍵字,或是讓 class 中僅有 private constructor

7、「final」關鍵字也可以僅讓 class 中的某一個 method 無法被 override

8、「override」的使用規則:
  • 參數必須一樣,且 return type 必須相容
  • method 的存取權不得縮小 (若原本是 public 則不能改為 private)

9、「overload」的使用規則:(僅是相同名稱的 method,與 inherit 或 polymorphism 無關)
  • return type 可以不同
  • 不能只改變 return type
  • 可以更改存取權限

Head First Java 讀後整理(3) - Using the Java Library

1、多使用 Java API,就可以上班總是遲到,下班又很準時

2、ArrayList 與 Array 的比較:
  • 一般 array 在建構時就必須決定大小;ArrayList 不必
  • 存放物件給一般 array 必須指定索引位置;ArrayList 不需要,改用 add() method
  • 一般 array 使用特殊的語法;ArrayList 提供了許多標準的 method
  • 在 Java 5.0 中的 ArrayList 是參數化(parameterized)的,例如:ArrayList<int>

3、提供各種 Java API 的 class 是被包裝在 package ;而要使用 API,必須知道它被放在那個 package 中

4、import 與 C 語言中的 include 並不同,運用 import 只是省下每個 class 前的 package 名稱,程式不會因此變大或變慢

2007年11月6日 星期二

Head First Java 讀後整理(2) - Extra-Strength Methods

1、系統開發前,首先進行高階設計,此時必須瞭解系統流程,接著要設想出需要哪些物件(以物件導向的方式思考,專注於程式中出現的事物,而非程序)

2、撰寫程式前必須思考 => 決定先建構出那個 class? 假設某些 class 需要同時運用到多個 class,又該從哪裡開始?

3、建構 Java 的 class 時,程序應該會是如下:
  • 找出 class 應該作的事情
  • 列出 instance variable 與 method
  • 編寫 method 的 precode (應該是 pseudocode)
  • 撰寫 method 測試用程式
  • 實作出 class
  • 測試 method
  • 除錯 or 重新設定

4、型別轉換(告知 compiler 以強制的方式進行轉換)
int randomNum = (int)(Math.random() * 5)

5、加強版的 for loop (即為 foreach)
String[] nameArray = new String[] { "First", "Second", "Third" };
for(String name : nameArray) { ....... }

2007年11月5日 星期一

Head First Java 讀後整理(1) - How Objects Behave

1、任一 class 的每個 instance 都帶有相同的 method,但 method 可根據 instance variable 的值來表現不同的行為
class song {
private String title;
private String artist;

public void setTitle(Atring title) {
this.title = title;
}

public void play() {
soundPlayer.playSound(title);
}

public static void main(String[] args) {
song s1 = new song();
s1.setTitle("first song");
s1.play(); //播放 first song

song s2 = new song();
s2.setTitle("second song");
s2.play(); //播放 second song
}
}
2、parameter 與 argument 的區別 => method 運用 parameter;而呼叫 method 的一方則傳入 argument

3、Java 是 pass-by-value,也就是 pass-by copy
不像 C 中的 pass-by-reference,在 method 中任意改變傳入變數的值,不會影響原本在外部的變數。

4、instance variable 的存取,請多用 setter 與 getter 的方式,亦即 Encapsulation(封裝)
如第一點中所附的程式碼,其中 title 與 artist 這兩個 instance variable 是無法被存取的,而必須透過 setTitle() 與 setArtist() 兩個 method 進行存取,雖然犧牲了點效能,但之後若是 instance variable 的方式有改變,可以不必更動透過 method 取值的其他程式。

5、instance variable 永遠都會有預設值,即使沒有指派值或是呼叫 setter,還是會有值
integer:0
floating points:0.0
booleans:false
references:null

6、local variable 沒有預設值,因此在變數初始化之前就使用,compiler 會顯示錯誤

7、使用「==」來比對 primitive,是比對其位元組合是否相同
int a = 3;
byte b = 3;
if(a == b) {....} //True

8、使用「==」來比對 reference,是比對是否參照到相同的 object
foo a = new foo();
foo b = new foo();
c = a;
if(a == b) {....} //False
if(a == c) {....} //True
if(b == c) {....} //False

2007年11月2日 星期五

Java 平台簡介

每每在學 Java 時,翻書學程式,上網找資料,總是會看到一堆名詞,像是 Java SE、Java EE、Java ME .... 等等不同平台,總覺得 Java 架構實在是複雜,想要瞭解這些名詞的相互關係為何,總是找不太到很明確的答案,所以乾脆到 Sun 的官方網站去找....

以下找到一篇很不錯的文章,是寫給新進入 Java 領域的使用者,裡面簡單扼要的說明了各種平台的不同:
  1. Java SE
    This kit is necessary for developing all applications, except those designed for consumer devices (See the Micro Edition). Java SE comes bundled with the compiler, a runtime environment, and core API.
  2. Java Platform Enterprise Edition (Java EE)
    This packages includes an application server, web server, J2EE APIs, support for Enterprise JavaBeans, Java Servlets API, and JavaServer Pages (JSP) technology. Use J2EE with the Java SE.
  3. Java Platform Micro Edition (Java ME)
    If you are interested in developing programs for Palm Pilots, screen phones, and other consumer devices, this kit provides tools for compiling, deployment and device configuration, and APIs that are specialized for each type of device.
  4. JavaFX
    JavaFX Script Technology is a highly productive scripting language that enables content developers to create rich media and content for deployment on Java environments.

另外,Java 號稱 cross platform,而是如何達到了,此文章中也是有說明:
Java programs are executed within a program called the JVM. Rather than running directly on the native operating system, the program is interpreted by the JVM for the native operating system. This is key to making your programs portable from one platform to another. In other words, you can develop your programs on a Solaris, Linux, Macintosh, or Windows, then run it on another server or platform.
稍微看了一下,大致上是瞭解各種不同 Java platform 之間的差異了,其他更詳細的內容可以多多參考 Sun 網站上的文件。

2007年10月27日 星期六

MySqlParameter 的使用 in C#

今天在寫用 C# 連結 MySQL 的程式,使用的是 MySQL AB 所提供的 Connector/Net 5.1

不過使用到 MySqlParameter 的時候,發生了一些問題.....

原本使用 SqlParameter 的時候,在設定 SQL 語法時,都會類似以下這樣...
insert into table1(col1, col2, col3) values(@col1, @col2, @col3)
上面的語法在連結 SQL Server 後可以正確執行。但是到了 MySQL 上,卻無法正確的指定 MySqlParameter 的 value (全部都會變成 Null)

後來 google 了一下,發現原來是連結 MySQL 就不一樣了! 原本的符號「@」要改為符號「?」(新版的 connector 改掉了!)

因此語法會變成以下這樣:
insert into table1(col1, col2, col3) values(?col1, ?col2, ?col3)
所以設定 MySqlParameter 時,就必須類似以下寫法:
MySqlParameter para1 = new MySqlParameter("?col1", MySqlDbType.VarChar, 50);
MySqlParameter para2 = new MySqlParameter("?col2", MySqlDbType.VarChar, 50);
MySqlParameter para3 = new MySqlParameter("?col3", MySqlDbType.VarChar, 50);
如此一來資料就可以正確新增囉!

參考資料

2007年10月23日 星期二

[phpMyEdit] 程式產生器 for 單一 Table 的編輯

今天接到老大命令,要寫個介面 maintain 某個 MySQL Table 的資料,乍聽之下真是件一點都不有趣的工作....

不過老大離開前丟了關鍵字「phpMyEdit」給我,要我去搜尋,說用這個工具應該可以很快作出來 ....

於是去搜尋一下資料,並下載試用後,發現真是個好用的工具....不過由於資料與介面的中文似乎都有問題,所以我作了以下兩個步驟來處理好中文的問題:

首先在產生出來的程式中,移到大約 90 行前後,找到 $opts['language'],將值設定為 「EN-US」。

接著開啟檔案 phpMyEdit.class.php,找到「function connect()」(大約在 3059~3079 行),在 SQL 連線後加上「mysql_query("SET NAMES 'utf8'"); 」,如此一來中文就可以正常顯示囉!

2007年10月22日 星期一

C# 取得本機 IP Address

今天在網路上搜尋如何用 C# 取得本機的 IP Address,結果範例都一長串..

後來整理了一下,結論只要以下這一行程式碼即可:
Dns.GetHostEntry(Dns.GetHostName()).AddressList[0].ToString()

2007年10月20日 星期六

[AWStats] 增加各國別(Countries)參觀詳細統計資訊

之前有接到要求,要讓顯示國家的部分出現來自各國流量所佔的比例....

恩....原本只有「網頁數」、「點擊數」「位元組」這三個選項,根本不符合要求,所以只好去看看 source code 要怎麼加上這些欄位了...

一打開 source code ....... 破萬行的程式..... Orz

看了半天,終於發現了,重點在於第 8059~8104 行,關鍵的變數為「$ShowDomainsStats」,因此我在 8049 與 8050 之間加入以下這行:
$ShowDomainsStats = 'UVPHB';
強制修改變數內容後,讓後面的顯示通通跑出來,就多出「參觀者」、「參觀次數」這兩個欄位囉! 其中還包含了百分比的計算!

其實說到底,也只是加了一行程式,把內建的功能打開而已。

2007年10月19日 星期五

[AWStats] Apache 的 Log 分析

之前將 awstats Log 分析軟體用在分析 IIS 所產生出來的 web log

這次改版為 PHP version,因此要改為分析 Apache 的 log,除了之前的設定需要注意外,基本上比較重要的在 httpd.conf 這個檔案的設定

由於 awstats 用的是 perl 去產生報表,因此 Apache 要設定為支援執行 perl 才行!

因此 httpd.conf 要作以下設定:
  1. 在 <Directory /></Directory> 目錄中加入「Options FollowSymLinks +ExecCGI」
  2. 確定有加入設定「LoadModule cgi_module modules/mod_cgi.so」
  3. 確定有加入設定「AddHandler cgi-script .cgi .pl」
  4. 為了讓 awstats 可以正確的 parse log 檔案,要將 CustomLog 的設定由「common」改為「combined」
其中前三個設定是為了讓 Apache 可以執行 perl 的檔案,最後一個則是讓 awstats 可以正確的 parse Apache 所產生出來的 log 檔案。

最後 awstats.pl 要將第一行的「#!/usr/bin/perl」改為「#!C:\Perl\bin\perl.exe」(由於我的環境是在 Windows 下,裝的是 ActivePerl;若是在 Linux 環境下就不需要修改這個部分了)

PS. 我用的架站套件是 xampp

參考資料
  1. AWStats Installation, Configuration and Reporting
  2. How to Add Perl CGI Script Support to Your Apache Server on Windows
  3. [教學] 超級網站記錄分析 Awstats 安裝指南(windows)

2007年10月18日 星期四

設定 MS SQL Server 2005 允許遠端連線

之前找台主機裝了 MS SQL Server 2005,然後要讓外部的主機可以連線到 SQL Server,發現到 SQL Server 2005 真是越作越安全的......@_@

之前 2000 只要 firewall tcp 3389 打開就好了,2005 只開 firewall 是不夠的,還要進行額外的設定,因此把所需要的設定條列如下:

首先,開啟 Microsoft SQL Server 2005 > Mircosoft SQL Server Management Studio,在左邊 Object Explorer 中的 SQL Server 主機名稱上按滑鼠右鍵,選擇「Properties」,會出現 Server Properties 的視窗。選擇「Connections」,並勾選「Allow remote connections to this server」

接著開啟 Microsoft SQL Server 2005 > Configuration Tools > SQL Server Surface Area Configuration,點選「Surface Area Configuration for Services and Connections」,在左邊的部分選到「MSSQLSERVER -> Database Engine -> Remote Connections」,最後選擇「Local and remote connections」(原本的選項是 Local connections only)再儲存設定。

最後重新啟動 SQL Server service 即可囉!

參考資料

2007年10月17日 星期三

Alchemi:小型 Windows Grid 的 middleware 首選

由於研究內容與 Grid 有所相關,因此之前想用 GT4 或 gLite 去架設 Grid 環境。

不過後來發現,用上面兩個實在是沒必要....

一來這兩個是蠻龐大的 middleware,有的沒的要搞一堆,例如:certificate。

二來研究的重心並不是在 Grid 環境的架設與運作,而是要證明 Grid 是不是可以協助我改善某些工作的處理效率。

再加上最近看了 80/20 相關法則的書,所以.......決定還是找一套輕巧好用的 Grid middleware 就行啦! (其實是我有點懶.....@_@)

之後在 google 發現了 Alchemi,以下是 Alchemi 官方網站的介紹與架構圖:
Alchemi is a .NET grid computing framework that allows you to painlessly aggregate the computing power of intranet and Internet-connected machines into a virtual supercomputer (computational grid) and to develop applications to run on the grid.


乍看之下,哇~號稱 painless 耶! 真的有這麼好嗎?

接著仔細研究了一下,發現他幾乎都把底層的部分做完了,說實在省了 developer 不少的工,因此 developer 只要專注於程式要做什麼就好,不太需要瞭解 job 是怎麼丟出去或是成功回傳....等等太細節的東西;不過基本上還是要瞭解一下在 Grid 環境中程式的寫法,因為跟一般的程式差異頗大。

但說到底,還真是感謝有如此好物出現....讓我省了好多麻煩阿....雖然架構看起來很簡單,不過實在是足夠滿足我研究要做的東西了!

Alchemi 運作的基本概念如下:
  1. 是以 Manager 為中心,由 User 送出 Grid Application
  2. Application 中包含許多 Threads,每個 Thread 有其需要負責的工作,由 Manager 將每個 Thread 送到各 Executor 去執行
  3. 最後由 Executor 回傳處理結果,再由 Manager 整合

上面所介紹的僅僅是基本概念,當然按照此基本概念去寫程式,就會有一些侷限,例如:
  • Thread Job 無法使用非 .NET Framework 所提供的 library (這個真的很麻煩,許多好用的 third party library 就無法使用了)
  • Manager 僅能被動的等待 Executor 回傳處理結果再進行整合 (如此一來無法主導整個程式的運作,也不是很方便)


這是目前所遇見的問題,後來去找教授討論後,他要求程式要修改的更完善,後來思考很久,在原本的運作模式下根本無法達成要求,因此又回來重新仔細翻翻 Alchemi 的文件,發現了一件事情......原來我疏忽了很多東西阿!! 原來 Alchemi 提供了許多我需要的功能.....(此時要再次向作者跪拜....Orz)


當然以上所提只是基本概念,其中 Alchemi 還提供了幾個很重要的功能: (後來仔細翻文件才發現....)
  1. 在 run-time 期間,Application 與 Thread 其實都是可以控制的,Manager 並非只能被動等待 Thread 工作完成。(前提是權限足夠的情況下)
  2. 透過設定 module dependency 後,就可以使用 third party library,如此一來還可以減少 Manager 與 Executor 之間資料的傳輸 (這真是令人振奮的發現!!)
  3. 由於在某些情況下,不能保證剛開始就可以確定所有執行的相關資訊,因此可以在 run-time 期間增加 Thread 到 Application 中

最後,當然也是有美中不足之處,就是目前 Thread 之間沒有辦法作訊息的交換,因此在開發程式時需要注意到有此問題! 因此在 concurrency control 就無法非常精確(不過在大多數情況下,這並無傷大雅....)。

之後預計要推出 Java API,不過目前還沒看到,看樣子作者似乎也很忙,短時間內大概也不會出現吧! 不過沒關係,這套 middleware 基本上還可以算是麻雀雖小,五臟俱全啦!

2007年9月28日 星期五

.NET 2.0 中 Crystal Report 到達處理上限的解決方式

今天系統的報表出現了一些問題,其中一個最重要的訊息如下:
已經到達您系統管理員所設定的最大報表處理工作限制
後來找到解決方式,只要修改登錄檔中「PrintJobLimit」的上限值,再重新啟動 IIS 即可!

參考連結
  1. CrystalReport 已經到達您系統管理員所設定的最大報表處理工作限制
  2. Error: "System.Exception: Load report failed…” when a web application processes a large print job

2007年9月25日 星期二

Java 學習筆記 (10) - Reflection

class 的載入與檢視

class 載入

為了妥善使用有限的資源,Java 在真正需要使用到 class 的時候才會將其載入,當每一個 class 被載入時,JVM 就會為其自動產生一個 Class object。

【註】每一個 class 載入僅會有一個 Class object。因此即使使用同一個 class 產生多次 object,也只會有一個與其對應 Class object。

每一個 object 都可以透過 getClass() 的方式取得 Class object,以下為一個簡單的範例:
public class ClassDemo {
public static void main(String args[]) {
String name = "godleon";
Class stringClass = name.getClass();

System.out.println("類別名稱:" + stringClass.getName());
System.out.println("是否為介面:" + stringClass.isInterface());
System.out.println("是否為基本型態:" + stringClass.isPrimitive());
System.out.println("是否為陣列物件:" + stringClass.isArray());
System.out.println("父類別名稱:" + stringClass.getSuperclass().getName());
}
}

而 Java 只有在真正要用到 class 的時候才會將其載入,而真正用到的時候是指以下情況:
  1. 使用 class 生成 object 時
  2. 使用者指定要載入 class (利用 Class.forName() 或是 ClassLoader.loadClass())

若僅是宣告並不會載入 class,以下用一段程式碼來測試:

TestClass.java
public class TestClass {
static {
System.out.println("類別被載入");
}
}
LoadClassTest.java
public class LoadClassTest {
public static void main(String args[]) {
TestClass test = null; //class不會載入,因此不會顯示「類別被載入」
System.out.println("宣告 TestClass 參考名稱");
test = new TestClass(); //class被載入,顯示「類別被載入」
System.out.println("生成 TestClass 實例");
}
}

此外,由於 Java 支援 Run-Time Type Identification(RTTI,執行時期型別辨識),因此 Class 的訊息在編譯時期就會被加入 .class 檔案中;而執行時,JVM 則會在使用某 class 時,會先檢查相對應的 Class object 是否已經載入,如果沒有載入,則會尋找相對應的 .class 檔案載入。

而一個 class 在 JVM 中只會有一個 Class object,所以每個 object 都可以透過 getClass() 或是 .class 屬性取得 Class object。

之前有說過,在 Java 中任何東西皆為 object,因此 Array 也不例外,甚至如 primitive type、關鍵字 void 都有相對應的 Class object,以下用一段程式來說明:
public class ClassDemo2 {
public static void main(String args[]) {
System.out.println(boolean.class); //boolean
System.out.println(void.class); //void

int[] intAry = new int[10];
System.out.println(intAry.getClass().toString()); //class [I

double[] dblAry = new double[10];
System.out.println(dblAry.getClass().toString()); //class [D
}
}
Class 類別中的 forName 與 newInstance 方法

若要動態載入類別,可以使用 Class 類別中的 forName() method,此為 static method,因此可以直接透過 Class 類別使用;另外,若要取得類別的 instance,可使用 Class 類別中的 newInstance() method,以下用依段程式來說明兩個 method 的使用方法:
import java.util.*;

public class ClassForNameDemo {
public static void main(String[] args) {
try {
//forName的使用
Class c = Class.forName(args[0]);
System.out.println("getName : " + c.getName());
System.out.println("isInterface : " + c.isInterface());
System.out.println("isPrimitive : " + c.isPrimitive());
System.out.println("isArray : " + c.isArray());
System.out.println("SuperClass : " + c.getCanonicalName());

//newInstance的使用
List lst = (List) c.newInstance();
for(int i = 0 ; i < 10 ; i++)
lst.add("element" + i);
for(Object o : lst.toArray())
System.out.println(o);
}
catch(Exception e) {
e.printStackTrace();
}
}
}
而當使用 newInstance() 取得 instance 後,就可以使用 getClassLoader()、getConstructor()、getFields()、getMethods()、getModifiers()、getPackage() ..... 等方法,取得 Class 所產生的 instance 的相關資訊,而類別檢視器就是利用這樣子的方式所做出來的!


Class Loader

當 Java 需要使用到 Class 時,才會將 Class 載入;而載入 Class 的工作是由 Class Loader(類別載入器) 所負責。

每當 Java 程式啟動後,會有以下三種 Class Loader 進行載入 Class 的工作:
  1. Bootstrap Loader
    由 C++ 開發,會去搜尋 JRE 目錄($JAVA_HOME/jre/)中的 class 以及 lib 資料夾中的 *.jar 檔,檢查是否有指定的 class 需要載入。
  2. ExtClassLoader
    會去搜尋 JRE 目錄($JAVA_HOME/jre/)中的 lib/ext 資料夾,檢查其中的 class 與 *.jat 檔案,是否有指定的 class 需要載入。
  3. AppClassLoader
    搜尋 classpath 中是否有指定的 class 需要載入。
以下用一張圖形來說明三個 Class Loader 的階層關係:

其中 Bootstrap Loader 為 ExtClassLoader 的 Parent Class;而 ExtClassLoader 則是 AppClassLoader 的 Parent Class。

而從 JVM 啟動後,載入的順序為 Bootstrap Loader -> ExtClassLoader -> AppClassLoader。

而每個 class 所產生出來的 instance 都可以使用 getClassLoader() 知道負責載入 class 的是哪一個 class loader。


動態載入類別、產生物件、呼叫方法

接著在這個部分,要介紹的是如何動態的載入將已經寫好的 class,並產生物件來呼叫 class 中所包含的 method,以下用兩段程式說明:

Student.java
public class Student {
private String name;
private int score;

public Student() {
name = "N/A";
}
public Student(String name, int score) {
this.name = name;
this.score = score;
}

public void setName(String name) {
this.name = name;
}
public void setScore(int score) {
this.score = score;
}

public String getName() {
return name;
}
public int getScore() {
return score;
}

public void showData() {
System.out.println("Name : " + this.name);
System.out.println("Score : " + this.score);
}
}
newInstanceDemo.java
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class newInstanceDemo {
public static void main(String[] args) {
Class c = null;
try {
c = Class.forName(args[0]);

/* (Begin)=============== 生成物件 ===============(Begin) */

//指定Constructor所使用的參數型態
Class[] oParam = new Class[2];
oParam[0] = String.class;
oParam[1] = Integer.TYPE;

//產生Constructor
Constructor constructor = c.getConstructor(oParam);

//指定參數的內容
Object[] paramObjs = new Object[2];
paramObjs[0] = "godleon";
paramObjs[1] = new Integer(90);

//透過Constructor產生物件
Object obj = constructor.newInstance(paramObjs);
System.out.println(obj);

/* (End)=============== 生成物件 ===============(End) */


/* (Begin)=============== 呼叫方法 ===============(Begin) */

//指定Method所使用的參數類型
Class[] mParam1 = {String.class}; //只有一個參數

//產生Method(指定method名稱與參數)
Method setName = c.getMethod("setName", mParam1);

//指定參數內容
Object[] mParamObjs1 = {"godleon"};

//呼叫方法
setName.invoke(obj, mParamObjs1); //呼叫setName方法

//指定Method所使用的參數類型
Class[] mParam2 = {Integer.TYPE};

//產生Method(指定method名稱與參數)
Method setScore = c.getMethod("setScore", mParam2);

//指定參數內容
Object[] mParamObjs2 = {new Integer(90)};

//呼叫方法
setScore.invoke(obj, mParamObjs2); //呼叫setScore方法

//產生Method(指定method名稱與參數)
Method showData = c.getMethod("showData", null);

//呼叫方法
showData.invoke(obj, null); //呼叫showData方法

/* (End)=============== 呼叫方法 ===============(End) */
}
catch(Exception e) {
e.printStackTrace();
}
}
}
由上面的程式碼可以知道,若是有執行時期動態載入 class,並呼叫其 method 的需求,透過使用 reflection,可以很簡單達成目的。

另外也可以動態的修改 Field 的值,可透過 getField() method 來達成,使用方式跟上面的程式大同小異,而若是要瞭解詳細的使用方式,可以參考此篇文章。

最後,此篇筆記就是著重在瞭解 Java 如何在執行期間動態載入 class 並使用,未來有機會用到的時候再來繼續深入探討.....

2007年9月20日 星期四

.NET DateTime 與 Unix Timestamp

剛剛在研究如何將 Unix Timestamp 與 .NET 中的 DateTime 物件互轉!

找到了以下幾篇文章:
  1. 瓶水相逢 - C# - Timestamp 與 DateTime 互轉
  2. UNIX timestamp to System.DateTime

然後還需要跟 JavaScript Date 物件中的 getTime() 方法進行轉換,然後用了以下程式碼:(忘記從哪一篇找來的.....)
//得到1970年的 timestamp
DateTime timeStamp = new DateTime(1970, 1, 1);

//注意這裡有時區問題,用now就要減掉8個小時
DateTime realNow = (new DateTime(2007, 9, 20, 12, 0, 0)).AddHours(-8);

//jsTimeStick即等於用JavaScript Date物件的getTime()方法跑出來的值
long jsTimeStick = (realNow.Ticks - timeStamp.Ticks) / 10000;

2007年9月17日 星期一

Windows Scripting - 簡易資料備份

今天收到命令,要做定時的備份工作,備份的目的地要在遠端的網路磁碟機上

掛載、卸載網路磁碟機

因此就去查詢如何在 Windows 上寫 script 掛載網路磁碟機,以下找到一段語法:
WshNetwork.MapNetworkDrive
maps a remote drive onto a local drive letter

Syntax:
WshNetwork.MapNetworkDrive (strLocalName, strRemoteName [,bUpdateProfile] [,strUser] [,strPassword])
strLocalName
Receives the drive letter to which to map the remtoe share point in strRemoteName.
strRemoteName
Receives the name ofd the remote share point that will be mapped to the drive letter in strLocalName.
bUpdateProfile
If this optional parameter is True, the mapping will be saved in the current user's profile.
strUser
When combined with strPassword, this parameter can be used to conenct to a remote drive using someone else's credentials.
strPassword
When combined with strUser, this parameter can be used to conenct to a remote drive using someone else's credentials.
接著是卸載網路磁碟機的語法:
WshNetwork.RemoveNetworkDrive
removes a previously mapped network drive

Syntax:
WshNetwork.RemoveNetworkDrive (strName, [,bForce] [,bUpdateProfile])
strName
Receives the name of a previously mapped share point to be removed.
bForce
If this optional parameter is set to True, then this method will remove the connection even if the resource is currently being used.
bUpdateProfile
If this optional parameter is set to True, the mapping will be removed from the current user's profile.

以下是他的 sample code:
Dim objNetwork
Set objNetwork = WScript.CreateObject("WScript.Network")
strLocalDrive = "H:"
strRemoteShare = "\\myserver\users"
objNetwork.MapNetworkDrive strLocalDrive, strRemoteShare, False '沒有第四、五個參數,表示不需帳號密碼
objNetwork.RemoveNetworkDrive strLocalDrive '卸載網路磁碟機

資料複製

另外還要將本地資料複製到網路磁碟機上,可用以下一段簡單的語法達成:(也是範例語法)
set fs = CreateObject("Scripting.FileSystemObject")

'將 c:\win98 中所有的內容複製到 z:\backup (不包含 c:\win98 資料夾)
fs.copyFolder "c:\win98","z:\win99"

'將 c:\win98 中所有的內容複製到 z:\backup (包含 c:\win98 資料夾)
fs.copyFolder "c:\win98","z:\win99\"

參考資料

  1. WSH >> Objects >> wshnetwork
  2. WSH >> wshnetwork >> MapNetworkDrive
  3. WSH >> wshnetwork >> RemoveNetworkDrive
  4. WshNetwork.MapNetworkDrive
  5. 資安論壇 - 求助!有時需要reboot或re-log才看見net use的network drive
  6. Windows Script 介紹 - 檔案及資料夾的處理

在 Ubuntu 中建置 mono 開發環境

在 Ubuntu 中建置 mono 的開發環境相當簡單,需要以下三個要素:
  1. mono (Runtime)
  2. mono-gmcs (compiler)
  3. monodevelop (IDE develop tool)
所以下達以下指令通通解決:
shell> sudo apt-get -y install mono monodevelop mono-gmcs
接著就可以使用 monodevelop 來開發 .NET 程式囉!

2007年9月6日 星期四

SAMBA + FTP 在 SELinux 的調整方式

要針對同一個目錄,讓可以透過 SAMBA 連入! 也可讓 FTP 連入!

在 SELinux 開啟的情況下,要將該目錄的 security context 設定為 「samba_share_t」

如此一來當權限設定好之後,就可以正常的透過服務存取目錄的資料了!

2007年9月4日 星期二

Linux SATA HowTo

關於 Linux 對於 SATA chipset 的支援,隨著 kernel 版本的持續更新,以及社群的強大協助開發下,陸陸續續將許多廠商所生產的 SATA chipset driver 都已經加入,因此在目前比較新的 Linux distribution 如 Fedora 7、Ubuntu 7.04、OpenSUSE 10.2、Mandriva Free 2007.....等,對於 SATA 的支援度都已經相當的好。

此外,若想要清楚了解目前 Linux 已經支援了哪些 SATA chipset,可以參考以下兩個網站,資料整理的相當詳細:

因此在購買硬體前,事先進行確認,可以避免掉後續許多硬體不支援的麻煩。

但若是因為一時不察,買到了內建目前各大 Linux distribution 都不支援的 SATA chiset 的產品(例如:主機板、SATA RAID Card .... 等等),雖然麻煩了點,但還是可以透過幾個方式解決:

1、進入 BIOS 設定為相容模式
在 BIOS 中尋找 SATA 的設定,假設在 IBM X60 上,可以找到 Config -> Serial ATA(SATA),設定為 Compatibility。

在其他不同廠牌的 BIOS 中,可能會有 Legacy ATA Mode 可供設定!

透過此種方式,BIOS 會將 SATA Device 視為 PATA 處理,當然效能會有所折損,不過可以在順利安裝 Linux 後,可以透過以下方式讓 Linux 支援 SATA 的裝置:
  1. 透過線上更新將 kernel 換成最新版的
    在 Fedora 中可以使用 yum;在 Ubuntu 中可以使用 apt。kernel 更新完成後,可能就支援原本不支援的 SATA 了,但也有可能一樣不支援!
  2. 重新編譯 kernel
    若是在 kernel 官方網站中,發現其實在新版的 kernel 中已經支援了該 SATA 裝置,就可以下載 kernel 的 source code 後,開啟 SATA 的支援並重新進行編譯,如此一來就可以正確驅動該 SATA chipset,接著置換原本的 kernel,就可以解決 SATA 裝置無法驅動的問題。而編譯 kernel 的方式可以參考此篇文章(鳥哥的 Linux 私房菜 - 核心編譯)。


2、使用廠商提供的 SATA chipset driver

若是很清楚 SATA chipset 是哪家廠商所生產的,並找到該廠商所提供的官方版 driver,就可以在安裝時,使用「linux dd」指令在安裝前載入 SATA chipset driver,如此一來安裝時,就可以正確的驅動 SATA device。

PS. 其實此種解決方式跟在 Windows 的模式是差不多的,在安裝 Windows 前若要載入 SATA driver,必須按 F6;只是在 Linux 的安裝中,必須下達指令「linux dd」。


結論
假設廠商沒有提供任何的 driver,線上更新 kernel 後也還是不支援,甚至在 Linux kernel 官方網站中都沒有提供支援該 SATA chipset 的計劃,就表示目前該 SATA chipset 在 Linux 是無法正常運作的,因此可能要重新購買其他已經支援的產品。


參考資訊

2007年9月3日 星期一

SQL Server 學習筆記 (1)

今天 K SQL server 的書,學到了幾個新的小技巧,因此作個記錄~

指令檢視 Table 詳細資訊

透過以下指令可以檢視 Table 的詳細資訊:
--指定 Database
USE studentPartTime
go
--查詢 Table 資訊
sp_help 'tbpwWork'
如此一來,table 的 schema、key、relation 都會很詳實的呈現出來,若是有 Date 相關欄位,還會有相對應的資訊顯示出來。


Index

除了 schema 設計的好壞之外,Index 的建置對於資料庫 performance tunning 是相當重要的一環,以下找到幾篇文章說明 index 設計時所必須考量的因素:
這幾篇看完應該會很清楚 index 應該要怎麼用了!

之後研究有其他更深入的心得,就再貼上來......


Set-Oriented (資料集導向)

在 RDBMS 中,最佳化都是針對 Data Set (資料集),若是用程序式的方式進行資料的操作,所得到的效能反而不好。

假設有個範例,要搜尋出住在各縣市的學生人數的統計資料!

若是用程序式的處理方式,就必須使用 cursor,並透過變數暫存數值,一筆一筆資料從頭掃瞄到尾,以便統計出完整資訊。

而用 Data Set 的處理方式,則是利用 select、count、group by 等關鍵字即可完成,遠遠比起使用 cursor 的方式來的輕鬆有效率,因此在 DML 的部分還是要多加鑽研才行!