1,为什么需要继承
假设我们要写几个动物的类:
小猫:眼睛,耳朵,尾巴,鼻子,爪子。。。。
小狗:眼睛,耳朵,尾巴,鼻子,爪子。。。。
小羊:眼睛,。。。。。。
很显然,这些类中是有许多共同之处的,比如它们都有眼睛,耳朵,尾巴等,不同的只是各个成员(眼睛,尾巴等)的具体内容,如眼睛的大小,尾巴的颜色,长短等而已。如果每个类都单独来写,某些相同的代码就需要重复写许多遍,但怎样能解决这个问题呢?可不可以将具有相同功能的内容只写一次呢?这时,就需要用到继承(inheritance)。
2,继承的作用
1,提高的代码的重用性;
2,提高代码的可扩展性
3,继承的格式
java中继承的关键字:extends
格式:
public class 类名(子类,派生类) extends 类名(父类,基类){
}
注意:1,java中的继承是单继承,也就是说一个子类只能继承一个父类,但一个父类可以被多个子类所继承;
2,子类可以继承到父类中的所有属性和方法,与访问修饰符无关。访问修饰符只能限制对象访问方法的范围。
3,子类可以定义父类中没有的属性和方法,但也只能由子类的对象进行调用。
4,子类继承父类之后,实例化子类对象时,系统会首先实例化父类对象。
举个例子:
package Extends;
public class Student{
//父类 学生类
public String name;
public void study(){ //学习方法
System.out.println("学生的名字是:"+name);
}
public void setName(String name){ //设置名字方法(参数)
this.name = name;
}
public void eat(){
System.out.println("学生每天都在食堂吃饭");
}
public void rest(){
System.out.println("学生每天都在宿舍休息 ");
}
}
package Extends;
public class UNStudent extends Student{
//子类 大学生类
public static void main(String[] args) {
// TODO Auto-generated method stub
UNStudent uns = new UNStudent();
uns.setName("张三");
uns.study();
uns.cet6();
}
public void cet6(){ //子类特有的方法
System.out.println("大学生不想考六级!!!");
}
}
4,自动转型
1),自动转型的条件是什么?
必须存在继承关系。
2),自动转型的格式:
一,将子类对象的类型定义成父类对象的类型,也就是说,子类对象的类型可以自动转型成父类对象类型。
父类名 对象名 = new 子类构造方法(参数值,...);
Student un = new UNStudent();
Student un = new UNStudent();
注意:1,子类类型转成父类类型后不能调用子类特有的方法;
2,方法重写时,先调用子类还是父类的方法,取决于new关键字之后的类。
二,访问修饰符 返回值类型 方法名(父类名 参数名,...){
}
子类名 对象名 = new 子类构造方法(参数值,...);
方法名(对象名)
}
子类名 对象名 = new 子类构造方法(参数值,...);
方法名(对象名)
3),自动转型会引发什么问题?
无法调用子类特有的属性和方法。
原因:java编译和运行机制的不同
java编译时,编译的是文件,是根据对象名的类型进行编译的。如果对象名调用的属性和方法在对象名的类型中存在,就会通过编译,否则不通过;
而运行时,会使用对象名中存储的对象的堆内存首地址。调用方法时,会根据对象名中存入的首地址优先进行调用,如果对象名中没有才会去调用父类的。
5,强制转型
1),强制转型的条件是什么?
1,必须存在继承关系
2,之前实例化的对象是子类的对象(也就是说,如果一个对象可以被强制转型,那么它肯定在前面某个地方被自动转型过)
2),强制转型的格式:
子类名 子类对象名 =(子类名)父类对象名;
例子:
Student stu = new UNStudent(); //将子类对象定义为父类类型(自动转型)
stu.setName("李四");
stu.study();
//对象stu不能调用子类特有的方法cet6
UNStudent stu2 = (UNStudent)stu; //将父类类型的对象stu强制转换成子类类型(强制转型)
stu2.cet6();
6,方法重写 override
1),什么时候需要方法重写:
当父类中存在一个方法,子类中也有这个方法,但子类中方法的具体实现与父类中不同时,重写该方法。
2),方法重写的条件:
1,必须存在继承关系;
2,子类方法的访问修饰符必须大于或等于父类方法的访问修饰符;
3,子类方法的返回值类型,方法名以及参数都必须与父类一致;
4,方法的实现不能相同。
package Extends;
public class JUStudent extends Student{
//子类 中学生类
public void study(){ //方法的重写
System.out.println(name + "是中学生");
}
public static void main(String[] args){
JUStudent jus = new JUStudent();
jus.setName("王五");
jus.study();
Student stu3 = new JUStudent();
stu3.setName("赵六");
stu3.study();
}
}
注意:在子类重写父类的某方法后,最后调用时调用的是子类的方法。这种情况下,如果一定要调用父类中的成员函数,可以使用super关键字。调用方法是:super.函数名
父类:
package Jicheng;
public class Dialog_01 {
protected String title;
public Dialog_01(String title){
this.title = title;
}
public void show(){
System.out.println(title+"对话框显示");
}
}
子类:
//子类
package Jicheng;
public class FrontDialog extends Dialog{
private String frontName;
public FrontDialog(String title,String frontName){
this.title = title;
this.frontName = frontName;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
FrontDialog fd = new FrontDialog("字体","宋体");
}
}
此时,子类public FrontDialog(String title,String frontName)处会报错。因为只要实例化子类对象,系统就会自动先实例化一个父类对象与之对应,当然此时调用的是父类没有参数的构造函数。
解决方法:
1),给父类增加一个无参的构造函数
//解决方法1:给父类增加一个没有参数的构造方法
package Jicheng;
public class Dialog_01 {
protected String title;
public Dialog_01(){}
public Dialog_01(String title){
this.title = title;
}
public void show(){
System.out.println(title+"对话框显示");
}
}
2)在子类的构造函数中,第一句用super给父类的构造函数传参数
//解决方法2:在子类的构造方法中,第一句用super()给父类构造函数传参数
package Jicheng;
public class FrontDialog_02 extends Dialog{
private String frontName;
public FrontDialog_02(String title,String frontName){
super(title);
this.frontName = frontName;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
FrontDialog_02 fd = new FrontDialog_02("字体","宋体");
}
}
注意:用super给父类传参时,必须写在子类构造函数的第一句,且传入的参数必须和父类构造函数中参数列表类型相匹配。7,接口
1),为什么需要接口?
一般情况下,父类都比子类更加抽象,因为父类中的属性和方法比子类更少,而且父类的方法中,我们只能笼统的描述,而不能像子类方法一样更为具体。
所以当我们只知道父类的对象应该做什么,但不知道该怎样做时,我们就可以定义这样的一个类,类中的方法只有方法的定义,没有方法体,这个类我们就叫它接口。
2),定义接口的格式;
public interface 接口名 extends 接口,... {
//定义常量
public static final 数据类型 常量名 = 值;
//定义抽象方法
public abstract 返回值类型 方法名(数据类型 参数名,...);
}
//定义常量
public static final 数据类型 常量名 = 值;
//定义抽象方法
public abstract 返回值类型 方法名(数据类型 参数名,...);
}
注意:
1.接口只有一个访问修饰符public。
2.接口默认会提供public、static、final、abstract关键字。
3.接口不能实例化对象。
1.接口只有一个访问修饰符public。
2.接口默认会提供public、static、final、abstract关键字。
3.接口不能实例化对象。
package Extends;
//定义一个接口(一种数据类型),主要用来扩展子类
//**只有方法体,但没有对方法的定义**
public interface Person { //接口的属性默认为public static final(公共的静态常量)
public static final int num = 1000;
void eat(); //接口中的方法默认为public abstract(公共的抽象方法)
public abstract void rest();
}
3),实现接口:
实现(继承)接口的关键字:implements
格式:
public class 类名 extends 类名 implements 接口,... {
//注意:类必须要实现(重写)接口中所有的抽象方法。(包含接口继承父接口中的抽象方法)
格式:
public class 类名 extends 类名 implements 接口,... {
//注意:类必须要实现(重写)接口中所有的抽象方法。(包含接口继承父接口中的抽象方法)
}
一个类可以继承多个接口。
package Extends;
public class Student implements Person{
public static void main(String[] args){ //实现person接口
Person per = new Student();
per.eat();
per.rest();
}
}
8,抽象类
1),什么是抽象类:
父类中如果规定一个抽象函数:abstract 函数名() ,其必须被重写。含有抽象函数的类就是抽象类,也必须用abstract修饰。
注意:1,抽象类不能被实例化;
2,抽象函数必须被重写,除非子类也是抽象类;
3,抽象类中也可以含有普通成员函数。
9,final关键字
1,用final修饰一个类:
表示该类不能被继承
2,用final修饰一个函数:
表示该类在被子类继承的情况下,该函数不能被重写
3,用final修饰一个成员变量:
表示该成员变量的值不许改变,也就是说,不允许重新赋值。因此,一般用final关键字定义一个常量。