Java source file -> List of required classes for compilation - java

For a given Java source code file I want to list all the (fully qualified names of) classes that are (directly) required for compilation. In other words: All classes that are directly used by the code in the source code file, coming from imports, fully qualified names in the source code and other compile time means, but not by reflection or other runtime means.
Is there a way to "ask" the java compiler for this list? Are there other ways to get it?
PS: By "directly" I mean the following: If my source code file requires class A for compilation which uses class B, then class B has to be present to compile the code, but it is not a direct use.

You can try this:
It can be eaily done with just javac. Delete your class files and then compile the main class. javac will recursively compile any classes it needs (providing you don't hide package private classes/interfaces in strangely named files).

Related

How to compile java classes independently

I'm currently wondering if you can compile Java class files without their dependencies, like .o files in C or C++. For example, I have a class A that has an instance of class B inside, but I only want to compile class A. Is there a way to do it? The point is to compile a java program using make because Gradle and Maven just won't let me do what I want to do.
Thank you.
Java is a statically typed language, like C/C++, so any class or method used by your class must be well-known, in order to compile your class.
In C/C++, we use header files to define classes and methods, without implementing them. That way we can compile classes that use them, using only the headers files, not the source files of the required classes/methods.
Java doesn't have header files, so the classes/methods must be available in full. They don't have to be available as source code, i.e. they can be pre-compiled and made available as .class files, most often packaged in .jar files.
So if you have class A depending on class B, you can compile B separately, then compile A separately, as long as B.class is on the classpath.
Unlike C/C++, the Java compiler can compile many files together, which is e.g. needed if A and B depends on each other (circular dependency).
If A and B are part of the same project, then compile them together. If A and B are part of different projects, build project B first, resulting in a B.jar file, then build project A, and give the jar file on the classpath when building project A.

How to compile and run java programs ignoring package info?

I have a pile of .java files. They all have the same class name public MyClass. They all have a main method. They all may or may not have a package declaration at top, and I do not know ahead of time.
I am trying to write a script to compile and run these java programs. This is easy for the files without the package declaration... I just do some cp operations to setup, javac MyClass.java and java MyClass, then rm to teardown. However, the files with the package declaration require special attention. I have a few options that occur to me, including deleting the package lines, or attempting to read the package lines so that I know what the resulting directory structure should be. Both of these require me to go parsing through the .java files, which makes me sad.
Is there a way to compile and run these files without having to parse the .java files? Something like:
javac --ignore_package_structure MyClass.java
would be ideal, but a quick look at the javac man pages suggests that such a thing doesn't exist.
If we can assume that each student submits a single source file named HelloWorld.java, then we can use the "Launch Single-File Source-Code Programs" feature added by JEP 330 in Java 11:
java HelloWorld.java
We don't run javac first, we don't get a .class file (no cleanup needed), and any package declaration is handled automatically.
Remember, the students are still allowed to use many classes, they just all have to be submitted to you in a single source file.
The name of the class doesn't even matter. The first class in the source file is executed.
There isn't any easy way to do this. You could use regex though, and replace all imports with this simple java regex:
"package \w+;"g
Simply stated, you create a Java program to replace all the package names.
How to replace files: Find and replace words/lines in a file

Why do java source files require package declarations?

I think I am failing to understand java package structure, it seemed redundant to me that java files have a package declaration within, and then are also required to be present in a directory that matches the package name. For example, if I have a MyClass.java file:
package com.example;
public class MyClass {
public static void main(String[] args) {
System.out.println("Hello, World");
}
}
Then I would be required to have this file located in com/example, relative to the base directory, and I would execute java com.example.MyClass from the base directory to run it.
Why wouldn't the compiler be able to infer the package name by looking at the directory structure? For example, if I compiled the file from the base directory javac com\example\MyClass.java, I am not understanding why the MyClass.java wouldn't implicity belong to the com.example package.
I understand there is a default package, but it still seems that the package declaration in the source file is redundant information?
As you (implicitly) acknowledged, you are not required to declare the name of a package in the case of the default package. Let us put that quibble aside ...
The reason for this seeming redundancy is that without a package declaration, the meaning of Java1 source code would be ambiguous. For example, a source file whose pathname was "/home/steve/project/src/com/example/Main.java" could have 7 different fully qualified names, depending on how you compiled the code. Most likely, only one of those will be the "correct" one. But you wouldn't be able to tell which FQN is correct by looking at (just) the one source file.
It should also be noted that the Java language specification does not require you to organize the source code tree according to the packages. That is a requirement of a (large) family of Java compilers, but a conformant compiler could be written that did not require this. For example:
The source code could be held in a database.
The source code could be held in a file tree with random file names2.
In such eventualities, the package declaration would not be duplicative of file pathnames, or (necessarily) of anything. However, unless there was some redundancy, finding the correct source "file" for a class would be expensive for the compiler ... and problematic for the programmer.
Considerations like the above are the practical reason that most Java tool chains rely on file tree structure to locate source and compiled classes.
1 - By this, I mean hypothetical dialect of Java which didn't require package declarations.
2 - The compiler would need to scan the file tree to find all Java files, and parse them to work out which file defined which class. Possible, but not very practical.
Turn the question on its head:
Assume that the package statement is the important thing - It represents the namespace of the class and belongs in the class file.
So now the question is - Why do classes have to be in folders that match their package?
The answer is that it makes finding them much easier - it is just a good way to organize them.
Does that help?
You have to keep in mind that packages do not just indicate the folder structure. The folder structure is the convention Java adopted to match the package names, just like the convention that the class name must match the filename.
A package is required to disambiguate a class from other classes with the same name. For instance java.util.Date is different from java.sql.Date.
The package also gives access to methods or members which are package-private, to other classes in the same package.
You have to see it the other way round. The class has all the information about itself, the class name and the package name. Then when the program needs it, and the class is not loaded yet, the JVM knows where to look for it by looking at the folder structure that matches the package name and the class with the filename matching its class name.
In fact there's no such obligation at all.
Oracle JDKs javac (and I believe most other implementations too) will happily compile your HelloWorld class, no matter what directory it is in and what package you declare in the source file.
Where the directory structure comes into the picture is when you compile multiple source files that refer to each other. At this point the compiler must be able to look them up somehow. But all it has in the source code is the fully qualified name of the referred class (which may not even have been compiled yet).
At runtime the story is similar: when a class needs to be loaded, its fully qualified name is the starting point. Now the class loader's job is to find a .class file (or an entry in a ZIP file, or any other imaginable source) based on the FQN alone, and again the simplest thing in a hierarchical file system is to translate the package name into a directory structure.
The only difference is that at runtime your "standalone" class too has to be loaded by the VM, therefore it needs to be looked up, therefore it should be in the correct folder structure (because that's how the bootstrap class loader works).

Subclass compilation in Java

I have read that Inheritance is a "compile-time' phenomenon. Also in a different place I have read that the superclass code is loaded by classloader, which I deduce happens at run-time. This is causing me some confusion regarding the nature of inheritance. Does the class file of sublass contain the actual compiled code of superclass, or is it accessed at run-time?
So consider you create a class that inherits a class that is included in a 3rd party jar file.
In order to compile your code you need to have the 3rd party jar file in the classpath of your compiler.
In order to run your code you will also need the jar file in the classpath of the java command that launches the application.
Your subclass does not contain the code of the superclass, it is in the jar files. Your compiled class contains a reference to the superclass. When your class is loaded by the classloader it searches the classpath for the superclass and loads it.
Where did you read it's compile time? I guess if you're compiling your subclass then yes, it needs to have a superclass to reference when being compiled.
But when you actually run the code it is dynamically linked as per:
http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html
"The Java Virtual Machine dynamically loads, links and initializes classes and interfaces"

Using classpaths

I plan on becoming a certified Java programmer and am studying from the Sierra-Bates book. I had a question about classpaths. Do classpaths need to find only the supporting classes of the class I'm running/compiling, or the supporting classes and the class itself? Also, when I'm getting classes in packages from classpaths, is it legal to just put the adress of the file(the path to it), instead of putting it's root package. Thanks.
1 - a classpath has to give access to each class that needs to run in your program. That would include the main class and any classes it calls and those they call. If there is some code in one of those classes that is never called, in many cases, you don't need to have the classes referenced by the uncalled code.
2 - you have to put the root of the packages in the classpath. So a class "com.bob.myprog.Main" would need to have the class path point to the folder where the "com" package/folder lies. It will need to contain a "bob" folder and "bob" will need to contain a "myprog" folder with "Main.class" in it.
Classpath has to contain both the supporting classes and the class itself.
However, sometimes you can run a single file without specifying classpath (and it will work).
As specified in http://docs.oracle.com/javase/tutorial/essential/environment/paths.html :
The default value of the class path is ".", meaning that only the
current directory is searched. Specifying either the CLASSPATH
variable or the -cp command line switch overrides this value.
Therefore, if you have a class MyClass compiled in the current directory, the following will work:
java MyClass
while pointing classpath to another directory will lead to an error (classpath no longer contains MyClass):
java -cp lib MyClass
When you have a class in a package, it is not enough to put the address to the class file in the classpath. According to SCJP Sun Certified Programmer for Java 5 Study Guide:
In order to find a class in a package, you have to have a directory in
your classpath that has the package's leftmost entry (the package's
"root") as a subdirectory.

Categories

Resources