目录
抽象类
一、概念 abstract
1、如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类
2、没有功能主体的方法称为抽象方法,只有方法声明,没有方法体,且必须定义在抽象类中。
public abstract void draw();
二、特点
- 抽象类不可以被实例化,也就是不可以用new创建对象 ,但有构造方法。
- 抽象类通过其****子类实例化,而子类需要覆盖掉抽象类中所有的抽象方法后才可以创建对象,否则该子类也是抽象类。
- 方法只有声明没有实现时,该方法就是抽象方法,需要用abstract来修饰,抽象方法必须定义在抽象类中,该类也必须被abstract来修饰。
- 用abstract修饰的类就是抽象类。抽象方法必须定义在抽象类中。但是抽象类中不一定有抽象方法。
- 抽象类可以有成员属性和非抽象的成员方法。
- 抽象类可以没有抽象方法。
- 抽象类可以继承普通类与抽象类。
- 抽象类不能直接使用类名创建实例,但是有构造方法,构造方法是让子类进行初始化。
abstract与其他修饰符的关系:
final与abstract****不能共存:
- final:它的作用 修饰类代表不可以继承 修饰方法不可重写
- abstract修饰类就是用来被继承的,修饰方法就是用来被重写的。
- static 修饰的方法可以用类名调用,对于abstract修饰的方法没有具体的方法实现,所以不能直接调用,也就是说不可以与static共存。
- private 修饰的只能在本类中使用,abstract方法是用来被子类进行重写的,有矛盾所有不能共存
三、抽象类与普通类的异同
相同点
抽象类和普通类都是用来描述事物的,都在内部定义了成员。
不同点
- 普通类有足够信息来描述事物,抽身象类描述事物的信息有可能不够充足
- 普通类不能定义抽象方法,只能定义普通方法,
- 抽象类中可以定义抽象方法,同时也可以定义普通方法
- 普通类可以被实例化,抽象类不可以被实例化
四、练习
当子类继承父类时,子类的构造方法没有显示调用父类的构造的时候,子类的构造方法会默认调用父类的无参构造;
Shapes shapes=new Shapes(){}抽象类不能被实例化
public abstract class MyShapes {
public abstract double getLen();
public abstract double getArea();
}
public class Circle extends MyShapes {
private double r;
private static final double PI = 3.14;
public Circle(double r) {
this.r = r;
}
@Override
public double getLen() {
return 2 * PI * this.r;
}
@Override
public double getArea() {
return PI * this.r * this.r;
}
}
public class Rect extends MyShapes {
private double width;
private double height;
public Rect(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double getLen() {
return 2 * (this.width + this.height);
}
@Override
public double getArea() {
return this.width * this.height;
}
}
public class MyShapeTest {
public static void main(String[] args) {
System.out.println("长方形====================");
MyShapes rect=new Rect(20,40);
showMassage(rect);
System.out.println("圆形======================");
MyShapes circle=new Circle(8);
showMassage(circle);
}
public static void showMassage(MyShapes myShapes){
System.out.println("周长为:"+myShapes.getLen());
System.out.println("面积为:"+myShapes.getArea());
}
}
接口
五、接口 interface
1、在java中接口主要是使用来拓展定义类的功能,可以弥补Java中单继承的缺点。
- 接口(硬件类接口)人类与电脑等,或人类与程序之间的接口,称为用户界面。
- 接口(软件类接口)接口简单理解就是一种约定,接口是设计层面的概念,往往由设计师设计,将定义与实现分离,程序员实现接口,实现具体方法
- 接口的定义 :1) java接口是一系列方法的声明,是一些抽象的集合,一个接口只有抽象方法,没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。2)简单地说,接口就是特殊的抽象类,即所有方法都是抽象方法的抽象类就是Java中的接口
接口中的成员修饰符是固定的:
- public static final 修饰的变量变常量,该值不能改变,只能读
- 修饰符:可选,用于指定接口的访问权限,可选值为public。默认public。
- 接口名:必选参数,用于指定接口的名称,接口名必须是合法的Java标识符。首字母大写
- 方法:接口中的方法只有定义而没有被实现。
六、类和接口的关系,接口与接口
1、类和接口实现关系 implement
- 类实现接口
- 接口不可以被实例化
- 实现了接口的实现类必须重写接口中所有抽象方法后,这个子类才可以被实例化,否则报错。
- 一个类可以实现多个接口,多个接口用逗号分隔,也可以不实现接口。
- Java不支持多继承,Java将多继承机制进行改良变成了多实现
- 一个类在继承另一个类同时,还可以实现多个接口
- 接口的出现避免了单继承的局限性
2、接口与接口继承关系,extends 可以多继承
/**
* 接口的所有方法都是抽象方法,所以abstract可以省略
* 接口的方法是方法的规范,为其他实现类服务,所以默认为public,且可以省略
*/
public interface Pencil {
double PI = 3.14;//等价于public static final double PI = 3.14;
void write();//等价于public abstract void write();
}
public interface Eraser {
void remove();
}
public interface InterfaceTest **extends** Pencil,Eraser{
//接口可以多继承其它接口
}
public class PencilTest {
public static void main(String[] args) {
System.out.println(Pencil.PI);
// PI是final
// Pencil.PI=12;
}
}
public class PencilWithEraser **implements** Pencil,Eraser{
//public class PencilWithEraser implements InterfaceTest{
@Override
public void remove() {
System.out.println("删除数据");
}
@Override
public void write() {
System.out.println("写数据");
}
}
public class PencilWithEraserTest {
public static void main(String[] args) {
// PencilWithEraser pwe=new PencilWithEraser();
// pwe.write();
// pwe.remove();
//实现多个接口,使用接口类型接受new的对象,会出现有的接口类型没有对应方法,进行强转成对应接口类型
Pencil pwe=new PencilWithEraser();//Pencil里面没有remove()方法
pwe.write();
Eraser eas=(Eraser) pwe;
eas.remove();
}
}
七、接口可实现多继承的原因:
接口继承与类继承对比:
Java类的继承是单一继承,Java接口的继承是多重继承
接口可实现多继承原因分析:
不允许类多重继承的主要原因是,如果A同时继承B和C,而B和C同时有一个D方法,A无法确定该继承那一个
接口全都是抽象方法,不存在实现冲突,继承谁都可以,所以接口可继承多个接口
八、JDK8接口的默认方法
Java8之前,Java接口纯粹是契约的集合,是一种程序设计的表达方式。从数据抽象的角度看,能够在不定义class的同时又可以定义type,是程序设计中强大而有用的机制。Java接口就是这些纯粹的接口组成的数据抽象
Java8之前,接口不能升级。因为在接口中添加一个方法,会导致老版本接口的所有实现类的实现新的方法(涉及到的类全部改动)
Java8之后,lambda表达式作为核心出现,为了配合lambda表达式,JDK中Collection库需要添加新的方法,如forEach(),stream()等,于是引入了默认方法(defender methods,Virtual extension methods)。
默认方法是库/框架设计者的后悔药。对于以前的遗留代码,大家都不知道有这个新方法,既不会调用,也不会去实现,如同不存在;编写新代码的程序员可以将它视为保底的方法体。类型层次中任何符合override规则的方法,优先于默认方法,因为遗留代码可能正好有同样的方法存在
默认方法的声明很简单,直接在接口中把方法声明为default,之后再写方法的实现即可。这样所有的实现类都会继承这个方法:
默认方法理论上抹杀了Java接口与抽象类的本质区别-前者是行为契约的集合,后者是接口与实现的结合体。当然,语法上两者的差别和以前一样。这就需要我们来自觉维护两者的本质区别,把默认方法作为库、框架向前兼容的手段
实例化实现接口的匿名类对象
public interface Apple {
void print();
}
public class AppleTest {
public static void main(String[] args) {
AppleTest test = new AppleTest();
//new 是一个实现Apple接口的匿名类(构造方法),创建了实现Apple接口的匿名类对象
Apple a = new Apple() {
//匿名类的实现接口的方法由程序员来完成
@Override
public void print() {
System.out.println("我是苹果");
}
};
//print就是存在就是上面写 匿名类对象的print方法
a.print();
}
}