黑马程序员——Java要点笔记——面向对象(四)

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

day09 01-面向对象-继承-子父类中的构造函数-子类实例化过程

1、代码示例

2、在子类构造对象时,发现,访问子类构造函数时,父类也运行了。为什么呢?

       原因是:在子类的构造函数中第一行有一个默认的隐式语句:super();(调用父类中的构造函数)

3、代码示例:

4、构造函数能有覆盖吗?不能!类名不一样,构造函数名就不一样。这就不符合函数覆盖的定义。覆盖要求,方法的声明方式完全一致。

5、为什么this和super不能同时出现在一个构造函数里面?

       答:简单解释:其实构造函数也是一个链条一样的东西,会调用最上面的那层钩子,然后依次不重复的往下。每个构造函数虽然原理是先调用super,但是当构造函数形成一个链条的时候,不会每次去调用,就和js的原型差不多。 

       之所以你的super放在哪里不对的原因是,编译器认为,你既然调用了this(3)这个构造器,那么这个构造器肯定会找到你的super并且调用,那么你在这里调用super是想迫使它调用两次父类的构造器,根据我上面的那个原理,是不成立的,所以编译不通过。 

day09 02-面向对象-继承-子父类中的构造函数-子类实例化过程-细节

1、子类构造函数为什么要调用父类中的构造方法?

       你拿人家的东西,是不是要知道人家是怎么对这个东西初始化的?

       那是因为子类继承父类,获取到了父类中的内容,所以在使用父类内容之前,要先看父类是如何对自己的内容进行初始化的。

       所以子类在构造对象时,必须访问父类中的构造函数。

       为什么完成这个必须的动作,就在子类的构造函数中加入了super();语句。

       如果父类中没有定义空参构造函数,那么子类的构造函数必须用super明确要调用父类中的哪一个构造函数。

2、注意suoer语句必须要定义在子类构造函数的第一行,因为父类的初始化动作要先完成。此外,this()和super()不能同时出现在一个构造函数里面。

3、同时,子类构造哦啊函数中,如果使用this调用了本类构造函数时,那么super();就没有了。因为suoer和this都只能定义在第一行,所以只能有一个。但是可以保证的是,子类中肯定会有其他的构造函数访问父类的构造函数。

4、代码示例,观察以下代码

class Demo{
    Demo(){
       super();
       return;
    }
}

       该构造函数有super();但是类Demo的声明上却没有extends。上帝类——Object。Object类——所有类都是这个Object类的子类,或间接子类。所以class  Demo隐含了一句extends  Object,你不写他也有。但你写了其他的extends,这句就没了,但可以保证的是,会从其他父类间接成为Object的子类,多重继承。

day09 03-面向对象-继承-子父类中的构造函数-子类实例化过程-图解

1、代码示例

public class TestDemo14 {
    public static void main(String[] args) {
       Zi z=new Zi();
       z.show();
    }
}
class Fu
{
    Fu(){
       show();
    }
    void show(){
       System.out.println("fushow");
    }
}
class Zi extends Fu{
    int num=8;
    Zi(){
   
    }
    void show(){
       System.out.println("zishow..."+num);
    }
}

运行结果:zi show...0

          zi show...8



       子类构造方法隐含super();会去调用父类的构造方法Fu()。




一通弄完后,再num=8。父类的内容先忙完,再弄自己的。

day09 04-面向对象-final关键字

 

1、为什么要有final?

class Fu{
    void method(){
       //调用了底层系统的资源
    }
}
class Zi extends Fu{
    void method(){
       System.out.println("haha");
    }
}

继承的弊端,打破了封装性。你把不该覆盖的东西给覆盖了。

2、final关键字

       ①final是一个修饰符,可以修饰类、方法、变量

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

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

       ④final修饰的变量,是一个常量,只能赋值一次。即后面不能对其再次赋值,其操作方式与常量相同。

3、final修饰变量举例

       final double PI=3.14;

       定义常量时,所有字母都大写,若单词数不唯一,单词之间用下划线连接。而普通变量第一个单词首字母小写,后面单词首字母均大写。

1、  你开发代码时,一旦涉及到了固定不变的值,你一定要起名字。

2、  代码示例

class Zi extends Fu{
    final double MY_PI=3.14;
    final int x;
}

       一编译final int x;这一句挂了。怎么回事?x直接定义在类中是成员变量,应该有默认值呀。但是这个对常量来说就不行,你定义常量,必须要亲自给他一个值,哪怕这个值跟默认值一样。

3、  static final double MY_PI=3.14;既然是常量,一般都不会是特有变量。一般都是static,被所有本类对象所共享。想想你定义一个常量,后面还都不能改变它的值,那你在堆内存里浪费什么空间啊。

4、  为什么要用final修饰变量?

       答:其实在程序中,如果一个数值是固定的,那么直接使用这个数据就可以了,但是这样阅读性很差,所以就给这个数值起一个名字。大家一眼就知道这个数值是做什么的。由于这个变量的值不能变化,所以加上final固定。

       写法规范:常量所有字母都大写,多个单词,中间用_连接。

day09 05-面向对象-抽象类-概述

1、抽象:笼统,模糊,看不懂,不具体。这个类的子类都有,但是具体说不清,道不明。

2、代码示例:

abstract class 犬科{
    abstract void 吼叫();
}
class 狼 extends 犬科{
    void 吼叫(){
       System.out.println("嗷嗷");
    }
}
class 狗 extends 犬科{
    void 吼叫(){
       System.out.println("汪汪");
    }
}

day09 06-面向对象-抽象类-特点

1、抽象类的特点:

       ①方法只有声明,没有实现时,该方法就是抽象方法,需要被abstract修饰。抽象方法必须定义在抽象类中,该类必须也被abstract修饰。

       ②抽象类不可以被实例化,为什么?

因为对象调用抽象方法没意义。一个抽象方法留存都不行。

       ③抽象类必须由其子类覆盖了所有的抽象方法之后,该子类才可以实例化。否则,这个子类还是抽象类。(只要有一个抽象方法未被覆盖,该子类就是抽象的。子类继承了父类中的全部内容,是全部继承,不是选择继承)

abstract class Demo{
    abstract void show();
}
class DemoA extends Demo{
   
}

    编译报错:DemoA不是抽象的,并且未覆盖A中的抽象方法show()

若子类中无方法来覆盖父类中的抽象方法——show方法,你继承过来的show方法还是抽象方法。

2、只要一个类中有一个抽象方法(继承过来的也算)。该类就是抽象的,该类就必须用abstract来修饰。

3、抽象类无法被实例化

abstract class A{
    abstract void show();
}
class C{
    public static void main(String args[]){
       A a=new A();
    }
}

报错:A是抽象的,无法实例化。

4、只要这个类前面加了abstract,那么他就无法被实例化。即便他里面没有抽象方法。抽象类必须由其子类覆盖了其所有的抽象方法之后,该子类才可以实例化。

代码示例:

abstract class D{
    void show(){
       System.out.println("show");
    }
}
class C{
    public static void main(String args[]){
       D d=new D();
    }
}

编译报错,D是抽象的,无法实例化。

5、abstract:修饰符,(可以修饰类和方法)。被他修饰的方法是抽象方法,被他修饰的类是抽象类。

day09 07-面向对象-抽象类-细节

1、抽象类中有构造函数吗?

答:有!用于给子类对象初始化。

2、抽象类中可以不定义抽象方法吗?

答:可以的。但是很少见。而这种类前面加abstract的目的就是不让该类创建对象。因为类名前面只要加了abstract,那么该类就不能被实例化。举例,AWT中的适配器就是这种类。通常这种类里的方法有方法体,但是却没有内容。

3、抽象关键字不可以和哪些关键字共存?

答:先记住一个原则——抽象父类中的所有抽象方法必须被子类覆盖,该子类才可以实例化。你要保证这个抽象方法能被子类所覆盖掉。该抽象方法才有使用的意义。

①private不行

抽象方法是要被覆盖的(抽象方法不能被覆盖,那它就没意义了),你加了private,他就不能被覆盖了。

private abstract void show();

报错:非法的修饰符组合  abstract和private

②static不行

       如果这个成员变成静态的了,还需要对象吗?还需要子类对象吗?你直接类名就调用了。但你直接拿类名调用抽象方法是没有意义的。

static abstract void show();

报错:非法的修饰符组合  abstract和static

③final不行

       抽象方法要被覆盖,但你又final它,不让他被覆盖,矛盾!

抽象类要被继承,但你又final它,不让他被继承,矛盾!

(final可以修饰类、方法、变量)

4、综上所述:要保证抽象方法能被子类对象的方法所覆盖,则private和finla都不行。而static,你覆不覆盖对我已经没有意义了,所以static也不行。所以,private、final、static都不能与abstract共存。

5、抽象类和一般类的异同点

       相同点:抽象类和一般类都是用来描述事物的,都在内部定义了成员。

       不同点:①一般类有足够的信息描述事物。

               抽象类描述事物的信息有可能不足。

                     ②一般类中不能定义抽象方法,只能定义非抽象方法。

              抽象类中可以定义抽象方法,同时也可以定义非抽象方法。

                     ③一般类可以被实例化。

                 抽象类不可以被实例化,只能由其子类覆盖了抽象父类中的所有抽象方法后,将子类实例化。

6、抽象类一定是个父类吗?

       答:抽象类一定是父类。因为需要子类覆盖其中的所有抽象方法之后,对子类实例化。

day09 08-面向对象-抽象类-练习

1、雇员示例:

需求:公司中程序员有姓名、工号、薪水、工作内容。

       项目经理出了姓名、工号、薪水,工作内容之外,还有奖金

       请对给出的需求进行数据建模。

分析:在这个问题领域中,先找出涉及的对象,可以通过名词提炼法。

       程序员:属性(姓名、工号、薪水)  行为(工作)

       经理:属性(姓名、工号、薪水、奖金)  行为(工作)

       程序员和经理不存在直接继承关系。但是程序员和经理却具有共性内容。可以进行抽取。因为他们都是公司的雇员。可以将程序员和经理进行抽取,建立关系。

2、代码示例

//描述雇员
abstract class Employee
{
    private String name;
    private int id;
    private double pay;
    Employee(String name,int id,double pay){
       this.name=name;
       this.id=id;
       this.pay=pay;
    }
    public abstract void work();
}
//描述程序员
class Programmer extends Employee{
    Programmer(String name,int id,double pay){
       super(name,id,pay);//注意!这里可不要写Employee(name,id,pay),那就废了!
    }
    public void work(){
       System.out.println("code");
    }
}
//描述项目经理
class Manager extends Employee{
    private int bonus;
    Manager(String name,int id,double pay,int bonus){
       super(name,id,pay);
       this.bonus=bonus;
    }
    public void work(){
       System.out.println("manage");
    }
}

3、子类使用父类的构造,使用super(……)。

day09 09-面向对象-接口-定义interface

1、当一个抽象类中的方法全部是抽象的时候,这时可以将该抽象类用另一种形式来定义和表示。就是接口——interface

2、定义接口,使用的关键字不是class,而是interface。

虽然接口不是用class来定义的,但是编译完之后,他还是*.class文件。因为java都是用*.class文件来封装数据的。

3、对于接口当中常见的成员:而且这些成员都有固定的修饰符。

①成员变量:public static final——公共静态常量

②成员函数:public abstract——公共抽象函数

4、代码示例

5、注意,由此可以得出,接口中的成员都是公共的权限。那么,根据以前方法覆盖的注意。子类去实现接口时,覆盖接口中抽象方法的时候,子类中的这些方法前面也全部要加上public。不然,会出现降低权限的报错。

6、接口中的全局常量,因为是static,所以用的时候可以直接写Demo.NUM;

7、为什么接口中都是公共静态常量?

答:接口就是提供一种统一的“协议”,而接口中的属性也属于“协议”中的成员。他们是公共的、静态的、最终的常量。相当于全局常量。

       抽象类是不完全的类,相当于是接口和具体类的一个中间层。既满足接口的抽象,也满足具体的实现。

       如果接口可以定义变量,但是接口中的方法又都是抽象的,在接口中无法通过行为来修改属性。有的人会说了,没有关系,可以通过实现接口的对象的行为来修改接口中的属性。有的人会说了,没有关系,可以通过实现接口的对象的行为来修改接口中的属性。这当然没有问题,但是考虑这样的情况:

如果接口A中有一个public访问权限的静态变量a。按照java的语意,我们可以不通过实现接口的对象来访问变量a。通过A..a=xxx;就可以改变接口中的变量a的值了,也就是说一个地方改变了a,所有这些对象中a的值也跟着变了。这和抽象类有什么区别呢?怎么体现接口更高的抽象级别呢?怎么体现接口提供的统一的协议呢?(既然是统一的协议,那大家遵守的就是同一个协议,定死的协议,里面什么东西你都不能乱改)那还要接口这种抽象做什么呢?

所以,接口中不能出现变量。如果有变量,就和接口提供的统一的协议这种思想是相抵触的。所以接口中的属性必然是常量,只能读不能改。这样才能为实现接口的对象提供一个统一的属性。

通俗的讲,你认为要变化的东西,就放在你自己的实现中,不能放在我接口(统一的协议)中去。接口只是对一类事物的属性和行为更高层次的抽象。

day09 10-面向对象-接口-实现implements

1、类与类之间是继承关系,类与接口之间是实现关系。接口不可以实例化,只能由实现了接口的子类并覆盖了接口中所有的抽象方法后,该子类才可以实例化。否则,这个子类就是一个抽象类。

2、所以final与interface也是一个错误的组合。

3、代码示例:

interface Demo{
    public static final int NUM=4;
    public abstract void show1();
    public abstract void show2();
}
class DemoImpl implements Demo{
    public void show1(){    
    }
    public void show2(){    
    }
}
class InterfaceDemo{
    public static void main(String args[]){
       DemoImpl d=new DemoImpl();
       System.out.println(d.NUM);
       System.out.println(DemoImpl.NUM);
       System.out.println(Demo.NUM);
       //以上3句,全对,类名直接调用公共静态常量。
    }
}

但是,你写d.NUM=3;不行,直接完蛋。因为NUM是常量,不允许被改变。因为NUM被final修饰,成为一个常量,不能被改变。

day09 11-面向对象-接口-多实现

1、在java中不直接支持多继承,因为可能会出现调用的不确定性。所以java将多继承机制进行了改良,在java中变成了多实现。

2、一个类可以实现多个接口。

3、代码示例,继续来看方法调用的不确定性

 

day09 12-面向对象-接口-细节

1、一个类在继承另一个类的同时,还可以实现多个接口。

class Q{
    public void method(){}
}
class Test extends Q implements A,Z{
   
}

先继承,找一个组织,在提供额外功能,通过实现接口的形式。

2、接口的出现,避免了单继承的局限性。接口在扩展性上的强大是不言而喻的。

3、类与类之间是继承。类与接口之间是实现。接口与接口之间呢?

可以肯定的是,接口与接口之间肯定不是实现。因为他们俩都是抽象的,谁实现谁啊?!

接口与接口之间是继承关系,并且可以多继承!

4、代码示例

interface A{
    public abstract void show1();
}
interface B{
    public abstract void show2();
}
interface C extends A,B{
    public abstract void show3();
}

多继承出问题就出在方法体上,接口中方法全部抽象,无方法体,所以接口可以多继承。

5、代码示例:

interface A{
    public abstract void show1();
    public abstract void show2();
}
class B implements A{
    public void show1(){
       System.out.println("show1");
    }
}

报错,类B必须覆盖接口A中的抽象方法show2()。因此,要用接口,那么该接口的实现类也必须要把接口中所有的方法覆盖掉。

day09 13-面向对象-接口-特点

1、接口,举例:笔记本上的USB接口。你不接个设备,这接口就等于没用(你不把抽象的类实例掉,等于没用接口)

2、接口是对外暴露的规则。接口是程序的功能扩展。接口的出现降低耦合性。

day09 14-面向对象-接口和抽象类的区别

1、我到底是把他定义成抽象类还是接口?

2、抽象类和接口的异同点

       相同点:都是不断向上抽取而来的

       不同点:

       ①抽象类需要被继承,而且只能单继承。

         接口需要被实现,而且可以多实现。

       ②抽象类中可以定义抽象方法和非抽象方法。子类继承后,可以直接使用非抽象方法。

         接口中只能定义抽象方法,必须由子类去实现。

       ③抽象类的继承是is a关系。在定义该体系的基本共性内容。

         接口的实现是like a关系。在定义该体系的额外功能。

3、代码示例:犬按照功能分——导盲犬、搜爆犬

abstract class 犬{
    abstract void 吼叫();
}
interface 导盲{
    abstract void 导盲();
}
class 导盲犬 extends 犬 implements 导盲{
    public void 吼叫(){
      
    }
    public void 导盲(){
      
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值