8继承
8.1什么是继承?
继承是从已有的类中派生出新的类,新的类能够吸收已有的类的属性和方法,并能派生出新的属性和方法。(换种说话,从已有的类中提取共有的属性和方法,把它作为父类,已有的类都继承这个类。)
8.2为什么要使用继承?
使用继承可以优化设计
Dog类 |
name ,health,love |
strain |
Print() |
Penguin类 |
name ,health,love |
Sex |
Print() |
Pet类 |
name ,health,love |
Print() |
8.3如何实现继承?
1.定义父类
2.子类 extends 父类
extends 关键字
[访问修饰符] class pet{
共有的属性 和方法
}
[访问修饰符] class Dog extends Pet{
子类特有的属性和方法
}
java 只支持单根继承,每个子类只能有一个直接父类,一个父类可以被多个子类继承。
8.4子类访问父类成员
(1)使用super关键字,super代表父类对象 ;
(2)在子类构造方法中调用且必须是第一句。
(3)不可以访问父类中定义为private的属性和方法。
访问父类构造方法
super() 表示调用父类的无参构造
super(name) 表示调用父类的带参构造 super(属性1,属性2,属性3….)
访问父类属性
super.name
访问父类方法
super.print() super.方法名 调用父类的方法
调用规则:
子类:没有通过this显示的调用自己的构造方法,也没有super(属性名)调用父类的带参构造方法,那么默认调用父类的无参构造方法。
子类:子类通过super(属性名)调用父类的带参构造方法,那么执行父类的带参构造,而不会执行无参构造
子类:子类构造方法通过this显示调用自身的其他构造方法时,在相应构造方法中应用以上两条规则
示例:
父类Pet
/**
* 1.将共有的属性,和方法 抽取到父类中
* 2.通过 extends 关键字 让子类继承父类 建立子父类之间的联系
* 构造的调用规则:
* super() 表示调用父类的无参构造 super(属性1,属性2,属性3...) 表示调用父类的带参构造
* 子类中没有通过this(属性名),this()调用自身的其他构造,也没有通过super(属性名)显式的调用父类的带参构造,默认调用父类的无参构造。
* 子类通过super(属性名)调用父类的带参构造方法,那么执行父类的带参构造,而不会执行无参构造
* 子类:子类构造方法通过this显示调用自身的其他构造方法时,在相应构造方法中应用以上两条规则
* super.方法名() 表示调用父类的方法
* @author Administrator
*/
public class Pet {
private String name; //名字
private int health;//健康值
private int love; //亲密度
/**
* 父类里无参构造
*/
public Pet() {
System.out.println("调用了父类Pet中的无参构造Pet()");
}
/**
* 父类里带2个参数 name,health的构造
* @param name
* @param health
*/
public Pet(String name,int health){
super(); //表示Object基类中的无参构造 ,super()写不写都一样,不写默认也会调用
this.name = name;
this.health = health;
System.out.println("调用了父类Pet中带2个参数的构造Pet(name,health)");
}
/**
* 父类Pet的构造中,只能出现super()表示调用基类Object的无参构造 ,this(),this(属性名) 表示调用自身的其他构造 ,不可能出现super(属性名)
* @param name
* @param health
* @param love
*/
public Pet(String name, int health, int love) {
this(name,health);
this.love = love;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getHealth() {
return health;
}
public void setHealth(int health) {
this.health = health;
}
public int getLove() {
return love;
}
public void setLove(int love) {
this.love = love;
}
public void print(){
System.out.print("宠物的自白:\n我叫"+this.getName()+",我的健康值是:"+this.getHealth()+",和主人的亲密度为:"+this.getLove());
}
}
子类Dog
/**
* 1如何写子父类之间带参构造
* 2.如何调用父类的方法 super.方法名
* @author Administrator
*
*/
public class Dog extends Pet {
private String strain; //品种 狗狗特有的属性
/**
* 无参构造
*/
public Dog() {
super();
System.out.println("调用了子类Dog()中的无参构造");
}
/**
* 带2个参数name,strain的构造
* @param name
* @param strain
*/
public Dog(String name,String strain){
super(); //表示显示的调用父类里的无参构造,写不写都一样,默认都会去调用父类的无参构造
//super.方法名 调用父类Pet中set方法给name属性赋值 this.setName(name);
super.setName(name);
this.strain = strain;
System.out.println("调用了子类Dog(name,strain)中带2个参数的构造");
}
/**
* 带3个参数 name,health,strain的构造
* @param name
* @param health
* @param strain
*/
public Dog(String name,int health,String strain){
super(name,health); //super(属性名)调用父类里带2个参数的构造
this.strain = strain;
System.out.println("调用了子类Dog(name,health,strain)中带3个参数的构造");
}
/**
* 子类Dog中带4个参数的构造
* @param name
* @param health
* @param love
* @param strain
*/
public Dog(String name,int health,int love,String strain){
this(name,health,strain); //调用自身的带3个参数的构造,给name,health,strain3个属性赋值
super.setLove(love);
System.out.println("调用了子类Dog(name,health,love,strain)中带4个参数的构造");
}
public String getStrain() {
return strain;
}
public void setStrain(String strain) {
this.strain = strain;
}
public void print(){
//先去调用父类的print()显示前3个属性的信息
super.print();
System.out.println(",我是一只"+this.getStrain());
}
}
子类Penguin:
public class Penguin extends Pet {
private String sex; //性别 企鹅特有的属性
public Penguin() {
}
public Penguin(String name,int health,int love,String sex) {
/*super.setName(name);
super.setHealth(health);
super.setLove(love);*/
super(name,health,love);
this.sex = sex;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public void print(){
super.print();
System.out.println(",我的性别是:"+this.getSex());
}
}
8.5子类继承父类什么?
只能继承public 和protected 修饰的,不管在不在同一包里
继承默认(default)修饰的,只能在同一个包里
构造方法不能继承。只能调用 ,通过super调用。
8.6何时用继承?
is-a 关系的设计使用继承 (猫是一种哺乳动物,藏獒是一种狗)
8.7 重写Override
子类根据需求对父类继承的方法进行重新编写,
重写时,可以用super.方法的方式来保留父类的方法
构造方法不能被重写
方法重写的规则:
方法名相同
参数列表相同
返回值类型相同或者是其子类
访问权限不能严于父类
(如果父类方法用protected修饰 ,子类可以用protected,public ,如果父类用default修饰的,子类可以用default,protected,public)
父类的静态方法不能被子类覆盖为非静态方法,父类的非静态方法不能被子类覆盖为静态方法。
子类可以定义与父类同名的静态方法,以便在子类中隐藏父类的静态方法。
父类的私有方法不能被子类覆盖
不能抛出比父类方法更多的异常,运行时异常除外。
面试题: 重写和重载的区别?
方法重写:父子类, 方法同名,同参,返回值相同或是其子类,访问权限不能严于父类的方法权限
方法重载:同一个类 方法同名,参数列表不同(个数,类型,顺序),和返回值,访问权限无关。
示例:
import java.io.FileNotFoundException;
import java.text.ParseException;
public class Base {
public void ceshi1(){
System.out.println("hello");
}
void ceshi2(){
System.out.println("测试2");
}
protected void ceshi2(String name,String sex){
}
public int ceshi3(int a){
return a;
}
public Base getBase(){
return new Base();
}
/**
* 父类的静态方法
*/
public static void ceshi4(){
}
/**
* 父类的非静态方法(普通方法)
*/
public void ceshi5(){
}
public static void ceshi6(){
System.out.println("父类Base中的静态方法ceshi6()");
}
private void ceshi7(int a){
System.out.println(10);
}
/**
* 该方法抛出2个可查的异常
* @throws FileNotFoundException
* @throws ParseException
*/
public void ceshi8() throws FileNotFoundException,ParseException{
}
/**
* 该方法抛出2个运行时异常
* @throws ArithmeticException
* @throws NullPointerException
*/
public void ceshi9() throws ArithmeticException,NullPointerException{
}
}
import java.io.FileNotFoundException;
import java.text.ParseException;
public class Sub extends Base {
public void ceshi1(){
System.out.println("world");
}
/**
* 编译错误 ,访问权限(访问修饰符级别>=父类的方法的访问级别) 不能严于父类,
*/
/*private void ceshi2(){
System.out.println("子类里的测试2");
}*/
protected/*public*/ void ceshi2(){
}
/**
* 重写,参数列表相同
*/
protected void ceshi2(String name,String sex){
}
/**
* 重写,返回值类型和父类相同
*/
public int ceshi3(int b){
return b;
}
/**
* 重写,返回值类型是其子类
*/
public Sub getBase(){
return new Sub();
}
/**
* 编译错误,不是重写,父类的静态方法不能被子类覆盖为非静态方法
*/
/*public void ceshi4(){
}*/
/**
* 编译错误,不是重写 父类的非静态方法不能被子类覆盖为静态方法
*/
/*public static void ceshi5(){
}*/
/**
*不是重写, 只是子类定义了父类同名的静态方法
*/
public static void ceshi6(){
System.out.println("子类Sub中的静态方法ceshi6()");
}
/**
* 不是重写,此处的ceshi7(int a)和父类Base中的私有方法ceshi7(int a),没有任何关系,就是方法同名
* 父类Base中的ceshi7(int a) 该方法的作用域只在类Base中有效,超过该类,任何地方都访问不到
* @param a
*/
public void ceshi7(int a){
System.out.println(12);
}
/**
* 不能抛出比父类方法更多的异常
*/
/*public void ceshi8() throws FileNotFoundException,ParseException,ClassNotFoundException{
}*/
/**
* 虽然抛出了比父类更多的异常,但是这些异常是运行时异常,所以不会编译出错,仍旧是重写
*/
public void ceshi9() throws ArithmeticException,NullPointerException,ClassCastException,ArrayStoreException{
}
}
8.8继承下构造方法的执行过程
例题1:
package com.njwb.inherit2;
class Person{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person() {
//super(); //调用Object基类的无参构造
System.out.println("execute Person()");
}
public Person(String name) {
super();
this.name = name;
System.out.println("execute Person(name)");
}
}
/**
* 学生类 继承Person人类
* @author Administrator
*
*/
class Student extends Person{
private String school; //学校
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
public Student() {
System.out.println("execute Student()");
}
public Student(String name,String school) {
super(name);
this.school = school;
System.out.println("execute School(name,school)");
}
}
/**
* 研究生类 继承学生类
* @author Administrator
*
*/
class PostGraduate extends Student{
private String guide; //导师
public String getGuide() {
return guide;
}
public void setGuide(String guide) {
this.guide = guide;
}
public PostGraduate() {
System.out.println("execute PostGraduate()");
}
public PostGraduate(String name,String school,String guide) {
super(name,school);
this.guide = guide;
System.out.println("executePostGraduate(name,school,guide) ");
}
}
@SuppressWarnings("unused")
public class TestStudent {
public static void main(String[] args) {
PostGraduate post = new PostGraduate();
System.out.println("---------------------\n");
PostGraduate post2 = new PostGraduate("张三","北京大学","王教授");
}
}
示例2:
父类的实例成员 --à父类的构造---à子类的实例成员----à子类的构造
class Father{
int num =getNum() ; //实例成员 getNum()虽然是静态方法,但是赋值给num变量,num是实例成员
public static int getNum(){
System.out.println("1父类Father中的getNum()");
return 10;
}
public Father() {
System.out.println("2父类Father的无参构造");
}
}
class Son extends Father{
int num2 = getNum2(); //实例成员
public static int getNum2(){
System.out.println("3子类Son中的getNum2()");
return 20;
}
public Son() {
System.out.println("4子类Son无参构造");
}
}
public class Test2 {
public static void main(String[] args) {
Son son = new Son();
}
}
注意:加载顺序:启动类(java虚拟机启动时,被标明为启动类的类)的static block 最先加载(父类静态成员,静态代码块----子类静态成员,静态代码块---父类实例成员,代码块-----父类构造函数------子类实例成员,代码块---子类构造函数)
8.9 final修饰属性、方法和类
final 修饰成员变量,则成为实例常量
final修饰类,类不能被继承
final修饰成员方法,则该方法不能被子类重写。
8.10常量类
/**
* 将项目中能够用到的常量都存放在一个类里,这个类叫做常量类
* @author Administrator
*
*/
public class Constant {
public final String GENDER="男";
public final double PI=3.14;
public final int MAX_COUNT=100;
}
8.11作业
见《封装和继承上机实践》
网吧管理系统
User用户类,包含用户名、密码等相关属性
UserManager用户管理类,有一个存放用户的数组,数组的每一个元素是一个User类
方法包括添加用户、删除用户、编辑用户、查询所有用户
UserManagerSystem测试类,包含main方法,调用UserManager用户管理类的增删改查。