| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> Java知识库 -> ??熬夜爆肝十万字??Java最简单最全入门基础知识(一)(小白必备--推荐小白收藏)?? -> 正文阅读 |
|
[Java知识库]??熬夜爆肝十万字??Java最简单最全入门基础知识(一)(小白必备--推荐小白收藏)?? |
写代码:1,明确需求。我要做什么? 2,分析思路。我要怎么做?1,2,3。 3,确定步骤。每一个思路部分用到哪些语句,方法,和对象。 4,代码实现。用具体的 java 语言代码把思路体现出来。 学习新技术的四点: 1,该技术是什么? 2,该技术有什么特点(使用注意): 3,该技术怎么使用。demo 4,该技术什么时候用?test。 如果此文章能帮助到你或者你学到了一点点东西,希望你能给个关注、点赞和评论。您的关注、点赞和评论对我来说就是我写下去的动力。 目录 Java 循环结构 - for, while 及 do...while 第十三章??Java StringBuffer 和 StringBuilder 类 Java StringBuffer 和 StringBuilder 类 appendReplacement 和 appendTail 方法 第十八章?Java 流(Stream)、文件(File)和IO 第一章 java简介Java 简介Java是由Sun Microsystems公司于1995年5月推出的Java面向对象程序设计语言和Java平台的总称。由James Gosling和同事们共同研发,并在1995年正式推出。 Java分为三个体系:
2005年6月,JavaOne大会召开,SUN公司公开Java SE 6。此时,Java的各种版本已经更名以取消其中的数字"2":J2EE更名为Java EE, J2SE更名为Java SE,J2ME更名为Java ME。 主要特性
发展历史
Java开发工具Java语言尽量保证系统内存在1G以上,其他工具如下所示:
安装好以上的工具后,我们就可以输出Java的第一个程序"Hello World!"
第二章 基础语法Java 基础语法一个Java程序可以认为是一系列对象的集合,而这些对象通过调用彼此的方法来协同工作。下面简要介绍下类、对象、方法和实例变量的概念。
第一个Java程序下面看一个简单的 Java 程序,它将打印字符串?Hello World
下面将逐步介绍如何保存、编译以及运行这个程序:
你将会在窗口看到 Hello World
基本语法编写Java程序时,应注意以下几点:
Java标识符Java所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符。 关于Java标识符,有以下几点需要注意:
Java修饰符像其他语言一样,Java可以使用修饰符来修饰类中方法和属性。主要有两类修饰符:
在后面的章节中我们会深入讨论Java修饰符。 Java变量Java中主要有如下几种类型的变量
Java数组数组是储存在堆上的对象,可以保存多个同类型变量。在后面的章节中,我们将会学到如何声明、构造以及初始化一个数组。 Java枚举Java 5.0引入了枚举,枚举限制变量只能是预先设定好的值。使用枚举可以减少代码中的 bug 。 例如,我们为果汁店设计一个程序,它将限制果汁为小杯、中杯、大杯。这就意味着它不允许顾客点除了这三种尺寸外的果汁。 实例
注意:枚举可以单独声明或者声明在类里面。方法、变量、构造函数也可以在枚举中定义。 Java关键字下面列出了Java保留字。这些保留字不能用于常量、变量、和任何标识符的名称。
Java注释类似于C/C++,Java也支持单行以及多行注释。注释中的字符将被Java编译器忽略。
Java 空行空白行,或者只有注释的行,Java编译器都会忽略掉。 继承在Java中,一个类可以由其他类派生。如果你要创建一个类,而且已经存在一个类具有你所需要的属性或方法,那么你可以将新创建的类继承该类。 利用继承的方法,可以重用已存在类的方法和属性,而不用重写这些代码。被继承的类称为超类(super class),派生类称为子类(subclass)。 接口在Java中,接口可理解为对象间相互通信的协议。接口在继承中扮演着很重要的角色。 接口只定义派生要用到的方法,但是方法的具体实现完全取决于派生类。 第三章 对象和类在理解Java的类和对象之前,先简单介绍一下面向对象的程序设计。程序设计是通过对象对程序进行设计,对象代表一个实体,实体可以清楚地被识别。 Java作为一种面向对象语言。支持以下基本概念:
本节我们重点研究对象和类的概念。
Java中的对象现在让我们深入了解什么是对象。看看周围真实的世界,会发现身边有很多对象,车,狗,人等等。所有这些对象都有自己的状态和行为。 拿一条狗来举例,它的状态有:名字、品种、颜色,行为有:叫、摇尾巴和跑。 对比现实对象和软件对象,它们之间十分相似。 软件对象也有状态和行为。软件对象的状态就是属性,行为通过方法体现。 在软件开发中,方法操作对象内部状态的改变,对象的相互调用也是通过方法来完成。 Java中的类类可以看成是创建 Java 对象的模板。 通过下面一个简单的类来理解下 Java 中类的定义:
一个类可以包含以下类型变量:
一个类可以拥有多个方法,在上面的例子中:? 构造方法每个类都有构造方法。如果没有显式地为类定义构造方法,Java编译器将会为该类提供一个默认构造方法。 在创建一个对象的时候,至少要调用一个构造方法。构造方法的名称必须与类同名,一个类可以有多个构造方法。 下面是一个构造方法示例:
创建对象对象是根据类创建的。在 Java 中,使用关键字 ?
下面是一个创建对象的例子:
编译并运行上面的程序,会打印出下面的结果:
访问实例变量和方法通过已创建的对象来访问成员变量和成员方法,如下所示:
实例下面的例子展示如何访问实例变量和调用成员方法:
编译并运行上面的程序,产生如下结果:
源文件声明规则在本节的最后部分,我们将学习源文件的声明规则。当在一个源文件中定义多个类,并且还有?
类有若干种访问级别,并且类也分不同的类型:抽象类和? 除了上面提到的几种类型,Java 还有一些特殊的类,如:内部类、匿名类。 Java包包主要用来对类和接口进行分类。当开发 Java 程序时,可能编写成百上千的类,因此很有必要对类和接口进行分类。 Import语句在 Java 中,如果给出一个完整的限定名,包括包名、类名,那么 Java 编译器就可以很容易地定位到源代码或者类。? 例如,下面的命令行将会命令编译器载入 java_installation/java/io 路径下的所有类
一个简单的例子在该例子中,我们创建两个类:Employee 和 EmployeeTest。 首先打开文本编辑器,把下面的代码粘贴进去。注意将文件保存为 Employee.java。 Employee 类有四个成员变量:name、age、designation 和 salary。该类显式声明了一个构造方法,该方法只有一个参数。
程序都是从 ? 下面给出 EmployeeTest 类,该类实例化2个 Employee 类的实例,并调用方法设置变量的值。 将下面的代码保存在 EmployeeTest.java 文件中。
编译这两个文件并且运行 EmployeeTest 类,可以看到如下结果:
第四章 基本数据类型Java 基本数据类型变量就是申请内存来存储值。也就是说,当创建变量的时候,需要在内存中申请空间。 内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据。 因此,通过定义不同类型的变量,可以在内存中储存整数、小数或者字符。 Java的两大数据类型:
内置数据类型Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。 byte:
short:
int:
long:
float:
double:
boolean:
char:
实例对于数值类型的基本类型的取值范围,我们无需强制去记忆,因为它们的值都已经以常量的形式定义在对应的包装类中了。请看下面的例子:
编译以上代码输出结果如下所示:
Float和Double的最小值和最大值都是以科学记数法的形式输出的,结尾的"E+数字"表示E之前的数字要乘以10的“数字”次幂。比如3.14E3就是3.14×1000=3140,3.14E-3就是3.14/1000=0.00314。 实际上,JAVA中还存在另外一种基本类型void,它也有对应的包装类 java.lang.Void,不过我们无法直接对它们进行操作。 引用类型
Java常量常量就是一个固定值。它们不需要计算,直接代表相应的值。 常量指不能改变的量。 在Java中用final标志,声明方式和变量类似:
虽然常量名也可以用小写,但为了便于识别,通常使用大写字母表示常量。 字面量可以赋给任何内置类型的变量。例如:
byte、int、long、和short都可以用十进制、16进制以及8进制的方式来表示。 当使用常量的时候,前缀0表明是8进制,而前缀0x代表16进制。例如:
和其他语言一样,Java的字符串常量也是包含在两个引号之间的字符序列。下面是字符串型字面量的例子:
字符串常量和字符常量都可以包含任何Unicode字符。例如:
Java语言支持一些特殊的转义字符序列。
这一节讲解了Java的基本数据类型。下一节将探讨不同的变量类型以及它们的用法。 第五章 变量类型在 Java 语言中,所有的变量在使用前必须声明。声明变量的基本格式如下:
格式说明:type 为 Java 数据类型。identifier 是变量名。可以使用逗号隔开来声明多个同类型变量。 以下列出了一些变量的声明实例。注意有些包含了初始化过程。
Java 语言支持的变量类型有:
Java局部变量
实例1在以下实例中 age 是一个局部变量。定义在 pupAge() 方法中,它的作用域就限制在这个方法中。
以上实例编译运行结果如下:
实例2在下面的例子中 age 变量没有初始化,所以在编译时出错。
以上实例编译运行结果如下:
实例变量
实例:
以上实例编译运行结果如下:
类变量(静态变量)
实例:
以上实例编译运行结果如下:
注意:如果其他类想要访问该变量,可以这样访问:Employee.DEPARTMENT。 本章节中我们学习了 Java 的变量类型,下一章节中我们将介绍Java修饰符的使用。 第六章 Java修饰符Java 修饰符Java 语言提供了很多修饰符,主要分为以下两类:
修饰符用来定义类、方法或者变量,通常放在语句的最前端。我们通过下面的例子来说明:
访问控制修饰符Java 中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持4种不同的访问权限。 默认的,也称为 default,在同一包内可见,不使用任何修饰符。 私有的,以 private 修饰符指定,在同一类内可见。 共有的,以 public 修饰符指定,对所有类可见。 受保护的,以 protected 修饰符指定,对同一包内的类和所有子类可见。 默认访问修饰符-不使用任何关键字使用默认访问修饰符声明的变量和方法,对同一个包内的类是可见的。接口里的变量都隐式声明为? 实例: 如下例所示,变量和方法的声明可以不使用任何修饰符。
私有访问修饰符-private私有访问修饰符是最严格的访问级别,所以被声明为 private 的方法、变量和构造方法只能被所属类访问,并且类和接口不能声明为 private。 声明为私有访问类型的变量只能通过类中公共的 getter 方法被外部类访问。 Private 访问修饰符的使用主要用来隐藏类的实现细节和保护类的数据。 下面的类使用了私有访问修饰符:
实例中,Logger 类中的 format 变量为私有变量,所以其他类不能直接得到和设置该变量的值。为了使其他类能够操作该变量,定义了两个? 公有访问修饰符-public被声明为 public 的类、方法、构造方法和接口能够被任何其他类访问。 如果几个相互访问的 public 类分布在不同的包中,则需要导入相应 public 类所在的包。由于类的继承性,类所有的公有方法和变量都能被其子类继承。 以下函数使用了公有访问控制:
Java 程序的 main() 方法必须设置成公有的,否则,Java 解释器将不能运行该类。 受保护的访问修饰符-protected被声明为 protected 的变量、方法和构造器能被同一个包中的任何其他类访问,也能够被不同包中的子类访问。 Protected 访问修饰符不能修饰类和接口,方法和成员变量能够声明为 protected,但是接口的成员变量和成员方法不能声明为 protected。 子类能访问 Protected 修饰符声明的方法和变量,这样就能保护不相关的类使用这些方法和变量。 下面的父类使用了 protected 访问修饰符,子类重载了父类的 openSpeaker() 方法。
如果把 openSpeaker() 方法声明为private,那么除了 AudioPlayer 之外的类将不能访问该方法。 如果把 openSpeaker() 声明为 public,那么所有的类都能够访问该方法。 如果我们只想让该方法对其所在类的子类可见,则将该方法声明为 protected。 访问控制和继承请注意以下方法继承的规则:
非访问修饰符为了实现一些其他的功能,Java 也提供了许多非访问修饰符。 static 修饰符,用来创建类方法和类变量。 final 修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。 abstract 修饰符,用来创建抽象类和抽象方法。 synchronized 和 volatile 修饰符,主要用于线程的编程。 static修饰符
对类变量和方法的访问可以直接使用 ? 如下例所示,static 修饰符用来创建类方法和类变量。
以上实例运行编辑结果如下:
final 修饰符final 变量: final 变量能被显式地初始化并且只能初始化一次。被声明为final的对象的引用不能指向不同的对象。但是 final 对象里的数据可以被改变。也就是说 final 对象的引用不能改变,但是里面的值可以改变。 final 修饰符通常和 static 修饰符一起使用来创建类常量。 实例:
final 方法类中的 Final 方法可以被子类继承,但是不能被子类修改。 声明 final 方法的主要目的是防止该方法的内容被修改。 如下所示,使用 final 修饰符声明方法。
final 类final 类不能被继承,没有类能够继承 final 类的任何特性。 实例:
abstract 修饰符抽象类: 抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。 一个类不能同时被 abstract 和 final 修饰。如果一个类包含抽象方法,那么该类一定要声明为抽象类,否则将出现编译错误。 抽象类可以包含抽象方法和非抽象方法。 实例:
抽象方法抽象方法是一种没有任何实现的方法,该方法的的具体实现由子类提供。抽象方法不能被声明成 final 和 static。 任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类。 如果一个类包含若干个抽象方法,那么该类必须声明为抽象类。抽象类可以不包含抽象方法。 抽象方法的声明以分号结尾,例如:public abstract sample(); 实例:
synchronized 修饰符synchronized 关键字声明的方法同一时间只能被一个线程访问。Synchronized 修饰符可以应用于四个访问修饰符。 实例:
transient 修饰符序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机 (JVM) 跳过该特定的变量。 该修饰符包含在定义变量的语句中,用来预处理类和变量的数据类型。 实例:
volatile修饰符volatile 修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。 一个 volatile 对象引用可能是 null。 实例:
一般地,在一个线程中调用run()方法,在另一个线程中调用stop()方法。如果line 1中的active位于缓冲区的值被使用,那么当把line 2中的active设置成false时,循环也不会停止。 第七章 Java运算符Java 运算符计算机的最基本用途之一就是执行数学运算,作为一门计算机语言,Java也提供了一套丰富的运算符来操纵变量。我们可以把运算符分成以下几组:
算术运算符算术运算符用在数学表达式中,它们的作用和在数学中的作用一样。下表列出了所有的算术运算符。 表格中的实例假设整数变量A的值为10,变量B的值为20:
实例下面的简单示例程序演示了算术运算符。复制并粘贴下面的Java程序并保存为Test.java文件,然后编译并运行这个程序:
以上实例编译运行结果如下:
关系运算符下表为Java支持的关系运算符 表格中的实例整数变量A的值为10,变量B的值为20:
实例下面的简单示例程序演示了关系运算符。复制并粘贴下面的Java程序并保存为Test.java文件,然后编译并运行这个程序:
以上实例编译运行结果如下:
位运算符Java定义了位运算符,应用于整数类型(int),长整型(long),短整型(short),字符型(char),和字节型(byte)等类型。 位运算符作用在所有的位上,并且按位运算。假设a = 60,和b = 13;它们的二进制格式表示将如下:
下表列出了位运算符的基本运算,假设整数变量A的值为60和变量B的值为13:
实例下面的简单示例程序演示了位运算符。复制并粘贴下面的Java程序并保存为Test.java文件,然后编译并运行这个程序:
以上实例编译运行结果如下:
逻辑运算符下表列出了逻辑运算符的基本运算,假设布尔变量A为真,变量B为假
实例下面的简单示例程序演示了逻辑运算符。复制并粘贴下面的Java程序并保存为Test.java文件,然后编译并运行这个程序:
以上实例编译运行结果如下:
赋值运算符下面是Java语言支持的赋值运算符:
实例面的简单示例程序演示了赋值运算符。复制并粘贴下面的Java程序并保存为Test.java文件,然后编译并运行这个程序:
以上实例编译运行结果如下:
条件运算符(?:)条件运算符也被称为三元运算符。该运算符有3个操作数,并且需要判断布尔表达式的值。该运算符的主要是决定哪个值应该赋值给变量。
实例
以上实例编译运行结果如下:
instanceof 运算符该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)。 instanceof运算符使用格式如下:
如果运算符左侧变量所指的对象,是操作符右侧类或接口(class/interface)的一个对象,那么结果为真。 下面是一个例子:
如果被比较的对象兼容于右侧类型,该运算符仍然返回true。 看下面的例子:
以上实例编译运行结果如下:
Java运算符优先级当多个运算符出现在一个表达式中,谁先谁后呢?这就涉及到运算符的优先级别的问题。在一个多运算符的表达式中,运算符优先级不同会导致最后得出的结果差别甚大。 例如,(1+3)+(3+2)*2,这个表达式如果按加号最优先计算,答案就是 18,如果按照乘号最优先,答案则是 14。 再如,x = 7 + 3 * 2;这里x得到13,而不是20,因为乘法运算符比加法运算符有较高的优先级,所以先计算3 * 2得到6,然后再加7。 下表中具有最高优先级的运算符在的表的最上面,最低优先级的在表的底部。
第八章 Java循环结构Java 循环结构 - for, while 及 do...while顺序结构的程序语句只能被执行一次。如果您想要同样的操作执行多次,,就需要使用循环结构。 Java中有三种主要的循环结构:
在Java5中引入了一种主要用于数组的增强型for循环。 while循环while是最基本的循环,它的结构为:
只要布尔表达式为true,循环体会一直执行下去。 实例
以上实例编译运行结果如下:
do…while循环对于while语句而言,如果不满足条件,则不能进入循环。但有时候我们需要即使不满足条件,也至少执行一次。 do…while循环和while循环相似,不同的是,do…while循环至少会执行一次。
注意:布尔表达式在循环体的后面,所以语句块在检测布尔表达式之前已经执行了。 如果布尔表达式的值为true,则语句块一直执行,直到布尔表达式的值为false。 实例
以上实例编译运行结果如下:
for循环虽然所有循环结构都可以用while或者do...while表示,但Java提供了另一种语句 —— for循环,使一些循环结构变得更加简单。 for循环执行的次数是在执行前就确定的。语法格式如下:
关于for循环有以下几点说明:
实例
以上实例编译运行结果如下:
Java增强for循环Java5引入了一种主要用于数组的增强型for循环。 Java增强for循环语法格式如下:
声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。 表达式:表达式是要访问的数组名,或者是返回值为数组的方法。 实例
以上实例编译运行结果如下:
break关键字break主要用在循环语句或者switch语句中,用来跳出整个语句块。 break跳出最里层的循环,并且继续执行该循环下面的语句。 语法break的用法很简单,就是循环结构中的一条语句:
实例
以上实例编译运行结果如下:
continue关键字continue适用于任何循环控制结构中。作用是让程序立刻跳转到下一次循环的迭代。 在for循环中,continue语句使程序立即跳转到更新语句。 在while或者do…while循环中,程序立即跳转到布尔表达式的判断语句。 语法continue就是循环体中一条简单的语句:
实例
以上实例编译运行结果如下:
第九章? Java分支结构Java 分支结构 - if...else/switch顺序结构只能顺序执行,不能进行判断和选择,因此需要分支结构。 Java有两种分支结构:
if语句一个if语句包含一个布尔表达式和一条或多条语句。 语法If 语句的用语法如下:
如果布尔表达式的值为 true,则执行if语句中的代码块。否则执行 If 语句块后面的代码。
以上代码编译运行结果如下:
if...else 语句if 语句后面可以跟 else 语句,当if语句的布尔表达式值为 false 时,else 语句块会被执行。 语法if…else 的用法如下:
实例
以上代码编译运行结果如下:
if...else if...else 语句if 语句后面可以跟 else if…else 语句,这种语句可以检测到多种可能的情况。 使用if,else if,else语句的时候,需要注意下面几点:
语法if...else 语法格式如下:
实例
以上代码编译运行结果如下:
嵌套的 if…else 语句使用嵌套的 if-else 语句是合法的。也就是说你可以在另一个 if 或者 else if 语句中使用 if 或者 else if 语句。 语法嵌套的 if…else 语法格式如下:
你可以像 if 语句一样嵌套 else if...else。 实例
以上代码编译运行结果如下:
switch 语句switch 语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支。 语法switch 语法格式如下:
switch 语句有如下规则:
实例
以上代码编译运行结果如下:
第十章? Java Number类Java Number类一般情况下我们会使用数据的基本数据类型:byte、int、short、long、double、float、boolean、char; 对应的包装类型也有八种:Byte、Integer、Short、Long、Double、Float、Character、Boolean; 包装类型都是用 final 声明了,不可以被继承重写;在实际情况中编译器会自动的将基本数据类型装箱成对象类型,或者将对象类型拆箱成基本数据类型;如下:
Number 类是 java.lang 包下的一个抽象类,提供了将包装类型拆箱成基本类型的方法,所有基本类型(数据类型)的包装类型都继承了该抽象类,并且是final声明不可继承改变;
这种由编译器特别支持的包装称为装箱,所以当内置数据类型被当作对象使用的时候,编译器会把内置类型装箱为包装类。相似的,编译器也可以把一个对象拆箱为内置类型。Number 类属于 java.lang 包。 下面是一个装箱与拆箱的例子:
以上实例编译运行结果如下:
当x被赋为整型值时,由于 x 是一个对象,所以编译器要对x进行装箱。然后,为了使x能进行加运算,所以要对x进行拆箱。 Java Math类Java?的?Math?包含了用于执行基本数学运算的属性和方法,如初等指数、对数、平方根和三角函数。 Math?的方法都被定义为?static?形式,通过?Math?类可以在主函数中直接调用。 实例
以上实例编译运行结果如下:
Number?&?Math?类方法 下面的表中列出的是常用的 Number 类和 Math 类的方法:
第十一章?Java Character类Java Character类使用字符时,我们通常使用的是内置数据类型 char。 实例
然而,在实际开发过程中,我们经常会遇到需要使用对象,而不是内置数据类型的情况。为了解决这个问题,Java 语言为内置数据类型 char 提供了包装类 Character 类。 Character类的用法:Character 类提供了一系列方法来操纵字符,你可以使用 Character 的构造方法创建一个 Character 类对象,例如:
在某些情况下,Java 编译器会自动创建一个 Character 对象。 例如,将一个 char 类型的参数传递给需要一个 Character 类型参数时,那么编译器会自动地将 char 类型参数转换为 Character 对象。 这种特征称为装箱,反过来称为拆箱。? 实例
转义序列前面有反斜杠(\)的字符代表转义字符,它对编译器来说是有特殊含义的。 下面列表展示了 Java 的转义序列:
实例当打印语句遇到一个转义序列时,编译器可以正确地对其进行解释。
以上实例编译运行结果如下:
Character 方法下面是 Character 类的方法:
|
序号 | 方法描述 |
---|---|
1 | public StringBuffer append(String s) 将指定的字符串追加到此字符序列。 |
2 | public StringBuffer reverse() ?将此字符序列用其反转形式取代。 |
3 | public delete(int start, int end) 移除此序列的子字符串中的字符。 |
4 | public insert(int offset, int i) 将? int ?参数的字符串表示形式插入此序列中。 |
5 | replace(int start, int end, String str) 使用给定? String ?中的字符替换此序列的子字符串中的字符。 |
下面的列表里的方法和 String 类的方法类似:
序号 | 方法描述 |
1 | int capacity() 返回当前容量。 |
2 | char charAt(int index) 返回此序列中指定索引处的? char ?值。 |
3 | void ensureCapacity(int minimumCapacity) 确保容量至少等于指定的最小值。 |
4 | void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) 将字符从此序列复制到目标字符数组? dst 。 |
5 | int indexOf(String str) 返回第一次出现的指定子字符串在该字符串中的索引。 |
6 | int indexOf(String str, int fromIndex) 从指定的索引处开始,返回第一次出现的指定子字符串在该字符串中的索引。 |
7 | int lastIndexOf(String str) 返回最右边出现的指定子字符串在此字符串中的索引。 |
8 | int lastIndexOf(String str, int fromIndex) 返回最后一次出现的指定子字符串在此字符串中的索引。 |
9 | int length() ?返回长度(字符数)。 |
10 | void setCharAt(int index, char ch) 将给定索引处的字符设置为? ch 。 |
11 | void setLength(int newLength) 设置字符序列的长度。 |
12 | CharSequence subSequence(int start, int end) 返回一个新的字符序列,该字符序列是此序列的子序列。 |
13 | String substring(int start) 返回一个新的? String ,它包含此字符序列当前所包含的字符子序列。 |
14 | String substring(int start, int end) 返回一个新的? String ,它包含此序列当前所包含的字符子序列。 |
15 | String toString() 返回此序列中数据的字符串表示形式。 |
数组对于每一门编程语言来说都是重要的数据结构之一,当然不同语言对数组的实现及处理也不尽相同。
Java 语言中提供的数组是用来存储固定大小的同类型元素。
你可以声明一个数组变量,如 numbers[100] 来代替直接声明 100 个独立变量 number0,number1,....,number99。
本教程将为大家介绍Java数组的声明、创建和初始化,并给出其对应的代码。
首先必须声明数组变量,才能在程序中使用数组。下面是声明数组变量的语法:
dataType[]?arrayRefVar;???//?首选的方法
或
dataType?arrayRefVar[];??//?效果相同,但不是首选方法
注意:?建议使用 dataType[] arrayRefVar 的声明风格声明数组变量。 dataType arrayRefVar[] 风格是来自 C/C++ 语言 ,在Java中采用是为了让? C/C++? 程序员能够快速理解 java 语言。
下面是这两种语法的代码示例:
double[]?myList;?????????//?首选的方法
或
double?myList[];?????????//??效果相同,但不是首选方法
Java 语言使用 new操作符来创建数组,语法如下:
arrayRefVar?=?new?dataType[arraySize];
上面的语法语句做了两件事:
一、使用 dataType[arraySize] 创建了一个数组。
二、把新创建的数组的引用赋值给变量 arrayRefVar。
数组变量的声明,和创建数组可以用一条语句完成,如下所示:
dataType[]?arrayRefVar?=?new?dataType[arraySize];
另外,你还可以使用如下的方式创建数组。
dataType[]?arrayRefVar?=?{value0,?value1,?...,?valuek};
数组的元素是通过索引访问的。数组索引从0开始,所以索引值从 0 到 arrayRefVar.length-1。
那么当数组开辟空间之后,就可以采用如下的方式的操作:
示例: 定义一个int型数组
public class ArrayDemo {
public static void main(String args[]) {
int data[] = new int[3]; /*开辟了一个长度为3的数组*/
data[0] = 10; // 第一个元素
data[1] = 20; // 第二个元素
data[2] = 30; // 第三个元素
for(int x = 0; x < data.length; x++) {
System.out.println(data[x]); //通过循环控制索引
}
}
}
数组本身除了声明并开辟空间之外还有另外一种开辟模式。
示例:?采用分步的模式开辟数组空间
public class ArrayDemo {
public static void main(String args[]) {
int data[] = null;
data = new int[3]; /*开辟了一个长度为3的数组*/
data[0] = 10; // 第一个元素
data[1] = 20; // 第二个元素
data[2] = 30; // 第三个元素
for(int x = 0; x < data.length; x++) {
System.out.println(data[x]); //通过循环控制索引
}
}
}
但是千万要记住,数组属于引用数据类型,所以在数组使用之前一定要开辟控件(实例化),如果使用了没有开辟控件的数组,则一定会出现?NullPointerException
?异常信息:
public class ArrayDemo {
public static void main(String args[]) {
int data[] = null;
System.out.println(data[x]);
}
}
这一原则和之前讲解的对象是完全相同的。
数组在开发之中一定会使用,但是像上面的操作很少。在以后的实际开发之中,会更多的使用数组概念,而直接使用,99%情况下都只是做一个?for?循环输出。
数组的元素类型和数组的大小都是确定的,所以当处理数组元素时候,我们通常使用基本循环或者 foreach 循环。
该实例完整地展示了如何创建、初始化和操纵数组:
public?class?TestArray?{
???public?static?void?main(String[]?args)?{
??????double[]?myList?=?{1.9,?2.9,?3.4,?3.5};
??????//?打印所有数组元素
??????for?(int?i?=?0;?i?<?myList.length;?i++)?{
??????????System.out.println(myList[i]?+?"?");
???????}
???????//?计算所有元素的总和
???????double?total?=?0;
???????for?(int?i?=?0;?i?<?myList.length;?i++)?{
??????????total?+=?myList[i];
???????}
???????System.out.println("Total?is?"?+?total);
???????//?查找最大元素
???????double?max?=?myList[0];
???????for?(int?i?=?1;?i?<?myList.length;?i++)?{
??????????if?(myList[i]?>?max)?max?=?myList[i];
??????}
??????System.out.println("Max?is?"?+?max);
???}
}
以上实例编译运行结果如下:
1.9
2.9
3.4
3.5
Total?is?11.7
Max?is?3.5
JDK 1.5 引进了一种新的循环类型,被称为 foreach 循环或者加强型循环,它能在不使用下标的情况下遍历数组。
语法格式如下:
for(type element: array)
{
System.out.println(element);
}
该实例用来显示数组 myList 中的所有元素:
public?class?TestArray?{
???public?static?void?main(String[]?args)?{
??????double[]?myList?=?{1.9,?2.9,?3.4,?3.5};
??????//?打印所有数组元素
??????for?(double?element:?myList)?{
?????????System.out.println(element);
??????}
???}
}
以上实例编译运行结果如下:
1.9
2.9
3.4
3.5
数组可以作为参数传递给方法。例如,下面的例子就是一个打印 int 数组中元素的方法。
public?static?void?printArray(int[]?array)?{
??for?(int?i?=?0;?i?<?array.length;?i++)?{
?????System.out.print(array[i]?+?"?");
???}
?}
下面例子调用 printArray 方法打印出 3,1,2,6,4和2:
printArray(new?int[]{3,?1,?2,?6,?4,?2});
public?static?int[]?reverse(int[]?list)?{
??int[]?result?=?new?int[list.length];
??for?(int?i?=?0,?j?=?result.length?-?1;?i?<?list.length;?i++,?j--)?{
?????result[j]?=?list[i];
???}
???return?result;
?}
以上实例中result数组作为函数的返回值。
java.util.Arrays 类能方便地操作数组,它提供的所有方法都是静态的。具有以下功能:
给数组赋值:通过 fill 方法。
对数组排序:通过 sort 方法,按升序。
比较数组:通过 equals 方法比较数组中元素值是否相等。
查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。
具体说明请查看下表:
序号 | 方法和说明 |
---|---|
1 | public static int binarySearch(Object[] a, Object key) 用二分查找算法在给定数组中搜索给定值的对象(Byte,Int,double等)。数组在调用前必须排序好的。如果查找值包含在数组中,则返回搜索键的索引;否则返回 (-(插入点) - 1)。 |
2 | public static boolean equals(long[] a, long[] a2) 如果两个指定的 long 型数组彼此相等,则返回 true。如果两个数组包含相同数量的元素,并且两个数组中的所有相应元素对都是相等的,则认为这两个数组是相等的。换句话说,如果两个数组以相同顺序包含相同的元素,则两个数组是相等的。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。 |
3 | public static void fill(int[] a, int val) 将指定的 int 值分配给指定 int 型数组指定范围中的每个元素。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。 |
4 | public static void sort(Object[] a) 对指定对象数组根据其元素的自然顺序进行升序排列。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。 |
java.util包提供了Date类来封装当前的日期和时间。 Date类提供两个构造函数来实例化Date对象。
第一个构造函数使用当前日期和时间来初始化对象。
Date( )
第二个构造函数接收一个参数,该参数是从1970年1月1日起的微秒数。
Date(long millisec)
Date对象创建以后,可以调用下面的方法。
序号 | 方法和描述 |
---|---|
1 | boolean after(Date date) 若当调用此方法的Date对象在指定日期之后返回true,否则返回false。 |
2 | boolean before(Date date) 若当调用此方法的Date对象在指定日期之前返回true,否则返回false。 |
3 | Object clone( ) 返回此对象的副本。 |
4 | int compareTo(Date date) 比较当调用此方法的Date对象和指定日期。两者相等时候返回0。调用对象在指定日期之前则返回负数。调用对象在指定日期之后则返回正数。 |
5 | int compareTo(Object obj) 若obj是Date类型则操作等同于compareTo(Date) 。否则它抛出ClassCastException。 |
6 | boolean equals(Object date) 当调用此方法的Date对象和指定日期相等时候返回true,否则返回false。 |
7 | long getTime( ) 返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。 |
8 | int hashCode( ) ?返回此对象的哈希码值。 |
9 | void setTime(long time) ? 用自1970年1月1日00:00:00 GMT以后time毫秒数设置时间和日期。 |
10 | String toString( ) 转换Date对象为String表示形式,并返回该字符串。 |
Java中获取当前日期和时间很简单,使用Date对象的 toString()方法来打印当前日期和时间,如下所示:
import java.util.Date;
public class DateDemo {
public static void main(String args[]) {
// 初始化 Date 对象
Date date = new Date();
// 使用 toString() 函数显示日期时间
System.out.println(date.toString());
}
}
以上实例编译运行结果如下:
Mon May 04 09:51:52 CDT 2013
Java使用以下三种方法来比较两个日期:
SimpleDateFormat是一个以语言环境敏感的方式来格式化和分析日期的类。SimpleDateFormat允许你选择任何用户自定义日期时间格式来运行。例如:
import java.util.*;
import java.text.*;
public class DateDemo {
public static void main(String args[]) {
Date dNow = new Date( );
SimpleDateFormat ft =
new SimpleDateFormat ("E yyyy.MM.dd 'at' hh:mm:ss a zzz");
System.out.println("Current Date: " + ft.format(dNow));
}
}
以上实例编译运行结果如下:
Current Date: Sun 2004.07.18 at 04:14:09 PM PDT
时间模式字符串用来指定时间格式。在此模式中,所有的ASCII字母被保留为模式字母,定义如下:
字母 | 描述 | 示例 |
---|---|---|
G | 纪元标记 | AD |
y | 四位年份 | 2001 |
M | 月份 | July or 07 |
d | 一个月的日期 | 10 |
h | ?A.M./P.M. (1~12)格式小时 | 12 |
H | 一天中的小时 (0~23) | 22 |
m | 分钟数 | 30 |
s | 秒数 | 55 |
S | 微妙数 | 234 |
E | 星期几 | Tuesday |
D | 一年中的日子 | 360 |
F | 一个月中第几周的周几 | 2 (second Wed. in July) |
w | 一年中第几周 | 40 |
W | 一个月中第几周 | 1 |
a | A.M./P.M. 标记 | PM |
k | 一天中的小时(1~24) | 24 |
K | ?A.M./P.M. (0~11)格式小时 | 10 |
z | 时区 | Eastern Standard Time |
' | 文字定界符 | Delimiter |
" | 单引号 | ` |
printf方法可以很轻松地格式化时间和日期。使用两个字母格式,它以t开头并且以下面表格中的一个字母结尾。例如:
import java.util.Date;
public class DateDemo {
public static void main(String args[]) {
// 初始化 Date 对象
Date date = new Date();
// 使用toString()显示日期和时间
String str = String.format("Current Date/Time : %tc", date );
System.out.printf(str);
}
}
以上实例编译运行结果如下:
Current Date/Time : Sat Dec 15 16:37:57 MST 2012
如果你需要重复提供日期,那么利用这种方式来格式化它的每一部分就有点复杂了。因此,可以利用一个格式化字符串指出要被格式化的参数的索引。
索引必须紧跟在%后面,而且必须以$结束。例如:
import java.util.Date;
public class DateDemo {
public static void main(String args[]) {
// 初始化 Date 对象
Date date = new Date();
// 使用toString()显示日期和时间
System.out.printf("%1$s %2$tB %2$td, %2$tY",
"Due date:", date);
}
}
以上实例编译运行结果如下:
Due date: February 09, 2004
或者,你可以使用<标志。它表明先前被格式化的参数要被再次使用。例如:
import java.util.Date;
public class DateDemo {
public static void main(String args[]) {
// 初始化 Date 对象
Date date = new Date();
// 显示格式化时间
System.out.printf("%s %tB %<te, %<tY",
"Due date:", date);
}
}
以上实例编译运行结果如下:
Due date: February 09, 2004
字符 | 描述 | 例子 |
c | 完整的日期和时间 | Mon May 04 09:51:52 CDT 2009 |
F | ISO 8601 格式日期 | 2004-02-09 |
D | U.S. 格式日期 (月/日/年) | 02/09/2004 |
T | 24小时时间 | 18:05:19 |
r | 12小时时间 | 06:05:19 pm |
R | 24小时时间,不包含秒 | 18:05 |
Y | 4位年份(包含前导0) | 2004 |
y | 年份后2位(包含前导0) | 04 |
C | 年份前2位(包含前导0) | 20 |
B | 月份全称 | February |
b | 月份简称 | Feb |
n | 2位月份(包含前导0) | 02 |
d | 2位日子(包含前导0) | 03 |
e | 2位日子(不包含前导0) | 9 |
A | 星期全称 | Monday |
a | 星期简称 | Mon |
j | 3位年份(包含前导0) | 069 |
H | 2位小时(包含前导0), 00 到 23 | 18 |
k | 2位小时(不包含前导0), ?0 到 23 | 18 |
I | 2位小时(包含前导0), 01 到 12 | 06 |
l | 2位小时(不包含前导0), ?1 到 12 | 6 |
M | 2位分钟(包含前导0) | 05 |
S | 2位秒数(包含前导0) | 19 |
L | 3位毫秒(包含前导0) | 047 |
N | 9位纳秒(包含前导0) | 047000000 |
P | 大写上下午标志 | PM |
p | 小写上下午标志 | pm |
z | 从GMT的RFC 822数字偏移 | -0800 |
Z | 时区 | PST |
s | 自 1970-01-01 00:00:00 GMT的秒数 | 1078884319 |
Q | 自 1970-01-01 00:00:00 GMT的毫妙 | 1078884319047 |
还有其他有用的日期和时间相关的类。对于更多的细节,你可以参考到Java标准文档。
SimpleDateFormat 类有一些附加的方法,特别是parse(),它试图按照给定的SimpleDateFormat 对象的格式化存储来解析字符串。例如:
import java.util.*;
import java.text.*;
public class DateDemo {
public static void main(String args[]) {
SimpleDateFormat ft = new SimpleDateFormat ("yyyy-MM-dd");
String input = args.length == 0 ? "1818-11-11" : args[0];
System.out.print(input + " Parses as ");
Date t;
try {
t = ft.parse(input);
System.out.println(t);
} catch (ParseException e) {
System.out.println("Unparseable using " + ft);
}
}
}
以上实例编译运行结果如下:
$ java DateDemo
1818-11-11 Parses as Wed Nov 11 00:00:00 GMT 1818
$ java DateDemo 2007-12-01
2007-12-01 Parses as Sat Dec 01 00:00:00 GMT 2007
Java 休眠(sleep)
你可以让程序休眠一毫秒的时间或者到您的计算机的寿命长的任意段时间。例如,下面的程序会休眠3秒:
import java.util.*;
public class SleepDemo {
public static void main(String args[]) {
try {
System.out.println(new Date( ) + "\n");
Thread.sleep(5*60*10);
System.out.println(new Date( ) + "\n");
} catch (Exception e) {
System.out.println("Got an exception!");
}
}
}
以上实例编译运行结果如下:
Sun May 03 18:04:41 GMT 2009
Sun May 03 18:04:44 GMT 2009
下面的一个例子表明如何测量时间间隔(以毫秒为单位):
import java.util.*;
public class DiffDemo {
public static void main(String args[]) {
try {
long start = System.currentTimeMillis( );
System.out.println(new Date( ) + "\n");
Thread.sleep(5*60*10);
System.out.println(new Date( ) + "\n");
long end = System.currentTimeMillis( );
long diff = end - start;
System.out.println("Difference is : " + diff);
} catch (Exception e) {
System.out.println("Got an exception!");
}
}
}
以上实例编译运行结果如下:
Sun May 03 18:16:51 GMT 2009
Sun May 03 18:16:57 GMT 2009
Difference is : 5993
我们现在已经能够格式化并创建一个日期对象了,但是我们如何才能设置和获取日期数据的特定部分呢,比如说小时,日,或者分钟? 我们又如何在日期的这些部分加上或者减去值呢? 答案是使用Calendar 类。
Calendar类的功能要比Date类强大很多,而且在实现方式上也比Date类要复杂一些。
Calendar类是一个抽象类,在实际使用时实现特定的子类的对象,创建对象的过程对程序员来说是透明的,只需要使用getInstance方法创建即可。
Calendar c = Calendar.getInstance();//默认是当前日期
使用Calendar类代表特定的时间,需要首先创建一个Calendar的对象,然后再设定该对象中的年月日参数来完成。
//创建一个代表2009年6月12日的Calendar对象
Calendar c1 = Calendar.getInstance();
c1.set(2009, 6 - 1, 12);
Calendar类中用一下这些常量表示不同的意义,jdk内的很多类其实都是采用的这种思想
常量 | 描述 |
---|---|
Calendar.YEAR | 年份 |
Calendar.MONTH | 月份 |
Calendar.DATE | 日期 |
Calendar.DAY_OF_MONTH | 日期,和上面的字段意义完全相同 |
Calendar.HOUR | 12小时制的小时 |
Calendar.HOUR_OF_DAY | 24小时制的小时 |
Calendar.MINUTE | 分钟 |
Calendar.SECOND | 秒 |
Calendar.DAY_OF_WEEK | 星期几 |
Set设置
如:
Calendar c1 = Calendar.getInstance();
调用:
public final void set(int year,int month,int date)
c1.set(2009, 6 - 1, 12);//把Calendar对象c1的年月日分别设这为:2009、5、12
利用字段类型设置
如果只设定某个字段,例如日期的值,则可以使用如下set方法:
public void set(int field,int value)
把 c1对象代表的日期设置为10号,其它所有的数值会被重新计算
c1.set(Calendar.DATE,10);
把c1对象代表的年份设置为2008年,其他的所有数值会被重新计算
c1.set(Calendar.YEAR,2008);
其他字段属性set的意义以此类推
Add设置
Calendar c1 = Calendar.getInstance();
把c1对象的日期加上10,也就是c1所表的日期的10天后的日期,其它所有的数值会被重新计算
c1.add(Calendar.DATE, 10);
把c1对象的日期减去10,也就是c1所表的日期的10天前的日期,其它所有的数值会被重新计算
c1.add(Calendar.DATE, -10);
其他字段属性的add的意义以此类推
Calendar c1 = Calendar.getInstance();
// 获得年份
int year = c1.get(Calendar.YEAR);
// 获得月份
int month = c1.get(Calendar.MONTH) + 1;
// 获得日期
int date = c1.get(Calendar.DATE);
// 获得小时
int hour = c1.get(Calendar.HOUR_OF_DAY);
// 获得分钟
int minute = c1.get(Calendar.MINUTE);
// 获得秒
int second = c1.get(Calendar.SECOND);
// 获得星期几(注意(这个与Date类是不同的):1代表星期日、2代表星期1、3代表星期二,以此类推)
int day = c1.get(Calendar.DAY_OF_WEEK);
Calendar类实现了公历日历,GregorianCalendar是Calendar类的一个具体实现。
Calendar 的getInstance()方法返回一个默认用当前的语言环境和时区初始化的GregorianCalendar对象。GregorianCalendar定义了两个字段:AD和BC。这些代表公历定义的两个时代。
下面列出GregorianCalendar对象的几个构造方法:
序号 | 构造函数和说明 |
1 | GregorianCalendar() 在具有默认语言环境的默认时区内使用当前时间构造一个默认的 GregorianCalendar。 |
2 | GregorianCalendar(int year, int month, int date) 在具有默认语言环境的默认时区内构造一个带有给定日期设置的 GregorianCalendar |
3 | GregorianCalendar(int year, int month, int date, int hour, int minute) 为具有默认语言环境的默认时区构造一个具有给定日期和时间设置的 GregorianCalendar。 |
4 | GregorianCalendar(int year, int month, int date, int hour, int minute, int second) ? 为具有默认语言环境的默认时区构造一个具有给定日期和时间设置的 GregorianCalendar。 |
5 | GregorianCalendar(Locale aLocale) 在具有给定语言环境的默认时区内构造一个基于当前时间的 GregorianCalendar。 |
6 | GregorianCalendar(TimeZone zone) 在具有默认语言环境的给定时区内构造一个基于当前时间的 GregorianCalendar。 |
7 | GregorianCalendar(TimeZone zone, Locale aLocale) ?在具有给定语言环境的给定时区内构造一个基于当前时间的 GregorianCalendar。 |
这里是GregorianCalendar 类提供的一些有用的方法列表:
序号 | 方法和说明 |
1 | void add(int field, int amount) 根据日历规则,将指定的(有符号的)时间量添加到给定的日历字段中。 |
2 | protected void computeFields() 转换UTC毫秒值为时间域值 |
3 | protected void computeTime() 覆盖Calendar ,转换时间域值为UTC毫秒值 |
4 | boolean equals(Object obj) 比较此 GregorianCalendar 与指定的 Object。 |
5 | int get(int field) 获取指定字段的时间值 |
6 | int getActualMaximum(int field) 返回当前日期,给定字段的最大值 |
7 | int getActualMinimum(int field) 返回当前日期,给定字段的最小值 |
8 | int getGreatestMinimum(int field) ?返回此 GregorianCalendar 实例给定日历字段的最高的最小值。 |
9 | Date getGregorianChange() 获得格里高利历的更改日期。 |
10 | int getLeastMaximum(int field) 返回此 GregorianCalendar 实例给定日历字段的最低的最大值 |
11 | int getMaximum(int field) 返回此 GregorianCalendar 实例的给定日历字段的最大值。 |
12 | Date getTime() 获取日历当前时间。 |
13 | long getTimeInMillis() 获取用长整型表示的日历的当前时间 |
14 | TimeZone getTimeZone() 获取时区。 |
15 | int getMinimum(int field) 返回给定字段的最小值。 |
16 | int hashCode() 重写hashCode. |
17 | boolean isLeapYear(int year) 确定给定的年份是否为闰年。 |
18 | void roll(int field, boolean up) 在给定的时间字段上添加或减去(上/下)单个时间单元,不更改更大的字段。 |
19 | void set(int field, int value) 用给定的值设置时间字段。 |
20 | void set(int year, int month, int date) 设置年、月、日的值。 |
21 | void set(int year, int month, int date, int hour, int minute) 设置年、月、日、小时、分钟的值。 |
22 | void set(int year, int month, int date, int hour, int minute, int second) 设置年、月、日、小时、分钟、秒的值。 |
23 | void setGregorianChange(Date date) 设置 GregorianCalendar 的更改日期。 |
24 | void setTime(Date date) 用给定的日期设置Calendar的当前时间。 |
25 | void setTimeInMillis(long millis) 用给定的long型毫秒数设置Calendar的当前时间。 |
26 | void setTimeZone(TimeZone value) 用给定时区值设置当前时区。 |
27 | String toString() 返回代表日历的字符串。 |
import java.util.*;
public class GregorianCalendarDemo {
public static void main(String args[]) {
String months[] = {
"Jan", "Feb", "Mar", "Apr",
"May", "Jun", "Jul", "Aug",
"Sep", "Oct", "Nov", "Dec"};
int year;
// 初始化 Gregorian 日历
// 使用当前时间和日期
// 默认为本地时间和时区
GregorianCalendar gcalendar = new GregorianCalendar();
// 显示当前时间和日期的信息
System.out.print("Date: ");
System.out.print(months[gcalendar.get(Calendar.MONTH)]);
System.out.print(" " + gcalendar.get(Calendar.DATE) + " ");
System.out.println(year = gcalendar.get(Calendar.YEAR));
System.out.print("Time: ");
System.out.print(gcalendar.get(Calendar.HOUR) + ":");
System.out.print(gcalendar.get(Calendar.MINUTE) + ":");
System.out.println(gcalendar.get(Calendar.SECOND));
// 测试当前年份是否为闰年
if(gcalendar.isLeapYear(year)) {
System.out.println("当前年份是闰年");
}
else {
System.out.println("当前年份不是闰年");
}
}
}
以上实例编译运行结果如下:
Date: Apr 22 2009
Time: 11:25:27
当前年份不是闰年
关于Calender 类的完整列表,你可以参考标准的Java文档。
正则表达式定义了字符串的模式。
正则表达式可以用来搜索、编辑或处理文本。
正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。
Java正则表达式和Perl的是最为相似的。
java.util.regex包主要包括以下三个类:
pattern对象是一个正则表达式的编译表示。Pattern类没有公共构造方法。要创建一个Pattern对象,你必须首先调用其公共静态编译方法,它返回一个Pattern对象。该方法接受一个正则表达式作为它的第一个参数。
Matcher对象是对输入字符串进行解释和匹配操作的引擎。与Pattern类一样,Matcher也没有公共构造方法。你需要调用Pattern对象的matcher方法来获得一个Matcher对象。
PatternSyntaxException是一个非强制异常类,它表示一个正则表达式模式中的语法错误。
捕获组是把多个字符当一个单独单元进行处理的方法,它通过对括号内的字符分组来创建。
例如,正则表达式(dog) 创建了单一分组,组里包含"d","o",和"g"。
捕获组是通过从左至右计算其开括号来编号。例如,在表达式((A)(B(C))),有四个这样的组:
可以通过调用matcher对象的groupCount方法来查看表达式有多少个分组。groupCount方法返回一个int值,表示matcher对象当前有多个捕获组。
还有一个特殊的组(组0),它总是代表整个表达式。该组不包括在groupCount的返回值中。
下面的例子说明如何从一个给定的字符串中找到数字串:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches
{
public static void main( String args[] ){
// 按指定模式在字符串查找
String line = "This order was placed for QT3000! OK?";
String pattern = "(.*)(\\d+)(.*)";
// 创建 Pattern 对象
Pattern r = Pattern.compile(pattern);
// 现在创建 matcher 对象
Matcher m = r.matcher(line);
if (m.find( )) {
System.out.println("Found value: " + m.group(0) );
System.out.println("Found value: " + m.group(1) );
System.out.println("Found value: " + m.group(2) );
} else {
System.out.println("NO MATCH");
}
}
}
以上实例编译运行结果如下:
Found value: This order was placed for QT3000! OK?
Found value: This order was placed for QT300
Found value: 0
字符 | 说明 |
---|---|
\ | 将下一字符标记为特殊字符、文本、反向引用或八进制转义符。例如,"n"匹配字符"n"。"\n"匹配换行符。序列"\\"匹配"\","\("匹配"("。 |
^ | 匹配输入字符串开始的位置。如果设置了?RegExp?对象的?Multiline?属性,^ 还会与"\n"或"\r"之后的位置匹配。 |
$ | 匹配输入字符串结尾的位置。如果设置了?RegExp?对象的?Multiline?属性,$ 还会与"\n"或"\r"之前的位置匹配。 |
* | 零次或多次匹配前面的字符或子表达式。例如,zo* 匹配"z"和"zoo"。* 等效于 {0,}。 |
+ | 一次或多次匹配前面的字符或子表达式。例如,"zo+"与"zo"和"zoo"匹配,但与"z"不匹配。+ 等效于 {1,}。 |
? | 零次或一次匹配前面的字符或子表达式。例如,"do(es)?"匹配"do"或"does"中的"do"。? 等效于 {0,1}。 |
{n} | n?是非负整数。正好匹配?n?次。例如,"o{2}"与"Bob"中的"o"不匹配,但与"food"中的两个"o"匹配。 |
{n,} | n?是非负整数。至少匹配?n?次。例如,"o{2,}"不匹配"Bob"中的"o",而匹配"foooood"中的所有 o。"o{1,}"等效于"o+"。"o{0,}"等效于"o*"。 |
{n,m} | M?和?n?是非负整数,其中?n?<=?m。匹配至少?n?次,至多?m?次。例如,"o{1,3}"匹配"fooooood"中的头三个 o。'o{0,1}' 等效于 'o?'。注意:您不能将空格插入逗号和数字之间。 |
? | 当此字符紧随任何其他限定符(*、+、?、{n}、{n,}、{n,m})之后时,匹配模式是"非贪心的"。"非贪心的"模式匹配搜索到的、尽可能短的字符串,而默认的"贪心的"模式匹配搜索到的、尽可能长的字符串。例如,在字符串"oooo"中,"o+?"只匹配单个"o",而"o+"匹配所有"o"。 |
. | 匹配除"\r\n"之外的任何单个字符。若要匹配包括"\r\n"在内的任意字符,请使用诸如"[\s\S]"之类的模式。 |
(pattern) | 匹配?pattern?并捕获该匹配的子表达式。可以使用?$0…$9?属性从结果"匹配"集合中检索捕获的匹配。若要匹配括号字符 ( ),请使用"\("或者"\)"。 |
(?:pattern) | 匹配?pattern?但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配。这对于用"or"字符 (|) 组合模式部件的情况很有用。例如,'industr(?:y|ies) 是比 'industry|industries' 更经济的表达式。 |
(?=pattern) | 执行正向预测先行搜索的子表达式,该表达式匹配处于匹配?pattern?的字符串的起始点的字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?=95|98|NT|2000)' 匹配"Windows 2000"中的"Windows",但不匹配"Windows 3.1"中的"Windows"。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。 |
(?!pattern) | 执行反向预测先行搜索的子表达式,该表达式匹配不处于匹配?pattern?的字符串的起始点的搜索字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?!95|98|NT|2000)' 匹配"Windows 3.1"中的 "Windows",但不匹配"Windows 2000"中的"Windows"。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。 |
x|y | 匹配?x?或?y。例如,'z|food' 匹配"z"或"food"。'(z|f)ood' 匹配"zood"或"food"。 |
[xyz] | 字符集。匹配包含的任一字符。例如,"[abc]"匹配"plain"中的"a"。 |
[^xyz] | 反向字符集。匹配未包含的任何字符。例如,"[^abc]"匹配"plain"中"p","l","i","n"。 |
[a-z] | 字符范围。匹配指定范围内的任何字符。例如,"[a-z]"匹配"a"到"z"范围内的任何小写字母。 |
[^a-z] | 反向范围字符。匹配不在指定的范围内的任何字符。例如,"[^a-z]"匹配任何不在"a"到"z"范围内的任何字符。 |
\b | 匹配一个字边界,即字与空格间的位置。例如,"er\b"匹配"never"中的"er",但不匹配"verb"中的"er"。 |
\B | 非字边界匹配。"er\B"匹配"verb"中的"er",但不匹配"never"中的"er"。 |
\cx | 匹配?x?指示的控制字符。例如,\cM 匹配 Control-M 或回车符。x?的值必须在 A-Z 或 a-z 之间。如果不是这样,则假定 c 就是"c"字符本身。 |
\d | 数字字符匹配。等效于 [0-9]。 |
\D | 非数字字符匹配。等效于 [^0-9]。 |
\f | 换页符匹配。等效于 \x0c 和 \cL。 |
\n | 换行符匹配。等效于 \x0a 和 \cJ。 |
\r | 匹配一个回车符。等效于 \x0d 和 \cM。 |
\s | 匹配任何空白字符,包括空格、制表符、换页符等。与 [?\f\n\r\t\v] 等效。 |
\S | 匹配任何非空白字符。与 [^?\f\n\r\t\v] 等效。 |
\t | 制表符匹配。与 \x09 和 \cI 等效。 |
\v | 垂直制表符匹配。与 \x0b 和 \cK 等效。 |
\w | 匹配任何字类字符,包括下划线。与"[A-Za-z0-9_]"等效。 |
\W | 与任何非单词字符匹配。与"[^A-Za-z0-9_]"等效。 |
\xn | 匹配?n,此处的?n?是一个十六进制转义码。十六进制转义码必须正好是两位数长。例如,"\x41"匹配"A"。"\x041"与"\x04"&"1"等效。允许在正则表达式中使用 ASCII 代码。 |
\num | 匹配?num,此处的?num?是一个正整数。到捕获匹配的反向引用。例如,"(.)\1"匹配两个连续的相同字符。 |
\n | 标识一个八进制转义码或反向引用。如果 \n?前面至少有?n?个捕获子表达式,那么?n?是反向引用。否则,如果?n?是八进制数 (0-7),那么?n?是八进制转义码。 |
\nm | 标识一个八进制转义码或反向引用。如果 \nm?前面至少有?nm?个捕获子表达式,那么?nm?是反向引用。如果 \nm?前面至少有?n?个捕获,则?n?是反向引用,后面跟有字符?m。如果两种前面的情况都不存在,则 \nm?匹配八进制值?nm,其中?n?和?m?是八进制数字 (0-7)。 |
\nml | 当?n?是八进制数 (0-3),m?和?l?是八进制数 (0-7) 时,匹配八进制转义码?nml。 |
\un | 匹配?n,其中?n?是以四位十六进制数表示的 Unicode 字符。例如,\u00A9 匹配版权符号 (?)。 |
索引方法提供了有用的索引值,精确表明输入字符串中在哪能找到匹配:
序号 | 方法及说明 |
---|---|
1 | public int start() 返回以前匹配的初始索引。 |
2 | public int start(int group) ?返回在以前的匹配操作期间,由给定组所捕获的子序列的初始索引 |
3 | public int end() 返回最后匹配字符之后的偏移量。 |
4 | public int end(int group) 返回在以前的匹配操作期间,由给定组所捕获子序列的最后字符之后的偏移量。 |
研究方法用来检查输入字符串并返回一个布尔值,表示是否找到该模式:
序号 | 方法及说明 |
---|---|
1 | public boolean lookingAt() ?尝试将从区域开头开始的输入序列与该模式匹配。 |
2 | public boolean find() 尝试查找与该模式匹配的输入序列的下一个子序列。 |
3 | public boolean find(int start) 重置此匹配器,然后尝试查找匹配该模式、从指定索引开始的输入序列的下一个子序列。 |
4 | public boolean matches() 尝试将整个区域与模式匹配。 |
替换方法是替换输入字符串里文本的方法:
序号 | 方法及说明 |
---|---|
1 | public Matcher appendReplacement(StringBuffer sb, String replacement) 实现非终端添加和替换步骤。 |
2 | public StringBuffer appendTail(StringBuffer sb) 实现终端添加和替换步骤。 |
3 | public String replaceAll(String replacement) ?替换模式与给定替换字符串相匹配的输入序列的每个子序列。 |
4 | public String replaceFirst(String replacement) ?替换模式与给定替换字符串匹配的输入序列的第一个子序列。 |
5 | public static String quoteReplacement(String s) 返回指定字符串的字面替换字符串。这个方法返回一个字符串,就像传递给Matcher类的appendReplacement 方法一个字面字符串一样工作。 |
下面是一个对单词"cat"出现在输入字符串中出现次数进行计数的例子:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches
{
private static final String REGEX = "\\bcat\\b";
private static final String INPUT =
"cat cat cat cattie cat";
public static void main( String args[] ){
Pattern p = Pattern.compile(REGEX);
Matcher m = p.matcher(INPUT); // 获取 matcher 对象
int count = 0;
while(m.find()) {
count++;
System.out.println("Match number "+count);
System.out.println("start(): "+m.start());
System.out.println("end(): "+m.end());
}
}
}
以上实例编译运行结果如下:
Match number 1
start(): 0
end(): 3
Match number 2
start(): 4
end(): 7
Match number 3
start(): 8
end(): 11
Match number 4
start(): 19
end(): 22
可以看到这个例子是使用单词边界,以确保字母 "c" "a" "t" 并非仅是一个较长的词的子串。它也提供了一些关于输入字符串中匹配发生位置的有用信息。
Start方法返回在以前的匹配操作期间,由给定组所捕获的子序列的初始索引,end方法最后一个匹配字符的索引加1。
matches 和lookingAt 方法都用来尝试匹配一个输入序列模式。它们的不同是matches要求整个序列都匹配,而lookingAt 不要求。
这两个方法经常在输入字符串的开始使用。
我们通过下面这个例子,来解释这个功能:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches
{
private static final String REGEX = "foo";
private static final String INPUT = "fooooooooooooooooo";
private static Pattern pattern;
private static Matcher matcher;
public static void main( String args[] ){
pattern = Pattern.compile(REGEX);
matcher = pattern.matcher(INPUT);
System.out.println("Current REGEX is: "+REGEX);
System.out.println("Current INPUT is: "+INPUT);
System.out.println("lookingAt(): "+matcher.lookingAt());
System.out.println("matches(): "+matcher.matches());
}
}
以上实例编译运行结果如下:
Current REGEX is: foo
Current INPUT is: fooooooooooooooooo
lookingAt(): true
matches(): false
replaceFirst 和replaceAll 方法用来替换匹配正则表达式的文本。不同的是,replaceFirst 替换首次匹配,replaceAll 替换所有匹配。
下面的例子来解释这个功能:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches
{
private static String REGEX = "dog";
private static String INPUT = "The dog says meow. " +
"All dogs say meow.";
private static String REPLACE = "cat";
public static void main(String[] args) {
Pattern p = Pattern.compile(REGEX);
// get a matcher object
Matcher m = p.matcher(INPUT);
INPUT = m.replaceAll(REPLACE);
System.out.println(INPUT);
}
}
以上实例编译运行结果如下:
The cat says meow. All cats say meow.
Matcher 类也提供了appendReplacement 和appendTail 方法用于文本替换:
看下面的例子来解释这个功能:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches
{
private static String REGEX = "a*b";
private static String INPUT = "aabfooaabfooabfoob";
private static String REPLACE = "-";
public static void main(String[] args) {
Pattern p = Pattern.compile(REGEX);
// 获取 matcher 对象
Matcher m = p.matcher(INPUT);
StringBuffer sb = new StringBuffer();
while(m.find()){
m.appendReplacement(sb,REPLACE);
}
m.appendTail(sb);
System.out.println(sb.toString());
}
}
以上实例编译运行结果如下:
-foo-foo-foo-
PatternSyntaxException 是一个非强制异常类,它指示一个正则表达式模式中的语法错误。
PatternSyntaxException 类提供了下面的方法来帮助我们查看发生了什么错误。
序号 | 方法及说明 |
---|---|
1 | public String getDescription() 获取错误的描述。 |
2 | public int getIndex() ?获取错误的索引。 |
3 | public String getPattern() 获取错误的正则表达式模式。 |
4 | public String getMessage() 返回多行字符串,包含语法错误及其索引的描述、错误的正则表达式模式和模式中错误索引的可视化指示。 |
第十七章 Java 方法
在前面几个章节中我们经常使用到System.out.println(),那么它是什么呢?
println()是一个方法(Method),而System是系统类(Class),out是标准输出对象(Object)。这句话的用法是调用系统类System中的标准输出对象out中的方法println()。
Java方法是语句的集合,它们在一起执行一个功能。
方法是解决一类问题的步骤的有序组合
方法包含于类或对象中
方法在程序中被创建,在其他地方被引用
一般情况下,定义一个方法包含以下语法:
修饰符?返回值类型?方法名?(参数类型?参数名){
????...
????方法体
????...
????return?返回值;
}
方法包含一个方法头和一个方法体。下面是一个方法的所有部分:
修饰符:修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。
返回值类型 :方法可能会返回值。returnValueType是方法返回值的数据类型。有些方法执行所需的操作,但没有返回值。在这种情况下,returnValueType是关键字void。
方法名:是方法的实际名称。方法名和参数表共同构成方法签名。
参数类型:参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
方法体:方法体包含具体的语句,定义该方法的功能。
如:
public?static?int?age(int?birthday){...}
参数可以有多个:
static?float?interest(float?principal,?int?year){...}
注意:?在一些其它语言中方法指过程和函数。一个返回非void类型返回值的方法称为函数;一个返回void类型返回值的方法叫做过程。
下面的方法包含2个参数num1和num2,它返回这两个参数的最大值。
/**?返回两个整型变量数据的较大值?*/
public?static?int?max(int?num1,?int?num2)?{
???int?result;
???if?(num1?>?num2){
??????result?=?num1;
???}else{
??????result?=?num2;
}
???return?result;?
}
Java支持两种调用方法的方式,根据方法是否返回值来选择。
当程序调用一个方法时,程序的控制权交给了被调用的方法。当被调用方法的返回语句执行或者到达方法体闭括号时候交还控制权给程序。
当方法返回一个值的时候,方法调用通常被当做一个值。例如:
int?larger?=?max(30,?40);
如果方法返回值是void,方法调用一定是一条语句。例如,方法println返回void。下面的调用是个语句:
System.out.println("Welcome?to?Java!");
下面的例子演示了如何定义一个方法,以及如何调用它:
public?class?TestMax?{
???/**?主方法?*/
???public?static?void?main(String[]?args)?{
??????int?i?=?5;
??????int?j?=?2;
??????int?k?=?max(i,?j);
??????System.out.println("The?maximum?between?"?+?i?+
????????????????????"?and?"?+?j?+?"?is?"?+?k);
???}
???/**?返回两个整数变量较大的值?*/
???public?static?int?max(int?num1,?int?num2)?{
??????int?result;
??????if?(num1?>?num2){
?????????result?=?num1;
??????}else{
?????????result?=?num2;
}
??????return?result;?
???}
}
以上实例编译运行结果如下:
The?maximum?between?5?and?2?is?5
这个程序包含main方法和max方法。Main方法是被JVM调用的,除此之外,main方法和其它方法没什么区别。
main方法的头部是不变的,如例子所示,带修饰符public和static,返回void类型值,方法名字是main,此外带个一个String[]类型参数。String[]表明参数是字符串数组。
本节说明如何声明和调用一个void方法。
下面的例子声明了一个名为printGrade的方法,并且调用它来打印给定的分数。
public?class?TestVoidMethod?{
???public?static?void?main(String[]?args)?{
??????printGrade(78.5);
???}
???public?static?void?printGrade(double?score)?{
??????if?(score?>=?90.0)?{
?????????System.out.println('A');
??????}
??????else?if?(score?>=?80.0)?{
?????????System.out.println('B');
??????}
??????else?if?(score?>=?70.0)?{
?????????System.out.println('C');
??????}
??????else?if?(score?>=?60.0)?{
?????????System.out.println('D');
??????}
??????else?{
?????????System.out.println('F');
??????}
???}
}
以上实例编译运行结果如下:
C
这里printGrade方法是一个void类型方法,它不返回值。
一个void方法的调用一定是一个语句。 所以,它被在main方法第三行以语句形式调用。就像任何以分号结束的语句一样。
调用一个方法时候需要提供参数,你必须按照参数列表指定的顺序提供。
例如,下面的方法连续n次打印一个消息:
public?static?void?nPrintln(String?message,?int?n)?{
??for?(int?i?=?0;?i?<?n;?i++)?????System.out.println(message);?}
下面的例子演示按值传递的效果。
该程序创建一个方法,该方法用于交换两个变量。
public?class?TestPassByValue?{
???public?static?void?main(String[]?args)?{
??????int?num1?=?1;
??????int?num2?=?2;
??????System.out.println("Before?swap?method,?num1?is?"?+
??????????????????????????num1?+?"?and?num2?is?"?+?num2);
??????//?调用swap方法
??????swap(num1,?num2);
??????System.out.println("After?swap?method,?num1?is?"?+
?????????????????????????num1?+?"?and?num2?is?"?+?num2);
???}
???/**?交换两个变量的方法?*/
???public?static?void?swap(int?n1,?int?n2)?{
??????System.out.println("\tInside?the?swap?method");
??????System.out.println("\t\tBefore?swapping?n1?is?"?+?n1
???????????????????????????+?"?n2?is?"?+?n2);
??????//?交换?n1?与?n2的值
??????int?temp?=?n1;
??????n1?=?n2;
??????n2?=?temp;
??????System.out.println("\t\tAfter?swapping?n1?is?"?+?n1
???????????????????????????+?"?n2?is?"?+?n2);
???}
}
以上实例编译运行结果如下:
Before?swap?method,?num1?is?1?and?num2?is?2
????????Inside?the?swap?method
????????????????Before?swapping?n1?is?1?n2?is?2
????????????????After?swapping?n1?is?2?n2?is?1
After?swap?method,?num1?is?1?and?num2?is?2
传递两个参数调用swap方法。有趣的是,方法被调用后,实参的值并没有改变。
上面使用的max方法仅仅适用于int型数据。但如果你想得到两个浮点类型数据的最大值呢?
解决方法是创建另一个有相同名字但参数不同的方法,如下面代码所示:
public?static?double?max(double?num1,?double?num2)?{
??if?(num1?>?num2){
????return?num1;
??}else{
????return?num2;
}
}
如果你调用max方法时传递的是int型参数,则 int型参数的max方法就会被调用;
如果传递的是double型参数,则double类型的max方法体会被调用,这叫做方法重载;
就是说一个类的两个方法拥有相同的名字,但是有不同的参数列表。
Java编译器根据方法签名判断哪个方法应该被调用。
方法重载可以让程序更清晰易读。执行密切相关任务的方法应该使用相同的名字。
重载的方法必须拥有不同的参数列表。你不能仅仅依据修饰符或者返回类型的不同来重载方法。
变量的范围是程序中该变量可以被引用的部分。
方法内定义的变量被称为局部变量。
局部变量的作用范围从声明开始,直到包含它的块结束。
局部变量必须声明才可以使用。
方法的参数范围涵盖整个方法。参数实际上是一个局部变量。
for循环的初始化部分声明的变量,其作用范围在整个循环。
但循环体内声明的变量其适用范围是从它声明到循环体结束。它包含如下所示的变量声明:
你可以在一个方法里,不同的非嵌套块中多次声明一个具有相同的名称局部变量,但你不能在嵌套块内两次声明局部变量。
有时候你希望运行一个程序时候再传递给它消息。这要靠传递命令行参数给main()函数实现。
命令行参数是在执行程序时候紧跟在程序名字后面的信息。
下面的程序打印所有的命令行参数:
public?class?CommandLine?{
???public?static?void?main(String?args[]){?
??????for(int?i=0;?i<args.length;?i++){??????????
System.out.println("args ["?+?i?+?"]:?"?+?args[i]);
??????}
????}
?}
如下所示,运行这个程序:
java?CommandLine?this?is?a?command?line?200?-100
运行结果如下:
args[0]:?this
args[1]:?is
args[2]:?a
args[3]:?command
args[4]:?line
args[5]:?200
args[6]:?-100
当一个对象被创建时候,构造方法用来初始化该对象。构造方法和它所在类的名字相同,但构造方法没有返回值。
通常会使用构造方法给一个类的实例变量赋初值,或者执行其它必要的步骤来创建一个完整的对象。
不管你是否自定义构造方法,所有的类都有构造方法,因为Java自动提供了一个默认构造方法,它把所有成员初始化为0。
一旦你定义了自己的构造方法,默认构造方法就会失效。
下面是一个使用构造方法的例子:
//?一个简单的构造函数
static class?MyClass?{
???int?x;
???
???//?以下是构造函数
???MyClass()?{
??????x?=?10;
???}
}
你可以像下面这样调用构造方法来初始化一个对象:
public?class?ConsDemo?{
???public?static?void?main(String?args[])?{
??????MyClass?t1?=?new?MyClass();
??????MyClass?t2?=?new?MyClass();
??????System.out.println(t1.x?+?"?"?+?t2.x);
???}
}
大多时候需要一个有参数的构造方法。
下面是一个使用构造方法的例子:
//?一个简单的构造函数
class?MyClass?{
???int?x;
???
???//?以下是构造函数
???MyClass(int?i?)?{
??????x?=?i;
???}
}
你可以像下面这样调用构造方法来初始化一个对象:
public?class?ConsDemo?{
???public?static?void?main(String?args[])?{
??????MyClass?t1?=?new?MyClass(?10?);
??????MyClass?t2?=?new?MyClass(?20?);
??????System.out.println(t1.x?+?"?"?+?t2.x);
???}
}
运行结果如下:
10?20
JDK 1.5 开始,Java支持传递同类型的可变参数给一个方法。
方法的可变参数的声明如下所示:
typeName...?parameterName
在方法声明中,在指定参数类型后加一个省略号(...) 。
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
public?class?VarargsDemo?{
???public?static?void?main(String?args[])?{
??????//?调用可变参数的方法
??printMax(34,?3,?3,?2,?56.5);
??????printMax(new?double[]{1,?2,?3});
???}
???public?static?void?printMax(?double...?numbers)?{
???if?(numbers.length?==?0)?{
??????System.out.println("No?argument?passed");
??????return;
???}
???double?result?=?numbers[0];
???for?(int?i?=?1;?i?<??numbers.length;?i++)
???????if?(numbers[i]?>??result){
?????? result?=?numbers[i];
}
??????System.out.println("The?max?value?is?"?+?result);
???}
}
以上实例编译运行结果如下:
The?max?value?is?56.5
The?max?value?is?3.0
Java允许定义这样的方法,它在对象被垃圾收集器析构(回收)之前调用,这个方法叫做finalize( ),它用来清除回收对象。
例如,你可以使用finalize()来确保一个对象打开的文件被关闭了。
在finalize()方法里,你必须指定在对象销毁时候要执行的操作。
finalize()一般格式是:
protected?void?finalize()
{
???//?在这里终结代码
}
关键字protected是一个限定符,它确保finalize() 方法不会被该类以外的代码调用。
当然,Java的内存回收可以由JVM来自动完成。如果你手动使用,则可以使用上面的方法。
public?class?FinalizationDemo?{??
????public?static?void?main(String[]?args)?{??
????????Cake?c1?=?new?Cake(1);??
????????Cake?c2?=?new?Cake(2);??
????????Cake?c3?=?new?Cake(3);??
??????????
????????c2?=?c3?=?null;??
????????System.gc();?//调用Java垃圾收集器
????}??
}??
??
class?Cake?extends?Object?{??
????private?int?id;??
????public?Cake(int?id)?{??
????????this.id?=?id;??
????????System.out.println("Cake?Object?"?+?id?+?"is?created");??
????}??
??????
????protected?void?finalize()?throws?java.lang.Throwable?{??
????????super.finalize();??
????????System.out.println("Cake?Object?"?+?id?+?"is?disposed");??
????}??
}
运行以上代码,输出结果如下:
C:\1>java?FinalizationDemo??
Cake?Object?1is?created??
Cake?Object?2is?created??
Cake?Object?3is?created??
Cake?Object?3is?disposed??
Cake?Object?2is?disposed
Java.io 包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标。
Java.io 包中的流支持很多种格式,比如:基本类型、对象、本地化字符集等等。
一个流可以理解为一个数据的序列。输入流表示从一个源读取数据,输出流表示向一个目标写数据。
Java为I/O 提供了强大的而灵活的支持,使其更广泛地应用到文件传输和网络编程中。
但本节讲述最基本的和流与 I/O 相关的功能。我们将通过一个个例子来学习这些功能。
Java 的控制台输入由 System.in 完成。
为了获得一个绑定到控制台的字符流,你可以把 System.in 包装在一个 BufferedReader 对象中来创建一个字符流。
下面是创建 BufferedReader 的基本语法:
BufferedReader?br?=?new?BufferedReader(new InputStreamReader(System.in));
BufferedReader 对象创建后,我们便可以使用 read() 方法从控制台读取一个字符,或者用 readLine() 方法读取一个字符串。
从 BufferedReader 对象读取一个字符要使用 read() 方法,它的语法如下:
int?read()?throws?IOException
每次调用 read() 方法,它从输入流读取一个字符并把该字符作为整数值返回。 当流结束的时候返回 -1。该方法抛出 IOException。
下面的程序示范了用 read() 方法从控制台不断读取字符直到用户输入 "q" 。
//?使用?BufferedReader?在控制台读取字符
import?java.io.*;
public?class?BRRead?{
???public?static?void?main(String?args[])?throws?IOException
???{
??????char?c;
??????//?使用?System.in?创建?BufferedReader?
??????BufferedReader?br?=?new?BufferedReader(new?
?????????????????????????InputStreamReader(System.in));
??????System.out.println("输入字符, 按下 'q' 键退出.");
??????//?读取字符
??????do?{
?????????c?=?(char)?br.read();
?????????System.out.println(c);
??????}?while(c?!=?'q');
???}
}
以上实例编译运行结果如下:
输入字符, 按下 'q' 键退出.
123abcq
1
2
3
a
b
c
q
从标准输入读取一个字符串需要使用 BufferedReader 的 readLine() 方法。
它的一般格式是:
String?readLine()?throws?IOException
下面的程序读取和显示字符行直到你输入了单词 "end"。
//?使用?BufferedReader?在控制台读取字符
import?java.io.*;
public?class?BRReadLines?{
???public?static?void?main(String?args[])?throws?IOException
???{
??????//?使用?System.in?创建?BufferedReader?
??????BufferedReader?br?=?new?BufferedReader(new
??????????????????????????????InputStreamReader(System.in));
??????String?str;
??????System.out.println("Enter?lines?of?text.");
??????System.out.println("Enter?'end'?to?quit.");
??????do?{
?????????str?=?br.readLine();
?????????System.out.println(str);
??????}?while(!str.equals("end"));
???}
}
以上实例编译运行结果如下:
Enter?lines?of?text.
Enter?'end'?to?quit.
This?is?line?one
This?is?line?one
This?is?line?two
This?is?line?two
end
end
在此前已经介绍过,控制台的输出由 print() 和 println() 完成。这些方法都由类 PrintStream 定义,System.out 是该类对象的一个引用。
PrintStream 继承了 OutputStream 类,并且实现了方法 write()。这样,write() 也可以用来往控制台写操作。
PrintStream 定义 write() 的最简单格式如下所示:
void?write(int?byteval)
该方法将 byteval 的低八位字节写到流中。
下面的例子用 write() 把字符 "A" 和紧跟着的换行符输出到屏幕:
import?java.io.*;
//?演示?System.out.write().
public?class?WriteDemo?{
???public?static?void?main(String?args[])?{
??????int?b;?
??????b?=?'A';
??????System.out.write(b);
??????System.out.write('\n');
???}
}
运行以上实例在输出窗口输出 "A" 字符
A
注意:write() 方法不经常使用,因为 print() 和 println() 方法用起来更为方便。
如前所述,一个流被定义为一个数据序列。输入流用于从源读取数据,输出流用于向目标写数据。
下图是一个描述输入流和输出流的类层次图。
下面将要讨论的两个重要的流是 FileInputStream 和 FileOutputStream:
该流用于从文件读取数据,它的对象可以用关键字 new 来创建。
有多种构造方法可用来创建对象。
可以使用字符串类型的文件名来创建一个输入流对象来读取文件:
InputStream?f?=?new?FileInputStream("C:/java/hello");
也可以使用一个文件对象来创建一个输入流对象来读取文件。我们首先得使用 File() 方法来创建一个文件对象:
File?f?=?new?File("C:/java/hello");
InputStream?f?=?new?FileInputStream(f);
创建了 InputStream 对象,就可以使用下面的方法来读取流或者进行其他的流操作。
序号 | 方法及描述 |
---|---|
1 | public void close() throws IOException{} 关闭此文件输入流并释放与此流有关的所有系统资源。抛出 IOException 异常。 |
2 | protected void finalize()throws IOException {} 这个方法清除与该文件的连接。确保在不再引用文件输入流时调用其 close 方法。抛出 IOException 异常。 |
3 | public int read(int r)throws IOException{} 这个方法从 InputStream 对象读取指定字节的数据。返回为整数值。返回下一字节数据,如果已经到结尾则返回 -1。 |
4 | public int read(byte[] r) throws IOException{} 这个方法从输入流读取 r.length 长度的字节。返回读取的字节数。如果是文件结尾则返回 -1。 |
5 | public int available() throws IOException{} 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取的字节数。返回一个整数值。 |
除了 InputStream 外,还有一些其他的输入流,更多的细节参考下面链接:
该类用来创建一个文件并向文件中写数据。
如果该流在打开文件进行输出前,目标文件不存在,那么该流会创建该文件。
有两个构造方法可以用来创建 FileOutputStream 对象。
使用字符串类型的文件名来创建一个输出流对象:
OutputStream?f?=?new?FileOutputStream("C:/java/hello")
也可以使用一个文件对象来创建一个输出流来写文件。我们首先得使用 File() 方法来创建一个文件对象:
File?f?=?new?File("C:/java/hello");
OutputStream?f?=?new?FileOutputStream(f);
创建 OutputStream 对象完成后,就可以使用下面的方法来写入流或者进行其他的流操作。
序号 | 方法及描述 |
---|---|
1 | public void close() throws IOException{} 关闭此文件输入流并释放与此流有关的所有系统资源。抛出 IOException 异常。 |
2 | protected void finalize()throws IOException {} 这个方法清除与该文件的连接。确保在不再引用文件输入流时调用其 close 方法。抛出 IOException 异常。 |
3 | public void write(int w)throws IOException{} 这个方法把指定的字节写到输出流中。 |
4 | public void write(byte[] w) 把指定数组中 w.length 长度的字节写到 OutputStream 中。 |
除了 OutputStream 外,还有一些其他的输出流,更多的细节参考下面链接:
下面是一个演示 InputStream 和 OutputStream 用法的例子:
import java.io.*;
public class fileStreamTest {
public static void main(String args[]) {
try {
byte bWrite[] = { 11, 21, 3, 40, 5 };
OutputStream os = new FileOutputStream("test.txt");
for (int x = 0; x < bWrite.length; x++) {
os.write(bWrite[x]); // writes the bytes
}
os.close();
InputStream is = new FileInputStream("test.txt");
int size = is.available();
for (int i = 0; i < size; i++) {
System.out.print((char) is.read() + " ");
}
is.close();
} catch (IOException e) {
System.out.print("Exception");
}
}
}
上面的程序首先创建文件 test.txt,并把给定的数字以二进制形式写进该文件,同时输出到控制台上。
以上代码由于是二进制写入,可能存在乱码,你可以使用以下代码实例来解决乱码问题:
//文件名?:fileStreamTest2.java
import?java.io.*;
public?class?fileStreamTest2{
public?static?void?main(String[]?args)?throws?IOException?{
File?f?=?new?File("a.txt");
FileOutputStream?fop?=?new?FileOutputStream(f);
//?构建FileOutputStream对象,文件不存在会自动新建
OutputStreamWriter?writer?=?new?OutputStreamWriter(fop,?"UTF-8");
//?构建OutputStreamWriter对象,参数可以指定编码,默认为操作系统默认编码,windows上是gbk
writer.append("中文输入");
//?写入到缓冲区
writer.append("\r\n");
//换行
writer.append("English");
//?刷新缓存冲,写入到文件,如果下面已经没有写入的内容了,直接close也会写入
writer.close();
//关闭写入流,同时会把缓冲区内容写入文件,所以上面的注释掉
fop.close();
//?关闭输出流,释放系统资源
FileInputStream?fip?=?new?FileInputStream(f);
//?构建FileInputStream对象
InputStreamReader?reader?=?new?InputStreamReader(fip,?"UTF-8");
//?构建InputStreamReader对象,编码与写入相同
StringBuffer?sb?=?new?StringBuffer();
while?(reader.ready())?{
sb.append((char)?reader.read());
//?转成char加到StringBuffer对象中
}
System.out.println(sb.toString());
reader.close();
//?关闭读取流
fip.close();
//?关闭输入流,释放系统资源
}
}
还有一些关于文件和 I/O 的类,我们也需要知道:
File 类中有两个方法可以用来创建文件夹:
mkdir( )?方法创建一个文件夹,成功则返回 true,失败则返回 false。失败表明 File 对象指定的路径已经存在,或者由于整个路径还不存在,该文件夹不能被创建。
mkdirs( )?方法创建一个文件夹和它的所有父文件夹。
下面的例子创建 "/tmp/user/java/bin" 文件夹:
import?java.io.File;
public?class?CreateDir?{
???public?static?void?main(String?args[])?{
??????String?dirname?=?"/tmp/user/java/bin";
??????File?d?=?new?File(dirname);
??????//?现在创建目录
??????d.mkdirs();
??}
}
编译并执行上面代码来创建目录 "/tmp/user/java/bin"。
注意:Java 在 UNIX 和 Windows 自动按约定分辨文件路径分隔符。如果你在 Windows 版本的 Java 中使用分隔符(/) ,路径依然能够被正确解析。
一个目录其实就是一个 File 对象,它包含其他文件和文件夹。
如果创建一个 File 对象并且它是一个目录,那么调用 isDirectory( ) 方法会返回 true。
可以通过调用该对象上的 list() 方法,来提取它包含的文件和文件夹的列表。
下面展示的例子说明如何使用 list() 方法来检查一个文件夹中包含的内容:
import?java.io.File; public?class?DirList?{ ???public?static?void?main(String?args[])?{ ??????String?dirname?=?"/tmp"; ??????File?f1?=?new?File(dirname); ??????if?(f1.isDirectory())?{ ?????????System.out.println(?"Directory?of?"?+?dirname); ?????????String?s[]?=?f1.list(); ?????????for?(int?i=0;?i?<?s.length;?i++)?{????????????? ????????????File?f?=?new?File(dirname?+?"/"?+?s[i]);????????????? ????????????if?(f.isDirectory())?{???????????????? ????????????????System.out.println(s[i]?+?"是一个目录");????????????? ????????????}?else?{ ????????????????System.out.println(s[i]?+?"是一个文件"); ?????????????} ????????} ?????}?else?{ ??????????System.out.println(dirname?+?"不是一个目录");
?????} ??} }
以上实例编译运行结果如下:
目录 /tmp
bin 是一个目录
lib 是一个目录
demo 是一个目录
test.txt 是一个文件
README 是一个文件
index.html 是一个文件
include 是一个目录
删除文件可以使用?java.io.File.delete()?方法。
以下代码会删除目录?/tmp/java/,需要注意的是当删除某一目录时,必须保证该目录下没有其他文件才能正确删除,否则将删除失败。
测试目录结构:
/tmp/java/ |-- 1.log |-- test
import java.io.File;
public class DeleteFileDemo {
public static void main(String args[]) {
// 这里修改为自己的测试目录
File folder = new File("/tmp/java/");
deleteFolder(folder);
}
// 删除文件及目录
public static void deleteFolder(File folder) {
File[] files = folder.listFiles();
if (files != null) {
for (File f : files) {
if (f.isDirectory()) {
deleteFolder(f);
} else {
f.delete();
}
}
}
folder.delete();
}
}
java.util.Scanner是Java5的新特征,我们可以通过 Scanner 类来获取用户的输入。
下面是创建 Scanner 对象的基本语法:
Scanner s = new Scanner(System.in);
接下来我们演示一个最简单的的数据输入,并通过 Scanner 类的 next() 与 nextLine() 方法获取输入的字符串,在读取前我们一般需要 使用 hasNext 与 hasNextLine 判断是否还有输入的数据:
import java.util.Scanner;
public class ScannerDemo {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
// 从键盘接收数据
//next方式接收字符串
System.out.println("next方式接收:");
// 判断是否还有输入
if(scan.hasNext()){
String str1 = scan.next();
System.out.println("输入的数据为:"+str1);
}
}
}
执行以上程序输出结果为:
$ javac ScannerDemo.java
$ java ScannerDemo
next方式接收:
youj com
输入的数据为:youj
可以看到 com 字符串并未输出,接下来我们看 nextLine。
import java.util.Scanner;
public class ScannerDemo {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
// 从键盘接收数据
//nextLine方式接收字符串
System.out.println("nextLine方式接收:");
// 判断是否还有输入
if(scan.hasNextLine()){
String str2 = scan.nextLine();
System.out.println("输入的数据为:"+str2);
}
}
}
执行以上程序输出结果为:
$ javac ScannerDemo.java
$ java ScannerDemo
nextLine方式接收:
youj com
输入的数据为:youj com
可以看到 com 字符串输出。
next():
nextLine():
如果要输入int或float类型的数据,在Scanner类中也有支持,但是在输入之前最好先使用 hasNextXxx() 方法进行验证,再使用 nextXxx() 来读取:
import java.util.Scanner;
public class ScannerDemo {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
// 从键盘接收数据
int i = 0 ;
float f = 0.0f ;
System.out.print("输入整数:");
if(scan.hasNextInt()){
// 判断输入的是否是整数
i = scan.nextInt() ;
// 接收整数
System.out.println("整数数据:" + i) ;
}else{
// 输入错误的信息
System.out.println("输入的不是整数!") ;
}
System.out.print("输入小数:");
if(scan.hasNextFloat()){
// 判断输入的是否是小数
f = scan.nextFloat() ;
// 接收小数
System.out.println("小数数据:" + f) ;
}else{
// 输入错误的信息
System.out.println("输入的不是小数!") ;
}
}
}
执行以上程序输出结果为:
$ javac ScannerDemo.java
$ java ScannerDemo
输入整数:12
整数数据:12
输入小数:1.2
小数数据:1.2
以下实例我们可以输入多个数字,并求其总和与平均数,每输入一个数字用回车确认,通过输入非数字来结束输入并输出执行结果:
import java.util.Scanner;
class ScannerDemo
{
public static void main(String[] args)
{
Scanner scan = new Scanner(System.in);
double sum = 0;
int m = 0;
while(scan.hasNextDouble())
{
double x = scan.nextDouble();
m = m + 1;
sum = sum + x;
}
System.out.println(m+"个数的和为"+sum);
System.out.println(m+"个数的平均值是"+(sum/m));
}
}
执行以上程序输出结果为:
$ javac ScannerDemo.java
$ java ScannerDemo
12
23
15
21.4
end
4个数的和为71.4
4个数的平均值是17.85
程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常。异常发生时,是任程序自生自灭,立刻退出终止,还是输出错误给用户?或者用C语言风格:用函数返回值作为执行状态?在Java中即,Java在编译或运行或者运行过程中出现的错误。?
异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。
比如说,你的代码少了一个分号,那么运行出来结果是提示是错误 java.lang.Error;如果你用 System.out.println(11/0),那么你是因为你用 0 做了除数,会抛出 java.lang.ArithmeticException 的异常。
异常发生的原因有很多,通常包含以下几大类:
用户输入了非法数据。
要打开的文件不存在。
网络通信时连接中断,或者 JVM 内存溢出。
这些异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的。-
要理解 Java 异常处理是如何工作的,你需要掌握以下三种类型的异常:
检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
运行时异常:?运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
错误:?错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
所有的异常类是从 java.lang.Exception 类继承的子类。
Exception 类是 Throwable 类的子类。除了Exception类外,Throwable 还有一个子类 Error 。
Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在 Java 程序处理的范畴之外。
Error 用来指示运行时环境发生的错误。
例如,JVM 内存溢出。一般地,程序不会从错误中恢复。
异常类有两个主要的子类:IOException 类和 RuntimeException 类。
在 Java 内置类中(接下来会说明),有大部分常用检查性和非检查性异常。
Java 语言定义了一些异常类在 java.lang 标准包中。
标准运行时异常类的子类是最常见的异常类。由于 java.lang 包是默认加载到所有的 Java 程序的,所以大部分从运行时异常类继承而来的异常都可以直接使用。
Java 根据各个类库也定义了一些其他的异常,下面的表中列出了 Java 的非检查性异常。
异常 | 描述 |
---|---|
ArithmeticException | 当出现异常的运算条件时,抛出此异常。例如,一个整数"除以零"时,抛出此类的一个实例。 |
ArrayIndexOutOfBoundsException | 用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。 |
ArrayStoreException | 试图将错误类型的对象存储到一个对象数组时抛出的异常。 |
ClassCastException | 当试图将对象强制转换为不是实例的子类时,抛出该异常。 |
IllegalArgumentException | 抛出的异常表明向方法传递了一个不合法或不正确的参数。 |
IllegalMonitorStateException | 抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。 |
IllegalStateException | 在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下。 |
IllegalThreadStateException | 线程没有处于请求操作所要求的适当状态时抛出的异常。 |
IndexOutOfBoundsException | 指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。 |
NegativeArraySizeException | 如果应用程序试图创建大小为负的数组,则抛出该异常。 |
NullPointerException | 当应用程序试图在需要对象的地方使用?null ?时,抛出该异常 |
NumberFormatException | 当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。 |
SecurityException | 由安全管理器抛出的异常,指示存在安全侵犯。 |
StringIndexOutOfBoundsException | 此异常由?String ?方法抛出,指示索引或者为负,或者超出字符串的大小。 |
UnsupportedOperationException | 当不支持请求的操作时,抛出该异常。 |
下面的表中列出了 Java 定义在 java.lang 包中的检查性异常类。
异常 | 描述 |
---|---|
ClassNotFoundException | 应用程序试图加载类时,找不到相应的类,抛出该异常。 |
CloneNotSupportedException | 当调用?Object ?类中的?clone ?方法克隆对象,但该对象的类无法实现?Cloneable ?接口时,抛出该异常。 |
IllegalAccessException | 拒绝访问一个类的时候,抛出该异常。 |
InstantiationException | 当试图使用?Class ?类中的?newInstance ?方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。 |
InterruptedException | 一个线程被另一个线程中断,抛出该异常。 |
NoSuchFieldException | 请求的变量不存在 |
NoSuchMethodException | 请求的方法不存在 |
下面的列表是 Throwable 类的主要方法:
序号 | 方法及说明 |
---|---|
1 | public String getMessage() 返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了。 |
2 | public Throwable getCause() 返回一个Throwable 对象代表异常原因。 |
3 | public String toString() 使用getMessage()的结果返回类的串级名字。 |
4 | public void printStackTrace() 打印toString()结果和栈层次到System.err,即错误输出流。 |
5 | public StackTraceElement [] getStackTrace() 返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底。 |
6 | public Throwable fillInStackTrace() 用当前的调用栈层次填充Throwable 对象栈层次,添加到栈层次任何先前信息中。 |
使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。
try/catch 代码块中的代码称为保护代码,使用? try/catch 的语法如下:
try
{
???//?程序代码
}catch(ExceptionName?e1)
{
???//Catch?块
}
Catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。
如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样。
下面的例子中声明有两个元素的一个数组,当代码试图访问数组的第三个元素的时候就会抛出一个异常。
//?文件名?:?ExcepTest.java
import?java.io.*;
public?class?ExcepTest{
???public?static?void?main(String?args[]){
??????try{
?????????int?a[]?=?new?int[2];
?????????System.out.println("Access?element?three?:"?+?a[3]);
??????}catch(ArrayIndexOutOfBoundsException?e){
?????????System.out.println("Exception?thrown??:"?+?e);
??????}
??????System.out.println("Out?of?the?block");
???}
}
以上代码编译运行输出结果如下:
Exception?thrown??:java.lang.ArrayIndexOutOfBoundsException:?3
Out?of?the?block
一个 try 代码块后面跟随多个 catch 代码块的情况就叫多重捕获。
多重捕获块的语法如下所示:
?try{
????//?程序代码
?}catch(异常类型1?异常的变量名1){
????//?程序代码
?}catch(异常类型2?异常的变量名2){
????//?程序代码
?}catch(异常类型2?异常的变量名2){
????//?程序代码
?}
上面的代码段包含了 3 个 catch 块。
可以在 try 语句后面添加任意数量的 catch 块。
如果保护代码中发生异常,异常被抛给第一个 catch 块。
如果抛出异常的数据类型与 ExceptionType1 匹配,它在这里就会被捕获。
如果不匹配,它会被传递给第二个 catch 块。
如此,直到异常被捕获或者通过所有的 catch 块。
该实例展示了怎么使用多重 try/catch。
try
{
???file?=?new?FileInputStream(fileName);
???x?=?(byte)?file.read();
}catch(IOException?i)
{
???i.printStackTrace();
???return?-1;
}catch(FileNotFoundException?f)?//Not?valid!
{
???f.printStackTrace();
???return?-1;
}
如果一个方法没有捕获一个检查性异常,那么该方法必须使用 throws 关键字来声明。throws 关键字放在方法签名的尾部。
也可以使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。
下面方法的声明抛出一个 RemoteException 异常:
import?java.io.*;
public?class?className
{
???public?void?deposit(double?amount)?throws?RemoteException
???{
??????//?Method?implementation
??????throw?new?RemoteException();
???}
???//Remainder?of?class?definition
}
一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。
例如,下面的方法声明抛出 RemoteException 和 InsufficientFundsException:
import?java.io.*;
public?class?className
{
???public?void?withdraw(double?amount)?throws?RemoteException,
??????????????????????????????InsufficientFundsException
???{
???????//?Method?implementation
???}
???//Remainder?of?class?definition
}
finally 关键字用来创建在 try 代码块后面执行的代码块。
无论是否发生异常,finally 代码块中的代码总会被执行。
在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。
finally 代码块出现在 catch 代码块最后,语法如下:
?try{
????//?程序代码
?}catch(异常类型1?异常的变量名1){
????//?程序代码
?}catch(异常类型2?异常的变量名2){
????//?程序代码
?}finally{
????//?程序代码
?}
?public?class?ExcepTest{
???public?static?void?main(String?args[]){
??????int?a[]?=?new?int[2];
??????try{
?????????System.out.println("Access?element?three?:"?+?a[3]);
??????}catch(ArrayIndexOutOfBoundsException?e){
?????????System.out.println("Exception?thrown??:"?+?e);
??????}
??????finally{
?????????a[0]?=?6;
?????????System.out.println("First?element?value:?"?+a[0]);
?????????System.out.println("The?finally?statement?is?executed");
??????}
???}
}
以上实例编译运行结果如下:
Exception?thrown??:java.lang.ArrayIndexOutOfBoundsException:?3
First?element?value:?6
The?finally?statement?is?executed
注意下面事项:
catch 不能独立于 try 存在。
在 try/catch 后面添加 finally 块并非强制性要求的。
try 代码后不能既没 catch 块也没 finally 块。
try, catch, finally 块之间不能添加任何代码。
在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。
所有异常都必须是 Throwable 的子类。
如果希望写一个检查性异常类,则需要继承 Exception 类。
如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
可以像下面这样定义自己的异常类:
class?MyException?extends?Exception{
}
只继承 Exception 类来创建的异常类是检查性异常类。
下面的 InsufficientFundsException 类是用户定义的异常类,它继承自 Exception。
一个异常类和其它任何类一样,包含有变量和方法。
//?文件名InsufficientFundsException.java
import?java.io.*;
public?class?InsufficientFundsException?extends?Exception
{
???private?double?amount;
???public?InsufficientFundsException(double?amount)
???{
??????this.amount?=?amount;
???}?
???public?double?getAmount()
???{
??????return?amount;
???}
}
为了展示如何使用我们自定义的异常类,
在下面的 CheckingAccount 类中包含一个 withdraw() 方法抛出一个 InsufficientFundsException 异常。
//?文件名称?CheckingAccount.java
import?java.io.*;
public?class?CheckingAccount
{
???private?double?balance;
???private?int?number;
???public?CheckingAccount(int?number)
???{
??????this.number?=?number;
???}
???public?void?deposit(double?amount)
???{
??????balance?+=?amount;
???}
???public?void?withdraw(double?amount)?throws
??????????????????????????????InsufficientFundsException
???{
??????if(amount?<=?balance)
???????{
??????????balance?-=?amount;
???????}
???????else
???????{
??????????double?needs?=?amount?-?balance;
??????????throw?new?InsufficientFundsException(needs);
???????}
????}
????public?double?getBalance()
????{
???????return?balance;
????}
????public?int?getNumber()
????{
???????return?number;
????}
?}
下面的 BankDemo 程序示范了如何调用 CheckingAccount 类的 deposit() 和 withdraw() 方法。
//文件名称?BankDemo.java
public?class?BankDemo
{
???public?static?void?main(String?[]?args)
???{
??????CheckingAccount?c?=?new?CheckingAccount(101);
??????System.out.println("Depositing?$500...");
??????c.deposit(500.00);
??????try
??????{
?????????System.out.println("\nWithdrawing?$100...");
?????????c.withdraw(100.00);
?????????System.out.println("\nWithdrawing?$600...");
?????????c.withdraw(600.00);
??????}catch(InsufficientFundsException?e)
??????{
?????????System.out.println("Sorry,?but?you?are?short?$"
??????????????????????????????????+?e.getAmount());
?????????e.printStackTrace();
??????}
????}
}
编译上面三个文件,并运行程序 BankDemo,得到结果如下所示:
Depositing?$500...
Withdrawing?$100...
Withdrawing?$600...
Sorry,?but?you?are?short?$200.0
InsufficientFundsException
????????at?CheckingAccount.withdraw(CheckingAccount.java:25)
????????at?BankDemo.main(BankDemo.java:13)
在 Java 中定义了两种类型的异常和错误。
JVM(Java虚拟机) 异常:由 JVM 抛出的异常或错误。例如:NullPointerException类
,ArrayIndexOutOfBoundsException类
,ClassCastException类
。
程序级异常:由程序或者API程序抛出的异常。例如?IllegalArgumentException类
,IllegalStateException类
。
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 13:26:24- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |