JAVA Basics
1. Introduction to Java
Java is a high-level, object-oriented programming language developed by Sun Microsystems (now owned by Oracle) in 1995. It is platform-independent, meaning you can write Java code on one system and run it on any other system that supports Java.
Key Features of Java:
- Object-Oriented: Java supports concepts such as inheritance, encapsulation, polymorphism, and abstraction.
- Platform-Independent: Java’s “Write Once, Run Anywhere” capability allows code to run on any device that has the Java Virtual Machine (JVM).
- Secure: Java has built-in security features like bytecode verification and a security manager.
- Robust: It handles memory management and supports garbage collection to prevent memory leaks.
- Multi-threaded: Java supports multithreading, allowing multiple tasks to run concurrently.
2. Setting Up the Java Development Environment
Installing Java
- Download and install the Java Development Kit (JDK) from the Oracle website or use OpenJDK (an open-source implementation of Java).
- Set the environment variable
JAVA_HOMEto the installation directory of the JDK. - Add the
bindirectory of the JDK to your system'sPATHenvironment variable.
Setting Up an Integrated Development Environment (IDE)
Popular IDEs for Java development:
- Eclipse (eclipse.org)
- IntelliJ IDEA (jetbrains.com)
- NetBeans (netbeans.apache.org)
You can also use a simple text editor and run Java code from the command line if you prefer.
3. Basic Syntax
Writing Your First Java Program
Here is a simple Java program that prints “Hello, World!” to the console:
public class Demo {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}Explanation:
1. Class Declaration
Example: `public class Demo`
- Access Modifier:
- Definition: Indicates the visibility of the class.
- Types:
- public: Accessible from any other class.
- private: Accessible only within the defined class.
- protected: Accessible within the same package and by subclasses.
- default: No modifier; accessible only within the same package.
- In Our Example: `public` allows `Demo` to be accessed from anywhere.
- Class Keyword:
- `class` is a reserved keyword in Java.
- It must be in lowercase.
- Every Java program must define a class.
- Class Name:
- The name of the class (`Demo` in this case) should start with a capital letter by convention.
- The file name should match the class name (`Demo.java`).
- Class names can include letters, digits, `$`, and `_`, but must begin with a letter.
2. Defining the Main Method
Example: `public static void main(String args[])`
- Access Modifier: Similar to the class, here it is `public`, allowing the method to be accessible from anywhere.
- Non-Access Modifier:
- static: This allows the method to be called without creating an instance of the class. It belongs to the class itself.
- Non-static methods require an object of the class to be called.
- Return Type:
- `void` indicates that this method does not return a value.
- Method Name:
- The name `main` is predefined in Java as the entry point for any Java application.
- Any method name can be identified by parentheses `()`.
- Command Line Arguments:
- `String args[]` allows the program to accept command line arguments.
- This can also be written as:
1. `String args[]`
2. `String[] args`
3. `String [] args`
- The capital `S` in `String` is crucial, as Java is case-sensitive.
3. Printing Statement
Example: `System.out.println(“This is my first program”);`
- System Class:
- `System` is a predefined class in Java.
- Dot Operator (.):
- Used to access members (methods or variables) of a class.
- Out Object:
- `out` is a static member of the `System` class, which is an instance of `PrintStream`.
- println() Method:
- A method of `PrintStream` that prints the provided string followed by a new line.
- String Literal:
- `”Hello World”` is the message to be printed. It is enclosed in double quotes.
Compiling and Running Java Code
- Compile:
javac HelloWorld.java - Run:
java HelloWorld
1) Difference Between C and Java Programs:
Key differences between C and Java revolve around their design, usage, and features:
- Platform Independence:
- C: C programs are compiled into machine-specific binary code, meaning a C program compiled on one platform cannot run on another without recompilation.
- Java: Java is platform-independent, as it is compiled into an intermediate bytecode that can run on any system with a Java Virtual Machine (JVM).
- Memory Management:
- C: In C, memory management is manual. The programmer has to explicitly allocate and deallocate memory using functions like malloc() and free().
- Java: Java has automatic memory management with its Garbage Collector, which automatically frees up memory when objects are no longer in use.
- Syntax:
- C: C uses procedural programming, and concepts like pointers and structures play a significant role.
- Java: Java uses object-oriented programming (OOP), where the focus is on classes, objects, and inheritance. Java does not have pointers or structures as C does.
- Execution Model:
- C: C compiles directly to machine code, which is specific to the processor.
- Java: Java code is compiled into bytecode, which is interpreted or compiled by the JVM at runtime.
- Low-Level Operations:
- C: C allows low-level programming (e.g., direct memory access via pointers), making it suitable for system programming.
- Java: Java abstracts away low-level operations, so direct memory access is not allowed for security and simplicity.
2) How Java Is Platform Independent:
Java is platform-independent because of its use of bytecode and the Java Virtual Machine (JVM). Here’s how it works:
1. Source Code Compilation: Java source code (.java files) is compiled into bytecode (.class files) by the Java compiler (javac). This bytecode is not machine-specific.
2. Bytecode Execution: The bytecode can run on any machine that has a JVM. The JVM interprets or JIT compiles the bytecode into machine code that is specific to the platform it is running on (Windows, Linux, macOS, etc.).
3. Write Once, Run Anywhere (WORA): Because the JVM is platform-specific but the bytecode is the same across platforms, the same compiled Java program can run on any device with a JVM, making Java platform-independent.
3) Why Native Code is Available in C but Not in Java:
- C Programs: C programs are compiled into native machine code that is specific to the operating system and processor architecture. This means that the code can directly interact with the hardware, giving C high performance but making the code non-portable across platforms without recompilation.
- Java Programs: Java does not compile into native code directly. Instead, Java compiles to bytecode, which runs on the JVM. This abstraction allows Java to be platform-independent. While Java can still call native code using the Java Native Interface (JNI), Java’s design avoids directly compiling into platform-specific machine code to maintain portability.
4) How to Compile and Run Java Programs Using Command Prompt:
To compile and run a Java program using the command prompt:
1. Write the Java Program:
Create a Java file (HelloWorld.java) with the following content:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}2. Compile the Program:
Open the command prompt and navigate to the directory where the .java file is saved. Use the following command to compile the program:
javac HelloWorld.javaThis will create a HelloWorld.class file (bytecode).
3. Run the Program:
After compiling, run the program using:
java HelloWorldNote: Do not include the .class extension while running the program with the java command.
5) What Happens if We Save a Blank File as a .java File?
- If you save a blank file with a .java extension and try to compile it using javac, the compiler will return an error:
error: Class names, 'filename.java', are only accepted if annotation
processing is explicitly requestedA .java file must contain at least one class or interface declaration. A blank file will not be considered a valid Java program by the compiler.
6) Can We Perform Changes in Bytecode? What Are the Issues?
Yes, you can make changes to bytecode manually, but it’s highly discouraged due to several issues:
- How Bytecode Can Be Modified:
- You can use bytecode editors or tools like javap (Java bytecode disassembler) to decompile and modify .class files.
- Libraries like ASM or Javassist allow programmatic manipulation of bytecode during runtime.
- Issues with Modifying Bytecode:
- Complexity: Bytecode is a low-level representation of Java code, and modifying it directly is error-prone and difficult to manage.
- ClassLoader Violations: Tampering with bytecode can lead to issues with the Java ClassLoader, especially if the changes break the internal structure or violate the Java specification.
- Security Risks: Bytecode manipulation opens up the program to security vulnerabilities, as malicious code could be injected.
- Loss of Original Source Mapping: Changes made directly to bytecode will not be reflected in the original Java source code, which can lead to confusion during debugging.
In general, bytecode manipulation should be reserved for advanced use cases like creating dynamic proxies or frameworks (e.g., AOP) and should be handled with great care.
Java Character Set
What is a Character Set?
A character set is a collection of characters that Java can recognize and work with. These characters are letters, numbers, symbols, and whitespace that are used in Java programs. Every character is internally stored using a numeric code in a character set, and Java supports Unicode, which makes it compatible with characters from different languages.
Java’s Character Set
Java uses Unicode as its character set. Unicode is a universal character encoding standard that assigns a unique number to every character, regardless of platform, language, or program. Java originally used 16-bit Unicode characters (UTF-16), allowing it to support many world languages.
Unicode can represent over 140,000 characters, including alphabets like Latin, Greek, Cyrillic, Han (Chinese characters), and more. Java specifically supports the UTF-16 encoding by default.
Why Unicode?
Java chose Unicode to make the language truly platform-independent and suitable for internationalization. As Java applications are used worldwide, Unicode provides a standardized way of encoding all the world’s characters.
Data Types in Java
A data type is a classification of data that informs the compiler or interpreter how the data will be used in a program. Each data type specifies a range of values it can store and the operations that can be performed on that data. Programming languages typically support various data types like integers, floating-point numbers, characters, strings, and booleans.
Data Types in Java
Data types in Java are divided into two main categories:
1. Primitive Data Types (Built-In Data Types)
2. User-Defined Data Types (Non-Primitive Data Types)
Primitive Data Types (Built-In Data Types)
Java supports eight primitive data types, which are the basic data types built into the language. These data types are predefined by Java and named by a keyword. They are used to store simple values directly.
Explanation of Code Example
Here is a Java program demonstrating some primitive data types:
package com.jtcindia.org;
/* Size of Data Type */
public class Jtc {
public void dataType2() {
System.out.println("---dataType2() in Jtc class---");
byte b11 = 127;
byte b22 = -128;
// byte b33 = 128; // Error: 128 is out of range for byte
// byte b44 = -129; // Error: -129 is out of range for byte
int i11 = 2147483647;
int i12 = -2147483648;
// int i13 = 2147483648; // Error: 2147483648 is out of range for int
// long l14 = 2147483649; // Error: 2147483649 is interpreted as int by default
long l12 = 2147483648L; // Correct usage for long type
// float f11 = 11.11; // Error: 11.11 is interpreted as double by default
float f12 = 11.11f; // Correct usage for float type
double d11 = 11.12;
double d12 = 11.13d;
System.out.println(b11);
System.out.println(b22);
System.out.println(i11);
System.out.println(i12);
System.out.println(l12);
System.out.println(f12);
System.out.println(d12);
}
public static void main(String[] args) {
Jtc jtc = new Jtc();
jtc.dataType2();
}
}Explanation of the Errors in the Commented Code:
1. `byte b33 = 128;`
- Error: `128` is outside the range of the `byte` data type, which can only hold values from `-128` to `127`.
2. `byte b44 = -129;`
- Error: `-129` is also outside the range of the `byte` data type.
3. `int i13 = 2147483648;`
- Error: `2147483648` is out of range for the `int` type, which supports values from `-2147483648` to `2147483647`.
4. `long l14 = 2147483649;`
- Error: Although `2147483649` fits within the `long` range, it is treated as an `int` by default in Java. Adding an `L` at the end like `2147483648L` tells the compiler to treat it as a `long`.
5. `float f11 = 11.11;`
- Error: By default, decimal numbers in Java are treated as `double`. To store a value as a `float`, you must append `f` or `F` at the end, like `11.11f`.
Range of Primitive Data Types
The range of a data type defines the maximum and minimum values that the type can hold. Here is another example demonstrating the range of different data types in Java:
package com.jtcindia.org;
/* Data Type Minimum and Maximum value */
public class Jtc {
public void dataType3() {
System.out.println("Byte Range: " + Byte.MIN_VALUE + " to " + Byte.MAX_VALUE);
System.out.println("Short Range: " + Short.MIN_VALUE + " to " + Short.MAX_VALUE);
System.out.println("int Range: " + Integer.MIN_VALUE + " to " + Integer.MAX_VALUE);
System.out.println("Long Range: " + Long.MIN_VALUE + " to " + Long.MAX_VALUE);
System.out.println("Float Range: " + Float.MIN_VALUE + " to " + Float.MAX_VALUE);
System.out.println("Double Range: " + Double.MIN_VALUE + " to " + Double.MAX_VALUE);
}
public static void main(String[] args) {
Jtc jtc = new Jtc();
jtc.dataType3();
}
}User-Defined Data Types (Non-Primitive Data Types)
User-defined data types in Java are created by the programmer to store multiple values, either of the same or different types. They include:
1. Classes: A class is a blueprint for creating objects with fields (variables) and methods (functions).
- Syntax:
class ClassName {
// fields
// methods
}2. Interfaces: An interface is a fully abstract data type that defines a contract for classes. An interface can contain abstract methods and constants.
- Syntax:
interface InterfaceName {
// abstract methods
// constants
}3. Enums: An enum is a special data type that represents a group of constants (unchangeable values).
- Syntax:
enum EnumName {
VALUE1, VALUE2, VALUE3
}Enum Example
enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
}
public class Jtc {
public static void main(String[] args) {
Day today = Day.SUNDAY;
System.out.println(today);
}
}Using print and println
- print(): Outputs text on the same line.
- println(): Outputs text and then moves to the next line.
Example:
System.out.print("Hello, ");
System.out.println("World!");
// Output:
// Hello, World!Commenting Code in Java
Comments are notes ignored by the compiler, useful for explaining code.
- Single-line comment:
// This is a single-line comment - Multi-line comment
/*
This is a
multi-line comment
*/- Documentation comment (used in JavaDoc):
/**
* This is a documentation comment
* which can be used for JavaDoc.
*/Interview Questions Related to Java Basics
Can we change the syntax of the main method?
- No, JVM recognizes
public static void main(String[] args)as the entry point, so it must remain unchanged for Java applications to run.
Can we keep the main method as private?
No, because JVM needs to access the main method from outside the class. If it’s private, the program will compile but not execute, resulting in an error.
Can we keep the main method non-static?
- No,
mainis static so JVM can call it without creating an object. If it’s non-static, JVM cannot call it directly, and the program will not execute.
Why do we need f for float literals?
- By default, decimal numbers are considered
double. Addingftells Java to treat the number as afloat.
Keywords in Java
Keywords are predefined, reserved words in Java that have special meanings. You cannot use keywords as identifiers (like variable names, class names, etc.). Here are a few important ones:
- Control flow: if, else, switch, for, while, do, break, continue, return
- Modifiers: public, private, protected, static, final, abstract
- Object-oriented keywords: class, interface, extends, implements, this, super
- Exception handling: try, catch, finally, throw, throws
- Others: void, null, true, false
Java has a total of 50+ keywords, and each serves a specific role in program flow, object creation, and behavior definition.
Identifiers in Java
In Java, identifiers are names given to various elements in a program, such as classes, methods, variables, and packages. Identifiers are essential as they help distinguish different parts of a program and make code more readable. They are defined by the programmer and must follow specific rules and conventions.
Examples of Identifiers
- Class name: `MyClass`
- Variable name: `totalSum`
- Method name: `calculateInterest`
- Package name: `com.example.application`
Rules and Conventions for Identifiers
Rules (mandatory):
1. Character Set: Identifiers can include letters (A-Z, a-z), digits (0–9), dollar signs (`$`), and underscores (`_`).
- Example: `myValue`, `total_sum`, `$price`
2. No Spaces: Spaces are not allowed within identifiers.
- Invalid Example: `my Value`, `public static void display details`
- Valid Example: `myValue`, `displayDetails`
3. Cannot Start with Digits: Identifiers cannot begin with a number.
- Invalid Example: `1stVariable`, `10a`
- Valid Example: `variable1`, `a10`
4. Cannot Use Keywords: Keywords in Java, such as `int`, `class`, `static`, etc., cannot be used as identifiers.
- Invalid Example: `class className {}`, `int static;`
- Valid Example: `className`, `isStatic`
Conventions (recommended but not enforced):
1. Class Names: Begin with an uppercase letter. If the name contains multiple words, capitalize the first letter of each word (Pascal case).
- Example: `MyFirstProgram`, `CustomerOrder`
2. Variable and Method Names: Begin with a lowercase letter. If the name contains multiple words, capitalize the first letter of each subsequent word (camel case).
- Example: `totalAmount`, `calculateInterest`
3. Package Names: Use lowercase letters. If multiple words are used, separate them by dots.
- Example: `com.mycompany.application`
4. Constants: Use uppercase letters with words separated by underscores for constant values.
- Example: `MAX_HEIGHT`, `MIN_AGE`
Important Notes
1. Default Access Modifier: Java does not have a keyword `default` as an access modifier, but it exists as a meaning. For example:
2. Non-Static Members: The term “non-static” isn’t a keyword in Java, but any member that is not defined with the `static` keyword is implicitly non-static. For example:
3. Java Keywords: All Java keywords are lowercase, so any identifier starting with uppercase letters will not conflict with keywords.
Rules for Multi-Word Identifiers
1. No Spaces in Identifiers: If the identifier contains multiple words, use conventions like Pascal case for classes and camel case for variables and methods.
- Invalid Example: `class My program {}`
- Valid Example: `class MyProgram {}`
2. Method and Variable Naming Conventions: When using multiple words in method or variable names, capitalize the first letter of each subsequent word (camel case).
- Example: `int myTotalMarks;`
- Method Example: `public void calculateInterest() {}`
Declaring Variables
Variables in Java can be declared with the following syntax:
<data_type> <variable_name>;For example:
int number;
String name;Final Keyword
The `final` keyword in Java is used to create constants. When a variable is declared as `final`, its value cannot be changed once it is initialized. Attempting to modify a `final` variable will result in a compilation error.
Example of a final variable:
class Jtc {
public static void main(String[] args) {
final int a = 10;
System.out.println(a);
// a = 4000; // Error: cannot assign a value to a final variable
// a = -56; // Error: cannot assign a value to a final variable
}
}Explanation:
- The variable `a` is declared as `final` and initialized with a value of `10`.
- Once `a` is declared as `final`, any attempt to modify its value (e.g., `a = 4000` or `a = -56`) will result in a compilation error.
Types of Variables in Java
Variables in Java can be classified into the following types:
1. Class Level Variables
2. Local Variables
Class Level Variables
- Class level variables are declared directly within a class but outside of any method, constructor, or block.
- They can be accessed from any method or block within the class.
- Memory for class-level variables is allocated in the heap.
- If not initialized, they are assigned default values by the Java Virtual Machine (JVM).
Class level variables are further divided into:
1. Instance Variables: Variables that are specific to each object of a class.
2. Static Variables: Variables that are shared among all objects of a class.
Example of Class Level Variables:
public class JtcClassLevelVariableExample {
public static void main(String[] args) {
Variable vl = new Variable();
vl.variable_m2();
}
}
class Variable {
// Class level variables
byte b;
short s;
int i;
long l;
float f;
double d;
boolean bl;
char c;
void variable_m2() {
System.out.println("byte : " + b);
System.out.println("short : " + s);
System.out.println("int : " + i);
System.out.println("long : " + l);
System.out.println("float : " + f);
System.out.println("double : " + d);
System.out.println("boolean : " + bl);
System.out.println("char : " + c);
}
}Output:
byte : 0
short : 0
int : 0
long : 0
float : 0.0
double : 0.0
boolean : false
char :Explanation:
- The variables are not explicitly initialized, so the JVM assigns them default values.
- The default values depend on the data type (e.g., `0` for integers, `false` for boolean, etc.).
Let’s break down the conclusion points regarding static and non-static members in Java, explaining each point in detail.
Conclusion
1. Static Variables Can Be Accessed Directly in Static Methods
- Static variables belong to the class rather than any specific instance. Therefore, they can be accessed directly within static methods, as both are associated with the class.
2. Static Variables Can Be Accessed Directly in Non-Static Methods
- Non-static methods are associated with instances of the class but still have access to static variables. They can directly access static variables without needing to create an instance.
3. Non-Static Variables Cannot Be Accessed Directly in Static Methods
- Non-static variables belong to specific instances of a class. Static methods do not have an instance context, so they cannot access non-static variables directly.
4. Non-Static Variables Can Be Accessed Directly in Non-Static Methods
- Non-static methods have access to both static and non-static variables. They can access non-static variables directly because they operate within the context of an instance.
5. Static Methods Can Be Called Directly from Static Methods
- Since both methods are associated with the class itself, a static method can directly call another static method without creating an instance.
6. Static Methods Can Be Called Directly from Non-Static Methods
- Non-static methods can call static methods directly because they have access to the class context, which includes static methods.
7. Non-Static Methods Cannot Be Called Directly from Static Methods
- Static methods do not have access to instance-specific data, so they cannot call non-static methods directly without an instance of the class.
8. Non-Static Methods Can Be Called Directly from Non-Static Methods
- Non-static methods can freely call each other because they operate within the context of the same instance of the class.
Local Variables
- Local variables are declared within a method, constructor, or block.
They must be initialized before use, as they do not have default values.
Is Intialisation of local variable is mandatory ??
Yes,it is mandatory to intialise local variables, if we did not intialise we will get compile time error saying that“Variable might not intialised”.
- Memory for local variables is allocated only when the method or block is executed and is released when it ends.
-Local variables can only be default or final i.e we cannot declare a local variable as private,public or protected
Example of Local Variables:
package com.jtcindia.org;
class LocalVariable {
int a = 10; // Class level variable
void localVariable_m1() {
int b = 20; // Local variable
System.out.println("--localVariable_m1() in LocalVariable class--");
System.out.println(b);
}
void localVariable_m2() {
int c = 30; // Local variable
System.out.println("--localVariable_m2() in LocalVariable class--");
System.out.println(c);
}
void localVariable_m3() {
int d = 40; // Local variable
System.out.println("--localVariable_m3() in LocalVariable class--");
System.out.println(d);
}
}
public class Jtc {
public static void main(String[] args) {
System.out.println(new LocalVariable().a);
new LocalVariable().localVariable_m1();
new LocalVariable().localVariable_m2();
new LocalVariable().localVariable_m3();
}
}Output:
10
- localVariable_m1() in LocalVariable class -
20
- localVariable_m2() in LocalVariable class -
30
- localVariable_m3() in LocalVariable class -
40Explanation:
- The variable `a` is a class level variable, while `b`, `c`, and `d` are local variables declared within different methods.
- Local variables must be explicitly initialized before they are used.
Key Differences Between Class Level and Local Variables:
Q. What if local variable and global variable names are same?
A. If local var and global var names are same first priority is always given to local variables because they are inside method and nearer for execution to JVM.
Conclusion
- Class Level Variables are used when you need to store data that can be accessed across multiple methods.
- Local Variables are used for temporary storage of data within a specific method or block.
- Using the `final` keyword makes a variable a constant that cannot be changed after its initial assignment.
Constants in Java
Constants are fixed values that cannot be changed once assigned. Java supports constants using the final keyword. A final variable cannot be re-assigned a new value after initialization.
Example:
final int MAX_VALUE = 100;Once you declare MAX_VALUE as final, it cannot be modified.
Final Variables in Java
A final variable is a constant, and its value can’t be changed once it’s initialized.
Can we change the value of a final variable?
No, you cannot change the value of a final variable once it’s initialized. If you try to, the compiler will throw an error.
Is it necessary to initialize the value of a final variable at the time of declaration?
No, it’s not necessary to initialize a final variable at the time of declaration. However, it must be initialized before the constructor finishes execution, ensuring that the final variable gets a value before the object is fully constructed.
This covers the basics of Java character set, data types, keywords, identifiers, variables, constants, and final variables. Let me know if you need more detail on any section!
Java Literals
Literals in Java are values that can be directly assigned to variables. These values can be used in the program as per the requirement. They come in different types, and each type has its own rules for representation.
Types of Literals:
1. Integral Literals
2. Floating-Point Literals
3. Boolean Literals
4. Character Literals
5. Null Literals
6. String Literals
1. Integral Literals
Integral literals are exact numeric values without a decimal point. They can be positive or negative, such as `234`, `-56`, `+45`. Integral literals are represented using different number systems:
- Decimal literals: A standard number system (0–9). It should not start with `0`.
- Octal literals: Values represented in base 8 (digits 0–7) and must start with `0`.
- Hexadecimal literals: Represented in base 16 (0–9 and a-f/A-F). It must start with `0x` or `0X`.
- Binary literals: Introduced in JDK 1.7, uses base 2 (0 and 1) and must start with `0b` or `0B`.
Example:
public class IntegralLiterals {
public static void main(String[] args) {
int decimal = 12345; // Valid decimal literal
int octal = 012345; // Valid octal literal, starts with 0
// int invalidOctal = 018; // Not valid, 8 is not an octal digit
int hex = 0xABC; // Valid hexadecimal literal, starts with 0x
// int invalidHex = 123AF; // Not valid, missing 0x prefix for hexadecimal
int binary = 0b10101; // Valid binary literal, starts with 0b
// int invalidBinary = 2b10101; // Not valid, must start with 0b
System.out.println("Decimal: " + decimal);
System.out.println("Octal: " + octal);
System.out.println("Hexadecimal: " + hex);
System.out.println("Binary: " + binary);
}
}2. Floating-Point Literals
Floating-point literals represent values with a decimal point. They can be specified using `float` or `double` data types:
- A float literal should end with `F` or `f`.
- A double literal can optionally end with `D` or `d`, but it is the default for decimal values.
- Scientific notation is also supported using `E` or `e`.
Example:
public class FloatingPointLiterals {
public static void main(String[] args) {
double d1 = 12.34; // Valid double literal
float f1 = 12.34f; // Valid float literal with suffix 'f'
// float invalidFloat = 12.34; // Not valid, float requires 'f' suffix
double scientificNotation = 1.23e2; // Valid scientific notation
System.out.println("Double d1: " + d1);
System.out.println("Float f1: " + f1);
System.out.println("Scientific Notation: " + scientificNotation);
}
}3. Boolean Literals
Boolean literals have only two values: `true` or `false`. In Java, you cannot represent `true` by `1` and `false` by `0`.
Example:
public class BooleanLiterals {
public static void main(String[] args) {
boolean isJavaFun = true; // Valid boolean literal
boolean isFishTasty = false; // Valid boolean literal
// boolean invalidBoolean = 1; // Not valid, Java does not accept 1 as true
// boolean anotherInvalidBoolean = 0; // Not valid, Java does not accept 0 as false
System.out.println("Is Java fun? " + isJavaFun);
System.out.println("Is fish tasty? " + isFishTasty);
}
}4. Character Literals
Character literals are enclosed in single quotes, like ’A’. Rules for character literals include:
- They cannot be empty.
- They must contain only one character.
- Special characters can be represented using escape sequences like `\n`, `\t`, etc.
public class CharacterLiterals {
public static void main(String[] args) {
char ch1 = 'A'; // Valid character literal
char ch2 = ' '; // Valid character literal with space
char ch3 = '\n'; // Valid escape sequence for new line
// char invalidChar1 = ''; // Not valid, empty character literal
// char invalidChar2 = 'AB'; // Not valid, multiple characters in single quotes
System.out.println("Character ch1: " + ch1);
System.out.println("Character ch2 (space): [" + ch2 + "]");
System.out.println("Character ch3 (newline): [" + ch3 + "]");
}
}- Escape sequences: Character literals can include escape sequences like \n (new line), \t (tab), \’ (single quote), \” (double quote), \\ (backslash), etc.
char newLine = '\n';
char singleQuote = '\'';- Unicode characters: You can also use Unicode values for characters by using the \u prefix followed by the 4-digit hexadecimal value.
char unicodeChar = '\u0041'; // Unicode representation of 'A'5. Null Literals
Null literals represent the absence of a value for reference types. They are used to signify that a variable does not reference any object.
public class NullLiterals {
String str;
NullLiterals nl;
System s1;
public static void main(String[] args) {
NullLiterals obj = new NullLiterals();
System.out.println("str: " + obj.str); // Prints null
System.out.println("nl: " + obj.nl); // Prints null
System.out.println("s1: " + obj.s1); // Prints null
}
}6. String Literals
String literals are enclosed in double quotes, like `”Hello”`. Strings are not primitive data types but are treated as objects of the `String` class.
Examples
public class StringLiterals {
public static void main(String[] args) {
String s1 = "Hello, World!"; // Valid string literal
String s2 = ""; // Valid empty string
String s3 = "Hello \n Java"; // Valid string with escape sequence
String s4 = "C:\\Program Files\\Java"; // Valid string with escaped backslashes
// String invalidString = "Hello"; // Incorrect escape sequence
System.out.println("String s1: " + s1);
System.out.println("String s2 (empty): [" + s2 + "]");
System.out.println("String s3 (with newline): " + s3);
System.out.println("String s4 (with path): " + s4);
}
}Explanation:
String invalidString = "Hello\";: This is incorrect due to improper use of escape sequences. In Java, double quotes inside strings must be escaped using\".String invalidString = "Hello\";: This is incorrect due to improper use of escape sequences. In Java, double quotes inside strings must be escaped using\".
- Underscore in numeric literals: Since Java 7, you can use underscores to improve the readability of large numbers.It should be not be used before or just after decimal.
int largeNumber = 1_000_000; // Same as 1000000Detailed Examples of Java Literals
public class LiteralExample {
public static void main(String[] args) {
// Integer Literals
int decimal = 100; // Decimal literal
int octal = 0144; // Octal literal
int hex = 0x64; // Hexadecimal literal
int binary = 0b1100100; // Binary literal
// Floating-point Literals
double pi = 3.14159; // Double literal (default)
float gravity = 9.81f; // Float literal
// Character Literals
char letter = 'A'; // Character literal
char newLine = '\n'; // Escape sequence
char unicodeChar = '\u0041'; // Unicode literal
// String Literals
String message = "Hello, World!"; // String literal
String empty = ""; // Empty string
// Boolean Literals
boolean isJavaFun = true; // Boolean literal (true)
boolean isTired = false; // Boolean literal (false)
// Null Literal
String nullString = null; // Null literal
// Printing literals
System.out.println("Decimal Literal: " + decimal);
System.out.println("Octal Literal: " + octal);
System.out.println("Hexadecimal Literal: " + hex);
System.out.println("Binary Literal: " + binary);
System.out.println("Double Literal: " + pi);
System.out.println("Float Literal: " + gravity);
System.out.println("Character Literal: " + letter);
System.out.println("New Line: " + newLine);
System.out.println("Unicode Literal: " + unicodeChar);
System.out.println("String Literal: " + message);
System.out.println("Empty String Literal: " + empty);
System.out.println("Boolean Literal (true): " + isJavaFun);
System.out.println("Boolean Literal (false): " + isTired);
System.out.println("Null Literal: " + nullString);
}
}Output:
Decimal Literal: 100
Octal Literal: 100
Hexadecimal Literal: 100
Binary Literal: 100
Double Literal: 3.14159
Float Literal: 9.81
Character Literal: A
New Line:
Unicode Literal: A
String Literal: Hello, World!
Empty String Literal:
Boolean Literal (true): true
Boolean Literal (false): false
Null Literal: null- Integer Literals: Represent whole numbers and can be in decimal, octal, hexadecimal, or binary format.
- Floating-point Literals: Represent numbers with fractional parts, default to double, and use the f suffix for float.
- Character Literals: Represent single characters and can include escape sequences and Unicode characters.
- String Literals: Represent sequences of characters, enclosed in double quotes.
- Boolean Literals: Represent logical values (true or false).
- Null Literal: Represents a null reference or no object.
Each type of literal has its purpose and allows you to express values clearly in Java code.
Key Points:
- No Empty Character Literal: You cannot create an empty character literal. For instance, char c = ‘’ will cause a compile-time error.
// This will give a compile-time error
char emptyChar = ‘’; // Error: unexpected type
- Valid Examples:
char letter = ‘a’;
char digit = ‘1’;
char symbol = ‘$’;
2. String Literals
Key Points:
- Strings are Objects: Unlike character literals, string literals are objects of the String class, which is part of the java.lang package.
String greeting = “Hello, World!”;
- Output: When printed, the string literal displays the text on the console.
System.out.println(greeting); // Output: Hello, World!
- Escape Sequences: Certain characters in strings need to be escaped, such as newlines (\n), tabs (\t), or quotes (\”).
String withEscape = “Line1\nLine2”; // \n creates a new line
System.out.println(withEscape);
3. Handling Null and Reference Variables
- Character Reference Variable: If you declare a character reference variable, you can assign it null, which indicates it doesn’t point to any character.
Character ch6 = null; // Valid: ch6 is a reference variable
- Null Assignment: Unlike primitive types, you can assign null to an object reference, which means it currently does not reference any object.
4. Compiler Behavior and Comments
- Comments: Lines that are commented out (using // or / /) are ignored by the compiler in terms of execution, but the compiler does not validate the syntax of these comments.
// This line is a comment and will not affect compilation
// char invalidChar = ‘’; // Error: unexpected type
Summary
- Character Literals: Use single quotes for single characters. No empty literals allowed.
- String Literals: Use double quotes. Strings are objects of the String class.
- Escape Sequences: Use backslashes to represent special characters.
- Reference Variables: You can assign null to reference variables, which is different from primitive types.
Operators in Java
Operators are symbols used to perform operations on variables and values. Operators act on operands (the values they manipulate) and return a result. There are several types of operators in Java, each serving different purposes.
1. Arithmetic Operators
Arithmetic operators perform basic mathematical operations such as addition, subtraction, multiplication, division, and modulus.
Common Arithmetic Operators:
- + : Addition
- — : Subtraction
- : Multiplication
- / : Division
- % : Modulus (returns remainder of division)
Example:
public class ArithmeticOperator {
public static void main(String[] args) {
int a = 100;
byte b = 20;
System.out.println(a + b); // 120
System.out.println(a - b); // 80
System.out.println(a * b); // 2000
System.out.println(a / b); // 5
System.out.println(a % b); // 0
}
}In the above code, basic arithmetic operations are performed on two variables a and b.
Arithmetic with Boolean:
Arithmetic operations cannot be performed on non-numeric types like boolean. Attempting to do so will result in a compilation error.
public class ArithmeticOperator {
public static void main(String[] args) {
int a = 100;
boolean b = true;
System.out.println(a + b); // Error: incompatible types
}
}Note:-
-The issue with performing byte b11 = b1 + b2 in Java (or similar languages) arises from the fact that the result of the addition of two byte values is promoted to an int. This means that the sum of b1 and b2 can exceed the range of a byte, which is from -128 to 127.
Here’s what happens in detail:
1. Promotion to int: When you add two byte values, Java first converts them to int. This is part of Java’s rules for arithmetic operations involving smaller data types.
2. Potential overflow: Since the result is an int, it can exceed the range of a byte. If the sum is greater than 127 or less than -128, you can’t directly assign that int value back to a byte without an explicit cast, which can lead to loss of data.
To correctly perform this operation while ensuring that it fits within the byte range, you should do the following:
byte b11 = (byte) (b1 + b2);This way, you’re explicitly casting the result back to a byte, but be aware that if the sum exceeds the byte range, it will wrap around due to overflow.
-When you add two char values in Java, they are treated similarly to byte values in that they undergo numeric promotion. Here’s what happens:
1. Promotion to int: When you add two char values, they are first promoted to int. This means the addition is performed using their integer values (which correspond to their Unicode code points).
2. Result type: The result of the addition will be of type int. This can exceed the range of char, which is from 0 to 65535 (since char in Java is unsigned).
3. No implicit casting: You cannot directly assign the result of the addition back to a char without an explicit cast. If you try to do something like:
char c1 = 'A'; // Unicode 65
char c2 = 'B'; // Unicode 66
char c3 = c1 + c2; // This will cause a compilation errorThis will result in a compilation error because you’re trying to assign an int to a char.
4. Correct way: If you want to store the result in a char, you should explicitly cast it:
char c3 = (char) (c1 + c2); // c3 will be assigned the value of 131 (which corresponds to '')5. Handling overflow: Be mindful that if the sum exceeds 65535, the resulting value will wrap around due to overflow, which may lead to unexpected characters.
In summary, adding two char values results in an int, and you need to handle the conversion back to char explicitly, being cautious of the range and potential overflow.
Operator Overloading in Java
Definition: Operator overloading is a feature that allows operators to have different meanings based on the context in which they are used. In many programming languages, this means that the same operator can perform different operations depending on the types of its operands.
Java’s Approach
Java does not support operator overloading in the way that some other languages (like C++ or Python) do. This design decision was made to keep the language simple and avoid ambiguity. However, the `+` operator is an exception, as it can serve two different roles: addition and concatenation.
1. Addition with the `+` Operator
When used with numeric types, the `+` operator performs addition. This applies to various numeric data types:
- Integers:
- Example:
int result = 122 + 233; // result = 355- Floating Point Numbers:
- Example:
double result = 445.33 + 556; // result = 1001.33- Mixed Types:
- Java automatically converts types to perform the operation:
float result = 667 + 45.3f; // result = 712.3Numeric Type Conversions
Java supports arithmetic operations between different numeric types, like `int`, `float`, `double`, etc. The following rules apply:
- When two different numeric types are used, Java converts them to the larger type before performing the operation (e.g., `int` to `double`).
2. Character Addition
Java treats characters as numeric values based on their Unicode representations.
- Character Arithmetic:
- Characters can be added together:
char ch = 'A'; // Unicode 65
int sum = ch + 65; // 65 + 65 = 130 widen to int
System.out.println(sum); // Prints 130
char ch1 = 'a'; // Unicode 97
char ch2 = 'z'; // Unicode 122
int combined = ch1 + ch2; // 97 + 122 = 219
System.out.println(combined); // Prints 219- Casting Character to Char:
- If you assign a numeric value to a `char`, it will represent the corresponding character:
char ch = 65; // 'A'
System.out.println(ch); // Prints 'A'3. Concatenation with the `+` Operator
When one of the operands is a `String`, the `+` operator behaves as a concatenation operator. This means it combines strings instead of performing arithmetic addition.
- String Concatenation:
- Example:
String s = "java";
int a = 123;
System.out.println(s + a); // "java123"- Multiple Strings:
- Concatenation can be chained:
System.out.println("I am " + s + " developer"); // "I am java developer"- Result Type:
- After concatenation, the result is always a `String`, even if the other operand is a number:
System.out.println(123 + " "); // "123 "Type Casting in Java
Type casting in Java is the process of converting a variable from one data type to another. Java supports two types of casting:
1. Widening Casting (Automatic): Converting a smaller data type to a larger data type (e.g., byte to int). This is done automatically by Java without any explicit cast.
2. Narrowing Casting (Explicit): Converting a larger data type to a smaller data type (e.g., int to byte). This requires an explicit cast because it can lead to data loss.
Example: byte b1 = 10;
Here, you are declaring a byte variable b1 and initializing it with the value 10. The byte data type can hold values from -128 to 127.
Case: b1 += 10;
Now, when you perform the operation b1 += 10, here’s what happens:
1. Promotion: The += operator is a compound assignment operator. When you use b1 += 10, Java first promotes b1 to an int to perform the addition. This is because the right-hand side (10 in this case) is treated as an int.
2. Result Calculation: The addition is performed: 10 + 10, which equals 20.
3. Assignment: The result of the addition (20, which is an int) is then assigned back to b1. However, since b1 is a byte, Java automatically casts the int result back to byte.
4. No Explicit Cast Needed: In this case, no explicit cast is necessary because the result (20) is within the range of byte (-128 to 127).
Conclusion
- The operation b1 += 10 is valid and results in b1 holding the value 20.
- If you were to use a regular addition, like b1 = b1 + 10;, this would require an explicit cast back to byte because the expression b1 + 10 results in an int. Thus, it would look like this:
b1 = (byte) (b1 + 10);In summary, b1 += 10 is a shorthand that handles type promotion and casting automatically, making it convenient for operations like this.
2. Assignment Operator (=)
The assignment operator assigns the value on the right-hand side (source) to the left-hand side (destination). The general syntax is:
destination = source;Example:
public class AssignmentOperator {
public static void main(String[] args) {
int a = 10;
int b = a; // Assign value of 'a' to 'b'
System.out.println(b); // 10
}
}Type Casting
Type casting is the process of converting one data type into another. It can be:
- Implicit Type Casting: Happens automatically when assigning a smaller type to a larger type.
- Explicit Type Casting: Must be specified by the programmer when assigning a larger type to a smaller type.
Implicit Casting Example:
public class AssignmentOperator {
public static void main(String[] args) {
byte b1 = 10;
short s1 = b1; // Implicit casting: byte to short
System.out.println(s1); // 10
}
}Explicit Casting Example:
public class AssignmentOperator {
public static void main(String[] args) {
short s2 = 260;
byte b2 = (byte) s2; // Explicit casting: short to byte
System.out.println(b2); // Output may show data loss (result: 4)
}
}Since byte can only hold values from -128 to 127, converting 260 causes data loss.
3. Relational Operators
Relational operators are used to compare two values. They return a boolean value (true or false).
Relational Operators:
- > : Greater than
- < : Less than
- >= : Greater than or equal to
- <= : Less than or equal to
- == : Equal to
- != : Not equal to
Example:
public class RelationalOperator {
public static void main(String[] args) {
int a = 20;
int b = 10;
System.out.println(a > b); // true
System.out.println(a < b); // false
System.out.println(a == b); // false
}
}Relational Operators with Boolean:
Relational operators cannot be used with boolean values.
public class RelationalOperator {
public static void main(String[] args) {
boolean a = true;
boolean b = false;
System.out.println(a > b); // Error: cannot compare booleans
}
}4. Logical Operators
Logical operators are used to perform logical operations on boolean expressions. They are:
- && : Logical AND
- || : Logical OR
- ! : Logical NOT
Example:
public class LogicalOperators {
public static void main(String[] args) {
boolean x = true;
boolean y = false;
System.out.println(x && y); // false
System.out.println(x || y); // true
System.out.println(!x); // false
}
}Logical operators can be used to combine multiple conditions.
5. Bitwise Operators
Bitwise operators work on binary representations of integers. They perform bit-level operations.
Common Bitwise Operators:
- & : Bitwise AND
- | : Bitwise OR
- ^ : Bitwise XOR
- << : Left shift
- >> : Right shift
- ~ : Bitwise NOT
Example:
public class BitwiseOperator {
public static void main(String[] args) {
int number1 = 5; // 00000101 in binary
int number2 = 7; // 00000111 in binary
System.out.println(number1 & number2); // Bitwise AND: 00000101 -> 5
System.out.println(number1 | number2); // Bitwise OR: 00000111 -> 7
System.out.println(number1 ^ number2); // Bitwise XOR: 00000010 -> 2
}
}The XOR (exclusive OR) operation is a fundamental binary operation that compares two bits and produces a result based on their values. Here’s how it works:
- Input: Two bits (0 or 1).
- Output: The result is 1 if the bits are different, and 0 if they are the same.
Truth Table for XOR
Explanation
- If both bits are 0 (0 XOR 0), the result is 0.
- If one bit is 0 and the other is 1 (0 XOR 1 or 1 XOR 0), the result is 1.
- If both bits are 1 (1 XOR 1), the result is 0.
Properties of XOR
1. Commutative: A XOR B = B XOR A
2. Associative: (A XOR B) XOR C = A XOR (B XOR C)
3. Identity: A XOR 0 = A
4. Self-inverse: A XOR A = 0
Example
Let’s say you want to perform XOR on two 4-bit binary numbers:
- A = 1101
- B = 1011
You would compare each corresponding bit:
1 1 0 1
XOR
1 0 1 1
-----------
0 1 1 0So, 1101 XOR 1011 = 0110.
Note:-
The logical AND (&&) and bitwise AND (&) operators serve different purposes and are used in different contexts in programming. Here’s a breakdown of their differences:
Logical AND (&&)
1. Usage: Primarily used with boolean expressions.
2. Short-circuiting: If the first operand evaluates to false, the second operand is not evaluated. This means if the first condition is false, the overall expression is guaranteed to be false, and the second condition is not checked.
3. Return type: Always returns a boolean value (true or false).
4. Example:
boolean result = (a > 0) && (b < 10);If a is not greater than 0, the condition (b < 10) won’t be evaluated.
Bitwise AND (&)
1. Usage: Can be used with integer types (like int, byte, char, etc.) as well as boolean types.
2. No short-circuiting: Both operands are always evaluated, regardless of the value of the first operand. This means that both bits (or boolean values) are always checked.
3. Return type: When used with integers, it returns an integer; when used with booleans, it returns a boolean.
4. Example with integers:
int result = a & b; // Performs a bitwise AND on the binary representations
// of a and b.If a is 5 (binary 0101) and b is 3 (binary 0011), the result would be 1 (binary 0001).
6. Ternary Operator (?:)
This operator is a shorthand for if-else statements. It evaluates a boolean expression and returns one of two values depending on whether the expression is true or false.
Syntax:
condition ? value_if_true : value_if_false;Example:
public class TernaryOperator {
public static void main(String[] args) {
int a = 10, b = 20;
int result = (a > b) ? a : b; // Returns b if a is not greater than b
System.out.println(result); // 20
}
}Example of a Nested Ternary Operator
Here’s a simple example that determines a grade based on a score:
int score = 85;
String grade = (score >= 90) ? "A" :
(score >= 80) ? "B" :
(score >= 70) ? "C" :
(score >= 60) ? "D" : "F";
System.out.println("Grade: " + grade);Explanation
- First Condition: If score >= 90, the grade is ”A”.
- Second Condition: If the first condition is false, it checks score >= 80. If true, the grade is ”B”.
- Third Condition: If both previous conditions are false, it checks score >= 70. If true, the grade is ”C”.
- Fourth Condition: If all previous conditions are false, it checks score >= 60. If true, the grade is ”D”.
- Else: If none of the conditions are true, the grade is ”F”.
Output
For a score of 85, the output will be:
Grade: B7. Increment/Decrement Operator
These are used to increase or decrease a value by one.
- ++ : Increment
- — : Decrement
They can be used in two forms:
- Pre-Increment/Pre-Decrement: ++a or — a
- Post-Increment/Post-Decrement: a++ or a —
Example:
public class IncrementDecrement {
public static void main(String[] args) {
int a = 10;
System.out.println(++a); // Pre-increment: 11
System.out.println(a++); // Post-increment: 11 (prints before increment)
System.out.println(a); // 12
}
}Note :-
1. Using 10++
In programming languages like Java, the expression 10++ is invalid. Here’s why:
- Literal Value: 10 is a literal constant, and you cannot increment a literal value. The increment operator (++) is intended to be used with variables, not constants.
- Increment Operator: The ++ operator increases the value of a variable by 1. Since 10 cannot be modified (it’s a fixed value), attempting to apply ++ to it results in a compilation error.
Example of Correct Usage:
Instead, you should use the increment operator with a variable:
int num = 10;
num++; // This is valid and increments num to 11.2. Can it be Used with a final Variable?
You cannot use the increment operator with a final variable in Java. A final variable is a constant that cannot be modified once assigned. Therefore, attempting to use ++ on a final variable will result in a compilation error.
Example:
final int num = 10;
num++; // This will cause a compilation error.3. What About ++i++?
The expression ++i++ is also invalid. Here’s why:
- Postfix and Prefix Increment: The expression tries to apply both prefix (++i) and postfix (i++) increment operators on the same variable i in a single statement. This is not allowed because the increment operators are not meant to be nested like this.
- Evaluation Order: The postfix increment (i++) returns the current value of i before incrementing, while the prefix increment (++i) increments the value of i before returning it. Combining these leads to ambiguity.
Example:
int i = 5;
++i++; // This will cause a compilation error.The new operator is used to create new objects in Java.
Example:
public class NewOperator {
public static void main(String[] args) {
String str = new String("Hello");
System.out.println(str);
}
}9. instanceof Operator
The instanceof operator checks whether an object is an instance of a specific class or subclass.
Example:
public class NewOperator {
public static void main(String[] args) {
String str = new String("Hello");
System.out.println(str);
}
}In Java, both instanceof and the isInstance method (part of the Class class) are used to check if an object is an instance of a specified class. However, they have different syntax and use cases. Here’s a breakdown of their differences:
instanceof
1. Syntax:
object instanceof ClassName2. Usage:
- Used in conditional expressions (like if statements).
- Directly checks whether the object is an instance of a specified class or its subclasses.
3. Example:
class Animal {}
class Dog extends Animal {}
Animal animal = new Dog();
if (animal instanceof Dog) {
System.out.println("animal is a Dog");
}4. Compile-time Check:
- The type on the right side of instanceof is checked at compile time for type safety.
- If the left operand is not compatible with the right operand, it results in a compile-time error.
isInstance
1. Syntax:
ClassName.class.isInstance(object)2. Usage:
- A method of the Class class, allowing for more dynamic type checking.
- Useful when the class type is not known at compile time, such as when working with reflection or generic programming.
3. Example:
Class<?> clazz = Dog.class;
Animal animal = new Dog();
if (clazz.isInstance(animal)) {
System.out.println("animal is an instance of Dog");
}4. Dynamic Check:
- More flexible, as you can determine the class type at runtime, which is especially useful in situations involving reflection.
Key Differences
1. Syntax: instanceof is an operator, while isInstance is a method of the Class class.
2. Flexibility:
- isInstance can be more dynamic, allowing you to check against class types determined at runtime.
- instanceof is more straightforward and generally easier to read for simple type checks.
3. Compile-time vs. Runtime:
- instanceof performs a compile-time check for type safety.
- isInstance does not provide compile-time type safety, as the class type can be determined at runtime.
Summary
- Use instanceof for straightforward type checks when the class type is known at compile time.
- Use isInstance when you need dynamic type checking or when dealing with reflection, where the class type may not be known until runtime.
Control Statements in Java
In Java, control statements manage the flow of program execution. By default, the code execution follows a top-to-bottom order, but using control statements, you can alter this flow based on your requirements. Control statements are crucial for making decisions, repeating code, or jumping to specific parts of the program. Java control statements can be categorized into three main types:
1. Conditional Control Statements
Conditional statements execute code based on certain conditions (evaluated as true or false). If the condition is true, a specific block of code runs; if false, a different block may execute or the execution may skip that part entirely.
Types of Conditional Control Statements:
1. If Statement
- The simplest form of decision-making. It checks a condition and executes a block of code only if the condition is true.
Syntax:
if (condition) {
// block of code
}Example:
if (number1 > number2) {
System.out.println("number1 is greater than number2");
}2. If-Else Statement
- Extends the if statement by adding an else block that executes if the condition is false.
Syntax:
if (condition) {
// code if condition is true
} else {
// code if condition is false
}Example:
if (number1 < number2) {
System.out.println("number1 is less than number2");
} else {
System.out.println("number1 is greater than number2");
}3. Nested If Statement
- You can place an if statement inside another if to create complex decision-making scenarios.
Syntax:
if (condition1) {
if (condition2) {
// code if both condition1 and condition2 are true
}
}4. If-Else Ladder
- Multiple if-else statements chained together. Useful when you need to test several conditions.
Syntax:
if (condition1) {
// code if condition1 is true
} else if (condition2) {
// code if condition2 is true
} else {
// code if none of the above are true
}5. Switch Statement
- Useful when a variable can take multiple distinct values. The switch statement selects one of many blocks of code to execute.
Syntax:
switch (variable) {
case value1:
// code block
break;
case value2:
// code block
break;
default:
// code block
}Example:
switch (input) {
case "A":
// Add operation
break;
case "S":
// Subtract operation
break;
default:
// Invalid option
}In Java, the switch statement allows you to execute different blocks of code based on the value of a variable or expression. However, there are specific types of elements that are permitted in a switch statement:
1. Primitive Data Types
- int: You can use int (including byte, short, and char as they can be implicitly converted to int).
- char: The char type is allowed, representing single 16-bit Unicode characters.
- byte and short: Both can be used as they can fit within an int.
2. Wrapper Classes
- Integer: You can use Integer (the wrapper class for int).
- Character: You can use Character (the wrapper class for char).
3. String
- String: You can use String objects in a switch statement starting from Java 7. This allows for more readable code when working with string values.
4. Enums
- enum Types: You can use enum types, which are special Java types that represent a fixed set of constants. Each enum constant can be treated as a distinct case.
5. Disallowed Types
- float, double, long: These types are not allowed in a switch statement.
- boolean: The boolean type cannot be used in a switch statement.
- Any Object Type: While String and enums are allowed, other object types cannot be directly used.
Can We Use Variables in Case Statements?
In Java, you cannot use variables in the case labels of a switch statement. The case labels must be constant expressions, which means they must be known at compile time. This includes:
- Literals: Such as 1, ”Monday”, or Color.RED.
- Constant Variables: These are variables declared with the final keyword that have a fixed value.
Example of Valid Case Labels
final int ONE = 1;
final int TWO = 2;
int number = 2;
switch (number) {
case ONE: // Valid
System.out.println("One");
break;
case TWO: // Valid
System.out.println("Two");
break;
default:
System.out.println("Not One or Two");
}Example of Invalid Case Labels
int variable = 3;
switch (number) {
case variable: // Invalid: cannot use variable in case label
System.out.println("This will cause a compile-time error.");
break;
}Example of a Switch Statement
Here’s an example using different allowed types:
int number = 2;
switch (number) {
case 1:
System.out.println("One");
break;
case 2:
System.out.println("Two");
break;
default:
System.out.println("Not One or Two");
}
String day = "Monday";
switch (day) {
case "Monday":
System.out.println("Start of the week");
break;
case "Friday":
System.out.println("End of the week");
break;
default:
System.out.println("Midweek day");
}
enum Color { RED, GREEN, BLUE }
Color color = Color.RED;
switch (color) {
case RED:
System.out.println("Red color");
break;
case GREEN:
System.out.println("Green color");
break;
}2. Looping Control Statements
These statements allow executing a block of code repeatedly until a certain condition is met or a certain number of times.
Types of Looping Control Statements:
1. For Loop
- Repeats a block of code a specific number of times.
Syntax:
for (initialization; condition; update) {
// code block
}Example:
for (int i = 1; i <= 100; i++) {
if (i % 7 == 0) {
sum += i;
}
}2. While Loop
- Repeats code while a condition is true. It checks the condition before executing the loop body.
Syntax:
while (condition) {
// code block
}Example:
while (i <= 10) {
System.out.println("2 x " + i + " = " + (2 * i));
i++;
}3. Do-While Loop
- Similar to the while loop, but it checks the condition after executing the loop body, ensuring it runs at least once.
Syntax:
do {
// code block
} while (condition);Example:
do {
System.out.println("Count is: " + count);
count++;
} while (count < 11);3. Unconditional Control Statements
These statements cause the program to jump from one point to another unconditionally. They don’t check for conditions to decide the flow.
Types of Unconditional Control Statements:
1. Break Statement
- Used to exit a loop or switch statement prematurely.
Example:
for (int i = 0; i <= 10; i++) {
if (i == 5) {
break;
}
System.out.println("i -> " + i);
}2. Continue Statement
- Skips the current iteration of the loop and moves on to the next iteration.
Example:
for (int i = 1; i <= 10; i++) {
if (i == 5) {
continue; // skips when i is 5
}
System.out.print("i -> " + i);
}Summary:
- Conditional statements control the flow based on conditions (if, if-else, switch).
- Looping statements repeat code (for, while, do-while).
-Unconditional statements manage abrupt changes in control (break, continue).
Array in Java
An array is a collection of similar data types stored in contiguous memory locations. Java arrays are objects and offer a structured way to store and access large amounts of data efficiently. Below is a detailed explanation of arrays in Java:
Salient Features of Java Array:
- Index-based storage: Each element in the array is stored at a specific index, and indexing begins from 0. For example, the first element is stored at index 0, the second at index 1, and so on.
- Preserves order: Arrays in Java follow the order in which elements are added. The first element is stored at index 0, and each subsequent element is stored at the next index.
- Random Access: You can access any element directly using its index, making arrays suitable for applications where fast retrieval is important.
- Homogeneous data type: Arrays store elements of the same type (e.g., int[] stores integers).
- Heap memory storage: Arrays are objects in Java and are stored in heap memory, which is managed by the JVM.
- Dynamic memory allocation: The size of an array is determined at runtime. JVM calculates the memory needed and allocates accordingly.
- Array length: The length property holds the number of elements in the array. This is a fixed value once the array is created.
- Maximum size: The maximum size of an array in Java is ²³¹ — 1, which equals 2,147,483,647.
Types of Array Declaration in Java
1. Static Array:
The array is initialized at the time of declaration with known values.
Syntax:
<data_type> <array_name>[] = {value1, value2, value3, …};Example:
int[] arr = {10, 20, 30, 40, 50};In this example, arr is an integer array of length 5, and it holds the values 10, 20, 30, 40, and 50.
2. Dynamic Array:
The array is initialized without knowing the exact values but specifying the size, and values can be added later dynamically.
Syntax:
<data_type> <array_name>[] = new <data_type>[size];Example:
int[] arr = new int[5];In this case, arr is an array of integers with a length of 5, but initially, it holds the default values for int (which is 0).
Types of Arrays
1. Single-Dimensional Array
A single-dimensional array stores elements linearly.
Example:
public class Example {
public static void main(String[] args) {
int[] arr = {5, 6, 3, 2, 1};
for (int i = 0; i < arr.length; i++) {
System.out.println("Index: " + i + ", Value: " + arr[i]);
}
}
}Output:
Index: 0, Value: 5
Index: 1, Value: 6
Index: 2, Value: 3
Index: 3, Value: 2
Index: 4, Value: 1In the example above, an integer array arr is initialized, and each element is printed using a for-loop.
2. Multi-Dimensional Array
A multi-dimensional array stores references to other arrays, which allows for representing matrices or higher-dimensional data.
Two-Dimensional Array (2D Array):
Syntax:
<data_type> <array_name>[][] = {{value1, value2}, {value3, value4}, …};Example:
int[][] matrix = {{101, 202, 303}, {404, 505, 606}, {707, 808, 909}};
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
System.out.print("matrix[" + i + "][" + j + "] = " + matrix[i][j] + " ");
}
System.out.println();
}Output:
matrix[0][0] = 101 matrix[0][1] = 202 matrix[0][2] = 303
matrix[1][0] = 404 matrix[1][1] = 505 matrix[1][2] = 606
matrix[2][0] = 707 matrix[2][1] = 808 matrix[2][2] = 909Three-Dimensional Array (3D Array):
Syntax:
<data_type> <array_name>[][][] = {{{value1, value2}, {value3, value4}}, …};Example:
int[][][] arr = {{{1, 2, 3}, {4, 5, 6}}, {{7, 8, 9}, {10, 11, 12}}};
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
for (int k = 0; k < arr[i][j].length; k++) {
System.out.print(arr[i][j][k] + " ");
}
System.out.println();
}
}Output:
1 2 3
4 5 6
7 8 9
10 11 12Command-Line Arguments
In Java, the main method accepts an array of strings (String[] args). These strings are the command-line arguments passed to the program during execution.
Example:
public class CommandLineExample {
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
System.out.println("arg[" + i + "] = " + args[i]);
}
}
}Execution from Command Line:
java CommandLineExample Hello World 123Output:
arg[0] = Hello
arg[1] = World
arg[2] = 123Command-line arguments are passed as strings. If necessary, they can be converted into other data types using methods like Integer.parseInt(), Double.parseDouble(), etc.
Why the main method parameter is a String Array?
The JVM allows the main method to accept a String[] because strings are versatile and can represent any kind of input data (numbers, characters, booleans, etc.) in textual form. This allows the user to pass diverse data formats at runtime via the command line.
Object-Oriented Programming (OOP) in Java
1. Class:
A class in Java is a blueprint or template that defines the structure and behavior (data members and methods) for objects. It is a user-defined data type. The syntax for defining a class in Java is:
class <class_name> {
// Members (variables, methods, etc.)
}- Class members: The text lists members of a class, which include:
- Variables: Used to store data (e.g., int sid, String sname).
- Blocks: Sections of code (e.g., static blocks or instance initializer blocks).
- Methods: Functions that define behaviors.
- Constructors: Special methods that initialize objects.
- Enums: Used to define a set of named constants.
- Inner Classes: Classes defined within another class.
- Interfaces: Used to define a contract for implementing classes.
Example:
A class named Student is defined to represent a student and contains fields for storing student data (ID, name, and email):
class Student {
int sid;
String sname;
String semail;
}This class defines a category (students), and these fields represent the information to be stored for each student.
Points to Remember
1. Multiple Classes in a Program
- In Java, a program can consist of multiple classes. Each class defined will result in a separate `.class` file after compilation.
Example:
class A {
// Class A code
}
class B {
// Class B code
}
class User {
public static void main(String[] args) {
// Main method code
}
}- After Compilation: This program will generate three files:
- `A.class`
- `B.class`
- `User.class`
- File Naming Convention: The program should be saved as `User.java` since it contains the `main()` method, which serves as the entry point for execution.
2. Public Class and Main Method
- When multiple classes exist in a single program, only one class should be declared as `public`, and this class should contain the `main()` method.
- The filename must match the name of the public class.
Example:
class A {
public static void main(String[] args) {
// Code for class A
}
}
class B {
public static void main(String[] args) {
// Code for class B
}
}
public class User {
public static void main(String[] args) {
// Code for main logic
}
}- File Naming: This should be saved as `User.java`.
3. Executing Classes with Main Methods
- If there are multiple classes in a program, each with its own `main()` method, only one class should be public. Each class can still be executed individually if required.
Example:
class A {
public static void main(String[] args) {
System.out.println("Main method of class A");
}
}
class B {
public static void main(String[] args) {
System.out.println("Main method of class B");
}
}
public class C {
public static void main(String[] args) {
System.out.println("Main method of class C");
}
}- File Naming: Save as `C.java`.
- Execution: You can compile and execute each class individually:
- `java A`
- `java B`
- `java C`
4. Class Types
- Business Logic Class: A class that does not contain the `main()` method but contains business logic. These classes are often designed for specific tasks, such as data processing or calculations, and are used by user logic classes.
- User Logic Class: A class that contains the `main()` method. This is where the program execution begins and typically coordinates calls to business logic classes.
2. Object:
An object is an instance of a class. It is created dynamically and allocated memory on the heap. Each object stores its data and can access the class’s methods and attributes. Objects are referenced using reference variables.
- Object Creation: When we create an object, it is done using the new keyword.
Example:
Student s1 = new Student();- s1 is the reference variable that holds the memory address of the Student object.
- new Student() allocates memory for a Student object and initializes it.
Objects are used to store and manipulate class-specific data.
3. Class Loading Concept:
Class loading refers to the process of loading a class into Java Virtual Machine (JVM) memory. Java uses Class Loaders to load classes dynamically when they are required by the program. Classes are not loaded all at once, but only when they are needed during the program’s execution.
Situations when a class is loaded:
1. Object Creation: When an object of a class is created, the JVM checks if the class is already loaded. If not, the class loader loads it first.
Example:
Student s1 = new Student(); // loads the Student class if not already loaded2. Running a class with a main method: When the JVM starts an application, it loads the class containing the main() method and executes it.
3. Accessing static members: If a class’s static members (variables or methods) are accessed, the class is loaded if not already done so.
Dynamic Class Loading:
In Java, classes can be loaded dynamically at runtime using the Class.forName() method. This is useful when you want to load a class based on some runtime condition.
- The forName() method takes a class name as a string and loads that class into the JVM.
Example:
Class.forName("A");- This method attempts to load the class A into memory. If the class is found, it is loaded; otherwise, a ClassNotFoundException is thrown.
Static Block Execution:
When a class is loaded into memory, any static blocks (code inside static { } block) are executed immediately.
Example:
class A {
static {
System.out.println("SB in class A");
}
}When Class.forName(“A”) is called, the static block inside class A is executed, printing “SB in class A.”
4. ClassNotFoundException:
When attempting to load a class that doesn’t exist (e.g., Class.forName(“ABC”)), Java throws a ClassNotFoundException. This is an exception that occurs when the JVM cannot locate the specified class file at runtime.
Example:
Class.forName("ABC");Output:
Exception in thread "main" java.lang.ClassNotFoundException: ABCInstance variables and static variables
1. Instance Variable:
An instance variable is a variable declared inside a class but outside any method and without the static keyword. It is also called a non-static variable. Each object of the class gets its own copy of the instance variable, meaning the value of the instance variable can differ between objects.
Key Points:
- Instance variables are created when an object of the class is instantiated.
- Each object of the class has its own separate memory allocation for instance variables.
-These variables hold unique data for each object.
-Instance variable cannot be directly accessed inside any static context.
Example:
class Student {
int sid; // Instance variable for student ID
String sname; // Instance variable for student name
String semail; // Instance variable for student email
}
class JTC {
public static void main(String[] args) {
// Creating two objects of the Student class
Student s1 = new Student();
s1.sid = 101;
s1.sname = "Vivek";
s1.semail = "vivek@jtcindia.org";
Student s2 = new Student();
s2.sid = 102;
s2.sname = "Rahul";
s2.semail = "rahul@jtcindia.org";
// Displaying details for both students
System.out.println("----Student Details----");
System.out.println("s1.sid: " + s1.sid);
System.out.println("s1.sname: " + s1.sname);
System.out.println("s1.semail: " + s1.semail);
System.out.println("----Student Details----");
System.out.println("s2.sid: " + s2.sid);
System.out.println("s2.sname: " + s2.sname);
System.out.println("s2.semail: " + s2.semail);
}
}Explanation:
- sid, sname, and semail are instance variables that hold unique data for each student.
- When two objects (s1 and s2) are created, each has its own memory space for sid, sname, and semail.
- The output shows that s1 and s2 have different values for these instance variables.
Output:
- - Student Details - -
s1.sid: 101
s1.sname: Vivek
s1.semail: vivek@jtcindia.org
- - Student Details - -
s2.sid: 102
s2.sname: Rahul
s2.semail: rahul@jtcindia.org2. Static Variable:
A static variable is a class-level variable that is shared by all instances (objects) of the class. It is declared using the static keyword. Static variables are not specific to any object and get memory allocated when the class is loaded, rather than when objects are created.
Key Points:
- A static variable is shared by all objects of the class.
- It gets memory allocation once when the class is loaded, and its value is the same for all objects.
- Static variables are used to store data that is common across all instances of a class.
Example:
class Student {
int sid; // Instance variable for student ID
String sname; // Instance variable for student name
String semail; // Instance variable for student email
static String snationality = "Indian"; // Static variable shared by all students
}
class JTC {
public static void main(String[] args) {
// Creating two objects of the Student class
Student s1 = new Student();
s1.sid = 101;
s1.sname = "Vivek";
s1.semail = "vivek@jtcindia.org";
Student s2 = new Student();
s2.sid = 102;
s2.sname = "Rahul";
s2.semail = "rahul@jtcindia.org";
// Displaying details for both students, including the static variable
System.out.println("s1.sid: " + s1.sid);
System.out.println("s1.sname: " + s1.sname);
System.out.println("s1.semail: " + s1.semail);
System.out.println("s1.snationality: " + s1.snationality);
System.out.println("s2.sid: " + s2.sid);
System.out.println("s2.sname: " + s2.sname);
System.out.println("s2.semail: " + s2.semail);
System.out.println("s2.snationality: " + s2.snationality);
}
}Explanation:
- sid, sname, and semail are instance variables and differ for each object.
- snationality is a static variable that is shared by all instances of the Student class. Its value, “Indian”, is the same for both s1 and s2.
- All objects of the class share the same snationality value, as it is static.
Output:
s1.sid: 101
s1.sname: Vivek
s1.semail: vivek@jtcindia.org
s1.snationality: Indian
s2.sid: 102
s2.sname: Rahul
s2.semail: rahul@jtcindia.org
s2.snationality: Indian3. Accessing Static Variables:
Static variables can be accessed in three different ways:
1. Using the class name:
- Access the static variable directly through the class name.
System.out.println(A.a);2. Using a class reference variable (even if it contains a null value):
- A reference variable can be used to access a static variable, even if the reference variable is null.
A a1 = null;
System.out.println(a1.a); // Still accesses the static variable3. Using a class object:
- Static variables can also be accessed using an object of the class.
A a2 = new A();
System.out.println(a2.a);Example of Static Variable Access:
class A {
static int a = 10; // Static variable
}
class JTC {
public static void main(String[] args) {
// Accessing static variable using the class name
System.out.println("A.a: " + A.a);
// Accessing static variable using a reference variable with a null value
A a1 = null;
System.out.println("a1.a: " + a1.a);
// Accessing static variable using an object
A a2 = new A();
System.out.println("a2.a: " + a2.a);
}
}Output:
A.a: 10
a1.a: 10
a2.a: 10Explanation:
- The static variable a is accessed in all three ways, and the value 10 is printed each time because it is the same across all accesses.
Note:-
- Instance and static variable both can be declared as final variable.
- Instance and static variable cannot be declared inside any local context and only local variable can be declared.
- It means we can use static variable in static context(method and block-early binding).
- Variable defined under instance or static block would be consided as local variable.
- So, inside instance block or method we can access both.
Thus we can say that,
- Instance Variables: Unique to each object, and memory is allocated when the object is created.
-Static Variables: Shared by all objects of the class, and memory is allocated once when the class is loaded. They can be accessed using the class name, a null reference, or an object of the class.
Note:-
In Java, a final variable is a variable whose value cannot be changed once it is assigned. Here’s a breakdown of how final and static final variables can be initialized:
Final Variables
1. At the time of declaration:
final int x = 10;
2. Using a block:
final int y;
{
y = 20; // Initialized in an instance initializer block
}3. In a constructor:
final int z;
MyClass(int value) {
z = value; // Initialized in the constructor
}Static Final Variables
For static final variables, they must be initialized as follows:
- At the time of declaration:
static final int a = 100; - Static block:
static final int b;
static {
b = 200; // Initialized in a static block
}If a static final variable is not initialized in either of these ways, it will lead to a compilation error. This ensures that the variable has a constant value at the class level.
-In Java, you can declare local variables as final, but you cannot declare them as private.
Final Local Variables
When you declare a local variable as final, it means that once it is initialized, its value cannot be changed. For example:
public void myMethod() {
final int number = 10;
// number = 20; // This would cause a compile-time error
}Private Local Variables
Local variables are inherently not associated with an access modifier like private, protected, or public. Access modifiers apply to class members (fields, methods) but not to local variables defined within methods or blocks. Local variables are only accessible within the scope of the method or block in which they are defined.
Summary
- You can declare local variables as final.
- You cannot declare local variables as private. They are already limited in scope to the method in which they are defined.
Blocks in Java
A Block in Java is a group of statements enclosed within curly braces {}. Blocks can be declared in different parts of a class, including inside methods, constructors, or directly within the class. Java has three types of blocks:
1. Instance Block or Non-Static Block
2. Static Block
3. Local Block
1. Instance Block or Non-Static Block
- A block declared inside a class without the static keyword is called an instance block or non-static block.
- It is executed every time an object of the class is created.
- Multiple instance blocks can be declared within a class, and they are executed in the order they are written.
- Instance blocks can access both instance and static members of the class.
-Instance blocks are typically used to execute code during object initialization.
Use Case: Useful for initializing instance variables or executing code that needs to run for every instance of a class.
Example:
class JTC{
public static void main(String arg[]){
A a1 = new A();
A a2 = new A();
A a3 = new A();
}
}
class A {
int a = 10; // Instance Variable
static int b = 20; // Static Variable
// Instance Block 1
{
System.out.println("IB-1 in A class");
System.out.println("a :- " + a);
System.out.println("b :- " + b);
}
// Instance Block 2
{
System.out.println("IB-2 in A class");
System.out.println("a :- " + a);
System.out.println("b :- " + b);
}
}Output:
IB-1 in A class
a :- 10
b :- 20
IB-2 in A class
a :- 10
b :- 20
…2. Static Block
- A block declared with the static keyword is called a static block.
- Static blocks are executed once, when the class is loaded into memory (before any objects are created).
- Static blocks can only access static members directly. To access instance members, an object of the class must be created.
- Static blocks are useful when you want to perform operations at class loading time (e.g., initializing static variables).
Use Case: Ideal for initializing static variables or performing actions that need to occur once for the class, such as loading configuration settings.
Example:
class JTC{
public static void main(String arg[]){
new A();
}
}
class A {
int a = 10; // Instance Variable
static int b = 20; // Static Variable
// Static Block
static {
System.out.println("SB in A class");
System.out.println("b :- " + b);
}
// Instance Block
{
System.out.println("IB in A class");
System.out.println("a :- " + a);
System.out.println("b :- " + b);
}
}Output:
SB in A class
b :- 20
IB in A class
a :- 10
b :- 203. Local Block
- A local block is a block of code defined inside a method or constructor.
- It is not static or an instance block and is executed when the containing method or constructor is invoked.
- Local blocks are typically used for block-level synchronization or scoping local variables.
-We cannot declare them as final.
Example:
class JTC{
public static void main(String arg[]){
new A().m1();
}
}
class A {
void m1(){
System.out.println("m1 method in A class");
System.out.println("Before Local Block");
// Local Block
{
System.out.println("Local Block in m1 method");
}
System.out.println("After Local Block");
}
}Output:
m1 method in A class
Before Local Block
Local Block in m1 method
After Local BlockDynamic Class Loading
Java provides a mechanism to load classes dynamically at runtime using the Class.forName() method. This method loads the class and executes the static blocks (if present) when the class is loaded. If the class is not found, it throws a ClassNotFoundException.
Example:
class JTC{
public static void main(String arg[]) throws ClassNotFoundException {
Class.forName("A");
Class.forName("B");
}
}
class A {
static {
System.out.println("SB in A class");
}
}
class B {
static {
System.out.println("SB in B class");
}
}Output:
SB in A class
SB in B classIn this example, both A and B classes are dynamically loaded at runtime, and their static blocks are executed.
Methods
In Java, methods are blocks of code designed to perform specific tasks, and they are central to how programs are structured and executed.
1. Method Declaration and Syntax:
A method in Java is declared using the following syntax:
<Access Modifier> <Return Type> <Method Name>(<List of Parameters>) {
// Method body
}- Access Modifier: Determines the visibility of the method (e.g., public, private, protected).
- Return Type: The type of data the method returns to the caller (int, String, void, etc.).
- Method Name: A unique identifier used to invoke the method.
- Parameters: (Optional) Input values to the method, declared as a comma-separated list of types and names.
Access Modifiers in Java
Access modifiers in Java are keywords used to define the visibility and accessibility of classes, methods, and variables. They play a crucial role in encapsulation, allowing developers to control how the data is accessed and modified. There are four main access modifiers in Java:
1. Public
- Definition: The public access modifier allows the class, method, or variable to be accessible from any other class in any package.
- Use Case: When you want to expose a method or variable to be usable by any other code.
2. Private
- Definition: The private access modifier restricts the visibility of the method or variable to within the class itself. No other class can access private members.
- Use Case: Used to encapsulate data and provide a level of protection, ensuring that sensitive data is not accessible outside its class.
3. Protected
The protected access modifier in Java plays a crucial role in inheritance and encapsulation. It allows you to define the visibility of class members (fields and methods) in a way that balances accessibility and protection.
Characteristics of protected
1. Visibility:
- Members declared as protected are accessible within the same package.
- They are also accessible in subclasses, even if those subclasses are located in different packages.
2. Use in Inheritance:
- The primary use of protected is to allow subclasses to inherit and access members of a superclass while still preventing access from unrelated classes outside of the package.
3. Default Behavior:
- If no access modifier is specified, the default (package-private) access level applies, which allows access only within the same package. protected extends this visibility to subclasses, thus offering a more flexible approach.
Example of protected
Here’s an example to illustrate how the protected access modifier works:
// Superclass
class Animal {
protected void makeSound() {
System.out.println("Animal makes a sound");
}
}
// Subclass in the same package
class Dog extends Animal {
public void bark() {
makeSound(); // Accessing protected method from superclass
System.out.println("Dog barks");
}
}
// Subclass in a different package
package anotherPackage;
import somePackage.Animal; // Importing superclass
class Cat extends Animal {
public void meow() {
makeSound(); // Accessing protected method from superclass
System.out.println("Cat meows");
}
}Accessing protected Members
- Within the Same Package:
Classes in the same package can access protected members of other classes within the same package.
- From Subclasses:
Subclasses can access protected members, even if they are in a different package.
- From Non-Subclass in Different Package:
Non-subclass classes in different packages cannot access protected members.
Example Demonstration
// File: Animal.java
package somePackage;
public class Animal {
protected void makeSound() {
System.out.println("Animal makes a sound");
}
}
// File: Dog.java
package somePackage;
public class Dog extends Animal {
public void bark() {
makeSound(); // Accessing protected method from superclass
System.out.println("Dog barks");
}
}
// File: Cat.java
package anotherPackage;
import somePackage.Animal;
public class Cat extends Animal {
public void meow() {
makeSound(); // Accessing protected method from superclass
System.out.println("Cat meows");
}
}
// File: Main.java
package anotherPackage;
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.bark(); // Works fine
Cat cat = new Cat();
cat.meow(); // Works fine
}
}Key Points
1. Encapsulation: Using protected can help maintain encapsulation while allowing subclasses to inherit and utilize superclass behavior.
2. Design Choice: protected is often used in class libraries and frameworks where you want to provide extensibility but still control how the members are accessed.
3. Avoid Overuse: While protected allows for more flexibility than private, it can lead to tighter coupling between classes. Consider the implications on design and maintainability before using it extensively.
4. Default (Package-Private)
- Definition: If no access modifier is specified, it defaults to “package-private,” meaning the class, method, or variable is accessible only within its own package.
- Use Case: When you want to restrict access to classes and methods to only those within the same package.
Example:
class DefaultExample {
void display() {
System.out.println("Default method can only be accessed within the same package.");
}
}Summary of Access Modifiers
2. Method Types:
Java methods can be classified into two main types:
a. Instance Methods (Non-Static Methods):
- These are declared without the static keyword and are associated with object instances.
- Instance methods work on late-binding (i.e., tied to specific instances of the class).
- Instance methods can access both instance and static members of the class directly.
- To invoke an instance method, an object of the class is required.
Example:
class A {
int a = 10;
void display() {
System.out.println("a: " + a);
}
}
public class Test {
public static void main(String[] args) {
A obj = new A();
obj.display(); // Calls instance method 'display()'
}
}b. Static Methods:
- These are declared with the static keyword and belong to the class, not the instance.
- Static methods work on early-binding (executed when the class is loaded).
- They can only access static members of the class directly.
- A static method can be invoked using the class name or an object.
Example:
class A {
static int b = 20;
static void show() {
System.out.println("b: " + b);
}
}
public class Test {
public static void main(String[] args) {
A.show(); // Calls static method 'show()'
}
}3. Method Return Type:
The return type of a method indicates what type of value it will return to the caller:
- Primitive types: The method can return values like int, float, boolean, etc.
- Reference types: The method can return objects, arrays, or user-defined types.
- Void: If a method has a void return type, it does not return any value.
4. Method Invocation:
When invoking a method, you need to ensure that:
- The number of arguments matches the number of parameters.
- The types of arguments match the parameter types.
- The order of arguments corresponds to the parameter list.
class Calculator {
int add(int x, int y) {
return x + y;
}
}
public class Test {
public static void main(String[] args) {
Calculator calc = new Calculator();
int result = calc.add(10, 20); // Invokes the 'add' method with arguments
System.out.println("Result: " + result);
}
}5. Memory Management of Methods:
- When a method is invoked, it gets memory allocated in the stack area as a stack frame.
- The method’s stack frame is created when the method is called and released once it completes execution.
- Java stack follows the Last-In-First-Out (LIFO) principle, meaning the last method invoked will be the first to complete.
Method chaining example:
class A {
void m1() {
System.out.println("m1 starts");
m2();
System.out.println("m1 ends");
}
void m2() {
System.out.println("m2 starts");
m3();
System.out.println("m2 ends");
}
void m3() {
System.out.println("m3 executes");
}
}
public class Test {
public static void main(String[] args) {
A obj = new A();
obj.m1();
}
}Output:
m1 starts
m2 starts
m3 executes
m2 ends
m1 ends6. Method Chaining:
Method chaining refers to calling multiple methods sequentially within another method, as demonstrated in the above example with m1, m2, and m3. The execution sequence depends on the order in which the methods are called.
Note:-
- we can access method inside method but we cannot create method inside method
main method
In Java, the main method is the entry point of any standalone application. Its signature is:
public static void main(String[] args)Calling the main Method Explicitly
You can indeed call the main method explicitly from another method. For example:
public class MainExample {
public static void main(String[] args) {
System.out.println("Main method called");
}
public static void anotherMethod() {
main(new String[]{"arg1", "arg2"}); // Calling main explicitly
}
public static void main(String[] args) {
anotherMethod();
}
}In this example, calling main from anotherMethod will execute the main method again with the provided arguments.
Overloading the main Method
You can overload the main method by creating methods with the same name but different parameters. For example:
public class OverloadedMain {
public static void main(String[] args) {
System.out.println("Main method with String[] args");
main(5); // Calling the overloaded method
}
public static void main(int number) {
System.out.println("Overloaded main method with int: " + number);
}
}In this case, when you run the program, it will only recognize the main method with the String[] args signature as the entry point. The overloaded main(int number) can be called from within the main method or other methods, but it won’t be invoked automatically by the Java runtime.
Multiple main Methods in Different Classes
If you have multiple classes each containing a main method, only the main method of the class specified when running the program will be executed. For example:
public class FirstClass {
public static void main(String[] args) {
System.out.println("First Class Main");
}
}
public class SecondClass {
public static void main(String[] args) {
System.out.println("Second Class Main");
}
}When you run the program, you specify which class to execute:
java FirstClassThis will output:
First Class MainIf you run:
java SecondClassYou will see:
Second Class MainQ. Can we overload final methods ?
A. Yes, we can overload final methods because final keyword says do not
change implementation and in overloading we are not changing
implementation rather we are changing arguments.
Q. Can we overload private methods or not ?
A. Yes, we can overload private methods because private methods are
accessible everywhere in same class and overloading also happens within
class.
Q. Can we overload non static methods or not ?
A. Yes, we can overload non static methods but to call them we have to
create an Object.
Overview of the Scanner Class
- Purpose: The Scanner class is a predefined class in Java used for reading input from various sources, such as the keyboard (System.in), files, or strings. It enables programs to accept user input at runtime, making applications interactive.
Steps to Use the Scanner Class
Get Pratik T’s stories in your inbox
Join Medium for free to get updates from this writer.
1. Importing the Scanner Class
- To use the Scanner class, you first need to import it from the `java.util` package. This can be done with one of the following statements:
import java.util.Scanner; // Import only Scanner classor
import java.util.*; // Import all classes in the java.util package- Package: A package is like a folder that groups related classes together. Importing is necessary to use classes defined in other packages.
2. Creating an Object of the Scanner Class
- After importing, you create an instance of the Scanner class to start reading input:
Scanner s = new Scanner(System.in);- Here, `System.in` indicates that the input will be read from the standard input stream (usually the keyboard).
3. Using Scanner Class Methods
- The Scanner class provides various methods to read different types of input. Some commonly used methods include:
- `nextByte()`: Reads a byte value.
- `nextBoolean()`: Reads a boolean value (true/false).
- `nextShort()`: Reads a short integer.
- `nextInt()`: Reads an integer.
- `nextFloat()`: Reads a float value.
- `nextDouble()`: Reads a double value.
- `nextLong()`: Reads a long integer.
- `next()`: Reads a single word (string without spaces).
- `nextLine()`: Reads a whole line (string with spaces).
Example Program
Here’s a simple Java program using the Scanner class to collect student details:
import java.util.Scanner;
class StdDetails {
public static void main(String args[]) {
Scanner s = new Scanner(System.in);
System.out.println("Enter name: ");
String name = s.nextLine();
System.out.println("Enter age: ");
int age = s.nextInt();
System.out.println("Enter percentage: ");
float percentage = s.nextFloat();
System.out.println("Enter result status (true/false): ");
boolean result = s.nextBoolean();
System.out.println("Enter contact number: ");
long contact = s.nextLong();
System.out.println("Enter Gender: ");
char gender = s.next().charAt(0);
System.out.println("Student Details Entered by you are:");
System.out.println("Name: " + name);
System.out.println("Age: " + age);
System.out.println("Gender: " + gender);
System.out.println("Contact: " + contact);
System.out.println("Score: " + percentage);
System.out.println("Result: " + result);
s.close(); // It's good practice to close the scanner
}
}Example Output
When you run the program, it will prompt you to enter details:
Enter name:
Pratik
Enter age:
27
Enter percentage:
86.80
Enter result status (true/false):
true
Enter contact number
12345678
Enter Gender:
M
Student Details Entered by you are:
Name: Pratik
Age: 27
Gender: M
Contact: 12345678
Score: 86.8
Result: trueCopy codeImportant Notes
- InputMismatchException: If the user provides an input of a type that doesn’t match the expected type, a
java.util.InputMismatchExceptionwill be thrown. For example, if you enter a float when an integer is expected:
Enter age: 44.5
Exception in thread "main" java.util.InputMismatchException- No
nextChar()Method: There is no method namednextChar(). Instead, to get a character input, you can read a string usingnext()and then usecharAt(index)to retrieve the desired character. - For example, if you input “Abcd”:
char gender = s.next().charAt(0); // 'A'Constructor Overview
Need of Constructor
Non static variables plays a special role in every program i.e. non
static refers to muliple copies which means that for every object there
is a seperate memory allocated for non static variable, which means
programmer can intialise everytime new value to non static variable.
A constructor is a special method in Java used to initialize objects. It is distinct from normal methods due to the following properties:
1. Constructor Name: Must be the same as the class name.
2. No Return Type: A constructor cannot have a return type, not even void. If a return type is specified, it becomes a regular method.
3. Automatic Invocation: Unlike normal methods, constructors are invoked automatically when an object is created using the new keyword.
4. Parameters: Constructors can have parameters similar to methods, and these are used to initialize object properties during object creation.
Key Points about Constructors:
- No Explicit Invocation: Constructors are called automatically during object creation and cannot be called explicitly.
- Argument Matching: When a constructor is invoked, the number, types, and order of arguments passed must match the parameters defined in the constructor.
Example: Constructor Basics
class LMS {
public static void main(String[] args) {
A a1 = new A(); // Invokes constructor A()
}
}
class A {
A() { // Constructor
System.out.println("Constructor in A class");
}
void A() { // This is a method, not a constructor
System.out.println("It is not a constructor, it is a method.");
}
}Output:
Constructor in A classIn this example, the constructor A() is called when an object of class A is created. The method void A() is not a constructor because it has a return type (void).
Constructor with Parameters
⦁ One of the drawbacks of no argument constructor is, it provides
same value for every object.
⦁ Therefore to overcome this drawback, we go for parameterized
constructor.
Constructors can accept parameters, which are used to initialize object properties:
class JTC {
public static void main(String[] args) {
Student s1 = new Student(101, "Vishal", "124@gmail.com", "Indian");
Student s2 = new Student(102, "Rohan", "r456@gmail.com", "USA");
s1.show();
s2.show();
}
}
class Student {
int sid;
String sname;
String semail;
static String snationality;
// Constructor with parameters
Student(int id, String name, String email, String nationality) {
sid = id;
sname = name;
semail = email;
snationality = nationality;
}
void show() {
System.out.println("----Student Details----");
System.out.println("sid :- " + sid);
System.out.println("sname :- " + sname);
System.out.println("semail :- " + semail);
System.out.println("snationality :- " + snationality);
}
}Here, each Student object is initialized with different values for sid, sname, and semail, but since snationality is a static variable, the last assigned value is shared across all objects.
Default Constructor
If no constructor is explicitly defined in a class, Java provides a default constructor that takes no parameters. However, if a parameterized constructor is defined, the default constructor is not automatically provided:
class A {
int a;
void m1() {}
public static void main(String[] args) {}
}Generated Default Constructor
class A {
int a;
A(); // Default constructor added by compiler
void m1();
public static void main(String[] args);
}If a constructor is defined explicitly, the default constructor is not generated:
class A {
int a;
A(int a) {} // Explicit constructor
void m1() {}
public static void main(String[] args) {}
}Here, no default constructor is generated because A(int a) is explicitly defined.
Constructor Chaining
Constructor chaining occurs when one constructor calls another constructor in the same class using this():
class JTC {
public static void main(String[] args) {
A a1 = new A(10, 20); // Invokes the 2-parameter constructor
}
}
class A {
A() {
System.out.println("No-parameter constructor in A class");
}
A(int a) {
this(); // Calls the no-parameter constructor
System.out.println("1-parameter constructor in A class");
}
A(int a, int b) {
this(a); // Calls the 1-parameter constructor
System.out.println("2-parameter constructor in A class");
}
}Output:
No-parameter constructor in A class
1-parameter constructor in A class
2-parameter constructor in A classIn this example:
1. The 2-parameter constructor A(int a, int b) calls the 1-parameter constructor A(int a) using this(a).
2. The 1-parameter constructor then calls the no-parameter constructor A() using this().
Rules for Constructor Chaining:
1. this() must be the first statement in the constructor.
2. Only one constructor call is allowed in each constructor.
3. Cyclic constructor calls are not allowed.
Note:-
You can declare a constructor as final or static in certain contexts, but it’s important to clarify what that means.
Final Constructor
In Java, you cannot declare a constructor as final. The final keyword is used to prevent inheritance of a class or overriding of a method, but it doesn’t apply to constructors. Constructors cannot be inherited, so making a constructor final is redundant and will result in a compilation error.
Static Constructor
Similarly, you cannot declare a constructor as static. Constructors are special methods used to initialize instances of a class and cannot be static. Static methods belong to the class itself rather than any specific instance. Since constructors are meant to create instances, they cannot be static.
Summary
- Final Constructor: Not allowed (will result in a compilation error).
- Static Constructor: Not allowed (will also result in a compilation error).
However, you can have static methods in your class (like a factory method) that can be used to create instances, and you can control access to the constructor by making it private (as in the Singleton example).
Note:-
You can declare a constructor as private in Java, and it’s actually quite common in certain design patterns like Singleton or Factory patterns. Let’s break down the concepts one by one.
1. Private Constructor in Java
A private constructor restricts the instantiation of a class from outside the class itself. This means that objects of the class cannot be created directly using the new keyword from outside the class. The main reasons for using a private constructor include:
- Singleton Pattern: Ensures that a class has only one instance and provides a global point of access to that instance.
- Factory Pattern: Controls how objects are created by using static methods to return instances.
- Utility Classes: Prevents instantiation of classes that only contain static methods (e.g., Math class in Java).
Example of a Singleton Pattern with a Private Constructor
public class Singleton {
private static Singleton instance;
// Private constructor to prevent instantiation
private Singleton() {}
// Public method to provide access to the single instance
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}In this example:
- The constructor is private, so no other class can create an instance of Singleton.
- The only way to get the instance of the class is through the getInstance() method, which ensures only one instance is created.
Benefits of Using a Constructor for Initialization
Using a constructor for initializing an object’s state has several benefits:
1. Mandatory Initialization: Constructors allow you to make certain fields mandatory by requiring their values to be provided when the object is created. This prevents the object from being in an incomplete or invalid state.
2. Convenience: Initialization through constructors can be more concise and clear than using multiple setter methods.
3. Immutable Objects: If you only initialize fields through a constructor and don’t provide setters, you can create immutable objects. Immutability is a desirable property for objects in many situations, especially in multi-threaded applications.
Example of Using a Constructor for Initialization
public class Student {
private int id;
private String name;
// Constructor for initializing fields
public Student(int id, String name) {
this.id = id;
this.name = name;
}
// Getters
public int getId() {
return id;
}
public String getName() {
return name;
}
}This approach ensures that both id and name must be provided when creating a Student object, making it clear what the state of the object will be.
Constructor vs. Getters and Setters for Initialization
Both constructors and getter/setter methods have their own use cases and benefits. Let’s compare them:
When to Use Constructors and When to Use Getters and Setters
- Use Constructors:
- When you need to ensure that an object is fully initialized at the time of creation.
- When you want to create immutable objects.
- When you need to initialize many fields at once.
- Use Getters and Setters:
- When you need to allow modification of fields after object creation.
- When you have optional fields that may or may not be set.
- When you need to perform specific actions or validation logic each time a field is changed.
Example Using Setters
public class Student {
private int id;
private String name;
// No-argument constructor
public Student() {}
// Setters for initialization
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
// Getters
public int getId() {
return id;
}
public String getName() {
return name;
}
}This approach allows you to create a Student object and then set its fields later:
Student student = new Student();
student.setId(101);
student.setName("John Doe");Which One Is Better?
- Use Constructors if you have fields that must be initialized for the object to be in a valid state. Constructors ensure that objects are created with all necessary data.
- Use Getters and Setters if you want to allow flexible object creation or if the object fields might need to be changed or updated after object creation.
In most scenarios, combining constructors with getters and setters provides a balanced approach. Use constructors to ensure required fields are initialized, and use setters for optional fields or to allow modifications after the object is created.
Difference between method and constructor
Variable Arguments (Var-Args) in Java
Variable Arguments, commonly known as var-args, allow methods to accept zero or more arguments of a specified type. This feature is particularly useful when you want to pass a varying number of arguments to a method. Before var-args were introduced in Java (starting with JDK 5), method overloading was necessary to handle methods with different numbers of parameters.
Key Points About Var-Args:
1. Syntax:
- The syntax of a variable argument is <data_type> … <var_arg_name>.
- Example: int… nums or String… words.
2. Internal Representation:
- Var-args are internally handled as a one-dimensional array. You can use array-style access to manipulate the arguments.
3. Order of Storage:
- Arguments are stored in the order in which they are passed.
4. Number of Arguments:
- You can pass any number of arguments, or even no arguments at all, and the method will still work. However, the arguments must be of the correct data type.
5. Restrictions:
- Positioning: The var-args parameter must always be the last in the parameter list. You cannot define another parameter after a var-args parameter.
- One Var-Args per Method: You can only have one var-args parameter in a method.
Example of Var-Args:
class JTC {
public static void main(String[] args) {
A obj = new A();
obj.m1(); // No argument
obj.m1(10); // One argument
obj.m1(10, 20); // Two arguments
obj.m1(10, 20, 30, 40); // Multiple arguments
}
}
class A {
void m1(int... a) {
System.out.println("m1(int... a) called.");
for (int i = 0; i < a.length; i++) {
System.out.println("Index: " + i + " Value: " + a[i]);
}
}
}Output:
m1(int… a) called.
m1(int… a) called.
Index: 0 Value: 10
m1(int… a) called.
Index: 0 Value: 10
Index: 1 Value: 20
m1(int… a) called.
Index: 0 Value: 10
Index: 1 Value: 20
Index: 2 Value: 30
Index: 3 Value: 40Combining Var-Args with Regular Parameters:
You can combine regular parameters and var-args in a method, but remember the var-args must always be the last parameter:
void m1(String s, int... nums) {
System.out.println("String: " + s);
for (int i = 0; i < nums.length; i++) {
System.out.println("Index: " + i + " Value: " + nums[i]);
}
}this Keyword in Java
The this keyword is a reference to the current object, the instance on which a method or constructor is being invoked. It is primarily used to distinguish between instance variables and parameters when they have the same names.
Key Characteristics of this:
1. Final Reference Variable: It is a final reference variable, meaning it always refers to the current object, and you cannot change its reference.
2. Usage:
- this can be used to access instance variables.
- It can also be used to call constructors from other constructors within the same class (constructor chaining).
3. Restrictions:
- this cannot be used in static methods or static contexts because static methods are class-level methods and do not have a reference to any instance.
Example Using this:
class Student {
int sid;
String sname;
String scity;
Student(int sid, String sname, String scity) {
this.sid = sid;
this.sname = sname;
this.scity = scity;
}
void show() {
System.out.println("Sid: " + sid);
System.out.println("Sname: " + sname);
System.out.println("Scity: " + scity);
}
}
class JTC {
public static void main(String[] args) {
Student s1 = new Student(101, "Vivek", "Noida");
s1.show();
}
}Output:
Sid: 101
Sname: Vivek
Scity: NoidaIn this example, this.sid = sid; ensures that the constructor parameter sid assigns its value to the instance variable sid.
Common Misconception:
If you do not use this in the constructor, like in the example below, the values won’t be assigned to the instance variables:
Student(int sid, String sname, String scity) {
sid = sid;
sname = sname;
scity = scity;
}This will result in the default values being printed (0 for integers and null for Strings) because the local variables (parameters) are being assigned to themselves, and the instance variables remain uninitialized.
Components of Object-Oriented Programming (OOP)
Object-Oriented Programming (OOP) is a programming paradigm that uses “objects” to design applications and software. It provides a clear modular structure, making it easier to understand, debug, and modify code. The four core principles of OOP are Encapsulation, Abstraction, Inheritance, and Polymorphism. Let’s go through each of these components in detail.
1. Encapsulation
Encapsulation is the concept of bundling data (variables) and methods (functions) that operate on the data into a single unit called an object. It restricts direct access to some of the object’s components, which is known as data hiding. Encapsulation helps in maintaining data integrity and makes code more modular.
- Example: If you have a Car object, the data (attributes like speed, fuel) and methods (like accelerate(), brake()) are bundled together within that object.
Key Concepts:
- Access Modifiers: Encapsulation often involves using access modifiers (such as private, public, and protected) to control access to the data.
- Getter and Setter Methods: These are used to access and modify private variables in a controlled way.
class Car {
private int speed; // Encapsulated data (private variable)
// Getter method
public int getSpeed() {
return speed;
}
// Setter method
public void setSpeed(int speed) {
this.speed = speed;
}
}2. Abstraction
Abstraction is the concept of hiding the complex implementation details of a system and exposing only the necessary and relevant parts to the user. It reduces complexity by allowing the user to interact with an interface without knowing its underlying implementation.
- Example: When you drive a car, you only need to know how to use the steering wheel, accelerator, and brake. You don’t need to understand how the engine or brake system works internally.
Key Concepts:
- Abstract Classes and Interfaces: In Java, abstraction is implemented using abstract classes and interfaces.
- Abstract methods in an abstract class have no body and must be implemented by subclasses.
abstract class Vehicle {
abstract void start(); // Abstract method
}
class Car extends Vehicle {
void start() {
System.out.println("Car starts with a key.");
}
}3. Inheritance
Inheritance is a mechanism that allows one class to inherit properties (fields and methods) from another class. The class that is inherited from is called the parent or superclass, and the class that inherits is called the child or subclass. Inheritance promotes code reusability.
4. Polymorphism
Polymorphism is the ability of an object to take on multiple forms. It allows methods to do different things based on the object it is acting upon, even if they share the same name. Polymorphism is achieved in two ways: Method Overloading and Method Overriding.
Types of Polymorphism:
1. Compile-time Polymorphism (Method Overloading): Multiple methods have the same name but different parameters. The method to be called is determined at compile time.
class Calculator {
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
}2. Runtime Polymorphism (Method Overriding): A subclass provides a specific implementation of a method that is already defined in its superclass. The method to be called is determined at runtime.
class Animal {
void makeSound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Dog barks");
}
}
public class Main {
public static void main(String[] args) {
Animal myAnimal = new Dog();
myAnimal.makeSound(); // Outputs: Dog barks
}
}Summary of OOP Components
Benefits of OOP
1. Reusability: Code can be reused through inheritance.
2. Modularity: Each object can function as an independent entity.
3. Scalability: Easier to manage and scale large software systems.
4. Maintainability: Easy to update and modify code with minimal impact on other parts of the program.
These four principles form the foundation of Object-Oriented Programming and make it a powerful tool for designing software systems that are more structured, scalable, and easier to understand.
Understanding Object Creation in Java
When you create an object of a class in Java, several key processes occur regarding memory allocation, variable initialization, and the execution of various blocks and constructors. Here’s a breakdown of what happens step by step, including both static and instance variables, as well as static and instance initializer blocks.
1. Class Loading
When the class is referenced for the first time (e.g., when an object is created), the Java Virtual Machine (JVM) loads the class into memory. During this process:
- Memory Allocation for Static Variables: Memory is allocated in the method area (also called the static area) for all static variables. Each static variable is associated with the class itself rather than any instance.
- Static Block Execution: Any static blocks in the class are executed in the order they appear. This is where you can perform static initialization.
2. Memory Allocation for the Object
When you create an object using the new keyword (e.g., MyClass obj = new MyClass();):
- Memory Allocation on the Heap: Memory is allocated for the new object on the heap. This memory will hold the instance variables declared in the class.
3. Initialization of Instance Variables
Once memory is allocated for the object:
- Default Initialization: All instance variables of the object are initialized to their default values:
- Numeric types (e.g., int, double) are initialized to 0.
- boolean types are initialized to false.
- Object references are initialized to null.
4. Execution of Instance Initializer Blocks
If there are any instance initializer blocks in the class, they are executed in the order they appear, immediately after the default initialization of instance variables.
5. Execution of the Constructor
After all instance initializer blocks have been executed, the constructor is called. The constructor can perform further initialization, including setting values for instance variables based on parameters passed to it.
6. Finalization of Object Creation
Once the constructor has completed its execution, the object is fully constructed, and a reference to the newly created object is returned. You can then use this reference to interact with the object.
Memory Layout Overview
1. Static Variables:
- Location: Method area (static area of the heap).
- Initialization: Allocated and initialized when the class is loaded.
2. Instance Variables:
- Location: Heap (allocated when an object is created).
- Initialization: Initialized to default values, then possibly modified by initializer blocks or constructors.
Complete Example
Here’s a complete example that includes static variables, static blocks, instance variables, instance initializer blocks, and constructors:
public class Example {
static int staticVar; // Static variable
static {
staticVar = 20; // Static block initializing static variable
System.out.println("Static block executed: staticVar = " + staticVar);
}
int instanceVar; // Instance variable
// Instance initializer block
{
instanceVar = 10; // Initializing instance variable
System.out.println("Instance initializer block executed: instanceVar = " + instanceVar);
}
// Constructor
public Example(int value) {
this.instanceVar = value; // Initializing instance variable with constructor parameter
System.out.println("Constructor executed: instanceVar = " + instanceVar);
}
public static void main(String[] args) {
Example obj1 = new Example(30);
System.out.println("obj1: staticVar = " + Example.staticVar + ", instanceVar = " + obj1.instanceVar);
Example obj2 = new Example(40);
System.out.println("obj2: staticVar = " + Example.staticVar + ", instanceVar = " + obj2.instanceVar);
}
}Expected Output
Static block executed: staticVar = 20
Instance initializer block executed: instanceVar = 10
Constructor executed: instanceVar = 30
obj1: staticVar = 20, instanceVar = 30
Instance initializer block executed: instanceVar = 10
Constructor executed: instanceVar = 40
obj2: staticVar = 20, instanceVar = 40Explanation of Output
1. Static Block Execution: The static block executes first when the class Example is loaded, initializing staticVar to 20.
2. First Object Creation (obj1):
- The instance initializer block executes, initializing instanceVar to 10.
- The constructor is called with the argument 30, setting instanceVar to 30.
- The values of staticVar and instanceVar are printed.
3. Second Object Creation (obj2):
- The instance initializer block executes again, initializing instanceVar to 10 (as part of the new object creation).
- The constructor is called with the argument 40, setting instanceVar to 40.
- The values are printed again.
Summary
- Static Variables: Shared across all instances of a class, stored in the method area, and initialized when the class is loaded.
- Static Blocks: Used for static initialization, executed when the class is loaded.
- Instance Variables: Unique to each instance, initialized when an object is created.
- Instance Initializer Blocks: Executed after default initialization of instance variables and before the constructor.
- Constructors: Called to further initialize an object after the instance initializer blocks.
Note:-1) Constructor cannot be defined under local context.
2) When accessing static variables in Java, the initialization behavior varies based on how the static variables are defined:
- Static Variable with Static Block:
— If you declare a static variable (like b) and initialize it in a static block, that block is executed when the class is loaded. Therefore, if you access b using the class name, the static block will be processed first.
class Example {
static int a = 10;
static int b;
static {
b = 20; // Static block is executed when the class is loaded
}
}2. Static Final Variable at Declaration:
— If you declare a static final variable (like c) and initialize it at the time of declaration, its value is available immediately, and there’s no need for a static block. Therefore, when you access c, it directly provides the initialized value without processing any static block.
class Example {
static final int c = 30; // Initialized at declaration
}Execution Process Overview
When you execute a Java program, the Java Virtual Machine (JVM) creates two main memory areas:
1. Stack Area (Execution Area)
2. Heap Area (Storage Area)
These areas play crucial roles in managing memory for static and non-static members, method calls, and variable storage.
Detailed Execution Steps
1. JVM Enters the Stack Area
- The JVM starts by entering the stack area to manage method calls and local variables.
2. Class Loader Invocation
- The JVM calls the class loader, a special function responsible for loading classes into memory.
- Its main job is to load all static members of the class into the Static Pool Area (SPA).
3. Static Pool Area (SPA)
- SPA is a part of the heap area where all static variables and static methods are stored.
- The class loader loads the static members and prepares them for use.
4. Class Loader Completes Its Job
- After loading all static members, the class loader exits the stack area, as its task is complete.
5. JVM Calls the `main` Method
- The JVM locates the `main` method, which serves as the entry point of the program, and loads it into the stack area.
6. Method Area in the Stack
- When a method is called, it is placed in the stack area under the method area. The method’s local variables and parameters will be stored here.
7. Execution of `main` Method Begins
- The execution of the `main` method starts. This is where the program’s logic is processed.
8. Completion of `main` Method Execution
- Once all statements in the `main` method are executed, it exits the stack area, indicating that the method’s execution is complete.
9. Garbage Collection Call
- Before the JVM exits the stack area, it invokes the garbage collector. The garbage collector’s role is to clean up any unused memory, preparing for the next execution.
Important Memory Management Points
1. Static Members Availability
- Static methods and static variables are loaded into the SPA before execution begins, making them accessible without needing to create an object.
2. Non-Static Members Accessibility
- Non-static members (variables and methods) are not available until an object is created. You must use the `new` keyword to instantiate an object, which loads non-static members into memory.
3. Loading of Non-Static Members
- Non-static variables and methods are stored together in the object created. This is why non-static methods can access non-static variables directly, as they belong to the same instance.
4. Single Copy of Static Members
- Static members are loaded only once in the SPA, which is why they are referred to as single copies.
5. Multiple Copies of Non-Static Members
- Non-static members are loaded each time an object is created, resulting in multiple copies of these members across different instances.
6. Local vs. Global Variables
- Local variables (e.g., method parameters) reside in the stack area, while global variables (like static and instance variables) are stored in the heap area.
7. Location of Static and Non-Static Variables
- Static variables are stored in the SPA within the heap, whereas non-static variables are part of the object in the heap.
8. Reference Variables as Local Variables
- All reference variables (pointers to objects) are considered local variables and reside in the stack area.
9. Scope of Local Variables
- Local variables exist only during the execution of a method. Once the method completes, the local variables are removed from the stack, hence their scope is limited to the method itself.
Volatile modifier in Java
The volatile modifier in Java is used to ensure visibility and ordering of variables across multiple threads in a multi-threaded environment. When you declare a variable as volatile, you are telling the Java Virtual Machine (JVM) that this variable’s value may be modified by different threads and that any changes made to it should be visible to all other threads immediately.
Key Characteristics of volatile Modifier
1. Visibility:
- Normally, each thread in Java has its own local cache of variables. When one thread updates a variable, other threads might not see this change immediately.
- A volatile variable ensures that the value is always read from the main memory, not from a thread’s local cache. This means any update to a volatile variable by one thread is immediately visible to all other threads.
2. Ordering:
- The volatile keyword ensures that read and write operations on the variable are performed in order. It prevents the reordering of instructions by the compiler or the processor, which can sometimes occur to optimize performance.
Use Cases for volatile
1. Flags:
- One of the most common use cases for volatile is for flags or status indicators that are checked and modified by multiple threads.
public class VolatileExample {
private volatile boolean running = true;
public void runTask() {
while (running) {
// Do some work
}
}
public void stop() {
running = false; // This change will be visible to the runTask() method
}
}2. Double-Checked Locking Singleton:
- volatile is used in the implementation of the Singleton pattern to prevent the reordering of instructions.
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton(); // Prevents reordering issues
}
}
}
return instance;
}
}Important Points to Remember
1. Atomicity:
- volatile does not guarantee atomicity of operations. For example, incrementing a volatile variable is not atomic.
volatile int count = 0;
count++; // This operation is not atomicFor atomic operations, you should use classes like AtomicInteger from the java.util.concurrent.atomic package.
2. Volatile vs. Synchronized:
- volatile ensures visibility of changes to variables across threads, while synchronized provides both visibility and mutual exclusion.
- synchronized blocks or methods are more expensive in terms of performance compared to using volatile.
- If you only need visibility without locking, use volatile. If you need atomicity and thread safety, use synchronized.
3. When to Use volatile:
- Use volatile when:
- You have a single variable being accessed and modified by multiple threads.
- The variable does not depend on its previous value (no compound actions like count++).
- You do not need to perform complex operations on the variable that require locking.
Example of Using volatile
public class VolatileExample {
private volatile boolean flag = true;
public void printMessage() {
while (flag) {
System.out.println("Running...");
}
}
public void stopPrinting() {
flag = false; // This change will be visible to the printMessage method immediately
}
public static void main(String[] args) {
VolatileExample example = new VolatileExample();
new Thread(example::printMessage).start();
try {
Thread.sleep(1000); // Let the thread run for a second
} catch (InterruptedException e) {
e.printStackTrace();
}
example.stopPrinting(); // Stops the running thread
}
}In this example:
- The flag variable is marked as volatile.
- The changes to the flag variable in the stopPrinting() method are immediately visible to the printMessage() method.
Summary
- volatile ensures that updates to a variable are immediately visible to other threads and prevents reordering of instructions.
- It does not provide atomicity, so it should be used when only visibility is required.
- It’s suitable for simple flags or status variables but not for complex operations that require synchronization.
Using the volatile keyword correctly can help in designing efficient multi-threaded applications without unnecessary synchronization overhead.
Inheritance
Inheritance is a key concept in object-oriented programming (OOP) that allows the creation of a new class using the properties and methods of an existing class. It helps in code reusability and the establishment of relationships between different classes. Below, let’s go through the details of inheritance concepts, its types, and some examples.
Inheritance Overview
- Definition: Inheritance allows a new class (called a derived class, child class, or subclass) to acquire properties and behavior from an existing class (called a base class, parent class, or superclass).
- Purpose: It is used to establish relationships between categories based on the logic of your application. It helps in reusing existing code, making software development more efficient.
- How It Works: In most programming languages like Java, inheritance is implemented using the extends keyword. This keyword indicates that a class is inheriting the features of another class.
Terminology
- Superclass (Parent Class / Base Class): The class whose properties and methods are inherited by another class.
- Subclass (Child Class / Derived Class): The class that inherits properties and methods from another class.
Important Notes on Inheritance
- The subclass contains all the functionalities of its superclass.
- The subclass can access all non-private members of the superclass.
- Private members of a superclass are not directly accessible from the subclass.
Types of Inheritance
1.Single Level
2.Multi Level
3.Hierarchical
4.Multiple
5.Hybrid
1. Single Inheritance
- A subclass inherits from one superclass.
Example:
class A {
int a = 10;
}
class B extends A {
void display() {
System.out.println("Value of a: " + a);
}
}
public class Test {
public static void main(String[] args) {
B obj = new B();
obj.display(); // Output: Value of a: 10
}
}- In this example, class B inherits the properties of class A.
2. Multiple Inheritance
- A class inherits from more than one class at the same time.
- Note: Java does not support multiple inheritance with classes to avoid ambiguity problems, but it can be achieved using interfaces.
- Example (Ambiguity Problem):
class A { int a = 10; }
class B { int a = 20; }
// class C extends A, B { } // This is not allowed in Java3. Multi-Level Inheritance
- A class is derived from another class, which is also derived from another class.
- Example:
class A {
int a = 10;
}
class B extends A {
int b = 20;
}
class C extends B {
int c = 30;
}
public class Test {
public static void main(String[] args) {
C obj = new C();
System.out.println("a: " + obj.a + ", b: " + obj.b + ", c: " + obj.c);
// Output: a: 10, b: 20, c: 30
}
}4. Hierarchical Inheritance
- More than one class inherits from the same base class.
- Example:
class A {
int a = 10;
}
class B extends A {
int b = 20;
}
class C extends A {
int c = 30;
}
public class Test {
public static void main(String[] args) {
B objB = new B();
C objC = new C();
System.out.println("B: a = " + objB.a + ", b = " + objB.b);
System.out.println("C: a = " + objC.a + ", c = " + objC.c);
// Output:
// B: a = 10, b = 20
// C: a = 10, c = 30
}
}5. Hybrid Inheritance
- It is a combination of two or more types of inheritance (e.g., multi-level and hierarchical).
- Java does not directly support hybrid inheritance using classes to avoid complexity but supports it using interfaces.
6. Cyclic Inheritance
- In cyclic inheritance, a class is inherited from itself or two classes extend each other, which is not allowed.
- Example:
class A extends A { } // Not allowed
class B extends C { }
class C extends B { }Understanding the super Keyword
- Purpose: The super keyword is used to access members (variables and methods) of the superclass.
- Usage: It can be used to call the superclass’s constructor and to differentiate between superclass and subclass members if they have the same name.
Accessing Superclass Methods
You can use super to call methods defined in the superclass. This is especially useful if the subclass overrides a method and you still want to access the original method from the superclass.
class Parent {
void display() {
System.out.println("Display from Parent");
}
}
class Child extends Parent {
void display() {
super.display(); // Calls the Parent's display method
System.out.println("Display from Child");
}
}2. Accessing Superclass Constructors
You can use super() to call a constructor from the superclass. This must be the first statement in the subclass constructor. If you don’t explicitly call a superclass constructor, Java will automatically call the no-argument constructor of the superclass.
class Parent {
Parent(int value) {
System.out.println("Parent Constructor: " + value);
}
}
class Child extends Parent {
Child(int value) {
super(value); // Calls Parent's constructor with an argument
System.out.println("Child Constructor");
}
}3. Accessing Superclass Fields
You can use super to access fields (variables) of the superclass, especially if they are shadowed by fields in the subclass.
class Parent {
int number = 10;
}
class Child extends Parent {
int number = 20;
void display() {
System.out.println("Child number: " + number); // Prints 20
System.out.println("Parent number: " + super.number); // Prints 10
}
}Summary
- super(): Calls a superclass constructor.
- super.methodName(): Calls a method from the superclass.
- super.fieldName: Accesses a field from the superclass.
Using super allows you to build upon the functionality of a parent class while still leveraging its existing methods and properties.
Constructors and the super() Statement
- If a constructor is not explicitly defined in the subclass, Java automatically calls the superclass’s no-argument constructor using super().
- The super() statement must always be the first line in the subclass constructor.
- If the superclass does not have a no-argument constructor, you must explicitly call the appropriate superclass constructor.
Question. Can we inherit constructor?
No we cannot inherit constructors because they are not member of a
class(members of classs are methods and variables) and constructors
are mainly used for intialistion of Non‐static variable.
Constructors cannot be inherited but they can be invoked by using
call to super.
Note:-
Object Class in Java
In Java, every class implicitly extends the java.lang.Object class, making it the root of the class hierarchy. When you extend a class, most of its members (methods and variables) are inherited, except for:
— private members: These are not accessible in the subclass.
— local members: These are not inherited because they are confined to the method where they are defined.
— final classes: A class declared as final cannot be inherited.
Using super in Static Context
— The super keyword is used to refer to the superclass and can only be used in instance contexts. You cannot use super in a static context (e.g., inside a static method or static block).
— Additionally, you cannot write super.super.variable, as Java does not support multi-level access to superclasses in this manner.
Thus,
We can inherit public,protected and default methods.
2. We cannot inherit private method because they are accessible only
within class.
3. We can inherit non static, final and abstract methods.
4. We cannot inherit static methds because they will be loaded only once
in SPA.
5. We cannot inherit constructors because they are mainly used for
initialisation and they are not member of class.
Interview question: Tell me in ur Project where you used inheritance ??
Constructor Invocation Order in Multi-Level Inheritance
In a multi-level inheritance scenario, the order of constructor invocation is from the top-most superclass to the subclass. For example:
class A {
A() {
System.out.println("Constructor of A");
}
}
class B extends A {
B() {
System.out.println("Constructor of B");
}
}
class C extends B {
C() {
System.out.println("Constructor of C");
}
}When you create an object of class C, the output will be:
Constructor of A
Constructor of B
Constructor of C
This shows that the constructors are invoked in the order of inheritance.
Default Constructor Behavior
When you create an object of class Hello, the JVM checks for the presence of a default constructor. If none is found, it inserts a default constructor that includes a super() call to invoke the superclass’s default constructor. If the superclass does not have a default constructor and only has parameterized constructors, the JVM cannot provide an implicit call to the superclass constructor, resulting in a compilation error.
Declaring a Class as Private
In Java, top-level classes cannot be declared as private. However, you can declare inner classes as private. A private inner class is only accessible within its enclosing class.
Difference Between super and this
— super:
— Refers to the superclass of the current object.
— Used to access superclass methods and constructors.
— this:
— Refers to the current object.
— Used to access instance variables and methods of the current class.
S-A Relationship (is-a relationship): This represents inheritance in Java. If class B extends class A, then B is a type of A. For example, Dog is an Animal.
- Coupling: Refers to the degree of interdependence between software modules. Low coupling is desirable as it allows modules to be understood and modified independently.
- Aggregation: A form of association representing a “whole-part” relationship where the part can exist independently of the whole. For example, a Library has Books, but books can exist without the library.
Polymorphism
Polymorphism in Java allows objects to be treated as instances of their parent class, making code more flexible and easier to maintain. The concept has two primary forms:
1. Compile-Time Polymorphism (Static Polymorphism)
2. Run-Time Polymorphism (Dynamic Polymorphism)
- Compile-Time Polymorphism (Static Polymorphism)
Technical definition :
a. The process of resolving call to overloaded method during compile
time depending on type of arguments is called compile time
polymorphism.
b. It is also called as static polymorphism or compile time binding.
1. Compile time binding :
• The process of connecting method signature with method implementation during compile time is called as compile time binding.
• Since, binding is happening before execution it is called as Early
binding.
• All static and final methods will get binded during compile time
i.e,during compilation only method will come to know what is its
implementation.
Compile-time polymorphism is achieved using method overloading, where multiple methods share the same name but differ in their parameter lists.
Key points about Method Overloading:
- Method name: Must be the same for all overloaded methods.
- Parameters: Must differ in type, number, or order.
- Return type and access modifier: Can be different but do not affect overloading.
Example
class A {
void m1() {
System.out.println("m1() in A class");
}
int m1(int a) {
System.out.println("m1(int) in A class");
return a;
}
public void m1(byte b) {
System.out.println("m1(byte) in A class");
}
}Here, the m1 method is overloaded with different parameter types. The compiler decides which method to call based on the argument types during method invocation.
2. Runtime Polymorphism (Dynamic Polymorphism)
Technical definition :
a. The process of resolving call to overridden method during run time
depending on type of object we create is called run time polymorphism.
b. It is also called dynamic binding or dynamic polymorphism.
Run time binding / Dynamic Binding:
• The process of connecting method signature with method implementation
during execution time is called as run time binding.
• Since binding is happening during execution it is called as Late
binding.
• All Non static and abstract methods will get binded during run time
i.e, until program will reason Non static and abstarct method can be
overridden.
Runtime polymorphism is achieved through method overriding and dynamic method dispatch.
Method Overriding
During Inheritance subclass has complete previlege to change the
(method)implementation of super class, this process is known as Method
Overriding.
Q. Why do we go for Overriding ??
A. When subclass want the properties of super class but does not want the
implementation of it. In such case, sub class can change implementation
by means of Overriding.
Key points about Method Overriding:
- The method in the subclass must have the same name, return type, and parameters as in the superclass.
- The access modifier of the overridden method must be the same or less restrictive than the superclass method.
In Java, the rules for overriding methods are as follows:
1. Private Methods:
— Cannot be overridden: Private methods are not visible to subclasses, so they cannot be overridden. They can be redefined in a subclass, but this is not overriding; it’s method hiding.
2. Static Methods:
— Cannot be overridden: Static methods are associated with the class rather than instances of the class. They can be redefined in a subclass (method hiding), but this does not constitute overriding. The static method of the superclass is not replaced by the subclass’s static method when called on an instance of the superclass.
3. Final Methods:
— Cannot be overridden: Final methods are declared with the final keyword, which prevents them from being overridden in subclasses. This is intended to maintain specific behavior in the superclass.
Summary:
— Private methods: Cannot be overridden (only redefined).
— Static methods: Cannot be overridden (only hidden).
— Final methods: Cannot be overridden.
These rules ensure encapsulation and control over method behavior in inheritance.
Dynamic Method Dispatch
Dynamic dispatch allows a superclass reference to refer to a subclass object, enabling the overridden method in the subclass to be called at runtime.
Example
class A {
void m1() {
System.out.println("m1 in A class");
}
}
class B extends A {
@Override
void m1() {
System.out.println("m1 in B class");
}
}
public class JTC {
public static void main(String[] args) {
A a = new B(); // Superclass reference to subclass object
a.m1(); // Calls the overridden method in class B
}
}Output:
m1 in B classThis code demonstrates how the method m1 from the subclass B is called even though the reference is of type A. This behavior is determined at runtime.
1. Dynamic Dispatch refers to the process of determining which method to execute at runtime based on the object’s actual type rather than the type of the reference variable.
2. Class Relationships:
— Superclass (A): A general class.
— Subclass (B): A specialized class that extends the superclass.
Code Examples Explained
1. Object Creation:
— A a1 = new A(); // ok
This is straightforward as you are creating an instance of A and assigning it to a reference of type A.
- B b1 = new B(); // ok
Similarly, this is valid because you are creating an instance of B, which is a subclass of A, and assigning it to a reference of type B.
2. Upcasting:
— A a2 = new B(); // ok
This is an example of upcasting. You can assign a subclass object (B) to a superclass reference (A). This is valid because a B is an A.
3. Downcasting:
— B b2 = new A(); // not ok
This is invalid because you cannot assign a superclass object to a subclass reference. A does not guarantee that it is a B.
Additional Class Examples
// Superclass
class Hello {
// Method in the superclass
public void greet() {
System.out.println("Hello from the Hello class!");
}
}
// Subclass
class Hai extends Hello {
// Method in the subclass
public void greet() {
System.out.println("Hello from the Hai class!");
}
// Additional method in the subclass
public void sayHai() {
System.out.println("Hai from the Hai class!");
}
}
- Hai hai2 = new Hello(); // ok
This is again upcasting, where a Hello object is assigned to a Hai reference.
- Hello h2 = new Hai(); // not ok
Similar to the previous example, you cannot downcast a superclass object (Hai) to a subclass reference (Hello).
Type Casting
- Upcasting: Allowed implicitly without type casting (e.g., Hai hai2 = new Hello();).
- Downcasting: Must be explicit and is only valid if the object is actually an instance of the subclass:
— Hello h5 = (Hello) hai2; // not ok if hai2 is not an instance of Hello
- ClassCastException: This runtime exception occurs when you try to downcast a superclass reference that doesn’t actually point to an instance of the subclass:
— In the case of Hello h3 = (Hello) hai1;, it throws a ClassCastException because hai1 is an instance of Hai, not Hello.
Summary of Rules
1. Upcasting (subclass to superclass) is safe and implicit.
2. Downcasting (superclass to subclass) requires explicit casting and can fail at runtime if the object isn’t of the expected subclass type.
3. Always check the actual type of an object before downcasting to avoid runtime exceptions.
Understanding these principles is crucial for effective use of inheritance and polymorphism in object-oriented programming.
Benefits of Polymorphism
- Code Reusability: Allows a general method to work on objects of different classes.
- Maintainability: Simplifies code updates and changes, as you can work with the superclass reference instead of individual subclass references.
- Flexibility: Makes it easy to introduce new classes without changing existing code.
Method Overloading vs. Overriding
- Method Overloading: Involves methods with the same name but different signatures within the same class. It’s determined at compile time.
- Method Overriding: Involves a subclass providing a specific implementation of a method that is already defined in its superclass. It’s determined at runtime.
Using these principles, Java enables robust and scalable applications, supporting a structured approach to code design that can handle complexity efficiently.
Note:-
Calling Methods with Superclass Reference
- Method Availability:
— When you call a method using a superclass reference that points to a subclass object, the method signature must be defined in the superclass. If the method is not present in the superclass, the compiler will throw an error.
class Superclass {
void display() {
System.out.println("Superclass display");
}
}
class Subclass extends Superclass {
void show() {
System.out.println("Subclass show");
}
}
Superclass obj = new Subclass();
obj.display(); // This works, but obj.show(); will give an error.2. Method Resolution:
— When invoking a method on a superclass reference pointing to a subclass object, the JVM first checks if the method signature exists in the superclass. If it does, it then checks if there’s an overridden implementation in the subclass.
— If an overridden method exists in the subclass, that implementation will be executed (demonstrating runtime polymorphism).
Static Methods
1. Static Method Behavior:
— If you call a static method using a superclass reference that points to a subclass object, the superclass’s static method implementation will always be executed. This is because static methods are bound at compile time (early binding).
2. No Runtime Polymorphism with Static Methods:
— Static methods cannot be overridden; they can only be hidden. This means if a subclass defines a static method with the same name, it doesn’t override the method from the superclass — it hides it.
— This results in static methods being resolved at compile time, hence supporting early binding.
Binding Concepts
- Early Binding (Static Binding):
— Applies to static methods and method calls on primitive types and references. The method to be executed is determined at compile time.
- Late Binding (Dynamic Binding):
— Applies to instance methods. The method to be executed is determined at runtime, allowing for the flexibility of polymorphism.
Summary
-Method Availability: Superclass reference must have the method defined for it to be callable.
— Method Resolution: JVM checks superclass first, then subclass for method implementations.
— Static Methods: Always call the superclass’s static method when accessed via a superclass reference; they do not support runtime polymorphism and are resolved at compile time.
Note:-
package Polymorphism5;
class Hello {
void m1() {
System.out.println("m1 in Hello");
}
void m2() {
System.out.println("m2 in Hello");
}
void m3() {
System.out.println("m3 in Hello");
}
}
class Hai extends Hello {
void m1() {
System.out.println("m1 in Hai");
}
void m2() {
System.out.println("m2 in Hai");
super.m2(); // Calls the superclass implementation of m2
}
}
public class Jtc114 {
public static void main(String[] args) {
Hai hai = new Hai();
hai.m1(); // Calls m1 in Hai
hai.m2(); // Calls m2 in Hai, which also calls m2 in Hello
hai.m3(); // Calls m3 in Hello
}
}The output of the program is:
m1 in Hai
m2 in Hai
m2 in Hello
m3 in HelloConceptual Explanation
1. Method Overriding:
— m1() Method:
— The m1() method in the Hai class overrides the m1() method in the Hello class. When hai.m1() is called, it executes the overridden version in Hai, resulting in m1 in Hai.
2. Using super to Access Superclass Implementation:
m2() Method:
— The m2() method in the Hai class overrides the m2() method in the Hello class. Inside this method, super.m2() is called, which invokes the m2() method in the Hello class.
— As a result, when hai.m2() is executed, it first prints m2 in Hai, then calls super.m2(), which prints m2 in Hello.
No Overriding:
m3() Method:
— The m3() method is not overridden in the Hai class. Therefore, when hai.m3() is called, it directly invokes the implementation in the Hello class, resulting in m3 in Hello.
— This illustrates that if you want to retain the superclass’s behavior, you can simply not override the method in the subclass.
Summary
Override to Change Behavior: If you want to change the behavior of a method from the superclass, you override it in the subclass (like m1()).
— Use super to Combine Behavior: If you want to extend the behavior (use both superclass and subclass implementations), you override the method and use super to call the superclass version (like m2()).
— Inherit as-is: If you want to use the superclass implementation without any changes, you simply don’t override the method (like m3()).
This structure allows for flexible and powerful use of polymorphism in Java, enabling you to define and refine behavior across class hierarchies.
-You cannot override the main method in Java in the way you might be thinking. The main method serves as the entry point for any Java application, and it has a specific signature:
public static void main(String[] args)
Key Points:
1. Static Context: The main method is static, meaning it belongs to the class itself rather than instances of the class. Because of this, it cannot be overridden in the traditional sense. Static methods can be hidden, but not overridden.
2. Method Hiding: If you define another main method in a subclass with the same signature, it won’t override the superclass’s main method; instead, it hides it. This means that when you run the program, the JVM will still call the main method of the class you specify on the command line.
3. Calling Main from Main: You can call the main method of the same class or a different class within the main method, but this does not constitute overriding. It simply involves invoking another method.
Example:
Here’s a simple example:
class Parent {
public static void main(String[] args) {
System.out.println("Main method in Parent");
Child.main(args); // Calling Child's main
}
}
class Child extends Parent {
public static void main(String[] args) {
System.out.println("Main method in Child");
}
}Output:
Main method in Parent
Main method in ChildIn this example:
— The main method in Parent calls the main method in Child, but this does not mean Child overrides Parent’s main.
— The main method of Parent runs because it’s the one called when the program starts.
Encapsulation
Encapsulation is the practice of bundling the data (attributes) and methods (functions) that operate on the data into a single unit, typically a class. This approach helps protect the integrity of the data by restricting direct access to it.
Key Concepts of Encapsulation
1. Data Hiding: By making class attributes private, you prevent external code from accessing them directly.
2. Controlled Access: Use public methods (getters and setters) to allow controlled access to the private data.
Why Use Encapsulation?
- Protect Data: Prevent invalid data from being set.
- Maintain Control: Ensure that only valid data is assigned to attributes.
- Flexibility: Change internal implementations without affecting other parts of the code.
Example 1: Simple Encapsulation
Unencapsulated Class
class App {
int monthnum; // Not protected
}
public class Demo {
public static void main(String[] args) {
App a1 = new App();
a1.monthnum = 22; // Invalid value for month
System.out.println("Month number is " + a1.monthnum); // Outputs 22
}
}Problem: The user can set `monthnum` to an invalid value (like 22).
Encapsulated Class
class Calendar {
private int monthnum; // Data member is private
// Setter method
public void setMonthNum(int monthnum) {
if (monthnum >= 1 && monthnum <= 12) {
this.monthnum = monthnum; // Valid month
} else {
System.out.println("Invalid month number"); // Prevents invalid assignment
}
}
// Getter method
public int getMonthNum() {
return monthnum; // Returns the valid month number
}
}
public class UserCal {
public static void main(String[] args) {
Calendar c1 = new Calendar();
c1.setMonthNum(22); // Invalid input
System.out.println("Month number is " + c1.getMonthNum()); // Outputs 0 (default value)
c1.setMonthNum(5); // Valid input
System.out.println("Month number is " + c1.getMonthNum()); // Outputs 5
}
}Explanation
- Private Data Member: The `monthnum` variable is private, so it can’t be accessed directly outside the `Calendar` class.
- Setter Method: `setMonthNum` checks if the input is valid (between 1 and 12) before assigning it.
- Getter Method: `getMonthNum` returns the value of `monthnum`.
Java Bean Class
A Java Bean class is a reusable software component that adheres to specific conventions in its structure. It encapsulates its properties (data members) and provides methods to access and modify those properties through getter and setter methods. This design promotes encapsulation and helps in maintaining data integrity.
Characteristics of a Java Bean Class
1. Private Data Members: All properties of the bean class should be declared private to restrict direct access from outside the class.
2. Public Setter and Getter Methods:
- Each private data member should have a corresponding setter method (to set the value) and a getter method (to retrieve the value).
Setter Method
A setter method is used to assign a value to a private data member. The characteristics of a setter method are:
1. Public Accessibility: The method must be declared `public` so that it can be accessed from outside the class.
2. No Return Type: The setter method does not return any value; its return type is `void`.
3. Arguments: It accepts one argument, which should be of the same type as the data member it is setting.
4. Naming Convention: The method name should start with “set” followed by the name of the data member (with the first letter capitalized).
Example of a Setter Method:
public void setEidPF(long eidPF) {
this.eidPF = eidPF; // 'this' keyword distinguishes the class member from the parameter
}Getter Method
A getter method retrieves the value of a private data member. The characteristics of a getter method are:
1. Public Accessibility: Like setters, getters must also be public.
2. Return Type: The return type must match the data type of the data member.
3. No Arguments: The getter method does not accept any parameters.
4. Naming Convention: The method name should start with “get” followed by the name of the data member (with the first letter capitalized).
Example of a Getter Method:
public long getEidPF() {
return eidPF; // Returns the value of eidPF
}Example of a Java Bean Class
Here’s a complete example that illustrates the concept of a Java Bean class using an `EPF` (Employee Provident Fund) class:
import java.util.Scanner;
class EPF {
private long eidPF; // Employee ID
private String pwd; // Password
// Setter method for eidPF
public void setEidPF(long eidPF) {
this.eidPF = eidPF;
}
// Getter method for eidPF
public long getEidPF() {
if (eidPF == 101123458L) {
System.out.println("Processing your EID, please wait...");
return eidPF;
} else {
System.out.println("Incorrect EID");
return 0;
}
}
// Setter method for pwd
public void setPwd(String pwd) {
this.pwd = pwd;
}
// Getter method for pwd
public String getPwd() {
if (pwd.equals("Hari123")) {
System.out.println("Processing your password, please wait...");
return "Password is Correct";
} else {
System.out.println("Incorrect Password");
return "Please check your Password again";
}
}
}
public class EmployeeEP {
public static void main(String args[]) {
Scanner sc = new Scanner(System.in);
EPF e1 = new EPF();
System.out.println("Please Enter your Employee PF-EID:");
long eid = sc.nextLong();
e1.setEidPF(eid);
System.out.println(e1.getEidPF());
System.out.println("Please Enter your Password:");
String pwd = sc.next();
e1.setPwd(pwd);
System.out.println(e1.getPwd());
}
}Output
When this program is executed, it will prompt the user for their Employee PF-EID and password, and return appropriate messages based on the inputs.
Types of Encapsulation
1. Fully Encapsulated Class: All data members are private, and access is controlled through getter and setter methods.
class A {
private int i;
private float f;
private String s;
// Getter and Setter methods
}2. Partially Encapsulated Class: At least one data member is public, allowing direct access.
class A {
private String name;
public int age; // Direct access allowed
}3. No Encapsulated Class: All data members are public, allowing direct access without any control.
class A {
public String name;
public int age;
}Advantages of Encapsulation
- Data Security: Protects the integrity of the data by restricting access.
- Control Over Data: You can enforce validation rules in setter methods.
- Flexibility: You can change the internal implementation without affecting external code.
- Read-Only or Write-Only: You can create classes that allow only reading (getters) or writing (setters).
Disadvantages of Encapsulation
- Increased Code Length: Requires more boilerplate code due to the need for getters and setters.
- Performance Overhead: Potentially slight overhead due to method calls, though usually negligible.
Object Casting in Java
Object casting is the process of converting one type of object into another. In Java, this involves working with class hierarchies where classes can inherit from one another. There are two primary types of object casting: upcasting and downcasting
1. Upcasting
Definition: Upcasting refers to converting a subclass object to a superclass reference type. Essentially, you create an object of a subclass and store it in a reference variable of the superclass type.
Characteristics:
- Only the superclass’s methods are directly visible when upcasting.
- Subclass behavior is hidden (not accessible directly).
- Upcasting is implicit, meaning it does not require an explicit cast.
Example:
class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
void sound() {
System.out.println("Dog barks");
}
}
public class UpcastingExample {
public static void main(String[] args) {
Animal animal = new Dog(); // Upcasting
animal.sound(); // Outputs: Dog barks
}
}Note: Even though the reference type is `Animal`, the actual object type is `Dog`. Therefore, the `Dog` class’s overridden `sound` method is execute
2. Downcasting
Definition: Downcasting is the process of converting a superclass reference back to a subclass reference. This allows access to the subclass-specific methods and properties.
Characteristics:
- Both superclass and subclass methods are accessible after downcasting.
- Downcasting is explicit, meaning you need to explicitly cast the reference.
Example:
class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
void sound() {
System.out.println("Dog barks");
}
void fetch() {
System.out.println("Dog fetches the ball");
}
}
public class DowncastingExample {
public static void main(String[] args) {
Animal animal = new Dog(); // Upcasting
animal.sound(); // Outputs: Dog barks
Dog dog = (Dog) animal; // Downcasting
dog.fetch(); // Outputs: Dog fetches the ball
}
}Note: The downcast is done using `(Dog) animal`. This allows you to access `Dog`-specific methods.
Important:
- If you try to downcast an object that is not an instance of the subclass, a `ClassCastException` will occur.
Type Casting in Java
In addition to object casting, Java supports type casting for primitive data types. This is divided into two types: widening and narrowing.
1. Widening (Implicit Casting)
Definition: Converting a smaller primitive data type to a larger one.
Types:
- `byte` → `short` → `int` → `long` → `float` → `double`
Characteristics:
- No loss of data.
- Automatic conversion by the compiler.
Example:
byte byteValue = 10;
int intValue = byteValue; // Widening
System.out.println(intValue); // Outputs: 102. Narrowing (Explicit Casting)
Definition: Converting a larger primitive data type to a smaller one.
Types:
- `double` → `float` → `long` → `int` → `short` → `byte`
Characteristics:
- Possible loss of data.
- Requires explicit casting by the programmer.
Example:
double doubleValue = 9.78;
int intValue = (int) doubleValue; // Narrowing
System.out.println(intValue); // Outputs: 9Abstraction in Java
Abstraction is one of the core principles of Object-Oriented Programming (OOP) that focuses on hiding the complex implementation details while exposing only the necessary features to the user. This helps simplify the interaction with the software and enhances usability.
Key Points:
- Hiding Implementation: The internal workings of a method or a class are hidden from the user, who only needs to know how to interact with it.
- Simplification: By exposing only essential features, abstraction makes systems easier to use and understand.
Everyday Examples:
1. Email: When sending an email, the user interacts with a “send” button. The underlying process of how the email is sent (through servers, protocols, etc.) is hidden from the user.
2. Remote Controls: When using a remote for a TV or an AC, users operate it through buttons, without knowing the complex electronics and programming involved.
3. Driving a Car: A driver knows how to accelerate or brake, but they don’t need to understand the intricate mechanics of the car’s engine or braking system.
Achieving Abstraction in Java
In Java, abstraction can be achieved in two main ways:
1. Abstract Classes
2. Interfaces
An abstract class is a blueprint for other classes, allowing shared methods and properties to be defined in a way that provides a foundation for more specific classes. Let’s delve into the core concepts with an emphasis on the rules, usage, and structure of abstract classes and methods.
What is an Abstract Class?
An abstract class is a class that contains at least one abstract method, making it incomplete by nature. The keyword `abstract` signals this incompleteness and indicates that subclasses must provide the specific implementations for any abstract methods within the class.
Abstract Method vs. Concrete Method
1. Abstract (Incomplete) Method:
- This is a method declared with only its signature (the method name and parameters) but without a body or implementation.
- It must be marked with the `abstract` keyword, and it should end with a semicolon (`;`).
- Example:
abstract public void run();2. Concrete (Complete) Method:
- This type of method includes both the method signature and the method body, containing actual logic.
- Example:
public void fly() {
System.out.println("Fly away");
}Rules for Abstract Classes and Methods
1. Instantiation:
- We cannot create an instance of an abstract class directly because it contains incomplete methods without bodies.
- Example:
abstract class Vehicle {
abstract public void noOfWheels();
}
// Vehicle v1 = new Vehicle(); // This would cause a compile-time error.2. Inheritance:
- A subclass can extend an abstract class and must provide implementations for all abstract methods.
- If a subclass does not implement all abstract methods, it must also be declared as abstract.
- This rule provides two approaches:
1. Implement all abstract methods in the subclass.
2. Declare the subclass as abstract and leave further implementation to subsequent subclasses.
3. Constructors:
- An abstract class can contain constructors, allowing initialization of instance variables, even though it cannot be instantiated directly.
- Example:
abstract class Student {
String name;
int id;
public Student(String name, int id) {
this.name = name;
this.id = id;
}
abstract public void details();
}4. Static, Non-static, and Final Members:
- An abstract class can include static, non-static, and final variables, allowing shared constants or values that subclasses can access without instantiation.
5. Absence of Abstract Methods:
- It’s possible for an abstract class not to have any abstract methods. This is often done if the class serves only as a superclass (like a blueprint), with only static methods or for providing shared functionality.
Restrictions with Abstract Classes
1. Final Keyword:
- An abstract class cannot be marked as `final` because final classes cannot be inherited, which conflicts with the purpose of an abstract class.
2. Static Abstract Methods:
- Abstract methods cannot be static because they are meant to be overridden in subclasses, while static methods belong to the class itself, not to instances.
3. Multiple Inheritance:
- Java does not support multiple inheritance for classes, including abstract classes, so a class cannot extend more than one abstract class.
Example Implementations
Example Using Rule 1 (Complete All Abstract Methods)
abstract class Vehicle {
abstract public void price();
abstract public void noOfWheels();
}class RoyalEnfield extends Vehicle {
@Override
public void price() {
System.out.println("1.6 Lakhs");
}
@Override
public void noOfWheels() {
System.out.println("Two wheels");
}
public static void main(String[] args) {
RoyalEnfield r = new RoyalEnfield();
r.noOfWheels();
r.price();
}
}Example Using Rule 2 (Declare Subclass as Abstract)
abstract class Vehicle {
abstract public void price();
abstract public void noOfWheels();
abstract public void model();
}abstract class RoyalEnfield extends Vehicle {
@Override
public void price() {
System.out.println("1.6 Lakhs");
}
@Override
public void noOfWheels() {
System.out.println("Two wheels");
}
}class EnfieldModel extends RoyalEnfield {
@Override
public void model() {
System.out.println("Classic 350");
System.out.println("Interceptor 650");
}
public static void main(String[] args) {
EnfieldModel r = new EnfieldModel();
r.noOfWheels();
r.price();
r.model();
}
}
Key Points on Abstract Classes
- Partial Abstraction: Since abstract classes can contain both concrete and abstract methods, they offer partial abstraction (0–100% abstraction).
- Interfaces for Full Abstraction: To achieve full (100%) abstraction, interfaces are preferred over abstract classes because they only declare methods without implementing them.
FAQ on Abstract Classes
1. Can we declare an abstract class without abstract methods?
Yes, an abstract class may not contain any abstract methods. It’s typically done if we don’t want instances of the class, or we want a base class for other classes.
2. Can we declare an abstract class as final?
No, because making a class `final` prevents it from being subclassed, which contradicts the purpose of an abstract class (which is to be extended).
3. Can an abstract method be declared as final?
No, because `final` methods cannot be overridden, but abstract methods must be overridden in subclasses.
4. Can an abstract method be declared as static?
No, because static methods belong to the class, not instances, and abstract methods are meant to be overridden.
5. Can we achieve multiple inheritance with abstract classes?
No, Java does not support multiple inheritance with classes, abstract or otherwise. However, multiple inheritance can be achieved through interfaces.
Key Points to remember:
- Abstract classes are used to provide a template for other classes.
- You cannot create an instance of an abstract class.
- Subclasses of an abstract class must override its abstract methods or be declared as abstract themselves.
- Abstract classes can have both abstract methods and concrete methods.
- Illegal Combinations:
— You cannot use the combinations “static abstract”, “final abstract”, or “private abstract”.
— Static methods belong to the class rather than instances, while abstract methods are meant to be implemented by subclasses, creating a contradiction.
— Final methods cannot be overridden, which conflicts with the purpose of an abstract method, and private abstract methods can’t be accessed in subclasses.
2. Method Availability:
— When calling a method on a superclass reference that points to a subclass object, the method signature must be defined in the superclass.
— If the method is not present in the superclass, you will receive an error when trying to call it.
3. Constructors in Abstract Classes:
— You cannot instantiate an abstract class directly, which means you can’t create objects of that class.
— However, when you create an object of a subclass that extends the abstract class, the constructor of the abstract class is still invoked.
— If the subclass does not define a constructor, the Java compiler automatically inserts a default constructor and a call to the superclass constructor (using super()).
— This ensures that any initialization logic in the abstract class’s constructor is executed when creating the subclass object.
Thus,
- Abstract Class: Cannot be instantiated directly, but its constructor is called when a subclass object is created.
- Method Signature Requirement: Any method called through a superclass reference must exist in the superclass.
- Constructor Behavior: Subclass constructors implicitly call the superclass constructor unless specified otherwise, ensuring proper initialization of inherited properties.
Conclusion
Abstract classes in Java allow for partial abstraction, meaning we can have both abstract and concrete methods within the same class, which leads to 0 to 100% abstraction. To achieve 100% abstraction, where all methods are abstract, interfaces should be used instead of abstract classes.
Interface in Java
An interface in Java is a blueprint of a class that can contain static constants and abstract methods. It is a way to define a contract for what methods a class must implement, without specifying the implementation itself. Let’s discuss its features, characteristics, and how it is used in Java.
Key Characteristics of Interfaces
1. Declaration: Interfaces are declared using the interface keyword.
interface I1 { }2. Compilation: Like classes, interfaces get compiled into .class files.
3. Variables in Interfaces: All variables in an interface are implicitly public, static, and final.
4. Methods in Interfaces: All methods in an interface are implicitly public and abstract unless explicitly defined as default or static (from Java 8 onwards).
Example of Variables in Interfaces
interface I1 {
int a = 10; // Equivalent to public static final int a = 10;
}
class JTC {
public static void main(String[] args) {
System.out.println(I1.a); // Accessing the static variable
// I1.a = 1000; // Error: cannot assign a value to a final variable
}
}Explanation: The variable a in interface I1 is treated as public static final. Hence, it is a constant, and its value cannot be changed.
Methods in Interfaces
All methods in an interface are abstract by default. You cannot provide a body for these methods directly in the interface.
interface I1 {
void m2(); // Equivalent to public abstract void m2();
}If you try to provide a method body in the interface, it will cause a compilation error because methods in interfaces are abstract by default.
Implementing an Interface
To use the methods of an interface, you need to create a class that implements the interface. The implements keyword is used for this purpose.
interface I1 {
int m1();
void m2();
}
abstract class A implements I1 {
public int m1() {
System.out.println("m1 in A class");
return 10;
}
}
class B extends A {
public void m2() {
System.out.println("m2 in A class");
}
}
class JTC {
public static void main(String[] args) {
I1 i1 = new B();
int a = i1.m1();
i1.m2();
System.out.println("a :- " + a);
}
}Output:
m1 in A class
m2 in A class
a :- 10Explanation:
- Class A implements the I1 interface but does not implement all its methods, so it is declared as abstract.
- Class B extends A and provides the implementation for the remaining method, making it a concrete class.
Multiple Inheritance with Interfaces
Interfaces allow Java to support multiple inheritance. A class can implement multiple interfaces, gaining the ability to inherit behavior from more than one interface.
interface I1 {
int a = 10;
}
interface I2 {
int b = 20;
}
interface I3 {
int c = 30;
}
class A implements I1, I2, I3 {
void m1() {
System.out.println("m1 in A class");
System.out.println("a :- " + a);
System.out.println("b :- " + b);
System.out.println("c :- " + c);
}
}
class JTC {
public static void main(String[] args) {
A a1 = new A();
a1.m1();
}
}Output:
m1 in A class
a :- 10
b :- 20
c :- 30Explanation: Class A implements multiple interfaces, accessing variables from all the interfaces without any ambiguity.
Handling Ambiguity in Multiple Interfaces
If multiple interfaces have variables with the same name, you can resolve the ambiguity by specifying the interface name.
interface I1 {
int a = 10;
}
interface I2 {
int a = 20;
}
interface I3 {
int a = 30;
}
class A implements I1, I2, I3 {
void m1() {
System.out.println("m1 in A class");
System.out.println("I1.a :- " + I1.a);
System.out.println("I2.a :- " + I2.a);
System.out.println("I3.a :- " + I3.a);
}
}
class JTC {
public static void main(String[] args) {
A a1 = new A();
a1.m1();
}
}Explanation: By using the interface names I1.a, I2.a, and I3.a, we can differentiate between the variables with the same name.
Inheritance of Interfaces
An interface can inherit other interfaces using the extends keyword.
interface I1 {
int a = 10;
}
interface I2 {
int b = 20;
}
interface I3 extends I1, I2 {
int c = 30;
}
class A implements I3 {
void m1() {
System.out.println("m1 in A class");
System.out.println("a :- " + a);
System.out.println("b :- " + b);
System.out.println("c :- " + c);
}
}
class JTC {
public static void main(String[] args) {
A a1 = new A();
a1.m1();
}
}Explanation: Interface I3 extends interfaces I1 and I2, so when class A implements I3, it inherits all the members of I1 and I2.
Conflict Resolution in Interfaces
In Java, if a class implements multiple interfaces that contain methods with the same signature, there could be a conflict. Java handles this through explicit rules that guide how to resolve these conflicts.
Scenario 1: Multiple Interfaces with the Same Method Signature
If a class implements multiple interfaces that define the same method, there’s no conflict if the method does not have a default implementation in either interface. The implementing class only needs to provide a single implementation.
interface A {
void show();
}
interface B {
void show();
}
class C implements A, B {
public void show() {
System.out.println("Show method implemented in class C");
}
}In this case, class `C` provides a single implementation for the `show()` method, fulfilling the contract of both interfaces `A` and `B`.
Scenario 2: Multiple Default Methods with the Same Signature
If a class implements multiple interfaces with a default method of the same name and parameters, Java will throw a compilation error due to ambiguity. The implementing class must override the method and provide its own implementation to resolve the conflict.
interface A {
default void display() {
System.out.println("Display from A");
}
}
interface B {
default void display() {
System.out.println("Display from B");
}
}
class C implements A, B {
// Resolving conflict by overriding
@Override
public void display() {
System.out.println("Display resolved in class C");
}
}In this example, class `C` must override the `display()` method to resolve the conflict between `A` and `B`. If it doesn’t, a compilation error will occur.
Scenario 3: Calling a Specific Interface’s Default Method
In cases where you want to specifically call a default method from one of the interfaces, you can use the following syntax within the overriding method:
class C implements A, B {
@Override
public void display() {
A.super.display(); // Calls display() from interface A
}
}Here, `A.super.display()` explicitly calls the `display()` method from interface `A`, providing a way to choose which default method to invoke.
Conflict Resolution Summary
1. Same Method Signature: If the interfaces have the same method signature without default implementations, there’s no conflict, and the class must implement the method.
2. Same Default Methods: If the interfaces have default methods with the same signature, the class must override the method to resolve ambiguity.
3. Choosing a Specific Interface’s Default Method: Use `InterfaceName.super.methodName()` to invoke a specific interface’s default method within the overriding method.
These rules help ensure that Java avoids ambiguity while allowing flexibility in using multiple interfaces.
Default and Static Methods in Interfaces (Java 8+)
From Java 8 onwards, interfaces can have methods with implementations using the default and static keywords.
interface I1 {
default void m1() {
System.out.println("m1 in I1 interface");
}
static int m2() {
System.out.println("m2 in I1 interface");
return 10;
}
}
class A implements I1 { }
class JTC {
public static void main(String[] args) {
A a1 = new A();
a1.m1(); // Calls the default method in the interface
int i = I1.m2(); // Calls the static method in the interface
System.out.println("i :- " + i);
}
}Explanation:
- Default Methods: Allow you to add new methods to interfaces without breaking the existing code.
- Static Methods: Can be called directly using the interface name and are not inherited by implementing classes.
Key Points
- Interface variables are public static final by default.
- Interface methods are public abstract by default (unless declared as default or static).
- A class uses the implements keyword to inherit an interface.
- Interfaces support multiple inheritance.
- Default and static methods in interfaces were introduced in Java 8.
Interfaces provide a way to achieve abstraction and multiple inheritance, making them an essential tool for designing flexible and maintainable code in Java.
Types of interfaces in Java:
1. Marker Interface
A Marker Interface is an interface without any methods or fields. It acts as a “marker” or “tag” to provide some metadata about the class implementing it. Marker interfaces are used to indicate that a class has a certain property or behavior. Java’s standard library includes a few well-known marker interfaces:
- Serializable: Marks a class as capable of being serialized, meaning its state can be converted into a byte stream and saved to a file or sent over a network.
- Cloneable: Marks a class as being capable of cloning, allowing `Object.clone()` to be called on it without throwing a `CloneNotSupportedException`.
- Remote: Marks a class as capable of being accessed remotely in Java RMI (Remote Method Invocation).
Example:
public interface MarkerInterface {} // No methods or fields2. Functional Interface
A Functional Interface has exactly one abstract method, making it a target for lambda expressions in Java. This is particularly useful in functional programming paradigms introduced with Java 8. Functional interfaces can have multiple default or static methods, but only one abstract method is allowed.
The `@FunctionalInterface` annotation is used to indicate that an interface is a functional interface, helping prevent accidental addition of other abstract methods.
- Runnable: Contains the `run()` method, commonly used in multithreading.
- Comparable<T>: Contains the `compareTo()` method for sorting objects.
- Predicate<T>: Contains the `test()` method, often used in streams for filtering elements.
Example:
@FunctionalInterface
public interface Calculator {
int calculate(int a, int b); // Single abstract method
default void print(String message) {
System.out.println(message);
}
}3. SAM (Single Abstract Method) Interface
SAM interfaces are another name for Functional Interfaces, meaning they have one single abstract method (SAM). Any interface with one abstract method can be treated as a SAM interface and used as a target for lambda expressions.
- ActionListener in Swing: Contains a single method, `actionPerformed(ActionEvent e)`.
Example:
@FunctionalInterface
interface Operation {
void perform();
}4. Tagging or Metadata Interface
The Tagging Interface is synonymous with the Marker Interface, as it doesn’t contain any methods or fields. Its purpose is to mark or tag a class with a certain property, allowing classes to be handled differently in certain contexts. For example, `Serializable` is both a Marker and Tagging Interface.
5. Normal Interface
A Normal Interface (or Pure Interface) is any standard interface in Java that contains multiple abstract methods. These interfaces are designed to define a set of related operations that implementing classes must provide.
Example:
public interface Animal {
void eat();
void sleep();
void makeSound();
}6. Abstract Interface
Technically, Abstract Interface isn’t a formal term in Java, but some developers refer to interfaces that use `default` and `static` methods as “abstract interfaces.” These types of interfaces provide a combination of abstract methods and implementations within default or static methods.
Example:
public interface Vehicle {
void drive();
default void startEngine() {
System.out.println("Engine started");
}
static void honk() {
System.out.println("Honk honk!");
}
}Summary
Each type of interface serves a different purpose:
- Marker/Tagging Interface: No methods, adds metadata to classes.
- Functional Interface (SAM Interface): Single abstract method, enables lambda expressions.
- Normal Interface: Multiple abstract methods, for defining complete behaviors.
- Abstract Interface: Combination of abstract methods and default/static methods, allowing partial implementation.
This variety helps Java provide flexibility, enabling both object-oriented and functional programming styles.
Can We Extend a Class and Implement an Interface at the Same Time?
Yes, in Java, a class can extend a superclass and implement one or more interfaces at the same time. This allows for a combination of inheritance from a class and adherence to a contract defined by interfaces. However, a class can only extend one superclass due to Java’s single inheritance model.
Difference Between extends and implements
Inner classes in Java
Inner classes in Java are classes defined within another class or within a method, which allows for logically grouping code to make it more readable and maintainable. They also provide more control over class visibility and help in creating more sophisticated data structures or objects. Let’s go through the key types of inner classes and their characteristics in detail:
Types of Inner Classes in Java:
1. Non-Static Inner Class (Instance Inner Class)
2. Static Inner Class
3. Method Local Inner Class
4. Anonymous Inner Class
1. Non-Static Inner Class (Instance Inner Class)
- Definition: An inner class that does not have the static modifier.
- Characteristics:
- It is an instance member of the outer class, meaning it needs an instance of the outer class to be created.
- It can access all the members (both static and non-static) of the outer class.
- Cannot be accessed directly; requires an object of the outer class.
Example:
class OuterClass {
int outerField = 10;
class InnerClass {
void display() {
System.out.println("Outer Field: " + outerField);
}
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
inner.display();
}
}Output:
Outer Field: 102. Static Inner Class
- Definition: An inner class that has the static modifier.
- Characteristics:
- Does not require an instance of the outer class to be instantiated.
- Can only access static members of the outer class.
- Acts as a regular top-level class except for being nested within another class.
Example:
class OuterClass {
static int staticOuterField = 20;
static class StaticInnerClass {
void display() {
System.out.println("Static Outer Field: " + staticOuterField);
}
}
public static void main(String[] args) {
OuterClass.StaticInnerClass staticInner = new OuterClass.StaticInnerClass();
staticInner.display();
}
}Output:
Static Outer Field: 203. Method Local Inner Class
- Definition: A class defined within a method or a block of code.
- Characteristics:
- Can only be accessed within the method or block where it is defined.
- Can access local variables of the enclosing method only if they are declared as final or effectively final.
- Primarily used for scenarios where you need to perform a specific function that’s local to a method.
Example:
class OuterClass {
void outerMethod() {
int localVariable = 30;
class LocalInnerClass {
void display() {
System.out.println("Local Variable: " + localVariable);
}
}
LocalInnerClass localInner = new LocalInnerClass();
localInner.display();
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
outer.outerMethod();
}
}Output:
Local Variable: 304. Anonymous Inner Class
- Definition: An inner class that does not have a name.
- Characteristics:
- Declared and instantiated all in one statement.
- Usually used when extending a class or implementing an interface with only one method.
- Often used for providing specific implementations to listeners or callbacks.
Example:
abstract class Animal {
abstract void sound();
}
public class Main {
public static void main(String[] args) {
Animal dog = new Animal() { // Anonymous inner class
void sound() {
System.out.println("Woof Woof");
}
};
dog.sound();
}
}Output:
Woof WoofBenefits of Using Inner Classes
1. Encapsulation: Inner classes can group related classes together, leading to better data encapsulation.
2. Code Organization: They help in logically organizing code that is only relevant in the context of the outer class.
3. Enhanced Readability: By grouping related code, inner classes can improve the readability and maintainability of code.
When to Use Inner Classes
- Instance Inner Classes: Use when the inner class requires access to the instance variables or methods of the outer class.
- Static Inner Classes: Use when the inner class does not need access to the outer class’s instance variables or methods.
- Method Local Inner Classes: Use for scenarios that require a class only within a single method.
- Anonymous Inner Classes: Use for implementing interfaces or abstract classes on the fly, especially for callbacks or event handling.
Inner classes are a powerful tool in Java that help in structuring code more elegantly and ensuring modular design. They can make your code more intuitive by linking closely related classes together.
Important Points to remember
1. How many `.class` files will be created if a class contains an inner class?
When a Java class contains an inner class, each inner class generates its own separate `.class` file upon compilation. For example, if you have an outer class `OuterClass` with an inner class `InnerClass`, the following `.class` files will be created:
- `OuterClass.class` (for the outer class)
- `OuterClass$InnerClass.class` (for the inner class)
This naming convention is used to represent the relationship between the outer class and the inner class.
2. Can we write an inner class inside another inner class?
Yes, you can write an inner class inside another inner class. This is known as a nested inner class or multi-level inner class. There is no limit to how deeply you can nest inner classes. Here’s an example:
class OuterClass {
class InnerClass1 {
class InnerClass2 {
void display() {
System.out.println("Inside InnerClass2");
}
}
}
}In this example, `InnerClass2` is nested within `InnerClass1`, which is nested within `OuterClass`.
3. Can we write a class within any interface?
Yes, you can declare a class inside an interface in Java. This is completely valid. By default, the class inside an interface is implicitly `public` and `static`. Here’s an example:
interface MyInterface {
class InnerClass {
void display() {
System.out.println("InnerClass inside Interface");
}
}
}You can access this inner class without needing an instance of the interface since it is `static` by default:
MyInterface.InnerClass obj = new MyInterface.InnerClass();
obj.display();4. Can we declare an inner class as private?
Yes, you can declare an inner class as `private`. When you do so, it can only be accessed within the outer class in which it is defined. For example:
class OuterClass {
private class InnerClass {
void display() {
System.out.println("This is a private inner class");
}
}
void show() {
InnerClass inner = new InnerClass();
inner.display();
}
}In this case, `InnerClass` is private and can only be accessed from within `OuterClass`.
5. Can we declare an outer class as static?
No, you cannot declare an outer class as `static` in Java. Only nested (inner) classes can be declared as `static`. Declaring an outer class as static would not make sense because static is related to members and their association with the instance of a class.
6. Where exactly is memory allocated for instance and static variables of static inner classes?
- Instance Variables: Memory for instance variables of a static inner class is allocated when you create an object of that static inner class.
- Static Variables: Memory for static variables of a static inner class is allocated when the outer class is loaded into memory, similar to how static members of regular classes are handled.
7. Is the `Object` class the superclass of all inner classes too?
Yes, the `Object` class is the superclass of all classes in Java, including inner classes. Every class in Java (whether it’s an inner class, nested class, or top-level class) implicitly extends the `Object` class unless it explicitly extends another class.
8. Between an interface and an abstract class, which one should be chosen?
The choice between using an interface and an abstract class depends on the design requirements:
- Use an Interface if:
- You need to define a contract for unrelated classes to implement.
- Multiple inheritance is required (as a class can implement multiple interfaces).
- All methods in the contract are abstract by default (with Java 8, you can also have default and static methods in interfaces).
- Use an Abstract Class if:
- You want to share code among closely related classes.
- You have a class hierarchy where you need to define common methods and allow subclasses to provide specific implementations.
- You need to maintain state (i.e., have instance variables) or use constructors.
9. What is an Adapter class?
An Adapter class in Java is a design pattern that provides a default implementation of an interface, allowing you to create subclasses without needing to implement all methods in that interface. It acts as a bridge between an interface and a concrete class, simplifying the process of handling interfaces with many methods.
Key Features of an Adapter Class
1. Partial Implementation: An adapter class provides empty or default implementations for all the methods of an interface. You can extend the adapter class and override only the methods you are interested in.
2. Convenience: It allows you to avoid writing boilerplate code for methods you do not need, making your code cleaner and easier to manage.
3. Usage with Listener Interfaces: Adapter classes are commonly used with listener interfaces in Java’s event-handling system. Many interfaces, like `MouseListener` or `KeyListener`, have multiple methods, even if you only need one or two of them.
Example: MouseListener Interface with an Adapter Class
Suppose you want to handle mouse events in a Java GUI application. The `MouseListener` interface has five methods:
- `mouseClicked()`
- `mousePressed()`
- `mouseReleased()`
- `mouseEntered()`
- `mouseExited()`
If you implement `MouseListener` directly, you must provide implementations for all five methods, even if you only need one of them. This is where an adapter class, `MouseAdapter`, becomes useful.
Example Without Adapter Class
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class MyMouseListener implements MouseListener {
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("Mouse clicked");
}
@Override
public void mousePressed(MouseEvent e) {}
@Override
public void mouseReleased(MouseEvent e) {}
@Override
public void mouseEntered(MouseEvent e) {}
@Override
public void mouseExited(MouseEvent e) {}
}In this example, you had to provide empty implementations for all the other methods, even if you didn’t need them.
Example With Adapter Class
Using the `MouseAdapter` class simplifies the code:
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class MyMouseListener extends MouseAdapter {
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("Mouse clicked");
}
}Explanation: By extending the `MouseAdapter` class, you only need to override the method you’re interested in (`mouseClicked()`), and you don’t have to provide implementations for the other methods.
Commonly Used Adapter Classes in Java
Java provides several adapter classes that correspond to specific listener interfaces, such as:
- `MouseAdapter` for the `MouseListener` and `MouseMotionListener` interfaces
- `KeyAdapter` for the `KeyListener` interface
- `WindowAdapter` for the `WindowListener` interface
- `FocusAdapter` for the `FocusListener` interface
These adapter classes provide default (empty) implementations of all methods in their respective listener interfaces.
Advantages of Using Adapter Classes
1. Code Simplification: You don’t have to implement all the methods of an interface, only those that are relevant to your use case.
2. Readability: Adapter classes make the code cleaner and more readable by reducing unnecessary code.
3. Flexibility: It makes it easier to create custom event handling by overriding only the specific methods you need.
When to Use Adapter Classes
- Use adapter classes when working with interfaces that have multiple methods, but you only need to implement a few of them.
- It is especially useful when working with Java’s event-handling framework.
Important Note
Adapter classes are primarily used when implementing listener interfaces. If an interface has only one or two methods, using an adapter class might not be necessary. In that case, implementing the interface directly is often more straightforward.
Example Scenario: Using `WindowAdapter`
Here’s an example using `WindowAdapter` to handle window closing in a GUI application:
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
public class MyFrame extends JFrame {
public MyFrame() {
setTitle("Adapter Class Example");
setSize(300, 200);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.out.println("Window is closing");
System.exit(0);
}
});
}
public static void main(String[] args) {
MyFrame frame = new MyFrame();
frame.setVisible(true);
}
}Explanation: Here, we only handle the `windowClosing()` method using the `WindowAdapter` class, ignoring the rest of the methods of the `WindowListener` interface.
Summary
- An adapter class provides a default implementation of an interface’s methods, allowing you to extend it and override only the methods you need.
- It is useful when you only want to handle specific events in Java’s event-handling system without implementing all methods of the interface.
- Adapter classes reduce boilerplate code, making the code cleaner and more maintainable.Java Packages
In Java, a package is a mechanism used to group related classes, interfaces, and sub-packages together. It helps organize code, manage large projects, and avoid naming conflicts. Let’s go through the different aspects of packages in Java.
Key Points About Packages
1. What is a Package?
- A package in Java is essentially a folder that organizes a set of related classes, interfaces, or sub-packages.
- When you compile a Java program, packages are created automatically in the folder structure corresponding to the package’s name.
2. Purpose of Packages
- Packages help to avoid naming conflicts by grouping related classes together.
- They make the code easier to find, use, and maintain.
- They provide access protection, restricting certain classes to be accessed only within the package.
3. Default Package
- If you don’t explicitly define a package in your Java program, all classes will belong to the default package (usually the current working directory).
- The classes that are part of the default package do not require an explicit import when accessed by other classes in the same directory.
Example Without a Package
Here’s an example of Java code that does not use a package:
class LMS {
public static void main(String[] args) {
Hello h1 = new Hello();
Hai hai = new Hai();
h1.m1();
hai.show();
}
}
class Hello {
void m1() {
System.out.println("m1 in Hello");
}
}
class Hai {
void show() {
System.out.println("show in Hai");
}
}Output:
m1 in Hello
show in Hai- After compiling this code, you’ll get three `.class` files: `LMS.class`, `Hello.class`, and `Hai.class`.
- Since we have not specified any package, these `.class` files will be located in the current working directory.
Creating Your Own Package
You can create your own package in Java using the following syntax:
package com.P1.P2;The package declaration must be the first line in the source file.
Compilation with Package
When using packages, you must compile your Java code with the `-d` flag to specify the directory structure:
- Example: `javac -d . Test.java`
- The `-d` flag instructs the compiler to create the necessary directories to match the package structure in the current directory.
- Example: `javac -d d:\ Test.java`
- This command creates the directory structure on the specified drive (`D:` in this case).
Running the Program with Package
To run a Java program that uses a package, you must specify the fully qualified name of the class:
- Example: `java com.P1.P2.LMS`
This ensures that Java can locate the class file in the correct package.
Example With a Package
Here’s an example where we create a package named `com.P1.P2`:
package com.P1.P2;
class JTC {
public static void main(String[] args) {
Hello h1 = new Hello();
Hai hai = new Hai();
h1.m1();
hai.show();
}
}
class Hello {
void m1() {
System.out.println("m1 in Hello");
}
}
class Hai {
void show() {
System.out.println("show in Hai");
}
}Output:
m1 in Hello
show in Hai- After compiling, a directory structure will be created as `com -> P1 -> P2`, where all `.class` files (`LMS.class`, `Hello.class`, and `Hai.class`) will be stored in the `P2` folder.
Accessing Classes from Another Package
Java enforces rules to access classes from different packages to ensure encapsulation. If you need to access a class from another package, you must use either the fully qualified name or the import statement.
Using Fully Qualified Names
package com.P2;
public class JTC {
public static void main(String[] args) {
com.P1.A a1 = new com.P1.A(); // Using the fully qualified name
a1.m1();
}
}Using the import Statement
package com.P2;
import com.P1.A; // Importing class A from the com.P1 package
public class JTC {
public static void main(String[] args) {
A a1 = new A();
a1.m1();
}
}This approach is more readable because you don’t have to type the full package name each time.
Importing an Entire Package
You can also import all classes in a package using the following syntax:
import com.P1.*; // Imports all classes in the com.P1 packageThis way, you can use all classes (`A`, `B`, `C`, etc.) in the `com.P1` package without explicitly importing each one.
Static Import in Java
Static import allows you to import static members of a class directly so you can use them without the class name.
Example
package com.P2;
import static com.P1.A.a; // Importing static variable 'a'
import static com.P1.A.m1; // Importing static method 'm1'
public class JTC {
public static void main(String[] args) {
System.out.println("a :- " + a); // Using static variable 'a' directly
m1(); // Calling static method 'm1' directly
}
}Summary of Key Points
1. Packages group related classes and help avoid naming conflicts.
2. Use the `-d` flag during compilation to create the package directory structure.
3. To access classes from another package, you must use the fully qualified name or the `import` statement.
4. You can import static members using the `static import` statement.
5. The package declaration must always be the first statement in a Java source file.
Packages are crucial for organizing code in Java, especially in large-scale applications, making them more modular and easier to maintain.
Access Modifiers in Java
Access modifiers in Java are keywords used to define the accessibility or visibility of variables, methods, constructors, and classes. They help manage access control and set the level of access for different parts of a program. In Java, there are four main access modifiers:
1. Public
2. Protected
3. Default (no keyword)
4. Private
Let’s go through each in detail.
1. Public Access Modifier
- Keyword: `public`
- Description: When a member (variable, method, or class) is declared as `public`, it is accessible from any other class, whether in the same package or in a different package.
- Scope:
- Same class: Yes
- Different class in the same package: Yes
- Different class in different package: Yes (through inheritance or directly)
- Outside the package: Yes
- Access Privilege: It has the highest access privilege among all modifiers.
Example:
package com.P1;
public class A {
public int a = 10;
public void display() {
System.out.println("Public member accessed");
}
}2. Protected Access Modifier
- Keyword: `protected`
- Description: Members declared with `protected` are accessible in the same package and can also be accessed in subclasses in different packages using inheritance.
- Scope:
- Same class: Yes
- Different class in the same package: Yes
- Different class in different package: Yes (only through inheritance)
- Outside the package: No (unless accessed through inheritance)
- Note: Objects of a class in a different package cannot directly access protected members without inheritance.
Example:
package com.P1;
public class A {
protected int b = 20;
protected void show() {
System.out.println("Protected member accessed");
}
}3. Default (No Modifier) Access Modifier
- Keyword: No keyword (`default` is not a keyword)
- Description: When no access modifier is specified, the member is considered to have default access. It is only accessible within the same package.
- Scope:
- Same class: Yes
- Different class in the same package: Yes
- Different class in different package: No
- Outside the package: No
- Note: You cannot explicitly use the `default` keyword.
Example:
package com.P1;
class A {
int c = 30; // Default access
void print() {
System.out.println("Default member accessed");
}
}4. Private Access Modifier
- Keyword: `private`
- Description: Private members are only accessible within the class in which they are declared. They are not visible to any other class, even subclasses or classes within the same package.
- Scope:
- Same class: Yes
- Different class in the same package: No
- Different class in different package: No
- Outside the package: No
- Access Privilege: It has the lowest access privilege among all modifiers.
Example:
package com.P1;
public class A {
private int d = 40; // Private access
private void secret() {
System.out.println("Private member accessed");
}
}Example Scenarios
To better understand how these access modifiers work in practice, let’s explore some scenarios using the classes defined above.
Example 1: Accessing from the Same Package
package com.P1;
public class B {
public static void main(String[] args) {
A a1 = new A();
System.out.println(a1.a); // Public access
System.out.println(a1.b); // Protected access
System.out.println(a1.c); // Default access
// System.out.println(a1.d); // Error: Private access
}
}Output:
10
20
30Example 2: Accessing from a Different Package Using Object
package com.P2;
import com.P1.A;
public class C {
public static void main(String[] args) {
A a1 = new A();
System.out.println(a1.a); // Public access
// System.out.println(a1.b); // Error: Protected access
// System.out.println(a1.c); // Error: Default access
// System.out.println(a1.d); // Error: Private access
}
}Output:
10Example 3: Accessing from a Different Package Using Inheritance
package com.P2;
import com.P1.A;
public class D extends A {
public static void main(String[] args) {
D d1 = new D();
System.out.println(d1.a); // Public access
System.out.println(d1.b); // Protected access
// System.out.println(d1.c); // Error: Default access
// System.out.println(d1.d); // Error: Private access
}
}Output:
10
20Summary Table of Access Modifier Visibility
This table summarizes the visibility of each access modifier for different scenarios, helping you decide which one to use based on your needs.
Explain early binding and late binding concept ?
In Java, early binding and late binding are concepts related to method invocation, specifically to when and how the method that should be called is determined by the Java Virtual Machine (JVM). Let’s go into detail about each concept.
1. Early Binding (Static Binding)
- Definition: Early binding, also known as static binding, occurs when the method to be called is determined at compile time. In other words, the method invocation is resolved by the compiler, and the address of the method is linked to the method call before the program runs.
- Use cases: Early binding happens with:
- Static methods
- Private methods
- Final methods
- Methods in the same class without using inheritance
These methods are resolved at compile time because they cannot be overridden.
- Example:
class Animal {
static void makeSound() { // Static method
System.out.println("Some generic animal sound");
}
}
class Dog extends Animal {
static void makeSound() { // Static method, doesn't override
System.out.println("Bark");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog();
animal.makeSound(); // Early binding occurs here
}
}Output:
Some generic animal soundExplanation: In this example, even though the object `animal` refers to an instance of `Dog`, the `makeSound()` method from the `Animal` class is called because it’s a static method. The method is resolved at compile time, which is why it’s known as early binding.
- Advantages: Early binding has a performance advantage because the method to be executed is determined during compilation, reducing the overhead at runtime.
2. Late Binding (Dynamic Binding)
- Definition: Late binding, also known as dynamic binding, occurs when the method to be called is determined at runtime based on the object’s type. This happens because the JVM defers the method resolution until the program runs.
- Use cases: Late binding occurs with:
- Instance methods that can be overridden (non-static and non-final)
- Methods that rely on polymorphism
This enables polymorphic behavior, where the appropriate method for the object type is called at runtime.
Example:
class Animal {
void makeSound() { // Instance method
System.out.println("Some generic animal sound");
}
}
class Dog extends Animal {
@Override
void makeSound() { // Overridden method
System.out.println("Bark");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog();
animal.makeSound(); // Late binding occurs here
}
}- Output:
BarkExplanation: In this case, `animal` is a reference of type `Animal` but is actually pointing to an instance of `Dog`. At runtime, the JVM looks at the actual object (`Dog`) and calls the `makeSound()` method of the `Dog` class, which is why we get the output `Bark`.
- Advantages: Late binding supports polymorphism, allowing code to be more flexible and extensible. It enables overriding methods, where the method called depends on the object’s runtime type.
Key Differences Between Early Binding and Late Binding
Why Early and Late Binding Matter in Java
- Efficiency: Early binding is faster because method calls are resolved at compile time, which makes the code execute more quickly.
- Flexibility: Late binding provides flexibility and extensibility in the code by allowing polymorphism, where the method that gets called depends on the runtime object.
Real-World Example Scenario
Consider an example of a payment system:
- Early Binding: If you have a `final` method that checks the status of a payment, it cannot be overridden by subclasses. This method is bound at compile time, ensuring that the correct version of the method is always used.
- Late Binding: If you have different types of payments like `CreditCardPayment` and `PaypalPayment` that inherit from a superclass `Payment`, and they override a method `processPayment()`, the method that gets called depends on the type of payment object created at runtime.
When to Use Early Binding and Late Binding
- Use early binding when the method does not need to be overridden, as it improves performance by avoiding runtime method lookups.
- Use late binding when you need polymorphic behavior, and the method should behave differently based on the actual object type.
Understanding the differences between early binding and late binding helps in designing efficient and flexible Java programs that leverage both compile-time and runtime capabilities.
How to create singleton class ?
Creating a singleton class in Java ensures that a class has only one instance and provides a global point of access to it. Here are a few common methods to implement a singleton in Java:
1. Lazy Initialization (Thread-Safe)
public class Singleton {
private static Singleton instance;
private Singleton() {
// private constructor to prevent instantiation
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}2. Eager Initialization
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {
// private constructor to prevent instantiation
}
public static Singleton getInstance() {
return instance;
}
}3. Thread-Safe Singleton with Double-Checked Locking
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
// private constructor to prevent instantiation
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}4. Enum Singleton
Using an enum is the simplest way to create a singleton in Java. It is inherently thread-safe and prevents serialization issues.
public enum Singleton {
INSTANCE;
// Any additional methods can be added here
}Summary
- Lazy Initialization is useful if the instance is resource-intensive and may not be needed immediately.
- Eager Initialization is simple and safe, but creates the instance at startup.
- Double-Checked Locking provides a balance of lazy initialization and thread safety.
- Enum Singleton is the recommended approach for its simplicity and built-in serialization protection.










