轉載至翰血寶碼 http://lecky.tianyablog.com
commons fileupload 是Apache commons項目的一部分,FileUpload 使你很容易在servlet及web 應用中提供一個魯棒的、高性能的文件上特性。FileUpload按照RFC 1867 ( "Form-based File Upload in HTML")處理HTTP請求。即,如果HTTP request 以 POST方法提交,并且content type 設置為"multipart/form-data",那么FileUpload可以處理該請求,在web應用中提供文件上載的功能。其使用方法見commons fileupload的相關文檔。
在把FileUpload與struts結合(jsp + uploadactiono)使用過程中發現,如果在action mapping配置中不指定formbean,文件上傳過程正常。如果指定了formbean,文件上傳不正常,取不到文件。以下是幾個文件片斷:
upload.jsp
**form action="uploadaction.do?method=uploadByFileUpload" method="post" enctype="multipart/form-data" ** **input type="file" name="uploadfile"** **/form**UploadAction.java public ActionForward uploadByFileUpload(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { ......
String dir =
request.getSession().getServletContext().getRealPath(
"/");
DiskFileUpload fu= new DiskFileUpload();
fu.setSizeMax( UPLOAD_MAXSIZE);
fu.setSizeThreshold( MAX_DATA_IN_MEM);
fu.setRepositoryPath( System.getProperty("java.io.tmpdir"));
try {
List fileItem= fu.parseRequest( request);
Iterator it= fileItem.iterator();
while( it.hasNext()){
FileItem item= (FileItem)it.next();
if( !item.isFormField() && null!= item.getName() &&
0!= item.getName().trim().length()){
String clientPath = item.getName();
String fileName = new File(clientPath).getName();
File destDir = new File( dir);
File file = new File(destDir, fileName);
item.write( file);
map.put( item.getFieldName(),
dir+ File.separator+ fileName);
}
}
} catch (Exception e) {
String str= "文件上載異常,錯誤信息:"+ e.getMessage();
System.out.println(str);
throw new Exception( str, e);
}
......
}
struts-config.xml
name="TestForm"
type="UploadAction"
parameter="method" >
現象:在struts-config.xml文件中,如果指定了formbean——name="TestForm" ,則文件無法正確上傳,UploadAction中的fu.parseRequest( request)方法返回值為null;如果去掉了說明formbean的name屬性,則文件可以正常上傳。
原因:struts的RequestProccessor.process已經包含了處理文件上傳的方法。如果在action配置中設置了formbean ,那么在你自己的action處理request之前,struts已經在RequestProccessor.populate方法中處理了request,因此,在自己的action中就取不到上傳的文件了。
處理:如果要自己在action中處理文件上傳工作,那么就不要在配置文件中配置formbean。
其他選擇:如果仍需使用formbean,那么可以使用struts內置的文件上傳功能。具體使用方法見struts的相關文檔,以及struts的upload例子。以下是幾個文件片斷:
upload.jsp
基本同上,修改form標簽的action屬性
UploadAction.java
public ActionForward uploadByStruts(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
ActionErrors errs= new ActionErrors();
if (form != null){
DynaActionForm theForm = (DynaActionForm)form;
FormFile file = (FormFile)theForm.get("uploadfile");
try{
String fileName= file.getFileName();
if ("".equals(fileName)) {return null;}
InputStream stream = file.getInputStream();
String dir =
request.getSession().getServletContext().getRealPath(
"/");
OutputStream bos = new FileOutputStream(dir+"/"+fileName);
int bytesRead = 0;
byte[] buffer = new byte[8192];
while ((bytesRead = stream.read(buffer, 0, 8192)) != -1) {
bos.write(buffer, 0, bytesRead);
}
bos.close();
stream.close();
}catch (FileNotFoundException fnfe) {
...
}catch (IOException ioe) {
...
}catch (NullPointerException e){
...
}
}else{
...
}
if (!errs.isEmpty()){
saveErrors( request, errs);
}
return mapping.findForward( "success");
}
struts-config.xml
**form-bean name="TestForm" type="org.apache.struts.action.DynaActionForm"**
form-property name="uploadfile" type="org.apache.struts.upload.FormFile" /**
/form-bean**
**action path="/uploadaction"
name="TestForm" **!--指定formbean--**
type="UploadAction"
parameter="method" **
**forward name="success" path="/success.jsp" /**
**/action**
**controller maxFileSize="2M" /**
注意,使用struts自帶的文件上傳功能,最帶文件尺寸限制用來配置。另為struts對文件上載功能提供了兩種處理實現:org.apache.struts.upload.CommonsMultipartRequestHandler 和 org.apache.struts.upload.DiskMultipartRequestHandler。struts默認的是前者,如果要使用后者,需在中配置,配置樣例如下。而且,DiskMultipartRequestHandler是使用commons uploadload實現的。
原文地址 http://www.tyl.cn/edu/ShowArticle.asp?ArticleID=1539
1. 棧(stack)與堆(heap)都是Java用來在Ram中存放數據的地方。與C++不同,Java自動管理棧和堆,程序員不能直接地設置棧或堆。
2. 棧的優勢是,存取速度比堆要快,僅次于直接位于CPU中的寄存器。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。另外,棧數據可以共享,詳見第3點。堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,Java的垃圾收集器會自動收走這些不再使用的數據。但缺點是,由于要在運行時動態分配內存,存取速度較慢。
3. Java中的數據類型有兩種。
一種是基本類型(primitive types), 共有8種,即int, short, long, byte, float, double, boolean, char(注意,并沒有string的基本類型)。這種類型的定義是通過諸如int a = 3; long b = 255L;的形式來定義的,稱為自動變量。值得注意的是,自動變量存的是字面值,不是類的實例,即不是類的引用,這里并沒有類的存在。如int a = 3; 這里的a是一個指向int類型的引用,指向3這個字面值。這些字面值的數據,由于大小可知,生存期可知(這些字面值固定定義在某個程序塊里面,程序塊退出后,字段值就消失了),出于追求速度的原因,就存在于棧中。
另外,棧有一個很重要的特殊性,就是存在棧中的數據可以共享。假設我們同時定義:
int a = 3;
int b = 3;
編譯器先處理int a = 3;首先它會在棧中創建一個變量為a的引用,然后查找有沒有字面值為3的地址,沒找到,就開辟一個存放3這個字面值的地址,然后將a指向3的地址。接著處理int b = 3;在創建完b的引用變量后,由于在棧中已經有3這個字面值,便將b直接指向3的地址。這樣,就出現了a與b同時均指向3的情況。
特別注意的是,這種字面值的引用與類對象的引用不同。假定兩個類對象的引用同時指向一個對象,如果一個對象引用變量修改了這個對象的內部狀態,那么另一個對象引用變量也即刻反映出這個變化。相反,通過字面值的引用來修改其值,不會導致另一個指向此字面值的引用的值也跟著改變的情況。如上例,我們定義完a與b的值后,再令a=4;那么,b不會等于4,還是等于3。在編譯器內部,遇到a=4;時,它就會重新搜索棧中是否有4的字面值,如果沒有,重新開辟地址存放4的值;如果已經有了,則直接將a指向這個地址。因此a值的改變不會影響到b的值。
另一種是包裝類數據,如Integer, String, Double等將相應的基本數據類型包裝起來的類。這些類數據全部存在于堆中,Java用new()語句來顯示地告訴編譯器,在運行時才根據需要動態創建,因此比較靈活,但缺點是要占用更多的時間。 4. String是一個特殊的包裝類數據。即可以用String str = new String("abc");的形式來創建,也可以用String str = "abc";的形式來創建(作為對比,在JDK 5.0之前,你從未見過Integer i = 3;的表達式,因為類與字面值是不能通用的,除了String。而在JDK 5.0中,這種表達式是可以的!因為編譯器在后臺進行Integer i = new Integer(3)的轉換)。前者是規范的類的創建過程,即在Java中,一切都是對象,而對象是類的實例,全部通過new()的形式來創建。Java中的有些類,如DateFormat類,可以通過該類的getInstance()方法來返回一個新創建的類,似乎違反了此原則。其實不然。該類運用了單例模式來返回類的實例,只不過這個實例是在該類內部通過new()來創建的,而getInstance()向外部隱藏了此細節。那為什么在String str = "abc";中,并沒有通過new()來創建實例,是不是違反了上述原則?其實沒有。
5. 關于String str = "abc"的內部工作。Java內部將此語句轉化為以下幾個步驟:
(1)先定義一個名為str的對String類的對象引用變量:String str;
(2)在棧中查找有沒有存放值為"abc"的地址,如果沒有,則開辟一個存放字面值為"abc"的地址,接著創建一個新的String類的對象o,并將o的字符串值指向這個地址,而且在棧中這個地址旁邊記下這個引用的對象o。如果已經有了值為"abc"的地址,則查找對象o,并返回o的地址。
(3)將str指向對象o的地址。
值得注意的是,一般String類中字符串值都是直接存值的。但像String str = "abc";這種場合下,其字符串值卻是保存了一個指向存在棧中數據的引用!
為了更好地說明這個問題,我們可以通過以下的幾個代碼進行驗證。
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
注意,我們這里并不用str1.equals(str2);的方式,因為這將比較兩個字符串的值是否相等。==號,根據JDK的說明,只有在兩個引用都指向了同一個對象時才返回真值。而我們在這里要看的是,str1與str2是否都指向了同一個對象。
結果說明,JVM創建了兩個引用str1和str2,但只創建了一個對象,而且兩個引用都指向了這個對象。
我們再來更進一步,將以上代碼改成:
String str1 = "abc";
String str2 = "abc";
str1 = "bcd";
System.out.println(str1 + "," + str2); //bcd, abc
System.out.println(str1==str2); //false
這就是說,賦值的變化導致了類對象引用的變化,str1指向了另外一個新對象!而str2仍舊指向原來的對象。上例中,當我們將str1的值改為"bcd"時,JVM發現在棧中沒有存放該值的地址,便開辟了這個地址,并創建了一個新的對象,其字符串的值指向這個地址。
事實上,String類被設計成為不可改變(immutable)的類。如果你要改變其值,可以,但JVM在運行時根據新值悄悄創建了一個新對象,然后將這個對象的地址返回給原來類的引用。這個創建過程雖說是完全自動進行的,但它畢竟占用了更多的時間。在對時間要求比較敏感的環境中,會帶有一定的不良影響。
再修改原來代碼:
String str1 = "abc";
String str2 = "abc";
str1 = "bcd";
String str3 = str1;
System.out.println(str3); //bcd
String str4 = "bcd";
System.out.println(str1 == str4); //true
str3這個對象的引用直接指向str1所指向的對象(注意,str3并沒有創建新對象)。當str1改完其值后,再創建一個String的引用str4,并指向因str1修改值而創建的新的對象。可以發現,這回str4也沒有創建新的對象,從而再次實現棧中數據的共享。
我們再接著看以下的代碼。
String str1 = new String("abc");
String str2 = "abc";
System.out.println(str1==str2); //false
創建了兩個引用。創建了兩個對象。兩個引用分別指向不同的兩個對象。
String str1 = "abc";
String str2 = new String("abc");
System.out.println(str1==str2); //false
創建了兩個引用。創建了兩個對象。兩個引用分別指向不同的兩個對象。
以上兩段代碼說明,只要是用new()來新建對象的,都會在堆中創建,而且其字符串是單獨存值的,即使與棧中的數據相同,也不會與棧中的數據共享。
6. 數據類型包裝類的值不可修改。不僅僅是String類的值不可修改,所有的數據類型包裝類都不能更改其內部的值。 7. 結論與建議:
(1)我們在使用諸如String str = "abc";的格式定義類時,總是想當然地認為,我們創建了String類的對象str。擔心陷阱!對象可能并沒有被創建!唯一可以肯定的是,指向String類的引用被創建了。至于這個引用到底是否指向了一個新的對象,必須根據上下文來考慮,除非你通過new()方法來顯要地創建一個新的對象。因此,更為準確的說法是,我們創建了一個指向String類的對象的引用變量str,這個對象引用變量指向了某個值為"abc"的String類。清醒地認識到這一點對排除程序中難以發現的bug是很有幫助的。
(2)使用String str = "abc";的方式,可以在一定程度上提高程序的運行速度,因為JVM會自動根據棧中數據的實際情況來決定是否有必要創建新對象。而對于String str = new String("abc");的代碼,則一概在堆中創建新對象,而不管其字符串值是否相等,是否有必要創建新對象,從而加重了程序的負擔。這個思想應該是享元模式的思想,但JDK的內部在這里實現是否應用了這個模式,不得而知。
(3)當比較包裝類里面的數值是否相等時,用equals()方法;當測試兩個包裝類的引用是否指向同一個對象時,用==。
(4)由于String類的immutable性質,當String變量需要經常變換其值時,應該考慮使用StringBuffer類,以提高程序效率。
這幾天服務器碰到了內存泄漏的問題,就從網上找了篇看了一下,希望對大家有一定的參考價值。
(原文地址http://www.tyl.cn/edu/ShowArticle.asp?ArticleID=1548)
內存泄漏的慨念
1.c/c++是程序員自己管理內存,Java內存是由GC自動回收的。
我雖然不是很熟悉C++,不過這個應該沒有犯常識性錯誤吧。
2.什么是內存泄露?
內存泄露是指系統中存在無法回收的內存,有時候會造成內存不足或系統崩潰。
在C/C++中分配了內存不釋放的情況就是內存泄露。
3.Java存在內存泄露
我們必須先承認這個,才可以接著討論。雖然Java存在內存泄露,但是基本上不用很關心它,特別是那些對代碼本身就不講究的就更不要去關心這個了。
Java中的內存泄露當然是指:存在無用但是垃圾回收器無法回收的對象。而且即使有內存泄露問題存在,也不一定會表現出來。
4.Java中參數都是傳值的。
對于基本類型,大家基本上沒有異議,但是對于引用類型我們也不能有異議。
Java內存泄露情況
JVM回收算法是很復雜的,我也不知道他們怎么實現的,但是我只知道他們要實現的就是:對于沒有被引用的對象是可以回收的。所以你要造成內存泄露就要做到:
持有對無用對象的引用!
不要以為這個很容易做到,既然無用,你怎么還會持有它的引用? 既然你還持有它,它怎么會是無用的呢?
我實在想不到比那個堆棧更經典的例子了,以致于我還要引用別人的例子,下面的例子不是我想到的,是書上看到的,當然如果沒有在書上看到,可能過一段時間我自己也想的到,可是那時我說是我自己想到的也沒有人相信的。
public class Stack {
private Object[] elements=new Object[10];
private int size = 0;
public void push(Object e){
ensureCapacity();
elements[size++] = e;
}
public Object pop(){
if( size == 0)
throw new EmptyStackException();
return elements[--size];
}
private void ensureCapacity(){
if(elements.length == size){
Object[] oldElements = elements;
elements = new Object[2 * elements.length+1];
System.arraycopy(oldElements,0, elements, 0, size);
}
}
}
上面的原理應該很簡單,假如堆棧加了10個元素,然后全部彈出來,雖然堆棧是空的,沒有我們要的東西,但是這是個對象是無法回收的,這個才符合了內存泄露的兩個條件:無用,無法回收。
但是就是存在這樣的東西也不一定會導致什么樣的后果,如果這個堆棧用的比較少,也就浪費了幾個K內存而已,反正我們的內存都上G了,哪里會有什么影響,再說這個東西很快就會被回收的,有什么關系。下面看兩個例子。
例子1
public class Bad{
public static Stack s=Stack();
static{
s.push(new Object());
s.pop(); //這里有一個對象發生內存泄露
s.push(new Object()); //上面的對象可以被回收了,等于是自愈了
}
}
因為是static,就一直存在到程序退出,但是我們也可以看到它有自愈功能,就是說如果你的Stack最多有100個對象,那么最多也就只有100個對象無法被回收其實這個應該很容易理解,Stack內部持有100個引用,最壞的情況就是他們都是無用的,因為我們一旦放新的進取,以前的引用自然消失!
例子2
public class NotTooBad{
public void doSomething(){
Stack s=new Stack();
s.push(new Object());
//other code
s.pop();//這里同樣導致對象無法回收,內存泄露.
}//退出方法,s自動無效,s可以被回收,Stack內部的引用自然沒了,所以
//這里也可以自愈,而且可以說這個方法不存在內存泄露問題,不過是晚一點
//交給GC而已,因為它是封閉的,對外不開放,可以說上面的代碼99.9999%的
//情況是不會造成任何影響的,當然你寫這樣的代碼不會有什么壞的影響,但是
//絕對可以說是垃圾代碼!沒有矛盾吧,我在里面加一個空的for循環也不會有
//什么太大的影響吧,你會這么做嗎?
}
上面兩個例子都不過是小打小鬧,但是C/C++中的內存泄露就不是Bad了,而是Worst了。他們如果一處沒有回收就永遠無法回收,頻繁的調用這個方法內存不就用光了!因為Java還有自愈功能(我自己起的名字,還沒申請專利),所以Java的內存泄露問題幾乎可以忽略了,但是知道的人就不要犯了。
不知者無罪!Java存在內存泄露,但是也不要夸大其辭。如果你對Java都不是很熟,你根本就不用關心這個,我說過你無意中寫出內存泄露的例子就像你中一千萬一樣概率小,開玩笑了,其實應該是小的多的多!
而且即使你有幸寫出這樣的代碼,中獎了!基本上都是一包洗衣粉,不會讓你發財,對系統沒有什么大的影響。
杞人憂天的情況
1.無話可說型
Object obj=new Object();
obj=null;
//這個完全多此一舉,因為退出了作用范圍,對象的引用自動消失
//不要在你的程序中出現這樣的語句,沒有錯,但是就是不雅觀
2.思考不對型
void func(Object o){
o=new Object();
return
}
當我們知道Java參數是傳值,就知道上面的方法什么也沒錯,就是申請了一個對象然后再丟給GC。因為是傳值,這里的o是一個調用時候的拷貝,會不會無法回收?不就是拷貝嗎,退出方法什么都沒了,這個對象怎么會留的住。
3.盡量避免型
class A{
B b=new B(this);
}
class B{
A a;
B(A a){this.a=a;}
}
這個存在互相引用,可能導致孤島現象,但是這個不會造成內存泄露不過我自己覺得這個會降低GC的效率,就從我的智力來看,我覺得這種情況比一般情況難以判斷怎么回收!當然GC比我聰明,不過應該也要動一點腦子吧