Lecture from: 01.10.2024 | Video: Videos ETHZ
Handling Program Input (Scanner)
Introduction
Every program needs to interact with its environment to function effectively. This interaction often involves receiving data – the input – which the program then processes and potentially outputs as a result. Input can come from various sources, each posing unique challenges and requiring specific handling techniques.
Diverse Input Sources: The Challenge of Variability
Programs can receive input from a wide range of sources, including:
- Console: Text-based input directly from the user via the command line interface.
- Files: Structured or unstructured data stored persistently on disk.
- Websites: Information retrieved dynamically from web servers through HTTP requests.
- Databases: Organized collections of data managed by specialized systems (e.g., MySQL, PostgreSQL).
Each input source presents its own set of complexities:
- Unpredictability: The behavior of external systems and users is inherently unpredictable. Input formats may vary, timing can be inconsistent, and unexpected errors can occur.
- Controllability: Programmers have limited control over the environment generating the input. We cannot dictate the exact content or structure of data received from external sources.
- Data Conversion: Raw input often needs to be converted into meaningful data types (e.g., integers, strings, booleans) that the program can understand and process effectively.
Java’s Scanner
In Java, the Scanner
class provides a convenient mechanism for reading user input from the console.
Example:
Key Scanner
Methods:
next()
: Reads the next token (usually a word) from the input stream.nextLine()
: Reads an entire line of text from the input stream.nextInt()
: Reads the next integer from the input stream.nextDouble()
: Reads the next double-precision floating-point number from the input stream.hasNext()
: Checks if there is more input available in the stream.
Note: Always close the Scanner
object when finished to release system resources using sc.close()
.
Methods
next()
:
-
This method reads the next token from the input stream. A token is typically a sequence of characters separated by whitespace (spaces, tabs, or newlines). Think of it like reading words individually.
-
Example: Imagine the user types: “Hello world this is a test”
-
Important:
sc.next()
will stop reading at the first whitespace it encounters. It won’t read the entire line.
nextLine()
:
-
This method reads the entire line of text from the input stream, including any spaces and up to the newline character (which marks the end of a line).
-
Example: Using the same input phrase “Hello world this is a test”
Key Differences:
Method | Behavior | Example Input | Output |
---|---|---|---|
next() | Reads the next token (word) | “Hello world this is a test" | "Hello” and “world” |
nextLine() | Reads the entire line of text | ”Hello world this is a test" | "Hello world this is a test” |
Potential Pitfalls: Data Type Mismatches
When using methods like nextInt()
, nextDouble()
, etc., the Scanner
class expects a specific data type from the user’s input.
If the user enters something that doesn’t match the expected type, you’ll encounter a runtime error. This means your program will halt unexpectedly during execution.
Example:
Explanation:
- The
sc.nextInt()
method tries to read an integer from the user’s input. - The user enters “hello”, which is not a valid integer representation.
- This causes a runtime error, specifically a
InputMismatchException
. - Our
try...catch
block gracefully handles this exception, preventing the program from crashing and providing a more informative message to the user.
Key Takeaways:
- Always validate user input to ensure it matches your program’s expectations.
- Use
try...catch
blocks to handle potential runtime errors caused by data type mismatches.
Random
While we often think of randomness as an inherent property of the universe, computers are fundamentally deterministic machines. They follow precise instructions and produce predictable outputs given a specific input.
Therefore, true “randomness” is elusive in the realm of computing. What we have instead are pseudo-random number generators (PRNGs). These sophisticated algorithms create sequences of numbers that appear random but are actually generated based on a starting value called a seed.
Java’s Random
Class: A Glimpse into Pseudo-Randomness
Java provides the java.util.Random
class to generate pseudo-random numbers. Let’s explore its workings:
Initialization:
- The
Random
constructor takes an optionallong
seed value. If no seed is provided, the system time is used as a default seed, ensuring different starting points for each program run.
Methods:
-
nextInt(int bound)
: Generates a pseudo-random integer between 0 (inclusive) andbound
(exclusive). For example,rand.nextInt(10)
will return a random number from 0 to 9. -
nextDouble()
: Generates a pseudo-random double-precision floating-point number between 0.0 (inclusive) and 1.0 (exclusive). Useful for generating random decimals. -
nextBoolean()
: Returns a pseudo-randomboolean
value (eithertrue
orfalse
).
Putting Random
to Work
Here are some common scenarios where the Java Random
class shines:
1. Random Integer within a Range [1, n]
- Use Case: Simulating dice rolls, selecting random items from a list of size n.
- Code Example:
2. Random Integer within a Range [min, max]
- Use Case: Generating random scores, simulating game events with specific boundaries.
- Code Example:
3. Random Double within a Range [min, max]
- Use Case: Simulating real-world phenomena with continuous values (e.g., temperature, distance).
- Code Example:
4. Random Selection from a Collection of Objects
- Use Case: Simulating card draws, choosing random players in a game.
- Code Example (Java List):
Control Flow
Control flow, at its core, dictates the execution sequence of a program – a fundamental concept underpinning every conceivable computational task.
Conditions
Conditional flow introduces a layer of strategic decision-making into programs by enabling them to execute different code paths based on evaluated conditions.
The if
Statement
The if
statement is the cornerstone of conditional logic in programming. It empowers your code to make decisions and execute different blocks of code based on whether a specific condition is true or false.
Basic Structure:
condition
: A boolean expression that evaluates to eithertrue
orfalse
.
Example: Determining Adult Status
In this example, the condition
is age >= 18
. Since age
is 20, which is greater than or equal to 18, the condition evaluates to true
, and the code inside the curly braces executes, printing “You are an adult.”
The else
Statement:
When you need to handle two distinct outcomes—one if the if
condition is true and another if it’s false—the else
statement comes into play.
Basic Structure:
Example: Age-Based Greetings
In this example:
- If
age
is 18 or greater, the first block executes (“You are an adult.”). - If
age
is less than 18, the second block executes (“You are a minor.”).
Comparison Operators
Comparison operators allow you to compare values and determine if one value is greater than, less than, equal to, or otherwise related to another. They return a boolean result (true
or false
).
Operator | Meaning | Example | Result |
---|---|---|---|
== | Equal to | 10 == 10 | True |
!= | Not equal to | 5 != 7 | True |
> | Greater than | 20 > 10 | True |
< | Less than | 5 < 10 | True |
>= | Greater than or equal to | 10 >= 10 | True |
<= | Less than or equal to | 8 <= 10 | True |
Important Note: Be Careful with =
vs ==
!
=
(Assignment operator): Assigns a value to a variable. The assignment itself has the type of the variable and the value that is being assigned. (x = 5
)==
(Equality operator): Compares values for equality. (x == 5
)
Here the assignment has the type boolean and is true, therefore the first part will be executed.
Logical (Boolean) Operators
Logical operators allow you to combine multiple conditions to create more complex expressions.
Operator | Meaning | Example | Result |
---|---|---|---|
&& | AND (Both conditions must be true) | age >= 18 && hasLicense == true | True if both are true |
|| | OR (At least one condition must be true) | hasStudentID || isGuest | True if either is true |
! | NOT (Reverses the truth value of a condition) | !(age < 18) | True if age is 18 or older |
Note: &&
has stronger precedence than ||
. These operators are also left associative.
Nested if
Statements:
For intricate decision-making, you can nest if
statements within each other, creating a hierarchy of conditions.
The else if
statement:
The else if
statement provides a structured way to check multiple conditions sequentially, executing the first true block.
- Think of it like a series of “if” checkpoints – once one condition is met, the rest are skipped.
Ternary Operator: A Compact Conditional
For simple decisions, Java offers the ternary operator (condition ? expression1 : expression2
), which condenses an if-else
into a single line.
- Example:
int result = (age >= 18) ? "Adult" : "Minor";
Single Statement No Brackets:
In Java, when the code block following an if
, else if
, or else
statement consists of a single statement, you can omit the curly braces ({}
).
Example:
Short-Circuit Evaluation
Java uses short-circuit evaluation to optimize logical expressions. This means that once the outcome of an expression is known (true or false), the remaining parts are not evaluated.
Example:
If hasLicense
is false
, the entire condition becomes false
, preventing the need to check age
. This saves time and resources, especially in complex scenarios.
This can also be used to prevent divide by 0 errors. You simply check if the denominator is not 0. If it is, the latter part will not be evaluated and you won’t get a runtime exception.
This also means that p && q
is not quite the same as q && p
. Example: b != 0 && a/b > 1
. The same holds for p || q
.
Loops: Repeating Actions with Precision
Loops allow you to execute a block of code repeatedly until a certain condition is met. They represent another fundamental control flow mechanism, facilitating the repeated execution of a block of code until a specific condition is met. Java offers several loop constructs.
The for
Loop
The for
loop is a powerful tool when you know exactly how many times you need to repeat a block of code. It operates on a clear structure: initialization, condition, and increment.
In this example, int i = 0
initializes a counter variable i
to 0. The i < 5
condition checks if i
is less than 5; as long as it is true, the loop continues. Finally, i++
increments i
by 1 after each iteration.
Infinite Loops
While uncommon, infinite loops occur when the loop condition never becomes false, leading to endless execution. These can quickly consume system resources and require manual intervention to stop.
- Example:
while (true) { ... }
– This loop runs continuously until explicitly terminated.
Breaking Out of Loops
The break;
statement provides an immediate exit from a loop, regardless of the loop condition.
Continuing to the Next Iteration
The continue;
statement skips the remaining code within the current iteration and jumps directly to the next one.
In this case, the code prints only odd numbers from 0 to 9.
The while
Loop
The while
loop provides a way to execute code repeatedly as long as a given condition holds true. It’s particularly well-suited when the exact number of iterations is unknown beforehand, and the loop should continue until a specific state or outcome is achieved.
This example demonstrates a while
loop that prints numbers from 0 to 4. The loop continues as long as the value of count
is less than 5. Inside the loop, we print the current value of count
and then increment it by 1 using count++
. Once count
reaches 5, the condition becomes false, and the loop terminates.
Continue here: 05 Java (Functions, Value vs Reference Semantics, Variable Scope)