黑马毕向东Java课程笔记(day07):面向对象(第三部分)继承+抽象类+模板方法设计模式+接口+final+继承补充(就业班)
在这一部分中,我们将讲解有关继承的相关内容,包括继承的概述、继承的特点、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+继承补充(就业班)相关推荐
- 黑马毕向东Java课程笔记(day20-1——20-17)IO流:File类及相关方法、递归、递归的相关练习、Properties、PrintWriter类与PrintStream类、合并流与切割流
1.File类概述 File是文件和目录路径名的抽象表示形式. 用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作. 前面说到的"流",它只能操作数据,想 ...
- 黑马毕向东Java课程笔记(day19-11——19-22)IO字节流:字节流及其读取、字节流缓冲区、自定义字节流(读取)的缓冲区、读取键盘的输入、读取/写入转换流、流操作规律
1.字节流--File 字节流的介绍 字符流:(一个字符2个字节16位) FileReader FileWriter. BufferedReader BufferedWriter字节流:(一个字节 ...
- 黑马毕向东Java课程笔记(day11):多线程(第一部分)——进程与线程+线程创建+线程安全与同步代码块+同步锁/死锁
多线程好文:添加链接描述 锁机制:synchronized.Lock.Condition.volatile(原子性可见性)--参考添加链接描述 1.进程与线程概述 首先,对于CPU执行每一个程序, ...
- 黑马毕向东Java课程笔记(day14-1——14-11):集合类(集合框架)——集合类分类与特点+List集合接口及其子类
1.集合类特点 为什么出现集合类? 面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式. 数组和集合类同是容器,有何不同 ...
- 黑马毕向东Java课程笔记(day16-1-16-9):集合类(集合框架)——Map集合
1.Map集合 Map集合的基本特点如下: 接口 Map<K,V>:将键映射到值的对象.一个映射不能包含重复的键:每个键最多只能映射到一个值.(但是值可以重复) K - 此映射所维护的 ...
- 毕向东java基础笔记
函数功能: pubic void getSum(int x, int y) { int ret = x+y; System.out.println(ret); } 这个功能定义的思想有问题,因为只为完 ...
- 【JAVA】毕向东Java基础视频教程-笔记
传智播客-毕向东Java基础视频教程 <2013年-33days>版-学习代码记录 链接: GitHub库:JavaBXD33 目录 01-Java基础知识 02-Java对象细节 03- ...
- java学习笔记day07 成员变量与局部变量、形式参数、匿名对象、封装、private、this、构造方法、类详细讲解、static
java学习笔记day07 1.成员变量和局部变量的区别 定义变量的注意事项 2.形式参数⭐ [P175] 形参是个类名,要的其实是一个对象 3.匿名对象(了解即可) 4.封装 class Stude ...
- 毕向东—Java基础知识总结(超级经典)
Java基础知识总结(超级经典) 写代码: 1,明确需求.我要做什么? 2,分析思路.我要怎么做?1,2,3. 3,确定步骤.每一个思路部分用到哪些语句,方法,和对象. 4,代码实现.用具体的java ...
最新文章
- 配置jdk环境 windows
- Asp.net的Session和Cookie传值方式
- 目标检测 - 如何在图片中标记Annotations中的坐标信息?
- 在MAC系统的eclipse里打开android sdk manager
- Linux kms 模式设置,linux – 使用KMS设置控制台视频分辨率
- 在CRM中怎么应用大数据挖掘
- AliSQL开源功能特性
- MyDriver2-397 XCTF 3rd-RCTF-2017 (windows 驱动题)
- 解决 No converter found for return value of type 的问题
- AFLGO插桩代码分析记录
- Python字符串逆序输出
- Python 自动化工具开源及办公自动化 10 高频操作,代码可直接套用
- 微信小游戏是个人尝试做游戏最好的选择
- kaggle 电商数据分析
- 计算机主板供电故障,电脑主板内存电路常见故障的检修
- Blender雕刻模块:2.81新功能遮罩提取(Mask Extract)
- python 根据身份证号计算年龄和性别_excel如何根据身份证号计算男女出生日期、性别和年龄?分享了!...
- 风格化半调效果如何制作?教程来了
- H5页面 禁止微信分享转发按钮
- c语言中数组名和数组名取地址理解