Overview
This unit on Custom Methods teaches how to design, write, test and document user-defined procedures and functions in a high-level programming language used in Class 10. It covers why we create custom methods, how to choose names and parameters, types of parameters (value and reference), return types, method overloading, scope and lifetime of variables, recursion, modular design and code reuse, debugging and testing of methods, and documentation and style. The unit emphasises breaking a program into smaller, reusable pieces so programs become easier to read, write and maintain. Students will learn to translate algorithms into methods, pass information between methods, and combine methods to solve problems typical for ICSE exams. Practical skills include writing correct syntax, calling methods from main, handling input and output, and tracing execution for logic and runtime errors. The unit matters because methods form the backbone of structured and object-oriented programming; mastering them prepares students for larger projects, helps write clearer code, and is essential for competitive exams and future study in computer science.
Learning Objectives
- Define what a custom method is and explain its purpose in program design.
- Write correct method declarations and calls with appropriate parameter lists and return types.
- Use local and global variables appropriately and explain scope and lifetime of variables.
- Demonstrate parameter passing by value and by reference where supported, and explain their differences.
- Design and implement method overloading and explain when to use it.
- Apply recursion to solve problems and explain the conditions for correct recursive solutions.
- Decompose a larger problem into smaller methods to create modular, reusable code.
- Test and debug methods using dry runs and tracing, and document methods with clear comments and specifications.
Topics in this chapter
17 topics · tap a topic title to jump straight to it.
Introduction to Custom Methods
What is a custom method?
A custom method is a named block of instructions written by the programmer to perform a particular task. It has a header that gives its name, any parameters it needs, and a return type (or void if it returns nothing). The body contains the statements that carry out the task. Methods allow you to hide complexity: the caller only needs to know the method name and how to use it, not how it works internally.
Why create methods?
Methods promote reuse. When the same operation is used multiple times, writing it once as a method reduces repetition. They also help test smaller parts of a program independently, making debugging easier. When a program is split into meaningful methods, each part can be understood and modified without reading the entire program.
Single responsibility
Each method should do one clear job. This principle—single responsibility—keeps methods small and focused. For example, separate reading input, computing results and printing output into different methods. This separation helps when an exam problem asks for a particular helper method; it is already available separately.
Designing a method
When designing a method, decide the following: what is its purpose, what inputs (parameters) are required, what it will return (if anything), and whether it should modify data passed to it. Choose parameter names that make the role clear and document expected ranges (for example index between 0 and n-1).
Advantages in learning and exams
For ICSE/ISC style questions, writing clear methods demonstrates understanding and earns method-design marks. Methods make code neater and easier to grade. In projects, methods make extension simple: add a new helper method for new features without changing existing verified code.
Examples of method usage
Typical small programs call methods like readArray(), computeSum(arr), and displayResult(sum). A method can be written to return a value or to perform an action like printing. Clear naming and short descriptions above each method are good practice for school assignments and exams.
Best practices summary
Keep methods short, give meaningful names, document inputs and outputs, prefer passing minimal necessary data, avoid global state where possible, and test each method with typical and edge inputs. Good method design leads to programs that are easy to read, test, and maintain.
- A method named add(a, b) that returns the sum of two numbers. Call: result = add(5, 3) gives result 8.
- A method printWelcome() that prints a welcome message when the program starts.
- Separate methods: readNumbers(), computeAverage(numbers), and displayAverage(avg) in a small program to find the average of exam scores.
- Method structure: <return-type> <method-name>(<parameter-list>) { <statements> }
- Procedure (no return): void <procedure-name>(<parameter-list>) { <statements> }
Method Declaration and Call Syntax
Declaration basics
A method declaration defines its name, return type and parameters, and provides the statements that will execute when the method is called. The declaration sets the method's interface: how other parts of the program can use it. Although exact syntax varies between languages, you should follow the language rules taught at school.
Components of a method header
The return type indicates what kind of value the method will produce, or use a special keyword (like void) when no value is returned. The method name should be meaningful and follow identifier rules of the language. The parameter list gives names and types of inputs; each parameter acts like a local variable within the method body.
Calling a method
To call a method, write its name and supply arguments in parentheses. If the method returns a value, you can store it in a variable or use it in expressions. Example: int total = add(10, 20); If the method returns no value, call it as a statement: printReport(); The order and types of arguments must match the method's parameters.
Argument matching rules
The number of arguments and their types must be compatible with the parameter list. Most classroom languages use positional matching: the first argument maps to the first parameter, the second to the second, and so on. Avoid relying on implicit conversions; use explicit conversions or matching types to prevent surprises.
Return behaviour and control flow
A method that declares a return type other than void must return a value of that type on every possible path through the code. A void method may use an early return statement with no value to exit before reaching the end. When a return executes, control goes back to the caller and the method's local variables are discarded.
Examples of correct and incorrect code patterns
Correct example: int square(int n) { return n * n; } Call: int a = square(5); Incorrect example: int sum(int a, int b) { System.out.println(a+b); } // Here a non-void method does not return a value. Also ensure parameters declared are the same names used inside the method body to avoid compile-time errors.
Overloading and defaults in classroom practice
Some languages allow default parameter values or named arguments, but ICSE practice generally expects explicit parameters and positional arguments. Overloading—having multiple methods with the same name but different parameter lists—is treated separately and should be used to provide convenience without confusing method selection.
Practical advice
In exams and assignments, write method headers first with a brief comment describing inputs and outputs, then implement the body. This approach reduces mistakes with parameter order and return types and makes your program easier to read and grade.
- Declaration: int square(int n) { return n * n; } Call: int a = square(5); // a becomes 25
- Declaration: void greet(String name) { System.out.println("Hello " + name); } Call: greet("Asha");
- Incorrect call: int x = square(); // Error: missing argument
- Call matches declaration: <method-name>(<arg1>, <arg2>, ...);
- Return statements: return <value>; in non-void methods, return; in void methods to exit early.
Parameters and Arguments
Definitions and distinction
Parameters are named variables inside a method header that describe the type and name of data the method expects. Arguments are the actual values supplied to the method when it is called. Parameters act as placeholders that receive the arguments. For example, in void greet(String name) { ... } the word name is a parameter; in greet("Raju") the string "Raju" is the argument.
Parameter types and usage
Parameters may be of simple primitive types like int, char, float or of complex types such as arrays, strings or objects. A parameter's declared type defines what operations the method can perform. Use clear names that explain expected content, e.g., index, size, threshold, to reduce confusion.
Pass-by-value explained
Passing by value gives the method a copy of the argument. For primitive types, changes to the parameter inside the method do not alter the caller's variable. For example, void increment(int x) { x = x + 1; } does not change the original variable passed in the caller.
Reference semantics for objects and arrays
Complex types like arrays and objects are usually passed by reference or via a copied reference. This means the method receives access to the same underlying object; modifying that object's contents inside the method affects the caller's object. However, reassigning the parameter to a new object inside the method does not change the caller's reference.
Parameter ordering and defaults
Parameters are positional: the first argument matches the first parameter, etc. Many classroom languages used in ICSE do not rely on default parameters; be explicit about every parameter when calling. If optional behaviour is needed, overload methods or document a sentinel value.
Parameter scope and lifetime
Parameters are local to the method and exist only during the call. They receive the argument values at the start of the call and are destroyed at return. Because parameters are local, their names are safe and will not affect other parts of the program unless they refer to a shared object (like an array) whose contents can be changed.
Practical tips
Keep parameter lists short; if many values are needed, group related data into an array or object. Document whether the method will modify parameters (like arrays) or treat them as read-only. This prevents unintended side-effects and helps examiners follow your reasoning.
- Function void increment(int x) { x = x + 1; } Calling int a=5; increment(a); // a remains 5 (pass-by-value)
- Function void clearArray(int[] arr) { for(i) arr[i]=0; } Calling array passed will be modified (reference semantics).
- Method swap(int[] arr, int i, int j) swaps arr[i] and arr[j] and affects the caller's array.
- Parameter lifetime: created at call, destroyed at return.
- Argument to parameter mapping: argument_i -> parameter_i
Return Types and Using Return Values
Purpose of a return type
The return type of a method tells the compiler and the programmer what kind of value to expect when the method finishes. It is part of the method's interface and determines how the caller can use the result. A return type may be a primitive (int, float), an object (String, array) or void if no value is returned.
Using returned values
When a method returns a value, the caller can store it in a variable, use it in expressions, or pass it directly to another method. Example: int area = computeArea(length, breadth); or System.out.println(computeArea(2,3));. Returning values separates computation from output, enabling reuse in different contexts.
Returning composite results
A method can return arrays or objects to convey multiple pieces of information. If multiple values are required, return an array or a small object/record containing named fields. For Class 10, returning an array or reusing a passed array for outputs are common patterns. Be clear whether the returned array is newly created or a reference to an existing array.
Multiple return points and correctness
Methods may have multiple return statements for different conditions (early exit). While convenient, ensure that for all possible execution paths a non-void method returns a value of the declared type. Failure to do so leads to compile-time errors in statically-typed languages.
Sentinels and absence of a meaningful value
Sometimes a method cannot produce a valid result (for example, searching for an element that is not present). Use accepted sentinel values (like -1 for an index not found) or boolean success indicators combined with output parameters. Document the sentinel choice so calling code can handle it correctly.
Boolean return types for checks
For queries and checks, prefer boolean returns, e.g., boolean isPrime(int n) returns true or false. Boolean methods read naturally when used in conditionals: if (isPrime(x)) { ... }.
Best practices for returns
Keep function responsibilities focused: functions should compute results and return them; procedures (void) should perform actions or change program state. Avoid mixing heavy side-effects with returned computations. Name methods to reflect their return behaviour: getSum, findIndex, computeAverage, isValid, etc.
- int factorial(int n) returns product of numbers from 1 to n. Call: int f = factorial(5); // f = 120
- int find(int[] arr, int key) returns index or -1 if not found. Call: int idx = find(arr, 7);
- void printTable(int n) prints multiplication table for n and returns nothing.
- Method declaration form: <return-type> <name>(...) { ... return <value>; }
- Sentinel for not found: return -1; when index not found
Scope and Lifetime of Variables
Understanding scope
Scope defines where a variable name can be used in a program. In method-based programs, there are typically local scope (inside a method or block), parameter scope (parameters act like locals), and global or class scope (variables declared outside methods). A variable is accessible only within its scope; using it outside causes compile errors.
Local variables and blocks
Local variables are declared inside a method or within smaller blocks such as loops or if-statements. Their lifetime starts when execution enters the block and ends when execution leaves it. Each call to a method creates a new set of its local variables and parameters. This means recursive calls get separate copies of these locals, avoiding interference between calls.
Global and class-level variables
Variables declared at class-level or outside all methods are visible to all methods in that class (subject to access control). These variables live for the lifetime of the object or program and can be used to share state. Excessive use of globals can harm modularity and make reasoning about code harder because many methods can change the same variables.
Shadowing and its pitfalls
Shadowing happens when a local variable or parameter uses the same name as a global variable. Inside the local scope the inner variable hides the outer one. This can cause subtle bugs if the programmer expects the global variable to be used but the local one is active. To avoid confusion, use distinct names or clear comments.
Parameter scope and lifetime
Parameters are local to the method and exist only during the call. They receive the argument values at the start of the call and are destroyed at return. Because parameters are local, their names are safe and will not affect other parts of the program unless they refer to a shared object (like an array) whose contents can be changed.
Static lifetime and persistent state
Some languages provide static or persistent variables that keep their value between calls. For Class 10 practice, mention class-level variables and their persistent lifetime across method calls. Document when methods read or modify such variables so behaviour is clear.
Best practices
Prefer local variables and pass data through parameters. Limit the use of globals to truly shared configuration values. Choose descriptive names to avoid accidental shadowing. When writing exam answers, explicitly state assumptions about scope if it affects behaviour, and show return to caller in trace tables to demonstrate lifetime of locals.
- Local: void f() { int x = 5; } // x exists only inside f
- Global: int count; void inc() { count = count + 1; } // count shared by methods
- Shadowing: int a = 10; void demo() { int a = 5; // local a hides global a }
- Scope: variable declared in block B is accessible only inside B and nested blocks.
- Lifetime: local variables created at entry of block, destroyed at exit.
Method Overloading
Definition and purpose
Method overloading means defining two or more methods with the same name in the same class or scope but with different parameter lists (different types or number of parameters). Overloading lets you present a single conceptual operation with multiple ways to call it depending on the data available. This improves readability because related operations share a single name.
How the language chooses
At compile time, the language chooses which overloaded method to call based on the number and types of arguments in the method call. The signature (method name + parameter list) must be unique for each overload. Return type alone cannot be used to distinguish overloaded methods in most languages; the parameters must differ.
Common uses of overloading
Overloading is used for convenience: write print(String s), print(int n), and print(double d) to handle different types. It is also used to support different numbers of inputs: add(a,b), add(a,b,c). Constructors are commonly overloaded to allow various ways of creating an object: default constructor, constructor with name, constructor with name and id, etc.
Ambiguities and conversions
Overloading can cause ambiguity if automatic type conversions make multiple overloads viable. For example, a call with a literal 5 might match both an int and a long method after conversion. To avoid ambiguity, supply exact types or cast explicitly. When designing overloads, prefer distinct parameter types or counts to help the compiler make a unique choice.
Design guidelines
Keep overloaded methods consistent in behaviour and purpose. They should perform closely related tasks and differ only in how input is provided. Avoid overloading when methods do very different jobs; different names are clearer. Document overloaded forms so callers know which one to use and what each expects.
Examples and good practice
Example: int max(int a,int b) and double max(double a,double b) both compute maximum, but for different types. Example: void save(String name) and void save(String name,int version). When answering exam questions, show all overloaded declarations and give examples of calls that choose each overload to demonstrate understanding.
- int max(int a, int b) and double max(double a, double b) — both named max but accept different types.
- void show(String s) and void show(String s, int times) — same name, different parameter count.
- Constructors: Student() and Student(String name) providing different ways to create objects.
- Overload rule: Methods with same name but different parameter lists are allowed; return type cannot disambiguate.
- Ambiguity arises when conversion allows multiple overloaded methods to match the call.
Modular Design and Decomposition
Concept of modular design
Modular design divides a program into separate units (modules or methods) each responsible for a well-defined task. Decomposition is the process of breaking a large problem into smaller subproblems that can be solved independently. This approach reduces complexity: developers can focus on one module at a time, test it thoroughly, and reuse it in other programs.
Top-down design methodology
Top-down design begins with a high-level description of the program tasks in main and then refines each task into helper methods. For example, a payroll program may start with computePayroll() and then decompose it into readEmployeeData(), computeSalary(emp), and printPayslip(emp). Each helper can further split into smaller methods if needed. This staged development clarifies responsibilities and interfaces.
Designing interfaces
A method's interface—its name, parameters and return type—should be stable and minimal. Decide what inputs are necessary and what the method should return. Keep interfaces small to reduce coupling between modules. When many values are needed, group them in an array or object to avoid long parameter lists and to make future changes easier.
Benefits of modularity
Modularity improves readability, maintainability, and testability. Individual modules can be tested (unit testing) and then integrated. Teams can develop modules in parallel. Reuse of modules across different programs reduces duplication. In exams, modular solutions gain clarity marks and let examiners award marks for individual correct methods even if the whole program is not fully working.
Balancing granularity
Decide the right size for a module: too large hides logic and makes testing hard; too small creates excessive overhead and reduces clarity. Aim for methods that fit on one screen and perform a single logical step, such as validateInput(), computeAverage(), or displayReport().
Refactoring and evolution
As requirements change, refactor by extracting repeated code into new methods, renaming methods for clarity or simplifying interfaces. Refactoring keeps code healthy. For assignments and projects, keep a brief design note explaining the decomposition and interfaces so marker or team members can understand the program at a glance.
- Designing a calculator: main() calls getInput(), performOperation(op, x, y), displayResult(res).
- A library management system broken into methods: addBook(), searchBook(), issueBook(), returnBook().
- Decompose sorting: main() calls readArray(), sortArray(), displayArray(), where sortArray() may use swap() as helper.
- Top-down decomposition: Problem -> Subtask1, Subtask2, ... -> Methods implementing each subtask
- Interface rule: Provide clear parameter and return descriptions for each method.
Recursion and Recursive Methods
What is recursion?
Recursion is a programming technique where a method calls itself to solve smaller instances of the same problem. A correct recursive solution requires two parts: a base case that stops further recursion, and a recursive case that reduces the problem to a smaller instance. When correctly designed, recursion maps naturally to problems described in terms of smaller subproblems.
Understanding the call stack
Each recursive call creates a new activation record on the call stack containing its local variables and parameters. The sequence of calls continues until the base case is reached; then the calls return values which are combined up the stack. For teaching and exams, drawing the call tree or using a trace table helps visualise the flow and ensure proper termination.
Common recursive problems
Frequent examples include factorial calculation, Fibonacci numbers, sum of first n numbers, reversing a string recursively, and traversal of tree-like structures. For factorial: fact(n) = n * fact(n-1) with base fact(0)=1. For sum: sum(n) = n + sum(n-1) with sum(0)=0.
Advantages and disadvantages
Advantages: recursion often yields concise, clear code matching mathematical definitions and problem statements. Disadvantages: recursion can use more memory (call stack) and be slower due to overhead. Some recursive formulations (like naive Fibonacci) are exponentially inefficient; iterative or memoised approaches are preferred for larger inputs.
Correct design and base cases
Always define an appropriate base case to avoid infinite recursion. Verify that each recursive step makes progress toward the base case. For instance, if a function calls itself with the same parameter, it will never end. Test recursion with small inputs first and trace the calls on paper for clarity.
Converting recursion to iteration
Many recursive solutions can be rewritten iteratively using loops or an explicit stack. For Class 10, focus on writing correct recursive solutions for standard tasks and understanding their trace. When efficiency is required, consider an iterative version or optimised recursion (memoisation) where appropriate.
- Factorial: int fact(int n) { if (n==0) return 1; else return n * fact(n-1); }
- Sum of first n numbers: int sumN(int n) { if (n==0) return 0; else return n + sumN(n-1); }
- Fibonacci (recursive): int fib(int n) { if (n<=1) return n; else return fib(n-1)+fib(n-2); }
- Recursive pattern: if (base_condition) return base_value; else return combine(current, recursive_call(smaller));
- Factorial: n! = n * (n-1)! with 0! = 1
Debugging and Testing Methods
Importance of testing
Testing and debugging are essential steps to ensure methods behave as expected. Rather than treating the program as a whole, test methods individually (unit testing) so you can find the exact location of faults. A sound testing habit saves time and avoids chasing bugs across unrelated code.
Unit testing approach
For each method, prepare test cases covering normal inputs, boundary cases and invalid inputs. For example, for a search method include tests where the key is at the start, at the end, not present, and when the array is empty. Record expected output and compare with actual results. In school work, write a short test table showing inputs, expected and actual outputs.
Dry runs and trace tables
Dry running a method means stepping through the code on paper or mentally and recording variable values at each step. Trace tables list variables and their values after each line or loop iteration. This method is particularly valuable for exam answers and for understanding recursion where the call stack must be visualised.
Debugging techniques
Use print statements to show parameter values and key intermediate results. Place prints at method entry and before return to check inputs and outputs. Remove or comment out debug prints after resolving the problem. When available, use a debugger to set breakpoints and inspect variables live.
Common error types
Pay attention to logic errors (wrong condition or formula), off-by-one errors in loops and array indices, null or uninitialised variables, and missing base cases for recursion. Runtime exceptions and type errors often point to incorrect assumptions about inputs; validate inputs to avoid these.
Systematic debugging steps
1) Reproduce the problem reliably. 2) Isolate the method causing it by testing parts. 3) Use a dry run or debug prints to locate the faulty line. 4) Correct and re-run tests including previous passing cases to ensure no regressions. Keep a short test log showing results.
Documentation of tests
Maintain a simple test plan for each method listing test inputs, expected outputs and actual outputs. This helps teachers see coverage and demonstrates careful work in exams and assignments.
- Testing max(a,b): test with a>b, a<b, a==b and negative numbers to ensure correctness.
- Debugging factorial: if fact(0) returns 0, find missing base case and correct it to return 1.
- Using trace: call sumN(3) and record sequence of calls and returns to verify the final sum is 6.
- Test coverage idea: include typical, edge and invalid cases for each method.
- Debug loop: reproduce -> isolate -> trace -> fix -> re-test
Documentation and Method Comments
Purpose of documentation
Clear documentation explains what a method does, what its parameters mean, what it returns, and any side-effects. Good comments make code easier to understand for teachers, other students, or the programmer returning to the code later. In exam answers, a short comment above each method helps the examiner quickly see your intent and awards marks for clear design.
Essential elements of a method comment
A compact comment block should include: a one-line purpose, parameter descriptions (name and meaning), return description, preconditions (assumptions about inputs) and side-effects (e.g., modifies array). For example: // Purpose: compute average of marks // Parameters: marks - array of integers (length>0) // Returns: average as double // Side-effects: none.
Inline comments and self-documenting code
Use short inline comments to explain tricky lines or decisions. Prefer self-documenting code by choosing descriptive method and variable names—this reduces the need for comments. For instance, computeAverage is clearer than ca or avgFn. Avoid redundant comments that simply restate the code; instead explain why something is done.
Comment placement and style
Put a brief comment above the method header describing the contract. Use consistent style for comment blocks. If the method is longer, use inline comments to mark sections. Keep comments updated when code changes—stale comments mislead readers and can be worse than none.
Examples for exams and projects
For ICSE answers, a short comment block showing inputs and outputs is often sufficient. For projects, include a README describing overall design and how methods relate. Provide sample inputs and outputs so testers can verify program behaviour quickly.
Documenting algorithms and complexity
For more complex methods, include a short note about algorithm idea and expected time complexity (informal: linear, quadratic). This shows deeper understanding and is useful when evaluating performance trade-offs in exams and assignments.
Practical checklist
Before submission, ensure each method has a comment, names are clear, preconditions are stated, and any non-obvious effects are documented. Clear documentation improves marks and makes your work easier to verify and maintain.
- // Purpose: returns maximum element // Parameters: arr - integer array (length>0) // Returns: maximum integer in arr int max(int[] arr) { ... }
- // Purpose: print student details // Parameters: name, rollNo // Returns: void void printStudent(String name, int roll) { ... }
- Comment template: // Purpose: ... // Parameters: name - description // Returns: ... // Side-effects: ...
- Naming rule: method names should reflect action or query (verb or is/has for boolean).
Error Handling in Methods
What is error handling?
Error handling is the practice of anticipating and managing situations where a method cannot perform its normal task because of invalid input, resource problems (like missing files) or unexpected conditions. Proper handling prevents abrupt crashes and lets calling code respond sensibly to failures. In Class 10 contexts, error handling usually means input validation and returning clear sentinel values or messages.
Input validation as first line of defence
Always check inputs at the start of a method: ensure indices are within array bounds, numbers fall within allowed ranges, and references are not null before use. If a precondition is not met, the method should either return a documented sentinel value or print an informative error message. Validating early prevents deeper logic errors.
Signalling errors: sentinel values and status flags
If a method cannot return a valid result, one option is to return a sentinel value, like -1 for a not-found index. Another option is to return a boolean indicating success or failure and use an output parameter (array or object) to pass back results. Choose a pattern and document it so callers know how to detect failures.
Graceful messages
When an error occurs, print a clear message that explains what went wrong and where (e.g., "Error: division by zero in computeAverage"). For assignment code, clear messages help teachers replicate problems; for exams, state the handling approach in comments if you cannot write full code for messaging.
Defensive programming
Defensive programming means writing methods to fail safely: check conditions, refuse to act on invalid data, and avoid changing global state when input is invalid. This reduces hard-to-find bugs. When a method changes a passed array only on valid input, the caller can rely on consistent state after a failure.
When exceptions are beyond scope
Many languages have exception mechanisms, but for Class 10 focus on simple checks and clear return values. If exceptions are used in project work, document which exceptions may be raised and where they should be caught. In exams, prefer explicit checks and documented sentinels.
Design rules and examples
1) Validate inputs at entry. 2) Return sentinel or boolean status for failure. 3) Document behaviour. 4) Avoid silent failures—report or return an indicator. Following these rules produces robust, testable methods.
- int safeDivide(int a, int b) { if (b==0) return Integer.MIN_VALUE; else return a/b; } // use MIN_VALUE as error sentinel
- int find(int[] arr, int key) { if (arr==null) return -1; ... } // check for null input
- boolean tryParse(String s, int[] out) { try parse; if fail return false; else out[0]=value; return true; }
- Error signalling: choose sentinel values or boolean status returns and document them.
- Validation rule: check preconditions at method entry: if (!precondition) handle or return error.
Working with Arrays and Methods
Passing arrays to methods
Arrays are common data structures in Class 10 problems. When passed to a method, an array reference allows the method to access and modify the actual array elements. This is efficient because the entire array is not copied. However, it means the called method can change the caller's array, so document whether the method modifies the array or treats it as read-only.
Typical array operations implemented as methods
Common helper methods include sum(int[] arr), max(int[] arr), min(int[] arr), countOccurrences(int[] arr, int key), reverse(int[] arr) and sort(int[] arr). Breaking each operation into its own method helps testing and reuse. For example, reverse may call swap(arr,i,j) as a small helper which swaps two elements in-place.
Returning arrays
Methods can return new arrays, for example when transforming input data. If returning a new array, make it clear whether the caller must free or replace the original (in languages that require it). In many school languages, simply returning a new array and assigning it in the caller is enough: int[] copy = copyArray(arr);.
Index checks and safety
Always check array bounds before accessing elements. Methods that accept an index should check that 0 <= index < arr.length and handle invalid indices gracefully. Off-by-one errors are frequent in loops; write loop bounds carefully and test with small arrays to ensure correct behaviour.
Sorting and search helpers
Implement simple sorts (bubble, selection) as methods using helper method swap. For searching, write linearSearch(arr,key) and, after sorting, a binarySearch(arr,key) method. Keep algorithms simple and correct: for Class 10, correctness and traceability matter more than advanced performance tricks.
Memory and copies
Avoid copying arrays unnecessarily. If you must preserve an original array while transforming data, make a copy before modifying. For small inputs typical of school problems, copying is acceptable; for larger data, prefer in-place algorithms and document behaviour.
Exam presentation
In exam answers, show readArray(), processArray() and displayArray() methods clearly. Provide traces and sample outputs to demonstrate understanding of how arrays and methods interact.
- int sum(int[] arr) { int s=0; for(i=0;i<arr.length;i++) s+=arr[i]; return s; }
- void reverse(int[] arr) { for(i=0;i<arr.length/2;i++) swap(arr,i,arr.length-1-i); } // modifies caller's array
- int[] copyArray(int[] arr) { int[] b=new int[arr.length]; for(i) b[i]=arr[i]; return b; }
- Sum: sum = Σ arr[i] for i=0..n-1
- Average: avg = (sum of elements) / n
String Handling with Methods
Why separate string tasks into methods?
Strings require frequent operations: comparing, searching, splitting, reversing and counting characters. Writing each operation as a method keeps the code organised and allows reuse. For Class 10 examples, string methods commonly include length, reverse, isPalindrome, countVowels, toUpperCase and substring search.
Immutable strings and effects
In many languages strings are immutable: methods that appear to change a string actually return a new string. Therefore, if you write String upper = toUpper(s); you must assign the result. If your language provides mutable string builders or character arrays, methods can modify these in-place—but document the behaviour clearly to avoid confusion.
Design choices for string methods
Decide whether comparisons are case-sensitive and whether spaces and punctuation are significant. For palindrome checks, state whether 'Madam' and 'madam' are treated as equal; if case-insensitive, convert both strings to a single case before comparing, and possibly remove non-letter characters if required.
Character level processing
For operations like reversing or swapping characters, convert the string to a character array and operate on indices, then rebuild a string. This approach is clear and easy to test. For counting vowels or consonants, loop through characters and use conditionals to increment counters, paying attention to both uppercase and lowercase letters.
Edge cases and null checks
Always check for empty or null strings at method entry. For example, reverse("") should return an empty string and not throw an error. Document how your method behaves on edge input and ensure tests include these cases.
Performance note
Building strings by concatenation inside loops may be inefficient in some languages. For small examples in exams this is acceptable, but for larger inputs prefer accumulating characters in a buffer or character array and then creating a string once at the end.
Examples and presentation
Provide clear method signatures: String reverse(String s), boolean isPalindrome(String s), int countVowels(String s). Include a brief comment above each method explaining whether input is modified or a new string is returned. This clarity helps examiners and teammates understand behaviour at a glance.
- String reverse(String s) { String res=""; for(i=s.length()-1;i>=0;i--) res += s.charAt(i); return res; }
- boolean isPalindrome(String s) { return s.equals(reverse(s)); }
- int countVowels(String s) { loop through chars and increment counter when char in {a,e,i,o,u,A,E,I,O,U} }
- Length: n = s.length()
- Palindrome: s == reverse(s) (subject to case/space rules)
Combining Methods to Solve Problems
High-level coordination
Solving real problems involves coordinating several helper methods. The main method acts as an orchestrator: it reads input, calls processing methods in a defined order, and prints results. Designing the data flow between methods—what is passed and what is returned—is an important part of program design.
Designing clear interfaces
When combining methods, ensure each method has a clear and minimal interface. Decide which method returns data and which modifies passed structures. For example, readInput() returns an array, computeStatistics(arr) returns a result object or array, and void printStats(stats) prints them. This separation keeps each method testable independently.
Example decomposition
Consider the problem: given marks for students, print the highest, lowest and average. Decompose as: int[] readMarks(int n), Stats computeStats(int[] marks) that returns sum/min/max/average, and void printStats(Stats s). The computeStats method could itself call helper methods findMax, findMin and computeAverage. This modular approach simplifies verification and reuse.
Chaining methods
Methods can be chained: result = methodC(methodB(methodA(input))). While chaining is powerful, ensure intermediate results are clear and intermediate computations are not wasted. For clarity, assign intermediate values to well-named variables in main to aid debugging and tracing.
Integration testing
After unit testing individual methods, perform integration tests to ensure methods work together. Use small realistic test cases covering normal and edge inputs. Integration tests verify that parameter/return contracts are respected and that combined behaviour gives expected final output.
Refactoring and reuse
As requirements evolve, refactor by extracting repeated logic into new helper methods and by renaming methods for clarity. Reuse existing tested methods in new programs to save time and reduce bugs.
Exam presentation
For ICSE answers, clearly present main and helper methods; provide a brief comment that describes the data flow. If required, include a sample run or trace that shows how values move between methods. This presentation helps examiners award marks for correct method decomposition and data handling.
- Program to manage exam marks: main calls getInput(), validateInput(), processMarks(), displayStats().
- Program to search and sort: main reads array, calls sortArray(arr), then calls binarySearch(arr, key) to find index.
- Second largest: findSecondLargest(arr) returns index or value after single-pass method.
- Data flow: result = method2(method1(input)) when chaining methods
- Integration testing rule: verify end-to-end behaviour using combined real inputs.
Performance Considerations and Best Practices
Why performance matters even in Class 10
While Class 10 focuses mainly on correctness and clarity, basic performance awareness helps write programs that finish quickly and use memory reasonably. Simple choices—avoiding repeated recomputation, using in-place operations, and selecting appropriate algorithms—make solutions practical for larger inputs and easier to grade.
Avoid repeated work
If a computed value is needed multiple times, compute it once and pass it to methods rather than recomputing every time. For example, compute the sum of an array once before using it to compute multiple statistics. Repeated work inside loops can drastically increase running time.
Choosing appropriate algorithms
Know the difference between linear algorithms (single loops) and quadratic algorithms (nested loops). For tasks such as finding a maximum or sum, a single pass is sufficient (O(n)). Sorting with simple algorithms like selection or bubble sort is easy to implement but is O(n^2); for large n this becomes slow. For Class 10, explain the difference and justify algorithm choice based on expected input sizes.
Memory considerations
Avoid unnecessary copies of large arrays or strings. If you must preserve an original array while transforming data, make a single copy and document it. For small inputs typical of school problems, copying is acceptable, but show awareness that repeated copying wastes memory and time.
Method size, cohesion and readability
Keep methods short and focused. Highly cohesive methods that perform one task are easier to test and understand. Very long methods hide details and increase the chance of errors. If a method needs many parameters, consider grouping related data into an array or object or splitting the method into smaller helpers.
Parameter passing and efficiency
Passing large data structures by reference is more efficient than copying them. Use this to your advantage but document whether a method may modify the passed object. When returning large arrays, consider returning a reference to avoid copying, unless the method must preserve the original input.
Readable code over micro-optimisation
For schoolwork and exams, prefer clear, well-documented code over tiny optimisations that reduce readability. A clear algorithm that is easy to follow scores better than clever, compressed code that is hard to understand. Mention algorithmic complexity informally (linear, quadratic) when relevant to show awareness.
Practical checklist
Use single-pass algorithms when possible, avoid redundant computations, keep methods short, document side-effects, and test performance with realistic inputs. These practices lead to solutions that are correct, maintainable and reasonably efficient.
- Avoid recomputing sum inside a loop: compute once before loop rather than inside repeated calls.
- Swap helper reduces repeated code in many sorting methods: use swap(arr,i,j) instead of repeating three assignments.
- Replace nested loops when possible: for finding max in array, one loop is enough instead of comparing every pair.
- Linear scan: O(n) time for operations like sum or max with one loop.
- Nested loops: O(n^2) operations for simple quadratic algorithms like bubble sort.
Sample ICSE-Style Problems and Solutions
Purpose and format
ICSE exam questions often ask for small programs or method implementations that demonstrate clear design, correct syntax and traceability. These problems favour modular answers where each helper method is shown with a brief comment, followed by a main that uses them. Marks are awarded for correct method signatures, logic and appropriate tracing where requested.
Common problem types
Typical problems include: write a method to compute factorial, method to check palindrome, method to find second largest in an array, reading and validating input, and combining methods to produce formatted output such as student reports. Each problem tests ability to design suitable parameters, choose correct return types, and show sample runs.
Solution strategy
1) Read the question and note input-output format and any constraints (like array length). 2) Decide decomposition: which helper methods you will write. 3) Write method headers with brief comments, then implement methods. 4) Write main that calls helpers and prints results. 5) If asked for a trace, provide a clear table showing method calls and key variable values for given sample input.
Example: Second largest problem
Decompose: readArray(), findSecondLargest(arr), and printResult(value). findSecondLargest should handle duplicates and return the second distinct largest value or a sentinel if not available. Provide a trace for a sample array showing updates to max and secondMax after each element. State assumptions like array length >=2.
Example: Palindrome problem
Provide reverseString(s) and isPalindrome(s) methods. In main, read a string, call isPalindrome and print "YES" or "NO". If the question mentions ignoring case or spaces, implement and document that step explicitly and show a sample input and output.
Presentation tips for exams
Use descriptive names and short comment blocks for methods. Provide sample input and the corresponding output. If asked for tracing, use neat columns with steps, method name, variables and returned values. Start by listing assumptions (e.g., array length >=2) so the examiner understands preconditions for your methods.
- Program template: // Purpose: ... public static void main(...) { // read input; call helper methods; print output } // helper methods below
- Second largest solution outline: findSecondLargest(arr) with single pass keeping max and secondMax.
- Palindrome program outline: reverseString and compare to original (ignoring case/spaces if specified).
- Second largest rule: keep max and secondMax; update when element > max or element between max and secondMax.
- Palindrome: compare s and reverse(s) respecting specified rules (case/space).
Exam Skills: Writing Methods in Answers and Tracing
What examiners look for
Examiners expect clear method headers, correct parameter lists and return types, short comments stating purpose, and a well-structured main that calls helper methods. Presenting a program in a modular manner with brief documentation and sample runs makes it easy to award marks for design and logic even if minor syntax errors are present.
Writing method signatures
Always write full method signatures with types, names and return types. Include a one-line comment above each method describing its purpose, parameters and return. For example: // Purpose: compute average of marks // Parameters: marks - int array // Returns: double average. This helps the examiner verify your method's contract quickly.
Dry run and trace technique
When asked to trace a method, present a table with columns like Step, Method/Line, Key Variables and Returned Value. Show calls and returns clearly. For recursive methods, show the call stack or a sequence of calls with parameter values at each call and the returned values when unwinding. A clear trace demonstrates understanding of control flow and variable scope.
Partial answers and partial credit
If you cannot implement everything, write correct method headers and explain the algorithm in comments or pseudocode. Examiners often award partial credit for correct design and clear logic. Show at least one method fully implemented and the rest as clear pseudocode if time is short.
Presentation and time management
Plan your answer: spend a few minutes deciding method decomposition before coding. Write main and method headers first; then implement the bodies. Keep indentation consistent and choose descriptive variable names. Provide a short sample input and output to show your expected behaviour. This organized approach saves time and makes grading easier.
Common pitfalls to avoid
Do not mix responsibilities in a single method; avoid very large parameter lists; explicitly state assumptions like minimum array length; and update comments if you change method behaviour. Demonstrating these exam skills increases clarity and helps secure method-design marks.
- Answer layout: Comment block -> full program with main and methods -> sample input/output -> trace table.
- Trace factorial(4): show stack of calls fact(4), fact(3), fact(2), fact(1), fact(0) and returns to compute 24.
- Method header example: // Purpose: find index of key int find(int[] arr, int key) { ... }
- Trace table columns: Step | Method | Variables | Return/Output
- Exam structure rule: always provide brief comment describing assumptions and inputs.
Key Concepts
- Custom method
- A named block of code written by the programmer to perform a specific task and be called from other parts of a program.
- Parameter
- A variable in a method declaration that accepts a value when the method is called.
- Argument
- The actual value or expression passed to a method when calling it.
- Return type
- The data type of value a method sends back to its caller, or void if it returns nothing.
- Scope
- The region of the program where a variable is visible and can be accessed.
- Lifetime
- The period during program execution that a variable exists in memory.
- Pass-by-value
- Parameter passing where a copy of the argument is given to the method so changes do not affect the caller's variable.
- Reference semantics
- When a method receives a reference to an object or array so modifications inside the method affect the original object.
- Method overloading
- Declaring multiple methods with the same name but different parameter lists to perform related tasks.
- Recursion
- A technique where a method calls itself to solve a smaller instance of the same problem, using a base case to stop.
- Modular design
- Dividing a program into independent methods or modules, each handling one specific task.
- Unit testing
- Testing individual methods in isolation with a set of inputs to verify correct behaviour.
- Sentinel value
- A special value returned by a method to indicate an error or absence of a valid result.
- Shadowing
- When a local variable uses the same name as a global variable and hides it within its scope.
- Side-effect
- Any change a method makes to data outside its local scope, such as modifying global variables or passed arrays.
Practice Questions
-
Write a method to calculate factorial of a non-negative integer and show a dry run for n=5. / एक विधि लिखिए जो किसी गैर-ऋणात्मक पूर्णांक का फैक्टोरियल निकाले और n=5 के लिये ड्राइ-रन दिखाइए।
Show answer
English answer: Declare int factorial(int n) { if (n==0) return 1; else return n * factorial(n-1); } Dry run for n=5: factorial(5) calls factorial(4) calls factorial(3) calls factorial(2) calls factorial(1) calls factorial(0) which returns 1. Then returns: factorial(1)=1*1=1, factorial(2)=2*1=2, factorial(3)=3*2=6, factorial(4)=4*6=24, factorial(5)=5*24=120. / हिंदी उत्तर: घोषणा:int factorial(int n) { if (n==0) return 1; else return n * factorial(n-1); } n=5 का ड्राइ-रन: factorial(5) -> factorial(4) -> factorial(3) -> factorial(2) -> factorial(1) -> factorial(0) जो 1 लौटाता है। फिर लौटते हुए: factorial(1)=1, factorial(2)=2, factorial(3)=6, factorial(4)=24, factorial(5)=120।
-
Explain difference between passing a primitive type and an array to a method with an example. / किसी मूल प्रकार और किसी ऐरे को विधि में पास करने में अंतर उदाहरण के साथ समझाइए।
Show answer
English answer: Passing a primitive (e.g., int) gives the method a copy; changes inside the method do not affect the caller's variable. Example: void inc(int x) { x = x+1; } Calling with a=5; inc(a); a remains 5. Passing an array passes a reference to the array object; the method can change its elements and the caller sees those changes. Example: void setZero(int[] arr) { arr[0]=0; } Calling with arr shows modified arr. / हिंदी उत्तर: मूल प्रकार पास करने पर विधि को एक प्रति मिलती है; विधि के अंदर बदलाव मूल चर को प्रभावित नहीं करते। उदाहरण: void inc(int x) { x = x+1; } यदि a=5 है और inc(a) बुलाते हैं तो a अब भी 5 रहता है। ऐरे पास करने पर विधि को ऐरे के संदर्भ का पता मिलता है; विधि इसके तत्त्व बदल सकती है और कॉलर को परिवर्तन दिखाई देगा। उदाहरण: void setZero(int[] arr) { arr[0]=0; } कॉल के बाद arr बदल गया होगा।
-
Write a method findSecondLargest(int[] arr) that returns the second largest value in an array of at least two elements. Show a trace for {5,12,7,12,3}. / एक विधि findSecondLargest(int[] arr) लिखिए जो कम से कम दो तत्त्व वाले ऐरे में दूसरा सबसे बड़ा मान लौटाए। {5,12,7,12,3} के लिये ट्रेस दिखाइए।
Show answer
English answer: Outline: int findSecondLargest(int[] arr) { int max = Integer.MIN_VALUE; int second = Integer.MIN_VALUE; for (int v: arr) { if (v > max) { second = max; max = v; } else if (v > second && v < max) { second = v; } } return second; } Trace for {5,12,7,12,3}: start max=-inf second=-inf; v=5 -> max=5 second=-inf; v=12 -> max=12 second=5; v=7 -> max=12 second=7; v=12 -> equal to max so no change; v=3 -> no change. Return 7. / हिंदी उत्तर: रूपरेखा: int findSecondLargest(int[] arr) { int max = Integer.MIN_VALUE; int second = Integer.MIN_VALUE; for (int v: arr) { if (v > max) { second = max; max = v; } else if (v > second && v < max) { second = v; } } return second; } {5,12,7,12,3} के लिये ट्रेस: आरंभ max=-∞ second=-∞; v=5 -> max=5 second=-∞; v=12 -> max=12 second=5; v=7 -> second=7; v=12 -> कोई परिवर्तन नहीं; v=3 -> कोई परिवर्तन नहीं। लौटाएँ 7.
-
Give a brief style guide for writing methods (naming, comments, size). / विधियाँ लिखने के लिये संक्षिप्त स्टाइल गाइड दीजिए (नामकरण, टिप्पणियाँ, आकार)।
Show answer
English answer: Use meaningful verb-based names (e.g., computeAverage). Keep methods short and focused on a single task. Use clear parameter names and document each method with a one-line purpose, parameter descriptions and return information. Avoid global variables when possible and prevent name shadowing. Test methods with normal and edge cases. / हिंदी उत्तर: अर्थपूर्ण क्रिया-आधारित नामों का प्रयोग करें (जैसे computeAverage)। विधियों को छोटा रखें और एक काम पर केन्द्रित रखें। स्पष्ट पैरामीटर नाम रखें और हर विधि के ऊपर एक-लाइन उद्देश्य, पैरामीटर विवरण और रिटर्न जानकारी लिखें। संभव हो तो ग्लोबल वेरिएबल से बचें और नाम शैडोइंग न करें। विधियों का सामान्य और किनारे के मामलों से परीक्षण करें।
-
What is method overloading? Give two examples. / मेथड ओवरलोडिंग क्या है? दो उदाहरण दीजिए।
Show answer
English answer: Method overloading is defining multiple methods with the same name but different parameter lists. Examples: int add(int a,int b) and double add(double a,double b); void print(String s) and void print(String s,int times). / हिंदी उत्तर: मेथड ओवरलोडिंग का अर्थ है एक ही नाम की अनेक विधियाँ परिभाषित करना जिनके पैरामीटर सूचियाँ अलग हों। उदाहरण: int add(int a,int b) और double add(double a,double b); void print(String s) और void print(String s,int times)।
-
Write a recursive method to compute the sum of first n natural numbers and give complexity reasoning. / पहले n प्राकृतिक संख्याओं के योग का रिकर्सिव विधि लिखिए और जटिलता बताइए।
Show answer
English answer: int sumN(int n) { if (n==0) return 0; else return n + sumN(n-1); } Time complexity: O(n) because there are n recursive calls; space complexity: O(n) due to call stack. / हिंदी उत्तर: int sumN(int n) { if (n==0) return 0; else return n + sumN(n-1); } समय जटिलता: O(n) क्योंकि n रिकर्सिव कॉल्स हैं; स्थान जटिलता: O(n) कॉल स्टैक के कारण।
-
A method is declared as void process(int[] arr). It sets arr[0]=0. If main has int[] a={1,2}, what is printed after calling process(a)? Explain. / एक विधि void process(int[] arr) घोषित है। यह arr[0]=0 सेट करती है। यदि main में int[] a={1,2} है, तो process(a) कॉल के बाद क्या प्रिंट होगा? समझाइए।
Show answer
English answer: After process(a) the array a will be {0,2} because the array reference passed to the method refers to the same object, so changing arr[0] affects the caller's array. Printing a[0] would show 0. / हिंदी उत्तर: process(a) के बाद ऐरे a = {0,2} होगा क्योंकि ऐरे का संदर्भ विधि को दिया गया और वही वस्तु बदली गयी। a[0] प्रिंट करने पर 0 दिखेगा।
-
Design methods to read student marks, compute average and print grade where grade is 'A' for avg>=75, 'B' for 60-74, 'C' for 50-59 and 'F' otherwise. Show method signatures. / छात्र के अंक पढ़ने, औसत निकालने और ग्रेड प्रिंट करने के लिये विधियाँ डिजाइन कीजिये जहाँ ग्रेड 'A' avg>=75 के लिये, 'B' 60-74 के लिये, 'C' 50-59 के लिये और अन्यथा 'F' हो। विधि हस्ताक्षर दिखाइए।
Show answer
English answer: Signatures could be: int[] readMarks(int n) // reads n marks and returns array double computeAverage(int[] marks) // returns average char gradeFromAverage(double avg) // returns grade char computeAndPrintGrade(int[] marks) // computes avg, prints grade and returns grade (or void) Example flow: marks = readMarks(n); avg = computeAverage(marks); char g = gradeFromAverage(avg); print(g); / हिंदी उत्तर: हस्ताक्षर हो सकते हैं: int[] readMarks(int n) // n अंक पढ़कर ऐरे लौटाए double computeAverage(int[] marks) // औसत लौटाए char gradeFromAverage(double avg) // ग्रेड लौटाए char computeAndPrintGrade(int[] marks) // औसत निकालकर ग्रेड प्रिंट करे और ग्रेड लौटाए (या void) प्रवाह: marks = readMarks(n); avg = computeAverage(marks); char g = gradeFromAverage(avg); print(g);
-
Explain shadowing with a short code example and its effect. / शैडोइंग को एक संक्षिप्त कोड उदाहरण और उसके प्रभाव के साथ समझाइए।
Show answer
English answer: Shadowing happens when a local variable has the same name as a variable in an outer scope. Example: int x = 10; void demo() { int x = 5; System.out.println(x); } Here demo() prints 5 because its local x hides the outer x. The outer x remains 10 outside the method. / हिंदी उत्तर: शैडोइंग तब होता है जब एक स्थानीय चर का नाम बाहरी स्कोप में मौज़ूद चर के समान हो। उदाहरण: int x = 10; void demo() { int x = 5; System.out.println(x); } यहाँ demo() 5 प्रिंट करेगा क्योंकि स्थानीय x बाहरी x को छिपाता है। बाहरी x कॉल के बाहर 10 ही रहेगा।
Related Laws & Principles
Explore allFoundational laws & principles connected to this chapter — tap to open in the Laws Explorer.