[零基础学JAVA]Java SE面向对象部分-13.面向对象高级(01)-阿里云开发者社区

开发者社区> 开发与运维> 正文

[零基础学JAVA]Java SE面向对象部分-13.面向对象高级(01)

简介:
本季目标:
image
本季讲解了JAVA中继承产生的原因及继承的基本实现,之后本季又重点阐述了JAVA中对于继承实现的各种限制,包括子类对象的实例化过程,方法的覆写、super关键字的使用等。
 
类的继承
image
子承父业,如果孩子不是个败家子,至少可以保持家业不败,如果孩子上进,则家业肯定越来越兴盛。
观察以下一种情况: 
· Person类:name属性、age属性、setter、getter 
· Student类:name属性、age属性、school属性、setter、getter
Student是一个人吧?Student 与Person类相比,发现只多了一个School属性。如果定义成两个类肯定是浪费的,至少Student类中的name 和age是无用的。我们可以把Student类作为Person类来处理。
class Person    
{    
        String name;    
        int age;    
        public String getInfo()    
        {    
                return "姓名:"+this.name+",年龄:"+this.age;    
        }    
};    
//Student类与Person类相比是扩充了Person类的功能,所以此处继承即可    
class Student extends Person    
{    
        // 如果此处任何内容都不写,则至少应该与Person类的内容一致    
};    
public class Demo01    
{    
        public static void main(String args[])    
        {    
                // 使用子类对象    
                Student s = new Student() ;    
                // 以下的全部操作都是在Person类中定义的    
                s.name = "王乾" ;    
                s.age = 27 ;    
                System.out.println(s.getInfo()) ;    
        }    
};
哪怕子类没有增加任何新的东西,则至少也可以从父类继承来很多的内容。下面显示的就是Student类继承Person类调用getInfo()方法。
image
那么子类实际上也可以扩展父类的功能,此时只需要像之前那样直接在子类中定义即可我们看下面Demo02。
class Person    
{    
        String name;    
        int age;    
        public String getInfo()    
        {    
                return "姓名:"+this.name+",年龄:"+this.age;    
        }    
};    
//Student类与Person类相比是扩充了Person类的功能,所以此处继承即可    
class Student extends Person    
{    
        // 如果此处任何内容都不写,则至少应该与Person类的内容一致    
        String school;    
};    
public class Demo02    
{    
        public static void main(String args[])    
        {    
                // 使用子类对象    
                Student s = new Student() ;    
                // 以下的全部操作都是在Person类中定义的    
                s.name = "王乾" ;    
                s.age = 27 ;    
                //在Student类中增加的school属性    
                s.school = "江南大学" ;    
                System.out.println(s.getInfo()+",学校:"+s.school) ;    
        }    
};
我们来验证一下哈,确实已经扩展了类滴功能哈,这就是继承的好处哈~
image
继承的规定
image
现实生活,一个孩子只能有一个亲爸,一个子类只能有一个父类。而不能有多个父类(实际上此概念是从C++来滴,C++允许子类有多个父类哈~~~)。但是一个父类可以有多个子类哈~~~
继承的规定
image
image
子类继承的时候是直接继承了父类中的非私有属性和非私有方法,而隐含的继承了父类中的私有属性(子类通过接口可以访问父类的私有属性和私有方法)。 
老头子 => 资产给孩子了 =>孩子可以通过老头子的钥匙|打开保险柜哈~
               => 情书(保密的,私有的) => 保险柜
class Person    
{    
        private String name;    
        private int age;    
        //私有属性要加入setter和getter操作就可以访问了哈~    
        public void setName(String name)    
        {    
                this.name = name ;    
        }    
        public void setAge(int age)    
        {    
                this.age = age ;    
        }    
        public String getName()    
        {    
                return this.name;    
        }    
        public int getAge()    
        {    
                return this.age;    
        }    
        public String getInfo()    
        {    
                return "姓名:"+this.name+",年龄:"+this.age;    
        }    
};    
//Student类与Person类相比是扩充了Person类的功能,所以此处继承即可    
class Student extends Person    
{    
        // 如果此处任何内容都不写,则至少应该与Person类的内容一致    
        String school;    
        public void fun()    
        {    
                setName("王乾");    
                setAge(27);    
        }    
        public void print(){    
                System.out.println(getInfo()+",学校:"+school);    
        }    
};    
public class Demo03    
{    
        public static void main(String args[])    
        {    
                // 使用子类对象    
                Student s = new Student() ;    

        }    
}; 

我们发现没有报错哈,通过setter和getter方法,子类就可以继承父类的私有属性name和age
image
私有属性可以在子类中直接通过setter和getter方法取得,所以对于私有的内容实际上应该是提供操作的出口的。
子类对象实例化的过程
image
我们来验证一下是否子类对象在实例化时先调用父类的无参构造方法,然后再调用子类的构造方法,我们来看Demo04
class Person    

        private String name; 
        private int age; 
        public Person() 
        { 
                System.out.println("*****父类的构造方法*****"); 
        } 
        //私有属性要加入setter和getter操作就可以访问了哈~ 
        public void setName(String name) 
        { 
                this.name = name ; 
        } 
        public void setAge(int age) 
        { 
                this.age = age ; 
        } 
        public String getName() 
        { 
                return this.name; 
        } 
        public int getAge() 
        { 
                return this.age; 
        } 
        public String getInfo() 
        { 
                return "姓名:"+this.name+",年龄:"+this.age; 
        } 
}; 
//Student类与Person类相比是扩充了Person类的功能,所以此处继承即可 
class Student extends Person 

        public Student() 
        { 
                System.out.println("*****子类的构造方法*****"); 
        } 
        // 如果此处任何内容都不写,则至少应该与Person类的内容一致 
        String school; 
        public void fun() 
        { 
                setName("王乾"); 
                setAge(27); 
        } 
        public void print(){ 
                System.out.println(getInfo()+",学校:"+school); 
        } 
}; 
public class Demo04 

        public static void main(String args[]) 
        { 
                // 使用子类对象 
                Student s = new Student() ;    

        } 
}; 

我们看到Student s = new Student() ; 实例化对象时确实如此哈~~~
image
做法的原因: 
假设张三是李四的孩子,你说:如果现在连李四都没有,能有张三吗?
实际上子类在继承父类之前,必须确保父类所有东西都初始化完毕了,这样在子类的构造方法中就隐含了一个:super()的代码,此代码就表示调用父类中的构造方法。Super是一个关键字,在国外父类也称为超类(Superclass),所以把访问父类的操作称为super
我们在子类中加入代码super()来验证下
class Person    

        private String name; 
        private int age; 
        public Person() 
        { 
                System.out.println("*****父类的构造方法*****"); 
        } 
        //私有属性要加入setter和getter操作就可以访问了哈~ 
        public void setName(String name) 
        { 
                this.name = name ; 
        } 
        public void setAge(int age) 
        { 
                this.age = age ; 
        } 
        public String getName() 
        { 
                return this.name; 
        } 
        public int getAge() 
        { 
                return this.age; 
        } 
        public String getInfo() 
        { 
                return "姓名:"+this.name+",年龄:"+this.age; 
        } 
}; 
//Student类与Person类相比是扩充了Person类的功能,所以此处继承即可 
class Student extends Person 

        public Student() 
        { 
                super(); 
                System.out.println("*****子类的构造方法*****"); 
        } 
        // 如果此处任何内容都不写,则至少应该与Person类的内容一致 
        String school; 
        public void fun() 
        { 
                setName("王乾"); 
                setAge(27); 
        } 
        public void print(){ 
                System.out.println(getInfo()+",学校:"+school); 
        } 
}; 
public class Demo04 

        public static void main(String args[]) 
        { 
                // 使用子类对象 
                Student s = new Student() ;    

        } 
}; 

验证效果没有区别哈~
image
现在我们希望在调用Person类的构造方法时可以直接为属性初始化,我们来看Demo05
class Person    

        private String name; 
        private int age; 
        //希望在调用Person类的构造方法时可以直接为属性初始化 
        public Person(String name,int age) 
        { 
                System.out.println("*****父类的构造方法*****"); 
                this.setName(name); 
                this.setAge(age); 
        } 
        //私有属性要加入setter和getter操作就可以访问了哈~ 
        public void setName(String name) 
        { 
                this.name = name ; 
        } 
        public void setAge(int age) 
        { 
                this.age = age ; 
        } 
        public String getName() 
        { 
                return this.name; 
        } 
        public int getAge() 
        { 
                return this.age; 
        } 
        public String getInfo() 
        { 
                return "姓名:"+this.name+",年龄:"+this.age; 
        } 
}; 
//Student类与Person类相比是扩充了Person类的功能,所以此处继承即可 
class Student extends Person 

        public Student() 
        { 
                super(); 
                System.out.println("*****子类的构造方法*****"); 
        } 
        // 如果此处任何内容都不写,则至少应该与Person类的内容一致 
        String school; 
        public void fun() 
        { 
                setName("王乾"); 
                setAge(27); 
        } 
        public void print(){ 
                System.out.println(getInfo()+",学校:"+school); 
        } 
}; 
public class Demo05 

        public static void main(String args[]) 
        { 
                // 使用子类对象 
                Student s = new Student() ;    

        } 
}; 

这样修改后我们父类中就没无参构造方法了哈~现在我们运行下看下效果哈~提示找不到无参Person()方法。
image
这样隐含的代码super();写与不写没两样了哈~此时最好可以明确的指出要调用父类中的有两个参数的构造方法,我们直接指明调用父类中有两个参数的构造方法,super(name,age)
class Person    

        private String name; 
        private int age; 
        //希望在调用Person类的构造方法时可以直接为属性初始化 
        public Person(String name,int age) 
        { 
                System.out.println("*****父类的构造方法*****"); 
                this.setName(name); 
                this.setAge(age); 
        } 
        //私有属性要加入setter和getter操作就可以访问了哈~ 
        public void setName(String name) 
        { 
                this.name = name ; 
        } 
        public void setAge(int age) 
        { 
                this.age = age ; 
        } 
        public String getName() 
        { 
                return this.name; 
        } 
        public int getAge() 
        { 
                return this.age; 
        } 
        public String getInfo() 
        { 
                return "姓名:"+this.name+",年龄:"+this.age; 
        } 
}; 
//Student类与Person类相比是扩充了Person类的功能,所以此处继承即可 
class Student extends Person 

        public Student(String name,int age,String school) 
        { 
                //这样隐含的代码super();写与不写没两样了哈~ 
                //super(); 
                //此时最好可以明确的指出要调用父类中的有两个参数的构造方法 
                //直接指明调用父类中有两个参数的构造方法。 
                super(name,age); 
                this.setSchool(school); 
                System.out.println("*****子类的构造方法*****"); 
        } 
        // 如果此处任何内容都不写,则至少应该与Person类的内容一致 
        private String school; 
        public void setSchool(String school) 
        { 
                this.school = school; 
        } 
        public String getSchool() 
        { 
                return this.school; 
        } 
        public void print(){ 
                System.out.println(getInfo()+",学校:"+school); 
        } 
}; 
public class Demo05 

        public static void main(String args[]) 
        { 
                // 使用子类对象 
                Student s = new Student("王乾",27,"江南大学") ; 
                s.print(); 
        } 
};
看下效果:
image
以上代码是完成功能了,但是还有一点的不足,所有的操作代码最好交给调用处完成哈,现在在Student类中print()方法是直接输出了哈,我们可不可以不要直接输出哈,在Person类中得到信息的方法是getInfo()方法,Student()类也是个人哈,所以我们要把getInfo()方法也写在Student()类中,这样一来,Person类中的getInfo()方法在Student类中重新定义,这种方式叫作父类中的方法在子类中重新复写了。我们接着往下看哈~
方法的复写
image
如果在子类中重新写了父类一样的方法,我们叫这种方式为复写也可以叫覆写
复写之后有什么特点呢?我们来看Demo06哈~
class A    

        public void fun1() 
        { 
                System.out.println("Hello"); 
        } 
}; 
class B extends A 

        // 此方法覆写父类中的fun1()方法 
        public void fun1() 
        { 
                System.out.println("World~~~"); 
        } 
}; 
public class Demo06 

        public static void main(String args[]) 
        { 
                B b = new B(); 
                b.fun1(); 
        } 
};
现在我们来看下b.fun1()是调用哪个方法哈~效果是b.fun1()是调用了子类B的fun1()方法后输出"World~~~"哈。
image
注意点:被子类覆写的方法不能拥有比父类更严格的访问权限。
访问权限: 
· public > default(什么都不写) > private
如果我们把A类中fun1()方法的访问权限修改成default,则此时B类中fun1()方法的访问权限比A类大,程序应该没有问题,我们验证下哈~
class A    

        void fun1() 
        { 
                System.out.println("Hello"); 
        } 
}; 
class B extends A 

        // 此方法覆写父类中的fun1()方法 
        public void fun1() 
        { 
                System.out.println("World~~~"); 
        } 
}; 
public class Demo06 

        public static void main(String args[]) 
        { 
                B b = new B(); 
                b.fun1(); 
        } 
};
验证效果正常,可以正常输出
如果我们把B类中fun1()方法的访问权限修改成default,则此时B类中fun1()方法的访问权限比A类小,则此时拥有比父类更严格的访问权限,程序应该异常,我们验证下效果哈~
class A    

        public void fun1() 
        { 
                System.out.println("Hello"); 
        } 
}; 
class B extends A 

        // 此方法覆写父类中的fun1()方法 
        void fun1() 
        { 
                System.out.println("World~~~"); 
        } 
}; 
public class Demo06 

        public static void main(String args[]) 
        { 
                B b = new B(); 
                b.fun1(); 
        } 
};
现在程序编译时就报错哈~
image
还有个特殊情况,如果父类的fun1()方法访问权限为private,子类的fun1()方法访问权限为default,明确说:这不叫覆写,因为父类中的fun1()方法子类看不见哈~~~
class A    

        private void fun1() 
        { 
                System.out.println("Hello"); 
        } 
        public void fun() 
        { 
                fun1(); 
        } 
}; 
class B extends A 

        //此时相当于子类重新定义了一个方法fun1() 
        void fun1() 
        { 
                System.out.println("World~~~"); 
        } 
}; 
public class Demo06 

        public static void main(String args[]) 
        { 
                B b = new B(); 
                b.fun(); 
        } 
};
验证下效果,现在输出的是字符串Hello哈~
image
明白了复写的概念和特点,我们就重新利用复写方法修改Demo05
class Person    

        private String name; 
        private int age; 
        //希望在调用Person类的构造方法时可以直接为属性初始化 
        public Person(String name,int age) 
        { 
                System.out.println("*****父类的构造方法*****"); 
                this.setName(name); 
                this.setAge(age); 
        } 
        //私有属性要加入setter和getter操作就可以访问了哈~ 
        public void setName(String name) 
        { 
                this.name = name ; 
        } 
        public void setAge(int age) 
        { 
                this.age = age ; 
        } 
        public String getName() 
        { 
                return this.name; 
        } 
        public int getAge() 
        { 
                return this.age; 
        } 
        public String getInfo() 
        { 
                return "姓名:"+this.name+",年龄:"+this.age; 
        } 
}; 
//Student类与Person类相比是扩充了Person类的功能,所以此处继承即可 
class Student extends Person 

        public Student(String name,int age,String school) 
        { 
                //这样隐含的代码super();写与不写没两样了哈~ 
                //super(); 
                //此时最好可以明确的指出要调用父类中的有两个参数的构造方法 
                //直接指明调用父类中有两个参数的构造方法。 
                super(name,age); 
                this.setSchool(school); 
                System.out.println("*****子类的构造方法*****"); 
        } 
        // 如果此处任何内容都不写,则至少应该与Person类的内容一致 
        private String school; 
        public void setSchool(String school) 
        { 
                this.school = school; 
        } 
        public String getSchool() 
        { 
                return this.school; 
        } 
        // 子类覆写了父类中的getInfo()方法 
        public String getInfo() 
        { 
                return "姓名:"+this.getName()+",年龄:"+this.getAge()+",学校:"+this.school; 
        } 
}; 
public class Demo07 

        public static void main(String args[]) 
        { 
                // 使用子类对象 
                Student s = new Student("王乾",27,"江南大学") ; 
                System.out.println(s.getInfo()); 
        } 
};
现在我们验证下是否调用了复写的getInfo()方法,确实出现了哈~
image
我们还可以精简下,明确的指定调用父类中的getInfo()方法哈~
class Person    

        private String name; 
        private int age; 
        //希望在调用Person类的构造方法时可以直接为属性初始化 
        public Person(String name,int age) 
        { 
                System.out.println("*****父类的构造方法*****"); 
                this.setName(name); 
                this.setAge(age); 
        } 
        //私有属性要加入setter和getter操作就可以访问了哈~ 
        public void setName(String name) 
        { 
                this.name = name ; 
        } 
        public void setAge(int age) 
        { 
                this.age = age ; 
        } 
        public String getName() 
        { 
                return this.name; 
        } 
        public int getAge() 
        { 
                return this.age; 
        } 
        public String getInfo() 
        { 
                return "姓名:"+this.name+",年龄:"+this.age; 
        } 
}; 
//Student类与Person类相比是扩充了Person类的功能,所以此处继承即可 
class Student extends Person 

        public Student(String name,int age,String school) 
        { 
                //这样隐含的代码super();写与不写没两样了哈~ 
                //super(); 
                //此时最好可以明确的指出要调用父类中的有两个参数的构造方法 
                //直接指明调用父类中有两个参数的构造方法。 
                super(name,age); 
                this.setSchool(school); 
                System.out.println("*****子类的构造方法*****"); 
        } 
        // 如果此处任何内容都不写,则至少应该与Person类的内容一致 
        private String school; 
        public void setSchool(String school) 
        { 
                this.school = school; 
        } 
        public String getSchool() 
        { 
                return this.school; 
        } 
        // 子类覆写了父类中的getInfo()方法 
        public String getInfo() 
        { 
                return super.getInfo()+",学校:"+this.school; 
        } 
}; 
public class Demo07 

        public static void main(String args[]) 
        { 
                // 使用子类对象 
                Student s = new Student("王乾",27,"江南大学") ; 
                System.out.println(s.getInfo()); 
        } 
};
验证正常,在最大程度上达到了程序滴复写哈~~~
image
总结
1、继承的概念及使用 
2、子类对象的实例化过程 
3、方法的覆写 
4、super关键字
#######################################################################






本文转自redking51CTO博客,原文链接:http://blog.51cto.com/redking/125417,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章