类和对象
1.面向对象的初步认识
(1)什么是面向对象
Java是一门纯面向对象的语言,在面向对象的世界里,一切皆为对象。面向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情。
(2)面向对象与面向过程
在之前学习的C语言就是一门面向过程的语言,下面我举个例子来描述面向对象和面向过程两者之间的区别:
以把大象放进冰箱为例:
- 面向对象:以面向对象的思想来解决,一共需要三步: 打开冰箱->把大象放进去->关上冰箱 。至于如何将大象放进去就不是我们需要考虑的事情。
- 面向过程:以面向过程的思想来解决: 打开冰箱->发现大象放不进去->设计解决方法->把大象放进去->关上冰箱。
2.类的定义和使用
面向对象程序设计关注的是对象,而对象是现实生活中的实体,比如:洗衣机。但是计算机并不认识洗衣机,所以需要开发人员告诉计算机什么是洗衣机,这就有了类的概念。
类是用来对 对象进行描述的。当我们创建一个洗衣机类,并将洗衣机的品牌、尺寸、型号、规格...等等一系列的信息添加到洗衣机类中,计算机便可以识别该洗衣机了。
(1)类的定义格式
在Java中定义类时需要用到class关键字,具体语法如下:
//创建类 class ClassName(类名必须大驼峰){ field; //字段属性或者成员变量 method; //行为或成员方法 }
类中包含内容称为类的成员。属性主要是用来描述类的,称之为类的成员属性或者类的成员变量。方法主要说明类具有哪些功能,称为类的成员方法。
而又根据是否又static关键字修饰,分为:
静态成员变量与静态成员方法又称之为类变量和类方法,无需通过实例化对象调用,而是可以直接使用类名进行调用
- 静态成员变量
- 成员变量
- 静态成员方法
- 成员方法
定义类的注意事项:
- 类名必须采用大驼峰的形式
- 成员前写法统一为 public
定义一个类示例:
3.类的实例化
(1)什么是实例化
定义了一个类,就相当于在计算机中定义了一种新的类型,与int,double类似,只不过int和double是java语言自带的内置类型,而类是用户自定义了一个新的类型。比如上述的:Pig类。它们都是类(一种新定义的类型)有了这些自定义的类型之后,就可以使用这些类来定义实例(或者称为对象)。
我们就可将类看作一个类型,通过该类型创建的变量就叫对象。
用类类型创建对象的过程,称为类的实例化,在java中采用new关键字,配合类名来实例化对象
实例化格式:
//假设类名为Pig Pig 实例化的变量名 = new Pig();
例如:我对前文中Pig类创建一个实例:
上图中,我通过Pig类创建了一个实例pig,那么我这个pig就具有Pig类中所有的成员,我可以修改其成员变量来表示我这个实例的特征,也可以调用其成员方法来表示我这个示例的动作等。
注意事项:
- new关键字用于创建一个对象的实例
- 使用.来访问对象中的属性和方法
- 同一个类可以实例多个对象
(2)类和对象的说明
- 类可以理解为一个模型,用来对一个实体进行描述,限定类有哪些成员
- 类是一个自定义类型,可以用来创建变量,创建变量的过程称之为类的实例化
- 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
- 可以将类理解为一个大的概念,如:人。在类中需要描述人的特征,这就是成员变量;还需要描述人的动作,这就是成员方法。当我们创建好一个类时,此时还是一个模型,我们使用类创建一个变量,对该变量的信息进行填充,这样他就可以描述一个具体的人。
(3)创建匿名对象
在Java中,我们可以实例化对象,但是不对其引用,使用完后由于该对象没有被引用,系统会将其自动释放,故为一次性的。此类对象称之为匿名对象。
创建匿名对象:
4.this引用
this引用指向当前对象(成员方法运行时调用该成员方法的对象),在成员方法中所有成员变量的操作,都是通过该引用去访问的。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
(1)this的作用
当我们在方法变量前添加this引用时,编译器便识别出了。
Java代码编写硬性要求:在类中,在所有成员方法、成员变量前都必须添加this引用(不加可能会错,加了一定不会错!)
(2)this引用的特性
- this的类型:对应类类型引用,即哪个对象调用就是哪个对象的引用对象
- this只能在“成员方法”中使用
- 在“成员方法”中,this只能引用当前对象,不能引用其他对象
- this是“成员方法”第一个隐藏的参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法对象的引用传递给该成员方法,this负责来接收
5.对象的构造及初始化
在前面的学习中我们已经知道,在Java方法内定义一个局部变量的时候,必须要初始化,否则会编译失败。
但是在类中我们定义的成员变量在未初始化的情况下也没有发生编译错误,这是为什么呢?这就要引出我们构造方法的概念了。
一:构造方法初始化
构造方法
(1)概念
构造方法(也称之为构造器)是一个特殊的成员方法,名字必须与类名相同,在创建对象时,由编译器自动调用,并且在整个对象生命周期内只调用一次。
(2)用例
构造方法格式:
//假设类名为Date class Date { public int year; public int month; public int day; //构造方法: //构造方法,方法名与类名相同,并且不写返回值,一般使用public修饰 //构造方法的作用就是对对象中的成员进行初始化,并不开辟空间 public Date (int year, int month, int day) { this.year = year; this.month = month; this.day = day; } //构造方法支持重载 public Date () { //语句 } }
特性:
- 方法必须和类名相同
- 没有返回值类型,void类型也不行
- 创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次(相当于人的出生,每个人只能出生一次)
- 构造方法可以重载(用户根据自己的需求提供不同参数的构造方法)
在一个类中可以存在多个构造方法,并且在构造方法中可以调用其它的构造方法,如下示例:
在构造方法内调用其它的构造方法:
(该代码必须是该方法内的第一段有效代码;
this();调用其他构造方法,该写法只能存在于构造方法当中)
2))若一个类中存在多个构造方法,则在实例化一个对象时可以通过传参来指定所需的构造方法,如下:
//以Date类为例 //构造方法都是在实例化对象时调用 Date date = new Date(通过传参来指定构造方法);
注意事项:
- 在类中不存在构造方法时,编译器会默认生成一个不带参数的构造方法。(一旦用户定义,编译器则不再生成。)
- 构造方法不能自己调用自己
- 构造方法不能相互调用形成闭环
二:就地初始化
在声明成员变量时,直接给出初始值就被称之为就地初始化。一般就地初始化也叫默认值,如下:
三:默认初始化
原因在于 new 关键字,new关键字编译后需要做以下事情:
- 检测对象对应的类是否加载了,如果没有加载则加载
- 为对象分配内存空间
- 处理并发安全问题
- 初始化所分配的空间
6.封装
面向对象程序(OOP)三大特性:封装、继承、多态。在类和对象阶段,主要研究的就是封装特性。何为封装?简单来说就是套壳屏蔽细节。
(1)封装拓展之包
1))包的概念
在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组,称为软件包。有点类似于目录。比如:为了更好的管理电脑中的歌曲,一种好的方式就是将相同属性的歌曲放在相同文件下,也可以对某个文件夹下的音乐进行更详细的分类。
在Java中也引入了包,包是对类、接口等封装机制的体现。包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可。
包就是一个文件夹,而同一个包的类都存放在同一个文件夹下。
2))导包
可以看到比较麻烦,所以我们可以使用 import 语句进行导包,如下:
import java.util.Scanner;
public class Test{
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
}
}
该导包方式是使用 替换符 * 来代替某个具体的类名,该写法即是相当于导入该包中的所有类。但是仅作了解,因为当出现相同的类名时会泵机。
同时需要注意的是:Java中的import 与 c++中的 #include不同,#include将该头文件的所有内容直接拷贝到当前文件,而import只会使用哪个就拿哪个。
3))自定义包
基本规则:
- 在文件的最上方加上一个 package 语句指定该代码在哪个包中。
- 包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如 com.baidu.www)
- 包名要和代码路径相匹配. 例如创建 com.bit.demo1 的包, 那么会存在一个对应的路径 com/bit/demo1 来存储代码
- 如果一个类没有 package 语句, 则该类被放到一个默认包中
- java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入 ;
- java.lang.reflect:java 反射编程包;
- java.net:进行网络编程开发包;
- java.sql:进行数据库开发的支持包;
- java.util:是java提供的工具程序包。(集合类等) 非常重要;
- java.io:I/O编程开发包。
(2)访问修饰限定符
Java中主要通过类和对象的访问权限实现封装:类可以将数据以及封装数据的方法结合在一起,而访问权限用来控制方法或者字段是否能直接在类外使用。
1)default
当我们在类中创建成员变量或者成员方法时,若是不对其添加任何的访问修饰限定符,编译器就会将其权限默认为default。
权限:default限制的权限是在同一个包中,不同类可以访问。
2)public
权限:在工程中任意地方都可以直接访问。
3)protected
权限:在同一包可以任意访问;在不同包中,仅有继承了其父类的子类能够访问
4)private
权限:仅限在当前类中访问
(3)static成员
只有成员才能被static修饰!
被static修饰的成员一般称之为静态成员,静态成员变量/方法不依赖于对象,不需要通过对象访问,只需要通过类名访问,因此静态成员变量/方法一般称之为类变量/方法。
1))static修饰成员变量
static修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的。
静态成员变量特性:
- 不属于某个具体的对象,是类的属性,是所有对象共享的,不存储在某一个对象的空间中
- 既可以通过对象访问,也可以通过类名访问,但一般更加推荐使用类名访问
- 类变量存储在方法区中
- 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)
2))static修饰成员方法
Java中,被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。静态成员一般是通过静态方法来访问的。
静态成员方法特性:
- 不属于某个具体的对象,是类方法
- 可以通过对象调用,也可以通过类名.静态方法名(...)方式调用
- 不能在静态方法中访问任何非静态成员变量 (静态方法中不能调用非静态的成员变量或者非静态的成员方法)
3))static成员变量的初始化
静态成员的初始化分为两种: 就地初始化 和 静态代码块初始化
4)代码块
使用 {} 定义的一段代码称为代码块。根据代码块定义的位置以及关键字,又可分为以下四种:
- 普通代码块
- 构造块
- 静态块
- 同步代码块
7.继承
(1)继承实例引入:
假设我们需要创建两个类:猫 和 狗,代码如下:
如上,我创建了两个类,分别是猫类和狗类,在这两个类中我们可以发现,这两个小动物之间都有姓名、年龄、吃饭这几个特点。
我将这里这两个类中相同的特点抽取出来,单独创建一个新的类Animal,再通过extends关键字将Animal类继承给Dog和Cat这两个类,同样也实现了这两个类的创建。
这就是继承,专门用来进行共性抽取,实现代码复用。
(2)继承概念:
继承机制:是面对对象程序设计使代码可以复用的最主要最重要的手段,它允许程序员在保持原有类特性的基础上进行拓展,增加新功能这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用。
在继承中,其被继承的类称之为父类,继承的类称之为子类。如上示例,Animal就是Cat和Dog的父类,Cat和Dog就是Animal的子类。
当然其还有更多叫法,如下图:
3)继承的语法
- 子类会将父类中的成员变量或者成员方法继承到子类中
- 子类继承父类之后,必须要新添加自己特有的成员,体现出与父类的不同,否则就没有必要继承了
4)父类成员的访问
子类中访问父类的成员变量
1)子类和父类不存在同名成员变量
当不存在同名成员变量时,直接访问从父类继承下来的成员变量。
2)子类和父类存在同名成员变量
如果子类和父类存在同名的成员变量,则优先访问子类的成员变量。
总结:
- 如果访问的成员变量子类中有,则优先访问自己的成员变量
- 如果访问的成员变量子类中没有,则访问父类继承下来的,如果父类中也没有定义,则编译报错