Your statement numbers.forEach(System.out::println); is an inlined version of
Consumer<Integer> c=System.out::println;
numbers.forEach(c);
This should better illustrate that the first line creates a reference to the method println of the instance of a PrintWriter found in System.out and doesn’t executed it. It doesn’t print anything—it wouldn’t even know what to print. The object to be printed is a parameter of the created Consumer’s accept method.
The second line numbers.forEach(c) will eventually print every number, because the specified Consumer has been created as a reference System.out::println. The forEach method receives an arbitrary Consumer and will perform it’s encapsulated action for every element by invoking said accept method for each element, passing the element as an argument.
Of course, you can inline the variable c getting your original statement numbers.forEach(System.out::println);, but then, difference between the consumer creation and execution is not so obvious, but also not that relevant for this use case. In general, you could keep the Consumer much longer than for a single use case.