【Java类和对象(1)】

Java类和对象

前言

Java是一门面向对象的编程语言,要学习Java就离不开学习面向对象,本篇文章将简单的介绍一下Java的类和对象,为后续的面向对象的学习做一个铺垫。

什么是面向对象

编程语言分为两种,一种是面向过程的编程语言(例如C语言),另一种是面向对象的编程语言。而Java是一种纯面向对象的编程语言。
我们先简单的说一下面向过程吧,在我们用面向过程的编程语言来写程序的时候,我们一直都在做着这么一件事情:将我们要做的事情或者实现的功能拆分为一个又一个的流程,然后编制一个又一个的函数实现这些流程,最后再将这些函数集成起来,我们就完成了我们要做的事情。就拿洗衣服这件事情来举例子吧,废话不多说,我们来看图:

在这里插入图片描述
我们可以看到这个流程并不是很简单,而且每一个流程的执行将会直接影响到下一个流程的执行,例如:如果手搓的不仔细就洗不干净衣服,后续的流程也就不再有意义了。并且将这些个流程集成也是一项复杂的工作,下面我们来看一下面向对象:
在这里插入图片描述
我们直接将要洗的衣服以及洗衣液放到洗衣机里,然后启动洗衣机即可。至于洗衣机怎么洗衣服的其实是通过对象之间的交互去完成的,我们其实不用过于的关心,可以看到相比较于面向过程,复杂性降低了很多。在笔者的个人理解中,面向对象的本质其实就是一个“叫人干活”,当我们要做一件事情的时候,我们可以先去找会做这个事情的“人”,然后把生产要素提供给这个“人”,就可以得到我们想要的结果。当然,如果我们找不到可以干活的“人”,我们还是要自己动手,丰衣足食的。
在这里插入图片描述
注意:面向过程和面向对象并不是一门编程语言,而是解决问题的方法,两者并无好坏之分,都有其专门应用的场景。

什么是类

类是对于自然界存在的事物的一种抽象,这种抽象可以分为两种:状态和行为。(第一次接触面向对象的读者可能没有看懂,没有关系,后续笔者会举一个具体的例子)类是用来描述一个实体(对象)的,主要描述这个实体有些什么特征(即类的属性)和行为(即类的方法),描述完了之后计算机就可以识别了。
上面的这段话有点抽象,下面我们来看一段示例代码,可以帮助读者理解上面这些抽象的概念。

/**一个用来解释类的定义的示例
 *
 * @author dengjiangming
 * @date 2025-06-02
 * @version 1.0.0
 */
public class Student {
    //属性用来描述实体的特征
    //姓名
    String name;
    //年龄
    int age;
    //班级
    String className;
    //平均绩点(成绩)
    double AverageGradePoint;
    //身高
    double high;
    //体重
    double weigh;
    //方法用来表示行为
    public void goToSchool() {
        System.out.println("去上学");
    }

    public void doHomeWork() {
        System.out.println("做作业");
    }

    public void play() {
        System.out.println("玩耍放松一下");
    }

}

在解释这一段示例代码之前,我们先思考一个问题:如果我们遇到了一个自己并不熟悉的学生,我们会怎么识别或者描述这个学生呢?我们可以说这个学生叫什么名字、大概多少岁了、身高多少、体重多少、在哪个学校上学、成绩怎么样,如果这个学生是硕博群体的话,我们还可以通过这个学生的导师是谁,在哪个课题组做研究进行识别。这些都是学生的特征,我们还可以通过这个学生的状态来描述这个学生,比如:上学时的课堂表现、作业完成情况、课下玩耍的时候的表现,这些就是学生的状态。
好了,想过了这个问题之后,我们再来解释上面这一段示例代码,public 是访问控制修饰符,这个后面会详细介绍,这里先跳过。class关键字表明这段代码是一个类的定义,类名为Student遵循大驼峰命名的规范,这个类的属性有字符串类型的name,表示学生的姓名、整型的age,用来表示学生的年龄、字符串类型的className,用来表示学生的班级等等,后续的属性作用也是一样的:都是用来表示学生的特征的。下面再来看方法,goToSchool这个方法表明学生有去上学这个行为,花括号之内的方法体简单实现了这个方法,其他的方法也是如此,这里就不再赘述了。
就像现实生活中我们知道了学生有以上的这些特征和状态之后就可以识别一个学生了一样,Java中的Java虚拟机也有一套这样的流程,这个过程就是类加载。
在刚刚开始学习Java的时候我们就知道,Java的源代码是没法直接在计算机上去运行的,需要经过JVM将.java文件编译成为.class文件才可以在计算机上运行,而当代码涉及到用户自定义类的时候,还会有一个类加载的过程:
在这里插入图片描述
我们现在需要了解的就是,类加载可以让JVM识别用户自定义的类,因为当用户新定义一个类的时候,相当于创建了一种新的数据类型,必须有类加载这个过程让JVM可以识别这个新的数据类型,否则的话程序就不能正常运行,上述的图画的并不够严谨,关于类加载的详细内容,我们会在介绍JVM时详细介绍。

定义在类体之内,方法体之外的变量叫做类的成员属性或者成员变量,定义在方法体之内的变量叫做局部变量,在使用时要千万注意一点:成员变量如果在定义的时候没有进行初始化,编译器会帮助我们赋一个默认值。而局部变量在定义时如果没有初始化编译器是不会进行默认初始化的,直接就报错了。

在这里插入图片描述
在写代码的时候,我们还需要注意以下几个点:

  1. 一般一个文件中只定义一个类
  2. public 修饰的类必须与文件名完全相同
  3. 不要轻易修改public关键字修饰的类,如果一定要修改,一定要通过开发工具修改

在这里插入图片描述

什么是对象

在了解了类了之后,再看对象会相对简单一些。一言以蔽之,对象就是一个类的实例。举一个具体的例子:鹿是一个类,而一头梅花鹿就是一个对象。总结以下,对象是类实例化的产物,而类是同一类对象经过抽象之后的结果。

类的实例化

在有了类之后,我们要如何使用它呢?这个问题可以类比为,我们有了做月饼的模子,我们要如何做出月饼呢?答案很简单,根据这个模子把月饼做出来就可以了。而要使用类,我们也只需要根据类实例化出一个对象就可以了。

/**
 * 测试类
 *
 * @author dengjiangming
 * @version 1.0.0
 * @date 2025-06-02
 */
public class Test {

    public static void main(String[] args) {
        //用new关键字实例化对象
        Student stu = new Student();
        //打印类的成员
        System.out.println(stu.name);
        System.out.println(stu.age);
        System.out.println(stu.className);
        System.out.println(stu.AverageGradePoint);
        System.out.println(stu.high);
        System.out.println(stu.weigh);
    }
}
new关键字

定义了类,相当于在Java中创建了一种新的数据类型,和int和double一样,上述的Student也是一样的,定义了它之后就可以创建对象了。Java中创建对象的过程又叫类的实例化,Java中利用new关键字配合类名来实例化对象。对于上述的示例代码,我们打开Debug视图,可以观察到如下的现象:
在这里插入图片描述
我们可以验证,在经过了第一行代码之后,Java确实在内存中开辟出一块空间来存放stu对象,并且所有的成员变量都已经完成了默认初始化,程序的内存图和运行结果如下:
在这里插入图片描述
在这里插入图片描述

通过点号(.)访问对象的成员

我们创建了对象之后,要怎么访问和修改对象的成员呢?我们可以通过对象名.成员名进行访问。说起来有点抽象,下面来看示例代码:


/**
 * 测试类
 *  * @author dengjiangming
 * @version 1.0.0
 * @date 2025-06-02
 */
public class Test {

    public static void main(String[] args) {
        //用new关键字实例化对象
        Student stu = new Student();
        //通过.访问对象的成员变量(属性)
        stu.name = "马车";
        stu.age = 18;
        stu.className = "Java";
        stu.AverageGradePoint = 3.6;
        stu.high = 1.82;
        stu.weigh = 80;
        //通过.访问对象的成员方法
        stu.goToSchool();
        stu.doHomeWork();
        stu.play();
        //打印类的成员
        System.out.println(stu.name);
        System.out.println(stu.age);
        System.out.println(stu.className);
        System.out.println(stu.AverageGradePoint);
        System.out.println(stu.high);
        System.out.println(stu.weigh);
    }
}

代码的运行结果如下:
在这里插入图片描述
注意事项

  • 创建对象的是new关键字(而不是构造方法,至于什么是构造方法,在后文会进行详细介绍)
  • 使用点号来访问对象中的属性和方法
  • 同一个类可以创建多个实例
小结:类和对象的再次理解
  1. 类只是一个模型一样的东西,用来对一个实体进行描述,限定了类有哪些成员。在Java中,类的作用是描述一种新的实体,并且让JVM可以识别这种新的实体
  2. 类是一种自定义的类型,可以用来定义变量
  3. 一个类可以实例化出多个对象,实例化出的对象,占用实际的物理空间,存储类的成员变量
  4. 打一个比方,类实例化出对象就像现实中使用建筑类图纸造房子,类就像是设计图,只设计出需要什么东西,但是没有实体的建筑存在,同样的类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间。

在这里插入图片描述

this关键字

下面我们来看一段代码,有兴趣的读者可以预测一下代码的输出结果


/**
 * 演示this关键字的示例类
 *
 * @author dengjiangming
 * @version 1.0.0
 * @date    2025-06-04
 */
public class Date {
    public int year;

    public int month;

    public int day;

    public void setDate(int year,int month,int day) {
        year = year;
        month = month;
        day = day;
    }

    public void printDate() {
        System.out.println(year + "/" + month + "/" + day);
    }

    public static void main(String[] args) {
        Date date1 = new Date();
        date1.setDate(2025,6,1);
        date1.printDate();

        Date date2 = new Date();
        date2.setDate(2025,6,2);
        date2.printDate();

        Date date3 = new Date();
        date3.setDate(2025,6,3);
        date3.printDate();
    }

}

我想细心的读者已经发现问题了,我们现在假装不知道发生了什么,下面来看代码的运行结果:
在这里插入图片描述
我们首先来分析以下上面的示例代码Date类,Date类中有三个成员属性,分别是年:year、月:month、日:day,以及两个成员方法,分别是设置日期的setDate方法和打印日期的printDate方法,在setDate方法中,我们想要给类的成员变量进行赋值操作,从结果来看,这个操作已经失败了。
原因也并不复杂,在Java中局部变量是可以屏蔽成员变量的,变量的访问遵循就近原则,如果局部变量和成员变量的变量名相同的话,优先访问局部变量。所以在setDate方法中,我们是将局部变量赋值给了局部变量自己,并没有访问到成员变量。这是第一个问题,还有第二个问题:在main方法中,我们实例化了3个对象,当这些对象调用方法的时候,Java是怎么知道是哪一个对象调用了这个方法呢?
这个时候就要提到我们的this关键字了,我们先将上面的Date类改进一下,让它可以正常的设置和打印日期,下面来看修改之后的代码:


/**
 * 演示this关键字的示例类,这是修改之后可以正常打印日期的
 *
 * @author dengjiangming
 * @version 1.0.0
 * @date    2025-06-04
 */
public class Date {
    public int year;

    public int month;

    public int day;

    public void setDate(int year,int month,int day) {
       this.year = year;
       this.month = month;
       this.day = day;
    }

    public void printDate() {
        System.out.println(year + "/" + month + "/" + day);
    }

    public static void main(String[] args) {
        Date date1 = new Date();
        date1.setDate(2025,6,1);
        date1.printDate();

        Date date2 = new Date();
        date2.setDate(2025,6,2);
        date2.printDate();

        Date date3 = new Date();
        date3.setDate(2025,6,3);
        date3.printDate();
    }

}

下面来看运行结果:
在这里插入图片描述
可以看到,我们的Date类已经可以正常的打印日期了。而我们只是在setDate方法里给要赋值的成员变量前面加上了一个this.
这个时候我们就不禁要问了,this到底是什么?this怎么用,有哪些用途,可以解决哪些问题?到底为什么会有this这个关键字呢?

this到底是什么

我们直接用Debug视图来看一下main方法执行的过程,只用看前两行就可以了。
在程序的第28行,也就是main方法的第一行打一个断点,然后启动Debug。我们可以看到如下画面:
在这里插入图片描述
请记住此时date1对象的内存地址的值,接下来我们在键盘上按下F8,然后按下F7,会看到如下画面:
在这里插入图片描述
请注意此时this的值,经过这个很小的一个实验,我们可以得出:this代表了当前对象的引用指向的那一片内存空间的地址值

this的用途

this的用途如下表:

语法格式用途
this.成员变量名引用成员变量
this. 成员方法名引用成员方法
this(参数列表)在构造方法中调用其他的构造方法

下面来看一段示例代码:

/**
 * 演示this的测试类
 *
 * @author dengjiangming
 * @date 2025-06-04
 * @version 1.0.0
 */
public class ThisDemo {

    public String name;

    public int year;

    public ThisDemo() {

    }

    public void method() {
        int year = 2;
        System.out.println(year);
        this.year = 10;
        System.out.println(this.year);
    }

    public ThisDemo(String name, int year) {
        //调用无参的构造方法
        this();
        this.name = name;
        this.year = year;
    }

    public static void main(String[] args) {
        ThisDemo thisDemo = new ThisDemo("dengjiangming",2025);
        thisDemo.method();
    }
}

下面来看演示结果:
在这里插入图片描述
我们可以看到在类的成员方法method中,定义了一个和成员变量同名的局部变量,初始赋值为2,我们直接打印year,Java遵循就近原则,会屏蔽成员变量year,直接去打印局部变量。当我们用this.year = 10给year赋值的时候,关键字this代表当前对象的引用,即告诉JVM,我们要赋值的是这个对象的成员变量year,然后进行打印。为了验证this.year和year确实是两个不同的变量,我们在Debug视图下进行查看。

在这里插入图片描述
可以看到year和this.year确实是两个不同的变量。

this引用的特性
  1. this的类型:对应类类型的引用,即哪个对象调用就是哪个对象的引用
  2. this只能在成员方法中使用,在static方法中无法使用this
  3. 在成员方法中,this只能引用当前对象,不能引用其他对象
  4. this是“成员方法”第一个隐藏的参数,编译器会自动传递,在成员方法执行时,编译器负责将调用成员方法对象的引用传递给该成员方法,this负责进行接收

当然了,this还可以在构造方法中表示对其他构造方法的调用,接下来我们说明一下什么是构造方法。

构造方法

下面我们先来看一个类:

/**
 * 用于演示构造方法的示例类
 * 
 * @author dengjiangming 
 * @version 1.0.0
 * @date 2025-06-04
 */
public class Constructor {

    public String name;

    public int age;

    public String className;

    public String favoriteBook;

    public String favoriteSport;

    @Override
    public String toString() {
        return "Constructor{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", className='" + className + '\'' +
                ", favoriteBook='" + favoriteBook + '\'' +
                ", favoriteSport='" + favoriteSport + '\'' +
                '}';
    }

    public static void main(String[] args) {
        Constructor constructor = new Constructor();
        constructor.name = "dengjiangming";
        constructor.age = 22;
        constructor.className = "Java";
        constructor.favoriteBook = "哈利波特";
        constructor.favoriteSport = "跑步";

        System.out.println(constructor);
    }
}

在main方法中我们可以看到,为了给constructor对象的成员属性赋值,我们写了五行代码。试想一下,如果这个类有十个属性呢?那我们岂不是要写10行代码来进行属性的赋值?有没有简单一些的方法呢?
当然是有的,构造方法就是为了解决给对象的成员属性初始化的问题,我们稍微改进一下上面的示例代码,大家可以看一看。


/**
 * 用于演示构造方法的示例类,利用构造方法优化过后的
 *
 * @author dengjiangming
 * @version 1.0.0
 * @date 2025-06-04
 */
public class Constructor {

    public String name;

    public int age;

    public String className;

    public String favoriteBook;

    public String favoriteSport;

    @Override
    public String toString() {
        return "Constructor{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", className='" + className + '\'' +
                ", favoriteBook='" + favoriteBook + '\'' +
                ", favoriteSport='" + favoriteSport + '\'' +
                '}';
    }

    public Constructor() {
    }

    public Constructor(String name, int age, String className, String favoriteBook, String favoriteSport) {
        this.name = name;
        this.age = age;
        this.className = className;
        this.favoriteBook = favoriteBook;
        this.favoriteSport = favoriteSport;
    }

    public static void main(String[] args) {
        Constructor constructor = new Constructor("dengjiangming",22,"Java","哈里波特","跑步");
        System.out.println(constructor);
    }
}

可以看到我们定义了一个特殊的方法,这个方法的方法名和类名完全相同,并且是没有返回值类型,方法体中也没有返回值,这个方法就是类的构造方法,是用来对类的成员属性进行初始化用的,在实例化对象的时候会执行。会给对象成员属性赋值。

使用this关键字在一个构造方法种调用其他的构造方法

一个类可以定义很多个构造方法,这些构造方法之间彼此构成方法的重载,在实例化对象时,编译器根据方法的参数列表调用对应的构造方法,这种机制在Java种也被称为静态绑定,在后续学习多态的时候,我们会详细讲解静态绑定和动态绑定的区别。这里只需要了解即可。我们同时也可以使用this关键字在构造方法中调用其他的构造方法。下面来看示例代码:

/**
 * 演示构造方法的重载和在一个构造方法中调用其他的构造方法
 * 
 * @authour dengjiangming
 * @date 2025-06-06
 * @version 1.0.0
 */

import java.util.Objects;


public class Student {
    private String name;

    private int age;

    public String id;

    private double averageGrade;

    public String className;

    public Student() {
    }

    public Student(String name, int age, String id, double averageGrade, String className) {
        this(name, age, id, averageGrade);
        this.className = className;
    }

    public Student(String name, int age, String id) {
        this.name = name;
        this.age = age;
        this.id = id;
    }

    public Student(String name, int age, String id, double averageGrade) {
        this(name, age, id);
        this.averageGrade = averageGrade;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public double getAverageGrade() {
        return averageGrade;
    }

    public void setAverageGrade(double averageGrade) {
        this.averageGrade = averageGrade;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", id='" + id + '\'' +
                ", averageGrade=" + averageGrade +
                ", className='" + className + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Double.compare(student.averageGrade, averageGrade) == 0 && Objects.equals(name, student.name) && Objects.equals(id, student.id) && Objects.equals(className, student.className);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age, id, averageGrade, className);
    }

    public static void main(String[] args) {
        Student stu = new Student("dengjiangming", 22, "007", 3.6,"Java");

        System.out.println(stu);
    }
}

在上面的示例代码中,我们可以看到,在带有五个参数中的构造方法中的第一行,是一个this(),里面带有四个参数,而在四个参数的构造方法中的第一行里面也是一个this(),里面带有三个参数,这就是在构造方法中调用其他的构造方法的格式,当然了也有一些需要注意的点

  1. 如果在构造方法中调用其他的构造方法,必须使用this关键字,并且this关键字必须在第一行。
  2. 不可以形成环状调用:例如:一参的构造方法调用无参的构造方法,无参的构造方法又去调用一参的构造方法。

有兴趣的读者可以发现,在上面的Student类中,有不少的get方法和set方法,并且Student类中的一部分属性是由关键字private进行修饰的。这些内容我们将在后续的文章中进行详细的介绍。

希望读者读完这篇文章后有所收获,祝所有的读者学习愉快,生活顺利。如果读者喜欢这篇文章请不要吝啬你们的点赞和三连,你们的支持都是对笔者莫大的鼓励,我们下一篇博客再见~

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值