When you've been writing Java for a while, it's easy to get caught up in big concepts like multithreading, Spring Boot, microservices, and more. But sometimes, the real charm lies in the tiny details. You know, those "wait… really?" moments. Here are 15 subtle but fascinating things about Java that might just surprise you, even if you're not a beginner.
1. Every Class Is a Descendant of Object:
Yup, every single class you write in Java, whether you extend another class or not, automatically extends java.lang.Object. It's like the grandparent of everything. This is why your class can use methods like .toString()
, .equals()
, and .hashCode()
even if you never explicitly added them.
2. Throwable Is the Topmost Boss of All Exceptions:
Most folks know about Exception and maybe RuntimeException
. But there is something above them called Throwable. Both Error (like OutOfMemoryError
) and Exception come from Throwable. If you use catch (Exception e), it only catches exceptions. Big problems like StackOverflowError
are errors, so they won't be caught.
3. You Can Return From a finally Block (But You Shouldn't):
In Java, you can write a return inside a finally block. But be careful because it can hide errors or change the return value from earlier. It is allowed, but it is like hiding a problem instead of fixing it.
public int testFinally() {
try {
return 1;
} finally {
return 2; // This wins, surprisingly!
}
}
4. A static Block Runs Before Anything Else:
If a class has a static block, it runs before the main method or any static method is called. You can think of it like a backstage crew getting things ready before the show begins.
static {
System.out.println("Hello from static block!");
}
5. break and continue Can Use Labels:
Yes, Java has labels, and they're not just for switch-case! You can use them with loops to break out of a specific one. It's similar to goto, but with more control and better rules.
outer:
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
if (j == 2) break outer;
}
}
6. Method Overloading Doesn't Care About Return Type:
Most people know this: you can't overload a method just by changing its return type. The compiler needs a different method signature, so the parameter types must be different.
// This won't compile:
int add(int x) {}
double add(int x) {} // Error!
7. Strings Are Immutable and Interned:
Everyone knows Strings are immutable. But Java also keeps identical string values in a pool so they do not use extra memory. That means two variables holding the same string literal will actually refer to the same object in memory.
String a = "Java";
String b = "Java";
System.out.println(a == b); // true!
8. finalize Method Is Not Reliable and Is Deprecated:
There was a time when you could add a finalize()
method to clean up before an object is destroyed. But it's not guaranteed to run, and Java actually deprecated it because it caused more problems than it fixed. It's better to use try-with-resources
or proper cleanup methods instead.
9. You Can Use Underscores in Large Numbers:
If large numbers in your code make your eyes hurt, Java has you covered. You can use underscores to make them easier to read.
int price = 1_00_000;
long cardNumber = 1234_5678_9012_3456L;
10. The main Method Is Just a Normal Method:
That famous main(String[] args)
method? It's not magical. It's just a regular static method that the Java Virtual Machine looks for when starting your program. You can even call it from somewhere else like any other method.
public static void main(String[] args) {
System.out.println("Main method called");
main(new String[]{"Called again"});
}
11. You Can Have Multiple main Methods:
Java lets you have more than one main()
method as long as they have different parameters. The JVM will only call the one with String[] args
, but you can overload it for testing or other purposes.
public static void main(String[] args) {
}
public static void main() {
System.out.println("Overloaded main method");
}
12. A Constructor Can Call Another Constructor:
With the this()
keyword, one constructor can call another constructor in the same class. This helps avoid repeating code.
public class Person {
Person() {
this("Guest");
}
Person(String name) {
System.out.println("Name: " + name);
}
}
13. You Cannot Create an Instance of an Interface:
You can't directly create an instance of an interface, but classes can implement it, or you can use it anonymously like this:
Runnable r = new Runnable() {
public void run() {
System.out.println("Running");
}
};
14. Package Names Should Follow Reverse Domain Convention:
It is common to use your company or project domain name in reverse when naming packages, like com.example.project
. This helps prevent name clashes between libraries.
If your company's website is www.mycompany.com, your Java package structure might look like this:
package com.mycompany.project;
public class MyClass {
public void display() {
System.out.println("Hello from MyClass");
}
}
15. Java Has Varargs (Variable-Length Arguments):
Java lets you pass a variable number of arguments to a method using the varargs feature. This makes your code cleaner and more flexible when you're unsure how many arguments will be passed.
public class VarargsExample {
public static void printNumbers(int... numbers) {
for (int num : numbers) {
System.out.println(num);
}
}
public static void main(String[] args) {
printNumbers(1, 2, 3, 4, 5); // You can pass any number of arguments
}
}
In the example above, the printNumbers()
method can accept any number of integers. The int... numbers parameter is treated as an array, so you can easily pass a list of numbers without worrying about their count.
Final Thoughts:
- Java is full of small surprises that do not always get noticed. These little details can actually make you a better developer. They are not just trivia; they help you understand the language on a deeper level and write smarter code.
- So next time you open your editor, remember that Java is not just about big frameworks and complex patterns. Sometimes, it is the small things that have the biggest impact.
Top comments (0)