Book 1: Java Foundations
Chapter 1: The Hello World.
1. Brief Theory: Your First Program!
Every journey begins with a first step, and in programming, that step is almost always "Hello World!". This simple program introduces you to the fundamental structure of a Java application.
- What is a Class? Think of a class as a blueprint or a template for creating objects. For now, just understand that every piece of Java code lives inside a class. It's like a container for your program's instructions.
- The
mainMethod: The Program's Entry Point. When you run a Java application, the Java Virtual Machine (JVM) looks for a special method calledmain. This is where your program starts executing. It's the "front door" of your application.public: This keyword means themainmethod can be accessed from anywhere. The JVM needs to access it.static: This means themainmethod belongs to the class itself, not to an object of the class. You don't need to create an object of your class to runmain.void: This means themainmethod doesn't return any value after it finishes its job.main: The actual name of the method.(String[] args): This is an array ofStringobjects that can hold command-line arguments. You won't use this much initially, but it's there if you need to pass information to your program when you run it.
System.out.println()vs.System.out.printf(): Showing Output.System.out.println(): This is your go-to command for printing text to the console (your screen). Thelnat the end stands for "line," meaning it prints your text and then moves to the next line.System.out.printf(): This stands for "print formatted." It's more powerful when you need to display text and variables with specific formatting (like showing only two decimal places for a number). It doesn't automatically move to the next line after printing, so you usually need to add\nfor a newline.
2. Professional Code: Hello World Examples
Example 1.1: Classic Hello World with println
// Chapter1/HelloWorldClassic.java
public class HelloWorldClassic {
public static void main(String[] args) {
System.out.println("Hello, Java World!");
System.out.println("My first program is running!");
}
}
Example 1.2: Using printf for a Simple Message
// Chapter1/HelloWorldPrintf.java
public class HelloWorldPrintf {
public static void main(String[] args) {
String greeting = "Welcome";
String language = "Java";
System.out.printf("%s to %s Programming!\n", greeting, language);
System.out.printf("This is cool, right?");
}
}
Example 1.3: Combining println and printf
// Chapter1/CombinedOutput.java
public class CombinedOutput {
public static void main(String[] args) {
System.out.println("--- Program Start ---");
String userName = "DAM Student";
int year = 2024;
System.out.printf("Hello, %s! It's currently the year %d.\n", userName, year);
System.out.println("Let's learn some awesome Java!");
System.out.println("--- Program End ---");
}
}
3. Line-by-Line Breakdown
Let's break down HelloWorldClassic.java:
// Chapter1/HelloWorldClassic.java
// This is a single-line comment. It's ignored by the compiler but helps humans understand the code.
public class HelloWorldClassic {
// 'public' makes this class accessible from anywhere.
// 'class' keyword declares a new class.
// 'HelloWorldClassic' is the name of our class. By convention, class names start with an uppercase letter.
public static void main(String[] args) {
// 'public': The JVM can access this method.
// 'static': The method belongs to the class itself, not an instance.
// 'void': This method doesn't return any value.
// 'main': The special name the JVM looks for as the starting point.
// '(String[] args)': Allows command-line arguments (we won't use them now).
// The curly braces `{}` define the block of code that belongs to the main method.
System.out.println("Hello, Java World!");
// 'System': A built-in Java class that provides access to system resources.
// '.out': A static member of the System class, representing the standard output stream (usually your console).
// '.println()': A method of the PrintStream object (out) that prints the given argument to the console
// and then moves the cursor to the next line.
// "Hello, Java World!": This is a String literal, the actual text to be printed.
System.out.println("My first program is running!");
// Another call to println(), printing a different message on a new line.
}
}
Now for HelloWorldPrintf.java:
public class HelloWorldPrintf {
public static void main(String[] args) {
String greeting = "Welcome";
// Declares a variable named 'greeting' of type String and assigns it the value "Welcome".
// We'll dive deep into variables in the next chapter!
String language = "Java";
// Declares another String variable named 'language' and assigns "Java".
System.out.printf("%s to %s Programming!\n", greeting, language);
// 'printf()': Prints formatted output.
// "%s": These are format specifiers. %s is a placeholder for a String.
// The values after the comma (greeting, language) are inserted into the placeholders in order.
// '\n': This is an "escape sequence" for a newline character. It explicitly tells printf to move to the next line.
System.out.printf("This is cool, right?");
// Another printf() call. Notice there's no '\n' here, so the next output would appear on the same line
// if we had more print statements afterwards.
}
}
4. Clean Code Pro-Tips
- Class Names: Always use
CamelCasefor class names (e.g.,MyAwesomeProgram,HelloWorld). Start with an uppercase letter. - File Names: Your Java source file (
.javafile) must have the exact same name as your public class, including capitalization (e.g.,HelloWorldClassic.javaforpublic class HelloWorldClassic). - Indentation: Use consistent indentation (4 spaces or 1 tab) to make your code readable. Most IDEs (Integrated Development Environments like IntelliJ IDEA or VS Code) will handle this for you automatically.
- Comments: Use comments (
//for single line,/* ... */for multi-line) to explain why you did something, not just what you did. - Meaningful Names: Even for simple examples, use names that describe their purpose (
greeting,languageare better thang,l).
5. Unsolved Exercise: My Introduction
Your task is to create a new Java program that introduces yourself.
- Create a class named
MyIntroduction. - Inside its
mainmethod, print your name. - Then, on a new line, print your current school program (e.g., "DAM Student").
- Finally, print a statement saying you are excited to learn Java, using
printfto insert the current year (e.g., 2024).
6. Complete Solution: My Introduction
// Chapter1/MyIntroduction.java
public class MyIntroduction {
public static void main(String[] args) {
System.out.println("Hello! My name is [Your Name Here].");
System.out.println("I am a DAM student.");
int currentYear = 2024;
System.out.printf("I am excited to learn Java in %d!\n", currentYear);
}
}
Chapter 2: Variables & Primitives.
1. Brief Theory: Storing Information
In programming, we constantly need to store and manipulate data. This is where variables come in. Think of a variable as a named container that holds a specific type of data.
- How to Declare Variables: To use a variable, you first need to declare it. This involves specifying its
type(what kind of data it will hold) and itsname. You can optionally give it an initialvalueright away.DataType variableName = initialValue; - What are Primitive Types? (The Stack)
Java has two main categories of data types: primitive types and reference types. For this chapter, we're focusing on primitive types.
- Primitive types store the actual data value directly. They are simple, fundamental data types that are built into Java.
- When you declare a primitive variable, the Java Virtual Machine (JVM) allocates a small chunk of memory on the Stack. The Stack is a fast, organized area of memory primarily used for local variables and method calls. Values on the Stack are managed very efficiently, and their memory is automatically reclaimed when they go out of scope.
- The "Why": Primitive types are efficient. They don't require the overhead of objects, making them faster and consuming less memory for simple values.
- Common Primitive Types:
int: Used for whole numbers (integers) without decimal points. (e.g.,10,-500,0). It has a specific range, roughly from -2 billion to +2 billion.double: Used for floating-point numbers (numbers with decimal points). This is the most common choice for decimal numbers due to its precision. (e.g.,3.14,-0.5,100.0).boolean: Used for logical values, onlytrueorfalse. Essential for making decisions in your code.char: Used for single characters. Enclosed in single quotes (e.g.,'A','z','5','!').
2. Professional Code: Variables in Action
Example 2.1: Declaring and Initializing Different Primitives
// Chapter2/PrimitiveVariables.java
public class PrimitiveVariables {
public static void main(String[] args) {
// Declare and initialize an integer variable for age
int studentAge = 19;
System.out.println("Student Age: " + studentAge);
// Declare and initialize a double variable for GPA (Grade Point Average)
double studentGPA = 3.85;
System.out.println("Student GPA: " + studentGPA);
// Declare and initialize a boolean variable for enrollment status
boolean isEnrolled = true;
System.out.println("Is Student Enrolled? " + isEnrolled);
// Declare and initialize a char variable for a grade
char studentGrade = 'A';
System.out.println("Student Grade: " + studentGrade);
// You can also declare first, then assign
int numberOfCourses; // Declaration
numberOfCourses = 5; // Assignment
System.out.println("Number of Courses: " + numberOfCourses);
}
}
Example 2.2: Basic Operations with Variables
// Chapter2/VariableOperations.java
public class VariableOperations {
public static void main(String[] args) {
int apples = 10;
int oranges = 5;
int totalFruits = apples + oranges; // Addition
System.out.println("Total fruits: " + totalFruits); // Output: 15
double pricePerKg = 2.50;
int weightKg = 3;
double totalPrice = pricePerKg * weightKg; // Multiplication
System.out.println("Total price: " + totalPrice); // Output: 7.5
// Integer division vs. Double division
int totalStudents = 30;
int groupsOf = 7;
int numberOfGroups = totalStudents / groupsOf; // Integer division truncates decimals
System.out.println("Number of groups (int division): " + numberOfGroups); // Output: 4
double totalScore = 95.5;
double maxScore = 100.0;
double percentage = (totalScore / maxScore) * 100; // Division and multiplication
System.out.println("Percentage: " + percentage + "%"); // Output: 95.5%
// Reassigning values
apples = 12; // Update the value of 'apples'
System.out.println("Updated apples: " + apples); // Output: 12
}
}
Example 2.3: Type Interactions and Limitations
// Chapter2/TypeInteractions.java
public class TypeInteractions {
public static void main(String[] args) {
int score = 85;
double percentage = 0.85;
// You can use different types in calculations, Java will often "promote" the smaller type
// 'score' (int) is promoted to double for the multiplication
double finalValue = score * percentage;
System.out.println("Final Value (int * double): " + finalValue); // Output: 72.25
// Implicit type conversion (widening conversion - safe)
// An int can be assigned to a double without loss of information
double scoreAsDouble = score;
System.out.println("Score as double: " + scoreAsDouble); // Output: 85.0
// Explicit type casting (narrowing conversion - potentially loses information)
// You cannot directly assign a double to an int without a cast.
// The decimal part will be truncated.
int roundedScore = (int) finalValue; // Cast 'finalValue' (double) to int
System.out.println("Rounded Score (int cast): " + roundedScore); // Output: 72
// Boolean values can't be directly converted to numbers
boolean isComplete = true;
// int status = isComplete; // This would cause a compile-time error!
System.out.println("Is complete: " + isComplete);
char initial = 'J';
System.out.println("My initial: " + initial);
// Chars are actually stored as numbers (ASCII/Unicode values).
// You can perform arithmetic on them.
char nextChar = (char)(initial + 1); // 'J' is 74, so 74 + 1 = 75 which is 'K'
System.out.println("Next char after 'J': " + nextChar); // Output: K
}
}
3. Line-by-Line Breakdown
Let's look at PrimitiveVariables.java:
public class PrimitiveVariables {
public static void main(String[] args) {
int studentAge = 19;
// 'int': This is the data type. It specifies that 'studentAge' will hold whole numbers.
// 'studentAge': This is the variable name. It's descriptive and follows Java's naming conventions.
// '=': This is the assignment operator. It assigns the value on the right to the variable on the left.
// '19': This is the literal value (an integer) being assigned to 'studentAge'.
// ';': The semicolon terminates the statement. Every statement in Java must end with one.
System.out.println("Student Age: " + studentAge);
// We use the '+' operator here for string concatenation. It joins the string literal
// "Student Age: " with the current value of the 'studentAge' variable (which is 19).
// The result is a single string "Student Age: 19" which is then printed.
double studentGPA = 3.85;
// 'double': Data type for floating-point numbers (numbers with decimals).
// '3.85': A double literal.
boolean isEnrolled = true;
// 'boolean': Data type that can only hold 'true' or 'false'.
// 'true': A boolean literal.
char studentGrade = 'A';
// 'char': Data type for a single character.
// `'A'`: A character literal, enclosed in single quotes.
int numberOfCourses; // Declaration
// Here, we just declare the variable 'numberOfCourses' of type 'int'.
// It doesn't have a value yet (it will have a default value of 0, but it's good practice to assign explicitly).
numberOfCourses = 5; // Assignment
// Now we assign the value '5' to the already declared variable 'numberOfCourses'.
}
}
4. Clean Code Pro-Tips
- CamelCase for Variables: Variable names should start with a lowercase letter, and then capitalize the first letter of each subsequent word (e.g.,
firstName,totalItemsInCart,isStudentEnrolled). - Meaningful Names: Always choose variable names that clearly describe their purpose.
ageis better thana,numberOfStudentsis better thannum. - Initialization: It's good practice to initialize variables when you declare them if you know their initial value. This avoids potential errors.
- Choose the Right Type: Don't use a
doubleif you only need whole numbers (intis more efficient). Don't use anintif you need decimals (double). - Final Variables: If a variable's value should never change after its initial assignment, declare it with the
finalkeyword (e.g.,final double PI = 3.14159;). This is a constant. By convention,finalvariables are named inSCREAMING_SNAKE_CASE.
5. Unsolved Exercise: Student Profile
Your task is to create a Java program that defines a simple profile for a student.
- Create a class named
StudentProfile. - Declare and initialize variables for:
- The student's
age(anint). - The student's
heightin meters (adouble). - Whether the student is
activein sports (aboolean). - The student's
firstInitialof their name (achar).
- The student's
- Print each of these variables to the console, clearly labeled.
- Then, update the student's age (e.g., add 1 year) and print the new age.
6. Complete Solution: Student Profile
// Chapter2/StudentProfile.java
public class StudentProfile {
public static void main(String[] args) {
// 1. Declare and initialize student profile variables
int studentAge = 18; // Student's age in years
double studentHeightMeters = 1.75; // Student's height in meters
boolean isActiveInSports = true; // Is the student active in sports?
char firstInitial = 'A'; // First initial of the student's name
// 2. Print each variable with clear labels
System.out.println("--- Student Profile ---");
System.out.println("Age: " + studentAge + " years");
System.out.println("Height: " + studentHeightMeters + " meters");
System.out.println("Active in Sports: " + isActiveInSports);
System.out.println("First Initial: " + firstInitial);
System.out.println("-----------------------");
// 3. Update the student's age and print the new age
studentAge = studentAge + 1; // Or studentAge++; which we'll see next chapter!
System.out.println("\n--- After One Year ---");
System.out.println("New Age: " + studentAge + " years");
System.out.println("----------------------");
}
}
Chapter 3: Basic Operators.
1. Brief Theory: Performing Actions
Operators are special symbols that tell the Java compiler to perform specific mathematical, relational, or logical operations and produce a result. They are the verbs of your programming language, allowing you to manipulate variables and values.
-
Arithmetic Operators: These are used to perform basic mathematical calculations.
+(Addition): Adds two operands.-(Subtraction): Subtracts the second operand from the first.*(Multiplication): Multiplies two operands./(Division): Divides the first operand by the second. Crucial: If both operands are integers, the result is an integer (decimal part is truncated, not rounded!). If at least one operand is adouble, the result will be adouble.%(Modulo/Remainder): Returns the remainder of the division of the first operand by the second. Very useful for checking even/odd numbers, or wrapping around values.
-
Increment/Decrement Operators: These are shortcuts for adding or subtracting
1from a variable. They only work on numerical variables.++(Increment): Increases the value of a variable by1.--(Decrement): Decreases the value of a variable by1.- Prefix vs. Postfix: This is important!
- Prefix (
++xor--x): The operation (increment/decrement) is performed first, and then the new value is used in the expression. - Postfix (
x++orx--): The current value of the variable is used in the expression first, and then the variable is incremented/decremented.
- Prefix (
-
Operator Precedence: Just like in mathematics, operators have an order of precedence (e.g., multiplication and division happen before addition and subtraction). You can use parentheses
()to explicitly control the order of operations.
2. Professional Code: Operators in Practice
Example 3.1: Arithmetic Operators
// Chapter3/ArithmeticOperations.java
public class ArithmeticOperations {
public static void main(String[] args) {
int num1 = 20;
int num2 = 6;
// Addition
int sum = num1 + num2;
System.out.println("Sum: " + sum); // Output: 26
// Subtraction
int difference = num1 - num2;
System.out.println("Difference: " + difference); // Output: 14
// Multiplication
int product = num1 * num2;
System.out.println("Product: " + product); // Output: 120
// Division (Integer Division vs. Double Division)
int quotientInt = num1 / num2; // Both are int, so result is int (truncates)
System.out.println("Quotient (int): " + quotientInt); // Output: 3 (20 / 6 = 3 with remainder 2)
double quotientDouble = (double) num1 / num2; // Cast one operand to double for float division
System.out.println("Quotient (double): " + quotientDouble); // Output: 3.3333333333333335
// Modulo (Remainder)
int remainder = num1 % num2;
System.out.println("Remainder: " + remainder); // Output: 2
// Combined operations with precedence
int result = num1 + num2 * 2; // num2 * 2 happens first (6*2=12), then num1 + 12 (20+12=32)
System.out.println("Result (num1 + num2 * 2): " + result); // Output: 32
int resultWithParentheses = (num1 + num2) * 2; // Parentheses force addition first (20+6=26), then 26*2=52
System.out.println("Result ((num1 + num2) * 2): " + resultWithParentheses); // Output: 52
}
}
Example 3.2: Increment and Decrement Operators
// Chapter3/IncrementDecrement.java
public class IncrementDecrement {
public static void main(String[] args) {
int counter = 5;
System.out.println("Initial counter: " + counter); // Output: 5
// Postfix Increment: Use current value, then increment
int postIncResult = counter++;
System.out.println("After post-increment (postIncResult): " + postIncResult); // Output: 5 (used current 5, THEN counter became 6)
System.out.println("Counter after postfix: " + counter); // Output: 6
// Prefix Increment: Increment, then use new value
int preIncResult = ++counter;
System.out.println("After pre-increment (preIncResult): " + preIncResult); // Output: 7 (counter became 7, THEN used 7)
System.out.println("Counter after prefix: " + counter); // Output: 7
// Decrement works similarly
int value = 10;
System.out.println("\nInitial value: " + value); // Output: 10
// Postfix Decrement
int postDecResult = value--;
System.out.println("After post-decrement (postDecResult): " + postDecResult); // Output: 10
System.out.println("Value after postfix: " + value); // Output: 9
// Prefix Decrement
int preDecResult = --value;
System.out.println("After pre-decrement (preDecResult): " + preDecResult); // Output: 8
System.out.println("Value after prefix: " + value); // Output: 8
// Simple increment/decrement (when not part of an assignment)
int score = 0;
score++; // score becomes 1
score--; // score becomes 0
System.out.println("\nFinal score after simple increment/decrement: " + score); // Output: 0
}
}
3. Line-by-Line Breakdown
Let's look at key lines from ArithmeticOperations.java and IncrementDecrement.java:
// From ArithmeticOperations.java
int quotientInt = num1 / num2;
// 'num1' is 20, 'num2' is 6.
// Since both 'num1' and 'num2' are 'int', Java performs integer division.
// 20 divided by 6 is 3 with a remainder of 2. The decimal part (.333...) is simply discarded.
// 'quotientInt' will store '3'.
double quotientDouble = (double) num1 / num2;
// '(double) num1': This is a "type cast". It temporarily converts the value of 'num1' (20) into a 'double' (20.0).
// Now, the expression is '20.0 / 6'. Since one operand is a 'double', Java performs floating-point division.
// The result is '3.3333333333333335'.
// 'quotientDouble' will store '3.3333333333333335'.
int remainder = num1 % num2;
// The '%' operator calculates the remainder after division.
// 20 divided by 6 is 3 with a remainder of 2.
// 'remainder' will store '2'.
int result = num1 + num2 * 2;
// Operator Precedence: Multiplication (*) has higher precedence than addition (+).
// First, 'num2 * 2' is calculated: 6 * 2 = 12.
// Then, 'num1 + 12' is calculated: 20 + 12 = 32.
// 'result' will store '32'.
int resultWithParentheses = (num1 + num2) * 2;
// Parentheses '()' explicitly override precedence. The expression inside parentheses is calculated first.
// First, '(num1 + num2)' is calculated: 20 + 6 = 26.
// Then, '26 * 2' is calculated: 52.
// 'resultWithParentheses' will store '52'.
// From IncrementDecrement.java
int postIncResult = counter++;
// 'counter' is currently 5.
// Postfix '++' means:
// 1. Use the *current* value of 'counter' (5) in the assignment to 'postIncResult'. So, 'postIncResult' becomes 5.
// 2. *Then*, increment 'counter' by 1. So, 'counter' becomes 6.
int preIncResult = ++counter;
// 'counter' is currently 6 (from the previous operation).
// Prefix '++' means:
// 1. Increment 'counter' by 1 *first*. So, 'counter' becomes 7.
// 2. *Then*, use the *new* value of 'counter' (7) in the assignment to 'preIncResult'. So, 'preIncResult' becomes 7.
4. Clean Code Pro-Tips
- Parentheses for Clarity: Even if operator precedence would give the correct result, use parentheses
()in complex expressions to make the order of operations explicit and easier to read. - Avoid Over-Complication with
++/--: While++and--are powerful, avoid using them within complex expressions (e.g.,someMethod(++x, y--)). It can make the code hard to understand and debug. Use them on a separate line for clarity (e.g.,x++;thensomeMethod(x, y);). - Watch Out for Integer Division: Be mindful that dividing two integers (
int / int) will always result in an integer, truncating any decimal part. If you need a precise decimal result, cast at least one of the operands to adouble. - Meaningful Variable Names: Keep using descriptive names for variables that store the results of operations (e.g.,
sum,product,remainder).
5. Unsolved Exercise: Budget Calculator
Imagine you're tracking a small budget.
- Create a class named
BudgetCalculator. - Declare an
intvariable forinitialBudgetand set it to1000. - Declare a
doublevariable foritemPriceand set it to25.50. - Declare an
intvariable forquantityand set it to3. - Calculate the
costOfItems(price * quantity) and store it in adoublevariable. - Calculate the
remainingBudget(initial budget - cost of items) and store it in adoublevariable. - Increment a counter variable
transactionCount(initialized to0) using++after each calculation. - Print the
initialBudget,itemPrice,quantity,costOfItems,remainingBudget, andtransactionCountwith clear labels. - Calculate and print how many times the
itemPrice(as aninttype) could be fully bought with theremainingBudget(also as aninttype - requiring casts). Use the/operator for this.
6. Complete Solution: Budget Calculator
// Chapter3/BudgetCalculator.java
public class BudgetCalculator {
public static void main(String[] args) {
// 1. Declare and initialize variables
int initialBudget = 1000;
double itemPrice = 25.50;
int quantity = 3;
int transactionCount = 0; // Initialize transaction counter
System.out.println("--- Budget Details ---");
System.out.println("Initial Budget: $" + initialBudget);
System.out.println("Item Price: $" + itemPrice);
System.out.println("Quantity to buy: " + quantity);
// 5. Calculate cost of items
double costOfItems = itemPrice * quantity;
System.out.println("Cost of " + quantity + " items: $" + costOfItems);
transactionCount++; // Increment after calculation 1
// 6. Calculate remaining budget
double remainingBudget = initialBudget - costOfItems; // int - double results in double
System.out.println("Remaining Budget: $" + remainingBudget);
transactionCount++; // Increment after calculation 2
// 7. Print transaction count
System.out.println("Total transactions processed: " + transactionCount);
// 9. Calculate how many more items can be bought with remaining budget
// We need to cast remainingBudget and itemPrice to int for integer division,
// which means we're only considering whole items.
int itemsPossibleWithRemainingBudget = (int)remainingBudget / (int)itemPrice;
System.out.println("\nWith remaining budget, " + itemsPossibleWithRemainingBudget + " more full items can be bought.");
}
}
Chapter 4: Strings for Beginners.
1. Brief Theory: Working with Text
So far, we've dealt with numbers, single characters, and true/false values. But what about sequences of characters, like names, sentences, or paragraphs? That's where Strings come in!
- Strings are Objects (The Heap): Unlike
int,double,boolean, andchar(which are primitive types),Stringis a reference type. This means aStringvariable doesn't hold the actual text value itself, but rather a reference (a memory address) to where the text data is stored in memory.- This actual text data is stored on the Heap. The Heap is a larger, more flexible area of memory where objects live. Memory on the Heap is managed by Java's Garbage Collector.
- The "Why": Strings can be of variable length, and Java needs a more dynamic way to manage their memory than the fixed-size allocations on the Stack. Objects on the Heap allow this flexibility.
- String Literals vs.
new String():- String Literal: When you create a string like
String name = "Alice";, Java uses a special area called the "String Pool" (a part of the Heap). If "Alice" already exists in the pool, it simply reuses the existing object. If not, it creates a new one. This is generally more efficient. new String(): When you useString name = new String("Alice");, you explicitly force Java to create a new String object on the Heap, even if "Alice" already exists in the String Pool. This is rarely necessary and less efficient.
- String Literal: When you create a string like
.equals()vs.==: Comparing Strings Correctly. This is one of the most common pitfalls for beginners!==: For objects (likeString),==compares their memory addresses. It checks if two variables refer to the exact same object in memory..equals(): This is a method available to all objects. ForStringobjects, it has been overridden to compare the actual content (the sequence of characters) of the strings.- Rule: Always use
.equals()to compare the content of two strings.
- Common String Methods: Strings come with a rich set of methods (functions that objects can perform) to manipulate text.
length(): Returns the number of characters in the string.toUpperCase(): Returns a new string with all characters converted to uppercase.toLowerCase(): Returns a new string with all characters converted to lowercase.charAt(int index): Returns the character at the specified index (position). Remember, indexing starts from0!indexOf(String str): Returns the index of the first occurrence of the specified substring.replace(char oldChar, char newChar): Returns a new string with all occurrences ofoldCharreplaced bynewChar.substring(int beginIndex, int endIndex): Returns a new string that is a substring of this string. The substring begins at the specifiedbeginIndexand extends to the character atendIndex - 1.concat(String str)/+: Concatenates (joins) two strings. The+operator is often preferred for readability.
2. Professional Code: Strings in Action
Example 4.1: String Declaration and Basic Methods
// Chapter4/StringBasics.java
public class StringBasics {
public static void main(String[] args) {
// String Literal - preferred way
String courseName = "Java Programming Foundations";
System.out.println("Course Name: " + courseName);
// Get length of the string
int nameLength = courseName.length();
System.out.println("Length of Course Name: " + nameLength); // Output: 28
// Convert to uppercase
String upperCaseName = courseName.toUpperCase();
System.out.println("Uppercase: " + upperCaseName); // Output: JAVA PROGRAMMING FOUNDATIONS
// Convert to lowercase
String lowerCaseName = courseName.toLowerCase();
System.out.println("Lowercase: " + lowerCaseName); // Output: java programming foundations
// Get character at a specific index (0-based)
char firstChar = courseName.charAt(0);
char lastChar = courseName.charAt(courseName.length() - 1);
System.out.println("First Character: " + firstChar); // Output: J
System.out.println("Last Character: " + lastChar); // Output: s
// Find the index of a substring
int progIndex = courseName.indexOf("Programming");
System.out.println("Index of 'Programming': " + progIndex); // Output: 5
// Check if a string contains another string
boolean containsFoundations = courseName.contains("Foundations");
System.out.println("Contains 'Foundations'? " + containsFoundations); // Output: true
}
}
Example 4.2: == vs. .equals()
// Chapter4/StringComparison.java
public class StringComparison {
public static void main(String[] args) {
String s1 = "hello"; // String literal
String s2 = "hello"; // String literal (references the same object in String Pool)
String s3 = new String("hello"); // New String object on the Heap
String s4 = "world"; // Different content
System.out.println("s1: " + s1);
System.out.println("s2: " + s2);
System.out.println("s3: " + s3);
System.out.println("s4: " + s4);
System.out.println("--------------------");
// Comparing s1 and s2 (both literals, same content)
System.out.println("s1 == s2: " + (s1 == s2)); // Output: true (same object in pool)
System.out.println("s1.equals(s2): " + s1.equals(s2)); // Output: true (same content)
System.out.println("--------------------");
// Comparing s1 and s3 (s1 literal, s3 new object, same content)
System.out.println("s1 == s3: " + (s1 == s3)); // Output: false (different objects in memory)
System.out.println("s1.equals(s3): " + s1.equals(s3)); // Output: true (same content)
System.out.println("--------------------");
// Comparing s3 and s3 (same object, same content)
System.out.println("s3 == s3: " + (s3 == s3)); // Output: true
System.out.println("s3.equals(s3): " + s3.equals(s3)); // Output: true
System.out.println("--------------------");
// Comparing s1 and s4 (different content)
System.out.println("s1 == s4: " + (s1 == s4)); // Output: false
System.out.println("s1.equals(s4): " + s1.equals(s4)); // Output: false
}
}
Example 4.3: String Concatenation and Manipulation
// Chapter4/StringManipulation.java
public class StringManipulation {
public static void main(String[] args) {
String firstName = "Alice";
String lastName = "Smith";
// Concatenation using '+' operator (most common and readable)
String fullName = firstName + " " + lastName;
System.out.println("Full Name: " + fullName); // Output: Alice Smith
// Concatenation using .concat() method
String greeting = "Hello ".concat(firstName).concat("!");
System.out.println("Greeting: " + greeting); // Output: Hello Alice!
// Replacing characters
String originalText = "Java is fun!";
String replacedText = originalText.replace('a', '@');
System.out.println("Original: " + originalText); // Output: Java is fun!
System.out.println("Replaced 'a' with '@': " + replacedText); // Output: J@v@ is fun!
// Substring
String message = "Welcome to Java Programming";
String sub1 = message.substring(0, 7); // From index 0 up to (but not including) index 7
String sub2 = message.substring(11); // From index 11 to the end
System.out.println("Substring (0, 7): " + sub1); // Output: Welcome
System.out.println("Substring (11): " + sub2); // Output: Java Programming
// Trimming whitespace
String messyString = " Hello World ";
String trimmedString = messyString.trim();
System.out.println("Messy: '" + messyString + "'");
System.out.println("Trimmed: '" + trimmedString + "'");
}
}
3. Line-by-Line Breakdown
Let's break down key lines from StringComparison.java and StringManipulation.java:
// From StringComparison.java
String s1 = "hello";
String s2 = "hello";
// Both 's1' and 's2' are String literals. Java's String Pool optimization means
// they both point to the *exact same "hello"* object in memory.
String s3 = new String("hello");
// Using 'new String("hello")' explicitly creates a *brand new* String object on the Heap.
// Even though its content is "hello", it is a different object in memory than the one 's1' and 's2' point to.
System.out.println("s1 == s2: " + (s1 == s2));
// s1 and s2 both point to the *same* memory location (the same object in the String Pool).
// So, '==' (memory address comparison) returns 'true'.
System.out.println("s1.equals(s2): " + s1.equals(s2));
// The '.equals()' method compares the *content* of the strings.
// "hello" is equal to "hello". So, this returns 'true'.
System.out.println("s1 == s3: " + (s1 == s3));
// 's1' points to the "hello" in the String Pool.
// 's3' points to a *new* "hello" object created outside the String Pool.
// They are *different objects in different memory locations*. So, '==' returns 'false'.
System.out.println("s1.equals(s3): " + s1.equals(s3));
// The '.equals()' method compares the *content*.
// The content of 's1' ("hello") is the same as the content of 's3' ("hello").
// So, this returns 'true'.
// From StringManipulation.java
String fullName = firstName + " " + lastName;
// The '+' operator acts as a concatenation operator when used with strings.
// It joins the string 'firstName' ("Alice"), the string literal " ", and the string 'lastName' ("Smith")
// into a single new string "Alice Smith".
String replacedText = originalText.replace('a', '@');
// '.replace(char oldChar, char newChar)' is a method of the String class.
// It searches the 'originalText' ("Java is fun!") for every occurrence of the character 'a'.
// It then creates a *new* string where each 'a' is replaced by '@'.
// Note: Strings are immutable. This method doesn't change 'originalText'; it returns a *new* string.
String sub1 = message.substring(0, 7);
// '.substring(int beginIndex, int endIndex)' extracts a portion of the string.
// 'beginIndex' (0) is inclusive: the character at index 0 ('W') is included.
// 'endIndex' (7) is exclusive: the character at index 7 (the space after 'e' in 'Welcome') is *not* included.
// The result is "Welcome".
4. Clean Code Pro-Tips
- Always Use
.equals()for Content Comparison: This is the golden rule for strings. Using==will lead to subtle bugs that are hard to find. - Prefer String Literals: Unless you have a specific reason to create a new
Stringobject (which is rare in introductory programming), always use string literals ("some text") for better performance and memory efficiency through the String Pool. - Strings are Immutable: Once a
Stringobject is created, its content cannot be changed. Methods liketoUpperCase(),replace(),substring(), or concatenation (+) always return a newStringobject with the modified content. The original string remains untouched. This is a fundamental concept. - Use
+for Concatenation: For simple concatenation, the+operator is generally more readable than theconcat()method. For very complex string building in loops, considerStringBuilder(a more advanced topic). - Meaningful String Content: Just like variable names, ensure the text content of your strings is clear and serves its purpose.
5. Unsolved Exercise: Message Processor
You've received a secret message!
- Create a class named
MessageProcessor. - Declare two
Stringvariables:word1with the value "secret" andword2with the value "java". - Declare a third
StringvariablesecretMessageas a literal "The secret code is java". - Compare
word1and "SECRET" using.equals(). What is the result? Why? - Compare
word2and the substring "java" extracted fromsecretMessageusing both==and.equals(). Print both results and explain the difference. - Concatenate
word1,word2(in that order, separated by a space) to form a new stringcombinedWord. Print it. - Print
secretMessagein all uppercase. - Find the index of the word "code" in
secretMessageand print it. - Replace all occurrences of 'e' with 'E' in
secretMessageand print the new message.
6. Complete Solution: Message Processor
// Chapter4/MessageProcessor.java
public class MessageProcessor {
public static void main(String[] args) {
// 1. Declare String variables
String word1 = "secret";
String word2 = "java";
String secretMessage = "The secret code is java"; // Literal
System.out.println("--- Message Processing ---");
System.out.println("word1: " + word1);
System.out.println("word2: " + word2);
System.out.println("secretMessage: " + secretMessage);
System.out.println("--------------------------");
// 4. Compare word1 and "SECRET" using .equals()
boolean equalsCaseSensitive = word1.equals("SECRET");
System.out.println("Does 'word1' equal 'SECRET' (case-sensitive)? " + equalsCaseSensitive);
// Explanation: It's false because .equals() is case-sensitive. 'secret' != 'SECRET'.
// If we wanted to compare case-insensitively, we'd use word1.equalsIgnoreCase("SECRET").
// 5. Compare word2 and substring "java" from secretMessage
String subFromSecretMessage = secretMessage.substring(19); // "java" starts at index 19
System.out.println("\nSubstring 'java' from secretMessage: " + subFromSecretMessage);
boolean equalsOperatorComparison = (word2 == subFromSecretMessage);
System.out.println("word2 == subFromSecretMessage: " + equalsOperatorComparison);
// Explanation: False. 'word2' is a literal from the String Pool.
// 'subFromSecretMessage' is a *new* String object created by the substring() method (it's not from the pool).
// They are different objects in memory.
boolean equalsMethodComparison = word2.equals(subFromSecretMessage);
System.out.println("word2.equals(subFromSecretMessage): " + equalsMethodComparison);
// Explanation: True. The content of both strings is "java".
// 6. Concatenate word1 and word2
String combinedWord = word1 + " " + word2;
System.out.println("\nCombined Word: " + combinedWord); // Output: secret java
// 7. Print secretMessage in all uppercase
System.out.println("Secret Message in Uppercase: " + secretMessage.toUpperCase());
// 8. Find the index of "code"
int indexOfCode = secretMessage.indexOf("code");
System.out.println("Index of 'code': " + indexOfCode); // Output: 11
// 9. Replace 'e' with 'E' in secretMessage
String replacedMessage = secretMessage.replace('e', 'E');
System.out.println("Message with 'e' replaced by 'E': " + replacedMessage);
}
}
Chapter 5: Conditionals (IF/ELSE).
1. Brief Theory: Making Decisions
Life is full of decisions, and so is programming! Conditional statements allow your program to execute different blocks of code based on whether certain conditions are true or false. This is how your programs become dynamic and responsive.
- Boolean Logic: The heart of conditionals is boolean logic. Conditions are expressed as boolean expressions (expressions that evaluate to either
trueorfalse).- Comparison Operators: Used to compare two values, resulting in a boolean.
>(greater than)<(less than)>=(greater than or equal to)<=(less than or equal to)==(equal to - for primitives!)!=(not equal to - for primitives!)
- Logical Operators: Used to combine multiple boolean expressions.
&&(AND): Returnstrueif both operands aretrue.||(OR): Returnstrueif at least one operand istrue.!(NOT): Reverses the boolean value (flipstruetofalse, andfalsetotrue).
- Comparison Operators: Used to compare two values, resulting in a boolean.
ifStatement: The most basic conditional. Executes a block of code only if the specified condition istrue.if (condition) { // Code to execute if condition is true }if-elseStatement: Provides an alternative block of code to execute if theifcondition isfalse.if (condition) { // Code if condition is true } else { // Code if condition is false }if-else if-elseChain: Used when you have multiple conditions to check in a specific order. The firsttruecondition's block is executed, and the rest are skipped. The finalelseis a catch-all if none of the precedingiforelse ifconditions are met.if (condition1) { // Code if condition1 is true } else if (condition2) { // Code if condition1 is false, AND condition2 is true } else if (condition3) { // Code if condition1 and condition2 are false, AND condition3 is true } else { // Code if none of the above conditions are true }- Ternary Operator (
? :): A shorthand for simpleif-elsestatements, often used for assigning a value based on a condition. It's a single expression.
This reads as: "Isresult = (condition) ? valueIfTrue : valueIfFalse;conditiontrue? If yes,resultgetsvalueIfTrue. If no,resultgetsvalueIfFalse."
2. Professional Code: Conditionals in Action
Example 5.1: Simple if and if-else
// Chapter5/SimpleConditionals.java
public class SimpleConditionals {
public static void main(String[] args) {
int score = 75;
int passingScore = 60;
// Simple if statement
if (score > passingScore) {
System.out.println("Congratulations! You passed the exam.");
}
// if-else statement
if (score >= 80) {
System.out.println("You got a good grade!");
} else {
System.out.println("You might want to review the material.");
}
System.out.println("Your score: " + score);
// Example with boolean variable
boolean isLoggedIn = false;
if (isLoggedIn) {
System.out.println("Welcome back, user!");
} else {
System.out.println("Please log in to continue.");
}
}
}
Example 5.2: if-else if-else Chain with Logical Operators
// Chapter5/GradingSystem.java
public class GradingSystem {
public static void main(String[] args) {
int studentScore = 88;
char grade;
if (studentScore >= 90) {
grade = 'A';
System.out.println("Excellent! Grade: " + grade);
} else if (studentScore >= 80) { // studentScore is less than 90 AND greater than or equal to 80
grade = 'B';
System.out.println("Very good! Grade: " + grade);
} else if (studentScore >= 70) { // studentScore is less than 80 AND greater than or equal to 70
grade = 'C';
System.out.println("Good effort! Grade: " + grade);
} else if (studentScore >= 60) { // studentScore is less than 70 AND greater than or equal to 60
grade = 'D';
System.out.println("Pass! Grade: " + grade);
} else { // All other cases (studentScore < 60)
grade = 'F';
System.out.println("Unfortunately, you failed. Grade: " + grade);
}
System.out.println("Final Grade: " + grade);
// Example with logical operators: Eligibility check
int age = 20;
boolean isCitizen = true;
if (age >= 18 && isCitizen) { // Both conditions must be true
System.out.println("You are eligible to vote.");
} else {
System.out.println("You are NOT eligible to vote.");
}
boolean hasLicense = false;
boolean hasVehicle = true;
if (hasLicense || hasVehicle) { // At least one condition must be true
System.out.println("You have either a license or a vehicle (or both).");
} else {
System.out.println("You have neither a license nor a vehicle.");
}
boolean isSunny = true;
if (!isSunny) { // Not sunny means it's not true (so it's false)
System.out.println("It's not sunny today. Might rain!");
} else {
System.out.println("It's sunny today. Enjoy!");
}
}
}
Example 5.3: Ternary Operator
// Chapter5/TernaryOperatorExample.java
public class TernaryOperatorExample {
public static void main(String[] args) {
int temperature = 25;
String weatherStatus = (temperature > 20) ? "Warm" : "Cool";
System.out.println("Weather Status: " + weatherStatus); // Output: Warm
temperature = 15;
weatherStatus = (temperature > 20) ? "Warm" : "Cool";
System.out.println("Weather Status: " + weatherStatus); // Output: Cool
// Another example: check if a number is even or odd
int number = 7;
String parity = (number % 2 == 0) ? "Even" : "Odd";
System.out.println(number + " is " + parity); // Output: 7 is Odd
number = 10;
parity = (number % 2 == 0) ? "Even" : "Odd";
System.out.println(number + " is " + parity); // Output: 10 is Even
// Ternary operator can also be used directly in print statements
System.out.println("Is " + number + " positive? " + (number > 0 ? "Yes" : "No"));
}
}
3. Line-by-Line Breakdown
Let's break down key lines from GradingSystem.java and TernaryOperatorExample.java:
// From GradingSystem.java
if (studentScore >= 90) {
grade = 'A';
System.out.println("Excellent! Grade: " + grade);
} else if (studentScore >= 80) {
// This 'else if' block is only reached if 'studentScore >= 90' was FALSE.
// So, effectively, this condition checks if (studentScore < 90 AND studentScore >= 80).
// This implicit chaining is why the order of 'else if' statements matters!