0% found this document useful (0 votes)
4 views97 pages

Option D - Object-Oriented Programming - Youtube

The document provides an overview of Object-Oriented Programming (OOP) principles and their application in a software development scenario for a fast food chain, Los Pollos. It covers key concepts such as classes, objects, inheritance, aggregation, polymorphism, encapsulation, and the use of libraries, along with their advantages and disadvantages. Additionally, it emphasizes coding style, naming conventions, and the importance of modularity in programming.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views97 pages

Option D - Object-Oriented Programming - Youtube

The document provides an overview of Object-Oriented Programming (OOP) principles and their application in a software development scenario for a fast food chain, Los Pollos. It covers key concepts such as classes, objects, inheritance, aggregation, polymorphism, encapsulation, and the use of libraries, along with their advantages and disadvantages. Additionally, it emphasizes coding style, naming conventions, and the importance of modularity in programming.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 97

Option D

Object-Oriented Programming
IB Computer Science

Sold to
[email protected]
Intro
- I am not worthy
- A walk down memory lane
SL Agenda
- Basic Info about programming languages
- Practical overview of OOP using a software development-based scenario
- Theoretical Option D content
- Practice questions

☕ Caffeine + 🤖 ChatGPT + ♥ Love of CS = The CS Classroom


Sold to
[email protected]
Modern Programming Languages
- Examples include Java, C++, Python

Features

- Use of Unicode
- Portability (Can be used on different machines and OSes)
- Abstraction - hides the underlying details of how the programming language
works
- Leads to expressive and easy to read code
- Allows for the use of paradigms like object-oriented programming to mimim real-like processes

Sold to
[email protected]
Coding Style and Naming Conventions
- A hallmark of a productive programmer
- Examples include: indentation, comments, meaningful variable, method, and
class names

Benefits

- Easier for other programmers to understand your work


- Easier to debug consistent, commented code

Sold to
[email protected]
Open-Source Code

Sold to
[email protected]
Open-Source Code (Pros & Cons)

Sold to
[email protected]
Object-Oriented Programming (OOP)
- OOP is a method of organizing information in a computer program that seeks
to model the real world.
- It organizes data by category.
- Objects represent a unit of data that can be passed around a program
- A class is a template from which an object is created and determines would
information needs to be in the object.
- Each object has variables and functions associated with it called attributes and
behaviors, respectively.

Sold to
[email protected]
The Scenario
- A well-known fast food chain
known as Los Pollos would like to
implement a software-based system
to keep track of their employees.
- Each employee has certain data
associated and will need to be
updated at various times, so the
CTO decided to use object-oriented
programming.

Sold to
[email protected]
Instantiation
Employee Class
Employee Object
Attributes
Attributes
- String : name
name = “Walter”
- int : employee_id
employee_id = 356
- double : salary
salary = 1000000
# String[] : complaints
complaints[] = [“Scarily smart.”]
Behaviors
Behaviors
+ void addComplaint (String)
addComplaint()
+ double getSalary()
getSalary()
+ void setSalary (int)
setSalary()
+ double getName()
getName()
+ int getEmployeeId()
getEmployeeId()
+ double calculateNetSalary(double,
calculateNetSalary()
double) Sold to
[email protected]
Constructors
- A special method that is used
to initialize an object of a
class
- Called when an instance of
the class is created
- Its purpose is to set the
initial values of the object's
data members or perform any
other required initialization
tasks

Sold to
[email protected]
public class Person {
Constructors (2) private String name;
private int age;

- A class may have more public Person(String name) {


this.name = name;
than one constructor this.age = 0;
- Each of these represent }

a different way that the public Person(String name, int


same object can be age) {
this.name = name;
created. this.age = age;
}
}

Person person1 = new Person("John"); // Uses the


first constructor
Person person2 = new Person("Jane", 25); // Uses the
second constructor Sold to
[email protected]
‘this’ Keyword
public class Person {
private String name;
- refers to the current private int age;
object instance of a
class. public Person(String name, int age) {
this.name = name;
- used to differentiate this.age = age;
between instance }
variables and
parameters or local public void printDetails() {
variables that have the System.out.println("Name: " + this.name + ",
Age: " + this.age);
same name. }
}

Sold to
[email protected]
The Scenario: Inheritance
- Los Pollos is a large organization and there
are many types of employees.
- The CTO wants to model this aspect and is
therefore creating classes to represent
different types of employees in the
organization.
- These new types of objects will contain all
the attributes and behavior of Employee
objects, in addition to some additional,
customized attributes and behaviors.
Sold to
[email protected]
Salesman Object

Attributes
name = “Tuco”
employee_id = 356
salary = 1000000
Employee Class (Parent) complaints[] = [“Lunatic.”]
Salesman Class (Child) Sale[] sales = new Sale[10]
Attributes commission_rate = .25
- String : name Attributes total_sales_value = 0
- int : employee_id - double : commission_rate
- double : salary - double : total_sales_value Behaviors
# String[] : complaints - Sale[] : sales getSalary()
setSalary (int)
Behaviors Behaviors getName()
+ void addComplaint (String) + double getEmployeeId()
+ double getSalary() getTotalSalesValue() calculateNetSalary(double,
+ void setSalary (int) + double getNetSalary() double)
+ double getName() + void addSale(Sale) getTotalSalesValue()
+ int getEmployeeId() + void addComplaint (String, getNetSalary()
+ double calculateNetSalary(double, int) addSale(Sale)
double) addComplaint (String, int)

Sold to
[email protected]
Inheritance
- Allows new classes to be created
based on existing classes, thus
inheriting their properties and
behavior
- Existing class is called the
superclass or parent class, and the
new class is called the subclass or
child class
- extends keyword is used to create
a subclass of a superclass
Sold to
[email protected]
Inheritance: Pros & Cons

Pros Cons
Code Reusability - allows reuse of code Tight Coupling - base class and derived
from existing classes, which reduces class are tightly coupled; makes it difficult
development time to modify base class without affecting the
derived class

Code Maintenance - changes to base class Fragile Base Class - modifications to base
are reflected in all derived classes, making class and potentially break functionality in
code easier to modify and maintain derived classes

Modularity - promotes the breaking down of


programs into smaller, specialized
components
Sold to
[email protected]
The Scenario: Aggregation
- Los Pollos wants to keep track of all
the sales that each salesman has
made
- Each Sale should have certain
information attached to it
- Each salesman will have a
maximum of 100 sales (none
survive beyond that many sales).

Sold to
[email protected]
Aggregation

Salesman Class Sale Class

Attributes Attributes
- double : commission_rate - int : transaction_id
- double : total_sales_value - double : sale_value
- Sale[] : sales - String : buyer_name

Behaviors Behaviors
+ double getTotalSalesValue() + int getTransactionId()
+ double getNetSalary() + double getSaleValue()
+ void addSale(Sale) + void getBuyerName(Sale)
+ void addComplaint (String, + void addComplaint (String,
int) int)

Sold to
[email protected]
Aggregation
- A relationship between classes
where one class contains an
instance of another class as a
member variable
- When that class is instantiated, we
end up with an object that contains
other objects
- If the “container object” is
destroyed, the “contained object”
will still exist
Sold to
[email protected]
Inheritance vs. Aggregation

Inheritance Aggregation
Behavior of a class is transferred to another A class is contained inside another class
class and used

Models situation when one thing is a Models a situation when one thing has
specific type of another thing many of another thing

Allows for code reuse Each class must be written in full

Sold to
[email protected]
The Scenario: Polymorphism & Method Overriding
- The CTO has a problem. The CEO wants
complaints to be in a different format
for salesman and for employees.
- He wants a sales transaction ID to be
included in every complaint for
salesman.
- To do this, even though the Employee
class already has an
addComplaint() method, he is
going to create a new
addComplaint() method with
different parameters and functionality.
Sold to
[email protected]
Polymorphism
- A situation in which something can occur in different form, and we can
therefore perform the same action in many different ways
- In Java, refers to method overloading and method overriding.
- method overloading - situation in which there are different methods in the same class with the
same name, but which have different parameters
- method overriding - situation in which there are different methods in the superclass and
subclass with the same name, but which have different parameters
- These can all be used, with parameters being the distinguishing factor.

Sold to
[email protected]
Libraries
- a library is a collection of
pre-written code that can be used
by other programs
- contain reusable classes and
methods that can save developers
time by providing common
functionality that they can
incorporate into their own programs
- Java includes a number of libraries,
but third-party libraries can be
downloaded and used as well
Sold to
[email protected]
Advantages of Library Usage
1) Saves development time - classes and objects do not have to be rewritten
every time certain functionality is required
2) Code is more likely to be error-free - the code has been used and tested many
times, so errors are more likely to have been eliminated
3) Promotes efficiency & organization - Code is shorter and easier to read;
development is faster
4) Promotes abstraction - code can be reused without knowledge of internal
workings

Sold to
[email protected]
The Scenario: Encapsulation
- Los Pollos is expanding rapidly and the new software system is being used in
nearly every branch.
- This means that there are many programmers working on the system.
- Therefore, it’s smart to employ data hiding.
- We don’t wants programmer to allow users to modify or view data or
functionality that would compromise the integrity of the system
- Some attributes should not be edited without the the change being validated
first by a function
- Other attributes should not be edited outside of a class as they could be
confused with attributes from other classes
Sold to
[email protected]
Encapsulation
- the technique of binding data and
functions together in a single unit
(class) and controlling access to
that unit from outside
- refers to the practice of hiding the
internal details of an object from
the outside world

https://www.crio.do/blog/encapsulation-in-java/

Sold to
[email protected]
Access Modifiers
- Used to control access to
attributes, methods,
constructors, and sometimes,
classes.

Sold to
[email protected]
The Scenario: Static Methods
- Some users have a tool to calculate
after tax salary.
- They want this to be a standalone
tool that does not make use of data
from an Employee object (or any
child object).
- It’s a bit irritating, but the user
wants what the user wants.

Sold to
[email protected]
Static Methods
- Means that the method is the
same for all instances of a class
- Defined the class level
- Contained in the classes rather
an instance of the class (object)
- Less memory is taken up
because memory is allocated for
only one instance, rather than
every objects
http://www.beginwithjava.com/java/classes-ii/stat
ic-class-members.html
Sold to
[email protected]
Modularity
- Modularity refers to the practice of
breaking down a program into smaller,
self-contained and independent parts or
modules that can be easily maintained,
reused, and tested
- Each part has a specific responsibility or
functionality
- Modularity is achieved through the use of
objects and classes, with each class
representing a different role
Sold to
[email protected]
Advantages of Modularity
1) Faster development - different programming teams can simultaneously work
on different modules
2) Easier to debug - smaller modules will have simpler, less complex mistakes
than one large program
3) Easier to update - easier and less complex to update one module or
self-contained component of a program than the entire program
4) Reusability - Modules can be stored in libraries and reused in different
programs

Sold to
[email protected]
UML (Unified Modeling Language) Diagrams
- graphical representation of
software systems, used by
software developers to
visualize, design, and
document their systems
- represents the structure of
a system by showing its
classes, attributes,
methods, and the
relationships between
them
Sold to
[email protected]
Sold to
[email protected]
The Scenario: UML Diagrams

Employee Class (Parent)


Salesman Class (Child) Sale Class
Attributes
- String : name Attributes
Attributes
- int : employee_id - double : commission_rate
- int : transaction_id
- double : salary - double : total_sales_value
- double : sale_value
# String[] : complaints - Sale[] : sales
- String : buyer_name
Behaviors Behaviors
Behaviors
+ void addComplaint (String) + double
+ int getTransactionId()
+ double getSalary() getTotalSalesValue()
+ double geSaleValue()
+ void setSalary (int) + double getNetSalary()
+ void getBuyerName(Sale)
+ double getName() + void addSale(Sale)
+ void addComplaint (String,
+ int getEmployeeId() + void addComplaint (String,
int)
+ double calculateNetSalary(double, int)
double)

Sold to
[email protected]
Sold to
[email protected]
Sold to
[email protected]
The Scenario: Relationship Diagrams

Sold to
[email protected]
OOP: Pros & Cons

Pros Cons
Modularity - makes code easier to maintain Not suitable for smaller projects - OOP
and modify increased complexity with little benefit

Encapsulation - protects data from being OOP makes programs larger (in terms of
modified accidentally memory usage) and therefore slower

Inheritance - allows new classes to be Takes more time and effort because of need
based on existing classes, reducing to split up program and organized in a
development time and improving code specific way
quality

Sold to
[email protected]
Terminology (1)
- Object - unit of data that contents variables and methods
- Class - Template (blueprint) for object including variables and methods
- Identifier - name that is used to identify a variable, method, class, or other
programming entity
- Object Reference - A variable that is used to access an object; the variable
contains the memory address of the object
- Object Instantiation - create an instance of an object from a class
- Attribute - variable or method
- Method - another word for a function - independent block of code that can be
called on to return an output given a specific range of inputs
- Method Signature - Included the name of the function and all its parameters -
generally the first line of the function
Sold to
[email protected]
Terminology (2)
- Mutator Method - a method that is used to modify an attribute (the state) of
an object
- Accessor Method - a method that is used to access and attribute (the state) of
an object
- Method Overriding - feature that allows a subclass to provide a different
implementation of a method that is already defined in its superclass
- Method Overloading - a feature that allows for different implementations of a
method in the same class

Sold to
[email protected]
Basic Programming Tasks (SL)
- Create a basic class to model a scenario
- Implement a constructor
- Create accessor and mutator methods
- Implement an aggregation relationship
- Iterate through an array of objects to find a particular element, maximum, etc.
- Create accessor and mutator methods for an array of objects in another object
- Convert the content of an object to a string
- Implement a static function
- Bonus: Use a library

Sold to
[email protected]
Sold to
[email protected]
Sold to
[email protected]
Wrap Up
- Tried to balance between brevity and content
- HL is Topic 5 on Steroids
- This will be my last video before the exam

Sold to
[email protected]
HL - Topic 5 meets Java

Sold to
[email protected]
HL Intro
- Requires a strong grounding in Topic 5
- Need to be very comfortable with Java
- Primarily ADT (Abstract Data Structure-oriented) Java and theoretical questions
- LinkedList
- ArrayList
- 2D Arrays
- Stacks
- Recursion
- Binary Search Trees
- Basic Programming Tasks

☕ Caffeine + 🤖 ChatGPT + ♥ Love of CS = The CS Classroom


Sold to
[email protected]
IB-Specific Java Advice
- Syntax does not need to be perfect, but should be close
- See JETS booklet for syntax
- See Option D Study Guide for Code Snippets
- Familiarize yourself with libraries for each data structure, although methods
are usually given on exam

Sold to
[email protected]
Linked Lists

Sold to
[email protected]
LinkedList
- LinkedLists are included as part
of the Java programming
language. This means that they
are included as part of Java’s
standard library.
- While all Topic 5 Abstract Data
Structures are referenced,
LinkedLists are arguably the
most frequently appearing data
structure
Sold to
[email protected]
LinkedList Characteristics
- Doubly-linked list
- Each piece of data is stored in a node, with pointers
- Located non-contiguously in memory - addresses of nodes are spread out
across RAM and nodes only linked by pointers
- Ideal for frequent insertion or deletion of elements, or when the size of the list
is unknown

Sold to
[email protected]
Sold to
[email protected]
Linked List - Basic Tasks
- Create and initialize a Linked List
- Read an array of objects into a linked list
- Find an remove a specific object from a linked list
- Merge two linked lists into one linked list

Sold to
[email protected]
Iterators
- an object that allows you to traverse the elements of a collection (such as an
ArrayList or LinkedList) one at a time
- provide a way to access elements of a collection without exposing its
underlying workings
- allow for safe modification of the collection during iteration

Sold to
[email protected]
Singly- or Doubly-Linked List?

Sold to
[email protected]
Sold to
[email protected]
Linked Lists Pros & Cons (vs. Arrays)
Pros Cons
Dynamic Size - Unlike arrays, linked lists can Access Time - Finding a specific value in a
grow or shrink in size linked list takes longer than in an array

Easy Insertion and Deletion - easy to insert Extra memory overhead - Pointers are
and delete elements from linked lists; utilized, which take up more memory than a
simply involves changing points of adjacent single array element
nodes

Efficient memory usage - memory is only Complexity - Linked Lists can be more
allocated for elements in the linked list difficult to implement and debug than
unlike arrays simple data structures like arrays

Versatility - Can be used to implement other


data structures like stacks and queues
Sold to
[email protected]
Linked Lists - Ideal Use Cases
1. Implementing a Queue: A Linked List can be used to implement a queue,
where the front of the queue is represented by the head of the Linked List,
and the rear of the queue is represented by the tail of the Linked List.
2. Implementing a Stack: A Linked List can also be used to implement a stack,
where the top of the stack is represented by the head of the Linked List, and
the bottom of the stack is represented by the tail of the Linked List.
3. Large data sets: Linked Lists are useful for managing large data sets
because they can dynamically allocate memory as needed. Unlike arrays,
which have a fixed size, Linked Lists can grow or shrink in size as needed.
Sold to
[email protected]
Array Lists

Sold to
[email protected]
ArrayLists
- Create and initialize an ArrayList
- Read an array of objects into a ArrayList
- Read an ArrayList into an array
- Find an remove a specific object from an ArrayList

Sold to
[email protected]
import java.util.ArrayList;
import java.util.Iterator;

public class Main {


public static void main(String[] args) {
// Create a new ArrayList of strings
ArrayList<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");

// Get an iterator for the ArrayList


Iterator<String> iterator = names.iterator();

// Use the iterator to traverse the elements of the ArrayList


while (iterator.hasNext()) {
String name = iterator.next();
System.out.println(name);
}
}
}
Sold to
[email protected]
LinkedLists vs. ArrayLists

Sold to
[email protected]
2D Arrays

Sold to
[email protected]
2D Arrays - Basic Tasks
- Create and initialize a 2D array in a class
- Access a specific position in a 2D array
- Insert values into a 2D array
- Calculate average of values in 2D array

Sold to
[email protected]
2D Arrays - Ideal Use Cases
- Overall, 2D arrays are ideal for situations where you need to store data in a grid or table-like
format, and where you need to access the data using row and column indices.

1. Image processing: Images can be represented as 2D arrays, where each pixel in the image is
represented as an element in the array. This allows for various image processing operations, such
as resizing, rotation, and filtering.

2. Games: 2D arrays can be used to represent game boards or game maps, where each cell in the
array represents a location in the game world. This allows for efficient game state management
and collision detection.

3. Data analysis: 2D arrays can be used to store and analyze large data sets, where each row in
the array represents a data point and each column represents a feature or attribute of the data.
This allows for efficient data processing and statistical analysis.
Sold to
[email protected]
Stacks

Sold to
[email protected]
Stacks - Basic Tasks
- Create and initialize a stack in a driver class
- Add and remove values from a stack
- Find the maximum value in a stack
- Find and remove a value from a stack

Sold to
[email protected]
Sold to
[email protected]
Sold to
[email protected]
Sold to
[email protected]
Sold to
[email protected]
Stacks - Ideal Use Cases
1. Undo/Redo functionality: Stacks are often used to implement undo and redo functionality in
software applications. Each time a user performs an action, such as typing a character or
moving an object, the action is added to the stack. If the user wants to undo the action, the
most recently added item is removed from the stack and the action is reversed.
2. Expression evaluation: Stacks can be used to evaluate expressions, such as mathematical
formulas or programming code. Each time a new operator or operand is encountered, it is
pushed onto the stack. When an operator is encountered, the operands are popped off the
stack, the operation is performed, and the result is pushed back onto the stack.
3. Function call stack: In computer programming, a function call stack is a stack that is used to
keep track of the order in which functions are called. Each time a function is called, its
parameters and local variables are pushed onto the stack. When the function returns, the values
are popped off the stack and control returns to the calling function.

Sold to
[email protected]
Recursion

Sold to
[email protected]
Recursion
- Solve trace tables
- Solve problems using recursive functions

Sold to
[email protected]
Using Recursion: Top Tips
1. Understand that recursion is used when we are repeating the same behavior
with consistently changing parameters.
2. Model the problem with an example input and output.
3. Turn the problem into a set of individual steps.
4. Keep in mind that at least one of the function parameters is going to have to
change on each recursive function call.
5. Understand that the base case is where the recursive calls will stop - it
effectively stops the function from calling itself

Sold to
[email protected]
factorial(n)
- We want factorial(4) to give us 4*3*2*1 = 24.
- So, 4 * factorial(3) should give us 24.
- And, 3 * factorial(2) should give us 12.
- And 2 * factorial(1) should give us 2.
- n * factorial(n - 1)

Sold to
[email protected]
Factorial (Trace)

Sold to
[email protected]
power(int base, int exponent)
- We want power(3, 4) to give us 3*3*3*3 = 81
- So, 3 * power(3, 3) should give us power(3, 4)
- And 3 * power(3, 2) should give us power(3, 3)
- And 3 * power(3, 1) should give us power (3, 2)
- base * power(base, exponent - 1)

Sold to
[email protected]
Power (Trace)

Sold to
[email protected]
isPalindrome(String word)
- A palindrome is a word like kayak that looks the same forward and backward.
- We need to use recursion, which means we need some repetitive behavior
until we head an endpoint.
- Remember in recursion, we are repeating the same behavior, with different
parameters.
- To make sure a word is a palindrome, the first letter has to be the same as the
last, the second letter has to be equal to second to last, the third letter, etc,=.
- This process stops when we get to the middle and there is no other letter to
compare it to.

Sold to
[email protected]
isPalindrome (Trace)

Sold to
[email protected]
Sold to
[email protected]
Sold to
[email protected]
Sold to
[email protected]
Sold to
[email protected]
Recursion - Ideal Use Cases
1. Traversing tree-like data structures: Recursion is ideal for traversing tree-like
data structures, such as Binary Search Trees or file systems. The recursive function
can be called on each node of the tree, allowing you to visit all of the nodes in a
structured and efficient way.

2. Solving mathematical problems: Many mathematical problems can be solved


using recursion, such as the Fibonacci sequence or the Tower of Hanoi puzzle.
Recursion can be used to break the problem down into smaller sub-problems,
which can be solved recursively until the final solution is reached.

Sold to
[email protected]
Binary Search Trees (Binary Trees)

Sold to
[email protected]
Binary Search Trees - Basic Tasks
- Create and initialize a binary search tree
- Use recursion to traverse a binary tree and print out all of the elements
- Find the minimum value in a binary search tree

Sold to
[email protected]
Our Binary Tree

Sold to
[email protected]
Why Do We Use Recursion?
1. We are navigating a tree of indeterminate size.
2. We have an individual set of instructions that we can execute each time
(check right and left leaves)
3. We executing operations based on the previous result (previously accessed
root node)

Sold to
[email protected]
Output the BST
1. Start with the root node
2. Go right and check both the right and left node. For reach of those nodes,
check, both the right and left node.
3. Go left and check both the right and left node. For each of those nodes, check
both the right and left node.
4. Repeat 2), until you hit a node with no right or left node.
5. Repeat 3), until you hit a node with no right or left node.
- Each time you access a node, print out the data attached to that node.

Sold to
[email protected]
Output the BST (Trace)

Sold to
[email protected]
Find the Minimum Value in a BST
- Focus on the left node, because those are the smallest.
1) Call the left leaf on the root node.
2) Repeat 1) until we reach a node with no left leaf.

Sold to
[email protected]
Find the Minimum Value in a BST (Trace)

Sold to
[email protected]
Binary Search Trees Pros & Cons

Pros Cons
Fast searching - can take a fraction of the Heavy memory usage - nodes are allocated
time as in an array dynamically and require pointers, which can
take up more memory

Dynamic structure - easy insertion and Complexity of deletion - entire tree has to
deletion of nodes be “rebalanced” when a node is removed

Easy implementation - easy to implement


and debug

Sold to
[email protected]
Why Searching a Binary Tree is Faster
[4, 2, 8, 3, 6, 5, 7, 9]

Sold to
[email protected]
Binary Search Trees
1. Sorting: BSTs can be used for sorting elements, where each element is stored as
a node in the tree. The elements can be sorted by performing an in-order traversal
of the tree, which will visit the nodes in ascending order.

2. Unique elements: BSTs are useful for maintaining a collection of unique


elements, as duplicate elements are automatically discarded when they are
inserted into the tree.

3. Database indexing: BSTs can be used for indexing database tables, where each
row in the table is stored as a node in the tree. This allows for efficient searching
and sorting of the data.
Sold to
[email protected]
Wrap Up
- No practice exam
- Option D Study Guide + Slides
- Intro to Java Course

Sold to
[email protected]

You might also like