代码块、接口和抽象类、匿名内部类

134 阅读5分钟

一、代码块

代码块分为两类,使用static修饰的叫静态代码块,没有static修饰的,叫普通代码块/非静态代码块

基本语法:

static】 {
    代码
}

好处:当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作

注:代码块中定义的变量只在代码块中有效,当执行完代码块内代码后,就会销毁


public class CodeBlock01 {
    public static void main(String[] args) {
        Movie movie = new Movie("你好,李焕英");
        System.out.println("===========");
        Movie movie1 = new Movie("唐探3", 100, "陈思诚");
    }
}
 
class Movie{
    private String name;
    private double price;
    private String director;
 
    //构造器重载
    //下面的三个构造器都有相同的语句
    //代码看起来比较冗余
    //这时我们把相同过的语句,放到一个代码块中
    //这样不管调用哪个构造器,创建对象,都会先调用代码块的内容
    //代码块调用的顺序优先于构造器
    {
        System.out.println("电影屏幕打开...");
        System.out.println("广告开始...");
        System.out.println("电影正是开始...");
    };
 
    public Movie(String name) {
        System.out.println("Movie(String name)被调用...");
        this.name = name;
    }
 
    public Movie(String name, double price) {
        this.name = name;
        this.price = price;
    }
 
    public Movie(String name, double price, String director) {
        System.out.println("Movie(String name, double price, String director被调用...");
        this.name = name;
        this.price = price;
        this.director = director;
    }
}

结果:

image.png

细节

  • static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象就执行。

  • 同样static修饰的静态变量不论实例了多少对象,只占用一份内存。

  • 类什么时候被加载

    • 创建对象实例时(new)
    • 创建子类对象实例,父类也会被加载
    • 使用类的静态成员时(静态属性,静态方法)

例:httpClient优化时,使用了static静态代码块创建httpClient对象,当外部再调用get、post方法时,httpc对象不会重新被加载,可以在整个应用程序中共享这个httpClient实例,减少资源消耗并提高性能。

二、接口和抽象类的区别

  • 定义方式:抽象类通过使用 abstract 关键字来定义,而接口使用 interface 关键字来定义

  • 成员变量:抽象类可以包含普通数据成员和static数据成员,而接口只能包含 static final 修饰的数据成员

  • 成员方法:抽象类可以包含普通方法和抽象方法,同时包含具体的方法实现,而接口只能包含抽象方法(默认public abstract修饰),没有方法体的方法声明

  • 构造函数:抽象类可以有构造函数,而接口不能有构造函数

  • 实现方式:一个类可以继承(extends)一个抽象类,而一个类可以实现(implements)多个接口

  • 多继承:Java不支持多继承,一个类只能继承一个抽象类,但可以实现多个接口

  • 实例化:抽象类和接口都不能被实例化

三、匿名内部类

三要素为:继承/实现,方法重写,创建对象

格式:

new 类名/接口名() {
重写方法;
};

我先创建一个Swim接口

public interface Swim {
void swim();
}

如果我现在要调用 swim 方法,该怎么做?

使用我们最为常规的方法,可以定义一个类然后实现 Swim 游泳接口,然后重写接口中的方法,再然后创建定义出来的那个类对象,利用类对象调用重写的 swim 游泳方法就可以了。

public class Student implements Swim{
    @Override
    public void swim() {
        System.out.println("重写的游泳方法");
    }
    // 定义 main 方法
    public static void main(String[] args) {
        // 创建 Student 类的对象 s1
        Student s1 = new Student();
        // 让对象 s1 调用实现的方法
        s1.swim();
    }
}

使用匿名内部类:

public class Student implements Swim{
    public static void main(String[] args) {
       new Swim() {
           @Override
           public void swim() {
              System.out.println("游泳");
           }
       }
    }
}

推到过程

public class Student implements Swim

这行代码代表类名,而匿名内部类就是没有类名,即不需要创建类。

那么这个没有名字的类,重写了Swim接口的方法,我们姑且把 Swim 接口放在这个没有名字的类的前面。

又因为创建对象为三要素之一,所以在Swim接口前加上new,后加上()。

解决代码冗余

我定义了一个 Animal 接口:

public interface Animal {
    void eat();
}

在另一个类中,我定义了一个静态方法,并且方法的参数需要传递一个 Animal 类型的对象,

public class Test6 {
    public static void method(Animal a){
        a.eat();
    }
}

现在,我要在测试类中调用 method 方法,该怎么办?

普通解决

创建 Dog 类 实现 Animal 接口

public class Dog implements Animal{
// 重写Animal 中的方法
    @Override
    public void eat() {
        System.out.println("狗吃东西");
    }
}
public class Test6 {
    public static void method(Animal a){
        a.eat();
    }
    public static void main(String[] args) {
        // 创建一个狗对象
        Dog dog = new Dog();
        // 将 dog 对象作为参数传递给 method
        method(dog);
    }
}

匿名内部类解决

public class Test6 {
    public static void method(Animal a){
        a.eat();
    }
 
    public static void main(String[] args) {
        method(new Animal() {
            @Override
            public void eat() {
                System.out.println("狗吃东西");
            }
        });
    }
}

注意:

匿名内部类语法本身创建出来的就是一个对象,既然是一个对象,它也可以作为对象调用它自己的方法

我们都知道接口不能创建对象,但现在我利用 多态 + 匿名内部类的方式就能创建 Animal 接口的对象,还能重写 eat 方法:

public class Test6 {
    public static void main(String[] args) {
        // 采用多态+匿名内部类的方式创建 Animal 接口的对象
        Animal animal = new Animal() {
            @Override
            public void eat() {
                System.out.println("猫吃小鱼");
            }
        };
        // 调用 animal 对象中的方法
        animal.eat();
    }
}