Java第三章面向对象技术.ppt
Java程序设计,面向对象技术,内容,类和对象类的创建对象的创建类的封装类的继承类的多态性数组字符串,3.1 类和对象,类是一个模型,它定义了一类对象的共同特征和行为;对象是类的一个实例。类的组成:成员变量:类内用于存储对象各项数据的变量,它们反映对象的状态,也称为属性;成员方法:描述对成员变量的操作。同一个类的不同对象可具有不同的状态,而对象中的方法用于改变自身的状态;,3.2.1 类定义,使用一个类之前必须先定义一个类类定义的组成部分:类声明、类体类定义的格式:类修饰符 class 类名 extends 父类名 implents 接口名 类体,3.2.2 类体,类体中定义了该类的所有成员变量和方法,这些成员变量和方法称为类的成员。,class 类名 成员变量;成员方法;,class Point int x,y;void init(int ix,int iy)x=ix;y=iy;,3.2.2.1 成员变量的声明,格式:修饰符 类型 变量名=初值;,修饰符:说明成员变量的访问权限类型:成员变量的类型变量名:Java合法标识符声明时可以给出变量的初始值可同时声明多个成员变量(不推荐)public int a=1,b=2;public int a=1;public int b=2;,public class Stack private int items;public int items().,3.2.2.2 成员函数的声明,格式修饰符 返回值类型 函数名(参数表)函数体,成员函数的声明必须放在class内必须给出函数的返回类型,若无返 回值类型为void,public class Stack private int items;public int items().,3.3.1 对象的创建声明,对象的声明格式:类型(class/interface名,)对象名;String s;对象的声明并不为对象分配内存,对象的初始值为null;此时不能访问对象的成员。,class Num int i;Num m;m.i=0;,3.3.1 对象的创建实例化,使用关键字new创建一个对象,并返回对象的引用(相当于对象在内存中的地址);可以在声明对象的同时创建对象;每一个对象拥有其成员的单独拷贝;,Num m=new Num();Num n=new Num();m.i=10;n.i=100;,Num m;m=new Num();m.i=10;,class Num int i;,class SimplePoint int x;int y;public class TwoSimplePoint public static void main(String args)SimplePoint p1=new SimplePoint();SimplePoint p2=new SimplePoint();p1.x=10;p1.y=20;p2.x=100;p2.y=200;System.out.println(p1=+p1.x+,+p1.y);System.out.println(p2=+p2.x+,+p2.y);,0 xFCA0,0 xFC00,p1,p2,对象1,对象2,内存,内存,3.3.2 构造函数,构造方法的特征构造方法的名称与类的名称相同构造方法没有返回值(不同于void返回值)可以有多个构造方法,但每个构造方法的参数个数或类型需不同构造方法的作用创建对象:对象的创建通过调用对象的构造方法实现;初始化对象:在构造方法添加初始化成员变量的代码,从而实现对成员变量的初始化(即,对象的初始化)。注意:Java中可以不定义构造方法,此时系统会自动为该系统生成一个默认的构造方法。这个构造方法的名字与类名相同,它没有任何形式参数,并将成员变量初始化为默认值(数值类型为0或0.0,boolean类型为false,复合数据类型为null),构造函数示例,NewPointTest.java,3.3.3 对象的使用,访问成员变量对象名.成员变量名调用成员方法对象名.成员方法名,NewPoint p2=new NewPoint(10);p2.x=10;,NewPoint p3=new NewPoint(100,200);p3.printLoc();,3.3.4 对象的销毁,Java的垃圾回收机制当没有任何引用能指向一个对象时,这个对象将被JVM释放。(示例:NoReference.java)void finalize()(教材P73页错误)JVM在释放一个对象之前,将调用该对象的finallize方法,因此可以在该方法中进行特定地清除操作。,3.3.5 再谈变量 实例变量和局部变量,实例变量:定义在类体之内,方法体之外的变量.局部变量:定义在方法体之内的变量以及方法的参数(形参).唯一性:在同一作用域中不能定义两个同名局部变量;在同一个类中不能定义两个同名的实例变量;但在一个类中可以定义与实例变量同名的局部变量,此时在该局部变量的作用域内,此局部变量将隐藏同名的实例变量.,实例变量和局部变量的唯一性示例(1)实例变量和局部变量的唯一性示例(2)TestInstanceVar.java,public class InstanceAndLocal int x,y,z;/double x;void init(int x,int y)int z=0;/*for(int x=0,y=0,z=0;z5;z+)x+;y+;*/,初始化局部变量在使用前必须初始化;实例变量可以不显式地初始化,此时Java编译器会将实例变量初始化为默认值数值类型的实例变量(byte short int long char float double)初始化为0(或0.0);boolean类型的实例变量初始化为false;复杂数据类型的实例变量(用类作为数据类型定义的实例变量)初始化为null.实例变量可以在定义时直接初始化,也可以在构造函数中初始化;,实例变量和局部变量初始化示例(1),public class InitVar int i;double d;boolean b;String s;/*void noInit()int n;n+;*/public static void main(String args)InitVar obj=new InitVar();System.out.println(i=+obj.i);System.out.println(d=+obj.d);System.out.println(b=+obj.b);System.out.println(s=+obj.s);,实例变量和局部变量初始化示例(2),public class InitInstanceVar int i=10;int j;int k=10;InitInstanceVar()j=10;k=20;public static void main(String args)InitInstanceVar obj=new InitInstanceVar();System.out.println(i=+obj.i);System.out.println(j=+obj.j);System.out.println(k=+obj.k);,生命期局部变量的生命期从定义之处开始,到该变量定义所在代码块结束而结束;实例变量属于对象,它随着对象的存在而存在,随着对象的消亡而消亡;对象什么时候诞生?当用new调用一个类的构造函数时,该类的一个对象被创建(诞生);对象什么时候消亡?当没有引用能够指向一个对象时,该对象将被”垃圾回收”(消亡).,实例变量和局部变量生命期示例,public class VarLiving int i;public static void main(String args)VarLiving obj=new VarLiving();for(int j=0;j10;j+)System.out.println(j=+j);/System.out.println(j=+j);System.out.println(i=+obj.i);obj=null;/System.out.println(i=+obj.i);,3.3.6 引用变量和赋值(1),变量类型为简单数据类型赋值是传递数值的拷贝,int i=1;int j=i;i=2;System.out.println(i=+i);System.out.println(j=+j);,1,i,1,j,2,i,3.3.6 引用变量和赋值(2),变量类型为复合数据类型(类)赋值是传递对象的引用,SimplePoint p1=new SimplePoint();p1.x=10;p1.y=20;SimplePoint p2=p1;p2.x=100;p2.y=200;System.out.println(p1.x+”,”+p1.y);,class SimplePoint int x;int y;,0 xFCA0,p1,3.3.7 向方法传递参数,方法参数的传递传值:基本数据类型传值调用不会改变所传参数的值;传引用:对象、数组传引用调用可改变所传对象的内容;示例:PassingParam.java输出结果:,x=100 y=200value=10i=0,3.3.8 返回对象,对于对象,与向方法传递参数类似,方法返回的是对象的引用;当一个方法返回对象时,对象将一直存在直到没有对它的引用为止,此时该对象将被回收。因此一个对象不会因为创建它的方法终止而被销毁。示例:TestReturnObj.java,3.4 封装,面向对象技术的特性封装继承多态性封装:将属性和方法装配到类中,并加以访问控制隐藏类的实现细节迫使类的使用者通过接口访问类内的数据减少类与类间的耦合性,增强代码的可维护性,3.4.1 访问控制,Java推出了“访问控制修饰符”的概念,允许库创建者声明哪些东西是客户程序员可以使用的,哪些是不可使用的。4种访问级别:public:共有的缺省访问控制private:私有的protected:保护的两种修饰位置:修饰类和修饰成员,3.4.2 public修饰符修饰类,一个类被声明为public类,表明它可以被所有的其他类所访问和引用,这里的访问和引用是指这个类作为整体是可见和可使用的,程序的其他部分可以创建这个类的对象、访问这个类内部可见的成员变量和调用它的可见的方法。一个类作为整体对程序的其他部分可见,并不能代表类内的所有属性和方法也同时对程序的其他部分可见,前者只是后者的必要条件,类的属性和方法能否为所有其他类所访问,还要看这些属性和方法自己的访问控制符。一个Java文件中可以有多个类,但只能有一个被修饰为public,如果java文件中有public类,该文件的名字必须和该public类同名。,3.4.3 public修饰符修饰成员,被public修饰符修饰的成员为公共的,所有class中均可访问该成员。,public class Alpha public int iampublic;public void publicMethod().,class Beta void accessMethod()Alpha a=new Alpha();a.iampublic=10;/a.publicMethod();/,3.4.4 private修饰符,声明为private的变量和方法是私有的,除了声明该变量或方法的class,而不能被任何其他类,包括该类的子类访问。,class Alpha private int iamprivate;private void privateMethod()iamprivate=0;,class Beta void accessMethod()Alpha a=new Alpha();a.iamprivate=10;/X a.privateMethod();/X,3.4.5 实例成员和类成员,一般情况下定义的成员为实例成员,在成员类型前加static定义一个类成员,class AnIntegerNamedX int x;public int x()return x;,class AnIntegerNamedX static int x;static public int x()return x;,3.4.5.1 实例成员的特点,每一个对象拥有该实例成员变量的独立拷贝每一个实例成员方法,作用于该对象的实例成员变量在类定义之外访问实例成员必须通过该对象,class A public int x;public void set(int i)x=i;,A a,b,c;a=new A();b=new A();c=new A();a.x=0;b.x=1;c.x=2;a.set(5);b.set(6);c.set(7);System.out.println(a.x);System.out.println(b.x);System.out.println(c.x);,3.4.5.2类成员变量,该类的所有对象共享一个类成员变量,class A public static int x;public staticvoid set(int i)x=i;,A a,b,c;a=new A();b=new A();c=new A();a.x=0;b.x=1;c.x=2;a.set(5);b.set(6);c.set(7);System.out.println(a.x);System.out.println(b.x);System.out.println(c.x);,3.4.5.3 Static代码块,static代码块写在class内,而不是方法内static代码块在class第一次被加载时执行,即使用class前被执行。static常用于初始化某些static成员变量;在static代码块中不能访问实例变量。示例:TestStaticVariable.java运行结果:,静态变量被初始化!调用PointStatic类的构造函数!一共产生了1个PointStatic类的对象!,3.4.5.4类成员方法,类成员方法只能访问类成员变量和类成员方法,class A public static int x;public int y;public static void set(int i)x=i;/y=i;/X,3.4.5.5类成员的访问,在类外访问类成员可通过类名实现,class A public static int x;public int y;public static void set(int i)x=i;,A a,b,c;a=new A();b=new A();c=new A();A.x=2;a.y=0;b.y=1;c.y=2;A.set(5);System.out.println(a.x);System.out.println(b.x);System.out.println(c.x);,3.4.5.6 类成员与实例成员区别总结,声明方式不同关联性不同(最重要的区别)访问方式不同,3.4.6 方法的重载,重载(overloading)的定义:可以用相同的方法名但不同的参数表来定义方法(参数表中参数的数量、类型或次序有差异),这称为方法重载。意义:同一方法名,对不同类型与个数的参数组合可有不同的实现及其输出类型。可以在同一类中用同名方法重载另一方法可以在子类中用同一方法重载父类的同名方法示例:OverloadDemo.javaOverloadConsDemo.java,3.4.7 this关键字的作用,指代当前对象本身;示例:TestUseThis.java当方法的参数或方法中的局部变量的名字与类的实例变量名相同时,实例变量将被隐藏。使用this引用它便可以访问被隐藏的实例变量。示例:TestUseThis.java(修改构造方法的参数名与实例变量名相同)调用本类的构造方法:this(参数列表)示例:ThisDemo.javathis是调用方法时自动传递给方法的隐式参数,它是调用方法的对象的引用;示例:NewPointTest.java,int x=0,int y=0,void printLoc()0 xFEA0,p1,int x=10,int y=10,int x=100,int y=200,void printLoc()0 xFEA0,void printLoc()0 xFEA0,0 xFEA0,this 说明,p3,p2,3.5 类的继承(1),继承模拟了客观世界中子孙和祖先的关系,类的继承(2),每一个子类均继承了父类中的状态和方法在子类中可以增加父类中没有的状态和方法子类可以覆盖父类中已有的方法继承具有层次结构Java中所有的类均是Object类的子类每一个子类对象也是一个超类对象Java中只支持单重继承,3.5.1 创建子类,格式修饰符 class 子类名 extends 超类名,class Father int i=0;class sub extends Father int getData()return i;,3.5.2 成员访问与继承,子类可访问父类中的public和protected类型的成员;子类可访问父类中没有访问修饰符的成员,并且父类和子类同处于一个package中;子类中不能访问超类中声明为private的成员若子类中的成员与父类的成员名称相同,那么子类中的成员将覆盖父类的成员,成员访问与继承示例,class TwoDShape1 private double width;private double height;TwoDShape1()width=0;height=0;TwoDShape1(int w,int h)width=w;height=h;,public class Triangle1 extends TwoDShape1 String style;Triangle1(String str)style=str;double area()/错误!return width*height/2;,子类隐藏父类定义的成员变量,class Father protected int x=0;class Child extends Father protected int x=1;int print()System.out.println(x);,输出结果:1,用super再现被隐藏的成员变量,class Father protected int x=0;class Child extends Father protected int x=1;Child()System.out.println(super.x);public static void main(String args)new Child();,输出结果:0,构造方法与继承,子类不继承父类的构造方法;子类构造方法负责创建子类对象,父类构造方法负责创建父类对象。在子类构造方法中若没有显式调用父类的构造方法,则在子类调用构造方法初始化的时候将首先调用父类的缺省构造方法,初始化父类对象。使用super关键字在子类中显式调用父类构造方法。(如何在构造方法中调用本类其他构造方法?)若子类构造方法中显式调用父类构造方法,则该调用语句必须是子类构造方法的第一条语句。(ShapeNewTest.java)若父类中的构造函数均是有参数的(即父类中有构造函数且不是默认构造函数),则子类的构造函数中必需首先使用super关键字调用父类的构造函数以初始化父类对象。(ShapeNew2.java),使用super关键字,调用超类的构造方法super(参数列表)使用supper访问子类中隐藏的超类成员变量和成员方法supper.变量名supper.方法名,若子类中的成员变量的名称与父类的成员变量的名称相同,那么子类中的成员变量将隐藏父类的成员变量;若子类成员方法的声明与父类成员方法的声明一样,子类成员方法将隐藏父类成员方法。,成员方法的覆盖,子类可覆盖父类定义的且该子类能看到的同名、同参数列表、同返回值类型的父类成员方法的实现;通过子类对象调用被覆盖的方法时,总是使用子类中定义的方法,父类的方法被隐藏。(Override1.java)使用关键字super可以在子类中访问父类中被隐藏的方法。(Override2.java)对于覆盖父类的方法,子类中方法的访问限制符限制力度的须不紧于父类该方法访问限制符的限制力度对于父类是static的方法,若子类中需要覆盖该方法,则子类方法也必须是static的。注意区分方法的覆盖和方法的重载,成员方法的覆盖示例(1),class Father int f()return 0;class Child extends Father int f()return 1;int ff()return super.f();public static void main(String args)Child cx=new Child();System.out.println(cx.f();System.out.println(cx.ff();,成员方法的覆盖示例(2),class Father int f()return 0;class Child extends Father String f()return:“OK”;public static void main(String args)Child cx=new Child();System.out.println(cx.f();,成员方法的覆盖示例(3),class Father int f()return 0;class Child extends Father int f(int i)return i+1;/方法重载 public static void main(String args)Child cx=new Child();System.out.println(cx.f();System.out.println(cx.f(1);,6.5.6 对象的类型转换,类型向上转换(upcasting)类型向下转换(downcasting),3.5.6.1 向上类型转换,从子类向父类转换,在继承图中是向上移动,通常称为向上类型转换。类型向上转换是安全的,因为这是从特殊类型到通用类型的转换。进行向上类型转换时,出现的唯一问题是可能丢失子类中定义的方法和变量,即,当超类引用变量指向子类对象时,通过该引用变量只能访问超类部分的成员变量。例如:,向上类型转换示例,class A int x;A(int i)x=i;void show()System.out.println(x);class B extends A int y;B(int i,int j)super(i);y=j;void show()System.out.println(y);,public class SupSubRef public static void main(String args)A a1=new A(10);B b=new B(5,6);A a2;a2=a1;System.out.println(“a2.x=+a2.x);a2=b;System.out.println(x2.x=+a2.x);a2.x=19;/a2.y=27;,问题:若超类引用变量访问被覆盖的成员方法时,访问的是超类版本还是子类版本?,3.5.6.2 向下类型转换,从父类向子类转换,在继承图中是向下移动,称为向下类型转换。类型向下转换是不安全的,因为这是从一般类型到特殊类型的转换。例如:DownCast.java,3.5.6 对象运算符instanceof,格式:引用 instanceof 类该表达式的运算结果为boolean功能:测试一个引用所指向的对象是否为指定类的一个实例,若是则返回true,否则返回false.示例:,class Simple int x=0;,Simple s=new Simple();int i=0;if(s instanceof Simple)i+;if(s instanceof Object)i+;,3.5.7 抽象方法与抽象类(1),什么是抽象方法与抽象类?抽象方法:被abstract关键字修饰,并且没有方法体(方法的实现)的方法.(AbsShape.java:)抽象类:被abstract关键字修饰的类,该类中可能包含抽象方法也可能不包含抽象方法,抽象类不能实例化,但可以被继承.(AbsShape.java:)为什么引入抽象方法?实现多态对某些方法,不同的子类具有相同的功能(概念)与结构,但具有不同的实现,如不同的几何形状都有面积的概念,但其计算公式是不同的;这样可以只在父类中定义该方法的占位符(只定义,不实现),而在子类中根据相应的形状实现面积的计算。,抽象方法与抽象类(2),形式:抽象类与抽象方法申明中加abstract修饰符。抽象方法的用法:超类申明,子类实现(类似c+纯虚函数)抽象类约束:abstract不可修饰static、private方法和构造方法;(WrongAbstract.java:WrongAbstract)final不能和abstract同时使用(WrongAbstract.java:WrongAbstract)包含抽象方法的类必须申明为抽象类;(WrongAbstract.java:WrongAbstract1)但抽象类中不一定包括抽象方法,只要有abstract修饰的类均为抽象类;(WrongAbstract.java:WrongAbstract2)抽象类不能创建对象实例,但可有构造方法(供子类调用);(WrongAbstract.java:WrongAbstract3)抽象类中的抽象方法必须在某子孙类中实现,否则该子孙类也是一个抽象类。(WrongAbstract.java:WrongAbstract3),final关键字,final成员:当final修饰成员变量后,该成员变量变为常量,即,初始化后该常量的值不能改变。final type var;(FinalVariables.java)实例成员常量的初始化位置声明处;构造函数中,但若在声明处已初始化,则不能在构造函数中再次赋值。示例:FinalVar.java,final方法与final类,final方法:final方法不能被子类方法覆盖。(FinalFather.java)final type method(parameter_list)注意:构造函数的定义前不能加final关键字final 类:不能进一步扩展子类的类。(FinalFather.java)final class 类名 extends 父类名,类的多态性,多态(Polymorphism):一个名字具有多种语义多态的实现方式:重载和重写实现机制:动态邦定超类的引用变量可以引用子类对象。对方法的调用a.f(t),Java系统根据引用变量a运行时所实际引用的对象的类型确定f的定义体示例:PolyMorph.java,AbsShape.java,3.7 递归方法,P107(自学),3.8 数组,数组是由数目固定、相同类型的元素组成的有序集合,每个元素相当于一个变量。数组的元素可以是简单类型也可以是引用类型数组本身是引用类型,3.8.1 一维数组,一维数组实质上是相同类型的变量列表一维数组的通用声明格式type var-name;type var-name;数组类型可为Java中的任何数据类型数组名,必须符合标识符定义规则声明时不可在 中指定数组元素的个数声明一个数组并未为数组元素分配空间,int student;int student;,student0=1;,3.8.1.1 数组的创建,先声明,后创建int iArray;iArray=new int10;/iArray=new int 10;声明+创建int iArray=new int10;int iArray=new int10;/int iArray10=new int 10;数组在用new创建后,各个元素被初始化为默认值(数值类型初始化为0或0.0,boolean类型初始化为false,引用数据类型初始化为null)。声明+创建+初始化int a=1,2,3,4,5;/if(a5=5),3.8.1.2 数组元素的访问,格式:数组名下标int iArray=new int10;iArray0=1;数组的下标为从0开始至数组长度减1的整数或表达式int i=5;iArrayi=6;/iArray10=10 数组的长度信息保存在数组对象的length属性中(数组在Java中被当成对象对待):int iLen=iArray.length,3.8.1.3 一维数组的引用模型,int a;,a=new int5;,for(int i=0;i5;i+)ai=i+1;,a,0XAF3E,0XAF3E,1,2,3,4,5,内存,int b=a;,b,0XAF3E,System.out.println(b1);,3.8.1.3 数组的使用(示例1),/顺序初始化数组,逆序输出数组内容class ArrayTest public static void main(String args)int i;int a=new int5;for(i=0;i=0;i-)System.out.println(a+i+=+ai);,3.8.2 二(多)维数组,N维数组是N-1维数组的数组,即每一个N维数组元素是一个N-1维数组。a23是一个二维数组,它的每一个元素是一个一维数组,而每一个一维数组中包含三个元素二维数组的声明数据类型数组名;数据类型 数组名;int a;/不能int a23;int a;/声明一个数组并未给数组元素分配内存,因此/不能访问该数组的元素,a00=0;,3.8.2.1 二维数组的创建,先声明,后创建int a;a=new int23;/不能为a=new int23声明+创建int a=new int23;声明+创建第一维+创建第二维int a=new int2;a0=new int3;a1=new int3;,a,3.8.2.2 二维数组的初始化,对每一元素分别赋初始化值;定义时,同时初始化int a=1,2,3;int a=1,2,3,4,5,6;,3.8.2.3 二维数组元素的访问,访问二维数组元素的格式:数组名 下标1 下标2如:a12访问二维数组a中第1维中的第2个元素下标从0开始,到该维长度减1为止。int a=new int1010;a的最后一个元素是a99,而不是a1010;,3.8.2.3 二维数组示例,显示方阵程序中数组元素tableij表示方阵的第i行第j列元素,1 2 3 4 56 7 8910 11 12,public class TwoD public static void main(String args)int table=new int34;for(int i=0;i 3;i+)for(int j=0;j 4;j+)tableij=(i*4)+j+1;System.out.print(tableij+);System.out.println();,3.8.2.4 不规则二维数组,当为多维数组分配内存时,可以先分配第一维(最左边的一维),然后再分别分配其余的每一维;,int a=new int2;a0=new int3;a1=new int3;,int a=new int2;a0=new int1;a1=new int2;,a,a0,a1,a,a0,a1,3.9 字符串,Java中,String类是字符串常量类,该类的对象在建立后不能修改。Java编译器保证每个字符串常量都是String类对象。用双引号括住的一串字符即为字符串常量,比如“Welcome to Java!,在通过编译器编译后成为String对象。因而,实例化一个String类对象既可以通过字符串常量,也可以通过系统提供的构造方法。,3.9.1 构造字符串,String str1=new String(java String);String str2=java String;String str3=new String(str2);System.out.println(str1);System.out.println(str2);System.out.println(str3);,3.9.2 String 类的方法,3.9.2.1 字符串比较,boolean equals(String s):判断本串与s是否(内容)相同注意equals与=的区别:仅当同一对象时为真equals:内容相等(可为不同对象)时为真,String s=new String(“abc”);String s1=new String(s);boolean b1=(s=s1);/结果为falseboolean b2=s.equals(s1);/结果为true,3.9.2.2 字符串的长度,int length():得到字符串的长度注意:不要与数组长度变量length混淆,String s=new String(“abc”);int i=s.length();/i 为 3,3.9.2.2 字符检索,char charAt(int i):返回本串的第i个字符(从0算起),String s=new String(“abc”);char c=s.charAt(1);/结果为:b,3.9.2.3 字符串比较,int compareTo(String s):比较本串与s的字典序大小结果为:0:本串等于s1:本串大于s-1:本串小于s,String s1=new String(“abc”);String s2=new String(“ade”);switch(pareTo(s2)case 0:System.out.println(“s1 等于 s2”);break;case 1:System.out.println(“s1 大于 s2”);break;default:System.out.println(“s1 小于 s2”);/结果:s1 小于 s2,