您的位置:首页 > 编程语言 > Java开发

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

2017-12-07 11:45 429 查看
为了提高代码的复用,将共性代码的代码进行抽取。
抽取到单独的一个类中进行封装。这时学生和工人类中就需要定义姓名和年龄了。可是学生和工人怎么获取到Person类中的姓名和年龄呢?可以让学生和Person产生一个关系,java中提供了一个关键字可以完成这个动作。extends(继承)。

例子1:父子类继承实例演示。

[java] view plain copy 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:继承的错误表达实例演示。


[java] view
plain copy

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:成员变量的实例演示。


[java] view
plain copy

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:成员函数的实例演示。


[java] view
plain copy

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:子父类构造函数的初始化的实例演示。


[java] view
plain copy

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关键字的应用实例。


[java] view
plain copy

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:
预热班学生:学习。
就业班学生:学习。
可以对这两个的学生进行共性抽取。形成一个父类:学员。


[java] view
plain copy

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:公司中程序员有姓名,工号,薪水,工作内容。项目经理除了有姓名,工号,薪水,还有奖金,工作内容。对给出需求进行数据建模。

[java] view plain copy 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(){}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: