05-Java面向对象构造器与封装

Java面向对象进阶:构造器、封装与权限修饰符

前言

学完类、对象、属性、方法这些基础后,你已经能用Java描述现实中的事物了。但真正的面向对象编程,还差几块关键拼图。

本文将带你走进构造器、封装、权限修饰符、this关键字与JavaBean规范的世界。它们是写出"规范、安全、可维护"Java代码的基石。


一、构造器(Constructor)

1.1 什么是构造器

构造器是类的第三个成员(前两个是成员变量和成员方法),它是一种特殊的方法。

打个比方:类是生产"产品"的图纸,而构造器就是生产流水线上的"组装工人"。每次你想造一个对象,都得请这位工人来组装。

它的写法如下:

java 复制代码
public class Student {
    private String name;  // 实例变量:姓名
    private int score;    // 实例变量:成绩

    // 这就是构造器:名字和类名一样,没有返回值类型
    public Student(String n, int s) {
        name = n;
        score = s;
    }
}

1.2 构造器的特点

构造器有四个鲜明特征,和普通方法区别开:

  • 名称必须与类名完全一致,大小写也要一样。
  • 没有返回值类型,连 void 都不能写。一旦写了返回值类型,它就变成了普通方法,不再是构造器。
  • 修饰符只能是 public、protected、缺省(不写)、private 这四种之一。
  • 每个类都有构造器,绝不会出现"一个构造器都没有"的情况。

小贴士:构造器与普通方法最大的区别就是"无返回值类型"。新手常犯的错误是给构造器加上 void,结果它就不再是构造器了。

1.3 默认构造器

如果你一个构造器都没写,编译器会"贴心地"帮你加上一个无参构造器:

java 复制代码
public class Student {
    private String name;
    // 你没写任何构造器,编译器自动补上:
    // public Student() { }
}

但要注意一条铁律:只要你手动写了任何一个构造器(无论有参无参),编译器就不再帮你补默认无参构造了。

注意:这是非常常见的坑。很多框架(如Spring、MyBatis)创建对象时会调用无参构造,如果你只写了有参构造而忘了无参构造,程序会报错。建议:只要写了有参构造,就顺手补一个无参构造。

1.4 构造器的作用

构造器有两个核心使命:

  1. 与 new 一起创建对象------它是对象诞生的"接生婆"。
  2. 为实例变量初始化------在对象出生的瞬间就给它赋上合理的值。
java 复制代码
public class StudentTest {
    public static void main(String[] args) {
        // new Student("张三", 89) 做了两件事:
        // 1. 创建一个 Student 对象
        // 2. 调用构造器,把 name 设为"张三",score 设为 89
        Student s = new Student("张三", 89);
        System.out.println(s.getInfo());
    }
}

如果没有构造器初始化,实例变量只会是默认值(int 为 0,引用类型为 null),往往不是我们想要的。

1.5 构造器的重载

和方法一样,构造器也可以重载:名字相同,参数列表不同。这样就能用多种方式创建对象。

java 复制代码
public class Student {
    private String name;
    private int score;

    // 构造器1:只传姓名
    public Student(String n) {
        name = n;
    }

    // 构造器2:传姓名和成绩
    public Student(String n, int s) {
        name = n;
        score = s;
    }

    // 构造器3:只传成绩
    public Student(int s) {
        score = s;
    }

    public String getInfo() {
        return "姓名:" + name + ",成绩:" + score;
    }
}

调用时根据传入参数的个数和类型,Java会自动匹配合适的构造器,就像点餐时选不同的套餐。

1.6 快捷键生成构造器

在IDEA中,按 Alt + Insert(部分笔记本需要 Fn + Alt + Insert),选择 Constructor,勾选要初始化的属性,即可一键生成构造器。

这能避免手写时漏掉字段或拼错名字,是日常开发的高效利器。


二、封装

2.1 封装的概念与意义

面向对象有三大特征:封装、继承、多态。封装是其中最基础的一个。

生活中的封装就像快递包裹:纸箱把商品包起来,既保护商品不被损坏,又隐藏了里面具体装了什么,还方便搬运。

Java中的封装,就是把对象的内部细节隐藏起来,只对外暴露必要的访问方式。它带来三大好处:

  • 安全:保护内部成员变量,外部不能随意修改,只能通过规定的方法访问。
  • 简便:外部使用者不需要关心内部如何实现,只要会调方法即可。
  • 可维护:内部实现变了,只要对外接口不变,外部代码无需改动。

2.2 四种权限修饰符

Java通过权限修饰符来实现封装。共有四种,可见性范围由小到大:

修饰符 本类内部 本包的其他类 其他包的子类 其他包的非子类
private 可以 不可以 不可以 不可以
缺省(不写) 可以 可以 不可以 不可以
protected 可以 可以 可以 不可以
public 可以 可以 可以 可以

记忆口诀:从 private 到 public,可见范围像洋葱一样一层层扩大。private 最严(只对自己开放),public 最宽(对全世界开放)。

小贴士:缺省(default)不是关键字,而是"什么都不写"时的默认状态。它允许同包访问,但不允许跨包。

2.3 修饰类与修饰成员的规则

权限修饰符既可以修饰类的成员(属性、方法、构造器),也可以修饰类本身,但规则略有不同:

  • 修饰类:只能用 public 或缺省。不能用 private、protected 修饰外部类(内部类除外)。
  • 修饰成员:四种修饰符都可以用。

换句话说,一个类对外的"可见性"只有两档:要么全包可见(缺省),要么全工程可见(public)。而类内部的成员,则有更细粒度的控制。

2.4 属性私有化与 getter/setter

封装最典型的应用就是属性私有化:把成员变量声明为 private,然后提供公开的 getter/setter 方法来访问它。

为什么这样做?假设有个年龄属性,如果直接 public 暴露,谁都能赋值为 -100 或 9999。但通过 setter,我们就能加校验逻辑:

java 复制代码
public class Employee {
    private String name;     // 属性私有化,外部无法直接访问
    private int age;

    // getter:供外部读取属性值
    // 命名规范:get + 属性名(首字母大写)
    public int getAge() {
        return age;
    }

    // setter:供外部修改属性值
    // 命名规范:set + 属性名(首字母大写)
    public void setAge(int age) {
        // 在这里可以做合法性校验,保证数据安全
        if (age < 0 || age > 150) {
            System.out.println("年龄不合法,已设为默认值0");
            this.age = 0;
        } else {
            this.age = age;
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

getter/setter 的规范要点

  • getter 以 get 开头,属性名首字母大写;boolean 类型用 is 代替 get (如 isMarry())。
  • getter 有返回值、无参数;setter 无返回值(void)、有参数。
  • 静态变量的 getter/setter 也都是静态的,实例变量的 getter/setter 是非静态的。

注意:boolean 类型属性的 getter 用 is 开头更符合英语习惯,比如 isMarry()isEmpty()

生成 getter/setter 同样用 Alt + Insert,选择 Getter / Setter / Getter and Setter 即可批量生成。


三、this关键字详解

3.1 this是什么

this 是一个代词,代表当前正在调用这个方法的对象。谁调用了这个方法,this 就指向谁。

它可以用在:构造器、实例方法、非静态代码块。

不能出现在:静态方法、静态代码块。

原因很简单:静态方法属于类本身,不属于任何对象,自然没有"当前对象"一说。

3.2 this.成员变量------区分重名

当方法的局部变量(包括参数)与实例变量同名时,Java遵循"就近原则",会优先使用局部变量。这时如果想让编译器认出实例变量,就要加 this.

java 复制代码
public class Employee {
    private String name;
    private double salary;

    // 参数 name 和实例变量 name 重名
    // 不加 this. 的话,name = name 相当于自己赋值给自己,实例变量没变
    public Employee(String name, double salary) {
        this.name = name;      // this.name 表示实例变量
        this.salary = salary;  // this.salary 表示实例变量
    }

    public void setName(String name) {
        this.name = name;  // this 代表调用 setName 的那个对象
    }
}

注意:如果局部变量和实例变量不重名,可以省略 this.,编译器会自动识别为实例变量。但养成加 this. 的习惯,能让代码可读性更强。

3.3 this()------调用本类其他构造器

this 还有一种用法:this(实参列表),用于在一个构造器中调用本类的另一个构造器,实现代码复用。

java 复制代码
public class Employee {
    private String name;
    private double salary;
    private char gender;

    // 无参构造
    public Employee() {
        System.out.println("一个员工对象被创建了");
    }

    // 部分参数构造:复用无参构造的代码
    public Employee(String name, double salary) {
        this();            // 调用本类的无参构造,复用其中的代码
        this.name = name;
        this.salary = salary;
    }

    // 全参构造:复用上面那个部分参数构造
    public Employee(String name, double salary, char gender) {
        this(name, salary);  // 调用本类的另一个构造器
        this.gender = gender;
    }
}

注意:this() 调用必须放在构造器的第一行。一个构造器中只能调用一次其他构造器。这个位置限制是为了保证对象初始化的顺序清晰。


四、对象数组

4.1 什么是对象数组

数组不仅能存基本类型(int、double),也能存对象。所谓对象数组,就是元素类型为某个类的数组

关键理解:数组中存的并不是对象本身,而是对象的引用(首地址)。就像一排信箱,每个信箱里装的不是房子,而是房子的门牌号。

java 复制代码
// 声明一个矩形对象数组
Rectangle[] arr = new Rectangle[3];

此时数组有3个格子,但每个格子的默认值是 null------还没装任何对象进去。

4.2 对象数组的使用

下面是一个完整示例:创建3个矩形对象存入数组,遍历输出,再按面积排序后输出。

java 复制代码
// 矩形类:属性私有、有构造器、getter/setter、业务方法
public class Rectangle {
    private double length;
    private double width;

    public Rectangle() {
    }

    public Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }

    public double getLength() {
        return length;
    }

    public void setLength(double length) {
        this.length = length;
    }

    public double getWidth() {
        return width;
    }

    public void setWidth(double width) {
        this.width = width;
    }

    // 面积
    public double area() {
        return length * width;
    }

    // 周长
    public double perimeter() {
        return 2 * (length + width);
    }

    // 综合信息
    public String getInfo() {
        return "长:" + length + ",宽:" + width + ",面积:" + area() + ",周长:" + perimeter();
    }
}

测试类:

java 复制代码
public class RectangleTest {
    public static void main(String[] args) {
        // 动态初始化:创建长度为3的矩形数组,元素默认值是 null
        Rectangle[] arr = new Rectangle[3];

        // 给数组元素赋值:每个位置都要 new 一个对象
        arr[0] = new Rectangle(5, 2);
        arr[1] = new Rectangle(6, 3);
        arr[2] = new Rectangle(5, 1);

        // 遍历输出(普通for循环)
        for (int i = 0; i < arr.length; i++) {
            // arr[i] 是一个对象引用,可以调用它的属性和方法
            System.out.println(arr[i].getInfo());
        }

        // 按面积从小到大排序(冒泡排序)
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                // 对象不能直接比较大小,要用某个属性或方法返回值
                if (arr[j].area() > arr[j + 1].area()) {
                    // 交换的是数组中的引用(门牌号),不是对象本身
                    Rectangle temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }

        // 排序后遍历输出(增强for循环)
        for (Rectangle r : arr) {
            System.out.println(r.getInfo());
        }
    }
}

注意:对象数组中,数组元素本身是引用 ,交换元素交换的是引用(门牌号),对象本身在堆内存中并没有移动。对象之间不能直接用 > 比较,要用具体属性(如 area())来比较。


五、标准JavaBean规范

5.1 JavaBean是什么

JavaBean 是Java中一种标准的类编写规范。符合规范的类被称为 JavaBean,它们能被各种框架、工具识别和使用。

5.2 编写规范

一个标准的JavaBean要满足以下几点:

  • 属性私有化:成员变量都用 private 修饰。
  • 提供无参构造:方便框架反射创建对象。
  • 提供有参构造(可选):方便快速初始化。
  • 提供 getter/setter:供外部读写属性。

下面是一个完整的标准JavaBean示例:

java 复制代码
public class Customer {
    // 1. 属性私有化
    private String name;
    private char gender;
    private int age;
    private String tel;
    private String email;

    // 2. 无参构造
    public Customer() {
    }

    // 3. 有参构造(全参)
    public Customer(String name, char gender, int age, String tel, String email) {
        this.name = name;
        this.gender = gender;
        this.age = age;
        this.tel = tel;
        this.email = email;
    }

    // 4. getter/setter 方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getTel() {
        return tel;
    }

    public void setTel(String tel) {
        this.tel = tel;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    // 通常还会提供一个 getInfo 方法,方便打印对象信息
    public String getInfo() {
        return "姓名:" + name + ",性别:" + gender + ",年龄:" + age
                + ",电话:" + tel + ",邮箱:" + email;
    }
}

5.3 IDE快捷键大全

IDEA为JavaBean的编写提供了强大的快捷键支持,熟练使用能极大提升效率:

  • Alt + Insert:弹出"生成"菜单,包含以下所有选项。
    • Constructor:生成构造器(可选无参、有参)。
    • Getter:生成getter方法。
    • Setter:生成setter方法。
    • Getter and Setter:同时生成getter和setter。
    • toString():生成toString方法,方便打印对象。
    • equals() and hashCode():生成equals和hashCode方法。
  • Ctrl + P:查看构造器/方法的参数列表(调用时按)。
  • 数组名.fori:快速生成普通for循环。
  • iter:快速生成增强for循环。

小贴士:写完属性后,依次用 Alt+Insert 生成 Constructor(全选属性生成有参构造,再生成一个无参)、Getter and Setter、toString,一个标准JavaBean几秒就能搭好。


六、小结

本文围绕"写出规范的Java类"这一目标,串联了几个核心知识点:

  1. 构造器:与类同名、无返回值类型,负责创建对象和初始化实例变量。不写时编译器补默认无参构造,写了就不再补。可重载,可用 Alt+Insert 快速生成。

  2. 封装:隐藏内部细节,对外暴露必要接口。通过权限修饰符实现,带来安全、简便、可维护三大好处。

  3. 权限修饰符:private(只对本类)、缺省(本包)、protected(本包+其他包子类)、public(全部)。类只能用 public 或缺省,成员四种皆可。

  4. 属性私有化:属性用 private,配 getter/setter 访问,可在 setter 中加校验。boolean 的 getter 用 is 开头。

  5. this关键字:代表当前对象。用于区分重名变量(this.成员变量)、调用本类构造器(this() 必须在首行)。静态方法中不能用 this。

  6. 对象数组:存储对象引用的数组。元素默认 null,需 new 对象赋值。通过 arri.方法() 调用。比较对象要用具体属性。

  7. JavaBean规范:属性私有 + 无参构造 + 有参构造 + getter/setter。配合 Alt+Insert 快捷键,几秒钟搭好一个标准类。

掌握这些,你就具备了写出"工业级"Java类的能力。下一篇我们将进入继承与多态,看看如何让类之间产生血缘关系,复用更多代码。

相关推荐
仙人球部落1 小时前
-python-LangGraph框架(3-31-LangGraph 「合并式状态管理」的原理与实践)
开发语言·javascript·python
2401_894915531 小时前
Geo搜索优化排名源码部署搭建全流程详解
android·开发语言·flask·php·精选
aaPIXa6221 小时前
C++采样引导优化SPGO——比PGO更智能的编译器决策新方案
java·c++·人工智能
蓝银草同学1 小时前
Stream 实战:博客列表排序、过滤与分页(AI 辅助学习 Java 8)
java·前端·后端
chouchuang2 小时前
day-025-面向对象-上
开发语言·python
某不知名網友2 小时前
C/C++动态内存管理,智能指针及RAlI思想。
java·c语言·c++
sunfdf2 小时前
Next.js 新手从零部署到首跑实战指南
开发语言·javascript·ecmascript
hold?fish:palm3 小时前
从源码到可执行文件:C++程序的编译过程
开发语言·c++
随性而行3603 小时前
微信API接口与AI自动化:开发者的实现思路
运维·服务器·开发语言·人工智能·微信·自动化