从浅入深讲解Java类

本文详细介绍了Java中的类概念、实例化过程、构造方法、this关键字的应用以及类对象的初始化机制,包括静态变量和静态方法。以Dog类为例,展示了这些概念的实际操作和内存结构。
摘要由CSDN通过智能技术生成

一、什么是类?

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;
}

这里的this指向调用该成员方法的对象

五、类对象的初始化问题

1、引入

我们知道,在Java中,一个未被初始化的变量是无法使用的。比如下面的代码在编译阶段就会发生报错。

public static void main(String[] args) {
    int a;
    System.out.println(a);
}

终端报错信息如下所示:
在这里插入图片描述
普通变量如此,那么类变量是怎样的呢?
我们还是以刚刚的Dog类为例子,当我们实例化一个Dog类对象后,如果不初始化,直接使用类中的成员变量,那么会发生什么呢?

为了解决这个问题,我们运行下面的代码:

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

运行起来后,我们观察终端的反应,不难发现,这段代码没有出现报错,并且还输出了一些信息。如下图所示:
在这里插入图片描述
那么这是为什么呢?想要解释这个问题,我们就要知道,当我们new一个对象的时候,Java虚拟机做了什么。

2、new关键字背后的过程

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

step2. 为对象分配内存空间。

step3. 处理并发安全问题。比如:多个线程同时申请对象,JVM要保证给对象分配的空间不冲突。

step4. 初始化所分配的空间。

step5. 设置对象头信息。

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

我们发现,在第四步中,JVM就已经为类对象中的成员变量进行了初始化。最后一步才是调用我们的构造方法。

这也就是说,如果我们通过构造方法进行初始化的话,在进行构造方法中赋值语句之前,类变量的值都是JVM赋予的初始值,构造方法中的赋值语句执行后,类变量的值才是我们自己所赋的值。

那么JVM的赋予的初始值都是多少呢?可以看下面这张表。
在这里插入图片描述

3、就地初始化

我们可以在声明成员变量时,就直接给出初始值。如下代码所示:

public class Dog{
	//就地初始化
    public String name = "Tom";
    public int age = 2;
    //行为描述
    public void eatFood(){
        System.out.println(name + "正在吃饭。");
    }
    public void barks(){
        System.out.println(name + "正在汪汪叫。");
    }
}    

但这里要注意的是!!!即使我们进行了就地初始化,JVM在初始化赋值的时候,依旧会按照上述的表格进行初始化赋值。

我们这些就地初始化的语句会被JVM自动放到构造方法中。

所以,当构造方法执行的时候,我们的就地初始化语句才会执行。

六、类对象的内部存储

我们直接打印一下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()方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值