Why does a Java class compile differently with a blank line? - java

I have the following Java class
public class HelloWorld {
public static void main(String []args) {
}
}
When I compile this file and run a sha256 on the resulting class file I get
9c8d09e27ea78319ddb85fcf4f8085aa7762b0ab36dc5ba5fd000dccb63960ff HelloWorld.class
Next I modified the class and added a blank line like this:
public class HelloWorld {
public static void main(String []args) {
}
}
Again I ran a sha256 on the output expecting to get the same result but instead I got
11f7ad3ad03eb9e0bb7bfa3b97bbe0f17d31194d8d92cc683cfbd7852e2d189f HelloWorld.class
I have read on this TutorialsPoint article that:
A line containing only white space, possibly with a comment, is known as a blank line, and Java totally ignores it.
So my question is, since Java ignores blank lines why is the compiled bytecode different for both programs?
Namely the difference in that in HelloWorld.class a 0x03 byte is replaced by a 0x04 byte.

Basically, line numbers are kept for debugging, so if you change your source code the way you did, your method starts at a different line and the compiled class reflects the difference.

You can see the change by using javap -v which will output verbose information. Like other already mentioned the difference will be in line numbers:
$ javap -v HelloWorld.class > with-line.txt
$ javap -v HelloWorld.class > no-line.txt
$ diff -C 1 no-line.txt with-line.txt
*** no-line.txt 2018-10-03 11:43:32.719400000 +0100
--- with-line.txt 2018-10-03 11:43:04.378500000 +0100
***************
*** 2,4 ****
Last modified 03-Oct-2018; size 373 bytes
! MD5 checksum 058baea07fb787bdd81c3fb3f9c586bc
Compiled from "HelloWorld.java"
--- 2,4 ----
Last modified 03-Oct-2018; size 373 bytes
! MD5 checksum 435dbce605c21f84dda48de1a76e961f
Compiled from "HelloWorld.java"
***************
*** 50,52 ****
LineNumberTable:
! line 3: 0
LocalVariableTable:
--- 50,52 ----
LineNumberTable:
! line 4: 0
LocalVariableTable:
More precisely the class file differs in the LineNumberTable section:
The LineNumberTable attribute is an optional variable-length attribute in the attributes table of a Code attribute (§4.7.3). It may be used by debuggers to determine which part of the code array corresponds to a given line number in the original source file.
If multiple LineNumberTable attributes are present in the attributes table of a Code attribute, then they may appear in any order.
There may be more than one LineNumberTable attribute per line of a source file in the attributes table of a Code attribute. That is, LineNumberTable attributes may together represent a given line of a source file, and need not be one-to-one with source lines.

The assumption that "Java ignores blank lines" is wrong. Here is a code snippet that behaves differently depending on the number of empty lines before the method main:
class NewlineDependent {
public static void main(String[] args) {
int i = Thread.currentThread().getStackTrace()[1].getLineNumber();
System.out.println((new String[]{"foo", "bar"})[((i % 2) + 2) % 2]);
}
}
If there are no empty lines before main, it prints "foo", but with one empty line before main, it prints "bar".
Since the runtime behavior is different, the .class files must be different, regardless of any timestamps or other metadata.
This holds for every language that has access to the stack frames with line numbers, not only for Java.
Note: if it's compiled with -g:none (without any debugging information), then the line numbers will not be included, getLineNumber() always returns -1, and the program always prints "bar", regardless of the number of line breaks.

As well as any line number details for debugging, your manifest may also store the build time and date. This will naturally be different every time you compile.

Related

Java program runs yet compilation fails

I wrote a Java program whose filename was (intentionally) different from the class I wrote inside the file. The javac command failed as expected on both CMD and WSL. The java command however worked and ran my print statement. I wrote the code intentionally this way so there is no way it was a previously compiled version of the code. The following code was written in a file called "explainJava.java" (notice the filename is different from the class name).
public class explain{
public static void main(String[] args) {
System.out.println("Java is weird");
}
}
I've had to google this myself, but I think I've found an explanation in this article.
According to that source as of Java 11 java is capable of compiling a single source file into memory.
What I conclude from that: When the file is compiled into memory and not written to disk it obviously cannot have a file name. If there is no filename there is no such thing as a wrong filename, therefore the code executes.
Please also note that the restriction of having to name a file like the public class within that file is more of a design decision to make work for the compiler easier/ faster. It is not a physical restriction so to speak. Have a look at the following thread for more details.
If you put this code:
public class explain {
public static void main(String[] args) {
System.out.println("Java is weird");
}
}
into a file named explainJava.java, and then compile it with this:
javac explainJava.java
you will get an error that correctly informs you that your filename ("explainJava") and the class defined inside that file ("explain") do not match:
explainJava.java:1: error: class explain is public, should be declared in a file named explain.java
public class explain{
^
1 error
If you run this command:
$ java explainJava.java
Java is weird
you see expected output, because you're skipping the explicit compilation step (that is, you aren't running javac first) and instead relying on behavior introduced in Java 11 that allows you to compile+run in a single step. Here's an explanation: Does the 'java' command compile Java programs?
So the answer is to either:
rename your file to match the class, so change the filename to "explain.java", or
rename the class to match the file, change public class explain to be public class explainJava

Java 14 text block leading \r\n inserted when used in Eclipse 4.15.0

I am in the process of learning how to cope with java 14 (preview) text blocks.
When using following text block in a Junit test I run across the following unexpected feature (simplified code example, in the real test I use an HTML fragment):
assertEquals("""
test""", "test");
Executing this test results in an error since "\r\ntest" does not match "test":
org.junit.ComparisonFailure: expected:<[
]test> but was:<[]test>
When I consult the documentation (https://docs.oracle.com/javase/specs/jls/se14/preview/specs/text-blocks-jls.html), it literally states:
The content of a text block is the sequence of characters that begins immediately after the line terminator of the opening delimiter, and ends immediately before the first double quote of the closing delimiter.
Did I miss something?
Update after several questions and suggestions:
I create a small test class:
public class Tester {
public static void main(String[] args) {
System.out.println(">>" + """
test""");
}
}
which, when executed, prints the following:
>>
test
The bytecode of this class is
Compiled from "Tester.java"
public class nl.paul.testapp.testutil.Tester {
public nl.paul.testapp.testutil.Tester();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #13 // String >>test
5: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
}
Eclipse 2020-03 (4.15) does not support Java 14. You have installed Java 14 Support for Eclipse 2020-03 (4.15) from the Eclipse Marketplace, which is the preview/beta version of the upcoming Java 14 support of Eclipse 2020-06 (4.16) which will be released on June 17th, 2020.
In the current release candidate, Eclipse 2020-06 (4.16) RC1, it works as expected. So it is a already fixed Eclipse Compiler for Java (ecj) bug in a build, not in a release (according to the Eclipse Development Process).
The Marketplace entry still refers to an outdated preview version and will probably be updated shortly after the release (as it happened with other new supported Java versions before).
Please note, text blocks are preview features of Java 13 (first preview) and Java 14 (second preview; improved) and therefore not yet part of a Java language specification. It is not intended to be used in production.
At last, the problem is clear. It is Eclipse related. When I compile and run the code from a command prompt (same java version as Eclipse uses), everything works as expected. When I compile and run the same code from within Eclipse, the text block is prepended with \r\n.
I will edit the question phrasing.
It looks you're doing something wrong. The JLS says:
The content of a text block is the sequence of characters that begins
immediately after the line terminator of the opening delimiter, and
ends immediately before the first double quote of the closing
delimiter.
JEP 378 (Text Blocks) confirms it:
The opening delimiter is a sequence of three double quote characters
(""") followed by zero or more white spaces followed by a line
terminator. The content begins at the first character after the line
terminator of the opening delimiter.
...
"""
line 1
line 2
line 3
"""
is equivalent to the string literal:
"line 1\nline 2\nline 3\n"
I also checked it on my machine:
System.out.println("""
test""".equals("test")); // Prints true

Java Classname with unicode character not Running

I'm Just trying to test Java Unicode support. I found that Java supports Unicode characters in their Class Names. But when I tried to use Unicode fonts It is not compiling. Below is the code
It Throws below error during Compilation
The character set of the File and Eclipse workspace is to UTF-8.
Update: Here is the Source. This has Unicode Tamil letters
public class தமிழ் {
private static String வணக்கம் = "வணக்கம்";
public static void main(String[] args) {
// TODO Auto-generated method stub
வணக்கம்சொல்();
}
private static void வணக்கம்சொல்() {
System.out.println(வணக்கம் + " வருக! வருக!!");
}
}
A quick demonstration about unicode characters in class names and the hassle on Windows.
Create following Java class file
Main.java
class Main {
public static void main(String...args) {
\u0ba4\u0bae\u0bbf\u0bb4\u0bcd.main(new String[0]);
}
}
class \u0ba4\u0bae\u0bbf\u0bb4\u0bcd {
public static void main(String[] arrstring) {
System.out.println("\u0bb5\u0ba3\u0b95\u0bcd\u0b95\u0bae\u0bcd unicode!");
}
}
All unicode characters are used with the unicode escape notation.
So actually following source would create the same class files
class Main {
public static void main(String...args) {
தமிழ்.main(new String[0]);
}
}
class தமிழ் {
public static void main(String[] args) {
System.out.println("வணக்கம் unicode!");
}
}
Compile the source (the one with the unicode escapes)
javac Main.java
this creates the class files Main.class and தமிழ்.class (you can check the file names e.g. with explorer . in the same directory)
in CMD console the unicode file name cannot be shown
> dir /b *.class
Main.class
?????.class
> java Main
??????? unicode!
in ConEmu the file name is displayed correctly
> dir /b *.class
Main.class
தமிழ்.class
> java Main
??????? unicode!
even the file name தமிழ்.class cannot be shown and accessed correctly in a CMD session, Java is able to execute the class. This means the class is stored correctly with the unicode characters. But the output is broken in both cases.
If you run the above code on a Linux machine the output will be as expected
$ java Main
வணக்கம் unicode!
edit the class with unicode characters can be executed on Linux directly
$ java தமிழ்
வணக்கம் unicode!
edit PowerShell ISE
PS > ls *.class
...
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 08/04/2018 12:34 317 Main.class
-a--- 08/04/2018 12:34 443 தமிழ்.class
PS > java Main
??????? unicode!
PS > java தமிழ்
java : Error: Could not find or load main class ?????
At line:1 char:1
+ java தமிழ்
edit Related to this bug report on Eclipse it seems it's working on Windows 10 (which I cannot verify, don't have one)
It is a matter of:
Unicode text normalisation: ĉ could be one Unicode code point (symbol) or two c and a combining diacritical mark ^ (zero-width). The operating system uses one of them. Ideally the IDE should enforce a canonical form. (No idea.)
Windows command line cmd.exe is restricted to its system encoding. However you could have a pure ASCII main class, calling the main of your class.
An executable jar file with an ASCII name should also pose no problem. The MANIFEST.MF is already in UTF-8, but as the line length should not exceed 72 bytes, and UTF-8 is multibyte per char, be careful.
Then there are version control systems that can make problems. Especially try switching between Windows and Linux.

Trimmomatic not acknowleding commands over Linux cluster

I am trying to use the program Trimmomatic to removed adapter sequences from an Illumina paired-end read over a computer cluster. While I can get the program to open, it will either not acknowledge the commands I enter or will return an error message. I have tried all kinds of permutations of the input commands without success. Examples of input code and error messages are below
Code:
java -classpath /*filepath*/Trimmomatic-0.32/trimmomatic-0.32.jar org.usadellab.trimmomatic.TrimmomaticPE \
-phred33 -trimlog /Results/log.txt \
~/*filepath*/data_R1.fq ~/*filepath*/data_R2.fq \
ILLUMINACLIP:/*filepath*/Trimmomatic-0.32/adapters/TruSeq3-PE-2.fa:2:30:10:3:"true"
Results: (the o/s seems to find and execute the software, but is not feeding in the command; I get the same result if I use the java -jar option for executing Trimmomatic)
TrimmomaticPE [-threads <threads>] [-phred33|-phred64] [-trimlog <trimLogFile>] [-basein <inputBase> | <inputFile1> <inputFile2>] [-baseout <outputBase> | <outputFile1P> <outputFile1U> <outputFile2P> <outputFile2U>] <trimmer1>...
Code: (If I add in the command PE immediately before all other commands, the program executes and can find the fasta file containing the adapter sequences, but then searches for and cannot fund a file called 'PE')
java -classpath /*filepath*/trimmomatic-0.32.jar org.usadellab.trimmomatic.TrimmomaticPE \
PE -phred33 -trimlog /Results/log.txt \
~/*filepath*/data_R1.fq ~/*filepath*/data_R2.fq \
ILLUMINACLIP:/*filepath*/Trimmomatic-0.32/adapters/TruSeq3-PE-2.fa:2:30:10:3:"true"
Results: (Programs rus and finds the fasta file of adapter sequences, but then fails to execute. Why is it looking for a PE file?)
TrimmomaticPE: Started with arguments: PE -phred33 -trimlog /Results/log.txt /*filepath*/data_R1.fq /*filepath*/data_R2.fq ILLUMINACLIP:/*filepath*/Trimmomatic-0.32/adapters/TruSeq3-PE-2.fa:2:30:10:3:true
Multiple cores found: Using 12 threads
Using PrefixPair: 'TACACTCTTTCCCTACACGACGCTCTTCCGATCT' and 'GTGACTGGAGTTCAGACGTGTGCTCTTCCGATCT'
Using Long Clipping Sequence: 'AGATCGGAAGAGCACACGTCTGAACTCCAGTCAC'
Using Long Clipping Sequence: 'TACACTCTTTCCCTACACGACGCTCTTCCGATCT'
Using Long Clipping Sequence: 'GTGACTGGAGTTCAGACGTGTGCTCTTCCGATCT'
Using Long Clipping Sequence: 'AGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGTA'
ILLUMINACLIP: Using 1 prefix pairs, 4 forward/reverse sequences, 0 forward only sequences, 0 reverse only sequences
Exception in thread "main" java.io.FileNotFoundException: PE (No such file or directory)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:146)
at org.usadellab.trimmomatic.fastq.FastqParser.parse(FastqParser.java:127)
at org.usadellab.trimmomatic.TrimmomaticPE.process(TrimmomaticPE.java:251)
at org.usadellab.trimmomatic.TrimmomaticPE.run(TrimmomaticPE.java:498)
at org.usadellab.trimmomatic.TrimmomaticPE.main(TrimmomaticPE.java:506)
I've never used trimmomatic but it looks like you are passing in the incorrect parameters.
the trimmomatic webpage lists the usage from version 0.27+ as:
java -jar <path to trimmomatic.jar> PE [-threads <threads] [-phred33 | -phred64] [-trimlog <logFile>] <input 1> <input 2> <paired output 1> <unpaired output 1> <paired output 2> <unpaired output 2> <step 1> ...
or using the "old way"
java -classpath <path to trimmomatic jar> org.usadellab.trimmomatic.TrimmomaticPE [-threads <threads>] [-phred33 | -phred64] [-trimlog <logFile>] <input 1> <input 2> <paired output 1> <unpaired output 1> <paired output 2> <unpaired output 2> <step 1> ...
Where the only difference is the new way is specifying "PE" as the main class instead of a fully qualified path.
First, addressing your 2nd problem:
You look like you are doing both: specifying a fully qualified class name as well as the PE part. This makes trimmomatic think you have a fastq file named "PE" which doesn't exist.
If you get rid of the "PE" OR the qualfited class name; it will call the correct class. Which is what you do first in your first problem.
1st problem
I don't think you have the correct number of arguments listed in your first invocation so trimmomatic displays the usage to tell you what parameters are required. It would be nice if it told you what was wrong but it doesn't.
Solution
It looks like you are only providing 2 fastq files but trimmmoatic needs 6 file paths. You are missing the output paired and unpaired files paths for the read 1 and read 2 data which I assume get created by the program when it runs.
I guess your 2nd attempt got further along in the program since it saw enough parameters that you potentially had enough file paths specified (however, it turns out you had optional step parameters)
Following the advice of dkatzel below and user blakeoft on SeqAnswers (http://seqanswers.com/forums/showthread.php?t=45094), I dropped the PE flag and added individual file names for each output file and the program executed properly.
java -classpath /*filepath*/Trimmomatic-0.32/trimmomatic-0.32.jar org.usadellab.trimmomatic.TrimmomaticPE \
-phred33 \
~/refs/lec12/data_R1.fq ~/refs/lec12/data_R2.fq \
lane1_forward_paired.fq lane1_forward_unpaired.fq lane1_reverse_paired.fq lane1_reverse_unpaired.fq \
ILLUMINACLIP:/*filepath*/Trimmomatic-0.32/adapters/TruSeq3-PE-2.fa:2:30:10:3:true
NB: I also tried using the -baseout flag rather than a list of four files, and the program would open but not execute any commands
NB: The a log file could be generated using the flag -trimlog filename, but only if I first made a blank text file with the same name as the intended log file.

How to get javac flags from a compiled class

Is it possible to get javac flags that was set when a class was compiled? I mean I have a 3rd party library and I can't debug its class in Eclipse because line: not available.
I would like to see what flags was used when the library was compiled.
Here is a similar problem I have. Its author writes:
Looking at javac compile flags, I see the -g:none flag.
So is it possible to see those flags?
You can do it with javap util. Run it with -v flag. If src was compiled with -g:source option javap will print file path in the first line of the output
Classfile /D:/workspace/x/target/classes/test/Test2.class
...
if -g:vars was used then you will see var names in LocalVariableTable
LocalVariableTable:
Start Length Slot Name Signature
0 31 0 args [Ljava/lang/String;
...
if -g:lines was used then javap will print LineNumberTable
LineNumberTable:
line 12: 0
...
You can also read these things programmatically with eg BCEL:
JavaClass jc = Repository.lookupClass(Test2.class);
String sf = jc.getSourceFileName();
Method m = jc.getMethods()[0];
LineNumberTable ln = m.getLineNumberTable();
LocalVariableTable lvt = m.getLocalVariableTable();

Categories