I think in all cases, recursion can be written in an iterative loop. Sometimes it's actually easier and/or faster (in running time and/or programming time) to do so. But other times it'll require a lot of work to unravel the recursion. In this case, switching to a loop is quite simple.
To help you understand this case, I would suggest doing the following... execute the program code by hand. There's only 4 numbers being added so it shouldn't be that hard. Draw a 'recursion tree': the root node will be the very first call to AddNumbers. Nodes of the tree represent function calls. In each node, you should list out the values taken by all the local variables so you can determine what is returned. Connect nodes with a branch where the function call occurs (i.e., addNumbers with 4 numbers in the list calls addNumbers with 3 numbers in the list).
Typically, you use recursion when solving a problem of size N involves solving smaller problems of "identical" structure. Any time you can apply induction, you can apply recursion--so you need 1) "identical" structure across different problem sizes; and 2) a set of base case(s). For example, in this case, the goal is to add N numbers together. Say I tell you I have an algorithm that can add N-1 numbers together & report the value in SUM. Can you use my algorithm to add N numbers together? Of course! return SUM + Nth number. What's the base case? Well if there are 0 numbers, the sum is 0.
The fibonnaci case works the same way. Fibonacci numbers are defined as f(n) = f(n-1) + f(n-2). The base case are f(0) = 0, f(1) = 1. So if I give you a function that can compute f(n-1) and f(n-2), you should be able to compute f(n) quite easily. Try drawing the recursion tree for this example (easily google-able if you get stuck). Looking at the recursion tree should help you figure out why recursion is such an expensive way of solving this problem.
Your question about why "runningTotal" isn't reset to 0 each time is a different matter. Each time you call a function, you can imagine that function setting up its own "scope". If it declares LOCAL variables, the value of those variables are local to the owning function. When the function's return statement executes, all these local values are lost. These local variables can be communicated to other parts of the program by copying their value or sending that data along or by passing a reference (a pointer to a location in memory). The latter method can be dangerous if the function returns and somewhere else, you still have reference(s) to its local variable(s).
So the key here is... (btw the text below gives away how to draw the recursion tree if you haven't thought that through, so try that first.)
return addNumbers(numberList) + runningTotal;
Say I call addNumbers with numberList = {2,3} (just 2 numbers in it). On the first call, addNumbers() sets its local copy of runningTotal = 3, removes the last entry of numberList, and calls:
return addNumbers({2}) + 3; (*)
On this second call, addNumbers() sets its local copy of runningTotal = 2, removes the last entry of numberList, and calls:
return addNumbers({empty}) + 2; (**)
On the third call to addNumbers will see that the list is empty, and return 0. Thus, the statement labeled (**) above resolves to:
return 0 + 2; (=2)
and the return of the call above it (labeled (*)) resolves to:
return 2 + 3; (=5)
and we're done.
Each function call gets its own, separate copy of runningTotal that has no bearing on the other calls' runningTotal value.