文章目录
面向对象
一、初识面向对象
1. 思想
-
面向过程思想
- 步骤清晰简单,第一步做什么,第二步做什么…
- 面对过程适合处理一些较为简单的问题
-
面对对象思想
- 物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
- 面向对象适合处理复杂的问题,适合处理需要多人协作的问题!
-
对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。
2. 什么是面向对象
-
面向对象编程(Object-Oriented Programming, OOP)
-
面向对象编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据。
-
抽象
-
三大特性:封装、继承、多态
-
从认识论角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象。
-
从代码运行角度考虑是先有类后有对象。类是对象的模板。
3. 什么是类
-
类:是一类具有相同特性的事物的抽象描述,是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该类事物。
-
属性:就是该事物的状态信息。对应成员变量。
-
行为:就是该事物能够做什么。对应成员方法。
4. 类与对象的关系
- 类是对一类事物的描述,是抽象的。
- 对象是一类事物的实例,是具体的。
- 类是对象的模板,对象是类的实体。
二、对象的创建和使用
1. 创建与初始化对象
- 使用new关键字创建对象
- 语法:
类名 对象名 = new 类名(); //实例化一个对象
- 使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用。
- 对象中存储的是对象地址。每个对象都有一个自己独立的区域(用于存储自身的属性值)。
2. 使用对象
2.1 对象属性的访问
- 赋值:
对象名.属性名 = 值;
- 取值:
对象名.属性名;
三、类的五大成员
1. 成员变量
1.1 分类
-
实例变量:变量属于对象
-
类变量: 变量属于类
1.2 实例变量的声明
[修饰符] class 类名{
[修饰符] 数据类型 属性名; //属性有默认值
[修饰符] 数据类型 属性名 = 值; //属性有初始值
}
1.3 实例变量的使用
-
前提:必须要有对象
-
语法:
直接使用 //在本类的实例方法中
对象名.成员变量名 //在其他类的方法中
1.4 成员变量的特点
- 成员变量有默认值(具体和数据类型相关)
数据类型 | 默认值 | |
---|---|---|
基本类型 | 浮点数(float,double) | 0.0 |
字符(char) | ‘\u0000’ | |
布尔(boolean) | false | |
整数(byte,short,int,long) | 0 | |
引用类型 | 数组,类,接口 | null |
- 实例变量的值是每个对象独立的
1.5 成员变量和局部变量的区别
1)位置不同:
成员变量:直接在类体中
局部变量:某个局部(方法内,if内,循环内…)
2)作用范围不同:
成员变量:本类以及其他类都能使用(要符合访问修饰符)
局部变量:只能在当前大括号内使用
3)初始化值不同:
成员变量:有默认值
局部变量:没有初始化不允许使用
4)内存中位置不同
成员变量:在堆内存
局部变量:在栈内存
5)生命周期不同:
成员变量:对象被回收时,销毁
局部变量:当前大括号运行完毕,销毁
1.6 成员变量类型
- 可以是任意类型(基本数据类型、引用数据类型)
2. 成员方法
- 类:一组具有相同特征(属性),相同行为(方法)的对象的集合,抽象出来的概念
- 属性:通过变量去存储数据
- 方法:一段可以运行的代码(实现某些需求)
2.1 方法的概念
方法也叫函数,是一个独立功能的定义,是一个类中最基本的功能单元。
把一个功能封装为方法,可以实现代码的重用,从而减少代码量。
2.2 方法的原则
方法的使用原则:
(1)必须先声明后使用。
类,变量,方法等都要先声明后使用
(2)不调用不执行,调用一次执行一次。调用一次,在栈中压入一个方法栈。
2.3 成员方法的分类
1)实例方法:属于对象的,必须有对象才可以
2)类方法:属于类
2.4 方法的语法
[修饰符] 返回值类型 方法名([形参列表]){
方法体
}
1)修饰符:可有可无
2)返回值类型
3)方法名:自定义名字(符合变量名的命名规则和规范)
4)形参列表
- 形参:在定义方法时方法名后面括号中的变量名称称为形式参数(简称形参),即形参出现在方法定义中。
- 实参:调用者方法中调用另一个方法时,方法名后面括号中的参数称为实际参数(简称实参),即实参出现在调用者方法中。
5)方法体
方法的声明位置是类体中,方法中不能嵌套方法
2.5 方法的形式
1)无参无返回值
[public] void 方法名(){方法体}
无参类型中 return; //默认存在,写不写都有,在方法的末尾
2)有参无返回值
[public] void 方法名(参数列表){方法体}
参数列表可以是任意类型(基本数据类型,引用数据类型)
参数列表中必须给相应的值(数据类型,个数,顺序)
3)无参有返回值
[public] 返回值类型 方法名(){方法体}
返回值类型要指定且只能指定一个(可以是任意类型)
如果是多个相同类型的数据,可以使用数组
如果是多个不同类型的数据,可以使用集合
有返回值的方法,必须要执行到 return 数据; 这个代码
接收返回值:数据类型 变量名 = 对象.方法();
4)有参有返回值
[public] 返回值类型 方法名(参数列表){方法体}
返回值类型和参数类型没有任何关系
2.6 实例方法的调用
1)在本类内:
直接调用 语法:[this.]本类的方法();
this是当前对象,是当前方法的调用者,可以省略。
2)在其他类中:
必须通过对象去调用
对象名.方法名(参数);
参数可以是任意类型,如果想传一个未被定义声明的数组,(new int[]{1,2,3})
- 总结:
(1)调用时,需要通过方法名来识别调用哪个方法
(2)调用时,需要传“实参”,实参的个数、类型、顺序顺序要与形参列表一一对应
如果方法没有形参,就不需要也不能传实参。
(3)调用时,如果方法有返回值,可以接受或处理返回值结果。
如果方法的返回值类型是void,不需要也不能接收和处理返回值结果。
2.7 可变参数
在JDK1.5之后,如果我们定义一个方法时,此时某个形参的类型可以确定,但是形参的个数不确定,那么我们可以使用可变参数。
- 语法:
修饰符 返回值类型 方法名([非可变参数部分的形参列表,] 参数类型...形参名){ 方法体 }
- 注意:
(1)可变参数就当成数组使用即可(本质就是数组)
(2)可变参数最多只能有一个,且必须是参数列表的最后一个
(3)可变参数的实参都必须符合数据类型
2.8 方法重载
-
方法重载:指在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可,与修饰符和返回值类型无关。
-
方法的要求:在同一个类中,不允许出现两个完全相同的方法
-
重载的要求:
(1)同一个类中,方法名可以相同
(2)参数列表必须不同(个数、顺序、类型,任何一个不同即可)
(3)和返回值、访问修饰符无关
-
重载方法调用的时候,先找完全符合的,找不到完全符合的找兼容的,如果有多个兼容的则报错。
例如:实参为int类型,但是形参中没有int类型但有double类型的,则找double类型的。
- 注意:同类型的数组和可变参数是相同的,无法重载。
2.9 参数的传递机制
-
形参:在方法定义位置,指定的形式参数
-
实参:在调用者方法中,指定的实际参数
-
传递机制:实参给形参赋值
-
基本数据类型,栈内存中存储的是值,在方法传参的时候,就是值传递。
-
引用数据类型,栈内存存储的是地址,在方法传参的时候,就是地址传递(引用),多个变量指向同一个空间(堆内存、常量池)。形参地址的改变不会影响实参,但形参地址值里面的数据改变会影响实参。
- 注意:String、Integer等特殊类型除外
-
在方法中,如果
形参 = 新new对象
,则和实参无关,因为形参指向了新对象,将一个新的地址值赋给了形参,与实参无关系了。
String对象不可变,一旦修改就会产生新对象。
2.10 递归
-
递归:指在当前方法内调用自己的这种现象。
-
递归分类:
1)直接递归:自己调用自己
2)间接递归:例如 A–>B–>C–>A
-
注意:
-
递归一定要有条件限定,保证递归能停止下来,否则就会栈内存溢出。
-
java.lang.StackOverflowError :栈内存溢出错误
-
-
出现宕机:①优化代码;②增加机器
2.11 对象数组
对象数组的用法和普通数组一样
对象数组的默认值是null
对象数组中的元素存的是各个对象的地址
对象类型[] 变量名 = new 对象类型[对象数组的大小];
3.构造器(Constructor)
3.1 概念
- 类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。
3.2 作用
-
要创建一个类的实例对象,必须调用一个对象的构造器,来完成类的实例初始化过程。实例初始化过程就是为实例变量赋初始值的过程。
-
当一个对象被创建时候,构造方法用来初始化该对象,给对象的成员变量赋初始值。
3.3 构造器语法
[修饰符] 构造器名([参数列表]) { 代码块 }
3.4 构造器特点
- 没有返回值(void也没有)
- 方法名必须和类名一致
3.5 构造器调用时机
- 在实例化对象时调用
- 实例化一次调用一次
3.6 注意
- 任何一个类都默认存在一个无参构造器
- 构造器可以重载
- 一旦定义了有参构造,无参就必须显示定义
3.7 this关键字
-
含义:当前对象(不是当前类)
-
this.内容(属性、方法)
- 区分局部变量和成员变量重名的问题
- 适用于所有方法(包括构造器)
- 在Java的封装类中,允许局部变量和成员变量重名
- 因为成员变量和局部变量不在同一内存(成员变量在堆,局部变量在栈)
- 如果成员变量和局部变量重名,默认识别的是局部变量,成员变量用
this.属性名
-
this()
- 应用在构造器
- 能够调用本类其他构造器
- 注意:this()必须在构造器的首行
3.8 标准JavaBean
JavaBean
是 Java语言编写类的一种标准规范。符合JavaBean
的类,要求类必须是具体的和公共的,并且具有无参数的构造方法,成员变量私有化,并提供用来操作成员变量的set
和get
方法。
3.9 包
- 语法:全小写,级别之间通过
.
进行分割 - 作用:
- 避免类的重名
- 分类组织管理众多的类,例如:
- java.lang----包含一些Java语言的核心类,如String、Math、Integer、 System和Thread等,提供常用功能(不需要导包)
- java.net----包含执行与网络相关的操作的类和接口。
- java.io ----包含能提供多种输入/输出功能的类。
- java.util----包含一些实用工具类,如集合框架类、日期时间、数组工具类Arrays,文本扫描仪Scanner,随机值产生工具Random。
- java.text----包含了一些java格式化相关的类
- java.sql和javax.sql----包含了java进行JDBC数据库编程的相关类/接口
- java.awt和java.swing----包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。
- 可以控制某些类型或成员的可见范围
- 使用方法:使用其他包下的内容时,需要导包(idea中自动导包)
3.10 static – 静态的
- stati用途:
- 用于属性,为了资源共享
- 用于方法,为了类名调用方便
- 用于初始化块,为了初始化类时加载一段代码
- 用于内部类,为了使内部类实例化时不依赖于外部类对象
1)成员变量
-
成员变量
- 实例变量:属于对象的
- 类变量:属于类的 在成员变量前添加修饰符static
-
实例变量的使用
- 对象.属性(符合权限访问修饰符范围)
- 类名.属性(符合权限访问修饰符范围)(推荐)
-
类加载顺序:
1)先加载所有的静态资源
2)再加载所有的普通资源
-
特点:
- 静态的属性,空间开辟在方法区内,并且只有一份,因为类只加载一次
- 所有对象操作该属性时,操作的都是方法区的这一个空间,达到共享的目的
2)成员方法
- 成员方法
- 实例方法:属于对象
- 类方法:属于类的 在成员方法前添加修饰符static
- 使用方法
- 对象.方法
- 类名.方法(推荐)(不依赖于对象,工具类方法就设置为static)
- 加载顺序:先加载静态方法,再加载普通方法
- 注意:
- 静态资源内只能直接使用静态资源,不能直接使用非静态资源
- 因为static没有this关键字,静态的先加载
- 非静态资源内都可以直接使用
- 静态资源内只能直接使用静态资源,不能直接使用非静态资源
3)初始化块
-
用于修饰初始化块时,该初始化块在类加载的时候自动执行
-
特点:在类加载的时候只执行一次
4)内部类
- 可以使成员内部类的对象实例化时,不依赖于外部类的对象
3.11 静态导入
例如:import static java.lang.Math.*
3.12 一些工具类介绍
1)数组工具类java.util.Arrays(每一个方法都有各种重载形式,以下只列出int[]类型的,其他类型的数组类推)
-
static int binarySearch(int[] a, int key)
:要求数组有序,在数组中查找key是否存在,如果存在返回第一次找到的下标,不存在返回负数 -
static int[] copyOf(int[] original, int newLength)
:根据original原数组复制一个长度为newLength的新数组,并返回新数组**(常用)** -
static int[] copyOfRange(int[] original, int from, int to)
:复制original原数组的[from,to)构成新数组,并返回新数组 -
static boolean equals(int[] a, int[] a2)
:比较两个数组的长度、元素是否完全相同 -
static void fill(int[] a, int val)
:用val填充整个a数组 -
static void fill(int[] a, int fromIndex, int toIndex, int val)
:将a数组[fromIndex,toIndex)部分填充为val -
static void sort(int[] a)
:将a数组按照从小到大进行排序**(常用)** -
static void sort(int[] a, int fromIndex, int toIndex)
:将a数组的[fromIndex, toIndex)部分按照升序排列 -
static String toString(int[] a)
:把a数组的元素,拼接为一个字符串,形式为:[元素1,元素2,元素3。。。](常用)
2)系统类System
-
static long currentTimeMillis()
:返回当前系统时间距离1970-1-1 0:0:0的毫秒值(注意返回值是long) -
static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
:从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。常用于数组的插入和删除
-
static void exit(int status)
:退出当前系统(一般退出status为0) -
tatic void gc()
:运行垃圾回收器。
统一用法:
System.类名(参数列表)
3)数学类Math
java.lang.Math类包含用于执行基本数学运算的方法。其所有方法均为静态方法,并且不会创建对象,调用起来非常简单。
-
public static double abs(double a)
:返回 double 值的绝对值。 -
public static double ceil(double a)
:返回大于等于参数的最小的整数。 -
public static double floor(double a)
:返回小于等于参数最大的整数。 -
public static long round(double a)
:返回最接近参数的 long。(相当于四舍五入方法) -
public static double pow(double a,double b)
:返回a的b幂次方法 -
public static double sqrt(double a)
:返回a的平方根 -
public static double random()
:返回[0,1)的随机值 -
public static final double PI
:返回圆周率 -
public static double max(double x, double y)
:返回x,y中的最大值 -
public static double min(double x, double y)
:返回x,y中的最小值
统一用法:
Math.类名(参数列表)
4.初始化块
4.1 分类
1)实例化初始化块
-
每次实例化对象自动执行的一个代码块
-
语法:
{}
- 功能:对类中多个构造器中相同内容的复用性提高
- 特点:在构造器之前执行
- 如果有继承:实例化子类的对象,会先执行父类构造器,再执行子类构造器
- 如果存在初始化块:父类初始化块->父类构造器->子类初始化块->子类构造器
2)类初始化块
- 在类加载(只加载一次)的时候自动执行的一个代码块(只执行一次)
- 语法
static{}
-
特点:在类加载的时候只执行一次
-
用途:该类一旦被使用,就执行一段代码
5.内部类
5.1 分类
-
成员内部类
-
静态成员内部类
-
局部(方法)内部类
-
匿名内部类
5.2 匿名内部类
- 语法:
new 类、抽象类、接口(){
重写方法...
}
-
解析:大括号相当于是类的子类、接口的实现类的大括号
-
优缺点:
- 优点:简单一些(省略了创建类)
- 缺点:复用性很差
-
JDK1.8 Lambda表达式对部分内部类简化
5.3 成员内部类
- 位置:类体中(和其他成员在相同位置)
- class文件:外部类$内部类.class
- 语法:
[修饰符] class 类名{}
-
成员内部类的对象实例化,依赖于外部类的对象
-
成员:五大成员都可以,但不可以有静态资源
-
修饰符:四个访问修饰符都可以,abstract和final都可以单独修饰
-
使用:
- 内部类使用外部类的资源:直接访问
- 外部类使用内部类的资源:需要实例化内部类对象
- 外部其他类(测试类)使用内部类:需要实例化内部类的对象
- 首先需要外部类对象
Outher outher = nwe Outher();
- 再实例化内部类对象
Outher.Inner inner = outher.new Inner();
- 或者一步到位
Outher.Inner inner = new Outher().new Inner();
- 首先需要外部类对象
-
冲突问题解决:
- 外部类和内部类的属性或者方法重名
this.内容
:this是当前对象Outher.this.内容
:外部类.this是外部类的对象
- 外部类和内部类的属性或者方法重名
5.4 静态成员内部类
- 位置:类体中(和其他成员在相同位置)
- class文件:外部类$内部类.class
- 语法:
[修饰符] static class 类名{}
-
成员内部类的对象实例化,不依赖于外部类的对象
-
成员:五大成员都可以,可以有静态资源
-
修饰符:四个访问修饰符都可以,abstract和final都可以单独修饰
-
使用:
- 内部类使用外部类的资源:静态资源可以直接使用,非静态的实例化对象使用
- 外部类使用内部类的资源:静态资源可以使用类名调用,非静态的实例化对象使用
- 外部其他类(测试类)使用内部类:
- 不依赖于外部类的对象,直接实例化对象即可
Outher.Inner2 inner2 = new Outher.Inner2();
- 冲突问题
- 外部类的静态资源和内部类的普通资源重复时,静态资源可以通过类名调用
5.5 方法(局部)内部类
- 位置:在某个局部(方法内)
- class文件:外部类$1内部类.class
- 语法:
[修饰符] class 类名{} //修饰符不能使用访问修饰符
-
修饰符:abstract、final可以单独修饰,访问修饰符不可以使用
-
成员:五大成员都可以,但不包括静态资源
-
使用:
- 内部类使用外部类的资源:直接使用
- 外部类使用内部类的资源:当前局部可以通过实例化对象的形式,其他地方无法访问
- 外部其他类(测试类)使用内部类:访问不到
-
冲突问题:
- this:当前对象
- Outher.this:外部类对象
-
如果当前局部存在局部变量,并且该变量在内部类中使用的话,默认会变为常量
四、面向对象三大特性
- 三大特性:封装、继承、多态
- 封装:安全、方便
- 继承:代码的复用性和拓展
- 多态:功能更灵活,可以使用父类类型的变量可以统一管理它的子类对象
1. 封装
1.1 概述
封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问。要访问该类的数据,必须通过指定的方式。适当的封装可以让代码更容易理解与维护,也加强了代码的安全性。
1.2 对属性的封装
-
封装原则
1)对需要封装的属性设置访问权限(私有的)
2)对外界提供两个方法(设置、获取)
-
属性封装的目的
- 隐藏类的实现细节
- 让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里面加入控制逻辑,限制对成员变量的不合理访问。
- 可以进行数据检查,从而有利于保证对象信息的完整性。
- 便于修改,提高代码的可维护性。
-
实现步骤
1)使用
private
修饰成员变量private 数据类型 变量名;
代码如下:
public class Student { private String name; }
2)提供
getXxx
方法 /setXxx
方法,可以访问私有化的成员变量,代码如下:public class Student { private String name; public void setName(String n) { name = n; } public String getName() { return name; } }
1.3 权限(访问)修饰符
- 设置元素(类、成员变量、成员方法)的访问范围。
类的权限修饰符:只能用public和缺省的
成员变量和成员方法:四个修饰符都可以用
2.继承
2.1 继承的理解
继承就是子类继承父类的属性和行为,使得子类对象具有与父类相同的属性、相同的行为。
2.2 继承的好处
- 提高代码复用性
- 提高代码拓展性
2.3 继承的格式
通过 extends
关键字:
[修饰符] class 子类 extends 父类 { ... }
2.4 继承中的成员变量
1)父类中所有成员变量都会被子类继承下去
- 如果父类中的成员变量是私有的,那么子类是不可以直接访问的。
2)成员变量不重名
- 访问成员变量没有影响
3)成员变量重名
this.成员变量
默认为就近原则(当前对象的引用)super.成员变量
访问父类的成员变量(父类空间的引用)
2.5 继承中的成员方法
1)成员方法不重名
- 调用成员方法没有影响
2)成员方法重名——重写
-
如果子类父类中出现重名的成员方法,这时的访问是一种特殊情况,叫做方法重写 (Override)。
-
目的:子类根据需求对父类的方法重新编写。
-
重写后依旧想用父类的方法:
- 本类:通过
super.方法名
调用父类的方法 - 其他类:没有直接的办法,要通过本类的super
- 本类:通过
3)重写的条件
- 必须是父子类
- 方法名必须相同
- 形参列表必须相同(个数、类型、顺序)
- 和返回值有关
- 基本数据类型和void:返回值类型必须一致
- 引用数据类型:子类的要小于等于父类的
- 和权限修饰符有关
- 子类的要大于等于父类的权限
4)不允许被重写
- 静态方法(静态方法属于类)
- 私有等在子类中不可见的方法不能被重写
- final方法不能被重写
5)继承的应用(难度很大)
-
this调用属性
- 看类型(看在哪个类)
-
this调用方法
- 看实际对象(看new的是谁)
2.6 继承中的构造器
-
由于构造器的名称必须和类名一致,所以子类是无法继承父类构造器的
-
构造器的作用是初始化成员变量的
-
类的加载顺序:
- 先加载父类再加载子类
- 实例化子类对象的时候,父类空间优于子类对象的产生(先执行父类构造器,再执行子类构造器
- 具体:
- 1.父类静态属性、静态代码块
- 2.子类静态属性、静态代码块
- 3.父类非静态属性、代码块
- 4.父类构造方法
- 5.子类非静态属性、代码块
- 6.子类构造方法
-
每一个构造器的首行都默认存在一行
super()
默认调用父类无参 -
要求:
- super()如果显示的创建,也必须在首行
- 父类必须存在无参构造器,否则默认情况会报错
-
当父类的构造器调用子类的方法时,属于对父类空间进行初始化,子类对象未被加载,子类中的属性不会被初始化(子类中的属性此时都为默认值)
2.7 继承中的单继承
一个类中只能又一个直接父类,但是一个类可以有多个子类
- 继承具有局限性
- 任何类都是有父类的,如果没有extend,则默认父类为Object
2.8 super
父类空间优先于子类对象产生
- super:指的是子类对象中的父类空间
- this:指的是子类对象
- 用法:
super.内容
:调用父类的属性或方法this.内容
:调用自己的属性或方法- 共同的:找不到都会往上一级一级的找
super([实参列表])
调用父类的构造器(对父类空间进行初始化)(只能从直接父类找)this()
调用本类的构造器
- 特点:都在构造器的第一行,不能同时出现
注意:super和this都不能出现在静态方法和静态代码块中,因为super和this都是存在与对象中的
2.9 final
final:最终的,不可更改的
-
修饰类
- 该类不可以被继承
- String类就是final修饰的
-
修饰变量
- 该变量变为常量
- final修饰成员变量必须要赋值(初始化)
- final修饰局部变量,可以不初始化,但无法使用
- 常量名:建议使用全大写,多个单词使用_隔开
-
修饰方法
- 该方法无法重写
3.多态
3.1 定义
同一行为,具有多个不同的表现形式
3.2 前提
1)父子类
2)必须有重写
3)父类的引用指向子类对象
3.3 多态的语法
父类的引用指向子类对象
- 语法
父类类型 变量名 = new 子类对象;
变量名.方法名();
例:
Person per = new Emplpyee();
per.eat();//名义上调用的是Person类中的eat方法,实际上是Employee的
3.4 多态的注意事项
- 使用不了子类独有的内容
- 因为名义是父类类型的,就只能调用父类中存在的,如果子类有重写调用子类的方法,没有重写就是父类的方法
- 总结:多态只能调用父类中的内容
- 成员变量
- 子类独有的访问不到,如果有重名属性,调用属性看类型
3.5 多态的用途
1)多态参数
实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展性与便利。
2)多态数组(对象数组的升级)
3.6 父子类之间的类型转换
1)向上转型(自动转换,小–>大)
多态本身就是子类类型向父类类型向上转换的过程,这个过程是默认的。
父类类型 变量名 = new 子类类型();
2)向下转型(强制转换,大–>小)
父类类型向子类类型向下转换的过程,这个过程是强制的。
因为多态调用不到子类独有的内容,所以需要向下转型。
- 语法
子类类型 变量名 = (子类类型)父类变量名;
- 向下转型时可能会出现类型转换异常(ClassCastException),例如:
Animal a = new Cat();
Dog d = (Dog)a;
- 所以强转之前要做判断,使用
instanceof
关键字
Employee emp = new Employee;
boolean flag = employee instanceof Employee; //instanceof判断前面的实际对象是否属于后面的类型,falg == ture
boolean flag = employee instanceof Person; //flag == ture
- 注意
Person per = new Employee;
boolean flag = person instanceof Employee; //flag == ture
3.7 native关键字
native–本地的原生的
追踪源码时会看见,代码是由c语言实现
native只能修饰方法,可以被重写
3.8 修饰符的使用问题
1)不能和abstract一起使用的修饰符?
- final:不能一起修饰方法和类
- static:不能一起修饰方法
- native:不能一起修饰方法
- private:不能一起修饰方法
2)static和final一起使用:
- 修饰方法:可以,因为都不能被重写
- 修饰成员变量:可以,表示静态常量
- 修饰局部变量:不可以,static不能修饰局部变量
- 修饰代码块:不可以,final不能修改代码块
- 修饰内部类:可以一起修饰成员内部类,不能一起修饰局部内部类(后面讲)
3.9 Object–根父类
默认是所有类的父类,该类中所有方法都可以被任意对象继承使用。
1)构造器Object();
2)方法
-
toString
:-
默认情况下,toString()返回的是“对象的运行时类型 @ 对象的hashCode值的十六进制形式"
-
直接用System.out.println(对象),默认会自动调用这个对象的toString()
-
源码:
-
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
-
getClass
:由C语言编写 -
功能:返回对象的运行时类型
- 源码:
public static void main(String[] args) { Object obj = new String(); System.out.println(obj.getClass());//运行时类型 }
-
int hashCode()
:- 功能:将当前对象,通过哈希算法,得到一个int值
- 两对相同的对象(地址相同):经过相同的hash算法,得到的int值相同
- 两个不同的对象,经过相同的hash算法,得到的int值可能相同,大概率不相同
-
finalize()
:- java中存在的垃圾回收机制(自动的机制)
- 没有引用的对象会被认为是垃圾,将不定时回收
- 垃圾对象被回收时默认调用该对象的finalize方法(此方法并不是回收的代码)
System.gc();
:通知垃圾回收机制来回收垃圾(不会立刻来回收垃圾对象)
-
equals
与==
区别:==
:判断两个对象的地址是否一致equals
:判断两个对象值是否一致- 注:
String str1 = new String; String str2 = new String; Systom.out.println("str1.equals(str2)"); //false
- (Object obj)中的equals源码:
public boolean equals(Object obj){ return (this == obj); }
和 == 没有区别
- String类中的equals方法源码:
public boolean equals(Object anObject) { //先判断地址是否一致 if (this == anObject) { return true; } //判断anObject是否是String类型 if (anObject instanceof String) { String anotherString = (String)anObject; //向下转型 int n = value.length; //判断长度是否一致 if (n == anotherString.value.length) { //v1是this的字符串内容 char v1[] = value; //v2是参数字符串的内容 char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
String中的equals对比的是内容
五、抽象类和接口
1.抽象类
包含抽象方法的类必须是抽象类。
1.1 定义
- 抽象方法 : 没有方法体的方法。
- 抽象类:被abstract所修饰的抽的类。
-
语法:
- 抽象类语法
【权限修饰符】 abstract class 类名{ } 【权限修饰符】 abstract class 类名 extends 父类{ }
- 抽象方法语法:
【其他修饰符】 abstract 返回值类型 方法名(【形参列表】);
注意:抽象方法没有方法体
1.2 抽象的使用(特点)
1)抽象方法存在的类,必须是抽象类。
2)子类在继承抽象类时,必须实现父类所有的抽象方法。
3)抽象类中可以有0-n个抽象方法或普通方法
4)如果子类不能实现抽象类中的抽象方法,那么子类也必须变为抽象类
5)抽象类不能实例化对象
6)抽象类中有构造器,初始化子类对象的父类空间时加载此构造器
1.3 抽象类应用
一般作为模板工具类
2.接口
2.1 概述
- 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则必须能…”的思想。继承是一个"是不是"的关系,而接口实现则是 "能不能"的关系。has-a
- 例如:你想要会飞,那么必须拥有fly()方法
- 例如:你想要连接USB,那么必须拥有read()/in()和write()/out()等
- 例如:你的数据库产品想要被Java连接,那么你需要实现Connection, Statement等
- 接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。
2.2 接口语法
[修饰符] interface 接口名{ //修饰符只能是public或者没有修饰符
//接口的成员列表:
// 静态常量
// 抽象方法
// 默认方法
// 静态方法
// 私有方法
}
-
JDK8之前:
- 接口中所有的成员变量,默认都是共有的静态常量(不允许修改)
- 接口的所有的成员方法,默认都是所有的抽象方法
- 接口不能实例化对象,因为没有构造器
- 接口中没有初始化块
- 接口只能作为父级使用
- 类可以实现多个接口,但是类需要实现接口中所有的抽象方法
- 接口可以继承多个接口
-
JDK8新增:(都可以有方法体)
-
默认方法(default)(可以认为是接口中的普通方法)
static void staticMethod(){}
使用方式:
实现类的对象.defaultMethod();
,默认方法可以被继承类重写 -
静态方法
default void defaultMethod(){}
使用方式:
接口名.staticMethod();
-
2.3 特点
- 接口不能实例化对象,但是接口支持多态
- 只能作为父类(父接口)
- 实现类实现父接口
- 实现类实现父接口,并实现父接口中所有的抽象方法
[public] class 实现类 implements B {
// 重写接口中抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
// 重写接口中默认方法【可选】
}
[public] class 实现类 extends 父类 implements 接口{ //父类必须在接口之前
// 重写接口中抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
// 重写接口中默认方法【可选】
}
- 类只能有一个父类,可以有多个父接口
2.4 关系
- 类与类之间:单继承
- 类与接口之间:多实现
- 接口和接口之间:多继承
2.5 冲突
-
实现类是可以实现接口,同时也可以继承父类
- 当父接口和父类中有相同的方法,默认调用父类的
-
实现类是可以实现多个接口的
-
若两个及以上父接口有相同的默认方法,则实现类必须重写该方法
①自己真正的重写
②在重写中选择父接口中的一个、几个或都选,语法:
父接口.super.方法
-
3. 经典接口
3.1 java.lang.Comparable(自然排序)
对比两个对象的大小
-
实现抽象方法——
int compareTo(Object obj)
-
功能:this和obj对比大小
-
返回值:
- 正数——this大
- 负数——this小
- 0——相同
-
sort排序会自动默认调用compareTo方法
-
接口的源码
package java.lang;
public interface Comparable{
int compareTo(Object obj);
}
- 具体步骤:
- 第一步:哪个类的对象要比较大小,哪个类就实现java.lang.Comparable接口,并重写方法
- 方法体就是你要如何比较当前对象和指定的另一个对象的大小
- 第二步:对象比较大小时,通过对象调用compareTo方法,根据方法的返回值决定谁大谁小。
- 第一步:哪个类的对象要比较大小,哪个类就实现java.lang.Comparable接口,并重写方法
- 举例:
public class Student implements Comparable{
......
public int compareTo(Object o) {
if (this==o){
return 0;
}
if (o instanceof Student){
Student s = (Student) o;
return this.getId() - o.getId();
}
return 0;
}
}
3.2 java.util.Comparator(定制排序)
前提:你没有权力修改某个类的代码,无法进行自然排序,如第三方的产品(只提供class文件)
- 实现方法——
int compare(Object o1,Object o2)
- 当定制排序和自然排序都存在时,以定制排序为主
- 举例:
class MyComparator implements Comparator {
@Override
public int compare(Object o1, Object o2) {
if (o1 == o2){
return 0;
}
if (o1 instanceof Employee && o2 instanceof Employee){
Employee emp1 = (Employee) o1;
Employee emp2 = (Employee) o2;
return emp1.getId() - emp2.getId();
}
return 0;
}
}
如果排序时比较的是double类型的,return时可以使用 Double.compare(emp1.getSalary(),stu2.getSalary()) 从低到高排序