Java类与对象详解(从语法到内部存储)

发布时间:2024年01月17日

一、什么是类?

1、定义

类(class)是Java中对一个实体的描述。对于一个实体,我们可以从属性和行为两个角度来描述。比如面对一只狗,它的属性有:姓名、性别等,它的行为有:吃饭、汪汪叫等。

那么在Java中如何体现这两个角度的描述呢?属性我们可以用基本数据类型来表示(在后面的学习中,我们也可以用其他的类来表示)。行为我们可以用方法(就是我们在C语言中说到的函数)来表示。

2、语法

[public] class 类名{
	属性
	
	方法
}

其中,类中的属性叫做“成员属性”,类中的方法叫做“成员方法”。

这里需要解释一个地方,class前面的public是可选择的,如果我们当前这个类和该java文件的文件名相同的时候,该类前面需要加public关键字,并且每个文件有且仅有一个类前面加public。比如我们有一个文件叫:test.java,那么在这个文件中,我们的test类的前面就需要加上public,但是其他类的前面不加该关键字。

另外大家不要随便地举一反三,有的读者可能了解到了public是一个访问限定符,就会有这样一种想法,这个能加public的类前面能不能将public替换成protected或者private呢?答案是不可以的。这个与文件名同名的类的前面只能加public

我们以刚刚的狗为例,看看我们应该如何描述?

public class Dog{
	//属性描述
    public String name;
    public int age;
    //行为描述
    public void eatFood(){
        System.out.println(name + "正在吃饭。");
    }
    public void barks(){
        System.out.println(name + "正在汪汪叫。");
    }
}    

二、类的实例化

1、什么是实例化

我们在上文中学到了如何描述一个对象,那么我们如何根据这个描述去实例化一个具体的对象呢?就好像,我们已经知道了狗有哪些属性以及哪些行为,那么我们是不是可以利用这些描述去描述生活中一只“具体”的狗呢?

这种根据抽象的描述创建具体的对象的过程就叫做实例化。

2、语法

类名 对象名 = new 类名();

我们还用刚刚的狗为例子,实例化过程如下。

Dog dog = new Dog();

3、成员变量和成员方法的访问

那么我们如何访问对象中的具体属性和方法呢?我们可以通过类名.属性名的语法去访问成员属性,当然也可以通过类名.方法名(参数)来访问成员方法。

我们创建一个main函数,那么我们可以写:

public static void main(String[] args) {
    Dog dog = new Dog();
    dog.name = "Tom";
    dog.eatFood();
    dog.barks();
    System.out.println("狗的名字是" + dog.name);
}

三、构造方法

我们在刚刚的语法中发现了一个很奇怪的地方。我们在new关键字的后面,写了一个类似于调用方法的代码段Dog(),这一部分其实就是调用了构造方法

1、什么是构造方法

构造方法就是在new对象的同时调用的方法,语法是类名()。但是我们会发现在之前的代码中,我们并未写构造方法,我们没写,编译器怎么调用呢?

其实,当我们不写的时候,Java会为我们提供一个默认的构造方法,这个构造方法没有参数,方法体内也是空白的,具体如下(以Dog类为例):

public Dog(){

}

这也就是为什么我们在调用构造方法的时候,没有在括号内写参数。

2、构造方法的重载

那么我们能不能根据我们的需要重新写构造函数呢?答案是可以的。语法如下:

public 类名(参数1,参数2...){
    方法体
}

我们以刚刚的Dog类为例子,如果我们想在构造Dog对象的同时给这个对象赋值。那么我们可以这样写构造方法。

public Dog(String n, int a){
    name = n;
    age = a;
}

3、构造方法的调用

由于我们重写了构造方法,所以原先编译器提供的构造方法就消失了,根据我们刚刚的例子,重写之后,我们有了形参,所以在new对象的时候就要传进去。

那么就可以写成

Dog dog = new Dog("Tom", 2);

四、this关键字

我们发现刚刚写的构造方法中,我们很难通过形参的名了解到它想让我们传入什么数据。那么我们可以把形参写地更清楚一些。

public Dog(String name, int age){
    name = name;
    age = age;
}

但是根据局部变量优先的原则,等号两边的name都是形参变量。但实际上,我们是想将形参变量name赋值给成员变量name。那么为了达到这一目的,我们就引入了this关键字,我们可以通过this.成员名来访问成员变量。

那么我们可以将上面的代码转化为:

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

五、类对象的内部存储结构

我们直接打印一下new出来的对象,如下所示:

public static void main(String[] args) {
     Dog dog = new Dog("Tom", 2);
     System.out.println(dog);
 }

通过终端我们可以观察到如下结果:
在这里插入图片描述
出乎意料的是,它并没有打印出dog中的成员属性。那么我来解释一下这段打印。
在这里插入图片描述
Dog所在包其实就可以理解为它所在的目录。后面才是重点。在学C语言的时候,我们都知道每一个变量对应一个硬件地址,那么这些地址经过转化后,会形成一串16进制的串,转化之后的这个值叫做哈希值,也叫做hash值

而我们的对象名指向的就是这一串hash值,而不是该对象。

具体存储结构如下:
在这里插入图片描述

因此我们把类似于dog的对象名称作引用。所以,我们的类又叫做引用类型

除此以外,类中的成员方法并不是和成员属性在一起的,而是共同存储在了成员方法区。

六、static关键字

1、static的基本认知

我们看下面这个例子:

public class Dog{
    public static String type;
    public String name;
    public int age;
}

那么我们new几个对象:

Dog d1 = new Dog("T1", 1, "Animal");
Dog d2 = new Dog("T2", 2, "Animal");
Dog d3 = new Dog("T3", 3, "Animal");
Dog d4 = new Dog("T4", 4, "Animal");
Dog d5 = new Dog("T5", 5, "Animal");

如果转化为内存的话,就相当于有5个引用分别是d1...d5,他们分别指向堆中五块不同的区域。但是我们发现成员属性type的值都是animal,因此我们没必要存储五份,只需要存储一份即可。

因此,我们可以将这一个属性抽离出来,让多个对象共用一个成员属性。

那么,我们就可以用static去修饰,此时就可以让多个对象共用一个成员变量。

public class Dog{
    public static String type;
    public String name;
    public int age;
}

那么此时的存储结构就会发生如下变化:

加static之前:
在这里插入图片描述

加static之后:
在这里插入图片描述

2、static的深入理解

既然所有的类对象共用一个静态变量,也就是说静态变量不依赖于对象。

所以即使我们没有实例化对象,我们依旧能够访问到类中的静态成员。

所以我们可以直接用:类名.静态变量名来访问,当然用实例化后的对象名.静态变量名访问也可以,但是不规范。

比如:

public static void main(String[] args) {
    Dog.type = "Animal";
}

除了成员变量之前可以加static关键字,我们的成员方法之前也可以加static。那么根据刚刚的分析,如果加了static。那么我们的静态方法也可以在不实例化对象的情况下调用。比如我们的println()方法。

文章来源:https://blog.csdn.net/weixin_72060925/article/details/135618399
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。