一、继承
1.1 继承概念
继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为,提升代码的复用,减少冗余;
比如:
猫和老虎属于属于猫科动物,狗和狼属于犬科动物
猫科动物和犬科动物又是属于动物类。
所以继承需要符合的关系是:is-a,父类更通用,子类更具体。
1.2 Java继承的格式
Java中通过extends关键字来申明一个类是从另外一个类继承而来的
//父类
class Animal{
}
//子类
class Canine extends Animal{
}
1.3 继承使用
假设你要设计一个仿真系统程序,可以让用户设定一群动物丢到某种环境中观察会发生什么事情,这个程序必须能够在任何时间加入新类型的动物,我们来设计一下:
1、找出具有共同属性和行为的对象
找出三个变量和三个方法,如下
我们有三个变量
food:此动物所吃的食物
hunger:代表饥饿程度的int值
bound:代表动物活动范围区域
还有三个方法:
eat():动物遇到食物时的行为程序
sleep():睡眠的行为程序
roam():不在进食或睡眠时的行为程序
2、设计代表共同状态和行为的类,Animal类
class Animal{
//变量
String food;
int hunger;
int bound;
//方法
public void eat(){
}
public void sleep(){
}
public void roam(){
}
}
3、设计子类,Cat 和Dog
class Cat extends Animal{
}
class Dog extends Animal{
}
这个时候就完成了继承关系,Cat 和Dog 继承了Animal说的变量和方法。
public class ExtendsTest {
public static void main(String[] args){
Cat c = new Cat();
Dog d = new Dog();
c.eat();
d.eat();
}
}
class Animal{
String food;
int hunger;
int bound;
public void eat(){
System.out.println("他正在吃东西");
}
public void sleep(){
System.out.println("他正在睡觉");
}
public void roam(){
System.out.println("他在干其他事情");
}
}
class Cat extends Animal{
}
class Dog extends Animal{
}
1.4 继承类型
需要注意的是 Java 不支持多继承,但支持多重继承。
这里还需关注下面几点:
1、子类拥有父类非 private 的属性、方法。
2、子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
3、子类可以用自己的方式实现父类的方法。
4、Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 A 类继承 B 类,B 类继承 C 类,所以按照关系就是 C 类是 B 类的父类,B 类是 A 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
1.5 super、this、fianl
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
this关键字:指向自己的引用。
public class ExtendsTest {
public static void main(String[] args){
Cat c = new Cat();
c.eadTest();
}
}
class Animal{
public void eat(){
System.out.println("他正在吃东西");
}
}
class Cat extends Animal{
public void eat(){
System.out.println("他正在吃鱼");
}
void eadTest(){
super.eat();//调用父类的方法
this.eat();//调用自己的方法
}
}
他正在吃东西
他正在吃鱼
Process finished with exit code 0
final 关键字声明类可以把类定义为不能继承的,即最终类;
如果用于修饰方法,该方法不能被子类重写:
如果用于修饰成员变量,则这个变量不能被修改;
class Animal{
final String food="finsh";//修饰成员变量,变量不能修改
final public void sleep(){ //修饰方法,方法不能重写
System.out.println("他正在睡觉");
}
}
final class Cat extends Animal{//修饰类,无法再被继承
}
1.6 继承过程中的构造器(构造方法或者构造函数)
子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。
如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。
例如,上面的继承关系,我们想要不同的动物吃不同的东西,就需要用到构造函数,通过传入不同的参数来返回不同的结果
public class ExtendsTest {
public static void main(String[] args){
Cat c = new Cat("鱼");
Dog d = new Dog("骨头");
c.eat();
d.eat();
}
}
class Animal{
String food;
//父类的构造函数
Animal(String f){
this.food = f;
}
public void eat(){
System.out.println("他正在吃" + food);
}
}
class Cat extends Animal{
Cat(String f) {
super(f);//调用父类的方法
}
}
class Dog extends Animal{
Dog(String f) {
super(f);
}
}
二、重载和重写
2.1 重写(Override)
1、重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
2、重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
3、重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。例如: 父类的一个方法申明了一个检查异常 IOException,但是在重写这个方法的时候不能抛出 Exception 异常,因为 Exception 是 IOException 的父类,只能抛出 IOException 的子类异常。
举个例子:
public class ExtendsTest {
public static void main(String[] args){
Cat c = new Cat();;
c.eatTest();
}
}
class Animal{
public void eat(){
System.out.println("他正在吃东西");
}
}
class Cat extends Animal{
//重写父类的eat方法
public void eat(){
System.out.println("他正在吃鱼");
}
public void eatTest(){
this.eat();//调用重写后的eat方法
super.eat();//调用父类的eat方法
}
}
他正在吃鱼
他正在吃东西
Process finished with exit code 0
方法的重写规则
1、参数列表必须完全与被重写方法的相同。
2、返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本3、返回类型要一样,java7 及更高版本可以不同)。
4、访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
5、父类的成员方法只能被它的子类重写。
6、声明为 final 的方法不能被重写。
7、声明为 static 的方法不能被重写,但是能够被再次声明。
8、子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
9、子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
10、重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
11、构造方法不能被重写。
12、如果不能继承一个方法,则不能重写这个方法。
2.2 重载(Overload)
1、重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
2、每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
public class ExtendsTest {
public static void main(String[] args){
Animal a = new Animal();
a.eat();
a.eat("鱼");
}
}
class Animal{
public void eat(){
System.out.println("他正在吃东西");
}
//重载eat方法,传入food
public void eat(String food){
System.out.println("他正在吃" + food);
}
}
重载规则:
1、被重载的方法必须改变参数列表(参数个数或类型不一样);
2、被重载的方法可以改变返回类型;
3、被重载的方法可以改变访问修饰符;
4、被重载的方法可以声明新的或更广的检查异常;
5、方法能够在同一个类中或者在一个子类中被重载。
6、无法以返回值类型作为重载函数的区分标准。
2.3 总结
区别点 | 重载 | 重写 |
参数列表 | 必须修改 | 一定不能修改 |
返回类型 | 可以修改 | 一定不能修改 |
异常 | 可以修改 | 可以减少或删除,一定不能抛出新的或者更广的异常 |
访问 | 可以修改 | 一定不能做更严格的限制(可以降低限制) |
方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,
重写是父类与子类之间多态性的一种表现,(父类与之类之间)
重载可以理解成多态的具体表现形式。(类内部)
(1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。
(2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。
(3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
三、多态
多态是一种事务,多种形态。用名字一样的代码根据要求的不同,干不一样的事情。
分类
1:对象的多态。(父对象引用指向子类实例)
Animal a = new Cat();
2:方法的多态。(分为方法的重载和重写或者覆盖,上面已经列举)
当使用对象的多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
public class ExtendsTest {
public static void main(String[] args){
ExtendsTest e = new ExtendsTest();
e.show(new Cat());
e.show(new Dog());
Animal a = new Cat();
a.eat();
Animal A = new Animal();
A.eat();
}
public void show(Animal a){
a.eat();
};
}
class Animal{
public void eat(){
System.out.println("他正在吃东西");
}
}
class Cat extends Animal{
public void eat(){
System.out.println("他正在吃鱼");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("他正在吃骨头");
}
}
四、接口与抽象
4.1 抽象类:
抽象类通过关键字abstract来抽象,抽象类不能直接引用,必须被继承
abstract class AnimalNew
抽象实例:
public class AbsTest {
public static void main(String[] args){
// AnimalNew a = new AnimalNew(): //抽象化的类不能直接引用,必须被继承
Cat c = new Cat();
c.eat();
c.eat("鱼");
}
}
abstract class AnimalNew{
public void eat(){
System.out.println("他正在吃东西");
};
}
class Cat extends AnimalNew{
public void eat(String food){
System.out.println("他正在吃" + food);
}
}
他正在吃东西
他正在吃鱼
Process finished with exit code 0
4.2 抽象方法:
1、抽象的方法,需要要实现;
2、抽象方法,必须在抽象类中;
public class AbsMontn {
public static void main(String[] args){
Cat c = new Cat();
c.eat();
c.eat("鱼");
}
}
abstract class AnimalNew{
//抽象方法所在类必须抽象,抽象的方法没有具体的实现,需要继承的类来实现
public abstract void eat();
}
class Cat extends AnimalNew{
public void eat(String food){
System.out.println("他正在吃" + food);
}
public void eat() {
System.out.println("他正在吃东西" );
}
}
4.3 接口及实现:
接口定义
可见度] interface 接口名称 [extends 其他的接口名] {
// 声明变量
// 抽象方法
}
接口的实现:
...implements 接口名称[, 其他接口名称, 其他接口名称..., ...] ...
当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。
类使用implements关键字实现接口。在类声明中,Implements关键字放在class声明后面。
通过接口可以实现解决多重继承的问题;
例子:
Animal.java文件
public interface Animal{
public void eat();
}
Cannie.java文件
public interface Cannie {
public void sleep();
}
ExtendsTest .java文件
public class ExtendsTest implements Animal,Cannie {
public void eat(){
System.out.println("他正在吃鱼");
}
public void sleep(){
System.out.println("他正在睡觉");
}
public static void main(String[] args){
ExtendsTest E = new ExtendsTest();
E.eat();
E.sleep();
}
}
接口与类相似点:
一个接口可以有多个方法。
接口文件保存在 .java 结尾的文件中,文件名使用接口名。
接口的字节码文件保存在 .class 结尾的文件中。
接口相应的字节码文件必须在与包名称相匹配的目录结构中。
接口与类的区别:
接口不能用于实例化对象。
接口没有构造方法。
接口中所有的方法必须是抽象方法。
接口不能包含成员变量,除了 static 和 final 变量。
接口不是被类继承了,而是要被类实现。
接口支持多继承。