目录
多态[面向对象的三大特征:封装 继承 多态]polymorphism
多态的好处
多态下的类型转换问题
注意事项
final关键字
注意事项
常量
抽象类abstract
好处
运用场景
接口interface
好处
接口中新增的三种方法:
接口的多继承
内部类[类中五大成分:成员变量、方法、构造器、内部类、代码块]
4、匿名内部类
多态[面向对象的三大特征:封装 继承 多态]polymorphism
多态是在继承/实现情况下的一种现象,表现为:对象多态、行为多态
例如:p1为父类,p2 extends p1,p3 extends p1
p1 p = new p2();
p1 pp = new p3();
编译看左边,执行看右边 例如:p.run(); 它的左边是p1,右边是p2 -> 行为多态
对于对象,编译、执行都看左边 例如:p.name; 打印的仍是p1对其的赋值,而不是p2
因此,多态的前提:有继承/实现关系;存在父类引用子类对象;存在方法重写
多态的注意事项:多态是对象、行为的多态,Java中的属性(成员变量)不谈多态
多态的好处
在多态形式下,右边对象是解耦合的,更便于扩展的维护 [紧耦合就很难修改]
可以使用父类类型的变量作为形参,可以接收一切子类对象 [某方法可以被多类对象使用,因为可以用父类做形参]
问题:多态下不能使用子类的独有功能。[因为编译看左边,左边父类没有子类独有功能]
多态下的类型转换问题
自动类型转换 父类 变量名 = new 子类();
强制类型转换 子类 变量名 = (子类) 父类变量
注意事项
存在继承/实现关系就可以在编译阶段进行强制类型转换,编译阶段不会报错哦
运行时,如果发现对象的真实类型与强转后的类型不同,就会报类型转换异常ClassCastException的错误出来
因此,使用instanceof关键字,判断当前对象的真实类型,再强转
例如:p1为父类,p2 extends p1
p1 p = new p2();
if(p instanceof p2){
p2 ppp = (p2) p; // 强转
}
final关键字
修饰类:该类则为最终类,不能被继承了
修饰方法:最终方法,不能被重写了
修饰变量:该变量只能被赋值一次[变量:1、局部变量 2、成员变量 2.1、静态成员变量 2.2、实例成员变量]
public static final 常量;
注意事项
final修饰基本类型变量,变量存储的数据不能改变
final修饰引用类型变量,变脸存储的地址不能改变,但地址所指向对象的内容可以改变
常量
用于记录系统的配置信息
static final 常量; 常量名用大写英文字母,下划线连接单词
好处:代码可读性更好,可维护性更好;程序编译后,常量会被“宏替换”,出现常量的地方会被替换为其记住的字面量,这样可以保证使用常量和使用字面量的性能是一样的
抽象类abstract
abstract修饰类 -> 抽象类
abstract修饰方法 -> 抽象方法 只有签名,没有方法体
抽象类不一定有抽象方法,有抽象方法的类一定是抽象类
抽象类可以有类有的成员(成员变量、方法、构造器)
抽象类的主要特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现,故父类 变量名 = new 子类();
一个类继承抽象类,要重写抽象类的所有抽象方法,否则这个类也必须被定义为抽象类
好处
父类知道每个子类都要做的某个行为,但每个子类要做的情况不一样,父类就定义成抽象方法,交给子类去重写实现,可以更好的支持多态。[感觉就是父类定义一个模板,子类去挨个填充]
运用场景
解决方法中存在重复代码的问题:
1、定义抽象类
2、在里面定义两个方法:一是模板方法:把相同代码放进去;二是抽象方法,具体实现交给子类
建议用final修饰模板方法[不被子类重写]
接口interface
只能由成员变量和成员方法,其他什么都不能有
public interface 接口名{
// 成员变量(常量)
// 成员方法(抽象方法)
}
类和接口是同级的,因此要在package里新建class中选interface而不是class
直接写String 变量名 = ...;就行,会默认是常量,用大写英文单词,下划线连接哈
抽象方法也直接写签名就行,默认是抽象方法,不能写方法体哦
接口不能创建对象,接口是用来被类实现implement的,实现接口的类被称为实现类
修饰符 class 实现类 implements 接口1,接口2,接口3,...{}
一个类可以实现多个接口,实现类实现多个接口,必须重写完全部接口的全部抽象方法,否则实现类需定义成抽象类
好处
弥补了类单继承的不足(extends 只能继承一个),一个类可以实现多个接口;让程序可以面向接口编程,这样可以灵活方便切换各种业务实现
例子:两种方案实现某功能,只需修改选择哪个实现类
StudentOperator[接口]
package cn.ptz.interface_demo;
import java.util.ArrayList;
public interface StudentOperator {
void printAllInofo(ArrayList<Student>students);
void printAverageScore(ArrayList<Student>students);
}
StudentOperatormpl1[实现类1]第一个方案
package cn.ptz.interface_demo;
import java.util.ArrayList;
public class StudentOperatormpl1 implements StudentOperator{
@Override
public void printAllInofo(ArrayList<Student> students) {
System.out.println("————全部全部学生信息");
for (int i = 0; i < students.size(); i++) {
Student s = students.get(i);
System.out.println("姓名:"+s.getName()+",性别:"+s.getSex()+",分数:"+s.getScore());
}
System.out.println("————————————————————");
}
@Override
public void printAverageScore(ArrayList<Student> students) {
double allScore = 0.0;
for (int i = 0; i < students.size(); i++) {
Student s = students.get(i);
allScore += s.getScore();
}
System.out.println("平均分:"+ (allScore) / students.size());
}
}
StudentOperatormpl2[实现类2]第二个方案
package cn.ptz.interface_demo;
import java.util.ArrayList;
public class StudentOperatormpl2 implements StudentOperator {
@Override
public void printAllInofo(ArrayList<Student> students) {
System.out.println("————全部全部学生信息");
int count1 = 0;
int count2 = 0;
for (int i = 0; i < students.size(); i++) {
Student s = students.get(i);
System.out.println("姓名:"+s.getName()+",性别:"+s.getSex()+",分数:"+s.getScore());
if (s.getSex() == '男'){
count1++;
}else {
count2++;
}
}
System.out.println("男生人数:" + count1 + ",女生人数:" + count2);
System.out.println("————————————————————");
}
@Override
public void printAverageScore(ArrayList<Student> students) {
double allScore = 0.0;
double max = students.get(0).getScore();
double min = students.get(0).getScore();
for (int i = 0; i < students.size(); i++) {
Student s = students.get(i);
if (s.getScore() > max){
max = s.getScore();
}
if (s.getScore() < min){
min = s.getScore();
}
allScore += s.getScore();
}
System.out.println("最高分:" + max + ",最低分:" + min);
System.out.println("平均分:"+ (allScore-max-min) / (students.size()-2));
}
}
ClassManager
package cn.ptz.interface_demo;
import java.util.ArrayList;
public class ClassManager {
private ArrayList<Student> students = new ArrayList<>();
private StudentOperator studentOperator = new StudentOperatormpl1(); //private StudentOperator studentOperator = new StudentOperatormpl2();
public ClassManager(){
students.add(new Student("小a",'女',80));
students.add(new Student("小b",'男',75));
students.add(new Student("小c",'女',95));
students.add(new Student("小d",'男',88));
}
// 打印全班学生信息
public void printInfo(){
studentOperator.printAllInofo(students);
}
public void printScore(){
studentOperator.printAverageScore(students);
}
}
接口中新增的三种方法:
1.默认方法,必须用default修饰,默认被public修饰
实例方法:对象的方法,必须使用实现类的对象来访问
2.私有方法:必须使用private修饰
实例方法:对象的方法,只能在接口内部被调用
3.类方法(静态方法):必须用static修饰,默认被public修饰
只能用接口名来调用
增强了接口能力,更便于项目扩展和维护
接口的多继承
一个接口可以同时继承多个接口
注意事项:
1.一个接口可以同时继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承
2.一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现
3.一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的
4.一个类实现了多个接口,多个接口中存在同名的默认方法(default),可以不冲突,这个类重写该方法即可
内部类[类中五大成分:成员变量、方法、构造器、内部类、代码块]
如果一个类定义在另一个类的内部,这个类就是内部类
1、成员内部类
类中的普通成员,类似普通的成员变量、成员方法
外部类名.内部类名 对象名 = new 外部类(...).new 内部类(...);
2、静态内部类
有static修饰
外部类名.内部类名 对象名 = new 外部类.内部类();
可以直接访问外部类的静态成员,不能直接访问外部类的实例成员
3、局部内部类
定义在方法中、代码块中、构造器等执行体中
4、匿名内部类
特殊的局部内部类
匿名:不需要为这个类声明名字
其实是new 类名()
这个类名的子类
所以是创建了一个子类对象
更方便地创建一个子类对象
通常作为一个参数传输给方法
简化代码