Class Members in Java
-
Last Updated: February 16, 2025
-
By: javahandson
-
Series
Class members in Java are the parts that make up a class: fields, methods, constructors, static members, initializer blocks, and nested classes. This guide walks through each one with plain language and runnable examples.
Think about a car for a second. It has data, like its colour and fuel level. It also does things, like starting and braking. A Java class works the same way.
Class members in Java are simply the pieces you write inside the curly braces of a class. Some hold data. Others run logic. A few even group related classes together.
Get comfortable with these building blocks and the rest of object-oriented programming clicks into place. Miss them, and inheritance and encapsulation feel like magic words.
A class is a blueprint. Class members are the labelled parts on that blueprint.
Picture a contact card in your phone. The name and number are data. The call button is an action. A Java class bundles both ideas into one unit.
Every class member falls into one of two camps. Fields hold state, meaning the data an object carries around. Methods provide behavior, meaning the things an object can do.
A BankAccount stores a balance. That is state. It also lets you deposit money. That is behavior.
Keep those two words in mind. They explain almost every design decision you will make later.
Here is what you can put inside a class body:
One small precision point, because interviewers love it. The Java Language Specification counts fields, methods, and nested types as members. Constructors and initializer blocks technically sit outside that definition, since you never inherit them. Most tutorials lump all of them together, and this article does too, because you write them in the same place.
Beginners mix these up constantly. A member lives directly in the class body. A local variable lives inside a method, constructor, or block.
The difference matters for two reasons:
Try to read a local variable before you assign it and the compiler stops you cold. That error message saves you from a whole category of bugs.
Instance variables describe what an object is. You declare them inside the class but outside every method.
This is the single most important rule. Instance variables belong to the object, never to the class.
Create two Student objects and the JVM carves out two separate sets of fields on the heap. Change one student’s marks and the other student never notices.
Think of a class as a cookie cutter. Each cookie carries its own sprinkles.
When you call new, the JVM clears the object’s memory and fills every field with a default. You never see garbage data.
| Field type | Default value |
|---|---|
byte, short, int, long | 0 |
float, double | 0.0 |
char | the null character (code point 0) |
boolean | false |
| Any object reference | null |
Notice that null default. It causes more NullPointerException crashes than anything else in Java, so treat uninitialized references with respect.
Let us make this concrete with two students who share a class but not their data.
class Student {
String name;
int rollNumber;
double marks;
void display() {
System.out.println(name + " (" + rollNumber + ") scored " + marks);
}
}
public class Main {
public static void main(String[] args) {
Student s1 = new Student();
s1.name = "Suraj";
s1.rollNumber = 101;
s1.marks = 85.5;
Student s2 = new Student();
s2.name = "Shweta";
s2.rollNumber = 102;
s2.marks = 92.0;
s1.display(); // Output: Suraj (101) scored 85.5
s2.display(); // Output: Shweta (102) scored 92.0
}
}Two objects, two independent copies of name, rollNumber, and marks. That independence is exactly what makes objects useful.
If fields say what an object is, methods say what it can do.
A method header packs several pieces into one line:
public or privatevoid when the method hands nothing backSo public double getMarks() tells you plenty before you read a single line of the body.
Parameters feed data in. The return type carries a result back out.
Some methods take input and return nothing. Others take nothing and return plenty. You pick whichever shape fits the job.
One habit pays off early: give each method a single clear responsibility. A method named calculateTotal should calculate a total, not print a report and email it too.
class Student {
String name;
int marks;
void setData(String n, int m) {
name = n;
marks = m;
}
String getResult() {
if (marks >= 40) {
return "Pass";
}
return "Fail";
}
}
public class Main {
public static void main(String[] args) {
Student s = new Student();
s.setData("Suraj", 85);
System.out.println(s.getResult()); // Output: Pass
}
}Look at what each method does. setData writes to the fields, and getResult reads them and decides something. Methods and fields work as a team.
One class can hold several methods that share a name. Java tells them apart by their parameter lists.
You have seen this already. System.out.println() accepts an int, a String, a double, and plenty more. Those are separate overloaded methods, not one clever method.
class Calculator {
int add(int a, int b) {
return a + b;
}
double add(double a, double b) { // different parameter types
return a + b;
}
int add(int a, int b, int c) { // different parameter count
return a + b + c;
}
}
public class Main {
public static void main(String[] args) {
Calculator c = new Calculator();
System.out.println(c.add(2, 3)); // Output: 5
System.out.println(c.add(2.5, 3.5)); // Output: 6.0
System.out.println(c.add(1, 2, 3)); // Output: 6
}
}The compiler picks the match at compile time, based purely on the arguments you pass.
Watch out for one limit. A different return type alone does not overload anything. Change int add(int, int) to double add(int, int) and the compiler reports a duplicate method, because the parameter lists still match.
A constructor prepares a fresh object for use. Java runs it automatically the moment you write new.
Three differences matter:
voidAdd void in front of a constructor and it quietly stops being a constructor. The compiler treats it as an ordinary method that happens to share the class name. This trips up plenty of beginners.
Write a class with no constructor at all and the compiler supplies one. It takes no arguments and leaves every field at its default value.
Now the catch. Declare even one constructor of your own and that free gift disappears.
class Student {
String name;
Student(String n) { // we declared this one
name = n;
}
}
// new Student(); // Compile error: no no-arg constructor existsWant both options? Then declare the no-argument version yourself.
A parameterized constructor lets you hand over real values up front. The object arrives valid instead of empty.
Compare the two styles. Without a constructor you create the object, then set four fields, and hope you remember all four. With a constructor you pass four arguments and the compiler checks your work.
class Book {
String title;
String author;
double price;
Book(String title, String author, double price) {
this.title = title; // this.title is the field
this.author = author; // title alone is the parameter
this.price = price;
}
}
public class Main {
public static void main(String[] args) {
Book b = new Book("Effective Java", "Bloch", 45.0);
System.out.println(b.title + " by " + b.author); // Output: Effective Java by Bloch
}
}Notice the this keyword on every line. The parameter and the field share a name here, so this.title points at the field while plain title points at the parameter. Drop the this and you would assign the parameter to itself, leaving the field null.
Naming them identically is normal practice in Java. Just remember what this is doing for you.
You can declare several constructors as long as their parameter lists differ. Java picks the right one by matching the arguments you pass.
The keyword this() lets one constructor call another, which keeps your setup logic in a single place.
class Student {
String name;
int marks;
Student() {
this("Unknown", 0); // delegates to the constructor below
}
Student(String n, int m) {
name = n;
marks = m;
}
void display() {
System.out.println(name + " : " + marks);
}
}
public class Main {
public static void main(String[] args) {
new Student().display(); // Output: Unknown : 0
new Student("Suraj", 90).display(); // Output: Suraj : 90
}
}One rule to remember: this() must sit on the very first line of the constructor body.
Sometimes every object should agree on one value. That is the job of a static variable.
Mark a field static and the class holds exactly one copy of it. Create a thousand objects and that count stays at one.
The JVM sets up static variables once, when it first initializes the class. Objects created later just read the value that already sits there.
class Student {
String name;
static String schoolName = "ABC School";
}
public class Main {
public static void main(String[] args) {
Student s1 = new Student();
Student s2 = new Student();
Student.schoolName = "XYZ School";
System.out.println(s1.schoolName); // Output: XYZ School
System.out.println(s2.schoolName); // Output: XYZ School
}
}Change it once and both objects see the new value. They were never holding separate copies to begin with.
Reach for static when the data describes the class rather than any one object:
static final double PIAlways prefer Student.schoolName over s1.schoolName when you access one. Both compile, but the class name shows your intent.
| Aspect | Instance variable | Static variable |
|---|---|---|
| Belongs to | The object | The class |
| Number of copies | One per object | Exactly one, ever |
| Created when | You call new | The JVM initializes the class |
| Preferred access | Object reference | Class name |
| Memory area | Heap, inside the object | Method area (class metadata) |
| Good for | Data unique to one object | Data every object shares |
Static methods follow the same idea. They belong to the class, so you can call them without any object at all.
You have used these already. Every call to Math.max() or Integer.parseInt() hits a static method.
Notice how none of those need an object. They take input, compute something, and return a result.
A static method runs without any object, which creates real limits:
this and super mean nothing here, so you cannot use themNeed object data inside a static method? Pass the object in as a parameter.
class Student {
String name;
static String schoolName = "ABC School";
static void showSchool() {
System.out.println(schoolName); // fine: static reads static
}
static void showName(Student s) {
System.out.println(s.name); // fine: we handed it an object
}
// static void broken() {
// System.out.println(name); // Compile error: name is not static
// }
}Here is a question interviewers ask a lot. Why does main carry the static keyword?
Think about the startup sequence. The JVM must call main before your program creates anything. No object exists yet, so an instance method would be unreachable.
Marking it static solves that chicken-and-egg problem neatly.
Initializer blocks are the members most tutorials skip. They handle setup that a simple field assignment cannot.
An instance initializer is a bare pair of braces in the class body. Java copies its code into every constructor, so it runs on each object you create.
These help when several constructors need identical setup and you would rather not repeat yourself.
Put static before those braces and the block runs once, when the JVM initializes the class.
Static blocks earn their keep when a static field needs more than one line to build, such as loading a configuration file or filling a lookup table.
This sequence shows up in interviews constantly. Run the example and watch it happen.
class Demo {
static { System.out.println("1. static block"); }
{ System.out.println("2. instance block"); }
Demo() { System.out.println("3. constructor"); }
public static void main(String[] args) {
new Demo();
System.out.println("---");
new Demo();
}
}
// Output:
// 1. static block
// 2. instance block
// 3. constructor
// ---
// 2. instance block
// 3. constructorRead that output carefully. The static block printed once and never again. The instance block and constructor ran for both objects.
So the rule is simple. Static setup happens once per class. Instance setup happens once per object, always before the constructor body.
A nested class is a class declared inside another class. Use one when the helper only makes sense next to its owner.
Leave off the static keyword and you get an inner class. It ties itself to an instance of the outer class.
That connection gives it a superpower. An inner class reaches every member of the outer object, private fields included.
class Outer {
private int x = 10;
class Inner {
void display() {
System.out.println("x is " + x); // reads a private field
}
}
}
public class Main {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner(); // note the syntax
inner.display(); // Output: x is 10
}
}Look at outer.new Inner(). That odd syntax exists because an inner class object cannot live without an outer object behind it.
Add static and the picture changes. A static nested class stands on its own, with no link to any outer instance.
class Outer {
static int x = 20;
static class Inner {
void display() {
System.out.println("x is " + x);
}
}
}
public class Main {
public static void main(String[] args) {
Outer.Inner inner = new Outer.Inner(); // no outer object needed
inner.display(); // Output: x is 20
}
}Because no outer object exists, this class can only touch the static members of Outer directly.
| Aspect | Inner class | Static nested class |
|---|---|---|
| Declaration | No static keyword | Uses static |
| Tied to | An outer object | The outer class |
| Creating it | outer.new Inner() | new Outer.Inner() |
| Outer access | Every member, private included | Static members only |
| Holds a reference to outer | Yes | No |
| Typical use | Helpers that need outer state | Builders and standalone helpers |
Prefer the static version by default. That hidden reference in an inner class keeps the outer object alive in memory longer than you might expect.
One footnote. Local classes and anonymous classes also live inside other code, but you declare them inside a method, so Java does not treat them as members.
Every member carries a visibility setting. It decides which code may touch that member.
| Modifier | Same class | Same package | Subclass elsewhere | Anywhere |
|---|---|---|---|---|
private | Yes | No | No | No |
| (no modifier) | Yes | Yes | No | No |
protected | Yes | Yes | Yes | No |
public | Yes | Yes | Yes | Yes |
Watch the second row. Java has no keyword for package-private access, so you simply write nothing. The word default exists in the language, but it applies to interface methods and switch statements instead.
Here is the habit worth building. Mark fields private, then expose them through public methods.
Why bother? Because a public field accepts any value at all. A setter can refuse the bad ones.
class Student {
private int marks;
public void setMarks(int m) {
if (m < 0 || m > 100) {
throw new IllegalArgumentException("Marks must be 0-100");
}
marks = m;
}
public int getMarks() {
return marks;
}
}Now nobody can set marks to 5000. The class defends its own data, which is exactly what encapsulation means.
Access modifiers control who can see a member. The final keyword controls something else entirely: whether anyone can change it.
Mark a field final and it accepts exactly one assignment. Try to reassign it later and the compiler stops you.
This helps more than it sounds. A field that never changes after construction cannot drift into a bad state halfway through your program.
One subtlety catches people out. Making a reference final locks the reference, not the object behind it.
final List<String> names = new ArrayList<>();
names.add("Suraj"); // fine: we changed the list contents
// names = new ArrayList<>(); // Compile error: cannot reassign namesSo final means “this variable keeps pointing at the same object”. It never promises the object itself stays frozen.
You can declare a final field without giving it a value. Java calls that a blank final.
Every constructor must then assign it exactly once. Miss one constructor and the compiler complains.
class Student {
private final String rollNumber; // no value yet
Student(String rollNumber) {
this.rollNumber = rollNumber; // assigned here, once
}
}Blank finals fit naturally with identity data. A roll number belongs to a student from birth and should never change afterwards.
Combine both keywords and you get a true constant. The class holds one copy, and nobody can ever change it.
Convention says to name these in capitals with underscores between words:
class MathUtils {
static final double PI = 3.14159;
static final int MAX_RETRIES = 3;
}
// Usage: MathUtils.MAX_RETRIESThe standard library follows this rule everywhere. Think of Integer.MAX_VALUE or Math.PI.
Add final to a method and subclasses lose the ability to override it. Use this when a method’s behavior must stay exactly as written, such as a security check or a validation rule.
Extend a class and the child picks up much of the parent. Not everything travels though, and knowing the difference clears up a lot of confusion.
A subclass inherits the members its access level lets it see:
public and protected fields and methods come across everywhere| Member | Inherited? | Why |
|---|---|---|
| Public and protected fields | Yes | Visible to the subclass |
| Public and protected methods | Yes | Visible, and you may override them |
private members | No | The subclass cannot see them at all |
| Constructors | No | They build one specific class, so each class declares its own |
| Initializer blocks | No | They belong to the class that declares them |
That table explains the earlier point about constructors. Because inheritance skips them, the specification does not treat them as members.
A private field still exists inside the child object, by the way. The child simply has no direct way to touch it, so a protected getter becomes the usual route in.
Modern Java can generate members on your behalf. Records, added for good in Java 16, are the clearest example.
record Point(int x, int y) { }
// Java generates: private final fields x and y,
// a constructor, accessors x() and y(),
// plus equals(), hashCode() and toString()One line replaces roughly forty. Records suit plain data carriers, where the fields never change after construction.
Learn the members by hand first, though. Records only save you time once you know what they generate.
These trip up almost every learner at least once.
void in front and you have written a plain method, not a constructor.name and write name = name, and you assign the parameter to itself. Use this.name = name.outer.new Inner() form.final freezes an object. It locks the reference only. A final List still accepts new elements all day long.protected accessor when a child genuinely needs the value.Time to tie every member type into one small program. This class uses instance fields, a static counter, a constructor, a static method, and encapsulation together.
class BankAccount {
private static int accountCount = 0; // shared by the class
private static final String BANK = "Java Bank";
private final String holder; // unique per object
private double balance;
BankAccount(String holder, double opening) {
this.holder = holder;
this.balance = opening;
accountCount++; // one more account exists
}
void deposit(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Deposit must be positive");
}
balance += amount;
}
double getBalance() {
return balance;
}
static int getAccountCount() { // no object needed
return accountCount;
}
void printSummary() {
System.out.println(BANK + " | " + holder + " | " + balance);
}
}
public class Main {
public static void main(String[] args) {
BankAccount a = new BankAccount("Suraj", 5000);
BankAccount b = new BankAccount("Shweta", 8000);
a.deposit(1500);
a.printSummary(); // Output: Java Bank | Suraj | 6500.0
b.printSummary(); // Output: Java Bank | Shweta | 8000.0
System.out.println(BankAccount.getAccountCount()); // Output: 2
}
}Trace what happened. Each constructor call set that object’s own holder and balance, then bumped the shared accountCount.
The deposit on account a changed only its balance. Account b stayed untouched, because instance fields never overlap.
Meanwhile getAccountCount() answered without any object, since the counter belongs to the class. And balance stayed private, so the only way in runs through deposit and its validation check.
That single class shows why these member types exist. Each one solves a different problem.
A: Class members are the parts you declare inside a class body. They include instance variables, static variables, methods, and nested classes. Constructors and initializer blocks sit alongside them, though the language specification does not count those as members because you never inherit them.
A: An instance variable gets one copy per object, so every object holds its own value. A static variable gets exactly one copy for the whole class, shared by every object. Change a static variable through one object and all the others see the new value.
A: A static method runs at the class level, and you can call it before any object exists. Instance variables only exist inside objects, so the method would have nothing to read from. Pass an object in as a parameter when you need its data.
A: The JVM calls main before your program has created a single object. A static method needs no object, so the JVM can invoke it straight from the class. Without static, the JVM would face a chicken-and-egg problem at startup.
A: The compiler stops supplying it. You only receive a free no-argument constructor when your class declares no constructor at all. Declare a parameterized one and calls to new MyClass() will fail to compile until you add the no-argument version yourself.
A: No. A constructor never declares a return type, not even void. Adding one turns it into an ordinary method that merely shares the class name, and the compiler will no longer call it during object creation.
A: An inner class belongs to an object of the outer class, so you create it with outer.new Inner() and it can read every outer member including private ones. A static nested class belongs to the class itself, so you create it with new Outer.Inner() and it reaches only static members directly.
A: Static blocks run first and only once, when the JVM initializes the class. After that, every object creation runs the instance initializer blocks and field initializers in the order you wrote them, then the constructor body.
A: Only instance variables do. The JVM sets numeric fields to 0, boolean fields to false, and reference fields to null. Local variables receive nothing, so the compiler rejects any attempt to read one before you assign it.
A: A private field blocks direct access from outside code, so every change must pass through methods you control. Those methods can validate input and reject bad values. That protection is the core idea behind encapsulation.
A: No. The final keyword locks the reference, so the variable keeps pointing at the same object forever. The object itself can still change. A final List rejects reassignment but happily accepts new elements through add().
A: A subclass inherits the public and protected fields, methods, and nested types of its parent, plus package-private members when both classes share a package. It never inherits private members, constructors, or initializer blocks.
A: Yes, as long as their parameter lists differ in type, count, or order. Java calls this method overloading and resolves the right one at compile time. A different return type alone is not enough, and the compiler reports a duplicate method.
Let us wrap up what we covered. Class members in Java are the pieces inside a class body, and each one plays a distinct role.
Instance variables hold data that belongs to one object. Methods define what that object can do. Constructors get a new object into a valid state before anyone uses it.
The static keyword lifts a member from the object up to the class. One shared copy, reachable without new. Initializer blocks fill the gaps, running setup code once per class or once per object.
Nested classes keep a tightly coupled helper close to its owner. Access modifiers then decide who may see any of it, and private fields with public methods give you real encapsulation.
Two extras round out the picture. The final keyword pins a member down so nobody reassigns it, and inheritance passes visible members to a subclass while leaving constructors behind.
Practice by writing one small class that uses all of them, the way the bank account example did. These fundamentals show up in every Java codebase you will ever touch.