------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
3.1 继承的概述
继承是面向对象的一个重要特征。当多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继那个类即可。这时,多个类可以称为子类,单独的这个类称为父类或者超类。
继承的好处:
1、提高了代码的复用性
2、提高了代码的维护性
3、让类与类之间产生了关系,是多态的前提
继承的坏处:
类与类产生了关系,其实也是继承的一个弊端,因为类的耦合性增强了。
继承的用法:Java中使用extends关键字来表示子类继承父类,使用格式:class 子类名 extends 父类名。示例:
class Person { public void eat() { System.out.println("吃饭"); } public void sleep() { System.out.println("睡觉"); } } class Student extends Person {} class Teacher extends Person {} class ExtendsDemo { public static void main(String[] args) { Student s = new Student(); s.eat(); s.sleep(); System.out.println("-------------"); Teacher t = new Teacher(); t.eat(); t.sleep(); } }
运行结果:
3.2 继承的特点
1、Java只支持单继承,不支持多继承 。
2、Java支持多层继承(继承体系)。例如:class Son extends Father { }; class Father extends GrandFather { }
3.3 继承的注意事项
1、子类只能继承父类所有非私有的成员(成员方法和成员变量)
2、子类不能继承父类的构造方法,但是可以通过super关键字去访问父类构造方法。
3、不要为了部分功能而去继承
3.4 继承中子父类成员的关系
3.4.1 super关键字
this和super的用法很相似,this代表本类对象的引用,super代表父类的内存空间的标识(可以理解为父类引用,可以操作父类的成员)。当本类的成员变量和局部变量同名用this来区分,当子父类中的成员变量同名用super来区分。
this调用的是当前类中的成员变量、构造方法和成员方法,super调用的是父类中的成员变量、构造方法和成员方法,并且调用构造方法时,该语句都必须是构造方法的第一句。
3.4.2 子父类中变量的特点
如果子类中出现非私有的同名成员变量时,子类要访问本类中的成员变量,用this。子类要访问父类中的同名成员变量,用super。this代表的是本类对象的引用。super代表的是父类对象的引用。示例:
class Father { public int num = 10; } class Son extends Father { public int num = 20; public void show() { int num = 30; System.out.println(num); System.out.println(this.num); System.out.println(super.num); } } class ExtendsDemo5 { public static void main(String[] args) { Son s = new Son(); s.show(); } }
运行结果:
3.4.3 子父类中构造方法的特点
子类中所有的构造方法默认都会访问父类中空参数的构造方法,也就是说,在对子类对象进行初始化时,会先运行父类的构造方法。因为子类的构造函数默认第一行会有一条隐式的语句super(),super()会访问父类中空参数的构造函数,而且子类中所有的构造函数默认第一行都是super()。示例:
class Father { int age; public Father() { System.out.println("Father的无参构造方法"); } public Father(String name) { System.out.println("Father的带参构造方法"); } } class Son extends Father { public Son() { //super(); 系统会默认在这个地方加上super(); System.out.println("Son的无参构造方法"); } public Son(String name) { //super(); 系统会默认在这个地方加上super(); System.out.println("Son的带参构造方法"); } } class ExtendsDemo6 { public static void main(String[] args) { Son s = new Son(); System.out.println("------------"); Son s2 = new Son("锤子"); } }
运行结果:
如果父类中没有无参的构造方法时,该怎么解决呢?
如果父类中没有无参的构造方法,可以通过使用super关键字去显示的调用父类的带参构造方法;也可以通过this关键字去调用本类的其他构造方法,但是必须保证子类中有一个构造方法访问了父类的构造方法。因为在子类对象进行初始化时,必须先初始化父类。示例:
class Father { public Father(String name) { System.out.println("Father的带参构造方法"); } } class Son extends Father { public Son() { super("名字"); System.out.println("Son的无参构造方法"); } public Son(String name) { this(); System.out.println("Son的带参构造方法---"+name); } } class ExtendsDemo7 { public static void main(String[] args) { Son s = new Son(); System.out.println("----------------"); Son ss = new Son("锤子"); } }
运行结果:
3.4.4 子父类中成员方法的特点
建立一个子类的对象,该对象可以分别调用父类和子类中方法。当子父类中的方法同名时,子类的对象调用该方法时,会执行子类中的该方法,而不是父类中的该方法,这种情况叫方法的覆盖,也叫重写(override)。
1、怎么判断是否为重写(override)呢?
1)、具有父子关系的两个类。
2)、父类和子类各有一个函数,这另个函数的的定义(返回值类型、函数名、参数列表)完全相同。
class Phone { public void call(String name) { System.out.println("给"+name+"打电话"); } } class NewPhone extends Phone { public void call(String name) { //System.out.println("给"+name+"打电话"); super.call(name); System.out.println("可以边打电话边看图片了"); } } class ExtendsDemo9 { public static void main(String[] args) { NewPhone np = new NewPhone(); np.call("锤子哥"); } }
运行结果:
2、方法重写(override)的注意事项
1)、父类中私有方法不能被重写,因为父类私有方法子类根本就无法继承
2)、子类重写父类方法时,访问权限不能低于父类该方法的权限,最好一致。
3)、父类静态方法,子类也必须通过静态方法进行重写
3、比较方法重载(overload)和重写(override)
1)、重载的两个函数存在于同一个类中;重写的两个函数存在于具有父子关系的两个类中。
2)、重载的函数名相同,参数列表(参数类型和参数数量)不同;重写的返回值类型、函数名和参数列表都相同。
示例:需求:创建一个动物的类,成员变量有name,age,color,构造方法包括有参和无参两种,成员方法有getXxx(),setXxx(),eat()。然后让Cat继承它,并复写eat()方法。
class Animal{ private String name; private int age; private String color; public Animal(){} public Animal(String name,int age,String color){ this.name = name; this.age = age; this.color = color; } public void eat(){ System.out.println("别睡觉了,该吃饭了"); } 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; } public void setColor(String color){ this.color = color; } public String getColor(){ return color; } } class Cat extends Animal{ public Cat(){} public Cat(String name,int age,String color){ super(name,age,color); } public void eat(){ System.out.println("我在吃东西"); } } class ExtendsTest5{ public static void main(String[] args){ //方式1 Cat c1 = new Cat(); c1.setName("小花"); c1.setAge(2); c1.setColor("黑白"); System.out.println(c1.getName()+"---"+c1.getAge()+"---"+c1.getColor()); c1.eat(); //方式2 Cat c2 = new Cat("小花",2,"黑白"); System.out.println(c1.getName()+"---"+c1.getAge()+"---"+c1.getColor()); c2.eat(); } }
运行结果:
3.5 子类实例化过程
以Zi z = new Fu(20)为例
1、把Zi.class文件加载到内存,由于Zi类继承Fu类,所以也将Fu.class文件加载到内存。
2、在栈内存为s开辟空间。
3、在堆内存为学生对象开辟空间。
4、给Zi类的成员变量age进行默认初始化(int类型的变量默认值为0)。
5、给Zi类的age成员变量在父类中进行显示初始化(int age = 1)。
6、通过父类的构造方法给age成员变量进行初始化(int age = 2)。
7、在子类中进行显示初始化(int age = 3)。
8、通过子类的构造方法给age进行初始化(int age = 4)。
9、对象构造完毕,把地址赋值给z变量。
验证:
class Fu{ int age = 1; public Fu(){ System.out.println(age); age = 2; System.out.println(age); } } class Zi extends Fu{ int age = 3; public Zi(){ System.out.println(age); age = 4; System.out.println(age); } } class Test{ public static void main(String[] args){ Zi z = new Zi(); } }
运行结果:
3.6 final关键字
1、final翻译成汉语就是“最终”的意思,可以修饰类、变量和函数。
被final修饰的类不可以被继承。
被final修饰的方法不可以被复写。
被final修饰的变量(成员变量和局部变量)是一个常量,且只能被赋值一次。
内部类只能访问被final修饰的局部变量。
2、常量的书写规范
所有字母大写。
如果由多个单词组成,单词间通过”_“连接。
3、final修饰局部变量
/* 面试题:final修饰局部变量的问题 基本类型:基本类型的值不能发生改变。 引用类型:引用类型的地址值不能发生改变,但是,该对象的堆内存的值是可以改变的。 */ class Student { int age = 10; } class FinalTest { public static void main(String[] args) { //局部变量是基本数据类型 int x = 10; x = 100; System.out.println(x); final int y = 10; //无法为最终变量y分配值 //y = 100; System.out.println(y); System.out.println("--------------"); //局部变量是引用数据类型 Student s = new Student(); System.out.println(s.age); s.age = 100; System.out.println(s.age); System.out.println("--------------"); final Student ss = new Student(); System.out.println(ss.age); ss.age = 100;//对象引用所指的堆内存中的值是可以改变的 System.out.println(ss.age); //重新分配内存空间 //无法为最终变量ss分配值 //ss = new Student(); } }
运行结果:
结论:基本类型:基本类型的值不能发生改变。
引用类型:引用类型的地址值不能发生改变,但是,该对象的堆内存的值是可以改变的。
版权声明:本文为博主原创文章,未经博主允许不得转载。