why can a variable of final array type be modified?
It can't, even though it looks like it can.
The declaration is below, which defines "prefix" as an array:
final String[] prefix
First, edit the code to include two println() calls right after the declaration, like this:
final String[] prefix = {"prefix_"};
System.out.println("prefix: " + prefix);
System.out.println(prefix[0]);
And at the end, add a second println() next to the one you already had, like this:
System.out.println("prefix: " + prefix);
System.out.println(prefix[0]);
If you run that code, you'll see that the object hashCode when printing prefix will be the same object each time. The thing that changes then is not what "prefix" references – that remains the same, it's the same array as before. Instead, what you're doing is changing something inside the array, which is different from the array itself.
Here are the relevant lines from a local run showing that the object reference remains the same, but the value for "prefix[0]" changes:
prefix: [Ljava.lang.String;@5ca881b5
prefix_
prefix: [Ljava.lang.String;@5ca881b5
NoPrefix_
If we try to assign an entirely new array to "prefix", then the compiler will show an error – it knows "prefix" was defined as final.
prefix = new String[]{"new"};
cannot assign a value to final variable prefix
If you're looking for a way to prevent changes to the data, and if it's possible to use a List (instead of an array), you could use
Collections.unmodifiableList():
Returns an unmodifiable view of the specified list. Query operations on the returned list "read through" to the specified list, and attempts to modify the returned list, whether direct or via its iterator, result in an UnsupportedOperationException.
prefixarray reference isn't changed. Just the contents of the array. Not that you should write code like this.List<String> flagList = list.stream().map(param -> String.format("%s_%s_%s", "NoPrefix", param, suffix)).collect(Collectors.toList());