类和对象(1) —— 类和对象的概念、类的实例化和初始化、构造方法和this关键词

目录

[1. 基础概念](#1. 基础概念)

[1.1 面向过程和面向对象](#1.1 面向过程和面向对象)

[1.2 类和对象](#1.2 类和对象)

[2. 类的创建](#2. 类的创建)

[2.1 类的基础创建格式](#2.1 类的基础创建格式)

[2.2 自动创建类 和 类的命名规则](#2.2 自动创建类 和 类的命名规则)

[3. 类实例化成对象 (对象的创建)](#3. 类实例化成对象 (对象的创建))

[3.1 类类型](#3.1 类类型)

[3.2 什么是实例化?怎样实例化?](#3.2 什么是实例化?怎样实例化?)

[3.3 实例化的意义 ------ 类和对象的关系](#3.3 实例化的意义 —— 类和对象的关系)

[4. 对象的初始化](#4. 对象的初始化)

[4.1 默认初始化](#4.1 默认初始化)

[4.2 就地初始化](#4.2 就地初始化)

[5. 类和对象中涉及到的生命周期和作用域(初步了解)](#5. 类和对象中涉及到的生命周期和作用域(初步了解))

[5.1 类、对象、实例变量](#5.1 类、对象、实例变量)

[5.2 成员变量 和 成员方法](#5.2 成员变量 和 成员方法)

[5.3 局部变量 和 全局变量【与c不同】](#5.3 局部变量 和 全局变量【与c不同】)

[6. 构造方法](#6. 构造方法)

[6.1 构造方法的概念和特性](#6.1 构造方法的概念和特性)

[6.2 构造方法的创建和使用](#6.2 构造方法的创建和使用)

[6.2.1 默认的构造方法](#6.2.1 默认的构造方法)

[6.2.2 无参数的构造方法](#6.2.2 无参数的构造方法)

[6.2.3 带多个参数的构造方法](#6.2.3 带多个参数的构造方法)

[6.2.4 构造方法的重载](#6.2.4 构造方法的重载)

[6.3 自动创建构造方法和成员方法](#6.3 自动创建构造方法和成员方法)

[6.4 构造方法的调用时期](#6.4 构造方法的调用时期)

[7. this关键字的功能](#7. this关键字的功能)

[7.1 this的概念(用this指向当前对象)](#7.1 this的概念(用this指向当前对象))

[7.2 用this区分成员变量和局部变量](#7.2 用this区分成员变量和局部变量)

[7.3 用this调用构造方法](#7.3 用this调用构造方法)

[7.4 this的类型 与 方法的链式调用](#7.4 this的类型 与 方法的链式调用)


1. 基础概念

1.1 面向过程和面向对象

面向对象和面向过程,这两者都是生活上的概念;面向对象编程和面向过程编程是由生活概念衍生或拓展出来的 编程概念

一、面向过程

  1. 生活概念

    **面向过程强调的是按照步骤或流程来解决问题,这与日常生活中的"按部就班"非常相似。**例如,烹饪一道菜时,需要先准备食材,然后按照一定的顺序进行烹饪,最后装盘上桌。

  2. 编程概念

    面向过程是一种以事件为中心的编程思想,编程时把解决问题的步骤分析出来,然后用函数把这些步骤实现,在一步一步的具体步骤中再按顺序调用函数。

二、面向对象

  1. 生活概念

    面向对象强调的是将问题分解为多个相互关联的对象,并通过对象之间的交互来解决问题。这与日常生活中的"团队合作"或"分工合作"相似。 例如,一个公司可以看作是由多个部门组成的对象集合,每个部门都有自己的职责和功能;我们不需要具体知道每个部门内部是如何运作,我们只需要知道每个部门能够做什么 ,通过部门之间的协作来实现公司的整体目标。

  2. 编程概念

    **面向对象编程是一种以对象为基本单位的编程方式。**它将数据和操作数据的函数(方法)封装在一起,通过对象间的交互来完成任务。面向对象编程适用于复杂、需要高度模块化和可扩展性的系统,如大型软件、游戏开发等。它注重整体性和对象之间的交互,通过类和对象的抽象来解决问题。

1.2 类和对象

类是多个对象所具有的相似属性和特点的抽象集合。
"类和对象"一般都是指编程上的概念(而不是生活上的概念)。 前面说了,面向对象编程是一种以对象为基础单位的编程方式,而对象是依靠类的语法而得以存在的。


我们以一个例子来说明:(面向对象------洗衣服)

  1. 在现代,洗衣服这个事情涉及到4个对象:人、衣服、洗衣粉、洗衣机。

(因为是面向对象,所以为了把衣服洗好,我们不需要关心衣服有多少对应多少洗衣粉、人要分几次才把衣服放完、洗衣机要滚动多少圈放多少次水............++我们只需要知道这4个对象的交互可以把衣服洗好++)

  1. 这4个对象并没有相似的属性和特点,所以我们需要用到4个类,以洗衣机类为例

提取所有洗衣机实体的所具有的属性和特点:

我们可以使用下面这个"类"来描述洗衣机对象了:

洗衣机类:

属性:产品品牌,型号,产品重量,外观尺寸,颜色...

功能:定时....
该例子中的洗衣机对象:

  • 属性:
    • 产品品牌: 樱花
    • 型号: XPB150-1505
    • 产品重量: 39kg
    • 外观尺寸: 850 x 500 x 1000(单位mm)
    • 颜色: 白色
    • ......
  • 功能:
    • 标准净洗
    • 烘干
    • 快洗
    • ......
  1. 类和对象是一种编程上的概念,而编程是讲究语法的,在Java中我们可以简单写成这样:
cpp 复制代码
class WashMachine{
    String brand = "樱花";   // 品牌
    String type = "XPB150-1505";    // 型号
    double weight = 30;  // 重量
    double length = 850;  // 长
    double width = 500;  // 宽
    double height = 1000;  // 高
    String color = "白色";   // 颜色

    void washClothes(){   // 普通洗衣服
        System.out.println("洗衣45分钟");
    }

    void dryClothes(){    // 脱水
        System.out.println("脱水功能");
    }
    
    void quickWash(){    //快洗
        System.out.println("快洗21分钟");
    }
}

(这只是比较简单的写法,实际上还会用到很多访问限定符和其他的关键字)


2. 类的创建

2.1 类的基础创建格式

在java中定义类时需要用到 class关键字

class 类名{

成员变量;

成员方法;

}

注意:我们习惯是把所有的成员变量写在开头; 当然,如果成员变量的创建写在调用该成员变量的成员方法后面,语法上也是支持的。
类中包含2种成员:

  1. 成员变量:用于存储对象的状态。它们可以是基本数据类型(如int、float等)或引用类型(如String、数组等)。
  2. 成员方法:用于描述对象的行为或功能。

2.2 自动创建类 和 类的命名规则

除了自己来写类,我们也可以通过系统来帮我们创造一个无成员的类,具体步骤如下:

步骤1~3:

完成后会弹出下面这个窗口,步骤4~6:

最后会生成一个访问限定符为public的类:


【注意事项】和 类的命名规则

①. 我们自动创建类,其实是创建了一个.java文件。该类由public修饰,且该类的名字和文件的名字相同。

(观察上面的两张图,确实是这样)

②. 硬性要求:

  1. 一个.java文件内只允许存在一个由public修饰的类,且此类的名字必须和文件名相同;
  2. 如果该文件有其他类并列存在,则其他类不能被访问限定符修饰

【所以最好的书写建议:一个文件一个类

例如:

③. 访问限定符有public、protected、private(具体的功能在下一篇文章讲),它们不仅可以修饰类,还能修饰成员变量和成员方法。目前成员变量和方法我们都先用着public,main方法先用着public static。

④.软性建议:类的命名采用大驼峰命名法 ,即每个单词的首字母都采用大写字母

比如刚刚的洗衣机类:WashMachine。


3. 类实例化成对象 (对象的创建)

3.1 类类型

定义了一个类,就相当于在计算机中定义了一种新的类型,该类的类型就称为类类型。

例如:

cpp 复制代码
public class Dog {
    public String name;
    public String color;

    public void barks(){
        System.out.println(name+":汪汪汪~");
    }

    public void wag(){
        System.out.println(name+":摇尾巴~");
    }
}

这里的Dog就是一种类类型。

类类型也是一种引用类型,用类类型创建的变量称为实例变量,实例变量存储的是地址。

3.2 什么是实例化?怎样实例化?

用类类型创建对象的过程,称为类的实例化,在java中采用new关键字,配合类名来实例化对象。

以刚刚的Dog类为例:

cpp 复制代码
​
public class Main{
    public static void main(String[] args) {
        Dog dogh = new Dog();     //通过new实例化对象
        dogh.name = "阿黄";
        dogh.color = "黑黄色";

        
        Dog dogs = new Dog();
        dogs.name = "大白";
        dogs.color = "白色";
    }
 }

​
  • "对象的创建" ,也叫"对象的实例化 ",也可以叫做"类实例化成对象";这几种说法都一样。
  • 实例化具体是:new一个类,给出一块空间来存储一个对象,而该对象所需要的空间大小根据所属类的大小来判断(这就是为什么前面说:在面向对象编程中,对象是依靠类而存在的)。比如这里的" Dog dogs = new Dog() "。
  • Dog类被new出来后,我们就说创建了一个Dog类的对象。
  • 变量dogs被称为实例变量,引用着这个Dog类实例化后的对象。使用" . "来访问对象中的属性和方法。

类名后面的括号不能省略,这与后面要讲到的构造方法有关。

而后面的内容,比如" dogs.name = "大白"; dogs.color = "白色"; ",其实是属于 对象的初始化

3.3 实例化的意义 ------ 类和对象的关系

做个比方:类相当于一个模板或蓝图 ,用于创建具体的对象。对象则是类的实例化结果,它是具体的实体,拥有类所定义的属性和方法。

4. 对象的初始化

我们知道,Java中对变量的初始化检查是很严格的:在main方法中变量没有初始化就使用,会编译报错。为什么在类中声明的成员变量不初始化也不会报错呢?

4.1 默认初始化

当我们new了一个对象的时候,如果没有赋值初始化,对象里面的属性(成员变量)会被赋予默认值

例如:

cpp 复制代码
public class Dog {
    public String name;
    public int age;
}

class Main{
    public static void main(String[] args) {
        Dog dog = new Dog();
        System.out.println(dog.name);
        System.out.println(dog.age);
    }
}

输出结果:


默认值和数组的默认初始化是一样的:整数是0,小数是0.0,布尔型是false,引用类型都是null......

4.2 就地初始化

在声明成员变量时,就直接给出了初始值。

cpp 复制代码
public class Dog {
    public String name = "大白";
    public int age = 4;
}

class Main{
    public static void main(String[] args) {
        Dog dog = new Dog();
        System.out.println(dog.name);
        System.out.println(dog.age);
    }
}

其实在实例化3.2中的例子,像这种在main方法中直接对成员变量赋值的情况是很少的。因为一般一个类,所有的成员变量都是用private修饰,在其他类的main方法中就不能直接使用private修饰的成员变量了。

5. 类和对象中涉及到的生命周期和作用域(初步了解)

5.1 类、对象、实例变量

    • 生命周期:
      • 从类加载开始,到程序运行结束后卸载。其中类加载发生在编译阶段。
    • 作用域:
      • 类的作用域受访问限定符限制,它们都属于包级的访问权限。
  • 对象:

    • 生命周期:
      • 从对象被实例化开始(从new出来开始),到 程序结束 或 没再被实例变量引用 时被回收。
    • 作用域:
      • 由类实例化,所以也受访问限定符限制。
  • 实例变量:

    • 生命周期:
      • 实例变量属于特殊的局部变量,可以引用实体对象。在main方法中被创建,到程序结束时回收。
    • 作用域:
      • 作用于方法体中。

5.2 成员变量 和 成员方法

成员变量和成员方法++都是对象的属性和功能++ ,所以是从对象被实例化开始存在,到对象被回收时结束。(生命周期与对象一致)

方法与函数的不同:

  1. 在C语言中,函数的定义必须写在所有函数的外面。而在Java中,方法的定义,必须写在类里面。
  2. 在C语言中,只要有头文件声明该函数的存在,在别的.c文件中就可以使用该函数。而在Java中,方法的使用还受到访问限定符的修饰,不是所有的包和类都可以使用该类下的方法。

5.3 局部变量 和 全局变量【与c不同】

局部变量

Java局部变量的定义:

在Java中,如果变量的创建或声明处于类的里面、方法的外面,则该变量是成员变量;如果变量的创建和声明处于方法的里面,则该变量是局部变量。

例如:

在Java中,局部变量不能被访问限定符修饰。

例如:

可以看到,用访问限定符public修饰局部变量,编译器会报错。

Java局部变量的生命周期与作用域,++与C语言是类似的。++从变量声明开始,到方法执行完毕或代码块结束时结束。定义域仅限于声明它的方法或代码块内部。


全局变量

在Java中已经不存在全局变量的概念了。因为在C语言中,全局变量的创建位于所有函数和代码块的外面;而在Java中,变量的声明至少也必须在类的里面。

不过也可以通过静态变量来时实现全局变量的功能,比如public类前提下的public static变量。静态变量的内容在下一篇博客会讲,这里简单提一下。

6. 构造方法

6.1 构造方法的概念和特性

构造方法的定义:

构造方法是一种没有返回值的成员方法。
构造方法的特性:

  1. 构造方法也叫构造器,是一种特殊的成员方法。
  2. 没有返回值类型,设置为void也不行。
  3. 名字必须与类名相同。
  4. 在整个对象的生命周期内只调用一次。

例如:

cpp 复制代码
public class Person {
    public String name;
    public int age;

    Person(){   //构造方法
        name = "人";
        age = 25;
    }
}

class Test1{
    public static void main(String[] args) {
        Person person = new Person();
        System.out.println(person.name);
        System.out.println(person.age);
    }
}

输出结果:

可以发现,此处的构造方法Person()方法是无返回值类型的。

如果构造方法的名字与类名不相同,则会报错。如果某方法与类名一致,但是有返回值,则该方法只是一个普通的成员方法,只不过名字恰好和类名相同。

构造方法也可以被访问限定符修饰,一般都是由public修饰。

6.2 构造方法的创建和使用

6.2.1 默认的构造方法

如果用户没有显式定义构造方法,编译器会生成一份默认的构造方法。

  1. 生成的默认构造方法一定是无参的。
  2. 默认构造方法的方法体一般是无内容的。(等学到继承会有点不同)
  3. 默认构造方法的访问修饰符与所在类的访问修饰符一致。

例1:(usage是IDEA给的提示,与代码无关)

例2:

6.2.2 无参数的构造方法

自己显式定义的没有参数的构造方法。在创建对象时,写法和无显式定义构造方法时一样,例如"Class c = new Class(); "。
注意:一旦自己显示定义了构造方法,编译器就不会生成默认构造方法。(救急不救穷)

例如:

cpp 复制代码
class Person {
    public String name;
    public int age;
    Person(){   //显示定义构造方法
        name = "person";
        age = 0;
    }
}
class Test2{
    public static void main(String[] args) {
        Person person = new Person();   //与无构造方法的写法相同
        System.out.println(person.name);
        System.out.println(person.age);
    }
}

输出结果:

它并不等于:

这里报错是因为两个构造方法没有构造方法重载,因为两个方法的参数列表完全相同。

6.2.3 带多个参数的构造方法

显示定义了带参数的构造方法且要使用该方法时,在new阶段就要写够参数。例如"Class c = new = Class(参数1,参数2,......参数n);"。

当然,还是救急不救穷。显示定义了构造方法,编译器就不会生成默认构造方法。

例如:

cpp 复制代码
class Person {
    public String name;
    public int age;
    Person(String s, int n){
        name = s;
        age = n;
    }
}
class Test3{
    public static void main(String[] args) {
        Person person = new Person("小明",18); //补足参数列表
        System.out.println(person.name);
        System.out.println(person.age);
    }
}

6.2.4 构造方法的重载

构造方法也是可以构成方法重载的。

因为构造方法名必须和类名相同,那么我们只需要保证参数列表不同。

至于在创建对象时使用的是哪一个构造方法?系统会根据你输入的参数个数、参数类型和参数顺序来判断所使用的构造方法。

例如:

cpp 复制代码
class Person {
    public String name;
    public int age;
    Person(){
    }

    Person(int n){
        age = n;
    }

    Person(String s, int n){
        name = s;
        age = n;
    }
}
class Test3{
    public static void main(String[] args) {
        Person p1 = new Person();
        Person p2 = new Person(34);
        Person p3 = new Person("小军",20);
        System.out.println(p1.name + ": " + p1.age);
        System.out.println(p2.name + ": " + p2.age);
        System.out.println(p3.name + ": " + p3.age);
    }
}

输出:

6.3 自动创建构造方法和成员方法

IDEA集成开发工具为我们提供了自动创建构造方法和成员方法的功能。

构造方法(Constructor)

详细步骤:

成员方法(Getter和Setter)

详细步骤:

创建成功后就会有get和set方法了:

6.4 构造方法的调用时期

对于一个简单的构造方法调用和对象的创建,例如:Person p = new Person("小明",18).

在程序层面只是简单的一条语句,在JVM层面需要做好多事情,下面简单介绍下:

1. 检测对象对应的类是否加载了,如果没有加载则加载

2. 为对象分配内存空间

3. 处理并发安全问题

比如:多个线程同时申请对象,JVM要保证给对象分配的空间不冲突

4. 初始化所分配的空间

即:对象空间被申请好之后,对象中包含的成员已经设置好了初始值

5. 设置对象头信息(关于对象内存模型后面会介绍)

6. 调用构造方法,给对象中各个成员赋值

7. this关键字的功能

7.1 this的概念(用this指向当前对象)

先看一个日期类的例子:

cpp 复制代码
public class Date {
    public int year;
    public int month;
    public int day;

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

    public void printDate(){
        System.out.println(year + "/" + month + "/" + day);
    }
}
class Main{
    public static void main(String[] args) {
        // 构造三个日期类型的对象 d1 d2 d3
        Date d1 = new Date();
        Date d2 = new Date();
        Date d3 = new Date();

        // 对d1,d2,d3的日期设置
        d1.setDay(2020,9,15);
        d2.setDay(2020,9,16);
        d3.setDay(2020,9,17);
        // 打印日期中的内容
        d1.printDate();
        d2.printDate();
        d3.printDate();
    }
}

输出结果:

我们想要成员变量的year、month、day被形参初始化赋值,可得到的结果都是0,这是为什么?

因为当成员变量和局部变量重名时,使用局部变量的使用优先级会高于成员变量。

【这与C语言中,全局变量与局部变量重名时类似,局部变量的优先级也会高于全局变量】

这时候,Java提供一个this关键字解决了这个问题。还是刚刚的例子,我们用this关键字:

cpp 复制代码
public class Date {
    public int year;
    public int month;
    public int day;

    public void setDay(int year, int month, int day){
        this.year = year;       这里全用上了this
        this.month = month;
        this.day = day;
    }

    public void printDate(){
        System.out.println(year + "/" + month + "/" + day);
    }
}
class Main{
    public static void main(String[] args) {
        // 构造三个日期类型的对象 d1 d2 d3
        Date d1 = new Date();
        Date d2 = new Date();
        Date d3 = new Date();

        // 对d1,d2,d3的日期设置
        d1.setDay(2020,9,15);
        d2.setDay(2020,9,16);
        d3.setDay(2020,9,17);
        // 打印日期中的内容
        d1.printDate();
        d2.printDate();
        d3.printDate();
    }
}

输出结果:

为什么可以这样?

因为this可以引用当前对象。

比如有一个" Person p = new Peron("小明") ",对象中的构造方法有" this.name = name ",那么这条语句就相当于:p.name = name。

注意:this引用的是当前对象,而不是当前类。(这一点会在下一篇的static部分讲解)

7.2 用this区分成员变量和局部变量

这在刚刚的例子中就有所体现,这里补充一点:

**this是"成员方法"第一个隐藏的参数,编译器会自动传递。**在成员方法执行时,编译器会负责将调用成员方法 对象的引用传递给该成员方法,this负责来接收。

cpp 复制代码
public class Date {
    public int year;
    public int month;
    public int day;

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

这个类中的成员方法就相当于下面这个类中的成员方法:

cpp 复制代码
public class Date {
    public int year;
    public int month;
    public int day;

    public void setDay(Date this, int year, int month, int day){ //隐藏的参数this
        this.year = year;       
        this.month = month;
        this.day = day;
    }
}

7.3 用this调用构造方法

this调用构造方法的要求:

  1. 当存在多个构造方法的时候,可以用"this(参数表)"的方式在一个构造方法中调用另一个构造方法。
  2. this(......) 语句只能在构造方法中。(因为构造方法只能被调用一次)
  3. this(......) 语句必须处于构造方法的第一行 。(这是硬性要求,Java语言的设计者为了保证成员变量在正确初始化之前不被使用)
  4. 构造方法不能递归调用 。(因为构造方法只能被调用一次)

对于要求1的错误例子:只有一个构造方法的时候也调用this(...)

cpp 复制代码
​class Date {
    public int year;
    public int month;
    public int day;

    public Date(){
        this();    //只有一个构造方法也使用this()
    }
}

这个方法就相当于public Date(){ Date();}。形成了递归结构,而构造方法是不能递归调用的。


要求2的错误例子:this(...)写在了成员方法里面


要求3的错误例子:this(...)没写在第一行


要求4的错误例子:多个构造方法成环

7.4 this的类型 与 方法的链式调用

this的类型

this引用的是当前对象,对象的类型就是this的数据类型。(属于类类型)

比如刚刚的Date类,里面所有的this它的数据类型都是Date类型。

方法的链式调用

在Java中,方法的链式调用是一种常见的编程技术,它允许在一个表达式中连续调用多个方法。

链式调用的特点:

每次对象调用的成员方法,其返回值均是该对象。

例如:

cpp 复制代码
class Person {
    public String name;
    public int age;

    public Person setName(String name) {    //返回类型是Person类型
        this.name = name;
        return this;            //返回当前对象
    }

    public Person setAge(int age) {   //返回类型是Person类型
        this.age = age;
        return this;            //返回当前对象
    }
}
class Main{
    public static void main(String[] args) {
        Person p = new Person().setName("小明").setAge(18); //链式调用
        System.out.println(p.name+":"+p.age);
    }
}

输出:

这里的调用逻辑是:先创建对象并调用构造方法,返回该对象后再调用setName方法,而setName的返回值也是该对象,再用返回值对象调用setAge方法,最终setAge方法返回对象给实例变量p。

注意:链式访问不是链式调用。链式访问是:一个方法的返回值,被作为另一个方法的参数。

假如有一个加法方法add:

复制代码
System.out.println(add(add(1, 2), 3));

最里面add方法的返回值,也是外面add方法的参数;而外面add方法的返回值,同时也是println方法的参数。


本期分享完毕,感谢大家的支持Thanks♪(・ω・)ノ

相关推荐
bingbingyihao13 分钟前
代码辅助工具 GPT / Cursor
android·java·gpt
CN.LG13 分钟前
浅谈Spring Boot之任务调度
java·spring boot·后端
少说多做34315 分钟前
Android 使用 LiveData/OnCheckedChangeListener 来监听变量变化
android·java·android-studio
安girl27 分钟前
springMVC重点知识
java·开发语言
被猫枕的咸鱼1 小时前
工作学习--Arrays.asList的问题
java·开发语言·学习
RT_01141 小时前
设计模式之策略模式
java·设计模式·策略模式
无名指的等待7121 小时前
Nacos实现IP动态黑白名单过滤
java·spring boot·tcp/ip
supercool71 小时前
SpringBoot(8)-任务
java·spring boot·后端
Erosion20202 小时前
JAVA 静态代理 & 动态代理
java
今天秃头了吗??2 小时前
贪心算法入门(三)
java·数据结构·算法·贪心算法