Project: A Student Report Card

What we'll build

A small report-card program. It reads students and their marks, then prints a report with each student's average and grade, the class topper, and how many students got each grade. It uses almost everything from this course.

Step 1: plan before coding

  1. A Student class holds a name and a list of marks, and can work out its own average and grade.
  2. main reads the input and creates the students. Bad marks are skipped with try/catch.
  3. Then we print the report: a loop for each student, a stream to find the topper, and a map to count grades.

Tip: Professional programmers plan in plain words first. Splitting a big problem into small steps is the most important programming skill of all.

Step 2: the whole program

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;
import java.util.TreeMap;

class Student {
    private final String name;
    private final List<Integer> marks = new ArrayList<>();

    Student(String name) {
        this.name = name;
    }

    String getName() { return name; }

    void addMark(int mark) {
        if (mark < 0 || mark > 100) {
            throw new IllegalArgumentException("mark out of range: " + mark);
        }
        marks.add(mark);
    }

    double average() {
        return marks.stream().mapToInt(m -> m).average().orElse(0);
    }

    char grade() {
        double avg = average();
        if (avg >= 90) return 'A';
        if (avg >= 75) return 'B';
        if (avg >= 50) return 'C';
        return 'F';
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int count = Integer.parseInt(sc.nextLine().trim());
        List<Student> students = new ArrayList<>();

        for (int i = 0; i < count; i++) {
            String[] parts = sc.nextLine().trim().split(" ");
            Student s = new Student(parts[0]);
            for (int j = 1; j < parts.length; j++) {
                try {
                    s.addMark(Integer.parseInt(parts[j]));
                } catch (IllegalArgumentException e) {
                    System.out.println("Skipping '" + parts[j] + "' for " + s.getName());
                }
            }
            students.add(s);
        }

        System.out.println("=== Report Card ===");
        TreeMap<Character, Integer> gradeCounts = new TreeMap<>();
        for (Student s : students) {
            System.out.printf("%-6s %6.1f  %c%n", s.getName(), s.average(), s.grade());
            gradeCounts.put(s.grade(), gradeCounts.getOrDefault(s.grade(), 0) + 1);
        }

        Student top = students.stream().max(Comparator.comparingDouble(Student::average)).get();
        System.out.println("Topper: " + top.getName());
        System.out.println("Grades: " + gradeCounts);
    }
}

Input (what the user types)

4
Asha 95 88 92
Ravi 70 65 abc 80
Meera 45 52 38
Arjun 88 79 150 91

Output

Skipping 'abc' for Ravi
Skipping '150' for Arjun
=== Report Card ===
Asha     91.7  A
Ravi     71.7  C
Meera    45.0  F
Arjun    86.0  B
Topper: Asha
Grades: {A=1, B=1, C=1, F=1}

Step 3: what each part uses

FeatureWhereModule
Classes, private fields, constructorStudent09
Throwing and catching exceptionsaddMark and the inner loop12
ArrayList and TreeMapmarks, students, gradeCounts13
Streams and a Comparatoraverage() and the topper14, 15
printf formattingthe report lines03

In %-6s the -6 means “at least 6 characters wide, lined up on the left”, and %6.1f means “6 wide with 1 decimal”. That's what makes the columns line up.

Make it your own

  • Add a highest() method to Student that returns their best mark.
  • Print the students sorted from the best average to the worst.
  • Print the class average at the end.
HOME LABS COMMUNITY DASHBOARD