Java Methods and Program Design
A progressive guide to designing, calling, tracing, and evaluating Java methods with parameters, return values, scope, contracts, overloading, and decomposition.
structure and behavior
A is a named block of Java code that runs when called. Methods support procedural abstraction: a caller can use a based on what it does without needing to know every implementation detail.
A declaration commonly contains these parts:
An access modifier, such as
publicorprivate, controls where the can be accessed.A return type states what kind of value the produces, or
voidwhen it produces no value.A name identifies the operation.
A list declares information that the caller can supply.
A body contains the statements that execute during the call.
For example, a with return type int can calculate and return a whole-number result. A void can print a greeting or perform another action, but its call cannot be assigned to a variable or used as part of a larger expression.
A non-void must return a compatible value on every possible execution path. A return statement immediately ends the current and sends its result to the caller. A void may use return without a value to exit early.
Takeaway
A declaration describes how to call a task, what information it receives, and whether it produces a result.
Parameters, arguments, and return values
A is a variable declared in a header, while an is the value supplied in a call. For example, in a that computes an average from first and second, those names are parameters; values such as 4.0 and 10.0 are arguments.
The number, order, and types of arguments must match the selected . If a expects an integer followed by a string, supplying a string followed by an integer is not a valid call.
Java passes arguments by value. For a primitive value, the receives a separate copy, so changing the does not change the caller's variable. For a reference-type , Java passes a copy of the reference. The and the caller's variable can therefore refer to the same object, allowing the to change that object's contents. However, assigning a new object to the changes only the local ; it does not redirect the caller's variable.
A can be stored in a variable, printed, used in an expression, passed as an to another , or returned by a larger . For example, a Boolean result can be used directly as the condition of an if statement.
Takeaway
Match arguments carefully, distinguish changing an object from replacing a reference, and treat returned results as values that can flow into other expressions.
signatures and overloading
A consists of the name and the ordered list of types. The return type is not part of the signature.
allows several methods in one class to share a name while accepting different lists. A class might provide one for two int arguments, another for two double arguments, and a third for three int arguments. The compiler chooses the version whose number and types of parameters best match the call.
Changing only the return type does not create a valid overload. Java also cannot distinguish two methods that have the same name and the same types merely because their variable names differ.
Do not confuse overloading with overriding. Overloading uses different lists for methods with the same name. Overriding occurs when a subclass provides a new implementation of an inherited .
A reliable call-analysis process
Identify the name.
Count the arguments and identify their types.
Compare them with the available lists.
Select the matching overloaded without using the return type as a deciding factor.
Trace the selected 's statements and returned result.
Takeaway
When resolving a call, focus on the name, count, order, and types.
, local variables, and shadowing
is the region in which a name can be used. A 's is the entire body of its , and a in one is separate from a with the same name in another .
A local variable is accessible from its declaration to the end of its enclosing block. A variable declared inside an if block cannot be used after that block ends. Similarly, a variable declared in a for loop is generally limited to that loop. Local variables must be initialized before they are used.
A or local variable can shadow a field with the same name. In a constructor such as Student(String name), the unqualified name name refers to the , while this.name refers to the object's field. The keyword this makes the field reference explicit.
Each call has its own and local-variable values. When tracing nested or repeated calls, keep those values separate rather than treating identically named variables as one shared variable.
Takeaway
For every variable, ask where it was declared, which block contains it, and whether a closer declaration shadows another name.
Preconditions, postconditions, and contracts
A is an assumption that must be true immediately before a runs. It can require valid values, a non-null array, a nonempty array, a sorted list, a valid index, or an initialized object. The caller is responsible for satisfying the documented unless the explicitly promises to handle invalid input.
A is a guarantee that must be true after the finishes. It may describe a returned result or a side effect, such as leaving an array unchanged. A describes the 's observable guarantee, not necessarily the algorithm used to achieve it.
Together, these conditions form a contract:
The caller satisfies the .
The performs its task.
The establishes the .
For example, a that returns an array element might require that the array is not null and that the index is valid. Its might guarantee that the array is unchanged. If a requires a nonempty array, calling it with an empty array violates the contract and may lead to incorrect behavior or an exception.
Takeaway
Check preconditions before trusting a call, then use postconditions to determine what can safely be assumed afterward.
and helper methods
means dividing a complex problem into smaller tasks. A supports another by implementing one focused subproblem. The calling can then express the overall algorithm at a higher level.
For a final-price calculation, one can coordinate the process: first call a that applies a discount, then call a that adds tax. Each supporting receives the information it needs and returns one intermediate result. The coordinating remains readable because it describes the sequence of major steps instead of containing every calculation at once.
A well-designed usually has one clear responsibility, a descriptive name, appropriate parameters, and a useful or action. Its contract should state important preconditions and postconditions. A counting , for example, can receive an array, return the number of positive elements, and leave the array unchanged.
provides several benefits:
Readability improves because each has a focused purpose.
Testing becomes easier because smaller methods can be tested independently.
Reuse increases when a helper can serve multiple callers.
Debugging becomes more local because an error can be isolated to one task.
Maintenance becomes simpler because one calculation can change without rewriting the whole algorithm.
Do not create a separate helper for every individual statement. A helper is most useful when it represents a meaningful subproblem and reduces the complexity of its caller.
Takeaway
Design methods around responsibilities: decide what each should do, what it needs, what it guarantees, and whether it should return a value or perform an action.
A complete -analysis workflow
When tracing or designing a Java , combine the ideas in a consistent order:
Identify the name and locate its declaration.
Match the count, order, and types with the list.
Record the initial value received by each .
Track local variables within their own scopes and keep separate calls separate.
Follow statements in execution order.
Distinguish changes to a shared object's contents from reassignment of a local reference.
Record the returned value, if any, and substitute it into the calling expression.
Check the before relying on the 's behavior.
Apply the after the completes.
If methods are overloaded, select by the list rather than by return type.
This process supports both code reading and program design. When writing a , choose a clear name, define the necessary parameters, select void or an appropriate return type, document the contract, and divide distinct subproblems into focused methods. When reading a , focus on its inputs, local state, observable effects, and result rather than assuming that similarly named variables are shared.
Final checklist
Does every non-
voidpath return a compatible value?Are all arguments matched to the correct parameters?
Is every variable used within its and initialized before use?
Are preconditions satisfied?
Does the establish its promised ?
Is the focused enough to be readable and testable?