在这一部分中,我们将讲解有关继承的相关内容,包括继承的概述、继承的特点、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,包含了静态的NUMSystem.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); // 10System.out.println(zi.numZi); // 20System.out.println("===========");// 等号左边是谁,就优先用谁
//Zi zi = new Zi();这个式子等号左边是子类,那么调用的是子类的num
//对于Fu fu = new Zi();这种多态的情况,等号左边是父类,则会调用父类的numSystem.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,所以这里用的是本类的numSystem.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下面不会标红@Overridepublic 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(); // 100one.show(); // 0two.show(); // 0three.show(); // 0System.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. 接口静态方法直接通过接口名调用,不需要考虑重复。*/

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

  1. 黑马毕向东Java课程笔记(day20-1——20-17)IO流:File类及相关方法、递归、递归的相关练习、Properties、PrintWriter类与PrintStream类、合并流与切割流

    1.File类概述   File是文件和目录路径名的抽象表示形式. 用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作.   前面说到的"流",它只能操作数据,想 ...

  2. 黑马毕向东Java课程笔记(day19-11——19-22)IO字节流:字节流及其读取、字节流缓冲区、自定义字节流(读取)的缓冲区、读取键盘的输入、读取/写入转换流、流操作规律

    1.字节流--File   字节流的介绍 字符流:(一个字符2个字节16位) FileReader FileWriter. BufferedReader BufferedWriter字节流:(一个字节 ...

  3. 黑马毕向东Java课程笔记(day11):多线程(第一部分)——进程与线程+线程创建+线程安全与同步代码块+同步锁/死锁

    多线程好文:添加链接描述 锁机制:synchronized.Lock.Condition.volatile(原子性可见性)--参考添加链接描述 1.进程与线程概述   首先,对于CPU执行每一个程序, ...

  4. 黑马毕向东Java课程笔记(day14-1——14-11):集合类(集合框架)——集合类分类与特点+List集合接口及其子类

    1.集合类特点   为什么出现集合类? 面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式.   数组和集合类同是容器,有何不同 ...

  5. 黑马毕向东Java课程笔记(day16-1-16-9):集合类(集合框架)——Map集合

    1.Map集合   Map集合的基本特点如下: 接口 Map<K,V>:将键映射到值的对象.一个映射不能包含重复的键:每个键最多只能映射到一个值.(但是值可以重复) K - 此映射所维护的 ...

  6. 毕向东java基础笔记

    函数功能: pubic void getSum(int x, int y) { int ret = x+y; System.out.println(ret); } 这个功能定义的思想有问题,因为只为完 ...

  7. 【JAVA】毕向东Java基础视频教程-笔记

    传智播客-毕向东Java基础视频教程 <2013年-33days>版-学习代码记录 链接: GitHub库:JavaBXD33 目录 01-Java基础知识 02-Java对象细节 03- ...

  8. java学习笔记day07 成员变量与局部变量、形式参数、匿名对象、封装、private、this、构造方法、类详细讲解、static

    java学习笔记day07 1.成员变量和局部变量的区别 定义变量的注意事项 2.形式参数⭐ [P175] 形参是个类名,要的其实是一个对象 3.匿名对象(了解即可) 4.封装 class Stude ...

  9. 毕向东—Java基础知识总结(超级经典)

    Java基础知识总结(超级经典) 写代码: 1,明确需求.我要做什么? 2,分析思路.我要怎么做?1,2,3. 3,确定步骤.每一个思路部分用到哪些语句,方法,和对象. 4,代码实现.用具体的java ...

最新文章

  1. 配置jdk环境 windows
  2. Asp.net的Session和Cookie传值方式
  3. 目标检测 - 如何在图片中标记Annotations中的坐标信息?
  4. 在MAC系统的eclipse里打开android sdk manager
  5. Linux kms 模式设置,linux – 使用KMS设置控制台视频分辨率
  6. 在CRM中怎么应用大数据挖掘
  7. AliSQL开源功能特性
  8. MyDriver2-397 XCTF 3rd-RCTF-2017 (windows 驱动题)
  9. 解决 No converter found for return value of type 的问题
  10. AFLGO插桩代码分析记录
  11. Python字符串逆序输出
  12. Python 自动化工具开源及办公自动化 10 高频操作,代码可直接套用
  13. 微信小游戏是个人尝试做游戏最好的选择
  14. kaggle 电商数据分析
  15. 计算机主板供电故障,电脑主板内存电路常见故障的检修
  16. Blender雕刻模块:2.81新功能遮罩提取(Mask Extract)
  17. python 根据身份证号计算年龄和性别_excel如何根据身份证号计算男女出生日期、性别和年龄?分享了!...
  18. 风格化半调效果如何制作?教程来了
  19. H5页面 禁止微信分享转发按钮
  20. c语言中数组名和数组名取地址理解

热门文章

  1. 向量范数的几何直观理解和等价定义——如何从几何上定义向量范数?
  2. 请不要错过身边爱你的人
  3. 如何将无线路由器设置为AP模式-Genie管理界面
  4. 一个传统的分销+仓储+物流的功能,软件能做些什么让它最快看到成果
  5. 张兴个人简历计算机,张兴-合肥工业大学电气与自动化工程学院
  6. python3tkinter去掉边框
  7. 一.mysql数据库保存微信用户名报错
  8. 小优机器人系统恢复_canbot小优机器人软件下载
  9. mysql接口用例增删改查_Mybatis通过接口的方式实现增删改查
  10. shell中的字符串处理