huoyuhan/
共64个网摘 [
1 2 3 ]
下一页 |
访问huoyuhan的个人空间
huoyuhan收录,使用标签:Java,时间:2008-6-24 15:27:51 | 相关网摘,我也收藏
Java编程那些事儿40—流程控制综合示例2
郑州游戏学院 陈跃峰
出自:http://blog.csdn.net/mailbomb
5.6.1.3 喝汽水问题
问题:共有1000瓶汽水,每喝完后一瓶得到的一个空瓶子,每3个空瓶子又能换1瓶汽水,喝掉以后又得到一个空瓶子,问总共能喝多少瓶汽水,最后还剩余多少个空瓶子?
这个问题其实是个比较典型的递推问题,每3个空瓶都可以再换1瓶新的汽水,这样一直递推下去,直到最后不能换到汽水为止。
第一种思路:每次喝一瓶,每有三个空瓶子就去换一瓶新的汽水,直到最后没有汽水可以喝为止。在程序中记忆汽水的数量和空瓶子的数量即可。
则实现的代码如下:
int num = 1000; //汽水数量
int drinkNum = 0; //喝掉的汽水数量
int emptyNum = 0; //空瓶子的数量
while(num > 0){ //有汽水可以喝
num--; //喝掉一瓶
emptyNum++; //空瓶子数量增加1
drinkNum++; //喝掉的汽水数量增加1
if(emptyNum == 3){ //有3个空瓶子,则去换
num++; //汽水数量增加1
emptyNum = 0; //空瓶子数量清零
}
}
System.out.println(“总共喝掉瓶数:” + drinkNum);
System.out.println(“剩余空瓶子数:” + emptyNum);
执行该程序,输出结果如下:
总共喝掉瓶数:1499
剩余空瓶子数:2
在该代码中,每次循环喝掉一瓶汽水,则汽水数量减少1,空瓶子数增加1,喝掉的总汽水瓶数增加1,每次判断空瓶子的数量是否达到3,如果达到3则换1瓶汽水,同时空瓶子的数量变为零。这种思路比较直观,但是循环的次数比较多,所以就有了下面的逻辑实现。
第二种思路:一次把所有的汽水喝完,获得所有的空瓶子,再全部换成汽水,然后再一次全部喝完,再获得所有的空瓶子,依次类推,直到没有汽水可喝为止。
则实现的代码如下:
int num = 1000; //汽水数量
int drinkNum = 0; //喝掉的汽水数量
int emptyNum = 0; //空瓶子的数量
while(num > 0){ //有汽水可以喝
drinkNum += num; //喝掉所有的汽水
emptyNum += num; //空瓶子数量等于上次剩余的加上这次喝掉的数量
num = emptyNum / 3; //兑换的汽水数量
emptyNum -= num * 3; //本次兑换剩余的空瓶子数量
}
System.out.println(“总共喝掉瓶数:” + drinkNum);
System.out.println(“剩余空瓶子数:” + emptyNum);
在该代码中,每次喝掉所有的汽水,也就是num瓶,则喝掉的总瓶数每次增加num,因为每次都可能剩余空瓶子(不足3个的),则总的空瓶子数量是上次空瓶子数量加上本次喝掉的num瓶。接着是对话汽水,则每次可以兑换的汽水数量是空瓶子的数量的1/3,注意这里是整数除法,而本次兑换剩余的空瓶子数量是原来的空瓶子数量减去兑换得到汽水数量的3倍,这就是一次循环所完成的功能,依次类推即可解决该问题。
5.6.1.4水仙花数
问题:水仙花数指三位数中,每个数字的立方和和自身相等的数字,例如370,3 × 3 × 3 + 7 × 7 × 7 + 0 × 0 × 0 =370,请输出所有的水仙花数。
该问题中体现了一个基本的算法——数字拆分,需要把一个数中每位的数字拆分出来,然后才可以实现该逻辑。
实现思路:循环所有的三位数,拆分出三位数字的个位、十位和百位数字,判断3个数字的立方和是否等于自身。
则实现的代码如下所示:
for(int i = 100;i < 1000;i++){ //循环所有三位数
int a = i % 10; //个位数字
int b = (i / 10) % 10; //十位数字
int c = i / 100; //百位数字
//判断立方和等于自身
if(a * a * a + b * b * b + c * c * c == i){
System.out.println(i);
}
}
在该代码中,拆分个位数字使用i和10取余即可,拆分十位数字时首先用i除以十,去掉个位数字,并使原来的十位数字变成个位,然后和10取余即可,因为i是一个三位数,所以i除以100即可得百位数字,因为这里都是整数除法,不存在小数的问题。然后只需要判断立方和是否等于自身即可。
注意:因为i是循环变量,这里不能改变i的值,不然可能造成死循环。
http://blog.csdn.net/Mailbomb/archive/2008/06/19/2566692.aspx
huoyuhan收录,使用标签:Java,时间:2008-6-24 15:27:35 | 相关网摘,我也收藏
Java编程那些事儿41—流程控制综合示例3
郑州游戏学院 陈跃峰
出自:http://blog.csdn.net/mailbomb
5.6.1.5 99乘法表
问题:在控制台打印数学上的99乘法表
该类问题是发现数字的规律,然后将数值的规律用程序描述出来。实际实现时,可能需要耐心的进行调试。在这里,需要实现数字的多行输出,前面使用的System.out.println是输出内容并换行,后续再输出的内容就再下一行显示,如果需要在输出时不换行,则可以使用System.out.print进行输出。
99乘法表的规则是总计9行,每行单独输出,第一行有1个数字,第二行有2个数字,依次类推,数字的值为行号和列号的乘积。
实现思路:使用一个循环控制打印9行,在该循环的循环体中输出该行的内容,一行中输出的数字个数等于行号,数字的值等于行号和列号的成绩。
实现代码如下:
for(int row = 1;row <= 9;row++){ //循环行
for(int col = 1;col <= row;col++){ //循环列
System.out.print(row * col); //输出数值
System.out.print(' '); //输出数字之间的间隔空格
}
System.out.println(); //一行输出结束,换行
}
该程序的输出为:
1
2 4
3 6 9
4 8 12 16
5 10 15 20 25
6 12 18 24 30 36
7 14 21 28 35 42 49
8 16 24 32 40 48 56 64
9 18 27 36 45 54 63 72 81
在该输出中,数字之间的对齐有些问题,第四行和第五行的对齐就很明显。那么如果在输出时想让数字对齐,那么就要首先思考数字为什么不能对齐?则问题直观的出现在有些数字是一位数有些是两位数,发现了原因就可以着手解决了,如果想实现数字的左对齐,则在一位数字的后续多输出一个空格,如果想实现数字的右对齐,则只需要在一位数字的前面输出一个空格即可。
以下代码实现了数字的右对齐:
for(int row = 1;row <= 9;row++){ //循环行
for(int col = 1;col <= row;col++){ //循环列
if(row * col < 10){ //一位数
System.out.print(' ');
}
System.out.print(row * col); //输出数值
System.out.print(' '); //输出数字之间的间隔空格
}
System.out.println(); //一行输出结束,换行
}
所以在实际书写代码时,代码的位置对于程序逻辑的影响很大,在编写代码时,需要认真考虑代码书写的位置。
5.6.1.6 打印图形
问题:在控制台中打印如下格式的图形
*
***
*****
*******
*********
由于受到控制台输出的限制,只能按照行的上下,依次进行输出,所以解决打印图形的问题时,只能按照从上到下依次输出每行的内容,关键是仔细观察,发现图形的规律。
第一种思路:外部循环循环5次打印5行,每行的内容分为两部分:空格和星号,每行空格的数量是5减去行号个,每行星号的数量是行号的2倍减1个,在外部循环内部先打印空格,再打印星号,每个都只打印1个,使用数量控制对应的打印次数,打印完星号以后换行。
则实现的代码如下:
for(int row = 1;row <= 5;row++){ //循环行
//打印空格
for(int c1 = 0;c1 < 5 - row;c1++){
System.out.print(' ');
}
//打印星号
for(int c2 = 0;c2 < 2 * row - 1;c2++){
System.out.print('*');
}
//换行
System.out.println();
}
该代码中row的循环用于控制打印的行数,row变量的值代表行号,内部的循环体分为三部分:打印空格,打印星号和换行,打印的数量参看图形的规律部分。
第二种思路:外部循环循环5次打印5行,内部每行打印的总字符数量是4加行号个,其中前5-行号个字符是空格,后续的字符是星号,所有字符打印完成以后换行。
则实现的代码如下:
for(int row = 1;row <= 5;row++){ //循环行
//循环总的字符数
for(int col = 0; col < 4 + row;col++){
if(col < 5 - row){ //打印空格
System.out.print(' ');
}else{ //打印星号
System.out.print('*');
}
}
//换行
System.out.println();
}
该代码的总体思路和第一种思路一样,都是按行打印,只是在考虑问题时首先考虑字符总的数量,把这个数量作为循环次数,内部控制那些该输出字符那些该输出星号即可。
5.6.1.7 质数判断
问题:判断一个自然数是否是质数。
质数指只能被1和自身整除自然数,也称素数,最小的质数是2。对于自然数来说,任何一个数字都可以被1和自身整除。
实现思路:利用数学上的反证法进行判断。则问题转换为只需要判断不能被1和自身以外的任何一个数字整除即可。则假设判断的数字是n的话,则这些数字的区间是[2,n-1]和大于n的所有数字。在数学上n不可能被大于n的数字整除,所以程序只需要判断[2,n-1]之间的数字即可,如果被该区间的任何一个数字整除了,则说明不是质数。
则实现的代码如下:
int n = 23;
boolean b = true; //存储是否是质数,假设是质数
for(int i = 2;i < n;i++){
//如果整除,则不是质数
if(n % i == 0){
b = false;
break; //后续比较没有意义,结束循环
}
}
//输出是否是质数
if(b){
System.out.println("是质数");
}else{
System.out.println("不是质数");
}
该代码是最容易思考出来的一种实现,其实在数学上只需要判断n是否可以被2到n的二次方根之间的数字即可。则实现的代码变为如下:
int n = 23;
boolean b = true; //存储是否是质数,假设是质数
for(int i = 2;i <= Math.sqrt(n);i++){
//如果整除,则不是质数
if(n % i == 0){
b = false;
break; //后续比较没有意义,结束循环
}
}
//输出是否是质数
if(b){
System.out.println("是质数");
}else{
System.out.println("不是质数");
}
通过缩小判断数字的区间,可以显著提高程序的执行效率。说明:这里的Math.sqrt的功能是计算n的二次方根。
关于流程控制的综合示例部分就介绍这么多,下面将整理一下流程控制的综合练习。
http://blog.csdn.net/Mailbomb/archive/2008/06/20/2570151.aspx
huoyuhan收录,使用标签:Java,时间:2008-6-24 15:27:14 | 相关网摘,我也收藏
Java编程那些事儿44—数组基础语法
郑州游戏学院 陈跃峰
出自:http://blog.csdn.net/mailbomb
6.2 数组基本语法
了解了数组的概念以后,下面来看一下数组的语法格式。数组的语法格式主要有四种:数组声明、数组初始化、引用数组元素和获得数组长度。
6.2.1 数组声明
和变量类似,数组在使用以前也必须声明,数组的声明语法格式为:
数据类型 数组名称[]
或:
数据类型[] 数组名称
例如:
int m[];
char c[];
double d[];
这里的数据类型可以是Java语言的任意数据类型,也就是说既可以是基本数据类型也可以是复合数据类型。在声明数组时使用一对中括号,该对中括号既可以放在数据类型的后面,也可以放在数组名称的后面。数组名称是一个标识符,可以根据需要设置其名称,在程序中使用该名称代表该数组。
这两种声明的语法格式在实际使用时完全等价,可以根据习惯进行使用。
数组声明以后在内存中不占用空间,没有地址,由于数组是复合数据类型,所以声明完成以后其默认值是null。
数组声明以后不能直接使用,必须对其初始化以后才可以进行使用。
6.2.2 数组初始化
数组初始化就是对数组进行赋值。数组的初始化语法分为两种:静态初始化和动态初始化。静态初始化适用于已知数组所有元素的值,一次初始化所有元素,动态初始化只申请空间,每个元素的值是数组声明时数据类型对应的初始值。
6.2.2.1 静态初始化
静态初始化,也称数组的整体赋值,是一次为数组中所有元素依次进行赋值的语法,通过可以语法可以指定数组中每个元素的值,同时也指定了数组的长度。
语法格式为:
数据类型[] 数组名称 = {值1,值2,……,值n};
例如:
int[] m = {1,2,3,4};
char c[] = {‘a’,’f’,’d’};
静态初始化必须和数组的声明位于同一行,换句话说,只能在声明数组的同时进行静态初始化。数组中的所有元素书写一对大括号的内部,系统按照值的书写顺序依次为数组运算进行赋值,例如数组m,则将1赋值给m数组的第一个元素,2赋值给m数组的第二个元素,依次类推,数组的总长度等于静态初始化时数值的个数。在实际书写时,需要注意,值的类型必须和数组声明时的类型匹配,或者可以自动进行转换。
在实际程序中,静态初始化一般书写一组已知的无规律数值,这样书写起来比较简单,格式比较统一。
6.2.2.2 动态初始化
动态初始化,也就是只为数组指定长度,并且在内存中申请空间。动态初始化可以不必和数组的声明放在一起,也可以重新初始化一个初始化的数组。
动态初始化的语法格式:
数据类型[] 数组名称 = new 数据类型[长度];
例如:
int[] m = new int[10];
char[] c;
n = new char[3];
动态初始化使用new关键字进行初始化,new关键字后续的数据类型要求和数组声明时的数据类型一样,中括号内部是需要初始化的数组长度,该长度值可以是数字也可以是整型变量,如果是整型变量则不能为long型。在实际使用时,也可以先声明再进行动态初始化。
动态初始化指定了数组的长度,在内存中申请了对应长度的空间,而每个元素的值取数组数据类型对应的默认值。默认值的规定如下:
a、 boolean类型的默认值是false。
b、 其它7种基本数据类型是0。说明:char的默认值是编码为0的字符,而不是字符0。
c、 复合数据类型的初始值是null。
动态初始化只专注于为数组申请对应长度的空间,具体存储的元素的值可以根据需要依次进行指定。
6.2.3 引用数组元素
数组是一组数的集合,在实际使用时还需要引用数组中的每个元素。则引用数组中元素的语法格式为:
数组名称[下标]
其中下标指数组中每个元素的索引值,Java语法规定数组中的第一个元素索引值是0,第二个是1,依次类推。在程序书写时,下标位置既可以书写常数也可以书写变量。而整个引用元素的表达式可以看作是一个变量,该变量的类型和数组的类型一致。
示例代码如下:
int[] m = {3,2,4,6};
m[1] = 4;
m[2] = m[3] + m[0];
在代码中,可以使用变量作为下标,示例代码如下:
char[] ch = new char[10];
int i = 2;
ch[i] = ‘a’;
使用变量作为数组的下标,极大的增强了数组元素使用的灵活性,也是灵活使用数组必须深刻理解的内容。
因为数组的下标都从0开始,所以有效的数组下标区间是0到数组的长度减1,其它的下标都是非法的。在代码中出现非法的下标不会出现语法错误,但是会导致运行时出现异常。
6.2.4 获得数组长度
为了方便的操作数组,Java语法中提供了获得数组长度的语法格式。对于一个已经初始化完成的数组,获得该数组长度的语法格式为:
数组名称.length
示例代码如下:
int[] n = {1,2,3,4,6};
int len = n.length;
则在该代码中n.length代表数组n的长度,由数组的初始化可以看出数组n的长度是5,则变量len的值将是5。使用该语法,可以只需要知道数组的名称就可以获得数组的长度,便于灵活操作数组。
综合前面下标的语法和长度的语法,则输出数组n中所有元素的代码为:
for(int i = 0;i < len;i++){
System.out.println(n[i]);
}
这种使用数组的方式称作数组的遍历,遍历数组是使用数组的基础,也是很多和数组相关逻辑实现的基础。
关于数组的语法就介绍这么,下面通过一些示例来演示数组的实际使用。
http://blog.csdn.net/Mailbomb/archive/2008/06/22/2575433.aspx
huoyuhan收录,使用标签:Java,时间:2008-5-12 22:47:59 | 相关网摘,我也收藏
一般我们在java中运行其它类中的方法时,无论是静态调用,还是动态调用,都是在当前的进程中执行的,也就是说,只有一个java虚拟机实例在运行。而有的时候,我们需要通过java代码启动多个java子进程。这样做虽然占用了一些系统资源,但会使程序更加稳定,因为新启动的程序是在不同的虚拟机进程中运行的,如果有一个进程发生异常,并不影响其它的子进程。
在Java中我们可以使用两种方法来实现这种要求。最简单的方法就是通过Runtime中的exec方法执行java classname。如果执行成功,这个方法返回一个Process对象,如果执行失败,将抛出一个IOException错误。下面让我们来看一个简单的例子。
// Test1.java文件
import java.io.*;
public class Test
{
public static void main(String[] args)
{
FileOutputStream fOut = new FileOutputStream("c:\\Test1.txt");
fOut.close();
System.out.println("被调用成功!");
}
}
// Test_Exec.java
public class Test_Exec
{
public static void main(String[] args)
{
Runtime run = Runtime.getRuntime();
Process p = run.exec("java test1");
}
}
通过java Test_Exec运行程序后,发现在C盘多了个Test1.txt文件,但在控制台中并未出现"被调用成功!"的输出信息。因此可以断定,Test已经被执行成功,但因为某种原因,Test的输出信息未在Test_Exec的控制台中输出。这个原因也很简单,因为使用exec建立的是Test_Exec 的子进程,这个子进程并没有自己的控制台,因此,它并不会输出任何信息。
如果要输出子进程的输出信息,可以通过Process中的getInputStream得到子进程的输出流(在子进程中输出,在父进程中就是输入),然后将子进程中的输出流从父进程的控制台输出。具体的实现代码如下如示:
// Test_Exec_Out.java
import java.io.*;
public class Test_Exec_Out
{
public static void main(String[] args)
{
Runtime run = Runtime.getRuntime();
Process p = run.exec("java test1");
BufferedInputStream in = new BufferedInputStream(p.getInputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String s;
while ((s = br.readLine()) != null)
System.out.println(s);
}
}
从上面的代码可以看出,在Test_Exec_Out.java中通过按行读取子进程的输出信息,然后在Test_Exec_Out中按每行进行输出。上面讨论的是如何得到子进程的输出信息。那么,除了输出信息,还有输入信息。既然子进程没有自己的控制台,那么输入信息也得由父进程提供。我们可以通过 Process的getOutputStream方法来为子进程提供输入信息(即由父进程向子进程输入信息,而不是由控制台输入信息)。我们可以看看如下的代码:
// Test2.java文件
import java.io.*;
public class Test
{
public static void main(String[] args)
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("由父进程输入的信息:" + br.readLine());
}
}
// Test_Exec_In.java
import java.io.*;
public class Test_Exec_In
{
public static void main(String[] args)
{
Runtime run = Runtime.getRuntime();
Process p = run.exec("java test2");
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
bw.write("向子进程输出信息");
bw.flush();
bw.close(); // 必须得关闭流,否则无法向子进程中输入信息
// System.in.read();
}
}
从以上代码可以看出,Test1得到由Test_Exec_In发过来的信息,并将其输出。当你不加bw.flash()和bw.close()时,信息将无法到达子进程,也就是说子进程进入阻塞状态,但由于父进程已经退出了,因此,子进程也跟着退出了。如果要证明这一点,可以在最后加上 System.in.read(),然后通过任务管理器(在windows下)查看java进程,你会发现如果加上bw.flush()和 bw.close(),只有一个java进程存在,如果去掉它们,就有两个java进程存在。这是因为,如果将信息传给Test2,在得到信息后, Test2就退出了。在这里有一点需要说明一下,exec的执行是异步的,并不会因为执行的某个程序阻塞而停止执行下面的代码。因此,可以在运行 test2后,仍可以执行下面的代码。
exec方法经过了多次的重载。上面使用的只是它的一种重载。它还可以将命令和参数分开,如exec("java.test2")可以写成exec("java", "test2")。exec还可以通过指定的环境变量运行不同配置的java虚拟机。
除了使用Runtime的exec方法建立子进程外,还可以通过ProcessBuilder建立子进程。ProcessBuilder的使用方法如下:
// Test_Exec_Out.java
import java.io.*;
public class Test_Exec_Out
{
public static void main(String[] args)
{
ProcessBuilder pb = new ProcessBuilder("java", "test1");
Process p = pb.start();
… …
}
}
在建立子进程上,ProcessBuilder和Runtime类似,不同的ProcessBuilder使用start()方法启动子进程,而Runtime使用exec方法启动子进程。得到Process后,它们的操作就完全一样的。
ProcessBuilder和Runtime一样,也可设置可执行文件的环境信息、工作目录等。下面的例子描述了如何使用ProcessBuilder设置这些信息。
ProcessBuilder pb = new ProcessBuilder("Command", "arg2", "arg2", ''');
// 设置环境变量
Map env = pb.environment();
env.put("key1", "value1");
env.remove("key2");
env.put("key2", env.get("key1") + "_test");
pb.directory("..\abcd"); // 设置工作目录
Process p = pb.start(); // 建立子进程
http://www.blogjava.net/nokiaguy/archive/2008/05/10/199738.html
huoyuhan收录,使用标签:Java,时间:2008-5-2 22:34:40 | 相关网摘,我也收藏
首先事件是指从客户端页面(浏览器)由用户操作触发的事件,Struts使用Action来接受浏览器表单提交的事件,这里使用了Command模式,每个继承Action的子类都必须实现一个方法execute。
在Struts中,实际是一个表单Form对应一个Action类(或DispatchAction),换一句话说:在Struts中实际是一个表单只能对应一个事件,Struts这种事件方式称为application event,application event和Component event相比是一种粗粒度的事件。
Struts重要的表单对象ActionForm是一种对象,它代表了一种应用,这个对象中至少包含几个字段,这些字段是Jsp页面表单中的input字段,因为一个表单对应一个事件,所以,当我们需要将事件粒度细化到表单中这些字段时,也就是说,一个字段对应一个事件时,单纯使用Struts就不太可能,当然通过结合JavaScript也是可以转弯实现的。
而这种情况使用JSF就可以方便实现
#{login.userId}表示从名为login的JavaBean的getUserId获得的结果,这个功能使用struts也可以实现,name="login" property="userId"
关键是第二行,这里表示如果userId的值改变并且确定提交后,将触发调用类UserLoginChanged的processValueChanged(...)方法。
JSF可以为组件提供两种事件:Value Changed和 Action. 前者我们已经在上节见识过用处,后者就相当于struts中表单提交Action机制,它的JSF写法如下:
从代码可以看出,这两种事件是通过Listerner这样观察者模式贴在具体组件字段上的,而Struts此类事件是原始的一种表单提交Submit触发机制。如果说前者比较语言化(编程语言习惯做法类似Swing编程);后者是属于WEB化,因为它是来自Html表单,如果你起步是从Perl/PHP开始,反而容易接受Struts这种风格。
基本配置
Struts和JSF都是一种框架,JSF必须需要两种包JSF核心包、JSTL包(标签库),此外,JSF还将使用到Apache项目的一些commons包,这些Apache包只要部署在你的服务器中既可。
JSF包下载地址:http://java.sun.com/j2ee/javaserverfaces/download.html选择其中Reference Implementation。
JSTL包下载在http://jakarta.apache.org/site/downloads /downloads_taglibs-standard.cgi
所以,从JSF的驱动包组成看,其开源基因也占据很大的比重,JSF是一个SUN伙伴们工业标准和开源之间的一个混血儿。
上述两个地址下载的jar合并在一起就是JSF所需要的全部驱动包了。与Struts的驱动包一样,这些驱动包必须位于Web项目的WEB-INF/lib,和Struts一样的是也必须在web.xml中有如下配置:
Faces Servlet
javax.faces.webapp.FacesServlet
1
Faces Servlet
*.faces
这里和Struts的web.xml配置何其相似,简直一模一样。
正如Struts的struts-config.xml一样,JSF也有类似的faces-config.xml配置文件:
/index.jsp
login
/welcome.jsp
user
com.corejsf.UserBean
session
在Struts-config.xml中有ActionForm Action以及Jsp之间的流程关系,在faces-config.xml中,也有这样的流程,我们具体解释一下Navigation:
在index.jsp中有一个事件:
Action的值必须匹配form-outcome值,上述Navigation配置表示:如果在index.jsp中有一个login事件,那么事件触发后下一个页面将是welcome.jsp
JSF有一个独立的事件发生和页面导航的流程安排,这个思路比struts要非常清晰。
managed-bean类似Struts的ActionForm,正如可以在struts-config.xml中定义ActionForm的scope一样,这里也定义了managed-bean的scope为session。
但是如果你只以为JSF的managed-bean就这点功能就错了,JSF融入了新的Ioc模式/依赖性注射等技术。
Ioc模式
对于Userbean这样一个managed-bean,其代码如下:
public class UserBean {
private String name;
private String password;
// PROPERTY: name
public String getName() { return name; }
public void setName(String newValue) { name = newValue; }
// PROPERTY: password
public String getPassword() { return password; }
public void setPassword(String newValue) { password = newValue; }
}
user
com.corejsf.UserBean
session
name
me
password
secret
faces-config.xml这段配置其实是将"me"赋值给name,将secret赋值给password,这是采取Ioc模式中的Setter注射方式。
Backing Beans
对于一个web form,我们可以使用一个bean包含其涉及的所有组件,这个bean就称为Backing Bean, Backing Bean的优点是:一个单个类可以封装相关一系列功能的数据和逻辑。
说白了,就是一个Javabean里包含其他Javabean,互相调用,属于Facade模式或Adapter模式。
对于一个Backing Beans来说,其中包含了几个managed-bean,managed-bean一定是有scope的,那么这其中的几个managed-beans如何配置它们的scope呢?
...
property-name>visit
#{sessionScope.visit}
这里配置了一个Backing Beans中有一个setVisit方法,将这个visit赋值为session中的visit,这样以后在程序中我们只管访问visit对象,从中获取我们希望的数据(如用户登陆注册信息),而visit是保存在session还是application或request只需要配置既可。
UI界面
JSF和Struts一样,除了JavaBeans类之外,还有页面表现元素,都是是使用标签完成的,Struts也提供了struts-faces.tld标签库向JSF过渡。
使用Struts标签库编程复杂页面时,一个最大问题是会大量使用logic标签,这个logic如同if语句,一旦写起来,搞的JSP页面象俄罗斯方块一样,但是使用JSF标签就简洁优美:
icon="/images/inbox.gif"
action="inbox"
disabled="#{!authenticationBean.inboxAuthorized}"/>
如果authenticationBean中inboxAuthorized
http://webservices.ctocio.com.cn/wsare/450/8095950.shtml
huoyuhan收录,使用标签:Java,时间:2008-5-2 22:33:03 | 相关网摘,我也收藏
在这篇文章里,对于Spring XML的配置,我将向你展示12种比较好的实践。其中的一些实践不仅是好的实践,更是必要的实践。除此以外,还有其他因素,例如领域模型的设计,都能影响XML的配置,但是这篇文章重点研究XML配置的易读性和易管理性。
1。不要使用autowiring
Spring 可以通过类的自省来自动绑定其依赖部分,使得你不必明确指明bean的属性和构造器。Bean的属性可以通过属性名称或类型匹配来实现自动绑定。构造器通过类型匹配来实现自动绑定。你甚至可以指定自动检测自动绑定模式,它可以引导Spring选择一种适当的运行机制。先来看看下面的一个例子:
< bean id="orderService" class="com.lizjason.spring.OrderService" autowire="byName"/>
OrderService 类的属性名在容器中用于匹配bean实例。自动绑定可以潜在地节省一些打字和减少一些混乱。但是在现实世界的工程里你不应该使用这种方式,这是因为它牺牲了配置的清晰性和可维护性。许多指南和介绍中大量吹捧自动绑定是Spring的一种极好的特征而没有提到这一特性所带来的牺牲。依我的观点,这就像 Spring中的object-pooling,它更像是一种为了占据更多市场的商业特征。它对于XML配置文件的小巧化是一个好办法,但实际上也增加了复杂程度,尤其当你运行有大量类声明的工程时。虽然Spring允许你混合自动绑定和手动绑定,但是这个矛盾会使XML配置更加晦涩难懂。
2.使用通俗的命名
这个方式对于Java编码也一样适用。在工程中使用清晰的、描述性的、协调的通俗名称对于开发者理解XML配置是十分有益的。例如对于bean ID,你可以根据通俗的Java类名来命名它。对于例子中OrderServiceDAO的bean ID命名为orderServiceDAO。对于大的工程,你可以在bean ID前面加上包名作为前缀。
3. 使用简洁的形式
简洁形式避免了冗长,是因为它从子元素中将属性值和参考写到属性中。例如下面的例子:
< bean id="orderService"
class="com.lizjason.spring.OrderService">
< property name="companyName">
< value>lizjason< /value>
< /property>
< constructor-arg>
< ref bean="orderDAO">
< /constructor-arg>
< /bean>
可以使用简洁形式将上述代码重写为:
< bean id="orderService"
class="com.lizjason.spring.OrderService">
< property name="companyName"
value="lizjason"/>
< constructor-arg ref="orderDAO"/>
< /bean>
简洁形式功能在1.2版本中可以使用。对于< ref local="...">没有简洁形式。
简洁形式不但可以节约你的打字,而且可以使XML配置文件清晰。它最引人注目的是当在一个配置文件中有大量定义的类时可以提高易读性。
4. 对于构造器参数匹配,类型名比序号好。
当一个构造器含有一个以上的同种类型的参数,或者属性值的标签已经被占用时,Spring允许你使用从0计数的序号来解决这些会带来混淆的问题。例如:
< bean id="billingService"
class="com.lizjason.spring.BillingService">
< constructor-arg index="0" value="lizjason"/>
< constructor-arg index="1" value="100"/>
< /bean>
像下面这样,利用类型属性来编写会更好一些:
< bean id="billingService"
class="com.lizjason.spring.BillingService">
< constructor-arg type="java.lang.String"
value="lizjason"/>
< constructor-arg type="int" value="100"/>
< /bean>
使用索引可以稍稍减少一些冗长,但是和使用类型属性相比,它还是有容易发生错误的倾向和难于阅读的缺点。你应该只在构造器参数不明确的时候,才使用索引这一方法。
5. 尽可能重用已定义过的bean
Spring 提供一种类似继承一样的机制来减少配置信息的复制并简化XML配置。定义一个子类可以从它父类那里继承配置信息,而父类实质上作为子类的一个模板。这就是大工程中所谓的重用。你所需要做的就是在父类bean中设置abstract=true,然后在子bean注明它自己的父类bean。例如:
< bean id="abstractService" abstract="true"
class="com.lizjason.spring.AbstractService">
< property name="companyName"
value="lizjason"/>
< /bean>
< bean id="shippingService"
parent="abstractService"
class="com.lizjason.spring.ShippingService">
< property name="shippedBy" value="lizjason"/>
< /bean>
ShippingService类从abstractService类那里继承companyName属性的值??lizjason。如果你没有为一个bean指明类或factory方法,那么这个bean便是抽象的。
6. 尽量使用ApplicationContext来装配定义的bean
像在Ant脚本中的引用一样,Spring的引用对于装配模块化的bean来说是很有用的。例如:
< beans>
< import resource="billingServices.xml"/>
< import resource="shippingServices.xml"/>
< bean id="orderService"
class="com.lizjason.spring.OrderService"/>
< beans>
相对于使用import在XML配置中来预装配,通过ApplicationContext来配置这些beans,显得更加灵活。利用 ApplicationContext也使得XML配置易于管理。你可以像下面的例子那样在ApplictionContext构造器里布置bean:
String[] serviceResources =
{"orderServices.xml",
"billingServices.xml",
"shippingServices.xml"};
ApplicationContext orderServiceContext = new
ClassPathXmlApplicationContext(serviceResources);
7. 利用id作为bean的标识符
你可以指定一个id或名称来作为bean的标识符。虽然使用id不会提高易读性,但是它可以让XML parser对bean的引用有效方面进行更好的验证。如果由于XML IDREF的限制而不能使用某个id,你可以利用names来作为bean的标识符。XML IDREF的限制是id必须以字母开头(或者在XML规范中定义的标点符号),后面接着字母,数字,连字号,下划线,冒号等。实际上,遇到XML IDREF限制的问题是很少见的。
8. 在开发阶段使用依赖检验
你可以在bean中给依赖检验的属性设置值,而不采用原先默认的空值,属性设置例如simple,object或all,以便容器进行依赖检验。当bean的全部的属性(或某类属性)需要被明确设置或自动绑定时,依赖检验便显得很有用。
< bean id="orderService"
class="com.lizjason.spring.OrderService"
dependency-check="objects">
< property name="companyName"
value="lizjason"/>
< constructor-arg ref="orderDAO"/>
< /bean>
在这个例子里,容器确保为orderService bean设置的属性不是primitives 或者 collections。为所有的bean设置默认依赖检测也是可以的,但是我们很少这样做,是因为有些bean的属性根本就不必设置。
9. 为每个配置文件加上一个header comment
最好使用descriptive id和名称来代替在XML配置文件中的注释。此外,加上一个配置文件header也很有用处,它可以概述文件中所定义的bean。你可以选择将描述内容加入description标签中。例如:
< beans>
< description>
This file defines billing service
related beans and it depends on
baseServices.xml,which provides
service bean templates...
< /description>
...
< /beans>
使用description标签的一个好处是可以容易地利用工具从标签中选取出description(的内容)。
10. 对于任何变化,要与队友积极交流
当你重构Java代码时,你需要随时更新配置文件并且通知队友。XML配置文件也是代码,它们是应用程序的至关重要的部分,但是它们难于阅读和维护。大部分时间你既要阅读XML配置文件又要阅读运行中的Java代码。
11. Setter injection优于constructor injection
Spring提供3种类型的依赖注入: constructor injection,setter injection, 和method injection。我们一般只用前两种类型。
< bean id="orderService"
class="com.lizjason.spring.OrderService">
< constructor-arg ref="orderDAO"/>
< /bean>
< bean id="billingService"
class="com.lizjason.spring.BillingService">
< property name="billingDAO"
ref="billingDAO">
< /bean>
这个例子中,orderService类使用的是constructor injection,而BillingService类使用的是setter injection。constructor injection可以确保bean不会在一个非法状态下被创建,但是setter injection更加灵活并且更易管理,尤其当类存在很多属性并且其中一些是可选的情况下。
12. 不要滥用依赖注入
作为最后一点,Spring ApplicationContext可以替你创建Java对象,但是并不是所有的Java对象都通过依赖注入来创建的。例如,全局的对象不应该通过 ApplicationContext来创建。Spring是一个很棒的框架,但是,就易读性和易管理性而言,当定义大量bean的时候,基于XML的配置问题就会突出。过度的依赖注入会使XML配置变得复杂而且臃肿。记住!使用强大的IDE时,例如Eclipse和IntelliJ,与XML文件相比, Java代码更加易读,易维护,易管理。
总结
对于Spring的配置,XML是很优秀的方式。但当定义大量 bean时,基于XML配置会变得冗长,笨拙。Spring提供了丰富的配置选项。适当地利用其中的选项可以使XML配置清晰,但是,有些选项,例如 autowiring(自动绑定),往往会降低易读性和易维护性。文章中所列举的实例,可以帮助你创建出清晰易读的XML配置文件。
http://webservices.ctocio.com.cn/wsjavtec/465/8094965.shtml
huoyuhan收录,使用标签:Java,时间:2008-5-2 22:29:44 | 相关网摘,我也收藏
1、为什么要用Java开发Linux GUI应用
1.1 Linux GUI应用开发现状
目前Linux操作系统在市场上呈现一种"叫好不叫座"的局面,在一定的程度上与在其上运行的好用的应用程序数量太少有着的密切的关系。尽管能在Linux上运行的应用程序已经很多,但大多数都较难安装与使用,而且很多不是很稳定。
要改善Linux应用程序的易用性,大量采用GUI(图形用户界面)是一个必然的趋势。但是目前Linux平台GUI应用程序的开发仍然不太容易。尽管有不少的开放源码开发工具在极力增强其GUI应用程序开发能力,但是由于标准不统一、资料太少等原因很难推广。Kylix等商业开发工具尽管具有大公司的技术实力支撑,但由于价格等因素也难以很快推广。这种局面不尽快打破,Linux的应用就不可能得到很快的普及。
综上所述,目前Linux应用软件开发领域急需一种容易学习、成本低、效率高的GUI应用程序开发方法。
1.2 Java的特点
自1995年Sun Microsystem推出Java语言以来,Java语言与Java技术均得到了飞速的发展。因其强大的网络功能、良好的跨平台特性、较高的开发效率成为国内外软件开发领域的一种强大工具。
目前Java程序已经能运行在从Windows系列、Unix系列、Mac OS系列到Linux等几乎所有操作系统平台上,而且特别重要的是它已经能做到"一次编写,到处运行";另一方面,与C++等传统程序设计语言相比,用Java来开发GUI应用程序的难度已经大大降低;加上Java语言所具有的纯面向对象、网络功能强大等特性,使得Java非常适于用来编制应用软件。
1.3 用Java开发Linux GUI应用的优点
正由于Java语言的特点以及Linux操作系统的发展现状,使得用Java语言来开发Linux平台GUI应用程序成为了一种很好的方法。
一方面,用Java语言在Linux平台上开发GUI应用程序对于习惯了在Windows平台上用Java编程的程序员来说无须改变编程习惯。因为Java有良好的跨平台特性,在Linux下编程与在Windows下编程没什么两样,如果用纯Java编程,则程序中使用的类、属性、方法等等都不用改变,甚至还可以在Windows下编辑、编译好Java程序再直接移植到Linux下执行。
另一方面,由于在Linux平台上存在大量的优秀的开放源代码软件开发工具,包括用于Java程序开发的如NetBeans等工具,这样如果在Linux平台上用这些开放源代码软件开发工具进行Java软件开发,其所需的成本可以比在Windows下进行开发低得多。当然在Linux下也存在像Borland Jbuilder、IBM Visual Age等商业Java软件开发工具,它们都具有强大的功能、完备的文档,更有利于在Linux上进行Java软件开发。
2、用Java开发Linux GUI应用的基本方法
2.1 开发环境的安装与配置
*JDK的安装
要Linux平台上开发Java应用,最基本的方式需要下载一个JDK,这是由Sun公司免费提供的一个Java软件开发包。下载得到的文件一般形如j2sdk1.3-linux.rpm.bin,首先将其拷贝到合适的目录(如/usr/src或/usr/local等)下,执行./j2sdk1.3-linux.rpm.bin,会产生一个j2sdk1.3-linux.rpm文件,这是一个典型的RPM软件包,再用RPM软件包管理工具就可直接安装JDK,在安装过程中会提示许可信息,同意其协议就可很顺利地安装了。
*路径的配置
为了做到在任意路径下均可直接执行Java编程相关命令,需要修改/etc/profile文件,在其中设置路径。具体方法是在其中加入这样一行:
PATH="$PATH/usr/jdk1.3"
这里/usr/jdk1.3是JDK的安装路径。
2.2 基本开发过程
①编辑源程序
在Linux下有很多优秀的程序编辑器软件,例如在控制台下有著名的Emacs、vi、jed等,在X window下有Xemacs、gedit、kedit等,这些软件通常都有语法着色、自动缩进等非常适合于程序编辑的功能。要在Linux系统下开发Java程序,首先需要用这些编辑器编辑Java源程序。至于具体用哪种编辑器要依个人的喜好而定,反正可用的软件很多。
②编译源程序
正确的编辑好Java源程序后,需要对其进行编译,以生成字节码文件。假定源程序文件名为Test.java,则可用如下形式:
javac Test.java
③调试运行程序
执行守编译命令后,对于源程序中的每一个类,系统将产生一个字节码文件,文件名为对应的类名,而其扩展名为class。一般地,每一个Java程序中应该有一个主类,该类是整个程序的入口。开发Java程序的下一步就是调试运行,仍以上面提到的情况为例,运行该Java程序的方法为:
java Test
事实上解释器将解释执行Test.class文件并按其中的调用解释执行其它字节码文件。
2.3 基本开发方法
其实无论在什么平台下,GUI应用程序的基本开发方法都是相似的。一般都包括下面这样四个步骤:
①创建容器
首先要创建一个GUI应用程序,需要创建一个用于容纳所有其它GUI组件元素的载体,Java中称为容器。典型的包括窗口(Window)、框架(Frame/JFrame)、对话框(Dialog/JDialog)、面板(Panel/JPanel)等。只有先创建了这些容器,其它界面元素如按钮(Button/JButton)、标签(Label/JLabel)、文本框(TextField/JTextField)等才有地方放。
②添加组件
为了实现GUI应用程序的功能,为了与用户交换,需要在容器上添加各种组件/控件。这需要根据具体的功能要求来决定用什么组件。例如,如果需要提示信息,可用标签(Label/JLabel);如果需要输入少量文本,可用文本框(TextField/JTextField);如果需要输入较多文本,可用文本区域(TextArea/JTextArea);如果需要输入密码,可用密码域(JPasswordField)等等。
③安排组件
与传统的Windows环境下的GUI软件开发工具不同,为了更好地实现跨平台,Java程序中各组件的位置、大小一般不是以绝对量来衡量,而是以相对量来衡量。例如有时候,程序的组件的位置是按"东/East"、"西/West"、"南/South"、"北/North"、"中/Center"这种方位来标识的。因此,在组织界面时,除了要考虑所需的组件种类外,还需要考虑如何安排这些组件的位置与大小。这一般是通过设置布局管理器(Layout Manager)及其相关属性来实现的。事实上上述按方位来安排组件就是采用了Java中多种布局管理器里的BorderLayout布局管理器。
④处理事件
为了完成一个GUI应用程序所应具备的功能,除了适当地安排各种组件产生美观的界面外,还需要处理各种界面元素事件,以便真正实现与用户的交换,完成程序的功能。在Java程序中这一般是通过实现适当的事件监听者接口来完成的。比如如果需要响应按钮事件,就需要实现ActionListener监听者接口;如果需要响应窗口事件,就需要实现WindowListener监听者接口。
3、开发实例
下面是一个在Linux下用Java开发GUI应用程序的实例。该程序的主要功能是提供一个用于输入用户名与密码以登录到某一系统的界面,如果用户输入相关信息后单击"登录"按钮,系统将在窗口的下半部分显示所输入的用户名与密码信息,如果单击"退出"按钮,程序将停止运行。如下图1所示:
本程序没有提供很强的功能,目的只在于介绍在Linux上用Java编写GUI应用程序的基本方法。以下是程序的源代码,其中的注释说明了GUI应用具体的创建步骤:
//以下三行用于引入添加组件、设置布局管理器及处理事件所需的软件包
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
//下行说明主类派生自JFrame/框架类,要实现ActionListener接口以处理动作事件
class LoginFrame extends JFrame implements ActionListener {
//以下四行用于声明要加到框架窗口中的所有组件
JLabel UserLabel,PasswordLabel,UserResult,PasswordResult;
JTextField User;
JPasswordField Password;
JButton LoginButton,ExitButton;
public LoginFrame() {
super("登录");//调用父类构造方法,设置窗口标题
setSize(300,200);//设置窗口大小
getContentPane().setLayout(new GridLayout(5,2,10,10));//设置布局管理器,将窗口分成5行2列,行、列间保留10个像素的空白
//以下8行具体创建组件实例
UserLabel=new JLabel("用户名称:");
PasswordLabel=new JLabel("用户密码:");
UserResult=new JLabel(" ");
PasswordResult=new JLabel(" ");
User=new JTextField(10);
Password=new JPasswordField(10);
LoginButton=new JButton("登录");
ExitButton=new JButton("退出");
//以下两行设置用于保存结果的标签的前景色属性
UserResult.setForeground(Color.blue);
PasswordResult.setForeground(Color.blue);
//以下两行为"登录"按钮与"退出"按钮注册监听者
LoginButton.addActionListener(this);
ExitButton.addActionListener(this);
//以下八行将所有组件加入到框架窗口中
getContentPane().add(UserLabel);
getContentPane().add(User);
getContentPane().add(PasswordLabel);
getContentPane().add(Password);
getContentPane().add(LoginButton);
getContentPane().add(ExitButton);
getContentPane().add(UserResult);
getContentPane().add(PasswordResult);
//以下一行用于窗口事件监听者注册
addWindowListener(new WindowManager());
setVisible(true);//使框架窗口变为可见
}
//以下函数用于处理按钮动作事件
public void actionPerformed(ActionEvent evt)
{
if(evt.getSource()==LoginButton)//表明按下了"登录"按钮
{
UserResult.setText("用户名称:"+User.getText());
PasswordResult.setText("用户密码:"+Password.getPassword());
}
else
if(evt.getSource()==ExitButton)//表明按下了"退出"按钮
{
dispose();
System.exit(0);
}
}
//以下为系统主函数,是程序的入口
public static void main(String args[]) {
LoginFrame mainFrame = new LoginFrame();//创建框架窗口实例
}
}
class WindowManager extends WindowAdapter//窗口事件监听者类
{
public void windowClosing(WindowEvent evt)//本方法用于窗口关闭事件
{
JFrame frame=(JFrame)(evt.getSource());
frame.dispose();
System.exit(0);
}
}
4、总结
用Java语言来开发Linux平台的图形用户界面应用程序,容易理解、容易学习、环境要求低、开发效率高,而且开发出来的应用程序可移植性好,可以直接移植到其它平台上运行而不会发生多大变化。这对于快速开发Linux GUI应用软件,丰富Linux平台应用软件的种类,增强Linux应用软件功能,从而更好地普及Linux系统的应用都有巨大的推动作用。
http://webservices.ctocio.com.cn/wsare/304/8095304.shtml
huoyuhan收录,使用标签:Java,时间:2008-5-2 22:29:04 | 相关网摘,我也收藏
使用Heritrix(Version:1.12.1)做了一个测试,原来并不是任何网站都可以被网络爬虫抓取到,其实本来就应该是这样,如果一些恶意爬虫没有规则地对网站进行抓取,势必增加网站的负荷。
而且,对于一个网站来说,可以通过一定的措施来设置网站接受爬虫的范围。一些应该受到保护的资源是不应该被网络爬虫抓取到的,管理员可以在网站中进行设置,运行哪些爬虫,限制哪些爬虫。
本来想拿我的百度空间(http://hi.baidu.com/shirdrn)作为种子页面,但是结果是令人失望的,根本就没有抓取到任何我想要的东西,比如我的文章、我的图片。
在我设置的Heritrix-1.12.1\jobs\Hi.Baidu-20080425073113453\mirror目录下面就是目录:\hi.baidu.com\。打开这个文件夹,下面只有一个robots.txt文件,内容如下:
User-agent: Baiduspider
Disallow:
User-agent: Googlebot
Disallow:
User-agent:
Disallow: /
User-agent: MSNBot
Allow: /
这个robots.txt文件就是百度(www.baidu.com)网站管理员设置的爬虫规则。并不是任何User-agent都可以对百度进行抓取。通过上面robots.txt文件的内容,我们可以了解到:
百度的网络爬虫叫做“Baiduspider”,百度当然运行百度网络蜘蛛进行抓取网站内容了,上面“Disallow:”就表示对hi.baidu.com目录下的内容,没有任何限制;
百度允许Google的网络爬虫访问百度网站,Google的网络爬虫叫做“Googlebot”,不限制Google的网络爬虫抓取hi.baidu.com目录下的内容;
百度还允许MSN(http://www.msn.com/)的网络爬虫对百度网站进行访问,“Allow: /”说明了可以抓取hi.baidu.com下面的内容;
这么说,百度空间拒绝其它的User-agent了,下面两行:
User-agent:
Disallow: /
其中“Disallow: /”表示对没有指定User-agent的网络爬虫进行限制,不允许抓取hi.baidu.com下面的内容。
如果使用Heritrix时,新建抓取任务时,种子页面选择www.baidu.com,则在目录Heritrix-1.12.1\jobs\Baidu-20080425072302515\mirror\www.baidu.com下面也存在一个robots.txt文件,内容是这样的:
User-agent: Baiduspider
Disallow: /baidu
User-agent: *
Disallow: /shifen/dqzd.html
每个站点只允许设置一个robots.txt文件,而且该文件不许用小写,存放在网站的根目录之下。在使用Heritrix抓取的过程中,种子页面选择www.baidu.com,在目录Heritrix-1.12.1\jobs\Baidu-20080425072302515\mirror下面存在一个和目录www.baidu.com并列的目录/utk.baidu.com/,该目录下面仍然能看到一个robots.txt文件,该文件内容有点不同:
404 Not Found
Not Found
The requested URL /robots.txt was not found on this server.
因为这个目录应该是目录www.baidu.com的子目录,自然找不到管理员实际设置的robots.txt文件,但它是在首页中的链接,与首页同级,Heritrix认为它是一个网站的根目录。
我也测试了一下Google网站,在目录Heritrix-1.12.1\jobs\Google-20080425072048531\mirror\www.google.com下面的robots.txt文件内容如下:
User-agent: *
Allow: /searchhistory/
Disallow: /news?output=xhtml&
Allow: /news?output=xhtml
Disallow: /search
Disallow: /groups
Disallow: /images
Disallow: /catalogs
Disallow: /catalogues
Disallow: /news
Disallow: /nwshp
Disallow: /?
Disallow: /addurl/image?
Disallow: /pagead/
Disallow: /relpage/
Disallow: /relcontent
Disallow: /sorry/
Disallow: /imgres
Disallow: /keyword/
Disallow: /u/
Disallow: /univ/
Disallow: /cobrand
Disallow: /custom
Disallow: /advanced_group_search
Disallow: /advanced_search
Disallow: /googlesite
Disallow: /preferences
Disallow: /setprefs
Disallow: /swr
Disallow: /url
Disallow: /default
Disallow: /m?
Disallow: /m/lcb
Disallow: /m/search?
Disallow: /wml?
Disallow: /wml/search?
Disallow: /xhtml?
Disallow: /xhtml/search?
Disallow: /xml?
Disallow: /imode?
Disallow: /imode/search?
Disallow: /jsky?
Disallow: /jsky/search?
Disallow: /pda?
Disallow: /pda/search?
Disallow: /sprint_xhtml
Disallow: /sprint_wml
Disallow: /pqa
Disallow: /palm
Disallow: /gwt/
Disallow: /purchases
Disallow: /hws
Disallow: /bsd?
Disallow: /linux?
Disallow: /mac?
Disallow: /microsoft?
Disallow: /unclesam?
Disallow: /answers/search?q=
Disallow: /local?
Disallow: /local_url
Disallow: /froogle?
Disallow: /products?
Disallow: /froogle_
Disallow: /product_
Disallow: /products_
Disallow: /print
Disallow: /books
Disallow: /patents?
Disallow: /scholar?
Disallow: /complete
Disallow: /sponsoredlinks
Disallow: /videosearch?
Disallow: /videopreview?
Disallow: /videoprograminfo?
Disallow: /maps?
Disallow: /mapstt?
Disallow: /mapslt?
Disallow: /maps/stk/
Disallow: /mapabcpoi?
Disallow: /translate?
Disallow: /ie?
Disallow: /sms/demo?
Disallow: /katrina?
Disallow: /blogsearch?
Disallow: /blogsearch/
Disallow: /blogsearch_feeds
Disallow: /advanced_blog_search
Disallow: /reader/
Disallow: /uds/
Disallow: /chart?
Disallow: /transit?
Disallow: /mbd?
Disallow: /extern_js/
Disallow: /calendar/feeds/
Disallow: /calendar/ical/
Disallow: /cl2/feeds/
Disallow: /cl2/ical/
Disallow: /coop/directory
Disallow: /coop/manage
Disallow: /trends?
Disallow: /trends/music?
Disallow: /notebook/search?
Disallow: /music
Disallow: /browsersync
Disallow: /call
Disallow: /archivesearch?
Disallow: /archivesearch/url
Disallow: /archivesearch/advanced_search
Disallow: /base/search?
Disallow: /base/reportbadoffer
Disallow: /base/s2
Disallow: /urchin_test/
Disallow: /movies?
Disallow: /codesearch?
Disallow: /codesearch/feeds/search?
Disallow: /wapsearch?
Disallow: /safebrowsing
Disallow: /reviews/search?
Disallow: /orkut/albums
Disallow: /jsapi
Disallow: /views?
Disallow: /c/
Disallow: /cbk
Disallow: /recharge/dashboard/car
Disallow: /recharge/dashboard/static/
Disallow: /translate_c?
Disallow: /s2
Disallow: /transconsole/portal/
Disallow: /gcc/
Disallow: /aclk
Disallow: /cse?
Disallow: /tbproxy/
Disallow: /MerchantSearchBeta/
Google网站设置得比较详细。
从上面的robots.txt文件中设置,可以看出,这对网络爬虫进行访问的设置是非常重要的,这时网站管理员应该做的的任务。robots.txt文件中设置的一般来说是限制的较多。下面是在robots.txt文件中进行设置的规则:
■ 拒绝网络爬虫对整个网站的抓取
User-agent: *
Disallow: /
■ 允许网络爬虫对整个网站的抓取
User-agent: *
Disallow:
■ 拒绝网络爬虫对网站指定的目录抓取
User-agent: *
Disallow: /importants/
Disallow: /website/test/
……
■ 允许某些搜索引擎的网络爬虫对网站抓取
User-agent: Baiduspider
Disallow:
User-agent: Googlebot
Disallow:
User-agent: MSNBot
Allow: /
■ 限制网络爬虫对网站中页面的抓取
User-agent: *
Disallow: /security/subs/securityitems.jsp
Disallow: /admin/admin.htm
http://daihaixiang.blog.163.com/blog/static/3830134200832844918738/
huoyuhan收录,使用标签:Java,时间:2008-5-2 22:28:44 | 相关网摘,我也收藏
摘要:本文主要介绍采用JDBC、ODBC接口实现了与SQL Server2000数据库的连接,并利用Java应用程序对其进行访问,同时通过图形用户界面实现了简单的查询功能。
关键词:Java,SQL Server,数据库
前言
数据库技术和网络技术是当今计算机领域的两大热门话题,数据库技术自产生以来,在技术上已发展成熟。而作为前端访问的开发工具和环境仍处在不断完善和发展之中,除了网络上使用的ASP、PHP、JSP作为前端连接数据库技术外,小型系统上常用访问数据库技术有Delphi、Visual Basic、PowerBuilder及VC++等,而Java也是其中之一。
Java语言是编写数据库应用程序的杰出语言之一,它提供了方便访问数据的技术。利用Java语言中的JDBC技术,用户能方便地开发出基于Web网页的数据库访问程序,从而扩充网络应用功能。JDBC(Java Database Connectivity,Java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一的访问接口。JDBC由一组用Java语言编写的类与接口组成,通过调用这些类和接口所提供的方法,用户能够以一致的方式连接多种不同的数据库系统(如Access、SQL Server 2000、Oracle、Sybase等),进而可使用标准的SQL语言来存取数据库中的数据,而不必再为每一种数据库系统编写不同的Java程序代码。
SQL Server2000是微软公司新一代的数据库产品,它是在SQL Server7.0建立的坚固基础上产生的,并对SQL Server7.0做了大量扩展。SQL Server2000通过高端硬件平台以及最新网络和存储技术的支持,可为最大的Web站点和企业级应用提供可扩展性和高可靠性。
Java通过JDBC、ODBC接口访问数据库
1 JDBC、ODBC接口
Java应用程序通过JDBC API(java.sql)与数据库连接,而实际的动作则是由JDBC驱动程序管理器(JDBC Driver Manager)通过JDBC驱动程序与数据库系统进行连接。ODBC(Open DataBase Connectivity)即开放式的接口,它为用户提供了一个访问关系数据库的标准接口,对于不同的数据库它提供了一套统一的API;可以使应用程序通过API访问任何提供了ODBC驱动程序的数据库,而目前所有的关系数据库都提供了ODBC驱动程序,所以ODBC已经成为数据库访问的业界标准,并得到了广泛应用。
JDBC-ODBC桥是一种JDBC驱动程序,它通过将JDBC操作转换为ODBC操作来实现的。利用JDBC-ODBC桥可以使程序开发人员不需要学习更多的知识就可以编写JDBC应用程序,并能够充分利用现有的ODBC数据源。JDBC-ODBC桥驱动程序可以使JDBC能够访问几乎所有类型的数据库。
2 利用Java应用程序访问SQL Server2000数据库
(1)建立数据库
启动“Microsoft SQL Server2000”,打开“企业管理器”在“数据库”中建立名为“mydata”的数据库,并在其下制作名为“wuzi”的数据表,如图1所示。
图1
(2)建立(ODBC)数据源和驱动程序
在控制面板上通过“管理工具”的“数据源(ODBC)”打开“ODBC数据源管理器”对话框,单击“系统DSN”选项卡,然后单击“添加”按钮,得到“创建数据源”对话框,选择“SQL Server”并单击“完成”按钮,在出现的“建立新的数据源到SQL Server”对话框中的“数据源名称”项填写“wzgl”并选取“服务器名”,然后单击“下一步”按钮,选择“使用网络登录ID的Windows NT验证”项目,单击“下一步”按钮,把默认的数据库改为“mydata”,再单击“下一步”,单击“完成”按钮,然后可以单击“测试数据源”,成功后,单击“确定”按钮,完成了(ODBC)数据源和驱动程序的建立。
3 编写代码
将代码保存在jdbc.java文件中:
import java.awt.*;import java.awt.event.*;import java.sql.*;
public class jdbc //定义主类
{
public static void main(String args[])
{
GUI gui=new GUI(); //创建类GUI的对象
gui.pack(); //装载执行GUI类
}
}
class GUI extends Frame implements Action Listener
{
TextArea text; Panel panel; TextField sno; Button btn;
GUI() //构造方法
{
super("物资情况查询");setLayout(new BorderLayout());
setBackground(Color.cyan);
setVisible(true);text=new TextArea();
btn=new Button("查询");
sno=new TextField(16);
panel=new Panel();
panel.add(new Label("输入被查询的物资编号:"));
panel.add(sno); panel.add(btn);
add("North",panel); add(text,"Center");
text.setEditable(false);btn.addActionListener(this);
addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
setVisible(false);
System.exit(0);
}
});
}
public void actionPerformed(ActionEvent e) {
if(e.getSource()==btn) //当用户按下查询按钮时
{
text.setText("查询结果"+'\n'); //显示提示信息
try
{
Liststudent();
}
catch(SQLException ee) { }
}
}
public void Liststudent() throws SQLException //针对数据库的操作
{
String bh,mc,xh,lb,dw,sj;
int sl; float dj,je;
try
{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
}
catch(ClassNotFoundException e) { }
Connection con=DriverManager.getConnection("jdbc:odbc:wzgl");
Statement sql=con.createStatement(); //创建Statement对象
ResultSet rs=sql.executeQuery("select * from wuzi");
while(rs.next()) //输出被查询的情况
{
bh=rs.getString("物资编号");
mc=rs.getString("物资名称");
xh=rs.getString("规格型号");
lb=rs.getString("类别");
dw=rs.getString("计量单位");
sl=rs.getInt("数量");
dj=rs.getFloat("单价");
je=rs.getFloat("金额");
sj=rs.getDate("时间").toString();
if(bh.trim().equals(sno.getText().trim()))
{
text.append('\n'+"物资编号"+" "+"物资名称"+" "+"规格型号"+" "+"类别"+" "+"计量单位"+" "+"数量"+" "+"单价"+" "+"金额"+" "+"时间"+'\n');
text.append('\n'+bh+" "+mc+" "+xh+" "+lb+" "+dw+" "+sl+" "+dj+" "+je+" "+sj+" "+'\n');
}
}
}
}
4 运行程序
首先编绎:javac jdbc.java
编译成功后,执行:java jdbc
执行后在文本框中输入要查询物资的物资编号,单击“查询”按钮,在下面的文本框中显示被查询物资的所有信息。如图2所示。
图2
结束语
本文只涉及到对SQL Server2000数据库访问的查询操作,也可以进行插入、删除、修改等操作。随着数据库技术的迅猛发展和Java版本的不断升级,利用Java对数据库的访问会越来越简单,并且应用的范围也会越来越广,当然还会有许多新的功能等待我们去开发。
http://hi.baidu.com/%D5%C5%F3%C6%D0%F8/blog/item/6acd2501d3bbb6d3267fb56c.html
huoyuhan收录,使用标签:Java,时间:2008-5-2 22:27:31 | 相关网摘,我也收藏
1. 模板目录->主题目录->主题(模板文件) 这是模板/主题的目录组织方式。以实际为例,打开 struts2-core-2.x.x.jar 可以看到里面有一个 template,在 template 下有5个目录 ajax、css_xhtml、simple、xhtml 和 archive,其中前四个分别是 ajax、css_xhtml、simple、xhtml 主题的目录,每个主题目录中有各自的模板文件,主要是 ftl 文件,还有 css 和 js 文件。最后一个 archive 是归档的主题目录,其下又有 ajax、simple、xhtml、模板文件是 .vm 文件。由此可知 Struts2 大力推荐的模板语言是 FreeMarker,而不是 Velocity,以后要好好看看 FreeMarker,只知道 FreeMarkder 更 XML 化。
2. 上面看到模板目录名是 template,是 struts2-core-2.x.x.jar 中,其实目录名是由 struts.ui.templateDir 常量来指定的,只是默认值是 template。意味着 Struts 2 从 Web 应用的 template 目录或 CLASSPATH 的 template 目录(包) 中依次加载特定的模板文件。
3. 比如我们使用一个 select 标签,且指定主题为 xhtml,则加载模板文件的顺序为 (1) Web 应用/template/xhmlt/select.flt (2) CLASSPATH/template/xhtml/select.ftl。Struts2 默认是用的 FreeMarkder 模板技术,可设置常量 struts.ui.templateSuffix 来改变默认的模板技术,可选值有 ftl、vm、jsp。但是对于 vm 和 jsp 要自己提供完整的实现,Struts2 可没帮你做这些。
4. 有时候我们想要自定义主题,如你希望输入框前的标签显示红颜色,你不想要校验错误提示在输入框正上方而是右边。当然你可以修改 struts2-core-2.x.x.jar 中 template 下某个主题的模板文件,或拷一份到 Web 应用目录的 template 目录修改要定制的模板,这样做总有些不爽。Struts2 还支持两种更灵活的主题定制方式。包装和继承现有主题,可以同时使用。
最简单的主题定制方式是利用主题模板的加载优先级,把自定义的模板文件放在优先级高的目录,比如放一个 text.ftl 在 WEB-INF/classes/template/xhtml/ 目录下,它将覆盖掉 struts2-core-2.x.x.jar 里的 template/xhtml/text.ftl 的定义。
5. 先看一个包装的例子,在 xhtml 下的 combobox.ftl 的内容如下:
<#include "/${parameters.templateDir}/${parameters.theme}/controlheader.ftl" />
<#include "/${parameters.templateDir}/simple/combobox.ftl" />
<#include "/${parameters.templateDir}/xhtml/controlfooter.ftl" /><#nt/>
它就是对 simple/combobox.ftl 的基础上包装上一个 controlheader.ftl 和一个 controlfooter.ftl,包装的局限性是你仍然是要为每一个 UI 组件提供单独模板文件,即全套的。
和设计模式中的包装(装饰)模式如出一辙,如 BufferedInputStream 包装了 InputStream,但在 BufferedInputStream 提供了全套的和 InputStream 一样的操作方法。
6. 再说继承来自定义主题,如果简单改变个别 UI 的风格,继承就是最为高效的了。此继承与 Java 的继承(即extends) 也是一马事。要改变或要新加的用自己定义的,其他的延用父主题的。举个例子,自定义一个名为 custom 的主题继承自 xhtml,只改变 select 标签的风格,你要做的就是编辑自己的一个 select.ftl 放在 WEB-INF/classes/template/custom 下,并在此目录下放一文件 theme.properties,内容是:
#指定该主题以 xhtml 为基础进行扩展
parent=xhml
使用可指定给 UI 标签,例如
7. 简单说一下 Struts2 的内建主题,包括 simple、xhtml、css_xhtml 和 ajax。simple 主题不用多说,比Struts1 的 html 标签还弱些,只对应简单的 html 元素,不生成额外内容。xhtml 是默认主题,是对 simple 主题的包装和扩展(也就是继承),该主题下有一个 head.ftl 用来导入 javascript 类库(如 dojo)。xhtml 在 simple 的基础上增加了以下特性:
1) 针对 HTML 标签(如 textfield和select标签) 使用标准的两列表格布局
2) 每个 HTML 标签有 label 属性,默认左边显示,可通过 labelposition 属性设定位置
3) 自动输出后台校验错误或 javascript 前端校验错误
8. 继续 Struts2 的内建主题的话题。css_xhtml 主题是对 xhtml 的扩展,显示是加入了 css 样式控制特性。ajax 主题是对 xhtml 主题的扩展,在 xhtml 主题的每个标签增加了 ajax 的支持(以 Djoj 和 DWR 为基础)。所增 Ajax 特性有:
1) Ajax 方式的客户端校验
2) 远程表单的异步提交
3) 高级 div 标签,允许局部更新
4) 高级 a 标签,允许动态加载并执行远端的 javascript 代码。
5) 提供支持 ajax 的 tabbedPanel
6) 提供“富客户端” 模型的 pub-sub 事件模型
9. 先前有网友问过我,他用了 Struts2 的校验,但是错误输出是在输入框的上方,但希望错误信息是显示在输入框的右方,该如何做。当时我只告诉了他要修改模板文件,也只是大概告诉了他是在某个 template 目录下的一个 ftl 文件,因那时具体操作自己也不太清楚。现在知道了线索,但实际修改还是很麻烦的。
输入框 默认是用的 xhtml/text.flt 模板,text.ftl 包装了 smple/text.ftl,错误信息可以追溯发现是在 controlheader-core.ftl 中定义显示的,所以你可以把 xthml/text.ftl 和 controlheader-core.ftl 拷到 WEB-INF/classes/template/xhtml 目录中进行修改,WEB-INF/classes/template/xhtml 中的模板文件是优先于 struts2-core-2.x.x.jar 里的 template/xhtml 目录中的模板文件加载。
10. 看有些地方只笼统介绍说:所有表单元素都存在一个特殊的属性:form,这个属性引用元素所在表单,通过该属性实现表单元素与表单间的交互,例如可通过 ${parameters.form.id} 访问表单的 ID。对这句话我只是感到一头雾水,查看像 等标签并无 form 属性,用 也看不到输出所在表单的 ID。去网上找找,才知道前面那句话有出入,其实说的是在主题模板文件里的用法,打开一些主题模板文件,如 combobox.ftl 或 controlheader-core.ftl 文件,你就能看到许多的 parameters 的表示法-- parameters.required、parameters.id。想见一下 parameters 属性才是根本,它代表了表单元素的属性集,parameters 说来还有点像 this,this.id、this.form.id、this.required 等等...
http://www.blogjava.net/Unmi/archive/2008/04/29/196973.html
http://www.blogjava.net/Unmi/archive/2008/04/29/196973.html
huoyuhan收录,使用标签:Java,时间:2008-5-2 22:20:25 | 相关网摘,我也收藏
如果安装JDK1.3那么安装程序一定会同时安装两套JRE。
一套位于 jdk\jre目录
一套位于program files\JavaSoft目录
如果是JDK 1.4可以选择是否安装program files\java目录下的jre,但是jdk安装目录下的jre这套jre必须安装
JRE与PC比较
JRE: java类函数库>原生函数库.dll>JAVA虚拟机(jvm.dll)>帮助函数库.dll
PC: Win32 API .dll>CPU
编写好的Java源文件必须要有JRE才能帮助我们运行,Java虚拟机只是JRE里的一个成员而已,或者说jvm只是jre里头一个动态连接函数库,
jdk里面的jre一般用于运行java本身的程序,比如javac,等等.programfiles下面的jre用于运行用户编写的java程序.
JRE下的bin\client 或者 bin\server 的jvm.dll就是JVM了
----------------------------
在刚装好jdk,没有对计算机进行任何设置时,进入命令行窗口
C:\Documents\Administrator>java -version
java version "1.5.0_11"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_11-b03)
Java HotSpot(TM) Client VM (build 1.5.0_11-b03, mixed mode, sharing)
C:\Documents\Administrator>java -server -version
Error: no `server' JVM at `C:\Program Files\Java\jre1.5.0_11\bin\server\jvm.dll'
-----------------------------------
当设置path路径中包含jdk\bin目录后
----------------------------
C:\>set path="C:\Program Files\Java\jdk1.5.0_11\bin";%path%;
C:\>java -version
java version "1.5.0_11"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_11-b03)
Java HotSpot(TM) Client VM (build 1.5.0_11-b03, mixed mode, sharing)
C:\>java -server -version
java version "1.5.0_11"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_11-b03)
Java HotSpot(TM) Server VM (build 1.5.0_11-b03, mixed mode)
-server 的-version就可以显示出来了
----------------------------
JDK里用Java所写的开发工具 如javac.exe jar.exe都存放在JDK安装目录\lib\tools.jar 这个档案中
javac.exe 只是一个包装器(wrapper),而制作目的是为了让开发者免于输入太长的指令。
实际等于:
java -classpath x:\jdk1.xx\lib\tools.jar com.sun.tools.javac.Main
当用j2me开发palm应用程序的时候,工具会帮我们打包jar档,然后用一个RPC档的外壳罩住,让java程序看起来像是一个原生(native)的应用程序。
用.NET 开发出来的执行文件也是一个包装器的概念。
JDK里面的工具几乎全是用java所写的,所以JDK本身就是Java应用程序,因此要用JDK附的工具来开发Java程序,
也必须要自行附一套JRE才行。这就是JDK安装目录\jre下需要一套JRE的原因。
位于program files\下的那套JRE就是拿来执行我们自己写的java应用程序。不过,两套中任何一套JRE 都可以拿来执行我们所撰写的Java 应用程序,
可是JDK 内附的开发工具在预设使用包装器(.exe) 来启动的情形下,都会自己去选用\jre 底下那套JRE。
------------------------
到底是执行哪一个java.exe
java xxx
当一台机器上有多个jvm可选择的时候,jvm的选择步骤:
1)当前目录有没有jre目录(不准确),
2)父目录下的jre子目录
3)注册表HEKY_LOCAL_MACHINE\SoftWare\Java\Java Runtime Environment\
所以当运行的是jdk\bin\java.exe的时候,用的jre是bin的父目录jdk下面的jre\
运行java.exe找到了jre后有一个验证程序,verify.dll验证jre和java.exe的版本是否一致,如果不一致则会发生错误
一般把常用的工具档放到JDK目录\jre\lib\ext下
把有关安全机制的配置文件放到
JDK目录\jre\lib\security下
调用了其他Java 函数库的程序,在编译阶段没有问题,可是却无法执行,显示ClassNotFoundException的原因可能是:
在system32和jdk\bin目录下都有java.exe 而 javac.exe 只有在jdk\bin目录下有
javac.exe 会自动调用JDK所在目录下的那套JRE ,因此在编译时JVM会找到函数库,所以编译不会发生问题,
但在执行时,键入java xxx的时候会优先执行 system32 下的java.exe
因此会自动调用program files目录下的那套JRE(稍后解释)
所以要执行就必须把外部jar文件放到相应jre\lib\ext目录下
JDK\jre\bin\下有两个目录 server,client
两个目录下都会有jvm.dll
client目录下的jvm.dll较小
server目录下的较大
-----------------------
系统默认path
C:\Documents\Administrator>set path
Path=C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH
在system32目录 底下找不到JRE 目录,在c:\windows目录 也找不到JRE 目录的情况下,根据下一个逻辑,就是去查询注册表
C:\Program Files\Java\jre1.xx 该目录下的bin 子目录却只有看到client 子目录,却没有看到server 子目录。这就是为何在一开始执行
java -server -version会报错的原因
------------------------------------
============================
============================
JAVA 类加载器
============================
============================
有了动态性,我们的应用程序就可以在不用全盘重新编译的情况下更新系统,或者在不用停止主程序运作的情况下,除去系统中原有的bug,或者是增加原本不具备的新功能。
一般来说,常见的程序语言先天上并不具有动态性的本质,如C、C++本身就不具备动态性。因此,为了让这些本身不具有动态性的程序语言具有某种程度的动态性,
就必须依赖底层的操作系统提供一些机制来实现动态性,Windows 操作系统底下的动态联结函式库(Dynamic Linking Library) 和Unix 底下的共享对象(Share Object)
要运用这些底层操作系统所提供的机制必须多费一些功夫来撰写额外的程序代码(例如Windows 平台上需要使用 LoadLibrary() 与GetProcAddress() ,
两个Win32 API 来完成动态性的需求),这些额外撰写的程序代码也会因为作业平台的不同而不同,毕竟这些额外的程序代码与程序本身的运作逻辑甚少关联,
所以维护起来虽不算复杂,但仍有其难度。
每个类对java机来说,都是一个独立的动态联结函数库,只不过扩展名不是.dll或.so 而是.class
所以可以在不重新编译其他java程序代码的情况下,只修改需要修改的执行单位,并放入文件系统中,等下次该java虚拟机重新启动时,
这个逻辑上的Java应用程序就会因为加载了新修改的.class文件,自己的功能也做了更新。这是一个最基本的动态性功能。
JSP/Servlet之类的Web Container或者高档的Application Server 里的EJB Container他们会提供一个Hot Deployment功能。
即在不关闭Web Server的情况下,放入已编译好的新Servlet以取代旧的Servlet,下次Http request时,就会自动释放旧的servlet而重载新的servlet。
程序运行时需要的核心类库位于 jre\lib\rt.jar中
类加载器的作用就是把类从表态的硬盘 .class文件,复制一份到内存中,并做一此 始化工作
java.exe就是利用几个原则找到JRE后,把.class直接转交给JRE运行后便功成身退
public class test
{
public static void main(String[] args)
{
System.out.println("Hello DD");
}
}
javac test.java
java -verbose:class test
------------------------------------------------------
classloader的两种载入方式:
1)pre-loading预先载入,载入基础类
2)load-on-demand按需求载入
只有实例化一个类时,该类才会被classloader载入,仅仅申明并不会载入
基础类库是预先加载的(pre-loading)
用户所写的一般类是按需加载的(load-on-demand)
按需加载
三个类
public class A
{
public void print()
{
System.out.println("Using Class A");
}
}
public class B
{
public void print()
{
System.out.println("Using Class B");
}
}
public class Main
{
public static void main(String[] args)
{
A a=new A();
B b;
a.print();
}
}
javac *.java
java -verbose:class Main
[Loaded Main from file:/C:/]
[Loaded A from file:/C:/]
Using Class A
[Loaded java.lang.Shutdown from shared objects file]
[Loaded java.lang.Shutdown$Lock from shared objects file]
没有看到
[Loaded Main from file:/C:/]
[Loaded A from file:/C:/]
[Loaded B from file:/C:/]
--------------------------------------------
动态加载的例子
三个类
public class Word
{
public void start()
{
System.out.println("Word start");
}
}
public class Excel
{
public void start()
{
System.out.println("Excel start");
}
}
public class Office
{
public static void main(String[] args)
{
if(args.length!=1)
{
return ;
}
if(args[0].equals("Word"))
{
Word w=new Word();
w.start();
}else if(args[0].equals("Excel"))
{
Excel e=new Excel();
e.start();
}
}
}
依需求加载的优点是节省内存,但是仍有其缺点。举例来说,当程序第一次用到该类别的时候,系统就必须花一些额外的时间来加载该类别,使得整体执行效能受到影响,
尤其是由数以万计的类别所构成的Java 程序。可是往后需要用到该类别时,由于类别在初次加载之后就会被永远存放在内存之中,直到Java 虚拟机关闭,
所以不再需要花费额外的时间来加载。
总的来说,就弹性上和速度上的考虑,如此的设计所带来的优点(弹性和省内存)远超过额外加载时间的花费(只有第一次用到时),因此依需求加载的设计是明智的选择。
如果我们新增了Access.java 和PowerPoint.java 这两个新类别时,
Office.java 里的主程序就必须增加两个if … else 的循环
那么如何来更好的展示java在可扩展性的优势呢
----------------------------------------------------------
使JAVA程序更有动态性的方法有两种
1)implicit隐式,即利用实例化才载入的特性来动态载入class
2)explicit显式方式,又分两种方式:
1)java.lang.Class的forName()方法
2)java.lang.ClassLoader的loadClass()方法
隐式的:new关键字 生成类的实例
第一种方法: Class.forName() 加载类
一个接口
public interface Assembly
{
public void start() ;
}
三个类
public class Office
{
public static void main(String args[]) throws Exception
{
Class c = Class.forName(args[0]) ;
/*Object o = c.newInstance() ;
Assembly a = (Assembly) o ;*/
Assembly a = (Assembly) c.newInstance();
a.start() ;
}
}
public class Word implements Assembly
{
public void start()
{
System.out.println("Word Start")
}
}
public class Excel implements Assembly
{
public void start()
{
System.out.println("Excel Start")
}
}
--------------------------------------------
有两个forName()方法,一个是只有一个参数的(就是之前程序之中所使用的):
public static Class forName(String className)
另外一个是需要三个参数的:
public static Class forName(String name, boolean initialize,ClassLoader loader)
这两个方法,最后都是连接到原生方法
forName0(),
其宣告如下:
private static native Class forName0(String name,boolean initialize, ClassLoader loader) throws ClassNotFoundException;
只有一个参数的forName()方法,最后调用的是:
forName0(className, true,ClassLoader.getCallerClassLoader());
而具有三个参数的forName()方法,最后调用的是:
forName0(name, initialize, loader);
关于名为loader 这个参数的用法
public class Office
{
public static void main(String args[]) throws Exception
{
Class c = Class.forName(args[0],true,null) ; //line
/*Object o = c.newInstance() ;
Assembly a = (Assembly) o ;*/
Assembly a = (Assembly) c.newInstance();
a.start() ;
}
}
C:\>java Office Excel
Exception in thread "main" java.lang.ClassNotFoundException: Excel
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:242)
at Office.main(Office.java:9)
//line前加上一行,再修改改为
Office o=new Office();
Class c=Class.forName(args[0],true,o.getClass().getClassLoader());
最终代码为
public class Office
{
public static void main(String args[]) throws Exception
{
Office o=new Office();
Class c = Class.forName(args[0],true,o.getClass().getClassLoader()) ;
Assembly a = (Assembly) c.newInstance();
a.start() ;
}
}
就可以运行了
只有一个参数的forName() 方法, 由于在内部使用了ClassLoader.getCallerClassLoader() 来取得加载呼叫他的类别所使用的类别加载器,
和我们自己写的程序有相同的效用。( 注意,ClassLoader.getCallerClassLoader()是一个private 的方法,所以我们无法自行叫用,
因此必须要自己产生一个Office 类别的实例,再去取得加载Office 类别时所使用的类别加载器)。
三个参数的 Class.forName()的第二参数
给类添加一个静态代码块
public class Word implements Assembly
{
static {
System.out.println("Word Static Initialization");
}
public void start()
{
System.out.println("Word start");
}
}
public class Office
{
public static void main(String args[]) throws Exception
{
Office o=new Office();
System.out.println("类准备载入");
//Class c=Class.forName(args[0],true,o.getClass().getClassLoader());
Class c=Class.forName(args[0],false,o.getClass().getClassLoader());
System.out.println("准备实例化");
Assembly a1=(Assembly)c.newInstance();
a1.start();
Assembly a2=(Assembly)c.newInstance();
a2.start();
}
}
为true 时
C:\>java Office Word
类准备载入
Word Static Initialization
准备实例化
Word start
Word start
为false时
C:\>java Office Word
类准备载入
准备实例化
Word Static Initialization
Word start
Word start
静态初始化区块是在类别第一次被实例化的时候才会被呼叫那仅仅一次
注意:
不管您使用的是new 来产生某类别的实例、或是使用只有一个参数的forName()方法,内部都隐含了”加载类别+呼叫静态初始化区块”的动作。
而使用具有三个参数的orName()方法时,如果第二个参数给定的是false,那么就只会命令类别加载器加载该类别,但不会叫用其静态初始化区块,
只有等到整个程序第一次实例化某个类别时,静态初始化区块才会被叫用
static块在什么时候执行?
1)当调用forName(String)载入class时执行,如果调用ClassLoader.loadClass并不会执行.forName(String,false,ClassLoader)时也不会执行.
2)如果载入Class时没有执行static块则在第一次实例化时执行.比如new ,Class.newInstance()操作
3)static块仅执行一次
public class Office
{
public static void main(String args[]) throws Exception
{
Office o=new Office();
System.out.println("类准备载入");
ClassLoader loader=o.getClass().getClassLoader();
Class c=loader.loadClass(args[0]);
System.out.println("准备实例化");
Assembly a1=(Assembly)c.newInstance();
a1.start();
Assembly a2=(Assembly)c.newInstance();
a2.start();
}
}
------------------------------------------------
第二种:直接使用ClassLoader 类别的loadClass() 方法来加载类
此方式只会把类加载至内存,并不会调用该类别的静态初始化区块,而必须等到第一次实例化该类别时,该类别的静态初始化区块才会被叫用。
这种情形与使用Class 类别的forName()方法时,第二个参数传入false 几乎是相同的结果。
另一种方式
public class Office
{
public static void main(String args[]) throws Exception
{
Office o=new Office();
Class co=Office.class;
System.out.println("类准备载入");
ClassLoader loader=co.getClassLoader();
Class c=loader.loadClass(args[0]);
System.out.println("准备实例化");
Assembly a1=(Assembly)c.newInstance();
a1.start();
Assembly a2=(Assembly)c.newInstance();
a2.start();
}
}
归纳
1.
Office o=new Office();
ClassLoader loader=o.getClass().getClassLoader();
调用对象的getClass()方式取得该对象的引用,再调用该引用的getClassLoader()方法取得该对象类加载器的引用
2.
Office o=new Office();
Class co=Office.class;
ClassLoader loader=co.getClassLoader();
直接定义一个此对象类的.class 类引用然后由此对象的getClassLoader()方法取得该对象类加载器的引用
然后 Class c=loader.loadClass(args[0]);
Class类的实例.
>>Class类无法手工实例化,当载入任意类的时候自动创建一个该类对应的Class的实例,
>>某个类的所有实例内部都有一个栏位记录着该类对应的Class的实例的位置.,
>>每个java类对应的Class实例可以当作是类在内存中的代理人.所以当要获得类的信息(如有哪些类变量,有哪些方法)时,都可以让类对应的Class的实例代劳。
java的Reflection机制就大量的使用这种方法来实现
>>每个java类都是由某个classLoader(ClassLoader的实例)来载入的,因此Class类别的实例中都会有栏位记录他的ClassLoader的实例,如果该栏位为null,
则表示该类别是由bootstrap loader载入的(也称root laoder),bootstrap loader不是java所写成,所以没有实例.
-------------------------------------------------------
自己建立类别加载器来加载类别
利用Java 本身提供的java.net.URLClassLoader
如实例化一个URLClassLoader. URLClassLoader ucl = new URLClassLoader(new URL[]{new URL("file:/e:/bin/")}),URLClassLoader优先找当前目录,再在url中找.class加载.URL中别忘在最后加"/"表示目录
import java.net.*;
public class Office
{
public static void main(String[] args) throws Exception
{
if(args.length!=1)
{
return ;
}else {
URL u=new URL("http://share/");
URLClassLoader ucl=new URLClassLoader(new URL[]{u});
Class c=ucl.loadClass(args[0]);
Assembly asm=(Assembly)c.newInstance();
asm.start();
}
}
}
------------------------------------------------
import java.net.*;
public class Office
{
public static void main(String[] args) throws Exception
{
URL u = new URL("Http://share/") ;
URLClassLoader ucl = new URLClassLoader(new URL[]{ u }) ;
Class c = ucl.loadClass(args[0]) ;
Assembly asm = (Assembly) c.newInstance() ;
asm.start() ;
URL u1 = new URL("Http://share/") ;
URLClassLoader ucl1 = new URLClassLoader(new URL[]{ u1 }) ;
Class c1 = ucl1.loadClass(args[0]) ;
Assembly asm1 = (Assembly) c1.newInstance() ;
asm1.start() ;
System.out.println(Office.class.getClassLoader()) ;
System.out.println(u.getClass().getClassLoader()) ;
System.out.println(ucl.getClass().getClassLoader()) ;
System.out.println(c.getClassLoader()) ;
System.out.println(asm.getClass().getClassLoader()) ;
System.out.println(u1.getClass().getClassLoader()) ;
System.out.println(ucl1.getClass().getClassLoader()) ;
System.out.println(c1.getClassLoader()) ;
System.out.println(asm1.getClass().getClassLoader()) ;
}
}
C:\>java Office Word
Word Static Initialization
Word start
Word start
sun.misc.Launcher$AppClassLoader@82ba41
null
null
sun.misc.Launcher$AppClassLoader@82ba41
sun.misc.Launcher$AppClassLoader@82ba41
null
null
sun.misc.Launcher$AppClassLoader@82ba41
sun.misc.Launcher$AppClassLoader@82ba41
Office.class 由AppClassLoader( 又称做System Loader,系统加载器)所加载,URL.class 与URLClassLoader.class 由Bootstrap Loader 所加载
(注意:输出null 并非代表不是由类别加载器所载入。
在Java 之中,所有的类别都必须由类别加载器加载才行,只不过Bootstrap Loader 并非由Java所撰写而成,
而是由C++ 制作而成,因此以Java 的观点来看,逻辑上并没有Bootstrap Loader 的类别实例) 。而Word.class 分别由两个不同的URLClassLoader 实例加载。
至于Assembly.class , 本身应该是由
AppClassLoader 加载,但是由于多型(Polymorphism) 的关系,所指向的类别实例(Word.class) 由特定的加载器所加载,
导致打印在屏幕上的内容是其所参考的类别实例之类别加载器。Interface 这种型态本身无法直接使用new 来实例化,所以在执行getClassLoader() 的时候,
调用的一定是所参考的类别实例的
getClassLoader() ,要知道Interface 本身由哪个类别加载器加载,您必须使用底下程序代码:
Assembly.class.getClassLoader()
-----------------------------------------------
-----------------------------------------------
一切都是由Bootstrap Loader 开始 : 类别加载器
-----------------------------------------------
-----------------------------------------------
当我们在命令行输入java xxx.class 的时候,java.exe 根据我们之前所提过的逻辑找到了JRE(Java Runtime Environment) ,
接着找到位在JRE 之中的jvm.dll( 真正的Java 虚拟机),最后加载这个动态联结函式库,启动Java 虚拟机。
虚拟机一启动,会先做一些初始化的动作,比方说抓取系统参数等。一旦初始化动作完成之后,就会产生第一个类别加载器,
即所谓的Bootstrap Loader,Bootstrap Loader 是由C++ 所撰写而成(所以前面我们说,以Java的观点来看,逻辑上并不存在Bootstrap Loader 的类别实例,
所以在Java 程序代码里试图印出其内容的时候,我们会看到的输出为null),这个Bootstrap Loader所做的初始工作中,
除了也做一些基本的初始化动作之外,最重要的就是加载定义在sun.misc 命名空间底下的Launcher.java 之中的ExtClassLoader
( 因为是inner class ,所以编译之后会变成Launcher$ExtClassLoader.class) ,
并设定其Parent 为null,代表其父加载器为Bootstrap Loader 。然后Bootstrap Loader ,
再要求加载定义于sun.misc 命名空间底下的Launcher.java 之中的AppClassLoader( 因为是inner class,所以编译之后会变成
Launcher$AppClassLoader.class) ,并设定其Parent 为之前产生的ExtClassLoader 实例。
这里要请大家注意的是,Launcher$ExtClassLoader.class与Launcher$AppClassLoader.class 都是由Bootstrap Loader 所加载,
所以Parent 和由哪个类别加载器加载没有关系
public class test{
public static void main(String args[]) {
ClassLoader cl = test.class.getClassLoader() ;
System.out.println(cl) ;
ClassLoader cl1 = cl.getParent() ;
System.out.println(cl1) ;
ClassLoader cl2 =
cl1.getParent() ;
System.out.println(cl2) ;
}
}
C:\>java test
sun.misc.Launcher$AppClassLoader@82ba41
sun.misc.Launcher$ExtClassLoader@923e30
null
AppClassLoader 和ExtClassLoader 都是URLClassLoader 的子类别。由于它们都是URLClassLoader 的子类别,所以它们也应该有URL 作为搜寻类别档的参考,
由原始码中我们可以得知,AppClassLoader 所参考的URL 是从系统参数java.class.path 取出的字符串所决定,而java.class.path 则是由我们在执行java.exe 时,
利用 –cp 或-classpath 或CLASSPATH 环境变量所决定。
在预设情况下,AppClassLoader的搜寻路径为”.”( 目前所在目录),如果使用-classpath 选项(与-cp 等效),就可以改变AppClassLoader 的搜寻路径,
如果没有指定-classpath 选项,就会搜寻环境变量CLASSPATH 。如果同时有CLASSPATH 的环境设定与-classpath 选项,则以-classpath 选项的内容为主,
CLASSPATH 的环境设定与-classpath 选项两者的内容不会有加成的效果。至于ExtClassLoader 也有相同的情形,不过其搜寻路径是参考系统参数java.ext.dirs
Bootstrap Loader ,我们可以经由查询由系统参数
sun.boot.class.path 得知Bootstrap Loader 用来搜寻类别的路径
java -Dsun.boot.class.path=
请回头看到java.class.path 与sun.boot.class.path,也就是说,AppClassLoader 与Bootstrap Loader 会搜寻它们所指定的位置(或JAR 文件),如果找不到就找不到了,
AppClassLoader 与Bootstrap Loader 不会递归式地搜寻这些位置下的其他路径或其他没有被指定的JAR 文件。反观ExtClassLoader,所参考的系统参数是java.ext.dirs,
意思是说,他会搜寻底下的所有JAR 文件以及classes 目录,作为其搜寻路径(所以您会发现上面我们在测试的时候,如果加入-Dsun.boot.class.path=c:\windows 选项时,
程序的起始速度会慢了些,这是因为c:\windows 目录下的文件很多,必须花额外的时间来列举JAR 文件)。
在命令行下参数时,使用–classpath / -cp / 环境变量CLASSPATH 来更改AppClassLoader 的搜寻路径,或者用 –Djava.ext.dirs 来改变ExtClassLoader的搜寻目录,
两者都是有意义的。可是用–Dsun.boot.class.path 来改变Bootstrap Loader 的搜寻路径是无效。这是因为AppClassLoader
与ExtClassLoader 都是各自参考这两个系统参数的内容而建立,当您在命令行下变更这两个系统参数之后,AppClassLoader 与ExtClassLoader
在建立实例的时候会参考这两个系统参数,因而改