为什么我在尝试编译Java代码时会收到“异常; 必须被捕获或声明为抛出”的消息?

49

考虑:

import java.awt.*;

import javax.swing.*;
import java.awt.event.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
import java.io.*;


public class EncryptURL extends JApplet implements ActionListener {

    Container content;
    JTextField userName = new JTextField();
    JTextField firstName = new JTextField();
    JTextField lastName = new JTextField();
    JTextField email = new JTextField();
    JTextField phone = new JTextField();
    JTextField heartbeatID = new JTextField();
    JTextField regionCode = new JTextField();
    JTextField retRegionCode = new JTextField();
    JTextField encryptedTextField = new JTextField();

    JPanel finishPanel = new JPanel();


    public void init() {

        //setTitle("Book - E Project");
        setSize(800, 600);
        content = getContentPane();
        content.setBackground(Color.yellow);
        content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));

        JButton submit = new JButton("Submit");

        content.add(new JLabel("User Name"));
        content.add(userName);

        content.add(new JLabel("First Name"));
        content.add(firstName);

        content.add(new JLabel("Last Name"));
        content.add(lastName);

        content.add(new JLabel("Email"));
        content.add(email);

        content.add(new JLabel("Phone"));
        content.add(phone);

        content.add(new JLabel("HeartBeatID"));
        content.add(heartbeatID);

        content.add(new JLabel("Region Code"));
        content.add(regionCode);

        content.add(new JLabel("RetRegionCode"));
        content.add(retRegionCode);

        content.add(submit);

        submit.addActionListener(this);
    }


    public void actionPerformed(ActionEvent e) {

        if (e.getActionCommand() == "Submit"){

            String subUserName = userName.getText();
            String subFName = firstName.getText();
            String subLName = lastName.getText();
            String subEmail = email.getText();
            String subPhone = phone.getText();
            String subHeartbeatID = heartbeatID.getText();
            String subRegionCode = regionCode.getText();
            String subRetRegionCode = retRegionCode.getText();

            String concatURL =
                "user=" + subUserName + "&f=" + subFName +
                "&l=" + subLName + "&em=" + subEmail +
                "&p=" + subPhone + "&h=" + subHeartbeatID +
                "&re=" + subRegionCode + "&ret=" + subRetRegionCode;

            concatURL = padString(concatURL, ' ', 16);
            byte[] encrypted = encrypt(concatURL);
            String encryptedString = bytesToHex(encrypted);
            content.removeAll();
            content.add(new JLabel("Concatenated User Input -->" + concatURL));

            content.add(encryptedTextField);
            setContentPane(content);
        }
    }

    public static byte[] encrypt(String toEncrypt) throws Exception{
        try{
            String plaintext = toEncrypt;
            String key = "01234567890abcde";
            String iv = "fedcba9876543210";

            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
            byte[] encrypted = cipher.doFinal(toEncrypt.getBytes());

            return encrypted;
        }
        catch(Exception e){
        }
    }


    public static byte[] decrypt(byte[] toDecrypt) throws Exception{
        String key = "01234567890abcde";
        String iv = "fedcba9876543210";

        SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
        IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
        byte[] decrypted = cipher.doFinal(toDecrypt);

        return decrypted;
    }


    public static String bytesToHex(byte[] data) {
        if (data == null)
        {
            return null;
        }
        else
        {
            int len = data.length;
            String str = "";
            for (int i=0; i<len; i++)
            {
                if ((data[i]&0xFF) < 16)
                    str = str + "0" + java.lang.Integer.toHexString(data[i]&0xFF);
                else
                    str = str + java.lang.Integer.toHexString(data[i]&0xFF);
            }
            return str;
        }
    }


    public static String padString(String source, char paddingChar, int size)
    {
        int padLength = size-source.length() % size;
        for (int i = 0; i < padLength; i++) {
            source += paddingChar;
        }
        return source;
    }
}

我遇到了一个未报告的异常:

java.lang.Exception; must be caught or declared to be thrown
byte[] encrypted = encrypt(concatURL);

同样也包括:

.java:109: missing return statement

我该如何解决这些问题?


每次加密时都应该生成不同的IV。IV不需要保密,只需将其与加密内容一起传递即可。 - erickson
6个回答

39

你所有的问题都源于此。

byte[] encrypted = cipher.doFinal(toEncrypt.getBytes());
return encrypted;

当被封装在try, catch块中时,问题在于如果程序发现异常,你没有返回任何内容。可以按照以下方式编写代码(根据你的程序逻辑进行修改):


``` try { // your code here } catch (Exception e) { // handle the exception return; // add a return statement here } ```
public static byte[] encrypt(String toEncrypt) throws Exception{
    try{
        String plaintext = toEncrypt;
        String key = "01234567890abcde";
        String iv = "fedcba9876543210";

        SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
        IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE,keyspec,ivspec);
        byte[] encrypted = cipher.doFinal(toEncrypt.getBytes());

        return encrypted;
    } catch(Exception e){
        return null;            // Always must return something
    }
}

对于第二个问题,您需要捕获来自encrypt方法调用的异常,像这样(还要根据您的程序逻辑进行修改):

public void actionPerformed(ActionEvent e)
  .
  .
  .
    try {
        byte[] encrypted = encrypt(concatURL);
        String encryptedString = bytesToHex(encrypted);
        content.removeAll();
        content.add(new JLabel("Concatenated User Input -->" + concatURL));

        content.add(encryptedTextField);
    setContentPane(content);
    } catch (Exception exc) {
        // TODO: handle exception
    }
}

你必须从中学到的教训:

  • 带有返回类型的方法在所有可能的情况下始终必须返回该类型的对象。
  • 所有被检查的异常始终必须被处理。

5
最后一个要点不完全正确。RuntimeException 是 Exception 的子类,以及它的子类,都是未检查异常,不需要被捕获或声明抛出。 - pauljm

10

问题出在这个方法中:

  public static byte[] encrypt(String toEncrypt) throws Exception{

这是方法签名,它基本上说明:

  • 方法名称是什么:encrypt
  • 它接收什么参数:一个名为toEncrypt的字符串
  • 它的访问修饰符:public static
  • 以及在调用时是否可能会抛出异常。

在这种情况下,方法签名表示当调用此方法时,“可能”会抛出类型为“Exception”的异常。

    ....
    concatURL = padString(concatURL, ' ', 16);
    byte[] encrypted = encrypt(concatURL); <-- HERE!!!!!
    String encryptedString = bytesToHex(encrypted);
    content.removeAll();
    ......

编译器的意思是:要么你用try/catch语句包围它,要么在方法(使用它的地方)中声明抛出“Exception”。

真正的问题在于“encrypt”方法的定义。没有任何方法应该返回“Exception”,因为它太泛泛而不明确可能会隐藏其他异常类型,最好有一个具体的异常。

试试这个:

public static byte[] encrypt(String toEncrypt) {
    try{
      String plaintext = toEncrypt;
      String key = "01234567890abcde";
      String iv = "fedcba9876543210";

      SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
      IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

      Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
      cipher.init(Cipher.ENCRYPT_MODE,keyspec,ivspec);
      byte[] encrypted = cipher.doFinal(toEncrypt.getBytes());

      return encrypted;
    } catch ( NoSuchAlgorithmException nsae ) { 
        // What can you do if the algorithm doesn't exists??
        // this usually won't happen because you would test 
        // your code before shipping. 
        // So in this case is ok to transform to another kind 
        throw new IllegalStateException( nsae );
    } catch ( NoSuchPaddingException nspe ) { 
       // What can you do when there is no such padding ( whatever that means ) ??
       // I guess not much, in either case you won't be able to encrypt the given string
        throw new IllegalStateException( nsae );
    }
    // line 109 won't say it needs a return anymore.
  }

基本上,在这种情况下,您应确保在系统中可用加密软件包。
Java需要扩展加密软件包,因此将异常声明为“已检查”异常。以便您在它们不存在时处理它们。
在这个小程序中,如果加密软件包不可用,则无法做任何事情,因此您要在“开发”时进行检查。如果当程序运行时抛出这些异常,那么您在“开发”中做错了什么,因此更适合使用RuntimeException子类。
最后一行不再需要返回语句,在第一个版本中,您捕获了异常并没有对其进行任何操作,这是错误的。
try { 
    // risky code ... 
} catch( Exception e ) { 
    // a bomb has just exploited
    // you should NOT ignore it 
} 

// The code continues here, but what should it do???

如果代码要失败,最好是快速失败

以下是一些相关的答案:


6

第一个错误

java.lang.Exception; 必须被捕获或声明为抛出 byte[] encrypted = encrypt(concatURL);

意味着你的 encrypt 方法抛出了一个未被处理或在你调用它的 actionPerformed 方法中声明的异常。请在Java 异常教程中详细阅读。

你有几个选择可以让代码编译通过。

  • 你可以从你的 encrypt 方法中删除 throws Exception,并在 encrypt 内部实际处理异常。
  • 你可以从 encrypt 中删除 try/catch 块,并将 throws Exception 和异常处理块添加到你的 actionPerformed 方法中。

通常最好在最低的级别上处理异常,而不是将其传递到更高的级别。

第二个错误只是意味着你需要在包含第109行代码的方法中添加一个返回语句(在这种情况下也是 encrypt)。虽然该方法中有一个返回语句,但如果抛出异常,则可能无法到达该语句,因此你需要在 catch 块中返回,或者像我之前提到的那样删除 encrypt 中的 try/catch。


1

您需要决定如何处理encrypt方法抛出的异常。

目前,encrypt被声明为throws Exception - 然而,在方法体中,异常被捕获在try/catch块中。我建议您要么:

  • encrypt中删除throws Exception子句,并在内部处理异常(至少考虑编写日志消息);或者
  • encrypt的主体中删除try/catch块,并将对encrypt的调用用try/catch包围起来(即在actionPerformed中)。

关于您提到的编译错误:如果在encrypt的try块中抛出异常,则在catch块完成后不会返回任何内容。您可以通过最初将返回值声明为null来解决这个问题:

public static byte[] encrypt(String toEncrypt) throws Exception{
  byte[] encrypted = null;
  try {
    // ...
    encrypted = ...
  }
  catch(Exception e){
    // ...
  }
  return encrypted;
}

然而,如果您可以纠正更大的问题(异常处理策略),那么这个问题将会得到解决 - 特别是如果您选择我建议的第二个选项。

0

actionPerformed(ActionEvent e) 中,您调用了声明为抛出 Exceptionencrypt()。 然而,actionPerformed 既没有使用 try/catch 捕获此异常(尝试包含对 encrypt() 的调用),也没有声明它自己会抛出 Exception

然而,您的 encrypt 方法却并未真正抛出 Exception。 它会吞下所有异常,甚至不报一句话。(这是一种不好的实践和风格!)

此外,您的 encrypt 方法执行以下操作:

public static byte[] encrypt(String toEncrypt) throws Exception {
  try{
    ....
    return encrypted; // HERE YOU CORRECTLY RETURN A VALUE
  } catch(Exception e) {
  }
  // YOU DO NOT RETURN ANYTHING HERE
}

也就是说,如果你捕获到任何异常,你会默默地将其丢弃,然后从你的encrypt方法底部掉出来,而实际上并没有返回任何东西。这不会编译(如你所见),因为一个声明要返回值的方法必须在每个可能的代码路径上要么返回一个值,要么抛出一个异常。

0
在你的“encrypt”方法中,你应该要么摆脱try/catch并在你调用encrypt(在“actionPerformed”内部)的地方添加try/catch,要么在encrypt中的catch中返回null(这是第二个错误)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接