//面向对象_继承_概述---单继承_多继承。
//描述学生。
/*
class Student
{
//属性。
String name;
int age;
//行为:
void study()
{
System.out.println("good good study");
}
}
class Worker
{
//属性:
String name;
int age;
//行为;
void work()
{
System.out.println("hard work");
}
}
*/
/*
为了提高代码的复用性,只建立一份代码就可以了。
一个类只要和另一个类产生关系就可以了。
关系:继承。
发现了获取到所需内容的同时也获取到不该具备的内容。
为什么?
发现原来这两个类之间根本就不存在继承关系。
怎么解决呢?
找到学生和工人的共性类型。将需要提供复用的代码进行抽取。
定义到一个共性的类中。
Person name age.
怎么在代码体现中,让学生和Persong产生关系呢?
只要通过关键字extends(继承),就可以了。
*/
class Person
{
//属性:
String name;
int age;
}
class Student extends Person//学生继承了Person,学生就是子类,Person就是父类(基类。超类)
{
//行为:
void study()
{
System.out.println("good good study");
}
}
class Worker extends Person
{
//行为;
void work()
{
System.out.println("hard work");
}
}
/*
面向对象的另一个特征:继承。
好处:提高了代码的复用性。让类和类产生了关系,给另一个特征。多态提供了前提。
什么时候定义继承?
必须保证类与类之间有所需(is a)关系.xxx是zzz中的一种。
学生是人中的一种。
苹果是水果中的一种。
狗是犬科中的一种。
在Java中继承的体现。
Java允许单继承。不直接支持多继承。将多继承进行其他方式的体现。
单继承:一个子类只能有一个父类。
多继承:一个子类可以有多个父类。
看上去,多继承很厉害!为什么?
class Fu1
{
void show1()
{}
}
class Fu2
{
void show2()
{}
}
//多继承。
class Zi extends Fu1,Fu2
{
}
Zi z = new Zi();
z.show1();
z.show2();
//问题随之而来,
万一多个父类具备了相同的功能呢?
class Fu1
{
void show()
{
sop("fu1 show run");
}
}
class Fu2
{
void show()
{
sop("fu2 show run");
}
}
//多继承。
class Zi extends Fu1,Fu2
{
}
Zi z = new Zi();
z.show();//调用就会产生不确定性。所以java保留了多继承的好处。
//改良了它的弊端,用多实现来体现。用多实现来体现。
//即将学到。
----------------------------------
java中还支持多重继承。
class A
{}
class B extends A
{}
class C extends B
{}
//形成继承体系。
学习继承体系时,应该参阅顶层的类中的内容。
了解这个体系的基本功能。
使用这个体系功能,需要创建最子类的对象。
看顶层,建底层。
*/
class ExtendsDemo
{
public static void main(String[] args)
{
Student stu = new Student();
stu.name = "xiaoming";
stu.age = 12;
stu.study();
}
}
//面向对象_继承_子父类中成员变量的特点。
/*
继承出现后,在代码中的体现。
重点在于成员的体现:
1,成员变量。重点明确原理。
特殊情况:
子父类中定义了一模一样的成员变量。
都存在于子类对象中。
如何在子类中直接访问同名的父类中的变量呢?
通过关键字super来调用。
super和this的用法很相似。
this代表的是本类对象的引用。
super代表的是父类的内存空间。
注意:这种情况开发见不到。因为父类一旦描述完了属性,
子类直接使用就可以了。
2,成员函数。
3,构造函数。
*/
//父类
class Fu
{
/*private int num1 = 3;*///父类中私有的内容子类不可以直接访问。
int num = 3;
}
class Zi extends Fu
{
/*int num2 = 4;*/
int num = 4;
void show()
{
// System.out.println("num1="+num1);
// System.out.println("num2="+num2);
System.out.println("fu num="+super.num);
System.out.println("zi num="+this.num);
System.out.println(this);
}
}
class ExtendsDemo
{
public static void main(String[] args)
{
Zi z = new Zi();
// System.out.println(z.num1);
// System.out.println(z.num2);
z.show();
}
}
/*
子父类中成员函数的特点。
特殊情况:
子父类中定义了一模一样的函数。
运行的结果是子类的函数在运行。
这种情况在子父类中,是函数另一个的特性,Override 重写(覆盖,复写)
重写什么时候用?
举例:
//描述手机
class Phone
{
//打电话。
void call()
{}
//来点显示。
void show()
{
System.out.println("电话号码");
}
}
Phone p = new Phone();
p.show();
//随着电话的升级,只显示号码不爽,希望显示姓名,大头贴等。
修改源码,虽然费劲,但是可以解决问题,但是不利于后期的维护和扩展。
为了扩展方便,新功能是不是新的电话具备呢?
单独描述,单独封装。新电话也是电话中的一种,继承。直接获取父类中的功能。
但是新电话的来电显功能已经变化了。需要重新定义。
那么定义一个新功能合适吗?比如NewShow,不合适,因为父类已经将来点显功能定义了。
子类完全不需要重新定义新功能,直接用就可以了,如果子类的来显功能内容不同。
只需要保留来显功能,定义子类的内容即可,这就是重写的应用。
class NewPhone extends Phone
{
void show()
{
//System.out.println("电话号码");
super.show();//如果还需要父类中原有的部分功能,可以通过super调用。
sop("姓名");
sop("大头贴");
}
}
重写的注意事项:
1,子类覆盖父类,必须保证权限要大于或者等于父类的权限。(private除外,因为子类
中不能直接访问父类中的private。)
Fu:
void show(){}
Zi:
void show(){}
2,静态覆盖静态。
写法上注意:不行一模一样:函数的返回值类型 函数名 函数的参数列表都要一样。
*/
class Fu
{
void show()
{
System.out.println("fu show1 run");
}
}
class Zi extends Fu
{
static void show()
{
System.out.println("zi show2 run");
}
}
class ExtendsDemo2
{
public static void main(String[] args)
{
Zi z = new Zi();
z.show();
}
}
/*
子父类中构造函数的特点。
当子父类中都有构造函数时,发现结果为:
Fu constructor run
zi constructor run
先执行了父类的构造函数,再执行子类中的构造函数。
为啥呢?
因为子类中的所有的构造函数中的第一行都有一句隐式额语句Super();
//默认调用的是父类中空参数的构造函数。
子类中构造函数为什么有一句隐式的super呢?
原因:子类会继承父类中的内容,所以子类在初始化时,必须先到父类中去
执行父类的初始化动作。
才可以更方便子类使用。
当父类中没有空参数构造时,子类的构造函数必须显示的super语句指定
要访问的父类中构造函数。
这就是传说中的子类实例化过程。
细节:
1,如果子类的构造函数第一行写了this调用了本类的构造函数,那么super调用父类的
语句还有吗?
没有的,因为this()或者super(),只能定义在构造函数的第一行,因为初始化动作要先执行。
2,子类构造函数里有super,那么父类构造函数有没有super呢?
是有的,记住:只要是构造函数默认第一行都是super();
父类的父类是谁呢?super 调用的到底是谁的构造函数呢?
Java体系在设计时,定义了一个所有对象的父类Object.
总结:
类中的构造函数默认第一行都由隐式的super语句,在访问父类中的构造函数。
所以父类的构造函数既可以给自己的对象初始化,也可以给自己的子类对象初始化。
如果默认的隐式super语句没有对应的构造函数,必须在构造函数中通过this或者super
的形式明确要调用的构造函数。
问题:
1,this 语句和super语句是否可以在同一个构造函数中出现呢?不行:因为必须定义在第一行。
2,为什么要定义在第一行呢?因为初始化动作要先执行。
*/
class Fu extends Object
{
int count = 3;
//Fu(){}
Fu()
{
//super();
//显示初始化。
System.out.println("Fu constructor run..A.."+count);
}
Fu(int x)
{
//显示初始化。
System.out.println("Fu constructor run..."+x);
}
}
class Zi extends Fu
{
Zi()
{
System.out.println("zi constructor run..C..");
}
Zi(int x)
{
this();
System.out.println("zi constructor run..."+x);
}
}
class ExtendsDemo
{
public static void main(String[] args)
{
//New Zi();
new Zi(6);
new Student("lisi",21);
}
}
/*
class Fu
{
Fu()
{
System.out.println("Fu constructor run..A..");
}
Fu(int x)
{
//显示初始化。
System.out.println("Fu constructor run..B..");
}
}
class Zi extends Fu
{
Zi()
{
//super();
System.out.println("Fu constructor run..C..");
}
Zi(int x)
{
//super();
System.out.println("Fu constructor run..D..");
}
}
class ExtendsDemo
{
public static void main(String[] args)
{
new Zi();
new Zi(6);//打印结果是A C A D.
}
}
*/
//子类的实例化过程的应用。也是super调用的应用。
//什么时候用super调用父类中的构造函数,只要使用父类的指定初始化动作,
//就在子类中使用super()调用就可以了。
class Person
{
private String name;
private int age;
public Person(String name,int age)
{
this.name = name;
this.age = age;
}
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return age;
}
}
class Student extends Person
{
public Student(String name,int age)
{
//调用父类,使用父类的初始化动作。
super(name,age);
}
}
class Worker extends Person
{
public Worker(String name,int age)
{
//调用父类,使用父类的初始化动作。
super(name,age);
}
}
//final关键字。
/*
继承的弊端:打破封装性。
不让其他类去继承该类,就不会有重写。
怎么能实现呢?通过java中的关键字来实现。final(最终化)
【final】
是一个修饰符。可以修饰类,方法,变量(成员变量,局部变量,静态变量)
【特点】
1,final修饰的类是一个最终类,不能再派生子类。
如果类中出现部分可以重写,部分不可以?怎么办?只要让指定的方法最终化就可以了。
2,final修饰的方法是最终方法,不可以重写。
3,fianl修饰变量是一个常量。只能被赋值一次。
【什么时候需要在程序中定义final常量呢?】
当程序中一个数据使用时是固定不变的,这时为了增加阅读性,可以给该数据起个名字。
这就是变量,为了保证这个变量的值不被修改,加上final修饰,这就是一个阅读性很强的
常量。书写规范,被final修饰的常量名所有的字母都是大写的。如果由多个单词组成,单词
之间用下划线连接。
*/
/*final*/ class Fu
{
/*final*/ void show()
{
//调用到一些系统的功能。
//功能的内容是不可以改变的。
}
}
class Zi extends Fu
{
static final int number = 9;//这个编译会失败,final修饰的变量必须赋初值。
static final double PI = 3.14;//为了访问方便,直接用static修饰,可以直接用类名调用。
//覆盖
void show()
{
final int count = 21;
//count = 2;//失败。
System.out.println(count);
}
}
class FinalDemo
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
/*
描述狗,行为,吼叫。
描述狼,行为:吼叫。
发现他们之间有共性,可以进行向上抽取。
当然是抽取它们的所属共性类型,犬科。
犬科这类事物,都具备吼叫行为。但是具体怎么叫,是不确定的,是由具体的子类来
明确的。
这些不具体的功能,需要在类中标识出来,通过java中的关键字来体现。
定义了抽象函数的类也必须别abstract修饰。
抽象类,在描述事物时,没有足够的信息描述一个事物,这时该事物就是抽象事物。
*/
/*
【抽象类的特点】
1,抽象类和抽象方法都需要被abstract修饰。
抽象方法一定要定义在抽象类中。
2,抽象类不可以创建实例。原因就在于调用抽象方法没意义。
3,只有覆盖了抽象类中所有的抽象方法后,其子类才可以实例化。
否则,该子类还是一个抽象类。
之所以继承,更多的是在思想上,是面对共性类型操作会更简单。
【细节问题】
1,抽象类一定是一个父类。
是的,因为是不断向上抽取而来的。
2,抽象类中是否有构造函数。
有,虽然不能给自己的对象初始化,但是可以给自己的子类对象初始化。
抽象类和一般类的异同点:
相同:
1,它们都是用来描述事物的。
2,它们之中都可以定义属性和行为。
不同:
1,一般类可以具体的描述事物,
抽象类描述事物的信息不具体。
2,抽象类中可以多定义一个成员,抽象汉。
3,一般类可以创建对象,而抽象类不能创建对象。
3,抽象类中是否可以不定义抽象方法。
是可以的。那这个抽象类的存在到底有什么意义呢?仅仅是不让不让该类创建对象。
4,抽象类关键字不可以哪些关键字共存。
1,final. 被final修饰的类是最终类,不能被继承,而抽象类就是要被子类继承的。
2,private. 被private修饰的方法,其子类无法直接访问,而抽象的方法就是要让子类覆盖的。
3,static. 被static修饰的方法要随着类的加载而加载,且可以直接由类名调用。
*/
abstract class 犬科 //extends Object
{
abstract void 吼叫();//抽象函数,需要abstract修饰。并分号;结束
}
//代码体现。
class Dog extends 犬科
{
void 吼叫()
{
System.out.println("汪汪汪汪");
}
}
class Wolf extends 犬科
{
void 吼叫()
{
System.out.println("嗷嗷嗷嗷");
}
}
class AbstractDemo
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
/*抽象类联系
需求:公司中程序员有姓名,工号,薪水,工作内容。
项目经理除了姓名,工号,薪水,还有奖金,工作内容。
对给定的需求进行数据建模。
在问题领域中先找寻其中的涉及的对象。
程序员:
属性:姓名,工号,薪水。
行为:工作。
项目经理:
属性:姓名,共号,薪水。
行为:工作。
这些对象是否有关系呢?因为发现了它们之间的共性,应该存在着关系。
可以将他们的共性向上抽取到共性类型,员工。
员工:
属性:姓名,工号,薪水。
行为:工作。
发现员工的工作内容本身就不具体,应该是抽象的,由具体的子类来体现。
*/
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 Programer extends Employee
{
Programer(String name,String id,double pay)
{
super(name,id,pay);
}
public void work()
{
System.out.println("code....");
}
}
//具体的子类,经理:
class Manager extends Employee
{
//特有属性。
private double bouns;
public Manager(String name,String id,double pay,double bouns)
{
super(name,id,pay);
this.bouns = bouns;
}
public void work()
{
System.out.pritnln("work");
}
}
class AbstractTest
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
/*
抽象类中是可以定义抽象方法的,当一个抽象类中方法全是抽象的,
这时,可以通过另一种特殊的形式来体现。
不再用class来定义,用接口来表示。
接口该如何定义呢?
interface
abstract class Demo
{
abstract void show1();
abstract void show2();
}
*/
/*
接口中的成员已经被限定为固定的几种。
【接口的定义格式先介绍两种】
1,定义变量,但是变量必须有固定的修饰符修饰,public static final所以接口中的变量
也称之为常量。
2,定义方法,方法也有固定的修饰符,public abstract
接口中的成员都是公共的。
【接口的特点】
1,接口不能创建对象。
2,子类必须覆盖掉接口中所有的抽象方法后子类才可以实例化。
否则子类是一个抽象类。
*/
interface Demo//定义一个名称为Demo的接口。
{
public static final int NUM = 3;
public abstract void show1();
public abstract void show2();
}
//定义子类去覆盖接口中的方法。子类必须和接口产生关系,类与类的关系是继承
//类与接口之间是实现。
class Demoimpl implements Demo //子类实现接口。
{
//重写接口中的方法。
public void show1(){}
public void show2(){}
}
/*
【接口最重要的体现】
解决了多继承的弊端。将多继承这种机制在java中通过多实现完成了。
*/
/*
interface A
{
void show1();
}
interface B
{
void show2();
}
class C implements A,B//多实现。同时实现多个接口。
{
public void show1(){}
public void show2(){}
}
*/
/*
【怎么解决多继承的弊端呢?】
弊端:多继承时,当多个父类中有相同的功能时,子类调用会产生不确定性。
其实核心原因就在于多继承父类中的功能有主体,而导致调用运行时,不确定运行哪个主体内容。
为什么多实现就解决了呢?
因为接口中的功能都没有方法体,由子类来明确。
interface A
{
void show();
}
interface B
{
void show();
}
class C implements A,B//多实现。同时实现多个接口。
{
public void show(){}
}
C c = new C();
c.show();
*/
/*
【基于接口的扩展】
class Fu
{
public void show(){}
}
//子类通过继承父类扩展功能,通过基础扩展的功能都是子类应该具备的基础功能。
//如果子类想要继续扩展其他类中的功能呢?这时可以通过实现接口来完成。
Interface Inter
{
public abstract void show1();
}
class Zi exntends Fu implements Inter
{
public void show1()
{
}
}
接口的出现避免了单继承的局限。
父类中定义的事物的基本功能。
接口中定义的事物的扩展功能。
*/
/*
接口出现后的一些小细节。
1,类与类之间是继承(is a)关系。类与接口之间是实现(like a)关系。
接口与接口之间有关系吗?接口与接口之间是继承关系。
*/
interface InterA
{
void show1();
}
interface InterAA
{
void show11();
}
interface InterB extends InterA,InterAA//接口支持多继承。
{
void show2();
}
class Test implements InterB
{
public void show1(){}
public void show2(){}
public void show11(){}
}
class InterfaceDemo
{
public static void main(String[] args)
{
Demoimpl d = new Demoimpl();
d.show1();
d.show2();
}
}
/*
抽象类中是否可以不定义抽象方法,
是可以的。原因是不让该类创建对象。
*/
interface Inter
{
//定义了四种显示功能。
public void show1();
public void show2();
public void show3();
public void show4();
}
//定义子类,要使用第一种显示方式,
class InterImpl1 implements Inter
{
//覆盖show1方法。
public void show1()
{
System.out.println("show1 run");
}
//为了让该类示例化,还需要覆盖其他三个方法,虽然该类用不上。
public void show2(){}
public void show3(){}
public void show4(){}
}
//另一个子类需要显示三方法。
class InterImpl3 implements Inter
{
//覆盖show1方法。
public void show3()
{
System.out.println("show3 run");
}
//为了让该类示例化,还需要覆盖其他三个方法,虽然该类用不上。
public void show2(){}
public void show1(){}
public void show4(){}
}
/*
出现问题,
为了使用接口中的部分方法,而覆盖了全部的方法。而且每一个子类都要
这么做,复用性差。
将这些不用的方法都单独抽取到一个独立的类中,
让这个类去实现接口,并覆盖接口中的所有方法。
这个类知道这些方法的具体实现内容么?不知道。
所有只能为了后期子类创建对象方便,而进行空实现。
而这时,这个类创建对象有有意义吗?没有意义,这个类创建对象就不需要,直接将其抽象化。
这就是没有抽象方法的抽象类。
*/
abstract class InterImpl implements Inter
{
//实现inter接口中的所有方法。
public void show1(){}
public void show2(){}
public void show3(){}
public void show4(){}
}
//如果有子类去使用show1方法。让子类继承实现类就可以。
class InterImpl11 extends InterImpl
{
public void show1()
{
System.out.println("show1 run");
}
}
class
{
public static void main(String[] args)
{
InterImpl1 in1 = new InterImpl1();
in1.show1();
InterImpl3 in3 = new InterImpl3();
in1.show3();
}
}