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阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,620评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,649评论 18 139
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,233评论 11 349
  • 旅行,就是从自己活腻的地方到别人活腻的地方去游玩,走一圈回来,我们变了吗?变了,视野和格局变了,整个人都容光焕发了...
    臻静阅读 297评论 0 1
  • 这段时间总感觉不对,好像没有之前那么有幸福感,总不是什么滋味。今天中午陪孩子睡觉时,我突然惊醒,脑洞大开,原...
    戴老师成长记录仪阅读 259评论 5 4