5.面向对象
Java面向对象的三大特点:封装、继承、多态
5.01面向过程与面向对象
面向对象是基于面向过程的编程思想
面向过程是最基本的解决思路
最终事情要被解决 绝对会面向过程
面向对象编程思想的特点:
- 是一种更符合我们思想习惯的思想
- 可以将复杂的事情简单化
- 将我们从执行者变成了指挥者
怎样才能更符合面向对象思想呢 ?
- 有哪些类?
- 每个类有哪些东西?
- 类与类之间的关系是什么?
面向对象开发: 就是不断地创建对象,使用对象,指挥对象做事情 。
面向对象设计: 其实就是在管理和维护对象之间的关系。
面向对象特征: 封装,继承,多态 。
5.02类与对象
类: 是一系列具有相关属性和行为的事物的集合,是一个抽象的概念 。
对象: 是该类事物的具体表现形式,具体存在的个体。
所以在描述一个事物(定义类)的时候,主要描述其 (属性-成员变量,行为-成员函数)
如何创建对象: 类名 对象名=new 类名();
对象的创建流程
案例1
class Person{
String name;
int age;
String sex;
void eat(){}
void sing(){}
void speak(){}
}
对象的内存图解
5.03成员变量和局部变量的区别
存储位置
- 成员变量 在堆内存中对象的所属空间里
- 局部变量 在栈内存中函数的所属空间里
生命周期
- 成员变量 随着对象的创建而创建 随着对象的消亡而消亡
- 局部变量 随着函数的进栈而存在 随着函数的弹栈而消失
初始化
- 成员变量 有默认初始化 可以直接通过对象调用
- 局部变量 没有默认的初始化之 必须先定义再赋值后才能使用
作用域
- 成员变量 一般而言全局使用
- 局部变量 只能在当前函数中使用
5.04形式参数的分类
数据类型作为参数使用
- 基本数据类型作为形式参数:传值(传址 是常量在常量池中的地址)
- 引用数据类型作为形式参数:传址(对象在堆内存中的地址)
5.05匿名对象
匿名对象:就是没有名字的对象(没有引用的对象),是对象的一种简化表示形式
new A();
匿名对象的两种使用情况
- 对象调用方法仅仅一次的时候
- 作为实际参数传递
5.06封装概述
封装仅仅是一种思想:函数、类、private
封装概述: 是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
好处:
- 隐藏实现细节,提供公共的访问方式
- 提高了代码的复用性
- 提高安全性
封装原则:
- 将不需要对外提供的内容都隐藏起来
- 把属性隐藏,提供公共方法对其访问
5.07构造函数
构造函数作用概述:给对象的数据进行初始化
构造函数格式:
- 函数名必须为类名
- 没有返回值类型,没有返回值
- 没有返回值类型不代表没有return
- 可以有权限修饰符
- 函数有参数列表,所以可以重载
- 没有定义任何构造函数时 会存在一个默认无参且隐藏的构造函数
- 如果一旦定义任何构造函数,则默认的不存在
- 永远将无参构造函数定义出来
- 有了构造函数 还是是否需要set get 看数据今后是否仍需修改
成员变量赋值步骤:
- 隐式初始化 (大家都没有)
- 显式初始化 (大家都一样)
- 针对性初始化 (大家有些不一样)
- 后期修改值
成员变量注意事项:
- 变量什么时候定义为成员变量?如果这个变量是用来描述这个信息的,那么该变量就应该定义为成员变量
- 变量到底定义在哪里?变量的范围越小越好,因为能及时被收回
5.08对象的创建流程
- 创建对象的引用变量
- 创建对象 开辟空间 分配地址
- 对成员变量进行默认初始化(隐式初始化)
- 构造函数进栈 同时显式初始化
- 依据构造函数的内容 进行针对性初始化
- 构造函数弹栈,对象创建完毕,将地址赋予引用变量
5.09构造函数与成员函数的区别
- 构造函数没有返回值 成员函数有返回值
- 构造函数只有在创建对象时使用且执行一次,成员函数是在对象创建之后 任意次数调用
- 成员函数不能调用构造函数
- 构造函数能调用成员函数(封装了构造函数中的代码,一般不会被外界调用)
- 构造函数能调用构造函数。(为了简化代码)
this调用构造函数只能在第一句
对this的调用必须是构造器中的第一个语句
构造函数之间的调用不能产生环路
5.10成员函数划分
返回值
- 有明确返回值
- 返回void类型的函数
形式参数
- 无参函数
- 带参函数
5.11 this关键字
this代表所属类的对象引用,方法被哪个对象调用,this就代表哪个对象
this的使用
- 局部变量和成员变量重名
- 和super使用
5.12private关键字
private关键字是一个权限修饰符,可以修饰成员,被修饰的成员只在本类中才能访问。
private最常见的应用:
- 把成员变量用private修饰
- 提供对应的getXxx()和setXxx()方法
5.13static关键字
可以修饰成员变量和成员方法
static关键字特点:
- 随着类的加载而加载
- 优先于对象存在
- 被类的所有对象共享(这也是我们是否使用静态关键字的判断条件)
- 可以通过类名调用
static关键字注意事项:
- 在静态方法中是没有this关键字的
- 静态方法只能访问静态的成员变量和静态的成员方法
5.14静态变量和成员变量的区别
所属不同
- 静态变量属于类,所以也称为类变量
- 成员变量属于对象,所以也称为实例变量
内存中位置不同
- 静态变量存储于方法区的静态区
- 成员变量存储于堆内存
内存中出现时间不同
- 静态变量随着类的加载加载,随着类的消失而消失
- 成员变量随着对象的创建而存在,随着对象的消失而消失
调用不同
- 静态变量可以通过类名调用,也可以通过对象调用
- 成员变量只能通过对象名调用
5.14继承
继承:传承--------从上而下而下传递
父类和子类
要点提示: 继承使得你可以定义一个通用的类(父类),之后扩充该类为一个更加特定的类(子类)。
当我们在描述某些事物的时候 发现 有些事物具有重复的属性和行为那么就可以将这些重复的东西进行抽取组合成一个新的类别。
我们将这个新的类称为父类,有些事物的类别称之为子类,子类是继承于父类的。
继承: 必须是is-a的关系,同一种族,才能继承。
继承的注意事项:
- 和传统的理解不同,子类并不是父类的一个子集。实际上,一个子类通常比它的父类包含更多的信息和方法。
- 父类中的私有数据域在该类之外是不可访问的。因此,不能在子类中直接使用。但 是,如果父类中定义了公共的访问器 / 修改器,那么可以通过这些公共的访问器 / 修 改器来访问和修改它们。
- 不是所有的 “是一种”(is-a)关系都该用继承来建模。
- 继承是用来为 “是一种” 关系(is-a) 建模的。不要仅仅为了重用方法这个原因而盲 目地扩展一个类。
- 在 Java中是不允许多重继承的。一个 Java类只可能直接 继承自一个父类。这种限制称为单继承(single inheritance)。如果使用extends 关 键字来定义一个子类,它只允许有一个父类。然而,多重继承是可以通过接口来实现的。
警告: 不要为了获得其他类的功能而随意 叫爸爸。
继承的好处: 简化代码(将重复代码进行抽取)。
5.15super关键字
要点提示: 关键字super指代父类,可以用于调用父类中的普通方法和构造方法。
this关键字的作用,他是对调用对象的引用。关键字super是指这个 super关键字所在类的父类。
super使用途径:
- 调用父类的构造方法:super()或者super(parameters)
- 调用父类的方法:super.方法名(参数)
super()调用父类的无参构造函数,super(arguments)调用与参数匹配的父类的构造方法,super()语句必须出现在子类构造 方法第一行,这是显式调用父类构造方法的唯一方式。
警告: :要调用父类构造方法就必须使用关键字 super, 而且这个调用必须是构造方法的第 一条语句。在子类中调用父类构造方法的名字会引起一个语法错误。
方法重写
要点提示: 要重写一个方法,需要在子类中使用和父类一样的参数列表以及一样的返回值类型 来对该方法进行定义。
重写: 子类从父类中继承方法。有时,子类需要修改父类中定义的方法的实现,这称作方法重写。
注意:
- 仅当实例方法是可访问时,它才能被覆盖。因为私有方法在它的类本身以外是不能 访问的,所以它不能被覆盖。如果子类中定义的方法在父类中是私有的,那么这两个方法完全没有关系。
- 与实例方法一样,静态方法也能被继承。但是,静态方法不能被覆盖。如果父类中 定义的静态方法在子类中被重新定义,那么在父类中定义的静态方法将被隐藏。可 以使用语法:父类名 .静态方法名(SuperClassName.staticMethodName) 调用隐藏的 静态方法。
方法重写与重载
要点提示: 重载意味着使用同样的名字但是不同的参数列表来定义多个方法。重写意味着在子 类中提供一个对方法的新的实现。
注意问题:
- 方法重写发生在通过继承而相关的不同类中;方法重载可以发生在同一个类中,也可以发生在由于继承而相关的不同类中。
- 方法重写具有同样的参数列表和返回值类型;方法重载具有同样的名字,但是不同的参数列表。
5.15抽象类
抽象: 模糊不定,不确定,不明白,不清晰。
抽象函数: 从代码上而言 只有函数的声明但是没有函数体;当描述一类事物的时候,发现每个事物都有相同的功能,但是具体执行却不一样,那么在对这些事物进行父类抽取的时候,仅仅保留该功能的声明,而无需写出函数体
此时,这个函数就是抽象函数,该函数的具体执行由子类决定。
抽象类: 拥有抽象函数的类就是抽象类,但是抽象类不一定有抽象函数。用abstract关键字来声明抽象类。
abstract class Living{
public abstract void eat();
}
class Dog extends Living{
public void eat(){
System.out.println("狗吃骨头");
}
}
class Cat extends Living{
public void eat(){
System.out.println("猫吃鱼");
}
}
- 抽象类有构造函数
1)抽象类一般而言是作为父类存在的
2)子类在创建对象时,父类肯定会为其进行一些初始化 - 抽象类不能创建对象
1)因为就算抽象类能创建出对象,但是其函数没有具体的执行,是不确定的
2)因为对象本身是一个确定的东西,所以冲突 - 抽象类和一般类有何区别?
1)唯一的区别就是抽象类可能有抽象函数
2)其余一律与一般函数相同
3)有构造函数、有成员变量、有成员函数、有静态成员 - 抽象函数不能和哪些关键字共存?
1)final修饰函数,表示该函数不能被重写
2)final修饰类,表示类不能被继承
3)private
4)static静态不存在重写的概念,静态必须是一个具体的事物
不管是抽象类,还是一般类,始终都在表示一个种族的问题。(同种体系的问题!)
5.16接口interface
接口(interface): 从代码上而言 就是一种特殊的抽象类。
当一个抽象类中,所有的函数都是抽象的,则可以用接口表示;但是接口中并不一定所有的函数都是抽象函数。
接口的组成:
- 属性(常量)
- 行为(抽象函数或一般函数)
接口的作用:
- 解耦(向外提供一套统一标准)
- 传参(基本数据类型,引用数据类型或一段代码)
- 对事物功能的拓展(你像什么)
要点提示:
- 类与类之间只有单继承的关系(extends)
- 类与接口之间有多实现的关系(implements)
- 接口与接口之间有多继承的关系
1)因为一个子类的父类1与父类2之间 纵使有同名函数 但是有不同函数体 所以导致子类无法确定执行哪个;
2)一个子类的接口1与接口2之间 纵使有同名函数 但是没有执行体 执行体由实现子类自行决定 所以不冲突。
5.17多态
要点提示: 多态意味着父类的变量可以指向子类对象。
多态: 一个事物具有多种形态,在不同的场合不同的地点具有不同的形态,但本质不变。
表示方法: 子类对象指向父类引用。
父类 变量名=new 子类();
成员变量在多态中的特点
- 只能访问父类中的成员变量
成员函数在多态中的特点
- 如果有重写,则调用重写函数
- 如果没有重写函数,则调用父类成员函数
- 如果没有重写,父类也没有,则报错
- 子类的特有不可调用
静态变量在多态中的特点
- 只能访问父类中的静态变量
静态函数在多态中的特点
- 只能访问父类中的静态函数
- 间接证明 静态函数可以继承,但不存在重写
instanceof运算符
判断对象是否属于同类
内部类: 在描述一个事物的时候,发现该事物中又有其他的事物其他的事物叫作内部类,该事物叫外部类。
局部内部类代码案例
匿名实现子类的匿名对象
class InterfaceDemo{
public static void main(String[] args){
Button bt_login=new Button();
//匿名实现子类的匿名对象
bt_login.doSomething(new Task(){
public void doWhat(){
System.out.println("登录....");
}
});
}
}
class Button{
public void doSomething(Task task){
task.doWhat();
}
}
interface Task{
public abstract void doWhat();
}