------<ahref="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
继承
我们在现实生活中会发现,事物和事物是有关联的,比如说不管狮子老虎,它们都是猫科动物,都具备一些猫科动物的特性,而不管猫科还是犬科,它们都是哺乳动物。我们可以像这样,不断向上抽取出事物之间共性的内容,这些共性的内容就可以理解为父类,或者基类,超类,而父类都有其对应的子类(们)。子类继承父类,写法为“子 extends 父”而我们通过继承让类与类之间产生了关系,这也是多态的前提。
子类继承父类,就会获得父类中所有的变量和方法,而子类要存在只需要加上自己特有的变量和方法即可。
注意:千万不要为了获取其他类的功能,简化代码而继承,必须是类与类有所属关系才可以继承。
判断是否有所属关系:父类里面是否所有功能子类都应该具备。
JAVA语言当中只支持单继承,不支持多继承(不是很严格),因为多继承容易带来安全隐患,比如说当多个父类中定义了相同功能,但功能内容不同时,子类对象不确定要运行哪一个。
但JAVA保留这种机制,并用另一种体现形式来完成表示,叫做多实现。实际上对C++的多继承是给改良了。
JAVA支持多层继承。
使用一个继承体系中的功能的步骤:
- 先查阅体系父类的描述,因为父类中定义的是该体系中共性内容。
- 通过了解共性功能,就可以知道该体系的基本功能,此时这个体系已经基本可以使用了。
- 在具体调用时,要创建最底层子类的对象。原因有二,一是因为有可能父类不能创建对象。(如抽象类和接口);二是创建子类对象可以使用更多功能,包括基本的也包括特有的。
简单一句话:查阅父类功能,创建子类对象使用功能。
super关键字
子类对象的成员前面默认有个this.,超类的内容用super.xxx表示。子父类对象如果同名,优先使用子类的thix.xxx 子类调用。有一种情况,如果父类中有num = 5;子类中没有定义num 此时如果调用this.num还是=5,因为子类继承了父类。而如果父类没有,子类有num = 5,这时如果调用super.num会报错,因为内存中不存在,也不会从this.num里获取。
另外,子类除了继承父类以外还可以重写父类的方法。
子类中的函数会覆盖父类中一模一样的函数(函数名,返回值,参数类型),这就叫重写,父类的方法其实还在内存中,只不过没有运行,运行的是覆盖了父类同样方法的子类方法。而重载的意思截然相反,重载是两个或两个以上函数同名且返回结果相同,但是传入的参数的种类和数量不一样,具体调用哪个函数依照传入参数的种类和数量决定。
重写:保留父类功能定义,并重写功能内容。
重写多用于实际开发,因为修改原先代码是灾难,还是覆盖为好。
如果同样的函数,子类只是添加少量新内容,用重写也可以。为了省略已经在父类里面定义好的内容,可以在相同函数内部调用父类函数。代码如图:
覆盖注意事项:
- 子类覆盖父类,必须保证子类权限大于等于父类权限才可以覆盖,否则编译失败。
- 静态只能覆盖静态。
子类的实例化过程
在对子类对象进行初始化时,父类的构造函数也会执行,那是因为子类的构造函数默认第一行有一条隐式的语句super();它会访问父类中空参数的构造函数。
为什么子类一定要访问父类的构造函数?
因为父类中的数据子类可以直接获取。所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的。
如果子类要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。另外子类对父类构造函数的访问如果要放必须放在子类构造函数第一行。子类里的构造函数第一行可以用super(); 也可以用this();指向本类中构造函数,但是子类中可以几个构造函调用来调用去,最后一定会有一个构造函数会访问父类中的某一个构造函数。(父类中的这个构造函数先执行)。this和super不能同时存在于同一个构造函数当中,因为他们都得写在第一行。
父类构造函数第一行也有默认的super();它指向的是JAVA中的“上帝”Object。
Final修饰符
继承的优点是提高了代码复用性,缺点是打破了类的封装性。比如说汽车这个类底下有很多子类,轿车卡车等等,但是如果我们新造一个类来继承汽车类并复写其轮子属性把轮子从圆形改为方形,那么就会产生一些问题和隐患。由此产生了一个两全其美的办法:final修饰符。
- 可以修饰类,函数,变量。
- 被final修饰的类不可以被继承。为了避免被继承,被子类复写功能。如 final class Xxx
- 被final修饰的方法不可以被复写。
- 被final修饰的变量是一个常量只能赋值一次,既可以修饰成员变量,又可以修饰局部变量。如果final x =4;以后不能再赋值,即使再赋值x = 4也不行。当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字。方便于阅读。而这个值不需要改变,所以加上final修饰。常量书写规范:所有字母都大写,如果由多个单词组成,单词间通过_连接。有些常量写成这样:publicstatic final double PI = 3.1415926;这就是权限无限大的全局静态常量,不用调用对象也能被所有人共享。共享数据一般用static修饰,因为不用每个对象都存一份。
- 内部类定义在类中的局部位置上时,只能访问该局部(与内部类同层级的)被final修饰的局部变量。
抽象类
定义:当多个类中出现相同功能,但是功能主体不同,这时也可以进行向上抽取。只抽取功能定义,而不抽取功能主体。
比如说工人,程序猿,艺术家等等职业的人都有学习功能,但是具体学习什么,怎么学习不确定,因此我们抽象出他们的父类——人,规定好了学习功能,但不定义该功能的具体内容。人这个类中的方法就是抽象方法,人这个类就叫抽象类。
抽象类特点:
1. 抽象方法一定定义在抽象类中。
2. 抽象方法和抽象类都必须被abstract关键字修饰。
3. 抽象类不可以用new创建对象(实例化)。因为调用抽象方法没意义(方法里没有内容)。
4. 抽象类中的抽象方法要被使用,必须由子类复写其所有的抽象方法后,建立子类对象调用。
如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。
5. 抽象的好处是可以强迫子类按照计划做一些事情。
6. 抽象类里面可以有抽象方法也可以有非抽象方法。
7. 抽象类和一般类没有太大的不同。该如何描述事物,就如何描述事物,只不过该事物中出现了一些看不懂的东西。这些不确定的部分,也是该事物的功能,需要明确出现。但是无法定义主体。抽象类仅比一般类多了个抽象函数(如果抽象类不定义抽象方法的话也可以,那么其存在的唯一目的就是不让该类建立对象)。
8. 抽象关键字abstract和final , private , static这些修饰符不可以共存。因为它必须被继承,而这与final和private的作用相违背,另外之所以抽象方法都不可以是静态的,是因为调用一个没有实际内容的抽象方法没有任何意义,这与第三点的规则是一致的。
9. 抽象类中也有构造函数,用于给子类对象进行初始化。
模板方法模式
在定义功能时,功能的一部分是确定的,但是有一部分是不确定的,而确定的部分在使用不确定的部分。那么这时就将不确定的部分暴露出区,由该类的子类去完成。这就可以用到模板方法模式,在父类里面把确定的内容定义好,把不确定的内容用抽象修饰交由子类具体实现,子类继承父类并复写其中的不确定的抽象方法后,结合父类已经确定的内容就可以得到最后我们想要的结果了。极大地方便开发,提高代码复用性。
例:获取一段程序运行的时间。
abstract class GetTime
{
public final void getTime()//封装好一个计时功能,模板方法模式
{
long start = System.currentTimeMillis();
runcode();//具体运行什么代码,模板无法确定,暴露出去。
long end = System.currentTimeMillis();
System.out.println("花费时间="+(end-start)+"毫秒");
}
abstract void runcode();//模板中的函数不一定是抽象的,有的具体情况需要调用默认方法。
}
class Sub extends GetTime
{
void runcode()//在子类中复写不确定的方法就可以了
{
for (int x =0;x<5000 ;x++ )
{
System.out.print("1");
}
}
}
class TemplateDemo
{
public static void main(String[] args)
{
new Sub().getTime();
}
}
接口
初期理解,可以认为是一个特殊的抽象类:当抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示。
可以理解为特殊类。
编译的时候会生成一个 接口名$.class文件
接口定义时,格式特点:
1, 接口中常见定义:常量,抽象方法。
2, 接口中的成员都有固定修饰符。
常量:public staticfinal
方法:public abstract
形如:
Interface Xxx
{
public static final int NUM = 3;
public abstract void show();
}
记住:接口中的成员都是public的。
上述两种格式如果其中哪个漏写了,或者完全不写也没事,因为interface会自动补上固定修饰符。但是为了阅读性,实际开发的时候都写全。否则有的时候会奇怪为什么运行会出现意想不到的结果。
类与类之间是继承关系,类与接口是实现关系,implements关键字
classDemo implements Xxx
接口:是不可以创建对象的,因为有抽象方法。
需要被子类实现,子类必须对接口中的抽象方法全都覆盖后,子类才可以实例化。否则子类是一个抽象类。
注意:子类定义时,方法的权限必须要设置为public否则无法覆盖,因为接口里的变量和方法全都是public,而要想继承或者实现,子类权限要大于等于父类。类不像接口,public修饰符不可以省略不写,java不会对类中函数或者变量的修饰符自动补齐。
接口可以被类多实现:一个类可以同时实现多个接口(类似于C中的多继承)。
比多继承优势在于:多实现方法里面没有方法体,不会相互冲突混乱。
一个类在继承另外一个类的同时还能实现多个接口。目的是扩展该类的功能。比如人这个类在继承哺乳动物的所有共性特征之外还可以实现“文化”,“逻辑”等接口,并实现里面的一些抽象方法。
写法如:
classHuman extends Mammal implements Culture, Logic
注意:extends一定要写在implements前面
接口与接口之间可以继承,而且可以多继承(没有方法体不冲突)。
如 interface C extendsB,A。
一个类或者接口在同时继承两个接口时,可以允许两个被继承接口里面有完全相同的函数。
但是如果其中一个接口中的函数这样:intshow();另一个接口中的同名函数这样:boolean show();这是不可以的,仅仅返回类型不同还是会产生混乱。对象在调用的时候输入同样的内容后会不知道返回哪个类型。
总结:
接口是用来功能扩展用的。
基本功能定义在类中,扩展功能定义在接口中。
抽象类和接口的区别:
1:抽象类只能被继承,而且只能单继承。
接口需要被实现,而且可以多实现。
2:抽象类中可以定义非抽象方法,子类可以直接继承使用。
接口中都有抽象方法,需要子类去实现。
3:抽象类使用的是 is a 关系。
接口使用的 like a 关系。
4:抽象类的成员修饰符可以自定义。
接口中的成员修饰符是固定的。常量是public static final,方法是public abstract。