【Java进阶】Day9

面向对象高级

static 关键字

在这里插入图片描述
当加载了类字节码文件时,如果类中存在静态成员,那么会存放到堆内存中的静态成员变量区。
工具类:为数据提供处理方法服务,通常这些方法都是静态方法。 且通常私有化该类的构造方法(使用private)。
在这里插入图片描述

继承

在这里插入图片描述
在这里插入图片描述
子类中定义了与父类相同命名的变量,在子类调用这种同名的变量时根据就近原则会优先使用子类里的变量。但如果我们就想使用父类里的变量,我们可以使用super解决。
在这里插入图片描述

方法重写

在继承体系中,子类可以继承到父类的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改这就需要采用方法的重写,方法重写又称方法覆盖。
在这里插入图片描述

可以通过在方法上方写注解@Override就能判断是否是重写方法。

@Override
public void show()

在这里插入图片描述
在这里插入图片描述
常使用最多的是 privatepublic
继承特点:Java 只支持单继承,不支持多继承,但支持多层继承。即子类的子类。
在这里插入图片描述
上面代码运行结果说明了:除了Object类,在所有构造方法的第一行代码,都默认隐藏了一句话 super();通过这句代码,访问父类的空参数构造方法。
细节:Java当中所有的类,都直接或者间接的继承到了Object类。

public class pearsonTest {
    public static void main(String[] args) {
        zi z = new zi(0,10);
        z.show();
    }
}
class Fu{
    private int num;
    public Fu(){};
    public Fu(int n){
        this.num = n;
    }
    public int getNum(){
        return this.num;
    }
}

class zi extends Fu{
    private int age;
    public zi() {
    }

    public zi(int n,int a) {
        super(n);
        this.age = a;
    }
    public void show(){
        System.out.println("编号:"+super.getNum()+" 年龄"+this.age);
    }
}

输出编号:0 年龄10
有了这种特性,我们可以将父类的数据交给父类处理,子类的数据交给子类去处理。
在这里插入图片描述
我们在new子类时,内存中会有专门一块区域存放父类的变量成员。
在子类中省略super访问父类的写法规则:

  1. 被调用的变量和方法在父类中不能带有private
  2. 被调用的变量和方法,在子类中不存在。
    在这里插入图片描述

省略规则

因为继承行为中,子类完全继承父类的变量和方法成员,所以当我们去访问的时候可以使用this,然后根据this的省略规则就可以不需要写this或者super
然而如果子类存在与父类定义相同的变量和方法成员,那么在调用父类的变量和方法成员时需要使用super

class Fu{
    int num;
    public Fu(){};
    public Fu(int n){
        this.num = n;
    }
}

class zi extends Fu{
    private int age;
    public zi() {}
    public zi(int n,int a) {
        super(n);
        this.age = a;
    }
    public void show(){
        System.out.println("编号:"+num+" 年龄"+age);
    }
}

例如上面的this.numthis.age就可以省略 this。
开闭原则:对功能扩展做开放,对修改代码做关闭。尽量不要修改源代码,有新功能重新编写。
细节:可以使用this();来调用自身的构造函数。例如下面:

class A{
    String a,b,c;
    public A(){};
    public A(String s1,String s2){
        a = s1;
        b = s2;
    };
    public A(String s1,String s2,String s3){
        this(s1,s2);
        c = s3;
    };
}

final 修饰符

在这里插入图片描述

final 修饰变量特点:
基本数据类型:数据值不可改变。
引用数据类型:地址值不可改变,但是内容可以改变。
final 修饰成员变量的注意事项

  1. final 修饰成员变量,不允许修饰默认值。即需要在构造方法结束之前完成赋值。
  2. final 修饰成员变量的初始化时机
    a. 在定义的时候直接赋值【推荐】
    b. 在构造方法中完成赋值

final 修饰变量的命名规范:

  • 如果变量名是一个单词,所有字母大写。例如 PI
  • 如果变量名是多个单词,所有字母大写,中间使用下划线分割。例如:DEGREES_TO_RADIANS

在这里插入图片描述
在这里插入图片描述

抽象类

抽象类是一种特殊的父类,允许编写抽象方法。
抽象方法:将共性的行为(方法)抽取到父类之后,发现该方法的实现逻辑无法在父类中给出具体明确,该方法就可以定义为抽象方法。
抽象类:如果一个类中存在抽象方法,那么该类就必须声明为抽象类。
要求子类必须重写方法就可以使用抽象类。
在这里插入图片描述
例如创建了一个动物的父类,编写“吃”这种成员方法,但没法具体明确每个动物吃什么,那么只能要求每个子类重写“吃”这种成员方法。

abstract class Animal{
    public abstract void eat();
}
class Cat extends Animal{
    public void eat(){
        System.out.println("吃鱼");
    };
}
class Dog extends Animal{
    public void eat(){
        System.out.println("骨头");
    };
}

在这里插入图片描述
在这里插入图片描述

接口

Java中的接口更多体现的是对行为的抽象,也是对行为的规范,是一套行为规则。
在这里插入图片描述
在这里插入图片描述
下面创建了接口类 Inter,规定了子类必须重写两个行为(方法)。再编写了 InterImpl 实现类用实现 Inter 定义的两个行为(方法)。
在这里插入图片描述
接口的子类除了是实现类,还可以是抽象类,但这种写法非常麻烦,在实战中少见。继承类并实现多个接口,这种写法比较常见。
在这里插入图片描述接口成员特点

  • 成员变量
    • 只能是常量。默认修饰符:public static final
  • 构造方法
    • 没有
  • 成员方法
    • 可以新建抽象方法(带abstract关键字)、非抽象方法(带default关键字)、静态方法(带static关键字)、私有方法(带private关键字)
      在这里插入图片描述
      DK9 之后可以在接口内创建私有方法,也就是可以使用private修饰符。
      在这里插入图片描述
      下面的代码中可以看到Zi类同时实现接口A、B,Zi应当重写show方法,但Zi同时还继承了Fu,在Fu中已经有了show所以现在不需要重写show方法了,因为已经实现了。
      在这里插入图片描述下面表现了接口可以继承多个接口,他们要求子类重写的方法只有一个相同的show方法,所以InterCImpl在实现时,只需要实现show方法即可。
      在这里插入图片描述

抽象类和接口的对比

  • 成员变量:
    • 抽象类:可以定义变量,也可以定义常量
    • 接口:只能定义常量
  • 成员方法:
    • 抽象类和接口都可以是具体方法,也可以是定义抽象方法。
  • 构造方法:
    • 抽象类:有
    • 接口:没有

抽象类非常灵活,本身是在类的基础上增加了对行为的约束,但其本身的作用还是用于对事物行为进行抽象(描述事物)。
接口相对严格,主要用于在对业务行为有了充分认识后制定了一个行为规范,后续实现业务接口只需按照规范来即可。

多态

多态的前提:

  1. 有继承/实现的关系
  2. 有方法重写
  3. 有父类引用指向子类对象

第三点可以使用代码直观的展示。
Animal a1 = new Dog();
其中Dog类继承Animal,且Dog类重写了父类里面的方法,而上面这种代码就是第三点的体现。
对象多态:
父类引用指向子类对象。

  • 如果是父类类型变量,可以指向不同的子类。
  • 如果方法的形参定义为父类类型,这个方法就可以接收到该父类的任意子类对象。
class Animal{
    public void eat(){};
}
class Cat extends Animal{
    public void eat(){
        System.out.println("吃鱼");
    };
}
class Dog extends Animal{
    public void eat(){System.out.println("骨头");};
}

对象多态体现在变量上:

Animal a1 = new Dog();
Animal a2 = new Cat();

对象多态体现在方法上:

public static void main(String[] args) {
    Animal a1 = new Dog();
    Animal a2 = new Cat();
    doit(a1);
    doit(a2);
}

public static void doit(Animal obj) {
    obj.eat();
}

输出:
骨头
吃鱼

行为多态:
同一个行为具有多个不同表现形式或形态能力。就是抽象行为可以有不同的实现方式。

多态的成员访问特点

成员变量:
编译看左边(父类),运行看左边(父类)。
成员方法:
编译看左边(父类),运行看右边(子类)。
这个看左边是指编译时会检查父类中是否有定义这个成员变量/成员方法,如果没有则没法编译。即在多态情景下,不能直接访问子类特有的成员。
如果一定要访问子类,那么需要强转到子类。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Animal{
    public void eat(){};
}
class Cat extends Animal{
    int num = 10;
    public void eat(){
        System.out.println("吃鱼");
    };
}
class Dog extends Animal{
    public void eat(){System.out.println("骨头");};
}

根据上面代码进行更改,其中Cat类中新增了一个成员变量num。我希望在执行doit方法时,打印该成员变量。

public static void main(String[] args) {
    Animal a1 = new Dog();
    Animal a2 = new Cat();
    doit(a1);
    doit(a2);
}

public static void doit(Animal obj) {
    if (obj instanceof Cat){
        System.out.println(((Cat) obj).num);
    }
    obj.eat();
}

先是使用了instanceof判断当前传入的对象是否是Cat子类,如果是就使用向下强转实现访问子类成员的目的。

代码块

在这里插入图片描述
构造代码块不经常使用,静态代码块适用于类中的静态成员变量初始化复杂的情况使用。
构造代码块编写:
在这里插入图片描述
编译后的 class 文件显示。
在这里插入图片描述
下面是静态代码块用于初始化类中静态成员变量的演示。

class Animal{
    static int num;
    static {
        num = 1;
    }
}

内部类

成员内部类

在这里插入图片描述
创建内部类。

class Outer{
    class Inner{
        int num = 10;
    }
}

创建内部类对象。

public static void main(String[] args) {
    Outer.Inner innerClass = new Outer().new Inner();
    System.out.println(innerClass.num);
}

内部类中,访问外部类成员,可以直接访问,包括设置的私有成员。
外部类中,访问内部类成员,需要创建对象访问。
如何区分内外部类同名成员使用情况。内部类访问外部类但成员同名可以使用外部类名.this
在这里插入图片描述

静态内部类

在这里插入图片描述

class Outer{
    static class Inner{
        public static void show(){
            System.out.println(1);
        }
    }
}

当你的想调用静态内部类的静态方法时,例如上面这种情况,你可以直接 Outer.Inner.show();这样链式访问。
静态只能访问静态,静态内部类中,访问外部类的成员,静态成员可以直接访问,非静态需要先创建对象访问。

局部内部类

局部内部类,放在于方法、代码块、构造器等执行体中,非常鸡肋。
在这里插入图片描述
上面就在方法中创建了内部类,只有运行该方法时才会用上。

匿名内部类【重要】

匿名内部类可以作为方法的实际参数进行传输。
在这里插入图片描述

  • new 类名(){}代表继承这个类。
  • new 接口名(){}代表实现这个接口。
class A{
    void show(){
        System.out.println("show");
    };
}

存在一个命名为A的类,且存在命名为showA的函数,需要传入一个类型为A的对象。

public static void showA(A a){
    a.show();
}

不想新建变量,可以直接使用new

public static void main(String[] args) {
    showA(new A());
}

换成接口。

interface A{
    void show();
}

函数不变。

public static void showA(A a){
    a.show();
}

直接实现该接口进行调用。

public static void main(String[] args) {
    showA(new A(){
        @Override
        public void show() {
            System.out.println("show");
        }
    });
}

如果接口内的抽象方法过多,不建议这么写。

Lambda表达式

在这里插入图片描述
注意:Lambda 表达式只能简化函数式接囗的匿名内部类的写法形式
函数式接口的定义:

  • 首先必须是接口,其次接口中有且仅有一个抽象方法的形式。
  • 通常我们会在接口上加上一个 注解@FunctionalInterface标记该接囗必须是满足函数式接口。
@FunctionalInterface
interface A{
    void show();
}

在这里插入图片描述
存在一个参数,且只有一行代码也不需要return。省略方式展示。
存在一个参数,且只有一行代码也不需要return。省略方式展示。
存在多个参数,且需要return。省略方式展示。

在这里插入图片描述
在这里插入图片描述

适配器设计模式(Adapter)

在这里插入图片描述
只想实现部分方法,但实现类是必须重写所有方法的,为了避免这种情况。我们可以编写一个抽象实现类当作适配器,然后我们再通过继承这个类来只重写我们需要的方法。

在这里插入图片描述
在这里插入图片描述

模板设计模式(Template)

在这里插入图片描述
在这里插入图片描述
我们定义了抽象类A,其中需要我们重写body方法。其中show方法就相当于模板,而body方法就相当于填充,我们只需要关注body即可。
模板设计模式的优势,模板已经定义了通用结构,使用者只需要关心自己需要实现的功能即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值