一、代码块
代码块分为两类,使用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;
}
}
结果:
细节
-
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();
}
}