Java编程语言最初由Sun Microsystems开发,由James Gosling发起,并于1995年发布,作为Sun Microsystems Java平台(Java 1.0 [J2SE])的核心组件。
Java标准版的最新版本是Java SE 8.随着Java的发展及其广泛的普及,创建了多种配置以适应各种类型的平台。例如:J2EE for Enterprise Applications,J2ME for Mobile Applications。
新的J2版本分别被重命名为Java SE,Java EE和Java ME。Java保证是一次写入,随时随地运行。
Java是 -
面向对象 - 在Java中,一切都是一个对象。Java可以轻松扩展,因为它基于Object模型。
平台独立 - 与许多其他编程语言(包括C和C ++)不同,当编译Java时,它不会编译为平台特定的机器,而是平台无关的字节码。该字节代码分布在Web上,并由虚拟机(JVM)在其正在运行的平台上进行解释。
简单 - Java旨在易于学习。如果您了解OOP Java的基本概念,那将很容易掌握。
安全 - 借助Java的安全功能,它可以开发无病毒,无篡改的系统。认证技术基于公钥加密。
架构中立 - Java编译器生成架构中立的对象文件格式,这使得编译代码在许多处理器上可执行,存在Java运行时系统。
便携式 - 由于架构中立,并且没有规范的实现依赖方面使Java可移植。Java编译器用ANSI C编写,带有一个干净的可移植性边界,它是一个POSIX子集。
强大的 - Java通过强调主要是编译时错误检查和运行时检查,努力消除容易出错的情况。
多线程 - 使用Java的多线程功能,可以编写可以同时执行许多任务的程序。此设计功能允许开发人员创建可以顺利运行的交互式应用程序。
解释 - Java字节代码是飞行转换为本地机器指令,不存储在任何地方。开发过程更加快速和分析,因为链接是一个增量和轻量级的过程。
高性能 - 通过使用即时编译器,Java可实现高性能。
分布式 - Java是专为互联网的分布式环境而设计的。
动态 - Java被认为比C或C ++更具动态性,因为它被设计为适应不断变化的环境。Java程序可以承载大量的运行时信息,可用于在运行时验证和解决对对象的访问。
詹姆斯·戈斯林(James Gosling)于1991年6月发起Java语言项目,用于他的许多机顶盒项目之一。在Gosling办公室外面的一棵橡树之后,这种语言最初叫做“橡树”,也是以“绿色”的名字命名的,后来被从Java列表中重新命名为随机单词。
Sun在1995年发布了第一个公共实现,就像Java 1.0一样。它承诺了一次写入,无处不在(WORA),在流行的平台上提供免费的运行时间。
2006年11月13日,Sun根据GNU通用公共许可证(GPL)的规定,将大量Java作为免费和开源软件发布。
2007年5月8日,Sun完成了这个过程,使得所有的Java的核心代码都是免费的和开源的,除了Sun没有版权的一小部分代码之外。
对于执行本教程中探讨的示例,您将需要一个至少64 MB RAM(推荐128 MB RAM)的Pentium 200-MHz计算机。
您还需要以下软件 -
本教程将提供使用Java创建GUI,网络和Web应用程序的必要技能。
我们为您提供了一个在线编译和执行可用代码的选项。只需点击代码窗口右上角的“ 尝试 ”按钮即可编译并执行可用的代码。有一些不能在线执行的例子,所以我们跳过了这些例子。
public class MyFirstJavaProgram { public static void main(String []args) { System.out.println("Hello World"); } }
可能有一种情况,您看不到编译/执行代码的结果。在这种情况下,您可以使用编译弹出窗口中可用的执行按钮重新尝试编译并执行代码。
下一章将指导您如何获取Java及其文档。最后,它指示您如何安装Java并准备开发Java应用程序的环境。
在本章中,我们将探讨如何为Java建立一个和谐的环境。
在线运行
我们已经在线设置了Java编程环境,因此可以在线编译和执行所有可用的示例。你可以学习的同时,在线验证程序。随意修改任何示例并在线执行。
使用我们的在线编译器可在尝试下面的例子CodingGround
public class MyFirstJavaProgram { public static void main(String []args) { System.out.println("Hello World"); } }对于本教程中给出的大多数示例,您将在右上角的网站代码部分找到一个Try it选项,该部分将带您进入在线编译器。所以只是利用它,享受你的学习。
如果您仍然愿意为Java编程语言设置环境,那么本节将介绍如何在您的计算机上下载和设置Java。以下是设置环境的步骤。
Java SE可以从链接下载Java免费获得。您可以根据您的操作系统下载一个版本。
按照说明下载Java并运行.exe在您的机器上安装Java。一旦在机器上安装了Java,您将需要设置环境变量来指向正确的安装目录 -
假设您已经在c: Program Files java jdk目录中安装了Java -
右键单击“我的电脑”,然后选择“属性”。
单击“高级”选项卡下的“环境变量”按钮。
现在,更改“Path”变量,以便它还包含Java可执行文件的路径。例如,如果路径当前设置为"C: WINDOWS SYSTEM32",则将路径更改为"C: WINDOWS SYSTEM32; c: Program Files java jdk bin"。
环境变量PATH应设置为指向Java二进制文件的安装位置。参考你的shell文档,如果你有麻烦这样做。
例如,如果您使用bash作为您的shell,那么您可以将以下行添加到“.bashrc:export PATH = / path / to / java:$ PATH”的末尾。
要编写Java程序,您需要一个文本编辑器。市场上还有更复杂的IDE。但是现在,您可以考虑以下之一 -
记事本 - 在Windows机器上,您可以使用任何简单的文本编辑器,如记事本(本教程推荐),TextPad。
Netbeans - 一个开放源代码和免费的Java IDE,可从https://www.netbeans.org/index.html下载。
Eclipse - 由Eclipse开源社区开发的Java IDE,可从https://www.eclipse.org/下载。
下一章将教您如何编写和运行您的第一个Java程序以及开发应用程序所需的Java中的一些重要基本语法。
当我们考虑一个Java程序时,它可以被定义为通过调用彼此的方法进行通信的对象的集合。现在让我们简要地看一下类,对象,方法和实例变量的含义。
对象 - 对象有状态和行为。示例:一只狗有颜色,名称,品种以及Swing尾巴,吠叫,吃饭等行为。一个对象是一个类的一个实例。
类 - 可以将类定义为描述其类型对象支持的行为/状态的模板/蓝图。
方法 - 一种方法基本上是一种行为。一个类可以包含很多方法。在逻辑写入的方法中,数据被操纵并且执行所有的动作。
实例变量 - 每个对象都有其唯一的实例变量集。对象的状态由分配给这些实例变量的值创建。
我们来看一个简单的代码,将打印“ 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 } }
我们来看看如何保存文件,编译和运行程序。请遵循后续步骤 -
打开记事本并添加上面的代码。
将文件保存为:MyFirstJavaProgram.java。
打开一个命令提示符窗口并转到保存该类的目录。假设它是C:。
键入"javac MyFirstJavaProgram.java",然后按Enter键编译代码。如果您的代码没有错误,命令提示符将带您到下一行(假设:设置路径变量)。
现在,输入"java MyFirstJavaProgram"来运行你的程序。
您将可以在窗口上看到“Hello World”。
C:> javac MyFirstJavaProgram.java C:> java MyFirstJavaProgram Hello World
关于Java程序,请记住以下几点:
案例灵敏度 - Java是区分大小写的,这意味着标识符Hello和hello在Java中具有不同的含义。
类名 - 对于所有类名,第一个字母应在大写。如果使用多个单词来形成类的名称,则每个内部单词的第一个字母应该是大写。
示例: MyFirstJavaClass类
方法名称 - 所有方法名称应以小写字母开头。如果使用多个单词来形成方法的名称,那么每个内部单词的第一个字母应该是大写。
示例: public void myMethodName()
程序文件名 - 程序文件的名称应与类名称完全相同。
保存文件时,您应该使用类名保存(记住Java是区分大小写的),并将".java"附加到名称的末尾(如果文件名和类名不匹配,则程序将不会编译)。
示例:假设"MyFirstJavaProgram"是类名。那么文件应该保存为"MyFirstJavaProgram.java"
public static void main(String args []) - Java程序处理从main()方法开始,这是每个Java程序的必需部分。
所有Java组件都需要名称。用于类,变量和方法的名称称为标识符。
在Java中,有几个要记住的标识符。他们如下 -
所有标识符应以字母(A到Z或a到z),货币字符($)或下划线(_)开头。
第一个字符后,标识符可以有任意的字符组合。
关键字不能用作标识符。
最重要的是,标识符区分大小写。
法定标识符示例:年龄,$ salary,_value,__1_value。
非法标识符示例:123abc,-salary。
像其他语言一样,可以通过使用修饰符修改类,方法等。有两类修饰符 -
访问修饰符 - 默认,公共,受保护,私有
非访问修饰符 - final,abstract,strictfp
我们将在下一节中详细介绍修饰符。
以下是Java中的变量类型 -
数组是存储相同类型的多个变量的对象。但是,数组本身就是堆上的一个对象。我们将在下一章中研究如何声明,构造和初始化。
枚举在Java 5.0中引入。枚举将变量限制为只有几个预定义值。枚举列表中的值被称为枚举。
使用枚举可以减少代码中的错误数量。
例如,如果我们考虑一个新鲜果汁店的申请,那么可以将玻璃尺寸限制在小,中,大。这将确保它不允许任何人订购除中小型以外的任何尺寸。
class FreshJuice { enum FreshJuiceSize{ SMALL, MEDIUM, LARGE } FreshJuiceSize size; } public class FreshJuiceTest { public static void main(String args[]) { FreshJuice juice = new FreshJuice(); juice.size = FreshJuice.FreshJuiceSize.MEDIUM ; System.out.println("Size: " + juice.size); } }
上述示例将产生以下结果 -
Size: MEDIUM
注 - 枚举可以被声明为自己或在课堂内。方法,变量,构造函数也可以在枚举内定义。
以下列表显示了Java中的保留字。这些保留字不能用作常量或变量或任何其他标识符名称。
抽象 | break | boolean | break |
byte | 案件 | catch | char |
class | const | continute | default |
do | double | other | enum |
延伸 | 最后 | 最后 | float |
对于 | go | 如果 | 工具 |
进口 | 实例 | int | 接口 |
long | 本地人 | new | 包 |
private | protect | public | return |
short | 静态的 | strictfp | 超 |
switch | 同步 | this | throw |
投掷 | 短暂的 | 尝试 | 无效 |
挥发性 | 而 |
Java支持与C和C ++非常相似的单行和多行注释。任何注释中可用的所有字符都被Java编译器忽略。
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"); } }
Hello World
一条只包含空白的行,可能有一个注释,被称为空白行,Java完全忽略它。
在Java中,类可以从类派生。基本上,如果您需要创建一个新类,并且这里已经是一个需要一些代码的类,那么可以从已经存在的代码中导出新的类。
这个概念允许您重用现有类的字段和方法,而无需重写新类中的代码。在这种情况下,现有类称为超类,派生类称为子类。
在Java语言中,可以将接口定义为对象之间如何进行通信的契约。接口在继承的概念上起着至关重要的作用。
接口定义了派生类(子类)应该使用的方法。但是方法的实现完全取决于子类。
下一节将介绍Java编程中的对象和类。在会话结束时,您将能够清楚地了解到什么是对象以及什么是Java中的类。
Java是面向对象的语言。作为具有面向对象功能的语言,Java支持以下基本概念 -
在本章中,我们将研究概念 - 类和对象。
对象 - 对象有状态和行为。示例:一只狗有颜色,名称,品种以及行为 - 摆动尾巴,吠叫,吃饭。一个对象是一个类的一个实例。
类 - 可以将类定义为描述其类型对象支持的行为/状态的模板/蓝图。
现在让我们深入了解什么是对象。如果我们考虑现实世界,我们可以在我们周围找到许多对象,汽车,狗,人类等等。所有这些对象都有一种状态和行为。
如果我们考虑一只狗,那么它的状态是 - 名称,品种,颜色,行为 - 吠叫,拖尾,跑步。
如果将软件对象与真实对象进行比较,则它们具有非常相似的特性。
软件对象也具有状态和行为。软件对象的状态存储在字段中,行为通过方法显示。
因此,在软件开发中,方法对对象的内部状态进行操作,并且通过方法进行对象到对象的通信。
类是创建单个对象的蓝图。
以下是课程的样本。
public class Dog { String breed; int ageC String color; void barking() { } void hungry() { } void sleeping() { } }
类可以包含以下任何变量类型。
局部变量 - 在方法,构造函数或块中定义的变量称为局部变量。该变量将在方法内被声明和初始化,当该方法完成时变量将被破坏。
实例变量 - 实例变量是类中的变量,但不包括任何方法。当类被实例化时,这些变量被初始化。可以从该特定类的任何方法,构造函数或块内访问实例变量。
类变量 - 类变量是在类之外声明的变量,不包括任何方法,使用static关键字。
一个类可以有多种方法来访问各种方法的值。在上面的例子中,barking(),hungry()和sleeping()是方法。
以下是在查看Java语言的类时需要探讨的一些重要主题。
在探讨课程时,最重要的一个主题是构造函数。每个类都有一个构造函数。如果我们没有为类创建一个构造函数,那么Java编译器会为该类建立一个默认构造函数。
每次创建一个新对象时,至少会调用一个构造函数。构造函数的主要规则是它们应该与类具有相同的名称。一个类可以有多个构造函数。
以下是一个构造函数的例子 -
public class Puppy { public Puppy() { } public Puppy(String name) { // This constructor has one parameter, name. } }
Java还支持Singleton类,您将只能创建一个类的一个实例。
注 - 我们有两种不同类型的构造函数。我们将在随后的章节中详细探讨构造函数。
如前所述,一个类提供了对象的蓝图。所以基本上,一个对象是从一个类创建的。在Java中,new关键字用于创建新对象。
从类创建对象时有三个步骤 -
声明 - 具有变量名称和对象类型的变量声明。
实例化 - "new"关键字用于创建对象。
初始化 - “new”关键字之后是对构造函数的调用。此调用初始化新对象。
以下是创建对象的示例 -
public class Puppy { public Puppy(String name) { // This constructor has one parameter, name. 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" ); } }
如果我们编译并运行上述程序,那么输出结果如下 -
Passed Name is :tommy
通过创建的对象访问实例变量和方法。要访问实例变量,以下是完全限定的路径 -
/* 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();
本例解释了如何访问类的实例变量和方法。
public class Puppy { int puppyAge; public Puppy(String name) { // This constructor has one parameter, name. 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 ); } }
如果我们编译并运行上述程序,那么输出结果如下 -
Name chosen is :tommy Puppy"s age is :2 Variable Value :2
作为本节的最后一部分,现在来看看源文件声明规则。在源文件中声明类,导入语句和包语句时,这些规则至关重要。
每个源文件只能有一个公共类。
源文件可以有多个非公共类。
公共类名称应该是源文件的名称,最后由.java附加。例如:类名称是public class Employee {},那么源文件应该是Employee.java。
如果类在包中定义,那么包语句应该是源文件中的第一个语句。
如果存在import语句,则必须在包语句和类声明之间写入。如果没有包语句,那么import语句应该是源文件中的第一行。
导入和包语句将暗示源文件中存在的所有类。不可能在源文件中将不同的import和/或package语句声明到不同的类。
类有几个访问级别,有不同类型的类; 抽象类,最终类等。我们将在访问修饰符一章中解释所有这些。
除了上述类型的类之外,Java还有一些名为Inner类和Anonymous类的特殊类。
简单来说,它是对类和接口进行分类的一种方法。在Java中开发应用程序时,将会写入数百个类和接口,因此对这些类进行分类是必须的,并且使生活更容易。
在Java中,如果给出了包含包和类名称的完全限定名称,则编译器可以轻松找到源代码或类。导入语句是为编译器找到该特定类的正确位置的一种方式。
例如,以下行将要求编译器加载目录java_installation / java / io中可用的所有类 -
import java.io.*;
对于我们的案例研究,我们将创建两个类。他们是员工和员工测试。
首先打开记事本并添加以下代码。记住这是Employee类,该类是一个公共类。现在,保存名为Employee.java的源文件。
Employee类有四个实例变量 - 名称,年龄,指定和工资。该类有一个明确定义的构造函数,它接受一个参数。
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); } }
如本教程前面所述,处理从主要方法开始。因此,为了让我们运行这个Employee类,应该有一个主要的方法,应该创建对象。我们将为这些任务创建一个单独的类。
以下是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来查看结果如下:
C:> javac Employee.java C:> javac EmployeeTest.java C:> java EmployeeTest Name:James Smith Age:26 Designation:Senior Software Engineer Salary:1000.0 Name:Mary Anne Age:21 Designation:Software Engineer Salary:500.0
在下一个会话中,我们将探讨Java中的基本数据类型,以及如何在开发Java应用程序时使用它们。
变量只不过是保存存储值的内存位置。这意味着当您创建一个变量时,您可以在内存中保留一些空间。
基于变量的数据类型,操作系统分配内存并决定可以存储在预留内存中的内容。因此,通过为变量分配不同的数据类型,可以在这些变量中存储整数,小数或字符。
Java中有两种可用的数据类型 -
Java支持八种基本数据类型。原始数据类型由语言预定义并由关键字命名。现在让我们详细研究八个基本的数据类型。
字节数据类型是一个8位有符号二进制补码整数
最小值为-128(-2 ^ 7)
最大值为127(含)(2 ^ 7 -1)
默认值为0
字节数据类型用于节省大数组中的空间,主要取代整数,因为一个字节比整数小四倍。
示例:字节a = 100,字节b = -50
短数据类型是16位有符号二进制补码整数
最小值为-32,768(-2 ^ 15)
最大值为32,767(含)(2 ^ 15 -1)
短数据类型也可用于将内存节省为字节数据类型。短小于整数的2倍
默认值为0。
示例:short s = 10000,短r = -20000
Int数据类型是32位有符号二进制补码整数。
最小值为-2,147,483,648(-2 ^ 31)
最大值为2,147,483,647(含)(2 ^ 31 -1)
整数通常用作积分值的默认数据类型,除非对内存有疑虑。
默认值为0
示例:int a = 100000,int b = -200000
浮点数据类型是单精度32位IEEE 754浮点数
Float主要用于在大量浮点数组中节省内存
默认值为0.0f
float数据类型从不用于诸如货币的精确值
示例:float f1 = 234.5f
双数据类型是双精度64位IEEE 754浮点数
该数据类型通常用作十进制值的默认数据类型,通常是默认选择
double数据类型不应该用于精确的值,如货币
默认值为0.0d
示例:double d1 = 123.4
引用变量使用定义的类构造函数创建。它们用于访问对象。这些变量被声明为不能更改的特定类型。例如,员工,小狗等
类对象和各种类型的数组变量都属于引用数据类型。
任何引用变量的默认值为null。
引用变量可用于引用声明类型或任何兼容类型的任何对象。
示例:动物动物=新动物(“长颈鹿”);
字面值是固定值的源代码表示。它们直接在代码中进行表示,无需任何计算。
文字可以分配给任何基本类型的变量。例如 -
byte a = 68; char a = "A"
字节,int,long和short可以表示为十进制(10位),十六进制(16位)或八进制(8位)数字系统。
前缀0用于指示八进制,前缀0x表示十六进制,当使用这些数字系统的文字时。例如 -
int decimal = 100; int octal = 0144; int hexa = 0x64;
Java中的字符string字面值通过在一对双引号之间包含一系列字符而被指定为大多数其他语言。字符string文字的例子是 -
"Hello World" "two lines" ""This is in quotes""
字符string和字符类型的文字可以包含任何Unicode字符。例如 -
char a = "u0001"; String a = "u0001";
Java语言对于String和char文字也支持很少的特殊转义序列。他们是 -
符号 | 字符代表 |
---|---|
n | 换行(0x0a) |
r | 回车(0x0d) |
F | Formfeed(0x0c) |
b | 退格(0x08) |
s | 空格(0x20) |
t | 标签 |
“ | 双引号 |
" | 单引号 |
反斜杠 | |
ddd | 八进制角色(ddd) |
uxxxx | 十六进制UNICODE字符(xxxx) |
本章介绍了各种数据类型。下一个主题解释不同的变量类型及其用法。这将使您很好地了解如何在Java类,接口等中使用它们。
一个变量为我们提供了我们的程序可以操作的命名存储。Java中的每个变量都有一个特定的类型,它决定了变量内存的大小和布局; 可以存储在该存储器中的值的范围; 以及可以应用于变量的一组操作。
您必须先声明所有变量才能使用。以下是变量声明的基本形式 -
data type variable [ = value][, variable [ = value] ...] ;
这里的数据类型是Java的数据类型之一,变量是变量的名称。要声明指定类型的多个变量,可以使用逗号分隔的列表。
以下是Java中变量声明和初始化的有效示例 -
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中有三种变量 -
局部变量在方法,构造函数或块中声明。
当输入方法,构造函数或块时,创建局部变量,一旦退出方法,构造函数或块,变量将被销毁。
访问修饰符不能用于局部变量。
局部变量仅在声明的方法,构造函数或块中可见。
局部变量在堆栈级内部实现。
局部变量没有默认值,因此应该声明局部变量,并在首次使用前分配初始值。
在这里,年龄是一个局部变量。这在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(); } }
输出结果如下 -
Puppy age is: 7
以下示例使用age而不初始化它,因此在编译时会给出错误。
public class Test { public void pupAge() { int age; age = age + 7; System.out.println("Puppy age is : " + age); } public static void main(String args[]) { Test test = new Test(); test.pupAge(); } }
这会在编译时产生以下错误 -
Test.java:4:variable number might not have been initialized age = age + 7; ^ 1 error
实例变量在类中声明,但在方法,构造函数或任何块之外。
当为堆中的对象分配空间时,将为每个实例变量值创建一个插槽。
当使用关键字"new"创建对象时,将创建实例变量,并在对象被销毁时被销毁。
实例变量保存必须由多个方法,构造函数或块或必须在整个类中存在的对象状态的必要部分引用的值。
实例变量可以在使用之前或之后在类级别声明。
访问修饰符可以给出实例变量。
实例变量对类中的所有方法,构造函数和块都是可见的。通常,建议将这些变量设为私有(访问级别)。然而,可以使用访问修饰符为这些变量提供子类的可见性。
实例变量具有默认值。对于数字,默认值为0,Booleans为false,对象引用为null。值可以在声明期间或在构造函数中分配。
可以通过调用类中的变量名直接访问实例变量。但是,在静态方法(当实例变量被赋予可访问性)时,应使用完全限定名来调用它们。ObjectReference.VariableName。
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(); } }
输出结果如下 -
name : Ransika salary :1000.0
类变量也称为静态变量,在类中使用static关键字声明,但在方法,构造函数或块之外。
每个类的每个类变量只能有一个副本,无论从中创建多少个对象。
静态变量很少被使用,而不是被声明为常量。常量是被声明为public / private,final和static的变量。常数变量从不改变其初始值。
静态变量存储在静态存储器中。除了声明final之外,使用静态变量也很少使用,作为公共或私有常量。
当程序停止时程序启动和销毁时,将创建静态变量。
可见性类似于实例变量。然而,大多数静态变量被声明为public,因为它们必须可用于该类的用户。
默认值与实例变量相同。对于数字,默认值为0; 对于布尔,这是假的; 对于对象引用,它为null。值可以在声明期间或在构造函数中分配。另外,可以在特殊静态初始化程序块中分配值。
静态变量可以通过调用ClassName.VariableName类来访问。
当将类变量声明为public static final时,变量名(常量)都是大写的。如果静态变量不是public和final,则命名语法与实例和局部变量相同。
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); } }
输出结果如下 -
Development average salary:1000
注意 - 如果从外部类访问变量,则常量应作为Employee.DEPARTMENT访问
本章中已经使用了访问修饰符(public和private)。下一章将详细介绍访问修饰符和非访问修饰符。
修饰符是您添加到这些定义以更改其含义的关键字。Java语言有各种各样的修饰符,包括以下内容 -
要使用修饰符,请将其关键字包含在类,方法或变量的定义中。修饰符在语句的其余部分之前,如以下示例所示。
public class className { // ... } private boolean myFlag; static final double weeks = 9.5; protected static final int BOXWIDTH = 42; public static void main(String[] arguments) { // body of method }
Java提供了许多访问修饰符来设置类,变量,方法和构造函数的访问级别。四个访问级别是 -
Java提供了许多非访问修饰符来实现许多其他功能。
用于创建类方法和变量的静态修饰符。
在最终的定稿类,方法和变量的实现修改。
用于创建抽象类和方法的抽象修饰符。
的同步和挥发性改性剂,其用于螺纹。
在下一节中,我们将探讨Java语言中使用的基本运算符。本章将为您介绍如何在应用程序开发过程中使用这些操作符。
Java提供了一组丰富的操作符来操纵变量。我们可以将所有Java运算符分成以下组 -
算术运算符与数学表达式中使用的代数方法相同。下表列出了算术运算符 -
假设整数变量A保持10,变量B保持20,则 -
操作符 | 描述 | 例 |
---|---|---|
+(加法) | 在操作符的两边添加值。 | A + B将给予30 |
- (减) | 从左手操作数减去右手操作数。 | A - B会给-10 |
*(乘法) | 在运算符的任一侧乘以值。 | A * B将给予200 |
/(司) | 用右手操作数除以左手操作数。 | B / A将给予2 |
%(模量) | 用右手操作数除以左手操作数,并返回余数。 | B%A将给0 |
++(增量) | 将操作数的值增加1。 | B ++给了21 |
- (减) | 将操作数的值减少1。 | B--给了19 |
Java语言支持以下关系运算符。
假设变量A保持10,变量B保持20,则 -
操作符 | 描述 | 例 |
---|---|---|
==(等于) | 检查两个操作数的值是否相等,如果是,则条件成立。 | (A == B)不正确。 |
!=(不等于) | 检查两个操作数的值是否相等,如果值不等于条件成为真。 | (A!= B)是真的。 |
>(大于) | 检查左操作数的值是否大于右操作数的值,如果是,则条件成为真。 | (A> B)不正确。 |
(少于) | 检查左操作数的值是否小于右操作数的值,如果是,则条件成为真。 | (A <B)是真的。 |
> =(大于或等于) | 检查左操作数的值是否大于或等于右操作数的值,如果是,则条件成为真。 | (A> = B)不正确。 |
<=(小于或等于) | 检查左操作数的值是否小于或等于右操作数的值,如果是,则条件成为真。 | (A <= B)是真的。 |
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,则 -
操作符 | 描述 | 例 |
---|---|---|
&(按位和) | 如果二进制AND运算符存在于两个操作数中,则二进制AND运算符将对结果复制一位。 | (A和B)将给出12是0000 1100 |
| (按位或) | 二进制OR运算符如果存在于任一操作数中,则复制一位。 | (A | B)将给出61,其为0011 1101 |
^(按位异或) | 二进制XOR操作符复制该位,如果它设置在一个操作数中,而不是两者。 | (A ^ B)将给出49,其为0011 0001 |
(按位称赞) | 二进制补码运算符是一元的,具有“翻转”位的作用。 | (A)将给出-61,由于有符号的二进制数,它是2 0的补码形式的1100 0011。 |
<<(左移) | 二进制左移操作符。左操作数值左移由右操作数指定的位数。 | A << 2将给出240是1111 0000 |
>>(右移) | 二进制右移操作符。左操作数值被右操作数指定的位移动。 | A >> 2将给出15是1111 |
>>>(零填补右移) | 右移零填充操作符。左操作数值右移由右操作数指定的位数,移位值用零填充。 | A >>> 2将给出15是0000 1111 |
下表列出了逻辑运算符 -
假设布尔变量A为真,变量B为假,则 -
操作符 | 描述 | 例 |
---|---|---|
&&(逻辑和) | 称为逻辑AND运算符。如果两个操作数都不为零,则条件成立。 | (A && B)是假的 |
|| (逻辑或) | 称为逻辑或运算符。如果两个操作数中的任何一个非零,则条件成立。 | (A || B)是真的 |
!(逻辑不) | 称为逻辑非运算符。用于反转其操作数的逻辑状态。如果条件为真,则逻辑NOT运算符将为false。 | !(A && B)是真的 |
以下是Java语言支持的赋值运算符 -
操作符 | 描述 | 例 |
---|---|---|
= | 简单赋值运算符。将右侧操作数的值分配给左侧操作数。 | C = A + B将A的值分配给C |
+ = | 添加AND赋值运算符。它将右操作数添加到左操作数,并将结果分配给左操作数。 | C + = A等价于C = C + A |
- = | 减去AND赋值运算符。它从左操作数中减去右操作数,并将结果分配给左操作数。 | C - = A等价于C = C-A |
* = | 乘以AND赋值运算符。它将右操作数与左操作数相乘,并将结果分配给左操作数。 | C * = A等价于C = C * A |
/ = | 除以AND赋值运算符。它将左操作数与右操作数分开,并将结果分配给左操作数。 | C / = A等价于C = C / A |
%= | 模数和赋值运算符。它需要使用两个操作数的模数,并将结果分配给左操作数。 | C%= A等价于C = C%A |
<< = | 左移和赋值运算符。 | C << = 2与C = C << 2相同 |
>> = | 右移和赋值运算符。 | C&= 2与C = C&2相同 |
&= | 按位AND赋值运算符。 | C >> = 2与C = C >> 2相同 |
^ = | 按位异或运算符和赋值运算符。 | C ^ = 2与C = C ^ 2相同 |
| = | 按位包含OR和赋值运算符。 | C | = 2与C = C |相同 2 |
Java语言支持的其他操作系统还不多。
条件运算符也称为三进制运算符。该运算符由三个操作数组成,用于评估布尔表达式。运算符的目标是决定哪个值应该分配给变量。操作符写为 -
variable x = (expression) ? value if true : value if false
以下是一个例子 -
例
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 ); } }
输出结果如下 -
输出
Value of b is : 30 Value of b is : 20
此运算符仅用于对象引用变量。操作符检查对象是否是特定类型(类类型或接口类型)。instanceof运算符写为 -
( Object reference variable ) instanceof (class/interface type)
如果由操作符左侧的变量引用的对象通过右侧的类/接口类型的IS-A检查,则结果将为真。以下是一个例子 -
例
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 ); } }
输出结果如下 -
输出
true
如果正在比较的对象与右侧的类型兼容,则该运算符仍将返回true。以下是另一个例子 -
例
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 ); } }
输出结果如下 -
输出
true
运算符优先级决定表达式中术语的分组。这会影响表达式的评估。某些运算符的优先级高于其他运算符; 例如,乘法运算符的优先级高于加法运算符 -
例如,x = 7 + 3 * 2; 这里x被分配13,而不是20,因为operator *的优先级高于+,所以它首先被乘以3 * 2,然后加到7中。
这里,优先级最高的运算符出现在表的顶部,最底层的运算符出现在底部。在一个表达式中,先优先级较高的算子。
类别 | 操作符 | 关联性 |
---|---|---|
后缀 | >()[]。(点运算符) | 左到右 |
一元 | > ++ - - ! | 右到左 |
乘法 | > * / | 左到右 |
添加剂 | > + - | 左到右 |
转移 | >>> >>> << | 左到右 |
关系型 | >>> = <<= | 左到右 |
平等 | > ==!= | 左到右 |
按位AND | >& | 左到右 |
按位XOR | > ^ | 左到右 |
按位OR | > | | 左到右 |
逻辑与 | > && | 左到右 |
逻辑或 | > || | 左到右 |
有条件的 | ?: | 右到左 |
分配 | > = + = - = * = / =%= >> = << =&= ^ = | = | 右到左 |
下一章将介绍Java编程中的循环控制。本章将介绍各种类型的循环,以及如何在Java程序开发中使用这些循环以及它们正在使用的目的。
当您需要执行一段代码多次时可能会出现这种情况。一般来说,语句依次执行:函数中的第一个语句先执行,后跟第二个语句,依此类推。
编程语言提供了允许更复杂的执行路径的各种控制结构。
一个循环语句允许我们执行的语句多次声明或组,下面是一个循环语句的一般形式在大多数编程语言-
Java编程语言提供以下类型的循环来处理循环需求。单击以下链接以查看其详细信息。
没有 | 循环和描述 |
---|---|
1 | while循环
在给定条件为真时,重复一个语句或一组语句。它在执行循环体之前测试状态。 |
2 | for循环
多次执行一系列语句,并缩写管理循环变量的代码。 |
3 | do... while循环
像while语句一样,除了它测试循环体结尾的条件。 |
循环控制语句从其正常顺序更改执行。当执行离开范围时,在该范围内创建的所有自动对象都将被销毁。
Java支持以下控制语句。单击以下链接以查看其详细信息。
没有 | 控制声明和说明 |
---|---|
1 | break
终止循环或切换语句,并将执行转移到循环或切换后立即执行。 |
2 | continute
导致循环跳过其身体的剩余部分,并在重申之前立即重新测试其状态。 |
从Java 5开始,引入了增强的for循环。这主要用于遍历元素的集合,包括数组。
以下是增强for循环的语法 -
for(declaration : expression) { // Statements }
声明 - 新声明的块变量是与正在访问的数组的元素兼容的类型。该变量将在for块中可用,其值将与当前数组元素相同。
表达式 - 这将评估到您需要循环的数组。表达式可以是返回数组的数组变量或方法调用。
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(" "); String [] names = {"James", "Larry", "Tom", "Lacy"}; for( String name : names ) { System.out.print( name ); System.out.print(","); } } }
输出结果如下 -
10, 20, 30, 40, 50, James, Larry, Tom, Lacy,
在下一章中,我们将学习Java编程中的判断语句。
判断结构具有一个或多个要由该程序评估或测试的条件,以及如果条件被确定为真,将执行的陈述或声明,以及可选地,如果条件被确定则要执行的其他语句是假的
以下是大多数编程语言中的典型判断结构的一般形式 -
Java编程语言提供以下类型的判断语句。单击以下链接以查看其详细信息。
没有 | 声明和说明 |
---|---|
1 | if语句
一个if语句由一个布尔表达式,后跟一个或多个语句组成。 |
2 | if... else语句
一个if语句可以跟随一个可选的else语句,当布尔表达式为false时,该语句将执行。 |
3 | 嵌套if语句
您可以在另一个if或else语句中使用一个if或else if语句。 |
4 | switch
甲switch语句允许一个变量来针对值的列表平等进行测试。 |
我们已经涵盖了条件运算符?:在上一章中可以用来替换if else else语句。它具有以下一般形式 -
Exp1 ? Exp2 : Exp3;
其中Exp1,Exp2和Exp3是表达式。注意冒号的使用和位置。
要确定整个表达式的值,首先评估exp1。
如果exp1的值为true,则Exp2的值将是整个表达式的值。
如果exp1的值为false,则对Exp3进行求值,其值将变为整个表达式的值。
在下一章中,我们将探讨Java语言中的Number类(在java.lang包中)及其子类。
我们将研究一些您将使用这些类的实例的情况,而不是原始数据类型,以及类别,例如格式化,数学函数,您需要了解何时使用数字。
通常,当我们使用Numbers时,我们使用原始数据类型,如byte,int,long,double等
int i = 5000; float gpa = 13.65; double mask = 0xaf;
然而,在开发中,我们遇到需要使用对象而不是基本数据类型的情况。为了实现这一点,Java提供了包装类。
所有包装类(整数,长,字节,双,浮点,短)是抽象类Number的子类。
包装类的对象包含或包装其各自的基本数据类型。将原始数据类型转换为对象称为装箱,并由编译器进行注意。因此,在使用包装类时,您只需要将原始数据类型的值传递给Wrapper类的构造函数。
并且Wrapper对象将被转换回原始数据类型,此过程称为取消装箱。该号码类是java.lang包的一部分。
以下是拳击和拆箱的例子 -
public class Test { public static void main(String args[]) { Integer x = 5; // boxes int to an Integer object x = x + 10; // unboxes the Integer to a int System.out.println(x); } }
输出结果如下 -
15
当x被分配一个整数值时,编译器将整数整数,因为x是整数对象。之后,x被取消装箱,以便它们可以作为一个整数被添加。
以下是Number类的所有子类实现的实例方法的列表:
没有 | 方法和说明 |
---|---|
1 | xxxValue()
将此 Number对象的值转换为xxx数据类型并返回。 |
2 | 相比于()
比较这 Number对象的说法。 |
3 | 等于()
确定此数字对象是否等于参数。 |
4 | 的价值()
返回一个保存指定基元值的整数对象。 |
5 | toString()
返回一个表示指定int或Integer值的String对象。 |
6 | parseInt()
该方法用于获取某个字符string的原始数据类型。 |
7 | abs()
返回参数的绝对值。 |
8 | ceil()
返回大于或等于参数的最小整数。以double身份退回 |
9 | 地板()
返回小于或等于参数的最大整数。以double身份退回 |
10 | rint()
返回与参数最接近的整数。以double身份退回 |
11 | 回合()
返回最接近的long或int,如方法返回类型所指示的那样。 |
12 | min()
返回两个参数中较小的一个。 |
13 | max()
返回两个参数中较大的一个。 |
14 | exp()
返回自然对数的基数e,取决于参数的幂。 |
15 | log()
返回参数的自然对数。 |
16 | pow()
将第一个参数的值返回到第二个参数的幂。 |
17 | sqrt()
返回参数的平方根。 |
18 | 罪()
返回指定double值的正字符string值。 |
19 | cos()
返回指定double值的余字符string值。 |
20 | 谭()
返回指定double值的正切值。 |
21 | asin()
返回指定double值的反正字符string值。 |
22 | acos()
返回指定double值的反余字符string。 |
23 | 晒黑()
返回指定double值的反正切值。 |
24 | atan2()
将直角坐标(x,y)转换为极坐标(r,θ)并返回θ。 |
25 | toDegrees()
将参数转换为度。 |
26 | toRadians()
将参数转换为弧度。 |
27 | 随机()
返回随机数。 |
在下一节中,我们将介绍Java中的Character类。您将学习如何使用Java中的对象字符和原始数据类型char。
通常,当我们使用字符时,我们使用原始数据类型char。
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 提供了包装类Character。
Character类提供了一些用于操作字符的有用类(即静态)方法。您可以使用Character构造函数创建一个Character对象 -
Character ch = new Character("a");
在某些情况下,Java编译器还将为您创建一个Character对象。例如,如果您将原始字符传递给期望对象的方法,则编译器会自动将char转换为字符。此功能称为自动装箱或拆箱,如果转换成另一种方式。
// 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");
前面带有反斜杠()的字符是一个转义序列,对编译器有特殊的含义。
在本教程中,System.out.println()语句中经常使用换行字符( n),以便在打印字符string后前进到下一行。
下表显示了Java转义序列 -
逃脱序列 | 描述 |
---|---|
t | 此时在文本中插入一个选项卡。 |
b | 此时在文本中插入退格。 |
n | 此时在文本中插入换行符。 |
r | 此时在文本中插入回车符。 |
F | 此时在文本中插入换页。 |
" | 此时在文本中插入单引号。 |
“ | 此时在文本中插入双引号。 |
此时在文本中插入反斜杠字符。 |
当打印语句中遇到转义序列时,编译器会相应地解释它。
如果要在引号内加上引号,您必须在内部引号上使用转义序列“”
public class Test { public static void main(String args[]) { System.out.println("She said "Hello!" to me."); } }
输出结果如下 -
She said "Hello!" to me.
以下是Character类的所有子类实现的重要实例方法的列表 -
没有 | 方法和说明 |
---|---|
1 | isLetter()
确定指定的char值是否为字母。 |
2 | isDigit()
确定指定的char值是否为数字。 |
3 | isWhitespace()
确定指定的char值是否为空格。 |
4 | isUpperCase()
确定指定的char值是否为大写。 |
5 | isLowerCase()
确定指定的char值是否为小写。 |
6 | toUpperCase()
返回指定字符值的大写形式。 |
7 | toLowerCase()
返回指定字符值的小写形式。 |
8 | toString()
返回一个String对象,表示指定的字符值,即一个字符的字符string。 |
有关方法的完整列表,请参阅java.lang.Character API规范。
在下一节中,我们将介绍Java中的String类。您将学习如何有效地声明和使用Strings以及String类中的一些重要方法。
在Java编程中广泛使用的字符string是字符序列。在Java编程语言中,字符string被视为对象。
Java平台提供了String类来创建和操作字符string。
创建字符string的最直接的方式是写 -
String greeting = "Hello world!";
无论何时遇到代码中的字符string字面值,编译器会在这种情况下创建一个String对象,其值为“Hello world!”。
与任何其他对象一样,您可以使用new关键字和构造函数来创建String对象。String类有11个构造函数,允许您使用不同的源(例如字符数组)来提供字符string的初始值。
public class StringDemo { public static void main(String args[]) { char[] helloArray = { "h", "e", "l", "l", "o", "." }; String helloString = new String(helloArray); System.out.println( helloString ); } }
输出结果如下 -
hello.
注意 - String类是不可变的,因此一旦创建了String对象就无法更改。如果有必要对字符string进行大量修改,那么您应该使用String Buffer&String Builder类。
用于获取有关对象的信息的方法称为访问器方法。一个可以使用字符string的访问器方法是length()方法,它返回字符string对象中包含的字符数。
以下程序是length(),方法String类的示例。
public class StringDemo { public static void main(String args[]) { String palindrome = "Dot saw I was Tod"; int len = palindrome.length(); System.out.println( "String Length is : " + len ); } }
输出结果如下 -
String Length is : 17
String类包括一个连接两个字符string的方法 -
string1.concat(string2);
这将返回一个新的字符string,该字符string是string1,最后添加了string2。您也可以使用concat()方法与字符string文字,如 -
"My name is ".concat("Zara");
字符string通常与+运算符连接,如 -
"Hello," + " world" + "!"
这导致 -
"Hello, world!"
我们来看下面的例子 -
public class StringDemo { public static void main(String args[]) { String string1 = "saw I was "; System.out.println("Dot " + string1 + "Tod"); } }
输出结果如下 -
Dot saw I was Tod
您可以使用printf()和format()方法打印具有格式化数字的输出。String类具有等效的类方法format(),它返回String对象而不是PrintStream对象。
使用String的静态格式()方法可以创建一个可重用的格式化字符string,而不是一次性的print语句。例如,而不是 -
System.out.printf("The value of the float variable is " + "%f, while the value of the integer " + "variable is %d, and the string " + "is %s", floatVar, intVar, stringVar);
你可以写 -
String fs; fs = String.format("The value of the float variable is " + "%f, while the value of the integer " + "variable is %d, and the string " + "is %s", floatVar, intVar, stringVar); System.out.println(fs);
以下是String类支持的方法列表 -
没有 | 方法和说明 |
---|---|
1 | char charAt(int index)
返回指定索引处的字符。 |
2 | int compareTo(Object o)
将此String与另一个对象进行比较。 |
3 | int compareTo(String anotherString)
按字典顺序比较两个字符string。 |
4 | int compareToIgnoreCase(String str)
按字典顺序比较两个字符string,忽略病例差异。 |
5 | String concat(String str)
将指定的字符string连接到该字符string的末尾。 |
6 | boolean contentEquals(StringBuffer sb)
当且仅当此String表示与指定的StringBuffer相同的字符序列时,才返回true。 |
7 | static String copyValueOf(char [] data)
返回一个字符string,表示指定的数组中的字符序列。 |
8 | static String copyValueOf(char [] data,int offset,int count)
返回一个字符string,表示指定的数组中的字符序列。 |
9 | boolean endsWith(String suffix)
测试此字符string是否以指定的后缀结尾。 |
10 | boolean equals(Object anObject)
将此字符string与指定对象进行比较。 |
11 | boolean equalsIgnoreCase(String anotherString)
将此String与另一个String进行比较,忽略大小写注意事项。 |
12 | byte getBytes()
使用平台的默认字符集将此String编码为一系列字节,将结果存储到新的字节数组中。 |
13 | byte [] getBytes(String charsetName)
使用命名的字符集将此String编码为一系列字节,将结果存储到新的字节数组中。 |
14 | void getChars(int srcBegin,int srcEnd,char [] dst,int dstBegin)
将此字符string中的字符复制到目标字符数组中。 |
15 | int hashCode()
返回此字符string的哈希码。 |
16 | int indexOf(int ch)
返回指定字符第一次出现的字符string内的索引。 |
17 | int indexOf(int ch,int fromIndex)
返回指定字符第一次出现的字符string内的索引,以指定的索引开始搜索。 |
18 | int indexOf(String str)
返回指定子字符string第一次出现的字符string内的索引。 |
19 | int indexOf(String str,int fromIndex)
返回指定子string的第一次出现的字符string中的索引,从指定的索引开始。 |
20 | String intern()
返回字符string对象的规范表示。 |
21 | int lastIndexOf(int ch)
返回指定字符的最后一次出现的字符string中的索引。 |
22 | int lastIndexOf(int ch,int fromIndex)
返回指定字符的最后一次出现的字符string中的索引,从指定的索引开始向后搜索。 |
23 | int lastIndexOf(String str)
返回指定子字符string最右边出现的字符string内的索引。 |
24 | int lastIndexOf(String str,int fromIndex)
返回指定子字符string的最后一次出现的字符string中的索引,从指定索引开始向后搜索。 |
25 | int length()
返回此字符string的长度。 |
26 | 布尔匹配(String regex)
告诉这个字符string是否与给定的正则表达式匹配。 |
27 | boolean regionMatches(boolean ignoreCase,int toffset,String other,int ooffset,int len)
测试两个字符string区域是否相等。 |
28 | boolean regionMatches(int toffset,String other,int ooffset,int len)
测试两个字符string区域是否相等。 |
29 | 字符string替换(char oldChar,char newChar)
返回一个新的字符string,它是用newChar替换此字符string中所有出现的oldChar的结果。 |
30 | String replaceAll(String regex,String replacement
用给定的替换替换与给定正则表达式匹配的此字符string的每个子字符string。 |
31 | String replaceFirst(String regex,String replacement)
用给定的替换替换与给定正则表达式匹配的此字符string的第一个子字符string。 |
32 | String [] split(String regex)
将此字符string拆分为给定正则表达式的匹配项。 |
33 | String [] split(String regex,int limit)
将此字符string拆分为给定正则表达式的匹配项。 |
34 | boolean startsWith(String prefix)
测试此字符string是否以指定的前缀开头。 |
35 | boolean startsWith(String prefix,int toffset)
测试此字符string是否以指定的前缀开头,指定一个指定的索引。 |
36 | CharSequence subSequence(int beginIndex,int endIndex)
返回一个新的字符序列,该序列是该序列的子序列。 |
37 | 字符stringsubstring(int beginIndex)
返回一个新字符string,该字符string是此字符string的子字符string。 |
38 | 字符string子string(int beginIndex,int endIndex)
返回一个新字符string,该字符string是此字符string的子字符string。 |
39 | char [] toCharArray()
将此字符string转换为新的字符数组。 |
40 | String toLowerCase()
使用默认语言环境的规则将此String中的所有字符转换为小写。 |
41 | String toLowerCase(Locale locale)
使用给定的区域设置的规则将此String中的所有字符转换为小写。 |
42 | String toString()
此对象(已经是字符string!)本身已被返回。 |
43 | String toUpperCase()
使用默认语言环境的规则将此字符string中的所有字符转换为大写。 |
44 | String toUpperCase(Locale locale)
使用给定的区域设置的规则将此String中的所有字符转换为大写。 |
45 | 字符stringtrim()
返回字符string的副本,省略前导和尾随空格。 |
46 | static String valueOf(原始数据类型x)
返回传递的数据类型参数的字符string表示形式。 |
Java提供了一个数据结构,所述数组,其存储相同类型的元件的固定大小的连续集合。数组用于存储数据集合,但将数组视为相同类型变量的集合通常更为有用。
您可以声明一个数组变量,例如数字和数字[0],数字[1]和...,数字[99]来表示数字变量,而不是声明个别变量,例如number0,number1,...和number99个体变量。
本教程介绍如何使用索引变量声明数组变量,创建数组和处理数组。
要在程序中使用数组,您必须声明一个变量来引用数组,并且必须指定变量可以引用的数组类型。这是声明数组变量的语法 -
dataType[] arrayRefVar; // preferred way. or dataType arrayRefVar[]; // works but not preferred way.
注 - 样式dataType [] arrayRefVar是首选。样式dataType arrayRefVar []来自C / C ++语言,并被Java采用以适应C / C ++程序员。
以下代码片段是此语法的示例 -
double[] myList; // preferred way. or double myList[]; // works but not preferred way.
您可以使用具有以下语法的新运算符创建数组:
arrayRefVar = new dataType[arraySize];
上述声明有两件事情 -
它使用新的dataType [arraySize]创建一个数组。
它将新创建的数组的引用分配给变量arrayRefVar。
声明数组变量,创建数组,并将数组的引用分配给变量可以组合在一个语句中,如下所示:
dataType[] arrayRefVar = new dataType[arraySize];
或者,您可以按如下创建数组 -
dataType[] arrayRefVar = {value0, value1, ..., valuek};
数组元素通过索引访问。数组索引为0; 也就是说,它们从0开始到arrayRefVar.length-1。
以下语句声明一个数组变量myList,创建一个由10个元素组成的double类型的数组,并将其引用分配给myList -
double[] myList = new double[10];
以下图片表示数组myList。这里,myList保存十个双精度值,索引为0到9。
处理数组元素时,我们经常使用for循环或foreach循环,因为数组中的所有元素都是相同的类型,数组的大小是已知的。
以下是一个完整的示例,显示如何创建,初始化和处理数组 -
public class TestArray { public static void main(String[] args) { double[] myList = {1.9, 2.9, 3.4, 3.5}; // Print all the array elements for (int i = 0; i < myList.length; i++) { System.out.println(myList[i] + " "); } // Summing all elements double total = 0; for (int i = 0; i < myList.length; i++) { total += myList[i]; } System.out.println("Total is " + total); // Finding the largest element double max = myList[0]; for (int i = 1; i < myList.length; i++) { if (myList[i] > max) max = myList[i]; } System.out.println("Max is " + max); } }
输出结果如下 -
1.9 2.9 3.4 3.5 Total is 11.7 Max is 3.5
JDK 1.5引入了一个称为foreach循环或增强for循环的新for循环,这使您能够顺序遍历完整的数组而不使用索引变量。
以下代码显示数组myList中的所有元素 -
public class TestArray { public static void main(String[] args) { double[] myList = {1.9, 2.9, 3.4, 3.5}; // Print all the array elements for (double element: myList) { System.out.println(element); } } }
输出结果如下 -
1.9 2.9 3.4 3.5
就像您可以将基本类型值传递给方法一样,还可以将数组传递给方法。例如,以下方法显示int数组中的元素 -
public static void printArray(int[] array) { for (int i = 0; i < array.length; i++) { System.out.print(array[i] + " "); } }
您可以通过传递数组来调用它。例如,以下语句调用printArray方法来显示3,1,2,6,4和2 -
printArray(new int[]{3, 1, 2, 6, 4, 2});
一种方法也可能返回一个数组。例如,以下方法返回一个数组,该数组是另一个数组的反转 -
public static int[] reverse(int[] list) { int[] result = new int[list.length]; for (int i = 0, j = result.length - 1; i < list.length; i++, j--) { result[j] = list[i]; } return result; }
java.util.Arrays类包含用于排序和搜索数组,比较数组和填充数组元素的各种静态方法。这些方法对于所有原始类型都是重载的。
没有 | 方法和说明 |
---|---|
1 | public static int binarySearch(Object [] a,Object键) 使用二进制搜索算法搜索指定值的Object(Byte,Int,double等)的指定数组。在进行此调用之前,必须对数组进行排序。这将返回搜索关键字的索引,如果它包含在列表中; 否则返回( - (插入点+ 1))。 |
2 | public static boolean equals(long [] a,long [] a2) 如果两个指定的longs数组彼此相等,则返回true。如果两个数组都包含相同数量的元素,则两个数组被认为是相等的,并且两个数组中所有对应的元素对都相等。如果两个数组相等,则返回true。所有其他原始数据类型(Byte,short,Int等)都可以使用相同的方法 |
3 | public static void fill(int [] a,int val) 将指定的int值分配给指定的int数组的每个元素。所有其他原始数据类型(Byte,short,Int等)都可以使用相同的方法 |
4 | public static void sort(Object [] a) 根据其元素的自然顺序,将指定的对象数组按升序排序。所有其他原始数据类型(Byte,short,Int等)都可以使用相同的方法 |
Java提供了java.util包中可用的Date类,该类封装了当前的日期和时间。
Date类支持两个构造函数,如下表所示。
没有 | 构造函数和描述 |
---|---|
1 | 日期() 此构造函数使用当前日期和时间初始化对象。 |
2 | 日期(长毫秒) 该构造函数接受一个等于从1970年1月1日午夜以来经过的毫秒数的参数。 |
以下是日期类的方法。
没有 | 方法和说明 |
---|---|
1 | 布尔后(日期日期) 如果调用的Date对象包含的日期晚于date指定的日期,则返回true,否则返回false。 |
2 | 布尔之前(日期日期) 如果调用的Date对象包含早于date指定的日期,则返回true,否则返回false。 |
3 | 对象克隆() 复制调用Date对象。 |
4 | int compareTo(日期日期) 将调用对象的值与date的值进行比较。如果值相等,则返回0。如果调用对象早于date,则返回一个负值。如果调用对象晚于日期,则返回正值。 |
5 | int compareTo(Object obj) 如果obj是类Date,则与compareTo(Date)的操作相同。否则,它会引发ClassCastException。 |
6 | boolean equals(Object date) 如果调用的Date对象包含与date指定的时间和日期相同的时间和日期,则返回true,否则返回false。 |
7 | long getTime() 返回从1970年1月1日起经过的毫秒数。 |
8 | int hashCode() 返回调用对象的哈希码。 |
9 | void setTime(long time) 设置时间指定的时间和日期,表示1970年1月1日午夜以来以毫秒为单位的经过时间。 |
10 | String toString() 将调用的Date对象转换为字符string并返回结果。 |
这是一个非常简单的方法来获取Java中的当前日期和时间。您可以使用带有toString()方法的简单Date对象来打印当前日期和时间如下:
import java.util.Date; public class DateDemo { public static void main(String args[]) { // Instantiate a Date object Date date = new Date(); // display time and date using toString() System.out.println(date.toString()); } }
输出结果如下 -
on May 04 09:51:52 CDT 2009
以下是比较两个日期的三种方式 -
您可以使用getTime()来获取1970年1月1日午夜以后两个对象所经过的毫秒数,然后比较这两个值。
您可以使用before(),after()和equals()之间的方法。因为这个月的十二号是在18日之前,所以新的Date(99,2,12).before(new Date(99,2,18))返回true。
您可以使用compareTo()方法,该方法由Comparable接口定义并由Date实现。
SimpleDateFormat是一个具体的类,用于以区域设置敏感的方式格式化和解析日期。SimpleDateFormat允许您从选择日期时间格式化的任何用户定义的模式开始。
import java.util.*; import java.text.*; public class DateDemo { public static void main(String args[]) { Date dNow = new Date( ); SimpleDateFormat ft = new SimpleDateFormat ("E yyyy.MM.dd "at" hh:mm:ss a zzz"); System.out.println("Current Date: " + ft.format(dNow)); } }
输出结果如下 -
Current Date: Sun 2004.07.18 at 04:14:09 PM PDT
要指定时间格式,请使用时间模式字符string。在这种模式中,所有ASCII字母都保留为模式字母,其定义如下:
字符 | 描述 | 例 |
---|---|---|
G | 时代标志 | 广告 |
y | 年份四位数 | 2001年 |
M | 一个月 | 七月或七月 |
d | 一个月 | 10 |
H | 小时在AM / PM(112) | 12 |
H | 小时(023) | 22 |
m | 分钟小时 | 30 |
s | 二分钟 | 55 |
S | 毫秒 | 234 |
E | 星期几 | 星期二 |
D | 一年一年 | 360 |
F | 一个星期的月份 | 2(七月二日) |
w | 一年 | 40 |
W | 一个月 | 1 |
一个 | AM / PM标记 | 下午 |
k | 小时(124) | 24 |
K | 小时在AM / PM(011) | 10 |
z | 时区 | 东部标准时间 |
“ | 逃脱文本 | 分隔符 |
“ | 单引号 | ` |
使用printf方法可以非常方便地进行日期和时间格式化。您使用双字母格式,从t开始,并以表中的一个字母结尾,如下面的代码所示。
import java.util.Date; public class DateDemo { public static void main(String args[]) { // Instantiate a Date object Date date = new Date(); // display time and date using toString() String str = String.format("Current Date/Time : %tc", date ); System.out.printf(str); } }
输出结果如下 -
Current Date/Time : Sat Dec 15 16:37:57 MST 2012
如果您必须多次提供日期格式化每个部分,这将会有点愚蠢。因此,格式字符string可以指示要格式化的参数的索引。
索引必须立即跟随%,并且必须由$终止。
import java.util.Date; public class DateDemo { public static void main(String args[]) { // Instantiate a Date object Date date = new Date(); // display time and date using toString() System.out.printf("%1$s %2$tB %2$td, %2$tY", "Due date:", date); } }
输出结果如下 -
Due date: February 09, 2004
或者,您可以使用<flag。这表明应该再次使用与上一格式规范相同的参数。
import java.util.Date; public class DateDemo { public static void main(String args[]) { // Instantiate a Date object Date date = new Date(); // display formatted date System.out.printf("%s %tB %<te, %<tY", "Due date:", date); } }
输出结果如下 -
Due date: February 09, 2004
字符 | 描述 | 例 |
---|---|---|
C | 完成日期和时间 | 星期一五月04 09:51:52 CDT 2009 |
F | ISO 8601日期 | 2004-02-09 |
D | 美国格式化日期(月/日/年) | 02/09/2004 |
T | 24小时的时间 | 18:05:19 |
r | 12小时的时间 | 06:05:19下午 |
R | 24小时的时间,没有秒 | 18:05 |
Y | 四位数年份(领先零) | 2004年 |
y | 年份的最后两位数字(前导零) | 04 |
C | 年头两位数字(前导零) | 20 |
B | 全月份名称 | 二月 |
b | 缩写月份名称 | 二月 |
m | 两位数月份(前导零) | 02 |
d | 两位数的一天(带前导零) | 03 |
e | 两位数的一天(无前导零) | 9 |
一个 | 完整的工作日名称 | 星期一 |
一个 | 缩写工作日名称 | 星期一 |
j | 三位数的一年(带前导零) | 069 |
H | 两位数小时(前导零),介于00和23之间 | 18 |
k | 两位数小时(不带前导零),介于0和23之间 | 18 |
一世 | 两位数小时(前导零),介于01和12之间 | 06 |
l | 两位数小时(无前导零),介于1到12之间 | 6 |
M | 两位数分钟(带前导零) | 05 |
S | 两位数秒(带前导零) | 19 |
L | 三位数毫秒(带前导零) | 047 |
N | 九位数纳秒(前导零) | 047000000 |
P | 大写上午或下午的标记 | 下午 |
p | 小写上午或下午的标记 | 下午 |
z | RFC 822与GMT的数字偏移 | -0800 |
Z | 时区 | 太平洋标准时间 |
s | 自1970-01-01 00:00:00 GMT以来的秒数 | 1078884319 |
问 | 自1970-01-01 00:00:00 GMT以来的毫秒数 | 1078884319047 |
还有与日期和时间相关的其他有用的类。有关更多详细信息,请参阅Java Standard文档。
SimpleDateFormat类有一些额外的方法,特别是parse(),它尝试根据给定的SimpleDateFormat对象中存储的格式来解析字符string。
import java.util.*; import java.text.*; public class DateDemo { public static void main(String args[]) { SimpleDateFormat ft = new SimpleDateFormat ("yyyy-MM-dd"); String input = args.length == 0 ? "1818-11-11" : args[0]; System.out.print(input + " Parses as "); Date t; try { t = ft.parse(input); System.out.println(t); }catch (ParseException e) { System.out.println("Unparseable using " + ft); } } }
上述程序的示例运行将产生以下结果 -
1818-11-11 Parses as Wed Nov 11 00:00:00 EST 1818
您可以睡一段时间,从您的计算机的一毫秒到一生。例如,以下程序将睡3秒钟 -
import java.util.*; public class SleepDemo { public static void main(String args[]) { try { System.out.println(new Date( ) + " "); Thread.sleep(5*60*10); System.out.println(new Date( ) + " "); }catch (Exception e) { System.out.println("Got an exception!"); } } }
输出结果如下 -
Sun May 03 18:04:41 GMT 2009 Sun May 03 18:04:51 GMT 2009
有时,您可能需要测量时间点(以毫秒为单位)。所以我们再次重写上面的例子 -
import java.util.*; public class DiffDemo { public static void main(String args[]) { try { long start = System.currentTimeMillis( ); System.out.println(new Date( ) + " "); Thread.sleep(5*60*10); System.out.println(new Date( ) + " "); long end = System.currentTimeMillis( ); long diff = end - start; System.out.println("Difference is : " + diff); }catch (Exception e) { System.out.println("Got an exception!"); } } }
输出结果如下 -
Sun May 03 18:16:51 GMT 2009 Sun May 03 18:16:57 GMT 2009 Difference is : 5993
GregorianCalendar是一个Calendar类的具体实现,它实现了您熟悉的普通公历。我们在本教程中没有探讨Calendar类,您可以查看标准的Java文档。
Calendar 的getInstance()方法返回在默认语言环境和时区中使用当前日期和时间初始化的GregorianCalendar。GregorianCalendar定义了两个字段:AD和BC。这些代表着公历所定义的两个时代。
GregorianCalendar对象还有几个构造函数 -
没有 | 构造函数和描述 |
---|---|
1 | 公历() 使用默认时区的默认语言环境创建默认的GregorianCalendar。 |
2 | GregorianCalendar(int year,int month,int date) 构造一个GregorianCalendar,其中给定日期在默认时区中设置为默认语言环境。 |
3 | GregorianCalendar(int year,int month,int date,int hour,int minute) 构造一个GregorianCalendar,其中给定的日期和时间为默认时区设置了默认语言环境。 |
4 | GregorianCalendar(int year,int month,int date,int hour,int minute,int second) 构造一个GregorianCalendar,其中给定的日期和时间为默认时区设置了默认语言环境。 |
5 | GregorianCalendar(Locale aLocale) 根据具有给定语言环境的默认时区中的当前时间创建GregorianCalendar。 |
6 | GregorianCalendar(时区) 根据具有默认语言环境的给定时区中的当前时间创建GregorianCalendar。 |
7 | GregorianCalendar(TimeZone zone,Locale aLocale) 根据给定时区的当前时间,使用给定的区域设置创建GregorianCalendar。 |
以下是GregorianCalendar类提供的几个有用的支持方法的列表 -
没有 | 方法和说明 |
---|---|
1 | void add(int field,int amount) 根据日历的规则,将指定的(已签名的)时间量添加到给定的时间字段中。 |
2 | protected void computeFields() 将UTC转换为毫秒到时间字段值。 |
3 | protected void computeTime() 覆盖日历将时间字段值转换为UTC为毫秒。 |
4 | boolean equals(Object obj) 将此GregorianCalendar与对象引用进行比较。 |
5 | int get(int field) 获取给定时间字段的值。 |
6 | int getActualMaximum(int field) 给出当前日期,返回此字段可能具有的最大值。 |
7 | int getActualMinimum(int field) 给定当前日期,返回此字段可能具有的最小值。 |
8 | int getGreatestMinimum(int field) 如果变化,则返回给定字段的最小值。 |
9 | 日期getGregorianChange() 获取公历日历更改日期。 |
10 | int getLeastMaximum(int field) 如果变化,则返回给定字段的最小值。 |
11 | int getMaximum(int field) 返回给定字段的最大值。 |
12 | 日期getTime() 获取此日历的当前时间。 |
13 | long getTimeInMillis() 获取这个日历的当前时间很长一段时间。 |
14 | TimeZone getTimeZone() 获取时区。 |
15 | int getMinimum(int field) 返回给定字段的最小值。 |
16 | int hashCode() 覆盖hashCode。 |
17 | boolean isLeapYear(int year) 确定给定年份是否是闰年。 |
18 | void roll(int field,boolean up) 在给定时间字段上添加或减少单个时间单位,而不改变较大的字段。 |
19 | void set(int field,int value) 设定给定值的时间字段。 |
20 | void set(int year,int month,int date) 设置字段年,月和日期的值。 |
21 | void set(int year,int month,int date,int hour,int minute) 设置字段年,月,日,小时和分钟的值。 |
22 | void set(int year,int month,int date,int hour,int minute,int second) 设置字段年,月,日,小时,分和秒的值。 |
23 | void setGregorianChange(Date date) 设置GregorianCalendar更改日期。 |
24 | void setTime(Date date) 将该日历的当前时间设置为给定的日期。 |
25 | void setTimeInMillis(long millis) 从给定的长值设置此日历的当前时间。 |
26 | void setTimeZone(TimeZone value) 以给定的时区值设置时区。 |
27 | String toString() 返回此日历的字符string表示形式。 |
import java.util.*; public class GregorianCalendarDemo { public static void main(String args[]) { String months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; int year; // Create a Gregorian calendar initialized // with the current date and time in the // default locale and timezone. GregorianCalendar gcalendar = new GregorianCalendar(); // Display current time and date information. System.out.print("Date: "); System.out.print(months[gcalendar.get(Calendar.MONTH)]); System.out.print(" " + gcalendar.get(Calendar.DATE) + " "); System.out.println(year = gcalendar.get(Calendar.YEAR)); System.out.print("Time: "); System.out.print(gcalendar.get(Calendar.HOUR) + ":"); System.out.print(gcalendar.get(Calendar.MINUTE) + ":"); System.out.println(gcalendar.get(Calendar.SECOND)); // Test if the current year is a leap year if(gcalendar.isLeapYear(year)) { System.out.println("The current year is a leap year"); }else { System.out.println("The current year is not a leap year"); } } }
输出结果如下 -
Date: Apr 22 2009 Time: 11:25:27 The current year is not a leap year
有关Calendar类中可用常量的完整列表,可以参考标准Java文档。
Java提供了用于与正则表达式进行模式匹配的java.util.regex包。Java正则表达式与Perl编程语言非常相似,非常容易学习。
正则表达式是一个特殊的字符序列,可以帮助您使用模式中保留的专门语法来匹配或查找其他字符string或字符string集。它们可用于搜索,编辑或操纵文本和数据。
java.util.regex包主要由以下三个类组成 -
模式类 - 模式对象是正则表达式的编译表示。Pattern类不提供公共构造函数。要创建一个模式,你必须首先调用其一个public static compile()方法,然后返回一个Pattern对象。这些方法接受正则表达式作为第一个参数。
匹配器类 - 匹配器对象是解释模式并针对输入字符string执行匹配操作的引擎。像Pattern类一样,Matcher没有定义公共构造函数。通过调用Pattern对象上的matcher()方法来获取Matcher 对象。
PatternSyntaxException - PatternSyntaxException对象是未经检查的异常,指示正则表达式模式中的语法错误。
捕获组是将多个字符视为单个单元的一种方式。他们通过将字符的圆括号内进行分组创建。例如,正则表达式(狗)创建包含字母“D”,“O”,和“g”的单个基团。
捕获组是从左至右计算其开括号编号。在表达式((A)(B(C))),例如,有四个这样的基团 -
要了解表达式中有多少组,请在匹配器对象上调用groupCount方法。groupCount方法返回一个int,显示匹配器模式中存在的捕获组的数量。
还有一个特殊的组,组0,它始终代表整个表达式。该组不包括在groupCount报告的总数中。
例
以下示例说明如何从给定的字母数字字符string中找到数字字符string -
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"); } } }
输出结果如下 -
输出
Found value: This order was placed for QT3000! OK? Found value: This order was placed for QT300 Found value: 0
这里是列出所有Java中可用的正则表达式元字符语法的表 -
子表达式 | 火柴 |
---|---|
^ | 匹配行的开头。 |
$ | 匹配行尾。 |
。 | 匹配除换行符以外的任何单个字符。使用m选项允许它匹配换行符。 |
[...] | 匹配括号中的任何单个字符。 |
[^ ...] | 匹配不在括号中的任何单个字符。 |
一个 | 开始整个字符string。 |
z | 整个字符string结束。 |
Z | 除了允许的最终行终止符之外,整个字符string的结尾。 |
回覆* | 匹配前面表达式的0或更多次出现。 |
re + | 匹配1以上的事情。 |
回覆? | 匹配0或1出现前面的表达式。 |
re {n} | 匹配正好n个前面表达式的出现次数。 |
再{n,} | 匹配前面表达式的n个或更多个出现次数。 |
re {n,m} | 匹配前面表达式的n个和最多m个出现次数。 |
a | b | 匹配a或b。 |
(回覆) | 组合正则表达式并记住匹配的文本。 |
(?: 回覆) | 组合正则表达式而不记住匹配的文本。 |
(?> re) | 匹配独立模式,无需追溯。 |
w | 匹配字符字符。 |
W | 匹配非字符。 |
s | 匹配空格。相当于[ t n r f]。 |
S | 匹配非空白空间。 |
d | 匹配数字。相当于[0-9]。 |
D | 匹配不定式 |
一个 | 匹配字符string的开头。 |
Z | 匹配字符string的末尾。如果存在换行符,则它将在换行符之前匹配。 |
z | 匹配字符string的末尾。 |
G | 匹配最后一场比赛完成的点。 |
n | 捕获组号“n”的反参考。 |
b | 在括号之外匹配单词边界。在括号内匹配退格(0x08)。 |
B | 匹配非字边界。 |
n, t等 | 匹配换行符,回车符,制表符等 |
Q | 逃避(报价)所有字符达 E。 |
E | 结束引用开始与 Q。 |
这是一个有用的实例方法的列表 -
索引方法提供有用的索引值,可精确显示在输入字符string中找到匹配的位置 -
没有 | 方法和说明 |
---|---|
1 | public int start() 返回上一个匹配的起始索引。 |
2 | public int start(int group) 返回给定组在上一个匹配操作期间捕获的子序列的开始索引。 |
3 | public int end() 返回最后一个字符匹配后的偏移量。 |
4 | public int end(int group) 返回在上次匹配操作期间由给定组捕获的子序列的最后一个字符之后的偏移量。 |
学习方法检查输入的字符string,并返回一个布尔值,表示是否找到模式 -
没有 | 方法和说明 |
---|---|
1 | public boolean lookingAt() 尝试将输入序列从区域开头开始与模式相匹配。 |
2 | public boolean find() 尝试找到匹配模式的输入序列的下一个子序列。 |
3 | public boolean find(int start) 重新设置该匹配器,然后尝试从指定的索引开始找到匹配模式的输入序列的下一个子序列。 |
4 | public boolean matches() 尝试将整个区域与模式进行匹配。 |
替换方法是替换输入字符string中的文本的有用方法 -
没有 | 方法和说明 |
---|---|
1 | public Matcher appendReplacement(StringBuffer sb,String replacement) 实施非终端附加和替换步骤。 |
2 | public StringBuffer appendTail(StringBuffer sb) 实施终端附加和替换步骤。 |
3 | public String replaceAll(String replacement) 将与模式匹配的输入序列的每个子序列替换为给定的替换字符string。 |
4 | public String replaceFirst(String replacement) 将与模式匹配的输入序列的第一个子序列替换为给定的替换字符string。 |
5 | public static String quoteReplacement(String s) 返回指定String的文字替换字符string。这种方法产生一个字符string,将工作作为文字置换小号在匹配器类的appendReplacement方法。 |
以下是计算输入字符string中出现“cat”一词的次数的示例:
例
import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexMatches { private static final String REGEX = "cat"; 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()); } } }
输出结果如下 -
输出
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”不仅仅是一个较长的单词中的一个子字符string。它还提供了一些有用的信息,关于输入字符string中匹配发生的位置。
start方法返回给定组在前一次匹配操作中捕获的子序列的起始索引,结束返回匹配的最后一个字符的索引,加上一个。
matches和lookingAt方法都尝试将输入序列与模式进行匹配。然而,区别在于匹配需要匹配整个输入序列,而lookAt则不匹配。
两种方法始终从输入字符string的开头开始。以下是说明功能的示例 -
例
import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexMatches { private static final String REGEX = "foo"; private static final String INPUT = "fooooooooooooooooo"; private static Pattern pattern; private static Matcher matcher; public static void main( String args[] ) { pattern = Pattern.compile(REGEX); matcher = pattern.matcher(INPUT); System.out.println("Current REGEX is: "+REGEX); System.out.println("Current INPUT is: "+INPUT); System.out.println("lookingAt(): "+matcher.lookingAt()); System.out.println("matches(): "+matcher.matches()); } }
输出结果如下 -
输出
Current REGEX is: foo Current INPUT is: fooooooooooooooooo lookingAt(): true matches(): false
replaceFirst和replaceAll方法替换与给定正则表达式匹配的文本。如其名称所示,replaceFirst替换第一次出现,replaceAll将替换所有出现的值。
以下是说明功能的示例 -
例
import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexMatches { private static String REGEX = "dog"; private static String INPUT = "The dog says meow. " + "All dogs say meow."; private static String REPLACE = "cat"; public static void main(String[] args) { Pattern p = Pattern.compile(REGEX); // get a matcher object Matcher m = p.matcher(INPUT); INPUT = m.replaceAll(REPLACE); System.out.println(INPUT); } }
输出结果如下 -
输出
The cat says meow. All cats say meow.
Matcher类还提供了用于文本替换的appendReplacement和appendTail方法。
以下是说明功能的示例 -
例
import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexMatches { private static String REGEX = "a*b"; private static String INPUT = "aabfooaabfooabfoob"; private static String REPLACE = "-"; public static void main(String[] args) { Pattern p = Pattern.compile(REGEX); // 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()); } }
输出结果如下 -
输出
-foo-foo-foo-
PatternSyntaxException是一个未经检查的异常,指示正则表达式模式中的语法错误。PatternSyntaxException类提供了以下方法来帮助您确定出了哪些问题 -
没有 | 方法和说明 |
---|---|
1 | public String getDescription() 检索错误的描述。 |
2 | public int getIndex() 检索错误索引。 |
3 | public String getPattern() 检索错误的正则表达式模式。 |
4 | public String getMessage() 返回一个多行字符string,其中包含语法错误及其索引的描述,错误的正则表达式模式以及模式中错误索引的可视指示。 |
Java方法是集合在一起以执行操作的语句集合。当你调用System.out。println()方法,例如,系统实际上执行几个语句,以便在控制台上显示一条消息。
现在,您将学习如何使用或不使用返回值创建自己的方法,使用或不使用参数调用方法,并在程序设计中应用方法抽象。
考虑以下示例来解释方法的语法 -
用法
public static int methodName(int a, int b) { // body }
这里,
public static - modifier
int - 返回类型
methodName - 方法的名称
a,b - 形式参数
int a,int b - 参数列表
方法定义由方法头和方法体组成。在下面的语法中也是如此 -
用法
modifier returnType nameOfMethod (Parameter List) { // method body }
上面显示的语法包括 -
修饰符 - 它定义方法的访问类型,它是可选的使用。
returnType - 方法可能返回一个值。
nameOfMethod - 这是方法名称。方法签名由方法名称和参数列表组成。
参数列表 - 参数列表,它是方法的类型,顺序和参数数。这些是可选的,方法可能包含零参数。
方法体 - 方法体定义方法对语句的作用。
例
这是上述定义的方法的源代码,称为max()。此方法采用两个参数num1和num2,并返回两者之间的最大值 -
/** the snippet returns the minimum between two numbers */ public static int minFunction(int n1, int n2) { int min; if (n1 > n2) min = n2; else min = n1; return min; }
对于使用方法,应该被调用。调用方法有两种方法,即方法返回值或不返回(无返回值)。
方法调用的过程很简单。当程序调用一个方法时,程序控制被转移到被调用的方法。这个方法然后在两个条件下将控制权返回给调用者,当 -
返回void的方法被视为对语句的调用。让我们考虑一个例子 -
System.out.println("This is tutorialspoint.com!");
方法返回值可以通过以下示例来理解 -
int result = sum(6, 9);
以下是演示如何定义方法和如何调用它的示例 -
例
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; } }
输出结果如下 -
输出
Minimum value = 6
void关键字允许我们创建不返回值的方法。这里,在下面的例子中我们考虑一个void方法methodRankPoints。这个方法是一个void方法,它不返回任何值。调用void方法必须是一个语句,即methodRankPoints(255.7);。它是一个Java语句,以一个分号结尾,如下例所示。
例
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"); } } }
输出结果如下 -
输出
Rank:A1
在调用过程中工作时,参数将被传递。这些应与方法规范中的各自参数的顺序相同。参数可以通过值或引用传递。
通过值传递参数意味着调用一个参数的方法。通过这个参数,参数值被传递给参数。
例
以下程序显示了按值传递参数的示例。即使在方法调用之后,参数的值也保持不变。
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(" **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); } }
输出结果如下 -
输出
Before swapping, a = 30 and b = 45 Before swapping(Inside), a = 30 b = 45 After swapping(Inside), a = 45 b = 30 **Now, Before and After swapping values will be same here**: After swapping, a = 30 and b is 45
当一个类具有两个或多个相同名称但不同参数的方法时,称为方法重载。它不同于压倒一切。在重写时,方法的名称,类型,参数数量等同一种方法。
让我们考虑前面探讨的最小数量的整数类型的例子。如果,我们想要找到最小数量的double类型。然后将引入重载的概念来创建两个或更多具有相同名称但参数不同的方法。
以下示例解释相同 -
例
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; } }
输出结果如下 -
输出
Minimum Value = 6 Minimum Value = 7.3
重载方法使程序可读。这里,两种方法由相同的名称给出,但具有不同的参数。结果是整型和双类型的最小数字。
有时,当你运行它时,你会想要将一些信息传递给一个程序。这通过将命令行参数传递给main()来实现。
命令行参数是在命令行执行时直接跟随程序名称的信息。访问Java程序中的命令行参数是非常容易的。它们作为字符string存储在传递给main()的String数组中。
例
以下程序会显示它所调用的所有命令行参数 -
public class CommandLine { public static void main(String args[]) { for(int i = 0; i<args.length; i++) { System.out.println("args[" + i + "]: " + args[i]); } } }
尝试执行这个程序,如下所示:
$java CommandLine this is a command line 200 -100
输出结果如下 -
输出
args[0]: this args[1]: is args[2]: a args[3]: command args[4]: line args[5]: 200 args[6]: -100
构造函数在创建对象时初始化它。它与其类具有相同的名称,并且在语法上类似于一种方法。但是,构造函数没有明确的返回类型。
通常,您将使用构造函数为类定义的实例变量提供初始值,或执行创建完全形成对象所需的任何其他启动过程。
所有类都有构造函数,无论是定义还是不定义,因为Java自动提供将所有成员变量初始化为零的默认构造函数。但是,一旦定义了自己的构造函数,就不再使用默认构造函数。
例
这是一个简单的例子,它使用没有参数的构造函数 -
// A simple constructor. class MyClass { int x; // Following is the constructor MyClass() { x = 10; } }
您将不得不调用构造函数来初始化对象,如下所示:
public class ConsDemo { public static void main(String args[]) { MyClass t1 = new MyClass(); MyClass t2 = new MyClass(); System.out.println(t1.x + " " + t2.x); } }
输出
10 10
通常,您将需要一个接受一个或多个参数的构造函数。参数以与添加到方法相同的方式添加到构造函数中,只需在构造函数的名称后面的括号内声明它们。
例
这是一个简单的例子,它使用一个带有参数的构造函数 -
// 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); } }
输出结果如下 -
输出
10 20
这是Java中的一个关键字,它被用作对当前类的对象的引用,使用实例方法或构造函数。使用这个可以引用类的成员,如构造函数,变量和方法。
注 -关键字这是用来仅在实例方法或构造函数
在一般情况下,关键字这是用来-
如果构造函数或方法中具有相同的名称,则将实例变量与局部变量区分开来。
class Student { int age; Student(int age) { this.age = age; } }
从类中的其他类调用一种类型的构造函数(参数化构造函数或默认值)。它被称为显式构造函数调用。
class Student { int age Student() { this(20); } Student(int age) { this.age = age; } }
例
以下是使用此关键字访问类的成员的示例。复制并粘贴以下程序的名称,文件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 num to the instance variable num this.num = num; } public void greet() { System.out.println("Hi Welcome to Tutorialspoint"); } public void print() { // Local variable num int num = 20; // Printing the instance variable System.out.println("value of local variable num is : "+num); // Printing the local 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(); } }
输出结果如下 -
输出
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
JDK 1.5使您能够将相同类型的可变数量的参数传递给方法。方法中的参数声明如下:
typeName... parameterName
在方法声明中,您可以指定类型,后跟省略号(...)。一个方法中只能指定一个可变长度的参数,该参数必须是最后一个参数。任何常规参数都必须在它之前。
例
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 max value is 56.5 The max value is 3.0
可以定义一个在垃圾回收器最终破坏对象之前调用的方法。这个方法称为finalize(),它可以用来确保一个对象终止。
例如,您可以使用finalize()来确保该对象拥有的打开文件已关闭。
要添加一个finalizer到一个类,你只需定义finalize()方法。Java运行时每当要回收该类的对象时都会调用该方法。
在finalize()方法中,您将指定在销毁对象之前必须执行的操作。
finalize()方法有这个一般形式 -
protected void finalize( ) { // finalization code here }
这里的关键字protected是一个说明符,它通过在其类之外定义的代码阻止对finalize()的访问。
这意味着你不知道什么时候甚至是finalize()将被执行。例如,如果您的程序在垃圾收集发生之前结束,则finalize()将不会执行。
java.io包中包含几乎所有可能需要在Java中执行输入和输出(I / O)的类。所有这些流都代表输入源和输出目标。java.io包中的流支持许多数据,如基元,对象,本地化字符等。
流可以被定义为数据序列。有两种Streams -
InPutStream - InputStream用于从源中读取数据。
OutPutStream - OutputStream用于将数据写入目的地。
Java为与文件和网络相关的I / O提供强大但灵活的支持,但本教程涵盖与流和I / O相关的基本功能。我们将逐个看到最常用的例子 -
Java字节流用于执行8位字节的输入和输出。虽然有许多与字节流相关的类,但最常用的类是FileInputStream和FileOutputStream。以下是使用这两个类将输入文件复制到输出文件中的示例 -
例
import java.io.*; 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
Java 字节流用于执行8位字节的输入和输出,而Java 字符流用于执行16位unicode的输入和输出。虽然有许多与字符流相关的类,但最常用的类是FileReader和FileWriter。虽然内部FileReader使用FileInputStream,FileWriter使用FileOutputStream,但主要区别在于FileReader一次读取两个字节,FileWriter一次写入两个字节。
我们可以重写上面的例子,这样使用这两个类将输入文件(有unicode字符)复制到输出文件中 -
例
import java.io.*; 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
所有编程语言都支持标准I / O,用户程序可以从键盘输入,然后在计算机屏幕上产生输出。如果您知道C或C ++编程语言,则必须注意STDIN,STDOUT和STDERR三种标准设备。类似地,Java提供以下三个标准流 -
标准输入 - 用于将数据提供给用户程序,通常键盘用作标准输入流,并表示为System.in。
标准输出 - 用于输出用户程序生成的数据,通常计算机屏幕用于标准输出流并表示为System.out。
标准错误 - 用于输出由用户程序生成的错误数据,通常计算机屏幕用于标准错误流并表示为System.err。
以下是一个简单的程序,它创建了InputStreamReader来读取标准输入流,直到用户键入“q”
例
import java.io.*; 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
如前所述,流可以被定义为数据序列。所述的InputStream用于读取从源数据和所述的OutputStream用于写入数据至目的地。
这是处理输入和输出流的类的层次结构。
两个重要的流是FileInputStream和FileOutputStream,这将在本教程中探讨。
此流用于从文件读取数据。可以使用关键字new创建对象,并且有几种类型的构造函数可用。
以下构造函数将文件名作为字符string创建一个输入流对象以读取文件 -
InputStream f = new FileInputStream("C:/java/hello");
以下构造函数使用文件对象来创建一个输入流对象来读取该文件。首先,我们使用File()方法创建一个文件对象,如下所示:
File f = new File("C:/java/hello"); InputStream f = new FileInputStream(f);
一旦你有InputStream对象,那么有一个帮助方法的列表,可以用来读取流或者在流上执行其他操作。
没有 | 方法和说明 |
---|---|
1 | public void close()throws IOException {} 此方法关闭文件输出流。释放与文件相关联的任何系统资源。抛出IOException。 |
2 | protected void finalize()throws IOException {} 此方法可清除与文件的连接。确保当没有对该流的更多引用时,将调用此文件输出流的close方法。抛出IOException。 |
3 | public int read(int r)throws IOException {} 该方法从InputStream读取数据的指定字节。返回一个int。返回数据的下一个字节,如果是文件的末尾,则返回-1。 |
4 | public int read(byte [] r)throws IOException {} 该方法将r.length字节从输入流读入数组。返回读取的总字节数。如果是文件的结尾,则返回-1。 |
5 | public int available()throws IOException {} 给出可从该文件输入流读取的字节数。返回一个int。 |
还有其他重要的输入流可用,更多的细节可以参考以下链接 -
FileOutputStream用于创建文件并将数据写入其中。如果流已经不存在,流将在打开输出之前创建一个文件。
这里有两个可以用来创建FileOutputStream对象的构造函数。
以下构造函数将文件名作为字符string创建输入流对象来写入文件 -
OutputStream f = new FileOutputStream("C:/java/hello")
以下构造函数使用一个文件对象来创建一个输出流对象来写入该文件。首先,我们使用File()方法创建一个文件对象,如下所示:
File f = new File("C:/java/hello"); OutputStream f = new FileOutputStream(f);
一旦你有OutputStream对象,那么就有一个帮助方法的列表,可以用来写入流或对流进行其他操作。
没有 | 方法和说明 |
---|---|
1 | public void close()throws IOException {} 此方法关闭文件输出流。释放与文件相关联的任何系统资源。抛出IOException。 |
2 | protected void finalize()throws IOException {} 此方法可清除与文件的连接。确保当没有对该流的更多引用时,将调用此文件输出流的close方法。抛出IOException。 |
3 | public void write(int w)throws IOException {} 此方法将指定的字节写入输出流。 |
4 | public void write(byte [] w) 将w.length字节从上述字节数组写入OutputStream。 |
还有其他重要的输出流可用,更多的细节可以参考以下链接 -
例
以下是演示InputStream和OutputStream的示例 -
import java.io.*; public class fileStreamTest { public static void main(String args[]) { try { byte bWrite [] = {11,21,3,40,5}; OutputStream os = new FileOutputStream("test.txt"); for(int x = 0; x < bWrite.length ; x++) { os.write( bWrite[x] ); // writes the bytes } os.close(); InputStream is = new FileInputStream("test.txt"); int size = is.available(); for(int i = 0; i < size; i++) { System.out.print((char)is.read() + " "); } is.close(); }catch(IOException e) { System.out.print("Exception"); } } }
上述代码将创建文件test.txt,并将以二进制格式写入给定的数字。与stdout屏幕上的输出相同。
还有其他几个课程,我们将要了解文件导航和I / O的基础知识。
目录是可以包含其他文件和目录列表的文件。您可以使用File对象创建目录,列出目录中可用的文件。有关完整的详细信息,请查看可以调用File对象的所有方法的列表以及与目录相关的内容。
有两个有用的文件实用程序方法可用于创建目录 -
MKDIR()方法创建一个目录,返回成功真假失败。失败表示File对象中指定的路径已经存在,或者由于整个路径不存在而无法创建该目录。
mkdirs()方法都将创建目录和目录下的所有的父母。
以下示例创建“/ tmp / user / java / bin”目录 -
例
import java.io.File; public class CreateDir { public static void main(String args[]) { String dirname = "/tmp/user/java/bin"; File d = new File(dirname); // Create directory now. d.mkdirs(); } }
编译并执行上述代码创建“/ tmp / user / java / bin”。
注意 - 根据约定,Java会自动在UNIX和Windows上处理路径分隔符。如果在Windows版本的Java上使用正斜杠(/),则路径仍将正确解析。
您可以使用File对象提供的list()方法列出目录中可用的所有文件和目录,如下所示:
例
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目录中提供的目录和文件产生以下结果-
输出
test1.txt test2.txt ReadDir.java ReadDir.class
异常(或异常事件)是在执行程序期间出现的问题。当异常发生时,程序的正常流程中断,程序/应用程序异常终止,这是不推荐的,因此,这些异常将被处理。
许多不同的原因可能会发生例外。以下是发生异常的情况。
用户已输入无效数据。
无法找到需要打开的文件。
网络连接在通信中丢失,或者JVM的内存已经用完。
这些异常中的一些是由用户错误引起的,其他的异常是由程序员错误引起的,而其他异常则是以某种方式失败的物理资源造成的。
基于这些,我们有三类异常。您需要了解它们以了解异常处理在Java中的工作原理。
检查的异常 - 检查的异常是在编译时发生的异常,这些异常也称为编译时异常。这些异常在编译时不能简单地被忽略,程序员应该处理(处理)这些异常。
例如,如果在程序中使用FileReader类从文件中读取数据,如果其构造函数中指定的文件不存在,则会发生FileNotFoundException异常,编译器会提示程序员处理异常。
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); } }
如果您尝试编译上述程序,您将收到以下异常。
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
注意 - 由于FileReader类的read()和close()方法抛出IOException,您可以观察到编译器通知处理IOException以及FileNotFoundException。
未经检查的异常 - 未经检查的异常是在执行时发生的异常。这些也称为运行时异常。这些包括编程错误,如逻辑错误或API的不当使用。运行时异常在编译时被忽略。
例如,如果您在程序中声明了大小为5的数组,并尝试调用数组的第 6 个元素,则会发生ArrayIndexOutOfBoundsExceptionexception。
public class Unchecked_Demo { public static void main(String args[]) { int num[] = {1, 2, 3, 4}; System.out.println(num[5]); } }
如果您编译并执行上述程序,您将收到以下异常。
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5 at Exceptions.Unchecked_Demo.main(Unchecked_Demo.java:8)
错误 - 这些不是例外,但是出现在用户或程序员控制之外的问题。您的代码中通常会忽略错误,因为您很少会对错误执行任何操作。例如,如果发生堆栈溢出,则会出现错误。编译时也会被忽略。
所有异常类都是java.lang.Exception类的子类型。异常类是Throwable类的一个子类。除了异常类之外,还有另一个称为Error的子类,它来自Throwable类。
错误是在严重故障的情况下发生的异常情况,这些情况不由Java程序处理。生成错误以指示运行时环境生成的错误。示例:JVM内存不足。通常,程序无法从错误中恢复。
Exception类有两个主要的子类:IOException类和RuntimeException类。
以下是最常见的已检查和未检查的Java的内置例外列表。
以下是Throwable类中可用的重要方法的列表。
没有 | 方法和说明 |
---|---|
1 | public String getMessage() 返回有关发生的异常的详细消息。此消息在Throwable构造函数中初始化。 |
2 | public Throwable getCause() 返回由Throwable对象表示的异常的原因。 |
3 | public String toString() 返回与getMessage()的结果连接的类的名称。 |
4 | public void printStackTrace() 将toString()的结果与堆栈跟踪一起打印到System.err,错误输出流。 |
5 | public StackTraceElement [] getStackTrace() 返回一个包含堆栈跟踪中每个元素的数组。索引0处的元素表示调用堆栈的顶部,数组中的最后一个元素表示调用堆栈底部的方法。 |
6 | public Throwable fillInStackTrace() 使用当前堆栈跟踪填充此Throwable对象的堆栈跟踪,添加堆栈跟踪中的任何先前信息。 |
一种方法使用try和catch关键字的组合捕获异常。一个try / catch块放在可能会产生异常的代码周围。try / catch块中的代码被称为受保护代码,并且使用try / catch的语法如下所示:
try { // Protected code }catch(ExceptionName e1) { // Catch block }
容易发生异常的代码放在try块中。发生异常时,发生的异常由与其关联的catch块处理。每个try块都应该被一个catch块或者最后一个块拦截。
catch语句涉及声明您尝试捕获的异常类型。如果受保护的代码发生异常,则会检查try之后的catch块(或块)。如果发生的异常类型列在catch块中,那么异常将传递给catch块,因为参数传递给一个method参数。
以下是使用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"); } }
输出结果如下 -
Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3 Out of the block
一个try块可以跟着多个catch块。多个catch块的语法如下所示:
try { // Protected code }catch(ExceptionType1 e1) { // Catch block }catch(ExceptionType2 e2) { // Catch block }catch(ExceptionType3 e3) { // Catch block }
以前的语句显示了三个catch块,但是您可以在单次尝试后可以使用任何数量的块。如果在受保护的代码中发生异常,该异常将抛出到列表中的第一个catch块。如果抛出异常的数据类型与ExceptionType1匹配,那么它将被捕获。如果没有,则异常传递到第二个catch语句。这将持续到异常被捕获或落在所有捕获中,在这种情况下,当前的方法停止执行,异常被抛出到调用堆栈上的先前的方法。
这里是显示如何使用多个try / catch语句的代码段。
try { file = new FileInputStream(fileName); x = (byte) file.read(); }catch(IOException i) { i.printStackTrace(); return -1; }catch(FileNotFoundException f) // Not valid! { f.printStackTrace(); return -1; }
从Java 7开始,您可以使用单个catch块处理多个异常,此功能简化了代码。这是怎么做的 -
catch (IOException|FileNotFoundException ex) { logger.log(ex); throw ex;
如果一个方法不处理检查的异常,该方法必须使用throws关键字声明它。throws关键字出现在方法签名的末尾。
通过使用throw关键字,您可以抛出一个异常,一个新实例化的异常或一个您刚被捕获的异常。
尝试了解throws和throw关键字之间的区别,throws用于推迟对已检查异常的处理,throw用于显式调用异常。
以下方法声明它抛出一个RemoteException -
import java.io.*; public class className { public void deposit(double amount) throws RemoteException { // Method implementation throw new RemoteException(); } // Remainder of class definition }
一个方法可以声明它引发多个异常,在这种情况下,异常在一个列表中声明,并以逗号分隔。例如,以下方法声明它抛出一个RemoteException和一个InsufficientFundsException -
import java.io.*; public class className { public void withdraw(double amount) throws RemoteException, InsufficientFundsException { // Method implementation } // Remainder of class definition }
最后一个块遵循try块或catch块。最终的代码块总是执行,而不管Exception是否发生。
使用finally块允许您运行任何要执行的清理类型的语句,无论发生在受保护的代码中。
一个finally块出现在catch块的末尾,并具有以下语法 -
try { // Protected code }catch(ExceptionType1 e1) { // Catch block }catch(ExceptionType2 e2) { // Catch block }catch(ExceptionType3 e3) { // Catch block }finally { // The finally block always executes. }
public class ExcepTest { public static void main(String args[]) { int a[] = new int[2]; try { System.out.println("Access element three :" + a[3]); }catch(ArrayIndexOutOfBoundsException e) { System.out.println("Exception thrown :" + e); }finally { a[0] = 6; System.out.println("First element value: " + a[0]); System.out.println("The finally statement is executed"); } } }
输出结果如下 -
Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3 First element value: 6 The finally statement is executed
请注意以下几点:
没有try语句,catch子句不可能存在。
每当一个try / catch块存在时,最后没有强制性的条款。
没有catch子句或finally子句,try块就不能存在。
任何代码都不能存在于try,catch,finally块之间。
通常,当我们使用任何资源,如流,连接等,我们必须使用finally块明确地关闭它们。在下面的程序中,我们使用FileReader从文件中读取数据,我们使用finally块关闭它。
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(也称为自动资源管理)是Java 7中引入的一种新的异常处理机制,它自动关闭try catch块中使用的资源。
要使用此语句,您只需要在括号内声明所需的资源,创建的资源将在块结束时自动关闭。以下是try-with-resources语句的语法。
try(FileReader fr = new FileReader("file path")) { // use the resource }catch() { // body of catch } }
以下是使用try-with-resources语句读取文件中的数据的程序。
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语句的类,它应该实现AutoCloseable接口,并在运行时自动调用它的close()方法。
您可以在try-with-resources语句中声明多个类。
虽然在try-with-resources语句的try块中声明了多个类,但是这些类以相反的顺序关闭。
除了括号内的资源声明,一切都与try块的正常try / catch块相同。
在try块开始之前,try中声明的资源被实例化。
在try块处声明的资源被隐式声明为final。
您可以在Java中创建自己的异常。在编写自己的异常类时,请记住以下几点:
所有例外必须是Throwable的孩子。
如果要编写由Handle或Declare Rule自动强制执行的检查异常,则需要扩展Exception类。
如果要编写运行时异常,则需要扩展RuntimeException类。
我们可以定义我们自己的Exception类如下:
class MyException extends Exception { }
您只需要扩展预定义的Exception类来创建自己的异常。这些被认为是检查异常。下面InsufficientFundsException类是扩展了异常类,使得它检查异常一个用户定义的异常。异常类就像任何其他类一样,包含有用的字段和方法。
// 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类包含一个引发InsufficientFundsException的withdraw()方法。
// 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(" Withdrawing $100..."); c.withdraw(100.00); System.out.println(" Withdrawing $600..."); c.withdraw(600.00); }catch(InsufficientFundsException e) { System.out.println("Sorry, but you are short $" + e.getAmount()); e.printStackTrace(); } } }
编译所有上述三个文件并运行BankDemo。输出结果如下 -
Depositing $500... Withdrawing $100... Withdrawing $600... Sorry, but you are short $200.0 InsufficientFundsException at CheckingAccount.withdraw(CheckingAccount.java:25) at BankDemo.main(BankDemo.java:13)
在Java中,可以定义两个“异常和错误”。
JVM异常 - 这些是由JVM专门或逻辑抛出的异常/错误。示例:NullPointerException,ArrayIndexOutOfBoundsException,ClassCastException。
编程异常 - 应用程序或API程序员明确抛出这些异常。示例:IllegalArgumentException,IllegalStateException。
在本章中,我们将探讨Java的内部类。
在Java中,就像方法一样,类的变量也可以有另一个类作为其成员。在Java中允许在另一个中编写一个类。写入的类称为嵌套类,并且保存内部类的类称为外部类。
用法
以下是编写嵌套类的语法。在这里,类Outer_Demo是外层类,Inner_Demo类是嵌套类。
class Outer_Demo { class Nested_Demo { } }
嵌套类分为两类:
非静态嵌套类 - 这是类的非静态成员。
静态嵌套类 - 这是类的静态成员。
内部类是Java中的安全机制。我们知道一个类不能与访问修饰符私有关联,但是如果我们将类作为其他类的成员,那么内部类可以被设为私有的。这也用于访问类的私有成员。
内部类有三种类型,具体取决于它们的定义和位置。他们是 -
创建一个内部类是非常简单的。你只需要在类中写一个类。与类不同,内部类可以是私有的,一旦声明一个内部类是私有的,就不能从类之外的对象访问它。
以下是创建内部类并访问它的程序。在给定的例子中,我们使内部类是私有的,并通过一种方法访问类。
例
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是内部类,display_Inner()是我们正在实例化内部类的方法,并且从main方法调用此方法。
如果您编译并执行上述程序,您将获得以下结果 -
输出
This is an inner class.
如前所述,内部类也用于访问类的私有成员。假设一个类有私人成员访问它们。在其中编写一个内部类,从内部类的方法返回私有成员,例如getValue(),最后从另一个类(要从中访问私有成员)调用内部类的getValue()方法类。
要实例化内部类,最初你必须实例化外部类。此后,使用外部类的对象,以下是实例化内部类的方式。
Outer_Demo outer = new Outer_Demo(); Outer_Demo.Inner_Demo inner = outer.new Inner_Demo();
以下程序显示如何使用内部类访问类的私有成员。
例
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()); } }
如果您编译并执行上述程序,您将获得以下结果 -
输出
The value of num in the class Test is: 175
在Java中,我们可以在方法中编写一个类,这将是本地类。像局部变量一样,内部类的范围在方法内受到限制。
方法局部内部类只能在定义内部类的方法中实例化。以下程序显示如何使用方法本地内部类。
例
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(); } }
如果您编译并执行上述程序,您将获得以下结果 -
输出
This is method inner class 23
被声明为没有类名的内部类被称为匿名内部类。在匿名内部类的情况下,我们同时声明和实例化它们。通常,当您需要覆盖类或接口的方法时,它们将被使用。匿名内部类的语法如下 -
用法
AnonymousInner an_inner = new AnonymousInner() { public void my_method() { ........ ........ } };
以下程序显示如何使用匿名内部类覆盖类的方法。
例
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(); } }
如果您编译并执行上述程序,您将获得以下结果 -
输出
This is an example of anonymous inner class
以同样的方式,您可以覆盖具体类的方法以及使用匿名内部类的接口。
一般来说,如果方法接受接口的对象,抽象类或具体类,那么我们可以实现接口,扩展抽象类,并将对象传递给方法。如果是一个类,那么我们可以直接将它传递给该方法。
但是在所有这三种情况下,您可以将匿名内部类传递给该方法。这是传递一个匿名内部类作为方法参数的语法 -
obj.my_Method(new My_Class() { public void Do() { ..... ..... } });
以下程序显示如何传递匿名内部类作为方法参数。
例
// 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"; } }); } }
如果你编译并执行上面的程序,它会给你以下结果 -
输出
Hello This is an example of anonymous inner class as an argument
静态内部类是一个嵌套类,它是外部类的静态成员。可以使用其他静态成员访问外部类。就像静态成员一样,静态嵌套类无法访问外部类的实例变量和方法。静态嵌套类的语法如下 -
用法
class MyOuter { static class Nested_Demo { } }
实例化静态嵌套类与实例化内部类有一点不同。以下程序显示如何使用静态嵌套类。
例
public class Outer { static class Nested_Demo { public void my_method() { System.out.println("This is my nested class"); } } public static void main(String args[]) { Outer.Nested_Demo nested = new Outer.Nested_Demo(); nested.my_method(); } }
如果您编译并执行上述程序,您将获得以下结果 -
输出
This is my nested class
继承可以定义为一个类获取另一个类的属性(方法和字段)的过程。通过使用继承,信息可以按层次顺序进行管理。
继承其他属性的类称为子类(派生类,子类),属性被继承的类称为超类(基类,父类)。
extends是用于继承类的属性的关键字。以下是extends关键字的语法。
用法
class Super { ..... ..... } class Sub extends Super { ..... ..... }
以下是演示Java继承的示例。在这个例子中,你可以看到两个类,即Calculation和My_Calculation。
使用extends关键字,My_Calculation继承了Calculation类的方法addition()和Subtraction()。
将以下程序复制并粘贴到名称为My_Calculation.java的文件中
例
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
执行程序后,输出结果如下-
输出
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 的方法multiplier()。
Calculation cal = new My_Calculation(); demo.addition(a, b); demo.Subtraction(a, b);
注 - 子类从其超类继承所有成员(字段,方法和嵌套类)。构造函数不是成员,所以它们不被子类继承,但是可以从子类调用超类的构造函数。
超级关键字类似于此关键字。以下是使用超级关键字的场景。
它用于区分超类的成员与子类的成员,如果它们具有相同的名称。
它用于从子类调用超类构造函数。
如果一个类继承了另一个类的属性。如果超类的成员具有与子类相同的名称,为区分这些变量,我们使用super关键字,如下所示。
super.variable super.method();
本节提供了一个演示超级关键字的用法的程序。
在给定的程序中,您有两个类,即Sub_class和Super_class,它们都有一个名为display()的不同实现的方法,另一个名为num的变量的值不同。我们正在调用这两个类的display()方法,并打印两个类的变量num的值。在这里,您可以观察到我们使用超级关键字来区分超类的成员与子类。
将程序复制并粘贴到名为Sub_class.java的文件中。
例
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
执行该程序时,将得到以下结果 -
输出
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
如果一个类继承了另一个类的属性,则子类将自动获取超类的默认构造函数。但是如果要调用超类的参数化构造函数,则需要使用super关键字,如下所示。
super(values);
本节中给出的程序演示了如何使用super关键字来调用超类的参数化构造函数。该程序包含一个超类和一个子类,其中超类包含接受字符string值的参数化构造函数,我们使用super关键字来调用超类的参数化构造函数。
将以下程序复制并粘贴到名称为Subclass.java的文件中
例
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 argd[]) { Subclass s = new Subclass(24); s.getAge(); } }
使用以下语法编译并执行上述代码。
javac Subclass java Subclass
执行该程序时,将得到以下结果 -
输出
The value of the variable named age in super class is: 24
IS-A是一种说法:该对象是该对象的一种类型。让我们看看如何使用extends关键字来实现继承。
公共类Animal { } 公共类Mammal extends Animal { } 公共类爬行扩展动物{ } 公共课狗延伸哺乳动物{ }
现在,基于上面的例子,在面向对象的术语中,以下是真实的 -
现在,如果我们考虑IS-A的关系,我们可以说 -
通过使用extends关键字,子类将能够继承超类的所有属性,但超类的私有属性除外。
我们可以确保Mammal实际上是使用实例操作符的动物。
例
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); } }
输出结果如下 -
输出
true true true
因为我们有一个很好的理解延伸的关键字,让我们看看到如何农具关键字用于获取IS-A的关系。
通常,implements关键字与类一起使用以继承接口的属性。接口不能被类扩展。
例
public interface Animal { } public class Mammal implements Animal { } public class Dog extends Mammal { }
让我们使用instanceof运算符来确定Mammal是否实际上是Animal,而Dog实际上是Animal。
例
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); } }
输出结果如下 -
输出
true true true
这些关系主要是基于使用。这决定某一类HAS-A是否确定。这种关系有助于减少代码的重复以及错误。
让我们来看一个例子 -
例
public class Vehicle{} public class Speed{} public class Van extends Vehicle { private Speed sp; }
这表明Van HAS-A速度。通过为Speed分开一个类,我们不必将Van class中的所有速度归入速度,这样可以在多个应用程序中重用Speed类。
在面向对象的功能中,用户不必担心哪个对象正在做真正的工作。为了实现这一点,Van类隐藏了Van类的用户的实现细节。所以,基本上发生的是用户会要求Van类做一些动作,Van Class会自己做这个工作,或者让另一个类执行这个动作。
有各种类型的继承,如下所示。
要记住的一个非常重要的事实是Java不支持多重继承。这意味着一个类不能扩展多个类。因此以下是非法的 -
例
public class extends Animal, Mammal{}
然而,一个类可以实现一个或多个接口,这有助于Java摆脱不可能的多重继承。
在上一章中,我们探讨了超类和子类。如果一个类从其超类继承一个方法,那么有一个机会可以覆盖该方法,只要它不被标记为final。
覆盖的好处是:能够定义特定于子类型的行为,这意味着一个子类可以根据其需求实现父类方法。
在面向对象的术语中,覆盖方法是覆盖现有方法的功能。
我们来看一个例子。
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 } }
输出结果如下 -
Animals can move Dogs can walk and run
在上面的例子中,您可以看到即使b是Animal类型,它也会在Dog类中运行move方法。其原因是:在编译时,对引用类型进行检查。但是,在运行时,JVM计算出对象类型,并运行属于该特定对象的方法。
因此,在上面的例子中,由于Animal类具有方法移动,程序将正确编译。然后,在运行时,它运行该对象特有的方法。
考虑以下示例 -
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(); } }
输出结果如下 -
TestDog.java:26: error: cannot find symbol b.bark(); ^ symbol: method bark() location: variable b of type Animal 1 error
这个程序会抛出一个编译时错误,因为b的引用类型Animal没有一个名称为bark的方法。
参数列表应该与重写方法完全相同。
返回类型应该是与超类中的原始覆盖方法中声明的返回类型相同的子类型。
访问级别不能比被覆盖的方法的访问级别更具限制性。例如:如果超类方法被声明为public,则子类中的重写方法不能是私有的或受保护的。
实例方法只有在子类继承时才能被覆盖。
声明final的方法不能被覆盖。
声明为static的方法不能被覆盖,但可以重新声明。
如果方法不能被继承,则不能被覆盖。
与实例的超类相同的包中的子类可以覆盖未声明为private或final的任何超类方法。
不同包中的子类只能覆盖已声明为public或protected的非final方法。
重写方法可以抛出任何不支持的异常,而不管覆盖的方法是否抛出异常。但是,重写方法不应该抛出与被覆盖方法声明的新的或更广泛的检查异常。重写方法可能会比覆盖的方法少得多或少的异常。
构造函数不能被覆盖。
当调用超类方法时,使用super关键字。
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 } }
输出结果如下 -
Animals can move Dogs can walk and run
多态性是对象承担许多形式的能力。当使用父类引用引用子类对象时,OOP中最常见的多态使用会发生。
任何可以通过多个IS-A测试的Java对象被认为是多态的。在Java中,所有Java对象都是多态的,因为任何对象都将通过IS-A测试来为自己的类型和类Object传递。
重要的是要知道访问对象的唯一可能方式是通过引用变量。参考变量只能是一种类型。一旦声明,引用变量的类型就无法更改。
引用变量可以重新分配给其他对象,只要它不被声明为final。引用变量的类型将确定它可以在对象上调用的方法。
引用变量可以引用其声明类型的任何对象或其声明类型的任何子类型。引用变量可以声明为类或接口类型。
我们来看一个例子。
public interface Vegetarian{} public class Animal{} public class Deer extends Animal implements Vegetarian{}
现在,Deer类被认为是多态的,因为它具有多重继承。以下是上面的例子 -
当我们将参考变量事实应用于Deer对象引用时,以下声明是合法的 -
Deer d = new Deer(); Animal a = d; Vegetarian v = d; Object o = d;
所有参考变量d,a,v,o指的是堆中相同的Deer对象。
在本节中,我将向您展示如何在Java中重写方法的行为允许您在设计类时利用多态。
我们已经探讨了覆盖方法,其中子类可以覆盖其父类中的方法。一个被覆盖的方法基本上隐藏在父类中,除非子类在重写方法中使用super关键字,否则不被调用。
/* 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(" Call mailCheck using Employee reference--"); e.mailCheck(); } }
输出结果如下 -
Constructing an Employee Constructing an Employee Call mailCheck using Salary reference -- Within mailCheck of Salary class ailing check to Mohd Mohtashim with salary 3600.0 Call mailCheck using Employee reference-- Within mailCheck of Salary class ailing check to John Adams with salary 2400.0
在这里,我们实例化两个薪水对象。一个使用工资参考小号,另一个使用一个雇员参考"e。
在调用s.mailCheck()时,编译器在编译时看到Salary类中的mailCheck(),并且JVM在运行时调用Salary类中的mailCheck()。
e上的mailCheck()是非常不同的,因为e是一个Employee参考。当编译器看到e.mailCheck()时,编译器会在Employee类中看到mailCheck()方法。
在编译时,编译器使用Employee中的mailCheck()验证此语句。但是,在运行时,JVM在Salary类中调用mailCheck()。
这种行为被称为虚拟方法调用,这些方法称为虚拟方法。在运行时调用重写的方法,无论在编译时使用源代码中的引用是什么数据类型。
根据字典,抽象是处理想法而不是事件的质量。例如,当您考虑电子邮件的情况时,复杂的详细信息(如发送电子邮件时会发生什么),您的电子邮件服务器使用的协议将从用户隐藏。因此,要发送一封电子邮件,您只需要输入内容,提及接收方的地址,然后单击发送。
同样在面向对象编程中,抽象是从用户隐藏实现细节的过程,只有功能将被提供给用户。换句话说,用户将具有关于对象所做的内容的信息,而不是使用它。
在Java中,抽象是使用抽象类和接口实现的。
在其声明中包含抽象关键字的类称为抽象类。
抽象类可以包含或不包含抽象方法,即没有body的方法(public void get();)
但是,如果一个类至少有一个抽象方法,则该类必须声明为抽象。
如果一个类被声明为抽象,则不能被实例化。
要使用抽象类,您必须从另一个类继承它,为其中的抽象方法提供实现。
如果你继承一个抽象类,那么你必须提供它所有抽象方法的实现。
本节为您提供抽象类的示例。要创建一个抽象类,只需在class关键字之前的类声明中使用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(" Call mailCheck using Employee reference--"); e.mailCheck(); } }
当你编译上面的类,它会给你以下错误 -
Employee.java:46: Employee is abstract; cannot be instantiated Employee e = new Employee("George W.", "Houston, TX", 43); ^ 1 error
我们可以通过以下方式继承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; } }
在这里,您不能实例化Employee类,但您可以实例化工资类,并使用此实例,您可以访问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(" Call mailCheck using Employee reference--"); e.mailCheck(); } }
这产生以下结果 -
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关键字用于将该方法声明为抽象。
您必须在方法声明中的方法名称之前放置抽象关键字。
抽象方法包含方法签名,但没有方法体。
而不是花括号,抽象的方法将在结尾有一个冒号(;)。
以下是抽象方法的一个例子。
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 }
封装是四个基本的OOP概念之一。另外三个是继承,多态和抽象。
Java中的封装是将数据(变量)和作为数据(方法)的代码作为一个单元进行包装的机制。在封装中,类的变量将从其他类隐藏,只能通过其当前类的方法进行访问。因此,它也被称为数据隐藏。
在Java中实现封装 -
将类的变量声明为私有的。
提供public setter和getter方法来修改和查看变量值。
以下是演示如何在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类的实例变量的访问点。通常,这些方法被称为getter和setter。因此,任何想要访问变量的类都可以通过这些getter和setter访问它们。
可以使用以下程序访问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()); } }
输出结果如下 -
Name : James Age : 20
类的字段可以是只读或只写。
一个类可以完全控制其字段中存储的内容。
类的用户不知道类如何存储其数据。一个类可以改变一个字段的数据类型,该类的用户不需要改变他们的任何代码。
接口是Java中的引用类型。它类似于类。它是抽象方法的集合。一个类实现一个接口,从而继承接口的抽象方法。
除了抽象方法之外,接口还可以包含常量,默认方法,静态方法和嵌套类型。方法体只存在于默认方法和静态方法。
编写界面类似于编写类。但是一个类描述了一个对象的属性和行为。一个接口包含类实现的行为。
除非实现接口的类是抽象的,否则接口的所有方法都需要在类中定义。
接口类似于以下几种类:
接口可以包含任意数量的方法。
一个接口写入带有.java扩展名的文件,其名称与文件的名称相匹配。
接口的字节码出现在.class文件中。
接口出现在包中,其对应的字节码文件必须在与包名称匹配的目录结构中。
然而,一个接口与几个方面不同,包括 -
您不能实例化一个界面。
接口不包含任何构造函数。
接口中的所有方法都是抽象的。
接口不能包含实例字段。只能在界面中出现的字段必须声明为static和final。
一个接口没有被一个类扩展; 它由一个类实现。
一个接口可以扩展多个接口。
的接口关键字用于声明的接口。这是一个简单的例子来声明一个接口 -
以下是一个接口的例子 -
/* 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关键字。
接口中的每个方法也是隐式抽象的,因此不需要抽象关键字。
接口中的方法是隐式公开的。
/* File name : Animal.java */ interface Animal { public void eat(); public void travel(); }
当一个类实现一个接口时,你可以将类认为是签约,同意执行接口的具体行为。如果类不执行接口的所有行为,则类必须声明为抽象。
一个类使用implements关键字实现一个接口。implements关键字出现在声明的扩展部分之后的类声明中。
/* 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(); } }
输出结果如下 -
Mammal eats Mammal travels
当覆盖接口中定义的方法时,有几个规则要遵循 -
检查的异常不应在实现方法上声明,而不是由接口方法声明的那些异常或由接口方法声明的那些子类声明。
当覆盖方法时,应保持接口方法的签名和相同的返回类型或子类型。
实现类本身可以是抽象的,如果是这样,则不需要实现接口方法。
当实现接口时,有几个规则 -
一个类可以一次实现多个接口。
一个类只能扩展一个类,但是实现很多接口。
一个接口可以扩展另一个接口,类似于类可以扩展另一个类。
接口可以以类扩展另一个类的方式扩展另一个接口。所述延伸关键字用于扩展接口,而子接口继承父接口的方法。
以下运动界面由曲棍球和足球界面扩展。
// 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); }
曲棍球界面有四种方法,但它从运动中继承了两种方法。因此,实施曲棍球的课程需要实现所有六种方法。同样,实现足球的课程需要从“足球”和“体育”两种方法中定义三种方法。
Java类只能扩展一个父类。不允许多重继承。接口不是类,而接口可以扩展多个父接口。
extend关键字使用一次,父接口以逗号分隔的列表声明。
例如,如果曲棍球界面扩展了运动和事件,它将被声明为 -
public interface Hockey extends Sports, Event
当父界面不包含任何方法时,扩展接口的最常见用途就是发生。例如,java.awt.event包中的MouseListener接口扩展java.util.EventListener,它被定义为 -
package java.util; public interface EventListener {}
其中没有方法的接口被称为标记接口。标记界面有两个基本的设计目的 -
创建一个公共父 - 与通过Java API中的数十个其他接口扩展的EventListener接口一样,您可以使用标记接口在一组接口中创建公共父级。例如,当接口扩展了EventListener时,JVM知道这个特定的接口将被用于事件委托方案。
向类添加数据类型 - 这种情况是标记来自哪里。实现标记接口的类不需要定义任何方法(因为接口没有),而是通过多态来形成接口类型。
软件包用于Java,以防止命名冲突,控制访问,使查找/定位和使用类,接口,枚举和注释更容易等。
一个包可以被定义为相关类型(类,接口,枚举和注解)提供访问保护和命名空间管理的分组。
Java中的一些现有软件包是 -
java.lang - 捆绑基础类
java.io - 用于输入,输出函数的类在此包中捆绑
程序员可以定义自己的软件包来捆绑一组类/接口等。对于由你实现的相关类进行分组是一个很好的做法,程序员可以很容易地确定类,接口,枚举和注释是相关的。
由于包创建一个新的命名空间,所以与其他包中的名称不会有任何名称冲突。使用包,更容易提供访问控制,并且更容易找到相关的类。
在创建软件包时,您应该为软件包选择一个名称,并在包含要包含在软件包中的类,接口,枚举和注释类型的每个源文件的顶部包含一个包语句以及该名称。
软件包语句应该是源文件中的第一行。每个源文件中只能有一个package语句,它适用于文件中的所有类型。
如果不使用package语句,则类,接口,枚举和注释类型将被放置在当前的默认包中。
要使用package语句编译Java程序,必须使用-d选项,如下所示。
javac -d Destination_folder file_name.java
然后在指定的目标中创建具有给定包名称的文件夹,并将编译的类文件放在该文件夹中。
我们来看一个创建一个名为animals的包的例子。使用小写字母的包名称避免与类和接口的名称发生冲突是一个很好的做法。
以下包示例包含接口命名动物 -
/* File name : Animal.java */ package animals; interface Animal { public void eat(); public void travel(); }
现在,让我们在同一个包中实现上述界面动物 -
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(); } }
现在编译java文件如下图所示:
$ javac -d . Animal.java $ javac -d . MammalInt.java
现在,将在当前目录中创建一个名为animals的包/文件夹,这些类文件将被放在其中,如下所示。
您可以在程序包中执行类文件,并获得如下所示的结果。
Mammal eats Mammal travels
如果一个类想在同一个包中使用另一个类,则不需要使用包名称。同一个包中的类找不到任何特殊语法。
在这里,一个名为Boss的类被添加到已经包含Employee的工资单包中。然后,Boss可以引用Employee类,而不使用工资核算前缀,如以下Boss类所示。
package payroll; public class Boss { public void payEmployee(Employee e) { e.mailCheck(); } }
如果员工类不在工资单中,会发生什么?Boss类必须使用以下技术之一来引用不同包中的类。
payroll.Employee
可以使用import关键字和通配符(*)导入包。例如 -
import payroll.*;
import payroll.Employee;
注 - 类文件可以包含任意数量的导入语句。import语句必须出现在package语句之后和类声明之前。
当一个类放在一个包中时,会发生两个主要的结果 -
包的名称成为类的名称的一部分,正如我们刚才在上一节中探讨的那样。
包的名称必须与相应字节码所在的目录结构相匹配。
以下是使用Java管理文件的简单方法 -
将类,接口,枚举或注释类型的源代码放在一个文本文件中,其名称是类型的简单名称,扩展名为.java。
例如 -
// File Name : Car.java package vehicle; public class Car { // Class implementation. }
现在,将源文件放在名称反映该类所属的包的名称的目录中 -
....vehicleCar.java
现在,合格的类名和路径名如下 -
一般来说,公司使用其反向互联网域名作为其包名。
示例 - 公司的互联网域名是apple.com,那么所有的包名都将以com.apple开头。包名称的每个组件对应于一个子目录。
示例 - 该公司有一个包含Dell.java源文件的com.apple.computers包,它将包含在一系列这样的子目录中 -
....comapplecomputersDell.java
在编译时,编译器为其中定义的每个类,接口和枚举创建不同的输出文件。输出文件的基本名称是类型的名称,其扩展名为.class。
例如 -
// File Name: Dell.java package com.apple.computers; public class Dell { } class Ups { }
现在,使用-d选项编译此文件如下:
$javac -d . Dell.java
这些文件将被编译如下 -
.comapplecomputersDell.class .comapplecomputersUps.class
您可以导入 com apple computers 中定义的所有类或接口,如下所示:
import com.apple.computers.*;
与.java源文件一样,编译的.class文件应该在一系列反映程序包名称的目录中。但是,.class文件的路径不一定与.java源文件的路径相同。您可以分别安排您的源代码和类目录,如
<path-one>sourcescomapplecomputersDell.java <path-two>classescomapplecomputersDell.class
通过这样做,可以将classes目录访问给其他程序员,而不会泄露您的来源。您还需要以这种方式管理源文件和类文件,以便编译器和Java虚拟机(JVM)可以找到程序使用的所有类型。
类目录<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文件,以便这些目录自动位于类路径中。
要显示当前的CLASSPATH变量,请在Windows和UNIX(Bourne shell)中使用以下命令 -
要删除CLASSPATH变量的当前内容,请使用 -
要设置CLASSPATH变量 -
Java实用程序包提供的数据结构非常强大,执行范围广泛的功能。这些数据结构由以下接口和类组成 -
所有这些类现在都是传统的,而Java-2引入了一个名为Collections Framework的新框架,这将在下一章中探讨。 -
枚举界面本身不是数据结构,但在其他数据结构的上下文中非常重要。枚举界面定义了一种从数据结构中检索连续元素的方法。
例如,枚举定义了一个名为nextElement的方法,用于获取包含多个元素的数据结构中的下一个元素。
要了解有关此界面的更多详细信息,请选中枚举。
BitSet类实现了可以单独设置和清除的一组位或标志。
在你需要跟上一组布尔值的情况下,这个类是非常有用的; 您只需为每个值分配一点,并根据需要进行设置或清除。
有关此类的更多详细信息,请查看BitSet。
Vector类与传统的Java数组类似,除了它可以根据需要增长以适应新元素。
像数组一样,Vector对象的元素可以通过向量中的索引来访问。
使用Vector类的好处在于,您不必担心在创建时将其设置为特定大小; 它在需要时自动收缩并增长。
有关此类的更多详细信息,请选择“矢量”。
Stack类实现了先进先出(LIFO)的元素堆栈。
您可以将堆栈字面意思上看作垂直堆叠的对象; 当你添加一个新的元素,它被堆叠在其他元素之上。
当您将一个元素从堆栈中拉出时,它从顶部脱落。换句话说,你添加到堆栈的最后一个元素是第一个要重新启动的元素。
有关此类的更多详细信息,请检查堆栈。
Dictionary类是一个抽象类,它定义了将键映射到值的数据结构。
这在您希望能够通过特定键而不是整数索引访问数据的情况下很有用。
由于Dictionary类是抽象的,它只提供了关键映射数据结构的框架,而不是特定的实现。
有关此类的更多详细信息,请选择“字典”。
Hashtable类提供了一种基于一些用户定义的键结构来组织数据的方法。
例如,在地址列表哈希表中,您可以根据邮政编码而不是某个人的名称来存储和排序数据。
关于哈希表的密钥的具体含义完全取决于哈希表及其包含的数据的使用。
有关此类的更多详细信息,请查看Hashtable。
属性是Hashtable的子类。它用于维护键的列表,其中的键是一个String,该值也是一个String。
Properties类被许多其他Java类使用。例如,它是获取环境值时由System.getProperties()返回的对象的类型。
有关此类的更多详细信息,请选择“属性”。
在Java 2之前,Java提供了诸如字典,向量,堆栈和属性之类的ad hoc类来存储和操作对象组。虽然这些课程很有用,但却缺乏中心统一的主题。因此,您使用Vector的方式与使用“属性”的方式不同。
收藏框架旨在实现几个目标,如 -
框架必须是高性能的。基本集合(动态数组,链表,树和哈希表)的实现将是高效的。
该框架必须允许不同类型的集合以类似的方式工作,并具有高度的互操作性。
框架必须容易地扩展和/或调整收藏。
为此,整个集合框架围绕一组标准接口进行设计。提供了这些接口的几个标准实现(如LinkedList,HashSet和TreeSet),您可以按原样使用,如果选择,您还可以实现自己的集合。
集合框架是用于表示和操作集合的统一架构。所有集合框架包含以下内容 -
接口 - 这些是表示集合的抽象数据类型。接口允许集合被独立于其表示的细节而被操纵。在面向对象语言中,界面通常形成层次结构。
实现,即类 - 这些是集合接口的具体实现。实质上,它们是可重用的数据结构。
算法 - 这些是对实现集合接口的对象执行有用的计算(如搜索和排序)的方法。这些算法被认为是多态的:也就是说,相同的方法可以在适当的收集接口的许多不同实现中使用。
除了集合之外,框架还定义了几个映射接口和类。地图存储键/值对。虽然地图是不是收藏在正确使用的术语,但它们与收藏完全集成。
集合框架定义了几个接口。本节提供每个界面的概述 -
没有 | 接口和说明 |
---|---|
1 | 集合界面
这使您能够处理对象组; 它位于集合层次结构的顶部。 |
2 | 列表界面
这扩展了Collection,List的一个实例存储有序的元素集合。 |
3 | 集合
这扩展了集合来处理集合,它必须包含唯一的元素。 |
4 | SortedSet
这扩展了Set来处理排序集。 |
5 | 地图
这将唯一键映射到值。 |
6 | Map.Entry
这描述了地图中的元素(键/值对)。这是Map的内部类。 |
7 | SortedMap
这扩展了Map,以便按照升序保持密钥。 |
8 | 枚举
这是传统界面定义了可以枚举(一次获取一个)对象集合中的元素的方法。这个传统界面已被Iterator所取代。 |
Java提供了一组实现Collection接口的标准集合类。一些类提供可以按原样使用的完整实现,而其他类是抽象类,提供用作创建具体集合的起点的骨架实现。
标准收集类汇总在下表中 -
没有 | Class及说明 |
---|---|
1 | 摘要 实现大部分的Collection界面。 |
2 | 抽象列表 扩展AbstractCollection并实现大多数List接口。 |
3 | AbstractSequentialList 扩展AbstractList供集合使用,该集合使用其元素的顺序而不是随机访问。 |
4 | LinkedList
通过扩展AbstractSequentialList来实现链表。 |
5 | ArrayList
通过扩展AbstractList来实现动态数组。 |
6 | 抽象集 扩展AbstractCollection并实现大多数Set接口。 |
7 | 哈希集
扩展AbstractSet用于哈希表。 |
8 | LinkedHashSet
扩展HashSet以允许插入顺序迭代。 |
9 | 树集
实现一个存储在树中的集合。扩展AbstractSet。 |
10 | 抽象图 实现大多数Map界面。 |
11 | HashMap
扩展AbstractMap以使用哈希表。 |
12 | TreeMap
扩展AbstractMap以使用树。 |
13 | WeakHashMap
扩展AbstractMap以使用带有弱键的哈希表。 |
14 | LinkedHashMap
扩展HashMap以允许插入顺序迭代。 |
15 | IdentityHashMap
扩展AbstractMap并在比较文档时使用参考等同。 |
本类AbstractCollection,AbstractSet,AbstractList中,AbstractSequentialList和AbstractMap类提供核心集合接口的骨干实现,以最小化来实现他们所需要的努力。
在上一章中探讨了由java.util定义的以下遗留类 -
没有 | Class及说明 |
---|---|
1 | 向量
这实现了一个动态数组。它类似于ArrayList,但有一些差异。 |
2 | 堆栈
Stack是Vector的一个子类,它实现了一个标准的先进先出的堆栈。 |
3 | 字典
字典是一个抽象类,表示一个键/值存储库,操作非常像Map。 |
4 | 哈希表
Hashtable是原始java.util的一部分,是Dictionary的具体实现。 |
5 | 属性
属性是Hashtable的子类。它用于维护键的列表,其中的键是一个String,该值也是一个String。 |
6 | BitSet
BitSet类创建一个特殊类型的数组,它保存位值。这个数组可以根据需要增加大小。 |
集合框架定义了可应用于集合和映射的几种算法。这些算法被定义为Collections类中的静态方法。
有几个方法可以抛出一个ClassCastException异常,当试图比较不兼容的类型,或发生UnsupportedOperationException异常,当试图修改一个不可修改的集合发生。
集合定义了三个静态变量:EMPTY_SET,EMPTY_LIST和EMPTY_MAP。都是不变的
没有 | 算法和描述 |
---|---|
1 | 集合算法
以下是所有算法实现的列表。 |
通常,您将想要循环遍历集合中的元素。例如,您可能希望显示每个元素。
最简单的方法是使用迭代器,它是一个实现Iterator或ListIterator接口的对象。
迭代器使您能够遍历集合,获取或删除元素。ListIterator扩展了Iterator以允许双向遍历列表和修改元素。
没有 | 迭代器方法和描述 |
---|---|
1 | 使用Java Iterator
以下是Iterator和ListIterator接口提供的示例的所有方法的列表。 |
TreeSet和TreeMap都以排序顺序存储元素。但是,比较器是正确定义排序顺序的意思。
这个界面让我们可以使用不同的方式对给定的集合进行排序。此接口也可用于对任何类的任何实例进行排序(甚至我们无法修改的类)。
没有 | 迭代器方法和描述 |
---|---|
1 | 使用Java比较器
以下是比较器接口提供的示例的所有方法的列表。 |
Java集合框架使程序员能够访问预先封装的数据结构以及操作它们的算法。
集合是可以保存对其他对象的引用的对象。集合接口声明可以对每种类型的集合执行的操作。
集合框架的类和接口在包java.util中。
如果我们可以编写一个排序方法,可以对Integer数组,String数组或支持排序的任何类型的数组进行排序,那将是很好的。
Java 泛型方法和通用类使程序员能够使用单个方法声明分别指定一组相关方法,或者使用单个类声明来指定一组相关类型。
泛型还提供编译时类型的安全性,允许程序员在编译时捕获无效类型。
使用Java Generic概念,我们可以编写一个用于排序对象数组的通用方法,然后使用Integer数组,Double数组,String数组等调用泛型方法来对数组元素进行排序。
您可以编写一个可以使用不同类型的参数调用的泛型方法声明。根据传递给通用方法的参数的类型,编译器会适当地处理每个方法调用。以下是定义通用方法的规则 -
所有通用方法声明都有一个类型参数部分,它以方括号返回类型(下一个示例中为<E>)之前的尖括号(<和>)分隔。
每个类型参数部分包含一个或多个类型参数,用逗号分隔。类型参数(也称为类型变量)是指定通用类型名称的标识符。
类型参数可用于声明返回类型,并作为传递给通用方法的参数类型的占位符,这些参数被称为实际类型参数。
通用方法的主体被声明为任何其他方法。请注意,类型参数只能表示引用类型,而不是原始类型(如int,double和char)。
以下示例说明了如何使用单一的Generic方法打印不同类型的数组 -
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(" Array doubleArray contains:"); printArray(doubleArray); // pass a Double array System.out.println(" Array characterArray contains:"); printArray(charArray); // pass a Character array } }
输出结果如下 -
Array integerArray contains: 1 2 3 4 5 Array doubleArray contains: 1.1 2.2 3.3 4.4 Array characterArray contains: H E L L O
有时候您可能希望限制允许传递给类型参数的类型。例如,对数字进行操作的方法可能只希望接受Number或其子类的实例。这是有界类型参数。
要声明一个有界类型的参数,列出type参数的名称,后跟extend关键字,然后是其上限。
以下示例说明如何在一般意义上使用扩展来表示“扩展”(如在类中)或“实现”(如在接口中)。这个例子是Generic方法返回最大的三个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 ", 3, 4, 5, maximum( 3, 4, 5 )); System.out.printf("Max of %.1f,%.1f and %.1f is %.1f ", 6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 )); System.out.printf("Max of %s, %s and %s is %s ","pear", "apple", "orange", maximum("pear", "apple", "orange")); } }
输出结果如下 -
Max of 3, 4 and 5 is 5 Max of 6.6,8.8 and 7.7 is 8.8 Max of pear, apple and orange is pear
通用类声明看起来像一个非泛型类声明,除了类名后跟一个类型参数部分。
与通用方法一样,泛型类的类型参数部分可以有一个或多个类型参数,用逗号分隔。这些类被称为参数化类或参数化类型,因为它们接受一个或多个参数。
下面的例子说明了我们如何定义一个泛型类 -
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 ", integerBox.get()); System.out.printf("String Value :%s ", stringBox.get()); } }
输出结果如下 -
Integer Value :10 String Value :Hello World
Java提供了一种称为对象序列化的机制,其中对象可以表示为包含对象数据的字节序列,以及有关对象的类型和存储在对象中的数据类型的信息。
将序列化对象写入文件后,可从文件中读取并反序列化,表示对象的类型信息和字节及其数据可用于重新创建内存中的对象。
最令人印象深刻的是,整个过程与JVM无关,意味着一个对象可以在一个平台上序列化,并且在完全不同的平台上反序列化。
类ObjectInputStream和ObjectOutputStream是包含用于序列化和反序列化对象的方法的高级流。
ObjectOutputStream类包含许多用于编写各种数据类型的写入方法,但是一种方法尤其突出 -
public final void writeObject(Object x) throws IOException
上述方法序列化一个对象并将其发送到输出流。类似地,ObjectInputStream类包含以下反序列化对象的方法 -
public final Object readObject() throws IOException, ClassNotFoundException
此方法从流中检索下一个Object,并将其反序列化。返回值为Object,因此您需要将其转换为适当的数据类型。
为了演示序列化在Java中的工作原理,我将使用我们早期探讨的Employee类。假设我们有以下Employee类,它实现了Serializable接口 -
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接口。
课程中的所有字段必须是可序列化的。如果一个字段不可序列化,那么它必须标记为瞬态。
如果您好奇知道Java Standard Class是否可序列化,请检查该类的文档。测试很简单:如果类实现java.io.Serializable,那么它是可序列化的; 否则,不是。
ObjectOutputStream类用于序列化对象。以下SerializeDemo程序实例化一个Employee对象并将其序列化到一个文件。
执行完程序后,将创建一个名为employee.ser的文件。该程序不产生任何输出,但研究代码,并尝试确定该程序正在做什么。
注 - 将对象序列化到文件时,Java中的标准约定是给文件扩展名为.ser。
import java.io.*; 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("/tmp/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(); } } }
以下DeserializeDemo程序反序列化在SerializeDemo程序中创建的Employee对象。研究程序并尝试确定其输出 -
import java.io.*; public class DeserializeDemo { public static void main(String [] args) { Employee e = null; try { FileInputStream fileIn = new FileInputStream("/tmp/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); } }
输出结果如下 -
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。
术语网络编程是指编写跨多个设备(计算机)执行的程序,其中设备都使用网络彼此连接。
J2SE API的java.net包包含提供低级别通信细节的类和接口的集合,允许您编写专注于解决现有问题的程序。
java.net包提供对两种常见网络协议的支持 -
TCP - TCP代表传输控制协议,允许两个应用之间的可靠通信。TCP通常通过互联网协议使用,这被称为TCP / IP。
UDP - UDP代表用户数据报协议,一种无连接协议,允许在应用程序之间传输数据包。
本章对以下两个主题给予了很好的理解 -
套接字编程 - 这是网络中最广泛使用的概念,已经非常详细地解释了。
网址处理 - 这将分别介绍。点击此处了解Java语言中的URL处理。
套接字提供使用TCP的两台计算机之间的通信机制。客户端程序在通信结束时创建一个套接字,并尝试将该套接字连接到服务器。
当建立连接时,服务器在通信结束时创建一个套接字对象。客户端和服务器现在可以通过写入和从套接字读取来进行通信。
java.net.Socket类表示一个套接字,而java.net.ServerSocket类为服务器程序提供了一种侦听客户端并与其建立连接的机制。
在使用套接字的两台计算机之间建立TCP连接时,会发生以下步骤 -
服务器实例化一个ServerSocket对象,表示要在哪个端口号进行通信。
服务器调用ServerSocket类的accept()方法。此方法等待客户端连接到给定端口上的服务器。
在服务器等待之后,客户端实例化一个Socket对象,指定要连接的服务器名称和端口号。
Socket类的构造函数尝试将客户端连接到指定的服务器和端口号。如果通信建立,客户端现在有一个能够与服务器进行通信的Socket对象。
在服务器端,accept()方法返回对连接到客户端套接字的服务器上的新套接字的引用。
建立连接后,可以使用I / O流进行通信。每个套接字都有一个OutputStream和一个InputStream。客户端的OutputStream连接到服务器的InputStream,客户端的InputStream连接到服务器的OutputStream。
TCP是双向通信协议,因此可以同时在两个流上发送数据。以下是提供完整的实现套接字方法的有用类。
java.net.ServerSocket中的类由服务器应用程序获得一个端口,监听客户端请求。
ServerSocket类有四个构造函数 -
没有 | 方法和说明 |
---|---|
1 | public ServerSocket(int port)抛出IOException 尝试创建绑定到指定端口的服务器套接字。如果端口已被其他应用程序绑定,则会发生异常。 |
2 | public ServerSocket(int port,int backlog)抛出IOException 与之前的构造函数类似,backlog参数指定存储在等待队列中的传入客户端数量。 |
3 | public ServerSocket(int port,int backlog,InetAddress address)抛出IOException 与以前的构造函数类似,InetAddress参数指定要绑定的本地IP地址。InetAddress用于可能具有多个IP地址的服务器,允许服务器指定哪个IP地址接受客户端请求。 |
4 | public ServerSocket()抛出IOException 创建未绑定的服务器套接字。使用此构造函数时,在准备绑定服务器套接字时,请使用bind()方法。 |
如果ServerSocket构造函数不会引发异常,这意味着您的应用程序已成功绑定到指定的端口,并可以为客户端请求准备。
以下是ServerSocket类的一些常见方法 -
没有 | 方法和说明 |
---|---|
1 | public int getLocalPort() 返回服务器套接字正在侦听的端口。如果您在构造函数中作为端口号传递0,并让服务器为您找到一个端口,则此方法非常有用。 |
2 | public Socket accept()抛出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连接,并且可以开始通信。
java.net.Socket中的类表示,无论是客户端和服务器使用相互沟通的插座。客户端通过实例化Socket对象,而服务器从accept()方法的返回值获取Socket对象。
Socket类有五个构造函数,客户端用于连接到服务器 -
没有 | 方法和说明 |
---|---|
1 | public Socket(String host,int port)throws UnknownHostException,IOException。 此方法尝试连接到指定端口上的指定服务器。如果此构造函数不会引发异常,则连接成功,并且客户端连接到服务器。 |
2 | public Socket(InetAddress host,int port)抛出IOException 该方法与以前的构造函数相同,除了主机由InetAddress对象表示。 |
3 | public Socket(String host,int port,InetAddress localAddress,int localPort)throws IOException。 连接到指定的主机和端口,在指定的地址和端口的本地主机上创建套接字。 |
4 | public Socket(InetAddress主机,int端口,InetAddress localAddress,int localPort)抛出IOException。 此方法与以前的构造函数相同,只是主机由InetAddress对象而不是String表示。 |
5 | public Socket() 创建一个未连接的套接字。使用connect()方法将此套接字连接到服务器。 |
当Socket构造函数返回时,它不会简单地实例化Socket对象,而是实际尝试连接到指定的服务器和端口。
这里列出了Socket类中的一些感兴趣的方法。请注意,客户端和服务器都有一个Socket对象,所以客户端和服务器都可以调用这些方法。
没有 | 方法和说明 |
---|---|
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()抛出IOException 返回套接字的输入流。输入流连接到远程插座的输出流。 |
7 | public OutputStream getOutputStream()抛出IOException 返回套接字的输出流。输出流连接到远程插座的输入流。 |
8 | public void close()throws IOException 关闭套接字,这使得此Socket对象不再能够再次连接到任何服务器。 |
此类表示Internet协议(IP)地址。以下是在执行套接字编程时需要的以下有用的方法 -
没有 | 方法和说明 |
---|---|
1 | static InetAddress getByAddress(byte [] addr) 给出给定原始IP地址的InetAddress对象。 |
2 | static InetAddress getByAddress(String host,byte [] addr) 根据提供的主机名和IP地址创建InetAddress。 |
3 | 静态InetAddress getByName(String host) 确定主机名称的IP地址。 |
4 | String getHostAddress() 返回文本显示中的IP地址字符string。 |
5 | String getHostName() 获取此IP地址的主机名。 |
6 | 静态InetAddress InetAddress getLocalHost() 返回本地主机。 |
7 | String toString() 将此IP地址转换为字符string。 |
以下GreetingClient是通过使用套接字连接到服务器并发送问候语的客户端程序,然后等待响应。
// 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(); } } }
以下GreetingServer程序是使用Socket类在由命令行参数指定的端口号上侦听客户端的服务器应用程序的示例 -
// 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() + " Goodbye!"); 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 GreetingClient localhost 6066 Connecting to localhost on port 6066 Just connected to localhost/127.0.0.1:6066 Server says Thank you for connecting to /127.0.0.1:6066 Goodbye!
使用Java应用程序发送电子邮件很简单,但首先应该在您的计算机上安装JavaMail API和Java Activation Framework(JAF)。
您可以从Java的标准网站下载最新版本的JavaMail(1.2版)。
您可以从Java的标准网站下载最新版本的JAF(版本1.1.1)。
下载并解压缩这些文件,在新创建的顶级目录中,您将会找到两个应用程序的大量jar文件。您需要在CLASSPATH中添加mail.jar和activation.jar文件。
以下是从您的机器发送简单电子邮件的示例。假设您的本地主机已连接到Internet,并且能够发送电子邮件。
// 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(); } } }
编译并运行此程序发送简单的电子邮件 -
$ java SendEmail Sent message successfully....
如果要将电子邮件发送给多个收件人,则将使用以下方法指定多个电子邮件ID -
void addRecipients(Message.RecipientType type, Address[] addresses) throws MessagingException
这是参数的描述 -
类型 - 这将设置为TO,CC或BCC。这里CC代表碳复制,BCC代表黑碳复制。示例:Message.RecipientType.TO
地址 - 这是一个电子邮件ID数组。您需要在指定电子邮件ID时使用InternetAddress()方法。
以下是从您的机器发送HTML电子邮件的示例。这里假设您的本地主机已连接到Internet,并且能够发送电子邮件。
这个例子与前一个非常相似,除了这里我们使用setContent()方法来设置第二个参数是“text / html”的内容来指定HTML内容被包含在消息中。
使用此示例,您可以发送像您喜欢的HTML内容一样大。
// 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电子邮件 -
$ java SendHTMLEmail Sent message successfully....
以下是从机器发送带有附件的电子邮件的示例。这里假设您的本地主机连接到互联网,并且能够发送电子邮件。
// 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 SendFileEmail Sent message successfully....
如果需要向电子邮件服务器提供用户ID和密码进行身份验证,则可以按如下方式设置这些属性:
props.setProperty("mail.user", "myuser"); props.setProperty("mail.password", "mypwd");
电子邮件发送机制的其余部分将保持如上所述。
Java是一种多线程编程语言,这意味着我们可以使用Java开发多线程程序。多线程程序包含两个或多个可同时运行的部件,每个部件可以同时处理不同的任务,从而最佳地利用可用资源,特别是当您的计算机有多个CPU时。
根据定义,多任务是当多个进程共享诸如CPU的公共处理资源时。多线程将多任务的概念扩展到可以将单个应用程序中的特定操作细分为单个线程的应用程序。每个线程可以并行运行。OS不仅在不同的应用程序之间划分处理时间,而且在应用程序中的每个线程之间划分处理时间。
多线程使您能够以同一程序同时进行多个活动的方式进行写入。
线程在其生命周期中经历了各个阶段。例如,线程诞生,启动,运行,然后死亡。下图显示了线程的完整生命周期。
以下是生命周期的阶段 -
新的 - 新线程在新的状态下开始其生命周期。直到程序启动线程为止,它保持在这种状态。它也被称为天生线。
Runnable - 新诞生的线程启动后,该线程可以运行。该状态的线程被认为正在执行其任务。
等待 - 有时,线程会转换到等待状态,而线程等待另一个线程执行任务。只有当另一个线程发信号通知等待线程才能继续执行时,线程才转回到可运行状态。
定时等待 - 可运行的线程可以在指定的时间间隔内进入定时等待状态。当该时间间隔到期或发生等待的事件时,此状态的线程将转换回可运行状态。
终止(Dead) - 可执行线程在完成任务或以其他方式终止时进入终止状态。
每个Java线程都有一个优先级,可以帮助操作系统确定安排线程的顺序。
Java线程优先级在MIN_PRIORITY(常数为1)和MAX_PRIORITY(常数为10)之间的范围内。默认情况下,每个线程都被赋予优先级NORM_PRIORITY(常数为5)。
具有较高优先级的线程对于一个程序来说更重要,应该在低优先级线程之前分配处理器时间。然而,线程优先级不能保证线程执行的顺序,并且依赖于平台。
如果您的类旨在作为线程执行,那么您可以通过实现Runnable接口来实现此目的。您将需要遵循三个基本步骤 -
作为第一步,您需要实现由Runnable接口提供的run()方法。该方法为线程提供了一个入口点,您将把完整的业务逻辑放在此方法中。以下是run()方法的简单语法 -
public void run( )
作为第二步,您将使用以下构造函数实例化一个Thread对象 -
Thread(Runnable threadObj, String threadName);
其中,threadObj是实现Runnable接口的类的实例,threadName是给予新线程的名称。
一旦创建了一个线程对象,您可以通过调用start()方法启动它,该方法执行对run()方法的调用。以下是一个简单的语法start()方法 -
void start();
这是一个创建一个新线程并开始运行的示例 -
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(); } }
输出结果如下 -
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类。这种方法在处理使用Thread类中可用的方法创建的多个线程时提供了更多的灵活性。
您将需要覆盖Thread类中可用的run()方法。该方法为线程提供了一个入口点,您将把完整的业务逻辑放在此方法中。以下是run()方法的简单语法 -
public void run( )
一旦创建了Thread对象,您可以通过调用start()方法启动它,该方法执行对run()方法的调用。以下是一个简单的语法start()方法 -
void start( );
这是以前的程序重写来扩展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(); } }
输出结果如下 -
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类中可用的重要方法的列表。
没有 | 方法和说明 |
---|---|
1 | public void start() 在单独的执行路径中启动线程,然后调用此Thread对象上的run()方法。 |
2 | public void run() 如果使用单独的Runnable目标实例化了此Thread对象,则在该Runnable对象上调用run()方法。 |
3 | public final void setName(String name) 更改Thread对象的名称。还有一个用于检索名称的getName()方法。 |
4 | public final void setPriority(int priority) 设置此Thread对象的优先级。可能的值在1到10之间。 |
5 | public final void setDaemon(boolean on) true的参数表示此线程作为守护线程。 |
6 | public final void join(long millisec) 当前线程在第二个线程上调用此方法,导致当前线程阻塞,直到第二个线程终止或指定的毫秒数通过。 |
7 | public void interrupt() 中断此线程,导致它由于任何原因被阻止而继续执行。 |
8 | public final boolean isAlive() 如果线程是活着的,那么线程启动之后,运行到完成之前的任何时间都会返回true。 |
以前的方法在一个特定的Thread对象上被调用。Thread类中的以下方法是静态的。调用其中一个静态方法对当前运行的线程执行操作。
没有 | 方法和说明 |
---|---|
1 | public static void yield() 导致当前运行的线程屈服于任何其他正在等待调度的优先级的其他线程。 |
2 | public static void sleep(long millisec) 使当前正在运行的线程至少阻塞指定的毫秒数。 |
3 | public static boolean holdingLock(Object x) 如果当前线程持有给定对象的锁,则返回true。 |
4 | public static Thread currentThread() 返回对当前正在运行的线程的引用,该线程是调用此方法的线程。 |
5 | public static void dumpStack() 打印当前正在运行的线程的堆栈跟踪,这在调试多线程应用程序时非常有用。 |
以下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..."); } }
输出结果如下。您可以一次又一次地尝试这个例子,每次都会得到不同的结果。
Starting hello thread... Starting goodbye thread... Hello Hello Hello Hello Hello Hello Goodbye Goodbye Goodbye Goodbye Goodbye .......
在Java中进行多线程编程时,您需要具有以下概念:
一个小程序是在Web浏览器中运行的Java程序。小程序可以是一个功能齐全的Java应用程序,因为它具有完整的Java API。
applet和独立Java应用程序之间有一些重要的区别,包括以下内容:
applet是扩展java.applet.Applet类的Java类。
一个main()方法不会在一个applet上被调用,一个applet类不会定义main()。
Applet旨在嵌入到HTML页面中。
当用户查看包含小程序的HTML页面时,该小程序的代码将被下载到用户的机器。
需要JVM才能查看小程序。JVM可以是Web浏览器的插件或单独的运行时环境。
用户机器上的JVM创建一个applet类的实例,并在applet的生命周期中调用各种方法。
Applet具有由Web浏览器执行的严格的安全规则。小程序的安全性通常被称为沙盒安全性,将小程序与在沙箱中玩的小孩进行比较,其中必须遵循各种规则。
Applet需要的其他类可以在单个Java Archive(JAR)文件中下载。
Applet类中的四种方法为您提供创建任何严重applet的框架 -
init - 此方法适用于您的Applet需要的初始化。在applet标签内的param标签已被处理之后被调用。
start - 这个方法在浏览器调用init方法后自动调用。每当用户在离开其他页面之后返回到包含小程序的页面时,也会调用它。
停止 - 当用户离开小程序所在的页面时,自动调用此方法。因此,它可以在同一小程序中重复调用。
destroy - 此方法仅在浏览器正常关闭时才调用。因为小程序是为了生活在HTML页面上,所以在用户离开包含小程序的页面之后,通常不应该留下资源。
paint - 在start()方法之后立即调用,并且任何时候applet需要在浏览器中重新绘制自己。paint()方法实际上是继承自java.awt的。
以下是一个名为HelloWorldApplet.java的简单小程序 -
import java.applet.*; import java.awt.*; public class HelloWorldApplet extends Applet { public void paint (Graphics g) { g.drawString ("Hello World", 25, 50); } }
这些import语句把类放在我们的applet类的范围内 -
没有这些import语句,Java编译器将无法识别applet类引用的类Applet和Graphics。
每个小程序都是java.applet.Applet类的扩展。基础Applet类提供派生的Applet类可以从浏览器上下文中调用以获取信息和服务的方法。
这些包括执行以下操作的方法 -
此外,Applet类提供了一个界面,通过该接口,查看器或浏览器可以获取关于小程序的信息并控制小程序的执行。观众可能 -
Applet类提供了每种方法的默认实现。这些实现可以根据需要被覆盖。
“Hello,World”小程序是完整的。覆盖的唯一方法是涂漆方法。
可以通过将指令嵌入到HTML文件中并通过Applet浏览器或支持Java的浏览器查看文件来调用小程序。
<applet>标签是将小程序嵌入到HTML文件中的基础。以下是一个调用“Hello,World”小程序的例子,
<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>
注意 - 您可以参考HTML小程序标签来了解更多关于从HTML调用applet。
<applet>标签的代码属性是必需的。它指定要运行的Applet类。还需要宽度和高度来指定小程序运行的面板的初始大小。applet指令必须用</ applet>标签关闭。
如果小程序获取参数,则可以通过在<applet>和</ applet>之间添加<param>标签来为参数传递值。浏览器忽略小程序标签之间的文本和其他标签。
非Java启用浏览器不处理<applet>和</ applet>。因此,与非applet相关的标签之间出现的任何内容在非启用Java的浏览器中都可见。
查看器或浏览器在文档的位置查找编译的Java代码。要指定,请使用<applet>标签的codebase属性,如图所示 -
<applet codebase = "https://amrood.com/applets" code = "HelloWorldApplet.class" width = "320" height = "120">
如果一个applet驻留在默认的包中,则必须使用句点(。)在代码属性中指定保持包以分隔包/类组件。例如 -
<applet = "mypackage.subpackage.TestApplet.class" width = "320" height = "120">
以下示例演示如何使applet响应文档中指定的设置参数。该小程序显示黑色和第二种颜色的棋盘图案。
可以将第二种颜色和每个正方形的大小指定为文档中的小程序的参数。
CheckerApplet在init()方法中获取参数。它也可以在paint()方法中获得它的参数。但是,在小程序开始时获取值并保存设置一次,而不是每次刷新,方便而有效。
applet查看器或浏览器调用它运行的每个小程序的init()方法。查看器在加载小程序后立即调用init()一次。(Applet.init()被实现为不执行任何操作。)覆盖默认实现以插入自定义初始化代码。
Applet.getParameter()方法获取给定参数名称的参数(参数的值始终为字符string)。如果值是数字或其他非字符数据,则必须解析该字符string。
以下是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()和private 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(),它解析一个字符string并返回一个整数。Integer.parseInt()只要参数无效就抛出异常。
因此,parseSquareSize()捕获异常,而不是允许小程序在坏输入上失败。
小程序调用parseColor()来将颜色参数解析为颜色值。parseColor()执行一系列字符string比较,以将参数值与预定义颜色的名称进行匹配。您需要实现这些方法来使此applet工作。
以下是嵌入了CheckerApplet的HTML文件的示例。HTML文件通过<param>标签指定小程序的两个参数。
<html> <title>Checkerboard Applet</title> <hr> <applet code = "CheckerApplet.class" width = "480" height = "320"> <param name = "color" value = "blue"> <param name = "squaresize" value = "30"> </applet> <hr> </html>
注 - 参数名称不区分大小写。
将图形化的Java应用程序(即使用AWT的应用程序,您可以从Java程序启动器开始)转换为可嵌入网页的小程序很容易。
以下是将应用程序转换为小程序的具体步骤。
使用适当的标签制作一个HTML页面来加载小程序代码。
提供JApplet类的子类。让这个类公开。否则,该小程序无法加载。
消除应用程序中的主要方法。不要为应用程序创建框架窗口。您的应用程序将显示在浏览器中。
将任何初始化代码从框架窗口构造函数移动到该applet的init方法。您不需要显式创建applet对象。浏览器为您实例化并调用init方法。
删除对setSize的调用; 对于小程序,使用HTML文件中的width和height参数来调整大小。
删除对setDefaultCloseOperation的调用。小程序不能关闭; 它会在浏览器退出时终止。
如果应用程序调用setTitle,请删除对该方法的调用。小程序不能有标题栏。(当然,您可以使用HTML标题标签来标题网页本身。)
不要调用setVisible(true)。该小程序将自动显示。
Applet从Container类继承了一组事件处理方法。Container类定义了几种方法,例如processKeyEvent和processMouseEvent,用于处理特定类型的事件,然后是一个称为processEvent的全部方法。
为了对事件作出反应,小程序必须覆盖适当的事件特定方法。
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! "); } }
现在,让我们称之为applet如下:
<html> <title>Event Handling</title> <hr> <applet code = "ExampleEventHandling.class" width = "300" height = "300"> </applet> <hr> </html>
最初,小程序将显示“初始化小程序,启动小程序”。然后一旦点击矩形,“鼠标点击”也将显示。
小程序可以显示GIF,JPEG,BMP等格式的图像。要在小程序中显示图像,请使用java.awt.Graphics类中的drawImage()方法。
以下是显示图像的所有步骤的示例 -
import java.applet.*; import java.awt.*; import java.net.*; public class ImageDemo extends Applet { private Image image; private AppletContext context; public void init() { context = this.getAppletContext(); String imageURL = this.getParameter("image"); if(imageURL == null) { imageURL = "java.jpg"; } try { URL url = new URL(this.getDocumentBase(), imageURL); image = context.getImage(url); }catch(MalformedURLException e) { e.printStackTrace(); // Display in browser status bar context.showStatus("Could not load image!"); } } public void paint(Graphics g) { context.showStatus("Displaying image"); g.drawImage(image, 0, 0, 200, 84, null); g.drawString("www.javalicense.com", 35, 100); } }
现在,让我们称之为applet如下:
<html> <title>The ImageDemo applet</title> <hr> <applet code = "ImageDemo.class" width = "300" height = "200"> <param name = "image" value = "java.jpg"> </applet> <hr> </html>
Applet可以播放java.applet包中的AudioClip接口所代表的音频文件。AudioClip界面有三种方法,包括 -
public void play() - 从头开始一次播放音频剪辑。
public void loop() - 导致音频剪辑不断重播。
public void stop() - 停止播放音频剪辑。
要获取AudioClip对象,必须调用Applet类的getAudioClip()方法。getAudioClip()方法立即返回,无论URL是否解析为实际的音频文件。在尝试播放音频剪辑之前,音频文件不会被下载。
以下是演示播放音频的所有步骤的示例 -
import java.applet.*; import java.awt.*; import java.net.*; public class AudioDemo extends Applet { private AudioClip clip; private AppletContext context; public void init() { context = this.getAppletContext(); String audioURL = this.getParameter("audio"); if(audioURL == null) { audioURL = "default.au"; } try { URL url = new URL(this.getDocumentBase(), audioURL); clip = context.getAudioClip(url); }catch(MalformedURLException e) { e.printStackTrace(); context.showStatus("Could not load audio file!"); } } public void start() { if(clip != null) { clip.loop(); } } public void stop() { if(clip != null) { clip.stop(); } } }
现在,让我们称之为applet如下:
<html> <title>The ImageDemo applet</title> <hr> <applet code = "ImageDemo.class" width = "0" height = "0"> <param name = "audio" value = "test.wav"> </applet> <hr> </html>
您可以在PC上使用test.wav来测试上述示例。
Java语言支持三种类型的注释 -
没有 | 注释和说明 |
---|---|
1 | / * text * / 编译器忽略从/ *到* /的所有内容。 |
2 | //文本 编译器忽略从//到行尾的所有内容。 |
3 | / **文件* / 这是一个文档注释,一般来说它被称为doc注释。在JDK的javadoc工具使用文档注释准备自动生成的文档时。 |
本章是关于解释Javadoc的。我们将看到我们如何利用Javadoc来生成有用的Java代码文档。
Javadoc是一个JDK附带的工具,它用于从Java源代码生成HTML格式的Java代码文档,需要以预定格式的文档。
Following is a simple example where the lines inside /*….*/ are Java multi-line comments. Similarly, the line which preceeds // is Java single-line comment.
/** * 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!"); } }
You can include required HTML tags inside the description part. For instance, the following example makes use of <h1>....</h1> for heading and <p> has been used for creating paragraph break -
/** * <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 tool recognizes the following tags -
Tag | Description | Syntax |
---|---|---|
@author | Adds the author of a class. | @author name-text |
{@code} | Displays text in code font without interpreting the text as HTML markup or nested javadoc tags. | {@code text} |
{@docRoot} | Represents the relative path to the generated document"s root directory from any generated page. | {@docRoot} |
@deprecated | Adds a comment indicating that this API should no longer be used. | @deprecated deprecatedtext |
@exception | Adds a Throws subheading to the generated documentation, with the classname and description text. | @exception class-name description |
{@inheritDoc} | Inherits a comment from the nearest inheritable class or implementable interface. | Inherits a comment from the immediate surperclass. |
{@link} | Inserts an in-line link with the visible text label that points to the documentation for the specified package, class, or member name of a referenced class. | {@link package.class#member label} |
{@linkplain} | Identical to {@link}, except the link"s label is displayed in plain text than code font. | {@linkplain package.class#member label} |
@param | Adds a parameter with the specified parameter-name followed by the specified description to the "Parameters" section. | @param parameter-name description |
@return | Adds a "Returns" section with the description text. | @return description |
@see | Adds a "See Also" heading with a link or text entry that points to reference. | @see reference |
@serial | Used in the doc comment for a default serializable field. | @serial field-description | include | exclude |
@serialData | Documents the data written by the writeObject( ) or writeExternal( ) methods. | @serialData data-description |
@serialField | Documents an ObjectStreamField component. | @serialField field-name field-type field-description |
@since | Adds a "Since" heading with the specified since-text to the generated documentation. | @since release |
@throws | The @throws and @exception tags are synonyms. | @throws class-name description |
{@value} | When {@value} is used in the doc comment of a static field, it displays the value of that constant. | {@value package.class#field} |
@version | Adds a "Version" subheading with the specified version-text to the generated docs when the -version option is used. | @version version-text |
Following program uses few of the important tags available for documentation comments. You can make use of other tags based on your requirements.
The documentation about the AddNum class will be produced in HTML file AddNum.html but at the same time a master file with a name index.html will also be created.
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); } }
Now, process the above AddNum.java file using javadoc utility as follows -
$ 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 $
You can check all the generated documentation here - AddNum. If you are using JDK 1.7 then javadoc does not generate a great stylesheet.css, so we suggest to download and use standard stylesheet from https://docs.oracle.com/javase/7/docs/api/stylesheet.css