1

https://stackoverflow.com/a/572550/1165790

我想在 Java 中使用此功能,因为我正在设计的函数很少被调用(但是当它被调用时,它会启动一个递归链),因此,我不想将变量作为实例字段来浪费内存每次实例化类时。

我也不想创建额外的参数,因为我不想用实现细节给函数的外部调用增加负担。

我尝试了 static 关键字,但 Java 说它是非法修饰符。有没有直接的替代方案?如果没有,推荐什么解决方法?

我希望它具有函数范围,而不是类范围。

4

8 回答 8

12

我希望它具有函数范围,而不是类范围。

那你就不走运了。Java 提供static(类作用域)、实例和局部变量。没有与 C 的函数范围static变量等效的 Java。


如果变量确实需要是静态的,那么您唯一的选择就是使其具有类作用域。这就是你所拥有的。

另一方面,如果这是在某些递归方法调用中使用的工作变量,那么将其设为静态将意味着您的算法不可重入。例如,如果您尝试在多个线程上运行它,它将分崩离析,因为线程都将尝试使用相同的静态...并相互干扰。在我看来,正确的解决方案是使用方法参数传递此状态。(您也可以使用所谓的“线程本地”变量,但它们有一些明显的缺点......如果您担心大约 200 字节的存储开销!)

于 2013-09-28T02:32:23.767 回答
5

你将如何在不“浪费内存”的情况下在调用之间保持一个值?并且消耗的内存可以忽略不计。

如果需要存储状态,存储状态:只需使用静态字段即可。


在多线程应用程序中使用静态变量时要小心:确保同步访问静态字段,以适应从不同线程同时调用的方法。最简单的方法是将synchronized关键字添加到static方法中,并将该方法作为使用该字段的唯一代码。考虑到该方法很少被调用,这种方法是完全可以接受的。

于 2013-09-28T02:13:45.620 回答
2

静态变量是类级别的变量。如果您在方法之外定义它,它将完全按照您的意愿行事。

请参阅文档:

了解实例和类成员

Java中该答案的代码...

public class MyClass {
    static int sa = 10;

    public static void foo() {
        int a = 10;

        a += 5;
        sa += 5;

        System.out.println("a = " + a + " sa = " + sa);
    }

    public static void main(String[] args) {
         for (int i = 0; i < 10; i++) {
             foo();
         }
    } 
}

Output: 
$ java MyClass
a = 15 sa = 15
a = 15 sa = 20
a = 15 sa = 25
a = 15 sa = 30
a = 15 sa = 35
a = 15 sa = 40
a = 15 sa = 45
a = 15 sa = 50
a = 15 sa = 55
a = 15 sa = 60

sa 在内存中只存在一次,类的所有实例都可以访问它。

于 2013-09-28T02:15:18.520 回答
2

我同意波西米亚的观点,记忆不太可能成为问题。另外,重复的问题:如何在 Java 中创建静态局部变量?

为了回应您对向方法添加附加参数并公开实现细节的担忧,想补充一点,有一种方法可以在不公开附加参数的情况下实现这一点。添加一个单独的私有函数,并让公共函数封装递归签名。我已经在函数式语言中多次看到过这种情况,但它在 Java 中当然也是一种选择。

你可以做:

public int getResult(int parameter){
    return recursiveImplementation(parameter, <initialState>)
}

private int recursiveImplementation(int parameter, State state){
    //implement recursive logic
}

尽管这可能不会解决您对内存的担忧,因为我认为 java 编译器不会考虑尾递归优化。

于 2013-09-28T02:19:06.790 回答
2

可能你的问题已经解决了,但这里有更多关于 Java 中静态的细节。可以有静态类、函数或变量。

class myLoader{

static int x;
void foo(){
 // do stuff
}

}

相对

class myLoader{


    static void foo(){

    int x;

    // do stuff
    }

    }

在第一种情况下,它充当类变量。您不必以这种方式“浪费内存”。您可以通过 myLoader.x 访问它。但是,在第二种情况下,方法本身是静态的,因此它本身属于该类。不能在此方法中使用任何非静态成员。单例设计模式将使用 static 关键字仅将类实例化一次。如果您正在使用多线程编程,请确保在同时访问您的静态变量时不要生成竞争条件。

于 2013-09-28T02:35:28.490 回答
1

在递归调用中设置在堆栈上的变量将是函数(帧)本地:

public class foo {
    public void visiblefunc(int a, String b) {
        set up other things;
        return internalFunc(a, b, other things you don't want to expose);          
    }                                                                              

    private void internalFunc(int a, String b, other things you don't want to expose) {
        int x; // a different instance in each call to internalFunc()                                                             
        String bar; // a different instance in each call to internalFunc()
        if(condition) {
            internalFunc(a, b, other things);
        }
    }

}

于 2013-09-28T02:25:26.417 回答
1

有时状态可以通过简单地传递来保存。如果仅在内部需要递归,则委托给具有附加状态参数的私有方法:

public void f() { // public API is clean
    fIntern(0); // delegate to private method
}

private void fIntern(int state) {
    ...
    // here, you can preserve state between
    // recursive calls by passing it as argument
    fIntern(state);
    ...
}
于 2013-09-28T02:41:12.200 回答
0

一个类似函数的小类怎么样?

static final class FunctionClass {
    private int state1;  // whichever state(s) you want.
    public void call() {
        // do_works...
        // modify state
    }

    public int getState1() {
        return state1;
    }
}

// usage:
FunctionClass functionObject = new FunctionClass();
functionObject.call(); // call1
int state1AfterCall1 = functionObject.getState1();
functionObject.call(); // call2
int state1AfterCall2 = functionObject.getState1();
于 2018-07-26T00:27:35.553 回答