JavaBeginnersTutorial-中文系列教程-一-
JavaBeginnersTutorial 中文系列教程(一)
Java 教程
Java 教程 – 入门
Java 的历史
原文: https://javabeginnerstutorial.com/core-java-tutorial/history-of-java/
Java 是一种通用的通用计算机编程语言,它是基于环境的,基于类的,面向对象的,并且经过专门设计以尽可能减少应用依赖项。 Java 最初是为交互式电视开发的,但是对于当时的数字有线电视行业来说,它是太先进的技术。 建议让应用开发人员“编写一次,就可以在任何地方运行”,这表明已编译的 Java 代码可以在纵容 Java 的所有平台上运行,而无需重新编译。 Java 应用被编译为可以在任何 Java 虚拟机(JVM)上运行的字节码,而与计算机的架构无关。
Java 包罗万象,但事实并非如此。 它毫不费力地开始。 这一切始于 1990 年,当时 Sun Microsystems 工程师 Patrick Naughton 对 Sun 的 C++ 和 C API 的情况更加恼火,并有机会作为 The Stealth Project 的一部分来创建替代语言。
Stealth Project 很快变成了 Green Project,Mike Sheridan 和 James Gosling 进入了行列,该小组开始开发用于对下一代智能设备进行编程的新技术。
编译后的代码对其他处理器毫无用处,因此必须重新编译。 因此 5 人的合作伙伴也被称为“绿色团队”,开始致力于产生一种可访问且具有成本效益的解决方案。 他们工作了 18 个月来建立一种灵活的,与平台无关的语言,该语言可以创建可以在不同环境下的各种处理器上运行的代码。 上面的必要性导致 Java 的创建。
Java 开发
Java 编程语言是由 James Gosling,Patrick Naughton,Chris Warth,Mike Sheridan 和 Ed Frank 的 5 位杰出人士开发的,但是 James Gosling 被认为是发明者,因为他完成了 Java 的原始设计并实现了 Java 的原始编译器, 虚拟机。 他们全都为 Sun Microsystems,Inc. 经营,并于 1991 年开发。该语言用了 18 个月的时间完成,并最初的名称为“Oak”,由于版权问题,该名称于 1995 年重命名为 Java。
合作伙伴聚集在一起,决定一个新名称。 建议的单词是“动态的”,“革命的”,“丝绸的”,“摇晃的”,“DNA”等。他们想要揭示技术真实性的东西:革命性的,动态的,活泼的,酷的,独特的且易于拼写的,乐于讲述的。
根据 James Gosling 的说法,“Java 是 Silk 的首选之一”。 最多的团队成员首选 Java,因为名称是唯一的。
Java 版本
自 JDK 1.0 以来,Java 语言经历了各种修改,并且将许多类和单元包含到标准库中。 从 J2SE 1.4 开始,Java 语言的开发已经由 Java Community Process(JCP)监督,该社区利用 Java Specification Requests(JSR)提出和指定对 Java 程序的扩展和修改。 该语言由 Java 语言规范(JLS)规定; 根据 JSR 901 对 JLS 进行更改。
除了语言的变化之外,Java 类库还进行了许多令人激动的更改,从 JDK 1.0 中的几百个类增加到 J2SE 5 中的三千多个。全新的 API,例如 Swing 和 Java2D 已提出,并且反对了许多原始的 JDK 1.0 类和实践。 某些程序允许将 Java 程序从 Java 平台的一种版本转换为旧版本(例如,将 Java 5.0 反向移植到 1.4)。
2017 年 9 月,Java 平台首席设计师 Mark Reinhold 的目标是将发布系列更改为“每六个月发布一次特色发布”,而不是当前的两年时间表,此计划随后生效。
Java 8 是当前支持的长期支持(LTS)版本,而 Java 10 是目前认可的加速发布版本,截至 3 月 20 日, 2018 年。Java10 支持终止于与 Java 11 支持开始的同一天(计划于 2018 年 9 月开始),Java 11 将成为 Java 8 之后的下一个 LTS。不再公开支持 Java 7,Java 9 自 Java 以来已停止接受更新。 9 是一个短期快速发布版本,已被 Java 10 取代,并且 Java 8 的“公共更新结束”预计将于 2019 年 1 月用于商业用途,而不早于 2020 年 12 月用于非商业用途。
已发布的几个 Java 版本,它们是:
- JDK Alpha 和 Beta(1995)
- JDK 1.0(1996 年 1 月 23 日)
- JDK 1.1(1997 年 2 月 19 日)
- J2SE 1.2(1998 年 12 月 8 日)
- J2SE 1.3(2000 年 5 月 8 日)
- J2SE 1.4(2002 年 2 月 6 日)
- J2SE 5.0(2004 年 9 月 30 日)
- Java SE 6(2006 年 12 月 11 日)
- Java SE 7(2011 年 7 月 28 日)
- Java SE 8(2014 年 3 月 18 日)
- Java SE 9(2017 年 9 月 21 日)
- Java SE 10(2018 年 3 月 20 日)
Java 基础知识:Java 入门
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-basicsgetting-started-with-java/
在这里,我们将讨论与 Java 相关的一些基础主题。
- 在哪里下载 Java。
- 如何安装 Java。
- 设置环境变量。
- 我们的第一个 Java 程序。
- 如何编译 Java 应用。
- 如何运行 Java 应用。
- Java 重要术语之间的区别(JDK 与 JRE 或 J2SE 与 J2EE ..)。
如何下载 Java
可以从 Java 网站下载最新版本的 Java。
Java 安装
安装 Java 时没有特殊要求。 确保您在计算机上具有适当的权限来安装软件。 它可以像其他任何软件一样安装(.exe
)。
设置环境变量
安装 Java 之后,需要设置一些环境变量。
CLASSPATH
:该环境变量指向 JDK 主目录的位置。 它还包含ClassLoader
从中加载 jar 的文件夹的地址(有关ClassLoader
的更多详细信息,请访问此处)JAVA_HOME
:此环境变量将指向 Java 主目录的位置。
如何在不同平台上设置环境变量
Windows XP
要在 Windows XP 中设置环境变量,请右键单击“我的电脑”图标并选择“属性”。 在“属性”窗口中,选择“高级”选项卡,然后单击“环境变量”。 将出现一个窗口,您可以通过选择“新建”按钮在系统变量下输入新的环境变量。
Windows Vista / 7
要在 Windows Vista / 7 中设置环境变量,请在“计算机”上单击鼠标右键,然后选择“属性”。 在属性窗口中选择“高级系统设置”,然后选择“高级”选项卡并单击“环境变量”。 将出现一个窗口,您可以通过选择“新建”按钮在用户/系统变量下输入新的环境变量。
如何检查是否已安装 Java
要检查 Java 是否正确安装,请打开“命令提示符”。 要打开命令提示符,请在运行命令中写入CMD
,然后按Enter
。 在命令提示符窗口中,输入“java -version
”。 如果正确安装了 Java,并且所有环境变量都配置正确,它将显示已安装的 Java 版本。 反映在命令提示符下的信息将像
C:\Users\Jbt>java -version
java version "1.8.0_51"
Java(TM) SE Runtime Environment (build 1.8.0_51-b16)
Java HotSpot(TM) Client VM (build 25.51-b03, mixed mode, sharing)
如果在安装或设置环境变量时出现任何问题,命令提示符下的输出将如下所示。
'java' is not recognized as an internal or external command,
operable program or batch file.
如何检查 Java 是否最新
要了解系统上安装的 Java 是否最新,请单击此处。
我们的第一个 Java 程序
尽管流行的第一个用 Java 编写的程序是“HelloWorld!!”。 这里我们将编写一个程序打印"Hello JBT!"
。🙂
打开一个编辑器并编写以下代码。
public class FirstProgramme {
public static void main(String args[]) {
System.out.println("Hello JBT!");
}
}
将名称为FirstProgramme.java
的文件保存在文件夹c\jbt
中。 请注意,文件名应与公开类的名称相同(有关类文件规则的更多详细信息,请单击此处)。 保存文件后,打开命令提示符,并将工作目录更改为c:\jbt
,即文件的保存位置。 编写“javac FirstProgramme.java
”来编译 Java 代码,如下所示。
C:\Users\JBT>cd C:JBT
C:JBT>javac FirstProgramme.java
C:JBT>
如果正确编译了 Java 文件,则编译器将为源 Java 创建一个类文件。 它将被保存在与源文件相同的位置。 由于在给定的代码中未声明任何包,因此将在相同的文件夹位置中创建.class
文件。
注意使用包声明的 Java 文件的区别。 让我们如下创建另一个 Java 文件。
package com.jbt;
public class FirstProgrammeWithPackage {
public static void main(String args[]) {
System.out.println("Hello JBT!");
}
}
将此文件另存为“FirstProgrammeWithPackage.java
”到“c:\jbt
”。 现在转到命令提示符并执行以下命令。
javac -d . FirstProgrammeWithPackage.java
它将在相应的包(com.jbt
)中创建类文件。
既然您知道了如何使用一个包和一个没有包来编译 Java 文件,下一步将运行这些类文件。
如何运行 Java 应用
现在您的 Java 文件已编译完毕,我们可以使用如下所示的“java
”命令执行应用。
不带包
C:\JBT>java FirstProgramme
Hello JBT!
C:\JBT>
带包
C:\JBT>java com.jbt.FirstProgrammeWithPackage
Hello JBT!
或者
C:\JBT>java com/jbt/FirstProgrammeWithPackage
Hello JBT!
注:java
命令使用不带扩展名的类文件名(.class
)。
这样,我们就完成了创建并运行我们的第一个 Java 应用。
在下一节中,我们将学习 JDK 和 JRE 之间的区别。
https://www.youtube.com/embed/nyOoLgWmmt8?feature=oembed
jdk vs jre vs jvm
原文: https://javabeginnerstutorial.com/core-java-tutorial/jdk-vs-jre-vs-jvm/
为了了解 JDK vs JRE vs JVM。 您需要首先了解每个术语。 因此,我们首先定义 JDK(Java 开发工具包),JRE(Java 运行时环境)和 JVM(Java 虚拟机)。
JDK(Java 开发套件)
JDK 包含开发和运行 Java 应用所需的所有内容。
JRE(Java 运行时环境)
JRE 包含运行已编译的 Java 应用所需的所有内容。 它不包含开发 Java 应用所需的代码库。
JVM(Java 虚拟机)
JVM 是一种虚拟机,可在您的操作系统上运行,为编译后的
Java 代码提供推荐的环境。 JVM 仅适用于字节码。 因此,您需要编译 Java 应用(.java
),以便可以将其转换为字节码格式(也称为.class
文件)。
然后,JVM 将使用它来运行应用。 JVM 仅提供执行 Java 字节码所需的环境。
JDK vs JRE vs JVM
下表显示了每种 Java 技术的不同功能。
图片提供:Oracle 公司
现在,根据该图,您可以确定有什么区别。
JRE = JVM + 运行应用所需的库。
JDK = JRE + 开发 Java 应用所需的库。
Java 可移植性
为了理解 Java 的可移植性,您需要从头到尾了解 Java 代码会发生什么。
- Java 源代码(由开发人员编写)(机器中性)
- 编译代码/字节码(由
javac
编译)(机器中性) - 执行的字节码(由 JVM 执行)(机器特定)
在步骤 2 中,javac
(Java 编译器)将 Java 代码转换为字节码。 可以将其移动到任何计算机(Windows/Linux)并由 JVM 执行。 JVM 读取字节码并生成机器特定的代码。 为了生成特定于机器的代码,JVM 需要特定于机器。 因此,每种类型的计算机(Windows/Linux/Mac)都具有特定的 JVM。 因此,编码器
无需费心生成字节码。 JVM 负责可移植性。 因此,最终的答案是 Java 是可移植
,而 JVM 是特定于机器的。
https://www.youtube.com/embed/RJ5NLb2zLhw?start=5&feature=oembed
public static void main(string args[])
说明
原文: https://javabeginnerstutorial.com/core-java-tutorial/public-static-void-mainstring-args-explanation/
JVM 将始终寻找特定的方法签名来开始运行应用,该签名将为public static void main(String args[])
。此处args
是字符串数组类型的参数。 字符串数组参数也可以写成String[] args
。 尽管参数(字符串数组)的类型是固定的,但是您仍然可以将名称从args
更改为任何内容。
同样,随着 Java 可变参数的引入,除了编写String args[]
之外,还可以使用String…args
。 不断学习以了解每个关键字的更多信息。
class JBT{
public static void main(String args[])
{
System.out.println("Hello JBT");
}
}
在以上应用示例中,我们使用了public static void main
。 每个词都有不同的含义和目的。
Public
它是访问修饰符,它定义谁可以访问此方法。 Public
意味着任何类都可以访问此方法(如果其他类可以访问此类。)。
static
static
是标识与类相关的事物的关键字。 这意味着给定的方法或变量与实例无关,而与类相关。 无需创建类实例即可对其进行访问。
void
它用于定义方法的返回类型。 它定义了方法可以返回的内容。 无效表示方法将不返回任何值。
main
main
是方法的名称。 JVM 搜索此方法名称,作为仅具有特定签名的应用的起点。
String args[]
/String… args
它是main
方法的参数。 参数名称可以是任何东西。 您可以使用String
数组(String args[]
)或String
类型的可变参数变量。 两者将以相同的方式工作。
面向初学者的 Java 类和对象教程
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-class-object-tutorial/
类是用于创建定义其状态和行为的对象的模板。 一个类包含用于定义其对象的状态和行为的字段和方法。
声明类的语法:
<Access Modifier> class <Class_Name> extends
<Super_Class_Name> implements <Interface_Name>
访问修饰符:定义 Java 世界中谁可以访问该类以及该类的成员。
CLASS_NAME
:特定包中类的唯一名称。
SUPER_CLASS_NAME
:给定类扩展的类的名称。 (为此使用 extends
关键字)
INTERFACE_NAME
:以上类实现的接口的名称。 (implements
关键字用于此目的)
类的内部结构
<Access_Modifier> class <Class_Name> extends <Super_Class_Name> implements <Interface_Name>{
<static initilizar block>
<ananymous_block>
<constructor_declarations>
<field_declarations (Static or Non-Static)>
<method_declarations (Static or Non-Static)>
<Inner_class_declarations>
<nested_interface_declarations>
variables_inside_class(Static or Non Static)
}
Java 类的示例
/*
* This is Multi Line Comment and Comment can appear at any place
*/
package com.jbt;
import java.lang.*;
/*
* As this file contains public class. Then the name of this file should be TestClass.java
*/
public class TestClass {
public int i; // This is Non Static variable
static {
System.out.println("This is static block");
}
{
System.out.println("This is ananuymous block");
}
TestClass() {
System.out.println("This is constructor");
}
void methid() {
System.out.println("This is method");
}
}
class AnotherClass {
}
将类写入 Java 源文件中。 一个源文件可以包含多个 Java 类。 有一些与 Java 源文件相关的规则,如下所示。
适用于源文件的规则
- 每个源代码文件只能有一个公开类,但可以有多个非公开类。
- 如果源代码文件中存在任何公开类,则文件名应为该类的名称。
- 源代码文件中的语句序列应为包 >>
import
>> 类声明。 - 没有序列规则适用于注释。
- 注释可以在源代码文件的任何部分的任何位置。
- 没有公开类的文件可以具有该类的任何名称。
- 导入和打包语句应应用于同一源代码文件中的所有类。
对象
关于类和对象之间的区别通常会感到困惑。 类是原型的创建,而对象是该原型的实际实现。 从技术上讲,类是描述该类实例可以具有的状态和行为的模板。 对象以变量和方法的形式实现状态和行为,并需要分配一些内存。
如何创建类的对象
要创建类的对象,可以使用new
关键字。
句法:
<Class_Name> ClassObjectReference = new <Class_Name>();
在这里,将执行类(Class_Name
)的构造器并创建一个对象(ClassObjectReference
将在内存中保存所创建对象的引用)。
示例
package com.jbt;
public class HelloWorld {
int i; // Class Variable(State)
void method() {
System.out.println("Inside Method");
}// Method (Behavior)
}
对象创建
HelloWorld obj = new HelloWorld();
如何访问类成员
(ClassObjectReference.member
)。 您将通过命名对象,后跟句点(.
),后跟方法的名称及其参数列表来调用对象的方法。
objectName.methodName(arg1, arg2, arg3).
项目要点
- 一个类只能具有公开和默认访问权限。
- 公开类必须使用相同名称的 Java 文件。
- 一个 Java 文件可以包含多个非公开类,但只能具有一个公开类。
- 所有包中的所有类都可以看到公开类
- 具有默认访问权限的类只能由同一包中的类看到。
- 没有公开类的 Java 文件没有命名限制。
- 该类还可以具有
final
,abstract
和strictfp
非访问修饰符。 - 不能实例化抽象类。
- 最终类不能被子类化。
- 类不能是最终的和抽象的。
- 类可见性可以在 3 个参数中看到
- 如果一个类可以扩展另一个类?
- 一个类是否可以创建另一个类的实例?
- 一个类是否可以访问另一个类的方法和变量?
https://www.youtube.com/embed/VW72ezYj3d4?feature=oembed
Java 构造器
原文: https://javabeginnerstutorial.com/core-java-tutorial/constructors-in-java/
Java 中的构造器可以视为类中的方法。 但是构造器和方法之间有很大的区别。 可以根据目的,语法和调用来定义这些差异。
Java 构造器的目的(Vs 方法)
构造器只有一个目的,创建一个类的实例。 该实例包括内存分配和成员初始化(可选)。
相比之下,不能使用方法创建类的实例。
构造器的语法(Vs 方法)
/*
* Here Class name is ConstructorExample, So constructor name needs to be the same.
*/
public class ConstructorExample {
/*
* As below signature has the name as Class name and it doesn't contain any
* return value so it will be treated as Constructor of the class
*/
public ConstructorExample() {
System.out.println("Inside Constructor");
}
/*
* Below method will be invoked only when it is invoked implicitly.
* Method has return type along with Non Access Modifier
*/
static void method() {
System.out.println("This is in method");
}
}
构造器的语法不同于下面描述的方法。
- 构造器不能具有非访问修饰符,而方法可以。
- 构造器不能具有返回类型(包括
void
),而方法需要它。 - 构造器名称必须与类名称相同,而方法不受限制。
- 根据 Java 命名约定,方法名称应为驼峰式,而构造方法名称应以大写字母开头。
方法可以具有与类名称相同的名称。
构造器的调用(Vs 方法)
构造器和方法的调用方式有所不同。 构造器不能显式调用,在生成类的实例时将隐式调用构造器(使用new
关键字)
构造器调用示例
/*
* Here Class name is ConstructorExample, So constructor name needs to be the same.
*/
public class ConstructorExample {
/*
* As below signature has the name as Class name and it doesn't contain any
* return value so it will be treated as Constructor of the class
*/
public ConstructorExample() {
System.out.println("Inside Constructor");
}
public static void main(String args[])
{
ConstructorExample cls = new ConstructorExample();
}
}
//Output will be
//Inside Constructor
方法调用示例
/*
* Here Class name is ConstructorExample, So constructor name needs to be the same.
*/
public class ConstructorExample {
/*
* As below signature has the name as Class name and it doesn't contain any
* return value so it will be treated as Constructor of the class
*/
public ConstructorExample() {
System.out.println("Inside Constructor");
}
/*
* Below method will be invoked only when it is invoked implicitly.
*/
void method() {
System.out.println("This is in method");
}
public static void main(String args[]) {
ConstructorExample cls = new ConstructorExample();
/*
* Now method will be called explicitly as below. It will execute the
* code within method.
*/
cls.method();
}
}
//The output would be
Inside Constructor
This is in method
类中的构造器必须与给定的类具有相同的名称。 构造器的语法不包含返回类型,因为构造器从不返回值。 构造器还可以包括各种类型的参数。 使用new
运算符调用构造器时,类型必须与构造器定义中指定的类型匹配。
如果未提供显式构造器,则 Java 提供一个默认构造器,其中不带参数并且不执行任何特殊操作或初始化。 隐式默认构造器执行的唯一操作是使用super()
调用来调用超类构造器。
Java 构造器规则
- 构造器不能具有返回类型。
- 构造器必须与类具有相同的名称。
- 构造器无法标记为静态
- 构造器不能标记为抽象
- 构造器不能被覆盖。
- 构造器不能是最终的。
如果类定义了显式构造器,则它不再具有默认的构造器来设置对象的状态。 如果此类需要默认构造器(不带参数的构造器),则必须提供其实现。
如果在这种情况下未提供显式的默认构造器,则任何调用默认构造器的尝试都将是编译时错误。
构造器重载:
像方法一样,构造器也可以重载。 由于类中的所有构造器都具有与该类相同的名称,因此它们的签名通过其参数列表来区分。
可以使用this()
构造在类中实现构造器的本地链接。 构造器中的this()
调用使用同一类中的相应参数列表来调用其他构造器。 Java 要求任何this()
调用都必须在构造器中作为第一条语句发生。
构造器链接:
每个构造器中都包含一个隐式super()
调用,该构造器不包含this()
或显式super()
调用作为第一个调用语句。 super()
语句用于调用超类的构造器。
隐式super()
可以由显式super()
代替。 超级语句必须是构造器的第一条语句。 显式超类允许将参数值传递给其超类的构造器,并且必须具有匹配的参数类型。 子类的构造器中的super()
调用将基于调用的签名,导致超类中相关构造器的调用。 这称为构造器链接。
super()
或this()
构造:如果在构造器中使用,则它必须作为构造器中的第一条语句出现,并且只能在构造器声明中使用。 这意味着this()
和super()
调用不能同时出现在同一构造器中。 就像this()
构造导致同一类中的构造器链接一样, super()
构造也导致了子类构造器与超类构造器的链接。 如果构造器既没有this()
也没有super()
构造作为其第一条语句,则调用超类默认构造器的super()
被隐式插入。
如果一个类仅定义非默认构造器,则其子类将不包含隐式
super()
调用。 这将被标记为编译时错误。然后,子类必须使用带有正确参数的
super()
构造与超类的适当构造器进行匹配,来显式调用超类构造器。
备忘单
- 创建新的对象时,调用构造器。
- 构造器也可以重载,但不能将覆盖。
- 每个类至少有一个构造器。 如果用户不提供任何内容,则 JVM 将提供默认的无参数构造器。
- 抽象类也具有构造器。
- 构造器必须与该类具有相同名称。
- 构造器不能具有返回类型。
- 如果与类同名的方法具有返回类型,则将其视为普通成员方法,而不是构造器。
- 构造器可以具有任何访问修饰符(全部)。
- 默认构造器是一个无参构造器,它调用超类的无参构造器。 如果超类没有任何无参数构造器,则它将抛出运行时异常。
- 在类具有默认构造器的情况下,其超类需要具有无参数构造器。
- 构造器的第一个语句可以是
this
或super
,但不能同时使用。 - 如果编码器未编写任何
this()
或super()
调用,则编译器将添加super()
调用。 super()
用于从超类调用构造器。this()
用于从同一类调用构造器。- 实例成员仅在超类构造器运行后才能访问。
- 接口没有构造器。
- 构造器是未继承的。 因此,无法将覆盖。
- 构造器不能直接调用。 当创建新对象或由其他构造器调用新对象时,将被调用(隐式)。
https://www.youtube.com/embed/l0b2qWuty2E?start=1&feature=oembed
使用 Eclipse 编写 Hello World 程序
原文: https://javabeginnerstutorial.com/core-java-tutorial/write-hello-world-application-using-eclipse/
在本教程中,我们将学习使用 Eclipse IDE 创建 Hello World 应用。 要了解以下材料,您应该熟悉如何使用 Eclipse 来获得有关 Eclipse 的说明,请单击此处。
创建项目
要创建我们的项目,请从菜单中选择“文件 -> 新建 -> Java 项目”。 如果找不到“Java 项目”作为选项,请单击“其他”,如下所示。
从下一个窗口中选择 Java 项目,然后单击“下一步”。 提供一个项目名称,如下所示。
单击“完成”。 Eclipse 将要求您将透视图更改为 Java 视图。 单击是。 如您所见,一个新的 Java 项目将出现“Java 视图”的“包浏览器视图”中。
创建一个包
现在,我们将为我们的 Java 项目创建一个包。 包用于避免命名冲突,以便控制访问(访问修饰符)和捆绑相关类型的组。
要创建包,请在 Java 项目(JBTProject
)中选择src
文件夹,右键单击该文件夹并选择“新建 -> 包”。
在对话框中输入包名称,然后单击“完成”。
创建 Java 类的步骤
创建包后,我们可以在包内创建 Java 类。 右键单击要在其中创建 Java 类的包,然后选择“新建 -> 类”。
在对话框中提供类名称,然后单击完成。 其他选项可供选择。 选择其他选项以创建您的main
方法。
创建类之后,项目的结构将如下所示。
编译和运行 Java 应用
创建 Java 应用之后,下一步就是编译并运行代码。
编译代码
要编译代码,请使用键盘快捷键CTRL + B
。 它将构建 Java 应用。 如果要构建单个应用,请单击该应用,然后从菜单中选择“项目 -> 构建项目”。
运行代码
要运行代码,您可以使用键盘快捷键ALT + SHIFT + X
和J
。
或者,您可以单击文件,然后从菜单中选择“运行 -> 运行为 -> Java 应用”。
单击“运行”后,将执行 Hello World 应用,并且您的输出将显示在“控制台视图”中。
现在我们已经学会了在 Eclipse 中创建 Hello World 应用。 在下一篇文章中,我们将讨论 Eclipse 提供的一些其他功能,这些功能使编码更容易。
https://www.youtube.com/embed/79l5QSuI4ko?feature=oembed
执行顺序
原文: https://javabeginnerstutorial.com/core-java-tutorial/order-of-execution-of-blocks-in-java/
在本文中,我们将学习 Java 中块执行的顺序。
Java 类中的不同块及其执行顺序
- 静态块
- 初始化块(匿名块)
- 构造器
/*
* Here we will learn to see how the different part (Ananymous Block, Constructor and Static Block ) of class will behave
* and what would be the order of execution.
*/
class JBTCLass {
/*
* Here Creating the Ananymous Block
*/
{
System.out.println("Inside Ananymous Block");
}
/*
* Now Creating the Static Block in Class
*/
static {
System.out.println("Inside Static Block");
}
/*
* Here Creating the Constructor of Class
*/
JBTCLass() {
System.out.println("Inside Constructor of Class");
}
public static void main(String[] args) {
// Creating the Object of the Class
JBTCLass obj = new JBTCLass();
System.out.println("*******************");
// Again Creating Object of Class
JBTCLass obj1 = new JBTCLass();
}
}
上面程序的输出
Inside Static Block
Inside Ananymous Block
Inside COnstructor of Class
*******************
Inside Ananymous Block
Inside COnstructor of Class
如您所见,STATIC
块仅在加载类时执行一次。
但是,每当创建一个类的对象时,匿名块和构造器就会运行。 初始化块将首先执行,然后构造器。
Java 中的访问修饰符
原文: https://javabeginnerstutorial.com/core-java-tutorial/access-modifier-in-java/
Java 中的访问修饰符可帮助您设置所需的类,变量和方法的访问级别。
有三个访问修饰符。 不包括默认访问修饰符。 默认值为访问控制,当未指定任何访问修饰符时将设置访问控制。
访问控制
public
private
protected
- 默认
访问修饰符(某些或全部)可以应用于类,变量,方法。
类的访问修饰符
Java 中的类只能使用public
和默认访问修饰符。
public
设置为public
时,java 世界中所有可用的类都可以访问给定的类。
默认
当设置为默认值时,给定的类将可供同一包中定义的类访问。
Java 类的访问修饰符表
可见性 | 公开访问修饰符 | 默认访问修饰符 |
---|---|---|
位于同一包中 | Yes | Yes |
来自同一个包 | Yes | NO |
实例和静态变量的访问修饰符
变量适用于所有下面提到的修饰符。
public
private
protected
- 默认
在检查在该类内部定义的变量的可见性之前,应检查该类的可见性。
如果该类是可见的,则在该类内部定义的变量将是可见的。
如果看不到类,则即使将其设置为
public
,也将无法访问任何变量。
默认
如果将变量设置为默认值,则同一包中定义的类将可以访问该变量。 在同一包中的类中定义的任何方法都可以访问变量或继承或直接访问。
public
如果将变量设置为公开变量,则可以从 Java 世界中可用的任何类中进行访问。 任何类中的任何方法都可以访问给定变量或继承或直接访问。
protected
如果将变量在类内设置为受保护的,则只能通过继承从相同或不同包中定义的子类中对其进行访问。
保护和默认之间的唯一区别是受保护的访问修饰符遵守类-子类关系,而默认则不。
private
只能从定义变量的类内部访问定义的变量private
。 此类变量不能从已定义的类外部访问,甚至不能在其子类中访问。
Java 变量的访问修饰符表
可见性 | 公共访问修饰符 | 私有访问修饰符 | 受保护的访问修饰符 | 默认访问修饰符 |
---|---|---|---|---|
在同一个类中 | Yes | Yes | Yes | Yes |
来自同一包装中的任何类 | Yes | No | Yes | Yes |
来自同一包中的任何子类 | Yes | No | Yes | Yes |
来自不同包的任何子类 | Yes | No | Yes(仅通过继承) | No |
来自不同包装中的任何非子类 | Yes | No | No | No |
方法的访问修饰符
方法适用于以下所有修饰符。
默认
当“方法”设置为默认值时,同一包中定义的类将可以访问该方法。 在同一包中定义的任何类中的任何方法都可以访问给定的方法或继承或直接访问。
public
将方法设置为public
时,可以从 Java 世界中可用的任何类中访问它。 任何类中的任何方法都可以访问或继承或直接访问的给定方法,具体取决于类级别的访问。
protected
如果将方法设置为在类内部进行保护,则可以从在相同或不同包中定义的子类访问该方法。
受保护的访问权限和默认访问权限之间的唯一区别是受保护的访问修饰符遵守的类-子类关系,而默认的则不这样做。
private
只能从定义其的类内部访问定义为私有的方法。 此类方法无法从已定义的类外部访问,甚至不能从其子类访问。
Java 方法的访问修饰符表
可见性 | 公共访问修饰符 | 私有访问修饰符 | 受保护访问修饰符 | 默认访问修饰符 |
---|---|---|---|---|
相同类中 | Yes | Yes | Yes | Yes |
来自同一包中的任何类 | Yes | No | Yes | Yes |
来自同一包中的任何子类 | Yes | No | Yes | Yes |
来自不同包的任何子类 | Yes | No | Yes(仅通过继承) | No |
来自不同包装中的任何非子类 | Yes | No | No | No |
局部变量的访问修饰符
无法将访问修饰符应用于局部变量。 只能将final
应用于局部变量,该局部变量是非访问修饰符。
继承或直接访问之间的区别
下面说明了继承和直接访问之间的区别。
超类
package com.jbt;
public class FirstClass {
public int i;
protected int j;
private int k;
int l;
}
同一包中的子类
package com.jbt;
class SecondClass extends FirstClass {
void method() {
System.out.println(i);
System.out.println(j); // Respect the parent child relationship irrespective of package
System.out.println(k); // Compilation Error
System.out.println(l); // Accessible as it is in same package
FirstClass cls = new FirstClass();
System.out.println(cls.i);
System.out.println(cls.j);
System.out.println(cls.l);
// Private variable will not be accessible here also.
System.out.println(cls.k); // Compilation error
}
}
不同包中的子类
package com.jbt.newpackage;
import com.jbt.FirstClass;
class SecondClass extends FirstClass {
void method() {
// Access through inheritance
System.out.println(i);
System.out.println(j); // Respect the parent child relationship irrespective of package
System.out.println(k); // Compilation error - private variable
System.out.println(l); // Compilation Error - not accessible as it is in diff package
FirstClass cls = new FirstClass();
System.out.println(cls.i); // Accessible because it is public
System.out.println(cls.j); // Compilation error
// Private variable will not be accessible here also.
System.out.println(cls.k); // Compilation error
System.out.println(cls.l); // Compilation error
}
}
备忘单
public
,private
,protected
是 3 种访问修饰符- Java 有 4 个访问级别。 公开,私有,受保护 & 默认。
- 一个类只能具有公开和默认访问级别。
- 方法和实例变量(非本地)可以使用所有 4 个访问级别。
- 如果某个类对其他类不可见,则即使该成员的访问级别是公开的,也没有访问该类成员的问题(重要)。
- 在成员可见之前,应检查类可见性。
- 如果超类具有公开成员,则子类将继承它,即使它在其他包中也是如此。
this
始终引用当前正在执行的对象。- 其他成员甚至可以从其他包中访问公开成员。
- 只能通过同一类中的代码访问私有成员。
- 默认成员对包外部的子类不可见。
- 即使在不同的包中,受保护的成员也可以被子类看到。
- 受保护与默认之间的不同之处在于,默认仅在包之外的子类的情况下才会显示。
- 局部变量不能具有访问修饰符。
- 局部变量只能具有可以应用的最终非访问修饰符。
https://www.youtube.com/embed/WZRTwkCOYBQ?feature=oembed
Java 中的非访问修饰符
原文: https://javabeginnerstutorial.com/core-java-tutorial/non-access-modifiers-in-java/
在本文中,您将学习非访问修饰符。 以下是可用的 Java 非访问修饰符。
final
abstract
static
strictfp
native
syncronized
transient
final
非访问修饰符
适用于 :
最终类:
一个类别设置为final
时,不能由任何其他类别扩展。
示例:java.lang
包中的字符串类
最终方法:
一个方法设置为final
时,不能被任何子类覆盖。
最终变量:
当变量设置为final
时,其值不能更改。 最终变量就像常量。
示例:public static final int i = 10;
abstract
非访问修饰符
关键词:
abstract
适用于:
- 类
- 方法
抽象类:
抽象类可以具有抽象方法。 如果类具有抽象方法,则该类将成为抽象类。 一个类也可以是没有任何抽象方法的抽象类。
抽象方法:
抽象方法是没有主体而只有签名的那些方法。
示例:public abstract void method();
syncronized
非访问修饰符
适用于
- 方法
同步方法
同步方法一次只能由一个线程访问。
native
非访问修饰符
适用于
- 方法
本机方法
本机方法表明该方法是在依赖于平台的代码上实现的。
Strictfp
非访问权限编辑
适用于
- 类
- 方法
Strictfp
类/方法
Strictfp
非访问修饰符强制浮点或浮点操作遵守 IEEE 754 标准。
注意:Strictfp
非访问修饰符无法应用于变量。
备忘单
- 非访问修饰符是
static
,final
,abstract
,syncronized
和volatile
。 static
关键字可以应用于变量和方法。- 静态变量是那些不与任何实例关联但与类关联的变量,这意味着所有实例都将访问该变量的同一单个副本。
- 局部变量不能声明为
static
。 static
关键字也可以应用于方法。 它们将适用于所有实例,并且它们将不依赖于实例创建。- 最终修饰符可以应用于方法和变量。
Final
是唯一可用于局部变量的修饰符。- 一旦声明为
final
,则变量的值无法更改。 - 赋值后,最终变量不会获得默认值,而实例变量值无法更改。
- 最终方法不能被覆盖。
Java 中的数据类型
原文: https://javabeginnerstutorial.com/core-java-tutorial/data-types-in-java/
Java 是一种静态类型的语言,这意味着必须先声明所有变量,然后才能使用它们。 这意味着变量的名称和类型必须先定义,然后才能在代码中使用。
boolean bool = true;
通过编写上面的行,我们告诉程序创建了一个名为boolean
的布尔类型的变量,其默认值为true
。
Java 有 8 种原始数据类型。
不同的原始数据类型
Java 支持以下提到的原始数据类型。
原始类型 | 默认值 | 包装类 | |
---|---|---|---|
boolean |
false |
Boolean |
|
char |
\u0000 |
Char |
|
整数 | byte |
(byte)0 |
Byte |
short |
(short)0 |
Short |
|
int |
0 |
Integer |
|
long |
0l |
Long |
|
浮点 | float |
0.0 |
Float |
double |
0.0d |
Double |
boolean
布尔数据类型用boolean
关键字声明。 它只有两个可能的值:true
和false
。 此数据类型可用于简单标记目的,以跟踪真/假条件。
char
char
数据类型是单个 16 位 Unicode 字符。 最小值为'\u0000'
(或 0),最大值为'\uffff'
(或 65,535(含))。
byte
字节数据类型用byte
关键字声明。 它的最小值可以为 -128,最大值可以为 127(含)。 默认值为 0。如果假定变量在给定范围内包含较小的值,则可以使用字节数据类型代替int
。 它有两个目的
- 以字节节省空间是 8 位有符号整数,比
int
小 4 倍 - 它们的限制有助于澄清您的代码,变量范围有限的事实可以作为文档的一种形式。
short
short
数据类型可以使用short
关键字声明。 short
数据类型是 16 位带符号的二进制补码整数。 其值介于 -32,768 到 32,767(含)之间。 它的默认值为 0。当变量的值介于范围之间时,可以使用short
数据类型代替int
。 如上所述,它将用于两个目的。
int
默认情况下,int
数据类型是 32 位带符号的(+/-
)二进制补码整数,最小值为-2^31
,最大值为2^31-1
。 在 Java SE 8 和更高版本中,您可以使用int
数据类型表示无符号(+
)32 位整数,其最小值为 0,最大值为2^32-1
。
请注意,即使在 Java 8 中,int
也是带符号的。 但是有些方法将它们视为无符号,但是无法在 Java 8 或更高版本中声明Unsigned int
。
尝试打印以下代码以获得int
支持的最小值和最大值。 这些值可以分配给变量。
Inetegr.MAX_VALUE
Inetegr.MIN_VALUE
long
long
数据类型是 64 位二进制补码整数。 有符号长号的最小值为-2^63
,最大值为2^63-1
。 用简单的方式,您可以说它是int
的增强版本。 当您需要的值范围比int
提供的值宽时,应使用此数据类型。
在 Java 8 及更高版本中,您可以使用long
数据类型表示无符号的 64 位长,其最小值为 0,最大值为2^64-1
。
请注意,即使在 Java 8 中,long
也是带符号的。 但是,有些方法将它们视为无符号。 compareUnsigned
和divideUnsigned
是Long
包装器类中的一些方法,支持无符号long
的算术运算。 有关Unsigned Int
或Long
的更多详细信息,可以在这里找到。
float
float
数据类型是单精度(32 位)IEEE 754 浮点。 当您需要小数部分但不需要很高的精度时,float
类型的变量很有用。 在单精度中,小数部分使用 23 位。 此数据类型可用于常规的日常计算。
double
double
数据类型是双精度(64 位)IEEE 754 浮点数。 它是浮点数,精度更高。 在双精度中,小数部分使用 52 位。 由于双精度比单精度需要更多的内存,因此不建议将其用于正常计算。 它主要用于科学计算。
Java 中的算术运算符
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-arithmetic-operator/
在本节中,我们将学习算术,运算符,优先级和运算符关联。
运算符优先级
优先级决定在同一计算中存在多个运算符的情况下,首先求值哪个运算符。
运算符优先级表
运算符 | 优先级(从高到低) |
---|---|
后缀 | expr++ expr-- |
一元 | ++expr --expr +expr –expr ~ ! |
乘法 | * / % |
加法 | + – |
移位 | << >> >>> |
关系 | < > <= >= instanceof |
相等 | == != |
按位与 | & |
按位异或 | ^ |
按位或 | | |
逻辑与 | && |
逻辑或 | || |
三元 | ?: |
赋值 | = += -= *= /= %= &= ^= |= <<= >>= >>>= |
优先级示例
/*
* Here we will see the effect of precedence in operators life
*/
class OperatorPrecedenceExample {
public static void main(String args[]) {
int i = 40;
int j = 80;
int k = 40;
int l = i + j / k;
/*
* In above calculation we are not using any bracket. So which operator
* will be evaluated first is decided by Precedence. As precedence of
* divison(/) is higher then plus(+) as per above table so divison will
* be evaluated first and then plus.
*
* So the output will be 42.
*/
System.out.println("value of L :" + l);
int m = (i + j) / k;
/*
* In above calculation brackets are used so precedence will not come in
* picture and plus(+) will be evaluated first and then divison()/. So
* output will be 3
*/
System.out.println("Value of M:" + m);
}
}
运算符关联性
如果两个运算符在计算中具有相同的优先级,则将使用运算符的关联性来决定首先执行哪个运算符。
关联性示例
package jbt.bean;
/*
* Here we will see the effect of precedence in operators life
*/
public class OperatorAssociativityExample {
public static void main(String args[]) {
int i = 40;
int j = 80;
int k = 40;
int l = i / k * 2 + j;
/*
* In above calculation we are not using any bracket. And there are two
* operator of same precedence(divion and multiplication) so which
* operator(/ or *) will be evaluated first is decided by association.
* Associativity of * & / is left to right. So divison will be evaluated
* first then multiplication.
*
* So the output will be 82.
*/
System.out.println("value of L :" + l);
int m = i / (k * 2) + j;
/*
* In above calculation brackets are used so associativity will not come
* in picture and multiply(*) will be evaluated first and then
* divison()/. So output will be 80
*/
System.out.println("Value of M:" + m);
}
}
Java 中的运算符
让我们分别讨论每个运算符。
赋值(=
)和算术运算符(+, -, *, /
)的工作方式与其他编程语言相同,因此在此不再赘述。 /
和*
运算符的优先级高于加法(+
)或减法(-
)或取模(%
)
Java 语句初学者教程
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-statements-tutorial-for-beginners/
在这里,我们将学习 Java 语句。
Java 语句的类型
- 表达式语句
- 控制语句
- 赋值语句
在本节中,我们将讨论控制语句。
控制语句
- 条件执行
- 循环控制
- 流控制语句
条件执行的类型
If
语句If-Else
语句If-Else-if
语句switch
语句
if
语句
Java 中的if
语句包含仅在应用条件为true
的情况下才执行的部分代码。 if
语句仅接受布尔表达式作为条件。
与其他语言不同,Java 不接受数字作为条件运算符。 它仅将布尔表达式视为返回
TRUE
或FALSE
的条件。
If
语句的语法
if (true)
System.out.println("Hello! This will always get printed");
if (Boolean Expression) {
Code block to get executed
}
If
语句的示例代码
public class if_condition {
public static void main(String[] args) {
String string = "Hello";
if ("Hello".equals(string)) {
System.out.println("String has the Value Hello");
}
if ("Hi".equalsIgnoreCase(string)) {
System.out.println("String has the value Hi");
}
}
}
如果运行上面的代码,它将显示“字符串具有值Hello
”。
else
语句
if-else if
语句含有多个if
条件和else
语句。如果条件为真,包含的代码会被执行,如果条件为假,则将检查下一个条件。 如果下一个if
条件为true
,则将执行包含的代码,否则将执行代码的else
部分。 if else if
语句仅采用布尔表达式作为有效条件。
与其他语言不同,java 不接受数字作为条件运算符。 它仅将布尔表达式视为返回
TRUE
或FALSE
的条件。
例如,if(1 == 1)
被接受,因为它是布尔表达式,将返回true
。if(x = 1)
语句在 Java 中是不允许的。
if-else
语句的语法
if (1==2)
System.out.println("Hello! This will not get printed");
else
System.out.println("Hello! This will get printed");
if (Boolean Expression) {
Code block to get executed
}
else{
code block to get executed when above condition is false
}
If Else
语句的示例代码
public class if_condition {
public static void main(String[] args) {
String string = "Hello";
if ("Hello".equals(string)) {
System.out.println("String has the Value Hello");
} else {
System.out.println("String is not Hi");
}
if ("HI".equals(string)) {
System.out.println("String has the Value Hi");
} else {
System.out.println("String is not Hi");
}
}
}
当您运行上面的代码时,它将打印出来。
String has the Value Hello
String is not Hi
if-else if
语句
if-else if
语句由多个if
条件和else
语句组成。 如果条件为真,则将执行随附的代码。 如果if
条件为假,则它将检查下一个if
条件。如果下一个条件为真,则将执行附带的代码,否则将执行代码的else
部分。 If Else If
语句仅将布尔表达式视为有效条件。
与其他语言不同,Java 不接受数字作为条件运算符。 它仅将布尔表达式视为返回
TRUE
或FALSE
的条件。例如
if(x = 1)
不允许,而if(1 == 1)
被接受,因为它是布尔表达式,并将返回true
。
if-else if
语句语法
if (1==2)
System.out.println("Hello! This will not get printed");
else if(1==1)
System.out.println("Hello! This will get printed");
else
System.out.println("This will get executed when Above conditio is FALSE");
if (Boolean Expression) {
Code block to get executed
}
else{
code block to get executed when above condition is false
}
Java 中If Else If
语句的示例代码
public class if_condition {
public static void main(String[] args) {
String string = "Hello";
if ("HI".equals(string)) {
System.out.println("String has the Value Hi");
} else if ("Hello".equals(string)) {
System.out.println("String is not Hi");
}
string = "NotHello";
if ("Hello".equals(string)) {
System.out.println("String has the Value Hello");
} else if ("Hi".equalsIgnoreCase(string)) {
System.out.println("String is not Hi");
} else {
System.out.println("String is neither Hello nor Hi");
}
}
}
当您运行上述代码时,它将打印。
String is not Hi
String is neither Hello nor Hi
循环语句的类型
for
循环While
循环do–While
循环
流控制语句的类型
return
语句continue
语句break
语句
用 Java 创建对象的不同方法
原文: https://javabeginnerstutorial.com/core-java-tutorial/different-ways-to-create-an-object-in-java/
您必须知道使用new
关键字创建类的对象,但这不是创建对象的唯一方法。
还有其他几种创建类对象的方法:
- 使用
new
关键字 - 使用新实例(反射)
- 使用克隆
- 使用反序列化
- 使用
ClassLoader
- …不知道了 🙂
使用new
关键字
使用new
关键字是创建对象的最基本方法。 new
关键字可用于创建类的对象。
public class ObjectCreationExample {
public static void main(String[] args) {
// Here we are creating Object of JBT using new keyword
JBT obj = new JBT();
}
}
class JBT{
String Owner;
}
使用newInstance
(反射)
您是否曾经尝试使用 Java 中的 JDBC 驱动程序连接到数据库? 如果您的回答是肯定的,那么您必须已经看到“Class.forName
”。 我们还可以使用它来创建类的对象。 Class.forName
实际上是在 Java 中加载该类,但未创建任何对象。 要创建对象,必须使用Class
类的newInstance
方法。
/*
* Here we will learn to create Object of a class without using new Operator.
* But newInstance method of Class class.
*/
class CreateObject {
public static void main(String[] args) {
try {
Class cls = Class.forName("JBTClass");
JBTClass obj = (JBTClass) cls.newInstance();
JBTClass obj1 = (JBTClass) cls.newInstance();
System.out.println(obj);
System.out.println(obj1);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
class JBTClass {
static int j = 10;
JBTClass() {
i = j++;
}
int i;
@Override
public String toString() {
return "Value of i :" + i;
}
}
如果要以这种方式创建对象,则类需要具有公开的默认构造器。
使用Clone
我们还可以使用Clone()
方法创建现有对象的副本。
/*
* Here we will learn to create an Object of a class without using new Operator.
* For this purpose we will use Clone Interface
*/
class CreateObjectWithClone {
public static void main(String[] args) {
JBTClassClone obj1 = new JBTClassClone();
System.out.println(obj1);
try {
JBTClassClone obj2 = (JBTClassClone) obj1.clone();
System.out.println(obj2);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
class JBTClassClone implements Cloneable {
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
int i;
static int j = 10;
JBTClassClone() {
i = j++;
}
@Override
public String toString() {
return "Value of i :" + i;
}
}
关于克隆的其他说明
- 在这里,我们正在创建现有对象而不是任何新对象的克隆。
- 克隆方法在
Object
类中声明为受保护的。 因此,只能在子类或同一包中对其进行访问。 这就是为什么它在类上被覆盖的原因。 - 类需要实现
Cloneable
接口,否则它将引发CloneNotSupportedException
。
使用对象反序列化
对象反序列化也可以用于创建对象。 它产生与序列化对象相反的操作。
使用ClassLoader
我们也可以使用ClassLoader
创建类的对象。 这种方式与Class.forName
选项相同。
/*
* Here we will learn to Create an Object using Class Loader
*/
public class CreateObjectWithClassLoader {
public static void main(String[] args) {
JBTClassLoader obj = null;
try {
obj = (JBTClassLoader) new CreateObjectWithClassLoader().getClass()
.getClassLoader().loadClass("JBTClassLoader").newInstance();
// Fully qualified classname should be used.
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(obj);
}
}
class JBTClassLoader {
static int j = 10;
JBTClassLoader() {
i = j++;
}
int i;
@Override
public String toString() {
return "Value of i :" + i;
}
}
下一篇文章
内部类
原文: https://javabeginnerstutorial.com/core-java-tutorial/inner-class/
Java 内部类是类中的类。 内部类实例与外部类具有特殊关系。 这种特殊的关系使内部类可以访问外部类的成员,就好像它们是外部类的一部分一样。
注意:Java 内部类实例可以访问外部类的所有成员(公开,私有和受保护)
创建内部类的语法
//outer class
class OuterClass {
//inner class
class InnerClass {
}
}
内部类的类型
- 静态
- 方法局部
- 匿名
- 除了这些正常的内部类之外
普通内部类
非方法局部,静态方法或匿名方法的内部类是普通内部类。
//outer class
class OuterClass {
//inner class
class InnerClass {
}
}
如果编译以上代码,它将生成两个类文件。
outer.class
inner$outer.class
注意:您无法使用java
命令直接执行内部类的.class
文件。
由于它不是静态内部类,因此我们不能对其使用static
关键字。
如何访问内部类
内部类只能通过外部类的活动实例进行访问。
通过外部类
外部类可以以与普通类成员相同的方式创建内部类的实例。
class OuterClass {
private int i = 9;
// Creating instance of inner class and calling inner class function
public void createInner() {
InnerClass i1 = new InnerClass();
i1.getValue();
}
// inner class declarataion
class InnerClass {
public void getValue() {
// accessing private variable from outer class
System.out.println("value of i -" + i);
}
}
}
从外部类外面
创建一个外部类实例,然后创建内部类实例。
class MainClass {
public static void main(String[] args) {
// Creating outer class instance
OuterClass outerclass = new OuterClass();
// Creating inner class instance
OuterClass.InnerClass innerclass = outerclass.new InnerClass();
// Classing inner class method
innerclass.getValue();
}
}
上面的代码也可以替换为
OuterClass.InnerClass innerClass = new OuterClass.new InnerClass();
this
关键字
有一些与相关的规则,它引用当前正在执行的对象。 因此,在内部类中,“this
”关键字将引用当前正在执行的内部类对象。 但是要获取外部类的代码,请使用“OuterClass.this
”。
应用的修饰符
普通内部类将被视为外部类的成员,因此与类相反,它可以具有多个修饰符。
final
abstract
public
private
protected
strictfp
注意:不要与“类”和“内部类”的修饰符混淆。 他们是完全不同的。
方法局部内部类
当在外部类的方法内部定义内部类时,它将成为方法局部内部类。
用于创建方法局部内部类的语法
class OuterClass {
private int i = 9;
// Creating instance of inner class and calling inner class function
public void innerMethod() {
// inner class declarataion inside method
class InnerClass {
public void getValue() {
// accessing private variable from outer class
System.out.println("value of i -" + i);
}
}
//inner class instance creation
InnerClass i1 = new InnerClass();
i1.getValue();
}
}
现在,内部类的定义位于外部类的方法内部。 仍然可以创建外部类的实例,但是只能在定义内部类之后才能创建,如上面所示。
注意:
- 方法局部内部类可以在定义它的方法内实例化,而不能在其他地方实例化。
- 方法本地内部类不能使用在其 ID 定义的方法中定义的变量,但仍可以使用实例变量。
- 如果方法局部变量是“最终的”,则方法局部内部类可以使用它。 (现在变量是最终的)
应用于方法局部内部类的修饰符
方法局部内部类可以使用修饰符(例如局部变量),因此方法局部内部类可以具有final
或abstract
。
备忘单
- 内部类是包含在类中的成员。
- 外部类引用需要启动内部类。
- 内部类有 4 种类型。
- 在方法内定义的内部类是方法局部内部类。
- 方法局部内部类不能访问方法局部变量。
final
和abstract
是可用于方法内部内部类的唯一修饰符。- 匿名内部类没有任何名称。
- 具有静态修饰符的内部类称为静态内部类。
- 静态嵌套类无法访问外部类的非静态成员。
参考文献
字符串构建器
原文: https://javabeginnerstutorial.com/core-java-tutorial/string-builder/
字符串构建器类似于字符串,但是可以修改。 字符串的缺点是,一旦创建,就无法对其进行修改。 为了克服这个问题,可以使用字符串缓冲区和字符串构建器。
字符串缓冲区和字符串构建器之间的区别
“StringBuffer
”可安全用于多个线程(线程安全)。 方法是同步的。 字符串构建器不是线程安全的。
在可能的情况下,建议使用StringBuilder
优先于StringBuffer
,因为在大多数情况下它将更快。
字符串缓冲区的重要方法
append()
insert()
length()
备忘单
StringBuilder
和StringBuffer
之间的区别在于StringBuilder
方法未同步。- 字符串构建器是可变的,与不可变的字符串相反。
StringBuilder
的equals
方法不会被覆盖,因此不会比较StringBuilder
的值。 就像使用String
一样。
Java 字符串教程
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-string-tutorial/
字符串是 Java 中大量使用的那些对象之一。 这就是String
在 Java(字符串池)中具有唯一处理的原因。
String
类表示字符串。- Java 程序中的所有字符串字面值(例如
"abc"
)都实现为此类的实例。 - 字符串就像常量,一旦创建,其值就无法更改。
- 字符串对象是不可变的,因此可以共享。
- 如果要执行很多字符串操作,则可以使用字符串缓冲区和字符串生成器代替字符串。
字符串创建
您可以通过两种方式在 Java 中创建字符串。 两者看起来相同,但是两种方法之间存在差异。
String jbt = new String("JBT");
String jbt = "JBT";
字符串的重要方面
- 不可变
重要方法
internal()
length()
toString()
trim()
substring()
字符串存储
如您所知,java 中除原语外的所有内容都是对象。 字符串也是如此。 字符串也是对象,因此它仅驻留在堆上。 但是还有另一个术语用于字符串存储,即字符串池/字符串字面值池。 大多数时候,人们将其视为字符串对象的独立池(对我来说也是如此)。 但是这是错误的。 字符串池/字符串字面值池不过是对String
对象的引用的集合。 我们知道String
对象是不可变的,因此最好将“String
”对象与多个引用“共享”。 (反之亦然,因为字符串对象在不同的引用之间共享,这就是字符串对象不可变的原因。)
字符串字面值和对象
字符串以以下两种格式创建,它们在很多方面彼此不同。
- 字符串字面值
- 使用
new
关键字
因此,当我们说字符串字面值时,实际上是指字符串池中的引用,而当我们说字符串对象(使用new
关键字)时,我们直接是指堆中的字符串对象。 但是在两种情况下,我们仅指的是堆中的对象(直接/间接)。
如何创建
现在的问题出现了如何创建字符串字面值和字符串对象。 众所周知,我们可以通过两种方式创建字符串对象。
String str1 = "abc";
使用new
关键字
String str=new String("javabeginnerstutorial");
当我们使用new
运算符创建String
时,实际上是像第一个选项那样在堆中创建String
对象。 在这里,我们在堆内存中创建一个字符串对象,其值为"abc"
。 和一个名为“str
”的引用。
还请注意,当我们使用""
(字面值)创建此String
对象时,还将创建字面值。 总共将有 1 个字符串对象 1 个字符串字面值并将创建引用。 但是引用将仅引用堆中的String
对象,而不引用字符串池中的字面值。
当我们使用""
时,我们正在创建字面值(""
中的所有内容均视为字面值)。 在这种情况下(第二种情况),JVM 将在字符串池中查找所有引用,并检查其中是否有指向值"abc"
的堆中的字符串对象,如果找到,则将返回该对象的引用。 否则,JVM 将在堆中创建一个新的字符串对象,并在字符串池中对其进行内化(使用inter()
方法)(此对象的引用将添加到字符串池中),以供以后引用。 并且将返回新创建对象的引用。 现在,如果我们再次编写相同的代码来创建String
字面值。
字符串字面值
String str2="javabeginnerstutorial";
这些类型的字符串是在堆中创建的,如果String
常量池中的对象具有相同的值,则堆中的对象将被引用为String
常量池中的该对象。 引用变量"str2"
将指向堆中的对象。
字符串常量池是一个存储唯一字符串对象的池,从而增强了不变性,即,即使您对先前创建的字符串进行连接,也将在堆和常量池中同时创建一个新对象(前提是没有相似之处) 之前存在的对象),然后将该对象相应地返回给程序。
字符串垃圾回收
您可能已经听说过String
字面值永远不适合进行垃圾回收。 注意,我们提到的是字符串字面值,而不是字符串对象。 它们是有区别的。 当不再从应用的活动部分引用对象时,该对象就有资格进行垃圾回收。 字符串字面值总是从字符串字面值池中对其进行引用。 这意味着它们始终引用它们,因此不符合垃圾回收的条件。 但是可能有些String
对象没有任何引用,那么它们就有资格进行垃圾回收。
备忘单
- 字符串对象用于保存文本字符串。
java.lang
包的一部分。- 字符串可以使用连接运算符(
+
)添加。 - 字符使用单引号(
'
)作为定界符,而字符串使用双引号("
)作为定界符。 - 字符串对象是不可变的。 字符串对象的值一经分配就无法更改。
- 字符串类是最终的方法,它不能覆盖字符串类的方法。
- 所有字符串字面值由 JVM 添加到字符串池。
- 字符串具有方法
length()
,而数组具有属性length
检查长度。 - 为了克服
String
的不可变属性,StringBuffer
和StringBuilder
出现在图片中。
Java 教程 – 变量
Java 中的变量
原文: https://javabeginnerstutorial.com/core-java-tutorial/variables-in-java/
介绍
在 Java 中,对象将其状态存储在变量中。 在应用的生命周期中,变量用作容器来保存值(int
,long
,string
…)。
变量定义
要定义变量,我们需要为该变量分配数据类型。 数据类型定义了此变量可以保存的值的类型(int
,long
或String
等)。
变量定义的示例
public final int var ;
public : Access Modifier applied to variable
final : Non Access Modifier applied to this variable
int : Datatype. It defines kind of value this variable can hold (int in this case)
var : Name of the variable
变量初始化
现在我们已经完成了定义变量的操作,我们可以通过为其分配值来初始化上述变量。 在这种情况下,我们为变量分配一个整数值。
public final int var = 9;
Java 中的变量类型
Java 中的变量可以在代码中的任何位置定义(在类旁,在方法旁或作为方法参数),并且可以具有不同的修饰符。 根据这些条件,Java 中的变量可以分为四类。
- 实例变量
- 静态变量
- 局部变量
- 方法参数
实例变量(非静态字段)
对象使用实例变量存储其状态。 在没有方法声明的情况下未使用STATIC
关键字定义的变量是特定于对象的,称为实例变量。 这样的变量称为实例变量,因为它们的值是实例特定的,而不是是实例之间共享的。 有关更多详细信息,请转到实例变量主题。
类变量(静态字段)
在类(在任何方法之外)中用STATIC
关键字声明的变量称为类变量/静态变量。 它们被称为类级别变量,因为这些变量的值并非特定于任何实例,而是类的所有实例所共有。 此类变量将由对象的所有实例共享。 对于更多
局部变量(方法局部)
当在方法内部声明变量时,称为方法局部变量。 局部变量的范围仅在方法内部,这意味着不能在该方法外部访问局部变量。 访问修饰符有一些限制可以应用于局部变量。 要了解有关访问修饰符的更多信息,请点击这里。 有关更多详细信息,请参见局部变量。
参数
Java 方法参数和参数是在方法中传递的变量。 例如,main
方法中的String args[]
变量是一个参数。
package com.jbt;
/*
* Here we will discuss about different type of Variables available in Java
*/
public class VariablesInJava {
public static void main(String args[]) {
System.out.println("Hello");
}
}
https://www.youtube.com/embed/SFreQogjXH0?feature=oembed
Java 中的局部变量
原文: https://javabeginnerstutorial.com/core-java-tutorial/local-variable-in-java/
让我们进一步了解 Java 中的局部变量。 在 Java 程序的方法中声明的变量称为局部变量。
局部变量规则
局部变量示例
package com.jbt;
/*
* Here we will discuss about different type of Variables available in Java
*/
public class VariablesInJava {
/*
* Below variable is INSTANCE VARIABLE as it is outside any method and it is
* not using STATIC modifier with it. It is using default access modifier.
* To know more about ACCESS MODIFIER visit appropriate section
*/
int instanceField;
/*
* Below variable is STATIC variable as it is outside any method and it is
* using STATIC modifier with it. It is using default access modifier. To
* know more about ACCESS MODIFIER visit appropriate section
*/
static String staticField;
public void method() {
/*
* Below variable is LOCAL VARIABLE as it is defined inside method in
* class. Only modifier that can be applied on local variable is FINAL.
* To know more about access and non access modifier visit appropriate
* section.
*
* Note* : Local variable needs to initialize before they can be used.
* Which is not true for Static or Instance variable.
*/
final String localVariable = "Initial Value";
System.out.println(localVariable);
}
public static void main(String args[]) {
VariablesInJava obj = new VariablesInJava();
/*
* Instance variable can only be accessed by Object of the class only as below.
*/
System.out.println(obj.instanceField);
/*
* Static field can be accessed in two way.
* 1- Via Object of the class
* 2- Via CLASS name
*/
System.out.println(obj.staticField);
System.out.println(VariablesInJava.staticField);
}
}
public class UserController {
// Instance variable
private String outsideVariable;
public void setLength()
{
//Local variable
String localVariable = "0";
// In order to use below line local variable needs to be initialzed
System.out.println("Value of the localVariable is-"+localVariable);
}
}
命名约定
命名局部变量没有特定的规则。 所有变量规则都适用于局部变量。
下面提到的是命名局部变量的规则。
- 变量名称区分大小写。
- 局部变量的长度没有限制。
- 如果变量名只有一个单词,则所有字符均应小写。
重点
- 局部变量不能使用任何访问级别,因为它们仅存在于方法内部。
Final
是唯一可以应用于局部变量的非访问修饰符。- 局部变量没有默认值,因此必须先启动局部变量,然后才能使用它们。
Java 中的实例变量
原文: https://javabeginnerstutorial.com/core-java-tutorial/instance-variable-java/
对象使用 Java 中的实例变量存储其状态。 没有使用STATIC
关键字定义的变量,并且在任何方法声明之外的都是特定于对象的,称为实例变量。 之所以这样称呼它们,是因为它们的值特定于实例,而不是在实例之间共享。
实例变量示例
class Page {
public String pageName;
// instance variable with public access
private int pageNumber;
// instance variable with private access
}
Java 中实例变量的规则
- 实例变量可以使用四个访问级别中的任何一个
- 他们可以标记为最终
- 可以将它们标记为瞬态
- 它们不能标记为抽象
- 无法将它们标记为已同步
- 它们不能标记为
strictfp
- 它们不能被标记为本地
- 它们不能标记为静态
备忘单
public, private, protected
所有这三种访问修饰符都可以应用于实例变量(默认)。- 实例变量可以标记为
final
。 - 实例变量可以标记为
transient
。 - 实例变量不能是
abstract
。 - 实例变量无法具有
syncronized
修饰符。 - 实例变量无法具有
strictfp
修饰符。 - 实例变量无法具有
native
修饰符。 - 实例变量不能具有
static
修饰符,因为它会变为类级别的变量。 - 实例变量将获得默认值,这意味着无需初始化即可使用实例变量。 对于局部变量,情况并非如此。
实例变量类型 | 默认值 |
---|---|
boolean |
false |
byte |
(char)0 |
short |
(short)0 |
int |
0 |
long |
0l |
char |
\u0000 |
float |
0.0f |
double |
0.0d |
Object |
null |
package com.jbt;
/*
* Here we will discuss about different type of Variables available in Java
*/
public class VariablesInJava {
/*
* Below variable is INSTANCE VARIABLE as it is outside any method and it is
* not using STATIC modifier with it. It is using default access modifier.
* To know more about ACCESS MODIFIER visit appropriate section
*/
int instanceField;
/*
* Below variable is STATIC variable as it is outside any method and it is
* using STATIC modifier with it. It is using default access modifier. To
* know more about ACCESS MODIFIER visit appropriate section
*/
static String staticField;
public void method() {
/*
* Below variable is LOCAL VARIABLE as it is defined inside method in
* class. Only modifier that can be applied on local variable is FINAL.
* To know more about access and non access modifier visit appropriate
* section.
*
* Note* : Local variable needs to initialize before they can be used.
* Which is not true for Static or Instance variable.
*/
final String localVariable = "Initial Value";
System.out.println(localVariable);
}
public static void main(String args[]) {
VariablesInJava obj = new VariablesInJava();
/*
* Instance variable can only be accessed by Object of the class only as below.
*/
System.out.println(obj.instanceField);
/*
* Static field can be accessed in two way.
* 1- Via Object of the class
* 2- Via CLASS name
*/
System.out.println(obj.staticField);
System.out.println(VariablesInJava.staticField);
System.out.println(new VariablesInJava().instanceField);
}
}
掌握 Java 中的静态变量,以了解它与实例变量的不同之处。
参考文献
- 官方文档
- 静态变量与实例变量
Java 引用变量
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-reference-variable/
引用变量用于引用对象。 它们以无法更改的特定类型声明。
引用变量的类型
- 静态变量
- 实例变量
- 方法参数
- 局部变量
静态变量示例
class test {
//below variable is static variable means it is class level variable
static int i;
public static void main(String[] args) {
// As i is an static variable it can be accessed directly without using any object
System.out.println("Value before calling method1: " + i);
test t1 = new test();
t1.method1();
System.out.println("Value after calling method1: " + i);
t1.method2();
System.out.println("Value after calling method2: " + i);
}
void method1() {
i++;
}
void method2() {
i++;
}
}
实例/局部/方法参数示例
class MPE {
// Instance Variable
int i;
public static void main(String[] args) {
/*
* Here i is an Instance variable.
*/
test t1 = new test();
System.out.println("Value before calling method1: " + t1.i);
}
/*
* Here j is a method parameter. And k is a local variable.
*
* Note**: Local variables life is only till the end of method
*/
void method1(int j) {
int k;
i = j;
/*
* Local Variable(k)'s life ends once execution for this method
* completes. As k is local is variable it needs to be initialized
* before we can use it. But as it is not getting used here, it can stay here without initializing
*/
}
}
变量遮盖
原文: https://javabeginnerstutorial.com/core-java-tutorial/variable-shadowing/
今天,我们将探讨 Java 的一项罕见功能:变量遮盖
首先,让我们定义什么是遮盖字段或方法:
当被遮盖时,该字段被视为
- 其声明类的子类声明一个具有同名的字段
- 在本地范围内声明具有相同名称和类型的变量
- 用相同名称和类型声明方法参数/参数
局部变量遮盖
public class MyClass
{
private int count = 10;
private void localVariable()
{
int count = 5;
System.out.println("count = "+ count);
}
public static void main(String[] args)
{
MyClass test = new MyClass();
test.localVariable();
}
}
上面的代码将输出
count=5
因为在第 7 行中声明的count
局部变量遮盖了在类级别声明的变量计数。 如果要访问实例变量,则需要添加this
关键字。
private void localVariable()
{
int count = 5;
System.out.println("count = "+ this.count);
}
方法参数遮盖
即使我们对此不太关注,这种情况也很常见。 下面是一个简单的获取器定义
private int count;
public void setCount(int count)
{
this.count = count;
}
this
关键字是解决歧义所必需的。 如果没有this
,编译器将无法知道我们是否要为其自身分配计数方法参数值。 如果删除this
关键字,则仍然会收到编译警告。
超类字段遮盖
让我们考虑以下类:
public class SuperClass
{
protected String val = "SUPER_VAL";
protected void display()
{
System.out.println("val = "+this.val);
}
}
public class ChildClass extends SuperClass
{
private String val;
public ChildClass(String value) {
this.val = value;
}
public static void main(String[] args)
{
ChildClass child = new ChildClass("CHILD_VAL");
child.display();
}
}
执行给出:
value = SUPER_VAL
在SuperClass
中声明了val
字段,但在ChildClass
中被遮盖了,因为后者声明了另一个具有相同名称和类型的字段。 尽管已使用CHILD_VAL
实例化了ChildClass
,但执行child.display()
却给您SUPER_VAL
。
原因很简单。 创建子实例时,有 2 个变量val
。 通过构造器,来自SuperClass
的一个具有值SUPER_VAL
,来自ChildClass
的一个具有注入值CHILD_VAL
。
调用display()
方法时,由于它是在超类中定义的,因此它是超类的上下文中的val
字段。输出显示SUPER_VAL
也就不足为奇了。
public class ChildClass extends SuperClass
{
private String val;
public ChildClass(String value) {
this.val = value;
super.val = value;
}
public static void main(String[] args)
{
ChildClass child = new ChildClass("CHILD_VAL");
child.display();
}
}
在上面修改的代码中,我们将SuperClass
中隐藏的val
字段的值强制为super.val = value
,输出给出:
value = CHILD_VAL
现在让我们在层次结构中添加另一个类
public class AncestorClass
{
protected String val = "ANCESTOR_VAL";
}
public class SuperClass extends AncestorClass
{
protected String val = "SUPER_VAL";
}
public class ChildClass extends SuperClass
{
private String val = "CHILD_VAL";
public void displayVal()
{
System.out.println("val = " + super.val);
}
public static void main(String[] args)
{
ChildClass child = new ChildClass();
child.displayVal();
}
}
显然,输出将显示
val = SUPER_VAL
现在的问题是:如果要显示祖先类的val
值,该怎么办? 显然,仅super
关键字引用了类层次结构中的第一个父类。
救援人员来了。 实际上,我们可以将代表当前类实例的关键字强制应用于类层次结构中的特定类型!
public class ChildClass extends SuperClass
{
private String val = "CHILD_VAL";
public void displayVal()
{
System.out.println("val = " + ((AncestorClass) this).val);
}
public static void main(String[] args)
{
ChildClass child = new ChildClass();
child.displayVal();
}
}
这次,我们确实有
val = ANCESTOR_VAL
Java 教程 – 循环
Java for
循环
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-for-loop/
Java for
循环和增强的for
循环是一种控制流语句,它提供了一种紧凑的方法来迭代值范围。 循环重复遍历代码,直到满足特定条件为止。
在此期间,Java for
循环具有不同类型。
for
循环- 增强
for
循环或foreach
for
循环
for
循环是 3 个表达式的组合,需要理解才能有效地使用for
循环。
- 初始化表达式初始化循环; 它会在循环开始时执行一次。
- 终止表达式用于停止循环。 每次迭代都会对它进行求值,并且当终止表达式的求值结果为
false
时,循环终止。 - 通过循环的每次迭代后,将调用递增/递减表达式。
循环的语法
for(Initialization; Termination; Increment/Decrement){
//Code to execute in loop
}
for
循环示例
public class for_loop {
public static void main(String[] args) {
for (int i = 0; i < 4; i++) {
System.out.println("Value of i: " + i);
}
int j;//declare variable outside for loop if needed beyond loop
for (j = 4; j < 8; j++) {
System.out.println("Value of j: " + j);
}
int k = 8;
for (; k < 12; k++) {
System.out.println("Value of k: " + k);
}
}
}
上述程序的输出
Value of i: 0
Value of i: 1
Value of i: 2
Value of i: 3
Value of j: 4
Value of j: 5
Value of j: 6
Value of j: 7
Value of k: 8
Value of k: 9
Value of k: 10
Value of k: 11
这里的代码在初始化表达式中声明了一个int
变量i
。 变量i
的范围从其声明扩展到for
语句块的末尾,因此它也可以在终止和增量表达式中使用。
如果循环外不需要控制for
语句的变量,则最好在初始化表达式中声明该变量。 如您在使用int i
变量的第一个for
循环中所看到的。 在初始化表达式中声明它们会限制它们的寿命并减少错误。
名称i
,j
和k
通常用于控制for
循环。
请注意,
for
循环的三个表达式是可选的。 因此,下面的两个代码都是有效的,尽管它将创建一个无限循环
for (; ; k++) {
System.out.println("Infinite loop");
}
for (; ; ) {
System.out.println("Infinite loop");
}
增强的for
循环
增强的for
循环是for
循环的另一种形式。 它是 Java 5 中引入的,它是一种更简单的方法来迭代集合和数组的所有元素。 它可以使您的循环更紧凑,更易于阅读。
增强的for
循环语法
for(DataType obj: array/collection){}
增强的for
循环示例
import java.util.Arrays;
import java.util.List;
public class for_loop {
public static void main(String[] args) {
//enhanced for loop
String[] array = {"Hello ", "Hi ", "How ", "are ", "you?"};
List<String> list = Arrays.asList(array);
for (String str : array) {
System.out.print(str);
}
System.out.println("\n");
for (String str : list) {
System.out.print(str);
}
}
}
上面代码的输出是
Hello Hi How are you?
Hello Hi How are you?
增强了循环遍历给定集合或数组中的每个对象,将对象存储在变量中和执行循环的主体。
建议尽可能使用增强的
for
循环。
在增强的 for 循环中找不到当前索引。 在需要索引号的情况下,可以使用旧的for
循环,也可以尝试以下替代方法来获取索引号。
import java.util.Arrays;
import java.util.List;
public class for_loop {
public static void main(String[] args) {
//enhanced for loop
String[] array = {"Hello ", "Hi ", "How ", "are ", "you?"};
List<String> list = Arrays.asList(array);
int index = 0;
for (String str : array) {
System.out.print(str);
System.out.println("Current Index :" + index++);
}
System.out.println("\n");
for (String str : list) {
System.out.print(str);
System.out.println("Current Index: " + list.indexOf(str));
}
}
}
代码输出
Hello Current Index :0
Hi Current Index :1
How Current Index :2
are Current Index :3
you?Current Index :4
Hello Current Index: 0
Hi Current Index: 1
How Current Index: 2
are Current Index: 3
you?Current Index: 4
可以在此处找到for
循环的代码。
Java 教程 – 异常
Java 异常教程
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-exceptions/
Java 中的异常用于处理错误或程序正常流程中发生的任何其他异常事件。 在 Java 中可以通过多种方式发生异常。
- 提供的数据不是预期的格式(例如
int
而不是String
)。 - 无法连接数据库。
- 网络连接丢失。
- 对象为空。
- …..
Java 异常层次结构
Java 中的每个异常都是Exception
类的子类型,而Exception
类又是Throwable
的子类。 正如我们所知道的,Java 中所有对象均来自Throwable
对象类,也来自Object
类。 异常和错误是从Throwable
派生的两个不同的类。 错误代表的情况不是由于编程错误而发生,而是可能在程序执行时发生,这些都是 Java 程序无法处理且不应费心处理的异常行为。 JVM 内存不足是一种错误,可能在运行时发生。
受检与非受检异常
受检异常
- 受检异常是
Exception
的子类,但RuntimeException
及其子类除外。 - 受检异常强制程序员处理可能引发的异常。
- 当方法中发生受检异常时,该方法必须捕获异常并采取适当的措施,或者将异常传递给其调用方。
示例
IOException
异常。
非受检异常
- 非受检异常是
RuntimeException
及其任何子类。 - 编译器不会强制程序员捕获非受检异常或在
throws
子句中声明它。 - 程序员甚至可能不知道会引发异常。
- 受检异常必须在编译时捕获。
- 运行时异常不是必需的。
示例
ArrayIndexOutOfBounds
异常。
Java 异常处理
现在我们知道 Java 程序可以在任何时间(或任何位置)发生异常。 因此,我们需要知道如何处理这些异常。 处理异常是开发健壮的应用时必需的属性。 处理异常意味着在发生异常时将程序的执行转移到适当的处理器。 我们可以使用try-catch
块来处理异常。
try
:try
用于定义可能发生异常的代码块。
catch
:catch
用于匹配特定类型的异常。 一个try
块可能有多个catch
子句。
finally
:finally
确定将在try
块之后始终执行的代码块。 即使在异常的情况下。
try {
throw new IOException();
} catch (IOException e) {
// Handle only IO Exception
// This block will get executed only in case of IOException
}catch (Exception e) {
// Handle only all other type of exception
// This block will get executed in case of all exception except IOException
}finally{
System.out.println("This block will get executed no matter exception occur or not");
}
注意:try
子句不能没有catch
或finally
块而存在。 它们都是必需的。
异常传播
不需要处理catch
块中try
块引发的所有异常(因为它不是必需的块)。 如果catch
块无法处理try
块引发的异常,它将传播到调用此方法的方法中。 如果先前调用此方法的方法也无法处理该异常,则该异常将传播到方法栈中的最后一个方法,除非某些方法处理该异常,否则它将以相同的方式传播。 如果没有方法在调用栈中处理它,则异常将触底,而 JVM 将对其进行处理。
(感谢 Mohita 的建议)
备忘单
- 受检和非受检异常是两种类型的异常。
- 受检异常是那些
Exception
类的子类型,但不包括扩展运行时异常的类。 - 错误和运行时异常的子类型属于非受检异常。
finally
块将始终在所有条件下调用。System.exit()
是无法阻止finally
块执行的唯一方法,在这种情况下,JVM 将关闭。- 还可以通过扩展
Exception
类来创建自定义异常。 Catch
块应以最特殊到最一般的形式排序。 否则,编译器将报错无法访问的代码。
https://www.youtube.com/embed/bpHcP14Onp4?start=1&feature=oembed
https://www.youtube.com/embed/bYY6SWnMpz4?start=1&feature=oembed
异常处理 – try-with-resources
语句
原文: https://javabeginnerstutorial.com/core-java-tutorial/exception-handling-try-resources/
通常,finally
块用于关闭所有资源(即文件,数据库连接,套接字或在任务完成后应关闭的任何东西),以防止任何泄漏。
示例代码:
public class ResourceMgt {
public static void main(String[] args) {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("C://test.txt"));
System.out.println(br.readLine());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null)
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
} //finally
} // main
}
如果您看上面的示例代码,要关闭BufferedReader
资源,我们必须检查它是否仍然打开,然后调用close()
方法。 close
方法可能会引发异常,因此必须将其包围在try catch
块中。 对于每个打开的资源,都将重复此代码。 对于大型应用,由于这个原因,您将看到很多重复的代码。
在 Java 7 和更高版本中,try-with-resources
语句可确保在该语句的末尾关闭所有打开的资源。 因此,try-with-resources
语句不过是声明一个或多个资源的try
语句。 所谓资源就是实现java.lang.AutoCloseable
接口的任何对象。 该接口又包括实现java.io.Closeable
接口的所有对象。
因此,使用try-with-resources
语句的同一示例可以写成:
public class ResourceMgt {
public static void main(String[] args) {
try(BufferedReader br = new BufferedReader(new FileReader("C://test.txt"))){
System.out.println(br.readLine());
} catch (IOException e) {
e.printStackTrace();
}
} // main
}
在try
关键字之后立即引入一个括号,并且所有资源都应仅在该括号内声明。 这些资源用分号分隔。
如您所见,使用try-with-resources
语句的代码
- 减少行数即可读取。
- 代码看起来很整洁。
- 如果
finally
块的唯一目的只是关闭资源,则不需要。 - 自动资源管理。
在我们的示例中,由于在try-with-resource
语句中声明了BufferedReader
实例,因此无论try
语句是否成功完成,都将关闭它(如``readLine()方法可以引发
IOException`)。
值得注意的要点:
try-with-resources
语句就像普通的try
语句一样。 它可以拥有常规catch
和finally
块。- 重要的是要记住,在
catch
和finally
块运行之前,已声明的资源已关闭。 - 要在同一
try
语句中声明多个资源,必须使用
例如,
try(BufferedReader br = new BufferedReader(new FileReader("C://test.txt"));
ZipFile zf = new ZipFile("test1.zip")){…}
- 一旦
try-with-resources
块完成(成功完成或引发异常),这些资源的close
方法将以相反的顺序自动调用。
考虑以上示例,
ZipFile
和BufferedWriter
的close()
方法将按照上述顺序(即反向顺序)被调用,以避免可能出现的任何依赖问题。
- 如果在初始化任何资源时有任何问题,那么到目前为止所有已初始化的资源都将以相反的顺序关闭。
- 要将自定义类用作资源,这些类必须实现
lang.AutoCloseable
或java.io.Closeable
接口。 Closeable
和AutoCloseable
接口的close()
方法分别引发IOException
和Exception
类型的异常。- 由于
AutoCloseable
接口的子类可以覆盖close
方法的此行为以引发专门的异常(例如IOException
或根本没有异常),因此最好使用AutoCloseable
进行自定义。
AutoCloseable
接口的自定义实现:
AutoCloseable
接口非常容易实现,因为它只有一个方法close()
。
public interface AutoClosable {
public void close() throws Exception;
}
让我们创建两个实现AutoCloseable
接口的自定义类,
Bar.java
public class Bar implements AutoCloseable{
public Bar(){
System.out.println("Inside Bar Class");
}
public void doSomething(){
System.out.println("Doing something in Bar!");
}
public void close() throws Exception{
System.out.println("Closed Bar");
}
}
Foo.java
public class Foo implements AutoCloseable{
public Foo(){
System.out.println("Inside Foo Class");
}
public void doSomething() throws Exception{
throw new Exception("Exception from Foo doSomething() method");
}
public void close() throws Exception{
System.out.println("Closing Foo");
throw new Exception("Unable to close Foo...");
}
}
Foo
类的doSomething()
和close()
方法引发异常。
MyTryWithResources.java
public class MyTryWithResources {
public static void main(String[] args){
try(Bar b = new Bar(); Foo f = new Foo()){
b.doSomething();
f.doSomething();
}catch(Exception ex){
System.out.println("In catch... " + ex);
}finally{
System.out.println("In finally...");
}
}
}
输出:
说明:
- 这里首先要注意的是打开和关闭资源的顺序。 打开栏,然后打开
Foo
,但在关闭时,遵循相反的顺序。Foo
关闭,然后关闭Bar
资源。 Foo
的close()
会引发异常,但是如果您注意到生成的输出,则会将其抑制。MyTryWithResources
的main
方法仅引发由doSomething()
在try
块中生成的异常。
这是在try-with-resources
中要理解的主要概念。 让我们尝试逐步传播这些信息,
Bar
和Foo
资源在try-with-resources
块中创建。Try
块开始执行。Bar
类的doSomething()
成功执行,并输出消息“在Bar
中做某事!” 进行控制台。Foo
类的doSomething()
方法引发异常。- 在将控件转移到
catch
方法以处理异常之前,通过调用资源各自的close()
方法来关闭资源。 Foo
类的close()
将消息“正在关闭Foo
”打印到控制台,并抛出一个异常,该异常在try
块抛出的异常暴露时被抑制。- 仅当
try
块和try-with-resources
语句(close()
方法)都抛出异常时,try-with-resources
语句对这种异常的抑制才发生。 Bar
类的close()
运行,并将消息“Closing Bar
”显示在控制台上。- 然后,
finally
块执行。
- 如果您还想检索抑制的异常怎么办? 不要担心。 在 Java SE 7 和更高版本中,可以使用
getSuppressed()
方法检索它们。 - 尽管关闭特定资源时会引发异常,但是所有打开的资源都将被关闭,而与引发的异常无关。 在我们的示例中,尽管关闭
Foo
资源时发生异常,但Bar
资源也成功关闭。
为了获取被抑制的异常,只需将行exception_handler_reference_variable.getSuppressed()
添加到MyTryWithResources
类的catch
块中,如下所示,
catch(Exception ex){
System.out.println("No. of suppressed exceptions: " + ex.getSuppressed().length);
System.out.println("In catch... " + ex);
}
输出:
Inside Bar Class
Inside Foo Class
Doing something in Bar!
Closing Foo
Closed Bar
No. of suppressed exceptions: 1
In catch... java.lang.Exception: Exception from Foo doSomething() method
In finally...
通过遵循这些示例,您可以毫不费力地围绕这个概念进行思考。 祝你今天愉快!
Java 异常处理 – try catch
块
原文: https://javabeginnerstutorial.com/core-java-tutorial/exception-handling-try-catch-java/
作为开发人员,我们每天处理风险情况。 服务器故障,或者没有足够的空间分配堆上的对象,或者给定位置不存在文件,依此类推。 因此,每次我们决定采取有风险的措施时,我们都必须通知编译器我们知道这是有风险的事情,并且已经准备好处理它。 我们如何处理这不是编译器的问题。 它需要记住的是,我们正在照顾可能出现的任何特殊情况。
我们通过将代码包装在 Java 的try catch
中来处理这些情况。
try/catch/finally
块的基本语法:
try{
//code that could throw an exception
//if exception thrown, following code is not reachable
//control jumps to catch block
}catch(ExceptionType referenceVariable){
//code that is executed only when an exception is thrown
//does something using the exception reference variable
//usually prints stack trace or exception description
}finally{
//cleanup code
//always executes regardless of an exception
}
注意
- 在
try
块和catch
块之间不能编写任何代码。 try
块必须紧随其后的是catch
或finally
块,或二者兼而有之。 如果没有catch
块,则尽管finally
方法具有try/finally
,但final
方法应声明异常。- 您不能拥有不带
catch
或finally
的try
块。 - 如果您不想在代码中处理异常,请使用引发并声明子句。 谁调用您的代码都必须使用
try/catch
块来处理它。
控制流
- 如果
try
块成功,即未引发异常,则控制将移至finally
块(如果存在)。 跳过catch
块。 在没有finally
块的情况下,将执行catch
块下面的任何代码。 - 如果
try
块失败(发生异常),则控制权转移到处理异常的catch
块。try
块中的其余代码永远不会执行。 如果存在finally
块,则在catch
块执行完成后运行。 - 如果
try/catch
块具有返回语句,那么即使执行finally
块! 流控制首先跳转到finally
块,然后返回return
语句。
示例
public class TryCatch1 {
public static void main(String[] args) {
System.out.println(riskyAction());
}
public static String riskyAction(){
try{
System.out.println("Started executing try block");
return "returning from try block";
}catch(Exception e){
return "returning from catch blcok";
}finally{
System.out.println("print statement from finally");
}
}
}
输出
Started executing try block
print statement from finally
returning from try block
解释
try
块运行并打印“开始执行try
块”。- 一旦遇到
return
语句,流程将立即转移到finally
块并打印“finally
的print
语句”。 finally
块执行完成后,控制权返回try
块中的return
语句,并返回“从try
块返回”。- 如果
finally
块具有返回语句,则来自try/catch
块的return
语句将被覆盖。
示例
public class TryCatch2{
public static void main(String[] args) {
System.out.println(riskyAction("hello"));
System.out.println("-----------");
System.out.println(riskyAction("howdy"));
}
public static String riskyAction(String greeting){
try{
if(greeting.equals("hello")){
System.out.println(greeting + " from try block");
}else{
throw new Exception();
}
return "returning from try block";
}catch(Exception e){
System.out.println(greeting + " from catch block");
return "returning from catch block";
}finally{
return "returning from finally block";
}
}
}
输出
hello from try block
returning from finally block
-----------
howdy from catch block
returning from finally block
解释
对于方法调用,riskyAction("hello")
:try
块成功并打印try
块中的“hello”。 由于它具有return
语句,因此控制权转移到finally
块。 finally
块还具有一个return
语句,该语句将覆盖try
块中的语句,因此该方法将返回并打印“finally
块返回”到控制台。
对于riskyAction("howdy")
:try
块引发异常,该异常在 catch 块中处理,该异常打印catch
块中的“howdy”。 就像我们在try
块成功的情况下看到的一样,finally
块的return
语句也覆盖catch
块中的return
语句。 结果,该方法返回并打印“从finally
块返回”到控制台。
注意:由于无论是否发生异常,finally
块总是被执行,因此,如果它具有return
语句,则可以预期到意外的结果,并且可能变得难以调试。 作为一种好的做法,最好避免在finally
块中编写return
语句。
捕获多个异常
在 Java 7 之前,为了处理多个异常,使用了多个catch
块(从最特定到最普通)。 编写代码是为了打印栈跟踪,执行错误恢复,链接异常,允许用户做出决定等。但是编写多个catch
块包含许多重复代码。 另外,程序员倾向于捕获更广泛或更普遍的异常,而不是特定的异常。 例如,捕获IOException
而不是FileNotFoundException
。
从 Java SE 7 和更高版本起,这些缺陷已通过单catch
块解决,该块可以处理多种类型的异常。 在这里,要处理的异常类型在以竖线(|
)分隔的catch
子句的主题中指定。
示例
catch(ArrayIndexOutOfBoundsException | SQLException ex){
ex.printStackTrace();
}
注意
- 每当单个
catch
块处理多个异常时,引用变量(上例中为“ex
”)为final
,因此将其视为常量。 因此,无法为其分配其他任何值。 在某些情况下,这限制了异常处理能力。 - 不能将异常类型与其父类组合在一起,因为子类异常由于已经被捕获而变得不可访问。
示例
仔细查看Finally
块
- 一旦
try
块中的控件退出,无论是否引发异常,都将始终执行finally
块。 finally
块未执行,- 如果在执行
try/catch
块代码时 JVM 退出 - 如果在控制权到达
finally
块之前执行了System.exit()
- 如果执行
try/catch
代码的线程被中断或杀死
- 如果在执行
finally
块通过关闭可能已打开的资源来防止任何资源泄漏。- 如果要恢复任何资源,则必须将代码放在
finally
块中。 - 仅带有
finally
块的try
块(即没有catch
块)仍应声明该异常以进行处理。
在 Java SE 7 和更高版本中,考虑使用try-with-resources
语句自动关闭不再使用的资源。 开发人员不需要为此记住通过编写finally
块来释放使用的资源。
try-with-resources
语句的文章提供了详细的信息以及其优势,摘要和示例代码片段。
Java 教程 – OOPS 概念
Java 重载
原文: https://javabeginnerstutorial.com/core-java-tutorial/overloading/
重载方法为您提供了一个选项,可以在类中使用与相同的方法名称,但具有不同的参数。
Java 方法规则中的重载
有一些与重载方法相关的规则。
重载方法
- 必须更改参数列表
- 可以更改返回类型
- 可以更改访问修饰符(更广泛)
- 可以声明一个新的或更广泛的受检异常
方法可以在类或子类中重载。
重载方法示例
//Overloaded method with one argument
public void add(int input1, int input2) {
System.out.println("In method with two argument");
}
//Overloaded method with one argument
public void add(int input1) {
System.out.println("In method with one argument");
}
调用重载方法
在几种可用的重载方法中,调用的方法基于参数。
add(3,4);
add(5);
第一个调用将执行第一个方法,第二个调用将执行第二个方法。
引用类型而不是对象决定调用哪个重载方法,和覆盖方法相反。
感谢 Manoj 指出拼写错误。
方法重载备忘单
- 使用相同的方法名称但使用不同的参数称为重载。
- 构造器也可以重载
- 重载的方法必须设置不同的参数。
- 重载的方法可能具有不同的返回类型。
- 重载的方法可能具有不同的访问修饰符。
- 重载的方法可能抛出不同的异常,更宽或更窄的没有限制。
- 超类中的方法也可以在子类中重载。
- 多态适用于覆盖和重载。
- 基于引用类型,确定在编译时间上确定将调用哪个重载方法。
方法重载示例
package com.overloading;
/*
* Here we will learn how to overload a method in the same class.
* Note: Which overloaded method is to invoke is depends on the argument passed to method.
*/
public class MethodOverloading {
public static void main(String args[])
{
//Creating object of the class MethodOverloading
MethodOverloading cls = new MethodOverloading();
System.out.println("calling overloaded version with String parameter");
cls.method("Hello");
System.out.println("calling overloaded version with Int parameter");
cls.method(3);
/*
* Here same method name has been used, But with different argument.
* Which method is to invoke is decided at the compile time only
*/
}
/*
* Overloaded version of the method taking string parameter
* name of the method are same only argument are different.
*/
void method(String str)
{
System.out.println("Value of the String is :"+str);
}
/*
* Overloaded version taking Integer parameter
*/
void method(int i)
{
System.out.println("Value of the Int is :"+i);
}
}
- 使用相同的方法名称但使用不同的参数称为重载。
- 构造器也可以重载
- 重载的方法必须设置不同的参数。
- 重载方法可能具有不同的返回类型。
- 重载的方法可能具有不同的访问修饰符。
- 重载的方法可能会抛出不同的异常,更宽或更窄的没有限制。
- 超类中的方法也可以在子类中重载。
- 多态适用于覆盖和重载。
- 基于引用类型,确定在编译时确定将调用哪个重载方法。
Java 方法覆盖
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-overriding/
从其超类继承该方法的类可以选择覆盖它。 覆盖的好处是可以定义特定于特定类的行为的能力。 对于具体的子类,如果在层次结构中没有其他超类实现抽象类中定义的所有方法,则将其强制实现。 覆盖有时称为运行时绑定。 这意味着将调用哪个覆盖方法将由引用类型而不是实例类型确定。
方法覆盖示例
public class ParentClass
{
public void show()
{
System.out.println("Show method of Super class");
}
}
public class SubClass extends ParentClass
{
//below method is overriding the ParentClass version of show method
public void show()
{
System.out.println("Show method of Sub class");
}
}
方法覆盖规则
- 覆盖方法不能具有比被覆盖的方法更多的限制性访问修饰符,但可以更少。
- 参数列表必须与覆盖的方法完全匹配,如果不匹配,则很可能是您正在重载该方法。
- 返回类型必须与在超类中的覆盖方法中声明的返回类型相同或为该子类型的子类型。
- 覆盖方法可以抛出任何非受检异常(运行时),但是它可以抛出比被覆盖方法声明的范围更广或更新的受检异常,但不能抛出更少或狭窄的异常检查。
- 不能覆盖最终的方法。
- 静态方法无法覆盖。 静态方法看起来可以覆盖,但它是隐藏的。
- 如果方法无法继承,则无法覆盖。
从超类调用被覆盖方法
如果要在执行子类方法之前调用超类的覆盖方法,该怎么办。 您可以使用SUPER
关键字。
public class SubClass extends superclass {
void method() {
super.method();
System.out.println("In Sub Class");
}
public static void main(String[] args) {
SubClass obj = new SubClass();
obj.method();
}
}
class superclass {
void method() {
System.out.println("In Super Class");
}
}
输出量
In Super Class
In Sub Class
静态方法不能被覆盖
静态方法不能被覆盖。 看起来好像它已被覆盖,但事实并非如此。 静态方法可以隐藏。
public class SubClass extends superclass {
static void method() {
// super.method(); // Super keyword will not work here. As it is not overriden method
System.out.println("In Sub Class");
}
@SuppressWarnings("static-access") // The static method method() from the type SubClass should be accessed in a static way
public static void main(String[] args) {
SubClass obj = new SubClass();
obj.method();
SubClass.method();// It is same as above. Same method will be invoked
}
}
class superclass {
static void method() {
System.out.println("In Super Class");
}
}
这里super
关键字不能用于调用超类方法。 因为它没有被超类的方法覆盖。
备忘单
- 不能覆盖构造器。
- 覆盖方法必须具有相同的参数集。
- 覆盖的方法必须具有相同的返回类型。 这些返回类型也可以是子类(协变返回)。
- 覆盖的方法无法具有更严格的访问修饰符。
- 覆盖的方法无法抛出新的或更广泛的异常(受检)。 看下面的示例
- 覆盖的方法可以引发任何非受检异常。
- 最终方法无法覆盖。
- 私有方法没有继承到子类,因此不能在子类中覆盖*。
- 多态适用于覆盖。
- 对象类型确定将调用哪个覆盖方法,并由运行时确定。
方法覆盖异常示例
package com.example.simple;
public class Overridding_Class extends base_class {
@Override
void method() throws exception_3 { // NO PROBLEM, It is not a broader Exception.
}
void method1() throws exception_1 { // It will give COMPILATION ERROR as it is throwing Broader Exception
}
}
class base_class {
void method() throws exception_2 {
}
void method1() throws exception_2 {
}
}
class excepion_1 extends Exception {
}
class exception_2 extends excepion_1 {
}
class exception_3 extends exception_2 {
}
方法覆盖示例
package com.override;
public class MethodOverrideRule {
public static void main(String args[])
{
// Here reference type and Object type is same
MethodOverrideRule scls = new MethodOverrideRule();
OverrideSubclass subcls = new OverrideSubclass();
// Here reference type is of Super class and Object is of child class
MethodOverrideRule subOcls = new OverrideSubclass();
// This will invoke method from Super class
scls.method();
// This will onvoke method form sub class
subcls.method();
/*
* Here overriding will work. Even reference type is of Super class still object type if of Subclass.
* Hence Subclass version of method will get invoked.
*/
subOcls.method();
/*
* Which overridden method is to be executed depends on the actual Object type at run time.
*/
}
void method(){
System.out.println("Overriding method without argument in Super");
}
int method(String str)
{
System.out.println("Overriding method with int argument in Super");
return 9;
}
}
class OverrideSubclass extends MethodOverrideRule{
/*
* Here we are overriding the method from super class.
* @Override annotation is used to confirm the same. It makes sure the all override rules get followed
*
*/
@Override
void method(){
System.out.println("Overriding method without argument in subclass");
}
@Override
int method(String str)
{
System.out.println("Overriding method with int argument in subclass");
return 10;
}
}
Java 接口
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-interface/
定义合同意味着创建接口。 该合同规定了一个类可以做什么而不必强制它应该怎么做。 在本 Java 接口教程中,我们将讨论接口。 如何创建一个和应用于接口的规则。
声明接口
可以使用interface
关键字定义接口。 在 Java 8 之后,接口定义发生了很多变化。
Java 接口示例
public interface MyInterface
{
int i=0;
public void Height(int height);
public abstract void setHeight();
}
Java 8 接口示例
package com.jbt;
public interface Interface_JAVA8 {
public void method();
public abstract void method1();
public default void method2() {
}
public static void method3() {
}
}
Java 8 引入了默认方法的概念。 现在,如果接口不是抽象的,则可以包含方法主体。 接口内部的方法可以包含“抽象”,“静态”或“默认”修饰符。
Java 9 接口私有方法
在 Java 9 中,允许在接口中使用私有方法。
接口声明规则
接口需要遵循一些规则。
- 所有接口方法都是隐式公开抽象的。 即使您使用关键字,也不会产生问题,如第二个方法声明中所见。 (在 Java 8 之前)
- 接口只能声明常量。 实例变量是不允许的。 这意味着接口内的所有变量必须是公开的,静态的,最终的。 接口内的变量是隐式公开静态最终的。
- 接口方法不能为静态。 (在 Java 8 之前)
- 接口方法不能是
final
,strictfp
或native
。 - 该接口可以扩展一个或多个其他接口。 注意:该接口只能扩展另一个接口。
接口声明规则(JAVA 8)
接口逻辑在 Java 8 中已更改。因此,上述某些逻辑不适用于 Java 8 之后的接口。
- 所有接口方法都可以具有
abstract
,static
或默认修饰符。 - 如果对方法使用
static
或default
修饰符,则接口方法可以具有主体。
接口与抽象类
接口就像100% 抽象类一样。 接口不能具有非抽象方法,而抽象类则可以。 一个类可以实现多个接口,而只能扩展一个类。 由于抽象类属于类的层次结构,因此它们可以扩展其他类,而接口只能扩展接口。
在类中使用接口
用 Java 创建接口后如何利用接口? 为了利用我们需要利用给定的接口实现我们的类。 Implement
关键字可用于此目的。
类实现接口的示例
示例 1:
public class InterfaceExampleOne implements interfaceOne {
}
interface interfaceOne {
}
示例 2:
/*
* As implmeneted Interface have any abstract method so this class
* need to implement any method.
*/
class InterfaceExampleTwo implements interfaceTwo {
@Override
public void methhod() {
System.out.println(var);
}
}
/*
* Below interface has an abstract method so implemented class needs to
* implement this method unless and untill it is abstract itself
*/
interface interfaceTwo {
public final int var = 9;
public abstract void methhod();
}
示例 3:
/*
* As below class is not abstract class and it is extending abstract class which
* has not yet implemented the method from interface so this class is FORCED to
* implement method from Interface in hierarachy(interfaceTwo).
*/
class InterfaceExampleTwo extends InterfaceExampleThree {
@Override
public void methhod() {
System.out.println(var);
}
}
/*
* Below interface has an abstract method so implemented class needs to
* implement this method unless and untill it is abstract itself
*/
interface interfaceTwo {
public final int var = 9;
public abstract void methhod();
}
/*
* Even if Interface has abstract method ABSTRACT CLASS is not forced to
* implement it. Abstract class may/may not navigate this responsibility of
* implementing abstract method to class which is not abstract.
*/
abstract class InterfaceExampleThree implements interfaceTwo {
// Method from Interface is not implemented here
}
重点
- 接口是 100% 抽象类(隐式)。 在 Java 8 之后,它不成立。
- 接口可以由任何继承树中的任何类实现。
- 接口中的所有方法都是抽象的。 (在 Java 8 中为
abstract
/static
/默认) - 接口可以具有常量,这些常量是公开的,静态的和最终的(隐式)。
- 接口方法是隐式公开和抽象的。 (在 Java 8 之前)
- 接口也可以具有私有方法。 (Java 9)
- 实现接口的类也可以是抽象类。
- 实现接口的抽象类不必实现所有抽象方法。
- 一个类可以实现多个接口。
- 接口不能扩展类或实现接口。
- 一个接口可以扩展另一个接口。
- 实现接口的非抽象类需要遵循一些规则。
- 此类需要提供所有抽象方法的具体实现。
- 必须遵守所有覆盖规则。
- 它必须维护方法的确切签名。
- 在 Java 9 更改之后,接口看上去很像抽象类,但是还是有一些区别。
- 抽象类可以具有带有不同修饰符的变量,这些修饰符不是常数
- 抽象类中的方法可以具有不同于私有或公开签名的签名
继承
原文: https://javabeginnerstutorial.com/core-java-tutorial/inheritance/
Java 继承定义了超类及其子类之间的 is-a 关系。 这意味着只要可以使用超类的对象,就可以使用子类的对象。 Java 中的类继承用于从现有类构建新的类。 继承关系是可传递的:如果类 x 扩展了类 y,则扩展类 x 的类 z 也将从类 y 继承。
例如,汽车类可以继承通用汽车类的某些属性。 在这里,我们发现基本类是车辆类,子类是更具体的汽车类。 子类必须使用extends
子句从超类派生,该超类必须写在子类定义的标头中。 子类继承了超类的成员,因此促进了代码重用。 子类本身可以添加其新的行为和属性。 java.lang.Object
类始终位于任何类继承层次结构的顶部。
使用 Java 类继承是不可能的?
- 超类的私有成员不被子类继承,并且只能间接访问。
- 由于构造器和初始化块不是类的成员,因此它们不会被子类继承。
- 子类只能扩展一个超类
- 在超类中具有默认可访问性的成员也不会被其他包中的子类继承,因为这些成员只能通过其与超类在同一包中的子类中的简单名称进行访问。
this
和super
关键字:
这两个关键字super
和this
帮助您显式命名所需的字段或方法。 使用this
关键字和super
关键字,您可以完全控制是否要调用同一类中的方法或字段,还是要从直接父类中调用。 this
关键字用作对作为当前类实例的当前对象的引用。 super
关键字还引用当前的对象,但作为当前类的超类的实例。
this
关键字引用当前对象,在局部变量隐藏或覆盖相同名称的字段的情况下很有用。 如果某个方法需要将当前对象传递给另一个方法,则可以使用此引用进行传递。 注意,该引用不能在静态上下文中使用,因为在任何对象的上下文中都不会执行静态代码。
Java 继承备忘单
- 父类的所有公开变量将由子类继承。
- 所有子类将仅继承同一包中的所有默认变量。 包之外的子类将不会继承任何默认成员。
- 私有成员无法由子类继承,因为它们对子类不可见,因此,子类可以使用相同的名称创建方法或属性,而不会出现任何问题。
- 所有子类都将继承同一包或外部包(与默认设置不同)中的受保护变量。
- 未继承的方法不能被覆盖。 因此,覆盖的规则无法应用于这些方法。 但是方法仍然可以在子类中定义,尽管这些方法将不会被覆盖。 相反,它代表一个新的方法。
- 静态方法或变量不参与继承。
- 即使静态方法或变量不参与继承且不能被覆盖,也可以在子类中重新定义它们。 重新定义不称为覆盖,而是隐藏。
https://www.youtube.com/embed/Oykbi03ipZs?start=1&feature=oembed
Java 教程 – 关键字
Java 中的this
关键字
原文: https://javabeginnerstutorial.com/core-java-tutorial/this-keyword-java/
this
是什么
this
是 Java 中的关键字。 可以在类的方法或构造器内部使用。 它(this
) 用作对当前对象的引用,当前对象的方法或构造器正在被调用。 this
关键字可用于从实例方法或构造器中引用当前对象的任何成员。
this
关键字和字段(实例变量)
this
关键字在处理变量隐藏时可能非常有用。 我们不能创建两个具有相同名称的实例/局部变量。 但是,创建一个实例变量&,一个具有相同名称的局部变量或方法参数是合法的。 在这种情况下,局部变量将隐藏实例变量,这称为变量隐藏。
变量隐藏示例
class JBT {
int variable = 5;
public static void main(String args[]) {
JBT obj = new JBT();
obj.method(20);
obj.method();
}
void method(int variable) {
variable = 10;
System.out.println("Value of variable :" + variable);
}
void method() {
int variable = 40;
System.out.println("Value of variable :" + variable);
}
}
上面程序的输出
Value of variable :10
Value of variable :40
如您在上面的示例中看到的那样,实例变量正在隐藏,并且局部变量(或“方法参数”)的值不显示为实例变量。 要解决此问题,请使用this
关键字,并使用一个字段指向实例变量而不是局部变量。
this
关键字用于变量隐藏的示例
class JBT {
int variable = 5;
public static void main(String args[]) {
JBT obj = new JBT();
obj.method(20);
obj.method();
}
void method(int variable) {
variable = 10;
System.out.println("Value of Instance variable :" + this.variable);
System.out.println("Value of Local variable :" + variable);
}
void method() {
int variable = 40;
System.out.println("Value of Instance variable :" + this.variable);
System.out.println("Value of Local variable :" + variable);
}
}
上面程序的输出
Value of Instance variable :5
Value of Local variable :10
Value of Instance variable :5
Value of Local variable :40
this
关键字和构造器
this
关键字可以在构造器内使用,以调用同一类中的另一个重载构造器。 这称为显式构造器调用。 如果一个类有两个重载的构造器,一个不带参数,另一个不带参数,则会发生这种情况。 然后this
关键字可用于从构造器中调用带有参数的构造器,而无需使用参数。 这是必需的,因为无法显式调用构造器。
this
与构造器的示例
class JBT {
JBT() {
this("JBT");
System.out.println("Inside Constructor without parameter");
}
JBT(String str) {
System.out
.println("Inside Constructor with String parameter as " + str);
}
public static void main(String[] args) {
JBT obj = new JBT();
}
}
The output of the above program
Inside Constructor with String parameter as JBT
Inside Constructor without parameter
如您所见,this
可用于调用同一类中的重载构造器。
注意:
this
关键字只能是构造器中的第一个语句。- 构造器可以具有
this
或super
关键字,但不能同时具有这两个关键字。
this
关键字和方法
this
关键字也可以在 Methods 内部使用,以从同一类调用另一个方法。
this
关键字与方法的示例
class JBT {
public static void main(String[] args) {
JBT obj = new JBT();
obj.methodTwo();
}
void methodOne(){
System.out.println("Inside Method ONE");
}
void methodTwo(){
System.out.println("Inside Method TWO");
this.methodOne();// same as calling methodOne()
}
}
上面程序的输出
Inside Method TWO
Inside Method ONE
this
关键字作为方法参数的示例
public class JBTThisAsParameter {
public static void main(String[] args) {
JBT1 obj = new JBT1();
obj.i = 10;
obj.method();
}
}
class JBT1 extends JBTThisAsParameter {
int i;
void method() {
method1(this);
}
void method1(JBT1 t) {
System.out.println(t.i);
}
}
如果您正确理解了this
关键字,那么下一步应该是从 Java 教程了解 Java 中的static
关键字。
参考文献
1- 官方文档
Java static
关键字
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-static-keyword/
什么是static
静态是非访问修饰符。
适用于
可以将Static
关键字应用于
- 方法
- 变量
- 嵌套在另一个类中的类
- 初始化块
不适用于
static
关键字不能应用于
- 类(未嵌套)
- 构造器
- 接口
- 方法局部内部类(与嵌套类之间的区别)
- 内部类方法
- 实例变量
- 局部变量
Java 中static
关键字的目的
静态词可用于将变量或方法附加到类。 标记为静态的变量或方法属于该类,而不是任何特定实例。 与实例变量相反。
如何调用
可以在没有类实例的情况下使用静态变量和方法。 只有类才需要调用静态方法或静态变量。
/*
* Here we will learn to access Static method and Static Variable.
*/
public class JavaStaticExample {
static int i = 10;
static void method() {
System.out.println("Inside Static method");
}
public static void main(String[] args) {
// Accessing Static method
JavaStaticExample.method();
// Accessing Static Variable
System.out.println(JavaStaticExample.i);
/*
* No Instance is required to access Static Variable or Method as we
* have seen above. Still we can access the same static variable and
* static method using Instace references as below.
*/
JavaStaticExample obj1 = new JavaStaticExample();
JavaStaticExample obj2 = new JavaStaticExample();
/*
* Accessing static variable in Non Static way. Compiler will warn you
* with below warning.
*
* The static field JavaStaticExample.i should be accessed in a static
* way.
*/
System.out.println(obj1.i);
// Accessing satic method using reference.
// Warning by compiler
// "The static method method() from the type JavaStaticExample should be accessed in a static way"
obj1.method();
}
}
上述程序的输出
Inside Static method
10
10
Inside Static method
注:static
关键字可以与变量和方法一起使用。 它不适用于类。
类变量 – 静态字段
类变量(也称为静态字段)在类中的所有对象之间共享特征。 当您声明字段为静态字段时,只会创建关联变量的单个实例,该实例对于该类的所有对象都是通用的。 因此,当一个对象更改类变量的值时,它将影响该类的所有对象。 我们可以通过使用类的名称来访问类变量,而不必使用对类内单个对象的引用。 即使不存在该类的对象,也可以访问静态变量。 类变量使用static
关键字声明。
类方法 – 静态方法
可以在没有类实例的情况下调用类似于类变量的类方法。 类方法通常用于为 Java 程序提供全局函数。 例如,java.lang.Math
包中的方法是类方法。 您不能从静态方法内部调用非静态方法。
static
关键字规则
- 标记为
static
的变量或方法属于类 ,而不是任何特定实例。 - 静态方法或变量可以在不创建或引用类实例的情况下使用。
- 如果存在实例,则该类的所有实例将共享一个类的静态变量,这将导致 仅一个副本 。
- 静态方法不能访问非静态变量,也不能直接调用非静态方法(它可以通过实例调用或访问方法或变量)。
备忘单
- 静态是非访问修饰符。
- 静态修饰符可以应用于变量或方法,块或内部类。
- 静态成员仅属于类而不是实例。
- 静态方法无法访问实例变量*。
- 静态方法不能替代,因为它们是特定于类的,并且不属于实例。
- 可以将重新定义静态方法。
- 如果一个类包含任何静态块,则仅当将该类加载到 JVM 中时,才执行该块。 创建多个实例不会多次运行静态块。 仅构造器将执行多次。
- 如果调用
Class.forName("class_name")
,则将执行类的静态块。
https://www.youtube.com/embed/QZwgz9tIy7I?start=1&feature=oembed
Java 教程 – 集合
Java 数组教程
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-array-tutorial/
Java 数组是一种数据结构类型,用于存储相同类型的多个变量。 数组可以包含原始类型和对象。 数组中的变量是有序的,但未排序。 数组中的每个元素都有一个索引,数组中元素的起始索引为 0。
数组将始终是堆中的对象。 不管它存储什么,原始类型还是对象。
数组声明(语法)
原始数组
//Single Dimensional Array
int[] arr; //recommended
int arr[];
//Multi Dimensional Array
int[][] arr; //recommended
int arr[][];
int[] arr[];Array Of Objects
对象数组
//Single Dimensional Array
String[] arr; //recommened
String arr[];
//Multi Dimensional Array
String[][] arr; //recommened
String arr[][];
String[] arr[];
注意:您不能在声明中包括数组的大小。
声明仅不会在堆中创建数组对象。
构造一个 Java 数组
这是将在堆上创建数组对象的步骤。 一旦创建,就无法更改数组的大小,因此在构造数组时需要提供数组的大小。 JVM 在堆上创建数组对象时将使用此大小。
数组的大小意味着一个数组可以包含多少元素。
一维数组
new
关键字将用于构造一维/多维数组。
int[] arr; //declares a new array
arr = new int[10]; One Dimensional Array
二维数组
这些是数组的数组。 因此,二维数组是int
数组的数组。 因此,当您说的时候,您将创建一个Long
类型的二维数组,这意味着它将是一个包含多个元素的数组,这些元素本身就是Long
类型的数组。
int[][] arr;
arr = new int[10][];
仅第一部分(第一维)需要大小,而不需要全部。
初始化数组
一旦创建了数组并为其分配了空间,下一步将是在其中添加元素。 数组的初始化是我们可以执行此操作的地方(在数组中添加元素)。
一维数组
int[] arr = new int[10];
arr[0] = 0;
arr[0] = 1;
int[][] arr = new int[10][]; // Multi Dimensional Array
arr[0][0] = 0;
arr[0][1] = 1;
Java 数组字面值
在 Java 中,如果您要创建基本类型或字符串的数组并且值是固定的,则可以利用数组字面值的语法。 这是定义数组字面值的方法。
String[] strArray = new String[]{"J", "B", "T"};
String[] strArray = {"J", "B", "T"};
{}
用于定义需要在数组中插入的值。 {}
中的值将确定数组的大小。
访问数组元素
数组中的每个元素都有一个索引。 索引从 0 开始,这意味着数组中的第一个元素的索引为 0,第二个元素的索引为 1。这些索引号可用于访问数组中的特定元素。 相同的索引可用于在给定索引处设置数组中的值。
String[] strArray = {"J","B","T"};
strArray[0] = "CHANGED";
String valueAtIndex = strArray[0];
数组中的最后一个元素的索引号为(size_of_the_array – 1
)。
Java 数组备忘单
- 数组是相同类型的变量的集合。
- 数组始终在堆中创建。
- 数组可以包含基本体和对象。
- 数组的大小不能包含在声明中。
- 该声明不会在堆中创建数组对象。
- 创建数组后,其大小无法更改。
- 可以通过长度字段访问数组的长度。
- Java
for
循环或增强循环可用于迭代数组元素。
数组示例
public class JavaArrayBasic {
public static void main(String args[]) {
/*
* Array needs to be initialized before it can be used
*/
int intArr[] = { 1, 2 };
int intArr1[];
// intArr1 can not be used @ this place as it has not been initialized yet
/*
* intArr = {1,2}; Array constants can only be used in initializers, So
* above line will give compile time error
*/
for (int i = 0; i < 2; i++)
System.out.println("Value in array @ index" + i + " is "
+ intArr[i]);
}
}
Java 集合
原文: https://javabeginnerstutorial.com/core-java-tutorial/collection-in-java/
在 JDK 1.2 中添加了集合框架,并在 1.4 -1.6 中对其进行了扩展
集合框架的接口金额类
集合 API 提供了一组接口供您选择,但同时也为您提供了一些可直接使用的具体类。
核心接口
Collection
List
Set
SortedSet
(扩展Set
接口)NavigableSet
(扩展SortedSet
)
Map
SortedMap
(扩展Map
接口)NavigableMap
(扩展SortedMap
)
Queue
实现类
1 Map
HashMap
HashTable
TreeMap
LinkedHashMap
2 Set
HashSet
LinkedHashSet
TreeSet
3 List
ArrayList
Vector
LinkedList
4 Queue
AbstractQueue
ArrayBlockingQueue
ConcurrentLinkedQueue
DelayQueue
LinkedBlockingDeque
LinkedBlockingQueue
LinkedTransferQueue
PriorityBlockingQueue
PriorityQueue
SyncronizedQueue
5 Deque
ArrayDeque
ConcurrentLinkedDeque
6 数组
并非集合框架中的所有类都实现
Collection
接口。没有与
Map
相关的类&接口从Collection
扩展。
集合也可以基于排序和排序进行划分。
1 - 有序集合
有序集合可以以特定顺序(非随机)进行迭代。 例如:数组,哈希表
LinkedHashSet
(迭代顺序是可预测的)- 数组
HashTable
ArrayList
2 – 无序集合
HashSet
3 - 排序的集合
在排序的集合中,集合的顺序是根据某些规则确定的。 例如List
List
接口
属性:
关注索引
具有与索引(indexOf
..)相关的方法
按索引位置排序
ArrayList
语法
List l = new ArrayList<E>();
属性:
- 可增长数组
- 快速迭代
- 快速随机访问
- 按索引排序
Vector
与ArrayList
相同,但Vector
中的方法是同步的。
LinkedList
属性
- 元素彼此双重链接。
- 按索引位置排序
- 适用于实现栈和队列
- 快速插入和删除
Set
接口
设置用于考虑对象唯一性的情况。 不允许重复的对象。 若要确定两个对象是否相等,Set
使用equals()
和hashcode()
方法。
HashSet
属性
- 无序
- 使用对象的哈希码
- 没有重复的对象
LinkedHashMap
- 有序(插入)
- 使用对象的哈希码
- 没有重复的对象
TreeSet
- 排序(使用树形结构)
- 使用对象的哈希码
- 没有重复的对象
——————————————————————————————
并发集合接口
BlockingQueue
扩展了Queue
TransferQueue
扩展了BlockingQueue
BlockingDeque
扩展了BlockingQueue
ConcurrentMap
扩展了Map
并发集合实现
LinkedBlockingQueue
扩展了AbstractQueue
实现了BlockingQueue
ArrayBlockingQueue
扩展了AbstractQueue
实现了BlockingQueue
PriorityBlockingQueue
扩展了AbstractQueue
实现了BlockingQueue
DelayQueue
扩展了AbstractQueue
实现了BlockingQueue
SynchronousQueue
扩展了AbstractQueue
实现了BlockingQueue
LinkedBlockingDeque
扩展了AbstractQueue
实现了BlockingDeque
LinkedTransferQueue
扩展了AbstractQueue
实现了TransferQueue
CopyOnWriteArrayList
实现List
,RandomAccess
CopyOnWriteArraySet
扩展AbstractSet
ConcurrentSkipListSet
扩展了AbstractSet
实现了NavigableSet
ConcurrentHashMap
扩展了AbstractMap
实现了ConcurrentMap
ConcurrentSkipListMap
扩展了AbstractMap
实现了ConcurrentNavigableMap
可能有助于决定集合类的因素
为特定问题选择适当的集合时,可以考虑多种因素。 这些因素是:
- 排序 - 元素中的某种排序。 例如,排序顺序,插入顺序或没有特定顺序。
- 重复项 - 可能或可能不想在集合中允许重复的元素。
- 线程安全 - 如果有多个线程访问集合,请确保集合中元素的安全。
- 键值对 - 存储在键值对中。
- 阻塞操作 - 检索元素时等待集合变为非空。
- 随机访问 - 即时检索元素。
- 上限 - 要限制集合可以容纳的最大元素数。
还有其他因素,例如优先级,延迟等。
备忘单
- 集合是存储对象的数据结构。
- 可以添加,删除对象,也可以在集合中对其进行遍历。
- 基本集合有 4 种类型
- 列表: 有序,允许重复,已索引。
- 集:可以有序或可以无序。 不允许复制。
- 映射:不允许使用重复的键。
- 队列:按 FIFO 或优先级进行排序。
ArrayList
:快速迭代&快速随机访问。Vector
:同步方法。LinkedList
:适用于实现栈和队列。HashSet
:快速访问,没有重复,没有排序。LinkedHashSet
:没有重复,按插入顺序进行迭代。TreeSet
:无重复,按排序顺序进行迭代。
Java 集合迭代器
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-collection-iterators/
在本文中,我们将开始一个新系列,我们将深入研究 1.8 版中可用的 Java 不同的集合解决方案。 对于具体的实现(例如ArrayList
或TreeSet
),我们将为您提供一些用例,这些用例将在这些用例中脱颖而出,并在某些情况下您应使用不同的用例。 当然,我们将写关于 Java 8 引入的流利 API 的信息。
在本文中,我们将从Iterator
接口开始,它是 Java 中所有集合导航的基础。
关于迭代器
当您要处理各种集合时,迭代器是基本概念。 顾名思义,它用于遍历集合的元素。
此接口在 Java 的版本 1.2 中引入。 在此之前,您可以使用Enumeration
接口-该接口仍然存在,但建议您使用基于Iterator
接口的集合。
如果我们看一下接口定义,它非常苗条:
package java.util;
import java.util.function.Consumer;
public interface Iterator {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
该接口支持 4 种操作:
hasNext
:如果当前可迭代对象具有更多要访问的元素,则此方法返回true
。next
:此方法返回当前可迭代对象中的下一个对象。 如果没有更多对象,则将获得java.util.NoSuchElementException
。remove
:此方法用于从给定的可迭代对象中删除元素。 如您所见,这是默认方法。 这意味着,并非Iterator
接口的每个实现都必须实现此方法。 而且,如果未实现,则会得到一个java.lang.UnsupportedOperationException
。forEachRemaining
:此方法在此可迭代过程中的每个剩余元素上调用提供的操作-或直到该操作引发异常。
如您所见,此接口具有正确数量的方法,可让您开始进行迭代。 您唯一不能做的就是将元素添加到可迭代对象中。 但是我们可以忍受。
正如您已经在思考的那样,唯一的限制是只能进行正向迭代。
在本系列的后面部分,我们将看一下ListIterator
,它扩展了Iterator
,是专门处理列表遍历的专家,并添加了一些有用的方法。
示例
现在是时候使用Iterator
接口进行一些示例了。 因为我们没有引入其他任何集合,所以我们使用由java.util.Arrays.asList
创建的列表。
迭代
这是非常基本的用例:迭代:
Iterator
iterator = Arrays.asList("HEARTS", "SPADES", "CLUBS", "DIAMONDS").iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
如您所料,这将导致以下结果:
HEARTSSPADESCLUBSDIAMONDS
即使迭代很容易,也有一些陷阱:
Iterator suits = Arrays.asList("HEARTS", "SPADES", "CLUBS", "DIAMONDS").iterator();
Iterator ranks = Arrays.asList("2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A").iterator();
while (ranks.hasNext()) {while (suits.hasNext()) {System.out.println(suits.next() + " " + ranks.next());}}
结果是:
HEARTS 2SPADES 3CLUBS 4DIAMONDS 5
这有一个小错误,但是如果我们切换迭代器的执行顺序,则会出现错误:
Iterator suits = Arrays.asList("HEARTS", "SPADES", "CLUBS", "DIAMONDS").iterator();
Iterator ranks = Arrays.asList("2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A").iterator();
while (suits.hasNext()) {while (ranks.hasNext()) {System.out.println(suits.next() + " " + ranks.next());}}
结果是
HEARTS 2SPADES 3CLUBS 4DIAMONDS 5Exception in thread "main" java.util.NoSuchElementException at java.util.AbstractList$Itr.next(AbstractList.java:364)
这不是您想要的。
在循环后使用迭代器
开发人员在开始时遇到的一个常见问题是,他们在循环之后访问迭代器:
Iterator iterator = Arrays.asList(1, 4, 223, 56, 23, 88, 346, 78, 45, 33).iterator();
while (iterator.hasNext()) {iterator.next();}iterator.next();
结果取决于 Iterator 在不同异常中的实现,但是如果实现遵循接口的约定,则会得到 java.util.NoSuchElementException:
Exception in thread "main" java.util.NoSuchElementException at java.util.AbstractList$Itr.next(AbstractList.java:364)
这意味着,您只能将一个迭代器导航到末尾。
使用for
循环
解决上述问题的方法是使用for
循环。 除了解决上述问题之外,它还使您的代码更清晰,更易读。 并且避免在循环后使用迭代器导致异常:
for (Iterator ranks = Arrays.asList("2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A").iterator(); ranks.hasNext(); ) {
String rank = ranks.next();
for (Iterator suits = Arrays.asList("HEARTS", "SPADES", "CLUBS", "DIAMONDS").iterator(); suits.hasNext(); ) {
String suit = suits.next();System.out.println(suit + " " + rank);}}
另外,您可以利用增强的for
循环使您的解决方案更具可读性:
for (String rank : Arrays.asList("2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A")) { for (String suit : Arrays.asList("HEARTS", "SPADES", "CLUBS", "DIAMONDS")) { System.out.println(suit + " " + rank); }}
而且,作为一个很好的副作用,我们甚至不需要了解Iterator
接口的工作原理,它全都封装在 Java 运行时的精巧手下。
移除元素
另一个常见的问题是在使用集合删除元素时。 不会导致错误或复制列表的最可行解决方案是使用Iterator
接口:
List numbers = new ArrayList<>(Arrays.asList(1, 4, 223, 56, 23, 88, 346, 78, 45, 33));Iterator iterator = numbers.iterator();while (iterator.hasNext()) { if (iterator.next() > 100) { iterator.remove(); }}
上面的这段代码从列表中删除了所有大于 100 的元素。如您所见,这里我们需要将列表的整个创建包装到一个显式的ArrayList
构造器中。 这是因为java.util.Arrays
是如何工作的:它返回一个不支持修改的特殊ArrayList
。 因此,如果您尝试删除元素,则会出现异常。
同样,请注意如何使用迭代器!
List numbers = new ArrayList<>(Arrays.asList(1, 4, 223, 56, 23, 88, 346, 78, 45, 33));Iterator iterator = numbers.iterator();while (iterator.hasNext()) { if (iterator.next() < 0 || iterator.next() > 100) { iterator.remove(); }}
System.out.println(numbers);
此代码导致以下结果:
[1, 4, 223, 56, 23, 88, 346, 78, 45, 33]
如您所见,元素未更改。 为什么? 因为if
条件在条件的两边都调用iterator.next()
,在这种情况下,我们跳过了大的值。
如果列表中包含奇数个元素,我们将获得异常而不是意外的结果。
我们将在后面的文章中看到,有一种更好和更易读的方式从集合中删除元素。
forEachRemaining
forEachRemaining
方法是 Java 8 中的新增功能。它获取java.util.function.Consumer
作为参数,该参数将为迭代器的每个其余元素调用。
目前,我看不到任何可以从这种新方法中受益的用例,但是应该有 Java 开发人员将其添加到组合中。
无论如何,想像一下,您要打印迭代器的所有元素,这些元素在大于 100 的第一个元素之后。您可以这样进行:
也有一些不同的解决方案,但它们涉及自定义逻辑。 但是,使用这种新方法,您可以像下面这样解决它:
Iterator iterator = Arrays.asList(1, 4, 223, 56, 23, 88, 346, 78, 45, 33).iterator();while (iterator.hasNext()) { if (iterator.next() > 100) { break; }}
iterator.forEachRemaining(System.out::println);
结果是:
562388346784533
而且,如果迭代器结束了,并且没有增加目标的任何元素,那么它也不会引发异常:
Iterator iterator = Arrays.asList(1, 4, 223, 56, 23, 88, 346, 78, 45, 33).iterator();while (iterator.hasNext()) { if (iterator.next() > 500) { break; }}iterator.forEachRemaining(System.out::println);
总结
如果您有一个元素集合并且想要浏览它们 – 并从该集合中删除元素,则Iterator
接口是基本的解决方案。
这里的方法是有限的:您不能向该集合中添加元素,也不能向后导航...并且使用 Java 1.5 ,引入了新的集合接口,这使我们的开发人员的生活更加轻松。
在下一篇文章中,我们将研究java.util.Collection
和java.util.Iterable
接口,以为以后处理不同集合实现的文章奠定基础。
Java Hashmap
教程
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-hashmap/
Java HashMap
是基于哈希表的Map
实现。 这就是为什么面试官总是要求HashMap
和HashTable
之间存在差异的原因。 HashMap
基本上等于HashTable
,但以下两个区别除外。
- 当
HashTable
同步时,HashMap
不同步。 HashMap
允许为null
,而HashTable
不允许为null
。
HashMap
的重要属性
DEFAULT_INITIAL_CAPACITY |
默认初始容量(2 的 n 次幂)。 HashMap 可以包含许多元素。 |
MAXIMUM_CAPACITY |
HashMap 的最大容量(2 的 n 次幂)。 |
loadFactor |
定义HashMap 的阈值。 重新调整大小时将在HashMap 中发生。 |
DEFAULT_LOAD_FACTOR |
在HashMap 的构造器中未定义任何负载因子时将使用。 |
size |
HashMap 包含的键/值对映射数。 |
创建HashMap
在创建HashMap
时未定义任何参数时,将使用默认的初始容量(16)和默认的负载系数(0.75)。 该HashMap
最多可以包含 16 个元素,并且在插入第 13 个元素时会调整HashMap
的大小。 这是因为负载系数为 75% (.75),并且在添加第 13 个元素(12 + 1
)时将超过此阈值。
您还可以提供初始容量和loadFactor
。 但是初始容量不能超过最大容量(2 的 30 次幂),并且负载系数不能为零或负数。
HashMap
中元素的添加
为了添加一个元素,您需要提供 2 个东西:键和值。
键:将与指定值关联的键。 null
是允许的。
值:与指定键关联的值。
首先,HashMap
将为给定键生成一个哈希码,然后检查是否已经有与给定键相关联的值。 如果是,则它将返回已经关联的值。 否则,它将通过提供的键在HashMap
中增加值。
重点
HashMap
不会按Map
中的元素顺序提供任何保证(均值顺序会随时间变化)。HashMap
为获得和设置操作提供恒定时间性能(如果使用正确的哈希算法)。- 迭代集合所需的时间与
HashMap
的“容量”(它可以容纳的元素)和大小(它当前容纳的元素)成比例。 - 如果迭代性能更为重要,那么建议不要将初始容量设置得太高而将负载系数设置得太低。 由于性能与初始容量和负载系数成正比。
- 容量是哈希表中的存储桶数。
- 初始容量(默认值为 16)只是创建哈希表时的容量。
- 负载因子(默认值 .75)是衡量哈希表在自动增加其容量之前的填充程度的度量。
- 当哈希表中的条目数超过负载因子与当前容量的乘积时,哈希表将被重映射(即内部数据结构将被重建)。
- 使用“
Collections.synchronizedMap()
”方法使映射同步。 - 由
HashMap
类返回的迭代器为“故障快速”。 HashMap
由数组(Key
)和LinkedList
(Value
)支持。HashMap
使用hashcode
(使用键)来标识应在HashMap
中放置或检索对象的确切位置。- 最后,
HashCode
返回后备数组中的确切位置(索引)。 - 支持数组的大小固定。 因此,只要数组已满(此映射中的键数达到其阈值)。 将创建一个具有新容量的新数组,并将所有元素添加到该新数组中。
- 在两种情况下(添加和检索对象)都将使用
HashCode
,而在任何情况下均可以使用或可以不使用equals()
方法。 HashMap
中Key
的最佳候选者是具有正确实现Equals
和Hashcode
方法的不可变类(示例:字符串类)。- 更好的哈希码和
equals
方法实现是HashMap
的更好性能。 - 这样,所有
String
和原始类型的包装类将是HashMap
中键的理想选择。
什么是重新哈希
每个HashMap
都有预定义的大小(初始容量),以及在需要时(超过阈值限制时)增加此大小(负载系数)的逻辑。
示例:
使用以下配置创建HashMap
初始容量:16(默认初始容量)
负载系数:.75(默认负载系数)
在给定的HashMap
中添加第 13 个元素后,超过给定HashMap
的阈值限制,系统将创建一个新的后备键集数组(此数组的大小将是前一个数组的两倍)。 系统将不得不再次计算确切的存储桶,应放置上一个存储桶中的元素,并将旧HashMap
中的所有元素复制到新的HashMap
中。 整个过程称为重新哈希,因为会再次为每个元素计算Hashcode
。
因为超时的HashMap
可能会被重新散布并且顺序可能会发生变化。
HashMap
的示例
在此示例中,您将学到以下几点
- 如何迭代映射
- 迭代映射的不同方法
HashCode
和Equals
何时被调用。 (请特别注意HashCode
的输出和equals
方法调用)
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class HashMapExample {
public static void main(String[] args) {
Map<JBT, Integer> m1 = new HashMap<JBT, Integer>();
JBT t1 = new JBT(1, 2);
JBT t2 = new JBT(1, 3);
JBT t3 = new JBT(2, 1);
m1.put(t1, 1);
m1.put(t2, 2);
m1.put(t3, 3);
System.out.println("Addition Done");
/*
* Below you can find 3 different ways to iterate a Map. Uncomment
* different section and see the different in Output. Pay attention to
* when Hashcode and Equals is called
*/
/* Set s = m1.entrySet();
for (Iterator i = s.iterator(); i.hasNext();) {
Map.Entry me = (Map.Entry) i.next();
System.out.println(me.getKey() + " : " + me.getValue());
}
*/
/* for (Map.Entry<JBT, Integer> entry : m1.entrySet()) {
System.out.println("Key : " + entry.getKey() + " Value : "
+ entry.getValue());
}
*/
for (Object key : m1.keySet()) {
System.out.println("Key : " + key.toString() + " Value : "
+ m1.get(key));
}
}
}
class JBT {
JBT(int i, int j) {
this.i = i;
this.j = j;
}
int i, j;
@Override
public int hashCode() {
System.out.println("Inside HashCode Method");
int k = i + j;
return k;
}
@Override
public boolean equals(Object obj) {
System.out.println("Inside Equals Method");
if (i == ((JBT) obj).i && j == ((JBT) obj).j)
return true;
else
return false;
}
@Override
public String toString() {
return String.valueOf(i).concat(String.valueOf(j));
}
}
链表
原文: https://javabeginnerstutorial.com/data-structure/linked-list/
什么是链表?
链表是另一种数据结构,也是一种常见的数据结构,它包括按顺序分为两组的一组节点,每个节点由数据和下一个节点的地址部分组成,并形成一个链。 它用于创建树和图。
优点
-
本质上是动态的,并在需要时分配内存。
-
有两个可以在链表中轻松实现的操作,即插入和删除。
-
减少访问时间。
**缺点 **
-
内存浪费了,因为指针需要额外的存储空间。
-
该元素不能随机访问,可以顺序访问。
-
在链表中,反向遍历很困难。
在哪里使用链表?
-
它们用于实现栈,队列,图形等。
-
它们使您可以在列表的开头和结尾插入元素。
-
在此不需要事先知道大小。
链表的类型
单链表
这种类型的列表包含具有数据部分和地址部分的节点,即next
,它指向给定节点序列中的下一个节点。 我们可以对单链列表执行的操作包括插入,删除和遍历。
双链表
在这种类型的列表中,每个节点包含两个链接,第一个链接将指向序列中的上一个节点,下一个链接将指向序列中的下一个节点。
循环链表 - 在这种类型的列表中,列表的最后一个节点包含第一个节点的地址,并将形成一个循环链。
单链表
单链表是其中每个节点仅包含一个指向下一个节点的链接字段的列表。 在这种情况下,节点分为两部分,第一部分是数据部分,另一部分是包含下一个节点地址的链接部分。 第一个节点是标头节点,其中包含下一个节点的数据和地址,依此类推。 单链列表也称为单向列表,因为它只能从左到右遍历,而另一种方式则是不可能的。
在哪里使用单链表?
单链列表可以在使用后进先出概念的栈中使用。 此列表维护与列表中头节点的链接,每个节点都指向列表中的下一个节点。 它也可以用于先入先出的队列中。
在 C 中实现单链表
#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
int data;
struct Node *next;
}node;
void insert(node *ptr, int data)
{
while(ptr->next!=NULL)
{
ptr = ptr -> next;
}
ptr->next = (node *)malloc(sizeof(node));
ptr = ptr->next;
ptr->data = data;
ptr->next = NULL;
}
int find(node *ptr, int key)
{
ptr = ptr -> next;
while(ptr!=NULL)
{
if(ptr->data == key)
{
return 1;
}
ptr = ptr -> next;
}
return 0;
}
void delete(node *ptr, int data)
{
while(ptr->next!=NULL && (ptr->next)->data != data)
{
ptr = ptr -> next;
}
if(ptr->next==NULL)
{
printf("Element %d is not present in the list\n",data);
return;
}
node *temp;
temp = ptr -> next;
ptr->next = temp->next;
free(temp);
return;
}
void print(node *ptr)
{
if(ptr==NULL)
{
return;
}
printf("%d ",ptr->data);
print(ptr->next);
}
int main()
{
node *start,*temp;
start = (node *)malloc(sizeof(node));
temp = start;
temp -> next = NULL;
printf("1. Insert\n");
printf("2. Delete\n");
printf("3. Print\n");
printf("4. Find\n");
while(1)
{
int option;
scanf("%d",&option);
if(option==1)
{
int data;
scanf("%d",&data);
insert(start,data);
}
else if(option==2)
{
int data;
scanf("%d",&data);
delete(start,data);
}
else if(option==3)
{
printf("The list is ");
print(start->next);
printf("\n");
}
else if(option==4)
{
int data;
scanf("%d",&data);
int result = find(start,data);
if(result)
{
printf("The element is found \n");
}
else
{
printf("The element is not found\n");
}
}
}
}
双链表
双链列表也称为双向列表或双向链。 在双向链表中,两个链接字段被保留,而不是像在单个链表中那样保留一个链接字段。 双链表是一个线性数据结构,其中每个节点都有两个链接,其中第一个链接用于指向前一个节点,下一个链接指向下一个节点。 在双向链表上执行的操作是插入,删除,搜索和遍历。
在哪里使用双链表?
-
用于表示游戏中的纸牌。
-
在具有“最近使用”列表的应用中使用。
-
在 Word 或 Photoshop 中用作撤消功能。
-
在浏览器缓存中使用,它使我们可以单击后退按钮。
使用 C 实现双向链表
#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
int data;
struct Node *next;
struct Node *prev;
}node;
void insert(node *ptr, int data)
{
while(ptr->next!=NULL)
{
ptr = ptr -> next;
}
ptr->next = (node *)malloc(sizeof(node));
(ptr->next)->prev = ptr;
ptr = ptr->next;
ptr->data = data;
ptr->next = NULL;
}
int find(node *ptr, int key)
{
ptr = ptr -> next;
while (ptr!=NULL)
{
if(ptr->data == key)
{
return 1;
}
ptr = ptr -> next;
}
return 0;
}
void delete(node *ptr, int data)
{
while(ptr->next!=NULL && (ptr->next)->data != data)
{
ptr = ptr -> next;
}
if(ptr->next==NULL)
{
printf("The element %d is not present in the list\n",data);
return;
}
node *temp;
temp = ptr -> next;
ptr->next = temp->next;
temp->prev = ptr;
free(temp);
return;
}
void print(node *ptr)
{
if(ptr==NULL)
{
return;
}
printf("%d ",ptr->data);
print(ptr->next);
}
int main()
{
node *start,*temp;
start = (node *)malloc(sizeof(node));
temp = start;
temp -> next = NULL;
temp -> prev = NULL;
printf("1. Insert\n");
printf("2. Delete\n");
printf("3. Print\n");
printf("4. Find\n");
while(1)
{
int option;
scanf("%d",&option);
if(option==1)
{
int data;
scanf("%d",&data);
insert(start,data);
}
else if(option==2)
{
int data;
scanf("%d",&data);
delete(start,data);
}
else if(option==3)
{
printf("The list is ");
print(start->next);
printf("\n");
}
else if(option==4)
{
int data;
scanf("%d",&data);
int result = find(start,data);
if(result)
{
printf("The element is found\n");
}
else
{
printf("The element is not found\n");
}
}
}
}
循环链表
循环链表是有点复杂的链接数据结构。 在此列表中,我们可以在列表中的任何位置插入元素,而在数组中,我们不能在列表中的任何位置插入元素,因为它在连续内存中。 在此列表中,上一个元素存储下一个元素的地址,最后一个元素存储第一个元素的地址。 列表中的元素以圆形的方式相互指向,形成圆形的链。 该列表具有动态大小,这意味着可以在需要时分配内存。
在哪里使用循环链表?
使用此列表的实际应用是在其上运行多个应用的PC。 循环链表在操作系统中很常见,因为它会将正在运行的应用放在列表中,并且当列表即将到达其末端时,操作系统很容易使用循环链表,因为操作系统可以循环运行到列表的最前面。 将该时隙分配给列表中的每个应用。
在 C 中实现循环链表
#include<stdio.h>
#include<stdlib.h>
struct node
{
int data;
struct node *next;
}node;
void insert(node *pointer, int data)
{
node *start = pointer;
while(pointer->next!=start)
{
pointer = pointer -> next;
}
pointer->next = (node *)malloc(sizeof(node));
pointer = pointer->next;
pointer->data = data;
pointer->next = start;
}
void delete(node *pointer, int data)
{
node *start = pointer;
while(pointer->next!=start && (pointer->next)->data != data)
{
pointer = pointer -> next;
}
if(pointer->next==start)
{
printf("Element %d is not present in the list\n",data);
return;
}
node *temp;
temp = pointer -> next;
pointer->next = temp->next;
free(temp);
free( )
return;
}
void display(node *start,node *pointer)
{
if(pointer = = start)
{
return;
}
printf("%d ",pointer->data);
display(start,pointer->next);
}
void main()
{
node *start,*temp;
start = (struct node *)malloc(sizeof(struct node));
temp = start;
temp -> next = start;
printf("1. Insert\n");
printf("2. Delete\n");
printf("3. Display\n");
while(1)
{
int query;
scanf("%d",&query);
if(query==1)
{
int data;
scanf("%d",&data);
insert(start,data);
}
else if(query==2)
{ int data;
scanf("%d",&data);
delete(start,data);
}
else if(query==3)
{ printf("The list is ");
display(start,start->next);
printf("\n");
} }
getch( );
}
线性链表
在链表中,我们可以通过三种方式插入元素:
-
插入列表的开头。
-
插入列表的中间。
-
插入到列表的末尾。
在将节点插入列表之前,我们将使用关键字struct
创建一个结构。 例如:
struct node
{ int data;
struct node *next;
};
struct employee *start=NULL, *temp,*q;
并且在定义了结构之后,在从列表中插入或删除节点的同时,给出了特定的功能定义或功能原型。 例如:
void insertbeg( );
void insertmiddle( );
void insertlast( );
void deletebeg( );
上面给出的函数定义在main
函数之前定义,即void main()
或int main()
。
在开头的插入元素
在开始时插入节点的步骤:
-
创建一个新节点。
-
在数据部分输入数据。
-
将地址部分或下一部分设为
NULL
。 -
现在将这个新创建的节点附加到起始或头部。
-
现在,将此起始节点设为起始节点或标头节点。
在中间的插入元素
在中间插入节点的步骤:
-
将要添加的新节点的数据写入列表及其位置。
-
通过调用
malloc()
创建一个新的空节点。 -
将数据插入新节点的数据部分。
-
将此新节点添加到列表中的所需位置。
-
转到步骤 1,直到您在列表中添加了所有值。
实现
void insertmiddle()
{ int pos,i,num;
if(start==NULL)
{ printf("\nList is empty!! Sorry...");
}
temp=(struct node*)malloc(sizeof(struct node));
printf("\nEnter the details:");
scanf("%d",num);
printf("\nEnter position:");
scanf("%d",&pos);
temp->data=num;
q=start
for(i=1;i<pos-1;pos++)
{
if(q->next==NULL)
{ printf("\nLess elements in the list");
}
q=q->next;
}
temp->next=q->next;
q->next=temp;
getch();
}
在列表的末尾插入元素:
最后插入节点的步骤:
- 创建新节点。
- 将数据输入到节点的数据部分。
- 将节点的下一部分设为
NULL
。 - 要在最后一个位置插入节点,因此我们必须遍历到最后一个节点。
- 在最后一个节点和新节点之间建立链接。
实现
void insertlast()
{ int num;
temp=(struct node*)malloc(sizeof(struct node));
printf("\nEnter the details:");
scanf("%d",&num);
temp->data=num;
temp->next=NULL;
if(start==NULL) //If list is empty
{
start=temp;
}
else
{
q=start;
while(q->next!=NULL)
q=q->next;
q->next=temp;
}
}
删除
可以通过三种方式删除该元素:
-
从列表的开头删除。
-
从列表的中间删除。
-
从列表末尾删除。
从开头删除元素
Implementation
void deletebeg()
{ if(start==NULL)
{ printf("\nThe list is empty...");
}
else
{
q=start;
start=start->next;
free(q);
printf("\nElement deleted...");
}
}
从中间删除元素
实现
void deletemiddle()
{ int pos,i;
if(start==NULL)
{ printf("\nThe list is empty...");
}
printf("\nEnter position to delete:");
scanf("%d",&pos);
for(i=1;i<pos-1;pos++)
{
if(q->next==NULL)
{
printf("\nLess elements...");
getch();
}
q=q->next;
}
temp=q->next;
q->next=temp->next;
free(temp);
printf("\nElement deleted...");
getch();
}
从末尾删除元素
实现
void deletelast()
{ if(start==NULL)
{
printf("\nThe list is empty...");
}
else
{
q=start;
while(q->next->next!=NULL)
q=q->next;
temp=q->next;
q->next=NULL;
free(temp);
printf("\nElement deleted...");
}
}
显示
在执行任何操作后显示列表的元素。
void display()
{ struct node *q;
q=start;
while(q!=NULL)
{
printf("%d\t",q->data);
q=q->next;
}
getch();
}
Java 初学者List
集合教程
原文: https://javabeginnerstutorial.com/core-java-tutorial/list-collection-tutorial-for-java-beginners/
在这里,我们将详细了解List
接口及其不同实现。 java.util
包中的列表接口是java.util.Collection
接口的子类型。
List
接口要点
- 它是
java.util
包的一部分。 - 它是
java.util.Collection
接口的子类型。 - 列表是有序集合。 意味着可以按有序方式(按索引)访问
List
的元素。 - 允许重复值。
- 允许为
NULL
值。 List
有一个名为ListIterator
的特殊迭代器。 这将有助于双向遍历列表。List
可以完全控制每个元素的插入位置。
List
接口可用的操作
- 位置访问
- 搜索
- 迭代
- 范围视图
List
接口的具体实现
java.util.ArrayList
java.util.LinkedList
java.util.Vector
java.util.Stack
创建列表实例
List listA = new ArrayList();
List listB = new LinkedList();
List listC = new Vector();
List listD = new Stack();
在列表中添加元素
List listA = new ArrayList();
listA.add("element 1");
listA.add("element 2");
listA.add("element 3");
listA.add(0, "element 0");
访问元素
语法:E set(int index, E element);
用途:用于返回指定位置的元素
返回:返回给定列表中指定位置的元素。
替换元素
语法:E set(int index, E element);
用途:用于替换列表中指定位置的元素。
返回:返回替换的元素。
性能要点
- 列表接口具有搜索对象的方法。 但是不应使用相同的方法,因为它会降低性能,因为在某些实现中它将使用线性搜索。
- 列表可以包含另一个列表作为元素,但是
HashCode
和Equals
不适用于这些列表,因此不应使用相同的列表。
Java 初学者的Map
集合教程
原文: https://javabeginnerstutorial.com/core-java-tutorial/map-collection-tutorial-for-java-beginners/
Map
是 Java 集合框架下的一个接口。
Map
接口的主要特征
- 它是一个用于将键映射到值的对象。
- 映射不能包含重复值。
- 每个键最多可以映射到一个值。
- 该接口替代了
Dictionary
类,后者是一个抽象类,而不是接口。 Map
接口允许 3 个集合视图- 键集
- 值集
- 键值映射集
- 由于
Map
如上所述提供了 3 个视图,因此您可以迭代键,值或键值对。 - 可以在迭代过程中以安全的方式从
Map
中删除条目。 - 不允许映射包含自身作为键。
- 映射可以包含自身作为值。
- 一些映射实现可以包含的键和值有一些限制。
- 尝试插入不合格的键或值将引发非受检异常。
具体映射实现
HashMap
HashTable
EnumMap
IdentityHashMap
LinkedHashMap
Properties
TreeMap
WeakHashMap
ConcurrentSkipListMap
ConcurrentHashMap
Map
接口中的重要方法
put(K key, V value)
:使用指定的键将指定的值放入映射中。putAll(Map<? extends K, ? extends V> m)
:将所有映射从给定Map
复制到此Map
。keySet()
:返回包含此映射中的键集。values()
:返回一个集合,其中包含此Map
中的所有值。isEmpty()
:检查给定Map
中是否有任何元素。remove(Object key)
:从此映射中删除键的映射(如果存在)。containsKey(Object key)
:检查指定键是否存在任何映射。containsValue(Object value)
:检查是否存在用于指定对象的任何映射(可能不止一个)。get(Object key)
:返回与此键关联的值。clear()
:用于从给定映射中移除所有映射。
Java 初学者的Set
教程
原文: https://javabeginnerstutorial.com/core-java-tutorial/set-collection-tutorial-for-java-beginners/
Set
是 Java 集合框架的一部分。 Set
是java.util
包中的一个接口。 它实现了Collection
接口。
与集有关的要点
- 它不包含重复的元素。
- 一些
Set
实现对NULL
元素有限制。
Set
接口的具体实现
HashSet
LinkedHashSet
TreeSet
EnumSet
Set
接口中的重要方法
此接口不包含任何特殊方法。 它仅继承Collection
接口的所有方法。
HashSet
- 此类是 Java 集合框架的一部分。
- 此类实现
Set
接口。 - 它由哈希表支持。
- 它是无序的。 (随着时间的推移,顺序不可预测)
- 基本操作(如添加,删除,包含和大小)的恒定时间性能。
- 它不同步。
Iterator
方法返回的迭代器为快速失败。
构造器
HashSet(Collection<? extends E> c)
:构造一个新集合,其中包含指定集合中的元素。HashSet(int initialCapacity, float loadFactor)
:构造一个新的空集; 支持的HashMap
实例具有指定的初始容量和指定的负载系数。HashSet(int initialCapacity)
:构造一个新的空集。
LinkedHashSet
- 元素的顺序是可预测的。
- 它维护遍历所有条目的双链表。
- 元素的顺序将与插入顺序相同。
- 它允许
NULL
值。 - 与
HashSet
一样,它为基本操作提供恒定的时间性能。 - 链表上的迭代时间与大小成正比。
- 初始容量和负载因子决定
LinkedHashSet
的性能。 - 它不同步。
- 此类的
Iterator
方法返回的迭代器为快速失败。
构造器
LinkedHashSet(int initialCapacity, float loadFactor)
:构造一个具有指定初始容量和负载因子的新的空链接哈希集。LinkedHashSet(int initialCapacity)
:构造一个新的,空的,具有指定初始容量和默认加载因子 0.75 的链接哈希集。LinkedHashSet()
:构造一个新的空链接哈希集,默认初始容量为 16,负载因子为 0.75。LinkedHashSet(Collection<? extends E> c)
:构造一个新的链接哈希集,其元素与指定的集合相同。
Java 初学者的SortedSet
集合教程
该接口是 Java 集合接口的一部分。
SortedSet
的重要点
- 它提供其元素的顺序。
- 元素根据创建
SortedSet
时提供的Comparable
/Comparator
进行排序。 - 迭代器将以升序遍历集合。
- 此接口是
SortedMap
的集合类似物。 - 插入
SortedSet
中的元素需要实现Comparable
接口(或接受比较器接口)。 SortedSet
的所有实现应提供 4 个构造器。- 没有带参构造器
- 具有单个
Comparator
参数的构造器。 - 具有单个
Collection
参数的构造器。 - 具有单个
SortedSet
参数的构造器。
SortedSet
中的重要方法
Comparator<? super E> comparator()
:返回用于对Set
中的元素进行排序的比较器。subSet(E fromElement, E toElement)
:返回此集合部分的视图,其元素范围从fromElement
(包括)到toElement
(不包括)。headSet(E toElement)
:返回此集合中的元素严格小于toElement
的部分的视图。tailSet(E fromElement)
:返回此集合中其元素大于或等于fromElement
的部分的视图
Java 初学者SortedMap
集合教程
SortedMap
的要点
- 此接口是
SortedSet
的映射类似物。 - 它按键提供顺序。
- 排序标准将是创建时提供的自然顺序或
Comparator
。 - 其中使用的键必须实现
Comparable
或接受指定的比较器。 - 该图中的键必须相互可比较。
- 使用
Map
接口equals
方法比较两个键,而在SortedMap
中使用compare
/compareTo
方法(来自Comparable
或Comparator
)。 - 需要由具体类实现的构造方法
- 无参构造器
- 具有单个
Comparator
参数的构造器。 - 具有单个
Map
参数的构造器。 - 具有单个
SortedMap
参数的构造器。
SortedMap
是 Java 集合框架的一部分。
SortedMap
接口中的重要方法
subMap(K fromKey, K toKey)
:返回此映射部分的视图,其元素范围从fromElement
(包括)到toElement
(不包括)。headMap(K toKey)
:返回此映射部分的视图,其元素严格小于toElement
。tailMap(K fromKey)
:返回此图的一部分,其元素大于或等于fromElement
的视图。
Java 教程 – 序列化
Java 序列化概念和示例
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-serialization-concept-example/
在这里,我将学习和教你什么是 Java 的序列化以及如何编写相同的代码。
什么是序列化
Java 序列化是一个过程,其中对象的当前状态将保存在字节流中。 字节流是平台无关的,因此一旦在一个系统中创建了对象,便可以在其他平台上反序列化。
序列化有什么用
如上所述,序列化会将对象状态转换为字节流。 该字节流可用于其他目的。
- 写入磁盘
- 存储在内存中
- 通过网络将字节流发送到其他平台
- 将字节流保存在 DB 中(作为 BLOB)
Java 中的序列化和反序列化
现在我们知道什么是序列化了。 但是我们需要了解如何用 Java 实现它以及它如何工作。
Java 已经提供了开箱即用的方式(java.io.Serializable
接口)来序列化对象。 如果要序列化任何类,则该类需要实现给定的接口。
注:Serializable
接口是标记接口。 因此,Serializable
接口中没有任何方法。
Java 类序列化代码
Employee.java
package com.jbt;
import java.io.Serializable;
public class Employee implements Serializable
{
public String firstName;
public String lastName;
private static final long serialVersionUID = 5462223600l;
}
SerializaitonClass.java
package com.jbt;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class SerializaitonClass {
public static void main(String[] args) {
Employee emp = new Employee();
emp.firstName = "Vivekanand";
emp.lastName = "Gautam";
try {
FileOutputStream fileOut = new FileOutputStream("./employee.txt");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(emp);
out.close();
fileOut.close();
System.out.printf("Serialized data is saved in ./employee.txt file");
} catch (IOException i) {
i.printStackTrace();
}
}
}
DeserializationClass.java
package com.jbt;
import java.io.*;
public class DeserializationClass {
public static void main(String[] args) {
Employee emp = null;
try {
FileInputStream fileIn = new FileInputStream("./employee.txt");
ObjectInputStream in = new ObjectInputStream(fileIn);
emp = (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("Deserializing Employee...");
System.out.println("First Name of Employee: " + emp.firstName);
System.out.println("Last Name of Employee: " + emp.lastName);
}
}
首先,运行“SerializaitonClass
”,您将创建“employee.txt
”文件。
第二次运行“DeserializationClass
”,Java 将反序列化该类,并在控制台中显示该值。
输出将是
Deserializing Employee...
First Name of Employee: Vivekanand
Last Name of Employee: Gautam
项目要点
- 序列化接口需要使对象序列化。
- 瞬态实例变量并未以对象状态序列化。
- 如果超类实现了
Serializable
,则子类也可以自动进行Serializable
。 - 如果无法对超类进行序列化,则在对子类进行反序列化时,将调用超类的默认构造器。 因此,所有变量将获得默认值,引用将为
null
。
在下一篇文章中,我们将讨论瞬态变量的使用。
- 序列化接口需要使对象序列化。
- 瞬态实例变量并未以对象状态序列化。
- 如果超类实现了
Serializable
,则子类也可以自动进行Serializable
。 - 如果无法对超类进行序列化,则在对子类进行反序列化时,将调用超类的默认构造器。 因此,所有变量将获得默认值,引用将为
null
。
Java 序列化概念和示例第二部分
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-serialization-concept-example-part-ii/
在第一部分中,我们讨论了什么是序列化以及如何在 Java 中实现。 在本文中,我们将讨论一些涉及序列化的高级主题。
在本文中,我们将使用与上一篇文章相同的代码库。
使用serialVersionUID
您必须已经看到在源代码中使用了一个名为“serialVersionUID
”的变量。 使用此变量有特定的原因。
serialVersionUID
是通过序列化运行时与每个可序列化类关联的版本号。 在反序列化过程中使用此版本号来验证序列化对象的发送者和接收者是否已为该对象加载了与序列化兼容的类。
- 在可序列化类中定义
serialVersionUID
字段不是强制性。 - 如果可序列化的类具有显式的
serialVersionUID
,则此字段的类型应为long
,并且必须是静态且最终的。 - 如果没有明确定义的
serialVersionUID
字段,则序列化运行时将计算该类的默认值。 随编译器的实现而有所不同。 因此建议定义serialVersionUID
。 - 建议对
serialVersionUID
使用私有访问修饰符。 - 数组类无法声明显式的
serialVersionUID
,因此它们始终具有默认的计算值,但是对于数组类,无需匹配serialVersionUID
值。 - 如果已加载的接收器类的
serialVersionUID
与相应的发送器类之间存在差异,则将引发InvalidClassException
。
瞬态的使用
我们可以使用Serializable
保存对象的状态。 但是,如果我不想保存字段状态怎么办? 在这种情况下,可以像下面这样使用瞬态修饰符。 序列化过程中不会保存瞬态字段状态,反序列化时会将默认值分配给同一变量。
使用瞬态变量更改Employee
类。
package com.jbt;
import java.io.Serializable;
public class Employee implements Serializable
{
public String firstName;
/*
* Here transient modifier is used for lastName variable.
* This variable's state will not be saved while serialzation.
* While De-Serialization process default value will be provide.
* null in this case.
*/
transient public String lastName;
private static final long serialVersionUID = 5462223600l;
}
如果您执行相同的类(SerializaitonClass
&DeserializationClass
),则输出将与之前的代码不同。
Deserializing Employee...
First Name of Employee: Vivekanand
Last Name of Employee: null
如您所见,姓氏为null
,因为在序列化过程中未保存该变量的状态。
类层次结构和可序列化
在这里,我将讨论Serializable
接口对类层次结构的影响。 如果一个类实现了Serializable
接口,则可以保存该类的状态。 但是,如果同一类扩展了另一个未实现Serializable
接口的类,则不会保存超类的状态。
为了了解区别,我们将更新原始的Employee
类。 现在,该类将扩展另一个类superEmployee
。 该超类将不会实现Serializable
接口。
package com.jbt;
import java.io.Serializable;
public class Employee extends superEmployee implements Serializable {
public String firstName;
private static final long serialVersionUID = 5462223600l;
}
class superEmployee {
public String lastName;
}
如果您一个接一个地执行“SerializaitonClass
”和“DeserializationClass
”,那么输出将如下所示
Deserializing Employee...
First Name of Employee: Vivekanand
Last Name of Employee: null
瞬态与静态变量
我已经写了一篇完整的文章。 请在此处访问。
Java 瞬态与静态变量
原文: https://javabeginnerstutorial.com/core-java-tutorial/transient-vs-static-variable-java/
在这里,我将向您展示静态和瞬态之间的区别。 无论如何,这两件事在范围上是完全不同的,但是人们经常会问我这个问题。 在本文的这篇文章中,我将尝试对其进行解释。 在之前的文章中,您可以在此处单击(第一部分),在此处单击以获取(第二部分)。
静态变量
静态变量属于一个类,而不属于任何单个实例。 序列化的概念与对象的当前状态有关。 仅与类的特定实例关联的数据被序列化,因此静态成员字段在序列化过程中将被忽略。
瞬态变量
如果您不想保存变量的状态,请在序列化时使用。 您必须将该变量标记为Transient
。 环境将知道应忽略此变量,并且不会保存相同的值。
即使概念是完全不同的,并且不进行储蓄的原因也不同,人们仍然对两者的存在感到困惑。 与这两种情况一样,变量值将不会保存。
两者之间的区别
源代码:注意每个代码更改。
Employee.java
package com.jbt;
import java.io.Serializable;
public class Employee extends superEmployee {
public String firstName;
private static final long serialVersionUID = 5462223600l;
}
class superEmployee implements Serializable{
public String lastName;
static String companyName;
transient String address;
static transient String companyCEO;
}
SerializaitonClass.java
package com.jbt;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class SerializaitonClass {
public static void main(String[] args) {
Employee emp = new Employee();
emp.firstName = "Vivekanand";
emp.lastName = "Gautam";
emp.companyName = "JBT";
//Below part needs to be removed in case address field is made final
emp.address = "MUM";
emp.companyCEO = "ME CEO";
try {
FileOutputStream fileOut = new FileOutputStream("./employee.txt");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(emp);
out.close();
fileOut.close();
System.out.printf("Serialized data is saved in ./employee.txt file");
} catch (IOException i) {
i.printStackTrace();
}
}
}
DeserializationClass.java
package com.jbt;
import java.io.*;
public class DeserializationClass {
public static void main(String[] args) {
Employee emp = null;
try {
FileInputStream fileIn = new FileInputStream("./employee.txt");
ObjectInputStream in = new ObjectInputStream(fileIn);
emp = (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("Deserializing Employee...");
System.out.println("First Name of Employee: " + emp.firstName);
System.out.println("Last Name of Employee: " + emp.lastName);
System.out.println("Company Name: "+emp.companyName);
System.out.println("Company CEO: "+emp.companyCEO);
System.out.println("Company Address: "+emp.address);
}
}
首先执行“SerializaitonClass
”,您将获得以下输出。
Serialized data is saved in ./employee.txt file
其次,执行“DeserializationClass
”,您将获得以下输出
Deserializing Employee...
First Name of Employee: Vivekanand
Last Name of Employee: Gautam
Company Name: null
Company CEO: null
Company Address: null
从输出中可以看到,只有姓氏值被保存。 静态和瞬态变量值均未保存。
现在,我将稍作更改代码,看看会发生什么。
Employee.java
package com.jbt;
import java.io.Serializable;
public class Employee extends superEmployee {
public String firstName;
private static final long serialVersionUID = 5462223600l;
}
class superEmployee implements Serializable {
public String lastName;
/*
* Here i am providing the value of company name,companyCEO and address
* while defining these variables.
*/
static String companyName = "TATA";
transient String address = "DEL";
static transient String companyCEO = "Jayshree";
}
再次执行相同的代码并查看输出
Deserializing Employee...
First Name of Employee: Vivekanand
Last Name of Employee: Gautam
Company Name: TATA
Company CEO: Jayshree
Company Address: null
非常仔细地查看输出。 此处已保存companyName
,companyCEO
的值,但未保存地址的值。 另外,检查这些变量的保存值。
Company Name: TATA
Company CEO: Jayshree
在这两种情况下,此处存储的case
值均来自类(Employee
类),而不是对象(emp
对象)。 此外,即使是临时变量,也会保存companyCEO
变量值。 因为static
修饰符会更改此变量的行为。
重点
- 静态变量无法序列化。
- 在反序列化时,如果在基类的初始化过程中提供了相同的值,则可用于静态变量。
- 这并不意味着静态变量将被序列化。 这仅意味着静态变量将使用相同的值初始化,并在加载类时分配(在这种情况下为
TATA
)。 如果之前未加载类(新 JVM)。 请注意示例代码。 - 如果类已经在 JVM 中加载并且静态变量值已更改,那么将显示更改后的值。
- 这并不意味着静态变量将被序列化。 这仅意味着静态变量将使用相同的值初始化,并在加载类时分配(在这种情况下为
- 如果将变量定义为“静态”和“瞬态”,则静态修饰符将控制变量的行为,而不是瞬态。
- 静态和瞬态是不同的。 在某些情况下,它们的行为看起来相同,但并非总是如此。 如果在加载类时为变量分配了一个值,则在反序列化类时该值将分配给静态变量,而不是瞬时的。 因此,如果您同时使用这两个修饰符和变量,那么从某种意义上来说,我要说的是静态将优先于瞬态。
- 瞬态变量值将不会保存。 同样,在反序列化过程中不能为其分配任何值。 这与静态行为不同。
最终修饰符对序列化的影响
要查看Final
修饰符的效果,我再次更改Employee
类的代码。
Employee.java
package com.jbt;
import java.io.Serializable;
public class Employee extends superEmployee {
public String firstName;
private static final long serialVersionUID = 5462223600l;
}
class superEmployee implements Serializable {
public String lastName;
/*
* Here i am providing the value of company name,companyCEO and address
* while defining these variables.
* I am making address as final here
*/
static String companyName = "TATA";
transient final String address = "DEL";
static transient String companyCEO = "Jayshree";
}
再次执行代码,看看区别。 上面代码的输出将是
Deserializing Employee...
First Name of Employee: Vivekanand
Last Name of Employee: Gautam
Company Name: TATA
Company CEO: Jayshree
Company Address: DEL
如您所见,序列化过程中还保存了地址字段,因为它现在是最终的。
接口和最终
我看到了一种情况,您可以序列化未序列化的接口内部的变量。
Employee.java
package com.jbt;
import java.io.Serializable;
public class Employee extends superEmployee implements variableConstant{
public String firstName;
private static final long serialVersionUID = 5462223600l;
}
class superEmployee implements Serializable {
public String lastName;
/*
* Here i am providing the value of company name,companyCEO and address
* while defining these variables.
* I am making address as final here
*/
static String companyName = "TATA";
transient final String address = "DEL";
static transient String companyCEO = "Jayshree";
}
interface variableConstant {
public static final String education = "MCA";
}
DeserializationClass.java
package com.jbt;
import java.io.*;
public class DeserializationClass {
public static void main(String[] args) {
Employee emp = null;
try {
FileInputStream fileIn = new FileInputStream("./employee.txt");
ObjectInputStream in = new ObjectInputStream(fileIn);
emp = (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("Deserializing Employee...");
System.out.println("First Name of Employee: " + emp.firstName);
System.out.println("Last Name of Employee: " + emp.lastName);
System.out.println("Company Name: "+emp.companyName);
System.out.println("Company CEO: "+emp.companyCEO);
System.out.println("Company Address: "+emp.address);
System.out.println("Education: "+emp.education);
}
}
执行以上代码,然后查看输出。
Deserializing Employee...
First Name of Employee: Vivekanand
Last Name of Employee: Gautam
Company Name: TATA
Company CEO: Jayshree
Company Address: DEL
Education: MCA
在这里,您可以看到教育的值得以保存。 此值是接口的一部分。 但是由于这是恒定的,因此在序列化时将其保存。
I think I have covered all possible scenarios. Do let me know in case any particular scenario is not covered in this article. Feel free to say me hi, if you like this article. And yes I didn’t qualify for the interview. 😛
serialVersionUID
的用途是什么
原文: https://javabeginnerstutorial.com/core-java-tutorial/use-serialversionuid/
在这里,我将讨论在可序列化类中使用的变量serialVersionUID
的重要性。
下面是一个使您了解变量的确切用法的示例。
示例代码
Employee.java
package com.jbt;
import java.io.Serializable;
public class Employee implements Serializable
{
public String firstName;
public String lastName;
private static final long serialVersionUID = 5462223600l;
}
SerializaitonClass.java
(此类将用于序列化)
package com.jbt;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class SerializaitonClass {
public static void main(String[] args) {
Employee emp = new Employee();
emp.firstName = "Vivekanand";
emp.lastName = "Gautam";
try {
FileOutputStream fileOut = new FileOutputStream("./employee.txt");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(emp);
out.close();
fileOut.close();
System.out.printf("Serialized data is saved in ./employee.txt file");
} catch (IOException i) {
i.printStackTrace();
}
}
}
DeserializationClass.java
(此类将用于反序列化)
package com.jbt;
import java.io.*;
public class DeserializationClass {
public static void main(String[] args) {
Employee emp = null;
try {
FileInputStream fileIn = new FileInputStream("./employee.txt");
ObjectInputStream in = new ObjectInputStream(fileIn);
emp = (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("Deserializing Employee...");
System.out.println("First Name of Employee: " + emp.firstName);
System.out.println("Last Name of Employee: " + emp.lastName);
}
}
现在执行“SerializationClass.java
”,然后执行“DeserializationClass.java
”。 它将首先创建一个具有Employee
对象状态的文件,然后在反序列化时从同一文件创建一个对象。 输出将如下所示。
Serialized data is saved in ./employee.txt file
Deserializing Employee...
First Name of Employee: Vivekanand
Last Name of Employee: Gautam
现在让我们尝试从Employee.java
文件中删除“serialVersionUID
”变量,然后再次运行“SerializationClass.java
”文件。 它将再次创建带有对象状态的“employee.txt
”文件。 现在让我们在Employee
类中假设字符串地址中添加一个新变量。
Employee.java
package com.jbt;
import java.io.Serializable;
public class Employee implements Serializable
{
public String firstName;
public String lastName;
public String Address;
//Variable is commented
// private static final long serialVersionUID = 5462223600l;
}
现在运行“DeserializationClass.java
”并查看输出。
java.io.InvalidClassException: com.jbt.Employee; local class incompatible: stream classdesc serialVersionUID = 5462223600, local class serialVersionUID = -3607530122250644586
at java.io.ObjectStreamClass.initNonProxy(Unknown Source)
at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)
at java.io.ObjectInputStream.readClassDesc(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at com.jbt.DeserializationClass.main(DeserializationClass.java:11)
它将引发不兼容的异常。 因为给定的类Employee.java
在序列化和反序列化过程之间进行了更改。 因此,系统无法识别它仍然是同一类。 为了使我们的系统了解到它是同一类,您必须在一个类中使用serialVersionUID
变量。
您可以尝试执行上述步骤,但保持serialVersionUID
不变,并查看输出。 反序列化过程将正常进行。
项目要点
- 在可序列化类中定义
serialVersionUID
字段不是必需的。 - 如果可序列化的类具有显式的
serialVersionUID
,则此字段的类型应为long
,并且必须是静态且最终的。 - 如果没有明确定义的
serialVersionUID
字段,则序列化运行时将计算该类的默认值。 该值可以根据编译器的实现而变化。 因此,建议定义serialVersionUID
。 - 建议对
serialVersionUID
使用私有访问修饰符。 - 不同的类可以具有相同的
serialVersionUID
。 - 数组类无法声明显式的
serialVersionUID
,因此它们始终具有默认的计算值,但是对于数组类,无需匹配serialVersionUID
值。 - 如果已加载的接收器类的
serialVersionUID
与相应的发送器类之间存在差异,则将引发InvalidClassException
。 - 如果要禁止序列化同一类旧版本的新类,则应对同一类的不同版本使用不同的
serialVersionUID
。
@SuppressWarnings("serial")
如果您没有在应该序列化的类中提供serialVersionId
,则编译器将给出有关该序列的警告消息。 如果要覆盖此警告,则可以使用给定的注解。 使用后,编译器将不再报错缺少的serialVersionUID
。
Java 教程 – 枚举
Java 枚举(enum
)
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-enum-enumerations/
在本文中,我将介绍 Java 枚举,这是在应用中定义和使用常量的最优雅的方式。
这是每个人都知道的基本功能,但还有一些您可能不知道的功能。
为什么要使用枚举?
好吧,您在 Java 代码中使用了枚举。 如果您不这样做,那么您做错了什么,或者拥有非常简单的应用而没有太多复杂功能。
无论如何,让我们看一下基础知识。 例如,您想要一个使用工作日的类。 您可以这样定义它:
public class Schedule {
private ??? workday;
}
要存储工作日,我们创建一个工具类来存储工作日的常量:
public class Workdays {
public static final String MONDAY = "Monday";
public static final String TUESDAY = "Tuesday";
public static final String WEDNESDAY = "Wednesday";
public static final String THURSDAY = "Thursday";
public static final String FRIDAY = "Friday";
}
现在,Schedule
类将如下所示:
public class Schedule {
// Workdays.MONDAY, Workdays.TUESDAY
// Workdays.WEDNESDAY, Workdays.THURSDAY
// Workdays.FRIDAY
private String workday;
}
我想您已经看到了这种方法的缺点:即使Workdays
类中未定义工作日,也可以轻松地将工作日设置为“星期六”或“星期日”。 此解决方案不是值安全的。 这不是我们想要实现的。
为了解决这个问题,我们将Workdays
类转换为一个枚举:
public enum Workday {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY
}
所需的打字次数更少,现在该解决方案是值安全的。 我们只需要调整Schredule
类:
public class Schedule {
private Workday workday;
public Workday getWorkday() {
return this.workday;
}
public void setWorkday(Workday workday) {
this.workday = workday;
}
}
条件和枚举
枚举非常适合条件表达式(if
语句或switch
块)。 关于枚举的好处是它们是常量值,因此您可以将它们与==
运算符进行比较,该运算符比使用equals()
更优雅-并且避免了可能的NullPointerExceptions
。 顺便说一句:如果您查看Enum
类equals()
的实现,您将看到以下内容:
public final boolean equals(Object other) {
return this==other;
}
因此,请自由使用==
,因为这是equals()
的默认实现。
让我们看一下如何使用它们的示例:
if(currentSchedule.getWorkday() == Workday.FRIDAY) {
System.out.println("Hurray! Tomorrow is weekend!");
}
或在switch
块中:
switch(currentSchedule.getWorkday()) {
case MONDAY:
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
System.out.println("Working and working and...");
break;
case FRIDAY:
System.out.println("Hurray! Tomorrow is weekend!");
break;
default:
System.out.println("Unknown day -.-");
}
迭代枚举
迭代枚举非常简单。 枚举类定义了一个称为values()
的方法,该方法返回给定枚举的值。 最好是看一个示例:
for(Workday w : Workday.values()) {
System.out.println(w.name());
}
上面的示例将产生以下输出:
MONDAY
TUESDAY
WEDNESDAY
THURSDAY
FRIDAY
如您所见,values()
的顺序与枚举本身中定义的顺序相同。 因此,Java 不会进行任何魔术排序。
枚举字段
有时,您只想将枚举打印到控制台(或以某种 UI 形式显示)。 在上面的示例(工作日)中,您可以简单地使用枚举的名称,尽管有时“TUESDAY
”似乎很喊,而“Tuesday
”更可取。 在这种情况下,您可以添加和使用Enum
对象的自定义字段:
public enum Workday {
MONDAY("Monday"),
TUESDAY("Tuesday"),
WEDNESDAY("Wednesday"),
THURSDAY("Thursday"),
FRIDAY("Friday");
private final String representation;
private Workday(String representation) {
this.representation = representation;
}
}
如您在上面的示例中所看到的,枚举获取一个私有字段,在这种情况下称为表示形式。 该字段是最终字段,因此以后无法更改,这意味着必须在枚举构造期间初始化此属性。 这是通过构造器完成的,并且提供了构造器参数以及枚举定义。
您可以根据需要在枚举中拥有任意数量的属性/字段,但是我建议您将这个数量保持在较低的水平,因为具有 15 个额外属性的枚举确实很奇怪。 在这种情况下,我将考虑使用一个类和/或多个枚举来保存相同的信息。
枚举方法
枚举字段很好,但是如何访问该字段? 我告诉过您,新的表示形式是表示该枚举的值,但是当前我们无法在枚举本身之外访问该属性。
除此之外,还有一个基本方法可用于所有枚举:name()
以字符串形式返回当前值的名称。 这意味着在基本情况下,您可以使用此方法显示枚举的值(例如,在 UI 或日志条目中)。 当然也存在toString()
函数,但是有时开发人员会覆盖它,以使其对程序员更友好(或用户友好?)显示。 作为最佳实践,如果要显示枚举的名称,建议您使用name()
方法而不是toString()
。
要从上面更改表示示例(当我们遍历values()
时),只需编写一个函数,该函数将返回新的变量表示并在迭代中使用它:
public enum Workday {
MONDAY("Monday"),
TUESDAY("Tuesday"),
WEDNESDAY("Wednesday"),
THURSDAY("Thursday"),
FRIDAY("Friday");
private final String representation;
private Workday(String representation) {
this.representation = representation;
}
public String getRepresentation() {
return this.representation;
}
}
现在更新迭代:
for(Workday w : Workday.values()) {
System.out.println(w.getRepresentation());
}
现在,结果如下:
Monday
Tuesday
Wednesday
Thursday
Friday
但这不是唯一可以实现枚举方法的用法。 在下一节中,我们将看到如何将Enum
值映射到String
并返回。
实现接口
关于枚举鲜为人知的一件事是它们可以实现接口。 这意味着,如果您需要不同枚举所需要的通用功能,则可以使用一个接口定义它,而枚举必须在该接口中实现方法。
一种这样的用例是使用 JPA 将枚举值存储在数据库中-尽管不是按名称或顺序(可通过@Enumeration
和EnumType
获得),而是通过短代码。
在这种情况下,您可以创建一个接口,其中包含将枚举转换为数据库表示形式并将枚举转换回的方法。
public interface DatabaseEnum<T extends Enum<T>> {
/**
* Converts the database representation back to the enumeration value
*/
T fromDatabase(String representation);
/**
* Converts the enum value to the database representation
*/
String toDatabaseString();
}
该接口使用泛型(由T
类型表示),并在String
和枚举类型之间进行转换。
要实现此接口,请扩展“工作日”示例:
public enum Workday implements DatabaseEnum<Workday>{
MONDAY("Monday"),
TUESDAY("Tuesday"),
WEDNESDAY("Wednesday"),
THURSDAY("Thursday"),
FRIDAY("Friday");
private final String representation;
private Workday(String representation) {
this.representation = representation;
}
public String getRepresentation() {
return this.representation;
}
@Override
public String toDatabaseString() {
return this.representation;
}
@Override
public Workday fromDatabase(String representation) {
for(Workday wd: Workday.values()) {
if(wd.representation.equals(representation)) {
return wd;
}
}
return null;
}
}
使用枚举而不是布尔值
有时,您最终得到一个采用布尔表达式的方法(或者您仅获得一个告诉您使用布尔值的项目说明,但感觉很痒)。 在这种情况下,可以随意引入一个新的枚举并使用正确的值而不是布尔值。
例如,一旦我有了一个规范,告诉我必须创建一个带有一些参数的方法和一个布尔值,称为“rightHandSide
”。 实际上,“rightHandSide
”的默认值为false
。 首先,我按照规范中的说明实现了该方法,但是这对我来说并不舒服,我认为这是摆脱这种冷酷感觉的另一种方法:
public void someMethod(String someParameter, boolean rightHandSide) {
if(!rightHandSide) {
// do something
}
}
好吧,这似乎并不坏,但是如何扩展功能呢? 如果一方不重要怎么办? 在这种情况下,可以使用其他布尔参数扩展该方法,但从长远来看,它不会成功。 而且因为“rightHandSide
”的默认值为false
,所以我需要创建一个未提供参数的调用,并且使用false
调用默认方法。
因此,我引入了一个名为Side
的新枚举,并替换了布尔参数:
public enum Side {
RIGHT_HAND,
LEFT_HAND
}
public void someMethod(String someParameter, Side side) {
if(side == Side.RIGHT_HAND) {
// do something
}
}
现在感觉好多了,以后可以将该方法扩展到更多情况。
总结
如您所见,枚举非常适合替换常量-有时也可以使用布尔方法参数。
Java 枚举示例
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-enum-example/
/**
* @author GHajba
*
*/
public class Main {
public static void main(final String... args) {
Schedule currentSchedule = new Schedule();
currentSchedule.setWorkday(Workday.FRIDAY);
System.out.println("--------");
if (currentSchedule.getWorkday() == Workday.FRIDAY) {
System.out.println("Hurray! Tomorrow is weekend!");
}
System.out.println("--------");
switch (currentSchedule.getWorkday()) {
case MONDAY:
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
System.out.println("Working and working and...");
break;
case FRIDAY:
System.out.println("Hurray! Tomorrow is weekend!");
break;
default:
System.out.println("Unknown day -.-");
}
System.out.println("--------");
System.out.println("Displaying all Workdays:");
for (Workday w : Workday.values()) {
System.out.println(w);
}
System.out.println("--------");
System.out.println("Displaying all Workdays:");
for (Workday w : Workday.values()) {
System.out.println(w.getRepresentation());
}
}
}
Schedule.java
/**
* @author GHajba
*
*/
public class Schedule {
private Workday workday;
public Workday getWorkday() {
return this.workday;
}
public void setWorkday(final Workday workday) {
this.workday = workday;
}
}
Workday.java
/**
* @author GHajba
*
*/
public enum Workday implements DatabaseEnum<Workday> {
MONDAY("Monday"),
TUESDAY("Tuesday"),
WEDNESDAY("Wednesday"),
THURSDAY("Thursday"),
FRIDAY("Friday");
private final String representation;
private Workday(final String representation) {
this.representation = representation;
}
public String getRepresentation() {
return this.representation;
}
@Override
public String toString() {
return getRepresentation();
}
@Override
public String toDatabaseString() {
return this.representation;
}
@Override
public Workday fromDatabase(final String representation) {
for (Workday wd : Workday.values()) {
if (wd.representation.equals(representation)) {
return wd;
}
}
return null;
}
}
核心 Java 教程 – 线程
Java 线程教程
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-thread-tutorial/
可以以两种方式使用线程术语
- 类
java.lang.Thread
的实例 - 执行线程
线程的实例是一个对象,与 Java 中的其他任何对象一样,它包含在堆上生存和死亡的变量和方法。 但是执行线程是一个单独的进程,具有其栈调用。 即使您没有在程序中创建任何线程,一个线程也将在其中运行,然后main()
方法将启动该线程。
注意:如果是线程,大多数事情都是不可预测的。
线程类型
线程可以是两种类型。
- 用户线程
- 守护程序线程
这两类线程之间的区别在于,JVM 仅在所有用户线程完成时才退出应用。 JVM 并不关心守护线程的状态。
线程定义
可以通过两种方式定义线程
- 扩展
java.lang.Thread
类。 - 实现
Runnable
接口。
扩展 Java 线程类:
该类需要扩展,线程类并覆盖run()
方法。
class MyThread extends Thread
{
public void run()
{
System.out.println("Important job running in MyThread");
}
}
但是这种方法的问题是类不能再扩展任何类。
线程的
run()
方法可以在类中重载。 但是,JVM 仅考虑run()
方法(不带参数)。 任何其他覆盖的方法都需要显式调用。
实现Runnable
接口
该类需要实现Runnable
接口,并且覆盖run()
方法。以这种方式创建线程使您可以灵活地扩展所需的任何类。
class MyRunnable implements Runnable {
public void run() {
System.out.println("Important job running in MyRunnable");
} }
实例化线程:
每个执行线程都以Thread
类的实例开始。 无论如何,都需要在创建执行线程之前创建线程对象。
两种情况下创建线程对象均不同。 如果扩展了Thread
类,则可以使用new
关键字直接启动类,因为它已经扩展了Thread
类本身。
MyThread t = new MyThread()
如果实现Runnable
接口。 首先创建的可运行类需要实例化。
MyRunnable r = new MyRunnable();
现在将此可运行对象传递给Thread
。
Thread t = new Thread(r);
如果使用无参构造器创建线程,则该线程将调用其自己的run()
。 这是在第一种情况下发生的(扩展Thread
类),但是在Runnable
的情况下,需要知道实现了Runnable
接口的类的run
方法需要被调用,而不是Thread
类的run()
。 因此,我们需要将该类作为参数传递给Thread
。 单个可运行实例可以传递给多个Thread
对象。
public class TestThreads {
public static void main (String [] args) {
MyRunnable r = new MyRunnable();
Thread foo = new Thread(r);
Thread bar = new Thread(r);
Thread bat = new Thread(r);
}}
将相同的目标分配给多个线程意味着多个执行线程将运行同一作业(并且同一作业将执行多次)。
Thread
类本身实现了Runnable
。 (毕竟,它有一个我们要覆盖的run()
方法。)这意味着您可以将Thread
传递给另一个Thread
的构造器。
Thread
类中的重载构造器:
Thread()
Thread(Runnable target)
Thread(Runnable target, String name)
Thread(String name)
到目前为止,我们已经创建了一个Thread
对象,并且它知道以run()
方法的形式做什么。 但是到目前为止,它仍然是一个对象。 它没有调用栈。 换句话说,执行线程尚未启动。 仅当在线程上调用start()
方法时,线程对象才会具有其栈。
到目前为止,线程具有两种状态。
- 新状态(已创建线程对象,但未调用
Start
方法) - 可运行(活动)状态(调用启动方法)
直接从 Java 代码调用
run()
方法意味着您正在调用方法,并且不会创建栈。
当线程处于可运行状态时,表示已创建线程,但尚未执行该线程的run
方法,并且该线程正在等待轮到他。 选定线程后,线程的run
方法将执行,该状态称为运行状态。
线程调度器:
线程调度器是 JVM 的一部分,它决定在任何给定的时间应该运行哪个线程,并使线程退出运行状态 。 调度器可以选择任何处于可运行状态的线程作为唯一正在运行的线程。 如果线程不处于可运行状态,则不能将其选择为当前正在运行的线程。 某种可能在某种程度上影响调度器的方法(注:我们无法控制线程调度器的行为)
这些方法来自线程类
* public static void sleep(long millis) throws InterruptedException
* public static void yield()
* public final void join() throws InterruptedException
* public final void setPriority(int newPriority)
Object
类中的方法。
* public final void wait() throws InterruptedException
* public final void notify()
* public final void notifyAll()
到目前为止,我们遇到了线程的三种状态
- 新建
- 可运行的
- 正在运行
线程还有更多状态,线程将无法运行。
- 等待/阻止/睡眠
- 死亡
当线程的run
方法完成执行时,该线程处于死亡状态。
sleep()
:sleep()
方法是Thread
类的静态方法。 (它仍然必须乞求成为当前正在运行的线程)。 由于它仅在当前是静态方法,因此运行Thread
将进入睡眠状态。 语法为:Thread.sleep()
yield()
:yield
方法与Thread
的优先级相关。 这也是一个静态方法,因此它仅适用于当前正在运行的线程。 调度器确保如果线程进入可运行状态,并且其优先级高于池中的任何线程,并且具有比当前正在运行的线程更高的优先级,则优先级较低的运行线程通常会被撞回可运行状态,然后将选择优先级最高的线程来运行。 在任何给定时间,当前正在运行的线程通常不会具有低于池中任何线程的优先级。 yield()
应该做的是使当前正在运行的线程回到可运行状态,以允许具有相同优先级的其他线程轮流使用。
Join()
:Join
是一种非静态方法。 它使一个线程“连接到另一线程的末端”。 如果将线程 B 连接线程 A,则直到线程 A 完成,线程 B 才会启动。
语法:
Thread t = new Thread();
t.start();
t.join();
接收当前正在运行的线程,并将其连接到t
引用的线程的末尾
线程可以离开运行状态并返回到Runnable
状态的其他几种方法。 运行方法完成对wait()
的调用
同步:与锁一起使用。 锁有两种类型。
静态锁定
非静态锁定
每个对象只有一个锁。 一旦线程获得了对象的锁,其他线程就无法进入给定对象的同步块/方法。
- 仅方法/块可以同步,而不是变量或类。
- 一个类可以同时具有同步/同步方法。
- 即使一个线程获得了给定对象的锁,线程也可以访问同步块。
- 如果线程进入睡眠状态,则它将拥有其拥有的所有锁。 一个线程可以同时具有不同对象的锁。
语法:
class SyncTest {
public void doStuff() {
System.out.println("not synchronized");
synchronized(this) {
System.out.println("synchronized");
}}}
同步静态方法:
静态数据只有一个副本,因此每个类只需要一个锁即可同步静态方法,即整个类的锁。 有这样的锁。 Java 中加载的每个类都有一个表示该类的java.lang.Class
对应实例。 就是那个java.lang.Class
实例,其锁用于保护类的静态方法。
语法:
public static synchronized int getCount() {
return count;
}
MyClass.class
事物称为类字面值。 它告诉编译器(谁告诉 JVM):去找我代表代表MyClass
类的类实例。
在同一类中调用非静态同步方法的线程只有在使用同一实例调用它们时才会彼此阻塞。 由于每个实例只有一个锁。 在同一类中调用静态同步方法的线程将始终相互阻塞,因为每个类只有一个锁。
静态同步方法和非静态同步方法不会相互阻塞。 由于他们需要为其他事物锁定(对象&类)
注意:join()
,sleep()
,yield()
这些方法保持锁定。 在等待释放锁定时。
备忘单
- 可以通过扩展线程类并覆盖
run()
方法来创建线程。 - 也可以通过以下方法创建线程:实现
Runnable
接口,然后调用带有Runnable
参数的Thread
构造器。 start()
方法只能在线程对象上调用一次。- 一旦在线程对象上调用
start()
方法,它将成为执行线程。 在调用start()
方法之前,它被称为处于新状态,并且不处于活动状态。 - 不能保证启动后线程执行的顺序。
- 我们可以通过设置
Thread
优先级(1-10)来影响Thread
执行的顺序。 - 正在运行的线程可以通过
wait()
,sleep
或join()
调用进入阻塞/等待状态。 - 正在运行的线程可能无法获取同步代码块的锁,因此可能会进入阻止/等待状态。
- 死线程无法再次启动。
Java 8 功能
Java Lambda:初学者指南
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-lambda-beginners-guide/
本文介绍了 Java 8 中的 Java Lambda 表达式。您可以在此处找到完整的代码。
Java Lambda 表达式
Java Lambda 表达式是 Java 的一个重要的新功能,已在 Java 8 中引入。Lambda 表达式是不属于任何类的匿名函数(无名称的函数),表示该函数没有名称,并且没有类。 Lambda 表达式通过促进 Java 中的函数式编程大大简化了开发。 它用于提供函数式接口的实现(函数式接口是仅包含一种方法的接口。例如,Java 标准的Runnable
接口是函数式接口)。 它还有助于迭代,过滤和从集合库中提取数据。 Lambda 表达式使您可以将代码视为数据,将函数视为方法参数。
Lambda 表达式的语法
让我们讨论 Lambda 表达式的语法。
Lambda 表达式包含三个组件,一组参数,一个 Lambda 运算符和一个函数体。 这是三个组成部分
- 参数列表:这里是参数,也可以为空或非空。
- Lambda 表达式: Lambda 表达式或箭头(
->
)用于分隔参数列表和函数体。 - 函数正文:它包含 Lambda 表达式的函数语句。
输入参数在 lambda 运算符的左侧,而函数体在 lambda 运算符的右侧。 此 Lambda 表达式语法可减少将五行代码合并为一行的代码的庞大性。
参数列表->
函数正文
Lambda 表达的特征
以下是 Lambda 表达的一些重要特征。
- 类型声明:类型声明是可选的。 由您自己决定,如果您不声明参数类型,则编译器可以从参数值中进行猜测。 例如你可以写
(5,4) -> function body
- 参数周围的括号:参数周围的括号也是可选的。 您可以根据需要放置括号,否则保留原样。 如果 Lambda 表达式中有多个参数,则需要括号,如上面的示例所示。 对于一个参数,您可以编写为
5 -> function body
- 花括号:如果只有一条语句,围绕函数主体的花括号也是可选的。 对于多个语句,需要在函数主体周围使用花括号。 例如
(5, 4) -> 5+4;
- 返回语句:在 Lambda 表达式中,返回语句也是可选的。 如果主体具有单个表达式,则 Java 编译器会自动返回值。 如果函数主体返回一个值,则应使用花括号将其括起来。
Lambda 表达的示例
以下是 Lambda 表达式的一些示例。
(x, y) -> x+y;
上面编写的 Lambda 表达式表示给定两个整数x
,y
,并返回另一个具有x
和y
之和的整数。
另一个
( ) -> 42
上面编写的 Lambda 表达式表示未给出整数,并返回整数 42。
前提条件
要运行 Java Lambda 表达式,必须满足以下条件。
- Java 开发套件(JDK8)
- NetBeans 或 Eclipse
没有参数的 Lambda 表达式
在此示例中,我将展示如何使用不带参数的 Java Lambda 表达式。 打开您的 IDE(我将使用 Eclipse Neon)并开始创建一个新项目。 将其命名为Lambda
。 创建一个名为NoParameter
的类。
public class NoParameter {
public static void main(String[] args) {
// TODO Auto-generated method stub
// lambda expression with return type
NoParameterInterface message = () -> {
return "Hello World with No Parameter";
};
System.out.println( message.HelloWorld( ));
}
}
@FunctionalInterface
interface NoParameterInterface {
//Here is a method with no parameter and return type as String
public String HelloWorld();
}
上面的示例通过使用不带参数的 Lambda 表达式显示了一个 Hello World 消息。
单参数 Lambda 表达式
您已经看到了不带参数的 Lambda 表达式示例。 我们来看一个带有单个参数的 Lambda 表达式示例。 为此,创建一个名为SingleParameter
的类,并将以下代码粘贴在那里。
public class SingleParameter {
public static void main(String[] args) {
// TODO Auto-generated method stub
// lambda expression with single parameter num and returns square of num without any return statement
SingleParameterInterface Square = (num) -> num*num;
System.out.println(Square.SquareOfFive(5));
}
}
@FunctionalInterface
interface SingleParameterInterface {
//A method with single parameter and return type as int
public int SquareOfFive(int s);
}
上面的示例显示了带有单个参数的 Lambda 表达式。 它以 5 作为输入,并返回 5 的平方,而没有任何return
语句。 如上所述,如果只有一条语句,则无需指定return
关键字。 编译器会自动返回该值。
多参数 Lambda 表达式
让我们以包含多个参数的 Java Lambda 表达式为例。 创建一个新的 Java 类,并将其命名为MulipleParamater
。 打开它,并在其中粘贴以下代码。
public class MulipleParamater {
public static void main(String[] args) {
// TODO Auto-generated method stub
//lambda expression which add two number
MulipleParamaterInterface add = (a, b) -> a + b;
System.out.println("Sum of 5 and 4 is: "+add.sum(5, 4));
}
}
interface MulipleParamaterInterface {
//here is a method with multiple parameter and int as return type
public int sum(int a, int b);
}
上面的示例显示了具有多个参数的 Lambda 表达式。 它以 5 和 4 作为输入,并返回sum
为 9。同样,它无需指定return
关键字即可工作。
事件监听器
让我们探索一些更高级的示例,这些示例是 Java 中的事件监听器。 Lambda 表达式如何用于实现事件监听器。 您将看到带有 Lambda 表达式和没有 Lambda 表达式的事件监听器的两个示例。
没有 Lambda 表达式的事件监听器
首先,检查没有 Lambda 表达式的事件监听器。 转到 eclipse 并创建一个新类,将其命名为eListener
。 打开它并粘贴以下代码。
import java.awt.event.*; //import for event listener
import javax.swing.JButton; //import for JButton class
import javax.swing.JFrame; //import for JFrame class
import javax.swing.JTextField; //import for JTextField
public class eListener {
//event listener class without Lambda Expressions
static JTextField textfield;
static JButton button;
static JFrame frame;
public static void main(String[] args) {
textfield=new JTextField();
button=new JButton("click");
frame=new JFrame("Event Listener without using Lambda Expression");
//set positions of text field and button
textfield.setBounds(50, 50,150,20);
button.setBounds(80,100,70,30);
//action listener on button
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
textfield.setText("hello world");
}
});
frame.add(textfield);
frame.add(button);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
frame.setSize(300, 200);
frame.setVisible(true);
}
}
上面编写的代码显示了 Java 中不使用 Lambda 表达式的事件监听器的示例。 因此,您必须熟悉该代码。 使用了简单的JButton``,JFrame
和JTextField
。 当用户单击按钮时,文本字段将显示 Hello World 消息。 这是输出
具有 Lambda 表达式的事件监听器
现在使用 Lambda 表达式检查事件监听器。 转到 eclipse 并创建一个名为eListenerLambda
的新类。打开并粘贴以下代码。
import javax.swing.JButton; //import for JButton class
import javax.swing.JFrame; //import for JFrame class
import javax.swing.JTextField; //import for JTextField
public class eListenerLambda {
//event listener class without Lambda Expressions
static JTextField textfield;
static JButton button;
static JFrame frame;
public static void main(String[] args) {
textfield=new JTextField();
button=new JButton("click");
frame=new JFrame("Event Listener without using Lambda Expression");
//set positions of text field and button
textfield.setBounds(50, 50,150,20);
button.setBounds(80,100,70,30);
// lambda expression implementing here.
button.addActionListener(e-> {textfield.setText("hello world");});
frame.add(textfield);
frame.add(button);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
frame.setSize(300, 200);
frame.setVisible(true);
}
}
上面编写的代码显示了带有 Lambda 表达式的 Java 事件监听器的示例。 如果我们将这两个代码进行比较,您将检查第一个示例中的动作监听器代码是否包含三个语句,而使用 Lambda 表达式可以在一个语句中解决问题。 这两个代码的输出与上面显示的相同。
使用 Lambda 表达式的比较器示例
让我们再看看使用 Lambda 表达式作为比较器示例的另一段代码。 创建一个新类,并将其命名为Comparatorlambda.java
。 打开它并粘贴以下代码。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
//account class holds the details of a bank account of customers like account number, name and account balance
class Account{
int accountNumber;
String name;
float AccountBalance;
public Account(int accountNumber, String name, float AccountBalance) {
super();
this.accountNumber = accountNumber;
this.name = name;
this.AccountBalance = AccountBalance;
}
}
//comparator class using Lambda Expressions
public class Comparatorlambda{
public static void main(String[] args) {
List<Account> list=new ArrayList<Account>();
//Adding account details in list
list.add(new Account(00235,"Harry",25000));
list.add(new Account(11687,"Donald",30088));
list.add(new Account(27865,"Caristano",15078));
System.out.println("Sorting on the basis of account name...");
// implementing lambda expression
Collections.sort(list,(p1,p2)->{
return p1.name.compareTo(p2.name);
});
System.out.println("Account Number: Account Name: Account Balance:");
for(Account p:list){
System.out.println(p.accountNumber+" \t\t"+p.name+" \t\t"+p.AccountBalance);
}
}
}
此示例具有一个帐户类,其中包含名称,号码和帐户余额之类的帐户信息。 Lambda 表达式用于根据帐户名称对帐户进行比较和排序。 这是用于此目的的 Lambda 表达式。
Collections.sort(list,(p1,p2)->{return p1.name.compareTo(p2.name); });
以下是此代码的输出
总结
Lambda 表达式用于定义函数式接口的内联实现。 您在上面的示例中看到,Lambda 表达式节省了我们的时间,并大大减少了代码语句。 它通过促进函数式编程为 Java 提供了强大的功能。 Java 的这一新功能改变了完整的编码方式,简化了开发过程,因为它利用了多核环境的并行功能。
Lambda 表达式简介
原文: https://javabeginnerstutorial.com/core-java-tutorial/introduction-lambda-expressions/
在本文中,我将向您简要介绍 Java 8 的一项新功能:lambda 表达式。 我将向您展示什么是 lambda 表达式以及如何利用这一新功能。
什么是 lambda 表达式?
Lambda 表达式类似于其他编程语言中的闭包-或 Java 本身中的匿名函数或内部类。
Lambda 表达式希望利用匿名类的用法。 您可能还记得将操作监听器注册到图形用户界面中的按钮的方式:
JButton clickMeButton = new JButton("Click me!");
clickMeButton.addActionListener(new ActionListener(){
@Override public void actionPerformed(ActionEvent event){
System.out.println("You clicked me!");
}
});
是的,上面的代码向您展示了一个匿名内部类,该类仅用于实现ActionListener
接口的唯一方法actionPerformed(ActionEvent)
。 而且这很笨拙—即使现代的 IDE 可以帮助您完成代码填充以填充方法的主体。
但是,使用 lambda 表达式,您可以像下面这样简单地实现此方法:
JButton clickMeButton = new JButton("Click me!");
clickMeButton.addActionListener(event -> System.out.println("You clicked me!"));
这样好多了,不是吗? 现在该深入了解如何构建自己的 lambda 表达式了。
Lambda 表达式具有以下语法:
parameters -> expression body
lambda 表达式的关键是->
箭头符号。
让我们看一个基本的示例:
(int x) -> { return x * x; };
还有其他一些特征可以简化 lambda 表达式的使用:
- 如果仅需要一个参数,则括号在参数周围是可选的
- 参数的类型声明是可选的
return
语句是可选的- 表达式主体周围的花括号是可选的—如果表达式主体包含单个语句
因为我们的简单表达式符合上述所有四个要点,所以我们可以像这样覆盖 lambda 表达式:
x -> x * x;
这使事情更具可读性。
函数式接口
Lambda 表达式可用于实现所谓的函数式接口的方法。 较新的接口可以带有注解@FunctionalInterface
,以指示此接口是函数式接口-但是,每个接口都可以视为仅具有一个抽象方法的函数式接口。因此,即使接口没有被非正式地标记为FunctionalInterface
,也可以使用 lambda 表达式替换/实现ActionListener
的addActionListener
。
默认方法不具有抽象性,因为它们具有实现。 如果接口在不提供实现的情况下覆盖了java.lang.Object
的方法,则该实现也不会被视为抽象,因为该实现将继承java.lang.Object
类的实现。 让我们来看一个示例:
/**
* @author GHajba
*
*/
public interface ExampleFunctionalInterface {
/**
* @return a name
*/
String getName();
/**
* prints a greeting
*/
default void printGreeting() {
System.out.println("Hello " + getName() + "!");
}
/**
*
* @return the string representation of this object
*/
@Override
String toString();
}
即使我们不添加注解,上面的接口也是一个函数式接口。 现在,我们创建一个 lambda 并使用它。 请记住,我们的 lambda 必须仅实现getName()
函数,该函数不需要任何参数并返回String
:
final ExampleFunctionalInterface example = () -> "GHajba"; // implements the getName() function
example.printGreeting();
System.out.println(example); // this calls example.toString() implicitly
如果运行示例,我们将得到与以下结果类似的结果:
Hello GHajba!
ChangeCalculator$$Lambda$1/[[email protected]](/cdn-cgi/l/email-protection)
如您所见,默认方法可以与实现的 lambda 一起正常工作,并且可以从java.lang.Object
调用toString
,因为不存在 lambda 表达式中的直接实现。
更多示例
了解了 lambda 表达式后,我们来看一些示例,在这些示例中我们可以使用它们来改进现有或新代码。
因为 lambda 表达式是匿名函数或内部类,所以我们可以用它们替换对现有匿名函数的调用。 就像 GUI 应用的动作监听器或Runnable
接口的本地实现一样。
final Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Hello lambdas?");
}
};
runnable.run();
上面的示例使用匿名类实现Runnable
接口。 现在来看看这个简单的“你好 lambdas”吗? 输出这是要做的一些工作。
final Runnable runnable_lambdas = () -> System.out.println("Hello lambdas!");
runnable_lambdas.run();
上面的代码示例使用 lambda 表达式,并且做完全相同的事情:它输出字符串“Hello lambdas!
”。 到控制台-但是无需创建匿名类来实现Runnable
接口。 自然,此代码仅适用于函数式接口(只有一种方法的接口)。
如您所见,也可以创建无参数的 lambda。 为此,您需要提供空括号作为参数,以表明没有任何内容。
除此之外,lambda 表达式也是闭包。 这意味着它们是可以保存在变量中并在以后重用的函数。 如果我们看一下上一节中的示例,我们可以将其保存到一个名为square
的变量中,并在以后重用:
IntUnaryOperator square = x -> x * x;
要使用它,我们可以直接调用它,也可以在某些流表达式中使用它,例如map
:
System.out.println(square.applyAsInt(128));
System.out.println("------");
IntStream.rangeClosed(1, 10).map(square).forEach(System.out::println);
上面的代码片段的结果是这样的:
16384
------
1
4
9
16
25
36
49
64
81
100
柯里化
柯里化是函数部分应用的名称。 这就是说,您有一个函数,该函数需要多个参数,但是大多数参数已经为人所知,并且将保持不变-只有一个会发生变化。 而且,除了在调用函数时提供每个参数之外,还可以部分咖喱函数并设置一些默认参数。
让我们看一下以下示例:
public static Function<Integer, Function<Integer, Integer>> multiply() {
return x -> y -> x * y;
}
这段代码创建了一个简单的函数,将两个Integer
类型的变量相乘。 因为返回的函数需要两个参数(x 和 y),所以我们可以创建这些函数的当前版本并在以后使用它们:
final Function<Integer, Integer> two_times = multiply().apply(2);
final Function<Integer, Integer> three_times = multiply().apply(3);
现在,我们对每个函数都应用了一个(变量)变量。 如果调用这些函数(应用第二个变量),我们可以看到结果表示已经应用的值:
System.out.println(two_times.apply(3)); // prints 6
System.out.println(three_times.apply(3)); // prints 9
每次自然地调用apply()
有点麻烦,但是 Java 目前无法使其变得更好。 如果可以编写以下代码,则将是更好的语法:
final Function<Integer, Integer> two_times = multiply()(2)
System.out.println(two_times(3));
这目前尚不起作用,但是添加到 Java 语言中将是一个不错的语法糖。
总结
如果您要实现函数式接口而无需创建和实例化实现该接口的类,那么 Lambda 表达式功能强大-主要是因为您只需要一次(例如,用作按钮的ActionListener
)。
我们研究了珂里化/函数部分应用,并讨论了一些功能将使其更易于在 Java 中使用,并希望在将来的版本中我们可以使用它。
Java 8 Lambda 列表foreach
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-8-lambda-foreach-list/
您一定已经听说过 Java 8 中引入的 Lambda 表达式。很快,我们将介绍有关它的详细主题。 但是现在在本文中,我将展示如何使用 Lambda 表达式来迭代List
集合。 如果要遍历Map
,可以查看 Java 8 lambda 映射foreach
文章。
清单 1:使用for
循环进行迭代
class java_forLoop_list {
public static void main(String[] args) {
List<String> jbtObjs = new ArrayList<>();
jbtObjs.add("Java");
jbtObjs.add("Beginners");
jbtObjs.add("Tutorial");
for(int i=0; i<jbtObjs.size();i++){
System.out.println(jbtObjs.get(i));;
}
Iterator i = jbtObjs.iterator();
while (i.hasNext())
{
String name = (String) i.next();
System.out.println(name);
}
}
}
清单 2:使用增强的for
循环(Java 5)进行迭代
class java_5_enhancedForLoop_list {
public static void main(String[] args) {
List<String> jbtObjs = new ArrayList<>();
jbtObjs.add("Java");
jbtObjs.add("Beginners");
jbtObjs.add("Tutorial");
for (String jbtObj : jbtObjs) {
System.out.println(jbtObj);
}
}
}
清单 3:Java 8 Lambda forEach
public class java_8_forEach_list {
public static void main(String[] args) {
List<String> jbtObjs = new ArrayList<>();
jbtObjs.add("Java");
jbtObjs.add("Beginners");
jbtObjs.add("Tutorial");
jbtObjs.forEach(name -> System.out.println(name));
}
}
Java 8 Lambda 映射foreach
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-8-lambda-foreach-map/
您一定已经听说过 Java 8 中引入的 Lambda 表达式。很快,我们将介绍有关它的详细主题。 但是现在在本文中,我将展示如何使用 Lambda 表达式来迭代集合。
在 Java 8 之前
class java_5_enhancedForLoop_Map {
public static void main(String[] args) {
Map<String, String> jbtObj = new HashMap<String, String>();
jbtObj.put("Website Name","Java Beginners Tutorial");
jbtObj.put("Language", "Java");
jbtObj.put("Topic", "Collection");
for (Map.Entry<String, String> entry : jbtObj.entrySet()) {
System.out.println(entry.getKey() + " : "+ entry.getValue());
}
// Iterating over collection object using iteration even before Java 5
Iterator<Entry<String, String>> iterator = jbtObj.entrySet().iterator();
while (iterator.hasNext()) {
Entry<String, String> thisEntry = (Entry<String, String>) iterator.next();
Object key = thisEntry.getKey();
Object value = thisEntry.getValue();
System.out.println(key+" : "+value);
}
}
}
在 Java 8 之后使用 Lambda 表达式
public class java_8_forEach_Map {
public static void main(String[] args) {
Map<String, String> jbtObj = new HashMap<String, String>();
jbtObj.put("Website Name","Java Beginners Tutorial");
jbtObj.put("Language", "Java");
jbtObj.put("Topic", "Collection");
jbtObj.forEach((key,value) -> System.out.println(key+" :"+value));
}
}
如您所见,通过单行代码,我们可以遍历Map
。 同样,我们可以使用 Lambda 中引入的过滤器过滤出结果。
Java 9
Java 9 功能
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-9-features/
预期的 Java 9 版本引发了许多创新和令人印象深刻的新功能,Oracle 宣布了 JDK 的新发布议程,而这仅仅是开始。 过去,开发人员经常批评 Java 的发展速度不够快。
Java SE 9 于 2017 年 9 月发布。它包含一长串功能,包括模块化,读求值打印循环,提前编译以及用于字符串存储的节省内存的开发。
它将为 Java 语言的发展增加一个独特的阶段,并使 JDK 团队可以更快地进行修改和创新。 由于 Java 9 是 Java 10 继而的短期加速发布版本,因此 Java 9 已停止获取更新。
此版本的 Java 具有一些基本的架构更改,并且进行了许多改进。
Java 9 功能包括
- JSR 376:Jigsaw 项目(Java 平台模块系统)下的 JDK 模块化
- JEP 222:jshell:Java Shell(Java REPL)
- JEP 295:AOT 编译
- JEP 268:XML 目录
- JEP 266:更多并发更新。 它包括反应式流的 Java 实现,包括一个新的
Flow
类,该类将包括反应式流当前提供的接口。 - JEP 193:变量句柄:建立标准方法来请求各种
java.util.concurrent.atomic
和sun.misc.Unsafe
操作的等效项 - JEP 282:jlink:Java 链接器:设计一种工具,该工具可以将一组模块及其主导结构构建和优化为自定义运行时映像。 它有效地允许产生完全正确的执行,包括运行它的 JVM。
- JavaDB 已从 JDK 中删除
- JEP 263:HiDPI 图形:自动缩放和调整大小
Java 9 应该为千兆字节的堆提供更好的帮助,更好的本机代码集成,不同的默认垃圾收集器(G1,“响应时间更短”)和自调整 JVM。
为了启动向模块化 Java 9 的平稳迁移,Java 9 允许对类路径上的代码进行未经授权的沉思访问,JRE 使用它来探索类和资源数据。 Java 9 之后的版本将禁止此功能。
当前的编译器控制功能旨在提供对 JVM 编译器的细粒度和方法上下文相关的控制,使开发人员可以在运行时更改编译器控制选项,而不会降低执行质量。 该工具还简化了 JVM 编译器错误的解决方法。
Java 9 还忽略了import
语句上的 Java 警告,以进一步编写大型代码库,而不再使用 lint 警告。 这些包含代码库的不推荐使用的功能通常必须保留一段时间,但是如果自愿使用了该不推荐使用的构造,则传达不推荐使用的构造并不能保证发出警告消息。
Java 的有效发布措施意味着开发人员不必等待显着的版本。 Java 工具供应商 ZeroTurnaround 的 Java 倡导总监西蒙·梅普尔(Simon Maple)说,这也可能意味着开发人员将跳过 Java 9 及其“朴素的”模块化功能,而等待六个月才能获得现代版本,这可能会消除任何复杂性。
新的为期 6 个月的发布顺序和适当的支持设计将要求对当前应用进行更快的更新,并在一致的基础上启动新功能。 结合尚存的框架(如 Java EE 或 spring)的发展,这将为 Java 世界增添新的活力。 而且,这也要求每隔几年更新其应用的所有公司都转变思维方式。
Java 10
Java 10 独特功能
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-10-features/
Java 10 是有 23 年历史的最快 Java 版本。 Java 因其缓慢的增长和发展而受到批评,但是 Java 10 恰恰相反。 这是一个具有许多发展的发行版,其范围和影响可能不明显,但牵强附会。 在本文的此处,我们将寻找一些关键的 Java 10 功能
新的 Java 10 功能将为 Java 添加一些语法上的甜蜜,对其进行分析并改善开发人员的体验。 在保持对非活动类型安全性的承诺的同时,它将减少与编写 Java 相关的单词的过多使用。
Java 10 由各种新功能和对许多功能领域的改进组成。 它的一些重要增强功能包括垃圾收集和编译以及局部变量类型的改进。
Java 10 功能
- JEP 286:局部变量类型推断
- JEP 322:基于时间的发行版本控制
- JEP 304:垃圾收集器接口
- JEP 307:适用于 G1 的并行全 GC
- JEP 316:备用存储设备上的堆分配
- JEP 296:将 JDK 林整合到单个仓库中
- JEP 319:根证书
- JEP 317:基于 Java 的实验性 JIT 编译器
- JEP 312:线程本地握手
- JEP 313:删除本机头生成工具
Java 10 功能的独特性
- 为了在不进行全局 VM 安全点的情况下在线程上实现回调,具有线程本地握手的功能。
- 应用类数据共享:用于通过跨方法共享公开类元数据来减少占用空间。 另外,启动时间也会调整。
- 他们正在促进基于 Grall Java 的即时编译器,该编译器将以创新的形式在 Linux/x64 平台上使用。
- G1 垃圾专家需要并行执行完全垃圾累积,以通过执行并行来产生平均案例延迟。
- 为了提高不同垃圾收集器的源代码隐私,提供了一个干净的垃圾收集器接口。 该提议致力于为 HotSpot 虚拟机中的内部垃圾收集代码提供更好的模块化,从而使其可以将新的垃圾收集器附加到 HotSpot。
- 改进了创建无法修改的集合的新 API。
Set.copyOf
,List.copyOf
和Map.copyOf
方法将从现有实例创建新的集合实例。 将诸如UnmodifiableMap
,UnmodifiableList
和UnmodifiableSet
之类的新方法添加到Stream
包的Collectors
类中,这将允许将流的组件组装为不可修改的数组。 - jShell REPL 工具的启动时间将更快,主要是在使用包含多个片段的启动文件时。
- 将提供三个新的 JVM 选项,以使 Docker 容器用户对系统内存有更大的控制权。
- 局部变量类型推断:将增强语言以将类型推断扩展到局部变量。 目的是减少与编码关联的“仪式”,同时保留对非活动类型安全性的承诺。
- Graal 是一种创新的即时编译器,可以应用于 Linux/x64 平台。
- 在 HotSpot 的帮助下,它将允许在用户指定的替代存储设备(例如 NVDIMM 内存模块)上指定对象堆。 由于此特性,可以预见,未来的系统可能会具有复杂的内存架构。
Oracle 的 Java 团队声明不可能:
- 方法参数
- 构造器参数
- 方法返回类型
- 字段
- 捕获形式(或任何其他种类的变量声明)。
到目前为止,新的 Java 版本的演变都是由功能驱动的。 这意味着您必须等待几年才能发布下一个版本。 诸如泛型,Lambdas,流和 Jigsaw 之类的重要开发使这一事实可以接受,但是许多较小的有益语言的改进始终以这种方式被延迟。
由于 Java 负责支持 Java 的早期版本,因此我们可以假定它不会破坏向后协调。
参考文献
核心 Java 教程 – 高级主题
Java 虚拟机基础
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-virtual-machine-basics/
JVM 是 Java 虚拟机的缩写形式。 它是一个 Java 字节码处理引擎,它将 Java 字节码转换为机器语言,然后由机器(计算机)执行。 Java 字节码是 Java 与必须在其上执行程序的机器之间的中间语言,并且当 JVM 将 Java 字节码处理为机器语言时,可以执行此执行。
JVM 简介
让我们用简单的词来理解 Java 字节码,JVM 和 Machine 的概念。
创建 Java 程序时,将生成扩展名为.java 的文件。 该文件可能包含几个属性,这些属性包括但不限于类,方法,对象等。 然后,使用 Java 编译器编译该.java 文件,该编译器将生成.class
文件(也称为 Java 字节码)。 JVM 读取此.class
文件,理解其代码,将代码解释为机器语言,然后执行该文件。
JVM 平台独立吗?
Java 由于具有 JVM 而与平台无关,但是 JVM 与平台有关。 必须在其上执行 Java 代码的计算机必须安装了 JVM。
根据必须在其上执行 Java 字节代码的机器,JVM 解释.class
文件并生成另一种机器语言,该语言仅对该机器可以理解。 不同的计算机可能具有不同的操作系统,例如 Windows,Mac,Unix 或 Linux。 如果没有 JVM,这些操作系统将无法解释 Java 字节码,因为操作系统只能理解机器语言。 JVM 将 Java 字节码转换为机器语言,并使操作系统可以理解以执行该代码。 这意味着 Java 可以在任何计算机上运行,但是它需要在该计算机上安装 JVM。
JVM 的组件
在解释和执行 Java 字节码时,JVM 使用了几个组件,例如栈,寄存器,垃圾收集器等。 让我们看一下它的一些组成部分:
- 栈:它是方法参数和局部变量的仓库。 可以在指向寄存器不同部分的寄存器的帮助下对其进行操作。
- 寄存器:JVM 中有许多寄存器,例如 Vars,Frame,Program Counter 和 OpTop。 Vars 寄存器指向当前方法正在使用的局部变量。 框架寄存器指向执行环境,这是用于栈的环境。 Optop 指向操作数栈,在其中执行字节码指令。 程序计数器寄存器指向保存字节码的“方法”区域。
- 方法区域:它是 Java 字节码的占位符。 该区域在所有线程之间共享,因此可以确保同步。
- 垃圾收集器:它是 JVM 的组件,用于存储实际的 Java 对象。 请注意,Java 对象的引用存储在栈中,而实际对象存储在垃圾收集器中。 Java 使用垃圾回收来释放分配给不同对象的内存,这与 C/C++ 不同,在 C/C++ 中,运算符用于释放内存。
Java 类加载器
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-class-loader/
简介:什么是类加载器
应用中的所有类文件在启动时都不会加载到内存中,而是根据程序的需要按需加载,此类的加载是由类加载器完成的。 类加载器是 JVM 的一部分,可将类加载到内存中。
Java 类加载器是用 Java 本身编写的(与以 C++ 编写的 JVM 相反)。 这意味着在需要时可以轻松开发自定义类加载器,而无需了解 JVM 的细节。 ClassLoader
是一个抽象类,它是java.lang
包的一部分。
类加载器的工作方式
一个应用可能需要不同的类才能在运行时正常运行,每当一个应用在运行时需要一个类时,类加载器都会查找给定的类名,如果找到则将其加载到内存中。 提供要加载的类的名称后,类加载器将尝试在不同位置查找构成类定义的数据。
类加载器搜索的位置将按照给定的顺序。
- 首先在 JRE 的
lib/ext
目录以及系统范围,特定于平台的扩展目录中的 JAR 文件中查找类。 - 其次在类路径(
java.class.path
属性)中查找该类。 类路径的默认值是当前目录,但是可以用不同的方式更改此值。
类加载器层次结构
ClassLoader
使用委托模型来搜索要加载的类和资源。 每个ClassLoader
实例将具有
关联的父类加载器。 当类加载器获得查找类的请求时,给定的 ClassLoader
会在尝试查找类或资源本身之前将对类或资源的搜索委托给其父类加载器。 Bootstrap 类加载器位于类加载器层次结构的顶部。 Bootstrap 类加载器是虚拟机中的内置类加载器。 Bootstrap 类加载器没有任何父项。
类加载器的类型
Java 类加载器可以分为 4 种类型。
自举类加载器
它是超类加载器。 它没有任何父类类加载器。 它将 Java 的核心类(如java.lang
,java.util
包中的类)加载。
扩展类加载器
扩展类加载器将在 JRE 的lib/ext
目录以及系统范围,特定于平台的扩展目录中的 JAR 文件中加载类。 自举类加载器将是扩展类加载器的父级。 开发人员可以在ext
文件夹中手动添加 jar,以使其由扩展类加载器加载。
系统类加载器
系统类加载器将在类路径(java.class.path
属性)中加载类。 扩展类加载器作为系统类加载器的父级。 类路径的默认值是当前目录,但是可以用不同的方式更改此值。
- 设置
CLASSPATH
环境变量。 - 通过
-classpath
或-cp
命令行选项提供的值。
自定义类加载器
开发人员还可以根据需要创建自定义类。
使用自定义类加载器的优势
- 应用服务器中的热部署
- 自定义类加载器,以供浏览器在执行不受信任的代码之前检查安全性证书。
Java 开发人员必须知道..
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-developers-must-know/
Java 虚拟机怎么运行?
- 首先 jvm 在类中查找
main
方法。如果存在,它将从该处开始执行 - 在 Java 虚拟机内部,线程有两种形式:守护程序和非守护程序。 守护程序线程通常是虚拟机本身使用的线程,例如执行垃圾收集的线程。 但是,该应用可以将其创建的任何线程标记为守护程序线程。 应用的初始线程(从
main()
开始的线程)是非守护程序线程。 - 只要任何非守护程序线程仍在运行,Java 应用就会继续执行(虚拟机实例继续运行)。 Java 应用的所有非守护程序线程都终止时,虚拟机实例将退出。 如果得到安全管理者的允许,则应用还可以通过调用
Runtime
或System
类的exit()
方法来使其自身灭亡。
Java 虚拟机的架构
- 所有的 JVM 都包含两个组成部分
类加载器子系统:一种机制,用于加载具有完全限定名称的类型(类和接口)。
执行引擎:一种机制,负责执行已加载类的方法中包含的指令。
- 当 Java 虚拟机运行程序时,它需要内存来存储许多东西,包括字节码,已加载的类文件中的信息,程序实例化的对象,方法的参数,返回值,局部变量以及中间的计算结果。
- 虚拟机的不同实现可能具有非常不同的内存限制。 一些实现可能需要大量内存才能工作,而其他实现可能很少。 一些实现可能能够利用虚拟内存,而其他一些则不能。 运行时数据区规范的抽象性质有助于简化在各种计算机和设备上实现 Java 虚拟机的过程。
- 不同的 Java 程序具有不同的 jvm 实例。 如果我执行一个类,则一个单独的 jvm 实例将处理该类
- 当虚拟机加载类文件时,它会从类文件中包含的二进制数据中解析有关类型的信息。 它将类型信息放入方法区域,将程序实例化的所有对象放入堆。
- 随着每个新线程的出现,它获得了自己的 pc 寄存器(程序计数器)和 Java 栈。 如果线程正在执行 Java 方法(不是本机方法),则 pc 寄存器的值指示要执行的下一条指令。 线程的 Java 栈存储该线程的 Java(非本机)方法调用状态。
- Java 栈由栈帧(或帧)组成。 栈框架包含一个 Java 方法调用的状态。 当线程调用方法时,Java 虚拟机将新框架推送到该线程的 Java 栈上。 该方法完成后,虚拟机将弹出并丢弃该方法的帧
- Java 虚拟机包含两种类加载器:
java.lang.ClassLoader
bootstrap 类加载器,用户定义的类加载器。 - 正在运行的程序的每个线程都有其自己的程序计数器寄存器或程序计数器,它们是在线程启动时创建的。 pc 寄存器的大小为一个字,因此它既可以保存本机指针,也可以保存 returnAddress。 当线程执行 Java 方法时,pc 寄存器包含该线程正在执行的当前指令的地址。 “地址”可以是本机指针,也可以是方法字节码开头的偏移量。 如果线程正在执行本机方法,则 pc 寄存器的值未定义。
- 这里有两个栈,Java 栈:由 JVM 维护,本机栈:取决于。
大家好,请评论我的帖子。 如果我的帖子有任何问题,请随时进行纠正,谢谢。
Selenium 教程
1 什么是 Selenium?
原文: https://javabeginnerstutorial.com/selenium/1-what-selenium/
嗨呀让我们进入 Selenium 的酷炫世界。 我一直说很多次,不是吗? 那么,为什么不了解这个世界是什么,以便您可以告诉我世界是否如此。 此外,这将是一个简短的!
在开始编写严肃的测试脚本并在浏览器上看到神奇的事物之前,让我们看一下 Selenium 的一般含义,以便我们可以根据需要有效地使用它。
Selenium 是用于自动化和测试 Web 应用的一组工具。 就这么简单!
由于它不是单个工具,因此称为套件。 我们有整篇文章专门介绍这些强大的工具,这些工具旨在满足不同的测试和测试环境要求。
为了更深入和更好地理解,请将 Selenium 视为网络浏览器的 API,即操纵其中的元素。 更好的是,这就像您能够对 Firefox 或 Chrome 或 IE 等进行编程,以使重复的手动测试用例自动化。 您可能想知道如何定位和捕获所需的 Web 或 UI 元素,但是必须等待更长的时间才能解密!
BrainBell:大脑为我们提供了很多帮助! 当我们努力学习某些东西时,我们的大脑会通过确保学习不会持续进行来帮忙。 是的,难过,但真实。 它将使我们想起所有其他重要的事情,这些事情使您进行ALT + TAB
或留出充足的空间。 因此,我们有必要欺骗我们的大脑,使他们认为生命取决于对 Selenium 及其世界的了解。 重复(其中一种技巧,虽然有点无聊,但仍然如此)以不同的方式说同一件事,增加了将内容编码到大脑多个区域的机会。
回来了,那么,什么使这个世界在测试专业人士中变得更加流行和最受欢迎? 它是开源的,因此是免费的,它使您能够在多个浏览器平台上运行测试。
希望您现在同意这个世界是一个很酷的地方! 话虽如此,在另一篇文章中再见。
祝你有美好的一天!
2 为什么要进行自动化测试?
原文: https://javabeginnerstutorial.com/selenium/2-automated-testing/
欢迎回到勇士们! 我知道您整天都在测试应用/系统,如果有的话,寻找错误。 从而确保承诺的质量。
此外,您还会听说过“自动测试”一词。 让我们看看它的含义以及我们为什么要真正关心它。 此外,为什么还要花费宝贵的时间和精力创建自动化测试? (在这里告诉我一个秘密:尽管我们使用一种工具来实现自动化,但是肯定要付出很大的努力。因此,只有在值得我们努力的情况下,我们才可以涉足这一领域!)
内容:自动测试是指借助工具自动执行手动过程以测试被测应用/系统的过程。
原因:在当今世界,由于高度互动,响应迅速且功能丰富的内容可以满足遍布全球的大量最终用户,因此编写的大多数软件应用都是以经济的代价基于 Web 的。 对质量的需求以及对上市时间和预算的竞争压力也急剧增加。 为了节省时间和金钱,增加测试覆盖范围,实现最佳质量并提高客户满意度,测试自动化已成为强制性要求!
何时变得有利?
- 回归测试 – 想象一下,系统中已经创建了功能,并且已经过测试。 在稍后的某个时间点,有一些更改会影响现有代码或引入新功能。 因此,现有的已经测试过的零件可能无法按预期工作,或者更糟,发生故障。 如果这种情况在生产环境中发生,并且您的客户之一对此表示报错,那是什么条件? 您甚至可以想象发生这样的事情吗? 听起来令人恐惧吧? 为了您的急救,我们这里有 Selenium。 我们可以使用 Selenium 创建一个自动化测试的回归套件,并且每次引入更改或新功能时,都可以运行该套件,而无需进行任何额外的工作(在敏捷过程中,每个 sprint 结束时) 确保没有任何损坏。
- 高级测试 – 单元测试可用于测试单个组件(类级别),集成测试可用于测试事物在类间级别上的工作方式。 对于 Web 应用,最后的座右铭是找出浏览器是否按照客户希望的方式正常工作。 使用 Selenium,我们可以像用户/客户一样执行测试!
- 组合测试 – 让我们以一个包含三组,每组具有三个单选按钮的网页为例。 手动测试所有组合可能会花费大量时间,但是以编程方式可以轻松完成,并且无需人工干预即可在一夜之间进行测试。
- 明确要求 – 手动编写的测试步骤可能不明确。 一个测试人员可能会测试并声明它已通过,但另一个测试人员可能会说失败了。 自动化测试具有非常确定的结果。 因此,无论开发人员是谁,都对开发人员和您的应用提出了确切的要求。 它或者通过或者失败。 而已!
- 可以在某些测试框架的帮助下生成自定义报告和屏幕截图,当开发人员或其他项目涉众期望快速反馈时,这将非常有用。
- 一旦安装了测试脚本并设置了测试环境,就可以执行任意数量的测试执行迭代。
- 可以执行这些自动测试的速度非常高。 因此,在频繁发布的情况下,自动化成为一个很好的解决方案。
- 当需要并行执行时,自动化是可取的。
- 由于避免了人为错误(可能随着时间的推移而使重复任务变得无聊而导致的错误),因此准确性和质量得以提高。
哪个可能是基于 Web 的自动化中使用最广泛的开源解决方案? – 拜托,显然是 Selenium!
有了这些知识,在另一篇文章中再见,以了解 Selenium 的历史。
祝你有美好的一天!
3 Selenium 的历史
原文: https://javabeginnerstutorial.com/selenium/3-history-selenium/
哎呀! 准备过去一段时间吗? 准备好被今天要出土的宝石所震撼。
很久以前,在 2004 年,有一个来自芝加哥 ThoughtWorks 的人叫 Jason Huggins。 他将 Selenium 的核心模式构建为“JavaScriptTestRunner”,以验证他正在测试的 Web 应用,时间和费用应用的行为。 他以一种人们可以使用 HTML 文件中的关键字驱动方法编写测试的方式来构建它。
慢慢地,他开始向大量受众展示他的工具的演示。 不久,人们开始讨论将该工具开源,并使其成为可重用的框架,以使其他此类基于 Web 的应用自动化。 这就是 Selenium 核心的故事。
不过,Core 还是有一些限制,例如“原产地政策”。 好的大字警报! 首先,让我们了解一下原点是什么。
来源不过是 URL 的方案,主机和端口。 因此,根据该策略,由于主机不同,因此不允许尝试访问从 http://abc.org/sample.html 检索的文档的 DOM 来检索文档。 另一个示例,我从 http://example.com/(端口:80)启动了一个 JavaScript 程序,由于端口不同,根据此策略,https://example.com(端口:443)变得不可访问。
现在,又有一个来自 ThoughtWorks 的名叫 Paul Hammant 的人通过提出 Selenium 1 或 Selenium RC(远程控制)来规避此限制。 它具有一个 Selenium 客户端和一个 Selenium 服务器。 客户端将命令发送到服务器,并且该服务器将使用 JavaScript 驱动浏览器。 因此,它不会直接挂钩到任何浏览器 API。 它还支持多种编程语言,我们将在后面详细介绍。
现在到下一个! Selenium IDE 不仅是最简单,最简单的工具,而且是在 Selenium Suite 中提供最少自动化编程脚本所需的最少编程知识的唯一工具。 我知道您会很想知道谁提出了这个宝石。 顺便说一下,这是“日本制造”的产品。 开发此工具的功劳归功于 Shinya Kasatani。 他是意识到 Selenium Core 代码可以包装到 IDE(集成开发环境)模块中并可以插入 Firefox 浏览器的人。 它具有记录和播放功能,比您期望的要酷! 让我们将其保存以备后用。
在 2007 年前后,ThoughtWorks 的另一个名叫 Simon Stewart 的人正在开发另一个名为 WebDriver 的 Web 测试工具,该工具不依赖 JavaScript。 与 Selenium RC 相比,它不仅为每个浏览器都有一个客户端,而且还有一个“更高级别”的 API。 这两个项目合并了,现在我们有了新的 Selenium 或 Selenium 2。
啊哈,所以结论是:
- Selenium RC = Selenium 1
- Selenium 与 WebDriver 合并 = Selenium 2
希望您喜欢这次旅行。 在另一篇文章中再见。 祝你有美好的一天!
4 Selenium 工具套件
原文: https://javabeginnerstutorial.com/selenium/4-selenium-tool-suite/
朋友! 到了另一个 BrainBell – 图片的时间! 我们通过可视化学习的内容越多,我们在以后的时间记得和回忆的就越好。
谁不喜欢图片,众所周知,一张图片值一千字。 因此,此博客文章将全部为图片! 我以视觉方式提出了 Selenium 的成分的高级概念。 因为图像使学习变得更加有趣和充满乐趣。
初探
Selenium 工具套件:
Selenium IDE:
Selenium RC:
Selenium 网格:
WebDriver:
您现在看到的是 Selenium 工具套件的快速转发版本。 有很多要说的,但是所有这些都可以等到他们相应的帖子让生活焕然一新!
在另一篇文章中再见。 祝你有美好的一天!
5 Selenium 工具支持的浏览器和平台
原文: https://javabeginnerstutorial.com/selenium/5-browsers-platforms-support/
欢迎回到 Selenium 的另一篇文章,今天我们将查找 Selenium IDE,Selenium RC 和 WebDriver 支持的浏览器,操作系统,编程语言和测试框架。
浏览器
Selenium IDE | Selenium RC | WebDriver |
---|---|---|
Firefox | Internet Explorer,Google Chrome,Mozilla Firefox,Safari,Opera,其他浏览器的部分支持 | Internet Explorer,Google Chrome,Mozilla Firefox,Safari,Opera,HtmlUnit,Phantomjs,Android,iOS |
操作系统
Selenium IDE | Selenium RC | WebDriver |
---|---|---|
Windows,Mac OS,Linux,Solaris | Windows,Mac OS,Linux,Solaris | Windows,Mac OS,Linux,Solaris |
编程语言
Selenium IDE | Selenium RC | WebDriver |
---|---|---|
可以生成自定义代码 | Java,C# ,Perl,Python,Ruby,PHP 和任何可以进行 HTTP 调用的语言 | Java,C# ,Perl,Python,Ruby,PHP |
测试框架
Selenium IDE | Selenium RC | WebDriver |
---|---|---|
可以生成代码 | RSpec(Ruby),Test::Unit(Ruby),unittest(Python 2),JUnit 3(Java),JUnit 4(Java),TestNG(Java),NUnit(C# ) | RSpec(Ruby),Test::Unit(Ruby),unittest(Python 2),JUnit 4(Java),TestNG(Java),NUnit(C# ) |
巨大的支持权!
在另一篇文章中再见。 祝你有美好的一天!
6 Selenium 工具:争霸
原文: https://javabeginnerstutorial.com/selenium/6-selenium-tools-fight-supremacy/
就像我们在众多产品中争夺霸主地位的测试人员一样,Selenium Tools 也进行了非常相似的战斗,以证明自己是最好的! 让我们看看它们之间到底发生了什么……
Selenium IDE – 专为 Selenium 初学者而设的历史领袖
- Selenium IDE 是整个可用套件中最简单的框架,并且拥有这样做的所有权利。 它也是最容易学习的。
- 不需要编程经验(不过,对 HTML 的了解很少!)
- 非常容易安装并开始一些操作。
- 简单的记录和回放工具。
- 用作插件,仅与 Mozilla Firefox 一起使用。
- 为扩展提供良好的支持。
- 帮助导出可以在 Selenium RC 和 WebDriver 中执行的测试。
Selenium RC – 系列的超人
- 跨浏览器和跨平台测试随着其引入而成为现实。
- 支持 C# ,Java,Ruby,Python,Perl 和 PHP 等语言,因此需要编程经验。
- Selenium RC 服务器应该正在运行以执行测试用例。
- 它具有简单易用的 API。
- 允许我们执行循环和条件操作。
- 支持数据驱动的测试。
WebDriver – 压倒性冠军!
- 与 Selenium RC 一样,支持跨浏览器和跨平台。
- 与浏览器进行本地交互,因此它比 Selenium IDE 和 Selenium RC 更快。
- 简洁的 API – 使我们的生活更轻松。
- 还支持不可见的浏览器 HtmlUnit。
- 还支持对 iPhone 和 Android 应用的测试。
Selenium 网格 – I-Am-You-For-You 冠军
现在它已内置在服务器本身中,因此可以称为“Selenium Server 网格模式”。 当您必须一次运行多个测试而感到担心时,Selenium 网格会为您提供帮助,说我在这里为您服务!
- 使用节点集线器概念。
- 帮助同时执行测试。
- 支持多种浏览器和平台。
- 因此节省了大量宝贵的时间。
所有这些都可以使沉闷的测试日变成充满机遇和阳光的一天。
是否不是您要在办公时间开始后每天早上至少使用一次“Selenium”? 根据被测应用,客户要求和可用时间表,可以选择上述工具之一进行自动化。
游戏开始!
7A Selenium IDE – 简介,优点和局限性
原文: https://javabeginnerstutorial.com/selenium/7a-ide-benefits-limitations/
Selenium 将我们带到了新的地方,现在是时候让我们进一步了解我们将生活的世界 – Selenium 世界。 它有四个主要部分,即
- Selenium IDE 的魔术草地通常被称为“Selenium 初学者的历史领袖”
- Selenium RC 的多样性增量以“系列超人”而闻名
- WebDriver 的南方惊喜,即“压倒性冠军”
- Selenium 网格的群集通道,这就是我们的“I-Am-There-For-You 冠军”
出发去魔术草地! 让我们永远离开这个尘土飞扬的旧手动测试世界。 哦,不用担心,我们随时可以发送蜗牛邮件。
BrainBell 重复的时间:Selenium IDE(集成开发环境)是随套件提供的最简单,最简单的框架。 它作为 Firefox 插件出现,因此非常容易安装和使用。 只需很少的编程知识和几乎为零的培训,即可开始使用 Selenium IDE。 对于初学者来说,为了窥视 Selenese 命令并了解脚本语法,这是一个很好的工具。
它提供了一个 GUI(图形用户界面)来记录 Firefox 浏览器中的必要操作。 神奇之处在于记录和回放功能,我们将很快详细介绍。 选择在 Firefox 浏览器当前显示的页面上显示的 UI 元素后,在 Selenium IDE 运行时右键单击将显示带有基于所选 UI 元素上下文的预定义参数的 Selenium 命令列表。 这使编写脚本变得更加容易!
甚至在播种前就知道我们将要收获的东西总是很有趣的! 因此,这里简要介绍了这片土地的一些好处和局限性。
Selenium IDE 的优点:
- 非常容易安装和使用。
- 无需编程经验。
- 具有内置的帮助功能,它显示有关所选或输入命令的文档。
- 通过显示信息和错误消息来帮助调试。
- 它使我们能够在必要时设置断点,插入命令和注释。
- 为扩展提供良好的支持。
- 帮助导出可以在 Selenium RC 和 WebDriver 中执行的测试。
Selenium IDE 的局限性:
- 由于它是 Firefox 插件,因此不支持 Mozilla Firefox 以外的任何浏览器。
- 它没有明确提供支持警报,弹出窗口和导航的帮助。
- 它不支持监听器。
- 不支持错误处理和数据库测试。
- 它不能用于测试 iPhone 和 Android 应用。
- 不支持从外部文件读取和上传文件。
- 不支持迭代和条件操作。
准备采取一些措施,因为在我们的下一篇文章中,我们将安装 Selenium IDE 并一直进行下去。
祝你有美好的一天!
7B Selenium IDE – Selenium IDE 和 Firebug 安装
原文: https://javabeginnerstutorial.com/selenium/7b-selenium-ide-firebug-installation/
欢迎来到我们的第一篇探索(就像唱歌一样!)帖子。 为了从我们的 Selenium 系列中获得最大收益,建议您密切注意并共同努力。 它是如此简单!
魔术草地的土地上,在我们看到真正的魔术发生之前,是时候为其设置环境了。 听起来有点技术性,不是吗? 但不用担心,这很容易,因为 Selenium IDE 是作为 Firefox 插件提供的。
因此,开始进行安装过程所需要的就是有效的互联网连接。
(那些已经在您的系统上安装 Mozilla Firefox 浏览器的人,应该坐下来放松一下,直到完成第 1 步。向下滚动一点,从最期待的第 2 步开始进行操作)
步骤 1:安装 Firefox
从 https://www.mozilla.org/zh-cn/firefox/new/ 下载最新版本的 Firefox
[
下载设置文件后,它看起来像这样。
因此,继续并单击 exe 文件。 提取达到 100% 后,将弹出 Mozilla Firefox 安装向导。 点击“下一步”按钮。
您可以很好地使用标准作为设置类型。 完全留给您方便。 我选择“标准”,然后单击“下一步”。
显示了将安装 Firefox 的位置的摘要。 只需单击“安装”按钮。
将进行安装,
单击“完成”,然后启动 Firefox 浏览器以开始使用。
步骤 2:安装 Selenium IDE
启动 Firefox 浏览器后,转到 https://www.seleniumhq.org/download/ 。 向下滚动,直到到达 Selenium IDE 部分。 单击最新版本号,如下所示。 (请注意,您看到的版本号可能与此处快照中的版本号不同,因为 2.9.0 是创建此帖子时的最新版本。)
由于安全性设置,单击版本号后,可能会弹出一个窗口。 单击“允许”按钮继续安装。
如图所示进行下载,
下载完成后,单击“安装”按钮。
安装完成后,单击“立即重新启动”。
Firefox 重新启动后,我们可以通过以下其中一种方式启动 Selenium IDE,
- 使用
Alt + Ctrl + S
的组合。 - 单击工具栏中的 Se 图标(如果已可用)。
- 您始终可以使用从 Firefox 菜单启动它的常规方法。 如果您没有看到菜单,请按 Alt 键,它将弹出。 单击“工具 -> Selenium IDE”或“工具 -> Web 开发者 -> Selenium IDE”。
现在,让我展示一下 Selenium IDE 在启动时的外观。
步骤 3:安装 Firebug
关于 Firebug 的几句话:它是 Mozilla Firefox 的免费开放源代码 Web 浏览器扩展。 我们将利用其优势,在使用 Selenium 进行自动化的情况下,识别和检查应用的 Web 元素。 如果您不了解我在说什么,那完全没问题。 只需立即安装 Firebug。 随着我们更深入地探索,一切都会落在原地。
该采取行动了:启动 Firefox 并导航至 https://getfirebug.com/downloads/ 用于下载 Firebug。
根据与当前 Firefox 版本的兼容性,单击“下载”链接。
单击下载链接将带您到 Firefox 加载项页面。 只需在 Firebug 部分中单击“添加到 Firefox”。
Firefox 将下载该加载项。 完成后,将出现一个对话框以安装 Firebug。 点击“安装”按钮。
完成安装后,将看到一条通知,指出“Firebug 已成功安装”。 即使没有出现,也没有什么可担心的。
而且,安装 Firebug 后无需重新启动浏览器。
您一定想知道如何启动此加载项。 这很简单,有两种方法可以实现,
- 按下功能键
F12
- 单击 Firebug 图标,该图标将在 Firefox 工具栏的右上角提供。
Firebug 通常会在 Firefox 窗口底部启动,这就是它的外观,
今天就这些了。 您已经准备好进行一些视觉处理,但这需要稍等片刻。
在另一篇文章中再见,我们将在 Selenium IDE 中探索更多有关选项的信息。 祝你今天愉快!
7C Selenium IDE – 突破表面:初探
原文: https://javabeginnerstutorial.com/selenium/7c-selenium-ide-first-look/
朋友! 今天我们要看看是什么使魔术草地如此神奇地生活在其中。不,说真的,这真是太棒了! 不相信我吗? 继续阅读...
我知道您有兴趣了解此 Selenium IDE 究竟将为您提供什么功能,以便您能够轻松地自动化测试中的应用。
因此,这里是对 Selenium IDE 的初步了解:
这应该使您对其中的所有内容都有基本的了解。 简短,甜美,而且更简单!
无论如何,我们将更深入地研究这些功能,但稍后我将对其进行保存。 请继续关注此空间,以确保您不会错过任何即将发布的帖子!
祝你有美好的一天!
7D Selenium IDE – 了解您的 IDE 功能
原文: https://javabeginnerstutorial.com/selenium/7d-know-ide-features/
欢迎回到魔术草地! 今天,我们将深入了解在我之前的文章“打破表面:初看起来”的帮助下安装的 Selenium IDE 的功能。
作为参考,我将粘贴一段时间后看到的第一张图片,
菜单栏
菜单栏有五个组件,即文件,编辑,操作,选项和帮助。 让我们专注于我们将经常使用的那些。
a. 文件菜单
- “文件”菜单用于创建,打开,保存,导出,关闭测试用例和测试套件。
- 也可以查看最近使用的测试用例/测试套件文件。
- 默认情况下,测试以 HTML 格式保存。
- 这里最方便我们使用的最佳选项是“将测试用例导出为…”和“将测试用例导出为…”,因为它们使我们可以将 Selenium IDE 测试用例,自动转换为可以直接在 Selenium 远程控制或 WebDriver 中使用的格式组合(编程语言/单元测试框架/ Selenium 框架)。 我们将继续进行介绍。
下面是文件菜单的快照,其中“导出测试用例为...”选项已展开。
如您所见,使用 Selenium IDE 2.9.0 版,我们可以在 Ruby,Python 2,C# 和 Java 语言中导出当前打开的测试用例,及其相应的单元测试框架。
b. 编辑菜单
- 编辑菜单使我们可以撤消,重做,剪切,复制,粘贴,删除和全选命令。
- 此菜单中值得记住的选项是“插入新命令”和“插入新注释”。
- “插入新命令”将允许用户在当前所选命令的顶部顶部插入新命令。
- “插入新注释”将允许用户在当前所选行的顶部上添加新注释。 这对于文档目的非常有用。
这是为您提供的“编辑”菜单,
是时候换另一个相关的 BrainBell 了! 使用我们已经知道的东西来关联我们正在学习的内容,可以帮助大脑更轻松地记住。 就像在大脑上贴笔记一样。
因此,这里是我们著名的诗,但稍作修改,
玫瑰是红色
紫色是蓝色
(在 Selenium IDE 中)
命令是黑色 &
注释是紫色!
如果您认为自己可以做得更好,请继续尝试。 随时欢迎您在评论部分中发表您的想法和想法。
c. 动作菜单
为了更好地理解和可视化,从菜单快照开始,
- “操作”菜单允许我们在处理测试用例时执行一些操作,例如记录浏览器操作,播放选定的测试用例/测试套件等。
- 通过在测试用例的任何执行步骤中设置一个或多个断点,在调试中使用“切换断点”。 这有助于强行中断执行。
- “设置/清除起点” 用于设置或清除任何测试步骤的起点。 因此允许我们从设置的起点开始执行。
- 可以使用“执行此命令”来选择并执行特定命令。
d. 选项菜单
- 选项菜单是最重要的菜单之一,因为它使我们能够配置 Selenium IDE 工具的各种预定义设置。
让我们一一看。 首先单击“选项…”,将打开“Selenium IDE 选项”对话框,如下所示:
在常规标签中:
- 默认超时值 – 这表示 Selenium IDE 在产生错误之前宁愿等待测试步骤执行的时间(以毫秒为单位)。 默认值设置为 30000ms,即 30s。 可以根据 Web 元素出现或可访问的速度适当增加或减少此值。
- 扩展 – Selenium IDE 扩展增强了该工具的功能,可以通过从下载所需的扩展来实现此功能 https://addons.mozilla.org/en-US/firefox / 链接,在页面右上角的搜索框中使用“Selenium”作为关键字。 除了作为基准 IDE 扩展的一部分定义的扩展之外,这些还将是其他扩展。
- 记住基本 URL – 选中此选项将确保 Selenium IDE 每次启动时都会记住该 URL 或将其加载到该工具的基本 URL 文本框中。 取消选中此选项后,IDE 将在基本 URL 栏中以空白值启动。
- 启用实验性功能 – 首次使用活动的互联网连接检查此字段将导入可用的格式,我们将在稍后看到。
格式选项卡:
此选项卡显示 Selenium IDE 中的可用格式以及所选格式的结构示例。 用户可以轻松地添加/重命名/删除任何可用格式。
“插件”选项卡显示该工具可用的默认插件。 提供了许多插件,可以轻松下载和安装。 有几个这样的示例,
- “Selenium IDE:PHP 格式化程序” – 用于将 HTML 测试用例转换为 PHP 格式
- “文件日志” – 将生成的日志保存到文件
定位器构建器选项卡,
定位器是 Selenium 在网页上唯一地定位或标识 Web 元素的一种方式。 例如,在记录一个测试用例时,如果我们单击一个 Web 元素,Selenium 将立即查找其“ui”属性以存储其位置,因为列表中首先提到了该属性。 如果“ui
”不可用,它将查找“id
”,因为它在列表中排第二。 一直进行到找到所单击元素的唯一定位器为止。 因此,此列表列出了在重新编码时优先确定定位器生成顺序的方法。 只需在左窗格中进行简单拖放,即可根据需要更改顺序。
其次,在“选项”菜单中有“格式”:
格式选项显示可以将 Selenium IDE 测试用例转换成的可用格式。 HTML 是选择的默认格式。 格式提供了将所选测试用例转换为的编程语言/单元测试框架/套件中可与代码一起使用的工具的组合。
第三个重要选项之一是“剪贴板格式”。 参见下图,
这看起来与我们刚刚看到的“格式”选项非常相似。 从测试脚本窗格复制命令时,剪贴板格式可帮助我们选择希望粘贴时显示代码段的格式。 HTML 也是此处的默认选择。
e. 帮助菜单
帮助始终适用于 Selenium,因为它具有广泛的社区和用户基础。 此选项使我们可以报告问题,搜索问题,仔细阅读可用的文档,查看发行说明,官方博客和网站; 从而提供了大量信息。
基本网址栏
这是一个下拉列表,用于记住所有以前访问的 URL,以便于访问。 它类似于我们在网页中看到的地址栏中的 URL。
带有空白目标的“打开”命令将导航至基本 URL 栏中指定的 URL。
鉴于如上图所示,如果将“打开”命令与“/download/
”之类的目标一起使用,Selenium IDE 将带我们进入 https://www.seleniumhq.org/download/ 页面 。 因此,目标被附加到指定的基本 URL。 因此,这在相对 URL 的情况下非常有用。
为了不使这篇文章太长,让我们休息一下,一旦您回来,请单击下一篇文章“继续了解您的 Selenium IDE 功能”。
再见。 祝大家有美好的一天!
7E Selenium IDE – 了解您的 IDE 功能(续)。
原文: https://javabeginnerstutorial.com/selenium/ide-know-ide-features-contd/
休息后欢迎回来。 猜猜您已经准备好继续我们的功能探索。 所以,我们开始!
工具栏
Selenium IDE 提供了一个非常简单但有用的工具栏,可用于点击来记录,播放,暂停,逐步和安排测试。 是时候深入了解了……
回放速度:允许控制所选测试用例/测试套件的执行速度。
播放整个测试套件:允许顺序播放属于一个测试套件的所有测试用例。
播放当前测试用例:允许播放在左侧的“测试用例”窗格中当前选择的测试用例。
播放当前测试用例:允许播放在左侧的“测试用例”窗格中当前选择的测试用例。
步骤:帮助逐步进入测试脚本中的每个命令,并且一次只能执行测试用例的一个步骤。 这在调试测试时很方便。
汇总:允许组合 Selenese 命令的重复序列并将其作为单个动作执行。 它是此工具的高级功能。
测试调度器:允许我们根据需要设置测试计划规则。 此选项可以打开或关闭。
单击该图标将弹出“测试调度器”窗口,如上所示。 可以提供职位名称。 可以通过单击适当的日期,小时,分钟。 提供高级选项,例如每小时,每 5 分钟等。
记录:用于开始/停止用户浏览器动作的记录。 在中央的测试脚本窗格中,每个动作均作为 Selenese 命令输入。 空心的红色圆圈表示该工具正在记录,而此处所示的实心圆圈表示记录已结束。 默认情况下,在启动 IDE 时,它处于记录模式。
测试用例窗格
Selenium IDE 工具的左侧有一个测试用例窗格,如下所示,
- 它显示了当前打开的测试用例的数量。
- 当前选择的测试用例以粗体显示。
- 可以打开多个测试用例。
- 打开测试套件将自动打开并列出与之相关的所有测试用例。
- 一旦执行了这些测试,它们就会用颜色编码。 绿色表示测试用例通过,红色表示失败。
- 测试结果显示在底部。 栏中的颜色表示整个测试套件的通过/失败状态。 还显示了运行的测试案例总数和失败的测试案例数量。
测试脚本窗格(或)编辑器
此部分显示记录的测试用例的脚本。 每个动作将按照在浏览器中执行的顺序记录和显示。 有两种观点。 表视图和源视图。
a. 表格检视:
- 这是启动 IDE 时将打开的默认视图。
- 它以可读的表格格式显示命令及其参数。
- 执行测试用例时,每个步骤都用绿色或红色进行颜色编码。
- 可以使用下面的“命令/目标/值”部分插入和编辑命令。 整个博客都专门介绍了这一部分。
b. 资料来源检视:
- 这显示了测试用例的保存格式。
- 默认情况下显示 HTML 格式。
- 其他编程语言(例如 C# ,Java,Ruby,Python)也可以借助菜单栏中的“选项”标签来选择。
- 源代码视图还可以帮助以原始格式编辑脚本以及剪切,复制和粘贴选项。
日志/参考/ UI 元素/汇总窗格
该窗格具有四个功能。 根据选择的选项卡,执行其相应的功能。
a. 日志窗格
- 即使未选择“日志”选项卡,这也是显示的默认窗格。
- 在执行测试用例时,将在运行时中显示信息和错误消息。
- 这些用于调试测试用例。
- clear 选项用于清除到目前为止生成的日志。
- 信息选项是一个下拉菜单,其中有四个选项-调试,信息,警告和错误。
- 通过选择我们的日志记录级别,可以过滤特定级别的消息。 下面显示了过滤错误消息的示例。
- 调试:提供有关 Selenium IDE 在后台执行的操作的技术信息,例如调用函数,加载外部 JavaScript 文件等。
- 信息:提供有关当前正在执行的命令的信息。
- 警告:在极少数情况下显示警告消息,例如无效的参数/意味着较少的条件等。
- 错误:在执行导致无法通过的测试用例期间发生错误时显示的消息。 如果同样不满足“断言”和“验证”命令中提到的条件,则会生成错误。
b. 参考窗格
它显示了当前所选命令的说明以及所需目标和值的说明。
这些图像显示了所选命令“点击并等待”的参考。
c. UI 元素窗格
该窗格对 Selenium IDE 的高级用户最有用。 为了定位元素并标识其映射,使用 JSON(JavaScript 对象表示法)。 详细的文档位于“UI 元素文档”选项下的“帮助”菜单中。
d. 汇总窗格
这也是该工具的高级功能。 关于此方面的详细信息,可在“帮助”菜单下的同一“UI 元素文档”选项中找到。 因此,简单地说,当将 Selenium 命令的重复序列组合在一起时,在我们的 Selenium 世界中被称为“汇总”。 可以将其作为单个操作执行,并且这些汇总可重复使用,即可以多次使用,从而缩短了脚本长度,并使脚本更有意义。
话虽如此,我们几乎涵盖了 Selenium IDE 工具的所有功能,术语和部分。 掌握它,您就可以开始自动执行手动测试。
在另一篇文章中再见。 祝你有美好的一天!
7F Selenium IDE – 命令,目标和值
原文: https://javabeginnerstutorial.com/selenium/7f-ide-command-target-value/
朋友! 知道我们可以对自己做的事情有更多的控制权,这真令人兴奋吗? 是。 在魔法草地的土地上,Selenium IDE 不仅提供录制功能,还使我们能够插入和编辑命令。 使得这一切成为现实的是“命令/目标/值编辑器”。
我们可以创建和编辑 Selenese 命令。
命令:
- 命令是我们希望执行的动作。
- 这里有几个示例, 点击并等待和关闭。
- 当我们开始在“命令”部分中键入内容时,将显示一个匹配的命令下拉列表。
- 此外,另一个优点是,“参考”部分显示了所选命令的简要说明以及所需的参数。
哦! 记住整个命令列表以及它们的语法,必需的参数及其描述并不是真的需要惊动我们的大脑。 生活变得更轻松!)
目标:
顾名思义,这是我们要执行操作的网络元素,即看到一些魔术正在发生。 名称,ID,XPath 等可用于唯一标识所需的 Web 元素。
值:
我们希望为选定目标提供的数据或输入称为值。
例如,如果我们选择在 Selenium 上执行 Google 搜索,
命令将是“类型”
目标将是可以识别的搜索框,其“ID”和值将为“Selenium”
我们刚才讨论的内容不清楚吗? 绝对不用担心! 因为当我们记录并运行我们的第一个 Selenium IDE 测试脚本时,将获得清晰的画面。 最好的是,这就是我们下一篇文章的全部内容!
因此,如果您不想错过任何一条信息,请点击“关注”按钮!
探索愉快! 祝你有美好的一天。
7G Selenium IDE – 记录和运行测试用例
原文: https://javabeginnerstutorial.com/selenium/7g-ide-recording-test-case/
嗨呀测试人员! 欢迎来到魔术草甸地区的另一篇探索文章。 密切关注并共同努力,在回放录音时看到一些真正的魔术。
我们将通过录制并在回放功能的帮助下执行,在 Selenium IDE 中创建我们的第一个测试脚本。
场景:
- 打开 Gmail 登录页面
- 输入任何无效的用户名和密码
- 提交登录详细信息
- 确认由于提交无效凭据而显示的错误消息
- 声明页面标题
- 插入新命令以等待特定值
- 验证来自 Selenium IDE 工具的日志
- 将测试用例保存在所需的位置
步骤 1:
- 启动 Firefox 浏览器和 Selenium IDE 工具
- 默认情况下,录制按钮将处于 ON 状态
- 在基本 URL 文本栏中输入 URL 值“https://accounts.google.com/”
步骤 2:
- 在 Firefox 浏览器中导航到“https://accounts.google.com/”
- 确保录制按钮处于 ON 状态,否则切换以将其打开
- 输入任何无效的用户名,然后单击“下一步”
- 输入任何无效的密码。 此时,Selenium IDE 编辑器部分将如下所示:
步骤 3:
- 点击“登录”按钮
- 将显示错误信息
- 要验证消息,请选择它并右键单击
- 这将打开 Selenium IDE 上下文菜单
- 要查看所有命令,请单击“显示可用命令”
- 选择“
assertText id = errormsg_0_Passwd
您输入的电子邮件和密码不匹配。”
步骤 4:
- 右键单击页面上除超链接和图像以外的任何位置
- 选择“断言标题 Gmail”以验证页面标题
- 切换记录按钮以将其更改为关闭状态
- 完成的脚本如下图所示
步骤 5:
- 让我们在输入密码之前插入一个新命令
- 在 Selenium IDE 中,在命令“类型”(目标为“
id = Passwd
”)上,右键单击并选择“插入新命令”
- 在“命令,目标,值”部分,为命令输入“
waitForValue
”,为目标输入“id = Passwd
”
步骤 6:
- 要进行播放,请点击“播放当前测试用例”图标,如下所示,
- 检查方案是否被完美复制
- 成功执行后,每个步骤将以绿色编码
- 可以查看日志并根据需要选择日志记录级别
- 万一测试用例失败,错误将显示在以红色编码的日志中
步骤 7:
- 对结果满意后,我们想保存此测试用例,以备将来之用,例如回归测试
- 点击文件菜单,然后选择“保存测试用例”
- 在弹出的窗口中输入文件名,然后点击“保存”
- 默认情况下,保存的文件将采用 HTML 格式
现在轮到您尝试新的情况了。 录制完成后,请放松,放松并观看 Selenium IDE 为您进行测试。
祝你有美好的一天!
7H Selenium IDE – Selenium 命令一览
原文: https://javabeginnerstutorial.com/selenium/7h-ide-selenium-commands/
欢迎来到 Selenium IDE 上的另一篇有趣的文章。 今天,让我们解开记录测试场景时在测试脚本窗格中看到的命令之谜。 示例如下
大字警报! Selenese – 就是一组 Selenium 命令。 这些命令的序列构成一个测试脚本。 每行是一个 Selenium 命令,具有三个部分,即命令,目标和值。
命令 - 做什么/需要执行什么动作
目标 – 必须在何处(网络元素)进行操作
值 – 必须将哪些数据传递给目标
一些命令并不总是需要目标和值。 例如,“关闭”命令用于关闭窗口。 而“点击”命令需要一个目标,而“类型”命令则需要目标和值。
Selenium 命令具有三种不同的样式:操作,访问器和断言。
动作:
这些命令通过直接与 Web 元素进行交互来操纵应用的状态。 如果操作失败或发生错误,则当前测试执行将停止。 该测试脚本中的以下命令将不会执行。
命令 | 说明 |
---|---|
type(locator, value) |
设置输入字段的值,就像您键入它一样。 |
click(locator) |
单击链接,按钮,复选框或单选按钮 |
close() |
模拟用户单击弹出窗口或选项卡的标题栏中的“关闭”按钮 |
echo(message) |
将指定的消息打印到 Selenese 表中的第三个表单元格中。 对于调试很有用。 |
focus(locator) |
将焦点移到指定的元素; 例如,如果元素是输入字段,则将光标移动到该字段。 |
open(url) |
在测试框架中打开一个 URL。 这既接受相对 URL,也接受绝对 URL。 |
setTimeout(timeout) |
指定 Selenium 等待操作完成的时间。 |
这些操作大多数都带有后缀“AndWait
”,例如 “clickAndWait
”。 此后缀确保 Selenium 等待新页面或元素加载。
访问器:
这些命令用于检查应用的状态。 这些让我们将结果存储在用户定义的变量中,这些变量可用于断言的目的。 它们不会直接与页面元素交互。
例如,“storeAllLinks
”将读取页面上所有链接的 ID,并将其存储在用户定义的变量中。 如果结果有多个值,则该变量将为数组类型。
断言:
它们与访问器相似,因为它们也不直接与 Web 元素交互。 断言主要用于验证应用状态是否符合预期。
断言有三种模式,
“声明” – 当“声明”失败时,将立即停止测试执行。 测试脚本的其余部分未执行。
“验证” – 当“验证”失败时,Selenium IDE 用红色记录失败,然后继续执行。
“等待” – 等待特定条件变为真,然后继续执行下一个命令。 默认情况下,超时值设置为 30 秒。 可以将其更改为所需的值。 失败时,测试执行将继续进行下一步。 Selenium IDE 日志窗格中记录了失败。
命令 | 说明 |
---|---|
verifyTitle /assertTitle |
验证预期的页面标题 |
verifyElementPresent |
验证页面上是否存在预期的 UI 元素(如其 HTML 标记所定义) |
verifyText |
验证页面上是否存在预期的文本及其相应的 HTML 标记 |
verifyForm |
验证表格的预期内容 |
waitForPageToLoad |
暂停执行,直到加载预期的新页面。 使用clickAndWait 时自动调用 |
waitForElementPresent |
暂停执行,直到页面上显示预期的 UI 元素(由其 HTML 标记定义)为止 |
有关即将发布的博客中的“声明”和“验证”的更多信息! 在此之前,请继续练习各种方案。 有关这些命令的完整参考资料,请参见官方网站。
在另一篇文章中再见。 祝你有美好的一天!
7I Selenium IDE – 设置超时,断点,起点
原文: https://javabeginnerstutorial.com/selenium/7i-ide-timeouts-breakpoints-startpoints/
生活不仅要在魔法草地的土地上玩耍,暂停和停止,还应有更多! 欢迎回到 Selenium IDE 上的另一篇有趣的文章!
自定义默认超时值 – 这有什么用? 嗯,让我们看看...
并非网络上的所有事情都如我们所愿发生(如果这样,生活会好得多)。 有时,网页上的特定 Web 元素显示的速度比预期的慢,即以较慢的速度可访问。 这可能会导致在回放我们的自动化测试用例并完全停止执行时出错。 这不是我们所希望的!
相反,我们希望 Selenium IDE 等待更长的时间,即在这种情况下可以访问我们的 Web 元素。
为方便起见,选项菜单的“常规”标签中提供了默认超时值选项,可以更改。
上图突出显示的该值表示 Selenium IDE 在生成错误之前希望等待测试步骤执行的时间(以毫秒为单位)。 变得硬核,该请求已发送到服务器,但由于默认值设置为 30000ms(即 30s),因此在 30 秒内没有任何响应。 该值可以适当地增加或减少以适合我们的需求。 这样就克服了超时错误! 欢呼!!!
BrainBell – 练习!是的,这一点很重要。 不要只盯着屏幕。 输入并执行一些测试方案。 如果我为您做一切,那么您的大脑健康就不会长久。 因此,准备好前进,走在那些坎坷的道路上吧!
下一步: 断点如何工作? 询问者可能希望知道……
从而导致测试用例失败。
“动作”菜单中显示的切换断点使我们能够通过简化复杂对象来实现目标。
切换,这意味着如果该断点已经存在,请单击“切换断点”将其删除,反之亦然(就像Caps Lock
键如何在两种模式之间切换一样)。 因此,将根据断点的现有状态将断点插入或从所选命令中删除。
因此,我们可以在单个测试用例中使用多个断点在所需的步骤处暂停执行。 这可以通过以下方式实现:
- 右键单击特定命令,然后选择“切换断点”选项。
2.选择所需的命令,然后按“b
”或“B
”(快捷键)。
3.选择所需的命令; 点击“选项”菜单,然后选择“切换断点”。
一旦执行到达带有断点的命令并暂停以进行移动,我们将再次有 2 个选项,
- 通过单击工具栏中的暂停/恢复按钮来恢复执行,如上图所示,或者,
- 一次单步执行/执行后续命令,单击“单步”按钮以查看每个命令的确切功能,并找出导致错误的命令。
我同意单步是一个非常棒的功能,在我们必须进行认真的调试时非常方便! 因此,我将即将发表的帖子完全用于同一主题。
下一步: 设置/清除起始点,这是一个指示器,用于告知 Selenium IDE 必须开始执行的命令。
我们可能希望在一个真正的大型测试案例中重复执行一小组测试步骤。 运行整个测试用例会浪费很多资源。 在这种情况下,我们可以通过两种方式设置起点,
- 右键单击必须设置起点的命令,然后选择“设置/清除起点”。
- 选择特定的命令,然后按“
s
”或“S
”(快捷键)。
在上面显示的示例中,播放将从第三行开始执行。 请务必注意,Firefox 浏览器当前显示的页面上存在依赖项。 如果您在错误的页面上无法执行所选命令,则测试用例将在起点失败。 同样,每个测试用例只能有一个起点。
为了清除正在设置的起点,请选择带有起点的命令,然后使用快捷键“S
”或右键单击并选择“设置/清除起点”选项。
有了这些知识,我就让您自由地继续今天的 BrainBell! 是的,练习我们刚刚讨论的所有内容,我将在 Selenium IDE 上发表另一篇文章。
祝你有美好的一天!
7J Selenium IDE – 调试
原文: https://javabeginnerstutorial.com/selenium/7j-ide-debugging/
嗨呀测试人员! 欢迎回到我们针对 Selenium 初学者的全职领导者的另一篇探索文章,即“Selenium IDE” !! 现在是时候密切注意进行调试了。
我们的目标雄心勃勃,但我们将一次迈出一步。
首先,什么是调试? 它正在识别并修复测试用例中可能存在的任何错误。 就如此容易! 而且最重要的是调试和测试我们的代码,以确保其以预期的方式工作。
让我们把脚弄湿! 提出了不同的方式来帮助我们调试错误的测试脚本,并在我们提供的产品上保持一流!
断点和起点
(有关详细说明,请参阅我以前的博客“7i。Selenium IDE – 设置超时,断点,起点”)
- 断点使我们能够运行测试用例中的特定命令,并在该点停止检查行为。
- 确保在要检查的命令之前在命令上设置一个断点。
- 我们可以在一个测试用例中设置多个断点。
- 起点在我们必须从中间到最后运行测试用例时很有用。
- 例如,假设第一步是登录,然后在网站上执行一系列测试,而您尝试调试其中一个。 为此,您只需登录一次,在测试用例的登录部分之后设置一个起点,然后根据需要重新运行多次。
- 每个测试用例只能有一个起点。
逐步通过测试用例
是! 单步按钮用于通过一次执行一个命令来逐步遍历我们的测试用例。 仔细查看日志窗格,您会发现为每个执行的命令附加了一行日志。 而且,您可能需要在运行测试用例之前清除日志窗格,因为 Selenium IDE 不会自动执行此操作。 它只是将新生成的日志附加到现有日志中。
是时候看一个示例来更好地理解“步进”功能了,
下面显示的场景非常简单,在 www.amazon.com 中创建了一个帐户。 在客户必须输入用户名的地方设置了一个断点。 因此,将执行所有带有断点的命令。
点击“单步”之前,
单击工具栏中的“播放当前测试用例”按钮后,将执行断点之前的所有命令。 它停在“typewait | id = ap_customer_name | user1
”这一行。
点击“单步”后
运行“typewait | id = ap_customer_name | user1
”行,测试用例失败。 仔细查看日志部分会显示错误以及说明。
该错误是由于未知命令“typewait
”引起的。 通过选择命令并在“命令/目标和值”部分的命令下拉菜单中将“类型等待”编辑为“类型”,即可解决该错误。
让我们按照讨论进行更改后重新运行测试用例。 由于存在断点,执行将再次在“type | id = ap_customer_name | user1
”行处暂停。 点击工具栏中的“步骤”按钮。
执行一个命令,即“type | id = ap_customer_name | user1
”,并在网页的“您的姓名”文本框中输入该命令的值列中显示的客户名称。 现在,执行将在下一个命令“type | id = ap_email |
”。
请务必注意,尽管此处没有断点,但执行仍然暂停。 这就是逐步执行的意思,一次执行一个步骤 – 功能的目的。 它使我们有更多时间检查每个命令的结果,从而帮助我们对其进行修复。 重复选择“步骤”按钮,直到到达测试用例的末尾,以了解整个测试是否通过。
外带:单击“单步”按钮一次将执行一个命令,而在下一个命令上暂停。
查找按钮
这看似不是杀手级功能,但确实很棒。 “查找”按钮对于验证我们是否正在使用/在当前测试用例中为所选命令在浏览器中显示的当前网页上选择了正确的 UI 元素非常有用。
选择要识别其 UI 元素的任何命令,然后单击“查找”按钮,如上图所示。 在选定的命令(本例中为“id
”)中由locator
参数指定的对应元素在 Firefox 浏览器显示的网页上以黄色的瞬间显示为高亮显示。 包含在绿色边框中。
因此,Selenium IDE 能够按预期识别和访问元素。 如果它在网页上突出显示了错误的元素或根本没有检测到元素,那么我们可以确保选择的定位符类型(在即将发布的博客中有更多相关内容)或脚本本身。
页面源
在确定问题时,此选项通常很方便。 页面源仅是被测试网页的 HTML。 在 Firefox 浏览器中,实现这一目标确实是小菜一碟。 有几种方法,
- 右键单击网页上的任意位置,然后选择“查看页面源”。 这将打开一个显示整个 HTML 的新标签。 使用搜索功能“
Ctrl + F
”搜索关键字并找到我们要测试的 UI 元素的 HTML 部分。 - 另一种简单的方法是选择我们希望看到其源代码的网页部分。 现在,右键单击并选择“查看选择源”。 这还将打开一个新选项卡,但仅显示 HTML 的一小部分,突出显示代表网页中所做选择的代码。
了解这些漂亮的调试功能后,您会不会感到更有力量? 那你还在等什么呢? 您的脚已经湿了,潜水!
在另一篇文章中再见。 祝你有美好的一天!
7K Selenium IDE – 定位元素(按 ID,名称,链接文本)
原文: https://javabeginnerstutorial.com/selenium/7k-ide-locating-elements/
大家好! 这篇文章是关于在网页上定位元素的(正如上一篇文章中所承诺的)。
如今,确定一个人的地理位置变得非常容易。 GPS 使这成为可能。 同样,可以使用定位符类型来确定 Selenium IDE 必须对其进行操作的确切 Web 元素(例如,文本框,复选框,按钮,链接,下拉列表等)。
如果您注意到由 Selenium IDE 生成的自动测试脚本,则可以看出大多数命令都需要目标。 该目标标识正在测试的 Web 元素,其格式为LocatorType = Location
。 LocatorType
在大多数情况下可以省略,我们很快就会看到。
到现在,您应该已经意识到,对于创建成功的测试脚本而言,准确地识别网页上的 Web 元素至关重要。 记住我的话,说起来容易做起来难! 有时,我们最终会遇到错误的元素或最坏的情况,根本没有任何元素。
当我们在魔法草地中时,为什么还要担心? Selenium IDE 提供了许多定位器类型,可以在网页上准确定位 Web 元素。
定位依据
- ID
- 名称
- 链接文本
- CSS
- DOM
- XPath
通过 ID 定位
知道网络元素的 ID 属性后,我们总是倾向于按 ID 进行定位。 由于 ID 是唯一的,因此这是定位被测元素的最常用方法。
格式:id = web_element_id
示例:打开 Google 帐户创建页面。 让我们通过 ID 识别名字文本框。
借助 HTML(页面源代码)或 Firebug(我们将在以后的文章中详细介绍),可以识别 ID,如上图所示。
在 Firefox 浏览器中打开 Selenium IDE,然后在目标文本框中输入“id = FirstName
”。 单击查找按钮,并注意“名称”部分的“第一个”文本框以黄色突出显示,带有绿色边框。 这表明 Selenium IDE 能够成功地按预期找到元素。
按名称定位
名称定位器类型使用匹配的名称属性来定位第一个 Web 元素。 这与按 ID 定位非常相似,而 ID 由名称代替。
格式:name = web_ element_name
示例:打开 Google 帐户创建页面。 让我们通过使用页面来源的名称来标识“选择您的用户名”文本框。
使用 Selenium IDE“查找”元素以验证我们是否准确定位了该元素。
如果多个 Web 元素具有相同的name
属性,则使用过滤器进一步细化定位策略。 默认过滤器类型为值。
示例:
如上所述,如果使用“name = travelType
”定位策略,则将在网页上选择一个单选按钮,因为它是具有给定name
属性的第一个 Web 元素。 但是,如果我们希望根据测试用例要求使用往返单选按钮,则将使用按名称和过滤器策略定位。
在 Selenium IDE 的“目标”框中输入“name = travelType value = roundtrip
”,然后单击“查找”按钮,将高亮显示往返单选按钮。
通过链接文本定位
在网页上找到超链接的最佳方法是使用“链接文本”策略进行定位。
格式:link = text_of_the_link
示例:打开 Google 帐户创建页面。 让我们使用网页来源通过链接文字访问“隐私权政策”链接。
使用 Selenium IDE “查找”元素以验证我们是否准确定位了该元素。
我认为一天就足够了。 其余的将在后续文章中讨论。 练习这些策略,并在评论部分有任何疑问时通知我。
到那时见! 祝你有美好的一天!
7L Selenium IDE – 定位元素(续)
原文: https://javabeginnerstutorial.com/selenium/7l-ide-locating-elements-contd/
嗨呀测试人员! 我回来了,比赛开始了! 让我们继续前进,在我们的网页上找到一些古怪的元素!!
在这篇文章中,我们将看到:
定位依据
- CSS
- DOM
- XPath
让我们一一看一下。
通过 CSS 定位
CSS(层叠样式表)将样式添加到网页中,通常用于描述 HTML 文档的显示或呈现。 为了将这些样式属性绑定到文档中的元素,使用了 CSS 选择器。 这些选择器用于根据其名称,ID,类,属性等的组合来选择 HTML 元素。
通常,高级 Selenium 用户会通过这种方法在网页上定位元素。 它有点复杂,但主要在元素没有 ID 或名称的情况下首选。
指定目标的格式以标签“css =
”开头,后跟相应的标签名称和其他必需元素。 让我们欣赏使用最常用的 CSS 选择器定位元素的巨大优势。
标签和 ID
HTML 标记和元素的 ID 与井号(#
)一起使用。
格式: css = tag#id
示例:打开 Amazon 的登录页面。 让我们找到“电子邮件(用于移动帐户的电话)”文本框。
标记名称为“输入”,元素的 ID 为“ap_email
”。
因此,按照目标格式,输入“css = input#ap_email
”并单击“查找”按钮将按预期突出显示网页上的“电子邮件”文本框。
标签和类别
此方法使用 HTML 标记和要访问的元素的类以及点(.
)符号。
格式:css = tag.class
示例:打开 Indeed.com,让我们突出显示文本“what
”。
标签名称为“td
”,元素的类别为“npb
”。
输入“css = td.npb
”,然后单击“查找”按钮,将突出显示文本“what
”。 如果多个元素具有相同的标记和类名称,则仅会识别与条件匹配的第一个元素。 在这种情况下必须使用过滤器。 在之前的博客文章中对此概念进行了详细说明。
标签和属性
在此方法中使用 HTML 标记,最好使用要访问的元素的唯一属性及其对应的值。 属性和值用方括号[]括起来。
格式: css = tag[prop=value]
示例:打开亚马逊登录页面,让我们找到“电子邮件”文本框。
标签名称为“input
”,属性为“name
”,其对应值为“email
”。
为目标输入“css = input[name=email]
”,然后单击“查找”按钮,将突出显示“电子邮件”文本框。
标签,类和属性
这与“标记和属性”定位策略相似,但是带有类名和点(.
)符号。 通常在两个 Web 元素具有相同的标记和类名称时使用。
格式: css = tag.class[prop=value]
示例:打开 facebook.com,让我们找到“电子邮件或电话”文本框。
请注意下图,“电子邮件”和“密码”文本框具有相同的标签“input
”和相同的类名“inputtext
”,但属性“tabindex
”的值不同。
因此,标签名称为“input
”,类为“inputtext
”,属性为“tabindex
”且其对应值为“1”的标签将突出显示“电子邮件或电话”文本框。 属性值与“2”相同的组合将突出显示“密码”文本框。
输入“css = input.inputtext[tabindex=1]
”以突出显示“电子邮件或电话”文本框。
内部文本
在某些情况下,ID,类或属性未用于特定元素。 要找到这样的元素,可以使用网页上显示的实际文本(内部文本)。
格式: css = tag:contains("innerText")
示例:打开亚马逊登录页面,让我们突出显示文本“Password
”。
这里的标签是“label
”,内部文本是“Password
”。 输入“css = lable:contains("Password")
”高亮显示文本“Password
”。
通过 DOM 定位
文档对象模型是用于表示 HTML 文档中元素的约定。 定位策略利用了 DOM 模型中使用的树结构。 因此,使用分层的点分符号来获取元素的位置。
指定 DOM 定位器时,不需要“dom =
”标签,因为只有 DOM 定位器以单词“document
”开头。
示例代码:
方法 1:document.getElementById("testForm")
– 使用元素的 ID。
方法 2:document.forms[0]
– “表单”将返回文档中使用的所有表单的集合。 因此,使用索引以唯一的方式指定被测元素。
方法 3:document.forms[0].user1
– 第一种形式,访问user1
元素。
方法 4:document.getElementsByName("seatingClass")[1]
– “getElementsByName
”也将返回括号内指定名称相同的元素集合。 因此,索引返回所需的元素。 在这种情况下,可以访问业务单选按钮。 如果使用索引[0]
,则可以访问经济单选按钮。
方法 5:访问document.forms["testForm"].elements["user2"]
– 名为“testForm
”的表单的user2
。
方法 6:document.forms[1].elements[2]
– 这将返回“seatingClassForm
”中的“第一个”单选按钮。
现在是时候使用 Selenium IDE 探索 Web 应用的 DOM 了。
通过 XPath 定位
XPath 是一种语法,用于在 XML 文档中导航和定位节点。 由于 HTML 只是 XML 的实现,因此可以使用 XPath 策略来查找所需的元素。
如果找不到合适的 ID,名称或类等来查找元素,则可以使用 XPath。 使用 XPath 定位元素有两种。 一个是绝对路径(不建议使用),因为它会在对现有 HTML 代码进行最小改动的情况下中断。 另一个选项是相对路径 – 使用 ID 或名称查找附近的元素,并使用两者之间的关系使用 XPath 查找所需的元素。 因此,几乎可以使用 XPath 策略定位网页上的任何元素。
这种定位策略非常复杂,通常是高级 Selenium 用户首选的定位策略。 但是不用担心! 为了使整个 XPath 查找方法变得容易,我们将“Firebug”作为 Firefox 浏览器的附加组件。 很快您就会遇到一篇博客文章,该文章完全致力于 Firebug 的安装和使用! 欢呼!!
另外,由于仅 XPath 定位符以“//
”开头,因此不需要为 XPath 定位符指定“xpath =
”。
示例:打开 Amazon 登录页面,然后让我们使用 XPath 找到“电子邮件”文本框。
点击 Firebug 图标并检查所需的元素(在本例中为“电子邮件”文本框)。 相应的源代码将显示在网页的下部。
右键点击突出显示的代码,然后选择“复制 XPath”,如下所示。
在目标中,输入一个正斜杠(/
),然后使用 Firebug 粘贴复制的 XPath,“//html/body/div[1]/div[1]/div[3]/div/div[2]/form/div/div/div/div[1]/input
”。 请注意,XPath 定位符应以两个正斜杠开头。
因此,“电子邮件”文本框将按预期突出显示。
你去! 我们刚刚完成了各种可用的定位策略。 是的,要选择的定位器类型完全取决于要测试的应用。
在将如此多的知识融入您的大脑之后,我知道您将每年两次要求我六个月的假期。 因此,在另一篇文章中再见。
祝你今天愉快!
7M Selenium IDE – 断言和验证
原文: https://javabeginnerstutorial.com/selenium/7m-ide-assert-verify/
嗨呀测试人员! 凝视水晶球,我发现在日常测试过程中非常需要断言和验证。 因此,在这里,我们什么也不做,专门为这个主题准备了一个完整的博客,以提高其亮度。
在测试 Web 应用时,我们一直期望以某种方式发生很多事情。 如果没有,则我们将测试断言为失败,然后中止执行或记录该特定异常以进行进一步分析,然后继续进行测试活动。 因此,这两个决定在 Selenium 中产生了两组不同的命令-断言和验证。
两者的主要区别在于,断言将使测试失败,中止当前测试用例的执行,而验证将使测试失败,但是 继续运行测试用例。
场景:
您可能希望检查页面第五段中是否有特定文本。 如果导航页面本身错误,执行这种检查有什么用? 在这种情况下,建议断言页面 URL,然后验证文本是否按预期出现。
您可能希望检查页面上多个属性的可用性。 这可以通过验证每个属性的存在来实现,因为在遇到第一个失败时测试用例不会中止。 这将帮助您同时查看页面的所有失败并采取必要的措施。
示例:
玩的时间到了! 让我们断言 Gmail 页面的标题,并进行文本,后退箭头和超链接验证。
*首先,让我们考虑一个全正面的情况。 我们导航到右侧页面,标题为 Gmail,输入电子邮件并单击下一步后,将显示文本“一个帐户。 所有 Google。”,后退箭头和指向“使用其他帐户登录”的超链接。 测试用例成功。
其次,让我们尝试断言失败的情况。 我们将打开 google 页面并断言其标题为“Selenium – Google Search
”。 这将导致失败并中止测试用例,因为当前页面标题将为“Google
”。
最后,我们将使验证语句失败,并确保测试用例即使在记录错误并整个测试用例失败之后也能继续执行。 打开 Google 页面,断言其标题为“Google
”,验证按钮“Google Search”,验证超链接“Gmail”,其值为“Gmail
”,并验证是否存在 Google Apps 图标。
通过将超链接的值更改为“Gmail11
”而不是“Gmail
”,验证命令将失败,但是测试用例将继续执行记录错误。
当心!您可能会遇到“找不到元素”错误。很奇怪:
即使命令只是一个验证,而我们要验证的元素不在页面上,也不会执行连续的步骤,并且测试用例也会失败。 由于验证通常用于检查文本的存在,并且如果未找到元素本身,则应该在应用中存在需要立即解决的错误。 因此结果。
是时候休息一下了! 练习一些场景并加深断言和验证的知识,迟早会成为 SeleniumVille 中生活的重要组成部分。 在另一个帖子中再见!
祝您有美好的一天!
7N Selenium IDE – 利用 Firebug 的优势
原文: https://javabeginnerstutorial.com/selenium/7n-ide-using-firebug/
嗨呀测试人员! 欢迎来到另一篇探索文章。
最近,我们讨论了如何使用 XPath 在网页上定位元素。 您可以使用页面的源代码,并提供一个非凡的 XPath,但是它可能无法正常工作。 我知道这并不是很能激发人们的兴趣,但是 XPath 策略非常复杂,高级 Selenium 测试人员经常使用它。 来吧,沉思它没有意义。
曾经有人说我的奴才,如果计划 A 失败,请记住您还有 25 个字母! 因此,不要放弃! 我们强大,无敌,无与伦比的测试人员今天将使用 Firebug 来发挥我们的优势。
Firebug 是 Mozilla Firefox 浏览器的附加组件。
安装步骤:
打开 Firefox 浏览器。 转到 https://addons.mozilla.org/en-US/firefox/addon/firebug/ 并单击“添加到 Firefox”按钮。
点击“安装”开始安装。
安装完成后,将显示如下通知。
Firebug 图标也会自动添加到浏览器的工具栏中。
单击工具栏上的图标或按 F12 键将显示 Firebug 的 UI,这使我们可以无限制地监视任何网页中实时存在的 CSS,HTML 和 JavaScript 以及其他功能,例如检视元素等。
检查所需元素将显示相应的源代码。 右键单击同一文件夹,将显示“复制 XPath”的选项。 它是如此简单!
现在,我建议您通过单击这里的来看看如何通过 XPath 策略定位元素。
在另一篇文章中再见。 祝你有美好的一天!
7O Selenium IDE – 以所需的语言导出测试用例
原文: https://javabeginnerstutorial.com/selenium/7o-ide-export-testcase/
您是否意识到您现在不再是 Selenium 的新手? 最好的部分是,您在使用 Selenium IDE 方面已经走了很长一段路,并且您将接近其边界! 因此,以同样的热情,让我们继续前进,并确保在这一美好的旅程中,我们不遗余力!
今天,让我们全神贯注,找出如何以我们希望使用的编程语言导出自动化测试代码。 相信我,除非您真正使用它,否则您不会意识到它有多方便。 那么,我们还等什么呢? 让我们开始吧!
场景:
- 打开“Google 帐户-登录”页面,网址为 https://accounts.google.com。
- 输入电子邮件 ID
- 单击下一步,然后输入密码,进行测试。
- 单击下一步,并确认文本“您输入的电子邮件和密码不匹配。” 出现在屏幕上。
- 断言当前页面的标题是 Gmail。
下图显示了默认表格式视图中的测试用例。
将测试用例导出为:
单击文件->将测试用例导出为...->选择所需的编程语言组合(应将所选的测试用例转换为该语言)/单元测试框架/套件中使用该代码的工具。
将文件保存在所需的本地路径中。 在这种情况下,文件将以“.java
”扩展名保存。 双击保存的文件将如下所示打开,
请注意,在这种情况下,一旦导入或粘贴到 IDE(例如 Eclipse,NetBeans 等),导出的测试用例可能会有警告或错误。 对于此单个测试用例,在选择的组合中运行也可能确实不需要一些不必要的代码。 我们将在 Selenium WebDriver 博客(即将发布)中详细介绍如何相应地编辑文件。 作为自动化测试人员,我总是建议编写代码,而不是记录和生成测试用例。 这么说,“将测试用例导出为……”似乎不是杀手级功能,说实话,它可以使用一些改进,但是对于初学者来说,了解并掌握该工具是一个很好的起点。 。
剪贴板格式:
另一种选择是“剪贴板格式”,在“选项”菜单中可用。
这有助于我们选择在执行粘贴操作时希望代码段显示的格式。 HTML 是默认选择。 在这种情况下,让我们选择“Java/Junit4/WebDriver”。 当选择了测试脚本窗格中的所有命令(Ctrl + a
)并执行了复制(Ctrl + c
)时,可以通过粘贴(Ctrl + v
)获得所选组合的代码段。 粘贴后的结果如下:
因此,借助 Selenium IDE 的“导出测试用例为”和“剪贴板格式”选项,可以简单地实现目标。 现在该轮到您探索这些功能,并在评论部分提出任何疑问。
在另一篇文章中再见。 祝你有美好的一天!
7P Selenium IDE – 其他功能
原文: https://javabeginnerstutorial.com/selenium/7p-ide-features/
在魔幻草原之地,您身边有很多功能,这使我们的生活更加轻松! 我同意,我们已经介绍了很多功能,但是在此我想重点介绍这两个特殊功能。
启用实验功能
在我们的上一篇文章中,我们看到了如何将测试用例导出到所需的编程语言,否则默认情况下将其记录为 HTML。 想象一下,您不仅希望导出,还希望在测试脚本窗格的“源”选项卡中以所需的所选语言查看确切的代码。 无论出于什么原因,您可能都希望进行一些探索和试验。
别担心,您的愿望就是我的命令。 让我向您展示我们现在和现在如何实现这一目标。 好吧,我们该怎么做? 简单。 通过选中一个复选框(我知道这听起来很机灵…),吹嘘自己为“启用实验性功能”!
让我们进入细节。 通常,在单击选项->格式后,您可能会看到一条消息“是否要返回格式? 点击阅读更多”,如下所示,
因此,在不显示格式的情况下,我们将如何查看所选语言的代码?
因此,单击“选项->选项...”,然后会打开“Selenium IDE 选项”窗口。 在“常规”标签中,选中“启用实验性功能”复选框。 点击“确定”按钮。 而已!
现在,“格式”选项显示了 Selenium IDE 测试用例可以转换成的可用格式。 HTML 是默认选择的格式。 该格式提供了将所选测试用例转换为的编程语言/单元测试框架/套件中可与代码一起使用的工具的组合。
选择所需的格式组合(在这种情况下为“Java/Junit4/WebDriver”)后,将出现警报。
注意:
如上图所示,此功能被标记为实验性,并且这样做是有原因的。 始终建议使用“文件”菜单中的“导出测试用例为”功能将测试用例转换为另一种语言。 通常会警告您,除非您确实确实想要,否则请不要使用“格式”菜单项。
想知道为什么所有这些复杂的功能看起来如此简单? 让我们看看……Selenium IDE 只能与基于 HTML 的 Selenese 一起使用。 因此,启用此功能后,它会以所选语言显示代码,就像以该语言进行录制一样。 但不是! 真正发生的是,在后台,它维护了测试用例的副本,并在进行中转换为所选的语言,并向您显示。 可悲的是,这种“格式”功能并不十分稳定。 保存测试用例并尝试重新打开后,它可能会或可能不会发生。 多数情况下可以,但是我注意到播放选项被禁用。 另外,不仅很难进行更改,而且当再次更改格式时所做的更改也会丢失。 现在是您的电话!
无论如何,如果您决定继续尝试一下,请继续并在警报框中单击“确定”,然后注意“表”视图窗格已禁用。
但是 Voila! 现在,“源”视图窗格将以选定的格式显示测试用例!!
生成命令的简单方法
Selenium 涉及的一项主要活动是使用不同的定位器来识别 Web 元素。 Selenium IDE 会自动为您执行此操作。 但是在某些情况下,您可能想插入新的命令(以及通过记录活动获得的自动生成的代码)来进行断言,验证等。在这种情况下,请不要使用检查元素功能来定位 Web 元素,然后努力使用正确的命令语法,我们有了一个捷径。 右键单击! 你没听错。
右键单击您要在其上执行操作的网页元素(显然是在 Firefox 浏览器中!),将自动打开一个菜单,提示与所选元素配合使用的大多数可能的 Selenium 命令。 要获取整个列表,只需单击“显示可用命令”选项。
这里唯一的问题是:应该打开 Selenium IDE。 不需要打开“记录”模式。 只需打开 IDE 窗口就足够了。
希望对您有所帮助! 因此,在另一篇文章中再见。
祝你有美好的一天!
7Q Selenium IDE – 快速浏览插件
原文: https://javabeginnerstutorial.com/selenium/7q-ide-plugins/
当心,您将很快进入 Selenium IDE 系列的最后一篇文章。 因此,有时间规划一下未来的美好生活! 让我们快速看一下几个可用的插件!
官方网站上有许多插件,您可以从页面上它们的相应部分下载,网址为 http://docs.seleniumhq.org/download/。
失败的屏幕截图
当测试步骤失败时,抓取屏幕截图非常重要,以便可以相应地报告它。 在 Selenium WebDriver 中,通过编程语言实现非常简单。 开发此插件是为了在 Selenium IDE 中获得相同的结果。 一旦安装并打开它,在测试失败时,将捕获整个网页。 最好的部分是,您可以选择指定一个自定义位置,以将这些屏幕截图保存在相应的项目文件夹下,以备将来参考或轻松识别。
让我们首先查看下载和安装过程。
在“Selenium IDE 插件”下,单击“ScreenShot on Fail”的下载链接。
您将被重定向到 Firefox 的“加载项”页面,您可以在其中找到屏幕截图和插件说明以及“添加到 Firefox”按钮。
点击“添加到 Firefox”按钮将提示如下,
点击“安装”和“立即重启”按钮。
现在,该插件将自动添加到 Selenium IDE 中,并显示在工具栏上。
单击下拉箭头将显示选项,以查看故障屏幕截图并自定义报告的保存位置。
让我们先这样做! 要自定义将故障报告存储在我们系统上的目标文件夹的位置,请单击“更改报告文件夹”,然后根据需要浏览路径。 点击“选择文件夹”以确认选择。
让我们看一个示例,以了解其工作原理!
- 打开“https://www.google.com/”
- 将页面标题声明为“Google”
- 验证是否存在“Google 搜索”按钮
- 验证超级链接是否包含“Gmail”
- 点击“Gmail”链接,然后等待页面加载
- 尝试验证页面上不可用的内容,以使测试用例失败。
为了继续进行操作,请确保通过单击工具栏中显示的“屏幕截图失败”按钮一次来打开插件。 播放测试用例,由于找不到特定的 Web 元素,因此在最后一步将失败。
测试用例执行完成后,再次单击插件按钮将其关闭。 要查看故障报告,有两种方法:
- 从插件按钮下拉菜单中单击“查看失败报告”,以在当前打开的 Firefox 浏览器的新标签中打开报告。
- 或导航到系统中的自定义路径。 将创建一个名为时间戳的新文件夹,并将错误屏幕截图保存在其中。 当要执行涵盖不同项目的大量测试用例时,此功能非常有用。 错误屏幕截图可以保存在其相应的项目路径中以供参考。
文件记录
与“ScreenShot on Fail”类似,此插件也可以从官方网站的“Selenium IDE 插件”部分的下载页面下载。
单击下载链接后,您将重定向到“加载项”页面。 点击“添加到 Firefox”按钮。 安装并重新启动浏览器。
打开 Selenium IDE 窗口后,在“日志/参考”窗格中,将看到一个新的“文件记录”选项卡,该选项卡默认情况下处于关闭状态。 单击下拉列表,选择“日志文件路径”和“日志级别”。
这些设置可以通过两种方式执行:
- 点击“文件记录”标签下拉菜单,然后点击“显示选项...”
- 或菜单栏中的“选项”->“选项...”,然后单击“文件记录”标签。
点击“浏览”按钮,浏览至所需路径并提供文件名。 如果使用相同的日志文件路径和名称执行多个测试用例,则将附加日志。 如果出现提示覆盖日志的提示,只需单击“是”。 这是插件中的问题,但无需担心。 另外,如果您希望停止记录,则将文件名保留为空白。
从“文件记录级别”下拉菜单中选择级别-调试,信息,警告或错误。 此日志记录级别与从“日志/参考”窗格中选择显示的日志级别无关。
根据需要单击相应的复选框,以将时间戳记和消息一起记录,将它们保存为 Unicode 并为计划的作业使用单独的日志文件。
点击“确定”按钮以保存所做的更改。
让我们执行相同的示例,并检查所选位置的日志文件。 如下面的屏幕快照所示,我为显示日志选择了“错误”级别,为文件记录选择了“信息”级别。
信息级别日志将以所选路径写入文件。
希望这两个插件能为您提供总体上如何根据需要和便利来下载,安装和使用 Selenium IDE 插件的总体情况。 这些特殊功能在您遇到困难时确实可以派上用场! 因此,如果您有任何疑问,请务必在评论部分中发掘并留言。
在另一篇文章中再见。 祝你有美好的一天!
7Q Selenium IDE – 暂停和反射
原文: https://javabeginnerstutorial.com/selenium/7r-ide-pause-reflect/
嗨呀测试人员! 欢迎来到我们有关 Selenium IDE 的最终文章。 摇一摇,列出我们到目前为止所学的功能!
我想借此机会向您介绍另一个出色的 BrainBell。
BrainBell – 想想! 不要仿佛错过了航班。 暂停,思考和思考。 您越用脑去思考,您就会越记得。
戴上 FeatureBelt。 也就是说,到目前为止,您已经在 Selenium IDE 中学习了有关功能的所有新技能。 您(是的,您没听错!您)现在将所有内容放在一起,并针对您自己的不同场景创建新的测试用例。 如果您在执行过程中遇到任何问题,请在评论部分给我大喊。
我们涵盖了很多领域; 您几乎已经到达魔法草原的边界。
下一个是什么? 现在该到南方旅行了!
随时在评论部分中发表您的想法和澄清。 祝您有美好的一天。
8 给新手的惊喜
原文: https://javabeginnerstutorial.com/selenium/8-surprise-freebie/
朋友! 希望您通过本博客系列学习 Selenium IDE 感到很愉快。 在进入我们的下一个工具 Selenium WebDriver 之前,我想给您一个免费赠品。
既然您已经在某种程度上目睹了 Selenium 工具的强大功能,则可能需要将此工具套件引入您的项目中以实现自动化。 要克服这里最困难的障碍就是说服您的客户。 大多数客户并没有真正了解您要介绍的工具的技术性,但会涉及成本和回报。
因此,如果您可以集中精力并强调 Selenium 工具套件在这些方面的积极方面,那么您一定会品尝到成功的。 而这正是我要令您惊讶的地方。
每个人都喜欢听与故事相关的故事。 同意吗?因此,让我们以故事的形式解释 Selenium 的超凡魅力,而不是使用传统的无聊的 Power Point PPT,让我们激动不已。 这将使他们一直充满吸引力和说服力。 我强烈建议您使用 prezi 创建业务演示,因为它超出了上述所有期望。 请注意,这是我根据我的专业经验提出的个人建议。
我已经使用 prezi 做了一个简单的演示,并以相同的场景作为示例。 这是 vimeo 链接,显示了我在幻灯片中浏览的视频。
您一定在想我是怎么想到这个好主意的。 正如小兵曾经说过的,我当然会自言自语! 有时我需要专家的建议。 (好吧,明白了,别那样看着我!)继续…
PPT 的简要说明:
该视频首先向您展示演示的整体情况。 我们的大脑更喜欢视觉而不是文字。 因此,始终要畅所欲言,并在幻灯片上显示相关图像,而不是数百个单词,这始终是一个好习惯。
-
幻灯片 1: 'WOW'。 用爆炸打开 PPT! 首先要引起您对样式的“迷恋”。 可能是现实生活中的事件,假想的场景,发人深省的报价或问题
-
幻灯片 2: 面临的挑战。 简单介绍一下我们在没有自动化的情况下面临的挑战/问题。 允许客户考虑。 这也应该作为介绍的介绍。 例如,显示简单场景的图形。 对于每个发行版,测试用例数量都会增加(回归套件+发行用例),从而增加了总体开发成本。 这最终减少了产品的上市时间,这可能会对总收入/销售额造成重大打击。
-
幻灯片 3: 现在是时候在他们的脑海中植入创意了。 引入Selenium!
-
幻灯片 4: 高亮显示 2 至 3 个关键点。 “FREE”是当今的流行语,因为客户担心金钱和数字。 现在,您甚至可以在他们的脸上看到微笑。
-
幻灯片 5: 简要介绍 Selenium Suite 的概述,涵盖每分钟工具。 切记不要听起来太技术性,并尽量避免使用专业术语。
-
幻灯片 6: 讨论优势。 这些内容应涵盖幻灯片 2 中突出显示的挑战。请保持内容的吸引力。
-
幻灯片 7: 谢谢。 现在是时候总结“要点”并用结束“钩子”结束演示的时候了。 您可以使用讲义,轶事,演示,音频/视频效果,助记符和标语最好将其链接到开幕式!
-
幻灯片 8: 问题。 花一些时间来澄清客户之间可能出现的疑问或疑虑,并确保他们带着满意的表情离开房间。
提示:
- 带着微笑出现。
- 合适的衣服 – 必须穿着舒适,整洁的衣服。
- 肢体语言 – 避免将手放在口袋中或握在固定装置上,在讲话时以轻微的身体移动或手势来表达您的感觉。
- 动听的声音 – 使用适当的调制,在必要时重复,请尽量减少使用 uh,um 等填充物,实际上,您知道等等。
- 传送信息的速度也起着重要作用。
- 眼神交流 – 使用眼动横扫听众,营造出个人化注意力的印象。
- 最重要的是:说话时,请勿在前面挡住投影的视图。
如果您遵循我在这篇文章中提到的大部分内容,则一定会留下您的印记。
所有这些看起来都不酷吗? 如果是,那您还等什么呢? 成为哈利·波特! 添加您的魔力和创造力,进行自定义,使其更具表现力和令人印象深刻!
像我创建的 PPT 一样,想要复制它并根据您的要求进行编辑吗? 这是我的 prezi 链接。 我将其设置为“公开且可重复使用”。 复制副本之前,请先点击“赞”按钮,希望您喜欢这个免费赠品。🙂
在评论部分让我知道您的想法和经验。 即将在我们即将推出的,期待已久的 WebDriver 系列中与您见面!
9A WebDriver – 架构及其工作方式
原文: https://javabeginnerstutorial.com/selenium/9a-webdriver-architecture/
学习 WebDriver 的方法很有趣,而且常常使人弯腰,所以请做好准备……在南方惊喜的土地上跋涉,我们将遇到一些奇妙,荒唐和古怪的事物。
您将发现 Selenium WebDriver 为什么将使您的生活变得如此好-从质量保证的角度来看,以及为什么我们将其称为“统治冠军”! 为了掌握该工具并构建测试自动化框架,对我们正在处理的内容进行更深入的了解非常重要。 那么,我们还等什么呢? 现在让我们开始打好基础!
哪种方式比图形表示更容易理解? 这就是我们的大脑喜欢记住事物的方式,这也是我们将要前进的方式。
从这张图片中我们可以看出,这个架构有 3 层,
- 绑定
- WebDriver API 和
- 驱动
让我们一次讨论这一步骤。 (大字警报!)绑定 – 根据维基百科,这意味着将一件事映射到另一件事。 只要记住这两个词,粘合代码即可。
有很多高级编程语言,您可能想使用 C# ,但其他人可能更喜欢 Python。 每个人都希望利用通用的 WebDriver API 来以自己舒适的语言自动化浏览器。 这是语言级别绑定进入图片的地方。 这些就像用相应语言编写的粘合代码/包装库与 WebDriver API 进行通信。 除了 Java,C# ,Ruby,Python 绑定外,还有更多。 也很容易添加新的。
接下来到驱动程序上。 WebDriver API 使我们可以拥有一些驱动程序,这些驱动程序知道如何驱动与之对应的特定浏览器。 我们有 Chrome 驱动程序,IE 驱动程序,Microsoft Edge 驱动程序,Firefox 驱动程序(内置)等。还有移动专用驱动程序,例如 iOS 驱动程序,Selendriod(适用于 Android 的 Selenium)等。例如,Chrome 驱动程序知道如何驱动 Chrome 浏览器执行低级活动,例如操纵 Web 元素,导航到网页,从中获取用户输入等等。
我们在代码中提到了所需的驱动程序。 该驱动程序服务器作为可执行文件提供。 当我们运行测试时,驱动程序服务器会监听本地计算机上的端口。 它将解释从 WebDriver API 接收的命令,在实际的浏览器上执行,并将结果通过 API 返回给我们的代码。
放大并将其放在一起:
在本系列中,我们将使用 Java 编写测试程序。 可以将其视为用于使浏览器自动化的脚本语言。 相应的 Java 绑定代码向 WebDriver API 发出命令。 与浏览器通信的 WebDriver 的所有实现都使用通用的有线协议。 有线协议基本上是基于 HTTP 的 RESTful Web 服务,以“命令”和“响应”的请求/响应对实现。 因此,我们可以将 HTTP 请求(例如 GET,POST,PUT 等)发送到驱动程序服务器。 该服务器是运行远程 WebDriver 的计算机。 例如,Chrome 驱动服务器是指直接实现有线协议的 Chrome 浏览器。 运行 Java 测试时,此服务器将监听并等待这些命令。 它会相应地解释它们,执行低级浏览器活动,然后以 HTTP 响应消息进行响应。
缩小:
语言级别绑定(发出命令)-> WebDriver 通用有线协议(基于 HTTP 的基于 REST 的 Web 服务)-> 驱动程序服务器(解释 HTTP 请求并以 HTTP 响应来响应消息 )
如果您没有获得完整的图像,请不要惊慌。 休息片刻,因为有很多帖子可以跟进,随着我们的前进,您一定会获得清晰的了解。
注意,在 Eclipse 中设置 WebDriver 时,您将看到实际的 Java 语言绑定。 很快我们将下载驱动程序服务器可执行文件,将它们包含在我们的代码中,并使浏览器操作也自动执行。 因此,加油并注意即将发布的帖子!
很快见,祝您有美好的一天。
9B WebDriver – 在 Eclipse 中设置
原文: https://javabeginnerstutorial.com/selenium/9b-webdriver-eclipse-setup/
欢迎来到我们的第一篇关于 WebDriver 系列的探索帖子。 建议您密切注意并共同努力,以使设置过程更加简单!
先决条件:
- 拥有有效的互联网连接。
- 在系统上下载并安装 Java SE Development Kit(JDK)。 (http://www.oracle.com/technetwork/java/javase/downloads/index.html)
- 下载 Eclipse IDE。 不需要安装。 只需将所有内容提取到一个文件夹中,然后双击可执行文件即可。 (http://www.eclipse.org/downloads/)
是时候按照 3 个步骤来设置环境了,
步骤 1:
转到 http://www.seleniumhq.org/download/ 。 在“Selenium Client & WebDriver 语言绑定”部分下,单击“Java”语言的下载链接。 (请注意,您看到的版本号可能与此处快照中的版本号不同,因为创建此文章时,最新的版本是 3.0.0-beta2。)
步骤 2:
将下载的文件解压缩到系统上所需的位置。 切换到 Eclipse 并选择所需的工作空间目录。
创建一个新的 Java 项目,如下所示:“文件 -> 新建 -> Java 项目”。 我已将项目命名为“WebDriver 测试”。
步骤 3:
右键单击创建的项目。 选择“构建路径”->“配置构建路径...”
现在有两种方式可以将外部 JAR 添加到我们的项目中。
方法 1:
确保已选择“库”标签。 单击“添加外部 JAR ...”按钮。 “JAR 选择”窗口将打开。 浏览至 seleniumhq.org 站点(Selenium 客户端和 WebDriver 语言绑定)中的文件的下载和提取位置。
确保选择所有类型为“可执行 Jar 文件”的文件。 目前,我在“selenium-java-3.0.0-beta2
”文件夹中有一个文件,在selenium-java-3.0.0-beta2\lib
文件夹中有 16 个文件。
点击“打开”,将它们添加到“库”标签中。
方法 2:
当您出于各种目的添加大量外部 JAR 文件(例如 WebDriver,JUnit,Ant 等)时,这确实非常方便。如果遵循方法 1,则区分为每种目的添加的 JAR 可能会变得非常困难。 在方法 2 中,我们将相应地创建一个单独的文件夹和名称,以便于识别和记录文档。
确保选择了“库”标签。 单击,添加库…->用户库->接下来。
现在,单击“用户库...”,将打开一个新窗口。 单击,新建...->指定文件夹的名称->确定。
单击“添加外部 JAR ...”按钮。 “JAR 选择”窗口将打开。 浏览至 seleniumhq.org 站点(Selenium 客户端和 WebDriver 语言绑定)中的文件的下载和提取位置。
确保选择所有类型为“可执行 Jar 文件”的文件。 目前,我在“selenium-java-3.0.0-beta2
”文件夹中有一个文件,在selenium-java-3.0.0-beta2\lib
文件夹中有 16 个文件。
点击“打开”,将它们添加到“用户库”窗口下创建的文件夹中。 点击“确定”,现在您将在“添加库”窗口中看到带有复选框的用户库。 确保选中此复选框,然后单击“完成”。 创建的用户库将在项目属性窗口的“库”选项卡中提供所有添加的外部 JAR。
单击“确定”按钮后,新添加的 JAR 将在“包资源管理器”窗格中的项目下显示。
注意:(这只是告诉您做一个注释)
此设置可与 Selenium 2 版本完美配合(我已经使用了很长时间了,一切都很好)。 但是,如果您使用的是 Selenium 3 beta 版本(如上面的屏幕快照所示),则需要执行一些其他步骤。
- 从“https://github.com/mozilla/geckodriver/releases”下载“
geckodriver.exe
”。
- 编写测试脚本时,请确保包括以下行(还要确保系统中的 Firefox 浏览器版本为 48+)
System.setProperty("webdriver.gecko.driver", "<path_to_geckodriver.exe>");
你猜怎么了?! 您已经准备就绪,很快我们将通过启动 Firefox 浏览器来创建和运行我们的第一个测试脚本。
在另一篇文章中再见。 祝你今天愉快!
9C WebDriver – 启动 Firefox 的第一个测试脚本
原文: https://javabeginnerstutorial.com/selenium/9c-webdriver-first-test-script-firefox/
嗨呀大家! 是时候喝杯 Java 了,我是说咖啡😉
事不宜迟,让我们开始使用 WebDriver 中的第一个测试脚本。 在我们根据上一篇文章(添加了 WebDriver JAR 文件)创建的同一项目下,我创建了一个名为com.blog.tests
的新包。
接下来,右键单击“包 -> 新建 -> 类”。
我将类命名为“HelloWorldFirefox.java
”。 创建所有这些文件后,“包浏览器”窗格将如下所示,
我们将在第一个测试脚本中考虑的场景是:
- 打开 Firefox 浏览器。
- 导航到“https://www.google.com/”
- 将页面标题声明为“Google”。
- 根据声明结果在控制台上显示一条消息。
- 关闭浏览器。
注意: 我们有一整篇文章专门用于声明和验证。 由于第一个脚本的主要座右铭是查看 WebDriver 的工作原理,因此让我们使用简单的 if-else 语句来比较实际和预期的页面标题。
代码如下,
package com.blog.tests;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
public class HelloWorldFirefox {
public static void main(String[] args) {
//Selenium version 3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver", "E:\\Softwares\\
Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe");
//Create a new instance for the class FirefoxDriver
//that implements WebDriver interface
WebDriver driver = new FirefoxDriver();
//Assign the URL to be invoked to a String variable
String baseUrl = "https://www.google.com";
String pageTitle = "";
String expectedTitle = "Google";
//Open baseUrl in Firefox browser window
driver.get(baseUrl);
//Get the page title and assign to a String variable
pageTitle = driver.getTitle();
//Check if obtained page title matches with the expected title
//and print the console output accordingly
if(pageTitle.equals(expectedTitle)){
System.out.println("Hello World! Result is as expected.");
}else{
System.out.println("Hello World! Assertion failed!");
}
//Close the Firefox browser
driver.quit();
}
}
我知道外表令人生畏! 不过不用担心,我们将阐明所有这些代码,并找出其含义。 让我们逐行走下去,不遗余力。
代码实践:
1.系统属性设置。
System.setProperty("webdriver.gecko.driver", "E:\\Softwares\\Selenium\\
geckodriver-v0.10.0-win64\\geckodriver.exe");
Selenium 3 Beta 版本不像 Selenium 2 版本那样支持直接启动 Firefox。 因此,必须通过“webdriver.gecko.driver
”系统属性设置驱动程序可执行文件“geckodriver.exe
”的路径。 指定在系统中相应保存可执行文件的路径。
2.接下来,为了实例化 Firefox 浏览器,我们将必须导入两个包。
键入“WebDriver driver = new FirefoxDriver();
”后,在“WebDriver
”和“FirefoxDriver()
”下方会出现一条波浪线。 悬停时,蚀将建议所有可能的快速修复。 单击建议导入相应包的第一个修复程序。
- 以下包指定了 WebDriver 接口,该接口用于根据需要实例化新的浏览器窗口。
import org.openqa.selenium.WebDriver
注意:我们不要说,WebDriver* driver = new WebDriver();
,因为WebDriver
是一个接口,并且只包含已定义但未实现的空方法。 因此无法实例化。
FirefoxDriver
是特定于 Firefox 浏览器的类。 它具有根据接口WebDriver
定义和实现的方法。 因此,这可以实例化。
import org.openqa.selenium.firefox.FirefoxDriver
因此,以下声明有点像我们的甘道夫。 (不要告诉我您还没有阅读或观看“指环王”系列!!)SeleniumVille 历来最强大,最受尊敬的台词。 因为它为FirefoxDriver
类创建了一个对象。 现在,根据WebDriver
接口的约定,通过调用FirefoxDriver
类中已经实现的方法,我们现在可以使用此对象(在这种情况下为“驱动程序”)自动执行 Firefox 浏览器上的各种操作。
WebDriver driver = new FirefoxDriver();
将启动的 Firefox 浏览器将具有默认配置文件。 它不会随 Firefox 实例一起加载任何扩展程序和插件,并且会在安全模式下运行。 如果您想了解有关 Firefox 配置文件的更多信息,请访问“https://support.mozilla.org/en-US/kb/profiles-where-firefox-stores-user-data”。
同样,对于其他浏览器(例如 Chrome,IE,Safari 等),我们按照WebDriver
接口中指定的相同协定(即,实现接口中定义的方法)编写特定的类。 在下一篇文章中,我们将通过特定示例了解如何使用其他浏览器。
3.现在,声明String
类型的必要变量。
String baseUrl = "https://www.google.com";
String pageTitle = "";
String expectedTitle = "Google";
我猜很简单,自我解释。 我们只是声明String
类型的变量并为其分配值。 baseUrl
是我们希望在 Firefox 浏览器窗口中为测试场景调用的 URL。 目前,pageTitle
保持为空,因为这将是我们将从浏览器中获取的实际值。 ExpectedTitle
是将与实际值进行比较的期望值。
4.导航到 Google 页面。
driver.get(baseUrl);
这是我们要做的第一件事。 “get
”方法用于导航到指定的 URL。 在这种情况下为 https://www.google.com。
注意: driver.navigate().to(baseUrl)
也达到相同的结果。 试试看!
5.获取页面标题。
pageTitle = driver.getTitle();
页面标题的实际值可以使用getTitle()
方法获得。 然后将标题保存到变量pageTitle
中,以供进一步声明。
6.比较页面标题并在控制台中显示结果。
if(pageTitle.equals(expectedTitle)){
System.out.println("Hello World! Result is as expected.");
}else{
System.out.println("Hello World! Assertion failed!");
}
检查存储在pageTitle
中的实际值是否等于辅助值的期望值。 System.out.println()
打印指定的参数和换行符。 这里只是纯 Java!
7.关闭浏览器窗口。
driver.quit();
quit()
方法关闭所有浏览器窗口,并完全结束 WebDriver 会话。 这样可以避免在未正确清除任何相关文件的情况下可能发生的内存泄漏。
注意: driver.close()
也可以使用。 不同之处在于,它将关闭当前关注的浏览器窗口。
让我们在这里休息一下,好好沉浸其中! 我们将在下一篇文章中执行此测试。
再见。 祝你有美好的一天!
9D WebDriver – 执行测试
原文: https://javabeginnerstutorial.com/selenium/9d-webdriver-executing-test/
如果您无法执行并查看输出,那么逐行代码遍历有什么好处? 因此,不必再大惊小怪了,让我们运行代码!!
Eclipse 使我们能够通过 3 种方式来完成相同的任务,
- 方法 1:单击 Eclipse 中的“运行”快捷方式图标。
- 方法 2:右键单击“类文件,运行方式 -> Java 应用”。
- 方法 3:使用组合键
CTRL + F11
。
散布一些视觉效果:
执行我们的测试将打开一个新的 Firefox 浏览器窗口,并按照代码执行测试步骤。 如上面的屏幕快照所示,输出显示在控制台中。
WebDriver 系列的第一个 BrainBell 的时间:回忆! 您知道吗,在记住事物的过程中,我们甚至还添加了新的想法? 回忆是对大脑中散布的元素的动态重建-称之为创造性的重新想象。 您记得的越多,就越记得!
因此,回想一下我们是如何为刚刚执行的拳头场景编写代码的,而您都可以尝试自己的一些简单场景。 如果您在旅途中遇到任何颠簸,请在评论部分给我大喊。
在另一个帖子中再见! 祝你今天愉快!
9E WebDriver – 用于启动其他浏览器的代码示例
原文: https://javabeginnerstutorial.com/selenium/9e-webdriver-code-launching-browsers/
如果您只能使用 Selenium(即 Firefox)在一个浏览器中自动化测试,但您的 Web 应用也支持 Chrome 和 IE,该怎么办? 这意味着所有三个浏览器都需要测试! 如果是这种情况,那么 Selenium 就像把头埋在沙子里一样有用。
哦,不要惊慌! 我们的统治冠军 WebDriver 在这里为我们提供帮助。 这是我们今天的主题。 让我们潜入吧!
第 1 步:首先! 让我们下载所需的可执行文件。 转到“www.seleniumhq.org/download
”。
InternetExplorerDriver
在“Internet Explorer 驱动程序服务器”部分下可用。ChromeDriver
,OperaDriver
和SafariDriver
位于“第三方浏览器驱动程序”部分下。
在本文中,我们将仅讨论InternetExplorerDriver
和ChromeDriver
。 设置其他浏览器也应遵循类似的步骤。
步骤 2:下载可执行文件后,解压缩并将它们保存在所需的路径中。 我在 Eclipse IDE 的项目中创建了一个名为“浏览器驱动程序”的文件夹(右键单击“包 -> 新建 -> 文件夹”),并复制了下载的文件以便于访问。
步骤 3:现在,让我们通过右键单击“包-> 新建 -> 类”并将它们命名为“HelloWorld_IE.java
”,创建两个新类。 和“HelloWorld_Chrome.java
”。
包浏览器窗格现在如下所示,
让我们考虑一个非常简单的情况,因为我们的主要目标是查看是否可以启动 IE 和 Chrome 浏览器,
- 根据测试用例打开 IE / Chrome 浏览器。
- 导航到“https://www.google.com/”
- 在控制台中显示“
Hello World
消息。 - 关闭浏览器。
首先让我们看看“HelloWorld_IE.java
”类的代码
package com.blog.tests;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
public class HelloWorld_IE {
public static void main(String[] args){
//System property set up
System.setProperty("webdriver.ie.driver", "browser- drivers\\IEDriverServer.exe");
//Create a new instance for the class InternetExplorerDriver
//that implements WebDriver interface
WebDriver driver = new InternetExplorerDriver();
//Assign the URL to be invoked to a String variable
String baseUrl = "https://www.google.com";
//Open baseUrl in IE browser window
driver.get(baseUrl);
//Print the message to console
System.out.println("Hello World!");
//Close the IE browser
driver.quit();
}
}
如果注意到的话,此代码与我们在上一篇文章“第一个测试脚本 – 通过启动 Firefox”中看到的代码非常相似。
这些其他浏览器需要设置系统属性,因为不支持直接启动该属性。
System.setProperty("webdriver.ie.driver", "browser-drivers\\IEDriverServer.exe");
要求驱动程序可执行文件“IEDriverServer.exe
”的路径必须由“webdriver.ie.driver
”系统属性设置。 指定在系统中相应保存可执行文件的路径。
为了实例化 IE 浏览器,需要进行的另一项主要更改是,
WebDriver driver = new InternetExplorerDriver();
键入此语句后,“WebDriver
”和“InternetExplorerDriver()
”下方会出现一条波浪线。 悬停时,Eclipse将建议所有可能的快速修复。 单击建议导入相应包的第一个修复程序。
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
根据提供的注释,以下代码看起来很不言自明。 IE 浏览器启动后,它便导航到 google 页面,将“Hello World
”消息打印到控制台并关闭浏览器窗口。
执行此测试后的控制台窗口如下所示,
HelloWorld_Chrome.java
类的代码,
package com.blog.tests;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
public class HelloWorld_Chrome {
public static void main(String[] args) {
// System property set up
System.setProperty("webdriver.chrome.driver", "browser-drivers\\chromedriver.exe");
// Create a new instance for the class ChromeDriver
// that implements WebDriver interface
WebDriver driver = new ChromeDriver();
// Assign the URL to be invoked to a String variable
String baseUrl = "https://www.google.com";
// Open baseUrl in IE browser window
driver.get(baseUrl);
// Print the message to console
System.out.println("Hello World!");
// Close the IE browser
driver.quit();
}
}
如果将此代码与“HelloWorld_IE.java
”进行比较,则只有两个值得注意的变化。
- 系统属性设置
ChromeDriver
类实例化
因此,我们可以得出结论,通过指定相应的系统属性并提供准确的驱动程序类实例化,可以借助 WebDriver 轻松启动和自动化相应的浏览器。
在执行 Chrome 浏览器的代码后,控制台窗口如下所示,
我猜想,WebDriver 终于慢慢地在阳光下崭露头角了! 如果您在启动各种浏览器时遇到任何问题,欢迎大家在评论部分中表达自己的意见。
在那之前,在另一篇文章中再见! 祝你今天愉快!
9F WebDriver – JUnit 环境设置
原文: https://javabeginnerstutorial.com/selenium/9f-webdriver-junit-environment-setup/
大家好! 带出隐藏在您体内的测试忍者,因为今天我们将为 JUnit 框架设置环境。
跳入:
步骤 1:
转到 http://junit.org/junit4/ ,然后点击“下载并安装”链接。
步骤 2:
以下几点将使该过程更加容易理解,
- 您将被重定向到 GitHub。 在“普通的 JAR”下,可以使用 junit 和 hamcrest-core JAR。
- 点击“
junit.jar
”会将您重定向到包含下载链接的页面。 点击最新版本的“jar”链接(撰写本文时为 v4.12)。 它将自动下载到您的系统。 - 接下来,点击“
hamcrest-core.jar
”。 与上述类似,单击最新的“jar”下载链接(截至本帖子发布之日起为 v1.3)。 - 这样,两个必需的 jar 都立即下载到您的系统中。
下图是突出显示步骤的图像,
步骤 3:
下一个工作是在 Eclipse IDE 中将这两个 jar 添加到我们的项目中。 该过程与我们在“9b 设置 WebDriver(在 Eclipse 中)”中遵循的过程非常相似。请参考该指南以获取分步说明以及屏幕截图。
- 打开 Eclipse IDE 并导航到相应的工作区。
- 右键单击“设置 Selenium 的项目 -> 构建路径 -> 配置构建路径…”
- 确保选择了“库”选项卡。 点击“添加库...”
- 选择“用户库”,接下来单击“用户库...”
- 单击“新建...”,并为库“JUnit4”命名,然后单击“确定”。
- 在“用户库”弹出窗口中,确保选择了新创建的 Junit4 库。 点击“添加外部 JAR ...”
- 导航到本地系统中下载的与 junit 相关的 jar 的保存路径。 选择 junit 和 hamcrest-core JAR,然后单击“打开”
相应地单击 OK 和“完成”按钮,新创建的 JUnit4 库将添加到项目构建路径,如下所示,
这样,JUnit 4 的环境就设置好了! 在我们的下一篇文章中,让我们在 JUnit 框架中编写 WebDriver 测试并执行它们以查看结果。
在另一个帖子中再见! 祝你今天愉快!
9G WebDriver – 在 JUnit4 中运行 WebDriver 测试
原文: https://javabeginnerstutorial.com/selenium/9g-webdriver-running-tests-junit4/
欢迎来到我们的 WebDriver 系列中的另一篇有趣的文章! 今天它会很有趣(可能比吃自己喜欢的巧克力要有趣得多)。
您准备好将帽子戴上戒指了吗? 如果是,那就开始吧!
展望未来,我们所有的测试都将使用 JUnit 框架。 因此,是时候通过右键单击“项目->新建->包”(已设置 Selenium 和 junit)创建新包了。 我已将包命名为“com.blog.junitTests
”。
我们的下一步是创建一个类。 右键单击“新创建的包 -> 新建 -> 类”。 给它起一个名字“FirstJunitTest.java
”,然后单击“完成”。 您的 IDE 现在看起来应该与此类似,
现在我们已经准备好教程的文件了,让我们采用与“第一个测试脚本 – 通过启动 Firefox”帖子中相同的方案。
- 打开 Firefox 浏览器。
- 导航到“https://www.google.com/”
- 将页面标题声明为“Google”。
- 根据声明结果在控制台上显示一条消息。
- 关闭浏览器。
选择相同方案的原因是代码说明保持不变。 无论有没有 JUnit 框架,这都将帮助您清楚地看到代码中的更改。
注意:这不是 JUnit 教程。 但是,将在需要时提供基本的解释,以使您有一个全面的了解。 有关详细和完整的信息,请参考 http://junit.org/junit4/。
代码如下,
package com.blog.junitTests;
import org.junit.*;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
public class FirstJunitTest {
//Declaring variables
private WebDriver driver;
private String baseUrl;
private String pageTitle;
private String expectedTitle;
@Before
public void setUp() {
//Selenium version 3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver",
"E:\\Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe");
// Create a new instance for the class FirefoxDriver
// that implements WebDriver interface
driver = new FirefoxDriver();
// Assign the URL to be invoked to a String variable
baseUrl = "https://www.google.com";
pageTitle = "";
expectedTitle = "Google";
}
@Test
public void testPageTitle(){
// Open baseUrl in Firefox browser window
driver.get(baseUrl);
// Get the page title and assign to a String variable
pageTitle = driver.getTitle();
// Check if obtained page title matches with the expected title
// and print the console output accordingly
if (pageTitle.equals(expectedTitle)) {
System.out.println("Hello World! Result is as expected.");
} else {
System.out.println("Hello World! Assertion failed!");
}
}
@After
public void tearDown() throws Exception{
// Close the Firefox browser
driver.quit();
}
}
如果将此代码与实现 JUnit 之前的代码进行比较,将会有明显的变化,
- 注解:
@Before
,@Test
和@After
- 方法:
setUp()
,testPageTitle()
,tearDown()
- 类开头的私有变量声明
- 在新创建的方法下,将相同的代码分为几部分
这会使视力模糊吗? 不要担心! 让我们立即清除雾气!!
大字警报! 注意:这些将特定的含义传达给 JUnit。 他们告诉 JUnit,应该附加到它的public void
方法,
@Test
– 被当作测试方法运行。
@Before
– 在指定的每种测试方法之前运行。 这用于各种目的。 读取或分配数据,初始化或如果多个测试需要先创建相似的对象才能执行,则最好在@Before
注解的方法下进行指定。
@After
– 在执行每种测试方法之后运行。 通常,会指定与环境清理相关的命令,例如,关闭打开的资源/数据库连接,删除临时数据,释放内存等。即使@Before
或@Test
方法引发异常,也可以保证运行带after
注解的方法。
键入这些注解,在它们下方会出现一条弯曲的线。 悬停时,Eclipse将建议所有可能的快速修复。 单击建议导入org.junit
包的第一个修复程序。
在这三个注解下的方法是公开的,返回类型为void
。 在junit.framework.TestCase
类下,我们有setUp()
和tearDown()
方法。 最佳实践是覆盖这些方法,以分别编写初始化代码和清除代码。 这不仅可以防止内存泄漏,还可以使代码更易于阅读。 JUnit 首先调用setUp()
方法,然后调用test
方法,最后调用tearDown()
方法。 对于附加到@Test
的每个测试方法,都会发生这种情况。
要运行测试
右键单击“类 -> 运行方式 -> JUnit 测试”。
将打开一个新的 Firefox 浏览器窗口,并按照代码执行测试步骤。 结果将在 Eclipse IDE 的 JUnit 视图中打开,其中显示成功的绿色条和错误的红色条。
显示测试用例名称。 发生错误时,将显示栈跟踪。 快捷方式可用于重新运行测试,仅显示失败,查看上一个和下一个失败的测试等。下面是显示错误的示例屏幕截图。
我们到了这篇文章的结尾。 现在轮到您尝试一些实现 JUnit 框架的方案,并在评论部分留言,以防您遇到问题或得到澄清。
在另一个帖子中再见! 祝你今天愉快!
9H WebDriver – 隐式等待
原文: https://javabeginnerstutorial.com/selenium/9h-webdriver-implicit-waits/
它说:“时间具有向我们展示真正重要内容的绝妙方式”。 但是,等到一切变为现实,这一点同样重要。 如今,网页大多使用 javascript 和 ajax 技术开发。 结果,页面上的不同元素在不同的时间加载。 当我们使用 selenium WebDriver 自动化我们的手动测试并将其作为测试套件运行时,是时候提防您了,因为您可能会遇到 Selenium 的怪异行为。
您的测试用例可以成功运行,并且一次执行一行时可以按预期工作,但是作为套件/批处理运行时,它可能会失败。 挠头以了解其背后的原因? 让我们潜入更多!
当我们使用driver.get("url_we_wish_to_access")
,driver.navigate().to("url")
或单击超链接等时,这些调用将在该加载活动完成之前加载启动页面并返回。 因此,如果页面的加载线程尚未填充 Web 元素,而您尝试使用driver.findElement("locating_strategy")
找到它,则得到的只是异常即NoSuchElementException
,ElementNotVisibleException
等。
所有这些意味着我们必须找到一种告诉 Selenium 的方法,我们希望它等待一定的时间,或者直到 Web 元素变得可访问/可单击/显示为止,然后再引发可怕的异常。 在那里,不要惊慌。 我看到一些汗珠滴到你的额头上! 那么,如何使这种压力成为我们自己的大力水手菠菜的版本呢? 感谢隐式,显式和流畅的等待。
是时候去拜访每一个了。
隐式等待
了解我们使用findElement
或findElements
命令在 WebDriver 中定位 Web 元素非常重要(后面的文章将详细介绍该主题以及各种定位策略)。 因此,当尝试在页面上查找特定的 Web 元素时,如果该 Web 元素不是立即可用的,则隐式等待告诉 WebDriver 在指定的时间内轮询 DOM。 万一在指定的等待时间内找到该元素,测试将继续执行。 如果不是,则抛出NoSuchElementException
。
下面是带有隐式等待命令的代码片段,以使您更好地理解,
@Before
public void setUp() throws Exception{
// Selenium version 3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver", "E:\\Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe");
// Create a new instance for the class FirefoxDriver
// that implements WebDriver interface
driver = new FirefoxDriver();
// Implicit wait for 3 seconds
driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
// Assign the URL to be invoked to a String variable
baseUrl = "https://www.google.com";
pageTitle = "";
expectedTitle = "Google";
}
如突出显示的那样,使用隐式等待的语法是:
driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
隐式等待有两个参数。 在此示例中,我们的第一个参数为 3 ,这是它必须等待的时间,第二个参数为TimeUnit.SECONDS
。 这是时间测量。 可以指定为DAYS
,HOURS
,MINUTES
,SECONDS
,MILLISECONDS
,MICROSECONDS
,NANOSECONDS
等。
此外,在键入此语句时,“TimeUnit
”下方会出现一条弯曲的线。 悬停时,蚀将建议所有可能的快速修复。 单击建议导入java.util.concurrent.TimeUnit
包的第一个修复程序。
- 隐式等待只需要初始化一次。 设置后,它将适用于
WebDriver
对象实例的寿命。 换句话说,它将在浏览器打开的整个过程中就位。 因此,WebDriver 将等待该额外时间,然后对所有findElement
和findElements
抛出异常 - 默认时间设置为 0。
- 隐式等待仅影响
finddElement(s)
,它不影响其他WebDriver
方法,例如driver.getCurrentUrl()
等。 - 在大多数情况下,尤其是在调试时,仅“
NoSuchElementException
”并不是很有用。 我们可能希望使用更多信息来自定义此错误消息,例如捕获屏幕快照或添加更多详细信息等。可以通过在每次finddElement(s)
调用周围使用try-catch
块来实现此目的,但这不是我们使用隐式等待的目标,因为这是一个全球性的时间设置。 - 由于有大量的 JavaScript,因此可能会出现一些奇怪的情况。 即使该元素位于 DOM 中,也可能无法立即单击或显示或启用它。 结果我们可能会看到
ElementNotVisibleException
,WebDriverException
,StaleElementReferenceException
- 由于不同的元素可能在不同的时间加载,因此我们可能会说服将等待时间设置为较高的值(或等效于最慢的 Web 元素的加载时间),例如 20 秒左右。 我们对此的理由是,“无论如何,所有元素的加载速度都将比此时快,并且测试将按预期进行。” 但是您甚至猜不到性能 - 这种方法不是一个好主意吗? 我在这里给您举几个示例,以便更好地了解,
情况 1:如果希望找到一个元素,但无论结果如何,都可以继续进行测试。
情况 2:如果仅需要验证是否缺少某个元素,例如等待警报框出现并关闭它。 即使这样,WebDriver 也将不得不等到超时,即在这种情况下为 20 秒。 在庞大的测试套件中运行时,这将产生巨大的影响。
为了克服隐式等待的一些缺点,我们有明确的等待来救援! 但是考虑到我们的大脑一次只能占用那么多的空间,我将在下一篇文章中保留这个主题。
再见! 有一个值得期待的一天!
9I WebDriver – 显式等待
原文: https://javabeginnerstutorial.com/selenium/9i-webdriver-explicit-waits/
大家好! 这篇文章是先前文章“9h WebDriver – 隐式等待”的延续。
事不宜迟,让我们利用“显式等待”的力量。 显式等待需要更多的编码,但是与隐式等待相比,它具有巨大的优势。 在这里,我们可以等到某种情况发生后再进行测试。 如果在指定的超时值内未满足条件,则将引发异常。
语法如下,
WebDriverWait wait = new WebDriverWait(driver, 15);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("element_id")));
因此,WebDriverWait
类用于指定最大超时值,在这种情况下为 15 秒。 ExpectedCondition
类的方法涵盖了我们希望在测试中出现之前要等待的大多数条件。 这些条件与WebDriverWait
一起使用。
上面的代码一直等到元素变为可点击(即显示并启用)并返回结果。 默认情况下,WebDriverWait
每 500 毫秒调用ExpectedCondition
,直到成功返回。 在这种情况下,它会在抛出TimeoutException
之前尝试长达 15 秒。 成功的返回值是布尔值true
或非null
对象。
ExpectedCondition
预定义方法的一些示例是,
elementToBeClickable(By locator)
– 用于检查元素的期望是可见的并已启用,以便您可以单击它。elementToBeSelected(WebElement element)
- 检查是否选择了给定元素。presenceOfElementLocated(Bylocator)
– 检查页面的 DOM 上是否存在元素。urlToBe(java.lang.String url)
– 检查当前页面的 URL 是一个特定的 URL。visibilityOf(WebElement element)
- 用于检查存在于页面的 DOM 上的已知元素的是可见的。
您可以在此处,找到适用于 Java 的ExpectedConditions
包的所有可用方法及其用法的详细信息。
注意:根据 Selenium 的官方文档,我们警告您不要混合使用隐式和显式等待,因为这可能会导致不可预测的等待时间。
流利的等待
谁不喜欢自定义? 对于显式等待,如果这是您要搜索的内容,那么我建议您使用流利的等待。 我们可以通过流畅的等待来配置以下内容,
- 在抛出异常之前我们希望等待(超时)条件发生的最长时间,
- 检查指定条件的频率,以及
- 我们在等待条件发生时要忽略的异常类型
示例代码段如下所示,
public WebElement fluentWait(final By locator) {
Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
.withTimeout(20, TimeUnit.SECONDS)
.pollingEvery(2, TimeUnit.SECONDS)
.ignoring(NoSuchElementException.class)
.until(new Function<WebDriver, WebElement>() {
public WebElement apply(WebDriver driver) {
return driver.findElement(locator);
}
});
}
在这里,我们声明超时值为 20 秒的流畅等待,每 2 秒(频率)轮询 DOM 直到找到元素为止,并在此时间间隔内忽略NoSuchElementException
。 我们在上面的代码片段中创建了一个新函数,以在.until()
中标识此 Web 元素。
有几点值得您花时间
- 与隐式等待不同,此方法适用于
findElement(s)
以及您可能会想到的在自动化网页时进行枚举的任何其他条件。 - 可以自定义默认超时值和频率,并且仅将其应用于特定元素,这与隐式等待不同,隐式等待在启动后就适用于
WebDriver
对象实例的寿命。 - 我们可以实现自己的条件,也可以使用
ExpectedConditions
类中的预定义条件之一。 - 使用
FluentWait
,我们可以忽略某些异常并改为指定自定义错误消息。 这将我们的调试经验提升到一个全新的水平。 - 通过忽略特定的异常直到满足我们的条件,流利的等待比隐式等待更可靠。
同意的显式等待包括对代码的更多修饰,但是您认为值得这样做吗?
注意: 您可能会想,为什么要经历所有这些痛苦? 我不能只使用Thread.sleep()
吗? 嗯,不,不建议这样做,因为它会使整个测试脚本无条件进入休眠状态。 在大多数情况下,指定的睡眠时间不够长(导致异常),或者变得太长,导致即使元素加载速度更快,测试也要等待。
令我全神贯注:虽然显式等待涉及更多的编程部分,但由于它能够解决很多问题,使我们的生活变得更加轻松,我宁愿选择此方法,而不是隐式等待(基于测试自动化的需求和复杂性涉及眨眼)。
您的视线是否模糊? 不要惊慌,因为所有水域都会落在后面的职位上。 我们将在屏幕截图中看到更实际的等待示例。 一旦涵盖了定位器的类型和策略,就可以更好地理解和掌握该主题。 因此,请继续关注此空间!
在另一个帖子中再见! 祝你今天愉快!
9J WebDriver – 定位元素:第 1 部分(按 ID,名称,标签名称)
原文: https://javabeginnerstutorial.com/selenium/9j-webdriver-locating-elements-1/
朋友! 如果您进入了 Selenium 世界,那么您一定已经遇到了 Web 元素! 文本框,按钮,链接,复选框,单选按钮,下拉菜单,警报等,所有内容均视为网络元素。 使用定位器类型和策略,我们可以轻松识别页面上任何所需的元素。 这是与页面进行交互并在代码中进一步引用这些元素的最关键步骤。 是的,它与我们在 Selenium IDE 中看到的非常相似,除了语法和概念的应用方式。
使用 Selenium webDriver 定位 Web 元素的一般语法是:
driver.findElement(By.LocatorStrategyType ("Locator_Value"));
说明:
driver
– 实现 WebDriver 接口的相应驱动程序类的实例变量findElement()
– 查找并返回具有指定位置策略的第一个 Web 元素findElements()
– 查找并返回与给定位置策略匹配的 Web 元素列表By
– 始终使用按类别定位目标LocatorStrategyType
– ID,名称,标记名,类名,cssSelector
,linkText
,partialLinkText
,xpath 是可用于在页面上定位元素的某些定位器类型Locator_Value
– 可以标识元素的值/ HTML 标记
可用的定位器类型包括:
- ID
- 名称
- 标签名
- 类名称
- 链接文本
partialLinkText
cssSelector
- XPath
可以随时通过编程方式掌握页面上所需的元素,但要实现此目的,我们的创作方可能不得不偷看一下。 原因是,我们并不总是可以控制页面上的 HTML。 这就是为什么我们有这么多的定位器类型,而且绝对没有理由惊慌!
让我们继续,并通过代码示例详细了解每种定位器类型。
通过 ID 定位
ID 是在页面上定位元素的最有用和首选的方式,因为它是唯一的。 但是,当心! 一些开发人员或者自动生成这些 ID,或者完全忘记添加一个。 那就是我们去寻找其他定位器类型的时候。
语法:driver.findElement(By.id("element_id"));
说明:找到具有匹配 ID 属性的第一个元素。
示例:打开 Google 帐户创建页面。 让我们通过 ID 识别名字文本框。
- Google Chrome – 右键单击要确定其 ID 的网络元素,然后选择“检查”。 或通过单击
F12
打开开发人员工具,单击左上角的检查元素图标,然后在页面上单击所需的 Web 元素。 两种方法都将突出显示与所选元素相对应的 HTML 代码。 - Mozilla Firefox – 右键单击 Web 元素,然后选择“使用 Firebug 检查元素”(以防您错过了 Firebug 安装和使用 上的帖子)。 或者,如果您确实不想使用 Firebug,则右键单击该元素并选择“检查元素”。 在两种情况下,都将打开并突出显示相应的 HTML 代码。
如上面的屏幕截图所示,“名字”文本框的 ID 为“名字”。 因此,此处使用的代码是
driver.findElement(By.id("FirstName"));
按名称定位
name
属性也可用于在页面上定位元素。
语法:driver.findElement(By.name("element_name"));
说明:找到具有匹配名称属性的第一个元素。
示例:让我们按名称标识姓氏文本框。
右键单击姓氏文本框,然后单击检查元素以获取相应的 HTML 代码。 在我们的案例中,name
属性的值为LastName
。 因此,按名称定位此网络元素的代码将是,
driver.findElement(By.name("LastName"));
通过标签名定位
在这里,我们使用标记的实际名称,例如<a>
作为锚,<form>
作为表。 当我们想要获得具有给定标签名称的所有元素时,这很有用。 如果被测页面只有一个标签,则findElement
将起作用。 但否则,建议对结果建立索引以找到我们希望使用的特定元素。
语法:driver.findElements(By.tagName("element_html_tag"));
说明:找到具有匹配标签名称的所有元素。
示例:让我们找到 Gmail 帐户创建页面右上角的“登录”按钮。
右键单击按钮,然后单击检查元素以获取相应的 HTML 代码。 我们可以看到它具有锚标记。 让我们继续并在列表中获取所有具有tagName="a"
的网络元素,然后通过其索引找到第一个元素,以便与“登录”按钮进行交互。
// List of all elements with anchor tag
List<WebElement> buttons = driver.findElements(By.tagName("a"));
// Locate 'Sign in' button by indexing the list
WebElement signInButton = buttons.get(0);
概览
为了避免在尝试使用这些定位器时可能遇到的意外意外,让我们看一个实现上述定位器类型的测试用例。
场景
- 打开 Firefox 浏览器。
- 导航到 Google 帐户创建页面
- 通过 ID 找到名字文本框
- 输入“
fname01
”作为名字 - 按名称找到姓氏文本框
- 输入“
lname01
”作为姓氏 - 使用
tagName
找到“登录”按钮 - 将按钮文字打印到控制台进行验证
此方案的 JUnit 代码是,
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
public class ElementLocatorTest1 {
//Declaring variables
private WebDriver driver;
private String baseUrl;
@Before
public void setUp() throws Exception{
// Selenium version 3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver", "E:\\Softwares\\"
+ "Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe");
// Create a new instance for the class FirefoxDriver
// that implements WebDriver interface
driver = new FirefoxDriver();
// Implicit wait for 5 seconds
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// Assign the URL to be invoked to a String variable
baseUrl = "https://accounts.google.com/SignUp";
}
@Test
public void testPageTitle() throws Exception{
// Open baseUrl in Firefox browser window
driver.get(baseUrl);
// Locate First Name text box by id and
// assign it to a variable of type WebElement
WebElement firstName = driver.findElement(By.id("FirstName"));
// Clear the default placeholder or any value present
firstName.clear();
// Enter/type the value to the text box
firstName.sendKeys("fname01");
// Locate last name text box by name
WebElement lastName = driver.findElement(By.name("LastName"));
// Clear and enter a value
lastName.clear();
lastName.sendKeys("lname01");
// List of all elements with anchor tag
List<WebElement> buttons = driver.findElements(By.tagName("a"));
// Locate 'Sign in' button by indexing the list
WebElement signInButton = buttons.get(0);
// Verifying if the button is located
System.out.println("First button text: " + signInButton.getText());
}
@After
public void tearDown() throws Exception{
// Close the Firefox browser
driver.close();
}
}
解释
将导入两个新的包,
导入org.openqa.selenium.WebElement
– 实例化一个新的 Web 元素。
导入org.openqa.selenium.By
– 该包引用了被称为定位器类型的By
类。
注释清楚地提供给每一行代码,因此很容易解释。
执行测试用例后,成功显示为绿色,并且输出显示在控制台上,确认“登录”按钮与tagName
一起位于。 图像的右半部分显示了在 Firefox 浏览器中执行的自动输出。 名字和姓氏文本框将填充代码中给出的值。
让我们在这里休息一下。 在下面的文章中,我们将通过示例看到另外三种查找元素的方法。 蒙克就这些想法直到那时!
祝你今天愉快!
9K WebDriver – 定位元素:第 2 部分(按className
,linkText
,partialLinkText
)
原文: https://javabeginnerstutorial.com/selenium/9k-webdriver-locating-elements-2/
朋友! 让我们今天更深入地研究一下定位元素的更多策略。 在这篇文章中,我们将重点放在
className
LinkText
partialLinkText
按类名称定位
类名称不过是用于设置 Web 元素样式的 CSS 类名称。 重要的是要注意页面上的许多 Web 元素可能具有相同的className
。 在这种情况下,可以使用findElements
方法,并且可以为结果建立索引。 请参考通过tagName
策略定位(解释以及先前文章中提供的示例)。 如果我们有一个具有唯一className
的元素,或者被测元素是该页面中使用该className
的第一个元素,则findElement
将执行此任务。
语法:driver.findElement(By.className("element_class_name"))
*
说明:找到具有匹配 CSS 类名称的第一个元素。
示例:让我们找到 gmail 帐户创建页面的“手机”文本框。
右键单击文本框,然后选择检查元素以获取相应的 HTML 代码。 我们可以看到“input
”标签包含class ="i18n_phone_number_input-inner_input"
。 让我们继续使用此类名称查找“手机”文本框,以进行进一步的交互。
代码:
driver.findElement(By.className("i18n_phone_number_input-inner_input"));
通过linkText
定位
当您想与超链接进行交互时,linkText
非常有用。 使用该链接在网页上显示的实际文本。 那有多容易?
语法:driver.findElement(By.linkText("hyperlink_text");
说明:找到具有匹配链接文本的第一个超链接。
示例:让我们找到在 gmail 帐户创建页面底部提供的超链接“了解更多信息”。
代码:
driver.findElement(By.linkText("Learn more"));
通过partialLinkText
定位
PartialLinkText
也用于与超链接进行交互,与linkText
定位策略非常相似。 此方法不提供部分完整链接,而是提供链接显示的完整文本。 因此,可以将链接文本的一部分作为匹配条件。
语法:driver.findElement(By.partialLinkText("hyperlink_partial_text");
说明:找到第一个超链接,其中包含指定的部分链接文本。
示例:让我们在 Gmail 帐户创建页面的“选择用户名”文本框下方找到超链接“我更喜欢使用我当前的电子邮件地址” - 文本:我更喜欢。
代码:
driver.findElement(By.partialLinkText("I prefer to"));
概览
让我们看一个实现上述三种定位器类型的测试用例。
场景
- 打开 Firefox 浏览器。
- 导航到 Google 帐户创建页面
- 通过
className
找到手机文本框 - 输入“
9496543210
”作为手机号码 - 通过
linkText
找到“了解详情”超链接 - 链接超链接
- 找到“我更喜欢使用当前的电子邮件地址”超链接和
partialLinkText
- 将完整的链接文本打印到控制台进行验证
此方案的 JUnit 代码是,
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
public class ElementLocatorTest2 {
//Declaring variables
private WebDriver driver;
private String baseUrl;
@Before
public void setUp() throws Exception{
// Selenium version 3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver", "E:\\Softwares\\"
+ "Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe");
// Create a new instance for the class FirefoxDriver
// that implements WebDriver interface
driver = new FirefoxDriver();
// Implicit wait for 5 seconds
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// Assign the URL to be invoked to a String variable
baseUrl = "https://accounts.google.com/SignUp";
}
@Test
public void testPageTitle() throws Exception{
// Open baseUrl in Firefox browser window
driver.get(baseUrl);
// Locate Mobile phone text box by className and
// assign it to a variable of type WebElement
WebElement mobileNum = driver.findElement(By.className("i18n_phone_number_input-inner_input"));
// Clear the default placeholder or any value present
mobileNum.clear();
// Enter/type the value to the text box
mobileNum.sendKeys("9496543210");
// Locate 'Learn more' hyperlink by link text
WebElement link1 = driver.findElement(By.linkText("Learn more"));
// Click on 'Learn more'
link1.click();
// Locate hyperlink by partial link text
WebElement link2 = driver.findElement(By.partialLinkText("I prefer to"));
// Printing the complete link text to console
System.out.println("Complete link text: " + link2.getText());
}
@After
public void tearDown() throws Exception{
// Close the Firefox browser
driver.close();
}
}
执行结果
注释清楚地提供给每一行代码,因此很容易解释。
在 JUnit 窗格中,绿色条显示测试用例已成功执行。 输出将打印到控制台,以确认仅提供子文本“我更喜欢”作为 partialLinkText,即可访问“我更喜欢使用当前的电子邮件地址”超链接。
该图像的左半部分显示输入的电话号码,右半部分显示在 Firefox 浏览器中执行的最终输出。 点击“了解详情”链接后,我们将重定向到相应的页面。
是时候再休息一次了。 在接下来的文章中,准备好消化更多的信息,因为我们将研究两种有效的元素定位技术。
享受这一天!
9L WebDriver – 定位元素:第 3a 部分(按cssSelector
定位)
原文: https://javabeginnerstutorial.com/selenium/9l-webdriver-locating-elements-3a/
嗨呀测试人员! 让我们直接进入cssSelector
定位策略。
如果我们没有选择使用 ID,类或名称来查找元素的方法,该怎么办? 袖子上有东西吗? 好吧,我有一个cssSelector
。 这是一种先进而有效的策略。
热身
- CSS (级联样式表)。 根据维基百科的说法,“CSS 是一种样式表语言,用于描述以标记语言编写的文档的表示形式”。 它通过添加样式来更改 HTML 元素的外观,并定义应如何在网页上显示它们。
- 选择器是使用 HTML 标记,属性及其值构造的模式。 然后将它们用于匹配所需的 Web 元素。
使用cssSelector
定位器优于 XPath 定位器的优点
- 速度更快,尤其是在 Internet Explorer 中
- 更简单
- 更具可读性
- 首选使用方式
通过cssSelector
定位
有多种使用cssSelector
定位器的方式,这取决于可用的标签,属性,关键字等。要列出它们,
- 使用标签和 ID 属性
- 使用标签和类属性
- 使用标签和名称属性
- 使用标签和多个属性
- 定位子元素
- 按子字符串匹配
在这篇文章中,我们将介绍前四种方法。 是时候一步一步地完成这些工作了。
1.使用标签和 ID 属性
借助cssSelector
定位策略,可以使用元素的 HTML 标记及其 ID 属性及其值来对其进行访问。
有两种方法可以实现此目的:
语法:driver.findElement(By.cssSelector("tag_name#value_of_id"));
或
driver.findElement(By.cssSelector("tag_name[id='value_of_id']"));;
说明:使用匹配的 CSS 选择器定位元素。 在第一种方法中,#
符号代表“id
”属性。
示例:让我们在 Gmail 帐户登录页面上找到“电子邮件”文本框。
右键单击“输入电子邮件”文本框,然后选择检查元素以获取相应的 HTML 代码。 我们可以看到“input
”标签的“id
”属性为“email
”。
代码:
driver.findElement(By.cssSelector("input#Email"));
(或者)
driver.findElement(By.cssSelector("input[id='Email']"));
2.使用标签和类属性
元素的 HTML 标签及其类属性及其值可用于访问它。 也有两种方法可以实现此目的,
语法:driver.findElement(By.cssSelector("tag_name.value_of_class"));
或
driver.findElement(By.cssSelector("tag_name[class='value_of_class']”));
说明:使用匹配的 CSS 选择器定位元素。 在第一种方法中,“.
”符号代表“class
”属性。
示例:让我们在使用类属性的 Gmail 帐户“注册”页面上找到“手机”文本框。
右键点击“手机”文本框,然后选择检查元素,以获取相应的 HTML 代码,如下所示,
<input tabindex="0" name="RecoveryPhoneNumber"
id="RecoveryPhoneNumber" value=""
class="i18n_phone_number_input-inner_input" type="tel">
我们可以看到“input
”标签具有“class
”属性,名称为“i18n_phone_number_input-inner_input
”。
代码:
driver.findElement(By.cssSelector("input.i18n_phone_number_input-inner_input "));
(or)
driver.findElement(By.cssSelector("input[class='i18n_phone_number_input-inner_input']"));
3.使用标签和名称属性
元素的 HTML 标记及其名称属性及其值可用于访问它。
语法:driver.findElement(By.cssSelector("tag_name[name='value_of_name']"))
说明:使用匹配的 CSS 选择器定位元素。
示例:让我们在 Gmail 帐户注册页面上找到名字文本框。
右键点击“名字”文本框,然后选择检查元素,以获取相应的 HTML 代码,如下所示,
<input value="" name="FirstName"
id="FirstName" spellcheck="false" class="
form-error" aria-invalid="true" type="text">
我们可以看到,“input
”标签的“name
”属性为“FirstName
”。
代码:
driver.findElement(By.cssSelector("input[name='FirstName']"));
4.使用标签和多个属性
元素的 HTML 标签和一个以上的属性及其值可用于访问它。
语法:driver.findElement(By.cssSelector("tag_name[attribute1='value_of_attribute1'][attribute2='value_of_attribute2']"))
说明:使用匹配的 CSS 选择器定位元素。 以这种方式可以提到许多属性。
示例:让我们使用 ID,类型和名称属性在 Gmail 帐户注册页面上找到“创建密码”文本框。
右键点击“创建密码”文本框,然后选择检查元素以获取相应的 HTML 代码,如下所示,
<input name="Passwd" id="Passwd" type="password">
我们可以看到,“input
”标签具有“name
”和“id
”属性,它们的值均为“Passwd
”,“type
”属性为password
。
代码:
driver.findElement(By.cssSelector("input#Passwd[name='Passwd']"));
(or)
driver.findElement(By.cssSelector("input[type='Password'][name='Passwd'"));
如果使用 id,则可以用“#
”符号表示,而可以用“.
”符号表示类。
感觉都糟透了吗? 需要一些时间练习吗?
我知道,您急需先休息一下。 你的愿望就是我的命令! 现在,时间全在您了。
我们的下一篇文章很快见。 祝你有美好的一天!
9M WebDriver – 定位元素:第 3b 部分(cssSelector
续)
原文: https://javabeginnerstutorial.com/selenium/9m-webdriver-locating-elements-3b/
嗨,忍者! 这篇文章是我们先前文章“9l WebDriver – 定位元素:第 3a 部分(由cssSelector
提供)”的延续。 。 在继续进行操作之前,请确保对它进行了仔细的研究。
使用cssSelector
定位器的最后两种方法尚待讨论:
- 定位子元素
- 按子字符串匹配
事不宜迟,让我们开始行动吧!
定位子元素
也可以使用cssSelectors
来定位子元素。
让我们考虑一下 HTML 代码,
锚标记是“div
”的子元素。 要访问子元素,
使用 ID:
driver.findElement(By.cssSelector("div#child a"));
#
代表“ID”,子元素标签写在空格后面。
使用类别:
driver.findElement(By.cssSelector("div.bg_main a"));
.
代表“类”
示例:让我们在 Gmail 帐户注册页面上找到名字文本框。
右键点击“名字”文本框,然后选择检查元素,以获取相应的 HTML 代码,如下所示,
<label id="recovery-email-label">
<strong>Your current email address</strong>
<input name="RecoveryEmailAddress" id="RecoveryEmailAddress" value=""
spellcheck="false" type="text">
</label>
“标签”标签的子元素可以通过其input
标签和name
属性进行访问。
代码:
driver.findElement(By.cssSelector("label#recovery-email-label input[name='RecoveryEmailAddress']"));
如果父元素具有多个子元素(例如下拉列表),并且它们没有“id
”或“class
”或此类属性来标识,则“nth-of-type
”用于定位特定的子元素。
考虑一下 HTML 代码,
<li>Cat</li>
<li>Dog</li>
<li>Birds</li>
要标识子元素“狗”,
代码:
driver.findElement(By.cssSelector("ul#pets li:nth-of-type(2)"));
按子字符串匹配
cssSelectors
也帮助我们使用子字符串来定位元素。
匹配前缀(或)开头
为了匹配具有已知前缀的元素,
语法:driver.findElement(By.cssSelector(")tag_name[attribute^='prefix_value_of_attribute']"))
解释:找到以给定前缀开头的元素。 用“^
”符号表示。
考虑以下代码,
<input value="" name="LastName" id="LastName" spellcheck="false" type="text">
我们可以看到input
标签具有一个名为“LastName
”的“id
”属性。 要找到此元素,我们可以指定查找以“Last
”开头的“id
”属性值。
代码:
driver.findElement(By.cssSelector("input[id^='Last']"));
匹配后缀(或)结尾
为了匹配具有已知后缀的元素,
语法:driver.findElement(By.cssSelector("tag_name[attribute$='suffix_value_of_attribute']"))
说明:找到以给定后缀结尾的元素。 用“$
”符号表示。
Considering the below code,
<input name="PasswdAgain" id="PasswdAgain" type="password">
我们可以看到“input
”标签的“name
”属性为“PasswdAgain
”。 要找到此元素,我们可以指定查找以“Again
”结尾的“name
”属性值。
代码:
driver.findElement(By.cssSelector("input[name$='Again']"));
匹配一个子字符串
为了使元素与子字符串匹配,
语法:driver.findElement(By.cssSelector("tag_name[attribute*='substring_of_attribute_value']"))
说明:找到包含给定子字符串的元素。 用“*
”符号表示。
Considering the below code,
<input name="PasswdAgain" id="PasswdAgain" type="password">
我们可以看到“input
”标签的“name
”属性为“PasswdAgain
”。 要找到此元素,我们可以指定查找包含“wdAg
”的“name
”属性值。
代码:
driver.findElement(By.cssSelector("input[name*='wdAg']"));
概览
让我们看一个测试用例,它实现了使用本方法和上一篇文章中介绍的cssSelector
定位器的不同方法,
场景
- 打开 Firefox 浏览器。
- 导航到 Google 帐户创建页面
- 找到带有 HTML 标签和名称属性的“名字”文本框
- 输入“
testFirst
”作为名字 - 在“姓氏”文本框中找到一个以子字符串开头的值
- 输入“
testLast
”作为姓氏 - 找到带有 HTML 标签,类型和名称属性的“创建密码”文本框
- 输入“
Pass1234!
”作为密码 - 在“确认您的密码”文本框中找到包含子字符串的值
- 输入“
Pass1234!
”作为确认密码 - 找到带有 HTML 标签和类别属性的“手机”文本框
- 输入“
9496543210
”作为电话号码 - 使用子元素方法找到“当前电子邮件地址”文本框
- 输入“
...
” - 验证“JUnit”窗格是否成功,并确认 Eclipse IDE 控制台输出屏幕
此方案的 JUnit 代码是,
package com.blog.junitTests;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
public class ElementLocatorTest3 {
//Declaring variables
private WebDriver driver;
private String baseUrl;
@Before
public void setUp() throws Exception{
// Selenium version 3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver", "E:\\Softwares\\"
+ "Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe");
// Create a new instance for the class FirefoxDriver
// that implements WebDriver interface
driver = new FirefoxDriver();
// Implicit wait for 5 seconds
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// Assign the URL to be invoked to a String variable
baseUrl = "https://accounts.google.com/SignUp";
}
@Test
public void testPageTitle() throws Exception{
// Open baseUrl in Firefox browser window
driver.get(baseUrl);
// Locate 'First Name' text box by cssSelector: tag and name attribute
// assign it to a variable of type WebElement
WebElement firstName = driver.findElement(By.cssSelector("input[name='FirstName']"));
// Clear the default placeholder or any value present
firstName.clear();
// Enter/type the value to the text box
firstName.sendKeys("testFirst");
//Locate 'Last Name' text box by cssSelector: begins with sub-string
WebElement lastName = driver.findElement(By.cssSelector("input[id^='Last']"));
lastName.clear();
lastName.sendKeys("testLast");
//Locate password text box by cssSelector: tag and type, name attributes
WebElement pwd = driver.findElement(By.cssSelector("input[type='Password'][name='Passwd']"));
pwd.clear();
pwd.sendKeys("Pass1234!");
//Locate 'Confirm your password' text box by cssSelector: contains sub-string
WebElement confirmPwd = driver.findElement(By.cssSelector("input[name*='wdAg']"));
confirmPwd.clear();
confirmPwd.sendKeys("Pass1234!");
// Locate Mobile phone text box by cssSelector: tag and class
WebElement mobileNum = driver.findElement(By.cssSelector("input.i18n_phone_number_input-inner_input"));
mobileNum.clear();
mobileNum.sendKeys("9496543210");
//Locate "current email address" text box by cssSelector: child element method
WebElement recoveryEmail = driver.findElement(By.cssSelector("label#recovery-email-label input[name='RecoveryEmailAddress']"));
recoveryEmail.clear();
recoveryEmail.sendKeys("[[email protected]](/cdn-cgi/l/email-protection)");
}
@After
public void tearDown() throws Exception{
// Close the Firefox browser
driver.close();
}
}
执行结果
注释清楚地提供给每行代码,因此是不言自明的。
在 JUnit 窗格中,绿色条显示测试用例已成功执行。 另外,控制台窗口中不会记录任何错误。
下图显示了在 Firefox 浏览器中执行的最终输出。
今天的忍者就这些了! 在另一篇文章中再见。
祝你有美好的一天!
9N WebDriver – 定位元素:第 4a 部分(通过 xpath)
原文: https://javabeginnerstutorial.com/selenium/9n-webdriver-locating-elements-4a/
欢迎回来,我们今天将讨论 XPath 策略。 这也是一种先进且有效的定位策略(cssSelectors
也是!)。 虽然有效,但有时可能会造成混淆。 因此,让我们深入研究如何理解我们经常被误解的朋友,并一劳永逸地提出好的代码。
当所有希望都丧失了时,拯救自己的唯一机会就是转向 XPath 策略。 因为在大多数情况下,我们将测试现有的东西并且无法修改。 我们并不总是能够控制页面以添加一些 ID,从而使自动化成为一项更简单的任务。 因此,请停止咆哮并认真对待!
准备开始
XPath (XML 路径语言):根据 w3schools 的说法,XPath 是一种“路径式”语言,用于标识和浏览 XML 文档中的各种元素和属性。
因此,XPath 提供了用于定位 HTML 文档中任何元素的语法。
注意:
如果到目前为止,您从未接触过 XPath,请在继续进行操作之前先了解 XPath 术语。 特别是,节点和这些节点之间的关系即父级,子级,同级,祖先,后代。
如果您已经知道这些术语,并且希望略微刷一下,请参考下图。 礼貌: w3schools
现在是当今的主要任务:通过 XPath 策略定位元素! 在这篇文章中,我们将研究以下技术,
- 捷径
- 绝对 XPath 和相对 XPath
- 使用标签和属性
- 使用两个条件
- 使用
contains()
- 查找多个元素
让我们从最简单的动机开始。
1.捷径:
是否想以简单的方式在网页上找到任何元素的 XPath? 做完了。是否希望几乎每次都处于完美的工作状态? 也做完了。 马上完成所有这些操作呢? 它让您涵盖了摇滚明星! 您只需要“Firebug”! 它可以作为 Firefox 浏览器的附加组件使用。
食谱来了:
- 点击 Firebug 图标或按“
F12
”。 - 检查其 XPath 是必需的元素。
- 相应的代码将在 Firebug 面板的“HTML”部分中突出显示。
- 右键点击突出显示的代码,然后选择“复制 XPath”
- 瞧! 您已将准备好的烘焙 XPath 复制到剪贴板!
瞥见我们刚才所说的话,
如果您希望获得有关 Firebug 的详细信息,请在处查看。
2.现在对于长短:
为了从头提出 XPath,我们首先需要了解可用的两种 Xpath。 它们是绝对 XPath 和相对 XPath。
绝对 XPath | 相对 XPath |
---|---|
它以单个正斜杠(/ )开头。 |
它以双正斜杠(// )开头。 |
/ 指示 XPath 引擎参考根节点搜索元素。 |
// 指示 XPath 引擎在 DOM 结构中的任何位置搜索匹配的元素。 |
与相对的 XPath 相比,元素标识更快。 | 由于仅指定了部分路径,因此需要花费更多时间来标识元素。 |
即使对 HTML DOM 结构进行了最细微的更改(例如添加标签或删除标签),绝对 XPath 也会失败。 | 相对 XPath 较短,更改的可能性较小,从而使其更可靠。 |
例如,/html/head/body/div[2]/form/input |
例如//input[@name="username"] |
有了这些基本知识,就让我们开始吧!
3.使用标签和属性:
可以使用其 HTML 标签,其属性(例如 ID,名称,类,标题,值,href
,src
等)及其相应的值来定位特定的 Web 元素。
语法:driver.findElement(By.xpath("//tag_name[@attribute='value']"));
解释:标识 XPath 指向的元素。 “//
”标识指定的节点,“@
”符号用于选择与给定值匹配的指定属性。
示例:让我们在 Gmail 帐户注册页面上找到名字文本框。
右键点击“名字”文本框,然后选择检查元素,以获取相应的 HTML 代码,如下所示,
<input value="" name="FirstName" id="FirstName" spellcheck="false"
class="form-error" aria-invalid="true" type="text">
我们可以看到“input
”标签具有一个“name
”属性,其值为“FirstName
”。
代码:(可以使用以下任一选项)
driver.findElement(By.xpath("//input[@name='FirstName']"));
driver.findElement(By.xpath("//input[@id='FirstName']"));
driver.findElement(By.xpath("//input[@class='form-error']"));
如果您希望使用绝对路径,
driver.findElement(By.xpath("/html/body/div[1]/div[2]/div/div[1]/div/form/div[1]/fieldset/label[1]/input"));
表单标签有许多子div
标签。 在我们的情况下,我们希望选择第一个div
标签。 可以将其指定为“div[1]
”。 这些方括号[]
中的数字表示要选择的确切同级。
4.使用两个条件
如果多个标签具有相同的属性值怎么办? 或者,如果您希望仅在元素与指定条件都匹配时才定位它,该怎么办?
语法:driver.findElement(By.xpath("//tag_name[@attribute1='value1'][@attribute2='value2']"))
说明:标识具有指定tag_name
的元素,这些元素的属性与给定值匹配。
示例:让我们在 Gmail 帐户注册页面上找到“确认密码”文本框。
右键点击“创建密码”和“确认密码”文本框,然后选择检查元素以获取相应的 HTML 代码,如下所示,
<input name="Passwd" id="Passwd" type="password">
<input name="PasswdAgain" id="PasswdAgain" type="password">
请注意,两个文本框的“class
”属性值均相同,但“id
”和“name
”的值均不同。 因此,为了找到“确认密码”文本框,让我们同时提及其“class
”和“id
”值。
代码:
driver.findElement(By.xpath("//input[@type='password'][@id='PasswdAgain']"));
5.使用contains()
:
如今,大多数属性值(例如“id
”,“src
”,“href
”等)都是使用恒定的前缀或后缀动态生成的。
想象一个网页每天都有变化的图像。 其src
可以是“Image_random-generated-key_date.jpg
”。 在这种情况下,我们可以通过 XPath 使用在其src
属性中包含“Image
”值的“img
”标记定位图像。 因此,通过指定属性的部分值,我们可以找到元素。
语法:driver.findElement(By.xpath("//tag_name[contains(@attribute, 'value')]]"))
说明:标识具有指定tag_name
的元素,该元素的属性与给定的部分值相匹配。
示例:让我们在 Gmail 帐户注册页面上找到“下一步”按钮。
右键单击按钮并检查元素以获取相应的 HTML 代码,如下所示,
代码:
driver.findElement(By.xpath("//div[contains(@class,'button')]/input"));
注意,该 XPath 标识div
元素,该div
元素包含带有部分值button
的class
属性(<div class="form-element nextstep-button">
),然后找到其子“input
”标签(即“提交”按钮)。
6.查找多个元素
您可能会遇到希望查找具有特定类或名称的所有元素并对它们执行某些操作的情况。 星号(*
)符号可助我们一臂之力!
示例:让我们在 Gmail 帐户注册页面上找到所有类值为“goog-inline-block
”的元素。
代码:
driver.findElements(By.xpath("//*[contains(@class,'goog-inline-block')]"));
这将找到在其“class
”属性中包含值“goog-inline-block
”的所有标签。
明确指出要使用“findElements
”,以便将所有已标识的 Web 元素添加到列表中。 如果使用“findElement
”,它将仅返回所标识的第一个元素。
概览
让我们来看一个测试案例,该案例实现了迄今为止本文中涵盖的所有技术,
场景
- 打开 Firefox 浏览器。
- 导航到 Google 帐户创建页面
- 使用绝对 XPath 找到“名字”文本框
- 输入“
testFirst
”作为名字 - 使用标签和“
id
”属性找到“姓氏”文本框(当然是相对的 XPath!) - 输入“
testLast
”作为姓氏 - 使用两个条件(类型和 ID 属性)找到“确认密码”文本框
- 输入“
Pass1234!
”作为确认密码 - 使用星号将所有包含在其“
class
”属性中值“goog-inline-block
”的元素定位 - 将找到的元素总数打印到控制台
- 将第一个标识的元素的标题值打印到控制台
- 使用
contains()
找到“下一步”按钮 - 将其“
name
”属性的值打印到控制台 - 验证 Eclipse IDE 控制台的输出屏幕和 JUnit 窗格是否成功
此方案的 JUnit 代码是,
package com.blog.junitTests;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
public class ElementLocatorTest4 {
//Declaring variables
private WebDriver driver;
private String baseUrl;
@Before
public void setUp() throws Exception{
// Selenium version3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver", "E:\\ Softwares\\Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe");
// Create a new instance for the class FirefoxDriver
// that implements WebDriver interface
driver = new FirefoxDriver();
// Implicit wait for 5 seconds
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// Assign the URL to be invoked to a String variable
baseUrl = "https://accounts.google.com/SignUp";
}
@Test
public void testPageTitle() throws Exception{
// Open baseUrl in Firefox browser window
driver.get(baseUrl);
// Locate 'First Name' text box by absolute XPath
// assign it to a variable of type WebElement
WebElement firstName = driver.findElement(By.xpath("/html/body/div[1]/div[2]/div/div[1]"
+ "/div/form/div[1]/fieldset/label[1]/input"));
// Clear the default placeholder or any value present
firstName.clear();
// Enter/type the value to the text box
firstName.sendKeys("testFirst");
// Locate 'Last Name' text box by relative XPath: using tag and id attribute
WebElement lastName = driver.findElement(By.xpath("//input[@id='LastName']"));
lastName.clear();
lastName.sendKeys("testLast");
// Locate 'Confirm your password' text box by XPath: using two conditions
WebElement confirmPwd = driver.findElement(By.xpath("//input[@type='password'][@id='PasswdAgain']"));
confirmPwd.clear();
confirmPwd.sendKeys("Pass1234!");
//Locate all elements with class 'goog-inline-block' by relative XPath: using asterisk symbol
List<WebElement> dropdowns = driver.findElements(By.xpath("//*[contains(@class,'goog-inline-block')]"));
// Prints to the console, the total number of elements located
System.out.println("Total elements containing the class 'goog-inline-block' are= " + dropdowns.size());
// Prints first identified element's title value to console
System.out.println("Value of the dropdown's 'title' attribute = " + dropdowns.get(0).getAttribute("title"));
// Locate 'Next step' button by XPath: contains() and child element
WebElement submitBtn = driver.findElement(By.xpath("//div[contains(@class,'button')]/input"));
// Prints submitBtn's 'name' attribute's value to the console
System.out.println("Value of the button's 'name' attribute = " + submitBtn.getAttribute("name"));
}
@After
public void tearDown() throws Exception{
// Close the Firefox browser
driver.close();
}
}
执行结果:
这段代码将作为本文讨论的每种技术的一部分进行解释。
在 JUnit 窗口中,绿色条显示测试用例已成功执行。 控制台窗口显示没有任何错误。 它还显示带有星号的 Web 元素总数,以及下拉菜单和按钮的属性值。
下图显示了成功执行测试脚本后获得的 Firefox 输出。
休息一下伙计! 我们的下一篇文章将是定位元素策略的最后一篇。 所以不要错过! 很快见,祝您愉快!
9O WebDriver – 定位元素:第 4b 部分(XPath 续)
原文: https://javabeginnerstutorial.com/selenium/9o-webdriver-locating-elements-4b/
嗨冠军! 欢迎来到我们关于定位元素的最后一篇文章。 好极了!!!
这是我们上一篇文章“9n。 WebDriver – 定位元素:第 4a 部分(由 XPath 提供)”。 您以前曾经听过我说过这个,然后您会再次听我说过……在继续使用 XPath 策略之前,请先阅读第 4a 部分。
在这篇文章中,我们将研究以下技术,
- 使用
Text()
- 使用
starts-with()
- 使用 XPath 轴
这些技术以及我们在第 4a 部分中看到的技术可以结合起来,并用于形成有效的 XPath,该 XPath 可以定位网页上的任何元素。 现在通过示例来介绍当今的技术。
1.使用text()
通过提供与网页上显示的文本完全相同的文本,这是一种轻松定位元素的方法。
注意:
当心! 即使错误地包含空格,您也可能遇到“ElementNotFound
”异常。 在代码中提供的文本必须与可见文本完全匹配,这一点非常重要,我的意思是从字面上看!
示例:让我们找到“Facebook 注册”页面上的“忘记帐户?”链接。
右键点击“忘记帐户?”链接,然后选择检查元素以获取相应的 HTML 代码,如下所示,
<a href="https://www.facebook.com/recover/initiate?lwv=111"
data-testid="forgot_account_link">Forgot account?</a>
让我们通过提供出现在 Facebook 页面上的文本来找到此链接。
代码:
driver.findElement(By.xpath("//a[text()='Forgot account?']"));
如果要查找包含部分文本的所有元素,可以将contains()
和text()
技术结合使用。 可以使用findElements
方法在列表中获取所有元素。
让我们尝试在同一示例中同时实现contains()
和text()
。 由于我们只有一个链接与该文本,因此将使用findElement
方法。
driver.findElement(By.xpath("//*[contains(text(),'Forgot')]"));
2.使用starts-with()
通过指定属性的部分值(前缀),可以使用starts-with()
查找元素。 当页面重新加载时属性值动态更改时,此功能非常有用。
示例:让我们在“Facebook 注册”页面上找到“新密码”文本框。
右键点击“新密码”文本框,然后选择检查元素以获取相应的 HTML 代码,
<input class="inputtext _58mg _5dba _2ph-" data-type="text"
name="reg_passwd__" aria-required="1" placeholder="" id="u_0_d"
aria-label="New password" type="password">
让我们通过提供type
属性的部分前缀值“通过”来找到此文本框。 请注意,“登录”部分中的密码字段还具有type
作为password
。
<input class="inputtext" name="pass" id="pass" tabindex="2" type="password">
因此,第二个元素必须位于我们的案例中。
代码:
driver.findElement(By.xpath("(//input[starts-with(@type,'pass')])[2]"));
3.使用 XPath 轴
XPath 轴定义在当前节点浏览 DOM 的树形结构时要考虑的相对于当前节点的节点集或方向。
下表(礼貌性表示: w3schools )显示了所有 13 个可用的 XPath 轴及其结果。
轴名称 | 结果 |
---|---|
祖先 | 选择当前节点的所有祖先(父,祖父级等) |
祖先或自己 | 选择当前节点的所有祖先(父,祖父级等)和当前节点本身 |
属性 | 选择当前节点的所有属性 |
子项 | 选择当前节点的所有子节点 |
后代 | 选择当前节点的所有后代(子代,孙代等) |
后代或自己 | 选择当前节点的所有后代(子代,孙代等)和当前节点本身 |
之后 | 选择当前节点的结束标记之后的文档中的所有内容 |
之后的同级 | 选择当前节点之后的所有同级 |
命名空间 | 选择当前节点的所有名称空间节点 |
父项 | 选择当前节点的父节点 |
之前 | 选择出现在文档中当前节点之前的所有节点,但祖先,属性节点和名称空间节点除外 |
之前的同级 | 选择当前节点之前的所有同级 |
自己 | 选择当前节点 |
让我们来看一些重要的
3a. 父轴
选择当前节点的父级。
示例:让我们找到位于 Facebook Sign Up 页面左上方的 Facebook 徽标的锚标记。
在检查元素后,
<a href="https://www.facebook.com/" title="Go to Facebook Home">
<i class="fb_logo img sp_euCDsy2vhU4 sx_af4dba">
Facebook
</i>
</a>
代码:
driver.findElement(By.xpath("//i[@class='fb_logo']/parent::a"));
将找到具有href
和title
属性的父节点“a
”。
3b. 祖先轴
选择当前节点的所有祖先(父代,祖父级等)。
示例:让我们在“Facebook 注册”页面上找到文字“生日”。
Upon inspecting the element,
Birthday
代码:
driver.findElement(By.xpath("//select[@id='month']/ancestor::div[@class='_5k_5']/preceding-sibling::div"));
带有 ID 的“select
”标签,选择了“month
”。 转到类“_5k_5
”的祖先div
标签。 然后到其前一个带有“div
”标签的同级节点,其文本为“Birthday
”。
选择该特定示例以显示可以组合多个轴以获得所需的结果。
3c. 子轴
选择当前节点的所有子节点
示例:让我们找到“Facebook 注册”页面上的“登录”按钮。
Upon inspecting the element,
<label class="uiButton uiButtonConfirm" for="u_0_q" id="loginbutton"></label>
代码:
driver.findElement(By.xpath("//label[@id='loginbutton']/child::input"));
标识为“loginbutton
”的标签的子节点-标识为“u_0_q
”的输入标签。
3d. 后代轴
选择当前节点的所有后代(子代,孙代等)。
示例:让我们在“Facebook 注册”页面上找到“名字”文本框。
Upon inspecting the element,
First name
<input id=”u_0_1″ class=”inputtext _58mg _5dba _2ph-” data-type=”text” name=”firstname” aria-required=”1″ placeholder=”” aria-label=”First name” type=”text”> </div>
代码:
driver.findElement(By.xpath("//div[contains(@class,'uiStickyPlaceholderInput')]/descendant::input"));
类别为uiStickyPlaceholderInput
的div
标签的后代节点-ID 为u_o_1
的输入标签已找到。
3e. 同级轴
选择当前节点之后的所有同级
示例:让我们在“Facebook 注册”页面页脚部分中找到“登录”链接。
Upon inspecting the element,
<td class="_51m- hLeft plm">
<a href="/r.php" title="Sign Up for Facebook">Sign Up</a>
</td>
<td class="_51m- hLeft plm">
<a href="/login/" title="Log into Facebook">Log In</a>
</td>
代码:
driver.findElement(By.xpath("//td[@class='_51m- hLeft plm']/following-sibling::td/child::a"));
以下td
标签的同级类_51m-hLeft plm
是另一个td
标签,其子对象是带有标题“登录 Facebook”的锚标签。
将后继同级和子级轴组合在一起,以在页脚部分中找到“登录”超链接。
3f. 上一个同级轴
选择当前节点之前的所有同级
示例:让我们找到“Facebook 注册”页面上的“女性”单选按钮。
Upon inspecting the element,
<span class="_5k_2 _5dba">
<input id="u_0_g" name="sex" value="1" type="radio">
<label class="_58mt" for="u_0_g">Female</label>
</span>
代码:
driver.findElement(By.xpath("//label[@class='_58mt']/preceding-sibling::input"));
“标签”标签的前面同级是“无线电”类型的input
标签。 这样就找到了所需的单选按钮。
这样,就涵盖了一些常用的 XPath 轴类型。
我们在 BrainBell 方面一直处于停滞状态。 那为什么要延迟呢? 开始了,
BrainBell – 注意! 注意您的大脑状态。
- 症状:没有任何东西被注册,开始浏览文章或忘记您刚刚阅读的内容。
- 诊断:您的大脑超负荷。
- 补救:休息一下! 但是记得很快回来😉
我将根据我的经验个人建议 Pomodoro 技术。 它非常有效。 试一试!
概览
让我们来看一个测试案例,该案例实现了迄今为止本文中涵盖的所有技术,
场景
- 打开 Firefox 浏览器。
- 导航到 www.facebook.com
- 使用
text()
找到“忘记帐户?”链接 - 将链接文本打印到控制台
- 使用
starts-with()
找到“新密码”文本框 - 输入值“
test1234!
” - 使用子轴找到“登录”按钮
- 将值属性打印到控制台
- 使用父轴找到 Facebook 徽标
- 将其
title
属性的值打印到控制台 - 在页脚部分的“兄弟姐妹”轴中找到“登录”链接
- 将其
title
属性的值打印到控制台 - 使用上一个同级轴找到“女性”单选按钮
- 点击单选按钮
- 使用祖先轴找到文本“生日”
- 将其文本打印到控制台
- 使用后代轴找到“名字”文本框
- 输入值“首先测试”
- 验证 Eclipse IDE 控制台的输出屏幕和 JUnit 窗格是否成功
此方案的 JUnit 代码是,
package com.blog.junitTests;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
public class ElementLocatorTest4b {
// Declaring variables
private WebDriver driver;
private String baseUrl;
@Before
public void setUp() throws Exception {
// Selenium version 3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver", "E:\\Softwares\\Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe");
// Create a new instance for the class FirefoxDriver
// that implements WebDriver interface
driver = new FirefoxDriver();
// Implicit wait for 5 seconds
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// Assign the URL to be invoked to a String variable
baseUrl = "https://www.facebook.com/";
}
@Test
public void testPageTitle() throws Exception {
// Open baseUrl in Firefox browser window
driver.get(baseUrl);
// Locate 'Forgot Account' link using XPath: text()
WebElement forgotAccLink = driver.findElement(By.xpath("//*[contains(text(),'Forgot')]"));
// Prints the link text to the console
System.out.println("Link text: " + forgotAccLink.getText());
// Locate 'New password' text box by XPath: using starts-with()
WebElement passwordNew = driver.findElement(By.xpath("(//input[starts-with(@type,'pass')])[2]"));
// Clear the default placeholder or any value present
passwordNew.clear();
// Enter/type the value to the text box
passwordNew.sendKeys("test1234!");
// Locate 'Log In' button using XPath: child axis
WebElement logIn = driver.findElement(By.xpath("//label[@id='loginbutton']/child::input"));
// Prints 'value' attribute to console
System.out.println("Child axis: " + logIn.getAttribute("value"));
// Locate 'Facebook' logo using XPath: parent axis
WebElement fbLogo = driver.findElement(By.xpath("//i[contains(@class,'fb_logo')]/parent::a"));
// Prints 'title' attribute's value to console
System.out.println("Parent axis: " + fbLogo.getAttribute("title"));
// Locate 'Log In' link in footer section using XPath: following-sibling axis
WebElement loginFooter = driver.findElement(By.xpath("//td[@class='_51m- hLeft plm']/following-sibling::td/child::a"));
//Prints 'title' attribute's value to console
System.out.println("Following-sibling: " + loginFooter.getAttribute("title"));
// Locate 'female' radio button using XPath: preceding-sibling axis
WebElement femaleRadioBtn = driver.findElement(By.xpath("//label[@class='_58mt']/preceding-sibling::input"));
// Click the radio button
femaleRadioBtn.click();
// Locate 'Birthday' text using XPath: ancestor axis
WebElement birthday = driver.findElement(By.xpath("//select[@id='month']/ancestor::div[@class='_5k_5']/preceding-sibling::div"));
//Prints text to console
System.out.println("Ancestor axis: " + birthday.getText());
// Locate 'first name' test box using XPath: descendant axis
WebElement firstName = driver.findElement(By.xpath("//div[contains(@class,'uiStickyPlaceholderInput')]/descendant::input"));
firstName.clear();
firstName.sendKeys("test first");
}
@After
public void tearDown() throws Exception {
// Close the Firefox browser
driver.close();
}
}
执行结果:
这段代码将作为本文讨论的每种技术的一部分进行解释。
在 JUnit 窗口中,绿色条显示测试用例已成功执行。 控制台窗口显示没有任何错误。 它还可以按预期显示所有打印结果。
下图显示了成功执行测试脚本后获得的 Firefox 输出。
好吧! 现在,您应该全都适应定位策略。 我们将在所有即将到来的示例中看到这些策略的应用。 因此,请继续关注此空间。
在另一个帖子中再见! 祝你有美好的一天!
9P WebDriver – 节省时间的捷径:定位器验证
原文: https://javabeginnerstutorial.com/selenium/9p-webdriver-shortcut-locator-validation/
今天锦上添花:“一种验证所选择的定位器策略是否唯一标识被测网络元素的捷径”。
想象一下,您有一个取决于许多因素的测试脚本。 当您要测试新的 Web 元素时,要求您对它进行一些补充。 为了定位每个元素,可以使用先前文章中讨论的任何定位器策略(按 ID,名称,tagName
,className
,linkText
,cssSelector
,xpath 等)。 要检查代码是否按预期工作并且 Web 元素是否正确定位,您将不得不一次又一次地运行整个测试用例。 真痛苦!
在这种情况下,谁不喜欢快捷键或节省时间! 我听到你了
现在该重新回顾我们的魔术师草地,“Selenium IDE”,并利用“命令/目标/值编辑器”中的目标和查找功能,以便开启我们的旅程。
示例:
让我们使用cssSelector
(标记和名称属性)在 Gmail 帐户注册页面上找到“您当前的电子邮件地址”文本框。
右键点击“您当前的电子邮件地址”文本框,然后选择检查元素以获取相应的 HTML 代码,如下所示,
<input name="RecoveryEmailAddress" id="RecoveryEmailAddress"
value="" spellcheck="false" style="background-color: rgb(255, 255, 255);"
type="text">
我们可以看到input
标签的name
属性为“RecoveryEmailAddress
”。 下图显示了验证我们的定位器所遵循的步骤。
- 输入“
css = input[name='RecoveryEmailAddress']
”作为目标。 要了解此命令的形成方式,请查阅“7l Selenium IDE – 定位元素(续)(通过 CSS,DOM,XPath)” - 点击查找
- “您当前的电子邮件地址”文本框在 Gmail 帐户注册页面上突出显示。
这表明我们的定位器可以唯一地标识所需的元素,并且可以更加有把握地直接在代码中使用它。 这种方法节省了大量时间。 任务完成!
容易挤柠檬! 是不是
在另一篇文章中再见。 祝您有美好的一天!
9Q WebDriver – 处理验证码
原文: https://javabeginnerstutorial.com/selenium/9q-webdriver-handling-captcha/
有人说验证码吗? 好吧,我听到了! 让我们看看如何使用 Selenium WebDriver 处理这些问题,是的,我们将尝试尽快完成这项工作,因为今天晚些时候我将不得不参加比赛。
准备开始
CAPTCHA 是“Completely Automated Public Turing test to tell Computers and Humans Apart
”(用于分辨电脑和人类的完备自动化公开图灵测试)的首字母缩写。
哦,别生我的气! 那不是错字! 确实有一个叫做backronym
的缩写。 当由其他词的缩写形成缩写时,称为缩写。 但是,如果创建了一个新短语以适合已经存在的首字母缩写,那么它被称为简称。
因此,现在您知道什么是简称,并且 CAPTCHA 代表什么,让我们详细介绍一下。 验证码主要用于确定用户是否为人类。 它会生成图像或人类可以解决的某种形式的测试,但漫游器/计算机程序无法解决。
因此,其主要目的是防止漫游器和其他自动化程序从网站获取任何敏感信息。 因此, 验证码可以杀死自动化! 如果我们能够使其自动化,那么使用验证码的原因就变得毫无价值。
我有袖子吗? 让我看看…
- 出于测试目的,我们总是可以要求开发人员
- 禁用验证码验证
- 提供解决方法或后门以获取验证码值
- 调整验证码,使其每次都接受一个特定输入值
- 开发一种方法,将随机文本存储在验证码图像的
alt
属性中,然后可以使用可用的定位符获取该随机文本并将其传递到验证码文本框中
但是,请确保它们仅在测试环境中处于活动状态,并且仅用于自动化目的。
请注意,在使用上述这些方法进行测试时,应用安全性受到损害。
- 如果您想按原样测试应用,即在不出于自动化目的对测试环境进行任何修改的情况下,请分离所有涉及验证码验证的测试脚本并将其打包为单个测试套件。 在人为干预的情况下运行此测试套件。 我知道这是部分自动化,但并非总是可以在给定的情况下将所有内容自动化。 为了得到一些东西,你应该准备放弃一些东西。
- 除验证码外,整个测试用例均可自动执行。
- 可以使用隐式或显式等待,并且提示用户输入显示的验证码。
- 运行测试脚本时,将照常执行每个步骤。 到达提示命令后,浏览器将弹出一个窗口。 用户输入屏幕上显示的验证码。
- 手动输入验证码后,将继续执行测试用例,并在浏览器上运行后续步骤。
这是我之前的一个项目中遵循的过程。 因为此方法可确保被测应用恰好是将被推入生产环境的应用,并且安全性丝毫不受影响。
让我们看一个示例来更好地理解。
场景
- 打开 Firefox 浏览器
- 导航至 https://www.in.ckgs.us/myaccount/
- 通过
cssSelector
找到“当前护照号码”文本框:标记和名称属性 - 输入“
123456789
” - 提示用户进行干预并输入显示的验证码
- 通过 ID 找到验证码文本框
- 将用户输入的值发送到找到的验证码文本框中
- 在 Eclipse IDE 中验证 JUnit 窗格是否成功
此方案的 JUnit 代码是,
package com.blog.junitTests;
import java.util.concurrent.TimeUnit;
import javax.swing.JOptionPane;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
public class CaptchaValidation {
//Declaring variables
private WebDriver driver;
private String baseUrl;
@Before
public void setUp() throws Exception{
// Selenium version 3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver", "E:\\Softwares\\Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe");
// Create a new instance for the class FirefoxDriver
// that implements WebDriver interface
driver = new FirefoxDriver();
// Implicit wait for 5 seconds
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// Assign the URL to be invoked to a String variable
baseUrl = "https://www.in.ckgs.us/myaccount/";
}
@Test
public void testPageTitle() throws Exception{
// Open baseUrl in Firefox browser window
driver.get(baseUrl);
// Locate 'Current Passport Number' text box by cssSelector: tag and name attribute
WebElement passportNo = driver.findElement(By.cssSelector("input[name='currentPassportNo']"));
// Clear the default placeholder or any value present
passportNo.clear();
// Enter/type the value to the text box
passportNo.sendKeys("123456789");
//prompting user to enter captcha
String captchaVal = JOptionPane.showInputDialog("Please enter the captcha value:");
//Type the entered captcha to the text box
driver.findElement(By.id("recaptcha_response_field")).sendKeys(captchaVal);
}
@After
public void tearDown() throws Exception{
// Close the Firefox browser
driver.close();
}
}
执行结果
注释清楚地提供给每一行代码。
String captchaVal = JOptionPane.showInputDialog("Please enter the captcha value:");
JoptionPane
弹出一个标准对话框,showInputDialog
提示用户进行某些输入。 用户输入显示的验证码并单击“确定”后,它将被保存到字符串captchaVal
中。
driver.findElement(By.id("recaptcha_response_field")).sendKeys(captchaVal);
保存在“captchaVal
”中的该值将在 ID 所在的验证码文本框中输入。
在 JUnit 窗格中,绿色条显示测试用例已成功执行。
下图显示了在 Firefox 浏览器中执行的最终输出。
我让你们全都受了验证码! 在另一篇文章中再见。
祝你有美好的一天!
9R WebDriver – 断言和验证
原文: https://javabeginnerstutorial.com/selenium/9r-webdriver-assert-and-verify/
嗨呀超级巨星! 我们已经定位元素很多天了。 让我们今天换一下话题,谈谈“确认和验证”。
要记住
当“条件/检查”的断言或验证失败时,两者之间的主要区别是:
- 断言将使测试失败,并且中止当前测试用例的执行。 跳过该特定代码行之后的所有其他测试步骤
- 验证将记录故障,但继续执行测试用例。
何时使用断言和验证?
最简单的答案是 – 由您决定,换句话说,就是您的愿望! 您可以根据情况使用断言或验证,即是希望测试中止还是在检查失败后继续进行。
使用断言的好处
大多数情况下,我们希望在检查失败时停止测试执行,而这正是我们通过断言得到的结果。 测试用例失败,并且清楚地突出显示为“失败”。 这将立即向我们显示哪些测试用例没有通过完整测试套件中的检查。 然后,我们可以直接转到那些失败的案例,并检查检查/条件未通过的原因。 那不是很方便吗? 由于此立即反馈的可用性,因此断言更为常用。
使用断言的缺点
当第一个断言条件失败时,将不再执行以下代码行。 可能还要执行其他检查,我们永远不会知道他们的结果。
使用验证的优势
即使条件之一失败,我们希望继续执行测试时,通常使用此方法。 故障将被记录或打印到控制台。 因此,无论测试是通过还是失败,我们都会获得测试用例中所有检查的结果。
使用验证的缺点
验证不提供立即反馈,因为条件失败后不会终止测试用例的执行。 因此,每次执行测试时,我们都必须花费大量时间在控制台中查看日志或打印的语句,以确定哪些检查失败。 例如,如果要针对不同的数据集多次执行数百个测试用例,则可能不可行。
示例场景
让我们获得为本教程系列创建的示例网页的标题。 这将是我们使用WebDriver
的getTitle()
方法获得的实际标题。 预期标题是“WebDriver 演示网站”。
情况 1:通过assertEquals
通过测试用例
实际标题与预期标题相同,因此条件Assert.assertEquals("WebDriver Demo Website", pageTitle);
的输出将是成功。 将执行此行之后的代码,并且将它传递给测试用例。
情况 2:使用assertNotEquals
使测试用例失败
实际标题与预期标题相同,因此条件Assert.assertNotEquals("WebDriver Demo Website", pageTitle);
的输出将是故障。 此行之后的代码将不执行。 测试执行被中止,并且测试用例将失败。
代码段
// Making the test fail
Assert.assertNotEquals("WebDriver Demo Website", pageTitle);
// Following lines will not be executed as above assert condition fails
System.out.println("Assert not equals failed");
上图中的控制台显示assertEquals
条件成功,因此将打印检查后的语句,“断言等于通过”,而assertNotEquals
条件失败,因此将不执行此检查之后的行。 打印语句“断言不等于失败”不会打印到控制台。
情况 3:尽管assertNotEquals
条件失败,但通过测试用例
要仅验证实际值和预期值是否不相等,请使用try-catch
块。
代码块
//Verify title not equal using try-catch block
try {
// Making the test fail
Assert.assertNotEquals("WebDriver Demo Website", pageTitle);
} catch(Error e){
// Following lines will be printed when the assert condition fails
System.out.println("Assert not equals failed. But test execution is not aborted.");
System.out.println("Error message: " + e.toString());
}
即使assertNotEquals
条件失败,catch
块中的语句也将被执行,并且错误消息将被打印到控制台。
如图所示,测试用例执行成功,并且错误被打印到控制台。
完整的代码
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
public class AssertAndVerify {
// Declaring variables
private WebDriver driver;
private String baseUrl;
@Before
public void setUp() throws Exception {
// Selenium version 3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver", "E:\\Softwares\\"
+ "Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe");
// Create a new instance for the class FirefoxDriver
// that implements WebDriver interface
driver = new FirefoxDriver();
// Implicit wait for 5 seconds
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// Assign the URL to be invoked to a String variable
baseUrl = "https://chandanachaitanya.github.io/selenium-practice-site/";
}
@Test
public void testPageTitle() throws Exception {
// Open baseUrl in Firefox browser window
driver.get(baseUrl);
// Get the page title
String pageTitle = driver.getTitle();
// Print the title to console
System.out.println("The actual title is: " + pageTitle);
// Check if actual and expected values are equal
Assert.assertEquals("WebDriver Demo Website", pageTitle);
// Printing success message
System.out.println("Assert equals passed.");
// Making the test fail
//Assert.assertNotEquals("WebDriver Demo Website", pageTitle);
// Following lines will not be executed as above assert condition fails
//System.out.println("Assert not equals failed");
//Verify title not equal using try-catch block
try {
// Making the test fail
Assert.assertNotEquals("WebDriver Demo Website", pageTitle);
} catch(Error e){
// Following lines will be printed when the assert condition fails
System.out.println("Assert not equals failed. But test execution is not aborted.");
System.out.println("Error message: " + e.toString());
}
} // End of @Test
@After
public void tearDown() throws Exception {
// Close the Firefox browser
driver.close();
}
}
所有代码文件都放置在 GitHub 仓库中,以方便访问。 您可以为仓库加注星标和分支以方便使用。 请仔细阅读“README.md
”文件以获取明确说明。
总结了断言和验证的这一部分。 祝你有美好的一天!
9S WebDriver – 处理文本框和图像
原文: https://javabeginnerstutorial.com/selenium/9s-webdriver-handling-text-boxes-images/
嗨呀测试人员! 如果您一直在关注我的帖子,那么您应该已经是使用 Selenium WebDriver 输入文本的专家。 如果不是(带着悲伤的表情),那就不用担心了! 我把你们都覆盖了。
处理文本框
您可能正在向两种类型的输入文本框输入文本。
- 第一种类型是您在每个用于输入所要询问信息的网页中看到的普通文本框。 它按原样显示输入的值。
- 第二种类型是密码文本框,用户在其中键入一个值,出于安全原因,它显示为点或星号或任何其他特殊字符。
好消息是,对于这两种文本框类型,都可以使用相同的命令输入所需的值。
示例:让我们在“名字”中输入“testFirst
”,在“创建密码”字段中输入“test1234!
。
说明:通过 id 找到所需的元素,然后使用sendKeys("value")
方法输入相应的值。
考虑以下代码,
<input value="" name="FirstName" id="FirstName" spellcheck="false" type="text">
<input name="Passwd" id="Passwd" type="password">
我们可以看到,input
标签具有“id
”属性,名称和密码字段的属性分别为“FirstName
”和“Passwd
”。 使用这些 ID 定位这些网络元素后,我们将分别传递“testFirst
”和“test1234!
”作为它们的值。
代码:
driver.findElement(By.id("FirstName")).sendKeys("testFirst");
driver.findElement(By.id("Passwd")).sendKeys("test1234!");
要清除文本框中可能存在的任何文本,请使用clear()
方法。 它不需要任何参数。
输出如下
点击图片
可以通过多种方式选择图像。 通过 ID,名称,className
,cssSelector
,xpath 等。 我希望这样做是因为定位图像有时会非常棘手。
请使用 WebDriver 中提供的使用各种策略定位元素的定位元素的先前帖子,以防您错过它们。
让我们来看一个示例场景,为什么这个过程很棘手。
示例:导航至 www.amazon.com ,然后单击主页上显示的“kindle”图像(与创建该帖子的日期相同)。 单击图像后,我们应该导航到相应页面。
右键点击“Kindle”图片和检查元素。 相应的 HTML 代码是
<a class="a-link-normal" href="/b/ref=br_pdt_mgUpt?_encoding=UTF8&node=6669702011&pf_rd_m=ATVPDKIKX0DER&pf_rd_s=&pf_rd_r=5VQBXJP2N17GX77H0TN9&pf_rd_t=36701&pf_rd_p=9c7b479f-fe0c-48f3-a1ab-c19df6492672&pf_rd_i=desktop">
<img alt="Kindle"
src="https://images-na.ssl-images-amazon.com/images/G/01/gateway/yiyiz/Kindle._CB300901238_.png" width="292px" height="292px">
</a>
说明:如您所见,“img
”标签既没有提供“id
”属性,也没有提供“class
”属性。 这样就很难找到一个小小的中间! 对于您在网络上找到的大多数图像,情况就是如此。
考虑到 HTML 代码,可以使用cssSelector
定位策略。 通过这种策略定位图像的方法有两种,
一个。 使用“img
”标签和“src
”属性
driver.findElement(By.cssSelector("img[src='https://images-na.ssl-images-amazon.com/images/G/01/gateway/yiyiz/Kindle._CB300901238_.png']"));
b。 使用“a
”标签,使用“.
”符号访问其类名。 在空格处指定其子元素,“img
”标签和“src
”属性
driver.findElement(By.cssSelector("a.a-link-normal img[src='https://images-na.ssl-images-amazon.com/images/G/01/gateway/yiyiz/Kindle._CB300901238_.png']"));
也可以使用 Xpath 定位策略。 通过这种策略定位图像的方法有两种,
一个。 将“a
”标签与“class
”属性一起使用,从而找到其子元素“img
”
driver.findElement(By.xpath("//a[@class='a-link-normal']/img"));
b。 直接将img
标签及其src
属性使用
driver.findElement(By.xpath("//img[@src='https://images-na.ssl-images-amazon.com/images/G/01/gateway/yiyiz/Kindle._CB300901238_.png']"));
正在考虑的场景,
- 打开 Firefox 浏览器
- 导航至 https://amazon.com
- 将页面标题打印到控制台
- 找到主页上显示的“Kindle”图片
- 点击图片
- 再次将导航的页面标题打印到控制台
- 在 Eclipse IDE 中验证 JUnit 窗口是否成功
此方案的 JUnit 代码是,
package com.blog.junitTests;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
public class Click_Image {
//Declaring variables
private WebDriver driver;
private String baseUrl;
@Before
public void setUp() throws Exception{
// Selenium v3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver", "E:\\Softwares\\"
+ "Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe");
// Create a new instance for the class FirefoxDriver
// that implements WebDriver interface
driver = new FirefoxDriver();
// Implicit wait for 5 seconds
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// Assign the URL to be invoked to a String variable
baseUrl = "https://www.amazon.com/";
}
@Test
public void testPageTitle() throws Exception{
// Open baseUrl in Firefox browser window
driver.get(baseUrl);
// Printing page title to console
System.out.println(driver.getTitle());
// Locate an image with xpath
WebElement kindle = driver.findElement(By.cssSelector("img[src='https://images-na.ssl-images-amazon.com/images/G/01/gateway/yiyiz/Kindle._CB300901238_.png']"));
// Click the image
kindle.click();
// Printing page title after clicking the image
System.out.println("Navigated page title: "+driver.getTitle());
}
@After
public void tearDown() throws Exception{
// Close the Firefox browser
driver.close();
}
}
执行结果:
代码是不言自明的,因为每行都有注释。
在 JUnit 窗口中,绿色条显示测试用例已成功执行。 控制台窗口显示没有任何错误以及单击图像前后的页面标题。
下图显示了成功执行测试脚本后获得的 Firefox 输出。
现在该轮到您尝试这些新学到的技能了。
有什么问题吗?在评论部分开火!
在那之前,请在另一篇文章中见。 祝你有美好的一天!
9T WebDriver – 处理单选按钮和复选框
原文: https://javabeginnerstutorial.com/selenium/9t-webdriver-handling-radio-buttons-checkboxes/
嗨呀超级巨星! 我知道您现在是处理文本框和图像的专家,但是还有很多东西要学习。 让我们成为铁杆! 让我们了解如何处理单选按钮,复选框,下拉菜单,表格,警报等,但我们一次只能看到一个。 在本文中,我们将专注于处理单选按钮和复选框。 最后,WebDriver 将使您表现出前所未有的表现力。
准备开始
单选按钮:在一组单选按钮中,一次只能选择一个。 在 HTML 中,它们使用<input>
标签表示,并带有type
属性,称为“radio
。
复选框:在一组复选框中,每个复选框都单独运行,用户可以根据需要选择多个复选框。 在 HTML 中,使用<input>
标签并将type
属性称为“checkbox
”来表示复选框。
现在我们对单选按钮和复选框的工作方式有了一些清晰的了解,让我们使用 Selenium WebDriver 进行一些有趣的操作。
- 使用各种定位策略定位单选按钮和复选框
- 检查是否显示单选按钮/复选框【提示:
isDisplayed()
方法】 - 检查单选按钮/复选框是否已启用,以便可以选择它【提示:
isEnabled()
方法】 - 检查默认情况下是否选中单选按钮/复选框【提示:
isSelected()
方法】 - 选择/单击单选按钮或复选框【提示:
click()
】
这五个操作将与演示站点中的示例一起进行说明。 它没有世界上最好的 UI,但目前已达到目的。
1.使用各种定位策略定位单选按钮和复选框
是时候再利用定位策略的功能了!
示例:让我们按 ID 定位“Bicycle
”复选框,按名称定位“Tricycle
”复选框,按 xPath 定位“Sedan
”复选框,并按“Magazines
”单选按钮定位cssSelector
。
右键单击所需元素,然后选择检查元素,将给出相应的 HTML 代码,如下所示,
<input id="bicycle-checkbox" type="checkbox" name="vehicle1" value="Bicycle"> Bicycle
<input id="tricycle-checkbox" type="checkbox" name="vehicle2" value="Tricycle" disabled> Tricycle
<input id="sedan-checkbox" type="checkbox" name="vehicle5" value="Sedan"> Sedan
<input id="magazines-radio-btn" type="radio" name="books" value="Magazines"> Magazines
代码:
driver.findElement(By.id("bicycle-checkbox"));
driver.findElement(By.name("vehicle2"));
driver.findElement(By.xpath("//input[@name='vehicle5']"));
driver.findElement(By.cssSelector("input[value='Magazines']"));
2. 检查单选按钮/复选框是否显示
使用 Selenium WebDriver,检查特定的单选按钮或复选框是否按预期显示在网页上是小菜一碟! 我们有isDisplayed()
方法来进行救援。 此方法返回一个布尔值(true
– 元素显示,false
– 元素不显示)。
示例:让我们检查网页上是否显示了“Tricycle
”复选框。
各自的 HTML 代码供参考,
<input id="tricycle-checkbox" type="checkbox" name="vehicle2" value="Tricycle" disabled> Tricycle
代码:
driver.findElement(By.name("vehicle2")).isDisplayed();
3. 检查单选按钮/复选框是否已启用,以便可以选择它
同样,为了验证是否启用了特定的单选按钮或复选框,我们有isEnabled()
方法。 这还会返回一个布尔值( true
– 元素已启用,false
– 元素已禁用)。
示例:让我们检查是否启用了“Tricycle”复选框。 如果是的话,让我们检查一下,否则我们只会在控制台上打印一条消息。
Respective HTML code for reference,
<input id="tricycle-checkbox" type="checkbox" name="vehicle2" value="Tricycle" disabled> Tricycle
代码:
WebElement tricycleCheckbox = driver.findElement(By.name("vehicle2"));
// Check if tricyle is enabled to select
if (tricycleCheckbox.isEnabled()) {
// Click if enabled
tricycleCheckbox.click();
} else {
// Print message to console if disabled
System.out.println("Unable to select 'Tricycle' checkbox as it is disabled.");
}
4. 检查默认情况下是否选中单选按钮/复选框
为了验证默认情况下是否选中了特定的单选按钮或复选框,我们有isSelected()
方法。 这还会返回一个布尔值(true
– 元素已选择,false
– 未选择元素)。
示例:让我们检查一下默认情况下是否选中了“杂志”单选按钮。 如果是,请让我们将该消息打印到控制台,否则我们将单击它以选择“杂志”选项。
Respective HTML code for reference,
<input id="magazines-radio-btn" type="radio" name="books" value="Magazines"> Magazines
代码:
// Locate 'Magazines' radio button using cssSelector
WebElement magazinesRadioBtn = driver.findElement(By
.cssSelector("input[value='Magazines']"));
// Check if radio button is selected by default
if (magazinesRadioBtn.isSelected()) {
// Print message to console
System.out.println("Magazines radio button is selected by default");
} else {
// Click the radio button
magazinesRadioBtn.click();
}
5. 选择/单击单选按钮或复选框
您为什么认为 Selenium WebDriver 中有click()
? 你懂我! 选中单选按钮非常容易,对于复选框,可以使用相同的click()
在选中和取消选中之间切换!
示例:让我们选中并取消选中“轿车”复选框。
Respective HTML code for reference,
<input id="sedan-checkbox" type="checkbox" name="vehicle5" value="Sedan"> Sedan
代码:
// Locate 'Sedan' checkbox using xPath: contains() and text()
WebElement sedanCheckbox = driver.findElement(By.xpath("//input[@name='vehicle5']"));
for (int i = 0; i < 2; i++) {
// Click the checkbox
sedanCheckbox.click();
// Print current status to console
System.out.println("Selection status of 'Sedan' checkbox: "
+ sedanCheckbox.isSelected());
}
此代码将在第一个迭代中选中“轿车”复选框,然后在第二个迭代中取消选中它。 选择状态消息将相应地打印到控制台。
概览
让我们来看一个测试案例,该案例实现了迄今为止本文中涵盖的所有操作,
场景
- 打开 Firefox 浏览器。
- 导航到演示站点
- 按名称找到“三轮车”复选框
- 检查是否显示“三轮车”复选框,并将相应消息打印到控制台
- 检查“三轮车”复选框是否已启用
- 如果是,请单击复选框,如果否,则将相应消息打印到控制台
- 根据“货车”和“SUV”复选框的当前选择状态,选中或取消选中并在执行
click()
动作之前和之后打印状态 - 使用 XPath 找到“轿车”复选框
- 使用两次迭代在选择状态和取消选择状态之间切换
- 使用
cssSelector
找到“杂志”单选按钮 - 检查是否默认选中
- 如果是,则将相应消息打印到控制台,否则,选择单选按钮
- 验证 Eclipse IDE 控制台输出屏幕和 JUnit 窗格是否成功
此方案的 JUnit 代码是,
package com.blog.junitTests;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
public class RadioBtns_Checkboxes {
// Declaring variables
private WebDriver driver;
private String baseUrl;
@Before
public void setUp() throws Exception {
// Selenium version 3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver", "E:\\Softwares\\
Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe");
// Create a new instance for the class FirefoxDriver
// that implements WebDriver interface
driver = new FirefoxDriver();
// Implicit wait for 5 seconds
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// Assign the URL to be invoked to a String variable
baseUrl = "https://chandanachaitanya.github.io/selenium-practice-site/";
}
@Test
public void testPageTitle() throws Exception {
// Open baseUrl in Firefox browser window
driver.get(baseUrl);
// Locate 'Tricycle' checkbox using name
WebElement tricycleCheckbox = driver.findElement(By.name("vehicle2"));
// Check if tricyle is displayed
System.out.println("Is tricycle displayed? "+ tricycleCheckbox.isDisplayed());
// Check if tricyle is enabled to select
if (tricycleCheckbox.isEnabled()) {
// Click if enabled
tricycleCheckbox.click();
} else {
// Print message to console if disabled
System.out.println("Unable to select 'Tricycle' checkbox as it is disabled.");
}
//Get all checkbox elements in a list
List<WebElement> list = driver.findElements(By
.cssSelector("input[type='checkbox']"));
// Loops through all checkboxe elements
for (int i = 0; i < list.size(); i++) {
// Checking if the checkbox is a 'Van' or 'SUV'
if ((list.get(i).getAttribute("value").trim()
.equalsIgnoreCase("van"))
|| (list.get(i).getAttribute("value").trim()
.equalsIgnoreCase("suv"))) {
// Print selection status to console
System.out.println("BEFORE: Is "
+ list.get(i).getAttribute("value") + " selected? "
+ list.get(i).isSelected());
// Check if the checkbox is selected
if (!(list.get(i).isSelected())) {
// Click the checkbox
list.get(i).click();
System.out.println("AFTER: Is "
+ list.get(i).getAttribute("value") + " selected? "
+ list.get(i).isSelected());
} else {
// Uncheck the checkbox
list.get(i).click();
System.out.println("AFTER: Is "
+ list.get(i).getAttribute("value") + " selected? "
+ list.get(i).isSelected());
}
System.out.println("Next...");
}
}
// Locate 'Sedan' checkbox using xPath
WebElement sedanCheckbox = driver.findElement(By
.xpath("//input[@name='vehicle5']"));
System.out.println("Trying to select and de-select Sedan checkbox...");
for (int i = 0; i < 2; i++) {
// Click the checkbox
sedanCheckbox.click();
// Print current status to console
System.out.println("Selection status of 'Sedan' checkbox: "
+ sedanCheckbox.isSelected());
}
// Locate 'Magazines' radio button using cssSelector
WebElement magazinesRadioBtn = driver.findElement(By
.cssSelector("input[value='Magazines']"));
// Check if radio button is selected by default
if (magazinesRadioBtn.isSelected()) {
// Print message to console
System.out.println("Magazines radio button is selected by default");
} else {
// Click the radio button
magazinesRadioBtn.click();
}
} //End of @Test
@After
public void tearDown() throws Exception {
// Close the Firefox browser
driver.close();
}
}
执行结果:
除一个片段外,部分代码将作为本文讨论的每种技术的一部分进行解释。
所有类型为复选框的 Web 元素均在列表中获得,
//Get all checkbox elements in a list
List<WebElement> list = driver.findElements(By.cssSelector("input[type='checkbox']"));
如果列表中的所有复选框元素的属性值均与“范”和“SUV”相匹配,则将对其进行验证。 他们的当前选择状态被打印到控制台作为一条消息,提示“之前”。
// Loops through all checkboxe elements
for (int i = 0; i < list.size(); i++) {
// Checking if the checkbox is a 'Van' or 'SUV'
if ((list.get(i).getAttribute("value").trim().equalsIgnoreCase("van"))
||(list.get(i).getAttribute("value").trim().equalsIgnoreCase("suv")))
{
// Print selection status to console
System.out.println("BEFORE: Is "
+ list.get(i).getAttribute("value") + " selected? "
+ list.get(i).isSelected());
使用isSelected()
方法,检查是否已选中“货车”或“SUV”复选框。 如果未选中复选框,则将其选中,然后将其选择状态打印到控制台,显示“之后”。 如果已经选择了它们,然后再次单击以取消选择它们并将相应的消息打印到控制台。
// Check if the checkbox is selected
if (!(list.get(i).isSelected())) {
// Click the checkbox list.get(i).click();
System.out.println("AFTER: Is "+ list.get(i).getAttribute("value") + " selected? "+ list.get(i).isSelected());
} else {
// Uncheck the checkbox
list.get(i).click();
System.out.println("AFTER: Is "+ list.get(i).getAttribute("value") + " selected? "+ list.get(i).isSelected());
}
System.out.println("Next...");
}
}
在 JUnit 窗口中,绿色条显示测试用例已成功执行。 控制台窗口显示没有任何错误。 它还按预期显示所有打印的消息。
下图显示了成功执行测试脚本后获得的 Firefox 输出。
是时候尝试今天的技能了。 是的,戴上安全帽,以免遇到任何异常!
再见。 祝你有美好的一天!
9U WebDriver – 通过两种方式选择项目(下拉菜单和多项选择)
原文: https://javabeginnerstutorial.com/selenium/9u-webdriver-select-items-two-ways/
嗨呀摇滚明星(虽然不包括吉他和音乐)! 在本文中,我们将深入研究下拉菜单以及如何以两种方式选择项目。 是的,我们还讨论了多个选择! 所有概念都将与演示站点中的示例一起进行说明。
方法 1:
使用可用定位策略之一使用相同年代的传统元素定位方法。 首先,我们找到下拉列表元素,然后找到需要选择的项目。 由于选项没有唯一的标识符,因此这并不困难。
示例:让我们通过 ID 查找“编程语言”下拉列表。 要从可用的下拉菜单中找到“C++”,我们将必须找到标记名称为“options
”的所有元素,并将其放在列表中。 遍历列表,或者如果知道其索引,则相应地检索元素并执行单击操作。
右键单击所需元素,然后选择检查元素,将给出相应的 HTML 代码,如下所示,
<select id="programming-languages" class="input-xlarge" name="languages">
<option value="Java">Java</option>
<option value="C++">C++</option>
代码:
// Locate 'Programming Languages' dropdown using id
WebElement progLanguages = driver.findElement(By.id("programming-languages"));
//Get all options in a list
List<WebElement> options = progLanguages.findElements(By.tagName("option"));
//Iterate thorough options
for (WebElement option : options) {
if("C++".equals(option.getText()))
option.click();
}
(或者)
WebElement progLanguages = driver.findElement(By.id("programming-languages"));
List<WebElement> options = progLanguages.findElements(By.tagName("option"));
//Selecting “C++” based on its index
options.get(1).click();
0、1、2、3 是下拉列表的索引值。
方法 2:使用Select
类。
让我们深入研究并了解Select
类的工作原理,并看一下它是否具有魔力。 以及为什么您首先要使用它?
使用方法 1 具有主要缺点。 由于没有唯一标识符,因此选择特定选项变得非常困难。 另外,要选择多个选项,请取消选择特定的项目,这会变得很复杂。 Selenium Webdriver 再次为我们解救! 它为Select
类提供了预定义的方法,专门用于处理下拉菜单和多选方案。
步骤 1:该选择类在“org.openqa.selenium.support.ui.Select
”包中可用。 因此,必须将此包导入我们的测试脚本中。
import org.openqa.selenium.support.ui.Select;
步骤 2:通过传递所需的下拉标识符为Select
类创建一个实例。
Select languages = new Select(driver.findElement(By.id("element_ID")));
可以使用前面讨论的任何定位策略(https://javabeginnerstutorial.com/selenium/9j-webdriver-locating-elements-1/)来定位下拉列表。
步骤 3:找到下拉 Web 元素并创建Select
类的对象后,就可以访问其所有方法来执行选择和取消选择。
注意:
Select
类仅适用于具有<select>
标签的 Web 元素。
下面是Select
类中所有可用方法的快照。
这里有很多要讨论和讨论的要点。 让我们通过示例和代码片段介绍一些常用的方法。
注意:
如果找不到匹配的选项,则所有select
和deselect
方法可能会碰到NoSuchElementException
。
1. selectByVisibleText(String arg0)
和deselectByVisibleText(String arg0)
这些方法选择和取消选择一个显示的文本与传递的String
参数完全匹配的选项。
语法:selectObject.selectByVisibleText("displayed_option_text")
selectObject.deselectByVisibleText("displayed_option_text")
示例:让我们从“编程语言”下拉菜单中选择“JavaScript”。
代码:(选择)
// Locate 'Programming Languages' dropdown using id
WebElement languagesDD = driver.findElement(By.id("programming-languages"));
// Create an instance of 'Select' class by passing the dropdown web element
Select languages = new Select(languagesDD);
// Select 'JavaScript' language by its visible text
languages.selectByVisibleText("JavaScript");
(取消选择)
languages.deselectByVisibleText("JavaScript");
2. selectByValue(String arg0)
和deselectByValue(String arg0)
这些方法选择和取消选择其value
属性与传递的String
参数匹配的选项。
语法:selectObject.selectByValue("value_attribute_text")
selectObject.deselectByValue("value_attribute_text")
示例:让我们从 Selenium 工具套件多选选项中选择“Selenium RC”。
右键单击所需的 Web 元素,然后选择检视元素,将给出相应的 HTML 代码,如下所示,
<select id="selenium_suite" multiple="multiple" name="selenium_suite">
<option value="IDE">Selenium IDE</option>
<option value="WebDriver">Selenium WebDriver</option>
<option value="RC">Selenium RC</option>
value
属性的值“RC
”将作为参数传递给selectByValue
方法。
代码:
multiSelect.selectByValue("RC");
3. selectByIndex(int arg0)
和deselectByIndex(int arg0)
这些方法选择和取消选择指定索引值处的选项。 重要的是要注意,索引值始终以零开头。
语法:selectObject.selectByIndex("option_index");
selectObject.deselectByIndex("option_index");
示例:让我们从 Selenium 工具腰间多选选项中取消选择“Selenium RC”。
基于 HTML 代码,Selenium IDE 的索引为 0,Selenium WebDriver 的索引为 1,Selenium RC 的索引为 2。
代码:
multiSelect.deselectByIndex(2);
4. deselectAll()
此方法清除选择。 没有传递参数。
注意:
此方法仅适用于多选方案,即<select>
标签应具有值为“multiple
”的multi
属性。 否则抛出NotImplementedError
。
语法:selectObject.deselectAll()
示例:让我们取消选择在 Selenium 工具套件多重选择框中选择的所有选项。
代码:
multiSelect.deselectAll();
5. isMultiple()
当您想知道 Web 元素是否允许多选时,此方法很方便。 它返回true
或false
。 没有传递参数。
语法:selectObject.isMultiple()
6. getOptions()
此方法作为 Web 元素返回在多重选择框中可用的所有选项的列表。
语法:selectObject.getOptions()
代码:
List<WebElement> allOptions = multiSelect.getOptions();
7. getFirstSelectedOption()
此方法在多重选择框中返回第一个选定的选项。 如果在仅允许单个选择的下拉菜单中使用此选项,则将所选选项作为 Web 元素返回。
语法:selectObject.getFirstSelectedOption().getText()
这将返回所选的选项文本。
8. getAllSelectedOptions()
此方法返回作为多选框的一部分而被选择为 Web 元素的所有选项的列表。
语法:selectObject.getAllSelectedOptions()
代码:
List<WebElement> allSelectedOptions = multiSelect.getAllSelectedOptions();
概览
让我们来看一个测试案例,该案例实现了迄今为止本文中涵盖的所有操作,
场景
- 打开 Firefox 浏览器
- 导航到演示站点
- 使用 ID 找到“编程语言”下拉菜单
- 为
Select
类创建一个对象 - 通过可见的文本选择“JavaScript”语言
- 将选定的选项打印到控制台
- 检查“编程语言”下拉列表是否支持多项选择并将相应消息打印到控制台
- 使用名称找到“Selenium Tool Suite”多选框
- 使用多选 Web 元素创建
Select
类的实例 - 根据其值选择“Selenium RC”和“优势”
- 通过索引取消选择“Selenium RC”
- 通过可见的文本选择“Selenium WebDriver”
- 在列表中获取所有选定的选项,并将它们打印到控制台
- 验证 Eclipse IDE 控制台输出屏幕和 JUnit 窗格是否成功
此方案的 JUnit 代码是,
package com.blog.junitTests;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.Select;
public class SelectItems {
// Declaring variables
private WebDriver driver;
private String baseUrl;
@Before
public void setUp() throws Exception {
// Selenium version 3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver", "E:\\ Softwares\\"
+ "Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe");
// Create a new instance for the class FirefoxDriver
// that implements WebDriver interface
driver = new FirefoxDriver();
// Implicit wait for 5 seconds
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// Assign the URL to be invoked to a String variable
// baseUrl = "https://chandanachaitanya.github.io/selenium-practice-site/";
baseUrl = "file:///E:/Chandu/My%20Blog/githubDemoSite/index.html";
}
@Test
public void testPageTitle() throws Exception {
// Open baseUrl in Firefox browser window
driver.get(baseUrl);
// Locate 'Programming Languages' dropdown using id
WebElement languagesDD = driver.findElement(By.id("programming-languages"));
// Create an instance of 'Select' class by passing the dropdown web element
Select languages = new Select(languagesDD);
// Select 'JavaScript' language by its visible text
languages.selectByVisibleText("JavaScript");
// Prints the selected option to console
System.out.println("Selected programming language is: " + languages.getFirstSelectedOption().getText());
// Check if languages dropdown supports multiple selection and print the result to console
if(languages.isMultiple()){
System.out.println("Languages dropdown supports multiple selection");
}else {
System.out.println("Languages dropdown does not support multiple selection");
}
// Locate multi select element using name
WebElement toolSuite = driver.findElement(By.name("selenium_suite"));
// Create an instance of 'Select' class by passing the multi select element
Select multiSelect = new Select(toolSuite);
// Select 'Selenium RC' by its value
multiSelect.selectByValue("RC");
// Select 'Advantages' by its value
multiSelect.selectByValue("Adv");
// De-select 'Selenium RC' by its index
multiSelect.deselectByIndex(2);
// Select "Selenium WebDriver" by its visible text
multiSelect.selectByVisibleText("Selenium WebDriver");
// Get all selected options in a list
List<WebElement> suiteItems = multiSelect.getAllSelectedOptions();
// Prints all selected options
System.out.println("Options selected under 'Selenium Tool Suite' are:");
for (WebElement option : suiteItems) {
System.out.println(option.getText());
}
} // End of @Test
@After
public void tearDown() throws Exception {
// Close the Firefox browser
driver.close();
}
}
执行结果:
每行代码都带有不言自明的注释,并且部分代码作为帖子中涉及的每个概念的一部分进行了说明。
在 JUnit 窗口中,绿色条显示测试用例已成功执行。 控制台窗口显示没有任何错误。 它还按预期显示所有打印的消息。
下图显示了成功执行测试脚本后获得的 Firefox 输出。
这个概念并不难缠您的头。 因此,开始大量练习。 我很快会在另一篇文章中再见。 祝你有美好的一天!
9V WebDriver – 以两种方式处理表
原文: https://javabeginnerstutorial.com/selenium/9v-webdriver-handling-tables-two-ways/
嗨呀冠军! 欢迎回到关于使用 Selenium WebDriver 处理 Web 元素的另一篇文章。 您每天都会学到一些新知识,而这就是我们今天将要学习的内容:使用和不使用 XPath 处理表! 让我们回到我们的演示站点以更好地理解这个概念。
餐桌就像到处都是名人。 好吧,是时候该去见我们的明星了! 在整个这篇文章中,我们将使用演示站点中的“实践表”表。 在很多情况下,您可能需要检查表的特定单元格中是否存在特定数据。 因此,让我们继续前进,击中头部的粗体!
方法 1:不使用 XPath 处理表
在“练习表”中,我们有一个具有两行两列的表。 我们在第一行第二列中嵌套了另一个表。
例如:
让我们尝试访问嵌套表的第一行,并将文本“Inner Row 1
”打印到控制台。 由于我们不在这里使用 XPath,因此它将变得有些复杂。 但是一旦您掌握了如何以一个嵌套级别定位一个单元,然后访问更深层次的单元就轻松了! 第一步是使用标签名称“table
”查找主表。 可以使用任何定位策略,但通常表的行和列没有 ID 或名称。 因此,访问它们的最佳方法是使用它们相应的标签名称。 第二步是使用获得的 Web 元素(表),找到嵌套表。 最后一步是,使用嵌套表 web 元素,找到第一行并获取其文本!
右键单击所需元素,然后选择检查元素,将给出相应的 HTML 代码段,如下所示,
<table class="table table-bordered">
<tr>
<td>Row 1</td>
<td>
<table class="table table-bordered">
<tr>
<td>Inner Row 1</td>
</tr>
<tr>
<td>Inner Row 2</td>
</tr>
</table>
代码:
// Locate 'Table For Practice' using tagName
WebElement practiceTable = driver.findElement(By.tagName("table"));
// Locate Inner table using tagName
WebElement innerTable = practiceTable.findElement(By.tagName("table"));
// Locate 'Inner Row 1' using tagName
WebElement innerRow1 = innerTable.findElement(By.tagName("td"));
// Print the first row text to console
System.out.println("Inner table first row text = " + innerRow1.getText());
方法 2:使用 XPath 处理表
第一种方法需要使用三个 Web 元素来访问所需的单元格。 如果涉及许多嵌套级别,则用于定位元素的命令数量将增加。 为了减轻这种情况,我们有 XPath 可以拯救我们! “XPath”一词会响起吗? 如果不是这样,请参考 XPath 的定位元素以简单的方式构造它。
例如:
让我们找到嵌套表的第二行,并将其内容打印到控制台。 相对路径是 XPath 的一部分,用于定位嵌套表的所需单元格。
代码:
// Locate 'Inner Row 2' using xPath
WebElement innerRow2 = driver.findElement(By.xpath("//table/tbody/tr[1]/td[2]/table/tbody/tr[2]/td[1]"));
概览
让我们一步一步地查看上面讨论的方法的代码!
场景
- 打开 Firefox 浏览器
- 导航到演示站点( https://chandanachaitanya.github.io/selenium-practice-site/ )
- 使用标签名称找到“练习表格”
- 使用标签名称找到嵌套表
- 使用标签名称找到嵌套表的第一行
- 打印文本“
Inner Row 1
”进行控制台 - 使用 XPath 找到嵌套表的第二行
- 打印文本“
Inner Row 2
”进行控制台
此方案的 JUnit 代码是,
package com.blog.junitTests;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
public class HandlingTables {
// Declaring variables
private WebDriver driver;
private String baseUrl;
@Before
public void setUp() throws Exception {
// Selenium version 3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver", "E:\\Softwares\\Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe");
// Create a new instance for the class FirefoxDriver
// that implements WebDriver interface
driver = new FirefoxDriver();
// Implicit wait for 5 seconds
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// Assign the URL to be invoked to a String variable
baseUrl = "https://chandanachaitanya.github.io/selenium-practice-site/";
}
@Test
public void testPageTitle() throws Exception {
// Open baseUrl in Firefox browser window
driver.get(baseUrl);
// Locate 'Table For Practice' using tagName
WebElement practiceTable = driver.findElement(By.tagName("table"));
// Locate Inner table using tagName
WebElement innerTable = practiceTable.findElement(By.tagName("table"));
// Locate 'Inner Row 1' using tagName
WebElement innerRow1 = innerTable.findElement(By.tagName("td"));
// Print the first row text to console
System.out.println("Inner table first row text = " + innerRow1.getText());
// Locate 'Inner Row 2' using xPath
WebElement innerRow2 = driver.findElement(By.xpath("//table/tbody/tr[1]/td[2]/table/tbody/tr[2]/td[1]"));
System.out.println("Inner table second row text = " + innerRow2.getText());
} //End of @Test
@After
public void tearDown() throws Exception {
// Close the Firefox browser
driver.close();
}
}
执行结果:
每行代码都带有不言自明的注释,并且作为到目前为止涵盖的概念的一部分,代码得到了很好的解释。
注意到 Eclipse IDE 的 JUnit 视图后,绿色条显示测试用例已成功执行。 控制台窗口显示没有任何错误。 它还按预期显示了两个嵌套表行的文本。
任何问题? 在评论部分开火!
稍后再见。 祝你今天愉快!
9W WebDriver – 遍历表元素
原文: https://javabeginnerstutorial.com/selenium/9w-webdriver-looping-table-elements/
欢迎回来,勇士们! 我们刚刚看到了如何检查使用和不使用 XPath 的表的特定单元格中是否存在特定的数据。 是时候遍历每个元素并可视化 Selenium WebDriver 的功能了。 让我们回到我们的演示站点并关注“书籍&作者”表以了解这一概念。
步骤 1:
使用其 ID “BooksAuthorsTable
”找到“图书&作者”表。 HTML 代码如下:
<table id="BooksAuthorsTable" class="table table-bordered">
代码:
WebElement BooksTable = driver.findElement(By.id("BooksAuthorsTable"));
步骤 2:
使用 XPath 计算行和列的总数。
让我们使用绝对 XPath 查找总行数。 请注意,XPath 以标签名称“tr
”结尾。 size()
方法给出使用 XPath 由findElements()
返回的元素数。
代码:
int rowNum = driver.findElements(By.xpath("/html/body/form/div[5]/div/div/table/tbody/tr")).size();
让我们稍微切换一下齿轮,并使用相对 XPath 查找总列数。
代码:
int colNum = driver.findElements(By.xpath("//table[@id='BooksAuthorsTable']/tbody/tr[1]/th")).size();
这种查找行和列总数的方法在表的行和列动态变化的情况下很有用。
步骤 3:
遍历表元素
如果表的行和列号固定为,那么遍历每个表元素将变得非常容易。 可以使用两个for()
循环,一个用于行,另一个用于访问列值。
代码:
for(int i=2; i<=6; i++){
for(int j=1; j<=4; j++){
System.out.print(driver.findElement(By.
xpath("//table[@id='BooksAuthorsTable']/tbody/tr[" + i +"]/td[" + j + "]")).getText() + "\t");
}
System.out.println("");
}
有一天,如果幸运的话,您可能会偶然发现一个表,该表在每次页面刷新时都会动态加载,因此行数通常会有所不同。 因此,每执行一次测试便要计算行数。
首先,让我们获取所有标签名称为“”的元素,并将它们放在名为“rowVals
”的列表中。
List<WebElement> rowVals = BooksTable.findElements(By.tagName("tr"));
要从第一行获取标头元素,请找到所有标签名称为“th
”的元素,并将其放在名为“colHeader
”的列表中。
List<WebElement> colHeader = rowVals.get(0).findElements(By.tagName("th"));
要将每个标题文本打印到控制台,请遍历colHeader
列表中的标题值并使用getText()
方法。
for(int i=0; i<colHeader.size(); i++){
System.out.println(colHeader.get(i).getText());
}
要将表内容打印到控制台,请遍历每一行。 对于每一行,遍历每一列,并使用相同的getText()
方法打印值。
for(int i=1; i<rowNum; i++){
List<WebElement> colVals = rowVals.get(i).findElements(By.tagName("td"));
for(int j=0; j<colNum; j++){
System.out.println(colVals.get(j).getText());
}
}
您有能力集中精力下坡吗? 如果是,让我们看看 BrainBell!
BrainBell – 可视化! 我们大多数人都知道,为我们希望记住的概念创建心理图片比单词更清晰,更容易记住。 我们不知道或做的是仔细观察它几秒钟。 这将有助于轻松调用它。
话虽如此,让我们看一下到目前为止讨论的整体情况!
场景
- 打开 Firefox 浏览器
- 导航到演示站点
- 使用 ID 定位“图书&作者”表
- 使用绝对 XPath 获取总行数
- 使用相对 XPath 获取总列数
- 将行数和列数打印到控制台
- 通过标签名称“
tr
”获取所有行值 - 通过标签名称“
th
”获取列表中的列标题值 - 循环浏览标题值并将其打印到控制台
- 遍历表内容(每一行的所有列)并获取其文本
- 将值打印到控制台
- 使用固定的行号和列号将表内容打印到控制台
此方案的 JUnit 代码是,
package com.blog.junitTests;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
public class LoopingThroughTableElements {
// Declaring variables
private WebDriver driver;
private String baseUrl;
@Before
public void setUp() throws Exception {
// Selenium version 3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver", "E:\\Softwares\\"Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe");
// Create a new instance for the class FirefoxDriver
// that implements WebDriver interface
driver = new FirefoxDriver();
// Implicit wait for 5 seconds
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// Assign the URL to be invoked to a String variable
baseUrl = " https://chandanachaitanya.github.io/selenium-practice-site/";
}
@Test
public void testPageTitle() throws Exception {
// Open baseUrl in Firefox browser window
driver.get(baseUrl);
// Locate 'Books & Authors' table using id
WebElement BooksTable = driver.findElement(By.id("BooksAuthorsTable"));
//Get all web elements by tag name 'tr'
List<WebElement> rowVals = BooksTable.findElements(By.tagName("tr"));
//Get number of rows and columns
//using absoulute xpath
int rowNum = driver.findElements(By.xpath("/html/body/form/div[5]/div/div/table/tbody/tr")).size();
//using relative xpath
int colNum = driver.findElements(By.xpath("//table[@id='BooksAuthorsTable']/tbody/tr[1]/th")).size();
System.out.println("Total number of rows = " + rowNum);
System.out.println("Total number of columns = " + colNum);
//Get column header values from first row
List<WebElement> colHeader = rowVals.get(0).findElements(By.tagName("th"));
//Loop through the header values and print them to console
System.out.println("Header values:");
for(int i=0; i<colHeader.size(); i++){
System.out.println(colHeader.get(i).getText());
}
System.out.println("---------------");
//Loop through the remaining rows
for(int i=1; i<rowNum; i++){
//Get each row's column values by tag name
List<WebElement> colVals = rowVals.get(i).findElements(By.tagName("td"));
//Loop through each column
for(int j=0; j<colNum; j++){
//Print the coulumn values to console
System.out.println(colVals.get(j).getText());
}
//Just a separator for each row
System.out.println("---------------");
}
//Printing table contents to console for fixed row and column numbers
for(int i=2; i<=6; i++){
for(int j=1; j<=4; j++){
System.out.print(driver.findElement(By.
xpath("//table[@id='BooksAuthorsTable']/tbody/tr[" + i +"]/td[" + j + "]")).getText() + "\t");
}
System.out.println("");
}
} //End of @Test
@After
public void tearDown() throws Exception {
// Close the Firefox browser
driver.close();
}
}
执行结果:
到目前为止,每行代码都作为概念的一部分进行了很好的解释。
注意到 Eclipse IDE 的 JUnit 视图后,绿色条显示测试用例已成功执行。
控制台窗口显示没有任何错误。 它还按预期显示固定和动态计算的行数和列数的打印表内容。
希望我总是给你一些东西来思考! 稍后再见。
祝你今天愉快!
9X WebDriver – 处理警报/弹出框
原文: https://javabeginnerstutorial.com/selenium/9x-webdriver-handling-alerts-popup-box/
嗨呀爱好者! 想知道为什么 Selenium WebDriver 中存在警报界面? 好吧,我的朋友,对于您所寻找的信息,仅此帖子即可。 我们将讨论基本上属于三种类型的弹出框-警报框,确认框和提示框。 与往常一样,我们有演示站点来说明这些概念。
注意:
如果网页上出现一个弹出框,则只要不接受或关闭警报,用户就无法对基础页面执行任何操作。 请注意,如果您尝试使用警报访问网页上的任何元素,则可能会碰到“UnhandledAlertException
:存在模态对话框”。
1.警报框
一个简单的警报框通常用于将信息传递给用户或发出一些警告。 除非单击“确定”按钮,否则用户无法继续进行操作。
JavaScript 代码:
window.alert("This is a simple alert box! \nYou can't escape from me until you click 'OK'!");
2. 确认框
确认框为用户提供了两个选项,以验证/接受或关闭某些内容。 用户必须单击“确定”或“取消”才能继续。 确认弹出框返回布尔值。 当用户点击“确定”时返回true
,而当点击“取消”时返回false
。
JavaScript code:
window.confirm("Click 'OK' or 'Cancel'.");
3. 提示框
在我们希望用户输入值的情况下,将使用提示框。 与其他警报框类似,用户必须单击“确定”或“取消”按钮才能继续操作。 提示框在单击“确定”时返回输入值,而在单击“取消”时返回空值。
JavaScript 代码:
window.prompt("Which Selenium Tool do you like the most?","e.g. Selenium IDE");
希望您现在对基于 Web 的弹出窗口的不同类型有清楚的了解。
处理弹出框:
Selenium WebDriver 尚未提供任何处理这些弹出框的方法。 你相信我刚才说的吗?
哈! 我知道没有骗你的! WebDriver 总是对我们所有问题都有答案。 警报接口随org.openqa.selenium.Alert
包一起提供!
经常使用的一些最有用的方法是:
1. WebDriver switchTo()
这是用于从主窗口切换到弹出窗口或显示的警报的方法。
driver.switchTo().alert();
2. void accept()
此方法用于接受警报。 点击“确定”按钮。
driver.switchTo().alert().accept();
3. void dismiss()
此方法用于消除警报。 点击“取消”按钮。
driver.switchTo().alert().dismiss();
4. String getText()
要使文本显示在弹出框中,请使用此方法,它将文本作为字符串返回。
driver.switchTo().alert().getText();
5. void sendKeys(String textToSend)
此方法用于在显示的弹出框中(通常是提示框)输入特定的String
。
driver.switchTo().alert().sendKeys(“sampleText”);
场景
让我们来看一个实现这些方法的测试用例,以更深入地了解这些概念,
- 打开 Firefox 浏览器
- 导航到演示站点
- 使用 ID 找到“警告框”按钮
- 单击按钮以弹出警报框
- 获取警报弹出窗口的显示文本并将其打印到控制台
- 接受警报弹出窗口
- 使用 ID 找到“确认框”按钮
- 单击按钮以确认框弹出
- 将警报消息打印到控制台
- 关闭确认弹出窗口
- 使用 XPath 找到“提示框”按钮
- 单击按钮,弹出提示框
- 获取提示框消息并将其显示到控制台
- 接受提示弹出窗口
- 验证 Eclipse IDE 控制台输出屏幕和 JUnit 窗格是否成功
此方案的 JUnit 代码是,
package com.blog.junitTests;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
public class PopupBoxes {
// Declaring variables
private WebDriver driver;
private String baseUrl;
@Before
public void setUp() throws Exception {
// Selenium version 3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver", "E:\\Softwares\\"
+ "Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe");
// Create a new instance for the class FirefoxDriver
// that implements WebDriver interface
driver = new FirefoxDriver();
// Implicit wait for 5 seconds
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// Assign the URL to be invoked to a String variable
baseUrl = "https://chandanachaitanya.github.io/selenium-practice-site/";
}
@Test
public void testPageTitle() throws Exception {
// Open baseUrl in Firefox browser window
driver.get(baseUrl);
// Locate 'Alert Box' button using id
WebElement alertBoxBtn = driver.findElement(By.id("alertBox"));
// Click the button for alert box popup
alertBoxBtn.click();
// Switch the control to 'Alert Box' popup
Alert alertPopUp = driver.switchTo().alert();
// Print the alert message to console
System.out.println("Alert Box message: " + alertPopUp.getText());
// Accept the alert popup
alertPopUp.accept();
Thread.sleep(5000);
// Locate 'Confirm Box' button using id
WebElement confirmBoxBtn = driver.findElement(By.id("confirmBox"));
// Click the button for confirm box popup
confirmBoxBtn.click();
// Switch control to 'Confirm Box' popup
Alert confirmPopUp = driver.switchTo().alert();
// Dismiss the popup
confirmPopUp.dismiss();
System.out.println("Confirm box popup dismissed!");
Thread.sleep(5000);
// Locate 'Prompt Box' button using XPath
WebElement promptBoxBtn = driver.findElement(By.xpath("/html/body/form/div[4]/div[3]/button"));
// Click the button for prompt box popup
promptBoxBtn.click();
// Switch control to 'Prompt Box' popup
Alert promptPopUp = driver.switchTo().alert();
// Display the prompt message to console
System.out.println(promptPopUp.getText());
// Click 'OK'
promptPopUp.accept();
} //End of @Test
@After
public void tearDown() throws Exception {
// Close the Firefox browser
driver.close();
}
}
代码说明:
1. 为了实例化一个弹出框,我们将必须导入import openqa.selenium.Alert
包。
键入上述代码后,“Alert
”一词下方会出现一条弯曲的线。 悬停时,蚀将建议所有可能的快速修复。 单击建议导入“警报”包的第一个修补程序。
// Switch the control to 'Alert Box' popup
Alert alertPopUp = driver.switchTo().alert();
该包指定了Alert
接口,该界面用于处理基于 Web 的弹出框。
“alertPopUp
”是为引用显示的弹出窗口而创建的新实例变量。
2. 要将控件从主窗口切换到弹出窗口,
driver.switchTo().alert();
3. 要使文本显示在弹出框中,请在引用所生成警报的实例变量上使用getText()
方法。
// Print the alert message to console
System.out.println("Alert Box message: " + alertPopUp.getText());
4. 要接受警报,请使用accept()
方法。
// Accept the alert popup
alertPopUp.accept();
5. 要关闭警报,请使用dismiss()
方法。
// Dismiss the popup
confirmPopUp.dismiss();
执行结果:
在 JUnit 窗口中,绿色条显示测试用例已成功执行。 控制台窗口显示没有任何错误。 它还按预期显示所有打印的消息。
是时候练习了,我很快会在另一篇文章中见。 祝你有美好的一天!
9Y WebDriver – 处理多个窗口
原文: https://javabeginnerstutorial.com/selenium/9y-webdriver-handling-multiple-windows/
大家好! 测试涉及多个窗口的工作流已成为生活的一部分。 借助 Selenium WebDriver,在这些打开的窗口之间进行平滑切换已成为小菜一碟。 如果您像我一样,那么您可能会急切希望看到所有这些在我们的代码中如何真正起作用。 因此,让我们继续深入探讨吧……
每当实例化一个新的 WebDriver 对象时,就会向每个打开的唯一窗口分配一个唯一的字母数字 ID。 这个独特的 ID 被称为“窗口句柄”- 这是 Selenium Ville 拥挤的街道上的另一个新名词。 因此,使用这些唯一的 ID,我们可以轻松地在窗口之间切换控件并执行所需的活动。
1. String getWindowHandle()
:
此方法用于获取当前窗口的窗口句柄。
语法:
// Get current window handle
String parentWinHandle = driver.getWindowHandle();
2. Set<String> getWindowHandles()
:
此方法用于获取Set
中所有打开的窗口的窗口句柄。
语法:
// Get the window handles of all open windows
Set<String> winHandles = driver.getWindowHandles();
可以使用分配给每个打开的窗口的唯一句柄的引用,使用相同的switchTo()
方法从一个窗口切换到另一窗口。 为了更好地了解switchTo()
,请单击此处。
。
场景
让我们看一个实现这些方法的测试案例,以更深入地了解这些概念,
- 打开 Firefox 浏览器
- 导航到演示站点
- 获取当前的窗口句柄并将其打印到控制台
- 使用 ID 找到“点击以打开新的浏览器窗口!”按钮
- 点击按钮打开新窗口
- 获取两个打开的窗口的窗口句柄
- 通过两个句柄循环
- 切换到带有句柄参考的新窗口
- 获取标题并将其打印到控制台
- 关闭新窗口
- 将控件切换回父窗口,然后将 URL 打印到控制台
- 验证 Eclipse IDE 控制台输出屏幕和 JUnit 窗格是否成功
此方案的 JUnit 代码是,
package com.blog.junitTests;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
public class MultipleWindows {
// Declaring variables
private WebDriver driver;
private String baseUrl;
@Before
public void setUp() throws Exception {
// Selenium version 3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver", "E:\\Softwares\\"
+ "Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe");
// Create a new instance for the class FirefoxDriver
// that implements WebDriver interface
driver = new FirefoxDriver();
// Implicit wait for 5 seconds
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// Assign the URL to be invoked to a String variable
baseUrl = "https://chandanachaitanya.github.io/selenium-practice-site/";
}
@Test
public void testPageTitle() throws Exception {
// Open baseUrl in Firefox browser window
driver.get(baseUrl);
// Get current window handle
String parentWinHandle = driver.getWindowHandle();
System.out.println("Parent window handle: " + parentWinHandle);
// Locate 'Click to open a new browser window!' button using id
WebElement newWindowBtn = driver.findElement(By.id("win1"));
// Click the button to open a new window
newWindowBtn.click();
// Get the window handles of all open windows
Set<String> winHandles = driver.getWindowHandles();
// Loop through all handles
for(String handle: winHandles){
if(!handle.equals(parentWinHandle)){
driver.switchTo().window(handle);
Thread.sleep(1000);
System.out.println("Title of the new window: " +
driver.getTitle());
System.out.println("Closing the new window...");
driver.close();
}
}
// Switching the control back to parent window
driver.switchTo().window(parentWinHandle);
// Print the URL to the console
System.out.println("Parent window URL: " + driver.getCurrentUrl());
} // End of @Test
@After
public void tearDown() throws Exception {
// Close the Firefox browser
driver.close();
}
}
执行结果:
代码是不言自明的,因为同时提供了注释。
在“Eclipse IDE 中 -> JUnit 窗格 -> 绿色条”显示测试用例已成功执行。 控制台窗口显示没有任何错误。 它还按预期显示所有打印的消息。
有了这个,我很快就会在另一篇文章中见。 祝你有美好的一天!
9Z WebDriver – 最大化窗口
原文: https://javabeginnerstutorial.com/selenium/9z-webdriver-window-maximize/
嗨冠军! 事情并非总是以我们希望的方式运作,这就是挑战的形式。 使用 Selenium Webdriver,我们的测试工作变得比我们期望的要容易得多。 一种这样的情况是最大化浏览器窗口。
屏幕截图可以节省生命,为了在抓取时查看所有 Web 元素,最大化浏览器窗口非常重要。 因此,与其向下滚动到特定元素,不如最大化窗口并完成手头的任务。
// Maximize the new window
driver.manage().window().maximize();
你相信吗? 这一条线就是您想要的! 很难消化? 别担心。 让我们看一个示例,看看这段代码的实际效果。
场景
- 打开 Firefox 浏览器
- 导航到演示站点
- 获取当前的窗口句柄
- 使用 ID 定位“单击以打开一个小窗口!”按钮
- 点击按钮打开小窗口
- 获取两个打开的窗口的窗口句柄
- 通过两个句柄循环
- 切换到带有句柄参考的新窗口
- 获取标题并将其打印到控制台
- 将小窗口最大化到全屏尺寸
- 关闭新窗口
- 将控件切换回父窗口,然后将 URL 打印到控制台
- 验证 Eclipse IDE 控制台输出屏幕和 JUnit 窗格是否成功
此方案的 JUnit 代码是,
package com.blog.junitTests;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
public class WindowMaximize {
// Declaring variables
private WebDriver driver;
private String baseUrl;
@Before
public void setUp() throws Exception {
// Selenium version 3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver", "E:\\Softwares\\"
+ "Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe");
// Create a new instance for the class FirefoxDriver
// that implements WebDriver interface
driver = new FirefoxDriver();
// Implicit wait for 5 seconds
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// Assign the URL to be invoked to a String variable
baseUrl = "https://chandanachaitanya.github.io/selenium-practice-site/";
}
@Test
public void testPageTitle() throws Exception {
// Open baseUrl in Firefox browser window
driver.get(baseUrl);
// Get current window handle
String parentWinHandle = driver.getWindowHandle();
// Locate 'Click to open a small window!' button using id
WebElement newWindowBtn = driver.findElement(By.id("win2"));
// Click the button to open a new window
newWindowBtn.click();
// Get the window handles of all open windows
Set<String> winHandles = driver.getWindowHandles();
// Loop through all handles
for (String handle : winHandles) {
if (!handle.equals(parentWinHandle)) {
driver.switchTo().window(handle);
System.out.println("Title of the new window: " + driver.getTitle());
// Maximize the new window
driver.manage().window().maximize();
driver.close();
}
}
// Switching the control back to parent window
driver.switchTo().window(parentWinHandle);
// Print the URL to the console
System.out.println("Parent window URL: " + driver.getCurrentUrl());
} // End of @Test
执行结果:
清晰的注释使代码不言自明。
在“Eclipse IDE 中 -> JUnit 窗格 -> 绿色条”显示测试用例已成功执行。 控制台窗口显示没有任何错误。 它还按预期显示所有打印的消息。
如有任何疑问,请不要在评论部分大喊大叫!
我很快会在另一篇文章中再见。 祝你有美好的一天!
9AA WebDriver – 执行 JavaScript 代码
原文: https://javabeginnerstutorial.com/selenium/9aa-webdriver-executing-javascript-code/
嗨冠军! 今天就与浏览器进行一些高质量的互动!! 因此,您能猜出浏览器首选的语言吗? 是的,你说对了。 确实是 JavaScript 。 如果您使用的是 Chrome,则单击“F12”将打开“开发人员工具”,这有助于我们直接从浏览器执行 JavaScript。 某些动作(例如滚动,显示警报等)变得更易于使用 JavaScript 进行管理。
除此之外,在其他情况下,我们可能找不到合适的 Selenium API 来执行特定操作。 但是我们可以通过执行 JavaScript 代码来执行该操作。 Selenium WebDriver 提供了一个界面,可以帮助我们做到这一点!
JavascriptExecutor
…您听说过,就像那些名人一样。 好吧,现在是我们该去见那颗璀璨的星星的时候了。 JavascriptExecutor
不需要添加任何外部 JAR 文件或插件。 只需导入一个包即可完成工作,“导入org.openqa.selenium.JavascriptExecutor
”。 它有两种重要的方法可以执行我们的 Selenium 测试中的 JavaScript 代码,以自动化测试中的应用,即
executeScript(script, args)
executeAsyncScript(script, args)
让我们通过几个简单的步骤来理解这一点。
步骤 1:
导入以下包,import org.openqa.selenium.JavascriptExecutor;
创建一个JavascriptExecutor
对象,并通过将其类型转换为JavascriptExecutor
来分配驱动程序对象。
// Typecast driver to JavascriptExecutor
JavascriptExecutor jsExecutor = (JavascriptExecutor)driver;
步骤 2:
这样创建的JavascriptExecutor
对象允许我们从 Selenium 测试中执行 JavaScript 代码。
// Scroll down by 100 pixels
jsExecutor.executeScript("window.scrollBy(0,100)");
此“excuteScript
”方法采用两个参数。 第一个是 JavaScript 代码,第二个是 Java 脚本代码所需的可选参数列表。
概览
让我们看一个测试案例,实现到目前为止所介绍的技术,
场景
- 打开 Firefox 浏览器
- 导航到演示站点(https://chandanachaitanya.github.io/selenium-practice-site/)
- 使用和不使用 JavaScript 代码打印页面 URL 进行控制台
- 垂直向下滚动页面 100 像素
- 刷新页面
- 导航到 Google 主页
- 使用 JavaScript 代码执行上述三个操作
- 验证 Eclipse IDE 控制台输出屏幕和 JUnit 窗格是否成功
此方案的 JUnit 代码是,
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
public class JavaScriptExecutorTest {
// Declaring variables
private WebDriver driver;
private String baseUrl;
@Before
public void setUp() throws Exception {
// Selenium version 3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver", "E:\\Softwares\\"
+ "Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe");
// Create a new instance for the class FirefoxDriver
// that implements WebDriver interface
driver = new FirefoxDriver();53
// Implicit wait for 5 seconds
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// Assign the URL to be invoked to a String variable
baseUrl = "https://chandanachaitanya.github.io/selenium-practice-site/";
}
@Test
public void testPageTitle() throws Exception {
// Open baseUrl in Firefox browser window
driver.get(baseUrl);
Thread.sleep(5000);
// Typecast driver to JavascriptExecutor
JavascriptExecutor jsExecutor = (JavascriptExecutor)driver;
// Execute JavaScript code and assign it to a String
String pageURL = (String)jsExecutor.executeScript("return document.URL;");
// Print the URL to the console
System.out.println("URL : " + pageURL);
// Print the URL without JavaScript to the console
System.out.println("URL without JavascriptExecutor: " + driver.getCurrentUrl());
// Scroll down by 100 pixels
jsExecutor.executeScript("window.scrollBy(0,100)");
// Refresh the page
jsExecutor.executeScript("history.go(0)");
// Navigating to a different page
jsExecutor.executeScript("window.location = 'https://www.google.com/';");
} // End of @Test
@After
public void tearDown() throws Exception {
// Close the Firefox browser
driver.close();
}
}
执行结果:
为每行代码提供了注释,使其易于说明。
在 JUnit 窗格中,绿色条显示测试用例已成功执行。 控制台窗口显示没有任何错误。 它还按预期显示所有打印的消息。
是时候尝试今天的技能了。 是的,戴上安全帽,以免遇到任何异常!
所有代码文件都放置在 GitHub 仓库中,以方便访问。 您可以为仓库加注星标和分支以方便使用。 请仔细阅读“README.md
”文件以获取明确说明。
祝你有美好的一天!
9AB WebDriver – 使用动作类
原文: https://javabeginnerstutorial.com/selenium/9bb-webdriver-actions-class/
灯光,摄像头,动作! 是的,今天所有有关动作的内容。 哦,我不是在谈论您在电影中观看的那些战斗顺序,而是在谈论键盘和鼠标的动作。 Selenium WebDriver 提供了一个面向用户的 API,用于执行复杂的用户手势。 我们到处都希望自动化! 因此,除了直接使用键盘和鼠标,我们还可以使用Actions
类来执行基本的click
,sendKeys
和复杂的操作,例如dragAndDrop
等。
一些可用的键盘和鼠标方法是
1. click(WebElement target)
参数:target
– 要单击的元素。
说明:单击给定目标的中间。
2. clickAndHold()
说明:单击当前鼠标位置不会释放。
3. doubleClick(WebElement target)
参数:target
– 要点击的元素。
说明:在给定目标元素的中间双击。 等效于:Actions.moveToElement(element).doubleClick();
4. dragAndDrop(WebElement src, WebElement dest)
参数:
source
– 要拖动的元素,即模拟按钮按下的位置
target
– 在以下位置移动并释放鼠标的元素
说明:在源元素的位置单击并按住,移至目标元素的位置,然后释放鼠标。
5. dragAndDropBy(WebElement src, int xOffset, int yOffset)
参数:
source
– 模拟按钮按下的元素。
xOffset
- 水平移动偏移。
yOffset
- 垂直移动偏移。
说明:在源元素的位置单击并按住,在 x 轴上移动给定的xOffset
,在 y 轴上移动给定的yOffset
,然后释放鼠标。
6. keyDown(java.lang.CharSequencemodifier_key)
参数:
Modifier_key
- 可以是Keys.SHIFT
,Keys.ALT
或Keys.CONTROL
。 如果提供的按键都不是,则抛出IllegalArgumentException
。
说明:执行修改键,但不释放键。 随后的交互可以假定按键被按下。 请注意,修饰键永远不会隐式释放-必须调用keyUp(theKey)
或sendKeys(Keys.NULL)
才能释放修饰键。
7. moveByOffset(int xOffset, int yOffset)
参数:
xOffset
- 水平偏移。 负值表示将鼠标左移。
xOffset
- 垂直偏移。 负值表示向上移动鼠标。
说明:将鼠标从其当前位置(或0, 0
)移动 x 轴上的给定xOffset
和 y 轴上的yOffset
。
8. moveToElement(WebElement target)
参数:
target
- 要移动的元素。
说明:将鼠标移到目标元素的中间。
9. perform()
描述:执行或执行所有动作。
10. release()
说明:在当前鼠标位置释放按下的鼠标左键。
概览
让我们看一个测试案例,实现我们刚才讨论的一些方法,
场景
- 开启 Chrome 浏览器
- 导航到演示站点
- 使用
Actions
类执行以下键盘和鼠标操作,- 找到并点击“自行车”复选框
- 找到文本“消息”,然后双击以突出显示它
- 在大写文本框中输入“
hi there
” - 将
click()
从第一位置拖放到可排序列表的第三位置
- 在 Chrome 浏览器中验证输出
- 验证 Eclipse IDE 控制台输出屏幕上是否有任何已打印的消息,并验证 JUnit 窗格是否成功
此方案的 JUnit 代码是,
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;
public class TestActions {
// Declaring variables
private WebDriver driver;
private String baseUrl;
@Before
public void setUp() throws Exception {
// System property set up for Chrome driver
System.setProperty("webdriver.chrome.driver", "browser-drivers\\chromedriver.exe");
// Create a new instance for the class ChromeDriver
// that implements WebDriver interface
driver = new ChromeDriver();
// Implicit wait for 5 seconds
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// Assign the URL to be invoked to a String variable
baseUrl = "https://chandanachaitanya.github.io/selenium-practice-site/";
}
@Test
public void testKeyboardAndMouseActions() throws Exception {
// Open baseUrl in Chrome browser window
driver.get(baseUrl);
// Locate 'Bicycle' checkbox using name
WebElement bicyle = driver.findElement(By.name("vehicle1"));
// Locate 'Message' textbox using id
WebElement messageTextBox = driver.findElement(By.id("enterText"));
// Locate 'click()' using id
WebElement element = driver.findElement(By.id("click"));
//Create an instance of the Actions Class
Actions actions = new Actions(driver);
// Perform multiple actions
// Type 'hi there' in uppercase in the text box
actions
.moveToElement(messageTextBox)
.keyDown(Keys.SHIFT)
.sendKeys(messageTextBox, "hi there")
.keyUp(Keys.SHIFT)
.perform();
// Click 'Bicycle' checkbox
actions
.click(bicyle)
.perform();
// Print a message to console
System.out.println("Bicylce checkbox clicked.");
// Double click/highlight the text, 'Message'
actions
.moveToElement(driver.findElement(By.id("labelText")))
.doubleClick()
.perform();
// Print the text of the element that will be dragged and dropped
System.out.println("Element that is dragged : " + element.getText());
// Drag and drop 'click()' using Actions class
actions
.dragAndDropBy(element, 50, 100)
.perform();
} // End of @Test
@After
public void tearDown() throws Exception {
// Close the Chrome browser
driver.close();
System.out.println("Closing the driver");
}
}
说明:
让我们破译一些复杂的动作。
1.突出显示“消息”文本:
actions
.moveToElement(driver.findElement(By.id("labelText")))
.doubleClick()
.perform();
将焦点移至“消息”文本,然后双击以突出显示。 使用perform()
方法执行操作。
2.在文本框中以大写字母键入“hi there
”
actions
.moveToElement(text)
.keyDown(Keys.SHIFT)
.sendKeys(text, "hi there")
.keyUp(Keys.SHIFT)
.perform();
首先将焦点移至文本框,按住SHIFT
键,键入文本“hi there
”,使其大写,然后在keyup
方法的帮助下释放SHIFT
键。
3.拖放“click()
”
这可以通过两种方式完成:
actions
.dragAndDropBy(element, 50, 100)
.perform();
最简单的方法是使用dragAndDropBy
方法,在该方法中,我们可以指定要拖动的元素以及xOffset
和yOffset
。
如果您想走复杂路线,看起来像忍者,
actions
.clickAndHold(element)
.moveByOffset(50, 100)
.release()
.perform();
单击目标元素,在本例中为html()
,并确保不要释放该喀哒声。 移动给定的 xOffset 和 yOffset,然后释放鼠标单击,以便将元素放置在新位置。 结果,该元素被拖放。
执行结果:
为每行代码提供了注释,使其其余部分不言自明。 散布演示站点输出的一些视觉效果,
自行车复选框位于并按预期检查。
双击文本Message
,结果将突出显示。 在文本框中,“hi there
”用大写字母键入。 从可排序列表中将click()
从其默认的第一位置拖动到第三位置。
在 JUnit 窗格中,绿色条显示测试用例已成功执行。 同样,控制台窗口显示没有错误以及所有打印的消息如预期的那样。
是时候把忍者带出来了。 尝试使用尽可能多的动作类方法,并从中获得乐趣!
上面讨论的所有代码都可以在 GitHub 仓库的“WebDriver”文件夹中找到。 您可以为仓库加注星标和分支以方便使用。 请仔细阅读README.md
文件以获取明确说明。
祝你有美好的一天!
9AC WebDriver – 无法轻松定位元素? 继续阅读...
原文: https://javabeginnerstutorial.com/selenium/9ac-webdriver-unable-to-locate-an-element-easily-read-on/
好吧,如果您的内存比我的好,那么您将记住,Selenium WebDriver 中有 8 种定位器类型,即 ID,名称,标签名称,类名称,链接文本,部分链接文本,CSS 选择器和 XPath。 令人遗憾的是,即使我们有很多定位器类型,定位元素仍然充满挑战,尤其是在被测试的应用很复杂的情况下。
例如,
- 为每个会话生成唯一的 ID(带有随机字母数字字符)
- 各种元素的重复类名
- 多个
div
标签使 XPath 的长度比平时更长
所有这些使自动化测试人员的工作更具挑战性! 绝对 XPath 可能会有所帮助,但是如果 DOM 中包含新元素或现有元素稍微移动了一点,那么脚本可能会失败,因为找不到该元素。 相对的 XPath 可能可以解决,但是即使那样,由于脚本变得复杂,整个过程也变得很困难。
处理此类情况的最简单便捷的方法是在 HTML 元素上引入data-*
属性(自定义数据属性)。 这里唯一的问题是,您将需要成为开发人员的好书! 他/她是根据您的要求将这些属性添加到代码中的人。😉
现在让我对这些自定义数据属性进行学习,
custom
属性允许我们在 HTML 元素上存储额外的数据或信息。 要遵循的规则是,
- 属性名称应以
data-
开头 - 在
data-
的此前缀之后,属性名称应至少一个字符长,并且只能使用小写字母 - 为此属性提供的值可以是任何字符串。
语法:<element data-*="value">
那很特别,你不觉得吗?
这里有一个示例,可以更好地可视化该概念,
<li data-test="poultry">Chickens</li>
有了这些数据属性,定位元素就轻松了!
WebElement poultry = driver.findElement(By.
xpath
("//li[@data-test='poultry']"));
而且,就是这么简单! 继续尝试一下,随时在评论部分中发表您的问题。
在另一篇文章中再见。
10A 高级 WebDriver – 使用 Apache ANT
原文: https://javabeginnerstutorial.com/selenium/10a-advanced-webdriver-apache-ant/
大家好! 您的构建中是否有 ANT? 开玩笑! 其实我不是。 同意这听起来很怪异,但不要害怕,因为我在这里! (现在,这就是我所说的,押韵。)
今天,让我们考虑一下这个 Apache ANT 到底是什么,以及为什么它将它作为 WebDriver 框架构建中的重要构建块。 到本文结束时,您一般应该可以处理任何 ANT 项目。
起步:
由 Apache Software Foundation 提供的 ANT 工具是用 Java 编写的构建工具。 它已经很老了(Maven 在这个领域赢得了胜利,Gradle 在相处中),非常受欢迎,并且在大多数项目中仍然可以找到。 Apache ANT 的优点在于,它仅执行被告知要做的事情。 引擎盖下没有太多魔术发生。 (请注意,有时这可能是一把双刃剑!)
创建可执行文件意味着
- 在
src
文件夹下编译所需的.java
文件 - 为 JAR 创建清单文件
- 将所有
.class
文件和清单文件一起压缩 - 使用 jar 命令创建可执行文件
但是,使用 Apache ANT 可以通过简单的目标处理所有这些步骤(我们将在一分钟内看到 Target 是什么!)。
要完成的所有工作都以结构化和模块化的方式在 XML 文件中指定,从而很容易识别是否有问题。 这个文件被称为著名的“build.xml
”。
Apache ANT 带有一个名为build.xml
的默认构建文件。 我们总是可以编辑或创建一个新的 XML 文件来满足我们的需求。 这基本上是命令行工具。 因此,使用命令“ant build_file_name.xml
”从命令提示符运行 Apache ANT 非常容易。 最好的部分是,如果您没有重命名build.xml
(只需修改提供的默认值或创建您自己的名称并将其命名为build.xml
),那么只有命令“ant
”会自动知道要查找build.xml
并执行它的作用!
Apache ANT 具有强大的功能,足以超越“谢谢”的门槛! 我们可以,
- 清理项目
- 编译源代码
- 生成可分发的 JAR 或 WAR
- 处理来自版本控制系统(例如 CVS,SVN 等)的代码
- 向指定级别的记录器和监听器回显消息(错误,警告,信息,详细信息,调试信息)
- 创建或删除目录
- 复制,移动,删除文件
- 可以压缩,解压缩,tar,untar,unjar,unwar 文件
- 执行 JUnit 测试,测试脚本等。
- 通过 JDBC 对数据库执行一系列 SQL 语句
- 生成 JUnit 测试报告
还有很多其他事情…
听起来很吓人,但到目前为止,我刚才提到的每件事都可以通过构建文件来实现。
是时候忙于大肆宣传的构建文件的元素了。 这些都是用 XML 编写的。
注意: 以下信息足以使您熟悉 Apache ANT 构建文件。 有关深入的知识,请访问 Apache ANT 用户手册(http://ant.apache.org/manual/)。
每个构建文件都有
- 项目 – 至少包含一个目标
- 目标 – 执行工作单元的一组任务
- 任务 – 可以执行的一段代码
示例build.xml
文件如下,
让我们一次将构建文件提取一个标签。
项目:这具有三个属性。
name
– 项目名称default
– 如果未提供任何内容,则应执行的默认目标baseDir
– 从中计算文件中相对路径的基本目录。 “.
” 指执行的当前目录
目标:如示例构建文件中所示,一个项目可以具有一个或多个目标。 目标是一组任务。 我们可以选择希望 ANT 执行的目标,并用逗号分隔它们的名称。 如果未指定目标,则将执行默认目标。
这里要注意的重要属性是“依赖”。 这指定了它所依赖的目标名称。 例如,编译目标将仅在其依赖的初始化目标执行后执行。 因此,depends
属性指定执行目标的顺序。
任务:这是一段可以执行的代码。 每个任务可以具有一个或多个属性作为键值对。
句法:
<name attribute1=”value1” attribute2=”value2” … />
name
是任务的名称,attributeN
和valueN
分别是属性名称和值名称。
这是现成的内置任务的长长列表。 我们也可以编写自己的任务。 (还记得吗?都是用 Java 编写的)。
您一定想知道,为什么要深入研究构建文件,而不是立即安装并开始执行操作! 传递这么多 ANT 知识是有原因的。 随着我们在旅途中的进一步发展,您将对此表示赞赏。 请稍等...
现在开始营业!
步骤 1 :转到“https://ant.apache.org/bindownload.cgi”,然后单击“apache-ant-1.10.2-bin.zip
”以在下面下载.zip
文件 “蚂蚁的当前版本”。
步骤 2:下载.zip
存档后,将所有文件提取到本地计算机上的目录中。
步骤 3 :如下设置环境变量ANT_HOME
和PATH
,
右键点击“计算机 -> 属性 -> 高级系统设置 -> ‘高级’标签 -> 环境变量”,在系统变量下单击“新建”。
ANT_HOME
设置为提取 Apache ANT 文件的文件夹的路径。
类似地,编辑Path
变量以包含%ANT_HOME%\bin
。
步骤 4 :通过在“命令提示符”中键入以下命令来验证是否已安装 Apache ANT
ant version
步骤 5:下一个任务是打开 Eclipse IDE,
- 右键单击“Java 项目 -> 导出”
- 在“常规”下,选择“蚂蚁构建文件”,然后点击“下一步”
- 确保已选择所需的项目
- 取消选中“创建目标以使用 Eclipse 编译器编译项目”,以删除对 Eclipse 的任何依赖
单击“完成”以查看自动生成的 eclipse 文件。
就是这样! 您已经准备好将所有项目作为 ANT 构建运行。 这很简单,不是吗?
注意: Eclipse 与 ANT 集成在一起。 为了确保 Eclipse 的“ANT Home”指向最新版本的 ANT,请单击 “Windows -> 首选项 -> Ant -> 运行时”。 点击“类路径”标签。 展开“ANT Home Import”并验证路径。 如果指向的是其他版本,请单击“ANT Home”并浏览用于提取 Apache ANT 文件的文件夹的位置。
在我们的下一篇文章中,让我们做到这一点,并在 ANT 目标的帮助下生成 JUnit 报告。
安装愉快!
10B 高级 WebDriver – 生成 JUnit 报告
原文: https://javabeginnerstutorial.com/selenium/10b-advanced-webdriver-generating-junit-reports/
嗨冠军! 报告,报告,无处不在的报告。 从哈利·波特时代开始,我们就一直在处理这些问题!! (还记得学校的进度报告吗?!)无论如何,报告真的很重要,尤其是在测试中,以便快速了解一切工作原理。
因此,今天,我们将使用 Apache ANT 的junitreport
任务来生成一个任务。 热身!
从我的上一篇文章中,我们生成了带有“junit
”作为默认 JUnit 输出目录的构建文件。 用简单的英语来说,这仅意味着将作为 JUnit 报告任务的一部分生成的所有文件都放置在名为“junit
”的目录下。
步骤 1 :
在项目“Selenium”下,将生成名为“build.xml
”的 ANT 构建文件。 我已经在“com.blog.junitTests
”包下创建了两个 JUnit 测试用例,分别是 RadioBtns_Checkboxes.java 和 SelectItems.java ,如下图所示。 该代码可在相应的帖子中找到。 只需单击文件名即可进行导航。
步骤 2 :
打开“build.xml
”,并确保自动生成“junitreport
”任务。 以下是我的项目的目标(初始化,清理,构建,RadioBtns_Checkboxes
,SelectItems
和junitreport
)。
<project basedir="." default="build" name="Selenium">
<property environment="env"/>
<property name="junit.output.dir" value="junit"/>
<property name="debuglevel" value="source,lines,vars"/>
<property name="target" value="1.8"/>
<property name="source" value="1.8"/>
<target name="init">
<mkdir dir="bin"/>
<copy includeemptydirs="false" todir="bin">
<fileset dir="src">
<exclude name="**/*.java"/>
</fileset>
</copy>
</target>
<target name="clean">
<delete dir="bin"/>
</target>
<target name="build" depends="init">
<echo message="${ant.project.name}: ${ant.file}"/>
<javac debug="true" debuglevel="${debuglevel}" destdir="bin" includeantruntime="false" source="${source}" target="${target}">
<src path="src"/>
<classpath refid="Selenium.classpath"/>
</javac>
</target>
<target name="RadioBtns_Checkboxes">
<mkdir dir="${junit.output.dir}"/>
<junit fork="yes" printsummary="withOutAndErr">
<formatter type="xml"/>
<test name="com.blog.junitTests.RadioBtns_Checkboxes" todir="${junit.output.dir}"/>
<classpath refid="Selenium.classpath"/>
</junit>
</target>
<target name="SelectItems">
<mkdir dir="${junit.output.dir}"/>
<junit fork="yes" printsummary="withOutAndErr">
<formatter type="xml"/>
<test name="com.blog.junitTests.SelectItems" todir="${junit.output.dir}"/>
<classpath refid="Selenium.classpath"/>
</junit>
</target>
<target name="junitreport">
<junitreport todir="${junit.output.dir}">
<fileset dir="${junit.output.dir}">
<include name="TEST-*.xml"/>
</fileset>
<report format="frames" todir="${junit.output.dir}"/>
</junitreport>
</target>
</project>
步骤 3 :
可以通过以下任意一种方式执行构建,
1.右键单击“构建文件(build.xml
)-> 运行方式 -> Ant Build”
2.右键单击“构建文件(build.xml
)-> 运行方式 -> 外部工具配置 -> 运行”
3.蚀工具栏中的快捷方式运行图标
让我们采用第二种方法,看看如何使用“外部工具配置”选项修改现有配置。
因此,右键单击“构建文件->运行方式->外部工具配置”:这将打开一个弹出窗口。 选择“主要”标签,并确保选择了正确的构建文件。
然后点击“目标”标签。
- 将列出构建文件中定义的所有目标名称,并且仅检查默认目标。 点击“运行”,选择要执行的所有目标。
- 在“目标执行顺序”框中,将从上一步中选择的所有目标按执行顺序列出。
- 如果您想更改顺序,请点击“顺序...”。 这将打开“顺序目标”弹出窗口。
- 我们可以通过选择目标并相应地单击“上”或“下”按钮来在此弹出窗口中指定目标执行顺序。 点击“确定”以确认最终的执行顺序。
- 完成所有操作后,点击“应用”和“运行”
步骤 4 :
可以通过查看控制台视图来验证成功执行。 它显示了按配置顺序执行的所有目标以及基于构建文件中提到的日志记录级别的日志。 如图所示,将显示运行构建所花费的总时间以及消息“BUILD SUCCESSFUL
”或“BUILD FAILED
”。
步骤 5 :
在 Eclipse 的“包浏览器”视图中,右键单击该项目,然后单击“刷新”或F5
。 已经创建了“junit
”文件夹(在生成构建文件时在“JUnit 输出目录”框中指定的名称)。 这是 JUnit 报告(index.html
)和 XML 一起提供的地方,用于执行的每个测试用例,显示其成功或失败。
或者,在文件浏览器中导航到项目路径,然后双击“junit
”文件夹。
步骤 6 :
在浏览器中打开“index.html
”,并检查生成的默认报告。 这将在左窗格中显示所涉及的包和所有已执行的 Java 文件。
右窗格显示测试结果的摘要,其中包括测试的总数,失败,错误,跳过,成功率和执行时间。 还显示了包列表,其中包含类似的详细信息。
要深入查看所有已执行的测试或失败等,请单击显示的数字,如下图所示,
用 Apache ANT 生成 JUnit 报告不是一件容易的事吗? 猜猜这就是我们今天要做的。
在另一篇文章中再见。 祝您报告愉快!
10C 高级 WebDriver – JUnit 报表自定义
原文: https://javabeginnerstutorial.com/selenium/10c-advanced-webdriver-junit-report-customization/
自定义! 谁不喜欢自己作品中的某些独特之处? 让我们在Apache ANT 生成的 JUnit 报告上添加一些糖霜,通过引入一些优点使其看起来更漂亮。
作为自定义自动生成报告的第一步,让我们了解其背后的代码(来自Build.xml
),
<junitreport todir="junit">
<fileset dir="junit">
<include name="TEST-*.xml"/>
</fileset>
<report format="frames" todir="junit/report"/>
</junitreport>
Apache ANT 的JunitReport
任务通过执行以下步骤来生成报告,
- JUnit 任务生成的所有单个 XML 文件都被合并并命名为
TESTS-TestSuites.xml
- CSS 样式应用于合并后的文档
- 最终的可浏览报告以 HTML 格式提供
<junitreport todir="junit">
属性“todir
”指定了将合并的 XML 文件保存到的目录。 如果未指定任何内容,则默认为当前目录。
<fileset dir="junit">
<include name="TEST-*.xml"/>
</fileset>
注意: 所有单独的 JUnit 测试都是通过“junit” ANT 任务执行的,生成的 XML 文件的名称使用“outfile
”属性指定。 如果未提及名称,则默认值为“TEST-name
”(其中name
是在 junit 任务的name
属性中指定的测试名称)。
使用fileset
元素,junitreport
会从“dir
”中提到的给定目录中收集所有单个 XML 文件,并根据上述代码段考虑所有以“TEST-
”开头的文件。
<report format="frames" todir="junit/report"/>
这是从合并文件生成可浏览报告的确切任务。
属性“格式”指定可用于生成报告的两种可用格式之一。 它们必须是“frames
”或“noframes
”。 如果未指定任何内容,则默认为“帧”格式。 “todir
”属性指定生成的报告必须保存到的目录。
- “
frames
”格式将生成一个报告,该报告将包含具有重定向功能的多个文件,并使用样式表来实现此效果。 这是一种更具可读性的格式。 - “
noframes
”格式会生成一个名为“junit-noframes.html
”的文件,没有任何框架,并且不使用任何类型的重定向。 这种类型更适合作为电子邮件附件发送或生成 pdf 文件。
既然您已经了解了事物的内在运作方式,那么让我们进入有趣的部分(可能比拥有圣代冰淇淋更有趣)!
与那些无聊的自动生成的报告说再见,并欢迎自定义! 这意味着,我们将要编写或编辑某些内容。 那就是一个 XSL 文件,它位于下载的apache-ant-1.10.2-bin.zip
的“etc
”目录中。 让我们走简单的路线,就是编辑!
还记得吗,我们有两种格式? 因此,要自定义:
- 框架 – 编辑
junit-frames.xsl
- 无框架 – 编辑
junit-noframes.xsl
就我而言,这两个 XSL 文件都位于“E:\apache-ant-1.10.2\etc
”路径中。 作为本文的一部分,让我们研究“框架”格式。 这意味着我们将编辑“junit-frames.xsl
”文件。 复制此文件并将其粘贴到项目路径中。 不要重命名文件。 为了使所有更改都能正常进行,请向报告任务添加属性styledir
,如下所示。 这指定了定义的样式表junit-frames.xsl
的位置。 .
指定此样式表位于当前目录中(与构建文件的目录相同)。
<report styledir="." format="frames" todir="junit/report"/>
今天我们将深入探讨以下内容,
- 更改标题和描述
- 增加栈跟踪字体大小
在下面的文章中,我们还将介绍
- 添加或删除列
- 改变风格
- 在标题部分添加徽标
- 修改静态文字
注意:因此,所有更改都是对放置在项目目录“E:\Selenium\
”(其中 Selenium 中的“junit-frames.xsl
”)进行的,在我的情况下是项目名称)。
更改报告标题和描述
在项目位置的“junit-frames.xsl
”样式表中,
1.将参数名称的文本(标题为TITLE
)更改为“JUnit Report
”。
之前
<xsl:param name="TITLE">Unit Test Results.</xsl:param>
之后
<!-- <xsl:param name="TITLE">Unit Test Results.</xsl:param> -->
<xsl:param name="TITLE">JUnit Report</xsl:param>
2.“页眉”部分负责显示标题和右侧显示的说明。 将它们分别更改为“自定义 JUnit 报告”和“由忍者设计!”。
之前
<!-- Page HEADER -->
<xsl:template name="pageHeader">
<h1><xsl:value-of select="$TITLE"/></h1>
<table width="100%">
<tr>
<td align="left"></td>
<td align="right">Designed for use with <a href="http://www.junit.org/">JUnit</a> and <a href="http://ant.apache.org/">Ant</a>.</td>
</tr>
</table>
<hr size="1"/>
</xsl:template>
之后
<xsl:template name="pageHeader">
<!-- <h1><xsl:value-of select="$TITLE"/></h1> -->
<h1>Custom JUnit Report<h1>
<table width="100%">
<tr>
<td align="left"></td>
<!-- <td align="right">Designed for use with <a href="http://www.junit.org/">JUnit</a> and <a href="http://ant.apache.org/">Ant</a>.</td> -->
<td align="right">Designed by ninjas!</td>
</tr>
</table>
<hr size="1"/>
</xsl:template>
保存更改并从 eclipse 生成 JUnit 报告。
增加栈跟踪字体大小
我们在发生故障时看到的栈跟踪信息非常小,很难读取。 为了增加其字体大小,只需在模板中添加几行名称为stylesheet.css
的行即可。
<xsl:template name="stylesheet.css">
.StackTrace {
font-size: 100%;
}
然后将该类添加到“display-failures
”模板的“code
”标签中,该模板负责显示栈跟踪信息。
之前
<code
<xsl:call-template name="br-replace">
<xsl:with-param name="word" select="."/>
</xsl:call-template>
</code>
之后
<code class="StackTrace">
<xsl:call-template name="br-replace">
<xsl:with-param name="word" select="."/>
</xsl:call-template>
</code>
现在是时候戴上帽子了。 愿 ANT 的力量与您同在。
在另一篇文章中再见。 自定义愉快!
10D 高级 WebDriver – JUnit 报告自定义续
原文: https://javabeginnerstutorial.com/selenium/10d-advanced-webdriver-junit-report-customization-part2/
嗨冠军! 如果您没有让之前的文章发挥您的想象力,那么我今天将帮助您做到这一点。
我们将成为,
- 添加或删除列
- 改变风格
- 在标题部分添加徽标
- 修改静态文字
添加或删除列
这真是小菜一碟。 添加或删除<th>
和<td>
标签可实现此任务。 让我们来看一个场景,在“All Tests
”表中,添加Executed By
列。
<!-- method header -->
<xsl:template name="testcase.test.header">
<xsl:param name="show.class" select="''"/>
<tr valign="top">
<xsl:if test="boolean($show.class)">
<th>Class</th>
</xsl:if>
<th>Name</th>
<th>Status</th>
<th width="60%">Type</th>
<th nowrap="nowrap">Time(s)</th>
<th>Executed By</th>
</tr>
</xsl:template>
在此模板下,<xsl:template match="testcase" mode="print.test">
连同现有的栏一起为新创建的“执行者”栏添加一个值。
<td>
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="@time"/>
</xsl:call-template>
</td>
<td>Tester1</td>
</tr>
</xsl:template>
之前
之后
改变风格
努力思考如何更改生成的报告中的样式? 无需为此费心! 因为和其他网页一样,我们也有一个 CSS 样式表来处理此报告的样式。 只需在“junit-frames.xsl
”文件中搜索名称为“stylesheet.css
”的模板即可。
<!-- this is the stylesheet css to use for nearly everything -->
<xsl:template name="stylesheet.css">
主体,表,标题,栈跟踪,错误,失败,段落,属性的样式均在此模板中指定。 继续前进,戴上帽子! 对 CSS 的每一行进行试验,并查看呈现的更改。 是的,您也可以添加自己的 CSS!
让我们来看一个入门的小示例。 表的详细信息和失败样式如下所示,
table.details tr th{
font-weight: bold;
text-align:left;
background:#a6caf0;
}
.Failure {
font-weight:bold; color:purple;
}
让我们将这些更改如下:
- 所有表格标题均居中对齐
- 表格标题行的背景颜色为“绿色黄色”(
#ADFF2F
) - 失败文字颜色变为栗色
table.details tr th{
font-weight: bold;
text-align:center;
background:#ADFF2F;
}
.Failure {
font-weight:bold; color:maroon;
}
之前
之后
我知道,它看起来并不吸引眼球,但我们证明了我们想要的。 所以,你去了!
在标头部分中添加徽标
我们大多数人都喜欢生成的报告上的徽标。 谁不喜欢个性化和一点营销? 如果您懂一点 HTML,就非常简单。 在名为“pageHeader
”的模板上,添加一个图像标签,并在src
属性中指定路径。 我已将徽标图像放置在生成index.html
文件的项目的“junit
”文件夹中。
<xsl:template name="pageHeader">
<!-- <h1><xsl:value-of select="$TITLE"/></h1> -->
<h1>Custom JUnit Report</h1>
<table width="100%">
<tr>
<td align="left"></td>
<td align="right"><img width="50" height="50" alt="Selenium" src="myLogo.jpg"/> Designed by ninjas!</td>
</tr>
</table>
<hr size="1"/>
</xsl:template>
结果
修改静态文本
这是锦上添花(哦!您现在已经知道了)。 要修改报告中显示的任何静态文本,您只需在“junit-frames.xsl
”文件中进行更改。 是的,你没看错。 就这么简单!
假设在报告的摘要表中,而不是“测试”,我希望它是“测试数量”。 只需在h2
标签上显示“摘要”的正文部分更改文本,
<table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
<tr valign="top">
<th>Number of Tests</th>
<th>Failures</th>
<th>Errors</th>
<th>Skipped</th>
<th>Success rate</th>
<th>Time</th>
</tr>
结果
是时候对我们到目前为止所学到的东西进行反思。
在另一篇文章中再见。 自定义愉快!
10E 高级 WebDriver – 生成 PDF 报告
原文: https://javabeginnerstutorial.com/selenium/10e-advanced-webdriver-generating-pdf-report/
嗨冠军! 到目前为止,我们在报告方面已经涵盖了很多基础,您已经达到了高潮文章。 如果要将 HTML 报告附加到电子邮件并将其发送给利益相关者,则生成 HTML 报告可能没有帮助。 由于 ANT 生成的 JUnit 报告具有index.html
文件,该文件又嵌入了其他一些 HTML 文件,例如overview-frame.html
,allclass-frame.html
和overview-summary.html
文件。
在这种情况下我们该怎么办? 我们如何将其附加到电子邮件? 我们不能将其作为单个文件而不是一组 HTML 文件获得吗? – 所有这些问题的唯一答案是,生成一个 PDF 文件。
让我们看看在不干扰我们到目前为止所做的自定义的情况下生成 PDF 报告的过程,
步骤 1:
我们需要 JUnit PDF 报告要点分发。 它还包含所有必需的依赖项。 从链接“https://sourceforge.net/projects/junitpdfreport/files/”下载最新版本的“必要” zip 文件。
将文件内容解压缩到本地文件夹并记下路径。 另外,请确保分发中包含“build-junitpdfreport.xml
”文件和“lib
”文件夹以及某些其他文件和文件夹。
步骤 2:
是时候在 Eclipse IDE 中浏览“Build.xml
”文件了。 有几行要添加。 首先要做的是告诉我们的项目,该junitpdfreport
基本 zip 文件的内容在哪里提取。 在构建文件的project
标签中,将以下行及其路径添加到分发位置。
<!-- JUnit PDF report installation location --> <import file="E:/junitpdfreport_essentials_1_0/build-junitpdfreport.xml"/>
步骤 3:
将以下目标也添加到构建文件中,以便 ANT 知道该怎么做。
<!-- PDF Report --> <target name="pdfReport"> <junitpdfreport todir="${junit.output.dir}" styledir="default"> <fileset dir="${junit.output.dir}"> <include name="TEST-*.xml"/> </fileset> </junitpdfreport> </target>
让我们尝试一次解密这一行,
1. junitpdfreport todir="${junit.output.dir}"
这是将生成的 pdf 报告保存的位置。
2. fileset dir="${junit.output.dir}"
提供所有 JUnit 测试结果所在的位置(请记住为执行的每个测试生成的 XML 文件,格式为“TEST-*.xml
”)。
就我而言,我有以下测试文件:TEST-com.blog.junitTests.RadioBtns_Checkboxes.xml
,TEST-com.blog.junitTests.SelectItems.xml
和TESTS-TestSuites.xml
保存在位置junit/
。
步骤 4:
右键单击并选择“2 ANT Build”,然后在“编辑配置”弹出窗口中选中“目标”标签。 确保已选中“pdfReport”目标,它是执行顺序中提到的最后一个目标。
点击“运行”以执行构建文件。
步骤 5:
在指定的输出目录(本例中为“junit/
”)中验证生成的 PDF 报告。
生成的 PDF 文件共有 8 页。 例如,前 5 页如下所示,
最后但并非最不重要的是,该构建文件的快照显示了 HTML 报告和 PDF 报告目标代码,
在您达到本文结尾时,让我们利用练习的力量。 在另一个主题上再见,让您掌握 WebDriver!
10F 高级 WebDriver – 截屏
原文: https://javabeginnerstutorial.com/selenium/10f-advanced-webdriver-taking-screenshot/
嗨冠军! 屏幕截图。 在软件测试的拥挤街道上,另一个经常听到的术语。 如果您的环境中出现错误,而开发中没有错误,则无法用截图来证明,这是一个好测试! 因此,现在是时候该了解如何使用 Selenium WebDriver 来抓取了。
我知道您想到了几个问题。 最重要的是,“如果是手动测试,那么我只要按一下键盘上的PrntScr
按钮,然后抓取一个漂亮的屏幕截图即可。 但是,当我实现自动化时,如何获得相同的结果?”
猜猜看,这很简单! 只需按照 3 个步骤进行操作,您就可以使用屏幕截图。 如果您像我一样,可能会急切地想看看它在代码中是如何工作的。 我的目标是取悦,所以请不要再拖延……
步骤 1:
使用 Selenium WebDriver 提供的TakesScreenshot
接口。 将 WebDriver 对象强制转换为TakesScreenshot
类型。
当您在TakesScreenshot
下面看到一条弯曲的线时,只需单击import org.openqa.selenium.TakesScreenshot;
包,您将没有任何错误。
// Cast driver object to TakesScreenshot
TakesScreenshot screenshot = (TakesScreenshot) driver;
步骤 2:
要将屏幕截图获取为图像文件,请调用“getScreenshotAs
”方法。
波浪线? - 点击:
import org.openqa.selenium.OutputType;
import java.io.File;
// Get the screenshot as an image File
File src = screenshot.getScreenshotAs(OutputType.FILE);
步骤 3:
将生成的图像文件复制到您选择的目标位置。 使用FileUtils
类的copyFile
方法可以轻松完成此操作。 重要的是要注意,此方法将引发IOException
。 因此,作为一种好习惯,请将这段代码包装在try-catch
块中。
线条更弯曲? - 点击:
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import java.text.SimpleDateFormat;
import java.util.Date;
确保完全按照指定的方式导入包。 通常,您可能需要下载org.apache.commons.io
jar(在撰写本文时为下载位置)并将其添加到项目的构建路径中。 之前,我们已经多次看到此过程,因此,我不再重复(请参阅本文第 3 步)。
另外,请注意,我们在代码中将图像另存为.jpg
文件。 也可以将其另存为.png
文件。
它可以很简单,
// Copy the screenshot to destination
FileUtils.copyFile(src, new File(“\\screenshot\\test.jpg”));
还是一样复杂
try {
// Specify the destination where the image will be saved
File dest = new File("\\Selenium\\screenshots\\" + testCaseName + "_" + timestamp() + ".jpg");
// Copy the screenshot to destination
FileUtils.copyFile(src, dest);
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
public static String timestamp() {
// Timestamp to make each screenshot name unique
return new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(new Date());
}
在我们的示例中,我们将使用复杂的版本。 因为我们要将所有与屏幕快照相关的代码放在一个单独的方法中(在新类中),并在每次我们希望捕获屏幕快照时调用它。 否则,我们将不得不为每种情况做相同的歌舞。
概览
使用两种方法创建一个名为“SaveScreenshot.java
”的新类。
public static void capture(String testCaseName, WebDriver driver)
– 具有捕获屏幕快照并将其保存到所需位置的所有代码。public static String timestamp()
– 用于生成时间戳并将其提供给上述方法,以使每个保存的屏幕截图都是唯一的。
示例场景
- 打开 Firefox 浏览器。
- 导航到 Google 帐户创建页面
- 通过 ID 找到名字文本框
- 输入“
fname01
”作为名字 - 按名称找到姓氏文本框
- 输入“
lname01
”作为姓氏 - 截取页面截图并将其保存到某个位置。
JUnit 代码:
-
SaveScreenshot.java
类
package com.blog.junitTests;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
public class SaveScreenshot {
public static void capture(String testCaseName, WebDriver driver) {
// Cast driver object to TakesScreenshot
TakesScreenshot screenshot = (TakesScreenshot) driver;
// Get the screenshot as an image File
File src = screenshot.getScreenshotAs(OutputType.FILE);
try {
// Specify the destination where the image will be saved
File dest = new File("\\Selenium\\screenshots\\" + testCaseName + "_" + timestamp() + ".jpg");
// Copy the screenshot to destination
FileUtils.copyFile(src, dest);
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
public static String timestamp() {
// Timestamp to make each screenshot name unique
return new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(new Date());
}
}
2. Screenshot.java
类(执行示例方案部分中详细介绍的步骤)
package com.blog.junitTests;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
public class ScreenshotTest {
//Declaring variables
private WebDriver driver;
private String baseUrl;
private String testCaseName = "ScreenshotTest";
@Before
public void setUp() throws Exception{
// Selenium version 3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver", "E:\\Softwares\\"
+ "Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe");
// Create a new instance for the class FirefoxDriver
// that implements WebDriver interface
driver = new FirefoxDriver();
// Implicit wait for 5 seconds
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// Assign the URL to be invoked to a String variable
baseUrl = "https://accounts.google.com/SignUp";
}
@Test
public void testPageTitle() throws Exception{
// Open baseUrl in Firefox browser window
driver.get(baseUrl);
// Locate First Name text box by id and
// assign it to a variable of type WebElement
WebElement firstName = driver.findElement(By.id("firstName"));
// Clear the default placeholder or any value present
firstName.clear();
// Enter/type the value to the text box
firstName.sendKeys("fname01");
// Locate last name text box by name
WebElement lastName = driver.findElement(By.name("lastName"));
// Clear and enter a value
lastName.clear();
lastName.sendKeys("lname01");
//Take a screenshot
SaveScreenshot.capture(testCaseName, driver);
}
@After
public void tearDown() throws Exception{
// Close the Firefox browser
driver.close();
}
}
为每行代码提供了注释,因此它是不言自明的。
在 Eclipse IDE 中,“JUnit”窗格清楚地显示了测试用例“ScreenshotTest.java
”已通过,并且控制台没有错误。
如代码中所指定,屏幕快照以上述格式的名称保存在“E:/Selenium/screenshots
”路径中。
这是捕获的屏幕截图,
现在是时候在您的测试过程中实现这一点并为自己可视化魔术了。
祝屏幕截图愉快!😉
10G 高级 WebDriver – 将屏幕截图保存到 Word 文档
嗨冠军! 希望您度过愉快的时光截屏并将其保存在本地。 今天,让我们看看如何创建 Word 文档,并将在测试用例中捕获的所有图像插入其中。 每个测试用例都有一个单独的文档,不仅可以帮助我们保持工作空间井井有条,而且搜索特定的屏幕快照也很容易。 最好的部分是,我们将编写代码,以便所有这些事情自动发生而无需任何人工干预。
请允许我直截了当。
步骤 1:
下载几个 JAR,使我们的工作更加轻松。
java2word-3.3.jar
- 帮助我们创建 Word 文档并以所需方式对其进行操作。
xstream-1.3.1.jar
– 处理图片
让我们继续从 https://code.google.com/archive/p/java2word/downloads (在撰写本文时的下载位置)下载这两个 JAR 文件。 我还将这些 JAR 以及本文中处理的所有其他代码文件一起放在我们的 GitHub 仓库中。
步骤 2:
将这些 JAR 添加到我们的项目构建路径中。
之前,我们已经多次看到此过程,因此,我不再重复(有关详细说明,请参阅文章的步骤 3)。
步骤 3:
创建一个类“SaveDocument.java
”。
将以下代码添加到类文件中,
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import word.api.interfaces.IDocument;
import word.w2004.Document2004;
import word.w2004.Document2004.Encoding;
import word.w2004.elements.BreakLine;
import word.w2004.elements.Heading1;
import word.w2004.elements.Heading3;
import word.w2004.elements.Image;
import word.w2004.elements.Paragraph;
import word.w2004.style.HeadingStyle.Align;
public class SaveDocument {
public static void createDoc(String testCaseName, String[] imgFileNames) {
// Create a document object
IDocument myDoc = new Document2004();
myDoc.encoding(Encoding.UTF_8);
// Inserts one breakline
myDoc.addEle(BreakLine.times(1).create());
// Add client logo to document header
myDoc.getHeader().addEle(Image.from_FULL_LOCAL_PATHL("/Selenium/Logo.png")
.setHeight("30")
.setWidth("20")
.getContent());
// Add Project name to document header
myDoc.getHeader().addEle(Heading3.with(" ProjectName").withStyle().align(Align.RIGHT).create());
// Specify Test case name as document heading
myDoc.addEle(Heading1.with(testCaseName + " Test Case").withStyle().align(Align.CENTER).create());
myDoc.addEle(BreakLine.times(1).create());
// Add a description paragraph
myDoc.addEle(Paragraph
.with("Following are the related screenshots")
.create());
myDoc.addEle(BreakLine.times(1).create());
// Add company name to document footer
myDoc.getFooter().addEle(
Paragraph.with("@CompanyName").create());
// Loop through all the image files
for(String file:imgFileNames){
// Insert each image file to the document
myDoc.addEle(Image.from_FULL_LOCAL_PATHL(
"/Selenium/screenshots/" + file + ".png")
.setHeight("350")
.setWidth("500")
.getContent());
// Insert 2 linebreaks at the end of each inserted image
myDoc.addEle(BreakLine.times(2).create());
}
// Insert an image from web
myDoc.addEle(Image
.from_WEB_URL("http://www.google.com/images/logos/ps_logo2.png"));
// Create the word document specifying a location
File fileObj = new File("\\Selenium\\" + testCaseName + ".doc");
PrintWriter writer = null;
try {
writer = new PrintWriter(fileObj);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
String myWord = myDoc.getContent();
writer.println(myWord);
writer.close();
// Print a confirmation image to console
System.out.println("Word document created successfully!");
}
}
每行都提供了注释,以使代码易于说明。
public static void createDoc(String testCaseName, String[] imgFileNames)
-此方法有两个参数。 第一个是一个字符串,它指定测试用例的名称。 这将是将要创建的单词文档的名称。 第二个参数是作为该测试用例的一部分捕获的所有屏幕快照名称的数组。
在这种方法中,我们将创建一个文档对象并执行诸如
- 添加标题,段落
- 在标题中添加客户徽标和公司名称
- 在页脚中添加公司名称
- 插入作为特定测试用例一部分捕获的所有屏幕截图
- 从网上插入图片(只是为了证明这种情况也是可行的)
- 将单词文档和测试用例的名称保存在特定位置
步骤 4:
对“SaveScreenshot.java
”文件进行了一些修改。
对我们在先前文章中创建的“SaveScreenshot.java
”类进行了一些更改。
- 删除生成时间戳的函数,并
- 文件扩展名从“
.jpg
”更改为“.png
”
现在的代码看起来像这样,
import java.io.File;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
public class SaveScreenshot {
public static void capture(String screenshotName, WebDriver driver) {
// Cast driver object to TakesScreenshot
TakesScreenshot screenshot = (TakesScreenshot) driver;
// Get the screenshot as an image File
File src = screenshot.getScreenshotAs(OutputType.FILE);
try {
// Specify the destination where the image will be saved
File dest = new File("\\Selenium\\screenshots\\" + screenshotName + ".png");
// Copy the screenshot to destination
FileUtils.copyFile(src, dest);
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
}
示例场景
- 打开 Firefox 浏览器。
- 导航到 Google 帐户创建页面
- 通过 ID 找到名字文本框
- 输入“
fname01
”作为名字 - 截取屏幕截图,并将其命名为“
testCaseName+1
” - 按名称找到姓氏文本框
- 输入“
lname01
”作为姓氏 - 截取屏幕截图并将其命名为“
testCaseName+2
” - 在指定位置创建一个 word 文档,并将这两个屏幕截图都插入其中。
JUnit 代码:
WordDocWithScreenshotTest.java
类
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import com.blog.utilities.SaveDocument;
import com.blog.utilities.SaveScreenshot;
public class WordDocWithScreenshotTest {
//Declaring variables
private WebDriver driver;
private String baseUrl;
private String testCaseName = "WordDocWithScreenshot";
@Before
public void setUp() throws Exception{
// Selenium version 3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver", "E:\\Softwares\\"
+ "Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe");
// Create a new instance for the class FirefoxDriver
// that implements WebDriver interface
driver = new FirefoxDriver();
// Implicit wait for 5 seconds
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// Assign the URL to be invoked to a String variable
baseUrl = "https://accounts.google.com/SignUp";
}
@Test
public void testPageTitle() throws Exception{
// Open baseUrl in Firefox browser window
driver.get(baseUrl);
// Locate First Name text box by id and
// assign it to a variable of type WebElement
WebElement firstName = driver.findElement(By.id("firstName"));
// Clear the default placeholder or any value present
firstName.clear();
// Enter/type the value to the text box
firstName.sendKeys("fname01");
//Take a screenshot
SaveScreenshot.capture(testCaseName + "1", driver);
// Locate last name text box by name
WebElement lastName = driver.findElement(By.name("lastName"));
// Clear and enter a value
lastName.clear();
lastName.sendKeys("lname01");
// Take a screenshot
SaveScreenshot.capture(testCaseName + "2", driver);
// Create a word document and include all screenshots
SaveDocument.createDoc(testCaseName, new String[]{testCaseName + "1",testCaseName + "2"});
}
@After
public void tearDown() throws Exception{
// Close the Firefox browser
driver.close();
}
}
如果遵循注释,该代码是不言自明的。 Eclipse 的输出如下,
在 Eclipse IDE 中,“JUnit”窗格清楚地显示了测试用例“WordDocWithScreenshotTest.java
”已通过,并且控制台没有错误。 按预期打印“Word 文档创建成功”。
按照代码中的指定,屏幕快照将以上述格式的名称保存到“E:/Selenium/screenshots
”路径中。
还将创建单词文档并将其保存在指定的位置“ E:/Selenium/
”中。 该文件如下所示,
创建的 Word 文档,所有代码文件和 JAR 文件都放置在 GitHub 仓库中,以便于访问。 您可以为仓库加注星标和分支以方便使用。 请仔细阅读“README.md
”文件以获取明确说明。
编码愉快! 祝你今天愉快!
10H 高级 WebDriver – 发送带有附件的电子邮件
原文: https://javabeginnerstutorial.com/selenium/10h-advanced-webdriver-sending-email-with-attachments/
嗨冠军! 现在我们已经有了 PDF 格式的 JUnit 报告,让我们将其附加到电子邮件中并发送给各个项目利益相关者。 因此,今天我们主要将仅使用 Java。 大家都来一杯咖啡(Java)!
我们将研究两个类。
SendMail.java
– 此类包含用于发送电子邮件的所有代码。InvokeMail.java
– 通过提供从地址到地址,主题行和一些文本来调用SendMail.java
。
代码
SendMail.java
package com.blog.utility;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
/*
* This class has the main code for sending mail
*/
public class SendMail {
public static void send(String from, String tos[], String subject,
String text) throws MessagingException {
// Get the session object
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.socketFactory.port", "465");
props.put("mail.smtp.socketFactory.class",
"javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.port", "465");
Session session = Session.getDefaultInstance(props,
new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(
"[[email protected]](/cdn-cgi/l/email-protection)",
"pass1234");// change accordingly
}
});
// compose message
try {
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));// change accordingly
for (String to : tos) {
message.addRecipient(Message.RecipientType.TO,
new InternetAddress(to));
}
/*
* for (String cc : ccs)
* message.addRecipient(Message.RecipientType.CC,new
* InternetAddress(cc));
*/
message.setSubject(subject);
// Option 1: To send normal text message
// message.setText(text);
// Option 2: Send the actual HTML message, as big as you like
// message.setContent("<h1>This is actual message</h1></br></hr>" +
// text, "text/html");
// Set the attachment path
String filename = "E:\\Selenium\\junit.pdf";
BodyPart objMessageBodyPart = new MimeBodyPart();
// Option 3: Send text along with attachment
objMessageBodyPart.setContent(
"<h1>Mail from Selenium Project!</h1></br>" + text, "text/html");
Multipart multipart = new MimeMultipart();
multipart.addBodyPart(objMessageBodyPart);
objMessageBodyPart = new MimeBodyPart();
DataSource source = new FileDataSource(filename);
objMessageBodyPart.setDataHandler(new DataHandler(source));
objMessageBodyPart.setFileName(filename);
multipart.addBodyPart(objMessageBodyPart);
message.setContent(multipart);
// send message
Transport.send(message);
System.out.println("message sent successfully");
} catch (MessagingException e) {
throw new RuntimeException(e);
}
}// End of SEND method
}
InvokeMail.java
package com.blog.junitTests;
import javax.mail.MessagingException;
import com.blog.utility.SendMail;
/*
* Invokes SendMail.java
*/
public class InvokeMail {
public static void main(String[] args) throws MessagingException {
//String to[] = {"[[email protected]](/cdn-cgi/l/email-protection)","[[email protected]](/cdn-cgi/l/email-protection)"};
String to[] = {"[[email protected]](/cdn-cgi/l/email-protection)"};
SendMail.send("[[email protected]](/cdn-cgi/l/email-protection)", to, "JUnit Report", "Check the PDF attachment.");
}
}
解释
直接看一下代码会使我们感到难以接受。 让我们一次了解一个摘要。
与往常一样,我们的第一步是下载几个 JAR。
activation.jar
javax.mail-1.6.1.jar
我还将它们都放在了我们的 GitHub 仓库中,以及作为本文一部分处理的所有其他代码文件中。
将这些 JAR 添加到我们的项目构建路径中。 我们之前已经多次看到此程序,因此我不再重复(有关详细说明,请参阅此文章的步骤 3)。
了解SendMail.java
,
1.编写所有代码的方法,以便我们可以从任何类轻松地调用它。
public static void send(String from, String tos[], String subject,
String text) throws MessagingException {}
2.给定的属性仅适用于 Gmail。 如果您根据项目要求使用 Outlook 或任何其他服务,则应相应地进行更改。
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.socketFactory.port", "465");
props.put("mail.smtp.socketFactory.class",
"javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.port", "465");
3.获取会话对象并传递您的电子邮件帐户的凭据(您在地址中提到的凭据)。
Session session = Session.getDefaultInstance(props,
new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("[[email protected]](/cdn-cgi/l/email-protection)","pass1234");// change accordingly
}
});
4.现在是有趣的部分。 我们将指定“发件人”和“收件人”地址。
message.setFrom(new InternetAddress(from));
for (String to : tos) {
message.addRecipient(Message.RecipientType.TO,
new InternetAddress(to));
}
如果您只想将此电子邮件发送给一个人,请更改以下代码,
message.setFrom(new InternetAddress(from));
message.addRecipient(Message.RecipientType.TO,
new InternetAddress(to));
如果您希望将其发送给抄送给某些人的用户,请按以下所示更改代码,
message.addRecipient(Message.RecipientType.CC,new InternetAddress(cc));
5.将主题行设置为message.setSubject(subject);
6.要发送,
- 普通短信
message.setText(text);
- 实际的 HTML 消息尽可能多
message.setContent("<h1>This is actual message</h1></br></hr>" + text, "text/html");
- 文本和附件(这就是我们想要的),
// Set the attachment path
String filename = "E:\\Selenium\\junit.pdf";
BodyPart objMessageBodyPart = new MimeBodyPart();
objMessageBodyPart.setContent("<h1>Mail from Selenium Project!</h1></br>" + text, "text/html");
Multipart multipart = new MimeMultipart();
multipart.addBodyPart(objMessageBodyPart);
objMessageBodyPart = new MimeBodyPart();
DataSource source = new FileDataSource(filename);
objMessageBodyPart.setDataHandler(new DataHandler(source));
objMessageBodyPart.setFileName(filename);
multipart.addBodyPart(objMessageBodyPart);
message.setContent(multipart);
7.用简单的一行发送电子邮件,
Transport.send(message);
了解InvokeMail.java
,
此类很容易理解,因为我们只是通过提供所有必需的参数从SendMail.java
调用“send
”方法。
String to[] = {"[[email protected]](/cdn-cgi/l/email-protection)"};
SendMail.send("[[email protected]](/cdn-cgi/l/email-protection)", to, "JUnit Report", "Check the PDF attachment.");
在“运行方式-> Java 应用”下,Eclipse IDE 控制台的输出如下所示,
该电子邮件将在收件人的收件箱中接收。
显示生成的带有附件的电子邮件,以供您参考。
注意: 当心! 您可能碰到“javax.mail.AuthenticationFailedException
”。 发生此异常的主要原因是 Google 提供的安全性和保护功能。 一种简单的解决方法是,通过单击链接 https://www.google.com/settings/security/lesssecureapps 来打开“允许安全程度较低的应用”的访问以进行测试。
试试看,让我知道在拍摄电子邮件时是否遇到任何问题。
实验愉快! 祝你今天愉快!
10I 高级 WebDriver – 使用属性文件
原文: https://javabeginnerstutorial.com/selenium/10i-advanced-webdriver-property-files/
嗨呀冠军! 欢迎回到另一篇有趣的文章,它告诉您为什么属性文件首先存在! 这个特定的概念不仅限于 Selenium WebDriver 项目。 它基本上可以用于任何涉及硬编码内容的 Java 项目中。
假设您有数百个自动化测试用例,并且在每个测试用例中,您都对要测试的应用的 URL 进行了硬编码。 到目前为止,一切都很好。 但是,如果弹出另一个版本,将应用 URL 更改为另一个版本,并且要再次执行相同的测试用例(回归测试),该怎么办? 您可能会想,“那很简单! 我只需要再运行一次构建文件。 只需单击一下,我就可以玩超级马里奥了!”。 您是否错过了 URL 部分? 手动转到每个测试用例并编辑硬编码的 URL 以使其正常工作又会带来多大的痛苦呢? 不好了!!!
别担心! 我们有一个解决方案。 财产文件将为我们提供帮助! 为了使您的测试用例更加动态,请确保不要在其中放入任何硬编码的值。 将这些值抽象到属性文件中,以便每当它们更改时,您都可以在一个位置进行编辑,并且测试用例可以再次完美地工作。
事不宜迟,让我们仅通过三个小步骤就可以了解如何在我们的项目中实现此目标,
步骤 1:
右键单击项目->新建->包。 确保“源文件夹”显示您的项目名称,并将包名称设为“资源”。
现在,右键单击“资源包->新建->文件”。 让文件名是“config.properties
”。
步骤 2:
现在是时候从测试用例中提取所有这些硬编码的值了。
在“config.properties
”文件中,将所有必需的属性作为键值对。 这将帮助我们在测试用例中引用每个属性及其键,该属性将在一分钟内演示。 可以在这个特定文件中对值进行任何更改,并且这些更改将神奇地反映在进行引用的所有测试用例中。
步骤 3:
为了在测试案例中使用这些属性,
Properties props = new Properties();
声明“属性”类型的“属性”变量。 这将创建一个没有默认值的空属性列表。
这需要从java.util
包导入import java.util.Properties;
FileInputStream fis = new FileInputStream("resources//config.properties");
–
创建一个连接以从“resources
”包下的“config.properties
”文件中读取所有属性。
这也需要从java.io
包中导入import java.io.FileInputStream;
props.load(fis);
- 使用打开的连接“fis
”从输入字节流中读取所有属性。
props.getProperty("baseURL");
– 要获取特定属性的值,请使用“getProperty
”方法。 将在双引号中提及相应的键作为 getProperty()
方法的参数。 它使用属性列表中的指定键搜索属性。 如果找不到键,则返回null
。
概览
让我们看一个测试案例,实现到目前为止所涵盖的概念,
场景
- 打开 Firefox 浏览器。
- 从属性文件中读取 firefox 驱动程序路径和基本 URL。
- 导航到演示站点
- 按名称找到“三轮车”复选框,然后将相应的消息打印到控制台
- 检查“三轮车”复选框是否已启用,并将相应消息打印到控制台
- 根据“货车”和“SUV”复选框的当前选择状态,选中或取消选中并打印执行
click()
动作前后的状态 - 使用 XPath 找到“轿车”复选框
- 使用两次迭代在选择状态和取消选择状态之间切换
- 使用
cssSelector
找到“杂志”单选按钮 - 检查是否默认选中
- 如果是,则将相应消息打印到控制台,如果否,请选择单选按钮
验证 Eclipse IDE 控制台输出屏幕和 JUnit 窗格是否成功
此方案的 JUnit 代码
Config.properties
#Properties as key-value pairs
baseUrl=https://chandanachaitanya.github.io/selenium-practice-site/
logoPath=E:\\Selenium\\Logo.png
gmail=tester01@gmail.com
pdfReportPath=E:\\Selenium\\junit.pdf
firefoxPath=E:\\Softwares\\Selenium\\geckodriver-v0.10.0-win64\\geckodriver.exe
chromePath=browser-drivers\\chromedriver.exe
IEPath=browser-drivers\\IEDriverServer.exe
RadioBtns_Checkboxes.java
package com.blog.junitTests;
import java.io.FileInputStream;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
public class RadioBtns_Checkboxes {
// Declaring variables
private WebDriver driver;
private String baseUrl;
Properties props;
@Before
public void setUp() throws Exception {
// Creates an empty property list
props = new Properties();
// A connection is created to config.properties file
FileInputStream fis = new FileInputStream("resources//config.properties");
// Reads the properties from the input byte stream
props.load(fis);
// Get the firefox driver path from property file
String firefoxPath = props.getProperty("firefoxPath");
// Assign the URL to be invoked to a String variable
baseUrl = props.getProperty("baseUrl");
// Mention the property where required
System.setProperty("webdriver.gecko.driver", firefoxPath);
// Create a new instance for the class FirefoxDriver
// that implements WebDriver interface
driver = new FirefoxDriver();
// Implicit wait for 5 seconds
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
}
@Test
public void testPageTitle() throws Exception {
// Open baseUrl in Firefox browser window
driver.get(baseUrl);
// Locate 'Tricycle' checkbox using name
WebElement tricycleCheckbox = driver.findElement(By.name("vehicle2"));
// Check if tricyle is displayed
System.out.println("Is tricycle displayed? "+ tricycleCheckbox.isDisplayed());
// Check if tricyle is enabled to select
if (tricycleCheckbox.isEnabled()) {
// Click if enabled
tricycleCheckbox.click();
} else {
// Print message to console if disabled
System.out.println("Unable to select 'Tricycle' checkbox as it is disabled.");
}
//Get all checkbox elements in a list
List<WebElement> list = driver.findElements(By
.cssSelector("input[type='checkbox']"));
// Loops through all checkboxe elements
for (int i = 0; i < list.size(); i++) {
// Checking if the checkbox is a 'Van' or 'SUV'
if ((list.get(i).getAttribute("value").trim()
.equalsIgnoreCase("van"))
|| (list.get(i).getAttribute("value").trim()
.equalsIgnoreCase("suv"))) {
// Print selection status to console
System.out.println("BEFORE: Is "
+ list.get(i).getAttribute("value") + " selected? "
+ list.get(i).isSelected());
// Check if the checkbox is selected
if (!(list.get(i).isSelected())) {
// Click the checkbox
list.get(i).click();
System.out.println("AFTER: Is "
+ list.get(i).getAttribute("value") + " selected? "
+ list.get(i).isSelected());
} else {
// Uncheck the checkbox
list.get(i).click();
System.out.println("AFTER: Is "
+ list.get(i).getAttribute("value") + " selected? "
+ list.get(i).isSelected());
}
System.out.println("Next...");
}
}
// Locate 'Sedan' checkbox using xPath
WebElement sedanCheckbox = driver.findElement(By
.xpath("//input[@name='vehicle5']"));
System.out.println("Trying to select and de-select Sedan checkbox...");
for (int i = 0; i < 2; i++) {
// Click the checkbox
sedanCheckbox.click();
// Print current status to console
System.out.println("Selection status of 'Sedan' checkbox: "
+ sedanCheckbox.isSelected());
}
// Locate 'Magazines' radio button using cssSelector
WebElement magazinesRadioBtn = driver.findElement(By
.cssSelector("input[value='Magazines']"));
// Check if radio button is selected by default
if (magazinesRadioBtn.isSelected()) {
// Print message to console
System.out.println("Magazines radio button is selected by default");
} else {
// Click the radio button
magazinesRadioBtn.click();
}
} //End of @Test
@After
public void tearDown() throws Exception {
// Close the Firefox browser
driver.close();
}
}
输出
每行代码都提供了清晰的注释,使其易于说明。 执行测试用例后,eclipse IDE 控制台窗口的输出如下,
属性文件使自动化测试人员的生活成真,梦想成真。 由此证明! 现在该尝试使用今天的概念了。 请务必戴好安全帽,以免碰到异常! 还有一件事,我可以在 GitHub 仓库中找到所有代码文件。 去看一下!
再见! 祝你有美好的一天!
10J 高级 WebDriver – 使用 POI 从 excel 读取数据
原文: https://javabeginnerstutorial.com/selenium/10j-advanced-webdriver-reading-data-from-excel-using-poi/
朋友! 今天,让我们深入研究 excel 表并了解如何从中读取数据。 作为自动化和构建自动化框架的一部分,我们倾向于将数据以预定义的格式(通常遵循模板)存储在 excel 表中。 我们存储的数据主要是测试数据,不同的测试 URL,发布特定的参数等。在这种情况下,知道如何在我们的代码中处理 excel 表就变得非常重要。
这将是另一篇纯 Java 文章。 因此,您该喝一杯咖啡(Java)了!! 我们将使用 POI jar 来实现此目的。
步骤 1:
与往常一样,我们的第一步是下载所需的 POI JAR。 转至 Apache POI ,然后下载最新稳定版本的二进制发行版(在撰写本文时,3.17 是最新稳定发行版)。 单击该 zip 文件的二进制版本,重定向到实际的下载页面。
步骤 2:
将这些 JAR 添加到我们的项目构建路径中。 确保选择“poi-x.xx
”,“ooxml-lib
”和“lib
”文件夹下的所有 JAR。 我还将这些以及其他所有代码文件都放在了我们的 GitHub 仓库中。
我们之前已经多次看到这种添加 JAR 来构建路径过程的内容,因此我没有在重复它(有关详细说明,请参阅此文章的步骤 3)。
步骤 3:
创建一个新类“ExcelOperationsUsingPOI.java
”。 在此类中,让我们有一种从特定位置读取 excel 文件的特定图纸的方法。
- 通过传递您要打开的 excel 文件的完整文件路径来创建
File
类的对象 -File file = new File(filePath+"\\"+fileName);
- 下一步是创建一个
FileInputStream
对象,以获取 excel 文件的输入字节 -FileInputStream inputStream = new FileInputStream(file);
- 创建一个工作簿对象 -
Workbook myWorkbook = null;
- Excel 文件在大多数情况下可以具有两个扩展名。 “
.xls
”或“.xlsx
”。 通过使用子字符串方法拆分文件名来找到扩展名,并相应地创建Workbook
对象。
//indexOf gives the index of . in the file name
//substring method splits the string starting from index of . to the end
String fileExtensionName = fileName.substring(fileName.indexOf("."));
//Check condition if the file is xlsx file
if(fileExtensionName.equals(".xlsx")){
//If it is xlsx file then create object of XSSFWorkbook class
myWorkbook = new XSSFWorkbook(inputStream);
}
//Check condition if the file is xls file
else if(fileExtensionName.equals(".xls")){
//If it is xls file then create object of HSSFWorkbook class
myWorkbook = new HSSFWorkbook(inputStream);
}
- 使用传递的确切工作表名称,可以读取特定工作表 -
Sheet mySheet = myWorkbook.getSheet(sheetName);
现在,使用行和列很容易,它们的交点将为我们提供我们希望读取的单元格内容。
现在让我们来看一下实现到目前为止讨论的全部功能的代码,
ExcelOperationsUsingPOI.java
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class ExcelOperationsUsingPOI {
public static void readExcel(String filePath,String fileName,String sheetName) throws IOException{
//Create a object of File class to open xlsx file
File file = new File(filePath+"\\"+fileName);
//Create an object of FileInputStream class to read excel file
FileInputStream inputStream = new FileInputStream(file);
Workbook myWorkbook = null;
//Find the file extension by spliting file name in substring and getting only extension name
//indexOf gives the index of . in the file name
//substring method splits the string starting from index of . to the end
String fileExtensionName = fileName.substring(fileName.indexOf("."));
//Check condition if the file is xlsx file
if(fileExtensionName.equals(".xlsx")){
//If it is xlsx file then create object of XSSFWorkbook class
myWorkbook = new XSSFWorkbook(inputStream);
}
//Check condition if the file is xls file
else if(fileExtensionName.equals(".xls")){
//If it is xls file then create object of HSSFWorkbook class
myWorkbook = new HSSFWorkbook(inputStream);
}
//Read sheet inside the workbook by its name
Sheet mySheet = myWorkbook.getSheet(sheetName);
//Find number of rows in excel file
int rowCount = mySheet.getLastRowNum()- mySheet.getFirstRowNum();
//Create a loop over all the rows of excel file to read it
for (int i = 0; i < rowCount+1; i++) {
Row row = mySheet.getRow(i);
//Create a loop to print cell values in a row
for (int j = 0; j < row.getLastCellNum(); j++) {
//Print excel data in console
System.out.print(row.getCell(j).getStringCellValue()+"|| ");
}
System.out.println();
}
}
}
ReadExcelData.java
用于调用readExcel
方法并传递必需的参数。
import java.io.IOException;
import com.blog.utility.ExcelOperationsUsingPOI;
public class ReadExcelData {
public static void main(String[] args) {
try {
ExcelOperationsUsingPOI.readExcel("E:\\Selenium", "ReadUsingPOI.xlsx", "Demographics");
} catch (IOException e) {
e.printStackTrace();
}
}
}
注释使代码不言自明。 所考虑的 Excel 工作表中的数据如下所示,
使用我们的代码访问此信息将按预期方式打印出所有用管道分隔的值,以便将其控制台。
如果您想检索代码段,请在评论部分留言,
- 给定条目的从零开始的行和列索引
- 使用给定的从零开始的行和列索引的值
- 基于给定条目的列表中的所有行值
- 基于给定条目的列表中的所有列值
试用这些功能,让我知道您是否遇到颠簸。
祝你今天愉快!
10K 高级 WebDriver – 使用 Log4j 第 1 部分
原文: https://javabeginnerstutorial.com/selenium/10k-advanced-webdriver-using-log4j-part-1/
现在让我来解释一下使用 Apache Log4j 记录 Selenium 的过程! 因此,事不宜迟,让我们开始吧!
为什么我们需要日志?
作为自动化测试人员,我们负责故障排除,调试等。 记录使整个过程变得非常容易! 它带给我们这些神奇的“眼睛”,让我们看到许多事物,例如,
- 测试用例实际上在做什么
- 应用如何响应我们的代码
- 使用自定义错误消息正确记录时,可以将异常,问题,失败,错误等追溯到其根源
- 可以作为以后成功执行的证明,因为它们可以轻松地保存到数据库或带有时间戳的外部文件中
- 所有这些日志都可以在无需人工干预的情况下生成
现在我们了解了将日志语句插入测试用例的重要性,让我们解读一下 Log4j 的全部含义。
什么是 Log4j
- 1996 年开发的基于 Java 的流行日志记录包
- 根据 Apache Software License 分发,因此它是开源的
- 具有各种级别的日志记录 – 允许我们控制日志输出的数量
- 一切都可以通过编辑简单的配置文件来控制-无需理会应用二进制文件
Log4j 日志级别
以下是 Log4j 附带的内置日志级别。
OFF
– 关闭登录FATAL
– 严重错误导致应用终止ERROR
– 意外情况和运行时错误WARN
– 警告消息INFO
– 提供有关一些有趣的运行时事件的信息DEBUG
– 最常用的日志级别,提供有关系统流程的详细信息TRACE
- 信息的详细程度最高
Log4j 组件
主要包括三个部分:
-
记录器
应用中用唯一名称标识的记录器可以有多个。 可以将每个记录器配置为特定级别-调试,信息,错误等。
要求我们创建记录器类的实例并指定日志级别。
-
附加器
创建记录器实例后,我们必须知道在哪里查看生成的日志。 这就是附加器出现的地方。 它们指定消息要记录到的目的地或输出,例如文件,标准输出或另一台计算机等。一些可用的附加器是FileAppender
,RollingFileAppender
,ConsoleAppender
,SocketAppender
等。 可以将同一信息记录到多个输出中,即一个记录器可以有多个附加器。
-
布局
既然我们知道如何生成日志并将其保存到特定的目的地,那么我们可能需要将它们呈现为不同的格式。 每个附加器必须映射到特定布局。 一些常用的布局是
- PatternLayout – 使用模式字符串的一次一行日志文件
- HTMLLayout – 以 HTML 格式呈现日志
- XMLLayout – 生成 XML 格式
Log4j 配置
可以在 XML,JSON,YAML 或属性文件格式的配置文件中配置这三个组件。 在本文中,我们将看到如何使用属性文件格式定义所有组件和记录消息。
环境设定
在 Eclipse IDE 中创建一个 Java 项目,在本例中为“Selenium”。
步骤 1:下载 Log4j
第一步(也是第一步)是从此处下载 Apache Log4j JAR。 单击镜像的 zip 文件格式将导航到一个新页面,其中包含实际的镜像站点详细信息,可从该位置将 JAR 下载到本地计算机。
将下载文件夹的内容提取到特定位置。
步骤 2:配置 Java 构建路径
将 Log4j JAR 作为外部库添加到项目的构建路径。
让我们导航到本地计算机中提取下载的 JAR 的路径,并将其添加为外部 JAR。
之前,我们已经多次看到此过程,因此,我不再重复(请参阅文章的步骤 3)。
步骤 3:建立必要的档案
我们将创建 3 个文件。
Log4j.properties
- 右键单击“
src
文件夹->新建->其他->常规->文件->下一页” - 提供“文件名”作为“
Log4j.properties
”,然后点击“完成”
结果看起来像这样,
我现在已经忽略了三个额外的包,因为我出于编码目的创建了它们。
日志文件
- 右键单击“
src
文件夹->新建->其他->常规->文件夹->下一页” - 提供“文件夹名称”作为“
resource
” - 右键点击“
resource
”文件夹并创建两个文件 - 将这些文件命名为“
system.log
”和“test.log
”
system.log
– 将具有系统生成的所有日志test.log
– 将包含由于用户提供的手动命令而生成的所有日志
现在,所有环境都已准备就绪,我们可以使用 Log4j 进行一些实验并生成日志。 与往常一样,将 JAR 放置在 GitHub 仓库中。 让我们在即将发布的帖子中详细了解这一点。
10L 高级 WebDriver – 使用 Log4j 第 2 部分
原文: https://javabeginnerstutorial.com/selenium/10l-advanced-webdriver-using-log4j-part-2/
这是本文的续篇,使用 Log4j 第 1 部分,因此,我建议您先阅读第 1 部分,然后再继续进行操作,以掌握发生的情况。
场景
- 配置
Log4j.properties
文件,- 日志级别 – 调试
- 记录器 –
rootLogger
和SeleniumTestLogger
- 附加器 –
RollingFileAppender
- 布局 –
PatternLayout
- 编写一个 JUnit4 测试用例“
Log4jTest.java
”,- 开启 Chrome 浏览器。
- 导航到演示站点
- 为
Logger
类创建一个实例 - 将“打开 Selenium 实践网站”登录到
test.log
- 按名称找到“自行车”复选框,然后单击它
- 记录“选择了自行车复选框”
- 使用
cssSelector
找到“杂志”单选按钮并选择它 - 记录“单击了杂志单选按钮”
- 日志“Log4jTest 成功执行”
- 校验,
- Eclipse IDE 控制台输出屏幕
- JUnit 窗格可显示成功结果
- 日志和
test.log
文件,并检查日志是否按预期更新
现在,我们今天的计划已成问题,让我们开始编写代码。
步骤 1:配置属性文件
首先,让我们看看 Log4j 的配置文件Log4j.properites
文件中包含什么代码。
#Root logger options
log4j.rootLogger=debug,file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=E:\\Selenium\\resources\\system.log
log4j.appender.file.maxFileSize=900KB
log4j.appender.file.maxBackupIndex=3
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L \u2013 %m%n
log4j.appender.file.Append=false
#Application Logs
log4j.logger.SeleniumTestLogger=DEBUG, dest
log4j.appender.dest.File=E:\\Selenium\\resources\\test.log
log4j.appender.dest=org.apache.log4j.RollingFileAppender
log4j.appender.dest.maxFileSize=500KB
log4j.appender.dest.maxBackupIndex=6
log4j.appender.dest.layout=org.apache.log4j.PatternLayout
log4j.appender.dest.layout.ConversionPattern=%d{dd/MM/yyyy HH:mm:ss} %c %m%n
log4j.appender.dest.Append=false
您现在正在寻找所有希腊语和拉丁语吗? 不用担心,让我们一次看看它。
我们将有两个记录器,
rootLogger
– 处理系统生成的日志,并将它们输出到system.log
文件,然后SeleniumTestLogger
– 处理由于用户手动插入代码而生成的日志,并输出到test.log
文件
这两个记录器都将具有RollingFileAppender
和PatterLayout
。
log4j.rootLogger=debug,file
– 日志级别指定为debug
,file
用作引用此特定记录器的标识符。
log4j.appender.file=org.apache.log4j.RollingFileAppender
– RollingFileAppender
是使用的附加程序类型,它将指定的文件附加到最大大小。
log4j.appender.file.File=E:\\Selenium\\resources\\system.log
– File
用于指定要保存日志的文件的位置,即目的地。
log4j.appender.file.maxFileSize=900KB
– 一个文件最多可以存储 900KB 的数据,然后创建一个具有相同名称的新文件。 较旧的文件将作为索引添加到最新的文件。
log4j.appender.file.maxBackupIndex=3
– 最多将保存三个文件作为备份。
log4j.appender.file.layout=org.apache.log4j.PatternLayout
– Pattern layout
用于格式化生成的日志。
log4j.appender.dest.layout.ConversionPattern=%d{dd/MM/yyyy HH:mm:ss} %c %m%n
– 这是用于生成布局的转换模式。
生成的示例测试日志如下(片段),
16/05/2019 22:18:17 SeleniumTestLogger Log4jTest executed successfully
dd/MM/yyyy
– 日期HH:mm:ss
– 执行时间%c
– 打印作为参数传递给Logger
实例的名称%m%n
– 日志消息
log4j.appender.file.Append=false
– 将此属性设置为false
将创建一个新文件,而不是更新现有文件。
log4j.logger.SeleniumTestLogger=DEBUG, dest
– 日志级别为debug
,dest
是此处使用的标识符。
log4j.appender.dest.File=E:\\Selenium\\resources\\test.log
– 借助File
来指定dest
标识符的文件位置。
其他属性与我们已经讨论过的相似,因此不言自明。
步骤 2:编写测试用例
下面是测试用例“Log4jTest.java
”,涵盖了开头讨论的场景中列出的所有要求。
package com.blog.junitTests;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
public class Log4jTest {
// Declaring variables
private WebDriver driver;
private String baseUrl;
private Logger log;
@Before
public void setUp() throws Exception {
// System property set up for Chrome driver
System.setProperty("webdriver.chrome.driver", "browser-drivers\\chromedriver.exe");
// Create a new instance for the class ChromeDriver
// that implements WebDriver interface
driver = new ChromeDriver();
// Implicit wait for 5 seconds
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
// Assign the URL to be invoked to a String variable
baseUrl = "https://chandanachaitanya.github.io/selenium-practice-site/";
// Create a logger instance and pass Class name which is Log4jTest in this case
log = Logger.getLogger("SeleniumTestLogger");
}
@Test
public void testElementsWithLogging() throws Exception {
// Open baseUrl in Chrome browser window
driver.get(baseUrl);
log.debug("opening selenium-practice-site");
// Locate 'Bicycle' checkbox using name
WebElement bicycle = driver.findElement(By.name("vehicle1"));
// Click the checkbox
bicycle.click();
log.debug("Bicycle checkbox selected");
// Locate 'Magazines' radio button using cssSelector
WebElement magazinesRadioBtn = driver.findElement(By
.cssSelector("input[value='Magazines']"));
// Click the radio button
magazinesRadioBtn.click();
log.debug("Magazines radio button clicked");
log.debug("Log4jTest executed successfully");
} // End of @Test
@After
public void tearDown() throws Exception {
// Close the Firefox browser
//driver.close();
System.out.println("Closing the driver");
}
}
import org.apache.log4j.Logger;
– 记录包已导入。
log = Logger.getLogger("SeleniumTestLogger");
– 创建一个名为的实例,记录Logger
类并传递“SeleniumTestLogger
”作为参数。
log.debug("opening selenium-practice-site");
– 此语句将记录在DEBUG
级别中用双引号引起的消息。
每行代码都给出了清晰的注释,使其易于遵循。 如您所见,调试级别的日志语句插入到测试用例的各个位置。
步骤 3:验证
Eclipse IDE 输出屏幕显示,
- “控制台”没有任何错误,并且
- “JUnit”窗格,带有绿色栏,显示成功执行测试用例
* system.log 和 test.log 文件都将按预期方式通过属性文件布局中指定的系统日志和带有时间戳的用户编码日志进行更新。
代码文件,日志文件和相关的 JAR 一如既往地放置在 GitHub 仓库中。 我希望您现在已经了解了如何在 Selenium WebDriver 自动化的测试用例中利用流行的记录器之一。
祝你今天愉快!
10M 高级 WebDriver – 以无头模式运行测试
原文: https://javabeginnerstutorial.com/selenium/10m-advanced-webdriver-running-tests-in-headless-mode/
我敢肯定,您现在会听说无头,因为 Google 为其 Chrome 浏览器(从 59 版开始)引入了无头选项! Firefox 紧随其后。 它也可以在无头模式下运行!! 让我们在没有 Firefox 的情况下在 Firefox 中执行一些自动化的 Selenium 测试! 😉换句话说,测试在后台运行,并且没有显示(即,浏览器 UI 不显示给用户)。 该代码还将提供给 Chrome 浏览器。
您脑海中可能会出现的第一个问题是,为什么我首先需要一个无头的浏览器? 我收到你的来信。 不用担心,我有答案并且相信我,这些都是很好的答案。 因此,事不宜迟,以下是您无头的几个原因,
- 如果您的目标是获得更好的速度和性能,那么别无所求! 由于无需为每个测试都启动浏览器 UI,从而避免了加载 JavaScript,CSS 和呈现 HTML 所需的所有时间,因此测试运行速度更快。
- 通常,在 CI(连续集成)管道中,它需要在没有实际 GUI 的服务器或 Linux OS 等系统上运行。 无头模式节省了一天!
- 如果您所有的测试都运行了几个小时怎么办? 在这种情况下,用户必须从字面上看屏幕,让系统执行其工作,直到所有测试占据整个屏幕为止。 相反,如果以无头模式执行,则用户可以在后台运行测试时继续执行其他任务。
- 当脚本被开发并且稳定时,并不需要真正看到它们正在运行。 以更快的方式在无头模式下运行它们似乎很有意义。 借助日志,可以执行所需的调试。
- 并行执行 – 无头可阻止打开多个浏览器,并让您执行多任务。
- 无头模式也可以拍摄截图。 因此,一旦发生故障,便始终可以以所需的任何方式获取并存储快照。
但是,瞧! 有时候无头可能不是很有用。 例如,
- 在开发脚本时,在传统的浏览器中运行它们可以帮助我们直观地查看实际情况,从而使自动化的初始阶段的调试变得容易。
- 当模拟应用的真实用户时
- 可能无法捕获某些错误,例如图像崩溃或无法加载
话虽如此,让我们看一些工作代码!
Chrome 无头模式,
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
public class HelloWorld_ChromeHeadless {
public static void main(String[] args) {
// System property set up
System.setProperty("webdriver.chrome.driver", "browser-drivers\\chromedriver.exe");
// Add options to Google Chrome
ChromeOptions options = new ChromeOptions();
// Setting headless argument
options.addArguments("--headless");
// To test responsive websites
options.addArguments("window-size=1400,600");
// Create a new instance for the class ChromeDriver
// that implements WebDriver interface
WebDriver driver = new ChromeDriver(options);
// Assign the URL to be invoked to a String variable
String baseUrl = "https://www.google.com";
// Open baseUrl in IE browser window
driver.get(baseUrl);
// Print messages to console
System.out.println(driver.getTitle());
System.out.println("Hello World!");
// Close the IE browser
driver.quit();
}
}
Firefox 在无头模式下,
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
public class HelloWorldFirefox_Headless {
public static void main(String[] args) {
//Selenium version 3 beta releases require system property set up
System.setProperty("webdriver.gecko.driver", "browser-drivers\\geckodriver.exe");
FirefoxOptions options = new FirefoxOptions();
options.setHeadless(true);
//Create a new instance for the class FirefoxDriver
//that implements WebDriver interface
WebDriver driver = new FirefoxDriver(options);
//Assign the URL to be invoked to a String variable
String baseUrl = "https://www.google.com";
String pageTitle = "";
String expectedTitle = "Google";
//Open baseUrl in Firefox browser window
driver.get(baseUrl);
//Get the page title and assign to a String variable
pageTitle = driver.getTitle();
//Check if obtained page title matches with the expected title
//and print the console output accordingly
if(pageTitle.equals(expectedTitle)){
System.out.println("Hello World! Result is as expected.");
}else{
System.out.println("Hello World! Assertion failed!");
}
//Close the Firefox browser
driver.quit();
}
}
执行结果:
为每行代码提供了注释,使其易于说明。
控制台窗口显示没有任何错误。 它还显示测试以无头模式运行,并且所有消息均按预期方式打印。
尝试今天的技能,祝您好运。 我坚信这些在您的自动化旅程中的某些时候会很有用。
祝你有美好的一天!
下一篇文章
Vue 教程
1 使用 Vue.js 的 Hello World
原文: https://javabeginnerstutorial.com/js/vue-js/1-hello-world-with-vue-js/
嗨,摇滚明星(音乐不包括在内)! 激动地进入 Vue.js 世界? 那我们还等什么呢?
好消息:入门 Vue 很简单。
今天让我们保持目标非常简单。 遵循古老的传统,让我们旨在使用 Vue 在我们的网页上显示消息“Hello World
”。
环境设置
由于我们已决定使一切保持简单,因此为什么不使用 CDN,它基本上是从另一台服务器导入 Vue.js 的。 在本教程系列的后面,我们将使用 Vue CLI 和 Webpack 进行更复杂的设置来捆绑所有文件。 但是就目前而言,为了练习,理解和学习 Vue 的基础知识,CDN 会做。 是的,您也可以将这种简单的设置用于非常小的项目。
选项 1:
<!-- development version -->
<script src="https://cdn.jsdelivr.net/npm/[[email protected]](/cdn-cgi/l/email-protection)/dist/vue.js"></script>
<!-- production version -->
<script src="https://cdn.jsdelivr.net/npm/[[email protected]](/cdn-cgi/l/email-protection)"></script>
选项 2:
Vue 也可用于 unpkg,
<script src="https://unpkg.com/[[email protected]](/cdn-cgi/l/email-protection)/dist/vue.js"></script>
在撰写本文时,最新的版本是 2.5.16。 建议在 URL 中包含特定的版本号。 您可以手动将版本编辑为使用中的最新版本。
但是,如果您不想提及该版本并使用最新版本,请继续,
<script src="https://cdn.jsdelivr.net/npm/[[email protected]](/cdn-cgi/l/email-protection)/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<!-- or -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
选项 3:
您可以将 Vue.js 的开发或生产版本下载到本地,并使用 HTML 文件中的script
标签直接将其包含在内。
注意:开发版本包含控制台警告,这些警告对于调试非常有用。 而生产版本主要针对大小(通过使用 vue.js 文件的缩小版本等)和速度进行了优化,这对于在实时环境中发布非常重要。
初始代码
我使用 Visual Studio Code 作为 IDE。 您可以使用 Sublime Text,WebStorm,Atom,Notepad ++或您选择的任何其他 IDE。 我的建议? 请选择 JS Fiddle 标签。 这仅仅是锦上添花。 最好的部分是,您可以在一个屏幕上查看 HTML,CSS,JS 和输出。 与必须每次刷新一次以反映更改的浏览器不同,JS 小提琴会检测到更改并在“结果”窗格中自动呈现输出。
使用以下代码创建一个 HTML 文件“index.html
”,
<!DOCTYPE html>
<html>
<head>
<title>Hello Vue!</title>
<!-- including Vue with development version CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>Hello World!</h1>
</div>
</body>
</html>
代码很简单。 我们给页面命名为“Hello Vue!
”。 以及<head>
部分中使用带有<script>
标签的 CDN 的 vue.js 的开发版本。 我们正在显示“Hello World!
” <body>
部分中带有id=”app
的<div>
元素内<h1>
标签内的消息。
Chrome 浏览器中的当前输出如下,
到目前为止和我在一起? 好的,我听到您说我们已经渲染了“Hello World!
”带有<h1>
标签,但这与我们使用 Vue.js 进行显示的最初目标不矛盾吗? 好问题。
让我们现在回答
步骤 1:创建新的 Vue 实例
我们已经使用<script>
标签将 Vue.js 导入了我们的项目,但这并不能解决问题。 这相当于在我们的工作台上有一台笔记本电脑。 要打开它并完成一些工作,我们必须按下电源按钮。 同样,要在我们的项目中使用 Vue,我们必须使用new
关键字创建 Vue 对象的实例。 这个实例是 Vue 的电源按钮!
new Vue();
步骤 2:传递选项对象
仅创建实例只会为 Vue 供电。 但是我们想对 Vue 做更多的。 为此,我们必须将选项或配置对象作为参数传递给刚创建的实例。 此选项对象具有 Vue 可以识别的一些保留属性,它们被指定为键值对。 顾名思义,并非所有属性都是必需的,可以在需要时指定。 通常,它们用于存储数据和执行某些操作。
例如:“el
”,“data
”,“method
”等。
new Vue({ options/config object });
步骤 3:建立与 DOM 的连接
我们希望以某种方式获取对我们希望操纵的一部分 HTML 代码的控制。 在我们的示例中,它是带有id
和“app
”的<div>
元素,因此我们可以通过 Vue 显示消息“Hello World”。
为了在 Vue 实例和 DOM 的一部分之间建立这种连接,以便可以根据我们的需要对其进行控制,我们有一个名为“el
”的保留属性,该属性转换为元素。 此属性将字符串作为值指定为 CSS 选择器,即id
的“#
”和“.
”对于class
。
new Vue({ el: "#app" });
通过此简单的代码行,我们现在将带有id
的<div>
元素,“app
”及其内容链接到 Vue 实例。 它们现在不可分割!
步骤 4:指定我们的数据
为了存储我们要在此 Vue 实例及其链接的 DOM 中使用的所有数据/信息,我们还有一个保留的属性,称为“data
”。 与“el
”不同,“data
”将对象作为其值。 由于我们要向 DOM 显示消息“Hello World
”,因此让我们在“数据”对象中将其指定为键值对。
new Vue({ el: "#app", data: { message: "Hello World" } });
步骤 5:将此数据呈现到 DOM
只需使用如下所示的两个大括号即可将 Vue 实例的数据对象中指定的值呈现到 DOM。 (有关更多信息,请参阅下一篇文章!)
<div id="app"> <h1>{{ message }}</h1> </div>
为了区分上一个输出(没有 Vue),使用 Vue 来显示,请让我们显示“Hello World!
”而不是“Hello World!
”。
好极了! 因此,我们创建了第一个 Vue 应用。 GitHub 仓库中提供了本文讨论的所有代码。
祝你有美好的一天!
2 模板语法和反应式的初探
原文: https://javabeginnerstutorial.com/js/vue-js/2-template-syntax-reactivity/
嘿! 欢迎阅读关于 Vue 的另一篇有趣的文章! 我们不仅要了解模板语法,还可以了解 Vue 的反应式。 这似乎就像我们之前的文章“Hello World with Vue.js ”的继续。 因此,请确保您快速浏览一下,以了解我们到目前为止讨论的内容。 请参阅 GitHub 仓库中的代码。
还记得我们如何在 HTML 代码中使用两个花括号将数据呈现到 DOM 吗? 让我们深入了解它,以了解 Vue 如何在引擎盖下工作。
来自“index.html
”的摘录,
<div id="app">
<h1>{{ message }}</h1>
</div>
这个“两个大括号”的意思是“模板语法”,可以用它声明性地将数据渲染到 DOM。 由于系统是 Vue.js 的核心,因此这是可能的。 模板语法也称为“胡子语法”。
以下是“index.js
”文件中的代码,
var app = new Vue({
el: "#app",
data: {
message: "Hello World!!!"
}
});
每当遇到模板语法时,Vue 都会自动查看其数据属性的对象,并将相应的值呈现给 DOM。
您是否注意到我已将 Vue 实例分配给变量“app
”? 下一节将使用它来演示 Vue 附带的一些反应式!
到目前为止的输出是(就像我们在上一篇文章中看到的那样),
初探 Vue 的反应式
如果您认为使用模板语法进行渲染本身很棒,那么就准备惊讶吧! 因为 Vue 是反应式。
反应式实际是什么意思? 我们知道,在 Vue 实例中指定的数据链接到 HTML 代码中“el
”范围内所引用的任何位置。 Vue 不仅会在被引用的 DOM 中呈现数据,而且每当在数据对象中更改其值时也会更新。
很难缠住头吗? 然后,让我们看到所有这些实时发生的反应。
- 在 Chrome 浏览器中打开 DevTools(快捷方式:
F12
)。 - 点击“控制台”。
- 记住,我们将整个 Vue 实例分配给变量“
app
”。 因此,要从数据对象访问“消息”,只需键入app.message = "Hey Vue, what's up?"
- 并且不要忘记单击“控制台”部分中的“输入”以查看其运行情况。
注意:
您也可以这样做app.$data.message = "Hey Vue, what's up?"
然后单击“Enter
”。 它将正常工作。 在即将发布的系列文章中,我们将回答您在太阳下遇到的每个“为什么”和“如何”问题。 现在,只知道这是访问message
属性的另一种方法。
请记住,可以在多个位置引用数据,并且可以在每个位置自动更新数据。 让我们也尝试一下!
将index.html
文件的<div>
部分的代码更新为
<div id="app">
<h1>Hey {{ message }}!</h1>
<hr/>
<p>This is my first {{ message }} app.</p>
<p>I successfully understood {{ message }}'s reactivity.</p>
</div>
另外,让我们将index.js
文件中message
属性的值更改为“Vue
”,而不是“Hello World !!!
”。 只是为了使这些句子更有意义。
var app = new Vue({
el: "#app",
data: {
message: "Vue"
}
});
现在,让我们看一下输出。
是时候在多个地方看到一些反应发生了。 和以前一样,让我们在 Chrome 浏览器的 DevTools 控制台中更改message
属性的值。
当您按下Enter
时,在我们的 HTML 代码中引用message
的每个位置都将立即更新为新值“Vue
”,这就是您现在在 DOM 中看到的内容。
这不仅令人惊讶吗? 是的,这就是我们忙于玩 Candy Crush 时 Vue 团队或 Evan You 所要做的! 在本系列教程中,我们将看到更多示例,以各种其他方式展示了反应式。
试想一下,如果您使用香草 JavaScript 或 jQuery 实现类似这样的功能,将需要多少编码!
话虽如此,我请假。 祝您有美好的一天!
3 Vue 指令简介
原文: https://javabeginnerstutorial.com/vue-js/3-vue-directives/
今天,我们将探讨 Vue 指令的全部内容。
首先是第一件事。 什么是指令? 这些是特殊的说明,它们会在附加到 HTML 元素时更改其行为。 换句话说,这些是附加到 HTML 元素的特殊属性,这些属性可以更改行为并基于 DOM 的表达式值提供对 DOM 的控制。
所有 Vue 指令均以v-
为前缀。 该前缀用于以下目的:
- 表示它是属于 Vue 的特殊属性
- 帮助保持语法一致
- 为开发人员提供可读性
Vue 带有一些内置指令。 请注意,我们可以编写自己的自定义指令,我们将在以后看到。 这些指令可以在许多情况下为我们提供帮助。
一些示例是,
- 单向和双向绑定:
v-bind
和v-model
- 监听 DOM 事件:
v-on
- 条件渲染:
v-if
,v-else
,v-for
- 插值:
v-once
,v-html
,v-text
在我们的教程系列中,我们将详细处理所有这些指令。 现在,让我们看一下v-once
的工作,并提供一个代码示例,以大致了解指令的工作方式。
场景:
- 显示标题
- 显示带有问候消息的段落,例如“嗨!”
Index.html
<html>
<head>
<title>Hello Vue!</title>
<!-- including Vue with development version CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{ message }}</h1>
<p>Calling the greet function - {{ greet() }}!</p>
</div>
<!-- including index.js file-->
<script src="index.js"></script>
</body>
</html>
Index.js
var app = new Vue({
el: "#app",
data: {
message: "Hi everyone!"
},
methods: {
greet() {
this.message = "Hi!"
return this.message;
}
}
});
你能猜出输出吗?
greet()
函数返回的标题和值都将为“Hi!
”。 因为一旦message
的值更改,所有出现的事件都会被重新渲染。 这是默认行为。
但是,在某些情况下,即使稍后更改属性,您可能仍要显示其初始值。 这是指令生效的地方。 在我们的场景中,我们希望显示message
属性的初始值“Hi everyone!
” 作为标题。 因此,通过将指令v-once
添加到<h1>
元素,该元素内部的所有内容将仅呈现一次。 稍后通过<p>
元素中的greet()
方法对其进行更改时,将不会对其进行更新。
与往常一样,所有代码文件都可以在 GitHub 仓库中找到。 请随意创建您自己的副本,以尝试使用v-once
指令。 不要忘记让您的想象力疯狂。 再见!
4 Vue Devtools 设置
原文: https://javabeginnerstutorial.com/vue-js/4-vue-devtools-setup/
警告:一旦发现如何使用 Vue Devtools 扩展,您将永远不会回到正常的世界。 通过提供用户友好的表示形式,调试变得非常容易。 不仅如此,在早期阶段安装它还将通过检查每个实例来帮助您了解 Vue 的工作原理,并加快整个学习和开发过程。 它可用作 Chrome 和 Firefox 的浏览器扩展。
事不宜迟,让我们通过在 chrome 浏览器中安装 Vue devtools 来扼杀它。 让我们开始吧!
步骤 1:
访问 chrome 网上商店,然后在搜索栏中输入“vue devtools
”。
点击显示的第一个建议“vue devtools
”,或仅按“Enter
”。
步骤 2:
点击“添加到 Chrome 浏览器”按钮,如下图所示。
您将看到一个弹出窗口,要求某些权限。 点击“添加扩展名”按钮进行确认。
步骤 3:
添加扩展名后,您会注意到一条确认消息以及一个小的快捷方式,如下所示。
瞧! 您的 Chrome 浏览器中已安装 Vue Devtools !!
让我们尝试启动一个页面,看看是否启用了此快捷方式。 换句话说,它变成了彩色? 这表明正在检测到 Vue.js,我们可以开始使用它。 我正在 chrome 浏览器中打开我们刚刚安装了 devtools 扩展程序的先前文章中讨论的index.html
文件。
如果快捷方式仍处于禁用状态,则不要惊慌,单击该快捷方式将显示“Vue.js not found
”。
这是因为我们正在从系统中打开 HTML 文件,并且未将其部署在任何服务器上,即我们正在使用文件 URL。 只需看一下地址栏,您会发现地址以file:///
开头。
解决此问题的简单方法是右键单击快捷方式,选择“管理扩展名”,然后打开“允许访问文件 URL”切换按钮。
现在,使用文件 URL 刷新页面,将按预期检测到 Vue.js。 要查看 Root 实例和数据对象,请打开 chrome 开发人员工具(按键盘上的F12
),然后在 Vue 面板上单击。
完全不了解当前 Vue 面板中显示的所有内容绝对可以。 我们将讨论各个方面,并与他们合作。
再见,祝您有美好的一天!
5 数据绑定第 1 部分(文本,原始 HTML,JavaScript 表达式)
原文: https://javabeginnerstutorial.com/vue-js/5-data-binding-p1/
今天,我非常激动,因为我们将要讨论 Vue.js 中一些最有趣的数据绑定技术。 有必要了解,我们提供了多种将 DOM 与基础 Vue 实例的数据对象绑定的方法。
文本插值
还记得我们在先前文章之一中讨论的胡须/模板语法{{ }}
吗? 这是数据绑定的基本形式,称为“文本插值”。 可以使用伪指令v-once
执行一次插值,尽管稍后会更新该属性的初始值。 单击此处,以获取有关v-once
的详细说明以及代码示例和屏幕截图。
原始 HTML 插值
默认情况下,Vue 始终将模板语法中的数据视为纯文本。 在大多数情况下,这正是我们想要的。 但是,编码完全是旅途中意外的转折。 如果有一天我们想传递原始 HTML 并相应地在 DOM 中呈现该怎么办? 当然,您不能使用那些双花括号,因为如果这样做,您将得到。
并不奇怪吗? 整个 HTML 代码呈现为纯文本。 这就是模板语法的工作方式,并且这种行为可以防止任何第三方将不需要的代码注入您的网站(多么可怕?!)。 因此,切勿在用户无法控制的用户提供的内容上使用此功能。 因此,如果您真的相信提供 HTML 代码的源代码是安全的,并希望将其呈现给 DOM,请使用另一个指令v-html
。
使用v-html
指令会将<span>
标记的内容替换为vueLink
数据属性的值,并将其解释为纯 HTML。 因此,超链接以指定的绿色显示。
使用 JavaScript 表达式
双花括号以及显示纯文本还可以求值单个 JavaScript 表达式。
请记住,仅是单个表达式。 不是语句,不是流控件,不是任何用户定义的全局变量! 让我们来看一些示例
- 可以始终在模板语法中访问“
message
”属性的值。 现在,我们可以对其应用任何可用的 JavaScriptString
方法。
{{ message.length }}
- js 允许访问模板表达式中的几个全局对象,即
Math
和Date
。 小心,不允许访问用户定义的全局变量。
{{ Math.PI }} {{ new Date() }}
- 流控制语句(如
if-else
,for
,while
,do-while
等)将不起作用,因此剩下的唯一选择是使用三元表达式。 始终建议将复杂的逻辑放入我们将在以后的文章中详细讨论的方法中。
{{ message === "Hi everyone!" ? "Welcome to the World of Vue" : "Hola" }}
此外,如前所述,在模板语法内只能使用单个表达式。 即使是一个简单的表达式也无法使用,例如“let level = 1
”。
完整的代码如下,
Index.html
<!DOCTYPE html>
<html>
<head>
<title>Hello Vue!</title>
<!-- including Vue with development version CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!-- text interpolations -->
<h1>{{ message }}</h1>
<p>For more info. on Vue, click {{ vueLink }}</p>
<!-- raw HTML interpolation -->
<p>With v-html directive:</br>
For more info. on Vue, click <span v-html="vueLink"></span>
</p>
<!-- JavaScript expressions -->
<p>
Length of the 'message' String: {{ message.length }} </br>
Value of PI: {{ Math.PI }} </br>
Today's date is: {{ new Date() }} </br>
Result of ternary expression is:
{{ message === "Hi everyone!" ? "Welcome to the World of Vue" : "Hola" }} </br>
</p>
</div>
<!-- including index.js file -->
<script src="index.js"></script>
</body>
</html>
Index.js
var app = new Vue({
el: "#app",
data: {
message: "Hi everyone!",
vueLink: "<a href='https://vuejs.org/' style='color:green'>here</a>"
}
});
输出如下,
上面讨论的所有代码都可以在 GitHub 仓库中找到。
在我请假之前,让我先给您的大脑一个小任务。 我们无法在 HTML 属性中使用此模板/胡子语法。 您能想到其他选择吗? 我相信你可以!
祝你今天愉快 ?
6 数据绑定第 2 部分(属性)
原文: https://javabeginnerstutorial.com/vue-js/6-data-binding-p2/
在我们的上一篇文章中,我给了您一些思考。 记得? 属性绑定! 是时候深入了解它了。 众所周知,示例有助于我们更轻松地理解概念。 那么,我们还等什么呢?
假设我们有一个锚标记,我们想将链接绑定到href
属性。 让我们尝试使用文本插值技术(模板语法)来执行此操作,然后检查结果。
失望了吗?这是发生了什么事。 正如预期的那样,“Vue 官方网站”文本已显示为超链接。 单击它会打开一个新选项卡,因为我们使用了_blank
作为目标(这里没什么疯狂的)。 现在,查看地址栏中的 URL。 不会像在 Vue 实例的数据对象中指定的那样导航到 https://vuejs.org/v2/guide/index.html,而是将href
属性的双引号内的值当做字符串,并解析为 URL {{ vueLink }}
。 这就是普通 HTML href
属性的工作方式,这就是您在新打开的标签页的地址栏中看到的内容。 显然,绑定从未发生过。
因此,这就是 Vue 的工作方式。 我们不能将模板语法/大括号括起来用于 HTML 属性绑定。 但是,如果我们仍然想将某些东西动态绑定到属性上怎么办? 不要担心! Vue 附带了另一个用于此目的的指令v-bind
。 只要我们花时间寻找指令,指令就在我们周围。
在这种情况下,使用v-bind
指令告诉 Vue.js 将data
属性的值绑定到该指令后面的属性名称。
语法
v-bind:html_attribute=”data_to_be_bound
示例
v-bind:href="vueLink"
让我们尝试执行此代码并检查输出。
奇迹般有效! 单击呈现的链接将打开一个新标签,该标签具有预期的指定链接。
其他示例场景
- 在网页上动态显示图片,即,当在 Vue 实例的数据对象中更改图片位置时,应该在网页上自动更新图片。
- 根据绑定到该字段的数据值启用或禁用输入字段。
让我们一次处理一种情况。 到最后,您将对 Vue.js 表现出超出预期的表现。
1.动态显示图像
让我们将 Vue 的徽标图像放置在与代码相同的文件夹中,并将其在数据对象中的位置指定为logo
。 将<img>
标记的src
属性绑定到logo
属性的值。
<img v-bind:src="logo" width="70" height="50"/>
图像将按预期渲染到 DOM。 将您的帽子戴上戒指,并尝试更改图像位置是否动态更新渲染的图像。 如果您在执行此操作时遇到任何挑战,请在评论部分给我大喊。 输出如下,
2.根据绑定的值启用或禁用输入字段
是时候做同样的歌舞了!
在 Vue 实例的数据对象中,让我们拥有isInputDisabled
属性,并将其值设置为true
isInputDisabled: true
让我们将其绑定到输入标签的disable
属性。
<input v-bind:disabled="isInputDisabled"/>
这会将禁用的属性绑定到所提到的属性的值,并禁用输入字段。 使用 Chrome 开发者工具检查该元素会显示其相应的 HTML 代码,如下所示,
现在让我们将isInputDisabled
的值更改为false
。
isInputDisabled: false
有趣的是,如果禁用的属性值为false
,null
或undefined
,则甚至不包含在呈现的<input>
元素中(使用开发人员工具检查该元素)。
完整的代码如下,
Index.html
<!DOCTYPE html>
<html>
<head>
<title>Hello Vue!</title>
<!-- including Vue with development version CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!-- text interpolations -->
<h1>{{ message }}</h1>
<p>
<input v-bind:disabled="isInputDisabled"/>
</p>
<div>
<img v-bind:src="logo" width="70" height="50"/>
<br/>
<a v-bind:href="vueLink" target="_blank">Vue official website</a>
</div>
</div>
<!-- including index.js file -->
<script src="index.js"></script>
</body>
</html>
Index.js
var app = new Vue({
el: "#app",
data: {
message: "Hi",
isInputDisabled: false,
logo: "vueLogo.png",
vueLink: "https://vuejs.org/v2/guide/index.html"
}
});
上面讨论的所有代码都可以在 GitHub 仓库中找到。
启动您喜欢的 IDE,并编写一些自己的方案! 如有任何疑问,请随时在“评论”部分中进行提问。 另一个有趣的概念很快见!
7 条件渲染第 1 部分(v-if
,v-else
,v-else-if
)
原文: https://javabeginnerstutorial.com/vue-js/7-conditional-rendering-part-1/
我们并不总是希望将所有内容呈现到我们的网页上。 根据特定条件或特定表达式的值,我们可能希望隐藏/显示/附加或分离元素。 这只不过是条件渲染 – 基于条件渲染元素。 这可以使用简单的if-else
语句来实现。 但是,我们将如何使用 Vue? 现在就纠正一下吧!
Vue 指令再来一次! 在这方面,我们有v-if
,v-else
和v-else-if
来帮助我们。 让我们逐一查看示例,以进行深入了解。 我将撒很多视觉效果以简化此过程。
首先,让我们有两个带有一些文本的段落标签,
1. v-if
指令
使用v-if
指令的语法如下:
<HTML-tag v-if=”condition or expression that evaluates to true or false”>
如果条件为真/求值为真,则将渲染标签。
让我们向 Vue 实例的数据对象添加display
属性,并为其赋予false
值。
data: { message: "Hi", display: false }
现在,将v-if
指令添加到第一段标签中,并指定display
条件,基于该条件,将不显示标签。
<p v-if="display">Display evaluates to true</p>
由于display
的值现在设置为false
,因此不会呈现带有文本“显示求值为真”的第一个<p>
标签。
将display
值更改为true
将呈现第一个<p>
标签的内容。
2\. v-else
指令
假设您要在显示为true
时显示第一个<p>
标签,并在显示结果为false
时显示第二个<p>
标签。 在这种情况下,我们可以使用v-else
指令。 它类似于else
块。
<p v-if="display">Display evaluates to true</p>
<p v-else>You always get to see me :)</p>
将display
设为true
时,将在v-if
指令的条件求值为true
的情况下呈现第一个<p>
标签。
将display
更改为false
只会使用v-else
伪指令呈现第二个<p>
标签,如下所示,
注意: 具有v-else
指令的 HTML 元素必须紧随具有v-if
的元素。 否则,v-else
元素将不会被识别。 换句话说,不要在v-if
元素和v-else
元素之间添加任何元素。
可以在<div>
,<template>
等帮助下将这些指令添加到单个 HTML 元素或元素块中。考虑一种情况,您希望根据条件显示错误消息。 在这种情况下if-else
块会派上用场。
3. v-else-if
指令
它也与v-if
元素一起使用。 该指令充当“else-if
”条件。 与其他任何编程语言一样,它可以链接多次。
让我们向数据对象添加val
属性,并为其提供数值。
data: { message: "Hi", val: 5 }
将v-if
,v-else-if
和v-else
伪指令添加到三个<p>
标签并添加条件。
<p v-if="val < 10">Val is less than 10</p>
<p v-else-if="val > 10 && val < 20">val is between 10 and 20</p>
<p v-else>Pretty high val!!</p>
现在,让我们更改val
属性的值,并查看输出如何变化。
首先,将val
设为5
。 由于它小于 10,因此v-if
条件求值为true
并被渲染。
现在将val
更改为15
。 v-else-if
条件求值为true
,并将其呈现到 DOM,如下所示。
将val
更改为90
会使v-if
和v-else-if
表达式均求值为false
。 因此,将渲染v-else
元素。
注意: 与v-else
相似,带有v-else-if
指令的元素必须紧随带有v-if
或v-else-if
的元素。 否则,将无法识别。
上面讨论的所有代码都可以在 GitHub 仓库中找到。
是时候戴上开发人员的帽子,并尝试使用这些指令了! 祝你今天愉快。
8 条件渲染第 2 部分(v-if
和v-show
)
原文: https://javabeginnerstutorial.com/vue-js/8-conditional-rendering-part-2/
我们是否没有足够的指令来处理条件渲染? 我们还需要v-show
吗? 还有另一件事困扰我们的大脑吗?
但是该怎么办? 我们忙于观看 Netflix 时,Evan You(Vue 的创建者)出于某种原因添加了它。 因此,让我们看看为什么该指令首先存在。 警告:这篇文章是“条件渲染第 1 部分”的继续,因此,我建议您在继续进行之前先充分了解这些概念。
我们讨论了v-if
,v-else
等不仅可以应用于单个 HTML 元素,而且可以应用于使用<div>
或<template>
等的元素块。让我们首先来看一个带有<template>
元素的示例,并了解它作为不可见包装器的行为,以最终结果呈现给 DOM。
使用<template>
元素
让我们在<template>
元素内包装标题和段落,并使用v-if
指令为其指定条件。 除此之外,我们还要添加没有任何条件的另一段。
首先,将display
属性添加到 Vue 实例的数据对象,并为其赋予true
值。
data: {
display: true
}
其次,对index.html
文件进行以下更改,
<template v-if="display">
<h1>Welcome</h1>
<p>Hi there!</p>
</template>
<p>How are you doing?</p>
如果由于display
设置为true
而看到了输出,则if
条件求值为true
,并显示模板块中的元素。
这里有趣的是,如果您打开浏览器的开发人员工具(在 Chrome 浏览器中为Ctrl + Shift + I
或F12
),然后在元素标签中,<template>
元素将不包含。 它神奇地变成了不可见的!
注意: 当v-if
条件的值为false
时,整个元素将从 DOM 中分离/删除。 当在我们的应用中不需要某个元素时,这通常是首选的行为,因为 DOM 中包含较少的元素会提高其性能。
v-show
指令
作为开发人员的生活是如此变幻莫测! 我们经常遇到需要频繁显示和隐藏元素的情况,即在网页上打开和关闭。 在这种情况下,最好使用v-show
指令。
使用v-show
的原因
该指令将确保 DOM 中始终存在元素。 将显示条件是否为真(求值为true
)。 当条件求值为false
时,该元素将通过自动添加 CSS 属性display: none
来隐藏! 听起来不有趣吗?
这个概念可能很难缠住我们的头脑。 不用担心,我已经覆盖了你。 示例和屏幕截图可为我们提供帮助!
在前面的示例中,让我们将v-show
指令添加到第二段标签中。
<p v-show="display">How are you doing?</p>
当display
为true
时,v-if
和v-show
的行为类似。 浏览器输出和开发人员工具显示的结果与以前相同(请参阅“使用template
元素”标题下显示的两个图像)。 这里没有惊喜!
让我们将display
属性的值更改为false
。 我们完整的代码如下所示:
Index.js
var app = new Vue({
el: "#app",
data: {
display: false
}
});
Index.html
<!DOCTYPE html>
<html>
<head>
<title>Hello Vue!</title>
<!-- including Vue with development version CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<template v-if="display">
<h1>Welcome</h1>
<p>Hi there!</p>
</template>
<p v-show="display">How are you doing?</p>
</div>
<!-- including index.js file -->
<script src="index.js"></script>
</body>
</html>
顺便说一句,让我们继续查看浏览器的输出和开发人员工具的结果。
不出所料,由于v-if
和v-show
条件都求值为false
,因此没有任何内容呈现给网页。 这里也没有什么魔术。 但是,当我们打开开发人员工具并仔细查看“元素”窗格时,有很多东西需要解码。
- 具有
v-if
伪指令的<template>
元素(包含<h1>
和<p>
元素)在求值为false
时,已从 DOM 中删除了。 - 仅具有
v-show
指令的第二个<p>
元素使用 CSSdisplay
属性切换元素的可见性。 元素始终附加到 DOM,并且始终显示。 它只是变得不可见。 就这样!
当心!
由于不支持将v-show
与<template>
元素一起使用,因此无法使用。 另外,它不适用于v-else
(if
和else
是灵魂伴侣,你知道吗?!不是,show
和else
!)
那么,我应该使用v-if
或v-show
吗?
好问题! 如果您想切换,通常将经常切换,因为 DOM 中始终存在该元素,请使用v-show
,无论初始条件是否为true
或false
。 借助 CSS display
属性只能切换可见性。 因此,初始渲染成本较高。
如果元素的状态不经常更改/切换,尤其是在运行时,请使用v-if
。 因为在这里附加和分离元素通常会变得昂贵。 另外,请记住,v-if
是惰性的**,即,如果条件在初次渲染时求值为false
,则直到条件变为true
时,元素或块才会被渲染。
还在头上有雾吗? 不用担心! 练习是这里的关键。 抓住巫师的帽子和魔杖,尝试一些咒语(示例场景),您将掌握它! 顺便说一下,以上讨论的所有代码都可以在 GitHub 仓库中找到。
祝您有美好的一天!
9 渲染列表第 1 部分(遍历数组)
原文: https://javabeginnerstutorial.com/vue-js/9-rendering-lists-part-1/
我们日复一日地处理清单。 在我们的应用中,我们经常遇到这样的情况,即我们必须将数组显示为列表,其项会动态变化。 因此,在我们的 HTML 中对数组项进行硬编码并不是真正的选择。 重要的是,我们知道如何使用 Vue 处理这些动态变化的数组,这并不奇怪,为此,我们还有另一个指令! 对,是v-for
! 与往常一样,我们将深入研究大量示例,以便于清楚理解。
遍历数组
我们面临的最常见和最简单的方案是遍历数组的各项并将它们呈现为列表。 以下是四种不同语言的问候语,我们希望将其显示为网页的无序列表。
data: {
greetings: ["hi", "bonjour", "hola", "ciao"]
}
在我们的index.html
文件中,让我们将v-for
指令添加到<li>
元素中,以动态地呈现greetings
数组的内容,如下所示:
<li v-for="greeting in greetings">{{ greeting }}</li>
好吧,不要惊慌! 我将逐字剖析此信息。
我们为v-for
指令使用的特殊语法是“greeting in greetings
”。
- 首先,
greeting
– 建议在我们的情况下使用单数名词,greeting
,作为数组元素的别名反复进行。 这可以是您选择的任何名称,然后可以在我们的代码中使用该名称来寻址每个数组项。 - 其次,作为语法一部分的 – 定界符
in
。 如果您具有 JavaScript 背景,那么所有这些看起来都应该很熟悉,因为在这里我们将for in
和for of
用于迭代目的。 即使使用 Vue,我们也可以使用of
作为分隔符而不是in
<li v-for="greeting of greetings">{{ greeting }}</li>
- 最后,
greetings
– 必须与 Vue 实例的data
对象中的数组名称完全相同。
在这里,为简单起见,我们在简单插值中使用变量greeting
。 但它可以像其他任何属性一样使用,例如将其作为值传递给函数或将其绑定到链接的引用等。输出如下,
使用小胡子语法的另一种方法是使用v-text
指令。 它将greeting
设置为文本内容。
<ul> <li v-for="greeting in greetings" v-text=greeting></li> </ul>
将呈现相同的结果。
Vue 是反应式的!
让我们沿着存储通道走一下。 我们了解到 Vue 的美丽在于其反应式。
快速回顾:使状态和视图保持同步。 换句话说,Vue 不仅在引用的 DOM 中将呈现为数据,而且无论何时在数据对象中更改其值,都会更新。
让我们再来看一次这种情况。
使用快捷方式F12
打开 Chrome DevTools ,然后点击“Vue”。 仅当您已遵循我们的教程系列并安装了 Vue Devtools Chrome 扩展 后,此面板才会出现。
由于我们没有使用任何全局变量来引用index.js
文件中的 Vue 实例,因此默认情况下会将其保存到$vm0
。 如下图所示,单击<Root>
即可看到。 因此,让我们使用$vm0
从开发者工具控制台访问 Vue 模型。
让我们转到控制台,使用push
方法向greetings
数组添加一个问候,然后查看视图是否进行了动态更新(神奇地!)。
$vm0.greetings.push("namaste")
这不是魔术吗?
访问当前数组项的索引
开发者的生活没有暂停按钮! 我们期望完成意外的事情,例如访问数组项的索引及其内容。 我们知道如何使用 Vue 渲染数组元素,但是否也可以获得其索引? 好吧,答案是肯定的!
语法与我们之前看到的非常相似,
<div v-for="(greeting, index) in greetings">{{ index }}. {{ greeting}}</div>
我们必须引入一个括号并指定两个用逗号分隔的参数,可以选择它们的名称。 第一个参数是指数组元素 – 在我们的示例中是greeting
。 第二个参数是可选的,它引用当前正在循环的项目的索引 – 在我们的示例中为index
。 此顺序是最重要的注意事项。 无论您使用什么名称,第一个始终是数组元素,第二个始终是v-for
语法中的索引。 然后可以根据需要将其与代码中的这些名称一起使用。
让我们看一下最终代码。
Index.html
<!DOCTYPE html>
<html>
<head>
<title>Hello Vue!</title>
<!-- including Vue with development version CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2>Greetings</h2>
<!-- Iterating through array elements-->
<ul>
<li v-for="greeting in greetings" v-text=greeting></li>
</ul>
<!-- Accessing array elements along with thier index -->
<h2>Greetings with Index</h2>
<div v-for="(greeting, index) in greetings">
{{ index }}. {{ greeting}}
</div>
</div>
<!-- including index.js file -->
<script src="index.js"></script>
</body>
</html>
Index.js
var app = new Vue({
el: "#app",
data: {
greetings: ["hi", "bonjour", "hola", "ciao"]
}
});
GitHub 仓库中也提供了上面讨论的所有代码。 随意分叉仓库并进行一些实验。
在下一篇文章中,我们将研究如何使用v-for
遍历对象。 在此之前,请继续练习!
10 渲染列表第 2 部分(遍历对象)
原文: https://javabeginnerstutorial.com/vue-js/10-rendering-lists-part-2/
希望您已经知道使用v-for
指令遍历数组元素。 在我们的应用中,我们处理的对象与数组一样多。 因此,不用费劲,让我们了解如何迭代对象的各种属性。 我听到你说:“那么对象数组呢?”我懂了。 我们也会处理这种情况!
遍历对象
是时候向我们的 Vue 实例的data
对象添加一个简单的对象了。
var app = new Vue({
el: "#app",
data: {
greetings: ["hi", "bonjour", "hola", "ciao"],
letters: {
a: "apple",
b: "banana",
c: "cat"
}
}
});
为了遍历对象的键值对,我们可以使用相同的v-for
指令。
1. 访问对象的值
访问对象的值非常简单。
<div v-for="value in letters"> {{ value }} </div>
这种语法与遍历数组值非常相似吗? 如果我们只想从每个键值对中获取值,那么就足够了。
- ‘
value
’– 您可以选择任何名称。 使用相同的名称访问每个值。 - ‘
letters
’– 必须与data
对象中的对象名称相同。
2. 获得键和值
如果我们想在v-for
语法中使用多个参数,则必须使用括号。 顺序在这里很重要。
- 第一个自变量将始终是值
- 第二个自变量将是键。
参数名称可以是我们选择的任何名称。 为了简单和易于理解,让我们使用value
和key
作为参数名称。
<div v-for="(value, key) in letters"> {{ key }} for {{ value }} </div>
3. 访问键,值和当前索引
第三个自变量可用于获取正在访问键值对的当前指标。 同样,这些论据的顺序很重要。 第一个参数将始终是值,第二个参数将是键,第三个参数是当前索引。 参数名称可以一如既往地由我们选择。 不要忘记将所有这些参数括在括号中。 为了更好地理解,让我们将“k
”用作键,将“v
”用作值,将“i
”用作当前索引。
<div v-for="(v, k, i) in letters"> {{ k }} for {{ v }} - ({{ i }}) </div>
注意: 请注意,这些键值对的显示顺序可能会因浏览器而异。 因为顺序基于Object.keys()
的结果,该结果返回对象的所有键。
由于参数名称可以是我们喜欢的任何名称,因此以下代码也提供了相同的结果。
<div v-for="(value, key, index) in letters"> {{ key }} for {{ value }} - ({{ index }}) </div>
遍历对象数组
假设我们的数据对象中有两个简单对象的数组。
movies: [
{ character: "Iron man", name: "Avengers" },
{ character: "groot", name: "Guardians of the Galaxy" }
]
为了访问数组中每个对象的值,
<li v-for="movie in movies">
<strong>Movie name:</strong> {{ movie.name }} and <br/>
<strong>Character:</strong> {{ movie.character }}
</li>
movie in movies
– 从movie
变量中的movies
数组中获取每个对象movie.name
和movie.character
– 从当前正在循环的单个对象中分别获取键name
和character
的值,并将其保存在“movie
”变量中。
请记住,如果您不想使用点运算符或动态呈现键值对,则可以嵌套另一个v-for
指令。 当然,您可以在其中嵌套任意数量的v-for
指令。
因此,使用点运算符的替代代码是
<li v-for="movie in movies">
<div v-for="(value, key) in movie">
{{ key }}: {{ value }}
</div>
</li>
跟踪独特的“键”属性
在处理简单列表时,我们可以像到目前为止讨论的那样使用v-for
指令。 但是在处理子组件(我们将在本系列教程的后面部分介绍),更新列表元素等过程中,您可能会遇到一些意外行为。为避免在编码时遇到此类颠簸,始终建议提供唯一值并将其绑定到特殊的key
属性,如下所示,
<li v-for="movie in movies" v-bind:key="movie.name">
希望电影的名称是唯一的。 这将有助于 Vue 跟踪每个节点的身份。
遍历一个范围
如果遇到仅打印从 1 到某些固定整数的数字的情况,那么再次说,这只是 Vue 的v-for
指令。
例如,
<span v-for="num in 3">
num
– 您选择的任何变量名称,它将在给定范围内循环时代表当前数字in
– 分隔符(根据语法)- 3 – 范围,表示循环时要考虑的最后一个数字。
假设我们要打印 5 到 1 到 3 之间的数字。 按照上面的示例很容易,
<div v-for="number in 5">
<strong>{{ number }}. </strong>
<span v-for="num in 3"> {{ num }} </span>
</div>
外部v-for
跟踪<div>
标签内的代码重复的次数,内部v-for
生成 1 到 3 的整数范围。
让我们只看完整的代码,而不是看看各个片段。
index.html
<!DOCTYPE html>
<html>
<head>
<title>Hello Vue!</title>
<!-- including Vue with development version CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2>Greetings</h2>
<!-- Iterating through array elements -->
<ul>
<li v-for="greeting in greetings" v-text=greeting></li>
</ul>
<!-- Accessing array elements along with thier index -->
<h2>Greetings with Index</h2>
<div v-for="(greeting, index) in greetings">
{{ index }}. {{ greeting}}
</div>
<hr/>
<!-- Iterating through objects -->
<h2>Object values</h2>
<div v-for="value in letters">{{ value }}</div>
<hr/>
<h2>Object key-value pairs</h2>
<div v-for="(value, key) in letters">
{{ key }} for {{ value }}
</div>
<hr/>
<h2>Object key-value and index</h2>
<div v-for="(value, key, index) in letters">
{{ key }} for {{ value }} - ({{ index }})
</div>
<hr/>
<!-- Iterating over an array of objects -->
<ul>
<li v-for="movie in movies" v-bind:key="movie.name">
<strong>Movie name:</strong> {{ movie.name }} and <br/>
<strong>Character:</strong> {{ movie.character }}
</li>
</ul>
<!-- Alternative method -->
<h4> Aliter </h4>
<ul>
<li v-for="movie in movies">
<div v-for="(value, key) in movie">
{{ key }}: {{ value }}
</div>
</li>
</ul>
<hr/>
<!-- Iterating over numbers -->
<div v-for="number in 5">
<strong> {{ number }}. </strong>
<span v-for="num in 3"> {{ num }} </span>
</div>
</div>
<!-- including index.js file -->
<script src="index.js"></script>
</body>
</html>
index.js
var app = new Vue({
el: "#app",
data: {
greetings: ["hi", "bonjour", "hola", "ciao"],
letters: {
a: "apple",
b: "banana",
c: "cat"
},
movies: [
{ character: "Iron man", name: "Avengers" },
{ character: "groot", name: "Guardians of the Galaxy" }
]
}
});
GitHub 仓库中也提供了上面讨论的所有代码。
故事的寓意,v-for
– 一个统治所有人的指令! ?
祝你有个美好的一天。
11 监听 DOM 事件和事件修饰符
原文: https://javabeginnerstutorial.com/vue-js/11-listening-to-dom-events-and-event-modifiers/
我们在事件方面一直处于停滞状态。 现在,让我们学习如何收听 DOM 事件,即click
事件。 看一下起始代码,
Index.html
<!DOCTYPE html>
<html>
<head>
<title>Hello Vue!</title>
<!-- including Vue with development version CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2>Welcome</h2>
<button>Say Hello</button>
<p> {{ message }} </p>
</div>
<!-- including index.js file -->
<script src="index.js"></script>
</body>
</html>
Index.js
var app = new Vue({
el: "#app",
data: {
message: "Hi"
}
});
地球上最著名的按钮事件是click
事件。 假设我们要在单击“说你好”按钮时将消息“嗨”更改为“你好”。 知道我们如何通过 Vue 实现这一目标吗?
亲们,人民,Vue 为此又有另一个指令,v-on
! 我们始终可以使用v-on
监听 DOM 事件,例如,
- 鼠标事件 –
click
,dblclick
,mousemove
,mouseover
等。 - 键盘事件 -
keypress
,keyup
,keydown
等。 - 表单事件 –
submit
,focus
,blur
等 - 还有很多
监听点击事件
因此,我们现在的任务是监听按钮“Say Hello”上发生点击事件时,将消息文本从“Hi
”更改为“Hello
”。 一件事很清楚。 我们必须将v-on
指令添加到将要监听click
事件的button
元素中。
<button v-on:click="message='Hello'">Say Hello</button>
剖析语法,
v-on
– Vue 知道我们正在使用此指令来监听事件click
– 指定在冒号之后我们正在监听的事件的类型=
– 在等号后,指定触发事件时我们要执行的方法名称或 JavaScript 代码。
我们正在从 Vue 实例的数据对象访问message
属性,并将其设置为“Hello
”。 输出如下,
酷! 但是在实际的应用中,我们将要做的不仅仅是改变属性的文本。
令人费解的问题:如何在v-on
指令的双引号中放入复杂的 JavaScript 代码?
使用方法
上面提到的大脑拖曳问题的答案是使用方法。 就像data
对象一样,我们的 Vue 实例有一个可选的methods
对象,我们可以在其中定义所有方法。
现在,在v-on
的双引号中,只需提及方法的名称,然后传递所需的参数(如果有)。 然后,在 Vue 实例的methods
对象中定义方法,该方法将在每次click
事件发生时触发。
Index.html
(代码段)
<button v-on:click="greet('howdy')">Say Hello</button>
Index.js
var app = new Vue({
el: "#app",
data: {
message: "Hi"
},
// define all the methods within the 'methods' object
methods: {
greet(greeting) {
// 'this' keyword refers to the current Vue instance
this.message = greeting;
}
}
});
您注意到this
关键字的用法了吗? 为了从 HTML 引用data
对象的属性,我们可以直接使用它们,因为我们使用el
关键字将特定的 HTML 部分与 Vue 实例挂钩。 但是,在methods
内部,我们必须使用‘this’
关键字指向 Vue 实例,然后访问data
对象的属性。
单击该按钮,“说声你好”触发了greet('howdy')
方法,并且该方法中的代码被执行,从而向网页呈现“Howdy
”。
事件修饰符
假设我们有一个名为“加 1”的按钮和一个初始值为zero
的计数器。 每次单击该按钮时,都会在现有的counter
值上加 1 并将其打印到屏幕上。
现在让我们将想象力带入现实。 只需将v-on
指令与click
用作参数,即可触发将counter
值加 1 的方法。
Index.html
(代码段)
<button v-on:click="addOne">Add 1</button> <p> {{ counter }} </p>
Index.js
(代码段)
methods: {
addOne: function() {
this.counter += 1;
}
}
脑筋急转弯问题: 如果我们只想先在click
上触发此方法,然后再不触发该方法怎么办?
该问题的答案是 – 事件修饰符! 这些使我们能够修改事件的行为(正如其名称所暗示的)。
用法:在事件名称(在本例中为click
)之后,添加一个点并指定要使用的修饰符的名称。
<button v-on:click.once="addOne">Add 1</button>
使用事件修饰符.once
,click
事件将仅触发一次。 这意味着,无论单击按钮多少次,只要将counter
的值增加到 1,addOne
方法将仅被调用。
同样,其他可用的事件修饰符是
.stop
.prevent
.capture
.self
.once
.passive
最常用的修饰符是.stop
和.prevent
,它们称为众所周知的event.stopPropagation()
和event.preventDefault()
方法。 这些是本机事件对象随附的方法。 这里与 Vue 无关。 Vue.js 只是提供了一种简单的方法,借助修饰符来处理此类常见事件详细信息,而不是在 Vue 实例的方法中显式指定这些事件方法(例如event.preventDefault()
来取消事件)。
两个最常用修饰符的快速刷新:
根据stopPropagation()
事件方法,.stop
修饰符将进一步停止事件的传播。
根据preventDefault()
方法,.prevent
修饰符将防止发生默认操作。 .prevent
的最佳示例是将其与Submit
按钮一起使用,以便触发提交事件时,它不会重新加载页面。
链接修饰符
警告:一开始可能会有些弯腰!
Vue 让我们像这样,将这些修饰符一个接一个地链接,
v-on:click.self.prevent
链接的顺序非常重要。 因为与每个修饰符相关的代码是按相同顺序生成的。
例如,
<a href="https://vuejs.org/" v-on:click.self.prevent target="_blank">Open Vue <p>Click me now</p> </a>
使用v-on:click.self.prevent
仅会阻止单击<a>
元素本身,而不是单击其child
元素。 好吧,我听到你说“请用英语”! 换句话说,这意味着
- 单击
<a>
标签的“打开 Vue”将阻止打开vuejs.org
页面 - 单击标签上的“立即单击我”,将在新标签中打开
vuejs.org
官方页面
相反,如果我们将链接修饰符的顺序更改为v-on:click.prevent.self
,则将防止发生所有单击事件。
<a href="https://vuejs.org/" v-on:click.prevent.self target="_blank">Open Vue <p>Click me now</p> </a>
单击<a>
的“打开 Vue”和<p>
的“立即单击我”都不会打开vuejs.org
(换句话说,它无法打开)。
现在,让我们看一下到目前为止处理的完整代码,
index.html
<!DOCTYPE html>
<html>
<head>
<title>Hello Vue!</title>
<!-- including Vue with development version CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2>Welcome</h2>
<!-- Listening to click event with v-on directive -->
<button v-on:click="greet('howdy')">Say Hello</button>
<p> {{ message }} </p>
<h2>Event Modifiers</h2>
<!-- Using .once to modify click event -->
<button v-on:click.once="addOne">Add 1</button>
<p> {{ counter }} </p>
<!-- chaining event modifiers -->
<a href="https://vuejs.org/" v-on:click.prevent.self target="_blank">Open Vue
<p>Click me now</p>
</a>
</div>
<!-- including index.js file -->
<script src="index.js"></script>
</body>
</html>
index.js
var app = new Vue({
el: "#app",
data: {
message: "Hi",
counter: 0
},
// define all the methods within the 'methods' object
methods: {
greet(greeting) {
// 'this' keyword refers to the current Vue instance
this.message = greeting;
},
// another way to define methods before es6
addOne: function() {
this.counter += 1;
}
}
});
我知道今天有很多事情要消化。 上面讨论的所有代码以及不言自明的注释在 GitHub 仓库中可用。 慢慢来,我很快就会回来讨论键盘和其他鼠标 DOM 事件。
祝你有个美好的一天!
12 监听键盘和鼠标事件
原文: https://javabeginnerstutorial.com/vue-js/12-listening-to-keyboard-and-mouse-events/
准备好使用 Vue 监听键盘上的某些键代码和某些鼠标按钮了吗? 那我们深入吧! 如果尚未阅读,请确保阅读“监听 DOM 事件和事件修饰符”。 因为这更多地是我们已经看到的内容的延续。
起始代码
Index.html
<!DOCTYPE html>
<html>
<head>
<title>Hello Vue!</title>
<!-- including Vue with development version CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2>Welcome</h2>
<button>Greet</button>
</div>
<!-- including index.js file -->
<script src="index.js"></script>
</body>
</html>
Index.js
new Vue({
el: "#app",
data: {
},
// define all custom methods within the 'methods' object
methods: {
greet() {
alert("Hi");
}
}
});
监听键盘事件
在开发人员世界中,即使用户没有明确单击页面上提供的 HTML 按钮来处理这种情况,我们也会经常听Enter
键以确保执行相应的操作。 在使用 Vue 监听键事件时,我们可以使用通常的v-on
指令来提及所需的键修饰符,如下所示:
Index.html
(代码段)
<!-- Using key modifiers along with keyboard events -->
<button v-on:keyup.13="greet">greet</button>
每当用户释放按键或按键上升时,就会触发keyup
事件。 在这种情况下,我们要监听Enter
键,而不仅仅是其他键。 因此,让我们通过在keyup
事件之后添加一个点来提及其键码为13
。 想知道我怎么知道那个关键代码? 常用的,通常会放在我们的头上。 如果不是,请尝试 https://keycode.info/ 获取键盘上任何键的键码。 太酷了!
但是,记住密码还是一直找 Google 麻烦吗? 不用担心,就像.once
,.prevent
,.self
等事件修饰符一样,Vue 为我们提供了用于大多数常用键的键修饰符。 清单是这样的
.enter
.tab
.delete
(同时捕获“删除”和“退格”键).esc
.space
.up
.down
.left
.right
现在,您可以安全地用键修饰符.enter
替换键码13
,它将像超级按钮一样工作。 专注于按钮并单击键盘上的Enter
,将显示一条警告消息,显示为Hi
。
注意:按键修饰符仅适用于与键盘相关的事件,例如keyup
。 这里没有火箭科学或量子物理学的东西!* ?
自定义键修饰符别名
除了 Vue 附带的关键修饰符之外,您还可以继续进行自己的设置,为您选择的任何关键代码设置自定义别名。 这是通过全局config.keyCodes
对象完成的。
将以下行添加到“index.js
”文件中以启用“v-on:keyup.a
”,
Vue.config.keyCodes.a = 65;
Index.html
(代码段)
<button v-on:keyup.a="greet">greet</button>
在此,释放键“a
”时,将触发键代码为65
的keyup
事件,并调用方法“greet()
”。 Cakewalk!
更多关于鼠标事件的示例!
在我们的上一篇文章中,我们看到了最著名的鼠标事件点击事件。 现在让我们再看看mouseover
。
假设,将鼠标指针悬停在“Hover over me!
”按钮上时,应显示“developer
”文本。
在 Vue 实例的数据对象中添加“role
”,并定义一个名为“showRole
”的方法,以将此角色属性更新为String
和developer
。 触发mouseover
事件时调用此方法。
Index.js
new Vue({
el: "#app",
data: {
role: ""
},
// define all custom methods within the 'methods' object
methods: {
showRole() {
// 'this' keyword refers to the current Vue instance
this.role = "developer";
}
}
});
Index.html
(代码段)
<button v-on:mouseover="showRole">Hover over me!</button>
{{ role }}
我们还没有完成!
使用事件对象
还记得 JavaScript 世界中的本机事件对象吗? 触发事件时,将创建Event
对象,并将其自动传递给使用v-on
指令调用的任何方法。 再次感谢 Vue! 让我们看一些示例来了解这一点。
Event
对象具有“target
”方法,该方法返回触发事件的元素,并与element.tagName
属性一起使用返回元素的 HTML 标记名称。
Index.html
(代码段)
<div>
<!-- Listening to mouse events -->
<button v-on:mouseover="showRole">Hover over me!</button>
{{ role }}
<p>{{ "Tag Name:" + tagName }}</p>
</div>
Index.js
new Vue({
el: "#app",
data: {
role: "",
tagName: ""
},
// define all custom methods within the 'methods' object
methods: {
showRole() {
// 'this' keyword refers to the current Vue instance
this.role = "developer";
this.tagName = event.target.tagName;
}
}
});
使用event.target
将返回“[object HTMLButtonElement]
”,而event.target.tagName
将返回“BUTTON
”。
使用事件对象传递参数
到目前为止,我们肯定知道两件事,
- 将创建
Event
对象,并将其自动传递给触发事件时正在调用的方法。 - 只需在方法名称后面的括号内指定参数,即可将参数传递给这些方法非常容易。
如果我们想使用此自动创建的Event
对象将自己的参数传递,该怎么办? 我们也可以这样做吗?
答案是肯定的! 绝对没错!!
这里要记住的重点是命名。 Vue 将原始 DOM 事件存储在名为“$event
”的变量中。 注意不要覆盖或拼写错误,因为它是 Vue.js 使用的受保护名称,以了解这不是自定义参数,而是Event
对象本身。
让我们对代码进行这些更改。
Index.html
(代码段)
<button v-on:mouseover="showRole('developer', $event)">Hover over me!</button>
{{ role }}
<p>{{ "Tag Name:" + tagName }}</p>
Index.js
(代码段)
showRole(customRole, event) {
// 'this' keyword refers to the current Vue instance
this.role = customRole;
this.tagName = event.target.tagName;
}
在总结之前,这里是供您欣赏的完整代码!
Index.html
<!DOCTYPE html>
<html>
<head>
<title>Hello Vue!</title>
<!-- including Vue with development version CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2>Welcome</h2>
<!-- Using key modifiers with keyboard events -->
<button v-on:keyup.enter="greet">greet</button>
<hr/>
<div>
<!-- Passing arguments along with original DOM event -->
<button v-on:mouseover="showRole('developer', $event)">Hover over me!</button>
{{ role }}
<p>{{ "Tag Name:" + tagName }}</p>
</div>
</div>
<!-- including index.js file -->
<script src="index.js"></script>
</body>
</html>
Index.js
new Vue({
el: "#app",
data: {
role: "",
tagName: ""
},
// define all custom methods within the 'methods' object
methods: {
greet() {
alert("Hi");
},
showRole(customRole, event) {
// 'this' keyword refers to the current Vue instance
this.role = customRole;
this.tagName = event.target.tagName;
}
}
});
因此,我们现在可以将自定义参数与带有$event
变量的自动创建的Event
对象一起传递。 上面讨论的所有代码以及注释都可以在 GitHub 仓库中找到。
这使我们到了本主题的结尾。 祝您有美好的一天!
13 让我们使用简写
原文: https://javabeginnerstutorial.com/vue-js/13-shorthands-for-v-bind-and-v-on/
欢迎回来! 有人说简写吗? 是的,这就是我们今天要关注的重点。 我们已经使用 Vue 指令已有一段时间了。 v-
前缀有多种帮助。 它直观地表示我们正在处理代码中与 Vue 相关的属性(最重要的原因)。
到目前为止,您应该已经了解v-bind
和v-on
是我们模板中最常用的两个指令。 为什么? 因为我们一直在处理事件(特别是单击)和数据绑定! 因此,对于这两个最常用的指令,Vue 为我们提供了捷径或编写它们的简便方法。
起始代码
Index.html
<!DOCTYPE html>
<html>
<head>
<title>Hello Vue!</title>
<!-- including Vue with development version CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2>Welcome</h2>
</div>
<!-- including index.js file -->
<script src="index.js"></script>
</body>
</html>
Index.js
new Vue({
el: "#app",
data: {
},
// define all custom methods within the 'methods' object
methods: {
}
});
v-bind
的简写
让我们使用v-bind
属性将 HTML <a>
标记的href
属性绑定到“https://www.google.com
” URL,
Index.html
(代码段)
<a v-bind:href="url" target="_blank">Google</a>
然后在 Vue 实例的数据对象中定义“url
”,
Index.js
(代码段)
data: {
url: "https://www.google.com"
}
这段代码工作得很好。 单击链接“Google
”,将打开一个新标签,并导航到 Google 页面。 但是我们想看起来很酷。 不是吗?因此,编写v-bind
指令的简短方法是一次性删除v-bind
一词,而仅使用冒号。
<!— Cool way of writing v-bind -->
<a :href="url" target="_blank">Google</a>
刚开始时可能看起来有些混乱,但是一旦您掌握了它,您很快就会感到赞赏。 这两个代码段(带和不带简写)的工作原理完全相同。 区别只是少了一些字符和更易读的代码。
如下图所示,即使是简写形式,所有受 Vue.js 支持的浏览器(在我们的示例中为 Chrome)都可以正确解析它,并将url
的值绑定到href
属性。 请注意,冒号(v-bind
的简写语法)没有出现在最终呈现的 HTML 中,可以在 Chrome DevTools 扩展坞的“元素”窗格中清楚地看到。
v-on
的简写
为了理解v-on
指令的简写语法,让我们有一个按钮。 单击它后,我们将触发名为“greet
”的方法,以在消息Hi
和Hello
之间切换。
完整的语法是
Index.html
(代码段)
<button v-on:click="greet">Click for a different greeting</button>
Index.js
(代码段)
data: {
message: "Hi",
url: "https://www.google.com"
},
methods: {
greet() {
this.message === "Hi" ? this.message = "Hello" : this.message = "Hi";
}
}
好的。 此处的缩写是将单词v-on
和冒号替换为@
符号。 就这样! 它是如此简单!!
Index.html
(代码段)
<!-- Using v-on shorthand --> <button @click="greet">Click for a different greeting</button>
最终代码
Index.html
<!DOCTYPE html>
<html>
<head>
<title>Hello Vue!</title>
<!-- including Vue with development version CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2>Welcome</h2>
<!-- Using v-on shorthand -->
<button @click="greet">Click for a different greeting</button>
<p> {{ message }} </p>
<!-- Using v-bind shorthand -->
<a :href="url" target="_blank">Google</a>
</div>
<!-- including index.js file -->
<script src="index.js"></script>
</body>
</html>
Index.js
new Vue({
el: "#app",
data: {
message: "Hi",
url: "https://www.google.com"
},
// define all custom methods within the 'methods' object
methods: {
greet() {
// 'this' keyword refers to the current Vue instance
this.message === "Hi" ? this.message = "Hello" : this.message = "Hi";
}
}
});
代码看起来更加简洁,优雅且不那么冗长(显然!)。 您将在处理事件负载的实际应用中更好地看到它。 请记住,使用简写语法完全是可选的,完全由您决定。 上面讨论的所有代码以及注释都可以在 GitHub 仓库中找到。
警告:我将在以后的所有代码示例中使用这些简写形式。 与他们合作的次数越多,您就越感到舒适!
为什么要使用简写?
让我们了解使用这些简写来说服您的原因,
- 由于经常以完整形式使用这些指令,因此代码变得冗长。
- 在将 Vue.js 用作管理所有前端代码的框架的应用(例如 SPA)中,很明显正在使用 Vue,而前缀
v-
并不那么重要。 - 干净,易读的代码是每个开发人员最终想要的。
这使我们到了本文的结尾。 祝你今天愉快。
14 使用v-model
进行双向数据绑定
原文: https://javabeginnerstutorial.com/vue-js/14-two-way-binding-v-model/
终于,有一天,要了解难题的缺失部分,即数据绑定。 到目前为止,我们已经了解了如何使用插值,如何将v-bind
用于属性绑定以及如何将v-on
用于监听事件。 缺少的部分是v-model
,它用于双向数据绑定,而这正是我们今天要重点关注的内容!
大字警报!
双向数据绑定:这只是模型(Vue 实例中的data
对象)和视图之间的关系。 对视图所做的任何更改(例如,由用户执行)将立即反映在基础 Vue 模型中,并将在视图中呈现此数据的所有位置进行更新。 换一种说法,
- 使用对
data
属性(模型)所做的更改来更新视图 - 并且,只要在视图中进行更改,数据属性(模型)就会更新
你的头在旋转吗? 别担心! 我们将通过一个示例来理解这个概念。
v-model
指令
通常使用表单输入和控件创建这种双向绑定。 而v-model
是用于实现该目标的指令! 具体来说,以下是v-model
所使用的确切 HTML 元素,
<input>
<textarea>
<select>
- 组件(我们将在“高级 VueJS”部分介绍此主题)
实践示例
让我们以<input>
元素为例,在网页上显示一个文本字段。 让我们在 Vue 实例的data
对象中也具有“message
”属性,该属性用作我们的 Vue 模型,并具有<p>
标签以使用胡须语法(文本插值)呈现值。 现在,我们要
- 用
message
属性的值填充文本字段,例如“hi
”,即使用模型更新视图。 - 每当用户在文本字段中更改值“
hi
”时,都必须在数据属性message
中进行更新,在我们的示例中,即使用视图和视图的更改来更新模型 - 同时将其呈现在
<p>
标签中
通过将v-model
指令添加到<input>
标记并将“message
”属性绑定到它,可以完成所有这三个步骤,
<input type="text" v-model="data_property_to_bind"></input>
Index.html
<!DOCTYPE html>
<html>
<head>
<title>Hello Vue!</title>
<!-- including Vue with development version CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2>Welcome</h2>
<div>
<!-- two-way binding with v-model -->
Greeting Message:
<input type="text" v-model="message"></input>
<p>The message is: {{ message }}</p>
</div>
</div>
<!-- including index.js file -->
<script src="index.js"></script>
</body>
</html>
Index.js
new Vue({
el: "#app",
data: {
message: "hi"
}
});
因此,v-model
指令告诉 vue.js 为输入字段和引号内提到的message
数据属性设置双向绑定。 当然,所有这些都会以反应发生!
初始输出如下。
图像的右半部分显示 Vue DevTools 窗格。 也可以看到具有message
属性及其值的data
对象。
当我们将文本字段中的消息从“hi
”更改为“hello
”时,我们键入的每个字符都将在基础数据模型(如 Vue Devtools 中所示)上进行反应式更新,并在输出结果的视图中进行更新,在<p>
标签中使用小胡子语法。
我强烈建议您如图所示打开 Vue Devtools 并更改文本字段中的值,以查看此更改是被动发生的。 这将是您的盛宴! 我可以保证。
修饰符
v-model
带有三个修饰符。 如果您错过了修饰符部分,请查看他。
用法:遵循v-model
指令,添加一个点并指定修饰符。
.lazy
– 在更改事件(而非输入事件)之后将输入与数据同步
<input v-model.lazy="message">
.number
– 用于将有效的输入字符串转换为数字
<input v-model.number="age">
.trim
– 自动修剪用户输入
<input v-model.trim="message">
该代码一如既往在 GitHub 仓库中可用。
试一试,并注意 Vue 如何根据输入类型是否为文本字段,复选框,单选按钮,选择,多选,文本区域元素等来正确更新元素……它看起来和听起来很神奇,因为你需要用于完成这项艰巨的的所有语法,都是一个简单的指令v-model
。 如果遇到任何问题,请随时在评论部分留言。
祝您有美好的一天!
15 表单输入绑定
原文: https://javabeginnerstutorial.com/vue-js/15-form-input-bindings/
欢迎参加有约束力的讨论! 现在我们已经了解了这个超级巨星v-model
,它用于在表单元素上实现双向数据绑定,让我们直观地了解 Vue 如何在引擎盖下神奇地工作,通过选择正确的更方式来更新特定 HTML 元素,基于它是单选按钮还是复选框还是select
元素。
今天,我们将讨论以下内容,
- 单选框
- 多个复选框
- 单选按钮
- 单选下拉菜单
- 多选下拉菜单
- 带有动态选项的下拉菜单
第一件事第一! 以下是我们将要使用的初始代码,
起始代码
index.html
<!DOCTYPE html>
<html>
<head>
<title>Hello Vue!</title>
<!-- including Vue with development version CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2>Welcome</h2>
</div>
<!-- including index.js file -->
<script src="index.js"></script>
</body>
</html>
index.js
new Vue({
el: "#app",
data: {
}
});
单选框
看下面的代码片段,
index.html
(代码段)
<div>
<h4>Single checkbox with a boolean value</h4>
<!-- form input bindings: single checkbox -->
<input type="checkbox" id="single" v-model="chosen">
<label for="single">{{ chosen }}</label>
</div>
index.js
new Vue({
el: "#app",
data: {
chosen: false
}
});
我们有一个id
,single
复选框。 让我们有一个变量chosen
,该变量在 Vue 实例的data
属性中分配为false
。 选中该复选框可使chosen
的值为true
。 当与输入类型checkbox
一起使用时,如果是布尔值,v-model
就是这样工作的。
多个复选框
现在,让我们有四个复选框,其值均绑定到同一数组。
index.html
(代码段)
<div>
<h4>Multiple checkboxes with values of an array</h4>
<!-- value bindings: multiple checkboxes -->
<input type="checkbox" id="cars" value="Cars" v-model="checkedFilms">
<label for="cars">Cars</label>
<input type="checkbox" id="brave" value="Brave" v-model="checkedFilms">
<label for="brave">Brave</label>
<input type="checkbox" id="up" value="Up" v-model="checkedFilms">
<label for="up">Up</label>
<input type="checkbox" id="ratatouille" value="Ratatouille" v-model="checkedFilms">
<label for="ratatouille">Ratatouille</label>
<br/>
<span>Selected pixar films are: {{ checkedFilms }}</span>
</div>
index.js
(代码段)
data: {
checkedFilms: []
}
因此,所有四个输入元素均为checkbox
类型,并且每个元素都具有与数组checkedFilms
”的双向绑定。 请注意,该数组的初始值在 Vue 实例的data
属性中为空。 在这里v-model
的工作方式是,在数组checkedFilms
中填充所选复选框的值。
单选按钮
很容易猜出v-model
在单选按钮下的行为。 由于我们只能从组中选择一个单选按钮,因此绑定两种方式的变量将获取所选单选按钮的值。
index.html
(代码段)
<div>
<h4>Radio buttons</h4>
<input type="radio" id="html" value="HTML" v-model="selected">
<label for="html">HTML</label>
<br>
<input type="radio" id="css" value="CSS" v-model="selected">
<label for="two">CSS</label>
<br>
<input type="radio" id="javascript" value="JavaScript" v-model="selected">
<label for="JavaScript">JavaScript</label>
<br>
<span>Selected option: {{ selected }}</span>
</div>
index.js
(代码段)
data: {
selected: ''
}
根据选择的选项,变量selected
获得HTML
或CSS
或JavaScript
的值。
单选下拉菜单
这是一个正常的下拉列表,您可以在其中选择一个选项。
index.html
(代码段)
<div>
<h4>Radio buttons</h4>
<input type="radio" id="html" value="HTML" v-model="selected">
<label for="html">HTML</label>
<br>
<input type="radio" id="css" value="CSS" v-model="selected">
<label for="two">CSS</label>
<br>
<input type="radio" id="javascript" value="JavaScript" v-model="selected">
<label for="JavaScript">JavaScript</label>
<br>
<span>Selected option: {{ selected }}</span>
</div>
index.js
(代码段)
data: {
singleSelect: ''
}
变量singleSelect
最初是一个空字符串。 由于v-model
的魔力,它获得了从下拉列表中选择的选项的值。
多选下拉菜单
在这里,我们可以从下拉菜单中选择多个选项,因此 Vue 实例的data
属性中multiselect
变量的初始值是一个空数组。
index.html
(代码段)
<div>
<h4>Multiselect dropdown</h4>
<select v-model="multiselect" multiple>
<option value="Dance">Dance</option>
<option value="Swim">Swim</option>
<option value="Paint">Paint</option>
<option value="Play tennis">Play tennis</option>
</select>
<br>
<span>I know to: {{ multiselect }}</span>
</div>
index.js
(代码段)
data: {
multiselect: []
}
将使用从下拉列表中选择的所有值填充数组。 (对于多项选择,请向下按Ctrl
键,然后选择。)
带有动态选项的下拉菜单
在实时场景中,下拉菜单的选项并不总是已知的。 另外,对值进行硬编码永远不是一个好习惯。 因此,在必须处理动态数据的情况下,可以将v-bind
与v-model
一起使用。 结合使用它们会赋予您无穷的力量! 让我们也利用v-for
指令遍历我们可能要处理的所有选项。
index.html
(代码段)
<div>
<h4>Handling dynamic options</h4>
<select v-model="dynamicSelection">
<option v-for="letter in alphabet" v-bind:value="letter.word">
{{ letter.for }}
</option>
</select>
<span>Selected letter: {{ dynamicSelection }}</span>
</div>
Index.js
(代码段)
data: {
dynamicSelection: 'Apple',
alphabet: [
{ word: 'Apple', for: 'A' },
{ word: 'Bat', for: 'B' },
{ word: 'Cat', for: 'C' }
]
}
在此,对于alphabet
数组中的每个对象,下拉列表中均列出了“A”,“B”和“C”,并且word
绑定到了选项标签的value
属性。 根据选择的值,word
在dynamicSelection
变量中更新。 请注意,初始值是alphabet
数组中的第一个单词。
到此为止,让我们看一下到目前为止的完整代码。
index.html
<!DOCTYPE html>
<html>
<head>
<title>Hello Vue!</title>
<!-- including Vue with development version CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2>Welcome</h2>
<div>
<h4>Single checkbox with a boolean value</h4>
<!-- value bindings: single checkbox -->
<input type="checkbox" id="single" v-model="chosen">
<label for="single">{{ chosen }}</label>
</div>
<hr/>
<div>
<h4>Multiple checkboxes with values of an array</h4>
<!-- value bindings: multiple checkboxes -->
<input type="checkbox" id="cars" value="Cars" v-model="checkedFilms">
<label for="cars">Cars</label>
<input type="checkbox" id="brave" value="Brave" v-model="checkedFilms">
<label for="brave">Brave</label>
<input type="checkbox" id="up" value="Up" v-model="checkedFilms">
<label for="up">Up</label>
<input type="checkbox" id="ratatouille" value="Ratatouille" v-model="checkedFilms">
<label for="ratatouille">Ratatouille</label>
<br/>
<span>Selected pixar films are: {{ checkedFilms }}</span>
</div>
<hr/>
<div>
<h4>Radio buttons</h4>
<input type="radio" id="html" value="HTML" v-model="selected">
<label for="html">HTML</label>
<br>
<input type="radio" id="css" value="CSS" v-model="selected">
<label for="two">CSS</label>
<br>
<input type="radio" id="javascript" value="JavaScript" v-model="selected">
<label for="JavaScript">JavaScript</label>
<br>
<span>Selected option: {{ selected }}</span>
</div>
<hr/>
<div>
<h4>Single select dropdown</h4>
<select v-model="singleSelect">
<option disabled value="">Please select one</option>
<option>Chocolate</option>
<option>Ice cream</option>
<option>Green Tea</option>
</select>
<span>I love: {{ singleSelect }}</span>
</div>
<hr/>
<div>
<h4>Multiselect dropdown</h4>
<select v-model="multiselect" multiple>
<option value="Dance">Dance</option>
<option value="Swim">Swim</option>
<option value="Paint">Paint</option>
<option value="Play tennis">Play tennis</option>
</select>
<br>
<span>I know to: {{ multiselect }}</span>
</div>
<hr/>
<div>
<h4>Handling dynamic options</h4>
<select v-model="dynamicSelection">
<option v-for="letter in alphabet" v-bind:value="letter.word">
{{ letter.for }}
</option>
</select>
<span>Selected letter: {{ dynamicSelection }}</span>
</div>
</div>
<!-- including index.js file -->
<script src="index.js"></script>
<link rel="stylesheet" href="style.css"></link>
</body>
</html>
index.js
new Vue({
el: "#app",
data: {
chosen: false,
checkedFilms: [],
selected: '',
singleSelect: '',
multiselect: [],
dynamicSelection: 'Apple',
alphabet: [
{ word: 'Apple', for: 'A' },
{ word: 'Bat', for: 'B' },
{ word: 'Cat', for: 'C' }
]
}
});
与往常一样,可以在 GitHub 仓库中找到整个代码,以方便参考。 随意拨叉并编写代码以符合您的内心需求。
现在是时候在您自己的示例中练习其中的一些表单输入,如果您有任何疑问,请在评论部分留言。
祝你今天愉快!
18 类绑定
原文: https://javabeginnerstutorial.com/vue-js/18-class-bindings/
欢迎再次参加具有约束力的讨论。 今天,我将为您学习类绑定。 换句话说,我们将通过使用 Vue 将数据绑定到 HTML 元素的class
属性来对其进行处理。 为什么? 因为可能需要我们经常操作元素的类列表。 就像任何其他数据绑定一样,我们在此处也执行相同的歌曲和舞蹈,即强大的“v-bind
”或其快捷方式“:
”可用于处理这种情况。 表达式可以计算为字符串,对象或数组。
听起来令人困惑? 别担心! 在一些示例的支持下,让我们一次迈出一步。 您会立即看到自己冲刺!
初始代码:
index.html
<!DOCTYPE html>
<html>
<head>
<title>Hello Vue!</title>
<!-- including Vue with development version CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2>Welcome</h2>
<div>
<!-- OUR CODE GOES HERE -->
</div>
</div>
<!-- including index.js file -->
<script src="index.js"></script>
<link rel="stylesheet" href="style.css"></link>
</body>
</html>
index.js
new Vue({
el: "#app",
data: {}
});
style.css
<!-- LET US WRITE SOME CODE AS WE GO -->
对象语法
假设我们有一个名为“success
”的类,其样式在style.css
样式表中定义如下,
.success {
color: green;
font-weight: bold;
font-size: 70px;
}
使用 Vue,通过将其作为对象传递给v-bind:class
来动态切换此类非常容易。
index.html
(代码段)
<div id="app">
<h2>Welcome</h2>
<div>
<!-- class bindings: Objects -->
<div :class="{ success : successFlag }">
Hello!!
</div>
<p>The value of successFlag is {{ successFlag }}</p>
</div>
</div>
快捷方式:
用于此处的指令v-bind
。
index.js
new Vue({
el: "#app",
data: {
successFlag : true
}
});
非常简单! 我们有一个div
标签,显示为“Hello !!
”。 由于data
属性的successFlag
值为true
,因此div
标签中包含类success
,并且其样式将应用于标签中的文本。
在浏览器中可以清楚地看到结果。 Chrome DevTools 窗格显示 HTML 代码中如何显示“success
”类。
如果我们将successFlag
的值设置为false
,那么这就是输出的样子。
index.js
(代码段)
data: {
successFlag : false
}
奇迹般有效!
如果我们拥有普通的class
属性,并且在此属性之上,我们想借助v-bind:class
来切换另一个类的存在? 换句话说,我们可以在一个元素上同时具有class
和v-bind:class
吗? 可能吗?
YESSSS! 使用 Vue,几乎所有内容都是肯定的!
最好的部分是,我们可以通过两种方式实现这一目标。
方法 1:内联对象
index.html
(代码段)
<div>
<!-- class bindings: Objects -->
<div class="underline" :class="{ success : successFlag, bcg : bcgFlag }">
Hello!!
</div>
<p>The value of successFlag is {{ successFlag }}</p>
</div >
index.js
(代码段)
data: {
successFlag: false,
bcgFlag: true
}
style.css
(代码段)
.underline {
text-decoration-color: red;
text-decoration-line: underline;
}
.bcg {
background-color: aqua;
padding: 5px;
width: fit-content;
}
普通的class
属性具有将始终呈现的类underline
。
还可能在对象中将更多字段传递给v-bind:class
。 为了理解这一点,让我们根据其值的真实性切换两个类。 在上面的代码中,两个类success
和bcg
作为对象传递给v-bind:class
。 successFlag
和bcgFlag
分别设置为false
和true
。 因此,呈现的类列表将变为“underline bcg
”,并且仅应用那些样式。
方法 2:绑定非内联对象
index.html
(代码段)
<div class="underline" :class="classObject"> Hello!! </div>
index.js
(代码段)
data: {
classObject: {
success : false,
bcg : true
}
}
在 vue 实例的数据中指定了要绑定到v-bind:class
的对象。
数组语法
可以使用类以数组的形式将类传递给v-bind:class
,而不是使用对象,
index.html
(代码段)
<!-- class bindings: Arrays -->
<div class="underline" :class="[successClass, bcgClass]">
Array Bindings.
</div>
index.js
(代码段)
data: {
successFlag: false,
bcgFlag: true,
successClass: 'success',
bcgClass: 'bcg'
}
为简单起见,本示例也使用相同的类。
在 chrome DevTools 中检查时,这将呈现所有三个类,
<div class="underline success bcg">
上面讨论的所有代码都可以在 GitHub 仓库中找到。
启动您喜欢的 IDE,并编写一些自己的方案! 如有任何疑问,请随时在“评论”部分中进行提问。 请继续关注,因为我们的下一篇文章都是关于绑定内联样式的。
Python 教程
Python 3 简介
原文: https://javabeginnerstutorial.com/python-tutorial/an-introduction-to-python-3-2/
在本文中,我将向您介绍 Python3。这是有关 Python 的系列文章的第一篇,旨在为您提供一个教程,您可以在其中开始学习 Python 并获得动手示例以简化学习工作。
Python 是高级,交互式,面向对象和解释脚本语言。 它被设计为具有可读的源代码,并且在其他编程语言使用标点符号的情况下,它更频繁地使用英语关键字。
让我们看看每个定义隐藏在其名称后面的内容:
- 高级:Python 易于开发人员阅读和编写。 当您深入到机器级别时,代码将变得更不易读了-最后,当计算机执行命令时,它只是一系列的 1 和 0。
- 交互式:Python 使您可以直接在解释器中进行编码,并且在您按回车键时将执行代码。
- 面向对象:Python 支持将信息封装到对象中的面向对象的开发风格。
- 解释:Python 在运行时由 Python 解释器处理,因此执行之前不需要任何编译。 这类似于 PHP 或 Perl 或 Shell 脚本。
为什么要学习?
如果您尚未确信要学习 Python,那么我只能告诉您一件事:大公司使用 Python 进行开发。 这些大公司中有一些是:Google,Dropbox 和迪士尼。
这些公司各自以不同的方式使用 Python。 Google 的 Youtube 主要基于 Python; Google App Engine 的第一个版本是使用 Python 开发的,其想法是仅在平台上使用 Python。 Dropbox 从一开始就使用 Python 开发,当他们注意到他们使用 Python 代码库为 4000 万客户提供服务时。 迪士尼使用 Python 来推动其创作过程。 顺便说一句:NASA 在某些开发中也使用 Python。
所有这些都是因为 Python 是一种易于学习且易于阅读的编程语言。
Python 2 和 Python 3 之间的区别
当前有两个主要版本供开发人员使用:2 和 3。我将使用 Python 3.5,因为它是最新版本,几乎每个 Python 3 脚本都可以使用 Python 2 解释器执行。 我说这几乎是因为存在一些差异,需要增加新的主要版本号。
由于 Python 3 是使用新版本号引入的,因此它在 Python 2 之间必须有所区别。因此,让我们简要介绍一下在开发 Python 2 和 Python 3 时可能遇到的主要区别:
- 整数除法
- 打印成为函数而不是语句
- 字符串在 Python 3 中默认为 unicode
- 异常的提出和处理发生了变化
- 输入解析已更改
xrange
已被删除,范围像以前的xrange
一样实现
在运行时确定 Python 版本
一种解决方案是在运行时确定 Python 版本并在用户使用错误版本执行脚本时退出应用。 为此,让我向您展示一个简单的代码。 现在,请看一下,您就会知道:您可以稍后回到本节中,根据所收集的知识进行修改。
import sys
if sys.version_info[0] != 3:
print("Wrong Python version used")
exit(-1)
或者,我们可以引发和异常-但在后面的文章中,当更详细地了解异常处理时,会更多地讨论这个问题。
安装 Python
Python 3.5 当前仅是候选版本。 这意味着不鼓励 Python 开发人员在生产环境中使用此版本-但计划于 9 月中旬推出该版本,因此在不久的将来会非常实际,并且候选发布版很可能包含最终版本。
也许您已经注意到您已经在计算机上安装了 Python。 要验证这一点,您必须输入
python
在命令提示符下,然后按回车键。 如果已安装,则应显示以下消息:
Python 2.7.10 (default, Jun 10 2015, 19:42:47)
[GCC 4.2.1 Compatible Apple LLVM 6.1.0 (clang-602.0.53)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
此消息在第一行包含版本信息。 如果仔细看,您会发现大多数时候都是 python 2.7(至少它已经安装了 Macs)。
要查看是否已安装 Python 3,请输入
python3
在命令提示符下,然后按回车键。 在大多数情况下,此版本尚未安装,因此让我们看一下如何安装。
您可以在中找到我安装的版本。
因为基本的分布式版本几乎适用于所有平台,所以我建议您按照站点提供的安装说明进行操作;或者,如果您具有 Mac 或 Windows OS,则可以像我那样获得可执行安装程序,因此没有复杂的安装步骤。 如果您使用的是 Linux,那么您现在应该知道如何在系统上编译(或安装)扩展程序和软件。
就是这样。 现在是时候启动 Python 解释器外壳了,可以确定安装正常:
python3
Python 3.4.2 (v3.4.2:ab2c023a9432, Oct 5 2014, 20:42:22)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
您应该会看到与此类似的内容,并显示正确的 Python 版本号。
使用其他模块
有时,您不想重新发明轮子,因为您已经完成了它,或者找到了可以用于您的目的的模块。
在这种情况下,您可以导入包含所需功能的模块。 我们看到了如何使用位于同一文件夹中的自己的脚本来完成此操作。 但是,它如何与其他开发人员的脚本一起使用?
用pip
安装
某些模块可通过 PyPI (Python 包索引)使用。
可能您已经听说过pip
命令。 它是“Python 安装包”或“Python 包安装器”或更多程序的缩写。 您也可以使用 on 版本。 您应该关心的主要事情是,使用pip
可以通过一个命令安装模块及其依赖项。 如果您有更新版本的模块,也可以使用pip
进行更新。
让我们继续并安装lxml
模块,稍后当我告诉您有关使用 Python 处理 XML 时将需要用到。
$ pip3 install lxml
Collecting lxml
Downloading lxml-3.4.4.tar.gz (3.5MB)
100% |████████████████████████████████| 3.5MB 153kB/s
Installing collected packages: lxml
Running setup.py install for lxml
Successfully installed lxml-3.4.4
如果您想知道为什么我使用pip3
而不是pip
,则原因非常简单:Python 2 具有自己的pip
,并且如果您为其他 Python 版本安装模块,则显然其他版本不可用。
要查看pip
命令是否适合您,您可以查找其版本并查看其修改的 Python 版本:
$ pip -V
pip 7.0.3 from /usr/local/lib/python2.7/site-packages/pip-7.0.3-py2.7.egg (python 2.7)
$ pip3 -V
pip 1.5.6 from /Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages (python 3.4)
上面的代码片段告诉我,我必须使用pip3
,因为它是 Python 3.4 的正确版本。
定位模块
有时,您没有要在 Python 包索引中使用的模块(例如,您从 GitHub 获得了代码),或者您有一个结构化的项目,其中包含一些文件夹和 Python 脚本文件,而您需要在另一个文件中使用一个文件。
在这种情况下,您可以使用以下经验法则:模块是不带.py
扩展名的 Python 脚本文件的名称,文件夹必须在脚本名称前添加点号(.
)作为分隔符。
命名导入的模块
如您所见,通过文件夹结构或长名称导入模块对于长期使用而言可能会成为问题。 我的意思是考虑以下几点:导入多处理。 在这种情况下,您每次都要键入multiprocessing.something
才能获得所需的函数或类。
否则,当您有两个具有相同名称的模块或要从恰好具有相同名称的不同模块中导入两个方法时,可能会发生另一个问题。 在这种情况下将使用哪一个? 您如何同时使用两者?
幸运的是,有一个解决方案:您可以为导入的模块甚至导入的部件(函数,类)指定自定义名称。
>>> import multiprocessing as mp # from now on you can use 'mp' instead of 'multiprocessing'
>>> from calendar import isleap
>>> from calendar import isleap as leap1 # 'leap1' has the same functionality than isleap
>>> isleap(2015)
False
>>> leap1(2016)
True
Python 基础知识 - 又称 Hello World 以及如何实现
在介绍并安装 Python 之后,让我们继续前进,并使用 Python 3 编写第一行代码。
第一步是使用 python3 命令启动交互式解释器。 三个箭头(或更大的符号)指示提示,我们可以在其中键入解释器的命令。 我们将输入以下文本:“print('We want a shrubbery!')
”,然后按回车键。
以下代码段显示了您应该看到的内容:
Python 3.4.2 (v3.4.2:ab2c023a9432, Oct 5 2014, 20:42:22)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> print('We want a shrubbery!')
We want a shrubbery!
这是基本的 Python 应用,它将给定的字符串输出到控制台。 基本上,我们将使用print
函数将所有消息写入控制台。
基本语法
Python 的基本语法与其他编程语言非常相似。 但是,有些差异会在开发应用时引起麻烦。
因此,让我们研究 Python 应用的基本语法。
缩进
这是您可能遇到的最令人不安的事情。 如果您没有正确配置 IDE 或使用其他格式设置扩展由他人编写的代码,这可能会在以后让您彻夜难眠。
但是缩进是什么意思?
在执行分组在一起的语句时,使用最广泛的编程语言会使用大括号({}
)。 Python 利用缩进,因为这样代码更易读。 让我们用一个小示例来演示一下:
Java 代码
if(a == 42){a = 25; System.out.println(a);}else{a = 42;}
Python 代码
if a == 42:
a = 25
print(a)
else:
a = 42
如您所见,Python 代码更具可读性(尽管我也是一个有良好基础的 Java 开发人员,但我认为这是我的看法,因此我可以覆盖前面的示例以使其可读)。 如果您有缩进行(请仔细查看 python 代码中的缩进),则表示它属于同一执行组。 例如,在 Java 中,它将不会:
if(a == 42)
a = 25;
System.out.println(a); // Indention will not work here. And it will give compilation error
else
a = 42;
如果不使用花括号,则会出现SyntaxError
,因为在上面的情况下System.out.println(a);
不属于if
语句。
但是这不是一篇有关 Java 或这两种编程语言之间的差异的文章,因此我从这件事回到缩进的主题。
为什么会导致不眠之夜? 因为您有两种缩进方式:空格或 TAB。 而且您不能将两者混为一谈! 如果这样做,则会出现TabError
:
>>> a = 33
>>> if a == 42:
... a = 25
... print(a)
File "<stdin>", line 3
print(a)
^
TabError: inconsistent use of tabs and spaces in indentation
在上面的示例中不可见,但是首先我使用空格,然后使用制表符使行缩进。 这导致异常。
如果我切换线路,则会收到另一个错误:
File "<stdin>", line 3
a = 25
^
IndentationError
:缩进与任何外部缩进级别都不匹配
这就是我不眠之夜的意思。 如果您混淆了所有内容,则必须浏览文件,找到在每个可能的地方都用制表符交换空格,反之亦然。
注释
注释代码有时是必要的(尽管一些开发人员说,如果必须编写注释来描述代码,则应重新编写代码)。
您可以使用井号(#
)。 这告诉解释器,井号后面的同一行上的文字仅是注释,它们无意执行:
>>> print(1+2) # results in 3 : this is comment
3
注意与其他编程语言一样,没有多行注释。 如果要禁用代码块,则必须在每行的开头放置#
。
标识符
标识符是用于标识变量,类,函数,模块或其他对象的名称。
在 Python 中,您可以以字母(大写或小写无关紧要)或下划线(_
)开头,并在其后跟随零个或多个字母,数字和下划线。 标识符名称中不允许使用标点符号(例如@
,$
和%
)。
该语言区分大小写,这意味着您可以拥有一个名为my_variable
的变量和另一个名为My_variable
的变量,它们将被视为不同的变量。 因此要小心,因为这在开发时也会引起问题。
保留字
高级编程语言几乎是自由形式的代码编写。 但是,有一些内部字词是为特殊用例保留的,当解释器遇到它们时,它们对解释器具有意义。
这意味着您不能将这些关键字用作标识符名称。 在其他情况下,您会根据关键字获得错误。
False class finally is return None continue for lambda try True def from nonlocal while and del global not with as elif if or yield assert else import pass break except in raise
当然,它们是区分大小写的。 这意味着您可以使用false
作为变量名,但不能使用False
。
用户输入
当您可以编写交互式应用时,编程会变得很有趣。 交互性意味着用户可以向应用输入一些输入,然后他/她将成为应用的答案。
我现在不会从这里开始编写带有用户输入的应用,但是我将告诉您如何要求用户提供一些东西。
它带有输入函数,并且使用非常简单:
>>> entered = input("Enter some text: ")
Enter some text: 42
>>> entered
'42'
如您所见,您可以将参数传递给input
函数,并且该参数将显示为用户的命令提示符。 他/她输入内容后,将其作为字符串存储在输入的变量中。
稍后,我们将更全面地检查input
函数。
基本运算符
现在是时候看看 Python 中可用的基本运算符了。 我知道在不了解基本类型的情况下并没有什么用,但是,嘿! 我们已经知道语言中至少有数字和字符串,这对运算符来说是一个很好的起点。
算术运算符
每个人都知道它们,它们是数学的基础。 因此,Python 也必须了解它们。 如果它们来自正确的类型,它们也可以使用变量。 这意味着您不能在数字和字符串上使用加法。
让我们快速列举一些示例。 我希望这些示例能说明自己。 如果不是这种情况,请随时给我写一封信息,我还将在示例中添加说明性文字。
>>> 6 + 5 # the + sign adds numbers up
11
>>> 6 - 5 # the - sign subtracts the second number from the firs
1
>>> 6 * 5 # the * sign multiplies the numbers
30
>>> 6.0 / 5 # the / sign divides the numbers
1.2
>>> 6.0 // 5 # the // sign makes an integer division
1.0
>>> 6 % 5 # the % calculates the modulo
1
>>> 6 ** 5 # the ** sign raises the first number to the power of the second
7776
比较运算符
这些运算符比较值(变量或值本身不再重要)。 结果来自布尔类型,可以为True
或False
。
>>> 5 == 4 # == is the equality operator, returns True when both sides are equal
False
>>> 4 == 4
True
>>> 5 != 5 # != is the inequality operator, returns False when both sides are equal
False
>>> 5 > 5 # > is the greater than operator
False
>>> 5 < 5 # < is the less than operator
False
>>> 5 >= 5 # >= is the greater or equal operator
True
>>> 5 <= 5 # is the less or equal operator
True
赋值运算符
我们已经知道 Python 中的赋值运算符,即等号(=
)。 但是,这些运算符中有相当一部分具有不同的用法,但所有这些运算符都会减少一些编写所需的代码。
在下面的示例中,我将一遍又一遍地使用相同的变量来说明这些运算符的工作方式。 同样,如果您想知道示例的含义,请随时给我写邮件,并在这些示例中添加说明。
仅输入变量名(在本例中为a
)并按回车键,便可以在交互式解释器中看到变量的值。
>>> a = 5
>>> a += 3 # a has the value of 8
>>> a -= 2 # a has the value of 6
>>> a *= 3 # a has the value of 18
>>> a /= 4 # a has the value of 4.5
>>> a //= 1 # a has the value of 4.0
>>> a %= 6 # a has the value of 4.0
>>> a **= 2 # a has the value of 16.0
>>> a
16.0
逻辑运算符
它们并不多:和,或不。 当我们看看爱是什么时,我们已经在复活节蛋中遇到了它们。 它们是逻辑运算符,因此最好是在使用它们的地方具有布尔值。
但是,这里有一点例外。 0,空对象和None
被视为False
,因此您可以在条件表达式中使用它们(稍后将在后面的文章中使用)。 数字和非空对象当然被视为True
。
现在,让我们看一些示例:
>>> a = True
>>> b = False
>>> a and b
False
>>> a or b
True
>>> not a
False
>>> not b
True
>>> not 0
True
>>> not 1
False
>>> not [] # this is an empty list
True
成员运算符
当我们想查看某物是否为集合的成员时,它会派上用场。 例如,数字 4 在前 5 个质数的列表中。
成员运算符是in
关键字。 有些书也没有添加到此列表中,但据我们了解,不是一个逻辑运算符,它否定了所使用的值。
结果是布尔值True
或False
。
>>> primes = [2,3,5,7,11]
>>> 4 in primes
False
>>> 4 not in primes
True
>>> 5 in primes
True
如何在 Windows 中安装 python
原文: https://javabeginnerstutorial.com/python-tutorial/how-to-install-python-in-windows/
如果使用 Windows,则必须比使用类似 Unix 的系统(Linux,Mac)做更多的配置。
您唯一需要做的就是将 Python 和pip
添加到PATH
,以便通过命令行使用它。 这可以通过三种方式实现。
一种是临时的,您编写一个批处理脚本,每次您通过此脚本打开命令行时,该脚本都会启动并设置PATH
。 我不会更详细地说明如何实现这一点,因为我认为这不是最好的方法,有时您想从自己所在的位置访问 Python。
这两个永久性解决方案是相同的,唯一的区别是,在一个版本中,您必须手动执行相同的步骤,而另一个版本则需要自动执行。
自动版本是在安装 Python 时选中“将 Python 3.5 添加到PATH
”复选框。 这会自动在路径中创建所需的条目,使您可以通过命令行访问 Python。
另外,如果您选择“自定义安装”,您也可以选择将 Python 添加到PATH
中。
在安装过程中将 Python 添加到PATH
第二个版本适用于那些错过了上一步的人。 在这种情况下,您必须手动编辑路径。 现在是以下步骤:
在 Windows 上打开“开始菜单”,然后选择“控制面板”:
打开控制面板
在“控制面板”中选择“系统和安全性”:
选择系统和安全性
在“系统和安全性”视图中,选择“系统”:
选择系统
在“系统”视图中,从左侧选择“高级系统设置”:
单击高级系统设置
在“高级系统设置”窗口中,从底部选择“环境变量…”:
打开环境变量
现在,您可以在窗口下部的“系统变量”中搜索“路径”条目。 在这里,您应该选择条目,然后单击“编辑...”。 现在,将 Python 的安装位置添加到该条目的末尾,并用分号将其与之前的条目分开。 例如,我将输入以下内容:
C:\Python3\Scripts\; C:\Python3\
Python 安装下的脚本文件夹包含点,因此将其也添加到路径是明智的。
系统变量
另外,您可以仅为您的用户创建一个环境变量。 在这种情况下,在窗口的上部选择“新建”,称为“...的用户变量”,然后添加一个新条目,其中变量设置为“PATH
”,并且值是您安装 Python 的位置 - 在上一步中。
用户变量
现在,如果您打开命令行并输入 python ,您应该会看到类似以下命令提示符的内容:
使用 Python 的命令提示符
就是这样。 现在,您每次打开命令行即可访问 Python。
如果要在其他平台上安装 python。 您可能希望在我们讨论 linux python 安装的地方看到本文。
参考文献
适用于 Windows,Mac,Linux 的 Python 设置
原文: https://javabeginnerstutorial.com/python-tutorial/python-setup-for-windows-mac-linux/
Python 适用于各种平台,包括 Linux,Windows 和 Mac OS。 因此,在这里我们将了解如何设置 python 环境。 对于本地环境设置,请打开终端窗口,然后在其中键入“python”以知道它是否已安装(如果已安装,则说明是哪个版本)。
- Unix/Linux
- Windows
- MacOS
- 其它
python 的安装程序也可以从官方网站安装以获得最新版本。 链接在这里
对于 python 的文档,可以从上面的相同链接下载。
如何安装 Python?
我们知道 python 可用于各种平台,因此您只需要下载适用于您的平台的二进制代码并安装它即可。 如果二进制代码不可用于您的平台,则需要 C 编译器手动编译代码。
现在,我们将看到如何在不同的平台上安装 python:
Unix 和 Linux 安装:
在计算机上安装 python 所需的步骤是:
- 打开网络浏览器,然后转到 http://www.python.org/download/ 。
- 请按照上面给出的链接下载压缩的源代码。
- 下载后,解压缩文件。
- 如果需要,请编辑模块/设置文件。
- 运行
./configure
脚本 - 配置
- 进行安装
此命令将 python 安装在标准位置/usr/local/bin
和/usr/local/lib/pythonAA
中的库中,其中AA
是 python 的版本。
现在,安装后,我们将设置路径。 在 Unix/Linux 上设置路径:
在 csh shell 中添加 Python 目录的路径为:
输入setenv PATH "$PATH:/usr/local/bin/python
,然后按Enter
。
注意:请记住,Unix 是区分大小写的。
在 Linux 的 bash shell 中添加 Python 目录的路径:
输入export PATH="$PATH:/usr/local/bin/python"
,然后按Enter
键
在 sh 或 ksh shell 中添加 Python 目录的路径:
输入PATH="$PATH:/usr/local/bin/python"
,然后按Enter
。
Windows 安装:
在 Windows 上安装 Python 所需的步骤是:
- 打开网络浏览器,然后转到 http://www.python.org/download/ 。
- 单击此链接可获取 Windows 安装程序
python-ABC.msi
文件,其中ABC
是版本。 - 当 Windows 支持 Microsoft Installer 2.0 时,可以使用安装程序
python-ABC.msi
。 将安装程序文件保存到本地计算机上,然后运行以确保您的计算机支持 MSI。 - 现在运行下载的文件。 安装向导后,接受默认设置,然后等待安装完成。
安装后,我们将在 Windows 中设置路径。 要在 Windows 的命令提示符下添加 python 目录:
type path %path%;
C: \Python and then press Enter. // It is the path of the python directory
Macintosh 安装
在 Mac 中使用 Python 时,无需安装或配置其他任何东西。 但是在安装 Python 之前,您需要先安装 GCC。 可以通过下载 XCode,命令行工具或 OSX GCC 安装包来获得 GCC。 但是,如果您已安装 XCode,则无需安装 OSX-GCC 安装程序,因为它可能会引起问题。 在执行 XCode 的全新安装时,您需要通过在终端上运行xcode-select –install
来添加命令行工具。 由于 OS X 由大量 UNIX 工具组成,因此熟悉 Linux 系统的人会注意到缺少一个像样的包管理器的关键组件,因此 Homebrew 将填补这一空白。
现在要安装 Homebrew,请打开终端或 OS X 终端仿真器并运行
$ ruby –e “$(curl –fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)”
安装 Homebrew 之后,将 Homebrew 目录添加到路径环境变量的顶部,或者可以通过以下方法完成
~/ .profile file
路径声明为
Export PATH = /usr/local/bin: /usr/local/sbin: $PATH
现在可以安装 Python 2.7。
$ brew install python
现在我们来看看简单的 python 程序。
num1 = input('Enter first number: ')
num2 = input('Enter second number: ')
#it will add two numbers
sum = float(num1) + float(num2)
# display the sum
print('The sum of {0} and {1} is {2}'.format(num1, num2, sum))
输出:
Enter first number: 4
Enter second number: 4
The sum of 4 and 4 is 8.0
在上面的代码中,数字是由用户输入的,它显示了由用户输入的两个数字的总和。 在此,我们使用了内置函数input()
从用户那里获取输入,它将返回字符串,然后将其转换为使用float()
函数的数字。 这两个数字通过算术运算符(+
)相加,我们还可以根据程序要求(例如减法,乘法,除法)更改运算符。
Python 数字和字符串
原文: https://javabeginnerstutorial.com/python-tutorial/python-numbers-and-strings-2/
在本文中,我们将介绍 Python 3 数字和字符串,因为它们是使用 Python 开发的基础。
Python 3 数字
您可能已经想到,数字数据类型存储数字值,例如整数和浮点数。 尽管您应该了解数字的另一种类型:复数。 让我们简要看一下这三种数字。
- 整数(也称为整数)是没有小数点的整数,可以包含正数或负数。 只要您希望提供良好的计算可能性,整数就可以。
- 浮点数是浮点数,这意味着即使它们是整数,它们也包含一个小数点。 它们涵盖了一组实数,因此您可以对其进行任何操作。
- 大多数情况下,复数出于科学目的。 但是很高兴知道还有更多东西。 您可能从数学上知道复数表示为
+ bi
,其中i
是表示 -1 的平方根的虚部。 但是在 Python 中,虚部表示为j
,因此,如果要编写复数,则应执行:c = 42 + 26j
。
特殊数字
您也可以在 Python 中使用两个常用的数字(至少在数学上是如此)。 这些数字是pi
和e
。 要使用它们,您必须导入数学模块:
>>> import math
>>> math.pi
3.141592653589793
>>> math.e
2.718281828459045
随机数
在一些书籍或教程的帮助下开始开发时,您会遇到随机数生成的问题。 稍后我们将使用随机数。 但是,现在是使用 Python 引入随机数生成的好时机。
为此,我们必须导入一个模块:random
。
>>> import random
>>> random.choice((1,2,3,4,'a')) # chooses one item of the provided list, tuple or string (it has to be an idexable object)
2
>>> random.random() # returns a random floating point number which is greater or equal to `0` and less than `1`
0.17489302862620337
>>> l = [1,2,3,4,5]
>>> >>> random.shuffle(l) # shuffles the list in place, returns `None`
>>> l
[1, 4, 3, 2, 5]
>>> random.randrange(1,10) # selects a random integer between the range of the first and the second parameter
4
>>> random.randrange(1,10,2) # the third parameter is an optional integer, defines the step in the range to select the elements from
5
字符串
字符串在 Python 中定义为引号之间表示的字符列表。 我们可以使用单引号('
或单引号)或双引号("
)来表示字符串。 我们可以在一个字符串中使用另一个字符串,但是如果您使用单引号将字符串开头,则也必须以该字符串结尾。
这些字符串是单行字符串,这意味着您无法将它们分散在多行中,这会导致错误。 但是,在讨论注释时,我们讨论了以三个引号开头的字符串(再次使用单引号或双引号也没有关系)。 它们可以通过多条线传播。 但是,它们会在最后转换为常规字符串,并且在写文本时仅在换行符(\n
)处按回车符。 这种字符串最常见的用法是文档。 当我们到达函数和类时,我们将讨论这个主题。
让我们举个简单的示例:
>>> "this is a
File "<stdin>", line 1
"this is a
^
SyntaxError: EOL while scanning string literal
>>> 'this is line "
File "<stdin>", line 1 # Starting and ending of String should be done with same type of quote
'this is line "
^
SyntaxError: EOL while scanning string literal
>>> 'this will " work " without any isue'
'this will " work " without any isue'
>>> ''' this is a
... string'''
' this is a\nstring'
访问和更新字符串
Python 没有字符的显式类型,它们被视为长度为 1 的字符串。 访问字符串与访问任何其他变量相同。
不能就地更新字符串,因为字符串在 Python 中是不可变的类型。 如果您“更新”字符串的值,则仅使用新字符串重新分配该值。
>>> s1 = "Hello World!"
>>> id(s1)
4318883504
>>> s1 = "Hello Python!"
>>> id(s1)
4318903344
>>> id('Hello World!')
4318883504
特殊字符串运算符
因为字符串是 Python 中的类型,被视为某种列表,所以字符串具有特殊的运算符,我们可以将其用于开发目的。 让我们用示例来看看它们。
在下一个小节中将对切片进行详细说明。
+
符号将字符串连接起来*
符号创建字符串的多个副本[int]
将字符串中给定位置(int
)的字符切片[int1:int2]
它获取给定范围内的字符(int1 – int2
)[::int]
获取给定范围之间的字符,以及字符之间的步长(int
)in
判断字符串中是否存在子字符串not in
判断字符串中是否不存在子字符串
>>> 'Hello' + ' ' + "Python" # the + sign concatenates the strings
'Hello Python'
>>> 'Hello'+'!' * 5 # the * sign creates multiple copies of the string
'Hello!!!!!'
>>> "Python"[2] # slices the character at the given position out of the string
't'
>>> "Hello World!"[0:5] # gets the characters between the given range
'Hello'
>>> 'Hello World!'[::2] # gets the characters between the given range with the amount of steps between the characters
'HloWrd'
>>> 'llo' in "Hello World!" # looks up if a substring is present in the string
True
>>> "hell" not in 'Hello World!' # looks if a substring is not present in the string
True
三引号
我已经提到了我对三重引号的看法:它们是多行字符串而不是注释。
多行字符串表示它们与单行(或普通)字符串的打印方式相同,但保留其显式的新行并将特殊的转义字符转换为其打印版本。
>>> spam = """Spam, Spam, Spam, lovely Spam
... Wonderful Spam, Lovely Spam.
... Spam, Spam, Spam, magnificent Spam,
... \tSuperlative Spam.
... Spam, Spam, Spam, wonderous Spam,\nSurgical Spam, splendiferous Spam.
... Eggs, Bacon, sausage!\rSpam, Spam, Spam, Spaaam!"""
>>> spam
'Spam, Spam, Spam, lovely Spam\nWonderful Spam, Lovely Spam.\nSpam, Spam, Spam, magnificent Spam,\n\tSuperlative Spam.\nSpam, Spam, Spam, wonderous Spam,\nSurgical Spam, splendiferous Spam.\nEggs, Bacon, sausage!\rSpam, Spam, Spam, Spaaam!'
>>> print(spam)
Spam, Spam, Spam, lovely Spam
Wonderful Spam, Lovely Spam.
Spam, Spam, Spam, magnificent Spam,
Superlative Spam.
Spam, Spam, Spam, wonderous Spam,
Surgical Spam, splendiferous Spam.
Spam, Spam, Spam, Spaaam!
如您所见,当不打印字符串时,他的特殊转义符将保持转义-甚至在创建多行字符串时,我们在添加换行符的地方 Python 都显式显示\n
。
字符串格式化
字符串格式化在 Python 中很酷。 格式有两种版本:带%
符号(类似于 C 的printf
方法)和字符串本身的format()
方法。 但是,在大多数情况下,我将同时介绍这两种版本,第二种版本更好,更易于使用。
让我们直接进入细节。
>>> name = 'Bob'
>>> age = 31
>>> 'My name is %s and I am %d years young!'%(name, age)
'My name is Bob and I am 31 years young!'
>>> 'My name is {} and I am {} years young!'.format(name, age)
'My name is Bob and I am 31 years young!'
>>> 'My name is {0} and I am {1} years young!'.format(name, age)
'My name is Bob and I am 31 years young!'
如您所见,两个格式化选项之间没有太大区别。 但是,如果您查看第一个版本,则可以看到变量使用的占位符(%s
和%d
)与第二个版本中的占位符不同。
关键是对于%
格式,您必须告诉参数的类型以根据结果字符串中的格式启用 python。 这也意味着如果要多次打印变量,则必须重复变量。
格式函数与参数索引一起使用,并根据类型确定格式。 因此,您不必明确提及参数的类型。 如果您提供的参数数量与占位符相同,则可以保留参数索引。
>>> 'My name is %s and I am %d years young and I have read %d books!'%(name, age, age)
'My name is Bob and I am 31 years young and I have read 31 books!'
>>> 'My name is {} and I am {} years young and I have read {} books!'.format(name, age)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: tuple index out of range
>>> 'My name is {} and I am {} years young and I have read {} books!'.format(name, age, age)
'My name is Bob and I am 31 years young and I have read 31 books!'
>>> 'My name is {0} and I am {1} years young and I have read {1} books!'.format(name, age)
'My name is Bob and I am 31 years young and I have read 31 books!'
取决于您以后使用哪种格式,但是在本系列文章中,我将坚持使用format
函数。 但是,如果您更喜欢%
,这里有一些字符以及它们在格式字符串中的作用:
%c
:字符%s
:格式化前通过str()
进行字符串转换%i
:带符号的十进制整数%d
:带符号的十进制整数%u
:无符号十进制整数%o
:弱八进制%x
:十六进制整数(小写字母)%X
:十六进制整数(大写字母)%e
:指数符号(小写的“e
”)%E
:指数表示法(大写的“E
”)%f
:浮点实数%g
:%f
和%e
中的较短者%G
:%F
和%E
中的较短者
参考文献
Python 列表
原文: https://javabeginnerstutorial.com/python-tutorial/python-list-2/
我们已经知道 python 列表可以用方括号之间的逗号分隔值编写,并且元素不必来自同一类型。 如果您倾向于忘记此功能,那么后者自然会引起一些混乱和错误。
>>> [1, 'Python', 3.14]
[1, 'Python', 3.14]
访问 python 列表中的元素
访问列表中的值需要使用它们的索引。 索引是分配给元素的数字,也称为元素的位置。 索引以 0 开头。您可以访问列表或一系列条目中的一个条目。
>>> a = [1,2,3,4,5]
>>> a[2]
3
>>> a[1:3]
[2, 3]
如果尝试访问未填充的索引(该索引不包含任何值),则会收到IndexError
。
>>> a = []
>>> a[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
但是,如果您对不包含索引的列表进行切片,就不会出现错误,而只是一个值比您期望的少的列表(或者当然是一个空列表):
>>> a = [1,2,3,4,5]
>>> a[:7]
[1, 2, 3, 4, 5]
>>> a[6:7]
[]
>>> a[8:]
[]
将新元素添加到 python 列表
大多数情况下,您没有可用于构建列表的预定义值。 因此,必须在列表中添加元素。
有很多方法。 一种是使用append
函数,该函数将一个元素附加到列表的末尾。 请注意,这是一个要素。 为什么? 您将很快在示例中看到。
第二种方法是使用切片。 这样,您可以将新元素添加到列表中(或替换当前存在的元素)。 在这种情况下,您必须使用列表作为操作的右侧。 同样,使用此方法时要小心,因为如果从错误的位置进行切片,最终可能会丢失当前内容。
而且,如果要将列表附加到列表的末尾,则必须使用需要将可迭代对象作为参数的extend
函数-否则可以使用连接。 连接与我们在字符串中学到的相同:加号(+
)。
将元素插入列表开头的最佳方法是对[:0]
使用切片。 这会将元素插入列表的开头。
如果必须在给定索引处将元素插入列表中的某处,请使用具有两个参数的列表insert
函数:插入位置的索引和要插入的对象。 如果索引被某个值占用,则新对象将在给定索引处插入此值之前。 如果未占用索引,则将对象附加到列表。
>>> a = []
>>> a[0] = 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list assignment index out of range
>>> a.append(2)
>>> a
[2]
>>> a[1:2] = [1]
>>> a
[2, 1]
>>> a[1:3] = [1]
>>> a
[2, 1]
>>> a[1:2] = [3,4,5,6]
>>> a
[2, 3, 4, 5, 6]
>>> a[:] = [2]
>>> a
[2]
>>> a[0:] = [1]
>>> a
[1]
>>> a.extend([2,3,4,5])
>>> a
[1, 2, 3, 4, 5]
>>> a.append([6,7,8])
>>> a
[1, 2, 3, 4, 5, [6, 7, 8]]
>>> a[:0] = [9]
>>> a
[9, 1, 2, 3, 4, 5, [6, 7, 8]]
>>> a[:0] = [10,11,12]
>>> a
[10, 11, 12, 9, 1, 2, 3, 4, 5, [6, 7, 8]]
>>> a += [13,14,15]
>>> a
[10, 11, 12, 9, 1, 2, 3, 4, 5, [6, 7, 8], 13, 14, 15]
>>> a = []
>>> a.insert(2,1)
>>> a
[1]
>>> a.insert(2,2)
>>> a
[1, 2]
>>> a.insert(2,3)
>>> a
[1, 2, 3]
>>> a.insert(2,4)
>>> a
[1, 2, 4, 3]
如您所见,将列表附加到列表会在末尾插入整个列表,而不是所提供列表的值。 因此,在这种情况下,请小心并使用扩展。
更新 python 列表中的元素
如您所知,您可以通过列表中的元素的索引来更新它。 再次:如果您尝试访问当前未填充的索引,则会收到IndexError
。
>>> a = ["Java", "is", "cool!"]
>>> a
['Java', 'is', 'cool!']
>>> a[0] = 'Python'
>>> a
['Python', 'is', 'cool!']
从列表中删除元素
有时您需要从列表中删除一个元素,因为不再需要它,或者您只是在该位置放置了错误的元素。 还有一些方法可以从列表中删除元素。
首先,您可以使用del
语句。 由于它适用于变量,因此适用于整个列表(因为它们是变量)和列表元素。 请记住,如果您删除列表变量,则在不重新分配的情况下将无法访问该列表。
另一种方法是从列表中弹出一个元素。 pop
函数采用一个可选参数,该参数是要删除的索引。 如果不提供此参数,则最后一个元素将被删除。 del
和pop
之间用法的核心区别在于pop
返回已删除的元素,因此您可以实际使用它。
>>> a = [1, 2, 3, 4, 5, [6, 7, 8]]
>>> a
[1, 2, 3, 4, 5, [6, 7, 8]]
>>> del a[5]
>>> a
[1, 2, 3, 4, 5]
>>> a.pop(3)
4
>>> a.pop()
5
>>> a
[1, 2, 3]
如果要从列表中删除所有元素,可以使用新的空列表重新分配列表,或使用列表的clear
函数。
这两种方法的区别在于,第一种方法确实在内存中留下了一些占用空间,并且列表的 ID 发生了变化,而清除列表则保留了 ID(内存位置)。
>>> a = [1, 2, 3, 4, 5, [6, 7, 8]]
>>> id(a)
4335030344
>>> a = []
>>> id(a)
4334935560
>>> b = [1, 2, 3, 4, 5, [6, 7, 8]]
>>> id(b)
4335073672
>>> b.clear()
>>> id(b)
4335073672
其他基本列表操作
您要在编程中使用的列表上的另外两个基本运算符是确定列表的长度,并使用给定的值初始化给定长度的列表。
第一个问题是众所周知的,第二个问题在进行动态编程或要从一种使用初始化的编程语言中迁移算法时最常见。
>>> a = [1, 2, 3, 4, 5]
>>> len(a) # returns the length of the given list
5
>>> a = ["Hello!"]*4 # creates a list with the element repeated 4 times
>>> a
['Hello!', 'Hello!', 'Hello!', 'Hello!']
So what next? Lets Learn Python 3 Set. 🙂