首页资源分类嵌入式开发嵌入式系统 > Java基础讲座

Java基础讲座

已有 460559个资源

下载专区


TI最新应用解决方案

工业电子 汽车电子 个人消费电子

上传者其他资源

文档信息举报收藏

标    签: Java基础面对对象的语言

分    享:

文档简介

Java面对对象的语言,可以作为c、c++语言学习的扩展。本文档为Java基础的入门资料,里面有作者博客的资料链接,感觉不错!

分类里好像没一个专门针对语言类的,所以只好选择嵌入式系统

文档预览

《Android 学习指南》博客地址:http://android.yaohuiji.com/about 说明: 本文档有《Android 学习指南》博客文章制作而成,所有版权归原作者所有。 由 ouling 制作。《Android 学习指南》还有大量的 android 入门的教学博客,确实是很好的学习 android 入门学习博客。 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 欧零博客地址:http://www.cnblogs.com/ouling/ 欢迎大家交流 目录 说明: ..............................................................................................................................................1 Java 基础第一讲:Java 的故事和 Java 编程环境搭建...................................................................2 Java 基础第二讲:Java 基本语法(一) .....................................................................................25 Java 基础第三讲:Java 基本语法(二) .....................................................................................36 Java 基础第四讲:Java 基本语法(三) .....................................................................................42 Java 基础第五讲:流程控制(一) .............................................................................................51 Java 基础第六讲:流程控制(二) .............................................................................................65 Java 基础第七讲:面向对象基础(一) .....................................................................................72 Java 基础第八讲:面向对象基础(二) .....................................................................................79 Java 基础第九讲:面向对象基础(三) .....................................................................................90 Java 基础第十讲:面向对象基础(四) ...................................................................................107 Java 基础第十一讲:面向对象基础(五) ...............................................................................113 Java 基础第十二讲:面向对象基础(六) ...............................................................................118 Java 基础第十三讲:数组...........................................................................................................125 Java 基础第十四讲:字符串.......................................................................................................128 Java 基础第十五讲:集合(一)...............................................................................................130 Java 基础第十六讲:集合(二)...............................................................................................139 Java 基础第十七讲:异常处理(一) .......................................................................................148 1 欧零博客地址:http://www.cnblogs.com/ouling/ Java 基础第一讲:Java 的故事和 Java 编程环 境搭建 一、《Java 基础讲座》前言: 学习 Android 如果 Java 基础不好,势必举步维艰,所以从今天开始连载《Android 的 Java 基础讲座》。本系列讲座假想对象是 Java 没有任何基础的朋友。Java 基础知识不是高科技,无数人都会,但是基础好的并不多,如何用清晰、简练、 生动有趣的方式把 Java 的基础概念讲清楚,也比较困难。我能承诺的是用心去 做、持续修订每一讲,本系列分 35 讲,具体目录可以看这里。 二、Java 的故事: 我们知道人可以使用程序软件来操控计算机的硬件来完成一些工作,而软件(程 序)则是由某种编程语言编写的。Java 就是这样一种正在广泛流行和使用的面 向对象的编程语言,也是我们今后研究的重点语言。 1、Java 的诞生 让我们把时空切换到 1982 年,那一年一个伟大的公司诞生于美国斯坦福大学校 园,它的名字叫 Sun Microsystems,直译的话叫太阳微系统公司,事实上 Sun 是 Stanford University Network 的缩写。Sun 在 IT 行业中被认为是最具创造 性的企业。是极少数几个同时拥有自己微处理器、电脑系统、操作系统的公司。 1990 年的一天,Sun 的总裁麦克尼利(McNealy)听说他最好的一个工程师詹姆 斯.高斯林(James Gosling)打算离职,他感觉事态很严重。直觉告诉他优秀的 员工的离去意味着公司正在出大麻烦。麦克尼利必须找高斯林和其他员工好好谈 谈,看看问题出在哪里。 这些员工的意见很一致。Sun 公司本来是硅谷极为特殊的一个公司,以充满活力、 富于创新著称。太阳微系统公司一直很尊重员工,尽量发挥他们的创造力和热情。 但是,近年来,太阳微系统公司却越来越像成熟的大公司了。连哥斯林这样的人, 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 公司也安排他去做一些为老系统写升级软件这种琐碎的工作。正在扼杀着太阳微 系统公司员工的创新思想和工作热情。高斯林他们想做一些伟大的、革命性的事 情,但在 Sun 公司现在的状况中是不可能实现的。 Java 的创造者 詹姆斯.高斯林(James Gosling) 随后,麦克尼利采取了一个大胆的举动,他让高斯林自己组建一个完全独立于公 司的小组,由小组成员自己决定工作目标和进度。麦克尼利对哥斯林说:“我不 管你们要做什么,要多少钱、多少人,也不管你们花多长时间做出来,公司都无 条件支持。” 这个后来取名为“绿色小组”所要研究的产品就是十年后风靡 IT 界的数字家 电、后 PC 设备和家庭网。事实证明,绿色小组的研究并不十分成功,直到 2001 年,Sun 在数字家电方面的业绩并不很突出。但是,绿色小组的一个副产品,高 斯林发明的 Java 程序设计语言,却深深改变了这个世界„„ 绿色小组成立之初只有 4 个人。他们有一个很模糊的想法,甚至连最终的目标产 品是硬件还是软件也不知道。但是他们知道必须发明一些技术或者产品让 Sun 公司赶上信息领域的下一波大浪潮。 当时人类已经发明了很多种消费类电子产品,包括微机、手机、手持电脑、录相 机、电视机、洗衣机、冰箱、微波炉等等。他们认为要将这些设备数字化并用网 络互联讲是今后的方向(物联网?)。绿色小组将这个需求归结成两个产品原型 目标,即发明一种手持遥控设备来实现所有家电设备的互联(硬件);发明一种 程序设计语言,用它来编写能在这些设备上运行的小巧程序(软件)。 高斯林给当时设计了一种运行在虚拟机中的面向对象的语言,起名叫 Oak(橡树, 高斯林窗外的一颗树)。 3 欧零博客地址:http://www.cnblogs.com/ouling/ 但是申请注册上商标时,发现 Oak 被其他公司注册了,不得不重新起名。当时他们正在咖 啡馆里喝着印尼爪哇(Java)岛出产的咖啡,有一个人灵机一动说就叫 Java 怎么样,并得到了 其他人的赞赏,于是他们就将这种程序语言命名为 Java。 绿色小组的成员每周工作七天,平均每天工作 12 到 14 个小时,后期工程师们几 乎住在实验室,没日没夜地干,只是每隔几天回家洗澡换衣服。三年以后他们制 作出了第一台样机,尽管实现了基本功能,但造价在一万美元以上,尽管市场前 景不明朗,技术上也还有很多问题,Sun 公司的管理层还是用奖金和股权大大奖 励了绿色小组的成员,并加大投入,努力实现产品化。 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 但是公司内外对其产品都不看好,市场也并不认可。绿色小组的成员在沮丧和失 望中度过了整个 1993 年和 1994 年。在士气最低落的时候,大部分成员都离开了 绿色小组,有的甚至离开了 Sin 公司。留下来的人也失去了工作热情。不少人每 天早上 11 点钟上班,下午 4 点钟就离开了。有些人一天到晚只是玩游戏,还有 的人则念学术论文。 在黑暗的日子里他们都期待着上天能眷顾他们这些苦命的人,期待着某种奇迹出 现„„ 当时互联网已经出现了 20 年左右,但 Ftp 和 Telnet 的方式无法在科研人员之外 的人群普及和应用,1994 年一个名叫网景的公司推出了一种叫做 Netscape 浏览 器的东西,加速了互联网的普及;高斯林他们意识到互联网是一个今后的发展方 向。开始制作针对互联网的 Java 应用,希望会有所斩获。 1995 年初的一天,高斯林和以往一样不停地参加各种会议以期让人们认可他们 的产品,这次他参加的是“硅谷-好莱坞”互联网及娱乐业的研讨会。演讲刚开 始是,大家对高斯林的讲解意兴阑珊,直到他将鼠标移向一个分子模型,这个分 子模型动起来了,而且会随着鼠标的移动上下翻滚!场面立刻发生了逆转,会场 一下子沸腾起来,人们惊叹不已、啧啧称奇。刹那间,人们对互联网的潜力进行 了一番新的审视!也就在刹那间,这一批有影响力的人成了高斯林最忠实也是最 有力的说客。 Java 活下来了,并且成了互联网时代最强势、最具代表性的语言。 2、微软和 Sun 针对 Java 的世纪之战 Java 特点是,一次编写,到处运行,可以适应于任何平台。而互联网就是这样 一个可以是任意平台的超大网络。所以 Java 借着互联网快速发展的东风,扶摇 而上,迅速穿红。 面对 Java 金矿,大家都跃跃欲试„„ 这其中要数微软和 Sun 之间的斗争最为典 型: 1996 年 9 月的某一天,微软浏览器部门的主管艾达姆·波茨瓦斯几经考量之后, 提笔给时任微软 CEO 的比尔·盖茨写了一邮件,他非常恳切地提醒比尔·盖茨注 意一个正在形成的威胁。他写到:"必须意识到 Java 不仅仅是一种语言,如果它 只是一种语言,我们愿意并且能够容易地为它建立最佳的表现形式,事情可以圆 满解决了。但是事实上,Java 绝不仅仅是一种语言,它是 COM 的替代者!" 而 COM 恰恰是 Windows 的编程模型。而 Java 编程很多时候比 C++编程要容易的多, 更致命的是他是跨平台的。波茨瓦斯也提出了对抗 Java 的方法,就是悄悄地为 Java 提供某些扩展,使得用 Java 编写的程序能够在 Windows 中工作得更好,但 是在其它平台上却不能运行。 盖茨显然被这封信吓坏了,他第二天就回信了:"这可把我吓坏了。我不清楚微 软的操作系统要为 Java 的客户应用程序代码提供什么样的东西,而这些东西将 5 欧零博客地址:http://www.cnblogs.com/ouling/ 足够让它来取代我们的市场地位。了解这一点非常重要,是应该最优先考虑的事 情。"(没想到,这封信成为几年后司法部针对微软的反托拉斯案的呈堂证供。) 自此微软和 Sun 针对 Java 的世纪之战拉开了„„ 第一回合:微软推出 J++语言,并推出了 Visual j++集成编程工具,对 Java 进 行了大量的修改。1997 年,Sun 公司以歧视使用 Java 软件,旨在维持其视窗操 作系统的垄断地位,违反反垄断法为由起诉微软,2001 年 1 月,SUN 胜诉,根据 双方达成的和解协议,微软不得对 Windows 操作系统中包含的 Java 语言作任何 改动,并获赔 2000 万美元。 第二回合:2001 年年底,微软在推出新版操作系统 Windows XP 和新版 IE 时, 故意不安装 Java 软件,并且推出自己仿造 Java 创造的语言 C#和.net 框架。2002 年的 3 月 8 日,SUN 公司向美国加州地区法庭提出起诉,称此举造成它直接经济 损失高达 10 亿美元。2002 年 6 月,微软干脆称从 2004 年起,因为安全原因微 软的 Windows 操作系统将不再支持 Java 语言。 就在双方口水战日益升级之际,迎来了有关 SUN 诉微软案的第一次听证会。SUN 起诉微软的听证会被安排在 2002 年 12 月的第一周,当时,在巴尔的摩市下了 近三年来最大的一场雪。整个城市几乎都停止运转。但是弗雷德里克·摩兹法官 坚持要求开庭,并且要求几十位与案件有关的律师到场出席;据审判时一位目击 者说,为了保证早上能够到庭,法官他自己在会议室中睡了一晚。 几周后,也就是 2002 年 12 月 23 日,摩兹法官发布了那份长达 42 页的判决书, 他裁定微软公司必须在其 Windows 操作系统和 IE 中发布与其竞争的 Java 编程 语言。摩兹法官的意见是:在微软的垄断下,Java 拥有一个并不健全的市场, 比如说,大部分 PC 上所安装的 Java 软件要么就是旧版本,要么就是仅适用于 Windows 的版本,这使得其它软件开发者对 Java 平台产生了厌恶的情绪,这些 都是因为微软反竞争行为的结果,看来微软已经利用 Windows 的垄断地位来破坏 SUN 对 Java 的销售渠道。树立市场正义的唯一方法是纠正微软的所作所为,"阻 止微软从它过去的错误中获得将来的利益!" 针尖对麦芒的斗争一直在继续„„ 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 和解:2004 年 4 月 2 日,两者达成和解协议微软将向 Sun 赔付 20 亿美元以消解 旧怨,他们开始共同应对来自 IBM 和 Linux 的挑战。 从上面的故事中可能有同学认为 Sun 是正义的,微软是非正义的,是这样的吗? 我们可以再看看下面的故事„„ 3、Oracle 和 Google 针对 Java 的再次对决 事实上,不止微软一家意识到 Java 是座金矿。Oracle 是第二家从 Sun 手中购买 Java 许可证的公司,而 IBM 甚至比 Sun 更早的意识到 Java 在企业级应用方面的 价值,在对 Java 支持上投入了巨大的精力,我们平时编写 Java 程序使用的 Eclipse IDE 集成编程环境,就是 IBM 主导开发、用以争夺 Java 领导权的重大 举措(有空可以细讲 IBM 和 Sun 的恩恩怨怨,从 Eclipse 这个名字就可以看到其 中的火药味)。 “和谐”的阴影: IBM 和 Intel 为了争夺 Java 的话语权,向 Sun 发出了新一轮的挑战,2005 年他 们支持 Apache 开源社区发起了一个叫做 Harmony 的项目,Harmony 有个有趣的 中文意思–和谐。 Harmony 的目的有两个: 1、在 Apache Licence v2 的许可之下,独立的(不阅读 Sun JDK 的源代码, 仅仅根据 Java SE 5 specification)开发一个与 Java SE 兼容的 JDK。 2、通过 Harmony 的开发社区,创建一个模块化的架构(包括虚拟机和类库)。 该架构允许所有的独立开发项目可以共享运行时组件。 简单的说,Harmony 就是让其他公司可以使用它来绕开 SUN JDK 的商业限制。Sun 为了保持自己对 Java 的主导权,坚决不给 Harmony 颁发 JDK 认证。 这让开源社区 Apache 和 SUN 发生了决裂„„ “太阳”的终结: Sun 创造了 Sparc、Solaris、Java 等伟大的产品,Sun 曾经风光无限,市值估价 2000 亿美金。Sun 预测到网络就是计算机,可是真正的网络时代到来时,它却没 有真正调整过来,不断的亏损和决策失误让它举步维艰。 2009 年 4 月 Oracle 宣布以 74 亿美金收购 Sun,2010 年 1 月欧盟决定无条件同 意这项收购,一个伟大公司就这样走到生命的尽头。 Java 的创造者 James Gosling 在自己的博客贴出了一幅画,并写了一句话 So long, old friend„ (再见了,老朋友) 7 欧零博客地址:http://www.cnblogs.com/ouling/ 墓前站的是 Linux 的吉祥物 Tux 塔克斯和 Java 的吉祥物 Duke 杜克) 虽然 Sun 已经离去,Java 还会继续前行„„ “机器人”的小伎俩: 1998 年 Sun 的共同创始人 Andy Bechtolsheim 给了斯坦福大学的两个学生一笔 10 万美金的天使投资,他们成立了一个小公司名字叫——Google。 2007 年 11 月 5 日,已经成长为互联网领域内巨人的 Google 发布了一个叫做 Android 的手机操作系统平台。Android 采用我们上面提到的 Harmony 来作为 JDK (Java 开发工具包)的替代品,使用 Dalivk 虚拟机来替代 JVM(Java 虚拟机), 它这次从头至尾都没有说它用的是 Java,可是所有的 Java 程序员都懂这就是 Java „„ Oracle 对 Google 的诉讼: 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 2010 年 8 月 12 日,Java 专利权的新主人 Oracle 指控 Google 在 Android 开发中 “故意,直接并反复侵犯 Oracle 的 Java 相关的知识产权”,新一轮的斗争正在 继续。 三、Java 的版本演进 1995 年 5 月 23 日 Java 语言发布 1996 年 1 月,JDK1.0 诞生, 4 月,10 个最主要的操作系统供应商申明将在其产品中嵌入 JAVA 技术, 9 月,约 8.3 万个网页应用了 JAVA 技术来制作 1997 年 2 月 18 日 JDK1.1 发布 1998 年 12 月 4 日 JDK1.2(Java2)发布 随后,SUN 公司发布 Java 的三个版本:标准版(J2SE)、企业版(J2EE)和微型版(J2ME), 为 Java 今后的方向指明了道路。 2000 年 5 月 8 日 J2SE1.3 发布 2000 年 5 月 29 日 J2SE1.4 发布 2004 年 9 月 30 日 J2SE1.5 发布,成为 Java 语言发展史上的又一里程碑。 为了表示该版本的重要性,J2SE1.5 更名为 Java SE 5.0 2005 年 6 月 Java SE 6.0 发布。J2EE 更名为 Java EE,J2SE 更名为 Java SE,J2ME 更名为 Java ME 四、Java JDK 安装和编程环境搭建 接下来我们就开始学习这个改变了世界的编程语言。我们学打扑克牌需要先买一 副扑克牌,然后打开牌盒先认认牌。学习一门语言的第一步就是搭建它的编程环 境,然后写一个简单的 Java 程序的例子,简单了解一下。最后看看别人都用这 个语言都弄出来些什么好玩的程序。 1、JDK 的下载(Windows 环境): JDK 的意思是 Java Development Kit ,直译就是 Java 开发套件。JDK 是我们学 习 Java 必备工具。 我们可以从 Oracle 的网站可以下载最新版的 JDK。 a、访问 http://www.oracle.com/technetwork/java/javase/downloads/index.html 点 击 Download JDK 按钮 9 欧零博客地址:http://www.cnblogs.com/ouling/ b、在出来的页面中点击 Download 按钮 c、在下面的页面中选择 Platform 为 Windows,然后点击 Continue 按钮 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 安装过程我就不演示了,只需要建议的是,可以把 JDK 安装在类似 “C:\javasoft” 的目录中,而不是 c:\program fileI 下。 d、在接下来的页面里点击 带下划线的下载链接即可 2、JDK 的安装: a、下面介绍一下安装步骤,双击下载回来的 jdk-6u21-windows-i586.exe,点 击下一步 11 欧零博客地址:http://www.cnblogs.com/ouling/ b、更改安装位置为 c:\javasoft\jdk1.6.0_21 c、稍等片刻 《Android 学习指南》博客地址:http://android.yaohuiji.com/about d、在选择 JRE(Java Runtime Environment Java 运行时环境)安装目录步骤时可 以直接点下一步。 e、安装 jre 的过程中竟然有个 OOo(OpenOfficeOrganization)的广告 13 欧零博客地址:http://www.cnblogs.com/ouling/ f、再稍等一下就可以看到成功安装的提示 g、最后打开命令行窗口敲一个命令检查是否运行正常 java –version ,这是 个查看 Java 版本号的命令,我们这次安装的是 JDK 1.6 的 第 21 个更新包,所 以看到的反馈应该是这样: 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 好吧,恭喜你完成了最重要的一步 ^_^ 。 3、环境变量 环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数,比如临 时文件夹位置和系统文件夹位置等。Java 的运行环境需要配置如下几个参数, JAVA_HOME,classpath 和 Path,下面简单叙述一下: 在“我的电脑”上点右键,在随后的菜单中选属性,可以看到系统属性选项卡, 右下角就是“环境变量”按钮。 15 欧零博客地址:http://www.cnblogs.com/ouling/ 点击“新建„”:变量名写 JAVA_HOME,变量值写 c:\javasoft\jdk1.6.0_21, 这样就配置好了 JAVA_HOME,这个变量时其他 JAVA 程序约定俗称的寻找 Java 安 装目录的依据。 找到变量 Path,点击“编辑„”,在最前端加入“%JAVA_HOME%\bin;” 这样, 我们就可以在任何路径下使用 java 和 javac 命令了。 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 找到 classpath,如果没有则新建一个。变量值为: “.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar” ,留意一下最前 面的”.”,它代表当前目录。classpath 的作用是让 java 虚拟机能够找到 class 文件的位置。 17 欧零博客地址:http://www.cnblogs.com/ouling/ 配置完成后需要检测一下,运行 cmd,敲命令 javac –version , 如果不报错, 就说明配置成功了。 4、创建、编译和运行一个 Java 文件 a、我们建立一个目录,今后所有的实验都将在这里进行 “c:\workspace\java” b、在 java 目录下建立今天课程的目录 lesson01,记住目录名小写 c、创建 Lesson01.java :建立一个名字叫 Lesson01.java 的文件,右键用 Editplus 打开(不会使用 EditPlus 的请自行搜索和学习使用,很简单不用怕)。 在文件中写入: 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 1 class Lesson01{ 2} 上面的代码你可以理解成:有个东西叫 Lesson01 d、编译 Lesson01.java : 在命令行窗口(不了解命令行窗口的同学可以自行搜 索并学习)中敲入如下命令 javac Lesson01.java 如果成功的话会没有任何提示,并且会出现一个名字叫 Lesson01.class 的文 件,生成 class 的过程就是编译的过程。 19 欧零博客地址:http://www.cnblogs.com/ouling/ e、运行 Lesson01.class :在命令行窗口中敲入如下命令, 1 java Lesson01 提示如下图所示:意思是“在主线程中产生了一个叫做没有 main 这个方法的错 误” 我们下面就来尝试修正这个错误,重新编辑 Lesson01.java ,内容如下: 1 class Lesson01{ 2 public static void main(String[] args){ 3 System.out.println("欢迎进入 Java 的世界!"); 4} 5} 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 再次编译、运行,这次看到将是: 恭喜你成功完成了一个 Java 程序从创建、编译到运行的全过程,Java 的世界就 此向你展开。 六、Java 虚拟机和 Java 程序运行原理 我们在命令行中运行一下 java –version ,看看结果: 21 欧零博客地址:http://www.cnblogs.com/ouling/ 注意它的反馈信息实际上有 3 行,分别是 java sdk 的版本,JRE 的版本和 JVM 的版本, 而 Java HotSpot Client Virtual Machine 就是 Sun 公司开发的 Java 虚拟机。 那么什么是 Java 虚拟机? Java 虚拟机(JVM)是可运行 Java 代码的假想计算机。只要根据 JVM 规格描述将 解释器移植到特定的计算机上,就能保证经过编译的任何 Java 代码能够在该系 统上运行。 Java 程序的运行过程大致就是先吧 Java 源文件(后缀是.java 的文件)编译成 (.class)文件,然后再运行 class 文件。 七、Java 程序演示(Demo) Java 可以做些什么,同学们可以先到 java 安装目录的 demo 子目录 (C:\javasoft\jdk1.6.0_21\demo)中看一看,这就是个本节课的作业,大家去 寻宝吧。 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 最后送大家一架钢琴,JavaSoundDemo.jar 这个小程序是我从 Mac OS 自带的 JDK 包目录里发现的,双击即可运行,鼠标放上去就会有音乐飘出,尽情的演奏吧^_^ 23 欧零博客地址:http://www.cnblogs.com/ouling/ 这个文件我打包源文件里了,不用向我单独要。 本讲就到这里,Take your time and enjoy it 。 《Android 学习指南》博客地址:http://android.yaohuiji.com/about Java 基础第二讲:Java 基本语法(一) 本讲内容:对象、标识符、关键字、变量、常量、字面值、基本数据类型、整数、 浮点数、布尔型、字符型、赋值、注释 Java 作为一门语言,必然有他的语法规则。学习编程语言的关键之一就是学好 语法规则,写作合乎语法规则的语句,控制计算机完成各种任务。而按编程语言 的语法规则写成的,完成某项功能的代码集合就可以叫做程序。 一、初识对象(Object): “初识对象的时候我们还不知道什么是对象。” Java 的一个重要特点就是面向对象(Object Oriented), 面向对象是相对于 面向过程(Process Oriented)来说的。 我们用一个从冰箱中取一杯牛奶的例子来说明面向过程和面向对象的区别。 先用面向过程的思路描述这个过程: 25 欧零博客地址:http://www.cnblogs.com/ouling/ 再用面向对象的思路描述这个过程: 我们把冰箱作为一个对象的时候,问题变得异常简单,冰箱有一个方法就是取牛 奶的方法,你调用这个方法,这个方法的返回值就是一杯牛奶。那么现实生活中 有这样智能的冰箱吗?有的,找个人站冰箱旁边就行了,把那个人和冰箱合起来 包装成一个对象它就是智能冰箱对象了^_^ 。 面向对象的编程语言把所有事物都看成对象:万事万物皆对象。 Java 的程序就是一些对象的集合,这些对象通过调用彼此的方法与其他对象交 互。每个对象都属于某种有一个类或者接口定义的类型。 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 二、标识符(Indentifier) Java 语言中的类名、对象名、方法名、常量名等等这些 Java 组件都需要起个名 字,在而这些组件的名称就被称为标识符(Indentifier)。 合法的标识符具有一些命名规则: 1. 必须以字母、美元符号或下划线开头。数字不能开头 2. 第一个字符之后可以是任意长度的包含数字、字母、美元符号、下划线的 任意组合。 3. 不能使用 Java 关键字和保留字做标识符 4. 标识符是大小写敏感的,Z 和 z 是两个不同的标识符。 5. Unicode 字符会被视为普通字母对待。 针对最后一条,连 SCJP(Sun Ceritified Java Programmer)考试也不会要求, 但是你要了解,这样是可以的: public class Lesson02{ public static void main(String[] args){ String 世界 = "阿凡达!"; System.out.println(世界); } } 上面的代码回成功的打印出“阿凡达”字样。 三、关键字(keywords) 和所有的编程语言一样,Java 具有一组内置的关键字,这些关键字绝对不能用 来做为标识符。Java SE6 里一共有 50 个关键字(keywords): 27 欧零博客地址:http://www.cnblogs.com/ouling/ abstract assert boolean break byte case catch char class const continue default do double else enum extentds final finally float for goto if implements import instanceof int interface long native new package private protected public return short static strictfp super switch synchronized this throw throws transient 瞬间 try void volatile while 这些单词有共同的特点是:全是小写的,不能用作标识符。(如果非要用做标识 符呢,会有什么事情发生?自己去试试看)。其中 instanceof 是 instance of 的连写 ,strictfp 是 strict float point 的连写 有三个看起来像是关键字,其实不是关键的东东,他们是字面值(literal), 字面值的概念下面立刻就会讲到。 true 布尔字面值 false 布尔字面值 null 空值字面值 四、变量(Variable)初识 所谓变量,就是值可以被改变的量。定义一个变量的时候不需要什么特殊的关键 字修饰。 这个变量的概念很接近数学里变量的概念,举个例子: public class Lesson02{ public static void main(String[] args){ String myName ="nabula"; myName = "nebulayao"; System.out.println(myName); } } 上述程序的打印结果大家想必能猜出来,是“nebulayao”。 五、常量(named constant、constant)初识 所谓常量,就是它的值不允许改变的量。要声明一个常量,就要用关键字 final 修饰,常量按照 Java 命名规范需要用全部大写,单词之间用下划线隔开: // 游戏方向设定 北 南 东 西 final int NORTH = 1; final int SOUTH = 2; 《Android 学习指南》博客地址:http://android.yaohuiji.com/about final int EAST = 3; final int WEST = 4; // 三种游戏元素 final int RED_STAR = 1; final int YELLOW_STAR = 2; final int GREEN_STAR = 3; 那么我真改了会怎么样呢?我们一起看看 public class Lesson02{ public static void main(String[] args){ final int SOUTH =2; SOUTH =1; } } 上面的代码,如果去编译的话会报下面的提示: 如果是在 Eclipse 等集成编程环境中写的话,会直接有红色错误标识出现: 29 欧零博客地址:http://www.cnblogs.com/ouling/ 六、字面值(literal、literal value) literal 这个单词被翻译了好多种说法、字面值、字面量、直接量、常值、文本 符等等,我们需要知道这些翻译都是一个意思,就是编程语言里一些内建数据类 型的源代码表示。譬如: 42 false 3.1415 'b' //整数字面值 //布尔字面值 //double 字面值 //char 字面值 七、基本数据类型(Primitive Type) Java 中数据类型(Data Type)分为基本数据类型(Primitive Type)和引用数 据类型(Reference Data Type)。 Java 中基本数据类型有八种: 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 1、整数(integer data type) Java 语言中使用 3 中表示整数的方法,分别是十进制、八进制和十六进制,平 时我们使用 10 进制,有时候也会用 16 进制,仅仅会因为趣味才会去用八进制。 下面演示一下三种整型表示法: public class Lesson02{ public static void main(String[] args){ int i=10; //十进制直接写 int j=012; //八进制前面加 0,八进制用 0-7 表示 int k=0xa; //十六进制前面加 0x 或者 0X,16 进制用 0-9 a-f 表示,为什么用 a-f,因为我们没有发明过 10-16 的数字符号,这里的 a-f x 大 小写都可以 int cafe = 0xcafe; //你觉得这个咖啡会等于几? System.out.println(i); System.out.println(j); System.out.println(k); System.out.println(cafe); } } 类型 字节 范围 举例 byte 1 -128 ~ 127 125 short 2 -32768 ~ 32767 20000 int 4 -2147483648 ~ 2147483647 123456789,2123456789 31 欧零博客地址:http://www.cnblogs.com/ouling/ long 8 -9223372036854775808~9223372036854775807 9876543210L 整数里的默认类型是 int,也就是说你写一个整数字面值如果不明确指定他的类 型,那么他一定是 int 类型。 想明确声明一个整数字面值是长整型的话,需要使用 l 或 L 做后缀。 public class Lesson02{ public static void main(String[] args){ byte a= 127; short b= 128; int c=2123456789; long d=9876543210l; long e=9876543210L; System.out.println("a="+a+" b="+b +" c="+c+" d="+d+" e="+e); } } 2、浮点数(floating-point data type) 类型 字节 范围 举例 float 4 1.4E-45 ~ 3.4028235E38 3.1415f double 8 4.9E-324 ~ 1.7976931348623157E308 3.1415,3.1415d public class Lesson02{ public static void main(String[] args){ float a= 3.1415926f; double b= 3.1415926; float c=2123456789; float d=9876543210L; double e=9876543210L; System.out.println("a="+a+" b="+b +" c="+c+" d="+d+" e="+e); System.out.println(Float.MIN_VALUE); System.out.println(Float.MAX_VALUE); System.out.println(Double.MIN_VALUE); System.out.println(Double.MAX_VALUE); } } 如果你想定义一个单精度浮点数字面量,那么你必须加 f 或 F 做后缀。 浮点数当然也可以存整数在里面。 3、布尔型(boolean) 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 布尔型表达一个真或假,是或否的意思。在 Java 中使用 boolean 关键字来声明 一个变量为布尔类型,在 Java 中布尔字面值只有 2 个:true 和 false。注意是 全小写的。 public class Lesson02{ public static void main(String[] args){ boolean flag =true; flag= false; System.out.println("flag="+flag); } } 4、字符型(char) Java 里用一个单引号内的单个字符来表示一个字符字面值。 类型 字节 范围 举例 char 2 字节,16 位 ‘\u0000’ ~ ‘犇’,'\u004e’,’n’,23002 ‘\uFFFF’ public class Lesson02{ public static void main(String[] args){ char a= 'a'; //ascii 字符可以 char b='1'; char c= '犇'; //汉字也可以 char d='\u004e'; //其实上所有的 Unicode 字符都可以 char e='\n'; //转义符表示的字符也可以 char f=65535; //也可以,因为 char 存储的时候占两个字节, 因为他不是整数,所以不需要符号位,因此它的最大值就是 65535 了。 char g=23002; char h=36745; System.out.println("a="+a+" b="+b +" c="+c+" d="+d+" e="+e+" f="+f+" g="+g+" h="+h); } } 上面的例子,如果文件以 utf-8 编码存储,那么编译时会报错, 33 欧零博客地址:http://www.cnblogs.com/ouling/ 因为命令行方式它人默认编码集是 GBK,所以编译时请特别指出编码集为 utf-8, 方法如下: 额外作业:有兴趣的同学可以写一个算式,把自己的名字打印出来,如果不会话 可以再学几讲再做,记住有这个事儿就行 八、赋值(assignment)初识 “=” 是个赋值运算符,我们讲运算符之前先把赋值运算符先行提出来讲一讲。 他和数学里的用法一样,都是把右侧的值赋予左侧的变量。 下面用例子演示一下三种赋值方式: public class Lesson02{ public static void main(String[] args){ int a=1; int b=1,c=1; int d=a=b=c=2; System.out.println("a="+a+" b="+b +" c="+c+" d="+d); } } 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 九、注释(annotation)初识 程序中的注释是程序设计者与程序阅读者之间沟通的手段,是写给程序员看的代 码。通常情况下编译器会忽略注释部分,不做语法检查。 1. 好的注释可以改善软件的可读性,可以让开发人员更快理解新的代码。 2. 好的注释可以最大限度的提高团队开发的合作效率。 3. 长期的注释习惯可以锻炼出更加严谨的思维能力。 Java 中的注释有三种: // 注释一行 /* 注释若干行 */ /** 注释若干行,并写入 javadoc 文档 */ 好了,本讲就到这里,下一讲我们讲基本类型的相互转换和赋值,Take your time and enjoy it. 35 欧零博客地址:http://www.cnblogs.com/ouling/ Java 基础第三讲:Java 基本语法(二) 本讲内容: 基本数据类型之间的转换、引用数据类型、字符串 一、基本数据类型之间的转换 所谓数据类型转换就是将变量从当前的数据类型转换为其它数据类型,类型转换 在 Java 里是个很严肃的事情^_^ ,我们现在讲解基本数据类型之间的转换。 先搞定布尔类型,布尔类型无法和其它基本数据类型之间发生任何方式的转换。 数字型的基本数据类型之间可以通过下面两种方式实现转换。 1、自动类型转换 我们把所有的基本数据按照取值范围从小到大排个序,就得到了下图: a、当把一个取值范围小的基本数据类型赋值给一个取值范围大的基本数据类型 时,Java 编译器会帮你完成类型转换。 b、还有一个秘密:“字面值的整数永远隐含着的意思是它是个 int”,当把一 个在 byte 允许的取值范围的 int 字面值赋予一个 byte 类型的变量时,编译器会 自动帮你完成。 《Android 学习指南》博客地址:http://android.yaohuiji.com/about public class Lesson03_1{ public static void main(String[] args){ byte byte1=100; short short1=10000; char char1= 23002; int int1= 1000000; long long1= 9876543210L; float float1= 9876543210.12345f; double double1= 9876543210.123456789; short short2=byte1; // byte byte2=short1; } } 上面的例子中 byte 能转换成 short,short 不能转换成 byte,这就是自动类型转 换,自动类型转换不会损失精度。那么现在的问题是上面的程序如果强行编译会 怎么样,大家看下图: 看到没,编译器说了,把 short 转成 byte 可能会损失精度!那么把 10000 存入 byte 会的确会损失精度,那么把值是 100 的 short 类型数字存入 byte 肯定不会 损失精度,那么编译器会怎么看待这件事情?我们问去采访一下它,改程序成这 样: public class Lesson03_2{ public static void main(String[] args){ short short1=100; byte byte1=short1; } } 37 运行之后: 欧零博客地址:http://www.cnblogs.com/ouling/ 我们看到编译器并不买账,我们看到编译器只对整数字面值的大小有感知,对这 种从大到小的类型转换是坚决不允许的,那么如何解决这个问题,就要用到后面 讲的强制类型转换。 2、强制类型转换 我们把上面的程序再改一下: public class Lesson03_2{ public static void main(String[] args){ short short1=100; byte byte1=(byte)short1; System.out.print(byte1); } } 我们发现这一次成功了,加一个括号,里面写一上要转换的类型,就搞定了。强 制类型转换果然很强! 强制类型转换的副作用也是有的,那就是的确可能导致精度丢失(数字不准确): public class Lesson03_2{ public static void main(String[] args){ short short1=250; byte byte1=(byte)short1; System.out.print(byte1); } } 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 这段代码的打印值是-6,我想这是大家都不愿意看到的情形。了解了强制类型转 换的特点我们使用的时候就会特别留意,自然就不会出问题^_^。 二、引用数据类型(Reference Data Type) 和基本数据类型对应的就是引用数据类型,引用数据类型包括类应用、接口引用 和数组引用。因为这三个东西大家还都没有学,所以这里只是让他们和大家见个 面,混个脸熟,直接上代码: import java.util.List; public class Lesson03_3{ //对象 static Object object; //接口 static List list =null; //数组 static int[] months; public static void main(String[] args){ System.out.println("object="+object); System.out.println("list="+list); System.out.println("months="+months); } } 查看运行结果: 我们看到引用数据类型中接口和对象的类型名都是首字母大写的,数组是带有方 括号的。他们和基本数据类型有着明显的不同。 39 欧零博客地址:http://www.cnblogs.com/ouling/ 用一句话来说就是基本数据类型变量里存储的是数据本身,而引用数据类型变量 里存储的是指向数据本身的地址。 三、字符串初识 字符串是程序开发中最常用的一种对象。关于对象我们有过一点点的了解,关于 字符串我们也先啊和他见见面,以后会慢慢熟悉起来的。下面我们用一个字符串 来举例理解引用数据类型在内存中的存储情况: public class Lesson03_4{ public static void main(String[] args){ String name1="nebulayao"; String name2=null; name2=name1; System.out.println("name2="+name2); } } 查看运行结果,打印的结果是 name2=nebulayao 。上述程序用图形来表示的话就 是这样: 好了,本讲就到这里,take your time and enjoy it 。 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 哦,回来说一句,除了整数字面值的默认类型 int 以外,你是不是也自己发现了 一个小秘密?那就是浮点数字面值的默认类型是 double 。如果你不知道的话, 做题会吃亏的^_^,出题人太坏了。 41 欧零博客地址:http://www.cnblogs.com/ouling/ Java 基础第四讲:Java 基本语法(三) 本讲内容: Java 中的运算符 前言:运算符 operator Java 运算符从一个或多个操作数产生新的值。而操作数指的是位于运算符左边 或者右边的内容。 Java operators produce new values from one or more operands (just so we’re all clear, remember the operands are the things on the right or left side of the operator). 一、赋值运算符 Assignment Operators 其实 Java 里有 12 种赋值运算符,“=”只是最常见的一种,下面我们讲解的四 个常用复合赋值运算符(+=、-=、*=、/=) public class Lesson04_1 { public static void main(String[] args){ int x=10; int y=10; y=y-6; x=x+2*5; System.out.println("x="+x); System.out.println("y="+y); x=10; y=10; y-=6; x+=2*5; System.out.println("x="+x); System.out.println("y="+y); 《Android 学习指南》博客地址:http://android.yaohuiji.com/about x=10; x*=2+5; System.out.println("x="+x); } } 我们可以看到上面两段代码的效果是等同的,下面的代码的等同的代码是 x=x*(2+5),为什么不是乘法优先呢?是因为*=还是赋值运算符,赋值运算符的优 先级是最低的。关于运算符优先级我们最后会有个总结。 最后把 11 种复合赋值运算符都列出来: 运算符 += -= *= /= %= &= |= ^= <<= >>= >>>= 举例 a += b a -= b a *= b a /= b a %= b a &= b a |= b a ^= b a <<= b a >>= b a >>>= b 等效表达式 a = a+b a = a-b a = a*b a = a/b a = a%b a = a&b a = a|b a = a^b a = a<>b a = a>>>b 二、关系运算符 Relational Operators 关系运算符用于比较操作数之间的关系,关系运算符总是产生一个布尔值(true 或 false)。 运算符 > < == != >= <= 功能 举例 大于 ‘a’>’b’ 小于 2<3.0 等于 ‘x’==88 不等于 true!=true 大于或等于 6.6>=8.9 小于或者等于 ‘M’<=88 运算结果 false true true flase flase true 可运算类型 整数、浮点数、字符 整数、浮点数、字符 任意 任意 整数、浮点数、字符 整数、浮点数、字符 当在两个字符类型数据之间比较或者把字符类型数据和整数浮点数类型比较时, Java 将字符的 Unicode 值当做数值与其它数值相比较。 相等性运算符 “Equality” Operators == 和 != 这两个关系运算符比较的是两个相似事物的是否相等。注意他们无法 比较两个不兼容的类型,什么叫无法比较?就是编译器会直接报错。 43 欧零博客地址:http://www.cnblogs.com/ouling/ 对于整数和浮点数之间的相等性比较如果他们的值相等,那么就返回 true。对 于引用类型的变量之间的比较,是看他们是否引用了同一个对象,如果变量的位 相等那么他们就是相等的。 import java.util.Date; public class Lesson04_2 { public static void main(String[] args){ System.out.println("'a'=='a' ? " + ('a'=='a')); System.out.println("'a'=='b' ? " + ('a'=='b')); System.out.println("5.0==5L ? " + (5.0==5L)); System.out.println("true==true ? " + (true==true)); Date date1 = new Date(); Date date2 = new Date(); Date date3=date1; boolean b1=date1==date2; boolean b2=date1==date3; System.out.println("date1="+date1.getTime()); System.out.println("date2="+date2.getTime()); System.out.println("b1="+b1); System.out.println("b2="+b2); } } 看一下运行结果: 三、instanceof 运算符 instanceof Comparison 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 因为你们还没有正式学过对象,所以这一节,等学过对象之后请再回来看一遍。 instanceof 运算符只值能用于比较对象引用变量,可以检查对象是否是某种类 型。这里的类型是指类、接口类型数组。或者说 instanceof 检查的是运算符左 边的变量引用的对象是否能通过右边类型或者接口的 IS-A 测试。 import java.util.Date; public class Lesson04_3 { public interface Foo{} public class A implements Foo{} public class B extends A{}; public void instance(){ A a = new A(); B b= new B(); Foo f= new A(); System.out.println(a instanceof Foo); System.out.println(b instanceof A); System.out.println(b instanceof Foo); System.out.println(f instanceof A); System.out.println(f instanceof B); } public static void main(String[] args){ Lesson04_3 lesson = new Lesson04_3(); lesson.instance(); } } 运行结果如下: 45 欧零博客地址:http://www.cnblogs.com/ouling/ 四、算术运算符 Arithmetic Operators 基本算术运算符: +加 –减 *乘 /除 求余运算符: % 递增和递减运算符: ++ – ,可以作为缀或后缀 下面举例说明: public class Lesson04_4 { public static void main(String[] args){ //求余运算 int a=10; int b=7; System.out.println(a%b); //自增运算 double x = 2; double y= 1.2; double z= x++ + ++y; System.out.println(z); } } 运行的结果是: 五、条件运算符 Conditional Operator 条件运算符根据条件来返回一个值。计算问号左边表达式的值,值为真是提供冒 号左边的操作数为返回值,值为假时提供冒号右边的操作数为返回值。这是 Java 里唯一的一个三元运算符,为了让你记忆深刻,我做了一张图: 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 其中括号可以省略。 public class Lesson04_5 { public static void main(String[] args){ int numOfPets =3; String status = (numOfPets < 4)?"宠物数量刚刚好":"宠物 数量太多了"; System.out.println(status); } } 编译和运行结果是: 六、逻辑运算符 Logical Operator 逻辑运算符只对布尔型操作数进行运算并返回一个布尔型数据。一共有 6 个逻辑 运算符:&& , || ,& , | ,!和 ^ 短路逻辑运算符 Short-Circuit Logical Operators: public class Lesson04_6 { public static void main(String[] args) { int i = 5; // 短路与运算符&&,要求左右两个表达式都为 true 时才返回 true,如果左边第一个表达式为 false 时,它立刻就返回 false,就好像短路了 一样立刻返回,省去了一些无谓的计算时间。 boolean flag = (i < 3) && (i < 4); System.out.println(flag); 47 欧零博客地址:http://www.cnblogs.com/ouling/ // 短路或运算符||,要求左右两个表达式有一个为 true 时就 返回 true,如果左边第一个表达式为 true 时,它立刻就返回 true,就好像短路 了一样立刻返回,省去了一些无谓的计算时间。 flag = (i > 4) || (i > 3); System.out.println(flag); } } 编译和运行结果是: 非短路逻辑运算符 Not Short-Circuit Operators: public class Lesson04_6 { public static void main(String[] args) { int i = 5; // 非短路与运算符&,要求左右两个表达式都为 true 时才返 回 true,两个表达式他都会计算 boolean flag = (i < 3) & (i < 4); System.out.println(flag); // 非短路或运算符|,要求左右两个表达式有一个为 true 时 就返回 true,两个表达式他都会计算 flag = (i > 4) | (i > 3); System.out.println(flag); } } 编译和运行结果是: 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 逻辑运算符 Logical Operators ^ and ! : ^ 异或运算符 XOR : 要使一个^表达式为真,必须有且只有一个操作数为真。 public class Lesson04_6 { public static void main(String[] args) { System.out.println("xor ((2<3)^(3<4)) :" + ((2<3)^(3<4))); System.out.println("xor ((2<3)^(3>4)) :" + ((2<3)^(3>4))); System.out.println("xor ((2>3)^(3>4)) :" + ((2>3)^(3>4))); } } 编译和运行结果是: 七、位运算符 Bitwise Operators 位运算符是对整数操作数以二进制的每一位进行操作,返回结果也是一个整数。 逻辑位运算符分别是 按位取反 ~ 、按位与 & 、按位或 | 和 按位异或 ^ 49 欧零博客地址:http://www.cnblogs.com/ouling/ 移位运算符有 左移<< 、 右移>> 、 从 SCJP 考试的 5.0 版本以后不再对此项知识点有任何涉及,果断决定不放在讲 座里讲,而是放在番外篇里提一提。 八、运算符优先级 优先级 1 2 3 4 5 6 7 8 9 10 11 12 13 14 运算符 () [] . ! +(正) -(负) ~ ++ – */% +(加) -(减) << >> >>> < <= > >= instanceof == != &(按位与) ^ | && || ?: = += -= *= /= %= &= |= ^= ~= <<= >>= >>>= 优先级是上面的高下面的低,用括号来解决优先级不清晰的部分是个良好的编程 习惯。 好了本讲就到这里,Take your time and enjoy it 。 《Android 学习指南》博客地址:http://android.yaohuiji.com/about Java 基础第五讲:流程控制(一) 本讲内容: 分支语句 流程控制 Flow Control : 流程控制语句是编程语言中的核心之一。可以分为 分支语句、循环语句和跳转 语句。 本讲内容包括分支语句的 if-else 和 switch , 它们又被称为判决语句 (decision statements),意思是根据某种条件做出朝哪个方向前进的判断。 一、if-else 分支控制语句 ( if-else Branching ) 1、最简单的 if 语句 假设我到办公室里问黄文强在不在?如果他在的话会说在,不在的话一般情况是 没人说话了。我们用程序模拟一下: public class Lesson06_1 { public static void main(String[] args) { //设置黄文强在 boolean flag = true; System.out.println("开始"); if (flag){ System.out.println("在"); } System.out.println("结束"); 51 欧零博客地址:http://www.cnblogs.com/ouling/ } } 为了把分支语句的前后界定清楚,我加了开始和结束标识。上面的运行结果是: 2、最简单的 if-else 语句 假设我到办公室里问黄文强在不在?如果他在的话会说在,不在的时候有热心同 事回答了一句“他不在”,那我就不立刻明白了。我们用程序模拟一下: public class Lesson06_1 { public static void main(String[] args) { //设置黄文强不在 boolean flag = false; System.out.println("开始"); if (flag){ System.out.println("在"); }else{ System.out.println("他不在"); } System.out.println("结束"); } } 上面的运行结果是: 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 3、简单的 if – else if 语句 好吧,如果黄文强不在的话,我想问问刘克强在不在?恰好,刘克强在,那么用 程序模拟是这样的: public class Lesson06_1 { public static void main(String[] args) { // 设置黄文强不在 boolean flag1 = false; // 设置刘克强在 boolean flag2 = true; System.out.println("开始->"); if (flag1) { System.out.println("黄文强在"); } else if (flag2) { System.out.println("刘克强在"); } System.out.println("->结束"); } } 上面的运行结果是: 53 欧零博客地址:http://www.cnblogs.com/ouling/ 4、复合 if- else if – else 语句 如果刘克强也不在,那么用程序模拟是这样的: public class Lesson06_1 { public static void main(String[] args) { // 设置黄文强不在 boolean flag1 = false; // 设置刘克强在 boolean flag2 = true; System.out.println("开始->"); if (flag1) { System.out.println("黄文强在"); } else if (flag2) { System.out.println("刘克强在"); } else { System.out.println("他们不在"); } System.out.println("->结束"); } } 上面的运行结果是: 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 5、if-else 语句规则: 1. if 后的括号不能省略,括号里表达式的值最终必须返回的是布尔值 2. 如果条件体内只有一条语句需要执行,那么 if 后面的大括号可以省略, 但这是一种极为不好的编程习惯。 3. 对于给定的 if,else 语句是可选的,else if 语句也是可选的 4. else 和 else if 同时出现时,else 必须出现在 else if 之后 5. 如果有多条 else if 语句同时出现,那么如果有一条 else if 语句的表 达式测试成功,那么会忽略掉其他所有 else if 和 else 分支。 6. 如果出现多个 if,只有一个 else 的情形,else 子句归属于最内层的 if 语句 6、实例练习: 下面我们看一个例子,(请注意这个例子的写法是不被推荐的,这里这么写是为 了讲解 if-else 的规则) public class Lesson06 { public static void main(String[] args) { boolean examIsDone = true; int score = 65; if (examIsDone) if (score >= 90)System.out.println("A ,Excellent"); else if (score >= 80) System.out.println("B ,Good"); else if (score >= 70) System.out.println("C ,Middle"); else if (score >= 60) System.out.println("D ,Pass"); else System.out.println("E ,Fail"); System.out.println("Done is Done"); } 55 欧零博客地址:http://www.cnblogs.com/ouling/ } 你认为 else 是属于哪个 if 语句的?System.out.println(“Done is Done”); 是在哪一行代码之后执行的? 二、分支控制语句 switch Statement Java 中有一个和 if 语句比较相似的分支控制语句叫 switch ,它在有一系列固 定值做分支时使用效率要比 if-else 方式效率高(别急,等一下再告诉你为什么 效率高)。 先看一个例子:假设我们不考虑闰年的话,我们如何知道一个月有多少天?先用 if-else 的方式来实现: public class Lesson06_2 { public static void main(String[] args) { int month=9; if(month==1){ System.out.println(month+"月有 31 天"); }else if(month==2){ System.out.println(month+"月有 28 天"); }else if(month==3){ System.out.println(month+"月有 31 天"); }else if(month==4){ System.out.println(month+"月有 30 天"); }else if(month==5){ System.out.println(month+"月有 31 天"); }else if(month==6){ System.out.println(month+"月有 30 天"); }else if(month==7){ System.out.println(month+"月有 31 天"); }else if(month==8){ System.out.println(month+"月有 31 天"); }else if(month==9){ System.out.println(month+"月有 30 天"); }else if(month==10){ System.out.println(month+"月有 31 天"); }else if(month==11){ System.out.println(month+"月有 30 天"); }else if(month==12){ System.out.println(month+"月有 31 天"); }else{ System.out.println("没有这个月份吧"); } } 《Android 学习指南》博客地址:http://android.yaohuiji.com/about } 接下来我们使用 switch 语句重新实现一次: public class Lesson06_4 { public static void main(String[] args) { int month = 9; switch (month) { case 1: System.out.println(month + "月有 31 天"); break; case 2: System.out.println(month + "月有 28 天"); break; case 3: System.out.println(month + "月有 31 天"); break; case 4: System.out.println(month + "月有 30 天"); break; case 5: System.out.println(month + "月有 31 天"); break; case 6: System.out.println(month + "月有 30 天"); break; case 7: System.out.println(month + "月有 31 天"); break; case 8: System.out.println(month + "月有 31 天"); break; case 9: System.out.println(month + "月有 30 天"); break; case 10: System.out.println(month + "月有 31 天"); break; case 11: System.out.println(month + "月有 30 天"); break; case 12: System.out.println(month + "月有 31 天"); break; 57 欧零博客地址:http://www.cnblogs.com/ouling/ default: System.out.println("没有这个月份吧"); break; } } } 运行 2 个程序,结果都是 9 月有 30 天。 从简洁程度和效率上,switch 都略胜一筹(上面的 2 个程序,把输出语句屏蔽掉, 各自循环运行 10 亿次,switch 快了 6636 毫秒,也就是 6 秒钟)。 为什么会快一些呢,因为 switch 是在编译时优化的。运行时进行的不是变量的 比较运算,而是直接跳转。举个例子 7×9 你是反射式的出来的结果,9×7 你需 要稍稍楞一小下,99×7 你就需要计算一下了。这就是他们之间的区别。如果你 觉得举例子不科学,改天我们再找虚拟机一起聊这个话题^_^ 好了,我们还是先学习一下 switch 都有什么使用规则吧: 1、留意 switch 格式的写法 标准且合法的格式: 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 非法格式说明: switch(x){ case 0 {} } switch(x){ 0:{} 1:{} } 能看到哪里错了吧,第一个没冒号,第二个没 case 关键字 2、switch 表达式必须能被求值成 char byte short int 或 enum 记忆的话可以记忆成非 lang 整形加枚举。请切记在 java 6 及以前版本中 string 是不允许用在 switch 表达式中的(Java 7 可以用 string,现在正式版还没出来) 3、case 常量必须是编译时常量,因为 case 的参数必须在编译时解析,也就是 说必须是字面量或者是在声明时就赋值的 final 变量。 这个不太好理解,举个例子看看: public class Lesson06_5 { public static void main(String[] args) { 59 欧零博客地址:http://www.cnblogs.com/ouling/ final int a = 2; final int b; b = 3; int x =2; switch(x){ case 1: //编译 OK case a: //编译 OK case b: //无法通过编译 } } } 编译一下看看: 4、case 常量被会转换成 switch 表达式的类型,如果类型不匹配也会出错: public class Lesson06_5 { public static void main(String[] args) { byte x =2; switch(x){ case 1: //编译 OK case 128: //无法自动转换成 byte,编译器会报错 } } } 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 5、多个 case 常量重复也会出错: public class Lesson06_5 { public static void main(String[] args) { byte x =2; switch(x){ case 1: //编译 OK case 1: } } } 编译一下看看: 6、匹配的 case 的语句是入口而不是独立分支: public class Lesson06_5 { public static void main(String[] args) { int x = 1; switch (x) { case 1:System.out.println("周一"); 61 欧零博客地址:http://www.cnblogs.com/ouling/ case 2:System.out.println("周二"); case 3:System.out.println("周三"); case 4:System.out.println("周四"); case 5:System.out.println("周五"); case 6:System.out.println("周六"); case 7:System.out.println("周日"); default:System.out.println("这个是星期几啊"); } } } switch 代码是找到一个匹配的 case 常量入口,然后自顶向下执行的。也就是英 文里的 Fall-Through,你可以形象的理解他的执行方式是自由落体式的一股脑 碰到啥就执行啥。 7、break:在 switch 语句块中,执行到 break 关键词时,立刻退出 switch 语 句块,转到 switch 的下一条语句。 我们结合 6、7 两点规则来重写一次,判断月份天数的例子: public class Lesson06_3 { public static void main(String[] args) { int month = 9; switch (month) { case 1: case 3: case 5: case 7: case 8: case 10: 《Android 学习指南》博客地址:http://android.yaohuiji.com/about case 12: System.out.println(month + "月有 31 天"); break; case 2: System.out.println(month + "月有 28 天"); break; case 4: case 6: case 9: case 11: System.out.println(month + "月有 30 天"); break; default: System.out.println("没有这个月份吧"); break; } } } 运行程序: 8、default :当 switch 中所有的 case 常量都不匹配时,会执行 default 分支: 再把上面判断星期几的代码改一下: public class Lesson06_5 { public static void main(String[] args) { int x = 8; switch (x) { case 1:System.out.println("周一"); default:System.out.println("这个是星期几啊"); case 2:System.out.println("周二"); case 3:System.out.println("周三"); case 4:System.out.println("周四"); case 5:System.out.println("周五"); 63 } } 运行程序: 欧零博客地址:http://www.cnblogs.com/ouling/ break; case 6:System.out.println("周六"); case 7:System.out.println("周日"); } 我们故意写了星期 8,所以触发了 default 分支,可是因为没有使用 break 语句, 所以又直落执行了。于是出现了这个看起来有点奇怪,其实很正常的输出。 好了,到这里我们就把 switch 讲完了。大家要记住的是当程序中有很多需要判 断的成点状数据需要分别处理时优先采用 switch,如果遇到区间判断时,不用思 考,还是用 if-else 吧。 对编程基础知识的理解会随着你的编程经验不断加深,不要奢望一次理解有多到 位,淡定,淡定。 本讲就到这里,Take your time and enjoy it . 《Android 学习指南》博客地址:http://android.yaohuiji.com/about Java 基础第六讲:流程控制(二) 本讲内容:循环、跳出循环、标签跳转 Java 中循环有三种形式 while 循环、do-while 循环 和 for 循环。其中从 Java 6 开始 for 循环又分 普通 for 循环 和 for-each 循环两种,我们接下来分别讲 解。 一、while 循环 当条件为真时执行 while 循环,一直到条件为假时再退出循环体,如果第一次条 件表达式就是假,那么 while 循环将被忽略,如果条件表达式一直为真,那么 while 循环将一直执行。关于 while 括号后的表达式,要求和 if 语句一样需要 返回一个布尔值,用作判断是否进入循环的条件。 public class Lesson06_6 { public static void main(String[] args) { int x = 8; while (x > 0) { System.out.println(x); x--; } } } 执行结果: 65 欧零博客地址:http://www.cnblogs.com/ouling/ 如果你把 x>0 改成大于 8 ,while 循环将一次都不执行 二、do-while 循环 好,如果你无论如何都想执行一次循环体内的代码,可以选择 do-while 循环, 它的特点是做了再说。 public class Lesson06_6 { public static void main(String[] args) { int x = 8; do{ System.out.println(x); x--; }while(x>8); } } x=8,条件是大于 8,查看运行结果,我们发现他总是会执行一次。 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 三、for 循环 当知道可以循环多少次时,是使用 for 循环的最佳时机。 1、基本 for 循环: 先举一个例子: public class Lesson06_6 { public static void main(String[] args) { for (int i = 2, j = 1; j < 10; j++) { if (j >= i) { System.out.println(i + "x" + j + "=" + i * j); } } } } 这个例子打印了从九九乘法表的一部分: 67 欧零博客地址:http://www.cnblogs.com/ouling/ 原谅我没有从最常用的 for 循环开始写,你把 int i=2 写在 for 循环前面就变 成最常用的 for 循环了。 下面说一下 for 循环的规则: 1. for 循环的三个部分任意部分都可以省略,最简单的 for 循环就是这样的 for(;;){ } 2. 中间的条件表达式必须返回一个布尔值,用来作为是否进行循环的判断 依据 3. 初始化语句可以由初始化多个变量,多个变量之间可以用逗号隔开,这 些在 for 循环中声明的变量作用范围就只在 for 循环内部 4. 最后的迭代语句可以是 i++,j++ 这样的表达式,也可以是毫无干系的 System.out.println(“哈哈”) 之类的语句,它照样在循环体执行完毕 之后被执行。 2、for-each 循环: for-each 循环又叫增强型 for 循环,它用来遍历数组和集合中的元素,因此我 们会在数组一章和集合一章里分别讲到,放心,你会掌握的很好。 这里举个例子给你看看先: public class Lesson06_6 { public static void main(String[] args) { int[] a = { 6, 2, 3, 8 }; for (int n : a) { System.out.println(n); } } } 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 运行结果如下: 四、跳出循环 break 、continue break 关键字用来终止循环或 switch 语句,continue 关键字用来终止循环的当 前迭代。当存在多层循环时,不带标签的 break 和 continue 只能终止离它所在 的最内层循环,如果需要终止它所在的较外层的循环则必须用,标签标注外层的 循环,并使用 break 和 continue 带标签的形式予以明确标示。 先看一个不带标签的例子 BreakAndContinue.java: public class BreakAndContinue { public static void main(String[] args) { int i =0; while(true){ System.out.println("i="+i); if(i==12){ i++; i++; continue; } i++; if(i==20){ break; } } } } 69 欧零博客地址:http://www.cnblogs.com/ouling/ 好例子自己会说话,这个例子打印了从 1 到 20 中除去 13 的数字。我们只需要看 明白这个例子的输出结果就能明白 break 和 continue 的区别了。 编译并运行代码,查看结果: 我们再看一个 break 带标签的例子: public class BreakAndContinue { public static void main(String[] args) { boolean isTrue = true; outer: for(int i=0;i<5;i++){ while(isTrue){ System.out.println("Hello"); break outer; } System.out.println("Outer loop."); } System.out.println("Good Bye!"); 《Android 学习指南》博客地址:http://android.yaohuiji.com/about } } 编译并运行程序,查看结果: 把上面的例子中 break 替换成 continue,再次编译和运行,查看结果: ok,本讲就到这里,take your time and enjoy it. 71 欧零博客地址:http://www.cnblogs.com/ouling/ Java 基础第七讲:面向对象基础(一) 本讲内容:面向对象的概念和发展、面向对象的特征 一、面向对象(Object Oriented)编程语言的历史 1950 年有个叫做荷兰德的学生作为程序员进入 IBM 的时候,这个世界上的程序 员只有几个而已。当时计算机很少,计算机性能也差,程序员也少,加上程序员 都是天才中的天才,智商超高,所以他们用十六进制的机器编码来操纵计算机, 似乎没有什么问题。 1960 年,计算机性能不断提升,应用领域也不断增多,程序员的人数也在增多, 程序的复杂程度也不断提高,很多程序需要好多人一起才能完成。而在此时在大 型项目中由于软件的原因导致的大量问题也不断暴露出来。由此催生了结构化程 序设计方法。结构化程序设计思想采取了模块分解和功能抽象的方法,把一个个 复杂的问题,分解成一个个易于控制的子程序,便于开发和维护,因此结构化程 序设计迅速走红,并从 70 年代起逐渐占据统治地位。 70 年代末,随着计算机科学的发展,结构化程序设计方法也渐渐显得力不从心。 于是面向对象设计思路和语言慢慢浮出水面。 1967 年挪威两个科学家发布了 simula 语言(simulation 模拟、仿真),它引入 了后来所有面向对象程序设计语言都会遵循的几个基础概念:类、对象、继承。 虽然因为 simula 比较难懂、难学,功能不完善而没有流行开来,但是它的思想 却指导着计算机这数十年的编程实践。 1972 年诞生的 smalltalk,被公认为是历史上第二个面向对象的程序设计语言, 和第一个真正的集成开发环境(IDE),smalltalk 对 Java 、Objective-C 、 Ruby 的诞生都起到了极大的推动作用。90 年代的许多软件开发思想,如设计模 式、敏捷编程和重构等也都源自于 smalltalk。在 smalltalk 里所有的东西都是 对象,15*19 会被理解成向 15 这个对象发送一个乘法的消息,参数是 19。 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 1985 年 c++商业版本的正式发布,标志着一个面向对象领域里的王者诞生了。C++ 在 c 语言的基础上,借鉴了 simula 中类的概念、从 algol 语言中继承了运算符 重载、引用以及在任何地方都可以声明变量的能力,从 BCPL 获得了//注释,从 Ada 语言中得到了模板,命名空间,从 Ada、Clu 和 ML 去取得了异常„„C++是 第一个广泛流行起来的面向对象的编程语言,至今魅力不减。 1995 年 Java 诞生的故事大家都耳熟能详了,我们也知道 Java 是 C++的语法与 Smalltalk 语义的结合。由此面向对象领域里又一个王者诞生了。 Java 里面向对象的概念六次讲座也就可以讲完。不要怕,这些概念很好理解; 不要轻视,很多深层的思想需要在实践中不断思考和分析才可以领悟。 二、类和对象的概念 1、类和对象的概念 人类自古就喜欢听故事,也喜欢写故事,我们从小也被要求写作文,为了帮助你 写作文。老师还总结了一些规律,譬如记叙文六要素:时间、地点、人物、起因、 经过、结果。 有了这样指导性的东西,我们写作文的时候就简单了许多。 面向对象程序语言的核心思想就是把一个事物的状态和行为封装起来作为一个 整体看待。类描述的就是对象知道知道什么和执行什么。 譬如我们用面向对象的思想来看待一架飞机: 如果我们站在顾客角度看飞机,那么它的状态是名字波音 777,座位数 380 人, 飞行速度 940 公里每小时,它的行为就是飞行,能把你从 A 地送到 B 地。 如果站在航空公司角度看飞机,那么它的状态是名字波音 777,资产编号 HNHK20100321,购买价格 18.7 亿人民币。它的行为就是能赚钱。 我们从不同角度去看待和抽象同一架飞机它的状态和行为不相同。 再从面向对象的角度看待一个家乐福超市的员工王丽: 她在上班的时候是个收银员,那么她的状态是编号 067,她的行为就是收银。她 下班以后去家门口的小店买菜,那么他的身份就是顾客,她的状态是有个购物商 品清单,她的行为就是付款。 73 欧零博客地址:http://www.cnblogs.com/ouling/ 我们从不同的角度和时间去看待同一个人,她的状态和行为也是不相同的,甚至 看起来是相反的。 好了,我们自己尝试分析一下,电脑的状态和行为,手机的状态和行为,桌子的 状态和行为,QQ 的状态和行为,小狗、小猫、老虎、大象、蚊子、苍蝇„„ 有 一个简单的方法区别什么是状态什么是行为:就是状态是个名词,行为是个动词。 2、类和对象的关系 类是对象的蓝图,它告诉虚拟机如何创建某个类型的对象。对象是根据蓝图建造 出来的实例。 譬如我们设计一个模拟 WOW 的格斗游戏,需要人或者怪兽来战斗吧,战斗需要武 器吧。那么圣骑士就是个类,人类圣骑士“锦马超”就是一个对象。如果双手剑 件是个类,那么拿在“锦马超”手里的“霜之哀伤”就是一个对象。 譬如我们要建立一个全班同学的通讯录,设计一个通讯录的格式,包括姓名、性 别、手机号、QQ 号、宿舍号。然后我们按照一定的格式印出来,交由每个同学 填写,那么每个同学填写的那一份就叫对象,我们填写的通讯录格式本身就是类。 譬如由一个寂寞的老人需要找个伴,要求:随时都可以陪着他,还不唠叨。有人 带了一条狗。那么老人提的需求就是蓝图,就是类。狗就是对类的实现,就是对 象。 3、定义类,创建对象 下面我们学习如何用 Java 的程序代码来定义类、创建对象。 定义一个类的步骤是:定义类名,编写类的属性(状态),编写类的方法(行为) public class Dog { // 定义了狗的个头大小的属性 private int size; // 定义设置个头的方法 public void setSize(int size) { if (size > 0 && size < 10) { this.size = size; } else { 《Android 学习指南》博客地址:http://android.yaohuiji.com/about size = 1; } } // 定义获取个头的方法 public int getSize() { return size; } // 定义狗叫的方法 public void bark(){ if(size<5){ System.out.println("汪汪汪!"); }else{ System.out.println("嗷!嗷!"); } } //定义 main 方法 public static void main(String[] args) { //创建了名字叫小黄的狗对象 Dog xiaoHang = new Dog(); //设置它的大小属性 xiaoHang.setSize(3); //调用它的叫方法 xiaoHang.bark(); //创建了名字叫大黄的狗对象 Dog daHang = new Dog(); //设置它的大小属性 daHang.setSize(7); //调用它的叫方法 daHang.bark(); } } 运行程序查看运行结果: 75 欧零博客地址:http://www.cnblogs.com/ouling/ 三、面向对象的三大特性 封装、继承、多态是面向对象的三大特性。这里先让大家有个概念,通过今后漫 长的学习过程不断加深对它们的理解。 封装(encapsulation):就是把属性私有化,提供公共方法访问私有对象。 这里 面有两层意思,第一隐藏数据,第二把数据和对数据操作的方法绑定。 实现封装的步骤: 1 修改属性的可见性来限制对属性的访问。 2 为每个属性创建一对赋值方法和取值方法,用于对这些属性的访问。 3 在赋值和取值方法中,加入对属性的存取限制。 封装的优点: 1 隐藏类的实现细节; 2 可加入控制逻辑,限制对属性的不合理操作; 3 便于修改,增强代码的可维护性; 这里举一个智能冰箱的例子(可乐灌装工厂也可)。 public class Lesson07 { private String 牛奶="一瓶牛奶"; public String 得到牛奶(){ System.out.println("给出了"+牛奶); return 牛奶; 《Android 学习指南》博客地址:http://android.yaohuiji.com/about } public void 设置牛奶(String s){ this.牛奶=s; } public static void main(String[] args) { Lesson07 lesson = new Lesson07(); lesson.得到牛奶(); lesson.设置牛奶("一罐牛奶"); lesson.得到牛奶(); } } 继承(inheritance):同类事物之间有它的共同性也有各自的独特性,我们把共 同的部分抽离出来,就可以得到使用这些事物的一般性的类,我们在把那些具有 特殊性的共同点再次抽象就可到了一些具有特殊性的类。而特殊类拥有一般类所 具有的一般性属性和方法,也拥有自己特有的某些属性和方法。我们把特殊类和 一般类之间的关系叫做继承。 举个例子:马儿都有四条腿,马儿都有会跑,我们把这些共同性抽象出来就成了 马类;而其中有一些马是白色的马,还有一些是黑色的马,我们把这些特殊性也 分别抽象出来,就成了白马类和黑马类。那么白马类和马类之间的关系就是继承 关系。它们是父子关系,马类是夫类、白马类是子类。 继承简化了人们对事物的认识和描述,清晰的体现了相关类间的层次关系。 继承达到了功能抽象、继承促进了代码复用、继承也带来了多态性。 这里先对继承有个概念,下面还会有详细的讲解。 77 欧零博客地址:http://www.cnblogs.com/ouling/ 多态(polymorphism):多态就是“一种定义、多种实现” 。Java 中可以把一个 子类的对象赋给一个父类的引用,这就出现了多态。这里先对多态有个概念,下 面还会有详细的讲解。 这一讲里我们迈入了对象的世界。学习了类和对象的概念,并对面向对象的特点 有了初步的了解,从下一讲开始将近距离接触它们。 好,本讲就到这里。 《Android 学习指南》博客地址:http://android.yaohuiji.com/about Java 基础第八讲:面向对象基础(二) 本讲内容:成员变量、方法、方法的重载、构造函数 一、用程序讲解小白的故事 小白是一条狗,它心情好的时候会恭喜人发财,它心情差的时候会对路人撒野, 吓得路人落荒而逃。下面我们用面向对象的方式用程序讲述一下小白的故事。 public class Dog { //构造函数 public Dog(){ size =3; } //定义叫声常量 final String BARK_NORMAL = "汪!汪汪!"; final String BARK_HAPPY = "旺!旺旺!"; final String BARK_SAD = "呜„„嗷!"; //定义心情常量 static final int NORMAL =0; static final int HAPPY =1; static final int SAD = 2; // 定义了狗的个头大小的属性 private int size; // 定义获取个头的方法 public int getSize() { return size; } // 定义狗叫的方法 public void bark(){ if(size<5){ System.out.println("汪汪汪!"); }else{ System.out.println("嗷!嗷!"); } 79 欧零博客地址:http://www.cnblogs.com/ouling/ } //定义狗叫的方法,带心情参数 public void bark(int mood){ switch(mood){ case NORMAL: System.out.println(BARK_NORMAL); break; case HAPPY: System.out.println(BARK_HAPPY); break; case SAD: System.out.println(BARK_SAD); break; } } //定义 main 方法 public static void main(String[] args) { //创建了名字叫小白的狗对象 Dog xiaoBai = new Dog(); //调用它叫的方法 xiaoBai.bark(); //调用带参数的方法 xiaoBai.bark(HAPPY); } } 运行程序,看一下输出结果: 二、类定义中的五个顶级成员(top-level member) 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 实体 成员变量 初始化块 构造方法 方法 接口 类 static 修饰 没用 static 修饰 类变量 实例变量 静态初始化块 实例初始化块 / 构造方法 类方法 实例方法 嵌套的接口 member interface/ 嵌套顶层类 nested top-level内部成员类 inner member class class 三、成员变量(类或对象的状态) 1、认识成员变量(类或对象的状态)、类变量、实例变量、局部变量、方法参 数之间的区别 成员变量(field)是没有定义在代码块(包括初始化块、成员方法)中的变量。 成员变量是类变量还是实例变量取决于在其声明中是否使用了 static 关键字。 类变量在声明是用了 static 关键字,它的另一个名字叫静态变量、静态成员变 量(static field) 。 实例变量是在声明时没有使用 static 关键字的成员变量,它的另一个名字叫非 静态成员变量(non-static field)。 定义在代码块里的变量被称为局部变量(local variable)。 定义在方法声明中的变量叫方法参数。 public class Lesson08 { // 类变量 static String s1 = "类变量"; // 实例变量 String s2 = "实例变量"; // 初始化代码块里的局部变量 { String s3 = "初始化代码块里的局部变量"; System.out.println(s3); } // 静态初始化代码块里的局部变量 static { String s4 = "静态初始化代码块里的局部变量"; System.out.println(s4); 81 欧零博客地址:http://www.cnblogs.com/ouling/ } // 方法的参数和方法里的局部变量 public void printString(String s5) { String s6 = "方法里的局部变量"; System.out.println("方法的参数:"+s5); System.out.println(s6); } // 类方法 public static void printString() { String s7="类方法里的局部变量"; System.out.println(s7); } // main 方法 public static void main(String[] args) { //调用类方法 Lesson08.printString(); //打印类变量 System.out.println(s1); //创建对象 Lesson08 lesson = new Lesson08(); //打印实例变量 System.out.println(lesson.s2); //调用实例方法 lesson.printString("参数的值"); } } 对于他们之间的区别,我们在以后的学习中你会越来越清晰的。 2、变量的初始化 实例变量一经定义就会有初始值,局部变量定义时不赋初值而直接使用,编译器 会报错 public class Lesson08_1 { 《Android 学习指南》博客地址:http://android.yaohuiji.com/about int i; static int j; { int k; System.out.println(k); } static { int l; System.out.println(l); } public void print(String m){ System.out.println(m); } // main 方法 public static void main(String[] args) { int n; System.out.println(n); Lesson08_1 lesson =new Lesson08_1(); lesson.print("m"); } } 运行程序,查看结果: 83 欧零博客地址:http://www.cnblogs.com/ouling/ 然后我们再给局部变量都附上初值,再把实例变量和类变量都打印出来看看,代 码如下: public class Lesson08_1 { int i; static int j; { int k=2; System.out.println(k); } static { int l=2; System.out.println(l); } public void print(String m){ System.out.println(m); } // main 方法 public static void main(String[] args) { System.out.println(j); int n=2; System.out.println(n); Lesson08_1 lesson =new Lesson08_1(); lesson.print("m"); System.out.println(lesson.i); } } 运行程序,查看结果: 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 我们看到类变量和实例变量没赋值照样有值打印出来,我们也看到 int 的初始值 是0 。 实例变量和类变量的类型 整数 浮点类型 字符类型 布尔类型 boolean 引用数据类型(譬如数组、接口、类) 初始值 0 0.0 ‘/u0000′ false null 四、方法(类或对象的行为) 1、方法 Java 中类的行为由类的成员方法来实现。类的成员方法由方法的声明和方法体 两部分组成。 修饰符,可选,用于指定谁有权限访问此方法。 返回值类型,必选,用于指定该方法的返回值数据类型;如果该方法没有返回值, 则要用关键字 void 进行标示。方法的返回值只能有一个。 参数列表,可以有 0 到多个,多个参数之间要用逗号隔开,参数的写法形如: String[] args, int age 这样。 85 欧零博客地址:http://www.cnblogs.com/ouling/ 方法名,必选,这个„„,好吧命名规则是方法名和变量名的首字母要小写,别 丢我人,弄个大写方法名出来。 方法体,可选,这个„„, 大括号,大括号不写的方法叫抽象方法。 2、属性和方法之间的关系 有句绕口令是这么说的:“状态影响行为,行为影响状态”。你有没有想过这问 题,如果每个对象都是从同一个类中生成出来,每个对象如果都一摸一样,那么 这个世界是不是太无趣了。好在,我们看到前面的例子中,小狗的大小属性影响 了他叫的方式。通过设置狗大小的方法又改变了它的状态。这些属性和方法的细 节上的不同导致了,多姿多彩的对象,我们后面还会讲到更多的技术,也会导致 更多的多样性。 五、方法重载 overload Java 里可以提供同一个方法的多个不同参数的版本供我们调用,譬如上面的小 白,它叫 bark() 的方法有两种,一种是很随意的叫,无拘无束的叫,还有一种 是根据它心情的不同来叫,当然我还可以再定义一个方法可以让他根据主人的脸 色来叫,我们也可以再定义一个方法,穿的参数是食物,那么它的叫声可能就是 边吃边幸福的吼叫了„„ 这样一个 bark 方法就带来了丰富多彩的变化。 在 Java 中允许类定义中多个方法的方法名相同,只要它们的参数声明不同即 可。这种情况下,该方法就被称为重载(overloaded ),这种方式就叫做方法 重载(method overloading )。方法重载是实现程序多样性的一个重要手段。 也可以称作多态的一种表现方式。 重载规则: 1. 重载方法必须改变方法参数列表 2. 重载方法可以改变返回类型 3. 重载方法可以改变访问修饰符 4. 重载方法可以声明新的或更广的检验异常 5. 方法能够在同一个类或者一个子类中被重载 《Android 学习指南》博客地址:http://android.yaohuiji.com/about public class lesson08_2 { static long max(long a,long b){ System.out.println("max(long a,long b)"); return a>b?a:b; } static long max(long a,int b){ System.out.println("max(long a,int b)"); return a>b?a:b; } static int max(int a,int b){ System.out.println("max(int a,int b)"); return a>b?a:b; } static byte max(byte a,byte b){ System.out.println("max(byte a,byte b)"); return a>b?a:b; } public static void main(String[] args) { byte byte1 = 125; byte byte2 = 126; int int1 = 1; int int2 = 2; long long1 = 1000; long long2 = 2000; System.out.println(max(byte1,byte2)); System.out.println(max(int1,int2)); System.out.println(max(byte1,int2)); System.out.println(max(int1,long2)); System.out.println(max(long1,int2)); System.out.println(max(long1,long2)); } } 上面的例子说明了参数声明不同的含义,那就是只要参数的个数,类型和顺序任 意一项不同就算不同的参数声明,即使它们看起来很相似,甚至看起来可能会让 虚拟机搞混。不过没关系,虚拟机很聪明,只要你按照规则走他就能分清。 六、构造函数 87 欧零博客地址:http://www.cnblogs.com/ouling/ 在 Java 中,对象是构造出来的,特意用了一个 new 关键字来标示这个创建的过 程。 我们把上一讲的例子修改一下,看看创建对象的过程发生了什么。 public class Dog { // 定义了狗的个头大小的属性 private int size=3; public Dog(int size){ System.out.println("带参数的构造函数"); this.size = size; } public Dog(){ System.out.println("不带参数的构造函数"); this.size=2; } // 定义狗叫的方法 public void bark(){ if(size<5){ System.out.println("汪汪汪!"); }else{ System.out.println("嗷!嗷!"); } } //定义 main 方法 public static void main(String[] args) { //创建了名字叫小黄的狗对象 Dog xiaoHang = new Dog(4); //调用它的叫方法 xiaoHang.bark(); //创建了名字叫大黄的狗对象 Dog daHang = new Dog(6); 《Android 学习指南》博客地址:http://android.yaohuiji.com/about //调用它的叫方法 daHang.bark(); //创建了名字叫小黑的狗对象 Dog xiaoHei = new Dog(); //调用它的叫方法 xiaoHei.bark(); } } 我们看到创建对象的过程就是执行构造函数的过程,而且也看到构造方法也可以 重载。 我们在这里明确的是说明构造函数或者说构造方法,它不是方法。它们之间的三 大区别,为了让你记清楚,我做了个图: 关于构造方法,还有一部分非常有趣的内容我们放在继承章节和大家一起分享。 本讲就到这里,各位再见,Take some time and enjoy it 。 89 欧零博客地址:http://www.cnblogs.com/ouling/ Java 基础第九讲:面向对象基础(三) 本讲内容:继承、变量隐藏、方法重写、包、修饰符、this、super 一、继承 1、继承的概念 继承是面向对象的三大特性之一。在语义上继承的意思是照法律或遵照遗嘱接受 死者的财产、头衔、地位等,在 Java 程序中的继承也有这个意思,不过子类继 承的是父类的属性和方法。 2、继承的语法结构(子类的定义方式) 3、继承的例子: 关于继承我们第七讲举了一个白马和马的例子,我们再举一个动物父类和鸟类子 类、鱼类子类的例子,这次我们用类图表示。 当我们写好了一个动物类,我们写鸟类的时候就可以继承动物类,自动获得动物 类所拥有的属性和方法,提高了代码重用性。 4、Object 类 《Android 学习指南》博客地址:http://android.yaohuiji.com/about Java 中的所有对象(类)都是 Object 类的子类。我们可以用 javap 查看一下一个 最简单的类的字节码: public class Lesson09 { } 5、继承的原则: 1. 子类能够继承父类中被声明为 public 和 protected 的成员变量和成员方 法。 2. 子类能够继承在同一个包中的默认修饰符修饰的成员变量和成员方法。 3. 如果子类声明了一个与父类变量同名的成员变量,则子类不能继承父类的 成员变量,这种做法叫做变量的隐藏。 4. 如果子类声明了一个与父类方法同名的成员方法,则子类不能继承父类的 成员方法,这种做法方法的重写。 二、包 package 1、编译单元(compilation unit) 在 Java 里,一个编译单元就是一个用来书写 Java 源代码的文本文件。我们前面 讲类的定义的时候只关注了类内部的东西,类外面是不是也有东西?答案是肯定 的。编译单元有三个组成部分: 91 欧零博客地址:http://www.cnblogs.com/ouling/ 这三个部分都是可选的,包声明如果要有必须写在最前面,并且只能写一份。导 入声明可以写多个,类声明也可以写多个。 2、包的概念(package) 类名是类之间彼此区分的标示,一个程序中类数量增多是,必然会遇到类名冲突 的情形。包提供了类的组织和管理方式。包的用途有以下三种: 1. 将功能相近的类放在同一个包里,方便使用和查找 2. 类名相同的文件可以放在不同的包里而不会产生冲突 3. 可以依据包设定访问权限 3、包的声明 4、 包的例子: //包的声明 package android.java.basic; //导入声明 import java.util.Date; //类声明 class Animal{ long birthTime = new Date().getTime(); void eat(){ System.out.println("eating"); } } //类声明 class Fish extends Animal { void swim(){ System.out.println("swimming"); 《Android 学习指南》博客地址:http://android.yaohuiji.com/about } } //类声明 public class Lesson09 { public static void main(String[] args){ //动物类 Animal a = new Animal(); a.eat(); System.out.println(a.birthTime); //鱼类 Fish f = new Fish(); f.eat(); f.swim(); System.out.println(f.birthTime); } } 运行程序,查看结果: 四、访问修饰符 public protected 默认的 private 在 Java 中可以用访问修饰符来控制类或者类成员对程序的其它部分的可见性, 从而在语言级别实现访问控制。当一个类无权访问另一个类或者类的成员时,编 译器会提示你试图访问一些可能不存在的内容。 看见性 public 从同一个类 是 从同一个包中的任何类 是 从同一个包中的子类 是 从包外的子类 是 从包外的任何非子类的 是 protected 是 是 是 是,通过继承 否 默认 是 是 是 否 否 private 是 否 否 否 否 93 欧零博客地址:http://www.cnblogs.com/ouling/ 类 1. 对于类的修饰符,只能有 2 个选择,用 public 修饰或者不用(不用就是 默认修饰符)。 2. 如果一个类本身对另一个类不可见,则即使将其成员声明为 public,也 没有一个成员是可见的,只有当你确类本身对你是可见的时,查看其各个 成员的访问级别才有意义。 3. 对于类的成员(member, 包括属性和方法),可以用 public protected 默 认的和 private 4 种修饰符。 4. 永远不要用访问修饰符修饰局部变量,编译器会毫不留情的报错。(记住: 局部变量只有一个修饰符可以用,那就是 final) 除了访问修饰符外,还有非访问修饰符 static、final、abstract、transient、 synchronization、native、strictfy ,我们在今后的学习中逐步掌握。 五、变量隐藏(shadowing)、方法重写(Overiding) 当子类继承父类时,子类中一不小心就会定义出父类名字相同的成员变量,对于 这种现象,规则里是怎么说的,又是怎么应用的?用一句话说,就是子类成员会 覆盖父类成员;对于变量就是变量隐藏,对于方法就是方法重写(方法覆盖)。 1、变量隐藏 shadow 在做名词时意思是阴影,在做动词时意思是遮蔽,那么这里的意思 shadowing 更多的是遮蔽的意思,不过我们翻译的时候大家已经习惯说这个叫变 量的隐藏。 先看一个局部变量遮蔽成员变量的例子: public class Lesson09_1 { int i=1; int j=1; int k=1; void test(int i){ int j=2; System.out.println("i="+i); System.out.println("j="+j); System.out.println("k="+k); } public static void main(String[] args){ Lesson09_1 lesson = new Lesson09_1(); lesson.test(2); 《Android 学习指南》博客地址:http://android.yaohuiji.com/about } } 我们可以看到,当方法内的局部和成员变量名字相同时,在方法内,局部变量遮 蔽住了成员变量,因此打印出来的是 2,而不是 1。 再看一个子类成员变量遮蔽父类成员变量的例子。 public class WhiteHorse extends Horse { private static String color ="白色"; public static int leg =4; public static void main(String[] args){ WhiteHorse xiaobai = new WhiteHorse(); System.out.println(xiaobai.color); System.out.println(xiaobai.leg); //类变量是遮蔽不住的 System.out.println(Horse.color); //强制转换后我们看到父类的实体 leg 变量还在,只是被隐藏 了 Horse xiaobai1 = (Horse)xiaobai; System.out.println(xiaobai1.leg); } } 运行程序,查看结果: 95 欧零博客地址:http://www.cnblogs.com/ouling/ 2、方法重写 Override 当子类继承父类时,如果子类方法的签名和父类方法的签名相同时,子类就无法 继承父类的方法,此时子类的方法就覆盖了父类的方法,我们称之为重写。重写 可以定义子类某个行为的特殊性。 譬如动物会喝水,但是猫喝水和人喝水的具体行为就不同。 重写方法的规则如下: 1. 参数列表必须与重写的方法的参数列表完全匹配(方法签名相同)。如果 不匹配,你得到的将是方法重载。 2. 返回类型必须与超类中被重写方法中原先声明的返回类型或其子类型相 同。 3. 访问级别的限制性可以比被重写方法弱,但是访问级别的限制性一定不能 比被重写方法的更严格。 4. 仅当实例方法被子类继承时,它们才能被重写。子类和超类在同一个包内 时,子类可以重写未标示为 private 和 final 的任何超类方法。不同包的 子类只能重写标示为 public 或 protected 的非 final 方法。 5. 无论父类的方法是否抛出某种运行时异常,子类的重写方法都可以抛出任 意类型的运行时异常。 6. 重写方法一定不能抛出比被重写方法声明的检验异常更新或更广的检验 异常,可以抛出更少或更有限的异常。 7. 不能重写标示为 final 的方法。 8. 不能重写标示为 static 的方法。 9. 如果方法不能被继承,那么方法不能被重写。 我们举一个重写的例子: Horse.java 《Android 学习指南》博客地址:http://android.yaohuiji.com/about public class Horse { //给马写个摆 Pose 的方法 public void pose(){ //样子很酷 System.out.println("Cool!"); } } WhiteHorse.java public class WhiteHorse extends Horse { //白马重写了摆 pose 的方法 public void pose(){ //白马更酷一点 System.out.println("Cool!!!!"); } public static void main(String[] args){ WhiteHorse xiaobai = new WhiteHorse(); xiaobai.pose(); } } 运行程序,查看结果: 我们再把白马类中 pose 方法的访问修饰符改成 private 试试看: 97 欧零博客地址:http://www.cnblogs.com/ouling/ 六、this 和 super 1、this.成员变量 当成员变量被局部变量隐藏时想使用成员变量,可以用 this 关键字来访问成员 变量。 public class Lesson09_1 { int i=1; int j=1; int k=1; static int l = 1; void test(int i){ int j=2; int l=2; System.out.println("i="+i); System.out.println("j="+j); System.out.println("k="+k); System.out.println("l="+l); System.out.println("this.i="+this.i); System.out.println("this.j="+this.j); System.out.println("this.k="+this.k); System.out.println("this.l="+this.l); } public static void main(String[] args){ Lesson09_1 lesson = new Lesson09_1(); lesson.test(2); 《Android 学习指南》博客地址:http://android.yaohuiji.com/about } } 运行程序,我们可以看到使用 this 关键字时可以看到被隐藏的成员变量可以正 常访问了。 2、this() 构造函数 在构造方法中可以使用 this() 来引用另一个构造方法。 public class Lesson { private int minute=0; Lesson(){ this(45); } Lesson(int minute){ this.minute = minute; } public static void main(String[] args){ Lesson lesson = new Lesson(); System.out.println(lesson.minute); Lesson lesson2 = new Lesson(30); System.out.println(lesson2.minute); } } 99 欧零博客地址:http://www.cnblogs.com/ouling/ 运行程序查看结果: 我们看到 this(45),的确调用了另外一个带参数的构造方法。需要注意的是 this()必须写在构造方法的第一行。 3、super.成员 当父类的成员变量被隐藏、成员方法被重写(覆盖),此时想使用父类的这些成 员时就要用 super 关键字。我们改一下上面马和白马的例子: Horse.java public class Horse { public int height =120; //给马写个摆 Pose 的方法 public void pose(){ //样子很酷 System.out.println("Cool!"); } } WhiteHorse.java public class WhiteHorse extends Horse { public int height =150; //白马重写了摆 pose 的方法 public void pose(){ //先摆一个马的 pose super.pose(); 《Android 学习指南》博客地址:http://android.yaohuiji.com/about //白马更酷一点 System.out.println("Cool!!!!"); } public void printHeight(){ //打印父类被隐藏的变量 System.out.println(super.height); //打印实例变量 System.out.println(height); } public static void main(String[] args){ WhiteHorse xiaobai = new WhiteHorse(); xiaobai.pose(); xiaobai.printHeight(); } } 运行程序查看结果: 我们看到在子类的方法里可以使用 super 来引用被隐藏的父类变量,被覆盖(重 写)的父类方法。 4、super() 父类构造函数 讲 super()之前,我们先看一下这个例子: Horse.java public class Horse { 101 欧零博客地址:http://www.cnblogs.com/ouling/ public Horse(){ System.out.println("马类的构造函数"); } } WhiteHorse.java public class WhiteHorse extends Horse { public WhiteHorse(){ System.out.println("白马类的构造函数"); } public static void main(String[] args){ new WhiteHorse(); } } 运行程序查看结果: 我们看到,构造白马类之前,虚拟机先构造了它的父类马类,由此我们看到了白 马类能继承马类的属性和方法的根本原因,原来每一个白马类同时也是一个马 类,还是一个 Object 类。在创建对象时,一个对象的逐级父类自顶向下依次都 创建了。 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 上图是一个白马对象在内存中的示意图,我们看到最外面的是 WhiteHorse,内 层还有一个 Horse 对象,更内层还有一个 Object 对象。 用下面的栈上的示意图可以更清晰的看到对象的创建过程。 103 欧零博客地址:http://www.cnblogs.com/ouling/ 首先调用的是 main 方法,main 方法调用 new WhiteHorse() ,WhiteHorse()构造 函数调用了一个默认的 super(),super()方法就是父类的构造方法,以此类推 最后调用了 Object()构造方法。 5、带参数的的 super()方法 在上面的例子里,我们看到编译器在你没有调用 super()方法的时候,插入了一 个默认的 super()方法。可惜的是编译器并不会自动插入带参数的 super(), 因 此我们遇到这种情况就只能自己手工插入对 super()的调用。 下面我们把上面的例子更改一下: Horse.java 的构造函数添加一个参数: public class Horse { protected int leg = 0; public Horse(int leg){ 《Android 学习指南》博客地址:http://android.yaohuiji.com/about this.leg=4; System.out.println("马类的构造函数"); } } 再次编译 WhiteHorse.java,出错提示如下: 标准 Java 编译器的提示有点故作神秘,这个提示几乎什么都没说;我们换个工 具,在中文 Eclipse 上的提示就明显多了: 我们按照它的提示更改一下 WhiteHorse 类: public class WhiteHorse extends Horse { public WhiteHorse(){ super(4); System.out.println("白马类的构造函数"); } public static void main(String[] args){ new WhiteHorse(); } } 再次编译和运行程序,我们发现这次安然通过。 105 欧零博客地址:http://www.cnblogs.com/ouling/ 到这里,我们是不是可以小小总结一下,构造函数只能用 new、this() 和 super() 的方式来访问,是不能像方法一样写方法名访问的。 本讲就到这里,下次再见。 《Android 学习指南》博客地址:http://android.yaohuiji.com/about Java 基础第十讲:面向对象基础(四) 本讲内容:抽象类、初始化块 一、抽象类 用 abstract 修饰的类定义,我们称之为抽象类,抽象类不能被实例化。 用 abstract 修饰的方法,我们称之为抽象方法,抽象方法不能有方法体。 面向对象中,所有的对象都是某一个类的实例,但是并不是每个类都可以实例化 成一个对象。如果一个类中没有足够的信息来描绘一个具体的对象,那么这个类 就不能被实例化,我们称之为抽象类。抽象类用来描述一系列看起来不同,但究 其本质是相同的对象。譬如把苹果、橘子、梨等抽象出来一个概念叫水果,我们 把狗、老鼠、猫、狮子、大象、猪等抽象出来个概念叫动物。这时候我们把动物 抽象成一个 Animal 类时,就最好不要让它直接初始化,创建出一个 Animal()实 例对象的结果似乎难以想象。 抽象类被继承之外,没有用途,没有目的。 下面我们用一个 Test.java 的例子看一下什么叫抽象类: abstract class Animal { abstract void makenoise(); } class Lion extends Animal { @Override void makenoise() { System.out.println("狮子吼!"); } } class Dog extends Animal { @Override void makenoise() { System.out.println("狗叫!"); } } 107 欧零博客地址:http://www.cnblogs.com/ouling/ public class Test { public static void main(String[] args){ Animal a1 = new Dog(); Animal a2 = new Lion(); a1.makenoise(); a2.makenoise(); } } 编译和运行程序,我们看看结果: 这个例子里,我们有这么几点需要留意: 1. 一个编译单元里是可以写多个顶级类的,只要 public 修饰的顶级类只有 一个就行了。 2. 用 abstract 修饰的类是抽象类 3. 用 abstract 修饰的方法是抽象方法,抽象方法没有方法体,也就是说不 能写大括号。 4. 抽象类实际上是定义了一个标准和规范,等着他的子类们去实现,譬如动 物这个抽象类里定义了一个发出声音的抽象方法,它就定义了一个规则, 那就是谁要是动物类的子类,谁就要去实现这个抽象方法。 5. 狗和狮子的类继承了动物这个抽象类,实现了发出声音的方法。 6. 一个对象除了被看成自身的类的实例,也可以被看成它的超类的实例。我 们把一个对象看做超类对象的做法叫做向上转型。譬如 Animal a1 = new Dog(); 7. 虽然都是动物类型,但是方法在运行时是按照它本身的实际类型来执行操 作的。因此 a1.makenoise()执行的是狗叫,a2.makenoise()执行的是狮 子吼,我们称之为运行时多态。 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 我们再看一下把一个类看做一个超类有什么样的损失或者不便,我们把上面的例 子稍微改一下: abstract class Animal { abstract void makenoise(); } class Lion extends Animal { @Override void makenoise() { System.out.println("狮子吼!"); } } class Dog extends Animal { @Override void makenoise() { System.out.println("狗叫!"); } void bark(){ System.out.println("汪,汪!"); } } public class Test { public static void main(String[] args){ Animal a1 = new Dog(); Animal a2 = new Lion(); a1.makenoise(); a2.makenoise(); ((Dog)a1).bark(); } } 运行程序,查看结果: 109 欧零博客地址:http://www.cnblogs.com/ouling/ 我们把焦点放在第 35 行,我们再 a1 前面加了一个(Dog),这个做法的意思是把 a1 强制转换为 Dog 对象,只有转换为 Dog 对象后,才能使用 bark 方法,否则即 使你知道他是一个 Dog 对象也不能调用 bark 方法。这就是子类对象付给超类引 用所带来的不便或者说是损失。 二、初始化块 我们已经知道在类中有两个位置可以放置执行操作的代码,这两个位置是方法和 构造函数。初始化块是第三个可以放置执行操作的位置。当首次加载类(静态初 始化块)或者创建一个实例(实例初始化块)时,就会运行初始化块。 我们看一个例子: class SuperClass{ SuperClass(){ System.out.println("父类 SuperClass 的构造函数"); } } public class Initialize extends SuperClass { Initialize(int x){ System.out.println("带参数的构造函数"); } Initialize(){ System.out.println("不带参数的构造函数"); } static { System.out.println("第一个静态初始化块"); } 《Android 学习指南》博客地址:http://android.yaohuiji.com/about { System.out.println("第一个实例初始化块");} { System.out.println("第二个实例初始化块");} static { System.out.println("第二个静态初始化块"); } public static void main(String[] args){ new Initialize(1); new Initialize(); } } 编译并运行程序,查看结果: 从上面的例子中我们需要留意如下几点: 1. 初始化块的语法相当简单,它没有名称,没有参数,也没有返回值,只有 一个大括号。用 static 修饰的初始化块就要静态初始化块,相对应的, 没有 static 修饰的初始化块就叫实例初始化块。 2. 静态初始化块在首次加载类时会运行一次。 3. 实例初始化块在每次创建对象时会运行一次。 4. 实例初始化块在构造函数的 super()调用之后运行。 111 欧零博客地址:http://www.cnblogs.com/ouling/ 5. 初始化块之间的运行顺序取决于他们在类文件中出现的顺序,出现在前面 的先执行。 6. 初始化块从书写惯例上应该写在靠近类文件的顶部,构造函数附近的某个 位置。 好吧,本讲就到这里。 《Android 学习指南》博客地址:http://android.yaohuiji.com/about Java 基础第十一讲:面向对象基础(五) 本讲内容:接口 一、为什么要有接口 我们已经知道 Java 中只支持单继承,或者说不允许多重继承的出现,又可以说 一个类只能有一个父类。为了提供类似多重继承的功能,Java 提供了接口的功 能,下面我们共同学习一下接口。 我们还是拿一个例子来引入接口的概念吧。 上面是一个高度浓缩过的例子,假设下面的子类数量远远不止 4 种,也假设方法 数来那个也不止 2 个。 首先我们定义了一个动物的类,它有吃和叫的方法,接下来我们想增加一个玩耍 的方法和亲近主人的方法,如果把这两个方法定义在动物类里,看起来确实不合 113 欧零博客地址:http://www.cnblogs.com/ouling/ 理,因为老虎对象也会继承到亲近主人的方法,如果该方法默认实现是用舌头舔 主人的脖子的话,就会产生老虎舔你脖子讨好你的场景,似乎有点太销魂了,这 种设计方式副作用太大。 如果把这两个方法定义在猫狗等需要的类里,这时候又会产生同样的内容重复写 多次的情形,更不可接受。 哎这时候你可能在想如果 Java 里可以多重继承多好啊,我再定义一个宠物类, 在里面定义玩耍和亲近主人的方法,然后让猫狗这些类也去继承宠物类问题不就 解决了?可惜 Java 不允许这么干„„ (关于为什么 Java 中不允许多重继承, 可以参见 Java 番外篇致命方块的诞生)。 好在 Java 里提供了接口的功能,你可以把宠物和动物都定义成接口,让猫狗去 实现这两个接口,也可以把动物定义成一个普通类或者抽象类,让猫狗去继承动 物,再让猫狗去实现宠物接口。 下面我们用代码表达出来。在这里我用中文标识符是为了提高教学效果,请在实 际编程中彻底断绝用中文标识符的想法,别因为猎奇的原因今后开始用中文,还 说是我教你的^_^ class Animal { public void 吃() { System.out.println("吃"); } public void 叫() { System.out.println("叫"); } } interface Pet { public void 玩耍(); public void 讨好主人(); } class Lion extends Animal { } class Tiger extends Animal { } class Cat extends Animal implements Pet{ 《Android 学习指南》博客地址:http://android.yaohuiji.com/about @Override public void 玩耍() { System.out.println("玩耍"); } @Override public void 讨好主人() { System.out.println("舔你脖子(什么嗜好„„)"); } } class Dog extends Animal implements Pet{ @Override public void 玩耍() { System.out.println("玩耍"); } @Override public void 讨好主人() { System.out.println("舔你脖子(狗也这样„„)"); } } public class XiaoBai{ public static void main(String[] args){ Dog xiaobai = new Dog(); xiaobai.叫(); xiaobai.吃(); xiaobai.玩耍(); xiaobai.讨好主人(); } } 编译并运行程序,查看结果: 115 欧零博客地址:http://www.cnblogs.com/ouling/ 我们看到使用接口完美的解决了上面的问题。 最后记住接口不仅仅是为了解决多重继承问题才出现的,要不然会被人笑话的 ^_^。 二、接口的几个规则 1. 接口名用 interface 修饰, 相对应的 类名用 class 修饰 2. 接口里定义的方法都是抽象方法,可以用 abstract 修饰,当然也可以不 用它修饰 3. 接口只能被实现 ( implements ) 4. 可以用抽象类实现接口,也就是说虽然实现了,但是没有真正写接口的任 何方法,它把责任推给了抽象类的子类 5. 普通类实现接口,则必须按照接口的契约,实现接口所定义的所有方法。 6. 接口可以继承接口,或者说一个接口可以是另一个接口的父类 7. 一个接口可以继承多个父类,也就是说一个接口之间可以多重继承。 以上规则,有点超浓缩了,请同学们慢慢体会。 最后总结一下,当你实现接口时就表明你同意遵守定义在接口中的契约,也意味 着你肯定实现了接口定义的所有方法。那么任何了解该接口方法形式的人,都确 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 信他们能够调用你所实现的类,去执行接口中的方法。 这时候我们说接口是一 个契约,是一个 like a 的关系(继承是 is a 关系)。 很多时候我们说不要滥用继承,要用接口,不要用继承,也是基于这样的思考。 好了本讲就到这里,Take your time and enjoy it 。 117 欧零博客地址:http://www.cnblogs.com/ouling/ Java 基础第十二讲:面向对象基础(六) 本讲内容:内部类 Java 语言允许在类中再定义类,这种在其它类内部定义的类就叫内部类。内部 类又分为:常规内部类、局部内部类、匿名内部类和静态嵌套类四种。我们内部 类的知识在 Android 手机开发中经常用到。 一、常规内部类 所谓常规内部类,或者说内部类,指的就是除去后面三种之外的内部类(这算什 么解释。。。) 先写一个最简单的内部类的例子,大家感觉一下: public class Outer { public class Inner{ } } 编译一下,我们看到目录中出现了两个 class 文件,其中有一个文件名叫做 Outer$Inner.class,带了一个$符号,这个特点让我们很容易的认出来这是内部 类编译后的 class 文件。 再写一个稍微复杂一点的内部类: public class Outer { private int x=1; public Outer(){ System.out.println("Outer initial"); } public class Inner{ public Inner(){ 《Android 学习指南》博客地址:http://android.yaohuiji.com/about System.out.println("Inner initial"); } private int x=2; public void add(){ int x=3; System.out.println(x); System.out.println(this.x); System.out.println(Outer.this.x); } } public static void main(String[] args){ Inner inner = new Outer().new Inner(); inner.add(); } } 我们编译以后,运行一下看看: 在上面的例子里我们可以清晰的看到: 1. 内部类就像一个实例成员一样存在于外部类中。 2. 内部类可以访问外部类的所有成员就想访问自己的成员一样没有限制。 3. 内部类中的 this 指的是内部类的实例对象本身,如果要用外部类的实例 对象就可以用类名.this 的方式获得。 4. 内部类对象中不能有静态成员,原因很简单,内部类的实例对象是外部类 实例对象的一个成员。 下面我们再小结一下内部类的创建方法: 119 欧零博客地址:http://www.cnblogs.com/ouling/ 1. 在外部类的内部,可以用 Inner inner = new Inner(); 方法直接创建 2. 在外部类外部,必须先创建外部类实例,然后再创建内部类实例,除了上 面 Inner inner = new Outer().new Inner()的写法以外,还有 Outer outer = new Outer(); Inner inner = outer.new Inner();的写法 二、局部内部类 我们也可以把类定义在方法内部,这时候我们称这个类叫局部内部类。 我们再看一个例子: public class Outer { int x =1; public void doSomething(){ final int y=2; class Inner{ int x =3; void print(){ int x=4; System.out.println(x); System.out.println(this.x); System.out.println(Outer.this.x); System.out.println(y); } } Inner inner = new Inner(); inner.print(); } public static void main(String[] args){ Outer outer = new Outer(); outer.doSomething(); } } 运行程序,查看结果: 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 我们通过上面这里例子也可以看到下面几点: 1. 局部内部类的地位和方法内的局部变量的位置类似,因此不能修饰局部变 量的修饰符也不能修饰局部内部类,譬如 public、private、protected、 static、transient 等 2. 局部内部类只能在声明的方法内是可见的,因此定义局部内部类之后,想 用的话就要在方法内直接实例化,记住这里顺序不能反了,一定是要先声 明后使用,否则编译器会说找不到。 3. 局部内部类不能访问定义它的方法内的局部变量,除非这个变量被定义为 final 。 是不是有点不好理解?关于为什么用 final 修饰以后就可以用了,我打算专门在 番外篇里专门写一篇博客给你讲清楚,先记住吧。 三、匿名内部类 当我们把内部类的定义和声明写到一起时,就不用给这个类起个类名而是直接使 用了,这种形式的内部类根本就没有类名,因此我们叫它匿名内部类。 我们再看一个有趣的例子: public class Dog { public interface Pet { public void beFriendly(); public void play(); } public static void main(String[] args){ 121 欧零博客地址:http://www.cnblogs.com/ouling/ Pet dog = new Pet(){ @Override public void beFriendly() { System.out.println("蹭蹭你^_^"); } @Override public void play() { System.out.println("把飞盘叼给你,逼你 把飞盘丢出去,然后它再捡回来让你继续扔,连续 500 次^_^"); } }; dog.beFriendly(); dog.play(); } } 编译和运行程序,查看结果: 竟然编译和运行都很正常,我们知道抽象类和接口肯定无法实例化的,因此刚才 的例子肯定有点意思: 1. 第一匿名内部类可以是个接口,这个没什么好奇怪的哈。 2. 第 12 行到第 21 行是一个语句,就是定义了一个对象,因此 21 行大括号 后面有个分号。 3. 匿名内部类用 new Pet(){ „ } 的方式把声明类的过程和创建类的实例 的过程合二为一。 4. 匿名内部类可以是某个类的继承子类也可以是某个接口的实现类。 好吧我们再看一个例子,方法参数内的匿名内部类: public class Dog { 《Android 学习指南》博客地址:http://android.yaohuiji.com/about static abstract class Ball { abstract String getName(); } void play(Ball b){ System.out.println(b.getName()); } public static void main(String[] args){ Dog dog = new Dog(); dog.play(new Ball(){ @Override String getName() { return "qiu qiu"; }}); } } 编译和运行以后的截图我就不给你了,返回值就是“qiu qiu”。 从第 14 行到第 18 行是一句话,就是执行一个 play 方法,而这个方法的参数就 由一个匿名内部类的实例来提供。 四、静态嵌套类 为了让你感觉舒服一些,我们也把最简单的内部类放在最后讲。 当一个内部类前面用 static 修饰时,我们称之为静态嵌套类或者说静态内部类。 上面的例子里其实我们已经看到过静态嵌套类了,下面我们再举一个例子: public class Outer { static int x =1; static class Nest { void print(){ System.out.println("Nest "+x); } } public static void main(String[] args){ 123 欧零博客地址:http://www.cnblogs.com/ouling/ Outer.Nest nest = new Outer.Nest(); nest.print(); } } 因为静态嵌套类和其他静态方法一样只能访问其它静态的成员,而不能访问实例 成员。因此静态嵌套类和外部类(封装类)之间的联系就很少了,他们之间可能 也就是命名空间上的一些关联。上面例子中你需要注意的就是静态嵌套类的声明 方法 new Outer.Nest() 连续写了两个类名,以至于我们都怀疑前面的 Outer 是个包名了,好在包名一般都小写的,要不还真分不清„„ 再强调一遍,内部类在 Android 中应用的非常多,理解和使用好显得蛮重要。好 了,本讲就到这里。 《Android 学习指南》博客地址:http://android.yaohuiji.com/about Java 基础第十三讲:数组 本讲内容:数组 数组是 Java 中的对象,它用来存储多个相同类型的基本数据类型或者对象引用。 一、声明数组 数组是通过说明它将要保存的元素类型来声明的,元素类型可以是对象或者基本 类型。类型后面的方括号可以在写在标识符的前面,也可以写在后面。当然我们 推荐还是写在前面。 int[] number1; int number2[]; int[] number1 ; 把方括号紧贴着类型写,会明确的告诉我们声明的是一个对象 他的名字是 number1,他的类型是数组类型,而且是只能存储 int 类型的数组。 而后一种写法是 C 程序员更喜欢的写法。 可以声明多维数组,可以声明对象类型的数组: String[][] s1; //二维数组 String[][][][] s2; //四维数组 String[] s3[]; //怪异写法的二维数组,这样写也是正确的,但是不 建议这么干 Java 中的二维数组就是一维数组中的每一个元素都还是个数组,那么合起来就 是二维数组了,以此类推。 最后记住一句话在声明数组的时候不能在方括号中写数组的长度,因为声明数组 的过程并没有创建数组本身,只是定义了一个变量,但是变量并没被赋值。 二、构建数组 | 创建数组 | 实例化数组 构建数组意味着在堆上创建数组对象(所有的对象都存储在堆上,堆是一种内存 存储结构,既然要存储就设计空间分配问题,因此此时需要指定数组的大小)。 而此时虽然有了数组对象,但数组对象里还没有值。 int[] scores; //声明数组 scores = new int[34]; //创建数组 int[] i = new int[22]; //声明并创建数组 125 欧零博客地址:http://www.cnblogs.com/ouling/ 构建数组意味着在堆上创建数组对象(所有的对象都存储在堆上,堆是一种内存 存储结构,既然要存储就设计空间分配问题,因此此时需要指定数组的大小)。 int[][] xy= new int[2][3]; //声明并创建二维数组 int[][] mn= new int[2][]; //声明并创建二维数组,只创建第一级数 组也是可以的。 mn[0]=int[4]; //分别定义第二级数组 mn[1]=int[5]; //他们的长度可以不同 三、初始化数组 | 给数组赋值 初始化数组就是把内容放在数组中。数组中的内容就是数组的元素。他们可以是 基本数据类型也可以是引用数据类型。如同引用类型的变量中保存的是指向对象 的引用而不是对象本身一样。数组中保存的也是对象的引用而不是对象本身。 Pig[] pigs = new Pig[3]; //声明并创建猪数组 pigs[0] = new Pig(); //给每一个元素赋值,创建了三个猪对象, 此时数组里才真正有了对象 pigs[1] = new Pig(); //数组用下标来赋值和访问,下标写在[] 中,数组下标最大是声明数量减 1 pigs[2] = new Pig(); 下面我们再看一个例子: int[] numbers={0,1,2,3,4,5,6,7,8,9}; Pig[] pigs = {new Pig(),new Pig(),new Pig}; int[][] xy={{2,3},{4,5},{6,7}}; 这种写法就是把声明、创建和初始化同时完成的快捷写法。注意这种写法不能被 分拆: int[] numbers; numbers={0,1,2,3,4,5,6,7,8,9}; //这样的写法在 java 中是不允许 的,这„„很不幸 然而,下面的写法则是合法的: int[] numbers; numbers=new int[]{0,1,2,3,4,5,6,7,8,9}; //创建匿名数组并赋值 int[][] xy= new int[][]{{2,3},{4,5},{5,6}}; //创建二维匿名数组 并赋值 int[] x=new int[3]{1,2,3}; //这样的写法是错误的 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 我们看到这样的写法多了个创建匿名数组的过程,记住创建匿名数组的时候不要 在中括号中填写数组的大小,否则会报错。 好了,本讲就到这里。 127 欧零博客地址:http://www.cnblogs.com/ouling/ Java 基础第十四讲:字符串 本讲内容:字符串 程序开发的工作中 80%的操作都和字符串有关,这这句话请起来还是蛮有道理。 字符串成了串,就形成了一个类,这类就叫 String。 让我们留意一下 String 的源代码,第一,String 永远不可能有子类,它的实例 也是无法改变的。第二,String 实现了 CharSequence 接口,而这个接口我们 在 Android 开发中还是经常可以看到的。 一、创建字符串对象 String s1 = new String("Milestone"); String s2 = "String"; 以上就是创建字符串的两种方法,第一种是常规写法,创建一个对象当然就可以 用 new 跟上个构造函数完成。第二种是字符串对象的特殊写法,主要是字符串太 常用了,所以 Java 在语言级别对其做了特殊照顾(作弊?)。第二种写法,最 常用,效率也高。(为什么说效率高,可以参见 Java 番外篇的相关文章) 二、字符串操作中的加号 我们经常要把两个或者更多的字符串拼接成一个字符串,除了普通的连接字符串 的方法以外,Java 语言专门为 String 提供了一个字符串连接符号“+” ,下面 看一个例子: public class StringTest { public static void main(String[] args) { 字符串 String s1 = "abc"; String s2= "xyz"; String s3=s1.concat(s2); String s4=s1+s2; System.out.println(s1); System.out.println(s3); //第一种,用方法连接两个 //第二种,用+号连接 《Android 学习指南》博客地址:http://android.yaohuiji.com/about System.out.println(s4); int i = 1; int j = 2; String s5="3"; System.out.println(i+j+s5); //第一个加号是数字和数字相 加,是算数运算,第二个加号是数字和字符串相加,就是连接操作了 System.out.println(""+i+j+s5); //为了保证都是字符串连 接,我们再前面加一个空串。 } } 编译并运行程序,查看结果: 三、字符串中的常用方法 charAt() 返回位于指定索引处的字符串 concat() 将一个字符串追加到另一个字符串的末尾 equalseIgnoseCase() 判断两个字符串的相等性,忽略大小写 length() 返回字符串中的字符个数 replace() 用新字符代替指定的字符 substring() 返回字符串的一部分 toLowerCase() 将字符串中的大写字符转换成小写字符返回 toString() 返回字符串的值 toUpperCase() 将字符串中的小写字符转换成大写字符返回。 trim() 删除字符串前后的空格 splite() 将字符串按照指定的规则拆分成字符串数组 (此处差一个例子)好了,本讲就到这里。 129 欧零博客地址:http://www.cnblogs.com/ouling/ Java 基础第十五讲:集合(一) 本讲内容:集合 collection 讲集合 collection 之前,我们先分清三个概念: 1. colection 集合,用来表示任何一种数据结构 2. Collection 集合接口,指的是 java.util.Collection 接口,是 Set、List 和 Queue 接口 的超类接口 3. Collections 集合工具类,指的是 java.util.Collections 类。 SCJP 考试要求了解的接口有:Collection , Set , SortedSet , List , Map , SortedMap , Queue , NavigableSet , NavigableMap, 还有一个 Iterator 接 口也是必须了解的。 SCJP 考试要求了解的类有: HashMap , Hashtable ,TreeMap , LinkedHashMap , HashSet , LinkedHashSet ,TreeSet , ArrayList , Vector , LinkedList , PriorityQueuee , Collections , Arrays 下面给出一个集合之间的关系图: 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 上图中加粗线的 ArrayList 和 HashMap 是我们重点讲解的对象。下面这张图看 起来层级结构更清晰些。 131 欧零博客地址:http://www.cnblogs.com/ouling/ 我们这里说的集合指的是小写的 collection,集合有 4 种基本形式,其中前三 种的父接口是 Collection。 1. List 关注事物的索引列表 2. Set 关注事物的唯一性 3. Queue 关注事物被处理时的顺序 4. Map 关注事物的映射和键值的唯一性 一、Collection 接口 Collection 接口是 Set 、List 和 Queue 接口的父接口,提供了多数集合常用 的方法声明,包括 add()、remove()、contains() 、size() 、iterator() 等。 add(E e) remove(Object o) contains(Object o) size() 将指定对象添加到集合中 将指定的对象从集合中移除,移除成功返回 true,不成功返回 false 查看该集合中是否包含指定的对象,包含返回 true,不包含返回 flase 返回集合中存放的对象的个数。返回值为 int 《Android 学习指南》博客地址:http://android.yaohuiji.com/about clear() iterator() toArray() toArray(T[] t) 移除该集合中的所有对象,清空该集合。 返回一个包含所有对象的 iterator 对象,用来循环遍历 返回一个包含所有对象的数组,类型是 Object 返回一个包含所有对象的指定类型的数组 我们在这里只举一个把集合转成数组的例子,因为 Collection 本身是个接口所 以,我们用它的实现类 ArrayList 做这个例子: import java.util.ArrayList; import java.util.Collection; public class CollectionTest { public static void main(String[] args) { String a = "a",b="b",c="c"; Collection list = new ArrayList(); list.add(a); list.add(b); list.add(c); String[] array = list.toArray(new String[1]); for(String s : array){ System.out.println(s); } } } 编译并运行程序,检查结果: 133 欧零博客地址:http://www.cnblogs.com/ouling/ 二、几个比较重要的接口和类简介 1、List 接口 List 关心的是索引,与其他集合相比,List 特有的就是和索引相关的一些方法: get(int index) 、 add(int index,Object o) 、 indexOf(Object o) 。 ArrayList 可以将它理解成一个可增长的数组,它提供快速迭代和快速随机访问 的能力。 LinkedList 中的元素之间是双链接的,当需要快速插入和删除时 LinkedList 成为 List 中的不二选择。 Vector 是 ArrayList 的线程安全版本,性能比 ArrayList 要低,现在已经很少 使用 2、Set 接口 Set 关心唯一性,它不允许重复。 HashSet 当不希望集合中有重复值,并且不关心元素之间的顺序时可以使用此 类。 LinkedHashset 当不希望集合中有重复值,并且希望按照元素的插入顺序进行迭 代遍历时可采用此类。 TreeSet 当不希望集合中有重复值,并且希望按照元素的自然顺序进行排序时可 以采用此类。(自然顺序意思是某种和插入顺序无关,而是和元素本身的内容和 特质有关的排序方式,譬如“abc”排在“abd”前面。) 3、Queue 接口 《Android 学习指南》博客地址:http://android.yaohuiji.com/about Queue 用于保存将要执行的任务列表。 LinkedList 同样实现了 Queue 接口,可以实现先进先出的队列。 PriorityQueue 用来创建自然排序的优先级队列。番外篇中有个例子 http://android.yaohuiji.com/archives/3454 你可以看一下。 4、Map 接口 Map 关心的是唯一的标识符。他将唯一的键映射到某个元素。当然键和值都是对 象。 HashMap 当需要键值对表示,又不关心顺序时可采用 HashMap。 Hashtable 注意 Hashtable 中的 t 是小写的,它是 HashMap 的线程安全版本,现 在已经很少使用。 LinkedHashMap 当需要键值对,并且关心插入顺序时可采用它。 TreeMap 当需要键值对,并关心元素的自然排序时可采用它。 三、ArrayList 的使用 ArrayList 是一个可变长的数组实现,读取效率很高,是最常用的集合类型。 1、ArrayList 的创建 在 Java5 版本之前我们使用: List list = new ArrayList(); 在 Java5 版本之后,我们使用带泛型的写法: List list = new ArrayList(); 上面的代码定义了一个只允许保存字符串的列表,尖括号括住的类型就是参数类 型,也成泛型。带泛型的写法给了我们一个类型安全的集合。关于泛型的知识可 以参见这里。 2、ArrayList 的使用: List list = new ArrayList(); list.add("nihao!"); list.add("hi!"); list.add("konikiwa!"); list.add("hola"); 135 欧零博客地址:http://www.cnblogs.com/ouling/ list.add("Bonjour"); System.out.println(list.size()); System.out.println(list.contains(21)); System.out.println(list.remove("hi!")); System.out.println(list.size()); 关于 List 接口中的方法和 ArrayList 中的方法,大家可以看看 JDK 中的帮助。 3、基本数据类型的的自动装箱: 我们知道集合中存放的是对象,而不能是基本数据类型,在 Java5 之后可以使用 自动装箱功能,更方便的导入基本数据类型。 List list = new ArrayList(); list.add(new Integer(42)); list.add(43); 4、ArrayList 的排序: ArrayList 本身不具备排序能力,但是我们可以使用 Collections 类的 sort 方 法使其排序。我们看一个例子: import java.util.ArrayList; import java.util.Collections; import java.util.List; public class Test { public static void main(String[] args) { List list = new ArrayList(); list.add("nihao!"); list.add("hi!"); list.add("konikiwa!"); list.add("hola"); list.add("Bonjour"); System.out.println("排序前:"+ list); Collections.sort(list); System.out.println("排序后:"+ list); } } 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 编译并运行程序查看结果: 排序前:[nihao!, hi!, konikiwa!, hola, Bonjour] 排序后:[Bonjour, hi!, hola, konikiwa!, nihao!] 5、数组和 List 之间的转换 从数组转换成 list,可以使用 Arrays 类的 asList()方法: import java.util.ArrayList; import java.util.Collections; import java.util.List; public class Test { public static void main(String[] args) { String[] sa = {"one","two","three","four"}; List list = Arrays.asList(sa); System.out.println("list:"+list); System.out.println("list.size()="+list.size()); } } 6、Iterator 和 for-each 在 for-each 出现之前,我们想遍历 ArrayList 中的每个元素我们会使用 Iterator 接口: import java.util.Arrays; import java.util.Iterator; import java.util.List; public class Test { public static void main(String[] args) { "four"); // Arrays 类为我们提供了一种 list 的便捷创建方式 List list = Arrays.asList("one", "two", "three", // 转换成 Iterator 实例 137 欧零博客地址:http://www.cnblogs.com/ouling/ Iterator it = list.iterator(); //遍历 while (it.hasNext()) { System.out.println(it.next()); } } } 在 for-each 出现之后,遍历变得简单一些: import java.util.Arrays; import java.util.Iterator; import java.util.List; public class Test { public static void main(String[] args) { "four"); // Arrays 类为我们提供了一种 list 的便捷创建方式 List list = Arrays.asList("one", "two", "three", for (String s : list) { System.out.println(s); } } } 好了,本讲就到这里,下次我们讲 HashMap。 《Android 学习指南》博客地址:http://android.yaohuiji.com/about Java 基础第十六讲:集合(二) 讲内容:Map HashMap 前面课程中我们知道 Map 是个接口,它关心的是映射关系,它里面的元素是成对 出现的,键和值都是对象且键必须保持唯一。这一点上看它和 Collection 是很 不相同的。 一、Map 接口 Map 接口的常用方法如下表所示: put(K key, V value) 向集合中添加指定的键值对 putAll(Map t) 把一个 Map 中的所有键值对添加到该集合 containsKey(Object key) 如果包含该键,则返回 true containsValue(Object value) 如果包含该值,则返回 true get(Object key) 根据键,返回相应的值对象 keySet() 将该集合中的所有键以 Set 集合形式返回 values() 将该集合中所有的值以 Collection 形式返回 remove(Object key) 如果存在指定的键,则移除该键值对,返回键 所对应的值,如果不存在则返回 null clear() 移除 Map 中的所有键值对,或者说就是清空集 合 isEmpty() 查看 Map 中是否存在键值对 size() 查看集合中包含键值对的个数,返回 int 类型 因为 Map 中的键必须是唯一的,所以虽然键可以是 null,只能由一个键是 null, 而 Map 中的值可没有这种限制,值为 null 的情况经常出现,因此 get(Object key) 方法返回 null,有两种情况一种是确实不存在该键值对,二是该键对应的值对象 为 null。为了确保某 Map 中确实有某个键,应该使用的方法是 containsKey(Object key) 。 二、HashMap HashMap 是最常用的 Map 集合,它的键值对在存储时要根据键的哈希码来确定值 放在哪里。 1、HashMap 的基本使用: 139 欧零博客地址:http://www.cnblogs.com/ouling/ import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; public class Test { public static void main(String[] args) { Map map = new HashMap(); map.put(1, "白菜"); map.put(2, "萝卜"); map.put(3, "茄子"); map.put(4, null); map.put(null, null); map.put(null, null); System.out.println("map.size()="+map.size()); System.out.println("map.containsKey(1)="+map.containsKey(2)); System.out.println("map.containsKey(null)="+map.containsKey(n ull)); System.out.println("map.get(null)="+map.get(null)); System.out.println("map.get(2)="+map.get(2)); map.put(null, "黄瓜"); System.out.println("map.get(null)="+map.get(null)); Set set = map.keySet(); System.out.println("set="+set); Collection c = map.values(); System.out.println("Collection="+c); } } 编译并运行程序,查看结果: 《Android 学习指南》博客地址:http://android.yaohuiji.com/about map.size()=5 map.containsKey(1)=true map.containsKey(null)=true map.get(null)=null map.get(2)=萝卜 map.get(null)=黄瓜 set=[null, 1, 2, 3, 4] Collection=[黄瓜, 白菜, 萝卜, 茄子, null] 2、HashMap 中作为键的对象必须重写 Object 的 hashCode()方法和 equals()方 法 下面看一个我花了 1 个小时构思的例子,熟悉龙枪的朋友看起来会比较亲切,设 定了龙和龙的巢穴,然后把它们用 Map 集合对应起来,我们可以根据龙查看它巢 穴中的宝藏数量,例子只是为了说明 hashCode 这个知识点,所以未必有太强的 故事性和合理性,凑合看吧: import java.util.HashMap; import java.util.Map; public class Test { public static void main(String[] args) { // 龙和它的巢穴映射表 Map map = new HashMap(); // 在 Map 中放入四只克莱恩大陆上的龙 map.put(new Dragon("锐刃", 98), new Nest(98)); map.put(new Dragon("明镜", 95), new Nest(95)); map.put(new Dragon("碧雷", 176), new Nest(176)); map.put(new Dragon("玛烈", 255), new Nest(255)); // 查看宝藏 System.out.println("碧雷巢穴中有多少宝藏:" + map.get(new Dragon("碧雷", 176)).getTreasure()); } } // 龙 class Dragon { Dragon(String name, int level) { this.level = level; 141 欧零博客地址:http://www.cnblogs.com/ouling/ this.name = name; } // 龙的名字 private String name; // 龙的级别 private int level; public int getLevel() { return level; } public void setLevel(int level) { this.level = level; } public String getName() { return name; } public void setName(String name) { this.name = name; } } // 巢穴 class Nest { //我研究的龙之常数 final int DRAGON_M = 4162; // 宝藏 private int treasure; // 居住的龙的级别 private int level; Nest(int level) { this.level = level; this.treasure = level * level * DRAGON_M; } 《Android 学习指南》博客地址:http://android.yaohuiji.com/about int getTreasure() { return treasure; } public int getLevel() { return level; } public void setLevel(int level) { this.level = level; this.treasure = level * level * DRAGON_M; } } 编译并运行查看结果: Exception in thread "main" java.lang.NullPointerException at Test.main(Test.java:18) 我们发现竟然报了错误,第 18 行出了空指针错误,也就是说 get 方法竟然没有 拿到预期的巢穴对象。 在这里我们就要研究一下为什么取不到了。我们这里先解释一下 HashMap 的工作 方式。 假设现在有个 6 张中奖彩票的存根,放在 5 个桶里(彩票首位只有 1-5,首位是 1 的就放在一号桶,是 2 的就放在 2 号桶,依次类推),现在你拿了 3 张彩票来 兑奖,一个号码是 113,一个号码是 213,一个号码是 313。那么现在先兑第一 张,取出一号桶里的存根发现存根号码和你的号码不符,所以你第一张没中奖。 继续兑第二张,二号桶里就没存根所以就直接放弃了,把三号桶里的所有彩票存 根都拿出来对应一番,最后发现有一个存根恰好是 313,那么恭喜你中奖了。 143 欧零博客地址:http://www.cnblogs.com/ouling/ HashMap 在确定一个键对象和另一个键对象是否是相同时用了同样的方法,每个 桶就是一个键对象的散列码值,桶里放的就是散列码相同的彩票存根,如果散列 码不同,那么肯定没有相关元素存在,如果散列码相同,那么还要用键的 equals() 方法去比较是否相同,如果相同才认为是相同的键。简单的说就是 hashCode() 相同 && equals()==true 时才算两者相同。 到了这里我们应该明白了,在没有重写一个对象的 hashcode()和 equals()方法 之前,它们执行的是 Object 中对应的方法。而 Object 的 hashcode()是用对象 在内存中存放的位置计算出来的,每个对象实例都不相同。Object 的 equals() 的实现更简单就是看两个对象是否==,也就是两个对象除非是同一个对象,否则 根本不会相同。因此上面的例子虽然都是名字叫碧雷的龙,但是 HashMap 中却无 法认可它们是相同的。 因此我们只有重写 Key 对象的 hashCode()和 equals()方法,才能避免这种情形 出现,好在 Eclipse 可以帮我们自动生成一个类的 hashCode()和 equals(),我 们把上面的例子加上这两个方法再试试看: import java.util.HashMap; import java.util.Map; public class Test { public static void main(String[] args) { // 龙和它的巢穴映射表 Map map = new HashMap(); // 在 Map 中放入四只克莱恩大陆上的龙 map.put(new Dragon("锐刃", 98), new Nest(98)); map.put(new Dragon("明镜", 95), new Nest(95)); map.put(new Dragon("碧雷", 176), new Nest(176)); map.put(new Dragon("玛烈", 255), new Nest(255)); // 查看宝藏 System.out.println("碧雷巢穴中有多少宝藏:" + map.get(new Dragon("碧雷", 176)).getTreasure()); } } // 龙 class Dragon { Dragon(String name, int level) { this.level = level; 《Android 学习指南》博客地址:http://android.yaohuiji.com/about this.name = name; } // 龙的名字 private String name; // 龙的级别 private int level; public int getLevel() { return level; } public void setLevel(int level) { this.level = level; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int hashCode() { final int PRIME = 31; int result = 1; result = PRIME * result + level; result = PRIME * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final Dragon other = (Dragon) obj; 145 欧零博客地址:http://www.cnblogs.com/ouling/ if (level != other.level) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } } // 巢穴 class Nest { //我研究的龙之常数 final int DRAGON_M = 4162; // 宝藏 private int treasure; // 居住的龙的级别 private int level; Nest(int level) { this.level = level; this.treasure = level * level * DRAGON_M; } int getTreasure() { return treasure; } public int getLevel() { return level; } public void setLevel(int level) { this.level = level; this.treasure = level * level * DRAGON_M; } } 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 编译并运行查看结果: 碧雷巢穴中有多少宝藏:128922112 这一次正常输出了,真不容易^_^ 好了本讲就到这里。 147 欧零博客地址:http://www.cnblogs.com/ouling/ Java 基础第十七讲:异常处理(一) 本讲内容:异常 软件开发中有 80%的工作是用来检查和处理错误,而检查并处理错误很多时候是 一件枯燥无趣的事情,如果在语言级别提供一些帮助的话,会减轻一些程序员的 负担。 而 Java 提供了一套比较优秀的异常处理机制: 1、使开发人员不必编写特殊代码来测试返回值就能发现问题, 2、在语法结构就把正常的代码和异常处理的代码清晰的分开来, 3、允许我们使用相同的异常处理代码来处理一定范围内的所有异常。 以期产生一种高效的、有组织的异常处理方式。 1、异常及异常的分类 异常是指在程序中出现的异常状况,在 Java 中异常被抽象成一个叫做 Throwable 的类。 其中如果程序出错并不是由程序本身引起的,而是硬件等其他原因引起的,我们 称之为 Error,一般情况下 Error 一旦产生,对程序来说都是致命的错误,程序 本身无能为力,所以我们可以不对 Error 作出任何处理和响应。 异常如果是由程序引起的我们称之为 Exception,Exception 又分两种,我们把 运行时才会出现的异常叫做 RuntimeException,RuntimeException 我们不好在 程序编写阶段加以事先处理,而其他异常则可以在程序编写和编译阶段加以事先 检查和处理,我们把这种异常叫做检验异常。 程序只需要捕捉和处理检验异常。 相应的我们把除检验异常(可检查异常)之外的异常称之为非检验异常,包括 Error 和 RuntimeException ,非检验异常可以捕捉也可以不捕捉,更多的时候 我们不捕捉,因为捕捉了我们也没办法处理,譬如程序运行时发生了一个 VirtualMachineError 异常,虚拟机都出错了,作为运行在虚拟机内的程序又有 什么办法处理呢? 下面我们用图来表示一下上面讲述的内容: 《Android 学习指南》博客地址:http://android.yaohuiji.com/about 2、异常的处理 try ^ catch ^ finally 异常相关的处理语法可以用下图概括一下: 异常处理的几条规则: 1. try 用于定义可能发生异常的代码段,这个代码块被称为监视区域,所有 可能出现检验异常的代码写在这里。 2. catch 代码段紧跟在 try 代码段后面,中间不能有任何其他代码。 3. try 后面可以没 catch 代码段,这实际上是放弃了捕捉异常,把异常捕捉 的任务交给调用栈的上一层代码。 4. try 后面可以有一个或者多个 catch 代码段,如果有多个 catch 代码段那 么程序只会进入其中某一个 catch。 5. catch 捕捉的多个异常之间有继承关系的话,要先捕捉子类后捕捉父类。 6. finally 代码段可以要也可以不要。 7. 如果 try 代码段没有产生异常,那么 finally 代码段会被立即执行,如果 产生了异常,那么 finally 代码段会在 catch 代码段执行完成后立即执行。 149 欧零博客地址:http://www.cnblogs.com/ouling/ 8. 可以只有 try 和 finally 没有 catch。 下面提供一个使用异常的例子,请注意看相关注释: import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class Bank02 { //用 ArrayList 存储储户信息 List list = new ArrayList(); //用初始化块准备一些测试数据 { String[] account = { "10001", "aya", "111111", "60000" }; list.add(account); account = new String[] { "10002", "bean", "222222", "40000" }; list.add(account); } //用 BufferedWriter 和 BufferedReader 读写文件 BufferedWriter bw; BufferedReader br; // 字符串数组转字符串,试图写一个通用方法 public static String arrayToString(String[] input, String space) { String output = ""; for (int i = 0; i < input.length; i++) { output += input[i] + space; } return output.substring(0, output.length() - 1); } // 写文件 public void writeFile() { try { //try 代码块又称之为"监视区域",表示有可能出问 题闹异常的代码写在这里 《Android 学习指南》博客地址:http://android.yaohuiji.com/about bw = new BufferedWriter(new FileWriter("bank.txt")); for (String[] account : list) { bw.write(arrayToString(account, " ")); bw.newLine(); } bw.flush(); } catch (IOException e) { //可以使用一个 catch 来捕捉异常 //doSomething //打印出异常的堆栈踪迹 e.printStackTrace(); } finally { //finally 提供了一个在正常时和异常时都需要执行 的代码段,在这里可以进行一些资源的释放工作,数据的清理工作 try { bw.close(); } catch (IOException e) { e.printStackTrace(); } } } // 读文件 public void readFile() { try { //try 代码块又称之为"监视区域",表示有可能出问 题闹异常的代码写在这里 br = new BufferedReader(new FileReader("bank.txt")); String temp = ""; list.clear(); while ((temp = br.readLine()) != null) { list.add(temp.split("")); } } catch (FileNotFoundException e) { //可以使用一个 catch 来捕捉异常 //doSomething //打印出异常的堆栈踪迹 e.printStackTrace(); } catch (IOException e) { 151 欧零博客地址:http://www.cnblogs.com/ouling/ //也可以用一组 catch 来捕捉多个异常,如果多个异 常之间有继承关系,那么父类异常写下面 e.printStackTrace(); }finally{ //finally 提供了一个在正常时和异常时都需要执行 的代码段,在这里可以进行一些资源的释放工作,数据的清理工作 try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } public static void main(String[] args) { //创建一个 bank 对象 Bank02 bank = new Bank02(); //写入数据 bank.writeFile(); //读取数据 bank.readFile(); } } 3、常见异常 ArrayIndexOfBoundsException ClassCastException IllegalArgumentException IllegalStateException NullPointException NumberFormatException AssertionError ExceptionInInitializerError StackOverflowError NoClassDefFoundError 数组下标越界异常 强制转换类失败异常 方法参数类型传入异常 非法的设备状态异常 传说中的空指针异常,如果一个对象不存在, 你有对这个对象使用点操作,那么就会出现该 异常 把字符串转成数字失败时出现的数字格式异 常 断言错误 试图初始化静态变量或者静态初始化块时抛 出 栈溢出错误 找不到指定的类错误 好了本讲就到这里,下节课我们讲自定义异常和调用栈。

Top_arrow
回到顶部
EEWORLD下载中心所有资源均来自网友分享,如有侵权,请发送举报邮件到客服邮箱bbs_service@eeworld.com.cn 或通过站内短信息或QQ:273568022联系管理员 高进,我们会尽快处理。