第5章 消息、继承与多态
一.消息
在对象之间的联系是通过消息来传递的
消息应该含有:对象名、方法名、实际参数、返回值或操作结果
共有消息和私有消息
特定于对象的消息可以分为以下三种类型:可以返回对象内部状态的消息,可以改变对象内部状态的消息,可以做一些特性操作改变系统状态的消息。
class Student{ public String name; public char sex; public int no; public int age; Student(int cno,String cname,char csex,int cage){ name=cname; sex=csex; no=cno; age=cage; } public void showNo(){ System.out.println("No:"+no); } public void showName(){ System.out.println("Name:"+name); } public void showSex(){ System.out.println("Sex:"+sex); } public void showAge(){ System.out.println("Age:"+age); } } class StudentScore{ private int no; private double score; public void sendScore(int cno,double cscore){ no=cno; score=cscore; } void printScore(){ System.out.println("No:"+no+" score:"+score); } } public class C5_1 { public static void main(String[] args){ int m; Student st1=new Student(101,"Zhang li",'F',18); Student st2=new Student(102,"hong bing",'M',17); StudentScore sc1=new StudentScore(); StudentScore sc2=new StudentScore(); st1.showNo(); st1.showName(); st1.showAge(); st1.age=20; m=st1.age; System.out.println("m="+m); st2.showNo(); st2.showName(); sc1.sendScore(101,97); sc2.sendScore(102,84); sc1.printScore(); sc2.printScore(); } }
非访问控制
类:final,obstract
数据成员:final,static
二.访问控制
1.公共访问控制符public
import这个public类就能访问这个类内部可见的数据成员和引用它的可见方法,只有当public类的数据成员和成员方法的访问控制符也被声明为public时,这个类的所有用public修饰的数据承运和成员方法才同时对其它类可见。但是安全性下降。
2.缺省访问控制符
缺省访问控制特性称为“友好访问”,只有在同一个包中的对象才能访问和引用这些类。
同一个包中用private修饰的父类的数据成员不能被子类的实例对象引用
class P1{ private int n=9; int nn; P1(){ nn=n++; } void ma(){ System.out.println("n="+n); } } public class C5_3 extends P1 { public static void main(String[] args){ P1 m1=new P1(); System.out.println("m1.nn="+m1.nn); //System.out.println("m1.n="+m1.n);这句话是错误的,不能调用private m1.ma(); } }
当父类的数据成员用protected修饰时,不能被其他包的实例对象访问
三.多态机制
1.Java提供两种多态机制,重载与覆盖
同命不同参的成员方法->不同响应
2.重载
主要通过形式参数列表中参数的个数,参数的数据类型和参数的顺序等方面的不同来区分
重载方法区别:参数个数、参数顺序、参数类型
编译器根据实际参数选择并执行对应的重载方法
public class C5_5{ static int add(int a,int b){ return a+b; } static double add(double x,double y){ return x+y; } static double add(double x,double y,double z){ return x+y+z; } public static void main(String[] args){ System.out.println("Sum is:"+add(8.5,2.3)); System.out.println("Sum is:"+add(21,35)); System.out.println("Sum is:"+add(8.5,2.3,8.5+2.3)); } }
四.继承机制
1.继承的概念
把一般类的对象实例和所有特殊类的对象实例都共同具有的属性和操作一次性的在一般类中进行显式定义,在特殊类中不再重复的定义一般类中已经定义的东西,但是在语义上,特殊类自动的银行的拥有他的一般类中定义的属性和操作。
2.继承的特性
(1)关系是传递的
(2)能清晰地体现相关类间的层次结构关系
(3)提供软件复用功能
(4)减少接口和界面
(5)提供多继承,Java仅仅支持单继承,二通过接口机制实现多继承
3.Java用extends指明继承关系
继承机制通过extends关键字实现,新定义的子类可以继承父类的所有非private属性和方法
(1)数据成员的继承:子类可以继承父类的所有非私有的数据成员
class A1{ int x=25; private int z; } public class C5_6 extends A1{ public static void main(String[] args){ C5_6 p=new C5_6(); System.out.println("p.x="+p.x); } }
(2)数据成员的隐蔽
数据成员的隐蔽是指子类中重新定义一个与父类中已定义的数据成员名完全相同的数据成员
class A11{ int x=8; } public class C5_7 extends A11 { int x=24; public static void main(String[] args){ int s1,s2; A11 p=new A11(); C5_7 p1=new C5_7(); s1=p.x; s2=p1.x; System.out.println("s1="+s1); System.out.println("s2="+s2); } }
(3)成员方法的继承
子类可以继承父类的非私有成员方法
class A2{ int x=0,y=1; void Myp(){ System.out.println("x="+x+" y="+y); } private void Printme(){ System.out.println("x="+x+" y="+y); } } public class C5_8 extends A2{ public static void main(String[] args){ int z=3; C5_8 p1=new C5_8(); p1.Myp(); //p1.Printme();错的,无法调用private类型方法 } }
(4)成员方法的覆盖
方法的覆盖与数据成员的隐藏的不同之处在于:子类隐藏父类的数据成员使其不可见,父类同名的数据成员在子类对象中仍然站有自己独立的内存空间,子类方法对父类同名方法的覆盖将清除父类方法占用的内存,从而使父类方法在子类对象中不复存在。
class A3{ int x=10; int y=31; public void Printme(){ System.out.println("x="+x+" y="+y); } } public class C5_9 extends A3{ int z=35; public void Printme(){ System.out.println("z="+z); } public static void main(String[] args){ A3 p2=new A3(); C5_9 p1=new C5_9(); p1.Printme(); p2.Printme(); } }
子类再重新定义父类已有方法的时候,应保持与父类完全相同的方法名,返回值类型和参数列表,否则就不是方法覆盖,而是子类定义自己特有的方法,与父类方法无关。
4.this与super
this使用场合
用来访问当前对象的数据成员:this.数据成员
用来访问当前对象的成员方法:this.成员方法(参数)
当有重载的构造方法时,用来引用同类的其他构造方法:this(参数)
this的使用
class A4{ int x=0; int y=1; public void Printme(){ System.out.println("x="+x+" y="+y); System.out.println("I am an "+this.getClass().getName()); } } public class C5_10 extends A4{ public static void main(String args[]){ C5_10 p1=new C5_10(); p1.Printme(); } }
当类的数据成员名和成员方法的形参名相同时,借助this来表示引用的是类数据成员
class AlassArea{ double x,y; double area(double x,double y){ double s; this.x=x; this.y=y; s=this.x*this.y; return s; } } public class C5_11 extends AlassArea{ public static void main(String[] args){ double a=2.2,b=3.1,z; C5_11 ss=new C5_11(); z=ss.area(a,b); System.out.println("z="+z); } }
super的使用场合
表示的是当前对象的直接父类对象,是当前对象的直接父类对象的引用。
使用方法:
(1)用来访问直接父类隐藏的数据成员:super.数据成员
(2)用来调用直接父类中被覆盖的成员方法:super.成员方法(参数)
(3)用来调用直接父类的构造方法:super(参数)
class A5{ int x=4; int y=1; public void printme(){ System.out.println("x="+x+" y="+y); System.out.println("class name: "+this.getClass().getName()); } } public class C5_13 extends A5 { int x; public void printme(){ int z=super.x+6; super.printme(); System.out.println("I am an "+this.getClass().getName()); x=5; System.out.println("z="+z+" x="+x); } public static void main(String[] args){ int k; A5 p1=new A5(); C5_13 p2=new C5_13(); p1.printme(); p2.printme(); } }
5.构造方法的重载与继承
重载:这个调用语句应该是整个构造方法的第一条可执行语句
class Addclass{ public int x=0,y=0,z=0; Addclass(int x){ this.x=x; } Addclass(int x,int y){ this(x); this.y=y; } Addclass(int x,int y,int z){ this(x,y); this.z=z; } public int add(){ return x+y+z; } } public class C5_14 { public static void main(String[] args){ Addclass p1=new Addclass(2,3,5); Addclass p2=new Addclass(10,20); Addclass p3=new Addclass(1); System.out.println("x+y+z="+p1.add()); System.out.println("x+y="+p2.add()); System.out.println("x="+p3.add()); } }
继承:
子类无条件地继承父类的不含参数的构造方法
如果子类自己没有构造方法,则它将继承父类的无参数构造方法。如果子类自己定义了构造方法,则在创建新对象时,他将限制性继承自弗雷德无参数构造方法,然后再执行自己的构造方法
对于父类的含参数构造方法,子类是可以通过在自己的构造方法中使用super关键字调用它,但是这个调用语句必须是子类构造方法的第一条可执行语句
class Addclass2{ public int x=0,y=0,z=0; Addclass2(int x){ this.x=x; } Addclass2(int x,int y){ this.x=x; this.y=y; } Addclass2(int x,int y,int z){ this.x=x; this.y=y; this.z=z; } public int add(){ return x+y+z; } } public class C5_15 extends Addclass2{ int a=0,b=0,c=0; C5_15(int x){ super(x); a=x+7; } C5_15(int x,int y){ super(x,y); a=x+5; b=y+7; } C5_15(int x,int y,int z){ super(x,y,z); a=x+4; b=y+4; c=z+4; } public int add(){ System.out.println("super:x+y+z="+super.add()); return a+b+c; } public static void main(String[] args){ C5_15 p1=new C5_15(2,3,5); C5_15 p2=new C5_15(10,20); C5_15 p3=new C5_15(1); System.out.println("a+b+c="+p1.add()); System.out.println("a+b="+p2.add()); System.out.println("a="+p3.add()); } }
6.向方法传递对象
传递给方法的参数若是变量,则只能有实参传递给形参,而不能有形参带回
在引用过程中,对于形参变量值的修改并不影响实参变量值
传递给方法的参数若是对象,则是参与形参的对象的引用指向同一个变量,因此成员方法中对对象的数据成员的修改,会使实参对象的数据成员之也发生同样的变化
这种参数的传递方式被称为双向地址传递
7.继承与封装的关系
继承是一种静态共享代码的手段
封装机制所提供的是一种动态共享代码的手段,通过封装到一个类,创建该类的实例,同样也能达到共享的目的
五.抽象类、接口与包
1.抽象类
是所有子类公共属性特征的集合
在抽象类派生的子类中必须实现抽象类中定义的所有抽象方法
(1)凡是用abstract修饰符修饰的类被称为抽象类。凡是用abstract修饰符修饰的成员方法被称为抽象方法
(2)抽象类中可以有0个或多个抽象方法,也可以包含非抽象的方法
(3)抽象类中可以没有抽象方法,但是,由抽象方法的类必须是抽象类
(4)对于抽象方法来说,在抽象类中只指定其方法名及其类型,而不书写其实现代码
(5)抽象类可以派生子类,在抽象类派生的子类中必须实现抽象类中定义的所有抽象方法
(6)抽象类不能创建对象,创建对象的工作由抽象类派生的子类来实现
(7)如果父类中已经有同名的abstract方法,则子类中就不能再用同名的抽象方法
(8)abstract不能与final并列修饰同一个类
(9)abstract不能与private、static、final或native并列修饰同一个方法
2.接口
多重继承是指一个子类可以有多个直接父类,该子类可以全部或部分继承所有直接父类的数据成员及成员方法。
[修饰符]interface 接口名[extends 父接口名列表] { 常量数据成员声明 抽象方法声明 }
interface是声明接口的关键字,可以把它看成一个特殊类
接口名要求符合Java标识符规定
修饰符有两种:public和默认
父接口名列表。定义一个接口时可以通过extends关键字声明该接口是某个已经存在的父接口的派生接口,他将继承父接口的所有属性和方法
常量数据成员执勤可以有也可以没有修饰符,是public static和final static,接口中的数据成员都是用final修饰的常量:修饰符 数据成员类型 数据成员名=常量值 或 数据成员名=常量值
抽象方法声明,接口中的方法都是用abstract修饰的抽象方法
定义接口注意事项:
接口定义用关键字interface而不是class
接口中定义的数据成员全是final static修饰的,即常量
接口中没有自身的构造方法,所有成员方法都是抽象方法
接口也具有继承性,可以通过extends关键字声明该接口的父接口
类实现接口的注意事项
用implements关键字就可以调用接口,一个类若要调用多个接口,可在implements后用逗号隔开多个接口的名字
如果实现某接口的类不是abstract的抽象类,则在类的定义部分必须实现指定接口的所有抽象方法
如果实现某接口的类是abstract的抽象类,则它可以不实现该接口所有的方法
接口的抽象方法的访问限制符都已指定为public,所以类在实现方法时,必须显式地使用publilc修饰符,否则将被系统警告为缩小了接口中定义的方法的访问控制访问