黑马程序员——Java基础:继承、final关键字、抽象、接口、多态

——- android培训java培训、期待与您交流!   ———-

 

一、面向对象之继承——extends

子类和父类:

       多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。多个类可以称为子类,单独这个类称为父类或者超类。

继承的作用:

       1.提高了代码的复用性

       2.让类和类之间产生了关系,有了这个类,才出现了多态的特性。

注意事项:

       1.不要为了获取其他类的功能、简化代码而继承;必须是类和类之间有所属关系(谁是谁的什么)才可以继承。

       2.Java语言中,java只支持单继承,不支持多继承,因为多继承易出现安全隐患;当多个父类中定义了相同的功能,当功能内容不同时,子类不确定要运行哪一个。

       3.Java支持多层继承,即一个继承体系。如儿子继承父亲,父亲继承爷爷。

如何使用一个继承体系中的功能呢?

       查阅父类功能,创建子类对象使用功能。

注:在子类中可用super.变量名 的方式来引用父类中的变量。如果子类中出现非私有的同名成员变量时,子类要访问本类中的变量,用this;如果子类要访问父类中的变量,用super。

this和super的使用:

      this:代表的是本类对象的引用。

      super:代表的是父类对象的引用。

重写:又叫覆盖,当子类出现和父类一模一样的函数时,当子类对象调用该函数,会运行子类函数的内容。如同父类的函数被覆盖一样。这种情况是函数的另一个特性:重写(覆盖)。

     当子类继承父类,沿袭了父类的功能,到子类中。但是子类虽具备该功能,但是功能的内容却和父类不一致,这时,没有必要定义新功能,而是使用覆盖特性,保留父类的功能定义,并重写功能内容。子类同时具有父类方法中的内容时,可以用super.方法();

 注:1.子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败。

        2.静态只能覆盖静态。

        3.父类中的私有方法不能被重写。

        4. 重载:只看同名函数的参数列表;重写:子父类方法要一模一样。

示例:

//定义一个人————父类
class Person
{
	//名字和年龄是人的共有属性
	private String name;
	private int age;
	String area;
	
	//在构造函数中对名字和年龄进行初始化
	Person(String name,int age)
	{
		this.name=name;
		this.age=age;
		System.out.println(name+"  "+age);
	}
	//人都具有睡觉的功能
	void sleep()
	{
		System.out.println("sleep");
	}
}
//定义一个学生,继承人————子类
class Student extends Person
{
	Student(String name,int age)
	{
		//super关键字表示父类,因为姓名和年龄在父类中进行了初始化动作,在这里可以直接调用
		super(name,age);
	}
	//学生具有特有的功能,学习
	void study()
	{
		System.out.println("study");
	}
	//复写/覆盖
	void sleep()
	{
		System.out.println("sleep——Student");
	}

}

class PersonDemo
{
	public static void main(String[] args)
	{
		Student s=new Student("zhangsan",25);
		System.out.println(s.area="shanghai");
		s.sleep();
		s.study();
	}
}

运行结果为:

子父类中的构造函数

示例:

class Fu//父类
{
	Fu()//父类的构造函数
	{
		System.out.println("输出父类的构造函数");
	}
}
class Zi extends Fu//子类继承父类
{
	Zi()//子类的构造函数
	{
		System.out.println("输出子类的构造函数");
	}
}
class ZiFu 
{
	public static void main(String[] args) 
	{
		Zi z=new Zi();
	}
}

运行结果为:

注:在对子类对象进行初始化时,父类的构造函数也会运行。那是因为子类的每一个构造函数默认第一行有一条隐式的语句super();

        super():会访问父类中空参数的构造函数。而且子类中所有的构造函数默认第一行都是super();

为什么子类一定要访问父类中的构造函数?

        因为父类中的数据子类可以直接获取,所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的。所以子类在对象初始化时,要先访问一下父类中的构造函数。

       如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。如在示例中的super(name,age);

注:super语句一定定义在子类构造函数中的第一行。

结论:

       子类的所有构造函数,默认都会访问父类中空参数的构造函数。因为子类每一个构造函数内的第一行都有一句隐式super();当父类中没有空参数的构

造函数时,子类必须手动通过supe语句或者this语句形式来指定要访问的构造函数。当然子类的构造函数第一行也可以手动指定this语句来访问本类中的

构造函数。子类中至少会有一个构造函数会访问父类中的构造函数。

注:this和super都必须在第一行,所以不可能同时存在于同一个构造函数。

二、final关键字

特点:1.可以修饰类、函数、变量。

            2.final修饰的类不可以被继承。这样就可以避免被继承、被子类复写功能。

            3.final修饰的方法不可以被复写。

            4.被final修饰的变量是一个常量只能赋值一次,既可以修饰成员变量,又可以修饰局部变量。

当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字。方便于阅读。而这个值不需要改变,所以加上final

饰。作为常量:常量的书写规范所有字母都大写,如果由多个单词组成,单词间通过_连接。

           5.内部类定义在类中的局部位置上时,只能访问该局部被final修饰的局部变量。

例如:public static final double PI=2.14;

三、抽象类——abstract

由来:当多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,只抽取了功能定义,并未抽取功能主体,那么只有功能声明,没有功能主体的方法称为抽象方法。

特点:1.抽象方法一定在抽象类中。

          2.抽象类和抽象方法必须用abstract关键字来修饰。

          3.抽象类不可以用new创建对象,因为抽象类是具体事物抽取出来的,本身是不具体的,没有对应的实例,调用抽象方法没有意义。

          4.抽象类通过其子类实例化,而子类需要覆盖掉抽象类中所有的抽象方法后才可以创建对象,否则该子类也是抽象类。

注:抽象类中可以有非抽象的方法。

抽象方法和一般方法的区别:

        1、抽象类和一般类没有太大的不同。该如何描述事物,还是如何描述事物。只不过,该事物中出现了一些不知道具体内容的方法部分。这些不确定的部分,也是该事物的功能,需要明确出来,但是无法定义主体。通过抽象方法来表示。

        2、抽象类比一般类多了个抽象函数。就是在类中可以定义抽象方法。

        3、抽象类不可以实例化。

        4、抽象类虽然不能创建对象,但是也有构造函数。供子类实例化调用。

注:被abstract修饰的函数不能同时被privatefinalstatic修饰。

      原因:

                final:被final修饰的类不能有子类。而被abstract修饰的类一定是一个父类。

                private:抽象类中的私有的抽象方法,不被子类所知,就无法被复写。而抽象方法出现的就是需要被复写。

                static:如果static可以修饰抽象方法,那么连对象都省了,直接类名调用就可以了。可是抽象方法运行没意义。

注:抽象类中可以不定义抽象方法。这样做可以不让本来实例化,也可以用于模块设计

模板设计:在定义功能时,功能的一部分是确定的,一部分是不确定的;而确定的部分在使用不确定的部分,那么这时就将不确定的部分暴露出去,由该类的子类去完成。

示例:获取一段程序运行的时间(使用了模板设计)

/*
思路:程序执行结束的时间减去程序的开始执行时间
*/
abstract class  GetTime
{
	public final void getTime()//加final表示不能被复写
	{
		long start=System.currentTimeMillis();//获取当前时间,即开始执行时间
		runCode();
		long end=System.currentTimeMillis();//执行结束时间
		System.out.println("毫秒"+(end-start));
	}
	abstract void runCode();//由于程序不确定,所以使用抽象
}

class GetProgram extends GetTime
{
	void runCode()//复写抽象方法
	{
		for(int x=0;x<100;x++)
			System.out.print(x+" ");
	}
}
class TemplateDemo
{
	public static void main(String[] args) 
	{
		new GetProgram().getTime();
	}
}

运行结果为:

四、接口——interface

接口:可以理解为一个特殊的抽象类。当抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示。接口使用interface来表示,子类中用implements实现。

class用于定义类。如:class a{ } //基本功能

interface用于定义接口。如:interface b{ } //扩展功能  或者  class  c  implements  b{ }

格式特点:

       1.接口中常见定义:常量,抽象方法。

       2.接口中的成员都有固定修饰符。

               常量:public static final

               方法:public abstract

       3.接口中的成员都是public的。

结论:接口中的成员都是public的。

在使用中,常量可以不写publicstatic final,方法可以不写public abstract,编译时Java会自动添加这些修饰符,因为这是固定的格式修饰符。但为了方便阅读,通常我们都写上。

示例:

interface Inter
{
	public static final int num=3;
	public abstract void show();
}
class Test implements Inter
{
	public void show()
	{
		System.out.println("Hello World!"+num);
	}
}
class InterfaceDemo 
{
	public static void main(String[] args) 
	{
		Test t=new Test();
		t.show();
		System.out.println(Test.num+"::::"+Inter.num);
	}
}
<span style="font-size:14px;">
</span>

运行结果为:

 

注:1.接口不可以创建对象的,因为有抽象方法。需要被子类实现(implements),子类对接口中的抽象方法全都覆盖后,子类才可以实例化。否则子类是一个抽象类。

       2.接口可以被类多实现,也就是对多继承不支持的一种转换形式,java支持多实现。

接口的特点:

        1、接口是对外暴露的规则。

        2、接口是程序的功能扩展。

        3、接口的出现降低耦合性。

        4、接口可以用来多实现。这也是对多继承不支持的转换形式。java支持多实现。

        5、类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。

        6、 接口与接口之间可以有继承关系。而且可以多继承。

接口与抽象类的共性与区别:

       共性:都是不断向上抽取出来的抽象的概念。

       区别:

                1、抽象类体现继承关系,一个类只能单继承。

                    接口体现实现关系,一个类可以多实现。同时接口与接口之间有继承关系。

                2、抽象类是继承,是 "is a "关系。

                    接口是实现,是 "like a"关系。

                3、抽象类中可以定义非抽象方法,供子类直接使用。

                    接口的方法都是抽象,接口中的成员都有固定修饰符。

                4、抽象类中可以私有变量或方法。

                    接口中的常量和方法都是public修饰的权限。

五、多态

多态:可以理解为事物存在的多种体现形态。

例:动物中有猫,狗。猫这个对象对应的类型是猫类型,如:猫 x = new();同时猫也是动物中的一种,也可以把猫称为动物。动物  y = new();那么动物就是猫和狗具体事物中抽取出来的父类型。父类型引用指向了子类对象。

多态的体现:

     1.父类的引用指向了自己子类的对象。 

     2.父类的引用也可以接收自己的子类对象。

多态的前提:

     1.类与类之间必须有关系,要么继承,要么实现。

     2.存在覆盖。父类中有方法被子类重写。

多态的好处与弊端:

     好处:提高了程序的可扩展性和后期可以维护性。

     弊端:只能使用父类中的引用访问父类中的成员,父类不能直接调用子类中的特有方法。

示例:

abstract class Animal//定义一个动物的抽象类
{
	abstract void eat();
}
class Cat extends Animal//猫继承于动物
{
	public void eat()
	{
		System.out.println("eat");//复写吃方法
	}
	public void catchMouse()//猫的特有属性
	{
		System.out.println("catchMouse");
	}
}
class AnimalDemo 
{
	public static void main(String[] args) 
	{
		Animal a=new Cat();//多态;类型提升,向上转型
		a.eat();

		function(a);
	}
	public static void function(Animal a)
	{
		if(a instanceof Cat)
		{
			Cat c=(Cat)a;//强制将父类的引用转换成子类类型,向下转型
			c.catchMouse();
		}
		else 
			System.out.println("输入的类型错误!");
	}
}

运行结果为:

注:  我们能转换的是父类引用指向了自己的子类对象时,该引用可以被提升,也可以被强制转换。多态至始至终都是子类对象在做着变化。

多态的特点:

1、多态中非静态成员函数的特点

       在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有编译失败。

如:在上面的示例中,如果用a.catchMouse();编译就会报错。这时只能通过强转,向下转型后,才可以使用子类的特有功能。

       在运行时期:参阅对象所属的类中是否有调用的方法。这就是说,如果父类中有一个非抽象的方法,而子类继承后又将其复写了,在多态运行时,父类的引用调用这个同名函数时,被运行的将是父类中的方法。

        简单总结就是:成员函数在多态调用时,编译看左边,运行看右边。

2、多态中成员变量的特点

        无论编译和运行,都参考左边(引用变量所属的类)。如:多态中的父类引用调用成员变量时,如果父类和子类有同名的成员变量,那么被调用的是父类中的成员变量。

3、多态中静态成员函数的特点

       无论编译和运行,都参考左边。也就是父类引用在调用静态同名函数时,被调用的是父类中的静态函数。这是因为,当类一被加载,静态函数就随类绑定在了内存中。此时,不需要创建对象,就可以使用类名直接调用。同时,父类中的静态成员函数一般是不被复写的。类在方法区中的分配:分为静态区和非静态区,而关键字thissuper在非静态区。

示例:

class  DuoTaiDemo
{
	public static void main(String[] args) 
	{
		Fu f=new Zi();//成员函数非静态时:编译时看左边(Fu),运行时看右边(Zi)
		              //  成员函数静态时:编译时、运行时都看左边
					  //        成员变量:编译时。运行时都看左边
		f.method1();
		f.method2();
		Fu f1=new Zi();
		Zi z1=new Zi();
		System.out.println("f1="+f1.num+"......z1="+z1.num);
	}
}
class Fu
{
	int num=4;//成员变量:编译时。运行时都看左边
	public static void method1()
	{
		System.out.println("fu-1");		
	}
	public static void method2()//  静态时:编译时、运行时都看左边
	{
		System.out.println("fu-2");		
	}
}
class Zi extends Fu
{
	int num=8;
	public static void method1()
	{
		System.out.println("zi-1");	
	}
	public static void method3()
	{
		System.out.println("zi-3");		
	}
}
<span style="font-size:14px;">
</span>

运行结果为:

——- android培训java培训、期待与您交流!   ———- 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值