If you’ll indulge my metaphor for a bit…
You probably have seen one of these before:
Notice that we call it a toaster. We do not call it a “BreadUtil.”
Similarly, utility methods can and should be placed in a class named for specific functionality, not “miscellaneous stuff related to bread.”
Most of the time, your static methods belong on a related class; for instance, Integer.parseInt is a static method of the Integer class, not a member of a theoretical IntegerUtil or NumberUtil class.
In the past, one case for creating a separate utility class was when the primary class of interest was an interface. An example of this is java.util.Collections. However, as of Java 8, this is not an excuse, as interfaces can have static methods and default methods. In fact, Collections.sort(List) has already been migrated to List.sort.
If you have a lot of utility methods and you feel they would clutter the relevant class, it’s fine to put them in a separate class, but not a “BreadUtil” class. It is never acceptable to put the word “util” in a class name (or “utils,” “utilities,” “misc,” “miscellaneous,” “general,” “shared,” “common,” or “framework”). Give the class a meaningful name that describes what the methods are for. If the methods are too diverse to allow for such a class name, you probably need to split them up into multiple classes. (Small classes with only a few methods are perfectly acceptable; many people even consider that good design.)
Going back to the Integer example, if you felt the methods were cluttering the class, you could create new classes like this:
public class IntegerMath {
private IntegerMath() { }
public static int compare(int x, int y) { /* ... */ }
public static int compareUnsigned(int x, int y) { /* ... */ }
public static int divideUnsigned(int dividend, int divisor) { /* ... */ }
public static int min(int a, int b) { /* ... */ }
public static int max(int a, int b) { /* ... */ }
public static int remainderUnsigned(int dividend, int divisor) { /* ... */ }
public static int signum(int i) { /* ... */ }
public static int sum(int a, int b) { /* ... */ }
public static long toUnsignedLong(int i) { /* ... */ }
}
public class IntegerBits {
private IntegerBits() { }
public static int bitCount(int i) { /* ... */ }
public static int highestOneBit(int i) { /* ... */ }
public static int lowestOneBit(int i) { /* ... */ }
public static int numberOfLeadingZeros(int i) { /* ... */ }
public static int numberOfTrailingZeros(int i) { /* ... */ }
public static int reverse(int i) { /* ... */ }
public static int reverseBytes(int i) { /* ... */ }
public static int rotateLeft(int i, int distance) { /* ... */ }
public static int rotateRight(int i, int distance) { /* ... */ }
}
public class IntegerParser {
private IntegerParser() { }
public static int parseInt(String s) { /* ... */ }
public static int parseInt(String s, int radix) { /* ... */ }
public static int parseUnsignedInt(String s) { /* ... */ }
public static int parseUnsignedInt(String s, int radix) { /* ... */ }
}
The last of those is an example of something that might be better without static methods:
public class IntegerParser {
public IntegerParser() { this(10); }
public IntegerParser(int radix) { /* ... */ }
public int parseInt(String s) { /* ... */ }
public int parseUnsignedInt(String s) { /* ... */ }
}