Thứ Hai, 11 tháng 10, 2010

IBM and Oracle Are Behind OpenJDK!

Years from now, when we all bore our grandchildren with stories of the history of the Java programming language, the date of 11 October 2010 may be considered a landmark date in Java's history. For on that date, the two biggest players in all of Javadom agreed to collaborate on OpenJDK. In this post, I briefly look at the long and winding road that got us here (see The Java History Timeline for additional details).

1991 Project Green

1992 Programming language is called "Oak" (sadly also marked Johnny Carson's exit from late night television's The Tonight Show)

1995 "Oak" renamed to "Java"

1996 First JavaOne Conference and JDK 1.0 (Java Development Kit) released

1997 JDK 1.1 released

1998 Java Community Process (JCP) formalized and JDK 1.2 released

2000 Apple commits to Java support at 2000 JavaOne and JDK 1.3 (Kestrel) released
 
2002 JDK 1.4.0 (Merlin) and JDK 1.4.1 (Hopper) released

2003 JDK 1.4.2 (Mantis) released

2004 J2SE 5 (Tiger/1.5.0) released along with version naming change

2005 Apache Harmony announced as an open source and free Java implementation

2006 Java SE 6 (1.6.0) released and Sun begins open sourcing Java

2007 OpenJDK comes to life

2009 Oracle purchases Sun (and Java) after IBM attempt to purchase Sun falls through

2010 Oracle sues Google over Android platform, Google boycotts JavaOne 2010, and IBM and Oracle announce IBM's commitment to collaborating on OpenJDK development


Today's announcement that IBM will collaborate on OpenJDK comes at an important time for the Java community. For years, Java developers wondered if Sun would ever open source Java. Recently, some have questioned Oracle's commitment to open source. Today's announcement may mean the most support for OpenJDK since its announcement with two major and well-funded sponsors in Oracle and IBM. It also appears to solidify Oracle's commitment to an open source Java. It is certainly in IBM's interest to have an open source Java, but it also can aid Oracle in terms of credibility in the Java community. OpenJDK offers one of those proverbial "win-win" situations for these two companies, but should benefit the rest of us "little people" as well.

The timing of this announcement was important. As the press release states, this announcement may reassure individuals and organizations who have invested significantly in Java-based applications. It may similarly reassure potential future customers. In addition, this announcement can be interpreted as a sign of how Oracle intends to fulfill its commitment to an open source Java. This is certainly a major announcement that should please most Java developers.

With an open source version of Java supported by the two largest players in the Java space available now, there will be some questions about the future of Apache Harmony. Choice is always preferable, but that doesn't necessarily mean the demand will be great enough to justify the effort. Indeed, Bob Sutor's post IBM Joins the OpenJDK Community, will help unify open source Java efforts states the following:
IBM will work with Oracle and the Java community to make OpenJDK the primary high performance open source runtime for Java. IBM will be shifting its development effort from the Apache Project Harmony to OpenJDK. ... We think this is the pragmatic choice. It became clear to us that first Sun and then Oracle were never planning to make the important test and certification tests for Java, the Java SE TCK, available to Apache. We disagreed with this choice, but it was not ours to make. So rather than continue to drive Harmony as an unofficial and uncertified Java effort, we decided to shift direction and put our efforts into OpenJDK.
I am also curious (and I'm not the only one) to see if Google will elect to join the OpenJDK and JCP processes or continue to go it alone. The Googling Google blog looks at the relationship between Oracle, IBM, and Google related to today's announcement in the post IBM and Oracle vs. Android? Good luck with that.

Overall, it's nice to see some news in the Java world related to convergence rather than divergence.

Related Resources

There are too many related posts and articles related to this announcement to list here, but I do list a few as a sample.

Thứ Bảy, 9 tháng 10, 2010

Seven Indispensable NetBeans Java Hints

I have found NetBeans Java Hints to be extremely useful in Java development. In this blog post I look at NetBeans Java Hints that I deem indispensable in Java development. I will be using NetBeans 6.9 for the screen snapshots in this post. As this Wiki page indicates, there are numerous hints new to NetBeans 6.9.

The NetBeans IDE makes it easier to control which hints are enabled. This granular level of control is desirable because not every hint is created equal. Some hints, in my opinion, should always be enabled and these are the subject of this post. Other hints, may not be quite as useful for general use and some might even get in the way. I like to have only the hints that matter enabled so that I have a better chance of clearing the yellow highlighting and margin marks that indicate a warning condition. I like to have a "clean" Java source file in NetBeans so that if any significant conditions are introduced, the emergence of yellow tips me off. It is nice to disable less important (to me) hints so that this clean IDE view of the source can be maintained.

It is easy to enable Java hints in NetBeans. Select "Tools" from the title bar, then select "Options" from the drop-down menu. Choose the "Editor" option and then select the "Hints" tab. At this point, make sure that the "Language" drop-down is set to "Java" (other choices include PHP, JavaScript, and Ruby). The screen snapshot below shows how this appears, including the twenty-four categories of hints. The + sign to the left of each hints category can be clicked on to expand it and see specific hints within each category. These can then be selected to have them enabled in the IDE.


One of the categories of hints is "Standard Javac warnings." These are the warnings that I discussed in my blog post javac -Xlint Options. As I stated in that post, some of these can bring a developer's attention to some important conditions in the Java code. In particular, the support for striking out deprecated code marked with @deprecated and @Deprecated is useful in keeping new code from using deprecated methods. The next screen snapshot shows these "Standard Javac warnings."


In this post, I won't look at any of NetBeans' hints in this "Standard Javac warnings" hints category because they are covered by javac's -Xlint. Instead, I focus in this post on warnings that NetBeans hints provide that are not available via javac. Indeed, NetBeans provides some handy warnings for some very dangerous ("suspicious") conditions that I wish the standard compiler warned me about!

It is arguable which is the "most important" of the NetBeans hints, but the hint for "suspicious method call" certainly should be high on anyone's list. This hint, which falls under "Probable Bugs" hints category, warns about the dangerous (more than suspicious in my mind) situation I described in the blog post The Contains Trap in Java Collections. The following code snippet shows code that causes this hint to lead to three warnings.

/**
* This method demonstrates the NetBeans "Suspicious method call" hint.
*/
private void demonstrateSuspiciousMethodsCalls()
{
final StringBuilder builder = new StringBuilder();
builder.append("Hints");

final Set<String> strings = new HashSet<String>();
strings.add("NetBeans");
strings.add("Hints");
strings.add("http://marxsoftware.blogspot.com/");
if (strings.contains(builder))
{
out.println("That String is contained!");
}
else
{
out.println("That String is NOT contained.");
}

final Map<String, String> stringsMap = new HashMap<String, String>();
stringsMap.put("One", "NetBeans");
stringsMap.put("Two", "Hints");
stringsMap.put("Three", "http://marxsoftware.blogspot.com/");
if (stringsMap.containsKey(builder))
{
stringsMap.remove(builder);
}
}

With the "suspicious method call" hint enabled as a warning, NetBeans clearly flags the three occurrences of this situation in the code above as demonstrated in the next image.


As the above screen snapshot indicates, NetBeans flags the three "suspicious method calls" that occur in the code. They are highlighted with yellow underlining and with yellow marks in the right margin. The light bulb icons on the left can be clicked on to see more details and potential resolution options.

In this case, I find this condition of significant enough concern that a warning is not sufficient. NetBeans allows me to specify that this hint results in an error instead of a warning. For example, I can click on the light bulb icon and select "Configure 'Suspicious method call' Hint" option to change it. The new screen is shown next.


With the "Show As" drop-down set to "Error," NetBeans now shows this significant issue as an error in the IDE! This is shown in the next screen snapshot, which is the same as above, but with error-level markings.


Setting the "suspicious method calls" hint to appear as error rather than warning helps make it even more obvious in large code bases. Although I won't specifically show this transition for each NetBeans hint to "error" in this post, it is not surprising that many of these "indispensable" hints could arguably be set to "error" rather than "warning" for NetBeans reporting. NetBeans gives us the flexibility to select which hints we want flagged and which hints we want flagged as errors versus as warnings. One important observation is that NetBeans's treatment of a hint as an error is only for viewing and does not prevent NetBeans from building the source code successfully.

An important NetBeans hint is the "Comparing Strings using == or != hint. This hint flags the well-known issue in Java when Strings (usually mistakenly) are compared using == or != instead of .equals. The danger is increased dramatically when coupled with String instantiations using the new keyword and that's what NetBeans's "String constructor" hint catches. The next code snippet leads to these two hints being triggered. The NetBeans response to that code is shown after the code.

/**
* This method demonstrates two NetBeans hints: comparing strings with == or
* != and string constructor initialization. These two suspicious cases are
* especially problematic when they exist together as demonstrated in this
* method.
*/
private void demonstrateComparingStringsAndStringConstructorInitialization()
{
final String stringOne = new String("NetBeans");
final String stringTwo = new String("NetBeans");
if (stringOne == stringTwo)
{
out.println(stringOne + " equals " + stringTwo + ".");
}
else
{
out.println(stringOne + " does NOT equal " + stringTwo + ".");
}
}

Another dangerous problem occurs when a constructor calls a method on the same class that might be overridden by child method. This is demonstrated in the next code snippet, but fortunately NetBeans can identify this bad behavior with the "Overridable method call in constructor" hint.

package dustin.examples;

/**
* This class is full of misbehavior to help demonstrate NetBeans hints.
*/
public class MisbehavingClass
{
/**
* This constructor does a bad thing: it calls an overridable method.
*/
public MisbehavingClass()
{
initialize();
}

protected void initialize()
{
// do some initialization
}
}

Not surprisingly, the "Probable Bugs" category of NetBeans hints has numerous hints that warn of potentially serious complications. For example, the ".equals on incompatible types" hint will be triggered by the following code snippet.

/**
* Demonstrate NetBeans ".equals on incompatible types" hint.
*/
private void demonstrateIncompatibleEquals()
{
final String string = "String";
final StringBuilder builder = new StringBuilder("String");
if (string.equals(builder))
{
out.println(string + " equals " + builder);
}
else
{
out.println(string + " is NOT equal to " + builder);
}
}

This hint is in many ways like the "suspicious method call" hint covered earlier. Both of these hints warn about cases where the fact that the API necessarily needs to accept an Object instance leaves open the possibility of passing in types that simply can never match what is expected.

I always prefer to know about code issues as early as possible in the development cycle as possible. Specifically, when possible, I'd rather know about code that's flat-out wrong at compile time rather than at runtime. NetBeans includes several hints that let me know of a problem in my code that I'd otherwise not know about until runtime. I've already covered some of these above (the "suspicious method call" and ".equals on incompatible type" hints are two such examples), but the Probable Bugs category of hints provides another particularly useful hint in the "Incompatible Cast/Incompatible instanceof" hint.

This "Class is incompatible with instance of" hint notifies the developer that the type being checked in an outer instanceof operator check is not the same type (and is not compatible with) the type being cast when the instanceof evaluates to true. This is important because the compiler does not report an error or warning in this case. Instead, the problem is not encountered until runtime when a ClassCastException is thrown when the cast attempts to an incompatible type. A code snippet that will trigger this NetBeans hint is shown next.

/**
* Demonstrate NetBeans hint "Cast is incompatible with given instanceof".
*/
private void demonstrateCastIncompatibleWithInstanceOf()
{
final Object stringBuilder = new StringBuilder("string");
if (stringBuilder instanceof String)
{
final StringBuilder newBuilder = (StringBuilder) stringBuilder;
out.println("StringBuilder: " + newBuilder);
}
else if (stringBuilder instanceof StringBuilder)
{
final String newString = (String) stringBuilder;
out.println("String: " + newString);
}
}

The above, when seen in NetBeans, looks like what is shown in the next screen snapshot.


The warning provided by NetBeans above about the cast type being incompatible with the type checked by the instanceof operator is much nicer (and much sooner) than when first encountered in runtime:


A third NetBeans hint from the Probable Bugs category is the "Incorrect Column Index in ResultSet" hint. Unlike many Java (and C/C++) based APIs, the columns in a result set accessed by column number are one-based rather than zero-based. A developer might accidentally (or out of habit) attempt to use a zero-based column index scheme. The next code sample shows how this might be done.

/**
* Demonstrate NetBeans hint related to incorrect column index used with
* JDBC {@code ResultSet}.
*/
private void demonstrateIncorrectColumnIndexInResultSet()
{
final String jdbcUrl = "jdbc:oracle:thin:@localhost:1521:orcl";
final String jdbcDriverName = "oracle.jdbc.pool.OracleDataSource";
final String queryStr = "select employee_id, last_name, first_name from employees";
try
{
Class.forName("oracle.jdbc.pool.OracleDataSource");
final Connection connection = DriverManager.getConnection(jdbcUrl, "hr", "hr");
final PreparedStatement statement = connection.prepareStatement(queryStr);
final ResultSet rs = statement.executeQuery();
while (rs.next())
{
final int id = rs.getInt(0); // BAD FORM!!!
final String lastName = rs.getString(1);
final String firstName = rs.getString(2);
out.println(firstName + " " + lastName + "'s ID is " + id + ".");
}
}
catch (ClassNotFoundException cnfEx)
{
out.println(
"Cannot find JDBC driver class '" + jdbcDriverName + "' - "
+ cnfEx.toString());
}
catch (SQLException sqlEx)
{
out.println("Don't do this: " + sqlEx.toString());
}
}

This NetBeans hint is also brought up for negative column indices, but that is less likely to happen then the use of a zero. This code will correctly compile, but will break at runtime with an SQLException ("Invalid column index"). Thankfully, NetBeans warns of this "probable bug" as shown in the next screen snapshot.



It is important to always provide an explicitly overridden hashCode() implementation when equals() is explicitly overridden for correct behavior. NetBeans provides a hint for this. The "Generate missing hashCode or equals" hint will warn if either of these two methods exists without the other. This is demonstrated in the following code sample and the image following the code sample shows NetBeans calling attention to the missing hashCode().

public boolean equals(Object obj)
{
if (obj == null)
{
return false;
}
if (this.getClass() != obj.getClass())
{
return false;
}
final MisbehavingClass other = (MisbehavingClass) obj;
return this.variable != null ? this.variable.equals(other.variable) : other.variable == null;
}


This image shows two NetBeans hints in action. The first is the "Generate missing hashCode()" hint because there is an equals(Object) method, but not a hashCode() method in this class. The second is another useful NetBeans hint that suggests that the developer should "Add @Override Annotation" to this method that overrides Object's hashCode(). I like to have this one turned on to ensure that I don't accidentally override a method that I am not intending to override. If I see this hint, I make sure it is supposed to be an overriding method and add the annotation or else change its name to not be overriding.

I've covered many aspects of NetBeans hints in this post. The ability to flag issues at compilation (or even before that) time rather than at runtime is very helpful. There are other tools that can do this, but it's really nice to have it incorporated and automatically implied directly in the IDE. Furthermore, the ability to select only those hints that are desired is useful and the ability to indicate whether a particular hint should be treated as a warning or error adds further control to the granularity of the hinting reporting. Finally, the ultimate flexibility is provided by the ability to create custom NetBeans hints, something I have not demonstrated in this post. With NetBeans 6.9 introducing so many useful hints, I'm finding reduced need of other static analysis tools.

NetBeans provides many useful hints that improve the quality, correctness, reliability, and maintainability of Java code. In this post, I've focused on some of them that I find particularly valuable. They are summarized here:
  1. Suspicious Method Call
  2. Comparing Strings Using == or !=  AND String Constructor
  3. Overridable Method Call in Constructor
  4. .equals Incompatible Types
  5. Incorrect Column Index in ResultSet
  6. Cast Incompatible with instanceof
  7. Generate .equals or .hashCode Method
These hints are so useful (almost always a real bug) and it is so advantageous to know about them sooner (compile/pre-compile time rather than runtime), that I'd like to see these be added to Sun's/Oracle's javac's -Xlint options.

New Links to My Oracle Technology Network Articles

I noticed that the URLs to Oracle Technology Network (OTN) articles have changed recently. In this brief post, I list the new URLs for the articles that I have written or co-written that are published on OTN.

Build a Java Application With Eclipse, Spring, and Oracle WebLogic Server (February 2010)
New URL: http://www.oracle.com/technetwork/articles/marx-oepe-spring-095718.html
Former URL: http://www.oracle.com/technology/pub/articles/marx-oepe-spring.html

Basic Java Persistence API Simple Best Practices (May 2008)
New URL: http://www.oracle.com/technetwork/articles/marx-jpa-087268.html
Former URL: http://www.oracle.com/technology/pub/articles/marx-jpa.html

Visualize Your Oracle Database Data with JFreeChart (October 2007)
New URL: http://www.oracle.com/technetwork/articles/marx-jchart-085298.html
Former URL: http://www.oracle.com/technology/pub/articles/marx-jchart.html

Better JPA, Better JAXB, and Better Annotations Processing with Java SE 6 (September 2007)
New URL: http://www.oracle.com/technetwork/articles/marx-jse6-090753.html
Former URL: http://www.oracle.com/technology/pub/articles/marx-jse6.html

Accessorize Oracle Database with Ruby (February 2007)
New URL: http://www.oracle.com/technetwork/articles/marx-ruby-092465.html
Former URL: http://www.oracle.com/technology/pub/articles/marx-ruby.html

Add Some Spring to Your Oracle JDBC Database Access (November 2005)
New URL: Unknown (see this Internet Archive Wayback Machine Copy)
Former URL: http://www.oracle.com/technetwork/articles/marx_spring.html

Thứ Ba, 5 tháng 10, 2010

javac's -Xprint Option

With ready accessibility to the Java SDK API documentation and Java EE API documentation online and with the convenience of IDE method name completion, it is often easy to determine a class's public exposed API. However, even with these great tools at hand, I always like to be aware of useful command-line tools to supplement these tools. Such knowledge can be particularly useful if in an environment where the online documentation or properly working IDE are not as easily available or are less desirable because of their overhead. The Sun/Oracle javac compiler supports the non-standard (and hence not guaranteed to be in other Java compiler implementations or even in future Oracle javac implementations) -Xprint option to make this easy. A side benefit of this option is for generating a textual representation of a class's API.

The javac documentation describes the -Xprint option: "Print out textual representation of specified types for debugging purposes; perform neither annotation processing nor compilation. The format of the output may change."

The output of -Xprint on a .class file looks very similar to javap's output. For example, javac -Xprint shows the following output when run against java.lang.Object:

javac -Xprint java.lang.Object
package java.lang;

public class Object {

public Object();

private static native void registerNatives();

public final native java.lang.Class getClass();

public native int hashCode();

public boolean equals(java.lang.Object arg0);

protected native java.lang.Object clone() throws java.lang.CloneNotSupportedException;

public java.lang.String toString();

public final native void notify();

public final native void notifyAll();

public final native void wait(long arg0) throws java.lang.InterruptedException;

public final void wait(long arg0,
int arg1) throws java.lang.InterruptedException;

public final void wait() throws java.lang.InterruptedException;

protected void finalize() throws java.lang.Throwable;
}

Compare the above output from javac -Xprint on java.lang.Object to the output (shown next) of running javap against java.lang.Object:


Compiled from "Object.java"
public class java.lang.Object{
public java.lang.Object();
public final native java.lang.Class getClass();
public native int hashCode();
public boolean equals(java.lang.Object);
protected native java.lang.Object clone() throws java.lang.CloneNotSupportedException;
public java.lang.String toString();
public final native void notify();
public final native void notifyAll();
public final native void wait(long) throws java.lang.InterruptedException;
public final void wait(long, int) throws java.lang.InterruptedException;
public final void wait() throws java.lang.InterruptedException;
protected void finalize() throws java.lang.Throwable;
static {};
}

Of course, the output above is shown with javap's default settings. The private and protected members can be displayed using javap's -private and -protected options.

One advantage of javac -Xprint, however, is that the -Xprint option can be easily used with javac if one wants to see the interface for multiple files. The javac compiler can be used as normal against the normal source code files, but with the -Xprint option specified, the interfaces will be printed out without actually compiling the code. This is demonstrated here. I add the -Xprint option to my javac compiler when compiling the examples from my last blog post on javac -Xlint and get the following output:

package dustin.examples;

/**
* Simple class intended to help demonstrate -Xlint:overrides by providing a
* method that won't be overridden quite the same by its child.
*/
public class BaseClass {
protected java.util.List<java.lang.String> names;

public BaseClass();

public void addNames(final java.lang.String[] newNames);
}
package dustin.examples;

/**
* Simple class intended to help demonstrate -Xlint:overrides by "sort of"
* overriding a method defined in its parent.
*/
public class ChildClass extends dustin.examples.BaseClass {

public ChildClass();

@java.lang.Override
public void addNames(final java.lang.String... newNames);
}
package dustin.examples;

/**
* Simple Color representation.
*/
public enum Color {

BLACK,
BLUE,
BROWN,
CORAL,
EGGSHELL,
GREEN,
MAUVE,
ORANGE,
PINK,
PURPLE,
RED,
TAN,
WHITE,
YELLOW;
public static dustin.examples.Color[] values();

public static dustin.examples.Color valueOf(java.lang.String name);

private Color();
}
package dustin.examples;

/**
* Gender.
*/
public enum Gender {

FEMALE,
MALE;
public static dustin.examples.Gender[] values();

public static dustin.examples.Gender valueOf(java.lang.String name);

private Gender();
}
package dustin.examples;

/**
* Main executable demonstrating HotSpot's non-standard Xlint warning flags.
*/
public class Main {

public Main();
/**
*Fred.
*/
private static final dustin.examples.Person fred;
/**
*Wilma.
*/
private static final dustin.examples.Person wilma;
/**
*Barney.
*/
private static final dustin.examples.Person barney;

/**
* Demonstrates -Xlint:cast warning of a redundant cast.
*/
private static void demonstrateCastWarning();

/**
* Cause -Xlint:deprecation to print warning about use of deprecated method.
*/
private static void demonstrateDeprecationWarning();

/**
* Cause -Xlint:fallthrough to print warning about use of switch/case
* fallthrough.
*/
private static void demonstrateFallthroughWarning();

/**
* Demonstrate -Xlint:finally generating warning message when a {@code finally}
* block cannot end normally.
*/
private static void demonstrateFinallyWarning();

/**
* Divide the provided divisor into the provided dividend and return the
* resulting quotient. No checks are made to ensure that divisor is not zero.
* @param dividend Integer to be divided.
* @param divisor Integer by which dividend will be divided.
* @return Quotient of division of dividend by divisor.
*/
private static double divideIntegersForDoubleQuotient(final int dividend,
final int divisor);

/**
* Demonstrate -Xlint:divzero in action by dividing an int by a literal zero.
*/
private static void demonstrateDivideByZeroWarning();

/**
* Surprisingly, there is no -Xlint warning option for this highly
* suspicious situation of passing an object to a Set.contains call that
* could not possibly hold that type of object (could never result to true).
*/
private static void demonstrateNoContainsWarning();

/**
* This method demonstrates how javac's -Xlint:empty works. Note that javac's
* -Xlint:empty will only flag the empty statement involved in the "if" block,
* but does not flag the empty statements associated with the do-while loop,
* the while loop, the for loop, or the if-else. NetBeans does flag these if
* the appropriate "Hints" are turned on.
*/
private static void demonstrateEmptyWarning();

/**
* Divide the provided divisor into the provided dividend and return the
* resulting quotient. No checks are made to ensure that divisor is not zero.
* @param dividend Integer to be divided.
* @return Quotient of division of dividend by divisor.
*/
private static long divideIntegerByZeroForLongQuotient(final int dividend);

/**
* Demonstrate the commonly seen -Xlint:unchecked in action.
* @return Set of Person objects.
*/
private static java.util.Set<dustin.examples.Person> demonstrateUncheckedWarning();

/**
* Main executable function to demonstrate -Xlint. Various -Xlint options
* are demonstrated as follows:
* <ul>
* <li>{@code -Xlint:cast}</li>: This class's method demonstrateCastWarning()
* demonstrates how a redundant cast can lead to this warning.</li>
* <li>{@code -Xlint:deprecation}: This class's method demonstrateDeprecationWarning()
* intentionally invokes a deprecated method in the Person class.</li>
* <li>{@code -Xlint:divzero}: This class's demonstrateDivideByZeroWarning()
* method demonstrates this warning generated when a literal zero is
* used as a divisor in integer division.</li>
* <li>{@code -Xlint:empty}: This class's demonstateEmptyWarning() method
* demonstrates that (likely accidental) "if" expression without any
* result of the condition being {@code true} being performed results in
* a warning this option's set.
* <li>{@code -Xlint:fallthrough}: This class's method demonstrateFallthroughWarning()
* demonstrates how {@code switch} statements with {@code case} expressions
* without their own {@code break} statements may or may not lead to
* this producing a warning message.</li>
* <li>{@code -Xlint:finally}: This class's method demonstrateFinallyWarning()
* demonstrates the warning related to a return from a {@code finally}
* clause.</li>
* <li>{@code -Xlint:overrides}: This is demonstrated in classes external to
* this one: {@code BaseClass} and its child class @{code ChildClass}.</li>
* <li>{@code -Xlint:path}: This is shown by providing a path to the javac
* compiler's classpath option for a location that does not exist.</li>
* <li>{@code -Xlint:serial}: The Person class implements the Serializable
* interface, but does not declare an explicit serialVersionUID.</li>
* <li>{@code -Xlint:unchecked}: This class's demonstrateUncheckedWarning()
* demonstrates this warning message.</li>
* </ul>
* @param arguments Command-line arguments: none expected.
*/
public static void main(final java.lang.String[] arguments);
}
package dustin.examples;

/**
* Person class that intentionally has problems that will be flagged as warnings
* by javac with -X non-standard options.
*/
public final class Person implements java.io.Serializable {
private final java.lang.String lastName;
private final java.lang.String firstName;
private final dustin.examples.Gender gender;
private final dustin.examples.Color favoriteColor;

public Person(final java.lang.String newLastName,
final java.lang.String newFirstName,
final dustin.examples.Gender newGender,
final dustin.examples.Color newFavoriteColor);

public java.lang.String getLastName();

public java.lang.String getFirstName();

public java.lang.String getFullName();

/**
* Provide the person's full name.
* @return Full name of this person.
* @deprecated Use getFullName() instead.
*/
@java.lang.Deprecated
public java.lang.String getName();

public dustin.examples.Gender getGender();

public dustin.examples.Color getFavoriteColor();

/**
* NetBeans-generated equals(Object) method checks for equality of provided
* object to me.
* @param obj Object to be compared to me for equality.
* @return {@code true} if the provided object and I are considered equal.
*/
@java.lang.Override
public boolean equals(java.lang.Object obj);

/**
* NetBeans-generated hashCode() method.
* @return Hash code for this instance.
*/
@java.lang.Override
public int hashCode();

@java.lang.Override
public java.lang.String toString();
}

The output of my own classes shown above is a reminder of another nicety of javac -Xprint: it includes the Javadoc comments of the members and methods that it prints out when source code is available. This can be helpful in understanding what the parameters of the API methods are. If javac -Xprint is run directly against a .class file, it does not include the Javadoc comments; if it's run against the .java source file, it does include the Javadoc comments.

For example, if I change my directory to the installation directory of the Spring Framework, I can use the command
javac -Xprint src\org\springframework\core\Constants.java
to see the org.springframework.core.Constants class members and methods with accompanying Javadoc comments. Alternatively, I could run javac -Xprint against the compiled .class file as well (but won't see the Javadoc comments in that output) with the command

javac -cp dist\spring.jar -Xprint org.springframework.core.Constants

It is nice to be able to run javac -Xprint against compiled binaries (.class) or raw source (.java).

Another interesting facet of the javac -Xprint is that it demonstrates what annotation processors can do in Java SE 6.

I usually use an IDE and the online Javadoc-based API documentation to learn new APIs or remind myself of the specifics of APIs I don't use frequently. However, every once in a while, I find it useful to be aware of the existence of javap and of javac -Xprint for providing a quick reminder of the available APIs on my own classes and on third-party classes.

Thứ Hai, 4 tháng 10, 2010

javac's -Xlint Options

The Java programming language compiler (javac) provided by Oracle (and formerly by Sun) has several non-standard options that are often useful. One of the most useful is the set of non-standard options that print out warnings encountered during compilation. That set of options is the subject of this post.

The javac page section on non-standard options lists and provides brief details on each of these options. The following is the relevant snippet from that page.


A listing of these options is also available from the command line (assuming the Java SDK is installed) with the command: javac -help -X. This is briefer than the man page/web page example shown above and is shown next.


As the previous snapshot from running javac -help -X indicates, the ten specific conditions for which Xlint warnings exist are (in alphabetical order): cast, deprecation, divzero, empty, fallthrough, finally, overrides, path, serial, and unchecked. I briefly look at each of these and provide a code snippet that leads to these warning occurring when Xlint is turned on. Note that the man page for javac and the Java SE 6 javac page both only list half of these Xlint options (documentation is apparently not as up-to-date as the javac usage/help). There is a useful NetBeans Wiki entry that summarizes all ten options.

The javac compiler allows all or none of the Xlint warnings to be enabled. If Xlint is not specified at all of the option -Xlint:none is explicitly specified, the behavior is to not show most of the warnings. Interestingly, the output does provide a warning about deprecation and unchecked warnings and recommends running javac with -Xlint enabled to see the details on these two types of warnings.

Before the end of this post, I'll demonstrate Java code that leads to 13 total reported Xlint warnings covering all ten of the options discussed above. However, without Xlint specified, the output is as shown in the next screen snapshot.


As the above image indicates, whether Xlint is not specified at all or is specified explicitly with "none", the result is the same: the majority of the warnings are not shown, but there are simple references to the deprecation and unchecked warnings with recommendations to run javac with -Xlint:deprecation and -Xlint:unchecked respectively for additional details. Running javac with -Xlint:all or -Xlint with no other options will show all warnings and would work to see the details regarding deprecated, unchecked, and all other applicable Xlint-enabled warnings. This will be shown after going through the source code and each Xlint warning individually.

-Xlint:cast
This option can be used to have the compiler warn the developer that a redundant cast is being made. Here is a code snippet that would get flagged if -Xlint, -Xlint:all, or -Xlint:cast was provided to javac when compiling the source.

/**
* Demonstrates -Xlint:cast warning of a redundant cast.
*/
private static void demonstrateCastWarning()
{
final Set<Person> people = new HashSet<Person>();
people.add(fred);
people.add(wilma);
people.add(barney);
for (final Person person : people)
{
// Redundant cast because generic type explicitly is Person
out.println("Person: " + ((Person) person).getFullName());
}
}

In the above code, there is no need to cast the person object inside the for loop to Person and -Xlint:cast will warn of this unnecessary and redundant cast with a message stating something like:
src\dustin\examples\Main.java:37: warning: [cast] redundant cast to dustin.examples.Person
out.println("Person: " + ((Person) person).getFullName());
^


-Xlint:deprecation
As discussed above, the Xlint deprecation warning was evidently deemed important enough to justify it being advertised even when Xlint is not explicitly run. This warning occurs when a deprecated method is invoked. The following code example demonstrates such a case.

/**
* Cause -Xlint:deprecation to print warning about use of deprecated method.
*/
private static void demonstrateDeprecationWarning()
{
out.println("Fred's full name is " + fred.getName());
}

You cannot tell without the source code for the Person class (of which "fred" is an instance), but that getName() method is deprecated in Person. The following output from running javac with -Xlint, -Xlint:all, or -Xlint:deprecation confirms that (or points it out if the developer missed it).

src\dustin\examples\Main.java:47: warning: [deprecation] getName() in dustin.examples.Person has been deprecated
out.println("Fred's full name is " + fred.getName());
^

-Xlint:divzero
The divzero Xlint option indicates when integral division divides by a literal zero. A code example that will demonstrate this is shown next:

/**
* Demonstrate -Xlint:divzero in action by dividing an int by a literal zero.
*/
private static void demonstrateDivideByZeroWarning()
{
out.println("Two divided by zero is " + divideIntegerByZeroForLongQuotient(2));
}

/**
* Divide the provided divisor into the provided dividend and return the
* resulting quotient. No checks are made to ensure that divisor is not zero.
*
* @param dividend Integer to be divided.
* @return Quotient of division of dividend by literal zero.
*/
private static long divideIntegerByZeroForLongQuotient(final int dividend)
{
// Hard-coded divisor of zero will lead to warning. Had the divisor been
// passed in as a parameter with a zero value, this would not lead to
// that warning.
return dividend / 0;
}

The output from javac when the above is compiled is now shown.

src\dustin\examples\Main.java:231: warning: [divzero] division by zero
return dividend / 0;
^

When I intentionally tried to force this warning, it seemed to only work for a hard-coded (literal) zero divisor. Also, it does not flag double division because Infinity can be returned as a valid answer in that case without throwing an exception.

-Xlint:empty
The purpose of -Xlint:empty is to notify the developer that an "empty" if conditional is in the code. From my tests, this seems to only apply for the case of the empty "if" block. NetBeans provides "hints" (those yellow underlined warnings that are also marked in the right margin of the source code editor) for several types of empty statements, but -Xlint:empty seems to only flag the empty "if" statements. I included the others that NetBeans flags along with the one -Xlint:empty flags in the next source code sample.

/**
* This method demonstrates how javac's -Xlint:empty works. Note that javac's
* -Xlint:empty will only flag the empty statement involved in the "if" block,
* but does not flag the empty statements associated with the do-while loop,
* the while loop, the for loop, or the if-else. NetBeans does flag these if
* the appropriate "Hints" are turned on.
*/
private static void demonstrateEmptyWarning()
{
int[] integers = {1, 2, 3, 4, 5};
if (integers.length != 5);
out.println("Not five?");

if (integers.length == 5)
out.println("Five!");
else;
out.println("Not Five!");

do;
while (integers.length > 0);

for (int integer : integers);
out.println("Another integer found!");

int counter = 0;
while (counter < 5);

out.println("Extra semicolons.");;;;
}

The code above is filled with problematic placement of semicolons that almost certainly are not what the developer wanted. This code will compile, but the developer is warned of these suspicious situations if -Xlint, -Xlint:all, or -Xlint:empty is used with javac. The warning messages that are printed in the otherwise successful compilation are shown next.

src\dustin\examples\Main.java:197: warning: [empty] empty statement after if
if (integers.length != 5);
^

Only the empty "if" statement clause is flagged; the others are not reported by -Xlint:empty.

-Xlint:fallthrough
A tempting but controversial convenience Java provides is the ability to "fallthrough" common expressions in a switch statement to apply the same logic to multiple integral values with one piece of code. If all of the integral values with the shared functionality are empty except the final one that actually performs the functionality and provides a break, the -Xlint:fallthrough won't be activated. However, if some of the case expressions do perform their own logic in addition to the common fallthrough logic, this warning is produced. An examples that demonstrates this is shown next.

/**
* Cause -Xlint:fallthrough to print warning about use of switch/case
* fallthrough.
*/
private static void demonstrateFallthroughWarning()
{
out.print("Wilma's favorite color is ");
out.print(wilma.getFavoriteColor() + ", which is ");

// check to see if 'artistic' primary color
// NOTE: This one will not lead to -Xlint:fallthrough flagging a warning
// because no functionality is included in any of the case statements
// that don't have their own break.
switch (wilma.getFavoriteColor())
{
case BLUE:
case YELLOW:
case RED:
out.print("a primary color for artistic endeavors");
break;
case BLACK:
case BROWN:
case CORAL:
case EGGSHELL:
case GREEN:
case MAUVE:
case ORANGE:
case PINK:
case PURPLE:
case TAN:
case WHITE:
default:
out.print("NOT a primary artistic color");
}
out.print(" and is ");
// check to see if 'additive' primary color
// NOTE: This switch WILL lead to -Xlint:fallthrough emitting a warning
// because there is some functionality being performed in a case
// expression that does not have its own break statement.
switch (wilma.getFavoriteColor())
{
case BLUE:
case GREEN:
out.println("(it's not easy being green!) ");
case RED:
out.println("a primary color for additive endeavors.");
break;
case BLACK:
case BROWN:
case CORAL:
case EGGSHELL:
case MAUVE:
case ORANGE:
case PINK:
case PURPLE:
case TAN:
case YELLOW:
case WHITE:
default:
out.println("NOT a primary additive color.");
}
}

The above code example intentionally shows both cases (pun intended) of the switch/case that will and will not lead to a warning message thanks to -Xlint:fallthrough. The output, with only one warning, is shown next.

src\dustin\examples\Main.java:95: warning: [fallthrough] possible fall-through into case
case RED:
^

The case that got flagged was the RED case following the GREEN case that did some logic of its own before falling through to the RED logic.

-Xlint:finally
More than one person has warned, "Don't return in a finally clause." In fact, "Java's return doesn't always" is in The Java Hall of Shame. A Java developer can be warned about this nefarious situation by using -Xlint, -Xlint:all, or -Xlint:finally. A piece of source code demonstrating how this warning could be generated is shown next.

/**
* Demonstrate -Xlint:finally generating warning message when a {@code finally}
* block cannot end normally.
*/
private static void demonstrateFinallyWarning()
{
try
{
final double quotient = divideIntegersForDoubleQuotient(10, 0);
out.println("The quotient is " + quotient);
}
catch (RuntimeException uncheckedException)
{
out.println("Caught the exception: " + uncheckedException.toString());
}
}


/**
* Divide the provided divisor into the provided dividend and return the
* resulting quotient. No checks are made to ensure that divisor is not zero.
*
* @param dividend Integer to be divided.
* @param divisor Integer by which dividend will be divided.
* @return Quotient of division of dividend by divisor.
*/
private static double divideIntegersForDoubleQuotient(final int dividend, final int divisor)
{
double quotient = 0.0;
try
{
if (divisor == 0)
{
throw new ArithmeticException(
"Division by zero not allowed: cannot perform " + dividend + "/" + divisor);
}
// This would not have led to Xlint:divzero warning if we got here
// with a literal zero divisor because Infinity would have simply been
// returned rather than implicit throwing of ArithmeticException.
quotient = (double) dividend / divisor;
}
finally
{
return quotient;
}
}

The above is flawed and likely isn't what the developer intended. The relevant warning javac provides when Xlint is enabled is shown next.

src\dustin\examples\Main.java:159: warning: [finally] finally clause cannot complete normally
}
^

-Xlint:overrides
The -Xlint:overrides option does not replace the @Overrides annotation. The latter is an error rather than a warning anyway. Instead, -Xlint:overrides indicates when a much less obvious situation has occurred. Two Java classes are shown here to illustrate how this warning might occur. The first class is the base class and the second class extends that base class, tries to override one of the base class's methods with inclusion of an @Overrides annotation. Like all code in my examples in this post, this code does compile.

BaseClass.java
package dustin.examples;

import java.util.ArrayList;
import java.util.List;

/**
* Simple class intended to help demonstrate -Xlint:overrides by providing a
* method that won't be overridden quite the same by its child.
*/
public class BaseClass
{
protected List<String> names = new ArrayList<String>();

public BaseClass() {}

public void addNames(final String[] newNames)
{
for (final String name : newNames)
{
names.add(name);
}
}
}

ChildClass.java
package dustin.examples;

/**
* Simple class intended to help demonstrate -Xlint:overrides by "sort of"
* overriding a method defined in its parent.
*/
public class ChildClass extends BaseClass
{
@Override
public void addNames(final String... newNames)
{
for (final String name : newNames)
{
this.names.add(name);
}
}
}

Here is the warning javac provides when the appropriate -Xlint flag is used.

src\dustin\examples\ChildClass.java:10: warning: addNames(java.lang.String...) in dustin.examples.ChildClass overrides addNames(java.lang.String[]) in dustin.examples.BaseClass; overridden method has no '...'
public void addNames(final String... newNames)
^

-Xlint:path
The -Xlint:path is one of my favorites. I like it so much, in fact, that I have blogged on it specifically. As I stated in that post, this is particularly handy in identifying assumed classpath locations that don't really exist. This knowledge can help in all kinds of class loader issues. The option is not limited to classpaths, but that is where I use it most.

There are a couple interesting notes about this particular option. First, this was one I was unable to generate when building with Ant because Ant automatically detects non-existent paths as well and doesn't apply them (therefore not giving -Xlint:path a chance to be the hero).

The type of Ant declaration I often use when compiling my Java code is shown next. It specifies -Xlint for all javac warnings in the compilerarg element nested with the javac element.

<target name="compile"
description="Compile the Java code."
depends="-init">
<javac srcdir="${src.dir}"
destdir="${classes.dir}"
classpathref="classpath"
debug="${javac.debug}"
includeantruntime="false">
<compilerarg value="-Xlint"/>
</javac>
</target>

When I build my sample application using Ant and the target shown above, I do not see the path-oriented warning. If I turn up the verbosity during the Ant build, I can detect why when it reports this message: "dropping C:\noSuchDirectory from path as it doesn't exist"

When I build my final example with javac on the command line, there are 13 warnings covering the ten Xlint categories displayed. When I build it using Ant and the target above, there are only 12 warnings displayed and all but the -Xlint:path warning are listed. Here is what the output from -Xlint:path does look like when javac is run from the command line:

warning: [path] bad path element "C:\noSuchDirectory": no such file or directory

The second interesting observation about -Xlint:path is that it's not a source code warning like the others covered here, but is instead more a warning about how javac itself is being applied to the source code. Given that, there's no source code to see here. Let's move on.

-Xlint:serial
Josh Bloch, in Effective Java, discusses the importance of generating a serialVersionUID for classes that are marked as Serializable. Indeed, the Javadoc for Serializable also cover the importance of this. The -Xlint:serial flag will warn a developer when a Serializable class does not have an explicit serialVersionUID. It's time to look at the previously mentioned Person class (discussed in conjunction with -Xlint:deprecation), which is Serializable, but does not have an explicit serialVersionUID declared.

package dustin.examples;

import java.io.Serializable;

/**
* Person class that intentionally has problems that will be flagged as warnings
* by javac with -X non-standard options.
*/
public final class Person implements Serializable
{
// no serialVersionUID should demonstrate -Xlint:serial

private final String lastName;

private final String firstName;

private final Gender gender;

private final Color favoriteColor;

public Person(
final String newLastName,
final String newFirstName,
final Gender newGender,
final Color newFavoriteColor)
{
this.lastName = newLastName;
this.firstName = newFirstName;
this.gender = newGender;
this.favoriteColor = newFavoriteColor;
}

public String getLastName()
{
return this.lastName;
}

public String getFirstName()
{
return this.firstName;
}

public String getFullName()
{
return this.firstName + " " + this.lastName;
}

/**
* Provide the person's full name.
*
* @return Full name of this person.
* @deprecated Use getFullName() instead.
*/
@Deprecated
public String getName()
{
return this.firstName + " " + this.lastName;
}

public Gender getGender()
{
return this.gender;
}

public Color getFavoriteColor()
{
return this.favoriteColor;
}


/**
* NetBeans-generated equals(Object) method checks for equality of provided
* object to me.
*
* @param obj Object to be compared to me for equality.
* @return {@code true} if the provided object and I are considered equal.
*/
@Override
public boolean equals(Object obj)
{
if (obj == null)
{
return false;
}
if (getClass() != obj.getClass())
{
return false;
}
final Person other = (Person) obj;
if ((this.lastName == null) ? (other.lastName != null) : !this.lastName.equals(other.lastName))
{
return false;
}
if ((this.firstName == null) ? (other.firstName != null) : !this.firstName.equals(other.firstName))
{
return false;
}
if (this.gender != other.gender)
{
return false;
}
if (this.favoriteColor != other.favoriteColor)
{
return false;
}
return true;
}


/**
* NetBeans-generated hashCode() method.
*
* @return Hash code for this instance.
*/
@Override
public int hashCode()
{
int hash = 7;
hash = 59 * hash + (this.lastName != null ? this.lastName.hashCode() : 0);
hash = 59 * hash + (this.firstName != null ? this.firstName.hashCode() : 0);
hash = 59 * hash + (this.gender != null ? this.gender.hashCode() : 0);
hash = 59 * hash + (this.favoriteColor != null ? this.favoriteColor.hashCode() : 0);
return hash;
}


@Override
public String toString()
{
return getFullName();
}
}

Here is what javac tells me about this when I have -Xlint, -Xlint:all, or -Xlint:serial specified.

src\dustin\examples\Person.java:9: warning: [serial] serializable class dustin.examples.Person has no definition of serialVersionUID
public final class Person implements Serializable
^

-Xlint:unchecked
We've finally arrived at the tenth -Xlint option. This one is covered last because "U" falls so late in the English alphabet, but it's arguably appropriate to cover it last anyway because it's one of the ones most Java developers probably see most often. This -Xlint:unchecked and the previously covered -Xlint:deprecation are the only two of the ten covered here that are warned about even when -Xlint is explicitly stated to warn about "none." Because it's so common, there are numerous code samples that demonstrate it. One simple one is shown here.


/**
* Demonstrate the commonly seen -Xlint:unchecked in action.
*
* @return Set of Person objects.
*/
private static Set demonstrateUncheckedWarning()
{
final Set people = new HashSet();
people.add(fred);
people.add(wilma);
people.add(barney);
return people;
}

I could have declared the Set interface and its HashSet implementation above to be specifically of type Person, but I failed to do so. This leads to four warnings as shown next.


src\dustin\examples\Main.java:243: warning: [unchecked] unchecked call to add(E) as a member of the raw type java.util.Set
people.add(fred);
^
src\dustin\examples\Main.java:244: warning: [unchecked] unchecked call to add(E) as a member of the raw type java.util.Set
people.add(wilma);
^
src\dustin\examples\Main.java:245: warning: [unchecked] unchecked call to add(E) as a member of the raw type java.util.Set
people.add(barney);
^
src\dustin\examples\Main.java:246: warning: [unchecked] unchecked conversion
found : java.util.Set
required: java.util.Set
return people;
^

The numerous warnings here are how my example got 13 total warnings for ten Xlint categories.

Other Noteworthy Items
This post is already pretty long, so I'm only going to briefly mention a last few items of interest. First, not only can one go without using -Xlint at all when using javac or use -Xlint:none to explicitly not use -Xlint, but one has even more granular control on what is not printed. This is available by using a minus sign (-) in front of a particular Xlint option to specify not to warn about it. For example, a developer who doesn't care about not having a serialVersionUID could specify -Xlint:-serial to explicitly tell the javac compiler to not warn about absence of a serialVersionUID in a Serializable class.

Another way to limit the printing of these Xlint-based warnings is (at least in some cases) the availability of the @SuppressWarnings annotation to state which warnings should be ignored. These annotations are placed directly in the source code, but will keep Xlint from reporting the warning. Casper Bang provides nice coverage of the use of this annotation in his post @SuppressWarnings values. Alex Miller has provided a nice summary of @SuppressWarnings options as well.

I mentioned briefly that NetBeans covers several more "empty" conditions than does -Xlint. This tends to be true of many other warnings as well. NetBeans and the other major Java IDEs tend to warn about more types of suspicious behavior than does -Xlint.

The Rest of the Code
I already included the complete source code for three classes above (Person.java, BaseClass.java, and ChildClass.java). Here I include source code for the Main.java that had many of the examples that led to Xlint complaining along with the source code for the two simple enums Gender and Color.

Gender.java
package dustin.examples;

/**
* Gender.
*/
public enum Gender
{
FEMALE,
MALE
}

Color.java
package dustin.examples;

/**
* Simple Color representation.
*/
public enum Color
{
BLACK,
BLUE,
BROWN,
CORAL,
EGGSHELL,
GREEN,
MAUVE,
ORANGE,
PINK,
PURPLE,
RED,
TAN,
WHITE,
YELLOW
}

Main.java
package dustin.examples;

import java.util.Set;
import java.util.HashSet;
import static java.lang.System.out;

/**
* Main executable demonstrating HotSpot's non-standard Xlint warning flags.
*/
public class Main
{
/** Fred. */
private final static Person fred =
new Person("Flintstone", "Fred", Gender.MALE, Color.ORANGE);

/** Wilma. */
private final static Person wilma =
new Person("Flintstone", "Wilma", Gender.FEMALE, Color.PURPLE);

/** Barney. */
private final static Person barney =
new Person("Rubble", "Barney", Gender.MALE, Color.BROWN);


/**
* Demonstrates -Xlint:cast warning of a redundant cast.
*/
private static void demonstrateCastWarning()
{
final Set<Person> people = new HashSet<Person>();
people.add(fred);
people.add(wilma);
people.add(barney);
for (final Person person : people)
{
// Redundant cast because generic type explicitly is Person
out.println("Person: " + ((Person) person).getFullName());
}
}


/**
* Cause -Xlint:deprecation to print warning about use of deprecated method.
*/
private static void demonstrateDeprecationWarning()
{
out.println("Fred's full name is " + fred.getName());
}


/**
* Cause -Xlint:fallthrough to print warning about use of switch/case
* fallthrough.
*/
private static void demonstrateFallthroughWarning()
{
out.print("Wilma's favorite color is ");
out.print(wilma.getFavoriteColor() + ", which is ");

// check to see if 'artistic' primary color
// NOTE: This one will not lead to -Xlint:fallthrough flagging a warning
// because no functionality is included in any of the case statements
// that don't have their own break.
switch (wilma.getFavoriteColor())
{
case BLUE:
case YELLOW:
case RED:
out.print("a primary color for artistic endeavors");
break;
case BLACK:
case BROWN:
case CORAL:
case EGGSHELL:
case GREEN:
case MAUVE:
case ORANGE:
case PINK:
case PURPLE:
case TAN:
case WHITE:
default:
out.print("NOT a primary artistic color");
}
out.print(" and is ");
// check to see if 'additive' primary color
// NOTE: This switch WILL lead to -Xlint:fallthrough emitting a warning
// because there is some functionality being performed in a case
// expression that does not have its own break statement.
switch (wilma.getFavoriteColor())
{
case BLUE:
case GREEN:
out.println("(it's not easy being green!) ");
case RED:
out.println("a primary color for additive endeavors.");
break;
case BLACK:
case BROWN:
case CORAL:
case EGGSHELL:
case MAUVE:
case ORANGE:
case PINK:
case PURPLE:
case TAN:
case YELLOW:
case WHITE:
default:
out.println("NOT a primary additive color.");
}
}


/**
* Demonstrate -Xlint:finally generating warning message when a {@code finally}
* block cannot end normally.
*/
private static void demonstrateFinallyWarning()
{
try
{
final double quotient = divideIntegersForDoubleQuotient(10, 0);
out.println("The quotient is " + quotient);
}
catch (RuntimeException uncheckedException)
{
out.println("Caught the exception: " + uncheckedException.toString());
}
}


/**
* Divide the provided divisor into the provided dividend and return the
* resulting quotient. No checks are made to ensure that divisor is not zero.
*
* @param dividend Integer to be divided.
* @param divisor Integer by which dividend will be divided.
* @return Quotient of division of dividend by divisor.
*/
private static double divideIntegersForDoubleQuotient(final int dividend, final int divisor)
{
double quotient = 0.0;
try
{
if (divisor == 0)
{
throw new ArithmeticException(
"Division by zero not allowed: cannot perform " + dividend + "/" + divisor);
}
// This would not have led to Xlint:divzero warning if we got here
// with a literal zero divisor because Infinity would have simply been
// returned rather than implicit throwing of ArithmeticException.
quotient = (double) dividend / divisor;
}
finally
{
return quotient;
}
}


/**
* Demonstrate -Xlint:divzero in action by dividing an int by a literal zero.
*/
private static void demonstrateDivideByZeroWarning()
{
out.println("Two divided by zero is " + divideIntegerByZeroForLongQuotient(2));
}


/**
* Surprisingly, there is no -Xlint warning option for this highly
* suspicious situation of passing an object to a Set.contains call that
* could not possibly hold that type of object (could never result to true).
*/
private static void demonstrateNoContainsWarning()
{
final Set<Person> people = new HashSet<Person>();
if (people.contains("Dustin"))
{
out.println("Here's there!");
}
}


/**
* This method demonstrates how javac's -Xlint:empty works. Note that javac's
* -Xlint:empty will only flag the empty statement involved in the "if" block,
* but does not flag the empty statements associated with the do-while loop,
* the while loop, the for loop, or the if-else. NetBeans does flag these if
* the appropriate "Hints" are turned on.
*/
private static void demonstrateEmptyWarning()
{
int[] integers = {1, 2, 3, 4, 5};
if (integers.length != 5);
out.println("Not five?");

if (integers.length == 5)
out.println("Five!");
else;
out.println("Not Five!");

do;
while (integers.length > 0);

for (int integer : integers);
out.println("Another integer found!");

int counter = 0;
while (counter < 5);

out.println("Extra semicolons.");;;;
}


/**
* Divide the provided divisor into the provided dividend and return the
* resulting quotient. No checks are made to ensure that divisor is not zero.
*
* @param dividend Integer to be divided.
* @return Quotient of division of dividend by literal zero.
*/
private static long divideIntegerByZeroForLongQuotient(final int dividend)
{
// Hard-coded divisor of zero will lead to warning. Had the divisor been
// passed in as a parameter with a zero value, this would not lead to
// that warning.
return dividend / 0;
}


/**
* Demonstrate the commonly seen -Xlint:unchecked in action.
*
* @return Set of Person objects.
*/
private static Set<Person> demonstrateUncheckedWarning()
{
final Set people = new HashSet();
people.add(fred);
people.add(wilma);
people.add(barney);
return people;
}


/**
* Main executable function to demonstrate -Xlint. Various -Xlint options
* are demonstrated as follows:
* <ul>
* <li>{@code -Xlint:cast}</li>: This class's method demonstrateCastWarning()
* demonstrates how a redundant cast can lead to this warning.</li>
* <li>{@code -Xlint:deprecation}: This class's method demonstrateDeprecationWarning()
* intentionally invokes a deprecated method in the Person class.</li>
* <li>{@code -Xlint:divzero}: This class's demonstrateDivideByZeroWarning()
* method demonstrates this warning generated when a literal zero is
* used as a divisor in integer division.</li>
* <li>{@code -Xlint:empty}: This class's demonstateEmptyWarning() method
* demonstrates that (likely accidental) "if" expression without any
* result of the condition being {@code true} being performed results in
* a warning this option's set.
* <li>{@code -Xlint:fallthrough}: This class's method demonstrateFallthroughWarning()
* demonstrates how {@code switch} statements with {@code case} expressions
* without their own {@code break} statements may or may not lead to
* this producing a warning message.</li>
* <li>{@code -Xlint:finally}: This class's method demonstrateFinallyWarning()
* demonstrates the warning related to a return from a {@code finally}
* clause.</li>
* <li>{@code -Xlint:overrides}: This is demonstrated in classes external to
* this one: {@code BaseClass} and its child class @{code ChildClass}.</li>
* <li>{@code -Xlint:path}: This is shown by providing a path to the javac
* compiler's classpath option for a location that does not exist.</li>
* <li>{@code -Xlint:serial}: The Person class implements the Serializable
* interface, but does not declare an explicit serialVersionUID.</li>
* <li>{@code -Xlint:unchecked}: This class's demonstrateUncheckedWarning()
* demonstrates this warning message.</li>
* </ul>
*
* @param arguments Command-line arguments: none expected.
*/
public static void main(final String[] arguments)
{
demonstrateCastWarning();
demonstrateDeprecationWarning();
demonstrateDivideByZeroWarning();
demonstrateEmptyWarning();
demonstrateFallthroughWarning();
demonstrateFinallyWarning();
demonstrateUncheckedWarning();
}
}

Conclusion

David Walend's post Amazing -Xlint points out that Xlint "made short work of the clean-up pass" and "pointed out places that needed some more thought, and helped keep me honest." I have found Xlint to be useful at time for similar reasons. The IDEs often provide me with warnings that cover the same ones as Xlint (and often many more), but there are times when things slip through and Xlint is there to capture them. These are not always deal-breaker issues, but sometimes they are and sometimes they have the potential to be.