try

Apex 中的异常

异常会记录中断正常代码流的错误和其他事件 执行。 语句用于 生成异常,而 、 和 语句用于从异常中正常恢复。throwtrycatchfinally

有许多方法可以处理代码中的错误,包括使用断言(如调用)或返回错误 代码或布尔值,那么为什么要使用异常呢?使用异常的优点是 它们简化了错误处理。异常从被调用的方法冒泡到 caller,根据需要设置多个级别,直到找到处理错误的语句。这种冒泡缓解了 您不必在每个方法中编写错误处理代码。此外,通过使用语句,您可以在一个位置进行恢复 从异常,例如重置变量和删除数据。System.assertcatchfinally

发生异常时会发生什么情况?

发生异常时,代码执行将停止。任何 在异常之前处理的 DML 操作将回滚,并且 未提交到数据库。异常记录在调试日志中。为 Salesforce 发送的未处理异常(代码未捕获的异常) 包含异常信息的电子邮件。最终用户看到错误 Salesforce 用户界面中的消息。

未经处理的异常电子邮件

当发生未经处理的 Apex 异常时,将发送电子邮件 包括 Apex 堆栈跟踪、异常消息以及客户的组织和用户 ID。报表不会返回其他数据。默认情况下,未处理的异常电子邮件会发送给指定的开发人员 在失败类的 LastModifiedBy 字段中,或者 触发。此外,您还可以将电子邮件发送给 Salesforce 的用户 组织和任意电子邮件地址。这些电子邮件收件人还可以 接收流程或流程错误电子邮件。要设置这些电子邮件通知, 从“设置”中,输入“快速查找”框,然后选择“Apex” 例外电子邮件。然后,输入的电子邮件地址将应用于 客户组织中的所有托管软件包。您还可以配置 Apex 使用 Tooling API 对象的异常电子邮件 Apex电子邮件通知。Apex Exception Email

注意

  • 如果同步运行的 Apex 代码中出现重复异常,或者 异步地,后续异常电子邮件将被禁止,并且只有 发送第一封电子邮件。此电子邮件抑制可防止泛滥 开发人员的收件箱中包含有关相同错误的电子邮件。
  • 对于匿名 Apex 遇到的异常,不会发送电子邮件 执行。
  • 每个应用程序每小时限制为 10 封电子邮件 Apex 例外电子邮件 服务器。由于此限制不是基于每个组织,因此电子邮件传递到 特定的组织可能不可靠。

用户界面中未处理的异常

如果最终用户在使用 标准用户界面,则显示错误消息。错误消息包含文本 与此处显示的通知类似。

“新商品”页面中未处理的异常
  • 异常语句
  • 异常处理示例
  • 内置异常和常用方法
  • 捕获不同的异常类型
  • 创建自定义例外

异常语句

Apex 使用异常来记录错误和其他事件 扰乱代码执行的正常流程。 语句可用于生成异常,而 、 和 可以 用于从异常中正常恢复。throwtrycatchfinally

抛出语句

语句允许您发出信号 发生错误。若要引发异常,请使用该语句并为其提供 一个 Exception 对象,用于提供有关特定错误的信息。 例如:

throwthrow

throw exceptionObject;

尝试-捕捉-最后 语句

、 和 语句可用于从引发的异常中正常恢复:

trycatchfinally

  • 该语句标识一个 可能发生异常的代码块。try
  • 该语句标识一个 可以处理特定类型异常的代码块。单个语句可以有零或 更多相关语句。 每个语句必须具有 唯一的异常类型。此外,一旦捕获到特定的异常类型 一个区块,其余区块(如果有的话), 不执行。catchtrycatchcatchcatchcatch
  • 该语句标识一个 保证执行的代码块,并允许您清理您的 法典。单个语句可以 最多有一个关联的语句。块中的代码 无论是否引发异常或类型如何,始终执行 引发的异常。由于该块始终执行,因此请将其用于清理代码,例如 至于释放资源。finallytryfinallyfinallyfinally

语法

、 和 语句的语法 如下所示。trycatchfinally

try {
  // Try block
 code_block
} catch (exceptionType variableName) {
  // Initial catch block.
  // At least the catch block or the finally block must be present. 
 code_block
} catch (Exception e) {
  // Optional additional catch statement for other exception types.
  // Note that the general exception type, 'Exception',
  // must be the last catch block when it is used.
 code_block
} finally {
  // Finally block.
  // At least the catch block or the finally block must be present.
 code_block
}

至少一个块或一个块必须与块一起存在。以下是 try-catch 块。catchfinallytry

try {
 code_block
} catch (exceptionType variableName) {
 code_block
}
// Optional additional catch blocks

以下是 try-finally 块的语法。

try {
 code_block
} finally {
 code_block
}

这是 try-catch-finally 的骨架示例 块。

try {
    // Perform some operation that 
    //   might cause an exception.
} catch(Exception e) {
    // Generic exception handling code here.
} finally {
    // Perform some clean up.
}

无法捕获的异常

某些特殊类型的内置异常无法捕获。这些例外与 闪电平台中的危急情况。这些情况需要堕胎 的代码执行,并且不允许通过异常恢复执行 处理。其中一个异常是限制异常 (),如果调控器限制具有 已超过,例如,当发出的最大 SOQL 查询数 超过。其他示例是断言语句失败时引发的异常 (通过方法)或许可证 异常。System.LimitExceptionSystem.assert

当异常无法捕获时,块以及块(如果有)不会捕获 执行。catchfinally

版本化行为更改

在 API 版本 41.0 及更高版本中,代码中的 unreachable 语句将导致 编译错误。例如,以下代码块生成编译时 API 版本 41.0 及更高版本中的错误。永远无法达到第三种说法 因为前面的语句抛出了一个无条件的 例外。

Boolean x = true; 
throw new NullPointerException();
x = false;

异常处理示例

若要查看操作中的异常,请执行一些导致引发 DML 异常的代码。 在 Developer 中执行以下命令 安慰:

Merchandise__c m = new Merchandise__c();
insert m;

DML 语句 在示例中导致 DmlException,因为我们插入 未设置任何必填字段的商品项。这 是您在调试日志中看到的异常错误。insert

System.DmlException: Insert failed. First exception on row 0; first error: REQUIRED_FIELD_MISSING, Required fields are missing: [Description, Price, Total Inventory]: [Description, Price, Total Inventory]接下来,在开发者控制台中执行此代码段。它基于前面的示例,但 包括一个 try-catch 块。

try {
    Merchandise__c m = new Merchandise__c();
    insert m;
} catch(DmlException e) {
    System.debug('The following exception has occurred: ' + e.getMessage());
}

请注意,开发者控制台中的请求状态现在报告成功。这是因为 代码处理异常。

在异常之后发生的 try 块中的任何语句都是 跳过,不执行。例如,如果添加语句 之后,此语句 不会被执行。执行以下命令:insert m;

try {
    Merchandise__c m = new Merchandise__c();
    insert m;
    // This doesn't execute since insert causes an exception
    System.debug('Statement after insert.');
} catch(DmlException e) {
    System.debug('The following exception has occurred: ' + e.getMessage());
}

在新的调试日志条目中,请注意,您没有看到 的调试消息。这是因为此调试语句发生在异常之后 由插入引起,并且永远不会被执行。继续执行 的代码语句,将语句放在 在 try-catch 块之后。执行这个修改后的代码片段,然后 请注意,调试日志现在具有 的调试消息。Statement after insertStatement after insert

try {
    Merchandise__c m = new Merchandise__c();
    insert m;
} catch(DmlException e) {
    System.debug('The following exception has occurred: ' + e.getMessage());
}
// This will get executed
System.debug('Statement after insert.');

或者,您可以包含其他 try-catch 块。这 代码片段在第二个 try-catch 块中具有该语句。执行它以查看 你得到和以前一样的结果。System.debug

try {
    Merchandise__c m = new Merchandise__c();
    insert m;
} catch(DmlException e) {
    System.debug('The following exception has occurred: ' + e.getMessage());
}

try {
    System.debug('Statement after insert.');
    // Insert other records
}
catch (Exception e) {
    // Handle this exception here
}

无论出现什么异常,finally 块始终执行 被抛出,即使没有抛出异常。让我们来看看吧 在行动中使用。执行以下命令:

// Declare the variable outside the try-catch block
// so that it will be in scope for all blocks.
XmlStreamWriter w = null;
try {
    w = new XmlStreamWriter();
    w.writeStartDocument(null, '1.0');
    w.writeStartElement(null, 'book', null);
    w.writeCharacters('This is my book');
    w.writeEndElement(); 
    w.writeEndDocument();

    // Perform some other operations
    String s;
    // This causes an exception because
    // the string hasn't been assigned a value.
    Integer i = s.length();
} catch(Exception e) {
    System.debug('An exception occurred: ' + e.getMessage());
} finally {
    // This gets executed after the exception is handled
    System.debug('Closing the stream writer in the finally block.');
    // Close the stream writer
    w.close();
}

前面的代码片段创建一个 XML 流编写器,并将 一些 XML 元素。接下来,由于访问 null 字符串变量。这 catch 块处理此异常。然后执行 finally 块。 它写入调试消息并关闭流编写器,从而释放 任何关联的资源。检查调试日志中的调试输出。 异常后,你将看到调试消息 错误。这告诉您,在异常之后执行的 finally 块 被抓住了。sClosing the stream writer in the finally block.

内置异常和常用方法

Apex 提供了许多内置的异常类型,运行时引擎会在以下情况下抛出这些异常类型 在执行过程中遇到错误。你已在前面的 例。下面是一些其他内置异常的示例。有关完整列表 内置异常类型,请参阅 Exception 类和内置异常。DmlExceptionDML 语句的任何问题,例如语句在记录中缺少必填字段。insert此示例使用 DmlException。此示例中的 DML 语句会导致 DmlException,因为 它插入商品项目而不设置任何所需的内容 领域。此异常在块中捕获,异常消息将写入调试日志 使用语句。insertcatchSystem.debug

try {
    Merchandise__c m = new Merchandise__c();
    insert m;
} catch(DmlException e) {
    System.debug('The following exception has occurred: ' + e.getMessage());
}

列表异常列表的任何问题,例如尝试访问超出 边界。此示例创建一个列表并向其添加一个元素。然后,进行尝试 访问两个元素,一个位于索引 0(存在),另一个位于索引 1(其中 导致引发 ListException,因为此索引处不存在任何元素。 此异常在 catch 块中被捕获。catch 块中的语句将以下内容写入 调试日志:。System.debugThe following exception has occurred: List index out of bounds: 1

try {
    List<Integer> li = new List<Integer>();
    li.add(15);
    // This list contains only one element,
    // but we're attempting to access the second element
    // from this zero-based list.
    Integer i1 = li[0]; 
    Integer i2 = li[1]; // Causes a ListException
} catch(ListException le) {
    System.debug('The following exception has occurred: ' + le.getMessage());
}

NullPointerException取消引用变量的任何问题。null此示例创建一个名为 String 的变量,但我们不将其初始化为值,因此它是 null。 在我们的 null 上调用该方法 变量导致 NullPointerException。例外情况在我们的捕获中被发现 块,这是写入调试日志的内容:。scontainsThe following exception has occurred: Attempt to de-reference a null object

try {
    String s;
    Boolean b = s.contains('abc'); // Causes a NullPointerException
} catch(NullPointerException npe) {
    System.debug('The following exception has occurred: ' + npe.getMessage());
}

查询异常SOQL 查询的任何问题,例如分配不返回任何记录的查询 或多条记录到一个单一实例 sObject 变量。此示例中的第二个 SOQL 查询会导致 QueryException。示例 将 Merchandise 对象分配给从查询返回的内容。请注意在查询中使用。这确保了 从数据库中最多返回一个对象,因此我们可以将其分配给 单个对象,而不是列表。但是,在这种情况下,我们没有 商品名为 XYZ,因此不会返回任何内容,并且尝试将 单个对象的返回值会导致 QueryException。例外情况是 捕获到我们的 catch 块中,这就是您将在调试日志中看到的内容:.LIMIT 1The following exception has occurred: List has no rows for assignment to SObject

try {
    // This statement doesn't cause an exception, even though 
    // we don't have a merchandise with name='XYZ'.
    // The list will just be empty.
    List<Merchandise__c> lm = [SELECT Name FROM Merchandise__c WHERE Name = 'XYZ'];
    // lm.size() is 0 
    System.debug(lm.size());
    
    // However, this statement causes a QueryException because 
    // we're assiging the return value to a Merchandise__c object
    // but no Merchandise is returned.
    Merchandise__c m = [SELECT Name FROM Merchandise__c WHERE Name = 'XYZ' LIMIT 1];
} catch(QueryException qe) {
    System.debug('The following exception has occurred: ' + qe.getMessage());    
}

SObjectExceptionsObject 记录的任何问题,例如尝试更改语句中的字段,该字段只能是 期间更改。updateinsert此示例在 try 块中生成 SObjectException,该块在 catch 块。该示例查询发票对帐单,并仅选择其 名称字段。然后,它尝试获取查询的 Description__c 字段 sObject,它不可用,因为它不在 在 SELECT 语句中查询的字段。这会导致 SObjectException。 这个异常在我们的 catch 块中被捕获,这就是你将看到的 在调试日志中:。The following exception has occurred: SObject row was retrieved via SOQL without querying the requested field: Invoice_Statement__c.Description__c

try {
    Invoice_Statement__c inv = new Invoice_Statement__c(
        Description__c='New Invoice');
    insert inv;

    // Query the invoice we just inserted
    Invoice_Statement__c v = [SELECT Name FROM Invoice_Statement__c WHERE Id = :inv.Id];
    // Causes an SObjectException because we didn't retrieve
    // the Description__c field.
    String s = v.Description__c;
} catch(SObjectException se) {
    System.debug('The following exception has occurred: ' + se.getMessage());
}

常见异常方法

您可以使用常见的异常方法来获取有关异常的详细信息,例如 作为异常错误消息或堆栈跟踪。前面的示例调用该方法,该方法返回 与异常关联的错误消息。还有其他异常方法 也可用。以下是一些有用方法的说明:

getMessage

  • getCause:返回 exception 作为异常对象。
  • getLineNumber:返回该行 引发异常的编号。
  • getMessage:返回错误 为用户显示的消息。
  • getStackTraceString:返回 以字符串形式引发异常的堆栈跟踪。
  • getTypeName:返回 异常,例如 DmlException、ListException、MathException 等 上。

若要了解一些常见方法返回的内容,请尝试运行此示例。

try {
    Merchandise__c m = [SELECT Name FROM Merchandise__c LIMIT 1];
    // Causes an SObjectException because we didn't retrieve
    // the Total_Inventory__c field.
    Double inventory = m.Total_Inventory__c;
} catch(Exception e) {
    System.debug('Exception type caught: ' + e.getTypeName());    
    System.debug('Message: ' + e.getMessage());    
    System.debug('Cause: ' + e.getCause());    // returns null
    System.debug('Line number: ' + e.getLineNumber());    
    System.debug('Stack trace: ' + e.getStackTraceString());    
}

所有语句的输出 如下所示:System.debug

17:38:04:149 USER_DEBUG [7]|DEBUG|Exception type caught: System.SObjectException

17:38:04:149 USER_DEBUG [8]|DEBUG|Message: SObject row was retrieved via SOQL without querying the requested field: Merchandise__c.Total_Inventory__c

17:38:04:150 USER_DEBUG [9]|DEBUG|Cause: null

17:38:04:150 USER_DEBUG [10]|DEBUG|Line number: 5

17:38:04:150 USER_DEBUG [11]|DEBUG|Stack trace: AnonymousBlock: line 5, column 1

catch 语句参数类型是泛型 Exception 类型。它抓住了更多 特定的 SObjectException。您可以通过检查退货来验证是否如此 的值 输出。输出还包含 SObjectException 的其他属性,如 错误消息、发生异常的行号和堆栈跟踪。 您可能想知道为什么返回 null。这是因为在我们的样本中,之前没有异常(内部 exception),导致此异常。在 Create Custom Exceptions 中,您将看到一个示例,其中 的返回值是实际值 例外。e.getTypeName()getCausegetCause

更多异常方法

某些异常类型(如 DmlException)具有特定的异常方法,这些方法 仅适用于它们,不适用于其他异常类型:

  • getDmlFieldNames(Index of the failed record):返回导致错误的字段的名称 指定的失败记录。
  • getDmlId(Index of the failed record): 返回导致指定错误的失败记录的 ID 失败的记录。
  • getDmlMessage(Index of the failed record):返回指定失败的错误消息 记录。
  • getNumDml:返回失败的次数 记录。

此代码片段利用 DmlException 方法获取有关 插入 Merchandise 对象列表时返回的异常。项目清单 要插入包含三个项目,其中最后两个不是必需的 字段和原因 异常。

Merchandise__c m1 = new Merchandise__c(
    Name='Coffeemaker',
    Description__c='Kitchenware',
    Price__c=25,
    Total_Inventory__c=1000);
// Missing the Price and Total_Inventory fields
Merchandise__c m2 = new Merchandise__c(
    Name='Coffeemaker B',
    Description__c='Kitchenware');
// Missing all required fields
Merchandise__c m3 = new Merchandise__c();
Merchandise__c[] mList = new List<Merchandise__c>();
mList.add(m1);
mList.add(m2);
mList.add(m3);

try {
    insert mList;
} catch (DmlException de) {
    Integer numErrors = de.getNumDml();
    System.debug('getNumDml=' + numErrors);
    for(Integer i=0;i<numErrors;i++) {
        System.debug('getDmlFieldNames=' + de.getDmlFieldNames(i));
        System.debug('getDmlMessage=' + de.getDmlMessage(i));  
    }
}

请注意,上面的示例在尝试中没有包含所有初始代码 块。只有可能生成异常的代码部分才会被包装 在块内,在这种情况下,该语句可能会在 如果输入数据无效。操作导致的异常由其后面的块捕获。执行后 此示例中,你将看到类似于以下内容的语句输出:tryinsertinsertcatchSystem.debug

14:01:24:939 USER_DEBUG [20]|DEBUG|getNumDml=2

14:01:24:941 USER_DEBUG [23]|DEBUG|getDmlFieldNames=(Price, Total Inventory)

14:01:24:941 USER_DEBUG [24]|DEBUG|getDmlMessage=Required fields are missing: [Price, Total Inventory]

14:01:24:942 USER_DEBUG [23]|DEBUG|getDmlFieldNames=(Description, Price, Total Inventory)

14:01:24:942 USER_DEBUG [24]|DEBUG|getDmlMessage=Required fields are missing: [Description, Price, Total Inventory]

DML 失败数正确报告为 2,因为我们列表中有两个项目 插入失败。此外,导致失败的字段名称和错误消息 对于每个失败的记录,将写入输出。

捕获不同的异常类型

在前面的示例中,我们使用了 catch 块。我们也可以捕获通用异常 键入所有示例,这将捕获所有异常类型。例如 尝试运行此示例,该示例会引发 SObjectException 并具有 catch 语句,参数类型为 Exception。The SObjectException 被 catch 块卡住。

try {
    Merchandise__c m = [SELECT Name FROM Merchandise__c LIMIT 1];
    // Causes an SObjectException because we didn't retrieve
    // the Total_Inventory__c field.
    Double inventory = m.Total_Inventory__c;
} catch(Exception e) {
    System.debug('The following exception has occurred: ' + e.getMessage());    
}

或者,您可以有多个 catch 块 – 一个 catch 块,以及捕获的最终 catch 块 泛型 Exception 类型。请看这个例子。请注意,它有 三个捕获块。

try {
    Merchandise__c m = [SELECT Name FROM Merchandise__c LIMIT 1];
    // Causes an SObjectException because we didn't retrieve
    // the Total_Inventory__c field.
    Double inventory = m.Total_Inventory__c;
} catch(DmlException e) {
    System.debug('DmlException caught: ' + e.getMessage());    
} catch(SObjectException e) {
    System.debug('SObjectException caught: ' + e.getMessage());    
} catch(Exception e) {
    System.debug('Exception caught: ' + e.getMessage());    
}

请记住,只执行一个 catch 块,其余的 catch 块 那些被绕过。此示例与上一个示例类似,但 它有更多的捕获块。运行此代码段时,一个 SObjectException 在以下行上抛出:。每个捕获块 按指定的顺序进行检查,以查找抛出的匹配项 exception 和 catch 块参数中指定的异常类型:

Double inventory = m.Total_Inventory__c;

  1. 第一个 catch 块参数的类型为 DmlException,其类型为 与引发的异常 (SObjectException) 不匹配。
  2. 第二个 catch 块参数的类型为 SObjectException,其 与我们的异常匹配,因此此块被执行并执行以下 消息将写入调试日志:。SObjectException caught: SObject row was retrieved via SOQL without querying the requested field: Merchandise__c.Total_Inventory__c
  3. 最后一个 catch 块被忽略,因为一个 catch 块已经 执行。

最后一个 catch 块很方便,因为它可以捕获任何异常 类型,从而捕获上一个未捕获的任何异常 catch 块。假设我们修改了上面的代码以导致 NullPointerException 要抛出,此异常将在最后一个 catch 块中捕获。 执行此修改后的示例。你将看到以下调试You’ll see the following debug 消息:。Exception caught: Attempt to de-reference a null object

try {
    String s;
    Boolean b = s.contains('abc'); // Causes a NullPointerException
} catch(DmlException e) {
    System.debug('DmlException caught: ' + e.getMessage());    
} catch(SObjectException e) {
    System.debug('SObjectException caught: ' + e.getMessage());    
} catch(Exception e) {
    System.debug('Exception caught: ' + e.getMessage());    
}

创建自定义例外

您不能抛出内置的 Apex 异常。你只能抓住他们。但是有定制 异常,您可以在方法中抛出和捕获它们。自定义异常使您能够 指定详细的错误消息,并在捕获中具有更多自定义错误处理 块。

异常可以是顶级类,也就是说,它们可以具有成员变量、方法和 构造函数,它们可以实现接口,等等。

若要创建自定义异常类,请扩展内置类,并确保类名以单词结尾,例如“MyException”或 “PurchaseException”。所有异常类都扩展了系统定义的基类,因此继承了所有公共类 异常方法。ExceptionExceptionException此示例定义了一个名为 的自定义异常。

MyException

public class MyException extends Exception {}

与 Java 类一样,用户定义的异常类型可以形成继承树,并捕获 块可以捕获此继承树中的任何对象。例如:

public class ExceptionExample {
    public virtual class BaseException extends Exception {}
    public class OtherException extends BaseException {}

    public static void testExtendedException() {
        try {
            Integer i=0;
            // Your code here
            if (i < 5) throw new OtherException('This is bad');
        } catch (BaseException e) {  
            // This catches the OtherException
            System.debug(e.getMessage());
        }  
    }
}

您可以通过以下几种方式创建异常对象,然后可以抛出这些对象。您可以构造异常:

  • 没有 参数:new MyException();
  • 使用指定错误的单个 String 参数 消息:new MyException('This is bad');
  • 使用单个 Exception 参数,该参数指定原因并显示在 任何堆栈 跟踪:new MyException(e);
  • 同时显示 String 错误消息和链式异常原因 任何堆栈 跟踪:new MyException('This is bad', e);

重新引发异常和内部异常

在 catch 块中捕获异常后,您可以选择重新抛出 捕获异常变量。如果方法被另一个方法调用,这将非常有用 并且您希望将异常的处理委托给调用方方法。您可以 将捕获的异常作为内部异常重新抛出,并具有 main 方法捕获自定义异常类型。

下面的示例演示如何将异常重新引发为内部异常。这 example 定义了两个自定义异常和 ,并生成一个堆栈跟踪,其中包含有关两者的信息。My1ExceptionMy2Exception

// Define two custom exceptions
public class My1Exception extends Exception {} 
public class My2Exception extends Exception {} 

try { 
    // Throw first exception
    throw new My1Exception('First exception'); 
} catch (My1Exception e) { 
    // Throw second exception with the first 
    // exception variable as the inner exception
    throw new My2Exception('Thrown with inner exception', e);
}

这是运行上述代码后生成的堆栈跟踪的样子:

15:52:21:073 EXCEPTION_THROWN [7]|My1Exception: First exception

15:52:21:077 EXCEPTION_THROWN [11]|My2Exception: Throw with inner exception

15:52:21:000 FATAL_ERROR AnonymousBlock: line 11, column 1

15:52:21:000 FATAL_ERROR Caused by

15:52:21:000 FATAL_ERROR AnonymousBlock: line 7, column 1

下一节中的示例演示如何使用 异常。getCause

内部异常示例

现在,您已经了解了如何创建自定义异常类以及如何构造 您的异常对象,让我们创建并运行一个示例来演示 自定义异常的有用性。

  1. 在开发者控制台中,创建一个名为 内容。MerchandiseExceptionpublic class MerchandiseException extends Exception { }你会 在创建的第二个类中使用此异常类。大括号 最后将异常类的主体括起来,我们将其留空,因为 我们得到了一些免费代码——我们的类继承了所有的构造函数和公共 异常方法,例如 来自内置类。getMessageException
  2. 接下来,创建名为 .MerchandiseUtilitypublic class MerchandiseUtility { public static void mainProcessing() { try { insertMerchandise(); } catch(MerchandiseException me) { System.debug('Message: ' + me.getMessage()); System.debug('Cause: ' + me.getCause()); System.debug('Line number: ' + me.getLineNumber()); System.debug('Stack trace: ' + me.getStackTraceString()); } } public static void insertMerchandise() { try { // Insert merchandise without required fields Merchandise__c m = new Merchandise__c(); insert m; } catch(DmlException e) { // Something happened that prevents the insertion // of Employee custom objects, so throw a more // specific exception. throw new MerchandiseException( 'Merchandise item could not be inserted.', e); } } }这 class 包含调用 . 后者通过插入不需要的商品来导致异常 领域。catch 块捕获此异常并抛出一个新异常,即 您之前创建的自定义 MerchandiseException。请注意,我们调用了 接受两个参数的异常的构造函数:错误消息和 原始异常对象。您可能想知道为什么我们要传递原始版本 例外?因为它是有用的信息 – 当 MerchandiseException 在第一种方法中捕获,原始异常(称为内部 exception) 确实是导致此异常的原因,因为它发生在 MerchandiseException。mainProcessinginsertMerchandisemainProcessing
  3. 现在让我们看看所有这些的实际效果,以便更好地理解。执行 以后:MerchandiseUtility.mainProcessing();
  4. 检查调试日志输出。您应该看到类似于 以后:18:12:34:928 USER_DEBUG [6]|DEBUG|Message: Merchandise item could not be inserted.18:12:34:929 USER_DEBUG [7]|DEBUG|Cause: System.DmlException: Insert failed. First exception on row 0; first error: REQUIRED_FIELD_MISSING, Required fields are missing: [Description, Price, Total Inventory]: [Description, Price, Total Inventory]18:12:34:929 USER_DEBUG [8]|DEBUG|Line number: 2218:12:34:930 USER_DEBUG [9]|DEBUG|Stack trace: Class.EmployeeUtilityClass.insertMerchandise: line 22, column 1一些感兴趣的项目:
    • MerchandiseException 的原因是 DmlException。您可以看到 DmlException 消息还指出必填字段是 失踪。
    • 堆栈跟踪是第 22 行,这是第二次出现异常 扔。它对应于 throw 语句 MerchandiseException。throw new MerchandiseException('Merchandise item could not be inserted.', e);