1. 项目概述:为什么是Java+Selenium?

如果你是一名Java开发者,或者正在学习Java,并且对自动化测试或者网页数据抓取感兴趣,那么“Java+Selenium”这个组合对你来说,可能是一个效率倍增器。我最初接触这个组合,是因为厌倦了重复的手工点击和表单填写。无论是测试一个Web应用的功能回归,还是需要定时从某个网站上抓取一些公开数据,手动操作不仅耗时,还容易出错。Selenium的出现,就像给浏览器装上了一双可以编程的手,而Java,作为一门成熟、稳定、生态丰富的语言,是驱动这双手的可靠大脑。

简单来说,Selenium是一个用于Web应用程序自动化测试的工具套件。它支持多种浏览器(Chrome、Firefox、Edge等)和多种编程语言(Java、Python、C#等)。我们这里选择Java,是因为它强大的类型系统、丰富的库支持(如TestNG、JUnit用于测试组织,Log4j用于日志记录)以及在企业级应用中的广泛使用,使得构建健壮、可维护的自动化脚本成为可能。这个入门指南,旨在帮你快速搭建环境,理解核心概念,并写出你的第一个自动化脚本,绕过那些我当初踩过的坑。

2. 环境准备与核心依赖配置

万事开头难,环境配置往往是第一个拦路虎。配置不对,后面所有的代码都跑不起来。我会详细列出每一步,并解释为什么这么做,确保你一次成功。

2.1 Java开发环境搭建

Selenium需要Java运行环境。我强烈建议使用JDK 11或JDK 17这两个长期支持(LTS)版本,它们在稳定性和社区支持上都有保障。避免使用过旧(如JDK 8以下)或过新的非LTS版本,以免遇到兼容性问题。

  1. 下载与安装 :前往Oracle官网或Adoptium等开源站点下载合适的JDK安装包。安装过程很简单,一路“下一步”即可。关键点在于记住安装路径,比如 C:\Program Files\Java\jdk-17

  2. 配置环境变量 :这是新手最容易出错的地方。你需要配置两个系统环境变量:

    • JAVA_HOME :变量值就是你的JDK安装路径,例如 C:\Program Files\Java\jdk-17 。这个变量告诉系统Java的“家”在哪里。
    • Path :在Path变量中,添加 %JAVA_HOME%\bin 。这相当于把Java的可执行文件目录(如 java.exe , javac.exe )加入到系统的命令搜索路径中。

    配置完成后,打开命令行(CMD或PowerShell),输入 java -version javac -version 。如果都能正确显示版本号,说明配置成功。

注意 :很多教程只让改Path,但规范地设置 JAVA_HOME 是业界最佳实践,后续像Maven、Gradle等构建工具,以及一些IDE都会依赖这个变量。

2.2 构建工具与依赖管理

对于Java项目,我推荐使用Maven或Gradle来管理依赖,它们能自动下载所需的库(JAR包),解决版本冲突,比手动下载JAR包要优雅得多。这里以Maven为例,因为它更直观。

  1. 安装Maven :从Apache官网下载Maven二进制包,解压到某个目录(如 D:\apache-maven-3.8.6 )。同样,需要配置环境变量:

    • MAVEN_HOME :设置为你的Maven解压路径。
    • Path :添加 %MAVEN_HOME%\bin 。 命令行输入 mvn -v 验证。
  2. 创建Maven项目 :你可以使用IDE(如IntelliJ IDEA或Eclipse)直接创建Maven项目,也可以在命令行使用 mvn archetype:generate 命令生成。IDE会更方便。

  3. 配置 pom.xml :这是Maven项目的核心配置文件。我们需要在其中添加Selenium的依赖。打开项目根目录下的 pom.xml 文件,在 <dependencies> 标签内添加如下内容:

<dependencies>
    <!-- Selenium Java Client -->
    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>4.14.0</version> <!-- 请使用当时最新稳定版 -->
    </dependency>
    <!-- WebDriverManager:自动管理浏览器驱动 -->
    <dependency>
        <groupId>io.github.bonigarcia</groupId>
        <artifactId>webdrivermanager</artifactId>
        <version>5.6.2</version>
    </dependency>
    <!-- 测试框架,用于组织测试用例 -->
    <dependency>
        <groupId>org.testng</groupId>
        <artifactId>testng</artifactId>
        <version>7.8.0</version>
        <scope>test</scope>
    </dependency>
</dependencies>

为什么这么配?

  • selenium-java :这是主依赖,包含了Selenium WebDriver的所有Java客户端API。
  • webdrivermanager :这是一个神器!传统方式需要你手动下载ChromeDriver、GeckoDriver等,并放到系统路径。WebDriverManager会在运行时自动检测你的浏览器版本,并下载匹配的驱动,极大简化了环境配置。这是我强烈推荐必装的库。
  • testng :一个比JUnit功能更丰富的测试框架,支持分组、依赖、参数化等,更适合组织复杂的自动化测试用例。当然,你也可以用JUnit。

保存 pom.xml 后,IDE会自动下载依赖(或手动执行 mvn compile )。看到项目 External Libraries 里出现相关JAR包,就说明依赖配置成功了。

3. 第一个自动化脚本:打开浏览器与基本操作

环境就绪,我们来写第一个脚本。目标是打开Chrome浏览器,访问百度首页,在搜索框输入“Selenium”,然后点击搜索按钮。

3.1 脚本编写与逐行解析

创建一个Java类,比如 FirstSeleniumTest.java

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import io.github.bonigarcia.wdm.WebDriverManager;
import org.testng.annotations.Test;

public class FirstSeleniumTest {

    @Test
    public void testBaiduSearch() {
        // 1. 自动设置ChromeDriver路径
        WebDriverManager.chromedriver().setup();

        // 2. 创建WebDriver实例,打开Chrome浏览器
        WebDriver driver = new ChromeDriver();

        try {
            // 3. 控制浏览器窗口最大化(非必须,但建议)
            driver.manage().window().maximize();

            // 4. 导航到百度首页
            driver.get("https://www.baidu.com");

            // 5. 定位搜索框元素
            // By.id()是最快、最稳定的定位方式之一
            WebElement searchBox = driver.findElement(By.id("kw"));

            // 6. 在搜索框中输入文本“Selenium”
            searchBox.sendKeys("Selenium");

            // 7. 定位搜索按钮并点击
            WebElement searchButton = driver.findElement(By.id("su"));
            searchButton.click();

            // 8. 等待一下,观察结果(生产环境应用显式等待,此处为演示)
            Thread.sleep(3000);

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 9. 关闭浏览器窗口
            driver.quit();
        }
    }
}

逐行解析与核心概念:

  1. WebDriverManager.chromedriver().setup(); :这行代码是WebDriverManager库提供的。它自动完成以下工作:检查系统已安装的Chrome版本 -> 从官方镜像站下载对应版本的 chromedriver -> 将其设置为系统可执行路径。你完全无需手动下载驱动,这是现代Selenium编程的标配。
  2. WebDriver driver = new ChromeDriver(); WebDriver 是一个接口, ChromeDriver 是其具体实现。这行代码实例化了一个Chrome浏览器的“遥控器”。同理,如果你想用Firefox,就是 new FirefoxDriver()
  3. driver.get(url) :命令浏览器导航到指定URL。它与 driver.navigate().to(url) 功能类似,但 get() 更常用,它会等待页面完全加载(取决于 pageLoadStrategy )。
  4. 元素定位 driver.findElement(By.xxx()) 是自动化操作的基石。 By.id(“kw”) 表示通过HTML元素的 id 属性来查找。百度的搜索框HTML代码通常是 <input id=”kw” …> 。这是 最优先推荐 的定位方式,因为ID通常唯一且稳定。
  5. 元素操作 :找到元素( WebElement 对象)后,就可以对其进行操作。 sendKeys() 用于输入文本, click() 用于点击。还有其他如 clear() 清空, submit() 提交表单等。
  6. driver.quit() vs driver.close() :务必在 finally 块中调用 driver.quit() quit() 会关闭所有关联的窗口,并终止WebDriver会话,释放资源。而 close() 只关闭当前标签页。不调用 quit() 可能导致后台浏览器进程残留。

3.2 元素定位策略详解

定位元素是Selenium自动化的核心技能。除了 By.id() ,还有多种策略,各有适用场景。

定位器 示例 (By.xxx) 描述 优先级与建议
ID By.id(“userName”) 通过元素的 id 属性定位。 最高 。ID通常唯一,定位最快、最稳定。首选。
Name By.name(“username”) 通过元素的 name 属性定位。 。常用于表单元素,也比较稳定。
ClassName By.className(“btn-primary”) 通过元素的 class 属性定位。 。注意class可能有多个值(空格分隔),且可能不唯一。
TagName By.tagName(“input”) 通过HTML标签名定位。 。通常一个页面有很多同标签元素,需结合其他方法筛选。
Link Text By.linkText(“登录”) 通过超链接的 完整 文本定位。 中高 。专用于 <a> 链接,文本需完全匹配。
Partial Link Text By.partialLinkText(“录”) 通过超链接的 部分 文本定位。 。当链接文本较长或动态时使用。
CSS Selector By.cssSelector(“input#kw”) 通过CSS选择器定位。功能强大灵活。 很高 。性能好,语法强大,可处理复杂情况(如: input[type=’submit’] )。
XPath By.xpath(“//input[@id=’kw’]”) 通过XML路径语言定位。功能最强大。 。当元素无ID/Name,或需要根据层级、文本等复杂条件定位时使用。但性能略低于CSS。

实操心得

  • 定位策略优先级 :ID > Name > CSS Selector > XPath > 其他。尽量使用前端开发赋予的稳定属性(ID、Name)。
  • 避免绝对XPath :像 /html/body/div[3]/div[2]/form/span/input 这种绝对路径极其脆弱,页面结构微调就会失效。应使用相对XPath或CSS Selector。
  • 使用浏览器开发者工具 :按F12打开,使用“检查”元素功能,可以右键元素直接“Copy” -> “Copy selector” (CSS) 或 “Copy XPath”,作为参考起点,但通常需要人工优化。

4. 等待机制:让脚本更稳定可靠

直接运行上面的脚本,你可能会遇到“元素找不到”的报错。这是因为网页加载需要时间,代码执行速度远快于网络和浏览器渲染。 Thread.sleep() 是一种 固定等待 (死等),它简单但低效(无论元素是否已就绪,都必须等够时间)。Selenium提供了两种更智能的等待方式。

4.1 隐式等待 (Implicit Wait)

隐式等待告诉WebDriver,在查找 任何一个 元素时,如果元素没有立即出现,可以等待一段全局时间。只需设置一次,对整个Driver生命周期有效。

driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)); // 设置隐式等待10秒
WebElement element = driver.findElement(By.id(“someId”)); // 会最多等10秒直到元素出现

原理与局限 findElement 会轮询查找元素,直到找到或超时。它只对 findElement findElements 方法有效。缺点是,它不关心元素是否处于 可交互状态 (如可点击、可见)。如果一个元素存在但被遮挡或禁用,隐式等待无效。

4.2 显式等待 (Explicit Wait)

显式等待是针对 特定条件 的等待,更灵活、更强大。它使用 WebDriverWait 类和 ExpectedConditions 工具类。

// 引入必要的类
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;
import java.time.Duration;

// 创建WebDriverWait对象,设置最大等待时间和轮询间隔
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));

// 用法1:等待元素可点击,然后才进行点击操作
WebElement button = wait.until(ExpectedConditions.elementToBeClickable(By.id(“submitBtn”)));
button.click();

// 用法2:等待元素可见
WebElement message = wait.until(ExpectedConditions.visibilityOfElementLocated(By.className(“alert”)));

// 用法3:等待页面标题包含特定文字
wait.until(ExpectedConditions.titleContains(“订单成功”));

// 用法4:自定义等待条件(Lambda表达式)
wait.until(d -> {
    WebElement el = d.findElement(By.id(“progress”));
    return el.getText().equals(“100%”);
});

为什么显式等待更优?

  1. 条件精准 :它不仅等待元素存在,还可以等待元素可见、可点击、被选中、包含特定文本等,更符合实际交互逻辑。
  2. 局部有效 :只为特定的操作设置等待,不影响其他操作,脚本整体效率更高。
  3. 清晰的超时与异常 :当条件不满足时,会抛出 TimeoutException ,并附带清晰的超时信息,便于调试。

最佳实践建议

  • 混合使用,以显式等待为主 :可以设置一个较短的全局隐式等待(如3-5秒)作为兜底,然后针对关键交互步骤使用显式等待。
  • 避免混合使用导致超时叠加 :注意,当显式等待和隐式等待同时存在时,在显式等待期间,如果隐式等待也在生效,可能会发生不可预期的长时间等待。通常建议 只使用显式等待 ,或者将隐式等待时间设得非常短。
  • 封装等待工具方法 :在实际项目中,可以将常用的等待操作封装成工具方法,提高代码复用性和可读性。

5. 高级操作与框架集成

掌握了基本操作和等待机制,你已经可以完成大部分简单的自动化任务。接下来,我们看看如何处理更复杂的场景,以及如何将Selenium脚本集成到专业的测试框架中。

5.1 处理常见UI组件

下拉框 (Select) 对于标准的HTML <select> 元素,Selenium提供了 Select 类来方便操作。

import org.openqa.selenium.support.ui.Select;

WebElement dropdownElement = driver.findElement(By.id(“country”));
Select dropdown = new Select(dropdownElement);

// 通过可见文本选择
dropdown.selectByVisibleText(“中国”);
// 通过value属性选择
dropdown.selectByValue(“CN”);
// 通过索引选择(从0开始)
dropdown.selectByIndex(1);

// 获取已选中的选项
WebElement selectedOption = dropdown.getFirstSelectedOption();
System.out.println(“选中的是:” + selectedOption.getText());

弹窗与警告框 (Alert) 处理JavaScript弹出的alert、confirm、prompt框。

// 触发一个alert
driver.findElement(By.id(“triggerAlert”)).click();

// 切换到alert
Alert alert = driver.switchTo().alert();

// 获取alert文本
String alertText = alert.getText();
System.out.println(“弹窗信息:” + alertText);

// 接受(点击“确定”)
alert.accept();
// 或取消(点击“取消”)
// alert.dismiss();

// 对于prompt,还可以输入文本
// alert.sendKeys(“输入的内容”);
// alert.accept();

iframe/Frame 如果目标元素嵌套在 <iframe> <frame> 中,必须先切换到对应的frame内才能操作。

// 通过ID或Name切换
driver.switchTo().frame(“frameNameOrId”);
// 通过WebElement切换
WebElement frameElement = driver.findElement(By.tagName(“iframe”));
driver.switchTo().frame(frameElement);
// 通过索引切换(从0开始)
// driver.switchTo().frame(0);

// 在frame内操作元素
driver.findElement(By.id(“innerButton”)).click();

// 操作完成后,切换回主文档
driver.switchTo().defaultContent();
// 或者切换到父级frame
// driver.switchTo().parentFrame();

浏览器窗口与标签页 当点击链接打开新窗口或标签页时,需要切换句柄。

// 获取当前窗口句柄
String originalWindow = driver.getWindowHandle();

// 点击打开新窗口的链接
driver.findElement(By.linkText(“新窗口”)).click();

// 获取所有窗口句柄
Set<String> allWindows = driver.getWindowHandles();

// 切换到新窗口
for (String windowHandle : allWindows) {
    if (!originalWindow.equals(windowHandle)) {
        driver.switchTo().window(windowHandle);
        break;
    }
}

// 在新窗口操作
System.out.println(“新窗口标题:” + driver.getTitle());

// 关闭新窗口,并切换回原窗口
driver.close();
driver.switchTo().window(originalWindow);

5.2 集成TestNG测试框架

使用TestNG可以更好地组织、运行和报告你的Selenium测试用例。

  1. 基本测试类结构 :使用 @Test 注解标记测试方法。 @BeforeMethod @AfterMethod 注解用于在每个测试方法前后执行设置和清理工作(如初始化Driver和退出Driver)。
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import io.github.bonigarcia.wdm.WebDriverManager;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class TestNGSeleniumDemo {
    WebDriver driver;

    @BeforeMethod
    public void setUp() {
        WebDriverManager.chromedriver().setup();
        driver = new ChromeDriver();
        driver.manage().window().maximize();
        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
    }

    @Test
    public void testLoginSuccess() {
        driver.get(“https://example.com/login”);
        // ... 执行登录成功的测试步骤
        // 使用Assert进行断言
        // Assert.assertEquals(driver.getTitle(), “Dashboard”);
    }

    @Test
    public void testLoginWithInvalidPassword() {
        driver.get(“https://example.com/login”);
        // ... 执行密码错误的测试步骤
        // Assert.assertTrue(driver.findElement(By.className(“error”)).isDisplayed());
    }

    @AfterMethod
    public void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }
}
  1. 数据驱动测试 :TestNG的 @DataProvider 注解可以让你用多组数据运行同一个测试方法,非常适合测试不同输入下的场景。
@Test(dataProvider = “loginData”)
public void testLoginWithMultipleUsers(String username, String password, boolean expectedSuccess) {
    driver.get(“https://example.com/login”);
    driver.findElement(By.id(“username”)).sendKeys(username);
    driver.findElement(By.id(“password”)).sendKeys(password);
    driver.findElement(By.id(“loginBtn”)).click();

    if (expectedSuccess) {
        // 断言登录成功
    } else {
        // 断言出现错误提示
    }
}

@DataProvider(name = “loginData”)
public Object[][] provideLoginData() {
    return new Object[][] {
        { “correctUser”, “correctPass”, true },
        { “wrongUser”, “somePass”, false },
        { “correctUser”, “wrongPass”, false },
        { “”, “”, false } // 空数据测试
    };
}
  1. 生成测试报告 :TestNG默认会生成一个HTML格式的测试报告(位于 test-output 目录下的 index.html ),清晰展示了测试通过率、失败原因、执行时间等信息。也可以集成更强大的报告框架,如ExtentReports或Allure。

6. 常见问题排查与实战技巧

即使按照教程一步步来,你也可能会遇到各种问题。这里总结了一些高频问题和我的解决经验。

6.1 高频问题速查表

问题现象 可能原因 解决方案
NoSuchElementException (元素找不到) 1. 元素定位表达式写错了。
2. 页面尚未加载完成,元素还未出现。
3. 元素在 <iframe> <shadow-root> 内。
4. 元素是动态生成的(AJAX)。
1. 用浏览器开发者工具复查定位器。
2. 添加 显式等待 ,等待元素出现/可见。
3. 使用 driver.switchTo().frame(...) 切换到对应frame。
4. 等待动态内容加载完成,或使用更稳定的定位方式(如等特征明显的父元素出现)。
ElementNotInteractableException (元素不可交互) 1. 元素被其他元素遮挡(如弹窗、蒙层)。
2. 元素不可见( display: none visibility: hidden )。
3. 元素处于禁用状态( disabled 属性)。
1. 移除遮挡物或等待其消失。
2. 检查CSS属性,或等待其变为可见。
3. 检查元素状态,或改用其他可交互元素。
InvalidSelectorException (选择器无效) XPath或CSS Selector语法写错了。 将定位表达式粘贴到浏览器开发者工具的Console里,用 $x(“你的XPath”) $$(“你的CSS”) 测试是否有效。
SessionNotCreatedException (会话创建失败) 浏览器驱动版本与已安装的浏览器版本不匹配。 使用WebDriverManager ,它能自动匹配。如果手动管理,需去官网下载对应版本的驱动。
脚本被网站检测为自动化工具 一些网站(如大型电商、社交平台)会检测Selenium的特征(如 cdc_ 变量)。 1. 使用 ChromeOptions 添加 --disable-blink-features=AutomationControlled 参数。
2. 使用 stealth 等第三方库(如puppeteer-extra-plugin-stealth的Java版本)来隐藏特征。
3. 避免高频、规律性操作,模拟人类行为(如随机等待、移动鼠标轨迹)。
StaleElementReferenceException (元素引用失效) 之前找到的元素,因为页面刷新、AJAX更新或DOM重排,已经“过时”了。 重新查找元素 。避免在页面可能刷新的前后,长时间持有同一个WebElement对象。在每次操作前即时查找是更安全的做法。
文件上传 input type=”file” 元素通常被隐藏或样式化,直接 sendKeys 可能无效。 找到真实的 <input type=”file”> 元素,直接对其使用 element.sendKeys(“文件完整路径”) 不要尝试点击“选择文件”按钮

6.2 实战技巧与心得

  1. 使用Page Object Model (POM) 设计模式 :这是中大型自动化项目的 标配 。其核心思想是将页面封装成对象,页面的元素定位和操作细节封装在对应的类中,测试脚本只调用页面对象提供的方法。这样做的好处是:

    • 高可维护性 :页面UI变动时,只需修改对应的Page类,测试脚本几乎不用改。
    • 高可读性 :测试脚本读起来像业务描述( loginPage.login(“user”, “pass”) ),而不是一堆 findElement
    • 低冗余 :公共操作可以抽象到基类中。
    // 示例:登录页面对象
    public class LoginPage {
        private WebDriver driver;
        private By usernameInput = By.id(“username”);
        private By passwordInput = By.id(“password”);
        private By loginButton = By.id(“loginBtn”);
    
        public LoginPage(WebDriver driver) {
            this.driver = driver;
        }
    
        public void enterUsername(String user) {
            driver.findElement(usernameInput).sendKeys(user);
        }
    
        public void enterPassword(String pass) {
            driver.findElement(passwordInput).sendKeys(pass);
        }
    
        public void clickLogin() {
            driver.findElement(loginButton).click();
        }
    
        // 业务方法:组合基本操作
        public void login(String user, String pass) {
            enterUsername(user);
            enterPassword(pass);
            clickLogin();
        }
    }
    
    // 在测试脚本中使用
    @Test
    public void testLogin() {
        LoginPage loginPage = new LoginPage(driver);
        loginPage.login(“testUser”, “testPass”);
        // ... 后续断言
    }
    
  2. 善用浏览器的“无头模式” (Headless Mode) :在服务器或CI/CD管道中运行测试时,没有图形界面。无头模式可以节省资源,运行更快。

    ChromeOptions options = new ChromeOptions();
    options.addArguments(“--headless”); // 启用无头模式
    options.addArguments(“--disable-gpu”); // 在Windows上可能需要
    options.addArguments(“--window-size=1920,1080”); // 设置窗口大小
    WebDriver driver = new ChromeDriver(options);
    
  3. 处理验证码 :这是一个常见难题。完全自动化解码验证码(尤其是复杂图形码)在法律和伦理上都有问题,且技术门槛高。实践中:

    • 测试环境 :让开发关闭验证码或提供万能验证码(如“0000”)。
    • 预生产环境 :使用简单的、可OCR识别的验证码,或使用第三方打码平台API(需评估成本与稳定性)。
    • 记住,自动化不是为了破解系统,而是为了提升测试效率 。对于强验证环节,有时保留部分手工测试是更合理的选择。
  4. 日志与截图 :测试失败时,光有错误堆栈信息不够直观。在 @AfterMethod 中(特别是失败时),自动截屏保存,能极大帮助定位问题。

    @AfterMethod
    public void tearDown(ITestResult result) {
        if (result.getStatus() == ITestResult.FAILURE) {
            // 截屏代码
            File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
            // 将screenshot文件保存到指定路径,可以用时间戳命名
            // 例如:screenshot.renameTo(new File(“./screenshots/” + result.getName() + “_” + System.currentTimeMillis() + “.png”));
        }
        if (driver != null) {
            driver.quit();
        }
    }
    

从环境搭建到第一个脚本,再到元素定位、等待机制、高级操作和框架集成,最后到问题排查和设计模式,这条路径覆盖了Java+Selenium入门到进阶的核心知识点。我个人的体会是,自动化脚本的编写,三分在代码,七分在对被测应用的理解和对不稳定因素的妥善处理上。多写、多调试、多总结,遇到问题善用搜索和查看官方文档,你会逐渐得心应手。最后一个小技巧:把你常用的等待、截图、数据读取等方法封装成自己的工具类,这会让你在后续的项目开发中事半功倍。

更多推荐