提示:本笔记基于黑马程序员java教程整理,仅供参考
前言
1.继承
2.多态
2.1对象多态
2.2行为对象
2.3多态的问题
本文纯笔记,主要记录了java面向对象的高级方法继承与多态
在创建多个对象类时,它们可能会有很多相似的属性,如姓名,身高,体重等,就会造成代码的重复,所以我们可以采用继承的思想,将它们相同的属性放在同一个父类中,而其它作为子类的都可以继承父类的属性,子类本身就只用定义自身独特的属性即可,减少了代码的重复书写。
extends
可以利用extends建立父子关系
public class A extends B
A为子类,B为父类,子类可以继承父类的非私有成员(创建两个java类分开放,不是放在同一个类中哦!)
例:
//父类
public class People {
private String name; //私有成员,不能继承
int age;
String sex;
//自行填写构造方法
//自行填写get set方法
}
//子类
public class Student extends People{ //可以继承父类的age,sex属性
private String school;
//......
}
这样可以少写很多重复的get,set方法
在继承过程中,我们可能会遇到父类中存在private,protect等权限修饰符修饰成员变量,以下是相关介绍:
private:表示该成员只能在本类中访问,无法被子类继承
protect:表示该成员变量可以由本类,本包中的类访问,可以被子类继承,无法被其他包访问
特点:(1)可以嵌套继承,即子类可以有且仅能有一个父类,父类还可以有一个父类,子类可以访问这两者的公开的成员变量
(2)方法也可以继承,但是当子类和父类有一个方法名相同但内容不同的方法时,是调用子类中的方法,叫做方法重写。
如:父类——鸟,子类——麻雀,父类有一个方法cry表示鸟叫,但麻雀可能不是这么叫的,于是需要在子类中重新定义一个cry方法,表示麻雀叫声。
//父类
public class Bird {
public void cry()
{
System.out.println("鸟叫");
}
}
//子类
public class Maque extends Bird {
@Override//加上这个标签可以强制让你重写的内容与父类保持一致,不会出错,如果不加标签,重写的内容不一致,test会调用父类的方法
public void cry(){
System.out.println("吱吱吱");
}
}
public class Test {
public static void main(String[] args) {
Maque maque=new Maque();
maque.cry();
}
}
toString方法
//父类
public class Bird {
private String name;
int age;
String sex;
}
//子类
public class Maque extends Bird {
String name;
public Maque(String name,int age,String sex){
this.name=name;
this.age=age;
this.sex=sex;
}
//自行构造set,get方法
@Override
public String toString() {//重写toString方法
return name+age+sex;
}
}
public class Test {
public static void main(String[] args) {
Maque maque=new Maque("小青",2,"雌");
System.out.println(maque);
}
}
当我们在test文件中创建完对象后,我们希望能通过直接输出得到其信息,此时System.out.println(maque);是默认调用toSring方法输出,toString方法是系统本身有的方法,但是我们此时输出得到的是maque的地址而不是数据,因此我们需要在子类中重写方法来输出数据。
构造器
当test中调用子类的构造器时,会先调用其父类的构造器,然后再调用子类的构造器,顺序很重要
super
实际上,在子类中会有一个super()方法,只是没有写出来,它用来查找父类中的构造器,这也是为什么当test中调用子类的构造器时,会先调用其父类的构造器,然后再调用子类的构造器的原因。
//父类
public class Bird {
private String name;
private int age;
public Bird(){}
public Bird(String name,int age)
{
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
//子类
public class Maque extends Bird {
String sex;
public Maque(String name,int age,String sex){
super(name,age); //用来找父类的构造器为name和age赋值
this.sex=sex;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
public class Test {
public static void main(String[] args) {
Maque maque=new Maque("小青",2,"雌");
System.out.println(maque.getName()+" "+maque.getAge()+" "+maque.getSex());
}
}
解释:当我们给父类中的成员变量私有时,我们在测试类(test)中调用子类的有参构造器为对象赋值,此时我们会发现,当我们输出时age和name的数据为空,那是因为这两个成员变量在父类中私有,无法直接赋值,于是我们可以构造super(),在其中加入父类中私有的成员变量(这样可以利用super来找父类中相关的有参构造器),然后在父类中创建一个包含这些成员变量的有参构造器为age和name赋值。
this调用兄弟构造器
在对象类中我们创建了一个有参构造器为对象赋值,比如
public class Bird {
private String name;
private int age;
private String sex;
public Bird(){}
public Bird(String name,int age,String sex)
{
this.name=name;
this.age=age;
this.sex=sex;
}
//自行构造无参构造器和get/set方法
}
当我们需要重新构造一些对象,但是它们的性别固定是雌性,我们可能需要重新写一个有参构造器,然后类比上一个有参构造器,将this.sex=sex改为this.sex="雌“。但是这样就会造成代码重复度太高,所有我们会采用this调用兄弟构造器的方法减少代码重复度。
public Bird(String name,int age){
this(name,age,“雌”)
}
注意:super()和this()必须写在构造器的第一行,并且不能同时存在
大范围到小范围定义对象
定义的对象,它们的行为不同
public class DuoTai {
public static void main(String[] args){
People a=new Teacher(); //People的范围更大,可以这样创建对象(对象多态)
a.learn(); //编译看左,运行看右
System.out.println(a.name)//编译看左,运行也看左
People b=new Student();
b.learn(); //学生和老师的学习行为不同(行为多态)
}
}
public class People {
private String name;
private int age;
private String sex;
public People(){}//无参构造
public void learn(){
System.out.println("人都学习");
}
}
public class Teacher extends People {
private String skill;
public Teacher(){}
@Override
public void learn() {
System.out.println("老师在教课中学习");
}
}
public class Student extends People{
public Student(){}
@Override
public void learn() {
System.out.println("学生在课堂中学习");
}
}
People a=new Teacher(); 如果用对象多态的方法定义对象,那么在调用learn方法时a.learn(),编译看左边,运行看右边,即先在People类中查找是否存在learn()方法,存在则编译,不存在则报错,而运行时还是运行Teacher中的重写的learn方法
如果用对象多态的方法定义对象,对于成员变量而言,System.out.println(a.name)编译看左,运行也看左,即运行People中的定义name结果(未写入代码)
多态优势:便于软件解耦合, People a=new Teacher(); a.learn()此处表示people和teacher是解耦合的,当Teacher类我们需要更换时,可以随时将其更换成其它类,进而改变所进行的业务a.learn(),这样更便于扩展和维护
例如:A公司可以与B,C,D公司合作,B,C,D三家公司的业务名称一致,但是内容有所不同,A首先与B合作,如果B不行,可以直接更换合作公司,并且不需要重新告诉要做什么业务,可以直接拿上一个公司的模板来做,即不需要重新编写方法
public class DuoTai {
public static void main(String[] args){
People a=new Teacher();
test(a);
People b=new Student();
test(b);
}
}
public class People {
public People(){}//无参构造
public void learn(){
System.out.println("人都学习");
}
}
public class Teacher extends People {
public Teacher(){}
@Override
public void learn() {
System.out.println("老师在教课中学习");
}
}
public class Student extends People{
public Student(){}
@Override
public void learn() {
System.out.println("学生在课堂中学习");
}
}
public void test(People p){
p.learn();
}
通过上述代码,我们发现, 主函数调用test方法,而test接收的是People p,这就是多态的运用,好处是当我们有多个不同的对象调用同一个方法时,不需要将People p改成对应的Teacher p或Student p,如果我们采用常规创建对象的方法Teacher a=new Teacher();Student b=new Student();那么在其调用test方法时,我们需要将括号中的参数修改2次,或者创建两个test方法,这样显然会导致代码重复度过高,造成代码冗余,所以在编写代码时,我们常常会将多态方法设为编写代码的必然要求
在多态状态下,不能调用子类独有的功能,因为对于方法,编译看左边,运行看右边,如果父类中没有这个方法,那么当我们采用a.eat()等方式调用子类独有功能时就会报错。
解决方法:强制类型转换
如:People a=new Teacher();我们可以添加代码Teacher t=(Teacher) a,这样就表示强制转换,此时t就代表将a转换成了Teacher对象,可以用t调用Teacher的独有功能。
public class Fangfa {
public void test(People p){
if(p instanceof Teacher){ //判断p是否是Teacher对象
Teacher t=(Teacher)p;
t.teach();
}
else if(p instanceof Student){ //......
Student s=(Student)p;
s.study();
}
}
}
如果在Fangfa中调用对象的独有方法,我们需要先判断是哪一个对象,然后再对其进行强制转换,然后再调用方法。