-
Dynamic object creation?
Using a simple class as an example i wanted too see if i could figure out how to load a class at runtime and create an instance of it to invoke any of it's methods. Almost like using the LoadFrom() or Load() methods contained in the System.Reflection.Assembly class in Visual Basic.Net. I looked in my java docs under the java.lang.System class and found the load() method but all of the docs refer to is loading a library for native method use.
So how do i modify the Example class to load and create a new instance of the Square class once the Square class is taken out of the current .java file of course. ;)
Code:
class Square{
public double squareRoot(double d){
return Math.sqrt(d);
}
}
public class Example{
public static void main(String[] args){
Square s = new Square();
System.out.println(s.squareRoot(25.0));
}
}
-
try...
Code:
Class c = Class.forName("Square");
Square s = (Square)c.newInstance();
s.squareRoot(25.0);
Obviously this is most useful if you use an interface and have a list of classes that implement that interface.
The string parameter to forName has to be the fully qualified class name so if you wanted to do JFrame you would have to do...
Code:
Class.forName("javax.swing.JFrame");
One last thing newInstance() only works if there is a default constructor (as far as i know)
Hope that helps!
Mrs Kensington
-
oh and the Square class needs to be found in the directory pointed to in your classpath variable. Otherwise you have to do the System.load("c:\\Square.class") thing first to load it into the VM.
Mrs Kensington
-
Perfect Mrs Kensington, that was exactly what i was
looking for. Looks almost like the same thing one would
do in Visual Basic.Net The only thing that i dont quite get
is that in Visual Basic you have to use basicaly the same class
System.Reflection.Assembly to load and create a new instance of a class because the load methods return an object refrence that you can work with. In Java the load method returns nothing. So how do i know that the class has been loaded into memory? Also for "Square s = (Square)c.newInstance();"im getting a "cannot resolve symbol" so am i making a mistake on the this line as to where the class is refrenced? Thanks. :)
Code:
public class Example{
public static void main(String[] args){
System.load("C:\\Square.class");
Class c = Class.forName("Square");
Square s = (Square)c.newInstance();
System.out.println(s.squareRoot(25.0));
}
}
-
How to tell if class has been loaded...
Sytem.load(String className)
can throw 2 exceptions...
SecurityException - if a security manager exists and its checkLink method doesn't allow loading of the specified dynamic library
UnsatisfiedLinkError - if the file does not exist.
so stick it in a try...catch block and if UnsatisfiedLinkError is thrown then the file doesn't exist. The other one would be if you were using it in an applet and didn't have security.
The reason your getting a "cannot resolve symbol" error is because the example class knows absolutely nothing about the Square class. i.e. it wasn't in your classpath when you compiled.
Obviously if it was in your classpath you wouldn't need to use the loading code as it was already loaded.
When i was looking into dynamic class loading i was using it for a plug-in system. The basic frame work was...
File Pluggable.java
Code:
public interface Pluggable
{
public void go();
}
File Plugin1.java
Code:
public class Plugin1 implements Pluggable
{
public void go()
 {
   System.out.println("Plugin 1");
 }
}
File Plugin2.java
Code:
public class Plugin2 implements Pluggable
{
public void go()
 {
   System.out.println("Plugin 2");
 }
}
File PluginManager.java
Code:
public class PluginManager
{
public void loadPlugin(String filename, String className)
{
System.load(filename);
Class c = Class.forname(className);
Pluggable p = (Pluggable)c.newInstance();
p.go();
}
public static void main(String[] args)
{
File f = new File(".\\plugins\\");
File[] files = f.listFiles();
String extension = ".class";
for(int i=0; i<files.length; i++)
{
if(files[i].isFile() && files[i].getName().endsWith(extension))
{
String path = files[i].getAbsolutePath();
String className = files[i].getName();
className = className.substring(0, className.length() - extension.length());
loadPlugin(path, className);
}
}
}
}
Plugin1.class and Plugin2.class would have been placed in the plugins sub-directory. When PluginManager was run it would search through the plugin directory find all files ending with .class, load them, create an instance of them and assign it to a Pluggable. The go method would then be called.
This would print out...
or the otherway round...
The only classes that needed to be in the classpath upon compiling where PluginManager and Pluggable. (When you compile Plugin1 or Plugin2 you also need Pluggable in the classpath).
Using this system people can create there own plugin easily, all they had to do was have the Pluggable class file, implement the interface and there they had created a plugin.
To make the code work properly it needs alot of try..catch blocks.
Don't know if this is what you were looking for but this is the way i used it.
Hope that helps you understand it a bit more
Mrs Kensington
-
Thanks for the example. Cool framework. :D Is there anyway i can load a class and create an instance of it without having it in my class path? Say for instance that Pluggable was a class located in another directory or another drive?
-
The only way to load a class from another drive or directory which isn't in the classpath is to use the System.load() method.
The problem with that way if you can't use that class directly. It only works if its a sub-class of a class that is in your classpath. Then you can assign your instance to to a variable of the super class.
***
Just re-read your question and heres a different responce (I managed to read your question in 2 different ways :))
You can pass a full path to System.load. So if the class you wanted to load was d:\other_classes\Square.class you could do...
System.load("d:\\other_classes\\Square.class");
I hope one of those answers helps you!
-
I get it and i dont. :confused: :D It makes sense that the superclass would have to be in the classpath in order to create an instance of and use it's subclass's. But i thought using the -classpath switch or setting the path manualy in the autoexec.bat file were so that the runtime would know where find the classes to execute. So if i am using the load method and explicty telling it System.load("C:\\Square.class"); what is the classpath needed for?
I running into a problem at the Square s = (Square)c.newInstance(); but i think im still fuzy on the java coding anyway. Doing this in Visual Baisc seems a little clearer to me
For instance in visual Basic.....
Code:
Public Sub Link()
Dim dll As Reflection.Assembly
Dim obj As Object
dll = System.Reflection.Assembly.LoadFrom("C:\Square")
obj = dll.CreateInstance("Square")
End Sub
It's like in Java once you load the class with the System.load("C:\\Square.class"); line, i dont see the link with Class c = Class.forName("Square"); Then say were creating the actual instance using Object o = c.newInstance(); what was c holding?
-
ok the easy one first of all...
when Java first runs it looks through all the paths on the classpath finds all .class files and loads them into the JVM.
But what if you want to load a class that wasn't in the classpath? Well then you have to use the System.load method to load it into the JVM.
What if we didn't have a classpath? We would have to call System.load for each class we wanted to use. Sure it might mean the memory usage would be a tiny bit better but it would be very annoying. And thats completely ignoring the issue of how do you originally load the class that loads all your classes.
The answer to the 2nd question is pretty much the same as whats the difference between a class and an instance.
Using an example of a Car...
The class is the blueprint of the car. It says its a car, it has a make/model, it has a certain number of wheels, it has a color, and an engine. But thats only a blueprint. You can't drive a blueprint, you can drive a blue, 600cc, 3 wheeled, Robin Reliant (if you so wished). The Robin reliant is our instance.
So the line Class c = Class.forname("Square");
creates a "blueprint" and thats what c holds. It has all the information about the class like, how many variables it has, their type, their names, whether it is an interface, whether its abstract, what methods it has... but it has none of the data.
newInstance() calls the default constructor of the class which (hopefully) fills it with data.
Having the Class class inbetween can be very useful. What if you want to know if your Dyanamically loaded class is assignable to ActionListener. You could do...
Code:
Class c = Class.forName("Square");
Object o = c.newInstance();
if(o instanceof ActionListener)
.....
or you could do
Code:
Class c = Class.forName("Square");
Class other = Class.forName("java.awt.event.ActionListener");
if(other.isAssignableFrom(c))
.....
They do exactly the same thing but the 2nd doesn't involve creating any instances (apart from the Class objects thats is)
I hope thats has answered your questions, and i haven't gone in to too much detail.
Mrs Kensington
-
Ok finally the boards are up. I wanted to try and get this post resolved but as we all know the boards went down. :D
As for the line Class c = Class.forName("Square"); i just found the syntax a bit weird comming from looking at the Visual Basic equivalent. In java it is almost like somthing is being abstracted out of the process. But going back the orgional problem, i decided to run the code without compiling the class to be created and a java.lang.UnsatisfiedLinkError was thrown because i guess it was unable to load the library. I compiled the class and ran the code but now im getting the same error with a message saying "A device attached to the system is not functioning" :confused: Any idea what this means?
-
I'm so sorry i've been giving you very wrong information.
This is a case of Mrs Kensington being very stupid and assuming something and not actually checking it.
System.load(String filename);
is used for loading native library's like .dll's (or .so's) not for loading class files.
Which means that my code (for the plugin framework) doesn't work unless you remove the System.load call and all the class's you want to load are in the classpath.
Class's are loaded using the ClassLoader class. I think you would have to create your own ClassLoader to actually load a class from anywhere (or just extend the current one).
Theres an article on javaworld about making one in Java 1.2 here...
http://www.javaworld.com/javaworld/j...classload.html
I'm pretty sure it would work in 1.4. It looks like all you have to do is load the class file into a byte array and use
defineClass(String name, byte[] b, int off, int len)
Looking at the javaworld article this section looks like the correct code...
Code:
FileInputStream fi = null;
try
{
String path = name.replace('.', '/');
fi = new FileInputStream("store/" + path + ".impl");
byte[] classBytes = new byte[fi.available()];
fi.read(classBytes);
definePackage(name);
return defineClass(name, classBytes, 0, classBytes.length);
}
I think that is how you dynamically load a class.
Hope i didn't confuse you too much
Mrs Kensington
-
That's ok. ;) I know that System.load() can be used to load native library's but i figured that i could load a class also. ll have too look in the classloader class and come back when i have an answer. :D
Code:
class Native{
static{
System.loadLibrary("nativeLib");
}
native void nativeMethod();
}
-
We are on the right track though. After reading some information in the java.lang.Class using the static Class.forName() method is the correct way to go. I tried using Class.forName() passing different paths to it but all i get is a java.lang.ClassNotFoundException. The java docs say that in order to load classes over a network or from any other source other than the standard system classes, you must use a custom ClassLoader object that knows how to obtain data from the source. Such as a java.net.URLClassLoader
-
I decided to try and alternate method. Now this is what i dont understand. Within the code below the if statement evaluates to true since the class file is in fact in that directory.
Code:
import java.io.*;
public class Example{
public static void main(String[] args){
try{
File f = new File("C:\\Square.class");
if(f.exists()){
System.out.println("Square exists");
}else{
System.out.println("Square dosesnt exist");
}
}catch(Exception e){System.out.println(e);}
}
}
Now when i try to load the class and run i keep getting a java.lang.ClassNotFoundException
Code:
public class Example{
public static void main(String[] args){
try{
ClassLoader cl = ClassLoader.getSystemClassLoader();
Class c = cl.loadClass("C:\\Square.class");
}catch(Exception e){System.out.println(e);}
}
}
This dosen't make any sense to me. If the class is in fact in the directory specified then why would the runtime say that it doesnt exist? :confused:
-
the parameter passed to loadClass is called name. If you look at other api calls. If they mean a path in the fileSystem they normally call it path or filepath. So they just be meaning the qualified name of the class like "java.lang.Object" or for yours probably "Square" (unless its in a package). So i'm assuming its saying it doesn't exist because the class "c:\Square.class" doesn't exist in its list of classes.
Obviously that theory only works if they follow the convention in the rest of the API but there is a way to test it...
change...
Class c = cl.loadClass("C:\\Square.class");
to...
Class c = cl.loadClass("Square");
now normally that wont work (as long as its not in your classpath). If you then modify your classpath to include c:\ then it will probably work.
Looking at the URLClassLoader you can pass it an array of URL's... am i right that you can have a URL begining with "file://" if so could you not create a URL that points to the directory on your hard drive that Square resides in. Then pass that into URLClassLoader and see if loadClass("Square") works.
If it doesn't try sticking the Square.class file in a JAR file and try pointing to that.
If that doesn't work then you may have to try loading the class file as a byte array and defining it.
Hope that helps
Mrs Kensington
-
Quoting from a book i have. "A class can be dynamicallyloaded by passing it's fully qualified name(ie package name plus class name)
to the static Class.forName() method. This method loads the named class(if not already loaded) into the Java interpreter and returns a Class object for it. Classes can also be loaded with the ClassLoader object.
After reading the above quote i think that i can take the classpath out of the equation because the main purpose of the Class.forName() is to load a class that has yet to be loaded. And if the class was in a directory that was defined in the classpath then it would have been already loaded into the Java interpreter. :p
I changed the method arguement to just the name of the class and still the same problem.
Code:
public class Example{
public static void main(String[] args){
try{
ClassLoader cl = ClassLoader.getSystemClassLoader();
Class c = cl.loadClass("Square");
}catch(Exception e){System.out.println(e);}
}
}
Ill have to look into this more and see if i can find some coding examples somewhere. Right now im just going by my refrence books. :)