Java:复制构造函数不按计划进行

时间:2011-10-24 19:32:56

标签: java constructor copy copy-constructor

我有点问题。我正在制作有限自动机检查器。 给定输入和DFA,它会以接受状态结束。

我的问题是从另一个目标创建一个新的DFA_State。

DFA_State state0, state1, curr_state, init_state, temp; //fine, I think
state0 = new DFA_State();
state1 = new DFA_State();
state0 = new DFA_State("State 0",true, state0, state1); //fine, I think
init_state = new DFA_State(state0);  //fine, I think

但是,这个问题正在惹恼。

temp = new DFA_State(curr_state.nextState(arr1[i]));
*
*
curr_state = new DFA_State(temp);

感谢您的帮助, 戴夫

编辑: 上帝,当我这样做时,我很迟钝,AFAIK,我只是没有直接思考,添加了将值设置为DFA_State对象的方法。

//in DFA_State class
public void set(DFA_State on_0, DFA_State on_1, Boolean is_accepting, String name){
    this.on_0 = on_0;
    this.on_1 = on_1;
    this.is_accepting = is_accepting;
    this.name = name;
}
//in main
DFA_State state0, state1, curr_state; 
state0 = new DFA_State();
state1 = new DFA_State();
state0.set(state0, state1, false, "State 0");
state1.set(state1, state0, true, "State 1");

curr_state = state0;//initial state
//iterate across string input changing curr_state depending on char c
curr_state = getNextState(c);
//at end
if(curr_state.isAccepting()) 
    System.out.println("Valid, " + curr_state.getName() + " is accepting);
else 
    System.out.println("Invalid, " + curr_state.getName() + " is not accepting);

4 个答案:

答案 0 :(得分:0)

在第一行中,您将变量state0state1curr_stateinit_statetemp声明为{{1}类型的变量}。但是,只声明它们,它们尚未初始化。接下来的几行都没关系。第二行创建一个没有任何内容的状态并将其分配给DFA_Statestate0的第三行也是如此。第四行会使用具有实际内容的新state1覆盖您之前的state0作业。第五行创建DFA_State作为DFA_State的副本,并将其分配给state0

假设这个和第二个代码块的第一行之间没有任何内容,现在你会遇到问题。您正在为init_state分配一个新的temp,该DFA_State使用带有依赖于curr_state的参数的复制构造函数。但到那时,该变量尚未初始化。仅仅因为它被声明并不意味着它已经以某种方式已经在内存中构建。当你在上面调用nextState时,根本就没有变量可以解决这个问题。不要期望得到类似指针的东西,这些指针最终会指向你放入curr_state的部分内容。

我只是猜测,但从你的代码风格来看,我会说你有C或C ++的背景知识。研究这些语言与Java之间的差异。如果可能的话,我还建议你让DFA_State类不可变,因为这样更可靠并且可以避免错误。这意味着摆脱no-args构造函数。这是对它的修改(实际上没有编译,可能包含错误):

package foundations.of.computing;

/**
 *
 * @author Kayotic
 */
class DFA_State {
    private final String state;
    private final DFA_State on_0;
    private final DFA_State on_1;
    private final boolean isAccepting;
    //private DFA_State dummy;

    public DFA_State(DFA_State arg) {
        //this(arg.is_accepting(), arg.on0(), arg.on1());
        state = arg.get_name();
        isAccepting = arg.is_accepting();
        on_0 = arg.on0();
        on_1 = arg.on1();
    }

    public DFA_State(String name, Boolean accepting, DFA_State on0, DFA_State on1) {
        state = name;
        isAccepting = accepting;
        on_0 = on0;
        on_1 = on1;
    }

    public String get_name(){
        return state;
    }


    public Boolean is_accepting() {
        return isAccepting;
    }

    public DFA_State on0() {
        return on_0;
    }

    public DFA_State on1() {
        return on_1;
    }

    public DFA_State nextState(char i) {
        if (i == '0') {
            return on0();
        } else if (i == '1') {
            return on1();
        } else {
            System.out.println("Error with input");
            return null;
        }
    }
}

即使你不能使实例变量最终,最好至少将它们设为私有,因为你已经有了获取它们的方法。

答案 1 :(得分:0)

与面向对象相比,DFA的内存表示更好。

您应该使用简单的查找表:

int[] table = new int[vocabularyCount][stateCount];

每个州和每个单词都有一个数字,从0开始。 使用状态转换填充表,如果没有转换,则填充-1。现在你只需要状态和单词的翻译方法。

这是一个通用的DFA算法:

public boolean checkSentence(String s, int[] finishes) {
    // fill table

    int state = 0; // assuming S0 is the start state
    for (int i = 0; i < s.length(); i++) {
        state = table[translate(s.charAt(i))][s];
    }

    for (int i = 0; i < finishes.length; i++) {
        if (finishes[i] == state) {
            return true;
        }
    }

    return false;
}

答案 2 :(得分:0)

该程序编写得很糟糕。在FoundationsOfComputing.java中查看此内容:

    state0 = new DFA_State();
    state1 = new DFA_State();

    state0 = new DFA_State("State 0",true, state0, state1);

你基本上创建了3个状态实例 - 两个未初始化的实例(代码中的前两行) - 它们的所有实例变量都为null。

然后创建第三个实例,指向前两个未初始化的实例,并将其分配给state0变量。请注意,此时,它只是变量的值,而不是您在DFA-State构造函数中传递的值!那么,你现在在state0中所拥有的是一个指向两个未初始化状态的状态。

现在让我们看一下FoundationsOfComputing.java中的代码:

    while (i < arr1.length) {//loops through array
        System.out.println(i + ". scan shows " + arr1[i]);
        temp = new DFA_State(curr_state.nextState(arr1[i]));
        System.out.println("   "+curr_state.get_name()+ " moves onto " + temp.get_name());
        curr_state = new DFA_State(temp);
        i++;
    }

我猜这会引发NullPointerException - 该代码移动到state0的on_0状态 - 这是一个尚未初始化的状态(它的所有实例变量都为null),因此在循环的后续传递中,当它调用时curr_state.nextState(无论如何),它将返回null,并且您尝试将其传递给将导致NPE的复制构造函数。

答案 3 :(得分:0)

好的,所以我们知道这是功课。让我们这样做,而不是告诉你答案让我们试着自己解决它。如果您看到NullPointerException(NPE)。抓住异常的第二行:

java.lang.NullPointerException: null
   at com.blah.blah.SomeObject.someMethod(SomeArgumentType):1234 <<< here
   ....

该1234是包含SomeObject的文件中的行号。如果您转到该行号,您可以准确地看到NPE的生成位置。例如,如果第1234行是:

this.foo = bar.indexOf("caramel");

您可以轻松推断出什么是null。没有线索?那么这个永远不会为空,所以 this.foo 不是问题。如果可能为null,则无法进入该方法,因为 this 指向您当前所在的实例。因此,解除引用变量的唯一其他语句是bar,因此 bar 必须为null。我们来看看你的代码:

temp = new DFA_State(curr_state.nextState(arr1[i]));

假设您发现上面的行正在抛出异常。那么可能有几件事可能是空的。 curr_state可以为null,或者arr1可以为null,在这种情况下,这一行会爆炸。但是,如果arr1 [i]为null或curr_state.nextState()返回null,那么您将看不到指向此行的NPE,但如果有人尝试在该方法参数上调用方法,则会出现构造函数。

希望通过了解异常堆栈跟踪,这将为您提供跟踪应用程序中的问题所需的工具。