Java进阶(一做一学)
一、Java类与对象
1、Java类的定义
类的定义 + 属性/字段 + 构造器 + 方法
类的定义 + 属性/字段 + 构造器 + 方法
类定义的一般形式
[public ] class <类名称> {
//下面是成员变量(属性)的定义
[public | protected | private ] <数据类型> <属性名>;
……//其它变量属性定义
//构造器方法的定义
public <类名>([参数]){
……
}
//下面是方法定义
[public | protected | private ] <返回值类型> 方法名([类型 参数1 [,类型 参数2,]…) {
……//方法体
}
……//其它方法定义
[public ] class <类名称> {
//下面是成员变量(属性)的定义
[public | protected | private ] <数据类型> <属性名>;
……//其它变量属性定义
//构造器方法的定义
public <类名>([参数]){
……
}
//下面是方法定义
[public | protected | private ] <返回值类型> 方法名([类型 参数1 [,类型 参数2,]…) {
……//方法体
}
……//其它方法定义
▲注意:
类有一个类头,主要是给类取一个名字;
类还包含方法体,包含字段、构造器和一些方法。
字段被用于存储使对象保存状态的数据。
构造器被用于建立对象的初始状态,。
方法实现了类对象的行为。其中包含获取和设置两种方法,获取方法提供对象状态信息,设置方法用于改变对象的状态。
构造器和一般方法不同之处:
首先构造器的名字必须和类名相同;
构造器和方法都能带参数,但是方法能够含有返回类型;
带有非void返回类型的方法要求在方法体的最后语句含有return语句;
return语句仅仅应用与方法。
构造器不能带任何形式的返回类型。
类还包含方法体,包含字段、构造器和一些方法。
字段被用于存储使对象保存状态的数据。
构造器被用于建立对象的初始状态,。
方法实现了类对象的行为。其中包含获取和设置两种方法,获取方法提供对象状态信息,设置方法用于改变对象的状态。
构造器和一般方法不同之处:
首先构造器的名字必须和类名相同;
构造器和方法都能带参数,但是方法能够含有返回类型;
带有非void返回类型的方法要求在方法体的最后语句含有return语句;
return语句仅仅应用与方法。
构造器不能带任何形式的返回类型。
项目实战1:
在学生管理系统中,学生是系统的核心管理对象,定义一个学生类
包含三个成员变量,要求定义为私有变量,分别用于存储学生的姓名、学号和学分;
定义一个构造器方法,用于创建学生对象时初始化对象状态,要求创建对象时给定学生的姓名和学号,学分初始化为0;
定义获取学生对象状态数据的getter方法
定义添加学生学分的方法
定义用于修改学生名字的方法
定义用于输出学生姓名、学号和学分信息的方法。
包含三个成员变量,要求定义为私有变量,分别用于存储学生的姓名、学号和学分;
定义一个构造器方法,用于创建学生对象时初始化对象状态,要求创建对象时给定学生的姓名和学号,学分初始化为0;
定义获取学生对象状态数据的getter方法
定义添加学生学分的方法
定义用于修改学生名字的方法
定义用于输出学生姓名、学号和学分信息的方法。
public class Student {
private String stuName;
private int stuNo;
private double stuCredit;
public Student(String stuName,int stuNo) {
this.stuName = stuName;
this.stuNo = stuNo;
stuCredit = 0;
}
public String getStuName() {
return this.stuName;
}
public int getStuNo() {
return this.stuNo;
}
public double getStuCredit() {
return this.stuCredit;
}
public void setCredit(double Stucredit) {
this.stuCredit = Stucredit;
}
public void addCredit(double credit) {
this.stuCredit = credit;
}
public void motifyName(String name) {
this.stuName = name;
}
public void printInfo() {
System.out.println("学生姓名"+stuName);
System.out.println("学生学号 "+stuNo);
System.out.println("学生学分 "+stuCredit);
}
}
项目实战2:
在一个图书管理系统中,书是系统的核心对象,按要求设计Book类
Book类包含用于保存书名、作者、出版社、价格、折扣率信息的字段;
Book类包含用于保存书名、作者、出版社、价格、折扣率信息的字段;
添加构造器方法,用于初始化书名、作者以及出版社字段;
设计获取价格的方法(getter)
设计设置折扣率的方法(setter)
设计计算按照折扣率购买图书的价格的方法
设计打印输出图书所有字段信息的方法。
public class Book {
private String bookName;
private String bookAuthor;
private String bookEditor;
private double bookPrice;
private double bookDiscount;
public Book(String bookName,String bookAuthor,String bookEditor,double bookPrice){
this.bookName = bookName;
this.bookAuthor = bookAuthor;
this.bookEditor = bookEditor;
this.bookPrice = bookPrice;
}
public double getBookPrice() {
return this.bookPrice;
}
public void setBookDiscount(double discount) {
this.bookDiscount = discount;
}
public double actualPrice() {
double price;
if(this.bookDiscount == 0) {
return this.bookPrice;
}else
return price = this.bookPrice * this.bookDiscount;
}
public void printInfo() {
System.out.println("###################");
System.out.println("the book's name"+bookName);
System.out.println("the book's author"+bookAuthor);
System.out.println("the book's price"+bookPrice);
System.out.println("the book's editor"+bookEditor);
System.out.println("the book's discount"+bookDiscount);
System.out.println("the book's actualPrice"+actualPrice());
System.out.println("###################");
}
}
class testBook {
public static void main(String[] args) {
Book b1 = new Book("活着","余华","中国出版社",60);
b1.setBookDiscount(0.8);
System.out.println("未打折前的价格"+b1.getBookPrice());
System.out.println("打折后书的价格"+b1.actualPrice());
b1.printInfo();
}
}
总结:
类包含成员变量、构造器和方法,定义对象的状态和行为。
在构造器和方法中,一系列语句定义了一个对象如何完成指定的任务。并且我们还学习了赋值语句和条件语句,并将在后面的章节中学习其他类型的语句。
成员变量:成员变量为对象存储数据,使用成员变量也被称为实例变量。
注释:注释被插入在源代码中主要是提供解释为阅读者。对程序的功能无任何影响。
构造器:构造器允许每个对象被正确的建立,当它第一次被产生
范围:变量的范围定义了源代码区,变量能够被获取的。
生命周期:变量的生命周期描述了变量存在的时间的长度,在其被销毁之前。
赋值:赋值语句存储值,将右边的表达式的值赋给左边的变量。
方法:方法由头部和方法体两部分组成。
获取方法:获取方法返回对象的状态的信息。
设置方法:设置方法修改对象的状态。
Println:System.out.println方法在终端上打印参数内容。
条件语句:根据测试的结果,采取多个可能动作中的一个执行。
boolean表达式:boolean表达式仅仅有两个可能的值:true和false,通常被用于条件语句,作为测试的条件,控制不同程序段的执行。
局部变量:在方法中声明和使用的变量。它的范围和生命周期限制在方法中。
在构造器和方法中,一系列语句定义了一个对象如何完成指定的任务。并且我们还学习了赋值语句和条件语句,并将在后面的章节中学习其他类型的语句。
成员变量:成员变量为对象存储数据,使用成员变量也被称为实例变量。
注释:注释被插入在源代码中主要是提供解释为阅读者。对程序的功能无任何影响。
构造器:构造器允许每个对象被正确的建立,当它第一次被产生
范围:变量的范围定义了源代码区,变量能够被获取的。
生命周期:变量的生命周期描述了变量存在的时间的长度,在其被销毁之前。
赋值:赋值语句存储值,将右边的表达式的值赋给左边的变量。
方法:方法由头部和方法体两部分组成。
获取方法:获取方法返回对象的状态的信息。
设置方法:设置方法修改对象的状态。
Println:System.out.println方法在终端上打印参数内容。
条件语句:根据测试的结果,采取多个可能动作中的一个执行。
boolean表达式:boolean表达式仅仅有两个可能的值:true和false,通常被用于条件语句,作为测试的条件,控制不同程序段的执行。
局部变量:在方法中声明和使用的变量。它的范围和生命周期限制在方法中。
2、深入Java类与对象
①方法的重载:
方法重载:指在同一类中定义方法名相同,参数列表不同的方法,不需要考虑方法的返回值类型。
构造器的重载:同一个类中可以定义多个构造器
②this对象
this对象是Java中不需要创建就可以直接使用的对象;
this对象是对当前对象的引用
this对象是对当前对象的引用
③静态成员变量与方法
静态属性或类属性的特点:
类属性不在对象中,也不是对象状态的一部分,或者说它和对象无关。
类属性是与类有关的,整个类只有一份,第一次使用类时(如通过“类名.类属性”方式访问类属性或第一次把类实例化对象时等)在类的公共区给类属性分配存储空间。
使用类属性无需创建类的对象,虽然可以通过“对象名.类属性”方式访问类属性,但建议通过“类名.类属性”方式访问类属性。
类方法属于一个类而不是属于实例对象。
类方法与类对象无关,在创建对象之前就可以使用“类名.类方法”来调用类方法。
类方法中没有this引用,所以在类方法中不能使用this引用,也就不能直接使用对象属性和调用对象方法。
类方法中可以访问类属性和其它类方法。
类方法使用“类名.类方法”方式在类外来调用,所以应该定义为public访问方式。
类方法与类对象无关,在创建对象之前就可以使用“类名.类方法”来调用类方法。
类方法中没有this引用,所以在类方法中不能使用this引用,也就不能直接使用对象属性和调用对象方法。
类方法中可以访问类属性和其它类方法。
类方法使用“类名.类方法”方式在类外来调用,所以应该定义为public访问方式。
项目演练:
在学生报道注册管理系统中,学生是系统的核心对象,按要求设计Student类
学生对象具有班级、班级人数、学号、姓名、报到状态、报道人数等信息(请根据需要,思考如何定义这些信息变量);
具有两个构造器方法,一个构造器对学号、姓名信息进行初始化;一个构造器对学号、姓名进行初始化外,同时进行报到注册;
设计给学生报到注册的方法;
设计输出班级、班级人数、报到学生人数等信息的方法;
设计输出学生信息的方法。
学生对象具有班级、班级人数、学号、姓名、报到状态、报道人数等信息(请根据需要,思考如何定义这些信息变量);
具有两个构造器方法,一个构造器对学号、姓名信息进行初始化;一个构造器对学号、姓名进行初始化外,同时进行报到注册;
设计给学生报到注册的方法;
设计输出班级、班级人数、报到学生人数等信息的方法;
设计输出学生信息的方法。
public class Student {
//1define the member variable
private String classNo="software1";
private int classMember=40;
private int stuNo;
private String stuName;
private String stuState;
public static int stuStateNum = 0;
//2define the constructor
public Student(int stuNo,String stuName) {
this.stuNo = stuNo;
this.stuName = stuName;
}
public Student(int stuNo,String stuName,String stuState) {
this.stuNo = stuNo;
this.stuName = stuName;
setStuState(stuState);
}
//3define the method of student state
public static void setStuState(String stuState) {
if(stuState == "state") {
Student.stuStateNum += 1;
}
else return ;
}
//4define the method of get some value
public String getClassNo() {
return this.classNo;
}
public int getClassMember() {
return this.classMember;
}
//5defihe method of print information
public void printInfo1() {
System.out.println("The class's No: "+classNo);
System.out.println("The class's Member: "+classMember);
System.out.println("The student State Num: "+stuStateNum);
}
public void printInfo2() {
System.out.println("The student's No: "+stuNo);
System.out.println("The student's Name: "+stuName);
}
public static void main(String[] args) {
//print the first stuStateNumber
System.out.println("The former number: "+Student.stuStateNum+"\n");
System.out.println("####################");
//create a object
Student s1 = new Student(17240000,"monkey","state");
System.out.println("####################");
Student s2 = new Student(17240000,"dog","state");
Student s3 = new Student(17240001,"cat","state");
s1.printInfo1();
s1.printInfo2();
//print the stuStateNumber now
System.out.println("The stated number: "+Student.stuStateNum);
System.out.println("\n"+"####################");
//create a object
s3.printInfo1();
s3.printInfo2();
//print the stuStateNumber now
System.out.println("The stated number: "+Student.stuStateNum);
System.out.println("####################"+"\n");
}
}
3、对象交互
对象交互 + 抽象与模块化
解决问题的思路:
类设计的原则之一:类的职责明确化
一个类对应一个具体职责
一个类对应一个具体职责
解决复杂问题的方法:模块化、分治法
把问题分化成子问题,然后再次分化成更小的问题等等。直到子问题变成足够小,以便容易解决
把问题分化成子问题,然后再次分化成更小的问题等等。直到子问题变成足够小,以便容易解决
项目实战:时钟
24小时制的时钟(示例结果:11:10)
时钟是由显示小时的2位数字和显示分钟的两位数字组成.
1.显示小时的2位数字变化的规律:
1)0-23
2)当时间变化到23点,再增加时,则回到0
2.显示分钟的2位数字变化的规律:
1)0-59
2)当分钟变化到59点,再增加时,则回到0
public class NumberDisplay {
private int limit;
private int value;
//
public NumberDisplay(int limit) {
this.limit = limit;
value = 0;
}
public void setValue(int value) {
if (value >= 0 && value < limit) {
this.value = value;
} else if (value >= limit) {
this.value = value % limit;
} else {
System.out.println("input error");
return;
}
}
public int getValue() {
return this.value;
}
public void increment() {
value = (value + 1) % limit;
}
public String getDisplayValue() {
if (value >= 10)
return value + "";
else
return "0" + value;
}
}
public class ClockDisplay {
private NumberDisplay hours;
private NumberDisplay minutes;
private String displayString;
public ClockDisplay() {
hours = new NumberDisplay(24);
minutes = new NumberDisplay(60);
updateDisplay();
}
public ClockDisplay(int hour, int minute) {
hours = new NumberDisplay(24);
minutes = new NumberDisplay(60);
hours.setValue(hour);
minutes.setValue(minute);
updateDisplay();
}
public String getDisplayString() {
return this.displayString;
}
public void setTime(int hour, int minute) {
hours.setValue(hour);
minutes.setValue(minute);
updateDisplay();
}
public void timeTick() {
minutes.increment();
if (minutes.getValue() == 0)
hours.increment();
updateDisplay();
}
public void updateDisplay() {
displayString = hours.getDisplayValue() + ":" + minutes.getDisplayValue();
}
}
public class ClockTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
ClockDisplay clock = new ClockDisplay(10, 59);
System.out.println(clock.getDisplayString());
clock.timeTick();
System.out.println(clock.getDisplayString());
}
}
二、类的继承
1、Java继承基础
①使用继承改善系统类的结构
继承是一种解决重复问题的机制。
继承允许定义一个类作为另一个类的扩充版本。描述的是类与类之间的“is a ”关系,即子类是比父类更为特殊的类,是父类的一种特例。继承机制的优点在于为我们提供了一种类级的代码重用机制,使我们可以在现有类的基础上,通过重用、修改或添加等方法创建一个新类。
继承允许定义一个类作为另一个类的扩充版本。描述的是类与类之间的“is a ”关系,即子类是比父类更为特殊的类,是父类的一种特例。继承机制的优点在于为我们提供了一种类级的代码重用机制,使我们可以在现有类的基础上,通过重用、修改或添加等方法创建一个新类。
class SubClassName extends SupClassName{ }
②类成员的继承规则
③子类构造器
理解子类构造器:
在内存机制中,父类与子类占用同一块内存,只不过子类在父类的基础上增加了自己的部分,即子类依附于父类,先有父类再有子类。因此,构建对象时,也要先构建父类的数据部分,再考虑子类增加的部分。
在内存机制中,父类与子类占用同一块内存,只不过子类在父类的基础上增加了自己的部分,即子类依附于父类,先有父类再有子类。因此,构建对象时,也要先构建父类的数据部分,再考虑子类增加的部分。
子类构造器的一般形式:
子类类名(构造器参数列表){
super(与父类构造器方法参数相对的实参); //①调用父 类构造器
//②对子类成员的初始化
}
子类类名(构造器参数列表){
super(与父类构造器方法参数相对的实参); //①调用父 类构造器
//②对子类成员的初始化
}
实战演练:
1.编写一个矩形类Rect,该类包含:
两个私有属性:矩形的长length和宽width。
一个构造器方法:实现对length和width属性的初始化。
两个公有成员方法:分别用于计算并返回矩形的面积和周长。
两个私有属性:矩形的长length和宽width。
一个构造器方法:实现对length和width属性的初始化。
两个公有成员方法:分别用于计算并返回矩形的面积和周长。
2.编写一个具有确定位置的矩形类PlainRect,该类继承于Rect类,其确定位置用矩形的左上角坐标来标识,为该类添加:
两个属性:矩形左上角坐标startX和startY。
两个构造器方法:
(1)带4个参数的构造器方法,用于对startX、startY、width和height属性初始化;
(2)不带参数的构造器方法,将矩形初始化为左上角坐标、长和宽都为0的矩形;
一个方法:方法isInside(double x,double y)。用于判断某个点是否在矩形内部,如在矩形内,返回true, 否则,返回false
两个属性:矩形左上角坐标startX和startY。
两个构造器方法:
(1)带4个参数的构造器方法,用于对startX、startY、width和height属性初始化;
(2)不带参数的构造器方法,将矩形初始化为左上角坐标、长和宽都为0的矩形;
一个方法:方法isInside(double x,double y)。用于判断某个点是否在矩形内部,如在矩形内,返回true, 否则,返回false
3.编写上题PlainRect类的测试程序
(1)创建一个左上角坐标为(10,10),长为20,宽为10的矩形对象;
(2)计算并打印输出矩形的面积和周长;
(3)判断点(25.5,13)是否在矩形内,并打印输出相关信息。
(2)计算并打印输出矩形的面积和周长;
(3)判断点(25.5,13)是否在矩形内,并打印输出相关信息。
public class Rect {
// 定义两个私有变量
private double length;
private double width;
// 初始化器
public Rect(double length, double width) {
this.length = length;
this.width = width;
}
public Rect() {
this.length = 0;
this.width = 0;
}
// 获取长和宽的值
public double getLength() {
return this.length;
}
public double getWidth() {
return this.width;
}
// 求周长和面积
public double primeter() {
return 2 * (length + width);
}
public double area() {
return length * width;
}
public void printInfo() {
System.out.println("##################");
System.out.println("矩形的长为:" + length);
System.out.println("矩形的宽为:" + width);
System.out.println("矩形的周长为:" + primeter());
System.out.println("矩形的面积为:" + area());
System.out.println("##################");
}
}
class PlainRect extends Rect {
// 定义两个私有变量(初始点)
private double startX;
private double startY;
// 定义两个构造器
public PlainRect(double startX, double startY, double length, double width) {
super(length, width);
this.startX = startX;
this.startY = startY;
}
public PlainRect() {
super();
startX = 0;
startY = 0;
}
public boolean isInside(double x, double y) {
if (x > startX && x < (super.getLength() + startX) && y < startY && y > (y - super.getWidth()))
return true;
else
return false;
}
}
class RectTest {
public static void main(String[] args) {
PlainRect p1 = new PlainRect(10, 10, 20, 10);
p1.printInfo();
boolean result = p1.isInside(25.5, 13);
System.out.println("是否在矩形内:" + result);
PlainRect p2 = new PlainRect();
p2.primeter();
}
}
总结提高:继承描述的是一种“is a”的关系
2、深入Java继承
①方法重写
多数时候子类以父类为基础,增加新的属性和方法,但有时需要在子类中重写父类方法,称为方法的重写(Override)。
方法重写规则:
两同两小一大(子类重写方法与父类被重写方法比较)
方法名与参数列表相同;
返回类型与异常类型范围更小或相等;
访问控制权限更大或相等。
方法名与参数列表相同;
返回类型与异常类型范围更小或相等;
访问控制权限更大或相等。
子类重写父类方法时,将对从父类继承下来的方法进行覆盖,使父类的方法在子类中不复存在。
两种特例:
private方法无法被重写;
static方法只能被隐藏;
private方法无法被重写;
static方法只能被隐藏;
②变量隐藏
与方法一样,可以在子类中定义与父类中成员变量同名的成员变量
③Java的单继承结构
每个类最多只能有一个父类,只支持单继承;
Object类是Java的终极超类,所有类都是Object类的直接或间接子类。
子类继承沿继承路径向上的所有父类的有关属性和方法。
Object类是Java的终极超类,所有类都是Object类的直接或间接子类。
子类继承沿继承路径向上的所有父类的有关属性和方法。
④Object类:祖宗类
实战演练:
1. 编写Employee类,该类包含:
四个受保护属性:雇员的姓名name、工号number、住址address和薪水salary。
一个构造器方法:用于初始化name、number和salary属性。
两个公有成员方法:分别实现职员基本信息的输出和按比例涨工资的功能。
四个受保护属性:雇员的姓名name、工号number、住址address和薪水salary。
一个构造器方法:用于初始化name、number和salary属性。
两个公有成员方法:分别实现职员基本信息的输出和按比例涨工资的功能。
2.编写Manager类,该类继承于Employee类
(1)为其添加:
两个属性:办公室officeID和年终分红bonus;
一个构造器方法:带有4个参数的构造器方法,用于对除bonus属性外的所有其它属性进行初始化;
方法:officeID属性和bonus属性的相关set和get方法;
(2)重写Employee类中的方法raiseSalary(double proportion),经理涨工资的计算方法为在雇员工资涨幅的基础上增加10%的比例。
(1)为其添加:
两个属性:办公室officeID和年终分红bonus;
一个构造器方法:带有4个参数的构造器方法,用于对除bonus属性外的所有其它属性进行初始化;
方法:officeID属性和bonus属性的相关set和get方法;
(2)重写Employee类中的方法raiseSalary(double proportion),经理涨工资的计算方法为在雇员工资涨幅的基础上增加10%的比例。
3.编写TemporaryEmployee(临时工)类,该类继承于Employee类
(1)为其添加:
一个属性:雇佣年限hireYears;
构造器方法:用于初始化该类的所有属性;
方法:hireYears属性的set和get方法;
(2)重写Employee类中的方法raiseSalary(double proportion),临时工的工资涨幅为正式雇员的50%。
(1)为其添加:
一个属性:雇佣年限hireYears;
构造器方法:用于初始化该类的所有属性;
方法:hireYears属性的set和get方法;
(2)重写Employee类中的方法raiseSalary(double proportion),临时工的工资涨幅为正式雇员的50%。
public class Employee {
//四个受保护的属性
protected String name;
protected String number;
protected String address;
protected double salary;
//构造器方法
public Employee(String name,String number,double salary) {
this.name = name;
this.number = number;
this.salary = salary;
}
//两个公有成员方法
public void printInfo() {
System.out.println("######################");
System.out.println("职员的姓名为:"+name);
System.out.println("职员的工号为:"+number);
System.out.println("职员的住址为:"+address);
System.out.println("职员的薪水为:"+salary);
System.out.println("######################");
}
public void raiseSalary(double proportion) {
salary = salary*(1+proportion);
}
}
class Manager extends Employee{
//定义两个属性
private String officeID;
private double bonus;
//一个构造器方法
public Manager(String name,String number,double salary,String officeID) {
super(name,number,salary);
this.officeID = officeID;
}
//set和get方法
public String getOfficeID() {
return this.officeID;
}
public double getBonus() {
return this.bonus;
}
public void setOfficeID(String officeID) {
this.officeID = officeID;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
//重写方法
public void raiseSalary(double proportion) {
super.raiseSalary(proportion);
this.salary *= (1+0.1);
}
}
class TemporaryEmployee extends Employee{
//定义私有属性
private int hireYears;
//定义构造器
public TemporaryEmployee(String name,String number,double salary,int hireYear){
super(name,number,salary);
this.hireYears = hireYears;
}
//定义set和get方法
public int getHireYears() {
return this.hireYears;
}
public void setHireYears(int hireYears) {
this.hireYears = hireYears;
}
//重写方法
public void raiseSalary(double proportion) {
super.raiseSalary(proportion);
this.salary = super.salary - super.salary * 0.5 * proportion;
}
}
总结提高:“重写”的实质是一种特性的修改,子类除了继承、扩展父类外,还可以在父类的基础上进行修改。
Java只支持单继承,任何Java类只具有一个直接或间接父类。如果要实现多继承机制,需要使用接口。
3、对象类型转换与Java多态性
①继承结构中对象间的类型转换:
上溯造型:自动进行
Vehicle vehicle=new Truck(……);
Vehicle vehicle=new Truck(……);
下溯造型:强制转化
Vehicle vehicle=new Truck(……);
Truck truck=(Truck)vehicle;
Vehicle vehicle=new Truck(……);
Truck truck=(Truck)vehicle;
关于Java对象类型变量的几个概念:
1.多态变量:Java中对象类型变量为多态变量,指一个变量可以保存不同类型的对象。
2.静态类型:在源代码的变量声明语句中声明的类型。
3.动态类型:当前保存的对象类型。
使用instanceof判断对象类型:
对象类型的上朔造型使得可以将一个子类的实例赋值给一个父类类型变量。而在一些情况下需要判断变量到底是一个什么类型的对象。
使用instanceof操作符可以判断一个变量是否是右操作数指出的类的一个对象。
实例:观察下面代码段的运行结果
Vehicle vehicle=new Truck(……);
System.out.println(“是Truck对象吗?”+ vehicle instanceof Truck);
System.out.println(“是Vehicle对象吗?”+vehicle instanceof Vehicle);
Vehicle vehicle=new Truck(……);
System.out.println(“是Truck对象吗?”+ vehicle instanceof Truck);
System.out.println(“是Vehicle对象吗?”+vehicle instanceof Vehicle);
②多态变量、静态类型与动态类型
几个概念
方法动态绑定:在存在方法重写的继承结构中,对象变量的方法调用是由该对象变量的具体实例决定的。
方法多态性:相同的方法调用语句在不同的时候可能调用不同的方法,取决于变量中对象的动态类型。
③多态特性
使用多态特性编程的实用技巧:
有一个继承(或实现接口的)层次关系;
在子类中重写父类的(实现接口的)方法;
通过父类的引用对子类对象进行调用;
使用多态特性编程的实用技巧:
有一个继承(或实现接口的)层次关系;
在子类中重写父类的(实现接口的)方法;
通过父类的引用对子类对象进行调用;
实战演练:
设计EmployeeData类,要求:
定义1个ArrayList类型的变量,用于存放多有雇员的信息;
设计方法addEmployee()。用于向雇员列表中添加新的雇员对象;
设计方法salaryRaise()。用于为雇员列表中所有的雇员涨工资。
设计方法listEmployees()。用于输出所有雇员信息。
编写EmployeeData类的测试程序,要求:
至少创建3个对象,其中包括Employee对象、Manager对象和TemporaryEmployee对象。
为所有雇员涨工资,涨幅为10%;
输出所有雇员的信息。
定义1个ArrayList类型的变量,用于存放多有雇员的信息;
设计方法addEmployee()。用于向雇员列表中添加新的雇员对象;
设计方法salaryRaise()。用于为雇员列表中所有的雇员涨工资。
设计方法listEmployees()。用于输出所有雇员信息。
编写EmployeeData类的测试程序,要求:
至少创建3个对象,其中包括Employee对象、Manager对象和TemporaryEmployee对象。
为所有雇员涨工资,涨幅为10%;
输出所有雇员的信息。
import java.util.ArrayList;
import java.util.Iterator;
public class EmployeeData {
private ArrayList employees;
public EmployeeData() {
employees = new ArrayList();
}
public void addEmployee(Employee aemployee) {
employees.add(aemployee);
}
public void salaryRaise(Employee employee) {
employee.raiseSalary(0.1);
}
public void listEmployees() {
for(Iterator iter = employees.iterator();iter.hasNext();) {
Employee employee = (Employee)iter.next();
employee.printInfo();
System.out.println();
}
}
}
class EmployeeTest{
public static void main(String[] args) {
//正式雇员类
Employee e1 = new Employee("张三","001",2000);
//经理类
Manager m1 = new Manager("李四","010",5000,"ABC");
//临时工类
TemporaryEmployee t1 = new TemporaryEmployee("王五","110",1000,2);
EmployeeData e = new EmployeeData();
e.addEmployee(e1);
e.addEmployee(m1);
e.addEmployee(t1);
e.salaryRaise(e1);
e.salaryRaise(m1);
e.salaryRaise(t1);
e.listEmployees();
}
}
总结与提高:
多态是面向对象方法中一个非常重要的概念
在程序中利用多态技术编程,能使代码的组织和可读性均能获得改善,并能有效提高应用程序的扩展性。
观点:尽可能的利用多态性编程
4、抽象方法与抽象类
关键字:abstract
将父类定义为抽象类后,父类的抽象方法也加上abstract,用分号结束,不需要方法体。
注意:子类不继承父类的抽象类方法时,要将子类也定义为abstract类。
public abstract class Animal {
private String type;
public Animal(String type){
this.type = type;
}
public abstract void sound();
public String toString(){
return "这是一只"+type;
}
}
class Cat extends Animal{
public Cat(String type){
super(type);
}
public void sound(){
System.out.println("喵喵喵");
}
}
class Dark extends Animal{
public Dark(String type){
super(type);
}
public void sound(){
System.out.println("嘎嘎嘎");
}
}
class AnimalTest{
public static void printInfo(Animal a){
a.sound();
}
public static void main(String args[]){
Cat c = new Cat("波斯");
printInfo(c);
}
}
5、接口
①接口的定义与实现:
抽象类中可以同时包含抽象方法和非抽象方法。通常将各子类实现不同的方法定义为父类的抽象方法,而将各子类实现相同的方法定义为父类的具体方法,以达到代码复用的目标。考虑当抽象类当中所有的方法都被定义为抽象方法的极端情况,此时,父类仅仅担当了一个框架作用,没有任何实现细节,所有的工作都由子类来承担。Java与其他面向对象程序设计语言一样,使用接口机制来描述这种情况和处理相关的问题。概括来说,接口是常量和抽象方法的集合,是所有实现接口的子类的“协议”与“规范”。
定义格式:[访问控制修饰符] interface InterfaceName {
// 接口体
}
实现格式:[访问控制修饰符] SubClass implements InterfaceName {
//类体
}
▲注意:接口中定义的方法的默认访问控制修饰符为:public abstract
若接口中定义了属性,则默认为 public static final
项目实战:
1、定义一个Instrument接口,接口里包含了play() , what() , adjust()的接口体
2、定义两个实现体:Piano和Violin,
3、调用了play()输出乐器.play(),调用what()是显示乐器名称,调用adjust()输出乐器.adjust()
interface Instrument {
void play();
String what();
void adjust();
}
class Piano implements Instrument{
public void play() {
System.out.print("Piano.play()");
}
public String what() {
return "Piano";
}
public void adjust() {
System.out.println("Piano.adjust()");
}
}
class Violin implements Instrument{
public void play() {
System.out.println("Violin.play()");
}
public String what() {
return "Violin";
}
public void adjust() {
System.out.println("Violin.adjust");
}
}
class InstrumentTest{
public static void main(String[] args) {
Piano p = new Piano();
Violin v = new Violin();
System.out.println(p.what());
System.out.println(v.what());
}
}
6、深入接口
可以通过继承接口创建新的接口
使用extends关键字实现接口的继承
使用extends关键字实现接口的继承
Java不支持类的多继承,但可以通过接口实现多继承机制;
一个类只能继承于一个父类,但能同时实现多个接口;
一个类只能继承于一个父类,但能同时实现多个接口;
实战演练:
1、编写一个Shape接口,内置抽象方法area()
2、编写Circle类(含有radius和area()方法)和Rectangle类(含有length,width和area()方法
3、编写两个接口继承于Shape,如下图
4、创建一个三维图形类Spere(球体),它实现Shape3D接口。
interface Shape {
//double PI = 3.14;
double area();
}
interface Shape2D extends Shape{
double perimeter();
}
interface Shape3D extends Shape{
double volumn();
}
interface Scaleable{
void scale(double proportion);
}
class Circle implements Shape2D{
private double radius;
public Circle(double radius){
this.radius = radius;
}
public void setRadius(double radius){
this.radius = radius;
}
public double getRadius(){
return this.radius;
}
public double area(){
//return PI * radius * radius;
return Math.PI * radius * radius;
}
public double perimeter(){
return 2 * Math.PI * radius;
}
}
class Rectangle implements Shape3D{
private double length;
private double width;
private double height;
public Rectangle(double length,double width,double height){
this.length = length;
this.width = width;
this.height = height;
}
public void setLWH(double length,double width,double height){
this.length = length;
this.width = width;
this.height = height;
}
public double getLength(){
return this.length;
}
public double getWidth(){
return this.width;
}
public double getHeight(){
return this.height;
}
public double area(){
return length*width;
}
public double volumn(){
return length*width*height;
}
}
class Spere implements Shape3D{
private double radius;
public Spere(double radius){
this.radius = radius;
}
public void nsetRadius(double radius){
this.radius = radius;
}
public double getRadius(){
return this.radius;
}
public double area(){
return 4*Math.PI*radius*radius;
}
public double volumn(){
return 4/3*Math.PI*radius*radius*radius;
}
}
class CircleScaleable extends Circle implements Scaleable{
public CircleScaleable(double radius){
super(radius);
}
public void scale(double proportion){
this.setRadius(this.getRadius()*proportion);
}
}
class Rectangleable extends Rectangle implements Scaleable{
public Rectangleable(double length,double width,double height){
super(length,width,height);
}
public void scale(double proportion){
this.setLWH(this.getLength()*proportion, this.getWidth()*proportion, this.getHeight()*proportion);
}
}
//测试类
class Test{
public static void main(String[] args){
CircleScaleable circle = new CircleScaleable(5.0);
System.out.println(circle.getRadius());
circle.scale(0.5);
System.out.println(circle.getRadius());
Rectangleable rectangle = new Rectangleable(10,20,30);
System.out.println(rectangle.getLength());
rectangle.scale(0.5);
System.out.println(rectangle.getLength());
}
}
怎样理解基于接口编程?
接口充分体现了规范和实现分离的设计思想。基于接口的设计一种松耦合设计。这种松耦合能为系统提供更好的可扩展性和可维护性。