So right now I have two classes, one of which creates an object out of another class:
import java.io.*;
public class PostfixConverter {
public static void main(String args[]) throws IOException, OperatorException {
...
String postfixLine;
while ((postfixLine = br.readLine()) != null) {
// write some gaurd clauses for edge cases
if (postfixLine.equals("")) {
...
Cpu cpu = new Cpu();
and
public class Cpu {
Cpu() {
// this linkedListStack is for processing the postfix
...
}
Currently I'm running javac PostfixConverter.java to compile the class but it cannot find the Cpu symbol. What can I do so that the compiler can discover the missing symbol? Shouldn't everything by default be packaged in the default package and therefore find each other?
javac PostfixConverter.java
This command should work if both files are located on the current directory, as the default classpath (-cp option) is the current directory (.).
You should compile both files, so that the Cpu class file will be available to PostfixConverter:
javac Cpu.java PostfixConverter.java
Keep in mind that in general it is not desirable to build an application where everything sits in the default package. Consider creating an appropriate package here. Also, you may want to use either an IDE and/or Maven, which would make the build process easier for you.
As the other answer mentions, Java will also automatically build all Java source files in the current directory, which might explain why other source files are also getting built.
Related
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
I use the class javax.tools.JavaCompiler (jdk6) to compile a source file, but the source file depends on some jar file. How to set the classpath of the javax.tools.JavaCompiler?
The javax.tools.JavaCompiler#getTask() method takes an options parameter that allows to set compiler options. The following message describes an easy way to set them in order to access the calling program's classpath:
You need to configure the standard
java file manager to know about the
jar files(s) - you use the compiler
options argument to do that.
By default the java compiler object
only seems to know about the default
locations for bootclasspath, extdirs
and endorseddirs directories in terms
of its classpath.
You need to add the calling program's
current classpath to the java compiler
instance's which gets passed on the
the standard file manager, which will
then find classes in the jar files.
Here's how I do it in the compiler
wrapper I wrote
List<String> optionList = new ArrayList<String>();
// set compiler's classpath to be same as the runtime's
optionList.addAll(Arrays.asList("-classpath",System.getProperty("java.class.path")));
// any other options you want
optionList.addAll(Arrays.asList(options));
JavaCompiler.CompilationTask task = compiler.getTask(out,jfm,diagnostics,optionList,null,jfos);
All you'll need then is to get the proper classpath set when running the calling program.
The same problem occurred to me recently, finally I found two workarounds. You can set the class path either by invoke StandardJavaFileManager.setLocation(StandardLocation.CLASS_PATH, "YOUR_CLASS_PATH") or Compiler.getTask(ARG_0, ARG_1, ARG_2, CLASS_PATH_OPTIONS, just as the first answer posted here says.
I needed something simpler than the examples above.
The following is a self-contained example of using the built-in Java compiler, and setting the classpath for the compiler to use.
It is equivalent to creating a source file called HelloPrinter.java and then compiling it as follows:
javac -classpath C:\Users\dab\Testing\a.jar;c:\path\etc org\abc\another\HelloPrinter.java
Note how the classpath can be set using a String[] of options. This should be familiar if you're already used to running javac on the command line (as above).
This code is compatible with Java 6. You will need a JDK, not a JRE, for this to run. This example doesn't actually use the classpath. It all does is print "Hello". You can add an import statement to the generated source and call a method in an external Jar file to test this properly.
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintStream;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
public class JavaCompilerExample {
public static void main(String[] args) throws Exception {
String className = "HelloPrinter";
String directoryName = "org/abc/another";
new File(directoryName).mkdirs();
FileOutputStream fos = new FileOutputStream(directoryName+"/"+className+".java");
PrintStream ps = new PrintStream(fos);
ps.println(
"package "+directoryName.replace("/", ".") + " ; "
+ "public class " +className +
"{ public static void main(String[] args){System.out.println(\"Hello\");} }");
ps.close();
JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
String javacOpts[] = {"-classpath",
"C:\\Users\\dab\\Testing\\a.jar;c:\\path\\etc;",
directoryName+"/"+className + ".java"};
if ( javac.run(null, null, null, javacOpts)!=0 ) {
System.err.println("Error");
System.exit(1);
}
}
}
I ran into library loading problems after creating a jar from my code via maven. I use intelliJ idea on Ubuntu. I broke the problem down to this situation:
Calling the following code from within idea it prints the path correctly.
package com.myproject;
public class Starter {
public static void main(String[] args) {
File classpathRoot = new File(Starter.class.getResource("/").getPath());
System.out.println(classpathRoot.getPath());
}
}
Output is:
/home/ted/java/myproject/target/classes
When I called mvn install and try to run it from command line using the following command I'm getting a NullPointerException since class.getResource() returns null:
cd /home/ted/java/myproject/target/
java -cp myproject-0.1-SNAPSHOT.jar com.myproject.Starter
same for calling:
cd /home/ted/java/myproject/target/
java -Djava.library.path=. -cp myproject-0.1-SNAPSHOT.jar com.myproject.Starter
It doesn't matter if I use class.getClassLoader().getRessource("") instead. Same problem when accessing single files inside of the target directory instead via class.getClassLoader().getRessource("file.txt").
I want to use this way to load native files in the same directory (not from inside the jar). What's wrong with my approach?
The classpath loading mechanism in the JVM is highly extensible, so it's often hard to guarantee a single method that would work in all cases. e.g. What works in your IDE may not work when running in a container because your IDE and your container probably have highly specialized class loaders with different requirements.
You could take a two tiered approach. If the method above fails, you could get the classpath from the system properties, and scan it for the jar file you're interested in and then extract the directory from that entry.
e.g.
public static void main(String[] args) {
File f = findJarLocation("jaxb-impl.jar");
System.out.println(f);
}
public static File findJarLocation(String entryName) {
String pathSep = System.getProperty("path.separator");
String[] pathEntries = System.getProperty("java.class.path").split(pathSep);
for(String entry : pathEntries) {
File f = new File(entry);
if(f.getName().equals(entryName)) {
return f.getParentFile();
}
}
return null;
}
I have this problem that i have a program that writes and creates a .java file and puts it in my package folder, after this it takes the information from the .java file and uses it in it self. (it creates a new class with a method that i then import).
The problem is that if it wont work until i with eclipse update the "self created file". is there a way to make my main file update the "self created file".
Sorry if this is a duplicate. I just couldn't find it any where.
my code:
package dk.Nicolai.Bonde;
import java.io.*;
public class main {
public String outputString ="Math.sqrt(25)" ;
static String outputPath ="src/output.txt";
/**
* #param args
* #throws UnsupportedEncodingException
* #throws FileNotFoundException
*/
public static void main(String[] args) throws IOException{
new main().doit(args);
}
public void doit(String[] args) throws IOException{
PrintWriter writer = new PrintWriter("src/dk/Nicolai/Bonde/calculate.java", "UTF-8");
writer.println("package dk.Nicolai.Bonde;");
writer.println("public class calculate{");
writer.println("public void calc(){");
writer.println("System.out.println("+outputString+");");
writer.println("}");
writer.println("}");
writer.flush();
writer.close();
calculate calcObj = new calculate();
calcObj.calc();
}
}
Your main mistake is that you expected that it's during runtime automagically compiled into a .class file after save (which a sane IDE such as Eclipse is doing automatically for you behind the scenes everytime you press Ctrl+S). This is thus not true. During runtime, you need to compile it yourself by JavaCompiler and then load by URLClassLoader. A concrete example is given in this related question&answer: How do I programmatically compile and instantiate a Java class?
You'll in the concrete example also notice that you can't do just a new calculate(); thereafter. The classpath won't be auto-refreshed during runtime or so. You'd need to do a Class#forName(), passing the FQN and the URLClassLoader. E.g.
Calculate calculate = (Calculate) Class.forName("com.example.Calculate", true, classLoader);
Your other mistake is that you're relying on the disk file system's current working directory always being the Eclipse project's source root folder. This is not robust. This folder is not present at all when building and distributing the application. You should instead write the file to a fixed/absolute folder elsewhere outside the IDE project's structure. This is also covered in the aforelinked answer.
No, you cannot. You have to manually update resources in Eclipse. Although you can write a plugin for Eclipse which runs your file and update resources.
Eclipse uses directories and files to store its resources but is not direct representation of file system.
Your code could not work, because
calculate is required at compile time of main. You supply it at runtime.
calculate.java will not compiled, so even other techniques to dynamically load classes will not work
If you want to build classes at runtime, consider to use the reflexion API
This is a followup to my own previous question and I'm kind of embarassed to ask this... But anyway: how would you start a second JVM from a standalone Java program in a system-independent way? And without relying on for instance an env variable like JAVA_HOME as that might point to a different JRE than the one that is currently running. I came up with the following code which actually works but feels just a little awkward:
public static void startSecondJVM() throws Exception {
String separator = System.getProperty("file.separator");
String classpath = System.getProperty("java.class.path");
String path = System.getProperty("java.home")
+ separator + "bin" + separator + "java";
ProcessBuilder processBuilder =
new ProcessBuilder(path, "-cp",
classpath,
AnotherClassWithMainMethod.class.getName());
Process process = processBuilder.start();
process.waitFor();
}
Also, the currently running JVM might have been started with some other parameters (-D, -X..., ...) that the second JVM would not know about.
I think that the answer is "Yes". This probably as good as you can do in Java using system independent code. But be aware that even this is only relatively system independent. For example, in some systems:
the JAVA_HOME variable may not have been set,
the command name used to launch a JVM might be different (e.g. if it is not a Sun JVM), or
the command line options might be different (e.g. if it is not a Sun JVM).
If I was aiming for maximum portability in launching a (second) JVM, I think I would do it using wrapper scripts.
It's not clear to me that you would always want to use exactly the same parameters, classpath or whatever (especially -X kind of stuff - for example, why would the child need the same heap settings as its parents) when starting a secondary process.
I would prefer to use an external configuration of some sort to define these properties for the children. It's a bit more work, but I think in the end you will need the flexibility.
To see the extent of possible configuration settings you might look at thye "Run Configurations" settings in Eclipse. Quite a few tabs worth of configuration there.
To find the java executable that your code is currently running under (i.e. the 'path' variable in your question's sample code) there is a utility method within apache ant that can help you. You don't have to build your code with ant - just use it as a library, for this one method.
It is:
org.apache.tools.ant.util.JavaEnvUtils.getJreExecutable("java")
It takes care of the sort of special cases with different JVM vendors that others have mentioned. (And looking at the source code for it, there are more special cases than I would have imagined.)
It's in ant.jar. ant is distributed under the Apache license so hopefully you can use it how you want without hassle.
Here's a way that determines the java executable which runs the current JVM using ProcessHandle.current().info().command().
The ProcessHandle API also should allow to get the arguments. This code uses them for the new JVM if available, only replacing the current class name with another sample class. (Finding the current main class inside the arguments gets harder if you don't know its name, but in this demo it's simply "this" class. And maybe you want to reuse the same JVM options or some of them, but not the program arguments.)
However, for me (openjdk version 11.0.2, Windows 10), the ProcessInfo.arguments() is empty, so the fallback else path gets executed.
package test;
import java.lang.ProcessBuilder.Redirect;
import java.lang.management.ManagementFactory;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TestStartJvm {
public static void main(String[] args) throws Exception {
ProcessHandle.Info currentProcessInfo = ProcessHandle.current().info();
List<String> newProcessCommandLine = new LinkedList<>();
newProcessCommandLine.add(currentProcessInfo.command().get());
Optional<String[]> currentProcessArgs = currentProcessInfo.arguments();
if (currentProcessArgs.isPresent()) { // I know about orElse, but sometimes isPresent + get is handy
for (String arg: currentProcessArgs.get()) {
newProcessCommandLine.add(TestStartJvm.class.getName().equals(arg) ? TargetMain.class.getName() : arg);
}
} else {
System.err.println("don't know all process arguments, falling back to passed args array");
newProcessCommandLine.add("-classpath");
newProcessCommandLine.add(ManagementFactory.getRuntimeMXBean().getClassPath());
newProcessCommandLine.add(TargetMain.class.getName());
newProcessCommandLine.addAll(List.of(args));
}
ProcessBuilder newProcessBuilder = new ProcessBuilder(newProcessCommandLine).redirectOutput(Redirect.INHERIT)
.redirectError(Redirect.INHERIT);
Process newProcess = newProcessBuilder.start();
System.out.format("%s: process %s started%n", TestStartJvm.class.getName(), newProcessBuilder.command());
System.out.format("process exited with status %s%n", newProcess.waitFor());
}
static class TargetMain {
public static void main(String[] args) {
System.out.format("in %s: PID %s, args: %s%n", TargetMain.class.getName(), ProcessHandle.current().pid(),
Stream.of(args).collect(Collectors.joining(", ")));
}
}
}
Before ProcessHandle was added in Java 9, I did something like this to query the current JVM's command-line:
Let the user pass or configure a "PID to command-line" command template; under Windows, this could be wmic process where 'processid=%s' get commandline /format:list.
Determine PID using java.lang.management.ManagementFactory.getRuntimeMXBean().getPid().
Expand command template; execute; parse its output.