11、面向对象(中级)
一、包
1、三个作用
- 区分相同名字的类
- 当类很多时,可以很好地管理
- 控制访问范围
2、基本语法
package 包名;
(1)包的本质是创建不同的文件夹来保存类文件
(2)包的创建:new——》package——》com.包名
(3)包的命名:只能包含数字,字母,下划线,小圆点,不能以数字开头,不能用关键字和保留字
(4)命名规范:小写字母 + 小圆点com.公司名.项目名.业务模块名
3、常用包
(1)java.lang.___
Java基本包
(2)java.util.___
系统工具包
(3)java.net.___
网络开发包
(4)java.awt.___
界面开发包、GUI
4、如何引入包
(1)引入所有包:import java.util.*
(2)引入特定包:import java.util.Scanner
5、包的使用细节
(1)package的作用是声明当前类所属的包,需要放在该类文件的最上面,最多有一个package语句
(2)import指令位置在package之下,在类定义之前,可以有多个且无序
6、访问修饰符
用于控制方法、属性的访问权限
访问级别 | 访问修饰控制符 | 同类 | 同包 | 子类 | 不同包 |
---|---|---|---|---|---|
公开 | public:对外公开 | ✔ | ✔ | ✔ | ✔ |
受保护 | protected:对子类和同一个包中的类公开 | ✔ | ✔ | ✔ | ❌ |
默认 | 无 | ✔ | ✔ | ❌ | ❌ |
私有 | private:不对外公开,类内部使用 | ✔ | ❌ | ❌ | ❌ |
(1)修饰符可以用来修饰类中的属性,成员方法,以及类
(2)只有默认和public才能修饰类
(3)成员方法于属性访问规则一致
二、面向对象三大特征
1、封装(encapsulation)
(1)把抽象的数据(属性)和对数据的操作(方法)封装在一起,数据保护在内部,程序的其他部分只有被授权的操作(方法)才能执行
作用:
- 隐藏实现细节
- 可以对数据进行验证,保证安全合理
(2)封装实现步骤(三步)
- 第一步:将属性私有化
private
,使外部不能修改属性
private String name;
- 第二步:提供一个公共的(
public
)set方法,对属性判断并赋值
public void setName(类型 参数名) {
//加入验证逻辑
属性 = 参数名;
}
- 第三步:提供一个公共的(
public
)get方法,用于获取属性的值
public String getName() {
//权限判断
return name;
}
(3)构造器于set方法的融合使用
在封装的情况下,使用构造器将跳过构造器直接对属性进行改变,因此需要将set方法在构造器中调用
public Person(String name) {
setName(name);
}
2、继承(extends)
利用特定对象之间的共有属性,解决代码的复用
(1)父类和子类
当多个类存在相同的属性和方法时,可以从中抽象出父类,在父类中定义这些相同的属性和方法,子类则不需要重新定义这些方法和属性
- 父类也称为基类、超类(super类)
- 图形类和三角形类为父类和子类的关系,三角形类和等边三角形类之间为父类和子类的关系
(2)语法
class 子类 extends 父类 {
}
(3)细节
- 子类继承了所有的属性和方法,但私有属性和方法不能在子类直接访问,要通过父类提供的公共方法去访问
- 子类必须调用父类的构造器,完成父类的初始化
super();//默认调用父类的无参构造器
- 当创建子类对象时,不管使用哪类构造器都会调用父类的无参构造器,若父类没有提供无参构造器 ,若父类没有提供无参构造器,则必须在子类中用super去指定使用父类中的特定构造器完成父类的初始化,否则编译不通过
super("name", age);
- 若需指定构造器,须调用
super(实参列表)
super();
应位于构造器代码块的第一行- super关键字只能在构造器中使用
this
和super
不能共存- Java所有类都是Object类的子类
- 父类构造器的调用不限于直接父类,将向上追溯至Object类(顶级父类)
- 子类最多直接继承一个父类(单继承机制)
- 不能滥用继承,子类和父类之间必须满足is-a的关系,即子类是一个父类的逻辑关系
//例子
class A {
A() {
System.out.prinln("a");
}
A(String name) {
System.out.println("a name");
}
}
class B extends A {
B() {
this("abc");
System.out.println("b");
}
B(String name) {
System.out.println("b name");
}
}
问:B b = new B();输出什么?
结果:
a
b name
b
分析:
(1)B b = new B();——建立一个引用b,并指向一个B类对象,同时调用B类的无参构造器
(2)执行B类无参构造器中的this("abc");——》有this,则无super,则调用本类的有参构造器B(String name)
(3)执行子类构造器时,若无指定,默认super();调用父类的无参构造器A()
(4)A()——》输出——a——》返回至 B(String name)
(5)输出——b name ——》返回至 B()
(6)输出——b
(4)继承的本质
当子类对象创建好后,建立查找关系
类的属性存储于堆中,各自为独立空间——在执行过程中,会将所有相关的父类,子类加载至方法区
(5)总结
- 注意几个要素
- 父子关系
- 属性
- 构造器
- 封装
- 封装和构造器的融合
- 方法
- 子类实例化时,构造器的使用,即super的使用
- 子类实例化时,实参列表的使用,需要结合父类和子类的构造器传参
- 父类构造器完成父类的初始化,子类完成子类的初始化
3、super的使用
(1)super代表父类的引用,用于访问父类的属性、方法、构造器
- 访问构造器的非私有属性
super.属性
- 访问父类非私有方法
super.方法名(实参列表)
- 在子类构造器中访问父类构造器
super(实参列表)
——只有一句且放在第一行
(2)super的好处
- 调用父类构造器,使分工明确,父类属性由父类初始化,子类属性由子类初始化
- 当子类与父类中成员(属性和方法)重名时,为了访问父类成员,必须通过super,否则会输出子类对应的属性。若无重名h,
super
,this
和直接访问的效果一致 - super的访问不限于直接父类,使用super访问遵循就近原则
属性和方法的调用
默认情况下,是从子类到父类寻找相关属性和方法,子类若有该属性或方法,且可调用,则调用子类的属性和方法,若没有,则向上回溯至父类寻找同名的属性和方法,若有但不可调用,则会报错
(3)super和this的比较
区别点 | this | super |
---|---|---|
访问属性(方法) | 访问本类中的属性,若无... | 访问父类中的属性 |
调用构造器 | 调用本类构造器 | 调用父类构造器 |
特殊 | 表示当前对象 | 子类中访问父类对象 |
4、方法重写/覆盖(overload)
(1)概念:若子类有一个方法,父类的某个方法的名称、返回类型、参数一样,则称子类的这个方法覆盖了父类的方法,也称方法重写了
(2)注意事项
- 子类方法名和参数要和父类完全一样(因为调用方法时就依据方法名,因为重载,也会依据参数列表)
- 返回类型:子类方法与父类方法一样,或是父类返回类型的子类(返回类型精度:子类 小于等于 父类)
- 子类方法不能缩小父类方法的访问权限(访问权限:子类 大于等于 父类)
5、多态(poly)
方法和对象具有多种形态,多态建立在封装和继承的基础上,多态提高代码复用性,有利于代码维护
5.1多态的具体表现
(1)方法的多态
- 方法重载的多态(本类)
- 方法重写的多态(父子类)
(2)对象的多态
- 一个对象的编译类型和运行类型可以不一致
//父类和子类
class Animal {}
class Dog extends Animal {}
Animal animal = new Dog();
//一个父类的引用可以指向一个子类的对象(的地址)
//上面语句中,animal的编译类型为Animal父类,运行类型为Dog子类
//一个名为animal的Animal类引用,指向了其子类Dog类的一个对象的地址——向上转型
//语义上:一个子类一定可以称为是一个父类
//内存上:只是地址的引用,其对象实际上还是子类
5.2 注意事项
-
本质:父类的一个引用指向了子类的一个对象
-
语法:
父类类型 引用名 = new 子类类型();
-
编译类型看左边,运行类型看右边
调用成员变量看编译类型的属性,调用成员方法看运行类型的方法
在运行时,实际上参与运行的还是父类引用所指向的子类对象
-
可以调用父类中的所有成员(在访问权限的限制下)
-
不能调用子类中特有的成员属性和方法
-
最终执行效果看运行类型的具体实现
5.3 多态的向下转型
多态向下转型的基础在于已经向上转型,即父类的引用已经指向了子类的一个对象,此时,可以向下转型,将父类的引用换成子类的引用指向子类的对象
- 语法:
子类类型 新引用名 = (子类类型) 父类引用
- 只能强制父类的引用,不能强制父类的对象
- 当前父类的引用必须指向当前目标类型的对象
- 可以调用子类类型中的所有成员
5.4 子类的重写问题
(1)属性没有重写之说,属性的值看编译类型,编译类型确定了继承中向上回溯的起点
(2)instanceOf
:比较操作符——用于判断对象的运行类型是否为XX类型或者是XX类型的子类型
aa instanceOf XX;
5.5动态绑定机制
(1)属性在哪里声明,就在哪里使用
(2)方法的动态绑定机制
当调用对象方法时,该方法和该对象的内存地址/运行类型绑定,即在运行时,实际上参与运行的还是父类引用所指向的子类对象
5.6 多态的应用
- 多态数组
概念:数组的定义类型为父类类型,保存的实际元素类型为子类类型
- 多态参数
概念:方法定义的形参类型为父类,实参类型允许为子类
6、“==”和equals
方法
(1)“==”是一个比较运算符
- 既可以判断基本类型,有可以判断引用类型
- 判断基本类型时,判断值是否相等
- 判断引用类型时,判断地址是否相等,即判断是否为同一对象
(2)equals
方法
equals
方法是Object类中的方法,只能判断引用类型- 默认判断地址是否相等,子类一般重写了该方法,用于判断内容是否相等
7、hashCode()
方法
六个结论
-
提高具有哈希结构的容器的效率
-
两个引用,若指向同一个对象,则哈希值是一样的
-
两个引用,若指向不同对象,则哈希值是不一样的
-
哈希值主要根据地址号写的,但是不能完全将哈希值等价于地址
-
(集合中讲)
8、toString()
方法
- 默认返回:全类名 + @ + 哈希值的十六进制
- 子类往往重写toString方法,用于返回对象的属性信息——使用
alt
+insert
即可重写 - 当直接输出对象时,toString方法被默认调用
System.out.println(person);
等价于==>
person.toString();
9、finalize
方法
(1)当对象被回收时,系统自动调用该对象的finalize方法,子类可以重写该方法,做一些释放资源的动作
(2)回收机制:当某个对象没有任何引用时,则JVM认为此对象为垃圾对象,会使用垃圾回收机制来销毁该对象,销毁前,会调用finalize方法
(3)垃圾回收机制的调用,由系统来决定(系统有自己的GC算法),也可以通过System.gc()主动触发
注:实际开发中几乎不会运用