public class Test {
/**
* @param args
* @throws java.text.ParseException
*/
public static void main(String[] args) throws java.text.ParseException {
// Date dateNow = new Date();
//
// String dateNowStr = dateFormat.format(dateNow);
// System.out.println(dateNowStr);
final String dayNames[] = { "星期日", "星期一", "星期二", "星期三", "星期四", "星期五",
"星期六" };
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日");
Calendar calendar = Calendar.getInstance();
Date date = new Date();
String datarq = dateFormat.format(date); //日期
calendar.setTime(date);
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK) - 1;
if (dayOfWeek < 0)
dayOfWeek = 0;
System.out.println(datarq + " " + dayNames[dayOfWeek]);
}
}
開始–運行–cmd 進入命令提示符 輸入netstat -ano 即可看到所有連接的PID 之后在任務管理器中找到這個PID所對應的程序如果任務管理器中沒有PID這一項,可以在任務管理器中選”查看”-”選擇列”
經常,我們在啟動應用的時候發現系統需要的端口被別的程序占用,如何知道誰占有了我們需要的端口,很多人都比較頭疼,下面就介紹一種非常簡單的方法,希望對大家有用。
我們需要確定誰占用了我們的80端口
在windows命令行窗口下執行:
C:\>netstat -aon|findstr “80″
TCP 0.0.0.0:19780 0.0.0.0:0 LISTENING 2044
TCP 10.72.224.5:2558 96.17.109.8:80 ESTABLISHED 2044
TCP 10.72.224.5:2757 60.210.176.209:80 TIME_WAIT 0
TCP 10.72.224.5:3956 125.46.1.234:80 ESTABLISHED 2044
TCP 10.72.224.5:3959 125.46.1.234:80 ESTABLISHED 2044
UDP 0.0.0.0:2280 *:* 3700
看到了嗎,端口被進程號為2044的進程占用,繼續執行下面命令:
C:\>tasklist|findstr “2044″
avp.exe 2044 Console 0 37,680 K
很清楚吧,avp 卡巴監聽80端口,調整卡巴網絡監聽設置。
當然,如果你的80端口被其他程序占用,kill掉或者調整端口。
package l6;
/**
* 實現棧的數據結構
*/
import java.util.LinkedList;
public class MyStack {
LinkedList mylist = new LinkedList();
public void push(Object o) {
mylist.addFirst(o);
}
public Object pop() {
return mylist.removeFirst();
}
public Object peek() {
return mylist.getFirst();
}
public boolean empty() {
return mylist.isEmpty();
}
public static void main(String[] args) {
MyStack ms = new MyStack();
ms.push("1");
ms.push("2");
ms.push("3");
ms.push("4");
System.out.println(ms.pop());
System.out.println(ms.pop());
System.out.println(ms.peek());
System.out.println(ms.empty());
}
}
package l6;
import java.util.LinkedList;
/**
* 實現隊列的數據結構
*
*/
public class MyQueue {
private LinkedList ll = new LinkedList();
public void put(Object o) {
ll.addLast(o);
}
public Object get() {
return ll.removeFirst();
}
public Object peek() {
return ll.getFirst();
}
public boolean empty() {
return ll.isEmpty();
}
/**
* @param args
*/
public static void main(String[] args) {
MyQueue mq = new MyQueue();
mq.put("1");
mq.put("2");
mq.put("3");
mq.put("4");
mq.put("5");
mq.put("6");
System.out.println(mq.get());
System.out.println(mq.get());
System.out.println(mq.get());
System.out.println(mq.get());
System.out.println(mq.peek());
System.out.println(mq.empty());
}
}
//學生類
package l6;
import java.util.Comparator;
public class Student implements Comparable {
public int num;
public String name;
public Student(int num, String name) {
this.num = num;
this.name = name;
}
public int compareTo(Object o) {
Student s = (Student) o;
if (num > s.num) {
return 1;
} else if (num == s.num) {
return 0;
} else {
return -1;
}
}
public String toString() {
return "num:" + num + " name:" + name;
}
public int compare(Object o1, Object o2) {
// TODO Auto-generated method stub
return 0;
}
}
//學生比較器
package l6;
import java.util.Comparator;
public class StudentComparator implements Comparator {
public int compare(Object o1, Object o2) {
Student s1 = (Student) o1;
Student s2 = (Student) o2;
if (s1.num > s2.num) {
return 1;
} else if (s1.num < s2.num) {
return -1;
} else {
return s1.name.compareTo(s2.name);
}
}
}
//測試類
package l6;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
public class ArrayListTest {
/**
* @param args
*/
public static void main(String[] args) {
ArrayList<Student> slist = new ArrayList<Student>();
Student s1 = new Student(1, "z1");
slist.add(s1);
Student s3 = new Student(2, "z3");
slist.add(s3);
Student s2 = new Student(2, "z2");
slist.add(s2);
Student s5 = new Student(2, "z5");
slist.add(s5);
Student s4 = new Student(4, "z4");
slist.add(s4);
// Collections.sort(slist); //未使用比較器
Collections.sort(slist, new StudentComparator());//未使用比較器
PaintE(slist);
}
public static void PaintE(Collection c) {
Iterator it = c.iterator();
while(it.hasNext()){
System.out.println(it.next().toString());
}
}
}
//All the divs on the page:
$$('div');
//All the divs and paragraphs
//note: this returns an array with all the divs first,then all the paragraphs:
$$('div', 'p');
//All the divs with the css class 'myClass':
$$('div.myClass')
/All the paragraphs that are inside divs:
$$('div p');
//All the bold tags in paragraphs with Class 'foo' in divs with class 'myClass':
$$('div.myClass p.foo b');
<1>hasClass 應用
Examples:
HTML
<div id="myElement" class="testClass"></div>
JavaScript
$('myElement').hasClass('testClass'); // returns true
<2>addClass 應用
Examples:
HTML
<div id="myElement" class="testClass"></div>
JavaScript
$('myElement').addClass('newClass');
<3>removeClass 應用
Examples:
HTML
<div id="myElement" class="testClass newClass"></div>
JavaScript
$('myElement').removeClass('newClass');
Resulting HTML
<div id="myElement" class="testClass"></div>
<4>toggleClass 應用
Adds or removes the passed in class name to the Element, depending on whether or not it's already present.
Examples:
HTML
<div id="myElement" class="myClass"></div>
JavaScript
$('myElement').toggleClass('myClass');
Resulting HTML
<div id="myElement" class=""></div>
JavaScript
$('myElement').toggleClass('myClass');
Resulting HTML
<div id="myElement" class="myClass"></div>
打印集合類的對象,將調用所有元素的toString()方法,如果是自己寫的類,則要實現toString()方法;
Java關鍵字final、static使用總結
Java關鍵字final有“這是無法改變的”或者“終態的”含義,它可以修飾非抽象類、非抽象類成員方法和變量。你可能出于兩種理解而需要阻止改變:設計或效率。
final類不能被繼承,沒有子類,final類中的方法默認是final的。
final方法不能被子類的方法覆蓋,但可以被繼承。
final成員變量表示常量,只能被賦值一次,賦值后值不再改變。
final不能用于修飾構造方法。
注意:父類的private成員方法是不能被子類方法覆蓋的,因此private類型的方法默認是final類型的。
1、final類
final類不能被繼承,因此final類的成員方法沒有機會被覆蓋,默認都是final的。在設計類時候,如果這個類不需要有子類,類的實現細節不允許改變,并且確信這個類不會載被擴展,那么就設計為final類。
2、final方法
如果一個類不允許其子類覆蓋某個方法,則可以把這個方法聲明為final方法。
使用final方法的原因有二:
第一、把方法鎖定,防止任何繼承類修改它的意義和實現。
第二、高效。編譯器在遇到調用final方法時候會轉入內嵌機制,大大提高執行效率。
3、final變量(常量)
用final修飾的成員變量表示常量,值一旦給定就無法改變!
final修飾的變量有三種:靜態變量、實例變量和局部變量,分別表示三種類型的常量。
4、final參數
當函數參數為final類型時,你可以讀取使用該參數,但是無法改變該參數的值。
二、static
static表示“全局”或者“靜態”的意思,用來修飾成員變量和成員方法,也可以形成靜態static代碼塊,但是Java語言中沒有全局變量的概念。
被static修飾的成員變量和成員方法獨立于該類的任何對象。也就是說,它不依賴類特定的實例,被類的所有實例共享。只要這個類被加載,Java虛擬機就能根據類名在運行時數據區的方法區內定找到他們。因此,static對象可以在它的任何對象創建之前訪問,無需引用任何對象。
用public修飾的static成員變量和成員方法本質是全局變量和全局方法,當聲明它類的對象市,不生成static變量的副本,而是類的所有實例共享同一個static變量。
static變量前可以有private修飾,表示這個變量可以在類的靜態代碼塊中,或者類的其他靜態成員方法中使用(當然也可以在非靜態成員方法中使用--廢話),但是不能在其他類中通過類名來直接引用,這一點很重要。實際上你需要搞明白,private是訪問權限限定,static表示不要實例化就可以使用,這樣就容易理解多了。static前面加上其它訪問權限關鍵字的效果也以此類推。
static修飾的成員變量和成員方法習慣上稱為靜態變量和靜態方法,可以直接通過類名來訪問,訪問語法為:
類名.靜態方法名(參數列表...)
類名.靜態變量名
用static修飾的代碼塊表示靜態代碼塊,當Java虛擬機(JVM)加載類時,就會執行該代碼塊(用處非常大,呵呵)。
1、static變量
按照是否靜態的對類成員變量進行分類可分兩種:一種是被static修飾的變量,叫靜態變量或類變量;另一種是沒有被static修飾的變量,叫實例變量。兩者的區別是:
對于靜態變量在內存中只有一個拷貝(節省內存),JVM只為靜態分配一次內存,在加載類的過程中完成靜態變量的內存分配,可用類名直接訪問(方便),當然也可以通過對象來訪問(但是這是不推薦的)。
對于實例變量,沒創建一個實例,就會為實例變量分配一次內存,實例變量可以在內存中有多個拷貝,互不影響(靈活)。
2、靜態方法
靜態方法可以直接通過類名調用,任何的實例也都可以調用,因此靜態方法中不能用this和super關鍵字,不能直接訪問所屬類的實例變量和實例方法(就是不帶static的成員變量和成員成員方法),只能訪問所屬類的靜態成員變量和成員方法。因為實例成員與特定的對象關聯!這個需要去理解,想明白其中的道理,不是記憶!!!
因為static方法獨立于任何實例,因此static方法必須被實現,而不能是抽象的abstract。
3、static代碼塊
static代碼塊也叫靜態代碼塊,是在類中獨立于類成員的static語句塊,可以有多個,位置可以隨便放,它不在任何的方法體內,JVM加載類時會執行這些靜態的代碼塊,如果static代碼塊有多個,JVM將按照它們在類中出現的先后順序依次執行它們,每個代碼塊只會被執行一次。
利用靜態代碼塊可以對一些static變量進行賦值,最后再看一眼這些例子,都一個static的main方法,這樣JVM在運行main方法的時候可以直接調用而不用創建實例。
4、static和final一塊用表示什么
static final用來修飾成員變量和成員方法,可簡單理解為“全局常量”!
對于變量,表示一旦給值就不可修改,并且通過類名可以訪問。
對于方法,表示不可覆蓋,并且可以通過類名直接訪問。
為了獲取對象的一份拷貝,我們可以利用Object類的clone()方法。
在派生類中覆蓋基類的clone()方法,并聲明為public。
在派生類的clone()方法中,調用super.clone()。
在派生類中實現Cloneable接口。
為什么我們在派生類中覆蓋Object的clone()方法時,一定要調用super.clone()呢?在運行時刻,Object中的clone()識別出你要復制的是哪一個對象,然后為此對象分配空間,并進行對象的復制,將原始對象的內容一一復制到新對象的存儲空間中。
Student s1 = new Student("gaoer", 14);
System.out.println("s1name=" + s1.getName());
Student s2 = s1; // 未使用clone,他倆使用一個地址,
s2.setAge(12);
s2.setName("zhangsan");
System.out.println("s1name=" + s1.getName());
Student s3 = (Student) s1.clone();
s3.setAge(12);
s3.setName("lisi");
System.out.println("s1name=" + s1.getName());
package l4;
/**
*
* @author Administrator
* 當沒有引用類型的變量時,為淺層次的拷貝
*
*/
public class Student implements Cloneable {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
protected Object clone() throws CloneNotSupportedException {
Object o = null;
o = super.clone();
return o;
}
}
package l4;
/**
*
* @author Administrator
* 當沒有引用類型的變量時,為淺層次的克隆
* 當有引用類型的變量時,為深層次的克隆
*/
public class Student implements Cloneable {
private String name;
private int age;
Point pt;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(String name, int age,Point pt) {
this.name = name;
this.age = age;
this.pt = pt;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
protected Object clone() throws CloneNotSupportedException {
Student o = null;
o = (Student)super.clone();
o.pt = (Point)pt.clone();
return o;
}
}
package l4;
public class Point implements Cloneable {
public int x;
public int y;
@Override
public String toString() {
return "x=" + x + "y=" + y;
}
protected Object clone() throws CloneNotSupportedException {
Object o = null;
o = super.clone();
return o;
}
}
public static void change(int x, int y) {
x = x + y;
y = x - y;
x = x - y;
}
public static void change(int[] num) {
num[0] = num[0] + num[1];
num[1] = num[0] - num[1];
num[0] = num[0] - num[1];
}
public static void change(Point pt) {
pt.x = pt.x + pt.y;
pt.y = pt.x - pt.y;
pt.x = pt.x - pt.y;
}
Eclipse快捷鍵大全
Ctrl+1 快速修復(最經典的快捷鍵,就不用多說了)
Ctrl+D: 刪除當前行
Ctrl+Alt+↓ 復制當前行到下一行(復制增加)
Ctrl+Alt+↑ 復制當前行到上一行(復制增加)
Alt+↓ 當前行和下面一行交互位置(特別實用,可以省去先剪切,再粘貼了)
Alt+↑ 當前行和上面一行交互位置(同上)
Alt+← 前一個編輯的頁面
Alt+→ 下一個編輯的頁面(當然是針對上面那條來說了)
Alt+Enter 顯示當前選擇資源(工程,or 文件 or文件)的屬性
Shift+Enter 在當前行的下一行插入空行(這時鼠標可以在當前行的任一位置,不一定是最后)
Shift+Ctrl+Enter 在當前行插入空行(原理同上條)
Ctrl+Q 定位到最后編輯的地方
Ctrl+L 定位在某行 (對于程序超過100的人就有福音了)
Ctrl+M 最大化當前的Edit或View (再按則反之)
Ctrl+/ 注釋當前行,再按則取消注釋
Ctrl+O 快速顯示 OutLine
Ctrl+T 快速顯示當前類的繼承結構
Ctrl+W 關閉當前Editer
Ctrl+K 參照選中的Word快速定位到下一個
Ctrl+E 快速顯示當前Editer的下拉列表(如果當前頁面沒有顯示的用黑體表示)
Ctrl+/(小鍵盤) 折疊當前類中的所有代碼
Ctrl+×(小鍵盤) 展開當前類中的所有代碼
Ctrl+Space 代碼助手完成一些代碼的插入(但一般和輸入法有沖突,可以修改輸入法的熱鍵,也可以暫用Alt+/來代替)
Ctrl+Shift+E 顯示管理當前打開的所有的View的管理器(可以選擇關閉,激活等操作)
Ctrl+J 正向增量查找(按下Ctrl+J后,你所輸入的每個字母編輯器都提供快速匹配定位到某個單詞,如果沒有,則在stutes line中顯示沒有找到了,查一個單詞時,特別實用,這個功能Idea兩年前就有了)
Ctrl+Shift+J 反向增量查找(和上條相同,只不過是從后往前查)
Ctrl+Shift+F4 關閉所有打開的Editer
Ctrl+Shift+X 把當前選中的文本全部變味小寫
Ctrl+Shift+Y 把當前選中的文本全部變為小寫
Ctrl+Shift+F 格式化當前代碼
Ctrl+Shift+P 定位到對于的匹配符(譬如{}) (從前面定位后面時,光標要在匹配符里面,后面到前面,則反之)
下面的快捷鍵是重構里面常用的,本人就自己喜歡且常用的整理一下(注:一般重構的快捷鍵都是Alt+Shift開頭的了)
Alt+Shift+R 重命名 (是我自己最愛用的一個了,尤其是變量和類的Rename,比手工方法能節省很多勞動力)
Alt+Shift+M 抽取方法 (這是重構里面最常用的方法之一了,尤其是對一大堆泥團代碼有用)
Alt+Shift+C 修改函數結構(比較實用,有N個函數調用了這個方法,修改一次搞定)
Alt+Shift+L 抽取本地變量( 可以直接把一些魔法數字和字符串抽取成一個變量,尤其是多處調用的時候)
Alt+Shift+F 把Class中的local變量變為field變量 (比較實用的功能)
Alt+Shift+I 合并變量(可能這樣說有點不妥Inline)
Alt+Shift+V 移動函數和變量(不怎么常用)
Alt+Shift+Z 重構的后悔藥(Undo)
編輯
作用域 功能 快捷鍵
全局 查找并替換 Ctrl+F
文本編輯器 查找上一個 Ctrl+Shift+K
文本編輯器 查找下一個 Ctrl+K
全局 撤銷 Ctrl+Z
全局 復制 Ctrl+C
全局 恢復上一個選擇 Alt+Shift+↓
全局 剪切 Ctrl+X
全局 快速修正 Ctrl1+1
全局 內容輔助 Alt+/
全局 全部選中 Ctrl+A
全局 刪除 Delete
全局 上下文信息 Alt+?
Alt+Shift+?
Ctrl+Shift+Space
Java編輯器 顯示工具提示描述 F2
Java編輯器 選擇封裝元素 Alt+Shift+↑
Java編輯器 選擇上一個元素 Alt+Shift+←
Java編輯器 選擇下一個元素 Alt+Shift+→
文本編輯器 增量查找 Ctrl+J
文本編輯器 增量逆向查找 Ctrl+Shift+J
全局 粘貼 Ctrl+V
全局 重做 Ctrl+Y
查看
作用域 功能 快捷鍵
全局 放大 Ctrl+=
全局 縮小 Ctrl+-
窗口
作用域 功能 快捷鍵
全局 激活編輯器 F12
全局 切換編輯器 Ctrl+Shift+W
全局 上一個編輯器 Ctrl+Shift+F6
全局 上一個視圖 Ctrl+Shift+F7
全局 上一個透視圖 Ctrl+Shift+F8
全局 下一個編輯器 Ctrl+F6
全局 下一個視圖 Ctrl+F7
全局 下一個透視圖 Ctrl+F8
文本編輯器 顯示標尺上下文菜單 Ctrl+W
全局 顯示視圖菜單 Ctrl+F10
全局 顯示系統菜單 Alt+-
導航
作用域 功能 快捷鍵
Java編輯器 打開結構 Ctrl+F3
全局 打開類型 Ctrl+Shift+T
全局 打開類型層次結構 F4
全局 打開聲明 F3
全局 打開外部javadoc Shift+F2
全局 打開資源 Ctrl+Shift+R
全局 后退歷史記錄 Alt+←
全局 前進歷史記錄 Alt+→
全局 上一個 Ctrl+,
全局 下一個 Ctrl+.
Java編輯器 顯示大綱 Ctrl+O
全局 在層次結構中打開類型 Ctrl+Shift+H
全局 轉至匹配的括號 Ctrl+Shift+P
全局 轉至上一個編輯位置 Ctrl+Q
Java編輯器 轉至上一個成員 Ctrl+Shift+↑
Java編輯器 轉至下一個成員 Ctrl+Shift+↓
文本編輯器 轉至行 Ctrl+L
搜索
作用域 功能 快捷鍵
全局 出現在文件中 Ctrl+Shift+U
全局 打開搜索對話框 Ctrl+H
全局 工作區中的聲明 Ctrl+G
全局 工作區中的引用 Ctrl+Shift+G
文本編輯
作用域 功能 快捷鍵
文本編輯器 改寫切換 Insert
文本編輯器 上滾行 Ctrl+↑
文本編輯器 下滾行 Ctrl+↓
文件
作用域 功能 快捷鍵
全局 保存 Ctrl+X
Ctrl+S
全局 打印 Ctrl+P
全局 關閉 Ctrl+F4
全局 全部保存 Ctrl+Shift+S
全局 全部關閉 Ctrl+Shift+F4
全局 屬性 Alt+Enter
全局 新建 Ctrl+N
項目
作用域 功能 快捷鍵
全局 全部構建 Ctrl+B
源代碼
作用域 功能 快捷鍵
Java編輯器 格式化 Ctrl+Shift+F
Java編輯器 取消注釋 Ctrl+\
Java編輯器 注釋 Ctrl+/
Java編輯器 添加導入 Ctrl+Shift+M
Java編輯器 組織導入 Ctrl+Shift+O
Java編輯器 使用try/catch塊來包圍 未設置,太常用了,所以在這里列出,建議自己設置。
也可以使用Ctrl+1自動修正。
運行
作用域 功能 快捷鍵
全局 單步返回 F7
全局 單步跳過 F6
全局 單步跳入 F5
全局 單步跳入選擇 Ctrl+F5
全局 調試上次啟動 F11
全局 繼續 F8
全局 使用過濾器單步執行 Shift+F5
全局 添加/去除斷點 Ctrl+Shift+B
全局 顯示 Ctrl+D
全局 運行上次啟動 Ctrl+F11
全局 運行至行 Ctrl+R
全局 執行 Ctrl+U
重構
作用域 功能 快捷鍵
全局 撤銷重構 Alt+Shift+Z
全局 抽取方法 Alt+Shift+M
全局 抽取局部變量 Alt+Shift+L
全局 內聯 Alt+Shift+I
全局 移動 Alt+Shift+V
全局 重命名 Alt+Shift+R
全局 重做 Alt+Shift+Y
<數據類型>[] 變量名 = new <數據類型>[數組長度]
int[] a = new int[5]
int a[1]=1;
int a[2]=2;
int a[3]=3;
int a[4]=4;
int a[5]=5;
int[] a = {1,2,3,4,5,6}
抽象類:
用absstract定義的類為抽象類,抽象類可以包含抽象方法也可以包含具體的方法;
沒有方法體的方法為抽象方法;
抽象的類不能創建對象,只能通過繼承使用這個類;
只用覆蓋了父類中的方法后,才能實例化,才能使用;
模板方法,用來定義規則;
接口:
接口就是特殊的類;
接口內沒有具體的方法,只能包含抽象的方法;
接口內默認就是抽象方法,不用加abstract;
接口內的方法默認是公用的,不用加public;
接口不能實例化;
接口可以被繼承,產生子接口,子接口也不能創建對象;
通過implements實現接口;
接口可以出現變量,但都是常量,用final定義,在聲明的時候就賦值,默認是final、static;
final最終的,不允許修改;
父類能適用的地方,子類也能適用;
Linux下可以超過3G,Windows下最大也就1.6G左右(x86)。
一般32位的機器做大只能用到2G內存,4G內存可以跑2個tomcat,通過Apache做cluster.
一些新手朋友對選擇器一知半解,不知道在什么情況下運用什么樣的選擇器,這是一個比較頭疼的問題,針對新手朋友,對CSS選擇器作一些簡單的說明,希望能對大家的學習工作有一定的幫助,更多的CSS知識請參考Webjx.com的其他文章。
準確而簡潔的運用CSS選擇器會達到非常好的效果。我們不必通篇給每一個元素定義類(class)或ID,通過合適的組織,可以用最簡單的方法實現同樣的效果。在實際工作中,最常用的選擇器有以下五類:
一、標簽選擇器:
顧名思議,標簽選擇器是直接將HTML標簽作為選擇器,可以是p、h1、dl、strong等HTML標簽。如:
p { font:12px;}
em { color:blue;}
dl { float:left; margin-top:10px;}
二、id選擇器:
我們通常給頁面元素定義id。例如定義一個層 <div id="menubar"></div> 然后在樣式表里這樣定義:
#menubar {
margin:0 auto;
background:#ccc;
color:#c00;
}
其中"menubar"是你自己定義的id名稱。注意在前面加"#"號。
id選擇器也同樣支持后代選擇器,例如: #menubar p { text-align:center; line-height:20px;; } 這個方法主要用來定義層和那些比較復雜,有多個“唯一后代”的元素。
三、類(class)選擇器:
在CSS里用一個點開頭表示類別選擇器定義,例如:
.da1 {
color:#f60;
font-size:14px ;
}
在頁面中,用class="類別名"的方法調用: <span class="da1">14px大小的字體</span> 這個方法比較簡單靈活,可以隨時根據頁面需要新建和刪除。但需要避免多class綜合癥。
四、群組選擇器:
當幾個元素樣式屬性一樣時,可以共同調用一個聲明,元素之間用逗號分隔。如:
p, td, li {
line-height:20px;
color:#c00;
}
#main p, #sider span {
color:#000;
line-height:26px;
}
.www_52css_com,#main p span {
color:#f60;
}
.text1 h1,#sider h3,.art_title h2 {
font-weight:100;
}
使用組群選擇器,將會大大的減化CSS代碼,將具有多個相同屬性的元素,合并群組進行選擇,定義同樣的CSS屬性,這大大的提高了編碼效率與CSS文件體積。
五、后代選擇器:
后代選擇器也叫派生選擇器。可以使用后代選擇器給一個元素里的子元素定義樣式,例如這樣:
li strong {
font-style:italic;
font-weight:800;
color:#f00;
}
#main p {
color:#000;
line-height:26px;
}
#sider .con span {
color:#000;
line-height:26px;
}
.www_52css_com p span {
color:#f60;
}
#sider ul li.subnav1 {
margin-top:5px;
} 第一段,就是給li下面的子元素strong定義一個斜體加粗而且套紅的樣式。其他以此類推。
后代選擇器的使用是非常有益的,如果父元素內包括的HTML元素具有唯一性,則不必給內部元素再指定class或id,直接應用此選擇器即可,例如下面的h3與ul就不必指定class或id。
<div id="sider">
<h3></h3>
<ul>
<li>...</li>
<li>...</li>
<li>...</li>
</ul>
</div>
在這里CSS就可以及樣寫:
#sider h3 {...}
#sider ul {...}
#sider ul li {...}
結合使用上面的四種CSS選擇器,基本滿足了CSS布局的需要,主要在于靈活的使用,特別是后代選擇器的使用能大大的簡化HTML文檔,使HTML做到結構化明確,最小的代碼實現同樣的效果。
CSS中用四個偽類來定義鏈接的樣式,分別是:a:link、a:visited、a:hover和a : active。
例如:
a:link{font-weight : bold ;text-decoration : none ;color : #c00 ;}
a:visited {font-weight : bold ;text-decoration : none ;color : #c30 ;}
a:hover {font-weight : bold ;text-decoration : underline ;color : #f60 ;}
a:active {font-weight : bold ;text-decoration : none ;color : #F90 ;}
以上語句分別定義了"鏈接、已訪問過的鏈接、鼠標停在上方時、點下鼠標時"的樣式。注意,必須按以上順序寫,否則顯示可能和你預期的不一致。記住它們的順序是“LVHA”。
Transactions(用戶事務分析)
用戶事務分析是站在用戶角度進行的基礎性能分析。
1、Transation Sunmmary(事務綜述)
對事務進行綜合分析是性能分析的第一步,通過分析測試時間內用戶事務的成功與失敗情況,可以直接判斷出系統是否運行正常。
2、Average Transaciton Response Time(事務平均響應時間)
“事務平均響應時間”顯示的是測試場景運行期間的每一秒內事務執行所用的平均時間,通過它可以分析測試場景運行期間應用系統的性能走向。
例:隨著測試時間的變化,系統處理事務的速度開始逐漸變慢,這說明應用系統隨著投產時間的變化,整體性能將會有下降的趨勢。
3、Transactions per Second(每秒通過事務數/TPS)
“每秒通過事務數/TPS”顯示在場景運行的每一秒鐘,每個事務通過、失敗以及停止的數量,使考查系統性能的一個重要參數。通過它可以確定系統在任何給定時刻的時間事務負載。分析TPS主要是看曲線的性能走向。
將它與平均事務響應時間進行對比,可以分析事務數目對執行時間的影響。
例:當壓力加大時,點擊率/TPS曲線如果變化緩慢或者有平坦的趨勢,很有可能是服務器開始出現瓶頸。
4、Total Transactions per Second(每秒通過事務總數)
“每秒通過事務總數”顯示在場景運行時,在每一秒內通過的事務總數、失敗的事務總署以及停止的事務總數。
5、Transaction Performance Sunmmary(事務性能摘要)
“事務性能摘要”顯示方案中所有事務的最小、最大和平均執行時間,可以直接判斷響應時間是否符合用戶的要求。
重點關注事務的平均和最大執行時間,如果其范圍不在用戶可以接受的時間范圍內,需要進行原因分析。
6、Transaction Response Time Under Load(事務響應時間與負載)
“事務響應時間與負載”是“正在運行的虛擬用戶”圖和“平均響應事務時間”圖的組合,通過它可以看出在任一時間點事務響應時間與用戶數目的關系,從而掌握系統在用戶并發方面的性能數據,為擴展用戶系統提供參考。此圖可以查看虛擬用戶負載對執行時間的總體影響,對分析具有漸變負載的測試場景比較有用。
7、Transaction Response Time(Percentile)(事務響應時間(百分比))
“事務響應時間(百分比)”是根據測試結果進行分析而得到的綜合分析圖,也就是工具通過一些統計分析方法間接得到的圖表。通過它可以分析在給定事務響應時間范圍內能執行的事務百分比。
8、Transaction Response Time(Distribution)(事務響應時間(分布))
“事務響應時間(分布)”顯示在場景運行過程中,事務執行所用時間的分布,通過它可以了解測試過程中不同響應時間的事務數量。如果系統預先定義了相關事務可以接受的最小和最大事務響應時間,則可以使用此圖確定服務器性能是否在可以接受的范圍內。
Web Resources(Web資源分析)
Web資源分析是從服務器入手對Web服務器的性能分析。
1、Hits per Second(每秒點擊次數)
“每秒點擊次數”,即使運行場景過程中虛擬用戶每秒向Web服務器提交的HTTP請求數。
通過它可以評估虛擬用戶產生的負載量,如將其和“平均事務響應時間”圖比較,可以查看點擊次數對事務性能產生的影響。通過對查看“每秒點擊次數”,可以判斷系統是否穩定。系統點擊率下降通常表明服務器的響應速度在變慢,需進一步分析,發現系統瓶頸所在。
2、Throughput(吞吐率)
“吞吐率”顯示的是場景運行過程中服務器的每秒的吞吐量。其度量單位是字節,表示虛擬用在任何給定的每一秒從服務器獲得的數據量。
可以依據服務器的吞吐量來評估虛擬用戶產生的負載量,以及看出服務器在流量方面的處理能力以及是否存在瓶頸。
“吞吐率”圖和“點擊率”圖的區別:
“吞吐率”圖,是每秒服務器處理的HTTP申請數。
“點擊率”圖,是客戶端每秒從服務器獲得的總數據量。
3、HTTP Status Code Summary(HTTP狀態代碼概要)
“HTTP狀態代碼概要”顯示場景或會話步驟過程中從Web服務器返回的HTTP狀態代碼數,該圖按照代碼分組。HTTP狀態代碼表示HTTP請求的狀態。
4、HTTP Responses per Second(每秒HTTP響應數)
“每秒HTTP響應數”是顯示運行場景過程中每秒從Web服務器返回的不同HTTP狀態代碼的數量,還能返回其它各類狀態碼的信息,通過分析狀態碼,可以判斷服務器在壓力下的運行情況,也可以通過對圖中顯示的結果進行分組,進而定位生成錯誤的代碼腳本。
5、Pages Downloader per Second(每秒下載頁面數)
“每秒下載頁面數”顯示場景或會話步驟運行的每一秒內從服務器下載的網頁數。使用此圖可依據下載的頁數來計算Vuser生成的負載量。
和吞吐量圖一樣,每秒下載頁面數圖標是Vuser在給定的任一秒內從服務器接收到的數據量。但是吞吐量考慮的各個資源極其大小(例,每個GIF文件的大小、每個網頁的大小)。而每秒下載頁面數只考慮頁面數。
注:要查看每秒下載頁數圖,必須在R-T-S那里設置“每秒頁面數(僅HTML模式)”。
6、Retries per Second(每秒重試次數)
“每秒重試次數”顯示場景或會話步驟運行的每一秒內服務器嘗試的連接次數。
在下列情況將重試服務器連接:
A、初始連接未經授權
B、要求代理服務器身份驗證
C、服務器關閉了初始連接
D、初始連接無法連接到服務器
E、服務器最初無法解析負載生成器的IP地址
7、Retries Summary(重試次數概要)
“重試次數概要”顯示場景或會話步驟運行過程中服務器嘗試的連接次數,它按照重試原因分組。將此圖與每秒重試次數圖一起使用可以確定場景或會話步驟運行過程中服務器在哪個時間點進行了重試。
8、Connections(連接數)
“連接數”顯示場景或會話步驟運行過程中每個時間點打開的TCP/IP連接數。
借助此圖,可以知道何時需要添加其他連接。
例:當連接數到達穩定狀態而事務響應時間迅速增大時,添加連接可以使性能得到極大提高(事務響應時間將降低)。
9、Connections Per Second(每秒連接數)
“每秒連接數”顯示方案在運行過程中每秒建立的TCP/IP連接數。
理想情況下,很多HTTP請求都應該使用同一連接,而不是每個請求都新打開一個連接。通過每秒連接數圖可以看出服務器的處理情況,就表明服務器的性能在逐漸下降。
10、SSLs Per Second(每秒SSL連接數)
“每秒SSL連接數”顯示場景或會話步驟運行的每一秒內打開的新的以及重新使用的SSL連接數。當對安全服務器打開TCP/IP連接后,瀏覽器將打開SSL連接。
Web Page Breakdown(網頁元素細分)
“網頁元素細分”主要用來評估頁面內容是否影響事務的響應時間,通過它可以深入地分析網站上那些下載很慢的圖形或中斷的連接等有問題的
元素。
1、Web Page Breakdown(頁面分解總圖)
“頁面分解”顯示某一具體事務在測試過程的響應情況,進而分析相關的事務運行是否正常。
“頁面分解”圖可以按下面四種方式進行進一步細分:
1)、Download Time Breaddown(下載時間細分)
“下載時間細分”圖顯示網頁中不同元素的下載時間,同時還可按照下載過程把時間進行分解,用不同的顏色來顯示DNS解析時間、建立連接時間、第一次緩沖時間等各自所占比例。
2)、Component Breakdown(Over Time)(組件細分(隨時間變化))
“組件細分”圖顯示選定網頁的頁面組件隨時間變化的細分圖。通過該圖可以很容易的看出哪些元素在測試過程中下載時間不穩定。該圖特別適用于需要在客戶端下載控件較多的頁面,通過分析控件的響應時間,很容易就能發現那些控件不穩定或者比較耗時。
3)、Download Time Breakdown(Over Time)(下載時間細分(隨時間變化))
“下載時間細分(隨時間變化)” 圖顯示選定網頁的頁面元素下載時間細分(隨時間變化)情況,它非常清晰地顯示了頁面各個元素在壓力測試過程中的下載情況。
“下載時間細分”圖顯示的是整個測試過程頁面元素響應的時間統計分析結果,“下載時間細分(隨時間變化)”顯示的事場景運行過程中每一秒內頁面元素響應時間的統計結果,兩者分別從宏觀和微觀角度來分析頁面元素的下載時間。
4)、Time to First Buffer Breakdown(Over Time)(第一次緩沖時間細分(隨時間變化))
“第一次緩沖時間細分(隨時間變化)”圖顯示成功收到從Web服務器返回的第一次緩沖之前的這段時間,場景或會話步驟運行的每一秒中每個網頁組件的服務器時間和網絡時間(以秒為單位)。可以使用該圖確定場景或會話步驟運行期間服務器或網絡出現問題的時間。
First Buffer Time:是指客戶端與服務器端建立連接后,從服務器發送第一個數據包開始計時,數據經過網絡傳送到客戶端,到瀏覽器接收到第一個緩沖所用的時間。
2、Page Component Breakdown(頁面組件細分)
“頁面組件細分”圖顯示每個網頁及其組件的平均下載時間(以秒為單位)。可以根據下載組件所用的平均秒數對圖列進行排序,通過它有助于隔離有問題的組件。
3、Page Component Breakdown(Over Time)(頁面組件分解(隨時間變化))
“頁面組件分解(隨時間變化)”圖顯示在方案運行期間的每一秒內每個網頁及其組件的平均響應時間(以秒為單位)。
4、Page Download Time Breakdown(頁面下載時間細分)
“頁面下載時間細分”圖顯示每個頁面組件下載時間的細分,可以根據它確定在網頁下載期間事務響應時間緩慢是由網絡錯誤引起還是由服務器錯誤引起。
“頁面下載時間細分”圖根據DNS解析時間、連接時間、第一次緩沖時間、SSL握手時間、接收時間、FTP驗證時間、客戶端時間和錯誤時間來對每個組件的下載過程進行細分。
5、Page Download Time Breakdown(Over Time)(頁面下載時間細分(隨時間變化))
“頁面下載時間細分(隨時間變化)”圖顯示方案運行期間,每一秒內每個頁面組件下載時間的細分。使用此圖可以確定網絡或服務器在方案執行期間哪一時間點發生了問題。
“頁面組件細分(隨時間變化)”圖和“頁面下載時間細分(隨時間變化)”圖通常結合起來進行分析:首先確定有問題的組件,然后分析它們的下載過程,進而定位原因在哪里。
6、Time to First Buffer Breakdown(第一次緩沖時間細分)
“第一次緩沖時間細分”圖顯示成功收到從Web服務器返回的第一次緩沖之前的這一段時間內的每個頁面組件的相關服務器/網路時間。如果組件的下載時間很長,則可以使用此圖確定產生的問題與服務器有關還是與網絡有關。
網絡時間:定義為第一個HTTP請求那一刻開始,直到確認為止所經過的平均時間。
服務器時間:定義為從收到初始HTTP請求確認開始,直到成功收到來自Web服務器的一次緩沖為止所經過的平均時間。
7、Time to First Buffer Breakdown(Over Time)(第一次緩沖時間細分(隨時間變化))
“第一次緩沖時間細分(隨時間變化)”圖顯示成功收到從Web服務器返回的第一個緩沖之前的這段時間內,場景運行的每一秒中每個網頁組件的服務器時間和網絡時間。可以使用此圖確定場景運行期間服務器或網絡出現問題的時間點。
8、Downloader Component Size(KB)(已下載組件大小)
“已下載組件大小”圖顯示每個已經下載的網頁組建的大小。通過它可以直接看出哪些組件比較大并需要進一步進行優化以提高性能。
你覺得自己是一個Java專家嗎?是否肯定自己已經全面掌握了Java的異常處理機制?在下面這段代碼中,你能夠迅速找出異常處理的六個問題嗎?
1 OutputStreamWriter out = ...
2 java.sql.Connection conn = ...
3 try { // ⑸
4 Statement stat = conn.createStatement();
5 ResultSet rs = stat.executeQuery(
6 "select uid, name from user");
7 while (rs.next())
8 {
9 out.println("ID:" + rs.getString("uid") // ⑹
10 ",姓名:" + rs.getString("name"));
11 }
12 conn.close(); // ⑶
13 out.close();
14 }
15 catch(Exception ex) // ⑵
16 {
17 ex.printStackTrace(); //⑴,⑷
18 }
作為一個Java程序員,你至少應該能夠找出兩個問題。但是,如果你不能找出全部六個問題,請繼續閱讀本文。
本文討論的不是Java異常處理的一般性原則,因為這些原則已經被大多數人熟知。我們要做的是分析各種可稱為“反例”(anti-pattern)的違背優秀編碼規范的常見壞習慣,幫助讀者熟悉這些典型的反面例子,從而能夠在實際工作中敏銳地察覺和避免這些問題。
反例之一:丟棄異常
代碼:15行-18行。
這段代碼捕獲了異常卻不作任何處理,可以算得上Java編程中的殺手。從問題出現的頻繁程度和禍害程度來看,它也許可以和C/C++程序的一個惡名遠播的問題相提并論??不檢查緩沖區是否已滿。如果你看到了這種丟棄(而不是拋出)異常的情況,可以百分之九十九地肯定代碼存在問題(在極少數情況下,這段代碼有存在的理由,但最好加上完整的注釋,以免引起別人誤解)。
這段代碼的錯誤在于,異常(幾乎)總是意味著某些事情不對勁了,或者說至少發生了某些不尋常的事情,我們不應該對程序發出的求救信號保持沉默和無動于衷。調用一下printStackTrace算不上“處理異常”。不錯,調用printStackTrace對調試程序有幫助,但程序調試階段結束之后,printStackTrace就不應再在異常處理模塊中擔負主要責任了。
丟棄異常的情形非常普遍。打開JDK的ThreadDeath類的文檔,可以看到下面這段說明:“特別地,雖然出現ThreadDeath是一種‘正常的情形’,但ThreadDeath類是Error而不是Exception的子類,因為許多應用會捕獲所有的Exception然后丟棄它不再理睬。”這段話的意思是,雖然ThreadDeath代表的是一種普通的問題,但鑒于許多應用會試圖捕獲所有異常然后不予以適當的處理,所以JDK把ThreadDeath定義成了Error的子類,因為Error類代表的是一般的應用不應該去捕獲的嚴重問題。可見,丟棄異常這一壞習慣是如此常見,它甚至已經影響到了Java本身的設計。
那么,應該怎樣改正呢?主要有四個選擇:
1、處理異常。針對該異常采取一些行動,例如修正問題、提醒某個人或進行其他一些處理,要根據具體的情形確定應該采取的動作。再次說明,調用printStackTrace算不上已經“處理好了異常”。
2、重新拋出異常。處理異常的代碼在分析異常之后,認為自己不能處理它,重新拋出異常也不失為一種選擇。
3、把該異常轉換成另一種異常。大多數情況下,這是指把一個低級的異常轉換成應用級的異常(其含義更容易被用戶了解的異常)。
4、不要捕獲異常。
結論一:既然捕獲了異常,就要對它進行適當的處理。不要捕獲異常之后又把它丟棄,不予理睬。
反例之二:不指定具體的異常
代碼:15行。
許多時候人們會被這樣一種“美妙的”想法吸引:用一個catch語句捕獲所有的異常。最常見的情形就是使用catch(Exception ex)語句。但實際上,在絕大多數情況下,這種做法不值得提倡。為什么呢?
要理解其原因,我們必須回顧一下catch語句的用途。catch語句表示我們預期會出現某種異常,而且希望能夠處理該異常。異常類的作用就是告訴Java編譯器我們想要處理的是哪一種異常。由于絕大多數異常都直接或間接從java.lang.Exception派生,catch(Exception ex)就相當于說我們想要處理幾乎所有的異常。
再來看看前面的代碼例子。我們真正想要捕獲的異常是什么呢?最明顯的一個是SQLException,這是JDBC操作中常見的異常。另一個可能的異常是IOException,因為它要操作OutputStreamWriter。顯然,在同一個catch塊中處理這兩種截然不同的異常是不合適的。如果用兩個catch塊分別捕獲SQLException和IOException就要好多了。這就是說,catch語句應當盡量指定具體的異常類型,而不應該指定涵蓋范圍太廣的Exception類。
另一方面,除了這兩個特定的異常,還有其他許多異常也可能出現。例如,如果由于某種原因,executeQuery返回了null,該怎么辦?答案是讓它們繼續拋出,即不必捕獲也不必處理。實際上,我們不能也不應該去捕獲可能出現的所有異常,程序的其他地方還有捕獲異常的機會??直至最后由JVM處理。
結論二:在catch語句中盡可能指定具體的異常類型,必要時使用多個catch。不要試圖處理所有可能出現的異常。
反例之三:占用資源不釋放
代碼:3行-14行。
異常改變了程序正常的執行流程。這個道理雖然簡單,卻常常被人們忽視。如果程序用到了文件、Socket、JDBC連接之類的資源,即使遇到了異常,也要正確釋放占用的資源。為此,Java提供了一個簡化這類操作的關鍵詞finally。
finally是樣好東西:不管是否出現了異常,Finally保證在try/catch/finally塊結束之前,執行清理任務的代碼總是有機會執行。遺憾的是有些人卻不習慣使用finally。
當然,編寫finally塊應當多加小心,特別是要注意在finally塊之內拋出的異常??這是執行清理任務的最后機會,盡量不要再有難以處理的錯誤。
結論三:保證所有資源都被正確釋放。充分運用finally關鍵詞。
反例之四:不說明異常的詳細信息
代碼:3行-18行。
仔細觀察這段代碼:如果循環內部出現了異常,會發生什么事情?我們可以得到足夠的信息判斷循環內部出錯的原因嗎?不能。我們只能知道當前正在處理的類發生了某種錯誤,但卻不能獲得任何信息判斷導致當前錯誤的原因。
printStackTrace的堆棧跟蹤功能顯示出程序運行到當前類的執行流程,但只提供了一些最基本的信息,未能說明實際導致錯誤的原因,同時也不易解讀。
因此,在出現異常時,最好能夠提供一些文字信息,例如當前正在執行的類、方法和其他狀態信息,包括以一種更適合閱讀的方式整理和組織printStackTrace提供的信息。
結論四:在異常處理模塊中提供適量的錯誤原因信息,組織錯誤信息使其易于理解和閱讀。
反例之五:過于龐大的try塊
代碼:3行-14行。
經常可以看到有人把大量的代碼放入單個try塊,實際上這不是好習慣。這種現象之所以常見,原因就在于有些人圖省事,不愿花時間分析一大塊代碼中哪幾行代碼會拋出異常、異常的具體類型是什么。把大量的語句裝入單個巨大的try塊就象是出門旅游時把所有日常用品塞入一個大箱子,雖然東西是帶上了,但要找出來可不容易。
一些新手常常把大量的代碼放入單個try塊,然后再在catch語句中聲明Exception,而不是分離各個可能出現異常的段落并分別捕獲其異常。這種做法為分析程序拋出異常的原因帶來了困難,因為一大段代碼中有太多的地方可能拋出Exception。
結論五:盡量減小try塊的體積。
反例之六:輸出數據不完整
代碼:7行-11行。
不完整的數據是Java程序的隱形殺手。仔細觀察這段代碼,考慮一下如果循環的中間拋出了異常,會發生什么事情。循環的執行當然是要被打斷的,其次,catch塊會執行??就這些,再也沒有其他動作了。已經輸出的數據怎么辦?使用這些數據的人或設備將收到一份不完整的(因而也是錯誤的)數據,卻得不到任何有關這份數據是否完整的提示。對于有些系統來說,數據不完整可能比系統停止運行帶來更大的損失。
較為理想的處置辦法是向輸出設備寫一些信息,聲明數據的不完整性;另一種可能有效的辦法是,先緩沖要輸出的數據,準備好全部數據之后再一次性輸出。
結論六:全面考慮可能出現的異常以及這些異常對執行流程的影響。
改寫后的代碼
根據上面的討論,下面給出改寫后的代碼。也許有人會說它稍微有點?嗦,但是它有了比較完備的異常處理機制。
OutputStreamWriter out = ...
java.sql.Connection conn = ...
try {
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery(
"select uid, name from user");
while (rs.next())
{
out.println("ID:" + rs.getString("uid") + ",姓名: " + rs.getString("name"));
}
}
catch(SQLException sqlex)
{
out.println("警告:數據不完整");
throw new ApplicationException("讀取數據時出現SQL錯誤", sqlex);
}
catch(IOException ioex)
{
throw new ApplicationException("寫入數據時出現IO錯誤", ioex);
}
finally
{
if (conn != null) {
try {
conn.close();
}
catch(SQLException sqlex2)
{
System.err(this.getClass().getName() + ".mymethod - 不能關閉數據庫連接: " + sqlex2.toString());
}
}
if (out != null) {
try {
out.close();
}
catch(IOException ioex2)
{
System.err(this.getClass().getName() + ".mymethod - 不能關閉輸出文件" + ioex2.toString());
}
}
}
本文的結論不是放之四海皆準的教條,有時常識和經驗才是最好的老師。如果你對自己的做法沒有百分之百的信心,務必加上詳細、全面的注釋。
另一方面,不要笑話這些錯誤,不妨問問你自己是否真地徹底擺脫了這些壞習慣。即使最有經驗的程序員偶爾也會誤入歧途,原因很簡單,因為它們確確實實帶來了“方便”。所有這些反例都可以看作Java編程世界的惡魔,它們美麗動人,無孔不入,時刻誘惑著你。也許有人會認為這些都屬于雞皮蒜毛的小事,不足掛齒,但請記住:勿以惡小而為之,勿以善小而不為。
NO.1 java.lang.NullPointerException
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
java.lang.NullPointerException
這個異常的解釋是 "程序遇上了空指針 ",簡單地說就是調用了未經初始化的對象或者是不存在的對象,對數組操作中出現空指針,很
多情況下是一些剛開始學習編程的朋友常犯的錯誤,即把數組的初始化和數組元素的初始化混淆起來了。數組的初始化是對數組分配需要的
空間,而初始化后的數組,其中的元素并沒有實例化,依然是空的,所以還需要對每個元素都進行初始化(如果要調用的話)
當應用程序試圖在需要對象的地方使用 null 時,拋出該異常。這種情況包括:
調用 null 對象的實例方法。
訪問或修改 null 對象的字段。
將 null 作為一個數組,獲得其長度。
將 null 作為一個數組,訪問或修改其時間片。
將 null 作為 Throwable 值拋出。
應用程序應該拋出該類的實例,指示其他對 null 對象的非法使用。
NO.2 java.lang.ClassNotFoundException
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.ClassNotFoundException
這個異常的解釋是 "指定的類不存在 ",這里主要考慮一下類的名稱和路徑是否正確即可.
當應用程序試圖使用以下方法通過字符串名加載類時,拋出該異常:
Class 類中的 forName 方法。
ClassLoader 類中的 findSystemClass 方法。
ClassLoader 類中的 loadClass 方法。
但是沒有找到具有指定名稱的類的定義。
NO.3 java.lang.ArithmeticException
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
java.lang.ArithmeticException
當出現異常的運算條件時,拋出此異常。
這個異常的解釋是 "數學運算異常 ",比如程序中出現了除以零這樣的運算就會出這樣的異常,對這種異常,大家就要好好檢查一下自
己程序中涉及到數學運算的地方,公式是不是有不妥了。
NO.4 java.lang.ArrayIndexOutOfBoundsException
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
java.lang.IndexOutOfBoundsException
java.lang.ArrayIndexOutOfBoundsException
這個異常的解釋是 "數組下標越界 ",現在程序中大多都有對數組的操作,因此在調用數組的時候一
定要認真檢查,看自己調用的下標是不是超出了數組的范圍,一般來說,顯示(即直接用常數當下標)調用不太容易出這樣的錯,但隱式(
即用變量表示下標)調用就經常出錯了,還有一種情況,是程序中定義的數組的長度是通過某些特定方法決定的,不是事先聲明的,這個時
候,最好先查看一下數組的length,以免出現這個異常。
用非法索引訪問數組時拋出的異常。如果索引為負或大于等于數組大小,則該索引為非法索引。
NO.5 java.lang.IllegalArgumentException
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
java.lang.IllegalArgumentException
拋出的異常表明向方法傳遞了一個不合法或不正確的參數。
這個異常的解釋是 "方法的參數錯誤 ",很多J2ME的類庫中的方法在一些情況下都會引發這樣的錯誤,比如音量調節方法中的音量參數
如果寫成負數就會出現這個異常,再比如 g.setColor(int red,int green,int blue)這個方法中的三個值,如果有超過255的也會出現
這個異常,因此一旦發現這個異常,我們要做的,就是趕緊去檢查一下方法調用中的參數傳遞是不是出現了錯誤。
NO.6 java.lang.IllegalAccessException
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.IllegalAccessException
這個異常的解釋是 "沒有訪問權限 ",當應用程序要調用一個類,但當前的方法即沒有對該類的訪問權限便會出現這個異常。對程序中
用了Package的情況下要注意這個異常。
當應用程序試圖創建一個實例(而不是數組)、設置或獲取一個字段,或者調用一個方法,但當前正在執行的方法無法訪問指定類、字段、
方法或構造方法的定義時,拋出 IllegalAccessException。
NO.7 java.lang.IncompatibleClassChangeError
java.lang.Object
java.lang.Throwable
java.lang.Error
java.lang.LinkageError
java.lang.IncompatibleClassChangeError
不兼容的類變化錯誤。當正在執行的方法所依賴的類定義發生了不兼容的改變時,拋出該異常。一般在修改了應用中的某些類的聲明定義而
沒有對整個應用重新編譯而直接運行的情況下,容易引發該錯誤。
在某些類定義中出現不兼容的類更改時拋出該異常。某些目前執行的方法所依賴的類定義已發生了變化。
NO.8 java.lang.InstantiationError
java.lang.Object
java.lang.Throwable
java.lang.Error
java.lang.LinkageError
java.lang.IncompatibleClassChangeError
java.lang.InstantiationError
實例化錯誤。當一個應用試圖通過Java的new操作符構造一個抽象類或者接口時拋出該異常.
通常由編譯器捕獲此錯誤;如果類定義中存在不兼容的更改,則此錯誤將只可能在運行時發生。
NO.9 java.lang.LinkageError
java.lang.Object
java.lang.Throwable
java.lang.Error
java.lang.LinkageError
鏈接錯誤。該錯誤及其所有子類指示某個類依賴于另外一些類,在該類編譯之后,被依賴的類改變了其類定義而沒有重新編譯所有的類,進
而引發錯誤的情況。
LinkageError 的子類指示一個類在一定程度上依賴于另一個類;但是,在編譯前一個類之后,后一個類發生了不相容的改變。
NO.10 java.lang.StackOverflowError
java.lang.Object
java.lang.Throwable
java.lang.Error
java.lang.VirtualMachineError
java.lang.StackOverflowError
堆棧溢出錯誤。當一個應用遞歸調用的層次太深而導致堆棧溢出時拋出該錯誤。
NO.11 java.lang.Exception
java.lang.Object
java.lang.Throwable
java.lang.Exception
Exception 類及其子類是 Throwable 的一種形式,它指出了合理的應用程序想要捕獲的條件。 (異常層次結構的根類)
NO.12 java.lang.RuntimeException
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
RuntimeException 是那些可能在 Java 虛擬機正常運行期間拋出的異常的超類。可能在執行方法期間拋出但未被捕獲的 RuntimeException
的任何子類都無需在 throws 子句中進行聲明。 (java.lang中異常的基類)
NO.13 java.util.InputMismatchException
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
java.util.NoSuchElementException
java.util.InputMismatchException
由 Scanner 拋出,表明用于檢索的標記與期望類型的模式不匹配,或者該標記處在期望類型的范圍之外。
NO.14 java.io.IOException
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.io.IOException
當發生某種 I/O 異常時,拋出此異常。此類為異常的通用類,它是由失敗的或中斷的 I/O 操作生成的。
NO.15 java.io.FileNotFoundException
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.io.IOException
java.io.FileNotFoundException
當試圖打開指定路徑名表示的文件失敗時,拋出此異常。
在不存在具有指定路徑名的文件時,此異常將由 FileInputStream、FileOutputStream 和 RandomAccessFile 構造方法拋出。
如果該文件存在,但是由于某些原因不可訪問,比如試圖打開一個只讀文件進行寫入,此時這些構造方法仍然會拋出該異常。
NO.16 java.io.EOFException
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.io.IOException
java.io.EOFException
當輸入過程中意外到達文件或流的末尾時,拋出此異常。
此異常主要被數據輸入流用來表明到達流的末尾。注意,其他許多輸入操作返回一個特殊值表示到達流的末尾,而不是拋出異常。
NO.17 java.lang.InterruptedException
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.InterruptedException
當線程在很長一段時間內一直處于正在等待、休眠或暫停狀態,而另一個線程用 Thread 類中的 iterrupt 方法中斷它時,拋出該異常。
NO.18 java.lang.NumberFormatException
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException
java.lang.IllegalArgumentException
java.lang.NumberFormatException
當應用程序試圖將字符串轉換成一種數值類型,但該字符串不能轉換為適當格式時,拋出該異常。
數據并發的問題
一個數據庫可能擁有多個訪問客戶端,這些客戶端都可以并發方式訪問數據庫。數據庫中的相同數據可能同時被多個事務訪問,如果沒有采取必要的隔離措施,就會導致各種并發問題,破壞數據的完整性。這些問題可以歸結為5類,包括3類數據讀問題(臟讀、幻象讀和不可重復讀)以及2類數據更新問題(第一類丟失更新和第二類丟失更新)。
臟讀(dirty read)
事務讀取B事務尚未提交的更改數據,并在這個數據的基礎上操作。如果恰巧B事務回滾,那么A事務讀到的數據根本是不被承認的。來看取款事務和轉賬事務并發時引發的臟讀場景
時間 |
轉賬事務A |
取款事務B |
T1 |
開始事務 |
|
T2 |
開始事務 |
|
T3 |
|
查詢賬戶余額為1000元 |
T4 |
|
取出500元把余額改為500元 |
T5 |
查詢賬戶余額為500元(臟讀) |
|
T6 |
撤銷事務余額恢復為1000元 |
|
T7 |
匯入100元把余額改為600元 |
|
T8 |
提交事務 |
B希望取款500元而后又撤銷了動作,而A往相同的賬戶中轉賬100元,就因為A事務讀取了B事務尚未提交的數據,因而造成賬戶白白丟失了500元。
不可重復讀(unrepeatable read)
不可重復讀是指A事務讀取了B事務已經提交的更改數據。假設A在取款事務的過程中,B往該賬戶轉賬100元,A兩次讀取賬戶的余額發生不一致:
時間 |
取款事務A |
轉賬事務B |
T1 |
開始事務 |
|
T2 |
開始事務 |
|
T3 |
|
查詢賬戶余額為1000元 |
T4 |
查詢賬戶余額為1000元 |
|
T5 |
|
取出100元把余額改為900元 |
T6 |
提交事務 |
|
T7 |
查詢賬戶余額為900元(和T4讀取的不一致) |
在同一事務中,T4時間點和T7時間點讀取賬戶存款余額不一樣。
幻象讀(phantom read)
A事務讀取B事務提交的新增數據,這時A事務將出現幻象讀的問題。幻象讀一般發生在計算統計數據的事務中,舉一個例子,假設銀行系統在同一個事務中,兩次統計存款賬戶的總金額,在兩次統計過程中,剛好新增了一個存款賬戶,并存入100元,這時,兩次統計的總金額將不一致:
時間 |
統計金額事務A |
轉賬事務B |
T1 |
開始事務 |
|
T2 |
開始事務 |
|
T3 |
統計總存款數為10000元 |
|
T4 |
新增一個存款賬戶,存款為100元 |
|
T5 |
提交事務 |
|
T6 |
再次統計總存款數為10100元(幻象讀) |
如果新增數據剛好滿足事務的查詢條件,這個新數據就進入了事務的視野,因而產生了兩個統計不一致的情況。
幻象讀和不可重復讀是兩個容易混淆的概念,前者是指讀到了其它已經提交事務的新增數據,而后者是指讀到了已經提交事務的更改數據(更改或刪除),為了避免這兩種情況,采取的對策是不同的,防止讀取到更改數據,只需要對操作的數據添加行級鎖,阻止操作中的數據發生變化,而防止讀取到新增數據,則往往需要添加表級鎖——將整個表鎖定,防止新增數據(Oracle使用多版本數據的方式實現)。
第一類丟失更新
A事務撤銷時,把已經提交的B事務的更新數據覆蓋了。這種錯誤可能造成很嚴重的問題,通過下面的賬戶取款轉賬就可以看出來:
時間 |
取款事務A |
轉賬事務B |
T1 |
開始事務 |
|
T2 |
開始事務 |
|
T3 |
查詢賬戶余額為1000元 |
|
T4 |
查詢賬戶余額為1000元 |
|
T5 |
匯入100元把余額改為1100元 |
|
T6 |
提交事務 |
|
T7 |
取出100元把余額改為900元 |
|
T8 |
撤銷事務 |
|
T9 |
余額恢復為1000元(丟失更新) |
A事務在撤銷時,“不小心”將B事務已經轉入賬戶的金額給抹去了。
第二類丟失更新
A事務覆蓋B事務已經提交的數據,造成B事務所做操作丟失:
時間 |
轉賬事務A |
取款事務B |
T1 |
|
開始事務 |
T2 |
開始事務 |
|
T3 |
|
查詢賬戶余額為1000元 |
T4 |
查詢賬戶余額為1000元 |
|
T5 |
取出100元把余額改為900元 |
|
T6 |
提交事務 |
|
T7 |
匯入100元 |
|
T8 |
提交事務 |
|
T9 |
把余額改為1100元(丟失更新) |
上面的例子里由于支票轉賬事務覆蓋了取款事務對存款余額所做的更新,導致銀行最后損失了100元,相反如果轉賬事務先提交,那么用戶賬戶將損失100元。
數據庫鎖機制
數據并發會引發很多問題,在一些場合下有些問題是允許的,但在另外一些場合下可能卻是致命的。數據庫通過鎖的機制解決并發訪問的問題,雖然不同的數據庫在實現細節上存在差別,但原理基本上是一樣的。
按鎖定的對象的不同,一般可以分為表鎖定和行鎖定,前者對整個表進行鎖定,而后者對表中特定行進行鎖定。從并發事務鎖定的關系上看,可以分為共享鎖定和獨占鎖定。共享鎖定會防止獨占鎖定,但允許其它的共享鎖定。而獨占鎖定既防止其它的獨占鎖定,也防止其它的共享鎖定。為了更改數據,數據庫必須在進行更改的行上施加行獨占鎖定,INSERT、UPDATE、DELETE和SELECT FOR UPDATE語句都會隱式采用必要的行鎖定。下面我們介紹一下ORACLE數據庫常用的5種鎖定:
? 行共享鎖定:一般通過SELECT FOR UPDATE語句隱式獲得行共享鎖定,在Oracle中你也可以通過LOCK TABLE IN ROW SHARE MODE語句顯式獲得行共享鎖定。行共享鎖定并不防止對數據行進行更改的操作,但是可以防止其它會話獲取獨占性數據表鎖定。允許進行多個并發的行共享和行獨占性鎖定,還允許進行數據表的共享或者采用共享行獨占鎖定;
? 行獨占鎖定:通過一條INSERT、UPDATE或DELETE語句隱式獲取,或者通過一條LOCK TABLE IN ROW EXCLUSIVE MODE語句顯式獲取。這個鎖定可以防止其它會話獲取一個共享鎖定、共享行獨占鎖定或獨占鎖定;
? 表共享鎖定:通過LOCK TABLE IN SHARE MODE語句顯式獲得。這種鎖定可以防止其它會話獲取行獨占鎖定(INSERT、UPDATE或DELETE),或者防止其它表共享行獨占鎖定或表獨占鎖定,它允許在表中擁有多個行共享和表共享鎖定。該鎖定可以讓會話具有對表事務級一致性訪問,因為其它會話在你提交或者回溯該事務并釋放對該表的鎖定之前不能更改這個被鎖定的表;
? 表共享行獨占:通過LOCK TABLE IN SHARE ROW EXCLUSIVE MODE語句顯式獲得。這種鎖定可以防止其它會話獲取一個表共享、行獨占或者表獨占鎖定,它允許其它行共享鎖定。這種鎖定類似于表共享鎖定,只是一次只能對一個表放置一個表共享行獨占鎖定。如果A會話擁有該鎖定,則B會話可以執行SELECT FOR UPDATE操作,但如果B會話試圖更新選擇的行,則需要等待;
? 表獨占:通過LOCK TABLE IN EXCLUSIVE MODE顯式獲得。這個鎖定防止其它會話對該表的任何其它鎖定。
事務隔離級別
盡管數據庫為用戶提供了鎖的DML操作方式,但直接使用鎖管理是非常麻煩的,因此數據庫為用戶提供了自動鎖機制。只要用戶指定會話的事務隔離級別,數據庫就會分析事務中的SQL語句,然后自動為事務操作的數據資源添加上適合的鎖。此外數據庫還會維護這些鎖,當一個資源上的鎖數目太多時,自動進行鎖升級以提高系統的運行性能,而這一過程對用戶來說完全是透明的。
ANSI/ISO SQL 92標準定義了4個等級的事務隔離級別,在相同數據環境下,使用相同的輸入,執行相同的工作,根據不同的隔離級別,可以導致不同的結果。不同事務隔離級別能夠解決的數據并發問題的能力是不同的。
表 1 事務隔離級別對并發問題的解決情況
隔離級別 |
臟讀 |
不可 重復讀 |
幻象讀 |
第一類丟失更新 |
第二類丟失更新 |
READ UNCOMMITED |
允許 |
允許 |
允許 |
不允許 |
允許 |
READ COMMITTED |
不允許 |
允許 |
允許 |
不允許 |
允許 |
REPEATABLE READ |
不允許 |
不允許 |
允許 |
不允許 |
不允許 |
SERIALIZABLE |
不允許 |
不允許 |
不允許 |
不允許 |
不允許 |
事務的隔離級別和數據庫并發性是對立的,兩者此增彼長。一般來說,使用READ UNCOMMITED隔離級別的數據庫擁有最高的并發性和吞吐量,而使用SERIALIZABLE隔離級別的數據庫并發性最低。
SQL 92定義READ UNCOMMITED主要是為了提供非阻塞讀的能力,Oracle雖然也支持READ UNCOMMITED,但它不支持臟讀,因為Oracle使用多版本機制徹底解決了在非阻塞讀時讀到臟數據的問題并保證讀的一致性,所以,Oracle的READ COMMITTED隔離級別就已經滿足了SQL 92標準的REPEATABLE READ隔離級別。
SQL 92推薦使用REPEATABLE READ以保證數據的讀一致性,不過用戶可以根據應用的需要選擇適合的隔離等級。