590 likes | 810 Views
参考《 Java 面向对象编程》的第6、7、8和12章. 第4课 对象和类. interface MyIFC{ void method1(); void method1(int a); } abstract class Base{ public void method1(){ System.out.println("hi"); } protected abstract void method2(); } class Sub extends Base implements MyIFC{ private int a;
E N D
参考《Java面向对象编程》的第6、7、8和12章 第4课 对象和类 interface MyIFC{ void method1(); void method1(int a); } abstract class Base{ public void method1(){ System.out.println("hi"); } protected abstract void method2(); } class Sub extends Base implements MyIFC{ private int a; privatestatic int b; public static final int C=1; Sub(){this(-1);} Sub(int a){this.a=a;} public void method1(){a++;} public void method1(int a){this.a=a;} public void method2(){a--;} public static void method3(){b++;} } • 构造方法 • 重载方法 • 覆盖方法 • 访问控制 • static变量,方法和初始代码块 • this引用的用途 • final类,方法和变量 • abstract类和方法,接口 • 解释如何以及何时使用内部类 • 降级,以及如何把Java程序从JDK的低版本升级到高版本
构造方法 • 声明构造方法的语法规则 • 重载构造方法,参见Employee.java • 默认构造方法, 参见Sample1.java • 子类调用父类的构造方法,参见Son.java,
构造方法的语法规则 • 一个新对象的初始化的最终步骤是去调用对象的构造方法。 • 构造方法必须满足以下条件: • 方法名必须与类名称完全相匹配; • 不要声明返回类型; • 不能被static、final、synchronized、abstract、native修饰。 public class Sample { int x; public Sample() { // No-arg constructor x=1; } public Sample(int x) { //int-arg constructor this.x=x; } }
构造方法的语法规则 public class Sample { int x; public void Sample() { x=1; } public static void main(String args[]){ Sample s=new Sample(); System.out.println(s.x); } }
重载构造方法 public class Employee { private String name; private int salary; public Employee(String n, int s) { name = n; salary = s; } public Employee(String n) { this(n, 0); } public Employee() { this( " Unknown " ); } } Employee tom=new Employee("Tom",1000); Employee jack=new Employee("Jack"); Employee someone=new Employee();
默认构造方法 • 默认构造方法是没有参数的构造方法,你可以显式定义类的默认构造方法。 • 为了保证每个类至少有一个构造方法,如果定义的类中一个构造方法也没有写,Java将自动提供一个默认构造方法。该构造方法没有参数,用public 修饰,而且方法体为空。格式如下: public ClassName(){} • 只要类中显式定义了一个或多个构造方法,而且所有显式定义的构造方法都带参数,那么将失去默认构造方法。
默认构造方法 public class Sample1{} public class Sample2{ public Sample2(int a){System.out.println(“My Constructor”);} } public class Sample3{ public Sample3(){System.out.println(“My Default Constructor”);} } Sample1 s1=new Sample1(); Sample2 s2=new Sample2(); //非法 Sample2 s22=new Sample2(1); Sample3 s3=new Sample3();
子类调用父类构造方法 • 在构造子类对象时,JVM会先调用父类的构造方法 • 子类构造方法中通过super语句调用父类构造方法 • 如果子类构造方法中没有通过super语句调用父类构造方法,那么JVM会调用父类的默认构造方法,如果不存在默认构造方法,将导致编译错误
子类调用父类构造方法 class Father{ String fatherName; Father(){ this.fatherName=“未知"; } Father(String fatherName){ this.fatherName=fatherName; } } class Son extends Father{ String sonName; Son(String sonName){ this.sonName=sonName; } Son(String sonName,String fatherName){ super(fatherName); this.sonName=sonName; } } Son son1=new Son("王小毛","王大毛"); Son son2=new Son("张三"); System.out.println(son1.sonName); System.out.println(son1.fatherName); System.out.println(son2.sonName); System.out.println(son2.fatherName);
修饰符 • 修饰符的类型 • 访问控制修饰符(public,protected,private) • static,abstract,final • 修饰符的修饰内容(类,方法,变量) • 修饰符的作用 • 使用修饰符的限制
成员变量或成员方法的访问控制 修饰符 同类 同包 子类 不同包 public 是 是 是 是 protected 是 是 是 默认 是 是 private是
成员变量或成员方法的访问控制 包1 包2 ClassA public int v1; protected int v2; int v3 private int v4; ClassC ClassB ClassD extends ClassA ClassB, ClassC,ClassD分别可以访问ClassA的哪些成员变量?
类的访问控制 • 顶层类只能是public或默认访问级别 • public级别的类可以被同一个包或者其他包中的类访问 • 默认级别的类只能被同一个包中的类访问 public class Sample{…} //public级别 class Sample{…} //默认访问级别 protected class Sample{…} //非法 private class Sample{…} //非法
static关键字 • 类(static)变量,参见Count.java • 类(static)方法,参见Wrong.java • 静态初始化程序,参见StaticBlock.java
静态变量和实例变量 • 静态变量在装载类的时候被分配内存并初始化,类只能被装载一次,所以静态变量在内存中只有一个拷贝 • 实例变量在创建实例时被分配内存并初始化,所以每个实例都有各自的实例变量 • 同一个类的实例之间共享静态变量
静态变量和实例变量 public class Count { private int serialNumber; private static int counter; public Count() { counter++; serialNumber = counter; System.out.println("My serialNumber is " + serialNumber); } public static void main(String args[]){ System.out.println("At first,counter="+ counter); Count count1=new Count(); System.out.println("after creat count1, counter="+counter); Count count2=new Count(); System.out.println("At last counter="+counter); System.out.println("count1.serialNumber"+count1.serialNumber); System.out.println("count2.serialNumber"+count2.serialNumber); System.out.println("count1.counter"+count1.counter); System.out.println("count2.counter"+count2.counter); System.out.println("Count.counter"+Count.counter); } }
Count.counter//合法 Count.serialNumber//非法 静态变量和实例变量 Count count1=new Count(); 堆区 方法区 Count对象 Count的类型信息 count1引用变量 serialNumber=1 counter=1 Count count2=new Count(); 堆区 方法区 Count对象 Count的类型信息 count1引用变量 serialNumber=1 counter=2 count2引用变量 Count对象 serialNumber=2
静态方法和实例方法 • 成员方法分为类方法和实例方法。用static修饰的方法叫类方法,或静态方法。 • 静态方法也和静态变量一样,不需创建类的实例,可以直接通过类名被访问。 • static方法不能被修饰成protected和abstract。 public class GeneralFunction { public static int addUp(int x, int y) { return x + y; } } public class UseGeneral { public void method() { int a = 9; int b = 10; int c = GeneralFunction.addUp(a, b); System.out.println("addUp() gives " + c); } }
静态方法和实例方法 堆区 public class Wrong { int x; void method(){x++;} public static void test() { x = 1; //非法 method();//非法 } public static void main(String args[]) { x = 9; //非法 method();//非法 } } Wrong对象 实例变量x Wrong对象 实例变量x Wrong.test() ? 静态方法中不允许直接访问实例变量和实例方法
静态方法和实例方法 堆区 public class Correct{ int x; void method(){ x++; //合法 } public static void main(String args[]) { Correct r1=new Correct(); r1.x = 9; // 合法 r1.method();// 合法 Correct r2=new Correct(); r2.x = 10; // 合法 r2.method();// 合法 System.out.println(r1.x); System.out.println(r2.x); } } Correct对象 实例变量x Correct对象 实例变量x 引用变量r1 引用变量r2
this关键字 Sample s1=new Sample(1); Sample s2=new Sample(2); System.out.println(s1.x); System.out.println(s2.x); • this关键字引用当前实例 • 在static方法中不能使用this关键字 public class Sample{ int x; Sample(int x){ this.x=x; method(this); } void method(Sample s){ s.x++; //合法 } public static void test() { this.x++; //非法 } } 堆区 Sample对象 实例变量x Sample对象 实例变量x 引用变量s1 引用变量s2
静态初始化程序 • 类中可以包含静态代码块,它不存在于任何方法体中。当类被装载时,静态代码块只被执行一次。类中不同的静态块按它们在类中出现的顺序被依次执行。 public class Sample{ static int i = 5; static { System.out.println(" First Static code i= "+ i++ ); } static { System.out.println(" Second Static code i= "+ i++ ); } public static void main(String args[]) { Sample s1=new Sample(); Sample s2=new Sample(); System.out.println("At last, i= "+ i ); } } 打印 First Static code i=5 Second Static code i=6 At last,i=7
final关键字 • final类:不能被继承 • final方法: 不能被子类覆盖 • final变量:必须被显式的初始化,并且只能初始化一次,参见InitFinal0.java public class A{ public final int method(){ return 1; } } public class B extends A{ public int method(){ //非法 return 2; } } public final class A{} public class B extends A{} //非法
final变量例:What will happen when compile the following code? public class Test{ final int x = 0; Test(){ x = 1; //非法 } final int aMethod(){ return x; } }
final变量例:What will happen when compile the following code? 1. class FinalTest{ 2. final int q; 3. 4. FinalTest(){ 5. this(0); 6. q = 1; } 7. 8. FinalTest(int x){ 9. q = x; 10. } 11. } FinalTest f=new FinalTest();
final变量例:What will happen when compile the following code? 1. class FinalTest{ 2. final int q; 3. 4. FinalTest(){} 5. 6. FinalTest(int x){ 7. q = x; 8. } 9. } FinalTest f=new FinalTest();
native关键字 • native只用来修饰方法。 • native方法用其它语言(如C语言)实现,所以没有程序代码块。 public static native int myNativeMethod(int p);
extends关键字和类的继承 • 继承是复用程序代码的有力手段,当多个类(Sub1,Sub2…Sub100)之间存在相同的属性和方法,可从这些类中抽象出父类Base,在父类Base中定义这些相同的属性和方法,所有的Sub类无需重新定义这些属性和方法,只需通过extends语句来声明继承Base类: public class Sub extends Base{…} Sub类就会自动拥有在Base类中定义的属性和方法。 • Java中不支持多继承 public class ClassC extends ClassA,ClassB{} //非法
abstract关键字 • abstract修饰符可用来修饰类和成员方法: • 用abstract修饰的类表示抽象类,抽象类位于继承树的抽象层,抽象类不能被实例化,即不允许创建抽象类本身的实例。没有用abstract修饰的类称为具体类,具体类可以被实例化。 • 用abstract修饰的方法表示抽象方法,抽象方法没有方法体。抽象方法用来描述系统具有什么功能,但不提供具体的实现。没有用abstract修饰的方法称为具体方法,具体方法具有方法体。 public abstract class Base{ //Base是抽象类 abstract void method1(); //抽象方法 void method2(){ //具体方法 System.out.println("method2"); } }
abstract关键字 • 一个类中有抽象方法,这个类必须是抽象类 • 抽象类中可以没有抽象方法 abstract class Base{ abstract void method1(); abstract void method2(); } class Sub extends Base{ //编译出错,Sub类必须声明为抽象类 void method1(){System.out.println("method1");} }
接口 • 在Java语言中,接口有两种意思: • 一是指概念性的接口,即指系统对外提供的所有服务。类的所有能被外部使用者访问的方法构成了类的接口。 • 二是指用interface关键字定义的实实在在的接口,也称为接口类型。它用于明确的描述系统对外提供的所有服务,它能够更加清晰的把系统的实现细节与接口分离。 public interface Transparency { public static final int OPAQUE=1; public static final int BITMASK=2; public static final int TRANSLUCENT=3; public int getTransparency(); }
接口的特征 • 接口中只能包含public、static、final类型的成员变量和public、abstract类型的成员方法。 • 接口中不能有非抽象方法 public interface A{ int var; //编译出错 void method1(){System.out.println("method1");} //编译出错 protected void method2(); //编译出错 static void method3(){System.out.println("method3");} //编译出错 }
接口的特征 • 接口之间允许存在继承关系 interface A{ void method1(); } interface B{ void method2(); } interface D extends A,B{} //合法 interface E implements A,B{} //错误
接口的实现 • 接口由类来实现 • 一个类能实现许多接口。 public class MyApplet extends Applet implements Runnable, MouseListener{ }
接口的实现 interface SayHello { void printMessage(); } class SayHelloImpl implements SayHello { public void printMessage() { System.out.println("Hello"); } }
接口的实现 interface SayHello { void printMessage(); void receiveMessage(); } abstract class SayHelloImpl implements SayHello { public void printMessage() { System.out.println("Hello"); } }
方法重载(overload) • 对于类的方法(包括从父类中继承的方法),如果有两个方法的方法名相同,但参数不一致,那么可以说,一个方法是另一个方法的重载方法。 • 重载方法必须满足以下条件: • 方法名相同。 • 方法的参数类型、个数、顺序至少有一项不相同。 • 方法的返回类型可以不相同。 • 方法的修饰符可以不相同。 //java.lang.Math类的用于取最大值的max方法, //有多个重载方法。 public static int max(int a,int b) public static long max(long a,long b) public static float max(float a,float b) public static double max(double a,double b) int a=Math.max(1,2); double d=Math.max(1,2.0);
方法重载(overload) 以下Sample类中已经定义了一个amethod()方法: public class Sample{ public void amethod(int i, String s){} //加入其他方法 } 下面哪些方法可以加入到Sample类中,并且保证编译正确呢? A)public void amethod(String s, int i){} //可以 B)public int amethod(int i, String s){return 0;} //不可以 C)private void amethod(int i, String mystring){} //不可以 D)public void Amethod(int i, String s) {} //可以 E)abstract void amethod(int i); //不可以
方法覆盖(override) • 方法覆盖是指子类重新实现了父类中的方法 • 以下代码中子类覆盖了父类的一个方法,然后定义了一个重载方法: public class Parent { public void method() {System.out.println(“Parent”); } } public class Child extends Parent { public void method(){System.out.println(“Child”);} //override public int method(int a) { //overload return 0; } }
方法覆盖的约束条件 • 子类方法的名称、参数签名和返回类型必须与父类方法的名称、参数签名和返回类型一致。 public class Base { public void method() {…} } public class Sub extends Base{ public int method() { //编译错误,返回类型不一致 return 0; } }
方法覆盖的约束条件 • 子类方法不能缩小父类方法的访问权限,但可以扩大访问权限 public class Parent { public void method() { } } public class Child extends Parent { private void method() { //编译错误 } }
方法覆盖的约束条件 • 子类方法不能抛出比父类方法更多的异常 class ExceptionA extends Exception{} class ExceptionB extends ExceptionA{} class ExceptionC extends ExceptionB{} public class Parent { void method() throws ExceptionB{} } public class Child1 extends Parent { void method()throws ExceptionA {} //非法 } public class Child2 extends Parent { void method()throws ExceptionC {} //合法 }
多态性 • Java语言允许某个类型的引用变量引用子类的实例,而且可以对这个引用变量进行类型转换: Animal animal=new Dog(); Dog dog=(Dog)animal; //向下转型,把Animal类型转换为Dog类型 Creature creature=animal; //向上转型,把Animal类型转换为Creature类型 animal=new Cat(); • 如果把引用变量转换为子类类型,称为向下转型,如果把引用变量转换为父类类型,称为向上转型。 • 在进行引用变量的类型转换时,会受到各种限制。而且在通过引用变量访问它所引用的实例的静态属性、静态方法、实例属性、实例方法,以及从父类中继承的方法和属性时,Java虚拟机会采用不同的绑定机制。
多态性(参见Tester.java) class Fathers{ String var="FatherVar"; static String staticVar="StaticFatherVar"; void method(){System.out.println("Father method");} static void staticMethod(){System.out.println("Static Father method");} } class Sons extends Fathers{ String var="SonVar"; static String staticVar="StaticSonVar"; void method(){System.out.println("Son method");} static void staticMethod(){System.out.println("Static Son method");} String sonVar=null; void sonMethod(){} }
多态性(参见Tester.java) public class Tester{ public void test(){ Fathers f=new Sons(); //Fathers f=new Fathers(); //Sons f=new Sons(); //Sons f=(Sons)new Fathers(); System.out.println(f.var); System.out.println(f.staticVar); f.method(); f.staticMethod(); } public static void main(String args[]){ new Tester().test(); } }
Fathers Sons var method() staticVar staticMethod() var method() staticVar staticMethod() sonVar sonMethod() 多态性 参见Tester.java • 对于一个引用类型的变量,编译器按照它声明的类型处理。 例如以下代码编译出错。 Fathers f=new Sons(); f.sonVar=“123”; f.sonMethod(); 如果要访问Sons的成员,必须通过强制转换: ((Sons)f).sonVar=“123”; ((Sons)f).sonMethod();
Fathers Sons var method() staticVar staticMethod() var method() staticVar staticMethod() sonVar sonMethod() 多态性 • 对于一个引用类型的变量,运行时按照它实际引用的对象处理。 • 例如以下代码虽然编译可以通过,但运行时会抛出ClassCastException。 Fathers f=new Fathers(); Sons s=(Sons)f;//throw exception when run Animal a=new Dog(); Cat c=(Cat)a;//throw exception when run
Fathers Sons var method() staticVar staticMethod() var method() staticVar staticMethod() sonVar sonMethod() 多态性 • 成员变量、静态方法按照引用变量声明的类型静态绑定;实例方法按照引用变量引用的实例动态绑定 例如,对于以下这段代码: Fathers f=new Sons(); System.out.println(“f.var=”+f.var); System.out.println(“f.staticVar=”+f.staticVar); f.method(); f.staticMethod(); 运行时将会输出如下结果: f.var=FatherVar f.staticVar=StaticFaterVar Son method Static Father method
例:What will be written to the standard output when the following program is run? (参见 PolyTester.java) class Base { int i; Base() {add(1);} void add(int v) { i+= v; } void print() {System.out.println(i);} } class Extension extends Base { Extension(){add(2);} void add(int v) { i+= v*2; } } 参见PolyTester.java public class PolyTester { public static void main(String args[]) { bogo(new Extension()); } static void bogo(Base b) { b.add(8); b.print(); } }
例:What will be written to the standard output when the following program is run? (参见 PolyTester.java) • 以上代码创建的是Extension类的实例,所以在运行时,所有调用add()方法的过程,将始终和Extension类的add()方法动态绑定。 • 以下是程序依次对变量i的改变过程: • 初试值:i=0 • step1:创建实例new Extension() 先调用父类的默认构造方法Base(),父类的默认构造方法中执行add(1),i=0+1*2 → i=2,再调用子类的默认构造方法Extension(),子类的默认构造方法中执行add(2),i=2+2*2→i=6。 • step2:执行add(8) i=6+8*2→i=22 本例子考察知识点: • 方法动态绑定 • 子类调用父类的构造方法