Skip to content
This repository was archived by the owner on Nov 7, 2026. It is now read-only.

Commit d2e36b9

Browse files
committed
Array-Ausgabe wie in Java: Referenz statt Inhalt, Arrays.toString zeigt den Inhalt
System.out.println(zahlen) gibt in der Simulation und in Programmieren jetzt wie echtes Java eine Referenz wie [I@… aus. Arrays.toString und Arrays.deepToString zeigen den Inhalt; Arrays braucht import java.util.Arrays (Code-Export ergänzt ihn). Lektion 15 erklärt das in de/en/pt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ugz28VVb2pkKNmmHKgsfxu
1 parent f8b1065 commit d2e36b9

14 files changed

Lines changed: 1480 additions & 1379 deletions

‎web/lessons/de/15-arrays.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ for (int i = 0; i < zahlen.length; i++) {
166166
- Ein leeres Array mit 5 Fächern legst du mit `new int[5]` an.
167167
- `zahlen[i]` ist das Fach mit dem Index `i`, `zahlen.length` die Länge.
168168

169+
Gibst du das ganze Array aus, also `System.out.println(zahlen);`, zeigt Java nicht die Zahlen. Du siehst nur einen Text wie `[I@1b6d3586`. Das ist die **Referenz**, also die Stelle, an der das Array im Speicher liegt. Den Inhalt zeigt erst `Arrays.toString(zahlen)`, das ergibt `[2, 4, 6, 8, 10]`. Dafür schreibst du ganz oben `import java.util.Arrays;`.
170+
169171
::: übung l15-java-noten
170172
titel: Notensumme in Java
171173
code: java

‎web/lessons/en/15-arrays.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ for (int i = 0; i < numbers.length; i++) {
166166
- You create an empty array with 5 slots with `new int[5]`.
167167
- `numbers[i]` is the slot with index `i`, `numbers.length` is the length.
168168

169+
If you output the whole array, that is `System.out.println(numbers);`, Java does not show the numbers. You only see a text like `[I@1b6d3586`. This is the **reference**, the place where the array is stored in memory. `Arrays.toString(numbers)` shows the content: `[2, 4, 6, 8, 10]`. For this you write `import java.util.Arrays;` at the very top.
170+
169171
::: übung l15-java-noten
170172
titel: Sum of grades in Java
171173
code: java

‎web/lessons/pt/15-arrays.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ for (int i = 0; i < numeros.length; i++) {
166166
- Um array vazio com 5 posições cria-se com `new int[5]`.
167167
- `numeros[i]` é a posição com o índice `i`, `numeros.length` é o comprimento.
168168

169+
Se mostrares o array inteiro, ou seja `System.out.println(numeros);`, o Java não mostra os números. Só vês um texto como `[I@1b6d3586`. É a **referência**, o sítio onde o array está guardado na memória. O conteúdo só aparece com `Arrays.toString(numeros)`, que dá `[2, 4, 6, 8, 10]`. Para isso escreves `import java.util.Arrays;` logo no início.
170+
169171
::: übung l15-java-noten
170172
titel: Soma das notas em Java
171173
code: java

‎web/src/core/codeGenerator.ts‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,27 +1095,31 @@ function documentContainsJavaInput(doc: VSDocument): boolean {
10951095
return found
10961096
}
10971097

1098+
const arraysImport = 'import java.util.Arrays;'
1099+
10981100
/**
10991101
* Imports für Dateien: `Files`/`Path` aus `java.nio.file`, `File`, `PrintWriter`, `FileReader` und `BufferedReader` aus
1100-
* `java.io`, dazu `java.util.Scanner` für einen Scanner auf einer Datei.
1102+
* `java.io`, dazu `java.util.Scanner` für einen Scanner auf einer Datei und `java.util.Arrays` für `Arrays.toString(…)`.
11011103
*/
11021104
export function fileImports(doc: VSDocument): string[] {
11031105
let nio = false
11041106
let io = false
11051107
let scanner = false
1108+
let arrays = false
11061109
walkElements(doc.elements, (e) => {
11071110
for (const l of e.textLines) {
1111+
if (/\bArrays\s*\./.test(l)) arrays = true
11081112
if (/\bFiles\s*\./.test(l)) nio = true
11091113
if (/\bnew\s+(?:File|PrintWriter|FileReader|FileWriter|BufferedReader)\s*\(/.test(l)) io = true
11101114
if (/\bnew\s+Scanner\s*\(\s*new\s+File\b/.test(l)) scanner = true
11111115
}
11121116
})
1113-
return [...(io ? ['import java.io.*;'] : []), ...(nio ? ['import java.nio.file.*;'] : []), ...(scanner ? ['import java.util.Scanner;'] : [])]
1117+
return [...(io ? ['import java.io.*;'] : []), ...(nio ? ['import java.nio.file.*;'] : []), ...(scanner ? ['import java.util.Scanner;'] : []), ...(arrays ? [arraysImport] : [])]
11141118
}
11151119

11161120
/** Arbeitet das Programm mit Dateien? Dann kann eine IOException kommen, und Java verlangt `throws` an der Methode. */
11171121
export function documentReadsFiles(doc: VSDocument): boolean {
1118-
return fileImports(doc).length > 0
1122+
return fileImports(doc).some((i) => i !== arraysImport)
11191123
}
11201124

11211125
function documentContainsJavaMathRandom(doc: VSDocument): boolean {
@@ -1804,7 +1808,7 @@ export function generateJavaClassFile(
18041808
const scannerField = methods.length > 0 && (hasInput || methodInput)
18051809
// `Files.readString` kann eine IOException werfen; Java verlangt dann `throws` an der Methode.
18061810
const imports = [...new Set([document, ...methodDocs.map((m) => m.d)].flatMap(fileImports))]
1807-
const files = imports.length > 0
1811+
const files = imports.some((i) => i !== arraysImport)
18081812
if ((hasInput || methodInput) && !imports.includes('import java.util.Scanner;')) imports.push('import java.util.Scanner;')
18091813
for (const line of imports) out += line + '\n'
18101814
if (imports.length) out += '\n'

‎web/src/core/javaParser.ts‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,14 +227,15 @@ class Parser {
227227

228228
/**
229229
* Wie javac: `Scanner` ohne `import java.util.Scanner;` (oder `java.util.*`) ist ein Fehler, ebenso `Files` und `Path` ohne
230-
* `java.nio.file` und `File`, `PrintWriter`, `FileReader`, `FileWriter`, `BufferedReader` ohne `java.io`.
230+
* `java.nio.file`, `File`, `PrintWriter`, `FileReader`, `FileWriter`, `BufferedReader` ohne `java.io` und `Arrays` ohne
231+
* `java.util.Arrays`.
231232
*/
232233
checkImports() {
233234
const io = ['File', 'PrintWriter', 'FileReader', 'FileWriter', 'BufferedReader', 'IOException', 'FileNotFoundException'].map((n) => [n, 'java.io'])
234-
for (const [name, pkg] of [['Scanner', 'java.util'], ['Files', 'java.nio.file'], ['Path', 'java.nio.file'], ['Paths', 'java.nio.file'], ...io]) {
235+
for (const [name, pkg] of [['Scanner', 'java.util'], ['Arrays', 'java.util'], ['Files', 'java.nio.file'], ['Path', 'java.nio.file'], ['Paths', 'java.nio.file'], ...io]) {
235236
const known = this.imports.some((i) => i === `import${pkg}.${name};` || i === `import${pkg}.*;`)
236237
// `Files.readString(…)`: nur als Klasse vor einem Punkt, damit eine Variable `path` o. Ä. nicht zählt.
237-
const use = this.toks.find((t, k) => t.kind === 'id' && t.text === name && (pkg !== 'java.nio.file' || this.toks[k + 1]?.text === '.'))
238+
const use = this.toks.find((t, k) => t.kind === 'id' && t.text === name && (name !== 'Arrays' && pkg !== 'java.nio.file' || this.toks[k + 1]?.text === '.'))
238239
if (!known && use) {
239240
this.problem('web_parse_missing_import', use, pkg === 'java.util' ? `import ${pkg}.${name};` : `import ${pkg}.*;`)
240241
return

‎web/src/core/simulation.ts‎

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,38 @@ function elementDefault(type: SimValueType): number | string | boolean {
245245
return type === 'string' ? '' : type === 'boolean' ? false : 0
246246
}
247247

248+
/**
249+
* Was `System.out.println(zahlen)` in Java ausgibt: nicht den Inhalt, sondern Typ und Kennung der Referenz,
250+
* z. B. `[I@1b6d3586`. Den Inhalt zeigt erst `Arrays.toString(zahlen)`. Die Kennung bleibt für dasselbe Array gleich.
251+
*/
252+
const arrayIds = new WeakMap<object, string>()
253+
let nextArrayId = 0
254+
255+
export function javaReference(v: SimValue): string {
256+
const code = (t: SimValueType) => ({ int: 'I', double: 'D', boolean: 'Z', string: 'Ljava.lang.String;' })[t]
257+
const prefix =
258+
v.kind === 'grid' ? '[[' + code(v.elementType)
259+
: v.kind === 'intArray' ? '[I'
260+
: v.kind === 'doubleArray' ? '[D'
261+
: v.kind === 'booleanArray' ? '[Z'
262+
: v.kind === 'stringArray' ? '[Ljava.lang.String;'
263+
: undefined
264+
if (!prefix || !('value' in v)) return describeValue(v)
265+
const key = v.value as object
266+
let id = arrayIds.get(key)
267+
if (!id) {
268+
id = ((Math.imul(++nextArrayId, 0x9e3779b1) >>> 0) & 0x7fffffff).toString(16)
269+
arrayIds.set(key, id)
270+
}
271+
return prefix + '@' + id
272+
}
273+
274+
/** Ein Fach wurde geändert und das Array dabei kopiert: Es ist in Java aber dasselbe Array, also dieselbe Kennung. */
275+
function keepArrayId(from: object, to: object): void {
276+
const id = arrayIds.get(from)
277+
if (id) arrayIds.set(to, id)
278+
}
279+
248280
function isArrayValue(v: SimValue): boolean {
249281
return v.kind === 'intArray' || v.kind === 'doubleArray' || v.kind === 'stringArray' || v.kind === 'booleanArray' || v.kind === 'grid'
250282
}
@@ -1938,7 +1970,10 @@ export class SimulationEngine {
19381970
}
19391971
}
19401972

1941-
/** `Integer.parseInt(…)`, `Double.parseDouble(…)`, `String.valueOf(…)`, `Files.readString(Path.of(…))`. */
1973+
/**
1974+
* `Integer.parseInt(…)`, `Double.parseDouble(…)`, `String.valueOf(…)`, `Files.readString(Path.of(…))`,
1975+
* `Arrays.toString(…)` und `Arrays.deepToString(…)`.
1976+
*/
19421977
private evaluateStaticCall(text: string, call: { target: string; method: string; args: string[] }): SimValue | undefined {
19431978
const one = () => {
19441979
if (call.args.length !== 1) throw invalid(text)
@@ -1964,6 +1999,17 @@ export class SimulationEngine {
19641999
}
19652000
case 'String.valueOf':
19662001
return { kind: 'string', value: this.evaluateOutputExpression(one()) }
2002+
case 'Arrays.toString':
2003+
case 'Arrays.deepToString': {
2004+
const array = this.state.variables.get(one().trim())
2005+
if (!array || !isArrayValue(array)) throw invalid(text)
2006+
// `Arrays.toString` zeigt bei einem 2D-Array nur die Referenzen der Zeilen, `deepToString` alles.
2007+
if (array.kind === 'grid' && call.method === 'toString') {
2008+
const rows = array.value.map((row) => javaReference({ kind: `${array.elementType}Array`, value: row } as SimValue))
2009+
return { kind: 'string', value: '[' + rows.join(', ') + ']' }
2010+
}
2011+
return { kind: 'string', value: describeValue(array) }
2012+
}
19672013
case 'Files.readString': {
19682014
const path = /^(?:Path\s*\.\s*of|Paths\s*\.\s*get)\s*\(\s*("(?:[^"\\]|\\.)*")\s*\)$/u.exec(one().trim())
19692015
if (!path) throw invalid(text)
@@ -2076,6 +2122,7 @@ export class SimulationEngine {
20762122
if (raw === undefined || isArrayValue(value)) throw invalid(trimmed)
20772123
const rows = old.value.map((row) => [...row])
20782124
rows[indices[0]][indices[1]] = raw as number | string | boolean
2125+
keepArrayId(old.value, rows)
20792126
this.state.variables.set(access.arrayName, { ...old, value: rows })
20802127
return
20812128
}
@@ -2095,6 +2142,7 @@ export class SimulationEngine {
20952142
}
20962143
if (index >= (updated.value as unknown[]).length) throw invalid(trimmed)
20972144
;(updated.value as unknown[])[index] = value.value
2145+
keepArrayId(old.value, updated.value)
20982146
this.state.variables.set(access.arrayName, updated)
20992147
}
21002148

@@ -2122,7 +2170,8 @@ export class SimulationEngine {
21222170
else sum = sum.kind === 'int' && value.kind === 'int' ? int(sum.value + value.value) : dbl((sum as { value: number }).value + (value as { value: number }).value)
21232171
continue
21242172
}
2125-
text = (text ?? (sum ? describeValue(sum) : '')) + describeValue(value)
2173+
// Ein ganzes Array gibt Java nur als Referenz aus, z. B. `[I@1b6d3586`.
2174+
text = (text ?? (sum ? describeValue(sum) : '')) + javaReference(value)
21262175
sum = undefined
21272176
}
21282177
return text ?? (sum ? describeValue(sum) : '')

‎web/test/javaParser.test.ts‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,13 @@ describe('Java tippen → Struktogramm', () => {
214214
expect(ask(fach + 'note[i] = scanner.nextInt();').request?.prompt).toBe('note[i]:')
215215
})
216216

217+
it('Arrays ohne import ist ein Fehler, wie bei javac', () => {
218+
const keys = (src: string) => parse(src).problems.map((p) => p.key)
219+
expect(keys('int[] a = {1, 2};\nSystem.out.println(Arrays.toString(a));')).toEqual(['web_parse_missing_import'])
220+
expect(keys('import java.util.Arrays;\nint[] a = {1, 2};\nSystem.out.println(Arrays.toString(a));')).toEqual([])
221+
expect(keys('int[] a = {1, 2};\nSystem.out.println(a);')).toEqual([])
222+
})
223+
217224
it('Aufruf eines Unterprogramms aus einem anderen Reiter', () => {
218225
expect(parse('int y = quadrat(4);', new Set(['quadrat'])).elements[0].type).toBe(ElementType.aufruf)
219226
expect(parse('int y = quadrat(4);').elements[0].type).toBe(ElementType.anweisung)

‎web/test/simulation.test.ts‎

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,14 +173,42 @@ describe('Simulation', () => {
173173
expect(engine.message).toBe('Simulation finished.')
174174
})
175175

176+
it('gibt ein ganzes Array wie Java als Referenz aus, den Inhalt erst mit Arrays.toString', () => {
177+
const state = runSimulation(
178+
doc([
179+
stmt('int[] zahlenreihe = {1, 2, 3}'),
180+
stmt('String[] namen = {"Ada", "Bob"}'),
181+
stmt('double[][] t = new double[2][2]'),
182+
stmt('output: zahlenreihe'),
183+
stmt('zahlenreihe[0] = 9'),
184+
stmt('output: "Reihe: " + zahlenreihe'),
185+
stmt('output: namen'),
186+
stmt('output: t'),
187+
stmt('output: Arrays.toString(zahlenreihe)'),
188+
stmt('output: Arrays.toString(namen)'),
189+
stmt('output: Arrays.deepToString(t)'),
190+
stmt('output: Arrays.toString(t)'),
191+
]),
192+
)
193+
const [ref, refAgain, names, grid, ...rest] = state.outputLines
194+
expect(ref).toMatch(/^\[I@[0-9a-f]+$/u)
195+
expect(refAgain).toBe('Reihe: ' + ref)
196+
expect(names).toMatch(/^\[Ljava\.lang\.String;@[0-9a-f]+$/u)
197+
expect(grid).toMatch(/^\[\[D@[0-9a-f]+$/u)
198+
expect(rest.slice(0, 3)).toEqual(['[9, 2, 3]', '[Ada, Bob]', '[[0.0, 0.0], [0.0, 0.0]]'])
199+
expect(rest[3]).toMatch(/^\[\[D@[0-9a-f]+, \[D@[0-9a-f]+\]$/u)
200+
// Die Variablentabelle zeigt weiter den Inhalt.
201+
expect(state.variables.get('zahlenreihe')).toEqual({ kind: 'intArray', value: [9, 2, 3] })
202+
})
203+
176204
it('Werte und Arrays', () => {
177205
const state = runSimulation(
178206
doc([
179207
stmt('double d = 7.0 / 2'),
180208
stmt('int[] a = {3, 4, 5}'),
181209
stmt('a[1] = a[0] * 10'),
182210
stmt('String s = "x"'),
183-
stmt('print: "len=" + a.length + " a=" + a + " d=" + d'),
211+
stmt('print: "len=" + a.length + " a=" + Arrays.toString(a) + " d=" + d'),
184212
stmt('d += 1'),
185213
]),
186214
)

0 commit comments

Comments
 (0)