java 上次编译的Java打印时间
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3336392/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java print time of last compilation
提问by zer0stimulus
I'm looking to embed a piece of code that will print out the time when the current class was last compiled. How can this be implemented in Java?
我希望嵌入一段代码,该代码将打印出上次编译当前类的时间。这如何在 Java 中实现?
采纳答案by mdma
There is no direct support for this in java, since there is no preprocessor. The closest equivalent is the "Build-Date" attribute in the JAR manifest. Many build systems add this attribute by default, or provide the means to add it.
在 java 中没有直接支持,因为没有预处理器。最接近的等效项是 JAR 清单中的“Build-Date”属性。许多构建系统默认添加此属性,或提供添加它的方法。
You can then read the manifest of the JAR at runtime to get the date. The answer to this SO questiondescribes how to read values from the JAR manifest.
然后,您可以在运行时读取 JAR 的清单以获取日期。这个SO 问题的答案描述了如何从 JAR 清单中读取值。
The alternative is to use resource filtering to add the date into a properties file, which is then read at runtime. This is quite ad-hoc, non-standard and if you have many jars, with different compilation times, then this quickly becomes difficult to manage, unless you can factor this into a common part of how all the jars are built.
另一种方法是使用资源过滤将日期添加到属性文件中,然后在运行时读取该文件。这是非常临时的、非标准的,如果你有很多 jars,编译时间不同,那么这很快就会变得难以管理,除非你能把它作为所有 jars 构建方式的公共部分。
回答by krico
This question has been answered a long time ago. But in case someone swings by here's a solution that works for me, similar to what Supah Fly suggested but supports jar and file.
这个问题很久以前就有答案了。但万一有人来这里,这里有一个对我有用的解决方案,类似于 Supah Fly 建议的,但支持 jar 和文件。
private long classBuildTimeMillis() throws URISyntaxException, IllegalStateException, IllegalArgumentException {
URL resource = getClass().getResource(getClass().getSimpleName() + ".class");
if (resource == null) {
throw new IllegalStateException("Failed to find class file for class: " +
getClass().getName());
}
if (resource.getProtocol().equals("file")) {
return new File(resource.toURI()).lastModified();
} else if (resource.getProtocol().equals("jar")) {
String path = resource.getPath();
return new File(path.substring(5, path.indexOf("!"))).lastModified();
} else {
throw new IllegalArgumentException("Unhandled url protocol: " +
resource.getProtocol() + " for class: " +
getClass().getName() + " resource: " + resource.toString());
}
}
But this won't handle zip files or a static context, and it throws exceptions instead of returning null if things go south. This is a bit more friendly:
但这不会处理 zip 文件或静态上下文,并且如果出现问题,它会抛出异常而不是返回 null。这更友好一点:
private static final Date buildDate = getClassBuildTime();
/**
* Handles files, jar entries, and deployed jar entries in a zip file (EAR).
* @return The date if it can be determined, or null if not.
*/
private static Date getClassBuildTime() {
Date d = null;
Class<?> currentClass = new Object() {}.getClass().getEnclosingClass();
URL resource = currentClass.getResource(currentClass.getSimpleName() + ".class");
if (resource != null) {
if (resource.getProtocol().equals("file")) {
try {
d = new Date(new File(resource.toURI()).lastModified());
} catch (URISyntaxException ignored) { }
} else if (resource.getProtocol().equals("jar")) {
String path = resource.getPath();
d = new Date( new File(path.substring(5, path.indexOf("!"))).lastModified() );
} else if (resource.getProtocol().equals("zip")) {
String path = resource.getPath();
File jarFileOnDisk = new File(path.substring(0, path.indexOf("!")));
//long jfodLastModifiedLong = jarFileOnDisk.lastModified ();
//Date jfodLasModifiedDate = new Date(jfodLastModifiedLong);
try(JarFile jf = new JarFile (jarFileOnDisk)) {
ZipEntry ze = jf.getEntry (path.substring(path.indexOf("!") + 2));//Skip the ! and the /
long zeTimeLong = ze.getTime ();
Date zeTimeDate = new Date(zeTimeLong);
d = zeTimeDate;
} catch (IOException|RuntimeException ignored) { }
}
}
return d;
}
回答by Yes Man
Since this was never mentioned, anyone looking to solve this problem by any means necessary, may find this as an appropriate, yet hacky, solution:
由于从未提及过这一点,因此任何希望通过任何必要方式解决此问题的人都可能会发现这是一个合适但又笨拙的解决方案:
new Date(new File(getClass().getClassLoader().getResource(getClass().getCanonicalName().replace('.', '/') + ".class").toURI()).lastModified()))
It may not be pretty, and it very well may not be compatible on other platforms, but this is the only way I've found to figure out the compile date of the current class in native Java.
它可能不漂亮,并且在其他平台上可能不兼容,但这是我发现的在本机 Java 中计算当前类的编译日期的唯一方法。
回答by Scott
It's a bit clunky, but you could do this with Ant filtering.
这有点笨拙,但您可以使用 Ant 过滤来做到这一点。
Pop the following method in your class:
在您的类中弹出以下方法:
public static String timeBuilt(){
return "Built at @timeBuilt@ on @dateBuilt@";
}
Then put the following in your Ant build file.
然后将以下内容放入您的 Ant 构建文件中。
<target name="get-time">
<tstamp>
<format property="buildTime" pattern="HH:mm:ss" locale="en,UK"/>
<format property="buildDate" pattern="dd-MM-yyyy" locale="en,UK"/>
</tstamp>
</target>
<target name="copy-files" depends="get-time">
<filter token="timeBuilt" value="${buildTime}"/>
<filter token="dateBuilt" value="${buildDate}"/>
<copy includeemptydirs="false" todir="build" filtering="true">
<fileset dir="src"/>
</copy>
</target>
This will copy everything in the "src" directory to "build" and in doing so will replace @timeBuilt@ and @dateBuilt@ with the time and date of the build, respectively. Simply make your build target depend on copy-files and build from the "build" directory - not the "src" directory.
这会将“src”目录中的所有内容复制到“build”,这样做将分别用构建的时间和日期替换@timeBuilt@ 和@dateBuilt@。简单地让你的构建目标依赖于复制文件并从“build”目录构建——而不是“src”目录。
The advantage of replacing the content of a static method is that this will operate on a per-class basis - if you were to take the class files produced and combine them with some other class files that were built at another time, they would each know when they were built. Property files are sensible, but unless you were to have multiple property files, you would only be able to have a build time for the package as a whole.
替换静态方法的内容的优点是这将在每个类的基础上进行操作 - 如果您将生成的类文件与其他一些在其他时间构建的类文件组合在一起,它们都会知道当它们被建造时。属性文件是合理的,但除非您有多个属性文件,否则您只能为整个包提供一个构建时间。
回答by Michael Spector
Create a shell script that updates class code with the compilation time by replacing special placeholders:
创建一个 shell 脚本,通过替换特殊占位符来根据编译时间更新类代码:
public final class CompilationInfo
{
public static final String TIME = "$COMPILE_TIME";
}
For more info, see this article.
有关详细信息,请参阅此文章。
回答by Mark Peters
Not knowing a standard way to do this, my suggestion is similar to spektom's link but would be to add a property file to your jar that is populated by your build script (Ant has a built-in task for generating a property file). Maybe put it at /buildinfo.properties. Then create a Java class that simply polls that property file at runtime.
不知道执行此操作的标准方法,我的建议类似于 spektom 的链接,但将向您的 jar 添加一个属性文件,该文件由您的构建脚本填充(Ant 具有用于生成属性文件的内置任务)。也许把它放在/buildinfo.properties。然后创建一个在运行时简单地轮询该属性文件的 Java 类。
In Ant, it might look something like this:
在 Ant 中,它可能看起来像这样:
...
<tstamp/>
...
<propertyfile file="${output.dir}/buildinfo.properties">
<entry key="build.date" value="${TSTAMP}"/>
</propertyfile>
And then corresponding Java
然后对应的Java
public Date getBuildDate() {
Properties buildProps = new Properties();
buildProps.load(getClass().getResourceAsStream("/buildinfo.properties"));
return someConversion(buildProps.getProperty("build.date"));
}
回答by Thorbj?rn Ravn Andersen
Let your build procedure create a property file containing the information you need, and then read the properties as a resource in your code
让您的构建过程创建一个包含您需要的信息的属性文件,然后在您的代码中将这些属性作为资源读取
回答by BullyWiiPlaza
Here is my class for detecting the build time of your Java program. It uses the code from thisanswer as well.
这是我用于检测 Java 程序构建时间的类。它也使用此答案中的代码。
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.attribute.FileTime;
import java.text.DateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Locale;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class BuildDate
{
private static Date buildDate;
static
{
try
{
buildDate = setBuildDate();
} catch (Exception exception)
{
exception.printStackTrace();
}
}
public static String getBuildDate()
{
int style = DateFormat.FULL;
Locale locale = Locale.getDefault();
DateFormat dateFormat = DateFormat.getDateInstance(style, locale);
DateFormat timeFormat = DateFormat.getTimeInstance(style, locale);
return dateFormat.format(buildDate) + " " + timeFormat.format(buildDate);
}
private static Date setBuildDate() throws Exception
{
if (ProgramDirectoryUtilities.runningFromIntelliJ())
{
return getClassBuildTime();
} else
{
return getNewestFileDate();
}
}
private static Date getNewestFileDate() throws Exception
{
String filePath = ProgramDirectoryUtilities.getJARFilePath();
File file = new File(filePath);
ZipFile zipFile = new ZipFile(file);
Enumeration entries = zipFile.entries();
long millis = -1;
while (entries.hasMoreElements())
{
ZipEntry entry = (ZipEntry) entries.nextElement();
if (!entry.isDirectory())
{
FileTime fileTime = entry.getLastModifiedTime();
long currentMillis = fileTime.toMillis();
if (millis < currentMillis)
{
millis = currentMillis;
}
}
}
return new Date(millis);
}
/**
* Handles files, jar entries, and deployed jar entries in a zip file (EAR).
*
* @return The date if it can be determined, or null if not.
*/
private static Date getClassBuildTime() throws IOException, URISyntaxException
{
Date date = null;
Class<?> currentClass = new Object()
{
}.getClass().getEnclosingClass();
URL resource = currentClass.getResource(currentClass.getSimpleName() + ".class");
if (resource != null)
{
switch (resource.getProtocol())
{
case "file":
date = new Date(new File(resource.toURI()).lastModified());
break;
case "jar":
{
String path = resource.getPath();
date = new Date(new File(path.substring(5, path.indexOf("!"))).lastModified());
break;
}
case "zip":
{
String path = resource.getPath();
File jarFileOnDisk = new File(path.substring(0, path.indexOf("!")));
try (JarFile jarFile = new JarFile(jarFileOnDisk))
{
ZipEntry zipEntry = jarFile.getEntry(path.substring(path.indexOf("!") + 2));//Skip the ! and the /
long zeTimeLong = zipEntry.getTime();
date = new Date(zeTimeLong);
}
break;
}
}
}
return date;
}
}
Utility class:
实用类:
import java.io.File;
import java.lang.invoke.MethodHandles;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class ProgramDirectoryUtilities
{
public static String getJARFilePath() throws URISyntaxException
{
return new File(MethodHandles.lookup().lookupClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getAbsolutePath();
}
public static boolean runningFromJAR()
{
try
{
String jarFilePath = new File(MethodHandles.lookup().lookupClass().getProtectionDomain()
.getCodeSource()
.getLocation()
.getPath()).
toString();
jarFilePath = URLDecoder.decode(jarFilePath, "UTF-8");
try (ZipFile zipFile = new ZipFile(jarFilePath))
{
ZipEntry zipEntry = zipFile.getEntry("META-INF/MANIFEST.MF");
return zipEntry != null;
}
} catch (Exception exception)
{
return false;
}
}
public static String getProgramDirectory()
{
if (runningFromJAR())
{
return getCurrentJARDirectory();
} else
{
return getCurrentProjectDirectory();
}
}
private static String getCurrentProjectDirectory()
{
return new File("").getAbsolutePath();
}
private static String getCurrentJARDirectory()
{
try
{
return new File(MethodHandles.lookup().lookupClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParent();
} catch (URISyntaxException exception)
{
exception.printStackTrace();
}
return null;
}
public static boolean runningFromIntelliJ()
{
String classPath = System.getProperty("java.class.path");
return classPath.contains("idea_rt.jar");
}
}
回答by kgw66
This is in my opinion the best solution. I'am using the export-function 'Runnable jar-file' of eclipse. This function generates the file "META-INF/MANIFEST.MF" which I'am using for determining the export-time. Those time tell's me when i had built the program. Under eclipse only the compile-time of the class of the parameter "obj" will be shown.
在我看来,这是最好的解决方案。我正在使用 eclipse 的导出功能“Runnable jar-file”。这个函数生成文件“META-INF/MANIFEST.MF”,我用它来确定导出时间。那些时间告诉我我何时构建了该程序。在 eclipse 下,只会显示参数“obj”的类的编译时间。
private static Date getDateOfJar(String path) throws IOException
{
Date ret=null;
JarFile jarFile = new JarFile(path);
Enumeration ent = jarFile.entries();
while (ent.hasMoreElements())
{
JarEntry entry = (JarEntry) ent.nextElement();
String name = entry.getName();
if (name.equals("META-INF/MANIFEST.MF"))
{
ret = new Date(entry.getTime());
break;
}
}
jarFile.close();
return ret;
}
public static String getClassBuildTime(Object obj)
{
String ret = "unknown";
try
{
Class<?> currentClass = obj.getClass().getEnclosingClass();
URL resource = currentClass.getResource(currentClass.getSimpleName() + ".class");
if (resource != null)
{
if (resource.getProtocol().equals("file"))
{
try
{
Date d = new Date(new File(resource.toURI()).lastModified());
ret = ""+d;
}
catch (URISyntaxException ignored)
{
}
}
else if (resource.getProtocol().equals("jar"))
{
String path = resource.getPath();
Date d=getDateOfJar(path.substring(5, path.indexOf("!")));
ret = ""+d;
}
}
}
catch (Exception e)
{
System.out.println("Error! FileLogger.getClassBuildTime() Exception e=" + e.getMessage());
e.printStackTrace();
}
return ret;
}
回答by Sergey Grigorchuk
Use @CompileTimeand create a field
使用@CompileTime并创建一个字段
@CompileTime
private static long COMPILE_TIME; // This field contains compilation time in ms

