530 likes | 661 Views
第 4 章 继承、多态与接口. 4.1 继承的概念 4.2 访问控制符 4.3 多态性 4.4 理解 final 修饰 4.5 抽象类和抽象方法 4.6 接口 4.7 内嵌类 4.8 对象引用转换. 父类 或超类。实际上是所有子类的公共域和公共方法的集合 子类 ,父类的特殊化 , 是对公共域和方法在功能、内涵方面的扩展和延伸 ,祖先类的所有成员均将成为子类拥有的 “ 财富 ” Object 类 是所有类的祖先. 继承的概念. class Student { // 未使用继承
E N D
第4章继承、多态与接口 4.1 继承的概念 4.2 访问控制符 4.3 多态性 4.4 理解final修饰 4.5 抽象类和抽象方法 4.6 接口 4.7 内嵌类 4.8 对象引用转换
父类或超类。实际上是所有子类的公共域和公共方法的集合父类或超类。实际上是所有子类的公共域和公共方法的集合 子类,父类的特殊化,是对公共域和方法在功能、内涵方面的扩展和延伸 ,祖先类的所有成员均将成为子类拥有的“财富” Object类是所有类的祖先 继承的概念
class Student { //未使用继承 private String address; //籍贯 private String name; //姓名private int age; //年龄String no; //学号 public Student(String name1,int age1) { name=name1; age=age1; } //其它… } Java继承的实现
class Student extends Person { String no; //学号 //其它… } Java继承的实现 只有no属性是新加入的,其它属性在Person类中均存在
(1) 当子类未定义构造方法时,创建对象时将无条件地调用父类的无参构造方法; (2) 对于父类的含参数构造方法,子类可以在自己构造方法中使用关键字super来调用它,但super调用语句必须是子类构造方法中的第一个可执行语句; (3) 子类在自己定义构造方法中如果没有用super明确调用父类的构造方法,则在创建对象时,将自动先执行父类的无参构造方法,然后再执行自己定义的构造方法。 继承关系中构造方法的作用
class parent { String my; public parent(String x) { my=x; } } public class subclass extends parent { } 以下程序在编译时将出错,为什么?
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(); 不能使用,子类无该构造方法 } } 例4-1 类的继承中构造方法的调用测试
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); }} 子类中将隐藏父类的同名变量
作为类的修饰符,将类声明为公共类, 表明它可以被所有的其它类所访问和引用 作为类的成员的访问修饰符,表明在其他类中可以无限制地访问该成员。 要真正做到类成员可以在任何地方访问,在进行类设计时必须同时满足两点:首先类被定义为public,其次,类的成员被定义为public。 4.2.1 公共访问控制符 public
没有给出访问控制符情形 该类只能被同一个包中的类访问和引用 4.2.2 缺省访问控制符
用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.2.3私有访问控制符 private
用protected修饰的成员可以在三种类中所引用: 该类本身; 与它在同一个包中的其它类; 在其它包中的该类的子类。 4.2.4保护访问控制符 protected
文件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); } } 例4-4 测试包的访问控制的一个简单程序
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.3.1 方法的重载(例)
通过形式参数表的差异来区分 方法调用的匹配原则: 1) 精确匹配 2) 自动转换匹配 基本类型转换 对象引用转换 4.3.1 方法的重载
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); } } 例4-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 ); } 【思考】
以下类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方法? 4.3.2 方法的覆盖
方法名、参数列表、返回类型完全相同才会产生方法覆盖; 如果返回类型不一样编译将报错。 方法覆盖不能改变方法的静态与非静态属性。子类中不能将父类非静态方法定义为静态方法,反之也一样。 不允许子类中方法的访问修饰符比父类有更多的限制。例如:子类不能将父类的public方法定义为protected方法。但可以将父类的private方法在子类中重新定义为public方法. final方法不能被覆盖。 关于方法覆盖有以下问题值得注意:
(1)把当前对象的引用作为参数传递给另一个方法。(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); } this ---出现在类的实例方法或构造方法中,用来代表使用该方法的对象
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; } (1) 用Super 访问超类的变量或方法
public class graduate_student extends Student { Date enterDate; //入校时间 public graduate_student(String name,int age, Date d) { super(name,age); enterDate=d; } (2)调用超类的构造方法 如果有super(),必须放在构造方法的第一条语句
子类中调用方法的查找过程以及this和super的用法 【思考】如果graduate_student中无toString()方法,则this.toString()将会出现什么情况?
final作为类修饰符 ----最终类 (不能有子类) 用final修饰方法 ----不能被子类重新定义 用final定义常量 ----只能赋值一次 注意: 如果将引用类型的变量标记为final,那么该变量固定指向一个对象,但可以改变对象内的属性值。 4.5final修饰符
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-7 常量赋值测试
abstract class 类名称 { 成员变量; 方法(){……} //一般方法 abstract 方法(); //抽象方法 } 在抽象类中可以包含一般方法和抽象方法。 所有的抽象方法必须存在于抽象类中。 抽象类表示的是一个抽象概念,不能被实例化为对象。 4.6.1 抽象类的定义
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 抽象类的实现
class Fish extends Animal { public Fish(String n) { name=n; } public int getLeg() { return 0; } } 4.6.2 抽象类的实现(续1)
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.6.2 抽象类的实现(续2)
[public] interface 接口名 [extends 父接口名列表 ] { [public] [static] [final] 域类型 域名 = 常量值 ; [public] [abstract] [native] 返回值 方法名(参数列表) [throw 异常列表]; } 声明接口可给出访问控制符; 一个接口还可以继承多个父接口,父接口间用逗号分隔。 系统默认接口中所有属性的修饰都是 public static final; 系统默认接口中所有方法的修饰都是 public abstract。 4.7.1 接口定义
interface Shape { void draw(); //用于绘制形状 double area(); //用于求面积 } 4.7.1 接口定义举例
abstractpublic 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; } } 4.7.2 接口的实现
一个类可以实现多个接口。接口间用逗号分隔; 如果实现某接口的类不是抽象类,则在类的定义部分必须实现指定接口的所有抽象方法; 一个类在实现某接口的抽象方法时,必须使用完全相同的方法头; 接口的抽象方法的访问限制符默认为 public,在实现时要在方法头中显式地加上public修饰。 有关接口的实现,要注意以下问题
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 接口应用举例
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 接口应用举例 (续1)
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 (); } } } 例4-9 接口应用举例 (续2)
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); } } 二义性问题
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(); } } 例4-10 一个简单例子(续) 调用内嵌类的方法
内嵌类经过编译后产生的字节码文件名为: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(); } 在main方法中间接创建内嵌类的对象
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); } } //内嵌类结束 在内嵌类中使用this ---在内嵌类中,this指内嵌类的对象,要访问外层类的当前对象须加上外层类名作前缀 内嵌类对象 外部类的当前对象
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-12 静态内嵌类举例
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); } } //内嵌类结束 方法中的内嵌类 (例4-13 ) 只能访问外部方法中的常量(带final修饰)
InnerTwo in2=new InnerTwo(); in2.innerMethod(); } public static void main(String arg[]) { OuterTwo my=new OuterTwo(); my.OuterMethod(8); } } 方法中的内嵌类(例4-13续)
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 x=new Apple(); Fruit m=new Orange(); Apple x=new Fruit();