Java - 軟件界的擎天柱

寫在前面

本文為那些年我們追過的語言之Java篇。Java是一門使用廣泛的向對象開發語言,用于開發應用程序的技術,通用高效安全。由于Java并非我的專長,本文由@Moonbal 主筆,我僅做完善補充。

學點Java

Java開發分為三個方向:

  1. J2SE(標準版):桌面應用程序開發/網絡管理/電信
  2. J2EE(企業版):Web開發/電子商務/安全網站
  3. J2ME(移動版):手機游戲

Java資源推薦:

  • Thinking in Java
    經典教程1
  • Effective Java
    經典教程2
  • Java 8 in Action: Lambdas, streams, and functional-style programming
    Java從Java8也開始支持函數式編程了。對于函數式編程,它是一種編程思想,在Java中使用函數式編程是為了使代碼更簡潔,且更可讀。
  • 深入理解Java虛擬機
    JVM是Java的核心與基礎,有人說,沒有學習過JVM卻說自己“精通Java”的同學就是在耍流氓。
  • 尚硅谷
    網站視頻包括《Java 基礎》、《Web 開發》、《JavaEE 框架》以及《Android 開發》,并且都是可以免費下載的。如果要學習建服務器和做項目,這里的內容蠻齊全的。
  • ImportNew
    這里有很多關于 Java 的優秀文章,基本每天都會有更新。

Mac中的Java開發

下面是OS X 10.10系統環境下,Java Web開發的環境配置與實現。

  1. 下載 JDK for Mac OS X

  2. 下載 Eclipse IDE for Java EE Developers

  3. 下載 Apache Tomcat(點擊 Binary Distributions 下面第一行的 zip

  4. 切換到解壓后的Tomcat目錄下,執行下列命令:

# 當前在 apache-tomcat 目錄下
cd bin
sudo chmod 755 *.sh
./startup.sh    # 打開服務。使用 ./shutdown.sh 可關閉服務
  1. 在 eclipse 中配置 apache-tomcat 服務(這一步是讓 eclipse 知道有 apache-tomcat 服務,類似于創建類):快捷鍵 cmd + , -> 選擇左邊的 Server -> 選擇 Runtime Environments -> 點擊右上角 Add... -> 選擇安裝的 apache-tomcat 版本 -> 點擊 next -> 點擊 Browse... 選擇 apache-tomcat 的根目錄 -> 點擊 Installed JREs... 選擇正確的 JRE -> 點擊 finish

  2. 參考eclipse中如何新建tomcat服務,在 eclipse 中新建 tomcat 服務(這一步是在 eclipse 中創建并打開服務,類似于創建實例)。如果服務沒有開啟(顯示 Stopped),右鍵單擊服務,選擇 Start。如果在控制臺中打開了 apache 服務,則 eclipse 會提示:8080 端口被占用。需先在控制臺關閉 apache 服務,再回到 eclipse 打開服務。

  3. 選擇 File -> New -> Dynamic Web Project -> 輸入工程名,創建好工程后 -> 在工程目錄的 WebContent 目錄下新建一個 html 文件 -> 右鍵該文件,選擇 Run As,點擊 Run On Server -> 選擇后點擊 finish。這樣,就能在 eclipse 自帶的瀏覽器中看到新建的 html 網頁了,網址的格式為:http://localhost:8080/工程名/文件名。所以說工程目錄的 WebContent 目錄就是網站的所有網頁文件的保存目錄了。

  4. 剛剛只是寫了個靜態 html 頁面。接下來就要開始編寫服務端,Servlet 客戶端與服務端交互的中樞。客戶端的請求要發給服務端的 Servlet,并在 Servlet 中處理后返回響應給客戶端。打開工程目錄下的 Java Resources,該目錄下的 src 目錄,它將保存工程的所有 Java 文件。雙擊 src -> 選擇 New -> 選擇 Class,輸入 Name: MyServlet。復制下面代碼到該文件中,然后按快捷鍵 cmd + shift + o 導入所需包。

@WebServlet(urlPatterns = {"/yogy.cc"}) 
// 它的作用是 HTTP 請求的 path 為 /yogy.cc 會把請求映射到這個 Servlet 做處理
// 最前面的 / 代表工程目錄
public class MyServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;
    
    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException 
    {
        // 請求的相關信息都可在 request 里找到
        // 要對客戶端做出的響應通過 response 實現
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {
            // 返回 JSON 數據直接返回 JSON 字符串
            out.println("<html><head>");
            out.println("<head>");
            out.println("<title>MyServlet</title>");
            out.println("</head>");
            out.println("<body>");
            out.println("<h1>Servlet MyFirstServlet at " + request.getContextPath() + "</h1>");
            out.println("</body></html>");
        } finally {
            out.close();
        }
    }
 
    @Override
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        //Do some other work
    }
}

雙擊該文件點 Run As,點擊 Run On Server -> 選擇后點擊 finish。仔細觀察瀏覽器中鏈接的路徑,并修改上面類文件,嘗試不同結果。如果你有其他網絡編程的經驗,你應該很輕松就明白了客戶端和服務端是怎么交互的。如果想繼續學習 Java 網絡編程,請參考 尚硅谷視頻鏈接

黑魔法

1. Hello World

So Easy?那可不一定,請看下面代碼。

import java.util.Random;

public class Yogy {
    public static void main(String[] args) {
        System.out.println(helloWorld());
    }    
    
    public static String randomString(int s) {
        Random ran = new Random(s);
        StringBuilder sb = new StringBuilder();
        for (int k; (k = ran.nextInt(27)) != 0; ) {
            sb.append((char)('`' + k));
        }
        return sb.toString();
    }
    
    public static String helloWorld() {
        return randomString(-229985452) + " " + randomString(-147909649);
    }
}

明明是在程序里使用了java.util.Random()函數產生隨機數,為什么每次打出的結果都是Hello World?請看Stackflow上有趣的討論

2. Integer 與 int

import java.lang.reflect.Method;
import java.util.Date;

public class Yogy {
    private static int MAXNUM = 1000000000;
    
    public static void main(String[] args) {
        getRunningTime("useAutoboxing"); // useAutoboxing 執行10.65s
        getRunningTime("notAutoboxing"); // notAutoboxing 執行 1.471s
    }

    public static void getRunningTime(String methodName) {
        try {
            Method method = Yogy.class.getMethod(methodName);
            long stTime = new Date().getTime();
            method.invoke(null);
            System.out.println(methodName + " 執行" + (new Date().getTime() - stTime) / 1000. + "s");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    
    public static void useAutoboxing() {
        Integer sum = 0;
        for (int i = 1000; i < MAXNUM; ++i) {
            sum += i;
        }
    }
    
    public static void notAutoboxing() {
        int sum = 0;
        for (int i = 0; i < MAXNUM; ++i) {
            sum += i;
        }
    }
}

我們發現使用Integer的確比int慢了很多,因為在useAutoboxing中,sum + = i;實際上執行的是 int result = sum.intValue() + i; sum = new Integer(result);,生成了很多臨時Integer對象。不僅Integer要轉換成int執行操作,還要增加GC垃圾回收的代價,所以在含有大量操作的時候,盡量使用基本數據類型代替包裝類。

3. hashCode()和equals()

import java.util.HashSet;

class Monkey { 
    private String nickName;

    private String language;

    public Monkey(String name, String lang) {
        this.nickName = name;
        this.language = lang;
    }

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

    public String getLanguage() {
        return language;
    }

    public void setLanguage(String language) {
        this.language = language;
    }

    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((language == null) ? 0 : language.hashCode());
        result = prime * result
                + ((nickName == null) ? 0 : nickName.hashCode());
        return result;
    }
}

public class Yogy {
    public static void main(String[] args) {
        HashSet<Monkey> myFriends = new HashSet<Monkey>();
        
        // 當我和Yogy成為朋友時,她使用的還是C++
        Monkey yogy = new Monkey("Yogy", "C++");
        myFriends.add(yogy);
        
        // 現在Python已成為她的最愛
        yogy.setLanguage("Python");
        
        // 但當我再次遇到Yogy時,我卻不能從我的腦海中找到她,我發現我失憶了
        System.out.println(myFriends.contains(yogy)); // 輸出false
    }
}

到底發什么什么呢?在Java中,對象都是引用類型的數據,myFriends中保存的是yogy對象的引用,所以當yogy改變時,myFriends中的對象也會改變,但我卻為什么不能想起她?理由是Monkey中重寫了hashCode()方法,并且字段language也參與了hashCode的生成。打印出yogy使用C++時的hashCode為4800998,而yogy使用Python時的hashCode為1563076845,在哈希表的實現中如果兩元素hashCode不等,則直接認為兩元素不等。

在上例中,刪除Monkey中對hashCode重載,我能想起yogy。但是如果重新new Monkey(“Yogy”, “Python”),我還是會失憶。因為默認的hashCode是對象在內存中地址,重新new的對象和以前對象的地址不同,hashCode也會不同。因此要重寫hashCode方法,使得只有字段nickName參與hashCode的生成,代碼如下。

    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((nickName == null) ? 0 : nickName.hashCode());
        return result;
    }

按照開始的版本,我能想起yogy。但對于new Monkey(“Yogy”, “Python”)還是不行,還需要重寫equals()方法,默認的equals()也是比較兩個對象的地址是否相等。重寫equals()使得只有nickName參與相等類型的比較。這樣我就能通過看到Yogy想起我的朋友了。

結束語

JAVA都有對象,但是經常找不到對象。

Java

轉載請注明出處

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容

  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,721評論 18 399
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,785評論 18 139
  • 從三月份找實習到現在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發崗...
    時芥藍閱讀 42,324評論 11 349
  • 旅行,就是從自己活膩的地方到別人活膩的地方去游玩,走一圈回來,我們變了嗎?變了,視野和格局變了,整個人都容光煥發了...
    臻靜閱讀 308評論 0 1
  • 這段時間總感覺不對,好像沒有之前那么有幸福感,總不是什么滋味。今天中午陪孩子睡覺時,我突然驚醒,腦洞大開,原...
    戴老師成長記錄儀閱讀 269評論 5 4