An original Thinka practice paper modelled on the structure and difficulty of the Jun 2023 CCEA AS Level Software Systems Development CL4 paper. Not affiliated with or reproduced from CCEA.
Main Paper
Answer all seven questions. Complete in black ink only. Write your answers in the spaces provided.
19 Question · 100 marks
Question 1 · Short Answer & Definition Matching
8 marks
Complete the table below by giving a correct description AND a suitable example for each control structure term. (a) Sequence [2] (b) Selection (nested IF) [2] (c) Repetition (conditional) [2] (d) Repetition (unconditional) [2]
Show answer & marking schemeHide answer & marking scheme
Worked solution
(a) Sequence: a set of instructions that are executed one after another, in the exact order in which they are written. Example: a series of statements that assign a value to a variable and then immediately print that value. (b) Selection (nested IF): a decision structure in which one IF statement is placed inside another IF (or ELSE) branch, allowing multiple, related conditions to be tested in turn. Example: a nested IF structure used to convert a numeric mark into a grade band (e.g. checking >=70 first, then within the ELSE checking >=50, and so on). (c) Repetition (conditional): a loop that continues to repeat a block of code for as long as (or until) a specified Boolean condition holds true; the number of repetitions is not fixed in advance. Example: a while loop that keeps prompting the user to enter data until a specific sentinel value (e.g. -1) is entered. (d) Repetition (unconditional): a loop that repeats a block of code a fixed, predetermined number of times, known in advance. Example: a for loop, `for (int i = 0; i < 10; i++)`, that executes its body exactly 10 times.
Marking scheme
For each of (a)-(d): [1] for a correct/accurate description of the term; [1] for a valid, correctly matched example. Maximum [8] (4 x [2]).
Question 2 · Short Answer & Definition Matching
8 marks
Complete the table below by giving a correct description AND a suitable example for each data structure term. (a) Static array [2] (b) String [2] (c) Array of objects [2] (d) Array index [2]
Show answer & marking schemeHide answer & marking scheme
Worked solution
(a) Static array: a data structure that stores a fixed number of elements of the same data type in a single named structure, with each element accessed via its position (index). Example: declaring an array to hold 10 integer test scores, `int[] scores = new int[10];`. (b) String: a data structure used to store and process a sequence of characters (text). Example: storing a customer's name, `String customerName = "Jane Doe";`. (c) Array of objects: an array in which each element holds a reference to an object (an instance of a class), rather than a simple/primitive value. Example: an array used to store 20 Student objects, `Student[] students = new Student[20];`. (d) Array index: a whole number used to identify and access the position of a specific element within an array; in most object oriented languages, indexing begins at 0. Example: `scores[0]` accesses the first element of the scores array.
Marking scheme
For each of (a)-(d): [1] for a correct/accurate description of the term; [1] for a valid, correctly matched example. Maximum [8] (4 x [2]).
Question 3 · Class & Constructor Implementation
4 marks
A company wants to develop an object oriented system to manage its employees. Each Employee object has the following private attributes: employeeID (String), name (String), and annualSalary (double).
Write the class header and the private attribute declarations for a class called Employee, with private attributes employeeID (String), name (String) and annualSalary (double).
Show answer & marking schemeHide answer & marking scheme
Worked solution
Java: ``` public class Employee { private String employeeID; private String name; private double annualSalary; } ``` C#: ``` public class Employee { private string employeeID; private string name; private double annualSalary; } ```
Marking scheme
[1] for correct class header (`public class Employee`); [1] for correctly declared private employeeID (String/string); [1] for correctly declared private name (String/string); [1] for correctly declared private annualSalary (double). Maximum [4]. Accept either Java or C# (or equivalent) syntax throughout.
Question 4 · Class & Constructor Implementation
4 marks
Write a constructor for the Employee class that accepts values for employeeID, name and annualSalary as parameters, and assigns each parameter to the corresponding attribute.
Show answer & marking schemeHide answer & marking scheme
[1] for a correct constructor header taking three parameters of the correct types; [1] for correctly assigning employeeID; [1] for correctly assigning name; [1] for correctly assigning annualSalary. Maximum [4].
Question 5 · Class & Constructor Implementation
4 marks
Write a public method called getAnnualSalary() that returns the value of the annualSalary attribute.
Show answer & marking schemeHide answer & marking scheme
Worked solution
Java: ``` public double getAnnualSalary() { return annualSalary; } ``` C#: ``` public double GetAnnualSalary() { return annualSalary; } ```
Marking scheme
[1]-[2] for a correct method header (public, correct return type double, correct/suitable method name); [1]-[2] for a correct return statement that returns the annualSalary attribute. Maximum [4].
Question 6 · Class & Constructor Implementation
4 marks
Write a public method called giveRaise(double percentage) that increases the employee's annualSalary by the given percentage.
Show answer & marking schemeHide answer & marking scheme
[1] for a correct method header (public, void, one double parameter); [1] for correctly calculating the increase amount (annualSalary * percentage / 100); [1] for correctly adding this increase to annualSalary; [1] for correctly assigning/updating the annualSalary attribute. Maximum [4]. Accept `annualSalary *= (1 + percentage/100)` as fully equivalent.
Question 7 · Class & Constructor Implementation
4 marks
Write a public method called toString() that returns a String containing the employee's name and annual salary, formatted as: 'Name: , Salary: £'.
Show answer & marking schemeHide answer & marking scheme
Worked solution
Java: ``` public String toString() { return "Name: " + name + ", Salary: \u00A3" + annualSalary; } ``` C#: ``` public override string ToString() { return "Name: " + name + ", Salary: \u00A3" + annualSalary; } ```
Marking scheme
[1] for correct method header (public, returns String/string, named toString/ToString); [1] for including 'Name: ' followed by the name attribute; [1] for including 'Salary: £' followed by the annualSalary attribute; [1] for correctly concatenating all parts into a single returned String. Maximum [4].
Question 8 · Class & Constructor Implementation
4 marks
State two benefits of making the attributes of the Employee class private rather than public, and explain how encapsulation still allows other parts of the program to access these attributes in a controlled way.
Show answer & marking schemeHide answer & marking scheme
Worked solution
Two benefits of making the Employee class's attributes private rather than public are: (1) it protects the data from being changed directly, accidentally or incorrectly, by code outside the class, helping to maintain the integrity of the object's data; and (2) it hides the internal implementation details of the class from other parts of the program (information hiding), meaning the internal representation of the class could be changed later without breaking other code that uses it, as long as its public interface stays the same. Encapsulation still allows controlled access to these private attributes through public methods, such as the getAnnualSalary() 'getter' method written above, or a 'setter' method to update an attribute; these public methods act as a controlled gateway, and can include validation code (for example, checking that a new annualSalary is not negative) before allowing the private attribute to be changed, which would not be possible if the attribute were simply public.
Marking scheme
[1] for a valid benefit of private attributes (e.g. protects/prevents invalid direct changes to data); [1] for a second valid, distinct benefit (e.g. hides implementation details/information hiding); [1]-[2] for correctly explaining how public getter/setter methods provide controlled access to private attributes (e.g. referencing getAnnualSalary(), and/or the ability to validate data in a setter). Maximum [4].
Write a method called safeDivide(int a, int b) that attempts to divide a by b and returns the result. If dividing by zero causes an error, the method should catch the exception and return -1 instead of allowing the program to crash. Use a try/catch block in your answer.
Show answer & marking schemeHide answer & marking scheme
Worked solution
Java: ``` public int safeDivide(int a, int b) { try { return a / b; } catch (ArithmeticException e) { return -1; } } ``` C#: ``` public int SafeDivide(int a, int b) { try { return a / b; } catch (DivideByZeroException e) { return -1; } } ```
Marking scheme
[1] for correct use of a try block containing the division a / b; [1] for a correctly matched catch block (ArithmeticException in Java, or DivideByZeroException in C#); [1] for correctly returning -1 within the catch block. Maximum [3].
Explain why exception handling (using try/catch blocks) is considered good defensive programming practice, giving two reasons in your answer. Explain what would happen if the division in your safeDivide() method from the previous question was not enclosed in a try/catch block, and b was 0.
Show answer & marking schemeHide answer & marking scheme
Worked solution
Exception handling is considered good defensive programming practice for two main reasons: firstly, it prevents the whole program from crashing unexpectedly when a runtime error (such as attempting to divide by zero) occurs, allowing the program to instead handle the problem gracefully and continue running; secondly, it gives the programmer control over exactly what happens when an error occurs (for example, returning a sensible default value, logging the error, or displaying a helpful message to the user), rather than the program simply stopping with a generic system-generated error. If the division a / b in safeDivide() was not enclosed in a try/catch block, and b was 0, an exception (an ArithmeticException in Java, or a DivideByZeroException in C#) would be thrown at runtime; since there is no surrounding try/catch to handle it, this exception would propagate up and, if not caught anywhere else in the program, would cause the program to terminate abnormally (crash), typically displaying an unhandled-exception error message/stack trace to the user instead of behaving as intended.
Marking scheme
[1] for a valid first reason (e.g. prevents the program crashing); [1] for a valid, distinct second reason (e.g. gives the programmer control over error handling/recovery); [1] for correctly stating that an (unhandled) exception would be thrown; [1] for correctly explaining that this would cause the program to crash/terminate abnormally. Maximum [4].
Write a method called countVowels(String word) that takes a String as a parameter and returns the number of vowels (a, e, i, o, u — case-insensitive) it contains.
Show answer & marking schemeHide answer & marking scheme
Worked solution
Java: ``` public int countVowels(String word) { int count = 0; String vowels = "aeiou"; for (int i = 0; i < word.length(); i++) { char ch = Character.toLowerCase(word.charAt(i)); if (vowels.indexOf(ch) != -1) { count++; } } return count; } ``` C#: ``` public int CountVowels(string word) { int count = 0; string vowels = "aeiou"; for (int i = 0; i < word.Length; i++) { char ch = char.ToLower(word[i]); if (vowels.IndexOf(ch) != -1) { count++; } } return count; } ```
Marking scheme
[1] for correct method header (public, int return type, one String/string parameter); [1] for declaring and initialising a counter variable to 0; [1]-[2] for a correctly bounded loop that iterates over every character of the parameter word; [1] for correctly accessing/extracting each character in turn; [1] for correctly converting the character to a consistent case (upper or lower) for case-insensitive comparison; [1] for a correct condition testing whether the character is a vowel; [1] for correctly incrementing the counter when a vowel is found and returning the counter. Maximum [8].
Write a method called reverseString(String word) that returns a new String containing the characters of the parameter word in reverse order, without using any built-in reverse method.
Show answer & marking schemeHide answer & marking scheme
Worked solution
Java: ``` public String reverseString(String word) { String reversed = ""; for (int i = word.length() - 1; i >= 0; i--) { reversed = reversed + word.charAt(i); } return reversed; } ``` C#: ``` public string ReverseString(string word) { string reversed = ""; for (int i = word.Length - 1; i >= 0; i--) { reversed = reversed + word[i]; } return reversed; } ```
Marking scheme
[1] for correct method header (public, String/string return type, one String/string parameter); [1] for declaring and initialising a String accumulator variable (e.g. to an empty string); [1]-[2] for a loop that correctly starts at the last valid index of word; [1] for the loop correctly counting down to (and including) index 0; [1] for correctly extracting each character in turn; [1]-[2] for correctly appending/building each character onto the accumulator in reverse order and returning it. Maximum [8].
Question 13 · Algorithm Tracing & Theory
5 marks
The following array of integers is to be sorted into ascending order using the bubble sort algorithm: [8, 3, 6, 1, 9]. Show the state of the array after each of the first three passes of the bubble sort, and state whether the array is fully sorted after the third pass.
Show answer & marking schemeHide answer & marking scheme
Worked solution
Starting array: [8, 3, 6, 1, 9]. Pass 1 (compare/swap each adjacent pair left to right): 8,3 → swap → [3,8,6,1,9]; 8,6 → swap → [3,6,8,1,9]; 8,1 → swap → [3,6,1,8,9]; 8,9 → no swap. Result after pass 1: [3, 6, 1, 8, 9]. Pass 2: 3,6 → no swap; 6,1 → swap → [3,1,6,8,9]; 6,8 → no swap; 8,9 → no swap. Result after pass 2: [3, 1, 6, 8, 9]. Pass 3: 3,1 → swap → [1,3,6,8,9]; 3,6 → no swap; 6,8 → no swap; 8,9 → no swap. Result after pass 3: [1, 3, 6, 8, 9]. The array [1, 3, 6, 8, 9] is now in full ascending order, so the array is fully sorted after the third pass.
Marking scheme
[1] for the correct array state after pass 1 ([3,6,1,8,9]); [1] for the correct array state after pass 2 ([3,1,6,8,9]); [1] for the correct array state after pass 3 ([1,3,6,8,9]); [1] for correct working/method shown (comparing and swapping adjacent elements); [1] for correctly identifying that the array is fully sorted after pass 3. Maximum [5]. Error carried forward (ECF) applies if an early pass contains a slip but the method is applied consistently thereafter.
Question 14 · Algorithm Tracing & Theory
5 marks
Describe how the linear search algorithm works when searching for a target value within an array, and state one disadvantage of linear search compared with binary search.
Show answer & marking schemeHide answer & marking scheme
Worked solution
Linear search works by starting at the first element (index 0) of the array and comparing it with the target value being searched for. If the current element matches the target, the search stops and the position (index) is returned as found. If it does not match, the algorithm moves on to check the next element in the array, repeating this process until either the target value is found, or the end of the array is reached without a match being found (in which case the search reports that the value is not present in the array). One disadvantage of linear search compared with binary search is that it is generally much less efficient for large arrays: in the worst case, linear search may need to check every single element in the array (an O(n) algorithm), whereas binary search (which requires the array to be sorted first) can locate a target far more quickly, in a much smaller number of comparisons, by repeatedly halving the section of the array being searched (an O(log n) algorithm).
Marking scheme
[1]-[2] for a correct description of the basic method of linear search (checking each element in turn, from the start, until a match is found or the array ends); [1] for correctly noting the search stops when the target is found; [1] for correctly noting what happens if the end of the array is reached without a match; [1] for a valid, correctly explained disadvantage compared with binary search (e.g. slower/more comparisons needed for large arrays, as it does not require the array to be sorted but is less efficient as a result). Maximum [5].
A subclass called Manager is created that inherits from Employee. In addition to the attributes inherited from Employee (employeeID, name, annualSalary), a Manager has an additional private attribute called teamSize (int), representing the number of employees they manage.
Write the class header for Manager, showing that it inherits from Employee, and declare the additional private attribute teamSize (int).
Show answer & marking schemeHide answer & marking scheme
Worked solution
Java: ``` public class Manager extends Employee { private int teamSize; } ``` C#: ``` public class Manager : Employee { private int teamSize; } ```
Marking scheme
[1]-[2] for a correct class header showing Manager inherits from Employee (`extends Employee` in Java, or `: Employee` in C#); [1]-[2] for a correctly declared private int teamSize attribute. Maximum [4].
Write a constructor for the Manager class that accepts employeeID, name, annualSalary and teamSize as parameters. The constructor should call the Employee base class constructor to set employeeID, name and annualSalary, and should then set teamSize.
Show answer & marking schemeHide answer & marking scheme
[1] for a correct constructor header taking all four parameters of the correct types; [1]-[2] for correctly calling the Employee base class constructor with employeeID, name and annualSalary (`super(...)` in Java, or `: base(...)` in C#); [1]-[2] for correctly assigning the teamSize parameter to the teamSize attribute. Maximum [5].
The Employee class has a method called toString() (as written in an earlier question). Explain what is meant by method overriding, and write an overridden toString() method for the Manager class that also includes the team size in its output.
Show answer & marking schemeHide answer & marking scheme
Worked solution
Method overriding is where a subclass (here, Manager) provides its own, more specific implementation of a method that is already defined in its superclass (here, Employee's toString() method), using the same method name, return type and parameters. When toString() is called on a Manager object, the overridden version defined in Manager is used instead of the inherited version from Employee, allowing the behaviour of the method to be adapted to suit the more specific subclass (a form of polymorphism).
Java: ``` @Override public String toString() { return super.toString() + ", Team Size: " + teamSize; } ``` C#: ``` public override string ToString() { return base.ToString() + ", Team Size: " + teamSize; } ```
Marking scheme
[1]-[2] for a correct explanation of method overriding (a subclass redefines/replaces an inherited method with its own implementation, using the same signature); [1] for correctly calling the base class version of toString() (super.toString() / base.ToString()) or otherwise correctly reproducing its output; [1] for correctly appending the teamSize attribute to the output. Maximum [4].
Describe four ways in which inheritance benefits software development, using the Employee/Manager example to illustrate your answer.
Show answer & marking schemeHide answer & marking scheme
Worked solution
1. Code reuse: Manager automatically inherits the employeeID, name and annualSalary attributes and associated methods (such as getAnnualSalary() and giveRaise()) from Employee, meaning this code does not need to be rewritten for Manager. 2. Easier maintenance: if a change needs to be made to shared behaviour (for example, how annualSalary is validated or formatted), it can be made once in the Employee base class, and both Employee and Manager objects will automatically benefit from the change, reducing duplication and the risk of inconsistent code. 3. Supports polymorphism: because Manager is a type of Employee, an array or list declared to hold Employee objects can also hold Manager objects, and calling an overridden method (such as toString()) on each object will automatically use the correct, most specific version for that object's actual class (e.g. Manager's version), allowing uniform code to work correctly with different subclasses. 4. Models a logical hierarchy and supports extensibility: inheritance allows the programmer to represent real-world 'is-a' relationships clearly in code (a Manager is a type of Employee), and makes the system easier to extend in future — for example, another subclass such as Director could later be added, also inheriting from Employee, without needing to change the existing Employee or Manager classes.
Marking scheme
[1] mark for each distinct, correctly explained benefit of inheritance (code reuse; easier maintenance/reduced duplication; supports polymorphism; models a logical class hierarchy/aids extensibility), up to a maximum of [5] (award the 5th mark for particularly clear or well-illustrated use of the Employee/Manager example throughout). Accept any other valid benefit of inheritance.
Question 19 · Array Iteration & Data Aggregation
9 marks
A company stores its employees in an array called employees, which can hold up to 50 Employee objects, and an integer variable called numEmployees, which records how many employees are currently stored in the array (note: numEmployees may be less than 50 if the array is not yet full). Write a method called getTotalPayroll() that iterates through the array and returns the sum of the annualSalary of all employees currently stored in the array.
Show answer & marking schemeHide answer & marking scheme
Worked solution
Java: ``` public double getTotalPayroll() { double total = 0; for (int i = 0; i < numEmployees; i++) { total = total + employees[i].getAnnualSalary(); } return total; } ``` C#: ``` public double GetTotalPayroll() { double total = 0; for (int i = 0; i < numEmployees; i++) { total = total + employees[i].GetAnnualSalary(); } return total; } ```
Marking scheme
[1] for correct method header (public, double return type, no parameters); [1] for declaring and initialising an accumulator variable (e.g. total) to 0; [1]-[2] for a correctly bounded loop that starts at index 0; [1]-[2] for correctly using numEmployees (not the fixed array length of 50) as the loop's upper bound, so only employees currently stored are included; [1] for correctly accessing each Employee object in the array in turn (employees[i]); [1] for correctly calling getAnnualSalary() on each object and adding the result to the accumulator; [1] for correctly returning the accumulator (total) at the end of the method. Maximum [9].
Ready to test yourself?
Turn these notes into exam-style practice. Get unlimited AI questions on this topic with instant marking and explanations.
thinka is an AI practice app for DSE students: unlimited questions, instant auto-marking, and detailed step-by-step solutions. 100,000+ students use it to confirm they actually know it, not just think they do.