So, every Java text book talks about how flexible Java is since it can load classes at run time. Just cobble together a string and give it to Class.forName()
, and
I'm pretty sure plugin loading in Java relies much on that.
The application checks for named functions and executes them
Eclipse actually uses that for plugins
The key idea is to execute code that wasn't planned at developpement time.
The ClassLoader is also used for non-class resources. Configuration files come to mind. Since there is a well-defined search order, it is easy to drop-in your own "log4j.xml" or "hibernate.properties", and the application will find and use it.
i think JUnit may also used a lot of reflection features to make the test framework generic.
"PLUGIN" and that is the big word.
Basically, you can load a class that you do not know when or does not exist when you write and compile your program.
For example, if you want a program to do spell check, you can write an interface SpellChecker
then load a class from a configuration file that implement the SpellChecker
interface. After that, you can write any SpellChecker and set in the configuration file the actual file name. This way, your program does not need to know what class will do the spell checking.
DB driver, Eclipse's plugin, Script language, Cryptography methods are all done this way as the original writer does not know (and in some case, has no idea) what class will actually be used.
Hope this helps.
The JDBC API is an excellent example for this. This way you can configure the JDBC driver externally in for example a properties file:
driver = com.dbvendor.jdbc.Driver url = jdbc:dbvendor://localhost/dbname username = stackoverflow password = youneverguess
..which you can use as:
Properties properties = new Properties();
properties.load(Thread.currentThread().getResourceAsStream("jdbc.properties"));
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
String username = properties.getProperty("username");
String password = properties.getProperty("password");
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, username, password);
Every JDBC driver implementation basically registers itself in the DriverManager
inside a static
initializer block. It's namely the one which get executed during Class#forName()
.
package com.dbvendor.jdbc;
public class Driver implements java.sql.Driver {
static {
java.sql.DriverManager.registerDriver(new Driver());
}
private Driver() {
// ...
}
public boolean acceptsURL(String url) {
return url.startsWith("jdbc:dbvendor");
}
}
Since the DriverManager
roughly look like this (it actually uses the old fashioned Vector
)
private static final Set<Driver> drivers = new HashSet<Driver>();
public static void registerDriver(Driver driver) {
drivers.add(driver);
}
public static Connection getConnection(String url, String username, String password) throws SQLException {
for (Driver driver : drivers) {
if (driver.acceptsURL(url)) {
return driver.connect(url, username, password);
}
}
throw new SQLException("No suitable driver");
}
...you can get a connection from it without the need to instantiate the driver itself!
This way the JDBC code is highly portable. You can change the DB or distribute the code among users with different DB's without the need to change/hack/rebuild the code itself.
It's not only JDBC which uses this approach, also other API's such as Servlet API, ORM's like Hibernate/JPA, dependency injection frameworks, etcetera uses reflection to load the classes based on externally configureable propertiesfiles, XML config files and/or annotations. It all just makes the code much more portable and pluggable.
You can use the Class::forName method if the class is in the class path. However if you need to give a path along with the class name i.e c:\document\xyz.class you will have to use the URLClassLoader class.