Java基础之抽象类和继承知识点总结


为了提高代码的复用,将共性代码的代码进行抽取。
抽取到单独的一个类中进行封装。这时学生和工人类中就需要定义姓名和年龄了。可是学生和工人怎么获取到Person类中的姓名和年龄呢?可以让学生和Person产生一个关系,java中提供了一个关键字可以完成这个动作。extends(继承)。
例子1:父子类继承实例演示。
package cn.itheima.day17;
class Person{
	String name;
	int age;
}
class Student extends Person{
//Student就称之为子类,Person就成为父类,或者超类,或者基类。
				   //子类可以获取到父类中的成员(当然,不是全部。一会要逐一说明)
	public void study(){
		System.out.println("study");
	}
}
class Work extends Person{
	public void work(){
		System.out.println("work");
	}
}
public class ExtendsDemo {
	public static void main(String[] args) {
		Student s = new Student();
		s.name="heima";
		s.age = 30;
		s.study();
	}
}
继承:
    好处:
       1)继承的出现,提高了代码的复用性。
       2)继承的出现,让类与类之间产生了关系。而这个关系出现,就导致面向对象的第三个特征,多态的产生。
简单说:继承就是多态的前提。
Java中支持单继承,不支持多继承(其实更确切的说,是java将多继承进行改良,避免了安全隐患的产生)。
    classA{void show(){System.out.println("a");}
    classB{}{void show(){System.out.println("b");}
    classC extends A,B{}//这就是多继承,一个子类同时有多个父类。在java中是允许的。
    C c= new C();
    c.show();//到底是运行a,还是运行b呢?所以这就是安全隐藏。为了避免,所以java不允许这样继承。
简单说:一个儿子只能有一个爹,不能同时有多个爹。
Java中可以存在多层(多重)继承。
    classA{}
    classB extends A{}
    classC extends B{}
这时就出现了继承体系。
记住一个原则:
    1)A类中定义是这个体系中的最共性的功能。
    2)要使用这个体系的功能,建议建立最子类的对象来完成调用。
父类的由来都是子类不断向上抽取而来的。就代表着,A中定义的功能是该体系中最共性的功能。所以要了解这个体系的功能,只要参考A类中的功能即可。
了解后,需要建立对象来调用这些功能,那么建立哪个类的对象好呢?
建议建立C的对象,因为C中可以使用A类中的共性功能,也可以使用C类中的特有功能。
简单说:要使用一个继承体系,原则:
   1)参阅父类功能,
   2)建立最子类对象。
什么时候定义继承呢?
    继承是用于程序设计的。只要一个事物是另一个事物的一种。就可以用继承体现。
例如:猫或者虎都是猫科这类事物中的一种。就是可以进行继承。狗或者狼都是犬科,也可以继承。猫科和犬科都是哺乳动物中的一种,也可以进行继承。简单的分析:怎么判断是另一个事物的中的一种呢?
    那么可以先视为可以继承,用更简单的方式判断,一个类如果继承了另一个类,那么这个类是否应该具备另一个类的所有的成员。如果可以。继承有效,如果不可以,那么无法继承。
例子2:继承的错误表达实例演示。
class A{
	void method1(){}
	void method2(){}
}
class B {//extends A//如果为了提高复用,让B继承A这样,B就不用在定义method1()方法了,
	//但是B也获取到它不应该具备的method2().那么这样的继承不可以存在。
	//但是我们发现,虽然A和B之间没有继承关系,但是他们有共性的内容,那么就可以向上抽取。
	void method1(){}
	void method3(){}
}
class C{
	void method1(){}
}
class A extends C{
	void method2(){}
}
class B extends C{
	void method3(){}
}
注意的是:不要为了获取另一个类中的部分功能,而继承。这样是仅仅为了提高复用性而继承,并没有判断事物之间的关系。这样不可取。
继承出现后,在子父类中的成员特点。
成员:
    成员变量。
    成员函数。
    构造函数。
在子父类中成员变量的特点当子父类中出现同名的成员变量时,这时为了区分两个变量,在子类,用this调用的是子类的变量。用     super调用的是父类中的变量。
     super其实和this的用法很相似。
     this代表是本类对象的引用
     super代表的是父类的存储空间。
     this可以区分局部变量和成员变量重名的情况。
     super可以用于区分子父类中成员变量重名的情况。
属性在类中通常都是私有的,而且父类定义完了以后,子类一般是不会定义同名变量的。而是直接使用父类的变量即可所以开发并不常见。而面试却很常见。
注意:在访问上,子类不可以直接访问父类中的私有成员。可以通过间接的形式访问。
例子3:成员变量的实例演示。
package cn.itheima.day17;
class Fu{
	protected int num = 3;
	public int getNum() {
		return num;
	}
	public void setNum(int num) {
		this.num = num;
	}
}
class Zi extends Fu{
	private int num = 5;
	public void show(){
		int num = 6;
		System.out.println("num="+num);
		System.out.println("num="+this.num);
		System.out.println("num="+super.num);
	}
}
public class ExtendsDemo2 {
	public static void main(String[] args) {
		Zi z = new Zi();
		z.show();
	}
}
子父类中成员函数的特点特殊情况:
    当子父类中出现一模一样的函数时,子类对象在调用该函数时,运行的是子类中的函数。父类中的函数好像被覆盖一样。这就是函数的另一个特性:覆盖(复写,重写)override 函数的另一个特性: 重载 overload
覆盖:在子父类中,如果出现一模一样的函数时,就会发生覆盖操作。
例子4:成员函数的实例演示。
package cn.itheima.day17;
class Fu1{
	void show(){
		System.out.println("fu show run");
	}
}
class Zi1 extends Fu1{
	void show(){
		System.out.println("zi show run");
	}
}
class Telephone{
	void show(){
		System.out.println("number");
	}
	void call(){
		System.out.println("call");
	}
}
/*在日后的几年中,手机也在不断的升级。来电显示功能,既可以显示号码,
 * 又可以显示姓名,还可以显示大头贴。
      解决办法:可以在原来的TelPhone类中对show功能的源代码进行修改。
      这种解决动作,一旦修改,改的就不仅仅是一个代码而是一堆,那就是灾难。
      解决方法二:重新对新手机进行描述,并定义新功能。但是有些功能没有变化,
      只要继承自原来的老版手机可以获取该功能。新手机定义一个新的来电显示功能,
   newShow.但是继承了TelPhone以后,新手机就有了两个来电显示功能。Show和newShow.
      这样的设计是不合理,因为新手机没有必要具备show功能。
那么重新设计,应该是这样:
      原手机中已有来电显示功能,而新手机,具备该功能,这时该功能的内容有所不同。
     所以没有必要定义新的功能,只要沿袭原来的功能声明,并定义自己新的功能实现即可。
     发现继承的好处,可以有利于程序的扩展。不需要改动原来代码的情况下,就可以实现
     程序的功能扩展定义。*/
class NewTelephone extends Telephone{
	void show(){
                //System.out.println("number");//发现来电号码显示父类已经定义完了,
                                     //子类没有必须重复定义,只要使用父类的已有功能即可。
                                    //这时只要调用父类已有功能。因为父类和子类出现了一模一样
		                    //的方法,为了区分,可以使用super关键字来完成。
		super.show();
		System.out.println("name");
		System.out.println("picture");
	}
}
public class ExtendsDemo3 {
	public static void main(String[] args) {
		Zi1 z = new Zi1();
		z.show();
		NewTelephone tp = new NewTelephone();
		tp.show();
		tp.call();
	}
}
子父类中的构造函数的特点通过结果发现,子类的构造函数运行前,父类的构造函数先运行了,为什么呢?
    原因在于在子类的构造函数的第一行,其实就一条默认的隐式语句 super();
    super():和this():用法是一样的。
       this();调用了本类中的构造函数。
       super():调用了父类中的构造函数。
子类的构造函数中为什么都有一个默认的super()语句呢?
子类在实例化的时候,因为子类继承了父类的成员数据,所以必须要先看父类是如何对自己的成员进行初始化的。子类的所有构造函数默认都会访问父类中空参数的构造函数。当父类中没有定义空参数的构造时,子类必须通过super语句或者this语句,明确指定要访问的子类中或者父类中的构造函数。                 
简单说:子类的构造函数无论如何,都必须要访问父类中的构造函数。要明确父类的初始化过程.                                  
super语句用于访问父类的初始化,而初始化动作要先完成,所以super语句必须定义在构造函数的第一行。那么就和曾经的this语句冲突了。因为this语句也要定义在构造函数的第一行。所以一个构造函数中,只能有一个要么this语句,要么super语句。而且不冲突,因为子类中至少会有一个构造函数会去访问父类中的构造函数。一样可以完成父类的初始化。这个就是子类的实例化过程。
例子5:子父类构造函数的初始化的实例演示。
package cn.itheima.day17;
class Fu2{
	int num;
	public Fu2(){
		num = 4;
		System.out.println("fu run");
	}
	public Fu2(int x) {
		System.out.println("fu..."+x);
	}
}
class Zi2 extends Fu2{
	public Zi2() {
		this(20);
		System.out.println("zi run..."+num);
	}
	public Zi2(int x) {
		//super(90);
		System.out.println("zi2...."+x);
	}
}
public class ExtendsDemo4 {
	public static void main(String[] args) {
		//Zi2 zi1 = new Zi2();
		Zi2 zi2 = new Zi2(2);
	}
}
继承的弊端:打破的封装性。如果恶意继承并进行不正确的覆盖,会导致原功能的错误。
不让其他类继承可以解决这个问题。这就需要一个关键字来完成 :final(最终)
final:作为一个修饰符     1)它可以修饰类,可以修饰方法,可以修饰变量。
     2)final修饰的类是一个最终类,不可以被继承。
     3)final修饰的方法不可以被覆盖。
     4)final修饰的变量的是一个常量,只能被赋值一次。这个赋值指的是显示初始化赋值。
什么时候将变量修饰成final的呢?
通常在程序中会使用一些不会变化的数据。也就是常见的一些常量值。比如 3.14。那么这个数据直接使用是可以的,但是不利于阅读,所以一般情况下,都会被该数据起个容易阅读的名称。final double PI = 3.14; final修饰的常量定义一般都有规范书写,被final修饰的常量名称,所有字母都大写。如果由多个单词所组成,每一个单词间用“_”连接。
例子6:final关键字的应用实例。
package cn.itheima.day17;
/*final*/ class Fu3{  //final修饰的类不能被继承
	/*final*/ void show(){  //final修饰的方法不能被覆盖
		System.out.println("调用系统资源");
	}
	void method(){
		System.out.println("method run");
	}
}
class Zi3 extends Fu3{
	final int num; //final修饰成员变量,最终化的值应该是显示初始化值。
	public static final int aa = 45; //全局常量
	public Zi3() {
		num = 8;
	}
	public Zi3(int x) {
		num = x;
	}
	void show(){
		final int MY_NUMBER = 4;
		System.out.println("haha...系统被干掉啦"+num);
	}
}
public class FinalDemo {
	public static void main(String[] args) {
		Zi3 z=new Zi3();
		z.show();
		Zi3 z1=new Zi3(55);
		z1.show();
	}
}
//饿汉式
class Single{
	private static final Single s = new Single();
	private Single(){}
	public static Single getInstance(){
		return s;
	}
}
*/
抽象类两个类DemoA DemoB里面都有共性的功能,可以进行抽取。可是功能的声明相同,但是功能的具体内容不同。这时,我们只对相同的功能声明进行抽取。而不抽取功能的内容。
抽象类就是子类不断向上抽取而来的,只抽取了子类的功能声明,而没有抽取子类的具体的功能内容。所以功能是抽象的,需要定义在抽象类中。
  抽象类的特点      1)抽象类和抽象方法必须用abstract关键字修饰。
      2)抽象方法一定要存放在抽象类中。
      3)抽象类不可以被实例化。也就是不可以通过该类建立对象。因为抽象类建立对象后,调用抽象方法是没有意义。
       4)只有抽象类的子类将抽象类中的抽象方法全都覆盖掉,该子类就可以了建立对象了。如果只是部分覆盖,那么该子类还是一个抽象类。
   实例演示:
     abstract class Demo{
          abstract void show();//这时这个函数就看不懂了,因为没有方法体。
          //这个就需要被标识一下成看不懂的函数。需要一个关键字来修饰一下。abstract(抽象)当类中出现了抽象方法后,该类也必须标识成抽象的。
      }
      class DemoA extends Demo {
            void show(){
            System.out.println("showa");
            }
      }
      class DemoB extends Demo {
             void show(){
             System.out.println("showb");
            }
      }
抽象类什么时候定义。
当我们分析事物时,对对象进行描述时,其实就不断把对象中的共性内容向上抽取。在抽取过程中,发现对象具备相同的功能,但是功能的细节不同。这时在定义类时,该功能是没有具体的实现的,是由具体的对象来完成的。那么该功能就是抽象的。抽象类可以定义事物的共性内容,而且定义抽象功能,可以强迫子类去实现。
例子7:
     预热班学生:学习。
      就业班学生:学习。
可以对这两个的学生进行共性抽取。形成一个父类:学员。 
package cn.itheima.day17;
/**
 * 需求:预热班学生:学习。就业班学生:学习。
 * 可以对这两个的学生进行共性抽取。形成一个父类:学员。 
 * @author wl-pc
 */
abstract class XueYuan{
	abstract void study();
}
class YuRenBanXueYuan extends XueYuan{
	@Override
	void study() {
		System.out.println("JAVA SE");
	}
}
class JiuYeBanXueYuan extends XueYuan{
	@Override
	void study() {
		System.out.println("JAVA EE");
	}
}
public class AbstractDemo {
	public static void main(String[] args) {
		YuRenBanXueYuan xueyuan1 = new YuRenBanXueYuan();
		xueyuan1.study();
		JiuYeBanXueYuan xueyuan2 = new JiuYeBanXueYuan();
		xueyuan2.study();
	}
}
例子8:公司中程序员有姓名,工号,薪水,工作内容。项目经理除了有姓名,工号,薪水,还有奖金,工作内容。对给出需求进行数据建模。
package cn.itheima.day17;
/*
需求:公司中程序员有姓名,工号,薪水,工作内容。
项目经理除了有姓名,工号,薪水,还有奖金,工作内容。
对给出需求进行数据建模。
分析:
这里有两个具体事物。
1,程序员
	属性: name id pay 
	行为: work()
2,项目经理。
	属性: name id pay  bonus
	行为:work()
发现这两个事物具备共性内容。为了提高代码的复用性。
两个事物间是不具备继承关系的。因为两个事物不存在谁是谁中一种。
但是,可以确定是无论程序员,还是经理,他们都是公司员工。
他们都是员工的一种。而且员工都具备一些基本属性和行为。
这时就可以将两个事物向上抽取,出一个员工类。该类中定义就是两个事物共性的内容。
*/
abstract class Employee{
	private String name;
	private String id;
	private double pay;
	public Employee(String name, String id, double pay) {
		super();
		this.name = name;
		this.id = id;
		this.pay = pay;
	}
	public abstract void work();
}
class Manager extends Employee{
    private double bonus;
	public Manager(String name, String id, double pay, double bonus) {
		super(name, id, pay);
		this.bonus = bonus;
	}
	@Override
	public void work() {
		System.out.println("manager");
	}
}
class Programmer extends Employee{
	public Programmer(String name, String id, double pay) {
		super(name,id,pay);
	}
	@Override
	public void work() {
		System.out.println("code");
	}
}
public class AbstractDemo2 {
	public static void main(String[] args) {
		Manager manager =new Manager("zhangsan", "12", 5000.00, 3000.00);
		manager.work();
		Programmer programmer =new Programmer("lisi", "03", 7000.00);
		programmer.work();
	}
}
抽象类的一些细节1,抽象类中是否有构造函数      有。只要是class定义的类,里面肯定有构造函数。抽象类中的构造函数,用于给子类提供实例化。其实抽象类和一般类没什么区别。该怎么描述事物,就怎么描述。只不过有些功能,是该类中无法确定的内容,所以比普通类多了抽象方法。
2,抽象类中是否可以不定义抽象方法     可以不定义抽象方法,没有抽象方法的抽象类存在意义仅仅是不让该类创建对象。因为创建的没意义。这种情况在java awt中有具体体现。
3,抽象关键字和哪些关键字不可以共存    final:如果方法被抽象,就需要被覆盖,而final是不可以被覆盖,所以冲突。
              编译提示:非法的修饰符组合:abstract和 final
    private:如果函数被私有了,子类无法直接访问,怎么覆盖呢?
              编译提示:非法的修饰符组合:abstract 和 private
    static : 不需要对象,类名既可以调用抽象方法。而调用抽象方法没有意义。 
              编译提示:非法的修饰符组合:abstract和 static
子父类中覆盖的一些细节。
      1,子类覆盖父类必须要保证权限大于等于父类. 重点。一定要注意权限。
      2,静态覆盖静态。开发的时候,一般没有静态覆盖静态的情况。
实例演示:
final class Fu{
    //abstract void show();
    //staticvoid method(){}
}
class Zi extends Fu{
    //publicvoid show(){}
    //staticvoid method(){}
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值