Upload
anoki
View
101
Download
0
Embed Size (px)
DESCRIPTION
第 4 章 继承、多态与接口. 4.1 继承的概念 4.2 访问控制符 4.3 多态性 4.4 理解 final 修饰 4.5 抽象类和抽象方法 4.6 接口 4.7 内嵌类 4.8 对象引用转换. 父类 或超类。实际上是所有子类的公共域和公共方法的集合 子类 ,父类的特殊化 , 是对公共域和方法在功能、内涵方面的扩展和延伸 ,祖先类的所有成员均将成为子类拥有的 “ 财富 ” Object 类 是所有类的祖先. 继承的概念. class Student { // 未使用继承 - PowerPoint PPT Presentation
Citation preview
继承的概念 • 父类或超类。实际上是所有子类的公共域和公共方法的集合• 子类,父类的特殊化 ,是对公共域和方法在功能、内涵方面的扩展和延伸 ,祖先类的所有成员均将成为子类拥有的“财富” • Object类是所有类的祖先
Java继承的实现 class Student { //未使用继承 private String address; //籍贯 private String name; //姓名 private int age; //年龄 String no; //学号 public Student(String name1,int age1) { name=name1; age=age1; } //其它… }
继承关系中构造方法的作用 ( 1) 当子类未定义构造方法时 ,创建对象时将无条件地调用父类的无参构造方法; ( 2) 对于父类的含参数构造方法 ,子类可以在自己构造方法中使用关键字 super来调用它 , 但 super调用语句必须是子类构造方法中的第一个可执行语句; ( 3) 子类在自己定义构造方法中如果没有用 super明确调用父类的构造方法,则在创建对象时 ,将自动先执行父类的无参构造方法 ,然后再执行自己定义的构造方法。
以下程序在编译时将出错,为什么?class parent { String my; public parent(String x) { my=x; } } public class subclass extends parent { }
例 4-1 类的继承中构造方法的调用测试 class Person { // Person类
private String address; //籍贯 private String name; //姓名 private int age; //年龄 public String getName() { //获取人名 return name; } public Person(String name1,String address1,int age1) { name=name1; address=address1; age=age1; } public Person() { name="无名氏 "; }
}
例 4-1 类的继承中构造方法的调用测试public class Student extends Person { String no; //学号 public Student(String name1,String address1,int age1,String
no1) { super(name1,address1,age1); no=no1; } public static void main(String a[]) { Student x=new Student("张三 ","江西 ",25, "20012541"); System.out.println("name="+x.getName()); System.out.println("no="+x.no); //Student y= new Student(); 不能使用,子类无该构造方法 } }
子类中将隐藏父类的同名变量 class parent {
int a=3; int m=2; }public class subclass extends parent { int a=4; int b=1; public static void main(String a[]) { subclass my=new subclass(); System.out.println("a="+my.a+",b="+my.b+",m="+my.m); }}
4.2.1 公共访问控制符 public
• 作为类的修饰符,将类声明为公共类 , 表明它可以被所有的其它类所访问和引用 • 作为类的成员的访问修饰符,表明在其他类中可以无限制地访问该成员。 • 要真正做到类成员可以在任何地方访问,在进行类设计时必须同时满足两点:首先类被定义为 public,其次,类的成员被定义
为 public。
4.2.3 私有访问控制符 private • 用 private修饰的域或方法只能被该类自身所访问例 4-3 测试对私有成员的访问 class Myclass { private int a; //私有变量 void display() { System.out.println(a); } } public class test { public static void main(String arg[]) { Myclass my=new Myclass(); my.a=5; my.display(); } }
例 4-4 测试包的访问控制的一个简单程序文件 1 : PackageData.java(该文件存放在 sub子目录下) package sub;
public class PackageData { protected static int number=1;}
文件 2 : Mytest.java import sub.*;
public class Mytest { public static void main( String args[] ) { System.out.println("result="+PackageData.number); }
}
各类访问控制符的作用可以归纳为表 3-1
可直接访问 修饰
同一类中 同一包中 不同包的子类中 其他 private Yes
默认 Yes Yes
protected Yes Yes Yes
public Yes Yes Yes Yes
4.3.1 方法的重载 (例)public class A { protected void test(int x,int y) { System.out.println("test(int,int):"+x+" "+y); } protected void test(int x) {
System.out.println("test(int):" + x); } protected void test(String str ) {
System.out.println("test(String):" + str); }public static void main (String[] args) {
A a1 = new A(); a1.test("hello"); a1.test(5,4);
} }
例 4-5 方法调用的匹配测试 public class A {
protected int x = 0; protected void test(int x) { System.out.println("test(int):" + x); } protected void test(Object obj) {
System.out.println("test(Object):" + obj ); } protected void test(String str ) {
System.out.println("test(String):" + str); } public static void main (String[] args) { A a1 = new A(); a1.test("hello"); a1.test(5); } }
【思考】
• 如果在以上程序中加上如下方法 ,并将 test(int x)方法注释掉 ,则调用 test(5)如何 ? protected void test(long x) {
System.out.println("test(long):" + x ); }
• 如果是以下情形 , 调用 test(6.3) ? protected void test(float x) {
System.out.println("test(float):" + x ); }
4.3.2 方法的覆盖 • 以下类 B定义的方法中,方法覆盖如何? class B extends A {
protected void test(int x) { System.out.println("in B.test(int):" + x); }
protected void test(String x , int y) { System.out.println("in B.test(String,int):" + x+","+y); }}
• 【思考】通过子类 B的对象可调用多少 test方法?
关于方法覆盖有以下问题值得注意:• 方法名、参数列表、返回类型完全相同才会产生方法覆盖; 如果返回类型不一样编译将报错。• 方法覆盖不能改变方法的静态与非静态属性。子类中不能将父类非静态方法定义为静态方法,反之也一样。 • 不允许子类中方法的访问修饰符比父类有更多的限制。例如:子类不能将父类的 public方法定义为
protected方法。但可以将父类的 private方法在子类中重新定义为 public方法 .• final方法不能被覆盖。
this ---出现在类的实例方法或构造方法中,用来代表使用该方法的对象 ( 1)把当前对象的引用作为参数传递给另一个方法。 如: obj.f(this) ( 2)可以调用当前对象的其它方法或访问当前对象的实例变量。 如: this.g(); ( 3)使用 this可以区分当前作用域中同名的不同变量。
String x; public Test(String x , int a) { this.x=x; }
( 4)一个构造方法中调用另一个构造方法。 public Test(final int x) { this(x,0); }
(1) 用 Super 访问超类的变量或方法class parent {
int a=3; void f() { a=a+1; } }public class subclass extends parent { int a=6; void f() { super.f(); a= a+super.a -3; }
( 2)调用超类的构造方法 public class graduate_student extends Student { Date enterDate; //入校时间 public graduate_student(String name,int age, Date d) { super(name,age); enterDate=d; }
如果有 super(),必须放在构造方法的第一条语句
4.5 final修饰符 • final作为类修饰符 ----最终类 (不能有子类)• 用 final修饰方法 ----不能被子类重新定义 • 用 final定义常量 ----只能赋值一次 注意: 如果将引用类型的变量标记为 final,那么该变量固定指向一个对象,但可以改变对象内的属性值。
例 4-7 常量赋值测试public final class test { public static int totalNumber=5; public final int id; public int weight; public test(int weight) { id=totalNumber++;
this.weight=weight; } public static void main(String args[]) { final test t=new test(5); t=new test(4); t.weight=t.weight+2; } }
4.6.1 抽象类的定义 abstract class 类名称 { 成员变量; 方法() {……} //一般方法 abstract 方法(); //抽象方法 } • 在抽象类中可以包含一般方法和抽象方法。• 所有的抽象方法必须存在于抽象类中。 • 抽象类表示的是一个抽象概念,不能被实例化为对象。
4.6.2 抽象类的实现 abstract class Animal { //抽象类 String name; abstract public int getLeg(); //抽象方法 } class Dog extends Animal { int leg=4; public Dog(String n) { name=n; } public int getLeg() { return leg ; } }
4.6.2 抽象类的实现(续 1)class Fish extends Animal { public Fish(String n) { name=n; } public int getLeg() { return 0; } }
4.6.2 抽象类的实现(续 2)public class test { public static void main(String args[]) { Animal a[]=new Animal[3]; a[0]=new Dog("dog-A "); a[1]=new Fish(“fish-A "); a[2]=new Dog("dog-B "); for (int i=0;i<3;i++) { System.out.println(a[i].name+"has "+
a[i].getLeg() +" legs"); } } }
4.7.1 接口定义 [public] interface 接口名 [extends 父接口名列表 ] { [public] [static] [final] 域类型 域名 = 常量值 ; [public] [abstract] [native] 返回值 方法名 (参数列
表 ) [throw 异常列表 ];} • 声明接口可给出访问控制符; • 一个接口还可以继承多个父接口,父接口间用逗号分隔。 • 系统默认接口中所有属性的修饰都是 public static final; • 系统默认接口中所有方法的修饰都是 public abstract 。
4.7.2 接口的实现 abstract public class Rectangle implements Shape { private double x,y,w,h; public Rectangle(double x,double y,double
w,double h) { this.x=x; this.y=y; this.w=w; this.h=h; } public double area() { return w*h; } }
有关接口的实现 ,要注意以下问题• 一个类可以实现多个接口。接口间用逗号分隔; • 如果实现某接口的类不是抽象类 ,则在类的定义部分必须实现指定接口的所有抽象方法; • 一个类在实现某接口的抽象方法时 ,必须使用完全相同的方法头; • 接口的抽象方法的访问限制符默认为 public,在实现时要在方法头中显式地加上 public修饰。
例 4-9 接口应用举例 interface StartStop { void start (); void stop (); } class Conference implements StartStop { public void start () { System.out.println ("Start the conference."); } public void stop () { System.out.println ("Stop the conference."); } }
例 4-9 接口应用举例 (续 1)class Car implements StartStop { public void start () { System.out.println ("Insert key in ignition and
turn."); } public void stop () { System.out.println ("Turn key in ignition and
remove."); } }
例 4-9 接口应用举例 (续 2)public class TestInterface { public static void main (String [] args) { StartStop [] ss ={ new Car(), new Conference() }; for (int i = 0; i < ss.length; i++) { ss[i].start (); ss[i].stop (); } } }
二义性问题 interface Frob { float v = 2.0f; } //接口定义 class Parent { int v = 3; } //父类定义 class Test extends Parent implements Frob { public static void main(String[] args) { new Test().printV(); } void printV() { System.out.println((super.v + Frob.v)/2); } }
例 4-10 一个简单例子 public class OuterOne { private int x=3; InnerOne ino = new InnerOne(); public class InnerOne { //内嵌类 private int y=5; public void innerMethod() { System.out.println("y is "+y); } public void innerMethod2() { System.out.println("x2 is "+x); } }
创建内嵌类的对象作为外部类的一个属性成员
例 4-10 一个简单例子(续)public void OuterMethod() { System.out.println("x is "+x); ino.innerMethod(); ino.innerMethod2(); } public static void main(String arg[]) { OuterOne my=new OuterOne(); my.OuterMethod(); } }
调用内嵌类的方法
有关说明• 内嵌类经过编译后产生的字节码文件名为:
OuterOne$InnerOne.class • 在内嵌类中可以访问外层类的成员 • 内嵌类可以使用访问控制符
public 、 protected 、 private修饰
在外层类中访问内嵌类的方法 • 方法 1:在外层类的成员定义中创建内嵌类的对象,例如: InnerOne ino=new InnerOne(); 然后,在外层类中通过该成员变量 ino访问内嵌类的方法
• 方法 2:在程序的某个方法体中创建内嵌类的对象,然后通过该对象访问内嵌类的成员,如: public void accessInner() { Innerone anInner=new Innerone(); anInner.innerMethod(); }
在 main方法中间接创建内嵌类的对象• 在 main方法中,要创建内嵌类的对象必须先创建外层类对象,然后通过外层类对象创建内嵌类对象。例如: public static void main(String arg[]) { OuterOne.InnerOne i = new OuterOne() . new
InnerOne(); i.innerMethod(); }
在内嵌类中使用 this ---在内嵌类中, this指内嵌类的对象,要访问外层类的当前对象须加上外层类名作前缀 public class A { private int x=3; public class B { //内嵌类 private int x=5;
public void M(int x) { System.out.println("x ="+x);
System.out.println("this.x="+this.x); System.out.println("A.this.x="+A.this.x);
} } //内嵌类结束
内嵌类对象
外部类的当前对象
例 4-12 静态内嵌类举例 public class Outertwo { private static int x=3; private int y=5; public static class Innertwo { //静态内嵌类 public static void Method() { System.out.println("x is "+x); } public void Method2() { System.out.println("x is "+x); } } //内嵌类结束 public static void main(String arg[]) { Outertwo.Innertwo.Method(); new Outertwo.Innertwo().Method2(); } }
方法中的内嵌类 (例 4-13 )public class OuterTwo { private int x=3; public void OuterMethod( int m ) { final int n=x+2; class InnerTwo { //方法内的内嵌类 private int y=5; public void innerMethod() { System.out.println("y is "+y); System.out.println("n is "+n); System.out.println("x is "+x); } } //内嵌类结束
只能访问外部方法中的常量(带 final修饰)
方法中的内嵌类(例 4-13续) InnerTwo in2=new InnerTwo(); in2.innerMethod(); } public static void main(String arg[]) { OuterTwo my=new OuterTwo(); my.OuterMethod(8); } }
匿名内嵌类interface sample { void testMethod(); } public class AnonymousInner { void OuterMethod() { new sample() { public void testMethod( ) { System.out.println("just test"); } } .testMethod(); //调用内嵌类中定义的方法 }
---字节码文件为 AnonymousInner$1.class。如果有更多的匿名内嵌类将按递增序号命名
由接口派生匿名内嵌类,根据该内嵌类创建对象
4.9.1 对象引用赋值转换 在赋值时允许的转换归纳如下: • 接口类型可转换为父接口或 Object类; • 类对象可以转换为父类或该类实现了的接口类型; • 数组可以转换为 Object,也可转换为 Cloneable 或
Serializable接口。数组还可转换为一个新数组,但旧数组的元素类型必须能够允许转换为新类型方可。
4.9.2 对象引用强制转换 将父类引用赋值给子类变量时要进行强制转换,这种强制转换在编译时总是认可的,但运行时的情况取决于对象的值
Friut x; Apple y; Orange m , n; n=new Orange(); x=n; m=(Orange)x; y=(Apple)x;