目录
前言
权限修饰符
封装思想
Private实现封装
Default(包访问权限)
继承思想
protected
super关键字
super修饰属性
super修饰方法
super修饰构造方法
super修饰普通方法
final的补充
多态思想
向上转型
多态的前提——方法重写(override)
向下转型
前言
我们在前面已经介绍了什么叫面向对象编程,现在我们在这章博客详细的去介绍面对对象编程的几大特性封装,多态和继承
权限修饰符
指的是修饰的属性,方法,类,可见的范围有多大,一共有四大访问修饰符,可见范围从小到大
private
- private 表示私有的,被private修饰的属性和方法,只在当前类内部可见,出来类的范围,对外部就完全隐蔽了,外部不知道其的存在
- default 不写任何修饰权限的修饰符,表示的就是default,包访问权限,当前包的内部可见,不包括子包(同级目录可见)
- protected 继承,不同包的有继承关系的类之间可见,也包括同一个包下没有继承关系的类
- public 公开的,被public修饰的,当前整个程序(项目)都是可以使用的
封装思想
封装是为了实现程序的保护性和易用性,让使用者不必太知道实现者是如何实现的,只要能用就行,降低了使用者的学习量
例子
拿银行卡这个类来说,银行卡有卡号,余额,密码三个属性,如果这三个属性都直接暴露在外部肯定是不合理的(直接就在卡上贴着)——>不安全,不能让这些属性可以通过对象直接调用
对于汽车这个类来说,车真正发动起来,我们需要启动发动机,变速箱,等等属性,但是现实中,我们只需要一键启动就行,对于这些具体的属性是不可见的,也是我们不关注的——>易用性
Private实现封装
只有Bank内部知道这三个属性的存在,在这个类的外部,是不知道这三个属性的存在,不能直接调用
那如何在外部去实现这些私有属性呢?
需要使用这个类提供的getter(取值)和setter(修改值)的方法,而且哪些属性要提供setter,哪些属性只提供getter,或者都提供,需要由这些属性来决定
import java.util.Scanner;
public class PrivateTest {
public static void main(String[] args) {
Bank bank=new Bank();
System.out.println(bank.getCardNum());
System.out.println(bank.getSal());
bank.setPassword();
}
}
class Bank{
private int cardNum=145678;
private double sal=20000.6;
private String password="12345678";
public int getCardNum() {
return cardNum;
}
public double getSal() {
return sal;
}
public void setPassword() {
int count=0;
Scanner scanner=new Scanner(System.in);
while (true){
System.out.println("请输入原来的密码");
String oldPassword=scanner.nextLine();
count++;
if (oldPassword.equals(password)){
System.out.println("验证成功,请输入你的新密码");
String newPassword=scanner.nextLine();
password=newPassword;
System.out.println("密码修改成功");
break;
}
else {
System.out.println("验证失败,请查证后重新输入");
if (count==3){
System.out.println("尝试次数过多,银行卡已经锁定");
break;
}
}
}
}
}
实现了对银行卡这个类的属性包含,让别人不能直接修改卡号和余额,只能通过Bank这个类的类方法去提取卡号和余额的数据,能改动密码,但是必须按照Bank这个类的规定的方法去改,实现了安全性
public class PrivateTest {
public static void main(String[] args) {
Car car=new Car();
car.start();
}
}
class Car{
private String engine;
private String bianSuXiang;
private String guLu;
public void start(){
engine="引擎启动";
bianSuXiang="启动变速箱";
guLu="轮胎开始转";
System.out.println("汽车启动了");
}
}
对于Car这个类,我们假设启动汽车,不需要知道其内部如何构成,只需要调用启动这个函数,就直接可以启动
阿里编码规范 JAVA类中所有的成员变量一律使用private封装,并根据自身属性实际情况对我提供getter和setter方法
Default(包访问权限)
default就是什么都不写的限权标识符,默认的就是包访问限权,表示的范围就是在一个包中,子包和包外都不行
什么是包呢?
- 同一个包下的类可以调用default修饰权限的属性或者方法
- 不同包下的类不能使用default修饰的属性或者非方法
- 相同包下不同的子包也不能使用default修饰的属性和方法
我们在前面已经介绍了什么叫面向对象编程,现在我们在这章博客详细的去介绍面对对象编程的几大特性封装,多态和继承
指的是修饰的属性,方法,类,可见的范围有多大,一共有四大访问修饰符,可见范围从小到大
private
- private 表示私有的,被private修饰的属性和方法,只在当前类内部可见,出来类的范围,对外部就完全隐蔽了,外部不知道其的存在
- default 不写任何修饰权限的修饰符,表示的就是default,包访问权限,当前包的内部可见,不包括子包(同级目录可见)
- protected 继承,不同包的有继承关系的类之间可见,也包括同一个包下没有继承关系的类
- public 公开的,被public修饰的,当前整个程序(项目)都是可以使用的
封装思想
封装是为了实现程序的保护性和易用性,让使用者不必太知道实现者是如何实现的,只要能用就行,降低了使用者的学习量
例子
拿银行卡这个类来说,银行卡有卡号,余额,密码三个属性,如果这三个属性都直接暴露在外部肯定是不合理的(直接就在卡上贴着)——>不安全,不能让这些属性可以通过对象直接调用
对于汽车这个类来说,车真正发动起来,我们需要启动发动机,变速箱,等等属性,但是现实中,我们只需要一键启动就行,对于这些具体的属性是不可见的,也是我们不关注的——>易用性
Private实现封装
只有Bank内部知道这三个属性的存在,在这个类的外部,是不知道这三个属性的存在,不能直接调用
那如何在外部去实现这些私有属性呢?
需要使用这个类提供的getter(取值)和setter(修改值)的方法,而且哪些属性要提供setter,哪些属性只提供getter,或者都提供,需要由这些属性来决定
import java.util.Scanner;
public class PrivateTest {
public static void main(String[] args) {
Bank bank=new Bank();
System.out.println(bank.getCardNum());
System.out.println(bank.getSal());
bank.setPassword();
}
}
class Bank{
private int cardNum=145678;
private double sal=20000.6;
private String password="12345678";
public int getCardNum() {
return cardNum;
}
public double getSal() {
return sal;
}
public void setPassword() {
int count=0;
Scanner scanner=new Scanner(System.in);
while (true){
System.out.println("请输入原来的密码");
String oldPassword=scanner.nextLine();
count++;
if (oldPassword.equals(password)){
System.out.println("验证成功,请输入你的新密码");
String newPassword=scanner.nextLine();
password=newPassword;
System.out.println("密码修改成功");
break;
}
else {
System.out.println("验证失败,请查证后重新输入");
if (count==3){
System.out.println("尝试次数过多,银行卡已经锁定");
break;
}
}
}
}
}
实现了对银行卡这个类的属性包含,让别人不能直接修改卡号和余额,只能通过Bank这个类的类方法去提取卡号和余额的数据,能改动密码,但是必须按照Bank这个类的规定的方法去改,实现了安全性
public class PrivateTest {
public static void main(String[] args) {
Car car=new Car();
car.start();
}
}
class Car{
private String engine;
private String bianSuXiang;
private String guLu;
public void start(){
engine="引擎启动";
bianSuXiang="启动变速箱";
guLu="轮胎开始转";
System.out.println("汽车启动了");
}
}
对于Car这个类,我们假设启动汽车,不需要知道其内部如何构成,只需要调用启动这个函数,就直接可以启动
阿里编码规范 JAVA类中所有的成员变量一律使用private封装,并根据自身属性实际情况对我提供getter和setter方法
Default(包访问权限)
default就是什么都不写的限权标识符,默认的就是包访问限权,表示的范围就是在一个包中,子包和包外都不行
什么是包呢?
- 同一个包下的类可以调用default修饰权限的属性或者方法
- 不同包下的类不能使用default修饰的属性或者非方法
- 相同包下不同的子包也不能使用default修饰的属性和方法
封装是为了实现程序的保护性和易用性,让使用者不必太知道实现者是如何实现的,只要能用就行,降低了使用者的学习量
例子
拿银行卡这个类来说,银行卡有卡号,余额,密码三个属性,如果这三个属性都直接暴露在外部肯定是不合理的(直接就在卡上贴着)——>不安全,不能让这些属性可以通过对象直接调用
对于汽车这个类来说,车真正发动起来,我们需要启动发动机,变速箱,等等属性,但是现实中,我们只需要一键启动就行,对于这些具体的属性是不可见的,也是我们不关注的——>易用性
只有Bank内部知道这三个属性的存在,在这个类的外部,是不知道这三个属性的存在,不能直接调用
那如何在外部去实现这些私有属性呢?
需要使用这个类提供的getter(取值)和setter(修改值)的方法,而且哪些属性要提供setter,哪些属性只提供getter,或者都提供,需要由这些属性来决定
import java.util.Scanner; public class PrivateTest { public static void main(String[] args) { Bank bank=new Bank(); System.out.println(bank.getCardNum()); System.out.println(bank.getSal()); bank.setPassword(); } } class Bank{ private int cardNum=145678; private double sal=20000.6; private String password="12345678"; public int getCardNum() { return cardNum; } public double getSal() { return sal; } public void setPassword() { int count=0; Scanner scanner=new Scanner(System.in); while (true){ System.out.println("请输入原来的密码"); String oldPassword=scanner.nextLine(); count++; if (oldPassword.equals(password)){ System.out.println("验证成功,请输入你的新密码"); String newPassword=scanner.nextLine(); password=newPassword; System.out.println("密码修改成功"); break; } else { System.out.println("验证失败,请查证后重新输入"); if (count==3){ System.out.println("尝试次数过多,银行卡已经锁定"); break; } } } } }实现了对银行卡这个类的属性包含,让别人不能直接修改卡号和余额,只能通过Bank这个类的类方法去提取卡号和余额的数据,能改动密码,但是必须按照Bank这个类的规定的方法去改,实现了安全性
public class PrivateTest { public static void main(String[] args) { Car car=new Car(); car.start(); } } class Car{ private String engine; private String bianSuXiang; private String guLu; public void start(){ engine="引擎启动"; bianSuXiang="启动变速箱"; guLu="轮胎开始转"; System.out.println("汽车启动了"); } }对于Car这个类,我们假设启动汽车,不需要知道其内部如何构成,只需要调用启动这个函数,就直接可以启动
阿里编码规范 JAVA类中所有的成员变量一律使用private封装,并根据自身属性实际情况对我提供getter和setter方法
Default(包访问权限)
default就是什么都不写的限权标识符,默认的就是包访问限权,表示的范围就是在一个包中,子包和包外都不行
什么是包呢?
- 同一个包下的类可以调用default修饰权限的属性或者方法
- 不同包下的类不能使用default修饰的属性或者非方法
- 相同包下不同的子包也不能使用default修饰的属性和方法
default就是什么都不写的限权标识符,默认的就是包访问限权,表示的范围就是在一个包中,子包和包外都不行
什么是包呢?
- 同一个包下的类可以调用default修饰权限的属性或者方法
- 不同包下的类不能使用default修饰的属性或者非方法
- 相同包下不同的子包也不能使用default修饰的属性和方法
(Package)的注意点
- 在不同的包中可以定义相同名称的类
- 包的命名使用全小写,多个单词下划线分割
- 引用一个包的内容,需要import 包名.类名(类的全名就是包名.类名)不能直接导入一个包,只能导入一个包下面的某个类
- JAVA中的包就是操作系统中的文件夹,声明一个包使用package关键字,若存在多个文集夹的嵌套,需要使用.来分割
如何导入包中的类
首先我们要明确一个类的全称是(包的路径+类的名称),而且在JAVA中我们只能导入某个具体的类,不能导入一个包
- 在src下的test类导入animal这个类
- 导入系统提供的包中的类 import 包的路径.类名
- 导入一个包中所有的类 import 包的路径.*
- 但是一般不推荐用*这种方式,因为会产生一些问题,我们可以使用包的完全类型,比如
java.sql.Date date=new java.sql.Date();或导入明确的类- import static 导入一个类的静态方法和属性,可以直接访问这个类的静态方法和属性,不需要通过类名称.方法或者属性 这样写不推荐
常见的系统包
- java.lang:JDK的基础类 System Stirng Object都在这个包下,JDK1.1之后的这个包所有的类自动导入
- java.lang.reflect 反射开发包
- java.util 工具包(集合类都在这个包内 Arrays,LinkList,HashMap)
- java.io I/O开发包,文件读取和写入
- java.net 网络编程开发包 Socket
- java.sql 数据库开发用到的包
继承思想
为什么引入继承?
我们可以发现我们定义的三个类,他们的代码是一样的,这样写代码会导致代码冗余,那我们怎么解决呢?
我们可以发现狗和猫都是属于动物的,可以描述成Cat is a Animal Dog is a Animal ,所以按道理说,动物应该有的行为和属性,狗和猫都应该有,就像名字和进食这种属性和行为,那我们就引入继承这种方法,让狗和猫的类去继承动物这个类
发生继承后的代码
- JAVA中用extends来表示继承
- 继承发生,像狗和猫这种类就是动物这个类的子类/派生类,动物就是狗的父类/基类
- 一个子类中只能用extends继承一个父类(单继承),但是可以进行多层继承,就像一个人只能有一个生理意义上的父亲,但是一个人可以有父亲,也能有爷爷和祖宗
- JAVA中不能为了省略代码就去使用继承,像要使用阶乘必须要满足is a关系,就像Dog is animal,没有这种逻辑关系千万不能使用继承
- 对于静态的方法和属性也是一样,子类都会继承,使用则看这个属性或者方法的修饰权限,区别就是在一个通过类调用,一个通过对象调用
- 子类继承了父类,那么父类中所有的属性和方法子类就是天然具备了(public修饰的属性和方法能直接使用)(private修饰的子类也会继承,但是是无法直接使用的,因为虽然继承,但是子类或者其他类都是没有限权去访问,只有本类可以去访问),对于隐式继承虽然不能直接调用,但是可以通过父类提供的方法去调用
如何理解隐式继承?
就像我们继承了我们父亲,但是我们父亲的余额是对于我们父亲是一个私有属性,我们肯定不能随意的去花我们父亲的钱(不能直接调用),我们要是想花我们父亲的钱,必须要按照我们父亲的要求才能去花,得到他的允许才能花(通过父类提供的方法去调用)
protected
它的访问权限是在不同包中的子类可见,和同一个包的下的类(不包括子包)
各个类所在的包的情况
Animal类的情况
- 不同包的非子类不可见
- 不同包的子类可见
- 相同包下的类(没有继承关系)可以直接调用(不包括子包)
- 加深理解
super关键字
首先我们来看一个现象
我们发现我们在new一个Taidi对象,会先产生一个父类对象Dog(调用了Dog的构造方法),我们可以类比一下现实生活,如果没有你爸怎么来的你,所以new一个对象肯定要先产生父类对象,如果父类对象也有父类对象,也会遵循这个规则(没有你爷,那来的你爸)
一道经典的阿里面试题来帮助我们理解静态块+构造块+构造方法+继承
class B{
public B(){
System.out.println("1.B的构造方法——————————————");
}
{
System.out.println("2.B的构造块————————————————");
}
static {
System.out.println("3.B的静态块————————————————");
}
}
class D extends B {
public D(){
System.out.println("4.D的构造方法——————————————");
}
{
System.out.println("5.D的构造块————————————————");
}
static {
System.out.println("6.D的静态块————————————————");
}
public static void main(String[] args) {
System.out.println("7.main开始————————————————");
new D();
new D();
System.out.println("8.main结束————————————————");
}
}
- 主方法在D这个类中,所以程序想要从主方法进入运行起来,我们先去加载D这个类,因为B是D的父类,所以加载D之前要先加载B,所以3这个静态块最先加载,然后加载B,执行6D的静态块
- 进入主方法后,我们们执行7这条语句
- 我们进行new对象时,先会产生父类对象也就是B,然后产生D的对象,会执行构造方法,但是构造块比构造方法先执行 也就是先执行 2 1 5 4
- 最后执行8,然后程序结束
为什么引入继承?
我们可以发现我们定义的三个类,他们的代码是一样的,这样写代码会导致代码冗余,那我们怎么解决呢?
我们可以发现狗和猫都是属于动物的,可以描述成Cat is a Animal Dog is a Animal ,所以按道理说,动物应该有的行为和属性,狗和猫都应该有,就像名字和进食这种属性和行为,那我们就引入继承这种方法,让狗和猫的类去继承动物这个类
发生继承后的代码
- JAVA中用extends来表示继承
- 继承发生,像狗和猫这种类就是动物这个类的子类/派生类,动物就是狗的父类/基类
- 一个子类中只能用extends继承一个父类(单继承),但是可以进行多层继承,就像一个人只能有一个生理意义上的父亲,但是一个人可以有父亲,也能有爷爷和祖宗
- JAVA中不能为了省略代码就去使用继承,像要使用阶乘必须要满足is a关系,就像Dog is animal,没有这种逻辑关系千万不能使用继承
- 对于静态的方法和属性也是一样,子类都会继承,使用则看这个属性或者方法的修饰权限,区别就是在一个通过类调用,一个通过对象调用
- 子类继承了父类,那么父类中所有的属性和方法子类就是天然具备了(public修饰的属性和方法能直接使用)(private修饰的子类也会继承,但是是无法直接使用的,因为虽然继承,但是子类或者其他类都是没有限权去访问,只有本类可以去访问),对于隐式继承虽然不能直接调用,但是可以通过父类提供的方法去调用
如何理解隐式继承?
就像我们继承了我们父亲,但是我们父亲的余额是对于我们父亲是一个私有属性,我们肯定不能随意的去花我们父亲的钱(不能直接调用),我们要是想花我们父亲的钱,必须要按照我们父亲的要求才能去花,得到他的允许才能花(通过父类提供的方法去调用)
它的访问权限是在不同包中的子类可见,和同一个包的下的类(不包括子包)
各个类所在的包的情况
Animal类的情况
- 不同包的非子类不可见
- 不同包的子类可见
- 相同包下的类(没有继承关系)可以直接调用(不包括子包)
- 加深理解
super关键字
首先我们来看一个现象
我们发现我们在new一个Taidi对象,会先产生一个父类对象Dog(调用了Dog的构造方法),我们可以类比一下现实生活,如果没有你爸怎么来的你,所以new一个对象肯定要先产生父类对象,如果父类对象也有父类对象,也会遵循这个规则(没有你爷,那来的你爸)
一道经典的阿里面试题来帮助我们理解静态块+构造块+构造方法+继承
class B{
public B(){
System.out.println("1.B的构造方法——————————————");
}
{
System.out.println("2.B的构造块————————————————");
}
static {
System.out.println("3.B的静态块————————————————");
}
}
class D extends B {
public D(){
System.out.println("4.D的构造方法——————————————");
}
{
System.out.println("5.D的构造块————————————————");
}
static {
System.out.println("6.D的静态块————————————————");
}
public static void main(String[] args) {
System.out.println("7.main开始————————————————");
new D();
new D();
System.out.println("8.main结束————————————————");
}
}
- 主方法在D这个类中,所以程序想要从主方法进入运行起来,我们先去加载D这个类,因为B是D的父类,所以加载D之前要先加载B,所以3这个静态块最先加载,然后加载B,执行6D的静态块
- 进入主方法后,我们们执行7这条语句
- 我们进行new对象时,先会产生父类对象也就是B,然后产生D的对象,会执行构造方法,但是构造块比构造方法先执行 也就是先执行 2 1 5 4
- 最后执行8,然后程序结束
首先我们来看一个现象
我们发现我们在new一个Taidi对象,会先产生一个父类对象Dog(调用了Dog的构造方法),我们可以类比一下现实生活,如果没有你爸怎么来的你,所以new一个对象肯定要先产生父类对象,如果父类对象也有父类对象,也会遵循这个规则(没有你爷,那来的你爸)
一道经典的阿里面试题来帮助我们理解静态块+构造块+构造方法+继承
class B{
public B(){
System.out.println("1.B的构造方法——————————————");
}
{
System.out.println("2.B的构造块————————————————");
}
static {
System.out.println("3.B的静态块————————————————");
}
}
class D extends B {
public D(){
System.out.println("4.D的构造方法——————————————");
}
{
System.out.println("5.D的构造块————————————————");
}
static {
System.out.println("6.D的静态块————————————————");
}
public static void main(String[] args) {
System.out.println("7.main开始————————————————");
new D();
new D();
System.out.println("8.main结束————————————————");
}
}
- 主方法在D这个类中,所以程序想要从主方法进入运行起来,我们先去加载D这个类,因为B是D的父类,所以加载D之前要先加载B,所以3这个静态块最先加载,然后加载B,执行6D的静态块
- 进入主方法后,我们们执行7这条语句
- 我们进行new对象时,先会产生父类对象也就是B,然后产生D的对象,会执行构造方法,但是构造块比构造方法先执行 也就是先执行 2 1 5 4
- 最后执行8,然后程序结束
super修饰属性
表示从父类中寻找同名属性
父类People
- 如果在一个方法里,局部变量和成员变量和父类的成员变量都是一个名字,那么就会遵循就近原则,局部>成员>父类(多层)
- this在继承中的向上寻找
- super只会去从直接父类去找同名属性,若不存在就在向上寻找
当super和private碰撞到一起
虽然我们可以在Person父类中找到,但是因为是private权限,所以不能在子类China中使用
super修饰方法
super修饰构造方法
class China extends Person{
// public String name="china";
public China(){
System.out.println("这是Chian的无参构造");
}
public void fun(){
System.out.println(super.name);
}
public static void main(String[] args) {
China china=new China();
}
}
class Person extends Animal {
public Person (){
System.out.println("这是Person的无参构造");
}
public String name="person";
}
class China extends Person{
// public String name="china";
public China(){
System.out.println("这是Chian的无参构造");
}
public void fun(){
System.out.println(super.name);
}
public static void main(String[] args) {
China china=new China();
}
}
我们new一个China对象,会发生什么
当产生子类对象时,默认先产生父类对象,若父类对象还有继承,继承向上先产生祖类对象
如果将Person类写一个有参构造
我们再去new一个Chian对象,会怎么样
为什么会发生这样?因为在有继承关系的类中,子类的构造方法的第一行会默认会有super(),表示调用父类的无参构造方法,如果父类没有无参构造,那么就需要我们在子类的构造方法自己写super(有参构造),表示调用父类的有参构造,不然就会报错
this()和super()的关系
因为this()表示调用构造方法,也必须放在第一行,super()调用父类构造方法,也必须放在第一行,所以它两不能同时显式同时出现,
super修饰普通方法
直接从父类中去寻找同名方法,跟属性差不多
class Person {
public Person (){
System.out.println("这是Person的无参构造");
}
public String name="person";
public void fun(){
System.out.println("这是Person的fun函数");
}
}
class China extends Person{
// public String name="china";
public China(){
System.out.println("这是Chian的无参构造");
}
public void fun(){
System.out.println(super.name);
}
public void test(){
super.fun();
}
public static void main(String[] args) {
China china=new China();
china.test();
}
}
test调用的式Person的fun方法
但是super不能指代当前父类的对象引用
final的补充
- 修饰属性表示属性的值和类型都不能改变
- 修饰类,表示这个类无法被继承
多态思想
多态:一个引用可以表现为多种行为/特性
首先我们来看编程中会存储的一个问题
class Animal {
public String name="Animal";
public void eat(){
System.out.println(name+"的eat方法");
}
}
class Bird extends Animal{
public String name="Bird";
public void eat(){
System.out.println(name+"的eat方法");
}
}
class Duck extends Bird{
public String name="Duck";
public void eat(){
System.out.println(name+"的eat方法");
}
}
public class Test {
public static void main(String[] args) {
fun(new Animal());
fun(new Bird());
fun(new Duck());
}
public static void fun (Animal animal){
animal.eat();
}
public static void fun (Bird bird){
bird.eat();
}
public static void fun (Duck duck){
duck.eat();
}
}
假如我们调用一个动物类的输出自己名字的功能,我们如果像以前一样,那么我们需要重载所有动物的动物类的fun功能的方法,在自然界中,动物的子类有几百万种,那么就要将fun写上几百万次?这显然是不可能的,既然任何动物的子类都是动物,我们为什么不能让动物来指代这些子类,所以我们引入了向上转型
向上转型
- 语法 父类名称 父类引用=new 子类对象();
- 前提 向上转型必须发生在有继承关系的类之间,可以不是之间子类,可以是子孙类,反正满足 is a的关系就行
- 意义通过最顶层的父类引用,指代所有的子类对象
引入向上转型后的代码
class Animal {
public String name="Animal";
public void eat(){
System.out.println(name+"的eat方法");
}
}
class Bird extends Animal{
public String name="Bird";
public void eat(){
System.out.println(name+"的eat方法");
}
}
class Duck extends Bird{
public String name="Duck";
public void eat(){
System.out.println(name+"的eat方法");
}
}
public class Test {
public static void main(String[] args) {
fun(new Animal());
fun(new Bird());
fun(new Duck());
}
public static void fun (Animal animal){
animal.eat();
}
}
我们引入了向上转型之后,我们只需要写一个fun方法,用顶层父类去指代所有的子类,就可以让所有子类实现这个功能,就非常方便
向上转型的时机
- 引用赋值的时候
- 方法传参的时候
- 方法返回值的时候
由向上转型引出多态
我们发现在fun这个方法中当我们传入不同的对象的时候,表现了出不同的eat方法(方法对应着行为) 我们将这种同一个引用,同一个方法名称,根据对象的不同表现出不同的行为称为多态
多态的前提——方法重写(override)
为什么在刚刚由相同的引用,相同的方法名,表现出了不同的行为,因为在子类中重写了eat方法。
概念
发生在有继承关系的类之间,子类定义了和父类除了权限不同,其他全部相同的方法,这一组方法被称为方法重写
如果在子类中没有重写方法,会调用谁的方法呢(就近原则,先碰到谁,就直接调用)
如果在Duck类中没有重写eat方法,那么调用结果是
如果在Duck和Bird都没有去重写eat方法,那么调用结果是 如何分辨到底调用了谁的方法
向上转型时,我们在看调用的时候,不要看前半部分引用类型是什么,主要看new的是那个类,如果这个类重写了相关的方法,则调用的一定是重写后的方法,如果new的这个类中没有重写这个方法,就遵循就近原则去向上寻找
方法重写与限权的关系
当发生重写的时候,子类权限必须要大于等于父类权限(不包括private)
- 当父类权限是public 子类为protected时 protected
-
当父类权限为protected 子类也为protected时 protected==protected
-
当父类权限为protetced 子类为public public>protected
-
当父类为private 子类为public (这个规则类名不包括privtae)
为什么不包括private权限呢?
package polymorphism;
class Bird extends Animal{
public String name="Bird";
public void eat(){
System.out.println(name+"的eat方法");
}
}
class Duck extends Bird{
public String name="Duck";
public void eat(){
System.out.println(name+"的eat方法");
}
}
public class Animal {
public String name="Animal";
private void eat(){
System.out.println(name+"的eat方法");
}
public static void main(String[] args) {
fun(new Animal());
fun(new Bird());
fun(new Duck());
}
public static void fun (Animal animal){
animal.eat();
}
}
为什么我们在每个子类重写了eat方法,调用之后都是Animal的方法,因为Animal的方法是privtae,它的权限只能在本类中可用,你的子类继承也没什么用,因为只能在本类中调用(本类调用,也只是本类的方法),在子类中根本就不知道这个方法的存在,那么何来重写之说,那用private权限修饰,还重写就没有任何意义
重写和static的关系
我们重写的本质是为了多态,多态的本质是调用不同的对象,这些对象的类重写了父类的方法,从而表现出了不同的行为特性,只要涉及到对象就跟static无关,所以方法重写只发生在普通方法
重写方法的返回值问题
在一般情况下,重写的方法的返回值必须是一样的,不然就会报错,但是存在一种特殊情况,就是在继承关系中,可以至少是向上转型的类
检查重写的手段@Override来检查
一道题加深重写和继承
重写(Override)和重载(Overload)的区别
No 区别 重载 重写 1 概念 方法名相同,参数的类型以及个数不同 方法名称,返回值类型(多态中有一种特殊情况),参数类型及个数完全相同 2 范围 一个类 继承关系 3 限制 没有权限要求 被覆写的方法不能拥有比父类更严格的访问权限 4 static 没有要求 不能覆写static方法
向下转型
为什么引入向下转型?
我们可以看到Duck类中,我们只能调用Animal中有的方法,而不能去调用Duck特有的swim方法,我们引入多态调用方法的规则
我们知道引入向上转型就是为了写代码方便,肯定是要用的,所以我们引入了向下转型
如何理解向下转型?
从上知道,我们向下转型是有前提的,如果随意向下转,可能会报错,如何避免?
instanceof——>返回布尔值,表示引用指向的本质是不是该类的对象
什么时候使用向上转型?什么时候使用向下转型?
- 当一个方法接收一个类和当前类的子类,参数就为指定的父类引用,发生向上转型
- 当某个特殊情况下,需要使用子类的拓展的方法,那么就把这个原本向上转型的类向下转型为原子类引用
抽象类
向上转型的给我们带来的最大好处就是参数统一化,我们使用一个共同的父类引用,就能接收所有的子类对象
比如我们描述一个图像类,我们要输出它的形状,这是比较抽象的,无法具体化,所以图像类的每个子类都需要重写这个输出形状的方法
class Sharp {
public void print(){
}
}
class Circle extends Sharp{
public void print(){
System.out.println("●");
}
}
class Square extends Sharp{
public void print(){
System.out.println("▬");
}
}
public class Test {
public static void main(String[] args) {
new Circle().print();
new Square().print();
}
public static void fun(Sharp sharp){
sharp.print();
}
}
我们发现Sharp的子类想要调用ptint方法,必须要重写print方法,那如何保证子类必须必须去重写父类的print方法呢——>抽象方法(强制让子类去重写父类的抽象方法)
abstract class Sharp {
public abstract void print();
}
class Circle extends Sharp{
public void print(){
System.out.println("●");
}
}
class Square extends Sharp{
public void print(){
System.out.println("▬");
}
}
public class Test {
public static void main(String[] args) {
new Circle().print();
new Square().print();
}
public static void fun(Sharp sharp){
sharp.print();
}
}
如果在子类中不重写父类的抽象方法,则会报错
抽象的概念
super修饰构造方法
class China extends Person{
// public String name="china";
public China(){
System.out.println("这是Chian的无参构造");
}
public void fun(){
System.out.println(super.name);
}
public static void main(String[] args) {
China china=new China();
}
}
class Person extends Animal {
public Person (){
System.out.println("这是Person的无参构造");
}
public String name="person";
}
class China extends Person{
// public String name="china";
public China(){
System.out.println("这是Chian的无参构造");
}
public void fun(){
System.out.println(super.name);
}
public static void main(String[] args) {
China china=new China();
}
}
我们new一个China对象,会发生什么
当产生子类对象时,默认先产生父类对象,若父类对象还有继承,继承向上先产生祖类对象
如果将Person类写一个有参构造
我们再去new一个Chian对象,会怎么样
为什么会发生这样?因为在有继承关系的类中,子类的构造方法的第一行会默认会有super(),表示调用父类的无参构造方法,如果父类没有无参构造,那么就需要我们在子类的构造方法自己写super(有参构造),表示调用父类的有参构造,不然就会报错
this()和super()的关系
因为this()表示调用构造方法,也必须放在第一行,super()调用父类构造方法,也必须放在第一行,所以它两不能同时显式同时出现,
直接从父类中去寻找同名方法,跟属性差不多
class Person { public Person (){ System.out.println("这是Person的无参构造"); } public String name="person"; public void fun(){ System.out.println("这是Person的fun函数"); } } class China extends Person{ // public String name="china"; public China(){ System.out.println("这是Chian的无参构造"); } public void fun(){ System.out.println(super.name); } public void test(){ super.fun(); } public static void main(String[] args) { China china=new China(); china.test(); } }test调用的式Person的fun方法
但是super不能指代当前父类的对象引用
final的补充
- 修饰属性表示属性的值和类型都不能改变
- 修饰类,表示这个类无法被继承
多态思想
多态:一个引用可以表现为多种行为/特性
首先我们来看编程中会存储的一个问题
class Animal {
public String name="Animal";
public void eat(){
System.out.println(name+"的eat方法");
}
}
class Bird extends Animal{
public String name="Bird";
public void eat(){
System.out.println(name+"的eat方法");
}
}
class Duck extends Bird{
public String name="Duck";
public void eat(){
System.out.println(name+"的eat方法");
}
}
public class Test {
public static void main(String[] args) {
fun(new Animal());
fun(new Bird());
fun(new Duck());
}
public static void fun (Animal animal){
animal.eat();
}
public static void fun (Bird bird){
bird.eat();
}
public static void fun (Duck duck){
duck.eat();
}
}
假如我们调用一个动物类的输出自己名字的功能,我们如果像以前一样,那么我们需要重载所有动物的动物类的fun功能的方法,在自然界中,动物的子类有几百万种,那么就要将fun写上几百万次?这显然是不可能的,既然任何动物的子类都是动物,我们为什么不能让动物来指代这些子类,所以我们引入了向上转型
向上转型
- 语法 父类名称 父类引用=new 子类对象();
- 前提 向上转型必须发生在有继承关系的类之间,可以不是之间子类,可以是子孙类,反正满足 is a的关系就行
- 意义通过最顶层的父类引用,指代所有的子类对象
引入向上转型后的代码
class Animal {
public String name="Animal";
public void eat(){
System.out.println(name+"的eat方法");
}
}
class Bird extends Animal{
public String name="Bird";
public void eat(){
System.out.println(name+"的eat方法");
}
}
class Duck extends Bird{
public String name="Duck";
public void eat(){
System.out.println(name+"的eat方法");
}
}
public class Test {
public static void main(String[] args) {
fun(new Animal());
fun(new Bird());
fun(new Duck());
}
public static void fun (Animal animal){
animal.eat();
}
}
我们引入了向上转型之后,我们只需要写一个fun方法,用顶层父类去指代所有的子类,就可以让所有子类实现这个功能,就非常方便
向上转型的时机
- 引用赋值的时候
- 方法传参的时候
- 方法返回值的时候
由向上转型引出多态
我们发现在fun这个方法中当我们传入不同的对象的时候,表现了出不同的eat方法(方法对应着行为) 我们将这种同一个引用,同一个方法名称,根据对象的不同表现出不同的行为称为多态
多态的前提——方法重写(override)
为什么在刚刚由相同的引用,相同的方法名,表现出了不同的行为,因为在子类中重写了eat方法。
概念
发生在有继承关系的类之间,子类定义了和父类除了权限不同,其他全部相同的方法,这一组方法被称为方法重写
如果在子类中没有重写方法,会调用谁的方法呢(就近原则,先碰到谁,就直接调用)
如果在Duck类中没有重写eat方法,那么调用结果是
如果在Duck和Bird都没有去重写eat方法,那么调用结果是 如何分辨到底调用了谁的方法
向上转型时,我们在看调用的时候,不要看前半部分引用类型是什么,主要看new的是那个类,如果这个类重写了相关的方法,则调用的一定是重写后的方法,如果new的这个类中没有重写这个方法,就遵循就近原则去向上寻找
方法重写与限权的关系
当发生重写的时候,子类权限必须要大于等于父类权限(不包括private)
- 当父类权限是public 子类为protected时 protected
-
当父类权限为protected 子类也为protected时 protected==protected
-
当父类权限为protetced 子类为public public>protected
-
当父类为private 子类为public (这个规则类名不包括privtae)
为什么不包括private权限呢?
package polymorphism;
class Bird extends Animal{
public String name="Bird";
public void eat(){
System.out.println(name+"的eat方法");
}
}
class Duck extends Bird{
public String name="Duck";
public void eat(){
System.out.println(name+"的eat方法");
}
}
public class Animal {
public String name="Animal";
private void eat(){
System.out.println(name+"的eat方法");
}
public static void main(String[] args) {
fun(new Animal());
fun(new Bird());
fun(new Duck());
}
public static void fun (Animal animal){
animal.eat();
}
}
为什么我们在每个子类重写了eat方法,调用之后都是Animal的方法,因为Animal的方法是privtae,它的权限只能在本类中可用,你的子类继承也没什么用,因为只能在本类中调用(本类调用,也只是本类的方法),在子类中根本就不知道这个方法的存在,那么何来重写之说,那用private权限修饰,还重写就没有任何意义
重写和static的关系
我们重写的本质是为了多态,多态的本质是调用不同的对象,这些对象的类重写了父类的方法,从而表现出了不同的行为特性,只要涉及到对象就跟static无关,所以方法重写只发生在普通方法
重写方法的返回值问题
在一般情况下,重写的方法的返回值必须是一样的,不然就会报错,但是存在一种特殊情况,就是在继承关系中,可以至少是向上转型的类
检查重写的手段@Override来检查
一道题加深重写和继承
重写(Override)和重载(Overload)的区别
No 区别 重载 重写 1 概念 方法名相同,参数的类型以及个数不同 方法名称,返回值类型(多态中有一种特殊情况),参数类型及个数完全相同 2 范围 一个类 继承关系 3 限制 没有权限要求 被覆写的方法不能拥有比父类更严格的访问权限 4 static 没有要求 不能覆写static方法
向下转型
为什么引入向下转型?
我们可以看到Duck类中,我们只能调用Animal中有的方法,而不能去调用Duck特有的swim方法,我们引入多态调用方法的规则
我们知道引入向上转型就是为了写代码方便,肯定是要用的,所以我们引入了向下转型
如何理解向下转型?
从上知道,我们向下转型是有前提的,如果随意向下转,可能会报错,如何避免?
instanceof——>返回布尔值,表示引用指向的本质是不是该类的对象
什么时候使用向上转型?什么时候使用向下转型?
- 当一个方法接收一个类和当前类的子类,参数就为指定的父类引用,发生向上转型
- 当某个特殊情况下,需要使用子类的拓展的方法,那么就把这个原本向上转型的类向下转型为原子类引用
抽象类
向上转型的给我们带来的最大好处就是参数统一化,我们使用一个共同的父类引用,就能接收所有的子类对象
比如我们描述一个图像类,我们要输出它的形状,这是比较抽象的,无法具体化,所以图像类的每个子类都需要重写这个输出形状的方法
class Sharp {
public void print(){
}
}
class Circle extends Sharp{
public void print(){
System.out.println("●");
}
}
class Square extends Sharp{
public void print(){
System.out.println("▬");
}
}
public class Test {
public static void main(String[] args) {
new Circle().print();
new Square().print();
}
public static void fun(Sharp sharp){
sharp.print();
}
}
我们发现Sharp的子类想要调用ptint方法,必须要重写print方法,那如何保证子类必须必须去重写父类的print方法呢——>抽象方法(强制让子类去重写父类的抽象方法)
abstract class Sharp {
public abstract void print();
}
class Circle extends Sharp{
public void print(){
System.out.println("●");
}
}
class Square extends Sharp{
public void print(){
System.out.println("▬");
}
}
public class Test {
public static void main(String[] args) {
new Circle().print();
new Square().print();
}
public static void fun(Sharp sharp){
sharp.print();
}
}
如果在子类中不重写父类的抽象方法,则会报错
抽象的概念
- 修饰属性表示属性的值和类型都不能改变
- 修饰类,表示这个类无法被继承
多态:一个引用可以表现为多种行为/特性
首先我们来看编程中会存储的一个问题
class Animal { public String name="Animal"; public void eat(){ System.out.println(name+"的eat方法"); } } class Bird extends Animal{ public String name="Bird"; public void eat(){ System.out.println(name+"的eat方法"); } } class Duck extends Bird{ public String name="Duck"; public void eat(){ System.out.println(name+"的eat方法"); } } public class Test { public static void main(String[] args) { fun(new Animal()); fun(new Bird()); fun(new Duck()); } public static void fun (Animal animal){ animal.eat(); } public static void fun (Bird bird){ bird.eat(); } public static void fun (Duck duck){ duck.eat(); } }假如我们调用一个动物类的输出自己名字的功能,我们如果像以前一样,那么我们需要重载所有动物的动物类的fun功能的方法,在自然界中,动物的子类有几百万种,那么就要将fun写上几百万次?这显然是不可能的,既然任何动物的子类都是动物,我们为什么不能让动物来指代这些子类,所以我们引入了向上转型
向上转型
- 语法 父类名称 父类引用=new 子类对象();
- 前提 向上转型必须发生在有继承关系的类之间,可以不是之间子类,可以是子孙类,反正满足 is a的关系就行
- 意义通过最顶层的父类引用,指代所有的子类对象
引入向上转型后的代码
class Animal { public String name="Animal"; public void eat(){ System.out.println(name+"的eat方法"); } } class Bird extends Animal{ public String name="Bird"; public void eat(){ System.out.println(name+"的eat方法"); } } class Duck extends Bird{ public String name="Duck"; public void eat(){ System.out.println(name+"的eat方法"); } } public class Test { public static void main(String[] args) { fun(new Animal()); fun(new Bird()); fun(new Duck()); } public static void fun (Animal animal){ animal.eat(); } }我们引入了向上转型之后,我们只需要写一个fun方法,用顶层父类去指代所有的子类,就可以让所有子类实现这个功能,就非常方便
向上转型的时机
- 引用赋值的时候
- 方法传参的时候
- 方法返回值的时候
由向上转型引出多态
我们发现在fun这个方法中当我们传入不同的对象的时候,表现了出不同的eat方法(方法对应着行为) 我们将这种同一个引用,同一个方法名称,根据对象的不同表现出不同的行为称为多态
多态的前提——方法重写(override)
为什么在刚刚由相同的引用,相同的方法名,表现出了不同的行为,因为在子类中重写了eat方法。
概念
发生在有继承关系的类之间,子类定义了和父类除了权限不同,其他全部相同的方法,这一组方法被称为方法重写
如果在子类中没有重写方法,会调用谁的方法呢(就近原则,先碰到谁,就直接调用)
如果在Duck类中没有重写eat方法,那么调用结果是
如果在Duck和Bird都没有去重写eat方法,那么调用结果是 如何分辨到底调用了谁的方法
向上转型时,我们在看调用的时候,不要看前半部分引用类型是什么,主要看new的是那个类,如果这个类重写了相关的方法,则调用的一定是重写后的方法,如果new的这个类中没有重写这个方法,就遵循就近原则去向上寻找
方法重写与限权的关系
当发生重写的时候,子类权限必须要大于等于父类权限(不包括private)
- 当父类权限是public 子类为protected时 protected
当父类权限为protected 子类也为protected时 protected==protected
当父类权限为protetced 子类为public public>protected
当父类为private 子类为public (这个规则类名不包括privtae)
为什么不包括private权限呢?
package polymorphism; class Bird extends Animal{ public String name="Bird"; public void eat(){ System.out.println(name+"的eat方法"); } } class Duck extends Bird{ public String name="Duck"; public void eat(){ System.out.println(name+"的eat方法"); } } public class Animal { public String name="Animal"; private void eat(){ System.out.println(name+"的eat方法"); } public static void main(String[] args) { fun(new Animal()); fun(new Bird()); fun(new Duck()); } public static void fun (Animal animal){ animal.eat(); } }为什么我们在每个子类重写了eat方法,调用之后都是Animal的方法,因为Animal的方法是privtae,它的权限只能在本类中可用,你的子类继承也没什么用,因为只能在本类中调用(本类调用,也只是本类的方法),在子类中根本就不知道这个方法的存在,那么何来重写之说,那用private权限修饰,还重写就没有任何意义
重写和static的关系
我们重写的本质是为了多态,多态的本质是调用不同的对象,这些对象的类重写了父类的方法,从而表现出了不同的行为特性,只要涉及到对象就跟static无关,所以方法重写只发生在普通方法
重写方法的返回值问题
在一般情况下,重写的方法的返回值必须是一样的,不然就会报错,但是存在一种特殊情况,就是在继承关系中,可以至少是向上转型的类
检查重写的手段@Override来检查一道题加深重写和继承
重写(Override)和重载(Overload)的区别
No 区别 重载 重写 1 概念 方法名相同,参数的类型以及个数不同 方法名称,返回值类型(多态中有一种特殊情况),参数类型及个数完全相同 2 范围 一个类 继承关系 3 限制 没有权限要求 被覆写的方法不能拥有比父类更严格的访问权限 4 static 没有要求 不能覆写static方法
向下转型
为什么引入向下转型?
我们可以看到Duck类中,我们只能调用Animal中有的方法,而不能去调用Duck特有的swim方法,我们引入多态调用方法的规则
我们知道引入向上转型就是为了写代码方便,肯定是要用的,所以我们引入了向下转型
如何理解向下转型?
从上知道,我们向下转型是有前提的,如果随意向下转,可能会报错,如何避免?
instanceof——>返回布尔值,表示引用指向的本质是不是该类的对象
什么时候使用向上转型?什么时候使用向下转型?
- 当一个方法接收一个类和当前类的子类,参数就为指定的父类引用,发生向上转型
- 当某个特殊情况下,需要使用子类的拓展的方法,那么就把这个原本向上转型的类向下转型为原子类引用
抽象类
向上转型的给我们带来的最大好处就是参数统一化,我们使用一个共同的父类引用,就能接收所有的子类对象
比如我们描述一个图像类,我们要输出它的形状,这是比较抽象的,无法具体化,所以图像类的每个子类都需要重写这个输出形状的方法
class Sharp {
public void print(){
}
}
class Circle extends Sharp{
public void print(){
System.out.println("●");
}
}
class Square extends Sharp{
public void print(){
System.out.println("▬");
}
}
public class Test {
public static void main(String[] args) {
new Circle().print();
new Square().print();
}
public static void fun(Sharp sharp){
sharp.print();
}
}
我们发现Sharp的子类想要调用ptint方法,必须要重写print方法,那如何保证子类必须必须去重写父类的print方法呢——>抽象方法(强制让子类去重写父类的抽象方法)
abstract class Sharp {
public abstract void print();
}
class Circle extends Sharp{
public void print(){
System.out.println("●");
}
}
class Square extends Sharp{
public void print(){
System.out.println("▬");
}
}
public class Test {
public static void main(String[] args) {
new Circle().print();
new Square().print();
}
public static void fun(Sharp sharp){
sharp.print();
}
}
如果在子类中不重写父类的抽象方法,则会报错
抽象的概念
为什么引入向下转型?
我们可以看到Duck类中,我们只能调用Animal中有的方法,而不能去调用Duck特有的swim方法,我们引入多态调用方法的规则
我们知道引入向上转型就是为了写代码方便,肯定是要用的,所以我们引入了向下转型
如何理解向下转型?
从上知道,我们向下转型是有前提的,如果随意向下转,可能会报错,如何避免?
instanceof——>返回布尔值,表示引用指向的本质是不是该类的对象
什么时候使用向上转型?什么时候使用向下转型?
- 当一个方法接收一个类和当前类的子类,参数就为指定的父类引用,发生向上转型
- 当某个特殊情况下,需要使用子类的拓展的方法,那么就把这个原本向上转型的类向下转型为原子类引用
向上转型的给我们带来的最大好处就是参数统一化,我们使用一个共同的父类引用,就能接收所有的子类对象
比如我们描述一个图像类,我们要输出它的形状,这是比较抽象的,无法具体化,所以图像类的每个子类都需要重写这个输出形状的方法
class Sharp {
public void print(){
}
}
class Circle extends Sharp{
public void print(){
System.out.println("●");
}
}
class Square extends Sharp{
public void print(){
System.out.println("▬");
}
}
public class Test {
public static void main(String[] args) {
new Circle().print();
new Square().print();
}
public static void fun(Sharp sharp){
sharp.print();
}
}
我们发现Sharp的子类想要调用ptint方法,必须要重写print方法,那如何保证子类必须必须去重写父类的print方法呢——>抽象方法(强制让子类去重写父类的抽象方法)
abstract class Sharp {
public abstract void print();
}
class Circle extends Sharp{
public void print(){
System.out.println("●");
}
}
class Square extends Sharp{
public void print(){
System.out.println("▬");
}
}
public class Test {
public static void main(String[] args) {
new Circle().print();
new Square().print();
}
public static void fun(Sharp sharp){
sharp.print();
}
}
如果在子类中不重写父类的抽象方法,则会报错
抽象的概念



