☕ Java

Java Class Attributes — Syntax, Types, Examples & Best Practices

Everything you need to know about Java Class Attributes — field declaration, instance vs static attributes, final fields, default values, access modifiers, naming conventions, encapsulation with getters/setters, and real-world production code examples.

📅

Last Updated

March 2026

⏱️

Read Time

20 min

🎯

Level

Beginner

🏷️

Chapter

14 of 35

What are Class Attributes in Java?

In Java, class attributes (also called fields or instance variables) are variables declared directly inside the class body — outside any method, constructor, or block. They define the state of an object — the data each object holds and carries throughout its lifetime. Every object created from a class gets its own separate copy of the class's instance attributes, holding its own unique values.

Real-world analogy: Think of a class as a form template. The blank fields on the form — Name, Age, Address, Phone — are like class attributes. When a person fills out the form (creates an object), their specific answers become the attribute values for that object. Two people filling the same form get the same attribute names, but their own unique values.

Java supports multiple types of class attributes: instance attributes (unique per object), static attributes (shared across all objects), and final attributes (constant, assigned once). Together with methods, attributes form the complete definition of what a class represents. Properly designed attributes — private, well-named, validated — are the hallmark of professional Java code.

Attribute Declaration — Syntax & Structure

A class attribute declaration consists of an optional access modifier, optional non-access modifiers (static, final), a data type, and the attribute name. By convention, attribute names use camelCase (e.g., firstName, accountBalance). Attributes are declared at the class level — directly inside the class body, not inside any method or constructor.

📌
Attribute Syntax

[access_modifier] [static] [final] dataType attributeName [= initialValue]; Examples: private String name; private int age = 0; public static int totalCount = 0; private static final double TAX_RATE = 0.18; protected boolean isActive = true;

📋
Attribute Rules

1. Declared inside class body, outside all methods. 2. Names use camelCase — firstName, accountBalance. 3. Constants (static final) use UPPER_SNAKE_CASE — MAX_SIZE, TAX_RATE. 4. Should be private for encapsulation (best practice). 5. Java assigns default values if not explicitly initialized. 6. Multiple attributes of same type can be declared in one line (not recommended for readability).

🔁
Attribute vs Local Variable

Attribute: declared in class body; has default value; accessible throughout all methods of the class; lives as long as the object lives. Local variable: declared inside a method/block; NO default value (must initialize before use); accessible only within that method/block; dies when method returns.

☕ JavaAttributeDeclaration.java
// ✅ Well-structured Java class showing all attribute types
public class Employee {

    // ── Instance attributes (each object gets its own copy) ──
    private String  employeeId;
    private String  name;
    private String  department;
    private double  salary;
    private int     age;
    private boolean isActive;

    // ── Static attribute (ONE shared copy for ALL Employee objects) ──
    private static int totalEmployees = 0;

    // ── Static final attribute (constant — never changes) ──
    public static final double MIN_WAGE      = 15000.0;
    public static final int    RETIREMENT_AGE = 60;
    public static final String COMPANY_NAME  = "Tech Sustainify";

    // ── Instance final attribute (set once in constructor, never changed) ──
    private final String panNumber; // PAN card — immutable identity

    // ── Constructor initializes instance attributes ──
    public Employee(String employeeId, String name, String department,
                    double salary, int age, String panNumber) {
        this.employeeId  = employeeId;
        this.name        = name;
        this.department  = department;
        this.salary      = salary;
        this.age         = age;
        this.isActive    = true;   // default active on creation
        this.panNumber   = panNumber; // final — set once here
        totalEmployees++;           // static — increments class-wide counter
    }

    // ── Display all attributes ──
    public void display() {
        System.out.println("ID       : " + employeeId);
        System.out.println("Name     : " + name);
        System.out.println("Dept     : " + department);
        System.out.printf( "Salary   : ₹%.2f%n", salary);
        System.out.println("Age      : " + age);
        System.out.println("Active   : " + isActive);
        System.out.println("PAN      : " + panNumber);
        System.out.println("Company  : " + COMPANY_NAME);
    }

    public static int getTotalEmployees() { return totalEmployees; }

    public static void main(String[] args) {
        Employee e1 = new Employee("E001", "Aarav Shah",  "Engineering", 75000, 28, "ABCDE1234F");
        Employee e2 = new Employee("E002", "Priya Mehta", "Marketing",   55000, 32, "FGHIJ5678K");

        e1.display();
        System.out.println("---");
        e2.display();
        System.out.println("Total Employees: " + Employee.getTotalEmployees()); // 2
    }
}

Instance vs Static Attributes — Object-Level vs Class-Level

The most important distinction in class attributes is between instance attributes and static attributes. Instance attributes represent the unique state of each individual object — every object has its own copy. Static attributes belong to the class itself — there is only ONE copy shared by all objects. Confusing these two is a very common source of subtle bugs in Java programs.

AspectInstance AttributeStatic Attribute
KeywordNone (no special keyword)static
Belongs toEach individual objectThe class itself
MemorySeparate copy per object (heap)Single shared copy (method area)
Access viaObject reference: obj.attributeNameClass name: ClassName.attributeName
Created whenEach time 'new' is calledWhen class is first loaded by JVM
Destroyed whenObject is garbage collectedWhen class is unloaded (program ends)
Typical usename, age, salary, color — unique per objecttotalCount, TAX_RATE, MAX_SIZE — shared
Thread safetySafer — each thread's object is independentRisky — all threads share same copy
☕ JavaInstanceVsStatic.java
public class BankAccount {

    // ── Instance attributes — UNIQUE per object ──
    private String accountNumber;
    private String holderName;
    private double balance;

    // ── Static attribute — SHARED across ALL BankAccount objects ──
    private static int    totalAccounts    = 0;
    private static double totalDepositsAll = 0.0;

    // ── Static constant ──
    public static final double MIN_BALANCE = 500.0;

    public BankAccount(String accountNumber, String holderName, double initialDeposit) {
        this.accountNumber = accountNumber;
        this.holderName    = holderName;
        this.balance       = initialDeposit;
        totalAccounts++;                  // shared counter goes up for every new account
        totalDepositsAll += initialDeposit; // shared total increases
    }

    public void deposit(double amount) {
        balance          += amount; // only THIS object's balance changes
        totalDepositsAll += amount; // shared class-wide total also changes
    }

    public void showBalance() {
        System.out.printf("%s (%s) → Balance: ₹%.2f%n",
                          holderName, accountNumber, balance);
    }

    // ── Static method to access static attributes ──
    public static void showBankSummary() {
        System.out.println("Total Accounts      : " + totalAccounts);
        System.out.printf( "Total Deposits (All): ₹%.2f%n", totalDepositsAll);
    }

    public static void main(String[] args) {
        BankAccount acc1 = new BankAccount("ACC001", "Aarav",  10000.0);
        BankAccount acc2 = new BankAccount("ACC002", "Priya",  25000.0);
        BankAccount acc3 = new BankAccount("ACC003", "Rohan",  5000.0);

        acc1.deposit(3000.0);
        acc2.deposit(7000.0);

        acc1.showBalance(); // Aarav  (ACC001) → Balance: ₹13000.00
        acc2.showBalance(); // Priya  (ACC002) → Balance: ₹32000.00
        acc3.showBalance(); // Rohan  (ACC003) → Balance: ₹5000.00

        System.out.println("Min Balance Required: ₹" + BankAccount.MIN_BALANCE);
        BankAccount.showBankSummary();
        // Total Accounts      : 3
        // Total Deposits (All): ₹50000.00
    }
}

Final Attributes — Constants & Immutable Fields

The final keyword on an attribute means its value can be assigned only once — after that, any attempt to reassign it causes a compile error. Final attributes communicate a clear design intent: this value is fixed and should never change. Java uses two flavors: static final for class-level constants, and instance final for object-level immutable fields.

🏛️
Static Final (Constants)

Declared as 'public static final dataType NAME = value;'. Belong to the class — one copy shared by all objects. By convention: UPPER_SNAKE_CASE names. Examples: MAX_SPEED, TAX_RATE, PI, DB_URL. Must be initialized at declaration. Accessible without creating any object: ClassName.CONSTANT_NAME. These replace magic numbers in code — 0.18 becomes TAX_RATE everywhere, making code readable and easy to update.

🔒
Instance Final (Immutable Fields)

Declared as 'private final dataType fieldName;'. Each object has its own copy, but that copy cannot change after being set. Must be initialized either at declaration or inside the constructor — not in a method. Common for: ID fields, PAN numbers, timestamps, identity values that should never change after object creation. Makes objects more predictable and thread-safe.

⚠️
Final Reference Caution

When a final attribute is an object reference (e.g., final List<String> items), the REFERENCE is constant — you can't reassign 'items' to a different list. But the object it points to CAN still be mutated: items.add("new item") is allowed! To make the collection truly immutable, use Collections.unmodifiableList() or List.of() from Java 9+. Final only locks the reference, not the object state.

☕ JavaFinalAttributes.java
import java.time.LocalDate;

public class CreditCard {

    // ── Static final constants (class-level, shared, UPPER_SNAKE_CASE) ──
    public  static final double MAX_CREDIT_LIMIT  = 500000.0;
    public  static final double LATE_FEE_RATE     = 0.02;    // 2%
    private static final int    CVV_LENGTH         = 3;
    public  static final String NETWORK            = "VISA";

    // ── Instance final attributes (per-object, immutable after construction) ──
    private final String    cardNumber;   // set once — never changes
    private final String    holderName;   // card holder name is permanent
    private final LocalDate issueDate;    // date of issue — fixed forever
    private final LocalDate expiryDate;

    // ── Regular instance attributes (mutable) ──
    private double  creditLimit;
    private double  outstandingBalance;
    private boolean isBlocked;

    public CreditCard(String cardNumber, String holderName, double creditLimit) {
        if (creditLimit > MAX_CREDIT_LIMIT)
            throw new IllegalArgumentException("Credit limit exceeds maximum allowed");

        this.cardNumber          = cardNumber;   // ✅ final — assigned in constructor
        this.holderName          = holderName;   // ✅ final — assigned in constructor
        this.issueDate           = LocalDate.now();
        this.expiryDate          = LocalDate.now().plusYears(5);
        this.creditLimit         = creditLimit;
        this.outstandingBalance  = 0.0;
        this.isBlocked           = false;
    }

    public void spend(double amount) {
        if (isBlocked) { System.out.println("Card is blocked."); return; }
        if (outstandingBalance + amount > creditLimit) {
            System.out.println("Credit limit exceeded."); return;
        }
        outstandingBalance += amount;
        System.out.printf("Spent ₹%.2f | Outstanding: ₹%.2f%n", amount, outstandingBalance);
    }

    public void display() {
        System.out.println("Card    : " + cardNumber);
        System.out.println("Holder  : " + holderName);
        System.out.println("Network : " + NETWORK);
        System.out.println("Expiry  : " + expiryDate);
        System.out.printf( "Limit   : ₹%.2f%n", creditLimit);
        System.out.printf( "Due     : ₹%.2f%n", outstandingBalance);
    }

    public static void main(String[] args) {
        CreditCard card = new CreditCard("4111-1111-1111-1111", "Aarav Shah", 100000.0);
        card.display();
        card.spend(15000.0);
        card.spend(90000.0); // exceeds limit

        // card.cardNumber = "NEW-NUMBER"; // ❌ COMPILE ERROR — final field
        System.out.println("Max Limit Allowed: ₹" + CreditCard.MAX_CREDIT_LIMIT);
    }
}

Default Values of Class Attributes

Java automatically initializes class attributes (fields) to default values if you do not explicitly assign them. This is one key difference between class attributes and local variables — local variables inside methods have NO default values and MUST be initialized before use. Understanding default values prevents bugs where you assume a field is set when it has simply been left at its default.

Data TypeDefault ValueExample DeclarationNotes
byte0byte count;8-bit integer default
short0short quantity;16-bit integer default
int0int age;Most common integer type
long0Llong population;64-bit integer default
float0.0ffloat temperature;32-bit floating point
double0.0double salary;64-bit floating point
char'\u0000'char grade;Null character — not '0'
booleanfalseboolean isActive;Always false by default
Object / StringnullString name;No object — be careful of NPE
Arraynullint[] scores;Array reference is null
☕ JavaDefaultValues.java
public class DefaultValues {

    // ── All attributes left uninitialized — Java applies defaults ──
    byte    byteVal;
    short   shortVal;
    int     intVal;
    long    longVal;
    float   floatVal;
    double  doubleVal;
    char    charVal;
    boolean boolVal;
    String  stringVal;   // Object reference — defaults to null
    int[]   arrayVal;    // Array reference — defaults to null

    public void printDefaults() {
        System.out.println("byte    : " + byteVal);    // 0
        System.out.println("short   : " + shortVal);   // 0
        System.out.println("int     : " + intVal);     // 0
        System.out.println("long    : " + longVal);    // 0
        System.out.println("float   : " + floatVal);   // 0.0
        System.out.println("double  : " + doubleVal);  // 0.0
        System.out.println("char    : [" + charVal + "]"); // [] — invisible null char
        System.out.println("boolean : " + boolVal);   // false
        System.out.println("String  : " + stringVal); // null
        System.out.println("int[]   : " + arrayVal);  // null
    }

    public static void main(String[] args) {
        DefaultValues obj = new DefaultValues();
        obj.printDefaults();

        // ⚠️ Accessing method on null reference — NullPointerException!
        // System.out.println(obj.stringVal.length()); // ❌ NPE

        // ✅ Always check before using object references
        if (obj.stringVal != null) {
            System.out.println(obj.stringVal.length());
        }

        // ── Local variable — NO default value ──
        int localVar;
        // System.out.println(localVar); // ❌ COMPILE ERROR: variable not initialized
        int localVar2 = 0;               // ✅ Must initialize explicitly
        System.out.println(localVar2);   // 0
    }
}

Access Modifiers for Attributes — Controlling Visibility

Access modifiers control which parts of the program can read or modify a class attribute. Choosing the correct access level for each attribute is one of the most important design decisions in Java. The golden rule: always use the most restrictive access level that still meets your needs. For class attributes, this almost always means private.

ModifierSame ClassSame PackageSubclass (other pkg)Other PackageRecommended For
private✅ Yes❌ No❌ No❌ NoAll instance fields (encapsulation)
default✅ Yes✅ Yes❌ No❌ NoPackage-internal utilities (rare for fields)
protected✅ Yes✅ Yes✅ Yes❌ NoFields needed by subclasses (inheritance)
public✅ Yes✅ Yes✅ Yes✅ YesConstants only (public static final)
☕ JavaAttributeAccessModifiers.java
public class Student {

    // ✅ private — most common for instance attributes
    private String name;
    private int    age;
    private double gpa;

    // ✅ protected — accessible in subclasses (use carefully)
    protected String enrollmentNumber;

    // ✅ public static final — constants are fine as public
    public static final int    MAX_CREDITS = 180;
    public static final double MAX_GPA     = 10.0;

    // ❌ public instance field — BAD PRACTICE (avoid this)
    // public String address; — anyone can set student.address = null

    public Student(String name, int age, double gpa, String enrollmentNumber) {
        this.name             = name;
        this.age              = age;
        this.gpa              = gpa;
        this.enrollmentNumber = enrollmentNumber;
    }

    // ── Public getters — controlled read access ──
    public String getName()           { return name;             }
    public int    getAge()            { return age;              }
    public double getGpa()            { return gpa;              }
    public String getEnrollmentNumber() { return enrollmentNumber; }

    // ── Public setters with validation — controlled write access ──
    public void setName(String name) {
        if (name == null || name.isBlank())
            throw new IllegalArgumentException("Name cannot be blank");
        this.name = name.trim();
    }

    public void setAge(int age) {
        if (age < 15 || age > 60)
            throw new IllegalArgumentException("Invalid student age: " + age);
        this.age = age;
    }

    public void setGpa(double gpa) {
        if (gpa < 0.0 || gpa > MAX_GPA)
            throw new IllegalArgumentException("GPA must be between 0 and " + MAX_GPA);
        this.gpa = gpa;
    }

    @Override
    public String toString() {
        return String.format("Student{name='%s', age=%d, gpa=%.1f, enroll='%s'}",
                             name, age, gpa, enrollmentNumber);
    }
}

Naming Conventions & Best Practices for Attributes

Good attribute naming makes code self-documenting — a reader should understand what data an attribute holds just from its name, without any comments. Java has well-established naming conventions that all professional developers follow. Violating these conventions doesn't break compilation, but it makes your code look unprofessional and harder to maintain.

camelCase for Instance Attributes

All instance and static (non-final) attributes use camelCase — start with lowercase, capitalize each new word. Good: firstName, accountBalance, isActive, totalScore, lastLoginDate. Bad: FirstName, first_name, FIRSTNAME, firstname. Boolean attributes often start with 'is', 'has', or 'can': isActive, hasPermission, canEdit.

🏛️
UPPER_SNAKE_CASE for Constants

All static final constants use UPPER_SNAKE_CASE — all caps with underscores between words. Good: MAX_SIZE, TAX_RATE, DEFAULT_TIMEOUT, PI, MIN_AGE. Bad: maxSize, Maxsize, maxsize. This makes constants instantly recognizable in code — you immediately know it's a fixed value.

📝
Meaningful, Specific Names

Names should be descriptive and specific — not abbreviations or single letters (except in loops). Good: customerName, orderDate, monthlyInterestRate, maximumRetryCount. Bad: n, temp, x, dat, mIR, mRC. Exception: universally understood short names like 'id', 'url', 'pi' are acceptable. Aim for the longest name that is still clear and not redundant.

🚫
Names to Avoid

Never use Java reserved keywords: class, int, for, if, etc. Avoid type encoding: stringName, intAge (Hungarian notation — outdated). Avoid redundant class name: Student.studentName → just Student.name. Avoid confusing negatives: notActive, isNotBlocked — prefer positive forms: isActive, isBlocked. Avoid single-character names outside loops — 'a', 'x', 'b' are meaningless as field names.

☕ JavaNamingConventions.java
// ❌ BAD naming — unprofessional, confusing
public class BadNaming {
    public String N;           // what is N?
    private int A;             // what is A?
    String first_name;         // snake_case — wrong for Java
    protected Boolean IsActive; // PascalCase — wrong for field
    public static final int maxsize = 100; // constant should be UPPER_SNAKE_CASE
    private String stringName;  // type prefix — unnecessary
    private boolean notBlocked; // confusing negative boolean
}

// ✅ GOOD naming — clean, readable, self-documenting
public class ProductInventory {

    // ── Constants: UPPER_SNAKE_CASE ──
    public  static final int    MAX_STOCK_QUANTITY  = 10000;
    public  static final double GST_RATE            = 0.18;
    private static final String DEFAULT_CATEGORY    = "General";

    // ── Static mutable: camelCase ──
    private static int totalProductsListed = 0;

    // ── Instance attributes: camelCase, descriptive ──
    private String  productId;
    private String  productName;
    private String  category;
    private double  unitPrice;
    private int     stockQuantity;
    private boolean isAvailable;      // boolean: starts with 'is'
    private boolean hasFreeShipping;  // boolean: starts with 'has'
    private String  manufacturerName;

    // ── Final instance: camelCase ──
    private final String sku;  // Stock Keeping Unit — never changes

    public ProductInventory(String productId, String productName,
                            double unitPrice, int stockQuantity, String sku) {
        this.productId      = productId;
        this.productName    = productName;
        this.category       = DEFAULT_CATEGORY;
        this.unitPrice      = unitPrice;
        this.stockQuantity  = stockQuantity;
        this.isAvailable    = stockQuantity > 0;
        this.hasFreeShipping = unitPrice > 999.0;
        this.manufacturerName = "Unknown";
        this.sku            = sku;
        totalProductsListed++;
    }

    @Override
    public String toString() {
        return String.format("Product{id='%s', name='%s', price=₹%.2f, stock=%d}",
                             productId, productName, unitPrice, stockQuantity);
    }
}

Encapsulation — Getters, Setters & Protecting Attributes

Encapsulation of attributes means keeping fields private and providing controlled access through public getter and setter methods. Getters allow reading a field's value; setters allow updating it — with validation. This gives the class full control over its own data: invalid values can never be assigned, and internal representation can change without affecting external code.

☕ JavaEncapsulatedAttributes.java
public class PatientRecord {

    // ── All attributes private — encapsulated ──
    private String  patientId;
    private String  fullName;
    private int     age;
    private double  weight;       // in kg
    private double  height;       // in cm
    private String  bloodGroup;
    private boolean isDiabetic;

    private static final double MIN_WEIGHT = 1.0;
    private static final double MAX_WEIGHT = 500.0;
    private static final double MIN_HEIGHT = 30.0;
    private static final double MAX_HEIGHT = 300.0;

    public PatientRecord(String patientId, String fullName, int age,
                         double weight, double height, String bloodGroup) {
        this.patientId  = patientId;
        setFullName(fullName);    // setter validates even in constructor
        setAge(age);
        setWeight(weight);
        setHeight(height);
        setBloodGroup(bloodGroup);
        this.isDiabetic = false;
    }

    // ── Getters — read-only access ──
    public String  getPatientId()  { return patientId;  }
    public String  getFullName()   { return fullName;   }
    public int     getAge()        { return age;        }
    public double  getWeight()     { return weight;     }
    public double  getHeight()     { return height;     }
    public String  getBloodGroup() { return bloodGroup; }
    public boolean isDiabetic()    { return isDiabetic; }  // boolean: isDiabetic()

    // ── Setters with validation ──
    public void setFullName(String fullName) {
        if (fullName == null || fullName.isBlank())
            throw new IllegalArgumentException("Patient name cannot be blank");
        this.fullName = fullName.trim();
    }

    public void setAge(int age) {
        if (age < 0 || age > 150)
            throw new IllegalArgumentException("Invalid age: " + age);
        this.age = age;
    }

    public void setWeight(double weight) {
        if (weight < MIN_WEIGHT || weight > MAX_WEIGHT)
            throw new IllegalArgumentException(
                String.format("Weight must be between %.1f and %.1f kg", MIN_WEIGHT, MAX_WEIGHT));
        this.weight = weight;
    }

    public void setHeight(double height) {
        if (height < MIN_HEIGHT || height > MAX_HEIGHT)
            throw new IllegalArgumentException(
                String.format("Height must be between %.1f and %.1f cm", MIN_HEIGHT, MAX_HEIGHT));
        this.height = height;
    }

    public void setBloodGroup(String bloodGroup) {
        java.util.Set<String> valid = java.util.Set.of(
            "A+","A-","B+","B-","AB+","AB-","O+","O-");
        if (!valid.contains(bloodGroup))
            throw new IllegalArgumentException("Invalid blood group: " + bloodGroup);
        this.bloodGroup = bloodGroup;
    }

    public void setDiabetic(boolean isDiabetic) {
        this.isDiabetic = isDiabetic;
    }

    // ── Computed attribute (derived from other fields) ──
    public double getBmi() {
        double heightInMetres = height / 100.0;
        return weight / (heightInMetres * heightInMetres);
    }

    @Override
    public String toString() {
        return String.format(
            "Patient{id='%s', name='%s', age=%d, blood='%s', BMI=%.1f}",
            patientId, fullName, age, bloodGroup, getBmi());
    }

    public static void main(String[] args) {
        PatientRecord p = new PatientRecord("P001", "Aarav Shah", 35, 72.5, 175.0, "B+");
        System.out.println(p);
        System.out.println("BMI: " + String.format("%.2f", p.getBmi()));

        p.setWeight(75.0);  // ✅ valid update
        // p.setAge(-5);   // ❌ IllegalArgumentException — invalid age
        // p.setBloodGroup("XY+"); // ❌ IllegalArgumentException — invalid group
    }
}

Attribute Initialization Techniques

Java provides four ways to initialize class attributes. Choosing the right technique depends on whether the value is fixed, computed, or passed in at object creation time. Proper initialization prevents null surprises and ensures objects are always in a valid state from the moment they are created.

1️⃣
Inline Initialization (at Declaration)

Assign a value directly where the field is declared: 'private int age = 0;' or 'private String status = "ACTIVE";'. Best for: fixed default values that are the same for every object. Runs before the constructor. Simple and readable. Cannot depend on constructor parameters.

2️⃣
Constructor Initialization

Assign values inside the constructor body using 'this.field = value;'. Best for: values that differ per object (passed as constructor parameters) or computed from constructor arguments. Most common initialization technique. Allows validation before assignment.

3️⃣
Instance Initializer Block

A code block inside the class but outside any method — '{ this.createdAt = LocalDateTime.now(); }'. Runs before every constructor call. Useful when multiple constructors share common initialization logic. Less common — constructor chaining with this() is usually preferred.

4️⃣
Static Initializer Block

A static { ... } block that runs once when the class is first loaded by JVM. Used for complex static field initialization that can't be done in a single expression: loading config files, building lookup maps, computing large constants. Example: static { TAX_TABLE = loadTaxConfig(); }

☕ JavaAttributeInitialization.java
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

public class Order {

    // ── Technique 1: Inline initialization ──
    private String  status      = "PENDING";     // fixed default for every order
    private int     retryCount  = 0;
    private boolean isPriority  = false;

    // ── Technique 3: Instance initializer block ──
    private LocalDateTime createdAt;
    private String        orderId;
    {
        // Runs before EVERY constructor
        createdAt = LocalDateTime.now();
        orderId   = "ORD-" + System.currentTimeMillis();
    }

    // ── Technique 4: Static initializer block ──
    private static final Map<String, Double> TAX_BY_CATEGORY;
    static {
        // Complex initialization — cannot be done in single expression
        TAX_BY_CATEGORY = new HashMap<>();
        TAX_BY_CATEGORY.put("Electronics", 0.18);
        TAX_BY_CATEGORY.put("Clothing",    0.05);
        TAX_BY_CATEGORY.put("Food",        0.00);
        TAX_BY_CATEGORY.put("Furniture",   0.12);
        System.out.println("Tax table loaded.");
    }

    // ── Technique 2: Constructor initialization ──
    private String customerId;
    private double totalAmount;
    private String category;

    public Order(String customerId, double totalAmount, String category) {
        // orderId and createdAt already set by instance initializer above
        this.customerId   = customerId;
        this.totalAmount  = totalAmount;
        this.category     = category;
    }

    public double getTaxAmount() {
        double rate = TAX_BY_CATEGORY.getOrDefault(category, 0.18);
        return totalAmount * rate;
    }

    @Override
    public String toString() {
        return String.format(
            "Order{id='%s', customer='%s', amount=₹%.2f, tax=₹%.2f, status='%s'}",
            orderId, customerId, totalAmount, getTaxAmount(), status);
    }

    public static void main(String[] args) {
        Order o1 = new Order("C001", 50000.0, "Electronics");
        Order o2 = new Order("C002", 2000.0,  "Clothing");
        Order o3 = new Order("C003", 500.0,   "Food");

        System.out.println(o1);
        System.out.println(o2);
        System.out.println(o3);
    }
}

Common Mistakes & Pitfalls — Attribute Bugs That Fool Everyone

These attribute-related mistakes appear repeatedly in beginner Java code. Each one compiles successfully (or fails at runtime) in a way that surprises new developers. Study them carefully.

☕ JavaAttributeMistakes.java
// ❌ MISTAKE 1: Shadowing — parameter hides field, 'this' forgotten
public class Box {
    private int width;
    private int height;
    public Box(int width, int height) {
        width  = width;   // ❌ Assigns parameter to itself — field stays 0
        height = height;  // ❌ Same bug
        // Fix: this.width = width; this.height = height;
    }
}

// ❌ MISTAKE 2: Accessing static attribute via object reference
public class Counter {
    public static int count = 0;
    public static void main(String[] args) {
        Counter c = new Counter();
        c.count = 10;    // ❌ Misleading — looks like instance access
                         // Actually modifies the class-level static field
        // Fix: Counter.count = 10; — use class name for static attributes
    }
}

// ❌ MISTAKE 3: Assuming all variables get default values
public class DefaultMistake {
    private int fieldVar;  // ✅ gets default 0 — class attribute
    public void method() {
        int localVar;      // ❌ NO default value — local variable
        // System.out.println(localVar); // COMPILE ERROR: variable not initialized
        System.out.println(fieldVar);    // ✅ OK — prints 0
    }
}

// ❌ MISTAKE 4: Reassigning a final attribute
public class FinalMistake {
    private final String id = "ABC";
    public void resetId() {
        // id = "NEW"; // ❌ COMPILE ERROR: cannot assign value to final variable
    }
}

// ❌ MISTAKE 5: NullPointerException from uninitialized object attribute
public class NpeDemo {
    private String name;   // default is null
    public void printUpper() {
        System.out.println(name.toUpperCase()); // ❌ NPE — name is null!
        // Fix 1: private String name = "";
        // Fix 2: if (name != null) { System.out.println(name.toUpperCase()); }
    }
}

// ❌ MISTAKE 6: Modifying object via final reference
import java.util.ArrayList;
import java.util.List;
public class FinalRefMistake {
    private final List<String> tags = new ArrayList<>();
    public void addTag(String tag) {
        tags.add(tag);  // ✅ Allowed — modifying the object, not the reference
        // tags = new ArrayList<>(); // ❌ COMPILE ERROR — cannot reassign final ref
    }
}

Bad Practices & Anti-Patterns — What Senior Developers Reject

These attribute anti-patterns are frequently flagged in code reviews in professional Java teams. Each violates fundamental OOP and clean code principles.

🚫
Public Instance Fields (Breaking Encapsulation)

Never declare instance attributes as public: 'public double balance;' means any code anywhere can set 'account.balance = -1000000;'. This makes validation impossible and future refactoring very hard. Always declare instance fields private and provide getters/setters. The only acceptable public fields are public static final constants.

🚫
Too Many Attributes (God Object)

A class with 20+ attributes is a warning sign. It's likely doing too many things at once, violating the Single Responsibility Principle. Example: a User class that stores login info, personal info, billing info, address, and preferences all in one. Split into: UserCredentials, UserProfile, BillingInfo, UserAddress, UserPreferences — each focused and manageable.

🚫
Mutable Static Fields (Uncontrolled Global State)

Static mutable fields behave like global variables — any class can modify them, causing unpredictable behavior. They make unit testing a nightmare (tests pollute each other's state). Use static fields ONLY for constants (static final) or counters with very deliberate shared semantics. Prefer passing data through constructors or method parameters.

🚫
Inconsistent Initialization (Half-Baked Objects)

Creating objects that are only partially initialized is dangerous: 'User u = new User(); u.setName("Raj"); u.setEmail("...");' — between 'new User()' and the setters, the object is in an invalid state. Prefer constructors that require all mandatory fields, so objects are always fully valid from the moment of creation.

🚫
Exposing Mutable Internal Collections Directly

Returning a direct reference to an internal list: 'public List<String> getItems() { return items; }' allows outside code to modify the list directly, bypassing all class control: 'obj.getItems().clear()'. Instead return a defensive copy or unmodifiable view: 'return Collections.unmodifiableList(items);' or 'return new ArrayList<>(items);'

🚫
Using Attributes as Temporary Variables

Instance attributes represent PERSISTENT object state — not temporary values needed only during one method call. Storing a temporary calculation result as an attribute (to avoid passing it between helper methods) pollutes the object's state and creates threading bugs. Use local variables for temporary computation; only promote to attribute if the value genuinely represents the object's persistent state.

☕ JavaAttributeAntiPatterns.java
// ❌ ANTI-PATTERN 1: Public instance fields
public class BadProduct {
    public String name;          // ❌ No validation possible
    public double price;         // ❌ Anyone can set price = -99
    public int    stockQuantity; // ❌ Anyone can set stock = -1000
}
// Usage: BadProduct p = new BadProduct(); p.price = -999; // legal but wrong!

// ✅ BETTER: Private fields with validation
public class GoodProduct {
    private String name;
    private double price;
    private int    stockQuantity;
    public void setPrice(double price) {
        if (price < 0) throw new IllegalArgumentException("Price cannot be negative");
        this.price = price;
    }
}

// ❌ ANTI-PATTERN 2: Exposing mutable internal list
public class BadOrder {
    private List<String> items = new ArrayList<>();
    public List<String> getItems() {
        return items; // ❌ Caller can: getItems().clear() — bypasses class control
    }
}

// ✅ BETTER: Return unmodifiable view
public class GoodOrder {
    private List<String> items = new ArrayList<>();
    public List<String> getItems() {
        return Collections.unmodifiableList(items); // ✅ Read-only view
    }
    public void addItem(String item) {
        if (item == null || item.isBlank()) throw new IllegalArgumentException();
        items.add(item); // Only class can modify
    }
}

// ❌ ANTI-PATTERN 3: Attribute used as temp variable
public class BadCalculator {
    private double tempResult; // ❌ Not persistent state — just a temp variable
    public double calculateTax(double amount) {
        tempResult = amount * 0.18;
        return tempResult;
    }
}

// ✅ BETTER: Use local variable
public class GoodCalculator {
    public double calculateTax(double amount) {
        double taxAmount = amount * 0.18; // ✅ local variable — appropriate scope
        return taxAmount;
    }
}

Real-World Production Code Examples — Attributes in Context

The following examples show class attribute design patterns from real enterprise Java codebases — a JPA entity with proper field design, and a Java Record used as an immutable DTO.

☕ JavaProduct.java — JPA Entity with Well-Designed Attributes
package com.techsustainify.catalog.entity;

import jakarta.persistence.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Entity
@Table(name = "products")
public class Product {

    // ── Immutable identity (final — set once, never changes) ──
    @Id
    @Column(name = "product_id", nullable = false, updatable = false)
    private final String productId;

    @Column(name = "sku", unique = true, nullable = false, updatable = false)
    private final String sku;

    @Column(name = "created_at", nullable = false, updatable = false)
    private final LocalDateTime createdAt;

    // ── Mutable attributes ──
    @Column(name = "product_name", nullable = false)
    private String productName;

    @Column(name = "description")
    private String description;

    @Column(name = "category", nullable = false)
    private String category;

    @Column(name = "unit_price", nullable = false, precision = 10, scale = 2)
    private BigDecimal unitPrice;

    @Column(name = "stock_quantity", nullable = false)
    private int stockQuantity;

    @Column(name = "is_active", nullable = false)
    private boolean isActive;

    @Column(name = "last_updated_at")
    private LocalDateTime lastUpdatedAt;

    // ── Static constants ──
    public  static final BigDecimal MAX_PRICE       = new BigDecimal("999999.99");
    private static final int        MAX_STOCK       = 100000;
    public  static final String     DEFAULT_CATEGORY = "General";

    // ── Constructor (required by JPA — no-arg; plus a working constructor) ──
    protected Product() {
        this.productId = null; this.sku = null; this.createdAt = null;
    }

    public Product(String productId, String sku, String productName,
                   String category, BigDecimal unitPrice, int stockQuantity) {
        if (productId == null || productId.isBlank())
            throw new IllegalArgumentException("Product ID required");
        if (unitPrice == null || unitPrice.compareTo(BigDecimal.ZERO) < 0)
            throw new IllegalArgumentException("Price must be non-negative");
        if (stockQuantity < 0 || stockQuantity > MAX_STOCK)
            throw new IllegalArgumentException("Invalid stock quantity");

        this.productId     = productId;
        this.sku           = sku;
        this.productName   = productName;
        this.category      = category != null ? category : DEFAULT_CATEGORY;
        this.unitPrice     = unitPrice;
        this.stockQuantity = stockQuantity;
        this.isActive      = true;
        this.createdAt     = LocalDateTime.now();
        this.lastUpdatedAt = this.createdAt;
    }

    // ── Getters ──
    public String         getProductId()    { return productId;     }
    public String         getSku()          { return sku;           }
    public String         getProductName()  { return productName;   }
    public String         getCategory()     { return category;      }
    public BigDecimal     getUnitPrice()    { return unitPrice;     }
    public int            getStockQuantity(){ return stockQuantity; }
    public boolean        isActive()        { return isActive;      }
    public LocalDateTime  getCreatedAt()    { return createdAt;     }
    public LocalDateTime  getLastUpdatedAt(){ return lastUpdatedAt; }

    // ── Setters with validation ──
    public void setProductName(String productName) {
        if (productName == null || productName.isBlank())
            throw new IllegalArgumentException("Product name cannot be blank");
        this.productName   = productName.trim();
        this.lastUpdatedAt = LocalDateTime.now();
    }

    public void setUnitPrice(BigDecimal unitPrice) {
        if (unitPrice == null || unitPrice.compareTo(BigDecimal.ZERO) < 0
                             || unitPrice.compareTo(MAX_PRICE) > 0)
            throw new IllegalArgumentException("Invalid price: " + unitPrice);
        this.unitPrice     = unitPrice;
        this.lastUpdatedAt = LocalDateTime.now();
    }

    public void adjustStock(int delta) {
        int newQty = stockQuantity + delta;
        if (newQty < 0)        throw new IllegalStateException("Stock cannot go below 0");
        if (newQty > MAX_STOCK) throw new IllegalStateException("Stock exceeds maximum");
        this.stockQuantity = newQty;
        this.lastUpdatedAt = LocalDateTime.now();
    }

    public void deactivate() { this.isActive = false; this.lastUpdatedAt = LocalDateTime.now(); }

    @Override
    public String toString() {
        return String.format(
            "Product{id='%s', sku='%s', name='%s', price=%s, stock=%d, active=%b}",
            productId, sku, productName, unitPrice, stockQuantity, isActive);
    }
}
☕ JavaProductSummaryDTO.java — Java Record for API Response
package com.techsustainify.catalog.dto;

import java.math.BigDecimal;

// ✅ Java Record — perfect for immutable DTO attributes (no boilerplate)
public record ProductSummaryDTO(
    String     productId,
    String     sku,
    String     productName,
    String     category,
    BigDecimal unitPrice,
    int        stockQuantity,
    boolean    isActive
) {
    // ── Compact constructor — validate all attributes ──
    ProductSummaryDTO {
        if (productId   == null || productId.isBlank())
            throw new IllegalArgumentException("productId cannot be blank");
        if (productName == null || productName.isBlank())
            throw new IllegalArgumentException("productName cannot be blank");
        if (unitPrice == null || unitPrice.compareTo(BigDecimal.ZERO) < 0)
            throw new IllegalArgumentException("unitPrice cannot be negative");
        if (stockQuantity < 0)
            throw new IllegalArgumentException("stockQuantity cannot be negative");
        productName = productName.trim();
        category    = category != null ? category.trim() : "General";
    }

    // ── Derived/computed attributes (methods from field data) ──
    public boolean isInStock()   { return stockQuantity > 0 && isActive; }
    public boolean isLowStock()  { return stockQuantity > 0 && stockQuantity <= 5; }

    public BigDecimal priceWithGst(double gstRate) {
        return unitPrice.multiply(BigDecimal.valueOf(1 + gstRate));
    }

    public String stockLabel() {
        if (!isActive)          return "DISCONTINUED";
        if (stockQuantity == 0) return "OUT OF STOCK";
        if (isLowStock())       return "LOW STOCK (" + stockQuantity + ")";
        return "IN STOCK";
    }
}

class DTOUsage {
    public static void main(String[] args) {
        ProductSummaryDTO dto = new ProductSummaryDTO(
            "P001", "LAP-001", "MacBook Air M3", "Electronics",
            new BigDecimal("112000.00"), 4, true
        );

        // Record accessors — same name as the attribute (no 'get' prefix)
        System.out.println(dto.productName());           // MacBook Air M3
        System.out.println(dto.unitPrice());             // 112000.00
        System.out.println(dto.stockLabel());            // LOW STOCK (4)
        System.out.println(dto.priceWithGst(0.18));      // 132160.00
        System.out.println(dto);                         // auto toString()

        // Records are IMMUTABLE — all attributes are final
        // dto.stockQuantity = 10; // ❌ COMPILE ERROR
    }
}

Attribute Types Flowchart — Choosing the Right Attribute

This flowchart helps you decide which type of class attribute to use for any given piece of data in your Java class design.

📋 Need to store data in a class?Start here for any field decision
start
🔁 Is value the SAME for every object?Shared across all instances?
yes — shared
🔒 Should value NEVER change?Fixed constant — never modified
yes — constant
🏛️ static final attributeConstant: MAX_SIZE, TAX_RATE, PI
📦 static attributeShared counter, config: totalCount
🔒 Should value be set ONCE per object?Immutable per-object identity field
yes — final
🔐 private final attributeIdentity: userId, PAN, createdAt
👁️ Who needs to read/write it?Determine visibility
only this class or public API
🔒 private attribute + getter/setterBest practice: encapsulate with validation
🛡️ protected attributeSubclass access: inheritance hierarchy

Code Execution Flow — from source to output

Java Class Attributes Interview Questions — Beginner to Advanced

These questions are consistently asked in Java fresher interviews, OCPJP certification exams, and campus placement tests related to class attributes and fields.

Practice Questions — Test Your Class Attributes Knowledge

Attempt each question independently before reading the answer — active recall is proven to be 2–3x more effective than passive reading.

1. What is the output? public class Demo { static int x = 5; int y = 10; public static void main(String[] args) { Demo d1 = new Demo(); Demo d2 = new Demo(); d1.y = 50; Demo.x = 99; System.out.println(d2.y); System.out.println(d1.x); } }

Easy

2. Find the bug and fix it: public class Rectangle { private int width; private int height; public Rectangle(int width, int height) { width = width; height = height; } public int area() { return width * height; } }

Easy

3. Design a 'Vehicle' class with: private instance attributes — brand (String), model (String), year (int), speed (double, default 0.0); a static attribute totalVehicles; a static final constant MAX_SPEED = 300.0; a constructor; and an accelerate(double amount) method that increases speed but does not exceed MAX_SPEED.

Easy

4. What types of default values do these attributes have? public class Defaults { int a; double b; boolean c; String d; int[] e; char f; }

Easy

5. Refactor this class to apply proper encapsulation: public class BankAccount { public String owner; public double balance; public String accountType; }

Medium

6. What is wrong with this code? public class Config { public static final List<String> ALLOWED_ROLES = new ArrayList<>(); static { ALLOWED_ROLES.add("ADMIN"); ALLOWED_ROLES.add("USER"); ALLOWED_ROLES.add("VIEWER"); } public List<String> getAllowedRoles() { return ALLOWED_ROLES; } }

Medium

7. Create a 'Temperature' class with a private final double attribute in Celsius. Provide getters for Celsius, Fahrenheit, and Kelvin (computed). Include a static factory method 'fromFahrenheit(double f)' and a static constant ABSOLUTE_ZERO_CELSIUS = -273.15. Validate that temperature cannot be below absolute zero.

Medium

8. Convert this class to a Java Record and ensure all attributes are validated: public class Address { private final String street; private final String city; private final String state; private final String pinCode; // constructor, getters, equals, hashCode, toString... }

Medium

Conclusion — Class Attributes: The State of Every Java Object

Class attributes are the state of every Java object. Without attributes, a class is just a collection of stateless behaviors — like a calculator with no memory. Attributes give objects their identity, their data, and their ability to behave differently from one another. How you design, name, initialize, and protect attributes directly determines the quality, reliability, and maintainability of your Java code.

The difference between beginner and professional attribute design is clear: beginners use public fields, skip validation, mix up instance and static, and ignore default null values. Professional Java code uses private fields with validated getters/setters, meaningful camelCase names, static final constants with UPPER_SNAKE_CASE, final instance fields for immutable identity, and defensive methods for mutable collections.

Attribute TypeKeywordAccessShared?Mutable?Use Case
Instance attribute(none)privateNo — per objYesname, age, balance, status
Static attributestaticprivate/publicYes — all objYestotalCount, sharedConfig
Static final constantstatic finalpublic/privateYes — all objNoMAX_SIZE, TAX_RATE, PI
Instance final attributefinalprivateNo — per objNouserId, orderId, createdAt
Record component (Java 16+)recordpublic accessorNo — per objNoDTO, value objects, API models

Your next step: Java Class Methods — where you'll learn how to define the behavior of your classes, how methods work with your attributes, method overloading, return types, and how static and instance methods differ in accessing class state. ☕

Frequently Asked Questions — Java Class Attributes