继承
继承的好处:
1/提高了代码的复用性
2/提高代码的维护性
3/让类与类之间产生了联系,给第三个特征多态提供了前提。(也是继承的一个弊端:类的耦合性增强)
开发的原则:低耦合,高内聚
耦合:类与类的关系
内聚:就是自己完成某件事情的能力
继承的基本格式(extends关键字)
class 子类名 extends 父类名 { }
范例1:
class Fu()
{
}
class Zi extends Fu //通过关键字extends让子类继承父类
{
}
class ExtendsDemo
{
public static void main(String[] args)
{
Student s = new Student();
s.name="zhangsan";
s.age=20;
s.study();
}
}
class Person
{
String name;
int age;
}
class Student extends Person //通过extends集成父类的属性
{
void study()
{
System.out.println(name+"....student study...."+age);
}
}
class worker extends Person //通过extends集成父类的属性
{
void work()
{
System.out.println(name+"....worker work...."+age);
}
}
JAVA中继承的特点:
1 / Java中支持单继承,不直接支持多继承,但对C++中的多继承进行了改良。不直接支持多继承,是因为多个父类中有相同成员,会产生调用的不确定性。
单继承:一个子类只能有一个直接父类。
2 / Java支持多层(多重)继承。(C继承B,B继承A),就会出现继承体系。
继承的注意事项:
1 / 子类只能集成父类所有非私有的成员(包括成员变量和成员方法)
2 / 子类不能继承父类的构造方法,但是可以通过super来访问父类的构造方法。
3 / 不要为了部分功能去继承
4 /
当要使用一个继承体系时:
1/查看该体系中的顶层类,了解该体系的基本功能。
2/创建体系中的最子类对象,完成功能的使用。
什么时候定义继承呢?
当类与类之间存在所属关系的时候,就定义继承。XXX是YYY中的一种,XXX extends YYY
————————————————————————————————————————————————————————————
继承中 成员(成员变量,构造方法,成员方法)的关系
继承者成员变量的关系:
a / 子类中的成员变量和父类中成员变量名称不一致。
b / 子类中的成员变量和父类中成员变量名称一样,会采用就近原则(就近原则的的先后顺序:先子类局部——>子类成员——>父类成员)。
调用成员变量的范例:
class Father //父类
{
int num = 10;
}
class Son extends Father //子类继承父类
{
int num = 20;
public void show()
{
int num = 30;
System.out.println(num); //采取的是就近原则。
}
}
class ExtendsDemo
{
public static void main(String[] args)
{
Son s = new Son();
s.show();
}
}
/*
结果是30
*/
this 和 super 的区别和应用
区别:
this 代表本类对应的引用。
super代表父类存储空间的标识(可以理解为父类引用,可以操作父类的成员)
如何调用:
a / 调用成员变量:
this . 成员变量 (表示调用本类的成员变量)
super . 成员变量 (表示调用父类的成员变量)
调用成员变量范例:
class Father //父类
{
int num = 10;
}
class Son extends Father //子类继承父类
{
int num = 20;
public void show()
{
int num = 30;
System.out.println(num); //采取的是就近原则。
System.out.println(this.num); //this访问的就是本类的成员(int num = 20;
System.out.println(super.num); //super访问的是父类存储空间的标识(可以理解为父类引用,可以操作父类的成员)
}
}
class ExtendsDemo
{
public static void main(String[] args)
{
Son s = new Son();
s.show();
}
}
/*
结果是30,20,10
*/
b / 调用构造方法
this (...) (表示调用本类的构造方法)
super(....) (表示调用父类的构造方法)
继承中构造方法的关系:
1 / 子类中所有的构造方法默认都会访问父类中空参数的构造方法
2 / 子类初始化之前,一定要先完成父类数据的初始化。然后才能继承父类中的数据。
3 / 子类的每一个构造方法的第一条语句默认都是:super();
调用构造方法的范例:
class Father
{
public Father() //Father无参构造方法
{
System.out.println("Father的无参构造方法");
}
public Father(String name) //Father带参构造方法
{
System.out.println("Father的带参构造方法");
}
}
class Son extends Father
{
public Son() //son的无参构造方法
{
System.out.println("son的无参构造方法");
}
public Son(String name) //son的带参构造方法
{
System.out.println("son的带参构造方法");
}
}
class ExtendsDemo2
{
public static void main(String[] args)
{
Son s = new Son();
System.out.println("------------------------");
Son s1 = new Son("阿紫");
}
}
/*
结果
Father的无参构造方法
son的无参构造方法
-------------------------
Father的无参构造方法 //说明子类默认访问父类空参数的构造方法
son的带参构造方法
*/
c / 调用成员方法
this . 成员方法 (表示调用本类的成员方法)
super . 成员方法 (表示调用父类的成员方法)
继承中成员方法的关系:
a / 子类中的方法和父类中的方法声明不一样。
b / 子类中的方法和父类中的方法声明一样。
通过子类对象调用方法的顺序:
---1 / 先找子类中看有没有这个方法,有就调用
---2 / 再看父类中有没有这个方法,有就调用,都没有就报错。
——————————————————————————————————————————————————————
方法重写:
子父类中的成员方法的特点:
当子父类中出现成员方法声明一模一样的情况(方法名,参数列表都一样),会运行子类的方法。
这种现象称为方法重写。这是方法在字符类中的特性。
方法的两个特性(仅方法有)
1/重载。同一个类中。(同一个类中,出现的方法名一样,参数列表不同的方法,且与返回值无关。)
2/重写。子类中。覆盖也称为重写,覆写。override (当子父类中出现成员方法声明一模一样的情况(方法名,参数列表都一样))
方法重写注意事项:
1/子类方法覆盖父类方法是,子类权限必须要大于等于父类权限。
2/静态函数只能覆盖静态函数,或被静态函数覆盖。
什么时候使用覆盖操作?(方法重写的应用)
当对一个类进行子类的扩展是,子类需要保留父类的功能声明,
但是要定义子类中该功能的特有内容时,就使用覆盖操作完成。
范例:
class ExtendsTest01
{
public static void main(String[] args)
{
NewPhone p = new NewPhone();
p.call();
p.show();
}
}
class Phone
{
void call()
{
}
void show()
{
System.out.println("number");
}
}
class NewPhone extends Phone //子类内容是对父类的扩展
{
void show()
{
System.out.println("name");
System.out.println("pic");
super.show(); //调用了父类的show方法
}
}
方法重写的注意事项:
a / 父类中的私有方法不能被重写。
b / 子类重写父类方法是,访问权限不能更低。
c / 父类静态方法,子类也必须通过静态方法重写。
练习01:
使用继承前的学生和老师的案例:
/*
老师和学生案例
分析:
学生:
成员变量:名字,年龄
构造函数:无参,有参
成员方法:getXxx() / setXxx()
老师
成员变量:名字,年龄
构造函数:无参,有参
成员方法:getXxx() / setXxx()
*/
//定义学生类
class Student
{
String name;
int age;
public Student() //无参构造方法
{
}
public Student(String name ,int age) //带参构造方法
{
this.name = name;
this.age = age;
}
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return age;
}
}
//定义一个老师类
class Teacher
{
String name;
int age;
public Teacher()
{
}
public Teacher(String name ,int age)
{
this.name = name;
this.age = age;
}
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return age;
}
}
class ExtendsTest_01
{
public static void main(String[] args)
{
Student s1 = new Student(); //无参构造方法的方式
s1.setName("阿朱");
s1.setAge(26);
System.out.println(s1.getName()+"----"+s1.getAge());
System.out.println("-------------------------------");
Student s2 = new Student("阿紫",27); //带参构造方法的调用方式
System.out.println(s2.getName()+"----"+s2.getAge());
}
}
/*
老师类和学生类相同部分向上抽取成person类,然后再继承person类
成员变量:名字,年龄
构造函数:无参
成员方法:getXxx() / setXxx()
*/
//定义一个person父类
class Person
{
private String name;
private int age;
public Person()
{
}
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return age;
}
}
//定义一个Student子类继承person父类
class Student extends Person
{
}
//定义要给Teacher子类继承Person父类
class Teacher extends Person
{
}
class ExtendsTest_02
{
public static void main(String[] args)
{
Student s = new Student();
s.setName("自尊宝");
s.setAge(27);
System.out.println(s.getName()+"----"+s.getAge());
System.out.println("----------------------------");
Teacher t = new Teacher();
t.setName("紫霞仙子");
t.setAge(26);
System.out.println(t.getName()+"----"+t.getAge());
}
}
猫狗案例
/*
猫狗案例
猫有自己的特性playGame
狗友自己的特性lookHome
分析:
猫狗具有一致的特性,吃睡,可以定义一个父类进行继承
成员变量:吃,睡
构造函数:带参
成员方法:getXxx() / setXxx()
不同的特性:
猫有自己的特性playGame
狗友自己的特性lookHome
*/
//定义一个父类,放置相同的共性
class Animal
{
private String eat;
private String sleep;
public Animal(String eat, String sleep) //定义了一个带参构造方法
{
this.eat = eat;
this.sleep = sleep;
}
public void setEat(String eat)
{
this.eat = eat;
}
public String getEat()
{
return eat;
}
public void setSleep(String sleep)
{
this.sleep = sleep;
}
public String getSleep()
{
return sleep;
}
}
//定义一个猫的类,继承animal
class Cat extends Animal
{
public Cat(String eat,String sleep) //定义了一个带参构造方法
{
super(eat,sleep); //通过super指向父类的带参构造方法
}
public void playGame() //定义一个方法,显示小猫特有的个性
{
System.out.println("小猫玩耍");
}
}
//定义个一个狗的类,继承animal
class Dog extends Animal
{
public Dog(String eat,String sleep)
{
super(eat,sleep);
}
public void lookHome()
{
System.out.println("小狗看家");
}
}
class ExtendsTest_03
{
public static void main(String[] args)
{
Cat c = new Cat("睡觉","吃鱼");
System.out.println(c.getEat()+"----"+c.getSleep());
c.playGame();
System.out.println("----------------------------");
Dog d = new Dog("睡觉","吃骨头");
System.out.println(d.getEat()+"----"+d.getSleep());
d.lookHome();
}
}
一个对象的实例化过程:
Person p = new Person();
1/jvm会读取指定路径下的Person.class文件并加载进内存,并会先加载Person的父类(如果有直接父类的情况下)。
2/在对内存中开辟空间,分配地址值。
3/并在对象空间中,对对象中的属性进行默认初始化。
4/调用对应的构造函数进行初始化。
5/在构造函数中,第一行会先调用父类中的构造函数进行初始化。
6/父类初始化完毕后,再对子类的属性进行显示初始化。
7/再进行子类构造函数的特定初始化。
8/初始化完毕后,将地址值赋值给引用变量。