目录
一、抽象类
1.1. 抽象的概念
1.2. 抽象类语法
1.3. 抽象类的特性
1.4. 图形类例子
二、 接口
2.1. 接口的概念
2.2. 语法规则
2.3. 接口的特性
2.4. 接口的使用
2.5. 实现多个接口
2.6. 工作当中常用的接口
一、抽象类
1.1. 抽象的概念
如果 一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。没有实际工作的方法, 我们可以把它设计成一个 抽象方法(abstract method), 包含抽象方法的类我们称为 抽象类(abstract class)。
就比如,Cat与Dog都属于Animal类,并且前两者与Animal都属于继承关系,但Animal不是一个具体的动物,也就无法实现.Bark()方法。
1.2. 抽象类语法
在Java中,一个类如果被 abstract 修饰称为抽象类,抽象类中被abstract 修饰的方法称为抽象方法,抽象方法不用给出具体的实现体。
1.3. 抽象类的特性
public abstract class Animal { public int age; public String name; public Animal(int age, String name) { this.age = age; this.name = name; } public void eat(){ System.out.println(this.name+"正在吃饭"); }}public class Dog extends Animal{ public Dog(int age, String name) { super(age, name); } @Override public void eat() { System.out.println(this.name+"正在吃狗粮"); }}public class Main { public static void main(String[] args) { Dog dog = new Dog(5,"泰克"); Animal animal = new Animal() }}
就比如上面的代码中,Animal不是一个具体的动物,无法用成员变量对其进行赋值。那我们就可以在Animal这个类加上一个abstract,就把Animal变成抽象类,并且不能再new一个新对象。在抽象类中,如果一个方法没有具体的实现,前面就用abstract修饰。但如果一个类没有被抽象,那么这个类中就不能有抽象方法。
如果一个类继承了一个抽象类,那么抽象类里的方法必须进行重写,否则就会报错(如下面的代码所示)。
public abstract class Animal { public int age; public String name; public abstract void eat(){//产生报错 System.out.println(this.name+"be eating"); } public Animal(int age, String name) { this.age = age; this.name = name; }}public class Dog extends Animal{//产生报错 public Dog(int age, String name) { super(age, name); } }
如果一个类继承了这个抽象类,然后不想重写抽象类当中的方法,只能把这个类也设置为抽象类。但最终还是得让一个普通类来继承这个抽象类,所以这些抽象方法最终都得被重写。
public abstract class Animal { public int age; public String name; public abstract void eat(); public Animal(int age, String name) { this.age = age; this.name = name; }}
public abstract class Dog extends Animal{//将Dog类也进行抽象化 public Dog(int age, String name) { super(age, name); }/* @Override public void eat() { System.out.println(this.name+"吃狗粮"); }*/不用再重写Animal类里的方法了 public abstract void bark();}class Labrador extends Dog{//再用一个普通类继承Dog类 public Labrador(int age, String name) { super(age, name); } @Override public void eat() { } @Override public void bark() { }}
而Labrador里面的这些方法怎么快速重写呢?将鼠标光标放在普通类后面,alt+回车,然后点击implement。但还是会出现报错,再次alt+回车,点击“Creat constructor……”,就能将成员变量重写,最后才能生成如上图所示的代码。
1.4. 图形类例子
抽象类已经不能被实例化,但是还可以进行向上转型与多态。
public abstract class Shape { public abstract void Draw();}public class Rect extends Shape{ @Override public void Draw() { System.out.println("画一个矩形"); }}public class Circle extends Shape{ @Override public void Draw() { System.out.println("画一个圆"); }}public class Flower extends Shape{ @Override public void Draw() { System.out.println("花一朵花"); }}
public class Main { public static void DrawOut(Shape picture){ picture.Draw(); } public static void main(String[] args) { Flower flower = new Flower(); DrawOut(flower); DrawOut(flower); DrawOut(flower); System.out.println("=========="); DrawOut(new Flower()); DrawOut(new Flower()); DrawOut(new Flower()); }}
运行结果如下: 虽然说打印结果一致,但其中的原理不同。
二、 接口
2.1. 接口的概念
在现实生活中,接口的例子比比皆是,比如:笔记本上的USB口,电源插座等。电脑的USB口上,可以插:U盘、鼠标、键盘;所有符合USB协议的设备 电源插座插孔上,可以插:电脑、电视机、电饭煲...所有符合规范的设备。
综上所述,接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。 在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。
2.2. 语法规则
public interface 接口名称 {}
我们如果点开out.目录底下的文件,可以找到接口对应的字节码文件,并且一个接口只对应一个字节码文件。接口名称一般以大写字母I开头,后面再跟一个形容词词性的单词。
2.3. 接口的特性
(1)接口不能直接实例化
public interface IShape {}public class Main { public static void main(String[] args) { IShape iShape = new IShape();//产生报错 }}
(2)接口中的成员变量,默认是public static final修饰的
int a = 10;//默认为public static finalpublic int b = 20;private int c = 30;//报错:Modifier 'private' not allowed herepublic static final int d = 40;
(3)接口当中的方法,要是有具体的实现,就只能被static或default修饰,这些方法默认都是public 修饰的
public void test(){ System.out.println("========");//Interface abstract methods cannot have body}default public void Hello(){ System.out.println("========");}
(4)接口当中的方法,要是没有具体的实现,就要写成public static修饰的抽象方法,默认也为public static。
public abstract void World();//warning:Modifier 'public''abstract' is redundant for interface membersvoid DrawOut();
2.4. 接口的使用
一个类要想与与接口建立联系,要使用implements实现。实现之后,还要进行方法的重写。
public interface IShape { public abstract void World(); void DrawOut();}public class Flower implements IShape{ @Override public void DrawOut() { System.out.println("画一朵花"); }}public class Main { public static void main(String[] args) { IShape iShape = new Flower();//通过向上转型来实例化 iShape.DrawOut(); }}
2.5. 实现多个接口
我们在上一期当中讲到了多态,多态能够降低代码的 "圈复杂度"。再有了接口之后, 类的使用者就不必关注具体类型, 而只关注某个类是否具备某种能力。这样我们在Main类里面再写一个Walk、Fly、Swim方法,无论有多少个参数,我们都能通过接口来进行传递。只要具备Walk、Fly、Swim这种能力的,我都能接受,不必关心什么类型的。
//父类对象public abstract class Animal { public String name; public int age; public Animal(String name, int age) { this.name = name; this.age = age; } public abstract void eat();}//子类对象public class Dog extends Animal implements ICanRun, ICanSwim { public Dog(String name, int age) { super(name, age); } @Override public void eat() { System.out.println(this.name+"正在吃狗粮"); } @Override public void run() { System.out.println(this.name+"正在跑"); } @Override public void swim() { System.out.println(this.name+"正在游泳"); }}public class Bird extends Animal implements ICanFly{ public Bird(String name, int age) { super(name, age); } @Override public void eat() { } @Override public void fly() { System.out.println(this.name+"正在飞"); }}
//接口public interface ICanFly { void fly();}public interface ICanRun { void run();}public interface ICanSwim { void swim();}
public class Main { public static void Fly(ICanFly canFly){ canFly.fly(); } public static void Walk(ICanRun canRun){ canRun.run(); } public static void Swim(ICanSwim canSwim){ canSwim.swim(); } public static void main(String[] args) { Dog dog = new Dog("斯派克",4); Bird bird = new Bird("金丝雀",1); Duck duck = new Duck("小黄鸭",1); Walk(duck); Walk(dog); System.out.println("==========="); Swim(duck); Swim(dog); System.out.println("============"); Fly(bird); Fly(duck); }}
执行结果:
2.6. 工作当中常用的接口
(1)引用类型的比较
public class Student { public int age; public int score; public String name; public Student(int age, int score, String name) { this.age = age; this.score = score; this.name = name; } @Override public String toString() { return "Student{" + "age=" + age + ", score=" + score + ", name='" + name + '\'' + '}'; }}public class Main { public static void main(String[] args) { Student student1 = new Student(10,74,"张三"); Student student2 = new Student(8,84,"李四"); if(student1 > student2)//报错:Operator '>' cannot be applied to 'Student', 'Student' Statement expected }}
<、>、=等关系运算符只能用于基本数据类型,在Java语法不支持地址的比较。要想实现引用类型的比较,就可以引入一个Comparable接口。我们看一下Comparable的源码,里面只有一个参数。同样需要重写一个方法。而我们要想进行比较,可以使用工具,比如我们新建一个AgeCompare类,需要接口Comparator,也需要重写方法。
public int compareTo(T o);
@Overridepublic int compareTo(Student o) { return 0;}
如果我们想进行年龄的比较,就可以通过改返回值来进行比较。以下是完整代码:
//定义Student类public class Student implements Comparable<Student>{ public int age; public int score; public String name; public Student(int age, int score, String name) { this.age = age; this.score = score; this.name = name; } @Override public String toString() { return "Student{" + "age=" + age + ", score=" + score + ", name='" + name + '\'' + '}'; } @Override public int compareTo(Student o) { return 0; }}
//比较的工具import java.util.Comparator;public class AgeCompare implements Comparator<Student> { @Override public int compare(Student o1, Student o2) { return o1.age - o2.age; }}
public class Main { public static void main(String[] args) { Student student1 = new Student(10,74,"张三"); Student student2 = new Student(8,84,"李四"); System.out.println("进行年龄的比较"); AgeCompare ageCompare = new AgeCompare(); if(ageCompare.compare(student1,student2) > 0) { System.out.println("student1 > student2"); }else { System.out.println("student1 < student2"); } }}
运行结果如下: