面向对象的编程—封装继承和多态
·构造函数的特点
·对象的声明和调用
·封装
·继承
·多态—重载、覆盖、父类子类的转换
构造函数(constructor),对象的生成,调用和消除
构造函数的特点,默认构造函数
基本的特点还是和c++相同的,例如与类名相同,没有返回类型,可以有参数可以没参数,不声明的时候会调用默认构造函数,但是Java中一旦自己声明了其他的构造函数而没有显示的写出默认构造函数的话,系统就不能再调用默认函数了,会报错。(c++似乎也是,自己写了其他的构造函数而没写出默认构造函数时,企图调用无参的默认函数应该是报错的)总之默认函数最好是被显示的写出来的。
构造函数可以重载,并且可以调用其他的构造函数(后面会提)
Java中类如果不是明显指示出的话,父类就是Object类,调用默认构造函数时调用其直接父类的不带参数的构造函数,并且会自动初始化成员变量(0),但是在方法体里(函数)的变量就得自己初始化了
Java里并没有拷贝构造函数
总结一下:
对于封装 ,构造函数一般是public的,但是不是public也不会报错的;默认构造函数最好手写出来,但是完全没有构造函数的时候自动会调用;
对于继承,构造函数不继承,会一层层找上去 ,先调父类再子类;子类可以用super()来调用父类的构造函数
对于多态,构造函数可重载,但是没有拷贝构造函数
这是一个C++的类
class TheClass{
int name = 1;
int space = 2
public:
TheClass(int a)
{
name = a; space = 3;
}
TheClass(){};//这个是没有参数的当做默认构造函数
int getname(){return name;}
};
int main()
{
TheClass *a = new TheClass(3);
TheClass b(3);
cout<<a->getname()<<” ”<<b.getame()<<endl;
//两种不同的调用方式,因为一个是指针
}
对比一下,内容和调用几乎一样的Java project
在src里面new两个类,一个是TheClass放上面c++类的内容,然后另一个类用于测试包含main函数
TheClass:
public classTheClass {
int name = 1;
int space = 2;
;//这种变量是可以自动初始化的,但是在函数里的局部变量必须自己初始化
public TheClass(int a)//构造函数
{
name = a; space = 3;
}
public intgetname(){return name;}
}
//可以看到 你要是直接一个public:下面都默认public是不行的
public classTestClass {
public static voidmain(String[] args) {
// TODOAuto-generated method stub
TheClassa = newTheClass(4);
System.out.println(a.getname());
//虽然是new出来的,但是用“.”
}
}
对象的声明和方法的调用
像c++里直接 TheClassa = new。。是不行的,得是指针才能new的,而且指针出来的只能用->不能用.但是在Java里面,没有指针的定义,但是是用new来分配空间的,一律用.没有->。在Java里直接TheClass b(3)是报错的但是在C++里完全没问题,而且此时不是指针,只能用.来调用相关函数
还想说的是,这两个类要放在同一个src下面,然后如果点src没反应的话,可以试试package explorer视图
在声明对象的时候: TheClass a;单纯的声明只会分配一个引用空间
A = new TheClass(a)在堆上为对象分配空间,然后地址穿给之前的引用
对象的清除
Java中内存处理不需要程序猿自己动手,是垃圾自动回收机制,比如delete之类的,能避免很多错误但是效率也随之降低
在c++中,指针应该delete掉,类里面有默认的析构函数会自动调用,但Java中没有类似的析构函数, 不过有个类似的方法: protected voidfinalize();
总结一下:
对象的生成: 类 对象名 = new 构造函数();
对象的调用 :对象名. 可以调用public的变量和方法
对象的清除:没有析构函数,自动处理垃圾
封装(Encapsulation)
封装的意义与c++是相似的,还是涉及到几个关键词的问题:
public protecteddefault private 注意到这里的default
关于这几个关键词的属性:
同类可见 同包可见 子类可见 全局可见
Private yes
Default yes yes
Protected yes yes yes
Public yes yes yes yes
就像前面说的,是不能像c++一样直接public:后面的都是Public的,得每个都声明
C++里默认的是private不过Java里默认的default
继承
继承,子类利用父类中的变量和方法(非private default),可以避免冗余代码。C++里一个子类可以继承自多个父类,但是Java不支持类的多继承,不过支持接口的多继承,但这是后话。
继承的关键词是extends
格式是
Modifier class class_name extendssupper_class_name {};
举例来说
Public class Student extends Human{};//Student类继承自父类Human
在举个更大的栗子:
class Person{
protected String name;
protected char sex;
protected int age;
public Person(){System.out.println
(“父类构造函数”);}
public Person(String name, char sex, intage)
{
this.name=name;
this.sex=sex;
this.age=age;
//这里This的用法,可以避免混淆含义
}
public String toString(){
return "姓名:"+name+"性别:"+sex
+"年龄:"+age;}}
//这个 toString函数一般是不让你直接void的,得return
class Student extends Person{
protected int chinese, math;
public Student(){System.out.println
(“子类构造函数”);}
public Student(String name,char sex,int age,
int chinese,int math){
super.name=name;//super关键词,调用父类的
super.sex=sex;
super.age=age;
this.chinese=chinese;//this关键词,指向自己的
this.math=math;
}
public int average(){
return (chinese+math)/2;}
}
这两个类可以写在eclipse的一个类文件里,但是只有一个public类,这个类的类名要和文件名相同
public classTestStudent{
public static void main(String args[]){
Student s1=new Student();//调默认构造函数,而且先调父类的
System.out.println("S1:"+s1.toString()+";成绩"+s1.average());
Student s2=newStudent("Tom",'M',25,88,97);
System.out.println("S2:"+s2.toString()+";成绩"+s2.average());
}
}
从输出结果可以知道,会先调用父类的构造函数然后在调用子类的构造函数。子类是不继承父类的构造函数的。不过可以通过super来调用父类的构造函数
比如:
public Student(String name,char sex,int age,
int chinese,int math){
super.name=name;
super.sex=sex;
super.age=age;
this.chinese=chinese;
this.math=math;
}
可以改成
publicStudent(String name,char sex,int age,
int chinese,int math){
super(name,sex,age);
this.chinese=chinese;
this.math=math;
}
关于构造函数还想说的是,Java里并没有拷贝构造函数。但是可以通过this来实现,this下面会提到
比如
Public Student(Student s)
{
this.name= s.name;
this.sex= s.sex;
}
Public addGrade()
{
Students = new Student(this);//在Student类里的另一个方法,可以这么生成新的对象
}
多态
• 多态是同一表现形式的不同实现机制,是面向对象的三大重要特征之一。
• 多态性也是实现软件可重用性的手段之一,它使得继承性更为灵活,并使程序具有良好的可扩展性。多态性包括:
– 静态多态性:在编译时,就可以被系统识别,也称为编译时多态 :
• 重载(overload):完全相同的方法名和不同的参数列表。
– 动态多态性:在编译时不能被系统识别,只有在运行时才能被系统识别,也称为运行时多态 :
• 覆盖(override):子类和父类定义了具有相同声明的方法,但它们一般具有不同的含义和功能。
• 抽象方法和接口。
函数重载:
参数列表不同。 但是返回类型不是判断是否重载的因素。若参数列表相同但是返回类型不同还是会报错的
然后在这里说一下构造函数的重载,前面略微提了一下
也是要求参数列表不一样,并且可以互相调用,用this.来调用其他的构造函数,不过这条this调用语句必须是构造函数中的第一个可执行语句。只要不是静态的都会有自己的this,向c++里的指针
举个例子
public Person(String str){
this();//调用了另一个构造函数,只不过是没有参数的
name=str;}
• This表示引用对象的本身:
• 主要用法:
– 使用this来解决局部变量与域同名的问题;
– 在构造方法中使用this调用另一个构造方法;
– 使用this来访问域或方法;
– 把this当做参数传入本类的其他方法中;
– 静态方法中不存在this的使用
函数覆盖
这方面比较像c++里的虚函数,就是父类和子类的函数头(返回类型,函数名称,参数列表)一样,但子类有自己的实现方法,调用的时候也是调用子类的函数而非父类的
可以用super关键词来调用父类的函数,如super.display();
Attention:
– 子类方法不能缩小父类方法的访问权限:
1. 一个package方法可以被重写为package、protected和public的;
2. 一个protected方法可以被重写为protected和public的;
3. 一个public方法只可以被重写为public的;
– 若父类方法定义时有异常抛出,则子类覆盖父类时,该方法也不能有更多的异常抛出,否则编译时会产生错误。
Upcasting downcasting转换
一个子类只能有一个父类,而一个父类可以有多个子类,子类转化成父类是没问题的,而父类转化成子类通常是不行的。
比如可以说学生是人但是不能说人是学生
若定义Student类是Person类的子类,Student有两个对象s1,s2; person有两个对象p1,p2
把子类付给父类: p1 = s1; p2 = s2;
若还是上面这个例子,
Student类是Person类的子类,Student有对象s1,s2,s3,s4; person有对象p1,p2,p3;
P1 = s1; s3 = (student)p1; --这句是正确的,因为本来就是子类向上转的
S4 = (Student)p3; --这句就要报错了,让父类向子类转了
所以,父类对象向子类对象强制转换时,通常用运算符instanceof判断要转换的对象是否为子类对象,如果不是则不能转换,否则会产生运行时错误。
用的时候就是 if(p1instanceof Student){ Student s1 =(Student)p1;}
Else
总结一下就是
向上转化没问题,程序隐式进行
向下转化应该用Instanceof检查一下,原本就是子类才可以,否则可能出现运行时错误