面向对象(下)


一、继承

1、概念:把多个类中相同的属性和行为进行向上抽取得到另一个类,之后定义这些类的时候就不再用定义这些相同的属性和行为,只要让这些类继承抽取出来的类即可。这样一来,这具有相同属性和行为的类称为子类,抽取出来的是父类,两者之间的关系叫做继承。例如:要定义一个描述猫的类,首先定义一个动物类,让后让猫类继承动物类,所以猫的描述类就是子类,动物类就是父类。Java中的继承用extends来表示,格式为——class 子类 extends 父类。
2、特点:
    1)提高了代码的复用性。
    2)让类与类之间产生了关系,有了继承关系,才有了多态的特性。
    3)java 语言中java 只支持单继承,不支持多继承(因为多继承容易带来安全隐患)。
    4)java 支持多层继承,也就是一个继承体系。
    5)父类的私有成员不能被子类继承;父类的构造方法不能被继承(也就是子类可以继承父类中非私有成员)。
    继承关系出现之后,因为父类中定义的是子类所共有的东西,所以要使用继承体系中的一个功能,就要查阅父类功能,创建子类对象使用功能。而继承体系中的子父类的成员也具有一定的特点,可以从类中的变量、函数、构造函数分析。
3、变量
    如果子类中出现非私有的同名成员变量时,子类要访问本类中的变量,用 this;子类要访问父类中的同名变量,用 super。
    1)super 的使用和 this 的使用几乎一致,this 代表的是本类对象的引用,super 代表的是父类存储空间。
    2)在子类方法中使用一个变量时:(就近原则)首先,在方法的局部变量中找这个变量,有则使用。否则,在本类中找成员变量,有则使用。否则,在父类中找成员变量,有则使用,否则,报错。
4、函数 
    1)当子类出现和父类一模一样的函数时,若子类对象调用该函数,会运行子类函数,如同父类的函数被覆盖(重写)一样。
    2)当子类继承父类,沿袭了父类的功能,到子类中,但是子类虽具备该功能,但是功能的内容却和父类不一致,这时,没有必要定义新功能,而是使用覆盖特殊,保留父类的功能定义,并重写功能内容。
Note:
   用子类对象使用一个方法时。首先,在子类中找这个方法,有则使用。否则,在父类中找这个方法,有则使用。否则,报错。
5、构造函数
    1)在对子类对象进行初始化时,父类的构造函数也会运行,那是因为子类的构造函数默认第一行有一条隐式的语句 super();
    2)super();会访问父类中空参数的构造函数。而且子类中所有的构造函数默认第一行都是 super();
Note:
    子类的所有构造函数,默认都会访问父类中空参数的构造函数。因为子类每一个构造函数内的第一行都有一句隐式super();当父类中没有空参数的构造函数时,子类必须手动通过supe语句或者this语句形式来指定要访问的构造函数。当然子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。子类中至少会有一个构造函数会访问父类中的构造函数。
6、final关键字
    继承的出现,打破了对象的封装性,使得子类可以随意复写父类中的功能。这也是继承的一大弊端。那么怎么解决这个问题呢?这里就引出了一个新的关键字——final(最终)。
final作为一个修饰符。具有以下特点:
    1)可以修饰类、函数、变量。
    2)被final修饰的类不可以被继承。这样就可以避免被继承、被子类复写功能。
    3)被final修饰的方法不可以被复写。
    4)被final修饰的变量是一个常量只能赋值一次,既可以修饰成员变量,又可以修饰局部变量。
    当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字。方便于阅读。而这个值不需要改变,所以加上final修饰。作为常量:常量的书写规范所有字母都大写,如果由多个单词组成,单词间通过“_”连接。
    5)内部类定义在类中的局部位置上时,只能访问该局部被final修饰的局部变量。
Note:
    1)final不能修饰构造函数。
    2)static final 用来修饰成员变量和成员方法,可简单理解为“全局量”。对于变量,表示一旦给值就不可修改,并且通过类名可以访问。对于方法,表示不可覆盖,并且可以通过类名直接访问。
二、抽象类(abstract关键字)
1、定义:当多个类中出现相同功能,但是功能主体不同,这时可以进行向上抽取。这时,只抽取功能定义,而不抽取功能主体。
2、特点:
    1)抽象方法一定在抽象类中。
    2)抽象方法和抽象类都必须被abstract关键字修饰。
    3)抽象类不可以用new创建对象。因为调用抽象方法没意义。
    4)抽象类中的抽象方法要被使用,必须由子类复写其所有的抽象方法后,建立子类对象调用。如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。
    说了这么多,下面用一个实例来说明抽象类的使用:

/*
假如我们在开发一个系统时需要对员工进行建模,员工包含 3 个属性:
姓名、工号以及工资。经理也是员工,除了含有员工的属性外,另为还有一个
奖金属性。请使用继承的思想设计出员工类和经理类。要求类中提供必要的方
法进行属性访问。

员工类:name id pay
经理类:继承了员工,并有自己特有的bonus。
*/
//员工类,也是父类
abstract class Employee
{
	private String name;//姓名
	private String id;  //工号
	private double pay; //工资
	
	//自定义构造函数初始化
	Employee(String name,String id,double pay)
	{
		this.name = name;
		this.id = id;
		this.pay = pay;
	}
	
	public abstract void work();//抽象的工作方法

}

//经理类,继承员工类
class Manager extends Employee
{
	private int bonus;//特有的奖金属性
	Manager(String name,String id,double pay,int bonus)//子类的构造方法
	{
		super(name,id,pay);//调用超类中的构造器
		this.bonus = bonus;
	}
	public void work()//经理类的工作方法内容
	{
		System.out.println("manager work");
	}
}

//普通员工类,继承员工类
class Commoner extends Employee
{
	Commoner (String name,String id,double pay)
	{
		super(name,id,pay);
	}
	public void work()//普通员工类的工作方法内容
	{
		System.out.println("commoner work");
	}
}

class  AbstractDemo 
{
	public static void main(String[] args) 
	{
		new Manager("manager","007",10000,2000).work();
		new Commoner("commoner","012",5000).work();
	}
}

3、抽象类与一般类的区别:
    1)抽象类和一般类没有太大的不同。只是要描述的事物中出现了一些不知道具体内容的方法部分。这些不确定的部分,也是该事物的功能,需要明确出来,但是无法定义主体,所以通过抽象方法来表示。
    2)抽象类比一般类多了个抽象函数,就是在类中可以定义抽象方法。
    3)抽象类不可以实例化。
    4)抽象类虽然不能创建对象,但是也有构造函数,供子类实例化调用。

Note:abstract 不能与 private、static、final 等同时修饰一个成员方法,因为:
        final:被 final 修饰的类不能有子类。而被 abstract 修饰的类一定是一个父类。
        private:  抽象类中的私有的抽象方法,不被子类所知,就无法被复写。而抽象方法出现的就是需要被复写。
        
static:如果 static 可以修饰抽象方法,那么连对象都省了,直接类名调用就可以了,可是抽象方法运行没意义。
三、接口
1、概述:初期理解,可以认为是一个特殊的抽象类当抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示。class用于定义类,interface 用于定义接口。
2、格式特点:
    1)接口中常见定义:常量,抽象方法。
    2)接口中的成员都有固定修饰符(接口中的成员都是public)。
      常量:public static final
      方法:public abstract 
Note:
    1)与类相同,接口也是一种数据类型。接口中的方法全部都是抽象方法。
    2)使用关键字 interface
    3)接口中的成员变量:一律用公共的、静态的、最终的,相应关键字可以省略不写。
    4)成员变量相当于已被修饰为常量,必须在声明的时候赋值
    5)接口中的成员方法:一律用公共的、抽象的,相应关键字可以省略不写。
    6)接口并没有继承 Object 类,所有的接口都默认具备 Object 中的方法的抽象形式,以备给子类使用。
    接口是不可以创建对象的,因为有抽象方法。需要被子类实现,子类对接口中的抽象方法全都覆盖后,子类才可以实例化。否则子类是一个抽象类。接口可以被类多实现,也是对多继承不支持的转换形式。java支持多实现。
3、接口的特点与相关说明
1)特点:
    a)接口是对外暴露的规则,降低了耦合性,紧密联系在一起。
    b)接口是程序的功能扩展
    c)接口可以用来多实现(类与接口的关系是实现关系,类可以继承一个类的同时实现多个接口)
    d)java 中只有接口与接口之间存在多继承。
2)相关说明:
    a)如果一个类在实现接口时没有实现其中的全部抽象方法,那么这个类就是抽象类,必须用 abstract 关键字声明。
    b)实现接口中的方法时应注意:接口中的抽象方法都是默认的 public 公共的,因此在实现接口的类中,重写接口的方法必须明确写出 pubilc 修饰符。这是因为继承关系中不允许缩小访问权限范围。
    c)类可以在使用 extends 继承某一父类的同时使用 implements 实现接口。
    d)类可以实现多个接口,用逗号将各接口名称分开即可。
    e)接口之间用 extends 实现继承关系。子接口无条件继承父接口所有内容。 
4、接口与抽象类的比较:
1)共性:都是不断向上抽取出来的抽象的概念。
2)区别:
    a)抽象类体现继承关系,一个类只能单继承。
       接口体现实现关系,一个类可以多实现。同时接口与接口之间有继承关系。
    b)抽象类是继承,是 "is a "关系。
       接口是实现,是 "like a"关系。|
    c)抽象类中可以定义非抽象方法,供子类直接使用。
       接口的方法都是抽象,接口中的成员都有固定修饰符。
    d)抽象类中可以私有变量或方法。
       接口中的常量和方法都是public修饰的权限。
以下演示接口的使用及相关特点:

interface Inter
{
	public static final int NUM = 3;
	public abstract void show();
}

interface InterA
{
	public abstract void show();
}

class Demo
{
	public void function(){}
}

class Test extends Demo implements Inter,InterA
{
	public void show(){}
}


interface A
{
	void methodA();
}
interface B //extends A
{
	void methodB();
}

interface C extends B,A //接口与接口之间存在多继承
{
	void methodC();
}

class D implements C
{
	public void methodA(){}
	public void methodC(){}
	public void methodB(){}
}

class InterfaceDemo 
{
	public static void main(String[] args) 
	{
		Test t = new Test();
		System.out.println(t.NUM);
		System.out.println(Test.NUM);
		System.out.println(Inter.NUM);

	}
}
       结果如下所示:

四、多态
1、概述:
    定义:多态可以理解为事物存在(具备)的多种体现形态。
    体现:父类的引用指向了自己的子类对象。父类的引用也可以接收自己的子类对象。
    前提:类与类之间必须有关系,继承、实现(接口);必须存在覆盖(或复写) 
    好处:多态的出现大大的提高了程序的扩展性。
    弊端:虽然提高了扩展性,但只能使用父类的引用,去访问父类中的成员。
2、在多态中成员的特点:
a)(非静态)成员函数:
    在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有编译失败。
    在运行时期:参阅对象所属的类中是否有调用的方法。
Note:成员函数在多态调用时,编译看左边,运行看右边。
b)成员变量:无论编译和运行,都参考左边(引用型变量所属的类)。
c)(静态)成员函数:无论编译和运行,都参考左边。
3、类型转换
    多态中往往是父类引用指向子类对象,这样就会出现类型的转换。
    向上转型(类型提升,转成父类型):子类对象——>父类对象(程序会自动完成)
       格式:父类  父类对象  =  子类实例;
    向下转型:父类对象——>子类对象
       格式:子类  子类对象=(子类)父类实例;
Note:发生向下转型关系之前必须先发生向上转型关系;对于向下转型时,必须明确的指明要转型的子类类型。此处要想知道类的类型用instanceof关键字判断。
4、instanceof:
用于判断某个对象是否是某种类型。 
格式:对象名  instanceof  子类(实现)名
      对象  intanceof  类型(类类型  接口类型)       <前提是子类对象是有限的>
多态是自继承后类的又一大特性,并且使用时往往涉及到方法重载和方法覆盖,此处也作出一些比较。
5、方法重载与方法覆盖
方法重载与方法覆盖均是实现多态的机制。
重载方法必须满足的条件:     
    1)方法名必须相同
    2)方法的参数列表必须不相同
    3)方法的返回类型可以不相同
    4)方法的修饰符可以不相同
    5)方法重载可以在同一个类内部进行,也可以在子类里对父类的方法进行重载。
方法覆盖必须满足的条件:
    1)子类方法的名称及参数表必须与所覆盖的方法相同
    2)子类方法的返回类型必须与所覆盖方法相同
    3)子类方法不能缩小所覆盖方法的访问权限
    4)子类方法不能抛出比所覆盖方法更多的异常
    5)方法覆盖限于子类对父类方法进行,不能在同一个类内部完成。
说了多态这么多的特点的使用,一下用代码演示:

/*
 * 猫和狗都属于动物,使用多态完成猫和狗各自的动作
 */
abstract class Animal {
	abstract void eat();

	abstract void method();
}

class Cat extends Animal {

	@Override
	public void eat() {
		System.out.println("Cat eat");
	}

	@Override
	public void method() {
		System.out.println("吃鱼");
	}

	public void catchMouse() {
		System.out.println("抓老鼠");
	}
}

class Dog extends Animal {

	@Override
	public void eat() {
		System.out.println("Dog eat");
	}

	@Override
	public void method() {
		System.out.println("啃骨头");
	}

	public void lookHome() {
		System.out.println("看家");
	}
}

public class DuotaiDemo {

	public static void main(String[] args) {
		Animal animal = new Cat();
		animal.eat();
		if (animal instanceof Dog) {
			Dog dog = (Dog) animal;
			dog.lookHome();
		} else if (animal instanceof Cat) {
			Cat cat = (Cat) animal;
			cat.catchMouse();
		}
		Animal animal2 = new Dog();
		animal2.eat();
		Dog dog = (Dog) animal2;
		dog.lookHome();
	}

}
        结果如下所示:

    本篇幅所描述的仅代表个人看法,如有出入请谅解。


转载于:https://my.oschina.net/u/1387164/blog/404112

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值