【Java】类和对象篇

面向对象概述

面向对象的基本概念

  1. 对象(Object)
    • 定义:对象是现实世界中实体的抽象,具有状态(属性)和行为(方法)。
    • 特点:
      • 状态:描述对象的静态特征(如汽车的品牌、颜色)。
      • 行为:描述对象的动态特征(如汽车的启动、加速)。

示例:

// 定义一个汽车对象
Car myCar = new Car();
myCar.brand = "Toyota";  // 状态
myCar.start();           // 行为
  1. 类(Class)
    • 定义:类是对象的模板,描述一类对象的共同特征和行为。
    • 关系:类是抽象,对象是类的实例。

示例:

// 定义汽车类
public class Car {
    // 状态(成员变量)
    String brand;
    int speed;

    // 行为(方法)
    void start() {
        System.out.println("Car is starting...");
    }
}
  1. 消息传递
    • 定义:对象之间通过方法调用进行交互,包含接收对象、方法名和参数。

示例:

class Calculator {
    int add(int a, int b) {
        return a + b;
    }
}

public class Main {
    public static void main(String[] args) {
        Calculator cal = new Calculator();
        int result = cal.add(3, 5); // 消息传递:调用add方法
        System.out.println("结果:" + result); // 输出:8
    }
}

面向对象的基本特征

  1. 封装(Encapsulation)
    • 核心思想:隐藏内部细节,通过公共接口访问数据。
    • 实现方式:
      • 使用 private 保护成员变量。
      • 通过 public 的 getter 和 setter 方法操作数据。

示例:

class BankAccount {
    private double balance;

    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            System.out.println("存款成功,余额:" + balance);
        }
    }

    public void withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            System.out.println("取款成功,余额:" + balance);
        }
    }
}
  1. 继承(Inheritance)
    • 核心思想:子类继承父类的属性和方法,实现代码复用。
    • 语法:使用 extends 关键字。

示例:

class Animal {
    String name;
    void eat() {
        System.out.println(name + "在吃东西");
    }
}

class Dog extends Animal {
    void bark() {
        System.out.println(name + "汪汪叫");
    }
}

// 使用继承类
public class Main {
    public static void main(String[] args) {
    	Dog myDog = new Dog();
		myDog.name = "多多";
		myDog.eat(); // 调用父类方法
		myDog.bark(); // 调用子类特有方法
    }
}
  1. 多态(Polymorphism)
    • 核心思想:同一方法在不同场景下有不同实现。
    • 实现方式:
      • 方法重载:同一类中同名方法参数不同。
      • 方法覆盖:子类重写父类方法。

示例:

// 方法重载
class MathUtils {
    int add(int a, int b) { return a + b; }
    double add(double a, double b) { return a + b; }
}

// 方法覆盖
class Animal {
    String name;
    void eat() {
        System.out.println(name + "在吃东西");
    }
}
class Cat extends Animal {
    @Override
    void eat() {
        System.out.println("猫吃鱼");
    }
}

面向对象编程(OOP)的优势

易维护: 封装和继承使得代码结构更加清晰,各个模块之间的耦合度降低,当需要修改某个功能时,只需要修改相关的类和方法,而不会影响到其他部分的代码。
可重用: 通过继承和组合,可以复用已有的类和方法,减少代码的重复编写,提高开发效率。
可扩展: 多态性使得代码具有良好的扩展性,当需要添加新的功能或类型时,只需要创建新的子类并实现相应的方法即可,而不需要修改现有的代码。

类的定义与对象操作

类的定义

类声明:

[访问修饰符] [修饰符] class 类名 [extends 父类名] [implements 接口名列表] {
    // 类体
}

类声明包含访问修饰符(如 public 或缺省)、类名、继承信息(使用 extends 关键字)和实现接口信息(使用 implements 关键字)。如果没有指定继承的类,默认继承自 Object 类。例如以下代码示例:

// 公共类,可被其他包中的类访问
public class MyClass {
    // 类体
}

// 缺省访问修饰符,只能在同一个包中访问
class AnotherClass {
    // 类体
}

// 继承自 MyClass 类
class SubClass extends MyClass {
    // 类体
}

类体的定义:
类体包含构造方法、成员变量和成员方法。
成员变量:
成员变量也称为属性,用于存储类或对象的状态信息。
语法:[访问修饰符] [修饰符] 数据类型 变量名 [= 初始值];
说明:

  • 访问修饰符:如 public、private、protected 或默认(无修饰符),用于控制成员变量的访问权限。
  • 修饰符:例如 static(静态变量,属于类而不是对象)、final(常量,值不可变)等。
  • 数据类型:可以是基本数据类型(如 int、double 等)或引用数据类型(如 String、自定义类等)。
  • 初始值:可选,用于在声明变量时为其赋初始值。

构造方法:
构造方法用于创建类的对象,并对对象的成员变量进行初始化。
语法:

[访问修饰符] 类名(参数列表) {
    // 构造方法体
}

说明:

  • 访问修饰符:控制构造方法的访问权限。
  • 类名:构造方法的名称必须与类名相同。
  • 参数列表:可以包含零个或多个参数,用于接收创建对象时传递的值。
  • this 关键字:用于引用当前对象,在构造方法中可用于区分成员变量和局部变量。

成员方法:
成员方法用于定义类的行为,实现特定的功能。
语法:

[访问修饰符] [修饰符] 返回值类型 方法名(参数列表) {
    // 方法体
    [return 返回值;]
}

说明:

  • 访问修饰符:控制方法的访问权限。
  • 修饰符:如 static(静态方法,属于类而不是对象)、final(最终方法,不能被重写)等。
  • 返回值类型:指定方法返回值的数据类型,如果方法不返回任何值,则使用 void。
  • 方法名:遵循 Java 命名规范,通常采用小驼峰命名法。
  • 参数列表:可以包含零个或多个参数,用于接收调用方法时传递的值。
  • return 语句:用于从方法中返回一个值,返回值的类型必须与方法声明的返回值类型一致。

以下是一个 Car 类的类声明以及类体定义的代码示例:

public class Car {
    private String brand;
    private int wheels;

    public Car(String brand, int wheels) {
        this.brand = brand;
        this.wheels = wheels;
    }

    // 成员方法,无返回值,打印汽车信息
    public void displayInfo() {
        System.out.println("Brand: " + brand + ", Wheels: " + wheels);
    }

    // 成员方法,有返回值,返回轮子数量
    public int getWheels() {
        return wheels;
    }
}

对象的创建与使用

对象创建:
对象创建有两种格式,以上面的 Car 类为例:

  1. 先声明对象的引用,然后使用 new 关键字实例化对象。
Car car;
car = new Car();
  1. 声明对象的引用的同时使用 new 关键字实例化对象。(推荐这种格式)
Car car = new Car();

访问成员:
使用点号运算符(.)访问类的成员变量和调用成员方法。以下是一个对象 rectangle 访问 Rectangle 类中成员变量以及调用成员方法的代码示例:

class Rectangle {
    double length;
    double width;

    public double getArea() {
        return length * width;
    }
    
    public static void main(String[] args) {
        Rectangle rectangle = new Rectangle();
        rectangle.length = 5;// 通过( .成员变量 )来定义成员变量
        rectangle.width = 3;
        double area = rectangle.getArea();// 通过( .成员方法 )来访问成员方法
        System.out.println("矩形的长是:" + rectangle.length);// 通过( .成员变量 )来访问成员变量
        System.out.println("矩形的宽是:" + rectangle.width);
        System.out.println("矩形的面积是:" + area);
    }
}

程序运行结果如下:

矩形的长是:5.0
矩形的宽是:3.0
矩形的面积是:15.0

对象引用赋值:
将一个对象的引用赋给另一个变量,两个变量将指向同一个对象。以下是一个坐标 Point 类引用赋值的代码示例:

class Point {
    int x;
    int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public void printCoordinates() {
        System.out.println("坐标:(" + x + ", " + y + ")");
    }

    public static void main(String[] args) {
        Point point1 = new Point(1, 2);
        Point point2 = point1;

        point2.x = 3;

        point1.printCoordinates(); // 输出:坐标:(3, 2)
        point2.printCoordinates(); // 输出:坐标:(3, 2)
    }
}

程序运行结果如下:

坐标:(3, 2)
坐标:(3, 2)

感兴趣或者想进一步了解引用赋值可以点击【Java】引用赋值进行阅读。

方法的使用

方法设计

方法的返回值
方法的返回值是方法调用结束后返回给调用者的数据。在定义方法时,需要指定返回值类型。如果方法有返回值,必须使用 return 语句将结果返回;如果方法没有返回值,返回值类型用 void 表示。

  1. 有返回值的方法
class Calculator {
    // 定义一个有返回值的方法,返回两个整数的和
    int add(int a, int b) {
        return a + b;
    }
}

在这个 add 方法中,返回值类型是 int,表示该方法会返回一个整数。return a + b; 语句将计算结果返回给调用者。

  1. 无返回值的方法
class Calculator {
    // 定义一个无返回值的方法,用于打印一条消息
    void printMessage() {
        System.out.println("这是一条无返回值的方法");
    }
}

在这个 printMessage 方法中,返回值类型是 void,表示该方法不返回任何数据。方法体中没有 return 语句(如果要提前结束方法,可以使用 return;,但通常不需要)。

方法的参数
方法可以有参数,也可以没有参数。有参数的方法在定义时要指定参数类型和名称,调用时要传递实际参数。参数的作用是将数据传递给方法,让方法根据这些数据进行相应的操作。

  1. 有参数的方法
class MathUtils {
    // 定义一个有参数的方法,计算两个小数的乘积
    double multiply(double a, double b) {
        return a * b;
    }
}

在这个 multiply 方法中,有两个参数 a 和 b,参数类型都是 double。在调用该方法时,需要传递两个 double 类型的实际参数。

  1. 无参数的方法
class MathUtils {
    // 定义一个无参数的方法,用于打印一条信息
    void printInfo() {
        System.out.println("这是一个无参方法");
    }
}

在这个 printInfo 方法中,没有参数,调用时不需要传递任何数据。

方法的实现
方法体是对方法功能的具体实现,包括局部变量声明和合法的 Java 语句。局部变量是在方法内部声明的变量,它们的作用域仅限于该方法。

class StringUtils {
    // 定义一个方法,用于反转字符串
    String reverseString(String str) {
        // 声明一个局部变量,用于存储反转后的字符串
        StringBuilder sb = new StringBuilder(str);
        // 调用 StringBuilder 的 reverse 方法反转字符串
        return sb.reverse().toString();
    }
}

在这个 reverseString 方法中,sb 是一个局部变量,用于存储反转后的字符串。方法体中通过 StringBuilder 类的 reverse 方法实现了字符串的反转,并将结果返回。

方法签名
方法签名是方法名、参数个数、参数类型和参数顺序的组合,用于区分类中的不同方法,不包括方法的返回值。也就是说,在同一个类中,不能有两个方法的方法签名完全相同。

class MethodSignatureDemo {
    // 方法 1
    void printInfo(int num) {
        System.out.println("传入的整数:" + num);
    }
    // 方法 2
    void printInfo(String str) {
        System.out.println("传入的字符串:" + str);
    }
    // 方法 3
    void printInfo(int a, int b) {
        System.out.println("传入的两个整数:" + a + "和" + b);
    }
}

在这个 MethodSignatureDemo 类中,有三个 printInfo 方法,它们的方法名相同,但参数个数和类型不同,因此方法签名不同,是合法的方法重载。

方法的调用

实例方法的调用
实例方法是属于对象的方法,必须通过对象引用才能调用。调用实例方法的步骤是:先创建对象,然后使用对象引用变量和点号运算符来调用方法。

class Animal {
    // 实例方法,用于表示动物的移动行为
    void move() {
        System.out.println("动物在移动");
    }
}
public class Main {
    public static void main(String[] args) {
        // 创建 Animal 对象
        Animal animal = new Animal();
        // 通过对象引用调用实例方法
        animal.move();
    }
}

在这个例子中,move 是 Animal 类的实例方法,必须先创建 Animal 对象 animal,然后通过 animal.move() 来调用该方法。

静态方法的调用
静态方法是属于类的方法,不需要创建对象就可以调用。通常使用类名直接调用静态方法,也可以通过对象引用调用,但不推荐这种方式,因为静态方法不依赖于对象的状态。

class MathUtils {
    // 静态方法,用于计算一个数的平方根
    public static double squareRoot(double num) {
        return Math.sqrt(num);
    }
}
public class Main {
    public static void main(String[] args) {
        // 使用类名调用静态方法
        double result = MathUtils.squareRoot(16);
        System.out.println("16 的平方根:" + result);
    }
}

在这个例子中,squareRoot 是 MathUtils 类的静态方法,可以直接使用 MathUtils.squareRoot(16) 来调用。

方法调用的场合
方法调用主要用于以下几种场合:

  1. 对象引用调用实例方法
    如前面提到的 Animal 类的 move 方法,通过创建对象并使用对象引用调用实例方法,这种方式用于调用与对象状态相关的方法。
  2. 类中方法调用本类其他方法
    在一个类的方法中,可以调用本类的其他方法,包括实例方法和静态方法。
class Utility {
    // 实例方法
    void instanceMethod() {
        System.out.println("这是实例方法");
        // 类中实例方法调用静态方法
        staticMethod();
    }
    // 静态方法
    static void staticMethod() {
        System.out.println("这是静态方法");
    }
}
public class Main {
    public static void main(String[] args) {
        // 创建 Utility 对象
        Utility util = new Utility();
        // 对象引用调用实例方法
        util.instanceMethod();
        // 类名调用静态方法
        Utility.staticMethod();
    }
}

在这个例子中,instanceMethod 是实例方法,在该方法中调用了静态方法 staticMethod。

  1. 类名直接调用静态方法
    如前面提到的 MathUtils 类的 squareRoot 方法,使用类名直接调用静态方法,这种方式用于调用与类相关而不依赖于对象状态的方法。

方法参数的传递

基本数据类型的参数传递
对于基本数据类型(如 int、double、char 等),在方法调用时,传递的是实际值的副本,而不是实际值本身。也就是说,在方法内部对参数的修改不会影响到方法外部的原始值。
示例代码:

public class PrimitiveParameterPassing {
    public static void main(String[] args) {
        int num = 10;
        System.out.println("调用方法前 num 的值: " + num);
        modifyValue(num);
        System.out.println("调用方法后 num 的值: " + num);
    }
    public static void modifyValue(int value) {
        value = 20;
        System.out.println("方法内部修改后 value 的值: " + value);
    }
}
  • 在 main 方法中定义了一个 int 类型的变量 num,并初始化为 10。
  • 调用 modifyValue 方法时,将 num 的值 10 复制一份传递给参数 value。
  • 在 modifyValue 方法内部,将 value 的值修改为 20,但这只会影响 value 这个副本,不会影响 main 方法中的 num 变量。
  • 因此,最终输出结果显示 num 的值仍然是 10。

引用数据类型的参数传递
对于引用数据类型(如类、数组、接口等),在方法调用时,传递的是对象引用的副本,而不是对象本身。这意味着虽然传递的是引用的副本,但它们指向同一个对象。所以在方法内部对对象的修改会影响到方法外部的原始对象。
示例 1:修改对象的属性

class Person {
    String name;
    public Person(String name) {
        this.name = name;
    }
}
public class ReferenceParameterPassing {
    public static void main(String[] args) {
        Person person = new Person("Alice");
        System.out.println("调用方法前 person 的 name: " + person.name);
        modifyPerson(person);
        System.out.println("调用方法后 person 的 name: " + person.name);
    }
    public static void modifyPerson(Person p) {
        p.name = "Bob";
        System.out.println("方法内部修改后 person 的 name: " + p.name);
    }
}
  • 在 main 方法中创建了一个 Person 对象,并将其引用赋值给变量 person。
  • 调用 modifyPerson 方法时,将 person 引用的副本传递给参数 p,p 和 person 指向同一个 Person 对象。
  • 在 modifyPerson 方法内部,修改了 p 所指向对象的 name 属性,由于 p 和 person 指向同一个对象,所以 main 方法中的 person 对象的 name 属性也会被修改。

示例 2:重新赋值引用

class Student {
    String name;
    public Student(String name) {
        this.name = name;
    }
}
public class ReassignReference {
    public static void main(String[] args) {
        Student student = new Student("Tom");
        System.out.println("调用方法前 student 的 name: " + student.name);
        reassignStudent(student);
        System.out.println("调用方法后 student 的 name: " + student.name);
    }
    public static void reassignStudent(Student s) {
        s = new Student("Jerry");
        System.out.println("方法内部重新赋值后 student 的 name: " + s.name);
    }
}
  • 在 main 方法中创建了一个 Student 对象,并将其引用赋值给变量 student。
  • 调用 reassignStudent 方法时,将 student 引用的副本传递给参数 s,s 和 student 最初指向同一个 Student 对象。
  • 在 reassignStudent 方法内部,将 s 重新赋值为一个新的 Student 对象,这只会改变 s 这个引用副本的指向,不会影响 main 方法中的 student 引用。所以 main 方法中的 student 对象的 name 属性仍然是 Tom。

方法重载

Java 允许在一个类中定义多个同名方法,但要求参数个数或类型不同,这就是方法重载。方法重载可以提高代码的可读性和可维护性,让开发者可以使用相同的方法名来完成不同的功能。

class OverloadDemo {
    // 无参数方法
    void display() {
        System.out.println("无参数方法");
    }
    // 一个整数参数的方法
    void display(int a) {
        System.out.println("传入的整数:" + a);
    }
    // 一个小数参数的方法
    void display(double b) {
        System.out.println("传入的小数:" + b);
    }
    // 两个整数参数的方法
    void display(int a, int b) {
        System.out.println("传入的两个整数:" + a + "和" + b);
    }
}
public class Main {
    public static void main(String[] args) {
        // 创建 OverloadDemo 对象
        OverloadDemo demo = new OverloadDemo();
        // 调用无参数方法
        demo.display();
        // 调用一个整数参数的方法
        demo.display(5);
        // 调用一个小数参数的方法
        demo.display(3.14);
        // 调用两个整数参数的方法
        demo.display(2, 3);
    }
}

在这个 OverloadDemo 类中,有四个 display 方法,它们的方法名相同,但参数个数和类型不同,因此构成了方法重载。在调用时,编译器会根据传递的参数的个数和类型来决定调用哪个方法。

构造方法

构造方法的概述

构造方法是一种特殊的方法,它的名称必须与类名相同,并且没有返回值类型(连 void 都不能有)。构造方法的主要作用是创建对象并初始化对象的成员变量。

class Product {
    // 成员变量,描述产品的名称和价格
    String name;
    double price;
    // 构造方法,用于初始化产品的名称和价格
    public Product(String name, double price) {
        this.name = name;
        this.price = price;
    }
}

在这个 Product 类中,定义了一个构造方法 Product,它接受两个参数 name 和 price,并将这两个参数的值赋给对象的成员变量。

默认构造方法

如果一个类没有显式地定义构造方法,Java 编译器会自动为该类提供一个默认构造方法。默认构造方法没有参数,方法体为空。

class Student {
    // 成员变量
    String name;
    int age;
    // 由于没有显式定义构造方法,Java 会自动提供默认构造方法
}
public class Main {
    public static void main(String[] args) {
        // 使用默认构造方法创建 Student 对象
        Student student = new Student();
    }
}

但是,如果一个类显式地定义了构造方法,Java 编译器就不会再提供默认构造方法。此时,如果需要使用无参构造方法,就必须显式地定义。

class Book {
    String title;
    String author;
    // 带参构造方法
    public Book(String title, String author) {
        this.title = title;
        this.author = author;
    }
    // 显式定义无参构造方法
    public Book() {}
}

构造方法的重载

和普通方法一样,构造方法也可以重载。也就是说,一个类可以有多个构造方法,它们的参数个数或类型不同。

class Rectangle {
    double width;
    double height;
    // 无参构造方法,初始化宽度和高度为默认值
    public Rectangle() {
        width = 1;
        height = 1;
    }
    // 带一个参数的构造方法,将宽度和高度初始化为相同的值
    public Rectangle(double side) {
        width = side;
        height = side;
    }
    // 带两个参数的构造方法,分别初始化宽度和高度
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
}

在这个 Rectangle 类中,有三个构造方法,分别用于不同的初始化场景。可以根据需要选择合适的构造方法来创建对象。

public class Main {
    public static void main(String[] args) {
        // 使用无参构造方法创建 Rectangle 对象
        Rectangle rect1 = new Rectangle();
        // 使用带一个参数的构造方法创建 Rectangle 对象
        Rectangle rect2 = new Rectangle(5);
        // 使用带两个参数的构造方法创建 Rectangle 对象
        Rectangle rect3 = new Rectangle(3, 4);
    }
}

访问方法和修改方法

通常把返回成员变量值的方法称为访问方法(getter 方法),修改成员变量值的方法称为修改方法(setter 方法)。这种设计模式遵循了封装的原则,通过访问方法和修改方法来控制对成员变量的访问和修改。

class Person {
    // 私有成员变量
    private String name;
    private int age;
    // 访问方法,用于获取 name 属性的值
    public String getName() {
        return name;
    }
    // 修改方法,用于设置 name 属性的值
    public void setName(String name) {
        this.name = name;
    }
    // 访问方法,用于获取 age 属性的值
    public int getAge() {
        return age;
    }
    // 修改方法,用于设置 age 属性的值
    public void setAge(int age) {
        this.age = age;
    }
}

在这个 Person 类中,getName 和 getAge 是访问方法,用于获取 name 和 age 属性的值;setName 和 setAge 是修改方法,用于设置 name 和 age 属性的值。

this 关键字

在 Java 中,this 是一个引用变量,它指向当前对象,也就是调用当前方法或构造函数的对象。this 关键字在 Java 里有多种重要的用途,下面详细介绍。

  1. 引用当前对象的成员变量
    当类的成员变量和方法的局部变量重名时,使用 this 关键字可以明确指定访问的是成员变量,而不是局部变量。

示例代码:

class Person {
    private String name;
    public Person(String name) {
        // 使用 this 关键字引用成员变量
        this.name = name;
    }
    public String getName() {
        return this.name;
    }
}
public class Main {
    public static void main(String[] args) {
        Person person = new Person("Alice");
        System.out.println(person.getName()); 
    }
}

在 Person 类的构造函数中,参数 name 和成员变量 name 重名。通过 this.name 可以明确表示要赋值的是成员变量 name。

  1. 调用当前对象的成员方法
    this 关键字可以用于在一个成员方法中调用该对象的其他成员方法。

示例代码:

class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
    public int calculateSum(int x, int y) {
        // 使用 this 调用 add 方法
        return this.add(x, y);
    }
}
public class Main {
    public static void main(String[] args) {
        Calculator calculator = new Calculator();
        int result = calculator.calculateSum(3, 5);
        System.out.println(result); 
    }
}

在 calculateSum 方法中,使用 this.add(x, y) 调用了同一个对象的 add 方法。

  1. 在构造函数中调用其他构造函数
    在一个类中,可以使用 this() 语句在一个构造函数中调用另一个构造函数,这样可以避免代码重复。

示例代码:

class Rectangle {
    private int width;
    private int height;
    // 第一个构造函数
    public Rectangle() {
        this(1, 1); 
    }
    // 第二个构造函数
    public Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }
    public int getArea() {
        return width * height;
    }
}
public class Main {
    public static void main(String[] args) {
        Rectangle rect1 = new Rectangle();
        System.out.println(rect1.getArea()); 
        Rectangle rect2 = new Rectangle(3, 4);
        System.out.println(rect2.getArea()); 
    }
}

在无参构造函数 Rectangle() 中,使用 this(1, 1) 调用了带参数的构造函数 Rectangle(int width, int height),这样可以复用带参数构造函数的代码。

  1. 返回当前对象
    this 关键字可以在方法中返回当前对象,这在实现链式调用时非常有用。

示例代码:

class StringBuilderExample {
    private StringBuilder sb = new StringBuilder();
    public StringBuilderExample append(String str) {
        sb.append(str);
        return this; 
    }
    public String toString() {
        return sb.toString();
    }
}
public class Main {
    public static void main(String[] args) {
        StringBuilderExample example = new StringBuilderExample();
        example.append("Hello ").append("World!");
        System.out.println(example); 
    }
}

在 append 方法中,使用 return this 返回当前对象,这样就可以实现链式调用,连续调用多个 append 方法。

类的静态成员

静态变量

静态变量是属于类的变量,而不是属于某个对象的变量。所有对象共享同一个静态变量的值。静态变量使用 static 关键字修饰,通常在类加载时就被初始化。

class Student {
    String name;
    // 静态变量,记录学生总数
    static int totalStudents = 0;
    public Student(String name) {
        this.name = name;
        totalStudents++;
    }
}
public class Main {
    public static void main(String[] args) {
        Student student1 = new Student("张三");
        Student student2 = new Student("李四");
        // 访问静态变量
        System.out.println("学生总数: " + Student.totalStudents);
    }
}

在这个例子中,totalStudents 是静态变量,每次创建新的 Student 对象时,totalStudents 的值就会加 1。可以通过类名直接访问静态变量。

静态方法

静态方法是属于类的方法,不需要创建对象就可以调用。静态方法只能访问静态成员变量和调用其他静态方法,不能直接访问实例成员变量和实例方法。

class MathUtils {
    // 静态方法,计算两个整数的乘积
    public static int multiply(int a, int b) {
        return a * b;
    }
}
public class Main {
    public static void main(String[] args) {
        // 直接通过类名调用静态方法
        int result = MathUtils.multiply(3, 4);
        System.out.println("乘积结果: " + result);
    }
}

在上述代码中,multiply 是静态方法,可以直接使用类名 MathUtils 调用,而不需要创建 MathUtils 对象。

静态代码块

静态代码块是使用 static 关键字修饰的代码块,它在类加载时执行,并且只执行一次。静态代码块通常用于对静态变量进行初始化。

class DatabaseConnection {
    static String url;
    static String username;
    static String password;
    // 静态代码块
    static {
        url = "jdbc:mysql://localhost:3306/mydb";
        username = "root";
        password = "123456";
        System.out.println("静态代码块执行,数据库连接信息初始化完成");
    }
}
public class Main {
    public static void main(String[] args) {
        // 类加载时静态代码块会自动执行
        System.out.println("数据库连接 URL: " + DatabaseConnection.url);
    }
}

在这个例子中,静态代码块在 DatabaseConnection 类加载时执行,对数据库连接信息进行初始化。

单例模式

  1. 单例模式的设计目的
    • 核心需求:某些类需要全局唯一实例(如数据库连接、配置管理)。
    • 实现方式:私有化构造方法,提供静态方法获取实例。
  2. 实现步骤
    • 将构造方法设为 private,禁止外部直接创建对象。
    • 在类内部创建静态实例。
    • 提供公共静态方法返回该实例。
class Singleton {
    // 静态实例
    private static Singleton instance;

    // 私有构造方法
    private Singleton() {
        System.out.println("单例对象已创建");
    }

    // 公共静态方法获取实例
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

// 使用单例
public Main{
	public static void main (String[] args){
		Singleton s1 = Singleton.getInstance();
		Singleton s2 = Singleton.getInstance();
		System.out.println(s1 == s2);  // 输出:true(说明是同一个对象)
	}
}
  1. 单例模式的线程安全问题(了解一下就好)
    • 问题:多线程环境下可能创建多个实例。
    • 解决方案:使用双重检查锁定或静态内部类。
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();
                }
            }
        }
        return instance;
    }
}

对象的生命周期

  1. 创建阶段
    对象的生命周期始于创建阶段。在 Java 中,使用 new 关键字调用构造方法来创建对象。构造方法不仅为对象分配内存空间,还负责初始化对象的成员变量。
class Person {
    String name;
    int age;
    // 构造方法
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println(name + "对象已创建");
    }
}
public class Main {
    public static void main(String[] args) {
        // 创建 Person 对象
        Person person = new Person("小明", 20);
    }
}

在上述代码中,new Person(“小明”, 20) 语句触发了 Person 类的构造方法,在堆内存中为 Person 对象分配了空间,并将 name 初始化为 “小明”,age 初始化为 20。

  1. 使用阶段
    一旦对象被创建,就可以使用它来调用其成员方法和访问其成员变量。对象的使用阶段是对象发挥其功能的阶段。
class Calculator {
    int add(int a, int b) {
        return a + b;
    }
}
public class Main {
    public static void main(String[] args) {
        // 创建 Calculator 对象
        Calculator calculator = new Calculator();
        // 使用对象调用方法
        int result = calculator.add(3, 5);
        System.out.println("计算结果: " + result);
    }
}

在这个例子中,创建了 Calculator 对象后,通过该对象调用 add 方法进行加法运算。

  1. 不可达阶段
    当一个对象不再被任何引用变量引用时,它就进入了不可达阶段。在 Java 中,对象的引用可以通过多种方式丢失,例如将引用变量赋值为 null,或者引用变量超出了其作用域。
public class Main {
    public static void main(String[] args) {
        // 创建 Person 对象
        Person person = new Person("小红", 22);
        // 将引用变量赋值为 null,对象进入不可达阶段
        person = null;
    }
}

在上述代码中,将 person 引用变量赋值为 null 后,之前创建的 Person 对象就不再被任何引用变量引用,从而进入不可达阶段。

  1. 垃圾回收阶段
    Java 的垃圾回收机制(Garbage Collection,简称 GC)会自动回收处于不可达阶段的对象所占用的内存空间。垃圾回收器会定期运行,检测不可达对象并释放其占用的内存。
public class Main {
    public static void main(String[] args) {
        for (int i = 0; i < 10000; i++) {
            // 创建大量对象
            new Person("临时对象", i);
        }
        // 建议垃圾回收器运行
        System.gc();
    }
}

在上述代码中,创建了大量的 Person 对象,这些对象在创建后没有被任何引用变量引用,成为不可达对象。System.gc() 方法建议垃圾回收器运行,但垃圾回收器是否立即执行回收操作是不确定的。

包和访问控制

包的概念

包是 Java 中组织类和接口的一种方式,它类似于文件系统中的文件夹。使用包可以避免类名冲突,并且便于管理和组织代码。
包的声明
在 Java 源文件的第一行使用 package 语句声明该文件所属的包。例如:

package com.example.myapp;
public class MyClass {
    // 类的内容
}

包的导入
如果要使用其他包中的类,需要使用 import 语句导入该类。例如:

import java.util.ArrayList;
public class Main {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
    }
}

访问控制修饰符

Java 提供了四种访问控制修饰符,用于控制类、成员变量和方法的访问权限。

  1. public
    public 表示公共的,使用 public 修饰的类、成员变量和方法可以被任何其他类访问。
package com.example.package1;
public class PublicClass {
    public int publicVar;
    public void publicMethod() {
        // 方法内容
    }
}
  1. private
    private 表示私有的,使用 private 修饰的成员变量和方法只能在同一个类中访问。
package com.example.package2;
class PrivateClass {
    private int privateVar;
    private void privateMethod() {
        // 方法内容
    }
}
  1. protected
    protected 表示受保护的,使用 protected 修饰的成员变量和方法可以在同一个包内的类中访问,也可以在不同包的子类中访问。
package com.example.package3;
class ProtectedClass {
    protected int protectedVar;
    protected void protectedMethod() {
        // 方法内容
    }
}
  1. 默认(无修饰符)
    如果没有使用任何访问控制修饰符,称为默认访问权限。默认访问权限的类、成员变量和方法只能在同一个包内的类中访问。
package com.example.package4;
class DefaultClass {
    int defaultVar;
    void defaultMethod() {
        // 方法内容
    }
}

总结

面向对象的基本概念

  1. 对象
    • 现实实体的抽象,包含状态(属性)和行为(方法)。
    • 示例:Car myCar = new Car();
    • 对象的模板,定义共有属性和方法。
    • 示例:public class Car { String brand; void start() { … } }
  2. 消息传递
    • 对象通过方法调用交互。
    • 示例:cal.add(3, 5);

面向对象的三大特征

  1. 封装
    • 隐藏细节通过公共接口访问数据。
    • 实现:private成员变量 + public的getter/setter。
    • 示例:BankAccount类的存取款方法。
  2. 继承
    • 子类继承父类属性和方法,使用extends。
    • 示例:Dog extends Animal,复用父类eat()方法。
  3. 多态
    • 同一方法的不同实现方式:
      • 重载:同方法名,参数不同(如MathUtils的add方法)。
      • 覆盖:子类重写父类方法(如Cat覆盖Animal的eat())。

类的定义与对象操作

  1. 类定义
    • 包含成员变量、构造方法、成员方法。
    • 示例:public class Car { private String brand; public Car() { … } }
  2. 对象操作
    • 创建:Car car = new Car();
    • 访问成员:car.start();
    • 引用赋值:多个变量指向同一对象(如Point类的引用示例)。

方法的使用

  1. 方法设计
    • 返回值类型(void表示无返回值)、参数(有参/无参)、方法体。
  2. 方法重载
    • 同名方法,参数不同(如OverloadDemo类的display方法)。
  3. 方法调用
    • 实例方法需通过对象调用,静态方法直接通过类名调用(如MathUtils.squareRoot())。

构造方法

  1. 特性
    • 名称与类名相同,无返回值,用于初始化对象。
  2. 默认构造方法
    • 无显式定义时由Java自动生成(无参、方法体为空)。
  3. 构造方法重载
    • 多个构造方法,参数不同(如Rectangle类的多个构造方法)。

封装与访问控制

  1. 访问方法(getter)与修改方法(setter)
    • 示例:Person类通过getName()和setName()操作私有变量。
  2. this关键字
    • 引用当前对象,解决变量名冲突,或在构造方法中调用其他构造方法。

静态成员与单例模式

  1. 静态成员
    • 静态变量(类共享,如Student.totalStudents)。
    • 静态方法(通过类名调用,如MathUtils.multiply())。
  2. 单例模式
    • 确保全局唯一实例,实现步骤:私有构造方法 + 静态实例 + 静态获取方法。

对象的生命周期

  1. 创建阶段:new关键字分配内存并初始化。
  2. 使用阶段:调用方法或访问属性。
  3. 不可达阶段:无引用指向对象(如person = null)。
  4. 垃圾回收:GC自动回收不可达对象。

包与访问控制修饰符

    • 组织类,避免命名冲突。package声明包,import导入其他包。
  1. 访问修饰符
    • public(全局可见)、private(仅本类)、protected(同包及子类)、默认(同包可见)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值