06_Java面向对象入门

Java面向对象入门:类与对象、构造方法与this关键字

文章目录

前言

Java是一门纯粹的面向对象编程语言 。面向对象编程(OOP,Object-Oriented Programming)不是简单的语法特性,而是一种编程思想------将现实世界的事物抽象为程序中的"对象",用对象之间的交互来解决问题。理解面向对象是成为Java开发者的必经之路。

很多从C语言转过来的程序员刚开始接触Java时最大的困惑就是"为什么一切都要封装到类里?",这是因为面向对象的核心优势在于代码复用维护性------当项目规模达到数万行时,面向过程的函数式写法会变得极难维护,而面向对象通过合理的类设计能让代码结构保持清晰。学完本文你将能够:独立定义类和创建对象、理解引用变量和堆内存的关系、正确使用构造方法初始化对象、用this解决变量同名问题。本文将从面向对象的核心概念开始,讲解类与对象的定义、对象的创建和使用、构造方法、this关键字以及对象的内存分析。

一、面向对象的基本概念

1.1 面向过程 vs 面向对象

在了解面向对象之前,我们先看看它的对比范式------面向过程

维度 面向过程 面向对象
核心思想 关注过程/步骤 关注对象/实体
程序单元 函数/方法 类/对象
数据管理 数据与操作分离 数据与操作封装在一起
典型语言 C语言 Java、C++
适用场景 简单任务、算法实现 大型项目、复杂业务

举例说明:用洗衣机洗衣服

  • 面向过程思维:打开洗衣机 → 放衣服 → 加洗衣液 → 选择模式 → 启动 → 等待 → 取出
  • 面向对象思维:洗衣机对象(含洗衣方法)、衣服对象、洗衣液对象。洗衣机.洗衣服(衣服, 洗衣液)

1.2 面向对象的三大特性(预习)

特性 说明 关键词
封装 隐藏内部实现,对外暴露接口 privategetter/setter
继承 子类继承父类的属性和方法 extends、父子关系
多态 同一行为的不同表现形式 方法重写、向上转型

本文重点讲类和对象的基础,下一章将详细展开三大特性的讲解。

二、类与对象

2.1 什么是类?什么是对象?

  • 类(Class) :是对一类事物的抽象描述,是创建对象的模板/蓝图
  • 对象(Object) :是类的具体实例,是一个真实存在的实体

类比理解: 是"汽车设计图纸",对象是根据图纸造出的"一辆真实的汽车"。

2.2 定义一个类

一个标准的Java类包含属性(成员变量)行为(成员方法)。在设计一个类时,我们需要思考:这个事物有哪些特征(属性)?它能做什么事情(行为)?这其实是面向对象分析(OOA)的核心------将现实世界的事物特征和功能映射到代码中。

举个例子:学生有姓名、年龄、学号等属性(用成员变量描述),有学习、考试等行为(用成员方法描述)。下面是一个标准的Java类定义:

java 复制代码
/**
 * 学生类------描述一个学生应该有哪些属性和行为
 */
public class Student {
    // ====== 属性(成员变量)======
    String name;        // 姓名
    int age;            // 年龄
    String studentId;   // 学号
    double score;       // 成绩
    
    // ====== 行为(成员方法)======
    public void study() {
        System.out.println(name + "正在努力学习...");
    }
    
    public void exam() {
        System.out.println(name + "正在参加考试...");
    }
    
    public void showInfo() {
        System.out.println("姓名:" + name);
        System.out.println("学号:" + studentId);
        System.out.println("年龄:" + age);
        System.out.println("成绩:" + score);
    }
}

2.3 创建与使用对象

定义好类只是画好了"图纸",接下来需要根据"图纸"创建真正的"汽车"------也就是实例化对象 。创建对象的核心关键字是new,它会做三件事:①在堆内存中分配空间;②调用构造方法初始化对象的属性;③返回对象的引用地址。

使用对象时需要注意区分.操作符的两个用途:访问属性用对象名.属性名,调用方法用对象名.方法名()。初学者容易忘记方法调用时的括号,导致编译错误。

java 复制代码
public class StudentTest {
    public static void main(String[] args) {
        // 创建对象:使用new关键字
        // 类名 对象名 = new 类名();
        Student stu1 = new Student();
        Student stu2 = new Student();
        
        // 访问属性并赋值
        stu1.name = "张三";
        stu1.age = 20;
        stu1.studentId = "2024001";
        stu1.score = 88.5;
        
        stu2.name = "李四";
        stu2.age = 21;
        stu2.studentId = "2024002";
        stu2.score = 92.0;
        
        // 调用方法
        stu1.showInfo();
        System.out.println("------------------------");
        stu2.showInfo();
        System.out.println("------------------------");
        stu1.study();
        stu2.exam();
    }
}

常见错误 :很多初学者会写出Student stu1;然后直接调用stu1.study(),此时stu1还没有指向任何对象(值为null),运行时会抛出NullPointerException(空指针异常) ------这是Java开发中最常见的异常之一。必须先new出来才能使用

2.4 对象的内存分析

了解对象在内存中的存储方式,对于理解Java运行机制非常重要。JVM内存主要分为:

  • 栈(Stack) :存储局部变量方法调用信息,方法执行完毕自动释放
  • 堆(Heap) :存储new出来的对象和数组,由GC(垃圾回收器)管理
  • 方法区(Method Area) :存储类的信息(.class文件)、静态变量、常量池
java 复制代码
// 内存分析示例
Student s1 = new Student();  // 在堆中创建一个Student对象,s1(在栈中)存其地址
s1.name = "王五";             // 通过地址找到堆中的对象,修改其name属性
Student s2 = s1;             // s2(在栈中)也存储同一个地址,s1和s2指向同一个对象
s2.name = "赵六";             // 修改的是同一个对象

System.out.println(s1.name);  // 输出"赵六",因为s1和s2指向同一对象

关键理解s1s2是引用变量,存在栈中,存储的是堆中对象的地址。new Student()创建的对象存在堆中。

面试常见问题 :"Java是值传递还是引用传递?"------这个问题的答案与本节的内存模型直接相关。Java总是值传递 ,但当传递的参数是引用类型时,传递的值是引用的副本 (即对象地址的拷贝)。这意味着在被调方法中修改对象的属性会影响到原对象(因为地址指向同一个堆中的对象),但修改引用本身(重新指向新对象)不会影响原引用。理解了Student s2 = s1的内存模型(两个栈变量指向同一个堆对象),这个问题就不需要死记硬背了。

三、成员变量与局部变量

成员变量局部变量看似都是"变量",但它们在内存中的位置、生命周期和作用范围完全不同。理解这个区别对后续学习继承、多态中的变量访问规则非常重要。特别是"同名变量屏蔽"的现象,是面试中经常被问到的细节。

核心记忆口诀:成员变量------类中方法外,有默认值,跟对象走;局部变量------方法内,无默认值,必须初始化

java 复制代码
public class VariableScope {
    // 成员变量:定义在类中,方法外
    // 有默认值,生命周期与对象同步
    String name;           // 默认值 null
    int age;               // 默认值 0
    
    public void method() {
        // 局部变量:定义在方法内
        // 没有默认值,必须手动初始化
        int x = 10;
        String localStr = "我是局部变量";
        
        // 局部变量可以和成员变量同名?
        String name = "局部name";  // 可以,但会屏蔽成员变量
        
        System.out.println("局部name:" + name);       // 局部name
        System.out.println("成员name:" + this.name);  // 成员name(null)
    }
    
    public void anotherMethod() {
        // 这里不能访问method()中的x和localStr
        System.out.println(name);  // 可以访问成员变量
    }
}
区别 成员变量 局部变量
定义位置 类中,方法外 方法内或代码块内
默认值 有(0、null等) 无,必须初始化
内存位置 堆内存 栈内存
生命周期 随对象存在 随方法调用结束
作用范围 整个类 所属的方法/代码块

四、构造方法

4.1 什么是构造方法?

构造方法(Constructor)是一种特殊的方法,在创建对象时自动调用 ,用于初始化对象的属性 。可以理解为对象的"出厂设置"------每当你用new创建一个对象,构造方法就会执行一次,确保对象一诞生就处在合理状态。

构造方法与普通方法最大的区别:①方法名必须等于类名且区分大小写;②绝对不能写返回值类型(连void都不能写);③只能用new关键字触发调用,不能像普通方法那样手动调用。

java 复制代码
public class Person {
    String name;
    int age;
    
    // 构造方法的定义格式:
    // public 类名(参数列表) { 初始化代码 }
    
    // 无参构造方法(默认构造方法)
    public Person() {
        System.out.println("无参构造方法被调用了!");
        name = "未知";
        age = 0;
    }
    
    // 有参构造方法
    public Person(String n, int a) {
        System.out.println("有参构造方法被调用了!");
        name = n;
        age = a;
    }
    
    public void show() {
        System.out.println("姓名:" + name + ",年龄:" + age);
    }
    
    public static void main(String[] args) {
        Person p1 = new Person();            // 调用无参构造
        p1.show();                           // 姓名:未知,年龄:0
        
        Person p2 = new Person("张三", 25);   // 调用有参构造
        p2.show();                           // 姓名:张三,年龄:25
    }
}

4.2 构造方法的注意事项

构造方法虽然是"特殊方法",但它仍然遵循方法重载的规则。以下是需要牢记的五个要点,特别是第3和第4条------这是新手最容易踩的坑:

  1. 构造方法名必须与类名完全相同
  2. 构造方法没有返回值 ,连void都不能写
  3. 如果没有定义 任何构造方法,Java会自动提供默认的无参构造方法
  4. 如果定义了任意一个构造方法 ,默认的无参构造方法就消失了,需要手动定义
  5. 构造方法支持重载

第4条的含义是:假设你写了一个带有参数String name的构造方法用于创建Person对象,但又想用new Person()创建无需指定姓名的对象,编译器会报错------因为默认无参构造在你定义了任何构造方法之后就被移除了。解决方案就是手动再写一个无参构造,这是很多框架(如Spring、MyBatis)要求实体类必须有无参构造的原因。

java 复制代码
public class ConstructorDemo {
    String name;
    
    // 如果只写了这个有参构造,默认无参构造就没了
    public ConstructorDemo(String name) {
        this.name = name;
    }
    
    public static void main(String[] args) {
        ConstructorDemo obj1 = new ConstructorDemo("测试");  // 正确
        // ConstructorDemo obj2 = new ConstructorDemo();     // 编译错误!无参构造没了
    }
}

实际开发建议 :在编写POJO(Plain Old Java Object)或实体类时,养成同时提供无参构造和全参构造的习惯。无参构造是框架反射创建对象的必要条件,全参构造方便测试和业务代码中快速构建对象。使用Lombok的@NoArgsConstructor@AllArgsConstructor可以自动生成,但理解手动写法是基础。

4.3 构造方法的重载

构造方法重载是一种常见的代码优化技巧------多个构造方法通过this()链式调用,可以避免重复编写初始化逻辑。这种设计模式的核心思想是"单一职责":最全参数的构造方法负责所有初始化工作,其他构造方法只需调用它并传入默认值。

java 复制代码
public class BankAccount {
    String accountNo;
    String owner;
    double balance;
    
    // 无参构造:创建默认账户
    public BankAccount() {
        this("00000000", "匿名用户", 0.0);
    }
    
    // 两个参数:开户必备
    public BankAccount(String accountNo, String owner) {
        this(accountNo, owner, 0.0);  // 通过this调用另一个构造方法
    }
    
    // 三个参数:完整开户
    public BankAccount(String accountNo, String owner, double balance) {
        this.accountNo = accountNo;
        this.owner = owner;
        this.balance = balance;
    }
    
    public void showAccount() {
        System.out.println("账号:" + accountNo);
        System.out.println("户主:" + owner);
        System.out.println("余额:" + String.format("%.2f", balance));
    }
    
    public static void main(String[] args) {
        BankAccount acc1 = new BankAccount();
        BankAccount acc2 = new BankAccount("6222-001", "张三");
        BankAccount acc3 = new BankAccount("6222-002", "李四", 10000.0);
        
        acc1.showAccount();
        System.out.println("---");
        acc2.showAccount();
        System.out.println("---");
        acc3.showAccount();
    }
}

五、this关键字

5.1 this是什么?

this是Java中的一个引用变量 ,它指向当前对象 ------谁调用这个方法,this就指向谁。理解this的本质是理解面向对象的关键一步:在Java中,同一个类的多个对象各自拥有独立的成员变量(存储在各自的堆内存区域),但它们共享同一份方法代码(存储在方法区)。那么方法执行时如何知道要操作哪个对象的数据?答案是:每个非静态方法都隐式携带了一个this参数,指向调用它的那个对象。

面试中常问:"this能出现在静态方法中吗?"------不能,因为静态方法属于类而非对象,调用时根本没有"当前对象"这个概念。

java 复制代码
public class ThisDemo {
    String name;
    
    // 使用this区分成员变量和局部变量
    public void setName(String name) {
        this.name = name;  // this.name是成员变量,右边的name是局部变量
    }
    
    public String getName() {
        return this.name;  // 这里的this可以省略
    }
    
    public void printAddress() {
        System.out.println("当前对象的地址:" + this);
    }
    
    public static void main(String[] args) {
        ThisDemo obj1 = new ThisDemo();
        ThisDemo obj2 = new ThisDemo();
        
        obj1.printAddress();  // this指向obj1
        obj2.printAddress();  // this指向obj2
        
        obj1.setName("对象1");
        obj2.setName("对象2");
        System.out.println(obj1.getName());  // 对象1
        System.out.println(obj2.getName());  // 对象2
    }
}

5.2 this的三种用法

java 复制代码
public class ThisUsage {
    private String name;
    private int age;
    
    // 用法1:this.成员变量  ← 区分同名的成员变量和局部变量
    public void setName(String name) {
        this.name = name;
    }
    
    // 用法2:this.成员方法  ← 调用本类的其他方法
    public void init() {
        this.setName("默认用户");  // this可省略
        this.setAge(18);           // 等价于直接调用setAge(18)
    }
    
    public void setAge(int age) {
        this.age = age;
    }
    
    // 用法3:this()构造方法  ← 在构造方法中调用另一个构造方法
    public ThisUsage() {
        this("未知", 0);  // 必须写在本构造方法的第一行
        System.out.println("无参构造完成");
    }
    
    public ThisUsage(String name) {
        this(name, 0);    // 调用两个参数的构造方法
    }
    
    public ThisUsage(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public void show() {
        System.out.println("姓名:" + name + ",年龄:" + age);
    }
    
    public static void main(String[] args) {
        ThisUsage u1 = new ThisUsage();           // 姓名:未知,年龄:0
        ThisUsage u2 = new ThisUsage("张三");      // 姓名:张三,年龄:0
        ThisUsage u3 = new ThisUsage("李四", 30);  // 姓名:李四,年龄:30
        
        u1.show();
        u2.show();
        u3.show();
    }
}

注意this()调用其他构造方法时,必须写在构造方法的第一行,且不能形成循环调用。

六、封装思想的初探

虽然封装是下一章的重点,但这里先感受一下。封装 的核心思想是:将数据(属性)和对数据的操作(方法)绑定到一起,同时限制外部对内部数据的直接访问。未封装的数据就像敞开大门的房子,任何人都可以进去修改你的东西。封装后的数据则设置了"门禁系统"------你需要通过getter/setter方法来访问,而这些方法里可以加入验证逻辑。

使用private修饰属性后,外部代码不能直接用对象.属性的方式访问。这带来的好处是:如果有一天你需要修改属性名或添加验证规则,只需要修改getter/setter内部实现,所有调用者不受影响------这就是"高内聚、低耦合"的体现。

java 复制代码
public class Product {
    // 使用private修饰属性,外部不能直接访问
    private String name;
    private double price;
    private int stock;
    
    // 提供public的getter和setter方法来访问属性
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        if (name != null && !name.trim().isEmpty()) {
            this.name = name;
        } else {
            System.out.println("商品名称不能为空!");
        }
    }
    
    public double getPrice() {
        return price;
    }
    
    public void setPrice(double price) {
        if (price > 0) {
            this.price = price;
        } else {
            System.out.println("价格必须大于0!");
        }
    }
    
    public int getStock() {
        return stock;
    }
    
    public void setStock(int stock) {
        if (stock >= 0) {
            this.stock = stock;
        } else {
            System.out.println("库存不能为负数!");
        }
    }
    
    public static void main(String[] args) {
        Product product = new Product();
        product.setName("笔记本电脑");
        product.setPrice(5999.00);
        product.setStock(100);
        
        // product.price = -100;  // 编译错误!price是private的
        
        product.setPrice(-100);   // 会被setter中的校验拦截
        System.out.println("商品:" + product.getName() 
                         + ",价格:" + product.getPrice() 
                         + ",库存:" + product.getStock());
    }
}

七、综合案例------简易通讯录系统

java 复制代码
public class Contact {
    private String name;
    private String phone;
    private String email;
    
    public Contact() {}
    
    public Contact(String name, String phone, String email) {
        this.name = name;
        this.phone = phone;
        this.email = email;
    }
    
    // getter和setter
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public String getPhone() { return phone; }
    public void setPhone(String phone) { this.phone = phone; }
    
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    
    public void display() {
        System.out.printf("姓名:%s | 电话:%s | 邮箱:%s%n", name, phone, email);
    }
    
    public static void main(String[] args) {
        Contact[] contacts = new Contact[3];
        
        contacts[0] = new Contact("张三", "138-0000-0001", "zhangsan@mail.com");
        contacts[1] = new Contact("李四", "138-0000-0002", "lisi@mail.com");
        contacts[2] = new Contact("王五", "138-0000-0003", "wangwu@mail.com");
        
        System.out.println("===== 通讯录 =====");
        for (Contact c : contacts) {
            c.display();
        }
        
        // 使用构造方法重载创建新联系人
        Contact newContact = new Contact();
        newContact.setName("赵六");
        newContact.setPhone("138-0000-0004");
        newContact.setEmail("zhaoliu@mail.com");
        
        System.out.println("\n===== 新增联系人 =====");
        newContact.display();
    }
}

总结

本文是Java面向对象编程的入门篇章,也是整个Java学习路线中最基础也最重要的内容。我们学习了以下核心内容:

  1. 面向对象概念:对比面向过程,理解OOP三大特性的雏形。面向过程关注"怎么做"(步骤),面向对象关注"谁来做"(对象),这个思维转变是编程能力的质变。
  2. 类与对象 :类是模板,对象是实例,用new关键字创建对象。new做了三件事:堆内存分配、构造方法调用、引用地址返回。理解这一点对后续内存分析至关重要。
  3. 成员变量与局部变量:定义位置、默认值、生命周期、作用域的区别。成员变量有默认值(int为0,引用类型为null),局部变量必须显式初始化;成员变量在堆中,局部变量在栈中。
  4. 构造方法 :在创建对象时自动调用,支持重载,用this()复用构造逻辑。没有返回值、方法名与类名相同------这两条规则是区分构造方法与普通方法的根本标志。
  5. this关键字:指向当前对象的引用,用于区分同名变量、调用成员方法和构造方法。静态方法中没有this,因为静态方法属于类而非对象。
  6. 封装初探 :用private隐藏属性,通过getter/setter控制访问。这是"高内聚低耦合"设计原则在代码层面的具体实践。

面向对象是一门需要大量实践才能内化的学问。本文的内容是基础中的基础,只有把这些概念彻底搞懂,才能在下一篇文章的封装、继承、多态学习中游刃有余。建议你将文中的每个代码示例都亲手敲一遍,体会每一行代码的含义。强烈建议亲手运行2.4节的内存分析代码,它是理解Java引用机制的最佳入口。

✅ 亮点总结

  • 面向过程 vs 面向对象对比:从把大象装冰箱的分步函数到把大象、冰箱、人抽象为对象,直观理解两种编程范式
  • 类与对象的精准比喻 :类是图纸/模板,对象是按照图纸造出来的实体,new关键字就是"按图纸制造"的动作
  • 成员变量与局部变量五大区别:定义位置、默认值、作用域、生命周期、内存位置,表格对比一目了然
  • 构造方法重载与this()链式调用 :无参构造、有参构造、this(参数)复用构造逻辑,避免代码重复
  • 封装初探------getter/setter模式 :用private保护数据,通过getter读取、setter写入实现可控的数据访问

适用场景

  • 将现实业务中的"用户""订单""商品"等概念直接映射为Java类,实现业务建模
  • 编写JavaBean/DTO/VO等数据传输对象,定义字段和对应的getter/setter方法
  • 使用Lombok的@Data注解自动生成构造方法/getter/setter,体会到手动编写再自动化的学习价值

扩展方向

  • 封装继承多态 :面向对象的三大核心特性深入,推荐阅读 07_Java封装继承多态
  • UML类图入门:学习用类图描述类与类之间的关系(关联、聚合、组合),提升系统设计能力
  • SOLID设计原则:了解单一职责、开闭原则、里氏替换等五大原则,为写出可维护代码打好理论基础
相关推荐
ZHW_AI课题组1 小时前
使用 Rectified Flow 和 Diffusion Transformer实现 MNIST 手写数字图像生成
人工智能·python·机器学习
Java_2017_csdn1 小时前
Java 策略模式(Strategy Pattern)-(二)
java·开发语言·策略模式
摇滚侠1 小时前
CSDN AI 数字营销测评 营销组件
java
Royzst1 小时前
一、IO 概述
开发语言·python
Java_2017_csdn1 小时前
Java 策略模式(Strategy Pattern)-(一)
java·开发语言·策略模式
思茂信息1 小时前
CST对一种用于中型无人机 433MHz 通信的宽带共形贴片天线
开发语言·单片机·嵌入式硬件·平面·无人机·cst
plainGeekDev1 小时前
XML Shape/Selector → Kotlin 动态创建
android·java·kotlin
Omics Pro1 小时前
P4医学4大支柱需绑定4大数字技术才可落地
人工智能·python·算法·机器学习·plotly
海鸥-w1 小时前
前端学习python第三天笔记整理(list 列表,str字符串,tuple元组,set集合,dect,函数,类型注解)
前端·python·学习