Java - 軟件界的擎天柱

寫在前面

本文為那些年我們追過(guò)的語(yǔ)言之Java篇。Java是一門使用廣泛的向?qū)ο箝_發(fā)語(yǔ)言,用于開發(fā)應(yīng)用程序的技術(shù),通用高效安全。由于Java并非我的專長(zhǎng),本文由@Moonbal 主筆,我僅做完善補(bǔ)充。

學(xué)點(diǎn)Java

Java開發(fā)分為三個(gè)方向:

  1. J2SE(標(biāo)準(zhǔn)版):桌面應(yīng)用程序開發(fā)/網(wǎng)絡(luò)管理/電信
  2. J2EE(企業(yè)版):Web開發(fā)/電子商務(wù)/安全網(wǎng)站
  3. J2ME(移動(dòng)版):手機(jī)游戲

Java資源推薦:

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

Mac中的Java開發(fā)

下面是OS X 10.10系統(tǒng)環(huán)境下,Java Web開發(fā)的環(huán)境配置與實(shí)現(xiàn)。

  1. 下載 JDK for Mac OS X

  2. 下載 Eclipse IDE for Java EE Developers

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

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

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

  2. 參考eclipse中如何新建tomcat服務(wù),在 eclipse 中新建 tomcat 服務(wù)(這一步是在 eclipse 中創(chuàng)建并打開服務(wù),類似于創(chuàng)建實(shí)例)。如果服務(wù)沒(méi)有開啟(顯示 Stopped),右鍵單擊服務(wù),選擇 Start。如果在控制臺(tái)中打開了 apache 服務(wù),則 eclipse 會(huì)提示:8080 端口被占用。需先在控制臺(tái)關(guān)閉 apache 服務(wù),再回到 eclipse 打開服務(wù)。

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

  4. 剛剛只是寫了個(gè)靜態(tài) html 頁(yè)面。接下來(lái)就要開始編寫服務(wù)端,Servlet 客戶端與服務(wù)端交互的中樞??蛻舳说恼?qǐng)求要發(fā)給服務(wù)端的 Servlet,并在 Servlet 中處理后返回響應(yīng)給客戶端。打開工程目錄下的 Java Resources,該目錄下的 src 目錄,它將保存工程的所有 Java 文件。雙擊 src -> 選擇 New -> 選擇 Class,輸入 Name: MyServlet。復(fù)制下面代碼到該文件中,然后按快捷鍵 cmd + shift + o 導(dǎo)入所需包。

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

    private static final long serialVersionUID = 1L;
    
    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException 
    {
        // 請(qǐng)求的相關(guān)信息都可在 request 里找到
        // 要對(duì)客戶端做出的響應(yīng)通過(guò) response 實(shí)現(xiàn)
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {
            // 返回 JSON 數(shù)據(jù)直接返回 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
    }
}

雙擊該文件點(diǎn) Run As,點(diǎn)擊 Run On Server -> 選擇后點(diǎn)擊 finish。仔細(xì)觀察瀏覽器中鏈接的路徑,并修改上面類文件,嘗試不同結(jié)果。如果你有其他網(wǎng)絡(luò)編程的經(jīng)驗(yàn),你應(yīng)該很輕松就明白了客戶端和服務(wù)端是怎么交互的。如果想繼續(xù)學(xué)習(xí) Java 網(wǎng)絡(luò)編程,請(qǐng)參考 尚硅谷視頻鏈接。

黑魔法

1. Hello World

So Easy?那可不一定,請(qǐng)看下面代碼。

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()函數(shù)產(chǎn)生隨機(jī)數(shù),為什么每次打出的結(jié)果都是Hello World?請(qǐng)看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 執(zhí)行10.65s
        getRunningTime("notAutoboxing"); // notAutoboxing 執(zhí)行 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 + " 執(zhí)行" + (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;
        }
    }
}

我們發(fā)現(xiàn)使用Integer的確比int慢了很多,因?yàn)樵?code>useAutoboxing中,sum + = i;實(shí)際上執(zhí)行的是 int result = sum.intValue() + i; sum = new Integer(result);,生成了很多臨時(shí)Integer對(duì)象。不僅Integer要轉(zhuǎn)換成int執(zhí)行操作,還要增加GC垃圾回收的代價(jià),所以在含有大量操作的時(shí)候,盡量使用基本數(shù)據(jù)類型代替包裝類。

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>();
        
        // 當(dāng)我和Yogy成為朋友時(shí),她使用的還是C++
        Monkey yogy = new Monkey("Yogy", "C++");
        myFriends.add(yogy);
        
        // 現(xiàn)在Python已成為她的最愛
        yogy.setLanguage("Python");
        
        // 但當(dāng)我再次遇到Y(jié)ogy時(shí),我卻不能從我的腦海中找到她,我發(fā)現(xiàn)我失憶了
        System.out.println(myFriends.contains(yogy)); // 輸出false
    }
}

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

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

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

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

結(jié)束語(yǔ)

JAVA都有對(duì)象,但是經(jīng)常找不到對(duì)象。

Java

轉(zhuǎn)載請(qǐng)注明出處

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容