Java 简明教程
Java - Overview
Java 编程语言最初由 Sun Microsystems 开发,由 James Gosling 发起,并于 1995 年作为 Sun Microsystems 的 Java 平台(Java 1.0 [J2SE])的核心组件发布。
Java Standard Edition 最新版本是 Java SE 8。随着 Java 的进步及其广泛普及,构建了多种配置以适应各种类型的平台。例如:用于企业应用程序的 J2EE,用于移动应用程序的 J2ME。
新 J2 版本分别更名为 Java SE、Java EE 和 Java ME。Java 有保证 Write Once, Run Anywhere.
Java 是 −
-
Object Oriented − 在 Java 中,一切都算一个对象。可以轻松扩展 Java,因为它基于对象模型。
-
Platform Independent − 与包括 C 和 C++ 在内的许多其他编程语言不同,Java 在编译时并不编译成特定于平台的机器,而是编译成平台独立的字节码。此字节码在网络上传播,由它正在运行的任何平台上的虚拟机 (JVM) 解释。
-
Simple − Java 的设计初衷是让它易于学习。如果你了解了 OOP Java 的基本概念,那么掌握它会很容易。
-
Secure − Java 的安全功能使其能够开发无病毒、无篡改的系统。认证技术基于公钥加密。
-
Architecture-neutral − Java 编译器生成架构中立的对象文件格式,在存在 Java 运行时系统的情况下,这使得编译代码可以在许多处理器上执行。
-
Portable − 由于是架构中立的,并且没有规范中依赖实现的方面,Java 是可移植的。Java 中的编译器是用 ANSI C 编写的,具有明确的可移植性边界,它是 POSIX 的一个子集。
-
Robust − Java 努力通过主要强调编译时错误检查和运行时检查来消除可能出错的情况。
-
Multithreaded − 利用 Java 的多线程功能,可以编写能够同时执行许多任务的程序。这个设计功能允许开发人员构建能够流畅运行的交互式应用程序。
-
Interpreted − Java 字节码被即时翻译成本机机器指令,且不会存储在任何地方。由于链接是一个增量轻量级过程,开发过程更加快速且分析。
-
High Performance − Java 通过使用即时编译器实现高性能。
-
Distributed − Java 是为互联网的分布式环境而设计的。
-
Dynamic − Java 被认为比 C 或 C++ 更具动态性,因为它旨在适应不断变化的环境。Java 程序可以携带大量运行时信息,这些信息可以用于在运行时验证和解析对对象的访问。
Hello World using Java Programming.
为了让你对 Java 编程产生一些兴趣,我将为你提供一个小型传统的 C 编程 Hello World 程序,你可以使用演示链接尝试一下。
public class MyFirstJavaProgram {
/* This is my first java program.
* This will print 'Hello World' as the output
*/
public static void main(String []args) {
System.out.println("Hello World"); // prints Hello World
}
}
History of Java
詹姆斯·高斯林在 1991 年 6 月发起了 Java 语言项目,用于在其众多的机顶盒项目之一中使用。这门语言最初被称为“Oak”,以高斯林办公室外的一棵橡树命名,也曾被称为“Green”,后来从一组随机单词中更名为 Java。
Sun 于 1995 年发布了第一个公共实施版本 Java 1.0。它承诺*Write Once, Run Anywhere*(WORA),提供了在流行平台上免费运行时。
2006 年 11 月 13 日,Sun 根据 GNU 通用公共许可证 (GPL) 的条款发布了大部分 Java 作为自由和开放源代码软件。
2007 年 5 月 8 日,Sun 完成了该过程,使 Java 的所有核心代码成为自由和开源,除了 Sun 没有持有其版权的一小部分代码。
Java - Environment Setup
Live Demo Option Online
我们在线设置了 Java 编程环境,以便你可以在线编译和执行所有可用的示例。它让你对你所读的内容充满信心,并使你能够使用不同的选项验证程序。随意修改任何示例并在线执行。
尝试使用如下示例代码框右上角提供的 Live Demo 选项执行以下示例 −
public class MyFirstJavaProgram {
public static void main(String []args) {
System.out.println("Hello World");
}
}
对于本教程中给出的大多数示例,你将发现我们网站代码部分右上角有一个 Try it 选项,它将带你到在线编译器。所以,善加利用并享受你的学习吧。
Local Environment Setup
如果你想为 Java 编程语言设置自己的环境,本节将指导你完成整个过程。请按照以下步骤来设置 Java 环境。
Java SE 可免费下载。若要下载 click here,请下载与你的操作系统兼容的版本。
按照说明下载 Java,并运行 .exe 在你的计算机上安装 Java。在计算机上安装 Java 后,你需要设置环境变量来指向正确的安装目录。
Setting Up the Path for Windows 2000/XP
假设你已将 Java 安装在 c:\Program Files\java\jdk 目录中 −
-
右键单击“我的电脑”,然后选择“属性”。
-
单击“高级”选项卡下的“环境变量”按钮。
-
现在,编辑“Path”变量,并在其末尾添加 Java 可执行目录的路径。例如,如果路径当前设置为 C:\Windows\System32 ,则按如下方式进行编辑 C:\Windows\System32;c:\Program Files\java\jdk\bin 。
Popular Java Editors
要编写 Java 程序,你需要一个文本编辑器。市场上还有功能更强大的 IDE。最受欢迎的简要介绍如下 −
-
Notepad − 在 Windows 计算机上,你可以使用任何简单的文本编辑器,如记事本(建议用于本教程)或 WordPad。Notepad++ 也是一个具有增强功能的免费文本编辑器。
-
Netbeans − 它是一个开源免费的 Java IDE,可以从 www.netbeans.org/index.html 下载。
-
Eclipse − 它也是由 Eclipse 开源社区开发的 Java IDE,可以从 www.eclipse.org 下载。
IDE 或集成开发环境提供编程所需的所有常见工具和设施,如源代码编辑器、构建工具和调试器等。
Java - Basic Syntax
当我们考虑 Java 程序时,可以将它定义为通过调用彼此的方法而相互通信的对象集合。现在,让我们简单了解一下类、对象、方法和实例变量的含义。
-
Object − 对象具有状态和行为。示例:狗的状态包括颜色、名称、品种,还有一些行为,如摇尾巴、吠叫、吃饭。一个对象是一个类的实例。
-
Class − 一个类可以定义为模板或蓝图,描述了其类型支持的对象的行为或状态。
-
Methods − 方法基本上是一种行为。一个类可以包含多个方法。逻辑编写在方法中,数据在方法中被操作,所有操作都在方法中执行。
-
Instance Variables − 每个对象都有自己唯一的一组实例变量。对象的状态由分配给这些实例变量的值创建。
First Java Program
让我们看一个简单的代码,它将打印单词 Hello World。
Example
public class MyFirstJavaProgram {
/* This is my first java program.
* This will print 'Hello World' as the output
*/
public static void main(String []args) {
System.out.println("Hello World"); // prints Hello World
}
}
让我们看看怎样来保存文件、编译和运行程序。请遵循以下步骤:
-
打开记事本并添加如上代码。
-
将文件另存为:MyFirstJavaProgram.java。
-
打开一个命令提示符窗口并转到保存该类的位置的目录。假设该位置是 C:\。
-
键入“javac MyFirstJavaProgram.java”并按 Enter 键编译代码。如果代码中没有错误,命令提示符将带你到下一行(假设:已设置路径变量)。
-
现在,输入“java MyFirstJavaProgram”来运行您的程序。
-
您将能够在窗口上看到打印出来的“Hello World”。
Basic Syntax
关于 Java 程序,牢记以下几点非常重要。
-
Case Sensitivity − Java 区分大小写,这意味着标识符 Hello 和 hello 在 Java 中具有不同的含义。
-
Class Names − 对于所有类名,第一个字母应该大写。如果用几个单词来形成一个类名,每个内部单词的第一个字母都应该大写。 Example: 类 MyFirstJavaClass
-
Method Names − 所有方法名称应以小写字母开头。如果使用多个单词来构成方法名称,则每个内部单词的第一个字母应大写。 Example: public void myMethodName()
-
Program File Name − 程序文件的名称应与类名完全匹配。保存文件时,您应使用类名(请记住 Java 区分大小写)并追加“ .java”到名称末尾(如果文件名与类名不匹配,您的程序将无法编译)。但请注意,如果您在文件中没有公共类,则文件名可以与类名不同。文件中有公共类也不是必需的。 Example: 假设“MyFirstJavaProgram”是类名。然后应将文件保存为“MyFirstJavaProgram.java”
-
public static void main(String args[]) − Java 程序处理从 main() 方法开始,它是每个 Java 程序的强制性部分。
Java Identifiers
所有 Java 组件都需要名称。用于类、变量和方法的名称称为 identifiers。
在 Java 中,关于标识符,有几个要点需要记住。它们如下所示:
-
所有标识符都应该以字母 (A 到 Z 或 a 到 z)、货币字符 ($) 或下划线 (_) 开头。
-
在第一个字符之后,标识符可以包含任何字符组合。
-
关键字不能用作标识符。
-
最重要的是,标识符区分大小写。
-
合法标识符示例:age、$salary、_value、__1_value。
-
非法标识符的示例:123abc、 -salary。
Java Modifiers
与其他语言一样,可以使用修饰符来修改类、方法等。有两类修饰符 -
-
Access Modifiers - 默认、public、protected、private
-
Non-access Modifiers - final、abstract、strictfp
我们将在下一部分详细了解修饰符。
Java Variables
以下是 Java 中的变量类型 -
-
Local Variables
-
Class Variables (Static Variables)
-
Instance Variables (Non-static Variables)
Java Enums
枚举是在 Java 5.0 中引入的。枚举将变量限制为仅具有少数预定义值之一。此枚举列表中的值称为枚举。
使用枚举可以减少代码中的错误数量。
例如,如果我们考虑一个鲜榨果汁店的应用程序,那么可以将玻璃杯的大小限制为小号、中号和大号。这将确保其他人无法订购除小号、中号或大号之外的任何尺寸。
Java Keywords
以下列表显示了 Java 中的保留字。这些保留字不能用作常量、变量或任何其他标识符名称。
Sr.No |
Reserved Words & Description |
1 |
abstract 根据词典, abstraction 是对付思想而非事件的品质。 |
2 |
assert assert 关键字在 Java 中用于定义断言。断言是 Java 中的一个语句,它确保程序中已经做出的任何假设的正确性。 |
3 |
boolean 布尔数据类型是 Java 支持的八个基本数据类型之一。它提供了创建布尔型变量的方法,该变量可以接受真或假等布尔值。 |
4 |
break Java 编程语言中的 break 语句具有以下两种用法: break 语句在循环内遇到时,循环将立即终止,程序控制将继续执行循环后的下一条语句。它可以用作在 switch 语句中终止一个情况。 |
5 |
byte 字节数据类型是 Java 支持的八个基本数据类型之一。它提供了创建字节类型变量的方法,该变量可以接受字节值。 |
6 |
case case 关键字是 switch 语句的一部分,该语句允许变量针对值列表进行相等性测试。 |
7 |
catch 异常(或异常事件)是在程序执行期间出现的、需要处理的问题。 |
8 |
char 字符数据类型是 Java 支持的八个基本数据类型之一。 |
9 |
class Java 是一种面向对象语言。作为一门具有面向对象特性的语言。 |
10 |
const final 关键字用于在 Java 中定义常量值或 final 方法/类。 |
11 |
continue continue 关键字可用于任何循环控制结构中。 |
12 |
default default 关键字是 switch 语句的一部分,该语句允许测试变量是否与值列表相等。 |
13 |
do do…​while 循环类似于 while 循环,但保证至少执行一次 do…while 循环。 |
14 |
double double 数据类型是 Java 支持的八种基本数据类型之一。 |
15 |
if if 语句之后可以跟一个可选的 else 语句,当布尔表达式为假时执行该语句。 |
16 |
enum Java Enum 类是所有 Java 语言枚举类型的公共基类。 |
17 |
extends extends 是用于继承类属性的关键字。以下是 extends 关键字的语法。 |
18 |
final final 关键字用于在 Java 中定义常量值或 final 方法/类。 |
19 |
finally finally 关键字用于定义 finally 块。finally 块紧跟 try 块或 catch 块之后。finally 代码块始终执行,无论是否出现异常。 |
20 |
float float 数据类型是 Java 支持的八种基本数据类型之一。它提供了创建可以接受浮点值浮点类型变量的方法。 |
21 |
for for 循环是一种重复控制结构,使您可以有效地编写需要执行特定次数的循环。 |
22 |
goto goto 语句目前不受 Java 支持。它作为保留关键字保留以供将来使用。作为替代,Java 支持带 break 和 continue 语句的标签。 |
23 |
if if 语句由布尔表达式后跟一条或多条语句组成。 |
24 |
implements 通常, implements 关键字与类一起使用,以继承接口的属性。 |
25 |
import import 键盘用于包的上下文中。 |
26 |
instanceof instanceof 关键字是一个运算符,仅用于对象引用变量。 |
27 |
int int 数据类型是 Java 支持的八种基本数据类型之一。 |
28 |
interface 接口是 Java 中的引用类型。它类似于类。它是抽象方法的集合。 |
29 |
long long 数据类型是 Java 支持的八个原始数据类型之一。 |
30 |
package 在 Java 中采用包是为了防止命名冲突、控制访问、简化类、接口、枚举和注解的搜索/查找以及使用等。 |
31 |
private 声明为私有的方法、变量和构造函数只能在声明该类本身内访问。 |
32 |
protected 受保护的访问修饰符无法应用于类和接口。 |
33 |
public 声明为公共类的类、方法、构造函数、接口等可以从任何其他类中访问。 |
34 |
short 通过向变量分配不同的数据类型,您可以在这些变量中存储整数、小数或字符。 |
35 |
static static 关键字用于创建独立于为类创建的任何实例的变量。 |
36 |
super super 关键字与 this 关键字类似。 |
37 |
switch switch 语句允许变量针对值列表进行相等性测试。 |
38 |
this this 关键字是识别对象的非常重要的关键字。以下是该关键字的用法。 |
39 |
throw 如果方法没有处理受检异常,则方法必须使用 throws 关键字声明它。 |
40 |
transient 序列化是一种概念,使用该概念,我们可以将对象的 state 写入字节流中,以便我们可以通过网络传输它(使用像 JPA 和 RMI 之类的技术)。 |
41 |
try 方法使用 try 和 catch 关键字的组合来捕获异常。 |
42 |
while Java 编程语言中的 while 循环语句在给定条件为真的情况下重复执行目标语句。 |
Comments in Java
Java 使用非常类似于 C 和 C++ 的单行和多行注释。Java 编译器忽略任何注释中可用的所有字符。
Example
public class MyFirstJavaProgram {
/* This is my first java program.
* This will print 'Hello World' as the output
* This is an example of multi-line comments.
*/
public static void main(String []args) {
// This is an example of single line comment
/* This is also an example of single line comment. */
System.out.println("Hello World");
}
}
Java - Variable Types
变量为我们的程序提供命名存储,以便其进行操作。Java 中的每个变量都有一个特定的类型,它决定了变量内存的大小和布局;可以在该内存中存储的值的范围;以及可以应用于该变量的操作集。
在你使用所有变量之前,都必须声明它们。以下是变量声明的基本形式 −
data type variable [ = value][, variable [ = value] ...] ;
此处数据类型是 Java 的数据类型之一,变量是变量的名称。若要声明指定类型的多个变量,可以使用逗号分隔列表。
以下是在 Java 中变量声明和初始化的有效示例 −
Example
int a, b, c; // Declares three ints, a, b, and c.
int a = 10, b = 10; // Example of initialization
byte B = 22; // initializes a byte type variable B.
double pi = 3.14159; // declares and assigns a value of PI.
char a = 'a'; // the char variable a iis initialized with value 'a'
本章将解释 Java 语言提供的多种变量类型。Java 中有三种变量 −
-
Local variables
-
Instance variables
-
Class/Static variables
Local Variables
-
局部变量在方法、构造函数或代码块中声明。
-
局部变量是在进入方法、构造函数或块时创建的,并且将在退出方法、构造函数或块后销毁该变量。
-
局部变量不能使用访问修饰符。
-
局部变量仅在已声明的方法、构造函数或块中可见。
-
局部变量在内部以栈级别实现。
-
局部变量没有默认值,因此在首次使用之前应声明局部变量并分配一个初始值。
Example
这里,age 是一个局部变量。它在 pupAge() 方法中定义,并且其范围仅限于此方法。
public class Test {
public void pupAge() {
int age = 0;
age = age + 7;
System.out.println("Puppy age is : " + age);
}
public static void main(String args[]) {
Test test = new Test();
test.pupAge();
}
}
Instance Variables
-
实例变量在类中声明,但方法、构造函数或任何代码块之外。
-
当在堆中为对象分配空间时,将为每个实例变量值创建一个槽。
-
当使用关键字“new”创建对象时将创建实例变量,并在销毁对象时销毁实例变量。
-
实例变量保存着必须由不止一个方法、构造函数或块引用的值,或者是必须在整个类中存在的对象的必需状态部分。
-
实例变量可以在使用之前或之后在类级别中声明。
-
实例变量可以提供访问修饰符。
-
实例变量对于类中的所有方法、构造函数和块可见。通常,建议将这些变量设为私有(访问级别)。但是,可以使用访问修饰符为这些变量提供子类的可见性。
-
实例变量具有默认值。对于数字,默认值是 0,对于布尔值,默认值是 false,而对于对象引用,默认值是 null。在声明期间或在构造函数中可以分配值。
-
可以通过在类内调用变量名称直接访问实例变量。但是,在静态方法中(当实例变量具有可访问性时),应使用完全限定名调用它们。ObjectReference.VariableName。
Example
import java.io.*;
public class Employee {
// this instance variable is visible for any child class.
public String name;
// salary variable is visible in Employee class only.
private double salary;
// The name variable is assigned in the constructor.
public Employee (String empName) {
name = empName;
}
// The salary variable is assigned a value.
public void setSalary(double empSal) {
salary = empSal;
}
// This method prints the employee details.
public void printEmp() {
System.out.println("name : " + name );
System.out.println("salary :" + salary);
}
public static void main(String args[]) {
Employee empOne = new Employee("Ransika");
empOne.setSalary(1000);
empOne.printEmp();
}
}
Class/Static Variables
-
类变量(也称为静态变量)在类中使用 static 关键字声明,但位于方法、构造函数或块外部。
-
每个类只会有一个类变量副本,无论从它创建了多少个对象。
-
静态变量很少使用,除了被声明为常量。常量是被声明为 public/private、final 和 static 的变量。常量变量永远不会从其初始值更改。
-
静态变量存储在静态内存中。除了声明为 final 并用作公共或私有常量外,很少使用静态变量。
-
静态变量在程序启动时创建,并在程序停止时销毁。
-
可见性类似于实例变量。然而,大多数静态变量被声明为公共的,因为它们必须对类的用户可用。
-
默认值与实例变量相同。对于数字,默认值是 0;对于布尔值,默认值是 false;而对于对象引用,默认值是 null。在声明期间或在构造函数中可以分配值。此外,还可以在特殊的静态初始化程序块中分配值。
-
可以通过用类名 ClassName.VariableName 调用访问静态变量。
-
在将类变量声明为 public static final 时,则变量名(常量)均为大写。如果静态变量不是公共的和最终的,则命名语法与实例变量和局部变量相同。
Example
import java.io.*;
public class Employee {
// salary variable is a private static variable
private static double salary;
// DEPARTMENT is a constant
public static final String DEPARTMENT = "Development ";
public static void main(String args[]) {
salary = 1000;
System.out.println(DEPARTMENT + "average salary:" + salary);
}
}
Java - Basic Datatypes
变量无非就是保留的内存位置,用于存储值。这意味着创建变量时,你将在内存中保留一些空间。
根据变量的数据类型,操作系统将分配内存,并决定在保留的内存中存储什么。因此,通过将不同的数据类型分配给变量,你可以在这些变量中存储整数、小数或字符。
Java 中有两种数据类型 −
-
Primitive Data Types
-
Reference/Object Data Types
Primitive Data Types
Java 支持八种基本数据类型。基本数据类型由语言预定义,并由关键字命名。下面我们详细介绍八种基本数据类型。
byte
-
字节数据类型是一个 8 位有符号二进制补码整数
-
最小值为 -128 (-2^7)
-
最大值为 127(包括)(2^7 -1)
-
Default value is 0
-
字节数据类型用于在大数组中节省空间,主要用于代替整数,因为一个字节的容量是一个整数的四分之一。
-
示例:byte a = 100,byte b = -50
short
-
短整型数据类型是一个 16 位有符号二进制补码整数
-
最小值为 -32,768 (-2^15)
-
最大值为 32,767(包括)(2^15 -1)
-
短整型数据类型也可以像字节数据类型一样,用于节省内存。一个短整型数字比一个整数小 2 倍
-
Default value is 0.
-
示例:short s= 10000,short r = -20000
int
-
整形数据类型是一个 32 位有符号二进制补码整数。
-
最小值是 - 2,147,483,648 (-2^31)
-
最大值是 2,147,483,647(含) (2^31 -1)
-
整数通常用作整数的默认数据类型,除非关心内存。
-
默认值是 0
-
示例 − int a = 100000,int b = -200000
long
-
长数据类型是一个 64 位的有符号二进制补码整数
-
Minimum value is -9,223,372,036,854,775,808(-2^63)
-
最大值为 9,223,372,036,854,775,807(包括)(2^63 -1)
-
需要比 int 更宽的范围时使用此类型
-
Default value is 0L
-
示例 − long a = 100000L,long b = -200000L
float
-
浮点数据类型是单精度 32 位 IEEE 754 浮点数
-
浮点主要用于节省大量浮点数数组的内存
-
Default value is 0.0f
-
浮点数据类型绝不用于货币等精确值
-
示例 − float f1 = 234.5f
double
-
双精度数据类型是双精度 64 位 IEEE 754 浮点数
-
此数据类型通常用作十进制值的默认数据类型,通常是默认选择
-
双精度数据类型绝不用于货币等精确值
-
Default value is 0.0d
-
示例 − double d1 = 123.4
boolean
-
布尔数据类型表示一位信息
-
只有两个可能的值:真和假
-
此数据类型用于跟踪真/假条件的简单标志
-
Default value is false
-
示例 − boolean one = true
char
-
char 数据类型是一个 16 位 Unicode 字符
-
最小值是 '\u0000'(或 0)
-
最大值为 ‘\uffff’ (或 65,535,含)
-
Char 数据类型用于存储任何字符
-
例如:char letterA = ‘A’
Example
以下示例展示了上面讨论的各种原始数据类型的用法。我们在数字数据类型上使用了 add 运算,而布尔变量和 char 变量则按原样打印。
public class JavaTester {
public static void main(String args[]) {
byte byteValue1 = 2;
byte byteValue2 = 4;
byte byteResult = (byte)(byteValue1 + byteValue2);
System.out.println("Byte: " + byteResult);
short shortValue1 = 2;
short shortValue2 = 4;
short shortResult = (short)(shortValue1 + shortValue2);
System.out.println("Short: " + shortResult);
int intValue1 = 2;
int intValue2 = 4;
int intResult = intValue1 + intValue2;
System.out.println("Int: " + intResult);
long longValue1 = 2L;
long longValue2 = 4L;
long longResult = longValue1 + longValue2;
System.out.println("Long: " + longResult);
float floatValue1 = 2.0f;
float floatValue2 = 4.0f;
float floatResult = floatValue1 + floatValue2;
System.out.println("Float: " + floatResult);
double doubleValue1 = 2.0;
double doubleValue2 = 4.0;
double doubleResult = doubleValue1 + doubleValue2;
System.out.println("Double: " + doubleResult);
boolean booleanValue = true;
System.out.println("Boolean: " + booleanValue);
char charValue = 'A';
System.out.println("Char: " + charValue);
}
}
Reference Datatypes
-
使用类的定义构造函数来创建引用变量。这些变量用于访问对象。声明这些变量是为了指定不能更改的特定类型。例如,Employee、Puppy 等。
-
类对象和各种类型的数组变量属于引用数据类型。
-
任何引用变量的默认值为 null。
-
一个引用变量可用于引用已声明类型或任何兼容类型的任何对象。
-
示例:Animal animal = new Animal(“giraffe”)
Java Literals
字面值是固定值的源代码表示形式。它们在代码中直接表示,无需任何计算。
字面值可分配给任何原始类型变量。例如:
byte a = 68;
char a = 'A';
byte、int、long 和 short 也可用十进制(基数 10)、十六进制(基数 16)或八进制(基数 8)数字系统来表示。
使用这些数字系统表示字面值时,前缀 0 用于表示八进制,前缀 0x 用于表示十六进制。例如:
int decimal = 100;
int octal = 0144;
int hexa = 0x64;
Java 中的字符串字面值按照它们在大多数其他语言中的指定方式,即用一对双引号来括起一系列字符。字符串字面值示例如下:
Example
"Hello World"
"two\nlines"
"\"This is in quotes\""
String 和 char 类型的字面值可以包含任何 Unicode 字符。例如:
char a = '\u0001';
String a = "\u0001";
Example
以下示例展示了上面讨论的各种字面值用法。
public class JavaTester {
public static void main(String args[]) {
int decimal = 100;
int octal = 0144;
int hexa = 0x64;
System.out.println(decimal);
System.out.println(octal);
System.out.println(hexa);
String msg1 = "Hello World";
String msg2 = "two\nlines";
String msg3 = "\"This is in quotes\"";
System.out.println(msg1);
System.out.println(msg2);
System.out.println(msg3);
char a = '\u0064';
String msg4 = "\u0064";
System.out.println(a);
System.out.println(msg4);
}
}
Output
100
100
100
Hello World
two
lines
"This is in quotes"
d
d
Java 语言还支持用于 String 和 char 字面值的一些特殊转义序列。它们包括:
Notation |
Character represented |
\n |
Newline (0x0a) |
\r |
Carriage return (0x0d) |
\f |
Formfeed (0x0c) |
\b |
Backspace (0x08) |
\s |
Space (0x20) |
\t |
tab |
\" |
Double quote |
\' |
Single quote |
|backslash |
\ddd |
Octal character (ddd) |
\uxxxx |
Java - Basic Operators
Java 提供了一组丰富的操作符用于操作变量。我们可以将所有 Java 操作符划分为以下组:
-
Arithmetic Operators
-
Relational Operators
-
Bitwise Operators
-
Logical Operators
-
Assignment Operators
-
Misc Operators
The Arithmetic Operators
算术运算符在数学表达式中使用的方式与其在代数中的使用方式相同。下表列出了算术运算符 −
假设整型变量 A 为 10,变量 B 为 20,那么 −
Operator |
Description |
Example |
+ (Addition) |
对运算符两边的值求和。 |
A + B 会给出 30 |
- (Subtraction) |
从左操作数减去右操作数。 |
A - B 会给出 -10 |
* (Multiplication) |
对运算符两边的值求积。 |
A * B 会给出 200 |
/ (Division) |
用左操作数除以右操作数。 |
B / A 会给出 2 |
% (Modulus) |
用左操作数除以右操作数并返回余数。 |
B % A 会给出 0 |
++ (Increment) |
使操作数的值增加 1。 |
B++ gives 21 |
— (Decrement) |
使操作数的值减少 1。 |
B-- gives 19 |
The Relational Operators
Java 语言支持以下关系运算符。
假设变量 A 为 10,变量 B 为 20,那么 −
Operator |
Description |
Example |
== (equal to) |
检查两个操作数的值是否相等,如果相等,则条件变为真。 |
(A == B) 不为真。 |
!= (not equal to) |
检查两个操作数的值是否相等,如果值不相等,则条件变为真。 |
(A != B) 为真。 |
> (greater than) |
检查左操作数的值是否大于右操作数的值,如果大于,则条件变为真。 |
(A > B) 不为真。 |
< (less than) |
检查左操作数的值是否小于右操作数的值,如果小于,则条件变为真。 |
(A < B) 为真。 |
>= (大于或等于) |
检查左操作数的值是否大于或等于右操作数的值,如果大于或等于,则条件变为真。 |
(A >= B) 不为真。 |
⇐ (小于或等于) |
检查左操作数的值是否小于或等于右操作数的值,如果小于或等于,则条件变为真。 |
(A ⇐ B) 为真。 |
The Bitwise Operators
Java 定义了多个按位运算符,它们可以应用于整数类型 long、int、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
下表列出了按位运算符:
假设整数变量 A 保存 60 且变量 B 保存 13,则:
Operator |
Description |
Example |
& (bitwise and) |
二进制与运算符将位复制到结果中(如果它存在于两个操作数中)。 |
(A 和 B) 结果是 12,即 0000 1100 |
(bitwise or) |
二进制 OR 运算符会在任一操作数中存在的情况下复制比特。 |
|
(A |
B) 结果是 61,即 0011 1101 |
^ (bitwise XOR) |
二进制 XOR 运算符会在其中一个操作数中但不在两个操作数中都设置的情况下复制比特。 |
(A ^ B) 结果为 49,即 0011 0001 |
⁓ (bitwise compliment) |
二进制逻辑补运算符是单操作数运算符,它会“翻转”比特。 |
(⁓A) 由于有符号二进制数字形式,结果为 -61,即 1100 0011。 |
<< (left shift) |
二进制左移运算符。左操作数的值会向左移动由右操作数指定的比特数。 |
A << 2 的结果是 240,即 1111 0000 |
>> (right shift) |
二进制右移运算符。左操作数的值会向右移动由右操作数指定的比特数。 |
A >> 2 的结果是 15,即 1111 |
>>>(零填充右移) |
The Logical Operators
下表列出了逻辑运算符:
假设布尔变量 A 为 true,变量 B 为 false,则:
Operator |
Description |
Example |
&& (logical and) |
称为逻辑 AND 运算符。如果两个操作数都非零,则条件变为 true。 |
(A && B) 为 false |
(logical or) |
||
称为逻辑 OR 运算符。如果两个操作数中任何一个非零,则条件变为 true。 |
(A |
|
B) is true |
! (logical not) |
称为逻辑 NOT 运算符。用于反转操作数的逻辑状态。如果条件为 true,则逻辑 NOT 运算符会将其变为 false。 |
The Assignment Operators
以下是 Java 语言支持的赋值运算符 −
Operator |
Description |
Example |
= |
简单赋值运算符。将右操作数的值赋给左操作数。 |
C = A + B 将 A + B 的值赋给 C |
+= |
添加和赋值运算符。它将右操作数加到左操作数,并将结果赋给左操作数。 |
C += A 等效于 C = C + A |
-= |
减和赋值运算符。它将左操作数减去右操作数,并将结果赋给左操作数。 |
C -= A 等效于 C = C - A |
*= |
乘和赋值运算符。它将右操作数乘以左操作数,并将结果赋给左操作数。 |
C *= A 等效于 C = C * A |
/= |
除和赋值运算符。它将左操作数除以右操作数,并将结果赋给左操作数。 |
C /= A 等效于 C = C / A |
%= |
模运算和赋值运算符。它使用两个操作数取模,并将结果赋给左操作数。 |
C %= A 等效于 C = C % A |
<⇐ |
左移和赋值运算符。 |
C <⇐ 2 等效于 C = C << 2 |
>>= |
右移和赋值运算符。 |
C >>= 2 等效于 C = C >> 2 |
&= |
Bitwise AND assignment operator. |
C &= 2 等效于 C = C & 2 |
^= |
按位异或和赋值运算符。 |
C ^= 2 等同于 C = C ^ 2 |
= |
按位或和赋值运算符。 |
|
C |
= 2 等同于 C = C |
2 |
Example
在这个示例中,我们创建了两个变量 a 和 b,并且使用 ternary operator 决定了 b 的值并打印出来了。
public class Test {
public static void main(String args[]) {
int a, b;
a = 10;
b = (a == 1) ? 20: 30;
System.out.println( "Value of b is : " + b );
b = (a == 10) ? 20: 30;
System.out.println( "Value of b is : " + b );
}
}
Example
在此示例中,我们创建了一个 String 变量 name,然后使用 instanceof operator 检查 name 是否为 String。
public class Test {
public static void main(String args[]) {
String name = "James";
// following will return true since name is type of String
boolean result = name instanceof String;
System.out.println( result );
}
}
Example
在此示例中,我们创建一个类的变量 a Vehicle,然后使用 instanceof operator 检查 name 是否为类型 Car。
class Vehicle {}
public class Car extends Vehicle {
public static void main(String args[]) {
Vehicle a = new Car();
boolean result = a instanceof Car;
System.out.println( result );
}
}
Precedence of Java Operators
运算符优先级决定表达式中术语的分组。这会影响表达式的评估方式。某些运算符的优先级高于其他运算符;例如,乘法运算符的优先级高于加法运算符 −
例如,x = 7 + 3 * 2;此处 x 分配为 13,而不是 20,因为运算符 * 的优先级高于 +,因此它首先与 3 * 2 相乘,然后加到 7 中。
此处,优先级最高的运算符显示在表顶部,优先级最低的运算符显示在表底部。在表达式中,将首先评估优先级较高的运算符。
Category |
Operator |
Associativity |
Postfix |
expression++ expression-- |
Left to right |
Unary |
++expression --expression +expression -expression ⁓ ! |
Right to left |
Multiplicative |
* / % |
Left to right |
Additive |
+ - |
Left to right |
Shift |
<< >> >>> |
Left to right |
Relational |
< > ⇐ >= instanceof |
Left to right |
Equality |
== != |
Left to right |
Bitwise AND |
& |
Left to right |
Bitwise XOR |
^ |
Left to right |
Bitwise OR |
||
Left to right |
Logical AND |
&& |
Left to right |
Logical OR |
|
Left to right |
||
Conditional |
?: |
Right to left |
Assignment |
= += -= *= /= %= ^= |
= <⇐ >>= >>>= |
Java - Loop Control
您可能需要执行一段代码块多次。通常,语句按顺序执行:函数中的第一个语句先执行,然后是第二个,依此类推。
编程语言提供了各种控制结构,允许执行更复杂的路径。
loop 语句允许我们执行一个语句或语句组多次,以下是大多数编程语言中循环语句的一般形式 −
Java 编程语言提供以下类型的循环来处理循环要求。单击以下链接查看其详细信息。
Sr.No. |
Loop & Description |
1 |
while loop 在给定条件为真时重复执行某个语句或语句组。它会在执行循环体之前测试条件。 |
2 |
for loop 多次执行一系列语句,并缩略管理循环变量的代码。 |
3 |
do…while loop 类似于 while 语句,只不过它在循环体末尾才测试条件。 |
Loop Control Statements
循环控制语句改变了它在正常序列中的执行。当执行退出一个作用域时,在该作用域中创建的所有自动对象会被销毁。
Java 支持以下控制语句。单击以下链接查看其详细信息。
Sr.No. |
Control Statement & Description |
1 |
break statement 终止 loop 或 switch 语句,并将执行转移到紧跟循环或 switch 之后的语句。 |
2 |
continue statement 导致循环跳过其余主体并立即在重复之前重新测试其条件。 |
Enhanced for loop in Java
Java 5 引入了增强型 for 循环。这主要用于遍历包含数组在内的元素集合。
Syntax
以下是增强型 for 循环的语法:
for(declaration : expression) {
// Statements
}
-
Declaration - 新声明的块变量与您正在访问的数组的元素的类型兼容。该变量将在 for 块中可用,其值将与当前数组元素相同。
-
Expression - 此表达式计算您需要循环访问的数组。该表达式可以是数组变量或返回数组的方法调用。
Example
在此示例中,我们展示了使用 foreach 循环打印数组内容。此处我们创建一个名为 numbers 的整数数组,并对其初始化一些值。然后使用 foreach 循环,打印每个数字。创建另一个名为 names 的字符串数组,并使用 foreach 循环对其进行迭代以打印每个元素。
public class Test {
public static void main(String args[]) {
int [] numbers = {10, 20, 30, 40, 50};
for(int x : numbers ) {
System.out.print( x );
System.out.print(",");
}
System.out.print("\n");
String [] names = {"James", "Larry", "Tom", "Lacy"};
for( String name : names ) {
System.out.print( name );
System.out.print(",");
}
}
}
Java - Decision Making
决策结构具有一条或多条要由程序评估或测试的条件,以及在确定条件为真时要执行的语句,并且在确定条件为假时可以选择执行其他语句。
以下是大多数编程语言中常见的典型决策结构的一般形式 −
Java 编程语言提供了以下类型的决策制定语句。单击以下链接以查看它们的详细信息。
Sr.No. |
Statement & Description |
1 |
if statement 一个 if statement 包含一个布尔表达式,后跟一个或多个语句。 |
2 |
if…else statement 一个 if statement 可以后跟一个可选的 else statement ,该语句在布尔表达式为 false 时执行。 |
3 |
nested if statement 你可以在另一个 if 或 else if 语句内使用一个 if 或 else if 语句。 |
4 |
switch statement 一个 switch 语句允许测试一个变量是否与值列表相等。 |
The ? : Operator
我们已经在上个章节中介绍了 conditional operator ? :,它可以替换 if…else 语句。它的通用格式如下 −
Exp1 ? Exp2 : Exp3;
其中 Exp1、Exp2 和 Exp3 是表达式。请注意冒号的用处和位置。
要确定整个表达式的值,最初会求出 exp1 的值。
-
如果 exp1 的值为 True,则 Exp2 的值将是整个表达式的值。
-
如果 exp1 的值为 False,则求出 Exp3,并以其值为整个表达式的值。
Java - if-else statement
if 语句后面可以紧跟一个可选的 else 语句,该语句在布尔表达式为假时执行。
Syntax
以下是 if…else 语句的语法:
if(Boolean_expression) {
// Executes when the Boolean expression is true
}else {
// Executes when the Boolean expression is false
}
如果布尔表达式求值为 true,则将执行 if 代码块,否则将执行 else 代码块。
Example 1
在此示例中,我们展示了 if else 语句的使用。我们创建了一个变量 x 并将其初始化为 30。然后在 if 语句中,我们用 20 检查 x。由于 if 语句是 false,因此执行 else 块中的语句。
public class Test {
public static void main(String args[]) {
int x = 30;
if( x < 20 ) {
System.out.print("This is if statement");
}else {
System.out.print("This is else statement");
}
}
}
The if…else if…else Statement
一个 if 语句后面可以跟一个可选的 else if…else 语句,该语句对于使用单个 if…else if 语句测试各种条件非常有用。
在使用 if、else if、else 语句时,有几点需要记住。
-
一个 if 可以有零个或一个 else,它必须位于任何 else if 之后。
-
一个 if 可以有零个到多个 else if,它们必须在 else 之前。
-
一旦一个 else if 成功,剩余的 else if 或 else 都不会被测试。
Syntax
以下是 if…else 语句的语法:
if(Boolean_expression 1) {
// Executes when the Boolean expression 1 is true
}else if(Boolean_expression 2) {
// Executes when the Boolean expression 2 is true
}else if(Boolean_expression 3) {
// Executes when the Boolean expression 3 is true
}else {
// Executes when the none of the above condition is true.
}
Example 2
在此示例中,我们展示了 if…else if…else 语句的使用。我们创建了一个变量 x 并将其初始化为 30。然后在 if 语句中,我们用 10 检查 x。由于 if 语句是 false,控制权跳转到 else if 语句,用另一个值检查 x,依此类推。
public class Test {
public static void main(String args[]) {
int x = 30;
if( x == 10 ) {
System.out.print("Value of X is 10");
}else if( x == 20 ) {
System.out.print("Value of X is 20");
}else if( x == 30 ) {
System.out.print("Value of X is 30");
}else {
System.out.print("This is else statement");
}
}
}
Example 3
在此示例中,我们展示了 if…else if…else 语句的使用。我们创建了一个变量 x 并将其初始化为 30.0。然后在 if 语句中,我们用 10,0 检查 x。由于 if 语句是 false,控制权跳转到 else if 语句,用另一个值检查 x,依此类推。
public class Test {
public static void main(String args[]) {
double x = 30.0;
if( x == 10.0 ) {
System.out.print("Value of X is 10.0");
}else if( x == 20.0 ) {
System.out.print("Value of X is 20.0");
}else if( x == 30.0 ) {
System.out.print("Value of X is 30.0");
}else {
System.out.print("This is else statement");
}
}
}
Java - switch statement
一个 switch 语句允许变量针对一组值进行相等性测试。每个值称为一个 case,并且正在切换的变量针对每个 case 进行检查。
Syntax
增强 switch 语句的语法为:
switch(expression) {
case value :
// Statements
break; // optional
case value :
// Statements
break; // optional
// You can have any number of case statements.
default : // Optional
// Statements
}
以下规则适用于 switch 语句:
-
switch 语句中使用的变量只能是整数、可转换整数(字节、短整数、字符)、字符串和枚举。
-
你可以在 switch 中放置任意数量的 case 语句。每个 case 后面都跟一个要比较的值和一个冒号。
-
case 的值必须与 switch 中变量的数据类型相同,它必须是一个常量或一个字面量。
-
当正在切换的变量等于某个 case 时,将执行该 case 后面的语句,直到到达 break 语句。
-
当到达 break 语句时,switch 终止,并且控制流跳到 switch 语句后面的下一行。
-
并非每个 case 都需要包含 break。如果没有出现 break,则控制流会向下至后续 case,直到到达 break。
-
switch 语句可以包含一个可选的 default case,它必须出现在 switch 的末尾。default case 可用于在所有 case 都为假时执行任务。default case 中不需要 break。
Example 1
在此示例中,我们展示了 switch 语句的使用,其中 case 基于字符。我们创建了一个变量 grade。根据 grade 的值,检查每个 case。如果满足了一个 case 并且存在 break 语句,则不会检查后续 case。
public class Test {
public static void main(String args[]) {
char grade = 'C';
switch(grade) {
case 'A' :
System.out.println("Excellent!");
break;
case 'B' :
case 'C' :
System.out.println("Well done");
break;
case 'D' :
System.out.println("You passed");
case 'F' :
System.out.println("Better try again");
break;
default :
System.out.println("Invalid grade");
}
System.out.println("Your grade is " + grade);
}
}
使用各种命令行参数编译并运行上述程序。这会产生以下结果:
Example 2
在此示例中,我们将展示基于 int 的交换语句的使用方法。我们创建了一个变量 grade。根据 grade 的值,检查每个 case。如果 case 满足并且有 break 语句,则后续 case 将不会被检查。
public class Test {
public static void main(String args[]) {
int grade = 3;
switch(grade) {
case 1 :
System.out.println("Excellent!");
break;
case 2 :
case 3 :
System.out.println("Well done");
break;
case 4 :
System.out.println("You passed");
case 5 :
System.out.println("Better try again");
break;
default :
System.out.println("Invalid grade");
}
System.out.println("Your grade is " + grade);
}
}
使用各种命令行参数编译并运行上述程序。这会产生以下结果:
Example 3
在此示例中,我们将展示基于字符串的交换语句的使用方法。我们创建了一个变量 grade。根据 grade 的值,检查每个 case。如果 case 满足并且有 break 语句,则后续 case 将不会被检查。
public class Test {
public static void main(String args[]) {
String grade = "C";
switch(grade) {
case "A" :
System.out.println("Excellent!");
break;
case "B" :
case "C" :
System.out.println("Well done");
break;
case "D" :
System.out.println("You passed");
case "F" :
System.out.println("Better try again");
break;
default :
System.out.println("Invalid grade");
}
System.out.println("Your grade is " + grade);
}
}
使用各种命令行参数编译并运行上述程序。这会产生以下结果:
Java - for Loop
for 循环是一种重复控制结构,允许您高效地编写需要执行特定次数的循环。
for 循环在你知道任务重复多少次时会很有用。
Syntax
for 循环的语法为:
for(initialization; Boolean_expression; update) {
// Statements
}
以下是 for 循环中的控制流程:
-
initialization 语句会先执行,并且只执行一次。此步骤允许你声明并初始化任意循环控制变量,并且此步骤以一个分号 (;) 结尾。
-
接下来,计算 Boolean expression。如果为真,则执行循环体。如果为假,则不会执行循环体,并且控制跳转到 for 循环之后的下一条语句。
-
在执行 for 循环的 body 之后,控制会跳回到更新语句。此语句允许您更新任何循环控制变量。此语句可以留空,在末尾加分号。
-
现在再次计算布尔表达式。如果为真,则循环执行并重复此过程(循环体,然后更新步骤,然后布尔表达式)。在布尔表达式为假之后,for 循环终止。
Example 1
在此示例中,我们将展示如何使用 for 循环来打印从 10 到 19 的数字。在此,我们在 for 循环的初始化块中初始化了一个 int 变量 x,值为 10。然后,在表达式块中,我们检查 x 是否小于 20,并且最后在更新块中,我们把 x 加 1。在 for 循环的代码块中,我们打印 x 的值。for 循环会一直运行直到 x 变为 20。一旦 x 为 20,循环就会停止执行,程序会退出。
public class Test {
public static void main(String args[]) {
for(int x = 10; x < 20; x = x + 1) {
System.out.print("value of x : " + x );
System.out.print("\n");
}
}
}
Output
value of x : 10
value of x : 11
value of x : 12
value of x : 13
value of x : 14
value of x : 15
value of x : 16
value of x : 17
value of x : 18
value of x : 19
Example 2
在此示例中,我们将展示如何使用 for 循环来打印数组的内容。在这里,我们创建了一个整数数组 numbers,并初始化了一些值。我们已经创建了一个名为 index 的变量来表示 for 循环中数组的索引,并检查它是否小于数组的大小,然后把它加 1。在 for 循环的代码块中,我们使用索引标记法来打印数组的元素。一旦索引与数组大小相同,for 循环就退出,程序就停止。
public class Test {
public static void main(String args[]) {
int [] numbers = {10, 20, 30, 40, 50};
for(int index = 0; index < numbers.length; index++) {
System.out.print("value of item : " + numbers[index] );
System.out.print("\n");
}
}
}
Output
value of item : 10
value of item : 20
value of item : 30
value of item : 40
value of item : 50
Java - for each Loop
for each 循环是一种特殊的重复控制结构,它能让你高效地编写需要执行特定次数的循环。
即使您不知道任务要重复多少次,for each 循环也很有用。
Syntax
以下是增强型 for 循环的语法:
for(declaration : expression) {
// Statements
}
-
Declaration - 新声明的块变量与您正在访问的数组的元素的类型兼容。该变量将在 for 块中可用,其值将与当前数组元素相同。
-
Expression - 此表达式计算您需要循环访问的数组。该表达式可以是数组变量或返回数组的方法调用。
Example 1
在此示例中,演示了使用 foreach 循环打印整数列表内容。此处将整数列表创建为数字,并初始化为一些值。然后,使用 foreach 循环打印每个数字。
import java.util.Arrays;
import java.util.List;
public class Test {
public static void main(String args[]) {
List<Integer> numbers = Arrays.asList(10, 20, 30, 40, 50);
for(Integer x : numbers ) {
System.out.print( x );
System.out.print(",");
}
}
}
Example 2
在此示例中,我们展示如何使用 foreach 循环来打印 String List 的内容。在此,我们创建了一个字符串数组 names,并初始化了一些值。然后,使用 foreach 循环打印每个名称。
import java.util.Arrays;
import java.util.List;
public class Test {
public static void main(String args[]) {
List<String> names = Arrays.asList("James", "Larry", "Tom", "Lacy");
for( String name : names ) {
System.out.print( name );
System.out.print(",");
}
}
}
Example 3
在此示例中,我们展示如何使用 foreach 循环来打印 Student Object 数组的内容。在此,我们创建了一个学生数组 Student Objects,并初始化了一些值。然后,使用 foreach 循环打印每个名称。
public class Test {
public static void main(String args[]) {
Student[] students = { new Student(1, "Julie"), new Student(3, "Adam"), new Student(2, "Robert") };
for( Students student : students ) {
System.out.print( student );
System.out.print(",");
}
}
}
class Student {
int rollNo;
String name;
Student(int rollNo, String name){
this.rollNo = rollNo;
this.name = name;
}
@Override
public String toString() {
return "[ " + this.rollNo + ", " + this.name + " ]";
}
}
Java - while Loop
Java 编程语言中的 while 循环语句只要给定的条件为真,就会重复执行目标语句。
Syntax
while 循环的语法为 −
while(Boolean_expression) {
// Statements
}
在此处,statement(s) 可能是一个简单的语句或一个语句块。condition 可能是任意表达式,并且 true 是任意非零值。
执行时,如果 boolean_expression 结果为真,那么循环内部的操作将被执行。只要该表达式结果为真,此操作就会继续。
当条件变为假时,程序控制权将传递到循环之后的行。
Example 1
在此示例中,我们展示如何使用 while 循环来打印从 10 到 19 的数字。在此,我们初始化了一个 int 变量 x,值为 10。然后,在 while 循环中,我们检查 x 是否小于 20,并且在 while 循环中,我们打印 x 的值并且让 x 的值加 1。while 循环会一直运行直到 x 变为 20。一旦 x 为 20,循环就会停止执行,程序会退出。
public class Test {
public static void main(String args[]) {
int x = 10;
while( x < 20 ) {
System.out.print("value of x : " + x );
x++;
System.out.print("\n");
}
}
}
Output
value of x : 10
value of x : 11
value of x : 12
value of x : 13
value of x : 14
value of x : 15
value of x : 16
value of x : 17
value of x : 18
value of x : 19
Example 2
在此示例中,我们展示如何使用 while 循环来打印数组的内容。在这里,我们创建了一个整数数组 numbers,并初始化了一些值。我们已经创建了一个名为 index 的变量来表示遍历数组时的数组索引。在 while 循环中,我们检查索引是否小于数组的大小,然后使用索引标记法打印数组的元素。index 变量加 1,并且循环会持续到索引变为数组的规定大小为止,然后循环退出。
public class Test {
public static void main(String args[]) {
int [] numbers = {10, 20, 30, 40, 50};
int index = 0;
while( index < 5 ) {
System.out.print("value of item : " + numbers[index] );
index++;
System.out.print("\n");
}
}
}
Output
value of item : 10
value of item : 20
value of item : 30
value of item : 40
value of item : 50
Java - do…while Loop
do…while 循环与 while 循环类似,不同之处在于 do…while 循环保证至少执行一次。
Syntax
以下是 do…while 循环的语法:
do {
// Statements
}while(Boolean_expression);
请注意布尔表达式出现在循环的末尾,因此循环中的语句在检查布尔表达式之前执行一次。
如果布尔表达式为真,控制权就会跳转回 do 语句,循环中的语句会再次执行。此过程会重复运行,直到布尔表达式为假为止。
Example 1
在此示例中,我们展示如何使用 while 循环来打印从 10 到 19 的数字。在此,我们初始化了一个 int 变量 x,值为 10。然后,在 do while 循环中,我们在 do while 循环代码块中检查 x 是否小于 20。在 do while 循环代码块中,我们打印 x 的值,并且把 x 的值加 1。while 循环会一直运行直到 x 变为 20。一旦 x 为 20,循环就会停止执行,程序会退出。
public class Test {
public static void main(String args[]) {
int x = 10;
do {
System.out.print("value of x : " + x );
x++;
System.out.print("\n");
}while( x < 20 );
}
}
Output
value of x : 10
value of x : 11
value of x : 12
value of x : 13
value of x : 14
value of x : 15
value of x : 16
value of x : 17
value of x : 18
value of x : 19
Example 2
在此示例中,我们展示如何使用 do while 循环来打印数组的内容。在这里,我们创建了一个整数数组 numbers,并初始化了一些值。我们已经创建了一个名为 index 的变量来表示遍历数组时的数组索引。在 do while 循环中,我们在循环代码块之后检查索引是否小于数组的大小,然后使用索引标记法打印数组的元素。在循环代码块中,index 变量加 1,并且循环会持续到索引变为数组的规定大小为止,然后循环退出。
public class Test {
public static void main(String args[]) {
int [] numbers = {10, 20, 30, 40, 50};
int index = 0;
do {
System.out.print("value of item : " + numbers[index] );
index++;
System.out.print("\n");
} while( index < 5 );
}
}
Output
value of item : 10
value of item : 20
value of item : 30
value of item : 40
value of item : 50
Java - break statement
Java 编程语言中的 break 语句具有以下两个用法:
-
当在循环中遇到 break 语句时,该循环将立即终止,程序控制将继续在循环后的下一个语句中。
-
它可用于中止 switch 语句中的某个 case(在下一章中介绍)。
Example 1
在这个示例中,我们展示了如何使用 break 语句来中断一个 while 循环,以打印从 10 到 14 的数字,而没有 break 语句,将会打印到 19。在这里,我们使用值 10 初始化了一个 int 变量 x。然后在 while 循环中,我们检查 x 是否小于 20,并且在 while 循环内,我们打印 x 的值并使 x 的值增加 1。while 循环将一直运行,直到 x 变成 15。一旦 x 变成 15,break 语句将中断 while 循环,程序退出。
public class Test {
public static void main(String args[]) {
int x = 10;
while( x < 20 ) {
if(x == 15){
break;
}
System.out.print("value of x : " + x );
x++;
System.out.print("\n");
}
}
}
Example 2
在这个示例中,我们展示了如何在 for 循环中使用 break 语句来打印数组的一些元素,而不是所有元素。在这里,我们创建一个名为 numbers 的整数数组,并初始化为一些值。我们创建了一个名为 index 的变量,以表示 for 循环中数组的索引,并检查数组的大小,并使其增加 1。在 for 循环体中,我们使用索引表示法打印数组元素。一旦遇到值为 30,break 语句将中断 for 循环,程序退出。
public class Test {
public static void main(String args[]) {
int [] numbers = {10, 20, 30, 40, 50};
for(int index = 0; index < numbers.length; index++) {
if(numbers[index] == 30){
break;
}
System.out.print("value of item : " + numbers[index] );
System.out.print("\n");
}
}
}
Java - continue statement
continue 关键字可以在任何循环控制结构中使用。它导致循环立即跳转到循环的下一个迭代。
-
在 for 循环中,continue 关键字将导致控制立即跳转到 update 语句。
-
在 while 循环或 do/while 循环中,控制立即跳转到布尔表达式。
Example 1
在这个示例中,我们展示了如何在 while 循环中使用 continue 语句跳过元素 15,该 while 循环用于打印 10 到 19 的元素。在这里,我们使用值 10 初始化了一个 int 变量 x。然后在 while 循环中,我们检查 x 是否小于 20,并且在 while 循环内,我们打印 x 的值并使 x 的值增加 1。while 循环将一直运行,直到 x 变成 15。一旦 x 变成 15,continue 语句将在跳过主体执行的同时跳出 while 循环,并且循环将继续。
public class Test {
public static void main(String args[]) {
int x = 10;
while( x < 20 ) {
x++;
if(x == 15){
continue;
}
System.out.print("value of x : " + x );
System.out.print("\n");
}
}
}
Output
value of x : 11
value of x : 12
value of x : 13
value of x : 14
value of x : 16
value of x : 17
value of x : 18
value of x : 19
value of x : 20
Example 2
在这个示例中,我们展示了如何在 for 循环中使用 continue 语句跳过数组元素以进行打印。在这里,我们创建一个名为 numbers 的整数数组,并初始化为一些值。我们创建了一个名为 index 的变量,以表示 for 循环中数组的索引,并检查数组的大小,并使其增加 1。在 for 循环体中,我们使用索引表示法打印数组元素。一旦遇到值为 30,continue 语句将跳转到 for 循环的更新部分,并且循环将继续。
public class Test {
public static void main(String args[]) {
int [] numbers = {10, 20, 30, 40, 50};
for(int index = 0; index < numbers.length; index++) {
if(numbers[index] == 30){
continue;
}
System.out.print("value of item : " + numbers[index] );
System.out.print("\n");
}
}
}
Example 3
在此示例中,我们展示了使用 continue 语句在 do while 循环中跳过元素 15 的用法,该循环用于打印从 10 到 19 的元素。此处,我们使用值 10 初始化了 int 变量 x。然后在 do while 循环中,我们在主体和 while 循环内检查 x 是否小于 20,打印 x 的值并将其增加 1。while 循环将运行,直至 x 变成 15。x 一旦变成 15,continue 语句将使 while 循环跳过主体执行并进入循环。
public class Test {
public static void main(String args[]) {
int x = 10;
do {
x++;
if(x == 15){
continue;
}
System.out.print("value of x : " + x );
System.out.print("\n");
} while( x < 20 );
}
}
Java - Object and Classes
Java 是一种面向对象的语言。作为具有面向对象特性的语言,Java 支持以下基本概念:
-
Polymorphism
-
Inheritance
-
Encapsulation
-
Abstraction
-
Classes
-
Objects
-
Instance
-
Method
-
Message Passing
在本章中,我们将研究概念 - 类和对象。
-
Object - 对象具有状态和行为。例如:一只狗具有以下状态 - 颜色、名称、品种,以及以下行为 - 摇尾巴、吠叫、饮食。一个对象是某个类的实例。
-
Class - 一个类可以被定义为描述其实例类型所支持的行为/状态的模板/蓝图。
Objects in Java
现在让我们深入了解什么是对象。如果我们考虑现实世界,我们可以在周围找到许多对象,例如汽车、狗、人类,等等。所有这些对象都具有状态和行为。
如果我们考虑一只狗,那么它的状态是 - 名称、品种、颜色,而它的行为是 - 吠叫、摇尾巴、奔跑。
如果你将软件对象与现实世界对象进行比较,它们具有非常相似的特性。
软件对象也具有状态和行为。软件对象的状态存储在字段中,而行为通过方法表现出来。
因此,在软件开发中,方法对对象的内部状态进行操作,并且对象到对象之间的通信是通过方法进行的。
Classes in Java
一个类是创建各个对象的蓝图。
下面是一个类的示例。
Example
public class Dog {
String breed;
int age;
String color;
void barking() {
}
void hungry() {
}
void sleeping() {
}
}
课程可以包含任何以下变量类型。
-
Local variables − 在方法、构造函数或块内定义的变量称为局部变量。变量将在方法内声明和初始化,并且在方法完成后销毁变量。
-
Instance variables − 实例变量是课程内的变量,但在任何方法的外部。这些变量在实例化课程时初始化。可以从特定课程的任何方法、构造函数或块内访问实例变量。
-
Class variables − 类变量是使用 static 关键字在课程内声明的变量,位于任何方法外部。
一个类可以具有任意数量的方法来访问各种类型的方法的值。在上面的示例中,barking()、hungry() 和 sleeping() 是方法。
在了解 Java 语言中类时,需要讨论以下一些重要主题。
Constructors
讨论类时,最重要的子主题之一是构造器。任何类都有一个构造器。如果我们没有明确地为类编写一个构造器,那么 Java 编译器将会为该类构建一个默认构造器。
每次创建一个新对象时,至少会调用一个构造器。构造器的主要规则是,它们应该与类同名。一个类可以有一个以上的构造器。
以下是一个构造器的示例:
Example
public class Puppy {
public Puppy() {
}
public Puppy(String name) {
// This constructor has one parameter, name.
}
}
Java 也支持 Singleton Classes ,这允许您仅创建一个类的实例。
Note - 我们有两种不同类型的构造器。我们将在后续章节中详细讨论构造器。
Creating an Object
正如之前提到的,一个类为对象提供蓝图。所以基本上,一个对象是从一个类创建的。在 Java 中,使用 new 关键字创建新对象。
从一个类创建对象时有三个步骤 −
-
Declaration − 使用对象类型和变量名称声明一个变量。
-
Instantiation − 使用“new”关键字创建对象。
-
Initialization − 在“new”关键字后面调用一个构造函数。此调用初始化新对象。
以下是如何创建一个对象的示例:
Example
在这个示例中,我们创建了一个名为 Puppy 的类。在 Puppy 类构造器中,要打印 puppy 的名字,这样当创建对象时,就会打印出它的名字。在 main 方法中,使用 new 操作符创建对象。
public class Puppy {
public Puppy(String name) {
// This constructor has one parameter, <i>name</i>.
System.out.println("Passed Name is :" + name );
}
public static void main(String []args) {
// Following statement would create an object myPuppy
Puppy myPuppy = new Puppy( "tommy" );
}
}
如果我们编译并运行以上程序,它将产生以下结果 −
Accessing Instance Variables and Methods
实例变量和方法可以通过创建对象来访问。要访问实例变量,以下是完全限定路径 −
/* First create an object */
ObjectReference = new Constructor();
/* Now call a variable as follows */
ObjectReference.variableName;
/* Now you can call a class method as follows */
ObjectReference.MethodName();
Example
在这个示例中,我们创建了一个名为 Puppy 的类。在 Puppy 类构造函数中,打印小狗的名字,以便在创建对象时打印其名字。添加了实例变量 puppyAge,并且可以使用 getter/setter 方法来处理年龄。在 main 方法中,使用 new 运算符创建一个对象。使用 setAge() 方法更新年龄,并且使用 getAge(),打印年龄。
public class Puppy {
int puppyAge;
public Puppy(String name) {
// This constructor has one parameter, <i>name</i>.
System.out.println("Name chosen is :" + name );
}
public void setAge( int age ) {
puppyAge = age;
}
public int getAge( ) {
System.out.println("Puppy's age is :" + puppyAge );
return puppyAge;
}
public static void main(String []args) {
/* Object creation */
Puppy myPuppy = new Puppy( "tommy" );
/* Call class method to set puppy's age */
myPuppy.setAge( 2 );
/* Call another class method to get puppy's age */
myPuppy.getAge( );
/* You can access instance variable as follows as well */
System.out.println("Variable Value :" + myPuppy.puppyAge );
}
}
如果我们编译并运行以上程序,它将产生以下结果 −
Source File Declaration Rules
作为本部分的最后一部分,现在让我们了解源文件声明规则。在源文件中声明类、导入语句和包语句时,这些规则至关重要。
-
每个源文件只能有一个 public 类。
-
一个源文件可以有多个非 public 类。
-
public 类名也应该是源文件的名字,而且后面应该附加 .java。例如,类名为 public class Employee{},则源文件也应该是 Employee.java。
-
如果类在包中定义,则包语句应该是源文件中的第一个语句。
-
如果存在 import 语句,则它们必须写在包语句和类声明之间。如果不存在包语句,则 import 语句应该是源文件中的第一行。
-
import 和包语句将适用于源文件中存在的所有类。不可能在源文件中对不同的类声明不同的 import 和/或包语句。
类有几个不同的访问级别,还有不同的类型,如抽象类、final 类等。我们将在访问修饰符章节中解释所有这些。
除了上述类型的类之外,Java 还有一些称为内部类和匿名类的特殊类。
Import Statements
在 Java 中,如果提供了完全限定名称(包括包和类名),那么编译器就可以轻松地找到源代码或类。导入语句是一种为编译器提供适当位置以找到特定类的方法。
例如,以下这行会要求编译器加载目录 java_installation/java/io 中可用的所有类:
import java.io.*;
A Simple Case Study
对于我们的案例研究,我们将创建两个类。它们是 Employee 和 EmployeeTest。
首先打开记事本并添加以下代码。请记住,这是 Employee 类,并且该类是一个公共类。现在,使用 Employee.java 的名称保存此源文件。
Employee 类有四个实例变量:name、age、designation 和 salary。该类有一个明确定义的构造函数,它接受一个参数。
Example
import java.io.*;
public class Employee {
String name;
int age;
String designation;
double salary;
// This is the constructor of the class Employee
public Employee(String name) {
this.name = name;
}
// Assign the age of the Employee to the variable age.
public void empAge(int empAge) {
age = empAge;
}
/* Assign the designation to the variable designation.*/
public void empDesignation(String empDesig) {
designation = empDesig;
}
/* Assign the salary to the variable salary.*/
public void empSalary(double empSalary) {
salary = empSalary;
}
/* Print the Employee details */
public void printEmployee() {
System.out.println("Name:"+ name );
System.out.println("Age:" + age );
System.out.println("Designation:" + designation );
System.out.println("Salary:" + salary);
}
}
正如本教程中前面提到的,处理从 main 方法开始。因此,为了让我们运行这个 Employee 类,应该有一个 main 方法并且应该创建对象。我们为这些任务创建一个单独的类。
以下是 EmployeeTest 类,它创建两个 Employee 类实例,并为每个对象调用方法来为每个变量分配值。
将以下代码保存在 EmployeeTest.java 文件中。
import java.io.*;
public class EmployeeTest {
public static void main(String args[]) {
/* Create two objects using constructor */
Employee empOne = new Employee("James Smith");
Employee empTwo = new Employee("Mary Anne");
// Invoking methods for each object created
empOne.empAge(26);
empOne.empDesignation("Senior Software Engineer");
empOne.empSalary(1000);
empOne.printEmployee();
empTwo.empAge(21);
empTwo.empDesignation("Software Engineer");
empTwo.empSalary(500);
empTwo.printEmployee();
}
}
现在,编译这两个类,然后运行 EmployeeTest 以查看结果,如下所示 −
Java - Methods
Java 方法是一组语句的集合,这些语句被分组在一起执行操作。例如,当你调用 System.out.println() 方法时,系统实际上执行了很多语句以便在控制台中显示一条消息。
现在,你将学习如何创建自己的方法,这些方法有返回值或没有返回值,调用有参数或没有参数的方法和在程序设计中应用方法抽象化。
Creating Method
考虑以下示例来解释一个方法的语法 −
Syntax
public static int methodName(int a, int b) {
// body
}
在此,
-
public static − modifier
-
int − return type
-
methodName - 方法的名称
-
a, b−形式参数
-
int a, int b−参数列表
方法定义包括方法头和方法体。在下述语法中显示了相同的内容:
Method Calling
要使用一个方法,应该调用它。有两种方式可以调用一个方法:方法返回一个值或不返回任何值(没有返回值)。
方法调用的过程很简单。当一个程序调用一个方法时,程序控制将转移到被调用的方法。这个被调用的方法然后在两个条件下将控制返回给调用者,当 −
-
执行了 return 语句。
-
它到达了方法结束大括号。
返回 void 的方法被视为调用语句。我们考虑一个示例−
System.out.println("This is tutorialspoint.com!");
返回值的方法可以由以下示例理解−
int result = sum(6, 9);
以下是如何定义一个方法以及如何调用它的示例−
Example
public class ExampleMinNumber {
public static void main(String[] args) {
int a = 11;
int b = 6;
int c = minFunction(a, b);
System.out.println("Minimum Value = " + c);
}
/** returns the minimum of two numbers */
public static int minFunction(int n1, int n2) {
int min;
if (n1 > n2)
min = n2;
else
min = n1;
return min;
}
}
The void Keyword
void 关键字允许我们创建不返回值的方法。在这里,在以下示例中,我们考虑了一个 void 方法 methodRankPoints。这个方法是一个 void 方法,不返回任何值。对 void 方法的调用必须是一个语句,即 methodRankPoints(255.7);。这是一个 Java 语句,以分号结尾,如下例所示。
Example
public class ExampleVoid {
public static void main(String[] args) {
methodRankPoints(255.7);
}
public static void methodRankPoints(double points) {
if (points >= 202.5) {
System.out.println("Rank:A1");
}else if (points >= 122.4) {
System.out.println("Rank:A2");
}else {
System.out.println("Rank:A3");
}
}
}
Passing Parameters by Value
在调用过程中,要传递参数。这些应该与方法规范中它们各自的参数处于相同的顺序。参数可以通过值或引用传递。
按值传递参数表示用一个参数调用一个方法。通过这种方式,参数值传递给参数。
Example
以下程序显示了一个按值传递参数的示例。即使在方法调用后,参数的值仍然保持不变。
public class swappingExample {
public static void main(String[] args) {
int a = 30;
int b = 45;
System.out.println("Before swapping, a = " + a + " and b = " + b);
// Invoke the swap method
swapFunction(a, b);
System.out.println("\n**Now, Before and After swapping values will be same here**:");
System.out.println("After swapping, a = " + a + " and b is " + b);
}
public static void swapFunction(int a, int b) {
System.out.println("Before swapping(Inside), a = " + a + " b = " + b);
// Swap n1 with n2
int c = a;
a = b;
b = c;
System.out.println("After swapping(Inside), a = " + a + " b = " + b);
}
}
Method Overloading
当一个类有两个或更多具有相同名称但不同参数的方法时,它被称为方法重载。它不同于重写。在重写中,一个方法具有相同的方法名、类型、参数数量等。
我们考虑前面讨论的用于查找最小整数类型数字的示例。如果我们希望找到最小双精度类型数字。然后将引入重载的概念来创建两个或更多具有相同名称但不同参数的方法。
以下示例对此进行了说明−
Example
public class ExampleOverloading {
public static void main(String[] args) {
int a = 11;
int b = 6;
double c = 7.3;
double d = 9.4;
int result1 = minFunction(a, b);
// same function name with different parameters
double result2 = minFunction(c, d);
System.out.println("Minimum Value = " + result1);
System.out.println("Minimum Value = " + result2);
}
// for integer
public static int minFunction(int n1, int n2) {
int min;
if (n1 > n2)
min = n2;
else
min = n1;
return min;
}
// for double
public static double minFunction(double n1, double n2) {
double min;
if (n1 > n2)
min = n2;
else
min = n1;
return min;
}
}
Using Command-Line Arguments
有时,在运行程序时,您将需要向程序传递一些信息。这可以通过将命令行参数传递给 main( ) 来实现。
当执行程序时,紧跟程序名称后的信息就是命令行参数。在 Java 程序中访问命令行参数非常容易。它们被存储为传递给 main( ) 的 String 数组中的字符串。
The this keyword
this 是 Java 中的一个关键字,用作当前类对象的引用,它存在于实例方法或构造函数中。通过使用此关键字,您可以引用类的成员,例如构造函数、变量和方法。
Note− 关键字 this 只用在实例方法或构造函数内
一般来说,关键字 this 用于 −
-
在构造函数或方法中,区分具有相同名称的实例变量和局部变量。
class Student {
int age;
Student(int age) {
this.age = age;
}
}
-
在类中,从另一个类中调用一种类型的构造函数(带参数构造函数或默认构造函数)。它称为显式构造函数调用。
class Student {
int age
Student() {
this(20);
}
Student(int age) {
this.age = age;
}
}
Example
以下是一个使用此关键字访问类成员的示例。将以下程序复制并粘贴到名为 This_Example.java 的文件中。
public class This_Example {
// Instance variable num
int num = 10;
This_Example() {
System.out.println("This is an example program on keyword this");
}
This_Example(int num) {
// Invoking the default constructor
this();
// Assigning the local variable <i>num</i> to the instance variable <i>num</i>
this.num = num;
}
public void greet() {
System.out.println("Hi Welcome to Tutorialspoint");
}
public void print() {
// Local variable num
int num = 20;
// Printing the local variable
System.out.println("value of local variable num is : "+num);
// Printing the instance variable
System.out.println("value of instance variable num is : "+this.num);
// Invoking the greet method of a class
this.greet();
}
public static void main(String[] args) {
// Instantiating the class
This_Example obj1 = new This_Example();
// Invoking the print method
obj1.print();
// Passing a new value to the num variable through parametrized constructor
This_Example obj2 = new This_Example(30);
// Invoking the print method again
obj2.print();
}
}
Output
This is an example program on keyword this
value of local variable num is : 20
value of instance variable num is : 10
Hi Welcome to Tutorialspoint
This is an example program on keyword this
value of local variable num is : 20
value of instance variable num is : 30
Hi Welcome to Tutorialspoint
Variable Arguments(var-args)
JDK 1.5 允许您向方法传递可变数量的同类型参数。方法中的参数声明如下 -
typeName... parameterName
在方法声明中,您指定类型,后跟省略号 (…)。一个方法中只能指定一个可变长度参数,该参数必须是最后一个参数。任何常规参数都必须在它之前。
Example
public class VarargsDemo {
public static void main(String args[]) {
// Call method with variable args
printMax(34, 3, 3, 2, 56.5);
printMax(new double[]{1, 2, 3});
}
public static void printMax( double... numbers) {
if (numbers.length == 0) {
System.out.println("No argument passed");
return;
}
double result = numbers[0];
for (int i = 1; i < numbers.length; i++)
if (numbers[i] > result)
result = numbers[i];
System.out.println("The max value is " + result);
}
}
The finalize( ) Method
可以定义一种方法,在垃圾收集器对某个对象进行最终销毁之前,将会调用该方法。此方法称为 finalize( ),可用于确保对象干净地终止。
例如,您可以使用 finalize( ) 确保由该对象拥有的打开文件已关闭。
要为类添加一个终结器,只需定义 finalize( ) 方法。每当 Java 运行时准备回收该类的对象时,就会调用该方法。
在 finalize( ) 方法中,您将指定在销毁对象之前必须执行的操作。
finalize( ) 方法具有以下常规形式 −
protected void finalize( ) {
// finalization code here
}
此处,关键字 protected 是一个说明符,该说明符阻止定义在其类外部的代码访问 finalize( )。
这意味着你无法知道finalize()何时或是否会被执行。例如,如果你的程序在垃圾收集发生之前结束,则finalize()将不会执行。
Java - Constructors
构造函数在创建对象时对该对象进行初始化。它具有与类相同的名称,并且在语法上类似于方法。但是,构造函数没有显式返回类型。
通常,你将使用构造函数为类定义的实例变量赋值,或者执行创建完全成型对象所需的任何其他启动过程。
无论你是否定义了一个构造函数,所有类都具有构造函数,因为 Java 会自动提供一个默认构造函数,将所有成员变量初始化为 0。但是,一旦定义了自己的构造函数,将不再使用默认构造函数。
Syntax
以下是构造函数的语法 −
class ClassName {
ClassName() {
}
}
Java 允许使用两种类型的构造函数,分别是 −
-
No argument Constructors
-
Parameterized Constructors
No argument Constructors
顾名思义,Java 的无参构造函数不接受任何参数,而是使用这些构造函数,将某个方法的实例变量使用固定值初始化为所有对象。
Parameterized Constructors
大多数情况下,您需要接受一个或多个参数的构造函数。将参数添加到构造函数中的方式与将它们添加到方法中的方式相同,只需在构造函数名称后的括号内声明它们即可。
Example
这里有一个使用构造函数的简单示例 −
// A simple constructor.
class MyClass {
int x;
// Following is the constructor
MyClass(int i ) {
x = i;
}
}
您可以按如下方式调用构造函数来初始化对象 −
public class ConsDemo {
public static void main(String args[]) {
MyClass t1 = new MyClass( 10 );
MyClass t2 = new MyClass( 20 );
System.out.println(t1.x + " " + t2.x);
}
}
Java - Access Modifiers
Java 提供了许多访问修饰符,用于为类、变量、方法和构造函数设置访问级别。四个访问级别是 −
-
对包可见,这是默认值。无需任何修饰词。
-
仅对类可见(私有)。
-
对整个世界可见(公共)。
-
对包和所有子类可见(受保护)。
Default Access Modifier - No Keyword
默认访问修饰符表示我们没有显式为类、字段、方法等声明访问修饰符。
在没有访问控制修饰符的情况下声明的变量或方法供相同包中的任何其他类使用。接口中的字段隐式为 public static final,接口中的方法默认情况下为 public。
Private Access Modifier - Private
仅在声明的类内部可以访问声明为 private 的方法、变量和构造函数。
私有访问修饰符是最严格的访问级别。类和接口不能为私有的。
如果类中有 public getter 方法,可以在类的外部访问声明为 private 的变量。
使用 private 修饰符是对象封装自身和隐藏来自外部世界数据的常用方法。
Example
以下类使用私有访问控制:
public class Logger {
private String format;
public String getFormat() {
return this.format;
}
public void setFormat(String format) {
this.format = format;
}
}
此处,Logger 类的 format 变量为私有的,因此其他类无法直接获取或设置其值。
所以,为了使此变量可供外部访问,我们定义了两个公共方法:getFormat(),它返回格式的值;和 setFormat(String),它设置其值。
Public Access Modifier - Public
声明为公有的类、方法、构造函数、接口等可从任何其他类访问。因此,可以在属于 Java Universe 的任何类中访问在公共类内部声明的字段、方法和块。
但是,如果我们尝试访问的公共类位于不同的包中,则仍然需要导入公共类。由于类继承,超类的所有公共方法和变量都会被其子类继承。
Protected Access Modifier - Protected
变量、方法和构造函数在超类中声明为 protected,只能由其他包中的子类或包含受保护成员类包中的任何类来访问。
不能将 protected 访问修饰符应用于类和接口。可以将方法、字段声明为 protected,但是不能将接口中的方法和字段声明为 protected。
Protected 访问为子类提供使用助手方法或变量的机会,同时防止不相关的类尝试使用它。
Example
以下父类使用 protected 访问控制,允许其子类重写 openSpeaker() 方法 −
class AudioPlayer {
protected boolean openSpeaker(Speaker sp) {
// implementation details
}
}
class StreamingAudioPlayer extends AudioPlayer {
boolean openSpeaker(Speaker sp) {
// implementation details
}
}
在这里,如果我们定义 openSpeaker() 方法为私有的,那么除 AudioPlayer 之外,任何其他类都不能访问它。如果我们将其定义为公有的,那么所有的外部世界都可以访问它。但我们的目的是仅将其方法公开给其子类,这就是我们使用 protected 修饰符的原因。
Access Control and Inheritance
强制执行以下继承方法规则 −
-
在超类中声明为 public 的方法也必须在所有子类中声明为 public。
-
在超类中声明为 protected 的方法在子类中必须声明为 protected 或 public;它们不能为 private。
-
声明为 private 的方法根本不会被继承,因此对它们没有规则。
Example
在此示例中,我们使用 private 变量 age 创建了一个类,并使用 name 作为默认作用域创建了一个变量。使用 setter/getter 方法,我们更新 age 并获取值,并直接更新 name。
public class Puppy {
private int age;
String name;
public Puppy() {
}
public void setAge( int age ) {
this.age = age;
}
public int getAge( ) {
return age;
}
public static void main(String []args) {
Puppy myPuppy = new Puppy();
// update age variable using method call
myPuppy.setAge( 2 );
// update name directly
myPuppy.name = "Tommy";
System.out.println("Age: " + myPuppy.getAge() +", name: " + myPuppy.name );
}
}
Output
Age: 2, name: Tommy
Java - Inheritance
继承可以定义为一个类获取另一个类的属性(方法和字段)的过程。通过使用继承,可以以层次结构的方式管理信息。
继承其他类属性的类称为子类(派生类、子类),而其属性被继承的类称为超类(基类、父类)。
Sample Code
以下是展示 Java 继承的示例。在此示例中,你可以观察到两个类,即 Calculation 和 My_Calculation。
通过使用 extends 关键字,My_Calculation 从 Calculation 类继承 addition() 和 Subtraction() 方法。
将以下程序复制并粘贴到一个名为 My_Calculation.java 的文件中:
Example
class Calculation {
int z;
public void addition(int x, int y) {
z = x + y;
System.out.println("The sum of the given numbers:"+z);
}
public void Subtraction(int x, int y) {
z = x - y;
System.out.println("The difference between the given numbers:"+z);
}
}
public class My_Calculation extends Calculation {
public void multiplication(int x, int y) {
z = x * y;
System.out.println("The product of the given numbers:"+z);
}
public static void main(String args[]) {
int a = 20, b = 10;
My_Calculation demo = new My_Calculation();
demo.addition(a, b);
demo.Subtraction(a, b);
demo.multiplication(a, b);
}
}
按如下所示编译并执行上述代码。
javac My_Calculation.java
java My_Calculation
执行程序后,它将生成以下结果 -
Output
The sum of the given numbers:30
The difference between the given numbers:10
The product of the given numbers:200
在给定的程序中,当创建 My_Calculation 类的对象时,超类的内容的副本在其内部生成。这就是为什么,使用子类的对象可以访问超类的成员的原因。
超类引用变量可以保存子类对象,但是仅使用该变量,你可以访问超类的成员,因此为了访问两个类的成员,建议始终为子类创建引用变量。
如果你考虑上述程序,你可以按如下所示实例化类。但是使用超类引用变量(在这种情况下为 cal),你不能调用属于子类 My_Calculation 的方法 multiplication()。
Calculation demo = new My_Calculation();
demo.addition(a, b);
demo.Subtraction(a, b);
Note - 子类会从超类中继承所有成员(字段、方法和嵌套类)。构造函数不是成员,因此不会被子类继承,但可以从子类调用超类的构造函数。
The super keyword
super 关键字类似于 this 关键字。以下是使用 super 关键字的场景。
-
如果超类和子类的成员具有相同名称,则用于从子类的成员中访问 differentiate the members 超类。
-
用于从子类中调用 invoke the superclass 构造函数。
Differentiating the Members
如果一个类正在继承另一个类的属性。并且如果超类的成员与子类的名称相同,则使用 super 关键字区分这些变量,如下所示。
super.variable
super.method();
Sample Code
本节提供了一个演示了 super 关键字用法的程序。
在给定的程序中,你有两个类,即 Sub_class 和 Super_class,它们都具有一个名为 display() 的方法(具有不同的实现)以及一个具有不同值的变量名为 num。我们正在调用两个类的 display() 方法并打印两个类的 num 变量的值。此处你可以观察到,我们使用了 super 关键字以将超类的成员与子类区分开来。
将程序复制并粘贴到名为 Sub_class.java 的文件中。
Example
class Super_class {
int num = 20;
// display method of superclass
public void display() {
System.out.println("This is the display method of superclass");
}
}
public class Sub_class extends Super_class {
int num = 10;
// display method of sub class
public void display() {
System.out.println("This is the display method of subclass");
}
public void my_method() {
// Instantiating subclass
Sub_class sub = new Sub_class();
// Invoking the display() method of sub class
sub.display();
// Invoking the display() method of superclass
super.display();
// printing the value of variable num of subclass
System.out.println("value of the variable named num in sub class:"+ sub.num);
// printing the value of variable num of superclass
System.out.println("value of the variable named num in super class:"+ super.num);
}
public static void main(String args[]) {
Sub_class obj = new Sub_class();
obj.my_method();
}
}
使用以下语法编译并执行上述代码。
javac Super_Demo
java Super
执行程序后,你将获得以下结果 -
Output
This is the display method of subclass
This is the display method of superclass
value of the variable named num in sub class:10
value of the variable named num in super class:20
Invoking Superclass Constructor
如果一个类正在继承另一个类的属性,则子类会自动获得超类的默认构造函数。但是如果你希望调用超类的参数化构造函数,则需要使用 super 关键字,如下所示。
super(values);
Sample Code
本节中给出的程序演示了如何使用 super 关键字调用超类的参数化构造函数。该程序包含一个超类和一个子类,其中超类包含一个接受整数值的参数化构造函数,且我们使用 super 关键字调用超类的参数化构造函数。
将以下程序复制并粘贴到名为 Subclass.java 的文件中
Example
class Superclass {
int age;
Superclass(int age) {
this.age = age;
}
public void getAge() {
System.out.println("The value of the variable named age in super class is: " +age);
}
}
public class Subclass extends Superclass {
Subclass(int age) {
super(age);
}
public static void main(String args[]) {
Subclass s = new Subclass(24);
s.getAge();
}
}
使用以下语法编译并执行上述代码。
javac Subclass
java Subclass
Output
The value of the variable named age in super class is: 24
IS-A Relationship
IS-A 是一种表示方式:此对象是该对象的一种类型。让我们看看 extends 关键字如何用于实现继承。
public class Animal {
}
public class Mammal extends Animal {
}
public class Reptile extends Animal {
}
public class Dog extends Mammal {
}
现在,根据上述示例,按面向对象术语,以下为真 -
-
动物是哺乳动物类的超类。
-
动物是爬行动物类的超类。
-
哺乳动物和爬行动物是动物类的子类。
-
狗既是哺乳动物类的子类,也是动物类的子类。
现在,如果我们考虑 IS-A 关系,我们可以说 −
-
Mammal IS-A Animal
-
Reptile IS-A Animal
-
Dog IS-A Mammal
-
因此:狗也属于动物
通过使用 extends 关键字,子类将能够继承超类的所有属性,除了超类的私有属性。
我们可以使用 instance 运算符来确保哺乳动物实际上是动物。
Example
class Animal {
}
class Mammal extends Animal {
}
class Reptile extends Animal {
}
public class Dog extends Mammal {
public static void main(String args[]) {
Animal a = new Animal();
Mammal m = new Mammal();
Dog d = new Dog();
System.out.println(m instanceof Animal);
System.out.println(d instanceof Mammal);
System.out.println(d instanceof Animal);
}
}
The instanceof Keyword
让我们使用 instanceof 运算符来检查确定哺乳动物是否实际上是动物,狗是否实际上是动物。
Example
interface Animal{}
class Mammal implements Animal{}
public class Dog extends Mammal {
public static void main(String args[]) {
Mammal m = new Mammal();
Dog d = new Dog();
System.out.println(m instanceof Animal);
System.out.println(d instanceof Mammal);
System.out.println(d instanceof Animal);
}
}
HAS-A relationship
这些关系主要基于使用情况。这确定了某个类是否 HAS-A 某些事情。这种关系有助于减少代码冗余和错误。
让我们看一个例子 −
Example
public class Vehicle{}
public class Speed{}
public class Van extends Vehicle {
private Speed sp;
}
这表明类 Van HAS-A Speed。通过为 Speed 单独创建一个类,我们不必将属于 speed 的所有代码都放入 Van 类中,这使得可以在多个应用程序中重用 Speed 类。
在面向对象特性中,用户不需要操心由哪个对象来完成实际工作。要实现此点,Van 类对 Van 类用户隐藏实现细节。因此,基本上发生的事情是用户会要求 Van 类执行某个动作,而 Van 类会自己完成工作或要求另一个类执行此动作。
Java - Polymorphism
多态性是指对象具有多种形式的能力。OOP 中最常见的多态性用途是当父类引用用于引用子类对象时。
任何可以传递多个 IS-A 测试的 Java 对象都被认为是多态的。在 Java 中,所有 Java 对象都是多态的,因为任何对象都将通过它们自己的类型和类对象的 IS-A 测试。
重要的是要知道访问对象的唯一可能方法是通过引用变量。引用变量只能是一种类型。声明后,引用变量的类型不能更改。
引用变量可以重新分配给其他对象,前提是它未被声明为 final。引用变量的类型将确定它可以在对象上调用哪些方法。
引用变量可以引用其声明类型的任何对象或其声明类型的任何子类型。引用变量可以被声明为类或接口类型。
Example
我们来看一个示例。
public interface Vegetarian{}
public class Animal{}
public class Deer extends Animal implements Vegetarian{}
现在,Deer 类被认为是多态的,因为它具有多重继承。以下是上述示例的 true 内容 −
-
A Deer IS-A Animal
-
A Deer IS-A Vegetarian
-
A Deer IS-A Deer
-
A Deer IS-A Object
当我们将引用变量事实应用于 Deer 对象引用时,以下声明是合法的 −
Example
Deer d = new Deer();
Animal a = d;
Vegetarian v = d;
Object o = d;
所有引用变量 d、a、v、o 都引用堆中同一个 Deer 对象。
Example
在此示例中,我们通过创建 Deer 对象并将相同的对象分配给超类或已实现接口的引用来展示上述概念。
interface Vegetarian{}
class Animal{}
public class Deer extends Animal implements Vegetarian{
public static void main(String[] args) {
Deer d = new Deer();
Animal a = d;
Vegetarian v = d;
Object o = d;
System.out.println(d instanceof Deer);
System.out.println(a instanceof Deer);
System.out.println(v instanceof Deer);
System.out.println(o instanceof Deer);
}
}
Virtual Methods
在本节中,我将向你展示 Java 中重写方法的行为如何允许你在设计类时利用多态性。
我们已经讨论了方法覆盖,子类可以在父类中覆盖方法。覆盖方法实质上隐藏在父类中,并且不会被调用,除非子类在覆盖方法中使用 super 关键字。
Example
/* File name : Employee.java */
public class Employee {
private String name;
private String address;
private int number;
public Employee(String name, String address, int number) {
System.out.println("Constructing an Employee");
this.name = name;
this.address = address;
this.number = number;
}
public void mailCheck() {
System.out.println("Mailing a check to " + this.name + " " + this.address);
}
public String toString() {
return name + " " + address + " " + number;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
public void setAddress(String newAddress) {
address = newAddress;
}
public int getNumber() {
return number;
}
}
现在,假设我们以如下方式扩展 Employee 类 -
/* File name : Salary.java */
public class Salary extends Employee {
private double salary; // Annual salary
public Salary(String name, String address, int number, double salary) {
super(name, address, number);
setSalary(salary);
}
public void mailCheck() {
System.out.println("Within mailCheck of Salary class ");
System.out.println("Mailing check to " + getName()
+ " with salary " + salary);
}
public double getSalary() {
return salary;
}
public void setSalary(double newSalary) {
if(newSalary >= 0.0) {
salary = newSalary;
}
}
public double computePay() {
System.out.println("Computing salary pay for " + getName());
return salary/52;
}
}
现在,仔细研究以下程序,并尝试确定其输出 -
/* File name : VirtualDemo.java */
public class VirtualDemo {
public static void main(String [] args) {
Salary s = new Salary("Mohd Mohtashim", "Ambehta, UP", 3, 3600.00);
Employee e = new Salary("John Adams", "Boston, MA", 2, 2400.00);
System.out.println("Call mailCheck using Salary reference --");
s.mailCheck();
System.out.println("\n Call mailCheck using Employee reference--");
e.mailCheck();
}
}
class Employee {
private String name;
private String address;
private int number;
public Employee(String name, String address, int number) {
System.out.println("Constructing an Employee");
this.name = name;
this.address = address;
this.number = number;
}
public void mailCheck() {
System.out.println("Mailing a check to " + this.name + " " + this.address);
}
public String toString() {
return name + " " + address + " " + number;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
public void setAddress(String newAddress) {
address = newAddress;
}
public int getNumber() {
return number;
}
}
class Salary extends Employee {
private double salary; // Annual salary
public Salary(String name, String address, int number, double salary) {
super(name, address, number);
setSalary(salary);
}
public void mailCheck() {
System.out.println("Within mailCheck of Salary class ");
System.out.println("Mailing check to " + getName()
+ " with salary " + salary);
}
public double getSalary() {
return salary;
}
public void setSalary(double newSalary) {
if(newSalary >= 0.0) {
salary = newSalary;
}
}
public double computePay() {
System.out.println("Computing salary pay for " + getName());
return salary/52;
}
}
Output
Constructing an Employee
Constructing an Employee
Call mailCheck using Salary reference --
Within mailCheck of Salary class
Mailing check to Mohd Mohtashim with salary 3600.0
Call mailCheck using Employee reference--
Within mailCheck of Salary class
Mailing check to John Adams with salary 2400.0
在这里,我们实例化两个 Salary 对象。一个使用 Salary 引用 s,另一个使用 Employee 引用 e。
在调用 s.mailCheck() 时,编译器在编译时在 Salary 类中看到 mailCheck(),而 JVM 在运行时在 Salary 类中调用 mailCheck()。
e 上的 mailCheck() 有很大不同,因为 e 是 Employee 引用。当编译器看到 e.mailCheck() 时,编译器在 Employee 类中看到 mailCheck() 方法。
在这里,在编译时,编译器在 Employee 中使用 mailCheck() 验证此语句。然而,在运行时,JVM 在 Salary 类中调用 mailCheck()。
此行为称为虚拟方法调用,并且这些方法称为虚拟方法。无论编译时在源代码中使用的引用是什么数据类型,覆盖方法都在运行时被调用。
Java - Overriding
在上一章中,我们讨论了超类和子类。如果一个类从其超类继承了一个方法,那么就有机会覆盖该方法,前提是它没有被标记为 final。
重写的优点是:能够定义特定于子类类型的一种行为,这意味着子类可以根据其需求实现父类方法。
以面向对象的方式来说,重写是指覆盖现有方法的功能。
Example
我们来看一个示例。
class Animal {
public void move() {
System.out.println("Animals can move");
}
}
class Dog extends Animal {
public void move() {
System.out.println("Dogs can walk and run");
}
}
public class TestDog {
public static void main(String args[]) {
Animal a = new Animal(); // Animal reference and object
Animal b = new Dog(); // Animal reference but Dog object
a.move(); // runs the method in Animal class
b.move(); // runs the method in Dog class
}
}
Output
Animals can move
Dogs can walk and run
在上面的示例中,尽管 b 是 Animal 的一种类型,但它运行 Dog 类中的 move 方法。这样做的原因是:在编译时,针对引用类型进行检查。然而,在运行时,JVM 找出对象类型,并运行属于该特定对象的该方法。
因此,在上面的示例中,程序将正确编译,因为 Animal 类具有 move 方法。然后,在运行时,运行特定于该对象的方法。
考虑以下示例 −
Example
class Animal {
public void move() {
System.out.println("Animals can move");
}
}
class Dog extends Animal {
public void move() {
System.out.println("Dogs can walk and run");
}
public void bark() {
System.out.println("Dogs can bark");
}
}
public class TestDog {
public static void main(String args[]) {
Animal a = new Animal(); // Animal reference and object
Animal b = new Dog(); // Animal reference but Dog object
a.move(); // runs the method in Animal class
b.move(); // runs the method in Dog class
b.bark();
}
}
Output
TestDog.java:26: error: cannot find symbol
b.bark();
^
symbol: method bark()
location: variable b of type Animal
1 error
此程序将抛出编译时错误,因为 b 的引用类型 Animal 没有任何名为 bark 的方法。
Rules for Method Overriding
-
参数列表应与被覆盖方法的参数列表完全相同。
-
返回类型应相同,或为超类中原始重写方法中声明的返回类型的子类型。
-
访问级别不能比被覆盖方法的访问级别更严格。例如:如果超类方法声明为 public,则子类中的覆盖方法不能是 private 或 protected。
-
只有当实例方法被子类继承时,才能覆盖它们。
-
声明为 final 的方法不能被覆盖。
-
声明为 static 的方法不能被覆盖,但可以重新声明。
-
如果一个方法不能被继承,那么它就不能被覆盖。
-
与实例超类的包相同的子类可以覆盖未声明为 private 或 final 的任何超类方法。
-
不同包中的子类只能覆盖声明为 public 或 protected 的非 final 方法。
-
覆盖方法可以抛出任何未选中异常,无论被覆盖方法是否抛出异常。但是,覆盖方法不应抛出比被覆盖方法声明的异常更新的或更广泛的已选中异常。覆盖方法可以抛出比被覆盖方法更窄或更少异常。
-
Constructors cannot be overridden.
Using the super Keyword
当调用一个被覆盖方法的超类版本时,使用 super 关键字。
Example
class Animal {
public void move() {
System.out.println("Animals can move");
}
}
class Dog extends Animal {
public void move() {
super.move(); // invokes the super class method
System.out.println("Dogs can walk and run");
}
}
public class TestDog {
public static void main(String args[]) {
Animal b = new Dog(); // Animal reference but Dog object
b.move(); // runs the method in Dog class
}
}
Java - Abstraction
如同字典所述,abstraction 是处理思想而非事件的质量。例如,在考虑电子邮件时,一些复杂的细节会对用户隐藏,例如发送电子邮件会发生什么、电子邮件服务器使用什么协议。因此,要发送电子邮件,您只需要键入内容、指出收件人的地址并单击发送。
同样,在面向对象编程中,抽象是向用户隐藏实现细节的过程,只向用户提供功能。换句话说,用户将获得对象做什么的信息,而不是它如何做。
在 Java 中,抽象是使用抽象类和接口实现的。
Abstract Class
在声明中包含 abstract 关键字的类称为抽象类。
-
抽象类可以包含或不包含抽象方法,即没有主体的方法(public void get();)
-
但是,如果一个类至少有一个抽象方法,那么该类 must 被声明为抽象类。
-
如果一个类被声明为抽象类,那么它就不能被实例化。
-
要使用抽象类,您必须从另一个类继承它,为其中的抽象方法提供实现。
-
如果您继承一个抽象类,您必须为其中的所有抽象方法提供实现。
Example
本节为您提供抽象类的示例。要创建一个抽象类,只需在类声明中,类关键字之前使用 abstract 关键字。
/* File name : Employee.java */
public abstract class Employee {
private String name;
private String address;
private int number;
public Employee(String name, String address, int number) {
System.out.println("Constructing an Employee");
this.name = name;
this.address = address;
this.number = number;
}
public double computePay() {
System.out.println("Inside Employee computePay");
return 0.0;
}
public void mailCheck() {
System.out.println("Mailing a check to " + this.name + " " + this.address);
}
public String toString() {
return name + " " + address + " " + number;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
public void setAddress(String newAddress) {
address = newAddress;
}
public int getNumber() {
return number;
}
}
您可以观察到,除了抽象方法外,Employee 类与 Java 中的普通类是相同的。该类现在是抽象类,但它仍然有三个字段、七个方法和一个构造器。
现在您可以尝试使用以下方法实例化 Employee 类 −
/* File name : AbstractDemo.java */
public class AbstractDemo {
public static void main(String [] args) {
/* Following is not allowed and would raise error */
Employee e = new Employee("George W.", "Houston, TX", 43);
System.out.println("\n Call mailCheck using Employee reference--");
e.mailCheck();
}
}
abstract class Employee {
private String name;
private String address;
private int number;
public Employee(String name, String address, int number) {
System.out.println("Constructing an Employee");
this.name = name;
this.address = address;
this.number = number;
}
public double computePay() {
System.out.println("Inside Employee computePay");
return 0.0;
}
public void mailCheck() {
System.out.println("Mailing a check to " + this.name + " " + this.address);
}
public String toString() {
return name + " " + address + " " + number;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
public void setAddress(String newAddress) {
address = newAddress;
}
public int getNumber() {
return number;
}
}
当您编译上述类时,它会向您显示以下错误 −
Employee.java:46: Employee is abstract; cannot be instantiated
Employee e = new Employee("George W.", "Houston, TX", 43);
^
1 error
Inheriting the Abstract Class
我们可以按照具体类中一样继承 Employee 类的属性,按照以下方法操作 −
Example
/* File name : Salary.java */
public class Salary extends Employee {
private double salary; // Annual salary
public Salary(String name, String address, int number, double salary) {
super(name, address, number);
setSalary(salary);
}
public void mailCheck() {
System.out.println("Within mailCheck of Salary class ");
System.out.println("Mailing check to " + getName() + " with salary " + salary);
}
public double getSalary() {
return salary;
}
public void setSalary(double newSalary) {
if(newSalary >= 0.0) {
salary = newSalary;
}
}
public double computePay() {
System.out.println("Computing salary pay for " + getName());
return salary/52;
}
}
此处,您不能实例化 Employee 类,但您可以实例化 Salary 类,并使用该实例访问 Employee 类中的所有三个字段和七个方法,如下所示。
/* File name : AbstractDemo.java */
public class AbstractDemo {
public static void main(String [] args) {
Salary s = new Salary("Mohd Mohtashim", "Ambehta, UP", 3, 3600.00);
Employee e = new Salary("John Adams", "Boston, MA", 2, 2400.00);
System.out.println("Call mailCheck using Salary reference --");
s.mailCheck();
System.out.println("\n Call mailCheck using Employee reference--");
e.mailCheck();
}
}
abstract class Employee {
private String name;
private String address;
private int number;
public Employee(String name, String address, int number) {
System.out.println("Constructing an Employee");
this.name = name;
this.address = address;
this.number = number;
}
public double computePay() {
System.out.println("Inside Employee computePay");
return 0.0;
}
public void mailCheck() {
System.out.println("Mailing a check to " + this.name + " " + this.address);
}
public String toString() {
return name + " " + address + " " + number;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
public void setAddress(String newAddress) {
address = newAddress;
}
public int getNumber() {
return number;
}
}
class Salary extends Employee {
private double salary; // Annual salary
public Salary(String name, String address, int number, double salary) {
super(name, address, number);
setSalary(salary);
}
public void mailCheck() {
System.out.println("Within mailCheck of Salary class ");
System.out.println("Mailing check to " + getName() + " with salary " + salary);
}
public double getSalary() {
return salary;
}
public void setSalary(double newSalary) {
if(newSalary >= 0.0) {
salary = newSalary;
}
}
public double computePay() {
System.out.println("Computing salary pay for " + getName());
return salary/52;
}
}
Output
Constructing an Employee
Constructing an Employee
Call mailCheck using Salary reference --
Within mailCheck of Salary class
Mailing check to Mohd Mohtashim with salary 3600.0
Call mailCheck using Employee reference--
Within mailCheck of Salary class
Mailing check to John Adams with salary 2400.0
Abstract Methods
如果希望一个类包含一个特定方法,但希望该方法的实际实现由子类确定,则可以在父类中将该方法声明为抽象。
-
abstract 关键字用于将方法声明为抽象方法。
-
您必须在方法声明的方法名称之前放置 abstract 关键字。
-
抽象方法包含方法签名,但没有方法体。
-
抽象方法的结尾处会用分号 (;) 而不是大括号。
以下是抽象方法示例。
Example
public abstract class Employee {
private String name;
private String address;
private int number;
public abstract double computePay();
// Remainder of class definition
}
将方法声明为抽象方法会产生两个后果 −
-
包含它的类必须声明为抽象类。
-
继承当前类的任何类都必须覆盖抽象方法或将自身声明为抽象。
− 最终,派生类必须实现抽象方法;否则,您将拥有一个无法实例化的抽象类的层次结构。
假设 Salary 类继承 Employee 类,那么它应该实现以下所示的 computePay() 方法:
/* File name : Salary.java */
public class Salary extends Employee {
private double salary; // Annual salary
public double computePay() {
System.out.println("Computing salary pay for " + getName());
return salary/52;
}
// Remainder of class definition
}
Example
以下示例展示了抽象方法的概念。
/* File name : AbstractDemo.java */
public class AbstractDemo {
public static void main(String [] args) {
Salary s = new Salary("Mohd Mohtashim", "Ambehta, UP", 3, 3600.00);
System.out.println("salary: " + s.computePay());
}
}
abstract class Employee {
private String name;
private String address;
private int number;
public Employee(String name, String address, int number) {
System.out.println("Constructing an Employee");
this.name = name;
this.address = address;
this.number = number;
}
public abstract double computePay();
// Remainder of class definition
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
class Salary extends Employee {
private double salary; // Annual salary
public Salary(String name, String address, int number, double salary) {
super(name, address, number);
this.salary = salary;
}
public double computePay() {
System.out.println("Computing salary pay for " + getName());
return salary/52;
}
// Remainder of class definition
}
Java - Encapsulation
Encapsulation 是面向对象编程的四个基本概念之一。其他三个是继承、多态和抽象。
Java 中的封装是一种将数据(变量)和作用于数据(方法)的代码作为一个个元包装起来的机制。在封装中,类的变量将对其他类隐藏,只能通过它们当前类的方法进行访问。因此,它也被称为 data hiding。
若要在 Java 中实现封装:
-
将类的变量声明为私有。
-
提供公有设置器和取值器方法,以修改和查看变量值。
Example
以下示例演示如何在 Java 中实现封装:
/* File name : EncapTest.java */
public class EncapTest {
private String name;
private String idNum;
private int age;
public int getAge() {
return age;
}
public String getName() {
return name;
}
public String getIdNum() {
return idNum;
}
public void setAge( int newAge) {
age = newAge;
}
public void setName(String newName) {
name = newName;
}
public void setIdNum( String newId) {
idNum = newId;
}
}
公有 setXXX() 和 getXXX() 方法是 EncapTest 类实例变量的访问点。通常,这些方法称为取值器和设置器。因此,要访问变量的任何类都应该通过这些取值器和设置器进行访问。
可以使用以下程序访问 EncapTest 类的变量:
/* File name : RunEncap.java */
public class RunEncap {
public static void main(String args[]) {
EncapTest encap = new EncapTest();
encap.setName("James");
encap.setAge(20);
encap.setIdNum("12343ms");
System.out.print("Name : " + encap.getName() + " Age : " + encap.getAge());
}
}
public class EncapTest {
private String name;
private String idNum;
private int age;
public int getAge() {
return age;
}
public String getName() {
return name;
}
public String getIdNum() {
return idNum;
}
public void setAge( int newAge) {
age = newAge;
}
public void setName(String newName) {
name = newName;
}
public void setIdNum( String newId) {
idNum = newId;
}
}
Java - Interfaces
接口是 Java 中的引用类型。它类似于类。它是抽象方法的一个集合。类通过继承接口的抽象方法,实现一个接口。
除了抽象方法,一个接口还可能包含常量、默认方法、静态方法和嵌套类型。方法体只存在于默认方法和静态方法。
撰写接口类似于撰写类。但是,一个类描述了一个对象的属性和行为。而一个接口包含一个类实现的行为。
除非实现接口的类是抽象的,否则所有接口的方法都需要在类中定义。
接口在以下方面类似于类 -
-
接口可以包含任意数量的方法。
-
接口是用 .java 扩展名写入文件的,接口的名称与文件的名称相匹配。
-
接口的字节码出现在 .class 文件中。
-
接口出现在包中,其对应的字节码文件必须位于与包名称相匹配的目录结构中。
然而,接口在以下几个方面与类不同,包括 -
-
您不能实例化接口。
-
接口不包含任何构造函数。
-
接口中的所有方法都是抽象方法。
-
接口不能包含实例字段。接口中出现的唯一字段必须同时声明为 static 和 final。
-
接口不由类扩展;它由类实现。
-
接口可以扩展多个接口。
Declaring Interfaces
interface 关键字用于声明接口。以下是声明接口的一个简单示例 −
Example
以下是接口示例 −
/* File name : NameOfInterface.java */
import java.lang.*;
// Any number of import statements
public interface NameOfInterface {
// Any number of final, static fields
// Any number of abstract method declarations\
}
接口具有以下属性 −
-
接口是隐式抽象的。声明接口时无需使用 abstract 关键字。
-
接口中的每个方法也是隐式抽象的,因此不需要 abstract 关键字。
-
接口中的方法是隐式公有的。
Implementing Interfaces
当一个类实现一个接口时,可以将该类视为在签署一份合约,同意执行接口的特定行为。如果一个类没有执行接口的所有行为,则该类必须声明自身为抽象的。
类使用 implements 关键字来实现接口。implements 关键字出现在类声明中,紧跟声明的 extends 部分。
Example
/* File name : MammalInt.java */
public class MammalInt implements Animal {
public void eat() {
System.out.println("Mammal eats");
}
public void travel() {
System.out.println("Mammal travels");
}
public int noOfLegs() {
return 0;
}
public static void main(String args[]) {
MammalInt m = new MammalInt();
m.eat();
m.travel();
}
}
interface Animal {
public void eat();
public void travel();
}
Extending Interfaces
接口可以扩展另一个接口,就像一个类可以扩展另一个类一样。*extends*关键字用于扩展接口,子接口继承父接口的方法。
以下的 Sports 接口被 Hockey 和 Football 接口扩展。
Example
// Filename: Sports.java
public interface Sports {
public void setHomeTeam(String name);
public void setVisitingTeam(String name);
}
// Filename: Football.java
public interface Football extends Sports {
public void homeTeamScored(int points);
public void visitingTeamScored(int points);
public void endOfQuarter(int quarter);
}
// Filename: Hockey.java
public interface Hockey extends Sports {
public void homeGoalScored();
public void visitingGoalScored();
public void endOfPeriod(int period);
public void overtimePeriod(int ot);
}
Hockey 接口有四个方法,但它从 Sports 继承了两个方法;因此,实现 Hockey 的类需要实现所有六个方法。类似地,实现 Football 的类需要定义 Football 中的三个方法和 Sports 中的两个方法。
Example
interface Sports {
public void setHomeTeam(String name);
public void setVisitingTeam(String name);
}
interface Football extends Sports {
public void homeTeamScored(int points);
public void visitingTeamScored(int points);
public void endOfQuarter(int quarter);
}
interface Hockey extends Sports {
public void homeGoalScored();
public void visitingGoalScored();
public void endOfPeriod(int period);
public void overtimePeriod(int ot);
}
public class HockeyDemo implements Hockey {
public void setHomeTeam(String name) {
System.out.println("Home team: " + name);
}
public void setVisitingTeam(String name) {}
public void homeGoalScored() {}
public void visitingGoalScored() {}
public void endOfPeriod(int period) {}
public void overtimePeriod(int ot) {}
public static void main(String[] args) {
Hockey hockeyDemo = new HockeyDemo();
hockeyDemo.setHomeTeam("India");
}
}
Extending Multiple Interfaces
Java 类只能扩展一个父类。不允许多重继承。但是,接口不是类,并且一个接口可以扩展多个父接口。
extends 关键字只用一次,父接口在一个逗号分隔的列表中声明。
例如,如果 Hockey 接口同时扩展 Sports 和 Event,它将被声明为 −
Example
public interface Hockey extends Sports, Event
interface Sports {
public void setHomeTeam(String name);
public void setVisitingTeam(String name);
}
interface Football extends Sports {
public void homeTeamScored(int points);
public void visitingTeamScored(int points);
public void endOfQuarter(int quarter);
}
interface Hockey extends Sports {
public void homeGoalScored();
public void visitingGoalScored();
public void endOfPeriod(int period);
public void overtimePeriod(int ot);
}
interface Event {
public void organize();
}
public class HockeyDemo implements Hockey, Event {
public void setHomeTeam(String name) {
System.out.println("Home team: " + name);
}
public void setVisitingTeam(String name) {}
public void homeGoalScored() {}
public void visitingGoalScored() {}
public void endOfPeriod(int period) {}
public void overtimePeriod(int ot) {}
public static void main(String[] args) {
HockeyDemo hockeyDemo = new HockeyDemo();
hockeyDemo.setHomeTeam("India");
hockeyDemo.organize();
}
public void organize() {
System.out.println("Match organized. ");
}
}
Tagging Interfaces
扩展接口最常见的用法是当父接口不包含任何方法时。例如,java.awt.event 包中的 MouseListenter 接口扩展了 java.util.EventListener,它被定义为 −
Example
package java.util;
public interface EventListener
{}
没有方法的接口被称为*tagging*接口。标记接口有两个基本设计目的 −
Creates a common parent − 与 EventListener 接口一样(Java API 中的其他数十个接口对其进行了扩展),您可以使用标记接口在接口组之间创建公共父接口。例如,当接口扩展 EventListener 时,JVM 知道该特定接口将在事件委托场景中使用。
Adds a data type to a class − 这是术语“标记”的由来。实现标记接口的类不需要定义任何方法(因为该接口没有任何方法),但该类通过多态性成为接口类型。
Java - Packages
在 Java 中使用包是为了防止命名冲突、控制访问、简化类、接口、枚举和注释的搜索/定位及使用等。
一个 Package 可以被定义为相关类型的分组(类、接口、枚举和注解),提供访问保护并管理命名空间。
Java 中一些现存的包有:
-
java.lang − 汇集基本类
-
java.io − 此包中汇集输入输出函数类
程序员可以定义自己的包来捆绑类的组/接口等。将您实现的相关类分组是一个不错的方法,这样程序员可以轻易地确定类、接口、枚举和注解的相关性。
由于包创建了一个新的名称空间,因此不会与其他包中的名称发生名称冲突。使用包,可以更轻松地提供访问控制,也可以更轻松地找到相关类。
Creating a Package
创建包时,你应当为包选择一个名称,并在包中想要包含的类、接口、枚举和注解类型的每个源文件的顶部包含一条 package 语句。
包语句应当是源文件中的第一行。每个源文件中只能有一个包语句,它适用于文件中的所有类型。
如果未使用包语句,则类、接口、枚举和注解类型将放置在当前默认包中。
若要编译带包语句的 Java 程序,你必须按如下所示使用 -d 选项。
javac -d Destination_folder file_name.java
然后在指定的目标位置创建一个带有给定包名称的文件夹,并将编译的类文件放置在该文件夹中。
Example
我们来看一个创建名为 animals 的包的示例。最好使用小写字母作为包的名称,以避免与类和接口的名称发生冲突。
以下包示例包含名为 animals 的接口:
/* File name : Animal.java */
package animals;
interface Animal {
public void eat();
public void travel();
}
现在让我们在同一包 animals 中实现上述接口:
package animals;
/* File name : MammalInt.java */
public class MammalInt implements Animal {
public void eat() {
System.out.println("Mammal eats");
}
public void travel() {
System.out.println("Mammal travels");
}
public int noOfLegs() {
return 0;
}
public static void main(String args[]) {
MammalInt m = new MammalInt();
m.eat();
m.travel();
}
}
interface Animal {
public void eat();
public void travel();
}
现在按如下所示编译 java 文件:
$ javac -d . Animal.java
$ javac -d . MammalInt.java
现在在当前目录中将创建一个名称为 animals 的包/文件夹,并将这些类文件放置其中,如下所示。
你可以在包中执行类文件并获取如下所示的结果。
Mammal eats
Mammal travels
The import Keyword
如果某个类想要使用同一包中的另一个类,则不必使用包名称。同一包中的类可在没有任何特殊语法的条件下找到彼此。
Example
在这里,名为 Boss 的一个类被添加到已包含 Employee 的 payroll 包中。然后 Boss 可引用 Employee 类,而无需使用 payroll 前缀,如下面的 Boss 类所示。
package payroll;
public class Boss {
public void payEmployee(Employee e) {
e.mailCheck();
}
}
如果 Employee 类不在 payroll 包中会发生什么?现在 Boss 类必须使用以下技巧之一来引用一个在另一个包中的类。
-
可以使用该类的完全限定名称。例如 −
payroll.Employee
-
该包可使用 import 关键字和通配符 (*) 导入。例如 −
import payroll.*;
-
可以使用 import 关键字导入该类本身。例如 −
import payroll.Employee;
Example
package payroll;
public class Employee {
public void mailCheck() {
System.out.println("Pay received.");
}
}
package payroll;
import payroll.Employee;
public class Boss {
public void payEmployee(Employee e) {
e.mailCheck();
}
public static void main(String[] args) {
Boss boss = new Boss();
Employee e = new Employee();
boss.payEmployee(e);
}
}
The Directory Structure of Packages
将一个类放入一个包中时,会产生两个主要结果 −
-
正如我们在上一节中讨论的,该包的名称变成该类的名称的一部分。
-
包的名称必须与包含相应字节码的目录结构匹配。
下面是一种管理你在 Java 中的文件的简单方法 −
将类的源代码、接口、枚举或注释类型放入一个文本文件中,其名称是该类型的简单名称,其扩展名为 .java。
例如 -
// File Name : Car.java
package vehicle;
public class Car {
// Class implementation.
}
现在,将该源文件放到一个目录中,其名称反映了该类所属的包的名称 −
....\vehicle\Car.java
现在,限定类名和路径名如下 −
-
Class name → vehicle.Car
-
路径名 → vehicle/Car.java(在 Windows 中)
通常情况下,一家公司会使用它反转的互联网域名作为其包名称。
Example − 一家公司的互联网域名是 apple.com,那么它所有的包名称都将以 com.apple 开头。包名的每个部分对应于一个子目录。
Example − 该公司有一个 com.apple.computers 包,它包含一个 Dell.java 源文件,它会被包含在这个一系列的子目录中 −
....\com\apple\computers\Dell.java
在编译时,编译器会为其中定义的每个类、接口和枚举创建一个独立的输出文件。该输出文件的基名是该类型的名称,而它的扩展名为 .class。
例如 -
// File Name: Dell.java
package com.apple.computers;
public class Dell {
}
class Ups {
}
现在,使用 -d 选项编译这个文件,如下 −
$javac -d . Dell.java
文件将按如下方式编译 −
.\com\apple\computers\Dell.class
.\com\apple\computers\Ups.class
你可以按如下方式导入在 \com\apple\computer\ 中定义的所有类或接口 −
import com.apple.computers.*;
与 .java 源文件类似,编译的 .class 文件应位于一系列反映包名的目录中。但是,通往 .class 文件的路径不必与通往 .java 源文件的路径相同。你可以单独排列你的源文件和类目录,如下 −
<path-one>\sources\com\apple\computers\Dell.java
<path-two>\classes\com\apple\computers\Dell.class
通过执行此操作,可以在不透露你的源代码的情况下,让其他程序员访问 classes 目录。你同样需要管理源代码和类文件,以便编译器和 Java 虚拟机 (JVM) 可以找到你的程序所使用的所有类型。
classes 目录的完整路径 <path-two>\classes 称为类路径,并使用 CLASSPATH 系统变量进行设置。编译器和 JVM 都通过向类路径中添加包名称,来构建到 .class 文件的路径。
假设 <path-two>\classes 是类路径,并且包名称为 com.apple.computers,那么编译器和 JVM 会在 <path-two>\classes\com\apple\computers 中寻找 .class 文件。
一个类路径中可包含多个路径。多个路径应使用分号(Windows)或冒号(Unix)分隔。默认情况下,编译器和 JVM 会搜索当前目录和包含 Java 平台类的 JAR 文件,以便这些目录自动位于类路径中。
Set CLASSPATH System Variable
要显示当前的 CLASSPATH 变量,请在 Windows 和 UNIX(Bourne shell)中使用以下命令:
-
在 Windows 中 → C:> set CLASSPATH
-
在 UNIX 中 → % echo $CLASSPATH
要删除 CLASSPATH 变量的当前内容,请使用:
-
在 Windows 中 → C:> set CLASSPATH =
-
在 UNIX 中 → % unset CLASSPATH; export CLASSPATH
要设置 CLASSPATH 变量,请使用:
-
在 Windows 中 → set CLASSPATH = C:\users\jack\java\classes
-
在 UNIX 中 → % CLASSPATH = /home/jack/java/classes; export CLASSPATH
Java - Inner classes
在本章中,我们将讨论 Java 的内部类。
Nested Classes
在 Java 中,就像方法一样,一个类的变量也可以将其另一个类作为自己的成员。在 Java 中允许在另一个类中编写类。在内部编写的这个类被称为 nested class,而持有内部类的这个类被称为 outer class。
Inner Classes (Non-static Nested Classes)
内部类是 Java 中的一种安全机制。我们知道一个类不可与访问修饰符 private 相关联,但是如果我们把这个类作为另一个类的成员,那么这个内部类可以设为私有的。这亦用于访问一个类的私有成员。
根据定义它们的位置和方式,内部类有三种类型。它们是 −
-
Inner Class
-
Method-local Inner Class
-
Anonymous Inner Class
Inner Class
创建内部类很简单。你只需要在一个类中写一个类。与类不同,内部类可以是私有的,一旦你将内部类声明为私有的,则无法通过该类外部的对象访问。
以下是创建内部类并访问它的程序。在给定的示例中,我们将内部类设为私有,并通过一个方法来访问该类。
Example
class Outer_Demo {
int num;
// inner class
private class Inner_Demo {
public void print() {
System.out.println("This is an inner class");
}
}
// Accessing he inner class from the method within
void display_Inner() {
Inner_Demo inner = new Inner_Demo();
inner.print();
}
}
public class My_class {
public static void main(String args[]) {
// Instantiating the outer class
Outer_Demo outer = new Outer_Demo();
// Accessing the display_Inner() method.
outer.display_Inner();
}
}
在这里你可以看到 Outer_Demo 是外部类,Inner_Demo 是内部类,我们在这个方法中实例化内部类,并且从 main 方法调用此方法。
如果您编译并执行上述程序,将得到以下结果 −
Output
This is an inner class.
Accessing the Private Members
如前所述,内部类还用来访问类的私有成员。假设一个类有要访问它们的私有成员。在其中写一个内部类,从内部类中的一个方法(例如 getValue())中返回私有成员,最后从另一个类(你要从中访问私有成员的类)调用内部类的 getValue() 方法。
要实例化内部类,首先必须实例化外部类。此后,使用外部类的对象,以下是实例化内部类的方法。
Outer_Demo outer = new Outer_Demo();
Outer_Demo.Inner_Demo inner = outer.new Inner_Demo();
以下程序显示如何使用内部类访问类的私有成员。
Example
class Outer_Demo {
// private variable of the outer class
private int num = 175;
// inner class
public class Inner_Demo {
public int getNum() {
System.out.println("This is the getnum method of the inner class");
return num;
}
}
}
public class My_class2 {
public static void main(String args[]) {
// Instantiating the outer class
Outer_Demo outer = new Outer_Demo();
// Instantiating the inner class
Outer_Demo.Inner_Demo inner = outer.new Inner_Demo();
System.out.println(inner.getNum());
}
}
如果您编译并执行上述程序,将得到以下结果 −
Output
This is the getnum method of the inner class: 175
Method-local Inner Class
在 Java 中,我们可以在一个方法中写一个类,它将成为一个局部类型。与局部变量一样,内部类的范围限制在方法内。
方法局部内部类只能在定义内部类的方法中实例化。以下程序演示如何使用方法局部内部类。
Example
public class Outerclass {
// instance method of the outer class
void my_Method() {
int num = 23;
// method-local inner class
class MethodInner_Demo {
public void print() {
System.out.println("This is method inner class "+num);
}
} // end of inner class
// Accessing the inner class
MethodInner_Demo inner = new MethodInner_Demo();
inner.print();
}
public static void main(String args[]) {
Outerclass outer = new Outerclass();
outer.my_Method();
}
}
如果您编译并执行上述程序,将得到以下结果 −
Anonymous Inner Class
未声明类名的内部类称为 anonymous inner class。对于匿名内部类,我们同时声明和实例化它们。通常,每当你需要重写类或接口的方法时,它们就会被使用。匿名内部类的语法如下 −
Syntax
AnonymousInner an_inner = new AnonymousInner() {
public void my_method() {
........
........
}
};
以下程序演示如何使用匿名内部类重写类的类。
Example
abstract class AnonymousInner {
public abstract void mymethod();
}
public class Outer_class {
public static void main(String args[]) {
AnonymousInner inner = new AnonymousInner() {
public void mymethod() {
System.out.println("This is an example of anonymous inner class");
}
};
inner.mymethod();
}
}
如果您编译并执行上述程序,将得到以下结果 −
Anonymous Inner Class as Argument
通常,如果一个方法接受接口、抽象类或具体类的对象,那么我们可以实现接口、扩展抽象类,并将对象传给方法。如果它是一个类,那么我们可以直接将它传给方法。
但在所有这三种情况下,你都可以将匿名内部类传给方法。以下是将匿名内部类作为方法参数传递的语法 −
obj.my_Method(new My_Class() {
public void Do() {
.....
.....
}
});
以下程序演示如何将匿名内部类作为方法参数传递。
Example
// interface
interface Message {
String greet();
}
public class My_class {
// method which accepts the object of interface Message
public void displayMessage(Message m) {
System.out.println(m.greet() +
", This is an example of anonymous inner class as an argument");
}
public static void main(String args[]) {
// Instantiating the class
My_class obj = new My_class();
// Passing an anonymous inner class as an argument
obj.displayMessage(new Message() {
public String greet() {
return "Hello";
}
});
}
}
如果您编译并执行上面的程序,您将获得以下结果-
Static Nested Class
静态内部类是一个嵌套类,它是外部类的静态成员。可以使用其他静态成员在不实例化外部类的情况下访问它。与静态成员一样,静态嵌套类无权访问外部类的实例变量和方法。静态嵌套类的语法如下-
Java - Character Class
通常,当我们处理字符时,我们使用原始数据类型char。
Example
char ch = 'a';
// Unicode for uppercase Greek omega character
char uniChar = '\u039A';
// an array of chars
char[] charArray ={ 'a', 'b', 'c', 'd', 'e' };
然而在开发中,我们会遇到需要使用对象来代替原始数据类型的情况。为了实现这一点,Java为原始数据类型char提供了包装类`@ {s0}`。
`Character`类提供了一些有用的类(即静态)方法来操作字符。你可以使用`Character`构造函数创建一个`Character`对象 -
Character ch = new Character('a');
在某些情况下,Java编译器还会为你创建一个`Character`对象。例如,如果你将一个原始char传递给一个期望对象的函数,编译器会自动为你将char转换成一个`Character`。此功能称为自动装箱或拆箱,如果转换相反。
Example
// Here following primitive char 'a'
// is boxed into the Character object ch
Character ch = 'a';
// Here primitive 'x' is boxed for method test,
// return is unboxed to char 'c'
char c = test('x');
Escape Sequences
一个带有反斜杠(\)的前缀字符是一个转义序列,并且对编译器有特殊的含义。
换行符(\n)在本教程中经常在`System.out.println()`语句中使用,以便在字符串打印后换到下一行。
下表显示了Java转义序列 -
Escape Sequence |
Description |
\t |
在此处在文本中插入制表符。 |
\b |
在此处在文本中插入退格符。 |
\n |
在此处在文本中插入换行符。 |
\r |
在此处在文本中插入回车符。 |
\f |
在此处在文本中插入换页符。 |
\' |
在此处在文本中插入一个单引号字符。 |
\" |
在此处在文本中插入一个双引号字符。 |
当在打印语句中遇到转义序列时,编译器会相应地对其进行解释。
Character Methods
以下是 Character 类的所有子类实现的重要实例化方法的列表 −
Sr.No. |
Method & Description |
1 |
isLetter() 确定指定 char 值是否为字母。 |
2 |
isDigit() 确定指定 char 值是否为数字。 |
3 |
isWhitespace() 确定指定 char 值是否为空白。 |
4 |
isUpperCase() 确定指定 char 值是否为大写。 |
5 |
isLowerCase() 确定指定的字符值是否是小写。 |
6 |
toUpperCase() 返回指定字符值的大写形式。 |
7 |
toLowerCase() 返回指定字符值的小写形式。 |
8 |
toString() 返回表示指定字符值的 String 对象,即一个单字符字符串。 |
有关方法的完整列表,请参阅 java.lang.Character API 规范。
Java - Files and I/O
java.io 包包含了你执行 Java 中的输入输出 (I/O) 所可能需要的几乎所有类。所有这些流都代表了一个输入源和一个输出目标。java.io 包中的流支持许多数据,比如基本类型、对象、本地化的字符等。
Stream
流可以被定义为一个数据序列。流有两种:
-
InPutStream - InputStream 用于从一个源读取数据。
-
OutPutStream - OutputStream 用于向一个目标写入数据。
Java 提供了对文件和网络的强大而灵活的 I/O 相关支持,但本教程涵盖了与流和 I/O 相关的非常基本的功能。我们一个一个地看最常用的例子:
Byte Streams
Java 字节流用于执行 8 位字节的输入和输出。虽然有许多与字节流相关的类,但最常用的类是 FileInputStream 和 FileOutputStream。下面是一个利用这两个类将一个输入文件复制到一个输出文件的示例:
Example
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyFile {
public static void main(String args[]) throws IOException {
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("input.txt");
out = new FileOutputStream("output.txt");
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
}finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
}
现在,我们有一个文件 input.txt,其中包含以下内容:
This is test for copy file.
接下来,编译上述程序并执行它,这将导致创建一个内容与 input.txt 中相同的名为 output.txt 的文件。那么,我们将上述代码放入 CopyFile.java 文件中,并执行以下操作 −
$javac CopyFile.java
$java CopyFile
Character Streams
Java Byte 流用于执行 8 位字节的输入和输出,而 Java Character 流用于执行 16 位 Unicode 的输入和输出。虽然存在许多与字符流相关的类,但最常用的类是 FileReader 和 FileWriter。尽管 FileReader 在内部使用 FileInputStream,FileWriter 在内部使用 FileOutputStream,但这里的主要区别在于 FileReader 一次读取两个字节,而 FileWriter 一次写入两个字节。
我们可重写上述示例,它利用这两个类将输入文件(包含 unicode 字符)复制到输出文件中 −
Example
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyFile {
public static void main(String args[]) throws IOException {
FileReader in = null;
FileWriter out = null;
try {
in = new FileReader("input.txt");
out = new FileWriter("output.txt");
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
}finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
}
现在,我们有一个文件 input.txt,其中包含以下内容:
This is test for copy file.
接下来,编译上述程序并执行它,这将导致创建一个内容与 input.txt 中相同的名为 output.txt 的文件。那么,我们将上述代码放入 CopyFile.java 文件中,并执行以下操作 −
$javac CopyFile.java
$java CopyFile
Standard Streams
所有的编程语言都提供对标准 I/O 的支持,用户程序可从键盘获取输入,然后在计算机屏幕上生成输出。如果您知道 C 或 C++ 编程语言,那么您肯定知道三个标准设备 STDIN、STDOUT 和 STDERR。同样,Java 提供了以下三个标准流 −
-
Standard Input − 用于向用户程序输入数据,通常会将键盘用作标准输入流,表示为 System.in。
-
Standard Output − 用于输出用户程序产生的数据,通常会将计算机屏幕用于标准输出流,表示为 System.out。
-
Standard Error − 用于输出用户程序产生的错误数据,通常会将计算机屏幕用于标准错误流,表示为 System.err。
以下是一个简单的程序,它创建 InputStreamReader 以读取标准输入流,直到用户键入“q” −
Example
import java.io.InputStreamReader;
public class ReadConsole {
public static void main(String args[]) throws IOException {
InputStreamReader cin = null;
try {
cin = new InputStreamReader(System.in);
System.out.println("Enter characters, 'q' to quit.");
char c;
do {
c = (char) cin.read();
System.out.print(c);
} while(c != 'q');
}finally {
if (cin != null) {
cin.close();
}
}
}
}
我们将上述代码保存在 ReadConsole.java 文件中,并尝试编译并执行它,如下面的程序所示。此程序将继续读取并输出相同的字符,直到我们按“q”为止 −
$javac ReadConsole.java
$java ReadConsole
Enter characters, 'q' to quit.
1
1
e
e
q
q
Reading and Writing Files
如前文所述,流可定义为数据序列。InputStream 用于从源读取数据,而 OutputStream 用于将数据写入目标。
以下是对输入和输出流进行处理的类层次结构。
两个重要的流是 FileInputStream 和 FileOutputStream,本教程将对它们进行讨论。
FileInputStream
此流用于从文件中读取数据。可以使用关键字 new 创建对象,并且有若干种构造函数可用。
以下构造函数将文件名作为字符串以创建输入流对象来读取文件 −
InputStream f = new FileInputStream("C:/java/hello");
以下构造函数获取文件对象以创建输入流对象来读取文件。我们首先使用 File() 方法创建一个文件对象,如下所示 −
File f = new File("C:/java/hello");
InputStream f = new FileInputStream(f);
一旦您掌握了 InputStream 对象,即可以使用一组辅助方法来读取流或对流执行其他操作。
Sr.No. |
Method & Description |
1 |
public void close() throws IOException{} 此方法关闭文件输出流。释放与文件关联的任何系统资源。引发 IOException。 |
2 |
protected void finalize()throws IOException {} 此方法清理与文件的连接。确保在这个文件输出流的引用消失时,调用该文件输出流的 close 方法。引发 IOException。 |
3 |
public int read(int r)throws IOException{} 此方法从 InputStream 读取指定的字节数据。返回 int。返回下一个字节数据,如果到达文件末尾则返回 -1。 |
4 |
public int read(byte[] r) throws IOException{} 此方法从输入流中读取 r.length 个字节到一个数组中。返回读取的总字节数。如果到达文件末尾,则返回 -1。 |
5 |
public int available() throws IOException{} 提供从此文件输入流读取的字节数。返回 int。 |
FileOutputStream
FileOutputStream 用于创建文件并将数据写入其中。流将在写入之前创建文件(如果尚未存在)。
下面有两个可用于创建 FileOutputStream 对象的构造函数。
下面的构造函数以字符串形式获取一个文件名,用于创建一个输入流对象以写入文件:
OutputStream f = new FileOutputStream("C:/java/hello")
下面的构造函数获取一个文件对象,用于创建一个输出流对象以写入文件。首先,我们使用 File() 方法创建一个文件对象,如下所示:
File f = new File("C:/java/hello");
OutputStream f = new FileOutputStream(f);
get OutputStream 对象后,可以使用一系列帮助方法将数据写入流或对流执行其他操作。
Sr.No. |
Method & Description |
1 |
public void close() throws IOException{} 此方法关闭文件输出流。释放与文件关联的任何系统资源。引发 IOException。 |
2 |
protected void finalize()throws IOException {} 此方法清理与文件的连接。确保在这个文件输出流的引用消失时,调用该文件输出流的 close 方法。引发 IOException。 |
3 |
public void write(int w)throws IOException{} 在此方法中,将指定的字节写入输出流。 |
4 |
public void write(byte[] w) 从上述字节数组中写入 w.length 个字节到 OutputStream 中。 |
Example
以下是演示 InputStream 和 OutputStream 的示例 −
import java.io.OutputStream;
public class fileStreamTest {
public static void main(String args[]) {
try {
byte bWrite [] = {11,21,3,40,5};
OutputStream os = new FileOutputStream("test.txt");
for(int x = 0; x < bWrite.length ; x++) {
os.write( bWrite[x] ); // writes the bytes
}
os.close();
InputStream is = new FileInputStream("test.txt");
int size = is.available();
for(int i = 0; i < size; i++) {
System.out.print((char)is.read() + " ");
}
is.close();
} catch (IOException e) {
System.out.print("Exception");
}
}
}
上述代码将创建文件 test.txt,并以二进制格式写入给定数字。stdout 屏幕上的输出也会相同。
Directories in Java
目录是一个可以包含其他文件和目录列表的文件。使用 File 对象创建目录,以列出目录中可用的文件。有关完整详细信息,请查看可以在文件对象上调用的所有方法的列表以及与目录相关的方法。
Creating Directories
有两个有用的 File 实用方法可用于创建目录 −
-
mkdir( ) 方法创建一个目录,成功时返回 true,失败时返回 false。失败表示文件对象中指定的路径已存在,或者由于整个路径尚未存在而无法创建目录。
-
mkdirs() 方法创建目录及其所有父目录。
以下示例创建 "/tmp/user/java/bin" 目录 −
Example
import java.io.File;
public class CreateDir {
public static void main(String args[]) {
String dirname = "/tmp/user/java/bin";
File d = new File(dirname);
// Create directory now.
d.mkdirs();
}
}
编译并执行上述代码以创建 "/tmp/user/java/bin"。
Note − Java 会自动按照惯例处理 UNIX 和 Windows 上的路径分隔符。如果你在 Java 的 Windows 版本上使用正斜杠 (/),路径仍然会正确解析。
Listing Directories
可以使用 File 对象提供的 list( ) 方法来列出目录中可用的所有文件和目录,如下所示 −
Example
import java.io.File;
public class ReadDir {
public static void main(String[] args) {
File file = null;
String[] paths;
try {
// create new file object
file = new File("/tmp");
// array of files and directory
paths = file.list();
// for each name in the path array
for(String path:paths) {
// prints filename and directory name
System.out.println(path);
}
} catch (Exception e) {
// if any error occurs
e.printStackTrace();
}
}
}
这将根据 /tmp 目录中可用的目录和文件生成以下结果 −
Output
test1.txt
test2.txt
ReadDir.java
ReadDir.class
Java - Exceptions
异常(或特殊事件)是在程序执行期间产生的问题。当 Exception 发生时,程序的正常流程会中断,程序/应用程序异常终止,这是不建议的,因此,这些异常需要被处理。
异常可能由于许多不同的原因而发生。以下是一些发生异常的情况。
-
用户输入了无效的数据。
-
找不到需要打开的文件。
-
在通信过程中网络连接丢失,或 JVM 内存不足。
其中一些异常是由用户错误引起的,另一些是由程序员错误引起的,还有一些是由某种方式失效的物理资源引起的。
基于这些,我们有三大类的异常。您需要理解它们才能知道 Java 中的异常处理是如何工作的。
-
Checked exceptions − Checked 异常是编译器在编译时检查(通知)的异常,也称为编译时异常。这些异常不能被简单忽略,程序员应当处理(管理)这些异常。
例如,如果您在程序中使用 FileReader 类从文件中读取数据,如果其构造函数中指定的文件不存在,则会发生 FileNotFoundException,并且编译器会提示程序员处理该异常。
Example
import java.io.File;
import java.io.FileReader;
public class FilenotFound_Demo {
public static void main(String args[]) {
File file = new File("E://file.txt");
FileReader fr = new FileReader(file);
}
}
如果您尝试编译上述程序,您将获得以下异常。
Output
C:\>javac FilenotFound_Demo.java
FilenotFound_Demo.java:8: error: unreported exception FileNotFoundException; must be caught or declared to be thrown
FileReader fr = new FileReader(file);
^
1 error
Note − 由于 FileReader 类的 read() 和 close() 方法会抛出 IOException,您可以观察到编译器通知处理 IOException,以及 FileNotFoundException。
-
Unchecked exceptions − 未检查异常是执行时发生的异常。这些也称为 Runtime Exceptions。其中包括编程错误,例如逻辑错误或不恰当使用 API。运行时异常在编译时会被忽略。
例如,如果您在程序中声明了一个长度为 5 的数组,并尝试调用该数组的第 6 个元素,则会发生 ArrayIndexOutOfBoundsException 异常。
Example
public class Unchecked_Demo {
public static void main(String args[]) {
int num[] = {1, 2, 3, 4};
System.out.println(num[5]);
}
}
如果您编译并执行上述程序,您将获得以下异常。
Output
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
at Exceptions.Unchecked_Demo.main(Unchecked_Demo.java:8)
-
Errors − 这些根本不是异常,而是超出用户或程序员掌控范围的问题。在您的代码中通常会忽略错误,因为对于错误您很少能做些什么。例如,如果出现堆栈溢出,将引发错误。在编译时也会忽略它们。
Exception Hierarchy
所有异常类都是 java.lang.Exception 类的子类型。Exception 类是 Throwable 类的子类。除了 exception 类之外,还有另一个称为 Error 的子类,它派生自 Throwable 类。
错误是发生在严重故障的情况下的异常条件,这些错误不受 Java 程序处理。生成错误是为了指示由运行时环境生成的错误。示例:JVM 已用完内存。通常,程序无法从错误中恢复。
Exception 类有两个主要子类:IOException 类和 RuntimeException 类。
以下列出了最常见的 Java’s Built-in Exceptions检查项和未检查项。
Exceptions Methods
以下是 Throwable 类中提供的重要方法的列表。
Sr.No. |
Method & Description |
1 |
public String getMessage() 返回关于发生的异常的详细信息。此消息在 Throwable 构造函数中初始化。 |
2 |
public Throwable getCause() 返回由 Throwable 对象表示的异常原因。 |
3 |
public String toString() 返回类名与 getMessage() 结果连接后的字符串。 |
4 |
public void printStackTrace() 将 toString() 的结果以及堆栈跟踪打印到 System.err(错误输出流)。 |
5 |
public StackTraceElement [] getStackTrace() 返回包含堆栈跟踪中每个元素的数组。索引为 0 的元素表示调用堆栈的顶部,数组中的最后一个元素表示调用堆栈底部的函数。 |
6 |
public Throwable fillInStackTrace() 使用当前堆栈跟踪填充此 Throwable 对象的堆栈跟踪,以添加堆栈跟踪中以前的任何信息。 |
Catching Exceptions
使用 try 和 catch 关键字组合的方法捕获异常。一个 try/catch 块被放置在可能生成异常的代码周围。try/catch 块内的代码被称为受保护代码,try/catch 使用的语法如下所示 −
Syntax
try {
// Protected code
} catch (ExceptionName e1) {
// Catch block
}
容易产生异常的代码被放置在 try 块中。当异常发生时,该发生的异常由与它关联的 catch 块处理。每个 try 块应该紧接着一个 catch 块或 finally 块。
catch 语句涉及声明你试图捕获的异常类型。如果受保护代码中发生异常,则检查 try 之后的 catch 块(或块)。如果发生的异常类型被列在一个 catch 块中,则异常被传递到 catch 块中,就像一个参数被传递到一个方法参数中一样。
Example
在下面的示例中,一个数组被声明为带有 2 个元素。然后代码尝试访问数组的第 3 个元素,从而抛出一个异常。
// File Name : ExcepTest.java
import java.io.*;
public class ExcepTest {
public static void main(String args[]) {
try {
int a[] = new int[2];
System.out.println("Access element three :" + a[3]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Exception thrown :" + e);
}
System.out.println("Out of the block");
}
}
Multiple Catch Blocks
一个 try 块可以跟随多个 catch 块。多个 catch 块的语法如下所示 −
Syntax
try {
// Protected code
} catch (ExceptionType1 e1) {
// Catch block
} catch (ExceptionType2 e2) {
// Catch block
} catch (ExceptionType3 e3) {
// Catch block
}
前面的语句演示了三个 catch 块,但是你可以在一个 try 之后可以有任意数量的 catch 块。如果受保护代码中发生异常,则异常被抛到列表中的第一个 catch 块。如果抛出的异常的数据类型匹配 ExceptionType1,则异常在该处被捕获。如果没有,则异常传递到第二个 catch 语句。这将持续到异常被捕获或者通过所有 catch,在这种情况下,当前方法停止执行并且异常被抛到调用堆栈上先前的那个方法。
Catching Multiple Type of Exceptions
自 Java 7 起,你可以使用一个 catch 块处理多个异常,这个功能简化了代码。以下是处理它的方法 −
catch (IOException|FileNotFoundException ex) {
logger.log(ex);
throw ex;
The Throws/Throw Keywords
如果一个方法没有处理已检查异常,则方法必须使用 throws 关键字声明它。throws 关键字显示在方法签名的末尾。
你可以使用 throw 关键字抛出一个异常,它既可以是一个新实例化的异常,也可以是你刚捕获的异常。
尝试理解 throws 和 throw 关键字之间的差异,throws 用于推迟对已检查异常的处理,而 throw 用于显式地调用一个异常。
以下方法声明它抛出一个 RemoteException −
Example
import java.io.*;
public class className {
public void deposit(double amount) throws RemoteException {
// Method implementation
throw new RemoteException();
}
// Remainder of class definition
}
一个方法可以声明它抛出多个异常,在这种情况下,异常被声明在一个以逗号分隔的列表中。例如,以下方法声明它抛出 RemoteException 和 InsufficientFundsException −
The Finally Block
finally 块跟随一个 try 块或一个 catch 块。无论异常是否发生,finally 代码块始终执行。
使用 finally 块允许你运行你想要执行的任何清理类型语句,无论受保护代码中发生什么。
finally 块出现在 catch 块的末尾并且具有以下语法 −
Syntax
try {
// Protected code
} catch (ExceptionType1 e1) {
// Catch block
} catch (ExceptionType2 e2) {
// Catch block
} catch (ExceptionType3 e3) {
// Catch block
}finally {
// The finally block always executes.
}
Example
public class ExcepTest {
public static void main(String args[]) {
int a[] = new int[2];
try {
System.out.println("Access element three :" + a[3]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Exception thrown :" + e);
}finally {
a[0] = 6;
System.out.println("First element value: " + a[0]);
System.out.println("The finally statement is executed");
}
}
}
The try-with-resources
通常,当我们使用任何资源(例如流、连接等)时,我们必须使用 finally 块显式关闭它们。在以下程序中,我们正在使用 FileReader 从文件中读取数据,并且我们正在使用 finally 块关闭它。
Example
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class ReadData_Demo {
public static void main(String args[]) {
FileReader fr = null;
try {
File file = new File("file.txt");
fr = new FileReader(file); char [] a = new char[50];
fr.read(a); // reads the content to the array
for(char c : a)
System.out.print(c); // prints the characters one by one
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fr.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
try-with-resources(也称为 automatic resource management)是 Java 7 引入的一种新的异常处理机制,它会自动关闭 try catch 块内使用的资源。
要使用此语句,您只需在括号中声明所需资源,创建的资源将在块结束时自动关闭。以下是 try-with-resources 语句的语法。
Syntax
try(FileReader fr = new FileReader("file path")) {
// use the resource
} catch () {
// body of catch
}
}
以下是使用 try-with-resources 语句读取文件中的数据的程序。
Example
import java.io.FileReader;
import java.io.IOException;
public class Try_withDemo {
public static void main(String args[]) {
try(FileReader fr = new FileReader("E://file.txt")) {
char [] a = new char[50];
fr.read(a); // reads the contentto the array
for(char c : a)
System.out.print(c); // prints the characters one by one
} catch (IOException e) {
e.printStackTrace();
}
}
}
在使用 try-with-resources 语句时,需要注意以下几点。
-
要使用带有 try-with-resources 语句的类,它应该实现 AutoCloseable 接口并自动在运行时调用它的 close() 方法。
-
你可以在 try-with-resources 语句中声明多个类。
-
当你在 try-with-resources 语句的 try 块中声明多个类时,这些类将按相反的顺序关闭。
-
除了括号内的资源声明之外,其他所有内容都与 try 块的 normal try/catch 块相同。
-
在 try 块中声明的资源在 try-block 开始之前被实例化。
-
在 try 块中声明的资源隐式声明为 final。
User-defined Exceptions
您可以在 Java 中创建自己的异常。在编写自己的异常类时,牢记以下几点:
-
所有异常都必须是 Throwable 的子类。
-
如果你想编写一个自动受到 Handle 或 Declare 规则强制执行的检查异常,则需要扩展 Exception 类。
-
如果您想编写运行时异常,则需要扩展 RuntimeException 类。
我们可以按如下方式定义自己的 Exception 类:
class MyException extends Exception {
}
您只需要扩展预定义的 Exception 类即可创建您自己的 Exception。这些被认为是已检查的异常。以下 InsufficientFundsException 类是一个用户定义的异常,它扩展了 Exception 类,使其成为一个已检查的异常。异常类就像任何其他类,包含有用的字段和方法。
Example
// File Name InsufficientFundsException.java
import java.io.*;
public class InsufficientFundsException extends Exception {
private double amount;
public InsufficientFundsException(double amount) {
this.amount = amount;
}
public double getAmount() {
return amount;
}
}
为了演示如何使用我们用户定义的异常,以下 CheckingAccount 类包含一个 withdraw() 方法,该方法会抛出一个 InsufficientFundsException。
// File Name CheckingAccount.java
import java.io.*;
public class CheckingAccount {
private double balance;
private int number;
public CheckingAccount(int number) {
this.number = number;
}
public void deposit(double amount) {
balance += amount;
}
public void withdraw(double amount) throws InsufficientFundsException {
if(amount <= balance) {
balance -= amount;
}else {
double needs = amount - balance;
throw new InsufficientFundsException(needs);
}
}
public double getBalance() {
return balance;
}
public int getNumber() {
return number;
}
}
以下 BankDemo 程序演示如何调用 CheckingAccount 的 deposit() 和 withdraw() 方法。
// File Name BankDemo.java
public class BankDemo {
public static void main(String [] args) {
CheckingAccount c = new CheckingAccount(101);
System.out.println("Depositing $500...");
c.deposit(500.00);
try {
System.out.println("\nWithdrawing $100...");
c.withdraw(100.00);
System.out.println("\nWithdrawing $600...");
c.withdraw(600.00);
} catch (InsufficientFundsException e) {
System.out.println("Sorry, but you are short $" + e.getAmount());
e.printStackTrace();
}
}
}
编译以上三个文件并运行 BankDemo。这将产生以下结果:
Java - Multithreading
Java 是一种多线程编程语言,这意味着我们可以使用 Java 开发多线程程序。多线程程序包含两个或更多可以同时运行的部分,每个部分都可以在同一时间处理不同的任务,从而优化利用可用资源,特别是在您的计算机有多个 CPU 时。
根据定义,多任务处理是多个进程共享公共处理资源(如 CPU)时发生的情况。多线程将多任务处理的概念扩展到应用程序中,您可以在其中将单个应用程序内的特定操作细分为各个线程。每个线程都可以并行运行。操作系统不仅在不同应用程序之间划分处理时间,还在应用程序内的每个线程之间划分处理时间。
通过使用多线程,您可以以在相同程序中可以并行执行多项活动的方式来编写代码。
Life Cycle of a Thread
线程在其生命周期中经过不同的阶段。例如,一个线程诞生、启动、运行,然后死亡。下图显示了线程的完整生命周期。
以下是生命周期的阶段 -
-
New - 新线程在其生命周期中处于新的状态。它一直保持此状态,直到程序启动线程。它也被称为 born thread。
-
Runnable - 新生的线程启动后,该线程变得可运行。处于此状态的线程被认为正在执行其任务。
-
Waiting - 有时,线程在等待另一个线程执行任务时会转变为等待状态。仅当其他线程发信号指示等待线程继续执行时,线程才会转换回可运行状态。
-
Timed Waiting - 可运行线程可以进入特定时间间隔的时间等待状态。当该时间间隔到期或它正在等待的事件发生时,处于此状态的线程会转换回可运行状态。
-
Terminated (Dead) - 当可运行线程完成其任务或以其他方式终止时,它会进入终止状态。
Thread Priorities
每个 Java 线程都有一个优先级,这有助于操作系统确定线程调度的顺序。
Java 线程优先级介于 MIN_PRIORITY(常量为 1)和 MAX_PRIORITY(常量为 10)之间。默认情况下,每个线程都获得 NORM_PRIORITY(常量为 5)优先级。
具有较高优先级的线程对程序而言更为重要,并且应该在较低优先级的线程之前分配处理器时间。但是,线程优先级不能保证线程执行的顺序,并且很大程度上取决于平台。
Create a Thread by Implementing a Runnable Interface
如果准备将您的类作为线程执行,那么可以通过实现 Runnable 接口实现此目的。您需要遵循三个基本步骤 −
Step 1
第一步,您需要实现 Runnable 接口提供的 run() 方法。此方法为线程提供了一个入口点,您需要将完整业务逻辑放入此方法内。以下是 run() 方法的一个简单语法:
public void run( )
Step 2
作为第二步,您将使用以下构造函数实例化一个 Thread 对象 −
Thread(Runnable threadObj, String threadName);
其中,threadObj 是实现 Runnable 接口的类的实例,threadName 是赋予新线程的名称。
Example
下面是创建一个新线程并开始运行它的示例:
class RunnableDemo implements Runnable {
private Thread t;
private String threadName;
RunnableDemo( String name) {
threadName = name;
System.out.println("Creating " + threadName );
}
public void run() {
System.out.println("Running " + threadName );
try {
for(int i = 4; i > 0; i--) {
System.out.println("Thread: " + threadName + ", " + i);
// Let the thread sleep for a while.
Thread.sleep(50);
}
} catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
}
public void start () {
System.out.println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
public static void main(String args[]) {
RunnableDemo R1 = new RunnableDemo( "Thread-1");
R1.start();
RunnableDemo R2 = new RunnableDemo( "Thread-2");
R2.start();
}
}
Output
Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.
Create a Thread by Extending a Thread Class
创建线程的第二种方法是创建一个扩展 Thread 类的类,使用以下两个简单步骤。这种方法在使用 Thread 类中可用方法创建多个线程时提供了更多的灵活性。
Step 1
您需要覆盖 Thread 类中可用的 run( ) 方法。此方法为线程提供一个入口点,您将把您的完整业务逻辑放在这个方法中。以下是 run() 方法的简单语法:
public void run( )
Step 2
一旦创建 Thread 对象,便可以通过调用 start() 方法来启动它,该调用会执行一个 run( ) 方法调用。以下是 start() 方法的一个简单语法 −
void start( );
Example
以下是按照 Thread 扩展重写的先前程序:
class ThreadDemo extends Thread {
private Thread t;
private String threadName;
ThreadDemo( String name) {
threadName = name;
System.out.println("Creating " + threadName );
}
public void run() {
System.out.println("Running " + threadName );
try {
for(int i = 4; i > 0; i--) {
System.out.println("Thread: " + threadName + ", " + i);
// Let the thread sleep for a while.
Thread.sleep(50);
}
} catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
}
public void start () {
System.out.println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
public static void main(String args[]) {
ThreadDemo T1 = new ThreadDemo( "Thread-1");
T1.start();
ThreadDemo T2 = new ThreadDemo( "Thread-2");
T2.start();
}
}
Output
Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.
Thread Methods
以下是 Thread 类中提供的重要方法列表:
Sr.No. |
Method & Description |
1 |
public void start() 在一个单独的执行路径中启动线程,然后调用此线程对象上的 run() 方法。 |
2 |
public void run() 如果此线程对象是使用单独的 Runnable 对象实例化的,则该 run() 方法将在这个 Runnable 对象上被调用。 |
3 |
public final void setName(String name) 更改线程对象名称。还有一个 getName() 方法用于检索名称。 |
4 |
public final void setPriority(int priority) 设置此 Thread 对象的优先级。可能的值介于 1 到 10 之间。 |
5 |
public final void setDaemon(boolean on) true 参数表示此 Thread 为守护程序线程。 |
6 |
public final void join(long millisec) 当前线程在此第二个线程上调用此方法,导致当前线程阻塞,直到第二个线程终止或经过指定数目的毫秒。 |
7 |
public void interrupt() 中断此线程,如果它因任何原因而被阻塞,则导致它继续执行。 |
8 |
public final boolean isAlive() 如果线程处于活动状态,则返回 true,即在启动线程之后的任何时间但在它运行完成之前。 |
之前的方法都是在特定 Thread 对象上调用的。Thread 类中的以下方法是静态的。调用其中一个静态方法对当前运行的线程执行该操作。
Sr.No. |
Method & Description |
1 |
public static void yield() 导致当前正在运行的线程向正在等待调度的相同优先级的任何其他线程让步。 |
2 |
public static void sleep(long millisec) 导致正在运行的当前线程阻塞,至少在指定毫秒数的时间内。 |
3 |
public static boolean holdsLock(Object x) 如果当前线程持有给定 Object 的锁,则返回 true。 |
4 |
public static Thread currentThread() 返回对当前正在运行的线程的引用,该线程调用此方法。 |
5 |
public static void dumpStack() 打印当前正在运行的线程的堆栈跟踪,这对在调试多线程应用程序时非常有用。 |
Example
以下 ThreadClassDemo 程序演示了 Thread 类的其中一些方法。考虑一下 DisplayMessage 类,它实现了 Runnable −
// File Name : DisplayMessage.java
// Create a thread to implement Runnable
public class DisplayMessage implements Runnable {
private String message;
public DisplayMessage(String message) {
this.message = message;
}
public void run() {
while(true) {
System.out.println(message);
}
}
}
以下是另一个扩展 Thread 类的类−
// File Name : GuessANumber.java
// Create a thread to extentd Thread
public class GuessANumber extends Thread {
private int number;
public GuessANumber(int number) {
this.number = number;
}
public void run() {
int counter = 0;
int guess = 0;
do {
guess = (int) (Math.random() * 100 + 1);
System.out.println(this.getName() + " guesses " + guess);
counter++;
} while(guess != number);
System.out.println("** Correct!" + this.getName() + "in" + counter + "guesses.**");
}
}
以下是使用上述定义类的主程序−
// File Name : ThreadClassDemo.java
public class ThreadClassDemo {
public static void main(String [] args) {
Runnable hello = new DisplayMessage("Hello");
Thread thread1 = new Thread(hello);
thread1.setDaemon(true);
thread1.setName("hello");
System.out.println("Starting hello thread...");
thread1.start();
Runnable bye = new DisplayMessage("Goodbye");
Thread thread2 = new Thread(bye);
thread2.setPriority(Thread.MIN_PRIORITY);
thread2.setDaemon(true);
System.out.println("Starting goodbye thread...");
thread2.start();
System.out.println("Starting thread3...");
Thread thread3 = new GuessANumber(27);
thread3.start();
try {
thread3.join();
} catch (InterruptedException e) {
System.out.println("Thread interrupted.");
}
System.out.println("Starting thread4...");
Thread thread4 = new GuessANumber(75);
thread4.start();
System.out.println("main() is ending...");
}
}
class DisplayMessage implements Runnable {
private String message;
public DisplayMessage(String message) {
this.message = message;
}
public void run() {
while(true) {
System.out.println(message);
}
}
}
class GuessANumber extends Thread {
private int number;
public GuessANumber(int number) {
this.number = number;
}
public void run() {
int counter = 0;
int guess = 0;
do {
guess = (int) (Math.random() * 100 + 1);
System.out.println(this.getName() + " guesses " + guess);
counter++;
} while(guess != number);
System.out.println("** Correct!" + this.getName() + "in" + counter + "guesses.**");
}
}
Java - Thread Synchronization
当我们在程序中启动两个或更多线程时,可能会出现多种线程尝试访问相同资源的情况,最终它们由于并发问题而产生无法预料的结果。例如,如果多个线程尝试写入相同文件,则它们可能会破坏数据,因为其中一个线程可以覆盖数据,或者在一个线程打开相同文件的同时,另一个线程可能关闭相同文件。
因此,需要同步多个线程的动作并确保仅有一个线程可以在给定的时间点访问该资源。这是通过一个称为 monitors 的概念来实现的。Java 中的每个对象都与一个监视器关联,线程可以锁定或解锁该监视器。一次只能有一个线程持有对一个监视器的锁定。
Java 编程语言提供了一种使用 synchronized 块创建线程并同步它们的 task 的非常便捷的方式。你将共享的资源保留在此块内。以下是同步语句的一般形式−
Syntax
synchronized(objectidentifier) {
// Access shared variables and other shared resources
}
此处,objectidentifier 是对一个对象的引用,其锁与同步语句所表示的监视器相关联。现在,我们将看到两个示例,其中我们将使用两个不同的线程打印计数器。当线程不同步时,它们会打印不是按顺序的计数器值,但是当我们通过将代码放入 synchronized() 块内打印计数器时,则对于这两个线程都会非常顺序地打印计数器。
Multithreading Example without Synchronization
这是一个简单的示例,它可能并不会按顺序打印 counter 的值,而且每次运行都会基于线程对 CPU 的可用性产生不同的结果。
Example
class PrintDemo {
public void printCount() {
try {
for(int i = 5; i > 0; i--) {
System.out.println("Counter --- " + i );
}
} catch (Exception e) {
System.out.println("Thread interrupted.");
}
}
}
class ThreadDemo extends Thread {
private Thread t;
private String threadName;
PrintDemo PD;
ThreadDemo( String name, PrintDemo pd) {
threadName = name;
PD = pd;
}
public void run() {
PD.printCount();
System.out.println("Thread " + threadName + " exiting.");
}
public void start () {
System.out.println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
public static void main(String args[]) {
PrintDemo PD = new PrintDemo();
ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD );
ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD );
T1.start();
T2.start();
// wait for threads to end
try {
T1.join();
T2.join();
} catch ( Exception e) {
System.out.println("Interrupted");
}
}
}
每次运行该程序都会产生不同的结果——
Multithreading Example with Synchronization
这里有一个在序列中打印 counter 值的相同示例,我们每次运行它时,它都会产生相同的结果。
Example
class PrintDemo {
public void printCount() {
try {
for(int i = 5; i > 0; i--) {
System.out.println("Counter --- " + i );
}
} catch (Exception e) {
System.out.println("Thread interrupted.");
}
}
}
class ThreadDemo extends Thread {
private Thread t;
private String threadName;
PrintDemo PD;
ThreadDemo( String name, PrintDemo pd) {
threadName = name;
PD = pd;
}
public void run() {
synchronized(PD) {
PD.printCount();
}
System.out.println("Thread " + threadName + " exiting.");
}
public void start () {
System.out.println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
public static void main(String args[]) {
PrintDemo PD = new PrintDemo();
ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD );
ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD );
T1.start();
T2.start();
// wait for threads to end
try {
T1.join();
T2.join();
} catch ( Exception e) {
System.out.println("Interrupted");
}
}
}
每次运行此程序都会产生相同的结果 −
Java - Interthread Communication
如果您了解进程间通信,那么您将很容易理解线程间通信。当您开发两个或更多线程交换某些信息的应用程序时,线程间通信非常重要。
有三个简单的方法和一个使线程通信成为可能的小技巧。所有三个方法如下所列 −
Sr.No. |
Method & Description |
1 |
public void wait() 导致当前线程等待,直至另一个线程调用 notify()。 |
2 |
public void notify() 唤醒正在此对象的监视器上等待的单个线程。 |
3 |
public void notifyAll() 唤醒所有在相同的对象上调用 wait( ) 的线程。 |
这些方法已作为 final 中的对象方法实现,因此在所有类中均可用。所有三个方法只能在 synchronized 上下文中调用。
Example
此示例显示了两个线程如何使用 wait() 和 notify() 方法进行通信。您可以使用相同概念创建复杂系统。
class Chat {
boolean flag = false;
public synchronized void Question(String msg) {
if (flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(msg);
flag = true;
notify();
}
public synchronized void Answer(String msg) {
if (!flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(msg);
flag = false;
notify();
}
}
class T1 implements Runnable {
Chat m;
String[] s1 = { "Hi", "How are you ?", "I am also doing fine!" };
public T1(Chat m1) {
this.m = m1;
new Thread(this, "Question").start();
}
public void run() {
for (int i = 0; i < s1.length; i++) {
m.Question(s1[i]);
}
}
}
class T2 implements Runnable {
Chat m;
String[] s2 = { "Hi", "I am good, what about you?", "Great!" };
public T2(Chat m2) {
this.m = m2;
new Thread(this, "Answer").start();
}
public void run() {
for (int i = 0; i < s2.length; i++) {
m.Answer(s2[i]);
}
}
}
public class TestThread {
public static void main(String[] args) {
Chat m = new Chat();
new T1(m);
new T2(m);
}
}
Java - Thread Deadlock
死锁描述了两个或多个线程被永久阻塞的情况,等待彼此。当多个线程需要相同锁但按不同顺序获取它们时,会发生死锁。Java 多线程程序可能会受到死锁条件的影响,因为 synchronized 关键字会导致执行线程在等待与指定对象关联的锁或监视器的同时阻塞。这里有一个例子。
Example
public class TestThread {
public static Object Lock1 = new Object();
public static Object Lock2 = new Object();
public static void main(String args[]) {
ThreadDemo1 T1 = new ThreadDemo1();
ThreadDemo2 T2 = new ThreadDemo2();
T1.start();
T2.start();
}
private static class ThreadDemo1 extends Thread {
public void run() {
synchronized (Lock1) {
System.out.println("Thread 1: Holding lock 1...");
try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("Thread 1: Waiting for lock 2...");
synchronized (Lock2) {
System.out.println("Thread 1: Holding lock 1 & 2...");
}
}
}
}
private static class ThreadDemo2 extends Thread {
public void run() {
synchronized (Lock2) {
System.out.println("Thread 2: Holding lock 2...");
try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("Thread 2: Waiting for lock 1...");
synchronized (Lock1) {
System.out.println("Thread 2: Holding lock 1 & 2...");
}
}
}
}
}
当您编译并执行上述程序时,您会发现死锁情况,以下是程序产生的输出 −
Output
Thread 1: Holding lock 1...
Thread 2: Holding lock 2...
Thread 1: Waiting for lock 2...
Thread 2: Waiting for lock 1...
上述程序将永久挂起,因为该位置的线程既无法进行,又等待彼此释放锁,所以您可以通过按 CTRL+C 退出程序。
Deadlock Solution Example
让我们更改锁的顺序并运行相同的程序,看看这两个线程是否仍在互相等待 −
Example
public class TestThread {
public static Object Lock1 = new Object();
public static Object Lock2 = new Object();
public static void main(String args[]) {
ThreadDemo1 T1 = new ThreadDemo1();
ThreadDemo2 T2 = new ThreadDemo2();
T1.start();
T2.start();
}
private static class ThreadDemo1 extends Thread {
public void run() {
synchronized (Lock1) {
System.out.println("Thread 1: Holding lock 1...");
try {
Thread.sleep(10);
} catch (InterruptedException e) {}
System.out.println("Thread 1: Waiting for lock 2...");
synchronized (Lock2) {
System.out.println("Thread 1: Holding lock 1 & 2...");
}
}
}
}
private static class ThreadDemo2 extends Thread {
public void run() {
synchronized (Lock1) {
System.out.println("Thread 2: Holding lock 1...");
try {
Thread.sleep(10);
} catch (InterruptedException e) {}
System.out.println("Thread 2: Waiting for lock 2...");
synchronized (Lock2) {
System.out.println("Thread 2: Holding lock 1 & 2...");
}
}
}
}
}
因此,仅仅更改锁的顺序就可以防止程序进入死锁情况,并完成以下结果 −
Java - Thread Control
核心 Java 对多线程程序提供了完全控制。您可以开发多线程程序,根据您的要求完全暂停、恢复或停止。有各种静态方法,您可以在线程对象上使用它们来控制它们的 behavior。下表列出了这些方法 −
Sr.No. |
Method & Description |
1 |
public void suspend() 此方法会使一个线程进入挂起状态,可以使用 resume() 方法恢复。 |
2 |
public void stop() 此方法会完全停止一个线程。 |
3 |
public void resume() 此方法会恢复一个线程,该线程使用 suspend() 方法挂起。 |
4 |
public void wait() 导致当前线程等待,直至另一个线程调用 notify()。 |
5 |
public void notify() 唤醒正在此对象的监视器上等待的单个线程。 |
注意,Java 的最新版本已声明 suspend( )、resume( ) 和 stop( ) 方法已弃用,因此你需要使用可用的替代方法。
Example
class RunnableDemo implements Runnable {
public Thread t;
private String threadName;
boolean suspended = false;
RunnableDemo( String name) {
threadName = name;
System.out.println("Creating " + threadName );
}
public void run() {
System.out.println("Running " + threadName );
try {
for(int i = 10; i > 0; i--) {
System.out.println("Thread: " + threadName + ", " + i);
// Let the thread sleep for a while.
Thread.sleep(300);
synchronized(this) {
while(suspended) {
wait();
}
}
}
} catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
}
public void start () {
System.out.println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
void suspend() {
suspended = true;
}
synchronized void resume() {
suspended = false;
notify();
}
}
public class TestThread {
public static void main(String args[]) {
RunnableDemo R1 = new RunnableDemo( "Thread-1");
R1.start();
RunnableDemo R2 = new RunnableDemo( "Thread-2");
R2.start();
try {
Thread.sleep(1000);
R1.suspend();
System.out.println("Suspending First Thread");
Thread.sleep(1000);
R1.resume();
System.out.println("Resuming First Thread");
R2.suspend();
System.out.println("Suspending thread Two");
Thread.sleep(1000);
R2.resume();
System.out.println("Resuming thread Two");
} catch (InterruptedException e) {
System.out.println("Main thread Interrupted");
}try {
System.out.println("Waiting for threads to finish.");
R1.t.join();
R2.t.join();
} catch (InterruptedException e) {
System.out.println("Main thread Interrupted");
}
System.out.println("Main thread exiting.");
}
}
以上程序产生以下输出 −
Output
Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 10
Running Thread-2
Thread: Thread-2, 10
Thread: Thread-1, 9
Thread: Thread-2, 9
Thread: Thread-1, 8
Thread: Thread-2, 8
Thread: Thread-1, 7
Thread: Thread-2, 7
Suspending First Thread
Thread: Thread-2, 6
Thread: Thread-2, 5
Thread: Thread-2, 4
Resuming First Thread
Suspending thread Two
Thread: Thread-1, 6
Thread: Thread-1, 5
Thread: Thread-1, 4
Thread: Thread-1, 3
Resuming thread Two
Thread: Thread-2, 3
Waiting for threads to finish.
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.
Main thread exiting.
Java - Networking
网络编程一词是指编写跨多台设备(计算机)执行的程序,其中这些设备都是通过网络彼此连接的。
J2SE API 的 java.net 包含一组类和接口,用于提供低级通信细节,让您可以编写专注于解决当前问题的程序。
java.net 包提供对两种常见网络协议的支持 −
-
TCP − TCP 是传输控制协议的缩写,它允许两个应用程序之间进行可靠的通信。TCP 通常通过网际协议使用,称为 TCP/IP。
-
UDP − UDP 是用户数据报协议的缩写,它是一种无连接协议,允许在应用程序之间传输数据包。
本章对以下两个主题进行了很好的阐述 −
-
Socket Programming − 这是网络中最广泛使用的概念,它已被非常详细地解释。
-
URL Processing - 这将单独进行介绍。单击此处以了解 Java 语言中的 URL Processing。
Socket Programming
套接字使用 TCP 在两台计算机之间提供通信机制。客户端程序在其通信端创建套接字,并尝试将该套接字连接到服务器。
建立连接时,服务器在其通信端创建一个套接字对象。现在,客户端和服务器可以通过向套接字写入和从套接字读取来进行通信。
java.net.Socket 类表示一个套接字,而 java.net.ServerSocket 类为服务器程序提供了一种机制,用于侦听客户端并与它们建立连接。
使用套接字在两台计算机之间建立 TCP 连接时,会发生以下步骤 −
-
服务器实例化一个 ServerSocket 对象,表示通信将在哪个端口号上进行。
-
服务器调用 ServerSocket 类的 accept() 方法。此方法会一直等到客户端通过给定端口连接到服务器。
-
在服务器正在等待后,客户端实例化一个 Socket 对象,指定服务器名称和要连接的端口号。
-
Socket 类的构造函数尝试将客户端连接到指定的服务器和端口号。如果建立了通信,客户端现在就拥有了一个能够与服务器通信的 Socket 对象。
-
在服务器端,accept() 方法返回一个对服务器上与客户端套接字连接的新套接字的引用。
建立连接后,可以使用 I/O 流进行通信。每个套接字都有一个 OutputStream 和一个 InputStream。客户端的 OutputStream 连接到服务器的 InputStream,客户端的 InputStream 连接到服务器的 OutputStream。
TCP 是一个双向通信协议,因此可以同时通过两个流发送数据。以下是提供实现套接字的完整方法集的有用类。
ServerSocket Class Methods
服务器应用程序使用 java.net.ServerSocket 类获取端口并侦听客户端请求。
ServerSocket 类有四个构造函数 −
Sr.No. |
Method & Description |
1 |
public ServerSocket(int port) throws IOException 尝试创建绑定到指定端口的服务器套接字。如果端口已被其他应用程序绑定,则会发生异常。 |
2 |
public ServerSocket(int port, int backlog) throws IOException 与前面的构造函数类似,backlog 参数指定在等待队列中存储多少个传入客户端。 |
3 |
public ServerSocket(int port, int backlog, InetAddress address) throws IOException 与前面的构造函数类似,InetAddress 参数指定要绑定的本地 IP 地址。InetAddress 用于可能具有多个 IP 地址的服务器,它允许服务器指定接受客户端请求的 IP 地址。 |
4 |
public ServerSocket() throws IOException 创建未绑定的服务器套接字。使用此构造函数时,当您准备绑定服务器套接字时,使用 bind() 方法。 |
如果 ServerSocket 构造函数未引发异常,则表示应用程序已成功绑定到指定端口并已准备好接受客户端请求。
以下是 ServerSocket 类的部分常用方法:
Sr.No. |
Method & Description |
1 |
public int getLocalPort() 返回服务器套接字正在侦听的端口。如果您在构造函数中将 0 传递为端口号,并让服务器为您查找端口,则此方法很有用。 |
2 |
public Socket accept() throws IOException 等待传入客户端。此方法阻塞,直到客户端连接到指定端口上的服务器,或者套接字超时(假设已使用 setSoTimeout() 方法设置超时值)。否则,此方法会无限期阻塞。 |
3 |
public void setSoTimeout(int timeout) 设置服务器套接字在 accept() 期间等待客户端的时间限制。 |
4 |
public void bind(SocketAddress host, int backlog) 将套接字绑定到 SocketAddress 对象中指定的服务器和端口。如果你使用无参数构造函数实例化了 ServerSocket,可以使用此方法。 |
当 ServerSocket 调用 accept() 时,该方法在客户端连接之前不会返回。在客户端连接后,ServerSocket会在未指定端口上创建新的 Socket,并返回对该新 Socket 的引用。在客户端和服务器间建立 TCP 连接后,便可开始通信。
Socket Class Methods
java.net.Socket 类表示客户端和服务器用于彼此通信的套接字。客户端通过实例化来获取 Socket 对象,而服务器则通过 accept() 方法的返回值来获取 Socket 对象。
Socket 类有五个构造函数供客户端用于连接到服务器 -
Sr.No. |
Method & Description |
1 |
public Socket(String host, int port) throws UnknownHostException, IOException. 此方法尝试连接到指定端口上的指定服务器。如果此构造函数不抛出异常,则连接成功,并且客户端将连接到服务器。 |
2 |
public Socket(InetAddress host, int port) throws IOException 此方法与前面的构造函数相同,只不过主机由 InetAddress 对象表示。 |
3 |
public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException. 连接到指定的主机和端口,在指定地址和端口上的本地主机上创建一个套接字。 |
4 |
public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException. 此方法与前面的构造函数相同,只不过主机由 InetAddress 对象表示,而不是 String。 |
5 |
public Socket() 创建一个未连接的套接字。使用 connect() 方法将此套接字连接到服务器。 |
当 Socket 构造器返回时,它不仅仅实例化一个 Socket 对象,而且实际上尝试连接到指定的服务器和端口。
此处列出 Socket 类中的一些感兴趣的方法。请注意,客户端和服务器都有一个 Socket 对象,因此客户端和服务器都可以调用这些方法。
Sr.No. |
Method & Description |
1 |
public void connect(SocketAddress host, int timeout) throws IOException 此方法将套接字连接到指定的主机。仅当您使用无参数构造函数实例化 Socket 时才需要此方法。 |
2 |
public InetAddress getInetAddress() 此方法返回此套接字连接到的其他计算机的地址。 |
3 |
public int getPort() 返回套接字在远程计算机上绑定的端口。 |
4 |
public int getLocalPort() 返回套接字在本地计算机上绑定的端口。 |
5 |
public SocketAddress getRemoteSocketAddress() 返回远程套接字的地址。 |
6 |
public InputStream getInputStream() throws IOException 返回套接字的输入流。输入流连接到远程套接字的输出流。 |
7 |
public OutputStream getOutputStream() throws IOException 返回套接字的输出流。输出流连接到远程套接字的输入流。 |
8 |
public void close() throws IOException 关闭套接字,这使得此套接字对象不再能够再次连接到任何服务器。 |
InetAddress Class Methods
此类表示互联网协议 (IP) 地址。在进行套接字编程时,您可能需要使用以下有用方法:
Sr.No. |
Method & Description |
1 |
static InetAddress getByAddress(byte[] addr) 返回给定原始 IP 地址的 InetAddress 对象。 |
2 |
static InetAddress getByAddress(String host, byte[] addr) 根据提供的 host name 和 IP 地址创建 InetAddress。 |
3 |
static InetAddress getByName(String host) 确定主机 IP 地址,给定主机的名称。 |
4 |
String getHostAddress() 以文本形式返回 IP 地址字符串。 |
5 |
String getHostName() 获取此 IP 地址的主机名。 |
6 |
static InetAddress InetAddress getLocalHost() 返回本地主机。 |
7 |
String toString() 将此 IP 地址转换成字符串。 |
Socket Client Example
以下 GreetingClient 是一个客户端程序,它使用套接字连接到服务器并发送问候语,然后等待响应。
Example
// File Name GreetingClient.java
import java.net.*;
import java.io.*;
public class GreetingClient {
public static void main(String [] args) {
String serverName = args[0];
int port = Integer.parseInt(args[1]);
try {
System.out.println("Connecting to " + serverName + " on port " + port);
Socket client = new Socket(serverName, port);
System.out.println("Just connected to " + client.getRemoteSocketAddress());
OutputStream outToServer = client.getOutputStream();
DataOutputStream out = new DataOutputStream(outToServer);
out.writeUTF("Hello from " + client.getLocalSocketAddress());
InputStream inFromServer = client.getInputStream();
DataInputStream in = new DataInputStream(inFromServer);
System.out.println("Server says " + in.readUTF());
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Socket Server Example
以下 GreetingServer 程序是一个服务器应用程序示例,它使用 Socket 类监听命令行参数指定的端口号上的客户端—
Example
// File Name GreetingServer.java
import java.net.*;
import java.io.*;
public class GreetingServer extends Thread {
private ServerSocket serverSocket;
public GreetingServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
serverSocket.setSoTimeout(10000);
}
public void run() {
while(true) {
try {
System.out.println("Waiting for client on port " +
serverSocket.getLocalPort() + "...");
Socket server = serverSocket.accept();
System.out.println("Just connected to " + server.getRemoteSocketAddress());
DataInputStream in = new DataInputStream(server.getInputStream());
System.out.println(in.readUTF());
DataOutputStream out = new DataOutputStream(server.getOutputStream());
out.writeUTF("Thank you for connecting to " + server.getLocalSocketAddress()
+ "\nGoodbye!");
server.close();
} catch (SocketTimeoutException s) {
System.out.println("Socket timed out!");
break;
} catch (IOException e) {
e.printStackTrace();
break;
}
}
}
public static void main(String [] args) {
int port = Integer.parseInt(args[0]);
try {
Thread t = new GreetingServer(port);
t.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
编译客户端和服务器,然后按以下方式启动服务器—
$ java GreetingServer 6066
Waiting for client on port 6066...
按照以下步骤检查客户端程序 −
Java - URL Processing
URL 代表统一资源定位符,表示万维网上的资源,例如网页或 FTP 目录。
此部分向您展示如何编写与 URL 通信的 Java 程序。URL 可以分解为以下部分:
protocol://host:port/path?query#ref
协议的示例包括 HTTP、HTTPS、FTP 和文件。此路径也称为文件名,而主机也称为授权。
以下是一个协议为 HTTP 的网页的 URL:
https://www.amrood.com/index.htm?language=en#j2se
请注意此 URL 未指定端口,在这种情况下将使用该协议的默认端口。对于 HTTP,默认端口为 80。
Constructors
java.net.URL 类表示一个 URL,并具有用于在 Java 中处理 URL 的一组完整方法。
URL 类具有多个构造方法,用于创建 URL,包括以下方法:
Sr.No. |
Constructors & Description |
1 |
public URL(String protocol, String host, int port, String file) throws MalformedURLException 通过组合给定的部分创建 URL。 |
2 |
public URL(String protocol, String host, String file) throws MalformedURLException 与前一个构造函数相同,但使用给定协议的默认端口。 |
3 |
public URL(String url) throws MalformedURLException 从给定的字符串创建 URL。 |
4 |
public URL(URL context, String url) throws MalformedURLException 通过解析 URL 和字符串参数创建 URL。 |
URL 类包含许多用于访问所表示 URL 的各个部分的方法。URL 类中的一些方法包括以下方法:
Sr.No. |
Method & Description |
1 |
public String getPath() 返回 URL 的路径。 |
2 |
public String getQuery() 返回 URL 的查询部分。 |
3 |
public String getAuthority() 返回 URL 的权限。 |
4 |
public int getPort() 返回 URL 的端口。 |
5 |
public int getDefaultPort() 返回 URL 协议的默认端口。 |
6 |
public String getProtocol() 返回 URL 的协议。 |
7 |
public String getHost() 返回 URL 的主机。 |
8 |
public String getHost() 返回 URL 的主机。 |
9 |
public String getFile() 返回 URL 的文件名。 |
10 |
public String getRef() 返回 URL 的引用部分。 |
11 |
public URLConnection openConnection() throws IOException 向 URL 打开连接,允许客户端与资源进行通信。 |
Example
以下 URLDemo 程序演示了 URL 的各个部分。URL 输入在命令行上,URLDemo 程序输出给定 URL 的每个部分。
// File Name : URLDemo.java
import java.io.IOException;
import java.net.URL;
public class URLDemo {
public static void main(String [] args) {
try {
URL url = new URL("https://www.tutorialspoint.com/index.htm?language=en#j2se");
System.out.println("URL is " + url.toString());
System.out.println("protocol is " + url.getProtocol());
System.out.println("authority is " + url.getAuthority());
System.out.println("file name is " + url.getFile());
System.out.println("host is " + url.getHost());
System.out.println("path is " + url.getPath());
System.out.println("port is " + url.getPort());
System.out.println("default port is " + url.getDefaultPort());
System.out.println("query is " + url.getQuery());
System.out.println("ref is " + url.getRef());
} catch (IOException e) {
e.printStackTrace();
}
}
}
此程序的示例运行将生成以下结果 −
URLConnections Class Methods
openConnection() 方法返回 java.net.URLConnection,其子类代表各种类型的 URL 连接的抽象类。
例如 -
-
如果您连接到协议为 HTTP 的 URL,则 openConnection() 方法返回 HttpURLConnection 对象。
-
如果您连接到表示 JAR 文件的 URL,则 openConnection() 方法返回 JarURLConnection 对象,等等。
URLConnection 类具有用于设置或确定有关连接信息的许多方法,包括以下方法 −
Sr.No. |
Method & Description |
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() 返回 expired 头字段的值。 |
8 |
long getIfModifiedSince() 返回此对象的 ifModifiedSince 字段的值。 |
9 |
public void setDoInput(boolean input) 传入 true 表示将使用该连接进行输入。默认值为 true,因为客户端通常从 URLConnection 中读取。 |
10 |
public void setDoOutput(boolean output) 参数为 true 时表示该连接将用于输出。默认值为 false,因为许多类型的 URL 不支持写入。 |
11 |
public InputStream getInputStream() throws IOException 返回 URL 连接的输入流,以从资源中进行读取。 |
12 |
public OutputStream getOutputStream() throws IOException 返回 URL 连接的输出流,以写入到资源中。 |
13 |
public URL getURL() 返回此 URLConnection 对象连接到的 URL。 |
Example
以下 URLConnectionDemo 程序连接到从命令行输入的 URL。
如果 URL 表示 HTTP 资源,则连接会被强制转换为 HttpURLConnection,并且资源中的数据会被逐行读取。
// File Name : URLConnDemo.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
public static void main(String [] args) {
try {
URL url = new URL("https://www.tutorialspoint.com");
URLConnection urlConnection = url.openConnection();
HttpURLConnection connection = null;
if(urlConnection instanceof HttpURLConnection) {
connection = (HttpURLConnection) urlConnection;
}else {
System.out.println("Please enter an HTTP URL.");
return;
}
BufferedReader in = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
String urlString = "";
String current;
while((current = in.readLine()) != null) {
urlString += current;
}
System.out.println(urlString);
} catch (IOException e) {
e.printStackTrace();
}
}
}
此程序的示例运行将产生以下结果:
Java - Generics
如果能够编写一个单一的 sort 方法对 Integer 数组、String 数组或支持排序的任何类型的数组中的元素进行排序,那就太好了。
Java Generic 方法和泛型类使程序员能够通过单个方法声明指定一组相关方法,或通过单个类声明指定一组相关类型。
泛型还提供编译时类型安全性,使程序员能够在编译时捕捉无效的类型。
使用 Java 泛型概念,我们可以为对象数组编写一个泛型方法,然后使用 Integer 数组、Double 数组、String 数组等调用泛型方法,对数组元素进行排序。
Generic Methods
你可以编写一个可以针对不同类型参数调用的单个泛型方法声明。根据传递给泛型方法的参数类型,编译器会分别处理每个方法调用。以下是定义泛型方法的规则:
-
所有泛型方法声明都具有一个类型参数部分,该部分用尖括号(< 和 >)分隔,位于方法的返回类型之前(在下一个示例中为 < E >)。
-
每个类型参数部分包含一个或多个以逗号分隔的类型参数。类型参数(也称为类型变量)是一个指定泛型类型名称的标识符。
-
类型参数可用于声明返回类型并充当传递给泛型方法的参数类型的占位符,这些参数被称为实际类型参数。
-
泛型方法的 body 的声明方式与其他方法相同。请注意,类型参数只能表示引用类型,不能表示基本类型(如 int、double 和 char)。
Example
以下示例说明了如何使用单个泛型方法打印不同类型的数组 -
public class GenericMethodTest {
// generic method printArray
public static < E > void printArray( E[] inputArray ) {
// Display array elements
for(E element : inputArray) {
System.out.printf("%s ", element);
}
System.out.println();
}
public static void main(String args[]) {
// Create arrays of Integer, Double and Character
Integer[] intArray = { 1, 2, 3, 4, 5 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
System.out.println("Array integerArray contains:");
printArray(intArray); // pass an Integer array
System.out.println("\nArray doubleArray contains:");
printArray(doubleArray); // pass a Double array
System.out.println("\nArray characterArray contains:");
printArray(charArray); // pass a Character array
}
}
Bounded Type Parameters
有时您可能希望限制允许传递给类型参数的类型种类。例如,对数字进行操作的方法可能只希望接受 Number 或其子类的实例。这就是有界类型参数的作用。
要声明有界类型参数,请列出类型参数的名称,后跟 extends 关键字,后跟其上限。
Example
以下示例说明了如何以常规意义使用 extends 表示“extends”(如在类中)或“implements”(如在接口中)。此示例是用于返回三个 Comparable 对象中最大的对象的泛型方法 -
public class MaximumTest {
// determines the largest of three Comparable objects
public static <T extends Comparable<T>> T maximum(T x, T y, T z) {
T max = x; // assume x is initially the largest
if(y.compareTo(max) > 0) {
max = y; // y is the largest so far
}
if(z.compareTo(max) > 0) {
max = z; // z is the largest now
}
return max; // returns the largest object
}
public static void main(String args[]) {
System.out.printf("Max of %d, %d and %d is %d\n\n",
3, 4, 5, maximum( 3, 4, 5 ));
System.out.printf("Max of %.1f,%.1f and %.1f is %.1f\n\n",
6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ));
System.out.printf("Max of %s, %s and %s is %s\n","pear",
"apple", "orange", maximum("pear", "apple", "orange"));
}
}
Generic Classes
泛型类声明看起来像非泛型类声明,除了类名后跟类型参数部分。
与泛型方法一样,泛型类的类型参数部分可以有多个类型参数,用逗号分隔。这些类被称为参数化类或参数化类型,因为它们接受一个或多个参数。
Example
以下示例说明了如何定义泛型类 -
public class Box<T> {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
public static void main(String[] args) {
Box<Integer> integerBox = new Box<Integer>();
Box<String> stringBox = new Box<String>();
integerBox.add(new Integer(10));
stringBox.add(new String("Hello World"));
System.out.printf("Integer Value :%d\n\n", integerBox.get());
System.out.printf("String Value :%s\n", stringBox.get());
}
}
Java - Collections Framework
在 Java 2 之前,Java 提供了诸如 Dictionary, Vector, Stack, 和 Properties 等特殊类来存储和操作对象组。尽管这些类非常有用,但它们缺少一个中心统一的主题。因此,您使用 Vector 的方式与您使用 Properties 的方式不同。
集合框架旨在满足若干目标,例如-
-
该框架必须是高性能的。基本集合(动态数组、链表、树和散列表)的实现必须是高效的。
-
该框架必须允许不同类型的集合以相似的方式,并具有高度可互操作性。
-
该框架必须轻松扩展和/或调整集合。
为此目的,整个 collections 框架围绕一组标准接口进行设计。提供 LinkedList, HashSet, 和 TreeSet 等这些接口的几个标准实现,您可以按原样使用它们,也可以根据需要实现自己的集合。
集合框架是一个用于表示和操作集合的统一体系结构。所有集合框架包含下列内容:
-
Interfaces − 这些是表示集合的抽象数据类型。接口允许独立于它们的表示的详细信息来操纵集合。在面向对象语言中,接口通常形成一个层次结构。
-
Implementations, i.e., Classes − 这些是集合接口的具体实现。从本质上讲,它们是可重用的数据结构。
-
Algorithms,这些方法执行有用的计算,例如在实现集合接口的对象上进行搜索和排序。这些算法被称为多态的:也就是说,可以在适当集合接口的许多不同实现上使用相同的方法。
除了集合之外,该框架还定义了几个映射接口和类。映射存储键值对。虽然在该术语的正确使用中,映射不是集合,但它们与集合完全集成。
The Collection Interfaces
集合框架定义了几个接口。本节概述了每个接口:
Sr.No. |
Interface & Description |
1 |
*集合接口*这使得你能够处理一组对象;它位于集合层次结构的顶部。 |
2 |
The List Interface 这个扩展了 Collection ,List 的一个实例存储元素的有序集合。 |
3 |
The Set 这个扩展了 Collection 去处理 Set,Set 必须包含唯一元素。 |
4 |
The SortedSet 这个扩展了 Set 去处理已排序的 Set。 |
5 |
The Map 这个将唯一键映射到值。 |
6 |
The Map.Entry 这个在映射中描述一个元素(一个键/值对)。这是 Map 的内部类。 |
7 |
The SortedMap 该方法扩展了 Map,以便按升序维护键。 |
8 |
The Enumeration 这个是旧版接口,它定义了在对象集合中枚举(一次获取一个)元素的方法。这个旧版接口已经被 Iterator 取代了。 |
The Collection Classes
Java 提供了一组实现 Collection 接口的标准集合类。有些类提供了可按原样使用的完整实现,而另一些类是抽象类,提供用作创建具体集合的起点的骨架实现。
标准集合类总结在以下表中 −
Sr.No. |
Class & Description |
1 |
AbstractCollection 实现了 Collection 接口的大部分方法。 |
2 |
AbstractList 扩展了 AbstractCollection,实现了 List 接口的大部分方法。 |
3 |
AbstractSequentialList 扩展了 AbstractList,由顺序访问而不是随机访问其元素的集合使用。 |
4 |
AbstractSet 扩展了 AbstractCollection,并实现了大部分 Set 接口。 |
5 |
AbstractMap 实现了大部分 Map 接口。 |
AbstractCollection、AbstractSet、AbstractList、AbstractSequentialList 和 AbstractMap 类提供了核心集合接口的基本实现,以最大程度减少实现这些接口所需的工作量。
java.util 定义的以下旧类已在上一章中讨论过−
Sr.No. |
Class & Description |
1 |
Queue 队列接口在 java.util 包中提供,它实现了集合接口。队列实现了 FIFO,即先进先出。这意味着先输入的元素是最先删除的元素。 |
The Collection Algorithms
集合框架定义了可以应用于集合和映射的几种算法。这些算法在 Collections 类中被定义为静态方法。
其中有些方法会抛出 ClassCastException,当尝试比较不兼容的类型时会发生 ClassCastException 错误,当尝试修改不可修改集合时会发生 UnsupportedOperationException 错误。
集合定义了三个静态变量:EMPTY_SET、EMPTY_LIST 和 EMPTY_MAP。所有都是不可变的。
Sr.No. |
Algorithm & Description |
1 |
The Collection Algorithms 以下是所有算法实现的列表。 |
How to Use an Iterator ?
通常,您会希望在集合中的元素间循环。例如,您可能希望显示每个元素。
最简单的做法是使用迭代器,它是一个实现了 Iterator 或 ListIterator 接口的对象。
迭代器使您能够在集合中循环,获取或删除元素。ListIterator 扩展了 Iterator,以允许双向遍历列表并修改元素。
Sr.No. |
Iterator Method & Description |
1 |
Using Java Iterator 以下是迭代器和列表迭代器接口提供的方法列表,其中包含示例。 |
How to Use a Comparator ?
TreeSet 和 TreeMap 都将元素存储在排序顺序中。但是,正是比较器精确地定义了排序顺序的含义。
该接口允许我们对给定集合进行任意次数的不同方式的排序。此接口还可以用于对任何类的任何实例(包括我们无法修改的类)进行排序。
Sr.No. |
Iterator Method & Description |
1 |
Using Java Comparator 以下是比较器接口提供的方法列表,其中包含示例。 |
How to Use a Comparable ?
TreeSet 和 TreeMap 都按顺序存储元素。我们可以使用 Comparable 接口精确定义排序顺序的意思。
该接口允许我们对给定集合进行任意次数的不同方式的排序。此接口还可以用于对任何类的任何实例(包括我们无法修改的类)进行排序。
Sr.No. |
Iterator Method & Description |
1 |
Using Java Comparable 以下是可比较接口提供的方法列表,其中包含示例。 |
Java - The List Interface
List 接口扩展 Collection 并声明存储元素序列的集合的行为。
-
可以通过使用基于零的索引在其在列表中的位置来插入或访问这些元素。
-
列表可能包含重复元素。
-
除了 Collection 定义的方法外,List 还定义了一些自己的方法,这些方法在下一表格中进行了总结。
-
列表方法中有几个会抛出 UnsupportedOperationException,如果集合不可修改,且一个对象与另一个对象不兼容时会生成 ClassCastException。
Sr.No. |
Method & Description |
1 |
void add(int index, Object obj) 在传入 index 中的 index 处将 obj 插入调用列表。插入点或其之后的任何现有元素都会向上移动。因此,不会覆盖任何元素。 |
2 |
boolean addAll(int index, Collection c) 将 c 的所有元素插入调用列表中传入 index 的 index 处。插入点或其之后的任何现有元素都会向上移动。因此,不会覆盖任何元素。如果调用列表发生更改,则返回 true,否则返回 false。 |
3 |
Object get(int index) 返回存储在调用集合中指定 index 处的对象。 |
4 |
int indexOf(Object obj) 返回 obj 在调用列表中第一个实例的 index。如果 obj 不是列表中的元素,则返回 -1。 |
5 |
int lastIndexOf(Object obj) 返回 obj 在调用列表中最后一个实例的 index。如果 obj 不是列表中的元素,则返回 -1。 |
6 |
ListIterator listIterator( ) 返回调用列表开始处的迭代器。 |
7 |
ListIterator listIterator(int index) 返回从指定 index 开始的调用列表的迭代器。 |
8 |
Object remove(int index) 从调用列表中删除位置 index 处的元素,并返回已删除的元素。结果列表会进行压缩。也就是说,后续元素的 index 会递减一。 |
9 |
Object set(int index, Object obj) 将 obj 分配给调用列表中 index 指定的位置。 |
10 |
List subList(int start, int end) 返回一个列表,其中包含调用列表中 start 到 end-1 的元素。已返回列表中的元素也由调用对象引用。 |
Example 1
上述接口已使用 ArrayList 实现。以下是使用上述集合方法中各个类实现的一示例,用于解释一些方法:
import java.util.ArrayList;
import java.util.List;
public class CollectionsDemo {
public static void main(String[] args) {
List<String> a1 = new ArrayList<>();
a1.add("Zara");
a1.add("Mahnaz");
a1.add("Ayan");
System.out.println(" ArrayList Elements");
System.out.print("\t" + a1);
}
}
Example 2
上述接口已使用 LinkedList 实现。以下是使用上述集合方法中各个类实现的一示例,用于解释一些方法:
import java.util.LinkedList;
import java.util.List;
public class CollectionsDemo {
public static void main(String[] args) {
List<String> a1 = new LinkedList<>();
a1.add("Zara");
a1.add("Mahnaz");
a1.add("Ayan");
System.out.println(" LinkedList Elements");
System.out.print("\t" + a1);
}
}
Example 3
上述接口已使用 ArrayList 实现。以下是另一个示例,用于使用上述集合方法中各个类实现的一示例,用于解释一些方法:
import java.util.ArrayList;
import java.util.List;
public class CollectionsDemo {
public static void main(String[] args) {
List<String> a1 = new ArrayList<>();
a1.add("Zara");
a1.add("Mahnaz");
a1.add("Ayan");
System.out.println(" ArrayList Elements");
System.out.print("\t" + a1);
// remove second element
a1.remove(1);
System.out.println("\n ArrayList Elements");
System.out.print("\t" + a1);
}
}
Java - Queue Interface
Queue 接口在 java.util 包中提供,它实现了集合接口。队列实现 FIFO,即先入先出。这意味着首先输入的元素是首先被删除的元素。队列通常用于在处理元素之前保存元素。一旦处理了一个元素,它就会从队列中删除,并且下一个元素将被选中进行处理。
Queue Methods
以下是队列接口的所有实现类实现的重要队列方法的列表 −
Sr.No. |
Method & Description |
1 |
boolean add(E e) 如果没有违反容量限制,则此方法会将指定元素插入到此队列中,在插入成功时返回 true,并且在当前没有可用空间时引发 IllegalStateException。 |
2 |
E element() 此方法检索但不删除此队列的头部。 |
3 |
boolean offer(E e) 此方法向此队列中插入指定元素,如果立即进行此操作而不会违反容量限制,则可以进行此操作。 |
4 |
E peek() 此方法检索但不删除此队列的头部,如果此队列为空,则返回 null。 |
5 |
E poll() 此方法检索并删除此队列的头部,如果此队列为空,则返回 null。 |
6 |
E remove() 此方法检索并删除此队列的头部。 |
Example
在此示例中,我们使用 LinkedList 实例来显示队列添加、窥视和大小操作。
package com.tutorialspoint;
import java.util.LinkedList;
import java.util.Queue;
public class QueueDemo {
public static void main(String[] args) {
Queue<Integer> q = new LinkedList<>();
q.add(6);
q.add(1);
q.add(8);
q.add(4);
q.add(7);
System.out.println("The queue is: " + q);
int num1 = q.remove();
System.out.println("The element deleted from the head is: " + num1);
System.out.println("The queue after deletion is: " + q);
int head = q.peek();
System.out.println("The head of the queue is: " + head);
int size = q.size();
System.out.println("The size of the queue is: " + size);
}
}
Java - The Map Interface
Map 接口将唯一密钥映射到值。密钥是您用来稍后检索值的某个对象。
-
给定键和值,您可以将其存储在 Map 对象中。存储值后,您可以使用其键来获取该值。
-
当调用映射中不存在任何项时,一些方法会引发 NoSuchElementException。
-
当一个对象与映射中的元素不兼容时,将引发 ClassCastException。
-
当试图使用 null 对象且 map 中不允许 null 时,抛出 NullPointerException。
-
当试图更改不可修改的映射时,抛出 UnsupportedOperationException。
Sr.No. |
Method & Description |
1 |
void clear( ) 从引发映射中移除所有键/值对。 |
2 |
boolean containsKey(Object k) 如果引发映射包含 k 作为一个键,则返回 true。否则,返回 false。 |
3 |
boolean containsValue(Object v) 如果映射包含 v 作为一个值,则返回 true。否则,返回 false。 |
4 |
Set entrySet( ) 返回包含地图中条目的集合。该集合包含 Map.Entry 类型的对象。此方法提供了一个对调用地图的集合视图。 |
5 |
boolean equals(Object obj) 如果 obj 是一个 Map 且包含相同的条目,则返回 true。否则,返回 false。 |
6 |
Object get(Object k) 返回与键 k 关联的值。 |
7 |
int hashCode( ) 返回调用映射的哈希代码。 |
8 |
boolean isEmpty( ) 如果调用映射为空,则返回 true。否则,返回 false。 |
9 |
Set keySet( ) 返回一个包含调用映射中键的集合。此方法提供对调用映射中键的集合视图。 |
10 |
Object put(Object k, Object v) 在调用映射中添加一个条目,覆盖与键关联的任何先前值。键和值分别是 k 和 v。如果键不存在,则返回 null。否则,返回链接到该键的先前值。 |
11 |
void putAll(Map m) 将 m 中的所有条目都放入此映射中。 |
12 |
Object remove(Object k) 删除其键等于 k 的条目。 |
13 |
int size( ) 返回映射中键/值对的数量。 |
14 |
Collection values( ) 返回一个包含映射中值的集合。此方法提供对映射中值的集合视图。 |
Example 1
Map 在各种类中都有其实现,如 HashMap。以下是如何解释地图功能的一个示例 −
import java.util.HashMap;
import java.util.Map;
public class CollectionsDemo {
public static void main(String[] args) {
Map<String, String> m1 = new HashMap<>();
m1.put("Zara", "8");
m1.put("Mahnaz", "31");
m1.put("Ayan", "12");
m1.put("Daisy", "14");
System.out.println();
System.out.println(" Map Elements");
System.out.print("\t" + m1);
}
}
Example 2
Map 在各种类中都有其实现,如 TreeMap,它基于密钥对条目进行排序。以下是如何使用 TreeMap 解释地图功能的示例 −
import java.util.Map;
import java.util.TreeMap;
public class CollectionsDemo {
public static void main(String[] args) {
Map<String, String> m1 = new TreeMap<>();
m1.put("Zara", "8");
m1.put("Mahnaz", "31");
m1.put("Ayan", "12");
m1.put("Daisy", "14");
System.out.println();
System.out.println(" Map Elements");
System.out.print("\t" + m1);
}
}
Example 3
映射在各种类中有着其实现,例如 HashMap。下面举例说明了使用 HashMap 来向映射添加和移除元素的映射功能:
import java.util.HashMap;
import java.util.Map;
public class CollectionsDemo {
public static void main(String[] args) {
Map<String, String> m1 = new HashMap<>();
m1.put("Zara", "8");
m1.put("Mahnaz", "31");
m1.put("Ayan", "12");
m1.put("Daisy", "14");
System.out.println();
System.out.println(" Map Elements");
System.out.print("\t" + m1);
m1.remove("Daisy");
System.out.println(" Map Elements");
System.out.print("\t" + m1);
}
}
Java - The SortedMap Interface
SortedMap 接口扩展了 Map。它确保按升序保持条目。
当调用映射中没有项时,多个方法会抛出一个 NoSuchElementException 异常。当对象与地图中的元素不兼容时,会抛出一个 ClassCastException 异常。如果尝试在不允许地图中的 null 时使用 null 对象,会抛出一个 NullPointerException 异常。
SortedMap 声明的方法汇总在以下表格中 −
Sr.No. |
Method & Description |
1 |
Comparator comparator( ) 返回调用排序映射的比较器。如果为调用映射使用自然排序,则返回 null。 |
2 |
Object firstKey( ) 返回调用映射中的第一个键。 |
3 |
SortedMap headMap(Object end) 返回键小于 end 的那些映射项的排序映射。 |
4 |
Object lastKey( ) 返回调用映射中的最后一个键。 |
5 |
SortedMap subMap(Object start, Object end) 返回一个映射,其中包含那些键大于或等于 start 且小于 end 的项。 |
6 |
SortedMap tailMap(Object start) 返回一个映射,其中包含那些键大于或等于 start 的项。 |
Example 1
以下是一个显示 TreeMap 如何用于获取 SortedMap 值的示例 -
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
public class MapDemo {
public static void main(String args[]) {
// Create a hash map
SortedMap<String, Double> map = new TreeMap<>();
// Put elements to the map
map.put("Zara", Double.valueOf(3434.34));
map.put("Mahnaz", Double.valueOf(123.22));
map.put("Ayan", Double.valueOf(1378.00));
map.put("Daisy", Double.valueOf(99.22));
map.put("Qadir", Double.valueOf(-19.08));
// Get a set of the entries
Set<Map.Entry<String, Double>> set = map.entrySet();
// Get an iterator
Iterator<Map.Entry<String, Double>> i = set.iterator();
// Display elements
while(i.hasNext()) {
Map.Entry<String, Double> me = i.next();
System.out.print(me.getKey() + ": ");
System.out.println(me.getValue());
}
}
}
Example 2
以下是一个显示 TreeMap 如何用于设置 SortedMap 值的示例 -
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
public class MapDemo {
public static void main(String args[]) {
// Create a hash map
SortedMap<String, Double> map = new TreeMap<>();
// Put elements to the map
map.put("Zara", Double.valueOf(3434.34));
map.put("Mahnaz", Double.valueOf(123.22));
map.put("Ayan", Double.valueOf(1378.00));
map.put("Daisy", Double.valueOf(99.22));
map.put("Qadir", Double.valueOf(-19.08));
// Get a set of the entries
Set<Map.Entry<String, Double>> set = map.entrySet();
// Get an iterator
Iterator<Map.Entry<String, Double>> i = set.iterator();
// Display elements
while(i.hasNext()) {
Map.Entry<String, Double> me = i.next();
me.setValue(me.getValue() * 10);
System.out.print(me.getKey() + ": ");
System.out.println(me.getValue());
}
}
}
Example 3
以下是一个显示 TreeMap 如何用于获取分类映射项的键的示例 -
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
public class MapDemo {
public static void main(String args[]) {
// Create a hash map
SortedMap<String, Double> map = new TreeMap<>();
// Put elements to the map
map.put("Zara", Double.valueOf(3434.34));
map.put("Mahnaz", Double.valueOf(123.22));
map.put("Ayan", Double.valueOf(1378.00));
map.put("Daisy", Double.valueOf(99.22));
map.put("Qadir", Double.valueOf(-19.08));
// Get a set of the entries
Set<Map.Entry<String, Double>> set = map.entrySet();
// Get an iterator
Iterator<Map.Entry<String, Double>> i = set.iterator();
// Display elements
while(i.hasNext()) {
Map.Entry<String, Double> me = i.next();
System.out.println(me.getKey());
}
}
}
Java - The Set Interface
集合是指不能包含重复元素的集合。它模拟了数学集合抽象。
Set 接口仅包含从集合继承的方法,并增加了不允许重复元素的限制。
Set 还对 equals 和 hashCode 操作的行为添加了一个更强的契约,即使 Set 实例的实现类型不同,也能对它们进行有意义的比较。
Set 声明的方法在以下表中总结 −
Sr.No. |
Method & Description |
1 |
add( ) 向集合添加一个对象。 |
2 |
clear( ) 从集合中移除所有对象。 |
3 |
contains( ) 如果没有规定对象是否在集合内,返回 true。 |
4 |
isEmpty( ) 如果没有元素,返回 true。 |
5 |
iterator( ) 返回用于集合的 Iterator 对象,可用于检索对象。 |
6 |
remove( ) 从集合中移除指定对象。 |
7 |
*size( )*返回集合中的元素数。 |
Example 1
Set 在各种类中都有其实现,如 HashSet、TreeSet、LinkedHashSet。以下是如何使用 HashSet 解释 Set 功能的示例 −
import java.util.HashSet;
import java.util.Set;
public class SetDemo {
public static void main(String args[]) {
int count[] = {34, 22,10,60,30,22};
Set<Integer> set = new HashSet<>();
try {
for(int i = 0; i < 5; i++) {
set.add(count[i]);
}
System.out.println(set);
}
catch(Exception e) {}
}
}
Output
[34, 22, 10, 60, 30]
The sorted list is:
[10, 22, 30, 34, 60]
The First element of the set is: 10
The last element of the set is: 60
Example 2
Set 在各种类中都有其实现,如 HashSet、TreeSet、LinkedHashSet。以下是如何使用 TreeSet 解释 Set 功能的示例 −
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
public class SetDemo {
public static void main(String args[]) {
int count[] = {34, 22,10,60,30,22};
Set<Integer> set = new HashSet<>();
try {
for(int i = 0; i < 5; i++) {
set.add(count[i]);
}
System.out.println(set);
TreeSet<Integer> sortedSet = new TreeSet<>(set);
System.out.println("The sorted list is:");
System.out.println(sortedSet);
System.out.println("The First element of the set is: "+ (Integer)sortedSet.first());
System.out.println("The last element of the set is: "+ (Integer)sortedSet.last());
}
catch(Exception e) {}
}
}
Output
[34, 22, 10, 60, 30]
The sorted list is:
[10, 22, 30, 34, 60]
The First element of the set is: 10
The last element of the set is: 60
Example 3
Set 在各种类中都有其实现,如 HashSet、TreeSet、LinkedHashSet。以下是如何使用 TreeSet 操作解释 Set 功能的示例 −
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
public class SetDemo {
public static void main(String args[]) {
int count[] = {34, 22,10,60,30,22};
Set<Integer> set = new HashSet<>();
try {
for(int i = 0; i < 5; i++) {
set.add(count[i]);
}
System.out.println(set);
TreeSet<Integer> sortedSet = new TreeSet<>(set);
System.out.println("The sorted list is:");
System.out.println(sortedSet);
sortedSet.clear();
System.out.println("The sorted list is:");
System.out.println(sortedSet);
}
catch(Exception e) {}
}
}
Java - The SortedSet Interface
SortedSet 接口扩展了 Set,并声明了按升序排序的集合的行为。除了 Set 定义的那些方法外,SortedSet 接口还声明在下表中总结的方法 −
当调用集中不包含任何项时,若干方法会抛出 NoSuchElementException。当对象与集中元素不兼容时,会抛出 ClassCastException。
如果尝试使用 null 对象且集中不允许 null,则会抛出 NullPointerException。
Sr.No. |
Method & Description |
1 |
Comparator comparator( ) 返回调用排序集的比较器。如果对该集使用自然比较,则返回 null。 |
2 |
Object first( ) 返回调用排序集中的第一个元素。 |
3 |
SortedSet headSet(Object end) 返回一个 SortedSet,其中包含调用排序集中包含的那些小于 end 的元素。已返回排序集中元素还由调用排序集引用。 |
4 |
Object last( ) 返回调用排序集中的最后一个元素。 |
5 |
SortedSet subSet(Object start, Object end) 返回一个 SortedSet,其中包含 start 和 end 之间的那些元素。1.已返回集合中的元素还由调用对象引用。 |
6 |
SortedSet tailSet(Object start) 返回一个 SortedSet,其中包含排序集中包含的那些大于或等于 start 的元素。已返回集中的元素还由调用对象引用。 |
Example 1
SortedSet 在 TreeSet 等各种类中实现了其实现。以下是带有 add 操作的 TreeSet 类示例−
import java.util.Iterator;
import java.util.SortedSet;
import java.util.TreeSet;
public class SortedSetDemo {
public static void main(String[] args) {
// Create the sorted set
SortedSet<String> set = new TreeSet<>();
// Add elements to the set
set.add("b");
set.add("c");
set.add("a");
// Iterating over the elements in the set
Iterator it = set.iterator();
while (it.hasNext()) {
// Get element
Object element = it.next();
System.out.println(element.toString());
}
}
}
Example 2
SortedSet 在 TreeSet 等各种类中实现了其实现。以下是具有 add 和 remove 操作的 TreeSet 类示例 −
import java.util.Iterator;
import java.util.SortedSet;
import java.util.TreeSet;
public class SortedSetDemo {
public static void main(String[] args) {
// Create the sorted set
SortedSet<String> set = new TreeSet<>();
// Add elements to the set
set.add("b");
set.add("c");
set.add("a");
set.add("d");
set.add("e");
set.add("f");
// remove elements
set.remove("c");
set.remove("f");
// Iterating over the elements in the set
Iterator it = set.iterator();
while (it.hasNext()) {
// Get element
Object element = it.next();
System.out.println(element.toString());
}
}
}
Example 3
SortedSet 在 TreeSet 等各种类中实现了其实现。以下是具有 add 和 clear 操作的 TreeSet 类示例 −
import java.util.Iterator;
import java.util.SortedSet;
import java.util.TreeSet;
public class SortedSetDemo {
public static void main(String[] args) {
// Create the sorted set
SortedSet<String> set = new TreeSet<>();
// Add elements to the set
set.add("b");
set.add("c");
set.add("a");
set.add("d");
set.add("e");
set.add("f");
System.out.println(set);
// remove elements
set.clear();
System.out.println(set);
}
}
Java - Data Structures
Java 实用程序包提供的的数据结构非常强大,可以执行广泛的功能。这些数据结构由以下接口和类组成 −
-
Enumeration
-
BitSet
-
Vector
-
Stack
-
Dictionary
-
Hashtable
-
Properties
现在所有这些类都已成为传统,而 Java-2 引入了一个称为 Collections Framework 的新框架,下一章将对此进行讨论:
The Enumeration
枚举接口本身不是一个数据结构,但它在其他数据结构的上下文中非常重要。枚举接口定义了一种从数据结构中检索连续元素的方法。
例如,Enumeration 定义了一个名为 nextElement 的方法,用于获取包含多个元素的数据结构中的下一个元素。
Example
以下是显示针对 Vector 使用 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<>();
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());
}
}
}
The BitSet
BitSet 类实现了一个可单独设置或清除的位或标志组。
当需要保留一组布尔值时,这个类非常有用;你只需为每个值分配一个位,并在合适的情况下设置或清除它。
Example
以下程序说明了此数据结构支持的几种方法 -
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);
}
}
The Vector
Vector 类类似于传统的 Java 数组,只不过它可根据需要而增长以容纳新元素。
和数组一样,可以根据向量中的索引访问向量对象的元素。
使用 Vector 类的优点是,在创建时不必担心将其设置为特定的大小;它会在必要时自动缩小和增长。
Example
以下程序说明了此集合支持的几种方法 -
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();
}
}
The Stack
Stack 类实现了元素的后进先出 (LIFO) 栈。
你可以从字面上将栈看成一个垂直的对象栈;当你添加一个新元素时,它会堆叠在其他元素之上。
当你从栈中拉出元素时,它是从顶部拉出的。换句话说,你添加到栈中的最后一个元素是返回的第一个元素。
Example
以下程序说明了此集合支持的几种方法 -
import java.util.*;
public class StackDemo {
static void showpush(Stack st, int a) {
st.push(new Integer(a));
System.out.println("push(" + a + ")");
System.out.println("stack: " + st);
}
static void showpop(Stack 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 st = new Stack();
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");
}
}
}
The Dictionary
Dictionary 类是一个抽象类,它定义了向值映射键的数据结构。
当你想通过特定键而不是整数索引访问数据时,这很有用。
由于 Dictionary 类是抽象的,它只提供键映射的数据结构的框架,而不是特定的实现。
Example
以下示例演示了如何使用 Java Dictionary keys() 方法。我们使用 Integer、Integer 的 Hashtable 对象创建一个字典实例。然后我们向其中添加了一些元素。使用 keys() 方法检索枚举,然后迭代枚举以打印字典的键。
package com.tutorialspoint;
import java.util.Enumeration;
import java.util.Dictionary;
import java.util.Hashtable;
public class DictionaryDemo {
public static void main(String[] args) {
// create a new hashtable
Dictionary<Integer, Integer> dictionary = new Hashtable<>();
// add 2 elements
dictionary.put(1, 1);
dictionary.put(2, 2);
Enumeration<Integer> enumeration = dictionary.keys();
while(enumeration.hasMoreElements()) {
System.out.println(enumeration.nextElement());
}
}
}
The Hashtable
Hashtable 类提供了一种基于某些用户定义的键结构来组织数据的方法。
举例来说,在一个地址列表哈希表中,你可以根据 ZIP 编码这样的键存储和对数据进行排序,而不是按一个人的姓名进行排序。
键与哈希表之间的具体含义完全取决于哈希表的用法和它所包含的数据。
Example
以下示例演示了如何使用 Java Hashtable contains() 方法来检查 Hashtable 中是否存在某个值。我们创建了一个 Integer、Integer 的 Hashtable 对象。然后添加了一些条目,打印出表格,并使用 contains() 检查表格中的两个值。
package com.tutorialspoint;
import java.util.Hashtable;
public class HashtableDemo {
public static void main(String args[]) {
// create hash table
Hashtable<Integer,Integer> hashtable = new Hashtable<>();
// populate hash table
hashtable.put(1, 1);
hashtable.put(2, 2);
hashtable.put(3, 3);
System.out.println("Initial table elements: " + hashtable);
System.out.println("Hashtable contains 2 as value: " + hashtable.contains(2));
System.out.println("Hashtable contains 4 as value: " + hashtable.contains(4));
}
}
The Properties
属性是散列表的一个子类。它用于维护值列表,其中键是一个字符串,值也是一个字符串。
许多其他 Java 类都使用 Properties 类。例如,它是 System.getProperties( ) 在获取环境值时返回的对象类型。
Example
以下示例演示了如何使用 Java Properties getProperty(String key) 方法根据 Properties 中的键获取值。我们创建了一个 Properties 对象。然后添加了一些条目。使用 getProperty() 方法,检索并打印一个值。
package com.tutorialspoint;
import java.util.Properties;
public class PropertiesDemo {
public static void main(String[] args) {
Properties properties = new Properties();
//populate properties object
properties.put("1", "tutorials");
properties.put("2", "point");
properties.put("3", "is best");
System.out.println("Properties elements: " + properties);
System.out.println("Value: " + properties.getProperty("1"));
}
}
Java - The Enumeration Interface
Enumeration 接口定义了获取的对象集合中元素(一次获取一个)的方法。
此旧版接口已被 Iterator 取代。尽管没有弃用,但 Enumeration 对于新代码来说已过时。然而,它被 Vector 和 Properties 等旧版类定义的几个方法使用,也由其他几个 API 类使用,目前在应用程序代码中广泛使用。
Enumeration 声明的方法汇总在以下表格中 −
Sr.No. |
Method & Description |
1 |
boolean hasMoreElements( ) 在实现时,它必须在仍有更多元素要提取时返回真,在所有元素已被枚举时返回假。 |
2 |
Object nextElement( ) 这将枚举中的下一个对象作为泛型对象引用返回。 |
Example 1
以下是显示针对 Vector 使用 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<>();
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());
}
}
}
Example 2
以下是一个示例,演示了如何使用 Enumeration 来打印 Properties 中的值。
import java.util.Vector;
import java.util.Enumeration;
import java.util.Properties;
public class EnumerationTester {
public static void main(String args[]) {
Enumeration<Object> days;
Properties dayNames = new Properties();
dayNames.put(1, "Sunday");
dayNames.put(2,"Monday");
dayNames.put(3,"Tuesday");
dayNames.put(4,"Wednesday");
dayNames.put(5,"Thursday");
dayNames.put(6,"Friday");
dayNames.put(7,"Saturday");
days = dayNames.elements();
while (days.hasMoreElements()) {
System.out.println(days.nextElement());
}
}
}
Java - The Collection Algorithms
集合框架定义了几种可应用于集合和映射的算法。
这些算法被定义为 Collections 类的静态方法。其中一些方法可能会抛出 ClassCastException,这是在尝试比较不兼容的类型时发生的,或者 UnsupportedOperationException,这是在尝试修改不可修改的集合时发生的。
集合框架算法中定义的方法总结在以下表格中 -
Sr.No. |
Method & Description |
1 |
static int binarySearch(List list, Object value, Comparator c) 按 c 排序来搜索列表中的值。返回列表中值的索引,或如果找不到值则返回 -1。 |
2 |
static int binarySearch(List list, Object value) 在列表中搜索值。列表必须已排序。返回列表中值的索引,或如果找不到值则返回 -1。 |
3 |
static void copy(List list1, List list2) 将列表 2 的元素复制到列表 1 中。 |
4 |
static Enumeration enumeration(Collection c) 返回对 c 的枚举。 |
5 |
static void fill(List list, Object obj) 将 obj 分配给列表中的每个元素。 |
6 |
static int indexOfSubList(List list, List subList) 查找列表中 subList 第一次出现的位置。返回第一次匹配的索引,或如果没有匹配则返回 .1。 |
7 |
static int lastIndexOfSubList(List list, List subList) 查找列表中 subList 最后一次出现的位置。返回最后一次匹配的索引,或如果没有匹配则返回 .1。 |
8 |
static ArrayList list(Enumeration enum) 返回包含 enum 元素的 ArrayList。 |
9 |
static Object max(Collection c, Comparator comp) 返回 c 中按 comp 确定的最大元素。 |
10 |
static Object max(Collection c) 返回 c 中按自然排序确定的最大元素。集合不必进行排序。 |
11 |
static Object min(Collection c, Comparator comp) 返回 c 中按 comp 确定的最小元素。集合不必进行排序。 |
12 |
static Object min(Collection c) 返回 c 中按自然排序确定的最小元素。 |
13 |
static List nCopies(int num, Object obj) 返回 num 个包含在不可变列表中的 obj 副本。num 必须大于或等于零。 |
14 |
static boolean replaceAll(List list, Object old, Object new) 用列表中 new 替换 old 的所有出现。如果至少发生一次替换则返回 true。否则,返回 false。 |
15 |
static void reverse(List list) 反转列表中的序列。 |
16 |
static Comparator reverseOrder( ) 返回反向比较器。 |
17 |
static void rotate(List list, int n) 将列表向右旋转 n 个位置。如果要向左旋转,则对 n 使用负值。 |
18 |
static void shuffle(List list, Random r) 使用 r 作为随机数源,打乱(即随机化)列表中的元素。 |
19 |
static void shuffle(List list) 打乱(即随机化)列表中的元素。 |
20 |
static Set singleton(Object obj) 将 obj 返回为不可变集合。这是将单个对象转换为集合的一种简单方法。 |
21 |
static List singletonList(Object obj) 将 obj 返回为不可变列表。这是将单个对象转换为列表的一种简单方法。 |
22 |
static Map singletonMap(Object k, Object v) 将键/值对 k/v 返回为不可变映射。这是将单个键/值对转换为映射的一种简单方法。 |
23 |
static void sort(List list, Comparator comp) 根据 comp 确定的元素对列表进行排序。 |
24 |
static void sort(List list) 基于其自然排序对列表中的元素进行排序。 |
25 |
static void swap(List list, int idx1, int idx2) 交换 idx1 和 idx2 指定的索引处列表中的元素。 |
26 |
static Collection synchronizedCollection(Collection c) 返回 c 支持的线程安全集合。 |
27 |
static List synchronizedList(List list) 返回列表支持的线程安全列表。 |
28 |
static Map synchronizedMap(Map m) 返回 m 支持的线程安全映射。 |
29 |
static Set synchronizedSet(Set s) 返回 s 支持的线程安全集合。 |
30 |
static SortedMap synchronizedSortedMap(SortedMap sm) 返回 sm 支持的线程安全排序集合。 |
31 |
static SortedSet synchronizedSortedSet(SortedSet ss) 返回 ss 支持的线程安全集合。 |
32 |
static Collection unmodifiableCollection(Collection c) 返回 c 支持的不可修改集合。 |
33 |
static List unmodifiableList(List list) 返回列表支持的不可修改列表。 |
34 |
static Map unmodifiableMap(Map m) 返回 m 支持的不可修改映射。 |
35 |
static Set unmodifiableSet(Set s) 返回 s 支持的不可修改集合。 |
36 |
static SortedMap unmodifiableSortedMap(SortedMap sm) 返回由 sm 支持的不可修改排序映射。 |
37 |
static SortedSet unmodifiableSortedSet(SortedSet ss) 返回由 ss 支持的不可修改排序集。 |
Example 1
下面是一个示例,演示使用 LinkedList 添加元素并以相反顺序对其元素进行排序的各种算法。使用迭代器,遍历列表,然后对其进行改组,最后检索并打印最小值和最大值。
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class AlgorithmsDemo {
public static void main(String args[]) {
// Create and initialize linked list
List<Integer< ll = new LinkedList<<();
ll.add(Integer.valueOf(-8));
ll.add(Integer.valueOf(20));
ll.add(Integer.valueOf(-20));
ll.add(Integer.valueOf(8));
// Create a reverse order comparator
Comparator<Integer< r = Collections.reverseOrder();
// Sort list by using the comparator
Collections.sort(ll, r);
// Get iterator
Iterator<Integer< li = ll.iterator();
System.out.print("List sorted in reverse: ");
while(li.hasNext()) {
System.out.print(li.next() + " ");
}
System.out.println();
}
}
Example 2
下面是一个示例,演示使用 LinkedList 添加元素并以自然顺序对其元素进行排序的各种算法。使用迭代器,遍历列表,然后对其进行改组,最后检索并打印最小值和最大值。
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class AlgorithmsDemo {
public static void main(String args[]) {
// Create and initialize linked list
List<Integer> ll = new LinkedList<>();
ll.add(Integer.valueOf(-8));
ll.add(Integer.valueOf(20));
ll.add(Integer.valueOf(-20));
ll.add(Integer.valueOf(8));
// Sort list by using the default comparator
Collections.sort(ll);
// Get iterator
Iterator<Integer> li = ll.iterator();
System.out.print("List sorted: ");
while(li.hasNext()) {
System.out.print(li.next() + " ");
}
System.out.println();
Collections.shuffle(ll);
// display randomized list
li = ll.iterator();
System.out.print("List shuffled: ");
while(li.hasNext()) {
System.out.print(li.next() + " ");
}
System.out.println();
System.out.println("Minimum: " + Collections.min(ll));
System.out.println("Maximum: " + Collections.max(ll));
}
}
Example 3
下面是一个示例,演示使用 LinkedList 添加字符串元素并以自然顺序对其元素进行排序的各种算法。打印列表,然后对其进行改组,最后检索并打印最小值和最大值。
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class AlgorithmsDemo {
public static void main(String args[]) {
// Create and initialize linked list
List<String> list = new LinkedList<>();
list.add("Ram");
list.add("Mohan");
list.add("Julie");
list.add("Raheem");
// Sort list by using the default comparator
Collections.sort(list);
// print the sorted list
System.out.println("Sorted List: " + list);
// shuffle the list
Collections.shuffle(list);
// print the shuffled list
System.out.println("Shuffled List: " + list);
System.out.println("Minimum: " + Collections.min(list));
System.out.println("Maximum: " + Collections.max(list));
}
}
Java - How to Use Iterator?
通常情况下,您需要遍历集合中的元素。例如,您可能需要显示每个元素。执行此操作最简单的方法是使用迭代器,该迭代器是一个实现 Iterator 或 ListIterator 接口的对象。
Iterator 让您可以循环处理集合,获取或移除元素。ListIterator 扩展 Iterator 以允许双向遍历列表并修改元素。
在您可以通过迭代器访问集合之前,必须获取一个迭代器。每个集合类都提供了一个 iterator( ) 方法,该方法返回一个迭代器,用于集合的开始。通过使用此迭代器对象,您可以一次访问集合中的每个元素。
一般来说,要使用迭代器循环处理集合的内容,请执行以下步骤:
-
通过调用集合的 iterator( ) 方法来获取一个用于集合起始位置的迭代器。
-
设置一个循环,该循环调用 hasNext( )。让循环迭代,只要 hasNext( ) 返回 true。
-
在循环内,通过调用 next( ) 来获取每个元素。
对于实现 List 的集合,您还可以通过调用 ListIterator 来获取一个迭代器。
The Methods Declared by Iterator
Sr.No. |
Method & Description |
1 |
boolean hasNext( ) 返回 true,如果还有更多元素。否则,返回 false。 |
2 |
Object next( ) 返回下一个元素。如果没有下一个元素,则抛出 NoSuchElementException。 |
3 |
void remove( ) 删除当前元素。如果没有先调用 next( ),那么尝试调用 remove( ) 将抛出 IllegalStateException。 |
The Methods Declared by ListIterator
Sr.No. |
Method & Description |
1 |
void add(Object obj) 在 next( ) 的下一个调用将返回的元素之前,将 obj 插入到列表中。 |
2 |
boolean hasNext( ) 返回 true,如果还有下一个元素。否则,返回 false。 |
3 |
boolean hasPrevious( ) 如果存在上一个元素,则返回 true。否则,返回 false。 |
4 |
Object next( ) 返回下一个元素。如果没有下一个元素,则抛出 NoSuchElementException。 |
5 |
int nextIndex( ) 返回下一个元素的索引。如果没有下一个元素,则返回列表的大小。 |
6 |
Object previous( ) 返回上一个元素。如果没有上一个元素,则抛出 NoSuchElementException。 |
7 |
int previousIndex( ) 返回上一个元素的索引。如果没有上一个元素,则返回 -1。 |
8 |
void remove( ) 从列表中移除当前元素。如果在调用 next() 或 previous() 之前调用 remove() ,则抛出 IllegalStateException。 |
9 |
void set(Object obj) 将对象分配给当前元素。这是由前一个 next() 或 previous() 调用最后返回的元素。 |
Example 1
以下示例演示了 Iterator 的使用。它使用了一个 ArrayList 对象,但一般原理适用于任何类型的集合。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IteratorDemo {
public static void main(String args[]) {
// Create an array list
List<String> al = new ArrayList<>();
// add elements to the array list
al.add("C");
al.add("A");
al.add("E");
al.add("B");
al.add("D");
al.add("F");
// Use iterator to display contents of al
System.out.print("Original contents of al: ");
Iterator itr = al.iterator();
while(itr.hasNext()) {
Object element = itr.next();
System.out.print(element + " ");
}
System.out.println();
}
}
Example 2
以下示例演示了 ListIterator 的使用。它使用了一个 ArrayList 对象,但一般原理适用于任何类型的集合。
当然,ListIterator 仅适用于那些实现 List 接口的集合。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class IteratorDemo {
public static void main(String args[]) {
// Create an array list
List<String> al = new ArrayList<>();
// add elements to the array list
al.add("C");
al.add("A");
al.add("E");
al.add("B");
al.add("D");
al.add("F");
// Use iterator to display contents of al
System.out.print("Original contents of al: ");
Iterator<String> itr = al.iterator();
while(itr.hasNext()) {
Object element = itr.next();
System.out.print(element + " ");
}
}
}
Example 3
以下示例演示了 ListIterator 如何在迭代时修改列表。它使用了一个 ArrayList 对象,但一般原理适用于任何类型的集合。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class IteratorDemo {
public static void main(String args[]) {
// Create an array list
List<String> al = new ArrayList<>();
// add elements to the array list
al.add("C");
al.add("A");
al.add("E");
al.add("B");
al.add("D");
al.add("F");
// Use iterator to display contents of al
System.out.print("Original contents of al: ");
Iterator<String> itr = al.iterator();
while(itr.hasNext()) {
Object element = itr.next();
System.out.print(element + " ");
}
System.out.println();
// Modify objects being iterated
ListIterator<String> litr = al.listIterator();
while(litr.hasNext()) {
Object element = litr.next();
litr.set(element + "+");
}
System.out.print("Modified contents of al: ");
itr = al.iterator();
while(itr.hasNext()) {
Object element = itr.next();
System.out.print(element + " ");
}
System.out.println();
// Now, display the list backwards
System.out.print("Modified list backwards: ");
while(litr.hasPrevious()) {
Object element = litr.previous();
System.out.print(element + " ");
}
System.out.println();
}
}
Java - How to Use Comparator?
TreeSet 和 TreeMap 都按排序顺序存储元素。然而,是比较器严格定义了排序顺序的含义。
Comparator 接口定义了两个方法:compareTo() 和 equals()。这里显示的 compareTo() 方法,比较两个元素的顺序 −
The compare Method
int compare(Object obj1, Object obj2)
obj1 和 obj2 是要比较的对象。如果对象相等,此方法返回零。如果 obj1 大于 obj2,则它返回正值。否则,返回负值。
通过重写 compareTo(),您可以改变对象的排序方式。例如,要按相反顺序进行排序,可以创建一个逆转比较结果的比较器。
The equals Method
这里显示的 equals() 方法,测试一个对象是否等于调用的比较器 −
boolean equals(Object obj)
obj 是要测试是否相等的对象。如果 obj 和调用对象都是 Comparator 对象并且使用相同的排序,则该方法返回 true。否则,它将返回 false。
重写 equals() 没有必要,大多数简单的比较器不会这样做。
Example 1
在这个示例中,我们使用 Comparator 接口来根据比较条件对自定义对象 Dog 进行排序。
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
class Dog implements Comparator<Dog>, Comparable<Dog> {
private String name;
private int age;
Dog() {
}
Dog(String n, int a) {
name = n;
age = a;
}
public String getDogName() {
return name;
}
public int getDogAge() {
return age;
}
// Overriding the compareTo method
public int compareTo(Dog d) {
return (this.name).compareTo(d.name);
}
// Overriding the compare method to sort the age
public int compare(Dog d, Dog d1) {
return d.age - d1.age;
}
@Override
public String toString() {
return this.name + "," + this.age;
}
}
public class ComparatorDemo {
public static void main(String args[]) {
// Takes a list o Dog objects
List<Dog> list = new ArrayList<>();
list.add(new Dog("Shaggy", 3));
list.add(new Dog("Lacy", 2));
list.add(new Dog("Roger", 10));
list.add(new Dog("Tommy", 4));
list.add(new Dog("Tammy", 1));
Collections.sort(list); // Sorts the array list
System.out.println("Sorted by name:");
// printing the sorted list of names
System.out.print(list);
// Sorts the array list using comparator
Collections.sort(list, new Dog());
System.out.println(" ");
System.out.println("Sorted by age:");
// printing the sorted list of ages
System.out.print(list);
}
}
Output
Sorted by name:
[Lacy,2, Roger,10, Shaggy,3, Tammy,1, Tommy,4]
Sorted by age:
[Tammy,1, Lacy,2, Shaggy,3, Tommy,4, Roger,10]
Note − Arrays 类中的排序与 Collections.相同。
Example 2
在这个示例中,我们使用 Comparator 接口对 Dog 进行反向排序。
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
class Dog implements Comparator<Dog>, Comparable<Dog> {
private String name;
private int age;
Dog() {
}
Dog(String n, int a) {
name = n;
age = a;
}
public String getDogName() {
return name;
}
public int getDogAge() {
return age;
}
// Overriding the compareTo method
public int compareTo(Dog d) {
return (this.name).compareTo(d.name);
}
// Overriding the compare method to sort the age
public int compare(Dog d, Dog d1) {
return d.age - d1.age;
}
@Override
public String toString() {
return this.name + "," + this.age;
}
}
public class ComparatorDemo {
public static void main(String args[]) {
// Takes a list o Dog objects
List<Dog> list = new ArrayList<>();
list.add(new Dog("Shaggy", 3));
list.add(new Dog("Lacy", 2));
list.add(new Dog("Roger", 10));
list.add(new Dog("Tommy", 4));
list.add(new Dog("Tammy", 1));
Collections.sort(list, Collections.reverseOrder()); // Sorts the array list
System.out.println("Sorted by name in reverse order:");
// printing the sorted list of names
System.out.print(list);
}
}
Output
Sorted by name in reverse order:
[Tommy,4, Tammy,1, Shaggy,3, Roger,10, Lacy,2]
在这个示例中,我们使用 Comparator 接口对字符串值进行反向排序。
Example 3
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ComparatorDemo {
public static void main(String args[]) {
// Takes a list o Dog objects
List<String> list = new ArrayList<>();
list.add("Shaggy");
list.add("Lacy");
list.add("Roger");
list.add("Tommy");
list.add("Tammy");
Collections.sort(list, Collections.reverseOrder()); // Sorts the array list
System.out.println("Sorted by name in reverse order:");
// printing the sorted list of names
System.out.print(list);
}
}
Java - Regular Expressions
Java 提供 java.util.regex 包,用于使用正则表达式进行模式匹配。Java 正则表达式与 Perl 编程语言非常相似,且非常容易学习。
正则表达式是由字符组成的特殊序列,它使用模式中包含的专门语法,帮助您匹配或查找其他字符串或字符串集。它们可以用来搜索、编辑或处理文本和数据。
java.util.regex 包主要包含以下三个类 −
-
Pattern Class − Pattern 对象是正则表达式的编译表示。Pattern 类不提供任何公共构造函数。要创建模式,你必须首先调用其公共静态 compile() 方法之一,然后该方法将返回一个 Pattern 对象。这些方法将正则表达式作为第一个参数接受。
-
Matcher Class - Matcher 对象是解释模式并在给定输入字符串上执行匹配操作的引擎。与 Pattern 类类似,Matcher 未定义任何公共构造函数。可以通过对 Pattern 对象调用 matcher() 方法来获取 Matcher 对象。
-
PatternSyntaxException - PatternSyntaxException 对象是未检查的异常,指示正则表达式模式中的语法错误。
Capturing Groups
捕获组是一种将多个字符视为单个单位的方法。它们是通过将待分组的字符置于一组圆括号内创建的。例如,正则表达式 (dog) 创建单个组,其中包含字母“d”、“o”和“g”。
捕获组通过从左到右计算其左括号进行编号。例如,在表达式 A)(B© 中,存在四个这样的组 -
-
A)(B©
-
(A)
-
(B©)
-
©
若要找出表达式中有多少组,请对匹配器对象调用 groupCount 方法。groupCount 方法返回一个 int,显示匹配器模式中存在的分组数。
还有一个特殊的组,组 0,它始终代表整个表达式。此组不包括在 groupCount 报告的总数中。
Example
以下示例说明如何从给定的字母数字字符串中找到数字字符串 -
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches {
public static void main( String args[] ) {
// String to be scanned to find the pattern.
String line = "This order was placed for QT3000! OK?";
String pattern = "(.*)(\\d+)(.*)";
// Create a Pattern object
Pattern r = Pattern.compile(pattern);
// Now create matcher object.
Matcher m = r.matcher(line);
if (m.find( )) {
System.out.println("Found value: " + m.group(0) );
System.out.println("Found value: " + m.group(1) );
System.out.println("Found value: " + m.group(2) );
}else {
System.out.println("NO MATCH");
}
}
}
Regular Expression Syntax
以下是列出 Java 中可用的所有正则表达式元字符语法的表格 -
Subexpression |
Matches |
^ |
匹配行的开头。 |
$ |
匹配行的末尾。 |
. |
匹配任何单个字符(换行除外)。使用 m 选项,也可以匹配换行。 |
[…] |
匹配括号中的任何单字符。 |
[^…] |
匹配括号中不存在的任何单字符。 |
\A |
整个字符串的开头。 |
\z |
整个字符串的结尾。 |
\Z |
除了允许的最终行终止符之外,整个字符串的结尾。 |
re* |
匹配前一个表达式的 0 次或多次出现。 |
re+ |
匹配前一项 1 次或多次出现。 |
re? |
匹配前一个表达式的 0 次或 1 次出现。 |
re{ n} |
与前一场表达式的匹配次数完全一致 |
re{ n,} |
与前场表达式的匹配次数相同或者超过 |
re{ n, m} |
与前场表达式的最少的次数到最多的次数之间匹配 |
a |
|
匹配 a 或 b |
(re) |
组合正则表达式并记住匹配的文本 |
(?: re) |
组合正则表达式而不记住匹配的文本 |
(?> re) |
在没有反向追踪的情况下匹配独立模式 |
\w |
Matches the word characters. |
\W |
Matches the nonword characters. |
\s |
匹配空白字符,相当于 [\t\n\r\f] |
\S |
Matches the nonwhitespace. |
\d |
匹配数字,相当于 [0-9] |
\D |
Matches the nondigits. |
\A |
匹配字符串的开头 |
\Z |
匹配字符串的末尾。如果存在换行符,則匹配换行符前的内容 |
\z |
匹配字符串的末尾 |
\G |
匹配上次匹配结束的地方 |
\n |
引用捕获组号“n”的匹配项 |
\b |
在方括号外,匹配单词边界。在方括号内,匹配退格符(0x08) |
\B |
Matches the nonword boundaries. |
\n, \t, etc. |
匹配换行符、回车、制表符等 |
\Q |
转义所有字符,直到 \E |
\E |
Methods of the Matcher Class
以下是有用的实例方法列表 -
Replacement Methods
替换方法是用于替换输入字符串中的文本的有用方法 −
Sr.No. |
Method & Description |
1 |
public Matcher appendReplacement(StringBuffer sb, String replacement) 实现非末端附加和替换步骤。 |
2 |
public StringBuffer appendTail(StringBuffer sb) 实现末端附加和替换步骤。 |
3 |
public String replaceAll(String replacement) 用给定的替换字符串替换源序列中所有匹配模式的子序列。 |
4 |
public String replaceFirst(String replacement) 用给定的替换字符串替换源序列中第一个匹配模式的子序列。 |
5 |
public static String quoteReplacement(String s) 针对指定字符串返回一个实际替换字符串。此方法会生成一个可在 Matcher 类的 appendReplacement 方法中用作实际替换的字符串 s 。 |
The start and end Methods
以下示例统计了单词“cat”在输入字符串中出现的次数:
Example
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches {
private static final String REGEX = "\\bcat\\b";
private static final String INPUT = "cat cat cat cattie cat";
public static void main( String args[] ) {
Pattern p = Pattern.compile(REGEX);
Matcher m = p.matcher(INPUT); // get a matcher object
int count = 0;
while(m.find()) {
count++;
System.out.println("Match number "+count);
System.out.println("start(): "+m.start());
System.out.println("end(): "+m.end());
}
}
}
Output
Match number 1
start(): 0
end(): 3
Match number 2
start(): 4
end(): 7
Match number 3
start(): 8
end(): 11
Match number 4
start(): 19
end(): 22
您可以看到,此示例使用了词边界以确保字母“c”、“a”和“t”并非更长单词中的一个子字符串。它还提供了一些有用的信息,说明匹配在输入字符串中的位置。
start 方法返回在先前的匹配操作期间给定组捕获的子序列的起始索引,end 返回匹配的最后一个字符的索引加 1。
The matches and lookingAt Methods
matches 和 lookingAt 方法均尝试将一个输入序列与模式进行匹配。但区别在于 matches 要求匹配整个输入序列,而 lookingAt 则不要求。
两种方法都始终从输入字符串的开头处开始。以下示例说明了该功能:
Example
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches {
private static final String REGEX = "foo";
private static final String INPUT = "fooooooooooooooooo";
private static Pattern pattern;
private static Matcher matcher;
public static void main( String args[] ) {
pattern = Pattern.compile(REGEX);
matcher = pattern.matcher(INPUT);
System.out.println("Current REGEX is: "+REGEX);
System.out.println("Current INPUT is: "+INPUT);
System.out.println("lookingAt(): "+matcher.lookingAt());
System.out.println("matches(): "+matcher.matches());
}
}
Output
Current REGEX is: foo
Current INPUT is: fooooooooooooooooo
lookingAt(): true
matches(): false
The replaceFirst and replaceAll Methods
replaceFirst 和 replaceAll 方法将替换与给定正则表达式匹配的文本。顾名思义,replaceFirst 替换第一个匹配项,而 replaceAll 替换所有匹配项。
以下示例说明了该功能:
Example
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches {
private static String REGEX = "dog";
private static String INPUT = "The dog says meow. " + "All dogs say meow.";
private static String REPLACE = "cat";
public static void main(String[] args) {
Pattern p = Pattern.compile(REGEX);
// get a matcher object
Matcher m = p.matcher(INPUT);
INPUT = m.replaceAll(REPLACE);
System.out.println(INPUT);
}
}
Output
The cat says meow. All cats say meow.
The appendReplacement and appendTail Methods
Matcher 类还提供了 appendReplacement 和 appendTail 方法用于文本替换。
以下示例说明了该功能:
Example
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches {
private static String REGEX = "a*b";
private static String INPUT = "aabfooaabfooabfoob";
private static String REPLACE = "-";
public static void main(String[] args) {
Pattern p = Pattern.compile(REGEX);
// get a matcher object
Matcher m = p.matcher(INPUT);
StringBuffer sb = new StringBuffer();
while(m.find()) {
m.appendReplacement(sb, REPLACE);
}
m.appendTail(sb);
System.out.println(sb.toString());
}
}
Output
-foo-foo-foo-
PatternSyntaxException Class Methods
PatternSyntaxException 是一个未经检查的异常,它表示正则表达式模式中的语法错误。PatternSyntaxException 类提供了以下方法来帮助您确定出错的原因:
Sr.No. |
Method & Description |
1 |
public String getDescription() 获取错误描述。 |
2 |
public int getIndex() 获取错误索引。 |
3 |
public String getPattern() 获取有问题的正则表达式模式。 |
4 |
public String getMessage() 返回一个多行字符串,其中包含语法错误及其索引、有问题的正则表达式模式以及模式中错误索引的可视指示。 |
Java - Serialization
Java 提供了一种称为对象序列化的机制,其中对象可以表示为包含对象数据以及有关对象类型和存储在对象中的数据类型的信息的字节序列。
将序列化对象写到文件后,可以从文件读取它并对其反序列化,即类型信息和表示对象及其数据的字节可用于在内存中重新创建该对象。
最令人印象深刻的是整个过程与 JVM 无关,这意味着可以在一个平台上序列化对象并在完全不同的平台上反序列化。
类 ObjectInputStream 和 ObjectOutputStream 是包含用于序列化和反序列化对象的的高级流。
ObjectOutputStream 类包含许多用于编写各种数据类型的编写方法,但是有一个方法特别突出 −
public final void writeObject(Object x) throws IOException
以上方法对 Object 序列化并将它发送到输出流中。类似地,ObjectInputStream 类包含以下用于反序列化对象的 -
public final Object readObject() throws IOException, ClassNotFoundException
此方法从流中检索下一个 Object 并对其进行反序列化。返回值为 Object,因此你需要将其强制转换为其适当的数据类型。
为了演示序列化如何在 Java 中工作,我将使用我们在本书开头讨论过的 Employee 类。假设我们有以下一个实现了 Serializable 接口的 Employee 类:
Example
public class Employee implements java.io.Serializable {
public String name;
public String address;
public transient int SSN;
public int number;
public void mailCheck() {
System.out.println("Mailing a check to " + name + " " + address);
}
}
请注意,若要成功序列化一个类,必须满足两个条件:
-
类必须实现 java.io.Serializable 接口。
-
类中的所有字段都必须可序列化。如果一个字段不可序列化,则必须标记为 transient。
如果你好奇一个 Java 标准类是否可序列化,请查看该类的文档。测试很简单:如果该类实现了 java.io.Serializable,那么它是可序列化的;否则,它不可序列化。
Serializing an Object
ObjectOutputStream 类用于序列化一个对象。以下 SerializeDemo 程序实例化一个 Employee 对象,并将其序列化到一个文件中。
当程序执行完毕时,会创建一个名为 employee.ser 的文件。该程序不会生成任何输出,但研究该代码,并尝试确定该程序正在做什么。
Note - 当将一个对象序列化到一个文件中时,Java 中的标准惯例是给该文件一个 .ser 扩展名。
Example
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class SerializeDemo {
public static void main(String [] args) {
Employee e = new Employee();
e.name = "Reyan Ali";
e.address = "Phokka Kuan, Ambehta Peer";
e.SSN = 11122333;
e.number = 101;
try {
FileOutputStream fileOut = new FileOutputStream("employee.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(e);
out.close();
fileOut.close();
System.out.printf("Serialized data is saved in /tmp/employee.ser");
} catch (IOException i) {
i.printStackTrace();
}
}
}
class Employee implements java.io.Serializable {
private static final long serialVersionUID = 1L;
public String name;
public String address;
public transient int SSN;
public int number;
public void mailCheck() {
System.out.println("Mailing a check to " + name + " " + address);
}
}
Deserializing an Object
以下 DeserializeDemo 程序反序列化在前面程序中创建的 Employee 对象。研究该程序并尝试确定其输出:
Example
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class DeserializeDemo {
public static void main(String [] args) {
Employee e = null;
try {
FileInputStream fileIn = new FileInputStream("employee.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
e = (Employee) in.readObject();
in.close();
fileIn.close();
} catch (IOException i) {
i.printStackTrace();
return;
} catch (ClassNotFoundException c) {
System.out.println("Employee class not found");
c.printStackTrace();
return;
}
System.out.println("Deserialized Employee...");
System.out.println("Name: " + e.name);
System.out.println("Address: " + e.address);
System.out.println("SSN: " + e.SSN);
System.out.println("Number: " + e.number);
}
}
class Employee implements java.io.Serializable {
private static final long serialVersionUID = 1L;
public String name;
public String address;
public transient int SSN;
public int number;
public void mailCheck() {
System.out.println("Mailing a check to " + name + " " + address);
}
}
Output
Deserialized Employee...
Name: Reyan Ali
Address:Phokka Kuan, Ambehta Peer
SSN: 0
Number:101
以下是要注意的几个重点:
-
try/catch 块尝试捕获 readObject() 方法声明的 ClassNotFoundException。为了使 JVM 能够反序列化对象,它必须能够找到类的字节码。如果 JVM 在对象的序列化过程中找不到类,它将抛出 ClassNotFoundException。
-
请注意,readObject() 的返回值会转化为 Employee 引用。
-
当该对象被序列化时,SSN 字段的值为 11122333,但由于该字段是短暂的,因此此值不会被发送到输出流中。反序列化的 Employee 对象的 SSN 字段为 0。
Java - Sending Email
使用 Java 应用程序发送电子邮件非常简单,但首先应该在计算机上安装 JavaMail API 和 Java Activation Framework (JAF)。
-
你可以从 Java 标准网站下载 JavaMail (Version 1.2) 的最新版本。
-
你可以从 Java 标准网站下载 JAF (Version 1.1.1) 的最新版本。
下载并解压缩这些文件,在新建的顶层目录中,你可以找到这两个应用程序的多个 jar 文件。你需要在你的 CLASSPATH 中添加 mail.jar 和 activation.jar 文件。
Send a Simple E-mail
这里是一个用来从你的机器发送一封简单邮件的示例。假定你的 localhost 已连接至互联网且有能力发送电子邮件。
Example
// File Name SendEmail.java
import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
public class SendEmail {
public static void main(String [] args) {
// Recipient's email ID needs to be mentioned.
String to = "abcd@gmail.com";
// Sender's email ID needs to be mentioned
String from = "web@gmail.com";
// Assuming you are sending email from localhost
String host = "localhost";
// Get system properties
Properties properties = System.getProperties();
// Setup mail server
properties.setProperty("mail.smtp.host", host);
// Get the default Session object.
Session session = Session.getDefaultInstance(properties);
try {
// Create a default MimeMessage object.
MimeMessage message = new MimeMessage(session);
// Set From: header field of the header.
message.setFrom(new InternetAddress(from));
// Set To: header field of the header.
message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
// Set Subject: header field
message.setSubject("This is the Subject Line!");
// Now set the actual message
message.setText("This is actual message");
// Send message
Transport.send(message);
System.out.println("Sent message successfully....");
} catch (MessagingException mex) {
mex.printStackTrace();
}
}
}
Output
编译并运行此程序来发送一封简单电子邮件 −
$ java SendEmail
Sent message successfully....
如果你想发送电子邮件给多个收件人,那么可以使用以下的方法来指定多个电子邮件 ID −
void addRecipients(Message.RecipientType type, Address[] addresses)
throws MessagingException
以下是参数说明 −
-
type − 这将设置为 TO(收件人)、CC(抄送)或 BCC(密送)。此处的 CC 表示抄送,BCC 表示密送。示例:Message.RecipientType.TO
-
addresses − 这是一组电子邮件 ID。在你指定电子邮件 ID 时需要使用 InternetAddress() 方法。
Send an HTML E-mail
这里是一个用来从你的机器发送 HTML 电子邮件的示例。此示例假定你的 localhost 已连接到互联网并且能够发送电子邮件。
这个示例与之前的示例非常相似,不同之处在于,这里我们使用 setContent() 方法来设置内容,其第二个参数是“text/html”,指定该消息中包含 HTML 内容。
使用这个示例,你可以发送任意长度的 HTML 内容。
Example
// File Name SendHTMLEmail.java
import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
public class SendHTMLEmail {
public static void main(String [] args) {
// Recipient's email ID needs to be mentioned.
String to = "abcd@gmail.com";
// Sender's email ID needs to be mentioned
String from = "web@gmail.com";
// Assuming you are sending email from localhost
String host = "localhost";
// Get system properties
Properties properties = System.getProperties();
// Setup mail server
properties.setProperty("mail.smtp.host", host);
// Get the default Session object.
Session session = Session.getDefaultInstance(properties);
try {
// Create a default MimeMessage object.
MimeMessage message = new MimeMessage(session);
// Set From: header field of the header.
message.setFrom(new InternetAddress(from));
// Set To: header field of the header.
message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
// Set Subject: header field
message.setSubject("This is the Subject Line!");
// Send the actual HTML message, as big as you like
message.setContent("<h1>This is actual message</h1>", "text/html");
// Send message
Transport.send(message);
System.out.println("Sent message successfully....");
} catch (MessagingException mex) {
mex.printStackTrace();
}
}
}
编译并运行此程序来发送一封 HTML 电子邮件 −
Send Attachment in E-mail
这里是一个用来从你的机器发送带有附件的电子邮件的示例。此示例假定你的 localhost 已连接到互联网且有能力发送电子邮件。
Example
// File Name SendFileEmail.java
import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
public class SendFileEmail {
public static void main(String [] args) {
// Recipient's email ID needs to be mentioned.
String to = "abcd@gmail.com";
// Sender's email ID needs to be mentioned
String from = "web@gmail.com";
// Assuming you are sending email from localhost
String host = "localhost";
// Get system properties
Properties properties = System.getProperties();
// Setup mail server
properties.setProperty("mail.smtp.host", host);
// Get the default Session object.
Session session = Session.getDefaultInstance(properties);
try {
// Create a default MimeMessage object.
MimeMessage message = new MimeMessage(session);
// Set From: header field of the header.
message.setFrom(new InternetAddress(from));
// Set To: header field of the header.
message.addRecipient(Message.RecipientType.TO,new InternetAddress(to));
// Set Subject: header field
message.setSubject("This is the Subject Line!");
// Create the message part
BodyPart messageBodyPart = new MimeBodyPart();
// Fill the message
messageBodyPart.setText("This is message body");
// Create a multipar message
Multipart multipart = new MimeMultipart();
// Set text message part
multipart.addBodyPart(messageBodyPart);
// Part two is attachment
messageBodyPart = new MimeBodyPart();
String filename = "file.txt";
DataSource source = new FileDataSource(filename);
messageBodyPart.setDataHandler(new DataHandler(source));
messageBodyPart.setFileName(filename);
multipart.addBodyPart(messageBodyPart);
// Send the complete message parts
message.setContent(multipart );
// Send message
Transport.send(message);
System.out.println("Sent message successfully....");
} catch (MessagingException mex) {
mex.printStackTrace();
}
}
}
编译并运行此程序来发送一封 HTML 电子邮件 −
Java - Applet Basics
一个 applet 是一个在 Web 浏览器中运行的 Java 程序。小应用程序可以是一个功能齐全的 Java 应用程序,因为它可以使用整个 Java API。
小应用程序与独立 Java 应用程序之间有一些重要的区别,包括以下区别 −
-
applet 是扩展 java.applet.Applet 类的一个 Java 类。
-
在 applet 上不会调用 main() 方法,applet 类也不会定义 main()。
-
applet 旨在嵌入到 HTML 页面中。
-
当用户查看包含小应用程序的 HTML 页面时,小应用程序的代码将下载到用户计算机上。
-
需要一个 JVM 来查看 applet。JVM 可以是 Web 浏览器的插件或单独的运行时环境。
-
用户计算机上的 JVM 会创建小应用程序类的实例,并在小应用程序的生命期内调用各种方法。
-
小应用程序有 Web 浏览器强制实施的严格安全规则。小应用程序的安全性通常称为沙盒安全性,将小应用程序比作在沙盒中玩耍的孩子,必须遵循各种规则。
-
小应用程序需要的其他类可以下载到一个 Java 归档 (JAR) 文件中。
Life Cycle of an Applet
Applet 类中的四种方法为你提供了构建任何重要小程序的基础架构 −
-
init− 此方法打算用于小程序所需的任何初始化。它在处理小程序标签中的参数标签之后调用。
-
start− 在浏览器调用 init 方法后,会自动调用此方法。每当用户浏览其他页面后返回包含小程序的页面时,也会调用此方法。
-
stop− 当用户离开放置小程序的页面时,会自动调用此方法。因此,在同一小程序中可以反复调用此方法。
-
destroy− 仅在浏览器正常关闭时调用此方法。由于小程序旨在包含在 HTML 页面中,因此当用户离开包含小程序的页面后,通常不应留下资源。
-
paint− 在 start() 方法之后立即调用,以及每次小程序需要在浏览器中自行重新绘制时调用。paint() 方法实际上是从 java.awt. 那里继承的。
A "Hello, World" Applet
这是一个名为 HelloWorldApplet.java 的简单小程序 −
import java.applet.*;
import java.awt.*;
public class HelloWorldApplet extends Applet {
public void paint (Graphics g) {
g.drawString ("Hello World", 25, 50);
}
}
这些导入语句将类引入到小程序类的范围内 −
-
java.applet.Applet
-
java.awt.Graphics
如果没有那些导入语句,Java 编译器将无法识别 applet 类引用的类 Applet 和 Graphics。
The Applet Class
每个小程序都是 java.applet.Applet 类的扩展。基本 Applet 类提供了派生小程序类可以调用的方法,以从浏览器环境获取信息和服务。
其中包括执行以下操作的方法 −
-
Get applet parameters
-
获取包含小程序的 HTML 文件的网络位置
-
获取小程序类目录的网络位置
-
在浏览器中打印一个状态消息
-
Fetch an image
-
Fetch an audio clip
-
Play an audio clip
-
Resize the applet
此外,Applet 类还提供了一个接口,供查看器或浏览器获取有关小程序的信息并控制小程序的执行。查看器可以 −
-
请求有关小程序作者、版本和版权的信息
-
请求对小程序识别的参数的描述
-
Initialize the applet
-
Destroy the applet
-
Start the applet’s execution
-
Stop the applet’s execution
Applet 类为每种方法提供默认实现。可以根据需要覆盖这些实现。
“Hello, World”小程序在当前状态下是完整的。唯一被覆盖的方法是 paint 方法。
Invoking an Applet
可以通过将指令嵌入 HTML 文件并在小程序查看器或支持 Java 的浏览器中查看该文件来调用小程序。
<applet> 标签是将 applet 嵌入到 HTML 文件中的基础。以下是调用“Hello, World”applet 的示例 −
<html>
<title>The Hello, World Applet</title>
<hr>
<applet code = "HelloWorldApplet.class" width = "320" height = "120">
If your browser was Java-enabled, a "Hello, World"
message would appear here.
</applet>
<hr>
</html>
Note −你可以参阅 HTML Applet Tag 以详细了解如何从 HTML 中调用小应用程序。
<applet> 标签的 code 属性是必需的。它指定要运行的 Applet 类。还要求宽度和高度来指定小程序运行的面板的初始大小。小程序指令必须使用 </applet> 标签关闭。
如果小程序带有参数,可以通过在 <applet> 和 </applet> 之间添加 <param> 标签来传递参数值。该浏览器将忽略 applet 标签之间的文本和其他标签。
不支持 Java 的浏览器不会处理 <applet> 和 </applet>。因此,标签之间的任何内容都与该小程序无关,并且在不支持 Java 的浏览器中是可见的。
该查看器或浏览器在文档位置查找已编译的 Java 代码。若要另行指定,请使用 <applet> 标签的 codebase 属性,如以下代码所示:
<applet codebase = "https://amrood.com/applets" code = "HelloWorldApplet.class"
width = "320" height = "120">
如果小程序位于其他包中,而且不是默认包,则必须使用句点 (.) 来分隔包/类组件,并通过 code 属性来指定包含的包。例如:
<applet = "mypackage.subpackage.TestApplet.class"
width = "320" height = "120">
Getting Applet Parameters
以下示例演示如何让小程序响应文档中指定的设置参数。该小程序显示黑白相间的棋盘格图案。
可以在文档中将第二种颜色和每个正方形的大小指定为小程序的参数。
CheckerApplet 在 init() 方法中获取其参数。它还可以在 paint() 方法中获取其参数。但是,在 applet 开始时获取值并保存设置(而不是每次刷新时),是方便且有效的。
小程序查看器或浏览器调用它运行的每个小程序的 init() 方法。该查看器在加载小程序后立即调用 init() 一次。(Applet.init() 被实现为不做任何事情。)为了插入定制初始化代码,请覆盖默认实现。
Applet.getParameter() 方法获取给定参数名称的参数(参数值始终为字符串)。如果该值为数字或其他非字符数据,则必须对字符串进行解析。
以下是 CheckerApplet.java 的结构:
import java.applet.*;
import java.awt.*;
public class CheckerApplet extends Applet {
int squareSize = 50; // initialized to default size
public void init() {}
private void parseSquareSize (String param) {}
private Color parseColor (String param) {}
public void paint (Graphics g) {}
}
以下是 CheckerApplet 的 init() 和私有 parseSquareSize() 方法:
public void init () {
String squareSizeParam = getParameter ("squareSize");
parseSquareSize (squareSizeParam);
String colorParam = getParameter ("color");
Color fg = parseColor (colorParam);
setBackground (Color.black);
setForeground (fg);
}
private void parseSquareSize (String param) {
if (param == null) return;
try {
squareSize = Integer.parseInt (param);
} catch (Exception e) {
// Let default value remain
}
}
该小程序调用 parseSquareSize() 来解析 squareSize 参数。parseSquareSize() 调用库方法 Integer.parseInt(),它解析字符串并返回一个整数。每当其参数无效时,Integer.parseInt() 都会引发异常。
因此,parseSquareSize() 捕获异常,而不是让小程序由于错误输入而失败。
该小程序调用 parseColor() 将 color 参数解析为 Color 值。parseColor() 执行一系列字符串比较,以将参数值与预定义颜色的名称匹配。您需要实现这些方法才能使此小程序工作。
Application Conversion to Applets
将图形 Java 应用程序(即使用 AWT、并且可以使用 Java 程序启动器启动的应用程序)很容易转换为可嵌入到网页中的小程序。
以下是将应用程序转换为小程序的具体步骤。
-
通过适当的标签创建一个 HTML 页面,用于加载小程序代码。
-
提供 JApplet 类的一个子类。将此类设为公有。否则,小应用程序无法加载。
-
消除应用程序中的主方法。不要为应用程序构建框架窗口。您的应用程序将在浏览器中显示。
-
把框架窗口构造函数中的任何初始化代码移到该小应用程序的初始化方法中。您不需要明确构建小应用程序对象。浏览器会为您实例化它并调用初始化方法。
-
移除对 setSize 的调用;对于小应用程序,大小使用 HTML 文件中的宽度和高度参数来设定。
-
移除对 setDefaultCloseOperation 的调用。小应用程序无法被关闭;它会在浏览器退出时终止。
-
如果应用程序调用 setTitle,请消除对该方法的调用。小应用程序不能有标题栏。(您当然可以使用 HTML title 标记为网页本身命名。)
-
不要调用 setVisible(true)。小应用程序会自动显示。
Event Handling
小应用程序从 Container 类继承一组事件处理方法。Container 类定义了一些方法(如处理特定类型事件的 processKeyEvent 和 processMouseEvent)以及一个通用方法 processEvent。
为了对事件做出反应,需要覆盖适当的事件特定方法。
Example
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 apple ");
}
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! ");
}
}
现在,我们按如下方式调用此小应用程序 −
<html>
<title>Event Handling</title>
<hr>
<applet code = "ExampleEventHandling.class"
width = "300" height = "300">
</applet>
<hr>
</html>
最初,小应用程序将显示“初始化小应用程序。正在启动小应用程序。”然后,一旦您点击矩形内的某个位置,“鼠标已点击”也将显示出来。
Displaying Images
小应用程序可以显示 GIF、JPEG、BMP 等格式的图像。若要在小应用程序内显示图像,请使用 java.awt.Graphics 类提供的 drawImage() 方法。
以下是说明显示图像的所有步骤的一个示例 −
Example
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);
}
}
现在,我们按如下方式调用此小应用程序 −
<html>
<title>The ImageDemo applet</title>
<hr>
<applet code = "ImageDemo.class" width = "300" height = "200">
<param name = "image" value = "java.jpg">
</applet>
<hr>
</html>
Playing Audio
小应用程序可以播放由 java.applet 包中的 AudioClip 接口表示的音频文件。AudioClip 接口有三个方法,包括 −
-
public void play() - 从头开始播放音频片段一次。
-
public void loop() - 使音频片段连续播放。
-
public void stop() - 停止播放音频片段。
要获取 AudioClip 对象,您必须调用 Applet 类的 getAudioClip() 方法。getAudioClip() 方法会立即返回,而不管 URL 是否解析为实际的音频文件。只有在尝试播放音频片段时才会下载音频文件。
以下是说明播放音频的所有步骤的一个示例 −
Example
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();
}
}
}
现在,我们按如下方式调用此小应用程序 −
<html>
<title>The ImageDemo applet</title>
<hr>
<applet code = "ImageDemo.class" width = "0" height = "0">
<param name = "audio" value = "test.wav">
</applet>
<hr>
</html>
可在您的 PC 上使用 test.wav 测试上述示例。
Java - Documentation Comments
Java 语言支持三种类型的注释 −
Sr.No. |
Comment & Description |
1 |
/ 文本 / 编译器忽略所有从 /* 到 */ 的内容。 |
2 |
//text 编译器忽略所有从 // 到行尾的内容。 |
3 |
/ * 文档 / * This is a documentation comment and in general its called *doc comment 。 JDK javadoc 工具在准备自动生成的文档时使用文档注释。 |
本章重点是解释 Javadoc。我们将了解如何使用 Javadoc 来生成 Java 代码的有用文档。
What is Javadoc?
Javadoc 是一个随 JDK 提供的工具,它用于从需要进行预定义格式文档的 Java 源代码生成 HTML 格式的 Java 代码文档。
以下是一个简单的示例,其中 /…./ 内部的是 Java 多行注释。类似地,// 前面的行是 Java 单行注释。
Example
/**
* The HelloWorld program implements an application that
* simply displays "Hello World!" to the standard output.
*
* @author Zara Ali
* @version 1.0
* @since 2014-03-31
*/
public class HelloWorld {
public static void main(String[] args) {
// Prints Hello, World! on standard output.
System.out.println("Hello World!");
}
}
Example
/**
* <h1>Hello, World!</h1>
* The HelloWorld program implements an application that
* simply displays "Hello World!" to the standard output.
* <p>
* Giving proper comments in your program makes it more
* user friendly and it is assumed as a high quality code.
*
*
* @author Zara Ali
* @version 1.0
* @since 2014-03-31
*/
public class HelloWorld {
public static void main(String[] args) {
// Prints Hello, World! on standard output.
System.out.println("Hello World!");
}
}
The javadoc Tags
javadoc 工具识别以下标记:
Tag |
Description |
Syntax |
@author |
添加类的作者。 |
@author name-text |
{@code} |
以代码字体显示文本,不将文本解释为 HTML 标记或嵌套 javadoc 标记。 |
{@code text} |
{@docRoot} |
代表从任意已生成页面到已生成文档根目录的相对路径。 |
{@docRoot} |
@deprecated |
添加注释,表明此 API 不应再被使用。 |
@deprecated deprecatedtext |
@exception |
向生成的文档中添加*Throws*小标题,其中包括类名和描述文本。 |
@exception class-name description |
{@inheritDoc} |
*nearest*从可继承类或可实现接口继承注释。 |
继承直接父类的注释。 |
{@link} |
插入使用可见文本标签的内联链接,该标签指向引用类指定的包、类或成员名称的文档。 |
{@link package.class#member label} |
{@linkplain} |
与 {@link} 相同,只是链接的标签以普通文本显示,而不是代码字体。 |
{@linkplain package.class#member label} |
@param |
将带有指定参数名称并后跟指定说明的参数添加到“参数”部分。 |
@param parameter-name description |
@return |
添加带有说明文本的“返回值”部分。 |
@return description |
@see |
添加带有指向参考的链接或文本项的“另请参见”标题。 |
@see reference |
@serial |
用于默认可序列化字段的文档注释中。 |
@serial field-description |
include |
exclude |
@serialData |
记录 writeObject( ) 或 writeExternal( ) 方法写入的数据。 |
@serialData data-description |
@serialField |
Documents an ObjectStreamField component. |
@serialField field-name field-type field-description |
@since |
将带有指定 since-text 的“Since”标题添加到生成的文档中。 |
@since release |
@throws |
@throws 和 @exception 标记是同义词。 |
@throws class-name description |
{@value} |
当在静态字段的文档注释中使用 {@value} 时,它会显示该常量的值。 |
{@value package.class#field} |
@version |
Example
下述程序使用文档注释中可用的少数几个重要标记。您可以根据您的要求使用其他标记。
有关 AddNum 类的文档会生成在 HTML 文件 AddNum.html 中,但同时也会创建具有名称 index.html 的主文件。
import java.io.*;
/**
* <h1>Add Two Numbers!</h1>
* The AddNum program implements an application that
* simply adds two given integer numbers and Prints
* the output on the screen.
* <p>
* <b>Note:</b> Giving proper comments in your program makes it more
* user friendly and it is assumed as a high quality code.
*
* @author Zara Ali
* @version 1.0
* @since 2014-03-31
*/
public class AddNum {
/**
* This method is used to add two integers. This is
* a the simplest form of a class method, just to
* show the usage of various javadoc Tags.
* @param numA This is the first paramter to addNum method
* @param numB This is the second parameter to addNum method
* @return int This returns sum of numA and numB.
*/
public int addNum(int numA, int numB) {
return numA + numB;
}
/**
* This is the main method which makes use of addNum method.
* @param args Unused.
* @return Nothing.
* @exception IOException On input error.
* @see IOException
*/
public static void main(String args[]) throws IOException {
AddNum obj = new AddNum();
int sum = obj.addNum(10, 20);
System.out.println("Sum of 10 and 20 is :" + sum);
}
}
让我们编译并运行上述程序,这将生成以下结果 −
Sum of 10 and 20 is :30
现在,使用 javadoc 实用程序按如下方式处理上述 AddNum.java 文件 -
$ javadoc AddNum.java
Loading source file AddNum.java...
Constructing Javadoc information...
Standard Doclet version 1.7.0_51
Building tree for all the packages and classes...
Generating /AddNum.html...
AddNum.java:36: 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...
1 warning
$
您可以在此处查看所有生成的文档 − AddNum。如果您正在使用 JDK 1.7,则 javadoc 不会生成很棒的 stylesheet.css,因此我们建议您从以下位置下载并使用标准样式表