一、继承
定义:继承(inheritance):从已有的类派生出新的类。
继承可以让你定义一个通用的类,该通用类包含一些不同类的公共特征和动作;以该通用类扩展别的特定的类。
1.1 父类和子类
如果一个类c1继承于类c2,那么c1称为次类(subclass)或者子类(child class)、扩展类(extended class)、派生类(derived class);C2称为超类(superclass)或者父类(parent class)、基类(base class)。
子类可以从其父类中继承可以访问的数据域和方法,还可以添加新的数据域和新方法。
继承的实例:先创建一个通用的几何类,由几何类派生出圆、和矩形类:
几何类(基类):
package demo; /** * GeometricObject
* Created by luts on 2015/11/28. */ public class GeometricObject { private String color = "white"; private boolean filled; private java.util.Date dateCreadted; public GeometricObject(){ dateCreadted = new java.util.Date(); } public GeometricObject(String newColor, boolean newFilled){ dateCreadted = new java.util.Date(); this.color = newColor; this.filled = newFilled; } public String getColor(){ return color; } public void setColor(String newColor){ this.color = newColor; } public boolean isFilled(){ return filled; } public void setFilled(boolean newFilled){ this.filled = newFilled; } public java.util.Date getDateCreadted(){ return dateCreadted; } public String toString(){ return "creadted on " + dateCreadted + "\ncolor: " + color + " and filled: " + filled; } }
圆类(子类):
package demo; /** * cricle * Created by luts on 2015/11/28. */ public class Circle extends GeometricObject { private double radius; public Circle(){ } public Circle(double newRadius){ this.radius = newRadius; } public Circle(double newRadius, String newColor, boolean newFilled){ this.radius = newRadius; setColor(newColor); setFilled(newFilled); } public double getRadius(){ return radius; } public double getArea(){ return radius * radius * Math.PI; } public double getDiameter(){ return 2 * radius; } public double getPerimeter(){ return 2* radius * Math.PI; } public void printCircle(){ System.out.println("The circle is creadted " + getDateCreadted() + " and the raius is " + radius); } }
矩形类(子类):
package demo; /** * Rectangle extends from GeometricObject * Created by luts on 2015/11/28. */ public class Rectangle extends GeometricObject{ private double width; private double height; public Rectangle(){ } public Rectangle(double newWidth, double newHeight){ this.width = newWidth; this.height = newHeight; } public Rectangle(double newWidth, double newHeight, String newColor, boolean newFilled){ this.width = newWidth; this.height = newHeight; setColor(newColor); setFilled(newFilled); } public double getWidth(){ return width; } public void setWidth(double newWidth){ this.width = newWidth; } public double getHeight(){ return height; } public void setHeight(double newHeight){ this.height = newHeight; } public double getArea(){ return width * height; } public double getPerimeter(){ return 2 * (width + height); } }
测试类:
package demo; /** * test for circle and rectangle * Created by luts on 2015/11/28. */ public class TestCircleRectangle { public static void main(String[] args){ //create a circle Circle circle = new Circle(1); System.out.println("A circle "+ circle.toString()); System.out.println("The radius is: " + circle.getRadius() + "\nThe area is: " + circle.getArea()); System.out.println("The diameter is: "+ circle.getPerimeter()); //create a rectangle Rectangle rectangle = new Rectangle(2,4); System.out.println("\nA rectangle "+ rectangle.toString()); System.out.println("The area is: " + circle.getArea()); System.out.println("The diameter is: "+ rectangle.getPerimeter()); } }
输出结果:
A circle creadted on Sat Nov 28 10:27:04 CST 2015 color: white and filled: false The radius is: 1.0 The area is: 3.141592653589793 The diameter is: 6.283185307179586 A rectangle creadted on Sat Nov 28 10:27:04 CST 2015 color: white and filled: false The area is: 3.141592653589793 The diameter is: 12.0
Circle和Rectangle类继承GeometricObject类中所有可以访问的数据域和方法,另外两个类都重新定义了属于自己的求面积和周长的方法。GeometricObject类中的color和filled数据域是私有的,只能在GeometricObject类中访问,所有在子类中,如果要修改这两个属性,需要通过set和get方法。如:子类中重载的构造函数。
小结:
-
- 1)子类并不是父类的一个子集,子类包含的信息通常比父类还多
- 2)父类中的私有数据域在父类之外是不可访问的。如果子类中要访问父类的私有数据域,需要在父类中定义其get、set方法,通过访问器和构造器来访问和修改。
- 3)继承关系是is-a关系建模。一个父类和子类之间必须存在是关系。
- 4)java中不允许多继承,一个java类只能继承一个父类 ,即单一继续。多继承可以通过接口实现。
1.2. 使用super关键字
super关键字的作用:
·1)调用父类的构造方法
super(); //调用父类的无参构造方法 super(parameter); //调用父类的有参构造方法
语句super()或super(参数)必须出现在子类构造方法的第一行,这是显示调用父类构造方法的唯一方法。
例如上例中的circle类中的一个含参构造方法可以替换为:
//原来的定义 public Circle(double newRadius, String newColor, boolean newFilled){ this.radius = newRadius; setColor(newColor); setFilled(newFilled); } //调用父类的构造方法 public Circle(double newRadius, String newColor, boolean newFilled){ super(newColor, newFilled); this.radius = newRadius; }
注意: 1. 要调用父类的构造方法必须使用super关键字,而且调用super语句必须是子类构造方法的第一条语句。在子类中调用父类的构造方法的名字会引起语法错误。
2. 构造方法可以用来构造一个类的实例。父类的构造方法不能被子类继承,只能通过super关键字调用。
3. 构造方法可以调用重载的构造方法或者其父类的构造方法。如果没有显示的调用,编译器会自动将super()作为构造方法的第一条语句:
public ClassName() { } 等价于: public ClassName() { super(); } public ClassName(double a) { } 等价于: public ClassName(double a) { super(); }
4. 构造一个类的实例的时候,将会调用沿着继承链的所有父类的构造方法。——构造方法链
·2)调用父类的方法
super关键字不仅可以调用父类的构造方法,还可以调用父类的方法:
super.方法名 (参数);
例如上例中的Circle类的一个方法: public void printCircle(){ System.out.println("The circle is creadted " + getDateCreadted() + " and the raius is " + radius); } 可以写出: public void printCircle(){ System.out.println("The circle is creadted " + super.getDateCreadted() + " and the raius is " + radius); }
1.3. 覆盖方法
方法覆盖(method overriding):当子类需要修改父类中的方法实现。
例如上例中的GeometricObject中的toString方法返回的是几何对象的字符串,这个方法在Circle中可以被覆盖:
public String toString(){ return super.toString() + "\nradius is " + radius; }
注意:· 1)仅当实例方法是可被访问的时候,它才能被覆盖。父类中的私有方法不能被子类覆盖。
· 2)虽然静态方法也能被子类继承,但是不能被覆盖。如果父类中的静态方法被子类重新定义,那么父类中的静态方法会被隐藏。可以使用:父类名.静态方法名(SuperClassName.staticMethodName)来调用静态方法。
1.4 覆盖和重载
重载(overload):对于类的方法(包括从父类中继承的方法),方法名相同,参数列表不同的方法之间就构成了重载关系。
----参数列表又叫参数签名,包括:参数的类型,参数的个数,参数的顺序。这三者只要有一个不同就叫做参数列表不同。
覆盖 (override):也叫重写,就是在当父类中的某些方法不能满足要求时,子类中改写父类的方法。当父类中的方法被覆盖了后,除非用super关键字,否则就无法再调用父类中的方法了。
发生覆盖的条件:
· 1、子类和父类的方法名称,参数列表,返回类型必须完全相同,而且子类方法的访问修饰符的权限不能比父类低。
· 2、子类方法不能抛出比父类方法更多的异常。即子类方法所抛出的异常必须和父类方法所抛出的异常一致,或者是其子类,或者什么也不抛出;
· 3、被覆盖的方法不能是final类型的。因为final修饰的方法是无法覆盖的。
· 4、被覆盖的方法不能为private。否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。
· 5、被覆盖的方法不能为static。所以如果父类中的方法为静态的,而子类中的方法不是静态的,但是两个方法除了这一点外其他都满足覆盖条件,那么会发生编译错误。反之亦然。即使父类和子类中的方法都是静态的,并且满足覆盖条件,但是仍然不会发生覆盖,因为静态方法是在编译的时候把静态方法和类的引用类型进行匹配。
方法的覆盖和重载具有以下相同点:
· 都要求方法同名
· 都可以用于抽象方法和非抽象方法之间
方法的覆盖和重载具有以下不同点:
-- 方法覆盖要求参数列表(参数签名)必须一致,而方法重载要求参数列表必须不一致。
-- 方法覆盖要求返回类型必须一致,方法重载对此没有要求。
-- 方法覆盖只能用于子类覆盖父类的方法,方法重载用于同一个类中的所有方法(包括从父类中继承而来的方法)
-- 方法覆盖对方法的访问权限和抛出的异常有特殊的要求,而方法重载在这方面没有任何限制。
-- 父类的一个方法只能被子类覆盖一次,而一个方法可以在所有的类中可以被重载多次。
覆盖:
public class Test{ public static void main(String[] args){ A.a = new A(); a.p(10); a.p(10.0); } class B{ public void p(double i){ System.out.println(i * 2); } } class A extends B(){ public void p(double i){ System.out.println(i); } } }
上述程序运行输出都是10.0。因为调用的都是A中定义的p (double i)方法。
重载:
public class Test{ public static void main(String[] args){ A.a = new A(); a.p(10); a.p(10.0); } class B{ public void p(double i){ System.out.println(i * 2); } } class A extends B(){ public void p(int i){ System.out.println(i); } } }
输出:10 //调用A类中的p(int i)方法
20.0 //调用B类中的p(double i)方法
1.5. 对象类Object和它的toString()方法
java中的每个类都起源于java.lang.Object类。如果一个类没有指定继承性,那么其父类就是Object类。
Object的toString()方法返回描述一个对象的字符串。
上例中的GeometricObject类重新了toString()方法:
public String toString(){ return "creadted on " + dateCreadted + "\ncolor: " + color + " and filled: " + filled; }
二、多态(dynamic)
多态:父类型的变量可以引用子类型的对象。
先看两个概念:声明类型和实际类型。声明类型是变量被声明的某种类型,实例可以用声明类型或者其子类型的构造方法创建。实际类型是被变量引用的对象的实际类。
Object a = new GeometricObject(); System.out.println(a.toString());
这里a声明的类型是Object,实际类型是GeometricObject,a指向使用new GeometricObject()创建的对象。a调用的是实际类型中的toString()方法。
多态存在的三个必要条件
1、要有继承;
2、要有重写;
3、父类引用指向子类对象。
package demo; /** * Created by luts on 2015/11/28. */ public class DynamicBindingDemo { public static void main(String[] args) { m(new GraDuateStudent()); m(new Student()); m(new Person()); m(new Object()); } public static void m(Object x) { System.out.println(x.toString()); } } class GraDuateStudent extends Student{ } class Student extends Person{ public String toString(){ return "Student"; } } class Person extends Object{ public String toString(){ return "Person"; } }
输出:
Student
Student
Person
java.lang.Object@1540e19d
三、对象转换与instanceof运算符
语句 Object a = new Student() 是合法的,称为隐式转换。---(1)
Student b = (Student) a; 称为显式转换。------(2)
(1)将一个子类的实例转换为一个父类的变量,称为向上转换,是可行的,因为子类的实例也是父类的实例。
(2)当把一个父类的实例转换成它的子类变量的时候(向下转换),必须使用“(子类名)”进行显示转换。
进行类型转换是因为在编译时声明类型决定了匹配的方法。通常将变量定义为父类型,这样就可以接收任何子类型的值。
instanceof运算符用于判断两个变量是不是同一类型。
object 的equals方法:测试两个对象是否相等。
object中的equals方法的签名: public boolean equals(Object obj){ return (this == obj) } 调用语法: object1.equals (object2)
这里的 == 比较运算符用来比较两个基本数据类型是否相等,或者判断两个对象是否具有相同的引用。
练习:答答出租车系统:(这里将数据域都设置为public了)
交通工具类:
package project1; public abstract class TransTool { public String name; public int price; public int rentDay; //public int capacity; //public int weight; public abstract void runPlay(); public int RentBill(){ return this.price * this.rentDay; } }
货车类:
package project1; public class GoodsCar extends TransTool { int weight; public GoodsCar(String newName, int newPrice, int newRentday, int newWeight){ this.name = newName; this.price = newPrice; this.rentDay = newRentday; this.weight = newWeight; } @Override public void runPlay() { // TODO Auto-generated method stub System.out.println(name+" 租金:"+ price + "元每天,载货:"+weight+"吨"); } }
载人的车类:
package project1; public class PassengerCar extends TransTool { public int capacity; public PassengerCar(String str, int newPrice, int newRentday, int newCapacity){ this.name = str; this.price = newPrice; this.rentDay = newRentday; this.capacity = newCapacity; } @Override public void runPlay() { // TODO Auto-generated method stub System.out.println(name+" 租金:"+ price + "元每天,载人:"+capacity+"人"); } }
皮卡车:
package project1; public class Pika extends TransTool { public int capacity; public int weight; public Pika(String newName, int newPrice, int newrentDay, int newCapacity, int newWeight){ this.name = newName; this.price = newPrice; this.rentDay = newrentDay; this.capacity = newCapacity; this.weight = newWeight; } public void runPlay() { // TODO Auto-generated method stub System.out.println(name + "租金"+price+"元每天,载人"+capacity+"人,载货"+weight+"吨"); } }
测试类:
package project1; import java.util.*; public class Initial { public static void main(String[] args) { // TODO Auto-generated method stub String[] names = {"AodiA4","Mazida6","Jinlong","Pika","Songhuojiang","Yiweike"}; String []carName = {"奥迪A4","马自达6","金龙","皮卡雪6","松花江","依维柯"}; int[] carCapacity={4,4,20,4,2,4,20}; int[] prices={500, 400, 800, 450, 400, 1000}; Scanner input = new Scanner(System.in); System.out.println("欢迎使用答答租车系统:"); System.out.println("您是否需要租车:1 是 0否"); int choice = input.nextInt(); if(choice == 1){ System.out.println("您可租车的类型及其价格表:"); System.out.println("序号 汽车名称 租金 容量"); System.out.println("1. 奥迪A4 500元/天 载人:4人 "); System.out.println("2. 马自达6 400元/天 载人:4人 "); System.out.println("3. 金龙 800元/天 载人:20人"); System.out.println("4. 皮卡雪6 450元/天 载人:4人 载货:2吨 "); System.out.println("5. 松花江 400元/天 载货:4吨 "); System.out.println("6. 依维柯 1000元/天 载货:20吨 "); System.out.println("请输入您要租汽车的数量: "); int sumCapacity = 0; int sumWeight = 0; int sumBill = 0; int day; int i ; int num = input.nextInt(); int[] carArray = new int[num]; TransTool []car = new TransTool[num]; for( i = 1; i <= num; i++){ System.out.println("请选择第"+i+"辆汽车类型"); carArray[i-1] = input.nextInt(); } System.out.println("请输入租车天数:"); day = input.nextInt(); for(i = 0; i < carArray.length;i++){ int Index = carArray[i]; if(Index < 4){ PassengerCar carTemp = new PassengerCar(names[Index - 1],prices[Index - 1],day,carCapacity[Index - 1]); sumCapacity +=carTemp.capacity; car[i] = carTemp; } else if(Index == 4){ Pika carTemp = new Pika("Pika",450,day,4,2); sumCapacity += carTemp.capacity; sumWeight += carTemp.weight; car[i] = carTemp; } else{ GoodsCar carTemp = new GoodsCar(names[Index - 1], prices[Index - 1], day, carCapacity[Index]); sumWeight += carTemp.weight; car[i] = carTemp; } sumBill += car[i].RentBill(); } input.close(); System.out.println("您的账单:"); System.out.println("***可载人的车有***"); for(i = 0; i < carArray.length;i++){ if(carArray[i] <= 4) System.out.print(carName[carArray[i] - 1]+" "); } System.out.println(" 共载人:"+sumCapacity); System.out.println("***可载货的有***"); for(i = 0; i < carArray.length;i++){ if(carArray[i] >= 4) System.out.print(carName[carArray[i] - 1]+" "); } System.out.println(" 共载货:"+sumWeight); System.out.println("***租车总价格:"+ sumBill); } else{ System.out.println("欢迎您下次使用答答出租系统,再见!"); } } }