Java防SQL注入,最簡單的辦法是杜絕SQL拼接,SQL注入攻擊能得逞是因為在原有SQL語句中加入了新的邏輯,如果使用PreparedStatement來代替Statement來執(zhí)行SQL語句,其后只是輸入?yún)?shù),SQL注入攻擊手段將無效,這是因為PreparedStatement不允許在不同的插入時間改變查詢的邏輯結(jié)構(gòu),大部分的SQL注入已經(jīng)擋住了,在WEB層我們可以過濾用戶的輸入來防止SQL注入比如用Filter來過濾全局的表單參數(shù)。
import java.io.IOException; import java.util.Iterator; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 通過Filter過濾器來防SQL注入攻擊 * */ public class SQLFilter implements Filter { private String inj_str = "'|and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char|declare|; |or|-|+|,"; protected FilterConfig filterConfig = null; /** * Should a character encoding specified by the client be ignored? */ protected boolean ignore = true; public void init(FilterConfig config) throws ServletException { this.filterConfig = config; this.inj_str = filterConfig.getInitParameter("keywords"); } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest)request; HttpServletResponse res = (HttpServletResponse)response; Iterator values = req.getParameterMap().values().iterator();//獲取所有的表單參數(shù) while(values.hasNext()){ String[] value = (String[])values.next(); for(int i = 0;i < value.length;i++){ if(sql_inj(value[i])){ //TODO這里發(fā)現(xiàn)sql注入代碼的業(yè)務(wù)邏輯代碼 return; } } } chain.doFilter(request, response); } public boolean sql_inj(String str) { String[] inj_stra=inj_str.split("\\|"); for (int i=0 ; i < inj_stra.length ; i++ ) { if (str.indexOf(" "+inj_stra[i]+" ")>=0) { return true; } } return false; } } |
也可以單獨在需要防范SQL注入的JavaBean的字段上過濾:
/** * 防止sql注入 * * @param sql * @return */ public static String TransactSQLInjection(String sql) { return sql.replaceAll(".*([';]+|(--)+).*", " "); } |
在Java中有兩類線程:用戶線程(UserThread)、守護線程(DaemonThread)。
所謂守護線程,是指在程序運行的時候在后臺提供一種通用服務(wù)的線程,比如垃圾回收線程就是一個很稱職的守護者,并且這種線程并不屬于程序中不可或缺的部分。因此,當(dāng)所有的非守護線程結(jié)束時,程序也就終止了,同時會殺死進程中的所有守護線程。反過來說,只要任何非守護線程還在運行,程序就不會終止。
用戶線程和守護線程兩者幾乎沒有區(qū)別,唯一的不同之處就在于虛擬機的離開:如果用戶線程已經(jīng)全部退出運行了,只剩下守護線程存在了,虛擬機也就退出了。因為沒有了被守護者,守護線程也就沒有工作可做了,也就沒有繼續(xù)運行程序的必要了。
將線程轉(zhuǎn)換為守護線程可以通過調(diào)用Thread對象的setDaemon(true)方法來實現(xiàn)。在使用守護線程時需要注意一下幾點:
(1)thread.setDaemon(true)必須在thread.start()之前設(shè)置,否則會跑出一個IllegalThreadStateException異常。你不能把正在運行的常規(guī)線程設(shè)置為守護線程。
(2)在Daemon線程中產(chǎn)生的新線程也是Daemon的。
(3)守護線程應(yīng)該永遠不去訪問固有資源,如文件、數(shù)據(jù)庫,因為它會在任何時候甚至在一個操作的中間發(fā)生中斷。
代碼示例:
- import java.util.concurrent.TimeUnit;
- /**
- * 守護線程
- */
- public class Daemons {
- /**
- * @param args
- * @throws InterruptedException
- */
- public static void main(String[] args) throws InterruptedException {
- Thread d = new Thread(new Daemon());
- d.setDaemon(true); //必須在啟動線程前調(diào)用
- d.start();
- System.out.println("d.isDaemon() = " + d.isDaemon() + ".");
- TimeUnit.SECONDS.sleep(1);
- }
- }
- class DaemonSpawn implements Runnable {
- public void run() {
- while (true) {
- Thread.yield();
- }
- }
- }
- class Daemon implements Runnable {
- private Thread[] t = new Thread[10];
- public void run() {
- for (int i=0; i<t.length; i++) {
- t[i] = new Thread(new DaemonSpawn());
- t[i].start();
- System.out.println("DaemonSpawn " + i + " started.");
- }
- for (int i=0; i<t.length; i++) {
- System.out.println("t[" + i + "].isDaemon() = " +
- t[i].isDaemon() + ".");
- }
- while (true) {
- Thread.yield();
- }
- }
- }
|
運行結(jié)果:
- d.isDaemon() = true.
- DaemonSpawn 0 started.
- DaemonSpawn 1 started.
- DaemonSpawn 2 started.
- DaemonSpawn 3 started.
- DaemonSpawn 4 started.
- DaemonSpawn 5 started.
- DaemonSpawn 6 started.
- DaemonSpawn 7 started.
- DaemonSpawn 8 started.
- DaemonSpawn 9 started.
- t[0].isDaemon() = true.
- t[1].isDaemon() = true.
- t[2].isDaemon() = true.
- t[3].isDaemon() = true.
- t[4].isDaemon() = true.
- t[5].isDaemon() = true.
- t[6].isDaemon() = true.
- t[7].isDaemon() = true.
- t[8].isDaemon() = true.
- t[9].isDaemon() = true.
|
以上結(jié)果說明了守護線程中產(chǎn)生的新線程也是守護線程。
如果將mian函數(shù)中的TimeUnit.SECONDS.sleep(1);注釋掉,運行結(jié)果如下:
- d.isDaemon() = true.
- DaemonSpawn 0 started.
- DaemonSpawn 1 started.
- DaemonSpawn 2 started.
- DaemonSpawn 3 started.
- DaemonSpawn 4 started.
- DaemonSpawn 5 started.
- DaemonSpawn 6 started.
- DaemonSpawn 7 started.
- DaemonSpawn 8 started.
- DaemonSpawn 9 started.
|
以上結(jié)果說明了如果用戶線程已經(jīng)全部退出運行了,只剩下守護線程存在了,虛擬機也就退出了。下面的例子也說明了這個問題。
代碼示例:
- import java.util.concurrent.TimeUnit;
- /**
- * Finally shoud be always run ?
- */
- public class DaemonsDontRunFinally {
- /**
- * @param args
- */
- public static void main(String[] args) {
- Thread t = new Thread(new ADaemon());
- t.setDaemon(true);
- t.start();
- }
- }
- class ADaemon implements Runnable {
- public void run() {
- try {
- System.out.println("start ADaemon...");
- TimeUnit.SECONDS.sleep(1);
- } catch (InterruptedException e) {
- System.out.println("Exiting via InterruptedException");
- } finally {
- System.out.println("This shoud be always run ?");
- }
- }
- }
|
運行結(jié)果:
start ADaemon...
如果將main函數(shù)中的t.setDaemon(true);注釋掉,運行結(jié)果如下:
start ADaemon...
This shoud be always run ?
前兩篇文章中已經(jīng)介紹了三種搭建Linux學(xué)習(xí)環(huán)境的方法,本文將介紹第四、第五種方法。
第四種方法:在Windows系統(tǒng)中,用VNC工具軟件登錄到遠程服務(wù)器上的 Linux桌面。這種遠程登錄的方式,一方面可以用于遠程配置和維護Linux服務(wù)器,另一方面也可以用它來在真實的環(huán)境中學(xué)習(xí)各種Linux命令。 VNC(Virtual Network Computing,虛擬網(wǎng)絡(luò)計算機)軟件主要由兩部分組成,VNC server和VNC viewer。用戶要先將VNC server安裝在被控端的計算機上才能在主控端執(zhí)行VNC viewer控制被控端。
VNC server與VNC viewer支持多種操作系統(tǒng),如Unix、Linux、Solaris、Windows和MacOS,因此可將VNC server 及VNC viewer分別安裝在不同的操作系統(tǒng)中進行控制。如果目前操作的主控端計算機沒有安裝VNC viewer,也可以通過一般的網(wǎng)頁瀏覽器來控制被控端。VNC的主要工作原理是在服務(wù)器端運行 VNC server服務(wù),然后在客戶端就可以遠程連接服務(wù)器端的桌面了。下面就介紹,在主控端的計算機操作系統(tǒng)為Win 7,被控端服務(wù)器的操作系統(tǒng)為64位RedHat Enterprise Linux 5.3上的VNC viewer與VNC server的安裝與配置步驟:
1、在服務(wù)器端的RedHat中安裝VNC server,安裝包的文件名為“vnc-4.1.2-14.el5.x86_64.rpm”,此文件在光盤中的路徑為“RedHat Enterprise Linux 5.3 for 64-bit AMD64 AND INTEL 64_Installation\Server”。
也可以打開網(wǎng)站http://rpm.pbone.net/,在如下所示的對話框中輸入“vnc-4.1.2-14.el5”,然后點擊SEARCH,也可以找到相應(yīng)的RPM安裝包,下載下來即可,如圖9所示。

圖9 下載RPM包網(wǎng)站圖示
2、安裝VNC server的rpm包。可以把安裝包“vnc-4.1.2-14.el5.x86_64.rpm”拷貝到\root\.vnc目錄下,然后在此目錄下直接運行安裝命令即可,安裝過程的顯示提示如下所示:
[root@localhost .vnc]# rpm -ivh vnc-4.1.2-14.el5.x86_64.rpm
warning: vnc-4.1.2-14.el5.x86_64.rpm: Header V3 DSA signature: NOKEY, key ID 37017186
Preparing... ########################################### [100%]
package vnc-4.1.2-14.el5.x86_64 is already installed
3、編輯.vnc目錄下的xstartup文件。可以使用vi編輯器進行編輯,命令為“[root@localhost .vnc]#vi xstartup”,打開xstartup文件后在鍵盤上點擊“A”字母鍵,使VI編輯器進入編輯狀態(tài)。然后屏蔽掉最后一行,即在最后一行的前面加上符號 “#”,變成“#twm &”,然后再在最下面加上“gnome-session &”。完成后,點擊“Esc”鍵,再點擊“:”鍵,然后輸入“wq”回車,即保存退出。加上“gnome-session &”是為了能夠在Windows系統(tǒng)上顯示Linxu的桌面,否則只能看到一個“終端”的命令行窗口。
4、設(shè)置登錄用戶。如果上面的安裝成功,在目錄/etc/sysconfig/下會有一個vncservers文件。用VI編輯器編輯vncservers文件,在最后加上VNCSERVERS="1:root",保存后退出。
5、設(shè)置VNC遠程登錄密碼。運行命令“[root@localhost ~]# vncpasswd”,然后按提示設(shè)置好遠程登錄的密碼。然后執(zhí)行命令“[root@localhost ~]#vncserver”,會有如下顯示:
New 'localhost.localdomain:1 (root)' desktop is localhost.localdomain:1
Starting applications specified in /root/.vnc/xstartup
Log file is /root/.vnc/localhost.localdomain:1.log
這里需要注意的是,上面的輸出“localhost.localdomain:1 (root)”,說明在用瀏覽器遠程登錄Linux系統(tǒng)時,在瀏覽器地址欄中要輸入的地址為“Linux服務(wù)器的IP地址:5801”,如果上面的輸出為 “2(root)”,則在瀏覽器地址欄中要輸入的命令就為“5802”。如果連接成功,輸入密碼后就可以遠程控制Linux桌面了。

圖10 用VNC Viewer軟件登錄的界面圖示
6、用瀏覽器登錄Linux桌面的畫質(zhì)可能不太好,可以在Windows下安裝VNC Viewer工具軟件直接連接。首先要在Windows系統(tǒng)中安裝RealVNC軟件,如“vnc-4.1.3-x86_win32.exe”。安裝完成后,打開“開始”菜單里的RealVNC ---> VNC Viewer 4 ---> Run VNC Viewer,在VNC Viewer的地址欄中輸入“Linux服務(wù)器的IP地址:1”,如果上面的輸出為“2”,則在VNC Viewer的地址欄中的IP地址后面把“:1”換成“:2”,如圖10所示,點擊“OK”,然后輸入在上面“5”中設(shè)置的密碼,就可成功連接到遠程 Linux桌面,猶如在本地操作Linux系統(tǒng)一樣,如圖11所示。

圖11 用VNC Viewer遠程登錄到Linux桌面圖示
若在VNC Viewer的“Server”地址欄中輸入的IP地址后面所接的端口號,不是“localhost.localdomain:1 (root)”中的“1”,而是寫成了其它的數(shù)字,那可能只能進入到Linux系統(tǒng)的終端命令行模式,而進入不到圖形化的桌面模式。
下面將介紹第五種:用SSH方式登錄到遠程服務(wù)器的Linux系統(tǒng)中。其實和SSH登錄方式非常相像的還有Telnet登錄,但因為Telnet登錄的用戶名和密碼,以及在配置管理當(dāng)中所使用的Linux命令,都是以明文方式傳送,沒有任何安全措施,所以目前它基本上已經(jīng)被SSH的登錄方式所取代。SSH服務(wù)在Linux下的設(shè)置非常簡單。下面就簡要地介紹下SSH服務(wù)的設(shè)置與登錄的步驟:
1、SSH服務(wù)的安裝狀態(tài)。此服務(wù)默認是安裝的,但也可以通過以下命令來查詢在Linux系統(tǒng)中是否安裝了SSH服務(wù)。
[root@localhost ~]# rpm -qa | grep ssh
openssh-clients-4.3p2-29.el5
openssh-4.3p2-29.el5
openssh-askpass-4.3p2-29.el5
openssh-server-4.3p2-29.el5
若出現(xiàn)以上的顯示結(jié)果,則表示此Linux系統(tǒng)已經(jīng)安裝了SSH服務(wù)。輸出內(nèi)容的第一行顯示的是SSH的客戶端軟件包;第二行顯示的是SSH的核心文件;第三行表示SSH支持對話框的顯示,是一個基于X系統(tǒng)的密碼診斷工具;第四行是SSH的服務(wù)器軟件包。
2、SSH服務(wù)的運行狀態(tài)。此服務(wù)默認也是自動運行的,但也可以通過以下命令來查詢SSH服務(wù)的運行狀態(tài)。
[root@localhost ~]#service sshd status
openssh-daemon (pid 5340) is running...
若出現(xiàn)以上的顯示結(jié)果,則表示此Linux系統(tǒng)的SSH服務(wù)已經(jīng)運行。其中,“sshd”是SSH服務(wù)的守護進程名稱。若SSH服務(wù)沒有啟動的話,則運行命令[root@localhost ~]#service sshd restart即可。

圖12 用SecureCRT以SSH方式登錄的參數(shù)設(shè)置
3、用SSH進行遠程登錄的設(shè)置。若是在Windows系統(tǒng)中,沒有自帶的SSH客戶端,可以在網(wǎng)上下載支持SSH遠程登錄的圖形化工具軟件,常用的有 SecureCRT、Putty等。如圖12所示,是用SecureCRT進行遠程SSH登錄的設(shè)置。在“Hostname”中輸入SSH服務(wù)器的IP地址,“Port”中輸入22,“Username”中輸入用戶名。然后根據(jù)提示,輸入密碼,就可以連接到遠程的SSH服務(wù)器。若是在Linux系統(tǒng)中進行遠程的SSH登錄,就可以在Linux的終端窗口的命令提示符下,直接使用命令#ssh 192.168.1.2進行遠程登錄即可。
4、SSH的配置文件。SSH有兩個主要的配置文件,一個為客戶端的配置文件ssh_config,一個為服務(wù)器端的配置文件sshd_config。這兩個配置文件都位于目錄/etc/ssh下。用VI編輯器就可以對這兩個配置文件進行詳細的配置和修改,以便用戶在使用SSH時能滿足一些特殊的要求。
總結(jié)
以上五種搭建Linux學(xué)習(xí)環(huán)境的模式,主要是根據(jù)實際情況,選擇最適合自己的,它們之間無好壞之分。通常Linux高手對Linux的架構(gòu)、命令都很熟悉,更習(xí)慣使用SSH的方式。這種方式在配置和管理服務(wù)器時更靈活,功能更強大。而對于網(wǎng)速帶寬有保證的地方,同時又有充足的硬件服務(wù)器資源的話,采用第一種模式是最好的。
第二種和第三種模式主要適用有個人固定的筆記本或臺式機電腦,但網(wǎng)絡(luò)帶寬卻不能保證的用戶。這時,在個人電腦或虛擬機上安裝Linux,也是不錯的選擇。第四種和第一種有相似之處,都要求網(wǎng)絡(luò)帶寬要有保證,但第四種沒有第一種使用起來更方便。第四種還必須在電腦上安裝RealVNC軟件才行,若不安裝的話使用瀏覽器和JAVA程序控制遠程的Linux,效果并不是很好。
相關(guān)鏈接:
五種方法教你搭建Linux學(xué)習(xí)環(huán)境(一)
五種方法教你搭建Linux學(xué)習(xí)環(huán)境(二)
問:SQL Server的警報問題應(yīng)該如何解決?
答:具體的解決方法請參考下文 :
◆ 檢查你是否使用了最新的Sql Server service pack。因為在最新的Sql Server service pack中已經(jīng)修補了很多Sql Server使用警告(Alerts)的漏洞。應(yīng)該確保在你的系統(tǒng)中已經(jīng)安裝了最新的Sql Server service pack補丁包。
◆ 檢查SqlServerAgent服務(wù)的帳戶是否作為成員運行在域用戶群組(Domain User Group)下。LocalSystem帳戶沒有訪問網(wǎng)絡(luò)的權(quán)限,所以,如果你需要將事件記錄在其他運行有Windows NT或Windows 2000機器上的應(yīng)用程序日志上,或者你的作業(yè)(jobs)需要跨網(wǎng)絡(luò)的資源,或者你需要通過e-mail或者pagers通知操作者,這時候,你就需要將SalServerAgent服務(wù)的帳戶設(shè)置在域用戶群組(Domain User Group)下作為它的成員。
◆ 如果所定義的警報都沒有觸發(fā),檢查SqlServerAgent和EventLog服務(wù)是否都在運行。如果你需要讓你定義的警報被觸發(fā),這些服務(wù)必須被開啟。所以,如果這些服務(wù)沒有被運行的話,請運行它們。
◆ 如果有某個警報沒有被觸發(fā),請確保它們是否被啟用。警報可以被啟用或禁用,為了檢查一個警報是否被啟用,你應(yīng)該做以下這些步驟:
1、運行Sql Server Enterprise Manager
2、請打開服務(wù)器群組,然后打開某個服務(wù)器
3、請打開管理(Management),然后再打開Sql Server Agent
4、雙擊合適的警報以查看這個警報是否被啟用了
5、檢查警報的歷史值以確定警報最后工作的日期和時間
為了檢查一個警報的歷史值,你應(yīng)該做以下這些事情:
1、運行Sql Server Enterprise Manager
2、請打開服務(wù)器群組,然后打開某個服務(wù)器
3、請打開管理(Management),然后再打開Sql Server Agent
4、雙擊合適的警報以查看警報的歷史值
5、核對每20秒維護的計數(shù)器值
因為Sql Server Agent每20秒間隔維護一個性能計數(shù)器,如果發(fā)現(xiàn)這個性能計數(shù)器只有幾秒鐘(少于20秒)才維護一次,那么極有可能你的這個警報將不會被觸發(fā)。
6、檢查Sql Server錯誤日志、Sql Server Agent錯誤日志以及Windows NT和Windows 2000的應(yīng)用程序日志,以獲得有關(guān)錯誤描述的更多詳細信息。仔細檢查核對當(dāng)產(chǎn)生警報失敗事件時,被記錄在Sql Server錯誤日志、Sql Server Agent錯誤日志以及Windows NT和Windows 2000的應(yīng)用程序日志中的日期和時間以及對錯誤的描述能幫助你分析產(chǎn)生警報失敗事件的原因。
7、假如警報被觸發(fā)了,但是這時候操作員卻沒有收到任何通知,請嘗試手動使用“e-mail”,“pager”或者用“net send”將信息發(fā)送給操作員。在很多情況下,可能你輸入了一個錯%C。
工作以來,代碼越寫越多,程序也越來越臃腫,效率越來越低,對于我這樣一個追求完美的程序員來說,這是絕對不被允許的,于是除了不斷優(yōu)化程序結(jié)構(gòu)外,內(nèi)存優(yōu)化和性能調(diào)優(yōu)就成了我慣用的“伎倆”。
要對Java程序進行內(nèi)存優(yōu)化和性能調(diào)優(yōu),不了解虛擬機的內(nèi)部原理(或者叫規(guī)范更嚴謹一點)是肯定不行的,這里推薦一本好書《深入Java虛擬機(第二版)》(Bill Venners著,曹曉剛 蔣靖 譯,實際上本文正是作者閱讀本書之后,對Java虛擬機的個人理解闡述)。當(dāng)然了,了解Java虛擬機的好處并不僅限于上述兩點好處。從更深一點的技術(shù)層面上看,了解Java虛擬機的規(guī)范和實現(xiàn),將更加有助于我們編寫高效、穩(wěn)定的Java代碼。比如,假如了解Java虛擬機的內(nèi)存模型,了解虛擬機的內(nèi)存回收機制,那么我們就不會過分依賴它,而會在需要的時候顯式的"釋放內(nèi)存"(Java代碼不能顯式釋放內(nèi)存,但是可以通過釋放對象引用告知垃圾回收器回收該對象需要被回收),以降低不必要的內(nèi)存消耗;假如我們了解Java棧的工作原理,那么我們就可以通過減少遞歸層數(shù),減少循環(huán)次數(shù)來降低堆棧溢出的風(fēng)險。可能對于應(yīng)用開發(fā)人員來說,可能不會直接去涉及這些Java虛擬機底層實現(xiàn)的工作,但是了解這些背景知識,或多或少,都會對我們寫的程序產(chǎn)生潛移默化的好的影響。
本篇文章,將簡明扼要的說明Java虛擬機的體系結(jié)構(gòu)和內(nèi)存模型,如有用詞不妥或解釋不準確之處,請不吝指正,深感榮幸!
Java 虛擬機體系結(jié)構(gòu)

類裝載子系統(tǒng)
Java虛擬機有兩種類裝載器,分別是啟動類裝載器和用戶自定義裝載器。
通類裝載子系統(tǒng)通過類的全限定名(包名和類名,網(wǎng)絡(luò)裝載還包括 URL)將 Class 裝載進運行時數(shù)據(jù)區(qū)。對于每一個被裝載的類型,Java虛擬機都會創(chuàng)建一個java.lang.Class類的實例來代表該類型,該實例被放在內(nèi)存中的堆區(qū),而裝載的類型信息則位于方法區(qū),這一點和所有其他對象都是一樣的。
類裝載子系統(tǒng)在裝載一個類型前,除了要定位和導(dǎo)入對應(yīng)的二進制class文件外,還要驗證導(dǎo)入類的正確性,為類變量分配并初始化內(nèi)存,以及解析符號引用為直接引用,這些動作嚴格按照以下順序進行:
1)裝載——查找并裝載類型的二進制數(shù)據(jù);
2)連接——執(zhí)行驗證,準備以及解析(可選)
3)驗證 確保被導(dǎo)入類型的正確性
4)準備 為類變量分配內(nèi)存,并將其初始化為默認值
5)解析 把類型中的符號引用轉(zhuǎn)換為直接應(yīng)用
方法區(qū)
對于每一個被類裝載子系統(tǒng)裝載的類型,虛擬機都會保存下列數(shù)據(jù)到方法區(qū):
◆ 類型的全限定名
◆ 類型超類的全限定名(java.lang.Object沒有超類)
◆ 類型是類類型還是接口類型
◆ 類型的訪問修飾符
◆ 任何直接超接口的全限定名有序列表
除了上述基本類型信息,還將保存如下信息:
◆ 類型的常量池
◆ 字段信息(包括字段名、字段類型、字段修飾符)
◆ 方法信息(包括方法名、返回類型、參數(shù)的數(shù)量和類型、方法修飾符,如果方法不是抽象和本地的,還將保存方法的字節(jié)碼、操作數(shù)棧和該方法棧幀中的局部變量區(qū)的大小和異常表)
◆ 常量以外的所有類變量(其實就是類的靜態(tài)變量,因為靜態(tài)變量是所有實例共享的,且與類型直接相關(guān),所以他們是類一級的變量,作為類的成員被保存在方法區(qū))
一個到類ClassLoader的引用
//返回的就是剛才保存的ClassLoader引用 String.class.getClassLoader(); |
一個到Class類的引用
//將返回剛才保存的Class類的引用 String.class; |
注意,方法區(qū)也是可以被垃圾回收器回收的。
堆
Java程序在運行時創(chuàng)建的所有類實例或數(shù)組都放在同一個堆中,而每一個Java虛擬機也是有一個對空間,所有線程共享一個堆(這就是一個多線程的Java程序會產(chǎn)生對象訪問的同步問題的原因了)。
由于每一種Java虛擬機都有對虛擬機規(guī)范的不同實現(xiàn),所以我們可能不知道每一種Java虛擬機在堆中是以何種形式表示對象實例的,不過我們可以通過下面這可能的實現(xiàn)來一窺端倪:

程序計數(shù)器
對于運行中的Java程序而言,每一個線程都有自己的PC(程序計數(shù)器)寄存器,它是在該線程啟動時創(chuàng)建的,大小為一個字長,用來保存需要被執(zhí)行的下一行代碼的位置。
Java棧
每一個線程都有一個Java棧,以棧幀為單位保存線程的運行狀態(tài)。虛擬機對Java棧的操作有兩種:壓棧和出棧,二者都已幀為單位。棧幀保存了傳入?yún)?shù)、局部變量、中間運算結(jié)果等數(shù)據(jù),在方法完成時被彈出,然后釋放。
看一下兩個局部變量相加時棧幀的內(nèi)存快照

本地方法棧
這是 Java 調(diào)用操作系統(tǒng)本地庫的地方,用來實現(xiàn) JNI(Java Native Interface,Java 本地接口)
執(zhí)行引擎
Java虛擬機的核心,控制裝入 Java 字節(jié)碼并解析;對于運行中的Java程序而言,每一個線程都是一個獨立的虛擬機執(zhí)行引擎的實例,從線程生命周期的開始到結(jié)束,他要么在執(zhí)行字節(jié)碼,要么在執(zhí)行本地方法。
本地接口
連接了本地方法棧和操作系統(tǒng)庫。
注:文中所有提到“Java虛擬機”的地方都是指“JavaEE和JavaSE平臺的Java虛擬機規(guī)范”。
摘要: 在Java中,我們在實現(xiàn)繼承的時候存在下面幾個事實: 1、準備兩個類,他們用extends關(guān)鍵字鏈接起來 2、如果超類沒有默認構(gòu)造函數(shù),需要在子類構(gòu)造函數(shù)中顯式的super并傳參,如果都是默認構(gòu)造函數(shù)也可以super,不super虛擬機是自動的 3、子類可追加,覆蓋,重載方法,子類可以有自己的私有屬性,他們在子類構(gòu)造函數(shù)中被構(gòu)造 4、字段是數(shù)據(jù),方法在代碼區(qū),和類建立方法表,同一個類的對象...
閱讀全文
1、腳本說明
此腳本用于清空數(shù)據(jù)庫數(shù)據(jù),只刪除相關(guān)表記錄,保留表結(jié)構(gòu)及存儲過程觸發(fā)器等主要架構(gòu)。
設(shè)計思路:
1)根據(jù)表添加時間逆向獲取所有用戶表信息
2)使用游標循環(huán)刪除每張表內(nèi)數(shù)據(jù)
3)使用delete進行刪除,即使有外鍵關(guān)系同樣可以刪除表記錄
4)表存在自增主鍵則將其重置為0
5)截斷日志,將數(shù)據(jù)庫表空間及日志文件縮減到最小
2、使用說明
1)建立刪除數(shù)據(jù)庫存儲過程SP_DaTaBaSeClear
以下是代碼片段: If( object_id('SP_DaTaBaSeClear') is not null ) drop procedure SP_DaTaBaSeClear go SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE SP_DaTaBaSeClearASBegin Transaction declare @BtableName varchar(200) declare curDel cursor for select rtrim(name) from sysobjects where type = 'U' order by crdate desc open curDel declare @delSQL varchar(500) fetch next from curDel into @BtableName while( @@fetch_status = 0) begin set @delSQL = 'delete from ' + @BtableName print @delSQL exec( @delSQL ) if( ident_seed(@BtableName) is not null ) begin dbcc checkident( @BtableName, reseed, 0 ) print '種子成功置為1' end fetch next from curDel into @BtableName end close curDel deallocate curDel Commit GO |
2)執(zhí)行該存儲過程,執(zhí)行過程中查看執(zhí)行信息,如有紅色信息則先手動刪除紅色信息表記錄
-- 執(zhí)行存儲過程刪除表數(shù)據(jù)
EXEC SP_DaTaBaSeClear

3)如仍然報出紅色信息則直接執(zhí)行以下語句進行刪除
以下是代碼片段: declare @BtableName varchar(128) declare curDel cursor for select rtrim(name) from sysobjects where type = 'U' order by crdate desc open curDel declare @delSQL varchar(255) fetch next from curDel into @BtableName while( @@fetch_status = 0) begin set @delSQL = 'delete from ' + @BtableName print @delSQL exec( @delSQL ) if( ident_seed(@BtableName) is not null ) begin dbcc checkident( @BtableName, reseed, 0 ) print '種子成功置為1' end fetch next from curDel into @BtableName end close curDel deallocate curDel |
4)最后執(zhí)行腳本重置數(shù)據(jù)庫大小
以下是代碼片段: backup log @DataBaseName with no_log dbccshrinkdatabase(@DataBaseName) dbccupdateusage(@DataBaseName) |
1、查看當(dāng)前可用的所有環(huán)境變量(=系統(tǒng)變量+用戶變量)
set
查看某個環(huán)境變量,如PATH
set PATH
添加環(huán)境變量,如xxx=aa
set xxx=aa
將環(huán)境變量(如xxx)的值置為空
set xxx=
在某個環(huán)境變量(如PATH)后添加新的值(如d:\xxx)
set PATH=%PATH%;d:\xxx
[注]:以命令行方式對環(huán)境變量的操作只對當(dāng)前窗口的應(yīng)用有效!
2、Windows下JAVA用到的環(huán)境變量主要有3個:JAVA_HOME、CLASSPATH、PATH。
1)JAVA_HOME指向的是JDK的安裝路徑,如x:\ j2sdk1.4.2,在這路徑下你應(yīng)該能夠找到bin、lib等目錄。設(shè)置方法:JAVA_HOME=c:\ j2sdk1.4.2
2)PATH變量的作用
java程序在運行時首先在path變量所定義的路徑去找java.exe,并以最先找到的為準,如果安裝完j2sdk后不加設(shè)置,一般是C:\WINDOWS\system32目錄。
j2sdk1.4(還有其它java開發(fā)工具如jbuilder8)在安裝后會將java.exe拷貝到C:\WINDOWS\system32目錄下,當(dāng)執(zhí)行java.exe時,需要裝載這個SDK下的一些文件。
如j2sdk1.4在安裝完成后,C:\WINDOWS\system32下的java.exe在運行時會在C:\Program File\java\目錄下裝載必需的一些文件。但安裝j2sdk后一般會在PATH變量的最前面設(shè)置C:\ j2sdk1.4.2 \bin。
當(dāng)先安裝j2sdk1.4.2,后安裝jbuilder8等開發(fā)工具時,由于jbuilder8的java.exe在拷貝到C:\WINDOWS\system32時可能覆蓋了j2sdk1.4.2的java.exe,那么這時在運行的java.exe會到j(luò)builder8所在的目錄去裝載必需的一些文件。
3)CLASSPATH環(huán)境變量的作用
告訴類裝載器到哪里去尋找第三方提供的類和用戶定義的類。也可用使用JVM命令行參數(shù)-classpath分別為應(yīng)用程序指定類路徑,在-classpath中指定的類路徑覆蓋CLASSPATH環(huán)境變量中指定的值。
3、當(dāng)機器內(nèi)裝有多個SDK版本時,如何查看所用的是哪個SDK?
java -verbose
在出現(xiàn)的屏幕信息中可以看出系統(tǒng)裝載的是哪個目錄下的文件。
4、Windows OS下設(shè)置PATH的方法
〔系統(tǒng)〕->〔環(huán)境〕-> 〔高級〕,在PATH變量的文本框中的最前面輸入C:\ j2sdk1.4.2\bin
或在命令行窗口中執(zhí)行 set path=c:\j2sdk1.4.2\bin;%path%; 這樣在命令行窗口的任一路徑下都可以執(zhí)行java.exe程序了。或設(shè)置PATH=%JAVA_HOME%\bin;%PATH%
5、對于CLASSPATH環(huán)境變量的設(shè)置方法要加倍小心,是因為以后你出現(xiàn)的莫名其妙80%以上的怪問題都可能是由于CLASSPATH設(shè)置不對引起的
CLASSPATH=.\;%JAVA_HOME%\lib\tools.jar
首先要注意的是最前面的".\;",——句點反斜杠分號。這個是告訴JDK,搜索CLASS時先查找當(dāng)前目錄的CLASS文件。
【Troubleshooting】
編譯會出現(xiàn)以下情況,看你是否真得都理解環(huán)境變量的設(shè)置,并能解決它。
[T1] error:java不是一個可運行的程序? 由于沒有設(shè)置環(huán)境變量path
[T2] error:不能打開某個目錄? 可能是忽視了path環(huán)境變量中的目錄的設(shè)置順序。
[T3] Exception on thread “main” java.lang.DoClassDefFoundError:Test?沒有設(shè)置classpath的路徑。