Both of these have O(N) performance, which is to say they scale linearly with the size of the array.
However, and without consideration of whether this is an optimization or not, if the size of the array is significant relative to the processor cache size(s), then the first one will have fewer cache misses, which will be a considerable factor in performance, since cache usage is a big deal to modern processors.
The latter approach will scan the memory linearly twice. So, if the array is sufficiently large, on the first pass, the beginning of the array will get pulled into the cache, but be purged by subsequent parts of the array.
The second pass will then have to pull the whole array thru the cache once more. If the work of the second pass can overlap with the work of the first, this extra pull thru the cache will be avoided.
For the best separation of concerns, we would do each separately, but that would look like this:
int min = Integer.MAX_VALUE;
for (int x : array) {
if (x < min) min = x;
}
int max = Integer.MIN_VALUE;
for (int x : array) {
if (x > max) max = x;
}
and perhaps each in its own method. (The difference being that we don't initialize min and max together then do min, then max.)
However, if you always need the pair together, you might use your first approach, and I would not even consider that an optimization over the latter approach, given obtaining the min/max pair as a requirement.