文章目录
抽象方法和抽象类
抽象方法
使用abstract修饰的方法,没有方法体,只有声明。定义的是一种规范,就是告诉子类必须要给抽象方法提供具体的实现(子类也可以部分实现或完全不实现父类的所有抽象方法,但此时子类必须声明为抽象类)。
抽象类
抽象类的概念以及使用
- 定义了抽象方法的类必须被声明为抽象类,不过抽象类可以没有抽象方法。
- 抽象类可以包含属性、方法、构造方法。但是构造方法不能用来new实例,只能用来被子类调用。
- 抽象类只能用来被继承,不能使用new关键字创建对象。
- 子类必须要给抽象方法提供具体的实现,子类也可以部分实现或完全不实现父类的所有抽象方法,但此时子类必须声明为抽象类。
为什么需要抽象类
- 抽象方法和抽象类看起来像是多余的,对于抽象方法,不知道如何实现,定义一个空的方法体不就好了,抽象类不能创建对象,看起来只是添加一个限制条件。
- 在Java中引入抽象方法和抽象类,是一种规范体现,对于某些类和方法的使用,能引导使用者正确的使用它们。
(1)使用抽象方法而不是使用非空方法体,子类就知道它必须实现该方法,而不能忽略,忽略的话编译器就会报错。
(2)使用抽象类,类的使用者创建对象的时候,就知道要使用某个具体子类,而不可能使用不完整的抽象类。
用一个植物的例子更直观的明白抽象类以及抽象方法的使用:
public class AbstractTest {
public static void main(String[] args) {
Flower flower=new Flower();
flower.plant();
flower.growsIn();
Aqua_plant aqua_plant=new Aqua_plant();
aqua_plant.plant();
aqua_plant.growsIn();
}
}
abstract class Plant{
//普通方法
public void plant(){
System.out.println("都是植物");
}
//抽象方法
public abstract void growsIn();
}
class Flower extends Plant{
@Override
public void growsIn() {
System.out.println("花生在土里。");
}
}
class Aqua_plant extends Plant{
@Override
public void growsIn() {
System.out.println("水草生在水里。");
}
}
接口
接口的概念
接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束。全面地专业地实现了:规范和具体实现的分离。
接口的使用
声明格式:
[访问修饰符] interface 接口名 [extends 父接口1,父接口2…] {
常量定义;
方法定义;
(静态方法定义;)
}
- 访问修饰符:只能是public或默认。
- 接口名:和类名采用相同命名机制。
- extends:接口可以多继承。
- 常量:接口中的属性只能是常量,总是:public static final 修饰。不写也是。
- 方法:接口中的方法只能是:public abstract。 省略的话,也是public abstract。
- JDK1.8之后,接口可以包含普通的静态方法
子类使用方法:
- 子类通过implements来实现接口中的规范,一个类可以实现多个接口。
- 接口不能创建实例,但是可用于声明引用变量类型。
- 一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是public的。
- JDK1.7之前,接口中只能包含静态常量、抽象方法,不能有普通属性、构造方法、普通方法,JDK1.8后,接口可以包含普通的静态方法。
接口与抽象类的区别
接口和抽象类中都包含了抽象类,但是接口和抽象类有以下几种区别:
- 接口是多继承的,而类是单继承。
- 一个类可以实现任意多个接口,但最多只能作为一个抽象类的子类。
- 一个抽象类可以有若干个抽象方法(但到少要有一个),而接口的所有方法都是抽象的,无论是否将它的方法显示地声明为抽象的。
- 一个抽象类可以声明实例变量,其子类可以继承这些实例变量。而一个接口不能声明实例变量,不过接口可以声明static final修饰域。
- 抽象类可以有构造方法,而接口不能,但是两者都不能直接实例化,都必须通过其他类的实现才能使用。
- 抽象类的可见性修饰符可以是public、protected、private或无修饰符(表示包内可见);而接口的可见性修饰符只能是 public,或无修饰符(包内可见)。
- 抽象类的方法的可见性修饰符可是以protected、private,或无(表示包内可见);而一个接口的方法的可见性修饰符只能是 public (JDK1.8之后,接口可以包含普通的静态方法)。
- 抽象类是从object类派生而来,它继承了object的clone()和equals()方法。
- 设计模式讲,继承强调是:is-a;而接口强调的则是has-a。
使用一个人的例子更直观的了解下接口:
public class InterfaceTest {
public static void main(String[] args) {
People.say(); //调用接口中的静态方法需要通过接口来调用
Teacher teacher=new Teacher();
teacher.profession();
teacher.work();
Student student=new Student();
student.profession();
student.work();
}
}
interface A{
}
interface B{
}
interface People extends A,B/*接口的多继承*/{
//定义静态常量
public static final int a=10;
/*public static final */boolean flag=true; //默认也是public static final修饰
//定义抽象方法
/*public abstract */void profession(); //默认也是public abstract修饰
public abstract void work();
//JDK1.8之后,接口可以包含普通的静态方法
public static void say(){
System.out.println("Hello!");
}
}
//子类必须实现接口中所有的抽象方法(静态方法就不需要实现)
class Teacher implements People,A,B/*子类可以实现多个接口*/ {
@Override
public void profession() {
System.out.print("教师");
}
@Override
public void work() {
System.out.println("教学");
}
}
class Student implements People{
@Override
public void profession() {
System.out.print("学生");
}
@Override
public void work() {
System.out.println("学习");
}
}
内部类
内部类的概念
内部类我们从外面看是非常容易理解的,无非就是在一个类的内部在定义一个类。一般情况,我们把类定义成独立的单元,有些情况下,我们把一个类放在另一个类的内部定义,称为内部类。内部类可以使用public、default、protected 、private以及static修饰,而外部类(我们以前接触的类)只能使用public和default修饰。
注意事项: 内部类只是一个编译时概念,一旦我们编译成功,就会成为完全不同的两个类。对于一个名为Outer的外部类和其内部定义的名为Inner的内部类。编译完成后会出现Outer.class和Outer$Inner.class两个类的字节码文件。所以内部类是相对独立的一种存在,其成员变量/方法名可以和外部类的相同。
内部类的作用
-
内部类提供了更好的封装。只能让外部类直接访问,不允许同一个包中的其他类直接访问。
-
内部类可以直接访问外部类的私有属性,内部类被当成其外部类的成员。 但外部类不能访问内部类的内部属性。
-
接口只是解决了多重继承的部分问题,而内部类使得多重继承的解决方案变得更加完整。
-
由于内部类提供了更好的封装特性,并且可以很方便的访问外部类的属性。所以,在只为外部类提供服务的情况下可以优先考虑使用内部类。
-
使用内部类间接实现多继承:每个内部类都能独立地继承一个类或者实现某些接口,所以无论外部类是否已经继承了某个类或者实现了某些接口,对于内部类没有任何影响。
内部类的分类
在Java中内部类主要分为成员内部类(非静态内部类、静态内部类)、匿名内部类、局部内部类。
成员内部类
非静态内部类
- 外部类里使用非静态内部类和平时使用其他类没什么不同。
- 非静态内部类必须寄存在一个外部类对象里。因此,如果有一个非静态内部类对象那么一定存在对应的外部类对象。非静态内部类对象单独属于外部类的某个对象。
- 非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类成员。
- 非静态内部类不能有静态方法、静态属性和静态初始化块。
- 外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例。
- 非静态内部类成员变量的访问方法:A.内部类里方法的局部变量:变量名。B.内部类属性:this.变量名。C.外部类属性:外部类名.this.变量名。
- 内部类的访问方法:A.外部类中定义内部类:
new 内部类名()
;B.外部类以外的地方使用非静态内部类:外部类名.内部类名 变量名= new 外部类名().new 内部类名()。
静态内部类
- 当一个静态内部类对象存在,并不一定存在对应的外部类对象。 因此,静态内部类的实例方法不能直接访问外部类的实例方法。
- 静态内部类看做外部类的一个静态成员。 因此,外部类的方法中可以通过:
静态内部类.名字
的方式访问静态内部类的静态成员,通过new 静态内部类()
访问静态内部类的实例。在外部类以外的地方使用可以通过外部类名.内部类名 变量名 =new 外部类名.内部类名();
来访问。
匿名内部类
匿名内部类适用于仅需要使用一次的类。
- 静态内部类的语法:
new 父类构造器(实参类表)或实现接口() {
//匿名内部类类体
}
- 注意事项:
- 匿名内部类没有访问修饰符。
- 匿名内部类没有构造方法。因为它连名字都没有那又何来构造方法呢。
局部内部类
局部内部类是定义在方法内部的,作用域只限于本方法。局部内部类的用法和成员内部类的用法一样,但是它只能在定义的方法体中使用,离开了方法体就失效。