黑马毕向东Java课程笔记(day07):面向对象(第三部分)继承+抽象类+模板方法设计模式+接口+final+继承补充(就业班)

it2024-07-11  58

  在这一部分中,我们将讲解有关继承的相关内容,包括继承的概述、继承的特点、super关键字、函数覆盖、子类的实例化过程、final关键字这几个部分的内容。

1、继承的概述以及特点 1.1、概述   多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承单独的那个类即可。多个类可以称为子类,单独这个类称为父类或者超类。   **子类可以直接访问父类中的非私有的属性和行为。**通过extends关键字让类与类之间产生继承关系,格式为

class SunClass extends FatherClass{}

  继承的出现提高了代码的复用性,并且让类与类之间产生了关系,提供了多态的前提。 1.2、特点   Java只支持单继承,不支持多继承,多继承容易带来安全隐患(25-day07-01-7.27分钟开始的部分说明)。也就是说,一个类只能有一个父类,不可以有多个父类。另一方面,java又支持多层继承,也就是说,多个类之间可以相互继承,形成继承体系。如下

class SubDemo extends Demo{}//ok class SubDemo extends Demol,Demo2...//error

  Java支持多层继承(继承体系)

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

  定义继承需要注意:不要仅为了获取其他类中某个功能而去继承,类与类之间要有所属(“is a")关系,也就是子类xx1应该要是父类xx2的一种,**也就是说,父类的内容,子类应该全部具备,而子类又可以有自己新的内容。**如果父类的某一个特性子类不应该具备,那么这两个类之间就不应该有继承关系。   继承的基本思想:基于某个父类的扩展,制定出一个新的子类,子类可以继承父类原先的属性与方法,也可以增加父类所不具备的属性与方法,或者重写父类的某些方法。

//不能多继承的原因 //当多个父类中定义了相同功能,当功能内容不同时,子类对象不确定要运行哪一个。 class A { void show() { System. out. println("a"); } } classB { void show() { System. out. print1n("b"); } } class C extends A,B { C c=new C(); c. show();//不知道调用的是A的show()方法还是B的show()方法 //接口不会有这种困境,因为我们实现多个接口后,必须对接口里面的方法进行复写后才能调用,因此不会有多个父类(接口)方法重名而不知道调用谁的困境。 }

  java的多继承还是存在的,优化了c++的多继承功能,用多实现的方式来提现,后面会讲到。   如何使用一个继承体系中的功能呢?   想要使用体系,先查阅体系父类的描述,因为父类中定义的是该体系中共性功能,通过了解共性功能,就可以知道该体系的基本功能,那么这个体系已经可以基本使用了。   在具体调用时,要创建最子类的对象,有两个原因 1)因为有可能父类不能创建对象; 2)创建子类对象可以使用更多的功能,包括基本的也包括特有的。   也就是,我们要先查阅父类功能,再创建子类对象来使用功能。

2、子类与父类关系确定之后,类成员的特点 2.1、子父类中变量的特点   我们先说一下super关键字,super和this的用法相同,this代表本类引用 super代表父类引用。   当子父类出现同名成员时,可以用super进行区分子类与父类;要调用父类构造函数时,可以使用super语句。   例子:

/* 1、变量 如果子类中出现非私有的同名成员变量时,子类要访问本类中的变量,用this;子类要访问父类中的同名变量,用super。super的使用和this的使用几乎一致。 this代表的是本类对象的引用。 super代表的是父类对象的引用。 */ public class ExtendsDemo { public static void main(String[] args) { Son obj = new Son(); System.out.println(obj.num);//打印2(子类),父类与子类属性相同时,用对象调用会调用子类属性 obj.show1();//打印2(子类) obj.show2();//打印1(父类) } } class Father { int num = 1; } class Son extends Father { int num = 2; void show1() { System.out.println(this.num);//this表示本类的引用 } void show2() { System.out.println(super.num);//super表示父类的引用 } }

2.2、子父类中函数的特点   当子类出现和父类一模一样的函数时,当子类对象调用该函数,会运行子类函数的内容,如同父类的函数被覆盖一样,这种情况是函数的另一个特性:重写(覆盖)   使用情景:当子类继承父类,沿袭了父类的功能,到子类中,但是子类虽具备该功能,但是功能的内容却和父类不一致,这时,没有必要定义新功能,而是使用覆盖功能,保留父类的功能定义,并重写功能内容。

重载见:(day03—第二部分):函数,注意区别重载与重写。(注意,子类可以重写父类方法,也可以重载父类方法!)   重载与覆盖(重写)的区别,看下面这个链接: 重载与覆盖(重写)的区别

  对于重载与覆盖,记住: 重载:只看同名函数的参数列表,同名参数列表不一样就是重载; 重写/覆盖:子父类方法形式要一模一样,包括返回值类型、参数列表,而子类的访问权限修饰符大于等于父类,子类抛出的异常不能大于父类,但是内容可以不同,在子父类中。子父类的构造方法不能算覆盖!

  覆盖(重写)需要注意的点: 1)父类中的私有方法不可以被覆盖; 2)在子类覆盖方法中,继续使用父类中被覆盖的方法可以通过super.函数名获取; 3)覆盖时,子类方法权限一定要大于等于父类方法权限; 4)静态只能覆盖静态。

  例子1

public class ExtendsDemo { public static void main(String[] args) { Son obj = new Son(); //当子类与父类中含有同名的方法时,调用的是子类的方法,这种情况称为“覆盖”或“重写” //我们前面提到的“重载”,重载是同一个类中可以有同名的方法,但是他们的形参列表必须不同 //覆盖(重写)指的是子类父类的方法可以同名,但是子类方法与父类方法的内容不同,可以定义子类特有的内容 obj.show();//结果是son } } class Father { void show() { System.out.println("father"); } } class Son extends Father { void show() { System.out.println("son"); } }

  例子2

public class ExtendsDemo { public static void main(String[] args) { NewTel obj = new NewTel(); obj.show();//结果是number name pic } } class Tel { void show() { System.out.println("number");//初代手机只显示号码 } } class NewTel extends Tel { void show() { //新手机要显示号码、姓名、图片 //System.out.println("number");我们这一句可以不用写,直接使用super关键字调用父类show()方法的部分 //用super关键字将父类的show()方法加进来,这样我们在重写的show()方法里面就不需要加入父类show()方法的内容,可以直接使用 super.show(); System.out.println("name"); System.out.println("pic"); } }

2.3、子父类中构造函数的特点   子父类构造函数的相关特点: 1)子类中所有的构造函数默认都会访问父类中空参数的构造函数(想调用其他得显式用super来调用),因为子类每一个构造函数的第一行都有一条默认的语句super();   例子1

public class ExtendsDemo { public static void main(String[] args) { //结果是father、son,也就是说,创建子类的对象,父类的构造方法先执行,再执行子类的构造方法 //因为子类构造方法中默认有一句super();,调用父类的构造方法 //注意,在同一类中,this()可以代表调用本类的构造方法,调用哪一个构造方法与this(参数)中参数相关 Son obj = new Son(); //这一部分的结果是father son4,因为子类每一个构造函数的第一行都有一条默认的语句super(),来调用父类空参数的构造函数 Son obj1 = new Son(4); } } class Father { Father() { System.out.println("father"); } } class Son extends Father { Son() { //super(); System.out.println("son"); } Son(int x) { System.out.println("son"+x); } }

2)为什么子类一定要访问父类中的构造函数?   因为父类中的数据子类可以直接获取,所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的,所以子类在对象初始化时,要先访问一下父类中的构造函数。如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。 3)当父类中没有空参数的构造函数时(既我们设置了自己的有参数的构造函数),子类的构造函数必须通过this(用this是访问本类的其他构造函数,不可以再用super(),但是,子类总有一个构造函数会有super来访问父类的构造函数)或者super(用super是直接访问父类的构造函数)语句指定要访问的构造函数。也就是说,子类一定要访问父类的至少一个构造函数!!!

public class ExtendsDemo { public static void main(String[] args) { //结果是father4 son //我们在子类的构造函数中用super(4)显式调用了父类中含参数的构造函数 Son obj = new Son(); } } class Father { Father(int x) { System.out.println("father"+x); } } class Son extends Father { Son() { //当父类中没有空参数的构造函数时(既我们设置了自己的有参数的构造函数,又没有创建新的空参数构造函数) //子类的构造函数必须通过super语句指定要访问的构造函数,否则会报错 super(4); System.out.println("son"); } }

  父类中定义完的内容,子类没必要重新定义,子类直接调用即可。子类调用父类的构造函数super(),子类调用父类的普通函数super.普通函数。   注意:super语句一定定义在子类构造函数的第一行。   总结:子类的实例化过程。 1)子类的所有的构造函数,默认都会访问父类中空参数的构造函数。因为子类每一个构造函数内的第一行都有一句隐式super();当父类中没有空参数的构造函数时,子类必须手动通过super语句形式来指定要访问父类中的构造函数。 2)当然,子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。但是子类中至少会有一个构造函数会访问父类中的构造函数。

3、final关键字 (注意final关键字是基于子父类继承的基础来说的)   final关键字的特点: 1)final可以修饰类,方法,变量。final修饰的类不可以被继承。final修饰的方法不可以被覆盖(但是可以被重载,因为重载是在同一个类中,不涉及继承)。final修饰的变量是一个常量,只能被赋值一次,可以修饰成员变量与局部变量。 2)类不能被继承,父类的方法不能被重写,这可以保障代码的封装性,保证一些代码不因为子类的覆盖(重写)而出现错误,或者说是保证一些重要代码不被子类重写; final修饰的变量是常量,只能赋值一次。当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字,并加上final修饰保证其不变。常量的书写规范所有字母都大写,如果由多个单词组成,单词之间通过“_”连接。

//定义一个全局常量 public static final double PI = 3.14; //public保证其权限足够大,可以被其他类调用;static使其在类一加载进来的时候就存在直到这个类消失,其他类可以通过类名直接访问这个常量;final使其为不变的常量;

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

4、抽象类(抽象只能定义类与方法) 4.1、抽象类概述   抽象定义:抽象就是从多个事物中将共性的,本质的内容抽取出来。例如:狼和狗共性都是犬科,犬科就是抽象出来的概念。   抽象类:Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。   抽象方法的由来:多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,只抽取了功能定义,并未抽取功能主体,那么只有功能声明,没有功能主体的方法称为抽象方法。例如:狼和狗都有吼叫的方法,可是吼叫内容是不一样的。所以抽象出来的犬科虽然有吼叫功能,但是并不明确吼叫的细节。

4.2、抽象类的特点   抽象类和抽象方法必须用abstract关键字来修饰。抽象方法只有方法声明,没有方法体,定义在抽象类中。格式:修饰符 abstract 返回值类型 函数名( 参数列表 )   抽象类特点: 1)抽象方法一定在抽象类中,抽象方法和抽象类都必须被abstract关键字修饰(抽象类可以有非抽象方法,但是只要有一个抽象方法其就是抽象类);

2)抽象类不可以被实例化,也就是不可以用new创建对象。 原因如下:抽象类是具体事物抽取出来的,本身是不具体的,没有对应的实例。例如:犬科是一个抽象的概念,真正存在的是狼和狗。而且抽象类即使创建了对象,调用抽象方法也没有意义。

3)抽象类中的抽象方法要被使用,必须由子类复写其所有的抽象方法后,建立子类对象调用; 如果子类只覆盖了部分的抽象方法,那么该子类还是一个抽象类,只能由子类的子类继续重写完所有的抽象方法后,创建子类的子类的对象来进行调用。 4)抽象类中可以有抽象方法,也可以有非抽象方法,抽象方法用于子类实例化; 5)如果一个类是抽象类,那么,继承它的子类,要么是抽象类,要么重写所有抽象方法。特殊:抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象。   不想让一个类创建对象的方法 (1)将该类设置为抽象类abstract; (2)将该类的构造方法私有化(参考单例设计模式)

  几个特殊的点: 1)抽象类不能被实例化,为什么还有构造函数? 只要是class定义的类里面就肯定有构造函数,抽象类中的构造函数是给子类实例化的。 2)一个类没有抽象方法,为什么定义为抽象类? 不想被继承,还不想被实例化。

  抽象关键字abstract不可以和哪些关键字共存? 1)final:如果方法被抽象,就需要被覆盖,而final是不可以被覆盖,所以冲突; 2)private:如果函数被私有了,子类无法直接访问,没办法覆盖,与abstract方法需要被覆盖冲突; 3)static:不需要对象,类名就可以调用抽象方法。而调用抽象方法没有意义。

  抽象类与一般类的区别: 1)抽象类比一般类多了抽象函数; 2)抽象类不能被实例化   具体例子:

public class AbstractDemo { public static void main(String[] args) { // Student obj1 = new Student();报错,因为抽象类无法被实例化。 } } //需要注意的是,包含抽象方法的类也必须是抽象的 abstract class Student { //void study() {};这个方法不需要定义功能主体,那么后面的{}多余 //写成抽象方法的模式,这种方法没有方法体,用";"来结束函数,后面的子类直接重写这个方法即可 //需要注意的是,一个方法定义为抽象方法后就必须被重写 //void study() {}:这种,子类可以不重写study() //abstract void study():这种,子类必须重写study(),这就是区别! abstract void study(); //我们创建这个抽象方法,如果Student类的子类不重写该方法,就会报错 abstract void speak(); //抽象类中可以有非抽象方法 void run() { System.out.println("run"); } } class BaseStudent extends Student { void study() { System.out.println("base study"); } void speak() { System.out.println("base"); } } //如果这个类不重写speak()方法,那么这个类也是抽象类,在它的前面加上abstract即可不报错,由AdvStudent的子类来重写speak()方法 abstract class AdvStudent extends Student { void study() { System.out.println("adv study"); } }

4.3、练习   练习

/* 需求: 假如我们在开发一个系统时需要对员工进行建模,员工包含3个属性:姓名、工号以及工资。 经理也是员工,除了含有员工的属性外,另为还有一个奖金属性。 请使用继承的思想设计出员工类和经理类,要求类中提供必要的方法进行属性访问。 */ public class AbstractDemo { public static void main(String[] args) { } } abstract class Employee { private String name; private String id; private double salary; //使用构造方法初始化变量 Employee(String name,String id,double salary) { this.name = name; this.id = id; this.salary = salary; } public abstract void work(); } //普通员工的类 class Common extends Employee { Common(String name,String id,double salary) { super(name,id,salary); } public void work() { System.out.println("common"); } } //经理的类 class Manager extends Employee { private double bonus; Manager(String name,String id,double salary,double bonus) { super(name,id,salary); this.bonus = bonus; } public void work() { System.out.println("manage"); } }

5、设计模式——模板方法模式   先看一段代码

/* 需求:获取一段程序运行的时间。 原理:获取程序开始和结束的时间并相减即可。 获取时间:System.currentrimeMillis(),利用System类的currentrimeMillis()方法 */ public class TemplateDemo { public static void main(String[] args) { GetTime obj = new GetTime(); obj.getTime(); } } class GetTime { public void getTime() { long start = System.currentTimeMillis(); for(int x=0; x<1000 ;x++) { System.out.print(x); } long end = System.currentTimeMillis(); System.out.println(); System.out.println("时间"+(end-start)); } }

  将代码优化

/* 需求:获取一段程序运行的时间。 原理:获取程序开始和结束的时间并相减即可。 获取时间:System.currentrimeMillis(),利用System类的currentrimeMillis()方法 */ public class TemplateDemo { public static void main(String[] args) { //我们创建子类对象 SubTime obj = new SubTime(); //子类对象直接调用父类方法getTime(),getTime()里面包含需要被测试的代码runCode()方法 //而我们在子类中重写了runCode()方法,调用的时候调用的是重写的runCode()方法,这样便可以自由定义需要测试的代码! obj.getTime(); } } abstract class GetTime { //对于getTime()方法,如果它能被子类复写,那么GetTime类就没有意义,用final使其不能被复写 public final void getTime() { long start = System.currentTimeMillis(); //如果我们要测试的代码是变化的,那么我们想到把这段代码单独拿出来,创建一个方法 runCode(); long end = System.currentTimeMillis(); System.out.println(); System.out.println("时间"+(end-start)); } //这段运行代码我们目前不知道,可以设置为抽象方法,由子类去重写 public abstract void runCode(); } //设置一个子类来继承父类,我们可以在子类里面重写我们需要测试时间的代码 class SubTime extends GetTime { public void runCode() { for(int x=0; x<1000 ;x++) { System.out.print(x); } } }

   上面这种解决问题的方式,称之为模版方法设计模式。   什么是模版方法呢?在定义功能时,功能的一部分是确定的,但是有一部分是不确定,而确定的部分在使用不确定的部分,那么这时就将不确定的部分暴露出去(比如将不确定的部分封装为抽象方法由子类去具体实现)。由该类的子类去完成。我们只需要在主函数中调用确定的部分,确定的部分会自动使用子类中重写的不确定部分,这样便可以完成整体。   需要注意,模板方法只是一种思想,不用死记硬背代码,而是记住这种思想,灵活运用。

5、接口 5.1、概述   定义:接口是抽象方法和常量值的集合(接口就是多个类的公共规范。)。从本质上讲,接口是一种特殊的抽象类,这种抽象类只包含常量和方法的定义,而没有变量和方法的实现。

格式:interface 接口名 {}(注意定义的形式,一般不需要加其他修饰*)

接口的出现将”多继承“通过另一种形式体现出来,即”多实现“。   备注:换成了关键字interface之后,编译生成的字节码文件仍然是:.java --> .class。   接口实现格式:class 类名 implements 接口名 {}   例子1

public class InterfaceDemo { public static void main(String[] args) { Test t = new Test(); System.out.println(t.NUM); //直接使用类名调用也是可以的,因为Test类实现了接口Inner,包含了静态的NUM System.out.println(Test.NUM); //这种写法也是可以的,我们编译后会出现Test.class、InterfaceDemo.class、Inner.class三个文件 //也就是说,Inner也是一个类,那么他的名字也可以直接调用其中的静态常量 System.out.println(Inner.NUM); } } interface Inner { public static final int NUM = 3; public abstract void show(); } class Test implements Inner { public void show(){ } }

5.2、接口特性   接口的特点: 1)接口不能被实例化; 2)一个类如果实现了接口,要么是抽象类,要么实现接口中的所有方法。

  接口成员的特点:接口中的成员(抽象类与常量)修饰符是固定的! 1)成员常量:public static final,接口里定义的变量是全局常量,而且修饰符只能是这三个关键字,都可以省略,常量名要大写。 2)成员方法:public abstract,接口里定义的方法都是抽象的,两个修饰符关键字可省略。 推荐:虽然系统会默认给出,但是永远手动给出修饰符。

  继承与实现的区别: 1)类与类之间称为继承关系:因为该类无论是抽象的还是非抽象的,它的内部都可以定义非抽象方法,这个方法可以直接被子类使用,子类继承即可。只能单继承,可以多层继承。((class) extends (class) ); 2)类与接口之间是实现关系:因为接口中的方法都是抽象的,必须由子类实现才可以实例化。可以单实现,也可以多实现;还可以在继承一个类的同时实现多个接口。((class) extends (class) implements (interface1,interface2…)); 3)接口与接口之间是继承关系:一个接口可以继承另一个接口,并添加新的属性和抽象方法,并且接口可以多继承。((interface) extends (interface1,interface2…))   支持多实现而不支持多继承的原因:多继承父类的方法有方法体,不能重复,而多实现接口的方法没有方法体,就算是实现的2个接口的抽象方法同名,但是他们没有方法体,我们重写的时候就不需要区分!!!

  抽象类和接口的区别: 1)成员变量:抽象类能有变量也可以有常量,接口只能有常量; 2)成员方法:抽象类可以有非抽象的方法,也可以有抽象的方法,接口只能有抽象的方法 构造方法; 3)抽象类有构造方法,接口没有构造方法。

  接口的思想特点: 1)接口是对外暴露的规则; 2)接口是程序的功能扩展; 3)接口的出现降低耦合性(实现了模块化开发,定义好规则,每个人实现自己的模块,大大提高了开发效率); 4)接口可以用来多实现; 5)多个无关的类可以实现同一个接口; 6)一个类可以实现多个相互直接没有关系的接口; 7)与继承关系类似,接口与实现类之间存在多态性。

  例子1

public class InterfaceDemo { public static void main(String[] args) { Test t = new Test(); } } interface Inner { public static final int NUM = 3; public abstract void show();//同名方法 } interface InnerA { public abstract void show();//同名方法 public abstract void method(); } class Demo { public void function() {} } //java指出接口的多实现! //Inner与InnerA都有相同的show()方法,但是这并不影响Test多实现这2个接口 //可以在继承的同时多实现,这样可以扩展类的功能 class Test extends Demo implements Inner,InnerA { public void show(){} public void method(){} } interface A{} interface B{} interface C extends A,B{}//接口之间存在多继承

  例子2

//接口用于功能扩展的例子 public class InterfaceDemo { public static void main(String[] args) { } } abstract class Student { abstract void study(); void sleep() { System.out.println("sleep"); } } //对于不是共性而是少数类特有的方法或者属性,将其设置为接口,供类实现 interface Smoking { public abstract void smoke(); } //如果张三想抽烟,那么实现Smoking接口即可 class ZhangSan extends Student implements Smoking { void study() {} public void smoke() {} } //如果李四不想抽烟,不实现接口,而且接口也可以供其他各种类实现 class LiSi extends Student { void study() {} }

6、继承补充   继承主要解决的问题是:共性抽取。

  例子1:子父类重名的成员变量的访问方式(局部变量直接写,本类成员变量:this.成员变量,父类成员变量:super.父类成员变量)

package cn.itcast.day09.demo02; /* 在父子类的继承关系当中,如果成员变量重名,则创建子类对象时,访问有两种方式: 直接通过子类对象访问成员变量: 等号左边是谁,就优先用谁,没有则向上找。 间接通过成员方法访问成员变量: 该方法属于谁,就优先用谁,没有则向上找。 */ public class Demo01ExtendsField { public static void main(String[] args) { Fu fu = new Fu(); // 创建父类对象 System.out.println(fu.numFu); // 只能使用父类的东西,没有任何子类内容 System.out.println("==========="); Zi zi = new Zi(); System.out.println(zi.numFu); // 10 System.out.println(zi.numZi); // 20 System.out.println("==========="); // 等号左边是谁,就优先用谁 //Zi zi = new Zi();这个式子等号左边是子类,那么调用的是子类的num //对于Fu fu = new Zi();这种多态的情况,等号左边是父类,则会调用父类的num System.out.println(zi.num); // 优先子类,200,找不到才会去调用父类 // System.out.println(zi.abc); // 到处都没有,编译报错! System.out.println("==========="); // 这个方法是子类的,优先用子类的,没有再向上找 zi.methodZi(); // 200 // 这个方法是在父类当中定义的, zi.methodFu(); // 100 } } -------------------------- package cn.itcast.day09.demo02; public class Fu { int numFu = 10; int num = 100; public void methodFu() { // 使用的是本类当中的,不会向下找子类的 System.out.println(num); } } ------------------------------- package cn.itcast.day09.demo02; public class Zi extends Fu { int numZi = 20; int num = 200; public void methodZi() { // 因为本类当中有num,所以这里用的是本类的num System.out.println(num); } }

  例子2:子父类重名的成员方法的访问方式

package cn.itcast.day09.demo04; /* 在父子类的继承关系当中,创建子类对象,访问成员方法的规则: 创建的对象是谁,就优先用谁,如果没有则向上找。 Fu fu = new Zi();多态情况下,由于创建的依然是子类的对象,运行依然会是子类的方法 注意事项: 无论是成员方法还是成员变量,如果没有都是向上找父类,绝对不会向下找子类的。 重写(Override) 概念:在继承关系当中,方法的名称一样,参数列表也一样。 重写(Override):方法的名称一样,返回值与参数列表【也一样】。覆盖、覆写。 重载(Overload):方法的名称一样,参数列表【不一样】。 方法的覆盖重写特点:创建的是子类对象,则优先用子类方法。 */ public class Demo01ExtendsMethod { public static void main(String[] args) { Zi zi = new Zi(); zi.methodFu(); zi.methodZi(); // 创建的是new了子类对象,所以优先用子类方法 zi.method(); } } -------------------------------- package cn.itcast.day09.demo04; public class Fu { public void methodFu() { System.out.println("父类方法执行!"); } public void method() { System.out.println("父类重名方法执行!"); } } -------------------------------- package cn.itcast.day09.demo04; public class Zi extends Fu { public void methodZi() { System.out.println("子类方法执行!"); } public void method() { System.out.println("子类重名方法执行!"); } }

  一种判断是不是覆盖(重写)的方法——使用override注解

package cn.itcast.day09.demo05; /* 方法覆盖重写的注意事项: 1. 必须保证父子类之间方法的名称相同,参数列表也相同。 @Override:写在方法前面,用来检测是不是有效的正确覆盖重写。 这个注解就算不写,只要满足要求,也是正确的方法覆盖重写。 2. 子类方法的返回值必须【小于等于】父类方法的返回值范围。 子类是父类的一种,也是父类的扩展,如果返回值类型大于父类,不合理 小扩展提示:java.lang.Object类是所有类的公共最高父类(祖宗类),java.lang.String就是Object的子类。 3. 子类方法的权限必须【大于等于】父类方法的权限修饰符。 小扩展提示:public > protected > (default) > private 备注:(default)不是关键字default,而是什么都不写,留空。 4. 子类方法的异常必须小于等于父类 */ public class Demo01Override { } -------------------------------- package cn.itcast.day09.demo05; public class Fu { public String method() { return null; } } -------------------------------- package cn.itcast.day09.demo05; public class Zi extends Fu { //如果是覆盖,那么override下面不会标红 @Override public String method() { return null; } }

  继承中构造方法的访问特点

package cn.itcast.day09.demo07; /* 继承关系中,父子类构造方法的访问特点: 1. 子类构造方法当中有一个默认隐含的“super()”调用,所以一定是先调用的父类构造,后执行的子类构造。 2. 子类构造可以通过super关键字来调用父类重载构造。 3. super的父类构造调用,必须是子类构造方法的第一个语句。不能一个子类构造调用多次super构造。 总结: 子类必须调用父类构造方法,不写则赠送super();写了则用写的指定的super调用,super只能有一个,还必须是第一个。 */ public class Demo01Constructor { public static void main(String[] args) { Zi zi = new Zi(); } }

  super关键字的使用

/* super关键字的用法有三种: 1. 在子类的成员方法中,访问父类的成员变量。 2. 在子类的成员方法中,访问父类的成员方法。 3. 在子类的构造方法中,访问父类的构造方法。 */

  this关键字的使用

package cn.itcast.day09.demo09; /* super关键字用来访问父类内容,而this关键字用来访问本类内容。用法也有三种: 1. 在本类的成员方法中,访问本类的成员变量。 2. 在本类的成员方法中,访问本类的另一个成员方法。 3. 在本类的构造方法中,访问本类的另一个构造方法。 在第三种用法当中要注意: A. this(...)调用也必须是构造方法的第一个语句,唯一一个。 B. super和this两种构造调用,不能同时使用。 */ public class Zi extends Fu { int num = 20; public Zi() { // super(); // 这一行不再赠送 this(123); // 本类的无参构造,调用本类的有参构造 // this(1, 2); // 错误写法! } public Zi(int n) { this(1, 2); } public Zi(int n, int m) { super();//多个子类构造方法必须有一个使用 super调用父类构造方法 } public void showNum() { int num = 10; System.out.println(num); // 局部变量 System.out.println(this.num); // 本类中的成员变量 System.out.println(super.num); // 父类中的成员变量 } public void methodA() { System.out.println("AAA"); } public void methodB() { this.methodA(); System.out.println("BBB"); } }

  super和this关键字在java中的内存图解——见就业班-day09-12图解

  抽象方法所定义的类必须是抽象类!!!   抽象类中,可以有构造方法,是供子类创建对象时,始化父类成员使用的。   抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

  就业班发红包案例(有用)

package cn.itcast.day09.demo14; import java.util.ArrayList; public class MainRedPacket { public static void main(String[] args) { Manager manager = new Manager("群主", 100); Member one = new Member("成员A", 0); Member two = new Member("成员B", 0); Member three = new Member("成员C", 0); manager.show(); // 100 one.show(); // 0 two.show(); // 0 three.show(); // 0 System.out.println("==============="); // 群主总共发20块钱,分成3个红包 ArrayList<Integer> redList = manager.send(20, 3); // 三个普通成员收红包 one.receive(redList); two.receive(redList); three.receive(redList); manager.show(); // 100-20=80 // 6、6、8,随机分给三个人 one.show(); two.show(); three.show(); } } ------------------ package cn.itcast.day09.demo14; public class User { private String name; // 姓名 private int money; // 余额,也就是当前用户拥有的钱数 public User() { } public User(String name, int money) { this.name = name; this.money = money; } // 展示一下当前用户有多少钱 public void show() { System.out.println("我叫:" + name + ",我有多少钱:" + money); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getMoney() { return money; } public void setMoney(int money) { this.money = money; } } ------------------ package cn.itcast.day09.demo14; import java.util.ArrayList; // 群主的类 public class Manager extends User { public Manager() { } public Manager(String name, int money) { super(name, money); } public ArrayList<Integer> send(int totalMoney, int count) { // 首先需要一个集合,用来存储若干个红包的金额 ArrayList<Integer> redList = new ArrayList<>(); // 首先看一下群主自己有多少钱 int leftMoney = super.getMoney(); // 群主当前余额 if (totalMoney > leftMoney) { System.out.println("余额不足"); return redList; // 返回空集合 } // 扣钱,其实就是重新设置余额 super.setMoney(leftMoney - totalMoney); // 发红包需要平均拆分成为count份 int avg = totalMoney / count; int mod = totalMoney % count; // 余数,也就是甩下的零头 // 除不开的零头,包在最后一个红包当中 // 下面把红包一个一个放到集合当中 for (int i = 0; i < count - 1; i++) { redList.add(avg); } // 最后一个红包 int last = avg + mod; redList.add(last); return redList; } } ------------------ package cn.itcast.day09.demo14; import java.util.ArrayList; import java.util.Random; // 普通成员 public class Member extends User { public Member() { } public Member(String name, int money) { super(name, money); } public void receive(ArrayList<Integer> list) { // 从多个红包当中随便抽取一个,给我自己。 // 随机获取一个集合当中的索引编号 int index = new Random().nextInt(list.size()); // 根据索引,从集合当中删除,并且得到被删除的红包,给我自己 int delta = list.remove(index); // 当前成员自己本来有多少钱: int money = super.getMoney(); // 加法,并且重新设置回去 super.setMoney(money + delta); } }

  java接口的不同版本内容的相应补充

如果是Java 7,那么接口中可以包含的内容有: 1. 常量 2. 抽象方法 如果是Java 8,还可以额外包含有: 3. 默认方法 4. 静态方法 如果是Java 9,还可以额外包含有: 5. 私有方法

  接口的默认方法相应的代码如下(参考就业班day10-06)

package cn.itcast.day10.demo01; /* 从Java 8开始,接口里允许定义默认方法。 格式: public default 返回值类型 方法名称(参数列表) { 方法体 } 备注:接口当中的默认方法,可以解决接口升级的问题,不需要创建抽象方法,那么这样子类就不需要实现接口的抽象方法,而可以直接使用接口的默认方法。 */ public interface MyInterfaceDefault { // 抽象方法 public abstract void methodAbs(); // 新添加的方法,改成默认方法。注意默认方法的public也是可以省略的,但是default不可以省略 public default void methodDefault() { System.out.println("这是新添加的默认方法"); } } --------------------- // 调用默认方法,如果实现的子类类当中没有,会向上找接口的默认方法。 /* 1. 接口的默认方法,可以通过实现接口的子类对象,直接调用。 2. 接口的默认方法,也可以被实现接口的子类进行覆盖重写。 */

  接口的静态方法相应的代码如下(参考就业班day10-07,08)

//有一些内容是与对象无关的,同一类的对象共享这些内容,将这些内容定义在静态方法里面 MyInterfaceStatic.java package cn.itcast.day10.demo01; /* 从Java 8开始,接口当中允许定义静态方法。 格式: public static 返回值类型 方法名称(参数列表) { 方法体 } 提示:就是将abstract或者default换成static即可,带上方法体。 */ public interface MyInterfaceStatic { public static void methodStatic() { System.out.println("这是接口的静态方法!"); } } --------------------- MyInterfaceStaticImpl.java package cn.itcast.day10.demo01; public class MyInterfaceStaticImpl implements MyInterfaceStatic { } --------------------- package cn.itcast.day10.demo01; /* 注意事项:不能通过接口实现类的对象来调用接口当中的静态方法。因为静态方法是与类相关的,所以必须通过类名(接口名调用)。而一个类可以实现多个接口,接口的静态方法可能有冲突,不可以通过对象调用,而通过接口名调用则可以避免方法名冲突。 正确用法:通过接口名称,直接调用其中的静态方法。 格式: 接口名称.静态方法名(参数); */ public class Demo03Interface { public static void main(String[] args) { // 创建了实现类对象 MyInterfaceStaticImpl impl = new MyInterfaceStaticImpl(); // 错误写法! // impl.methodStatic(); // 直接通过接口名称调用静态方法 MyInterfaceStatic.methodStatic(); } }

  接口的私有方法相应的代码如下(参考就业班day10-09)

MyInterfacePrivateA.java package cn.itcast.day10.demo01; /* 问题描述: 我们需要抽取一个共有方法,用来解决两个默认方法之间重复代码的问题。 但是这个共有方法不应该让实现类使用,应该是私有化的。 解决方案: 从Java 9开始,接口当中允许定义私有方法。 1. 普通私有方法,解决多个默认方法之间重复代码问题 格式: private 返回值类型 方法名称(参数列表) { 方法体 } 2. 静态私有方法,解决多个静态方法之间重复代码问题 格式: private static 返回值类型 方法名称(参数列表) { 方法体 } */ public interface MyInterfacePrivateA { public default void methodDefault1() { System.out.println("默认方法1"); //这部分是相同内容,封装到同一个方法methodCommon,methodCommon不应该暴露给实现接口的子类将其私有化 methodCommon(); } public default void methodDefault2() { System.out.println("默认方法2"); methodCommon(); } //将这个方法私有化,那么实现该接口的子类就访问不断,只能是methodDefault1方法与methodDefault2方法访问 private void methodCommon() { System.out.println("AAA"); System.out.println("BBB"); System.out.println("CCC"); } } -------------------------- MyInterfacePrivateAImpl.java package cn.itcast.day10.demo01; public class MyInterfacePrivateAImpl implements MyInterfacePrivateA { public void methodAnother() { // 直接访问到了接口中的默认方法,这样是错误的! // methodCommon();这个方法不应该让实现接口的子类访问到! //将methodCommon()私有化即可,只有methodDefault1与methodDefault2可以使用methodCommon } } ------------------------ MyInterfacePrivateB.java package cn.itcast.day10.demo01; public interface MyInterfacePrivateB { public static void methodStatic1() { System.out.println("静态方法1"); methodStaticCommon(); } public static void methodStatic2() { System.out.println("静态方法2"); methodStaticCommon(); } private static void methodStaticCommon() { System.out.println("AAA"); System.out.println("BBB"); System.out.println("CCC"); } } --------------- package cn.itcast.day10.demo01; public class Demo04Interface { public static void main(String[] args) { MyInterfacePrivateB.methodStatic1(); MyInterfacePrivateB.methodStatic2(); // 错误写法!私有化后这个方法访问不到 // MyInterfacePrivateB.methodStaticCommon(); } }

  使用接口时应该注意的点

/* 使用接口的时候,需要注意: 1. 接口是没有静态代码块或者构造方法的。 2. 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。 格式: public class MyInterfaceImpl implements MyInterfaceA, MyInterfaceB { // 覆盖重写所有抽象方法 } 3. 如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次即可。 4. 如果实现类没有覆盖重写所有接口当中的所有抽象方法,那么实现类就必须是一个抽象类。 5. 如果实现类所实现的多个接口当中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写。重写后只使用重写的默认方法,而不需要去使用接口重复的默认方法。 6. 一个类如果直接父类当中的方法,和接口当中的默认方法产生了冲突,优先用父类当中的方法。 7. 接口静态方法直接通过接口名调用,不需要考虑重复。 */
最新回复(0)