多态
- 前提: 存在继承/实现关系,存在父类引用子类对象,存在方法重写
- 多态是对象、行为的多态。属性(成员变量)不谈多态
- 多态是指: 父亲类型 变量名 = new 子类对象
package demo0320;
public class Father {
String name = "父亲";
public void run(){
System.out.println("父亲正在奔跑");
}
}
package demo0320;
public class SonFrist extends Father{
String name = "儿子1";
@Override
public void run(){
System.out.println("孩子1正在奔跑");
}
}
package demo0320;
public class SonSecond extends Father{
String name = "儿子2";
@Override
public void run(){
System.out.println("孩子2正在奔跑");
}
}
package demo0320;
public class test {
public static void main(String[] args) {
// 对象多态
Father F1 = new SonFrist();
// 行为多态
// 编译看左边,执行看右边
F1.run();
System.out.println(F1.name);
Father F2 = new SonSecond();
F2.run();
System.out.println(F2.name);
}
}
多态的优势
1.可以实现解耦合,右边子类对象可以随时切换,后续可以根据业务需求随时变化
2.可以使用父类类型作为变量类型,右边可以是一切子类对象
3.多态的缺陷: 多态不能使用子类的独有功能
多态的强制类型转换
目的: 为了解决父类对象无法调用子类对象的独有功能
package demo0320;
public class Father {
String name = "父亲";
public void run(){
System.out.println("父亲正在奔跑");
}
}
package demo0320;
public class SonFrist extends Father{
String name = "儿子1";
@Override
public void run(){
System.out.println("孩子1正在奔跑");
}
public void Certificate(){
System.out.println("孩子1有5张奖状");
}
}
package demo0320;
public class SonSecond extends Father{
String name = "儿子2";
@Override
public void run(){
System.out.println("孩子2正在奔跑");
}
public void Certificate(){
System.out.println("孩子2有3张奖状");
}
}
package demo0320;
public class test {
public static void main(String[] args) {
// 对象多态
Father F1 = new SonFrist();
Father F2 = new SonSecond();
// 此处会出现问题,虽然是new的子类对象,但是编译过程中看到是左边,也就是父类对象,由于父类中没有Certificate方法,所以会编译错误
// F1.Certificate()
// 可以通过强转,来保障编译器的顺利通过
SonFrist s1 = (SonFrist) F1;
s1.Certificate();
// 此时不会ide不会产生红色异常,但是在运行是会报错ClassCastException
// 原因在于,F1右边是创建的SonFrist对象,此时强转为另一个不同的子类对象,这是不被允许的
// SonSecond s2 = (SonSecond) F1;
SonSecond s3 = (SonSecond) F2;
// 比较好的强转方式是在强转前,通过instanceof关键字先判断是否是期望的对象
// 通过instanceof先判断在强转,可以有效保障强转不产生异常
if(F1 instanceof SonSecond){
SonSecond s4 = (SonSecond) F2;
}
}
}
final
- final关键字有最终的意思,可以修饰(类、方法、变量)
- 修饰类: 该类被称为最终类,特点是不能被继承了
- 修饰方法: 该方法称为最终方法,特点是不能被重写了
- 修饰变量: 该变量只能被赋值一次
package demo0320;
final class A{};
// A类被final修饰,无法在被继承
// class B extends A{}
class C{
final public void run(){
System.out.println("对象在奔跑");
}
}
class D extends C{
// run方法被final修饰,不能在被重写
// @Override
// public void run(){
//
// }
}
public class test {
// 由于final只能赋值一次,且该变量在类被创建的时候没有值的情况下,会给到一个默认值null,导致后续在想对这个变量修改变得不在可能
// 所以对于这些再类创建时候就产生默认值的变量,如果永final修饰,必须给到想要的初始值
public static final String name = "童话";
public final int age = 10;
public static void main(String[] args) {
final int a;
a = 10;
// 在修改a变量的值是不可以的,赋值只能有一次
// a = 20;
}
public static void buy(final double z){
// 用final修饰的形参,是无法在函数内部修改的
// z = 0.75;
}
}
final修饰变量的注意事项
- 修饰基本类型的变量,变量存储的数据不能被改变
- 修饰引用类型的变量,变量存储的地址不能被改变,但是地址所指向的对象的内容是可以改变的
常量
- 使用static final修饰的成员变量被称为常量
- 作用: 通常用来记录系统的配置信息,不可被改变
- 优势: 代码可读性更好,可维护性也更好
- 程序编译后,常量会被宏替换:出现常量的地方全部都会被替换成其记住的字面量,这样可以保证使用常量和使用字面量的性能是一样的
抽象类
- 在java中有一个关键字abstract,用它修饰的类叫做抽象类,用它修饰的方法叫做抽象方法
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
- 类该有的成员(成员变量、方法、构造器)抽象类也可以有
- 抽象类的主要特点: 抽象类是无法new对象的,仅仅作为一种特殊的父类,让子类继承并实现
- 一个类继承抽象类,必须重写完所有的抽象方法,否则这个类也必须被定义为抽象类
- abstract修饰的方法称为抽象方法,特点是有方法签名,但是不能有方法体
package demo0320;
// abstract修饰的类称为抽象类
// 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类,ublic abstract class Father 丢掉abstract会报错,方法丢掉abstract没问题
// 类该有的成员(成员变量、方法、构造器)抽象类也可以有
// 抽象类的主要特点: 抽象类是无法new对象的,仅仅作为一种特殊的父类,让子类继承并实现
// 一个类继承抽象类,必须重写完所有的抽象方法,否则这个类也必须被定义为抽象类
public abstract class Father {
// abstract修饰的方法称为抽象方法,特点是有方法签名,但是不能有方法体
public abstract void run();
public String name;
public Father(){
System.out.println("无参数构造器");
}
public void eat(){
System.out.println("普通的成员方法");
}
}
抽象类的场景和好处
- 父类知道每个子类都要实现的某个方法,但是不同子类实现情况不一样,父类就定义为抽象方法,交给子类去重写实现
- 个人理解: 在工作中,我作为主管,我只要写好类,和这些类的方法,然后交给实习生去完成这些类的具体内容,因为是抽象类,所以实习生必须要写完我给的方法
package demo0320;
public abstract class Father {
// abstract修饰的方法称为抽象方法,特点是有方法签名,但是不能有方法体
public abstract void run();
public abstract void speak();
public String name;
public Father(){
System.out.println("无参数构造器");
}
public void eat(){
System.out.println("普通的成员方法");
}
}
package demo0320;
public class SonFrist extends Father{
String name = "儿子1";
// 必须要全部重写,少写一个都不行
@Override
public void run(){
System.out.println("孩子1正在奔跑");
}
@Override
public void speak(){
System.out.println("孩子1在讲话");
}
public void Certificate(){
System.out.println("孩子1有5张奖状");
}
}
package demo0320;
public class test {
public static void main(String[] args) {
// 无法new出来抽象类对象
// Father F1 = new Father();
// 但是可以通过父类 变量 = new 子类对象来创建
Father F1 = new SonFrist();
F1.eat();
F1.speak();
}
}
接口
- Java提供了一个关键字interface,用这个关键字可以定义出一个特殊得结构:接口
public interface 接口名称{
// 成员变量(常量)
// 成员方法(抽象方法)
}
package demo0320;
// 接口有且只有两个内容,成员变量(常量),成员方法(抽象方法)
public interface A {
// 成员变量(常量)
// 会出现异常,因为着不是常量
// String name;
// java会自动帮变量添加static final修饰
// public static final String name = "童话";
// 所谓得常量,就是要给定初始值,且初始值不会发生改变
String name = "童话";
// 这样定义的方法是不被允许得,方法只能是抽象方法
// public void test(){
//
// }
// 这样定义得方法是多余的,因为java也是会自动帮忙添加public abstract
// public abstract void test();
void test();
}
接口的实现
- 接口不能创建对象,接口时用来被类实现的,通过implements,实现接口的类被称为抽象类
- 一个类可以实现多个接口,实现类实现多个接口,必须重写完所有接口的全部抽象方法,否则实现类需要定义为抽象类
package demo0320;
public interface A {
String name = "童话";
void test();
}
package demo0320;
public interface B {
void test1();
void test2();
}
package demo0320;
public interface C {
void test3();
}
package demo0320;
public class D implements A,B,C{
@Override
public void test() {
}
@Override
public void test1() {
}
@Override
public void test2() {
}
@Override
public void test3() {
}
}
接口的好处
1.弥补了类单继承的不足,一个类可以实现多个接口
2.让程序可以面向接口编程
3.实现类可以同时继承(extends)一个父类和继承(implements)多个接口
4.同多态一样,可以通过 接口类型 变量名 = new 实现类(),可以实现快速切换
接口中新增的三种方法
package demo0320;
public interface E {
/*
1.默认方法,必须使用的default修饰,默认会被public修饰
实现方法, 必须用实现类的对象来访问
*/
default void test1(){
System.out.println("默认方法");
}
/*
私有方法,必须使用private修饰(JDK9)
实例方法: 只能在接口内部的默认方法或者静态方法中调用
*/
private void test2(){
System.out.println("私有方法");
}
/*
静态方法,必须使用static修饰,默认会被public修饰
可以通过接口.方法
*/
static void test3(){
System.out.println("静态方法");
}
default void test4(){
test2();
}
}
package demo0320;
public class D implements E{
}
package demo0320;
public class test {
public static void main(String[] args) {
E E1 = new D();
E1.test1();
E.test3();
E1.test4();
}
}
接口的多继承
1.一个接口可以同时继承多个父类接口
2.接口继承多个父类接口的时,不用重写父类方法
3.继承多个接口时,要保证这多个接口不会出现重复的方法签名,否则无法多继承
4.一个类继承了父类,又同时继承了接口,如果接口和父类中有同名的默认方法,实现类会有限用父类的
5.一个实现类实现多个接口,多个接口存在重复的同名的默认方法,可以不冲突,实现类重写该方法即可