zoukankan      html  css  js  c++  java
  • Java基础之:OOP——继承

    Java基础之:OOP——继承

    面向对象编程OOP(Object Oriented Programming)的三大特征之二:继承

    首先看一个案例,分别创建小学生与研究生类,输出他们的信息:

    小学生类:

    public class Pupil { //小学生类
    ​
        String name;
        double score;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public double getScore() {
            return score;
        }
        public void setScore(double score) {
            this.score = score;
        }
        public void testing() {
            System.out.println("小学生考语文...");
        }
        public void showScore() {
            System.out.println("学生名" + name + " 成绩=" + score);
        }
    }
    研究生类:
    public class Graduate { //研究生
    ​
        String name;
        double score;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public double getScore() {
            return score;
        }
        public void setScore(double score) {
            this.score = score;
        }
        public void testing() {
            System.out.println("研究生考的是微积分...");
        }
        public void showScore() {
            System.out.println("学生名 " + name + " 成绩=" + score);
        }
    }

    可以看到 在小学生类与研究生类中 ,有大量的内容是重复的,只有 testing() 方法不同。所以我们可以将它们两个类中共同的属性或方法抽象出来创建一个Student类,再继承Student。

    继承介绍

    继承可以解决代码的复用性问题,让编程更解决我们人类的思维逻辑,多个类出现相同的属性/方法时,可以将这些属性/方法抽象出来,放在一个父类中来定义,所有的子类都不需要再定义这些属性/方法,只需要继承(extends)父类即可。

    继承语法

    class 子类名 extends 父类名 {}

    说明:

    1) 子类就会自动拥有父类定义的属性和方法

    2) 父类又叫 超类,基类。

    3) 子类又叫派生类。

    简单案例

    对上面的小学生类和研究生类进行改进。

    Student类:

    public class Student { //父类
    ​
        String name;
        double score;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public double getScore() {
            return score;
        }
        public void setScore(double score) {
            this.score = score;
        }
        public void showScore() {
            System.out.println("学生名 " + name + " 成绩=" + score);
        }
    }

    Pupil类:

    public class Pupil extends Student { //小学生类, 子类
    ​
        public void testing() {
            System.out.println("小学生考语文...");
        }
        
    }
    ​ 

    Graduate类:

    public class Graduate extends Student{ //研究生 子类
    ​
        public void testing() {
            System.out.println("研究生考的是微积分...");
        }
        
    }

    继承优点

    1. 代码复用性提高了。

    2. 代码的扩展性和维护性提高了。

    继承使用细节与注意事项

    1. 子类继承了所有的属性和方法,只是私有的属性不能直接访问,需要通过公共方法进行访问。(封装的体现)

    2. 子类没有继承父类的构造器,但在子类的构造器中必须调用父类的构造器,完成父类的初始化。(例:肯定是先有爷爷再有爸爸最后有儿子)

    3. 当创建子类时,不管你使用子类的哪个构造方法,默认情况下总会去调用父类的无参构造函数,如果父类没有提供无参构造函数,则必须在子类的构造函数中用 super 去指定使用父类的哪个构造函数完成对父类的初始化工作,否则,编译不会通过。

    4. 如果希望指定调用父类的某个构造方法,需要使用super关键字显式调用。

      1. 无参构造器:super();

      2. 有一个参数:super(参数);

      3. 要注意super在使用时,需要放在方法体的第一句位置。

    5. super() 和 this() 都只能放在构造方法句首,因此这两个方法不能共存在一个方法中

    6. java中所有的类都是Object类的子类

    7. 子类最多只能有一个直接父类,也就是只能继承一个父类。(若需要A类继承B类和C类,则A继承B,B继承C)。

    8. 父类构造器的调用不限于直接父类!将一直往上追溯直到Object类。同样的,若子类调用父类提供的方法,也不限于直接父类。

    简单案例

    import java.util.ArrayList;
    ​
    public class ExtendsDetail {
    ​
        public static void main(String[] args) {
            BB bb = new BB();
            bb.m1();
        }
    }
    ​
    class DD {
        public DD() {
            System.out.println("DD() 被调用");
        }
    }
    ​
    // 子类继承了所有的属性和方法,只是私有的属性不能直接访问,需要通过公共的方法去访问
    class AA extends DD {
        // 属性
        public int n1 = 10;
        protected int n2 = 20;
        int n3 = 30;
        private int n4 = 40;
        
        public int getN4() {
            return n4;
        }
        
    //  public AA() {
    //      System.out.println("AA() 构造器..");
    //  }
         public AA(String name) {
         }
         
         public AA() {
             
         }
         
         public AA(int num) {
             //super();
         }
    }
    ​
    //类的BB继承 ctrl+t
    class BB extends AA { //子类BB 继承 AA
        public void m1() {
            System.out.println(n1 + " " + n2 + " " + n3 + " " /*+ n4 */);
            System.out.println(getN4());
        }
        
        //子类没有继承父类的构造器,但必须调用父类的构造器, 完成父类的初始化.
        //至于调用父类的哪个构造器,无所谓,但是一定要调用一个
        public BB() {
            //默认有一句话 super(), 父类的无参构造器
            
            //如果希望指定去调用父类的某个构造方法,则显示的调用一下
            //super在使用时,需要放在方法体的第一句位置
            //super() 和 this() 都只能放在构造方法句首,因此这两个方法不能共存在一个方法中
            super(10);
            System.out.println("BB() 构造器..");
        }   
    }
     
    

      

    内存分析案例

    public class ClassTest {
        public static void main(String[] args) {
            son son = new son();
        }
    }
    ​
    class Guandpa {
        private String name;
        
        public Guandpa(String name) {
            super();    //指向 Object();
            this.name = name;
        }
    ​
        public Guandpa() {
            //默认存在super();  即使不显式的写出
            this.name = "爷爷";
        }
        
        public  void show() {
            System.out.println("Guandpa:" + name);
        }
    }
    ​
    class father extends Guandpa{
        private String name;
    ​
        public father(String name) {
            super();
            this.name = name;
        }
    ​
        public father() {
            super();// 指向 Guandpa();
            this.name = "父亲";
        }
        
        public void show(){
            System.out.println("father"+name);
        }
        
    }
    ​
    class son extends father{
        private String name;
        
        public son(String name) {
            super();    
            //默认存在super();即使不显式的写出。
            //当我们在father类中 无参构造方法被覆盖时, son子类的构造方法就会报错。
            //因为son子类在构造时 ,会首先调用super(); 
            this.name = name;
        }
    ​
        public son() {
            super();    //指向 father();
            this.name = "儿子";
        }
        
        public void f1() {
            show();
        }
    }
    

      

    super关键字

    super代表父类的引用,用于访问父类的属性、方法、构造器

    基本语法

    1. 访问父类的属性 , 不能访问父类的private属性 [案例] super.属性名;

    2. 访问父类的方法,不能访问父类的private方法

      super.方法名(参数列表);

    3. 访问父类的构造器(只能访问非私有的父类构造器):

        super(参数列表); 构造器的调用只能放在构造器中,且一定在第一行。

    细节说明

    1. 调用父类的构造器 (分工明确, 父类属性由父类初始化,子类的属性由子类初始化)

    2. 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果!

    3. super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类中都有同名的成员,使用super访问遵循就近原则。A->B->C

    简单案例

    public class SuperTest {
    
    	public static void main(String[] args) {
    		BB bb = new BB();
    		bb.m1();
    	}
    
    }
    
    class AA {
    	public int n1 = 10;
    	protected int n2 = 20;
    	int n3 = 30;
    	private int n4 = 40;
    	
    	public void run() {
    		
    	}
    	protected void eat() {
    		
    	}
    	void sleep() {
    		
    	}
    	private void cry() {
    		
    	}
    	
    	public  AA() {
    		
    	}
    	public AA(String name) {
    		
    	}
    }
    
    class BB extends AA{
    	public void m1() {
    		//访问父类的属性 , 不能访问父类的private属性 [案例]    super.属性名
    		//如果子类,和父类不在同一个包 ,默认的属性是否可以访问? 答不能
    		System.out.println(super.n1 + " " + super.n2 + " " + super.n3 /*+ super.n4*/);
    	}
    	
    	//访问父类的方法,不能访问父类的private方法    super.方法名(参数列表);
    	//如果子类,和父类不在同一个包 ,默认的方法是否可以访问? 答不能
    	
    	public void m2() {
    		
    		super.eat();
    		super.run();
    		super.sleep();
    		//super.cry();
    		
    	}
    	
    	//访问父类的构造器(这点前面用过):    super(参数列表);只能放在构造器的第一句,而且只能出现一句
    	//也只能访问 非私有的构造器,如果子类和父类不在同一个包,默认的构造器,也不能使用
    	
    	public BB() {
    //		super();
    		super("hello");
    	}
    }
    

      特别注意:如果子类,和父类不在同一个包 ,默认的属性不可以访问。在上面的案例中可以可以通过super访问AA类中的属性与方法,是因为AA与BB在同一个包下!

    super与this

     

    对于继承的总结:

    继承的本质是建立一种查找关系,就像this关键字中的访问属性和调用方法时一样。

     

     

    继承应用案例1

    为了代码方便阅读这里将各个类写在了一起,但在实际开发中应该保证一个类一个文件,所有类在同一个包下。

    /**
     * 
     * 编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信息
     * 编写PC子类,继承Computer类,添加特有属性【品牌brand】 编写NotePad子类,继承Computer类,添加特有属性【演示color】
     * 编写Test类,在main方法中创建PC和NotePad对象,分别给对象中特有的属性赋值,
     * 以及从Computer类继承的属性赋值,并使用方法并打印输出信息
     *
     */
    public class Test {
        public static void main(String[] args) {
            PC pc = new PC("intel_i9", "16GB", "1TB", "PCbrand");
            NotePad notePad = new NotePad("cpu", "8GB", "128GB", "NotePadcolor");
            
            System.out.println(pc.getDetails());
            System.out.println(notePad.getDetails());
        }
    }
    ​
    //编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信息
    class Computer {
        //private + set & get 方法,体现封装
        private String CPU;
        private String memory;
        private String disk;
        
        public String getCPU() {
            return CPU;
        }
    ​
        public void setCPU(String cPU) {
            CPU = cPU;
        }
    ​
        public String getMemory() {
            return memory;
        }
    ​
        public void setMemory(String memory) {
            this.memory = memory;
        }
    ​
        public String getDisk() {
            return disk;
        }
    ​
        public void setDisk(String disk) {
            this.disk = disk;
        }
    ​
        
        public String getDetails() {
            return "Computer [CPU=" + CPU + ", memory=" + memory + ", disk=" + disk + "]";
        }
    ​
        public Computer(String cPU, String memory, String disk) {
            super();
            CPU = cPU;
            this.memory = memory;
            this.disk = disk;
        }
    ​
        public Computer() {
            super();
        }
        
        
    }
    ​
    //编写PC子类,继承Computer类,添加特有属性【品牌brand】
    class PC extends Computer {
        private String brand;   //private + set & get 方法,体现封装
        
        public String getBrand() {
            return brand;
        }
    ​
        public void setBrand(String brand) {
            this.brand = brand;
        }
    ​
        @Override
        public String getDetails() {
            return super.getDetails() + "
    PC [brand=" + brand + "]";
        }
    ​
        public PC(String cPU, String memory, String disk, String brand) {
            super(cPU, memory, disk);
            this.brand = brand;
        }
        
        
    }
    ​
    //编写NotePad子类,继承Computer类,添加特有属性【演示color】
    class NotePad extends Computer {
        private String color;   //private + set & get 方法,体现封装
        
        public String getColor() {
            return color;
        }
    ​
        public void setColor(String color) {
            this.color = color;
        }
    ​
        @Override
        public String getDetails() {
            return super.getDetails() + "
    NotePad [color=" + color + "]";
        }
    ​
        public NotePad(String cPU, String memory, String disk, String color) {
            super(cPU, memory, disk);
            this.color = color;
        }
        
    }

    继承应用案例2

    为了代码方便阅读这里将各个类写在了一起,但在实际开发中应该保证一个类一个文件,所有类在同一个包下。

    /**
     * 定义一个ManKind类: 成员变量int sex和int salary; 要求加上两个参数有参构造 
     * 方法void manOrWomen():根据sex的值显示“man”(sex==1)或者“women”(sex==0); 
     * 方法void employeed():根据salary的值显示“no job”(salary==0)或者“ job”(salary!=0)。 
     * 定义类Kids继承ManKind,并包括成员变量int yearsOld; 方法printAge()打印yearsOld的值。 
     * 在Kids类的main方法中实例化Kids的对象someKid,用该对象访问其父类的成员变量及方法。
     */
    public class HomeWork {
    ​
        public static void main(String[] args) {
            Kids kids = new Kids(2,-100,18);
            
            kids.manOrWomen();
            kids.employeed();
            kids.printAge();
    ​
        }
    ​
    }
    ​
    class ManKind {
        private int sex;
        private int salary;
     
        public int getSex() {
            return sex;
        }
    ​
        public void setSex(int sex) {
            this.sex = sex;
        }
    ​
        public int getSalary() {
            return salary;
        }
    ​
        public void setSalary(int salary) {
            this.salary = salary;
        }
    ​
        // 方法void manOrWomen():根据sex的值显示“man”(sex==1)或者“women”(sex==0);
        public void manOrWomen() {
            if (sex == 1) {
                System.out.println("man");
            } else{
                System.out.println("women");
            }
        }
    ​
        // 方法void employeed():根据salary的值显示“no job”(salary==0)或者“ job”(salary!=0)。
        public void employeed() {
            if (salary == 0) {
                System.out.println("no job!");
            } else {
                System.out.println("job!");
            }
        }
    ​
        public ManKind(int sex, int salary) {
            super();
            if(sex != 0 && sex !=1) {
                System.out.println("性别输入内容错误,0表示女,1表示男!");
                this.sex = 0;
            }else {
                this.sex = sex;
            }
            if (salary < 0) {
                System.out.println("输入工资错误,默认值1000");
                this.salary = 1000;
            } else {
                this.salary = salary;
            }
        }
    ​
        public ManKind() {
            super();
        }
    ​
    }
    ​
    //定义类Kids继承ManKind,并包括成员变量int yearsOld; 方法printAge()打印yearsOld的值。 
    class Kids extends ManKind{
        private int yearsOld;
        
        public int getYearsOld() {
            return yearsOld;
        }
    ​
        public void setYearsOld(int yearsOld) {
            this.yearsOld = yearsOld;
        }
    ​
        public void printAge() {
            System.out.println("年龄:"+yearsOld);
        }
    ​
        public Kids(int sex, int salary, int yearsOld) {
            super(sex, salary);
            this.yearsOld = yearsOld;
        }
        
        public static void main(String[] args) {    
            //在另外一个非public类中 也有一个main时,点击Run As 会询问执行哪一个main
            Kids kids = new Kids(1,2000,18);
            kids.setSex(0);
            kids.manOrWomen();
            kids.employeed();
            kids.printAge();
        }
    }
    

      

     

  • 相关阅读:
    override new virtual 的比较
    c#页面无内容解决方案
    插入排序
    排序算法(转)
    treenode遍历文件夹
    案例篇(1)
    索引器(转)
    迭代器的实现
    抽象类和接口的区别
    索引器与迭代器,属性的区别
  • 原文地址:https://www.cnblogs.com/SongHai/p/14075033.html
Copyright © 2011-2022 走看看