一樣的男人,不一樣的思想,造就不一樣的人生

          心胸有多大,舞臺就有多大

           

          2006年1月6日

          JAVA上傳文件

          轉載至翰血寶碼 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實現的。

          posted @ 2006-01-18 21:19 大夯 閱讀(1419) | 評論 (0)編輯 收藏

          Java本質論之關于Java棧與堆的思考

          原文地址 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類,以提高程序效率。

          posted @ 2006-01-08 00:59 大夯 閱讀(484) | 評論 (1)編輯 收藏

          論java內存泄露問題

          這幾天服務器碰到了內存泄漏的問題,就從網上找了篇看了一下希望對大家有一定的參考價值。
          (原文地址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比我聰明,不過應該也要動一點腦子吧

          posted @ 2006-01-08 00:42 大夯 閱讀(332) | 評論 (0)編輯 收藏

          一個程序員的一生

          一個程序員的一生
          作者:佚名 

              我在程序員的時候,我一開始追逐這個API怎么用,數據庫SQL怎么寫更優化,Dcom技術的細節,然后我發現我寫出來的產品為了符合客戶需求必須要大量修改,但是我的代碼卻粘在了一起,第一個感覺就是一個函數太長,一看就頭痛,而且一個函數干了好多事。這些事本來可以一段一段的,每段寫上注釋,然后有意義命名,自己管理錯誤和內存,然后把這些函數連在一起,
          然后我作了這些:

          1、小函數;
          2、寫上注釋;
          3、有意義命名;
          4、自己管理錯誤和內存;
          5、流程函數;

              最后我發現我這些函數可以組合成各種各樣的流程,我的程序終于好修改了,我很高興。但是我又發現,我的界面和我的流程混在了一起,另一個程序也想使用我的函數,但是我的函數中有對我的特定界面關聯的代碼,我不能連界面一起都給他,因為他有他的界面,但作的事我已經實現了,于是我把功能函數和界面控制分開了

              我就作了這些,我的代碼很容易理解,即使新員工,只要他看完業務手冊和數據結構,他就明白我代碼為什么這么寫。而且我的函數由于都是自己負責輸入參數和輸出參數的校驗,有明確和統一的報錯信息,所以很容易找到錯誤進行BUG修復。由于我的程序都是小函數組成的,都有明確報錯,所以錯誤很容易找到,經過測試組的專業測試后,我的代碼很穩定,即使出錯,也擴散不大,都是小bug,對系統整體沒有大影響

               雖然我在前進的過程中也經歷過困惑,一心鉆在OOP和設計模式中。但是有可能是功力不夠,不得其解。看著Delphi的源碼,應用了很多的OOP和模式,并且他的類庫多年發展也沒有多大的改變,所以深信OO和模式的威力,而對自己的能力很灰心。但是代碼還得繼續寫,還想進一步提高,于是才摸索出現在的一套做法。既實用又簡單應用,每個人都能辦到。

              我認為我的代碼方法已經可以滿足現在的產品制造,并且在軟件性能調整上也積累了一些珍貴的經驗。我發現性能最容易提高也效果最明顯的就是用SQL profilter,優化SQL。優化代碼,因為涉及到業務,很不好著手。優化數據庫結構,由于代碼都是構建在特定數據表之上,所以這是最難改的地方,但是我高興了沒多久,我又遇到問題了。因為我的程序即使再好改,但是客戶的需求真是千奇百怪,我每天在接聽用戶的電話,并且修改用戶千奇百怪的問題。我很煩。于是我作了實施員。我想真正看看客戶到底怎么回事。于是我理解了很多。我明白了很多的事情不是技術和軟件所能解決的,而是現實環境的弊病。但是這個弊病還不是一個工程就能解決的,這是一個復雜的網。所以這些問題我就說服用戶不要用軟件來處理,因為軟件是死的,而人的做法是靈活的。而且我發現用戶雖然提了很多需求,但是有的需求他一個月用不了一次,但是修改起來卻不容易。有的需求修改完,在實際應用中卻發現不可行,那個需求只是客戶想解決過去的問題而想的一個辦法根本沒有經過實際的校驗。有的需求修改來修改去都是表面問題,在實際應用中才發現重點問題沒有提需求所以上線又擱下了,我作了總結:

          1、軟件擅長大數據量計算和查詢,還有數據聯網共享,如果需求不能發揮軟件特點,就不讓軟件實現。這樣我少修改了一些;
          2、有的需求都是表面需求,修改了也用處不大,反而耽誤了重點需求的提出和修改,所以告訴用戶只修改核心功能。但是用戶提了很多需求,不修改完不上線。后來發現,由于他們沒有深刻理解我們系統的整體思想,所以沒有上線實際用,根本不知道新改的功能是否好用。用戶只是脫離了整體,單獨思考想怎么就怎么,沒上線根本他不知道后果,怎么說也不行,就得讓他看見教訓他才反悔,但是已經修改了。往往出現這樣的情況。最后得出一個結論:一次只提三個需求,并且用書面提出,免的說了不算算了不說。核心功能的需求修改可以滿足80%的日常使用就上線。這樣我少修改了很多;
          3、并且我在實際做工程中,積累了大量的經驗,寫成FAQ,各種成功案例,讓用戶在沒有提需求之前先看看自己到底有多少老軟件實在不能解決而才買新軟件幫助的事。新軟件就是解決你過去解決不了的事。如果你沒有解決不了的事,提什么需求;

              我的產品終于可以很快完成上線,所以可以大規模推廣市場了,但是我們的產品制造又出問題了。因為客戶越來越多,客戶的需求越來越多。我們需要開發更多的系統,但是我們的時間有限,我們的人手有限,而且我們的人手大多是新手。怎么辦。我們遇到了災難。我們的代碼質量因人而異。我們的版本管理混亂。我們的文檔沒有人編寫,大家都被分配到用戶處去上線。怎么準備數據字典,怎么切換系統,怎么記錄客戶需求,怎么管理系統,怎么修改代碼,我們沒有任何記錄。現場不能離開程序員一步,一離開用戶出事了就不知怎么辦,沒有任何可查的資料。于是我又做了項目管理,我們缺少很多規范。事有千萬,先從緊處來。寫文檔費時間,就開會給大家講做事的經驗。實施和代碼修改需要什么必要規范就制定什么規范。在這期間最容易犯的錯就是中央集權,什么事都必須自己做主。下屬不管大事小事都請示你。我被搞的什么都干不了,都成了救火隊員。我的團隊陷入了混亂之中,因為我煩亂之中作了很多飲鴆止渴的決定。我于是又犯了一個錯誤,我說你們能決定的事盡量自己決定,不要問我,我權利下放。結果是:各自作各自的事,互相不通知。有的事沒人管,有的事多人修改,各有一套。

              我終于明白了,我作了以下總結:
          1、項目經理是找到得力的人,指導他們做事的方向。如果下屬不知如何作時,及時提供給下屬做事方法;
          2、制定規范,其實也就是做事方法;
          3、制定計劃,分配人力去作。檢查結果;
          4、有緊急事務立刻做出果斷解決,繼續前進;

              我的團隊終于平靜了下來,但是大家都很疲憊。大家干的很累,但是由于實施和修改消耗了大量的錢,我們沒有賺錢,大家什么都沒有得到。團隊很灰心,也很失望。我下了計劃,我自己都很灰心,大家認為再努力也不會再有結果,所以拖拖拉拉,進度和成本已成不可再提的事情。人,缺少了精氣神,就什么都沒有了。我們就是缺少了這些。我就開始重新建立團隊的精神。我發現有人為了跳槽開始學習新的技術,而這種技術是公司現有產品不需要的,但是他們卻在上班時間作。我先從此下手。我講了技術的方向,讓他們認清他們現在所學將會很快淘汰。我又講了現在市場的實況,讓他們認清外面公司也不好過。我還講了我們所從事的行業多有潛力,我們公司將有新的舉措。人心又開始一點一點收回了。

              但是我們仍然需要完成那些未收尾的工作,仍然需要去奔赴新的客戶市場。雖然員工很疲憊,雖然我們剛從飄搖中過來,但是我們不能止步,因為我們為盈利而存在,我們別無選擇。我能夠將代碼寫的很好,性能很高,產品制造很有計劃和成本控制,團隊很有戰斗力。但是我發現了一個問題,我們的產品市場不再擴大了。市場份額大規模開拓已很艱難,因為新產品的新鮮感已經過去了。我們在動蕩的日子作的項目給公司帶來了陰影,公司一直沒有大賺錢,投資方很生氣。我明白了。公司畢竟是為利潤而存在的。公司不是為產品制造而存在,不是為了解決別人的問題而存在。賺錢是第一位。不賺錢即使你在媒體上作的很風光也一文不值。有人靠手賺錢,有人靠嘴賺錢,有人靠腦子賺錢,有人靠身體賺錢,不管黑貓白貓,只要抓住老鼠就是好貓。成在營銷,敗在管理。我開始關注資本運作,聯盟伙伴建設,市場營銷,客戶關系營運。

              我知道,生活才剛剛開始。

          posted @ 2006-01-06 13:00 大夯 閱讀(936) | 評論 (0)編輯 收藏

          導航

          統計

          常用鏈接

          留言簿(1)

          隨筆檔案

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 汪清县| 阿瓦提县| 天祝| 剑川县| 仪征市| 阿鲁科尔沁旗| 南通市| 商都县| 镇康县| 灯塔市| 广昌县| 商洛市| 汤原县| 天台县| 张家川| 寿阳县| 穆棱市| 通化市| 龙胜| 额济纳旗| 邢台县| 无锡市| 调兵山市| 元江| 县级市| 冷水江市| 利辛县| 林西县| 铜陵市| 四川省| 如皋市| 年辖:市辖区| 上栗县| 惠东县| 海兴县| 海阳市| 连平县| 河池市| 忻城县| 株洲市| 沙洋县|