写在前面:今天上课讲了接口、类之间的继承,听得我糊里糊涂,准备自己学习一下,写一篇博客来记录我的学习!
Java接口
我在理解的过程中遇到了很多困难,理解接口,要先理解几个概念:
几个概念
1.抽象类
⾯向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是⽤来描绘对象的,如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象,这样的类就是抽象类。在Java语言中抽象类用abstract来修饰。
抽象类是它的所有子类的公共属性的集合,抽象类可以包含一般方法和抽象方法。需要注意的是,抽象类不一定有抽象方法,但有抽象方法的类一定是抽象类。
2.抽象方法
抽象方法在方法头后直接跟分号,定义时用abstract修饰;而一般方法含有以大括号框住的方法体。抽象方法只声明了形式,而没有具体操作。
abstract class A{//定义一个抽象类
public void fun(){//普通方法
System.out.println("存在方法体的方法");
}
public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰
}
接口的概念
接口(Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
就是说,接口可以很字面的理解为连接了类和一些抽象方法。而定义接口的目的就是是的所有继承接口的类对外呈现相同名字的方法。
接口和类(抽象类)
接口并不是类!
二者的区别在于:
- 接口不能用于实例化对象。
- 接口没有构造方法。
- 接口不能包含成员变量,除了 static 和 final 变量。
- 接口不是被类继承了,而是要被类实现。
- 接口支持多继承。
接口是要被类实现的!
一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
接口也不是抽象类!
区别:
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
- 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
- 接口和它内部的每个方法都是隐式抽象的,在声明的时候不用写abstract。
接口的声明
/* 文件名 : Animal.java */
interface Animal {
public void eat();
public void travel();
}
接口的实现(实现类)
/* 文件名 : MammalInt.java */
public class MammalInt implements Animal{
public void eat(){
System.out.println("Mammal eats");
}
public void travel(){
System.out.println("Mammal travels");
}
public int noOfLegs(){
return 0;
}
public static void main(String args[]){
MammalInt m = new MammalInt();
m.eat();
m.travel();
}
}
实现接口的一些规则:
- 一个类可以同时实现多个接口。
- 一个类只能继承一个类,但是能实现多个接口。
- 一个接口能继承另一个接口,这和类之间的继承比较相似。
方法重写
上面提到的类MammalInt实现了接口Animal,该类中对Animal中的抽象方法进行了重写。
重写接口中声明的方法时,需要注意以下规则:
- 类在实现接口的方法时,不能抛出强制性异常,只能在接口中,或者继承接口的抽象类中抛出该强制性异常。
- 类在重写方法时要保持一致的方法名,并且应该保持相同或者相兼容的返回值类型。
- 如果实现接口的类是抽象类,那么就没必要实现该接口的方法。
接口的继承
接口之间的继承和类之间的继承很类似,都是用extends,子接口继承了父接口中的所有方法,因而要实现子接口的实现类就需要实现子接口和他继承的父接口中的所有抽象方法。
Football接口自己声明了3个方法,从Sports接口继承了2个方法,这样,实现Football接口的类需要实现5个方法。
// 文件名: Sports.java
public interface Sports
{
public void setHomeTeam(String name);
public void setVisitingTeam(String name);
}
// 文件名: Football.java
public interface Football extends Sports
{
public void homeTeamScored(int points);
public void visitingTeamScored(int points);
public void endOfQuarter(int quarter);
}
Java继承
字面意思理解,这里比接口好理解很多。继承就是儿子具有了父亲的东西。子类继承父类的特征和行为,就使得子类对象具有父类的实例域和方法,可以实现父类方法可以做出的行为。至于为什么要定义继承这种行为,个人理解是因为避免一些极其相似的代码重复出现,如果允许不同的类的对象具有相同或者相似的行为,就可以将这种行为定义在父类中,通过继承(方法重写)是的子类对象获得作出该行为的许可。
继承:
public class Animal {
private String name;
private int id;
public Animal(String myName, int myid) {
//初始化属性值
}
public void eat() { //吃东西方法的具体实现 }
public void sleep() { //睡觉方法的具体实现 }
}
public class Penguin extends Animal{
}
一些要记住的规则:
- Java中的继承用extends关键字来声明。
- Java 不支持多继承,但支持多重继承。
- 当一个类没有继承的两个关键字,则默认继承object祖先类。
- 有一些地方……不区分继承和实现,所以也可以说使用 implements 关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔)。
- 子类继承了父类所有属性和方法。“所有”中只是包含public,protected修饰的,private修饰的属性和方法子类不能直接使用。
- 使用 final 关键字声明类,就是把类定义定义为最终类,不能被继承。如果final用来修饰方法,该方法不能被子类重写。
-
super和this关键字:super用来引用当前对象的父类和实现对父类成员的访问;this用来声明指向自己的引用。具体如下
class Animal {
void eat() {
System.out.println("animal : eat");
}
}
class Dog extends Animal {
void eat() {
System.out.println("dog : eat");
}
void eatTest() {
this.eat(); // this 调用自己的方法
super.eat(); // super 调用父类方法
}
}
继承中的构造器
子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用。
子类调用父类的构造器的方式有以下两种:
- 显式调用:如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。
- 隐式调用:如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。
子类构造器有没有参数、参数的类型是什么和调用哪一个父类的构造器没有关系!
class SuperClass {
private int n;
SuperClass(){//父类的无参数构造器
System.out.println("SuperClass()");
}
SuperClass(int n) {//父类的有参数构造器
System.out.println("SuperClass(int n)");
this.n = n;
}
}
class SubClass extends SuperClass{
private int n;
SubClass(){//子类的无参数构造器
super(300); // 调用父类中带有参数的构造器
System.out.println("SubClass");
}
public SubClass(int n){ //子类的有参数构造器
// 自动调用父类的无参数构造器
System.out.println("SubClass(int n):"+n);
this.n = n;
}
}
继承之后的一些调用
比如我们有如下所示的继承关系,显然子类对父类的方法进行了从写,
class Animal{
int age=0;
public int growUp(){
age++;
}
}
class Dog extends Animal{
int age=0;
public int growUp(){
age+=2;
}
}
如果在main函数中有以下语句:
Animal a=new Dog();
int gUp=a.growUp();
那么这个时候调用的growUp方法究竟是父类Animal里的还是子类Dog里的?
编译时看对象a是属于那个类、a是那个类的引用(在这里a是Animal类的),看这个类中是否有名字叫growUp,无参数的返回一个int型的方法,如果没有就会产生报错;运行时看的是a具体是个什么类型的对象(这里a是Dog类的对象),调用这个类中的对应方法,没有也是会报错的。
方法中对属性进行了更改,更改的age是父类Animal里的还是子类Dog里的?
更改的是和被调用的方法处于同一类中的属性。
重写和重载
重写:和原方法有相同的signature(保持一致的方法名,并且应该保持相同或者相兼容的返回值类型),重写后的方法与原方法是一个类型的方法。
注意如果在子类中进行了对父类的方法重写后还想调用父类的方法,就需要用super.方法名进行复用。
重载:多个方法具有同样的名字,但是有不同的参数列表或者返回值类型。在编一阶段根据方法的参数或者返回值决定调用哪一个方法。要看是哪一个类型的引用!
比如(我也不知道我写的对不对,大概就是这个意思……)
class Animal{
int age=0;
public void growUp(){
age++;
}
}
class Dog extends Animal{
int age=0;
public int growUp(Dog a){
age+=a.age;
}
}
public class useAnimal{
public void doStuff(Animal a){
System.out.println("animal");
}
public void doStuff(Dog a){
System.out.println("dog");
}
}
public class testAnimal{
public static void main(String [] argv){
Animal animalobj=new Animal();
Dog dogobj=new Dog();
Animal obj=new Dog();
useAnimal us=new useAnimal();
us.doStuff(obj);//打印animal,因为a是Animal类的引用
}
}
脑仁儿疼,好不容易感觉学明白一点点,后续有进一步学习再更新!
致谢: