Java笔记_廖雪峰
1、抽象类
如果父类的方法本身不需要实现任何功能,仅仅是为了定义方法签名,目的是让子类去覆写它,那么,可以把父类的方法声明为抽象方法。
如果一个class定义了方法,但没有具体执行代码,这个方法就是抽象方法,抽象方法用abstract
修饰。
因为无法执行抽象方法,因此这个类也必须申明为抽象类(abstract class)。
使用abstract修饰的类就是抽象类。我们无法实例化一个抽象类,因为抽象类本身被设计成只能用于被继承,因此,抽象类可以强迫子类实现其定义的抽象方法,否则编译会报错。因此,抽象方法实际上相当于定义了“规范”。
为什么需要抽象类?
抽象方法是空方法体,子类继承抽象类必须要重写抽象方法。那定义一个空方法体不就行了吗?不行的。使用抽象方法而非空方法体,子类就知道它必须要实现该抽象方法,否则编译器会出错。
小结:
-
通过abstract定义的方法是抽象方法,它只有定义,没有实现。抽象方法定义了子类必须实现的接口规范;
-
定义了抽象方法的class必须被定义为抽象类,从抽象类继承的子类必须实现抽象方法;
-
如果不实现抽象方法,则该子类仍是一个抽象类;
-
面向抽象编程使得调用者只关心抽象方法的定义,不关心子类的具体实现。
2、接口
如果一个抽象类没有字段,所有方法全部都是抽象方法,就可以把该抽象类改写为接口:interface
。
所谓interface
,就是比抽象类还要抽象的纯抽象接口,因为它连字段都不能有(可以有public static final字段)。因为接口定义的所有方法默认都是public abstract
的,所以这两个修饰符不需要写出来(写不写效果都一样)。
一个类可以实现多个interface
。
在接口中,可以定义default
方法。实现类可以不必覆写default
方法。default方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default
方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法。
default
方法和抽象类的普通方法是有所不同的。因为interface
没有字段,default
方法无法访问字段,而抽象类的普通方法可以访问实例字段。
使用接口的优点:
- 代码复用: 同一套代码可以处理多种不同的数据类型。
- 降低了耦合,提高了灵活性
接口的细节:
- 接口中可以定义变量,修饰符
public static final
,可以写也可以不写。 - 一个接口可以继承其他接口,还可以有多个父接口。
- 类可以在继承基类的情况下,同时实现一个或多个接口。
- 可以用
instanceof
判断一个对象是否实现了某接口。
需要说明:
以上都是Java8之前的接口特性,Java8和Java9允许在接口中定义静态方法和默认方法。
小结:
-
Java的接口(interface)定义了纯抽象规范,一个类可以实现多个接口;
-
接口也是数据类型,适用于向上转型和向下转型;
-
接口的所有方法都是抽象方法,接口不能定义实例字段;
-
接口可以定义default方法(JDK>=1.8)。
3、接口与继承的比较
abstract calss | interface | |
---|---|---|
继承 | 子类只能extends一个abstract calss | 可以implements多个interface |
字段 | 可以定义实例字段 | 不能定义实例字段 |
抽象方法 | 可以定义抽象方法 | 可以定义抽象方法 |
非抽象方法 | 可以定义非抽象方法 | 不能定义非抽象方法 |
联系: 抽象类和接口是配合而非替代关系,他们经常一起使用,接口声明能力,抽象类提供默认实现,实现全部或部分方法,一个接口经常有一个对应的抽象类。(Java编程的逻辑P110)
4、Java中访问修饰符public、private、protect、default范围
自身 | 同包子类 | 不同包子类 | 同包类 | 其他类 | |
---|---|---|---|---|---|
private | 访问 | 访问,不能继承 | 访问,不能继承 | 不能访问 | 不能访问 |
package/friendly/default | 访问 | 继承 | 访问,不能继承 | 继承 | 不能访问 |
protected | 访问 | 继承 | 继承 | 访问 | 不能访问 |
public | 访问 | 继承 | 继承 | 访问 | 访问 |
5、内部类(Nested class/Inner Class)、静态内部类(Static Nested Class)和匿名类(Anonymous Class)
内部类分为四种:
- 非静态内部类
- 静态内部类
- 匿名类
- 本地类
非静态内部类(普通内部类Nested class/Inner Class)
class Outer {
class Inner {
// 定义了一个Inner Class
}
}
上述定义的Outer
是一个普通类,而Inner
是一个Inner Class
,它与普通类有个最大的不同,就是Inner Class
的实例不能单独存在,必须依附于一个Outer Class
的实例。
语法: new 外部类().new 内部类()
Inner Class
除了有一个this
指向它自己,还隐含地持有一个Outer Class
实例,可以用Outer.this
访问这个实例。所以,实例化一个Inner Class
不能脱离Oute
r实例。
Inner Class
和普通Class
相比,除了能引用Outer
实例外,还有一个额外的“特权”,就是可以修改Outer Class
的private
字段,因为Inner Class
的作用域在Outer Class
内部,所以能访问Outer Class
的private
字段和方法。
静态内部类(Static Nested Class)
与非静态内部类不同,静态内部类的实例化 不需要一个外部类的实例为基础,可以直接实例化
语法:new 外部类.静态内部类()
因为没有一个外部类的实例,所以在静态内部类里面不可以访问外部类的实例属性和方法
除了可以访问外部类的私有静态成员外,静态内部类和普通类没什么大的区别
匿名类(Anonymous Class)
匿名类指的是在声明一个类的同时实例化它,使代码更加简洁精练
通常情况下,要使用一个接口或者抽象类,都必须创建一个子类
有的时候,为了快速使用,直接实例化一个抽象类,并“当场”实现其抽象方法。
既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。
这样的类,叫做匿名类
匿名类和Inner Class
一样,可以访问Outer Class
的private
字段和方法。之所以我们要定义匿名类,是因为在这里我们通常不关心类名,比直接定义Inner Class
可以少写很多代码。
本地类
本地类可以理解为有名字的匿名类
内部类与匿名类不一样的是,内部类必须声明在成员的位置,即与属性和方法平等的位置。
本地类和匿名类一样,直接声明在代码块里面,可以是主方法,for循环里等等地方