多态
分类:
编译时多态
方法重载:方法名 参数列表不同
在编译期间可以根据实参的个数以及数据类型确定调用哪个重载的方法
运行时多态
向上造型+方法重写
运行时多态基于继承
向上造型:
定义一个父类的引用指向子类的对象
格式:
父类 引用名 = new 子类();
例如:
Pet pet = new Cat();
注意:
1、向上造型中,如果子类重写了父类的方法,则实际运行时调用的是子类重写之后的方法;
Pet pet = new Cat();
pet.eat();//这里调用的是Cat对象重写之后的方法
2、编译看左边 运行看右边
编译时调用的方法会检查在左边对应的类型中是否包含,如果调用的方法左边的类型中并不包含则编译报错
如果包含编译通过,但是具体调用谁得运行时才能确定
向上造型的常见的使用场景:
1、可以将多态使用在类的属性类型 方法的参数
可以将父类型作为参数,实参可以是各种子类类型–灵活
public class Pet {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//父类的方法
public void eat(){
System.out.println("吃~~");
}
public void play(){
System.out.println("玩~~");
}
}
public class Cat extends Pet{
//重写父类的eat方法
@Override
public void eat() {
System.out.println("猫抓老鼠~~");
super.eat();
}
public void sleep(){
System.out.println("睡觉~~");
}
}
public class Dog extends Pet{
//重写父类的eat方法
@Override
public void eat() {
System.out.println("狗刨骨头~~");
super.eat();
}
}
public class Master{
private Pet pet;
public Master(){}
//如果参数为具体的子类类型 则每个子类都需要定义一个对应的构造方法
// public Master(Cat cat){
// pet = cat;
// }
// public Master(Dog dog){
// pet = dog;
// }
// public Master(Bird bird){
// pet = bird;
// }
//参数指定为父类类型 可以接收任意子类类型的对象
public Master(Pet pet){
//编译时检查调用的方法Pet类型中是否含有
//运行时调用实际传入的子类对象的方法
//无论子类对象是什么 此处代码都无需改变--灵活
pet.eat();
pet.play();
this.pet = pet;
}
}
public class PetShop {
//当前宠物店所有的宠物 每个宠物都定义一个变量 --不可行
// private Cat cat;
// private Cat cat1;
//...
//数组来存储多个数据 每一类宠物都定义一个数组 --不完美
// private Cat[] cats;//宠物猫
// private Dog[] dogs;//宠物狗
// ...
//1只猫 2只狗 1只鸟
//pets[0] = new Cat(); 向上造型
//数组元素为Pet 无论具体的宠物是Cat 还是Dog 都可以存储 非常灵活
private Pet[] pets = {new Cat(),new Dog(),new Dog(),new Bird()};
//获取宠物店所有宠物 并调用每个宠物的eat方法
public void showPets(){
//普通for循环
for (int i=0;i<=pets.length-1;i++) {
Pet pet = pets[i];
pet.eat();
System.out.println("===============");
}
//foreach循环
// for (Pet pet:pets) {
// pet.eat();
// System.out.println("===============");
// }
//这里输出是所有对象的地址值
// [cn.tedu.dt.Cat@28d93b30, cn.tedu.dt.Dog@1b6d3586, cn.tedu.dt.Dog@4554617c, cn.tedu.dt.Bird@74a14482]
// System.out.println(Arrays.toString(pets));
}
}
//测试宠物商店的案例 多态在数组元素的使用
PetShop shop = new PetShop()
shop.showPets();
3、多态作为方法返回值的使用
宠物店类中设计出售宠物方法
//向上造型应用在方法的返回值
//返回值为Pet 实际返回的是具体Cat Dog等的对象
public Pet sell(){
return new Dog();
}
向上造型:
灵活,提高代码的复用性
扩展性强
向下造型:
格式:
子类 引用名 = (子类)父类引用;
可以通过向下造型来调用对象独有的方法和属性;
注意:
只有当父类的引用指向的是实际的子类对象时,才能转;
ClassCastException 类型强转异常
当父类引用实际指向的子类对象和强转的类型对象不一致时
instanceof
判断类型
格式:
引用 instanceof 类型 ( if(pet instanceof Cat){ )
如果引用指向的对象是指定的类型,则返回true;否则返回false
一般会先通过instanceof判断类型,再进行向下造型;
方法重写:
五大原则:
两小两等一大
两等:
方法签名一致
返回值类型为void 基本数据类型
两小:
返回值类型为引用数据类型,子类的返回值应该和父类保持一致或者是其子类
原因:反证
public class Father{
public A method(){
System.out.println("这是父类的method的方法~~");
return new A();
}
}
public class Son extends Father{
@Override
public C method() {
super.method();
return new B();
}
}
class A extends C{}
class B extends A{}
测试:
Father father = new Son();
A a = father.method();
如果父类方法返回值为A,在测试类中调用method方法编译看左边,则需将返回值赋值给A类型的变量,但是实际运行时,调用子类重写的method方法返回值为C,C为A的父类, 父类对象赋值给子类引用,编译报错;
异常
一大:
子类的权限修饰符大于等于父类的权限修饰符
static关键字
概念:表示静态 可以修饰属性 方法 代码块
static修饰属性:
静态属性 类变量
格式:
static 属性类型 变量名[= 初始值];
使用方式:
建议:类名.静态属性名
可以提高程序的可读性
不建议:对象名.静态属性名
特点:
static修饰的属性和对象无关,而是随着类发生变化的;
一旦static发生变化,则所有对象该属性都会发生变化;
总而言之,static修饰的属性是属于类的;
注意:
静态属性通常定义在类中方法外;构造代码块 构造方法 普通方法可以使用定义号的静态属性,但是不能定义新的静态属性;
静态修饰方法
静态方法
格式:
权限修饰符 静态修饰符(static) 返回值 方法名([参数列表]){
方法体;
return 返回值;
}
类.静态方法名称();
注意:
静态方法不能直接调用非静态方法,或者可以先创建对象再通过对象来调用非静态方法;
静态代码块:
格式:
static{
代码块;
}
位置:
类中方法外
执行:
静态代码块只在类加载时执行一次
创建对象时
调用静态属性 静态方法时
使用场景:
对于所有对象都要用到的数据,可以放在static当中只在类加载时执行一次;
数据库的配置信息配置文件中
都配置文件的代码放到static当中 在类加载时就把配置数据读入到项目中
用到配置数据的地方直接用即可 不用重复读取
static关键字
静态属性
必须掌握 使用静态属性的场景
static修饰的数据 所有对象都共享同一份
静态方法
工具类中的方法都是静态方法
Arrays.toString();
静态代码块
掌握静态代码的执行:类加载时执行且执行一次
Java内存
JDK1.6
java堆(heap):对象
java虚拟机栈:
栈帧:一个栈帧一个方法
方法区:永久代
运行时常量池:类版本号 类名 方法 字段等
静态区:static修饰的
本地方法栈:native的方法
pc寄存器:程序计数器
下一个要执行的指令
JDK1.8
java堆(heap):对象
静态属性
java虚拟机栈:
栈帧:一个栈帧一个方法
方法区:jvm规范–元数据–本地内存
运行时常量池:类版本号 类名 方法 字段等
本地方法栈:native的方法
pc寄存器:程序计数器
下一个要执行的指令
JVM调优
总结
面向对象:
一切皆对象
面向过程和面向对象:
编程思想
**面向过程:**关注细节,不利于大型项目的分工合作;
**面向对象:**贴近人类思考问题的方式,专注于各自核心的功能;方便进行团队合作开发;
面向对象的三大特征:
封装 继承 多态
类和对象:
**类:**抽象的概念 一类事物的概括
**对象:**是类具体化 实例化
构造方法/构造器:
方法名和类名保持一致 没有返回值部分
程序会默认帮生成一个无参构造
自定义构造方法,程序不会再提供无参构造
可以是无参构造也可以有参构造
构造方法允许重载 (无参构造 有参构造)
初始化属性值(this.属性=形参)–》开发工具直接生成
this:
关键字
当前对象
this.属性
this.方法名()
this([参数列表]);
this语句放在首行
方法返回值:
return this;
方法的参数:
method(this);
public void method(Person p){}
构造代码块 局部代码块
构造代码块:
{代码块}
类中方法外
**作用:**初始化共同的属性
**执行特点:**没创建一个对象 无论调用哪个构造方法 都会执行一次
局部代码块:
{代码块}
方法内
**作用:**限制局部变量的生命周期
匿名对象:
创建对象时没有指定对象名
特点:只能直接使用一次
匿名对象.方法名();
方法的参数
方法的返回值
面向对象的三大特征:
封装:
共有的属性和行为封装到类中
私有化属性 提供公开的getter setter方法
封装的优点:
提高了代码复用性
安全性
继承:
将多个类共有的属性和方法提取到一个父类中,子类就无需再次编写,直接继承父类即可;
extends关键字
子类只能继承一个父类–单继承
保持了继承结构的单一性 调用方法时较为明确
子类可以继承父类所有的属性和方法 但是private修饰的则不可见 构造方法不能继承
继承的优点:
提高了代码的复用性
构建项目的整体的架构
多态:
编译时多态:
方法重载
运行时多态:
基于继承的;
向上造型 方法重写
向上造型:
将子类对象赋值给父类引用
父类 对象名 = new 子类();
子类 对象名1 = new 子类();
父类 对象名2 = 对象名1;
注意:
通过父类类型的对象名来调用方法时,编译看左边 运行看右边
向上造型的应用:
1、属性类型的定义
父类类型 属性;
属性 = 子类对象;
2、方法的参数
方法(父类类型 对象名)
方法(子类对象)
3、方法的返回值
父类类型 方法名(){
return 子类对象;
}
父类类型 变量 = 方法();
4、数组元素设置父类类型
每个元素都可以实际为不同的子类对象
多态的优点:
灵活
扩展性强
static:
静态
修饰属性 方法 代码块
静态修饰的内容都是属于类
**使用:**类名.静态属性 System.in System.out
类名.静态方法 Arrays.toString() Arrays.sort()
public static void main(String[] args){
直接调用方法必须静态方法
或者先创建对象 对象.非静态方法
}
静态属性:
在类加载时就要在静态区创建静态属性的空间,并赋值;
Java内存:堆 栈 方法区
JDK1.8之前:静态属性是存在方法区中–永久代
JDK1.8以及之后:静态属性 字符串常量池则到了堆中
注意:
构造代码块 构造方法中定义静态属性吗?
不可以
静态方法:
静态方法不能直接调用非静态方法
静态方法中能否定义静态属性?-- 不可以 静态方法必须调用才能执行
子类能否继承父类的静态方法?–可以
静态方法可以重载;
子类不能重写父类的静态方法;但是子类中可以存在和父类方法签名一致的静态方法;通过子类来调用该静态方法时,调用的是子类自己的;
静态代码块
格式:
static{
代码块;
}
执行特点:
只执行一次
创建对象
类.静态属性
类.静态方法
场景:
读取配置文件等 在整个程序只执行一次即可的
注意:
1、在静态代码块中不能定义静态属性 但是可以修改或者赋值
执行程序:
构造代码块 构造方法 静态代码块?
先静态后非静态
静态代码块-》构造代码块-》构造方法
自定义类的属性直接赋值 构造代码块 构造方法 静态代码块
静态代码块-》创建属性变量-》构造代码块-》构造方法
结果如下:
这是Father的静态代码块
这是Son的静态代码块
这是Father的构造代码块
这是Father的构造方法
这是Son的构造代码块
这是Son的构造方法
总结:先父后子 先静态后非静态