前言
最近刚刚学完Java中的类和对象相关的知识点,因为涉及到细节知识点比较多,所以给自己总结一下以方便后期复习,大家有什么想要补充的可以评论区留言哦~
目录
1. 认识面向对象
1.1 面向对象的概念
面向对象是解决问题的一种思想,主要依靠对象之间的交互完成某件事情。比如我去食堂打饭,那么我,食堂,饭,就是涉及到的对象,也就是我们三个对象之前的交互,完成我去食堂打饭这件事情。
1.2 面向对象和面向过程(示例:买手机)
1.2.1 面向过程图例及解释
你买手机的每一个具体步骤都需要你自己来完成,每个人买手机的方式都各不相同,南方人和北方人因为所处地域不一样,手机店的位置就不同;每个手机店卖的手机品牌也不同。只要哪个环节不一样代码就会很复杂,而且如果你买的不是手机而是电脑,那就得重新实现了。这样代码的复用性就不高了,就会不便于扩展和维护。
1.2.2 面向对象图例及解释
这件事情涉及到三个对象:人,自己的熟人,手机。是由我们三个之间的交互去处理买手机这个问题。
注:面向过程和面向对象一样都是解决问题的方法,没有好坏之分,各有自己的用处。
2. 类定义和使用
面向对象,对象可不是你的另一半哈哈哈哈哈哈哈,而是现实生活中的实体哦~比如你这个人,手机,猫等等。但是计算机怎么认识对象呢?所以我们得将对象抽象后,采用面向对象的Java语言(面向对象的语言不一定只有Java)来进行描述对象,方便计算机识别。
2.1 类是什么?
怎么描述一个对象呢?Java中采用类来描述一个对象。一般从两个方面描述一个对象,一是对象的属性(美女的五官身高体重,动物的毛色胖瘦等等),另一个是对象的行为或功能(美女能吃饭睡觉打代码,电视能开机关机切换频道等等)。
具体示例(美女):
属性:头发,脸型,身高,体重.......
行为:吃饭,睡觉,打代码,弹钢琴......
2.2 类的定义格式
需用到class关键字,className为类名,{}中为类的主体
//注意:类名注意采用大驼峰定义
class ClassName{
//字段,属性或成员变量
field;
//行为或成员方法
method;
}
2.3 类定义示例
学生类定义:
class Student{
public String name;//学生姓名
public String gender;//性别
public short age;//年龄
public double score;//考试成绩
//吃饭
public void eat(){
System.out.println(name+"在吃饭");
}
//考试
public void exam(){
System.out.println(name+"在考试");
}
}
注意:1.一般一个.java文件只定义一个类
2.main方法所在的类一般要用public修饰(Eclipse默认会在public修饰的类中找main方法)
3.public修饰的类必须要和文件名相同
4.不要轻易去修改public修饰的类的名称
3. 类的实例化
3.1 实例化概念
类定义后相当于一个房子的搭建图纸,在计算机中相对于定义了一种新的类型(自定义类型)。int,float...类型为Java的内置类型。当我们定义了学生类之后,我们就可以使用学生类来定义一个对象了。而使用某个类来创建某对象的过程,就叫类的实例化(采用new关键字)。
实例化一个学生对象:
//学生的实例化
Student stu = new Student();
stu.name = "张三"; //使用.来访问对象中的属性
stu.age = 21;
stu.gender = "男";
stu.score = 97.5f;
stu.exam(); //使用.来访问对象中的方法
System.out.println("姓名:"+stu.name+" 年龄:"+stu.age+" 性别:"+stu.gender+" 成绩:"+stu.score);
运行效果:
注:同一个类可以创建多个实例(美女可以有多个,嘿嘿)
3.2 类和对象的说明
1) 类就是一个模型(房屋设计图),用来对一个实体进行描述,限定类中可以有哪些成员。
2) 类也是一种自定义类型,可以用来定义变量。
3) 一个类可以实例化多个对象(用房屋设计图建造的房子),只有实例化出的对象占用实际的物理空间去存储数据。
4. this引用
4.1 this引用的作用
有以下两种情况:
1.成员方法的形参名不小心与成员变量名相同。
2.当多个对象调用类中的成员方法时,是如何区分是哪个对象调用的呢?
具体示例:
情况一:成员方法形参名和成员变量名相同。
//修改学生类
class Student{
public String name;//学生姓名
public String gender;//性别
public short age;//年龄
public double score;//考试成绩
//成员方法的形参名称与成员变量名相同
public void setStudent(String name,String gender,short age,double score){
name = name;
gender = gender;
age = age;
score = score;
}
//吃饭
public void eat(){
System.out.println(name+"在吃饭");
}
//考试
public void exam(){
System.out.println(name+"在考试");
}
}
在成员方法中,因为局部变量优先原则,如果不用this引用指向当前对象的成员变量,就会造成形参自己给自己赋值,而对应成员变量无法被赋值。所以要将setStudent()成员方法修改如下:
//成员方法的形参名称与成员变量名相同需要用this去指向当前对象之后用.去访问其成员变量
public void setStudent(String name,String gender,short age,double score){
this.name = name;
this.gender = gender;
this.age = age;
this.score = score;
}
这样修改的就是this指定对象的成员变量的值了。(后面会单独给大家讲解this在哪里和是如何指向当前对象)。
如果大家还有些不懂,我们可以试试调试(打断点-->右击选择Debug),首先实例化一个学生对象,在不用this引用指向当前对象情况下去看看该学生对象的成员变量值是否被修改:
由图可知,stu对象的成员变量未能被赋值。我们再试试看加了this引用之后stu对象的成员变量的值是否被修改:
大家可以看见stu对象的成员变量已经被成功赋值啦!我们继续冲冲冲!
情况二:有两个学生对象,该如何确定是对哪个对象的成员变量进行赋值呢?
//学生的实例化
Student stu = new Student();
stu.setStudent("张三","男", (short) 21,97.5f);
System.out.println("-------------------------------------");
Student stu2 = new Student();
stu2.setStudent("李四","男",(short)25,88.5f);
我们先来认识一下this引用吧~
4.2 什么是this引用
this引用指向当前对象(哪个对象调用的成员方法,那当前对象就是它),当然在成员方法中对成员变量的所有操作都得用this引用去访问。接下来我们来调试运行一下情况二的代码:
stu的成员方法调用及成员变量赋值:
stu2的成员方法调用及成员变量赋值:
通过调试可以看出this引用指向的当前调用成员方法的对象。当前对象的成员变量可以通过this.去访问并赋值。
4.3 this引用的特性
1. this的类型:对应类类型引用(示例中this就是Student类 类型的引用),哪个对象调用就是哪个对象的引用类型(stu和stu2的引用类型都是Student)。
2.this只能在成员方法中使用。
3.在某个成员方法中,this只能引用当前对象,不能引用其他对象。
4.this是成员方法的第一个隐藏参数,由编译器自动传递,成员方法执行时,编译器会将调用成员方法的对象的引用传递给该成员方法,不管我们写不写this引用参数,this引用都会存在。如示例中的setStudent()成员方法可以这样写:
//this引用一般默认为对应成员方法的第一个参数(可写可不写)
public void setStudent(Student this,String name,String gender,short age,double score){
this.name = name;
this.gender = gender;
this.age = age;
this.score = score;
}
5. 对象的构造及初始化
5.1 如何初始化对象
当在Java方法中定义局部变量并且不给该局部变量初始化的时候,编译会失败。如图:
那我们试试不给成员变量初始化,编译会失败吗?
由图可知,不给成员变量初始化代码不仅不会编译失败,且能够正常输出,那代表成员变量会默认有初始值(后面会给大家分享各种类型成员变量的默认值)。
大家可能发现了,每次我们都只能把对象创建好之后再通过调用某个成员方法对成员变量进行赋值,那我们能不能在对象创建的时候就对成员变量赋值呢?这是可以的!接下来我们进入构造方法的学习啦~
5.2 构造方法
5.2.1 构造方法概念
构造方法是一个特殊的成员方法,名字必须与类名相同,在创建对象时,由编译器自动调用,并且在整个对象的生命周期内只调用一次。代码示例:
//洗衣机类
class WashMachine{
public String brand;//品牌
public String type;//型号
public String weight;//重量
public double length ;//长
public WashMachine(String brand,String type,String weight,double length){
this.brand = brand;
this.type = type;
this.weight = weight;
this.length = length;
}
//洗衣服
public void printWashMachine(){
System.out.println("品牌:"+this.brand+" 型号:"+this.type+" 重量:"+this.weight+" 长度:"+length);
}
}
在main方法中测试一下:
成功初始化好w1的成员变量。
注:构造方法的作用就是对对象中的成员进行初始化,并不负责给对象开辟空间。
5.2.2 构造方法特性
1.名字必须与类名相同。
2.没有返回值,且不能加void。
3.创建对象时由编译器自动调用,并且只会被调用一次(如同人只会出生一次)
4.构造方法可以重载(根据需求定义成员方法的参数),如:
//洗衣机类
class WashMachine{
public String brand;//品牌
public String type;//型号
public String weight;//重量
public double length ;//长
//无参的构造方法
public WashMachine(){
}
//两个参数的构造方法
public WashMachine(String brand,String type){
this.brand = brand;
this.type = type;
}
//四个参数的构造方法
public WashMachine(String brand,String type,String weight,double length){
this.brand = brand;
this.type = type;
this.weight = weight;
this.length = length;
}
//洗衣服
public void printWashMachine(){
System.out.println("品牌:"+this.brand+" 型号:"+this.type+" 重量:"+this.weight+" 长度:"+length);
}
}
三个构造方法名字相同但是参数列表不同,所以构成了方法重载。
5.如果用户没有显示定义,编译器会生成一份默认无参的构造方法(就如示例中第一个构造方法一样,但是不会显示出来),如:
注:不论用户定义了有参还是无参的构造方法,默认的无参的构造方法都不会生成!
6.构造方法中,可以通过this调用其他构造方法来简化代码。如下示例:
class WashMachine{
public String brand;//品牌
public String type;//型号
public String weight;//重量
public double length ;//长
//无参的构造方法
public WashMachine(){
//必须为第一条语句,否则编译失败
this("海尔","滚筒式洗衣机","40公斤",700);
}
//两个参数的构造方法
public WashMachine(String brand,String type){
this.brand = brand;
this.type = type;
}
//四个参数的构造方法
public WashMachine(String brand,String type,String weight,double length){
this.brand = brand;
this.type = type;
this.weight = weight;
this.length = length;
}
}
注: 1. 调用另一个构造方法时,this(...)必须在第一行
2. 不能形成环(两个构造方法互相用this调用对方),如下示例:
7.绝大多数情况下使用public修饰,特殊场景下会被private修饰。
5.3 默认初始化
之前给大家说成员变量会有默认值,其实在创建一个对象时,程序是有很多步骤的,给成员变量设置初始值仅仅只是其中的一步而已。至于到底是哪些步骤大家现在不用深究,对应数据类型的默认值图表如下:
5.4 就地初始化
就地肯定就是直接呗!犹豫就会败北!对待喜欢的人就得就地表白一样的!直接给成员变量赋值,如图:
注:代码编译完成后,编译器会将所有给成员初始化的这些语句添加到各个构造函数中
6. static成员
6.1 以学生类作为示例
学生对象是通过学生的姓名,年龄,分数等等这些属性来区分的,但是当这些学生具有同一属性,比如同一个班的学生是在同一个教室上课,难道我们还要每个对象都增加成员变量吗?这时候就需要用static修饰的成员,来存储所有对象的共有属性。在Java中,被static修饰的成员,称为静态成员(类成员),它不属于某个具体的对象,是所有对象共享的。
6.2 static修饰成员变量
用static修饰的成员变量称为静态成员变量。代码示例如下:
class Student{
//就地初始化
public String name = "张三大Boss";//学生姓名
public String gender = "男";//性别
public short age = 21;//年龄
public double score = 97.5f;//考试成绩
//学生对象共有属性
public static String classRoom = "2栋3301";
//成员方法的形参名称与成员变量名相同需要用this指定当前对象之后用.去使用成员变量
//this引用一般默认为对应成员方法的第一个参数(可写可不写)
public void setStudent(Student this,String name,String gender,short age,double score){
this.name = name;
this.gender = gender;
this.age = age;
this.score = score;
}
}
特性:1. 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中。
如图:
可以看出classRoom静态成员变量并没有存储到某个对象的空间中。
2. 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问(因为它是类成员)。如下:
3. 类变量既不像局部变量存储在栈上,也不像对象存储在堆上,它存储在方法区中。
4. 随类的加载而创建,随类的卸载而销毁。
6.3 static修饰成员方法
如果静态成员变量用private(只能在当前类访问该成员变量)修饰,而成员方法用public(任何地方都能访问)修饰,我们该如何在类外访问该静态成员变量呢?我们可以通过static修饰的成员方法,也就是静态成员方法来访问。示例代码如下:
//private修饰静态成员变量
private static String classRoom = "2栋3301";
//使用静态成员方法访问私人的静态成员变量
public static String getClassRoom() {
return classRoom;
}
在main方法中测试一下:
特性:1. 不属于某个具体的对象,是类方法
2. 可以通过对象调用,也可以类名.静态方法名(...)方式调用,建议通过类名调用
3. 不能在静态方法中访问任何非静态成员变量,如图:
4. 静态方法中不能调用任何非静态方法,因为非静态方法有this参数,在静态方法中调用时候无法传递this引用。如图:
6.4 static成员变量初始化
1. 就地初始化:直接在定义时赋初始值。(上述示例中我就是就地初始化的)
2. 静态代码块初始化,接下来就开始进行代码块知识点的学习。
7. 代码块
7.1 代码块概念及分类
使用{}定义的一段代码称为代码块。根据位置和关键字分为四种:普通代码块,构造代码块,静态代码块,同步代码块(使用synchronized关键字加上一个锁对象来定义一段代码,这就叫同步代码块。在后面写多线程博客会给大家说到)。
7.2 普通代码块
定义在方法中的代码块(这种用法比较少见)。如图
7.3 构造代码块
定义在类中的代码块(没有任何修饰符),别名:实例代码块/非静态代码块。一般用于初始化实例成员变量。如图:
我们在main方法中测试一下:
7.4 静态代码块
使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。
如图:
在main方法中测试:
特性:1. 静态代码块不管生成多少个对象,其只会执行一次
2. 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
3. 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次合并执行。
4.构造代码块只有在创建对象时才会执行。
7.5 普通代码块,构造代码块,静态代码块总结图例
注:执行顺序:静态代码块---->构造代码块---->构造方法
8. 对象的打印
直接用System.out.println(对象名称)会打印出这样的结果:
注:这里的对象地址并不是对象真实的地址,而是由其真实地址计算出来得到的Hash值(唯一),可以先暂时理解为其对象的地址。
如果要默认打印对象的属性可以重写toString方法:
//重写toString方法打印成员变量
@Override
public String toString() {
return "姓名:"+name+" 性别:"+gender+" 年龄:"+age+" 成绩:"+score;
}
在main方法中测试一下: