Name |
Severity |
Description |
RuleAwtPeer |
Severe |
java.awt.peer interfaces were designed to handle
platform-specific graphics issues in Java. Since Java is meant to be
platform-independent, using java.awt.peer directly is forbidden.
|
public
static void
main( String[] args ) {
Frame frame = new
Frame( "Peers" );
Button button = new
Button( "Hello World!" );
frame.setLayout(
new GridLayout() );
frame.add(
button );
frame.setVisible(
true );
ButtonPeer peer = (ButtonPeer)button.getPeer();
peer.dispose();
}
|
|
|
Remove calls to peer objects.
Use java.awt or javax.swing
interfaces and classes instead.
public
static void
main( String[] args ) {
Frame frame = new
Frame( "Peers" );
Button button = new
Button( "Hello World!" );
frame.setLayout(
new GridLayout() );
frame.add(
button );
frame.setVisible(
true );
}
|
|
|
RuleCloneableSuperClone |
Severe |
Calling super.clone() from the clone() is essential in order
to ensure that a complete copy of the object is created. Failure to do
so is likely to result in a NullPointerException thrown later or an
unexpected object state in the course of program execution.
|
public
static void
main (String[] args){
double x = 3.1;
int i = (int)x;
System.out.println(i);
}
|
|
|
Change the underlying data type
to be lower precision.
public
static void
main (String[] args){
int i = 3;
System.out.println(i);
}
|
|
|
Update the calculation to
operate on the higher precision data type.
public
static void
main (String[] args){
double x = 3.1;
System.out.println(
x );
}
|
|
|
RuleComparisonEqualsHashCode |
Severe |
java.lang.Object.equals() and java.lang.Object.hashCode() are
designed to work together for fast hash lookups. The value returned
from the hashCode() is used to calculate the index of the bucket in
the hash table array. The equals() is then used to find the actual
matching object among all the objects that sit in the same bucket.
|
public
EqualsHashCode_Example(String s){
super();
this .s = s;
}
public
int hashCode(){
return s.hashCode();
}
private
String s = "";
|
|
|
Implement both hashCode() and
equals().
public
EqualsHashCode_Solution(String s){
super();
this .s = s;
}
public
int hashCode(){
return s.hashCode();
}
public
boolean equals( Object other ) {
return ( other instanceof EqualsHashCode_Solution
) &&
((EqualsHashCode_Solution)other).s.equals(
s );
}
private
String s = "";
|
|
|
RuleComparisonClassNameComparison |
Severe |
Using java.lang.Class.getName() bypasses the standard way of
comparing classes and may lead to problems, including security
violations. A standard way of comparing class objects is to use
java.lang.Class.equals(). This method bases the class equality on all
aspects of the class, for example, the ClassLoader that loaded the
class.
|
public
static void
main(String[] args){
Class c1 = null;
Class c2 = null;
if ( args.length > 1){
try {
c1 =
Class.forName( args[0] );
c2 =
Class.forName( args[1] );
System.out.println(
c1.getName().equals(c2.getName()));
} catch (
ClassNotFoundException e){
e.printStackTrace();
}
}
}
|
|
|
Change the code to use
java.lang.Class.equals() to establish class equality.
public
static void
main(String[] args){
Class c1 = null;
Class c2 = null;
if ( args.length > 1){
try {
c1 =
Class.forName( args[0] );
c2 =
Class.forName( args[1] );
System.out.println(
c1.equals( c2 ) );
} catch (
ClassNotFoundException e){
e.printStackTrace();
}
}
}
|
|
|
RuleNullEmptyArray |
Severe |
Returning null instead of empty array is error-prone in
majority of cases. By Java convention, the user of the interface that
returns array does not have to check for null, but should assume the
zero length array. Violating this convention is likely to result in
null pointer exceptions.
|
public
NullEmptyArray_Example() {
super();
}
public
void addValue( Integer value ) {
if ( values == null
) {
values = new
ArrayList(10);
}
values.add(
value );
}
public
Integer[] getValues() {
if ( values == null
) {
return null;
}
return (Integer[])values.toArray(
new Integer[ 0
] );
}
private
List values;
public
static void
main( String[] args ) {
NullEmptyArray_Example example = new NullEmptyArray_Example();
Integer[] values = example.getValues();
for ( int
i = 0; i < values.length; i++ )
{
System.out.println( values[ i ] );
}
}
|
|
|
Return zero length array instead
of null.
public
NullEmptyArray_Solution() {
super();
}
public
void addValue( Integer value ) {
if ( values == null
) {
values = new
ArrayList(10);
}
values.add(
value );
}
public
Integer[] getValues() {
return ( values == null ) ? new Integer[
0 ] :
(Integer[])values.toArray(
new Integer[ 0
] );
}
private
List values;
public
static void
main( String[] args ) {
NullEmptyArray_Solution example = new NullEmptyArray_Solution();
Integer[] values = example.getValues();
for ( int
i = 0; i < values.length; i++ )
{
System.out.println( values[ i ] );
}
}
|
|
|
RuleSerializationReadWriteObject |
Severe |
Declaring readObject() and writeObject() private is essential
to follow Java serialization protocol. Failing to do so may lead to
security problems in the application.
|
public
static class
SerializableObject implements
Serializable{
public void
writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
}
public void
readObject(ObjectInputStream s) throws IOException,
ClassNotFoundException {
s.defaultReadObject();
}
static final
long serialVersionUID = 123;
}
|
|
|
Declare readObject() and
writeObject() private.
public
static class
SerializableObject implements
Serializable{
private void
writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
}
private void
readObject(ObjectInputStream s) throws IOException,
ClassNotFoundException {
s.defaultReadObject();
}
static final
long serialVersionUID = 123;
}
|
|
|
RuleSerializationResolveObject |
Severe |
Declaring resolveObject() protected is essential in order to
follow Java serialization protocol. Failing to do so may lead to
security problems in the application.
|
public
class ResolveObject implements Serializable {
public static
ResolveObject getInstance() {
return INSTANCE;
}
private static
final ResolveObject
INSTANCE = new ResolveObject();
private ResolveObject() {
super();
}
public Object
readResolve() throws
ObjectStreamException {
return
INSTANCE;
}
static final
long serialVersionUID = 123;
}
|
|
|
Declare resolveObject() and
writeReplace() protected.
public
class ResolveObject implements Serializable {
public static
ResolveObject getInstance() {
return INSTANCE;
}
private static
final ResolveObject
INSTANCE = new ResolveObject();
private ResolveObject() {}
private Object
readResolve() throws
ObjectStreamException {
return
INSTANCE;
}
static final
long serialVersionUID = 123;
}
|
|
|
RuleSerializationSerializableField |
Severe |
Every field of a serializable class must be either
serializable or transient. Declaring non-transient fields of
non-serializable type inside of a serializable class will result in an
exception thrown during the serialization.
|
public static
final class
SomeType {}
public
class SerializableField implements Serializable {
public
SerializableField( String str ) {
super();
this .str
= str;
nonser = new
SomeType();
}
private String
str;
private SomeType
nonser;
private static
final long
serialVersionUID = 123;
}
|
|
|
Make the field type
serializable.
public
static final
class SomeType implements Serializable {}
public
class SerializableField()
implements Serializable {
public SerializableField( String str ) {
super();
this .str
= str;
nonser = new
SomeType();
}
private String
str;
private SomeType
nonser;
private static
final long
serialVersionUID = 123;
}
|
|
|
Declare the field transient.
public
static final
class SomeType {}
public
class SerializableField implements Serializable {
public SerializableField( String str ) {
super();
this .str
= str;
nonser = new
SomeType();
}
private String
str;
private transient
SomeType nonser;
private static
final long
serialVersionUID = 123;
}
|
|
|
nonsynchronizednotify |
Severe |
A thread that is currently locking sections of the code that
use a common lock should invoke java.lang.Object.notify() to tell the
blocking thread to resume processing. Calling notify() from
non-synchronized blocks will result in IllegalMonitorStateException,
because no lock has been obtained.
|
public
static void
main( String[] args ){
Lock lock = new
Lock();
final Consumer
consumer = new Consumer( lock );
final Producer
producer = new Producer( lock );
(new Thread( new
Runnable() {
public void run() {
consumer.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
producer.produce();
}
})).start();
}
public
static final
class Lock {
public Lock() {
super();
lock();
}
public boolean
isAvailable() {
return
available;
}
public void
lock() {
available = false;
}
public void
unlock() {
available = true;
}
private boolean
available = false;
}
public
static final
class Consumer {
public Consumer( Lock lock ) {
this .lock
= lock;
}
public void
consume() {
System.out.println( "Consumer:
consume was called" );
synchronized
( lock ) {
while ( !lock.isAvailable() ) {
System.out.println( "Consumer:
waiting..." );
try {
lock.wait();
} catch (InterruptedException e) {}
}
}
System.out.println( "Consumer:
exiting" );
}
private Lock
lock;
}
public
static final
class Producer {
public Producer( Lock lock ) {
this .lock
= lock;
}
public void
produce() {
System.out.println( "Producer:
produce was called" );
System.out.println( "Producer:
unlocking..." );
lock.unlock();
lock.notify();
System.out.println( "Producer:
exiting" );
}
private Lock
lock;
}
|
|
|
Place the call to
java.lang.Object.notify() inside of the synchronized block.
public
static void
main( String[] args ){
Lock lock = new
Lock();
final Consumer
consumer = new Consumer( lock );
final Producer
producer = new Producer( lock );
(new Thread( new
Runnable() {
public void run() {
consumer.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
producer.produce();
}
})).start();
}
public
static final
class Lock {
public Lock() {
super();
lock();
}
public boolean
isAvailable() {
return
available;
}
public void
lock() {
available = false;
}
public void
unlock() {
available = true;
}
private boolean
available = false;
}
public
static final
class Consumer {
public Consumer( Lock lock ) {
this .lock
= lock;
}
public void
consume() {
System.out.println( "Consumer:
consume was called" );
synchronized
( lock ) {
while ( !lock.isAvailable() ) {
System.out.println( "Consumer:
waiting..." );
try {
lock.wait();
} catch (InterruptedException e) {}
}
}
System.out.println( "Consumer:
exiting" );
}
private Lock
lock;
}
public
static final
class Producer {
public Producer( Lock lock ) {
this .lock
= lock;
}
public void
produce() {
System.out.println( "Producer:
produce was called" );
System.out.println( "Producer:
unlocking..." );
synchronized
( lock ) {
lock.unlock();
lock.notify();
}
System.out.println( "Producer:
exiting" );
}
private Lock
lock;
}
|
|
|
nonsynchronizednotifyall |
Severe |
A thread that is currently locking sections of the code that
use common lock should invoke java.lang.Object.notifyAll() to tell the
blocking threads to resume processing. Calling notify() from
non-synchronized blocks will result in IllegalMonitorStateException,
because no lock has been obtained.
|
public
static void
main( String[] args ){
Lock lock = new
Lock();
final Consumer
consumer1 = new Consumer( lock, 1 );
final Consumer
consumer2 = new Consumer( lock, 2 );
final Producer
producer = new Producer( lock );
(new Thread( new
Runnable() {
public void run() {
consumer1.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
consumer2.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
producer.produce();
}
})).start();
}
public
static final
class Lock {
public Lock() {
super();
lock();
}
public boolean
isAvailable() {
return
available;
}
public void
lock() {
available = false;
}
public void
unlock() {
available = true;
}
private boolean
available = false;
}
public
static final
class Consumer {
public Consumer( Lock lock, int
id ) {
this .lock
= lock;
this .id =
id;
}
public void
consume() {
System.out.println( "Consumer
" + id + ": consume was called" );
synchronized
( lock ) {
while ( !lock.isAvailable() ) {
System.out.println( "Consumer"
+ id + ": waiting..." );
try {
lock.wait();
} catch (InterruptedException e) {}
}
}
System.out.println( "Consumer"
+ id + ": exiting" );
}
private Lock
lock;
private int
id;
}
public
static final
class Producer {
public Producer( Lock lock ) {
this .lock
= lock;
}
public void
produce() {
System.out.println( "Producer:
produce was called" );
System.out.println( "Producer:
unlocking..." );
lock.unlock();
lock.notifyAll();
System.out.println( "Producer:
exiting" );
}
private Lock
lock;
}
|
|
|
Place the call to
java.lang.Object.notifyAll() inside of the synchronized block.
public
static void
main( String[] args ){
Lock lock = new
Lock();
final Consumer
consumer1 = new Consumer( lock, 1 );
final Consumer
consumer2 = new Consumer( lock, 2 );
final Producer
producer = new Producer( lock );
(new Thread( new
Runnable() {
public void run() {
consumer1.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
consumer2.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
producer.produce();
}
})).start();
}
public
static final
class Lock {
public Lock() {
super();
lock();
}
public boolean
isAvailable() {
return
available;
}
public void
lock() {
available = false;
}
public void
unlock() {
available = true;
}
private boolean
available = false;
}
public
static final
class Consumer {
public Consumer( Lock lock, int
id ) {
this .lock
= lock;
this .id =
id;
}
public void
consume() {
System.out.println( "Consumer
" + id + ": consume was called" );
synchronized
( lock ) {
while ( !lock.isAvailable() ) {
System.out.println( "Consumer"
+ id + ": waiting..." );
try {
lock.wait();
} catch (InterruptedException e) {}
}
}
System.out.println( "Consumer"
+ id + ": exiting" );
}
private Lock
lock;
private int
id;
}
public
static final
class Producer {
public Producer( Lock lock ) {
this .lock
= lock;
}
public void
produce() {
System.out.println( "Producer:
produce was called" );
System.out.println( "Producer:
unlocking..." );
synchronized
( lock ) {
lock.unlock();
lock.notifyAll();
}
System.out.println( "Producer:
exiting" );
}
private Lock
lock;
}
|
|
|
nonsynchronizedwait |
Severe |
A thread that needs to wait on a common lock should invoke
java.lang.Object.wait() to tell the JVM to suspend its processing
until it receives notification from another thread. Calling wait()
from non-synchronized blocks will result in
IllegalMonitorStateException, because no lock has been obtained.
|
public
static void
main( String[] args ){
Lock lock = new
Lock();
final Consumer
consumer = new Consumer( lock );
final Producer
producer = new Producer( lock );
(new Thread( new
Runnable() {
public void run() {
consumer.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
producer.produce();
}
})).start();
}
public
static final
class Lock {
public Lock() {
super();
lock();
}
public boolean
isAvailable() {
return
available;
}
public void
lock() {
available = false;
}
public void
unlock() {
available = true;
}
private boolean
available = false;
}
public
static final
class Consumer {
public Consumer( Lock lock ) {
this .lock
= lock;
}
public void
consume() {
System.out.println( "Consumer:
consume was called" );
while (
!lock.isAvailable() ) {
System.out.println(
"Consumer: waiting..." );
try {
lock.wait();
} catch (InterruptedException
e) {}
}
System.out.println( "Consumer:
exiting" );
}
private Lock
lock;
}
public
static final
class Producer {
public Producer( Lock lock ) {
this .lock
= lock;
}
public void
produce() {
System.out.println( "Producer:
produce was called" );
System.out.println( "Producer:
unlocking..." );
synchronized
( lock ) {
lock.unlock();
lock.notifyAll();
}
System.out.println( "Producer:
exiting" );
}
private Lock
lock;
}
|
|
|
Place the call to
java.lang.Object.wait() inside of the synchronized block.
public
static void
main( String[] args ){
Lock lock = new
Lock();
final Consumer
consumer = new Consumer( lock );
final Producer
producer = new Producer( lock );
(new Thread( new
Runnable() {
public void run() {
consumer.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
producer.produce();
}
})).start();
}
public
static final
class Lock {
public Lock() {
super();
lock();
}
public boolean
isAvailable() {
return
available;
}
public void
lock() {
available = false;
}
public void
unlock() {
available = true;
}
private boolean
available = false;
}
public
static final
class Consumer {
public Consumer( Lock lock ) {
this .lock
= lock;
}
public void
consume() {
System.out.println( "Consumer:
consume was called" );
synchronized
( lock ) {
while ( !lock.isAvailable() ) {
System.out.println( "Consumer:
waiting..." );
try {
lock.wait();
} catch (InterruptedException e) {}
}
}
System.out.println( "Consumer:
exiting" );
}
private Lock
lock;
}
public
static final
class Producer {
public Producer( Lock lock ) {
this .lock
= lock;
}
public void
produce() {
System.out.println( "Producer:
produce was called" );
System.out.println( "Producer:
unlocking..." );
synchronized
( lock ) {
lock.unlock();
lock.notifyAll();
}
System.out.println( "Producer:
exiting" );
}
private Lock
lock;
}
|
|
|
destroy |
Severe |
java.lang.Thread.destroy() method destroys the thread without
releasing the monitors or doing a cleanup. Calling this method is
likely to lead to synchronization and resource management issues.
|
public
static void
main( String[] args ){
Lock lock = new
Lock();
final Consumer
consumer = new Consumer( lock );
final Producer
producer = new Producer( lock );
(new Thread( new
Runnable() {
public void run() {
consumer.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
producer.produce();
}
})).start();
}
public
static final
class Lock {
public Lock() {
super();
lock();
}
public boolean
isAvailable() {
return
available;
}
public void
lock() {
available = false;
}
public void
unlock() {
available = true;
}
private boolean
available = false;
}
public
static final
class Consumer {
public Consumer( Lock lock ) {
this .lock
= lock;
}
public void
consume() {
System.out.println( "Consumer:
consume was called" );
synchronized
( lock ) {
while ( !lock.isAvailable() ) {
System.out.println( "Consumer:
waiting..." );
try {
lock.wait();
} catch (InterruptedException e) {}
}
}
System.out.println( "Consumer:
exiting" );
}
private Lock
lock;
}
public
static final
class Producer {
public Producer( Lock lock ) {
this .lock
= lock;
}
public void
produce() {
System.out.println( "Producer:
produce was called" );
synchronized
( lock ) {
Thread.currentThread().destroy();
}
System.out.println( "Producer:
exiting" );
}
private Lock
lock;
}
|
|
|
Do not call destroy(), rely on
default thread termination.
public
static void
main( String[] args ){
Lock lock = new
Lock();
final Consumer
consumer = new Consumer( lock );
final Producer
producer = new Producer( lock );
(new Thread( new
Runnable() {
public void run() {
consumer.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
producer.produce();
}
})).start();
}
public
static final
class Lock {
public Lock() {
super();
lock();
}
public boolean
isAvailable() {
return
available;
}
public void
lock() {
available = false;
}
public void
unlock() {
available = true;
}
private boolean
available = false;
}
public
static final
class Consumer {
public Consumer( Lock lock ) {
this .lock
= lock;
}
public void
consume() {
System.out.println( "Consumer:
consume was called" );
synchronized
( lock ) {
while ( !lock.isAvailable() ) {
System.out.println( "Consumer:
waiting..." );
try {
lock.wait();
} catch (InterruptedException e) {}
}
}
System.out.println( "Consumer:
exiting" );
}
private Lock
lock;
}
public
static final
class Producer {
public Producer( Lock lock ) {
this .lock
= lock;
}
public void
produce() {
System.out.println( "Producer:
produce was called" );
System.out.println( "Producer:
unlocking..." );
synchronized
( lock ) {
lock.unlock();
lock.notifyAll();
}
System.out.println( "Producer:
exiting" );
}
private Lock
lock;
}
|
|
|
resume |
Severe |
The following is the extract from Java doc (copyright by Sun
Microsystems): This method exists solely for use with suspend(), which
has been deprecated because it is deadlock-prone.
|
public
static final
Boolean lock = Boolean.TRUE;
public
static boolean
runCond = true;
public
static void
main( String[] args ) {
Runnable runnable = new Runnable() {
public void run() {
synchronized (lock){
while (
runCond ) {
System.out.println(
"Hello World!" );
try { wait( 1000
); } catch (InterruptedException e) {}
}
}
}
};
Thread thread = new
Thread( runnable );
thread.start();
try { Thread.sleep( 3000 ); } catch
(InterruptedException e) {}
thread.suspend();
try { Thread.sleep( 3000 ); } catch
(InterruptedException e) {}
thread.resume();
runCond =
false;
}
|
|
|
Remove the code that calls
suspend/resume thread.
Use provided SuspendSafeRunnable.
public
static abstract
class SuspendSafeRunnable extends StopSafeRunnable {
public void
doRun() {
checkForPause();
doWork();
}
public void
suspend() {
synchronized
( lock ) {
suspended = true;
lock.notifyAll();
}
}
public void
resume() {
synchronized
( lock ) {
suspended = false;
lock.notifyAll();
}
}
public boolean
isPaused() {
return
suspended;
}
protected abstract
void doWork();
private void
checkForPause() {
synchronized
( lock ) {
while ( suspended ) {
try {
lock.wait();
} catch (InterruptedException e) {}
}
}
}
private boolean
suspended = false;
private Object
lock = new Object();
}
public
static void
main( String[] args ) {
SuspendSafeRunnable runnable = new SuspendSafeRunnable() {
public void doWork() {
System.out.println(
"Hello World!" );
try { Thread.sleep( 1000 ); } catch
(InterruptedException e) {}
}
};
Thread thread = new
Thread( runnable );
thread.start();
try { Thread.sleep( 3000 ); } catch
(InterruptedException e) {}
runnable.suspend();
try { Thread.sleep( 3000 ); } catch
(InterruptedException e) {}
runnable.resume();
try { Thread.sleep( 3000 ); } catch
(InterruptedException e) {}
runnable.stop();
}
|
|
|
stop |
Severe |
The following is the extract from Java doc (copyright by Sun
Microsystems): This method is inherently unsafe. Stopping a thread
with Thread.stop causes it to unlock all of the monitors that it has
locked (as a natural consequence of the unchecked ThreadDeath
exception propagating up the stack). If any of the objects previously
protected by these monitors were in an inconsistent state, the damaged
objects become visible to other threads, potentially resulting in
arbitrary behavior.
|
public
static final
Boolean lock = Boolean.TRUE;
public
static void
main( String[] args ) {
Runnable runnable = new Runnable() {
public void run() {
synchronized (lock){
while ( true ) {
System.out.println(
"Hello World!" );
try { wait( 1000
); } catch (InterruptedException e) {}
}
}
}
};
Thread thread = new
Thread( runnable );
thread.start();
try { Thread.sleep( 3000 ); } catch
(InterruptedException e) {}
thread.stop();
}
|
|
|
The following is the extract
from Java doc (copyright by Sun Microsystems)
Many uses of stop should be replaced by code that simply modifies
some variable to indicate that the target thread should stop
running.
The target thread should check this variable regularly, and return
from its run method in an orderly fashion if the variable indicates
that it is to stop running. If the target thread waits for long
periods (on a condition variable, for example), the interrupt method
should be used to interrupt the wait.
Use provided StopSafeRunnable.
public
static abstract
class StopSafeRunnable implements Runnable {
public final
void run() {
while (
!stopped ) {
doRun();
}
}
public void
stop() {
stopped = true;
}
public boolean
isStopped() {
return
stopped;
}
protected abstract
void doRun();
private boolean
stopped = false;
}
public
static void
main(String[] args) {
StopSafeRunnable runnable = new StopSafeRunnable() {
public void doRun() {
System.out.println(
"Hello World" );
try { Thread.sleep( 1000 ); } catch
(InterruptedException e) {}
}
};
Thread thread = new
Thread( runnable );
thread.start();
try { Thread.sleep( 3000 ); } catch
(InterruptedException e) {}
runnable.stop();
}
|
|
|
suspend |
Severe |
The following is the extract from Java doc (copyright by Sun
Microsystems): This method has been deprecated, as it is inherently
deadlock-prone. If the target thread holds a lock on the monitor
protecting a critical system resource when it is suspended, no thread
can access this resource until the target thread is resumed. If the
thread that would resume the target thread attempts to lock this
monitor prior to calling resume, deadlock results. Such deadlocks
typically manifest themselves as "frozen" processes.
|
public
static final
Boolean lock = Boolean.TRUE;
public
static boolean
runCond = true;
public
static void
main( String[] args ) {
Runnable runnable = new Runnable() {
public void run() {
synchronized (lock){
while (
runCond ) {
System.out.println(
"Hello World!" );
try { wait( 1000
); } catch (InterruptedException e) {}
}
}
}
};
Thread thread = new
Thread( runnable );
thread.start();
try { Thread.sleep( 3000 ); } catch
(InterruptedException e) {}
thread.suspend();
try { Thread.sleep( 3000 ); } catch
(InterruptedException e) {}
thread.resume();
runCond =
false;
}
|
|
|
Remove the code that calls
suspend/resume thread.
Use provided SuspendSafeRunnable.
public
static abstract
class SuspendSafeRunnable extends StopSafeRunnable {
public void
doRun() {
checkForPause();
doWork();
}
public void
suspend() {
synchronized
( lock ) {
suspended = true;
lock.notifyAll();
}
}
public void
resume() {
synchronized
( lock ) {
suspended = false;
lock.notifyAll();
}
}
public boolean
isPaused() {
return
suspended;
}
protected abstract
void doWork();
private void
checkForPause() {
synchronized
( lock ) {
while ( suspended ) {
try {
lock.wait();
} catch (InterruptedException e) {}
}
}
}
private boolean
suspended = false;
private Object
lock = new Object();
}
public
static void
main( String[] args ) {
SuspendSafeRunnable runnable = new SuspendSafeRunnable() {
public void doWork() {
System.out.println(
"Hello World!" );
try { Thread.sleep( 1000 ); } catch
(InterruptedException e) {}
}
};
Thread thread = new
Thread( runnable );
thread.start();
try { Thread.sleep( 3000 ); } catch
(InterruptedException e) {}
runnable.suspend();
try { Thread.sleep( 3000 ); } catch
(InterruptedException e) {}
runnable.resume();
try { Thread.sleep( 3000 ); } catch
(InterruptedException e) {}
runnable.stop();
}
|
|
|
overridesynchronizable |
Severe |
Synchronized methods use this as a lock to prevent other
methods and blocks with the same lock from parallel execution.
Overriding the synchronized method with a non-synchronized one
introduces the potential for problems and data inconsistency in the
base class.
|
public
static class
Base {
public synchronized
void setValue( int value ) {
try {
Thread.sleep( 1000 ); } catch (InterruptedException
e) {}
this
.value = value;
}
public synchronized
int getValue() {
return
value;
}
protected int
value = 0;
}
public
static class
Derived extends Base {
public int
getValue() {
return this .value;
}
}
public
static void
main(String[] args) {
System.out.println(
"Testing Base..." );
test( new Base() );
try { Thread.sleep( 5000 ); } catch
(InterruptedException e) {}
System.out.println(
"Testing Derived..." );
test( new Derived() );
}
private
static void
test( final Base base ) {
Runnable writer = new Runnable() {
public void run() {
System.out.println(
"Writer set value " );
base.setValue(
1 );
}
};
Runnable reader = new Runnable() {
public void run() {
System.out.println(
"Reader: " + base.getValue() );
}
};
(new Thread( writer ) ).start();
(new Thread( reader ) ).start();
}
|
|
|
Change the overriding method to
be synchronized.
public
static class
Base {
public synchronized
void setValue( int value ) {
try {
Thread.sleep( 1000 ); } catch (InterruptedException
e) {}
this
.value = value;
}
public synchronized
int getValue() {
return
value;
}
protected int
value = 0;
}
public
static class
Derived extends Base {
public synchronized
int getValue() {
return this .value;
}
}
public
static void
main(String[] args) {
System.out.println(
"Testing Base..." );
test( new Base() );
try { Thread.sleep( 5000 ); } catch
(InterruptedException e) {}
System.out.println(
"Testing Derived..." );
test( new Derived() );
}
private
static void
test( final Base base ) {
Runnable writer = new Runnable() {
public void run() {
System.out.println(
"Writer set value " );
base.setValue(
1 );
}
};
Runnable reader = new Runnable() {
public void run() {
System.out.println(
"Reader: " + base.getValue() );
}
};
(new Thread( writer ) ).start();
(new Thread( reader ) ).start();
}
|
|
|
RuleCastingPrimitives |
Warning |
Casting primitives to a lower precision may result in the
information loss. Such casting indicates an incorrect choice of the
data type or a calculation that ignores properties of the data that it
operates on.
|
public
static void
main (String[] args){
double x = 3.1;
int i = (int)x;
System.out.println(i);
}
|
|
|
Change the underlying data type
to be lower precision.
public
static void
main (String[] args){
int i = 3;
System.out.println(i);
}
|
|
|
Update the calculation to
operate on the higher precision data type.
public
static void
main (String[] args){
double x = 3.1;
System.out.println(
x );
}
|
|
|
RuleCloneableOverrideClone |
Warning |
Implementing Cloneable without overriding clone() is
confusing and unclear. By implementing Cloneable, the class signals
that the default, shallow copy mechanism, is not sufficient, and that
the class needs to perform additional operations in order to create a
valid copy.
|
public
OverrideClone_Example() {
super();
elements =
new int[ 1
];
}
public
void setElements( int value ) {
for ( int
i = 0; i < elements.length; i++
) {
elements[ i ] = value;
}
}
public
void print() {
for ( int
i = 0; i < elements.length; i++
) {
System.out.println( elements[ i ] );
}
}
private
int[] elements;
public
static void
main(String[] args){
OverrideClone_Example original = new OverrideClone_Example();
original.setElements(
1 );
OverrideClone_Example clone = null;
try {
clone = (OverrideClone_Example)original.clone();
System.out.println( "Clone
before modification of original" );
clone.print();
original.setElements( 2 );
System.out.println( "Clone
after modification of original" );
clone.print();
}catch ( CloneNotSupportedException e ) {
e.printStackTrace();
}
}
|
|
|
Remove the implements Cloneable
declaration or implement the clone() properly.
public
OverrideClone_Solution() {
super();
elements =
new int[ 1
];
}
public
void setElements( int value ) {
for ( int
i = 0; i < elements.length; i++
) {
elements[ i ] = value;
}
}
public
void print() {
for ( int
i = 0; i < elements.length; i++
) {
System.out.println( elements[ i ] );
}
}
public
Object clone() throws CloneNotSupportedException {
OverrideClone_Solution clone = (OverrideClone_Solution)super .clone();
clone.elements
= new int[ elements.length ];
for ( int
i = 0; i < elements.length; i++
) {
clone.elements[ i ] = elements[ i ];
}
return clone;
}
private
int[] elements;
public
static void
main(String[] args){
OverrideClone_Solution original = new OverrideClone_Solution();
original.setElements(
1 );
OverrideClone_Solution clone = null;
try {
clone = (OverrideClone_Solution)original.clone();
System.out.println( "Clone
before modification of original" );
clone.print();
original.setElements( 2 );
System.out.println( "Clone
after modification of original" );
clone.print();
}
catch ( CloneNotSupportedException
e ) {
e.printStackTrace();
}
}
|
|
|
RuleCloneableImplementCloneable |
Warning |
Overriding clone() without implementing Cloneable is
confusing and unclear. By implementing Cloneable, the class signals
that the default, shallow copy mechanism, is not sufficient, and that
the class needs to perform additional operations in order to create a
valid copy.
|
public class
ExampleClone {
public ExampleClone() {
super();
elements = new int[
1 ];
}
public void
setElements( int value ) {
for ( int i = 0;
i < elements.length; i++ ) {
elements[ i ] =
value;
}
}
public void
print() {
for ( int i = 0;
i < elements.length; i++ ) {
System.out.println(
elements[ i ] );
}
}
public Object
clone() throws
CloneNotSupportedException {
ExampleClone
clone = (ExampleClone)super .clone();
clone.elements = new int[
elements.length ];
for ( int i = 0;
i < elements.length; i++ ) {
clone.elements[
i ] = elements[ i ];
}
return
clone;
}
private int[]
elements;
}
|
|
|
Use the implements Cloneable
declaration or change the name of the clone() method.
public
class ExampleClone implements Cloneable {
public ExampleClone() {
super();
elements = new int[
1 ];
}
public void
setElements( int value ) {
for ( int i = 0;
i < elements.length; i++ ) {
elements[ i ] =
value;
}
}
public void
print() {
for ( int i = 0;
i < elements.length; i++ ) {
System.out.println(
elements[ i ] );
}
}
public Object
clone() throws
CloneNotSupportedException {
ExampleClone
clone = (ExampleClone)super .clone();
clone.elements = new int[
elements.length ];
for ( int i = 0;
i < elements.length; i++ ) {
clone.elements[
i ] = elements[ i ];
}
return
clone;
}
private int[]
elements;
}
|
|
|
RuleComparisonInstanceOfEquals |
Warning |
Using instanceof in the equals() of an object is critical in
order to ensure that equals does not throw a ClassCastException and
returns the correct value.
|
public
InstanceofEquals_Example( String str
) {
super();
this .str = str;
}
public
String getString(){
return str;
}
public
int hashCode() {
return str.hashCode();
}
public
boolean equals(Object other){
return str.equals( ( (InstanceofEquals_Example)other
).getString() );
}
private
String str;
|
|
|
Use instanceof
in the equals() before casting.
public
InstanceofEquals_Solution( String
str ) {
super();
this .str = str;
}
public
String getString(){
return str;
}
public
int hashCode() {
return str.hashCode();
}
public
boolean equals(Object other) {
return ( other instanceof InstanceofEquals_Solution)
&&
str.equals( ( (InstanceofEquals_Solution)other
).getString() );
}
private
String str;
|
|
|
RuleComparisonReferenceEquality |
Warning |
Operators == and != compare references of objects. In most
cases, the two objects should be considered the same if they have the
same state. It is better to use java.lang.Object.equals() for
comparison, since this method allows for the implementer to make the
object state comparison.
|
public
static void
main( String[] args ){
Integer first = new
Integer( 1 );
Integer second = new Integer( 1
);
System.out.println(
first == second );
}
|
|
|
Use java.lang.Object.equals().
public
static void
main( String[] args ){
Integer first = new
Integer( 1 );
Integer second = new Integer( 1
);
System.out.println(
first.equals( second ) );
}
|
|
|
RuleExceptionsInstanceofCatch |
Warning |
Since Java supports multiple catch clauses in the try block,
there is never a need to check for the instance of the exception in
the catch clause. Catch clauses should be short, clear statements that
deal directly with the specific exception that was thrown. Adding
instanceof exception checks is likely to lead to non-standard,
difficult to maintain code.
|
public
static class
SomeException extends Exception{
public SomeException(String str, int
value ){
super( str );
this
.value = value;
}
public int
getValue() {
return
value;
}
private int
value;
}
public
static void
createProblem() throws
SomeException {
throw new
SomeException( "Problem", 10 );
}
public
static void
main(String[] args){
try {
createProblem();
} catch ( Exception
e ){
if ( e instanceof SomeException
) {
System.out.println(
((SomeException)e).getValue() );
} else {
System.out.println(
e.getLocalizedMessage() );
e.printStackTrace();
}
}
}
|
|
|
Remove instanceof checks on
exceptions and add dedicated catch clauses to the try/catch block.
public
static class
SomeException extends Exception{
public SomeException(String str, int
value ){
super( str );
this
.value = value;
}
public int
getValue() {
return
value;
}
private int
value;
}
public
static void
createProblem() throws
SomeException {
throw new
SomeException( "Problem", 10 );
}
public
static void
main(String[] args){
try {
createProblem();
} catch ( SomeException
e ){
System.out.println( ((SomeException)e).getValue() );
}
}
|
|
|
RuleLoopAssignLoopVariable |
Warning |
Assigning the for loop control variable in the body of the
loop is likely to lead to an error. For loops already provide the
default increment mechanism that should be followed. When the default
increment mechanism does not apply, the for loop should be changed to
a while or a do loop.
|
public
static void
main( String[] args ) {
for ( int
i = 0; i < args.length; i++ ) {
System.out.println( args[ i ] );
i = i + 1;
}
}
|
|
|
Move the assignment-to-control
variable from inside of the for loop to the increment clause of the
loop.
public
static void
main( String[] args ) {
for ( int
i = 0; i < args.length; i = i +
2 ) {
System.out.println( args[ i ] );
}
}
|
|
|
Change the for loop to a while
loop
public
static void
main( String[] args ) {
int i = 0;
while ( i < args.length ) {
System.out.println( args[ i ] );
i = i + 2;
}
}
|
|
|
RuleNullEnumeration |
Warning |
Returning null instead of empty Enumeration is error-prone in
majority of cases. By Java convention, the client of the interface
that returns Enumeration does not have to check for null, but should
assume the empty enumeration that returns false in hasMoreElements().
Violating this convention is likely to result in null pointer
exceptions.
|
public
NullEnumeration_Example() {
super();
}
public
void addValue( Integer value ) {
if ( values == null
) {
values = new
Vector(10);
}
values.add(
value );
}
public
Enumeration getValues() {
if ( values == null
) {
return null;
}
return values.elements();
}
private
Vector values;
public
static void
main( String[] args ) {
NullEnumeration_Example example = new NullEnumeration_Example();
for ( Enumeration
enum = example.getValues(); enum.hasMoreElements(); ) {
System.out.println( enum.nextElement() );
}
}
|
|
|
Return empty Enumeration instead
of null.
public
NullEnumeration_Solution() {
super();
}
public
void addValue( Integer value ) {
if ( values == null
) {
values = new
Vector(10);
}
values.add(
value );
}
public
Enumeration getValues() {
return ( values != null ) ? values.elements() : new Enumeration() {
public boolean hasMoreElements() {
return false;
}
public Object nextElement() {
throw new
UnsupportedOperationException();
}
};
}
private
Vector values;
public
static void
main( String[] args ) {
NullEnumeration_Example example = new NullEnumeration_Example();
for ( Enumeration
enum = example.getValues(); enum.hasMoreElements(); ) {
System.out.println( enum.nextElement() );
}
}
|
|
|
RuleNullIterator |
Warning |
Returning null instead of empty Iterator is error-prone in
majority of cases. By Java convention, the client of the interface
that returns Iterator does not have to check for null, but should
assume the empty Iterator that returns false in hasNext(). Violating
this convention is likely to result in null pointer exceptions.
|
public
NullIterator_Example() {
super();
}
public
void addValue( Integer value ) {
if ( values == null
) {
values = new
ArrayList(10);
}
values.add(
value );
}
public
Iterator getValues() {
if ( values == null
) {
return null;
}
return values.iterator();
}
private
List values;
public
static void
main( String[] args ) {
NullIterator_Example example = new NullIterator_Example();
for ( Iterator
iter = example.getValues(); iter.hasNext(); ) {
System.out.println( iter.next() );
}
}
|
|
|
Return empty Iterator instead of
null.
public
NullIterator_Solution() {
super();
}
public
void addValue( Integer value ) {
if ( values == null
) {
values = new
ArrayList(10);
}
values.add(
value );
}
public
Iterator getValues() {
return ( values != null ) ? values.iterator() :
new
Iterator() {
public boolean
hasNext() {
return false;
}
public Object
next() {
throw new UnsupportedOperationException();
}
public void
remove() {
throw new UnsupportedOperationException();
}
};
}
private
List values;
public
static void
main( String[] args ) {
NullIterator_Example example = new NullIterator_Example();
for ( Iterator
iter = example.getValues(); iter.hasNext(); ) {
System.out.println( iter.next() );
}
}
|
|
|
RulePortabilitySystemGetenv |
Warning |
The following is the extract from Java doc (copyright by Sun
Microsystems): The preferred way to extract system-dependent
information is the system properties of the
java.lang.System.getProperty methods and the corresponding getTypeName
methods of the Boolean, Integer, and Long primitive.
|
public
static void
main(String[] args){
System.out.println(
System.getenv("PATH") );
}
|
|
|
Use
java.lang.System.getProperty()
public
static void
main(String[] args){
System.out.println(
System.getProperty("PATH") );
}
|
|
|
RulePortabilityLineSeparators |
Warning |
Hard coding line separators as '\n' or '\r' makes the code
non portable. In Java there is a system property called
'line.separator' specifically designed for portability.
|
public
static void
main( String[] args) {
FileWriter fw = null;
try {
fw = new
FileWriter( "LineSeparators.txt" );
fw.write( "Hello
World!" );
fw.write("\n");
}catch (Exception
e){
e.printStackTrace();
} finally {
if ( fw !=
null ) {
try { fw.close(); } catch ( Exception
e ) {}
}
}
}
|
|
|
Use System.getProperty(
"line.separator" ) instead.
public
static void
main( String[] args) {
FileWriter fw = null;
try {
fw = new
FileWriter( "LineSeparators.txt" );
fw.write( "Hello
World!" );
fw.write( System.getProperty( "line.separator" ) );
}catch (Exception
e){
e.printStackTrace();
} finally {
if ( fw !=
null ) {
try { fw.close(); } catch ( Exception
e ) {}
}
}
}
|
|
|
RulePortabilityFileSeparator |
Warning |
Hard coding a file separator as '/' or '\' makes the code non
portable. The java.io.File class contains a special field called
'separator' specifically designed for portability.
|
public
static void
main(String[] args) {
if ( args.length > 2 ) {
File f = new File( args[ 0 ] + "/"
+ args[ 1 ] );
System.out.println( f.getAbsolutePath() );
}
}
|
|
|
Use java.io.File.separator
instead.
public
static void
main(String[] args) {
if ( args.length > 2 ) {
File f = new File( args[ 0 ] + File.separator + args[ 1 ] );
System.out.println( f.getAbsolutePath() );
}
}
|
|
|
RuleSwitchBreak |
Warning |
If a break is missing in a case branch of a switch statement,
the code continues by executing the next case branch. While there are
situations when this is the intent, they are quite rare. More often,
the intent is not to execute the following case but to break out of
the current case branch. Putting the break at the end of each case
branch makes code safer and more clear.
|
public
static void
main(String[] args){
switch ( args.length ){
case 0:
System.out.println("No
parameters");
case 1:
System.out.println("Only
one parameter: args[0]");
default :
System.out.println("Parameters: ");
for ( int i = 0,
n = args.length; i < n; i ++){
System.out.println(args[i]);
}
}
}
|
|
|
Insert the break statement.
public
static void
main(String[] args){
switch ( args.length ){
case 0:
System.out.println("No
parameters");
break ;
case 1:
System.out.println("Only
one parameter: args[0]");
break ;
default :
System.out.println("Parameters: ");
for ( int
i = 0, n = args.length; i < n; i
++){
System.out.println(args[i]);
}
break ;
}
}
|
|
|
RuleCastingObjectDowncast |
Recommendation |
Returning java.lang.Object is unclear and error prone. The
problem can manifest itself as the runtime ClassCastException. In
order to avoid this exception, the users of the interface need to read
(and often re-read) the documentation and maybe even look into the
details of the implementation.
|
public
ObjectDowncast_Example( String str )
{
super();
this .str = str;
}
public
Object getString(){
return str;
}
private
String str;
public
static void
main(String[] args){
ObjectDowncast_Example example = new ObjectDowncast_Example("Hello World!");
String str = (String)example.getString();
System.out.println(
str );
}
|
|
|
Change the method to return the
actual data type instead of java.lang.Object.
If the method returns objects of types that do not share a common
interface or a class, create the new interface or class and
encapsulate the object in it.
public
ObjectDowncast_Solution( String str
) {
super();
this .str = str;
}
public
String getString(){
return str;
}
private
String str;
public
static void
main(String[] args){
ObjectDowncast_Solution example = new ObjectDowncast_Solution("Hello World!");
String str = example.getString();
System.out.println(
str );
}
|
|
|
RuleComparisonPlaceConstants |
Recommendation |
Placing constants on the left side of equals() eliminates the
need for extra checking and reduces incidences of null pointer
exceptions.
|
public
static void
main( String[] args ) {
for ( int
i = 0; i < args.length; i++ ) {
if ( args[
i ].equals( "Hello World!" ) ) {
System.out.println(
i );
}
}
}
|
|
|
Change the
equals () expression to have the constant on the left side.
public
static void
main( String[] args ) {
for ( int
i = 0; i < args.length; i++ ) {
if ( "Hello World!".equals( args[ i ] ) ) {
System.out.println(
i );
}
}
}
|
|
|
RuleConditionalTernary |
Recommendation |
Using an if/else statement instead of a ternary operator
makes code longer than necessary. A ternary operator makes the code
more compact, while preserving the clarity of the if/else statement.
|
public
static void
main( String[] args ) {
ternaryVariableAssignment(
args );
ternaryReturn(
args );
}
public
static void
ternaryVariableAssignment( String[]
args ) {
int j = 0;
for ( int
i = 0; i < args.length; i++ ) {
if ( i
> 5 ) {
j = i;
} else {
j = 10;
}
}
}
public
static boolean
ternaryReturn( String[] args ) {
if ( args.length > 5 ) {
return true;
} else {
return false;
}
}
|
|
|
Use a ternary operator instead
of an if/else statement.
public
static void
main( String[] args ) {
ternaryVariableAssignment(
args );
ternaryReturn(
args );
}
public
static void
ternaryVariableAssignment( String[]
args ) {
int j = 0;
for ( int
i = 0; i < args.length; i++ ) {
j = (i > 5)
? i : 10;
}
}
public
static boolean
ternaryReturn( String[] args ) {
return (args.length > 5) ? true
: false;
}
|
|
|
RuleConditionalNegationIfCondition |
Recommendation |
Negation in if/else conditions makes the code more confusing.
Since if/else branches have statements for cases when the condition is
either true or false, there is no need to use negation.
|
public
static void
main( String[] args ) {
for ( int
i = 0; i < args.length; i++ ) {
if ( !
Character.isDigit(args[i].charAt(0)))
{
System.out.println("Not a digit");
} else {
System.out.println("A digit");
}
}
}
|
|
|
- Remove the negation
- Switch if and else parts
public
static void
main( String[] args ) {
for ( int
i = 0; i < args.length; i++ ) {
if
(Character.isDigit(args[i].charAt(0)))
{
System.out.println("A digit");
} else {
System.out.println("Not a digit");
}
}
}
|
|
|
RuleConstructorsAbstractMethods |
Recommendation |
Calling an abstract method in the object constructor of an
abstract class is problematic because its subclass may not have been
properly initialized. Such calls may result in NullPointerException or
an incorrect object state.
|
public
static abstract
class Base implements Comparable {
public Base( Object
other ) {
super();
System.out.println( compareTo( other ) );
}
}
public
static final
class Derived extends Base {
public Derived( String
str, Object other ) {
super( other );
this .str
= str;
}
public int
compareTo( Object other ) {
return
str.compareTo( other.toString() );
}
private String
str;
}
public
static void
main( String[] args ) {
Derived derived = new Derived( "Hello
World!", "Hello World!" );
}
|
|
|
Remove the call to abstract
method from the constructor.
public
static abstract
class Base implements Comparable {
public Base() {}
}
public
static final
class Derived extends Base {
public Derived( String
str ) {
super();
this .str
= str;
}
public int
compareTo( Object other ) {
return
str.compareTo( other.toString() );
}
private String
str;
}
public
static void
main( String[] args ) {
Derived derived = new Derived( "Hello
World!" );
System.out.println(
derived.compareTo( "Hello World!" )
);
}
|
|
|
RuleConstructorsOverridableMethods |
Recommendation |
Calling an overridable method in an object constructor is
problematic because the subclass may not have been properly
initialized. Such calls may result in NullPointerException or an
incorrect object state.
|
public
static class
Base {
public Base( Integer
value1, Integer value2 ) {
super();
this
.value1 = value1;
this
.value3 = getThirdValue();
this
.value2 = value2;
}
protected Integer
getThirdValue() {
return
value1;
}
protected Integer
value1;
protected Integer
value2;
protected Integer
value3;
}
public
static class
Derived extends Base {
public Derived( Integer
value1, Integer value2 ) {
super( value1, value2 );
}
protected Integer
getThirdValue() {
return new Integer( value2.intValue() * 10 );
}
}
public
static void
main( String[] args ) {
Derived derived = new Derived( new
Integer( 1 ), new Integer( 2
) );
}
|
|
|
Avoid complex computations in
constructors, use constructors only to initialize the fields of an
object.
public
static class
Base {
public Base( Integer
value1, Integer value2 ) {
this
.value1 = value1;
this
.value2 = value2;
}
public void
init() {
value3 = getThirdValue();
}
protected Integer
getThirdValue() {
return
value1;
}
protected Integer
value1;
protected Integer
value2;
protected Integer
value3;
}
public
static class
Derived extends Base {
public Derived( Integer
value1, Integer value2 ) {
super( value1, value2 );
}
protected Integer
getThirdValue() {
return new Integer( value2.intValue() * 10 );
}
}
public
static void
main( String[] args ) {
Derived derived = new Derived( new
Integer( 1 ), new Integer( 2
) );
derived.init();
}
|
|
|
RuleDeclarationCArrayStyle |
Recommendation |
The C-style array declaration is uncommon and archaic in
Java. Avoid it because most Java programmers are not familiar with it.
|
public
static void
main( String[] args ) {
String copy[] = new String[ args.length ];
for ( int
i = 0; i < args.length; i++ ) {
copy[ i ] = args[ i ];
}
}
|
|
|
Use the Java standard for
declaring arrays.
public
static void
main( String[] args ) {
String[] copy = new String[ args.length ];
for ( int
i = 0; i < args.length; i++ ) {
copy[ i ] = args[ i ];
}
}
|
|
|
RuleDeclarationMultipleDeclaration |
Recommendation |
Declaring several variables in the same statement makes the
code unclear. Such declaration leads to cumbersome initialization and
may increase the time needed to maintain the code.
|
public
static void
main( String[] args ) {
String var1 = null,
var2 = null;
var1 =
args[ 0 ];
var2 =
args[ 1 ];
}
private
int field1, field2;
|
|
|
Split the declaration into
separate declarations.
public
static void
main( String[] args ) {
String var1 = null;
String var2 = null;
var1 =
args[ 0 ];
var2 =
args[ 1 ];
}
private
int field1;
private
int field2;
|
|
|
RuleDeclarationMixedDeclaration |
Recommendation |
Declaring basic and array variables in the same statement
makes the code harder to comprehend. Such declaration leads to
cumbersome initialization and may increase the time needed to maintain
the code.
|
public
static void
main( String[] args ) {
String notClear1 = null, notClear2[] = null;
notClear1
= args[ 0 ];
notClear2[
0 ] = args[ 1
];
}
|
|
|
Split the declaration of basic
and array variables.
public
static void
main( String[] args ) {
String notClear1 = null;
String[] notClear2 = null;
notClear1
= args[ 0 ];
notClear2[
0 ] = args[ 1
];
}
|
|
|
RuleDeclarationDeclareConstants |
Recommendation |
Hard coding string literals is problematic for readability
and globalization. To improve the performance of the code and to
ensure that it is easy to read and make globalization compliant, avoid
declaring explicit string literals, especially duplicate ones.
|
public
static void
main(String[] args){
System.out.println(
"Hello" );
System.out.println(
"Bye" );
System.out.println(
"Hello" );
}
|
|
|
Declare constant
- Declare constant with a meaningful name to represent the
string literal.
- Replace all occurences of the string literal with
references to declared constant.
public
static void
main(String[] args) {
System.out.println(
HELLO );
System.out.println(
BYE );
System.out.println(
HELLO );
}
private
static final
String HELLO = "Hello";
private
static final
String BYE = "Bye";
|
|
|
RuleDeclarationInterfaceConstants |
Recommendation |
Interfaces in Java provide a mechanism for behavioral
inheritance without implementation inheritance. While it is possible
to declare constants in an interface and then extend this interface,
this is not recommended. Constants should be declared in an interface
or a class to which they logically belong. All other classes and
interfaces should then refer to the constant declarations.
|
public
interface ICommonParsingConstants
{
public static
final int
IF_TOKEN = 1;
public static
final int
WHILE_TOKEN = 2;
public static
final String
END_OF_LINE = "\n";
}
public
class Parser implements ICommonParsingConstants {
public void
processToken( int iToken ) {
if (
iToken == IF_TOKEN ) {
System.out.println(
"if" );
} else if ( iToken == WHILE_TOKEN ) {
System.out.println(
"while" );
}
}
}
public
class Tokenizer implements ICommonParsingConstants {
public String
getToken( String str ) {
return (
END_OF_LINE.equals( str ) ? "" :
str );
}
}
|
|
|
- Move each constant to the interface or to the class to
which it logically belongs.
- Remove the empty interface.
public
class Parser {
public static
final int
IF_TOKEN = 1;
public static
final int
WHILE_TOKEN = 2;
public void
processToken( int iToken ) {
if (
iToken == IF_TOKEN ) {
System.out.println(
"if" );
} else if ( iToken == WHILE_TOKEN ) {
System.out.println(
"while" );
}
}
}
public
class Tokenizer {
public static
final String
END_OF_LINE = "\n";
public String
getToken( String str ) {
return (
END_OF_LINE.equals( str ) ? "" :
str );
}
}
|
|
|
RuleExceptionsSpecificExceptions |
Recommendation |
For clarity it is better to maintain the complete list of
thrown exceptions in the throws clause.
|
public
class SpecificException extends Exception {
public SpecificException() {
super();
}
}
public
class Example {
public void
compute(Object subject) throws SpecificException {
if
(subject == null){
throw new
SpecificException();
}
}
public void
declaresException(Object o) throws Exception {
compute(o);
}
}
|
|
|
Declare all checked exceptions
in the throws clause.
public
class SpecificException extends Exception {
public SpecificException() {
super();
}
}
public
class Solution {
public void
compute(Object subject) throws SpecificException {
if
(subject == null) {
throw new
SpecificException();
}
}
public void
declaresException(Object o) throws SpecificException {
compute(o);
}
}
|
|
|
RuleExceptionsCatchGeneric |
Recommendation |
Creating broad catch clauses that handle generic exceptions
such as java.lang.Exception or java.lang.Throwable exceptions may make
it difficult to correct the problem. Since exceptions in Java are
designed as regular classes, it is better to create and throw a
specific type of an exception and to have a dedicated catch clause for
each exception.
|
public
static class
Loader {
public void
load() throws
UnsupportedOperationException, ClassNotFoundException {}
}
public
static void
main(String[] args) {
Loader loader = new
Loader();
try {
loader.load();
} catch ( Exception
e ) {
e.printStackTrace();
}
}
|
|
|
Create a dedicated catch clause
for each exception that is declared in the throws clause.
public
static class
Loader {
public void
load() throws
UnsupportedOperationException, ClassNotFoundException {}
}
public
static void
main(String[] args) {
Loader loader = new
Loader();
try {
loader.load();
} catch ( UnsupportedOperationException
e1 ) {
System.out.println( "load
is not implemented" );
} catch ( ClassNotFoundException
e2 ) {
System.out.println( "No
class " + e2.getMessage() );
}
}
|
|
|
RuleExceptionsExtendError |
Recommendation |
java.lang.Error is a runtime exception, primarily meant to be
extended and used for signaling runtime problems encountered by the
Java environment. The runtime exceptions are unchecked, that is the
compiler will not require the user of the interface to handle it.
Generally, it is preferable to create the checked exception instead of
the unchecked ones.
|
public
static class
ErrorSubclass extends Error{
public ErrorSubclass( String s ){
super(s);
}
}
public
class Example {
public void
run() {
throw new ErrorSubclass( "Problem" );
}
}
|
|
|
Extend java.lang.Exception.
public
static class
ExceptionSubclass extends
Exception {
public ExceptionSubclass( String s ){
super(s);
}
}
public
class Example {
public void
run() throws ExceptionSubclass {
throw new ExceptionSubclass( "Problem" );
}
}
|
|
|
RuleExceptionsExtendRuntimeException |
Recommendation |
java.lang.RuntimeException is a runtime exception, primarily
meant to be extended and used for signaling runtime problems
encountered by the Java environment. The runtime exceptions are
unchecked, that is the compiler will not require the user of the
interface to handle it. Generally, it is preferable to create the
checked exception instead of the unchecked ones.
|
public
static class
RuntimeExceptionSubclass extends
RuntimeException {
public RuntimeExceptionSubclass( String s ){
super(s);
}
}
public
class Example {
public void
run() {
throw new RuntimeExceptionSubclass( "Problem" );
}
}
|
|
|
Extend java.lang.Exception.
public
static class
ExceptionSubclass extends
Exception {
public ExceptionSubclass( String s ){
super(s);
}
}
public
class Example {
public void
run() throws ExceptionSubclass {
throw new ExceptionSubclass( "Problem" );
}
}
|
|
|
RuleExceptionsExtendThrowable |
Recommendation |
java.lang.Throwable is a common base class for both checked
and unchecked exceptions in Java. This class is not meant to be
extended directly. It is preferable to create the checked
exceptionthat descends from java.lang.Exception.
|
public
static class
ThrowableSubclass extends
Throwable {
public ThrowableSubclass( String s ){
super(s);
}
}
public
class Example {
public void
run() throws ThrowableSubclass {
throw new ThrowableSubclass( "Problem" );
}
}
|
|
|
Extend java.lang.Exception.
public
static class
ExceptionSubclass extends
Exception {
public ExceptionSubclass( String s ){
super(s);
}
}
public
class Example {
public void
run() throws ExceptionSubclass {
throw new ExceptionSubclass( "Problem" );
}
}
|
|
|
RuleExceptionsThrowError |
Recommendation |
java.lang.Error is a runtime exception, primarily meant to be
extended and used for signaling runtime problems encountered by the
Java environment. The runtime exceptions are unchecked, that is the
compiler will not require the user of the interface to handle it.
Generally, it is preferable to throw checked exceptions instead of the
unchecked ones.
|
public
class Example {
public void
run() {
throw new Error( "Problem"
);
}
}
|
|
|
- Extend the java.lang.Exception.
- Change the throws clause to use this new exception.
public
static class
ExceptionSubclass extends
Exception {
public ExceptionSubclass( String s ){
super(s);
}
}
public
class Example {
public void
run() throws ExceptionSubclass {
throw new ExceptionSubclass( "Problem" );
}
}
|
|
|
RuleExceptionsThrowException |
Recommendation |
java.lang.Exception is a generic checked exception in Java.
Since exceptions in Java are designed as regular classes, it is better
to create and throw specific sub classes of this generic class and to
have a dedicated catch clause for each exception.
|
public
class Example {
public void
run() throws Exception {
throw new Exception( "Problem"
);
}
}
|
|
|
Create the specific subclass of
java.lang.Exception.
public
static class
ExceptionSubclass extends
Exception {
public ExceptionSubclass( String s ){
super(s);
}
}
public
class Example {
public void
run() throws ExceptionSubclass {
throw new ExceptionSubclass( "Problem" );
}
}
|
|
|
RuleExceptionsThrowThrowable |
Recommendation |
java.lang.Throwable is a common base class for both checked
and unchecked exceptions in Java. This class is not meant to be used
directly in the throws clause. It is preferable to create and use
specific checked exceptions thfrom java.lang.Exception.
|
public
class Example {
public void
run() throws Throwable {
throw new Throwable( "Problem"
);
}
}
|
|
|
Create specific subclass of java.lang.Exception.
public
static class
ExceptionSubclass extends
Exception {
public ExceptionSubclass( String s ){
super(s);
}
}
public
class Example {
public void
run() throws ExceptionSubclass {
throw new ExceptionSubclass( "Problem" );
}
}
|
|
|
RuleInitializationInitializeStaticFields |
Recommendation |
Uninitialzed static fields may lead to problems such as
NullPointerException. Also, when a field is not explicitely
initialized, someone reading the code may not know the default value
and may have difficulty understanding the code.
|
public
interface IHandler {
public void
process( Object o );
}
public
static void
registerHandler( Class c, IHandler handler ) {
handlers.put(
c, handler );
}
public
static IHandler
getHandler( Class c ) {
return (IHandler)handlers.get(
c );
}
private
InitializeStaticFields_Example() {
super();
}
private
static Map
handlers;
public
static void
main(String[] args){
IHandler handler = new IHandler() {
public void process( Object
o ) {
System.out.println(
String.valueOf( o ) );
}
};
InitializeStaticFields_Example.registerHandler(
Boolean.class, handler );
InitializeStaticFields_Example.getHandler(
Boolean.class ).process(
Boolean.TRUE );
}
|
|
|
Always initilize static fields.
public
interface IHandler {
public void
process( Object o );
}
public
static void
registerHandler( Class c, IHandler handler ) {
handlers.put(
c, handler );
}
public
static IHandler
getHandler( Class c ) {
return (IHandler)handlers.get(
c );
}
private
InitializeStaticFields_Solution() {
super();
}
private
static Map
handlers = new HashMap(10);
public
static void
main(String[] args){
IHandler handler = new IHandler() {
public void process( Object
o ) {
System.out.println(
String.valueOf( o ) );
}
};
InitializeStaticFields_Solution.registerHandler(
Boolean.class, handler );
InitializeStaticFields_Solution.getHandler(
Boolean.class ).process(
Boolean.TRUE );
}
|
|
|
RuleInitializationChainingAssignments |
Recommendation |
Chaining assignment operators introduces the potential for
errors and makes the code unclear. The code is more robust and
self-explanatory if the assignments are separated.
|
public
static void
main( String[] args ) {
int x = 0;
int y = 0;
x = y = 10;
System.out.println(
x + " " + y );
}
|
|
|
Assign the variables in separate
statements.
public
static void
main( String[] args ) {
int x = 0;
int y = 0;
x = 10;
y = 10;
System.out.println(
x + " " + y );
}
|
|
|
RuleInitializationAssigningMethodParameter |
Recommendation |
Changing the value of a method parameter alters its original
intent and makes the code unclear.
|
public
static void
main( String[] args ) {
loop(
Integer.parseInt( args[ 0 ] ) );
}
private
static void
loop( int iMax ) {
iMax =
Math.max( iMax, 20 );
for ( int
i = 0; i < iMax; ) {
System.out.println( i );
}
}
|
|
|
Introduce another variable
instead of changing the value of a method parameter.
public
static void
main( String[] args ) {
loop(
Integer.parseInt( args[ 0 ] ) );
}
private
static void
loop( int iMax ) {
int iBoundedMax = Math.max( iMax, 20 );
for ( int
i = 0; i < iBoundedMax; ) {
System.out.println( i );
}
}
|
|
|
RuleLoopNoConditionForLoop |
Recommendation |
A for loop without a condition may lead to an infinite loop.
Such for loops are also more difficult to understand and maintain.
|
public
static void
main( String[] args ) {
for ( int
i = 0; ; i++ ) {
System.out.println( args[ i ] );
if ( i
< args.length ) {
break ;
}
}
}
|
|
|
Insert a condition into the for loop.
public
static void
main( String[] args ) {
for ( int
i = 0; i < args.length; i++ ) {
System.out.println( args[ i ] );
}
}
|
|
|
RuleLoopNoInitIncrForLoop |
Recommendation |
A for loop without an initializer and an incrementor is
misleading and error-prone. Besides hiding the intend, the loop may
lead to 'infinite loop' problems.
|
public
static void
main( String[] args ) {
int i = 0;
for ( ; i < args.length ; ) {
System.out.println( args[ i ] );
i++;
}
}
|
|
|
Replace the for loop with a while loop.
public
static void
main( String[] args ) {
int i = 0;
while ( i < args.length ) {
System.out.println( args[ i ] );
i++;
}
}
|
|
|
RuleLoopContinueStatement |
Recommendation |
Continue statements add additional, unnecessary complexity to
the body of a loop. Continue statements typically occur in long loops
with non-trivial logic. Such loops need to be refactored into smaller,
more clear calculations. After performing the refactoring, it will be
possible to replace the continue statement with an if statement
without complicating the code.
|
public
static void
main( String[] args ) {
for ( int
i = 0; i < args.length; i++ ) {
if ( i
< 5 ) {
continue ;
}
System.out.println( args[ i ] );
}
}
|
|
|
- While continue is part of
a long loop, refactor the loop using Extract Method refactoring
- Negate the condition before the continue
statement
- Move the code below the continue
statement to inside of the if
block
public
static void
main( String[] args ) {
for ( int
i = 0; i < args.length; i++ ) {
if ( i
>= 5 ) {
System.out.println(
args[ i ] );
}
}
}
|
|
|
RuleReflectionGetDeclaredField |
Recommendation |
Java reflection facilities are designed for generic
processing of class content. For example, the reflection is used by
Java Beans framework to enable generic inspection of properties of
Java Beans. The reflection is not meant to circumvent the standard
java field or method access. Thus, calling getDeclaredField() with the
hard coded name of the field is not recommended.
|
public
GetDeclaredField_Example() {
super();
}
public
void setValue( int value ) {
this .value = value;
}
public
int getValue() {
return value;
}
private
int value;
public
static void
main(String[] args){
try {
Field field =
GetDeclaredField_Example.class.getDeclaredField(
"value" );
GetDeclaredField_Example
obj = new
GetDeclaredField_Example();
field.set( obj, new
Integer( 1 ) );
System.out.println( obj.getValue() );
} catch (SecurityException
e) {
System.out.println( "Can't
access field 'value'" );
} catch (NoSuchFieldException
e) {
System.out.println( "No
field 'value'" );
} catch (IllegalArgumentException
e) {
System.out.println( e.getMessage() );
} catch (IllegalAccessException
e) {
System.out.println( "Can't
access private field 'value'" );
}
}
|
|
|
Use direct field access instead
of getDeclaredField() with the hard coded name of the field.
public
GetDeclaredField_Solution() {
super();
}
public
void setValue( int value ) {
this .value = value;
}
public
int getValue() {
return value;
}
private
int value;
public
static void
main(String[] args){
GetDeclaredField_Example obj = new GetDeclaredField_Example();
obj.setValue(
1 );
System.out.println(
obj.getValue() );
}
|
|
|
RuleReflectionGetDeclaredMethod |
Recommendation |
Java reflection facilities are designed for generic
processing of class content. For example, the reflection is used by
Java Beans framework to enable generic inspection of properties of
Java Beans. The reflection is not meant to circumvent the standard
java field or method access. Thus, calling getDeclaredMethod() with
the hard coded name of the method is not desirable.
|
public
GetDeclaredMethod_Example() {
super();
}
public
void setValue( int value ) {
this .value = value;
}
public
int getValue() {
return value;
}
private
int value;
public
static void
main(String[] args){
try {
Method method
= GetDeclaredMethod_Example.class.getDeclaredMethod(
"getValue", new Class[] { Void.class
} );
GetDeclaredMethod_Example
obj = new
GetDeclaredMethod_Example();
method.invoke( obj, new Object[] { new
Integer( 1 ) } );
System.out.println( obj.getValue() );
} catch (IllegalAccessException
e) {
System.out.println( "Can't
access private method 'getValue'" );
} catch (InvocationTargetException
e) {
System.out.println( "Problem
calling method" );
} catch (NoSuchMethodException
e) {
System.out.println( "No
method getValue" );
}
}
|
|
|
Use direct method call instead
of getDeclaredMethod() with the hard coded name of the method.
public
GetDeclaredMethod_Solution() {
super();
}
public
void setValue( int value ) {
this .value = value;
}
public
int getValue() {
return value;
}
private
int value;
public
static void
main(String[] args) {
GetDeclaredMethod_Example obj = new GetDeclaredMethod_Example();
obj.setValue(
1 );
System.out.println(
obj.getValue() );
}
|
|
|
RuleReflectionGetField |
Recommendation |
Java reflection facilities are designed for generic
processing of class content. For example, the reflection is used by
Java Beans framework to enable generic inspection of properties of
Java Beans. The reflection is not meant to circumvent the standard
java field or method access. Thus, calling getField() with the hard
coded name of the field is not desirable.
|
public
GetField_Example() {
super();
}
public
void setValue( int value ) {
this .value = value;
}
public
int getValue() {
return value;
}
private
int value;
public
static void
main(String[] args){
try {
Field field =
GetField_Example.class.getField( "value" );
GetField_Example
obj = new GetField_Example();
field.set( obj, new
Integer( 1 ) );
System.out.println( obj.getValue() );
} catch (SecurityException
e) {
System.out.println( "Can't
access field 'value'" );
} catch (NoSuchFieldException
e) {
System.out.println( "No
field 'value'" );
} catch (IllegalArgumentException
e) {
System.out.println( e.getMessage() );
} catch (IllegalAccessException
e) {
System.out.println( "Can't
access private field 'value'" );
}
}
|
|
|
Use direct field access instead
of getField() with the hard coded name of the field.
public
GetField_Solution() {
super();
}
public
void setValue( int value ) {
this .value = value;
}
public
int getValue() {
return value;
}
private
int value;
public
static void
main(String[] args){
GetField_Solution obj = new GetField_Solution();
obj.setValue(
1 );
System.out.println(
obj.getValue() );
}
|
|
|
RuleReflectionGetMethod |
Recommendation |
Java reflection facilities are designed for generic
processing of class content. For example, the reflection is used by
Java Beans framework to enable generic inspection of properties of
Java Beans. The reflection is not meant to circumvent the standard
java field or method access. Thus, calling getMethod() with the hard
coded name of the method is not desirable.
|
public
GetMethod_Example() {
super();
}
public
void setValue( int value ) {
this .value = value;
}
public
int getValue() {
return value;
}
private
int value;
public
static void
main(String[] args){
try {
Method method
= GetMethod_Example.class.getMethod(
"getValue", new Class[] { Void.class
} );
GetMethod_Example
obj = new GetMethod_Example();
method.invoke( obj, new Object[] { new
Integer( 1 ) } );
System.out.println( obj.getValue() );
} catch (IllegalAccessException
e) {
System.out.println( "Can't
access private method 'getValue'" );
} catch (InvocationTargetException
e) {
System.out.println( "Problem
calling method" );
} catch (NoSuchMethodException
e) {
System.out.println( "No
method getValue" );
}
}
|
|
|
Use direct method call instead
of getMethod with the hard coded name of the method.
public
GetMethod_Solution() {
super();
}
public
void setValue( int value ) {
this .value = value;
}
public
int getValue() {
return value;
}
private
int value;
public
static void
main(String[] args) {
GetMethod_Solution obj = new GetMethod_Solution();
obj.setValue(
1 );
System.out.println(
obj.getValue() );
}
|
|
|
RuleSerializationSerialVersionUID |
Recommendation |
The serialVersionUID field is used by Java serialization to
ensure correct deserialization of old objects when the underlying
class is altered. Failing to declare the serialVersionUID inside of a
serializable class will result in the IOException when attempting to
load old instances of this class.
|
public
class SerialVersionUID implements Serializable {
public SerialVersionUID() {
super();
}
public String
getString(){
return
field;
}
public void
setString(String field){
this
.field = field;
}
private String
field;
}
|
|
|
Declare static final
serialVersionUID field.
public
class SerialVersionUID implements Serializable {
public SerialVersionUID() {
super();
}
public String
getString(){
return
field;
}
public void
setString(String field){
this
.field = field;
}
private String
field;
static final
long serialVersionUID = 123;
}
|
|
|
RuleSerializationTransientField |
Recommendation |
The transient keyword is used in Java to denote the
non-serializable fields inside of serializable classes. Using this
keyword inside of non-serializable classes makes the code unclear and
cluttered.
|
public static
final class
SomeType {}
public
class SerializableField implements Serializable {
public
SerializableField( String str ) {
super();
this .str
= str;
nonser = new
SomeType();
}
private String
str;
private SomeType
nonser;
private static
final long
serialVersionUID = 123;
}
|
|
|
Make the field type
serializable.
public
static final
class SomeType implements Serializable {}
public
class SerializableField()
implements Serializable {
public SerializableField( String str ) {
super();
this .str
= str;
nonser = new
SomeType();
}
private String
str;
private SomeType
nonser;
private static
final long
serialVersionUID = 123;
}
|
|
|
Declare the field transient.
public
static final
class SomeType {}
public
class SerializableField implements Serializable {
public SerializableField( String str ) {
super();
this .str
= str;
nonser = new
SomeType();
}
private String
str;
private transient
SomeType nonser;
private static
final long
serialVersionUID = 123;
}
|
|
|
RuleStatementSurroundWithBraces |
Recommendation |
An unsurrounded if or loop statement introduces ambiguity and
the possibility of an error into the code. For example, if a line
below the statement is removed, then the next line below it becomes
part of the statement, which may not be the intent. Also, statements
without curly braces rely on indentation to communicate the meaning.
It is better to be explicit and demonstrate the intent by surrounding
all such statements with curly braces.
|
public
static void
main( String[] args ) {
surroundIf(
args );
surroundLoop(
args );
}
private
static void
surroundIf(String[] args) {
if ( args.length == 0 )
System.out.println( "No
arguments" );
else
System.out.println( args.length + " arguments");
System.out.println( args[ 0 ] );
}
private
static void
surroundLoop( String[] args ) {
for ( int
i = 0; i < args.length; i++ )
System.out.println(args[i]);
System.out.println(
"Done" );
}
|
|
|
Surround if and loop
statements with curly braces.
|
|
RuleStatementEmptyStatement |
Recommendation |
An empty statement is an extraneous piece of code. Such
statements make code more difficult to understand and maintain. Empty
statements typically arise as a result of accidental code deletions.
|
public
static void
main( String[] args ) {
emptyIf(
args );
emptyLoop(
args );
}
private
static void
emptyIf( String[] args ) {
if ( args.length == 0 ){
}else {
for ( int i = 0;
i < args.length; i++ ) {
if ( i > 0
);
System.out.println(
args[ i ] );
}
}
}
private
static void
emptyLoop( String[] args ) {
int i = 0;
while ( i < args.length );
System.out.println(
args[ i++ ] );
for ( int
j = 0; j < args.length; j++ ){}
}
|
|
|
Enclude the statements below
into the loop or if statement.
public
static void
main( String[] args ) {
emptyIf(
args );
emptyLoop(
args );
}
private
static void
emptyIf( String[] args ) {
if ( args.length > 0 ){
for ( int i = 0;
i < args.length; i++ ) {
if ( i > 1
) {
System.out.println( args[ i ] );
}
}
}
}
private
static void
emptyLoop( String[] args ) {
int i = 0;
while ( i < args.length ){
System.out.println( args[ i++ ] );
}
for ( int
j = 0; j < args.length; j++ ){
System.out.println( args[ j++ ] );
}
}
|
|
|
RuleSwitchDefaultLabel |
Recommendation |
A missing default label in a switch statement makes the code
incomplete because the intended behavior is not clear. Providing the
default label results in more explicit and robust code.
|
public
static final
int YESTERDAY =
0;
public
static final
int TODAY = 1;
public
static final
int TOMORROW =
2;
public
static void
main( String[] args ) {
int dateType = Integer.parseInt( args[ 0 ] );
Calendar calendar =
Calendar.getInstance();
switch ( dateType ) {
case 0:
calendar.add(
Calendar.DATE, -1 );
break ;
case 1:
break ;
case 2:
calendar.add(
Calendar.DATE, 1 );
break ;
}
System.out.println(
calendar.getTime() );
}
|
|
|
Add the default label to the switch statement.
public
static final
int YESTERDAY =
0;
public
static final
int TODAY = 1;
public
static final
int TOMORROW =
2;
public
static void
main( String[] args ) {
int dateType = Integer.parseInt( args[ 0 ] );
Calendar calendar =
Calendar.getInstance();
switch ( dateType ) {
case 0:
calendar.add(
Calendar.DATE, -1 );
break ;
case 1:
break ;
case 2:
calendar.add(
Calendar.DATE, 1 );
break ;
default :
break ;
}
System.out.println(
calendar.getTime() );
}
|
|
|
RuleSwitchBranch |
Recommendation |
The switch statement is meant to be used to avoid a lot of
nested if/else constructs. However, when there are just two choices,
using a switch statement is unnecessary and unclear.
|
public
static void
main(String[] args){
switch ( args.length ) {
case 0:
System.out.println("No parameters");
break ;
default :
System.out.println("Parameters: ");
for ( int
i = 0, n = args.length; i < n; i
++){
System.out.println(args[i]);
}
break ;
}
}
|
|
|
Use if/else instead.
public
static void
main(String[] args){
if ( args.length == 0 ) {
System.out.println("No
Paramaters");
} else {
System.out.println("Parameters:
");
for ( int i = 0,
n = args.length; i < n; i ++){
System.out.println(args[i]);
}
}
}
|
|
|
yield |
Recommendation |
Thread.yield() is meant to suspend the execution of the
current thread and let the other active threads run. Yield is
equivalent to Thread.sleep( 0 ). Even though the yield() is guaranteed
to work, there is no guarantee that another thread will not be
suspended again. This is because the task intended by yield() call may
not be fully accomplished before the control is returned to the
yielding thread. When designing threading behavior in complex,
large-scale programs it is not recommended to use yield to ensure the
order of thread execution. The lock-based wait()/notify() approach is
preferred instead.
|
public
static void
main( String[] args ){
Data data = new
Data();
final Consumer
consumer = new Consumer( data );
final Producer
producer = new Producer( data );
(new Thread( new
Runnable() {
public void run() {
consumer.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
producer.produce();
}
})).start();
}
public
static final
class Data {
public Data() {
super();
}
public int
getValue() {
return
value;
}
public void
setValue( int value ) {
this
.value = value;
}
private int
value = 0;
}
public
static final
class Consumer {
public Consumer( Data data ) {
this .data
= data;
}
public void
consume() {
System.out.println( "Consumer:
consume was called" );
System.out.println( "Consumer:
yielding..." );
Thread.yield();
System.out.println( "Consumer:
value is " + data.getValue() );
System.out.println( "Consumer:
exiting" );
}
private Data
data;
}
public
static final
class Producer {
public Producer( Data data ) {
this .data
= data;
}
public void
produce() {
System.out.println( "Producer:
produce was called" );
try {
Thread.sleep( 3000 ); } catch (InterruptedException
e) {}
data.setValue( 1
);System.out.println( "Producer: exiting" );
}
private Data
data;
}
|
|
|
Use wait()/notify() instead of
yield().
public
static void
main( String[] args ){
Data data = new
Data();
Lock lock = new
Lock();
final Consumer
consumer = new Consumer( data,
lock );
final Producer
producer = new Producer( data,
lock );
(new Thread( new
Runnable() {
public void run() {
consumer.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
producer.produce();
}
})).start();
}
public
static final
class Data {
public Data() {
super();
}
public int
getValue() {
return
value;
}
public void
setValue( int value ) {
this
.value = value;
}
private int
value = 0;
}
public
static final
class Lock {
public Lock() {
super();
lock();
}
public boolean
isAvailable() {
return
available;
}
public void
lock() {
available = false;
}
public void
unlock() {
available = true;
}
private boolean
available = false;
}
public
static final
class Consumer {
public Consumer( Data data, Lock
lock ) {
this .data
= data;
this .lock
= lock;
}
public void
consume() {
System.out.println( "Consumer:
consume was called" );
System.out.println( "Consumer:
waiting..." );
synchronized
( lock ) {
try {
lock.wait();
} catch (InterruptedException
e) {}
}
System.out.println( "Consumer:
value is " + data.getValue() );
System.out.println( "Consumer:
exiting" );
}
private Data
data;
private Lock
lock;
}
public
static final
class Producer {
public Producer( Data data, Lock
lock ) {
this .data
= data;
this .lock
= lock;
}
public void
produce() {
System.out.println( "Producer:
produce was called" );
synchronized
( lock ) {
try { Thread.sleep( 3000 ); } catch
(InterruptedException e) {}
data.setValue(
1 );System.out.println(
"Producer: exiting" );
lock.unlock();
lock.notifyAll();
}
}
private Data
data;
private Lock
lock;
}
|
|
|
extendthread |
Recommendation |
Extending Thread vs. implementing Runnable problem is an
example of the Inheritance vs. Delegation Principle. In most cases,
delegation is preferred to inheritance. Here as well, there is no good
reason to extend Thread class. Instead, it is cleaner to implement
Runnable interface and instantiate Thread to run it.
|
public
static class
ICThread extends Thread {
public void
run() {
System.out.println(
System.currentTimeMillis() );
}
}
public
static void
main( String[] args ) {
(new ICThread()).start();
}
|
|
|
Implement Runnable
instead of extending Thread.
public
static class
ICRunnable implements Runnable {
public void
run() {
System.out.println(
System.currentTimeMillis() );
}
}
public
static void
main( String[] args ) {
(new Thread( new
ICRunnable() )).start();
}
|
|
|
synchronizedthis |
Recommendation |
Synchronization problems are difficult to identify and debug,
so it is critical to consider what exactly is being locked and why.
Creating separate locks to protect different data sets is a good
practice that will lead to improved performance and clarity. Using
this as a lock is not recommended because it is also used as an
implicit lock when the method is declared synchronized.
|
public
int getNumber() {
synchronized ( this ) {
try {
Thread.sleep( 3000 ); } catch (InterruptedException
e) {}
return
number;
}
}
public
String getString() {
synchronized ( this ) {
return
str;
}
}
private
String str = "Hello
world!";
private
int number = 10;
public
static void
main( String[] args ) {
final SynchronizedThis_Example
data = new
SynchronizedThis_Example();
Runnable numberReader = new Runnable() {
public void run() {
System.out.println(
"Getting number..." );
System.out.println(
data.getNumber() );
}
};
Runnable stringReader = new Runnable() {
public void run() {
System.out.println(
"Getting string..." );
System.out.println(
data.getString() );
}
};
(new Thread( numberReader ) ).start();
(new Thread( stringReader ) ).start();
}
|
|
|
Declare an explicit separate
lock to protect different data sets.
public
int getNumber() {
synchronized ( numberLock ) {
try {
Thread.sleep( 3000 ); } catch (InterruptedException
e) {}
return
number;
}
}
public
String getString() {
synchronized ( stringLock ) {
return
str;
}
}
private
String str = "Hello
world!";
private
int number = 10;
private
Object numberLock = new Object();
private
Object stringLock = new Object();
public
static void
main( String[] args ) {
final SynchronizedThis_Solution
data = new
SynchronizedThis_Solution();
Runnable numberReader = new Runnable() {
public void run() {
System.out.println(
"Getting number..." );
System.out.println(
data.getNumber() );
}
};
Runnable stringReader = new Runnable() {
public void run() {
System.out.println(
"Getting string..." );
System.out.println(
data.getString() );
}
};
(new Thread( numberReader ) ).start();
(new Thread( stringReader ) ).start();
}
|
|
|
notifyall |
Recommendation |
The difference between java.lang.Object.notify() and
java.lang.Object.notifyAll() is that the first method tells JVM to
inform the first thread that is waiting on a lock, while the second
tells JVM to inform all the threads that are waiting on a lock. Since
the number of waiting threads is often difficult to anticipate, it is
recommended to call notifyAll() instead of notify().
|
public
static void
main( String[] args ){
Lock lock = new
Lock();
final Consumer
consumer1 = new Consumer( lock, 1 );
final Consumer
consumer2 = new Consumer( lock, 2 );
final Producer
producer = new Producer( lock );
(new Thread( new
Runnable() {
public void run() {
consumer1.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
consumer2.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
producer.produce();
}
})).start();
}
public
static final
class Lock {
public Lock() {
super();
lock();
}
public boolean
isAvailable() {
return
available;
}
public void
lock() {
available = false;
}
public void
unlock() {
available = true;
}
private boolean
available = false;
}
public
static final
class Consumer {
public Consumer( Lock lock, int
id ) {
this .lock
= lock;
this .id =
id;
}
public void
consume() {
System.out.println( "Consumer
" + id + ": consume was called" );
synchronized
( lock ) {
while ( !lock.isAvailable() ) {
System.out.println( "Consumer"
+ id + ": waiting..." );
try {
lock.wait();
} catch (InterruptedException e) {}
}
}
System.out.println( "Consumer"
+ id + ": exiting" );
}
private Lock
lock;
private int
id;
}
public
static final
class Producer {
public Producer( Lock lock ) {
this .lock
= lock;
}
public void
produce() {
System.out.println( "Producer:
produce was called" );
System.out.println( "Producer:
unlocking..." );
synchronized
( lock ) {
lock.unlock();
lock.notify();
}
System.out.println( "Producer:
exiting" );
}
private Lock
lock;
}
|
|
|
Call notifyAll() instead of
notify()
public
static void
main( String[] args ){
Lock lock = new
Lock();
final Consumer
consumer1 = new Consumer( lock, 1 );
final Consumer
consumer2 = new Consumer( lock, 2 );
final Producer
producer = new Producer( lock );
(new Thread( new
Runnable() {
public void run() {
consumer1.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
consumer2.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
producer.produce();
}
})).start();
}
public
static final
class Lock {
public Lock() {
super();
lock();
}
public boolean
isAvailable() {
return
available;
}
public void
lock() {
available = false;
}
public void
unlock() {
available = true;
}
private boolean
available = false;
}
public
static final
class Consumer {
public Consumer( Lock lock, int
id ) {
this .lock
= lock;
this .id =
id;
}
public void
consume() {
System.out.println( "Consumer
" + id + ": consume was called" );
synchronized
( lock ) {
while ( !lock.isAvailable() ) {
System.out.println( "Consumer"
+ id + ": waiting..." );
try {
lock.wait();
} catch (InterruptedException e) {}
}
}
System.out.println( "Consumer"
+ id + ": exiting" );
}
private Lock
lock;
private int
id;
}
public
static final
class Producer {
public Producer( Lock lock ) {
this .lock
= lock;
}
public void
produce() {
System.out.println( "Producer:
produce was called" );
System.out.println( "Producer:
unlocking..." );
synchronized
( lock ) {
lock.unlock();
lock.notifyAll();
}
System.out.println( "Producer:
exiting" );
}
private Lock
lock;
}
|
|
|
waitoutsideloop |
Recommendation |
The call to wait() suspends the execution of the calling
thread until another thread notifies it that the intended state of the
program is achieved. The wait() should occur based on the termination
condition. It is highly recommended that the wait() call is made
inside of the while loop, because another thread may invoke notify
even if the condition on which the current thread is waiting is not
met
|
public
static void
main( String[] args ){
Lock lock = new
Lock();
final Consumer
consumer1 = new Consumer( lock, 1 );
final Consumer
consumer2 = new Consumer( lock, 2 );
final Producer
producer = new Producer( lock );
(new Thread( new
Runnable() {
public void run() {
consumer1.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
consumer2.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
producer.produce();
}
})).start();
}
public
static final
class Lock {
public Lock() {
super();
lock();
}
public boolean
isAvailable() {
return
available;
}
public void
lock() {
available = false;
}
public void
unlock() {
available = true;
}
private boolean
available = false;
}
public
static final
class Consumer {
public Consumer( Lock lock, int
id ) {
this .lock
= lock;
this .id =
id;
}
public void
consume() {
System.out.println( "Consumer
" + id + ": consume was called" );
if (
!lock.isAvailable() ) {
System.out.println(
"Consumer" + id + ": waiting..." );
synchronized ( lock ) {
try {
lock.wait();
} catch (InterruptedException e) {}
}
}
System.out.println( "Consumer"
+ id + ": exiting" );
}
private Lock
lock;
private int
id;
}
public
static final
class Producer {
public Producer( Lock lock ) {
this .lock
= lock;
}
public void
produce() {
System.out.println( "Producer:
produce was called" );
synchronized
( lock ) {
lock.notifyAll();
}
synchronized
( lock ) {
System.out.println(
"Producer: unlocking..." );
lock.unlock();
lock.notifyAll();
}
System.out.println( "Producer:
exiting" );
}
private Lock
lock;
}
|
|
|
Place wait() inside of a while
loop.
public
static void
main( String[] args ){
Lock lock = new
Lock();
final Consumer
consumer1 = new Consumer( lock, 1 );
final Consumer
consumer2 = new Consumer( lock, 2 );
final Producer
producer = new Producer( lock );
(new Thread( new
Runnable() {
public void run() {
consumer1.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
consumer2.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
producer.produce();
}
})).start();
}
public
static final
class Lock {
public Lock() {
super();
lock();
}
public boolean
isAvailable() {
return
available;
}
public void
lock() {
available = false;
}
public void
unlock() {
available = true;
}
private boolean
available = false;
}
public
static final
class Consumer {
public Consumer( Lock lock, int
id ) {
this .lock
= lock;
this .id =
id;
}
public void
consume() {
System.out.println( "Consumer
" + id + ": consume was called" );
synchronized
( lock ) {
while ( !lock.isAvailable() ) {
System.out.println( "Consumer"
+ id + ": waiting..." );
try {
lock.wait();
} catch (InterruptedException e) {}
}
}
System.out.println( "Consumer"
+ id + ": exiting" );
}
private Lock
lock;
private int
id;
}
public
static final
class Producer {
public Producer( Lock lock ) {
this .lock
= lock;
}
public void
produce() {
System.out.println( "Producer:
produce was called" );
synchronized
( lock ) {
lock.notifyAll();
}
synchronized
( lock ) {
System.out.println(
"Producer: unlocking..." );
lock.unlock();
lock.notifyAll();
}
System.out.println( "Producer:
exiting" );
}
private Lock
lock;
}
|
|
|
synchronizedmethod |
Recommendation |
Synchronized method implicitly uses 'this' as a lock to
prevent other methods and blocks with the same lock from parallel
execution. There are several reasons why this is undesirable. Most
important is that since synchronized blocks are computationally
expensive, it is desirable to place as less code as possible inside of
these blocks. If the method that is declared as synchronized is
operating on more data that needs to be protected then such method is
unnecessarily expensive. Another reason is that some programmers are
not aware that 'this' is used as a lock when method is declared
synchronized, so it is better to be explicit
|
public
synchronized int getNumber() {
try { Thread.sleep( 3000 ); } catch
(InterruptedException e) {}
return number;
}
public
synchronized String getString() {
return str;
}
private
String str = "Hello
world!";
private
int number = 10;
public
static void
main( String[] args ) {
final SynchronizedMethod_Example
data = new
SynchronizedMethod_Example();
Runnable numberReader = new Runnable() {
public void run() {
System.out.println(
"Getting number..." );
System.out.println(
data.getNumber() );
}
};
Runnable stringReader = new Runnable() {
public void run() {
System.out.println(
"Getting string..." );
System.out.println(
data.getString() );
}
};
(new Thread( numberReader ) ).start();
(new Thread( stringReader ) ).start();
}
|
|
|
Create synchronized block inside
of the method that only locks necessary statements.
public
int getNumber() {
synchronized ( numberLock ) {
try {
Thread.sleep( 3000 ); } catch (InterruptedException
e) {}
return
number;
}
}
public
String getString() {
synchronized ( stringLock ) {
return
str;
}
}
private
String str = "Hello
world!";
private
int number = 10;
private
Object numberLock = new Object();
private
Object stringLock = new Object();
public
static void
main( String[] args ) {
final SynchronizedMethod_Solution
data = new
SynchronizedMethod_Solution();
Runnable numberReader = new Runnable() {
public void run() {
System.out.println(
"Getting number..." );
System.out.println(
data.getNumber() );
}
};
Runnable stringReader = new Runnable() {
public void run() {
System.out.println(
"Getting string..." );
System.out.println(
data.getString() );
}
};
(new Thread( numberReader ) ).start();
(new Thread( stringReader ) ).start();
}
|
|
|
whilesleep |
Recommendation |
The sleep() call causes the currently executing thread to
cease execution for the specified number of milliseconds. The thread
does not, however, loose the ownership of any of its monitors (locks).
Calling sleep inside of the while loop is known as a busy-wait, since
it does not allow other threads to operate on the shared data.
|
public
static void
main( String[] args ){
Lock lock = new
Lock();
final Consumer
consumer = new Consumer( lock );
final Producer
producer = new Producer( lock );
(new Thread( new
Runnable() {
public void run() {
consumer.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
producer.produce();
}
})).start();
}
public
static final
class Lock {
public Lock() {
super();
lock();
}
public boolean
isAvailable() {
return
available;
}
public void
lock() {
available = false;
}
public void
unlock() {
available = true;
}
private boolean
available = false;
}
public
static final
class Consumer {
public Consumer( Lock lock ) {
this .lock
= lock;
}
public void
consume() {
System.out.println( "Consumer:
consume was called" );
while (
!lock.isAvailable() ) {
System.out.println(
"Consumer: sleeping..." );
try { Thread.sleep( 1000 ); } catch
(InterruptedException e) {}
}
System.out.println( "Consumer:
exiting" );
}
private Lock
lock;
}
public
static final
class Producer {
public Producer( Lock lock ) {
this .lock
= lock;
}
public void
produce() {
System.out.println( "Producer:
produce was called" );
try {
Thread.sleep( 5000 ); } catch (InterruptedException
e) {}
System.out.println( "Producer:
unlocking..." );
lock.unlock();
System.out.println( "Producer:
exiting" );
}
private Lock
lock;
}
|
|
|
Change while()/sleep() to
wait()/notify().
public
static void
main( String[] args ){
Lock lock = new
Lock();
final Consumer
consumer = new Consumer( lock );
final Producer
producer = new Producer( lock );
(new Thread( new
Runnable() {
public void run() {
consumer.consume();
}
})).start();
(new Thread( new
Runnable() {
public void run() {
producer.produce();
}
})).start();
}
public
static final
class Lock {
public Lock() {
super();
lock();
}
public boolean
isAvailable() {
return
available;
}
public void
lock() {
available = false;
}
public void
unlock() {
available = true;
}
private boolean
available = false;
}
public
static final
class Consumer {
public Consumer( Lock lock ) {
this .lock
= lock;
}
public void
consume() {
System.out.println( "Consumer:
consume was called" );
synchronized
( lock ) {
while ( !lock.isAvailable() ) {
System.out.println( "Consumer:
waiting..." );
try {
lock.wait();
} catch (InterruptedException e) {}
}
}
System.out.println( "Consumer:
exiting" );
}
private Lock
lock;
}
public
static final
class Producer {
public Producer( Lock lock ) {
this .lock
= lock;
}
public void
produce() {
System.out.println( "Producer:
produce was called" );
System.out.println( "Producer:
unlocking..." );
synchronized
( lock ) {
lock.unlock();
lock.notifyAll();
}
System.out.println( "Producer:
exiting" );
}
private Lock
lock;
}
|
|
|