Reputation: 5620
I wanted to create my own basic console log for a small Java utility program I'm developing. Everything is working great in basically real-time; however, there is a small piece of code that is bothering me. I shall give a brief rundown of all the code below.
NOTE: Almost all the code here is untested/unoptimized. If there is anything wrong that I am doing, please let me know, I'm very open to criticism.
Static variables used for logging purposes:
static ArrayList<String> logs = new ArrayList<>();
static int offset = 0;
static DateFormat dateFormat = new SimpleDateFormat("yyy/MM/dd HH:mm:ss");
static Date date = new Date();
Code that populates the JTextArea
- This appends the log messages and keeps in mind the offset (so I don't iterate through the whole ArrayList for something that has already been logged).
public static void populateTextArea(final JTextArea textArea) {
for(int i = offset; i < logs.size()-1; i++) {
textArea.append(dateFormat.format(date) + ": " + logs.get(i)+"\n");
}
offset = logs.size()-1;
}
The following automatically updates the console every 150ms:
public static void timerLogging(final JTextArea textArea) {
Timer timer = new Timer(150, new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e)
{
updateDateAndTime();
populateTextArea(textArea);
}
});
timer.start();
}
Now the following part is what bothers me:
public static void updateDateAndTime() {
date = new Date();
}
Is there a way to simply do something such as date = GlobalDate.getCurrentDate();
? With the current way it's set up, won't it create a small overhead (probably not even noticeable however I'm not a 'just get it to work' kind of guy)?
Upvotes: 1
Views: 2839
Reputation: 338835
As noted by the other two Answers, generally you should not worry about the overhead of creating objects in Java. At least the implementation provided by Oracle is highly optimized for creation and destruction of short-lived objects. Several per second is not a problem.
While coding a long-running tight loop, you might briefly consider re-using objects. But only devote yourself to reducing instantiation when you've proven a performance problem. Otherwise you fall into the trap of premature optimization.
The ISO 8601 standard defines sensible formats for textual representations fo date-time values. The YYYY-MM-DDTHH:MM:SS.SSSZ
format makes sense for logging.
Almost always better to work with date-time values in the UTC time zone, then adjust to a local time zone for presentation to the user. This means your business logic, your database storage, and your logging should be done in UTC.
The java.util.Date and .Calendar classes bundled with Java are notoriously troublesome, confusing, and flawed. Avoid them.
Instead use either the Joda-Time library or the new java.time package built into Java 8 (inspired by Joda-Time).
Both Joda-Time and java.time use ISO 8601 as their defaults when generating or parsing strings. The java.time classes extend ISO 8601 by appending the name of the time zone, in addition to the usual offset number.
Example code using Joda-Time 2.7.
DateTime nowUtc = DateTime.now( DateTimeZone.UTC ) ;
Example output.
2013-08-21T00:16:26.941+09:00
For presentation to user, assign another time zone.
DateTime nowQuébec = nowUtc.withZone( DateTimeZone.forID( "America/Montreal" ) ) ;
Example code using java.time in Java 8.
ZonedDateTime nowUtc = ZonedDateTime.now( ZoneOffset.UTC );
Example output.
2013-08-21T00:16:26.941+09:00[Asia/Tokyo]
Adjusting the time zone.
ZoneId zone = ZoneId.of("America/Montreal");
ZonedDateTime nowQuébec = ZonedDateTime.of( nowUtc , zone ) ;
Creating several DateTime
instances per second is not a problem.
To quote from the FAQ entry, How well does it perform?…
Joda-Time is designed for performance. Compared to java.util.Calendar, java.text.SimpleDateFormat, and java.util.TimeZone, nearly all equivalent operations in Joda-Time are faster. The significant exceptions are operations to get or set an individual field.
Calling "get" on java.util.Calendar is very fast because it doesn't do any work. Calendar calculates all fields in advance, even if many of those fields you won't need. Calendar's set method is fast because it defers calculations until later. Calling Calendar.get after calling Calendar.set forces all the field values to be re-calculated. Calling Joda's DateTime.get method after calling DateTime.set only performs the minimum amount of calculations, and the pair is faster than Calendar.
Joda-Time also allocates very few temporary objects during operations, and performs almost no thread synchronization. In systems that are heavily multi-threaded or use a lot of memory, Calendar, SimpleDateFormat, and TimeZone can become bottlenecks. When the Joda-Time classes are used instead, the bottlenecks go away.
You may want to replace your use of Timer
with an Executor
, specifically a ScheduledExecutorService
. Timer is largely outmoded by Executor. Especially in Servlet/Java EE web apps, you should never use Timer. Search StackOverflow and Google for more information.
If your Question is truly regarding logging, I suggest learning to use one of the several good logging frameworks available in Java.
First step is to get slf4j. This project is only interfaces, an API abstraction.
Because there are so many logging frameworks in Java, you may encounter different ones in different projects, or you may want to switch frameworks in your own projects. The catch is that logging means you have many calls sprinkled throughout your codebase. So learning or changing frameworks is troublesome. The slf4j project aims to solve this problem by providing a set of interfaces you call throughout your code. Behind the scenes as adapter intervenes to translate those slf4j calls into calls for your specific logging framework. Adapters exist for log4j, Java’s java.util.logging, and more.
Actually, slf4j also comes bundled with a very simple implementation of the interfaces. This implementation is meant only for development (to get you started), or for use in very simple projects. By default that implementation logs only a few of the log levels, but you can adjust that. You can start with this simple imlementation and later switch to Logback; that is the entire point to slf4j, enabling you to switch logging frameworks.
Furthermore, the creator of slf4j also created Logback. Logback is a full-featured logging framework that directly implements the interfaces of slf4j. Being a direct implementation of the interfaces means no adapter is needed.
By the way, the creator of slf4j and Logback, Ceki Gülcü, is also the creator of the famous log4j. He took his vast experience in logging to create a successor, slf4j & Logback.
One striking flaw in Logback is its lack of support for ISO 8601 formatting of the date-times (as mentioned above). You can adjust that, per this StackOverflow Question.
For new projects I recommend starting with slf4j + Logback. For existing projects using another logging framework, keep that logging framework in place while making calls through slf4j via an adapter. Someday in the future, after all the logging calls have been replaced with slf4j calls, you have the option of replacing your logging framework with Logback if your needs warrant.
Upvotes: 1
Reputation: 18865
The first place to check any time you see Java and Date in the same sentence is Joda Time.
java.lang.Date
is mutable, so you could keep the same date object and change the date it represent:
date.setTime(System.currentTimeMillis());
The fact that java.lang.Date
is mutable is usually considered a mistake in the way the Java API was designed. Today and Tomorrow are not the same date, they should not be the same object.
Both Joda-Time and the new java.time package in Java 8 largely use immutable objects.
Upvotes: 2
Reputation: 726669
There is no "overhead" to speak about: creating one additional object seven times a second does not count as an overhead.
However, if you would like to keep the same Date
object, you can re-initialize it in the same way the default constructor of Date
uses:
public static void updateDateAndTime() {
date.setTime(System.currentTimeMillis());
}
Upvotes: 4