目录
视频教程传送门 -> https://www.bilibili.com/video/BV1Cv411372m?p=109
多态
同类型的对象,执行同一个行为,会表现出不同的行为特征。
多态的常见形式
父类类型 对象名称 = new 子类构造器;
接口 对象名称 = new 实现类构造器;
多态的前提:
有继承/实现关系;有父类引用指向子类对象;有方法重写
多态中成员访问特点:
方法调用 -> 编译看左边,运行看右边。(多态侧重行为多态)
变量调用 -> 编译、运行都看左边。
优势:
在多态形式下,右边对象可以实现解耦合,便于扩展和维护。
定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的一切子类对象。
问题:
多态下不能使用子类的独有功能 => 通过强制类型转换解决
多态下引用数据类型的类型转换
自动类型转换(从子到父):子类对象赋值给父类类型
强制类型转换(从父到子)
此时必须进行强制类型转换:子类 对象变量 = (子类)父类类型的变量
强制类型转换
- 可以转换成真正的子类类型,从而调用子类独有功能
- 有继承关系/实现的2个类型就可以进行强制转换,编译无问题
- 运行时,如果发现强制转换后的类型不是对象真实类型则报错
类型转换异常:ClassCastException
=> 建议强转转换前使用instanceof判断当前对象的真实类型,再进行强制转换
变量名 instanceof 真实类型
判断关键字左边的变量指向的对象的真实类型,是右边的类型或者是其子类类型返回true
【例】电脑USB接入功能,USB可以是多种设备
需求:
设计一个电脑对象,可以安装2个USB设备
鼠标 -> 被安装时可以完成接入、调用点击功能、拔出功能
键盘 -> 被安装时可以完成接入、调用打字功能、拔出功能
分析:
定义一个USB的接口(申明USB设备的规范必须是:可以接入和拔出)-> USB.java
提供2个USB实现类代表鼠标(KeyBoard.java)和键盘(Mouse.java),让其实现USB接口,并分别定义独有功能
创建电脑对象(Computer.java),创建2个USB实现类对象,分别安装到电脑中并触发功能的执行
接口 USB.java
package com.itheima.d4_polymorphic_test;
public interface USB {
void connect();
void unconnect();
}
实现类 KeyBoard.java
package com.itheima.d4_polymorphic_test;
/**
实现类(子类)
*/
public class KeyBoard implements USB{
private String name;
public KeyBoard(String name) {
this.name = name;
}
@Override
public void connect() {
System.out.println(name + "成功接入设备");
}
@Override
public void unconnect() {
System.out.println(name + "成功弹出设备");
}
/**
独有功能
*/
public void keyDown(){
System.out.println(name + "今天你的鲨鱼干嘛了>OO<");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
实现类 Mouse.java
package com.itheima.d4_polymorphic_test;
/**
实现类(子类)
*/
public class Mouse implements USB{
private String name;
public Mouse(String name) {
this.name = name;
}
@Override
public void connect() {
System.out.println(name + "成功接入设备");
}
@Override
public void unconnect() {
System.out.println(name + "成功弹出设备");
}
/**
独有功能
*/
public void click(){
System.out.println(name + "一键三连~~~");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Comupter.java 提供安装USB的入口(定义了一个方法installUSB)
package com.itheima.d4_polymorphic_test;
public class Computer {
/**
提供一个安装的入口:行为。
*/
public void installUSB(USB u){
u.connect();
// 独有功能
if(u instanceof Mouse){
Mouse m = (Mouse) u;
m.click();
}else if(u instanceof KeyBoard) {
KeyBoard k = (KeyBoard) u;
k.keyDown();
}
u.unconnect();
}
}
测试类 Test.java
package com.itheima.d4_polymorphic_test;
/**
USB设备模拟
1、定义USB接口:接入 拔出
2、定义2个USB的实现类:鼠标、键盘。
3、创建一个电脑对象,创建USB设备对象,安装启动。
*/
public class Test {
public static void main(String[] args) {
// a、创建电脑对象
Computer c = new Computer();
// b、创建USB设备对象
USB u = new Mouse("罗技鼠标");
c.installUSB(u);
System.out.println("----------------");
USB k = new KeyBoard("CHERRY键盘");
c.installUSB(k);
}
}
输出:
内部类
内部类就是定义在一个类里面的类,里面的类可以理解成(寄生),外部类可以理解成(宿主)。
静态内部类
格式:外部类名.内部类名 对象名 = new 外部类名.内部类构造器;
特点、使用与普通类是一样的,类有的成分它都有,只是位置在别的类里面。
可以直接访问外部类的静态成员,不能直接访问外部类的实例成员。
成员内部类
格式:外部类名.内部类名 对象名 = new 外部类构造器.new 内部类构造器();
例如:Outer.Inner in = new Outer().new Inner();
无static修饰,属于外部类的对象。
可以直接访问外部类的静态成员,实例方法中可以直接访问外部类的实例成员。
在成员内部类中访问所在外部类对象 ,格式:外部类名.this
局部内部类
放在方法、代码块、构造器等执行体中。
匿名内部类
不用定义实现类,匿名内部类直接构建一个对象代表子类对象
格式:
new 类|抽象类名|或者接口名() {
重写方法;
};
特点:
匿名内部类是一个没有名字的内部类。
匿名内部类写出来就会产生一个匿名内部类的对象。
匿名内部类的对象类型相当于是当前new的那个的类型的子类类型。
Animal a = new Animal() {
public void run() {
System.out.println("鲨鲨游啊游");
}
};
a. run();
作用:方便创建子类对象,最终目的为了简化代码编写。
推荐阅读:
java中的内部类总结 https://www.cnblogs.com/nerxious/archive/2013/01/24/2875649.html
JAVA匿名内部类 https://www.cnblogs.com/yixianyixian/p/3810118.html
JAVA匿名内部类(Anonymous Classes) https://www.cnblogs.com/wuhenzhidu/p/anonymous.html
Lambda表达式
作用 => 简化匿名内部类的代码写法
Lambda表达式的简化格式
(匿名内部类被重写方法的形参列表) -> {
被重写方法的方法体代码
}
说明:"->" 是语法形式,无实际含义
注意:Lambda表达式只能简化函数式接口的匿名内部类的写法形式
函数式接口 => 首先必须是接口、其次接口中有且仅有一个抽象方法
【例】简化匿名内部类
package com.test.d9_lambda;
public class LambdaDemo2 {
public static void main(String[] args) {
// 使用Lambda的标准格式简化匿名内部类的代码形式
// 注意:Lambda只能简化接口中只有一个抽象方法的匿名内部类形式(函数式接口)
//eg1,实现类重写接口方法00
// Swimming s1 = new Swimming() {
// @Override
// public void swim() {
// System.out.println("老师游泳贼溜~~~~~");
// }
// };
//eg1,简化匿名内部类01
// Swimming s1 = () -> {
// System.out.println("老师游泳贼溜~~~~~");
// };
//eg1,大括号内只有一行代码,进一步简化匿名内部类02
Swimming s1 = () -> System.out.println("老师游泳贼溜~~~~~");
go(s1);
System.out.println("---------------------");
//eg2,go()中直接传入匿名内部类
// go(new Swimming() {
// @Override
// public void swim() {
// System.out.println("学生游泳很开心~~~");
// }
// });
//eg2,简化匿名内部类01
// go(() ->{
// System.out.println("学生游泳很开心~~~");
// });
//eg2,进一步简化匿名内部类02,注意分号
go(() -> System.out.println("学生游泳很开心~~~"));
}
public static void go(Swimming s){
System.out.println("开始。。。");
s.swim();
System.out.println("结束。。。");
}
}
@FunctionalInterface // 一旦加上这个注解必须是函数式接口,里面只能有一个抽象方法
interface Swimming{
void swim();
}
输出:
开始。。。
老师游泳贼溜~~~~~
结束。。。
---------------------
开始。。。
学生游泳很开心~~~
结束。。。
Lambda表达式的省略写法
1)参数类型可以省略不写
2)如果只有一个参数,"()"也可以省略
3)如果Lambda表达式的方法体代码只有一行代码,可以省略大括号不写,同时要省略分号
此时,如果这行代码是return语句,必须省略return不写
package com.test.d9_lambda;
import javax.swing.*;
import java.util.Arrays;
public class LambdaDemo3 {
public static void main(String[] args) {
Integer[] ages1 = {34, 12, 42, 23};
//1.匿名内部类,重写比较方法
// Arrays.sort(ages1, new Comparator<Integer>() {
// @Override
// public int compare(Integer o1, Integer o2) {
// return o2 - o1; // 降序
// }
// });
//2.Lambda表达式简化
// Arrays.sort(ages1, (Integer o1, Integer o2) -> {
// return o2 - o1; // 降序
// });
//3.进一步简化,参数类型不写
// Arrays.sort(ages1, ( o1, o2) -> {
// return o2 - o1; // 降序
// });
//4.再进一步简化,一行代码大括号不写,省略return不写
Arrays.sort(ages1, ( o1, o2 ) -> o2 - o1 );
System.out.println(Arrays.toString(ages1));
}
}
输出:[42, 34, 23, 12]