面向对象三大特征

封装

封装性在Java中的体现:
1.方法就是一种封装
2.关键字private也是一种封装
可以阻止不合理的代码进入
一旦使用了private进行修饰,那么本类当中仍然可以随意访问。
但是!超出了本类范围之外就不能再直接访问(XXX.xxx)了。

间接访问private成员变量:就是定义一对Getter/Setter方法
必须叫setXxx或者是getXxx命名规则
对于Getter来说,不能有参数,返回值类型和成员变量对应
对于Setter来说,不能有返回值,参数类型和成员变量对应
但是,对于基本类型中的boolean值,Getter方法一定写成isXxx的形式,而

public class Person{
    private String name;
    private int age;
    private boolean male;
    
    public void show(){

        System.out.println("我叫:" + name + ",年龄:" + age);
    }
    //这个成员方法,专门用于向age设置数据
    //只能使用void,并且必须时setAge格式
    public void setAge(int num){
        age = num;
    }
    
    //这个成员方法专门用于获取age数据
    //无参数、有返回值、且返回值类型和成员变量类型对应
    public int getAge(){
        return age;
    }
    public void setMale(boolean b){
        male = b;
    }
    public boolean isMale(){
        return male;
    }
}

封装就是将一些细节信息隐藏起来,对于外界不可见。


继承

继承是多态的前提,没有继承就没有多态
java.lang.Object类是继承类的顶端

继承主要解决的问题就是共性抽取

共性抽取:几个类里有多个共性,则重新定义一个新类,新类中定义存在的共性部分,然后其他类自底向上继承新类。

父类(基类、超类)

将公共的内容抽取到父类当中

子类(派生类)

子类中自动拥有父类中内容的特性,可以拥有自己专有的内容。

在继承关系中,“子类就是一个父类”,子类可以被当做父类看待
例如父类是员工,子类是讲师,那么“讲师是一个员工”

继承定义的格式

定义父类的格式(一个普通类的定义)

public class 父类名称{
    //....
}

定义子类的格式

public class 子类名称 extends 父类名称 {
    //...
}

例子:

//定义一个父类:员工
public class Employee {
    public void method(){
        System.out.println("....");
    }
}

//定义一个员工的子类:讲师
public class Teacher extends Employee {
    //虽然不写,但是已经继承了父类中的方法等!
    //可以调用父类中的方法等!
}

继承中成员变量的访问特点:

父类new出来的对象只能使用父类的东西,没有任何子类的内容
在父子类的继承关系当中,如果成员变量重名,则创建子类对象时,访问有两种方式
直接通过子类对象访问成员变量:new上一个子,然后.就是直接
等号左边是谁,就优先用谁,没有则向上找
Zi zi = new Zi();

System.out.println(zi.num);//num是父类和子类中重名部分
//zi是由Zi类new出来的,等号左边是Zi,优先用子类中

zi.abc//子类中没有,再查找上一级父类,如果都没有,就报错
间接通过成员方法访问成员变量
该方法属于谁(定义在哪个类),就优先用谁,没有则向上找
public class Fu {
    int numFu = 10;
    int num = 100;
    
    public void methodFu(){
    //使用的本类当中,不会向下找子类的
        System.out.println(num);//100
    }
}

区分子类方法中重名的三种变量

局部变量: 直接用
本类成员变量:this.成员变量名
父类成员变量:super.成员变量名

继承中成员方法的访问特点

在不重名的情况下,子类在创建对象后也可以调用父类的方法
但是重名的情况下,创建的对象是谁,就有限用谁,没有则向上找

无论是成员方法还是成员变量,在子类中没有的话,都是向上寻找

重写(Override)

在继承关系中,方法名称一样,参数列表也一样。(覆盖、覆写)
特点:

创建的是子类对象,则优先用子类方法。

注意事项:

  • 1、父子类之间方法名称相同,参数列表也应该相同。
  • 2、在子类中覆盖方法上一行写上@Override,可以检测是否为覆盖重写
  • 3、子类方法的返回值范围必须 父类方法的返回值范围
  • 4、子类方法的权限必须父类方法的修饰符
    public > protected > (default) > private
    (default)并不是关键字,而是什么都不写,留空

应用场景:体现一下设计原则
设计原则:对于已经投入使用的类,尽量不要进行修改。
推荐定义一个新的类,来重复利用其中共性内容,并且添加改动新内容
(问题:如果原来的类当中,有效率不高的方法,是不是还有可能会被调用,出现bug呢??)

方法中利用super.父类方法名可以继承这个方法,之后再添加新功能

继承中构造方法的访问特点:
1、子类构造方法当中有一个默认隐含的“super()【无参父类方法】”调用,所以一定时先调用的父类构造,后执行子类构造。
2、子类构造可以通过super关键字来调用父类重载调用
3、super的父类构造调用,必须是子类构造方法并且是第一个语句,不能多次调用super构造
4、并且有参和无参只能选一个,因为肯定会有一个变成后面的声明调用

public Zi(){
    super();
}

public void method(){
    super();     ×××错误
}
super关键字(主要用来访问父类内容)

1、在子类的成员方法中,访问父类的成员变量
2、子类的成员方法中,访问父类的成员方法

public void method(){
    super.method();  //调用父类中的成员方法
    System.out.println("555");
}

3、子类的构造方法中,访问父类的构造方法

public Zi(){
    super(); //会赠送一个无参数
}
this关键字(访问本类内容)

1、在本类的成员方法中,访问本类的成员变量
2、在本类的成员方法中,访问本类的另一个成员方法

public void methodA(){
}
public void methodB(){
    this.methodA();
    .....
}

3、在本类的构造方法中,访问本类的另一个构造方法
并且this(...)也必须是构造方法的第一个语句,意味着一个构造方法中只能调用一次
super和this两种构造调用,不能同时使用

public Zi(){
    this(10);   //本类的无参构造调用本类的有参
}
public Zi(int n){
    ...
} 

重载(Overload)
方法名称一样,参数列表不一样

抽象类:

所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,
如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类

抽象方法:加上abstract关键字,然后去掉大括号,直接分号结束
抽象类:抽象方法所在的类,必须是抽象类才行。在class之前加上abstract

如何使用抽象类和抽象方法:
1、不能直接创建new抽象类,对象都是具体的,抽象类不能被实例化。
2、必须用一个子类来继承抽象父类
3、子类必须覆盖重写抽象父类当中所有的抽象方法
覆盖重写(实现):子类去掉抽象方法的abstract关键字,然后补上方法体大括号。

接口

因为Java只允许单继承,接口就是为了实现类似多继承的功能
接口就会一种公共的规范标准,只要符合规范标准,就可以通用

格式

public interface 接口名称 {
    //接口内容
}

class换成关键字interface之后,编译生成的字节码文件仍然是
.java --> .class

Java7,接口包括:

1.常量
2.抽象方法

Java8,额外包括:

3.默认方法
4.静态方法

Java9,额外包括:

5.私有方法

接口的抽象方法定义:
1.接口的抽象方法,修饰符必须是两个固定的关键字:public abstract
2.两个关键字可以选择性省略
3.方法的三要素可以随意定义

public interface MyInterfaceAbstract {
    public abstract void myMethod();
//    private abstract void mythod();  Error!!!

    abstract void myMethod1();
    public void myMethod2();
    void myMethod3();

}

接口的抽象方法使用:
接口使用步骤:
1.接口不能直接使用,必须有一个“实现类”来“实现”接口
格式:

//区别于  public class 实现类名 extends 父类{
//       }
public class 实现类名称 implements 接口名称 {
    //...
}

2.接口的实现类必须覆盖重写接口中所有的抽象方法
3.创建实现类的对象,进行使用
不能直接new接口类,要new它的实现类
如果实现类并没有覆盖重写接口中所有的抽象方法,那么这个实现类自己就必须是抽象类,(如果有抽象方法没管的,就好比把这些抽象方法继承下来,含继承方法必须是抽象类)
而抽象类(即原本的实现类)又是不能new的。

//接口
public interface MyInterfaceAbstract {
    public abstract void myMethod();
//    private abstract void mythod();  Error!!!

    abstract void myMethod1();

}

//未全部重写的实现类,必须定义为抽象类

public abstract class MyInterfaceAbstractImp implements MyInterfaceAbstract{
//    未重写的类
//    @Override
//    public void myMethod() {
//        System.out.println("1");
//
//    }
    @Override
    public void myMethod1() {
        System.out.println("2");
    }
}

//而抽象类使用必须定义一个子类继承

public class Test extends MyInterfaceAbstractImp{
// 接口的实现类未重写的,会被实现类继承下来,
// 若要使用,又必须由新定义的子类给覆盖重写   
   @Override
    public void myMethod() {

    }
}

接口的默认方法定义:
存在原因:
为了解决接口升级的问题(比如接口中的抽象方法有变化)
格式:

public default 返回值类型 方法名(参数列表) {
    方法体
}

把原本接口中,新添加的方法改成默认方法
默认方法会被实现类给继承下去

public default void methodDefault(){
    //...
}

注意事项:
1.接口的默认方法,可以通过接口实现类对象,直接调用
2.接口的默认方法,也可以被接口实现类进行覆盖重写

接口的静态方法
接口是允许定义静态方法
格式:

public static 返回值类型 方法名称(参数列表){
    //方法体
}

不能通过接口实现类的对象来调用接口当中的静态方法!!!(一个类可以有多个接口,多个静态方法有可能会产生冲突)(默认方法就不冲突??)
正确用法:

接口名称.静态方法名(参数);
使用接口需要注意:

1.接口没有静态代码块,不能有构造方法

2.一个类的直接父类有且仅有一个,但是一个类可以同时实现多个接口
public class MyInterfaceImpl implements MyinterfaceA, MyintefaceB {

//覆盖重写左右抽象方法

}

3.如果实现类所实现的多个接口中,存在重复的抽象方法,那么只需要覆盖重写一次即可

4.如果实现类没有覆盖重写所有接口的所有抽象方法,那么实现类必须是一个抽象类

5.如果实现类所实现的多个接口中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写

6.一个类如果直接父类当中的方法,和接口当中的默认方法产生了冲突,优先使用父类当中的方法

public class Zi extends Fu implements interfaceA, interfaceB{
    //...
}

多态

指一个对象含有多种形态

小明即使一个学生,也是一个人类
既有学生形态,也有人类形态
多态实际指的是同一个对象,在不同时刻体现出来不同的状态。

格式:
父类名称 对象名 = new 子类名称();
IMG_1062(20200710-114602).PNG

为什么要用多态

节省代码量,多个子类继承于一个父类时,只需要更改子类名称就可以

多态成员变量访问特点:

同继承一样
成员变量。编译看左边,运行看左边

多态方法访问特点:

构造方法:创建子类对象时访问父类的无参构造方法,对父类数据初始化

静态方法:编译看左边,运行看左边。(静态只能被静态重写,不需要看成是方法重写,因为静态与类有关)

成员方法:编译看左边,运行看右边。(因为成员方法重写,其实重写与否,调用的都是子类的成员方法)
编译看左边,运行看右边

    Fu obj = new Zi();
    
    obj.method();//父子共有方法,编译通过,但是运行看右边,
                 // 因此输出 子类中的method
    obj.methodFu();//编译看左边,左边为父类,编译通过
    obj.methodZi();//编译看左边,但此为子类,编译不通过

父类一定要有此成员,子类可以没有,因为可以继承。
(父类不为抽象情况下)

如果父类有而子类没有此方法,调用父类方法只是表面现象,其本质是调用了子类的方法,因为子类继承了这个方法。

如果该方法体内调用了一个方法,此方法在子类中也重写了
那么此刻代码是调用了是子类方法

对象的向上转型:

就是多态写法
父类名称 对象名 = new 子类名称();

    Animal obj = new Cat();

右侧创建一个子类对象,把它当做父类来看待使用
向上转型一定是安全的,从小范围转向大范围

弊端:对象一旦向上转型为父类,无法访问子类中独有的方法
解决方案:用对象的向下转型--还原

对象的向下转型:

一个【还原动作】,类似基本数据类型中的强制转换

    int num = (int) 10.0;  //可以
    int num = (int) 10.5;  //不可以

子类名称 对象名 = (子类名称) 原本父类对象;

    Animal obj = new Cat(); //定义一个多态,向上转型
    
    Cat cat = (Cat)obj;     //向下转型

将父类对象,【还原】成本来的子类对象
注意事项:

  1. 必须保证对象本来创建的时候是啥,才能向下转型变成啥,否则报错

如何知道父类的引用是指向哪个子类
动物类引用是指向猫还是狗

格式:
对象 instanceof 类名称
得到一个boolean值,判断前面对象能不能当做后面类型是实例。

public class Dinstanceof {
    public static void main(String[] args) {
        //Animal类引用指向Dog类 多态,向上转型
        Animal animal = new Dog();
        animal.eat();
        
        //根据if判定,如果指向不是Cat类,则返回值为0,进入不了
        //结果是,进入不了
        if (animal instanceof Cat){
            Cat cat = (Cat) animal;
            cat.methodZi();         //Cat类独有方法
        }
        
        if (animal instanceof Dog) {
            Dog dog = (Dog) animal;
            dog.methodDog();        //Dog类独有方法
        }
    }
    
    //在另一个方法中使用判定
    giveMeAPet(new Dog());
}

public static void giveMeAPet(Animal animal) {

        if (animal instanceof Cat){
            Cat cat = (Cat) animal;
            cat.methodZi();         //Cat类独有方法
        }
        
        if (animal instanceof Dog) {
            Dog dog = (Dog) animal;
            dog.methodDog();        //Dog类独有方法
        }
}

三种方式:
1.向上转型,转好了再放入参数列表 OK
2.不进行转型,放入参数列表 OK
3.使用子类对象、匿名对象 OK
类比 基本数据类型的自动转化

如果需要使用实现类中的特有方法:
先用instanceof进行判断后,向下转型再调用,最好用else if

下例:Usb接口、Laptop类、Mouse实现类、keyBoard实现类

public class Demo08Main {
    public static void main(String[] args) {
        //创建一个笔记本
        Laptop computer = new Laptop();
        computer.powerOn();
        
1.向上转型,转好了再放入参数列表  OK
        //准备一个鼠标,供电脑使用
        Mouse mouse = new Mouse();
        //首先进行向上转型
        Usb usbMouse = mouse;    //or:Usb usbMouse = new Mouse();多态写法
        //参数是Usb类型,正好传进去Usb鼠标
        computer.useDevice(usbMouse);

2.不进行转型,放入参数列表       OK
        //准备一个键盘,供电脑使用
        keyBoard key = new keyBoard();
        //or:Usb usbKey = new keyBoard();    多态写法
//        computer.useDevice(usbKey); //没有使用多态写法 正确!!
        //方法参数是Usb类型,传递进去是方法实现类
        computer.useDevice(key);    //正确!!也发生了向上转型成为接口类型,小范围到了大范围,自动转化
        
3.使用子类对象、匿名对象         OK
        computer.useDevice(new keyBoard());  //使用子类对象,匿名对象都OK
        //实现类 --> 接口,我需要接口,但是实现类向上转型为接口,于是可以使用
        
        computer.powerOff();
    }
    
}    

Laptop类,使用实现类的独有方法
    public void useDevice(Usb usb) {
        usb.open();//打开设备
        if (usb instanceof Mouse) {
            //向下转型
            Mouse mouse = (Mouse) usb;
            mouse.click();
        } else if (usb instanceof keyBoard){
            //向下转型
            keyBoard key = (keyBoard) usb;
            key.pang();
        }
        
    }



waikiki
4 声望2 粉丝