java.lang.ClassNotFoundException: A - java

I was testing an example of accessing private method from another class and got an exception
public class WithoutMain
{
public static void main(String args[]) throws Exception
{
Class c = Class.forName("A");
Object o = c.newInstance();
Method m = c.getDeclaredMethod("message", null);
m.setAccessible(true);
m.invoke(o, null);
}
}
public class A {
private void message(){
System.out.println("This is a private method.");
}
}
Getting following exception
Exception in thread "main" java.lang.ClassNotFoundException: A
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
These 2 class resides in same package. Can anyone tell me why this exception shows?

You need to use the fully qualified name of a class to load it using Class.forName()
Now you may argue why? As in your case both the classes are in the same directory/package.
I would argue the other way, there is no rule defined in java that the class loader will look in the same directory first.
If you want to learn how class loading works, I would suggest you source code for the class java.lang.ClassLoader class
So when you invoke the Class.forName it uses delegation and the class loader which is assigned the job to load this class will not know the current package or any location. Hence, it needs the fully qualified class name.
One more tip, in java a fully qualified class name of a loaded class is <ClassLoader><packageName><className>. This is how the classes in JVM are uniquely identified.
Hope this helps
EDIT
Your code will work only in one condition and that is, if both the classes are in default package.

You have to provide the fully qualified name of the class, not only the simple class name.
Class c = Class.forName("<package>.A");

Try changing
Class c = Class.forName("A");
to
Class c = Class.forName("yourPackagePath.A");
the forName method does not take in account the package of the user call.

You need the FQN as per the docs:
Parameters:
className - the fully qualified name of the desired class.

The Java Class class isn't in your package, so it needs to know where the class is located otherwise it doesn't know where it is in order to load it(just like the classloader loads it from a full file path).
so
Class<? extends Object> c = Class.forName("A");
needs to be
Class<? extends Object> c = Class.forName("package.A");
where package is the full qualified name of the package so if the package is
foo.bar
it would become
Class<? extends Object> c = Class.forName("foo.bar.A");

As already stated in answers before, needs full name.
To avoid manually typing it you can simply use:
Class<?> clazz = Class.forName(A.class.getName());

Related

Get class properties from class absolute path using reflection in java

im generating new classes using file package but when i want to get the class properties using reflection it doesn't work and it gave me the error bellow but when i refresh my package by clicking on the right button of my mouse and i click on refresh and i rerun my function it works.
So now i think that i need to change my method to get those properties by using absolut path instead of name of package.
my code
import java.lang.reflect.Field;
public class Main7{
public static void main(String[] args) throws Exception {
Class classe = Class.forName("com.test.model.Client");
// affiche tous les attributs
System.out.println("Attributs -------------------------------------");
for (Field attribut : classe.getDeclaredFields()) {
// System.out.print(" "+Modifier.toString(attribut.getModifiers()));
String type = attribut.getType().getName();
if(type.contains(".")) {
String[] tab = type.split("\\.");
type=tab[2];
}
System.out.println(type+" "+attribut.getName());
}
}
}
error
Exception in thread "main" java.lang.ClassNotFoundException: ma.dxc.generator.model.Client
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at generatortest.Main7.main(Main7.java:16)
good result
Attributs -------------------------------------
long id
String name
String city
The easiest way to get class properties is like this:
Class klass = com.test.model.Client.class;
Or simply:
Class klass = Client.class;
If you've already imported the class above.
Or if you have an intance of an object:
Class klass = obj.getClass();
Class.forName() is used to dynamically load new classes into the system. And is not necessary to obtain the class properties through reflection.
Once you have the Class object you can obtain more info like getDeclaredFields() and others.
If you want to load something outside the current classpath you could use URLClassLoader like this:
URLClassLoader loader = new URLClassLoader(new URL[] { "file://path/to/jar/or/directory"});
Class klass = loader.loadClass("com.test.model.Client");
This article may be relevant as well: How to use URLClassLoader to load a *.class file?
Class.forName() won't look beyond your classpath. Its intended to dynamically load something that has already been added to your classpath.

Can't find Class with Class.forName() but it exists

I have a program in which I am generating classes at runtime (included only variable and associated getters and setters methods). Later I want to fill the classes.
To get the class - I know its Name, but its not in the classpath - I tried .forName() but I always get a ClassNotFoundException.
Here is my example:
Exception in thread "main" java.lang.ClassNotFoundException: com.test.wam.business.wsobjects.Testclass
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at gui.Application.main(Application.java:94)
And the code:
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
DynamicURLClassLoader dynamicURLClassLoader = new DynamicURLClassLoader(urlClassLoader);
dynamicURLClassLoader.addURL(new URL("file://C:\\dev\\Eclipse_was\\guitest\\generated"));
Class c = Class.forName("com.test.wam.business.wsobjects.Testclass");
Object classInstance = c.newInstance();
The ClassLoader:
public class DynamicURLClassLoader extends URLClassLoader {
public DynamicURLClassLoader(URLClassLoader classLoader) {
super(classLoader.getURLs());
}
#Override
public void addURL(URL url) {
super.addURL(url);
}
}
The full qualified Name to the file (created with eclipse -> copy full qualified Name)
/guitest/generated/com/test/wam/business/wsobjects/Testclass.java
What is wrong here?
I can see two problems:
1) You are creating a DynamicURLClassLoader and adding the URL to it, but you are not actually using it. This statement:
Class c = Class.forName("com.test.wam.business.wsobjects.Testclass");
will use the classloader that loaded the current class. That is probably the application's default classloader, but it is certainly NOT the classloader you just created. The javadoc says:
[Class.forName(className)] returns the Class object associated with the class or interface with the given string name. Invoking this method is equivalent to:
Class.forName(className, true, currentLoader)
where currentLoader denotes the defining class loader of the current class.
So .... the solution is:
Class c = Class.forName("com.test.wam.business.wsobjects.Testclass",
true, dynamicURLClassLoader);
2) This string:
"file://C:\\dev\\Eclipse_was\\guitest\\generated"
is not a valid "file:" URL. The correct URL for the path you are trying to reference would be:
"file:///C:/dev/Eclipse_was/guitest/generated"
The way that you wrote the URL might work, but it is not the correct way to do it.
Reference:
File URIs in Windows
The method Class.forName(String) uses the ClassLoader of the caller class, if you want to use a specific ClassLoader to load your class you need to use Class.forName(String name, boolean initialize, ClassLoader loader) instead as next:
Class c = Class.forName(
"com.test.wam.business.wsobjects.Testclass", true, dynamicURLClassLoader
);
NB: This will work if and only if the URL that you provide to your DynamicURLClassLoader is valid and is the path to the parent folder in which you have your class

Why does the whole package structure need to be included in Class.forName()?

I have developed a normal Java project and I am trying to understand the difference between ClassNotFoundException and NoClassDefFoundException. I have found one weird behavior, which is that I need to include the whole package structure when I am calling Class.forName().
See the code below:
package org.com;
public class MainApp {
public static void main(String[] args) {
try {
Class cls = Class.forName("org.com.MainApp");
System.out.println("Class = " + cls.getName());
} catch(ClassNotFoundException ex) {
System.out.println(ex.toString());
}
}
}
If I use Class.forName("MainApp") instead of Class.forName("org.com.MainApp") an exception is thrown.
Can someone please explain the actual reason for this?
Basically, because the contract says so:
className - the fully qualified name of the desired class.
You could very well have two classes named MainApp, in different packages. How would the classloader know which class to load if you omit the fully qualified name (i.e. including the package)?
In other words, it is a design desicion, and in my opinion, a good one.
Class.forName("MainApp") would actually try to load a class named MainApp located in the default package, that is no package name is specified in the class.
Because your class exist in that Package and forName method requires Fully Qualified name
Suppose if java has a AI to identify your classes by specifying only name, what will happend when you specify "Date" ?
Class cls = Class.forName("Date");
Is it need to give you java.util. Date class or java.sql.Date class or any third party library Date class ?
Thats why you need to specify package name to uniquely identify that class
Class.forName has no concept whatsover of your class or where it's called from.
It's a general purpose tool.
For example:
You are the ONLY person in the city called alexander.
You order a package by amazon and say, deliver it to Alexander.
You, from your perspective are the only one alexander in the city, quite easy not?
Now look from the perspective of amazon.. they have no clue where to send it.
The same logic applies to class.forName.
When you compile code, the compiler knows which package you are compiling and which classes you have imported, so when you give it a class name it can check these things to find which fully qualified class name you meant.
However, Class.forName is method on the class Class. All it knows is what you passed it, the class name. It doesn't know the package of the caller, nor what imports you used. Only the fully qualified class name can work.
Simple answer:
Same class may be part of multiple packages and JVM is not sure which class you are want to load.
You may have MainApp class in package1 and package2.
To avoid ambiguity, fully qualified class name is required in Class.forName
e.g.
Class.forName("package1.MainApp")
Class.forName("package2.MainApp")
Notes from documentation page:
public static Class<?> forName(String className)
throws ClassNotFoundException
Returns the Class object associated with the class or interface with the given string name. Invoking this method is equivalent to:
Class.forName(className, true, currentLoader)
Parameters:
className - the fully qualified name of the desired class.
Returns: the Class object for the class with the specified name.

Java refers to lib java.lang.String not my java.lang.String

I have created a class with the same name and package as the String java class.
package java.lang;
public class String {
public String() {
System.out.println("This is my string ");
}
public void show() {
System.out.println("From show mathod .");
}
}
When I try to invoke this show method it gives error for no such method. It refers to API class. why.?
import java.lang.*;
public class Test {
public static void main(String[] args) {
String str = new String();
str.show();
}
}
I know that I am violating the rule of unique package and class name. But I want to know if I add two jars in my class-path and they have same structure. In such case is there any behaviour defined by Java which class will be loaded.
And why java does not load my class instead of Java API classes? (I also tried this with other api defined classes)
#Please do not give answer like I am violating the rule or work arounds.
Java comes shipped with classes. One of these classes is java.lang.String.
These pre-shipped classes take precedence in the classpath over any class that you might write that have the same package and name.
Whenever you do a new java.lang.String(), regardless if you have a class with the same package and name, the runtime class shipped by Oracle will take precedence.
Since java.lang.String is also a final class, there's no way you will be able to add new methods to it. The best solution is for you to create a new class in a different package or under a different name.
classes can not be redefined after the VM has started and the class has been loaded. As java.lang.String is being used way before your code, it would already be loaded.
I am not sure that you could even achieve this using a byte code enhancement library like javassist
Just tried to write custom ClassLoader to load my class named java.lang.String. Ran out of luck here:
Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.lang
at java.lang.ClassLoader.preDefineClass(ClassLoader.java:658)
at java.lang.ClassLoader.defineClass(ClassLoader.java:794)
at java.lang.ClassLoader.defineClass(ClassLoader.java:643)
at temp.TempLoader.findClass(TempLoader.java:20)
at temp.TempLoader.main(TempLoader.java:12)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Offending code in JRE java.lang.ClassLoader:
if ((name != null) && name.startsWith("java.")) {
throw new SecurityException
("Prohibited package name: " +
name.substring(0, name.lastIndexOf('.')));
}
It would be interesting to see any "pure Java" solution (not involving out-of-JVM changes to runtime) which can workaround this.
You may wonder how JVM itself loads java.* classes? Well, they loaded by so called bootstrap loader, during initialization of JVM instance. I assume, there's no public API to that classloader.
I don't know what you are trying to do exactly, but I can suggest a different approach.
In java String class is final, so, it can't be extended. Create a wrapper class which has your additional methods and a String field.. Now you can define and call show() on your class (You can't do it on the String directly..) If split() is called on your class, then just delegate the call to String class's split().
expose a method getInstance() in your wrapper . Whenever myWrapper.getInstance() is called, call new String() from there..

java.lang.ClassNotFoundException for a specified class

In my main I have the following statement
Class booki = Class.forName("Book");
which throws a java.lang.ClassNotFoundException exception
when I use the full path like Class booki = Class.forName("javatests.Book"); it is ok.
The main class and the Book class are in the same package, I also tried using import static javatests.Book.*; but still it throws the exception if I don't set the full path javatests.Book. Can someone explain to me why?
Class.forName resolves a fully qualified class name to the class. Since a method does not know where it is called from neither the package of the calling class nor imports in the calling class play any role.
From docs Class#forName
public static Class<?> forName(String className)
throws ClassNotFoundException
Parameters:
className - the fully qualified name of the desired class.
So this will not throw ClassNotFoundException
Class booki = Class.forName("javatests.Book");
For example, it is not needed to import java.lang.* package in java program but to load class Thread from java.lang package you need to write
Class t = Class.forName("java.lang.Thread");
the above code fragment returns the runtime Class descriptor for the class named java.lang.Thread
You always need a qualified class name unless it's inside the same package. If i define a class foo in my package i can call a method Class testClass = Class.forName("foo") , but i can't call Class testClass = Class.forName("SecureRandom"); even if I import SecureRandom. That's just how the function works. It probably has a shortcut where it tries to find things inside local packages, but doesn't do much behind that.
Firstly the Book class must be in the package javatests.
the JVM load the class by name,through the classpath.
There is no class named "Book" in the classpath.
So JVM give you a ClassNotFoundException when excuse Class.forName("Book").
But 'Class.forName("javatests.Book")' tells JVM the class named 'Book' is in package 'javatests'.
So the JVM can find it and load it.
I hope my answer is helpful :)
JLS provides the following description:
Class lookup is always on behalf of a referencing class and is done through an instance of ClassLoader. Given the fully qualified name of a class, this method attempts to locate, load, and link the class.
The JDK uses one instance of ClassLoader that searches the set of directory tree roots specified by the CLASSPATH environment variable; and obviously it is not aware of the place (package) it has been called. That is why it needs fully qualified name.

Categories

Resources