How are multiple Java files compiled together?

J

Jason Kim

I am new to Java.

Here are two files.

VolcanoApplication.java
class VolcanoApplication {
public static void main(String[] arguments) {
VolcanoRobot dante = new VolcanoRobot();
dante.status = "exploring";
dante.speed = 2;
dante.temperature = 510;

dante.showAttributes();
System.out.println("Increasing speed to 3.");
dante.speed = 3;
dante.showAttributes();
System.out.println("Changing temperature to 670");
dante.temperature = 670;
dante.showAttributes();
System.out.println("Checking the temperature.");
dante.checkTemperatur();
dante.showAttributes();
}
}

VolcanoRobot.java
class VolcanoRobot {
String status;
int speed;
float temperature;

void checkTemperatur() {
if (temperature > 50) {
status = "returning home";
speed = 5;
}
}

void showAttributes() {
System.out.println("Status: " + status);
System.out.println("Speed: " + speed);
System.out.println("Temperature: " + temperature);
}
}

I compiled the program by doing
$ javac VolcanoApplication.java

But how does Java know that VolcanoRobot.java should also be in the compilation?
 
K

Knute Johnson

I am new to Java.

Here are two files.

VolcanoApplication.java
class VolcanoApplication {
public static void main(String[] arguments) {
VolcanoRobot dante = new VolcanoRobot();
dante.status = "exploring";
dante.speed = 2;
dante.temperature = 510;

dante.showAttributes();
System.out.println("Increasing speed to 3.");
dante.speed = 3;
dante.showAttributes();
System.out.println("Changing temperature to 670");
dante.temperature = 670;
dante.showAttributes();
System.out.println("Checking the temperature.");
dante.checkTemperatur();
dante.showAttributes();
}
}

VolcanoRobot.java
class VolcanoRobot {
String status;
int speed;
float temperature;

void checkTemperatur() {
if (temperature> 50) {
status = "returning home";
speed = 5;
}
}

void showAttributes() {
System.out.println("Status: " + status);
System.out.println("Speed: " + speed);
System.out.println("Temperature: " + temperature);
}
}

I compiled the program by doing
$ javac VolcanoApplication.java

But how does Java know that VolcanoRobot.java should also be in the compilation?

If it didn't know, think about what a pain that would be every time you
had to compile a program with a lot of files. You reference
VolcanoRobot in VolcanoApplication, the compiler looks for that that
class in the classpath and compiles it if necessary.

That feature can be the source of a very tricky problem and that is if
there is a VolcanoRobot.class file in the classpath the compiler won't
compile VolcanoRobot.java even if it has changed since the
VolcanoRobot.class file was created.
 
J

Jason Kim

I am new to Java.

Here are two files.

VolcanoApplication.java
class VolcanoApplication {
public static void main(String[] arguments) {
VolcanoRobot dante = new VolcanoRobot();
dante.status = "exploring";
dante.speed = 2;
dante.temperature = 510;

dante.showAttributes();
System.out.println("Increasing speed to 3.");
dante.speed = 3;
dante.showAttributes();
System.out.println("Changing temperature to 670");
dante.temperature = 670;
dante.showAttributes();
System.out.println("Checking the temperature.");
dante.checkTemperatur();
dante.showAttributes();
}
}

VolcanoRobot.java
class VolcanoRobot {
String status;
int speed;
float temperature;

void checkTemperatur() {
if (temperature> 50) {
status = "returning home";
speed = 5;
}
}

void showAttributes() {
System.out.println("Status: " + status);
System.out.println("Speed: " + speed);
System.out.println("Temperature: " + temperature);
}
}

I compiled the program by doing
$ javac VolcanoApplication.java

But how does Java know that VolcanoRobot.java should also be in the compilation?

If it didn't know, think about what a pain that would be every time you
had to compile a program with a lot of files. You reference
VolcanoRobot in VolcanoApplication, the compiler looks for that that
class in the classpath and compiles it if necessary.

That feature can be the source of a very tricky problem and that is if
there is a VolcanoRobot.class file in the classpath the compiler won't
compile VolcanoRobot.java even if it has changed since the
VolcanoRobot.class file was created.

I didn't know Java compiler could be that smart.
As for the potential problem you mentioned, I don't think I'll have to worry about it any time soon. I won't be programming anything crazy complicated for some time.
Thanks Knute.
 
R

Roedy Green

But how does Java know that VolcanoRobot.java should also be in the compilation?

1. say javac *.java

2. compiler sees you calling some method and it can't find the class
file for it. So it looks on the sourcepath for the *.java file to
compile.

3. It can compare the dates on the class file and corresponding *.java
file. If the *.java file is newer it knows to recompile.
see http://mindprod.com/jgloss/javac.html

4. Use ANT. See http://mindprod.com/jgloss/ant.html
--
Roedy Green Canadian Mind Products
http://mindprod.com
I would be quite surprised if the NSA (National Security Agency)
did not have a computer program to scan bits of shredded
documents and electronically put them back together like a giant
jigsaw puzzle. This suggests you cannot just shred, you must also burn.
..
 
R

Roedy Green

3. It can compare the dates on the class file and corresponding *.java
file. If the *.java file is newer it knows to recompile.
see http://mindprod.com/jgloss/javac.html

This gives me trouble all the time. Javac can recompile code with
target 1.7 that was intended to be compiled with 1.6 if it gets
recompiled as a side effect of compilng something with 1.7.

I wrote a utility http://mindprod.com/products1.html#JARCHECK to make
sure everything is the version it is supposed to be. If it is out of
whack, I manually clean compile it. If it is really screwed up, I do
a clean rebuild of everything.
--
Roedy Green Canadian Mind Products
http://mindprod.com
I would be quite surprised if the NSA (National Security Agency)
did not have a computer program to scan bits of shredded
documents and electronically put them back together like a giant
jigsaw puzzle. This suggests you cannot just shred, you must also burn.
..
 
L

Lew

Get used to declaring top-level classes 'public':

public class VolcanoApplication

Once in a while you don't, but really almost never.

(The top-level class is the one whose name matches the file name.)
public static void main(String[] arguments) {
VolcanoRobot dante = new VolcanoRobot();

As you have learned, the "javac" compiler is smart enough to recognize the
reference to 'VolcanoRobot' and hunt down the source or class.

You absolutely do have to give the compiler some help. This help comes in two
forms: "sourcepath" and "classpath". (The JVM runner, "java", also knows about
"classpath".)

In your case, without any other specification, both paths by default are ".",
that means the current directory.

So when the compiler hunts the classpath, it looks in the current directory.
The first time it does not find "VolcanoRobot.class" in the classpath, so it
hunts for "VolcanoRobot.java" in the sourcepath. In your case, it found it
there, compiled it, then used the resulting "VolcanoRobot.class" in the classpath.

Next time you compile, if you have made no source changes that are newer than
the class files, it will skip the step.

If you compile just 'VolcanoApplication', and there are no newer changes to
'VolcanoRobot' the compiler will find the class file and be happy. If the
source is newer, the compiler will recompile "VolcanoRobot.java", then find
the class file and be happy.

....
That is not always correct, only if the compiler can't find the source or if
the change is limited to compile-time constants.
"Note: Classes found through the classpath may be subject to automatic
recompilation if their sources are also found. See Searching For Types."
I didn't know Java compiler could be that smart.
As for the potential problem you mentioned, I don't think I'll have to worry
about it any time soon. I won't be programming anything crazy complicated for some time.

Never give yourself such an excuse to defer learning.

How do you know, as a self-admitted beginner, what you do and do not need to
learn?

As it happens, understanding how the compiler and JVM invoker work are among
the very first and most important things you should learn. It isn't about
whether what you do will be "crazy complicated", or even slightly complicated,
or even dog simple. It's about whether you can do anything at all.

<http://docs.oracle.com/javase/6/docs/technotes/tools/>

P.S., Don't quote sigs.
 
K

Knute Johnson

That is not always correct, only if the compiler can't find the source
or if the change is limited to compile-time constants.
"Note: Classes found through the classpath may be subject to automatic
recompilation if their sources are also found. See Searching For Types."
<http://docs.oracle.com/javase/6/docs/technotes/tools/solaris/javac.html>

Lew:

Did I mis-remember that or has it changed since version 4 or there
abouts? I seem to remember a long thread here on this subject and I've
always erased all my .class files before every compilation to avoid it.
Have I generated my own myth?
 
R

Roedy Green

3. It can compare the dates on the class file and corresponding *.java
file. If the *.java file is newer it knows to recompile.
see http://mindprod.com/jgloss/javac.html

There is a gotcha.
If have a static final constant X in class A.

Class B references X.

You modify the value of X.

The compeller will be smart enough to recompile A, but not B. It
should recompile B, because of constant inlining.

When you update non-private constant values, do a clean compile of
everything to get the inlined constant values propagated.

--
Roedy Green Canadian Mind Products
http://mindprod.com
Controlling complexity is the essence of computer programming.
~ Brian W. Kernighan 1942-01-01
..
 
G

Gene Wirchenko

On Sun, 27 May 2012 21:08:29 -0700, Knute Johnson

[snip]
Did I mis-remember that or has it changed since version 4 or there
abouts? I seem to remember a long thread here on this subject and I've
always erased all my .class files before every compilation to avoid it.
Have I generated my own myth?

Maybe, but I can believe you had a difficulty that needed clean
compiles.

I had a problem with jCreator where I was using code from a
textbook on an assignment. (Many of the exercises used textbook
source code.) I had to compile the textbook source code, but then
subsequent compilations would fail claiming that the textbook source
code was not valid Java code! The solution^Whorrible kludge was to
the delete the textbook source code used after the first compilation.

When things like this happen, I am not surprised that people get
shy. I prefer clean, full compiles myself.

Sincerely,

Gene Wirchenko
 
L

Lew

Gene said:
Knute Johnson wrote:
[snip]
Did I mis-remember that or has it changed since version 4 or there
abouts? I seem to remember a long thread here on this subject and I've
always erased all my .class files before every compilation to avoid it.
Have I generated my own myth?

I live in the now. I don't know what you remember, nor how well, nor what changed.
Maybe, but I can believe you had a difficulty that needed clean
compiles.
+1

I had a problem with jCreator where I was using code from a
textbook on an assignment. (Many of the exercises used textbook
source code.) I had to compile the textbook source code, but then
subsequent compilations would fail claiming that the textbook source
code was not valid Java code! The solution^Whorrible kludge was to
the delete the textbook source code used after the first compilation.

Or put it in it own JAR.

Or figure out why it went wrong and solve it from understanding.
When things like this happen, I am not surprised that people get
shy. I prefer clean, full compiles myself.

The Java compiler does the best it can to resolve dependencies. It can't cover
a lot of scenarios, and it can really lose it over sometimes having source in
the sourcepath and sometimes not. It treats constants (as the JLS defines
them) differently from other immutable or mutable expressions.

Clean compiles are fine for your non-library (not in JARs) code and sometimes
necessary. We're not supposed to behave by superstition, but clean compile is
a prevalent and fairly harmless one.

Better protection is to organize your code into JARs, where you never compile
upstream JARs except when you work on their projects explicitly.

I espouse full knowledge of the rules, but I struggle to stay up on the corner
cases myself. I keep returning to study them, and discussions like this one
help to elucidate them.

For dependencies beyond what javac can handle, we have Ant. That why we have Ant.

I am jaundiced by project after project after project where I've stepped in
the build mess left by predecessors who created build systems by cult ritual.
One of the worst was a "refactoring" of the build consigned to a contract
company that claimed great expertise and tangled the ball of yarn even
further. (That one used Maven, which complicated the repair process.)

Build and deployment procedures require even more professionalism, diligence
and attention to detail than program code.
 
L

Lew

Roedy said:
There is a gotcha.
If have a static final constant X in class A.

I think it applies to instance and local constants also.
Class B references X.

You modify the value of X.

The compeller will be smart enough to recompile A, but not B. It
should recompile B, because of constant inlining.

"Should"?

What do you mean by that?

The constant, by virtue of it being a constant, is in the B class as a
constant. It is not a reference because it's a constant. Class B has no way of
knowing that the constant once had a label in another class. Because it's a
constant.

That's the way it is. I do not express an opinion on how it should be.

The JLS does explain why that is, though.
When you update non-private constant values, do a clean compile of
everything to get the inlined constant values propagated.

We create non-private constants, but we really aren't supposed to.
"Other than for true mathematical constants, we recommend that source code
make very sparing use of class variables that are declared static and final.
If the read-only nature of final is required, a better choice is to declare a
private static variable and a suitable accessor method to get its value."
<http://docs.oracle.com/javase/specs/jls/se7/html/jls-13.html#jls-13.4.9>

BTW, public, don't confuse constants with "static final" variables. Only some
of the latter are the former, and some of the former are not the latter. The
JLS doesn't restrict the definition of "constant variable" to static
variables, or even member variables.
<http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.4>
"A variable of primitive type or type _String_, that is _final_ and
initialized with a compile-time constant expression (§15.28), is called a
/constant variable/."

One of the implications:
"One other thing to note is that static final fields that have constant values
(whether of primitive or String type) must never appear to have the default
initial value for their type (§4.12.5). This means that all such fields appear
to be initialized first during class initialization (§8.3.2.1, §9.3.1, §12.4.2)."
[op. cit.]
 
G

Gene Wirchenko

Gene said:
Knute Johnson wrote:
[snip]
Did I mis-remember that or has it changed since version 4 or there
abouts? I seem to remember a long thread here on this subject and I've
always erased all my .class files before every compilation to avoid it.
Have I generated my own myth?

I live in the now. I don't know what you remember, nor how well, nor what changed.
Maybe, but I can believe you had a difficulty that needed clean
compiles.
+1

I had a problem with jCreator where I was using code from a
textbook on an assignment. (Many of the exercises used textbook
source code.) I had to compile the textbook source code, but then
subsequent compilations would fail claiming that the textbook source
code was not valid Java code! The solution^Whorrible kludge was to
the delete the textbook source code used after the first compilation.

Or put it in it own JAR.

Or figure out why it went wrong and solve it from understanding.

Easy to say. Not necessarily easy to do.

The error message was quite misleading. The solution was by
accident. There was nothing that I could see that would help me in
tracking down the cause of the problem.

While I do like solving problems, sometimes, there is no clean
solution.

This still fits.
The Java compiler does the best it can to resolve dependencies. It can't cover
a lot of scenarios, and it can really lose it over sometimes having source in
the sourcepath and sometimes not. It treats constants (as the JLS defines
them) differently from other immutable or mutable expressions.

Well, it sure seems to have done that here.
Clean compiles are fine for your non-library (not in JARs) code and sometimes
necessary. We're not supposed to behave by superstition, but clean compile is
a prevalent and fairly harmless one.

It does remove the problem of loss of synchronisation.

[snip]

Sincerely,

Gene Wirchenko
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top