Java学习系列(007)——继承和多态
- 继承:(inheritance):从已有的类创建新类的过程。提供继承信息的类叫父类(超类、基类),得到继承信息的类叫子类(派生类、衍生类)。
- is-a关系,子类is-a(是一个)父类
- 子类要继承父类的属性,其属性应该使用protected修饰
- 多态:(polymorphism):执行相同的行为却做了不同的事情(产生了不同的结果)。
- 方法重写(在继承过程中)
- 对象造型(将子类造型成父类)
抽象:(abstraction):寻找共性。定义类的过程就是一个抽象的过程,需要做数据抽象和行为抽象。
- 抽象类不能实例化,专门用来让别的类继承它,如果一个类中有抽象方法,那么该类必须被申明为抽象类。
相关概念
- 抽象类:被abstract关键字修饰的类,抽象类不能实例化(不能创建对象),专门为其他类提供继承信息。
- 抽象方法:如果一个方法没有方法体,就可以定义为抽象方法,也是用abstract关键字修饰,如果一个类有抽象方法,这个类必须被声明为抽象类。子类继承该抽象类时必须重写抽象方法。
- 终结类:被final关键字修饰的类,终结类不能被继承,工具类通常声明为终结类。
- 终结方法:被final关键字修饰的方法,子类中不能重写终结方法。
- 静态方法:被static修饰的方法,静态的方法和属性属于类,不属于对象,在内存中只有唯一的拷贝,静态方法和属性调用的方式是用类名调用,而不是通过对象的引用来调用。
练习:创建一个自定义窗口类、一个图形类、一个矩形类、一个椭圆类、一个直线类,编程实现在自定义窗口中画出矩形、椭圆及直线
package cn.libill.ui;
import java.awt.Graphics;
import javax.swing.JFrame;
import cn.libill.util.MyUtil;
import cn.liblill.shape.Oval;
import cn.liblill.shape.Shape;
/**
* 自定义窗口
* @author libill
*
*/
@SuppressWarnings("serial")
public class MyFrame extends JFrame {
private Shape[] shapes = new Shape[100];
public MyFrame() {
this.setTitle("围棋"); //设置窗口标题
this.setSize(800, 600); //设置窗口大小
this.setResizable(false); //不可调整窗口大小
this.setDefaultCloseOperation(EXIT_ON_CLOSE); //关闭窗口时退出程序(后台)
this.setLocationRelativeTo(null); //窗口居中
for (int i = 0; i < shapes.length; i++) {
switch(MyUtil.random(1, 3)) {
case 1: shapes[i] = new Oval(); break;
case 2: shapes[i] = new Oval(); break;
case 3: shapes[i] = new Oval(); break;
}
shapes[i].setX1(MyUtil.random(0, 800));
shapes[i].setY1(MyUtil.random(0, 600));
shapes[i].setX2(MyUtil.random(0, 800));
shapes[i].setY2(MyUtil.random(0, 600));
shapes[i].setColor(MyUtil.createRandomColor()); //设置画笔颜色
}
}
@Override
public void paint(Graphics g) {
super.paint(g);
/* g.setColor(Color.ORANGE);
g.fillRect(0, 0, 600, 600);
g.setColor(Color.BLACK);
g.drawRect(36, 36, 548, 548);
for (int i = 0; i < 19; i++) {
g.drawLine(40, 40 + 30 * i, 580, 40 + 30 * i);
g.drawLine(40 + 30 * i, 40, 40 + 30 * i, 580);
}
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
g.fillOval(120 + 180 * i + 5, 120 + 180 * j + 5, 10, 10);
}
}*/
for (Shape s : shapes) {
s.draw(g);
}
}
}
package cn.liblill.shape;
import java.awt.Color;
import java.awt.Graphics;
/**
* 图形抽象类
* @author libill
*
*/
public abstract class Shape {
protected int x1; //起点横坐标
protected int y1; //起点纵坐标
protected int x2; //终点横坐标
protected int y2; //终点纵坐标
protected Color color; //画笔颜色
/**
* 绘画(抽象方法留给子类实现)
* @param g 画笔
*/
public abstract void draw(Graphics g);
/**
* 起点横坐标访问器
* @return 起点横坐标
*/
public int getX1() {
return x1;
}
/**
* 起点横坐标修改器
* @param x1 起点横坐标
*/
public void setX1(int x1) {
this.x1 = x1;
}
/**
* 起点纵坐标访问器
* @return 起点纵坐标
*/
public int getY1() {
return y1;
}
/**
* 起点纵坐标修改器
* @param y1 起点纵坐标
*/
public void setY1(int y1) {
this.y1 = y1;
}
/**
* 终点横坐标访问器
* @return 终点横坐标
*/
public int getX2() {
return x2;
}
/**
* 终点横坐标修改器
* @param x2 终点横坐标
*/
public void setX2(int x2) {
this.x2 = x2;
}
/**
* 终点纵坐标访问器
* @return 终点纵坐标
*/
public int getY2() {
return y2;
}
/**
* 终点纵坐标修改器
* @param y2 终点纵坐标
*/
public void setY2(int y2) {
this.y2 = y2;
}
/**
* 画笔颜色修改器
* @param color 画笔颜色
*/
public void setColor(Color color) {
this.color = color;
}
}
package cn.liblill.shape;
import java.awt.Graphics;
/**
* 矩形类
* @author libill
*
*/
public class Rectangle extends Shape {
@Override
public void draw(Graphics g) {
int x1 = getX1() < getX2() ? getX1() : getX2();
int y1 = getY1() < getY2() ? getY1() : getY2();
int x2 = getX1() > getX2() ? getX1() : getX2();
int y2 = getY1() > getY2() ? getY1() : getY2();
int width = Math.abs(x2 - x1);
int height = Math.abs(y2 - y1);
g.setColor(color);
g.drawRect(x1, y1, width, height);
}
}
package cn.liblill.shape;
import java.awt.Graphics;
/**
* 椭圆类
* @author libill
*
*/
public class Oval extends Shape {
@Override
public void draw(Graphics g) {
int x1 = getX1() < getX2() ? getX1() : getX2();
int y1 = getY1() < getY2() ? getY1() : getY2();
int x2 = getX1() > getX2() ? getX1() : getX2();
int y2 = getY1() > getY2() ? getY1() : getY2();
int width = Math.abs(x2 - x1);
int height = Math.abs(y2 - y1);
g.setColor(color);
g.drawOval(x1, y1, width, height);
}
}
package cn.liblill.shape;
import java.awt.Graphics;
/**
* 直线类
* @author libill
*
*/
public class Line extends Shape {
@Override
public void draw(Graphics g) {
g.setColor(color);
g.drawLine(x1, y1, x2, y2);
}
}
package cn.libill.util;
import java.awt.Color;
/**
* 自定义工具类
* @author libill
*
*/
public final class MyUtil { //加final表明该类不能被继承,没有任何子类(断子绝孙类)
/**
* 将构构器私有,不能在外部创建MyUtil类,要访问该类中的方法,只能用(MyUtil.)来调用
*/
private MyUtil() {
}
/**
* 产生指定范围的随机数
* @param min 最小值 (闭区间)
* @param max 最大值 (闭区间)
* @return 指定范围的随机数
*/
public static int random(int min, int max) {
return (int) (Math.random() * (max - min + 1) + min);
}
/**
* 生成随机颜色
* @return Color对象
*/
public static Color createRandomColor() {
int r = random(0, 255);
int g = random(0, 255);
int b = random(0, 255);
return new Color(r, g, b);
}
}
package cn.libill.test;
import cn.libill.ui.MyFrame;
/**
* 测试类
* @author libill
*
*/
public class MyFrameTest {
public static void main(String[] args) {
new MyFrame().setVisible(true);
}
}
作业:公司员工有三类,分别是部门经理、程序员、销售员。部门经理每月工资8000元,程序员每小时100元,销售人员底薪1200元,加上当月销售额5%的提成,编程完成工资结算系统)
package cn.libill;
/**
* 员工类
* @author libill
*
*/
public abstract class Employee {
protected String name; //员工姓名
/**
* 构造器
* @param name 员工姓名
*/
public Employee(String name) {
super();
this.name = name;
}
/**
* 工资
*/
public abstract double salary();
/**
* 员工姓名访问器
* @return 员工姓名
*/
public String getName() {
return name;
}
}
package cn.libill;
/**
* 部门经理类
* @author libill
*
*/
public class Manager extends Employee {
/**
* 构造器
* @param name 经理姓名
*/
public Manager(String name) {
super(name);
}
@Override
public double salary() {
return 8000;
}
}
package cn.libill;
/**
* 程序员类
* @author libill
*
*/
public class Programmer extends Employee {
private double hours; //工作小时
/**
* 构造器
* @param name 程序员姓名
*/
public Programmer(String name) {
super(name);
}
/**
* 设置程序员工作多少小时
* @param hours 工作小时
*/
public void setHours(double hours) {
this.hours = hours;
}
@Override
public double salary() {
return 100 * hours;
}
}
package cn.libill;
/**
* 销售员类
* @author libill
*
*/
public class SalesMan extends Employee {
private double salesVolume; //销售额
/**
* 构造器
* @param name 销售员姓名
*/
public SalesMan(String name) {
super(name);
}
/**
* 设置销售员销售额
* @param salesVolume 销售额
*/
public void setSalesVolume(double salesVolume) {
this.salesVolume = salesVolume;
}
@Override
public double salary() {
return 1200 + salesVolume * 0.05;
}
}
package cn.libill;
import java.util.Scanner;
/**
* 工资结算类
* @author libill
*
*/
public class WageCalculation {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Employee[] e = new Employee[10];
String[] names = {"张 三", "李 四", "王麻子", "王大锤", "元 芳",
"刘 阳", "肖继芳", "淑 芳", "陈 明", "李 静"};
for (int i = 0; i < e.length; i++) {
double m = Math.random();
if (m <= 0.6) { //60%的员工为销售员
e[i] = new SalesMan(names[i]);
} else if (m <= 0.9) { //30%的员工为程序员
e[i] = new Programmer(names[i]);
} else { //10%的员工为部门经理
e[i] = new Manager(names[i]);
}
}
for (int i = 0; i < e.length; i++) {
if (e[i] instanceof Manager) {
System.out.printf("【%s】 职位:经 理,本月工资为:%.2f\n", e[i].getName(), e[i].salary());
} else if (e[i] instanceof Programmer) {
System.out.printf("【%s】 职位:程序员, 请输入工作小时:", e[i].getName());
((Programmer) e[i]).setHours(sc.nextDouble());
System.out.printf("\t本月工资为:%.2f\n", e[i].salary());
} else {
System.out.printf("【%s】 职位:销售员, 请输入销售额:", e[i].getName());
((SalesMan) e[i]).setSalesVolume(sc.nextDouble());
System.out.printf("\t本月工资为:%.2f\n", e[i].salary());
}
}
sc.close();
}
}
由于部门经理、程序员和销售人员的共有特性都是公司的员工,所以创建一个Employee父类,并且都要计算工资,所以父类中应该具有姓名属性以及工资计算方法,因为在父类中无法实现对每个子类工资的计算,所以将其申明为抽象方法,让各子类去实现工资计算方法。由于父类中有抽象方法,所以父类也必须申明为抽象类。