Java Reflection, Class Objects - java

My objective is to read in to the command line the name of a Class I wish to observe info on. When I know the class name before runtime, I have no issue. What I can't seem to manage is how to create a class object based on a string input.
public class Tester {
static void methodInfo2(Object obj) throws ClassNotFoundException {
//some stuff
System.out.print("Test!");
}
public static void main (String args[]) throws ClassNotFoundException{
String className = args[0];
System.out.println("Class: "+className);
//myclass2 mc = new myclass2();
//Class c = mc.getClass();
Class argClass = Class.forName(className);
try {
methodInfo2(argClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
The 2 commented out lines in the main method show what I have done in the past when I know the class name before I compile. The following uncommented line shows what I thought should work, but I receive a ClassNotFoundException. The class certainly exists so I'm not sure what problem I'm having.

Two suggestions:
Make sure you're giving it the fully-qualified name (e.g. "java.lang.Thread" and not just "Thread").
Make sure the compiled class file is actually on the classpath.

Class.forName is the right way to load a class by name at runtime.
Either your argument is wrong or your class isn't in the classpath.

Related

Java - why does this input not generate ClassNotFoundException

While I am using reflection I came upon this error. I have a class named Test1, and when I try to get a Class object of it by entering test1 as input to Class.forName() it generates LinkageError instead of ClassNotFoundException. Why does the lower-case letter confuse the program and prevent it from generating the usual ClassNotFoundException as with for example "sjghdjgh"?
Minimal example
Main
public class Main {
public static void main(String[] args) {
try {
Class.forName("test1");
}
catch (ClassNotFoundException cnfe) {
System.out.println(cnfe.toString());
}
catch (LinkageError le) {
System.out.println(le.toString());
}
}
}
Test1
public class Test1 {
}
If you ask some file systems for test1.class they will give you back a Test1.class instead of a file-not-found (because they are case-insensitive) and then that class file does not contain a class called test1, but instead a Test1 (Java class names are case-sensitive), so that the classloader errors out.
This is rarely a problem in practice, because most applications are deployed using Jar files (so that there are no individual files for the classes in the filesystem).

Check what java class type is this

I want to check String is interface or not using Reflection method .isInterface. Here is what I tried but it gives Class not found exception.
public class CheckingClassType {
public static void main(String args[]) {
try {
Class c = Class.forName("String");
System.out.println(c.isInterface());
} catch (Exception e) {
System.out.println(e);
}
}
}
Use Class c = Class.forName("java.lang.String");
You need to give the full package name of the class. The reflection needs to know that to instantiate the class name as multiple classes with same name can exist in different packages.

Java changing variable name changes program behaviour

I found a scenario where java program behaves differently after renaming a variable. I understand this isn't actually code that anyone would use but if someone knows whats going on it would be nice to have an explanation. I tried this with java 1.6 on Eclipse Kepler.
package _test;
public class TestClass{
public static void main(String...args){
Object testClazz$1 = new Object (){
public String toString() {
return "hello";
}
};
TestClass$1 test = new TestClass$1();
System.out.println(testClazz$1.toString());
test.doStuff();
}
}
class TestClass$1{
public void doStuff(){
System.out.println("hello2");
}
}
This outputs:
hello
Exception in thread "main" java.lang.NoSuchMethodError:
_test.TestClass$1.doStuff()V at _test.TestClass.main(TestClass.java:13)
As far as I understand the compiler creates a TestClass$1.class file for the testClazz$1 object and this causes a naming collision.
But after renaming the object to testClass$1:
package _test;
public class TestClass{
public static void main(String...args){
Object testClass$1 = new Object (){
public String toString() {
return "hello";
}
};
TestClass$1 test = new TestClass$1();
System.out.println(testClass$1.toString());
test.doStuff();
}
}
class TestClass$1{
public void doStuff(){
System.out.println("hello2");
}
}
The output is:
_test.TestClass$1#2e6e1408
hello2
Any ideas what is going on here?
Anonymous classes are named automatically by appending a $ sign and an increasing number to the name of the enclosing class.
In your first example the anoymous class will be named TestClass$1 which has no doStuff() method, you only override toString() that's why you get NoSuchMethodError error.
In your 2nd example you already have a local variable named TestClass$1, so the auto-generated name chosen by the compiler will be a different name, most likely TestClass$2. Since you instantiate TestClass$1 which is not an anonymous class but a class explicitly defined by you, that will be instantiated which has a doStuff() method which properly prints "hello2" and which does not override Object.toString() so printing the value returned by its toString() method will print the default value as specified in java.lang.Ojbect (which is the class name appended with a # sign followed by the default hash code in hexadecimal format).
Conclusion: While this is an interesting example, you should never use the $ sign in your class names and in identifier names.
When the classloader encounters the anonymous Object() {...} class it loads it under the name TestClass$1. This creates a conflict with class TestClass$1 {...} which was explicitly defined.
However, class name conflicts are handled rather ungracefully. This bit of documentation tells us that
If the class c has already been linked, then this method simply returns.
That's what happens in your case. You only ever load one of the two TestClass$1 classes.
The "different variable names" are not responsible for anything other than re-compiling and re-linking within your compiler. At this point, the classloader is free to pick whichever one of the two TestClass$1 likes better and use that everywhere.
If you're using something like eclipse (like I am) then your bytecode will get cached, until a new touch operation on the source file (and updating of timestamps...). Here's what I did to reproduce (running openjdk 1.7, Eclipse Kepler under RedHat):
Put this inside a source file TestClass.java:
package javaclasses.classes;
public class TestClass{
public static void main(String...args){
Object o = new Object (){
public String toString() {
return "hello";
}
};
TestClass$1 test = new TestClass$1();
System.out.println(o.toString());
test.doStuff();
}
}
class TestClass$1{
public void doStuff(){
System.out.println("hello2");
}
}
ctrl + F11 outputs:
javaclasses.classes.TestClass$1#42293b53
hello2
Open this in a console and touch TestClass.java
Go back in eclipse and ctrl + F11 now outputs:
hello
Exception in thread "main" java.lang.NoSuchMethodError: javaclasses.classes.TestClass$1.doStuff()V
at javaclasses.classes.TestClass.main(TestClass.java:13)
Conlusion: All that can be said definitively is that the default ClassLoader is unreliable for manually resolving classes with the same fully qualified names. Changing variable names doesn't matter, the updated timestamp on your source file does.
I modified your code to remove the "$" from the class names and renamed testClass$1 to t and changed the println slightly as follows:
public class TestClass{
public static void main(String...args){
Object t = new Object (){
public String toString() {
return "t.toString()";
}
};
TestClass1 tc1 = new TestClass1();
System.out.println(t.toString());
tc1.doStuff();
}
}
class TestClass1{
public void doStuff(){
System.out.println("TestClass1.doStuff()");
}
}
Output is now:
t.toString()
TestClass1.doStuff()
Is this what you expect?

Class not found exception in Java Reflection

Hi I am using the following code from this site: http://java.sun.com/developer/technicalArticles/ALT/Reflection/
But when I am running it it showing exception java.lang.ClassNotFoundException: A
May be I am going somewhere wrong Please help.
Here is the code:
package com.Test;
class A {}
public class instance1 {
public static void main(String args[])
{
try {
Class cls = Class.forName("A");
System.out.println("gfsdga");
boolean b1
= cls.isInstance(new Integer(37));
System.out.println(b1);
boolean b2 = cls.isInstance(new A());
System.out.println(b2);
}
catch (Throwable e) {
System.err.println(e);
}
}
}
The class is actually called com.Test.A because you've declared it within the com.Test package - Class.forName() takes the package-qualified class name. (Note that com.Test is a pretty odd package name, too.)
You need Class.forName("com.Test.A") instead.

Trouble creating custom class loader

I am trying to create a custom class loader to accomplish the following:
I have a class in package com.company.MyClass
When the class loader is asked to load anything in the following format:
com.company.[additionalPart].MyClass
I'd like the class loader to load com.company.MyClass effectively ignoring the [additionalPart] of the package name.
Here is the code I have:
public class MyClassLoader extends ClassLoader {
private String packageName = "com.company.";
final private String myClass = "MyClass";
public MyClassLoader(ClassLoader parent) {
super(parent);
}
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
Class<?> clazz = null;
String className = name;
// Check if the class name to load is of the format "com.company.[something].MyClass
if (name.startsWith(packageName)) {
String restOfClass = className.substring(packageName.length());
// Check if there is some additional part to the package name
int index = restOfClass.indexOf('.');
if (index != -1) {
restOfClass = restOfClass.substring(index + 1);
//finally, check if the class name equals MyClass
if (restOfClass.equals(myClass)) {
// load com.company.MyClass instead of com.company.[something].MyClass
className = packageName + myClass;
clazz = super.loadClass(className, true);
}
}
}
if (clazz == null) {
// Normal clase: just let the parent class loader load the class as usual
clazz = super.loadClass(name);
}
return clazz;
}
}
And here is my test code:
public class TestClassLoader {
public void testClassLoader () throws Exception {
ClassLoader loader = new MyClassLoader(this.getClass().getClassLoader());
clazz = Class.forName("com.company.something.MyClass", true, loader );
}
public static void main (String[] args) throws Exception {
TestClassLoader tcl = new TestClassLoader();
tcl.testClassLoader();
}
}
MyClassLoader picks up the correct class (i.e. com.company.MyClass) and returns it just correctly from loadClass (I have stepped through the code), however, it throws an exception at some later point (i.e. after loadClass is called from the JVM) as follows:
Exception in thread "main"
java.lang.ClassNotFoundException:
com/company/something/MyClass at
java.lang.Class.forName0(Native
Method)
at
java.lang.Class.forName(Class.java:247)
at [my code]
Now, I realize some of you may be thinking "Why would anyone need to do this? There has to be a better way". I am sure that there is, but this is something of education for me, as I'd like to understand how class loaders work, and gain a deeper understanding into the jvm class loading process. So, if you can overlook the inanity of the procedure, and humor me, I'd be very grateful.
Your Question
This is pure speculation. The class name is stored in the java byte code. Thus the classes you manage to load by this technique will be defective. This is probably confusing the system.
The ClassLoader probably keeps a reference to com.company.something.MyClass, but the class itself probably keeps a reference to com.company.MyClass. (I use probably a lot because I don't really know for sure.) Probably everything works OK until you use the MyClass class for something. Then the inconsistency creates trouble. So when is this exception thrown?
If you are interested in learning how class loaders work, then you can use javap to get at the byte code. This would also allow you to check my hypothesis.
If my hypothesis is correct, then the solution would be to fix the byte code. There are several packages that allow you to engineer byte code. Copy a class, change the name of the copied class, and then load it.
Aside
While not relevant to your question: I find the below to be unnecessarily complicated (and it doesn't work on com.company.something.somethingelse.MyClass).
// Check if the class name to load is of the format "com.company.[something].MyClass
if (name.startsWith(packageName)) {
String restOfClass = className.substring(packageName.length());
// Check if there is some additional part to the package name
int index = restOfClass.indexOf('.');
if (index != -1) {
restOfClass = restOfClass.substring(index + 1);
//finally, check if the class name equals MyClass
if (restOfClass.equals(myClass)) {
// load com.company.MyClass instead of com.company.[something].MyClass
className = packageName + myClass;
clazz = super.loadClass(className, true);
}
}
Why not?
//Check if the class name to load is of the format "com.com // Check if the class name to load is of the format "com.company.[something].MyClass"
if ( ( name . startsWith ( packageName ) ) && ( name . endsWith ( myClass ) ) )
I don't think you can really do that via a classloader. Theoretically if some other class is attempting to load a class it assumes is called 'com.mycompany.foo.MyClass' then it's too late, someone already has a class with bytecode referencing 'com.mycompany.foo' and that class is already loaded.
Repackaging is a lot easier at the static level, by using something like ASM to repackage all the code at build time. You of course have to modify both he classes package itself and all the classes that my refer to that package.
If you use Maven, check out the shade plugin. If not I seem to recall a tool called JarJar.
You can of course do that kind of byte code manipulation at runtime via a javaagent and a ClassTransformer. The code for the maven-shade-plugin is actually pretty small -- if you grabbed it and ripped out the maven parts you'd probably have something working in 2-3 days.

Categories

Resources