Week 3, Tuesday, first half: string concatenation, the 2.3.3 recap, and 2.4 (Scanner, Random).
These notes follow the slide numbering and merge all four sessions. Where the lecturer misspoke or was unsure, the correct version is given and the discrepancy is flagged.
1. Executive Overview & Core Objectives
This chapter is a deliberate “speedrun” that gets you productive in the exercises quickly:
2.1: how source code becomes output, i.e. compiler, bytecode, JVM, compile-time vs. runtime errors, and the mandatory skeleton of a Java program.
2.2:methods as the first tool for structuring programs and avoiding redundancy.
2.3:types, variables and arithmetic expressions with Java’s exact rules for precedence, associativity, integer division, modulo, overflow, floating point, implicit conversion and casts.
2.4: interactive input with Scanner and pseudo-random numbers with Random.
Learning objectives. After this week you should be able to:
explain the pipeline source → compiler → bytecode → JVM → output, and classify an error as a compile-time error (Compilerfehler) or a runtime error (Laufzeitfehler);
write a syntactically valid Java program from scratch and respect naming rules and conventions;
predict the output of print/println with escape sequences;
decompose a problem into sub-tasks, implement them as static methods, and trace control flow through nested calls (also in the debugger);
declare, initialise, assign and evaluate variables of the primitive types;
evaluate arithmetic expressions by hand with correct types: implicit bracketing, expression trees, integer division, %, overflow, int/double mixing, casts, string concatenation;
read input with Scanner and generate random numbers in arbitrary ranges with Random.
2. Core Concepts & Formal Definitions
2.1 Introduction: from source code to output (Vom Quelltext zur Ausgabe)
Two ways of working:
A) Whole program (the focus of this course)
B) Single statements
Cycle
create → run → modify, repeated
Read → Evaluate → Print, in a Loop (REPL)
Use
real programs; it scales
quick experiments
Tool
IDE (IntelliJ)
JShell
Definition (Compiler, compile time, runtime)
A compiler (Compiler) is a program that translates source code in a high-level language into a low-level language. Java is first translated into Java bytecode, which is close to machine language (like assembler). Translating is also called compiling (kompilieren, übersetzen).
The compiler checks certain correctness conditions while translating, but very few of them (“honestly, almost none”).
Compile time (Compilezeit): translation happens once. A compile-time error means the program makes no sense as Java, so translation aborts and the program cannot run at all.
Runtime (Laufzeit): the processor and runtime system execute the instructions, possibly many times with different data. A runtime error aborts this particular execution.
Classic example: a program reads x and y and computes x / y. Whether y is 0 depends on the input at runtime, so the compiler cannot know. A runtime check (generated for you) aborts with an error if y == 0.
Why compile to bytecode instead of machine code? (student question)
There are many CPU architectures and many operating systems. C/C++ must compile for every combination of hardware and OS, which is a lot of effort.
Java compiles once to bytecode. Every platform provides a Java Virtual Machine (JVM), a virtual computer that executes bytecode. The responsibility shifts to the platform: “you want an obscure CPU? Provide a JVM.”
Further advantage: runtime optimisation and just-in-time compilation on the bytecode.
Both approaches have trade-offs; there is no right or wrong. Details follow in Systems courses and are not relevant for EProg.
Only Java in this course (student question): learning several languages at once is like starting Spanish, Italian and Portuguese simultaneously.
Definition (IDE)
An integrated development environment (Entwicklungsumgebung, IDE), e.g. IntelliJ, integrates editor, compiler control, execution and debugger. IDEs are the industry standard but not strictly necessary:
Plain editors: Vim, Emacs.
Code editors such as VS Code, in between: very popular, and the lecturer’s own default.
Discrepancy: the lecturer once said “in our case it is Eclipse”. The course uses IntelliJ; Eclipse was used in earlier years.
Why IntelliJ and not VS Code? (student question) A&D’s CodeExpert is one extreme: it hides almost everything. A full IDE is the other extreme, so that you see one at least once before later courses expect it.
IntelliJ shows a lot. Focus on what has been explained and accept the rest for now. You can hardly break anything by experimenting: “experimenting physicists must be far more careful than experimenting computer scientists.”
2.1.1 The first Java program
public class HelloWorld { // class = program named HelloWorld public static void main(String[] args) { // main = entry point of execution System.out.println("Hello World"); // statements = the functionality System.out.println("Hello World again"); }}
Rules for every executable Java program (compiler-enforced)
File name = program name: the file HelloWorld.java contains public class HelloWorld.
One class per program/file. Exceptions exist and come later.
The class contains a method named exactly main. This is where execution starts automatically when you click Run.
main contains a sequence of statements, and each statement ends with ;.
Brackets ( ), { }, [ ] occur in matching pairs.
Goals for now: (1) read, (2) understand, (3) create/modify programs. Reading is easier than writing on a blank page.
The pink-highlighted skeleton is boilerplate: required structure that is neither interesting nor essential for beginners. public, class, static, void, String[] args are explained later.
Remark (Simplified main)
Newer Java versions allow a reduced form without the class skeleton. The slide says Java 25; the lecturer said “since Java 21, I think”. It was a preview feature from Java 21 and became final in Java 25. The course teaches the full form, because the short form cannot be combined with larger programs or libraries.
Demo: the Run button compiles and runs. The compilation step is invisible except for some artefacts in the console, e.g. the java command line that is printed.
2.1.2 Syntax rules as EBNF
The Java Language Specification defines Java largely with EBNF. Simplified versions:
<lowercaseletter> ⟵ a | b | c | ... | z
<uppercaseletter> ⟵ A | B | C | ... | Z
<letter> ⟵ <lowercaseletter> | <uppercaseletter>
<digit> ⟵ 0 | 1 | 2 | ... | 9
<identifier> ⟵ <letter> { <letter> | <digit> }
<javaprogram> ⟵ public class <identifier> ⟦{⟧ <methoddefinition> ⟦}⟧
<methoddefinition> ⟵ public static void main ⟦(⟧ String ⟦[⟧⟦]⟧ <identifier> ⟦)⟧ ⟦{⟧ <statementsequence> ⟦}⟧
Definition (Identifier, Bezeichner)
Names in programs (program, class, method, variable names) must satisfy:
at least one character;
start with a (Unicode) letter, never a digit;
then letters or digits (0–9).
This is a simplification; see JLS §3.8 (e.g. _ and $ are also allowed).
Student question: are Java’s brackets part of EBNF? No. In the Java EBNF they are literal characters of the defined language, which is why they would be boxed.
Student question: can EBNF forbid keywords as identifiers? No. The specification combines the EBNF with additional textual rules (“this is not allowed…”) that are checked outside the formalism.
2.1.3 Statements and method calls
A method contains a sequence of statements (Aufreihung von Anweisungen). Possible statements:
input/output
branches and loops (control flow, next week)
method/function calls
…
“Method” vs. “function”: technically almost the same. Java says method, C++ says function. Use the term of the community you are in; it isn’t worth a flame war.
Call format
Meaning
methodName();
call a globally/locally known method
object.methodName();
call a method on an object (dot notation, typical for Java and OO languages)
System.out.println("Hello".toUpperCase()); // prints HELLO
System.out is an object; println is its method.
"Hello" is a string object; toUpperCase() returns a new string, which is passed on to println.
Nesting works like f(g(x)) in maths.
Demo: "Hello".toLowerCase().toUpperCase() is pointless but legal.
Student question about the Locale overload of toUpperCase(Locale): a locale describes computer-specific language settings (character set, currency format, decimal comma vs. point). It is irrelevant here; use the default.
Definition (Object, first intuition; Objekt)
An object can be created, read and modified by a program. It has
state (Zustand): a collection of values, e.g. a person’s first and last name;
behaviour (Verhalten): operations for or with it, e.g. taking a spouse’s name on marriage, or println;
identity (Identität): objects can be distinguished even with identical state. Two people both named “Malte Schwerhoff” are different people.
Value vs. object:5∈N is a value. A 5 stays a 5; 5+1 produces a new value 6. A table of numbers (e.g. an Excel sheet) is an object: its content can change, and it offers operations such as sorting.
Student question: how can identical objects have different identities? Each object created at runtime gets a unique identifier, typically its memory address. This becomes relevant when comparing objects later.
2.1.4 Names: rules vs. conventions
Rule (compiler)
Convention (humans)
Program/class
valid identifier, no keyword, = file name
UpperCamelCase: HelloWorld (no spaces, hyphens or underscores)
When you join a team or language community, adopt its conventions. Don’t be the newcomer who tells every Java programmer “in C++ I’d do it like this.”
Exam (student question): grading is test-based, so style and conventions are not penalised. Get used to them anyway: they become automatic, and future teams will care.
Definition (Keyword, Schlüsselwort)
A keyword is a reserved identifier with a special meaning in the language, so it can’t be used as a name: abstract assert boolean break byte case catch char class const continue default do double else enum extends final finally float for goto if implements import instanceof int interface long native new package private protected public return short static strictfp super switch synchronized this throw throws transient try var void volatile while true false null
main and String are not keywords. Other rules (;, bracket pairing) are separate syntax rules.
2.1.5 Whitespace and formatting
Java ignores whitespace and line breaks between tokens. The lecturer demonstrated a deliberately “catastrophic” layout that still compiles. Structure comes from ; and { }.
Student question about Python: in Python (and Haskell) indentation is meaningful. In most languages, including Java, brackets and semicolons carry the structure. Formatting still matters for human readers.
2.1.6 Comments (Kommentare)
Definition (Comment)
Notes in the program text that help humans understand it and have no effect on execution. The compiler discards them.
// … comments out everything until the end of the line.
/* … */ comments out everything up to the closing */ (multi-line).
Uses:
explanations;
warnings (“divisor must not be 0”);
// TODO;
temporarily disabling code while debugging: comment out the last change and see whether the old behaviour returns.
The leading * on each line of a block comment and the /** */ form are conventions for documentation tools. Not relevant here; use whichever comment form you prefer.
2.1.7 Strings (Zeichenketten)
Definition (String)
A string is a sequence of characters enclosed in double quotes "…". Examples: "Hallo Welt", "hallo welt", "3+2". println("3+2") prints 3+2, not 5; it is text, not arithmetic.
Restrictions on string literals:
One line only. A line break inside "…" is a compiler error (“unclosed string literal”). Workaround: concatenate several strings with + across lines. + between strings is concatenation (Verkettung), not addition. (Another way, text blocks, was mentioned but not shown.)
A raw " is not allowed inside. Use ' instead, or escape it.
The lecturer’s aside: a Java program is itself just one long string without quotes. The compiler reads text and produces an executable program, which is covered in Compiler Design.
Definition (Escape sequence, Ersatzdarstellung)
A backslash \ (the escape character, Maskierungszeichen) gives the next character a special meaning:
Sequence
Meaning
\n
new line
\t
tab
\"
double quote
\\
backslash (the backslash escapes itself; student question)
There are more; memorising them is not worthwhile. If string output misbehaves, check for unescaped characters.
Strings vs. comments: whichever starts first wins. In println("// Kommentar") the string starts first, so // is just two characters. A // outside a string starts a real comment.
2.1.8 println vs. print
Definition
println(str) outputs str followed by "\n".
print(str) outputs only str. Several prints can form one line.
It is the ln, not the placement on separate source lines, that causes the line break. print is the essential one, since you can always add \n yourself; println is a convenience. print becomes really useful with expressions and loops.
Started from the command line. Type single statements or expressions and see the result immediately.
Methods can be entered without public static.
Tab autocompletes and lists options, e.g. System.out. + Tab shows println, print, printf, write, …
The REPL embeds your code in a hidden full program, so its behaviour sometimes differs. Example: declared but uninitialised variables get default values (0) in JShell but cause compile errors in real programs. Good for experiments; don’t rely on it 100 %.
Demo: 1/2 gives 0, so 1/(1/2) gives ArithmeticException: / by zero, a runtime error.
2.2 Methods I: program structuring (Methoden I: Programmstrukturierung)
Definition (Method, Methode)
A named sequence of statements. The statements run when the method is called. main is called automatically at program start.
Mathematical analogy:x hides a complex computation behind a simple symbol. Writing it is easy; computing it is not.
Why methods?
Structure: instead of putting everything in one main, which could in principle be millions of lines (e.g. Windows).
Avoid repetition: written once in the program text, called and executed many times. A change happens in one place.
New, specialised statements: a language has n built-in commands, never enough for everything (“load my CodeExpert points and compute my grade” is not a Java command). Methods build big things from small “Lego bricks”. Your method is the next person’s library function: this is the idea behind libraries.
Rough recipe for program development
Decompose the overall task into sub-tasks. This requires understanding the problem and problem-solving strategies.
Solve each sub-task in a separate method. Smaller, so easier to implement.
Compose the partial solutions into the overall solution, which again requires understanding the problem.
2.2.1 Control flow with method calls
Definition (Linear control flow, linearer Kontrollfluss)
A sequence of statements runs top to bottom, in textual order. Branches and loops come later.
The call format name(); works here only because the methods are static. Non-static methods use obj.name(); and come later.
Order of methods
The order in which methods appear in the file is irrelevant in Java (student question, asked twice). A method may be called above its definition. This is not true in every language, e.g. C++ and Python.
2.2.2 How methods come about: two strategies
A: afterwards (im Nachhinein)
B: following sub-problems (Teilproblemen folgend)
Process
develop the algorithm, implement it all in main, then split it into methods (reusable parts, remove redundancy)
identify (recurring) sub-problems while designing; implement directly as methods
Pros
no time wasted on methods that turn out useless; everything there makes sense; easier at the start; fine “locally” while developing one method
structures the thinking; small units instead of one long block; redundancy never arises; enables teamwork (essential in practice)
Cons
redundancy in the meantime; little structure; no names that document intent; large mains become unmanageable
you need to understand the decomposition up front
Recommendation
B is generally preferable. A is acceptable at the beginning and for small problems. Brainstorming in main first is fine, but eventually you must switch to B.
Student arguments collected in lecture (Thursday):
Pro A: you don’t invest time in methods that turn out pointless.
Contra A: you must think through the whole implementation first. The lecturer’s reply: with B you also need to understand which methods you need.
Contra A: harder to find errors? The lecturer was cautious: more methods also mean more jumps when debugging. For a few lines, linear code can even be easier to follow; for 1000-line mains the argument clearly holds.
Contra A: redundancy. With seven copies of printNasa, renaming the rocket means seven edits.
Pro methods:a method name documents intent.computeSquareRoot is understood immediately; 17 lines of arithmetic must be decoded.
Pro methods:smaller context. Inside squareRoot only its parameter matters; all variables of main can be ignored.
Recipe analogy: “put spaghetti in hot water” omits how to heat water or that you lift the lid. Good method names give that level of abstraction.
Programs operate on data, e.g. a boat’s height, length, depth, price, name, location, owner.
Some operations make sense: volume as length × width × height, 75 % of the price.
Others don’t: “shortest path between the boat’s location and the day before yesterday”, “start a houseboat breeding programme”.
Goal of programming languages: make sensible computations easy and prevent nonsensical ones.
Definition (Type, Typ)
A type describes a category of values/data, similar to mathematical sets (Z vs. Q). It determines the properties of the data and therefore which operations are possible.
Example: types Boat and Cat.
Shared: an owner, “feeding” (cat food vs. diesel).
Different: a cat can be (somewhat) trained and keeps mice away; a boat cannot, unless the mice walk into the propeller voluntarily.
Why types? Many languages require explicit types for:
Safety (important for EProg): types prevent errors. You can’t add an AHV number to a salary if they have different types. If the compiler rejects 7.toUpperCase(), you never have to hunt that bug.
Documentation (important for EProg): types specify the parts of a computation, i.e. what is expected (price? location?) and what is computed.
Efficiency (less important here): compilers and runtimes use types for optimisation.
Exam Trap: "Python has no types" is wrong
Python is dynamically typed (dynamisch getypt): types exist at runtime and cause crashes on unsupported operations. You just don’t write them in the code.
Java is statically typed (statisch getypt): types are checked at compile time and are only partially available at runtime.
C/C++: the compiler uses types, but they are largely gone at runtime.
Python, JavaScript: types exist only at runtime.
The lecturer found this remarkable: in principle, type information can be thrown away completely after compilation, because the compiler has already ensured that only sensible operations are applied to each bit sequence.
Definition (Primitive types, primitive Typen)
Java has eight primitive types for numbers, characters and truth values. The relevant ones:
Type
Description
Example values
int
integers
-2147483648, -1, 46325
long
large integers
-42, 0, 9223372036854775807
double
real numbers
3.1, -0.25, 3102327700601.337
char
single characters
'a', 'X', '?', '\n'
boolean
truth values
true, false
Details: JLS §4.2. The others (byte, short, float) are irrelevant for EProg.
From bit sequences to data: the language defines how each type is implemented, i.e. the representation of values and the definition of operations. Internally everything is 0s and 1s:
97 is 01100001.
"a" has ASCII code 97, so it is also 01100001. "ab" is 01100001 01100010.
What does 01100001 mean? That’s what the type says. So "a".toUpperCase() makes sense, but 97.toUpperCase() does not.
Definition (Where types come from)
Valid Java types are:
built-in base types (Basistypen), always available: primitive types such as int, and types from Java’s standard library such as String;
user-defined types, from us or third-party libraries, introduced with classes (Klassen) later. They let you model reality: Boat for a boat rental, Student, Lecturer, Employee for a university.
Motivation for variables: surface area of a cuboid (Oberfläche eines Quaders) with l=70, h=40, d=27 cm: A=2(ld+dh+hl)=2(70⋅27+27⋅40+40⋅70)=2(1890+1080+2800)=11540 cm2
public class Quader { // Oberflächenberechnung für einen bestimmten Quader public static void main(String[] args) { System.out.print("Die Oberfläche beträgt "); System.out.print(2 * (70 * 27 + 27 * 40 + 40 * 70)); System.out.println(" cm2"); // Die Oberfläche beträgt 11540 cm2 }}
Every dimension appears twice. A slip such as 50 * 70 instead of 40 * 70 silently gives 12940.
Changing a dimension means editing every occurrence.
Discrepancy: in the demo the lecturer called the result “Volumen”. It is the surface area in cm², as on the slides.
Definition (Variable)
A variable gives a value a meaningful name, e.g. height for 40. This is known from maths: in computations a variable is “replaced” by its value, so height * height stands for 40 * 40, which is 1600.
But program variables can change (more below). In Java every variable needs a name and a type; here integer values fit, so int.
Definition (Declaration, Deklaration)
Syntax:type name; e.g. int height;
Announces the variable. Afterwards it can store any value of type.
The name is (almost) freely chosen. Rule: no keyword, starts with a letter. Convention: lowerCamelCase.
Definition (Initialisation and assignment)
Declaration with initialisation:type name = expression; e.g. int height = 40;. The expression must have a matching type (“expression ≈ value” for now).
= means assignment (Zuweisung): from now on, the variable has this value.
An assignment is not mathematical equality, even though the operator unfortunately looks like it.
int length = 70;int height = 40; // declaration + direct initialisationint depth = 27;System.out.print("Die Oberfläche beträgt ");System.out.print(2 * (length*depth + depth*height + height*length)); // 11540System.out.println(" cm2");
Declaration and initialisation may be separate statements (int length; … length = 70;). In this example there is no reason to separate them, but sometimes it is necessary.
No default value for local primitive variables
Local variables of primitive type have no default value; they must be explicitly initialised. Reading an uninitialised variable is a compiler error (“variable … might not have been initialized”). JShell behaves differently: it initialises with a default value, e.g. 0 for int.
Reassignment (demo): height = 45; gives height the value 45 from then on. Setting it to 0 makes the area result much smaller.
Variable names as documentation (Variablennamen als Dokumentation):
Names are irrelevant to the computer, but not to humans.
Reasonably short:lengthInMM, not lengthInTheMetricSystemUnitMillimeter. Even with autocomplete, you must read them.
Distinguishable in context:firstName, middleName rather than name1, name2.
Good names are hard across intermediate values (salary, salaryAfterRaise, salaryAfterRaiseWithChristmasBonus, …). Short names (i, x, n) are fine for short-lived variables.
“Be pragmatic, not dogmatic.” Train good naming from the start, “like in sports”.
Definition (Type inference, Typinferenz)
var score = 99; is legal: the compiler infers int. Recommendation: write types explicitly as documentation. In var result = findHomeCityForLegi("24-135-987"); the reader can’t tell whether result is a truth value, a name or a postcode.
2.3.2 Expressions over base types (Ausdrücke über Basistypen)
Definition (Expression, Ausdruck)
An expression represents a computation that evaluates to a value. 27 is an expression, and so is 2 * (length*depth + depth*height + height*length).
Syntax (a simplified, incomplete EBNF; no surprises compared with school maths):
<number> describes number literals (Zahl-Literale).
A literal is not the same as a value: 1e1 and 10.0 are different literals for the same value.
The EBNF describes only syntax: no types, no semantics.
Definition (Arithmetic operators)
Operators form expressions from sub-expressions:
Operator
Meaning
+
addition
-
subtraction, or negation (unary)
*
multiplication
/
division
%
modulo (remainder)
For every X ⊗ Y (with X, Y int-typed expressions) two questions must be answered: what is the result (semantics), and what is its type?
Type rule
For + - * / %: TypeA ⊗ TypeA gives TypeA.
2 * 3 gives 6 of type int, because 2 and 3 are ints.
-(7) gives -7 of type int.
The type never changes automatically (student question). Integer.MAX_VALUE + 1 does not switch to long; the result stays int and wraps around.
Definition (Overflow, Über-/Unterlauf)
+ - * behave as in maths, almost. Numbers are stored as finite bit sequences: 32 bits for int, 64 for long. So there is a smallest and a largest number, and arithmetic wraps around like a clock: MAX+1=MIN,MIN−1=MAX
For int: MAX=231−1=2147483647 and MIN=−231.
Demo: Integer.MAX_VALUE + 1 is a large negative number; Integer.MIN_VALUE - 10 a large positive one. long behaves identically (student question), just with a larger range. Bit-level details come in Digital Design (2nd semester). Even a billion bits would still have a min and a max.
Definition (Integer division)
int / int gives int. The mathematically arising decimal places are truncated (abgeschnitten), not rounded commercially:
5 / 2 is 2 (not 2.5)
10 / 6 is 1 (not 1.666…)
32 / -5 is -6 (not −6.4)
Division by 0 aborts the program: a runtime error.
Definition (Modulo)
% gives the remainder of integer division:
5 % 2 is 1
10 % 6 is 4
32 % -5 is 2
The following identity always holds: (a/b)⋅b+(a%b)=a
Division by 0 again aborts the program.
Uses of modulo:
Last digit: 230857 % 10 is 7.
Last 4 digits: 658236489 % 10000 is 6489, i.e. modulo 104.
Even or odd: 42 % 2 is 0, 7 % 2 is 1.
Beyond EProg: cryptography (Discrete Maths) and hash-based data structures (A&W).
Definition (Associativity, Assoziativität)
For a chain X ⊗ Y ⊗ Z of the same operator, associativity decides to which operator Y belongs:
left-associative (linksassoziativ): X ⊗ Y ⊗ Z = (X ⊗ Y) ⊗ Z. The operator “grabs everything to its left”.
right-associative (rechtsassoziativ): X ⊗ Y ⊗ Z = X ⊗ (Y ⊗ Z).
Aside: in mathematics an operator is associative if (X⊗Y)⊗Z=X⊗(Y⊗Z). In programming even + needs a defined direction, because sub-expressions can have side effects later, so their order matters.
Definition (Precedence, Präzedenz: also binding strength or rank order, Bindungsstärke, Rangordnung)
For different operators, X ⊗ Y ⊙ Z, operand Y binds to the operator with higher precedence.
“Point before line” (Punkt vor Strich): * / % bind tighter than + -. So 2 + 6 * 5 = 2 + (6 * 5).
Unary operators (e.g. negation -) bind tighter than binary ones. -8 + 3 is (-8) + 3 = −5, regardless of spacing.
Explicit parentheses are never overridden by precedence (student question): (2 + 6) * 5 is 40.
Implicit bracketing (implizite Klammerung): the algorithm
An operand is bound by the operator with higher precedence: 2 * x + 3 = (2 * x) + 3.
If two operators have the same precedence, then in Java they also have the same associativity (not so in C++, where the algorithm is more complex), and associativity decides:
x - 7 + y = (x - 7) + y (same precedence, left-associative)
x = y = 0 = x = (y = 0) (same precedence, right-associative)
Recommendation: use explicit parentheses when you want a different order, and when expressions become complex, for readability. Don’t over-parenthesise trivial point-before-line cases.
The operands of an operator are evaluated left to right. For the expressions so far this makes no difference, but it will once expressions have side effects. Note that an assignment already has one.
Definition (Type double)
Literals: 6.022, -42.0 (like integers, but with a point); exponent notation 1e2, 1e-3, 2.143e17, where XeY stands for X⋅10Y.
Different notations can be semantically equivalent: 1e1 and 10.0 are the same value.
All arithmetic operators are defined for double. Division is now as expected: 15.0 / 2.0 is 7.5.
double values are stored with 64 bits, a finite representation. Consequences:
there are a largest and a smallest value;
there are rounding errors, because of gaps in the value range.
Why “floating point”:0,01⋅107=1,0⋅105=100,0⋅103. Normalising by adjusting the exponent lets the decimal point “float”. Only the pair (significand, exponent) is stored, which is space-efficient.
Almost all languages use 64 bits, because CPUs have a floating point unit (FPU) that computes the IEEE 754 formats in hardware. A 128-bit double would have to be done in software and be slow.
Details: Digital Design (2nd semester), Numerical Methods (later).
Definition (Mixed types, implicit conversion)
int/long combined with double gives double. Double “wins”, because it can represent integers and fractions:
3 / 2.0 and 3.0 / 2 both give 1.5.
4.0 / 2 gives 2.0, not the int 2.
The conversion (Umwandlung) from the smaller to the larger type happens implicitly (automatically), separately for each operator and its operands. It is not decided “for the whole expression”.
Definition (Cast, explizite Typumwandlung)
Syntax:(type) expression
Uses: increase precision, e.g. cast int to double so a division is exact; or reduce precision, e.g. cut off unwanted decimals.
(type) is the cast operator: unary, right-associative, with higher precedence than the arithmetic operators. It converts only the operand directly to its right (“greedy”).
It exists for almost all primitive types. A second kind of cast comes later with subtyping.
Typical use: percentages from integer counts. Enrolled and present students are naturally ints (no one is “400.0 students”), but the percentage should have decimals.
Why not just write .0? With literals you could, but with variables (x + y / z) you can’t. You must cast.
Compatibility of base types (Kompatibilität ausgewählter Basistypen)
Source (expression) → target (variable)
int
long
double
int
✓
implicit
implicit
long
explicit cast
✓
implicit, possibly lossy
double
explicit cast
explicit cast
✓
long x = 1024; converts int to long implicitly.
int y = (int) x; needs an explicit cast. Without it you get “possible lossy conversion”.
double d = 9507129656401991L; compiles, but stores 9507129656401992.0. Large longs fall into the gaps of double. This is the one irregular entry in the table.
A cast is how you “override” the compiler: “I know I lose precision, and that’s fine.”
Narrowing wraps around: (int) Long.MAX_VALUE is -1 (demo). There is no other option besides rejecting it.
Other numeric types and literal formats (look them up when needed):
long: range −263 to 263−1; literals need the suffix L, e.g. 9876543210L. Without L it is an int literal.
short: −32768 to 32767, no own literals.
char: technically numeric, range 0 to 65535; literals like 'g', '\u042E'.
byte, float.
Underscores as separators: 10_000, 1_000_000.
Hexadecimal: 0xFF. Octal also exists.
2.3.3 Strings and concatenation (Zeichenketten verketten)
Status so far:
Primitive types, e.g. int and double: values (4, 2.17) and operations (division with /) seen.
Standard-library types, e.g. String: values seen ("hello", "2 + 3"). Now: concatenation with +. Later: operations via methods ("hello".toUpperCase()).
Later: self-developed types.
Definition (Concatenation, Verkettung)
The operator + joins two strings:
"You" + "Tube" gives "YouTube"
":" + "-" + ")" gives ":-)"
Definition (Standard string representation)
In Java every value of every type has a default representation as a String. That is why println can print anything. For primitives it is as expected: 42 gives "42", -36.7 gives "-36.7", and x with int x = 77 gives "77". (Hexadecimal or binary would also be possible, but the human-friendly form is chosen.) For self-developed types the representation can be changed later.
Concatenation with other types
If one operand of + is a String, the other value’s string representation is concatenated:
3 + " Grad Celsius" gives "3 Grad Celsius"
"Olympia " + 2024 gives "Olympia 2024"
Concatenation + has the same precedence and associativity as arithmetic +: below *, left-associative.
double grade = 5.25 + 0.25;System.out.println("Endnote ist " + grade); // Endnote ist 5.5
Recap of variable terminology:
double inch; // declarationinch = 6.4; // initialisation (first write)double centimeter = inch * 2.54; // declaration + initialisation; inch is evaluated (read)System.out.println(inch + "\" sind " + centimeter + "cm"); // 6.4" sind 16.256cminch = 22; // assignment (int 22 implicitly becomes 22.0)System.out.println(inch + "\" sind " + inch * 2.54 + "cm"); // 22.0" sind 55.88cm
Definition (The four roles of a variable)
Declaration: reserves memory large enough for the type (int 32 bits, double 64 bits) under a name. Picture a “memory tape with named cells”; technical details come in Compiler Design.
Initialisation: the first write of a value into that cell.
Assignment: writing a (new) value into the cell.
Evaluation (Auswertung): in an expression, a variable evaluates to the value its cell has at that moment.
Typical errors (all compile-time):
Error
Code
Message (roughly)
Missing declaration
teeth = 28; without int teeth;
cannot be resolved to a variable
Use before declaration
teeth = 28; int teeth;
cannot be resolved to a variable
Double declaration (types irrelevant)
int teeth; long teeth;
duplicate local variable
Missing initialisation
int teeth; println(teeth);
may not have been initialized
Wrong type
int teeth; teeth = "28";
type mismatch: cannot convert from String to int
Assignment is not equality
In maths, x=x+1 (“x equals itself plus one”) is nonsense, equivalent to false. In Java, x = x + 1; first evaluates the right side with the current value, then stores the result. An assignment has an effect (a state change), and effects do not exist in declarative mathematics.
The lecturer: this is where CS first clearly departs from school maths, and intuition often “sags” later with objects and references, where effects matter a lot.
2.4 Input and Random (Eingabe und Random)
Interactive programs react to the user: print a prompt (“Ihr Vorname lautet: ”), read the typed name into a variable, print it.
Console (Konsole): originally a separate typewriter-like device, today just a window (also called terminal or shell).
Standard input is System.in; standard output is System.out.
IntelliJ uses one window for both, so afterwards you can’t distinguish your input from the program’s output.
Input is complex:
Sources differ.
User behaviour is unpredictable and uncontrollable, so faulty input must be handled. Interacting with the environment always means: expect every error.
The console text must be converted into the internal representation (decimal text to binary), which is covered in Digital Design.
In Java, Scanner provides this functionality. Output is handled by e.g. println.
Definition (Scanner)
import java.util.Scanner; // at the top of the file, before the classScanner console = new Scanner(System.in); // new Scanner object; input source = console
A new object of a non-primitive type must be created with the keyword new. Unlike System.out, the scanner doesn’t exist yet. Learn the line by heart for now; details come with objects.
Methods are called with dot notation:
Method
Reads
nextInt()
an int from the user and returns it
nextDouble()
a double
next()
a one-wordString
nextLine()
a one-lineString
Full documentation: Oracle Java API docs for java.util.Scanner. Ignore what you don’t understand yet. Python’s input() does the same thing, just more concisely.
Typical input flow
Print a prompt (Eingabeaufforderung) so the user knows that, and what, input is expected. It is good style; the demo without a prompt just “hangs” silently. Use print, so the input appears on the same line.
Input: the program is interrupted (it waits) until input has been made. Input is buffered: it is only sent when the user presses Enter, unlike a game like Snake that reacts to each keypress (possible in Java, not shown).
The program continues.
Definition (Token, Eingabeelement)
A sequence of characters that the scanner reads as one unit, separated by whitespace (Zwischenraum): space, tab, newline.
8 9 is two tokens, 8 and 9 (not 89). 8 99 gives 8 and 99.
45 18 $2.50 hello world is five tokens.
"hello world" with the quotes is two tokens, "hello and world". Quotes have no special meaning.
Ideally tokenisation would be context-dependent (e.g. $2.50 with its unit as one token). Describing it needs something EBNF-like; Java’s default tokenisation is used here.
If a token has the wrong form for the method, you get a runtime error. The compiler cannot know what the user will type.
Resources warning: open resources (files, …) must be closed again, e.g. in a try block (later). IntelliJ may mark a bare new Scanner(System.in) as a resource leak. For console input you can ignore this or accept IntelliJ’s suggestion.
2.4.1 Random numbers (Zufallszahlen)
Random numbers can be used instead of user input, e.g. to fake data while developing.
Definition (Random)
import java.util.Random;Random rand = new Random();
Method
Returns
nextInt()
a “random” int from the whole int range
nextInt(n)
a random int in [0,n), i.e. from {0,…,n−1}
nextDouble()
a random double in [0.0,1.0); can be 0.9999…, never 1.0
Definition (Pseudo-random numbers)
Standard (non-quantum) hardware is deterministic, so it cannot produce true randomness. A pseudo-random number generator is a mathematical function that deterministically computes new values from a start value, the seed. The values are distributed so that they look random.
True randomness (e.g. for cryptographic keys such as SSH/PGP) needs interaction with the environment:
mouse movements (why key generators ask you to wiggle the mouse);
timing of network packets;
CPU heat;
dedicated hardware modules.
More in Discrete Maths. For us, random-looking is good enough.
3. Step-by-Step Derivations
3.1 Implicit bracketing of 6 + x / 2 * y
Precedence:/ and * bind tighter than +, so they are handled first.
/ and * have equal precedence, so they have equal associativity: left-associative. The left one, /, grabs x (everything to its left) and its right operand 2: 6 + (x / 2) * y.
Then * grabs everything to its left, (x / 2), and y: 6 + ((x / 2) * y).
Finally the lower-ranked + grabs 6 and the whole bracketed expression: (6 + ((x / 2) * y)).
The lecturer would write 6 + (x / 2) * y. He advises against both the bare version, which leaves too much room for interpretation for beginners, and the fully bracketed one, which is overkill.
3.2 Expression tree and evaluation
The fully bracketed expression (1 * 2) + ((3 * 5) % 4) corresponds to an expression tree (Ausdrucksbaum):
+
/ \
* %
/ \ / \
1 2 * 4
/ \
3 5
The deepest brackets are computed first, and the root operator is applied last. Operands are evaluated left to right:
1 * 2 gives 2. The + still needs its right operand, so switch to the right subtree.
3 * 5 gives 15.
15 % 4 gives 3.
2 + 3 gives 5.
Mathematically you could compute 3 * 5 first, but Java doesn’t, and with side effects the order will matter.
This is exactly what compilers do: they build such syntax trees and do all their work on them (Compiler Design). Bracketing and evaluating overly complex expressions is a classic introductory exam task. The lecturer still draws trees himself.
3.3 Type derivation with the tree: 7 / 3 * 1.2 + 3 / 2
The conversion happens per operator. The compiler does not see “the result is double, so compute everything in double”.
3.4 Modulo with negative numbers via the identity
The identity (a/b)⋅b+(a%b)=a determines % once / is known (truncation toward 0).
-11 % -4:
-11 / -4: mathematically 2.75, truncated to 2.
2 * (-4) is −8.
We need −8+r=−11, so r=−3.
-11 % 4:
-11 / 4: mathematically −2.75, truncated to -2.
(-2) * 4 is −8.
−8+r=−11, so r=−3.
11 % -4:
11 / -4 is -2.
(-2) * (-4) is 8.
8+r=11, so r=3.
Sign rule
In Java the result of % is zero or has the sign of the left operand (the dividend).
Discrepancy: a student proposed this rule in lecture, and the lecturer was unsure whether it holds in general. It does; it follows from truncating division and is specified in the Java Language Specification.
In-lecture discussion of 8 % 20: 20 fits zero times into 8, so the whole 8 remains. This holds up to 19 % 20 = 19. 20 % 20 = 0, 21 % 20 = 1, 28 % 20 = 8. The lecturer noted that exactly this discussion (“shouldn’t it be 0?”) happens every year.
3.5 Chained assignment x = y = z = 5
Demo in JShell with int x, y, z; (pre-filled with 0 in JShell):
= is right-associative, so the expression is x = (y = (z = 5)).
z = 5 has the effect of storing 5 in z, and it evaluates to the new value of z, which is 5.
y = 5 stores 5 in y and evaluates to 5.
x = 5 stores 5 in x. All three are 5.
There is no special treatment of =: the assignment is simply an expression that has both an effect and a value.
A student’s alternative interpretation (shift values: x gets old y, y gets old z, z gets 5) is not what happens.
Wrong bracketing:(x = y) = 0.
x = y evaluates to a value, e.g. −1.
That leaves -1 = 0, which is nonsense.
This is rejected by the compiler: the left side of an assignment must be a variable, not a value. The error message is the same for every wrongly bracketed variant.
3.6 Random ranges: derivations
Integer in [1,n]:1 + rand.nextInt(n)
nextInt(n) gives {0,…,n−1}.
Shifting by 1 gives {1,…,n}.
Integer in [min,max]:min + rand.nextInt(max - min + 1)
We need max−min+1 distinct values, so nextInt(max - min + 1) gives {0,…,max−min}.
Shifting by min gives {min,…,max}.
Double in [min,max):min + rand.nextDouble() * (max - min)
nextDouble() gives [0,1).
Scaling by the width gives [0,max−min).
Shifting gives [min,max).
Random object out of n: number the objects 1..n or 0..n−1, draw a number, and look it up in a list (lists come later).
Student question: in school, the probability of drawing a specific real number is 0, because the reals are uncountable. Here double values are finitely many, so each value has a small but non-zero probability. The generator also uses a particular distribution, which can be influenced; not relevant here.
3.7 Control-flow trace with nested calls (slide example)
public class ControlFlowExample2 { public static void main(String[] args) { System.out.println("Anfang"); message1(); System.out.println("Mitte"); message2(); System.out.println("Ende"); } public static void message1() { System.out.println("1. Nachricht"); } public static void message2() { message1(); System.out.println("2. Nachricht"); }}
Step
Active calls
Executed
Output so far
1
main
println("Anfang")
Anfang
2
main → message1
println("1. Nachricht"), then return
+ 1. Nachricht
3
main
println("Mitte")
+ Mitte
4
main → message2 → message1
println("1. Nachricht"), then return to message2. We are now three calls deep.
Compiler messages are not always directly understandable. A missing closing } may be reported far from where it actually is.
4.2 The ASCII rocket (decomposition)
Target:
/\
/ \
/ \
+------+
| NA |
| SA |
+------+
| NA |
| SA |
+------+
/\
/ \
/ \
Version 1: thirteen printlns in main. It works, but it is redundant: renaming “NASA” requires several edits, and forgetting one gives a crooked rocket.
Finding sub-tasks (student suggestions):
The hat (tip) is a building block. It reappears as the exhaust at the bottom.
The separator line+------+ is its own block.
The block| NA | / | SA |without the lines. A first suggestion included the line above the block, but then the final separator would need special handling.
Too fine a decomposition (one method per line or character) is “pushing molecules around”. There is no right or wrong; it is intuition, feedback, team decisions and rules of thumb.
public class AsciiRocketV2 { public static void main(String[] args) { // overall solution printHat(); // T1 / m1 printLine(); // T2 / m2 printBlock(); // T3 / m3 printLine(); printBlock(); printLine(); printHat(); } public static void printHat() { System.out.println(" /\\"); // the right edge must be escaped System.out.println(" / \\"); System.out.println("/ \\"); } public static void printLine() { System.out.println("+------+"); } public static void printBlock() { System.out.println("| NA |"); System.out.println("| SA |"); }}
In the demo the hat method was called printHead. IntelliJ can do this automatically (“Extract Method”); doing it by hand is equally fine.
Thursday refinement: introduce printCompartment() = printLine(); printBlock();. Then main becomes printHat(); printCompartment(); printCompartment(); printLine(); printHat();, and a third compartment is one more line.
The lecturer: he knows no killer argument that this is definitely better than V2, and naming gets harder (“Block” vs. “Compartment”). Programming is technical, but not always right-or-wrong.
Suggested exercise (slide 63): a well-structured program printing several figures (egg shapes, a STOP sign, a hat). Identify the recurring parts (upper half, lower half, +--------+ line) and give each a method.
4.3 The debugger (preview; covered in exercises in week 5)
Breakpoint: click the gutter next to the line numbers, which shows a red dot. Run with the bug icon (Debug) instead of Play. Execution pauses before the marked line, and the console shows nothing yet.
Step Into: follow into the called method line by line.
Step Over: execute the call completely in one step. It is not skipped; you just don’t watch it.
Resume: run to the next breakpoint or to the end. Afterwards the controls disappear.
Call stack (“Threads & Variables”, read bottom to top): e.g. main line 4 → printCompartment line 13 → printBlock line 18, three calls deep.
Why breakpoints: in a large program you don’t want to click through every input; you stop exactly where you suspect the bug. Later the debugger also shows variable contents.
4.4 Escape-sequence questions (slides 35–36)
Slide 35:println("\\Hallo\nWie\tgeht's \"uns\"?\\\\") prints \Hallo on line 1 and Wie⟶geht's "uns"?\\ on line 2 (⟶ = tab).
Code
Output
println("a\tb\\\tc")
a⟶b\⟶c: \t, then \\ is one \, then \t
println("'")
': a single quote needs no escape inside "…"
println("\"\"\"")
"""
println("// Kommentar")
// Kommentar: inside a string, so not a comment
println("C:\neuer-S\tiefel")
C: on line 1, then euer-S⟶iefel on line 2. \n and \t are interpreted!
Reverse question: which literal prints / \ /// \\\? Every backslash must be doubled: "/ \\ /// \\\\\\".
4.5 Integer division and modulo (slide 92, plus the lecture’s JShell checks)
Expression
Result
Check with (a/b)b+a%b=a
10 / 4
2
-23 / 11
-2
−2⋅11=−22 is the closest an integer multiple gets
8 % 20
8
0⋅20+8
2 % 2
0
11 % -4
3
(−2)(−4)+3=11
-11 % -4
-3
2(−4)−3=−11
-11 % 4
-3
(−2)(4)−3=−11
4.6 Precedence (slide 101)
Expression
Steps
Result
9 / 5
int division
1
695 % 20
34⋅20=680
15
7 + 6 * 5
7 + 30
37
7 * 6 + 5
42 + 5
47
248 % 100 / 5
left-assoc.: 48 / 5
9
6 * 3 - 9 / 4
18 - 2
16
6 + 18 % (17 - 12)
6 + 18 % 5 = 6 + 3
9
Demos: 64 / 8 / 2 is 4, 64 / (8 / 2) is 16. 2 + 6 * 5 is 32, (2 + 6) * 5 is 40. Bracketing differently in JShell and comparing results is a good way to test your own understanding.
The exact result 1.2 lies in a “gap” and is approximated.
Repeating such operations can accumulate error. Making algorithms numerically stable is the subject of Numerical Methods.
A student asked why the literal 1.2 doesn’t become that same value. Converting a literal to bits is a different process from computing an addition, so the two approximations can differ. Not deepened, as it isn’t EProg’s focus.
For exact results, one option is to compute with integers and scale at the end.
Consequence for later: 1.1 + 0.1 == 1.2 is false. Never compare doubles with ==.
Example: int x = 1.0; (student question)
int x = 1; compiles, but int x = 1.0; gives a compile error, “possible lossy conversion”. The literal 1.0 has type double, even though its value is a whole number.
(double) 1 + 1 / 2 is 1.0, not 1.5. The cast only converts the 1 directly to its right. 1 / 2 is still integer division, giving 0. The lecturer: “in my experience, many expect 0.5 here.”
Likewise (double) (a / b) casts after the integer division, so the fraction is already lost (student question). Write (double) a / b.
x * y is mixed, so x is converted and the result is double.
Storing a double in the int variable x needs an explicit cast: x = (int) (x * y);.
The second-most popular answer had the correct arithmetic result, but overlooked the compile error.
Follow-up:x = (int) x * y; gives the same error. The unary, right-associative cast grabs only x, which is already an int, so the cast is a no-op. Then int × double is double again.
Float and int (student question)
Can float represent every int exactly? The lecturer didn’t know and wanted to look it up.
Answer: no.float has a 24-bit significand, so integers above 224 can be rounded. For example 224+1=16777217 is not representable. Java still converts int to float implicitly, just as with long to double.
Exam Trap: Other languages, other rules
Java -7 % 3 is -1, while Python gives 2. Mathematics knows two definitions (mod vs. rem). Java uses the more widespread one; neither is “wrong”.
Python -2**2 is -4, while Excel =-2^2 is 4. They give negation and exponentiation different precedence.
The concepts (precedence, associativity) exist in every language. The concrete rules must be checked anew.
4.10 String concatenation (slide 119, discussed in week 3 Tuesday)
Expression
Steps
Result
12 - 3 + 5
(12 - 3) + 5
14
2 + 3 + " Zehen"
(2 + 3) = 5 first (int addition), then concatenation
Discrepancy: for "Nummer " + (3 + 2) the lecturer said “then we would have Nummer 15”. The correct result is "Nummer 5".
Exam Trap: Mixed +
"Sum: " + a + b concatenates, giving "Sum: 23" for a = 2, b = 3. Write "Sum: " + (a + b) to add. The leftmost pair decides whether a given + is addition or concatenation.
4.11 Variable traces
teeth trace:
Statement
Cell teeth
Output
int teeth = 28;
28
println(teeth);
28
28
teeth = (13 + 17) / 2;
15
println(teeth + 1);
15 (unchanged, only read)
16
teeth = teeth + 1;
16
4.12 Scanner examples and lecture experiments
import java.util.Scanner;public class UserInputExample { public static void main(String[] args) { Scanner myConsole = new Scanner(System.in); System.out.print("Wie alt sind Sie? "); // prompt int alter = myConsole.nextInt(); int jahre = 65 - alter; System.out.println(jahre + " Jahre bis zur Pensionierung"); }}// Wie alt sind Sie? 32// 33 Jahre bis zur Pensionierung
Reading two numbers:
System.out.print("Zwei Zahlen bitte: ");int a = myConsole.nextInt();int b = myConsole.nextInt();System.out.println(a * b); // input "6 7" gives 42
Experiments from the lecture:
Input
Behaviour
Explanation
2 Enter, then extra blank lines, then 3
6
whitespace, including newlines, between tokens is skipped; the program waits for the second int
2 Enter, then 9 (with an intermediate println("a = " + a))
prints a = 2, waits, then the result
each nextInt() blocks until a token is available
acht 9 for nextInt()
InputMismatchException at the nextInt() line
Java chose the strict semantics: no silent skipping of non-ints
1e1 8 for nextInt()
InputMismatchException at the first nextInt()
1e1 is not an int literal, even though its value 10 fits in an int
1 2 3 for two nextInt()s
result from 1 and 2; the 3 is silently ignored
Java (like most languages) does not complain about unread input; you can query the buffer (not shown)
typing \n
read literally as \ and n
escape sequences only exist in source code; converting them is post-processing
The lecturer’s design point: lenient semantics (silently skipping or ignoring) are easier to program against but give the user no feedback. That is dangerous when data comes unobserved from files. Strict error messages are usually the better default, depending on the use case.
next() vs. nextLine() with name = name.toUpperCase(); and println(name + " has " + name.length() + " letters"):
Input
next()
nextLine()
Minnie
MINNIE has 6 letters
MINNIE has 6 letters
Minnie Mouse
MINNIE has 6 letters (only the first word)
MINNIE MOUSE has 12 letters (the space counts)
"Minnie Mouse"
"MINNIE has 7 letters (the quote is a character)
"MINNIE MOUSE" has 14 letters
nextLine() ignores types and reads everything until the end of the line. You can then tokenise the line yourself, e.g. comma-separated table rows, which comes later with files.
4.13 Random examples
Random rand = new Random();int face = 1 + rand.nextInt(6); // die: {1, …, 6}double r = 5.0 * rand.nextDouble(); // 0 <= r < 5double grade = 1.0 + r; // [1.0, 6.0): 6.0 is never reachedint even = 2 * rand.nextInt(5) + 4; // {0..4}, times 2 = {0, 2, …, 8}, plus 4 = {4, 6, …, 12}
Demo outputs: dice 1, 3, 6, 6, 5; grades like 3.x, 5.85, 1.x (“grades like CodeExpert produces; they must be rounded afterwards”); even numbers 10, 12, 8, 10. To include 6.0 for grades you would have to round, which changes the distribution.
5. Intuition & Conceptual Bridges
Intuition: EBNF → Java
Week 1’s formalism now defines a real language: identifiers, the program skeleton, expressions (<expr> ⟵ <term> { <op> <term> }), even Scanner tokens. As in week 1, EBNF covers only syntax. Types, keyword restrictions and meaning are additional rules. Expression trees are the arithmetic analogue of derivation trees; precedence and associativity decide which tree an unbracketed expression gets.
Intuition: Variables as named memory cells
Declaration reserves a cell, assignment writes, evaluation reads the current value. That is why x = x + 1 makes sense: it is a state change, not an equation. Methods are the analogue for code: a name for a sequence of statements, just as a variable is a name for a value.
Intuition: Types turn bits into meaning
The same bit pattern can be 97 or 'a'. Types tell the compiler which operations make sense, catching nonsense like 97.toUpperCase() before the program ever runs.
Intuition: Integers on a clock, doubles with gaps
int is a clock face with 232 positions: going past the top wraps to the bottom. double is a ruler with finitely many tick marks, dense near 0 and sparse far out. Results between ticks are rounded to the nearest one.
Connections:
Next weeks:
control structures: boolean, if/else, loops (where print becomes really useful);
methods with parameters and return values;
side effects (x++) that make the left-to-right evaluation order matter;
strings and arrays as sequences.
Later courses:
Digital Design: two’s complement, IEEE 754, rounding modes;
Numerical Methods: error propagation, stability;
Compiler Design: syntax trees, type checking;
Discrete Maths: modular arithmetic, randomness.
6. Summary & Self-Assessment Questions
Cheat sheet
Topic
Key facts
Program skeleton
public class Name { public static void main(String[] args) { … } } in Name.java
Errors
compile-time: no execution at all; runtime: the current run aborts (/ 0, InputMismatchException)
Naming
classes UpperCamelCase; methods and variables lowerCamelCase; identifiers start with a letter; no keywords
Comments
// line, /* block */
Escapes
\n newline, \t tab, \" quote, \\ backslash
Output
println adds \n; print doesn’t
Method call
jump to method, run the body linearly, jump back; definition order irrelevant
Variables
declare type name;; initialise/assign name = expr;; locals must be initialised before reading
Primitive types
int (32 bit), long (64 bit, suffix L), double (64-bit IEEE 754), char, boolean
/ on ints
truncates toward 0; / 0 is a runtime error
%
remainder, sign of the left operand; (a/b)⋅b+a%b=a
Precedence
cast and unary - > * / % > + - > =
Associativity
arithmetic left-assoc.; assignment and cast right-assoc.
Mixed types
int/long ⊗ double gives double, decided per operator
Cast
(type) expr: unary, binds tightest, affects only the right operand; narrowing needs it
Overflow
MAX + 1 = MIN (wraps around); the type never upgrades automatically
Doubles
finite, with gaps: 1.1 + 0.1 is 1.2000000000000002
Strings
+ with any String operand concatenates, left to right
(a) Left-associative: 5 / 2 first. Both are ints, so the result is 2. Then 2 * 2.0 = 4.0 (double).
(b) 5 * 2.0 = 10.0 (int × double gives double). Then 10.0 / 2 = 5.0 (double). Same numbers as (a), different order, different result.
(c) The parentheses force 5 / 2 = 2 first (integer division). The cast then gives 2.0 (double). The fraction was lost before the cast.
(d) The cast binds tighter than /, so (double) 5 = 5.0. Then 5.0 / 2 = 2.5 (double).
(e) Left to right:
1 + 2 = 3 (int addition)
3 + "3" = "33" (now a String)
"33" + 4 = "334"
"334" + 5 = "3345"
Result: "3345" (String). Once the left operand is a String, every following + concatenates.
Question 3. Find and explain every error, and classify each as compile-time or runtime.
public class Calc { public static void main(String[] args) { int a; int b = 0; System.out.println(a); int class = 5; System.out.println(10 / b) }}
Solution to Question 3
System.out.println(a);: a is declared but never initialised. Local primitive variables have no default value. Compile-time error: “variable a might not have been initialized”.
int class = 5;: class is a keyword and cannot be a variable name. Compile-time error.
System.out.println(10 / b): the ; is missing. Compile-time error: ”’;’ expected”.
If 1–3 were fixed, 10 / b with b == 0 would compile, since the compiler does not track values. At execution it throws ArithmeticException: / by zero: a runtime error.
Also, the file must be named Calc.java.
Question 4. Write a program that asks for a number of seconds (int) and prints it as hours, minutes and seconds. For example, 3725 gives 1 h 2 min 5 s. Use only / and %.
Solution to Question 4
Idea: 1 h = 3600 s and 1 min = 60 s. Integer division gives whole units; % gives what remains.
import java.util.Scanner;public class TimeSplitter { public static void main(String[] args) { Scanner console = new Scanner(System.in); System.out.print("Sekunden: "); int total = console.nextInt(); int hours = total / 3600; // whole hours int minutes = total % 3600 / 60; // remainder after hours, in whole minutes int seconds = total % 60; // remainder after whole minutes System.out.println(hours + " h " + minutes + " min " + seconds + " s"); }}
Check with 3725:
3725 / 3600 = 1
3725 % 3600 = 125. % and / have equal precedence and are left-associative, so this is (3725 % 3600) / 60 = 125 / 60 = 2, as intended.
3725 % 60 = 5, because 62⋅60=3720.
Output: 1 h 2 min 5 s ✓
In the output line, every + has a String on its left after hours + " h ", so the numbers are concatenated, not added.
Question 5. Give Java expressions using Random rand for: (a) a random int in [−5,5], (b) a random multiple of 10 in {10,20,…,100}, (c) a random double in [2.5,7.5).
Solution to Question 5
(a) Use min + nextInt(max - min + 1) with min=−5, max=5. That needs 11 values: -5 + rand.nextInt(11) gives {−5,…,5} ✓
(b) There are 10 values. rand.nextInt(10) gives {0..9}; add 1 to get {1..10}; multiply by 10: 10 * (1 + rand.nextInt(10)), or equivalently 10 + 10 * rand.nextInt(10).
(c) Use min + nextDouble() · (max − min): 2.5 + rand.nextDouble() * 5.0. nextDouble() ∈ [0,1); scaling gives [0,5.0); shifting gives [2.5,7.5) ✓. The upper bound 7.5 is excluded.