java学习笔记7--抽象类与抽象方法

1、终结类与终结方法

被final修饰符修饰的类和方法,终结类不能被继承,终结方法不能被当前类的子类重写

终结类的特点:不能有派生类

终结类存在的理由:

安全: 黑客用来搅乱系统的一个手法是建立一个类的派生类,然后用他们的类代替原来的类

设计: 你认为你的类是最好的或从概念上你的类不应该有任何派生类

终结方法的特点:不能被派生类覆盖

终结方法存在的理由:

对于一些比较重要且不希望子类进行更改的方法,可以声明为终结方法。可防止子类对父类关键方法的错误重写,增加了代码的安全性和正确性

提高运行效率。通常,当java运行环境(如java解释器)运行方法时,它将首先在当前类中查找该方法,接下来在其超类中查找,并一直沿类层次向上查找,直到找到该方法为止

final 方法举例:

class Parent
{
   public Parent() {   } //构造方法
   final int getPI() { return Math.PI; } //终结方法
}

说明: getPI()是用final修饰符声明的终结方法,不能在子类中对该方法进行重载

2、抽象类

抽象类:代表一个抽象概念的类

  • 没有具体实例对象的类,不能使用new方法进行实例化
  • 类前需加修饰符abstract
  • 可包含常规类能够包含的任何东西,例如构造方法,非抽象方法
  • 也可包含抽象方法,这种方法只有方法的声明,而没有方法的实现

存在意义:

  • 抽象类是类层次中较高层次的概括,抽象类的作用是让其他类来继承它的抽象化的特征
  • 抽象类中可以包括被它的所有子类共享的公共行为
  • 抽象类可以包括被它的所有子类共享的公共属性
  • 在程序中不能用抽象类作为模板来创建对象;
  • 在用户生成实例时强迫用户生成更具体的实例,保证代码的安全性

举个例子:

将所有图形的公共属性及方法抽象到抽象类Shape。再将2D及3D对象的特性分别抽取出来,形成两个抽象类TwoDimensionalShape及ThreeDimensionalShape

–2D图形包括Circles、Triangles、Rectangles和Squares

–3D图形包括Cube、Sphere、或Tetrahedron

–在UML中,抽象类的类名为斜体,以与具体类相区别:

抽象类声明的语法形式为

abstract class Number {
    . . .
}

3、抽象方法

声明的语法形式为:

public abstract <returnType> <methodName>(...);

仅有方法头,而没有方法体和操作实现,具体实现由当前类的不同子类在它们各自的类声明中完成,抽象类可以包含抽象方法

需注意的问题:

  • 一个抽象类的子类如果不是抽象类,则它必须为父类中的所有抽象方法书写方法体,即重写父类中的所有抽象方法
  • 只有抽象类才能具有抽象方法,即如果一个类中含有抽象方法,则必须将这个类声明为抽象类
  • 除了抽象方法,抽象类中还可以包括非抽象方法

抽象方法的优点:

  • 隐藏具体的细节信息,所有的子类使用的都是相同的方法头,其中包含了调用该方法时需要了解的全部信息
  • 强迫子类完成指定的行为,规定其子类需要用到的“标准”行为

举一个绘图的例子:

各种图形都需要实现绘图方法,可在它们的抽象父类中声明一个draw抽象方法

abstract class GraphicObject {
    int x, y;
    void moveTo(int newX, int newY) { . . . }
    abstract void draw();
}

然后在每一个子类中重写draw方法,例如:

class Circle extends GraphicObject {
    void draw() {  . . .  }
}
class Rectangle extends GraphicObject {
    void draw() {  . . .  }
}

举一个例子:

首先定义一个抽象类:

abstract class Person {
    private String name;   //具体数据
    public Person(String n) {  //构造函数
        name = n;
    }
    public String getName() {  //具体方法
        return name;
    }
    public abstract String getDescription(); //抽象方法
}

下面通过抽象类Person扩展一个具体子类Student:

class Student extends Person {
    private String major;
    public Student(String n, String m) {
        super(n);
        major = m;
    }
    public String getDescription() {  //实现抽象类中的getDescription方法
        return "a student majoring in " + major;
    }
}

通过抽象类Person扩展一个具体子类Employee:

class Employee extends Person {
    private double salary;
    private Date hireDay;
    public Employee(String n, double s, int year, int month, int day) {
        super(n);
        salary = s;
        GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
        hireDay = calendar.getTime();
    }
    public double getSalary() {
        return salary;
    }
    public Date getDate() {
        return hireDay;
    }
    public String getDescription() {  //实现抽象类中的getDescription方法
        return String.format("an employee with a salary of $%.2f", salary);
    }
    public void raiseSalary(double byPercent) {
        double raise = salary * byPercent / 100;
        salary += raise;
    }
}

测试程序如下:

public class javatest {
    public static void main(String[] args) {
        Person[] people = new Person[2];
        people[0] = new Employee("Hary Hacker", 50000, 1989, 10, 1);
        people[1] = new Student("Maria Morris", "Computer science");
        for(Person p : people)
            System.out.println(p.getName() + ", " + p.getDescription());
    }
}

运行结果如下:

Hary Hacker, an employee with a salary of $50000.00
Maria Morris, a student majoring in Computer science

3、类的组合

面向对象编程的一个重要思想就是用软件对象来模仿现实世界的对象,现实世界中,大多数对象由更小的对象组成

与现实世界的对象一样,软件中的对象也常常是由更小的对象组成,Java的类中可以有其他类的对象作为成员,这便是类的组合

组合的语法很简单,只要把已存在类的对象放到新类中即可,可以使用“has a”语句来描述这种关系

例如,考虑Kitchen类提供烹饪和冷藏食品的功能,很自然的说“my kitchen ‘has a‘ cooker/refrigerator”。所以,可简单的把对象myCooker和myRefrigerator放在类Kitchen中。格式如下:

class Cooker{   // 类的语句  }
class Refrigerator{   // 类的语句}
class Kitchen{
    Cooker myCooker;
        Refrigerator myRefrigerator;
}

一条线段包含两个端点:

public class Point   //点类
{
     private int x, y;  //coordinate
     public Point(int x, int y) { this.x = x; this.y = y;}
     public int GetX()  {  return x; }
     public int GetY()  {  return y; }
}
class Line   //线段类
{
   private Point  p1,p2;     // 两端点
   Line(Point a, Point b) {
      p1 = new Point(a.GetX(),a.GetY());
      p2 = new Point(b.GetX(),b.GetY());
   }
   public double Length() {
      return Math.sqrt(Math.pow(p2.GetX()-p1.GetX(),2) + Math.pow(p2.GetY()-p1.GetY(),2));
   }
}

组合与继承的比较:

“包含”关系用组合来表达

如果想利用新类内部一个现有类的特性,而不想使用它的接口,通常应选择组合,我们需在新类里嵌入现有类的private对象

l如果想让类用户直接访问新类的组合成分,需要将成员对象的属性变为public

“属于”关系用继承来表达

取得一个现成的类,并制作它的一个特殊版本。通常,这意味着我们准备使用一个常规用途的类,并根据特定需求对其进行定制

举个例子:

car(汽车)对象是一个很好的例子,由于汽车的装配是故障分析时需要考虑的一项因素,所以有助于客户程序员理解如何使用类,而且类创建者的编程复杂程度也会大幅度降低

class Engine {  //发动机类
    public void start() {}
    public void rev() {}
    public void stop() {}
}
class Wheel { //车轮类
    public void inflate(int psi) {}
}
class Window { //车窗类
    public void rollup() {}
    public void rolldown() {}
}
class Door { //车门类
    public Window window = new Window();
    public void open() {}
    public void close() {}
}
public class Car {
    public Engine engine = new Engine();
    public Wheel[] wheel = new Wheel[4];
    public Door left = new Door(),right = new Door(); public Car() {
        for(int i = 0; i < 4; i++)
            wheel[i] = new Wheel();
    }
    public static void main(String[] args) {
        Car car = new Car();
        car.left.window.rollup();
        Car.wheel[0].inflate(72);
    }
}

许多时候都要求将组合与继承两种技术结合起来使用,创建一个更复杂的类

举个例子(组合与继承举例):

class Plate {  //声明盘子
    public Plate(int i) {
        System.out.println("Plate constructor");
    }
}
class DinnerPlate extends Plate { //声明餐盘为盘子的子类
    public DinnerPlate(int i) {
        super(i);
        System.out.println("DinnerPlate constructor");
    }
}
class Utensil { //声明器具
    Utensil(int i) {
        System.out.println("Utensil constructor");
    }
}
class Spoon extends Utensil { //声明勺子为器具的子类
    public Spoon(int i) {
        super(i);
        System.out.println("Spoon constructor");
    }
}
class Fork extends Utensil { //声明餐叉为器具的子类
    public Fork(int i) {
        super(i);
        System.out.println("Fork constructor");
    }
}
class Knife extends Utensil { //声明餐刀为器具的子类
    public Knife(int i) {
        super(i);
        System.out.println("Knife constructor");
    }
}
class Custom { // 声明做某事的习惯
    public Custom(int i) { System.out.println("Custom constructor");}
}
public class PlaceSetting extends Custom {//声明餐桌的布置
    Spoon sp; Fork frk; Knife kn;
    DinnerPlate pl;
    public PlaceSetting(int i) {
        super(i + 1);
        sp = new Spoon(i + 2);
        frk = new Fork(i + 3);
        kn = new Knife(i + 4);
        pl = new DinnerPlate(i + 5);
        System.out.println("PlaceSetting constructor");
    }
    public static void main(String[] args) {
        PlaceSetting x = new PlaceSetting(9);
    }
} 

运行结果:

Custom constructor

Utensil constructor

Spoon constructor

Utensil constructor

Fork constructor

Utensil constructor

Knife constructor

Plate constructor

DinnerPlate constructor

PlaceSetting constructor

时间: 2024-10-10 11:15:49

java学习笔记7--抽象类与抽象方法的相关文章

阿花宝宝 Java基础笔记 之 抽象类与抽象方法

一 . 抽闲累和抽象方法   1.  抽象类中的抽象方法必须被子类实现  除非子类也是抽象的.   2.抽象类不能呗实例化  但是抽象类的引用可以志向一个具体实现他的子类对象   3.  抽象类中的有构造方法  不能通过实例化的方式调用 但是可以在创建子类对象时候调用  所以说抽象累的构造方法是为让子类调用的    4. 抽象类中的抽象方法不可能 被调用  因为不能有一个抽象类的对象(抽象类不能被实例化)   5. 抽象类中可以有普通成员(变量 方法)  静态方法  但是子类的中同名的静态方法不

Java学习笔记之方法重载,动态方法调度和抽象类

一.方法重载 如果子类中的方法与它的超类中的方法有相同的方法名,则称子类中的方法重载超类中的方法,特别是当超类和子类中的方法名和参数类型都相同时,在子类中调用该方法时,超类中的方法会被隐藏.考虑下面程序: 1 class A 2 { 3 int i, j; 4 A(int a, int b) 5 { 6 i = a; 7 j = b; 8 } 9 10 // display i and j 11 void show() 12 { 13 System.out.println("i and j: &

java学习笔记8--接口总结

接着前面的学习: java学习笔记7--抽象类与抽象方法 java学习笔记6--类的继承.Object类 java学习笔记5--类的方法 java学习笔记4--对象的初始化与回收 java学习笔记3--类与对象的基础 java学习笔记2--数据类型.数组 java学习笔记1--开发环境平台总结 本文地址:http://www.cnblogs.com/archimedes/p/java-study-note8.html,转载请注明源地址. 生活中的接口: 什么是接口? 一个Java接口是一些方法特

java学习笔记(三)java中的修饰符abstract、static与final

一.四种访问级别的访问范围 访问级别 访问修饰符 同类 同包 子类 不同的包 公开 public 受保护 protected 默认 没有访问修饰符 私有的 private 二.使用abstract修饰符需要遵守的语法规则 1.抽象类中可以没有抽象方法,但是包含抽象方法的类必须定义为抽象类,即用abstract修饰: 2.抽象类跟抽象方法不能被final修饰符修饰: 3.抽象类中可以有非抽象方法,因为继承抽象类的子类必须重写父类中所有的抽象方法,因此抽象类中不能有抽象构造方法和抽象静态方法: 4.

【Java学习笔记之二十六】深入理解Java匿名内部类

在[Java学习笔记之二十五]初步认知Java内部类中对匿名内部类做了一个简单的介绍,但是内部类还存在很多其他细节问题,所以就衍生出这篇博客.在这篇博客中你可以了解到匿名内部类的使用.匿名内部类要注意的事项.如何初始化匿名内部类.匿名内部类使用的形参为何要为final. 一.使用匿名内部类内部类 匿名内部类由于没有名字,所以它的创建方式有点儿奇怪.创建格式如下: new 父类构造器(参数列表)|实现接口() { //匿名内部类的类体部分 } 在这里我们看到使用匿名内部类我们必须要继承一个父类或者

Java 学习笔记(2015.7.13~17)

Java 学习笔记(2015.7.13~17) Java this关键字 表示本类中的属性,调用本类中的方法 class Person {        private String name;         private int age;         public Person(String name, int age) {         this.name = name;//调用本类中的属性         this.age = age;//同上} //get&set方法:    

Java学习笔记之接口

一.接口的概念与定义 首先考虑一个简单的接口的定义: public interface Output { int MAX_LINE = 40; void out(); void getData(String msg); } 定义接口使用关键字interface 修饰符interface前面的public可以省略,如果省略,则采用默认访问控制,即只有在相同包结构的代码才可以访问此接口 接口不可以有构造方法(区别于类中的构造方法) 接口里面的所有成员,包括常量.方法等都是public访问权限,所以在

Java 学习笔记(2015.7.20~24)

Java 学习笔记(2015.7.20~24) Java Java 学习笔记(2015.7.20~24) 1.StringBuffer与StringBuilder的区别: 2.基本数据类型包装类 3.Date.Calendar.DateFormat等与时间相关的类 1.常用类String 2.正则表达式 3.常用类 StringBuffer 代表可变的字符序列 拆箱与装箱 包装类的应用 匿名内部类 数组 day11 day12 day13 day14 day11 父类中使用public定义的方法

java学习笔记9--内部类总结

java学习笔记系列: java学习笔记8--接口总结 java学习笔记7--抽象类与抽象方法 java学习笔记6--类的继承.Object类 java学习笔记5--类的方法 java学习笔记4--对象的初始化与回收 java学习笔记3--类与对象的基础 java学习笔记2--数据类型.数组 java学习笔记1--开发环境平台总结 本文地址:http://www.cnblogs.com/archimedes/p/java-study-note9.html,转载请注明源地址. java内部类分为:

非专业码农 JAVA学习笔记 3 抽象、封装和类(2)

(2).静态域-放在内存公共存储单元,不放在特定的对象,用static修饰 (续上一篇<非专业码农 JAVA学习笔记 3 抽象.封装和类(1)>...) (3).静态初始器-由static引导的一对大括号括起来的语句组,作用跟构造函数相似 (4).最终域-final引导的,值在整个过程都不发生改变的 5.方法 (1)方法的定义:修饰词1 修饰词2…返回值类型 方法名(参数) throw[异常列表] 这里个人经验就是注意定义了返回值的方法,要在方法体里面增加return 该类型变量:此外遇到if