导图社区 Java教程
本思维导图包括Java基础知识(如基础语法概念、基本数据类型、控制语句、常用类、方法、数组、异常处理等)
编辑于2021-12-25 17:30:47Java教程
Java基础
Java前期准备
Java简介
主要特性
Java 语言是简单的
丢弃了操作符重载、多继承、自动的强制类型转换
Java 语言不使用指针,而是引用
提供了自动分配和回收内存空间,使得程序员不必为内存管理而担忧
Java 语言是面向对象的
提供类、接口和继承等面向对象的特性
只支持类之间的单继承,但支持接口之间的多继承
支持类与接口之间的实现机制(关键字为 implements)
Java 语言全面支持动态绑定,而 C++语言只对虚函数使用动态绑定
Java语言是分布式的
接口中有一个网络应用编程接口(java net)
提供了用于网络应用编程的类库,包括 URL、 URLConnection、Socket、ServerSocket 等
Java 的 RMI(远程方法激活)机制也是开发分布式应用的重要手段
Java 语言是健壮的
强类型机制、异常处理、垃圾的自动收集等
对指针的丢弃是 Java 的明智选择
Java 的安全检查机制使得 Java 更具健壮性
Java语言是安全的
提供了一个安全机制以防恶意代码的攻击
安全防范机制(类 ClassLoader),分配不同的名字 空间以防替代本地的同名类、字节代码检查
安全管理机制(类 SecurityManager)
Java 语言是体系结构中立的
Java 程序编译为体系结构中立的字节码格式(后缀为 class 的文件)
可以在实现这个 Java 平台的任何系统中运行
适合于异构的网络环境和软件的分发
Java 语言是可移植的
严格规定了各个基本数据类型的长度
Java 编译器是用 Java 实现的,Java 的运行环境是用 ANSI C 实现的
Java 语言是解释型的
Java 解释器对这些字节码进行解释执行
执行过程中需要的类在联接阶段被载入到运行环境中
Java 是高性能的
与那些解释型的高级脚本语言相比,Java 的确是高性能的
Java 的运行速度随着 JIT(Just-In-Time)编译器技术的发展越来越接近于 C++
Java 语言是多线程的
Java 语言中,线程是一种特殊的对象,它必须由 Thread 类或其子(孙)类来创建
两种方法来创建线程
Thread 类已经实现了 Runnable 接口,因此,任何一个线程均有它的 run 方法,而 run 方法中包含了线程所要运行的代码
使用型构为 Thread(Runnable) 的构造子类 将一个实现了 Runnable 接口的对象包装成一个线程
从 Thread 类派生出子类并重写 run 方法,使用该子类创建的对象即为线程
线程的活动由一组方法来控制
支持多个线程的同时执行
提供多线程之间的同步机制(关键字为 synchronized)
Java 语言是动态的
适应于动态变化的环境
类能够动态地被载入到运行环境,也可以通过网络来载入所需要的类
Java 中的类有一个运行时刻的表示,能进行运行时刻的类型检查
发展历史
1995 年 5 月 23 日,Java 语言诞生
1996 年 1 月,第一个 JDK-JDK1.0 诞生
1996 年 4 月,10 个最主要的操作系统供应商申明将在其产品中嵌入 JAVA 技术
1996 年 9 月,约 8.3 万个网页应用了 JAVA 技术来制作
1997 年 2 月 18 日,JDK1.1 发布
1997 年 4 月 2 日,JavaOne 会议召开,参与者逾一万人,创当时全球同类会议规模之纪录
1997 年 9 月,JavaDeveloperConnection 社区成员超过十万
1998 年 2 月,JDK1.1 被下载超过 2,000,000次
1998 年 12 月 8 日,JAVA2 企业平台 J2EE 发布
1999 年 6月,SUN 公司发布 Java 的三个版本:标准版(JavaSE, 以前是 J2SE)、企业版(JavaEE 以前是 J2EE)和微型版(JavaME,以前是 J2ME)
2000 年 5 月 8 日,JDK1.3 发布
2000 年 5 月 29 日,JDK1.4 发布
2001 年 6 月 5 日,NOKIA 宣布,到 2003 年将出售 1 亿部支持 Java 的手机
2001 年 9 月 24 日,J2EE1.3 发布
2002 年 2 月 26 日,J2SE1.4 发布,自此 Java 的计算能力有了大幅提升
2004 年 9 月 30 日 18:00PM,J2SE1.5 发布,成为 Java 语言发展史上的又一里程碑。为了表示该版本的重要性,J2SE1.5 更名为 Java SE 5.0
2005 年 6 月,JavaOne 大会召开,SUN 公司公开 Java SE 6。此时,Java 的各种版本已经更名,以取消其中的数字 "2":J2EE 更名为 Java EE,J2SE 更名为 Java SE,J2ME 更名为 Java ME
2006 年 12 月,SUN 公司发布 JRE6.0
2009 年 04 月 20 日,甲骨文 74 亿美元收购 Sun,取得 Java 的版权。
2010 年 11 月,由于甲骨文对于 Java 社区的不友善,因此 Apache 扬言将退出 JCP。
2011 年 7 月 28 日,甲骨文发布 Java7.0 的正式版。
2014 年 3 月 18 日,Oracle 公司发表 Java SE 8。
2017 年 9 月 21 日,Oracle 公司发表 Java SE 9
2018 年 3 月 21 日,Oracle 公司发表 Java SE 10
2018 年 9 月 25 日,Java SE 11 发布
2019 年 3 月 20 日,Java SE 12 发布
Java开发工具
Linux 系统、Mac OS 系统、Windows 95/98/2000/XP,WIN 7/8系统
Java JDK 7、8……
vscode 编辑器或者其他编辑器
IDE:Eclipse、 IntelliJ IDEA、NetBeans 等
Java的第一个程序 "Hello World!"
Java开发环境配置
Java的专业术语
JDK(Java Development Kit ):编写Java程序的程序员使用的软件
SDK——soft development kit,软件开发工具包。sdk是一个大的概念,比如开发安卓应用,你需要安卓开发工具包,叫 android sdk,比如你开发java程序,需要用到java sdk,所以一般使用sdk这个概念,你需要在前面加上限定词。 JDK——可以理解为 java sdk,它是编写java程序,使用到的工具包,为程序员提供一些已经封装好的 java 类库。
JRE(Java Runtime Environment):运行Java程序的用户使用的软件
Server JRE (Java SE Runtime Environment):服务端使用的 Java 运行环境
SDK(Software Development Kit):软件开发工具包,在Java中用于描述1998年~2006年之间的JDK
DAO(Data Access Object):数据访问接口,数据访问,顾名思义就是与数据库打交道
MVC(Model View Controller):模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用于组织代码用一种业务逻辑和数据显示分离的方法
window系统安装java
下载JDK

下载java开发工具包JDK
下载地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html
选择安装目录为 C:\Program Files (x86)\Java\jdk1.8.0_91
配置环境变量

安装完成后,右击"我的电脑",点击"属性",选择"高级系统设置";
选择"高级"选项卡,点击"环境变量";
在 "系统变量" 中设置 3 项属性,JAVA_HOME、PATH、CLASSPATH(大小写无所谓),若已存在则点击"编辑",不存在则点击"新建"。
变量设置参数
变量名:
JAVA_HOME
变量值:
C:\Program Files (x86)\Java\jdk1.8.0_91 // 要根据自己的实际路径配置
变量名:
CLASSPATH
变量值:
.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar; //记得前面有个"."
变量名:
Path
变量值:
%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;
JAVA_HOME 设置

PATH设置

CLASSPATH 设置

测试JDK是否安装成功

1、"开始"->"运行",键入"cmd";
2、键入命令: java -version、java、javac 几个命令,出现以下信息,说明环境变量配置成功;
流行JAVA开发工具
Eclipse(推荐)
另一个免费开源的java IDE,下载地址: http://www.eclipse.org/downloads/packages/
选择 Eclipse IDE for Java Developers:

JetBrains 的 IDEA
现在很多人开始使用了,功能很强大,下载地址:https://www.jetbrains.com/idea/download/
Notepad++
Notepad++ 是在微软视窗环境之下的一个免费的代码编辑器,下载地址: http://notepad-plus-plus.org/
Netbeans
开源免费的java IDE,下载地址: http://www.netbeans.org/index.html
常见DOS命令操作
1.快捷键:
windows+R 呼出 DOS 窗口。
2.输入
cmd (大小写不用区分)回车,打开 DOS 窗口。
常见的命令(基本都不区分大小写)
1.切换盘符: 盘符(就是电脑的C,D,E..盘) : (如 :
D :
2.查看文件或者文件夹:
dir
3.进入某个文件夹:
cd 文件夹名
4.返回上一级目录:
cd..
5.清屏:
cls
6.删除某个文件:
del 文件名
7.删除某个文件夹:
rd 文件夹名
8.退出
DOS 窗口: exit
Java基本语法、概念
Java基础语法
基本概念
对象
对象是类的一个实例,有状态和行为
类
类是一个模板,它描述一类对象的行为和状态
方法
方法就是行为,一个类可以有很多方法
实例变量
每个对象都有独特的实例变量,对象的状态由这些实例变量的值决定
第一个Java程序
一个完整Java的各部分

package语句,该部分至多只有一句,必须放在源程序的第一句。
由于Java编译器为每个类生成一个字节码文件,且文件名与类名相同因此同名的类有可能发生冲突。为了解决这一问题,Java提供包来管理类名空间,包实 提供了一种命名机制和可见性限制机制。
import语句,该部分可以有若干import语句或者没有,必须放在所有的类定义之前。
public classDefinition,公共类定义部分,至多只有一个公共类的定义,Java语言规定该Java源程序的文件名必须与该公共类名完全一致。
classDefinition,类定义部分,可以有0个或者多个类定义。
interfaceDefinition,接口定义部分,可以有0个或者多个接口定义。
程序的保存、编译和运行
文件名保存为:HelloWorld.java 与类名一致
打开 cmd 命令窗口,进入目标文件所在的位置
输入 javac HelloWorld.java 按下回车键编译代码
输入 java HelloWorld 按下回车键就可以运行程序了
基本语法
大小写敏感
Java 是大小写敏感的,这就意味着标识符 Hello 与 hello 是不同的
类名
对于所有的类来说,类名的首字母应该大写
如果类名由若干单词组成,那么每个单词的首字母应该大写,例如MyFirstJavaClass
方法名
所有的方法名都应该以小写字母开头
如果方法名含有若干单词,则后面的每个单词首字母大写
源文件名
源文件名必须和类名相同
当保存文件的时候,你应该使用类名作为文件名保存(切记 Java 是大小写敏感的),文件名的后缀为 .java。(如果文件名和类名不相同则会导致编译错误)
主方法入口
所有的 Java 程序由 public static void main(String[] args) 方法开始执行
Java标识符
规则
(1) 由字母、数字、下划线、$组成,不能以数字开头。
(2) 大小写敏感。
(3) 不得使用java中的关键字和保留字。
true、false、null 严格说不应该算关键字,应称其为保留字更合适。
习惯
(1) 标识符要符合语义信息。
(2) 包名所有字母小写。
(3) 类名每个单词首字母大写,其它小写,如:TarenaStudent。
(4) 变量和方法:第一个单词小写,从第二个单词开始首字母大写,如:tarenaStudent。
(5) 常量:所有字母大写,每个单词之间用 _ 连接。
合法标识符举例:age、$salary、_value、__1_value
非法标识符举例:123abc、-salary
编程命名规范
package的命名:
package 的名字由全部小写的字母组成,例如:com.runoob。
class和interface的命名:
class和interface的名字由大写字母开头而其他字母都小写的单词组成,例如:Person,RuntimeException。
class变量的命名:
变量的名字用一个小写字母开头,后面的单词用大写字母开头,例如:index,currentImage。
class 方法的命名:
方法的名字用一个小写字母开头,后面的单词用大写字母开头,例如:run(),getBalance()。
staticfinal变量的命名:
static final变量的名字所有字母都大写,并且能表示完整含义。例如:PI,PASSWORD。
参数的命名:
参数的名字和变量的命名规范一致。
数组的命名:
数组应该总是用这样的方式来命名:byte[] buffer。
Java修饰符
修饰符来修饰类中方法和属性
访问控制修饰符
default, public , protected, private
非访问控制修饰符
final, abstract, static, synchronized
Java变量
局部变量
类变量(静态变量)
成员变量(非静态变量)
Java数组
数组是储存在堆上的对象,可以保存多个同类型变量
Java枚举
枚举特点
枚举限制变量只能是预先设定好的值
使用枚举可以减少代码中的 bug
枚举可以单独声明或者声明在类里面。
方法、变量、构造函数也可以在枚举中定义
实例
Java关键字
注意
这些保留字不能用于常量、变量、和任何标识符的名称
Java 的 null 不是关键字,类似于 true 和 false, 它是一个字面常量,不允许作为标识符使用
访问控制
private 私有的
protected 受保护的
public 公共的
default 默认
类、方法和变量修饰符
abstract 声明抽象
class 类
extends 扩充,继承
final 最终值,不可改变的
implements 实现(接口)
interface 接口
native 本地,原生方法(非 Java 实现)
new 新,创建
static 静态
strictfp 严格,精准
synchronized 线程,同步
transient 短暂
volatile 易失
程序控制语句
break 跳出循环
case 定义一个值以供 switch 选择
continue 继续
default 默认
do 运行
else 否则
for 循环
if 如果
instanceof 实例
return 返回
switch 根据值选择执行
while 循环
错误处理
assert 断言表达式是否为真
catch 捕捉异常
finally 有没有异常都执行
throw 抛出一个异常对象
throws 声明一个异常可能被抛出
try 捕获异常
包相关
import 引入
package 包
基本类型
boolean 布尔型
byte 字节型
char 字符型
double 双精度浮点
float 单精度浮点
int 整型
long 长整型
short 短整型
变量引用
super 父类,超类
this 本类
void 无返回值
保留关键字
goto 是关键字,但不能使用
const 是关键字,但不能使用
Java注释
Java 也支持单行以及多行注释。注释中的字符将被 Java 编译器忽略
实例
Java空行
空白行或者有注释的行,Java 编译器都会忽略掉。
继承
一个类可以由其他类派生
如果你要创建一个类,而且已经存在一个类具有 你所需要的属性或方法,那么你可以将新创建的类继承该类
利用继承的方法,可以重用已存在类的方法和属性,而不用重写这些代码
被继承的类称为超类(super class),派生类称为子类(subclass)
接口
接口可理解为对象间相互通信的协议
接口只定义派生要用到的方法,但是方法的具体实现完全取决于派生类
Java 源程序与编译型运行区别
Java对象和类

基本概念
多态
继承
封装
抽象
类
对象
实例
方法
重载
Java中的对象
软件对象的状态就是属性,行为通过方法体现
Java 中的类

类可以看成是创建 Java 对象的模板。
类中变量类型
局部变量

在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
成员变量

成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
成员变量和局部变量区别 1.声明位置不同 成员变量也就是属性,在类中声明的。 局部变量,在方法中声明或代码块中声明。 2.初始值不同 成员变量如果没有赋值则是有默认值的,数据类型不同则默认值不同。 局部变量是没有默认值,也就是说必须先声明,再赋值,最后才使用。 3.在一个类中,局部变量可以与成员变量同名,但是局部变量优先,如果非要访问成员变量的属性,则必须使用 this.color this 代表当前这个对象,也就是当前谁调用这个方法则这个对象就是谁。
类变量

类变量也声明在类中,方法体之外,但必须声明为 static 类型。
构造方法

1、构造方法的名字和类名相同,并且没有返回值。
2、构造方法主要用于为类的对象定义初始化状态。
3、我们不能直接调用构造方法,必须通过new关键字来自动调用,从而创建类的实例。
4、Java的类都要求有构造方法,如果没有定义构造方法,Java编译器会为我们提供一个缺省的构造方法,也就是不带参数的构造方法。
实例
创建对象
声明
声明一个对象,包括对象名称和对象类型。
实例化
使用关键字 new 来创建一个对象。
初始化
使用 new 创建对象时,会调用构造方法初始化对象。
new关键字的作用
1、为对象分配内存空间。
2、引起对象构造方法的调用。
3、为对象返回一个引用。
对象与引用区别 对象是具体的一个实例,如:new Student(); new 表示创建一个对象,并在堆内存中开辟一块空间。 引用名称是存放的对象的地址。
实例
访问实例变量和方法
通过已创建的对象来访问成员变量和成员方法
格式
实例
运行结果: 
源文件声明规则
一个源文件中只能有一个 public 类
因为每个编译单元都只能有一个公共接口,用 public 类来表现
一个源文件可以有多个非 public 类
源文件的名称应该和 public 类的类名保持一致。例如:源文件中 public 类的类名是 Employee,那么源文件应该命名为Employee.java。
如果一个类定义在某个包中,那么 package 语句应该在源文件的首行。
如果源文件包含 import 语句,那么应该放在 package 语句和类定义之间。如果没有 package 语句,那么 import 语句应该在源文件中最前面。
import 语句和 package 语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明。
Java 包
包主要用来对类和接口进行分类
当开发 Java 程序时,可能编写成百上千的类,因此很有必要对类和接口进行分类
import 语句
提供一个合理的路径,使得编译器可以找到某个类
import java.io.*;
载入 java_installation/java/io 路径下的所有类
一个简单的例子
创建两个类:Employee 和 EmployeeTest
Employee.java 文件代码
import java.io.*; public class Employee{ String name; int age; String designation; double salary; // Employee 类的构造器 public Employee(String name){ this.name = name; } // 设置age的值 public void empAge(int empAge){ age = empAge; } /* 设置designation的值*/ public void empDesignation(String empDesig){ designation = empDesig; } /* 设置salary的值*/ public void empSalary(double empSalary){ salary = empSalary; } /* 打印信息 */ public void printEmployee(){ System.out.println("名字:"+ name ); System.out.println("年龄:" + age ); System.out.println("职位:" + designation ); System.out.println("薪水:" + salary); } }
EmployeeTest.java 文件代码
import java.io.*; public class EmployeeTest{ public static void main(String[] args){ /* 使用构造器创建两个对象 */ Employee empOne = new Employee("RUNOOB1"); Employee empTwo = new Employee("RUNOOB2"); // 调用这两个对象的成员方法 empOne.empAge(26); empOne.empDesignation("高级程序员"); empOne.empSalary(1000); empOne.printEmployee(); empTwo.empAge(21); empTwo.empDesignation("菜鸟程序员"); empTwo.empSalary(500); empTwo.printEmployee(); } }
编译运行结果

Java数据类型、变量类型
Java基本数据类型
内置数据类型
实例 对于数值类型的基本类型的取值范围,我们无需强制去记忆,因为它们的值都已经以常量的形式定义在对应的包装类中了。请看下面的例子。 public class PrimitiveTypeTest { public static void main(String[] args) { // byte System.out.println("基本类型:byte 二进制位数:" + Byte.SIZE); System.out.println("包装类:java.lang.Byte"); System.out.println("最小值:Byte.MIN_VALUE=" + Byte.MIN_VALUE); System.out.println("最大值:Byte.MAX_VALUE=" + Byte.MAX_VALUE); System.out.println(); // short System.out.println("基本类型:short 二进制位数:" + Short.SIZE); System.out.println("包装类:java.lang.Short"); System.out.println("最小值:Short.MIN_VALUE=" + Short.MIN_VALUE); System.out.println("最大值:Short.MAX_VALUE=" + Short.MAX_VALUE); System.out.println(); // int System.out.println("基本类型:int 二进制位数:" + Integer.SIZE); System.out.println("包装类:java.lang.Integer"); System.out.println("最小值:Integer.MIN_VALUE=" + Integer.MIN_VALUE); System.out.println("最大值:Integer.MAX_VALUE=" + Integer.MAX_VALUE); System.out.println(); // long System.out.println("基本类型:long 二进制位数:" + Long.SIZE); System.out.println("包装类:java.lang.Long"); System.out.println("最小值:Long.MIN_VALUE=" + Long.MIN_VALUE); System.out.println("最大值:Long.MAX_VALUE=" + Long.MAX_VALUE); System.out.println(); // float System.out.println("基本类型:float 二进制位数:" + Float.SIZE); System.out.println("包装类:java.lang.Float"); System.out.println("最小值:Float.MIN_VALUE=" + Float.MIN_VALUE); System.out.println("最大值:Float.MAX_VALUE=" + Float.MAX_VALUE); System.out.println(); // double System.out.println("基本类型:double 二进制位数:" + Double.SIZE); System.out.println("包装类:java.lang.Double"); System.out.println("最小值:Double.MIN_VALUE=" + Double.MIN_VALUE); System.out.println("最大值:Double.MAX_VALUE=" + Double.MAX_VALUE); System.out.println(); // char System.out.println("基本类型:char 二进制位数:" + Character.SIZE); System.out.println("包装类:java.lang.Character"); // 以数值形式而不是字符形式将Character.MIN_VALUE输出到控制台 System.out.println("最小值:Character.MIN_VALUE=" + (int) Character.MIN_VALUE); // 以数值形式而不是字符形式将Character.MAX_VALUE输出到控制台 System.out.println("最大值:Character.MAX_VALUE=" + (int) Character.MAX_VALUE); } } 编译以上代码输出结果如下所示: 基本类型:byte 二进制位数:8 包装类:java.lang.Byte 最小值:Byte.MIN_VALUE=-128 最大值:Byte.MAX_VALUE=127 基本类型:short 二进制位数:16 包装类:java.lang.Short 最小值:Short.MIN_VALUE=-32768 最大值:Short.MAX_VALUE=32767 基本类型:int 二进制位数:32 包装类:java.lang.Integer 最小值:Integer.MIN_VALUE=-2147483648 最大值:Integer.MAX_VALUE=2147483647 基本类型:long 二进制位数:64 包装类:java.lang.Long 最小值:Long.MIN_VALUE=-9223372036854775808 最大值:Long.MAX_VALUE=9223372036854775807 基本类型:float 二进制位数:32 包装类:java.lang.Float 最小值:Float.MIN_VALUE=1.4E-45 最大值:Float.MAX_VALUE=3.4028235E38 基本类型:double 二进制位数:64 包装类:java.lang.Double 最小值:Double.MIN_VALUE=4.9E-324 最大值:Double.MAX_VALUE=1.7976931348623157E308 基本类型:char 二进制位数:16 包装类:java.lang.Character 最小值:Character.MIN_VALUE=0 最大值:Character.MAX_VALUE=65535
byte
byte 数据类型是8位、有符号的,以二进制补码表示的整数;
最小值是
-128(-2^7)
最大值是
127(2^7-1)
默认值是
0
byte 类型用在大型数组中节约空间,主要代替整数,因为 byte 变量占用的空间只有 int 类型的四分之一;
例子:byte a = 100,byte b = -50。
short
short 数据类型是 16 位、有符号的以二进制补码表示的整数
最小值是
-32768(-2^15)
最大值是
32767(2^15 - 1)
默认值是
0
Short 数据类型也可以像 byte 那样节省空间。一个short变量是int型变量所占空间的二分之一;
例子:short s = 1000,short r = -20000。
int
int 数据类型是32位、有符号的以二进制补码表示的整数;
最小值是
-2,147,483,648(-2^31)
最大值是
2,147,483,647(2^31 - 1)
一般地整型变量默认为 int 类型;
默认值是
0
例子:int a = 100000, int b = -200000。
long
long 数据类型是 64 位、有符号的以二进制补码表示的整数;
最小值是
-9,223,372,036,854,775,808(-2^63)
最大值是
9,223,372,036,854,775,807(2^63 -1)
这种类型主要使用在需要比较大整数的系统上;
默认值是
0L
例子: long a = 100000L,Long b = -200000L。 "L"理论上不分大小写,但是若写成"l"容易与数字"1"混淆,不容易分辩。所以最好大写。
float
float 数据类型是单精度、32位、符合IEEE 754标准的浮点数;
float 在储存大型浮点数组的时候可节省内存空间;
默认值是
0.0f
浮点数不能用来表示精确的值,如货币;
例子:float f1 = 234.5f。
double
double 数据类型是双精度、64 位、符合 IEEE 754 标准的浮点数;
浮点数的默认类型为 double 类型;
double类型同样不能表示精确的值,如货币;
默认值是
0.0d
例子
7 是一个 int 字面量,而 7D,7. 和 8.0 是 double 字面量。
boolean
boolean数据类型表示一位的信息;
只有两个取值:true 和 false;
这种类型只作为一种标志来记录 true/false 情况;
默认值是
false
例子:boolean one = true。
char
char 类型是一个单一的 16 位 Unicode 字符;
最小值是
\u0000 (十进制等效值为 0);
最大值是
\uffff (即为 65535);
char 数据类型可以储存任何字符;
例子:char letter = 'A';。
注
科学记数法
Float和Double的最小值和最大值都是以科学记数法的形式输出的,结尾的"E+数字"表示E之前的数字要乘以10的多少次方。比如3.14E3就是3.14 × 103 =3140,3.14E-3 就是 3.14 x 10-3 =0.00314。
基本类型 void
实际上,JAVA中还存在另外一种基本类型 void,它也有对应的包装类 java.lang.Void,不过我们无法直接对它们进行操作。
类型默认值
public class Test { static boolean bool; static byte by; static char ch; static double d; static float f; static int i; static long l; static short sh; static String str; public static void main(String[] args) { System.out.println("Bool :" + bool); System.out.println("Byte :" + by); System.out.println("Character:" + ch); System.out.println("Double :" + d); System.out.println("Float :" + f); System.out.println("Integer :" + i); System.out.println("Long :" + l); System.out.println("Short :" + sh); System.out.println("String :" + str); } } 实例输出结果为: Bool :false Byte :0 Character: Double :0.0 Float :0.0 Integer :0 Long :0 Short :0 String :null
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char 'u0000'
String (or any object) null
boolean false
引用类型
在Java中,引用类型的变量非常类似于C/C++的指针。引用类型指向一个对象,指向对象的变量是引用变量。这些变量在声明时被指定为一个特定的类型,比如 Employee、Puppy 等。变量一旦声明后,类型就不能被改变了。
对象、数组都是引用数据类型。
所有引用类型的默认值都是null。
一个引用变量可以用来引用任何与之兼容的类型。
例子:Site site = new Site("Runoob")。
Java 常量
final 关键字来修饰常量
final double PI = 3.1415927;
为了便于识别,通常使用大写字母表示常量
字面量可以赋给任何内置类型的变量
byte a = 68;
char a = 'A';
byte、int、long、和short都可以用十进制、16进制以及8进制的方式来表示
前缀 0 表示 8 进制,而前缀 0x 代表 16 进制
int decimal = 100; int octal = 0144; int hexa = 0x64;
字符串常量也是包含在两个引号之间的字符序列
"Hello World"
"two\nlines"
"\"This is in quotes\""
字符串常量和字符常量都可以包含任何Unicode字符
char a = '\u0001';
String a = "\u0001";
转义字符序列
\n 换行 (0x0a)
\r 回车 (0x0d)
\f 换页符(0x0c)
\b 退格 (0x08)
\0 空字符 (0x0)
\s 空格 (0x20)
\t 制表符
\" 双引号
\' 单引号
\\ 反斜杠
\ddd 八进制字符 (ddd)
\uxxxx 16进制Unicode字符 (xxxx)
类型转换
类型转换规则
转换从低级到高级
低 ------------------------------------> 高
byte,short,char—> int —> long—> float —> double
1. 不能对boolean类型进行类型转换。
2. 不能把对象类型转换成不相关类的对象。
3. 在把容量大的类型转换为容量小的类型时必须使用强制类型转换。
4. 转换过程中可能导致溢出或损失精度,例如:
int i =128;
byte b = (byte)i;
因为 byte 类型是 8 位,最大值为127,所以当 int 强制转换为 byte 类型时,值 128 时候就会导致溢出
5. 浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入,例如:
(int)23.7 == 23;
(int)-45.89f == -45
自动类型转换
转换前的数据类型的位数要低于转换后的数据类型
short数据类型的位数为16位,就可以自动转换位数为32的int类型
float数据类型的位数为32,可以自动转换为64位的double类型
实例
解析:c1 的值为字符 a ,查 ASCII 码表可知对应的 int 类型值为 97, A 对应值为 65,所以 i2=65+1=66。
强制类型转换
1. 条件是转换的数据类型必须是兼容的。
2. 格式:(type)value type是要强制类型转换后的数据类型
实例
隐含强制类型转换
1、 整数的默认类型是 int。
2. 小数默认是 double 类型浮点型,在定义 float 类型时必须在数字后面跟上 F 或者 f。
Java变量类型
变量的声明
基本格式
type identifier [ = value][, identifier [= value] ...] ;
格式说明:type为Java数据类型。identifier是变量名。可以使用逗号隔开来声明多个同类型变量。
声明实例
变量类型
类变量:独立于方法之外的变量,用 static 修饰。
实例变量:独立于方法之外的变量,不过没有 static 修饰。
局部变量:类的方法中的变量。
成员变量、局部变量、静态变量的区别
类变量可在类中直接使用,实例变量需实例化后才能使用
静态变量属于类,该类不生产对象,通过类名就可以调用静态变量。
实例变量属于该类的对象,必须产生该类对象,才能调用实例变量。
实例
Java 局部变量
局部变量声明在方法、构造方法或者语句块中;
局部变量在方法、构造方法、或者语句块被执行的时候创建,当它们执行完成后,变量将会被销毁;
访问修饰符不能用于局部变量;
局部变量只在声明它的方法、构造方法或者语句块中可见;
局部变量是在栈上分配的。
局部变量没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用。
实例 1
age是一个局部变量。定义在pupAge()方法中,它的作用域就限制在这个方法中
实例 2
age 变量没有初始化,所以在编译时会出错
实例变量
实例变量声明在一个类中,但在方法、构造方法和语句块之外;
当一个对象被实例化之后,每个实例变量的值就跟着确定;
实例变量在对象创建的时候创建,在对象被销毁的时候销毁;
实例变量的值应该至少被一个方法、构造方法或者语句块引用,使得外部能够通过这些方式获取实例变量信息;
实例变量可以声明在使用前或者使用后;
访问修饰符可以修饰实例变量;
实例变量对于类中的方法、构造方法或者语句块是可见的。一般情况下应该把实例变量设为私有。通过使用访问修饰符可以使实例变量对子类可见;
实例变量具有默认值。数值型变量的默认值是0,布尔型变量的默认值是false,引用类型变量的默认值是null。变量的值可以在声明时指定,也可以在构造方法中指定;
实例变量可以直接通过变量名访问。但在静态方法以及其他类中,就应该使用完全限定名:ObejectReference.VariableName。
实例
import java.io.*; public class Employee{ // 这个实例变量对子类可见 public String name; // 私有变量,仅在该类可见 private double salary; //在构造器中对name赋值 public Employee (String empName){ name = empName; } //设定salary的值 public void setSalary(double empSal){ salary = empSal; } // 打印信息 public void printEmp(){ System.out.println("名字 : " + name ); System.out.println("薪水 : " + salary); } public static void main(String[] args){ Employee empOne = new Employee("RUNOOB"); empOne.setSalary(1000.0); empOne.printEmp(); } } 以上实例编译运行结果如下: $ javac Employee.java $ java Employee 名字 : RUNOOB 薪水 : 1000.0
类变量(静态变量)
类变量也称为静态变量,在类中以 static 关键字声明,但必须在方法之外。
无论一个类创建了多少个对象,类只拥有类变量的一份拷贝。
静态变量除了被声明为常量外很少使用,静态变量是指声明为 public/private,final 和 static 类型的变量。静态变量初始化后不可改变。
静态变量储存在静态存储区。经常被声明为常量,很少单独使用 static 声明变量。
静态变量在第一次被访问时创建,在程序结束时销毁。
与实例变量具有相似的可见性。但为了对类的使用者可见,大多数静态变量声明为 public 类型。
默认值和实例变量相似。数值型变量默认值是 0,布尔型默认值是 false,引用类型默认值是 null。变量的值可以在声明的时候指定,也可以在构造方法中指定。此外,静态变量还可以在静态语句块中初始化。
静态变量可以通过:
ClassName.VariableName 的方式访问。
类变量被声明为 public static final 类型时,类变量名称一般建议使用大写字母。如果静态变量不是 public 和 final 类型,其命名方式与实例变量以及局部变量的命名方式一致。
实例
Java修饰符、运算符
Java 修饰符
访问修饰符
default
(即默认,什么也不写), 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
private
在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
public
对所有类可见。使用对象:类、接口、变量、方法
protected
对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
访问控制
非访问修饰符
static
静态变量
static 关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。 静态变量也被称为类变量。局部变量不能被声明为 static 变量。
静态方法
static 关键字用来声明独立于对象的静态方法。静态方法不能使用类的非静态变量。静态方法从参数列表得到数据,然后计算这些数据。
final
final 变量
final 表示"最后的、最终的"含义,变量一旦赋值后,不能被重新赋值。被 final 修饰的实例变量必须显式指定初始值。
final 修饰符通常和 static 修饰符一起使用来创建类常量。
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() 方法(在 Runnable 开启的线程),在另一个线程调用 stop() 方法。 如果第一行 中缓冲区的 active 值被使用,那么在 第二行 的 active 值为 false 时循环不会停止。
但是以上代码中我们使用了 volatile 修饰 active,所以该循环会停止。
Java 运算符
分类
算术运算符
关系运算符
位运算符
逻辑运算符
赋值运算符
其他运算符
算术运算符
public class Test { public static void main(String[] args) { int a = 10; int b = 20; int c = 25; int d = 25; System.out.println("a + b = " + (a + b) ); System.out.println("a - b = " + (a - b) ); System.out.println("a * b = " + (a * b) ); System.out.println("b / a = " + (b / a) ); System.out.println("b % a = " + (b % a) ); System.out.println("c % a = " + (c % a) ); System.out.println("a++ = " + (a++) ); System.out.println("a-- = " + (a--) ); // 查看 d++ 与 ++d 的不同 System.out.println("d++ = " + (d++) ); System.out.println("++d = " + (++d) ); } } 以上实例编译运行结果如下: a + b = 30 a - b = -10 a * b = 200 b / a = 2 b % a = 0 c % a = 5 a++ = 10 a-- = 11 d++ = 25 ++d = 27
+ 加法 - 相加运算符两侧的值 A + B 等于 30
- 减法 - 左操作数减去右操作数 A – B 等于 -10
* 乘法 - 相乘操作符两侧的值 A * B等于200
/ 除法 - 左操作数除以右操作数 B / A等于2
% 取余 - 左操作数除以右操作数的余数 B%A等于0
++ 自增: 操作数的值增加1 B++ 或 ++B 等于 21(区别详见下文)
-- 自减: 操作数的值减少1 B-- 或 --B 等于 19(区别详见下文)
自增自减运算符的区别
 
++a,--a
先进行自增或者自减运算,再进行表达式运算
a++,a--
先进行表达式运算,再进行自增或者自减运算
关系运算符
== 检查如果两个操作数的值是否相等,如果相等则条件为真。 (A == B)为假。
!= 检查如果两个操作数的值是否相等,如果值不相等则条件为真。 (A != B) 为真。
> 检查左操作数的值是否大于右操作数的值,如果是那么条件为真。 (A> B)为假。
< 检查左操作数的值是否小于右操作数的值,如果是那么条件为真。 (A <B)为真。
>= 检查左操作数的值是否大于或等于右操作数的值,如果是那么条件为真。 (A> = B)为假。
<= 检查左操作数的值是否小于或等于右操作数的值,如果是那么条件为真。 (A <= B)为真。
位运算符
Java定义了位运算符,应用于整数类型(int),长整型(long),短整型(short),字符型(char),和字节型(byte)等类型。 位运算符作用在所有的位上,并且按位运算。假设a = 60,b = 13;它们的二进制格式表示将如下: A = 0011 1100 B = 0000 1101 ----------------- A&B = 0000 1100 A | B = 0011 1101 A ^ B = 0011 0001 ~A= 1100 0011
& 如果相对应位都是1,则结果为1,否则为0 (A&B),得到12,即0000 1100
| 如果相对应位都是 0,则结果为 0,否则为 1 (A | B)得到61,即 0011 1101
^ 如果相对应位值相同,则结果为0,否则为1 (A ^ B)得到49,即 0011 0001
〜 按位取反运算符翻转操作数的每一位,即0变成1,1变成0。 (〜A)得到-61,即1100 0011
<< 按位左移运算符。左操作数按位左移右操作数指定的位数。 A << 2得到240,即 1111 0000
>> 按位右移运算符。左操作数按位右移右操作数指定的位数。 A >> 2得到15即 1111
>>> 按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。 A>>>2得到15即0000 1111
逻辑运算符
&& 称为逻辑与运算符。当且仅当两个操作数都为真,条件才为真。 (A && B)为假。
| | 称为逻辑或操作符。如果任何两个操作数任何一个为真,条件为真。 (A | | B)为真。
! 称为逻辑非运算符。用来反转操作数的逻辑状态。如果条件为true,则逻辑非运算符将得到false。 !(A && B)为真。
短路逻辑运算符

当使用与逻辑运算符时,在两个操作数都为true时,结果才为true,但是当得到第一个操作为false时,其结果就必定是false,这时候就不会再判断第二个操作了。
赋值运算符
 
= 简单的赋值运算符,将右操作数的值赋给左侧操作数 C = A + B将把A + B得到的值赋给C
+ = 加和赋值操作符,它把左操作数和右操作数相加赋值给左操作数 C + = A等价于C = C + A
- = 减和赋值操作符,它把左操作数和右操作数相减赋值给左操作数 C - = A等价于C = C - A
* = 乘和赋值操作符,它把左操作数和右操作数相乘赋值给左操作数 C * = A等价于C = C * A
/ = 除和赋值操作符,它把左操作数和右操作数相除赋值给左操作数 C / = A,C 与 A 同类型时等价于 C = C / A
(%)= 取模和赋值操作符,它把左操作数和右操作数取模后赋值给左操作数 C%= A等价于C = C%A
<< = 左移位赋值运算符 C << = 2等价于C = C << 2
>> = 右移位赋值运算符 C >> = 2等价于C = C >> 2
&= 按位与赋值运算符 C&= 2等价于C = C&2
^ = 按位异或赋值操作符 C ^ = 2等价于C = C ^ 2
| = 按位或赋值操作符 C | = 2等价于C = C | 2
条件运算符
variable x = (expression) ? value if true : value if false
运算符有3个操作数,并且需要判断布尔表达式的值
instanceof 运算符
用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)
( Object reference variable ) instanceof (class/interface type)
如果运算符左侧变量所指的对象,是操作符右侧类或接口(class/interface)的一个对象,那么结果为真。

如果被比较的对象兼容于右侧类型,该运算符仍然返回true。
 
Java运算符优先级
后缀 () [] . (点操作符) 左到右
一元 expr++ expr-- 从左到右
一元 ++expr --expr + - ~ ! 从右到左
乘性 * /% 左到右
加性 + - 左到右
移位 >> >>> << 左到右
关系 > >= < <= 左到右
相等 == != 左到右
按位与 & 左到右
按位异或 ^ 左到右
按位或 | 左到右
逻辑与 && 左到右
逻辑或 | | 左到右
条件 ?: 从右到左
赋值 = + = - = * = / =%= >> = << =&= ^ = | = 从右到左
逗号 , 左到右
Java控制语句
Java循环结构
while 循环
while是最基本的循环,它的结构为
实例
do…while 循环
即使不满足条件,也至少执行一次
实例
for循环
for循环执行的次数是在执行前就确定的
说明
最先执行初始化步骤。可以声明一种类型,但可初始化一个或多个循环控制变量,也可以是空语句。
然后,检测布尔表达式的值。如果为 true,循环体被执行。如果为false,循环终止,开始执行循环体后面的语句。
执行一次循环后,更新循环控制变量。
再次检测布尔表达式。循环执行上面的过程。
实例
Java 增强 for 循环
Java5 引入了一种主要用于数组的增强型 for 循环
声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。 表达式:表达式是要访问的数组名,或者是返回值为数组的方法。
实例
break关键字
break 主要用在循环语句或者 switch 语句中,用来跳出整个语句块。
break 跳出最里层的循环,并且继续执行该循环下面的语句。
实例
continue 关键字
continue 适用于任何循环控制结构中。作用是让程序立刻跳转到下一次循环的迭代。
在 for 循环中,continue 语句使程序立即跳转到更新语句。
在 while 或者 do…while 循环中,程序立即跳转到布尔表达式的判断语句。
实例
Java条件语句-if...else
if...else语句
格式
实例
if...else if...else 语句
格式
注意
if 语句至多有 1 个 else 语句,else 语句在所有的 else if 语句之后。
if 语句可以有若干个 else if 语句,它们必须在 else 语句之前。
一旦其中一个 else if 语句检测为 true,其他的 else if 以及 else 语句都将跳过执行
实例
嵌套的 if…else 语句
格式
实例
Java switch case语句
语法
规则
switch 语句中的变量类型可以是: byte、short、int 或者 char。从 Java SE 7 开始,switch 支持字符串 String 类型了,同时 case 标签必须为字符串常量或字面量。
switch 语句可以拥有多个 case 语句。每个 case 后面跟一个要比较的值和冒号。
case 语句中的值的数据类型必须与变量的数据类型相同,而且只能是常量或者字面常量。
当变量的值与 case 语句的值相等时,那么 case 语句之后的语句开始执行,直到 break 语句出现才会跳出 switch 语句。
当遇到 break 语句时,switch 语句终止。程序跳转到 switch 语句后面的语句执行。case 语句不必须要包含 break 语句。如果没有 break 语句出现,程序会继续执行下一条 case 语句,直到出现 break 语句。
switch 语句可以包含一个 default 分支,该分支一般是 switch 语句的最后一个分支(可以在任何位置,但建议在最后一个)。default 在没有 case 语句的值和变量值相等的时候执行。default 分支不需要 break 语句。
switch case 执行时,一定会先进行匹配,匹配成功返回当前 case 的值,再根据是否有 break,判断是否继续输出,或是跳出判断
实例
Java数组
声明数组变量
语法
注意: 建议使用 dataType[] arrayRefVar 的声明风格声明数组变量。 dataType arrayRefVar[] 风格是来自 C/C++ 语言 ,在Java中采用是为了让 C/C++ 程序员能够快速理解java语言。
实例
创建数组
语法
使用new操作符来创建数组
arrayRefVar = new dataType[arraySize];
一、使用 dataType[arraySize] 创建了一个数组。
二、把新创建的数组的引用赋值给变量 arrayRefVar。
数组变量的声明,和创建数组可以用一条语句完成
dataType[] arrayRefVar = new dataType[arraySize];
还可以使用如下的方式创建数组
dataType[] arrayRefVar = {value0, value1, ..., valuek};
数组的元素是通过索引访问的。数组索引从 0 开始,所以索引值从 0 到 arrayRefVar.length-1
实例
下面的图片描绘了数组 myList。这里 myList 数组里有 10 个 double 元素,它的下标从 0 到 9。 
处理数组
数组的元素类型和数组的大小都是确定的,所以当处理数组元素时候,我们通常使用基本循环或者 For-Each 循环
创建、初始化和操纵数组
For-Each 循环
语法
实例
数组作为函数的参数
数组可以作为参数传递给方法
数组作为函数的返回值
多维数组
多维数组的动态初始化
直接为每一维分配空间,格式如下:
type[][] typeName = new type[typeLength1][typeLength2];
type 可以为基本数据类型和复合数据类型,typeLength1 和 typeLength2 必须为正整数,typeLength1 为行数,typeLength2 为列数。
例如:
int[][] a = new int[2][3];
解析:
二维数组 a 可以看成一个两行三列的数组。
从最高维开始,分别为每一维分配空间
多维数组的引用
对二维数组中的每个元素,引用方式为 arrayName[index1][index2],例如:
num[1][0];
Arrays 类
java.util.Arrays 类能方便地操作数组,它提供的所有方法都是静态的。
方法
给数组赋值:通过 fill 方法。
对数组排序:通过 sort 方法,按升序。
比较数组:通过 equals 方法比较数组中元素值是否相等。
查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。
方法说明
public static void fill(int[] a, int val)
将指定的 int 值分配给指定 int 型数组指定范围中的每个元素。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。
public static void sort(Object[] a)
对指定对象数组根据其元素的自然顺序进行升序排列。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。
public static boolean equals(long[] a, long[] a2)
如果两个指定的 long 型数组彼此相等,则返回 true。如果两个数组包含相同数量的元素,并且两个数组中的所有相应元素对都是相等的,则认为这两个数组是相等的。换句话说,如果两个数组以相同顺序包含相同的元素,则两个数组是相等的。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。
public static int binarySearch(Object[] a, Object key)
用二分查找算法在给定数组中搜索给定值的对象(Byte,Int,double等)。数组在调用前必须排序好的。如果查找值包含在数组中,则返回搜索键的索引;否则返回 (-(插入点) - 1)。
Java方法
方法介绍
System.out.println()
println() 是一个方法。
System 是系统类。
out 是标准输出对象。
什么是方法
方法是解决一类问题的步骤的有序组合
方法包含于类或对象中
方法在程序中被创建,在其他地方被引用
方法的优点
1. 使程序变得更简短而清晰。
2. 有利于程序维护。
3. 可以提高程序开发的效率。
4. 提高了代码的重用性。
方法的命名规则
1.方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头写,不使用连接符。例如:addPerson
2.下划线可能出现在 JUnit 测试方法名称中用以分隔名称的逻辑组件。一个典型的模式是:test<MethodUnderTest>_<state> ,例如 testPop_emptyStack
方法的定义
语法

修饰符:
修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。
返回值类型 :
方法可能会返回值。returnValueType 是方法返回值的数据类型。有些方法执行所需的操作,但没有返回值。在这种情况下,returnValueType 是关键字 void
方法名:
是方法的实际名称。方法名和参数表共同构成方法签名。
参数类型:
参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
方法体:
方法体包含具体的语句,定义该方法的功能。
如:
public static int age(int birthday){...}
static float interest(float principal, int year){...}
注意:
在一些其它语言中方法指过程和函数。一个返回非void类型返回值的方法称为函数;一个返回void类型返回值的方法叫做过程。
实例
方法调用
两种调用方法的方式,根据方法是否返回值来选择
int larger = max(30, 40);
System.out.println("欢迎访问菜鸟教程!");
实例
这个程序包含 main 方法和 max 方法。main 方法是被 JVM 调用的,除此之外,main 方法和其它方法没什么区别。 main 方法的头部是不变的,如例子所示,带修饰符 public 和 static,返回 void 类型值,方法名字是 main,此外带个一个 String[] 类型参数。String[] 表明参数是字符串数组。
void 关键字
通过值传递参数
按照参数列表指定的顺序提供
示例
方法的重载
创建另一个有相同名字但参数不同的方法
如果你调用max方法时传递的是int型参数,则 int型参数的max方法就会被调用;
如果传递的是double型参数,则double类型的max方法体会被调用,这叫做方法重载;
重载的方法必须拥有不同的参数列表。你不能仅仅依据修饰符或者返回类型的不同来重载方法。
变量作用域
变量的范围是程序中该变量可以被引用的部分
局部变量
方法内定义的变量被称为局部变量。
局部变量的作用范围从声明开始,直到包含它的块结束。
局部变量必须声明才可以使用。
方法的参数范围涵盖整个方法。参数实际上是一个局部变量。
for循环
for循环的初始化部分声明的变量,其作用范围在整个循环。
但循环体内声明的变量其适用范围是从它声明到循环体结束。
命令行参数的使用
命令行参数是在执行程序时候紧跟在程序名字后面的信息
构造方法
可变参数
可变参数的声明
typeName... parameterName
在方法声明中,在指定参数类型后加一个省略号(...)
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明
实例
finalize() 方法
在对象被垃圾收集器析构(回收)之前调用,用来清除回收对象
你可以使用 finalize() 来确保一个对象打开的文件被关闭
在 finalize() 方法里,你必须指定在对象销毁时候要执行的操作
finalize() 一般格式
关键字 protected 是一个限定符,它确保 finalize() 方法不会被该类以外的代码调用
当然,Java 的内存回收可以由 JVM 来自动完成
实例
Java常用类
Java Number & Math 类
Java Number 类
包装类
Boolean boolean
Byte byte
Short short
Integer int
Long long
Character char
Float float
Double double
Number 类属于 java.lang 包
装箱、拆箱
实例
当 x 被赋为整型值时,由于x是一个对象,所以编译器要对x进行装箱。然后,为了使x能进行加运算,所以要对x进行拆箱。
Java 中 int 和 Integer 的区别
1. int 是基本数据类型,int 变量存储的是数值。Integer 是引用类型,实际是一个对象,Integer 存储的是引用对象的地址。
2.因为 new 生成的是两个对象,其内存地址不同。
3.int 和 Integer 所占内存比较:
Integer 对象会占用更多的内存。Integer 是一个对象,需要存储对象的元数据。但是 int 是一个原始类型的数据,所以占用的空间更少。
4. 非 new 生成的 Integer 变量与 new Integer() 生成的变量比较,结果为 false。
因为非 new 生成的 Integer 变量指向的是 java 常量池中的对象,而 new Integer() 生成的变量指向堆中新建的对象,两者在内存中的地址不同。所以输出为 false。
5. 两个非 new 生成的 Integer 对象进行比较,如果两个变量的值在区间 [-128,127] 之间,比较结果为 true;否则,结果为 false。
java 在编译 Integer i1 = 127 时,会翻译成 Integer i1 = Integer.valueOf(127)。
6. Integer 变量(无论是否是 new 生成的)与 int 变量比较,只要两个变量的值是相等的,结果都为 true。
包装类 Integer 变量在与基本数据类型 int 变量比较时,Integer 会自动拆包装为 int,然后进行比较,实际上就是两个 int 变量进行比较,值相等,所以为 true。
Java Math 类
Math 的方法都被定义为 static 形式,通过 Math 类可以在主函数中直接调用
实例
Number & Math 类方法
xxxValue()
将 Number 对象转换为xxx数据类型的值并返回。
compareTo()
将number对象与参数比较。
equals()
判断number对象是否与参数相等。
valueOf()
返回一个 Number 对象指定的内置数据类型
toString()
以字符串形式返回值。
parseInt()
将字符串解析为int类型。
abs()
返回参数的绝对值。
ceil()
返回大于等于( >= )给定参数的的最小整数,类型为双精度浮点型。
floor()
返回小于等于(<=)给定参数的最大整数 。
rint()
返回与参数最接近的整数。返回类型为double。
round()
它表示四舍五入,算法为 Math.floor(x+0.5),即将原来的数字加上 0.5 后再向下取整,所以,Math.round(11.5) 的结果为12,Math.round(-11.5) 的结果为-11。
min()
返回两个参数中的最小值。
max()
返回两个参数中的最大值。
exp()
返回自然数底数e的参数次方。
log()
返回参数的自然数底数的对数值。
pow()
返回第一个参数的第二个参数次方。
sqrt()
求参数的算术平方根。
sin()
求指定double类型参数的正弦值。
cos()
求指定double类型参数的余弦值。
tan()
求指定double类型参数的正切值。
asin()
求指定double类型参数的反正弦值。
acos()
求指定double类型参数的反余弦值。
atan()
求指定double类型参数的反正切值。
atan2()
将笛卡尔坐标转换为极坐标,并返回极坐标的角度值。
toDegrees()
将参数转化为角度。
toRadians()
将角度转换为弧度。
random()
返回一个随机数。
floor,round 和 ceil 实例
Java Character 类
创建Character类对象
装箱与拆箱
转义序列
\t 在文中该处插入一个tab键
\b 在文中该处插入一个后退键
\n 在文中该处换行
\r 在文中该处插入回车
\f 在文中该处插入换页符
\' 在文中该处插入单引号
\" 在文中该处插入双引号
\\ 在文中该处插入反斜杠
Character 方法
提取字符中的大小写字母: public class UpperLowerCase { public static void main(String []args) { String StrA="I am Tom.I am from China."; String StrB=""; String StrC=""; for(int i=0;i<StrA.length();i++){ if(Character.isUpperCase(StrA.charAt(i))) StrB +=StrA.charAt(i)+" "; if(Character.isLowerCase(StrA.charAt(i))) StrC +=StrA.charAt(i)+" "; } System.out.println("字符串中大写字母有:"+StrB); System.out.println("字符串中小写字母有:"+StrC); } } 输出结果为: 字符串中大写字母有:I T I C 字符串中小写字母有:a m o m a m f r o m h i n a
isLetter()
是否是一个字母
isDigit()
是否是一个数字字符
isWhitespace()
是否是一个空白字符
isUpperCase()
是否是大写字母
isLowerCase()
是否是小写字母
toUpperCase()
指定字母的大写形式
toLowerCase()
指定字母的小写形式
toString()
返回字符的字符串形式,字符串的长度仅为1
Java String 类
创建字符串
最简单的方式
String str = "Runoob";
构造函数
String str2=new String("Runoob");
String 创建的字符串存储在公共池中, 而 new 创建的字符串对象在堆上
字符数组参数初始化字符串
注意:
String 类是不可改变的,所以你一旦创建了 String 对象,那它的值就无法改变了(详看笔记部分解析)。
String 类是不可改变的解析,例如: String s = "Google"; System.out.println("s = " + s); s = "Runoob"; System.out.println("s = " + s); 输出结果为: Google Runoob 从结果上看是改变了,但为什么门说String对象是不可变的呢? 原因在于实例中的 s 只是一个 String 对象的引用,并不是对象本身,当执行 s = "Runoob"; 创建了一个新的对象 "Runoob",而原来的 "Google" 还存在于内存中。 
关于 String 为啥是不可改变的
这里可以根据 jdk 的源码来分析。
字符串实际上就是一个 char 数组,并且内部就是封装了一个 char 数组。
并且这里 char 数组是被 final 修饰的:
并且 String 中的所有的方法,都是对于 char 数组的改变,只要是对它的改变,方法内部都是返回一个新的 String 实例。
如果需要对字符串做很多修改,那么应该选择使用 StringBuffer & StringBuilder 类
String 类的常见面试问题
面试题一: String s1 = "abc"; // 常量池 String s2 = new String("abc"); // 堆内存中 System.out.println(s1==s2); // false两个对象的地址值不一样。 System.out.println(s1.equals(s2)); // true 面试题二: String s1="a"+"b"+"c"; String s2="abc"; System.out.println(s1==s2); System.out.println(s1.equals(s2)); java 中常量优化机制,编译时 s1 已经成为 abc 在常量池中查找创建,s2 不需要再创建。 面试题三: String s1="ab"; String s2="abc"; String s3=s1+"c"; System.out.println(s3==s2); // false System.out.println(s3.equals(s2)); // true 先在常量池中创建 ab ,地址指向 s1, 再创建 abc ,指向 s2。对于 s3,先创建StringBuilder(或 StringBuffer)对象,通过 append 连接得到 abc ,再调用 toString() 转换得到的地址指向 s3。故 (s3==s2) 为 false。
字符串长度
length() 方法,它返回字符串对象包含的字符数
length() 方法,length 属性和 size() 方法的区别
import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { String array[] = { "First", "Second", "Third" }; String a = "HelloWorld"; List<String> list = new ArrayList<String>(); list.add(a); System.out.println("数组array的长度为" + array.length); System.out.println("字符串a的长度为" + a.length()); System.out.println("list中元素个数为" + list.size()); } } 输出的值为: 数组array的长度为3 字符串a的长度为10 list中元素个数为1
1、length() 方法是针对字符串来说的,要求一个字符串的长度就要用到它的length()方法;
2、length 属性是针对 Java 中的数组来说的,要求数组的长度可以用其 length 属性;
3、Java 中的 size() 方法是针对泛型集合说的, 如果想看这个泛型有多少个元素, 就调用此方法来查看!
连接字符串
concat() 方法
string1.concat(string2);
"我的名字是 ".concat("Runoob");
使用'+'操作符来连接字符串
"Hello," + " runoob" + "!"
实例
创建格式化字符串
printf()
format()
String 方法
1 char charAt(int index)
返回指定索引处的 char 值。
2 int compareTo(Object o)
把这个字符串和另一个对象比较。
3 int compareTo(String anotherString)
按字典顺序比较两个字符串。
4 int compareToIgnoreCase(String str)
按字典顺序比较两个字符串,不考虑大小写。
5 String concat(String str)
将指定字符串连接到此字符串的结尾。
6 boolean contentEquals(StringBuffer sb)
当且仅当字符串与指定的StringBuffer有相同顺序的字符时候返回真。
7 static String copyValueOf(char[] data)
返回指定数组中表示该字符序列的 String。
8 static String copyValueOf(char[] data, int offset, int count)
返回指定数组中表示该字符序列的 String。
9 boolean endsWith(String suffix)
测试此字符串是否以指定的后缀结束。
10 boolean equals(Object anObject)
将此字符串与指定的对象比较。
11 boolean equalsIgnoreCase(String anotherString)
将此 String 与另一个 String 比较,不考虑大小写。
12 byte[] getBytes()
使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
13 byte[] getBytes(String charsetName)
使用指定的字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
14 void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
将字符从此字符串复制到目标字符数组。
15 int hashCode()
返回此字符串的哈希码。
16 int indexOf(int ch)
返回指定字符在此字符串中第一次出现处的索引。
17 int indexOf(int ch, int fromIndex)
返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索。
18 int indexOf(String str)
返回指定子字符串在此字符串中第一次出现处的索引。
19 int indexOf(String str, int fromIndex)
返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始。
20 String intern()
返回字符串对象的规范化表示形式。
21 int lastIndexOf(int ch)
返回指定字符在此字符串中最后一次出现处的索引。
22 int lastIndexOf(int ch, int fromIndex)
返回指定字符在此字符串中最后一次出现处的索引,从指定的索引处开始进行反向搜索。
23 int lastIndexOf(String str)
返回指定子字符串在此字符串中最右边出现处的索引。
24 int lastIndexOf(String str, int fromIndex)
返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索。
25 int length()
返回此字符串的长度。
26 boolean matches(String regex)
告知此字符串是否匹配给定的正则表达式。
27 boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len)
测试两个字符串区域是否相等。
28 boolean regionMatches(int toffset, String other, int ooffset, int len)
测试两个字符串区域是否相等。
29 String replace(char oldChar, char newChar)
返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
30 String replaceAll(String regex, String replacement)
使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
31 String replaceFirst(String regex, String replacement)
使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
32 String[] split(String regex)
根据给定正则表达式的匹配拆分此字符串。
33 String[] split(String regex, int limit)
根据匹配给定的正则表达式来拆分此字符串。
34 boolean startsWith(String prefix)
测试此字符串是否以指定的前缀开始。
35 boolean startsWith(String prefix, int toffset)
测试此字符串从指定索引开始的子字符串是否以指定前缀开始。
36 CharSequence subSequence(int beginIndex, int endIndex)
返回一个新的字符序列,它是此序列的一个子序列。
37 String substring(int beginIndex)
返回一个新的字符串,它是此字符串的一个子字符串。
38 String substring(int beginIndex, int endIndex)
返回一个新字符串,它是此字符串的一个子字符串。
39 char[] toCharArray()
将此字符串转换为一个新的字符数组。
40 String toLowerCase()
使用默认语言环境的规则将此 String 中的所有字符都转换为小写。
41 String toLowerCase(Locale locale)
使用给定 Locale 的规则将此 String 中的所有字符都转换为小写。
42 String toString()
返回此对象本身(它已经是一个字符串!)。
43 String toUpperCase()
使用默认语言环境的规则将此 String 中的所有字符都转换为大写。
44 String toUpperCase(Locale locale)
使用给定 Locale 的规则将此 String 中的所有字符都转换为大写。
45 String trim()
返回字符串的副本,忽略前导空白和尾部空白。
46 static String valueOf(primitive data type x)
返回给定data type类型x参数的字符串表示形式。
47 contains(CharSequence chars)
判断是否包含指定的字符系列。
48 isEmpty()
判断字符串是否为空。
Java StringBuffer 和 StringBuilder 类
JAVA 中的 StringBuilder 和 StringBuffer 适用的场景是什么? 最简单的回答是,stringbuffer 基本没有适用场景,你应该在所有的情况下选择使用 stringbuiler,除非你真的遇到了一个需要线程安全的场景,如果遇到了,请务必在这里留言通知我。 然后,补充一点,关于线程安全,即使你真的遇到了这样的场景,很不幸的是,恐怕你仍然有 99.99....99% 的情况下没有必要选择 stringbuffer,因为 stringbuffer 的线程安全,仅仅是保证 jvm 不抛出异常顺利的往下执行而已,它可不保证逻辑正确和调用顺序正确。大多数时候,我们需要的不仅仅是线程安全,而是锁。 最后,为什么会有 stringbuffer 的存在,如果真的没有价值,为什么 jdk 会提供这个类?答案太简单了,因为最早是没有 stringbuilder 的,sun 的人不知处于何种愚蠢的考虑,决定让 stringbuffer 是线程安全的,然后大约 10 年之后,人们终于意识到这是一个多么愚蠢的决定,意识到在这 10 年之中这个愚蠢的决定为 java 运行速度慢这样的流言贡献了多大的力量,于是,在 jdk1.5 的时候,终于决定提供一个非线程安全的 stringbuffer 实现,并命名为 stringbuilder。顺便,javac 好像大概也是从这个版本开始,把所有用加号连接的 string 运算都隐式的改写成 stringbuilder,也就是说,从 jdk1.5 开始,用加号拼接字符串已经没有任何性能损失了。 如诸多评论所指出的,我上面说,"用加号拼接字符串已经没有任何性能损失了"并不严谨,严格的说,如果没有循环的情况下,单行用加号拼接字符串是没有性能损失的,java 编译器会隐式的替换成 stringbuilder,但在有循环的情况下,编译器没法做到足够智能的替换,仍然会有不必要的性能损耗,因此,用循环拼接字符串的时候,还是老老实实的用 stringbuilder 吧。
StringBuffer 和 StringBuilder 类的对象 能够被多次的修改,并且不产生新的未使用对象
两者区别
StringBuilder 的方法不是线程安全的(不能同步访问)
由于 StringBuilder 相较于 StringBuffer 有速度优势, 所以多数情况下建议使用 StringBuilder 类
StringBuilder 类实例

StringBuffer 类实例
StringBuffer 方法
主要方法
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 insert(int offset, String str)
将 str 参数的字符串插入此序列中。
6 replace(int start, int end, String str)
使用给定 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)
返回 String 对象中子字符串最后出现的位置。
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 日期时间Date类
实例化Date对象
Date( )
使用当前日期和时间来初始化对象
Date(long millisec)
接收一个参数,该参数是从 1970 年 1 月 1 日起的毫秒数
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: dow mon dd hh:mm:ss zzz yyyy 其中: dow 是一周中的某一天 (Sun, Mon, Tue, Wed, Thu, Fri, Sat)。
获取当前日期时间
使用 Date 对象的 toString() 方法来打印当前日期和时间
日期比较
使用 getTime() 方法获取两个日期(自1970年1月1日经历的毫秒数值),然后比较这两个值。
使用方法 before(),after() 和 equals()。例如,一个月的12号比18号早,则 new Date(99, 2, 12).before(new Date (99, 2, 18)) 返回true。
使用 compareTo() 方法,它是由 Comparable 接口定义的,Date 类实现了这个接口。
使用 SimpleDateFormat 格式化日期
SimpleDateFormat ft = new SimpleDateFormat ("yyyy-MM-dd hh:mm:ss"); 这一行代码确立了转换的格式,其中 yyyy 是完整的公元年,MM 是月份,dd 是日期,HH:mm:ss 是时、分、秒。 注意:有的格式大写,有的格式小写,例如 MM 是月份,mm 是分;HH 是 24 小时制,而 hh 是 12 小时制。
日期和时间的格式化编码
时间模式字符串用来指定时间格式。在此模式中,所有的 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 开头并且以下面表格中的一个字母结尾
转换符
c
包括全部日期和时间信息
星期六 十月 27 14:21:20 CST 2007
F
"年-月-日"格式
2007-10-27
D
"月/日/年"格式
10/27/07
r
"HH:MM:SS PM"格式(12时制)
02:25:51 下午
T
"HH:MM:SS"格式(24时制)
14:28:16
R
"HH:MM"格式(24时制)
14:28
实例
解析字符串为时间
parse()
按照给定的SimpleDateFormat 对象的格式化存储来解析字符串
实例
Java 休眠(sleep)
sleep()
使当前线程进入停滞状态(阻塞当前线程),让出CPU的使用、目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会。
休眠3秒
测量时间
子主题
Calendar类
Calender的月份是从0开始的,但日期和年份是从1开始的 示例代码: import java.util.Calendar; public class Test { public static void main(String[] args) { Calendar c1 = Calendar.getInstance(); c1.set(2017, 1, 1); System.out.println(c1.get(Calendar.YEAR) +"-"+c1.get(Calendar.MONTH) +"-"+c1.get(Calendar.DATE)); c1.set(2017, 1, 0); System.out.println(c1.get(Calendar.YEAR) +"-"+c1.get(Calendar.MONTH) +"-"+c1.get(Calendar.DATE)); } } 运行结果: 2017-1-1 2017-0-31 可见,将日期设为0以后,月份变成了上个月,但月份可以为0 把月份改为2试试: import java.util.Calendar; public class Test { public static void main(String[] args) { Calendar c1 = Calendar.getInstance(); c1.set(2017, 2, 1); System.out.println(c1.get(Calendar.YEAR) +"-"+c1.get(Calendar.MONTH) +"-"+c1.get(Calendar.DATE)); c1.set(2017, 2, 0); System.out.println(c1.get(Calendar.YEAR) +"-"+c1.get(Calendar.MONTH) +"-"+c1.get(Calendar.DATE)); } } 运行结果: 2017-2-1 2017-1-28 可以看到上个月的最后一天是28号,所以Calendar.MONTH为1的时候是2月 既然日期设为0表示上个月的最后一天,那是不是可以设为负数呢? import java.util.Calendar; public class Test { public static void main(String[] args) { Calendar c1 = Calendar.getInstance(); c1.set(2017, 2, 1); System.out.println(c1.get(Calendar.YEAR) +"-"+c1.get(Calendar.MONTH) +"-"+c1.get(Calendar.DATE)); c1.set(2017, 2, -10); System.out.println(c1.get(Calendar.YEAR) +"-"+c1.get(Calendar.MONTH) +"-"+c1.get(Calendar.DATE)); } } 运行结果: 2017-2-1 2017-1-18 果然可以,所以日期才可以自由加减。 月份也可以是负数,规则与日期一样,就不上代码了。 实测将年份设为非正数时,会自动变为绝对值+1,不知其意义。
创建一个代表系统当前日期的Calendar对象
Calendar c = Calendar.getInstance();//默认是当前日期
创建一个指定日期的Calendar对象
Calendar类对象字段类型
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 星期几
Calendar类对象信息的设置
Set设置
Calendar c1 = Calendar.getInstance();
调用:
public final void set(int year,int month,int date)
c1.set(2009, 6, 12);//把Calendar对象c1的年月日分别设这为:2009、6、12
利用字段类型设置
如果只设定某个字段,例如日期的值,则可以使用如下set方法:
public void set(int field,int value)
把 c1对象代表的日期设置为10号,其它所有的数值会被重新计算
c1.set(Calendar.DATE,10);
把c1对象代表的年份设置为2008年,其他的所有数值会被重新计算
c1.set(Calendar.YEAR,2008);
Add设置
Calendar c1 = Calendar.getInstance();
把c1对象的日期加上10,也就是c1也就表示为10天后的日期,其它所有的数值会被重新计算
c1.add(Calendar.DATE, 10);
把c1对象的日期减去10,也就是c1也就表示为10天前的日期,其它所有的数值会被重新计算
c1.add(Calendar.DATE, -10);
Calendar类对象信息的获得
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。
方法
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()
返回代表日历的字符串。
实例
Java Scanner 类
创建Scanner对象的基本语法
Scanner s = new Scanner(System.in);
使用 next 方法
只打印输入的第1个单词
使用 nextLine 方法
打印输入的一整行
next() 与 nextLine() 区别
next():
1、一定要读取到有效字符后才可以结束输入。
2、对输入有效字符之前遇到的空白,next() 方法会自动将其去掉。
3、只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。
next() 不能得到带有空格的字符串。
nextLine():
1、以Enter为结束符,也就是说 nextLine()方法返回的是输入回车之前的所有字符。
2、可以获得空白。
hasNextXxx() 方法
如果要输入 int 或 float 类型的数据,在 Scanner 类中也有支持,但是在输入之前最好先使用 hasNextXxx() 方法进行验证,再使用 nextXxx() 来读取
实例
输入多个数字,并求其总和与平均数,每输入一个数字用回车确认,通过输入非数字来结束输入并输出执行结果
Java 正则表达式
正则表达式是什么
正则表达式定义了字符串的模式。
正则表达式可以用来搜索、编辑或处理文本。
正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。
正则表达式实例
this is text
匹配字符串 "this is text"
this\s+is\s+text
注意字符串中的 \s+。
匹配单词 "this" 后面的 \s+ 可以匹配多个空格,之后匹配 is 字符串,再之后 \s+ 匹配多个空格然后再跟上 text 字符串。
可以匹配这个实例:this is text
^\d+(\.\d+)?
^ 定义了以什么开始
\d+ 匹配一个或多个数字
? 设置括号内的选项是可选的
\. 匹配 "."
可以匹配的实例:"5", "1.5" 和 "2.21"。
java.util.regex 包
Pattern 类:
pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。
Matcher 类:
Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。
PatternSyntaxException:
PatternSyntaxException 是一个非强制异常类,它表示一个正则表达式模式中的语法错误。
捕获组
通过从左至右计算其开括号来编号
表达式((A)(B(C)))
((A)(B(C)))
(A)
(B(C))
(C)
groupCount 方法
返回一个 int 值,表示matcher对象当前有多个捕获组
还有一个特殊的组(group(0)),它总是代表整个表达式。 该组不包括在 groupCount 的返回值中
实例
从一个给定的字符串中找到数字串
正则表达式语法
根据 Java Language Specification 的要求,Java 源代码的字符串中的反斜线被解释为 Unicode 转义或其他字符转义。因此必须在字符串字面值中使用两个反斜线,表示正则表达式受到保护,不被 Java 字节码编译器解释。例如,当解释为正则表达式时,字符串字面值 "\b" 与单个退格字符匹配,而 "\\b" 与单词边界匹配。字符串字面值 "\(hello\)" 是非法的,将导致编译时错误;要与字符串 (hello) 匹配,必须使用字符串字面值 "\\(hello\\)"。
\
将下一字符标记为特殊字符、文本、反向引用或八进制转义符。例如, 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 匹配版权符号 (©)。
Matcher 类的方法
索引方法
提供了有用的索引值,精确表明输入字符串中在哪能找到匹配
public int start()
返回以前匹配的初始索引。
public int start(int group)
返回在以前的匹配操作期间,由给定组所捕获的子序列的初始索引
public int end()
返回最后匹配字符之后的偏移量。
public int end(int group)
返回在以前的匹配操作期间,由给定组所捕获子序列的最后字符之后的偏移量。
查找方法
检查输入字符串并返回一个布尔值,表示是否找到该模式
public boolean lookingAt()
尝试将从区域开头开始的输入序列与该模式匹配。
public boolean find()
尝试查找与该模式匹配的输入序列的下一个子序列。
public boolean find(int start)
重置此匹配器,然后尝试查找匹配该模式、从指定索引开始的输入序列的下一个子序列。
public boolean matches()
尝试将整个区域与模式匹配。
替换方法
替换输入字符串里文本的方法
public Matcher appendReplacement(StringBuffer sb, String replacement)
实现非终端添加和替换步骤。
public StringBuffer appendTail(StringBuffer sb)
实现终端添加和替换步骤。
public String replaceAll(String replacement)
替换模式与给定替换字符串相匹配的输入序列的每个子序列。
public String replaceFirst(String replacement)
替换模式与给定替换字符串匹配的输入序列的第一个子序列。
public static String quoteReplacement(String s)
返回指定字符串的字面替换字符串。这个方法返回一个字符串,就像传递给Matcher类的appendReplacement 方法一个字面字符串一样工作。
start 和 end 方法
对单词 "cat" 出现在输入字符串中出现次数进行计数
可以看到这个例子是使用单词边界,以确保字母 "c" "a" "t" 并非仅是一个较长的词的子串。它也提供了一些关于输入字符串中匹配发生位置的有用信息。 Start 方法返回在以前的匹配操作期间,由给定组所捕获的子序列的初始索引,end 方法最后一个匹配字符的索引加 1。
matches 和 lookingAt 方法
都用来尝试匹配一个输入序列模式。
它们的不同是 matches 要求整个序列都匹配,而lookingAt 不要求。
lookingAt 方法虽然不需要整句都匹配,但是需要从第一个字符开始匹配。
这两个方法经常在输入字符串的开始使用。
实例
replaceFirst 和 replaceAll 方法
用来替换匹配正则表达式的文本
replaceFirst 替换首次匹配
replaceAll 替换所有匹配
appendReplacement 和 appendTail 方法
appendReplacement 和 appendTail 方法用于文本替换
PatternSyntaxException 类的方法
PatternSyntaxException 是一个非强制异常类, 它指示一个正则表达式模式中的语法错误
方法
public String getDescription()
获取错误的描述。
public int getIndex()
获取错误的索引。
public String getPattern()
获取错误的正则表达式模式。
public String getMessage()
返回多行字符串,包含语法错误及其索引的描述、错误的正则表达式模式和模式中错误索引的可视化指示。
Java 流(Stream)、文件(File)和IO
BufferedReader
读取控制台输入
Java 的控制台输入由 System.in 完成
把 System.in 包装在一个 BufferedReader 对象中来创建一个字符流
创建 BufferedReader 的基本语法
BufferedReader 对象创建后,我们便可以使用 read() 方法从控制台读取一个字符,或者用 readLine() 方法读取一个字符串
从控制台读取多字符输入
read() 方法
int read( ) throws IOException
从输入流读取一个字符并把该字符作为整数值返回
实例
从控制台不断读取字符直到用户输入 q
从控制台读取字符串
readLine() 方法
String readLine( ) throws IOException
实例
读取和显示字符行直到你输入了单词"end"
控制台输出
类 PrintStream
System.out 是该类对象的一个引用
print( ) 和 println()
继承了 OutputStream类
实现了方法 write()
write()
void write(int byteval)
将 byteval 的低八位字节写到流中
实例
把字符 "A" 和紧跟着的换行符输出到屏幕
注意:write() 方法不经常使用,因为 print() 和 println() 方法用起来更为方便
读写文件
流
一个流被定义为一个数据序列
输入流用于从源读取数据
输出流用于向目标写数据
输入流和输出流的类层次图
FileInputStream
用途和创建方法
用于从文件读取数据,它的对象可以用关键字 new 来创建
使用字符串类型的文件名来创建一个输入流对象来读取文件
InputStream f = new FileInputStream("C:/java/hello");
使用一个文件对象来创建一个输入流对象来读取文件
File f = new File("C:/java/hello"); InputStream in = new FileInputStream(f);
方法
public void close() throws IOException{}
关闭此文件输入流并释放与此流有关的所有系统资源。抛出IOException异常。
protected void finalize()throws IOException {}
这个方法清除与该文件的连接。确保在不再引用文件输入流时调用其 close 方法。抛出IOException异常。
public int read(int r)throws IOException{}
这个方法从 InputStream 对象读取指定字节的数据。返回为整数值。返回下一字节数据,如果已经到结尾则返回-1。
public int read(byte[] r) throws IOException{}
这个方法从输入流读取r.length长度的字节。返回读取的字节数。如果是文件结尾则返回-1。
public int available() throws IOException{}
返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取的字节数。返回一个整数值。
FileOutputStream
用途和创建方法
用来创建一个文件并向文件中写数据
使用字符串类型的文件名来创建一个输出流对象
OutputStream f = new FileOutputStream("C:/java/hello")
使用一个文件对象来创建一个输出流来写文件
File f = new File("C:/java/hello"); OutputStream fOut = new FileOutputStream(f);
方法
public void close() throws IOException{}
关闭此文件输入流并释放与此流有关的所有系统资源。抛出IOException异常。
protected void finalize()throws IOException {}
这个方法清除与该文件的连接。确保在不再引用文件输入流时调用其 close 方法。抛出IOException异常。
public void write(int w)throws IOException{}
这个方法把指定的字节写到输出流中。
public void write(byte[] w)
把指定数组中w.length长度的字节写到OutputStream中。
InputStream 和 OutputStream 用法的例子
实例1
上面的程序首先创建文件test.txt,并把给定的数字以 二进制形式写进该文件,同时输出到控制台上
实例2
以上代码由于是二进制写入,可能存在乱码,你可以使用 以下代码实例来解决乱码问题
文件和I/O
File Class(类)
FileReader Class(类)
FileWriter Class(类)
Java中的目录
创建目录
创建方法
mkdir( )
方法创建一个文件夹,成功则返回true,失败则返回false。失败表明File对象指定的路径已经存在,或者由于整个路径还不存在,该文件夹不能被创建。
mkdirs()
方法创建一个文件夹和它的所有父文件夹。
实例
创建 "/tmp/user/java/bin"文件夹
读取目录
调用该对象上的 list() 方法,来提取它包含的文件和文件夹的列表
实例
删除目录或文件
使用 java.io.File.delete() 方法
实例
删除目录 /tmp/java/
Java异常处理
异常的类型
简单来说
用户输入了非法数据。
要打开的文件不存在。
网络通信时连接中断,或者JVM内存溢出。
从运行角度
检查性异常:
最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
运行时异常:
运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
错误:
错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
Exception 类的层次
java.lang.Exception 类
所有的异常类是从 java.lang.Exception 类继承的子类
异常类有两个主要的子类:IOException 类和 RuntimeException 类
Java 内置异常类
非检查性异常
ArithmeticException 当出现异常的运算条件时,抛出此异常。例如,一个整数"除以零"时,抛出此类的一个实例。
ArrayIndexOutOfBoundsException 用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。
ArrayStoreException 试图将错误类型的对象存储到一个对象数组时抛出的异常。
ClassCastException 当试图将对象强制转换为不是实例的子类时,抛出该异常。
IllegalArgumentException 抛出的异常表明向方法传递了一个不合法或不正确的参数。
IllegalMonitorStateException 抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。
IllegalStateException 在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下。
IllegalThreadStateException 线程没有处于请求操作所要求的适当状态时抛出的异常。
IndexOutOfBoundsException 指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。
NegativeArraySizeException 如果应用程序试图创建大小为负的数组,则抛出该异常。
NullPointerException 当应用程序试图在需要对象的地方使用 null 时,抛出该异常
NumberFormatException 当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。
SecurityException 由安全管理器抛出的异常,指示存在安全侵犯。
StringIndexOutOfBoundsException 此异常由 String 方法抛出,指示索引或者为负,或者超出字符串的大小。
UnsupportedOperationException 当不支持请求的操作时,抛出该异常。
检查性异常类
ClassNotFoundException 应用程序试图加载类时,找不到相应的类,抛出该异常。
CloneNotSupportedException 当调用 Object 类中的 clone 方法克隆对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。
IllegalAccessException 拒绝访问一个类的时候,抛出该异常。
InstantiationException 当试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。
InterruptedException 一个线程被另一个线程中断,抛出该异常。
NoSuchFieldException 请求的变量不存在
NoSuchMethodException 请求的方法不存在
异常方法
Throwable 类的主要方法
public String getMessage()
返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了。
public Throwable getCause()
返回一个Throwable 对象代表异常原因。
public String toString()
使用getMessage()的结果返回类的串级名字。
public void printStackTrace()
打印toString()结果和栈层次到System.err,即错误输出流。
public StackTraceElement [] getStackTrace()
返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底。
public Throwable fillInStackTrace()
用当前的调用栈层次填充Throwable 对象栈层次,添加到栈层次任何先前信息中。
捕获异常
使用 try 和 catch 关键字可以捕获异常
try/catch 的语法
Catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查
如果发生的异常包含在 catch 块中,异常会被传递到 该 catch 块,这和传递一个参数到方法是一样。
实例
声明有两个元素的一个数组,当代码试图 访问数组的第四个元素的时候就会抛出一个异常
多重捕获块
一个 try 代码块后面跟随多个 catch 代码块的情况
语法
实例
throws/throw 关键字
一个方法可以声明抛出多个异常,多个异常之间用逗号隔开
使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的
实例
finally关键字
无论是否发生异常,finally 代码块中的代码总会被执行。
在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。
语法
实例
注意
catch 不能独立于 try 存在。
在 try/catch 后面添加 finally 块并非强制性要求的。
try 代码后不能既没 catch 块也没 finally 块。
try, catch, finally 块之间不能添加任何代码。
声明自定义异常
注意
所有异常都必须是 Throwable 的子类。
如果希望写一个检查性异常类,则需要继承 Exception 类。
如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
语法
只继承Exception 类来创建的异常类是检查性异常类。
实例
通用异常
JVM(Java虚拟机)异常
由 JVM 抛出的异常或错误。例如:NullPointerException 类,ArrayIndexOutOfBoundsException 类,ClassCastException 类。
程序级异常
由程序或者API程序抛出的异常。例如 IllegalArgumentException 类,IllegalStateException 类。
Java面向对象
Java 继承
继承的概念
生活中的继承
兔子和羊属于食草动物类,狮子和豹属于食肉动物类。
食草动物和食肉动物又是属于动物类。
类的继承格式
继承的实例
企鹅类
老鼠类
上面两段代码重复且维护性不高,将相同的部分提取组成一个父类
公共父类
继承之后的代码
企鹅类
老鼠类
子类调用父类的方法
继承类型
Java 不支持多继承,但支持多重继承
继承关键字
默认继承类
所有的类都是继承于 java.lang.Object
当一个类没有继承的两个关键字,则默认继承 object(这个类在 java.lang 包中,所以不需要 import)祖先类
extends关键字
一个子类只能拥有一个父类
implements关键字
implements 关键字可以变相的使java具有多继承的特性
使用范围为类继承接口的情况,可以同时继承 多个接口(接口跟接口之间采用逗号分隔)
super 与 this 关键字
super关键字
通过super关键字来实现对父类成员的访问,用来引用当前对象的父类
this关键字
指向自己的引用
final关键字
把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写
声明类:
声明方法:
注意
实例变量也可以被定义为 final,被定义为 final 的变量不能被修改
被声明为 final 类的方法自动地声明为 final,但是实例变量并不是 final
构造器
子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)
如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表
如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。
实例
Java Override/Overload
重写(Override)
外壳不变,核心重写
子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变
重写的好处
子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法
不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常
例如: 父类的一个方法申明了一个检查异常 IOException,但是在重写这个方法的时候不能抛出 Exception 异常,因为 Exception 是 IOException 的父类,抛出 IOException 异常或者 IOException 的子类异常
实例
方法的重写规则
参数列表与被重写方法的参数列表必须完全相同。
返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
父类的成员方法只能被它的子类重写。
声明为 final 的方法不能被重写。
声明为 static 的方法不能被重写,但是能够被再次声明。
子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
构造方法不能被重写。
如果不能继承一个类,则不能重写该类的方法。
Super 关键字的使用
子类中调用父类的被重写方法时,要使用 super 关键字
实例
重载(Overload)
一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
最常用的地方就是构造器的重载。
重载规则
被重载的方法必须改变参数列表(参数个数或类型不一样);
被重载的方法可以改变返回类型;
被重载的方法可以改变访问修饰符;
被重载的方法可以声明新的或更广的检查异常;
方法能够在同一个类中或者在一个子类中被重载。
无法以返回值类型作为重载函数的区分标准。
实例
重写与重载之间的区别
重载就是同样的一个方法能够根据输入数据的不同,做出不同的处理
重写就是当子类继承自父类的相同方法,输入数据一样,但要做出有别于父类的响应时,你就要覆盖父类方法
重载反映的是"随机应变". 同样一项功能, 根据数据类型的不同, 采用不同的处理方式. 比如, 同样是吃饭, 去高档餐厅吃西餐, 你会西装革履, 但是去吃火锅, 穿便装就比较合适.
重写反映的是"父子差异". 你"继承"了父亲吃火锅的爱好, 但是吃同一份鸳鸯锅(注意, 数据类型相同) , 你喜欢涮红汤, 你父亲喜欢涮清汤.
总结
(1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。
(2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。
(3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
实例比较
Java 多态
多态的优点
1. 消除类型之间的耦合关系
2. 可替换性
3. 可扩充性
4. 接口性
5. 灵活性
6. 简化性
多态存在的三个必要条件
继承
重写
父类引用指向子类对象:
Parent p = new Child();
实例
画画
当使用多态方式调用方法时,首先检查父类中是否有该方法, 如果没有,则编译错误;如果有,再去调用子类的同名方法
动物
虚函数
重写
当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。
要想调用父类中被重写的方法,则必须使用关键字super
例子解析
Employee类
Salary类
执行
结果
虚拟方法调用
实例中,实例化了两个 Salary 对象:一个使用 Salary 引用 s,另一个使用 Employee 引用 e。
当调用 s.mailCheck() 时,编译器在编译时会在 Salary 类中找到 mailCheck(),执行过程 JVM 就调用 Salary 类的 mailCheck()。
e 是 Employee 的引用,但引用 e 最终运行的是 Salary 类的 mailCheck() 方法。
在编译的时候,编译器使用 Employee 类中的 mailCheck() 方法验证该语句, 但是在运行的时候,Java虚拟机(JVM)调用的是 Salary 类中的 mailCheck() 方法。
多态的实现方式
方式一:重写
见Java 重写(Override)与重载(Overload)
方式二:接口
1. 生活中的接口最具代表性的就是插座,例如一个三接头的插头都能接在三孔插座中,因为这个是每个国家都有各自规定的接口规则,有可能到国外就不行,那是因为国外自己定义的接口类型。
2. java中的接口类似于生活中的接口,就是一些方法特征的集合,但没有方法的实现。
方式三:抽象类和抽象方法
见Java抽象类
Java 抽象类
概念
如果一个类中没有包含足够的信息来描绘一个具体的对象
除了不能实例化对象之外,类的其它功能依然存在, 成员变量、成员方法和构造方法的访问方式和普通类一样
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。
在 Java 中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
抽象类
使用 abstract class 来定义抽象类
实例
注意到该 Employee 类没有什么不同,尽管该类是抽象类,但是它仍然有 3 个成员变量,7 个成员方法和 1 个构造方法
不能创建抽象类对象
继承抽象类
抽象方法
特别的成员方法,该方法的具体实现由它的子类确定,在父类中声明
Abstract 关键字用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体
抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号。
定义方法
注意
如果一个类包含抽象方法,那么该类必须是抽象类。
任何子类必须重写父类的抽象方法,或者声明自身为抽象类。
继承抽象方法的子类必须重写该方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该抽象方法,否则,从最初的父类到最终的子类都不能用来实例化对象
继承抽象方法实例
抽象类总结规定
1. 抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
2. 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
3. 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
4. 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。
5. 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
Java 封装
封装概念
一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法
一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问
要访问该类的代码和数据,必须通过严格的接口控制。
封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
封装的优点
1. 良好的封装能够减少耦合。
2. 类内部的结构可以自由修改。
3. 可以对成员变量进行更精确的控制。
4. 隐藏信息,实现细节。
实现Java封装的步骤
1. 修改属性的可见性来限制对属性的访问(一般限制为private),例如:
这段代码中,将 name 和 age 属性设置为私有的,只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏
2. 对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问,例如:
采用 this 关键字是为了解决实例变量(private String name)和局部变量(setName(String name)中的name变量)之间发生的同名的冲突。
实例
以上实例中public方法是外部类访问该类成员变量的入口。
通常情况下,这些方法被称为getter和setter方法。
因此,任何要访问类中私有成员变量的类都要通过这些getter和setter方法
Java 接口
接口概念
接口
一个抽象类型,是抽象方法的集合,接口通常以interface来声明
一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
接口与类相似点:
一个接口可以有多个方法。
接口文件保存在 .java 结尾的文件中,文件名使用接口名。
接口的字节码文件保存在 .class 结尾的文件中。
接口相应的字节码文件必须在与包名称相匹配的目录结构中。
接口与类的区别:
接口不能用于实例化对象。
接口没有构造方法。
接口中所有的方法必须是抽象方法,Java 8 之后 接口中可以使用 default 关键字修饰的非抽象方法。
接口不能包含成员变量,除了 static 和 final 变量。
接口不是被类继承了,而是要被类实现。
接口支持多继承。
接口特性
接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为
public abstract (只能是 public abstract,其他修饰符都会报错)。
接口中可以含有变量,但是接口中的变量会被隐式的指定为
public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
抽象类和接口的区别
1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final 类型的。
3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
接口的声明
语法
Interface关键字用来声明一个接口
实例
接口是隐式抽象的,当声明一个接口的时候,不必使用abstract 关键字。
接口中每一个方法也是隐式抽象的,声明时同样不需要abstract 关键字。
接口中的方法都是公有的。
接口的实现
语法
注意
当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。
类使用implements关键字实现接口。在类声明中,Implements关键字放在class声明后面。
实例
重写接口声明中方法的规则
类在实现接口的方法时,不能抛出强制性异常,只能在接口中,或者继承接口的抽象类中抛出该强制性异常。
类在重写方法时要保持一致的方法名,并且应该保持相同或者相兼容的返回值类型。
如果实现接口的类是抽象类,那么就没必要实现该接口的方法。
实现接口的规则
一个类可以同时实现多个接口。
一个类只能继承一个类,但是能实现多个接口。
一个接口能继承另一个接口,这和类之间的继承比较相似。
接口的继承
一个接口能继承另一个接口,和类之间的继承方式比较相似
接口的继承使用extends关键字,子接口继承父接口的方法
实例
Hockey接口自己声明了四个方法,从Sports接口继承了两个方法,这样,实现Hockey接口的类需要实现六个方法。
相似的,实现Football接口的类需要实现五个方法,其中两个来自于Sports接口。
接口的多继承
类的多继承是不合法,但接口允许多继承
在接口的多继承中extends关键字只需要使用一次,在其后跟着继承接口
以上的程序片段是合法定义的子接口,与类不同的是,接口允许多继承,而 Sports及 Event 可以定义或是继承相同的方法
标记接口
没有任何方法的接口被称为标记接口,最常用的继承接口是没有包含任何方法的接口。
标记接口是没有任何方法和属性的接口.它仅仅表明它的类属于一个特定的类型,供其他代码来测试允许做一些事情。
标记接口作用:简单形象的说就是给某个对象打个标(盖个戳),使对象拥有某个或某些特权。
java.util.EventListener 接口定义
目的
建立一个公共的父接口:
正如EventListener接口,这是由几十个其他接口扩展的Java API,你可以使用一个标记接口来建立一组接口的父接口。例如:当一个接口继承了EventListener接口,Java虚拟机(JVM)就知道该接口将要被用于一个事件的代理方案。
向一个类添加数据类型:
这种情况是标记接口最初的目的,实现标记接口的类不需要定义任何接口方法(因为标记接口根本就没有方法),但是该类通过多态性变成一个接口类型。
Java 枚举
一个特殊的类,一般表示一组常量,比如一年的 4 个季节,一个年的 12 个月份,一个星期的 7 天,方向有东南西北等。
Java 枚举类使用 enum 关键字来定义,各个常量使用逗号 , 来分割
颜色实例
内部类中使用枚举
每个枚举都是通过 Class 在内部实现的,且所有的枚举值都是 public static final 的
内部类实现
迭代枚举元素
使用 for 语句来迭代枚举元素
在 switch 中使用枚举类
枚举类常应用于 switch 语句中
values(), ordinal() 和 valueOf() 方法
enum 定义的枚举类默认继承了 java.lang.Enum 类,并实现了 java.lang.Seriablizable 和 java.lang.Comparable 两个接口。
values(), ordinal() 和 valueOf() 方法位于 java.lang.Enum 类中
values() 返回枚举类中所有的值。
ordinal()方法可以找到每个枚举常量的索引,就像数组索引一样。
valueOf()方法返回指定字符串值的枚举常量。
实例
枚举类成员
枚举跟普通类一样可以用自己的变量、方法和构造函数,构造函数只能使用 private 访问修饰符,所以外部无法调用。
枚举既可以包含具体方法,也可以包含抽象方法。 如果枚举类具有抽象方法,则枚举类的每个实例都必须实现它。
实例
抽象方法实现
枚举类中的抽象方法实现,需要枚举类中的每个对象都对其进行实现
Java 包(package)
包的作用
1、把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
2、如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。
3、包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。
4、Java 使用包(package)这种机制是为了防止命名冲突,访问控制,提供搜索和定位类(class)、接口、枚举(enumerations)和注释(annotation)等。
5、开发者可以自己把一组类和接口等打包,并定义自己的包。而且在实际开发中这样做是值得提倡的,当你自己完成类的实现之后,将相关的类分组,可以让其他的编程者更容易地确定哪些类、接口、枚举和注释等是相关的。
6、由于包创建了新的命名空间(namespace),所以不会跟其他包中的任何名字产生命名冲突。使用包这种机制,更容易实现访问控制,并且让定位相关类更加简单。
包语句语法格式
那么它的路径应该是net/java/util/Something.java 这样保存的。 package(包) 的作用是把不同的 java 程序分类保存,更方便的被其他 java 程序调用。
一个包(package)可以定义为一组相互联系的类型(类、接口、枚举和注释),为这些类型提供访问保护和命名空间管理的功能。
常见包
java.lang
-打包基础的类
java.io
-包含输入输出功能的函数
创建包
创建包的时候,你需要为这个包取一个合适的名字。之后,如果其他的一个源文件包含了这个包提供的类、接口、枚举或者注释类型的时候,都必须将这个包的声明放在这个源文件的开头。
包声明应该在源文件的第一行,每个源文件只能有一个包声明,这个文件中的每个类型都应用于它。
如果一个源文件中没有使用包声明,那么其中的类,函数,枚举,注释等将被放在一个无名的包(unnamed package)中。
实例
通常使用小写的字母来命名避免与类、接口名字的冲突
编译这两个文件,并把他们放在一个叫做animals的子目录中
import 关键字
import 语句应位于 package 语句之后,所有类的定义之前
语法格式
如果在一个包中,一个类想要使用本包中的另一个类,那么该包名可以省略
实例
payroll 包已经包含了 Employee 类,接下来向 payroll 包中添加一个 Boss 类。Boss 类引用 Employee 类的时候可以不用使用 payroll 前缀
如果 Boss 类不在 payroll 包中
使用类全名描述,例如:
payroll.Employee
import 关键字引入,使用通配符 *
import payroll.*;
import 关键字引入 Employee 类:
import payroll.Employee;
注意
类文件中可以包含任意数量的 import 声明。import 声明必须在包声明之后,类声明之前。
package 的目录结构
类放在包中会有两种主要的结果
包名成为类名的一部分,正如我们前面讨论的一样。
包名必须与相应的字节码所在的目录结构相吻合。
将类、接口等类型的源码放在一个文本中,这个文件的名字就是这个类型的名字,并以.java作为扩展名
接下来,把源文件放在一个目录中,这个目录要对应类所在包的名字。
正确的类名和路径
类名 -> vehicle.Car
路径名 -> vehicle\Car.java (在 windows 系统中)
例如
有一个 com.runoob.test 的包,这个包包含一个叫做 Runoob.java 的源文件,那么相应的,应该有如下面的一连串子目录
编译的时候,编译器为包中定义的每个类、接口等类型各创建一个不同的输出文件,输出文件的名字就是这个类型的名字,并加上 .class 作为扩展后缀
例如
现在,我们用-d选项来编译这个文件,如下:
这样会像下面这样放置编译了的文件
你可以像下面这样来导入所有 \com\runoob\test\ 中定义的类、接口等
编译之后的 .class 文件应该和 .java 源文件一样,它们放置的目录应该跟包的名字对应起来。但是,并不要求 .class 文件的路径跟相应的 .java 的路径一样。你可以分开来安排源码和类的目录。
这样,你可以将你的类目录分享给其他的编程人员,而不用透露自己的源码。用这种方法管理源码和类文件可以让编译器和java 虚拟机(JVM)可以找到你程序中使用的所有类型。
类目录的绝对路径叫做class path 。设置在系统变量 CLASSPATH 中。编译器和 java 虚拟机通过将 package 名字加到 class path 后来构造 .class 文件的路径。
<path- two>\classes 是 class path,package 名字是 com.runoob.test,而编译器和 JVM 会在 <path-two>\classes\com\runoob\test 中找 .class 文件。
一个 class path 可能会包含好几个路径,多路径应该用分隔符分开。默认情况下,编译器和 JVM 查找当前目录。JAR 文件按包含 Java 平台相关的类,所以他们的目录默认放在了 class path 中。
设置 CLASSPATH 系统变量
用下面的命令显示当前的CLASSPATH变量:
Windows 平台(DOS 命令行下):C:\> set CLASSPATH
UNIX 平台(Bourne shell 下):# echo $CLASSPATH
删除当前CLASSPATH变量内容:
Windows 平台(DOS 命令行下):C:\> set CLASSPATH=
UNIX 平台(Bourne shell 下):# unset CLASSPATH; export CLASSPATH
设置CLASSPATH变量:
Windows 平台(DOS 命令行下): C:\> set CLASSPATH=C:\users\jack\java\classes
UNIX 平台(Bourne shell 下):# CLASSPATH=/home/jack/java/classes; export CLASSPATH
Java 中带包(创建及引用)的类的编译
只有一个文件时编译:
javac A.java
一个包的文件都在时编译:
javac -d . *.java
运行:
编译之后会自己生成文件夹,不要进入这个文件夹,直接运行 java -cp /home/test test.Run ,其中源文件在 test 文件夹中,包名为 test,启动文件为 Run.java
Java高级教程
Java 数据结构
枚举(Enumeration)
Enumeration接口中定义了一些方法,通过这些方法可以枚举(一次获得一个)对象集合中的元素。 这种传统接口已被迭代器取代,虽然Enumeration 还未被遗弃,但在现代代码中已经被很少使用了。尽管如此,它还是使用在诸如Vector和Properties这些传统类所定义的方法中,除此之外,还用在一些API类,并且在应用程序中也广泛被使用。 下表总结了一些Enumeration声明的方法:  实例 以下实例演示了Enumeration的使用: 实例 import java.util.Vector; import java.util.Enumeration; public class EnumerationTester { public static void main(String args[]) { Enumeration<String> days; Vector<String> dayNames = new Vector<String>(); dayNames.add("Sunday"); dayNames.add("Monday"); dayNames.add("Tuesday"); dayNames.add("Wednesday"); dayNames.add("Thursday"); dayNames.add("Friday"); dayNames.add("Saturday"); days = dayNames.elements(); while (days.hasMoreElements()){ System.out.println(days.nextElement()); } } } 以上实例编译运行结果如下: Sunday Monday Tuesday Wednesday Thursday Friday Saturday
枚举(Enumeration)接口虽然它本身不属于数据结构,但它在其他数据结构的范畴里应用很广。
枚举(The Enumeration)接口定义了一种从数据结构中取回连续元素的方式。
位集合(BitSet)
一个Bitset类创建一种特殊类型的数组来保存位值。BitSet中数组大小会随需要增加。这和位向量(vector of bits)比较类似。 这是一个传统的类,但它在Java 2中被完全重新设计。 BitSet定义了两个构造方法。 第一个构造方法创建一个默认的对象: BitSet() 第二个方法允许用户指定初始大小。所有位初始化为0。 BitSet(int size) BitSet中实现了Cloneable接口中定义的方法如下表所列: 序号 方法描述 1 void and(BitSet set) 对此目标位 set 和参数位 set 执行逻辑与操作。 2 void andNot(BitSet set) 清除此 BitSet 中所有的位,其相应的位在指定的 BitSet 中已设置。 3 int cardinality( ) 返回此 BitSet 中设置为 true 的位数。 4 void clear( ) 将此 BitSet 中的所有位设置为 false。 5 void clear(int index) 将索引指定处的位设置为 false。 6 void clear(int startIndex, int endIndex) 将指定的 startIndex(包括)到指定的 toIndex(不包括)范围内的位设置为 false。 7 Object clone( ) 复制此 BitSet,生成一个与之相等的新 BitSet。 8 boolean equals(Object bitSet) 将此对象与指定的对象进行比较。 9 void flip(int index) 将指定索引处的位设置为其当前值的补码。 10 void flip(int startIndex, int endIndex) 将指定的 fromIndex(包括)到指定的 toIndex(不包括)范围内的每个位设置为其当前值的补码。 11 boolean get(int index) 返回指定索引处的位值。 12 BitSet get(int startIndex, int endIndex) 返回一个新的 BitSet,它由此 BitSet 中从 fromIndex(包括)到 toIndex(不包括)范围内的位组成。 13 int hashCode( ) 返回此位 set 的哈希码值。 14 boolean intersects(BitSet bitSet) 如果指定的 BitSet 中有设置为 true 的位,并且在此 BitSet 中也将其设置为 true,则返回 true。 15 boolean isEmpty( ) 如果此 BitSet 中没有包含任何设置为 true 的位,则返回 true。 16 int length( ) 返回此 BitSet 的"逻辑大小":BitSet 中最高设置位的索引加 1。 17 int nextClearBit(int startIndex) 返回第一个设置为 false 的位的索引,这发生在指定的起始索引或之后的索引上。 18 int nextSetBit(int startIndex) 返回第一个设置为 true 的位的索引,这发生在指定的起始索引或之后的索引上。 19 void or(BitSet bitSet) 对此位 set 和位 set 参数执行逻辑或操作。 20 void set(int index) 将指定索引处的位设置为 true。 21 void set(int index, boolean v) 将指定索引处的位设置为指定的值。 22 void set(int startIndex, int endIndex) 将指定的 fromIndex(包括)到指定的 toIndex(不包括)范围内的位设置为 true。 23 void set(int startIndex, int endIndex, boolean v) 将指定的 fromIndex(包括)到指定的 toIndex(不包括)范围内的位设置为指定的值。 24 int size( ) 返回此 BitSet 表示位值时实际使用空间的位数。 25 String toString( ) 返回此位 set 的字符串表示形式。 26 void xor(BitSet bitSet) 对此位 set 和位 set 参数执行逻辑异或操作。 实例 下面的程序说明这个数据结构支持的几个方法: 实例 import java.util.BitSet; public class BitSetDemo { public static void main(String args[]) { BitSet bits1 = new BitSet(16); BitSet bits2 = new BitSet(16); // set some bits for(int i=0; i<16; i++) { if((i%2) == 0) bits1.set(i); if((i%5) != 0) bits2.set(i); } System.out.println("Initial pattern in bits1: "); System.out.println(bits1); System.out.println("\nInitial pattern in bits2: "); System.out.println(bits2); // AND bits bits2.and(bits1); System.out.println("\nbits2 AND bits1: "); System.out.println(bits2); // OR bits bits2.or(bits1); System.out.println("\nbits2 OR bits1: "); System.out.println(bits2); // XOR bits bits2.xor(bits1); System.out.println("\nbits2 XOR bits1: "); System.out.println(bits2); } } 以上实例编译运行结果如下: Initial pattern in bits1: {0, 2, 4, 6, 8, 10, 12, 14} Initial pattern in bits2: {1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14} bits2 AND bits1: {2, 4, 6, 8, 12, 14} bits2 OR bits1: {0, 2, 4, 6, 8, 10, 12, 14} bits2 XOR bits1: {}
位集合类实现了一组可以单独设置和清除的位或标志。
该类在处理一组布尔值的时候非常有用,你只需要给每个值赋值一"位",然后对位进行适当的设置或清除,就可以对布尔值进行操作了。
向量(Vector)
Vector 类实现了一个动态数组。和 ArrayList 很相似,但是两者是不同的: Vector 是同步访问的。 Vector 包含了许多传统的方法,这些方法不属于集合框架。 Vector 主要用在事先不知道数组的大小,或者只是需要一个可以改变大小的数组的情况。 Vector 类支持 4 种构造方法。 第一种构造方法创建一个默认的向量,默认大小为 10: Vector() 第二种构造方法创建指定大小的向量。 Vector(int size) 第三种构造方法创建指定大小的向量,并且增量用 incr 指定。增量表示向量每次增加的元素数目。 Vector(int size,int incr) 第四种构造方法创建一个包含集合 c 元素的向量: Vector(Collection c) 除了从父类继承的方法外 Vector 还定义了以下方法: 序号 方法描述 1 void add(int index, Object element) 在此向量的指定位置插入指定的元素。 2 boolean add(Object o) 将指定元素添加到此向量的末尾。 3 boolean addAll(Collection c) 将指定 Collection 中的所有元素添加到此向量的末尾,按照指定 collection 的迭代器所返回的顺序添加这些元素。 4 boolean addAll(int index, Collection c) 在指定位置将指定 Collection 中的所有元素插入到此向量中。 5 void addElement(Object obj) 将指定的组件添加到此向量的末尾,将其大小增加 1。 6 int capacity() 返回此向量的当前容量。 7 void clear() 从此向量中移除所有元素。 8 Object clone() 返回向量的一个副本。 9 boolean contains(Object elem) 如果此向量包含指定的元素,则返回 true。 10 boolean containsAll(Collection c) 如果此向量包含指定 Collection 中的所有元素,则返回 true。 11 void copyInto(Object[] anArray) 将此向量的组件复制到指定的数组中。 12 Object elementAt(int index) 返回指定索引处的组件。 13 Enumeration elements() 返回此向量的组件的枚举。 14 void ensureCapacity(int minCapacity) 增加此向量的容量(如有必要),以确保其至少能够保存最小容量参数指定的组件数。 15 boolean equals(Object o) 比较指定对象与此向量的相等性。 16 Object firstElement() 返回此向量的第一个组件(位于索引 0) 处的项)。 17 Object get(int index) 返回向量中指定位置的元素。 18 int hashCode() 返回此向量的哈希码值。 19 int indexOf(Object elem) 返回此向量中第一次出现的指定元素的索引,如果此向量不包含该元素,则返回 -1。 20 int indexOf(Object elem, int index) 返回此向量中第一次出现的指定元素的索引,从 index 处正向搜索,如果未找到该元素,则返回 -1。 21 void insertElementAt(Object obj, int index) 将指定对象作为此向量中的组件插入到指定的 index 处。 22 boolean isEmpty() 测试此向量是否不包含组件。 23 Object lastElement() 返回此向量的最后一个组件。 24 int lastIndexOf(Object elem) 返回此向量中最后一次出现的指定元素的索引;如果此向量不包含该元素,则返回 -1。 25 int lastIndexOf(Object elem, int index) 返回此向量中最后一次出现的指定元素的索引,从 index 处逆向搜索,如果未找到该元素,则返回 -1。 26 Object remove(int index) 移除此向量中指定位置的元素。 27 boolean remove(Object o) 移除此向量中指定元素的第一个匹配项,如果向量不包含该元素,则元素保持不变。 28 boolean removeAll(Collection c) 从此向量中移除包含在指定 Collection 中的所有元素。 29 void removeAllElements() 从此向量中移除全部组件,并将其大小设置为零。 30 boolean removeElement(Object obj) 从此向量中移除变量的第一个(索引最小的)匹配项。 31 void removeElementAt(int index) 删除指定索引处的组件。 32 protected void removeRange(int fromIndex, int toIndex) 从此 List 中移除其索引位于 fromIndex(包括)与 toIndex(不包括)之间的所有元素。 33 boolean retainAll(Collection c) 在此向量中仅保留包含在指定 Collection 中的元素。 34 Object set(int index, Object element) 用指定的元素替换此向量中指定位置处的元素。 35 void setElementAt(Object obj, int index) 将此向量指定 index 处的组件设置为指定的对象。 36 void setSize(int newSize) 设置此向量的大小。 37 int size() 返回此向量中的组件数。 38 List subList(int fromIndex, int toIndex) 返回此 List 的部分视图,元素范围为从 fromIndex(包括)到 toIndex(不包括)。 39 Object[] toArray() 返回一个数组,包含此向量中以恰当顺序存放的所有元素。 40 Object[] toArray(Object[] a) 返回一个数组,包含此向量中以恰当顺序存放的所有元素;返回数组的运行时类型为指定数组的类型。 41 String toString() 返回此向量的字符串表示形式,其中包含每个元素的 String 表示形式。 42 void trimToSize() 对此向量的容量进行微调,使其等于向量的当前大小。 实例 下面的程序说明这个集合所支持的几种方法: import java.util.*; public class VectorDemo { public static void main(String args[]) { // initial size is 3, increment is 2 Vector v = new Vector(3, 2); System.out.println("Initial size: " + v.size()); System.out.println("Initial capacity: " + v.capacity()); v.addElement(new Integer(1)); v.addElement(new Integer(2)); v.addElement(new Integer(3)); v.addElement(new Integer(4)); System.out.println("Capacity after four additions: " + v.capacity()); v.addElement(new Double(5.45)); System.out.println("Current capacity: " + v.capacity()); v.addElement(new Double(6.08)); v.addElement(new Integer(7)); System.out.println("Current capacity: " + v.capacity()); v.addElement(new Float(9.4)); v.addElement(new Integer(10)); System.out.println("Current capacity: " + v.capacity()); v.addElement(new Integer(11)); v.addElement(new Integer(12)); System.out.println("First element: " + (Integer)v.firstElement()); System.out.println("Last element: " + (Integer)v.lastElement()); if(v.contains(new Integer(3))) System.out.println("Vector contains 3."); // enumerate the elements in the vector. Enumeration vEnum = v.elements(); System.out.println("\nElements in vector:"); while(vEnum.hasMoreElements()) System.out.print(vEnum.nextElement() + " "); System.out.println(); } } 以上实例编译运行结果如下: Initial size: 0 Initial capacity: 3 Capacity after four additions: 5 Current capacity: 5 Current capacity: 7 Current capacity: 9 First element: 1 Last element: 12 Vector contains 3. Elements in vector: 1 2 3 4 5.45 6.08 7 9.4 10 11 12
向量(Vector)类和传统数组非常相似,但是Vector的大小能根据需要动态的变化。
和数组一样,Vector对象的元素也能通过索引访问。
使用Vector类最主要的好处就是在创建对象的时候不必给对象指定大小,它的大小会根据需要动态的变化。
栈(Stack)
栈是Vector的一个子类,它实现了一个标准的后进先出的栈。 堆栈只定义了默认构造函数,用来创建一个空栈。 堆栈除了包括由Vector定义的所有方法,也定义了自己的一些方法。 Stack() 除了由Vector定义的所有方法,自己也定义了一些方法: 序号 方法描述 1 boolean empty() 测试堆栈是否为空。 2 Object peek( ) 查看堆栈顶部的对象,但不从堆栈中移除它。 3 Object pop( ) 移除堆栈顶部的对象,并作为此函数的值返回该对象。 4 Object push(Object element) 把项压入堆栈顶部。 5 int search(Object element) 返回对象在堆栈中的位置,以 1 为基数。 实例 下面的程序说明这个集合所支持的几种方法 实例 import java.util.*; public class StackDemo { static void showpush(Stack<Integer> st, int a) { st.push(new Integer(a)); System.out.println("push(" + a + ")"); System.out.println("stack: " + st); } static void showpop(Stack<Integer> st) { System.out.print("pop -> "); Integer a = (Integer) st.pop(); System.out.println(a); System.out.println("stack: " + st); } public static void main(String args[]) { Stack<Integer> st = new Stack<Integer>(); System.out.println("stack: " + st); showpush(st, 42); showpush(st, 66); showpush(st, 99); showpop(st); showpop(st); showpop(st); try { showpop(st); } catch (EmptyStackException e) { System.out.println("empty stack"); } } } 以上实例编译运行结果如下: stack: [ ] push(42) stack: [42] push(66) stack: [42, 66] push(99) stack: [42, 66, 99] pop -> 99 stack: [42, 66] pop -> 66 stack: [42] pop -> 42 stack: [ ] pop -> empty stack
栈(Stack)实现了一个后进先出(LIFO)的数据结构。
你可以把栈理解为对象的垂直分布的栈,当你添加一个新元素时,就将新元素放在其他元素的顶部。
当你从栈中取元素的时候,就从栈顶取一个元素。换句话说,最后进栈的元素最先被取出。
字典(Dictionary)
Dictionary 类是一个抽象类,用来存储键/值对,作用和Map类相似。 给出键和值,你就可以将值存储在Dictionary对象中。一旦该值被存储,就可以通过它的键来获取它。所以和Map一样, Dictionary 也可以作为一个键/值对列表。 Dictionary定义的抽象方法如下表所示: 序号 方法描述 1 Enumeration elements( ) 返回此 dictionary 中值的枚举。 2 Object get(Object key) 返回此 dictionary 中该键所映射到的值。 3 boolean isEmpty( ) 测试此 dictionary 是否不存在从键到值的映射。 4 Enumeration keys( ) 返回此 dictionary 中的键的枚举。 5 Object put(Object key, Object value) 将指定 key 映射到此 dictionary 中指定 value。 6 Object remove(Object key) 从此 dictionary 中移除 key (及其相应的 value)。 7 int size( ) 返回此 dictionary 中条目(不同键)的数量。 Dictionary类已经过时了。在实际开发中,你可以实现Map接口来获取键/值的存储功能。
字典(Dictionary) 类是一个抽象类,它定义了键映射到值的数据结构。
当你想要通过特定的键而不是整数索引来访问数据的时候,这时候应该使用Dictionary。
由于Dictionary类是抽象类,所以它只提供了键映射到值的数据结构,而没有提供特定的实现。
哈希表(Hashtable)
Hashtable是原始的java.util的一部分, 是一个Dictionary具体的实现 。 然而,Java 2 重构的Hashtable实现了Map接口,因此,Hashtable现在集成到了集合框架中。它和HashMap类很相似,但是它支持同步。 像HashMap一样,Hashtable在哈希表中存储键/值对。当使用一个哈希表,要指定用作键的对象,以及要链接到该键的值。 然后,该键经过哈希处理,所得到的散列码被用作存储在该表中值的索引。 Hashtable定义了四个构造方法。第一个是默认构造方法: Hashtable() 第二个构造函数创建指定大小的哈希表: Hashtable(int size) 第三个构造方法创建了一个指定大小的哈希表,并且通过fillRatio指定填充比例。 填充比例必须介于0.0和1.0之间,它决定了哈希表在重新调整大小之前的充满程度: Hashtable(int size,float fillRatio) 第四个构造方法创建了一个以M中元素为初始化元素的哈希表。 哈希表的容量被设置为M的两倍。 Hashtable(Map m) Hashtable中除了从Map接口中定义的方法外,还定义了以下方法: 序号 方法描述 1 void clear( ) 将此哈希表清空,使其不包含任何键。 2 Object clone( ) 创建此哈希表的浅表副本。 3 boolean contains(Object value) 测试此映射表中是否存在与指定值关联的键。 4 boolean containsKey(Object key) 测试指定对象是否为此哈希表中的键。 5 boolean containsValue(Object value) 如果此 Hashtable 将一个或多个键映射到此值,则返回 true。 6 Enumeration elements( ) 返回此哈希表中的值的枚举。 7 Object get(Object key) 返回指定键所映射到的值,如果此映射不包含此键的映射,则返回 null. 更确切地讲,如果此映射包含满足 (key.equals(k)) 的从键 k 到值 v 的映射,则此方法返回 v;否则,返回 null。 8 boolean isEmpty( ) 测试此哈希表是否没有键映射到值。 9 Enumeration keys( ) 返回此哈希表中的键的枚举。 10 Object put(Object key, Object value) 将指定 key 映射到此哈希表中的指定 value。 11 void rehash( ) 增加此哈希表的容量并在内部对其进行重组,以便更有效地容纳和访问其元素。 12 Object remove(Object key) 从哈希表中移除该键及其相应的值。 13 int size( ) 返回此哈希表中的键的数量。 14 String toString( ) 返回此 Hashtable 对象的字符串表示形式,其形式为 ASCII 字符 ", " (逗号加空格)分隔开的、括在括号中的一组条目。 实例 下面的程序说明这个数据结构支持的几个方法: import java.util.*; public class HashTableDemo { public static void main(String args[]) { // Create a hash map Hashtable balance = new Hashtable(); Enumeration names; String str; double bal; balance.put("Zara", new Double(3434.34)); balance.put("Mahnaz", new Double(123.22)); balance.put("Ayan", new Double(1378.00)); balance.put("Daisy", new Double(99.22)); balance.put("Qadir", new Double(-19.08)); // Show all balances in hash table. names = balance.keys(); while(names.hasMoreElements()) { str = (String) names.nextElement(); System.out.println(str + ": " + balance.get(str)); } System.out.println(); // Deposit 1,000 into Zara's account bal = ((Double)balance.get("Zara")).doubleValue(); balance.put("Zara", new Double(bal+1000)); System.out.println("Zara's new balance: " + balance.get("Zara")); } } 以上实例编译运行结果如下: Qadir: -19.08 Zara: 3434.34 Mahnaz: 123.22 Daisy: 99.22 Ayan: 1378.0 Zara's new balance: 4434.34
Hashtable类提供了一种在用户定义键结构的基础上来组织数据的手段。
例如,在地址列表的哈希表中,你可以根据邮政编码作为键来存储和排序数据,而不是通过人名。
哈希表键的具体含义完全取决于哈希表的使用情景和它包含的数据。
属性(Properties)
Properties 继承于 Hashtable。表示一个持久的属性集.属性列表中每个键及其对应值都是一个字符串。 Properties 类被许多 Java 类使用。例如,在获取环境变量时它就作为 System.getProperties() 方法的返回值。 Properties 定义如下实例变量.这个变量持有一个 Properties 对象相关的默认属性列表。 Properties defaults; Properties类定义了两个构造方法. 第一个构造方法没有默认值。 Properties() 第二个构造方法使用propDefault 作为默认值。两种情况下,属性列表都为空: Properties(Properties propDefault) 除了从 Hashtable 中所定义的方法,Properties 还定义了以下方法: 序号 方法描述 1 String getProperty(String key) 用指定的键在此属性列表中搜索属性。 2 String getProperty(String key, String defaultProperty) 用指定的键在属性列表中搜索属性。 3 void list(PrintStream streamOut) 将属性列表输出到指定的输出流。 4 void list(PrintWriter streamOut) 将属性列表输出到指定的输出流。 5 void load(InputStream streamIn) throws IOException 从输入流中读取属性列表(键和元素对)。 6 Enumeration propertyNames( ) 按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。 7 Object setProperty(String key, String value) 调用 Hashtable 的方法 put。 8 void store(OutputStream streamOut, String description) 以适合使用 load(InputStream)方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。 实例 下面的程序说明这个数据结构支持的几个方法: 实例 import java.util.*; public class PropDemo { public static void main(String args[]) { Properties capitals = new Properties(); Set states; String str; capitals.put("Illinois", "Springfield"); capitals.put("Missouri", "Jefferson City"); capitals.put("Washington", "Olympia"); capitals.put("California", "Sacramento"); capitals.put("Indiana", "Indianapolis"); // Show all states and capitals in hashtable. states = capitals.keySet(); // get set-view of keys Iterator itr = states.iterator(); while(itr.hasNext()) { str = (String) itr.next(); System.out.println("The capital of " + str + " is " + capitals.getProperty(str) + "."); } System.out.println(); // look for state not in list -- specify default str = capitals.getProperty("Florida", "Not Found"); System.out.println("The capital of Florida is " + str + "."); } } 以上实例编译运行结果如下: The capital of Missouri is Jefferson City. The capital of Illinois is Springfield. The capital of Indiana is Indianapolis. The capital of California is Sacramento. The capital of Washington is Olympia. The capital of Florida is Not Found.
Properties 继承于 Hashtable.Properties 类表示了一个持久的属性集.属性列表中每个键及其对应值都是一个字符串。
Properties 类被许多Java类使用。例如,在获取环境变量时它就作为System.getProperties()方法的返回值。
附
迭代器 iterator 用法
Java 中的 Iterator 功能比较简单,并且只能单向移动:
(1) 使用方法 iterator() 要求容器返回一个 Iterator。第一次调用 Iterator 的 next() 方法时,它返回序列的第一个元素。注意:iterator() 方法是 java.lang.Iterable 接口,被 Collection 继承。
(2) 使用 next() 获得序列中的下一个元素。
(3) 使用 hasNext() 检查序列中是否还有元素。
(4) 使用 remove() 将迭代器新返回的元素删除。
Java 集合框架
早在 Java 2 中之前,Java 就提供了特设类。比如:Dictionary, Vector, Stack, 和 Properties 这些类用来存储和操作对象组。
虽然这些类都非常有用,但是它们缺少一个核心的,统一的主题。由于这个原因,使用 Vector 类的方式和使用 Properties 类的方式有着很大不同。
设计目标
该框架必须是高性能的。基本集合(动态数组,链表,树,哈希表)的实现也必须是高效的。
该框架允许不同类型的集合,以类似的方式工作,具有高度的互操作性。
对一个集合的扩展和适应必须是简单的。
为此,整个集合框架就围绕一组标准接口而设计。你可以直接使用这些接口的标准实现,诸如: LinkedList, HashSet, 和 TreeSet 等,除此之外你也可以通过这些接口实现自己的集合。
Java集合框架图
从上面的集合框架图可以看到,Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。Collection 接口又有 3 种子类型,List、Set 和 Queue,再下面是一些抽象类,最后是具体实现类,常用的有ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap
内容

接口:
是代表集合的抽象数据类型。例如 Collection、List、Set、Map 等。之所以定义多个接口,是为了以不同的方式操作集合对象
实现(类):
是集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构,例如:ArrayList、LinkedList、HashSet、HashMap。
算法:
是实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序。这些算法被称为多态,那是因为相同的方法可以在相似的接口上有着不同的实现。
集合接口
Collection 接口
Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素, Java不提供直接继承自Collection的类,只提供继承于的子接口(如List和set)。
Collection 接口存储一组不唯一,无序的对象。
List 接口
List接口是一个有序的 Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在List中位置,类似于数组的下标)来访问List中的元素,第一个元素的索引为 0,而且允许有相同的元素。
List 接口存储一组不唯一,有序(插入顺序)的对象。
Set
Set 具有与 Collection 完全一样的接口,只是行为上不同,Set 不保存重复的元素。
Set 接口存储一组唯一,无序的对象。
SortedSet
继承于Set保存有序的集合。
Map
Map 接口存储一组键值对象,提供key(键)到value(值)的映射。
Map.Entry
描述在一个Map中的一个元素(键/值对)。是一个 Map 的内部接口。
SortedMap
继承于 Map,使 Key 保持在升序排列。
Enumeration
这是一个传统的接口和定义的方法,通过它可以枚举(一次获得一个)对象集合中的元素。这个传统接口已被迭代器取代。
Set和List的区别
1. Set 接口实例存储的是无序的,不重复的数据。List 接口实例存储的是有序的,可以重复的元素。
2. Set检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变
<实现类有HashSet,TreeSet>
3. List和数组类似,可以动态增长,根据实际存储的数据的长度自动增长List的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变<实现类有ArrayList,LinkedList,Vector>
集合实现类(集合类)
标准集合类
AbstractCollection
实现了大部分的集合接口。
AbstractList
继承于AbstractCollection 并且实现了大部分List接口。
AbstractSequentialList
继承于 AbstractList ,提供了对数据元素的链式访问而不是随机访问。
LinkedList
该类实现了List接口,允许有null(空)元素。主要用于创建链表数据结构,该类没有同步方法,如果多个线程同时访问一个List,则必须自己实现访问同步,解决方法就是在创建List时候构造一个同步的List。例如:
List list=Collections.synchronizedList(newLinkedList(...));
LinkedList 查找效率低。
ArrayList
该类也是实现了List的接口,实现了可变大小的数组,随机访问和遍历元素时,提供更好的性能。该类也是非同步的,在多线程的情况下不要使用。ArrayList 增长当前长度的50%,插入删除效率低。
AbstractSet
继承于AbstractCollection 并且实现了大部分Set接口。
HashSet
该类实现了Set接口,不允许出现重复元素,不保证集合中元素的顺序,允许包含值为null的元素,但最多只能一个。
LinkedHashSet
具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。
TreeSet
该类实现了Set接口,可以实现排序等功能。
AbstractMap
实现了大部分的Map接口。
HashMap
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
该类实现了Map接口,根据键的HashCode值存储数据,具有很快的访问速度,最多允许一条记录的键为null,不支持线程同步。
TreeMap
继承了AbstractMap,并且使用一颗树。
WeakHashMap
继承AbstractMap类,使用弱密钥的哈希表。
LinkedHashMap
继承于HashMap,使用元素的自然顺序对元素进行排序.
IdentityHashMap
继承AbstractMap类,比较文档时使用引用相等。
java.util包中定义的类
Vector
该类和ArrayList非常相似,但是该类是同步的,可以用在多线程的情况,该类允许设置默认的增长长度,默认扩容方式为原来的2倍。
Stack
栈是Vector的一个子类,它实现了一个标准的后进先出的栈。
Dictionary
Dictionary 类是一个抽象类,用来存储键/值对,作用和Map类相似。
Hashtable
Hashtable 是 Dictionary(字典) 类的子类,位于 java.util 包中。
Properties
Properties 继承于 Hashtable,表示一个持久的属性集,属性列表中每个键及其对应值都是一个字符串。
BitSet
一个Bitset类创建一种特殊类型的数组来保存位值。BitSet中数组大小会随需要增加。
集合算法
集合框架定义了几种算法,可用于集合和映射。这些算法被定义为集合类的静态方法。
在尝试比较不兼容的类型时,一些方法能够抛出 ClassCastException异常。当试图修改一个不可修改的集合时,抛出UnsupportedOperationException异常。
集合定义三个静态的变量:EMPTY_SET,EMPTY_LIST,EMPTY_MAP的。这些变量都不可改变。
Collection Algorithms
这里是一个列表中的所有算法实现。
如何使用迭代器
通常情况下,你会希望遍历一个集合中的元素。例如,显示集合中的每个元素。
一般遍历数组都是采用for循环或者增强for,这两个方法也可以用在集合框架,但是还有一种方法是采用迭代器遍历集合框架,它是一个对象,实现了Iterator接口或 ListIterator接口。
迭代器,使你能够通过循环来得到或删除集合的元素。ListIterator 继承了 Iterator,以允许双向遍历列表和修改元素。
使用 Java Iterator
迭代器 it 的两个基本操作是 next 、hasNext 和 remove。 调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。 调用 it.hasNext() 用于检测集合中是否还有元素。 调用 it.remove() 将迭代器返回的元素删除。 Iterator 类位于 java.util 包中,使用前需要引入它,语法格式如下: import java.util.Iterator; // 引入 Iterator 类 获取一个迭代器 集合想获取一个迭代器可以使用 iterator() 方法: 实例 // 引入 ArrayList 和 Iterator 类 import java.util.ArrayList; import java.util.Iterator; public class RunoobTest { public static void main(String[] args) { // 创建集合 ArrayList<String> sites = new ArrayList<String>(); sites.add("Google"); sites.add("Runoob"); sites.add("Taobao"); sites.add("Zhihu"); // 获取迭代器 Iterator<String> it = sites.iterator(); // 输出集合中的第一个元素 System.out.println(it.next()); } } 执行以上代码,输出结果如下: Google 循环集合元素 让迭代器 it 逐个返回集合中所有元素最简单的方法是使用 while 循环: while(it.hasNext()) { System.out.println(it.next()); } 以下输出集合 sites 中的所有元素: 实例 // 引入 ArrayList 和 Iterator 类 import java.util.ArrayList; import java.util.Iterator; public class RunoobTest { public static void main(String[] args) { // 创建集合 ArrayList<String> sites = new ArrayList<String>(); sites.add("Google"); sites.add("Runoob"); sites.add("Taobao"); sites.add("Zhihu"); // 获取迭代器 Iterator<String> it = sites.iterator(); // 输出集合中的所有元素 while(it.hasNext()) { System.out.println(it.next()); } } } 执行以上代码,输出结果如下: Google Runoob Taobao Zhihu 删除元素 要删除集合中的元素可以使用 remove() 方法。 以下实例我们删除集合中小于 10 的元素: 实例 // 引入 ArrayList 和 Iterator 类 import java.util.ArrayList; import java.util.Iterator; public class RunoobTest { public static void main(String[] args) { ArrayList<Integer> numbers = new ArrayList<Integer>(); numbers.add(12); numbers.add(8); numbers.add(2); numbers.add(23); Iterator<Integer> it = numbers.iterator(); while(it.hasNext()) { Integer i = it.next(); if(i < 10) { it.remove(); // 删除小于 10 的元素 } } System.out.println(numbers); } } 执行以上代码,输出结果如下: [12, 23]
这里通过实例列出 Iterator 和 ListIterator 接口提供的所有方法
遍历 ArrayList
三种方法都是用来遍历ArrayList集合,第三种方法是采用迭代器的方法,该方法可以不用担心在遍历的过程中会超出集合的长度
遍历Map
如何使用比较器
TreeSet和TreeMap的按照排序顺序来存储元素. 然而,这是通过比较器来精确定义按照什么样的排序顺序。
这个接口可以让我们以不同的方式来排序一个集合。
使用 Java Comparator
这里通过实例列出Comparator接口提供的所有方法
总结
Java集合框架为程序员提供了预先包装的数据结构和算法来操纵他们。
集合是一个对象,可容纳其他对象的引用。集合接口声明对每一种类型的集合可以执行的操作。
集合框架的类和接口均在java.util包中。
任何对象加入集合类后,自动转变为Object类型,所以在取出的时候,需要进行强制类型转换。
Java ArrayList
ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。
ArrayList 继承了 AbstractList ,并实现了 List 接口。
ArrayList类使用语法
E
泛型数据类型,用于设置 objectName 的数据类型, 只能为引用数据类型
objectName
对象名。
添加元素
添加元素到 ArrayList 可以使用 add() 方法
访问元素
访问 ArrayList 中的元素可以使用 get() 方法
修改元素
如果要修改 ArrayList 中的元素可以使用 set() 方法
删除元素
如果要删除 ArrayList 中的元素可以使用 remove() 方法
计算大小
如果要计算 ArrayList 中的元素数量可以使用 size() 方法
迭代数组列表
使用 for 来迭代数组列表中的元素
使用 for-each 来迭代元素
其他的引用类型
<E> 只能为引用数据类型,这时我们就需要使用到基本类型的包装类
基本类型对应的包装类
BigInteger、BigDecimal 用于高精度的运算,BigInteger 支持任意精度的整数,也是引用类型,但它们没有相对应的基本类型
使用 ArrayList 存储数字
ArrayList 排序
Collections 类也是一个非常有用的类,位于 java.util 包中,提供的 sort() 方法可以对字符或数字列表进行排序
Java ArrayList 方法
add() 将元素插入到指定位置的 arraylist 中
addAll() 添加集合中的所有元素到 arraylist 中
clear() 删除 arraylist 中的所有元素
clone() 复制一份 arraylist
contains() 判断元素是否在 arraylist
get() 通过索引值获取 arraylist 中的元素
indexOf() 返回 arraylist 中元素的索引值
removeAll() 删除存在于指定集合中的 arraylist 里的所有元素
remove() 删除 arraylist 里的单个元素
size() 返回 arraylist 里元素数量
isEmpty() 判断 arraylist 是否为空
subList() 截取部分 arraylist 的元素
set() 替换 arraylist 中指定索引的元素
sort() 对 arraylist 元素进行排序
toArray() 将 arraylist 转换为数组
toString() 将 arraylist 转换为字符串
ensureCapacity() 设置指定容量大小的 arraylist
lastIndexOf() 返回指定元素在 arraylist 中最后一次出现的位置
retainAll() 保留 arraylist 中在指定集合中也存在的那些元素
containsAll() 查看 arraylist 是否包含指定集合中的所有元素
trimToSize() 将 arraylist 中的容量调整为数组中的元素个数
removeRange() 删除 arraylist 中指定索引之间存在的元素
replaceAll() 将给定的操作内容替换掉数组中每一个元素
removeIf() 删除所有满足特定条件的 arraylist 元素
forEach() 遍历 arraylist 中每一个元素并执行特定操作
Java LinkedList
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的地址。
链表分类
单向链表
包含两个值: 当前节点的值和一个指向下一个节点的链接
双向链表
有三个整数值: 数值、向后的节点链接、向前的节点链接
与ArrayList区别
与 ArrayList 相比,LinkedList 的增加和删除的操作效率更高,而查找和修改的操作效率较低
以下情况使用 ArrayList
频繁访问列表中的某一个元素。
只需要在列表末尾进行添加和删除元素操作。
以下情况使用 LinkedList
你需要通过循环迭代来访问列表中的某些元素。
需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。
LinkedList特性
LinkedList 继承了 AbstractSequentialList 类。
LinkedList 实现了 Queue 接口,可作为队列使用。
LinkedList 实现了 List 接口,可进行列表的相关操作。
LinkedList 实现了 Deque 接口,可作为队列使用。
LinkedList 实现了 Cloneable 接口,可实现克隆。
LinkedList 实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。
LinkedList语法
创建链表
在列表开头添加元素
在列表结尾添加元素
在列表开头移除元素
在列表结尾移除元素
获取列表开头的元素
获取列表结尾的元素
迭代元素
常用方法
public boolean add(E e) 链表末尾添加元素,返回是否成功,成功为 true,失败为 false。
public void add(int index, E element) 向指定位置插入元素。
public boolean addAll(Collection c) 将一个集合的所有元素添加到链表后面,返回是否成功,成功为 true,失败为 false。
public boolean addAll(int index, Collection c) 将一个集合的所有元素添加到链表的指定位置后面,返回是否成功,成功为 true,失败为 false。
public void addFirst(E e) 元素添加到头部。
public void addLast(E e) 元素添加到尾部。
public boolean offer(E e) 向链表末尾添加元素,返回是否成功,成功为 true,失败为 false。
public boolean offerFirst(E e) 头部插入元素,返回是否成功,成功为 true,失败为 false。
public boolean offerLast(E e) 尾部插入元素,返回是否成功,成功为 true,失败为 false。
public void clear() 清空链表。
public E removeFirst() 删除并返回第一个元素。
public E removeLast() 删除并返回最后一个元素。
public boolean remove(Object o) 删除某一元素,返回是否成功,成功为 true,失败为 false。
public E remove(int index) 删除指定位置的元素。
public E poll() 删除并返回第一个元素。
public E remove() 删除并返回第一个元素。
public boolean contains(Object o) 判断是否含有某一元素。
public E get(int index) 返回指定位置的元素。
public E getFirst() 返回第一个元素。
public E getLast() 返回最后一个元素。
public int indexOf(Object o) 查找指定元素从前往后第一次出现的索引。
public int lastIndexOf(Object o) 查找指定元素最后一次出现的索引。
public E peek() 返回第一个元素。
public E element() 返回第一个元素。
public E peekFirst() 返回头部元素。
public E peekLast() 返回尾部元素。
public E set(int index, E element) 设置指定位置的元素。
public Object clone() 克隆该列表。
public Iterator descendingIterator() 返回倒序迭代器。
public int size() 返回链表元素个数。
public ListIterator listIterator(int index) 返回从指定位置开始到末尾的迭代器。
public Object[] toArray() 返回一个由链表元素组成的数组。
public T[] toArray(T[] a) 返回一个由链表元素转换类型而成的数组。
Java HashSet
HashSet特性

HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。
HashSet 允许有 null 值。
HashSet 是无序的,即不会记录插入的顺序。
HashSet 不是线程安全的, 如果多个线程尝试同时修改 HashSet,则最终结果是不确定的。 您必须在多线程访问时显式同步对 HashSet 的并发访问。
HashSet 实现了 Set 接口。
HashSet 中的元素实际上是对象,一些常见的基本类型可以使用它的包装类

HashSet 语法
添加元素
添加元素可以使用 add() 方法
在上面的实例中,Runoob 被添加了两次,它在集合中也只会出现一次,因为集合中的每个元素都必须是唯一的
判断元素是否存在
使用 contains() 方法来判断元素是否存在于集合当中
删除元素
使用 remove() 方法来删除集合中的元素
删除所有元素
删除集合中所有元素可以使用 clear 方法
计算大小
计算 HashSet 中的元素数量可以使用 size() 方法
迭代HashSet
使用 for-each 来迭代 HashSet 中的元素
Java HashMap
HashMap特性
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步。
HashMap 是无序的,即不会记录插入的顺序。
HashMap 继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口。

HashMap 的 key 与 value 类型可以相同也可以不同,可以是字符串(String)类型的 key 和 value,也可以是整型(Integer)的 key 和字符串(String)类型的 value。

HashMap 中的元素实际上是对象,一些常见的基本类型可以使用它的包装类。

HashMap 语法
添加元素
添加键值对(key-value)可以使用 put() 方法
访问元素
使用 get(key) 方法来获取 key 对应的 value
删除元素
使用 remove(key) 方法来删除 key 对应的键值对(key-value)
删除所有元素
删除所有键值对(key-value)可以使用 clear 方法
计算大小
计算 HashMap 中的元素数量可以使用 size() 方法
迭代 HashMap
使用 for-each 来迭代 HashMap 中的元素
如果你只想获取 key,可以使用 keySet() 方法,然后可以通过 get(key) 获取对应的 value,如果你只想获取 value,可以使用 values() 方法。
HashMap 方法
clear() 删除 hashMap 中的所有键/值对
clone() 复制一份 hashMap
isEmpty() 判断 hashMap 是否为空
size() 计算 hashMap 中键/值对的数量
put() 将键/值对添加到 hashMap 中
putAll() 将所有键/值对添加到 hashMap 中
putIfAbsent() 如果 hashMap 中不存在指定的键,则将指定的键/值对插入到 hashMap 中。
remove() 删除 hashMap 中指定键 key 的映射关系
containsKey() 检查 hashMap 中是否存在指定的 key 对应的映射关系。
containsValue() 检查 hashMap 中是否存在指定的 value 对应的映射关系。
replace() 替换 hashMap 中是指定的 key 对应的 value。
replaceAll() 将 hashMap 中的所有映射关系替换成给定的函数所执行的结果。
get() 获取指定 key 对应对 value
getOrDefault() 获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值
forEach() 对 hashMap 中的每个映射执行指定的操作。
entrySet() 返回 hashMap 中所有映射项的集合集合视图。
keySet() 返回 hashMap 中所有 key 组成的集合视图。
values() 返回 hashMap 中存在的所有 value 值。
merge() 添加键值对到 hashMap 中
compute() 对 hashMap 中指定 key 的值进行重新计算
computeIfAbsent() 对 hashMap 中指定 key 的值进行重新计算,如果不存在这个 key,则添加到 hasMap 中
computeIfPresent() 对 hashMap 中指定 key 的值进行重新计算,前提是该 key 存在于 hashMap 中。
Java Iterator
Iterator介绍
Java Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法,可用于迭代 ArrayList 和 HashSet 等集合
Iterator 是 Java 迭代器最简单的实现,ListIterator 是 Collection API 中的接口, 它扩展了 Iterator 接口。
基本操作
迭代器 it 的两个基本操作是 next 、hasNext 和 remove。
调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。
调用 it.hasNext() 用于检测集合中是否还有元素。
调用 it.remove() 将迭代器返回的元素删除。
引用语法
import java.util.Iterator; // 引入 Iterator 类
获取一个迭代器
循环集合元素
让迭代器 it 逐个返回集合中所有元素最简单的方法是使用 while 循环
删除元素
要删除集合中的元素可以使用 remove() 方法
Java Object
Object类
Java Object 类是所有类的父类,也就是说 Java 的所有类都继承了 Object,子类可以使用 Object 的所有方法。
Object 类位于 java.lang 包中,编译时会自动导入,我们创建一个类时,如果没有明确继承一个父类,那么它就会自动继承 Object,成为 Object 的子类。
Object继承方式
显示继承
隐式继承
类的构造函数
Object()
构造一个新对象。
类的方法
protected Object clone()
创建并返回一个对象的拷贝
boolean equals(Object obj)
比较两个对象是否相等
protected void finalize()
当 GC (垃圾回收器)确定不存在对该对象的有更多引用时,由对象的垃圾回收器调用此方法。
Class<?> getClass()
获取对象的运行时对象的类
int hashCode()
获取对象的 hash 值
void notify()
唤醒在该对象上等待的某个线程
void notifyAll()
唤醒在该对象上等待的所有线程
String toString()
返回对象的字符串表示形式
void wait()
让当前线程进入等待状态。直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。
void wait(long timeout)
让当前线程处于等待(阻塞)状态,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过参数设置的timeout超时时间。
void wait(long timeout, int nanos)
与 wait(long timeout) 方法类似,多了一个 nanos 参数,这个参数表示额外时间(以纳秒为单位,范围是 0-999999)。 所以超时的时间还需要加上 nanos 纳秒。。
Java 泛型
泛型概念
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
假定我们有这样一个需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?答案是可以使用Java 泛型
使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序。
泛型方法
定义规则
所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的<E> )。
每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int、double、char 等)。
泛型标记符
E
- Element (在集合中使用,因为集合中存放的是元素)
T
- Type(Java 类)
K
- Key(键)
V
- Value(值)
N
- Number(数值类型) - 表示不确定的 java 类型
实例1
如何使用泛型方法打印不同类型的数组元素
实例2
泛型方法返回三个可比较对象的最大值
泛型类
泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。
和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。
实例
类型通配符
1、类型通配符一般是使用 ? 代替具体的类型参数。例如 List<?> 在逻辑上是 List<String>,List<Integer> 等所有 List<具体类型实参> 的父类。
解析: 因为 getData() 方法的参数是 List<?> 类型的,所以 name,age,number 都可以作为这个方法的实参,这就是通配符的作用。
2、类型通配符上限通过形如List来定义,如此定义就是通配符泛型值接受Number及其下层子类类型。
子主题
解析:在 //1 处会出现错误,因为 getUperNumber() 方法中的参数已经限定了参数泛型上限为 Number ,所以泛型为 String 是不在这个范围之内,所以会报错。
3、类型通配符下限通过形如List<? super Number> 来定义,表示类型只能接受 Number 及其上层父类类型,如 Object 类型的实例。
Java 序列化
序列化机制
Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。
将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化,也就是说,对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。
整个过程都是 Java 虚拟机(JVM)独立的,也就是说,在一个平台上序列化的对象可以在另一个完全不同的平台上反序列化该对象。
类 ObjectInputStream 和 ObjectOutputStream 是高层次的数据流,它们包含反序列化和序列化对象的方法。
ObjectOutputStream 类包含很多写方法来写各种数据类型,但是一个特别的方法例外: public final void writeObject(Object x) throws IOException 上面的方法序列化一个对象,并将它发送到输出流。相似的 ObjectInputStream 类包含如下反序列化一个对象的方法: public final Object readObject() throws IOException, ClassNotFoundException 该方法从流中取出下一个对象,并将对象反序列化。它的返回值为Object,因此,你需要将它转换成合适的数据类型。
序列化实例
一个类的对象要想序列化成功,必须满足两个条件:
该类必须实现 java.io.Serializable 接口。
该类的所有属性必须是可序列化的。如果有一个属性不是可序列化的,则该属性必须注明是短暂的。
如果你想知道一个 Java 标准类是否是可序列化的,请查看该类的文档。检验一个类的实例是否能序列化十分简单, 只需要查看该类有没有实现 java.io.Serializable接口。
序列化对象
ObjectOutputStream 类用来序列化一个对象,如下的 SerializeDemo 例子实例化了一个 Employee 对象,并将该对象序列化到一个文件中。
该程序执行后,就创建了一个名为 employee.ser 文件。该程序没有任何输出,但是你可以通过代码研读来理解程序的作用。
注意:
当序列化一个对象到文件时, 按照 Java 的标准约定是给文件一个 .ser 扩展名。
反序列化对象
下面的 DeserializeDemo 程序实例了反序列化,/tmp/employee.ser 存储了 Employee 对象
注意
readObject() 方法中的 try/catch代码块尝试捕获 ClassNotFoundException 异常。对于 JVM 可以反序列化对象,它必须是能够找到字节码的类。如果JVM在反序列化对象的过程中找不到该类,则抛出一个 ClassNotFoundException 异常。
readObject() 方法的返回值被转化成 Employee 引用。
当对象被序列化时,属性 SSN 的值为 111222333,但是因为该属性是短暂的,该值没有被发送到输出流。所以反序列化后 Employee 对象的 SSN 属性为 0。
Java 网络编程
网络编程
编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来
java.net 包中的网络协议
TCP(英语:Transmission Control Protocol,传输控制协议) 是一种面向连接的、可靠的、基于字节流的传输层通信协议,TCP 层是位于 IP 层之上,应用层之下的中间层。TCP 保障了两个应用程序之间的可靠通信。通常用于互联网协议,被称 TCP / IP。
UDP (英语:User Datagram Protocol,用户数据报协议),位于 OSI 模型的传输层。一个无连接的协议。提供了应用程序之间要发送数据的数据报。由于UDP缺乏可靠性且属于无连接协议,所以应用程序通常必须容许一些丢失、错误或重复的数据包。
Socket编程
Socket(套接字)
套接字使用TCP提供了两台计算机之间的通信机制。 客户端程序创建一个套接字,并尝试连接服务器的套接字。
java.net.Socket 类代表一个套接字,并且 java.net.ServerSocket 类为服务器程序提供了一种来监听客户端,并与他们建立连接的机制。
Socket建立TCP连接机制
服务器实例化一个 ServerSocket 对象,表示通过服务器上的端口通信。
服务器调用 ServerSocket 类的 accept() 方法,该方法将一直等待,直到客户端连接到服务器上给定的端口。
服务器正在等待时,一个客户端实例化一个 Socket 对象,指定服务器名称和端口号来请求连接。
Socket 类的构造函数试图将客户端连接到指定的服务器和端口号。如果通信被建立,则在客户端创建一个 Socket 对象能够与服务器进行通信。
在服务器端,accept() 方法返回服务器上一个新的 socket 引用,该 socket 连接到客户端的 socket。
当连接建立时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取来进行通信。
连接建立后,通过使用 I/O 流在进行通信,每一个socket都有一个输出流和一个输入流,客户端的输出流连接到服务器端的输入流,而客户端的输入流连接到服务器端的输出流。
TCP 是一个双向的通信协议,因此数据可以通过两个数据流在同一时间发送.以下是一些类提供的一套完整的有用的方法来实现 socket。
ServerSocket类的方法
服务器应用程序通过使用 java.net.ServerSocket 类以获取一个端口,并且侦听客户端请求
创建非绑定服务器套接字。 如果 ServerSocket 构造方法没有抛出异常,就意味着你的应用程序已经成功绑定到指定的端口,并且侦听客户端请求。
构造方法
常用方法
Socket类的方法
java.net.Socket 类代表客户端和服务器都用来互相沟通的套接字。客户端要获取一个 Socket 对象通过实例化 ,而 服务器获得一个 Socket 对象则通过 accept() 方法的返回值。
当 Socket 构造方法返回,并没有简单的实例化了一个 Socket 对象,它实际上会尝试连接到指定的服务器和端口。
构造方法
常用方法
InetAddress 类的方法
这个类表示互联网协议(IP)地址
常用方法
Socket 客户端实例
GreetingClient 是一个客户端程序,该程序通过 socket 连接到服务器并发送一个请求,然后等待一个响应
Socket 服务端实例
GreetingServer 程序是一个服务器端应用程序,使用 Socket 来监听一个指定的端口
Java URL 处理
URL介绍
URL(Uniform Resource Locator)中文名为统一资源定位符,有时也被俗称为网页地址。表示为互联网上的资源,如网页或者 FTP 地址
URL组成
protocol://host:port/path?query#fragment
protocol(协议)可以是 HTTP、HTTPS、FTP 和 File,port 为端口号,path为文件路径及文件名
URL解析
http://www.runoob.com/index.html?language=cn#j2se
协议为(protocol)
http
主机为(host:port)
www.runoob.com
端口号为(port)
80 ,以上URL实例并未指定端口,因为 HTTP 协议默认的端口号为 80。
文件路径为(path)
/index.html
请求参数(query)
language=cn
定位位置(fragment)
j2se,定位到网页中 id 属性为 j2se 的 HTML 元素位置 。
URL 类方法
URL构建方式
URL方法
实例
使用java.net的URL类获取URL的各个部分参数
URLConnections 类方法
openConnection() 返回一个 java.net.URLConnection
如果你连接HTTP协议的URL, openConnection() 方法返回 HttpURLConnection 对象。
如果你连接的URL为一个 JAR 文件, openConnection() 方法将返回 JarURLConnection 对象。
URLConnection 方法
1 Object getContent()
检索URL链接内容
2 Object getContent(Class[] classes)
检索URL链接内容
3 String getContentEncoding()
返回头部 content-encoding 字段值。
4 int getContentLength()
返回头部 content-length字段值
5 String getContentType()
返回头部 content-type 字段值
6 int getLastModified()
返回头部 last-modified 字段值。
7 long getExpiration()
返回头部 expires 字段值。
8 long getIfModifiedSince()
返回对象的 ifModifiedSince 字段值。
9 public void setDoInput(boolean input)
URL 连接可用于输入和/或输出。如果打算使用 URL 连接进行输入,则将 DoInput 标志设置为 true;如果不打算使用,则设置为 false。默认值为 true。
10 public void setDoOutput(boolean output)
URL 连接可用于输入和/或输出。如果打算使用 URL 连接进行输出,则将 DoOutput 标志设置为 true;如果不打算使用,则设置为 false。默认值为 false。
11 public InputStream getInputStream() throws IOException
返回URL的输入流,用于读取资源
12 public OutputStream getOutputStream() throws IOException
返回URL的输出流, 用于写入资源。
13 public URL getURL()
返回 URLConnection 对象连接的URL
实例
URL采用了HTTP 协议。 openConnection 返回HttpURLConnection对象
Java 发送邮件
下载Javamail
https://www.oracle.com/java/technologies/javamail.html
下载并解压缩这些文件,在新创建的顶层目录中,您会发现这两个应用程序的一些 jar 文件。您需要把 mail.jar 和 activation.jar 文件添加到您的 CLASSPATH 中。
发送一封简单的 E-mail
源码
// 文件名 SendEmail.java import java.util.*; import javax.mail.*; import javax.mail.internet.*; import javax.activation.*; public class SendEmail { public static void main(String [] args) { // 收件人电子邮箱 String to = "abcd@gmail.com"; // 发件人电子邮箱 String from = "web@gmail.com"; // 指定发送邮件的主机为 localhost String host = "localhost"; // 获取系统属性 Properties properties = System.getProperties(); // 设置邮件服务器 properties.setProperty("mail.smtp.host", host); // 获取默认session对象 Session session = Session.getDefaultInstance(properties); try{ // 创建默认的 MimeMessage 对象 MimeMessage message = new MimeMessage(session); // Set From: 头部头字段 message.setFrom(new InternetAddress(from)); // Set To: 头部头字段 message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); // Set Subject: 头部头字段 message.setSubject("This is the Subject Line!"); // 设置消息体 message.setText("This is actual message"); // 发送消息 Transport.send(message); System.out.println("Sent message successfully...."); }catch (MessagingException mex) { mex.printStackTrace(); } } } 编译并运行这个程序来发送一封简单的E-mail: $ java SendEmail Sent message successfully.... 如果你想发送一封e-mail给多个收件人,那么使用下面的方法来指定多个收件人ID: void addRecipients(Message.RecipientType type, Address[] addresses) throws MessagingException 下面是对于参数的描述: type:要被设置为 TO, CC 或者 BCC,这里 CC 代表抄送、BCC 代表秘密抄送。举例:Message.RecipientType.TO addresses: 这是 email ID 的数组。在指定电子邮件 ID 时,你将需要使用 InternetAddress() 方法。
发送一封 HTML E-mail
源码
// 文件名 SendHTMLEmail.java import java.util.*; import javax.mail.*; import javax.mail.internet.*; import javax.activation.*; public class SendHTMLEmail { public static void main(String [] args) { // 收件人电子邮箱 String to = "abcd@gmail.com"; // 发件人电子邮箱 String from = "web@gmail.com"; // 指定发送邮件的主机为 localhost String host = "localhost"; // 获取系统属性 Properties properties = System.getProperties(); // 设置邮件服务器 properties.setProperty("mail.smtp.host", host); // 获取默认的 Session 对象。 Session session = Session.getDefaultInstance(properties); try{ // 创建默认的 MimeMessage 对象。 MimeMessage message = new MimeMessage(session); // Set From: 头部头字段 message.setFrom(new InternetAddress(from)); // Set To: 头部头字段 message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); // Set Subject: 头字段 message.setSubject("This is the Subject Line!"); // 发送 HTML 消息, 可以插入html标签 message.setContent("<h1>This is actual message</h1>", "text/html" ); // 发送消息 Transport.send(message); System.out.println("Sent message successfully...."); }catch (MessagingException mex) { mex.printStackTrace(); } } } 编译并运行此程序来发送HTML e-mail: $ java SendHTMLEmail Sent message successfully....
发送带有附件的 E-mail
源码
// 文件名 SendFileEmail.java import java.util.*; import javax.mail.*; import javax.mail.internet.*; import javax.activation.*; public class SendFileEmail { public static void main(String [] args) { // 收件人电子邮箱 String to = "abcd@gmail.com"; // 发件人电子邮箱 String from = "web@gmail.com"; // 指定发送邮件的主机为 localhost String host = "localhost"; // 获取系统属性 Properties properties = System.getProperties(); // 设置邮件服务器 properties.setProperty("mail.smtp.host", host); // 获取默认的 Session 对象。 Session session = Session.getDefaultInstance(properties); try{ // 创建默认的 MimeMessage 对象。 MimeMessage message = new MimeMessage(session); // Set From: 头部头字段 message.setFrom(new InternetAddress(from)); // Set To: 头部头字段 message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); // Set Subject: 头字段 message.setSubject("This is the Subject Line!"); // 创建消息部分 BodyPart messageBodyPart = new MimeBodyPart(); // 消息 messageBodyPart.setText("This is message body"); // 创建多重消息 Multipart multipart = new MimeMultipart(); // 设置文本消息部分 multipart.addBodyPart(messageBodyPart); // 附件部分 messageBodyPart = new MimeBodyPart(); String filename = "file.txt"; DataSource source = new FileDataSource(filename); messageBodyPart.setDataHandler(new DataHandler(source)); messageBodyPart.setFileName(filename); multipart.addBodyPart(messageBodyPart); // 发送完整消息 message.setContent(multipart ); // 发送消息 Transport.send(message); System.out.println("Sent message successfully...."); }catch (MessagingException mex) { mex.printStackTrace(); } } } 编译并运行你的程序来发送一封带有附件的邮件。 $ java SendFileEmail Sent message successfully....
用户认证部分
以QQ邮件为例
如果需要提供用户名和密码给e-mail服务器来达到用户认证的目的,你可以通过如下设置来完成: props.put("mail.smtp.auth", "true"); props.setProperty("mail.user", "myuser"); props.setProperty("mail.password", "mypwd"); e-mail其他的发送机制和上述保持一致。 需要用户名密码验证邮件发送实例: 本实例以 QQ 邮件服务器为例,你需要在登录QQ邮箱后台在"设置"=》账号中开启POP3/SMTP服务 ,如下图所示:  QQ 邮箱通过生成授权码来设置密码:  Java 代码如下: // 需要用户名密码邮件发送实例 //文件名 SendEmail2.java //本实例以QQ邮箱为例,你需要在qq后台设置 import java.util.Properties; import javax.mail.Authenticator; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.PasswordAuthentication; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; public class SendEmail2 { public static void main(String [] args) { // 收件人电子邮箱 String to = "xxx@qq.com"; // 发件人电子邮箱 String from = "xxx@qq.com"; // 指定发送邮件的主机为 smtp.qq.com String host = "smtp.qq.com"; //QQ 邮件服务器 // 获取系统属性 Properties properties = System.getProperties(); // 设置邮件服务器 properties.setProperty("mail.smtp.host", host); properties.put("mail.smtp.auth", "true"); // 获取默认session对象 Session session = Session.getDefaultInstance(properties,new Authenticator(){ public PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("xxx@qq.com", "qq邮箱授权码"); //发件人邮件用户名、授权码 } }); try{ // 创建默认的 MimeMessage 对象 MimeMessage message = new MimeMessage(session); // Set From: 头部头字段 message.setFrom(new InternetAddress(from)); // Set To: 头部头字段 message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); // Set Subject: 头部头字段 message.setSubject("This is the Subject Line!"); // 设置消息体 message.setText("This is actual message"); // 发送消息 Transport.send(message); System.out.println("Sent message successfully....from runoob.com"); }catch (MessagingException mex) { mex.printStackTrace(); } } }
如果需设置SSL加密
// 关于QQ邮箱,还要设置SSL加密,加上以下代码即可 MailSSLSocketFactory sf = new MailSSLSocketFactory(); sf.setTrustAllHosts(true); props.put("mail.smtp.ssl.enable", "true"); props.put("mail.smtp.ssl.socketFactory", sf); 参考消息: import java.security.GeneralSecurityException; import java.util.Properties; import javax.mail.Authenticator; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.PasswordAuthentication; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import com.sun.mail.util.MailSSLSocketFactory; public class SendEmail { public static void main(String [] args) throws GeneralSecurityException { // 收件人电子邮箱 String to = "XXXXX@qq.com"; // 发件人电子邮箱 String from = "XXXXXX@qq.com"; // 指定发送邮件的主机为 smtp.qq.com String host = "smtp.qq.com"; //QQ 邮件服务器 // 获取系统属性 Properties properties = System.getProperties(); // 设置邮件服务器 properties.setProperty("mail.smtp.host", host); properties.put("mail.smtp.auth", "true"); MailSSLSocketFactory sf = new MailSSLSocketFactory(); sf.setTrustAllHosts(true); properties.put("mail.smtp.ssl.enable", "true"); properties.put("mail.smtp.ssl.socketFactory", sf); // 获取默认session对象 Session session = Session.getDefaultInstance(properties,new Authenticator(){ public PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("429240967@qq.com", "授权的 QQ 邮箱密码"); //发件人邮件用户名、密码 } }); try{ // 创建默认的 MimeMessage 对象 MimeMessage message = new MimeMessage(session); // Set From: 头部头字段 message.setFrom(new InternetAddress(from)); // Set To: 头部头字段 message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); // Set Subject: 头部头字段 message.setSubject("This is the Subject Line!"); // 设置消息体 message.setText("This is actual message"); // 发送消息 Transport.send(message); System.out.println("Sent message successfully....from runoob.com"); }catch (MessagingException mex) { mex.printStackTrace(); } } }
Java 多线程编程
线程和进程
线程
一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
一个线程不能独立的存在,它必须是进程的一部分
进程
一个进程包括由操作系统分配的内存空间,包含一个或多个线程。
一个进程一直运行,直到所有的非守护线程都结束运行后才能结束
多线程优点
多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。
多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。
一个线程的生命周期
新建状态:
使用new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行run() ,此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
线程的优先级
每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。
Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。
默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。
具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。
线程的几个主要概念
线程同步
线程间通信
线程死锁
线程控制:挂起、停止和恢复
多线程的使用
有效利用多线程的关键是理解程序是并发执行而不是串行执行的。例如:程序中有两个子系统需要并发执行,这时候就需要利用多线程编程。
通过对多线程的使用,可以编写出非常高效的程序。不过请注意,如果你创建太多的线程,程序执行的效率实际上是降低了,而不是提升了。
请记住,上下文的切换开销也很重要,如果你创建了太多的线程,CPU 花费在上下文的切换的时间将多于执行程序的时间!
创建一个线程
三种方法
通过实现 Runnable 接口;
通过继承 Thread 类本身;
通过 Callable 和 Future 创建线程。
通过实现Runnable接口来创建线程
实现Runnable需调用方法run()
创建一个实现 Runnable 接口的类之后,你可以在类中实例化一个线程对象
threadOb 是一个实现 Runnable 接口的类的实例,并且 threadName 指定新线程的名字
新线程创建之后,你调用它的 start() 方法它才会运行
创建线程并开始让它执行
通过继承Thread来创建线程
创建一个线程的第二种方法是创建一个新的类,该类继承 Thread 类,然后创建一个该类的实例。
继承类必须重写 run() 方法,该方法是新线程的入口点。它也必须调用 start() 方法才能执行。
该方法尽管被列为一种多线程实现方式,但是本质上也是实现了 Runnable 接口的一个实例。
实例
通过Callable和Future创建线程
1. 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。
2. 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
3. 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
4. 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。
实例
创建线程的三种方式的对比
1. 采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。
2. 使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。
Thread方法
重要方法
1 public void start()
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
2 public void run()
如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
3 public final void setName(String name)
改变线程名称,使之与参数 name 相同。
4 public final void setPriority(int priority)
更改线程的优先级。
5 public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。
6 public final void join(long millisec)
等待该线程终止的时间最长为 millis 毫秒。
7 public void interrupt()
中断线程。
8 public final boolean isAlive()
测试线程是否处于活动状态。
静态方法
1 public static void yield()
暂停当前正在执行的线程对象,并执行其他线程。
2 public static void sleep(long millisec)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
3 public static boolean holdsLock(Object x)
当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。
4 public static Thread currentThread()
返回对当前正在执行的线程对象的引用。
5 public static void dumpStack()
将当前线程的堆栈跟踪打印至标准错误流。
实例
Java Applet 基础
何为Applet
Applet 是一种 Java 程序。它一般运行在支持 Java 的 Web 浏览器内。因为它有完整的 Java API支持,所以Applet 是一个全功能的 Java 应用程序。
与独立的Java的不同
Java 中 Applet 类继承了 java.applet.Applet 类。
Applet 类没有定义 main(),所以一个 Applet 程序不会调用 main() 方法。
Applet 被设计为嵌入在一个 HTML 页面。
当用户浏览包含 Applet 的 HTML 页面,Applet 的代码就被下载到用户的机器上。
要查看一个 Applet 需要 JVM。 JVM 可以是 Web 浏览器的一个插件,或一个独立的运行时环境。
用户机器上的 JVM 创建一个 Applet 类的实例,并调用 Applet 生命周期过程中的各种方法。
Applet 有 Web 浏览器强制执行的严格的安全规则,Applet 的安全机制被称为沙箱安全。
Applet 需要的其他类可以用 Java 归档(JAR)文件的形式下载下来。
Applet的生命周期
init:
该方法的目的是为你的 Applet 提供所需的任何初始化。在 Applet 标记内的 param 标签被处理后调用该方法。
start:
浏览器调用 init 方法后,该方法被自动调用。每当用户从其他页面返回到包含 Applet 的页面时,则调用该方法。
stop:
当用户从包含 Applet 的页面移除的时候,该方法自动被调用。因此,可以在相同的 Applet 中反复调用该方法。
destroy:
此方法仅当浏览器正常关闭时调用。因为 Applet 只有在 HTML 网页上有效,所以你不应该在用户离开包含 Applet 的页面后遗漏任何资源。
paint:
该方法在 start() 方法之后立即被调用,或者在 Applet 需要重绘在浏览器的时候调用。paint() 方法实际上继承于 java.awt。
"Hello, World" Applet:
没有这些 import 语句,Java 编译器就识别不了 Applet 和 Graphics 类
"Hello,World"applet 都是按标准编写的。唯一被重写的方法是 paint 方法。
Applet 类
衍生类调用的方法
得到 Applet 的参数
得到包含 Applet 的 HTML 文件的网络位置
得到 Applet 类目录的网络位置
打印浏览器的状态信息
获取一张图片
获取一个音频片段
播放一个音频片段
调整此 Applet 的大小
接口
请求 Applet 作者、版本和版权的信息
请求 Applet 识别的参数的描述
初始化 Applet
销毁 Applet
开始执行 Applet
结束执行 Applet
Applet 的调用
<applet> 标签是在HTML文件中嵌入 Applet 的基础
调用"Hello World"applet的例子
注意
你可以参照 HTML Applet 标签来更多的了解从 HTML 中调用 applet 的方法。
<applet> 标签的属性指定了要运行的 Applet 类。width 和 height 用来指定 Applet 运行面板的初始大小。Applet 必须使用 </applet> 标签来关闭。
如果 Applet 接受参数,那么参数的值需要在 <param> 标签里添加,该标签位于 <applet> 和 </applet> 之间。浏览器忽略了 applet 标签之间的文本和其他标签。
不支持 Java 的浏览器不能执行 <applet> 和 </applet>。因此,在标签之间显示并且和 applet 没有关系的任何东西,在不支持的 Java 的浏览器里是可见的。
Viewer 或者浏览器在文档的位置寻找编译过的 Java 代码,要指定文档的路径,得使用 <applet> 标签的 codebase 属性指定。

获得applet参数
下面的例子演示了如何使用一个 Applet 响应来设置文件中指定的参数。该 Applet 显示了一个黑色棋盘图案和第二种颜色。
第二种颜色和每一列的大小通过文档中的 Applet 的参数指定。
CheckerApplet 在 init() 方法里得到它的参数。也可以在 paint() 方法里得到它的参数。然而,在 Applet 开始得到值并保存了设置,而不是每一次刷新的时候都得到值,这样是很方便,并且高效的。
Applet viewer 或者浏览器在 Applet 每次运行的时候调用 init() 方法。在加载 Applet 之后,Viewer 立即调用 init() 方法(Applet.init()什么也没做),重写该方法的默认实现,添加一些自定义的初始化代码。
Applet.getParameter() 方法通过给出参数名称得到参数值。如果得到的值是数字或者其他非字符数据,那么必须解析为字符串类型。
下例是 CheckerApplet.java 的修改:
下面是 CheckerApplet 类的 init() 方法和私有的 parseSquareSize() 方法:
该 Applet 调用 parseSquareSize(),来解析 squareSize 参数。parseSquareSize() 调用了库方法 Integer. parseInt() 该方法将一个字符串解析为一个整数,当参数无效的时候,Integer.parseInt() 抛出异常。
因此,parseSquareSize() 方法也是捕获异常的,并不允许 Applet 接受无效的输入。
Applet 调用 parseColor()方法将颜色参数解析为一个 Color 值。parseColor() 方法做了一系列字符串的比较,来匹配参数的值和预定义颜色的名字。你需要实现这些方法来使 Applet 工作。
指定 applet 参数
如下的例子是一个HTML文件,其中嵌入了 CheckerApplet 类。HTML文件通过使用 <param> 标签的方法给 applet 指定了两个参数。
注意: 参数名字大小写不敏感。
应用程序转换成 Applet
将图形化的 Java 应用程序(是指,使用AWT的应用程序和使用 java 程序启动器启动的程序)转换成嵌入在web页面里的applet是很简单的。
应用程序转换成 Applet
编写一个 HTML 页面,该页面带有能加载 applet 代码的标签。
编写一个 JApplet 类的子类,将该类设置为 public。否则,Applet 不能被加载。
消除应用程序的 main()方法。不要为应用程序构造框架窗口,因为你的应用程序要显示在浏览器中。
将应用程序中框架窗口的构造方法里的初始化代码移到 Applet 的 init() 方法中,你不必显示的构造 Applet 对象,浏览器将通过调用 init() 方法来实例化一个对象。
移除对 setSize() 方法的调用,对于 Applet 来讲,大小已经通过 HTML 文件里的 width 和 height 参数设定好了。
移除对 setDefaultCloseOperation() 方法的调用。Applet 不能被关闭,它随着浏览器的退出而终止。
如果应用程序调用了 setTitle() 方法,消除对该方法的调用。applet 不能有标题栏。(当然你可以给通过 html 的 title 标签给网页自身命名)
不要调用 setVisible(true),Applet 是自动显示的。
事件处理
Applet 类从 Container 类继承了许多事件处理方法。Container 类定义了几个方法,例如:processKeyEvent() 和processMouseEvent(),用来处理特别类型的事件,还有一个捕获所有事件的方法叫做 processEvent。
为了响应一个事件,Applet 必须重写合适的事件处理方法。
import java.awt.event.MouseListener; import java.awt.event.MouseEvent; import java.applet.Applet; import java.awt.Graphics; public class ExampleEventHandling extends Applet implements MouseListener { StringBuffer strBuffer; public void init() { addMouseListener(this); strBuffer = new StringBuffer(); addItem("initializing the applet "); } public void start() { addItem("starting the applet "); } public void stop() { addItem("stopping the applet "); } public void destroy() { addItem("unloading the applet"); } void addItem(String word) { System.out.println(word); strBuffer.append(word); repaint(); } public void paint(Graphics g) { //Draw a Rectangle around the applet's display area. g.drawRect(0, 0, getWidth() - 1, getHeight() - 1); //display the string inside the rectangle. g.drawString(strBuffer.toString(), 10, 20); } public void mouseEntered(MouseEvent event) { } public void mouseExited(MouseEvent event) { } public void mousePressed(MouseEvent event) { } public void mouseReleased(MouseEvent event) { } public void mouseClicked(MouseEvent event) { addItem("mouse clicked! "); } } 如下调用该 Applet: <html> <title>Event Handling</title> <hr> <applet code="ExampleEventHandling.class" width="300" height="300"> </applet> <hr> </html> 最开始运行,Applet 显示 "initializing the applet. Starting the applet.",然后你一点击矩形框,就会显示 "mouse clicked" 。
显示图片
Applet 能显示 GIF,JPEG,BMP 等其他格式的图片。为了在 Applet 中显示图片,你需要使用 java.awt.Graphics 类的drawImage()方法
import java.applet.*; import java.awt.*; import java.net.*; public class ImageDemo extends Applet { private Image image; private AppletContext context; public void init() { context = this.getAppletContext(); String imageURL = this.getParameter("image"); if(imageURL == null) { imageURL = "java.jpg"; } try { URL url = new URL(this.getDocumentBase(), imageURL); image = context.getImage(url); }catch(MalformedURLException e) { e.printStackTrace(); // Display in browser status bar context.showStatus("Could not load image!"); } } public void paint(Graphics g) { context.showStatus("Displaying image"); g.drawImage(image, 0, 0, 200, 84, null); g.drawString("www.javalicense.com", 35, 100); } } 如下调用该applet: <html> <title>The ImageDemo applet</title> <hr> <applet code="ImageDemo.class" width="300" height="200"> <param name="image" value="java.jpg"> </applet> <hr> </html>
播放音频
Applet 能通过使用 java.applet 包中的 AudioClip 接口播放音频。AudioClip 接口定义了三个方法:
public void play():
从一开始播放音频片段一次。
public void loop():
循环播放音频片段
public void stop():
停止播放音频片段
为了得到 AudioClip 对象,你必须调用 Applet 类的 getAudioClip() 方法。无论 URL 指向的是否是一个真实的音频文件,该方法都会立即返回结果。
直到要播放音频文件时,该文件才会下载下来。
import java.applet.*; import java.awt.*; import java.net.*; public class AudioDemo extends Applet { private AudioClip clip; private AppletContext context; public void init() { context = this.getAppletContext(); String audioURL = this.getParameter("audio"); if(audioURL == null) { audioURL = "default.au"; } try { URL url = new URL(this.getDocumentBase(), audioURL); clip = context.getAudioClip(url); }catch(MalformedURLException e) { e.printStackTrace(); context.showStatus("Could not load audio file!"); } } public void start() { if(clip != null) { clip.loop(); } } public void stop() { if(clip != null) { clip.stop(); } } } 如下调用applet: <html> <title>The ImageDemo applet</title> <hr> <applet code="ImageDemo.class" width="0" height="0"> <param name="audio" value="test.wav"> </applet> <hr> 你可以使用你电脑上的 test.wav 来测试上面的实例。
Java 文档注释
三种注释方式
前两种分别是 // 和 /* */
第三种被称作说明注释,它以 /** 开始,以 */结束,说明注释允许你在程序中嵌入关于程序的信息
javadoc 标签
@author 标识一个类的作者 @author description
@deprecated 指名一个过期的类或成员 @deprecated description
{@docRoot} 指明当前文档根目录的路径 Directory Path
@exception 标志一个类抛出的异常 @exception exception-name explanation
{@inheritDoc} 从直接父类继承的注释 Inherits a comment from the immediate surperclass.
{@link} 插入一个到另一个主题的链接 {@link name text}
{@linkplain} 插入一个到另一个主题的链接,但是该链接显示纯文本字体 Inserts an in-line link to another topic.
@param 说明一个方法的参数 @param parameter-name explanation
@return 说明返回值类型 @return explanation
@see 指定一个到另一个主题的链接 @see anchor
@serial 说明一个序列化属性 @serial description
@serialData 说明通过writeObject( ) 和 writeExternal( )方法写的数据 @serialData description
@serialField 说明一个ObjectStreamField组件 @serialField name type description
@since 标记当引入一个特定的变化时 @since release
@throws 和 @exception标签一样. The @throws tag has the same meaning as the @exception tag.
{@value} 显示常量的值,该常量必须是static属性。 Displays the value of a constant, which must be a static field.
@version 指定类的版本 @version info
文档注释
实例
在开始的/** 之后,第一行或几行是关于类、变量和方法的主要描述。
之后,你可以包含一个或多个各种各样的@ 标签。每一个 @ 标签必须在一个新行的开始或者在一行的开始紧跟星号(*).
多个相同类型的标签应该放成一组。例如,如果你有三个@see 标签,可以将它们一个接一个的放在一起。
javadoc 输出什么
javadoc 工具将你 Java 程序的源代码作为输入,输出一些包含你程序注释的HTML文件。
每一个类的信息将在独自的HTML文件里。javadoc 也可以输出继承的树形结构和索引。
由于 javadoc 的实现不同,工作也可能不同,你需要检查你的 Java 开发系统的版本等细节,选择合适的 Javadoc 版本。
实例
在经过 javadoc 处理之后,SquareNum 类的注释将在 SquareNum.html 中找到
import java.io.*; /** * 这个类演示了文档注释 * @author Ayan Amhed * @version 1.2 */ public class SquareNum { /** * This method returns the square of num. * This is a multiline description. You can use * as many lines as you like. * @param num The value to be squared. * @return num squared. */ public double square(double num) { return num * num; } /** * This method inputs a number from the user. * @return The value input as a double. * @exception IOException On input error. * @see IOException */ public double getNumber() throws IOException { InputStreamReader isr = new InputStreamReader(System.in); BufferedReader inData = new BufferedReader(isr); String str; str = inData.readLine(); return (new Double(str)).doubleValue(); } /** * This method demonstrates square(). * @param args Unused. * @return Nothing. * @exception IOException On input error. * @see IOException */ public static void main(String args[]) throws IOException { SquareNum ob = new SquareNum(); double val; System.out.println("Enter value to be squared: "); val = ob.getNumber(); val = ob.square(val); System.out.println("Squared value is " + val); } } 如下,使用 javadoc 工具处理 SquareNum.java 文件: $ javadoc SquareNum.java Loading source file SquareNum.java... Constructing Javadoc information... Standard Doclet version 1.5.0_13 Building tree for all the packages and classes... Generating SquareNum.html... SquareNum.java:39: warning - @return tag cannot be used\ in method with void return type. Generating package-frame.html... Generating package-summary.html... Generating package-tree.html... Generating constant-values.html... Building index for all the packages and classes... Generating overview-tree.html... Generating index-all.html... Generating deprecated-list.html... Building index for all classes... Generating allclasses-frame.html... Generating allclasses-noframe.html... Generating index.html... Generating help-doc.html... Generating stylesheet.css... 1 warning $