Java 是否可以使一个方法只执行一次?

Java 是否可以使一个方法只执行一次?,java,Java,我有一个for循环和如下结构: for(....) .... .... if(isTrue) ... do something.. .. method to be executed once (doTrick) is declared outside for loop. ....endif endfor public void doTrick() ... ... ..end for循环中的方法是否可能只执行一次?也许break关键字就是您所需要的?运行方法后调用break;很抱歉,你的问题没

我有一个for循环和如下结构:

for(....)
....
....
if(isTrue)
... do something..
.. method to be executed once (doTrick) is declared outside for loop.
....endif
endfor

public void doTrick()
...
...
..end

for循环中的方法是否可能只执行一次?

也许break关键字就是您所需要的?运行方法后调用break;很抱歉,你的问题没有100%清楚你的意思

从sun文档看一看

当然

if(!alreadyExecuted) {
    doTrick();
    alreadyExecuted = true;
}
您可以使用以下技巧避免使用
if()

private Runnable once;
private final static Runnable NOP = new Runnable () {
    public void run () {
        // Do nothing
    }
}

public void method () {
    once = new Runnable () {
        public void run () {
            doTrick();
            once = NOP;
        }
    }

    for (...) {
        ...
        once.run();
        ...
    }
}

另一个过度杀戮的解决方案:

根据您想要执行的操作,可以使用静态初始化块

public class YourKlass{
    public void yourMethod(){

        DoTrick trick; 

        for( int i = 0; condition; i++){
            // ... (1)
            trick = new DoTrick(); // or any kind of accessing DoTrick
            // ... (2)
        }

    } 
}

public class DoTrick{
    static{
        // Whatever should be executed only once 
    }
}
简单解决方案:

或者,您只想在循环之外执行第一部分:

int i = 0;
if( condition ){
    // ... (1)
    // do trick
    // ... (2)
}
for(i = 1; condition; i++){
    // ... (1)
    // ... (2)
}
import java.util.ArrayList;
类移除副本{
公共静态void main(字符串[]args){
ArrayList originalList=新的ArrayList();
原始列表。添加(“foo”);
原始列表。添加(“条”);
原始列表。添加(“bat”);
原始列表。添加(“baz”);
原始列表。添加(“条”);
原始列表。添加(“条”);
String str=“bar”;
ArrayList duplicateList=新的ArrayList();
//将重复项添加到重复项列表
for(字符串currentString:originalList){
if(currentString.startsWith(str)){
duplicateList.add(当前字符串);
}
}
//删除duplicatesList中的重复项
用于(字符串重新投资:重复列表){
原始列表。移除(重新投资);
}
//最后添加重复元素
原始列表添加(str);
for(字符串currEntry:originalList){
系统输出打印项次(currEntry);
}
}
}
我的应用程序示例:

boolean alreadyExecuted = false;
然后:

private void startMyService() {

    if(!alreadyExecuted) {

        final Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
          @Override
          public void run() {
            //Do something after 4 seconds
              context.startService(new Intent(context, myService.class));
              alreadyExecuted = true;
          }
        }, 4000);
       }

在Java 8中,您可以使用如下所述的自动备忘录来有效地执行此操作:

我承认,对于“运行一次”的场景来说,记忆可能被认为是过火了,但它与前面的答案中描述的一些场景相比,是一个相当干净的替代方案

例如:

public void doSomething() { ... }

Map<Boolean, Boolean> cache = new ConcurrentHashMap<>();

public void doSomethingOnce() {
  cache.computeIfAbsent(true, x -> {
    doSomething();
    return true;
  });
}
public void doSomething(){…}
映射缓存=新的ConcurrentHashMap();
公共无效doSomethingOnce(){
cache.computeIfAbsent(true,x->{
doSomething();
返回true;
});
}

您可以使用
AtomicBoolean
确保只在第一次调用任务:

import java.util.concurrent.atomic.AtomicBoolean;

public class Once {
    private final AtomicBoolean done = new AtomicBoolean();
    
    public void run(Runnable task) {
        if (done.get()) return;
        if (done.compareAndSet(false, true)) {
            task.run();
        }
    }
}
用法:

Once once = new Once();
once.run(new Runnable() {
    @Override
    public void run() {
        foo();
    }
});

// or java 8
once.run(() -> foo());

如果使用kotlin,可以执行以下操作:

val由lazy执行一次{
打印(“你好,世界”)
}

我想做一些稍微复杂一点的事情。在多线程环境中,确保只运行一个方法(这由上面的Hover Ruan的答案解决),但还要阻止任何线程,以便在方法完成之前不会返回任何线程

我的解决方案是使用信号量进行阻塞

public class Once implements Runnable {
    private static Semaphore signal = new Semaphore(1,true);
    private static boolean done=false;  

    @Override
    public void run() {
        if(done)
            return;
        signal.acquireUninterruptibly();
        if(done) {
            signal.release();
            return;
        }
        doTrick(); // actually run the task
        done=true;
        signal.release();
        return;
    }

    static int result; // Result of running the task
    static int count;  // number of times its been called

    /**
     * The task we only want to run once
     */
    public void doTrick() {
        ++count;
        Random rnd = new Random();
        for(int i=0;i<10000;++i) {
            result += rnd.nextInt(100);
        }
        try {
            Thread.sleep(1000); // just to ensure all thread start
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(10);
        for(int i=0;i<5;++i) { // multiple instances
            final Once task = new Once();
            for(int j=0;j<5;++j) { // multiple calls of same instance
                executor.submit(() -> {
                    task.run();
                    System.out.println("Result "+Once.result+" count "+Once.count);
                } );
            }
        }
        executor.shutdown();
        try {
            executor.awaitTermination(1, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }           
    }
}
public类一旦实现Runnable{
私有静态信号量信号=新信号量(1,真);
私有静态布尔值done=false;
@凌驾
公开募捐{
如果(完成)
返回;
信号。不间断地获取();
如果(完成){
信号。释放();
返回;
}
doTrick();//实际运行任务
完成=正确;
信号。释放();
返回;
}
static int result;//运行任务的结果
静态int count;//调用它的次数
/**
*我们只想运行一次的任务
*/
公共空间{
++计数;
随机rnd=新随机();

对于(int i=0;i,这里有一个示例方法。只需使用“new Getlineonce();”

class Getlineonce{
静态int[]linesRun=新int[0];
公共Getlineonce(){
布尔lineRan=false;
int line=Thread.currentThread().getStackTrace()[2].getLineNumber();
对于(int i=0;i
或使用共享首选项

    sharedpref = PreferenceManager.getDefaultSharedPreferences(this);


isFirstRun = sharedpref.getBoolean("FIRSTRUN", true);

if (isFirstRun)
{

    // Do your unique code magic here

    SharedPreferences.Editor editor = wmbPreference.edit();
    editor.putBoolean("FIRSTRUN", false);
    editor.commit();
}else{
    //this will repeat everytime 
}

“所有大写字母都意味着它是首字母缩写词,而事实并非如此。@Gandalf它不是首字母缩写词,而是一个名称,所以你不用大写字母书写。我们都知道我的意思,所以正如James所说,没有什么不同。在多线程环境中,你可能希望同步该块。虽然alreadyExecuted可能是一个局部变量though、 这会管用,但有点过分了,不是吗?罗斯迪的答案更简单/更清楚。@Tomas:拓展你的视野:-)@vickirk:这是针对这样一个简单的案例。一旦情况变得更复杂,你需要不止一次“跑一次”对于每个循环,这个技巧可以变得更具可读性,因为您可以以很低的成本从循环中提取代码。对不起,我不相信,如果if中的代码很复杂,那么请将其移动到一个私有方法中,如果逻辑变得更复杂,那么op将需要在此时进行重构,可能会沿着策略/模板方法路线进行重构。这种方式使单元测试变得更加复杂,使用runnable会让开发人员试图找出线程在何处变得复杂。聪明。:)我使用C编写了一个代码库,它通过将指针移到全局
void nil()来做类似的事
method。似乎是同一个想法!中断会立即结束for循环。由于循环包含的代码比方法调用多,我假设中断不是故意的。-1第一个解决方案是多余的,第二个解决方案不在循环中。这与问题有何关系?
import java.util.concurrent.atomic.AtomicBoolean;
注意第二次调用
Once.run(Runnable)
可能会在第一次调用之前返回
class Getlineonce {
    static int[] linesRun = new int[0];

    public Getlineonce() {
        boolean lineRan = false;
        int line = Thread.currentThread().getStackTrace()[2].getLineNumber();
        for(int i = 0; i < linesRun.length; i++) {
            if(line == linesRun[i]) {
                lineRan = true; //Dont print
            }
        }

        if(!lineRan) {
            add(line);
            System.out.println(line + " ran!");
        }

    }

    public void add(int line) {
        int newSize = linesRun.length+1;
        int[] linesRunBuff = new int[newSize];
        for(int i = 0; i < newSize-1; i++) {
            linesRunBuff[i] = linesRun[i];
        }
        linesRunBuff[newSize-1] = line;
        linesRun = linesRunBuff;
    }

}
    sharedpref = PreferenceManager.getDefaultSharedPreferences(this);


isFirstRun = sharedpref.getBoolean("FIRSTRUN", true);

if (isFirstRun)
{

    // Do your unique code magic here

    SharedPreferences.Editor editor = wmbPreference.edit();
    editor.putBoolean("FIRSTRUN", false);
    editor.commit();
}else{
    //this will repeat everytime 
}