编辑
前言:
大家好,我还是那个不会打拳的程序猿。今天我给大家讲解的是类和对象,文章从类的定义及使用、类的实例化、this引用等来讲解。相信大家在之前的学习中都是面向过程的思想,那么今天就让我们走向面向对象的世界吧。
编辑
目录
1.面向过程VS面向对象
在我们学习类和对象之前,我们必须了解到面向对象和面向过程的概念是什么。相信大部分朋友都学习过C语言,C语言就是一个面向过程的语言。而Java是一个面向对象的语言,它可以引用一些对象和类比较方便。那什么是面向过程与面向对象呢,下面我就来讲解。
1.1什么是面向过程
相信大家都取过快递,我们拿快递的时候就是一个过程,我面向的就是拿快递的这个过程。我需要先到达快递驿站,然后找到快递所在位置,再拿到出库口,拿出手机打开出货码对准扫描器完成出货。这就是一个面向过程的概念。
编辑
1.2什么是面向对象
同样,还是以拿快递这个概念来举例子。如果我是一个老板,我有一个秘书。当我的快递到了的时候,我直接叫秘书给我拿一下。而我就不需要进行拿快递这个过程,这就是面向对象的概念。
编辑
我们可以看到,面向对象的概念更为方便一些。那么Java中也是如此,我们直接调用一个方法或者直接引用一个对象,就可以多次避免一个操作或过程的重复定义。我们自己调用或引用一下就好了这就是,面向对象的强大。因此面向过程VS面向对象当然是面向对象胜出。
2.类的定义与使用
在我们面向对象设计时,关注的是对象。在显示生活中对象是一些实体。比如一个手机它是一个实体,计算机是不能直接识别这个实体的,我们需要将这个实体的内容用编程语言给写出来。
编辑
那么上图中的描述,就是一个手机的参数的描述。我们需要将这些参数用Java语言给编写出来,这就会使用到Java中的类:Class。
2.1初识类
那么类是用一些实体(对象)来进行描述的,主要描述的是这些实体的特征与性能。比如一部手机:
手机,它在Java中可以将其看成是一个类。
属性:品牌、型号、功能、存储、外观、像素、颜色....
功能:打电话、看电影、听音乐、购物等等
那么我们知道了一个类里面包含着什么内容,那么如何对上述的手机进行定义呢?
2.2类的定义与格式
Java中定一个类需要用到Class关键字,语法格式:
class ClassName { field;//字段(属性)、成员变量 method;//行为或者成员方法 }
以上代码中,class为定义类的关键字,ClassName为类的名称,{}里面的内容为类的主体,这些主体可以是对这个类的一些描述也可以是一些方法。比如一个手机类:
class Phone { public String bradnd;//品牌 public String type;//型号 public double weight;//重量 public double length;//长度 //打游戏方法 public void playGame() { System.out.println("打游戏"); } //听音乐方法 public void listenMusic() { System.out.println("听音乐"); } //打电话方法 public void call() { System.out.println("打电话"); } }
在这个手机类里面,我定义了一个描述于这个手机的变量,有品牌、型号、重量、长度。和一些这个手机类会做到的一些功能方法,有打游戏、听音乐、打电话。当然你也可以添加更多的功能,那么以上代码就是一个简单的类的创建。
再比如定义一个狗类:
class Dog { public String name;//名字 public int age;//年龄 public void eat() { System.out.println("狗粮"); } public void sleep() { System.out.println("天天睡觉"); } }
我们在定义一个狗类的时候,自然联想到这个狗有名字有年龄会吃饭会睡觉。因此就可以在类体里面定义一些关于这个狗会做出的一些动作。
注意:
- 一个.java文件中只能定义一个类
- 一个.java文件里只能有一个public类
- 类名注意采用大驼峰定义如MyDogIs
- 成员前面统一使用public
- 类里面定义的方法,可以有static修饰,也可以不用static修饰
3.类的实例化
3.1什么是类的实例化
定义一个类,就相当于在计算机中定义了一种新的类型,像我们编译器中int,double,char这些都是Java内置的类型,而我们直接定义的类如狗类就是我们自己人工自定义了一个新类。
编辑
我们知道了类的定义方式,就可以拿到类里面的对象。而这些拿到这些对象前必须对这些对象进行实例化。在Java中采用new关键字,配合类名来实例化。格式:类名 任意名 = new 类名()。在上面我们定义了两个类一个是手机类一个是狗类。这两个类里面的内容只是定义了没有被使用,我们实例化类后就可以用使用这些对象。
public class Test { public static void main(String[] args) { Dog dog = new Dog(); Phone phone = new Phone(); dog.name = "旺财"; dog.age = 3; phone.bradnd ="苹果"; phone.type ="苹果13"; phone.weight = 1.2; phone.length = 120; dog.eat(); phone.playGame(); System.out.println(dog.name+" "+dog.age); System.out.println(phone.bradnd+" "+phone.type+" "+phone.weight+" "+phone.length); } }
运行后输出:
编辑
上述程序,我们使两个类都通过new关键字实例化了 。一个名为dog一个名为phone,我们通过.号来访问对象的属性和或方法并可以对这些属性进行操作。当然,一类可以同时创建多个实例,同样也是使用new关键字来实现。如:
public class Test { public static void main(String[] args) { Dog dog1 = new Dog(); Dog dog2 = new Dog(); dog1.name = "旺财"; dog2.name = "来福"; System.out.println(dog1.name); System.out.println(dog2.name); } }
运行后输出:
编辑
当然,通过一个类创建的多个实例不会在内存从相互冲突。就拿Dog类实例化的dog1和dog2这两个实例。它们在是在内存中是这个样子的:
编辑
注意:
- new关键字用于创建一个对象的实例
- 使用.号来访问对象中的属性和方法
- 同一个类可以创建多个实例
3.2类和对象的说明
- 类就像一个模型,用来对一个实体进行描述,类这个模型限定了里面有创造出的东西有哪些成员。
- 类是一个自定义类型,可用来定义变量。
- 一个类可以实例化多个对象,实例化出对象占用的内存根据类里面的存储的数据类型来计算。
我们可以这样去理解。类型实例化对象就像手机在工厂中,手机要通过一个模型来制造出一个个性能一样的手机。但是这个模型没有实际的实体,我们只能通过这个模型来创造出一个实体。类亦是如此,只是一个模型,我们需要通过new来制造一个实体这就叫实例化类。
编辑
当然怎样new一个对象,我们在上面已经讲到了,那么上图描述的大概就是类与实例化的关系。
4.this引用
4.1为什么要有this引用
this为当前对象的引用,我们来看一个例子,定义一个日期类并通过main方法来调用这个类:
class Date { public int year; public int month; public int day; public void setData(int y,int m,int d) { year = y; month = m; day = d; } public void show() { System.out.println(year+"年"+month+"月"+day+"日"); } } public class Test { public static void main(String[] args) { Date date1 = new Date(); Date date2 = new Date(); date1.setData(2023,3,10); date2.setData(2023,3,11); date1.show(); date2.show(); } }
输出:
编辑
以上代码,定义了一个Date类。然后在main方法中创建了两个对象分别为date1和date2,并通过Date类中seDate方法对Date里面的对象进行修改,并通过show方法打印出设置好的内容。这样是没有任何问题的,但是还有两个疑问点:
1.形参名不小心与成员变量名相同
public void setData(int year,int month,int day) { year = year; month = month; day = day; }
那方法体中到底是谁给谁赋值?成员变量给成员变量?参数给参数?参数给成员变量?成员变量参数?估计自己都搞不清楚了。因此,我们得这样定义:
public void setData(int year,int month,int day) { this.year = year; this.month = month; this.day = day; }
加上了this就表明的当前对象的引用。什么意思呢,就是说明了被this的这个才是当前的对象。this的具体用法我们下面会讲到。
2.还是拿上述代码说明
两个对象都在调用setDate和show方法,但是这两个方法中没有任何有关对象的说明,那么setDate和show方法是如何知道打印哪一个对象的数据呢?因此我们可以加上this关键字。
class Date { public int year; public int month; public int day; public void setData(int year,int month,int day) { this.year = year; this.month = month; this.day = day; } public void show() { System.out.println(this.year+"年"+this.month+"月"+this.day+"日"); } } public class Test { public static void main(String[] args) { Date date1 = new Date(); Date date2 = new Date(); date1.setData(2023,3,10); date2.setData(2023,3,11); date1.show(); date2.show(); } }
如果加上this后,无论是我们还是编译器都能很清晰的知道。诶,我要找的就是这个对象,我要打印的就是这个对象(类中的成员变量)。
4.2什么是this引用
this引用是指,指向当前对象(成员方法运行时调用该成员方法的对象),在成员方法中所有成员变量的操作,都是通过该引用去访问的。注意this引用后面要加.号。
class Date { public int year; public int month; public int day; public void setData(int year,int month,int day) { this.year = year; this.month = month; this.day = day; } public void show() { System.out.println(this.year+"年"+this.month+"月"+this.day+"日"); } }
比如以上的代码,我们的setData方法中形参名和Date的类成员变量名相同了。那么这时候可以用到this来引用成员变量名这样使得我们程序员和编译器都能很清晰的知道,这就是我想要进行的操作。
4.3this引用的特性
- this的类型是对应类型的类型引用,比如一个类里面有个对象为int那么this的类型为引用的这个对象的类型
- this只能在"成员方法"中使用
- 在"成员方法"中,this只能引用当前对象,不能再引用其他对象
- this是“成员方法”第一个隐藏的参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法对象的引用传递给该成员方法,this负责来接收
编辑
5.对象的构造及初始化
在上述内容总,我们知道了this可以访问成员变量。其实this还有另外两种用法:this访问构造方法、this访问成员方法,当然我们在使用这两种方法时,得知道怎样去构造一个对象。具体怎么使用,下面我就来一一讲解。
5.1如何初始化对象
在学习数据类型时,我们知道了在Java中定义一个局部面必须要初始化,否则编译器会编译失败。
public class Test { public static void main(String[] args) { int num; System.out.println(num); } }
显示:
编辑
要让以上代码成功的编译通过,我们只需要在num上赋个初始值 。那如果是对象呢?拿4.1中的日期类举例:
class Date { public int year; public int month; public int day; public void setData(int y,int m,int d) { year = y; month = m; day = d; } public void show() { System.out.println(year+"年"+month+"月"+day+"日"); } } public class Test { public static void main(String[] args) { Date date = new Date(); date.show(); date.setData(2023,3,10); date.show(); } }
输出:
编辑
我们发现,对象不初始化会默认自带值跟数组不初始化化默认值为0类似,当然并不是所有对象都默认值为0根据不同的类型有不同的默认值。那么对象的默认值我在下方会讲到,先把目前的难点搞懂。
因此我想对象有初始值就调用setDate方法才能将具体的日期设置到对象中。因此我们可以发现有两个问题:
- 每次对象创建好后调用setDate方法才能设置具体的日期,非常麻烦,那么对象如何初始化呢?
- 局部变量必须要初始化才能使用,为什么对象里面的字段声明后没有值依然可以使用?
以上问题解决方法:对象的初始化与为何对象里面字段不声明有默认值,下文中有很好的讲解。
5.2构造方法
我们知道方法有返回值,参数列表。而构造方法,没有返回值,只有参数列表,并且一般由public修饰。
class GouZao { public GouZao(){ System.out.println("这是一个构造方法"); } } public class Test { public static void main(String[] args) { GouZao gouzao = new GouZao(); } }
输出:
编辑
以上代码,我在GouZao类里面创建了一个构造方法。我们可以发现这个构造方法没有返回值,参数列表什么都没有,这就是一个构造方法。 这个构造方法在我们的main方法中直接实例化GouZao类的时候,就已经被调用了。而且只能被调用一次。
5.2.1构造方法概念
上面我们简单认识了构造方法的创建,构造方法(也称为构造器)是一个特殊的成员方法,名字必须与类名相同,在创建对象时,由编译器自动调用,并且在整个对象的生命周期内只调用一次。
class Date { public int year; public int month; public int day; public Date(int year,int month,int day) { this.year = year; this.month = month; this.day = day; } public void show() { System.out.println(year+"年"+month+"月"+day+"日"); } } public class Test { public static void main(String[] args) { Date date = new Date(2023,3,11); date.show(); } }
输出:
编辑
以上代码,还是使用一个日期类来给大家展示。我们可以看到Date类里面创建了一个带有参数的构造方法和一个普通show方法。我们直接在main方法里面创建一个对象也就是实例化Date类,并且往这个对象里面塞入三个int类型参数传给构造方法,使得这个对象里面有值了。并调用show方法来输出刚刚塞入的三个int类型值,这就是带有参数的构造方法使用方式。不带参数的在上方5.2中已经演示过了在此就不再重复讲解。
注意:构造方法的作用就是对对象中的成员进行初始化,并不负责给对象开辟内存空间。
5.2.2构造方法特性
- 特性1,构造方法只能在一个类中创建,构造方法名必须与类名相同
- 特性2,构造方法没有返回值类型,设置为void也不行
- 特性3,如果用户没有显式定义,编译器会生成一份默认的构造方法,且默认的构造方法一定是无参的
- 特性4,构造方法可以重载(用户根据自己的需求提供不同参数的构造方法)
- 特性5,创建对象时给定对象中参数,对象中的参数会自动跳入到构造方法中。并且编译器也会自动打印构造方法。
- 特性6,构造方法中,可以使用this调用其他构造方法来简化代码
- 特性7,创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次(相当于人的出生,每个人只能出生一次)
以上6个特性中1-3的特性我已经在上面给大家展示到了,但为了更好的让大家理解,我再次展示一遍。
特性1、特性2
class Dog { public Dog() { } }
以上代码中,Dog类中的就是一个构造方法。满足了特性1的所有特征,构造方法名与类名相同,并且满足特性2中无返回值。
特性3
class Dog { public Dog() { System.out.println("自动调用了"); } } public class Test { public static void main(String[] args) { Dog dog = new Dog(); } }
运行后输出:
编辑
以上代码,我们发现。在main方法中创建一个dog对象后,编译器自动调用了构造方法,从而输出了“自动调用了”。当然构造方法里面什么都没有的话,编译器运行时也调用只不过什么都没有打印出来。
特性4、特性7
class Dog { public String name; public int age; public String hobby; public Dog() { System.out.println("这是一个构造方法"); } public Dog(String name,int age,String hobby) { System.out.println(name+age+"岁了,爱好是"+hobby); } } public class Test { public static void main(String[] args) { Dog dog = new Dog("旺财",2,"睡觉"); } }
运行后输出:
编辑
以上代码中,在Dog类里我创建了两个构造方法,一个为无参一个未为有参的。当然有参的构造方法比无参的优先级更高,那是因为我在main方法中创建对象时已经把参数传给构造方法里了。而且我们可以看到最后程序输出的只有一个构造方法,印证了特性7中只调用一次构造方法。
特性5
class Dog { public String name; public int age; public String hobby; public Dog(String name,int age,String hobby) { System.out.println(name+age+"岁了,爱好是"+hobby); } } public class Test { public static void main(String[] args) { Dog dog = new Dog("旺财",3,"睡觉"); } }
运行输出:
编辑
以上代码满足了特性5中,创建对象时给定对象中参数,对象中的参数会自动跳入到构造方法中。并且编译器也会自动打印构造方法。
编辑
当你把构造方法中参数给定义了又没有在创建对象时候给定参数时,这时候编译器会报错。要求你给定相应参数。
特性6
class Dog { public String name; public int age; public String hobby; public Dog() { this("赛虎",4,"吃饭"); System.out.println("这是一个无参的构造方法"); } public Dog(String name,int age,String hobby) { System.out.println(name+age+"岁了,爱好是"+hobby); } } public class Test { public static void main(String[] args) { Dog dog = new Dog(); } }
运行后输出:
编辑
以上代码就是构造方法中使用this调用其他构造方法,我们知道main方法中创建对象的时候没有给参数,那么编译器会调用无参的构造方法,创建对象时给定了参数则调用有参的构造方法。但是如果我想要调用有参的构造方法却不在创建对象的时候给定参数,那怎么办呢?就使用this来给有参的构造方法赋值,因此在程序运行后输出了无参和有参的构造方法。
5.3对象的默认值
在5.1中我提到的第二个问题:为什么局部变量在使用时必须要初始化,而成员变量可以不用呢?
class Snake { public boolean name; public int age; public void show() { System.out.println(name+" "+age); } } public class Test { public static void main(String[] args) { Snake S1 = new Snake(); S1.show(); } }
运行后输出:
编辑
编辑
因为类在创建后,里面的成员变量根据自己的类型都有了默认值。那么这些默认值是根据数据类型来分配的,如下表所示:
数据类型 | 默认值 |
byte | 0 |
char | '\u0000' |
short | 0 |
int | 0 |
long | 0L |
boolean | false |
float | 0.0f |
double | 0.0 |
reference(引用) | null |
那么我们在创建一个类里面的成员变量或者实例化一个类的时候,虽然只是简单的几行代码,但JVM想要做很多事情,如:
- 检测对象对应的类是否加载了,如果没有加载则加载
- 为对象分配内存空间
- 处理并发安全问题,如多个线程同时申请对象,JVM要保证给对象分配的空间不冲突
- 初始化所分配的空间,也就是默认的初始值如上述表格一样
5.4就地初始化
就地初始化就比较简单了,就是在创建类里面的成员变量时直接给初始化上,如以下代码:
class Dog { public String name = "小米"; public int age = 3; public String hobby = "睡觉"; public Dog() { System.out.println("这是一个无参的构造方法"); } public Dog(String name,int age,String hobby) { System.out.println(this.name+"今年"+this.age+"岁,爱好是"+this.hobby); } } public class Test { public static void main(String[] args) { Dog dog1 = new Dog(); Dog dog2 = new Dog("赛虎",3,"跑步"); } }
运行后输出:
编辑
编辑
那么本期博客就到这里结束了,感谢你的阅读。如有问题欢迎在评论区指出,如有疑问也可在评论区提出。
编辑
下期预告:封装