黑马程序员,Java基础知识四:继承

来源:互联网 发布:seo基础入门教程 编辑:程序博客网 时间:2024/05/19 23:56

继承


继承的概念:

当我们在开发中,有多个类存在相同属性和行为时,就将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承单独的那个类即可。多个类可以称为子类,单独的这个类称为父类或者超类。子类可以直接访问父类中的非私有的属性和行为。通过extends关键字让类与类之间产生继承关系,例如:

class SubDemo extends Demo{}

继承的出现提高了代码的复用性,它让类与类之间产生了关系,它为多态提供了前提。

特点:

1.Java只支持单继承,不支持多继承。也就是说一个类只能有一个父类,不可以有多个父类。

2.Java支持多层继承,形成继承体系。例如:

class A{}class B extends A {}class C extends B {}

注意:

1.不要仅仅为了获取其他类中的某个功能而去继承。

2.类与类之间要有所属关系,即A是B的一种,才可以让A去继承B。

super关键字

super和this的用法相同,只是this代表本类引用,而super代表父类引用。

在子类中定义成员时与父类成员名相同时,可以用super进行区分。

而当子类要调用父类构造函数时,可以使用super语句。

重写

重写override,也称复写,或者函数覆盖,是当子类中出现与父类相同的方法时,会出现覆盖的操作。

父类中的私有方法不可以被覆盖。

在子类的覆盖方法中,继续使用被覆盖的方法可以通过super.函数名来获取

注意:

1.只能用静态方法去覆盖另一个静态方法。

2.覆盖时,子类方法权限一定要大于等于父类方法权限,权限修饰符的大小顺序:public > protected > default > private  (其中default是方法的默认权限,不必写出来)

应用:

当子类需要父类的功能,而功能主体子类有自己特有的内容时,可以复写父类中的方法,这样既沿袭了父类的功能,又定义了子类特有的内容。

子类的实例化过程

当我们对子类对象进行初始化时,父类的构造函数也会运行,那是因为子类的构造函数默认第一行有一条隐式的语句super();这条语句会访问父类中空参数的构造函数,而且子类中所有的构造函数默认的第一行都是super();
为什么子类一定要访问父类中的构造函数?
因为父类中的数据可以在子类中直接获取,所以当子类对象建立时,需要先查看父类是如何对这些数据进行初始化的。
如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。
注意:super语句一定定义在子类构造函数的第一行。
当然,我们在子类的构造函数第一行也可以手动指定this语句来访问本类中的其他构造函数,但至少会有一个子类访问到父类的构造函数,导致其他子类也访问到父类的构造函数。

    public class thisClass extends superClass{         static String name = "这是儿子";         thisClass(){          this(0);          System.out.println("儿子");         }         thisClass(int num){          super(num);          System.out.println("这是子类哦哦哦哦哦哦"+num);         }      protected void getName(){          System.out.println("hello this is your son "+name);      }       class superClass{          String name  = "我是父亲";                superClass(int num){              System.out.println(num);          }           void getName(){              System.out.println(name+"hello好久不见");          }      }      class Extends {          public static void main(String[] args){              thisClass tc = new thisClass();               tc.getName();          }      }  

在这段代码中,子类thisClass继承了父类superClass,父类中没有默认的空参数构造函数,这时在子类的构造函数中必须指定继承了父类的哪个构造函数,或者用this语句来指定创建了同类中哪一个构造函数,否则,编译无法通过。

final关键字

final可以修饰类,方法和变量。

被final修饰的类不可以被继承。

被final修饰的方法不可以被覆盖。

被final修饰的变量只是一个常量,只能被赋值一次。

内部类只能访问被final修饰的局部变量。

抽象类


抽象类和一般类没有太大的不同。该如何描述事物,就如何描述事物,只不过,该事物出现了一些看不懂的东西。这些不确定的部分,也是该事物的功能,需要明确出现,但是无法定义主体,通过抽象方法来表示。

抽象类比一般类多个了抽象函数,所以类中可以定义抽象方法,但不可以实例化。


抽象定义:


抽象就是从多个事物中将共性的,本质的内容抽取出来。

例如,狼和狗共性都是犬科,犬科就是抽象出来的概念。


抽象类:


Java中可以定义没有方法体的方法,该方法的具体实现由子类去完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。

抽象方法的由来


多个对象都具有相同的功能,但是功能具体内容有所不同,所以我们在抽取过程中,只抽取了功能定义,并不抽取功能主体,然后这些只有功能声明而没有功能主体的方法就称为抽象方法。

例如:狼和狗都有吼叫的方法,可是吼叫的内容是不一样的。所以抽象出来的犬科虽然有吼叫的功能,但是并不明确吼叫的细节。


抽象类的特点


抽象类和抽象方法必须用abstract关键字来修饰。在类中只要定义了一个abstract方法,这个类也要被定义为abstract。抽象类通过其子类实例化,而子类需要去覆盖掉抽象类中的抽象方法后才可以创建对象,否则该子类也是抽象类。抽象方法只有方法声明,没有方法体,定义在抽象中。格式:

修饰符  abstract  返回值类型  函数名(参数列表);



抽象类不可以被实例化,也就是不可以用new来创建对象。原因如下:

1.抽象类是具体事物抽取出来的,本身是不具体的,没有对应的实例。例如:犬科就是一个抽象的概念,真正存在的是狼和狗。

2.而且抽象类即使创建了对象,调用抽象方法也没有意义。

注意:

1.抽象类中也可以没有抽象方法,这样子仅仅是为了不让该类创建对象。

2.abstract关键字不可以与final关键字共存,被final修饰的类不能有子类,而被abstract修饰的类一定是一个父类。所以它们是无法共存的。

3.abstract关键字不可以与private关键字共存,抽象类中的私有的抽象方法,不被子类所知,就无法被复写。 而抽象方法是必须被复写的。

4.abstract关键字不可以与static关键字共存,如果用static修饰抽象方法,那么连对象都省了,直接类名调用就可以了。可是抽象方法没有运行意义。

5.抽象类也需要构造函数,虽然本身无法被实例化,但它要为它的子类的提供实例的初始化。

抽象类示例:

abstract class Student{abstract void study();        //抽象类中也可以有不抽象的方法:        void sleep(){System.out.println("躺着");}}class ChongCiStudent extends Student{ void study() {System.out.println("chongci study");}}class BaseStudent extends Student{void study(){System.out.println("base study");}}class AdvStudent extends Student{ void study(){System.out.println("advanced study"); }}class AbstractDemo {public static void main(String[] args) {new BaseStudent().study(); }}

在父类Student中我们是不需要描述study方法的,具体是个怎样的study就交由子类去重写。

接口


接口定义

我们可以把接口理解为一个特殊的抽象类,当抽象类中的方法都是抽象的时候,那么该类可以通过接口的形式来表示。

class用于定义类
interface 用于定义接口。

在定义接口时,接口中的成员都有固定修饰符,应遵循以下格式:

常量:public static final

方法:public abstract 

接口的特点:

1.接口是对外暴露的规则,是程序的功能扩展。

2.类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。

3.接口与接口之间可以有继承关系,而且一个接口可以继承多个接口。

注意

1.接口是不可以创建对象的,因为有抽象的方法,它需要被子类实现implement,子类对接口中的抽象方法全都覆盖后,子类才可以实例化。否则子类是一个抽象类。

2.一个类可以实现多个接口,也是对多继承不支持的转换形式,Java支持多实现。

interface Inter{public static final int NUM = 3;public abstract void show();}interface InterA{public abstract void show();}class Demo{public void function(){}}class Test extends Demo implements Inter,InterA      //Java支持单继承多实现。{public void show(){}        //在实现接口时要复写接口中所有的抽象方法。}interface A{void methodA();}interface B {void methodB();}interface C extends B,A   //接口可以多继承{void methodC();}class D implements C{public void methodA(){}    //要复写三个方法public void methodB(){}public void methodC(){}}class InterfaceDemo {public static void main(String[] args) {Test t = new Test();System.out.println(t.NUM);System.out.println(Test.NUM);System.out.println(Inter.NUM);}}

多态


多态定义:

可以理解为事物存在的多种体现形态。例如:动物中的猫,狗,多态的出现大大的提高程序的扩展性和后期可维护性。

猫这个对象对应的类型是猫类型。

猫 x = new 猫();

同时猫又是动物中个的一种,也可以把猫称为动物:

动物 y = new 猫();

动物是猫和狗具体事物中抽取出来的父类型,父类的引用指向了自己的子类对象。另外父类的引用也可以接收自己的子类对象。

多态的前提

1.必须是类与类之间有关系。要么继承,要么实现。
2.通常还有一个前提:存在覆盖。


4,多态的弊端:
    提高了扩展性,但是只能使用父类的引用访问父类中的成员。

多态的特点

在多态中成员函数的特点:
在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有编译失败。
在运行时期:参阅对象所属的类中是否有调用的方法。
简单总结就是:成员函数在多态调用时,编译看左边,运行看右边。


在多态中,成员变量的特点:
无论编译和运行,都参考左边(引用型变量所属的类)。


在多态中,静态成员函数的特点:
无论编译和运行,都参考做左边。

注意

1.当我们使用父类的引用指向自己的子类对象时,它隐式的将类型提升了,我们称之为向上转型。比如:

Animal a = new Cat();   //类型提升,向上转型。

这时候如果要调用子类的特有方法时,要强制将父类的引用转成子类类型,我们称之为向下转型。比如:

<pre name="code" class="java">Animal a = new Cat();
Cat c = (Cat)a;

一般在做这个操作前,会先判断这个父类引用是否指向了这一个子类对象,用instanceOf,如果是,才可以向下转型,否则编译失败。例如:

<pre name="code" class="java"><pre name="code" class="java">Animal a = new Cat();

if(a instanceof Cat){ 
   Cat c = (Cat)a;
  System.out.println("成功的向下转型");}
2.多态自始至终都是子类对象在做着变化
class animal {void eat() {}}class dog extends animal {void eat() {System.out.println("啃骨头");}void kanMen() {System.out.println("看门ing");}}class tiger extends animal {void eat() {System.out.println("吃动物");}void catchAnimal() {System.out.println("抓动物来吃");}}class cat extends animal {void eat() {System.out.println("吃鱼");}void catchMouse() {System.out.println("抓老鼠");}}public class duotai {public static void main(String[] args) {animal xiaomao = new cat();xiaomao.eat();function(xiaomao);animal xiaogou = new dog();xiaogou.eat();function(xiaogou);toEat(xiaomao);toEat(xiaogou);}public static void toEat(animal a ){a.eat();}public static void function(animal a) {if (a instanceof cat) {cat c = (cat) a;c.catchMouse();} else if (a instanceof dog) {dog c = (dog) a;c.kanMen();} else if (a instanceof tiger) {tiger c = (tiger) a;c.catchAnimal();}}}

内部类


内部类的定义

将一个类定义在另一个类里面,对里面那个类就称为内部类,也称内置类或者嵌套类。

当描述事物时,事物的内部还有事物,该事物用内部类来描述,因为内部事务在使用外部事物的内容。

内部类的访问规则:

1,内部类可以直接访问外部类中的成员,包括私有。
    之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用,格式 外部类名.this
2,外部类要访问内部类,必须建立内部类对象。

内部类的特点

(注意,这里的outer和Iner分别指的是外部类和内部类)

1、内部类可以直接访问外部类的成员,包括私有。

2、外部类要访问内部类的话,要先创建内部类的对象。

3、内部类可以被private修饰。

4、在其他类中想访问内部类,要用此格式:

Outer.Iner.xxx()

或者
new Outer().new Iner();

4、其他类访问内部类的非静态成员:

new Outer.Iner().run();

5、其他类访问内部类的静态成员:

Outer.Iner.run();

(也即不用创建对象即可访问静态方法。)

6、匿名内部类一定是一个匿名子类对象。格式:

new 父类或者接口(){定义子类的内容}。

注意

1.当内部类中定义了静态成员,该内部类必须是static的。

2.注意:用外部类的静态方法访问内部类时,该内部类也必须是静态的。

3.当内部类定义在成员位置上时,它可以被private static成员修饰符修饰,但被static修饰的内部类只能访问外部类中的静态成员。

4.当内部类定义在局部位置上时,它不可以被成员修饰符修饰,可以直接访问外部类中的成员,因为还持有外部类中的引用。但是不可以访问它所在的局部中的变量。只能访问被final修饰的局部变量。

public class OuterAndIner {public static void main(String[] args) {Outer.Iner in = new Outer().new Iner();in.method();}}class Outer {private int x = 3;class Iner {int x = 5;void method() {System.out.println("x=" + x);}}void method() {Iner in = new Iner();in.method();}}

匿名内部类

匿名内部类是内部类的一种简化写法。它其实是一个匿名的子类对象,只是这个对象有点胖,你可以理解为带内容的对象,或者是外部类或者接口的一个带内容的子类匿名对象。(好像更复杂了。。)

定义匿名内部类的前提:

内部类必须是继承一个类或者实现接口。

匿名内部类的格式

new 父类或者接口(){定义子类的内容}

注意

匿名内部类中定义的方法最好不要超过3个。

class neibu {void show() {System.out.println("匿名内部类 run");}}class user {public static void main(String[] args) {new neibu(){void heihei(){super.show();System.out.println("哈哈");}}.heihei();           //匿名内部类的使用方法1function().show();}static neibu function(){      //匿名内部类的使用方法2return new neibu(){void show(){super.show();System.out.println("不仅用了父类方法而且还复写了哦");}};}}




0 0
原创粉丝点击