Blame
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1 | # **Core Java** |
| 2 | ||||
| 3 | | Version | Date | Description | Author | |
|||
| 4 | | ------- | ------------ | --------------------------------- | -------------- | |
|||
| 5 | | 1.0 | 30 Oct. 2018 | Core Java Coding Standards | Ronak Vakharia | |
|||
| 6 | | 2.0 | 12 Jan. 2026 | Added new Java features standards | Ravi Bansode | |
|||
| 7 | ||||
| 8 | ## **1. Introduction** |
|||
| 9 | ### **1.1 Purpose** |
|||
| 10 | This document describes the Java coding standard to be adhered to when writing software at Blast Radius. |
|||
| 11 | ### **1.2 Scope** |
|||
| 12 | This document is intended to be used by Blast Radius software developers. |
|||
| 13 | ### **1.3 Application** |
|||
| 14 | * In a team development environment, programmers often need to maintain code developed by other programmers. When a wide variety of coding styles exist in that environment, maintenance usually takes longer and is more defect-prone. Coding to a standard style creates an end product that is easier to read and maintain. Following an agreed-upon set of rules when programming also allows tools and utilities to be built that gather information about the source code. |
|||
| 15 | * If the rules and recommendations are followed, the result source code should be easy to maintain, have a style consistent with that produced by other developers on the project, and be free of practices that are error-prone. |
|||
| 16 | * The following is a list of some of the positive and negative impacts of using a set of coding standards: |
|||
| 17 | * **Consistency**: The team of developers can share a common view when reading, inspecting, and maintaining code. |
|||
| 18 | * **Reduce errors**: The coding standards restrict the use of error-prone language features, and formatting. |
|||
| 19 | * **Training**: Writing good code takes experience. If all existing code meets a minimal standard, new programmers are provided with good examples to start from. |
|||
| 20 | * **Freedom**: Developer implementation freedom is reduced. |
|||
| 21 | ||||
| 22 | ### **2. Rules and Recommendations** |
|||
| 23 | #### **2.1 General** |
|||
| 24 | ##### **2.1.1 Rules:** |
|||
| 25 | * Every time a rule in the coding standard is broken, there must be a clearly documented reason for doing so. |
|||
| 26 | * No coding standard can anticipate every problem that a programmer will face, and this one doesn't pretend to. But be prepared to defend a divergence from the standard, and do so in a comment. This will prevent a well-intentioned attempt to fix the code later. |
|||
| 27 | * When there is a difference between this coding standard and that of the client, the client's coding standard will take precedence. |
|||
| 28 | ##### **2.1.2 Recommendations:** |
|||
| 29 | * Code for readability first. Optimize for performance only when there's a demonstrated and proven performance problem, and a clearly understood working solution is already available. |
|||
| 30 | * **Exceptions**: when it's known beforehand that the performance of the code will be a critical bottleneck (e.g., network interface, disk access, etc), and where this coding standard indicates a practice for performance reasons. |
|||
| 31 | ||||
| 32 | #### **2.2 Coding Style** |
|||
| 33 | ##### **2.2.1 Rules:** |
|||
| 34 | * One level of indentation equals four spaces. Tabs must be replaced with four spaces. |
|||
| 35 | * Line length should not exceed 120 characters. |
|||
| 36 | * Opening and closing braces must be formatted using one of the following two methods: |
|||
| 37 | **Method 1:** On a line by themselves at the same level of indentation as the initiating keyword. |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 38 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 39 | |
| 40 | if ( byteBuf == null ) |
|||
| 41 | { |
|||
| 42 | byteBuf = new ByteBuffer( BUFFER_SIZE ); |
|||
| 43 | } |
|||
| 44 | ``` |
|||
| 45 | **Exception**: the closing brace of the do-while statement. |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 46 | ```Java |
| 47 | do |
|||
| 48 | { |
|||
| 49 | charNumber++; |
|||
| 50 | ch = clientName.getChar( charNumber ); |
|||
| 51 | ||||
| 52 | } while ( ch != '\0' ); |
|||
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 53 | ``` |
| 54 | **Method 2:** Opening brace on the same line as the initiating keyword and closing brace on a line by itself at the same level of indentation as the initiating keyword. |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 55 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 56 | if ( byteBuf == null ) { |
| 57 | byteBuf = new ByteBuffer( BUFFER_SIZE ); |
|||
| 58 | } |
|||
| 59 | ``` |
|||
| 60 | **Exception**: the closing brace of the do-while and try statements. |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 61 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 62 | do { |
| 63 | charNumber++; |
|||
| 64 | ch = clientName.getChar( charNumber ); |
|||
| 65 | ||||
| 66 | } while ( ch != '\0' ); |
|||
| 67 | ||||
| 68 | try { |
|||
| 69 | doSomeThing(); |
|||
| 70 | } catch (ExceptionClass e) { |
|||
| 71 | handleException(); |
|||
| 72 | } |
|||
| 73 | ``` |
|||
| 74 | * The two methods for formatting braces must not be mixed in same file or package. When modifying an existing file or package, the existing brace formatting style must be used and not changed. |
|||
| 75 | ||||
| 76 | ##### **2.2.2 Recommendations:** |
|||
| 77 | * Always write the left parenthesis directly after a function name (no intervening space). |
|||
| 78 | * Surround operators with spaces. |
|||
| 79 | Don't do things like this: |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 80 | ```java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 81 | newXPos+=recClient.Width()+recUpdate.Width()+margin*columns; |
| 82 | ``` |
|||
| 83 | It makes the operators easy to miss in a quick inspection. Do this instead: |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 84 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 85 | newXPos += recClient.Width() + recUpdate.Width() + margin * columns; |
| 86 | ``` |
|||
| 87 | * When a statement spans more than one line, break the statement in a way that each line is obviously syntactically incorrect, and indent the continued line or lines in a way that makes the continuation more visible. |
|||
| 88 | This prevents misreading of code. For example, the first line of the statement below could be interpreted as a complete statement: |
|||
| 89 | ``` |
|||
| 90 | Java |
|||
| 91 | String addressLine1 = getApartmentNumber() + getStreetAddress() + getStreetName(); |
|||
| 92 | ``` |
|||
| 93 | The code below provides a strong visual cue that it's continued on the next line: |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 94 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 95 | String addressLine1 = getApartmentNumber() + getStreetAddress() |
| 96 | + getStreetName(); |
|||
| 97 | ``` |
|||
| 98 | #### **2.3 Javadoc Comments** |
|||
| 99 | ##### **2.3.1 Rules:** |
|||
| 100 | * All classes and public methods must be preceded by a javadoc comment. |
|||
| 101 | * Class javadoc comments must contain a meaningful description of the class and an @author tag for each person who has worked on any part of the class. |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 102 | |
| 103 | ```Java |
|||
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 104 | /** |
| 105 | * <p>COPYRIGHT</p> |
|||
| 106 | * <p>Blast Radius -- Copyright (C) 2007 Blast Radius |
|||
| 107 | * All rights reserved. No part of this computer program |
|||
| 108 | * may be used or reproduced in any form by any |
|||
| 109 | * means without prior written permission of |
|||
| 110 | * Blast Radius.</p> |
|||
| 111 | * |
|||
| 112 | * <p>DESCRIPTION</p> |
|||
| 113 | * <p>Contains the dimensions of a bounding box that will enclose |
|||
| 114 | * zero or more graphical objects on a screen. The dimensions |
|||
| 115 | * will increase as required when items are added to the box.</p> |
|||
| 116 | * |
|||
| 117 | * @author Pete Peterson |
|||
| 118 | * @version 1.0 |
|||
| 119 | * @see related_class |
|||
| 120 | */ |
|||
| 121 | class BoundingBox { |
|||
| 122 | // ... |
|||
| 123 | } |
|||
| 124 | ``` |
|||
| 125 | * Method javadoc comments must include a meaningful description of the method, @param tags for each parameter, and, if applicable, @return and @exception tags. |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 126 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 127 | /** |
| 128 | * Adds a graphic object to the bounding box. If any of the object's |
|||
| 129 | * dimensions fall outside the box, the box's dimensions will grow to |
|||
| 130 | * include them. |
|||
| 131 | * |
|||
| 132 | * @param element the graphic element to be added to the box |
|||
| 133 | * @return true if the bounding box has expanded as a result of adding |
|||
| 134 | * the item, false if the dimensions are unchanged |
|||
| 135 | * @see see_related_method or material |
|||
| 136 | */ |
|||
| 137 | public boolean addItem( GraphicElement element ) { |
|||
| 138 | // ... |
|||
| 139 | } |
|||
| 140 | ``` |
|||
| 141 | ##### **2.3.2 Recommendations:** |
|||
| 142 | * Precede all non-public methods by a javadoc comment. |
|||
| 143 | ||||
| 144 | ### **2.4 Files** |
|||
| 145 | ###### **2.4.1 Rules:** |
|||
| 146 | * All files or packages must include the standard comment header with copyright information as required by the project. |
|||
| 147 | ##### **2.4.2 Recommendations:** |
|||
| 148 | * Try to keep the size of source files to less than one thousand lines. |
|||
| 149 | If source files become larger than this, there's a good chance that the design should be looked at again, with more functionality being moved into smaller objects. |
|||
| 150 | ||||
| 151 | ### **2.5 Naming Conventions** |
|||
| 152 | #### **2.5.1 Rules:** |
|||
| 153 | * Whenever mixed-case names are made up of more than one word, the first letter of each word following the first should be uppercase. |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 154 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 155 | closeFile(); |
| 156 | Button okButton; |
|||
| 157 | int accountTotal; |
|||
| 158 | int average; |
|||
| 159 | ``` |
|||
| 160 | * Whenever all-uppercase names are made up of more than one word, each word should be separated with an underscore. |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 161 | ```java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 162 | final int MAX_RANGE = 100; |
| 163 | final String BUTTON_NAME = "a button name"; |
|||
| 164 | ``` |
|||
| 165 | * Methods should be named so that they describe what the method does, starting with an active verb whenever possible. |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 166 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 167 | protected void hideComponent() { /* ... */ } |
| 168 | public int getAverage() { /* ... */ } |
|||
| 169 | public void refreshScreen() { /* ... */ } |
|||
| 170 | ``` |
|||
| 171 | * Accessor methods that retrieve an attribute should begin with "get". |
|||
| 172 | * Modifier methods should begin with "set". |
|||
| 173 | * Components should end with the type of the component. |
|||
| 174 | * This is to make the use of the variable a bit more obvious. For example `"ageTextField"` is unambiguous, while "age" may be mistaken for a numeric value. |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 175 | ```java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 176 | Button cancelButton = new Button(); |
| 177 | List customerNameList; |
|||
| 178 | BigGrid salesTrackingGrid; |
|||
| 179 | ``` |
|||
| 180 | * Class names should be a description of the class, and should be mixed case and begin with an uppercase letter. |
|||
| 181 | * Interface names should be a description of the interface's services. |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 182 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 183 | interface Compressable { /* ... */ } |
| 184 | interface Singleton { /* ... */ } |
|||
| 185 | ``` |
|||
| 186 | * Package names should be all lower case, and be prefixed with the Internet domain of your organization, but with the components of the domain name in reverse order. |
|||
| 187 | * For example, a for-profit organization with a domain `"ourcompany.com"` should create packages with names like `"com.ourcompany.ourproject"`, `"com.ourcompany.anotherproject"`, and `"com.ourcompany.ourproject.packageabc"`. Do not use capitals for the class A prefix. |
|||
| 188 | * File names should be camel case. Java source files should match the name of the class they contain. Whether the first letter of the filename is upper or lower case should depend on the use of the file, standard conventions should be followed. Filenames should only contain `'a'-'z', 'A'-'Z', '0'-'9' and '_'`. |
|||
| 189 | ||||
| 190 | ##### **2.5.2 Recommendations:** |
|||
| 191 | * Avoid the overuse of "i", "j", and "k" as variable names. |
|||
| 192 | * These variable names don't offer much in the way of providing readability. Compare: |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 193 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 194 | for ( int i = 0; i < totalMonthsDisplayed; i++ ) |
| 195 | { |
|||
| 196 | for ( j = 0; j < totalSales; j++ ) |
|||
| 197 | { |
|||
| 198 | refresh( i, j ); |
|||
| 199 | } |
|||
| 200 | } |
|||
| 201 | ``` |
|||
| 202 | ... with ... |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 203 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 204 | for ( int row = 0; row < totalMonthsDisplayed; row++ ) |
| 205 | { |
|||
| 206 | for ( column = 0; column < totalSales; column++ ) |
|||
| 207 | { |
|||
| 208 | refresh( row, column ); |
|||
| 209 | } |
|||
| 210 | } |
|||
| 211 | ``` |
|||
| 212 | ||||
| 213 | #### **2.6 Class Member Access** |
|||
| 214 | ##### **2.6.1 Rules:** |
|||
| 215 | * **All non-final fields should be private.** |
|||
| 216 | * Hiding data provides all the benefits of using abstract data types, and limits the places that data can be changed (and/or corrupted). Giving an object the ability to prevent access to its internal state also allows it to test itself whenever its state is queried or changed to ensure that the change is consistent with the object's internal state. |
|||
| 217 | ||||
| 218 | **Don't do this:** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 219 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 220 | public class Foo |
| 221 | { |
|||
| 222 | public int counter; |
|||
| 223 | } |
|||
| 224 | ``` |
|||
| 225 | **Do this instead:** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 226 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 227 | public class Foo |
| 228 | { |
|||
| 229 | private int counter; |
|||
| 230 | ||||
| 231 | public int getCounter( void ) |
|||
| 232 | { |
|||
| 233 | return counter; |
|||
| 234 | } |
|||
| 235 | ||||
| 236 | public void setCounter( int newCounter ) |
|||
| 237 | { |
|||
| 238 | counter = newCounter; |
|||
| 239 | } |
|||
| 240 | } |
|||
| 241 | ``` |
|||
| 242 | Another problem with public, protected, or package access fields becomes apparent when a variable name must be changed or removed from the class. Every derived class (or friends of the derived classes) that directly reference those variables must also be modified. |
|||
| 243 | * **Avoid having public member function return a reference to a private field.** |
|||
| 244 | * This allows outside code to change the state of an object without that object knowing about it. When possible, return a clone of the member object. |
|||
| 245 | #### **2.6.2 Recommendations:** |
|||
| 246 | * When a private data member has a public or protected access method, those methods should be used internally by the class as much as possible to provide consistency. |
|||
| 247 | **For example, given the following class:** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 248 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 249 | class Dimensions |
| 250 | { |
|||
| 251 | public void setDimensions( int width, int height ) { /* ... */ } |
|||
| 252 | ||||
| 253 | public int getWidth( void ) { /* ... */ } |
|||
| 254 | public int getHeight( void ) { /* ... */ } |
|||
| 255 | public int getArea( void ) { /* ... */ } |
|||
| 256 | ||||
| 257 | private int width; // width dimension |
|||
| 258 | private int height; // height dimension |
|||
| 259 | } |
|||
| 260 | ``` |
|||
| 261 | The code in the body of `Dimension.getArea()` could look like this: |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 262 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 263 | area = width * height; |
| 264 | ``` |
|||
| 265 | This would work, but will need to be modified if the height or width were changed from a data member to a calculated value. The following code would require no such modification: |
|||
| 266 | ||||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 267 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 268 | area = getWidth() * getHeight(); |
| 269 | ``` |
|||
| 270 | ||||
| 271 | #### **2.7 Class Constructors & Initialization** |
|||
| 272 | ##### **2.7.1 Rules:** |
|||
| 273 | * **Instance initialization code blocks should only be used in anonymous classes.** |
|||
| 274 | Putting construction code in constructors keeps all this related code together. When various code blocks initialize data throughout the class definition, understanding what's being initialized by whom becomes difficult. |
|||
| 275 | * **Always explicitly call the base class constructor in any place that the compiler would implicitly call it.** |
|||
| 276 | In any constructor that does not have as its first line of code (a) a call to a base class constructor, or (b) a call to another "this" constructor, the compiler will implicitly insert a call to `super()`. This can lead to confusion when the base class has no no-argument constructor. |
|||
| 277 | An additional benefit is that tracing execution through a constructor chain makes more sense when there are explicit calls to `super()`. |
|||
| 278 | * **Always explicitly create any constructor that the compiler would implicitly create, even if it contains no statements other than a call to super().** |
|||
| 279 | The compiler will explicitly create an empty no-argument constructor when you define no constructors for a class. Explicitly coding one prevents some confusion when debugging, and makes more sense when tracing through constructor chains. |
|||
| 280 | ||||
| 281 | ##### **2.7.2 Recommendations:** |
|||
| 282 | * Avoid methods with many arguments. |
|||
| 283 | * Avoid long and complex methods. |
|||
| 284 | ||||
| 285 | #### **2.8 Variables and Types** |
|||
| 286 | ##### **2.8.1 Rules:** |
|||
| 287 | * Avoid the use of numeric values in code, use symbolic values instead. |
|||
| 288 | **For example:** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 289 | ```java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 290 | colWidth = strWidth + 10 + ( level * 3 ); |
| 291 | // ... |
|||
| 292 | cellWidth = cellStrWidth + 10; |
|||
| 293 | ``` |
|||
| 294 | For the unfortunate programmer who has to maintain (or fix) the above code, there's no indication of what "10" or "3" represents, or whether both "10"s represent the same thing. And if the code maintainer finally figures out that "10" probably represents margin widths, and changes it to something smaller, there's an excellent chance that an instance will be missed, and more time will be wasted tracking down a difficult-to-find bug. Something like the following code should be written in the first place: |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 295 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 296 | final int COL_INDENT_WIDTH = 3; // amount to indent, per level |
| 297 | final int COL_LEFT_MARGIN = 5; // width of column left margin in pixels |
|||
| 298 | final int COL_RIGHT_MARGIN = 5; // width of column right margin in pixels |
|||
| 299 | ||||
| 300 | // pixel width of both margins combined |
|||
| 301 | final int COL_MARGINS_WIDTH = COL_LEFT_MARGIN + COL_RIGHT_MARGIN; |
|||
| 302 | ||||
| 303 | colWidth = strWidth + COL_MARGINS_WIDTH + ( level * COL_INDENT_WIDTH ); |
|||
| 304 | // ... |
|||
| 305 | cellWidth = cellStrWidth + COL_MARGINS_WIDTH; |
|||
| 306 | ``` |
|||
| 307 | * Variables are to be declared with the smallest possible scope. |
|||
| 308 | * Each variable is to be declared in a separate declaration statement, and have a commented description. |
|||
| 309 | **Don't do this:** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 310 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 311 | int moveAmount, selectedIndex; |
| 312 | ``` |
|||
| 313 | **Do this:** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 314 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 315 | int moveAmount; // number of items to move |
| 316 | int selectedIndex; // currently selected item |
|||
| 317 | ``` |
|||
| 318 | #### **2.9 Flow Control Statements** |
|||
| 319 | ###### **2.9.1 Rules:** |
|||
| 320 | * The code which follows a case label must always be terminated by a break statement. |
|||
| 321 | * The switch statement must always contain a default branch, which handles unexpected cases. |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 322 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 323 | switch (tag) |
| 324 | { |
|||
| 325 | case A: |
|||
| 326 | { |
|||
| 327 | // Do something |
|||
| 328 | break; |
|||
| 329 | } |
|||
| 330 | default: |
|||
| 331 | { |
|||
| 332 | // If no match in above cases, this is executed |
|||
| 333 | } |
|||
| 334 | } |
|||
| 335 | ``` |
|||
| 336 | **Exception: **when several different cases have identical handling. |
|||
| 337 | ||||
| 338 | * Use opening and closing braces in case blocks that contain variable declarations. |
|||
| 339 | * A switch statement must always contain a default branch which handles unexpected cases. |
|||
| 340 | * All flow control statements (if, else, while, for and do) must have opening and closing braces, even if the block contains no statements. |
|||
| 341 | Empty loops can be deceiving. In the example below, it's easy to mistakenly think that the last line is contained in the loop: |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 342 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 343 | for ( int rowNumber = initialRow; |
| 344 | ( rowNumber < totalRows ) && !getRow( rowNumber ).getIsSelected() && |
|||
| 345 | ( getRow( rowNumber ).getNumberOfCells() > 0 ); |
|||
| 346 | rowNumber++ ); |
|||
| 347 | numberOfRowFound = rowNumber; |
|||
| 348 | ``` |
|||
| 349 | When the braces are included, the empty loop is unmistakable: |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 350 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 351 | for ( int rowNumber = initialRow; |
| 352 | ( rowNumber < totalRows ) && !getRow( rowNumber ).getIsSelected() && |
|||
| 353 | ( getRow( rowNumber ).getNumberOfCells() > 0 ); |
|||
| 354 | rowNumber++ ) |
|||
| 355 | { |
|||
| 356 | // empty |
|||
| 357 | } |
|||
| 358 | numberOfRowFound = rowNumber; |
|||
| 359 | ``` |
|||
| 360 | * Use an explicit if...else... structure rather than the ternary statement. |
|||
| 361 | The ternary statement acts as a shortcut for the if...else... statement. However, the statement: |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 362 | ```java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 363 | ( rowNumber == 0 ) ? selectTable() : selectRow( rowNumber ); |
| 364 | ``` |
|||
| 365 | ...will likely compile the same as the more readable: |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 366 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 367 | if ( rowNumber == 0 ) |
| 368 | { |
|||
| 369 | selectTable(); |
|||
| 370 | } |
|||
| 371 | else |
|||
| 372 | { |
|||
| 373 | selectRow( rowNumber ); |
|||
| 374 | } |
|||
| 375 | ``` |
|||
| 376 | **Exception**: cases in which a ternary statement simplifies an assignment. |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 377 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 378 | double scale = ( isHalfScale() ? 0.5 : 1 ); |
| 379 | ||||
| 380 | // ...may be more readable than... |
|||
| 381 | ||||
| 382 | double scale; |
|||
| 383 | if ( isHalfScale() ) |
|||
| 384 | { |
|||
| 385 | scale = 0.5; |
|||
| 386 | } |
|||
| 387 | else |
|||
| 388 | { |
|||
| 389 | scale = 1; |
|||
| 390 | } |
|||
| 391 | ``` |
|||
| 392 | ##### **2.9.2 Recommendations:** |
|||
| 393 | * **Use inclusive lower limits and exclusive upper limits.** |
|||
| 394 | Rather than say that x is the interval x >= 0 and x <= 9, use the limits x >= 0 and x < 10. If you do so, then: |
|||
| 395 | * the difference in the limits is the size of the interval (interval = 10-0 rather than interval = 9-0+1) |
|||
| 396 | * if the interval is zero, the limits will be equal |
|||
| 397 | * the upper limit will never be less than the lower limit. |
|||
| 398 | ||||
| 399 | * **Avoid the use of continue.** |
|||
| 400 | `continue` tends to make loops harder to understand by concealing the structure of program execution. When tempted to use them, try to use an if...else.. statement instead. |
|||
| 401 | Exception: a `continue` statement near the top of a loop can sometimes make the code more readable by avoiding a series of if...else... statements, or a complex loop control structure. For example: |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 402 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 403 | while ( row != null ) |
| 404 | { |
|||
| 405 | if ( row.getIsEmpty() ) |
|||
| 406 | { |
|||
| 407 | // row cannot contain data, do not process |
|||
| 408 | continue; |
|||
| 409 | } |
|||
| 410 | // ... process row ... |
|||
| 411 | row = row.getNext(); |
|||
| 412 | } |
|||
| 413 | ``` |
|||
| 414 | * **Avoid the use of break in anything but the switch statement.** |
|||
| 415 | break tends to make loops harder to understand by concealing their exit conditions. Here's an example of break that confuses the structure of a loop: |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 416 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 417 | while ( true ) |
| 418 | { |
|||
| 419 | Rectangle areaRect = area.getRectArea(); |
|||
| 420 | if ( areaRect.pointInRect( firstPoint ) ) |
|||
| 421 | { |
|||
| 422 | if ( area.getType() == NON_CLIENT_AREA ) |
|||
| 423 | areaWidth -= areaRect.Width(); |
|||
| 424 | else |
|||
| 425 | break; |
|||
| 426 | } |
|||
| 427 | if ( area.getNext() == null ) |
|||
| 428 | break; |
|||
| 429 | else |
|||
| 430 | area = area.getNext(); |
|||
| 431 | } |
|||
| 432 | ||||
| 433 | ``` |
|||
| 434 | **Exception**: break can sometimes be used judiciously to avoid messy nested if...else statements in loops or a large number of boolean conditions in the control statement at the top: |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 435 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 436 | while ( ch != '\u0000' ) |
| 437 | { |
|||
| 438 | // if this is a metastring token, drop out of the loop |
|||
| 439 | if ( ch == '%' ) |
|||
| 440 | { |
|||
| 441 | tokenPosition = pos; |
|||
| 442 | break; // <------ early exit from loop |
|||
| 443 | } |
|||
| 444 | // ... more code to process 'ch' ... |
|||
| 445 | } |
|||
| 446 | ``` |
|||
| 447 | * **Avoid the use of multiple return statements.** |
|||
| 448 | The flow of control within a function is more difficult to understand when there are multiple exit points. |
|||
| 449 | ||||
| 450 | **Exception**: early returns can sometimes be used at the beginning of a function to avoid long if...else.. constructions. When used in this way, it's recommended that they are documented in a way that draws attention to them. |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 451 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 452 | boolean setName( Client client, String name ) |
| 453 | { |
|||
| 454 | int clientIndex = findClient( client ); |
|||
| 455 | if ( clientIndex == CLIENT_NOT_FOUND ) |
|||
| 456 | { |
|||
| 457 | return; // <--------- early return for invalid client |
|||
| 458 | } |
|||
| 459 | // ... |
|||
| 460 | } |
|||
| 461 | ``` |
|||
| 462 | #### **2.10 Comments** |
|||
| 463 | ##### **2.10.1 Recommendations:** |
|||
| 464 | * **Comment in a style that's easy to maintain.** |
|||
| 465 | Example of a hard-to-maintain commenting style: |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 466 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 467 | //--------------------------------------// |
| 468 | // get the size of the buffer // |
|||
| 469 | //--------------------------------------// |
|||
| 470 | ``` |
|||
| 471 | Example of easy-to-maintain commenting styles: |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 472 | ```Java |
| 473 | //------------------------------ |
|||
| 474 | // get the size of the buffer |
|||
| 475 | //------------------------------ |
|||
| 476 | ||||
| 477 | /* |
|||
| 478 | * get the size of the buffer |
|||
| 479 | * then allocate another buffer of the same size. |
|||
| 480 | */ |
|||
| 481 | ``` |
|||
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 482 | * **Comment as you go.** |
| 483 | Don't code with the intention of going back later and putting comments in. Try to comment first and code around the comments. |
|||
| 484 | * **Avoid non-data endline comments.** |
|||
| 485 | Endline comments are difficult to maintain because they have to aligned properly, are difficult to edit, and there usually isn't enough space to comment effectively. |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 486 | |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 487 | **Exceptions**: data declarations, and comments at the end of blocks. Example of these: |
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 488 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 489 | int numberOfKeys; // number of virtual keystrokes |
| 490 | // ... |
|||
| 491 | } // switch |
|||
| 492 | } // for |
|||
| 493 | ``` |
|||
| 494 | * **Don't duplicate the code in the comment.** |
|||
| 495 | The following comments are redundant: |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 496 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 497 | // if the allocation flag is more than zero |
| 498 | if ( allocSize > 0 ) |
|||
| 499 | { |
|||
| 500 | // initialize buffer size to allocSize |
|||
| 501 | bufferSize = allocSize; |
|||
| 502 | } |
|||
| 503 | ``` |
|||
| 504 | This comment explains what's actually happening: |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 505 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 506 | // if a buffer was successfully allocated, record the size |
| 507 | if ( allocSize > 0 ) |
|||
| 508 | { |
|||
| 509 | bufferSize = allocSize; |
|||
| 510 | } |
|||
| 511 | ``` |
|||
| 512 | #### **2.11 Exception-Handling** |
|||
| 513 | ##### **2.11.1 Rules:** |
|||
| 514 | * **Where there is a clear separation between presentation classes and integration classes, the integration class should repackage checked exceptions to pass to the presentation class to preserve encapsulation.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 515 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 516 | public class UserDAOImpl implements UserDAO |
| 517 | { |
|||
| 518 | findByUserId(String userId) |
|||
| 519 | { |
|||
| 520 | User user = null; |
|||
| 521 | try |
|||
| 522 | { |
|||
| 523 | user = (User)getSqlMapClientTemplate().queryForObject(GET_USER, userId); |
|||
| 524 | } |
|||
| 525 | catch(DataAccessException ex) |
|||
| 526 | { |
|||
| 527 | //service class, must repackage exception |
|||
| 528 | logger.error("could not access database",ex); |
|||
| 529 | throw new ApplicationException("could not connect to database", ex); |
|||
| 530 | } |
|||
| 531 | } |
|||
| 532 | } |
|||
| 533 | ``` |
|||
| 534 | * **Preserve the original stack trace information throughout the life of the Exception.** |
|||
| 535 | If an exception is caught in an integration class, and an exception is to be re-thrown to the presentation class, the newly thrown exception must preserve the caught exception stack trace. |
|||
| 536 | * **Exceptions should be logged once whenever they are caught except as outlined by rule 4 and dealt with where they are caught, exceptions should not be passed to generic exception handling methods.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 537 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 538 | public class UserDAOImpl implements UserDAO |
| 539 | { |
|||
| 540 | findByUserId(String userId) |
|||
| 541 | { |
|||
| 542 | User user = null; |
|||
| 543 | try |
|||
| 544 | { |
|||
| 545 | user = (User)getSqlMapClientTemplate().queryForObject(GET_USER, userId); |
|||
| 546 | } |
|||
| 547 | catch(DataAccessException ex) |
|||
| 548 | { |
|||
| 549 | //service class, must repackage exception |
|||
| 550 | logger.error("could not access database",ex); |
|||
| 551 | throw new ApplicationException("could not connect to database", ex); |
|||
| 552 | } |
|||
| 553 | } |
|||
| 554 | } |
|||
| 555 | ``` |
|||
| 556 | * **Swallowed checked exceptions should be avoided, and where they cannot be avoided proper commenting should document why an empty catch block exists. The general exception java.lang.Exception should never be caught.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 557 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 558 | try |
| 559 | { |
|||
| 560 | AdditionalData additionalData = getAdditionalUserData(userId); |
|||
| 561 | } |
|||
| 562 | catch(UserNotRegistered ex) |
|||
| 563 | { |
|||
| 564 | /* |
|||
| 565 | * thrown if the user has not been registered in the third party service |
|||
| 566 | * which isn't necessary for updating the user information |
|||
| 567 | */ |
|||
| 568 | } |
|||
| 569 | ``` |
|||
| 570 | * **A function should not contain split try catch blocks, where multiple checked exceptions are caught they should be handled in subsequent catch blocks or if necessary in nested try blocks.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 571 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 572 | try |
| 573 | { |
|||
| 574 | User user = (User)getSqlMapClientTemplate().queryForObject(GET_USER, userId); |
|||
| 575 | UserMetaData userMetaData = userMetaDataService.getUserMetaDataByUser(user); |
|||
| 576 | } |
|||
| 577 | catch(SQLException ex) |
|||
| 578 | { |
|||
| 579 | throw new ApplicationException("could not access database" , ex); |
|||
| 580 | } |
|||
| 581 | catch(WebServiceConnectionException ex) |
|||
| 582 | { |
|||
| 583 | throw new ApplicationException("could not connect to web service" , ex); |
|||
| 584 | } |
|||
| 585 | ``` |
|||
| 586 | * **List all normal exceptions thrown in the throws clause, not just base class of related exceptions.** |
|||
| 587 | In other words, if your method throws MyException and MyDerivedException (which is derived from MyException), don't just declare MyException. While this is legal in the language, it hides useful information from those using your class. |
|||
| 588 | ||||
| 589 | * **Catch subclasses before base classes.** |
|||
| 590 | If you catch the base class of an exception before trying to catch classes derived from it, the first catch block will catch all derived exceptions: |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 591 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 592 | try |
| 593 | { |
|||
| 594 | doSomething(); |
|||
| 595 | } |
|||
| 596 | catch ( BaseExceptionClass e ) // this should come after the next block |
|||
| 597 | { |
|||
| 598 | // ... |
|||
| 599 | } |
|||
| 600 | catch ( DerivedFromBaseExceptionClass e ) |
|||
| 601 | { |
|||
| 602 | // ... will never get here, because the previous catch |
|||
| 603 | // statement will catch all classes derived from |
|||
| 604 | // BaseExceptionClass |
|||
| 605 | } |
|||
| 606 | ``` |
|||
| 607 | #### **2.12 Event-Handling** |
|||
| 608 | ##### **2.12.1 Rules:** |
|||
| 609 | ||||
| 610 | * **Don't call message handlers directly.** |
|||
| 611 | If another function needs to invoke code identical to that in a message handler, move the code out into a different function, and have both the message handler and the other function call the common code. |
|||
| 612 | ||||
| 613 | * **Event handler methods should be lightweight and delegate complex logic to service or business logic classes.** |
|||
| 614 | Event handlers should primarily coordinate actions and delegate to appropriate service methods rather than containing complex business logic themselves. |
|||
| 615 | ||||
| 616 | #### **2.13 Memory Management and Performance** |
|||
| 617 | ##### **2.13.1 Rules:** |
|||
| 618 | ||||
| 619 | * **Always use try-with-resources for AutoCloseable resources.** |
|||
| 620 | This ensures proper resource cleanup and prevents resource leaks. |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 621 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 622 | // Good |
| 623 | try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) { |
|||
| 624 | return reader.readLine(); |
|||
| 625 | } |
|||
| 626 | ||||
| 627 | // Bad |
|||
| 628 | BufferedReader reader = new BufferedReader(new FileReader("file.txt")); |
|||
| 629 | try { |
|||
| 630 | return reader.readLine(); |
|||
| 631 | } finally { |
|||
| 632 | reader.close(); |
|||
| 633 | } |
|||
| 634 | ``` |
|||
| 635 | * **Close resources in the reverse order of their creation in finally blocks (when try-with-resources cannot be used).** |
|||
| 636 | * **Avoid creating unnecessary objects in loops.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 637 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 638 | // Bad |
| 639 | for (int i = 0; i < 1000; i++) { |
|||
| 640 | String status = "Processing " + i; |
|||
| 641 | log.debug(status); |
|||
| 642 | } |
|||
| 643 | ||||
| 644 | // Good |
|||
| 645 | for (int i = 0; i < 1000; i++) { |
|||
| 646 | log.debug("Processing {}", i); |
|||
| 647 | } |
|||
| 648 | ``` |
|||
| 649 | * **Never ignore InterruptedException without re-interrupting the thread.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 650 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 651 | try { |
| 652 | Thread.sleep(1000); |
|||
| 653 | } catch (InterruptedException e) { |
|||
| 654 | Thread.currentThread().interrupt(); // Preserve interrupt status |
|||
| 655 | throw new RuntimeException("Thread interrupted", e); |
|||
| 656 | } |
|||
| 657 | ``` |
|||
| 658 | ##### **2.13.2 Recommendations:** |
|||
| 659 | * **Use StringBuilder or StringBuffer for string concatenation in loops.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 660 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 661 | // Bad |
| 662 | String result = ""; |
|||
| 663 | for (String item : items) { |
|||
| 664 | result += item; |
|||
| 665 | } |
|||
| 666 | ||||
| 667 | // Good |
|||
| 668 | StringBuilder result = new StringBuilder(); |
|||
| 669 | for (String item : items) { |
|||
| 670 | result.append(item); |
|||
| 671 | } |
|||
| 672 | ``` |
|||
| 673 | * **Avoid premature optimization. Profile first, then optimize.** |
|||
| 674 | * **Consider using object pooling only for expensive-to-create objects (e.g., database connections, thread pools).** |
|||
| 675 | * **Avoid using finalizers. Use try-with-resources or explicit cleanup methods instead.** |
|||
| 676 | * **Be aware of autoboxing/unboxing costs in performance-critical code.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 677 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 678 | // Bad - autoboxing in loop |
| 679 | Integer sum = 0; |
|||
| 680 | for (int i = 0; i < 1000000; i++) { |
|||
| 681 | sum += i; // Boxing and unboxing on each iteration |
|||
| 682 | } |
|||
| 683 | ||||
| 684 | // Good |
|||
| 685 | int sum = 0; |
|||
| 686 | for (int i = 0; i < 1000000; i++) { |
|||
| 687 | sum += i; |
|||
| 688 | } |
|||
| 689 | ``` |
|||
| 690 | * **Use lazy initialization for expensive objects only when necessary.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 691 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 692 | private volatile ExpensiveObject instance; |
| 693 | ||||
| 694 | public ExpensiveObject getInstance() { |
|||
| 695 | if (instance == null) { |
|||
| 696 | synchronized (this) { |
|||
| 697 | if (instance == null) { |
|||
| 698 | instance = new ExpensiveObject(); |
|||
| 699 | } |
|||
| 700 | } |
|||
| 701 | } |
|||
| 702 | return instance; |
|||
| 703 | } |
|||
| 704 | ``` |
|||
| 705 | #### **2.14 Concurrency and Thread Safety** |
|||
| 706 | ##### **2.14.1 Rules:** |
|||
| 707 | * **Always use the highest-level concurrency utilities available** (Executor framework, concurrent collections) rather than wait(), notify(), and synchronized blocks when possible. |
|||
| 708 | * **Document thread-safety guarantees for all classes.** |
|||
| 709 | Use annotations like` @ThreadSafe`, `@NotThreadSafe`, or `@Immutable` in javadoc. |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 710 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 711 | /** |
| 712 | * This class is thread-safe. All methods can be called concurrently. |
|||
| 713 | * @ThreadSafe |
|||
| 714 | */ |
|||
| 715 | public class ThreadSafeCounter { |
|||
| 716 | // ... |
|||
| 717 | } |
|||
| 718 | ``` |
|||
| 719 | * **Never expose mutable internal state without proper synchronization.** |
|||
| 720 | * **Always use the volatile keyword for flags that are set by one thread and read by others.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 721 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 722 | private volatile boolean shutdownRequested = false; |
| 723 | ``` |
|||
| 724 | * **Avoid holding locks while calling methods on objects outside your control.** |
|||
| 725 | This can lead to deadlocks. |
|||
| 726 | * **Always acquire multiple locks in a fixed global order to prevent deadlock.** |
|||
| 727 | * **Use thread-safe collections instead of manually synchronizing collection access.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 728 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 729 | // Good |
| 730 | Map<String, String> map = new ConcurrentHashMap<>(); |
|||
| 731 | ||||
| 732 | // Bad |
|||
| 733 | Map<String, String> map = Collections.synchronizedMap(new HashMap<>()); |
|||
| 734 | ``` |
|||
| 735 | ##### **2.14.2 Recommendations:** |
|||
| 736 | * **Prefer immutable objects for thread safety.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 737 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 738 | public final class ImmutablePerson { |
| 739 | private final String name; |
|||
| 740 | private final int age; |
|||
| 741 | ||||
| 742 | public ImmutablePerson(String name, int age) { |
|||
| 743 | this.name = name; |
|||
| 744 | this.age = age; |
|||
| 745 | } |
|||
| 746 | ||||
| 747 | public String getName() { return name; } |
|||
| 748 | public int getAge() { return age; } |
|||
| 749 | } |
|||
| 750 | ``` |
|||
| 751 | * **Use ExecutorService instead of creating threads directly.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 752 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 753 | // Good |
| 754 | ExecutorService executor = Executors.newFixedThreadPool(10); |
|||
| 755 | executor.submit(() -> performTask()); |
|||
| 756 | executor.shutdown(); |
|||
| 757 | ||||
| 758 | // Bad |
|||
| 759 | new Thread(() -> performTask()).start(); |
|||
| 760 | ``` |
|||
| 761 | * **Prefer ReentrantLock over synchronized when you need advanced features** (tryLock, timed locks, interruptible locks). |
|||
| 762 | * **Use CountDownLatch, CyclicBarrier, or Phaser for coordinating multiple threads.** |
|||
| 763 | * **Avoid ThreadLocal unless absolutely necessary, and always clean up ThreadLocal variables.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 764 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 765 | private static final ThreadLocal<DateFormat> dateFormat = |
| 766 | ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd")); |
|||
| 767 | ||||
| 768 | public void cleanup() { |
|||
| 769 | dateFormat.remove(); // Always clean up |
|||
| 770 | } |
|||
| 771 | ``` |
|||
| 772 | * **Use CompletableFuture for asynchronous programming.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 773 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 774 | CompletableFuture.supplyAsync(() -> fetchData()) |
| 775 | .thenApply(data -> processData(data)) |
|||
| 776 | .thenAccept(result -> saveResult(result)) |
|||
| 777 | .exceptionally(ex -> { |
|||
| 778 | log.error("Error in async chain", ex); |
|||
| 779 | return null; |
|||
| 780 | }); |
|||
| 781 | ``` |
|||
| 782 | * **Document lock ordering to prevent deadlocks.** |
|||
| 783 | * **Consider using the Fork/Join framework for recursive parallel tasks.** |
|||
| 784 | ||||
| 785 | #### **2.15 API Design and Interfaces** |
|||
| 786 | ##### **2.15.1 Rules:** |
|||
| 787 | ||||
| 788 | * **Prefer interfaces to abstract classes for defining types.** |
|||
| 789 | Interfaces provide more flexibility as Java supports multiple interface implementation but only single inheritance. |
|||
| 790 | * Use functional interfaces for single abstract method interfaces, and annotate with @FunctionalInterface. |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 791 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 792 | @FunctionalInterface |
| 793 | public interface DataProcessor { |
|||
| 794 | void process(Data data); |
|||
| 795 | } |
|||
| 796 | ``` |
|||
| 797 | * **Never expose collection implementations in public APIs. Return interfaces instead.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 798 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 799 | // Good |
| 800 | public List<String> getNames() { |
|||
| 801 | return new ArrayList<>(names); |
|||
| 802 | } |
|||
| 803 | ||||
| 804 | // Bad |
|||
| 805 | public ArrayList<String> getNames() { |
|||
| 806 | return names; |
|||
| 807 | } |
|||
| 808 | ``` |
|||
| 809 | * **Return empty collections or arrays instead of null.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 810 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 811 | // Good |
| 812 | public List<Customer> getCustomers() { |
|||
| 813 | return customers.isEmpty() ? Collections.emptyList() : new ArrayList<>(customers); |
|||
| 814 | } |
|||
| 815 | ||||
| 816 | // Bad |
|||
| 817 | public List<Customer> getCustomers() { |
|||
| 818 | return customers.isEmpty() ? null : customers; |
|||
| 819 | } |
|||
| 820 | ``` |
|||
| 821 | * **Use defensive copying for mutable objects passed to or returned from methods.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 822 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 823 | public class DateRange { |
| 824 | private final Date start; |
|||
| 825 | private final Date end; |
|||
| 826 | ||||
| 827 | public DateRange(Date start, Date end) { |
|||
| 828 | this.start = new Date(start.getTime()); // Defensive copy |
|||
| 829 | this.end = new Date(end.getTime()); // Defensive copy |
|||
| 830 | } |
|||
| 831 | ||||
| 832 | public Date getStart() { |
|||
| 833 | return new Date(start.getTime()); // Defensive copy |
|||
| 834 | } |
|||
| 835 | } |
|||
| 836 | ``` |
|||
| 837 | * **Never return null from methods that return collections or arrays** |
|||
| 838 | ||||
| 839 | ##### **2.15.2 Recommendations:** |
|||
| 840 | * **Design interfaces for the clients, not for implementations.** |
|||
| 841 | * **Keep interfaces small and focused (Interface Segregation Principle).** |
|||
| 842 | * **Use default methods in interfaces sparingly, primarily for evolution.** |
|||
| 843 | ||||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 844 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 845 | public interface Repository { |
| 846 | List<Entity> findAll(); |
|||
| 847 | ||||
| 848 | default List<Entity> findAllSorted() { |
|||
| 849 | return findAll().stream() |
|||
| 850 | .sorted() |
|||
| 851 | .collect(Collectors.toList()); |
|||
| 852 | } |
|||
| 853 | } |
|||
| 854 | ``` |
|||
| 855 | * **Prefer method overloading to optional parameters for API design.** |
|||
| 856 | * **Use Builder pattern for classes with many parameters.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 857 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 858 | public class User { |
| 859 | private final String name; |
|||
| 860 | private final String email; |
|||
| 861 | private final int age; |
|||
| 862 | ||||
| 863 | private User(Builder builder) { |
|||
| 864 | this.name = builder.name; |
|||
| 865 | this.email = builder.email; |
|||
| 866 | this.age = builder.age; |
|||
| 867 | } |
|||
| 868 | ||||
| 869 | public static class Builder { |
|||
| 870 | private String name; |
|||
| 871 | private String email; |
|||
| 872 | private int age; |
|||
| 873 | ||||
| 874 | public Builder name(String name) { |
|||
| 875 | this.name = name; |
|||
| 876 | return this; |
|||
| 877 | } |
|||
| 878 | ||||
| 879 | public Builder email(String email) { |
|||
| 880 | this.email = email; |
|||
| 881 | return this; |
|||
| 882 | } |
|||
| 883 | ||||
| 884 | public Builder age(int age) { |
|||
| 885 | this.age = age; |
|||
| 886 | return this; |
|||
| 887 | } |
|||
| 888 | ||||
| 889 | public User build() { |
|||
| 890 | return new User(this); |
|||
| 891 | } |
|||
| 892 | } |
|||
| 893 | } |
|||
| 894 | ||||
| 895 | // Usage |
|||
| 896 | User user = new User.Builder() |
|||
| 897 | .name("John") |
|||
| 898 | .email("john@example.com") |
|||
| 899 | .age(30) |
|||
| 900 | .build(); |
|||
| 901 | ``` |
|||
| 902 | * **Document preconditions, postconditions, and invariants in javadoc.** |
|||
| 903 | * **Consider using marker interfaces for metadata (like Serializable, Cloneable).** |
|||
| 904 | * **Provide programmatic access to string representations (toString() should not be the only way).** |
|||
| 905 | ||||
| 906 | #### **2.16 Collection Framework Best Practices** |
|||
| 907 | ##### **2.16.1 Rules:** |
|||
| 908 | * **Choose the correct collection type based on the use case:** |
|||
| 909 | * `ArrayList` for random access and iteration |
|||
| 910 | * `LinkedList` for frequent insertions/deletions |
|||
| 911 | * `HashSet` for fast lookups with no duplicates |
|||
| 912 | * `TreeSet` for sorted sets |
|||
| 913 | * `HashMap` for key-value pairs |
|||
| 914 | * `LinkedHashMap` for insertion-ordered maps |
|||
| 915 | * `TreeMap` for sorted maps |
|||
| 916 | ||||
| 917 | * **Specify initial capacity for collections when the size is known in advance.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 918 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 919 | // Good |
| 920 | List<String> list = new ArrayList<>(expectedSize); |
|||
| 921 | Map<String, String> map = new HashMap<>(expectedSize); |
|||
| 922 | ||||
| 923 | // Bad |
|||
| 924 | List<String> list = new ArrayList<>(); // Will resize multiple times |
|||
| 925 | ``` |
|||
| 926 | * **Never remove elements from a collection while iterating with for-each loop.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 927 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 928 | // Bad - ConcurrentModificationException |
| 929 | for (String item : list) { |
|||
| 930 | if (condition) { |
|||
| 931 | list.remove(item); |
|||
| 932 | } |
|||
| 933 | } |
|||
| 934 | ||||
| 935 | // Good |
|||
| 936 | Iterator<String> iterator = list.iterator(); |
|||
| 937 | while (iterator.hasNext()) { |
|||
| 938 | String item = iterator.next(); |
|||
| 939 | if (condition) { |
|||
| 940 | iterator.remove(); |
|||
| 941 | } |
|||
| 942 | } |
|||
| 943 | ||||
| 944 | // Better (Java 8+) |
|||
| 945 | list.removeIf(item -> condition); |
|||
| 946 | ``` |
|||
| 947 | * **Use Collections.unmodifiable or List.of(), Set.of(), Map.of() to return immutable collections.*** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 948 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 949 | // Good (Java 9+) |
| 950 | List<String> immutableList = List.of("a", "b", "c"); |
|||
| 951 | Map<String, Integer> immutableMap = Map.of("key1", 1, "key2", 2); |
|||
| 952 | ||||
| 953 | // Good (Java 8) |
|||
| 954 | List<String> immutableList = Collections.unmodifiableList( |
|||
| 955 | new ArrayList<>(Arrays.asList("a", "b", "c")) |
|||
| 956 | ); |
|||
| 957 | ``` |
|||
| 958 | * **Never use raw types; always parameterize collections.** |
|||
| 959 | ``` |
|||
| 960 | Java |
|||
| 961 | // Bad |
|||
| 962 | List list = new ArrayList(); |
|||
| 963 | ||||
| 964 | // Good |
|||
| 965 | List<String> list = new ArrayList<>(); |
|||
| 966 | ``` |
|||
| 967 | ||||
| 968 | ##### **2.16.2 Recommendations:** |
|||
| 969 | * **Use the diamond operator (<>) to avoid redundant type information.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 970 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 971 | // Good |
| 972 | Map<String, List<String>> map = new HashMap<>(); |
|||
| 973 | ||||
| 974 | // Bad |
|||
| 975 | Map<String, List<String>> map = new HashMap<String, List<String>>(); |
|||
| 976 | ``` |
|||
| 977 | * **Prefer Arrays.asList() or List.of() for fixed-size lists.** |
|||
| 978 | ``` |
|||
| 979 | Java |
|||
| 980 | // Good (immutable, Java 9+) |
|||
| 981 | List<String> list = List.of("a", "b", "c"); |
|||
| 982 | ||||
| 983 | // Good (fixed-size, pre-Java 9) |
|||
| 984 | List<String> list = Arrays.asList("a", "b", "c"); |
|||
| 985 | ``` |
|||
| 986 | * **Use EnumSet instead of bit fields for enum collections.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 987 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 988 | // Good |
| 989 | Set<DayOfWeek> weekend = EnumSet.of(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY); |
|||
| 990 | ||||
| 991 | // Bad |
|||
| 992 | int weekend = SATURDAY | SUNDAY; |
|||
| 993 | ``` |
|||
| 994 | * **Use EnumMap for enum keys instead of HashMap.** |
|||
| 995 | ``` |
|||
| 996 | Java |
|||
| 997 | Map<DayOfWeek, String> schedule = new EnumMap<>(DayOfWeek.class); |
|||
| 998 | ``` |
|||
| 999 | * **Consider using Guava collections for advanced use cases (Multimap, BiMap, Table, etc.).** |
|||
| 1000 | * **Use Collection.toArray(new T[0]) instead of Collection.toArray(new T[size]).** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1001 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1002 | // Good |
| 1003 | String[] array = list.toArray(new String[0]); |
|||
| 1004 | ||||
| 1005 | // Outdated |
|||
| 1006 | String[] array = list.toArray(new String[list.size()]); |
|||
| 1007 | ``` |
|||
| 1008 | * **Sort collections using Comparator and Collection.sort() or Stream API.** |
|||
| 1009 | ``` |
|||
| 1010 | Java |
|||
| 1011 | // Good |
|||
| 1012 | list.sort(Comparator.comparing(Person::getName)); |
|||
| 1013 | ||||
| 1014 | // Or using Stream |
|||
| 1015 | List<Person> sorted = list.stream() |
|||
| 1016 | .sorted(Comparator.comparing(Person::getName)) |
|||
| 1017 | .collect(Collectors.toList()); |
|||
| 1018 | ``` |
|||
| 1019 | ||||
| 1020 | #### **2.17 Java 8+ Features Standards** |
|||
| 1021 | ##### **2.17.1 Rules:** |
|||
| 1022 | * **Use lambda expressions instead of anonymous inner classes for functional interfaces.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1023 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1024 | // Good |
| 1025 | list.forEach(item -> System.out.println(item)); |
|||
| 1026 | ||||
| 1027 | // Bad |
|||
| 1028 | list.forEach(new Consumer<String>() { |
|||
| 1029 | @Override |
|||
| 1030 | public void accept(String item) { |
|||
| 1031 | System.out.println(item); |
|||
| 1032 | } |
|||
| 1033 | }); |
|||
| 1034 | ``` |
|||
| 1035 | * **Use method references when they are clearer than lambda expressions.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1036 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1037 | // Good |
| 1038 | list.forEach(System.out::println); |
|||
| 1039 | ||||
| 1040 | // Acceptable |
|||
| 1041 | list.forEach(item -> System.out.println(item)); |
|||
| 1042 | ``` |
|||
| 1043 | * **Use Optional to represent potentially absent values, but not for collections, arrays, or method parameters.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1044 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1045 | // Good |
| 1046 | public Optional<User> findUserById(String id) { |
|||
| 1047 | return Optional.ofNullable(userMap.get(id)); |
|||
| 1048 | } |
|||
| 1049 | ||||
| 1050 | // Bad - don't use Optional for parameters |
|||
| 1051 | public void processUser(Optional<User> user) { } |
|||
| 1052 | ||||
| 1053 | // Bad - don't return Optional collections |
|||
| 1054 | public Optional<List<User>> getUsers() { } |
|||
| 1055 | ``` |
|||
| 1056 | * **Never call Optional.get() without checking Optional.isPresent() first, or use proper Optional methods.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1057 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1058 | // Bad |
| 1059 | Optional<User> user = findUser(); |
|||
| 1060 | User u = user.get(); // May throw NoSuchElementException |
|||
| 1061 | ||||
| 1062 | // Good |
|||
| 1063 | Optional<User> user = findUser(); |
|||
| 1064 | User u = user.orElse(defaultUser); |
|||
| 1065 | ||||
| 1066 | // Or |
|||
| 1067 | user.ifPresent(u -> processUser(u)); |
|||
| 1068 | ``` |
|||
| 1069 | * **Use Stream API for collection processing, but avoid overusing streams for simple iterations.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1070 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1071 | // Good - streams add value |
| 1072 | List<String> names = users.stream() |
|||
| 1073 | .filter(user -> user.isActive()) |
|||
| 1074 | .map(User::getName) |
|||
| 1075 | .sorted() |
|||
| 1076 | .collect(Collectors.toList()); |
|||
| 1077 | ||||
| 1078 | // Bad - simple loop is clearer |
|||
| 1079 | users.stream().forEach(user -> processUser(user)); |
|||
| 1080 | // Better |
|||
| 1081 | for (User user : users) { |
|||
| 1082 | processUser(user); |
|||
| 1083 | } |
|||
| 1084 | ``` |
|||
| 1085 | * **Always close streams that are backed by I/O resources.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1086 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1087 | try (Stream<String> lines = Files.lines(Paths.get("file.txt"))) { |
| 1088 | lines.filter(line -> line.contains("error")) |
|||
| 1089 | .forEach(System.out::println); |
|||
| 1090 | } |
|||
| 1091 | ``` |
|||
| 1092 | * **Use java.time package (LocalDate, LocalDateTime, ZonedDateTime) instead of Date and Calendar.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1093 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1094 | // Good |
| 1095 | LocalDate today = LocalDate.now(); |
|||
| 1096 | LocalDateTime now = LocalDateTime.now(); |
|||
| 1097 | ZonedDateTime zonedNow = ZonedDateTime.now(ZoneId.of("UTC")); |
|||
| 1098 | ||||
| 1099 | // Bad |
|||
| 1100 | Date date = new Date(); |
|||
| 1101 | Calendar calendar = Calendar.getInstance(); |
|||
| 1102 | ``` |
|||
| 1103 | ||||
| 1104 | ##### **2.17.2 Recommendations:** |
|||
| 1105 | * **Prefer functional composition over imperative code when it improves readability.** |
|||
| 1106 | * **Use Collectors for stream terminal operations.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1107 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1108 | // Collect to list |
| 1109 | List<String> names = users.stream() |
|||
| 1110 | .map(User::getName) |
|||
| 1111 | .collect(Collectors.toList()); |
|||
| 1112 | ||||
| 1113 | // Collect to map |
|||
| 1114 | Map<String, User> userMap = users.stream() |
|||
| 1115 | .collect(Collectors.toMap(User::getId, Function.identity())); |
|||
| 1116 | ||||
| 1117 | // Joining strings |
|||
| 1118 | String csv = names.stream() |
|||
| 1119 | .collect(Collectors.joining(", ")); |
|||
| 1120 | ||||
| 1121 | // Grouping |
|||
| 1122 | Map<Department, List<Employee>> byDept = employees.stream() |
|||
| 1123 | .collect(Collectors.groupingBy(Employee::getDepartment)); |
|||
| 1124 | ``` |
|||
| 1125 | * **Use Optional.orElseThrow() for validation.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1126 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1127 | User user = findUserById(id) |
| 1128 | .orElseThrow(() -> new UserNotFoundException("User not found: " + id)); |
|||
| 1129 | ``` |
|||
| 1130 | * **Prefer flatMap for nested optionals or streams.** |
|||
| 1131 | ``` |
|||
| 1132 | Java |
|||
| 1133 | Optional<String> result = user |
|||
| 1134 | .flatMap(User::getAddress) |
|||
| 1135 | .flatMap(Address::getZipCode); |
|||
| 1136 | ``` |
|||
| 1137 | * **Use parallel streams judiciously, only for CPU-intensive operations on large datasets.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1138 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1139 | // Use parallel streams only when appropriate |
| 1140 | long count = largeList.parallelStream() |
|||
| 1141 | .filter(this::expensiveOperation) |
|||
| 1142 | .count(); |
|||
| 1143 | ``` |
|||
| 1144 | * **Use Stream.iterate() or Stream.generate() for infinite streams with limit().** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1145 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1146 | Stream.iterate(0, n -> n + 1) |
| 1147 | .limit(10) |
|||
| 1148 | .forEach(System.out::println); |
|||
| 1149 | ``` |
|||
| 1150 | * **Use CompletableFuture for asynchronous programming.** |
|||
| 1151 | * **Leverage default and static methods in interfaces for backward compatibility.** |
|||
| 1152 | * **Use @FunctionalInterface annotation for functional interfaces.** |
|||
| 1153 | * **Consider using var (Java 10+) for local variables when the type is obvious.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1154 | ``` Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1155 | // Good - type is obvious |
| 1156 | var list = new ArrayList<String>(); |
|||
| 1157 | var user = userRepository.findById(id); |
|||
| 1158 | ||||
| 1159 | // Bad - type is not obvious |
|||
| 1160 | var result = process(); // What type is this? |
|||
| 1161 | ``` |
|||
| 1162 | #### **2.18 Logging Standards** |
|||
| 1163 | ##### **2.18.1 Rules:** |
|||
| 1164 | * **Use SLF4J as the logging facade, not Log4j or java.util.logging directly.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1165 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1166 | import org.slf4j.Logger; |
| 1167 | import org.slf4j.LoggerFactory; |
|||
| 1168 | ||||
| 1169 | public class UserService { |
|||
| 1170 | private static final Logger logger = LoggerFactory.getLogger(UserService.class); |
|||
| 1171 | } |
|||
| 1172 | ``` |
|||
| 1173 | * **Initialize loggers as private static final fields.** |
|||
| 1174 | * **Use appropriate log levels:** |
|||
| 1175 | * **ERROR**: System failures, unrecoverable errors |
|||
| 1176 | * **WARN:** Recoverable errors, deprecated API usag |
|||
| 1177 | * **INFO**: Important business events, application lifecycle |
|||
| 1178 | * **DEBUG**: Detailed information for debugging |
|||
| 1179 | * **TRACE**: Very detailed information, typically for diagnosis |
|||
| 1180 | * **Never log sensitive information (passwords, credit cards, PII, tokens, session IDs)** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1181 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1182 | // Bad |
| 1183 | logger.info("User logged in: {}", user.getPassword()); |
|||
| 1184 | ||||
| 1185 | // Good |
|||
| 1186 | logger.info("User logged in: {}", user.getUsername()); |
|||
| 1187 | ``` |
|||
| 1188 | * **Use parameterized logging instead of string concatenation.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1189 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1190 | // Good |
| 1191 | logger.debug("Processing user {} with status {}", userId, status); |
|||
| 1192 | ||||
| 1193 | // Bad |
|||
| 1194 | logger.debug("Processing user " + userId + " with status " + status); |
|||
| 1195 | ``` |
|||
| 1196 | * **Always log exceptions with the exception object, not just the message.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1197 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1198 | // Good |
| 1199 | try { |
|||
| 1200 | processData(); |
|||
| 1201 | } catch (DataException e) { |
|||
| 1202 | logger.error("Failed to process data for user {}", userId, e); |
|||
| 1203 | } |
|||
| 1204 | ||||
| 1205 | // Bad |
|||
| 1206 | catch (DataException e) { |
|||
| 1207 | logger.error("Failed to process data: " + e.getMessage()); |
|||
| 1208 | } |
|||
| 1209 | ``` |
|||
| 1210 | * **Guard expensive log statements with level checks.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1211 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1212 | if (logger.isDebugEnabled()) { |
| 1213 | logger.debug("Complex calculation result: {}", expensiveCalculation()); |
|||
| 1214 | } |
|||
| 1215 | ``` |
|||
| 1216 | ##### **2.18.2 Recommendations:** |
|||
| 1217 | * **Use MDC (Mapped Diagnostic Context) for request tracking.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1218 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1219 | MDC.put("requestId", requestId); |
| 1220 | MDC.put("userId", userId); |
|||
| 1221 | try { |
|||
| 1222 | // Process request |
|||
| 1223 | logger.info("Processing request"); |
|||
| 1224 | } finally { |
|||
| 1225 | MDC.clear(); |
|||
| 1226 | } |
|||
| 1227 | ``` |
|||
| 1228 | * **Log method entry and exit only at DEBUG or TRACE level.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1229 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1230 | public void processOrder(Order order) { |
| 1231 | logger.debug("Entering processOrder with order: {}", order.getId()); |
|||
| 1232 | try { |
|||
| 1233 | // Process order |
|||
| 1234 | logger.debug("Exiting processOrder successfully"); |
|||
| 1235 | } catch (Exception e) { |
|||
| 1236 | logger.debug("Exiting processOrder with error", e); |
|||
| 1237 | throw e; |
|||
| 1238 | } |
|||
| 1239 | } |
|||
| 1240 | ``` |
|||
| 1241 | * **Use structured logging for complex data.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1242 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1243 | logger.info("Order processed: orderId={}, userId={}, amount={}, status={}", |
| 1244 | order.getId(), order.getUserId(), order.getAmount(), order.getStatus()); |
|||
| 1245 | ``` |
|||
| 1246 | * **Avoid logging in loops unless absolutely necessary.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1247 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1248 | // Bad |
| 1249 | for (User user : users) { |
|||
| 1250 | logger.debug("Processing user: {}", user.getId()); |
|||
| 1251 | processUser(user); |
|||
| 1252 | } |
|||
| 1253 | ||||
| 1254 | // Good |
|||
| 1255 | logger.debug("Processing {} users", users.size()); |
|||
| 1256 | for (User user : users) { |
|||
| 1257 | processUser(user); |
|||
| 1258 | } |
|||
| 1259 | ``` |
|||
| 1260 | * **Include context in log messages.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1261 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1262 | // Bad |
| 1263 | logger.error("Processing failed"); |
|||
| 1264 | ||||
| 1265 | // Good |
|||
| 1266 | logger.error("Order processing failed for orderId: {}, userId: {}", |
|||
| 1267 | orderId, userId); |
|||
| 1268 | ``` |
|||
| 1269 | * **Use consistent log message formatting across the application.** |
|||
| 1270 | * **Configure appropriate log rotation and retention policies.** |
|||
| 1271 | * **Use asynchronous logging for high-throughput applications.** |
|||
| 1272 | ||||
| 1273 | #### **2.19 Testing and Mocking Standards** |
|||
| 1274 | ##### **2.19.1 Rules:** |
|||
| 1275 | * **Follow naming convention for test classes**: `<ClassUnderTest>Test` |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1276 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1277 | // Class under test: UserService.java |
| 1278 | // Test class: UserServiceTest.java |
|||
| 1279 | ``` |
|||
| 1280 | * **Follow naming convention for test methods:** `testMethodName_StateUnderTest_ExpectedBehavior` or `methodName_StateUnderTest_ExpectedBehavior` |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1281 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1282 | @Test |
| 1283 | public void findUserById_UserExists_ReturnsUser() { } |
|||
| 1284 | ||||
| 1285 | @Test |
|||
| 1286 | public void findUserById_UserNotFound_ThrowsException() { } |
|||
| 1287 | ``` |
|||
| 1288 | * **Structure tests using the Arrange-Act-Assert (AAA) pattern.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1289 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1290 | @Test |
| 1291 | public void calculateTotal_WithDiscount_ReturnsDiscountedTotal() { |
|||
| 1292 | // Arrange |
|||
| 1293 | Order order = new Order(); |
|||
| 1294 | order.addItem(new Item("Product", 100.0)); |
|||
| 1295 | Discount discount = new Discount(0.1); // 10% discount |
|||
| 1296 | ||||
| 1297 | // Act |
|||
| 1298 | double total = order.calculateTotal(discount); |
|||
| 1299 | ||||
| 1300 | // Assert |
|||
| 1301 | assertEquals(90.0, total, 0.01); |
|||
| 1302 | } |
|||
| 1303 | ``` |
|||
| 1304 | * **Each test method should test one specific behavior or scenario.** |
|||
| 1305 | * **Use @Before/@BeforeEach for common setup and @After/@AfterEach for cleanup.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1306 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1307 | @BeforeEach |
| 1308 | public void setUp() { |
|||
| 1309 | userRepository = mock(UserRepository.class); |
|||
| 1310 | userService = new UserService(userRepository); |
|||
| 1311 | } |
|||
| 1312 | ||||
| 1313 | @AfterEach |
|||
| 1314 | public void tearDown() { |
|||
| 1315 | // Cleanup resources |
|||
| 1316 | } |
|||
| 1317 | ``` |
|||
| 1318 | * **Always use assertion libraries (JUnit assertions, AssertJ, Hamcrest).** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1319 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1320 | // Good - JUnit 5 |
| 1321 | assertEquals(expected, actual); |
|||
| 1322 | assertThrows(IllegalArgumentException.class, () -> service.process(null)); |
|||
| 1323 | ||||
| 1324 | // Good - AssertJ |
|||
| 1325 | assertThat(result).isNotNull() |
|||
| 1326 | .hasSize(3) |
|||
| 1327 | .contains("item1", "item2"); |
|||
| 1328 | ``` |
|||
| 1329 | * **Never catch exceptions in tests; use assertThrows instead.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1330 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1331 | // Good |
| 1332 | assertThrows(UserNotFoundException.class, () -> userService.findById("invalid")); |
|||
| 1333 | ||||
| 1334 | // Bad |
|||
| 1335 | try { |
|||
| 1336 | userService.findById("invalid"); |
|||
| 1337 | fail("Expected UserNotFoundException"); |
|||
| 1338 | } catch (UserNotFoundException e) { |
|||
| 1339 | // Expected |
|||
| 1340 | } |
|||
| 1341 | ``` |
|||
| 1342 | * **Mock external dependencies, but don't mock the class under test.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1343 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1344 | @Test |
| 1345 | public void createUser_ValidUser_SavesUser() { |
|||
| 1346 | // Arrange |
|||
| 1347 | UserRepository mockRepository = mock(UserRepository.class); |
|||
| 1348 | UserService service = new UserService(mockRepository); |
|||
| 1349 | User user = new User("John", "john@example.com"); |
|||
| 1350 | ||||
| 1351 | when(mockRepository.save(any(User.class))).thenReturn(user); |
|||
| 1352 | ||||
| 1353 | // Act |
|||
| 1354 | User result = service.createUser(user); |
|||
| 1355 | ||||
| 1356 | // Assert |
|||
| 1357 | verify(mockRepository).save(user); |
|||
| 1358 | assertEquals("John", result.getName()); |
|||
| 1359 | } |
|||
| 1360 | ``` |
|||
| 1361 | ||||
| 1362 | ##### **2.19.2 Recommendations:** |
|||
| 1363 | * **Use Mockito for mocking, avoid PowerMock unless absolutely necessary.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1364 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1365 | @ExtendWith(MockitoExtension.class) |
| 1366 | public class UserServiceTest { |
|||
| 1367 | ||||
| 1368 | @Mock |
|||
| 1369 | private UserRepository userRepository; |
|||
| 1370 | ||||
| 1371 | @InjectMocks |
|||
| 1372 | private UserService userService; |
|||
| 1373 | ||||
| 1374 | @Test |
|||
| 1375 | public void testFindUser() { |
|||
| 1376 | when(userRepository.findById("123")).thenReturn(Optional.of(user)); |
|||
| 1377 | // Test logic |
|||
| 1378 | } |
|||
| 1379 | } |
|||
| 1380 | ``` |
|||
| 1381 | * **Use ArgumentCaptor to verify complex method arguments.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1382 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1383 | @Test |
| 1384 | public void createUser_ValidUser_SavesWithCorrectData() { |
|||
| 1385 | ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class); |
|||
| 1386 | ||||
| 1387 | userService.createUser("John", "john@example.com"); |
|||
| 1388 | ||||
| 1389 | verify(userRepository).save(userCaptor.capture()); |
|||
| 1390 | User capturedUser = userCaptor.getValue(); |
|||
| 1391 | assertEquals("John", capturedUser.getName()); |
|||
| 1392 | assertEquals("john@example.com", capturedUser.getEmail()); |
|||
| 1393 | } |
|||
| 1394 | ``` |
|||
| 1395 | * **Use @ParameterizedTest for testing multiple inputs.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1396 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1397 | @ParameterizedTest |
| 1398 | @ValueSource(strings = {"", " ", " "}) |
|||
| 1399 | public void isBlank_BlankStrings_ReturnsTrue(String input) { |
|||
| 1400 | assertTrue(StringUtils.isBlank(input)); |
|||
| 1401 | } |
|||
| 1402 | ||||
| 1403 | @ParameterizedTest |
|||
| 1404 | @CsvSource({ |
|||
| 1405 | "1, 1, 2", |
|||
| 1406 | "2, 3, 5", |
|||
| 1407 | "5, 5, 10" |
|||
| 1408 | }) |
|||
| 1409 | public void add_TwoNumbers_ReturnsSum(int a, int b, int expected) { |
|||
| 1410 | assertEquals(expected, calculator.add(a, b)); |
|||
| 1411 | } |
|||
| 1412 | ``` |
|||
| 1413 | * **Use test fixtures for complex test data setup.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1414 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1415 | public class UserFixtures { |
| 1416 | public static User createDefaultUser() { |
|||
| 1417 | return new User("John", "john@example.com", 30); |
|||
| 1418 | } |
|||
| 1419 | ||||
| 1420 | public static User createAdminUser() { |
|||
| 1421 | User user = createDefaultUser(); |
|||
| 1422 | user.setRole(Role.ADMIN); |
|||
| 1423 | return user; |
|||
| 1424 | } |
|||
| 1425 | } |
|||
| 1426 | ``` |
|||
| 1427 | * **Aim for high test coverage, but focus on critical paths and edge cases.** |
|||
| 1428 | * **Use integration tests for testing component interactions, unit tests for isolated logic**. |
|||
| 1429 | * **Use @SpringBootTest sparingly; prefer @WebMvcTest, @DataJpaTest, etc., for focused testing.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1430 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1431 | @WebMvcTest(UserController.class) |
| 1432 | public class UserControllerTest { |
|||
| 1433 | @Autowired |
|||
| 1434 | private MockMvc mockMvc; |
|||
| 1435 | ||||
| 1436 | @MockBean |
|||
| 1437 | private UserService userService; |
|||
| 1438 | ||||
| 1439 | @Test |
|||
| 1440 | public void getUser_ValidId_ReturnsUser() throws Exception { |
|||
| 1441 | when(userService.findById("123")).thenReturn(user); |
|||
| 1442 | ||||
| 1443 | mockMvc.perform(get("/users/123")) |
|||
| 1444 | .andExpect(status().isOk()) |
|||
| 1445 | .andExpect(jsonPath("$.name").value("John")); |
|||
| 1446 | } |
|||
| 1447 | } |
|||
| 1448 | ``` |
|||
| 1449 | * **Use test containers for integration testing with databases and external services** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1450 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1451 | @Testcontainers |
| 1452 | public class UserRepositoryIntegrationTest { |
|||
| 1453 | ||||
| 1454 | @Container |
|||
| 1455 | static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:13"); |
|||
| 1456 | ||||
| 1457 | @Test |
|||
| 1458 | public void testDatabaseOperations() { |
|||
| 1459 | // Test with real database |
|||
| 1460 | } |
|||
| 1461 | } |
|||
| 1462 | ``` |
|||
| 1463 | * **Never use System.out.println() in tests; use proper assertions and logging.** |
|||
| 1464 | * **Keep tests fast. Slow tests won't be run frequently.** |
|||
| 1465 | * **Use BDD style for better readability (given-when-then).** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1466 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1467 | @Test |
| 1468 | public void userRegistration_ValidData_CreatesUser() { |
|||
| 1469 | // Given |
|||
| 1470 | String email = "test@example.com"; |
|||
| 1471 | String password = "password123"; |
|||
| 1472 | ||||
| 1473 | // When |
|||
| 1474 | User user = userService.register(email, password); |
|||
| 1475 | ||||
| 1476 | // Then |
|||
| 1477 | assertThat(user).isNotNull(); |
|||
| 1478 | assertThat(user.getEmail()).isEqualTo(email); |
|||
| 1479 | assertThat(user.isActive()).isTrue(); |
|||
| 1480 | } |
|||
| 1481 | ``` |
|||
| 1482 | * **Document complex test scenarios with comments.** |
|||
| 1483 | * **Use @Disabled with a reason when temporarily disabling tests.** |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1484 | ```Java |
| 077f35 | Melisha Dsouza | 2026-01-15 16:09:48 | 1485 | @Test |
| 1486 | @Disabled("Disabled until bug #123 is fixed") |
|||
| 1487 | public void testComplexScenario() { |
|||
| 1488 | // Test code |
|||
| 1489 | } |
|||
| 1490 | ``` |
|||
| a837c9 | Melisha Dsouza | 2026-01-16 09:25:51 | 1491 | |
| 1492 | **Document** - [Enovate Coding Standards - Core Java](https://docs.google.com/document/d/1EaOaSk-hbuje0igvIxl1DXcHQROc3KP-S49eD1IUGqg/edit?tab=t.0) |